Compare commits

...

87 Commits

Author SHA1 Message Date
Zach Hilman 81fff7aec0 qt: Fix game name format error 2019-10-06 15:07:04 -04:00
bunnei deecd7f074 Merge pull request #2942 from ReinUsesLisp/clang-warnings
Silence miscellaneous warnings
2019-10-05 20:41:20 -04:00
bunnei 6f511c8006 Merge pull request #2943 from DarkLordZach/azure-titlebars-v2
ci: Add custom titlebars for mainline and patreon
2019-10-05 19:29:35 -04:00
Fernando Sahmkow 47ccfabe18 Merge pull request #2944 from lioncash/ast
video_core/shader: Minor changes
2019-10-05 12:02:51 -04:00
Lioncash f883cd4f0e video_core/control_flow: Eliminate variable shadowing warnings 2019-10-05 09:14:27 -04:00
Lioncash 25702b6256 video_core/control_flow: Eliminate pessimizing moves
These can inhibit the ability of a compiler to perform RVO.
2019-10-05 09:14:27 -04:00
Lioncash d82b181d44 video_core/ast: Unindent most of IsFullyDecompiled() by one level 2019-10-05 09:14:27 -04:00
Lioncash 6c41d1cd7e video_core/ast: Make ShowCurrentState() take a string_view instead of std::string
Allows the function to be non-allocating in terms of the output string.
2019-10-05 09:14:27 -04:00
Lioncash 3c54edae24 video_core/ast: Eliminate variable shadowing warnings 2019-10-05 09:14:26 -04:00
Lioncash 5a0a9c7449 video_core/ast: Replace std::string with a constexpr std::string_view
Same behavior, but without the need to heap allocate
2019-10-05 09:14:26 -04:00
Lioncash 3a20d9734f video_core/ast: Default the move constructor and assignment operator
This is behaviorally equivalent and also fixes a bug where some members
weren't being moved over.
2019-10-05 09:14:26 -04:00
Lioncash 43503a69bf video_core/{ast, expr}: Organize forward declaration
Keeps them alphabetically sorted for readability.
2019-10-05 09:14:26 -04:00
Lioncash 50ad745585 video_core/expr: Supply operator!= along with operator==
Provides logical symmetry to the interface.
2019-10-05 09:14:26 -04:00
Lioncash 8eb1398f8d video_core/{ast, expr}: Use std::move where applicable
Avoids unnecessary atomic reference count increments and decrements.
2019-10-05 09:14:23 -04:00
Lioncash 8e0c80f269 video_core/ast: Supply const accessors for data where applicable
Provides const equivalents of data accessors for use within const
contexts.
2019-10-05 08:22:03 -04:00
David 3728bbc22a Merge pull request #2888 from FernandoS27/decompiler2
Shader_IR: Implement a full control flow decompiler for the shader IR.
2019-10-05 21:52:20 +10:00
Zach Hilman 57fe7fdec0 qt: Change titlebar formatting 2019-10-05 00:10:04 -04:00
Zach Hilman 3d4a0b94e3 common: Add additional SCM revision fields 2019-10-05 00:09:49 -04:00
Zach Hilman d45ad75404 ci: Add version counter variable 2019-10-05 00:09:11 -04:00
bunnei 0a662d009b Merge pull request #2917 from FernandoS27/fermi-deduction-2
TextureCache: Add the ability to deduce if two textures are depth on blit.
2019-10-04 20:12:01 -04:00
ReinUsesLisp 25ee892d5e audio/audout_u: Change formatting for old clang-format versions 2019-10-04 23:51:56 +00:00
ReinUsesLisp e1afeec76d yuzu/game_list_worker: Silence warnings 2019-10-04 23:41:22 +00:00
ReinUsesLisp f297e9ff22 yuzu/game_list: Silence -Wswitch and -Wunused-variable 2019-10-04 23:41:22 +00:00
ReinUsesLisp 2b9b695fa7 yuzu/configure_service: Silence -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp e03f46fb0e yuzu_tester: Remove unused variable 2019-10-04 23:41:22 +00:00
ReinUsesLisp 8d0b1a957e service/nvdrv: Silence -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp 5c907f85fc service/nfp: Silence -Wunused and -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp 0759df0aff service/hid: Silence -Wunused and -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp ab6f8d8a1e service/am: Silence -Wreorder 2019-10-04 23:41:21 +00:00
ReinUsesLisp 634c6e24b0 service/hid: Remove unused system reference 2019-10-04 23:41:21 +00:00
ReinUsesLisp 1dbd22e695 service/friend: Remove unused field 2019-10-04 23:41:21 +00:00
ReinUsesLisp 99db7d23dd service/filesystem: Silence -Wunused-variable 2019-10-04 23:41:21 +00:00
ReinUsesLisp 8566096794 service/bcat: Silence -Wreorder and -Wunused 2019-10-04 23:41:21 +00:00
ReinUsesLisp 87e7cc2d5a service/audio: Silence -Wunused 2019-10-04 23:28:34 +00:00
ReinUsesLisp aacb473aa2 service/apm: Silence -Wunused and -Wreorder 2019-10-04 23:28:34 +00:00
ReinUsesLisp f4417eab8f common/file_util: Silence -Wswitch 2019-10-04 23:28:34 +00:00
Fernando Sahmkow ab47a660c8 Texture_Cache: Blit Deduction corrections and simplifications. 2019-10-04 18:53:47 -04:00
Fernando Sahmkow 2036504a82 TextureCache: Add the ability to deduce if two textures are depth on blit. 2019-10-04 18:53:46 -04:00
Fernando Sahmkow e6eae4b815 Shader_ir: Address feedback 2019-10-04 18:52:57 -04:00
Fernando Sahmkow 3c09d9abe6 Shader_Ir: Address Feedback and clang format. 2019-10-04 18:52:57 -04:00
Fernando Sahmkow 507a9c6a40 vk_shader_decompiler: Correct Branches inside conditionals. 2019-10-04 18:52:56 -04:00
Fernando Sahmkow 000ad558dd vk_shader_decompiler: Clean code and be const correct. 2019-10-04 18:52:55 -04:00
Fernando Sahmkow 7c756baa77 Shader_IR: clean up AST handling and add documentation. 2019-10-04 18:52:55 -04:00
Fernando Sahmkow 5ea740beb5 Shader_IR: Correct OutwardMoves for Ifs 2019-10-04 18:52:54 -04:00
Fernando Sahmkow 100a4bd988 vk_shader_compiler: Don't enclose branches with if(true) to avoid crashing AMD 2019-10-04 18:52:54 -04:00
Fernando Sahmkow 189a50bc2a gl_shader_decompiler: Refactor and address feedback. 2019-10-04 18:52:53 -04:00
Fernando Sahmkow b3c46d6948 Shader_IR: corrections and clang-format 2019-10-04 18:52:53 -04:00
Fernando Sahmkow 466cd52ad4 vk_shader_compiler: Correct SPIR-V AST Decompiling 2019-10-04 18:52:52 -04:00
Fernando Sahmkow 2e9a810423 Shader_IR: allow else derivation to be optional. 2019-10-04 18:52:52 -04:00
Fernando Sahmkow ca9901867e vk_shader_compiler: Implement the decompiler in SPIR-V 2019-10-04 18:52:51 -04:00
Fernando Sahmkow 0366c18d87 Shader_IR: mark labels as unused for partial decompile. 2019-10-04 18:52:51 -04:00
Fernando Sahmkow 47e4f6a52c Shader_Ir: Refactor Decompilation process and allow multiple decompilation modes. 2019-10-04 18:52:50 -04:00
Fernando Sahmkow 38fc995f6c gl_shader_decompiler: Implement AST decompiling 2019-10-04 18:52:50 -04:00
Fernando Sahmkow 6fdd501113 shader_ir: Declare Manager and pass it to appropiate programs. 2019-10-04 18:52:49 -04:00
Fernando Sahmkow 8be6e1c522 shader_ir: Corrections to outward movements and misc stuffs 2019-10-04 18:52:48 -04:00
Fernando Sahmkow 4fde66e609 shader_ir: Add basic goto elimination 2019-10-04 18:52:48 -04:00
Fernando Sahmkow c17953978b shader_ir: Initial Decompile Setup 2019-10-04 18:52:47 -04:00
Rodrigo Locatti d633397883 Merge pull request #2941 from FernandoS27/fix-master
SDL: Fix missing header
2019-10-04 22:50:15 +00:00
Fernando Sahmkow 678d9ccad6 SDL: Fix missing header
This fixes linux and mingw builds.
2019-10-04 18:14:11 -04:00
bunnei 94c34f23d7 Merge pull request #2896 from FearlessTobi/port-4950
Port citra-emu/citra#4950: "Add FPS to SDL title bar"
2019-10-04 15:51:03 -04:00
bunnei 7fbaf62bac Merge pull request #2936 from VPeruS/use-isallzeroarray
[crypto] Use IsAllZeroArray helper function
2019-10-04 15:44:35 -04:00
bunnei accdb84993 Merge pull request #2940 from lioncash/zlib
externals: Track mainline zlib as a submodule
2019-10-04 15:44:13 -04:00
Lioncash e29492d114 CMakeLists: Make libzip excluded from the ALL target
Likewise, we also only want to link in the libraries that we actually
make use of (so we don't need to worry about linking in test targets).
2019-10-04 05:02:01 -04:00
Lioncash 80bdb44ead externals: Use upstream zlib
We don't need to depend on a custom fork for this. We can add the
library as is, and then make it excluded from the ALL target, so we only
link in the libraries that we actually make use of.
2019-10-04 05:01:57 -04:00
bunnei c818728513 Merge pull request #2898 from FearlessTobi/port-4004
Port citra-emu/citra#4004: "qt_themes: add two colorful themes"
2019-10-03 21:34:40 -04:00
David 9aac7fbc22 Merge pull request #2539 from DarkLordZach/bcat
bcat: Implement BCAT service and connect to yuzu Boxcat server
2019-10-03 19:06:13 +10:00
bunnei 6bfabdedfd Merge pull request #2937 from DarkLordZach/azure-msvc
ci: Add windows MSVC builds to patreon and mainline pipelines
2019-10-02 19:28:03 -04:00
Zach Hilman a86e52a375 ci: Correct mainline release dependency 2019-10-02 18:54:05 -04:00
Zach Hilman 53be058e74 ci: Add Mainline tagline 2019-10-02 18:51:21 -04:00
Zach Hilman d648cd562a ci: Use MSVC windows for patreon 2019-10-02 18:23:09 -04:00
Zach Hilman bfa60e2d4e ci: Use MSVC windows for mainline 2019-10-02 17:58:52 -04:00
Zach Hilman 514b74a098 ci: Add MSVC build template 2019-10-02 17:58:33 -04:00
Zach Hilman 49344111cc ci: Add Windows MSVC package script 2019-10-02 17:53:53 -04:00
Zach Hilman c56822a405 ci: Fix unset environment variable bug 2019-10-02 17:53:37 -04:00
vperus 29b1d0db0f [crypto] Use IsAllZeroArray helper function 2019-10-02 19:20:45 +03:00
bunnei f8ce672b67 Merge pull request #2932 from DarkLordZach/azure-gitpages
ci: Iterate through GitHub pages for pull requests
2019-10-01 23:22:49 -04:00
Zach Hilman 0e58bfedfd ci: Check additional pages on GitHub 2019-10-01 21:54:26 -04:00
Zach Hilman 7d46416a16 ci: Iterate through GitHub pages for pull requests 2019-10-01 21:38:09 -04:00
bunnei 5e677a3178 Merge pull request #2904 from ogniK5377/better-signal-hid
Signal styleset changes at a better time
2019-10-01 10:58:02 -04:00
bunnei c26e9c4cd1 Merge pull request #2924 from MysticExile/readme-2
Remove AppVeyor status from the readme.md
2019-10-01 10:57:19 -04:00
bunnei 80d6abc08b Merge pull request #2929 from DarkLordZach/azure-patreon-merge-extra
ci: Merge patreon tagged PRs from public repository during merge step.
2019-10-01 10:56:50 -04:00
Zach Hilman 4ea425d6cf ci: Correct arguments for mergebot script 2019-09-30 13:43:34 -04:00
Zach Hilman e6f3aad84e ci: Populate patreon PRs on public repository 2019-09-30 09:29:28 -04:00
Ethan 4bd6196b0a Remove AppVeyor status 2019-09-29 22:32:44 +02:00
David Marcec 77fbf29047 Signal styleset changes at a better time
We should signal when a net controller is added and our event should be manual, not automatic.
2019-09-24 17:24:37 +10:00
FearlessTobi 855e7237ff qt_themes: add two colorful themes
These two colorful themes are based on the Default and Dark themes, and contain icons that are colored rather than black and white. These icons come from icons8.com and they have been slightly revised by me. I'm pretty sure I was licensed to use them for Citra.

Co-Authored-By: Pengfei Zhu <zhupengfei321@sina.cn>
2019-09-22 16:42:00 +02:00
jroweboy 64dbc92b61 Add FPS to SDL title bar
Also fix a small issue with incorrect shutdown ordering in SDL.
Previously the system would still be running so the telemetry task
didn't launch and detached_tasks would assert(count == 0)
2019-09-22 15:49:39 +02:00
84 changed files with 2795 additions and 315 deletions
+3 -2
View File
@@ -5,10 +5,11 @@ cd /yuzu
ccache -s
mkdir build || true && cd build
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
ninja
ccache -s
ctest -VV -C Release
# Ignore zlib's tests, since they aren't gated behind a CMake option.
ctest -VV -E "(example|example64)" -C Release
+1 -1
View File
@@ -2,4 +2,4 @@
mkdir -p "ccache" || true
chmod a+x ./.ci/scripts/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh $1
@@ -1,41 +1,45 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, urllib3.request, shutil, subprocess, os, traceback
import requests, sys, json, shutil, subprocess, os, traceback
org = os.getenv("PrivateMergeOrg".upper(), "yuzu-emu")
repo = os.getenv("PrivateMergeRepo".upper(), "yuzu-private")
tagline = os.getenv("MergeTaglinePrivate".upper(), "")
org = os.getenv("PRIVATEMERGEORG", "yuzu-emu")
repo = os.getenv("PRIVATEMERGEREPO", "yuzu-private")
tagline = sys.argv[3]
user = sys.argv[1]
http = urllib3.PoolManager()
dl_list = {}
TAG_NAME = sys.argv[2]
def check_individual(repo_id, pr_id):
url = 'https://%sdev.azure.com/%s/%s/_apis/git/repositories/%s/pullRequests/%s/labels?api-version=5.1-preview.1' % (user, org, repo, repo_id, pr_id)
response = requests.get(url)
if (response.ok):
j = json.loads(response.content)
for tg in j['value']:
if (tg['name'] == sys.argv[2]):
return True
try:
js = response.json()
return any(tag.get('name') == TAG_NAME for tag in js['value'])
except:
return False
return False
try:
def merge_pr(pn, ref):
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", 'origin/' + ref.replace('refs/heads/','')]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
def main():
url = 'https://%sdev.azure.com/%s/%s/_apis/git/pullrequests?api-version=5.1' % (user, org, repo)
response = requests.get(url)
if (response.ok):
j = json.loads(response.content)
for pr in j["value"]:
repo_id = pr['repository']['id']
pr_id = pr['pullRequestId']
if (check_individual(repo_id, pr_id)):
pn = pr_id
ref = pr['sourceRefName']
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", 'origin/' + ref.replace('refs/heads/','')]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
except:
traceback.print_exc(file=sys.stdout)
sys.exit(-1)
js = response.json()
tagged_prs = filter(lambda pr: check_individual(pr['repository']['id'], pr['pullRequestId']), js['value'])
map(lambda pr: merge_pr(pr['pullRequestId'], pr['sourceRefName']), tagged_prs)
if __name__ == '__main__':
try:
main()
except:
traceback.print_exc(file=sys.stdout)
sys.exit(-1)
+9 -3
View File
@@ -3,7 +3,7 @@
import requests, sys, json, urllib3.request, shutil, subprocess, os
tagline = os.getenv("MergeTaglinePublic".upper(), "")
tagline = sys.argv[2]
http = urllib3.PoolManager()
dl_list = {}
@@ -14,11 +14,13 @@ def check_individual(labels):
return True
return False
try:
url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls'
def do_page(page):
url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls?page=%s' % page
response = requests.get(url)
if (response.ok):
j = json.loads(response.content)
if j == []:
return
for pr in j:
if (check_individual(pr["labels"])):
pn = pr["number"]
@@ -26,5 +28,9 @@ try:
print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", "pr-%s" % pn]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
try:
for i in range(1,30):
do_page(i)
except:
sys.exit(-1)
+1 -1
View File
@@ -13,7 +13,7 @@ echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build || true && cd build
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
ninja
# Clean up the dirty hacks
+1 -1
View File
@@ -2,4 +2,4 @@
mkdir -p "ccache" || true
chmod a+x ./.ci/scripts/windows/docker.sh
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh $1
+32
View File
@@ -0,0 +1,32 @@
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-",""
$GITREV = $(git show -s --format='%h')
$RELEASE_DIST = "yuzu-windows-msvc"
$MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
$MSVC_BUILD_PDB = "yuzu-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", ""
$MSVC_SEVENZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
$env:BUILD_ZIP = $MSVC_BUILD_ZIP
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
$env:BUILD_UPDATE = $MSVC_SEVENZIP
$BUILD_DIR = ".\build\bin\Release"
mkdir pdb
Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
rm "$BUILD_DIR\*.pdb"
mkdir $RELEASE_DIST
mkdir "artifacts"
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
rm "$RELEASE_DIST\*.exe"
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
Copy-Item .\license.txt -Destination $RELEASE_DIST
Copy-Item .\README.md -Destination $RELEASE_DIST
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
7z a $MSVC_SEVENZIP $RELEASE_DIST
Get-ChildItem . -Filter "*.zip" | Copy-Item -destination "artifacts"
Get-ChildItem . -Filter "*.7z" | Copy-Item -destination "artifacts"
+22
View File
@@ -0,0 +1,22 @@
parameters:
artifactSource: 'true'
cache: 'false'
version: ''
steps:
- script: mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64" --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
displayName: 'Configure CMake'
- task: MSBuild@1
displayName: 'Build'
inputs:
solution: 'build/yuzu.sln'
maximumCpuCount: true
configuration: release
- task: PowerShell@2
displayName: 'Package Artifacts'
inputs:
targetType: 'filePath'
filePath: './.ci/scripts/windows/upload.ps1'
- publish: artifacts
artifact: 'yuzu-$(BuildName)-windows-msvc'
displayName: 'Upload Artifacts'
+2 -3
View File
@@ -1,10 +1,9 @@
parameters:
artifactSource: 'true'
cache: 'false'
version: ''
steps:
- script: export DATE=`date '+%Y.%m.%d'` && export CI=true && AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && AZURE_REPO_TAG=$(BuildName)-$DATE
displayName: 'Determine Build Name'
- task: DockerInstaller@0
displayName: 'Prepare Environment'
inputs:
@@ -15,7 +14,7 @@ steps:
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
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh ${{ parameters['version'] }}
displayName: 'Build'
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
displayName: 'Package Artifacts'
+5 -1
View File
@@ -1,3 +1,6 @@
parameters:
version: ''
jobs:
- job: build
displayName: 'standard'
@@ -20,4 +23,5 @@ jobs:
- template: ./build-single.yml
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
cache: $(parameters.cache)
version: $(parameters.version)
+4
View File
@@ -1,3 +1,6 @@
parameters:
version: ''
jobs:
- job: build_test
displayName: 'testing'
@@ -31,3 +34,4 @@ jobs:
parameters:
artifactSource: 'false'
cache: 'false'
version: $(parameters.version)
+11 -4
View File
@@ -8,16 +8,23 @@ steps:
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
displayName: 'Apply Git Configuration'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches'
displayName: 'Discover, Download, and Apply Patches (Mainline)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabelPublic }} patches-public'
arguments: '${{ parameters.matchLabelPublic }} $(MergeTaglinePublic) patches-public'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches'
displayName: 'Discover, Download, and Apply Patches (Patreon Public)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Public" patches-mixed-public'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches (Patreon Private)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
arguments: '$(PrivateMergeUser) ${{ parameters.matchLabel }} patches-private'
arguments: '$(PrivateMergeUser) ${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Private" patches-private'
workingDirectory: '$(System.DefaultWorkingDirectory)'
+1 -1
View File
@@ -11,5 +11,5 @@ steps:
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabel }} patches'
arguments: '${{ parameters.matchLabel }} Tagged patches'
workingDirectory: '$(System.DefaultWorkingDirectory)'
+1 -1
View File
@@ -2,7 +2,7 @@ steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download Windows Release'
inputs:
artifactName: 'yuzu-$(BuildName)-windows-mingw'
artifactName: 'yuzu-$(BuildName)-windows-msvc'
buildType: 'current'
targetPath: '$(Build.ArtifactStagingDirectory)'
- task: DownloadPipelineArtifact@2
+44 -4
View File
@@ -1,6 +1,9 @@
trigger:
- master
variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
@@ -15,12 +18,49 @@ stages:
dependsOn: format
displayName: 'build'
jobs:
- template: ./templates/build-standard.yml
parameters:
cache: 'true'
- job: build
displayName: 'standard'
pool:
vmImage: ubuntu-latest
strategy:
maxParallel: 10
matrix:
linux:
BuildSuffix: 'linux'
ScriptFolder: 'linux'
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-single.yml
parameters:
artifactSource: 'false'
cache: 'true'
version: $(DisplayVersion)
- stage: build_win
dependsOn: format
displayName: 'build-windows'
jobs:
- job: build
displayName: 'msvc'
pool:
vmImage: vs2017-win2016
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-msvc.yml
parameters:
artifactSource: 'false'
cache: 'true'
version: $(DisplayVersion)
- stage: release
displayName: 'Release'
dependsOn: build
dependsOn:
- build
- build_win
jobs:
- job: github
displayName: 'GitHub Release'
+16 -10
View File
@@ -1,6 +1,9 @@
trigger:
- master
variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
@@ -15,14 +18,17 @@ stages:
dependsOn: format
displayName: 'build'
jobs:
- template: ./templates/build-standard.yml
parameters:
cache: 'true'
- stage: release
displayName: 'release'
dependsOn: build
jobs:
- job: azure
displayName: 'azure'
- job: build
displayName: 'windows-msvc'
pool:
vmImage: vs2017-win2016
steps:
- template: ./templates/release-universal.yml
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-msvc.yml
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
version: $(DisplayVersion)
+4 -4
View File
@@ -47,8 +47,8 @@
path = externals/sirit
url = https://github.com/ReinUsesLisp/sirit
[submodule "libzip"]
path = externals/libzip
url = https://github.com/DarkLordZach/libzip
path = externals/libzip
url = https://github.com/DarkLordZach/libzip
[submodule "zlib"]
path = externals/zlib
url = https://github.com/DarkLordZach/zlib
path = externals/zlib
url = https://github.com/madler/zlib
+6
View File
@@ -83,9 +83,15 @@ set(HASH_FILES
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/warp.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/ast.cpp"
"${VIDEO_CORE}/shader/ast.h"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/compiler_settings.cpp"
"${VIDEO_CORE}/shader/compiler_settings.h"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/expr.cpp"
"${VIDEO_CORE}/shader/expr.h"
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
-1
View File
@@ -1,7 +1,6 @@
yuzu emulator
=============
[![Travis CI Build Status](https://travis-ci.org/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.org/yuzu-emu/yuzu)
[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/77k97svb2usreu68?svg=true)](https://ci.appveyor.com/project/bunnei/yuzu)
[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](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/).
+4 -4
View File
@@ -77,11 +77,11 @@ if (ENABLE_VULKAN)
add_subdirectory(sirit)
endif()
# libzip
add_subdirectory(libzip)
# zlib
add_subdirectory(zlib)
add_subdirectory(zlib EXCLUDE_FROM_ALL)
# libzip
add_subdirectory(libzip EXCLUDE_FROM_ALL)
if (ENABLE_WEB_SERVICE)
# LibreSSL
+1 -1
+21 -12
View File
@@ -341,15 +341,24 @@ 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
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 (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
sd_card.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
Note:
Some icons are different in different themes, and they are separately listed
only when they have different licenses/origins.
+18
View File
@@ -15,11 +15,23 @@ if (DEFINED ENV{CI})
set(BUILD_TAG $ENV{AZURE_REPO_TAG})
endif()
endif()
if (DEFINED ENV{TITLEBARFORMATIDLE})
set(TITLE_BAR_FORMAT_IDLE $ENV{TITLEBARFORMATIDLE})
endif ()
if (DEFINED ENV{TITLEBARFORMATRUNNING})
set(TITLE_BAR_FORMAT_RUNNING $ENV{TITLEBARFORMATRUNNING})
endif ()
if (DEFINED ENV{DISPLAYVERSION})
set(DISPLAY_VERSION $ENV{DISPLAYVERSION})
endif ()
add_custom_command(OUTPUT scm_rev.cpp
COMMAND ${CMAKE_COMMAND}
-DSRC_DIR="${CMAKE_SOURCE_DIR}"
-DBUILD_REPOSITORY="${BUILD_REPOSITORY}"
-DTITLE_BAR_FORMAT_IDLE="${TITLE_BAR_FORMAT_IDLE}"
-DTITLE_BAR_FORMAT_RUNNING="${TITLE_BAR_FORMAT_RUNNING}"
-DBUILD_TAG="${BUILD_TAG}"
-DBUILD_ID="${DISPLAY_VERSION}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
DEPENDS
# WARNING! It was too much work to try and make a common location for this list,
@@ -60,9 +72,15 @@ add_custom_command(OUTPUT scm_rev.cpp
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/warp.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/ast.cpp"
"${VIDEO_CORE}/shader/ast.h"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/compiler_settings.cpp"
"${VIDEO_CORE}/shader/compiler_settings.h"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/expr.cpp"
"${VIDEO_CORE}/shader/expr.h"
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
+2 -1
View File
@@ -713,7 +713,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
case UserPath::RootDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
break;
case UserPath::UserDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP;
@@ -721,6 +720,8 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP;
paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP;
break;
default:
break;
}
}
+6
View File
@@ -11,6 +11,9 @@
#define BUILD_DATE "@BUILD_DATE@"
#define BUILD_FULLNAME "@BUILD_FULLNAME@"
#define BUILD_VERSION "@BUILD_VERSION@"
#define BUILD_ID "@BUILD_ID@"
#define TITLE_BAR_FORMAT_IDLE "@TITLE_BAR_FORMAT_IDLE@"
#define TITLE_BAR_FORMAT_RUNNING "@TITLE_BAR_FORMAT_RUNNING@"
#define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@"
namespace Common {
@@ -22,6 +25,9 @@ const char g_build_name[] = BUILD_NAME;
const char g_build_date[] = BUILD_DATE;
const char g_build_fullname[] = BUILD_FULLNAME;
const char g_build_version[] = BUILD_VERSION;
const char g_build_id[] = BUILD_ID;
const char g_title_bar_format_idle[] = TITLE_BAR_FORMAT_IDLE;
const char g_title_bar_format_running[] = TITLE_BAR_FORMAT_RUNNING;
const char g_shader_cache_version[] = SHADER_CACHE_VERSION;
} // namespace
+3
View File
@@ -13,6 +13,9 @@ extern const char g_build_name[];
extern const char g_build_date[];
extern const char g_build_fullname[];
extern const char g_build_version[];
extern const char g_build_id[];
extern const char g_title_bar_format_idle[];
extern const char g_title_bar_format_running[];
extern const char g_shader_cache_version[];
} // namespace Common
+1 -1
View File
@@ -423,7 +423,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
const RSAKeyPair<2048>& key) {
const auto issuer = ticket.GetData().issuer;
if (issuer == std::array<u8, 0x40>{})
if (IsAllZeroArray(issuer))
return {};
if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
+1 -2
View File
@@ -147,6 +147,7 @@ private:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
Core::System& system;
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
Kernel::EventPair accumulated_suspended_tick_changed_event;
@@ -154,8 +155,6 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
Core::System& system;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
+2 -1
View File
@@ -13,7 +13,7 @@ constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION =
PerformanceConfiguration::Config7;
Controller::Controller(Core::Timing::CoreTiming& core_timing)
: core_timing(core_timing), configs{
: core_timing{core_timing}, configs{
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
} {}
@@ -63,6 +63,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
void Controller::SetClockSpeed(u32 mhz) {
LOG_INFO(Service_APM, "called, mhz={:08X}", mhz);
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
// TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
}
} // namespace Service::APM
+3 -3
View File
@@ -50,7 +50,7 @@ enum class PerformanceMode : u8 {
// system during times of high load -- this simply maps to different PerformanceConfigs to use.
class Controller {
public:
Controller(Core::Timing::CoreTiming& core_timing);
explicit Controller(Core::Timing::CoreTiming& core_timing);
~Controller();
void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config);
@@ -62,9 +62,9 @@ public:
private:
void SetClockSpeed(u32 mhz);
std::map<PerformanceMode, PerformanceConfiguration> configs;
[[maybe_unused]] Core::Timing::CoreTiming& core_timing;
Core::Timing::CoreTiming& core_timing;
std::map<PerformanceMode, PerformanceConfiguration> configs;
};
} // namespace Service::APM
+1 -1
View File
@@ -205,7 +205,7 @@ private:
AudioCore::StreamPtr stream;
std::string device_name;
AudoutParams audio_params{};
[[maybe_unused]] AudoutParams audio_params {};
/// This is the event handle used to check if the audio buffer was released
Kernel::EventPair buffer_event;
+1 -1
View File
@@ -88,7 +88,7 @@ std::ostream& operator<<(std::ostream& os, DownloadResult result) {
constexpr u32 PORT = 443;
constexpr u32 TIMEOUT_SECONDS = 30;
constexpr u64 VFS_COPY_BLOCK_SIZE = 1ull << 24; // 4MB
[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
namespace {
+1 -1
View File
@@ -567,7 +567,7 @@ std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) {
Module::Interface::Interface(std::shared_ptr<Module> module, FileSystem::FileSystemController& fsc,
const char* name)
: ServiceFramework(name), module(std::move(module)), fsc(fsc),
: ServiceFramework(name), fsc(fsc), module(std::move(module)),
backend(CreateBackendFromSettings([&fsc](u64 tid) { return fsc.GetBCATDirectory(tid); })) {}
Module::Interface::~Interface() = default;
+1 -1
View File
@@ -803,7 +803,7 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
-1
View File
@@ -237,7 +237,6 @@ private:
};
Common::UUID uuid;
bool is_event_created = false;
Kernel::EventPair notification_event;
std::queue<SizedNotificationInfo> notifications;
States states{};
@@ -11,11 +11,10 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
Controller_DebugPad::Controller_DebugPad(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_DebugPad::Controller_DebugPad(Core::System& system) : ControllerBase(system) {}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
@@ -89,6 +89,5 @@ private:
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
analogs;
Core::System& system;
};
} // namespace Service::HID
@@ -10,8 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
Controller_Gesture::Controller_Gesture(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {}
@@ -59,6 +59,5 @@ private:
std::array<GestureState, 17> gesture_states;
};
SharedMemory shared_memory{};
Core::System& system;
};
} // namespace Service::HID
@@ -12,8 +12,7 @@ namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
constexpr u8 KEYS_PER_BYTE = 8;
Controller_Keyboard::Controller_Keyboard(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Keyboard::Controller_Keyboard(Core::System& system) : ControllerBase(system) {}
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
@@ -53,6 +53,5 @@ private:
keyboard_keys;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
keyboard_mods;
Core::System& system;
};
} // namespace Service::HID
@@ -11,7 +11,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system) {}
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
@@ -53,6 +53,5 @@ private:
std::unique_ptr<Input::MouseDevice> mouse_device;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
mouse_button_devices;
Core::System& system;
};
} // namespace Service::HID
+8 -10
View File
@@ -20,7 +20,7 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
@@ -105,6 +105,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
@@ -165,13 +167,14 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
styleset_changed_events[controller_idx].writable->Signal();
}
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i));
kernel, Kernel::ResetType::Manual, fmt::format("npad:NpadStyleSetChanged_{}", i));
}
if (!IsControllerActivated()) {
@@ -238,7 +241,7 @@ void Controller_NPad::OnRelease() {}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
const auto controller_type = connected_controllers[controller_idx].type;
[[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
return;
}
@@ -345,6 +348,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
handheld_entry.connection_status.IsWired.Assign(1);
@@ -433,7 +438,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
bool had_controller_update = false;
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
auto& controller = connected_controllers[i];
if (!controller.is_connected) {
@@ -452,10 +456,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
controller.type = requested_controller;
InitNewlyAddedControler(i);
}
had_controller_update = true;
}
if (had_controller_update) {
styleset_changed_events[i].writable->Signal();
}
}
}
@@ -481,7 +481,6 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
styleset_changed_events[npad_index].writable->Signal();
shared_memory_entries[npad_index].pad_assignment = assignment_mode;
}
}
@@ -507,7 +506,6 @@ Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEven
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
// be signalled at least once, and signaled after a new controller is connected?
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
styleset_event.writable->Signal();
return styleset_event.readable;
}
@@ -9,8 +9,7 @@
namespace Service::HID {
Controller_Stubbed::Controller_Stubbed(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Stubbed::Controller_Stubbed(Core::System& system) : ControllerBase(system) {}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
@@ -30,6 +30,5 @@ public:
private:
bool smart_update{};
std::size_t common_offset{};
Core::System& system;
};
} // namespace Service::HID
@@ -13,8 +13,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}
@@ -69,6 +69,5 @@ private:
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
s64_le last_touch{};
Core::System& system;
};
} // namespace Service::HID
@@ -10,7 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system) {}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
@@ -56,6 +56,5 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
Core::System& system;
};
} // namespace Service::HID
+4 -2
View File
@@ -38,8 +38,10 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
[[maybe_unused]] constexpr s64 accelerometer_update_ticks =
static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
+5 -4
View File
@@ -18,8 +18,8 @@
namespace Service::NFP {
namespace ErrCodes {
constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
-1); // TODO(ogniK): Find the actual error code
[[maybe_unused]] constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
-1); // TODO(ogniK): Find the actual error code
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
} // namespace ErrCodes
@@ -35,7 +35,7 @@ Module::Interface::~Interface() = default;
class IUser final : public ServiceFramework<IUser> {
public:
IUser(Module::Interface& nfp_interface, Core::System& system)
: ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
: ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
@@ -183,6 +183,8 @@ private:
case DeviceState::TagRemoved:
device_state = DeviceState::Initialized;
break;
default:
break;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -324,7 +326,6 @@ private:
Kernel::EventPair deactivate_event;
Kernel::EventPair availability_change_event;
const Module::Interface& nfp_interface;
Core::System& system;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
@@ -45,6 +45,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
return GetVARegions(input, output);
case IoctlCommand::IocUnmapBufferCommand:
return UnmapBuffer(input, output);
default:
break;
}
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
@@ -38,9 +38,10 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
return IocCtrlEventUnregister(input, output);
case IoctlCommand::IocCtrlEventSignalCommand:
return IocCtrlEventSignal(input, output);
default:
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -40,9 +40,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
return FlushL2(input, output);
case IoctlCommand::IocGetGpuTime:
return GetGpuTime(input, output);
default:
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
@@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
return GetWaitbase(input, output);
case IoctlCommand::IocChannelSetTimeoutCommand:
return ChannelSetTimeout(input, output);
default:
break;
}
if (command.group == NVGPU_IOCTL_MAGIC) {
+6
View File
@@ -105,9 +105,15 @@ add_library(video_core STATIC
shader/decode/warp.cpp
shader/decode/xmad.cpp
shader/decode/other.cpp
shader/ast.cpp
shader/ast.h
shader/control_flow.cpp
shader/control_flow.h
shader/compiler_settings.cpp
shader/compiler_settings.h
shader/decode.cpp
shader/expr.cpp
shader/expr.h
shader/node_helper.cpp
shader/node_helper.h
shader/node.h
@@ -19,6 +19,7 @@
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
@@ -334,39 +335,24 @@ constexpr bool IsVertexShader(ProgramType stage) {
return stage == ProgramType::VertexA || stage == ProgramType::VertexB;
}
class ASTDecompiler;
class ExprDecompiler;
class GLSLDecompiler final {
public:
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage,
std::string suffix)
: device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
void Decompile() {
DeclareVertex();
DeclareGeometry();
DeclareRegisters();
DeclarePredicates();
DeclareLocalMemory();
DeclareSharedMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareConstantBuffers();
DeclareGlobalMemory();
DeclareSamplers();
DeclarePhysicalAttributeReader();
DeclareImages();
code.AddLine("void execute_{}() {{", suffix);
++code.scope;
void DecompileBranchMode() {
// VM's program counter
const auto first_address = ir.GetBasicBlocks().begin()->first;
code.AddLine("uint jmp_to = {}U;", first_address);
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
// unlikely that shaders will use 20 nested SSYs and PBKs.
constexpr u32 FLOW_STACK_SIZE = 20;
if (!ir.IsFlowStackDisabled()) {
constexpr u32 FLOW_STACK_SIZE = 20;
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
@@ -392,10 +378,37 @@ public:
code.AddLine("default: return;");
code.AddLine("}}");
for (std::size_t i = 0; i < 2; ++i) {
--code.scope;
code.AddLine("}}");
--code.scope;
code.AddLine("}}");
}
void DecompileAST();
void Decompile() {
DeclareVertex();
DeclareGeometry();
DeclareRegisters();
DeclarePredicates();
DeclareLocalMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareConstantBuffers();
DeclareGlobalMemory();
DeclareSamplers();
DeclarePhysicalAttributeReader();
code.AddLine("void execute_{}() {{", suffix);
++code.scope;
if (ir.IsDecompiled()) {
DecompileAST();
} else {
DecompileBranchMode();
}
--code.scope;
code.AddLine("}}");
}
std::string GetResult() {
@@ -424,6 +437,9 @@ public:
}
private:
friend class ASTDecompiler;
friend class ExprDecompiler;
void DeclareVertex() {
if (!IsVertexShader(stage))
return;
@@ -1821,10 +1837,9 @@ private:
return {};
}
Expression Exit(Operation operation) {
void PreExit() {
if (stage != ProgramType::Fragment) {
code.AddLine("return;");
return {};
return;
}
const auto& used_registers = ir.GetRegisters();
const auto SafeGetRegister = [&](u32 reg) -> Expression {
@@ -1856,7 +1871,10 @@ private:
// already contains one past the last color register.
code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat());
}
}
Expression Exit(Operation operation) {
PreExit();
code.AddLine("return;");
return {};
}
@@ -2253,6 +2271,208 @@ private:
ShaderWriter code;
};
static constexpr std::string_view flow_var = "flow_var_";
std::string GetFlowVariable(u32 i) {
return fmt::format("{}{}", flow_var, i);
}
class ExprDecompiler {
public:
explicit ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
void operator()(VideoCommon::Shader::ExprAnd& expr) {
inner += "( ";
std::visit(*this, *expr.operand1);
inner += " && ";
std::visit(*this, *expr.operand2);
inner += ')';
}
void operator()(VideoCommon::Shader::ExprOr& expr) {
inner += "( ";
std::visit(*this, *expr.operand1);
inner += " || ";
std::visit(*this, *expr.operand2);
inner += ')';
}
void operator()(VideoCommon::Shader::ExprNot& expr) {
inner += '!';
std::visit(*this, *expr.operand1);
}
void operator()(VideoCommon::Shader::ExprPredicate& expr) {
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
inner += decomp.GetPredicate(pred);
}
void operator()(VideoCommon::Shader::ExprCondCode& expr) {
const Node cc = decomp.ir.GetConditionCode(expr.cc);
std::string target;
if (const auto pred = std::get_if<PredicateNode>(&*cc)) {
const auto index = pred->GetIndex();
switch (index) {
case Tegra::Shader::Pred::NeverExecute:
target = "false";
case Tegra::Shader::Pred::UnusedIndex:
target = "true";
default:
target = decomp.GetPredicate(index);
}
} else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) {
target = decomp.GetInternalFlag(flag->GetFlag());
} else {
UNREACHABLE();
}
inner += target;
}
void operator()(VideoCommon::Shader::ExprVar& expr) {
inner += GetFlowVariable(expr.var_index);
}
void operator()(VideoCommon::Shader::ExprBoolean& expr) {
inner += expr.value ? "true" : "false";
}
std::string& GetResult() {
return inner;
}
private:
std::string inner;
GLSLDecompiler& decomp;
};
class ASTDecompiler {
public:
explicit ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
void operator()(VideoCommon::Shader::ASTProgram& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(VideoCommon::Shader::ASTIfThen& ast) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
decomp.code.scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
decomp.code.scope--;
decomp.code.AddLine("}}");
}
void operator()(VideoCommon::Shader::ASTIfElse& ast) {
decomp.code.AddLine("else {{");
decomp.code.scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
decomp.code.scope--;
decomp.code.AddLine("}}");
}
void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) {
UNREACHABLE();
}
void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) {
decomp.VisitBlock(ast.nodes);
}
void operator()(VideoCommon::Shader::ASTVarSet& ast) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("{} = {};", GetFlowVariable(ast.index), expr_parser.GetResult());
}
void operator()(VideoCommon::Shader::ASTLabel& ast) {
decomp.code.AddLine("// Label_{}:", ast.index);
}
void operator()(VideoCommon::Shader::ASTGoto& ast) {
UNREACHABLE();
}
void operator()(VideoCommon::Shader::ASTDoWhile& ast) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("do {{");
decomp.code.scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
decomp.code.scope--;
decomp.code.AddLine("}} while({});", expr_parser.GetResult());
}
void operator()(VideoCommon::Shader::ASTReturn& ast) {
const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
if (!is_true) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
decomp.code.scope++;
}
if (ast.kills) {
decomp.code.AddLine("discard;");
} else {
decomp.PreExit();
decomp.code.AddLine("return;");
}
if (!is_true) {
decomp.code.scope--;
decomp.code.AddLine("}}");
}
}
void operator()(VideoCommon::Shader::ASTBreak& ast) {
const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
if (!is_true) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
decomp.code.scope++;
}
decomp.code.AddLine("break;");
if (!is_true) {
decomp.code.scope--;
decomp.code.AddLine("}}");
}
}
void Visit(VideoCommon::Shader::ASTNode& node) {
std::visit(*this, *node->GetInnerData());
}
private:
GLSLDecompiler& decomp;
};
void GLSLDecompiler::DecompileAST() {
const u32 num_flow_variables = ir.GetASTNumVariables();
for (u32 i = 0; i < num_flow_variables; i++) {
code.AddLine("bool {} = false;", GetFlowVariable(i));
}
ASTDecompiler decompiler{*this};
VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
decompiler.Visit(program);
}
} // Anonymous namespace
std::string GetCommonDeclarations() {
@@ -11,12 +11,16 @@
namespace OpenGL::GLShader {
using Tegra::Engines::Maxwell3D;
using VideoCommon::Shader::CompileDepth;
using VideoCommon::Shader::CompilerSettings;
using VideoCommon::Shader::ProgramCode;
using VideoCommon::Shader::ShaderIR;
static constexpr u32 PROGRAM_OFFSET = 10;
static constexpr u32 COMPUTE_OFFSET = 0;
static constexpr CompilerSettings settings{CompileDepth::NoFlowStack, true};
ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) {
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
@@ -31,13 +35,14 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
)";
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a);
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings);
const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB;
ProgramResult program = Decompile(device, program_ir, stage, "vertex");
out += program.first;
if (setup.IsDualProgram()) {
const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b);
const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b,
settings);
ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b");
out += program_b.first;
}
@@ -80,7 +85,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
)";
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a);
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings);
ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry");
out += program.first;
@@ -114,7 +119,8 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
};
)";
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a);
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings);
ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment");
out += program.first;
@@ -133,7 +139,7 @@ ProgramResult GenerateComputeShader(const Device& device, const ShaderSetup& set
std::string out = "// Shader Unique Id: CS" + id + "\n\n";
out += GetCommonDeclarations();
const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a);
const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a, settings);
ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute");
out += program.first;
@@ -88,6 +88,9 @@ bool IsPrecise(Operation operand) {
} // namespace
class ASTDecompiler;
class ExprDecompiler;
class SPIRVDecompiler : public Sirit::Module {
public:
explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage)
@@ -97,27 +100,7 @@ public:
AddExtension("SPV_KHR_variable_pointers");
}
void Decompile() {
AllocateBindings();
AllocateLabels();
DeclareVertex();
DeclareGeometry();
DeclareFragment();
DeclareRegisters();
DeclarePredicates();
DeclareLocalMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareConstantBuffers();
DeclareGlobalBuffers();
DeclareSamplers();
execute_function =
Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void)));
Emit(OpLabel());
void DecompileBranchMode() {
const u32 first_address = ir.GetBasicBlocks().begin()->first;
const Id loop_label = OpLabel("loop");
const Id merge_label = OpLabel("merge");
@@ -174,6 +157,43 @@ public:
Emit(continue_label);
Emit(OpBranch(loop_label));
Emit(merge_label);
}
void DecompileAST();
void Decompile() {
const bool is_fully_decompiled = ir.IsDecompiled();
AllocateBindings();
if (!is_fully_decompiled) {
AllocateLabels();
}
DeclareVertex();
DeclareGeometry();
DeclareFragment();
DeclareRegisters();
DeclarePredicates();
if (is_fully_decompiled) {
DeclareFlowVariables();
}
DeclareLocalMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareConstantBuffers();
DeclareGlobalBuffers();
DeclareSamplers();
execute_function =
Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void)));
Emit(OpLabel());
if (is_fully_decompiled) {
DecompileAST();
} else {
DecompileBranchMode();
}
Emit(OpReturn());
Emit(OpFunctionEnd());
}
@@ -206,6 +226,9 @@ public:
}
private:
friend class ASTDecompiler;
friend class ExprDecompiler;
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
void AllocateBindings() {
@@ -294,6 +317,14 @@ private:
}
}
void DeclareFlowVariables() {
for (u32 i = 0; i < ir.GetASTNumVariables(); i++) {
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
Name(id, fmt::format("flow_var_{}", static_cast<u32>(i)));
flow_variables.emplace(i, AddGlobalVariable(id));
}
}
void DeclareLocalMemory() {
if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) {
const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4);
@@ -615,9 +646,15 @@ private:
Emit(OpBranchConditional(condition, true_label, skip_label));
Emit(true_label);
++conditional_nest_count;
VisitBasicBlock(conditional->GetCode());
--conditional_nest_count;
Emit(OpBranch(skip_label));
if (inside_branch == 0) {
Emit(OpBranch(skip_label));
} else {
inside_branch--;
}
Emit(skip_label);
return {};
@@ -980,7 +1017,11 @@ private:
UNIMPLEMENTED_IF(!target);
Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue())));
BranchingOp([&]() { Emit(OpBranch(continue_label)); });
Emit(OpBranch(continue_label));
inside_branch = conditional_nest_count;
if (conditional_nest_count == 0) {
Emit(OpLabel());
}
return {};
}
@@ -988,7 +1029,11 @@ private:
const Id op_a = VisitOperand<Type::Uint>(operation, 0);
Emit(OpStore(jmp_to, op_a));
BranchingOp([&]() { Emit(OpBranch(continue_label)); });
Emit(OpBranch(continue_label));
inside_branch = conditional_nest_count;
if (conditional_nest_count == 0) {
Emit(OpLabel());
}
return {};
}
@@ -1015,11 +1060,15 @@ private:
Emit(OpStore(flow_stack_top, previous));
Emit(OpStore(jmp_to, target));
BranchingOp([&]() { Emit(OpBranch(continue_label)); });
Emit(OpBranch(continue_label));
inside_branch = conditional_nest_count;
if (conditional_nest_count == 0) {
Emit(OpLabel());
}
return {};
}
Id Exit(Operation operation) {
Id PreExit() {
switch (stage) {
case ShaderStage::Vertex: {
// TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't
@@ -1067,12 +1116,35 @@ private:
}
}
BranchingOp([&]() { Emit(OpReturn()); });
return {};
}
Id Exit(Operation operation) {
PreExit();
inside_branch = conditional_nest_count;
if (conditional_nest_count > 0) {
Emit(OpReturn());
} else {
const Id dummy = OpLabel();
Emit(OpBranch(dummy));
Emit(dummy);
Emit(OpReturn());
Emit(OpLabel());
}
return {};
}
Id Discard(Operation operation) {
BranchingOp([&]() { Emit(OpKill()); });
inside_branch = conditional_nest_count;
if (conditional_nest_count > 0) {
Emit(OpKill());
} else {
const Id dummy = OpLabel();
Emit(OpBranch(dummy));
Emit(dummy);
Emit(OpKill());
Emit(OpLabel());
}
return {};
}
@@ -1267,17 +1339,6 @@ private:
return {};
}
void BranchingOp(std::function<void()> call) {
const Id true_label = OpLabel();
const Id skip_label = OpLabel();
Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten));
Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0));
Emit(true_label);
call();
Emit(skip_label);
}
std::tuple<Id, Id> CreateFlowStack() {
// TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely
// that shaders will use 20 nested SSYs and PBKs.
@@ -1483,6 +1544,8 @@ private:
const ShaderIR& ir;
const ShaderStage stage;
const Tegra::Shader::Header header;
u64 conditional_nest_count{};
u64 inside_branch{};
const Id t_void = Name(TypeVoid(), "void");
@@ -1545,6 +1608,7 @@ private:
Id per_vertex{};
std::map<u32, Id> registers;
std::map<Tegra::Shader::Pred, Id> predicates;
std::map<u32, Id> flow_variables;
Id local_memory{};
std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{};
std::map<Attribute::Index, Id> input_attributes;
@@ -1580,6 +1644,223 @@ private:
std::map<u32, Id> labels;
};
class ExprDecompiler {
public:
explicit ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {}
Id operator()(VideoCommon::Shader::ExprAnd& expr) {
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
const Id op1 = Visit(expr.operand1);
const Id op2 = Visit(expr.operand2);
return decomp.Emit(decomp.OpLogicalAnd(type_def, op1, op2));
}
Id operator()(VideoCommon::Shader::ExprOr& expr) {
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
const Id op1 = Visit(expr.operand1);
const Id op2 = Visit(expr.operand2);
return decomp.Emit(decomp.OpLogicalOr(type_def, op1, op2));
}
Id operator()(VideoCommon::Shader::ExprNot& expr) {
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
const Id op1 = Visit(expr.operand1);
return decomp.Emit(decomp.OpLogicalNot(type_def, op1));
}
Id operator()(VideoCommon::Shader::ExprPredicate& expr) {
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred)));
}
Id operator()(VideoCommon::Shader::ExprCondCode& expr) {
const Node cc = decomp.ir.GetConditionCode(expr.cc);
Id target;
if (const auto pred = std::get_if<PredicateNode>(&*cc)) {
const auto index = pred->GetIndex();
switch (index) {
case Tegra::Shader::Pred::NeverExecute:
target = decomp.v_false;
case Tegra::Shader::Pred::UnusedIndex:
target = decomp.v_true;
default:
target = decomp.predicates.at(index);
}
} else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) {
target = decomp.internal_flags.at(static_cast<u32>(flag->GetFlag()));
}
return decomp.Emit(decomp.OpLoad(decomp.t_bool, target));
}
Id operator()(VideoCommon::Shader::ExprVar& expr) {
return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index)));
}
Id operator()(VideoCommon::Shader::ExprBoolean& expr) {
return expr.value ? decomp.v_true : decomp.v_false;
}
Id Visit(VideoCommon::Shader::Expr& node) {
return std::visit(*this, *node);
}
private:
SPIRVDecompiler& decomp;
};
class ASTDecompiler {
public:
explicit ASTDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {}
void operator()(VideoCommon::Shader::ASTProgram& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(VideoCommon::Shader::ASTIfThen& ast) {
ExprDecompiler expr_parser{decomp};
const Id condition = expr_parser.Visit(ast.condition);
const Id then_label = decomp.OpLabel();
const Id endif_label = decomp.OpLabel();
decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone));
decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label));
decomp.Emit(then_label);
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
decomp.Emit(decomp.OpBranch(endif_label));
decomp.Emit(endif_label);
}
void operator()(VideoCommon::Shader::ASTIfElse& ast) {
UNREACHABLE();
}
void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) {
UNREACHABLE();
}
void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) {
decomp.VisitBasicBlock(ast.nodes);
}
void operator()(VideoCommon::Shader::ASTVarSet& ast) {
ExprDecompiler expr_parser{decomp};
const Id condition = expr_parser.Visit(ast.condition);
decomp.Emit(decomp.OpStore(decomp.flow_variables.at(ast.index), condition));
}
void operator()(VideoCommon::Shader::ASTLabel& ast) {
// Do nothing
}
void operator()(VideoCommon::Shader::ASTGoto& ast) {
UNREACHABLE();
}
void operator()(VideoCommon::Shader::ASTDoWhile& ast) {
const Id loop_label = decomp.OpLabel();
const Id endloop_label = decomp.OpLabel();
const Id loop_start_block = decomp.OpLabel();
const Id loop_end_block = decomp.OpLabel();
current_loop_exit = endloop_label;
decomp.Emit(decomp.OpBranch(loop_label));
decomp.Emit(loop_label);
decomp.Emit(
decomp.OpLoopMerge(endloop_label, loop_end_block, spv::LoopControlMask::MaskNone));
decomp.Emit(decomp.OpBranch(loop_start_block));
decomp.Emit(loop_start_block);
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
ExprDecompiler expr_parser{decomp};
const Id condition = expr_parser.Visit(ast.condition);
decomp.Emit(decomp.OpBranchConditional(condition, loop_label, endloop_label));
decomp.Emit(endloop_label);
}
void operator()(VideoCommon::Shader::ASTReturn& ast) {
if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) {
ExprDecompiler expr_parser{decomp};
const Id condition = expr_parser.Visit(ast.condition);
const Id then_label = decomp.OpLabel();
const Id endif_label = decomp.OpLabel();
decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone));
decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label));
decomp.Emit(then_label);
if (ast.kills) {
decomp.Emit(decomp.OpKill());
} else {
decomp.PreExit();
decomp.Emit(decomp.OpReturn());
}
decomp.Emit(endif_label);
} else {
const Id next_block = decomp.OpLabel();
decomp.Emit(decomp.OpBranch(next_block));
decomp.Emit(next_block);
if (ast.kills) {
decomp.Emit(decomp.OpKill());
} else {
decomp.PreExit();
decomp.Emit(decomp.OpReturn());
}
decomp.Emit(decomp.OpLabel());
}
}
void operator()(VideoCommon::Shader::ASTBreak& ast) {
if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) {
ExprDecompiler expr_parser{decomp};
const Id condition = expr_parser.Visit(ast.condition);
const Id then_label = decomp.OpLabel();
const Id endif_label = decomp.OpLabel();
decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone));
decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label));
decomp.Emit(then_label);
decomp.Emit(decomp.OpBranch(current_loop_exit));
decomp.Emit(endif_label);
} else {
const Id next_block = decomp.OpLabel();
decomp.Emit(decomp.OpBranch(next_block));
decomp.Emit(next_block);
decomp.Emit(decomp.OpBranch(current_loop_exit));
decomp.Emit(decomp.OpLabel());
}
}
void Visit(VideoCommon::Shader::ASTNode& node) {
std::visit(*this, *node->GetInnerData());
}
private:
SPIRVDecompiler& decomp;
Id current_loop_exit{};
};
void SPIRVDecompiler::DecompileAST() {
const u32 num_flow_variables = ir.GetASTNumVariables();
for (u32 i = 0; i < num_flow_variables; i++) {
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
Name(id, fmt::format("flow_var_{}", i));
flow_variables.emplace(i, AddGlobalVariable(id));
}
ASTDecompiler decompiler{*this};
VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
decompiler.Visit(program);
const Id next_block = OpLabel();
Emit(OpBranch(next_block));
Emit(next_block);
}
DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
Maxwell::ShaderStage stage) {
auto decompiler = std::make_unique<SPIRVDecompiler>(device, ir, stage);
+738
View File
@@ -0,0 +1,738 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/expr.h"
namespace VideoCommon::Shader {
ASTZipper::ASTZipper() = default;
void ASTZipper::Init(const ASTNode new_first, const ASTNode parent) {
ASSERT(new_first->manager == nullptr);
first = new_first;
last = new_first;
ASTNode current = first;
while (current) {
current->manager = this;
current->parent = parent;
last = current;
current = current->next;
}
}
void ASTZipper::PushBack(const ASTNode new_node) {
ASSERT(new_node->manager == nullptr);
new_node->previous = last;
if (last) {
last->next = new_node;
}
new_node->next.reset();
last = new_node;
if (!first) {
first = new_node;
}
new_node->manager = this;
}
void ASTZipper::PushFront(const ASTNode new_node) {
ASSERT(new_node->manager == nullptr);
new_node->previous.reset();
new_node->next = first;
if (first) {
first->previous = new_node;
}
if (last == first) {
last = new_node;
}
first = new_node;
new_node->manager = this;
}
void ASTZipper::InsertAfter(const ASTNode new_node, const ASTNode at_node) {
ASSERT(new_node->manager == nullptr);
if (!at_node) {
PushFront(new_node);
return;
}
const ASTNode next = at_node->next;
if (next) {
next->previous = new_node;
}
new_node->previous = at_node;
if (at_node == last) {
last = new_node;
}
new_node->next = next;
at_node->next = new_node;
new_node->manager = this;
}
void ASTZipper::InsertBefore(const ASTNode new_node, const ASTNode at_node) {
ASSERT(new_node->manager == nullptr);
if (!at_node) {
PushBack(new_node);
return;
}
const ASTNode previous = at_node->previous;
if (previous) {
previous->next = new_node;
}
new_node->next = at_node;
if (at_node == first) {
first = new_node;
}
new_node->previous = previous;
at_node->previous = new_node;
new_node->manager = this;
}
void ASTZipper::DetachTail(ASTNode node) {
ASSERT(node->manager == this);
if (node == first) {
first.reset();
last.reset();
return;
}
last = node->previous;
last->next.reset();
node->previous.reset();
ASTNode current = std::move(node);
while (current) {
current->manager = nullptr;
current->parent.reset();
current = current->next;
}
}
void ASTZipper::DetachSegment(const ASTNode start, const ASTNode end) {
ASSERT(start->manager == this && end->manager == this);
if (start == end) {
DetachSingle(start);
return;
}
const ASTNode prev = start->previous;
const ASTNode post = end->next;
if (!prev) {
first = post;
} else {
prev->next = post;
}
if (!post) {
last = prev;
} else {
post->previous = prev;
}
start->previous.reset();
end->next.reset();
ASTNode current = start;
bool found = false;
while (current) {
current->manager = nullptr;
current->parent.reset();
found |= current == end;
current = current->next;
}
ASSERT(found);
}
void ASTZipper::DetachSingle(const ASTNode node) {
ASSERT(node->manager == this);
const ASTNode prev = node->previous;
const ASTNode post = node->next;
node->previous.reset();
node->next.reset();
if (!prev) {
first = post;
} else {
prev->next = post;
}
if (!post) {
last = prev;
} else {
post->previous = prev;
}
node->manager = nullptr;
node->parent.reset();
}
void ASTZipper::Remove(const ASTNode node) {
ASSERT(node->manager == this);
const ASTNode next = node->next;
const ASTNode previous = node->previous;
if (previous) {
previous->next = next;
}
if (next) {
next->previous = previous;
}
node->parent.reset();
node->manager = nullptr;
if (node == last) {
last = previous;
}
if (node == first) {
first = next;
}
}
class ExprPrinter final {
public:
void operator()(const ExprAnd& expr) {
inner += "( ";
std::visit(*this, *expr.operand1);
inner += " && ";
std::visit(*this, *expr.operand2);
inner += ')';
}
void operator()(const ExprOr& expr) {
inner += "( ";
std::visit(*this, *expr.operand1);
inner += " || ";
std::visit(*this, *expr.operand2);
inner += ')';
}
void operator()(const ExprNot& expr) {
inner += "!";
std::visit(*this, *expr.operand1);
}
void operator()(const ExprPredicate& expr) {
inner += "P" + std::to_string(expr.predicate);
}
void operator()(const ExprCondCode& expr) {
u32 cc = static_cast<u32>(expr.cc);
inner += "CC" + std::to_string(cc);
}
void operator()(const ExprVar& expr) {
inner += "V" + std::to_string(expr.var_index);
}
void operator()(const ExprBoolean& expr) {
inner += expr.value ? "true" : "false";
}
const std::string& GetResult() const {
return inner;
}
std::string inner{};
};
class ASTPrinter {
public:
void operator()(const ASTProgram& ast) {
scope++;
inner += "program {\n";
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
inner += "}\n";
scope--;
}
void operator()(const ASTIfThen& ast) {
ExprPrinter expr_parser{};
std::visit(expr_parser, *ast.condition);
inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n";
scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
scope--;
inner += Ident() + "}\n";
}
void operator()(const ASTIfElse& ast) {
inner += Ident() + "else {\n";
scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
scope--;
inner += Ident() + "}\n";
}
void operator()(const ASTBlockEncoded& ast) {
inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) +
");\n";
}
void operator()(const ASTBlockDecoded& ast) {
inner += Ident() + "Block;\n";
}
void operator()(const ASTVarSet& ast) {
ExprPrinter expr_parser{};
std::visit(expr_parser, *ast.condition);
inner +=
Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n";
}
void operator()(const ASTLabel& ast) {
inner += "Label_" + std::to_string(ast.index) + ":\n";
}
void operator()(const ASTGoto& ast) {
ExprPrinter expr_parser{};
std::visit(expr_parser, *ast.condition);
inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" +
std::to_string(ast.label) + ";\n";
}
void operator()(const ASTDoWhile& ast) {
ExprPrinter expr_parser{};
std::visit(expr_parser, *ast.condition);
inner += Ident() + "do {\n";
scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
scope--;
inner += Ident() + "} while (" + expr_parser.GetResult() + ");\n";
}
void operator()(const ASTReturn& ast) {
ExprPrinter expr_parser{};
std::visit(expr_parser, *ast.condition);
inner += Ident() + "(" + expr_parser.GetResult() + ") -> " +
(ast.kills ? "discard" : "exit") + ";\n";
}
void operator()(const ASTBreak& ast) {
ExprPrinter expr_parser{};
std::visit(expr_parser, *ast.condition);
inner += Ident() + "(" + expr_parser.GetResult() + ") -> break;\n";
}
std::string& Ident() {
if (memo_scope == scope) {
return tabs_memo;
}
tabs_memo = tabs.substr(0, scope * 2);
memo_scope = scope;
return tabs_memo;
}
void Visit(ASTNode& node) {
std::visit(*this, *node->GetInnerData());
}
const std::string& GetResult() const {
return inner;
}
private:
std::string inner{};
u32 scope{};
std::string tabs_memo{};
u32 memo_scope{};
static constexpr std::string_view tabs{" "};
};
std::string ASTManager::Print() {
ASTPrinter printer{};
printer.Visit(main_node);
return printer.GetResult();
}
ASTManager::ASTManager(bool full_decompile, bool disable_else_derivation)
: full_decompile{full_decompile}, disable_else_derivation{disable_else_derivation} {};
ASTManager::~ASTManager() {
Clear();
}
void ASTManager::Init() {
main_node = ASTBase::Make<ASTProgram>(ASTNode{});
program = std::get_if<ASTProgram>(main_node->GetInnerData());
false_condition = MakeExpr<ExprBoolean>(false);
}
void ASTManager::DeclareLabel(u32 address) {
const auto pair = labels_map.emplace(address, labels_count);
if (pair.second) {
labels_count++;
labels.resize(labels_count);
}
}
void ASTManager::InsertLabel(u32 address) {
const u32 index = labels_map[address];
const ASTNode label = ASTBase::Make<ASTLabel>(main_node, index);
labels[index] = label;
program->nodes.PushBack(label);
}
void ASTManager::InsertGoto(Expr condition, u32 address) {
const u32 index = labels_map[address];
const ASTNode goto_node = ASTBase::Make<ASTGoto>(main_node, std::move(condition), index);
gotos.push_back(goto_node);
program->nodes.PushBack(goto_node);
}
void ASTManager::InsertBlock(u32 start_address, u32 end_address) {
ASTNode block = ASTBase::Make<ASTBlockEncoded>(main_node, start_address, end_address);
program->nodes.PushBack(std::move(block));
}
void ASTManager::InsertReturn(Expr condition, bool kills) {
ASTNode node = ASTBase::Make<ASTReturn>(main_node, std::move(condition), kills);
program->nodes.PushBack(std::move(node));
}
// The decompile algorithm is based on
// "Taming control flow: A structured approach to eliminating goto statements"
// by AM Erosa, LJ Hendren 1994. In general, the idea is to get gotos to be
// on the same structured level as the label which they jump to. This is done,
// through outward/inward movements and lifting. Once they are at the same
// level, you can enclose them in an "if" structure or a "do-while" structure.
void ASTManager::Decompile() {
auto it = gotos.begin();
while (it != gotos.end()) {
const ASTNode goto_node = *it;
const auto label_index = goto_node->GetGotoLabel();
if (!label_index) {
return;
}
const ASTNode label = labels[*label_index];
if (!full_decompile) {
// We only decompile backward jumps
if (!IsBackwardsJump(goto_node, label)) {
it++;
continue;
}
}
if (IndirectlyRelated(goto_node, label)) {
while (!DirectlyRelated(goto_node, label)) {
MoveOutward(goto_node);
}
}
if (DirectlyRelated(goto_node, label)) {
u32 goto_level = goto_node->GetLevel();
const u32 label_level = label->GetLevel();
while (label_level < goto_level) {
MoveOutward(goto_node);
goto_level--;
}
// TODO(Blinkhawk): Implement Lifting and Inward Movements
}
if (label->GetParent() == goto_node->GetParent()) {
bool is_loop = false;
ASTNode current = goto_node->GetPrevious();
while (current) {
if (current == label) {
is_loop = true;
break;
}
current = current->GetPrevious();
}
if (is_loop) {
EncloseDoWhile(goto_node, label);
} else {
EncloseIfThen(goto_node, label);
}
it = gotos.erase(it);
continue;
}
it++;
}
if (full_decompile) {
for (const ASTNode& label : labels) {
auto& manager = label->GetManager();
manager.Remove(label);
}
labels.clear();
} else {
auto label_it = labels.begin();
while (label_it != labels.end()) {
bool can_remove = true;
ASTNode label = *label_it;
for (const ASTNode& goto_node : gotos) {
const auto label_index = goto_node->GetGotoLabel();
if (!label_index) {
return;
}
ASTNode& glabel = labels[*label_index];
if (glabel == label) {
can_remove = false;
break;
}
}
if (can_remove) {
label->MarkLabelUnused();
}
}
}
}
bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const {
u32 goto_level = goto_node->GetLevel();
u32 label_level = label_node->GetLevel();
while (goto_level > label_level) {
goto_level--;
goto_node = goto_node->GetParent();
}
while (label_level > goto_level) {
label_level--;
label_node = label_node->GetParent();
}
while (goto_node->GetParent() != label_node->GetParent()) {
goto_node = goto_node->GetParent();
label_node = label_node->GetParent();
}
ASTNode current = goto_node->GetPrevious();
while (current) {
if (current == label_node) {
return true;
}
current = current->GetPrevious();
}
return false;
}
bool ASTManager::IndirectlyRelated(const ASTNode& first, const ASTNode& second) const {
return !(first->GetParent() == second->GetParent() || DirectlyRelated(first, second));
}
bool ASTManager::DirectlyRelated(const ASTNode& first, const ASTNode& second) const {
if (first->GetParent() == second->GetParent()) {
return false;
}
const u32 first_level = first->GetLevel();
const u32 second_level = second->GetLevel();
u32 min_level;
u32 max_level;
ASTNode max;
ASTNode min;
if (first_level > second_level) {
min_level = second_level;
min = second;
max_level = first_level;
max = first;
} else {
min_level = first_level;
min = first;
max_level = second_level;
max = second;
}
while (max_level > min_level) {
max_level--;
max = max->GetParent();
}
return min->GetParent() == max->GetParent();
}
void ASTManager::ShowCurrentState(std::string_view state) {
LOG_CRITICAL(HW_GPU, "\nState {}:\n\n{}\n", state, Print());
SanityCheck();
}
void ASTManager::SanityCheck() {
for (auto& label : labels) {
if (!label->GetParent()) {
LOG_CRITICAL(HW_GPU, "Sanity Check Failed");
}
}
}
void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) {
ASTZipper& zipper = goto_node->GetManager();
const ASTNode loop_start = label->GetNext();
if (loop_start == goto_node) {
zipper.Remove(goto_node);
return;
}
const ASTNode parent = label->GetParent();
const Expr condition = goto_node->GetGotoCondition();
zipper.DetachSegment(loop_start, goto_node);
const ASTNode do_while_node = ASTBase::Make<ASTDoWhile>(parent, condition);
ASTZipper* sub_zipper = do_while_node->GetSubNodes();
sub_zipper->Init(loop_start, do_while_node);
zipper.InsertAfter(do_while_node, label);
sub_zipper->Remove(goto_node);
}
void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) {
ASTZipper& zipper = goto_node->GetManager();
const ASTNode if_end = label->GetPrevious();
if (if_end == goto_node) {
zipper.Remove(goto_node);
return;
}
const ASTNode prev = goto_node->GetPrevious();
const Expr condition = goto_node->GetGotoCondition();
bool do_else = false;
if (!disable_else_derivation && prev->IsIfThen()) {
const Expr if_condition = prev->GetIfCondition();
do_else = ExprAreEqual(if_condition, condition);
}
const ASTNode parent = label->GetParent();
zipper.DetachSegment(goto_node, if_end);
ASTNode if_node;
if (do_else) {
if_node = ASTBase::Make<ASTIfElse>(parent);
} else {
Expr neg_condition = MakeExprNot(condition);
if_node = ASTBase::Make<ASTIfThen>(parent, neg_condition);
}
ASTZipper* sub_zipper = if_node->GetSubNodes();
sub_zipper->Init(goto_node, if_node);
zipper.InsertAfter(if_node, prev);
sub_zipper->Remove(goto_node);
}
void ASTManager::MoveOutward(ASTNode goto_node) {
ASTZipper& zipper = goto_node->GetManager();
const ASTNode parent = goto_node->GetParent();
ASTZipper& zipper2 = parent->GetManager();
const ASTNode grandpa = parent->GetParent();
const bool is_loop = parent->IsLoop();
const bool is_else = parent->IsIfElse();
const bool is_if = parent->IsIfThen();
const ASTNode prev = goto_node->GetPrevious();
const ASTNode post = goto_node->GetNext();
const Expr condition = goto_node->GetGotoCondition();
zipper.DetachSingle(goto_node);
if (is_loop) {
const u32 var_index = NewVariable();
const Expr var_condition = MakeExpr<ExprVar>(var_index);
const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
zipper2.InsertBefore(var_node_init, parent);
zipper.InsertAfter(var_node, prev);
goto_node->SetGotoCondition(var_condition);
const ASTNode break_node = ASTBase::Make<ASTBreak>(parent, var_condition);
zipper.InsertAfter(break_node, var_node);
} else if (is_if || is_else) {
const u32 var_index = NewVariable();
const Expr var_condition = MakeExpr<ExprVar>(var_index);
const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
if (is_if) {
zipper2.InsertBefore(var_node_init, parent);
} else {
zipper2.InsertBefore(var_node_init, parent->GetPrevious());
}
zipper.InsertAfter(var_node, prev);
goto_node->SetGotoCondition(var_condition);
if (post) {
zipper.DetachTail(post);
const ASTNode if_node = ASTBase::Make<ASTIfThen>(parent, MakeExprNot(var_condition));
ASTZipper* sub_zipper = if_node->GetSubNodes();
sub_zipper->Init(post, if_node);
zipper.InsertAfter(if_node, var_node);
}
} else {
UNREACHABLE();
}
const ASTNode next = parent->GetNext();
if (is_if && next && next->IsIfElse()) {
zipper2.InsertAfter(goto_node, next);
goto_node->SetParent(grandpa);
return;
}
zipper2.InsertAfter(goto_node, parent);
goto_node->SetParent(grandpa);
}
class ASTClearer {
public:
ASTClearer() = default;
void operator()(const ASTProgram& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(const ASTIfThen& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(const ASTIfElse& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()([[maybe_unused]] const ASTBlockEncoded& ast) {}
void operator()(ASTBlockDecoded& ast) {
ast.nodes.clear();
}
void operator()([[maybe_unused]] const ASTVarSet& ast) {}
void operator()([[maybe_unused]] const ASTLabel& ast) {}
void operator()([[maybe_unused]] const ASTGoto& ast) {}
void operator()(const ASTDoWhile& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()([[maybe_unused]] const ASTReturn& ast) {}
void operator()([[maybe_unused]] const ASTBreak& ast) {}
void Visit(const ASTNode& node) {
std::visit(*this, *node->GetInnerData());
node->Clear();
}
};
void ASTManager::Clear() {
if (!main_node) {
return;
}
ASTClearer clearer{};
clearer.Visit(main_node);
main_node.reset();
program = nullptr;
labels_map.clear();
labels.clear();
gotos.clear();
}
} // namespace VideoCommon::Shader
+400
View File
@@ -0,0 +1,400 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "video_core/shader/expr.h"
#include "video_core/shader/node.h"
namespace VideoCommon::Shader {
class ASTBase;
class ASTBlockDecoded;
class ASTBlockEncoded;
class ASTBreak;
class ASTDoWhile;
class ASTGoto;
class ASTIfElse;
class ASTIfThen;
class ASTLabel;
class ASTProgram;
class ASTReturn;
class ASTVarSet;
using ASTData = std::variant<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTBlockDecoded,
ASTVarSet, ASTGoto, ASTLabel, ASTDoWhile, ASTReturn, ASTBreak>;
using ASTNode = std::shared_ptr<ASTBase>;
enum class ASTZipperType : u32 {
Program,
IfThen,
IfElse,
Loop,
};
class ASTZipper final {
public:
explicit ASTZipper();
void Init(ASTNode first, ASTNode parent);
ASTNode GetFirst() const {
return first;
}
ASTNode GetLast() const {
return last;
}
void PushBack(ASTNode new_node);
void PushFront(ASTNode new_node);
void InsertAfter(ASTNode new_node, ASTNode at_node);
void InsertBefore(ASTNode new_node, ASTNode at_node);
void DetachTail(ASTNode node);
void DetachSingle(ASTNode node);
void DetachSegment(ASTNode start, ASTNode end);
void Remove(ASTNode node);
ASTNode first{};
ASTNode last{};
};
class ASTProgram {
public:
ASTZipper nodes{};
};
class ASTIfThen {
public:
explicit ASTIfThen(Expr condition) : condition{std::move(condition)} {}
Expr condition;
ASTZipper nodes{};
};
class ASTIfElse {
public:
ASTZipper nodes{};
};
class ASTBlockEncoded {
public:
explicit ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {}
u32 start;
u32 end;
};
class ASTBlockDecoded {
public:
explicit ASTBlockDecoded(NodeBlock&& new_nodes) : nodes(std::move(new_nodes)) {}
NodeBlock nodes;
};
class ASTVarSet {
public:
explicit ASTVarSet(u32 index, Expr condition) : index{index}, condition{std::move(condition)} {}
u32 index;
Expr condition;
};
class ASTLabel {
public:
explicit ASTLabel(u32 index) : index{index} {}
u32 index;
bool unused{};
};
class ASTGoto {
public:
explicit ASTGoto(Expr condition, u32 label) : condition{std::move(condition)}, label{label} {}
Expr condition;
u32 label;
};
class ASTDoWhile {
public:
explicit ASTDoWhile(Expr condition) : condition{std::move(condition)} {}
Expr condition;
ASTZipper nodes{};
};
class ASTReturn {
public:
explicit ASTReturn(Expr condition, bool kills)
: condition{std::move(condition)}, kills{kills} {}
Expr condition;
bool kills;
};
class ASTBreak {
public:
explicit ASTBreak(Expr condition) : condition{std::move(condition)} {}
Expr condition;
};
class ASTBase {
public:
explicit ASTBase(ASTNode parent, ASTData data)
: data{std::move(data)}, parent{std::move(parent)} {}
template <class U, class... Args>
static ASTNode Make(ASTNode parent, Args&&... args) {
return std::make_shared<ASTBase>(std::move(parent),
ASTData(U(std::forward<Args>(args)...)));
}
void SetParent(ASTNode new_parent) {
parent = std::move(new_parent);
}
ASTNode& GetParent() {
return parent;
}
const ASTNode& GetParent() const {
return parent;
}
u32 GetLevel() const {
u32 level = 0;
auto next_parent = parent;
while (next_parent) {
next_parent = next_parent->GetParent();
level++;
}
return level;
}
ASTData* GetInnerData() {
return &data;
}
const ASTData* GetInnerData() const {
return &data;
}
ASTNode GetNext() const {
return next;
}
ASTNode GetPrevious() const {
return previous;
}
ASTZipper& GetManager() {
return *manager;
}
const ASTZipper& GetManager() const {
return *manager;
}
std::optional<u32> GetGotoLabel() const {
auto inner = std::get_if<ASTGoto>(&data);
if (inner) {
return {inner->label};
}
return {};
}
Expr GetGotoCondition() const {
auto inner = std::get_if<ASTGoto>(&data);
if (inner) {
return inner->condition;
}
return nullptr;
}
void MarkLabelUnused() {
auto inner = std::get_if<ASTLabel>(&data);
if (inner) {
inner->unused = true;
}
}
bool IsLabelUnused() const {
auto inner = std::get_if<ASTLabel>(&data);
if (inner) {
return inner->unused;
}
return true;
}
std::optional<u32> GetLabelIndex() const {
auto inner = std::get_if<ASTLabel>(&data);
if (inner) {
return {inner->index};
}
return {};
}
Expr GetIfCondition() const {
auto inner = std::get_if<ASTIfThen>(&data);
if (inner) {
return inner->condition;
}
return nullptr;
}
void SetGotoCondition(Expr new_condition) {
auto inner = std::get_if<ASTGoto>(&data);
if (inner) {
inner->condition = std::move(new_condition);
}
}
bool IsIfThen() const {
return std::holds_alternative<ASTIfThen>(data);
}
bool IsIfElse() const {
return std::holds_alternative<ASTIfElse>(data);
}
bool IsBlockEncoded() const {
return std::holds_alternative<ASTBlockEncoded>(data);
}
void TransformBlockEncoded(NodeBlock&& nodes) {
data = ASTBlockDecoded(std::move(nodes));
}
bool IsLoop() const {
return std::holds_alternative<ASTDoWhile>(data);
}
ASTZipper* GetSubNodes() {
if (std::holds_alternative<ASTProgram>(data)) {
return &std::get_if<ASTProgram>(&data)->nodes;
}
if (std::holds_alternative<ASTIfThen>(data)) {
return &std::get_if<ASTIfThen>(&data)->nodes;
}
if (std::holds_alternative<ASTIfElse>(data)) {
return &std::get_if<ASTIfElse>(&data)->nodes;
}
if (std::holds_alternative<ASTDoWhile>(data)) {
return &std::get_if<ASTDoWhile>(&data)->nodes;
}
return nullptr;
}
void Clear() {
next.reset();
previous.reset();
parent.reset();
manager = nullptr;
}
private:
friend class ASTZipper;
ASTData data;
ASTNode parent{};
ASTNode next{};
ASTNode previous{};
ASTZipper* manager{};
};
class ASTManager final {
public:
ASTManager(bool full_decompile, bool disable_else_derivation);
~ASTManager();
ASTManager(const ASTManager& o) = delete;
ASTManager& operator=(const ASTManager& other) = delete;
ASTManager(ASTManager&& other) noexcept = default;
ASTManager& operator=(ASTManager&& other) noexcept = default;
void Init();
void DeclareLabel(u32 address);
void InsertLabel(u32 address);
void InsertGoto(Expr condition, u32 address);
void InsertBlock(u32 start_address, u32 end_address);
void InsertReturn(Expr condition, bool kills);
std::string Print();
void Decompile();
void ShowCurrentState(std::string_view state);
void SanityCheck();
void Clear();
bool IsFullyDecompiled() const {
if (full_decompile) {
return gotos.empty();
}
for (ASTNode goto_node : gotos) {
auto label_index = goto_node->GetGotoLabel();
if (!label_index) {
return false;
}
ASTNode glabel = labels[*label_index];
if (IsBackwardsJump(goto_node, glabel)) {
return false;
}
}
return true;
}
ASTNode GetProgram() const {
return main_node;
}
u32 GetVariables() const {
return variables;
}
const std::vector<ASTNode>& GetLabels() const {
return labels;
}
private:
bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const;
bool IndirectlyRelated(const ASTNode& first, const ASTNode& second) const;
bool DirectlyRelated(const ASTNode& first, const ASTNode& second) const;
void EncloseDoWhile(ASTNode goto_node, ASTNode label);
void EncloseIfThen(ASTNode goto_node, ASTNode label);
void MoveOutward(ASTNode goto_node);
u32 NewVariable() {
return variables++;
}
bool full_decompile{};
bool disable_else_derivation{};
std::unordered_map<u32, u32> labels_map{};
u32 labels_count{};
std::vector<ASTNode> labels{};
std::list<ASTNode> gotos{};
u32 variables{};
ASTProgram* program{};
ASTNode main_node{};
Expr false_condition{};
};
} // namespace VideoCommon::Shader
@@ -0,0 +1,26 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/shader/compiler_settings.h"
namespace VideoCommon::Shader {
std::string CompileDepthAsString(const CompileDepth cd) {
switch (cd) {
case CompileDepth::BruteForce:
return "Brute Force Compile";
case CompileDepth::FlowStack:
return "Simple Flow Stack Mode";
case CompileDepth::NoFlowStack:
return "Remove Flow Stack";
case CompileDepth::DecompileBackwards:
return "Decompile Backward Jumps";
case CompileDepth::FullDecompile:
return "Full Decompilation";
default:
return "Unknown Compiler Process";
}
}
} // namespace VideoCommon::Shader
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "video_core/engines/shader_bytecode.h"
namespace VideoCommon::Shader {
enum class CompileDepth : u32 {
BruteForce = 0,
FlowStack = 1,
NoFlowStack = 2,
DecompileBackwards = 3,
FullDecompile = 4,
};
std::string CompileDepthAsString(CompileDepth cd);
struct CompilerSettings {
CompileDepth depth{CompileDepth::NoFlowStack};
bool disable_else_derivation{true};
};
} // namespace VideoCommon::Shader
+126 -29
View File
@@ -4,13 +4,14 @@
#include <list>
#include <map>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/control_flow.h"
#include "video_core/shader/shader_ir.h"
@@ -64,12 +65,13 @@ struct CFGRebuildState {
std::list<u32> inspect_queries{};
std::list<Query> queries{};
std::unordered_map<u32, u32> registered{};
std::unordered_set<u32> labels{};
std::set<u32> labels{};
std::map<u32, u32> ssy_labels{};
std::map<u32, u32> pbk_labels{};
std::unordered_map<u32, BlockStack> stacks{};
const ProgramCode& program_code;
const std::size_t program_size;
ASTManager* manager;
};
enum class BlockCollision : u32 { None, Found, Inside };
@@ -415,38 +417,133 @@ bool TryQuery(CFGRebuildState& state) {
}
} // Anonymous namespace
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
std::size_t program_size, u32 start_address) {
CFGRebuildState state{program_code, program_size, start_address};
void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) {
const auto get_expr = ([&](const Condition& cond) -> Expr {
Expr result{};
if (cond.cc != ConditionCode::T) {
result = MakeExpr<ExprCondCode>(cond.cc);
}
if (cond.predicate != Pred::UnusedIndex) {
u32 pred = static_cast<u32>(cond.predicate);
bool negate = false;
if (pred > 7) {
negate = true;
pred -= 8;
}
Expr extra = MakeExpr<ExprPredicate>(pred);
if (negate) {
extra = MakeExpr<ExprNot>(extra);
}
if (result) {
return MakeExpr<ExprAnd>(extra, result);
}
return extra;
}
if (result) {
return result;
}
return MakeExpr<ExprBoolean>(true);
});
if (branch.address < 0) {
if (branch.kill) {
mm.InsertReturn(get_expr(branch.condition), true);
return;
}
mm.InsertReturn(get_expr(branch.condition), false);
return;
}
mm.InsertGoto(get_expr(branch.condition), branch.address);
}
void DecompileShader(CFGRebuildState& state) {
state.manager->Init();
for (auto label : state.labels) {
state.manager->DeclareLabel(label);
}
for (auto& block : state.block_info) {
if (state.labels.count(block.start) != 0) {
state.manager->InsertLabel(block.start);
}
u32 end = block.branch.ignore ? block.end + 1 : block.end;
state.manager->InsertBlock(block.start, end);
if (!block.branch.ignore) {
InsertBranch(*state.manager, block.branch);
}
}
state.manager->Decompile();
}
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size,
u32 start_address,
const CompilerSettings& settings) {
auto result_out = std::make_unique<ShaderCharacteristics>();
if (settings.depth == CompileDepth::BruteForce) {
result_out->settings.depth = CompileDepth::BruteForce;
return result_out;
}
CFGRebuildState state{program_code, program_size, start_address};
// Inspect Code and generate blocks
state.labels.clear();
state.labels.emplace(start_address);
state.inspect_queries.push_back(state.start);
while (!state.inspect_queries.empty()) {
if (!TryInspectAddress(state)) {
return {};
result_out->settings.depth = CompileDepth::BruteForce;
return result_out;
}
}
// Decompile Stacks
state.queries.push_back(Query{state.start, {}, {}});
bool decompiled = true;
while (!state.queries.empty()) {
if (!TryQuery(state)) {
decompiled = false;
break;
bool use_flow_stack = true;
bool decompiled = false;
if (settings.depth != CompileDepth::FlowStack) {
// Decompile Stacks
state.queries.push_back(Query{state.start, {}, {}});
decompiled = true;
while (!state.queries.empty()) {
if (!TryQuery(state)) {
decompiled = false;
break;
}
}
}
use_flow_stack = !decompiled;
// Sort and organize results
std::sort(state.block_info.begin(), state.block_info.end(),
[](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; });
ShaderCharacteristics result_out{};
result_out.decompilable = decompiled;
result_out.start = start_address;
result_out.end = start_address;
for (const auto& block : state.block_info) {
[](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; });
if (decompiled && settings.depth != CompileDepth::NoFlowStack) {
ASTManager manager{settings.depth != CompileDepth::DecompileBackwards,
settings.disable_else_derivation};
state.manager = &manager;
DecompileShader(state);
decompiled = state.manager->IsFullyDecompiled();
if (!decompiled) {
if (settings.depth == CompileDepth::FullDecompile) {
LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:");
} else {
LOG_CRITICAL(HW_GPU, "Failed to remove all backward gotos!:");
}
state.manager->ShowCurrentState("Of Shader");
state.manager->Clear();
} else {
auto characteristics = std::make_unique<ShaderCharacteristics>();
characteristics->start = start_address;
characteristics->settings.depth = settings.depth;
characteristics->manager = std::move(manager);
characteristics->end = state.block_info.back().end + 1;
return characteristics;
}
}
result_out->start = start_address;
result_out->settings.depth =
use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack;
result_out->blocks.clear();
for (auto& block : state.block_info) {
ShaderBlock new_block{};
new_block.start = block.start;
new_block.end = block.end;
@@ -456,26 +553,26 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
new_block.branch.kills = block.branch.kill;
new_block.branch.address = block.branch.address;
}
result_out.end = std::max(result_out.end, block.end);
result_out.blocks.push_back(new_block);
result_out->end = std::max(result_out->end, block.end);
result_out->blocks.push_back(new_block);
}
if (result_out.decompilable) {
result_out.labels = std::move(state.labels);
return {std::move(result_out)};
if (!use_flow_stack) {
result_out->labels = std::move(state.labels);
return result_out;
}
// If it's not decompilable, merge the unlabelled blocks together
auto back = result_out.blocks.begin();
auto back = result_out->blocks.begin();
auto next = std::next(back);
while (next != result_out.blocks.end()) {
while (next != result_out->blocks.end()) {
if (state.labels.count(next->start) == 0 && next->start == back->end + 1) {
back->end = next->end;
next = result_out.blocks.erase(next);
next = result_out->blocks.erase(next);
continue;
}
back = next;
++next;
}
return {std::move(result_out)};
return result_out;
}
} // namespace VideoCommon::Shader
+9 -5
View File
@@ -6,9 +6,11 @@
#include <list>
#include <optional>
#include <unordered_set>
#include <set>
#include "video_core/engines/shader_bytecode.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/compiler_settings.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon::Shader {
@@ -67,13 +69,15 @@ struct ShaderBlock {
struct ShaderCharacteristics {
std::list<ShaderBlock> blocks{};
bool decompilable{};
std::set<u32> labels{};
u32 start{};
u32 end{};
std::unordered_set<u32> labels{};
ASTManager manager{true, true};
CompilerSettings settings{};
};
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
std::size_t program_size, u32 start_address);
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size,
u32 start_address,
const CompilerSettings& settings);
} // namespace VideoCommon::Shader
+125 -45
View File
@@ -35,58 +35,138 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
} // namespace
class ASTDecoder {
public:
ASTDecoder(ShaderIR& ir) : ir(ir) {}
void operator()(ASTProgram& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(ASTIfThen& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(ASTIfElse& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(ASTBlockEncoded& ast) {}
void operator()(ASTBlockDecoded& ast) {}
void operator()(ASTVarSet& ast) {}
void operator()(ASTLabel& ast) {}
void operator()(ASTGoto& ast) {}
void operator()(ASTDoWhile& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(ASTReturn& ast) {}
void operator()(ASTBreak& ast) {}
void Visit(ASTNode& node) {
std::visit(*this, *node->GetInnerData());
if (node->IsBlockEncoded()) {
auto block = std::get_if<ASTBlockEncoded>(node->GetInnerData());
NodeBlock bb = ir.DecodeRange(block->start, block->end);
node->TransformBlockEncoded(std::move(bb));
}
}
private:
ShaderIR& ir;
};
void ShaderIR::Decode() {
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
disable_flow_stack = false;
const auto info = ScanFlow(program_code, program_size, main_offset);
if (info) {
const auto& shader_info = *info;
coverage_begin = shader_info.start;
coverage_end = shader_info.end;
if (shader_info.decompilable) {
disable_flow_stack = true;
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
if (label == static_cast<u32>(exit_branch)) {
return;
}
basic_blocks.insert({label, nodes});
};
const auto& blocks = shader_info.blocks;
NodeBlock current_block;
u32 current_label = static_cast<u32>(exit_branch);
for (auto& block : blocks) {
if (shader_info.labels.count(block.start) != 0) {
insert_block(current_block, current_label);
current_block.clear();
current_label = block.start;
}
if (!block.ignore_branch) {
DecodeRangeInner(current_block, block.start, block.end);
InsertControlFlow(current_block, block);
} else {
DecodeRangeInner(current_block, block.start, block.end + 1);
}
}
insert_block(current_block, current_label);
return;
}
LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method");
// we can't decompile it, fallback to standard method
decompiled = false;
auto info = ScanFlow(program_code, program_size, main_offset, settings);
auto& shader_info = *info;
coverage_begin = shader_info.start;
coverage_end = shader_info.end;
switch (shader_info.settings.depth) {
case CompileDepth::FlowStack: {
for (const auto& block : shader_info.blocks) {
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
}
return;
break;
}
LOG_WARNING(HW_GPU, "Flow Analysis Failed! Falling back to brute force compiling");
// Now we need to deal with an undecompilable shader. We need to brute force
// a shader that captures every position.
coverage_begin = main_offset;
const u32 shader_end = static_cast<u32>(program_size / sizeof(u64));
coverage_end = shader_end;
for (u32 label = main_offset; label < shader_end; label++) {
basic_blocks.insert({label, DecodeRange(label, label + 1)});
case CompileDepth::NoFlowStack: {
disable_flow_stack = true;
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
if (label == static_cast<u32>(exit_branch)) {
return;
}
basic_blocks.insert({label, nodes});
};
const auto& blocks = shader_info.blocks;
NodeBlock current_block;
u32 current_label = static_cast<u32>(exit_branch);
for (auto& block : blocks) {
if (shader_info.labels.count(block.start) != 0) {
insert_block(current_block, current_label);
current_block.clear();
current_label = block.start;
}
if (!block.ignore_branch) {
DecodeRangeInner(current_block, block.start, block.end);
InsertControlFlow(current_block, block);
} else {
DecodeRangeInner(current_block, block.start, block.end + 1);
}
}
insert_block(current_block, current_label);
break;
}
case CompileDepth::DecompileBackwards:
case CompileDepth::FullDecompile: {
program_manager = std::move(shader_info.manager);
disable_flow_stack = true;
decompiled = true;
ASTDecoder decoder{*this};
ASTNode program = GetASTProgram();
decoder.Visit(program);
break;
}
default:
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
[[fallthrough]];
case CompileDepth::BruteForce: {
coverage_begin = main_offset;
const u32 shader_end = static_cast<u32>(program_size / sizeof(u64));
coverage_end = shader_end;
for (u32 label = main_offset; label < shader_end; label++) {
basic_blocks.insert({label, DecodeRange(label, label + 1)});
}
break;
}
}
if (settings.depth != shader_info.settings.depth) {
LOG_WARNING(
HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth));
}
}
+93
View File
@@ -0,0 +1,93 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <variant>
#include "video_core/shader/expr.h"
namespace VideoCommon::Shader {
namespace {
bool ExprIsBoolean(const Expr& expr) {
return std::holds_alternative<ExprBoolean>(*expr);
}
bool ExprBooleanGet(const Expr& expr) {
return std::get_if<ExprBoolean>(expr.get())->value;
}
} // Anonymous namespace
bool ExprAnd::operator==(const ExprAnd& b) const {
return (*operand1 == *b.operand1) && (*operand2 == *b.operand2);
}
bool ExprAnd::operator!=(const ExprAnd& b) const {
return !operator==(b);
}
bool ExprOr::operator==(const ExprOr& b) const {
return (*operand1 == *b.operand1) && (*operand2 == *b.operand2);
}
bool ExprOr::operator!=(const ExprOr& b) const {
return !operator==(b);
}
bool ExprNot::operator==(const ExprNot& b) const {
return *operand1 == *b.operand1;
}
bool ExprNot::operator!=(const ExprNot& b) const {
return !operator==(b);
}
Expr MakeExprNot(Expr first) {
if (std::holds_alternative<ExprNot>(*first)) {
return std::get_if<ExprNot>(first.get())->operand1;
}
return MakeExpr<ExprNot>(std::move(first));
}
Expr MakeExprAnd(Expr first, Expr second) {
if (ExprIsBoolean(first)) {
return ExprBooleanGet(first) ? second : first;
}
if (ExprIsBoolean(second)) {
return ExprBooleanGet(second) ? first : second;
}
return MakeExpr<ExprAnd>(std::move(first), std::move(second));
}
Expr MakeExprOr(Expr first, Expr second) {
if (ExprIsBoolean(first)) {
return ExprBooleanGet(first) ? first : second;
}
if (ExprIsBoolean(second)) {
return ExprBooleanGet(second) ? second : first;
}
return MakeExpr<ExprOr>(std::move(first), std::move(second));
}
bool ExprAreEqual(const Expr& first, const Expr& second) {
return (*first) == (*second);
}
bool ExprAreOpposite(const Expr& first, const Expr& second) {
if (std::holds_alternative<ExprNot>(*first)) {
return ExprAreEqual(std::get_if<ExprNot>(first.get())->operand1, second);
}
if (std::holds_alternative<ExprNot>(*second)) {
return ExprAreEqual(std::get_if<ExprNot>(second.get())->operand1, first);
}
return false;
}
bool ExprIsTrue(const Expr& first) {
if (ExprIsBoolean(first)) {
return ExprBooleanGet(first);
}
return false;
}
} // namespace VideoCommon::Shader
+139
View File
@@ -0,0 +1,139 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <variant>
#include "video_core/engines/shader_bytecode.h"
namespace VideoCommon::Shader {
using Tegra::Shader::ConditionCode;
using Tegra::Shader::Pred;
class ExprAnd;
class ExprBoolean;
class ExprCondCode;
class ExprNot;
class ExprOr;
class ExprPredicate;
class ExprVar;
using ExprData =
std::variant<ExprVar, ExprCondCode, ExprPredicate, ExprNot, ExprOr, ExprAnd, ExprBoolean>;
using Expr = std::shared_ptr<ExprData>;
class ExprAnd final {
public:
explicit ExprAnd(Expr a, Expr b) : operand1{std::move(a)}, operand2{std::move(b)} {}
bool operator==(const ExprAnd& b) const;
bool operator!=(const ExprAnd& b) const;
Expr operand1;
Expr operand2;
};
class ExprOr final {
public:
explicit ExprOr(Expr a, Expr b) : operand1{std::move(a)}, operand2{std::move(b)} {}
bool operator==(const ExprOr& b) const;
bool operator!=(const ExprOr& b) const;
Expr operand1;
Expr operand2;
};
class ExprNot final {
public:
explicit ExprNot(Expr a) : operand1{std::move(a)} {}
bool operator==(const ExprNot& b) const;
bool operator!=(const ExprNot& b) const;
Expr operand1;
};
class ExprVar final {
public:
explicit ExprVar(u32 index) : var_index{index} {}
bool operator==(const ExprVar& b) const {
return var_index == b.var_index;
}
bool operator!=(const ExprVar& b) const {
return !operator==(b);
}
u32 var_index;
};
class ExprPredicate final {
public:
explicit ExprPredicate(u32 predicate) : predicate{predicate} {}
bool operator==(const ExprPredicate& b) const {
return predicate == b.predicate;
}
bool operator!=(const ExprPredicate& b) const {
return !operator==(b);
}
u32 predicate;
};
class ExprCondCode final {
public:
explicit ExprCondCode(ConditionCode cc) : cc{cc} {}
bool operator==(const ExprCondCode& b) const {
return cc == b.cc;
}
bool operator!=(const ExprCondCode& b) const {
return !operator==(b);
}
ConditionCode cc;
};
class ExprBoolean final {
public:
explicit ExprBoolean(bool val) : value{val} {}
bool operator==(const ExprBoolean& b) const {
return value == b.value;
}
bool operator!=(const ExprBoolean& b) const {
return !operator==(b);
}
bool value;
};
template <typename T, typename... Args>
Expr MakeExpr(Args&&... args) {
static_assert(std::is_convertible_v<T, ExprData>);
return std::make_shared<ExprData>(T(std::forward<Args>(args)...));
}
bool ExprAreEqual(const Expr& first, const Expr& second);
bool ExprAreOpposite(const Expr& first, const Expr& second);
Expr MakeExprNot(Expr first);
Expr MakeExprAnd(Expr first, Expr second);
Expr MakeExprOr(Expr first, Expr second);
bool ExprIsTrue(const Expr& first);
} // namespace VideoCommon::Shader
+7 -5
View File
@@ -22,8 +22,10 @@ using Tegra::Shader::PredCondition;
using Tegra::Shader::PredOperation;
using Tegra::Shader::Register;
ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size)
: program_code{program_code}, main_offset{main_offset}, program_size{size} {
ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size,
CompilerSettings settings)
: program_code{program_code}, main_offset{main_offset}, program_size{size}, basic_blocks{},
program_manager{true, true}, settings{settings} {
Decode();
}
@@ -137,7 +139,7 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff
return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer));
}
Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) {
Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const {
const Node node = MakeNode<InternalFlagNode>(flag);
if (negated) {
return Operation(OperationCode::LogicalNegate, node);
@@ -367,13 +369,13 @@ OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) {
return op->second;
}
Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) {
Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) const {
switch (cc) {
case Tegra::Shader::ConditionCode::NEU:
return GetInternalFlag(InternalFlag::Zero, true);
default:
UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc));
return GetPredicate(static_cast<u64>(Pred::NeverExecute));
return MakeNode<PredicateNode>(Pred::NeverExecute, false);
}
}
+28 -5
View File
@@ -15,6 +15,8 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/engines/shader_header.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/compiler_settings.h"
#include "video_core/shader/node.h"
namespace VideoCommon::Shader {
@@ -64,7 +66,8 @@ struct GlobalMemoryUsage {
class ShaderIR final {
public:
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size);
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size,
CompilerSettings settings);
~ShaderIR();
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
@@ -144,11 +147,31 @@ public:
return disable_flow_stack;
}
bool IsDecompiled() const {
return decompiled;
}
const ASTManager& GetASTManager() const {
return program_manager;
}
ASTNode GetASTProgram() const {
return program_manager.GetProgram();
}
u32 GetASTNumVariables() const {
return program_manager.GetVariables();
}
u32 ConvertAddressToNvidiaSpace(const u32 address) const {
return (address - main_offset) * sizeof(Tegra::Shader::Instruction);
}
/// Returns a condition code evaluated from internal flags
Node GetConditionCode(Tegra::Shader::ConditionCode cc) const;
private:
friend class ASTDecoder;
void Decode();
NodeBlock DecodeRange(u32 begin, u32 end);
@@ -213,7 +236,7 @@ private:
/// Generates a node representing an output attribute. Keeps track of used attributes.
Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
/// Generates a node representing an internal flag
Node GetInternalFlag(InternalFlag flag, bool negated = false);
Node GetInternalFlag(InternalFlag flag, bool negated = false) const;
/// Generates a node representing a local memory address
Node GetLocalMemory(Node address);
/// Generates a node representing a shared memory address
@@ -271,9 +294,6 @@ private:
/// Returns a predicate combiner operation
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
/// Returns a condition code evaluated from internal flags
Node GetConditionCode(Tegra::Shader::ConditionCode cc);
/// Accesses a texture sampler
const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler,
Tegra::Shader::TextureType type, bool is_array, bool is_shadow);
@@ -357,6 +377,7 @@ private:
const ProgramCode& program_code;
const u32 main_offset;
const std::size_t program_size;
bool decompiled{};
bool disable_flow_stack{};
u32 coverage_begin{};
@@ -364,6 +385,8 @@ private:
std::map<u32, NodeBlock> basic_blocks;
NodeBlock global_code;
ASTManager program_manager;
CompilerSettings settings{};
std::set<u32> used_registers;
std::set<Tegra::Shader::Pred> used_predicates;
+144 -2
View File
@@ -224,8 +224,13 @@ public:
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config,
const Tegra::Engines::Fermi2D::Config& copy_config) {
std::lock_guard lock{mutex};
std::pair<TSurface, TView> dst_surface = GetFermiSurface(dst_config);
std::pair<TSurface, TView> src_surface = GetFermiSurface(src_config);
SurfaceParams src_params = SurfaceParams::CreateForFermiCopySurface(src_config);
SurfaceParams dst_params = SurfaceParams::CreateForFermiCopySurface(dst_config);
const GPUVAddr src_gpu_addr = src_config.Address();
const GPUVAddr dst_gpu_addr = dst_config.Address();
DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr);
std::pair<TSurface, TView> dst_surface = GetSurface(dst_gpu_addr, dst_params, true, false);
std::pair<TSurface, TView> src_surface = GetSurface(src_gpu_addr, src_params, true, false);
ImageBlit(src_surface.second, dst_surface.second, copy_config);
dst_surface.first->MarkAsModified(true, Tick());
}
@@ -357,6 +362,29 @@ private:
BufferCopy = 3,
};
enum class DeductionType : u32 {
DeductionComplete,
DeductionIncomplete,
DeductionFailed,
};
struct Deduction {
DeductionType type{DeductionType::DeductionFailed};
TSurface surface{};
bool Failed() const {
return type == DeductionType::DeductionFailed;
}
bool Incomplete() const {
return type == DeductionType::DeductionIncomplete;
}
bool IsDepth() const {
return surface->GetSurfaceParams().IsPixelFormatZeta();
}
};
/**
* `PickStrategy` takes care of selecting a proper strategy to deal with a texture recycle.
* @param overlaps, the overlapping surfaces registered in the cache.
@@ -691,6 +719,120 @@ private:
MatchTopologyResult::FullMatch);
}
/**
* `DeduceSurface` gets the starting address and parameters of a candidate surface and tries
* to find a matching surface within the cache that's similar to it. If there are many textures
* or the texture found if entirely incompatible, it will fail. If no texture is found, the
* blit will be unsuccessful.
* @param gpu_addr, the starting address of the candidate surface.
* @param params, the paremeters on the candidate surface.
**/
Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) {
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
const auto cache_addr{ToCacheAddr(host_ptr)};
if (!cache_addr) {
Deduction result{};
result.type = DeductionType::DeductionFailed;
return result;
}
if (const auto iter = l1_cache.find(cache_addr); iter != l1_cache.end()) {
TSurface& current_surface = iter->second;
const auto topological_result = current_surface->MatchesTopology(params);
if (topological_result != MatchTopologyResult::FullMatch) {
Deduction result{};
result.type = DeductionType::DeductionFailed;
return result;
}
const auto struct_result = current_surface->MatchesStructure(params);
if (struct_result != MatchStructureResult::None &&
current_surface->MatchTarget(params.target)) {
Deduction result{};
result.type = DeductionType::DeductionComplete;
result.surface = current_surface;
return result;
}
}
const std::size_t candidate_size = params.GetGuestSizeInBytes();
auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
if (overlaps.empty()) {
Deduction result{};
result.type = DeductionType::DeductionIncomplete;
return result;
}
if (overlaps.size() > 1) {
Deduction result{};
result.type = DeductionType::DeductionFailed;
return result;
} else {
Deduction result{};
result.type = DeductionType::DeductionComplete;
result.surface = overlaps[0];
return result;
}
}
/**
* `DeduceBestBlit` gets the a source and destination starting address and parameters,
* and tries to deduce if they are supposed to be depth textures. If so, their
* parameters are modified and fixed into so.
* @param gpu_addr, the starting address of the candidate surface.
* @param params, the parameters on the candidate surface.
**/
void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params,
const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) {
auto deduced_src = DeduceSurface(src_gpu_addr, src_params);
auto deduced_dst = DeduceSurface(src_gpu_addr, src_params);
if (deduced_src.Failed() || deduced_dst.Failed()) {
return;
}
const bool incomplete_src = deduced_src.Incomplete();
const bool incomplete_dst = deduced_dst.Incomplete();
if (incomplete_src && incomplete_dst) {
return;
}
const bool any_incomplete = incomplete_src || incomplete_dst;
if (!any_incomplete) {
if (!(deduced_src.IsDepth() && deduced_dst.IsDepth())) {
return;
}
} else {
if (incomplete_src && !(deduced_dst.IsDepth())) {
return;
}
if (incomplete_dst && !(deduced_src.IsDepth())) {
return;
}
}
const auto inherit_format = ([](SurfaceParams& to, TSurface from) {
const SurfaceParams& params = from->GetSurfaceParams();
to.pixel_format = params.pixel_format;
to.component_type = params.component_type;
to.type = params.type;
});
// Now we got the cases where one or both is Depth and the other is not known
if (!incomplete_src) {
inherit_format(src_params, deduced_src.surface);
} else {
inherit_format(src_params, deduced_dst.surface);
}
if (!incomplete_dst) {
inherit_format(dst_params, deduced_dst.surface);
} else {
inherit_format(dst_params, deduced_src.surface);
}
}
std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params,
bool preserve_contents) {
auto new_surface{GetUncachedSurface(gpu_addr, params)};
@@ -73,6 +73,8 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
const auto res = Service::BCAT::Boxcat::GetStatus(global, map);
switch (res) {
case Service::BCAT::Boxcat::StatusResult::Success:
break;
case Service::BCAT::Boxcat::StatusResult::Offline:
return {QString{},
tr("The boxcat service is offline or you are not connected to the internet.")};
+6 -2
View File
@@ -172,9 +172,7 @@ void GameList::onTextChanged(const QString& new_text) {
const int folder_count = tree_view->model()->rowCount();
QString edit_filter_text = new_text.toLower();
QStandardItem* folder;
QStandardItem* child;
int children_total = 0;
QModelIndex root_index = item_model->invisibleRootItem()->index();
// If the searchfield is empty every item is visible
// Otherwise the filter gets applied
@@ -272,6 +270,8 @@ void GameList::onUpdateThemedIcons() {
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
break;
default:
break;
}
}
}
@@ -392,6 +392,8 @@ void GameList::ValidateEntry(const QModelIndex& item) {
case GameListItemType::AddDir:
emit AddDirectory();
break;
default:
break;
}
}
@@ -462,6 +464,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
case GameListItemType::SysNandDir:
AddPermDirPopup(context_menu, selected);
break;
default:
break;
}
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
}
+6 -3
View File
@@ -247,7 +247,7 @@ public:
Qt::DecorationRole);
setData(QObject::tr("System Titles"), Qt::DisplayRole);
break;
case GameListItemType::CustomDir:
case GameListItemType::CustomDir: {
const QString icon_name = QFileInfo::exists(game_dir->path)
? QStringLiteral("folder")
: QStringLiteral("bad_folder");
@@ -256,8 +256,11 @@ public:
Qt::DecorationRole);
setData(game_dir->path, Qt::DisplayRole);
break;
};
};
}
default:
break;
}
}
int type() const override {
return static_cast<int>(dir_type);
+6 -6
View File
@@ -326,10 +326,10 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
}
} else {
std::vector<u8> icon;
const auto res1 = loader->ReadIcon(icon);
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
std::string name = " ";
const auto res3 = loader->ReadTitle(name);
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
const FileSys::PatchManager patch{program_id};
@@ -354,20 +354,20 @@ void GameListWorker::run() {
for (UISettings::GameDir& game_dir : game_dirs) {
if (game_dir.path == QStringLiteral("SDMC")) {
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
emit DirEntryReady({game_list_dir});
emit DirEntryReady(game_list_dir);
AddTitlesToGameList(game_list_dir);
} else if (game_dir.path == QStringLiteral("UserNAND")) {
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
emit DirEntryReady({game_list_dir});
emit DirEntryReady(game_list_dir);
AddTitlesToGameList(game_list_dir);
} else if (game_dir.path == QStringLiteral("SysNAND")) {
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
emit DirEntryReady({game_list_dir});
emit DirEntryReady(game_list_dir);
AddTitlesToGameList(game_list_dir);
} else {
watch_list.append(game_dir.path);
auto* const game_list_dir = new GameListDir(game_dir);
emit DirEntryReady({game_list_dir});
emit DirEntryReady(game_list_dir);
provider->ClearAllEntries();
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 2,
game_list_dir);
+3 -2
View File
@@ -75,8 +75,9 @@ private:
std::shared_ptr<FileSys::VfsFilesystem> vfs;
FileSys::ManualContentProvider* provider;
QStringList watch_list;
const CompatibilityList& compatibility_list;
QVector<UISettings::GameDir>& game_dirs;
const CompatibilityList& compatibility_list;
QStringList watch_list;
std::atomic_bool stop_processing;
};
+15 -6
View File
@@ -1889,15 +1889,24 @@ void GMainWindow::OnCaptureScreenshot() {
}
void GMainWindow::UpdateWindowTitle(const QString& title_name) {
const QString full_name = QString::fromUtf8(Common::g_build_fullname);
const QString branch_name = QString::fromUtf8(Common::g_scm_branch);
const QString description = QString::fromUtf8(Common::g_scm_desc);
const auto full_name = std::string(Common::g_build_fullname);
const auto branch_name = std::string(Common::g_scm_branch);
const auto description = std::string(Common::g_scm_desc);
const auto build_id = std::string(Common::g_build_id);
const auto date =
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString();
if (title_name.isEmpty()) {
setWindowTitle(QStringLiteral("yuzu %1| %2-%3").arg(full_name, branch_name, description));
const auto fmt = std::string(Common::g_title_bar_format_idle);
setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt,
full_name, branch_name, description,
std::string{}, date, build_id)));
} else {
setWindowTitle(QStringLiteral("yuzu %1| %4 | %2-%3")
.arg(full_name, branch_name, description, title_name));
const auto fmt = std::string(Common::g_title_bar_format_running);
setWindowTitle(QString::fromStdString(
fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name,
description, title_name.toStdString(), date, build_id)));
}
}
+2
View File
@@ -9,6 +9,8 @@ namespace UISettings {
const Themes themes{{
{"Default", "default"},
{"Dark", "qdarkstyle"},
{"Colorful", "colorful"},
{"Colorful Dark", "colorful_dark"},
}};
Values values = {};
+1 -1
View File
@@ -24,7 +24,7 @@ struct Shortcut {
ContextualShortcut shortcut;
};
using Themes = std::array<std::pair<const char*, const char*>, 2>;
using Themes = std::array<std::pair<const char*, const char*>, 4>;
extern const Themes themes;
struct GameDir {
@@ -4,6 +4,9 @@
#include <SDL.h>
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/core.h"
#include "core/perf_stats.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "input_common/motion_emu.h"
@@ -170,6 +173,16 @@ void EmuWindow_SDL2::PollEvents() {
break;
}
}
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
const auto title = fmt::format(
"yuzu {} | {}-{} | FPS: {:.0f} ({:.0%})", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc, results.game_fps, results.emulation_speed);
SDL_SetWindowTitle(render_window, title.c_str());
last_time = current_time;
}
}
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) {
@@ -60,4 +60,7 @@ protected:
/// Internal SDL2 render window
SDL_Window* render_window;
/// Keeps track of how often to update the title bar during gameplay
u32 last_time = 0;
};
+2 -2
View File
@@ -186,8 +186,6 @@ int main(int argc, char** argv) {
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
SCOPE_EXIT({ system.Shutdown(); });
const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
switch (load_result) {
@@ -227,6 +225,8 @@ int main(int argc, char** argv) {
system.RunLoop();
}
system.Shutdown();
detached_tasks.WaitForAllTasks();
return 0;
}
-1
View File
@@ -93,7 +93,6 @@ void Config::ReadValues() {
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
const auto size = sdl2_config->GetInteger("System", "users_size", 0);
Settings::values.current_user = std::clamp<int>(
sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);