Browse Source

Merge remote-tracking branch 'origin/fw21-stubs' into fw21-stubs

pull/3004/head
Maufeat 3 months ago
parent
commit
28986dd0fc
  1. 2
      .ci/license-header.sh
  2. 19
      .github/workflows/sources.yml
  3. 61
      .github/workflows/translations.yml
  4. 2
      .gitignore
  5. 34
      CMakeLists.txt
  6. 58
      CMakeModules/MinGWClangCross.cmake
  7. 57
      CMakeModules/MinGWCross.cmake
  8. BIN
      dist/eden.ico
  9. 9
      dist/icon_variations/README.md
  10. 80
      dist/icon_variations/base_small.svg
  11. BIN
      dist/yuzu.bmp
  12. 46
      docs/Build.md
  13. 22
      docs/Deps.md
  14. 7
      externals/CMakeLists.txt
  15. 28
      externals/ffmpeg/CMakeLists.txt
  16. 4
      externals/nx_tzdb/cpmfile.json
  17. 6
      src/CMakeLists.txt
  18. 37
      src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
  19. 4
      src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
  20. 1
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
  21. 12
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
  22. 20
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
  23. 8
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
  24. 57
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
  25. 4
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
  26. 9
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt
  27. 6
      src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
  28. 1
      src/android/app/src/main/jni/android_settings.h
  29. 55
      src/android/app/src/main/res/values/arrays.xml
  30. 29
      src/android/app/src/main/res/values/strings.xml
  31. 47
      src/common/atomic_ops.h
  32. 16
      src/common/logging/formatter.h
  33. 3
      src/common/settings.cpp
  34. 3
      src/common/settings.h
  35. 279
      src/common/swap.h
  36. 7
      src/core/arm/dynarmic/arm_dynarmic_32.cpp
  37. 7
      src/core/arm/dynarmic/arm_dynarmic_64.cpp
  38. 13
      src/core/core.cpp
  39. 14
      src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
  40. 6
      src/core/hle/service/nvdrv/devices/nvhost_gpu.h
  41. 5
      src/dynarmic/CMakeLists.txt
  42. 7
      src/dynarmic/src/dynarmic/backend/x64/a64_interface.cpp
  43. 2
      src/shader_recompiler/CMakeLists.txt
  44. 459
      src/shader_recompiler/frontend/ir/attribute.cpp
  45. 464
      src/shader_recompiler/frontend/ir/attribute.h
  46. 82
      src/shader_recompiler/frontend/ir/flow_test.cpp
  47. 82
      src/shader_recompiler/frontend/ir/flow_test.h
  48. 28
      src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp
  49. 8
      src/video_core/renderer_opengl/gl_rasterizer.cpp
  50. 14
      src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
  51. 2
      src/video_core/renderer_vulkan/vk_graphics_pipeline.h
  52. 7
      src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
  53. 145
      src/video_core/renderer_vulkan/vk_rasterizer.cpp
  54. 7
      src/video_core/vulkan_common/vulkan_device.cpp
  55. 14
      tools/translations/lupdate.sh
  56. 9
      tools/translations/qt-source.sh
  57. 19
      tools/update-icons.sh
  58. 19
      tools/windows/install-vulkan-sdk.ps1
  59. 36
      tools/windows/install-vulkan-sdk.sh
  60. 42
      tools/windows/load-msvc-env.ps1
  61. 24
      tools/windows/load-msvc-env.sh
  62. 433
      tools/windows/vcvarsall.sh

2
.ci/license-header.sh

@ -4,7 +4,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# specify full path if dupes may exist
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
# license header constants, please change when needed :))))
YEAR=2025

19
.github/workflows/sources.yml

@ -0,0 +1,19 @@
name: tx-src
on:
push:
branches: [ master ]
jobs:
sources:
runs-on: source
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Push New Sources
run: |
export PATH=/usr/lib/qt6/bin:$PATH
./tools/translations/qt-source.sh
tx-cli push -s

61
.github/workflows/translations.yml

@ -0,0 +1,61 @@
name: tx-pull
on:
# monday, wednesday, saturday at 2pm
schedule:
cron:
- '0 14 * * 1,3,6'
jobs:
tx-update:
runs-on: source
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get New Translations
run: tx-cli pull -t -f
- name: Push branch
run: |
git config --local user.name "Eden CI"
git config --local user.email "ci@eden-emu.dev"
git config --local user.signingkey "D57652791BB25D2A"
git config --local push.autoSetupRemote true
git remote set-url origin ci:eden-emu/eden.git
TIMESTAMP=$(date +"%s")
echo "TIMESTAMP=$TIMESTAMP" >> "$GITHUB_ENV"
git switch -c update-translations-$TIMESTAMP
git add dist src/android/app/src/main/res
git commit -sS -m "[dist, android] Update translations from Transifex"
git push
- name: Create PR
run: |
DATE=$(date +"%b %d")
TITLE="[dist, android] Update translations from Transifex for $DATE"
BODY="Automatic translation update for $DATE"
BASE=master
HEAD=update-translations-$TIMESTAMP
cat << EOF > data.json
{
"base": "$BASE",
"body": "$BODY",
"head": "$HEAD",
"title": "$TITLE"
}
EOF
curl -X 'POST' \
'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \
-H 'accept: application/json' \
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
-H 'Content-Type: application/json' \
-d "@data.json" --fail

2
.gitignore

@ -7,6 +7,7 @@
# Build directory
/[Bb]uild*/
doc-build/
out/
AppDir/
uruntime
@ -59,3 +60,4 @@ eden-windows-msvc
artifacts
*.AppImage*
/install*
vulkansdk*.exe

34
CMakeLists.txt

@ -307,11 +307,9 @@ endif()
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
cmake_dependent_option(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" OFF)
if (ENABLE_SDL2)
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" OFF "NOT MSVC" OFF)
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
endif()
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Build SDL2 from external source" OFF "ENABLE_SDL2;NOT MSVC" OFF)
cmake_dependent_option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "ENABLE_SDL2" OFF)
# qt stuff
option(ENABLE_QT "Enable the Qt frontend" ON)
@ -332,10 +330,14 @@ option(YUZU_USE_CPM "Use CPM to fetch system dependencies (fmt, boost, etc) if n
# ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
# sirit
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${EXT_DEFAULT})
set(BUNDLED_SIRIT_DEFAULT OFF)
if ((MSVC AND NOT (CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo") OR ANDROID))
set(BUNDLED_SIRIT_DEFAULT ON)
endif()
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# Re-allow on FreeBSD once its on mainline ports
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR APPLE" OFF)
@ -372,6 +374,17 @@ if(USE_CCACHE)
else()
message(FATAL_ERROR "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
endif()
# Follow SCCache recommendations:
# <https://github.com/mozilla/sccache/blob/main/README.md?plain=1#L144>
if(WIN32)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
endif()
endif()
endif()
# TODO(crueter): CI this?
@ -443,13 +456,6 @@ if (ANDROID)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5) # Workaround for Oboe
endif()
# We need to downgrade debug info (/Zi -> /Z7) to use an older but more cacheable format
# See https://github.com/nanoant/CMakePCHCompiler/issues/21
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
# Default to a Release build
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)

58
CMakeModules/MinGWClangCross.cmake

@ -1,58 +0,0 @@
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}clang)
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}clang++)
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
set(CMAKE_C_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
set(CMAKE_CXX_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
set(CMAKE_C_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
set(CMAKE_CXX_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
# Mingw tools
set(STRIP ${MINGW_TOOL_PREFIX}strip)
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
# ccache wrapper
option(USE_CCACHE "Use ccache for compilation" OFF)
if(USE_CCACHE)
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache found in PATH")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
else(CCACHE)
message(WARNING "USE_CCACHE enabled, but no ccache found")
endif(CCACHE)
endif(USE_CCACHE)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Echo modified cmake vars to screen for debugging purposes
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
message("")
message("Custom cmake vars: (blank = system default)")
message("-----------------------------------------")
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
message("* WINDRES : ${WINDRES}")
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
message("* STRIP : ${STRIP}")
message("* USE_CCACHE : ${USE_CCACHE}")
message("")
# So that the debug info only appears once
set(ENV{MINGW_DEBUG_INFO} SHOWN)
endif()

57
CMakeModules/MinGWCross.cmake

@ -1,57 +0,0 @@
# SPDX-FileCopyrightText: 2018 tech4me <guiwanglong@gmail.com>
# SPDX-License-Identifier: GPL-2.0-or-later
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Actually a hack, w/o this will cause some strange errors
set(CMAKE_HOST_WIN32 TRUE)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++)
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
# Mingw tools
set(STRIP ${MINGW_TOOL_PREFIX}strip)
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
# ccache wrapper
option(USE_CCACHE "Use ccache for compilation" OFF)
if(USE_CCACHE)
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache found in PATH")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
else(CCACHE)
message(WARNING "USE_CCACHE enabled, but no ccache found")
endif(CCACHE)
endif(USE_CCACHE)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Echo modified cmake vars to screen for debugging purposes
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
message("")
message("Custom cmake vars: (blank = system default)")
message("-----------------------------------------")
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
message("* WINDRES : ${WINDRES}")
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
message("* STRIP : ${STRIP}")
message("* USE_CCACHE : ${USE_CCACHE}")
message("")
# So that the debug info only appears once
set(ENV{MINGW_DEBUG_INFO} SHOWN)
endif()

BIN
dist/eden.ico

9
dist/icon_variations/README.md

@ -0,0 +1,9 @@
# Icon variations
These icons are licensed under GPLv3. Please see the [script for generating icons](../../tools/README.md) and appropriatedly redirect for seasonal icons.
- `base_named.svg` - Named variant.
- `base_small.svg`: Variant used for tiny icons (16x16, 64x64, etc).
- `base.svg`: Variant without branding/naming.
Try to keep the icons simple. And preferably automatically be able to be generated (to reduce maintanaince burden). Additionally keep the files small (use `svgo` and `optipng`) to not clutter the git.

80
dist/icon_variations/base_small.svg

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="512"
fill="none"
viewBox="0 0 512 512"
version="1.1"
id="svg7"
sodipodi:docname="base_small.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs7">
<linearGradient
id="linearGradient10"
inkscape:collect="always">
<stop
style="stop-color:#f977d9;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#a655d5;stop-opacity:1;"
offset="0.54051435"
id="stop13" />
<stop
style="stop-color:#984fd5;stop-opacity:1;"
offset="0.5714342"
id="stop12" />
<stop
style="stop-color:#8c4ad4;stop-opacity:1;"
offset="0.59666741"
id="stop14" />
<stop
style="stop-color:#3928d2;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient10"
id="linearGradient11"
x1="264.17508"
y1="28.385544"
x2="264.17508"
y2="488.65109"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.97815818,0,0,0.97880258,4.570042,5.8799159)" />
</defs>
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.88388348"
inkscape:cx="153.30075"
inkscape:cy="243.24473"
inkscape:window-width="1600"
inkscape:window-height="849"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg7"
showguides="false" />
<path
id="path9"
style="fill:url(#linearGradient11);fill-opacity:1;stroke:none;stroke-width:13.3314;stroke-opacity:1;paint-order:stroke fill markers"
d="M 262.97461 33.6875 A 218.44267 225.23091 0 0 0 138.83789 73.589844 L 141.13867 72.265625 L 161.2207 65.195312 L 181.01953 61.517578 L 204.35352 64.205078 L 228.96094 76.650391 L 265.80078 115 L 283 99.400391 L 304.59961 85.800781 L 331 76.400391 L 360.59961 69.599609 L 379.95508 69.208984 A 218.44267 225.23091 0 0 0 262.97461 33.6875 z M 380.07617 69.291016 L 350.19922 77.800781 L 329.19922 89.199219 L 307.40039 108 L 288.80078 129.80078 L 287.40039 135 L 302.40039 129.59961 L 319 127.80078 L 348.80078 131.80078 L 370.19922 141.40039 L 393 161.40039 L 399.59961 171.59961 L 374.80078 160.80078 L 338.40039 150.80078 L 309.19922 150 L 288.80078 154.40039 L 293.19922 155.19922 L 319.19922 168.80078 L 338 187.19922 L 350.80078 224 L 349.19922 260 L 326 222.80078 L 302.80078 194 L 277.59961 172.40039 L 269.19922 187.59961 L 256.80078 281.59961 L 258 364 L 278.40039 452.80078 L 297.19531 481.36914 A 218.44267 225.23091 0 0 0 481.41797 258.91797 A 218.44267 225.23091 0 0 0 380.07617 69.291016 z M 133.07422 77.839844 A 218.44267 225.23091 0 0 0 44.533203 258.91797 A 218.44267 225.23091 0 0 0 257.04102 484.06641 L 247.3457 458.62891 L 237.87109 418.18359 L 233.0625 380.42383 L 230.375 354.9668 L 229.95117 321.30859 L 232.35547 291.32812 L 237.44727 254.98242 L 254.55859 191.9082 L 261.62891 172.5332 L 255.54883 174.08984 L 240.98242 180.87695 L 229.59961 190.19922 L 210.59961 208.19922 L 197.40039 229.40039 L 186.40039 252.59961 L 173.40039 269 L 171.40039 253.19922 L 173.59961 229.40039 L 183 202.40039 L 199.40039 178 L 221.04102 153.44141 L 209.02148 155.70508 L 177.05859 162.77539 L 148.06836 171.40234 L 119.92578 183 L 120.63281 180.16992 L 129.82422 165.88672 L 151.17969 147.50195 L 172.95898 135.48242 L 190.07031 129.54102 L 209.02148 127.98633 L 227.6875 128.69336 L 247.76953 128.41016 L 246.49805 125.1582 L 226.69922 105.92383 L 219.62695 97.439453 L 221.4668 90.085938 L 206.75781 94.044922 L 195.86914 95.458984 L 179.46289 88.671875 L 156.12891 81.458984 L 133.07422 77.839844 z " />
<path
style="fill:#1b1b1b;fill-opacity:0.12492698;stroke:none;stroke-width:13.374;stroke-opacity:0.415999;paint-order:stroke fill markers"
d="m 259.36665,490.16617 39.03323,-6.96642 -20,-30.4 -20.4,-88.8 -1.2,-82.4 12.4,-94 8.4,-15.2 25.2,21.6 23.2,28.8 23.2,37.2 1.6,-36 -12.8,-36.8 -18.8,-18.4 -26,-13.6 -4.4,-0.8 20.4,-4.4 29.2,0.8 36.4,10 24.8,10.8 -6.6,-10.2 -22.8,-20 -21.4,-9.6 -29.8,-4 -16.6,1.8 -15,5.4 1.4,-5.2 18.6,-21.8 21.8,-18.800003 21,-11.4 30.2,-8.6 -19.8,0.4 -29.6,6.8 -26.4,9.4 -21.6,13.6 -17.2,15.600003 -36.83882,-38.349628 -24.60732,-12.445079 -23.33452,-2.687006 -19.79899,3.676955 -20.08184,7.071068 -9.33381,5.374012 24.32448,3.818376 23.33452,7.212489 16.40488,6.788226 10.88944,-1.414214 14.70782,-3.959798 -1.83847,7.353911 7.07106,8.485288 19.79899,19.2333 1.2728,3.25269 -20.08184,0.28284 -18.66762,-0.7071 -18.95046,1.55563 -17.11198,5.9397 -21.77889,12.02081 -21.35462,18.38478 -9.19239,14.28356 -0.70711,2.82843 28.14285,-11.59656 28.99138,-8.6267 31.96122,-7.07107 12.02082,-2.26274 -21.64158,24.55783 -16.4,24.4 -9.4,27 -2.2,23.8 2,15.8 13,-16.4 11,-23.2 13.2,-21.2 19,-18 11.38199,-9.32209 14.5664,-6.78822 6.08112,-1.55564 -7.07107,19.37473 -17.11198,63.07393 -5.09117,36.34528 -2.40416,29.98133 0.42426,33.65828 2.68701,25.45585 4.80832,37.7595 9.47523,40.44651 z"
id="path8-4" />
</svg>

BIN
dist/yuzu.bmp

46
docs/Build.md

@ -26,7 +26,7 @@ Android has a completely different build process than other platforms. See its [
If the configure phase fails, see the `Troubleshooting` section below. Usually, as long as you followed the dependencies guide, the defaults *should* successfully configure and build.
### Qt Creator
### Option A: Qt Creator
This is the recommended GUI method for Linux, macOS, and Windows.
@ -46,39 +46,51 @@ Hit "Configure Project", then wait for CMake to finish configuring (may take a w
</details>
### Command Line
This is recommended for *BSD, Solaris, Linux, and MSYS2. MSVC is possible, but not recommended.
### Option B: Command Line
<details>
<summary>Click to Open</summary>
Note that CMake must be in your PATH, and you must be in the cloned Eden directory. On Windows, you must also set up a Visual C++ development environment. This can be done by running `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat` in the same terminal.
Recommended generators:
> [!WARNING]
>For all systems:
>- *CMake* **MUST** be in your PATH (and also *ninja*, if you are using it as `<GENERATOR>`)
>- You *MUST* be in the cloned *Eden* directory
>
>On Windows:
> - It's recommended to install **[Ninja](https://ninja-build.org/)**
> - You must load **Visual C++ development environment**, this can be done by running our convenience script:
> - `tools/windows/load-msvc-env.ps1` (for PowerShell 5+)
> - `tools/windows/load-msvc-env.sh` (for MSYS2, Git Bash, etc)
Available `<GENERATOR>`:
- MSYS2: `MSYS Makefiles`
- MSVC: Install **[ninja](https://ninja-build.org/)** and use `Ninja`, OR use `Visual Studio 17 2022`
- MSVC: `Ninja` (preferred) or `Visual Studio 17 2022`
- macOS: `Ninja` (preferred) or `Xcode`
- Others: `Ninja` (preferred) or `UNIX Makefiles`
BUILD_TYPE should usually be `Release` or `RelWithDebInfo` (debug symbols--compiled executable will be large). If you are using a debugger and annoyed with stuff getting optimized out, try `Debug`.
Available `<BUILD_TYPE>`:
- `Release` (default)
- `RelWithDebInfo` (debug symbols--compiled executable will be large)
- `Debug` (if you are using a debugger and annoyed with stuff getting optimized out)
Caveat for Debug Builds:
- If you're building with CCache, you will need to add the environment variable `CL` with the `/FS` flag ([Reference](https://learn.microsoft.com/pt-br/cpp/build/reference/fs-force-synchronous-pdb-writes?view=msvc-170))
Also see the [Options](Options.md) page for additional CMake options.
```sh
cmake -S . -B build -G "GENERATOR" -DCMAKE_BUILD_TYPE=<BUILD_TYPE> -DYUZU_TESTS=OFF
cmake -S . -B build -G "<GENERATOR>" -DCMAKE_BUILD_TYPE=<BUILD_TYPE> -DYUZU_TESTS=OFF
```
If you are on Windows and prefer to use Clang:
```sh
cmake -S . -B build -G "GENERATOR" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
cmake -S . -B build -G "<GENERATOR>" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
```
</details>
### [CLion](https://www.jetbrains.com/clion/)
### Option C: [CLion](https://www.jetbrains.com/clion/)
<details>
<summary>Click to Open</summary>
@ -133,13 +145,13 @@ Many platforms have quirks, bugs, and other fun stuff that may cause issues when
## Building & Running
### Qt Creator
### On Qt Creator
Simply hit Ctrl+B, or the "hammer" icon in the bottom left. To run, hit the "play" icon, or Ctrl+R.
### Command Line
### On Command Line
If you are not on Windows and are using the `UNIX Makefiles` generator, you must also add `-j$(nproc)` to this command.
If you are using the `UNIX Makefiles` or `Visual Studio 17 2022` as `<GENERATOR>`, you should also add `--parallel` for faster build times.
```
cmake --build build
@ -157,4 +169,4 @@ Some platforms have convenience scripts provided for building.
- **[Linux](scripts/Linux.md)**
- **[Windows](scripts/Windows.md)**
macOS scripts will come soon.
macOS scripts will come soon.

22
docs/Deps.md

@ -3,10 +3,13 @@
To build Eden, you MUST have a C++ compiler.
* On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+
- GCC 12 also requires Clang 14+
* On Windows, this is either:
- **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option),
- clang-cl - can be downloaded from the MSVC installer,
- or **[MSYS2](https://www.msys2.org)**
* On Windows, we support:
- **[MSVC](https://visualstudio.microsoft.com/downloads/)** (default)
- It's STRONGLY RECOMMENDED to use the **Community** option and **Visual Studio 2022**
- You need to install: **[Desktop development with C++](https://learn.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=msvc-170)**
- **[clang-cl](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-180)**
- You need to install: **C++ Clang tools for Windows**
- **[MSYS2](https://www.msys2.org)** (experimental)
* On macOS, this is Apple Clang
- This can be installed with `xcode-select --install`
@ -15,20 +18,23 @@ The following additional tools are also required:
* **[CMake](https://www.cmake.org/)** 3.22+ - already included with the Android SDK
* **[Git](https://git-scm.com/)** for version control
- **[Windows installer](https://gitforwindows.org)**
* **[Python3](https://www.python.org/downloads/)** 3.10+ - necessary to download external repositories
* On Windows, you must install the **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** as well
- *A convenience script to install the latest SDK is provided in `.ci/windows/install-vulkan-sdk.ps1`*
- *A convenience script to install the latest SDK is provided in:*
- `tools/windows/install-vulkan-sdk.ps1` (for PowerShell 5+)
- `tools/windows/install-vulkan-sdk.sh` (for MSYS2, Git Bash, etc)
If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the recommended IDE for building)
If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the **RECOMMENDED** IDE for building)
* On Linux, *BSD and macOS, this can be done by the package manager
- If you wish to use Qt Creator, append `qtcreator` or `qt-creator` to the commands seen below.
* MSVC/clang-cl users on Windows must install through the [official installer](https://www.qt.io/download-qt-installer-oss)
* MSVC/clang-cl users on Windows must install through the official [Qt](https://www.qt.io/download-qt-installer-oss) installer
* Linux and macOS users may choose to use the installer as well.
* MSYS2 can also install Qt 6 via the package manager
If you are on Windows, a convenience script to install MSVC, MSYS2, Qt, all necessary packages for MSYS2, and set up a zsh environment with useful keybinds and aliases can be found [here](https://git.crueter.xyz/scripts/windev).
- For help setting up Qt Creator, run `./install.sh -h qtcreator`
If you are on Windows and NOT building with MSYS2, you may go [back home](Build.md) and continue.
If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue.
## Externals
The following are handled by Eden's externals:

7
externals/CMakeLists.txt

@ -160,13 +160,6 @@ if (YUZU_USE_BUNDLED_SIRIT)
AddJsonPackage(sirit-ci)
else()
AddJsonPackage(sirit)
# Change to old-but-more-cacheable debug info on Windows
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
get_target_property(sirit_opts sirit COMPILE_OPTIONS)
list(FILTER sirit_opts EXCLUDE REGEX "/Zi")
list(APPEND sirit_opts "/Z7")
set_target_properties(sirit PROPERTIES COMPILE_OPTIONS "${sirit_opts}")
endif()
if(MSVC AND CXX_CLANG)
target_compile_options(siritobj PRIVATE -Wno-error=unused-command-line-argument)
endif()

28
externals/ffmpeg/CMakeLists.txt

@ -25,18 +25,26 @@ if (UNIX AND NOT ANDROID)
if (NOT APPLE)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"/usr/lib/xorg/amd64/libdrm.so")
find_library(LIBDRM_LIB libdrm PATHS /usr/lib/64 /usr/lib/amd64 /usr/lib)
if(LIBDRM_LIB)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"${LIBDRM_LIB}")
message(STATUS "Found libdrm at: ${LIBDRM_LIB}")
else()
message(WARNING "libdrm not found, disabling libdrm support")
list(APPEND FFmpeg_HWACCEL_FLAGS
--disable-libdrm)
endif()
else()
pkg_check_modules(LIBDRM libdrm REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-libdrm)
endif()
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-libdrm)
endif()
if(LIBVA_FOUND)
@ -247,11 +255,19 @@ else()
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
# BSD make or Solaris make don't support ffmpeg make-j8
if (PLATFORM_LINUX OR ANDROID OR APPLE OR WIN32 OR PLATFORM_FREEBSD)
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
else()
set(FFmpeg_MAKE_ARGS "")
endif()
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
make -j${SYSTEM_THREADS}
make ${FFmpeg_MAKE_ARGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)

4
externals/nx_tzdb/cpmfile.json

@ -5,7 +5,7 @@
"git_host": "git.crueter.xyz",
"artifact": "%VERSION%.tar.gz",
"tag": "%VERSION%",
"hash": "87abb2aeca716d5d77b05317086dbc2f8acfc2f3f76ce4778345ee3df19973e6cd8ecbf16cfab5ad94c9636a6c44fd3588f9aadd3cba89403cfd56c8bec645c5",
"version": "091025"
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
"version": "121125"
}
}

6
src/CMakeLists.txt

@ -35,7 +35,6 @@ if (MSVC AND NOT CXX_CLANG)
# /W4 - Level 4 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zm - Specifies the precompiled header memory allocation limit
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
@ -98,11 +97,6 @@ if (MSVC AND NOT CXX_CLANG)
)
endif()
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
if (ARCHITECTURE_x86_64)
add_compile_options(/QIntel-jcc-erratum)
endif()

37
src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt

@ -16,11 +16,15 @@ import java.io.FileOutputStream
import java.security.KeyStore
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
import android.content.res.Configuration
import android.os.LocaleList
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.PowerStateUpdater
import java.util.Locale
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
@ -73,5 +77,38 @@ class YuzuApplication : Application() {
val appContext: Context
get() = application.applicationContext
private val LANGUAGE_CODES = arrayOf(
"system", "en", "es", "fr", "de", "it", "pt", "pt-BR", "ru", "ja", "ko",
"zh-CN", "zh-TW", "pl", "cs", "nb", "hu", "uk", "vi", "id", "ar", "ckb", "fa", "he", "sr"
)
fun applyLanguage(context: Context): Context {
val languageIndex = IntSetting.APP_LANGUAGE.getInt()
val langCode = if (languageIndex in LANGUAGE_CODES.indices) {
LANGUAGE_CODES[languageIndex]
} else {
"system"
}
if (langCode == "system") {
return context
}
val locale = when {
langCode.contains("-") -> {
val parts = langCode.split("-")
Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).build()
}
else -> Locale.Builder().setLanguage(langCode).build()
}
Locale.setDefault(locale)
val config = Configuration(context.resources.configuration)
config.setLocales(LocaleList(locale))
return context.createConfigurationContext(config)
}
}
}

4
src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt

@ -86,6 +86,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var foregroundService: Intent? = null
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true
ThemeHelper.setTheme(this)

1
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt

@ -32,6 +32,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
MAX_ANISOTROPY("max_anisotropy"),
THEME("theme"),
THEME_MODE("theme_mode"),
APP_LANGUAGE("app_language"),
OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity"),
LOCK_DRAWER("lock_drawer"),

12
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt

@ -667,10 +667,11 @@ abstract class SettingsItem(
)
)
put(
SliderSetting(
SpinBoxSetting(
IntSetting.CPU_TICKS,
titleId = R.string.cpu_ticks,
descriptionId = 0,
valueHint = R.string.cpu_ticks,
min = 77,
max = 65535
)
@ -756,6 +757,15 @@ abstract class SettingsItem(
titleId = R.string.enable_update_checks,
)
)
put(
SingleChoiceSetting(
IntSetting.APP_LANGUAGE,
titleId = R.string.app_language,
descriptionId = R.string.app_language_description,
choicesId = R.array.appLanguageNames,
valuesId = R.array.appLanguageValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_DEBUG,

20
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt

@ -1,8 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import org.yuzu.yuzu_emu.YuzuApplication
import android.os.Bundle
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
@ -24,6 +29,7 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.collect
class SettingsActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsBinding
@ -32,6 +38,10 @@ class SettingsActivity : AppCompatActivity() {
private val settingsViewModel: SettingsViewModel by viewModels()
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
@ -125,6 +135,16 @@ class SettingsActivity : AppCompatActivity() {
NativeConfig.savePerGameConfig()
NativeConfig.unloadPerGameConfig()
}
if (settingsViewModel.shouldRecreateForLanguageChange.value) {
settingsViewModel.setShouldRecreateForLanguageChange(false)
val relaunchIntent = packageManager?.getLaunchIntentForPackage(packageName)
if (relaunchIntent != null) {
relaunchIntent.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK or android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(relaunchIntent)
android.os.Process.killProcess(android.os.Process.myPid())
}
}
}
}

8
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt

@ -425,6 +425,14 @@ class SettingsAdapter(
position
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
// reset language if detected
if (item.setting.key == "app_language") {
// recreate page apply language change instantly
fragment.requireActivity().recreate()
settingsViewModel.setShouldRecreateForLanguageChange(true)
}
return true
}

57
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt

@ -14,6 +14,9 @@ import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.MotionEvent
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
@ -197,6 +200,54 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
updateValidity(newValue)
}
fun attachRepeat(button: View, delta: Int) {
val handler = Handler(Looper.getMainLooper())
var runnable: Runnable? = null
val initialDelay = 400L
val minDelay = 40L
val accelerationFactor = 0.75f
button.setOnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
val current = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
val newValue = (current + delta)
spinboxBinding.editValue.setText(newValue.toString())
updateValidity(newValue)
var delay = initialDelay
runnable = object : Runnable {
override fun run() {
val curr = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
val next = curr + delta
spinboxBinding.editValue.setText(next.toString())
updateValidity(next)
// accelerate
delay = (delay * accelerationFactor).toLong().coerceAtLeast(minDelay)
handler.postDelayed(this, delay)
}
}
handler.postDelayed(runnable!!, initialDelay)
true
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
if (runnable != null) {
handler.removeCallbacks(runnable!!)
runnable = null
}
v.performClick()
true
}
else -> false
}
}
}
attachRepeat(spinboxBinding.buttonDecrement, -1)
attachRepeat(spinboxBinding.buttonIncrement, 1)
spinboxBinding.editValue.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
@ -336,6 +387,12 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
.show()
}
scSetting.setSelectedValue(value)
if (scSetting.setting.key == "app_language") {
settingsViewModel.setShouldRecreateForLanguageChange(true)
// recreate page apply language change instantly
requireActivity().recreate()
}
}
is StringSingleChoiceSetting -> {

4
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt

@ -1030,8 +1030,10 @@ class SettingsFragmentPresenter(
override fun reset() = IntSetting.THEME.setInt(defaultValue)
}
add(HeaderSetting(R.string.app_settings))
add(IntSetting.APP_LANGUAGE.key)
if (NativeLibrary.isUpdateCheckerEnabled()) {
add(HeaderSetting(R.string.app_settings))
add(BooleanSetting.ENABLE_UPDATE_CHECKS.key)
}

9
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -54,6 +57,8 @@ class SettingsViewModel : ViewModel() {
private val _shouldShowResetInputDialog = MutableStateFlow(false)
val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow()
private val _shouldRecreateForLanguageChange = MutableStateFlow(false)
val shouldRecreateForLanguageChange = _shouldRecreateForLanguageChange.asStateFlow()
fun setShouldRecreate(value: Boolean) {
_shouldRecreate.value = value
}
@ -103,6 +108,10 @@ class SettingsViewModel : ViewModel() {
_shouldShowResetInputDialog.value = value
}
fun setShouldRecreateForLanguageChange(value: Boolean) {
_shouldRecreateForLanguageChange.value = value
}
fun getCurrentDeviceParams(defaultParams: ParamPackage): ParamPackage =
try {
InputHandler.registeredControllers[currentDevice]

6
src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.view.View
@ -53,6 +54,7 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
import kotlin.text.compareTo
import androidx.core.net.toUri
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.YuzuApplication
class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding
@ -68,6 +70,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val CHECKED_DECRYPTION = "CheckedDecryption"
private var checkedDecryption = false
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }

1
src/android/app/src/main/jni/android_settings.h

@ -62,6 +62,7 @@ namespace AndroidSettings {
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
Settings::Category::Android};
Settings::Setting<s32> app_language{linkage, 0, "app_language", Settings::Category::Android};
Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks",
Settings::Category::Android};

55
src/android/app/src/main/res/values/arrays.xml

@ -391,6 +391,61 @@
<item>2</item>
</integer-array>
<string-array name="appLanguageNames">
<item>@string/app_language_system</item>
<item>@string/app_language_english</item>
<item>@string/app_language_spanish</item>
<item>@string/app_language_french</item>
<item>@string/app_language_german</item>
<item>@string/app_language_italian</item>
<item>@string/app_language_portuguese</item>
<item>@string/app_language_brazilian_portuguese</item>
<item>@string/app_language_russian</item>
<item>@string/app_language_japanese</item>
<item>@string/app_language_korean</item>
<item>@string/app_language_simplified_chinese</item>
<item>@string/app_language_traditional_chinese</item>
<item>@string/app_language_polish</item>
<item>@string/app_language_czech</item>
<item>@string/app_language_norwegian</item>
<item>@string/app_language_hungarian</item>
<item>@string/app_language_ukrainian</item>
<item>@string/app_language_vietnamese</item>
<item>@string/app_language_indonesian</item>
<item>@string/app_language_arabic</item>
<item>@string/app_language_central_kurdish</item>
<item>@string/app_language_persian</item>
<item>@string/app_language_hebrew</item>
<item>@string/app_language_serbian</item>
</string-array>
<integer-array name="appLanguageValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
<item>18</item>
<item>19</item>
<item>20</item>
<item>21</item>
<item>22</item>
<item>23</item>
<item>24</item>
</integer-array>
<string-array name="outputEngineEntries">
<item>@string/auto</item>
<item>@string/oboe</item>

29
src/android/app/src/main/res/values/strings.xml

@ -1028,6 +1028,35 @@
<string name="use_black_backgrounds">Black backgrounds</string>
<string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
<!-- App Language -->
<string name="app_language">App Language</string>
<string name="app_language_description">Change the language of the app interface</string>
<string name="app_language_system">Follow System</string>
<string name="app_language_english">English</string>
<string name="app_language_spanish">Español</string>
<string name="app_language_french">Français</string>
<string name="app_language_german">Deutsch</string>
<string name="app_language_italian">Italiano</string>
<string name="app_language_portuguese">Português</string>
<string name="app_language_brazilian_portuguese">Português do Brasil</string>
<string name="app_language_russian">Русский</string>
<string name="app_language_japanese">日本語</string>
<string name="app_language_korean">한국어</string>
<string name="app_language_simplified_chinese">简体中文</string>
<string name="app_language_traditional_chinese">繁體中文</string>
<string name="app_language_polish">Polski</string>
<string name="app_language_czech">Čeština</string>
<string name="app_language_norwegian">Norsk bokmål</string>
<string name="app_language_hungarian">Magyar</string>
<string name="app_language_ukrainian">Українська</string>
<string name="app_language_vietnamese">Tiếng Việt</string>
<string name="app_language_indonesian">Bahasa Indonesia</string>
<string name="app_language_arabic">العربية</string>
<string name="app_language_central_kurdish">کوردیی ناوەندی</string>
<string name="app_language_persian">فارسی</string>
<string name="app_language_hebrew">עברית</string>
<string name="app_language_serbian">Српски</string>
<!-- Static Themes -->
<string name="static_theme_color">Theme Color</string>
<string name="eden_theme">Eden (Default)</string>

47
src/common/atomic_ops.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -103,46 +106,60 @@ template <>
#else
// Some architectures lack u128, there is no definitive way to check them all without even more macro magic
// so let's just... do this; add your favourite arches once they get u128 support :)
#if (defined(__clang__) || defined(__GNUC__)) && (defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64))
using RealU128 = unsigned __int128;
# define SYNC_VAL_COMPARE_AND_SWAP(p, e, v) __sync_val_compare_and_swap(p, e, v)
# define SYNC_BOOL_COMPARE_AND_SWAP(p, e, v) __sync_bool_compare_and_swap(p, e, v)
# define U128_ZERO_INIT 0
#else
using RealU128 = u128;
# define SYNC_VAL_COMPARE_AND_SWAP(p, e, v) ((*p == e) ? *p = v : *p)
# define SYNC_BOOL_COMPARE_AND_SWAP(p, e, v) ((*p == e) ? (void)(*p = v) : (void)0), true
# define U128_ZERO_INIT {}
#endif
template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
return SYNC_BOOL_COMPARE_AND_SWAP(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
RealU128 value_a;
RealU128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
return SYNC_BOOL_COMPARE_AND_SWAP((RealU128*)pointer, expected_a, value_a);
}
template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected, T& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
actual = SYNC_VAL_COMPARE_AND_SWAP(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected,
u128& actual) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
unsigned __int128 actual_a;
[[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected, u128& actual) {
RealU128 value_a;
RealU128 expected_a;
RealU128 actual_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
actual_a = SYNC_VAL_COMPARE_AND_SWAP((RealU128*)pointer, expected_a, value_a);
std::memcpy(actual.data(), &actual_a, sizeof(u128));
return actual_a == expected_a;
}
[[nodiscard]] inline u128 AtomicLoad128(u64* pointer) {
unsigned __int128 zeros_a = 0;
unsigned __int128 result_a =
__sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
RealU128 zeros_a = U128_ZERO_INIT;
RealU128 result_a = SYNC_VAL_COMPARE_AND_SWAP((RealU128*)pointer, zeros_a, zeros_a);
u128 result;
std::memcpy(result.data(), &result_a, sizeof(u128));
return result;
}
#undef U128_ZERO_INIT
#undef SYNC_VAL_COMPARE_AND_SWAP
#undef SYNC_BOOL_COMPARE_AND_SWAP
#endif

16
src/common/logging/formatter.h

@ -1,11 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <type_traits>
#include <fmt/ranges.h>
#include "common/swap.h"
// adapted from https://github.com/fmtlib/fmt/issues/2704
// a generic formatter for enum classes
@ -20,3 +23,14 @@ struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
}
};
#endif
template <typename T, typename U>
struct fmt::formatter<SwapStructT<T, U>> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const SwapStructT<T, U>& reg, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "{}", T(reg));
}
};

3
src/common/settings.cpp

@ -359,6 +359,9 @@ void RestoreGlobalState(bool is_powered_on) {
for (const auto& reset : values.linkage.restore_functions) {
reset();
}
// Reset per-game flags
values.use_squashed_iterated_blend = false;
}
static bool configuring_global = true;

3
src/common/settings.h

@ -759,6 +759,9 @@ struct Values {
// Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons;
// Per-game overrides
bool use_squashed_iterated_blend;
};
extern Values values;

279
src/common/swap.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2012 PPSSPP Project
// SPDX-FileCopyrightText: 2012 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -17,7 +20,17 @@
namespace Common {
#ifdef _MSC_VER
#if defined(__Bitrig__) || defined(__OpenBSD__)
// We'll redefine swap16, swap32, swap64 as inline functions
// but OpenBSD is like "wow I bring my own stuff"
// It would be nice if we could use them without C++ namespace shenanigans
// But alas :)
#undef swap16
#undef swap32
#undef swap64
#endif
#if defined(_MSC_VER)
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return _byteswap_ushort(data);
}
@ -28,12 +41,6 @@ namespace Common {
return _byteswap_uint64(data);
}
#elif defined(__clang__) || defined(__GNUC__)
#if defined(__Bitrig__) || defined(__OpenBSD__)
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
#endif
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return __builtin_bswap16(data);
}
@ -44,7 +51,9 @@ namespace Common {
return __builtin_bswap64(data);
}
#else
// Generic implementation.
// Generic implementation - compiler will optimise these into their respective
// __builtin_byteswapXX() and such; if not, the compiler is stupid and we probably
// have bigger problems to worry about :)
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return (data >> 8) | (data << 8);
}
@ -62,33 +71,27 @@ namespace Common {
[[nodiscard]] inline float swapf(float f) noexcept {
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
u32 value;
std::memcpy(&value, &f, sizeof(u32));
value = swap32(value);
std::memcpy(&f, &value, sizeof(u32));
return f;
}
[[nodiscard]] inline double swapd(double f) noexcept {
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
u64 value;
std::memcpy(&value, &f, sizeof(u64));
value = swap64(value);
std::memcpy(&f, &value, sizeof(u64));
return f;
}
} // Namespace Common
template <typename T, typename F>
struct swap_struct_t {
using swapped_t = swap_struct_t;
struct SwapStructT {
using SwappedT = SwapStructT;
protected:
T value;
@ -101,137 +104,137 @@ public:
T swap() const {
return swap(value);
}
swap_struct_t() = default;
swap_struct_t(const T& v) : value(swap(v)) {}
SwapStructT() = default;
SwapStructT(const T& v) : value(swap(v)) {}
template <typename S>
swapped_t& operator=(const S& source) {
value = swap(static_cast<T>(source));
SwappedT& operator=(const S& source) {
value = swap(T(source));
return *this;
}
operator s8() const {
return static_cast<s8>(swap());
return s8(swap());
}
operator u8() const {
return static_cast<u8>(swap());
return u8(swap());
}
operator s16() const {
return static_cast<s16>(swap());
return s16(swap());
}
operator u16() const {
return static_cast<u16>(swap());
return u16(swap());
}
operator s32() const {
return static_cast<s32>(swap());
return s32(swap());
}
operator u32() const {
return static_cast<u32>(swap());
return u32(swap());
}
operator s64() const {
return static_cast<s64>(swap());
return s64(swap());
}
operator u64() const {
return static_cast<u64>(swap());
return u64(swap());
}
operator float() const {
return static_cast<float>(swap());
return float(swap());
}
operator double() const {
return static_cast<double>(swap());
return double(swap());
}
// +v
swapped_t operator+() const {
SwappedT operator+() const {
return +swap();
}
// -v
swapped_t operator-() const {
SwappedT operator-() const {
return -swap();
}
// v / 5
swapped_t operator/(const swapped_t& i) const {
SwappedT operator/(const SwappedT& i) const {
return swap() / i.swap();
}
template <typename S>
swapped_t operator/(const S& i) const {
SwappedT operator/(const S& i) const {
return swap() / i;
}
// v * 5
swapped_t operator*(const swapped_t& i) const {
SwappedT operator*(const SwappedT& i) const {
return swap() * i.swap();
}
template <typename S>
swapped_t operator*(const S& i) const {
SwappedT operator*(const S& i) const {
return swap() * i;
}
// v + 5
swapped_t operator+(const swapped_t& i) const {
SwappedT operator+(const SwappedT& i) const {
return swap() + i.swap();
}
template <typename S>
swapped_t operator+(const S& i) const {
return swap() + static_cast<T>(i);
SwappedT operator+(const S& i) const {
return swap() + T(i);
}
// v - 5
swapped_t operator-(const swapped_t& i) const {
SwappedT operator-(const SwappedT& i) const {
return swap() - i.swap();
}
template <typename S>
swapped_t operator-(const S& i) const {
return swap() - static_cast<T>(i);
SwappedT operator-(const S& i) const {
return swap() - T(i);
}
// v += 5
swapped_t& operator+=(const swapped_t& i) {
SwappedT& operator+=(const SwappedT& i) {
value = swap(swap() + i.swap());
return *this;
}
template <typename S>
swapped_t& operator+=(const S& i) {
value = swap(swap() + static_cast<T>(i));
SwappedT& operator+=(const S& i) {
value = swap(swap() + T(i));
return *this;
}
// v -= 5
swapped_t& operator-=(const swapped_t& i) {
SwappedT& operator-=(const SwappedT& i) {
value = swap(swap() - i.swap());
return *this;
}
template <typename S>
swapped_t& operator-=(const S& i) {
value = swap(swap() - static_cast<T>(i));
SwappedT& operator-=(const S& i) {
value = swap(swap() - T(i));
return *this;
}
// ++v
swapped_t& operator++() {
SwappedT& operator++() {
value = swap(swap() + 1);
return *this;
}
// --v
swapped_t& operator--() {
SwappedT& operator--() {
value = swap(swap() - 1);
return *this;
}
// v++
swapped_t operator++(int) {
swapped_t old = *this;
SwappedT operator++(int) {
SwappedT old = *this;
value = swap(swap() + 1);
return old;
}
// v--
swapped_t operator--(int) {
swapped_t old = *this;
SwappedT operator--(int) {
SwappedT old = *this;
value = swap(swap() - 1);
return old;
}
// Comparison
// v == i
bool operator==(const swapped_t& i) const {
bool operator==(const SwappedT& i) const {
return swap() == i.swap();
}
template <typename S>
@ -240,7 +243,7 @@ public:
}
// v != i
bool operator!=(const swapped_t& i) const {
bool operator!=(const SwappedT& i) const {
return swap() != i.swap();
}
template <typename S>
@ -249,7 +252,7 @@ public:
}
// v > i
bool operator>(const swapped_t& i) const {
bool operator>(const SwappedT& i) const {
return swap() > i.swap();
}
template <typename S>
@ -258,7 +261,7 @@ public:
}
// v < i
bool operator<(const swapped_t& i) const {
bool operator<(const SwappedT& i) const {
return swap() < i.swap();
}
template <typename S>
@ -267,7 +270,7 @@ public:
}
// v >= i
bool operator>=(const swapped_t& i) const {
bool operator>=(const SwappedT& i) const {
return swap() >= i.swap();
}
template <typename S>
@ -276,7 +279,7 @@ public:
}
// v <= i
bool operator<=(const swapped_t& i) const {
bool operator<=(const SwappedT& i) const {
return swap() <= i.swap();
}
template <typename S>
@ -285,82 +288,82 @@ public:
}
// logical
swapped_t operator!() const {
SwappedT operator!() const {
return !swap();
}
// bitmath
swapped_t operator~() const {
SwappedT operator~() const {
return ~swap();
}
swapped_t operator&(const swapped_t& b) const {
SwappedT operator&(const SwappedT& b) const {
return swap() & b.swap();
}
template <typename S>
swapped_t operator&(const S& b) const {
SwappedT operator&(const S& b) const {
return swap() & b;
}
swapped_t& operator&=(const swapped_t& b) {
SwappedT& operator&=(const SwappedT& b) {
value = swap(swap() & b.swap());
return *this;
}
template <typename S>
swapped_t& operator&=(const S b) {
SwappedT& operator&=(const S b) {
value = swap(swap() & b);
return *this;
}
swapped_t operator|(const swapped_t& b) const {
SwappedT operator|(const SwappedT& b) const {
return swap() | b.swap();
}
template <typename S>
swapped_t operator|(const S& b) const {
SwappedT operator|(const S& b) const {
return swap() | b;
}
swapped_t& operator|=(const swapped_t& b) {
SwappedT& operator|=(const SwappedT& b) {
value = swap(swap() | b.swap());
return *this;
}
template <typename S>
swapped_t& operator|=(const S& b) {
SwappedT& operator|=(const S& b) {
value = swap(swap() | b);
return *this;
}
swapped_t operator^(const swapped_t& b) const {
SwappedT operator^(const SwappedT& b) const {
return swap() ^ b.swap();
}
template <typename S>
swapped_t operator^(const S& b) const {
SwappedT operator^(const S& b) const {
return swap() ^ b;
}
swapped_t& operator^=(const swapped_t& b) {
SwappedT& operator^=(const SwappedT& b) {
value = swap(swap() ^ b.swap());
return *this;
}
template <typename S>
swapped_t& operator^=(const S& b) {
SwappedT& operator^=(const S& b) {
value = swap(swap() ^ b);
return *this;
}
template <typename S>
swapped_t operator<<(const S& b) const {
SwappedT operator<<(const S& b) const {
return swap() << b;
}
template <typename S>
swapped_t& operator<<=(const S& b) const {
SwappedT& operator<<=(const S& b) const {
value = swap(swap() << b);
return *this;
}
template <typename S>
swapped_t operator>>(const S& b) const {
SwappedT operator>>(const S& b) const {
return swap() >> b;
}
template <typename S>
swapped_t& operator>>=(const S& b) const {
SwappedT& operator>>=(const S& b) const {
value = swap(swap() >> b);
return *this;
}
@ -370,167 +373,167 @@ public:
// Arithmetic
template <typename S, typename T2, typename F2>
friend S operator+(const S& p, const swapped_t v);
friend S operator+(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend S operator-(const S& p, const swapped_t v);
friend S operator-(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend S operator/(const S& p, const swapped_t v);
friend S operator/(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend S operator*(const S& p, const swapped_t v);
friend S operator*(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend S operator%(const S& p, const swapped_t v);
friend S operator%(const S& p, const SwappedT v);
// Arithmetic + assignments
template <typename S, typename T2, typename F2>
friend S operator+=(const S& p, const swapped_t v);
friend S operator+=(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend S operator-=(const S& p, const swapped_t v);
friend S operator-=(const S& p, const SwappedT v);
// Bitmath
template <typename S, typename T2, typename F2>
friend S operator&(const S& p, const swapped_t v);
friend S operator&(const S& p, const SwappedT v);
// Comparison
template <typename S, typename T2, typename F2>
friend bool operator<(const S& p, const swapped_t v);
friend bool operator<(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend bool operator>(const S& p, const swapped_t v);
friend bool operator>(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend bool operator<=(const S& p, const swapped_t v);
friend bool operator<=(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend bool operator>=(const S& p, const swapped_t v);
friend bool operator>=(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend bool operator!=(const S& p, const swapped_t v);
friend bool operator!=(const S& p, const SwappedT v);
template <typename S, typename T2, typename F2>
friend bool operator==(const S& p, const swapped_t v);
friend bool operator==(const S& p, const SwappedT v);
};
// Arithmetic
template <typename S, typename T, typename F>
S operator+(const S& i, const swap_struct_t<T, F> v) {
S operator+(const S& i, const SwapStructT<T, F> v) {
return i + v.swap();
}
template <typename S, typename T, typename F>
S operator-(const S& i, const swap_struct_t<T, F> v) {
S operator-(const S& i, const SwapStructT<T, F> v) {
return i - v.swap();
}
template <typename S, typename T, typename F>
S operator/(const S& i, const swap_struct_t<T, F> v) {
S operator/(const S& i, const SwapStructT<T, F> v) {
return i / v.swap();
}
template <typename S, typename T, typename F>
S operator*(const S& i, const swap_struct_t<T, F> v) {
S operator*(const S& i, const SwapStructT<T, F> v) {
return i * v.swap();
}
template <typename S, typename T, typename F>
S operator%(const S& i, const swap_struct_t<T, F> v) {
S operator%(const S& i, const SwapStructT<T, F> v) {
return i % v.swap();
}
// Arithmetic + assignments
template <typename S, typename T, typename F>
S& operator+=(S& i, const swap_struct_t<T, F> v) {
S& operator+=(S& i, const SwapStructT<T, F> v) {
i += v.swap();
return i;
}
template <typename S, typename T, typename F>
S& operator-=(S& i, const swap_struct_t<T, F> v) {
S& operator-=(S& i, const SwapStructT<T, F> v) {
i -= v.swap();
return i;
}
// Logical
template <typename S, typename T, typename F>
S operator&(const S& i, const swap_struct_t<T, F> v) {
S operator&(const S& i, const SwapStructT<T, F> v) {
return i & v.swap();
}
// Comparison
template <typename S, typename T, typename F>
bool operator<(const S& p, const swap_struct_t<T, F> v) {
bool operator<(const S& p, const SwapStructT<T, F> v) {
return p < v.swap();
}
template <typename S, typename T, typename F>
bool operator>(const S& p, const swap_struct_t<T, F> v) {
bool operator>(const S& p, const SwapStructT<T, F> v) {
return p > v.swap();
}
template <typename S, typename T, typename F>
bool operator<=(const S& p, const swap_struct_t<T, F> v) {
bool operator<=(const S& p, const SwapStructT<T, F> v) {
return p <= v.swap();
}
template <typename S, typename T, typename F>
bool operator>=(const S& p, const swap_struct_t<T, F> v) {
bool operator>=(const S& p, const SwapStructT<T, F> v) {
return p >= v.swap();
}
template <typename S, typename T, typename F>
bool operator!=(const S& p, const swap_struct_t<T, F> v) {
bool operator!=(const S& p, const SwapStructT<T, F> v) {
return p != v.swap();
}
template <typename S, typename T, typename F>
bool operator==(const S& p, const swap_struct_t<T, F> v) {
bool operator==(const S& p, const SwapStructT<T, F> v) {
return p == v.swap();
}
template <typename T>
struct swap_64_t {
struct Swap64T {
static T swap(T x) {
return static_cast<T>(Common::swap64(x));
return T(Common::swap64(x));
}
};
template <typename T>
struct swap_32_t {
struct Swap32T {
static T swap(T x) {
return static_cast<T>(Common::swap32(x));
return T(Common::swap32(x));
}
};
template <typename T>
struct swap_16_t {
struct Swap16T {
static T swap(T x) {
return static_cast<T>(Common::swap16(x));
return T(Common::swap16(x));
}
};
template <typename T>
struct swap_float_t {
struct SwapFloatT {
static T swap(T x) {
return static_cast<T>(Common::swapf(x));
return T(Common::swapf(x));
}
};
template <typename T>
struct swap_double_t {
struct SwapDoubleT {
static T swap(T x) {
return static_cast<T>(Common::swapd(x));
return T(Common::swapd(x));
}
};
template <typename T>
struct swap_enum_t {
struct SwapEnumT {
static_assert(std::is_enum_v<T>);
using base = std::underlying_type_t<T>;
public:
swap_enum_t() = default;
swap_enum_t(const T& v) : value(swap(v)) {}
SwapEnumT() = default;
SwapEnumT(const T& v) : value(swap(v)) {}
swap_enum_t& operator=(const T& v) {
SwapEnumT& operator=(const T& v) {
value = swap(v);
return *this;
}
@ -540,22 +543,20 @@ public:
}
explicit operator base() const {
return static_cast<base>(swap(value));
return base(swap(value));
}
protected:
T value{};
// clang-format off
using swap_t = std::conditional_t<
std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
// clang-format on
std::is_same_v<base, u16>, Swap16T<u16>, std::conditional_t<
std::is_same_v<base, s16>, Swap16T<s16>, std::conditional_t<
std::is_same_v<base, u32>, Swap32T<u32>, std::conditional_t<
std::is_same_v<base, s32>, Swap32T<s32>, std::conditional_t<
std::is_same_v<base, u64>, Swap64T<u64>, std::conditional_t<
std::is_same_v<base, s64>, Swap64T<s64>, void>>>>>>;
static T swap(T x) {
return static_cast<T>(swap_t::swap(static_cast<base>(x)));
return T(swap_t::swap(base(x)));
}
};
@ -581,17 +582,17 @@ struct AddEndian<u8, SwapTag> {
template <>
struct AddEndian<u16, SwapTag> {
using type = swap_struct_t<u16, swap_16_t<u16>>;
using type = SwapStructT<u16, Swap16T<u16>>;
};
template <>
struct AddEndian<u32, SwapTag> {
using type = swap_struct_t<u32, swap_32_t<u32>>;
using type = SwapStructT<u32, Swap32T<u32>>;
};
template <>
struct AddEndian<u64, SwapTag> {
using type = swap_struct_t<u64, swap_64_t<u64>>;
using type = SwapStructT<u64, Swap64T<u64>>;
};
template <>
@ -601,33 +602,33 @@ struct AddEndian<s8, SwapTag> {
template <>
struct AddEndian<s16, SwapTag> {
using type = swap_struct_t<s16, swap_16_t<s16>>;
using type = SwapStructT<s16, Swap16T<s16>>;
};
template <>
struct AddEndian<s32, SwapTag> {
using type = swap_struct_t<s32, swap_32_t<s32>>;
using type = SwapStructT<s32, Swap32T<s32>>;
};
template <>
struct AddEndian<s64, SwapTag> {
using type = swap_struct_t<s64, swap_64_t<s64>>;
using type = SwapStructT<s64, Swap64T<s64>>;
};
template <>
struct AddEndian<float, SwapTag> {
using type = swap_struct_t<float, swap_float_t<float>>;
using type = SwapStructT<float, SwapFloatT<float>>;
};
template <>
struct AddEndian<double, SwapTag> {
using type = swap_struct_t<double, swap_double_t<double>>;
using type = SwapStructT<double, SwapDoubleT<double>>;
};
template <typename T>
struct AddEndian<T, SwapTag> {
static_assert(std::is_enum_v<T>);
using type = swap_enum_t<T>;
using type = SwapEnumT<T>;
};
// Alias LETag/BETag as KeepTag/SwapTag depending on the system

7
src/core/arm/dynarmic/arm_dynarmic_32.cpp

@ -210,9 +210,12 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
config.wall_clock_cntpct = m_uses_wall_clock;
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
// Solaris doesn't support kPageSize >= 512MiB
// Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);
#endif
// Allow memory fault handling to work
if (m_system.DebuggerEnabled()) {

7
src/core/arm/dynarmic/arm_dynarmic_64.cpp

@ -269,9 +269,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
config.wall_clock_cntpct = m_uses_wall_clock;
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
// Solaris doesn't support kPageSize >= 512MiB
// Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);
#endif
// Allow memory fault handling to work
if (m_system.DebuggerEnabled()) {

13
src/core/core.cpp

@ -297,6 +297,9 @@ struct System::Impl {
std::string vendor = gpu_core->Renderer().GetDeviceVendor();
LOG_INFO(Core, "GPU Vendor: {}", vendor);
// Reset all per-game flags
Settings::values.use_squashed_iterated_blend = false;
// Insert PC overrides here
#ifdef ANDROID
@ -322,6 +325,13 @@ struct System::Impl {
#endif
// Ninja Gaiden Ragebound
constexpr u64 ngr = 0x0100781020710000ULL;
if (programId == ngr) {
LOG_INFO(Core, "Enabling game specifc override: use_squashed_iterated_blend");
Settings::values.use_squashed_iterated_blend = true;
}
}
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
@ -425,6 +435,9 @@ struct System::Impl {
void ShutdownMainProcess() {
SetShuttingDown(true);
// Reset per-game flags
Settings::values.use_squashed_iterated_blend = false;
is_powered_on = false;
exit_locked = false;
exit_requested = false;

14
src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp

@ -168,7 +168,15 @@ NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) {
NvResult nvhost_gpu::SetChannelPriority(IoctlChannelSetPriority& params) {
channel_priority = params.priority;
LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
LOG_INFO(Service_NVDRV, "called, priority={:X}", channel_priority);
switch (static_cast<ChannelPriority>(channel_priority)) {
case ChannelPriority::Low: channel_timeslice = 1300; break;
case ChannelPriority::Medium: channel_timeslice = 2600; break;
case ChannelPriority::High: channel_timeslice = 5200; break;
default : return NvResult::BadParameter;
}
return NvResult::Success;
}
@ -427,6 +435,10 @@ NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) {
NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) {
LOG_INFO(Service_NVDRV, "called, timeslice={:#X}", params.timeslice);
if (params.timeslice < 1000 || params.timeslice > 5000) {
return NvResult::BadParameter;
}
channel_timeslice = params.timeslice;
return NvResult::Success;

6
src/core/hle/service/nvdrv/devices/nvhost_gpu.h

@ -66,6 +66,12 @@ private:
CtxChannelGPFIFO = 0xB06F,
};
enum class ChannelPriority : u32 {
Low = 0x32,
Medium = 0x64,
High = 0x96,
};
struct IoctlSetNvmapFD {
s32_le nvmap_fd{};
};

5
src/dynarmic/CMakeLists.txt

@ -81,11 +81,6 @@ if (MSVC)
/bigobj # Increase number of sections in .obj files
/DNOMINMAX)
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
if (CXX_CLANG)
list(APPEND DYNARMIC_CXX_FLAGS
-Qunused-arguments

7
src/dynarmic/src/dynarmic/backend/x64/a64_interface.cpp

@ -80,16 +80,15 @@ public:
};
// TODO: Check code alignment
const CodePtr aligned_code_ptr = CodePtr((uintptr_t(GetCurrentBlock()) + 15) & ~uintptr_t(15));
const CodePtr current_code_ptr = [this, aligned_code_ptr] {
const CodePtr current_code_ptr = [this] {
// RSB optimization
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
jit_state.rsb_ptr = new_rsb_ptr;
return CodePtr(jit_state.rsb_codeptrs[new_rsb_ptr]);
}
return aligned_code_ptr;
//return GetCurrentBlock();
return CodePtr((uintptr_t(GetCurrentBlock()) + 15) & ~uintptr_t(15));
}();
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);

2
src/shader_recompiler/CMakeLists.txt

@ -81,14 +81,12 @@ add_library(shader_recompiler STATIC
environment.h
exception.h
frontend/ir/abstract_syntax_list.h
frontend/ir/attribute.cpp
frontend/ir/attribute.h
frontend/ir/basic_block.cpp
frontend/ir/basic_block.h
frontend/ir/breadth_first_search.h
frontend/ir/condition.cpp
frontend/ir/condition.h
frontend/ir/flow_test.cpp
frontend/ir/flow_test.h
frontend/ir/ir_emitter.cpp
frontend/ir/ir_emitter.h

459
src/shader_recompiler/frontend/ir/attribute.cpp

@ -1,459 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/ranges.h>
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/attribute.h"
namespace Shader::IR {
bool IsGeneric(Attribute attribute) noexcept {
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
}
u32 GenericAttributeIndex(Attribute attribute) {
if (!IsGeneric(attribute)) {
throw InvalidArgument("Attribute is not generic {}", attribute);
}
return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u;
}
u32 GenericAttributeElement(Attribute attribute) {
if (!IsGeneric(attribute)) {
throw InvalidArgument("Attribute is not generic {}", attribute);
}
return static_cast<u32>(attribute) % 4;
}
std::string NameOf(Attribute attribute) {
switch (attribute) {
case Attribute::PrimitiveId:
return "PrimitiveId";
case Attribute::Layer:
return "Layer";
case Attribute::ViewportIndex:
return "ViewportIndex";
case Attribute::PointSize:
return "PointSize";
case Attribute::PositionX:
return "Position.X";
case Attribute::PositionY:
return "Position.Y";
case Attribute::PositionZ:
return "Position.Z";
case Attribute::PositionW:
return "Position.W";
case Attribute::Generic0X:
return "Generic[0].X";
case Attribute::Generic0Y:
return "Generic[0].Y";
case Attribute::Generic0Z:
return "Generic[0].Z";
case Attribute::Generic0W:
return "Generic[0].W";
case Attribute::Generic1X:
return "Generic[1].X";
case Attribute::Generic1Y:
return "Generic[1].Y";
case Attribute::Generic1Z:
return "Generic[1].Z";
case Attribute::Generic1W:
return "Generic[1].W";
case Attribute::Generic2X:
return "Generic[2].X";
case Attribute::Generic2Y:
return "Generic[2].Y";
case Attribute::Generic2Z:
return "Generic[2].Z";
case Attribute::Generic2W:
return "Generic[2].W";
case Attribute::Generic3X:
return "Generic[3].X";
case Attribute::Generic3Y:
return "Generic[3].Y";
case Attribute::Generic3Z:
return "Generic[3].Z";
case Attribute::Generic3W:
return "Generic[3].W";
case Attribute::Generic4X:
return "Generic[4].X";
case Attribute::Generic4Y:
return "Generic[4].Y";
case Attribute::Generic4Z:
return "Generic[4].Z";
case Attribute::Generic4W:
return "Generic[4].W";
case Attribute::Generic5X:
return "Generic[5].X";
case Attribute::Generic5Y:
return "Generic[5].Y";
case Attribute::Generic5Z:
return "Generic[5].Z";
case Attribute::Generic5W:
return "Generic[5].W";
case Attribute::Generic6X:
return "Generic[6].X";
case Attribute::Generic6Y:
return "Generic[6].Y";
case Attribute::Generic6Z:
return "Generic[6].Z";
case Attribute::Generic6W:
return "Generic[6].W";
case Attribute::Generic7X:
return "Generic[7].X";
case Attribute::Generic7Y:
return "Generic[7].Y";
case Attribute::Generic7Z:
return "Generic[7].Z";
case Attribute::Generic7W:
return "Generic[7].W";
case Attribute::Generic8X:
return "Generic[8].X";
case Attribute::Generic8Y:
return "Generic[8].Y";
case Attribute::Generic8Z:
return "Generic[8].Z";
case Attribute::Generic8W:
return "Generic[8].W";
case Attribute::Generic9X:
return "Generic[9].X";
case Attribute::Generic9Y:
return "Generic[9].Y";
case Attribute::Generic9Z:
return "Generic[9].Z";
case Attribute::Generic9W:
return "Generic[9].W";
case Attribute::Generic10X:
return "Generic[10].X";
case Attribute::Generic10Y:
return "Generic[10].Y";
case Attribute::Generic10Z:
return "Generic[10].Z";
case Attribute::Generic10W:
return "Generic[10].W";
case Attribute::Generic11X:
return "Generic[11].X";
case Attribute::Generic11Y:
return "Generic[11].Y";
case Attribute::Generic11Z:
return "Generic[11].Z";
case Attribute::Generic11W:
return "Generic[11].W";
case Attribute::Generic12X:
return "Generic[12].X";
case Attribute::Generic12Y:
return "Generic[12].Y";
case Attribute::Generic12Z:
return "Generic[12].Z";
case Attribute::Generic12W:
return "Generic[12].W";
case Attribute::Generic13X:
return "Generic[13].X";
case Attribute::Generic13Y:
return "Generic[13].Y";
case Attribute::Generic13Z:
return "Generic[13].Z";
case Attribute::Generic13W:
return "Generic[13].W";
case Attribute::Generic14X:
return "Generic[14].X";
case Attribute::Generic14Y:
return "Generic[14].Y";
case Attribute::Generic14Z:
return "Generic[14].Z";
case Attribute::Generic14W:
return "Generic[14].W";
case Attribute::Generic15X:
return "Generic[15].X";
case Attribute::Generic15Y:
return "Generic[15].Y";
case Attribute::Generic15Z:
return "Generic[15].Z";
case Attribute::Generic15W:
return "Generic[15].W";
case Attribute::Generic16X:
return "Generic[16].X";
case Attribute::Generic16Y:
return "Generic[16].Y";
case Attribute::Generic16Z:
return "Generic[16].Z";
case Attribute::Generic16W:
return "Generic[16].W";
case Attribute::Generic17X:
return "Generic[17].X";
case Attribute::Generic17Y:
return "Generic[17].Y";
case Attribute::Generic17Z:
return "Generic[17].Z";
case Attribute::Generic17W:
return "Generic[17].W";
case Attribute::Generic18X:
return "Generic[18].X";
case Attribute::Generic18Y:
return "Generic[18].Y";
case Attribute::Generic18Z:
return "Generic[18].Z";
case Attribute::Generic18W:
return "Generic[18].W";
case Attribute::Generic19X:
return "Generic[19].X";
case Attribute::Generic19Y:
return "Generic[19].Y";
case Attribute::Generic19Z:
return "Generic[19].Z";
case Attribute::Generic19W:
return "Generic[19].W";
case Attribute::Generic20X:
return "Generic[20].X";
case Attribute::Generic20Y:
return "Generic[20].Y";
case Attribute::Generic20Z:
return "Generic[20].Z";
case Attribute::Generic20W:
return "Generic[20].W";
case Attribute::Generic21X:
return "Generic[21].X";
case Attribute::Generic21Y:
return "Generic[21].Y";
case Attribute::Generic21Z:
return "Generic[21].Z";
case Attribute::Generic21W:
return "Generic[21].W";
case Attribute::Generic22X:
return "Generic[22].X";
case Attribute::Generic22Y:
return "Generic[22].Y";
case Attribute::Generic22Z:
return "Generic[22].Z";
case Attribute::Generic22W:
return "Generic[22].W";
case Attribute::Generic23X:
return "Generic[23].X";
case Attribute::Generic23Y:
return "Generic[23].Y";
case Attribute::Generic23Z:
return "Generic[23].Z";
case Attribute::Generic23W:
return "Generic[23].W";
case Attribute::Generic24X:
return "Generic[24].X";
case Attribute::Generic24Y:
return "Generic[24].Y";
case Attribute::Generic24Z:
return "Generic[24].Z";
case Attribute::Generic24W:
return "Generic[24].W";
case Attribute::Generic25X:
return "Generic[25].X";
case Attribute::Generic25Y:
return "Generic[25].Y";
case Attribute::Generic25Z:
return "Generic[25].Z";
case Attribute::Generic25W:
return "Generic[25].W";
case Attribute::Generic26X:
return "Generic[26].X";
case Attribute::Generic26Y:
return "Generic[26].Y";
case Attribute::Generic26Z:
return "Generic[26].Z";
case Attribute::Generic26W:
return "Generic[26].W";
case Attribute::Generic27X:
return "Generic[27].X";
case Attribute::Generic27Y:
return "Generic[27].Y";
case Attribute::Generic27Z:
return "Generic[27].Z";
case Attribute::Generic27W:
return "Generic[27].W";
case Attribute::Generic28X:
return "Generic[28].X";
case Attribute::Generic28Y:
return "Generic[28].Y";
case Attribute::Generic28Z:
return "Generic[28].Z";
case Attribute::Generic28W:
return "Generic[28].W";
case Attribute::Generic29X:
return "Generic[29].X";
case Attribute::Generic29Y:
return "Generic[29].Y";
case Attribute::Generic29Z:
return "Generic[29].Z";
case Attribute::Generic29W:
return "Generic[29].W";
case Attribute::Generic30X:
return "Generic[30].X";
case Attribute::Generic30Y:
return "Generic[30].Y";
case Attribute::Generic30Z:
return "Generic[30].Z";
case Attribute::Generic30W:
return "Generic[30].W";
case Attribute::Generic31X:
return "Generic[31].X";
case Attribute::Generic31Y:
return "Generic[31].Y";
case Attribute::Generic31Z:
return "Generic[31].Z";
case Attribute::Generic31W:
return "Generic[31].W";
case Attribute::ColorFrontDiffuseR:
return "ColorFrontDiffuse.R";
case Attribute::ColorFrontDiffuseG:
return "ColorFrontDiffuse.G";
case Attribute::ColorFrontDiffuseB:
return "ColorFrontDiffuse.B";
case Attribute::ColorFrontDiffuseA:
return "ColorFrontDiffuse.A";
case Attribute::ColorFrontSpecularR:
return "ColorFrontSpecular.R";
case Attribute::ColorFrontSpecularG:
return "ColorFrontSpecular.G";
case Attribute::ColorFrontSpecularB:
return "ColorFrontSpecular.B";
case Attribute::ColorFrontSpecularA:
return "ColorFrontSpecular.A";
case Attribute::ColorBackDiffuseR:
return "ColorBackDiffuse.R";
case Attribute::ColorBackDiffuseG:
return "ColorBackDiffuse.G";
case Attribute::ColorBackDiffuseB:
return "ColorBackDiffuse.B";
case Attribute::ColorBackDiffuseA:
return "ColorBackDiffuse.A";
case Attribute::ColorBackSpecularR:
return "ColorBackSpecular.R";
case Attribute::ColorBackSpecularG:
return "ColorBackSpecular.G";
case Attribute::ColorBackSpecularB:
return "ColorBackSpecular.B";
case Attribute::ColorBackSpecularA:
return "ColorBackSpecular.A";
case Attribute::ClipDistance0:
return "ClipDistance[0]";
case Attribute::ClipDistance1:
return "ClipDistance[1]";
case Attribute::ClipDistance2:
return "ClipDistance[2]";
case Attribute::ClipDistance3:
return "ClipDistance[3]";
case Attribute::ClipDistance4:
return "ClipDistance[4]";
case Attribute::ClipDistance5:
return "ClipDistance[5]";
case Attribute::ClipDistance6:
return "ClipDistance[6]";
case Attribute::ClipDistance7:
return "ClipDistance[7]";
case Attribute::PointSpriteS:
return "PointSprite.S";
case Attribute::PointSpriteT:
return "PointSprite.T";
case Attribute::FogCoordinate:
return "FogCoordinate";
case Attribute::TessellationEvaluationPointU:
return "TessellationEvaluationPoint.U";
case Attribute::TessellationEvaluationPointV:
return "TessellationEvaluationPoint.V";
case Attribute::InstanceId:
return "InstanceId";
case Attribute::VertexId:
return "VertexId";
case Attribute::FixedFncTexture0S:
return "FixedFncTexture[0].S";
case Attribute::FixedFncTexture0T:
return "FixedFncTexture[0].T";
case Attribute::FixedFncTexture0R:
return "FixedFncTexture[0].R";
case Attribute::FixedFncTexture0Q:
return "FixedFncTexture[0].Q";
case Attribute::FixedFncTexture1S:
return "FixedFncTexture[1].S";
case Attribute::FixedFncTexture1T:
return "FixedFncTexture[1].T";
case Attribute::FixedFncTexture1R:
return "FixedFncTexture[1].R";
case Attribute::FixedFncTexture1Q:
return "FixedFncTexture[1].Q";
case Attribute::FixedFncTexture2S:
return "FixedFncTexture[2].S";
case Attribute::FixedFncTexture2T:
return "FixedFncTexture[2].T";
case Attribute::FixedFncTexture2R:
return "FixedFncTexture[2].R";
case Attribute::FixedFncTexture2Q:
return "FixedFncTexture[2].Q";
case Attribute::FixedFncTexture3S:
return "FixedFncTexture[3].S";
case Attribute::FixedFncTexture3T:
return "FixedFncTexture[3].T";
case Attribute::FixedFncTexture3R:
return "FixedFncTexture[3].R";
case Attribute::FixedFncTexture3Q:
return "FixedFncTexture[3].Q";
case Attribute::FixedFncTexture4S:
return "FixedFncTexture[4].S";
case Attribute::FixedFncTexture4T:
return "FixedFncTexture[4].T";
case Attribute::FixedFncTexture4R:
return "FixedFncTexture[4].R";
case Attribute::FixedFncTexture4Q:
return "FixedFncTexture[4].Q";
case Attribute::FixedFncTexture5S:
return "FixedFncTexture[5].S";
case Attribute::FixedFncTexture5T:
return "FixedFncTexture[5].T";
case Attribute::FixedFncTexture5R:
return "FixedFncTexture[5].R";
case Attribute::FixedFncTexture5Q:
return "FixedFncTexture[5].Q";
case Attribute::FixedFncTexture6S:
return "FixedFncTexture[6].S";
case Attribute::FixedFncTexture6T:
return "FixedFncTexture[6].T";
case Attribute::FixedFncTexture6R:
return "FixedFncTexture[6].R";
case Attribute::FixedFncTexture6Q:
return "FixedFncTexture[6].Q";
case Attribute::FixedFncTexture7S:
return "FixedFncTexture[7].S";
case Attribute::FixedFncTexture7T:
return "FixedFncTexture[7].T";
case Attribute::FixedFncTexture7R:
return "FixedFncTexture[7].R";
case Attribute::FixedFncTexture7Q:
return "FixedFncTexture[7].Q";
case Attribute::FixedFncTexture8S:
return "FixedFncTexture[8].S";
case Attribute::FixedFncTexture8T:
return "FixedFncTexture[8].T";
case Attribute::FixedFncTexture8R:
return "FixedFncTexture[8].R";
case Attribute::FixedFncTexture8Q:
return "FixedFncTexture[8].Q";
case Attribute::FixedFncTexture9S:
return "FixedFncTexture[9].S";
case Attribute::FixedFncTexture9T:
return "FixedFncTexture[9].T";
case Attribute::FixedFncTexture9R:
return "FixedFncTexture[9].R";
case Attribute::FixedFncTexture9Q:
return "FixedFncTexture[9].Q";
case Attribute::ViewportMask:
return "ViewportMask";
case Attribute::FrontFace:
return "FrontFace";
case Attribute::BaseInstance:
return "BaseInstance";
case Attribute::BaseVertex:
return "BaseVertex";
case Attribute::DrawID:
return "DrawID";
}
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
}
} // namespace Shader::IR

464
src/shader_recompiler/frontend/ir/attribute.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -6,240 +9,261 @@
#include <fmt/ranges.h>
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
namespace Shader::IR {
enum class Attribute : u64 {
PrimitiveId = 24,
Layer = 25,
ViewportIndex = 26,
PointSize = 27,
PositionX = 28,
PositionY = 29,
PositionZ = 30,
PositionW = 31,
Generic0X = 32,
Generic0Y = 33,
Generic0Z = 34,
Generic0W = 35,
Generic1X = 36,
Generic1Y = 37,
Generic1Z = 38,
Generic1W = 39,
Generic2X = 40,
Generic2Y = 41,
Generic2Z = 42,
Generic2W = 43,
Generic3X = 44,
Generic3Y = 45,
Generic3Z = 46,
Generic3W = 47,
Generic4X = 48,
Generic4Y = 49,
Generic4Z = 50,
Generic4W = 51,
Generic5X = 52,
Generic5Y = 53,
Generic5Z = 54,
Generic5W = 55,
Generic6X = 56,
Generic6Y = 57,
Generic6Z = 58,
Generic6W = 59,
Generic7X = 60,
Generic7Y = 61,
Generic7Z = 62,
Generic7W = 63,
Generic8X = 64,
Generic8Y = 65,
Generic8Z = 66,
Generic8W = 67,
Generic9X = 68,
Generic9Y = 69,
Generic9Z = 70,
Generic9W = 71,
Generic10X = 72,
Generic10Y = 73,
Generic10Z = 74,
Generic10W = 75,
Generic11X = 76,
Generic11Y = 77,
Generic11Z = 78,
Generic11W = 79,
Generic12X = 80,
Generic12Y = 81,
Generic12Z = 82,
Generic12W = 83,
Generic13X = 84,
Generic13Y = 85,
Generic13Z = 86,
Generic13W = 87,
Generic14X = 88,
Generic14Y = 89,
Generic14Z = 90,
Generic14W = 91,
Generic15X = 92,
Generic15Y = 93,
Generic15Z = 94,
Generic15W = 95,
Generic16X = 96,
Generic16Y = 97,
Generic16Z = 98,
Generic16W = 99,
Generic17X = 100,
Generic17Y = 101,
Generic17Z = 102,
Generic17W = 103,
Generic18X = 104,
Generic18Y = 105,
Generic18Z = 106,
Generic18W = 107,
Generic19X = 108,
Generic19Y = 109,
Generic19Z = 110,
Generic19W = 111,
Generic20X = 112,
Generic20Y = 113,
Generic20Z = 114,
Generic20W = 115,
Generic21X = 116,
Generic21Y = 117,
Generic21Z = 118,
Generic21W = 119,
Generic22X = 120,
Generic22Y = 121,
Generic22Z = 122,
Generic22W = 123,
Generic23X = 124,
Generic23Y = 125,
Generic23Z = 126,
Generic23W = 127,
Generic24X = 128,
Generic24Y = 129,
Generic24Z = 130,
Generic24W = 131,
Generic25X = 132,
Generic25Y = 133,
Generic25Z = 134,
Generic25W = 135,
Generic26X = 136,
Generic26Y = 137,
Generic26Z = 138,
Generic26W = 139,
Generic27X = 140,
Generic27Y = 141,
Generic27Z = 142,
Generic27W = 143,
Generic28X = 144,
Generic28Y = 145,
Generic28Z = 146,
Generic28W = 147,
Generic29X = 148,
Generic29Y = 149,
Generic29Z = 150,
Generic29W = 151,
Generic30X = 152,
Generic30Y = 153,
Generic30Z = 154,
Generic30W = 155,
Generic31X = 156,
Generic31Y = 157,
Generic31Z = 158,
Generic31W = 159,
ColorFrontDiffuseR = 160,
ColorFrontDiffuseG = 161,
ColorFrontDiffuseB = 162,
ColorFrontDiffuseA = 163,
ColorFrontSpecularR = 164,
ColorFrontSpecularG = 165,
ColorFrontSpecularB = 166,
ColorFrontSpecularA = 167,
ColorBackDiffuseR = 168,
ColorBackDiffuseG = 169,
ColorBackDiffuseB = 170,
ColorBackDiffuseA = 171,
ColorBackSpecularR = 172,
ColorBackSpecularG = 173,
ColorBackSpecularB = 174,
ColorBackSpecularA = 175,
ClipDistance0 = 176,
ClipDistance1 = 177,
ClipDistance2 = 178,
ClipDistance3 = 179,
ClipDistance4 = 180,
ClipDistance5 = 181,
ClipDistance6 = 182,
ClipDistance7 = 183,
PointSpriteS = 184,
PointSpriteT = 185,
FogCoordinate = 186,
TessellationEvaluationPointU = 188,
TessellationEvaluationPointV = 189,
InstanceId = 190,
VertexId = 191,
FixedFncTexture0S = 192,
FixedFncTexture0T = 193,
FixedFncTexture0R = 194,
FixedFncTexture0Q = 195,
FixedFncTexture1S = 196,
FixedFncTexture1T = 197,
FixedFncTexture1R = 198,
FixedFncTexture1Q = 199,
FixedFncTexture2S = 200,
FixedFncTexture2T = 201,
FixedFncTexture2R = 202,
FixedFncTexture2Q = 203,
FixedFncTexture3S = 204,
FixedFncTexture3T = 205,
FixedFncTexture3R = 206,
FixedFncTexture3Q = 207,
FixedFncTexture4S = 208,
FixedFncTexture4T = 209,
FixedFncTexture4R = 210,
FixedFncTexture4Q = 211,
FixedFncTexture5S = 212,
FixedFncTexture5T = 213,
FixedFncTexture5R = 214,
FixedFncTexture5Q = 215,
FixedFncTexture6S = 216,
FixedFncTexture6T = 217,
FixedFncTexture6R = 218,
FixedFncTexture6Q = 219,
FixedFncTexture7S = 220,
FixedFncTexture7T = 221,
FixedFncTexture7R = 222,
FixedFncTexture7Q = 223,
FixedFncTexture8S = 224,
FixedFncTexture8T = 225,
FixedFncTexture8R = 226,
FixedFncTexture8Q = 227,
FixedFncTexture9S = 228,
FixedFncTexture9T = 229,
FixedFncTexture9R = 230,
FixedFncTexture9Q = 231,
ViewportMask = 232,
FrontFace = 255,
// Implementation attributes
BaseInstance = 256,
BaseVertex = 257,
DrawID = 258,
#define SRIR_ATTRIBUTE_LIST \
SRIR_ATTRIBUTE_ELEM(PrimitiveId, 24) \
SRIR_ATTRIBUTE_ELEM(Layer, 25) \
SRIR_ATTRIBUTE_ELEM(ViewportIndex, 26) \
SRIR_ATTRIBUTE_ELEM(PointSize, 27) \
SRIR_ATTRIBUTE_ELEM(PositionX, 28) \
SRIR_ATTRIBUTE_ELEM(PositionY, 29) \
SRIR_ATTRIBUTE_ELEM(PositionZ, 30) \
SRIR_ATTRIBUTE_ELEM(PositionW, 31) \
SRIR_ATTRIBUTE_ELEM(Generic0X, 32) \
SRIR_ATTRIBUTE_ELEM(Generic0Y, 33) \
SRIR_ATTRIBUTE_ELEM(Generic0Z, 34) \
SRIR_ATTRIBUTE_ELEM(Generic0W, 35) \
SRIR_ATTRIBUTE_ELEM(Generic1X, 36) \
SRIR_ATTRIBUTE_ELEM(Generic1Y, 37) \
SRIR_ATTRIBUTE_ELEM(Generic1Z, 38) \
SRIR_ATTRIBUTE_ELEM(Generic1W, 39) \
SRIR_ATTRIBUTE_ELEM(Generic2X, 40) \
SRIR_ATTRIBUTE_ELEM(Generic2Y, 41) \
SRIR_ATTRIBUTE_ELEM(Generic2Z, 42) \
SRIR_ATTRIBUTE_ELEM(Generic2W, 43) \
SRIR_ATTRIBUTE_ELEM(Generic3X, 44) \
SRIR_ATTRIBUTE_ELEM(Generic3Y, 45) \
SRIR_ATTRIBUTE_ELEM(Generic3Z, 46) \
SRIR_ATTRIBUTE_ELEM(Generic3W, 47) \
SRIR_ATTRIBUTE_ELEM(Generic4X, 48) \
SRIR_ATTRIBUTE_ELEM(Generic4Y, 49) \
SRIR_ATTRIBUTE_ELEM(Generic4Z, 50) \
SRIR_ATTRIBUTE_ELEM(Generic4W, 51) \
SRIR_ATTRIBUTE_ELEM(Generic5X, 52) \
SRIR_ATTRIBUTE_ELEM(Generic5Y, 53) \
SRIR_ATTRIBUTE_ELEM(Generic5Z, 54) \
SRIR_ATTRIBUTE_ELEM(Generic5W, 55) \
SRIR_ATTRIBUTE_ELEM(Generic6X, 56) \
SRIR_ATTRIBUTE_ELEM(Generic6Y, 57) \
SRIR_ATTRIBUTE_ELEM(Generic6Z, 58) \
SRIR_ATTRIBUTE_ELEM(Generic6W, 59) \
SRIR_ATTRIBUTE_ELEM(Generic7X, 60) \
SRIR_ATTRIBUTE_ELEM(Generic7Y, 61) \
SRIR_ATTRIBUTE_ELEM(Generic7Z, 62) \
SRIR_ATTRIBUTE_ELEM(Generic7W, 63) \
SRIR_ATTRIBUTE_ELEM(Generic8X, 64) \
SRIR_ATTRIBUTE_ELEM(Generic8Y, 65) \
SRIR_ATTRIBUTE_ELEM(Generic8Z, 66) \
SRIR_ATTRIBUTE_ELEM(Generic8W, 67) \
SRIR_ATTRIBUTE_ELEM(Generic9X, 68) \
SRIR_ATTRIBUTE_ELEM(Generic9Y, 69) \
SRIR_ATTRIBUTE_ELEM(Generic9Z, 70) \
SRIR_ATTRIBUTE_ELEM(Generic9W, 71) \
SRIR_ATTRIBUTE_ELEM(Generic10X, 72) \
SRIR_ATTRIBUTE_ELEM(Generic10Y, 73) \
SRIR_ATTRIBUTE_ELEM(Generic10Z, 74) \
SRIR_ATTRIBUTE_ELEM(Generic10W, 75) \
SRIR_ATTRIBUTE_ELEM(Generic11X, 76) \
SRIR_ATTRIBUTE_ELEM(Generic11Y, 77) \
SRIR_ATTRIBUTE_ELEM(Generic11Z, 78) \
SRIR_ATTRIBUTE_ELEM(Generic11W, 79) \
SRIR_ATTRIBUTE_ELEM(Generic12X, 80) \
SRIR_ATTRIBUTE_ELEM(Generic12Y, 81) \
SRIR_ATTRIBUTE_ELEM(Generic12Z, 82) \
SRIR_ATTRIBUTE_ELEM(Generic12W, 83) \
SRIR_ATTRIBUTE_ELEM(Generic13X, 84) \
SRIR_ATTRIBUTE_ELEM(Generic13Y, 85) \
SRIR_ATTRIBUTE_ELEM(Generic13Z, 86) \
SRIR_ATTRIBUTE_ELEM(Generic13W, 87) \
SRIR_ATTRIBUTE_ELEM(Generic14X, 88) \
SRIR_ATTRIBUTE_ELEM(Generic14Y, 89) \
SRIR_ATTRIBUTE_ELEM(Generic14Z, 90) \
SRIR_ATTRIBUTE_ELEM(Generic14W, 91) \
SRIR_ATTRIBUTE_ELEM(Generic15X, 92) \
SRIR_ATTRIBUTE_ELEM(Generic15Y, 93) \
SRIR_ATTRIBUTE_ELEM(Generic15Z, 94) \
SRIR_ATTRIBUTE_ELEM(Generic15W, 95) \
SRIR_ATTRIBUTE_ELEM(Generic16X, 96) \
SRIR_ATTRIBUTE_ELEM(Generic16Y, 97) \
SRIR_ATTRIBUTE_ELEM(Generic16Z, 98) \
SRIR_ATTRIBUTE_ELEM(Generic16W, 99) \
SRIR_ATTRIBUTE_ELEM(Generic17X, 100) \
SRIR_ATTRIBUTE_ELEM(Generic17Y, 101) \
SRIR_ATTRIBUTE_ELEM(Generic17Z, 102) \
SRIR_ATTRIBUTE_ELEM(Generic17W, 103) \
SRIR_ATTRIBUTE_ELEM(Generic18X, 104) \
SRIR_ATTRIBUTE_ELEM(Generic18Y, 105) \
SRIR_ATTRIBUTE_ELEM(Generic18Z, 106) \
SRIR_ATTRIBUTE_ELEM(Generic18W, 107) \
SRIR_ATTRIBUTE_ELEM(Generic19X, 108) \
SRIR_ATTRIBUTE_ELEM(Generic19Y, 109) \
SRIR_ATTRIBUTE_ELEM(Generic19Z, 110) \
SRIR_ATTRIBUTE_ELEM(Generic19W, 111) \
SRIR_ATTRIBUTE_ELEM(Generic20X, 112) \
SRIR_ATTRIBUTE_ELEM(Generic20Y, 113) \
SRIR_ATTRIBUTE_ELEM(Generic20Z, 114) \
SRIR_ATTRIBUTE_ELEM(Generic20W, 115) \
SRIR_ATTRIBUTE_ELEM(Generic21X, 116) \
SRIR_ATTRIBUTE_ELEM(Generic21Y, 117) \
SRIR_ATTRIBUTE_ELEM(Generic21Z, 118) \
SRIR_ATTRIBUTE_ELEM(Generic21W, 119) \
SRIR_ATTRIBUTE_ELEM(Generic22X, 120) \
SRIR_ATTRIBUTE_ELEM(Generic22Y, 121) \
SRIR_ATTRIBUTE_ELEM(Generic22Z, 122) \
SRIR_ATTRIBUTE_ELEM(Generic22W, 123) \
SRIR_ATTRIBUTE_ELEM(Generic23X, 124) \
SRIR_ATTRIBUTE_ELEM(Generic23Y, 125) \
SRIR_ATTRIBUTE_ELEM(Generic23Z, 126) \
SRIR_ATTRIBUTE_ELEM(Generic23W, 127) \
SRIR_ATTRIBUTE_ELEM(Generic24X, 128) \
SRIR_ATTRIBUTE_ELEM(Generic24Y, 129) \
SRIR_ATTRIBUTE_ELEM(Generic24Z, 130) \
SRIR_ATTRIBUTE_ELEM(Generic24W, 131) \
SRIR_ATTRIBUTE_ELEM(Generic25X, 132) \
SRIR_ATTRIBUTE_ELEM(Generic25Y, 133) \
SRIR_ATTRIBUTE_ELEM(Generic25Z, 134) \
SRIR_ATTRIBUTE_ELEM(Generic25W, 135) \
SRIR_ATTRIBUTE_ELEM(Generic26X, 136) \
SRIR_ATTRIBUTE_ELEM(Generic26Y, 137) \
SRIR_ATTRIBUTE_ELEM(Generic26Z, 138) \
SRIR_ATTRIBUTE_ELEM(Generic26W, 139) \
SRIR_ATTRIBUTE_ELEM(Generic27X, 140) \
SRIR_ATTRIBUTE_ELEM(Generic27Y, 141) \
SRIR_ATTRIBUTE_ELEM(Generic27Z, 142) \
SRIR_ATTRIBUTE_ELEM(Generic27W, 143) \
SRIR_ATTRIBUTE_ELEM(Generic28X, 144) \
SRIR_ATTRIBUTE_ELEM(Generic28Y, 145) \
SRIR_ATTRIBUTE_ELEM(Generic28Z, 146) \
SRIR_ATTRIBUTE_ELEM(Generic28W, 147) \
SRIR_ATTRIBUTE_ELEM(Generic29X, 148) \
SRIR_ATTRIBUTE_ELEM(Generic29Y, 149) \
SRIR_ATTRIBUTE_ELEM(Generic29Z, 150) \
SRIR_ATTRIBUTE_ELEM(Generic29W, 151) \
SRIR_ATTRIBUTE_ELEM(Generic30X, 152) \
SRIR_ATTRIBUTE_ELEM(Generic30Y, 153) \
SRIR_ATTRIBUTE_ELEM(Generic30Z, 154) \
SRIR_ATTRIBUTE_ELEM(Generic30W, 155) \
SRIR_ATTRIBUTE_ELEM(Generic31X, 156) \
SRIR_ATTRIBUTE_ELEM(Generic31Y, 157) \
SRIR_ATTRIBUTE_ELEM(Generic31Z, 158) \
SRIR_ATTRIBUTE_ELEM(Generic31W, 159) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseR, 160) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseG, 161) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseB, 162) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseA, 163) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularR, 164) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularG, 165) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularB, 166) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularA, 167) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseR, 168) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseG, 169) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseB, 170) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseA, 171) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularR, 172) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularG, 173) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularB, 174) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularA, 175) \
SRIR_ATTRIBUTE_ELEM(ClipDistance0, 176) \
SRIR_ATTRIBUTE_ELEM(ClipDistance1, 177) \
SRIR_ATTRIBUTE_ELEM(ClipDistance2, 178) \
SRIR_ATTRIBUTE_ELEM(ClipDistance3, 179) \
SRIR_ATTRIBUTE_ELEM(ClipDistance4, 180) \
SRIR_ATTRIBUTE_ELEM(ClipDistance5, 181) \
SRIR_ATTRIBUTE_ELEM(ClipDistance6, 182) \
SRIR_ATTRIBUTE_ELEM(ClipDistance7, 183) \
SRIR_ATTRIBUTE_ELEM(PointSpriteS, 184) \
SRIR_ATTRIBUTE_ELEM(PointSpriteT, 185) \
SRIR_ATTRIBUTE_ELEM(FogCoordinate, 186) \
SRIR_ATTRIBUTE_ELEM(TessellationEvaluationPointU, 188) \
SRIR_ATTRIBUTE_ELEM(TessellationEvaluationPointV, 189) \
SRIR_ATTRIBUTE_ELEM(InstanceId, 190) \
SRIR_ATTRIBUTE_ELEM(VertexId, 191) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0S, 192) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0T, 193) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0R, 194) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0Q, 195) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1S, 196) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1T, 197) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1R, 198) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1Q, 199) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2S, 200) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2T, 201) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2R, 202) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2Q, 203) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3S, 204) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3T, 205) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3R, 206) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3Q, 207) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4S, 208) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4T, 209) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4R, 210) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4Q, 211) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5S, 212) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5T, 213) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5R, 214) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5Q, 215) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6S, 216) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6T, 217) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6R, 218) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6Q, 219) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7S, 220) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7T, 221) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7R, 222) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7Q, 223) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8S, 224) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8T, 225) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8R, 226) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8Q, 227) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9S, 228) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9T, 229) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9R, 230) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9Q, 231) \
SRIR_ATTRIBUTE_ELEM(ViewportMask, 232) \
SRIR_ATTRIBUTE_ELEM(FrontFace, 255) \
/* Implementation attributes */ \
SRIR_ATTRIBUTE_ELEM(BaseInstance, 256) \
SRIR_ATTRIBUTE_ELEM(BaseVertex, 257) \
SRIR_ATTRIBUTE_ELEM(DrawID, 258)
#define SRIR_ATTRIBUTE_ELEM(n, v) n = v,
SRIR_ATTRIBUTE_LIST
#undef SRIR_ATTRIBUTE_ELEM
};
constexpr size_t NUM_GENERICS = 32;
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
[[nodiscard]] inline bool IsGeneric(Attribute attribute) noexcept {
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
}
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
[[nodiscard]] inline u32 GenericAttributeIndex(Attribute attribute) {
if (!IsGeneric(attribute))
throw InvalidArgument("Attribute is not generic {}", attribute);
return (u32(attribute) - u32(Attribute::Generic0X)) / 4u;
}
[[nodiscard]] u32 GenericAttributeElement(Attribute attribute);
[[nodiscard]] inline u32 GenericAttributeElement(Attribute attribute) {
if (!IsGeneric(attribute))
throw InvalidArgument("Attribute is not generic {}", attribute);
return u32(attribute) % 4;
}
[[nodiscard]] std::string NameOf(Attribute attribute);
[[nodiscard]] inline std::string NameOf(Attribute attribute) {
switch (attribute) {
#define SRIR_ATTRIBUTE_ELEM(n, v) case Attribute::n: return #n;
SRIR_ATTRIBUTE_LIST
#undef SRIR_ATTRIBUTE_ELEM
default:
return fmt::format("<reserved attribute {}>", int(attribute));
}
}
[[nodiscard]] constexpr IR::Attribute operator+(IR::Attribute attribute, size_t value) noexcept {
return static_cast<IR::Attribute>(static_cast<size_t>(attribute) + value);
return IR::Attribute(size_t(attribute) + value);
}
} // namespace Shader::IR

82
src/shader_recompiler/frontend/ir/flow_test.cpp

@ -1,82 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#include <fmt/ranges.h>
#include "shader_recompiler/frontend/ir/flow_test.h"
namespace Shader::IR {
std::string NameOf(FlowTest flow_test) {
switch (flow_test) {
case FlowTest::F:
return "F";
case FlowTest::LT:
return "LT";
case FlowTest::EQ:
return "EQ";
case FlowTest::LE:
return "LE";
case FlowTest::GT:
return "GT";
case FlowTest::NE:
return "NE";
case FlowTest::GE:
return "GE";
case FlowTest::NUM:
return "NUM";
case FlowTest::NaN:
return "NAN";
case FlowTest::LTU:
return "LTU";
case FlowTest::EQU:
return "EQU";
case FlowTest::LEU:
return "LEU";
case FlowTest::GTU:
return "GTU";
case FlowTest::NEU:
return "NEU";
case FlowTest::GEU:
return "GEU";
case FlowTest::T:
return "T";
case FlowTest::OFF:
return "OFF";
case FlowTest::LO:
return "LO";
case FlowTest::SFF:
return "SFF";
case FlowTest::LS:
return "LS";
case FlowTest::HI:
return "HI";
case FlowTest::SFT:
return "SFT";
case FlowTest::HS:
return "HS";
case FlowTest::OFT:
return "OFT";
case FlowTest::CSM_TA:
return "CSM_TA";
case FlowTest::CSM_TR:
return "CSM_TR";
case FlowTest::CSM_MX:
return "CSM_MX";
case FlowTest::FCSM_TA:
return "FCSM_TA";
case FlowTest::FCSM_TR:
return "FCSM_TR";
case FlowTest::FCSM_MX:
return "FCSM_MX";
case FlowTest::RLE:
return "RLE";
case FlowTest::RGT:
return "RGT";
}
return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test));
}
} // namespace Shader::IR

82
src/shader_recompiler/frontend/ir/flow_test.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -7,45 +10,58 @@
#include <fmt/ranges.h>
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
namespace Shader::IR {
enum class FlowTest : u64 {
F,
LT,
EQ,
LE,
GT,
NE,
GE,
NUM,
NaN,
LTU,
EQU,
LEU,
GTU,
NEU,
GEU,
T,
OFF,
LO,
SFF,
LS,
HI,
SFT,
HS,
OFT,
CSM_TA,
CSM_TR,
CSM_MX,
FCSM_TA,
FCSM_TR,
FCSM_MX,
RLE,
RGT,
#define SRIR_FLOW_TEST_LIST \
SRIR_FLOW_TEST_ELEM(F) \
SRIR_FLOW_TEST_ELEM(LT) \
SRIR_FLOW_TEST_ELEM(EQ) \
SRIR_FLOW_TEST_ELEM(LE) \
SRIR_FLOW_TEST_ELEM(GT) \
SRIR_FLOW_TEST_ELEM(NE) \
SRIR_FLOW_TEST_ELEM(GE) \
SRIR_FLOW_TEST_ELEM(NUM) \
SRIR_FLOW_TEST_ELEM(NaN) \
SRIR_FLOW_TEST_ELEM(LTU) \
SRIR_FLOW_TEST_ELEM(EQU) \
SRIR_FLOW_TEST_ELEM(LEU) \
SRIR_FLOW_TEST_ELEM(GTU) \
SRIR_FLOW_TEST_ELEM(NEU) \
SRIR_FLOW_TEST_ELEM(GEU) \
SRIR_FLOW_TEST_ELEM(T) \
SRIR_FLOW_TEST_ELEM(OFF) \
SRIR_FLOW_TEST_ELEM(LO) \
SRIR_FLOW_TEST_ELEM(SFF) \
SRIR_FLOW_TEST_ELEM(LS) \
SRIR_FLOW_TEST_ELEM(HI) \
SRIR_FLOW_TEST_ELEM(SFT) \
SRIR_FLOW_TEST_ELEM(HS) \
SRIR_FLOW_TEST_ELEM(OFT) \
SRIR_FLOW_TEST_ELEM(CSM_TA) \
SRIR_FLOW_TEST_ELEM(CSM_TR) \
SRIR_FLOW_TEST_ELEM(CSM_MX) \
SRIR_FLOW_TEST_ELEM(FCSM_TA) \
SRIR_FLOW_TEST_ELEM(FCSM_TR) \
SRIR_FLOW_TEST_ELEM(FCSM_MX) \
SRIR_FLOW_TEST_ELEM(RLE) \
SRIR_FLOW_TEST_ELEM(RGT)
#define SRIR_FLOW_TEST_ELEM(n) n,
SRIR_FLOW_TEST_LIST
#undef SRIR_FLOW_TEST_ELEM
};
[[nodiscard]] std::string NameOf(FlowTest flow_test);
[[nodiscard]] inline std::string NameOf(FlowTest flow_test) {
switch (flow_test) {
#define SRIR_FLOW_TEST_ELEM(n) case FlowTest::n: return #n;
SRIR_FLOW_TEST_LIST
#undef SRIR_FLOW_TEST_ELEM
default:
return fmt::format("<invalid flow test {}>", int(flow_test));
}
}
} // namespace Shader::IR

28
src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp

@ -145,7 +145,7 @@ bool IsSizeInt32(Size size) {
}
void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR::Reg coord_reg,
IR::Reg bindless_reg, AtomicOp op, Clamp clamp, Size size, Type type,
std::optional<IR::Reg> bindless_reg, AtomicOp op, Clamp clamp, Size size, Type type,
u64 bound_offset, bool is_bindless, bool write_result) {
if (clamp != Clamp::IGN) {
throw NotImplementedException("Clamp {}", clamp);
@ -158,8 +158,7 @@ void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR
const TextureType tex_type{GetType(type)};
const IR::Value coords{MakeCoords(v, coord_reg, type)};
const IR::U32 handle{is_bindless != 0 ? v.X(bindless_reg)
: v.ir.Imm32(static_cast<u32>(bound_offset * 4))};
const IR::U32 handle = is_bindless ? v.X(*bindless_reg) : v.ir.Imm32(u32(bound_offset * 4));
IR::TextureInstInfo info{};
info.type.Assign(tex_type);
info.image_format.Assign(format);
@ -185,7 +184,7 @@ void TranslatorVisitor::SUATOM(u64 insn) {
BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> operand_reg;
BitField<36, 13, u64> bound_offset; // !is_bindless
BitField<36, 13, u64> bound_offset; // !is_bindless
BitField<39, 8, IR::Reg> bindless_reg; // is_bindless
} const suatom{insn};
@ -196,21 +195,20 @@ void TranslatorVisitor::SUATOM(u64 insn) {
void TranslatorVisitor::SURED(u64 insn) {
// TODO: confirm offsets
// SURED unlike SUATOM does NOT have a binded register
union {
u64 raw;
BitField<51, 1, u64> is_bound;
BitField<21, 3, AtomicOp> op;
BitField<33, 3, Type> type;
BitField<20, 3, Size> size;
BitField<49, 2, Clamp> clamp;
BitField<0, 8, IR::Reg> operand_reg;
BitField<8, 8, IR::Reg> coord_reg;
BitField<36, 13, u64> bound_offset; // is_bound
BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
BitField<24, 3, AtomicOp> op; //OK - 24 (SURedOp)
BitField<33, 3, Type> type; //OK? - 33 (Dim)
BitField<20, 3, Size> size; //?
BitField<49, 2, Clamp> clamp; //OK - 49 (Clamp4)
BitField<0, 8, IR::Reg> operand_reg; //RA?
BitField<8, 8, IR::Reg> coord_reg; //RB?
BitField<36, 13, u64> bound_offset; //OK 33 (TidB)
} const sured{insn};
ImageAtomOp(*this, IR::Reg::RZ, sured.operand_reg, sured.coord_reg, sured.bindless_reg,
ImageAtomOp(*this, IR::Reg::RZ, sured.operand_reg, sured.coord_reg, std::nullopt,
sured.op, sured.clamp, sured.size, sured.type, sured.bound_offset,
sured.is_bound == 0, false);
false, false);
}
} // namespace Shader::Maxwell

8
src/video_core/renderer_opengl/gl_rasterizer.cpp

@ -1142,6 +1142,14 @@ void RasterizerOpenGL::SyncBlendState() {
glDisable(GL_BLEND);
return;
}
// Temporary workaround for games that use iterated blending
if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) {
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
return;
}
glEnable(GL_BLEND);
glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.color_source),
MaxwellToGL::BlendFunc(regs.blend.color_dest),

14
src/video_core/renderer_vulkan/fixed_pipeline_state.cpp

@ -11,6 +11,7 @@
#include <ranges>
#include "common/cityhash.h"
#include "common/common_types.h"
#include "common/settings.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
@ -201,6 +202,19 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
};
if (!regs.blend_per_target_enabled) {
// Temporary workaround for games that use iterated blending
// even when dynamic blending is off so overrides work with EDS = 0 as well
if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) {
equation_rgb.Assign(PackBlendEquation(Maxwell::Blend::Equation::Add_GL));
equation_a.Assign(PackBlendEquation(Maxwell::Blend::Equation::Add_GL));
factor_source_rgb.Assign(PackBlendFactor(Maxwell::Blend::Factor::One_GL));
factor_dest_rgb.Assign(PackBlendFactor(Maxwell::Blend::Factor::One_GL));
factor_source_a.Assign(
PackBlendFactor(Maxwell::Blend::Factor::OneMinusSourceColor_GL));
factor_dest_a.Assign(PackBlendFactor(Maxwell::Blend::Factor::Zero_GL));
enable.Assign(1);
return;
}
setup_blend(regs.blend);
return;
}

2
src/video_core/renderer_vulkan/vk_graphics_pipeline.h

@ -80,7 +80,7 @@ public:
PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos);
// True if this pipeline was created with VK_DYNAMIC_STATE_VERTEX_INPUT_EXT
bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; }
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;

7
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

@ -414,13 +414,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1,
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2,
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2,
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 0,
};
LOG_INFO(Render_Vulkan, "DynamicState1: {}", dynamic_features.has_extended_dynamic_state);
LOG_INFO(Render_Vulkan, "DynamicState2: {}", dynamic_features.has_extended_dynamic_state_2);
LOG_INFO(Render_Vulkan, "DynamicState3: {}", dynamic_features.has_extended_dynamic_state_3_enables);
LOG_INFO(Render_Vulkan, "DynamicVertexInput: {}", dynamic_features.has_dynamic_vertex_input);
}
PipelineCache::~PipelineCache() {

145
src/video_core/renderer_vulkan/vk_rasterizer.cpp

@ -956,38 +956,24 @@ void RasterizerVulkan::UpdateDynamicStates() {
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
auto features = DynamicFeatures{
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0,
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1,
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1,
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2,
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2,
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
};
if (features.has_extended_dynamic_state) {
if (device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0) {
UpdateCullMode(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
UpdateStencilOp(regs);
if (state_tracker.TouchStateEnable()) {
UpdateDepthBoundsTestEnable(regs);
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateStencilTestEnable(regs);
if (features.has_extended_dynamic_state_2) {
if (device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
if (features.has_extended_dynamic_state_3_enables) {
if (device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2) {
using namespace Tegra::Engines;
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
struct In {
const Maxwell3D::Regs::VertexAttribute::Type d;
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
@ -995,36 +981,27 @@ void RasterizerVulkan::UpdateDynamicStates() {
return n.type == d;
}
};
auto has_float = std::any_of(regs.vertex_attrib_format.begin(),
regs.vertex_attrib_format.end(),
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable)
auto has_float = std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable) {
regs.logic_op.enable = static_cast<u32>(!has_float);
UpdateLogicOpEnable(regs);
} else {
UpdateLogicOpEnable(regs);
}
}
UpdateLogicOpEnable(regs);
UpdateDepthClampEnable(regs);
UpdateLineStippleEnable(regs);
UpdateConservativeRasterizationMode(regs);
}
}
if (features.has_extended_dynamic_state_2_extra) {
if (device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1) {
UpdateLogicOp(regs);
}
if (features.has_extended_dynamic_state_3_enables) {
if (device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2) {
UpdateBlending(regs);
UpdateLineStippleEnable(regs);
UpdateConservativeRasterizationMode(regs);
}
}
if (features.has_dynamic_vertex_input) {
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline();
gp && gp->HasDynamicVertexInput()) {
if (device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 0)
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput())
UpdateVertexInput(regs);
}
}
}
void RasterizerVulkan::HandleTransformFeedback() {
@ -1050,19 +1027,25 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
return;
}
if (!regs.viewport_scale_offset_enabled) {
const auto x = static_cast<float>(regs.surface_clip.x);
const auto y = static_cast<float>(regs.surface_clip.y);
const auto width = static_cast<float>(regs.surface_clip.width);
const auto height = static_cast<float>(regs.surface_clip.height);
float x = static_cast<float>(regs.surface_clip.x);
float y = static_cast<float>(regs.surface_clip.y);
float width = std::max(1.0f, static_cast<float>(regs.surface_clip.width));
float height = std::max(1.0f, static_cast<float>(regs.surface_clip.height));
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
y += height;
height = -height;
}
VkViewport viewport{
.x = x,
.y = y,
.width = width != 0.0f ? width : 1.0f,
.height = height != 0.0f ? height : 1.0f,
.width = width,
.height = height,
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); });
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) {
cmdbuf.SetViewport(0, viewport);
});
return;
}
const bool is_rescaling{texture_cache.IsRescaling()};
@ -1089,16 +1072,21 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
return;
}
if (!regs.viewport_scale_offset_enabled) {
const auto x = static_cast<float>(regs.surface_clip.x);
const auto y = static_cast<float>(regs.surface_clip.y);
const auto width = static_cast<float>(regs.surface_clip.width);
const auto height = static_cast<float>(regs.surface_clip.height);
VkRect2D scissor;
scissor.offset.x = static_cast<u32>(x);
scissor.offset.y = static_cast<u32>(y);
scissor.extent.width = static_cast<u32>(width != 0.0f ? width : 1.0f);
scissor.extent.height = static_cast<u32>(height != 0.0f ? height : 1.0f);
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); });
u32 x = regs.surface_clip.x;
u32 y = regs.surface_clip.y;
u32 width = std::max(1u, static_cast<u32>(regs.surface_clip.width));
u32 height = std::max(1u, static_cast<u32>(regs.surface_clip.height));
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
y = regs.surface_clip.height - (y + height);
}
VkRect2D scissor{};
scissor.offset.x = static_cast<int32_t>(x);
scissor.offset.y = static_cast<int32_t>(y);
scissor.extent.width = width;
scissor.extent.height = height;
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) {
cmdbuf.SetScissor(0, scissor);
});
return;
}
u32 up_scale = 1;
@ -1557,22 +1545,41 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
if (state_tracker.TouchBlendEquations()) {
std::array<VkColorBlendEquationEXT, Maxwell::NumRenderTargets> setup_blends{};
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
const auto blend_setup = [&]<typename T>(const T& guest_blend) {
auto& host_blend = setup_blends[index];
host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
};
if (!regs.blend_per_target_enabled) {
blend_setup(regs.blend);
continue;
const auto blend_setup = [&](auto& host_blend, const auto& guest_blend) {
host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
};
// Single blend equation for all targets
if (!regs.blend_per_target_enabled) {
// Temporary workaround for games that use iterated blending
if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) {
setup_blends[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
setup_blends[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
setup_blends[0].colorBlendOp = VK_BLEND_OP_ADD;
setup_blends[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
setup_blends[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
setup_blends[0].alphaBlendOp = VK_BLEND_OP_ADD;
} else {
blend_setup(setup_blends[0], regs.blend);
}
// Copy first blend state to all other targets
for (size_t index = 1; index < Maxwell::NumRenderTargets; index++) {
setup_blends[index] = setup_blends[0];
}
} else {
// Per-target blending
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
blend_setup(setup_blends[index], regs.blend_per_target[index]);
}
blend_setup(regs.blend_per_target[index]);
}
scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorBlendEquationEXT(0, setup_blends);
});
@ -1607,7 +1614,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
highest_dirty_attr = index;
}
}
for (size_t index = 0; index < highest_dirty_attr; ++index) {
for (size_t index = 0; index <= highest_dirty_attr; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
const u32 binding{attribute.buffer};
dirty[Dirty::VertexAttribute0 + index] = false;

7
src/video_core/vulkan_common/vulkan_device.cpp

@ -745,21 +745,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (Settings::values.dyna_state.GetValue() == 0) {
must_emulate_scaled_formats = true;
LOG_INFO(Render_Vulkan, "Dynamic state is disabled (dyna_state = 0), forcing scaled format emulation ON");
LOG_INFO(Render_Vulkan, "Extended dynamic state is fully disabled, scaled format emulation is ON");
// Disable dynamic state 1-3 and all extensions
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
dynamic_state3_blending = false;
dynamic_state3_enables = false;
LOG_INFO(Render_Vulkan, "All dynamic state extensions and features have been disabled");
} else {
must_emulate_scaled_formats = false;
LOG_INFO(Render_Vulkan, "Dynamic state is enabled (dyna_state = 1-3), disabling scaled format emulation");
LOG_INFO(Render_Vulkan, "Extended dynamic state is enabled, scaled format emulation is OFF");
}
logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld);

14
tools/translations/lupdate.sh

@ -1,14 +0,0 @@
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
for i in dist/languages/*.ts; do
SRC=en_US
TARGET=`head -n1 $i | awk -F 'language="' '{split($2, a, "\""); print a[1]}'`
# requires fd
SOURCES=`fd . src/yuzu src/qt_common -tf -e ui -e cpp -e h -e plist`
lupdate -source-language $SRC -target-language $TARGET $SOURCES -ts /data/code/eden/$i
done

9
tools/translations/qt-source.sh

@ -3,10 +3,7 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
SRC=en_US
TARGET=en_US
SOURCES=$(find src/yuzu src/qt_common -type f \( -name "*.ui" -o -name "*.cpp" -o -name "*.h" -o -name "*.plist" \))
# requires fd
SOURCES=`fd . src/yuzu src/qt_common -tf -e ui -e cpp -e h -e plist`
lupdate -source-language $SRC -target-language $TARGET $SOURCES -ts dist/languages/en.ts
# shellcheck disable=SC2086
lupdate -source-language en_US -target-language en_US $SOURCES -ts dist/languages/en.ts

19
tools/update-icons.sh

@ -1,4 +1,4 @@
#!/bin/sh -e
#!/bin/sh -ex
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
@ -8,20 +8,21 @@
#which png2icns || (which yay && yay libicns) || exit
which magick || exit
EDEN_SVG_ICO="dist/dev.eden_emu.eden.svg"
EALT_SVG_ICO="dist/eden_named.svg"
EDEN_BASE_SVG="dist/icon_variations/base.svg"
EDEN_SMALL_SVG="dist/icon_variations/base_small.svg"
EDEN_NAMED_SVG="dist/icon_variations/base_named.svg"
magick -density 256x256 -background transparent $EDEN_SVG_ICO -define icon:auto-resize -colors 256 dist/eden.ico || exit
convert -density 256x256 -resize 256x256 -background transparent $EDEN_SVG_ICO dist/yuzu.bmp || exit
magick -density 256x256 -background transparent $EDEN_SMALL_SVG -define icon:auto-resize -colors 256 dist/eden.ico || exit
convert -density 256x256 -resize 256x256 -background transparent $EDEN_SMALL_SVG dist/yuzu.bmp || exit
magick -size 256x256 -background transparent $EDEN_SVG_ICO dist/qt_themes/default/icons/256x256/eden.png || exit
magick -size 256x256 -background transparent $EALT_SVG_ICO dist/qt_themes/default/icons/256x256/eden_named.png || exit
magick -size 256x256 -background transparent $EDEN_BASE_SVG dist/qt_themes/default/icons/256x256/eden.png || exit
magick -size 256x256 -background transparent $EDEN_NAMED_SVG dist/qt_themes/default/icons/256x256/eden_named.png || exit
magick dist/qt_themes/default/icons/256x256/eden.png -resize 256x256! dist/qt_themes/default/icons/256x256/eden.png || exit
magick dist/qt_themes/default/icons/256x256/eden_named.png -resize 256x256! dist/qt_themes/default/icons/256x256/eden_named.png || exit
# Now do more fancy things (like composition)
TMP_PNG="dist/eden-tmp.png"
magick -size 1024x1024 -background transparent $EDEN_SVG_ICO $TMP_PNG || exit
magick -size 1024x1024 -background transparent $EDEN_BASE_SVG $TMP_PNG || exit
composite $TMP_PNG -gravity center -geometry 2048x2048+0+0 \
src/android/app/src/main/res/drawable/ic_icon_bg_orig.png \
src/android/app/src/main/res/drawable/ic_launcher.png || exit
@ -31,6 +32,6 @@ optipng -o7 src/android/app/src/main/res/drawable/ic_launcher.png
optipng -o7 dist/qt_themes/default/icons/256x256/eden_named.png
optipng -o7 dist/qt_themes/default/icons/256x256/eden.png
png2icns dist/eden.icns $TMP_PNG
png2icns dist/eden.icns $TMP_PNG || echo 'non fatal'
cp dist/eden.icns dist/yuzu.icns
rm $TMP_PNG

19
.ci/windows/install-vulkan-sdk.ps1 → tools/windows/install-vulkan-sdk.ps1

@ -1,25 +1,36 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
$ErrorActionPreference = "Stop"
# Check if running as administrator
if (-not ([bool](net session 2>$null))) {
try {
net session 1>$null 2>$null
} catch {
Write-Host "This script must be run with administrator privileges!"
Exit 1
}
$VulkanSDKVer = "1.4.321.1"
$VulkanSDKVer = "1.4.328.1"
$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer"
$ExeFile = "vulkansdk-windows-X64-$VulkanSDKVer.exe"
$Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile"
$Destination = "./$ExeFile"
# Check if Vulkan SDK is already installed
if (Test-Path $VULKAN_SDK) {
Write-Host "-- Vulkan SDK already installed at $VULKAN_SDK"
return
}
echo "Downloading Vulkan SDK $VulkanSDKVer from $Uri"
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile($Uri, $Destination)
echo "Finished downloading $ExeFile"
$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer"
$Arguments = "--root `"$VULKAN_SDK`" --accept-licenses --default-answer --confirm-command install"
echo "Installing Vulkan SDK $VulkanSDKVer"
@ -36,4 +47,4 @@ echo "Finished installing Vulkan SDK $VulkanSDKVer"
if ("$env:GITHUB_ACTIONS" -eq "true") {
echo "VULKAN_SDK=$VULKAN_SDK" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "$VULKAN_SDK/Bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
}
}

36
tools/windows/install-vulkan-sdk.sh

@ -0,0 +1,36 @@
#!/usr/bin/sh
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
: "${VULKAN_SDK_VER:=1.4.328.1}"
: "${VULKAN_ROOT:=C:/VulkanSDK/$VULKAN_SDK_VER}"
EXE_FILE="vulkansdk-windows-X64-$VULKAN_SDK_VER.exe"
URI="https://sdk.lunarg.com/sdk/download/$VULKAN_SDK_VER/windows/$EXE_FILE"
VULKAN_ROOT_UNIX=$(cygpath -u "$VULKAN_ROOT")
# Check if Vulkan SDK is already installed
if [ -d "$VULKAN_ROOT_UNIX" ]; then
echo "-- Vulkan SDK already installed at $VULKAN_ROOT_UNIX"
exit 0
fi
echo "Downloading Vulkan SDK $VULKAN_SDK_VER from $URI"
[ ! -f "./$EXE_FILE" ] && curl -L -o "./$EXE_FILE" "$URI"
chmod +x "./$EXE_FILE"
echo "Finished downloading $EXE_FILE"
echo "Installing Vulkan SDK $VULKAN_SDK_VER..."
if net session > /dev/null 2>&1; then
./$EXE_FILE --root "$VULKAN_ROOT" --accept-licenses --default-answer --confirm-command install
else
echo "This script must be run with administrator privileges!"
exit 1
fi
echo "Finished installing Vulkan SDK $VULKAN_SDK_VER"
# GitHub Actions integration
if [ \"${GITHUB_ACTIONS:-false}\" = \"true\" ]; then
echo \"VULKAN_SDK=$VULKAN_ROOT\" >> \"$GITHUB_ENV\"
echo \"$VULKAN_ROOT/bin\" >> \"$GITHUB_PATH\"
fi

42
tools/windows/load-msvc-env.ps1

@ -0,0 +1,42 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
$osArch = $env:PROCESSOR_ARCHITECTURE
switch ($osArch) {
"AMD64" { $arch = "x64" }
"ARM64" { $arch = "arm64" }
default {
Write-Error "load-msvc-env.ps1: Unsupported architecture: $osArch"
exit 1
}
}
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (!(Test-Path $vswhere)) {
Write-Error "load-msvc-env.ps1: vswhere not found"
exit 1
}
$vs = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
if (-not $vs) {
Write-Error "load-msvc-env.ps1: Visual Studio (with Desktop development with C++) not found"
exit 1
}
$bat = "$vs\VC\Auxiliary\Build\vcvarsall.bat"
if (!(Test-Path $bat)) {
Write-Error "load-msvc-env.ps1: (vcvarsall.bat) not found"
exit 1
}
cmd /c "`"$bat`" $arch && set" | ForEach-Object {
if ($_ -match "^(.*?)=(.*)$") {
[Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
}
}
Write-Host "load-msvc-env.ps1: MSVC environment loaded for $arch ($vs)"

24
tools/windows/load-msvc-env.sh

@ -0,0 +1,24 @@
#!/usr/bin/bash
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
ARCH_RAW="$PROCESSOR_ARCHITECTURE"
case "$ARCH_RAW" in
AMD64) ARCH="x64" ;;
ARM64) ARCH="arm64" ;;
*) echo "load-msvc-env.sh: Unsupported architecture: $ARCH_RAW"; exit 1 ;;
esac
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VCVARS_BASH="$SCRIPT_DIR/vcvarsall.sh"
if [ ! -f "$VCVARS_BASH" ]; then
echo "load-msvc-env.sh: vcvarsall.sh not found in $SCRIPT_DIR"
#exit 1
fi
chmod +x "$VCVARS_BASH"
eval "$("$VCVARS_BASH" "$ARCH")"
echo "MSVC environment loaded for $ARCH with vcvars-bash script"

433
tools/windows/vcvarsall.sh

@ -0,0 +1,433 @@
#!/usr/bin/env bash
#
# SPDX-FileCopyrightText: Copyright 2023 Nathan Poirier <nathan@poirier.io>
# SPDX-License-Identifier: MIT License
#
# Source: https://github.com/nathan818fr/vcvars-bash
#
set -Eeuo pipefail
shopt -s inherit_errexit
declare -r VERSION='2025-07-09.1'
function detect_platform() {
case "${OSTYPE:-}" in
cygwin* | msys* | win32)
declare -gr bash_platform='win_cyg'
;;
*)
if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
declare -gr bash_platform='win_wsl'
else
printf 'error: Unsupported platform (%s)\n' "${OSTYPE:-}" >&2
printf 'hint: This script only supports Bash on Windows (Git Bash, WSL, etc.)\n' >&2
return 1
fi
;;
esac
}
function detect_mode() {
# Detect the mode depending on the name called
if [[ "$(basename -- "$0")" == vcvarsrun* ]]; then
declare -gr script_mode='run' # vcvarsrun.sh
else
declare -gr script_mode='default' # vcvarsall.sh
fi
}
function print_usage() {
case "$script_mode" in
default)
cat <<EOF
Usage: eval "\$(vcvarsall.sh [vcvarsall.bat arguments])"
Script version: $VERSION
Load MSVC environment variables using vcvarsall.bat and export them.
The script writes a list of export commands to stdout, to be evaluated by a
POSIX-compliant shell.
Example: eval "\$(vcvarsall.sh x86)"
EOF
;;
run)
cat <<EOF
Usage: vcvarsrun.sh [vcvarsall.bat arguments] -- command [arguments...]
Script version: $VERSION
Load MSVC environment variables using vcvarsall.bat and execute a command with
them.
Example: vcvarsrun.sh x64 -- cl /nologo /EHsc /Fe:hello.exe hello.cpp
EOF
;;
esac
cat <<EOF
Environment variables:
VSINSTALLDIR
The path to the Visual Studio installation directory.
Example: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community"
Default: The latest Visual Studio installation directory found by
vswhere.exe
VSWHEREPATH
The path to the vswhere.exe executable.
Example: "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe"
Default: Automatically detected (in the PATH or well-known locations)
VSWHEREARGS
The arguments to pass to vswhere.exe.
This value is always automatically prefixed with "-latest -property installationPath".
Default: "-products *"
EOF
local vcvarsall
vcvarsall=$(find_vcvarsall)
printf '\nvcvarsall.bat arguments: %s\n' "$({ cmd "$(cmdesc "$vcvarsall")" -help </dev/null 2>/dev/null || true; } | fix_crlf | sed '/^Syntax:/d; s/^ vcvarsall.bat //')"
}
function main() {
detect_platform
detect_mode
# Parse arguments
if [[ $# -eq 0 ]]; then
print_usage >&2
return 1
fi
local arg vcvarsall_args=()
for arg in "$@"; do
shift
if [[ "$arg" == '--' ]]; then
if [[ "$script_mode" == 'default' ]]; then
printf 'error: Unexpected argument: --\n' >&2
printf 'hint: Use vcvarsrun to run a command\n' >&2
return 1
fi
break
fi
vcvarsall_args+=("$(cmdesc "$arg")")
done
if [[ "$script_mode" == 'run' && $# -eq 0 ]]; then
printf 'error: No command specified\n' >&2
return 1
fi
# Get MSVC environment variables from vcvarsall.bat
local vcvarsall vcvarsall_env
vcvarsall=$(find_vcvarsall)
vcvarsall_env=$({ cmd "$(cmdesc "$vcvarsall")" "${vcvarsall_args[@]}" '&&' 'set' </dev/null || true; } | fix_crlf)
# Filter MSVC environment variables and export them.
# The list of variables to export was based on a comparison between a clean environment and the vcvarsall.bat
# environment (on different MSVC versions, tools and architectures).
#
# Windows environment variables are case-insensitive while Unix-like environment variables are case-sensitive, so:
# - we always use uppercase names to prevent duplicates environment variables on the Unix-like side
# - we also ensure that only the first occurrence of a variable is exported (see below)
#
# While Windows environment variables are case-insensitive, it is possible to have duplicates in some edge cases.
# e.g. using Git Bash:
# export xxx=1; export XXX=2; export xXx=3; cmd.exe //c set XxX=4 '&&' set
# will output:
# XXX=4
# xXx=3
# xxx=1
declare -A seen_vars
function export_env() {
local name=${1^^}
local value=$2
if [[ ! "$name" =~ ^[A-Z0-9_]+$ ]]; then return; fi
if [[ -n "${seen_vars[$name]:-}" ]]; then return; fi
seen_vars[$name]=1
if [[ "$script_mode" == 'run' ]]; then
export "${name}=${value}"
else
printf "export %s='%s'\n" "$name" "${value//\'/\'\\\'\'}"
fi
}
local name value initialized=false
while IFS='=' read -r name value; do
if [[ "$initialized" == 'false' ]]; then
if [[ -n "$value" ]]; then name+="=$value"; fi
if [[ "$name" == *' Environment initialized for: '* ]]; then initialized=true; fi
printf '%s\n' "$name" >&2
continue
fi
case "${name^^}" in
LIB | LIBPATH | INCLUDE | EXTERNAL_INCLUDE | COMMANDPROMPTTYPE | DEVENVDIR | EXTENSIONSDKDIR | FRAMEWORK* | \
PLATFORM | PREFERREDTOOLARCHITECTURE | UCRT* | UNIVERSALCRTSDK* | VCIDE* | VCINSTALL* | VCPKG* | VCTOOLS* | \
VSCMD* | VSINSTALL* | VS[0-9]* | VISUALSTUDIO* | WINDOWSLIB* | WINDOWSSDK*)
export_env "$name" "$value"
;;
PATH)
# PATH is a special case, requiring special handling
local new_paths
new_paths=$(pathlist_win_to_unix "$value") # Convert to unix-style path list
new_paths=$(pathlist_normalize "${PATH}:${new_paths}") # Prepend the current PATH
export_env 'WINDOWS_PATH' "$value"
export_env 'PATH' "$new_paths"
;;
esac
done <<<"$vcvarsall_env"
if [[ "$initialized" == 'false' ]]; then
printf 'error: vcvarsall.bat failed' >&2
return 1
fi
# Execute command if needed
if [[ "$script_mode" == 'run' ]]; then
exec "$@"
fi
}
# Locate vcvarsall.bat
# Inputs:
# VSINSTALLDIR: The path to the Visual Studio installation directory (optional)
# VSWHEREPATH: The path to the vswhere.exe executable (optional)
# VSWHEREARGS: The arguments to pass to vswhere.exe (optional)
# Outputs:
# stdout: The windows-style path to vcvarsall.bat
function find_vcvarsall() {
local vsinstalldir
if [[ -n "${VSINSTALLDIR:-}" ]]; then
vsinstalldir="$VSINSTALLDIR"
else
local vswhere
if [[ -n "${VSWHEREPATH:-}" ]]; then
vswhere=$(unixpath "$VSWHEREPATH")
else
vswhere=$(command -v 'vswhere' 2>/dev/null || unixpath 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe')
fi
local vswhereargs=(-latest -property installationPath)
if [[ -n "${VSWHEREARGS:-}" ]]; then
parse_args_simple "$VSWHEREARGS"
vswhereargs+=("${result[@]}")
else
vswhereargs+=(-products '*')
fi
if [[ "${VCVARSBASH_DEBUG:-}" == 1 ]]; then
printf 'debug: vswhere path: %s\n' "$vswhere" >&2
printf 'debug: vswhere args:\n' >&2
printf 'debug: [ %s ]\n' "${vswhereargs[@]}" >&2
fi
vsinstalldir=$("$vswhere" "${vswhereargs[@]}" </dev/null | fix_crlf)
if [[ -z "$vsinstalldir" ]]; then
printf 'error: vswhere returned an empty installation path\n' >&2
return 1
fi
fi
printf '%s\n' "$(winpath "$vsinstalldir")\\VC\\Auxiliary\\Build\\vcvarsall.bat"
}
# Split command arguments into an array, with a simple logic
# - Arguments are separated by whitespace (space, tab, newline, carriage return)
# - To include whitespace in an argument, it must be enclosed in double quotes (")
# - When inside a double-quoted argument, quotes can be escaped by doubling them ("")
# Inputs:
# $1: The string to parse
# Outputs:
# result: An array of parsed arguments
function parse_args_simple() {
declare -g result=()
local str="$1 " # add a trailing space to simplify the last argument handling
local args=()
local current_type=none # none = waiting for next arg, normal = inside normal arg, quoted = inside quoted arg
local current_value=''
local i=0 len=${#str}
while ((i < len)); do
local c=${str:i:1}
case "$current_type" in
none)
case "$c" in
' ' | $'\t' | $'\n' | $'\r')
# Ignore whitespace
;;
'"')
# Start a quoted argument
current_type=quoted
current_value=''
;;
*)
# Start a normal argument
current_type=normal
current_value="$c"
;;
esac
;;
normal)
case "$c" in
' ' | $'\t' | $'\n' | $'\r')
# End of normal argument, add it to the list
args+=("$current_value")
current_type=none
current_value=''
;;
*)
# Continue building the normal argument
current_value+="$c"
;;
esac
;;
quoted)
case "$c" in
'"')
if [[ "${str:i+1:1}" != '"' ]]; then
# End of quoted argument, add it to the list
args+=("$current_value")
current_type=none
current_value=''
else
# Escaped quote, add a single quote to the current value
current_value+='"'
((++i)) # Skip the next quote
fi
;;
*)
# Continue building the quoted argument
current_value+="$c"
;;
esac
;;
esac
((++i))
done
if [[ "$current_type" != none ]]; then
printf 'error: Unfinished %s argument: %s\n' "$current_type" "$current_value" >&2
return 1
fi
declare -g result=("${args[@]}")
}
# Run a command with cmd.exe
# Inputs:
# $@: The command string to run (use cmdesc to escape arguments when needed)
# Outputs:
# stdout: The cmd.exe standard output
# stderr: The cmd.exe error output
function cmd() {
# This seems to work fine on all supported platforms
# (even with all the weird path and argument conversions on MSYS-like)
MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' cmd.exe /s /c " ; $* "
}
# Escape a cmd.exe command argument
# Inputs:
# $1: The argument to escape
# Outputs:
# stdout: The escaped argument
function cmdesc() {
# shellcheck disable=SC2001
sed 's/[^0-9A-Za-z]/^\0/g' <<<"$1"
}
# Convert path to an absolute unix-style path
# Inputs:
# $1: The path to convert
# Outputs:
# stdout: The converted path
function unixpath() {
local path=$1
case "$bash_platform" in
win_wsl)
case "$path" in
[a-zA-Z]:\\* | [a-zA-Z]:/* | \\\\* | //*)
# Convert windows path using wslpath (unix mode, absolute path)
wslpath -u -a -- "$path"
;;
*)
# Convert unix path using realpath
realpath -m -- "$path"
;;
esac
;;
*)
cygpath -u -a -- "$path"
;;
esac
}
# Convert path to an absolute windows-style path
# Inputs:
# $1: The path to convert
# Outputs:
# stdout: The converted path
function winpath() {
local path=$1
case "$bash_platform" in
win_wsl)
case "$path" in
[a-zA-Z]:\\* | [a-zA-Z]:/* | \\\\* | //*)
# Already a windows path
printf '%s' "$path"
;;
*)
# Convert using wslpath (windows mode, absolute path)
wslpath -w -a -- "$path"
;;
esac
;;
*)
# Convert using cygpath (windows mode, absolute path, long form)
cygpath -w -a -l -- "$path"
;;
esac
}
# Convert a windows-style path list to a unix-style path list
# Inputs:
# $1: The windows-style path list to convert
# Outputs:
# stdout: The converted unix-style path list
function pathlist_win_to_unix() {
local win_paths=$1
local path_dir first=true
while IFS= read -r -d';' path_dir; do
if [[ -z "$path_dir" ]]; then continue; fi
if [[ "$first" == 'true' ]]; then first=false; else printf ':'; fi
printf '%s' "$(unixpath "$path_dir")"
done <<<"${win_paths};"
}
# Normalize a unix-style path list, removing duplicates and empty entries
# Inputs:
# $1: The list to normalize
# Outputs:
# stdout: The normalized path list
function pathlist_normalize() {
local unix_paths=$1
declare -A seen_paths
local path_dir first=true
while IFS= read -r -d ':' path_dir; do
if [[ -z "$path_dir" ]]; then continue; fi
if [[ -n "${seen_paths[$path_dir]:-}" ]]; then continue; fi
seen_paths[$path_dir]=1
if [[ "$first" == 'true' ]]; then first=false; else printf ':'; fi
printf '%s' "$path_dir"
done <<<"${unix_paths}:"
}
# Convert CRLF to LF
# Inputs:
# stdin: The input to convert
# Outputs:
# stdout: The converted input
function fix_crlf() {
sed 's/\r$//'
}
eval 'main "$@";exit "$?"'
Loading…
Cancel
Save