Compare commits
208 Commits
mainline-1
...
mainline-4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3e1ec25fc | ||
|
|
ea8244301d | ||
|
|
a956d0b0eb | ||
|
|
d8e59a28ea | ||
|
|
67bdd8ed58 | ||
|
|
0a4f0b6a5d | ||
|
|
e77d2b2103 | ||
|
|
6177cbdbe1 | ||
|
|
a1d48b5f52 | ||
|
|
fe83ee102b | ||
|
|
a139fdf4ac | ||
|
|
30448641f2 | ||
|
|
c49c3e9f27 | ||
|
|
053da44ecd | ||
|
|
13891fd62d | ||
|
|
5aaafa6a56 | ||
|
|
dfec9c9a43 | ||
|
|
7a8f484020 | ||
|
|
2d8eba5baf | ||
|
|
7fc5af3622 | ||
|
|
cd81194fc0 | ||
|
|
ef98828d40 | ||
|
|
d6969fa7d4 | ||
|
|
ee35f7adf7 | ||
|
|
5130b8a6a9 | ||
|
|
1559477740 | ||
|
|
a83eb90a78 | ||
|
|
9cf52d027d | ||
|
|
03276e7490 | ||
|
|
6c449793b8 | ||
|
|
922c7f4e51 | ||
|
|
ce3edaad26 | ||
|
|
84815fa879 | ||
|
|
573a1e7662 | ||
|
|
ec95c73a12 | ||
|
|
1449ed9dbf | ||
|
|
e1981b8b8d | ||
|
|
58783b8a46 | ||
|
|
19af91434e | ||
|
|
81fbc5370d | ||
|
|
d4f33b822b | ||
|
|
137d165672 | ||
|
|
86b39e0677 | ||
|
|
7397289266 | ||
|
|
b1ca56bed2 | ||
|
|
952f010c2c | ||
|
|
4ea572791b | ||
|
|
22fd208e8d | ||
|
|
ba661c8d9a | ||
|
|
50b5bb44a0 | ||
|
|
52a41f482f | ||
|
|
4d4f9cc104 | ||
|
|
67cc2d5046 | ||
|
|
878adee0a3 | ||
|
|
a67c4e6e02 | ||
|
|
5adbe66ae8 | ||
|
|
46a962eb5d | ||
|
|
f0c75573b1 | ||
|
|
ca4ca8a6dc | ||
|
|
e424615839 | ||
|
|
f8cc5668f8 | ||
|
|
680ab61327 | ||
|
|
e3534700d7 | ||
|
|
b13fbc25b8 | ||
|
|
6207751b00 | ||
|
|
4e35177e23 | ||
|
|
83ec2091c1 | ||
|
|
6ce2c85047 | ||
|
|
de8ff8a1c6 | ||
|
|
286f4c446a | ||
|
|
5f4b746a1e | ||
|
|
86d8563314 | ||
|
|
862bec001b | ||
|
|
b4a8cfbd00 | ||
|
|
d654b3d82e | ||
|
|
dfdd20142e | ||
|
|
cedc1aab4a | ||
|
|
74a7ce1df7 | ||
|
|
80702aa88f | ||
|
|
9cdf5c6c31 | ||
|
|
8ad7268c75 | ||
|
|
9a76e94b3d | ||
|
|
ef584f1a3a | ||
|
|
ca61e298b3 | ||
|
|
87bbefe55f | ||
|
|
93abe1ccf3 | ||
|
|
509734d818 | ||
|
|
e2392fe46f | ||
|
|
0e9e166d85 | ||
|
|
5980aa1e51 | ||
|
|
2ff8044806 | ||
|
|
ec0da3ef64 | ||
|
|
221250d922 | ||
|
|
978f7067ee | ||
|
|
9aef7e5e22 | ||
|
|
6b2937bf76 | ||
|
|
a2d2a6b6dd | ||
|
|
d3ea2df06d | ||
|
|
6e11cfcdf0 | ||
|
|
a0ee10b114 | ||
|
|
bcbec6f37c | ||
|
|
e52c895559 | ||
|
|
52f54c728d | ||
|
|
77f1a676a1 | ||
|
|
a452ff983d | ||
|
|
b0ff3179ef | ||
|
|
4d26550f5f | ||
|
|
ccbc554949 | ||
|
|
31e8a61527 | ||
|
|
9be9600bdc | ||
|
|
12514ccd35 | ||
|
|
104641db07 | ||
|
|
f601f25bcc | ||
|
|
27e10e0442 | ||
|
|
6738fb5fef | ||
|
|
11f4e739bd | ||
|
|
0a67416971 | ||
|
|
369be67039 | ||
|
|
aa599ac709 | ||
|
|
a2edb27158 | ||
|
|
f470bcb826 | ||
|
|
7a35178ee2 | ||
|
|
1158777737 | ||
|
|
febb88efc4 | ||
|
|
45c162444d | ||
|
|
6c4985edc9 | ||
|
|
024b5fe91a | ||
|
|
0901c33753 | ||
|
|
9bede4eeed | ||
|
|
16730c4c43 | ||
|
|
b9ebab71be | ||
|
|
ed0485c599 | ||
|
|
7653e4babc | ||
|
|
6ecbc6c557 | ||
|
|
c1c89411da | ||
|
|
1780e0e3d0 | ||
|
|
a162a844d2 | ||
|
|
56bc11d952 | ||
|
|
e7b39f47f8 | ||
|
|
6885e7e7ec | ||
|
|
45fa12a05c | ||
|
|
47df844338 | ||
|
|
3df9558593 | ||
|
|
1109db86b7 | ||
|
|
5d369112d9 | ||
|
|
63bda67a34 | ||
|
|
5a06e33859 | ||
|
|
43f57d668c | ||
|
|
3a3fee5abf | ||
|
|
d3b71ff80d | ||
|
|
0b65e9335e | ||
|
|
74632c76ce | ||
|
|
87909d327f | ||
|
|
e7bdf8b22a | ||
|
|
84027f4808 | ||
|
|
73b2dc6d4f | ||
|
|
d4b95bfc25 | ||
|
|
5e457bf258 | ||
|
|
4be61013a1 | ||
|
|
5ad889f6fd | ||
|
|
7826f0afd9 | ||
|
|
8cdbfe69b1 | ||
|
|
0ff4a5fa39 | ||
|
|
fec32fed18 | ||
|
|
a081dea8ab | ||
|
|
0d3db58657 | ||
|
|
f2e7b29c14 | ||
|
|
e42bcf2314 | ||
|
|
223a535f3f | ||
|
|
bebbdc2067 | ||
|
|
60926ac16b | ||
|
|
44d87ff641 | ||
|
|
d614193e49 | ||
|
|
e2d7dda166 | ||
|
|
2a4044a858 | ||
|
|
6b0d017675 | ||
|
|
56bca83bde | ||
|
|
bbecd13697 | ||
|
|
725ba6cf63 | ||
|
|
37a352e9d3 | ||
|
|
56c7912159 | ||
|
|
eb6f55d880 | ||
|
|
5085a16d78 | ||
|
|
0fc98958a3 | ||
|
|
8c91d5c166 | ||
|
|
f3a39e0c9c | ||
|
|
d20ede40b1 | ||
|
|
b391e5f638 | ||
|
|
0335a25d1f | ||
|
|
b6844bec60 | ||
|
|
7d1b974bca | ||
|
|
61697864c3 | ||
|
|
efdeab3a1d | ||
|
|
ea97589624 | ||
|
|
24408cce9b | ||
|
|
f2e026a1d8 | ||
|
|
0706d633bf | ||
|
|
600dddf88d | ||
|
|
c13433aee4 | ||
|
|
78add28aab | ||
|
|
eef55f493b | ||
|
|
a45643cb3b | ||
|
|
8942047d41 | ||
|
|
e0027eba85 | ||
|
|
7039ece0a0 | ||
|
|
82b829625b | ||
|
|
737e978f5b | ||
|
|
ceb5f5079c |
@@ -1,20 +1,22 @@
|
||||
parameters:
|
||||
artifactSource: 'true'
|
||||
cache: 'false'
|
||||
|
||||
steps:
|
||||
- task: DockerInstaller@0
|
||||
displayName: 'Prepare Environment'
|
||||
inputs:
|
||||
dockerVersion: '17.09.0-ce'
|
||||
- task: CacheBeta@0
|
||||
displayName: 'Cache Build System'
|
||||
inputs:
|
||||
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
|
||||
path: $(System.DefaultWorkingDirectory)/ccache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
- ${{ if eq(parameters.cache, 'true') }}:
|
||||
- task: CacheBeta@0
|
||||
displayName: 'Cache Build System'
|
||||
inputs:
|
||||
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
|
||||
path: $(System.DefaultWorkingDirectory)/ccache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
|
||||
displayName: 'Build'
|
||||
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && ./.ci/scripts/$(ScriptFolder)/upload.sh
|
||||
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
|
||||
displayName: 'Package Artifacts'
|
||||
- publish: artifacts
|
||||
artifact: 'yuzu-$(BuildName)-$(BuildSuffix)'
|
||||
|
||||
@@ -3,7 +3,7 @@ jobs:
|
||||
displayName: 'standard'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
matrix:
|
||||
windows:
|
||||
@@ -19,4 +19,5 @@ jobs:
|
||||
needSubmodules: 'true'
|
||||
- template: ./build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
artifactSource: 'false'
|
||||
cache: $(parameters.cache)
|
||||
@@ -3,19 +3,21 @@ jobs:
|
||||
displayName: 'testing'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
strategy:
|
||||
maxParallel: 5
|
||||
matrix:
|
||||
windows:
|
||||
BuildSuffix: 'windows-testing'
|
||||
ScriptFolder: 'windows'
|
||||
steps:
|
||||
- script: sudo apt-get update && sudo apt-get --only-upgrade -y install python3-pip && pip install requests urllib3
|
||||
displayName: 'Prepare Environment'
|
||||
- task: PythonScript@0
|
||||
condition: eq(variables['Build.Reason'], 'PullRequest')
|
||||
displayName: 'Determine Testing Status'
|
||||
inputs:
|
||||
scriptSource: 'filePath'
|
||||
scriptPath: '../scripts/merge/check-label-presence.py'
|
||||
scriptPath: '.ci/scripts/merge/check-label-presence.py'
|
||||
arguments: '$(System.PullRequest.PullRequestNumber) create-testing-build'
|
||||
- ${{ if eq(variables.enabletesting, 'true') }}:
|
||||
- template: ./sync-source.yml
|
||||
@@ -27,4 +29,5 @@ jobs:
|
||||
matchLabel: 'testing-merge'
|
||||
- template: ./build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
artifactSource: 'false'
|
||||
cache: 'false'
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Windows Release'
|
||||
inputs:
|
||||
artifactName: 'yuzu-$(BuildName)-windows-mingw'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Linux Release'
|
||||
inputs:
|
||||
artifactName: 'yuzu-$(BuildName)-linux'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Release Point'
|
||||
inputs:
|
||||
artifactName: 'yuzu-$(BuildName)-release-point'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
- script: echo '##vso[task.setvariable variable=tagcommit]' && cat $(Build.ArtifactStagingDirectory)/tag-commit.sha
|
||||
displayName: 'Calculate Release Point'
|
||||
- task: GitHubRelease@0
|
||||
inputs:
|
||||
gitHubConnection: $(GitHubReleaseConnectionName)
|
||||
repositoryName: '$(GitHubReleaseRepoName)'
|
||||
action: 'create'
|
||||
target: $(variables.tagcommit)
|
||||
title: 'yuzu $(BuildName) #$(Build.BuildId)'
|
||||
assets: '$(Build.ArtifactStagingDirectory)/*'
|
||||
@@ -21,3 +21,5 @@ stages:
|
||||
dependsOn: format
|
||||
jobs:
|
||||
- template: ./templates/build-standard.yml
|
||||
parameters:
|
||||
cache: 'true'
|
||||
|
||||
@@ -15,4 +15,6 @@ stages:
|
||||
dependsOn: format
|
||||
jobs:
|
||||
- template: ./templates/build-standard.yml
|
||||
- template: ./templates/build-testing.yml
|
||||
parameters:
|
||||
cache: 'false'
|
||||
- template: ./templates/build-testing.yml
|
||||
|
||||
@@ -81,6 +81,7 @@ set(HASH_FILES
|
||||
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/shift.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/warp.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.h"
|
||||
|
||||
@@ -2,6 +2,7 @@ yuzu emulator
|
||||
=============
|
||||
[](https://travis-ci.org/yuzu-emu/yuzu)
|
||||
[](https://ci.appveyor.com/project/bunnei/yuzu)
|
||||
[](https://dev.azure.com/yuzu-emu/yuzu/)
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
|
||||
31
dist/license.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
The icons in this folder and its subfolders have the following licenses:
|
||||
|
||||
Icon Name | License | Origin/Author
|
||||
--- | --- | ---
|
||||
qt_themes/default/icons/16x16/checked.png | Free for non-commercial use
|
||||
qt_themes/default/icons/16x16/failed.png | Free for non-commercial use
|
||||
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
|
||||
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use
|
||||
qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use
|
||||
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
|
||||
qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/colorful/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/colorful/icons/48x48/plus.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/colorful/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
|
||||
|
||||
<!-- TODO: Add the license of the yuzu icon -->
|
||||
BIN
dist/qt_themes/colorful/icons/16x16/lock.png
vendored
Normal file
|
After Width: | Height: | Size: 330 B |
BIN
dist/qt_themes/colorful/icons/256x256/plus_folder.png
vendored
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/bad_folder.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/chip.png
vendored
Normal file
|
After Width: | Height: | Size: 582 B |
BIN
dist/qt_themes/colorful/icons/48x48/folder.png
vendored
Normal file
|
After Width: | Height: | Size: 460 B |
BIN
dist/qt_themes/colorful/icons/48x48/plus.png
vendored
Normal file
|
After Width: | Height: | Size: 496 B |
BIN
dist/qt_themes/colorful/icons/48x48/sd_card.png
vendored
Normal file
|
After Width: | Height: | Size: 680 B |
14
dist/qt_themes/colorful/icons/index.theme
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
[Icon Theme]
|
||||
Name=colorful
|
||||
Comment=Colorful theme
|
||||
Inherits=default
|
||||
Directories=16x16,48x48,256x256
|
||||
|
||||
[16x16]
|
||||
Size=16
|
||||
|
||||
[48x48]
|
||||
Size=48
|
||||
|
||||
[256x256]
|
||||
Size=256
|
||||
15
dist/qt_themes/colorful/style.qrc
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons/colorful">
|
||||
<file alias="index.theme">icons/index.theme</file>
|
||||
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
|
||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
|
||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="colorful">
|
||||
<file>style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
4
dist/qt_themes/colorful/style.qss
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
This file is intentionally left blank.
|
||||
We do not want to apply any stylesheet for colorful, only icons.
|
||||
*/
|
||||
BIN
dist/qt_themes/colorful_dark/icons/16x16/lock.png
vendored
Normal file
|
After Width: | Height: | Size: 401 B |
8
dist/qt_themes/colorful_dark/icons/index.theme
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
[Icon Theme]
|
||||
Name=colorful_dark
|
||||
Comment=Colorful theme (Dark style)
|
||||
Inherits=default
|
||||
Directories=16x16
|
||||
|
||||
[16x16]
|
||||
Size=16
|
||||
57
dist/qt_themes/colorful_dark/style.qrc
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons/colorful_dark">
|
||||
<file alias="index.theme">icons/index.theme</file>
|
||||
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
|
||||
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
||||
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
||||
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
||||
<file alias="48x48/plus.png">../colorful/icons/48x48/plus.png</file>
|
||||
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
||||
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
|
||||
<qresource prefix="qss_icons">
|
||||
<file alias="rc/up_arrow_disabled.png">../qdarkstyle/rc/up_arrow_disabled.png</file>
|
||||
<file alias="rc/Hmovetoolbar.png">../qdarkstyle/rc/Hmovetoolbar.png</file>
|
||||
<file alias="rc/stylesheet-branch-end.png">../qdarkstyle/rc/stylesheet-branch-end.png</file>
|
||||
<file alias="rc/branch_closed-on.png">../qdarkstyle/rc/branch_closed-on.png</file>
|
||||
<file alias="rc/stylesheet-vline.png">../qdarkstyle/rc/stylesheet-vline.png</file>
|
||||
<file alias="rc/branch_closed.png">../qdarkstyle/rc/branch_closed.png</file>
|
||||
<file alias="rc/branch_open-on.png">../qdarkstyle/rc/branch_open-on.png</file>
|
||||
<file alias="rc/transparent.png">../qdarkstyle/rc/transparent.png</file>
|
||||
<file alias="rc/right_arrow_disabled.png">../qdarkstyle/rc/right_arrow_disabled.png</file>
|
||||
<file alias="rc/sizegrip.png">../qdarkstyle/rc/sizegrip.png</file>
|
||||
<file alias="rc/close.png">../qdarkstyle/rc/close.png</file>
|
||||
<file alias="rc/close-hover.png">../qdarkstyle/rc/close-hover.png</file>
|
||||
<file alias="rc/close-pressed.png">../qdarkstyle/rc/close-pressed.png</file>
|
||||
<file alias="rc/down_arrow.png">../qdarkstyle/rc/down_arrow.png</file>
|
||||
<file alias="rc/Vmovetoolbar.png">../qdarkstyle/rc/Vmovetoolbar.png</file>
|
||||
<file alias="rc/left_arrow.png">../qdarkstyle/rc/left_arrow.png</file>
|
||||
<file alias="rc/stylesheet-branch-more.png">../qdarkstyle/rc/stylesheet-branch-more.png</file>
|
||||
<file alias="rc/up_arrow.png">../qdarkstyle/rc/up_arrow.png</file>
|
||||
<file alias="rc/right_arrow.png">../qdarkstyle/rc/right_arrow.png</file>
|
||||
<file alias="rc/left_arrow_disabled.png">../qdarkstyle/rc/left_arrow_disabled.png</file>
|
||||
<file alias="rc/Hsepartoolbar.png">../qdarkstyle/rc/Hsepartoolbar.png</file>
|
||||
<file alias="rc/branch_open.png">../qdarkstyle/rc/branch_open.png</file>
|
||||
<file alias="rc/Vsepartoolbar.png">../qdarkstyle/rc/Vsepartoolbar.png</file>
|
||||
<file alias="rc/down_arrow_disabled.png">../qdarkstyle/rc/down_arrow_disabled.png</file>
|
||||
<file alias="rc/undock.png">../qdarkstyle/rc/undock.png</file>
|
||||
<file alias="rc/checkbox_checked_disabled.png">../qdarkstyle/rc/checkbox_checked_disabled.png</file>
|
||||
<file alias="rc/checkbox_checked_focus.png">../qdarkstyle/rc/checkbox_checked_focus.png</file>
|
||||
<file alias="rc/checkbox_checked.png">../qdarkstyle/rc/checkbox_checked.png</file>
|
||||
<file alias="rc/checkbox_indeterminate.png">../qdarkstyle/rc/checkbox_indeterminate.png</file>
|
||||
<file alias="rc/checkbox_indeterminate_focus.png">../qdarkstyle/rc/checkbox_indeterminate_focus.png</file>
|
||||
<file alias="rc/checkbox_unchecked_disabled.png">../qdarkstyle/rc/checkbox_unchecked_disabled.png</file>
|
||||
<file alias="rc/checkbox_unchecked_focus.png">../qdarkstyle/rc/checkbox_unchecked_focus.png</file>
|
||||
<file alias="rc/checkbox_unchecked.png">../qdarkstyle/rc/checkbox_unchecked.png</file>
|
||||
<file alias="rc/radio_checked_disabled.png">../qdarkstyle/rc/radio_checked_disabled.png</file>
|
||||
<file alias="rc/radio_checked_focus.png">../qdarkstyle/rc/radio_checked_focus.png</file>
|
||||
<file alias="rc/radio_checked.png">../qdarkstyle/rc/radio_checked.png</file>
|
||||
<file alias="rc/radio_unchecked_disabled.png">../qdarkstyle/rc/radio_unchecked_disabled.png</file>
|
||||
<file alias="rc/radio_unchecked_focus.png">../qdarkstyle/rc/radio_unchecked_focus.png</file>
|
||||
<file alias="rc/radio_unchecked.png">../qdarkstyle/rc/radio_unchecked.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="colorful_dark">
|
||||
<file alias="style.qss">../qdarkstyle/style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
14
dist/qt_themes/default/default.qrc
vendored
@@ -5,7 +5,21 @@
|
||||
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
|
||||
|
||||
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
|
||||
|
||||
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
|
||||
|
||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||
|
||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||
|
||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||
|
||||
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
|
||||
|
||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||
|
||||
<file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
|
||||
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
BIN
dist/qt_themes/default/icons/16x16/lock.png
vendored
Normal file
|
After Width: | Height: | Size: 279 B |
BIN
dist/qt_themes/default/icons/256x256/plus_folder.png
vendored
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
dist/qt_themes/default/icons/48x48/bad_folder.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
dist/qt_themes/default/icons/48x48/chip.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
dist/qt_themes/default/icons/48x48/folder.png
vendored
Normal file
|
After Width: | Height: | Size: 410 B |
BIN
dist/qt_themes/default/icons/48x48/plus.png
vendored
Normal file
|
After Width: | Height: | Size: 316 B |
BIN
dist/qt_themes/default/icons/48x48/sd_card.png
vendored
Normal file
|
After Width: | Height: | Size: 614 B |
5
dist/qt_themes/default/icons/index.theme
vendored
@@ -1,10 +1,13 @@
|
||||
[Icon Theme]
|
||||
Name=default
|
||||
Comment=default theme
|
||||
Directories=16x16,256x256
|
||||
Directories=16x16,48x48,256x256
|
||||
|
||||
[16x16]
|
||||
Size=16
|
||||
|
||||
[48x48]
|
||||
Size=48
|
||||
|
||||
[256x256]
|
||||
Size=256
|
||||
BIN
dist/qt_themes/qdarkstyle/icons/16x16/lock.png
vendored
Normal file
|
After Width: | Height: | Size: 304 B |
BIN
dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png
vendored
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/chip.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/folder.png
vendored
Normal file
|
After Width: | Height: | Size: 542 B |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/plus.png
vendored
Normal file
|
After Width: | Height: | Size: 339 B |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png
vendored
Normal file
|
After Width: | Height: | Size: 676 B |
7
dist/qt_themes/qdarkstyle/icons/index.theme
vendored
@@ -2,10 +2,13 @@
|
||||
Name=qdarkstyle
|
||||
Comment=dark theme
|
||||
Inherits=default
|
||||
Directories=16x16,256x256
|
||||
Directories=16x16,48x48,256x256
|
||||
|
||||
[16x16]
|
||||
Size=16
|
||||
|
||||
|
||||
[48x48]
|
||||
Size=48
|
||||
|
||||
[256x256]
|
||||
Size=256
|
||||
7
dist/qt_themes/qdarkstyle/style.qrc
vendored
@@ -1,6 +1,13 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons/qdarkstyle">
|
||||
<file alias="index.theme">icons/index.theme</file>
|
||||
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
|
||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
|
||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="qss_icons">
|
||||
<file>rc/up_arrow_disabled.png</file>
|
||||
|
||||
2
externals/fmt
vendored
16
license.txt
@@ -337,3 +337,19 @@ proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
|
||||
The icons used in this project have the following licenses:
|
||||
|
||||
Icon Name | License | Origin/Author
|
||||
--- | --- | ---
|
||||
checked.png | Free for non-commercial use
|
||||
failed.png | Free for non-commercial use
|
||||
lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
plus_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
bad_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
chip.png | CC BY-ND 3.0 | https://icons8.com
|
||||
folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
|
||||
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
sd_card.png | CC BY-ND 3.0 | https://icons8.com
|
||||
|
||||
@@ -55,6 +55,7 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/shift.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/warp.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.h"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
@@ -37,4 +38,63 @@ constexpr bool IsWordAligned(T value) {
|
||||
return (value & 0b11) == 0;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t Align = 16>
|
||||
class AlignmentAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
public:
|
||||
pointer address(reference r) noexcept {
|
||||
return std::addressof(r);
|
||||
}
|
||||
|
||||
const_pointer address(const_reference r) const noexcept {
|
||||
return std::addressof(r);
|
||||
}
|
||||
|
||||
pointer allocate(size_type n) {
|
||||
return static_cast<pointer>(::operator new (n, std::align_val_t{Align}));
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type) {
|
||||
::operator delete (p, std::align_val_t{Align});
|
||||
}
|
||||
|
||||
void construct(pointer p, const value_type& wert) {
|
||||
new (p) value_type(wert);
|
||||
}
|
||||
|
||||
void destroy(pointer p) {
|
||||
p->~value_type();
|
||||
}
|
||||
|
||||
size_type max_size() const noexcept {
|
||||
return size_type(-1) / sizeof(value_type);
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
struct rebind {
|
||||
using other = AlignmentAllocator<T2, Align>;
|
||||
};
|
||||
|
||||
bool operator!=(const AlignmentAllocator<T, Align>& other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// Returns true if and only if storage allocated from *this
|
||||
// can be deallocated from other, and vice versa.
|
||||
// Always returns true for stateless allocators.
|
||||
bool operator==(const AlignmentAllocator<T, Align>& other) const noexcept {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -111,6 +111,8 @@ add_library(core STATIC
|
||||
frontend/scope_acquire_window_context.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hardware_interrupt_manager.cpp
|
||||
hardware_interrupt_manager.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
hle/kernel/address_arbiter.cpp
|
||||
@@ -372,6 +374,7 @@ add_library(core STATIC
|
||||
hle/service/nvdrv/devices/nvmap.h
|
||||
hle/service/nvdrv/interface.cpp
|
||||
hle/service/nvdrv/interface.h
|
||||
hle/service/nvdrv/nvdata.h
|
||||
hle/service/nvdrv/nvdrv.cpp
|
||||
hle/service/nvdrv/nvdrv.h
|
||||
hle/service/nvdrv/nvmemp.cpp
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -151,7 +152,7 @@ struct System::Impl {
|
||||
if (!renderer->Init()) {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
gpu_core = VideoCore::CreateGPU(system);
|
||||
|
||||
is_powered_on = true;
|
||||
@@ -298,6 +299,7 @@ struct System::Impl {
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
|
||||
CpuCoreManager cpu_core_manager;
|
||||
bool is_powered_on = false;
|
||||
|
||||
@@ -444,6 +446,14 @@ const Tegra::GPU& System::GPU() const {
|
||||
return *impl->gpu_core;
|
||||
}
|
||||
|
||||
Core::Hardware::InterruptManager& System::InterruptManager() {
|
||||
return *impl->interrupt_manager;
|
||||
}
|
||||
|
||||
const Core::Hardware::InterruptManager& System::InterruptManager() const {
|
||||
return *impl->interrupt_manager;
|
||||
}
|
||||
|
||||
VideoCore::RendererBase& System::Renderer() {
|
||||
return *impl->renderer;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,10 @@ namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Core::Hardware {
|
||||
class InterruptManager;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
@@ -234,6 +238,12 @@ public:
|
||||
/// Provides a constant reference to the core timing instance.
|
||||
const Timing::CoreTiming& CoreTiming() const;
|
||||
|
||||
/// Provides a reference to the interrupt manager instance.
|
||||
Core::Hardware::InterruptManager& InterruptManager();
|
||||
|
||||
/// Provides a constant reference to the interrupt manager instance.
|
||||
const Core::Hardware::InterruptManager& InterruptManager() const;
|
||||
|
||||
/// Provides a reference to the kernel instance.
|
||||
Kernel::KernelCore& Kernel();
|
||||
|
||||
|
||||
30
src/core/hardware_interrupt_manager.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2019 Yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/service/nvdrv/interface.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Core::Hardware {
|
||||
|
||||
InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
|
||||
gpu_interrupt_event =
|
||||
system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) {
|
||||
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
|
||||
const u32 syncpt = static_cast<u32>(message >> 32);
|
||||
const u32 value = static_cast<u32>(message);
|
||||
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
|
||||
});
|
||||
}
|
||||
|
||||
InterruptManager::~InterruptManager() = default;
|
||||
|
||||
void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
|
||||
const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
|
||||
system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg);
|
||||
}
|
||||
|
||||
} // namespace Core::Hardware
|
||||
31
src/core/hardware_interrupt_manager.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2019 Yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Core::Hardware {
|
||||
|
||||
class InterruptManager {
|
||||
public:
|
||||
explicit InterruptManager(Core::System& system);
|
||||
~InterruptManager();
|
||||
|
||||
void GPUInterruptSyncpt(u32 syncpoint_id, u32 value);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
Core::Timing::EventType* gpu_interrupt_event{};
|
||||
};
|
||||
|
||||
} // namespace Core::Hardware
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -77,7 +78,7 @@ struct CodeSet final {
|
||||
}
|
||||
|
||||
/// The overall data that backs this code set.
|
||||
std::vector<u8> memory;
|
||||
Kernel::PhysicalMemory memory;
|
||||
|
||||
/// The segments that comprise this code set.
|
||||
std::array<Segment, 3> segments;
|
||||
|
||||
19
src/core/hle/kernel/physical_memory.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/alignment.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
// This encapsulation serves 2 purposes:
|
||||
// - First, to encapsulate host physical memory under a single type and set an
|
||||
// standard for managing it.
|
||||
// - Second to ensure all host backing memory used is aligned to 256 bytes due
|
||||
// to strict alignment restrictions on GPU memory.
|
||||
|
||||
using PhysicalMemory = std::vector<u8, Common::AlignmentAllocator<u8, 256>>;
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -184,19 +184,11 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
}
|
||||
|
||||
void Process::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
// The kernel always ensures that the given stack size is page aligned.
|
||||
main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
|
||||
|
||||
// Allocate and map the main thread stack
|
||||
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
|
||||
// of the user address space.
|
||||
const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
|
||||
vm_manager
|
||||
.MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
|
||||
0, main_thread_stack_size, MemoryState::Stack)
|
||||
.Unwrap();
|
||||
AllocateMainThreadStack(stack_size);
|
||||
tls_region_address = CreateTLSRegion();
|
||||
|
||||
vm_manager.LogLayout();
|
||||
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
SetupMainThread(*this, kernel, main_thread_priority);
|
||||
@@ -226,6 +218,9 @@ void Process::PrepareForTermination() {
|
||||
stop_threads(system.Scheduler(2).GetThreadList());
|
||||
stop_threads(system.Scheduler(3).GetThreadList());
|
||||
|
||||
FreeTLSRegion(tls_region_address);
|
||||
tls_region_address = 0;
|
||||
|
||||
ChangeStatus(ProcessStatus::Exited);
|
||||
}
|
||||
|
||||
@@ -252,7 +247,7 @@ VAddr Process::CreateTLSRegion() {
|
||||
ASSERT(region_address.Succeeded());
|
||||
|
||||
const auto map_result = vm_manager.MapMemoryBlock(
|
||||
*region_address, std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE), 0,
|
||||
*region_address, std::make_shared<PhysicalMemory>(Memory::PAGE_SIZE), 0,
|
||||
Memory::PAGE_SIZE, MemoryState::ThreadLocal);
|
||||
ASSERT(map_result.Succeeded());
|
||||
|
||||
@@ -282,7 +277,7 @@ void Process::FreeTLSRegion(VAddr tls_address) {
|
||||
}
|
||||
|
||||
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
|
||||
const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory));
|
||||
|
||||
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
|
||||
MemoryState memory_state) {
|
||||
@@ -325,4 +320,16 @@ void Process::ChangeStatus(ProcessStatus new_status) {
|
||||
WakeupAllWaitingThreads();
|
||||
}
|
||||
|
||||
void Process::AllocateMainThreadStack(u64 stack_size) {
|
||||
// The kernel always ensures that the given stack size is page aligned.
|
||||
main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
|
||||
|
||||
// Allocate and map the main thread stack
|
||||
const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
|
||||
vm_manager
|
||||
.MapMemoryBlock(mapping_address, std::make_shared<PhysicalMemory>(main_thread_stack_size),
|
||||
0, main_thread_stack_size, MemoryState::Stack)
|
||||
.Unwrap();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -135,6 +135,11 @@ public:
|
||||
return mutex;
|
||||
}
|
||||
|
||||
/// Gets the address to the process' dedicated TLS region.
|
||||
VAddr GetTLSRegionAddress() const {
|
||||
return tls_region_address;
|
||||
}
|
||||
|
||||
/// Gets the current status of the process
|
||||
ProcessStatus GetStatus() const {
|
||||
return status;
|
||||
@@ -296,6 +301,9 @@ private:
|
||||
/// a process signal.
|
||||
void ChangeStatus(ProcessStatus new_status);
|
||||
|
||||
/// Allocates the main thread stack for the process, given the stack size in bytes.
|
||||
void AllocateMainThreadStack(u64 stack_size);
|
||||
|
||||
/// Memory manager for this process.
|
||||
Kernel::VMManager vm_manager;
|
||||
|
||||
@@ -358,6 +366,9 @@ private:
|
||||
/// variable related facilities.
|
||||
Mutex mutex;
|
||||
|
||||
/// Address indicating the location of the process' dedicated TLS region.
|
||||
VAddr tls_region_address = 0;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
|
||||
shared_memory->other_permissions = other_permissions;
|
||||
|
||||
if (address == 0) {
|
||||
shared_memory->backing_block = std::make_shared<std::vector<u8>>(size);
|
||||
shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size);
|
||||
shared_memory->backing_block_offset = 0;
|
||||
|
||||
// Refresh the address mappings for the current process.
|
||||
@@ -59,8 +59,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
|
||||
}
|
||||
|
||||
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
|
||||
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
|
||||
KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
|
||||
u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
|
||||
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
|
||||
|
||||
shared_memory->owner_process = nullptr;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -62,12 +63,10 @@ public:
|
||||
* block.
|
||||
* @param name Optional object name, used for debugging purposes.
|
||||
*/
|
||||
static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
|
||||
std::shared_ptr<std::vector<u8>> heap_block,
|
||||
std::size_t offset, u64 size,
|
||||
MemoryPermission permissions,
|
||||
MemoryPermission other_permissions,
|
||||
std::string name = "Unknown Applet");
|
||||
static SharedPtr<SharedMemory> CreateForApplet(
|
||||
KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
|
||||
u64 size, MemoryPermission permissions, MemoryPermission other_permissions,
|
||||
std::string name = "Unknown Applet");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "SharedMemory";
|
||||
@@ -135,7 +134,7 @@ private:
|
||||
~SharedMemory() override;
|
||||
|
||||
/// Backing memory for this shared memory block.
|
||||
std::shared_ptr<std::vector<u8>> backing_block;
|
||||
std::shared_ptr<PhysicalMemory> backing_block;
|
||||
/// Offset into the backing block for this shared memory.
|
||||
std::size_t backing_block_offset = 0;
|
||||
/// Size of the memory block. Page-aligned.
|
||||
|
||||
@@ -843,9 +843,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query user exception context address, returned 0");
|
||||
*result = 0;
|
||||
*result = process->GetTLSRegionAddress();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
|
||||
@@ -1739,8 +1737,8 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
|
||||
// Wait for an address (via Address Arbiter)
|
||||
static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
|
||||
s64 timeout) {
|
||||
LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
|
||||
address, type, value, timeout);
|
||||
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address,
|
||||
type, value, timeout);
|
||||
|
||||
// If the passed address is a kernel virtual address, return invalid memory state.
|
||||
if (Memory::IsKernelVirtualAddress(address)) {
|
||||
@@ -1762,8 +1760,8 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
|
||||
// Signals to an address (via Address Arbiter)
|
||||
static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
|
||||
s32 num_to_wake) {
|
||||
LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
|
||||
address, type, value, num_to_wake);
|
||||
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
|
||||
address, type, value, num_to_wake);
|
||||
|
||||
// If the passed address is a kernel virtual address, return invalid memory state.
|
||||
if (Memory::IsKernelVirtualAddress(address)) {
|
||||
|
||||
@@ -47,7 +47,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
backing_block = std::make_shared<std::vector<u8>>(size);
|
||||
backing_block = std::make_shared<PhysicalMemory>(size);
|
||||
|
||||
const auto map_state = owner_permissions == MemoryPermission::None
|
||||
? MemoryState::TransferMemoryIsolated
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
@@ -82,7 +83,7 @@ private:
|
||||
~TransferMemory() override;
|
||||
|
||||
/// Memory block backing this instance.
|
||||
std::shared_ptr<std::vector<u8>> backing_block;
|
||||
std::shared_ptr<PhysicalMemory> backing_block;
|
||||
|
||||
/// The base address for the memory managed by this instance.
|
||||
VAddr base_address = 0;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_hook.h"
|
||||
@@ -103,7 +104,7 @@ bool VMManager::IsValidHandle(VMAHandle handle) const {
|
||||
}
|
||||
|
||||
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
||||
std::shared_ptr<std::vector<u8>> block,
|
||||
std::shared_ptr<PhysicalMemory> block,
|
||||
std::size_t offset, u64 size,
|
||||
MemoryState state, VMAPermission perm) {
|
||||
ASSERT(block != nullptr);
|
||||
@@ -260,7 +261,7 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
|
||||
|
||||
if (heap_memory == nullptr) {
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>(size);
|
||||
heap_memory = std::make_shared<PhysicalMemory>(size);
|
||||
heap_end = heap_region_base + size;
|
||||
} else {
|
||||
UnmapRange(heap_region_base, GetCurrentHeapSize());
|
||||
@@ -341,7 +342,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||
const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
|
||||
if (vma.state == MemoryState::Unmapped) {
|
||||
const auto map_res =
|
||||
MapMemoryBlock(cur_addr, std::make_shared<std::vector<u8>>(map_size, 0), 0,
|
||||
MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size, 0), 0,
|
||||
map_size, MemoryState::Heap, VMAPermission::ReadWrite);
|
||||
result = map_res.Code();
|
||||
if (result.IsError()) {
|
||||
@@ -442,7 +443,7 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
|
||||
if (result.IsError()) {
|
||||
for (const auto [map_address, map_size] : unmapped_regions) {
|
||||
const auto remap_res =
|
||||
MapMemoryBlock(map_address, std::make_shared<std::vector<u8>>(map_size, 0), 0,
|
||||
MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size, 0), 0,
|
||||
map_size, MemoryState::Heap, VMAPermission::None);
|
||||
ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error");
|
||||
}
|
||||
@@ -593,7 +594,7 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
|
||||
ASSERT_MSG(vma_offset + size <= vma->second.size,
|
||||
"Shared memory exceeds bounds of mapped block");
|
||||
|
||||
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
|
||||
const std::shared_ptr<PhysicalMemory>& backing_block = vma->second.backing_block;
|
||||
const std::size_t backing_block_offset = vma->second.offset + vma_offset;
|
||||
|
||||
CASCADE_RESULT(auto new_vma,
|
||||
@@ -606,7 +607,7 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
|
||||
void VMManager::RefreshMemoryBlockMappings(const PhysicalMemory* block) {
|
||||
// If this ever proves to have a noticeable performance impact, allow users of the function to
|
||||
// specify a specific range of addresses to limit the scan to.
|
||||
for (const auto& p : vma_map) {
|
||||
@@ -764,7 +765,7 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre
|
||||
right.backing_block->begin() + right.offset + right.size);
|
||||
} else {
|
||||
// Slow case: make a new memory block for left and right.
|
||||
auto new_memory = std::make_shared<std::vector<u8>>();
|
||||
auto new_memory = std::make_shared<PhysicalMemory>();
|
||||
new_memory->insert(new_memory->end(), left.backing_block->begin() + left.offset,
|
||||
left.backing_block->begin() + left.offset + left.size);
|
||||
new_memory->insert(new_memory->end(), right.backing_block->begin() + right.offset,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_hook.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
|
||||
|
||||
// Settings for type = AllocatedMemoryBlock
|
||||
/// Memory block backing this VMA.
|
||||
std::shared_ptr<std::vector<u8>> backing_block = nullptr;
|
||||
std::shared_ptr<PhysicalMemory> backing_block = nullptr;
|
||||
/// Offset into the backing_memory the mapping starts from.
|
||||
std::size_t offset = 0;
|
||||
|
||||
@@ -348,7 +349,7 @@ public:
|
||||
* @param size Size of the mapping.
|
||||
* @param state MemoryState tag to attach to the VMA.
|
||||
*/
|
||||
ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
|
||||
ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block,
|
||||
std::size_t offset, u64 size, MemoryState state,
|
||||
VMAPermission perm = VMAPermission::ReadWrite);
|
||||
|
||||
@@ -547,7 +548,7 @@ public:
|
||||
* Scans all VMAs and updates the page table range of any that use the given vector as backing
|
||||
* memory. This should be called after any operation that causes reallocation of the vector.
|
||||
*/
|
||||
void RefreshMemoryBlockMappings(const std::vector<u8>* block);
|
||||
void RefreshMemoryBlockMappings(const PhysicalMemory* block);
|
||||
|
||||
/// Dumps the address space layout to the log, for debugging
|
||||
void LogLayout() const;
|
||||
@@ -777,7 +778,7 @@ private:
|
||||
// the entire virtual address space extents that bound the allocations, including any holes.
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
|
||||
std::shared_ptr<std::vector<u8>> heap_memory;
|
||||
std::shared_ptr<PhysicalMemory> heap_memory;
|
||||
|
||||
// The end of the currently allocated heap. This is not an inclusive
|
||||
// end of the range. This is essentially 'base_address + current_size'.
|
||||
|
||||
@@ -1057,6 +1057,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{120, nullptr, "ExecuteProgram"},
|
||||
{121, nullptr, "ClearUserChannel"},
|
||||
{122, nullptr, "UnpopToUserChannel"},
|
||||
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
|
||||
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
|
||||
{1000, nullptr, "CreateMovieMaker"},
|
||||
{1001, nullptr, "PrepareForJit"},
|
||||
@@ -1064,6 +1065,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
|
||||
}
|
||||
|
||||
IApplicationFunctions::~IApplicationFunctions() = default;
|
||||
@@ -1285,6 +1290,14 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(size.journal);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(gpu_error_detected_event.readable);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
|
||||
@@ -242,6 +242,9 @@ private:
|
||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
|
||||
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Kernel::EventPair gpu_error_detected_event;
|
||||
};
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
std::make_shared<AudCtl>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudOutA>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudOutU>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudOutU>(system)->InstallAsService(service_manager);
|
||||
std::make_shared<AudInA>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudInU>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudRecA>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudRecU>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudRenA>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudRenU>()->InstallAsService(service_manager);
|
||||
std::make_shared<AudRenU>(system)->InstallAsService(service_manager);
|
||||
std::make_shared<CodecCtl>()->InstallAsService(service_manager);
|
||||
std::make_shared<HwOpus>()->InstallAsService(service_manager);
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
@@ -11,6 +15,6 @@ class ServiceManager;
|
||||
namespace Service::Audio {
|
||||
|
||||
/// Registers all Audio services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -40,8 +40,8 @@ enum class AudioState : u32 {
|
||||
|
||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
|
||||
std::string&& unique_name)
|
||||
IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core,
|
||||
std::string&& device_name, std::string&& unique_name)
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core),
|
||||
device_name(std::move(device_name)), audio_params(audio_params) {
|
||||
// clang-format off
|
||||
@@ -65,7 +65,6 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
// This is the event handle used to check if the audio buffer was released
|
||||
auto& system = Core::System::GetInstance();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
|
||||
|
||||
@@ -212,6 +211,22 @@ private:
|
||||
Kernel::EventPair buffer_event;
|
||||
};
|
||||
|
||||
AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
|
||||
{2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
|
||||
{3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
audio_core = std::make_unique<AudioCore::AudioOut>();
|
||||
}
|
||||
|
||||
AudOutU::~AudOutU() = default;
|
||||
|
||||
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
@@ -248,7 +263,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
|
||||
auto audio_out_interface = std::make_shared<IAudioOut>(
|
||||
params, *audio_core, std::move(device_name), std::move(unique_name));
|
||||
system, params, *audio_core, std::move(device_name), std::move(unique_name));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -256,20 +271,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(params.channel_count);
|
||||
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
|
||||
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
|
||||
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
|
||||
rb.PushIpcInterface<IAudioOut>(audio_out_interface);
|
||||
|
||||
audio_out_interfaces.push_back(std::move(audio_out_interface));
|
||||
}
|
||||
|
||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
||||
static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
|
||||
{2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
|
||||
{3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
|
||||
RegisterHandlers(functions);
|
||||
audio_core = std::make_unique<AudioCore::AudioOut>();
|
||||
}
|
||||
|
||||
AudOutU::~AudOutU() = default;
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace AudioCore {
|
||||
class AudioOut;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
}
|
||||
@@ -21,15 +25,17 @@ class IAudioOut;
|
||||
|
||||
class AudOutU final : public ServiceFramework<AudOutU> {
|
||||
public:
|
||||
AudOutU();
|
||||
explicit AudOutU(Core::System& system_);
|
||||
~AudOutU() override;
|
||||
|
||||
private:
|
||||
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
|
||||
std::unique_ptr<AudioCore::AudioOut> audio_core;
|
||||
|
||||
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "common/alignment.h"
|
||||
@@ -25,7 +26,7 @@ namespace Service::Audio {
|
||||
|
||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
|
||||
public:
|
||||
explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params,
|
||||
explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params,
|
||||
const std::size_t instance_number)
|
||||
: ServiceFramework("IAudioRenderer") {
|
||||
// clang-format off
|
||||
@@ -46,7 +47,6 @@ public:
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
system_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(
|
||||
@@ -160,28 +160,33 @@ private:
|
||||
|
||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
public:
|
||||
IAudioDevice() : ServiceFramework("IAudioDevice") {
|
||||
explicit IAudioDevice(Core::System& system, u32_le revision_num)
|
||||
: ServiceFramework("IAudioDevice"), revision{revision_num} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
|
||||
{2, nullptr, "GetAudioDeviceOutputVolume"},
|
||||
{2, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolume"},
|
||||
{3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
|
||||
{4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
|
||||
{5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
|
||||
{6, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceNameAuto"},
|
||||
{7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{8, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, nullptr, "QueryAudioDeviceInputEvent"},
|
||||
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
|
||||
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
|
||||
{13, nullptr, "GetAudioSystemMasterVolumeSetting"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IAudioOutBufferReleasedEvent");
|
||||
|
||||
// Should be similar to audio_output_device_switch_event
|
||||
audio_input_device_switch_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioInputDeviceSwitchedEvent");
|
||||
|
||||
// Should only be signalled when an audio output device has been changed, example: speaker
|
||||
// to headset
|
||||
audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair(
|
||||
@@ -189,15 +194,47 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
using AudioDeviceName = std::array<char, 256>;
|
||||
static constexpr std::array<std::string_view, 4> audio_device_names{{
|
||||
"AudioStereoJackOutput",
|
||||
"AudioBuiltInSpeakerOutput",
|
||||
"AudioTvOutput",
|
||||
"AudioUsbDeviceOutput",
|
||||
}};
|
||||
enum class DeviceType {
|
||||
AHUBHeadphones,
|
||||
AHUBSpeakers,
|
||||
HDA,
|
||||
USBOutput,
|
||||
};
|
||||
|
||||
constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
|
||||
ctx.WriteBuffer(audio_interface);
|
||||
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const bool usb_output_supported =
|
||||
IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
|
||||
const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
|
||||
|
||||
std::vector<AudioDeviceName> name_buffer;
|
||||
name_buffer.reserve(audio_device_names.size());
|
||||
|
||||
for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
|
||||
const auto type = static_cast<DeviceType>(i);
|
||||
|
||||
if (!usb_output_supported && type == DeviceType::USBOutput) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& device_name = audio_device_names[i];
|
||||
auto& entry = name_buffer.emplace_back();
|
||||
device_name.copy(entry.data(), device_name.size());
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(name_buffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(1);
|
||||
rb.Push(static_cast<u32>(name_buffer.size()));
|
||||
}
|
||||
|
||||
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
|
||||
@@ -213,15 +250,32 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
void GetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
|
||||
ctx.WriteBuffer(audio_interface);
|
||||
const auto device_name_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(device_name_buffer);
|
||||
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(1);
|
||||
rb.Push(1.0f);
|
||||
}
|
||||
|
||||
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
// Currently set to always be TV audio output.
|
||||
const auto& device_name = audio_device_names[2];
|
||||
|
||||
AudioDeviceName out_device_name{};
|
||||
device_name.copy(out_device_name.data(), device_name.size());
|
||||
|
||||
ctx.WriteBuffer(out_device_name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
@@ -242,6 +296,15 @@ private:
|
||||
rb.Push<u32>(1);
|
||||
}
|
||||
|
||||
// Should be similar to QueryAudioDeviceOutputEvent
|
||||
void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(audio_input_device_switch_event.readable);
|
||||
}
|
||||
|
||||
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
@@ -250,12 +313,14 @@ private:
|
||||
rb.PushCopyObjects(audio_output_device_switch_event.readable);
|
||||
}
|
||||
|
||||
u32_le revision = 0;
|
||||
Kernel::EventPair buffer_event;
|
||||
Kernel::EventPair audio_input_device_switch_event;
|
||||
Kernel::EventPair audio_output_device_switch_event;
|
||||
|
||||
}; // namespace Audio
|
||||
|
||||
AudRenU::AudRenU() : ServiceFramework("audren:u") {
|
||||
AudRenU::AudRenU(Core::System& system_) : ServiceFramework("audren:u"), system{system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
|
||||
@@ -328,7 +393,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
};
|
||||
|
||||
// Calculates the portion of the size related to the mix data (and the sorting thereof).
|
||||
const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) {
|
||||
const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
// The size of the mixing info data structure.
|
||||
constexpr u64 mix_info_size = 0x940;
|
||||
|
||||
@@ -400,7 +465,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
// Calculates the part of the size related to the splitter context.
|
||||
const auto calculate_splitter_context_size =
|
||||
[this](const AudioCore::AudioRendererParameter& params) -> u64 {
|
||||
[](const AudioCore::AudioRendererParameter& params) -> u64 {
|
||||
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -447,7 +512,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to performance statistics.
|
||||
const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) {
|
||||
const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
// Extra size value appended to the end of the calculation.
|
||||
constexpr u64 appended = 128;
|
||||
|
||||
@@ -474,78 +539,76 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
};
|
||||
|
||||
// Calculates the part of the size that relates to the audio command buffer.
|
||||
const auto calculate_command_buffer_size =
|
||||
[this](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
|
||||
const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
|
||||
|
||||
if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
|
||||
constexpr u64 command_buffer_size = 0x18000;
|
||||
if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
|
||||
constexpr u64 command_buffer_size = 0x18000;
|
||||
|
||||
return command_buffer_size + alignment;
|
||||
}
|
||||
return command_buffer_size + alignment;
|
||||
}
|
||||
|
||||
// When the variadic command buffer is supported, this means
|
||||
// the command generator for the audio renderer can issue commands
|
||||
// that are (as one would expect), variable in size. So what we need to do
|
||||
// is determine the maximum possible size for a few command data structures
|
||||
// then multiply them by the amount of present commands indicated by the given
|
||||
// respective audio parameters.
|
||||
// When the variadic command buffer is supported, this means
|
||||
// the command generator for the audio renderer can issue commands
|
||||
// that are (as one would expect), variable in size. So what we need to do
|
||||
// is determine the maximum possible size for a few command data structures
|
||||
// then multiply them by the amount of present commands indicated by the given
|
||||
// respective audio parameters.
|
||||
|
||||
constexpr u64 max_biquad_filters = 2;
|
||||
constexpr u64 max_mix_buffers = 24;
|
||||
constexpr u64 max_biquad_filters = 2;
|
||||
constexpr u64 max_mix_buffers = 24;
|
||||
|
||||
constexpr u64 biquad_filter_command_size = 0x2C;
|
||||
constexpr u64 biquad_filter_command_size = 0x2C;
|
||||
|
||||
constexpr u64 depop_mix_command_size = 0x24;
|
||||
constexpr u64 depop_setup_command_size = 0x50;
|
||||
constexpr u64 depop_mix_command_size = 0x24;
|
||||
constexpr u64 depop_setup_command_size = 0x50;
|
||||
|
||||
constexpr u64 effect_command_max_size = 0x540;
|
||||
constexpr u64 effect_command_max_size = 0x540;
|
||||
|
||||
constexpr u64 mix_command_size = 0x1C;
|
||||
constexpr u64 mix_ramp_command_size = 0x24;
|
||||
constexpr u64 mix_ramp_grouped_command_size = 0x13C;
|
||||
constexpr u64 mix_command_size = 0x1C;
|
||||
constexpr u64 mix_ramp_command_size = 0x24;
|
||||
constexpr u64 mix_ramp_grouped_command_size = 0x13C;
|
||||
|
||||
constexpr u64 perf_command_size = 0x28;
|
||||
constexpr u64 perf_command_size = 0x28;
|
||||
|
||||
constexpr u64 sink_command_size = 0x130;
|
||||
constexpr u64 sink_command_size = 0x130;
|
||||
|
||||
constexpr u64 submix_command_max_size =
|
||||
depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
|
||||
constexpr u64 submix_command_max_size =
|
||||
depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
|
||||
|
||||
constexpr u64 volume_command_size = 0x1C;
|
||||
constexpr u64 volume_ramp_command_size = 0x20;
|
||||
constexpr u64 volume_command_size = 0x1C;
|
||||
constexpr u64 volume_ramp_command_size = 0x20;
|
||||
|
||||
constexpr u64 voice_biquad_filter_command_size =
|
||||
biquad_filter_command_size * max_biquad_filters;
|
||||
constexpr u64 voice_data_command_size = 0x9C;
|
||||
const u64 voice_command_max_size =
|
||||
(params.splitter_count * depop_setup_command_size) +
|
||||
(voice_data_command_size + voice_biquad_filter_command_size +
|
||||
volume_ramp_command_size + mix_ramp_grouped_command_size);
|
||||
constexpr u64 voice_biquad_filter_command_size =
|
||||
biquad_filter_command_size * max_biquad_filters;
|
||||
constexpr u64 voice_data_command_size = 0x9C;
|
||||
const u64 voice_command_max_size =
|
||||
(params.splitter_count * depop_setup_command_size) +
|
||||
(voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size +
|
||||
mix_ramp_grouped_command_size);
|
||||
|
||||
// Now calculate the individual elements that comprise the size and add them together.
|
||||
const u64 effect_commands_size = params.effect_count * effect_command_max_size;
|
||||
// Now calculate the individual elements that comprise the size and add them together.
|
||||
const u64 effect_commands_size = params.effect_count * effect_command_max_size;
|
||||
|
||||
const u64 final_mix_commands_size =
|
||||
depop_mix_command_size + volume_command_size * max_mix_buffers;
|
||||
const u64 final_mix_commands_size =
|
||||
depop_mix_command_size + volume_command_size * max_mix_buffers;
|
||||
|
||||
const u64 perf_commands_size =
|
||||
perf_command_size *
|
||||
(CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
|
||||
const u64 perf_commands_size =
|
||||
perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
|
||||
|
||||
const u64 sink_commands_size = params.sink_count * sink_command_size;
|
||||
const u64 sink_commands_size = params.sink_count * sink_command_size;
|
||||
|
||||
const u64 splitter_commands_size =
|
||||
params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
|
||||
const u64 splitter_commands_size =
|
||||
params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
|
||||
|
||||
const u64 submix_commands_size = params.submix_count * submix_command_max_size;
|
||||
const u64 submix_commands_size = params.submix_count * submix_command_max_size;
|
||||
|
||||
const u64 voice_commands_size = params.voice_count * voice_command_max_size;
|
||||
const u64 voice_commands_size = params.voice_count * voice_command_max_size;
|
||||
|
||||
return effect_commands_size + final_mix_commands_size + perf_commands_size +
|
||||
sink_commands_size + splitter_commands_size + submix_commands_size +
|
||||
voice_commands_size + alignment;
|
||||
};
|
||||
return effect_commands_size + final_mix_commands_size + perf_commands_size +
|
||||
sink_commands_size + splitter_commands_size + submix_commands_size +
|
||||
voice_commands_size + alignment;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
|
||||
@@ -578,12 +641,16 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 aruid = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
|
||||
|
||||
// Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
|
||||
// always assumes the initial release revision (REV1).
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<Audio::IAudioDevice>();
|
||||
rb.PushIpcInterface<IAudioDevice>(system, Common::MakeMagic('R', 'E', 'V', '1'));
|
||||
}
|
||||
|
||||
void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) {
|
||||
@@ -593,13 +660,19 @@ void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
struct Parameters {
|
||||
u32 revision;
|
||||
u64 aruid;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [revision, aruid] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different
|
||||
// based on the current revision
|
||||
rb.PushIpcInterface<IAudioDevice>(system, revision);
|
||||
}
|
||||
|
||||
void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
|
||||
@@ -608,14 +681,16 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IAudioRenderer>(params, audren_instance_count++);
|
||||
rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
|
||||
}
|
||||
|
||||
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
|
||||
// Byte swap
|
||||
const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
|
||||
|
||||
switch (feature) {
|
||||
case AudioFeatures::AudioUSBDeviceOutput:
|
||||
return version_num >= 4U;
|
||||
case AudioFeatures::Splitter:
|
||||
return version_num >= 2U;
|
||||
case AudioFeatures::PerformanceMetricsVersion2:
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
}
|
||||
@@ -14,7 +18,7 @@ namespace Service::Audio {
|
||||
|
||||
class AudRenU final : public ServiceFramework<AudRenU> {
|
||||
public:
|
||||
explicit AudRenU();
|
||||
explicit AudRenU(Core::System& system_);
|
||||
~AudRenU() override;
|
||||
|
||||
private:
|
||||
@@ -26,14 +30,19 @@ private:
|
||||
|
||||
void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
Splitter,
|
||||
PerformanceMetricsVersion2,
|
||||
VariadicCommandBuffer,
|
||||
};
|
||||
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
|
||||
std::size_t audren_instance_count = 0;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
// Describes a particular audio feature that may be supported in a particular revision.
|
||||
enum class AudioFeatures : u32 {
|
||||
AudioUSBDeviceOutput,
|
||||
Splitter,
|
||||
PerformanceMetricsVersion2,
|
||||
VariadicCommandBuffer,
|
||||
};
|
||||
|
||||
// Tests if a particular audio feature is supported with a given audio revision.
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fmt/time.h>
|
||||
#include <fmt/chrono.h>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
|
||||
@@ -636,10 +636,15 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
|
||||
return LedPattern{0, 0, 0, 0};
|
||||
};
|
||||
}
|
||||
|
||||
void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
|
||||
can_controllers_vibrate = can_vibrate;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsVibrationEnabled() const {
|
||||
return can_controllers_vibrate;
|
||||
}
|
||||
|
||||
void Controller_NPad::ClearAllConnectedControllers() {
|
||||
for (auto& controller : connected_controllers) {
|
||||
if (controller.is_connected && controller.type != NPadControllerType::None) {
|
||||
@@ -648,6 +653,7 @@ void Controller_NPad::ClearAllConnectedControllers() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectAllConnectedControllers() {
|
||||
std::for_each(connected_controllers.begin(), connected_controllers.end(),
|
||||
[](ControllerHolder& controller) { controller.is_connected = false; });
|
||||
|
||||
@@ -119,6 +119,7 @@ public:
|
||||
void DisconnectNPad(u32 npad_id);
|
||||
LedPattern GetLedPattern(u32 npad_id);
|
||||
void SetVibrationEnabled(bool can_vibrate);
|
||||
bool IsVibrationEnabled() const;
|
||||
void ClearAllConnectedControllers();
|
||||
void DisconnectAllConnectedControllers();
|
||||
void ConnectAllDisconnectedControllers();
|
||||
|
||||
@@ -216,8 +216,8 @@ Hid::Hid() : ServiceFramework("hid") {
|
||||
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
|
||||
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
|
||||
{203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
|
||||
{204, nullptr, "PermitVibration"},
|
||||
{205, nullptr, "IsVibrationPermitted"},
|
||||
{204, &Hid::PermitVibration, "PermitVibration"},
|
||||
{205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"},
|
||||
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
|
||||
{207, nullptr, "SendVibrationGcErmCommand"},
|
||||
{208, nullptr, "GetActualVibrationGcErmCommand"},
|
||||
@@ -679,6 +679,27 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IActiveVibrationDeviceList>();
|
||||
}
|
||||
|
||||
void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto can_vibrate{rp.Pop<bool>()};
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetVibrationEnabled(can_vibrate);
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
|
||||
}
|
||||
|
||||
void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
@@ -114,6 +114,8 @@ private:
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
|
||||
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
|
||||
void PermitVibration(Kernel::HLERequestContext& ctx);
|
||||
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
|
||||
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -175,6 +175,10 @@ MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
|
||||
} // namespace
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source) {
|
||||
if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) {
|
||||
return os << "[UNKNOWN SOURCE]";
|
||||
}
|
||||
|
||||
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ enum class LoadState : u32 {
|
||||
Done = 1,
|
||||
};
|
||||
|
||||
static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
|
||||
static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output,
|
||||
std::size_t& offset) {
|
||||
ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
|
||||
"Shared fonts exceeds 17mb!");
|
||||
@@ -94,7 +94,7 @@ static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& ou
|
||||
offset += transformed_font.size() * sizeof(u32);
|
||||
}
|
||||
|
||||
static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
static void EncryptSharedFont(const std::vector<u8>& input, Kernel::PhysicalMemory& output,
|
||||
std::size_t& offset) {
|
||||
ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!");
|
||||
const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT;
|
||||
@@ -121,7 +121,7 @@ struct PL_U::Impl {
|
||||
return shared_font_regions.at(index);
|
||||
}
|
||||
|
||||
void BuildSharedFontsRawRegions(const std::vector<u8>& input) {
|
||||
void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) {
|
||||
// As we can derive the xor key we can just populate the offsets
|
||||
// based on the shared memory dump
|
||||
unsigned cur_offset = 0;
|
||||
@@ -144,7 +144,7 @@ struct PL_U::Impl {
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
|
||||
|
||||
/// Backing memory for the shared font data
|
||||
std::shared_ptr<std::vector<u8>> shared_font;
|
||||
std::shared_ptr<Kernel::PhysicalMemory> shared_font;
|
||||
|
||||
// Automatically populated based on shared_fonts dump or system archives.
|
||||
std::vector<FontRegion> shared_font_regions;
|
||||
@@ -166,7 +166,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
|
||||
// Rebuild shared fonts from data ncas
|
||||
if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
|
||||
FileSys::ContentRecordType::Data)) {
|
||||
impl->shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE);
|
||||
impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
|
||||
for (auto font : SHARED_FONTS) {
|
||||
const auto nca =
|
||||
nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
|
||||
@@ -207,7 +207,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
|
||||
}
|
||||
|
||||
} else {
|
||||
impl->shared_font = std::make_shared<std::vector<u8>>(
|
||||
impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(
|
||||
SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
|
||||
|
||||
const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir);
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
@@ -15,7 +20,7 @@ namespace Service::Nvidia::Devices {
|
||||
/// implement the ioctl interface.
|
||||
class nvdevice {
|
||||
public:
|
||||
nvdevice() = default;
|
||||
explicit nvdevice(Core::System& system) : system{system} {};
|
||||
virtual ~nvdevice() = default;
|
||||
union Ioctl {
|
||||
u32_le raw;
|
||||
@@ -33,7 +38,11 @@ public:
|
||||
* @param output A buffer where the output data will be written to.
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0;
|
||||
virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) = 0;
|
||||
|
||||
protected:
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvdisp_disp0 ::~nvdisp_disp0() = default;
|
||||
|
||||
u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
}
|
||||
@@ -34,9 +36,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
|
||||
transform, crop_rect};
|
||||
|
||||
auto& instance = Core::System::GetInstance();
|
||||
instance.GetPerfStats().EndGameFrame();
|
||||
instance.GPU().SwapBuffers(framebuffer);
|
||||
system.GetPerfStats().EndGameFrame();
|
||||
system.GPU().SwapBuffers(&framebuffer);
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -17,10 +17,11 @@ class nvmap;
|
||||
|
||||
class nvdisp_disp0 final : public nvdevice {
|
||||
public:
|
||||
explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev);
|
||||
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvdisp_disp0() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
||||
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
|
||||
|
||||
@@ -22,10 +22,12 @@ enum {
|
||||
};
|
||||
}
|
||||
|
||||
nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_as_gpu::~nvhost_as_gpu() = default;
|
||||
|
||||
u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
@@ -65,7 +67,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
|
||||
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
|
||||
params.page_size, params.flags);
|
||||
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
auto& gpu = system.GPU();
|
||||
const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
|
||||
if (params.flags & 1) {
|
||||
params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
|
||||
@@ -85,7 +87,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
|
||||
std::vector<IoctlRemapEntry> entries(num_entries);
|
||||
std::memcpy(entries.data(), input.data(), input.size());
|
||||
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
auto& gpu = system.GPU();
|
||||
for (const auto& entry : entries) {
|
||||
LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
|
||||
entry.offset, entry.nvmap_handle, entry.pages);
|
||||
@@ -136,7 +138,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
// case to prevent unexpected behavior.
|
||||
ASSERT(object->id == params.nvmap_handle);
|
||||
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
if (params.flags & 1) {
|
||||
params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size);
|
||||
@@ -173,8 +175,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
return 0;
|
||||
}
|
||||
|
||||
params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset,
|
||||
itr->second.size);
|
||||
params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
|
||||
buffer_mappings.erase(itr->second.offset);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
|
||||
@@ -17,10 +17,11 @@ class nvmap;
|
||||
|
||||
class nvhost_as_gpu final : public nvdevice {
|
||||
public:
|
||||
explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev);
|
||||
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvhost_as_gpu() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
|
||||
@@ -7,14 +7,20 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_ctrl::nvhost_ctrl() = default;
|
||||
nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
|
||||
: nvdevice(system), events_interface{events_interface} {}
|
||||
nvhost_ctrl::~nvhost_ctrl() = default;
|
||||
|
||||
u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
@@ -22,11 +28,15 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
|
||||
case IoctlCommand::IocGetConfigCommand:
|
||||
return NvOsGetConfigU32(input, output);
|
||||
case IoctlCommand::IocCtrlEventWaitCommand:
|
||||
return IocCtrlEventWait(input, output, false);
|
||||
return IocCtrlEventWait(input, output, false, ctrl);
|
||||
case IoctlCommand::IocCtrlEventWaitAsyncCommand:
|
||||
return IocCtrlEventWait(input, output, true);
|
||||
return IocCtrlEventWait(input, output, true, ctrl);
|
||||
case IoctlCommand::IocCtrlEventRegisterCommand:
|
||||
return IocCtrlEventRegister(input, output);
|
||||
case IoctlCommand::IocCtrlEventUnregisterCommand:
|
||||
return IocCtrlEventUnregister(input, output);
|
||||
case IoctlCommand::IocCtrlEventSignalCommand:
|
||||
return IocCtrlEventSignal(input, output);
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
@@ -41,23 +51,137 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
bool is_async) {
|
||||
bool is_async, IoctlCtrl& ctrl) {
|
||||
IocCtrlEventWaitParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
LOG_WARNING(Service_NVDRV,
|
||||
"(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}",
|
||||
params.syncpt_id, params.threshold, params.timeout, is_async);
|
||||
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
|
||||
params.syncpt_id, params.threshold, params.timeout, is_async);
|
||||
|
||||
// TODO(Subv): Implement actual syncpt waiting.
|
||||
params.value = 0;
|
||||
if (params.syncpt_id >= MaxSyncPoints) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
// This is mostly to take into account unimplemented features. As synced
|
||||
// gpu is always synced.
|
||||
if (!gpu.IsAsync()) {
|
||||
return NvResult::Success;
|
||||
}
|
||||
auto lock = gpu.LockSync();
|
||||
const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
|
||||
const s32 diff = current_syncpoint_value - params.threshold;
|
||||
if (diff >= 0) {
|
||||
params.value = current_syncpoint_value;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
}
|
||||
const u32 target_value = current_syncpoint_value - diff;
|
||||
|
||||
if (!is_async) {
|
||||
params.value = 0;
|
||||
}
|
||||
|
||||
if (params.timeout == 0) {
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Timeout;
|
||||
}
|
||||
|
||||
u32 event_id;
|
||||
if (is_async) {
|
||||
event_id = params.value & 0x00FF;
|
||||
if (event_id >= MaxNvEvents) {
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
} else {
|
||||
if (ctrl.fresh_call) {
|
||||
const auto result = events_interface.GetFreeEvent();
|
||||
if (result) {
|
||||
event_id = *result;
|
||||
} else {
|
||||
LOG_CRITICAL(Service_NVDRV, "No Free Events available!");
|
||||
event_id = params.value & 0x00FF;
|
||||
}
|
||||
} else {
|
||||
event_id = ctrl.event_id;
|
||||
}
|
||||
}
|
||||
|
||||
EventState status = events_interface.status[event_id];
|
||||
if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) {
|
||||
events_interface.SetEventStatus(event_id, EventState::Waiting);
|
||||
events_interface.assigned_syncpt[event_id] = params.syncpt_id;
|
||||
events_interface.assigned_value[event_id] = target_value;
|
||||
if (is_async) {
|
||||
params.value = params.syncpt_id << 4;
|
||||
} else {
|
||||
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
|
||||
}
|
||||
params.value |= event_id;
|
||||
events_interface.events[event_id].writable->Clear();
|
||||
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
|
||||
if (!is_async && ctrl.fresh_call) {
|
||||
ctrl.must_delay = true;
|
||||
ctrl.timeout = params.timeout;
|
||||
ctrl.event_id = event_id;
|
||||
return NvResult::Timeout;
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Timeout;
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return 0;
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
// TODO(bunnei): Implement this.
|
||||
return 0;
|
||||
IocCtrlEventRegisterParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
const u32 event_id = params.user_event_id & 0x00FF;
|
||||
LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
|
||||
if (event_id >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (events_interface.registered[event_id]) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
events_interface.RegisterEvent(event_id);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocCtrlEventUnregisterParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
const u32 event_id = params.user_event_id & 0x00FF;
|
||||
LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
|
||||
if (event_id >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (!events_interface.registered[event_id]) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
events_interface.UnregisterEvent(event_id);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocCtrlEventSignalParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
// TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
|
||||
// It is believed from RE to cancel the GPU Event. However, better research is required
|
||||
u32 event_id = params.user_event_id & 0x00FF;
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
|
||||
if (event_id >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (events_interface.status[event_id] == EventState::Waiting) {
|
||||
auto& gpu = system.GPU();
|
||||
if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
|
||||
events_interface.assigned_value[event_id])) {
|
||||
events_interface.LiberateEvent(event_id);
|
||||
events_interface.events[event_id].writable->Signal();
|
||||
}
|
||||
}
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -8,15 +8,17 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_ctrl final : public nvdevice {
|
||||
public:
|
||||
nvhost_ctrl();
|
||||
explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
|
||||
~nvhost_ctrl() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
@@ -132,9 +134,16 @@ private:
|
||||
|
||||
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
|
||||
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
|
||||
IoctlCtrl& ctrl);
|
||||
|
||||
u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
EventInterface& events_interface;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default;
|
||||
nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
|
||||
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
|
||||
|
||||
u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
@@ -185,7 +186,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
|
||||
|
||||
IoctlGetGpuTime params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||
const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks());
|
||||
params.gpu_time = static_cast<u64_le>(ns.count());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
|
||||
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_ctrl_gpu final : public nvdevice {
|
||||
public:
|
||||
nvhost_ctrl_gpu();
|
||||
explicit nvhost_ctrl_gpu(Core::System& system);
|
||||
~nvhost_ctrl_gpu() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_gpu::~nvhost_gpu() = default;
|
||||
|
||||
u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
@@ -119,8 +121,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
|
||||
params.unk3);
|
||||
|
||||
params.fence_out.id = 0;
|
||||
params.fence_out.value = 0;
|
||||
auto& gpu = system.GPU();
|
||||
params.fence_out.id = assigned_syncpoints;
|
||||
params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
|
||||
assigned_syncpoints++;
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
@@ -143,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
params.address, params.num_entries, params.flags);
|
||||
params.address, params.num_entries, params.flags.raw);
|
||||
|
||||
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader),
|
||||
@@ -153,10 +157,18 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
|
||||
Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
|
||||
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
|
||||
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
|
||||
if (params.flags.increment.Value()) {
|
||||
params.fence_out.value += current_syncpoint_value;
|
||||
} else {
|
||||
params.fence_out.value = current_syncpoint_value;
|
||||
}
|
||||
gpu.PushGPUEntries(std::move(entries));
|
||||
|
||||
params.fence_out.id = 0;
|
||||
params.fence_out.value = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo));
|
||||
return 0;
|
||||
}
|
||||
@@ -168,16 +180,24 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
params.address, params.num_entries, params.flags);
|
||||
params.address, params.num_entries, params.flags.raw);
|
||||
|
||||
Tegra::CommandList entries(params.num_entries);
|
||||
Memory::ReadBlock(params.address, entries.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
|
||||
Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
|
||||
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
|
||||
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
|
||||
if (params.flags.increment.Value()) {
|
||||
params.fence_out.value += current_syncpoint_value;
|
||||
} else {
|
||||
params.fence_out.value = current_syncpoint_value;
|
||||
}
|
||||
gpu.PushGPUEntries(std::move(entries));
|
||||
|
||||
params.fence_out.id = 0;
|
||||
params.fence_out.value = 0;
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
@@ -20,10 +21,11 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
|
||||
|
||||
class nvhost_gpu final : public nvdevice {
|
||||
public:
|
||||
explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev);
|
||||
explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvhost_gpu() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
@@ -113,11 +115,7 @@ private:
|
||||
static_assert(sizeof(IoctlGetErrorNotification) == 16,
|
||||
"IoctlGetErrorNotification is incorrect size");
|
||||
|
||||
struct IoctlFence {
|
||||
u32_le id;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
|
||||
static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
|
||||
|
||||
struct IoctlAllocGpfifoEx {
|
||||
u32_le num_entries;
|
||||
@@ -132,13 +130,13 @@ private:
|
||||
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
|
||||
|
||||
struct IoctlAllocGpfifoEx2 {
|
||||
u32_le num_entries; // in
|
||||
u32_le flags; // in
|
||||
u32_le unk0; // in (1 works)
|
||||
IoctlFence fence_out; // out
|
||||
u32_le unk1; // in
|
||||
u32_le unk2; // in
|
||||
u32_le unk3; // in
|
||||
u32_le num_entries; // in
|
||||
u32_le flags; // in
|
||||
u32_le unk0; // in (1 works)
|
||||
Fence fence_out; // out
|
||||
u32_le unk1; // in
|
||||
u32_le unk2; // in
|
||||
u32_le unk3; // in
|
||||
};
|
||||
static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
|
||||
|
||||
@@ -153,10 +151,16 @@ private:
|
||||
struct IoctlSubmitGpfifo {
|
||||
u64_le address; // pointer to gpfifo entry structs
|
||||
u32_le num_entries; // number of fence objects being submitted
|
||||
u32_le flags;
|
||||
IoctlFence fence_out; // returned new fence object for others to wait on
|
||||
union {
|
||||
u32_le raw;
|
||||
BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
|
||||
BitField<1, 1, u32_le> add_increment; // append an increment to the list
|
||||
BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
|
||||
BitField<8, 1, u32_le> increment; // increment the returned fence
|
||||
} flags;
|
||||
Fence fence_out; // returned new fence object for others to wait on
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence),
|
||||
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
|
||||
"IoctlSubmitGpfifo is incorrect size");
|
||||
|
||||
struct IoctlGetWaitbase {
|
||||
@@ -184,6 +188,7 @@ private:
|
||||
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
u32 assigned_syncpoints{};
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_nvdec::nvhost_nvdec() = default;
|
||||
nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
|
||||
nvhost_nvdec::~nvhost_nvdec() = default;
|
||||
|
||||
u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_nvdec final : public nvdevice {
|
||||
public:
|
||||
nvhost_nvdec();
|
||||
explicit nvhost_nvdec(Core::System& system);
|
||||
~nvhost_nvdec() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_nvjpg::nvhost_nvjpg() = default;
|
||||
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
|
||||
nvhost_nvjpg::~nvhost_nvjpg() = default;
|
||||
|
||||
u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_nvjpg final : public nvdevice {
|
||||
public:
|
||||
nvhost_nvjpg();
|
||||
explicit nvhost_nvjpg(Core::System& system);
|
||||
~nvhost_nvjpg() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_vic::nvhost_vic() = default;
|
||||
nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
|
||||
nvhost_vic::~nvhost_vic() = default;
|
||||
|
||||
u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_vic final : public nvdevice {
|
||||
public:
|
||||
nvhost_vic();
|
||||
explicit nvhost_vic(Core::System& system);
|
||||
~nvhost_vic() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
|
||||
@@ -18,7 +18,7 @@ enum {
|
||||
};
|
||||
}
|
||||
|
||||
nvmap::nvmap() = default;
|
||||
nvmap::nvmap(Core::System& system) : nvdevice(system) {}
|
||||
nvmap::~nvmap() = default;
|
||||
|
||||
VAddr nvmap::GetObjectAddress(u32 handle) const {
|
||||
@@ -28,7 +28,8 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
|
||||
return object->addr;
|
||||
}
|
||||
|
||||
u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (static_cast<IoctlCommand>(command.raw)) {
|
||||
case IoctlCommand::Create:
|
||||
return IocCreate(input, output);
|
||||
|
||||
@@ -16,13 +16,14 @@ namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvmap final : public nvdevice {
|
||||
public:
|
||||
nvmap();
|
||||
explicit nvmap(Core::System& system);
|
||||
~nvmap() override;
|
||||
|
||||
/// Returns the allocated address of an nvmap object given its handle.
|
||||
VAddr GetObjectAddress(u32 handle) const;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
|
||||
/// Represents an nvmap object.
|
||||
struct Object {
|
||||
|
||||
@@ -8,12 +8,18 @@
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/interface.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
|
||||
nvdrv->SignalSyncpt(syncpoint_id, value);
|
||||
}
|
||||
|
||||
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NVDRV, "called");
|
||||
|
||||
@@ -36,11 +42,31 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::vector<u8> output(ctx.GetWriteBufferSize());
|
||||
|
||||
IoctlCtrl ctrl{};
|
||||
|
||||
u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl);
|
||||
|
||||
if (ctrl.must_delay) {
|
||||
ctrl.fresh_call = false;
|
||||
ctx.SleepClientThread(
|
||||
"NVServices::DelayedResponse", ctrl.timeout,
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
IoctlCtrl ctrl2{ctrl};
|
||||
std::vector<u8> output2 = output;
|
||||
u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2);
|
||||
ctx.WriteBuffer(output2);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(result);
|
||||
},
|
||||
nvdrv->GetEventWriteable(ctrl.event_id));
|
||||
} else {
|
||||
ctx.WriteBuffer(output);
|
||||
}
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output));
|
||||
|
||||
ctx.WriteBuffer(output);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
|
||||
@@ -66,13 +92,19 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 fd = rp.Pop<u32>();
|
||||
u32 event_id = rp.Pop<u32>();
|
||||
// TODO(Blinkhawk): Figure the meaning of the flag at bit 16
|
||||
u32 event_id = rp.Pop<u32>() & 0x000000FF;
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(query_event.readable);
|
||||
rb.Push<u32>(0);
|
||||
if (event_id < MaxNvEvents) {
|
||||
rb.PushCopyObjects(nvdrv->GetEvent(event_id));
|
||||
rb.Push<u32>(NvResult::Success);
|
||||
} else {
|
||||
rb.Push<u32>(0);
|
||||
rb.Push<u32>(NvResult::BadParameter);
|
||||
}
|
||||
}
|
||||
|
||||
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
|
||||
@@ -127,10 +159,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
|
||||
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"NVDRV::query_event");
|
||||
}
|
||||
|
||||
NVDRV::~NVDRV() = default;
|
||||
|
||||
@@ -19,6 +19,8 @@ public:
|
||||
NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
|
||||
~NVDRV() override;
|
||||
|
||||
void SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value);
|
||||
|
||||
private:
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
void Ioctl(Kernel::HLERequestContext& ctx);
|
||||
@@ -33,8 +35,6 @@ private:
|
||||
std::shared_ptr<Module> nvdrv;
|
||||
|
||||
u64 pid{};
|
||||
|
||||
Kernel::EventPair query_event;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
48
src/core/hle/service/nvdrv/nvdata.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
constexpr u32 MaxSyncPoints = 192;
|
||||
constexpr u32 MaxNvEvents = 64;
|
||||
|
||||
struct Fence {
|
||||
s32 id;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Fence) == 8, "Fence has wrong size");
|
||||
|
||||
struct MultiFence {
|
||||
u32 num_fences;
|
||||
std::array<Fence, 4> fences;
|
||||
};
|
||||
|
||||
enum NvResult : u32 {
|
||||
Success = 0,
|
||||
BadParameter = 4,
|
||||
Timeout = 5,
|
||||
ResourceError = 15,
|
||||
};
|
||||
|
||||
enum class EventState {
|
||||
Free = 0,
|
||||
Registered = 1,
|
||||
Waiting = 2,
|
||||
Busy = 3,
|
||||
};
|
||||
|
||||
struct IoctlCtrl {
|
||||
// First call done to the servioce for services that call itself again after a call.
|
||||
bool fresh_call{true};
|
||||
// Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
|
||||
bool must_delay{};
|
||||
// Timeout for the delay
|
||||
s64 timeout{};
|
||||
// NV Event Id
|
||||
s32 event_id{-1};
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
|
||||
@@ -22,8 +25,9 @@
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) {
|
||||
auto module_ = std::make_shared<Module>();
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
|
||||
Core::System& system) {
|
||||
auto module_ = std::make_shared<Module>(system);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager);
|
||||
@@ -32,17 +36,25 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
|
||||
nvflinger.SetNVDrvInstance(module_);
|
||||
}
|
||||
|
||||
Module::Module() {
|
||||
auto nvmap_dev = std::make_shared<Devices::nvmap>();
|
||||
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev);
|
||||
devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev);
|
||||
devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>();
|
||||
Module::Module(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
|
||||
events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, event_label);
|
||||
events_interface.status[i] = EventState::Free;
|
||||
events_interface.registered[i] = false;
|
||||
}
|
||||
auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
|
||||
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
|
||||
devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev);
|
||||
devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
|
||||
devices["/dev/nvmap"] = nvmap_dev;
|
||||
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
|
||||
devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>();
|
||||
devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>();
|
||||
devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>();
|
||||
devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>();
|
||||
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
|
||||
devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
|
||||
devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system);
|
||||
devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
|
||||
devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system);
|
||||
}
|
||||
|
||||
Module::~Module() = default;
|
||||
@@ -59,12 +71,13 @@ u32 Module::Open(const std::string& device_name) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
auto itr = open_files.find(fd);
|
||||
ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
|
||||
|
||||
auto& device = itr->second;
|
||||
return device->ioctl({command}, input, output);
|
||||
return device->ioctl({command}, input, output, ctrl);
|
||||
}
|
||||
|
||||
ResultCode Module::Close(u32 fd) {
|
||||
@@ -77,4 +90,22 @@ ResultCode Module::Close(u32 fd) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
if (events_interface.assigned_syncpt[i] == syncpoint_id &&
|
||||
events_interface.assigned_value[i] == value) {
|
||||
events_interface.LiberateEvent(i);
|
||||
events_interface.events[i].writable->Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
|
||||
return events_interface.events[event_id].readable;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
|
||||
return events_interface.events[event_id].writable;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -8,8 +8,14 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
@@ -20,16 +26,72 @@ namespace Devices {
|
||||
class nvdevice;
|
||||
}
|
||||
|
||||
struct IoctlFence {
|
||||
u32 id;
|
||||
u32 value;
|
||||
struct EventInterface {
|
||||
// Mask representing currently busy events
|
||||
u64 events_mask{};
|
||||
// Each kernel event associated to an NV event
|
||||
std::array<Kernel::EventPair, MaxNvEvents> events;
|
||||
// The status of the current NVEvent
|
||||
std::array<EventState, MaxNvEvents> status{};
|
||||
// Tells if an NVEvent is registered or not
|
||||
std::array<bool, MaxNvEvents> registered{};
|
||||
// When an NVEvent is waiting on GPU interrupt, this is the sync_point
|
||||
// associated with it.
|
||||
std::array<u32, MaxNvEvents> assigned_syncpt{};
|
||||
// This is the value of the GPU interrupt for which the NVEvent is waiting
|
||||
// for.
|
||||
std::array<u32, MaxNvEvents> assigned_value{};
|
||||
// Constant to denote an unasigned syncpoint.
|
||||
static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
|
||||
std::optional<u32> GetFreeEvent() const {
|
||||
u64 mask = events_mask;
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
const bool is_free = (mask & 0x1) == 0;
|
||||
if (is_free) {
|
||||
if (status[i] == EventState::Registered || status[i] == EventState::Free) {
|
||||
return {i};
|
||||
}
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
void SetEventStatus(const u32 event_id, EventState new_status) {
|
||||
EventState old_status = status[event_id];
|
||||
if (old_status == new_status) {
|
||||
return;
|
||||
}
|
||||
status[event_id] = new_status;
|
||||
if (new_status == EventState::Registered) {
|
||||
registered[event_id] = true;
|
||||
}
|
||||
if (new_status == EventState::Waiting || new_status == EventState::Busy) {
|
||||
events_mask |= (1ULL << event_id);
|
||||
}
|
||||
}
|
||||
void RegisterEvent(const u32 event_id) {
|
||||
registered[event_id] = true;
|
||||
if (status[event_id] == EventState::Free) {
|
||||
status[event_id] = EventState::Registered;
|
||||
}
|
||||
}
|
||||
void UnregisterEvent(const u32 event_id) {
|
||||
registered[event_id] = false;
|
||||
if (status[event_id] == EventState::Registered) {
|
||||
status[event_id] = EventState::Free;
|
||||
}
|
||||
}
|
||||
void LiberateEvent(const u32 event_id) {
|
||||
status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
|
||||
events_mask &= ~(1ULL << event_id);
|
||||
assigned_syncpt[event_id] = unassigned_syncpt;
|
||||
assigned_value[event_id] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
Module();
|
||||
Module(Core::System& system);
|
||||
~Module();
|
||||
|
||||
/// Returns a pointer to one of the available devices, identified by its name.
|
||||
@@ -44,10 +106,17 @@ public:
|
||||
/// Opens a device node and returns a file descriptor to it.
|
||||
u32 Open(const std::string& device_name);
|
||||
/// Sends an ioctl command to the specified file descriptor.
|
||||
u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl);
|
||||
/// Closes a device file descriptor and returns operation success.
|
||||
ResultCode Close(u32 fd);
|
||||
|
||||
void SignalSyncpt(const u32 syncpoint_id, const u32 value);
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(u32 event_id) const;
|
||||
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
|
||||
|
||||
private:
|
||||
/// Id to use for the next open file descriptor.
|
||||
u32 next_fd = 1;
|
||||
@@ -57,9 +126,12 @@ private:
|
||||
|
||||
/// Mapping of device node names to their implementation.
|
||||
std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
|
||||
|
||||
EventInterface events_interface;
|
||||
};
|
||||
|
||||
/// Registers all NVDRV services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
|
||||
Core::System& system);
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -34,7 +34,8 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||
buffer_wait_event.writable->Signal();
|
||||
}
|
||||
|
||||
std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
||||
u32 height) {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
|
||||
// Only consider free buffers. Buffers become free once again after they've been Acquired
|
||||
// and Released by the compositor, see the NVFlinger::Compose method.
|
||||
@@ -51,7 +52,7 @@ std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
}
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
return itr->slot;
|
||||
return {{itr->slot, &itr->multi_fence}};
|
||||
}
|
||||
|
||||
const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
|
||||
@@ -63,7 +64,8 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
|
||||
}
|
||||
|
||||
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect) {
|
||||
const Common::Rectangle<int>& crop_rect, u32 swap_interval,
|
||||
Service::Nvidia::MultiFence& multi_fence) {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(),
|
||||
[&](const Buffer& buffer) { return buffer.slot == slot; });
|
||||
ASSERT(itr != queue.end());
|
||||
@@ -71,12 +73,21 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
itr->status = Buffer::Status::Queued;
|
||||
itr->transform = transform;
|
||||
itr->crop_rect = crop_rect;
|
||||
itr->swap_interval = swap_interval;
|
||||
itr->multi_fence = multi_fence;
|
||||
queue_sequence.push_back(slot);
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
|
||||
return buffer.status == Buffer::Status::Queued;
|
||||
});
|
||||
auto itr = queue.end();
|
||||
// Iterate to find a queued buffer matching the requested slot.
|
||||
while (itr == queue.end() && !queue_sequence.empty()) {
|
||||
u32 slot = queue_sequence.front();
|
||||
itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
|
||||
return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
|
||||
});
|
||||
queue_sequence.pop_front();
|
||||
}
|
||||
if (itr == queue.end())
|
||||
return {};
|
||||
itr->status = Buffer::Status::Acquired;
|
||||
|
||||