From b7584cb2c3ba211595ceb86de416edb8f04617e5 Mon Sep 17 00:00:00 2001 From: crueter Date: Wed, 12 Nov 2025 04:38:32 +0100 Subject: [PATCH 01/28] [ci] push sources on every master push (#3007) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3007 --- .github/workflows/sources.yml | 19 +++++++++++++++++++ tools/translations/lupdate.sh | 14 -------------- tools/translations/qt-source.sh | 9 +++------ 3 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/sources.yml delete mode 100755 tools/translations/lupdate.sh diff --git a/.github/workflows/sources.yml b/.github/workflows/sources.yml new file mode 100644 index 0000000000..20cb03c449 --- /dev/null +++ b/.github/workflows/sources.yml @@ -0,0 +1,19 @@ +name: tx-src + +on: + push: + branches: [ master ] + +jobs: + license-header: + 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/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 From 87c4f658ce7e471c6b7ce33c92d1973460af6091 Mon Sep 17 00:00:00 2001 From: crueter Date: Wed, 12 Nov 2025 05:21:49 +0100 Subject: [PATCH 02/28] [ci] tx update ci (#3008) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3008 --- .github/workflows/sources.yml | 2 +- .github/workflows/translations.yml | 61 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/translations.yml diff --git a/.github/workflows/sources.yml b/.github/workflows/sources.yml index 20cb03c449..8e98419cba 100644 --- a/.github/workflows/sources.yml +++ b/.github/workflows/sources.yml @@ -5,7 +5,7 @@ on: branches: [ master ] jobs: - license-header: + sources: runs-on: source steps: - uses: actions/checkout@v4 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 + From d89df63a287b75ed4402cfac43c23261d778a9bb Mon Sep 17 00:00:00 2001 From: MaranBr Date: Thu, 13 Nov 2025 03:04:00 +0100 Subject: [PATCH 03/28] [video_core] Clean up the code and fix some inconsistences (#3015) This cleans up the code and fixes some inconsistencies in the EDS settings. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3015 Co-authored-by: MaranBr Co-committed-by: MaranBr --- .../renderer_vulkan/vk_pipeline_cache.cpp | 7 +-- .../renderer_vulkan/vk_rasterizer.cpp | 47 ++++++------------- .../vulkan_common/vulkan_device.cpp | 7 ++- 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 10ee14773f..0532df05d8 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 > 2, }; - - 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..530c7e8e41 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,33 +981,28 @@ 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); - } + } 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 > 2) { + if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) { UpdateVertexInput(regs); } } 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); From 028765867feae4a1cae77bfc3370c73381cfeb2d Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Thu, 13 Nov 2025 03:20:38 +0100 Subject: [PATCH 04/28] externals: Fix Debug builds and remove PCH leftover (#3000) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3000 Reviewed-by: Lizzie Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: Caio Oliveira Co-committed-by: Caio Oliveira --- CMakeLists.txt | 34 ++++++++++-------- CMakeModules/MinGWClangCross.cmake | 58 ------------------------------ CMakeModules/MinGWCross.cmake | 57 ----------------------------- externals/CMakeLists.txt | 7 ---- src/CMakeLists.txt | 6 ---- src/dynarmic/CMakeLists.txt | 5 --- 6 files changed, 20 insertions(+), 147 deletions(-) delete mode 100644 CMakeModules/MinGWClangCross.cmake delete mode 100644 CMakeModules/MinGWCross.cmake 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/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/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/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 From 7832afc5dd23d4a4343ac356b8864386cb9ad064 Mon Sep 17 00:00:00 2001 From: crueter Date: Thu, 13 Nov 2025 03:25:55 +0100 Subject: [PATCH 05/28] [externals] update nx-tzdb to 121125 (#3011) real gzipped archive Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3011 Reviewed-by: Caio Oliveira --- externals/nx_tzdb/cpmfile.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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" } } From ba9e03a612f4d4d50ad619ab3135bcb04b0fc29d Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 13 Nov 2025 03:40:29 +0100 Subject: [PATCH 06/28] [shared_recompiler/maxwell] fix SURED() wrong encodings (#2983) SURED does NOT have a binding register and stuff, it is strictly just a binding-offset * 4 Signed-off-by: lizzie lizzie@eden-emu.dev Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2983 Reviewed-by: MaranBr Reviewed-by: Caio Oliveira Co-authored-by: lizzie Co-committed-by: lizzie --- .../impl/surface_atomic_operations.cpp | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) 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 From f51d61e4a41218780815bfa7d2c13ccc5e2744b5 Mon Sep 17 00:00:00 2001 From: kleidis Date: Thu, 13 Nov 2025 03:45:41 +0100 Subject: [PATCH 07/28] [android] Use spinbox setting type for CPU_TICKS (#2952) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2952 Reviewed-by: crueter Reviewed-by: MaranBr Reviewed-by: Caio Oliveira Co-authored-by: kleidis Co-committed-by: kleidis --- .../settings/model/view/SettingsItem.kt | 3 +- .../settings/ui/SettingsDialogFragment.kt | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) 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..bbcfe3da45 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 ) 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..eb1b0a4257 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) {} From cfbef5c487005077224f0c5d26317680243cadfc Mon Sep 17 00:00:00 2001 From: kleidis Date: Thu, 13 Nov 2025 03:45:58 +0100 Subject: [PATCH 08/28] [android] Setting to manually set app language (#2951) It is on the app settings section Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2951 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: kleidis Co-committed-by: kleidis --- .../java/org/yuzu/yuzu_emu/YuzuApplication.kt | 37 +++++++++++++ .../yuzu_emu/activities/EmulationActivity.kt | 4 ++ .../features/settings/model/IntSetting.kt | 1 + .../settings/model/view/SettingsItem.kt | 9 +++ .../features/settings/ui/SettingsActivity.kt | 20 +++++++ .../features/settings/ui/SettingsAdapter.kt | 8 +++ .../settings/ui/SettingsDialogFragment.kt | 6 ++ .../settings/ui/SettingsFragmentPresenter.kt | 4 +- .../features/settings/ui/SettingsViewModel.kt | 9 +++ .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 6 ++ .../app/src/main/jni/android_settings.h | 1 + .../app/src/main/res/values/arrays.xml | 55 +++++++++++++++++++ .../app/src/main/res/values/strings.xml | 29 ++++++++++ 13 files changed, 188 insertions(+), 1 deletion(-) 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 bbcfe3da45..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 @@ -757,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 eb1b0a4257..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 @@ -387,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) From 9a046190c77905c278b2ec7e9c299d15cbb855bb Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 13 Nov 2025 13:22:02 +0100 Subject: [PATCH 09/28] [shader_recompiler] macro-ify flow_test and attribute (#2900) Of course - macros my beloved :) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2900 Reviewed-by: crueter Reviewed-by: MaranBr Reviewed-by: Caio Oliveira Co-authored-by: lizzie Co-committed-by: lizzie --- src/shader_recompiler/CMakeLists.txt | 2 - .../frontend/ir/attribute.cpp | 459 ----------------- src/shader_recompiler/frontend/ir/attribute.h | 464 +++++++++--------- .../frontend/ir/flow_test.cpp | 82 ---- src/shader_recompiler/frontend/ir/flow_test.h | 82 ++-- 5 files changed, 293 insertions(+), 796 deletions(-) delete mode 100644 src/shader_recompiler/frontend/ir/attribute.cpp delete mode 100644 src/shader_recompiler/frontend/ir/flow_test.cpp 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 From 66db2613b52233ac9841ab3aafb212312d8c9f8f Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 13 Nov 2025 13:23:46 +0100 Subject: [PATCH 10/28] [common] fix formatting of swapped u32_le/u64_le for BE targets (#2998) Fixes a bunch of errors :) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2998 Reviewed-by: Caio Oliveira Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/common/logging/formatter.h | 16 +- src/common/swap.h | 279 +++++++++++++++++---------------- 2 files changed, 155 insertions(+), 140 deletions(-) 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/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 From 450c483de0e9779153db1de3dfe1238dcc9055c2 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 13 Nov 2025 13:26:40 +0100 Subject: [PATCH 11/28] [cmake, externals/ffmpeg]: fix Solaris and BSD* builds with troubling makes (#3014) Partial backport of https://github.com/pflyly/eden-nightly/blob/main/patches/solaris.patch Signed-off-by: lizzie lizzie@eden-emu.dev Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3014 Reviewed-by: Caio Oliveira Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- externals/ffmpeg/CMakeLists.txt | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) 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} ) From 3edfcabdea14fd7e9e8e2baa66d372cffac7ffbf Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 13 Nov 2025 13:27:03 +0100 Subject: [PATCH 12/28] [dist] small low-fi version of the icon w/o antialias artifacts (#3006) This version of the icon is mainly so the main one doesn't look like it has been compressed like a JPEG when shown on the taskbar :) Signed-off-by: lizzie lizzie@eden-emu.dev Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3006 Reviewed-by: Caio Oliveira Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- dist/eden.ico | Bin 322147 -> 395668 bytes dist/icon_variations/README.md | 9 ++++ dist/icon_variations/base_small.svg | 80 ++++++++++++++++++++++++++++ dist/yuzu.bmp | Bin 262282 -> 262282 bytes tools/update-icons.sh | 19 +++---- 5 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 dist/icon_variations/README.md create mode 100644 dist/icon_variations/base_small.svg diff --git a/dist/eden.ico b/dist/eden.ico index f34d31d29ea486d92ea77f4d49a9c4ad71502df5..4dc347af283d60b43fd626d208609a382cb54047 100644 GIT binary patch literal 395668 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
KV3qi_JE`lx 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 VhGMo^Z~$ufBnV^iMEFC0{{~B5vv&Xh literal 322147 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�ieXhLk3wRzb!fv#9XU_b2=o06{T*&45b_w_loGU#0@_EMf=h-(CThBk@HyU1B zn!9ZT&$j%b1o#!LvcFwUG^Qrz_i|PJ}(Nu^!6Xlky}8ck}G+Z0qC9NX82=d{lw`$79ER%&z1DITI|oY?^y<$;Un;wp&q9bMEzlApH-Cn b)=9cAQfGNJ{1CK1{E}r#@>@jzZa?%t4|r=Y 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 45de9b13ebf2d213634281b61c4f1b870ed094c6..a548aaf7e6da5425b2ab520be6fc9ed756dd7ff8 100644 GIT binary patch literal 262282 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 literal 262282 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 AA^-pY 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 From 8eaa7c28ce3cfea76d64af54334ac4971659085a Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 13 Nov 2025 13:27:32 +0100 Subject: [PATCH 13/28] [common] provide fallback for lack of atomic u128 support (#2999) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2999 Reviewed-by: Caio Oliveira Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/common/atomic_ops.h | 47 ++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) 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 From 32db6c18779a3e69b065ecbd68be2b61cb2a9c4d Mon Sep 17 00:00:00 2001 From: xbzk Date: Thu, 13 Nov 2025 14:01:32 +0100 Subject: [PATCH 14/28] [renderer] NG ragebound workaround via LoadOverrides + vk_rasterizer UpdateBlending TouchBlendequations Rework (#2934) this pull should impact ninja gaiden ragebound only! it makes it playable past stage 4-1. it contains a workaround for missing maxwell_3d's iterated_blend functionality, which fixes several graphics all over the game. the issue causes transparency enabled blends (mostly lighting fx) to be wrongly blended into destination, turning textures into black frames. in stage 4-1 there are lighthing layers in the foreground, causing sprites layer to become overlapped by these opaque black frames, including entire screen in a mid boss fight, making it unplayable* (players maneuvered by turning immortal option on and swinging sword all around until defeating it). also only in stage 4-1 the fix has a short drawback: when you buff up next attack these problematique blends will be drawn back as black frames, but only for a split second, so no big deal. this workaround was already discovered and available in PR 302, but in an unconventional way for a game specific override, so we did forbidden it. now it uses classic game specific override solution exampled in core.cpp's System::Impl::LoadOverrides method, so now i guess it's worth to merge it and deliver this to players until we harness iterated_blend control. additionally I've slightly reworked vk_rasterizer.cpp's RasterizerVulkan::UpdateBlending, if (state_tracker.TouchBlendEquations()) {...} session. it was made in a way that for a single blend, it exhaustly calls 48 (6 x 8) MaxwellToVK redundant functions, and declared a lambda function inside a 8 laps loop. reworked it so that instead of 48 calls it makes only the necessary 6 calls, and then merely safely copy the result for the other 7 times. Co-authored-by: Allison Cunha Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2934 Reviewed-by: MaranBr Reviewed-by: Shinmegumi Reviewed-by: Maufeat Co-authored-by: xbzk Co-committed-by: xbzk --- src/common/settings.cpp | 3 ++ src/common/settings.h | 3 ++ src/core/core.cpp | 13 +++++ .../renderer_opengl/gl_rasterizer.cpp | 8 ++++ .../renderer_vulkan/fixed_pipeline_state.cpp | 14 ++++++ .../renderer_vulkan/vk_rasterizer.cpp | 47 +++++++++++++------ 6 files changed, 74 insertions(+), 14 deletions(-) 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/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/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_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 530c7e8e41..134327fa8d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1538,22 +1538,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); }); From e9d84d098d59667edd3828fd7c4914f37717455b Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 14 Nov 2025 15:07:13 +0100 Subject: [PATCH 15/28] [dynarmic] attempt fix totk regression from #358 (#3013) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3013 Reviewed-by: MaranBr Reviewed-by: Caio Oliveira Co-authored-by: lizzie Co-committed-by: lizzie --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 7 +++++-- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 7 +++++-- src/dynarmic/src/dynarmic/backend/x64/a64_interface.cpp | 7 +++---- 3 files changed, 13 insertions(+), 8 deletions(-) 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/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); From 3f226678dd293f41d48edbef43eee528e129ae46 Mon Sep 17 00:00:00 2001 From: Shinmegumi Date: Fri, 14 Nov 2025 15:13:29 +0100 Subject: [PATCH 16/28] [vk] Fix fallback viewport/scissor origin handling (#294) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When viewport_scale_offset_enabled is disabled, the fallback path previously assumed a top-left origin for both viewport and scissor. This caused incorrect positioning or inverted geometry when the GPU state expected a lower-left origin. This change: - Adjusts viewport setup: if window_origin is lower-left, shift Y and flip height negative to emulate lower-left in Vulkan’s top-left space. - Updates scissor setup: recalculates Y for lower-left origin and ensures width/height fall back to 1 if zero, avoiding invalid extents. This aligns Vulkan’s viewport/scissor behavior with Maxwell state, fixing rendering issues in paths without scale/offset enabled. Co-authored-by: MaranBr Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/294 Co-authored-by: Shinmegumi Co-committed-by: Shinmegumi --- .../renderer_vulkan/vk_graphics_pipeline.h | 2 +- .../renderer_vulkan/vk_rasterizer.cpp | 55 +++++++++++-------- 2 files changed, 33 insertions(+), 24 deletions(-) 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_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 134327fa8d..44fe42ce9e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -984,11 +984,9 @@ void RasterizerVulkan::UpdateDynamicStates() { 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); @@ -1031,19 +1029,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()}; @@ -1070,16 +1074,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; @@ -1607,7 +1616,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; From a27914f0f405c0a988ae292cddb6de41309ed34d Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 15 Nov 2025 08:15:12 +0100 Subject: [PATCH 17/28] [vk] quick fix for EDS1,2 with VIDS (#3022) Here: src/video_core/renderer_vulkan/vk_pipeline_cache.cpp --> .has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 0 And Here: src/video_core/renderer_vulkan/vk_rasterizer.cpp --> if (device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 0) { Signed-off-by: lizzie lizzie@eden-emu.dev Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3022 Co-authored-by: lizzie Co-committed-by: lizzie --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 2 +- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 0532df05d8..14a4fee69b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -414,7 +414,7 @@ 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() && dynamic_state > 2, + .has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 0, }; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 44fe42ce9e..1d67601ab4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -999,11 +999,9 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateBlending(regs); } } - if (device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 2) { - 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() { From 680384f7b831189bc55715373086fa5f0f060af1 Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Sat, 15 Nov 2025 14:49:43 +0100 Subject: [PATCH 18/28] [gpu/NVDRV] Unstub SetChannelPriority and adjust ChannelSetTimeSlice (#3017) Games usually rely on either 1 of this services or both. The last call adjusts channel_timeslice. This behavior closely resembles Ryujinx accurate behavior for setting the channel_timeslice accordingly to the situation. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3017 Reviewed-by: Caio Oliveira Reviewed-by: crueter Co-authored-by: SDK Chan Co-committed-by: SDK Chan --- src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | 14 +++++++++++++- src/core/hle/service/nvdrv/devices/nvhost_gpu.h | 6 ++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 5f754650d9..fcc7f9d392 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; } @@ -402,6 +410,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{}; }; From 7e730a121bfa0539cbb132b0b0e19eb182840581 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Sat, 15 Nov 2025 17:53:13 +0100 Subject: [PATCH 19/28] [tools] MSVC environment loader, POSIX vulkan/MSVC install scripts (#2993) * also move install-vulkan-sdk.ps1 to tools Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2993 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: Caio Oliveira Co-committed-by: Caio Oliveira --- .ci/license-header.sh | 2 +- .gitignore | 2 + docs/Build.md | 46 +- docs/Deps.md | 22 +- {.ci => tools}/windows/install-vulkan-sdk.ps1 | 19 +- tools/windows/install-vulkan-sdk.sh | 36 ++ tools/windows/load-msvc-env.ps1 | 42 ++ tools/windows/load-msvc-env.sh | 24 + tools/windows/vcvarsall.sh | 433 ++++++++++++++++++ 9 files changed, 596 insertions(+), 30 deletions(-) rename {.ci => tools}/windows/install-vulkan-sdk.ps1 (79%) create mode 100644 tools/windows/install-vulkan-sdk.sh create mode 100644 tools/windows/load-msvc-env.ps1 create mode 100644 tools/windows/load-msvc-env.sh create mode 100644 tools/windows/vcvarsall.sh 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/.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/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/.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 "$?"' From dd2a30dd2b84cdeca404a97417469df4b9b4b3a1 Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 11 Nov 2025 22:24:28 +0000 Subject: [PATCH 20/28] [hle] Up fw version to 21, stub extra functions from 21+ --- src/core/hle/api_version.h | 10 ++-- .../am/service/application_functions.cpp | 5 ++ .../service/am/service/audio_controller.cpp | 1 + .../hle/service/audio/audio_controller.cpp | 1 + src/core/hle/service/audio/audio_device.cpp | 1 + src/core/hle/service/hid/hid_debug_server.cpp | 9 ++++ src/core/hle/service/hid/hid_server.cpp | 51 +++++++++++++++++++ .../hle/service/hid/hid_system_server.cpp | 8 +++ src/core/hle/service/nifm/nifm.cpp | 1 + .../olsc/remote_storage_controller.cpp | 10 +++- .../set/firmware_debug_settings_server.cpp | 2 + .../service/set/system_settings_server.cpp | 13 +++++ 12 files changed, 105 insertions(+), 7 deletions(-) diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h index e18397cc04..6677849821 100644 --- a/src/core/hle/api_version.h +++ b/src/core/hle/api_version.h @@ -14,8 +14,8 @@ namespace HLE::ApiVersion { // Horizon OS version constants. -constexpr u8 HOS_VERSION_MAJOR = 20; -constexpr u8 HOS_VERSION_MINOR = 1; +constexpr u8 HOS_VERSION_MAJOR = 21; +constexpr u8 HOS_VERSION_MINOR = 0; constexpr u8 HOS_VERSION_MICRO = 1; // NintendoSDK version constants. @@ -24,9 +24,9 @@ constexpr u8 SDK_REVISION_MAJOR = 1; constexpr u8 SDK_REVISION_MINOR = 0; constexpr char PLATFORM_STRING[] = "NX"; -constexpr char VERSION_HASH[] = "9ffad64d79dd150490201461bdf66c8db963f57d"; -constexpr char DISPLAY_VERSION[] = "20.1.1"; -constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 20.1.1-1.0"; +constexpr char VERSION_HASH[] = "f6b2425b6888a66590db104fc734891696e0ecb3"; +constexpr char DISPLAY_VERSION[] = "21.0.0"; +constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 21.0.0-1.0"; // Atmosphere version constants. diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index eacc345e15..e70ed2cccd 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -89,6 +89,11 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ {190, nullptr, "SendServerMaintenanceOverlayNotification"}, {200, nullptr, "GetLastApplicationExitReason"}, {210, D<&IApplicationFunctions::GetUnknownEvent210>, "Unknown210"}, + {220, nullptr, "Unknown220"}, // [20.0.0+] + {300, nullptr, "Unknown300"}, // [20.0.0+] + {310, nullptr, "Unknown310"}, // [20.0.0+] + {320, nullptr, "Unknown320"}, // [20.0.0+] + {330, nullptr, "Unknown330"}, // [20.0.0+] {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, {1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"}, diff --git a/src/core/hle/service/am/service/audio_controller.cpp b/src/core/hle/service/am/service/audio_controller.cpp index ad731c7bd3..b0e39ba4f7 100644 --- a/src/core/hle/service/am/service/audio_controller.cpp +++ b/src/core/hle/service/am/service/audio_controller.cpp @@ -15,6 +15,7 @@ IAudioController::IAudioController(Core::System& system_) {2, D<&IAudioController::GetLibraryAppletExpectedMasterVolume>, "GetLibraryAppletExpectedMasterVolume"}, {3, D<&IAudioController::ChangeMainAppletMasterVolume>, "ChangeMainAppletMasterVolume"}, {4, D<&IAudioController::SetTransparentVolumeRate>, "SetTransparentVolumeRate"}, + {5, nullptr, "Unknown5"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp index 4b6f468c92..a9129e3802 100644 --- a/src/core/hle/service/audio/audio_controller.cpp +++ b/src/core/hle/service/audio/audio_controller.cpp @@ -68,6 +68,7 @@ IAudioController::IAudioController(Core::System& system_) {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"}, {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"}, + {10200, nullptr, "Unknown10200"}, // [20.0.0+] {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"}, {50001, nullptr, "OverrideDefaultTargetForDebug"}, {50003, nullptr, "SetForceOverrideExternalDeviceNameForDebug"}, diff --git a/src/core/hle/service/audio/audio_device.cpp b/src/core/hle/service/audio/audio_device.cpp index 782dddc804..58d12815f2 100644 --- a/src/core/hle/service/audio/audio_device.cpp +++ b/src/core/hle/service/audio/audio_device.cpp @@ -35,6 +35,7 @@ IAudioDevice::IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u {18, nullptr, "ReleaseAudioOutputDeviceNotification"}, // 17.0.0+ {19, nullptr, "SetAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+ {20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"} // 18.0.0+ + {21, nullptr, "IsActiveOutputDeviceEstimatedLowLatency"} // 21.0.0+ }; RegisterHandlers(functions); diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp index 0a5d479094..14e48dade3 100644 --- a/src/core/hle/service/hid/hid_debug_server.cpp +++ b/src/core/hle/service/hid/hid_debug_server.cpp @@ -38,6 +38,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr r {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, {310, C<&IHidServer::ResetSevenSixAxisSensorTimestamp>, "ResetSevenSixAxisSensorTimestamp"}, + {320, nullptr, "EnableNpadImu"}, //21.0.0+ + {321, nullptr, "DisableNpadImu"}, //21.0.0+ {400, C<&IHidServer::IsUsbFullKeyControllerEnabled>, "IsUsbFullKeyControllerEnabled"}, {401, nullptr, "EnableUsbFullKeyController"}, {402, nullptr, "IsUsbFullKeyControllerConnected"}, @@ -187,7 +189,56 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr r {1002, C<&IHidServer::SetTouchScreenConfiguration>, "SetTouchScreenConfiguration"}, {1003, C<&IHidServer::IsFirmwareUpdateNeededForNotification>, "IsFirmwareUpdateNeededForNotification"}, {1004, C<&IHidServer::SetTouchScreenResolution>, "SetTouchScreenResolution"}, + {1270, nullptr, "DeleteButtonConfigStorageRight"}, + {1271, nullptr, "IsUsingCustomButtonConfig"}, + {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, + {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, + {1274, nullptr, "SetDefaultButtonConfig"}, + {1275, nullptr, "SetAllDefaultButtonConfig"}, + {1276, nullptr, "SetHidButtonConfigEmbedded"}, + {1277, nullptr, "SetHidButtonConfigFull"}, + {1278, nullptr, "SetHidButtonConfigLeft"}, + {1279, nullptr, "SetHidButtonConfigRight"}, + {1280, nullptr, "GetHidButtonConfigEmbedded"}, + {1281, nullptr, "GetHidButtonConfigFull"}, + {1282, nullptr, "GetHidButtonConfigLeft"}, + {1283, nullptr, "GetHidButtonConfigRight"}, + {1284, nullptr, "GetButtonConfigStorageEmbedded"}, + {1285, nullptr, "GetButtonConfigStorageFull"}, + {1286, nullptr, "GetButtonConfigStorageLeft"}, + {1287, nullptr, "GetButtonConfigStorageRight"}, + {1288, nullptr, "SetButtonConfigStorageEmbedded"}, + {1289, nullptr, "SetButtonConfigStorageFull"}, + {1290, nullptr, "SetButtonConfigStorageLeft"}, + {1291, nullptr, "SetButtonConfigStorageRight"}, + {1308, nullptr, "SetButtonConfigVisible"}, + {1309, nullptr, "IsButtonConfigVisible"}, + {1320, nullptr, "WakeTouchScreenUp"}, + {1321, nullptr, "PutTouchScreenToSleep"}, + {1322, nullptr, "AcquireTouchScreenAsyncWakeCompletedEvent"}, + {1323, nullptr, "StartTouchScreenAutoTuneForSystemSettings"}, + {1324, nullptr, "AcquireTouchScreenAutoTuneCompletedEvent"}, + {1325, nullptr, "IsTouchScreenAutoTuneRequiredForRepairProviderReplacement"}, + {1420, nullptr, "GetAppletResourceProperty"}, {2000, nullptr, "ActivateDigitizer"}, + {3000, nullptr, "GetDebugPadGenericPadMap"}, + {3001, nullptr, "SetDebugPadGenericPadMap"}, + {3002, nullptr, "ResetDebugPadGenericPadMap"}, + {3003, nullptr, "GetDebugPadKeyboardMap"}, + {3004, nullptr, "SetDebugPadKeyboardMap"}, + {3005, nullptr, "ResetDebugPadKeyboardMap"}, + {3006, nullptr, "GetFullKeyGenericPadMap"}, + {3007, nullptr, "SetFullKeyGenericPadMap"}, + {3008, nullptr, "ResetFullKeyGenericPadMap"}, + {3009, nullptr, "GetFullKeyKeyboardMap"}, + {3010, nullptr, "SetFullKeyKeyboardMap"}, + {3011, nullptr, "ResetFullKeyKeyboardMap"}, + {3012, nullptr, "GetDebugPadGenericPadMap"}, //21.0.0+ + {3013, nullptr, "SetDebugPadGenericPadMap"}, //21.0.0+ + {3014, nullptr, "GetDebugPadKeyboardMap"}, //21.0.0+ + {3015, nullptr, "SetDebugPadKeyboardMap"}, //21.0.0+ + {3150, nullptr, "SetMouseLibraryVersion"}, //21.0.0+ + // What? -- {12010, nullptr, "SetButtonConfigLeft"}, }; // clang-format on diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 0f79844c94..91789e38da 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -96,9 +96,12 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr, "GetSecondarySave"}, {23, nullptr, "TouchSecondarySave"}, {24, nullptr, "GetSecondarySaveDataInfo"}, {25, nullptr, "RegisterDownloadSaveDataTransferTaskForAutonomyRegistration"}, - {900, nullptr, "Unknown900"}, + {26, nullptr, "Unknown26"}, //20.0.0+ + {27, nullptr, "Unknown27"}, //20.0.0+ + {28, nullptr, "Unknown28"}, //20.0.0+ + {29, nullptr, "Unknown29"}, //21.0.0+ + {800, nullptr, "Unknown800"}, //20.0.0+ + {900, nullptr, "SetLoadedDataMissing"}, + {901, nullptr, "Unknown901"}, //20.2.0+ }; // clang-format on diff --git a/src/core/hle/service/set/firmware_debug_settings_server.cpp b/src/core/hle/service/set/firmware_debug_settings_server.cpp index b3a5e623b6..5977d3f20a 100644 --- a/src/core/hle/service/set/firmware_debug_settings_server.cpp +++ b/src/core/hle/service/set/firmware_debug_settings_server.cpp @@ -18,6 +18,8 @@ IFirmwareDebugSettingsServer::IFirmwareDebugSettingsServer(Core::System& system_ {21, nullptr, "SetAllowedSslHosts"}, {22, nullptr, "SetHostFsMountPoint"}, {23, nullptr, "SetMemoryUsageRateFlag"}, + {24, nullptr, "CommitSettings"}, //20.0.0+ + {27, nullptr, "SetHttpAuthConfigs"}, //21.0.0+ }; // clang-format on diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index 22e12b1b9c..ba517daf40 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -307,6 +307,19 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {222, nullptr, "SetForceMonauralOutputFlag"}, //17.0.0+ {251, nullptr, "GetAccountIdentificationSettings"}, //18.0.0+ {252, nullptr, "SetAccountIdentificationSettings"}, //18.0.0+ + {263, nullptr, "AcquireVphymDirtyFlagEventHandle"}, //20.0.0+ + {264, nullptr, "GetVphymDirtyFlags"}, //20.0.0+ + {282, nullptr, "ConvertToProductModel"}, //20.0.0+ + {283, nullptr, "ConvertToProductModelName"}, //20.0.0+ + {289, nullptr, "GetDefaultAccountIdentificationFlagSet"}, //20.0.0+ + {300, nullptr, "AcquirePushNotificationDirtyFlagEventHandle"}, //20.0.0+ + {301, nullptr, "GetPushNotificationDirtyFlags"}, //20.0.0+ + {306, nullptr, "GetPinCodeReregistrationGuideAccounts"}, //20.0.0+ + {307, nullptr, "SetPinCodeReregistrationGuideAccounts"}, //20.0.0+ + {315, nullptr, "GetHttpAuthConfigs"}, //21.0.0+ + {319, nullptr, "GetAccountUserSettings"}, //21.0.0+ + {320, nullptr, "SetAccountUserSettings"}, //21.0.0+ + {321, nullptr, "GetDefaultAccountUserSettings"}, //21.0.0+ }; // clang-format on From 6368b3dbde191a44755ddaf8e69c4d80da0c4d3e Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 11 Nov 2025 22:28:38 +0000 Subject: [PATCH 21/28] license --- src/core/hle/service/am/service/audio_controller.cpp | 3 +++ src/core/hle/service/audio/audio_controller.cpp | 3 +++ src/core/hle/service/audio/audio_device.cpp | 3 +++ src/core/hle/service/hid/hid_system_server.cpp | 3 +++ src/core/hle/service/olsc/remote_storage_controller.cpp | 3 +++ src/core/hle/service/set/firmware_debug_settings_server.cpp | 3 +++ 6 files changed, 18 insertions(+) diff --git a/src/core/hle/service/am/service/audio_controller.cpp b/src/core/hle/service/am/service/audio_controller.cpp index b0e39ba4f7..76c1566153 100644 --- a/src/core/hle/service/am/service/audio_controller.cpp +++ b/src/core/hle/service/am/service/audio_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp index a9129e3802..4456869d35 100644 --- a/src/core/hle/service/audio/audio_controller.cpp +++ b/src/core/hle/service/audio/audio_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio_device.cpp b/src/core/hle/service/audio/audio_device.cpp index 58d12815f2..5a92290963 100644 --- a/src/core/hle/service/audio/audio_device.cpp +++ b/src/core/hle/service/audio/audio_device.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 91789e38da..7e1949568f 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/olsc/remote_storage_controller.cpp b/src/core/hle/service/olsc/remote_storage_controller.cpp index 08bf70ba90..7e04b7c042 100644 --- a/src/core/hle/service/olsc/remote_storage_controller.cpp +++ b/src/core/hle/service/olsc/remote_storage_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/firmware_debug_settings_server.cpp b/src/core/hle/service/set/firmware_debug_settings_server.cpp index 5977d3f20a..b7c66047e6 100644 --- a/src/core/hle/service/set/firmware_debug_settings_server.cpp +++ b/src/core/hle/service/set/firmware_debug_settings_server.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later From b28648f234974e3d7f81f61dc8163e52f4df01cd Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 11 Nov 2025 17:30:56 -0500 Subject: [PATCH 22/28] remove fw warning + fix build Signed-off-by: crueter --- src/core/hle/api_version.h | 2 +- src/core/hle/service/audio/audio_device.cpp | 4 ++-- src/frontend_common/firmware_manager.cpp | 6 ------ src/frontend_common/firmware_manager.h | 4 +--- src/yuzu/main_window.cpp | 21 +-------------------- 5 files changed, 5 insertions(+), 32 deletions(-) diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h index 6677849821..0317fa1c9f 100644 --- a/src/core/hle/api_version.h +++ b/src/core/hle/api_version.h @@ -16,7 +16,7 @@ namespace HLE::ApiVersion { constexpr u8 HOS_VERSION_MAJOR = 21; constexpr u8 HOS_VERSION_MINOR = 0; -constexpr u8 HOS_VERSION_MICRO = 1; +constexpr u8 HOS_VERSION_MICRO = 0; // NintendoSDK version constants. diff --git a/src/core/hle/service/audio/audio_device.cpp b/src/core/hle/service/audio/audio_device.cpp index 5a92290963..1f7c384846 100644 --- a/src/core/hle/service/audio/audio_device.cpp +++ b/src/core/hle/service/audio/audio_device.cpp @@ -37,8 +37,8 @@ IAudioDevice::IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u {17, nullptr, "AcquireAudioOutputDeviceNotification"}, // 17.0.0+ {18, nullptr, "ReleaseAudioOutputDeviceNotification"}, // 17.0.0+ {19, nullptr, "SetAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+ - {20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"} // 18.0.0+ - {21, nullptr, "IsActiveOutputDeviceEstimatedLowLatency"} // 21.0.0+ + {20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+ + {21, nullptr, "IsActiveOutputDeviceEstimatedLowLatency"} // 21.0.0+ }; RegisterHandlers(functions); diff --git a/src/frontend_common/firmware_manager.cpp b/src/frontend_common/firmware_manager.cpp index be3014e3f2..6f3f409aa8 100644 --- a/src/frontend_common/firmware_manager.cpp +++ b/src/frontend_common/firmware_manager.cpp @@ -121,18 +121,12 @@ FirmwareManager::FirmwareCheckResult FirmwareManager::VerifyFirmware(Core::Syste return ErrorFirmwareMissing; } else { const auto pair = GetFirmwareVersion(system); - const auto firmware_data = pair.first; const auto result = pair.second; if (result.IsError()) { LOG_INFO(Frontend, "Unable to read firmware"); return ErrorFirmwareCorrupted; } - - // TODO: update this whenever newer firmware is properly supported - if (firmware_data.major > 20) { - return ErrorFirmwareTooNew; - } } return FirmwareGood; diff --git a/src/frontend_common/firmware_manager.h b/src/frontend_common/firmware_manager.h index 40814a11f8..5e02ad52f6 100644 --- a/src/frontend_common/firmware_manager.h +++ b/src/frontend_common/firmware_manager.h @@ -58,7 +58,6 @@ enum FirmwareCheckResult { FirmwareGood, ErrorFirmwareMissing, ErrorFirmwareCorrupted, - ErrorFirmwareTooNew, }; static constexpr std::array FIRMWARE_CHECK_STRINGS = { @@ -66,8 +65,7 @@ static constexpr std::array FIRMWARE_CHECK_STRINGS = { "Firmware missing. Firmware is required to run certain games and use the Home Menu. " "Eden only works with firmware 19.0.1 and earlier.", "Firmware reported as present, but was unable to be read. Check for decryption keys and " - "redump firmware if necessary.", - "Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier.", + "redump firmware if necessary." }; /** diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 1547c96dd7..01397241eb 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -4067,26 +4067,7 @@ void MainWindow::OnHomeMenu() { QMessageBox::warning(this, tr("Firmware Corrupted"), tr(FirmwareManager::GetFirmwareCheckString(result))); return; - case FirmwareManager::ErrorFirmwareTooNew: { - if (!UISettings::values.show_fw_warning.GetValue()) break; - - QMessageBox box(QMessageBox::Warning, - tr("Firmware Too New"), - tr(FirmwareManager::GetFirmwareCheckString(result)) + tr("\nContinue anyways?"), - QMessageBox::Yes | QMessageBox::No, - this); - - QCheckBox *checkbox = new QCheckBox(tr("Don't show again")); - box.setCheckBox(checkbox); - - int button = box.exec(); - if (checkbox->isChecked()) { - UISettings::values.show_fw_warning.SetValue(false); - } - - if (button == static_cast(QMessageBox::No)) return; - break; - } default: + default: break; } From 72252c8c68aa4bcbd7cd751a30aa76741e7d0bd6 Mon Sep 17 00:00:00 2001 From: JPikachu Date: Wed, 12 Nov 2025 00:17:24 +0000 Subject: [PATCH 23/28] update --- src/core/crypto/key_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 50945eee91..f7da4b322d 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -41,7 +41,7 @@ namespace Core::Crypto { namespace { -constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; +constexpr u64 CURRENT_CRYPTO_REVISION = 0x15; using Common::AsArray; From 7ca5c47fc97a5ab25cb1f38399f722c6dbe4ed16 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Wed, 12 Nov 2025 12:42:37 +0100 Subject: [PATCH 24/28] add prepo --- src/core/file_sys/savedata_factory.cpp | 1 + src/core/hle/service/prepo/prepo.cpp | 60 ++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index dda8d526d3..4dfb0ded51 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -95,6 +95,7 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { case SaveDataSpaceId::System: return "/system/"; case SaveDataSpaceId::User: + case SaveDataSpaceId::SdUser: return "/user/"; case SaveDataSpaceId::Temporary: return "/temp/"; diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 14e8df63a0..5a81a050a0 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -27,8 +27,10 @@ public: {10200, &PlayReport::RequestImmediateTransmission, "RequestImmediateTransmission"}, {10300, &PlayReport::GetTransmissionStatus, "GetTransmissionStatus"}, {10400, &PlayReport::GetSystemSessionId, "GetSystemSessionId"}, - {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"}, - {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"}, + {20100, &PlayReport::SaveSystemReportOld, "SaveSystemReport"}, +{20101, &PlayReport::SaveSystemReportWithUserOld, "SaveSystemReportWithUser"}, +{20102, &PlayReport::SaveSystemReport, "SaveSystemReport"}, +{20103, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"}, {20200, nullptr, "SetOperationMode"}, {30100, nullptr, "ClearStorage"}, {30200, nullptr, "ClearStatistics"}, @@ -121,24 +123,21 @@ private: rb.Push(system_session_id); } - void SaveSystemReport(HLERequestContext& ctx) { + void SaveSystemReportOld(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto title_id = rp.PopRaw(); const auto data1 = ctx.ReadBufferA(0); const auto data2 = ctx.ReadBufferX(0); - LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", + LOG_ERROR(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", title_id, data1.size(), data2.size()); - const auto& reporter{system.GetReporter()}; - reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2}); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } - void SaveSystemReportWithUser(HLERequestContext& ctx) { + void SaveSystemReportWithUserOld(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto user_id = rp.PopRaw(); const auto title_id = rp.PopRaw(); @@ -146,7 +145,7 @@ private: const auto data1 = ctx.ReadBufferA(0); const auto data2 = ctx.ReadBufferX(0); - LOG_DEBUG(Service_PREPO, + LOG_ERROR(Service_PREPO, "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, " "data2_size={:016X}", user_id[1], user_id[0], title_id, data1.size(), data2.size()); @@ -158,6 +157,49 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } + + // (21.0.0+) buffers: [0x9 (X), 0x5 (A)], inbytes: 0x10 + void SaveSystemReport(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto field0 = rp.PopRaw(); + const auto title_id = rp.PopRaw(); + + const auto data_x = ctx.ReadBufferX(0); + const auto data_a = ctx.ReadBufferA(0); + + LOG_ERROR(Service_PREPO, + "called, field0={}, title_id={:016X}, data_a_size={}, data_x_size={}", + field0, title_id, data_a.size(), data_x.size()); + + const auto& reporter{system.GetReporter()}; + reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data_a, data_x}); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + // (21.0.0+) buffers: [0x9 (X), 0x5 (A)], inbytes: 0x20 + void SaveSystemReportWithUser(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto user_id = rp.PopRaw(); + const auto title_id = rp.PopRaw(); + const auto reserved = rp.PopRaw(); + + const auto data_x = ctx.ReadBufferX(0); + const auto data_a = ctx.ReadBufferA(0); + + LOG_ERROR(Service_PREPO, + "called, user_id={:016X}{:016X}, title_id={:016X}, reserved={}, data_a_size={}, data_x_size={}", + user_id[1], user_id[0], title_id, reserved, data_a.size(), data_x.size()); + + const auto& reporter{system.GetReporter()}; + reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data_a, data_x}, + std::nullopt, user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } }; void LoopProcess(Core::System& system) { From 2c51651636471128549ea8357dd2bd1168a96cc8 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Wed, 12 Nov 2025 15:39:47 +0100 Subject: [PATCH 25/28] add atmosphere changes from 21.0.0 branch + correct prepo struct --- src/core/hle/kernel/k_auto_object.cpp | 1 + src/core/hle/kernel/k_auto_object.h | 10 +- src/core/hle/kernel/k_condition_variable.cpp | 22 +- src/core/hle/kernel/k_scheduler.cpp | 3 + src/core/hle/kernel/k_thread.cpp | 2156 +++++++++-------- src/core/hle/kernel/k_thread.h | 25 +- .../hle/service/filesystem/fsp/fsp_srv.cpp | 37 +- src/core/hle/service/prepo/prepo.cpp | 21 +- 8 files changed, 1184 insertions(+), 1091 deletions(-) diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp index 9cd7a9fd51..6b6222151e 100644 --- a/src/core/hle/kernel/k_auto_object.cpp +++ b/src/core/hle/kernel/k_auto_object.cpp @@ -8,6 +8,7 @@ namespace Kernel { KAutoObject* KAutoObject::Create(KAutoObject* obj) { obj->m_ref_count = 1; + obj->m_class_token = obj->GetTypeObj().GetClassToken(); return obj; } diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index c61b10625a..789530ec79 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -74,6 +74,10 @@ protected: return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken(); } + static constexpr bool IsClassTokenDerivedFrom(ClassTokenType self, ClassTokenType base) { + return (self | base) == self; + } + private: const char* m_name; ClassTokenType m_class_token; @@ -84,6 +88,7 @@ private: public: explicit KAutoObject(KernelCore& kernel) : m_kernel(kernel) { + m_class_token = GetStaticTypeObj().GetClassToken(); RegisterWithKernel(); } virtual ~KAutoObject() = default; @@ -107,11 +112,11 @@ public: } bool IsDerivedFrom(const TypeObj& rhs) const { - return this->GetTypeObj().IsDerivedFrom(rhs); + return TypeObj::IsClassTokenDerivedFrom(m_class_token, rhs.GetClassToken()); } bool IsDerivedFrom(const KAutoObject& rhs) const { - return this->IsDerivedFrom(rhs.GetTypeObj()); + return TypeObj::IsClassTokenDerivedFrom(m_class_token, rhs.m_class_token); } template @@ -180,6 +185,7 @@ protected: private: std::atomic m_ref_count{}; + ClassTokenType m_class_token{}; // neu: gespeicherter Klassentoken zur Devitalisierung }; class KAutoObjectWithListContainer; diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index 94ea3527ab..d6cfc3b7a2 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -23,8 +23,8 @@ bool ReadFromUser(KernelCore& kernel, u32* out, KProcessAddress address) { return true; } -bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) { - GetCurrentMemory(kernel).Write32(GetInteger(address), *p); +bool WriteToUser(KernelCore& kernel, KProcessAddress address, u32 val) { + GetCurrentMemory(kernel).Write32(GetInteger(address), val); return true; } @@ -133,7 +133,7 @@ Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress a // Write the value to userspace. Result result{ResultSuccess}; - if (WriteToUser(kernel, addr, std::addressof(next_value))) [[likely]] { + if (WriteToUser(kernel, addr, next_value)) { result = ResultSuccess; } else { result = ResultInvalidCurrentMemory; @@ -207,13 +207,13 @@ void KConditionVariable::SignalImpl(KThread* thread) { // TODO(bunnei): We should call CanAccessAtomic(..) here. can_access = true; - if (can_access) [[likely]] { + if (can_access) { UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag, Svc::HandleWaitMask); } } - if (can_access) [[likely]] { + if (can_access) { if (prev_tag == Svc::InvalidHandle) { // If nobody held the lock previously, we're all good. thread->EndWait(ResultSuccess); @@ -225,7 +225,7 @@ void KConditionVariable::SignalImpl(KThread* thread) { static_cast(prev_tag & ~Svc::HandleWaitMask)) .ReleasePointerUnsafe(); - if (owner_thread) [[likely]] { + if (owner_thread) { // Add the thread as a waiter on the owner. owner_thread->AddWaiter(thread); owner_thread->Close(); @@ -261,8 +261,8 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { // If we have no waiters, clear the has waiter flag. if (it == m_tree.end() || it->GetConditionVariableKey() != cv_key) { - const u32 has_waiter_flag{}; - WriteToUser(m_kernel, cv_key, std::addressof(has_waiter_flag)); + constexpr u32 HasNoWaiterFlag = 0; + WriteToUser(m_kernel, cv_key, HasNoWaiterFlag); } } } @@ -305,13 +305,13 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti // Write to the cv key. { - const u32 has_waiter_flag = 1; - WriteToUser(m_kernel, key, std::addressof(has_waiter_flag)); + constexpr u32 HasWaiterFlag = 1; + WriteToUser(m_kernel, key, HasWaiterFlag); std::atomic_thread_fence(std::memory_order_seq_cst); } // Write the value to userspace. - if (!WriteToUser(m_kernel, addr, std::addressof(next_value))) { + if (!WriteToUser(m_kernel, addr, next_value)) { slp.CancelSleep(); R_THROW(ResultInvalidCurrentMemory); } diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 27d1c38466..55e3b2ec7f 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -381,6 +381,9 @@ void KScheduler::SwitchThread(KThread* next_thread) { // Set the new Thread Local region. // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress())); + + // Update the thread's cpu time differential in TLS, if relevant. + next_thread->UpdateTlsThreadCpuTime(cur_tick); } void KScheduler::ScheduleImpl() { diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 6b68a56ab4..bfc8691e95 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "common/assert.h" #include "common/bit_util.h" @@ -67,1385 +68,1402 @@ static void ResetThreadContext64(Kernel::Svc::ThreadContext& ctx, u64 stack_top, } // namespace namespace Kernel { + namespace { + + struct ThreadLocalRegion { + static constexpr std::size_t MessageBufferSize = 0x100; + std::array message_buffer; + std::atomic_uint16_t disable_count; + std::atomic_uint16_t interrupt_flag; + std::atomic_uint8_t cache_maintenance_flag; + std::atomic_int64_t thread_cpu_time; + }; + static_assert(offsetof(ThreadLocalRegion, disable_count) == 0x100); + static_assert(offsetof(ThreadLocalRegion, interrupt_flag) == 0x102); + static_assert(offsetof(ThreadLocalRegion, cache_maintenance_flag) == 0x104); + static_assert(offsetof(ThreadLocalRegion, thread_cpu_time) == 0x108); + + class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { + public: + explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel) + : KThreadQueueWithoutEndWait(kernel) {} + }; + + class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue { + public: + explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel, KThread::WaiterList* wl) + : KThreadQueue(kernel), m_wait_list(wl) {} + + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + // Remove the thread from the wait list. + m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); + + // Invoke the base cancel wait handler. + KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + } -namespace { + private: + KThread::WaiterList* m_wait_list{}; + }; + + } // namespace + + KThread::KThread(KernelCore& kernel) + : KAutoObjectWithSlabHeapAndContainer{kernel}, m_activity_pause_lock{kernel} {} + KThread::~KThread() = default; + + Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, + s32 prio, s32 virt_core, KProcess* owner, ThreadType type) { + // Assert parameters are valid. + ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) || + (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority)); + ASSERT((owner != nullptr) || (type != ThreadType::User)); + ASSERT(0 <= virt_core && virt_core < static_cast(Common::BitSize())); + + // Convert the virtual core to a physical core. + const s32 phys_core = Core::Hardware::VirtualToPhysicalCoreMap[virt_core]; + ASSERT(0 <= phys_core && phys_core < static_cast(Core::Hardware::NUM_CPU_CORES)); + + // First, clear the TLS address. + m_tls_address = {}; + + // Next, assert things based on the type. + switch (type) { + case ThreadType::Main: + ASSERT(arg == 0); + [[fallthrough]]; + case ThreadType::User: + ASSERT(((owner == nullptr) || + (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); + ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) || + (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); + break; + case ThreadType::HighPriority: + case ThreadType::Dummy: + break; + case ThreadType::Kernel: + UNIMPLEMENTED(); + break; + default: + ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast(type)); + break; + } + m_thread_type = type; -struct ThreadLocalRegion { - static constexpr std::size_t MessageBufferSize = 0x100; - std::array message_buffer; - std::atomic_uint16_t disable_count; - std::atomic_uint16_t interrupt_flag; -}; + // Set the ideal core ID and affinity mask. + m_virtual_ideal_core_id = virt_core; + m_physical_ideal_core_id = phys_core; + m_virtual_affinity_mask = 1ULL << virt_core; + m_physical_affinity_mask.SetAffinity(phys_core, true); -class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { -public: - explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel) - : KThreadQueueWithoutEndWait(kernel) {} -}; + // Set the thread state. + m_thread_state = (type == ThreadType::Main || type == ThreadType::Dummy) + ? ThreadState::Runnable + : ThreadState::Initialized; -class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue { -public: - explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel, KThread::WaiterList* wl) - : KThreadQueue(kernel), m_wait_list(wl) {} + // Set TLS address. + m_tls_address = 0; - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { - // Remove the thread from the wait list. - m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); + // Set parent and condvar tree. + m_parent = nullptr; + m_condvar_tree = nullptr; - // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); - } + // Set sync booleans. + m_signaled = false; + m_termination_requested = false; + m_wait_cancelled = false; + m_cancellable = false; -private: - KThread::WaiterList* m_wait_list{}; -}; + // Set core ID and wait result. + m_core_id = phys_core; + m_wait_result = ResultNoSynchronizationObject; -} // namespace + // Set priorities. + m_priority = prio; + m_base_priority = prio; -KThread::KThread(KernelCore& kernel) - : KAutoObjectWithSlabHeapAndContainer{kernel}, m_activity_pause_lock{kernel} {} -KThread::~KThread() = default; - -Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, - s32 prio, s32 virt_core, KProcess* owner, ThreadType type) { - // Assert parameters are valid. - ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) || - (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority)); - ASSERT((owner != nullptr) || (type != ThreadType::User)); - ASSERT(0 <= virt_core && virt_core < static_cast(Common::BitSize())); - - // Convert the virtual core to a physical core. - const s32 phys_core = Core::Hardware::VirtualToPhysicalCoreMap[virt_core]; - ASSERT(0 <= phys_core && phys_core < static_cast(Core::Hardware::NUM_CPU_CORES)); - - // First, clear the TLS address. - m_tls_address = {}; - - // Next, assert things based on the type. - switch (type) { - case ThreadType::Main: - ASSERT(arg == 0); - [[fallthrough]]; - case ThreadType::User: - ASSERT(((owner == nullptr) || - (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); - ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) || - (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); - break; - case ThreadType::HighPriority: - case ThreadType::Dummy: - break; - case ThreadType::Kernel: - UNIMPLEMENTED(); - break; - default: - ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast(type)); - break; - } - m_thread_type = type; - - // Set the ideal core ID and affinity mask. - m_virtual_ideal_core_id = virt_core; - m_physical_ideal_core_id = phys_core; - m_virtual_affinity_mask = 1ULL << virt_core; - m_physical_affinity_mask.SetAffinity(phys_core, true); - - // Set the thread state. - m_thread_state = (type == ThreadType::Main || type == ThreadType::Dummy) - ? ThreadState::Runnable - : ThreadState::Initialized; - - // Set TLS address. - m_tls_address = 0; - - // Set parent and condvar tree. - m_parent = nullptr; - m_condvar_tree = nullptr; - - // Set sync booleans. - m_signaled = false; - m_termination_requested = false; - m_wait_cancelled = false; - m_cancellable = false; - - // Set core ID and wait result. - m_core_id = phys_core; - m_wait_result = ResultNoSynchronizationObject; - - // Set priorities. - m_priority = prio; - m_base_priority = prio; - - // Initialize sleeping queue. - m_wait_queue = nullptr; - - // Set suspend flags. - m_suspend_request_flags = 0; - m_suspend_allowed_flags = static_cast(ThreadState::SuspendFlagMask); - - // We're neither debug attached, nor are we nesting our priority inheritance. - m_debug_attached = false; - m_priority_inheritance_count = 0; - - // We haven't been scheduled, and we have done no light IPC. - m_schedule_count = -1; - m_last_scheduled_tick = 0; - m_light_ipc_data = nullptr; - - // We're not waiting for a lock, and we haven't disabled migration. - m_waiting_lock_info = nullptr; - m_num_core_migration_disables = 0; - - // We have no waiters, but we do have an entrypoint. - m_num_kernel_waiters = 0; - - // Set our current core id. - m_current_core_id = phys_core; - - // We haven't released our resource limit hint, and we've spent no time on the cpu. - m_resource_limit_release_hint = false; - m_cpu_time = 0; - - // Set debug context. - m_stack_top = user_stack_top; - m_argument = arg; - - // Clear our stack parameters. - std::memset(static_cast(std::addressof(this->GetStackParameters())), 0, - sizeof(StackParameters)); - - // Set parent, if relevant. - if (owner != nullptr) { - // Setup the TLS, if needed. - if (type == ThreadType::User) { - R_TRY(owner->CreateThreadLocalRegion(std::addressof(m_tls_address))); - owner->GetMemory().ZeroBlock(m_tls_address, Svc::ThreadLocalRegionSize); - } + // Initialize sleeping queue. + m_wait_queue = nullptr; - m_parent = owner; - m_parent->Open(); - } + // Set suspend flags. + m_suspend_request_flags = 0; + m_suspend_allowed_flags = static_cast(ThreadState::SuspendFlagMask); - // Initialize thread context. - if (m_parent != nullptr && !m_parent->Is64Bit()) { - ResetThreadContext32(m_thread_context, GetInteger(user_stack_top), GetInteger(func), arg); - } else { - ResetThreadContext64(m_thread_context, GetInteger(user_stack_top), GetInteger(func), arg); - } + // We're neither debug attached, nor are we nesting our priority inheritance. + m_debug_attached = false; + m_priority_inheritance_count = 0; - // Setup the stack parameters. - StackParameters& sp = this->GetStackParameters(); - sp.cur_thread = this; - sp.disable_count = 1; - this->SetInExceptionHandler(); + // We haven't been scheduled, and we have done no light IPC. + m_schedule_count = -1; + m_last_scheduled_tick = 0; + m_light_ipc_data = nullptr; - // Set thread ID. - m_thread_id = m_kernel.CreateNewThreadID(); + // We're not waiting for a lock, and we haven't disabled migration. + m_waiting_lock_info = nullptr; + m_num_core_migration_disables = 0; - // We initialized! - m_initialized = true; + // We have no waiters, but we do have an entrypoint. + m_num_kernel_waiters = 0; - // Register ourselves with our parent process. - if (m_parent != nullptr) { - m_parent->RegisterThread(this); - if (m_parent->IsSuspended()) { - RequestSuspend(SuspendType::Process); - } - } + // Set our current core id. + m_current_core_id = phys_core; - R_SUCCEED(); -} + // We haven't released our resource limit hint, and we've spent no time on the cpu. + m_resource_limit_release_hint = false; + m_cpu_time = 0; -Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, - KProcessAddress user_stack_top, s32 prio, s32 core, - KProcess* owner, ThreadType type, - std::function&& init_func) { - // Initialize the thread. - R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); + // Set debug context. + m_stack_top = user_stack_top; + m_argument = arg; - // Initialize emulation parameters. - thread->m_host_context = std::make_shared(std::move(init_func)); + // Clear our stack parameters. + std::memset(static_cast(std::addressof(this->GetStackParameters())), 0, + sizeof(StackParameters)); - R_SUCCEED(); -} + // Set parent, if relevant. + if (owner != nullptr) { + // Setup the TLS, if needed. + if (type == ThreadType::User) { + R_TRY(owner->CreateThreadLocalRegion(std::addressof(m_tls_address))); + owner->GetMemory().ZeroBlock(m_tls_address, Svc::ThreadLocalRegionSize); + } -Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) { - // Initialize the thread. - R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); + m_parent = owner; + m_parent->Open(); + } - // Initialize emulation parameters. - thread->m_stack_parameters.disable_count = 0; + // Initialize thread context. + if (m_parent != nullptr && !m_parent->Is64Bit()) { + ResetThreadContext32(m_thread_context, GetInteger(user_stack_top), GetInteger(func), arg); + } else { + ResetThreadContext64(m_thread_context, GetInteger(user_stack_top), GetInteger(func), arg); + } - R_SUCCEED(); -} + // Setup the stack parameters. + StackParameters& sp = this->GetStackParameters(); + sp.cur_thread = this; + sp.disable_count = 1; + this->SetInExceptionHandler(); -Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { - R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, - ThreadType::Main, system.GetCpuManager().GetGuestActivateFunc())); -} + // Set thread ID. + m_thread_id = m_kernel.CreateNewThreadID(); -Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { - R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, - ThreadType::Main, system.GetCpuManager().GetIdleThreadStartFunc())); -} + // We initialized! + m_initialized = true; -Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, s32 virt_core) { - R_RETURN(InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, - ThreadType::HighPriority, - system.GetCpuManager().GetShutdownThreadStartFunc())); -} + // Register ourselves with our parent process. + if (m_parent != nullptr) { + m_parent->RegisterThread(this); + if (m_parent->IsSuspended()) { + RequestSuspend(SuspendType::Process); + } + } -Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, - uintptr_t arg, KProcessAddress user_stack_top, s32 prio, - s32 virt_core, KProcess* owner) { - system.Kernel().GlobalSchedulerContext().AddThread(thread); - R_RETURN(InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, system.GetCpuManager().GetGuestThreadFunc())); -} + R_SUCCEED(); + } -Result KThread::InitializeServiceThread(Core::System& system, KThread* thread, - std::function&& func, s32 prio, s32 virt_core, - KProcess* owner) { - system.Kernel().GlobalSchedulerContext().AddThread(thread); - std::function func2{[&system, func_{std::move(func)}] { - // Similar to UserModeThreadStarter. - system.Kernel().CurrentScheduler()->OnThreadStart(); + Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, + KProcessAddress user_stack_top, s32 prio, s32 core, + KProcess* owner, ThreadType type, + std::function&& init_func) { + // Initialize the thread. + R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); - // Run the guest function. - func_(); + // Initialize emulation parameters. + thread->m_host_context = std::make_shared(std::move(init_func)); - // Exit. - Svc::ExitThread(system); - }}; + R_SUCCEED(); + } - R_RETURN(InitializeThread(thread, {}, {}, {}, prio, virt_core, owner, ThreadType::HighPriority, - std::move(func2))); -} + Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) { + // Initialize the thread. + R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); + + // Initialize emulation parameters. + thread->m_stack_parameters.disable_count = 0; -void KThread::PostDestroy(uintptr_t arg) { - KProcess* owner = reinterpret_cast(arg & ~1ULL); - const bool resource_limit_release_hint = (arg & 1); - const s64 hint_value = (resource_limit_release_hint ? 0 : 1); - if (owner != nullptr) { - owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value); - owner->Close(); + R_SUCCEED(); } -} -void KThread::Finalize() { - // If the thread has an owner process, unregister it. - if (m_parent != nullptr) { - m_parent->UnregisterThread(this); + Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { + R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, + ThreadType::Main, system.GetCpuManager().GetGuestActivateFunc())); } - // If the thread has a local region, delete it. - if (m_tls_address != 0) { - ASSERT(m_parent->DeleteThreadLocalRegion(m_tls_address).IsSuccess()); + Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { + R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, + ThreadType::Main, system.GetCpuManager().GetIdleThreadStartFunc())); } - // Release any waiters. - { - ASSERT(m_waiting_lock_info == nullptr); - KScopedSchedulerLock sl{m_kernel}; + Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, + KThreadFunction func, uintptr_t arg, s32 virt_core) { + R_RETURN(InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, + ThreadType::HighPriority, + system.GetCpuManager().GetShutdownThreadStartFunc())); + } - // Check that we have no kernel waiters. - ASSERT(m_num_kernel_waiters == 0); + Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, + uintptr_t arg, KProcessAddress user_stack_top, s32 prio, + s32 virt_core, KProcess* owner) { + system.Kernel().GlobalSchedulerContext().AddThread(thread); + R_RETURN(InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, + ThreadType::User, system.GetCpuManager().GetGuestThreadFunc())); + } - auto it = m_held_lock_info_list.begin(); - while (it != m_held_lock_info_list.end()) { - // Get the lock info. - auto* const lock_info = std::addressof(*it); + Result KThread::InitializeServiceThread(Core::System& system, KThread* thread, + std::function&& func, s32 prio, s32 virt_core, + KProcess* owner) { + system.Kernel().GlobalSchedulerContext().AddThread(thread); + std::function func2{[&system, func_{std::move(func)}] { + // Similar to UserModeThreadStarter. + system.Kernel().CurrentScheduler()->OnThreadStart(); - // The lock shouldn't have a kernel waiter. - ASSERT(!lock_info->GetIsKernelAddressKey()); + // Run the guest function. + func_(); - // Remove all waiters. - while (lock_info->GetWaiterCount() != 0) { - // Get the front waiter. - KThread* const waiter = lock_info->GetHighestPriorityWaiter(); + // Exit. + Svc::ExitThread(system); + }}; - // Remove it from the lock. - if (lock_info->RemoveWaiter(waiter)) { - ASSERT(lock_info->GetWaiterCount() == 0); - } + R_RETURN(InitializeThread(thread, {}, {}, {}, prio, virt_core, owner, ThreadType::HighPriority, + std::move(func2))); + } - // Cancel the thread's wait. - waiter->CancelWait(ResultInvalidState, true); - } + void KThread::PostDestroy(uintptr_t arg) { + KProcess* owner = reinterpret_cast(arg & ~1ULL); + const bool resource_limit_release_hint = (arg & 1); + const s64 hint_value = (resource_limit_release_hint ? 0 : 1); + if (owner != nullptr) { + owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value); + owner->Close(); + } + } - // Remove the held lock from our list. - it = m_held_lock_info_list.erase(it); + void KThread::Finalize() { + // If the thread has an owner process, unregister it. + if (m_parent != nullptr) { + m_parent->UnregisterThread(this); + } - // Free the lock info. - LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + // If the thread has a local region, delete it. + if (m_tls_address != 0) { + ASSERT(m_parent->DeleteThreadLocalRegion(m_tls_address).IsSuccess()); } - } - // Release host emulation members. - m_host_context.reset(); + // Release any waiters. + { + ASSERT(m_waiting_lock_info == nullptr); + KScopedSchedulerLock sl{m_kernel}; - // Perform inherited finalization. - KSynchronizationObject::Finalize(); -} + // Check that we have no kernel waiters. + ASSERT(m_num_kernel_waiters == 0); -bool KThread::IsSignaled() const { - return m_signaled; -} + auto it = m_held_lock_info_list.begin(); + while (it != m_held_lock_info_list.end()) { + // Get the lock info. + auto* const lock_info = std::addressof(*it); -void KThread::OnTimer() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + // The lock shouldn't have a kernel waiter. + ASSERT(!lock_info->GetIsKernelAddressKey()); - // If we're waiting, cancel the wait. - if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->CancelWait(this, ResultTimedOut, false); - } -} + // Remove all waiters. + while (lock_info->GetWaiterCount() != 0) { + // Get the front waiter. + KThread* const waiter = lock_info->GetHighestPriorityWaiter(); + + // Remove it from the lock. + if (lock_info->RemoveWaiter(waiter)) { + ASSERT(lock_info->GetWaiterCount() == 0); + } + + // Cancel the thread's wait. + waiter->CancelWait(ResultInvalidState, true); + } -void KThread::StartTermination() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + // Remove the held lock from our list. + it = m_held_lock_info_list.erase(it); - // Release user exception and unpin, if relevant. - if (m_parent != nullptr) { - m_parent->ReleaseUserException(this); - if (m_parent->GetPinnedThread(GetCurrentCoreId(m_kernel)) == this) { - m_parent->UnpinCurrentThread(); + // Free the lock info. + LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + } } - } - // Set state to terminated. - this->SetState(ThreadState::Terminated); + // Release host emulation members. + m_host_context.reset(); - // Clear the thread's status as running in parent. - if (m_parent != nullptr) { - m_parent->ClearRunningThread(this); + // Perform inherited finalization. + KSynchronizationObject::Finalize(); } - // Clear previous thread in KScheduler. - KScheduler::ClearPreviousThread(m_kernel, this); + bool KThread::IsSignaled() const { + return m_signaled; + } - // Register terminated dpc flag. - this->RegisterDpc(DpcFlag::Terminated); -} + void KThread::OnTimer() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); -void KThread::FinishTermination() { - // Ensure that the thread is not executing on any core. - if (m_parent != nullptr) { - for (std::size_t i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { - KThread* core_thread{}; - do { - core_thread = m_kernel.Scheduler(i).GetSchedulerCurrentThread(); - } while (core_thread == this); + // If we're waiting, cancel the wait. + if (this->GetState() == ThreadState::Waiting) { + m_wait_queue->CancelWait(this, ResultTimedOut, false); } } - // Acquire the scheduler lock. - KScopedSchedulerLock sl{m_kernel}; + void KThread::StartTermination() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Signal. - m_signaled = true; - KSynchronizationObject::NotifyAvailable(); + // Release user exception and unpin, if relevant. + if (m_parent != nullptr) { + m_parent->ReleaseUserException(this); + if (m_parent->GetPinnedThread(GetCurrentCoreId(m_kernel)) == this) { + m_parent->UnpinCurrentThread(); + } + } - // Close the thread. - this->Close(); -} + // Set state to terminated. + this->SetState(ThreadState::Terminated); -void KThread::DoWorkerTaskImpl() { - // Finish the termination that was begun by Exit(). - this->FinishTermination(); -} + // Clear the thread's status as running in parent. + if (m_parent != nullptr) { + m_parent->ClearRunningThread(this); + } -void KThread::Pin(s32 current_core) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + // Clear previous thread in KScheduler. + KScheduler::ClearPreviousThread(m_kernel, this); - // Set ourselves as pinned. - GetStackParameters().is_pinned = true; + // Register terminated dpc flag. + this->RegisterDpc(DpcFlag::Terminated); + } - // Disable core migration. - ASSERT(m_num_core_migration_disables == 0); - { - ++m_num_core_migration_disables; + void KThread::FinishTermination() { + // Ensure that the thread is not executing on any core. + if (m_parent != nullptr) { + for (std::size_t i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { + KThread* core_thread{}; + do { + core_thread = m_kernel.Scheduler(i).GetSchedulerCurrentThread(); + } while (core_thread == this); + } + } - // Save our ideal state to restore when we're unpinned. - m_original_physical_ideal_core_id = m_physical_ideal_core_id; - m_original_physical_affinity_mask = m_physical_affinity_mask; + // Acquire the scheduler lock. + KScopedSchedulerLock sl{m_kernel}; - // Bind ourselves to this core. - const s32 active_core = this->GetActiveCore(); + // Signal. + m_signaled = true; + KSynchronizationObject::NotifyAvailable(); - this->SetActiveCore(current_core); - m_physical_ideal_core_id = current_core; - m_physical_affinity_mask.SetAffinityMask(1ULL << current_core); + // Close the thread. + this->Close(); + } - if (active_core != current_core || - m_physical_affinity_mask.GetAffinityMask() != - m_original_physical_affinity_mask.GetAffinityMask()) { - KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, - m_original_physical_affinity_mask, active_core); - } + void KThread::DoWorkerTaskImpl() { + // Finish the termination that was begun by Exit(). + this->FinishTermination(); } - // Disallow performing thread suspension. - { - // Update our allow flags. - m_suspend_allowed_flags &= ~(1 << (static_cast(SuspendType::Thread) + - static_cast(ThreadState::SuspendShift))); + void KThread::Pin(s32 current_core) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Update our state. - this->UpdateState(); - } + // Set ourselves as pinned. + GetStackParameters().is_pinned = true; - // TODO(bunnei): Update our SVC access permissions. - ASSERT(m_parent != nullptr); -} + // Disable core migration. + ASSERT(m_num_core_migration_disables == 0); + { + ++m_num_core_migration_disables; -void KThread::Unpin() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + // Save our ideal state to restore when we're unpinned. + m_original_physical_ideal_core_id = m_physical_ideal_core_id; + m_original_physical_affinity_mask = m_physical_affinity_mask; - // Set ourselves as unpinned. - this->GetStackParameters().is_pinned = false; + // Bind ourselves to this core. + const s32 active_core = this->GetActiveCore(); - // Enable core migration. - ASSERT(m_num_core_migration_disables == 1); - { - m_num_core_migration_disables--; + this->SetActiveCore(current_core); + m_physical_ideal_core_id = current_core; + m_physical_affinity_mask.SetAffinityMask(1ULL << current_core); - // Restore our original state. - const KAffinityMask old_mask = m_physical_affinity_mask; + if (active_core != current_core || + m_physical_affinity_mask.GetAffinityMask() != + m_original_physical_affinity_mask.GetAffinityMask()) { + KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, + m_original_physical_affinity_mask, active_core); + } + } - m_physical_ideal_core_id = m_original_physical_ideal_core_id; - m_physical_affinity_mask = m_original_physical_affinity_mask; + // Disallow performing thread suspension. + { + // Update our allow flags. + m_suspend_allowed_flags &= ~(1 << (static_cast(SuspendType::Thread) + + static_cast(ThreadState::SuspendShift))); - if (m_physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { - const s32 active_core = this->GetActiveCore(); + // Update our state. + this->UpdateState(); + } - if (!m_physical_affinity_mask.GetAffinity(active_core)) { - if (m_physical_ideal_core_id >= 0) { - this->SetActiveCore(m_physical_ideal_core_id); - } else { - this->SetActiveCore(static_cast( - Common::BitSize() - 1 - - std::countl_zero(m_physical_affinity_mask.GetAffinityMask()))); + // TODO(bunnei): Update our SVC access permissions. + ASSERT(m_parent != nullptr); + } + + void KThread::Unpin() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + + // Set ourselves as unpinned. + this->GetStackParameters().is_pinned = false; + + // Enable core migration. + ASSERT(m_num_core_migration_disables == 1); + { + m_num_core_migration_disables--; + + // Restore our original state. + const KAffinityMask old_mask = m_physical_affinity_mask; + + m_physical_ideal_core_id = m_original_physical_ideal_core_id; + m_physical_affinity_mask = m_original_physical_affinity_mask; + + if (m_physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { + const s32 active_core = this->GetActiveCore(); + + if (!m_physical_affinity_mask.GetAffinity(active_core)) { + if (m_physical_ideal_core_id >= 0) { + this->SetActiveCore(m_physical_ideal_core_id); + } else { + this->SetActiveCore(static_cast( + Common::BitSize() - 1 - + std::countl_zero(m_physical_affinity_mask.GetAffinityMask()))); + } } + KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, old_mask, active_core); } - KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, old_mask, active_core); } - } - // Allow performing thread suspension (if termination hasn't been requested). - if (!this->IsTerminationRequested()) { - // Update our allow flags. - m_suspend_allowed_flags |= (1 << (static_cast(SuspendType::Thread) + - static_cast(ThreadState::SuspendShift))); + // Allow performing thread suspension (if termination hasn't been requested). + if (!this->IsTerminationRequested()) { + // Update our allow flags. + m_suspend_allowed_flags |= (1 << (static_cast(SuspendType::Thread) + + static_cast(ThreadState::SuspendShift))); - // Update our state. - this->UpdateState(); - } + // Update our state. + this->UpdateState(); + } - // TODO(bunnei): Update our SVC access permissions. - ASSERT(m_parent != nullptr); + // TODO(bunnei): Update our SVC access permissions. + ASSERT(m_parent != nullptr); - // Resume any threads that began waiting on us while we were pinned. - for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); - it = m_pinned_waiter_list.erase(it)) { - it->EndWait(ResultSuccess); + // Resume any threads that began waiting on us while we were pinned. + for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); + it = m_pinned_waiter_list.erase(it)) { + it->EndWait(ResultSuccess); + } } -} -u16 KThread::GetUserDisableCount() const { - if (!this->IsUserThread()) { - // We only emulate TLS for user threads - return {}; + u16 KThread::GetUserDisableCount() const { + if (!this->IsUserThread()) { + // We only emulate TLS for user threads + return {}; + } + + auto& memory = this->GetOwnerProcess()->GetMemory(); + return memory.Read16(m_tls_address + offsetof(ThreadLocalRegion, disable_count)); } - auto& memory = this->GetOwnerProcess()->GetMemory(); - return memory.Read16(m_tls_address + offsetof(ThreadLocalRegion, disable_count)); -} + void KThread::SetInterruptFlag() { + if (!this->IsUserThread()) { + // We only emulate TLS for user threads + return; + } -void KThread::SetInterruptFlag() { - if (!this->IsUserThread()) { - // We only emulate TLS for user threads - return; + auto& memory = this->GetOwnerProcess()->GetMemory(); + memory.Write16(m_tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1); } - auto& memory = this->GetOwnerProcess()->GetMemory(); - memory.Write16(m_tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1); -} + void KThread::ClearInterruptFlag() { + if (!this->IsUserThread()) { + // We only emulate TLS for user threads + return; + } -void KThread::ClearInterruptFlag() { - if (!this->IsUserThread()) { - // We only emulate TLS for user threads - return; + auto& memory = this->GetOwnerProcess()->GetMemory(); + memory.Write16(m_tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0); } - auto& memory = this->GetOwnerProcess()->GetMemory(); - memory.Write16(m_tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0); -} - -Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { - KScopedSchedulerLock sl{m_kernel}; + void KThread::UpdateTlsThreadCpuTime(s64 switch_tick) { + if (!this->IsUserThread()) { + return; + } + if (m_tls_address == 0) { + return; + } - // Get the virtual mask. - *out_ideal_core = m_virtual_ideal_core_id; - *out_affinity_mask = m_virtual_affinity_mask; + const s64 value = this->GetCpuTime() - switch_tick; + auto& memory = this->GetOwnerProcess()->GetMemory(); + memory.Write64(m_tls_address + offsetof(ThreadLocalRegion, thread_cpu_time), static_cast(value)); + } - R_SUCCEED(); -} + Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{m_kernel}; -Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { - KScopedSchedulerLock sl{m_kernel}; - ASSERT(m_num_core_migration_disables >= 0); + // Get the virtual mask. + *out_ideal_core = m_virtual_ideal_core_id; + *out_affinity_mask = m_virtual_affinity_mask; - // Select between core mask and original core mask. - if (m_num_core_migration_disables == 0) { - *out_ideal_core = m_physical_ideal_core_id; - *out_affinity_mask = m_physical_affinity_mask.GetAffinityMask(); - } else { - *out_ideal_core = m_original_physical_ideal_core_id; - *out_affinity_mask = m_original_physical_affinity_mask.GetAffinityMask(); + R_SUCCEED(); } - R_SUCCEED(); -} - -Result KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) { - ASSERT(m_parent != nullptr); - ASSERT(v_affinity_mask != 0); - KScopedLightLock lk(m_activity_pause_lock); - - // Set the core mask. - u64 p_affinity_mask = 0; - { - KScopedSchedulerLock sl(m_kernel); + Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{m_kernel}; ASSERT(m_num_core_migration_disables >= 0); - // If we're updating, set our ideal virtual core. - if (core_id != Svc::IdealCoreNoUpdate) { - m_virtual_ideal_core_id = core_id; + // Select between core mask and original core mask. + if (m_num_core_migration_disables == 0) { + *out_ideal_core = m_physical_ideal_core_id; + *out_affinity_mask = m_physical_affinity_mask.GetAffinityMask(); } else { - // Preserve our ideal core id. - core_id = m_virtual_ideal_core_id; - R_UNLESS(((1ULL << core_id) & v_affinity_mask) != 0, ResultInvalidCombination); + *out_ideal_core = m_original_physical_ideal_core_id; + *out_affinity_mask = m_original_physical_affinity_mask.GetAffinityMask(); } - // Set our affinity mask. - m_virtual_affinity_mask = v_affinity_mask; + R_SUCCEED(); + } - // Translate the virtual core to a physical core. - if (core_id >= 0) { - core_id = Core::Hardware::VirtualToPhysicalCoreMap[core_id]; - } + Result KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) { + ASSERT(m_parent != nullptr); + ASSERT(v_affinity_mask != 0); + KScopedLightLock lk(m_activity_pause_lock); - // Translate the virtual affinity mask to a physical one. - while (v_affinity_mask != 0) { - const u64 next = std::countr_zero(v_affinity_mask); - v_affinity_mask &= ~(1ULL << next); - p_affinity_mask |= (1ULL << Core::Hardware::VirtualToPhysicalCoreMap[next]); - } + // Set the core mask. + u64 p_affinity_mask = 0; + { + KScopedSchedulerLock sl(m_kernel); + ASSERT(m_num_core_migration_disables >= 0); - // If we haven't disabled migration, perform an affinity change. - if (m_num_core_migration_disables == 0) { - const KAffinityMask old_mask = m_physical_affinity_mask; + // If we're updating, set our ideal virtual core. + if (core_id != Svc::IdealCoreNoUpdate) { + m_virtual_ideal_core_id = core_id; + } else { + // Preserve our ideal core id. + core_id = m_virtual_ideal_core_id; + R_UNLESS(((1ULL << core_id) & v_affinity_mask) != 0, ResultInvalidCombination); + } - // Set our new ideals. - m_physical_ideal_core_id = core_id; - m_physical_affinity_mask.SetAffinityMask(p_affinity_mask); + // Set our affinity mask. + m_virtual_affinity_mask = v_affinity_mask; - if (m_physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { - const s32 active_core = GetActiveCore(); - - if (active_core >= 0 && !m_physical_affinity_mask.GetAffinity(active_core)) { - const s32 new_core = static_cast( - m_physical_ideal_core_id >= 0 - ? m_physical_ideal_core_id - : Common::BitSize() - 1 - - std::countl_zero(m_physical_affinity_mask.GetAffinityMask())); - SetActiveCore(new_core); - } - KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, old_mask, active_core); + // Translate the virtual core to a physical core. + if (core_id >= 0) { + core_id = Core::Hardware::VirtualToPhysicalCoreMap[core_id]; } - } else { - // Otherwise, we edit the original affinity for restoration later. - m_original_physical_ideal_core_id = core_id; - m_original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); - } - } - - // Update the pinned waiter list. - ThreadQueueImplForKThreadSetProperty wait_queue(m_kernel, std::addressof(m_pinned_waiter_list)); - { - bool retry_update{}; - do { - // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); - // Don't do any further management if our termination has been requested. - R_SUCCEED_IF(this->IsTerminationRequested()); + // Translate the virtual affinity mask to a physical one. + while (v_affinity_mask != 0) { + const u64 next = std::countr_zero(v_affinity_mask); + v_affinity_mask &= ~(1ULL << next); + p_affinity_mask |= (1ULL << Core::Hardware::VirtualToPhysicalCoreMap[next]); + } - // By default, we won't need to retry. - retry_update = false; + // If we haven't disabled migration, perform an affinity change. + if (m_num_core_migration_disables == 0) { + const KAffinityMask old_mask = m_physical_affinity_mask; - // Check if the thread is currently running. - bool thread_is_current{}; - s32 thread_core; - for (thread_core = 0; thread_core < static_cast(Core::Hardware::NUM_CPU_CORES); - ++thread_core) { - if (m_kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) { - thread_is_current = true; - break; - } - } + // Set our new ideals. + m_physical_ideal_core_id = core_id; + m_physical_affinity_mask.SetAffinityMask(p_affinity_mask); - // If the thread is currently running, check whether it's no longer allowed under the - // new mask. - if (thread_is_current && ((1ULL << thread_core) & p_affinity_mask) == 0) { - // If the thread is pinned, we want to wait until it's not pinned. - if (this->GetStackParameters().is_pinned) { - // Verify that the current thread isn't terminating. - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), - ResultTerminationRequested); + if (m_physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { + const s32 active_core = GetActiveCore(); - // Wait until the thread isn't pinned any more. - m_pinned_waiter_list.push_back(GetCurrentThread(m_kernel)); - GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); - } else { - // If the thread isn't pinned, release the scheduler lock and retry until it's - // not current. - retry_update = true; + if (active_core >= 0 && !m_physical_affinity_mask.GetAffinity(active_core)) { + const s32 new_core = static_cast( + m_physical_ideal_core_id >= 0 + ? m_physical_ideal_core_id + : Common::BitSize() - 1 - + std::countl_zero(m_physical_affinity_mask.GetAffinityMask())); + SetActiveCore(new_core); + } + KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, old_mask, active_core); } + } else { + // Otherwise, we edit the original affinity for restoration later. + m_original_physical_ideal_core_id = core_id; + m_original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); } - } while (retry_update); - } + } - R_SUCCEED(); -} + // Update the pinned waiter list. + ThreadQueueImplForKThreadSetProperty wait_queue(m_kernel, std::addressof(m_pinned_waiter_list)); + { + bool retry_update{}; + do { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); -void KThread::SetBasePriority(s32 value) { - ASSERT(Svc::HighestThreadPriority <= value && value <= Svc::LowestThreadPriority); + // Don't do any further management if our termination has been requested. + R_SUCCEED_IF(this->IsTerminationRequested()); - KScopedSchedulerLock sl{m_kernel}; + // By default, we won't need to retry. + retry_update = false; - // Change our base priority. - m_base_priority = value; + // Check if the thread is currently running. + bool thread_is_current{}; + s32 thread_core; + for (thread_core = 0; thread_core < static_cast(Core::Hardware::NUM_CPU_CORES); + ++thread_core) { + if (m_kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) { + thread_is_current = true; + break; + } + } + + // If the thread is currently running, check whether it's no longer allowed under the + // new mask. + if (thread_is_current && ((1ULL << thread_core) & p_affinity_mask) == 0) { + // If the thread is pinned, we want to wait until it's not pinned. + if (this->GetStackParameters().is_pinned) { + // Verify that the current thread isn't terminating. + R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), + ResultTerminationRequested); + + // Wait until the thread isn't pinned any more. + m_pinned_waiter_list.push_back(GetCurrentThread(m_kernel)); + GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); + } else { + // If the thread isn't pinned, release the scheduler lock and retry until it's + // not current. + retry_update = true; + } + } + } while (retry_update); + } - // Perform a priority restoration. - RestorePriority(m_kernel, this); -} + R_SUCCEED(); + } -KThread* KThread::GetLockOwner() const { - return m_waiting_lock_info != nullptr ? m_waiting_lock_info->GetOwner() : nullptr; -} + void KThread::SetBasePriority(s32 value) { + ASSERT(Svc::HighestThreadPriority <= value && value <= Svc::LowestThreadPriority); -void KThread::IncreaseBasePriority(s32 priority) { - ASSERT(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority); - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - ASSERT(!this->GetStackParameters().is_pinned); + KScopedSchedulerLock sl{m_kernel}; - // Set our base priority. - if (m_base_priority > priority) { - m_base_priority = priority; + // Change our base priority. + m_base_priority = value; // Perform a priority restoration. RestorePriority(m_kernel, this); } -} -void KThread::RequestSuspend(SuspendType type) { - KScopedSchedulerLock sl{m_kernel}; + KThread* KThread::GetLockOwner() const { + return m_waiting_lock_info != nullptr ? m_waiting_lock_info->GetOwner() : nullptr; + } - // Note the request in our flags. - m_suspend_request_flags |= - (1U << (static_cast(ThreadState::SuspendShift) + static_cast(type))); + void KThread::IncreaseBasePriority(s32 priority) { + ASSERT(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority); + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(!this->GetStackParameters().is_pinned); - // Try to perform the suspend. - this->TrySuspend(); -} + // Set our base priority. + if (m_base_priority > priority) { + m_base_priority = priority; -void KThread::Resume(SuspendType type) { - KScopedSchedulerLock sl{m_kernel}; + // Perform a priority restoration. + RestorePriority(m_kernel, this); + } + } - // Clear the request in our flags. - m_suspend_request_flags &= - ~(1U << (static_cast(ThreadState::SuspendShift) + static_cast(type))); + void KThread::RequestSuspend(SuspendType type) { + KScopedSchedulerLock sl{m_kernel}; - // Update our state. - this->UpdateState(); -} + // Note the request in our flags. + m_suspend_request_flags |= + (1U << (static_cast(ThreadState::SuspendShift) + static_cast(type))); -void KThread::WaitCancel() { - KScopedSchedulerLock sl{m_kernel}; + // Try to perform the suspend. + this->TrySuspend(); + } - // Check if we're waiting and cancellable. - if (this->GetState() == ThreadState::Waiting && m_cancellable) { - m_wait_cancelled = false; - m_wait_queue->CancelWait(this, ResultCancelled, true); - } else { - // Otherwise, note that we cancelled a wait. - m_wait_cancelled = true; + void KThread::Resume(SuspendType type) { + KScopedSchedulerLock sl{m_kernel}; + + // Clear the request in our flags. + m_suspend_request_flags &= + ~(1U << (static_cast(ThreadState::SuspendShift) + static_cast(type))); + + // Update our state. + this->UpdateState(); } -} -void KThread::TrySuspend() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - ASSERT(this->IsSuspendRequested()); + void KThread::WaitCancel() { + KScopedSchedulerLock sl{m_kernel}; - // Ensure that we have no waiters. - if (this->GetNumKernelWaiters() > 0) { - return; + // Check if we're waiting and cancellable. + if (this->GetState() == ThreadState::Waiting && m_cancellable) { + m_wait_cancelled = false; + m_wait_queue->CancelWait(this, ResultCancelled, true); + } else { + // Otherwise, note that we cancelled a wait. + m_wait_cancelled = true; + } } - ASSERT(this->GetNumKernelWaiters() == 0); - // Perform the suspend. - this->UpdateState(); -} + void KThread::TrySuspend() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(this->IsSuspendRequested()); -void KThread::UpdateState() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + // Ensure that we have no waiters. + if (this->GetNumKernelWaiters() > 0) { + return; + } + ASSERT(this->GetNumKernelWaiters() == 0); + + // Perform the suspend. + this->UpdateState(); + } - // Set our suspend flags in state. - const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); - const auto new_state = - static_cast(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); - m_thread_state.store(new_state, std::memory_order_relaxed); + void KThread::UpdateState() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Note the state change in scheduler. - if (new_state != old_state) { - KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + // Set our suspend flags in state. + const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); + const auto new_state = + static_cast(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); + m_thread_state.store(new_state, std::memory_order_relaxed); + + // Note the state change in scheduler. + if (new_state != old_state) { + KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + } } -} -void KThread::Continue() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::Continue() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Clear our suspend flags in state. - const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); - m_thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed); + // Clear our suspend flags in state. + const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); + m_thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed); - // Note the state change in scheduler. - KScheduler::OnThreadStateChanged(m_kernel, this, old_state); -} + // Note the state change in scheduler. + KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + } -void KThread::CloneFpuStatus() { - // We shouldn't reach here when starting kernel threads. - ASSERT(this->GetOwnerProcess() != nullptr); - ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(m_kernel)); + void KThread::CloneFpuStatus() { + // We shouldn't reach here when starting kernel threads. + ASSERT(this->GetOwnerProcess() != nullptr); + ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(m_kernel)); - m_kernel.CurrentPhysicalCore().CloneFpuStatus(this); -} + m_kernel.CurrentPhysicalCore().CloneFpuStatus(this); + } -Result KThread::SetActivity(Svc::ThreadActivity activity) { - // Lock ourselves. - KScopedLightLock lk(m_activity_pause_lock); + Result KThread::SetActivity(Svc::ThreadActivity activity) { + // Lock ourselves. + KScopedLightLock lk(m_activity_pause_lock); - // Set the activity. - { - // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + // Set the activity. + { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); - // Verify our state. - const auto cur_state = this->GetState(); - R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), - ResultInvalidState); + // Verify our state. + const auto cur_state = this->GetState(); + R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), + ResultInvalidState); - // Either pause or resume. - if (activity == Svc::ThreadActivity::Paused) { - // Verify that we're not suspended. - R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); + // Either pause or resume. + if (activity == Svc::ThreadActivity::Paused) { + // Verify that we're not suspended. + R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); - // Suspend. - this->RequestSuspend(SuspendType::Thread); - } else { - ASSERT(activity == Svc::ThreadActivity::Runnable); + // Suspend. + this->RequestSuspend(SuspendType::Thread); + } else { + ASSERT(activity == Svc::ThreadActivity::Runnable); - // Verify that we're suspended. - R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); + // Verify that we're suspended. + R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); - // Resume. - this->Resume(SuspendType::Thread); + // Resume. + this->Resume(SuspendType::Thread); + } } - } - // If the thread is now paused, update the pinned waiter list. - if (activity == Svc::ThreadActivity::Paused) { - ThreadQueueImplForKThreadSetProperty wait_queue(m_kernel, - std::addressof(m_pinned_waiter_list)); + // If the thread is now paused, update the pinned waiter list. + if (activity == Svc::ThreadActivity::Paused) { + ThreadQueueImplForKThreadSetProperty wait_queue(m_kernel, + std::addressof(m_pinned_waiter_list)); - bool thread_is_current{}; - do { - // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + bool thread_is_current{}; + do { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); - // Don't do any further management if our termination has been requested. - R_SUCCEED_IF(this->IsTerminationRequested()); + // Don't do any further management if our termination has been requested. + R_SUCCEED_IF(this->IsTerminationRequested()); - // By default, treat the thread as not current. - thread_is_current = false; + // By default, treat the thread as not current. + thread_is_current = false; - // Check whether the thread is pinned. - if (this->GetStackParameters().is_pinned) { - // Verify that the current thread isn't terminating. - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), - ResultTerminationRequested); + // Check whether the thread is pinned. + if (this->GetStackParameters().is_pinned) { + // Verify that the current thread isn't terminating. + R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), + ResultTerminationRequested); - // Wait until the thread isn't pinned any more. - m_pinned_waiter_list.push_back(GetCurrentThread(m_kernel)); - GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); - } else { - // Check if the thread is currently running. - // If it is, we'll need to retry. - for (auto i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { - if (m_kernel.Scheduler(i).GetSchedulerCurrentThread() == this) { - thread_is_current = true; - break; + // Wait until the thread isn't pinned any more. + m_pinned_waiter_list.push_back(GetCurrentThread(m_kernel)); + GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); + } else { + // Check if the thread is currently running. + // If it is, we'll need to retry. + for (auto i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { + if (m_kernel.Scheduler(i).GetSchedulerCurrentThread() == this) { + thread_is_current = true; + break; + } } } - } - } while (thread_is_current); - } + } while (thread_is_current); + } - R_SUCCEED(); -} + R_SUCCEED(); + } -Result KThread::GetThreadContext3(Svc::ThreadContext* out) { - // Lock ourselves. - KScopedLightLock lk{m_activity_pause_lock}; + Result KThread::GetThreadContext3(Svc::ThreadContext* out) { + // Lock ourselves. + KScopedLightLock lk{m_activity_pause_lock}; - // Get the context. - { - // Lock the scheduler. - KScopedSchedulerLock sl{m_kernel}; + // Get the context. + { + // Lock the scheduler. + KScopedSchedulerLock sl{m_kernel}; - // Verify that we're suspended. - R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); + // Verify that we're suspended. + R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); - // If we're not terminating, get the thread's user context. - if (!this->IsTerminationRequested()) { - *out = m_thread_context; + // If we're not terminating, get the thread's user context. + if (!this->IsTerminationRequested()) { + *out = m_thread_context; - // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. - constexpr u32 El0Aarch64PsrMask = 0xF0000000; - constexpr u32 El0Aarch32PsrMask = 0xFE0FFE20; + // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. + constexpr u32 El0Aarch64PsrMask = 0xF0000000; + constexpr u32 El0Aarch32PsrMask = 0xFE0FFE20; - if (m_parent->Is64Bit()) { - out->pstate &= El0Aarch64PsrMask; - } else { - out->pstate &= El0Aarch32PsrMask; + if (m_parent->Is64Bit()) { + out->pstate &= El0Aarch64PsrMask; + } else { + out->pstate &= El0Aarch32PsrMask; + } } } - } - R_SUCCEED(); -} + R_SUCCEED(); + } -void KThread::AddHeldLock(LockWithPriorityInheritanceInfo* lock_info) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::AddHeldLock(LockWithPriorityInheritanceInfo* lock_info) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Set ourselves as the lock's owner. - lock_info->SetOwner(this); + // Set ourselves as the lock's owner. + lock_info->SetOwner(this); - // Add the lock to our held list. - m_held_lock_info_list.push_front(*lock_info); -} + // Add the lock to our held list. + m_held_lock_info_list.push_front(*lock_info); + } -KThread::LockWithPriorityInheritanceInfo* KThread::FindHeldLock(KProcessAddress address_key, - bool is_kernel_address_key) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + KThread::LockWithPriorityInheritanceInfo* KThread::FindHeldLock(KProcessAddress address_key, + bool is_kernel_address_key) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Try to find an existing held lock. - for (auto& held_lock : m_held_lock_info_list) { - if (held_lock.GetAddressKey() == address_key && - held_lock.GetIsKernelAddressKey() == is_kernel_address_key) { - return std::addressof(held_lock); + // Try to find an existing held lock. + for (auto& held_lock : m_held_lock_info_list) { + if (held_lock.GetAddressKey() == address_key && + held_lock.GetIsKernelAddressKey() == is_kernel_address_key) { + return std::addressof(held_lock); + } } + + return nullptr; } - return nullptr; -} + void KThread::AddWaiterImpl(KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(thread->GetConditionVariableTree() == nullptr); -void KThread::AddWaiterImpl(KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - ASSERT(thread->GetConditionVariableTree() == nullptr); + // Get the thread's address key. + const auto address_key = thread->GetAddressKey(); + const auto is_kernel_address_key = thread->GetIsKernelAddressKey(); - // Get the thread's address key. - const auto address_key = thread->GetAddressKey(); - const auto is_kernel_address_key = thread->GetIsKernelAddressKey(); + // Keep track of how many kernel waiters we have. + if (is_kernel_address_key) { + ASSERT((m_num_kernel_waiters++) >= 0); + KScheduler::SetSchedulerUpdateNeeded(m_kernel); + } - // Keep track of how many kernel waiters we have. - if (is_kernel_address_key) { - ASSERT((m_num_kernel_waiters++) >= 0); - KScheduler::SetSchedulerUpdateNeeded(m_kernel); - } + // Get the relevant lock info. + auto* lock_info = this->FindHeldLock(address_key, is_kernel_address_key); + if (lock_info == nullptr) { + // Create a new lock for the address key. + lock_info = + LockWithPriorityInheritanceInfo::Create(m_kernel, address_key, is_kernel_address_key); - // Get the relevant lock info. - auto* lock_info = this->FindHeldLock(address_key, is_kernel_address_key); - if (lock_info == nullptr) { - // Create a new lock for the address key. - lock_info = - LockWithPriorityInheritanceInfo::Create(m_kernel, address_key, is_kernel_address_key); + // Add the new lock to our list. + this->AddHeldLock(lock_info); + } - // Add the new lock to our list. - this->AddHeldLock(lock_info); + // Add the thread as waiter to the lock info. + lock_info->AddWaiter(thread); } - // Add the thread as waiter to the lock info. - lock_info->AddWaiter(thread); -} - -void KThread::RemoveWaiterImpl(KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::RemoveWaiterImpl(KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Keep track of how many kernel waiters we have. - if (thread->GetIsKernelAddressKey()) { - ASSERT((m_num_kernel_waiters--) > 0); - KScheduler::SetSchedulerUpdateNeeded(m_kernel); - } + // Keep track of how many kernel waiters we have. + if (thread->GetIsKernelAddressKey()) { + ASSERT((m_num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(m_kernel); + } - // Get the info for the lock the thread is waiting on. - auto* lock_info = thread->GetWaitingLockInfo(); - ASSERT(lock_info->GetOwner() == this); + // Get the info for the lock the thread is waiting on. + auto* lock_info = thread->GetWaitingLockInfo(); + ASSERT(lock_info->GetOwner() == this); - // Remove the waiter. - if (lock_info->RemoveWaiter(thread)) { - m_held_lock_info_list.erase(m_held_lock_info_list.iterator_to(*lock_info)); - LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + // Remove the waiter. + if (lock_info->RemoveWaiter(thread)) { + m_held_lock_info_list.erase(m_held_lock_info_list.iterator_to(*lock_info)); + LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + } } -} -void KThread::RestorePriority(KernelCore& kernel, KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + void KThread::RestorePriority(KernelCore& kernel, KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); - while (thread != nullptr) { - // We want to inherit priority where possible. - s32 new_priority = thread->GetBasePriority(); - for (const auto& held_lock : thread->m_held_lock_info_list) { - new_priority = - (std::min)(new_priority, held_lock.GetHighestPriorityWaiter()->GetPriority()); - } + while (thread != nullptr) { + // We want to inherit priority where possible. + s32 new_priority = thread->GetBasePriority(); + for (const auto& held_lock : thread->m_held_lock_info_list) { + new_priority = + (std::min)(new_priority, held_lock.GetHighestPriorityWaiter()->GetPriority()); + } - // If the priority we would inherit is not different from ours, don't do anything. - if (new_priority == thread->GetPriority()) { - return; - } + // If the priority we would inherit is not different from ours, don't do anything. + if (new_priority == thread->GetPriority()) { + return; + } - // Get the owner of whatever lock this thread is waiting on. - KThread* const lock_owner = thread->GetLockOwner(); + // Get the owner of whatever lock this thread is waiting on. + KThread* const lock_owner = thread->GetLockOwner(); - // If the thread is waiting on some lock, remove it as a waiter to prevent violating red - // black tree invariants. - if (lock_owner != nullptr) { - lock_owner->RemoveWaiterImpl(thread); - } + // If the thread is waiting on some lock, remove it as a waiter to prevent violating red + // black tree invariants. + if (lock_owner != nullptr) { + lock_owner->RemoveWaiterImpl(thread); + } - // Ensure we don't violate condition variable red black tree invariants. - if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { - BeforeUpdatePriority(kernel, cv_tree, thread); - } + // Ensure we don't violate condition variable red black tree invariants. + if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { + BeforeUpdatePriority(kernel, cv_tree, thread); + } - // Change the priority. - const s32 old_priority = thread->GetPriority(); - thread->SetPriority(new_priority); + // Change the priority. + const s32 old_priority = thread->GetPriority(); + thread->SetPriority(new_priority); - // Restore the condition variable, if relevant. - if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { - AfterUpdatePriority(kernel, cv_tree, thread); - } + // Restore the condition variable, if relevant. + if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { + AfterUpdatePriority(kernel, cv_tree, thread); + } - // If we removed the thread from some lock's waiting list, add it back. - if (lock_owner != nullptr) { - lock_owner->AddWaiterImpl(thread); - } + // If we removed the thread from some lock's waiting list, add it back. + if (lock_owner != nullptr) { + lock_owner->AddWaiterImpl(thread); + } - // Update the scheduler. - KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority); + // Update the scheduler. + KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority); - // Continue inheriting priority. - thread = lock_owner; + // Continue inheriting priority. + thread = lock_owner; + } } -} -void KThread::AddWaiter(KThread* thread) { - this->AddWaiterImpl(thread); + void KThread::AddWaiter(KThread* thread) { + this->AddWaiterImpl(thread); - // If the thread has a higher priority than us, we should inherit. - if (thread->GetPriority() < this->GetPriority()) { - RestorePriority(m_kernel, this); + // If the thread has a higher priority than us, we should inherit. + if (thread->GetPriority() < this->GetPriority()) { + RestorePriority(m_kernel, this); + } } -} -void KThread::RemoveWaiter(KThread* thread) { - this->RemoveWaiterImpl(thread); + void KThread::RemoveWaiter(KThread* thread) { + this->RemoveWaiterImpl(thread); - // If our priority is the same as the thread's (and we've inherited), we may need to restore to - // lower priority. - if (this->GetPriority() == thread->GetPriority() && - this->GetPriority() < this->GetBasePriority()) { - RestorePriority(m_kernel, this); + // If our priority is the same as the thread's (and we've inherited), we may need to restore to + // lower priority. + if (this->GetPriority() == thread->GetPriority() && + this->GetPriority() < this->GetBasePriority()) { + RestorePriority(m_kernel, this); + } } -} -KThread* KThread::RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key, - bool is_kernel_address_key_) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + KThread* KThread::RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key, + bool is_kernel_address_key_) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Get the relevant lock info. - auto* lock_info = this->FindHeldLock(key, is_kernel_address_key_); - if (lock_info == nullptr) { - *out_has_waiters = false; - return nullptr; - } + // Get the relevant lock info. + auto* lock_info = this->FindHeldLock(key, is_kernel_address_key_); + if (lock_info == nullptr) { + *out_has_waiters = false; + return nullptr; + } - // Remove the lock info from our held list. - m_held_lock_info_list.erase(m_held_lock_info_list.iterator_to(*lock_info)); + // Remove the lock info from our held list. + m_held_lock_info_list.erase(m_held_lock_info_list.iterator_to(*lock_info)); - // Keep track of how many kernel waiters we have. - if (lock_info->GetIsKernelAddressKey()) { - m_num_kernel_waiters -= lock_info->GetWaiterCount(); - ASSERT(m_num_kernel_waiters >= 0); - KScheduler::SetSchedulerUpdateNeeded(m_kernel); - } + // Keep track of how many kernel waiters we have. + if (lock_info->GetIsKernelAddressKey()) { + m_num_kernel_waiters -= lock_info->GetWaiterCount(); + ASSERT(m_num_kernel_waiters >= 0); + KScheduler::SetSchedulerUpdateNeeded(m_kernel); + } - ASSERT(lock_info->GetWaiterCount() > 0); + ASSERT(lock_info->GetWaiterCount() > 0); - // Remove the highest priority waiter from the lock to be the next owner. - KThread* next_lock_owner = lock_info->GetHighestPriorityWaiter(); - if (lock_info->RemoveWaiter(next_lock_owner)) { - // The new owner was the only waiter. - *out_has_waiters = false; + // Remove the highest priority waiter from the lock to be the next owner. + KThread* next_lock_owner = lock_info->GetHighestPriorityWaiter(); + if (lock_info->RemoveWaiter(next_lock_owner)) { + // The new owner was the only waiter. + *out_has_waiters = false; - // Free the lock info, since it has no waiters. - LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); - } else { - // There are additional waiters on the lock. - *out_has_waiters = true; + // Free the lock info, since it has no waiters. + LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + } else { + // There are additional waiters on the lock. + *out_has_waiters = true; - // Add the lock to the new owner's held list. - next_lock_owner->AddHeldLock(lock_info); + // Add the lock to the new owner's held list. + next_lock_owner->AddHeldLock(lock_info); - // Keep track of any kernel waiters for the new owner. - if (lock_info->GetIsKernelAddressKey()) { - next_lock_owner->m_num_kernel_waiters += lock_info->GetWaiterCount(); - ASSERT(next_lock_owner->m_num_kernel_waiters > 0); + // Keep track of any kernel waiters for the new owner. + if (lock_info->GetIsKernelAddressKey()) { + next_lock_owner->m_num_kernel_waiters += lock_info->GetWaiterCount(); + ASSERT(next_lock_owner->m_num_kernel_waiters > 0); - // NOTE: No need to set scheduler update needed, because we will have already done so - // when removing earlier. + // NOTE: No need to set scheduler update needed, because we will have already done so + // when removing earlier. + } } - } - // If our priority is the same as the next owner's (and we've inherited), we may need to restore - // to lower priority. - if (this->GetPriority() == next_lock_owner->GetPriority() && - this->GetPriority() < this->GetBasePriority()) { - RestorePriority(m_kernel, this); - // NOTE: No need to restore priority on the next lock owner, because it was already the - // highest priority waiter on the lock. - } + // If our priority is the same as the next owner's (and we've inherited), we may need to restore + // to lower priority. + if (this->GetPriority() == next_lock_owner->GetPriority() && + this->GetPriority() < this->GetBasePriority()) { + RestorePriority(m_kernel, this); + // NOTE: No need to restore priority on the next lock owner, because it was already the + // highest priority waiter on the lock. + } - // Return the next lock owner. - return next_lock_owner; -} + // Return the next lock owner. + return next_lock_owner; + } -Result KThread::Run() { - while (true) { - KScopedSchedulerLock lk{m_kernel}; + Result KThread::Run() { + while (true) { + KScopedSchedulerLock lk{m_kernel}; - // If either this thread or the current thread are requesting termination, note it. - R_UNLESS(!this->IsTerminationRequested(), ResultTerminationRequested); - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested); + // If either this thread or the current thread are requesting termination, note it. + R_UNLESS(!this->IsTerminationRequested(), ResultTerminationRequested); + R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested); - // Ensure our thread state is correct. - R_UNLESS(this->GetState() == ThreadState::Initialized, ResultInvalidState); + // Ensure our thread state is correct. + R_UNLESS(this->GetState() == ThreadState::Initialized, ResultInvalidState); - // If the current thread has been asked to suspend, suspend it and retry. - if (GetCurrentThread(m_kernel).IsSuspended()) { - GetCurrentThread(m_kernel).UpdateState(); - continue; - } + // If the current thread has been asked to suspend, suspend it and retry. + if (GetCurrentThread(m_kernel).IsSuspended()) { + GetCurrentThread(m_kernel).UpdateState(); + continue; + } - // If we're not a kernel thread and we've been asked to suspend, suspend ourselves. - if (KProcess* owner = this->GetOwnerProcess(); owner != nullptr) { - if (this->IsUserThread() && this->IsSuspended()) { - this->UpdateState(); + // If we're not a kernel thread and we've been asked to suspend, suspend ourselves. + if (KProcess* owner = this->GetOwnerProcess(); owner != nullptr) { + if (this->IsUserThread() && this->IsSuspended()) { + this->UpdateState(); + } + owner->IncrementRunningThreadCount(); } - owner->IncrementRunningThreadCount(); - } - // Open a reference, now that we're running. - this->Open(); + // Open a reference, now that we're running. + this->Open(); - // Set our state and finish. - this->SetState(ThreadState::Runnable); + // Set our state and finish. + this->SetState(ThreadState::Runnable); - R_SUCCEED(); + R_SUCCEED(); + } } -} -void KThread::Exit() { - ASSERT(this == GetCurrentThreadPointer(m_kernel)); + void KThread::Exit() { + ASSERT(this == GetCurrentThreadPointer(m_kernel)); - // Release the thread resource hint, running thread count from parent. - if (m_parent != nullptr) { - m_parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1); - m_resource_limit_release_hint = true; - m_parent->DecrementRunningThreadCount(); - } + // Release the thread resource hint, running thread count from parent. + if (m_parent != nullptr) { + m_parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1); + m_resource_limit_release_hint = true; + m_parent->DecrementRunningThreadCount(); + } - // Perform termination. - { - KScopedSchedulerLock sl{m_kernel}; + // Perform termination. + { + KScopedSchedulerLock sl{m_kernel}; - // Disallow all suspension. - m_suspend_allowed_flags = 0; - this->UpdateState(); + // Disallow all suspension. + m_suspend_allowed_flags = 0; + this->UpdateState(); - // Disallow all suspension. - m_suspend_allowed_flags = 0; + // Disallow all suspension. + m_suspend_allowed_flags = 0; + + // Start termination. + this->StartTermination(); - // Start termination. - this->StartTermination(); + // Register the thread as a work task. + KWorkerTaskManager::AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, this); + } - // Register the thread as a work task. - KWorkerTaskManager::AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, this); + UNREACHABLE_MSG("KThread::Exit() would return"); } - UNREACHABLE_MSG("KThread::Exit() would return"); -} + Result KThread::Terminate() { + ASSERT(this != GetCurrentThreadPointer(m_kernel)); -Result KThread::Terminate() { - ASSERT(this != GetCurrentThreadPointer(m_kernel)); + // Request the thread terminate if it hasn't already. + if (const auto new_state = this->RequestTerminate(); new_state != ThreadState::Terminated) { + // If the thread isn't terminated, wait for it to terminate. + s32 index; + KSynchronizationObject* objects[] = {this}; + R_TRY(KSynchronizationObject::Wait(m_kernel, std::addressof(index), objects, 1, + Svc::WaitInfinite)); + } - // Request the thread terminate if it hasn't already. - if (const auto new_state = this->RequestTerminate(); new_state != ThreadState::Terminated) { - // If the thread isn't terminated, wait for it to terminate. - s32 index; - KSynchronizationObject* objects[] = {this}; - R_TRY(KSynchronizationObject::Wait(m_kernel, std::addressof(index), objects, 1, - Svc::WaitInfinite)); + R_SUCCEED(); } - R_SUCCEED(); -} - -ThreadState KThread::RequestTerminate() { - ASSERT(this != GetCurrentThreadPointer(m_kernel)); + ThreadState KThread::RequestTerminate() { + ASSERT(this != GetCurrentThreadPointer(m_kernel)); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{m_kernel}; - // Determine if this is the first termination request. - const bool first_request = [&]() -> bool { - // Perform an atomic compare-and-swap from false to true. - bool expected = false; - return m_termination_requested.compare_exchange_strong(expected, true); - }(); + // Determine if this is the first termination request. + const bool first_request = [&]() -> bool { + // Perform an atomic compare-and-swap from false to true. + bool expected = false; + return m_termination_requested.compare_exchange_strong(expected, true); + }(); + + // If this is the first request, start termination procedure. + if (first_request) { + // If the thread is in initialized state, just change state to terminated. + if (this->GetState() == ThreadState::Initialized) { + m_thread_state = ThreadState::Terminated; + return ThreadState::Terminated; + } - // If this is the first request, start termination procedure. - if (first_request) { - // If the thread is in initialized state, just change state to terminated. - if (this->GetState() == ThreadState::Initialized) { - m_thread_state = ThreadState::Terminated; - return ThreadState::Terminated; - } + // Register the terminating dpc. + this->RegisterDpc(DpcFlag::Terminating); - // Register the terminating dpc. - this->RegisterDpc(DpcFlag::Terminating); + // If the thread is pinned, unpin it. + if (this->GetStackParameters().is_pinned) { + this->GetOwnerProcess()->UnpinThread(this); + } - // If the thread is pinned, unpin it. - if (this->GetStackParameters().is_pinned) { - this->GetOwnerProcess()->UnpinThread(this); - } + // If the thread is suspended, continue it. + if (this->IsSuspended()) { + m_suspend_allowed_flags = 0; + this->UpdateState(); + } - // If the thread is suspended, continue it. - if (this->IsSuspended()) { - m_suspend_allowed_flags = 0; - this->UpdateState(); - } + // Change the thread's priority to be higher than any system thread's. + this->IncreaseBasePriority(TerminatingThreadPriority); - // Change the thread's priority to be higher than any system thread's. - this->IncreaseBasePriority(TerminatingThreadPriority); + // If the thread is runnable, send a termination interrupt to cores it may be running on. + if (this->GetState() == ThreadState::Runnable) { + // NOTE: We do not mask the "current core", because this code may not actually be + // executing from the thread representing the "current core". + if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask(); core_mask != 0) { + Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask); + } + } - // If the thread is runnable, send a termination interrupt to cores it may be running on. - if (this->GetState() == ThreadState::Runnable) { - // NOTE: We do not mask the "current core", because this code may not actually be - // executing from the thread representing the "current core". - if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask(); core_mask != 0) { - Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask); + // Wake up the thread. + if (this->GetState() == ThreadState::Waiting) { + m_wait_queue->CancelWait(this, ResultTerminationRequested, true); } } - // Wake up the thread. - if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->CancelWait(this, ResultTerminationRequested, true); - } + return this->GetState(); } - return this->GetState(); -} + Result KThread::Sleep(s64 timeout) { + ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(this == GetCurrentThreadPointer(m_kernel)); + ASSERT(timeout > 0); + + ThreadQueueImplForKThreadSleep wait_queue(m_kernel); + KHardwareTimer* timer{}; + { + // Setup the scheduling lock and sleep. + KScopedSchedulerLockAndSleep slp(m_kernel, std::addressof(timer), this, timeout); + + // Check if the thread should terminate. + if (this->IsTerminationRequested()) { + slp.CancelSleep(); + R_THROW(ResultTerminationRequested); + } -Result KThread::Sleep(s64 timeout) { - ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - ASSERT(this == GetCurrentThreadPointer(m_kernel)); - ASSERT(timeout > 0); - - ThreadQueueImplForKThreadSleep wait_queue(m_kernel); - KHardwareTimer* timer{}; - { - // Setup the scheduling lock and sleep. - KScopedSchedulerLockAndSleep slp(m_kernel, std::addressof(timer), this, timeout); - - // Check if the thread should terminate. - if (this->IsTerminationRequested()) { - slp.CancelSleep(); - R_THROW(ResultTerminationRequested); + // Wait for the sleep to end. + wait_queue.SetHardwareTimer(timer); + this->BeginWait(std::addressof(wait_queue)); + this->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); } - // Wait for the sleep to end. - wait_queue.SetHardwareTimer(timer); - this->BeginWait(std::addressof(wait_queue)); - this->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); + R_SUCCEED(); } - R_SUCCEED(); -} + void KThread::RequestDummyThreadWait() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(this->IsDummyThread()); -void KThread::RequestDummyThreadWait() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - ASSERT(this->IsDummyThread()); + // We will block when the scheduler lock is released. + std::scoped_lock lock{m_dummy_thread_mutex}; + m_dummy_thread_runnable = false; + } - // We will block when the scheduler lock is released. - std::scoped_lock lock{m_dummy_thread_mutex}; - m_dummy_thread_runnable = false; -} + void KThread::DummyThreadBeginWait() { + if (!this->IsDummyThread() || m_kernel.IsPhantomModeForSingleCore()) { + // Occurs in single core mode. + return; + } -void KThread::DummyThreadBeginWait() { - if (!this->IsDummyThread() || m_kernel.IsPhantomModeForSingleCore()) { - // Occurs in single core mode. - return; + // Block until runnable is no longer false. + std::unique_lock lock{m_dummy_thread_mutex}; + m_dummy_thread_cv.wait(lock, [this] { return m_dummy_thread_runnable; }); } - // Block until runnable is no longer false. - std::unique_lock lock{m_dummy_thread_mutex}; - m_dummy_thread_cv.wait(lock, [this] { return m_dummy_thread_runnable; }); -} - -void KThread::DummyThreadEndWait() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - ASSERT(this->IsDummyThread()); + void KThread::DummyThreadEndWait() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(this->IsDummyThread()); - // Wake up the waiting thread. - { - std::scoped_lock lock{m_dummy_thread_mutex}; - m_dummy_thread_runnable = true; + // Wake up the waiting thread. + { + std::scoped_lock lock{m_dummy_thread_mutex}; + m_dummy_thread_runnable = true; + } + m_dummy_thread_cv.notify_one(); } - m_dummy_thread_cv.notify_one(); -} -void KThread::BeginWait(KThreadQueue* queue) { - // Set our state as waiting. - this->SetState(ThreadState::Waiting); + void KThread::BeginWait(KThreadQueue* queue) { + // Set our state as waiting. + this->SetState(ThreadState::Waiting); - // Set our wait queue. - m_wait_queue = queue; -} + // Set our wait queue. + m_wait_queue = queue; + } -void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result) { - // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result) { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); - // If we're waiting, notify our queue that we're available. - if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->NotifyAvailable(this, signaled_object, wait_result); + // If we're waiting, notify our queue that we're available. + if (this->GetState() == ThreadState::Waiting) { + m_wait_queue->NotifyAvailable(this, signaled_object, wait_result); + } } -} -void KThread::EndWait(Result wait_result) { - // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + void KThread::EndWait(Result wait_result) { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); - // If we're waiting, notify our queue that we're available. - if (this->GetState() == ThreadState::Waiting) { - if (m_wait_queue == nullptr) { - // This should never happen, but avoid a hard crash below to get this logged. - ASSERT_MSG(false, "wait_queue is nullptr!"); - return; - } + // If we're waiting, notify our queue that we're available. + if (this->GetState() == ThreadState::Waiting) { + if (m_wait_queue == nullptr) { + // This should never happen, but avoid a hard crash below to get this logged. + ASSERT_MSG(false, "wait_queue is nullptr!"); + return; + } - m_wait_queue->EndWait(this, wait_result); + m_wait_queue->EndWait(this, wait_result); + } } -} -void KThread::CancelWait(Result wait_result, bool cancel_timer_task) { - // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + void KThread::CancelWait(Result wait_result, bool cancel_timer_task) { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); - // If we're waiting, notify our queue that we're available. - if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->CancelWait(this, wait_result, cancel_timer_task); + // If we're waiting, notify our queue that we're available. + if (this->GetState() == ThreadState::Waiting) { + m_wait_queue->CancelWait(this, wait_result, cancel_timer_task); + } } -} -void KThread::SetState(ThreadState state) { - KScopedSchedulerLock sl{m_kernel}; + void KThread::SetState(ThreadState state) { + KScopedSchedulerLock sl{m_kernel}; - // Clear debugging state - this->SetWaitReasonForDebugging({}); + // Clear debugging state + this->SetWaitReasonForDebugging({}); - const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); - m_thread_state.store( - static_cast((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)), - std::memory_order_relaxed); - if (m_thread_state.load(std::memory_order_relaxed) != old_state) { - KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); + m_thread_state.store( + static_cast((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)), + std::memory_order_relaxed); + if (m_thread_state.load(std::memory_order_relaxed) != old_state) { + KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + } } -} - -std::shared_ptr& KThread::GetHostContext() { - return m_host_context; -} -void SetCurrentThread(KernelCore& kernel, KThread* thread) { - kernel.SetCurrentEmuThread(thread); -} + std::shared_ptr& KThread::GetHostContext() { + return m_host_context; + } -KThread* GetCurrentThreadPointer(KernelCore& kernel) { - return kernel.GetCurrentEmuThread(); -} + void SetCurrentThread(KernelCore& kernel, KThread* thread) { + kernel.SetCurrentEmuThread(thread); + } -KThread& GetCurrentThread(KernelCore& kernel) { - return *GetCurrentThreadPointer(kernel); -} + KThread* GetCurrentThreadPointer(KernelCore& kernel) { + return kernel.GetCurrentEmuThread(); + } -KProcess* GetCurrentProcessPointer(KernelCore& kernel) { - return GetCurrentThread(kernel).GetOwnerProcess(); -} + KThread& GetCurrentThread(KernelCore& kernel) { + return *GetCurrentThreadPointer(kernel); + } -KProcess& GetCurrentProcess(KernelCore& kernel) { - return *GetCurrentProcessPointer(kernel); -} + KProcess* GetCurrentProcessPointer(KernelCore& kernel) { + return GetCurrentThread(kernel).GetOwnerProcess(); + } -s32 GetCurrentCoreId(KernelCore& kernel) { - return GetCurrentThread(kernel).GetCurrentCore(); -} + KProcess& GetCurrentProcess(KernelCore& kernel) { + return *GetCurrentProcessPointer(kernel); + } -Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) { - return GetCurrentProcess(kernel).GetMemory(); -} + s32 GetCurrentCoreId(KernelCore& kernel) { + return GetCurrentThread(kernel).GetCurrentCore(); + } -KScopedDisableDispatch::~KScopedDisableDispatch() { - // If we are shutting down the kernel, none of this is relevant anymore. - if (m_kernel.IsShuttingDown()) { - return; + Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) { + return GetCurrentProcess(kernel).GetMemory(); } - if (GetCurrentThread(m_kernel).GetDisableDispatchCount() <= 1) { - auto* scheduler = m_kernel.CurrentScheduler(); + KScopedDisableDispatch::~KScopedDisableDispatch() { + // If we are shutting down the kernel, none of this is relevant anymore. + if (m_kernel.IsShuttingDown()) { + return; + } + + if (GetCurrentThread(m_kernel).GetDisableDispatchCount() <= 1) { + auto* scheduler = m_kernel.CurrentScheduler(); - if (scheduler && !m_kernel.IsPhantomModeForSingleCore()) { - scheduler->RescheduleCurrentCore(); + if (scheduler && !m_kernel.IsPhantomModeForSingleCore()) { + scheduler->RescheduleCurrentCore(); + } else { + KScheduler::RescheduleCurrentHLEThread(m_kernel); + } } else { - KScheduler::RescheduleCurrentHLEThread(m_kernel); + GetCurrentThread(m_kernel).EnableDispatch(); } - } else { - GetCurrentThread(m_kernel).EnableDispatch(); } -} - -} // namespace Kernel +} \ No newline at end of file diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 6bcb21b4a1..e79704ce4b 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -99,6 +99,12 @@ enum class DpcFlag : u32 { Terminated = (1 << 1), }; +enum class ExceptionFlag : u8 { + IsCallingSvc = 1 << 0, + InExceptionHandler = 1 << 1, +}; +DECLARE_ENUM_FLAG_OPERATORS(ExceptionFlag); + enum class ThreadWaitReasonForDebugging : u32 { None, ///< Thread is not waiting Sleep, ///< Thread is waiting due to a SleepThread SVC @@ -153,7 +159,7 @@ public: /** * Sets the thread's current priority. - * @param priority The new priority. + * @param value The new priority. */ void SetPriority(s32 value) { m_priority = value; @@ -340,6 +346,8 @@ public: void SetInterruptFlag(); void ClearInterruptFlag(); + void UpdateTlsThreadCpuTime(s64 switch_tick); + KThread* GetLockOwner() const; const KAffinityMask& GetAffinityMask() const { @@ -446,6 +454,7 @@ public: bool is_pinned; s32 disable_count; KThread* cur_thread; + std::atomic exception_flags{0}; }; StackParameters& GetStackParameters() { @@ -456,6 +465,16 @@ public: return m_stack_parameters; } + void SetExceptionFlag(ExceptionFlag flag) { + GetStackParameters().exception_flags.fetch_or(static_cast(flag), std::memory_order_relaxed); + } + void ClearExceptionFlag(ExceptionFlag flag) { + GetStackParameters().exception_flags.fetch_and(static_cast(~static_cast(flag)), std::memory_order_relaxed); + } + bool IsExceptionFlagSet(ExceptionFlag flag) const { + return (GetStackParameters().exception_flags.load(std::memory_order_relaxed) & static_cast(flag)) != 0; + } + class QueueEntry { public: constexpr QueueEntry() = default; @@ -511,10 +530,12 @@ public: void SetInExceptionHandler() { this->GetStackParameters().is_in_exception_handler = true; + SetExceptionFlag(ExceptionFlag::InExceptionHandler); } void ClearInExceptionHandler() { this->GetStackParameters().is_in_exception_handler = false; + ClearExceptionFlag(ExceptionFlag::InExceptionHandler); } bool IsInExceptionHandler() const { @@ -523,10 +544,12 @@ public: void SetIsCallingSvc() { this->GetStackParameters().is_calling_svc = true; + SetExceptionFlag(ExceptionFlag::IsCallingSvc); } void ClearIsCallingSvc() { this->GetStackParameters().is_calling_svc = false; + ClearExceptionFlag(ExceptionFlag::IsCallingSvc); } bool IsCallingSvc() const { diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index 98634c6f60..9d59f96973 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -298,8 +298,41 @@ Result FSP_SRV::OpenSaveDataFileSystem(OutInterface out_interface, Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface out_interface, FileSys::SaveDataSpaceId space_id, FileSys::SaveDataAttribute attribute) { - LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem"); - R_RETURN(OpenSaveDataFileSystem(out_interface, space_id, attribute)); + LOG_INFO(Service_FS, "called, space_id={}, {}", + space_id, attribute.DebugInfo()); + + R_UNLESS(attribute.system_save_data_id != FileSys::InvalidSystemSaveDataId, + FileSys::ResultInvalidArgument); + + if (attribute.program_id == 0) { + attribute.program_id = program_id; + } + + FileSys::VirtualDir dir{}; + R_TRY(save_data_controller->OpenSaveData(&dir, space_id, attribute)); + + FileSys::StorageId id{}; + switch (space_id) { + case FileSys::SaveDataSpaceId::User: + id = FileSys::StorageId::NandUser; + break; + case FileSys::SaveDataSpaceId::SdSystem: + case FileSys::SaveDataSpaceId::SdUser: + id = FileSys::StorageId::SdCard; + break; + case FileSys::SaveDataSpaceId::System: + id = FileSys::StorageId::NandSystem; + break; + case FileSys::SaveDataSpaceId::Temporary: + case FileSys::SaveDataSpaceId::ProperSystem: + case FileSys::SaveDataSpaceId::SafeMode: + ASSERT(false); + } + + *out_interface = + std::make_shared(system, std::move(dir), SizeGetter::FromStorageId(fsc, id)); + + R_SUCCEED(); } Result FSP_SRV::OpenReadOnlySaveDataFileSystem(OutInterface out_interface, diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 5a81a050a0..edf5366fa9 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -3,6 +3,9 @@ #include "common/hex_util.h" #include "common/logging/log.h" +#include "common/uuid.h" +#include + #include "core/core.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/ipc_helpers.h" @@ -145,10 +148,12 @@ private: const auto data1 = ctx.ReadBufferA(0); const auto data2 = ctx.ReadBufferX(0); + Common::UUID uuid{}; + std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); + LOG_ERROR(Service_PREPO, - "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, " - "data2_size={:016X}", - user_id[1], user_id[0], title_id, data1.size(), data2.size()); + "called, user_id={}, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", + uuid.FormattedString(), title_id, data1.size(), data2.size()); const auto& reporter{system.GetReporter()}; reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2}, @@ -182,16 +187,20 @@ private: void SaveSystemReportWithUser(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + // 21.0.0+: field0 (u64), user_id (u128), title_id (u64) + const auto field0 = rp.PopRaw(); const auto user_id = rp.PopRaw(); const auto title_id = rp.PopRaw(); - const auto reserved = rp.PopRaw(); const auto data_x = ctx.ReadBufferX(0); const auto data_a = ctx.ReadBufferA(0); + Common::UUID uuid{}; + std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); + LOG_ERROR(Service_PREPO, - "called, user_id={:016X}{:016X}, title_id={:016X}, reserved={}, data_a_size={}, data_x_size={}", - user_id[1], user_id[0], title_id, reserved, data_a.size(), data_x.size()); + "called, user_id={}, field0={:016X}, title_id={:016X}, data_a_size={}, data_x_size={}", + uuid.FormattedString(), field0, title_id, data_a.size(), data_x.size()); const auto& reporter{system.GetReporter()}; reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data_a, data_x}, From 9c243a48b436e1739433c0ac54c63e31dea39aef Mon Sep 17 00:00:00 2001 From: Maufeat Date: Sat, 15 Nov 2025 15:56:31 +0100 Subject: [PATCH 26/28] add kernel changes --- src/core/hle/kernel/k_handle_table.cpp | 4 +- src/core/hle/kernel/k_handle_table.h | 2 +- src/core/hle/kernel/svc/svc_event.cpp | 6 ++- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 39 +++++++++++++++---- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index 3535ddc0ca..56fc5909f6 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -6,7 +6,7 @@ namespace Kernel { -Result KHandleTable::Finalize() { +void KHandleTable::Finalize() { // Get the table and clear our record of it. u16 saved_table_size = 0; { @@ -22,8 +22,6 @@ Result KHandleTable::Finalize() { obj->Close(); } } - - R_SUCCEED(); } bool KHandleTable::Remove(Handle handle) { diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 7381e951f5..731a5284dc 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h @@ -68,7 +68,7 @@ public: return m_max_count; } - Result Finalize(); + void Finalize(); bool Remove(Handle handle); template diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp index 8e4beb3965..7b88017c0d 100644 --- a/src/core/hle/kernel/svc/svc_event.cpp +++ b/src/core/hle/kernel/svc/svc_event.cpp @@ -34,7 +34,8 @@ Result ClearEvent(Core::System& system, Handle event_handle) { { KScopedAutoObject event = handle_table.GetObject(event_handle); if (event.IsNotNull()) { - R_RETURN(event->Clear()); + event->Clear(); + R_SUCCEED(); } } @@ -42,7 +43,8 @@ Result ClearEvent(Core::System& system, Handle event_handle) { { KScopedAutoObject readable_event = handle_table.GetObject(event_handle); if (readable_event.IsNotNull()) { - R_RETURN(readable_event->Clear()); + readable_event->Clear(); + R_SUCCEED(); } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index fcc7f9d392..aab4de39e4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx(IoctlAllocGpfifoEx& params, DeviceFD fd) { params.reserved[2]); if (channel_state->initialized) { - LOG_CRITICAL(Service_NVDRV, "Already allocated!"); + LOG_DEBUG(Service_NVDRV, "Channel already initialized; AllocGPFIFOEx returning AlreadyAllocated"); return NvResult::AlreadyAllocated; } @@ -196,6 +196,15 @@ NvResult nvhost_gpu::AllocGPFIFOEx(IoctlAllocGpfifoEx& params, DeviceFD fd) { program_id = session->process->GetProgramId(); } + // Store program id for later lazy initialization + channel_state->program_id = program_id; + + // If address space is not yet bound, defer channel initialization. + if (!channel_state->memory_manager) { + params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); + return NvResult::Success; + } + system.GPU().InitChannel(*channel_state, program_id); params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); @@ -211,7 +220,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx& params, DeviceFD fd) { params.reserved[2]); if (channel_state->initialized) { - LOG_CRITICAL(Service_NVDRV, "Already allocated!"); + LOG_DEBUG(Service_NVDRV, "Channel already initialized; AllocGPFIFOEx2 returning AlreadyAllocated"); return NvResult::AlreadyAllocated; } @@ -220,6 +229,15 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx& params, DeviceFD fd) { program_id = session->process->GetProgramId(); } + // Store program id for later lazy initialization + channel_state->program_id = program_id; + + // If address space is not yet bound, defer channel initialization. + if (!channel_state->memory_manager) { + params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); + return NvResult::Success; + } + system.GPU().InitChannel(*channel_state, program_id); params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); @@ -244,9 +262,10 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { LOG_DEBUG(Service_NVDRV, "called, class_num={:#X}, flags={:#X}, obj_id={:#X}", params.class_num, params.flags, params.obj_id); - if (!channel_state || !channel_state->initialized) { - LOG_CRITICAL(Service_NVDRV, "No address space bound to allocate a object context!"); - return NvResult::NotInitialized; + // Do not require channel initialization here: some clients allocate contexts before binding. + if (!channel_state) { + LOG_ERROR(Service_NVDRV, "No channel state available!"); + return NvResult::InvalidState; } std::scoped_lock lk(channel_mutex); @@ -268,11 +287,12 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { } if (ctxObjs[ctx_class_number_index].has_value()) { - LOG_ERROR(Service_NVDRV, "Object context for class {:#X} already allocated on this channel", - params.class_num); + LOG_WARNING(Service_NVDRV, "Object context for class {:#X} already allocated on this channel", + params.class_num); return NvResult::AlreadyAllocated; } + // Defer actual hardware context binding until channel is initialized. ctxObjs[ctx_class_number_index] = params; return NvResult::Success; @@ -326,6 +346,11 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL std::scoped_lock lock(channel_mutex); + // Lazily initialize channel when address space is available + if (!channel_state->initialized && channel_state->memory_manager) { + system.GPU().InitChannel(*channel_state, channel_state->program_id); + } + const auto bind_id = channel_state->bind_id; auto& flags = params.flags; From 436853e233a75562e46434cca6deaabec479839c Mon Sep 17 00:00:00 2001 From: Maufeat Date: Sat, 15 Nov 2025 17:54:05 +0100 Subject: [PATCH 27/28] more changes, show app tile name in qlaunch --- .../ns/application_manager_interface.cpp | 4 ++-- ...nly_application_control_data_interface.cpp | 24 +++---------------- ..._only_application_control_data_interface.h | 2 +- src/core/hle/service/prepo/prepo.cpp | 7 ++++-- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp index c36af6ca49..e19a1cdd4e 100644 --- a/src/core/hle/service/ns/application_manager_interface.cpp +++ b/src/core/hle/service/ns/application_manager_interface.cpp @@ -548,8 +548,8 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out o } Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground( - u64 unk, u64 application_id) { - LOG_WARNING(Service_NS, "(STUBBED), app={:016X} unk={}", application_id, unk); + u64 control_source, u64 application_id) { + LOG_WARNING(Service_NS, "(STUBBED), control_source={} app={:016X}", control_source, application_id); R_SUCCEED(); } diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp index 42d8fc3338..abc0ee8050 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp @@ -27,7 +27,8 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa {2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, {4, nullptr, "SelectApplicationDesiredLanguage"}, - {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIconSize>, "GetApplicationControlDataWithIconSize"}, + {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"}, + {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"}, }; // clang-format on @@ -163,7 +164,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData( R_SUCCEED(); } -Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIconSize( +Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon( OutBuffer out_buffer, Out out_total_size, ApplicationControlSource application_control_source, @@ -173,19 +174,11 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIc constexpr size_t kExpectedBufferSize = 0x14000; constexpr size_t kNACPSize = sizeof(FileSys::RawNACP); - constexpr size_t kMaxIconSize = kExpectedBufferSize - kNACPSize; const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()}; const auto control = pm.GetControlMetadata(); - const auto size = out_buffer.size(); - - if (size < kExpectedBufferSize) { - LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, required={:016X})", size, kExpectedBufferSize); - R_THROW(ResultUnknown); - } - // Copy NACP if (control.first != nullptr) { const auto bytes = control.first->GetRawBytes(); std::memcpy(out_buffer.data(), bytes.data(), bytes.size()); @@ -193,17 +186,6 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIc std::memset(out_buffer.data(), 0, kNACPSize); } - // Copy icon, pad with zeros if needed - size_t icon_size = control.second ? control.second->GetSize() : 0; - if (icon_size > kMaxIconSize) { - icon_size = kMaxIconSize; // Truncate if too large - } - if (control.second != nullptr && icon_size > 0) { - control.second->Read(out_buffer.data() + kNACPSize, icon_size); - } else { - std::memset(out_buffer.data() + kNACPSize, 0, kMaxIconSize); - } - *out_total_size = kExpectedBufferSize; R_SUCCEED(); } diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.h b/src/core/hle/service/ns/read_only_application_control_data_interface.h index 365e1f7ee3..0a0848bc73 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.h +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.h @@ -31,7 +31,7 @@ public: Out out_actual_size, ApplicationControlSource application_control_source, u64 application_id); - Result GetApplicationControlDataWithIconSize( + Result GetApplicationControlDataWithoutIcon( OutBuffer out_buffer, Out out_total_size, ApplicationControlSource application_control_source, diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index edf5366fa9..3f9fcbf5b3 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -133,6 +133,9 @@ private: const auto data1 = ctx.ReadBufferA(0); const auto data2 = ctx.ReadBufferX(0); + LOG_ERROR(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", + title_id, data1.size(), data2.size()); + LOG_ERROR(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", title_id, data1.size(), data2.size()); @@ -151,7 +154,7 @@ private: Common::UUID uuid{}; std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); - LOG_ERROR(Service_PREPO, + LOG_DEBUG(Service_PREPO, "called, user_id={}, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", uuid.FormattedString(), title_id, data1.size(), data2.size()); @@ -172,7 +175,7 @@ private: const auto data_x = ctx.ReadBufferX(0); const auto data_a = ctx.ReadBufferA(0); - LOG_ERROR(Service_PREPO, + LOG_DEBUG(Service_PREPO, "called, field0={}, title_id={:016X}, data_a_size={}, data_x_size={}", field0, title_id, data_a.size(), data_x.size()); From 3f45a99440c62fb5244a8de8c13897e0c518cc40 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Sat, 15 Nov 2025 17:54:43 +0100 Subject: [PATCH 28/28] change log level for SaveSystemReport --- src/core/hle/service/prepo/prepo.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 3f9fcbf5b3..aa58b87efc 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -133,10 +133,7 @@ private: const auto data1 = ctx.ReadBufferA(0); const auto data2 = ctx.ReadBufferX(0); - LOG_ERROR(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", - title_id, data1.size(), data2.size()); - - LOG_ERROR(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", + LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", title_id, data1.size(), data2.size()); IPC::ResponseBuilder rb{ctx, 2}; @@ -201,7 +198,7 @@ private: Common::UUID uuid{}; std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); - LOG_ERROR(Service_PREPO, + LOG_DEBUG(Service_PREPO, "called, user_id={}, field0={:016X}, title_id={:016X}, data_a_size={}, data_x_size={}", uuid.FormattedString(), field0, title_id, data_a.size(), data_x.size());