diff --git a/.ci/license-header.sh b/.ci/license-header.sh index f438d59dac..784c6bac5a 100755 --- a/.ci/license-header.sh +++ b/.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 diff --git a/.github/workflows/sources.yml b/.github/workflows/sources.yml new file mode 100644 index 0000000000..8e98419cba --- /dev/null +++ b/.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 diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml new file mode 100644 index 0000000000..07037c7f94 --- /dev/null +++ b/.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 + diff --git a/.gitignore b/.gitignore index 0886224d8d..d070d94681 100644 --- a/.gitignore +++ b/.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 diff --git a/CMakeLists.txt b/CMakeLists.txt index ff0667d8e8..6f099834c3 100644 --- a/CMakeLists.txt +++ b/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: + # + 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) diff --git a/CMakeModules/MinGWClangCross.cmake b/CMakeModules/MinGWClangCross.cmake deleted file mode 100644 index 286a59a7ad..0000000000 --- a/CMakeModules/MinGWClangCross.cmake +++ /dev/null @@ -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() diff --git a/CMakeModules/MinGWCross.cmake b/CMakeModules/MinGWCross.cmake deleted file mode 100644 index 61464f7dae..0000000000 --- a/CMakeModules/MinGWCross.cmake +++ /dev/null @@ -1,57 +0,0 @@ -# SPDX-FileCopyrightText: 2018 tech4me -# 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() diff --git a/dist/eden.ico b/dist/eden.ico index f34d31d29e..4dc347af28 100644 Binary files a/dist/eden.ico and b/dist/eden.ico differ diff --git a/dist/icon_variations/README.md b/dist/icon_variations/README.md new file mode 100644 index 0000000000..7c444a7e04 --- /dev/null +++ b/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. diff --git a/dist/icon_variations/base_small.svg b/dist/icon_variations/base_small.svg new file mode 100644 index 0000000000..41db18afc1 --- /dev/null +++ b/dist/icon_variations/base_small.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + diff --git a/dist/yuzu.bmp b/dist/yuzu.bmp index 45de9b13eb..a548aaf7e6 100644 Binary files a/dist/yuzu.bmp and b/dist/yuzu.bmp differ diff --git a/docs/Build.md b/docs/Build.md index 52a671ab1e..0bb70ee322 100644 --- a/docs/Build.md +++ b/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 -### Command Line - -This is recommended for *BSD, Solaris, Linux, and MSYS2. MSVC is possible, but not recommended. +### Option B: Command Line
Click to Open -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 ``) +>- 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 ``: - 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 ``: +- `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= -DYUZU_TESTS=OFF +cmake -S . -B build -G "" -DCMAKE_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 "" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl ```
-### [CLion](https://www.jetbrains.com/clion/) +### Option C: [CLion](https://www.jetbrains.com/clion/)
Click to Open @@ -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 ``, 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. \ No newline at end of file +macOS scripts will come soon. diff --git a/docs/Deps.md b/docs/Deps.md index 15ffff28a8..05764341ec 100644 --- a/docs/Deps.md +++ b/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: diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 096760925f..51980dfffe 100644 --- a/externals/CMakeLists.txt +++ b/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() diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 1289c53c99..58461d8934 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/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} ) diff --git a/externals/nx_tzdb/cpmfile.json b/externals/nx_tzdb/cpmfile.json index 00507a85e3..908201564f 100644 --- a/externals/nx_tzdb/cpmfile.json +++ b/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" } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5f2d076899..2510458812 100644 --- a/src/CMakeLists.txt +++ b/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() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 8fcc6a055c..0ba8519f92 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/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) + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 8079e9b782..58598ccdc4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/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) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index d5556a337b..3bccc97607 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/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"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 6dc5d66c7b..716d3aae56 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/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, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index 455b3b5ff1..dd932fcafb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/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()) + } + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index bdc51b7070..71a3e54cb3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/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 } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt index 2a97f15892..51d0455fd5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt +++ b/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 -> { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 8a0bea158e..b495206bb2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/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) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt index fbdca04e9c..d47e33244e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt +++ b/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] diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 126d85d715..da790a4fc4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/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 } diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index 53fdab8b4a..8a46a22df1 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -62,6 +62,7 @@ namespace AndroidSettings { Settings::Setting theme_mode{linkage, -1, "theme_mode", Settings::Category::Android}; Settings::Setting black_backgrounds{linkage, false, "black_backgrounds", Settings::Category::Android}; + Settings::Setting app_language{linkage, 0, "app_language", Settings::Category::Android}; Settings::Setting enable_update_checks{linkage, true, "enable_update_checks", Settings::Category::Android}; diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 3cd1e1d0ab..12dec24219 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -391,6 +391,61 @@ 2 + + @string/app_language_system + @string/app_language_english + @string/app_language_spanish + @string/app_language_french + @string/app_language_german + @string/app_language_italian + @string/app_language_portuguese + @string/app_language_brazilian_portuguese + @string/app_language_russian + @string/app_language_japanese + @string/app_language_korean + @string/app_language_simplified_chinese + @string/app_language_traditional_chinese + @string/app_language_polish + @string/app_language_czech + @string/app_language_norwegian + @string/app_language_hungarian + @string/app_language_ukrainian + @string/app_language_vietnamese + @string/app_language_indonesian + @string/app_language_arabic + @string/app_language_central_kurdish + @string/app_language_persian + @string/app_language_hebrew + @string/app_language_serbian + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + + @string/auto @string/oboe diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 03f546333a..fb80eaf053 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1028,6 +1028,35 @@ Black backgrounds When using the dark theme, apply black backgrounds. + + App Language + Change the language of the app interface + Follow System + English + Español + Français + Deutsch + Italiano + Português + Português do Brasil + Русский + 日本語 + 한국어 + 简体中文 + 繁體中文 + Polski + Čeština + Norsk bokmål + Magyar + Українська + Tiếng Việt + Bahasa Indonesia + العربية + کوردیی ناوەندی + فارسی + עברית + Српски + Theme Color Eden (Default) diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h index 9bf6f2f81c..c4cd549893 100644 --- a/src/common/atomic_ops.h +++ b/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 [[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 [[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 diff --git a/src/common/logging/formatter.h b/src/common/logging/formatter.h index 07efba9c27..343174a0c7 100644 --- a/src/common/logging/formatter.h +++ b/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 - #include +#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, char>> } }; #endif + +template +struct fmt::formatter> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template + auto format(const SwapStructT& reg, FormatContext& ctx) const { + return fmt::format_to(ctx.out(), "{}", T(reg)); + } +}; diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 6800c20f69..56b65c527c 100644 --- a/src/common/settings.cpp +++ b/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; diff --git a/src/common/settings.h b/src/common/settings.h index 360a49c6c6..2e16e4bc59 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -759,6 +759,9 @@ struct Values { // Add-Ons std::map> disabled_addons; + + // Per-game overrides + bool use_squashed_iterated_blend; }; extern Values values; diff --git a/src/common/swap.h b/src/common/swap.h index fde343e452..1cf8a6ba39 100644 --- a/src/common/swap.h +++ b/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 -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 - swapped_t& operator=(const S& source) { - value = swap(static_cast(source)); + SwappedT& operator=(const S& source) { + value = swap(T(source)); return *this; } operator s8() const { - return static_cast(swap()); + return s8(swap()); } operator u8() const { - return static_cast(swap()); + return u8(swap()); } operator s16() const { - return static_cast(swap()); + return s16(swap()); } operator u16() const { - return static_cast(swap()); + return u16(swap()); } operator s32() const { - return static_cast(swap()); + return s32(swap()); } operator u32() const { - return static_cast(swap()); + return u32(swap()); } operator s64() const { - return static_cast(swap()); + return s64(swap()); } operator u64() const { - return static_cast(swap()); + return u64(swap()); } operator float() const { - return static_cast(swap()); + return float(swap()); } operator double() const { - return static_cast(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 - 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 - 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 - swapped_t operator+(const S& i) const { - return swap() + static_cast(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 - swapped_t operator-(const S& i) const { - return swap() - static_cast(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 - swapped_t& operator+=(const S& i) { - value = swap(swap() + static_cast(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 - swapped_t& operator-=(const S& i) { - value = swap(swap() - static_cast(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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 - 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 - 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 - 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 - 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 - 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 - swapped_t& operator^=(const S& b) { + SwappedT& operator^=(const S& b) { value = swap(swap() ^ b); return *this; } template - swapped_t operator<<(const S& b) const { + SwappedT operator<<(const S& b) const { return swap() << b; } template - swapped_t& operator<<=(const S& b) const { + SwappedT& operator<<=(const S& b) const { value = swap(swap() << b); return *this; } template - swapped_t operator>>(const S& b) const { + SwappedT operator>>(const S& b) const { return swap() >> b; } template - 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 - friend S operator+(const S& p, const swapped_t v); + friend S operator+(const S& p, const SwappedT v); template - friend S operator-(const S& p, const swapped_t v); + friend S operator-(const S& p, const SwappedT v); template - friend S operator/(const S& p, const swapped_t v); + friend S operator/(const S& p, const SwappedT v); template - friend S operator*(const S& p, const swapped_t v); + friend S operator*(const S& p, const SwappedT v); template - friend S operator%(const S& p, const swapped_t v); + friend S operator%(const S& p, const SwappedT v); // Arithmetic + assignments template - friend S operator+=(const S& p, const swapped_t v); + friend S operator+=(const S& p, const SwappedT v); template - friend S operator-=(const S& p, const swapped_t v); + friend S operator-=(const S& p, const SwappedT v); // Bitmath template - friend S operator&(const S& p, const swapped_t v); + friend S operator&(const S& p, const SwappedT v); // Comparison template - friend bool operator<(const S& p, const swapped_t v); + friend bool operator<(const S& p, const SwappedT v); template - friend bool operator>(const S& p, const swapped_t v); + friend bool operator>(const S& p, const SwappedT v); template - friend bool operator<=(const S& p, const swapped_t v); + friend bool operator<=(const S& p, const SwappedT v); template - friend bool operator>=(const S& p, const swapped_t v); + friend bool operator>=(const S& p, const SwappedT v); template - friend bool operator!=(const S& p, const swapped_t v); + friend bool operator!=(const S& p, const SwappedT v); template - friend bool operator==(const S& p, const swapped_t v); + friend bool operator==(const S& p, const SwappedT v); }; // Arithmetic template -S operator+(const S& i, const swap_struct_t v) { +S operator+(const S& i, const SwapStructT v) { return i + v.swap(); } template -S operator-(const S& i, const swap_struct_t v) { +S operator-(const S& i, const SwapStructT v) { return i - v.swap(); } template -S operator/(const S& i, const swap_struct_t v) { +S operator/(const S& i, const SwapStructT v) { return i / v.swap(); } template -S operator*(const S& i, const swap_struct_t v) { +S operator*(const S& i, const SwapStructT v) { return i * v.swap(); } template -S operator%(const S& i, const swap_struct_t v) { +S operator%(const S& i, const SwapStructT v) { return i % v.swap(); } // Arithmetic + assignments template -S& operator+=(S& i, const swap_struct_t v) { +S& operator+=(S& i, const SwapStructT v) { i += v.swap(); return i; } template -S& operator-=(S& i, const swap_struct_t v) { +S& operator-=(S& i, const SwapStructT v) { i -= v.swap(); return i; } // Logical template -S operator&(const S& i, const swap_struct_t v) { +S operator&(const S& i, const SwapStructT v) { return i & v.swap(); } // Comparison template -bool operator<(const S& p, const swap_struct_t v) { +bool operator<(const S& p, const SwapStructT v) { return p < v.swap(); } template -bool operator>(const S& p, const swap_struct_t v) { +bool operator>(const S& p, const SwapStructT v) { return p > v.swap(); } template -bool operator<=(const S& p, const swap_struct_t v) { +bool operator<=(const S& p, const SwapStructT v) { return p <= v.swap(); } template -bool operator>=(const S& p, const swap_struct_t v) { +bool operator>=(const S& p, const SwapStructT v) { return p >= v.swap(); } template -bool operator!=(const S& p, const swap_struct_t v) { +bool operator!=(const S& p, const SwapStructT v) { return p != v.swap(); } template -bool operator==(const S& p, const swap_struct_t v) { +bool operator==(const S& p, const SwapStructT v) { return p == v.swap(); } template -struct swap_64_t { +struct Swap64T { static T swap(T x) { - return static_cast(Common::swap64(x)); + return T(Common::swap64(x)); } }; template -struct swap_32_t { +struct Swap32T { static T swap(T x) { - return static_cast(Common::swap32(x)); + return T(Common::swap32(x)); } }; template -struct swap_16_t { +struct Swap16T { static T swap(T x) { - return static_cast(Common::swap16(x)); + return T(Common::swap16(x)); } }; template -struct swap_float_t { +struct SwapFloatT { static T swap(T x) { - return static_cast(Common::swapf(x)); + return T(Common::swapf(x)); } }; template -struct swap_double_t { +struct SwapDoubleT { static T swap(T x) { - return static_cast(Common::swapd(x)); + return T(Common::swapd(x)); } }; template -struct swap_enum_t { +struct SwapEnumT { static_assert(std::is_enum_v); using base = std::underlying_type_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(swap(value)); + return base(swap(value)); } protected: T value{}; - // clang-format off using swap_t = std::conditional_t< - std::is_same_v, swap_16_t, std::conditional_t< - std::is_same_v, swap_16_t, std::conditional_t< - std::is_same_v, swap_32_t, std::conditional_t< - std::is_same_v, swap_32_t, std::conditional_t< - std::is_same_v, swap_64_t, std::conditional_t< - std::is_same_v, swap_64_t, void>>>>>>; - // clang-format on + std::is_same_v, Swap16T, std::conditional_t< + std::is_same_v, Swap16T, std::conditional_t< + std::is_same_v, Swap32T, std::conditional_t< + std::is_same_v, Swap32T, std::conditional_t< + std::is_same_v, Swap64T, std::conditional_t< + std::is_same_v, Swap64T, void>>>>>>; static T swap(T x) { - return static_cast(swap_t::swap(static_cast(x))); + return T(swap_t::swap(base(x))); } }; @@ -581,17 +582,17 @@ struct AddEndian { template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template <> @@ -601,33 +602,33 @@ struct AddEndian { template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template <> struct AddEndian { - using type = swap_struct_t>; + using type = SwapStructT>; }; template struct AddEndian { static_assert(std::is_enum_v); - using type = swap_enum_t; + using type = SwapEnumT; }; // Alias LETag/BETag as KeepTag/SwapTag depending on the system diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 163772d8d5..21641744d5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -210,9 +210,12 @@ std::shared_ptr 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()) { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 1d74215971..b00a0d4346 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -269,9 +269,12 @@ std::shared_ptr 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()) { diff --git a/src/core/core.cpp b/src/core/core.cpp index 7315f35e0c..6c321afdbb 100644 --- a/src/core/core.cpp +++ b/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; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 58d4c79ea6..aab4de39e4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/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(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; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index fb0a5be959..b8854aab95 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/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{}; }; diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 03532c3344..5f9506273f 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/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 diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_interface.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_interface.cpp index b895e42251..0fe738e212 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_interface.cpp +++ b/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); diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 4f19e00ee4..c385951318 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/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 diff --git a/src/shader_recompiler/frontend/ir/attribute.cpp b/src/shader_recompiler/frontend/ir/attribute.cpp deleted file mode 100644 index 97f253602d..0000000000 --- a/src/shader_recompiler/frontend/ir/attribute.cpp +++ /dev/null @@ -1,459 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#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(attribute) - static_cast(Attribute::Generic0X)) / 4u; -} - -u32 GenericAttributeElement(Attribute attribute) { - if (!IsGeneric(attribute)) { - throw InvalidArgument("Attribute is not generic {}", attribute); - } - return static_cast(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("", static_cast(attribute)); -} - -} // namespace Shader::IR diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h index 7a06713803..943c8d673f 100644 --- a/src/shader_recompiler/frontend/ir/attribute.h +++ b/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 #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("", int(attribute)); + } +} [[nodiscard]] constexpr IR::Attribute operator+(IR::Attribute attribute, size_t value) noexcept { - return static_cast(static_cast(attribute) + value); + return IR::Attribute(size_t(attribute) + value); } } // namespace Shader::IR diff --git a/src/shader_recompiler/frontend/ir/flow_test.cpp b/src/shader_recompiler/frontend/ir/flow_test.cpp deleted file mode 100644 index c2dbe2690f..0000000000 --- a/src/shader_recompiler/frontend/ir/flow_test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include - -#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("", static_cast(flow_test)); -} - -} // namespace Shader::IR diff --git a/src/shader_recompiler/frontend/ir/flow_test.h b/src/shader_recompiler/frontend/ir/flow_test.h index 014ae6b659..0bb9666538 100644 --- a/src/shader_recompiler/frontend/ir/flow_test.h +++ b/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 #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("", int(flow_test)); + } +} } // namespace Shader::IR diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp index b7d9d468b6..e75cdf7c0f 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp +++ b/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 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(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 diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 718958fc11..6f25267a8f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/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), diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 453a3d942b..e643e98ead 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -11,6 +11,7 @@ #include #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; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 650c8e07ed..c8e89d60a4 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/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 stages, const std::array& 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; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 10ee14773f..14a4fee69b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/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() { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6bad5eca0b..1d67601ab4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/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(!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(regs.surface_clip.x); - const auto y = static_cast(regs.surface_clip.y); - const auto width = static_cast(regs.surface_clip.width); - const auto height = static_cast(regs.surface_clip.height); + float x = static_cast(regs.surface_clip.x); + float y = static_cast(regs.surface_clip.y); + float width = std::max(1.0f, static_cast(regs.surface_clip.width)); + float height = std::max(1.0f, static_cast(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(regs.surface_clip.x); - const auto y = static_cast(regs.surface_clip.y); - const auto width = static_cast(regs.surface_clip.width); - const auto height = static_cast(regs.surface_clip.height); - VkRect2D scissor; - scissor.offset.x = static_cast(x); - scissor.offset.y = static_cast(y); - scissor.extent.width = static_cast(width != 0.0f ? width : 1.0f); - scissor.extent.height = static_cast(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(regs.surface_clip.width)); + u32 height = std::max(1u, static_cast(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(x); + scissor.offset.y = static_cast(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 setup_blends{}; - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - const auto blend_setup = [&](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; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 7b7c4b0b78..81e60f1c6a 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/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); diff --git a/tools/translations/lupdate.sh b/tools/translations/lupdate.sh deleted file mode 100755 index 0442d25d02..0000000000 --- a/tools/translations/lupdate.sh +++ /dev/null @@ -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 diff --git a/tools/translations/qt-source.sh b/tools/translations/qt-source.sh index 3480070624..89c881d8c0 100755 --- a/tools/translations/qt-source.sh +++ b/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 \ No newline at end of file diff --git a/tools/update-icons.sh b/tools/update-icons.sh index 132e62da86..5fff11d6c5 100755 --- a/tools/update-icons.sh +++ b/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 diff --git a/.ci/windows/install-vulkan-sdk.ps1 b/tools/windows/install-vulkan-sdk.ps1 similarity index 79% rename from .ci/windows/install-vulkan-sdk.ps1 rename to tools/windows/install-vulkan-sdk.ps1 index 4c5274d1b7..072b531201 100755 --- a/.ci/windows/install-vulkan-sdk.ps1 +++ b/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 -} \ No newline at end of file +} diff --git a/tools/windows/install-vulkan-sdk.sh b/tools/windows/install-vulkan-sdk.sh new file mode 100644 index 0000000000..cc2bcf2c50 --- /dev/null +++ b/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 diff --git a/tools/windows/load-msvc-env.ps1 b/tools/windows/load-msvc-env.ps1 new file mode 100644 index 0000000000..8b98101bdb --- /dev/null +++ b/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)" diff --git a/tools/windows/load-msvc-env.sh b/tools/windows/load-msvc-env.sh new file mode 100644 index 0000000000..008621f7e4 --- /dev/null +++ b/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" \ No newline at end of file diff --git a/tools/windows/vcvarsall.sh b/tools/windows/vcvarsall.sh new file mode 100644 index 0000000000..e6270730d7 --- /dev/null +++ b/tools/windows/vcvarsall.sh @@ -0,0 +1,433 @@ +#!/usr/bin/env bash +# +# SPDX-FileCopyrightText: Copyright 2023 Nathan Poirier +# 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 </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' &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[@]}" &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 "$?"'