diff options
351 files changed, 16551 insertions, 5621 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md index 70e1bba67..5706243bb 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md | |||
| @@ -1,4 +1,13 @@ | |||
| 1 | <!-- | 1 | --- |
| 2 | name: Bug Report / Feature Request | ||
| 3 | about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better. | ||
| 4 | title: '' | ||
| 5 | labels: '' | ||
| 6 | assignees: '' | ||
| 7 | |||
| 8 | --- | ||
| 9 | |||
| 10 | <!--- | ||
| 2 | Please keep in mind yuzu is EXPERIMENTAL SOFTWARE. | 11 | Please keep in mind yuzu is EXPERIMENTAL SOFTWARE. |
| 3 | 12 | ||
| 4 | Please read the FAQ: | 13 | Please read the FAQ: |
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..52faafad3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | blank_issues_enabled: false | ||
| 2 | contact_links: | ||
| 3 | - name: yuzu Discord | ||
| 4 | url: https://discord.com/invite/u77vRWY | ||
| 5 | about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed. | ||
| 6 | - name: Community forums | ||
| 7 | url: https://community.citra-emu.org | ||
| 8 | about: This is an alternative place for tech support, however helpers there are not as active. | ||
diff --git a/.gitmodules b/.gitmodules index 2ec9dda62..79028bbb5 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -13,6 +13,9 @@ | |||
| 13 | [submodule "soundtouch"] | 13 | [submodule "soundtouch"] |
| 14 | path = externals/soundtouch | 14 | path = externals/soundtouch |
| 15 | url = https://github.com/citra-emu/ext-soundtouch.git | 15 | url = https://github.com/citra-emu/ext-soundtouch.git |
| 16 | [submodule "libressl"] | ||
| 17 | path = externals/libressl | ||
| 18 | url = https://github.com/citra-emu/ext-libressl-portable.git | ||
| 16 | [submodule "discord-rpc"] | 19 | [submodule "discord-rpc"] |
| 17 | path = externals/discord-rpc | 20 | path = externals/discord-rpc |
| 18 | url = https://github.com/discordapp/discord-rpc.git | 21 | url = https://github.com/discordapp/discord-rpc.git |
| @@ -31,3 +34,9 @@ | |||
| 31 | [submodule "xbyak"] | 34 | [submodule "xbyak"] |
| 32 | path = externals/xbyak | 35 | path = externals/xbyak |
| 33 | url = https://github.com/herumi/xbyak.git | 36 | url = https://github.com/herumi/xbyak.git |
| 37 | [submodule "externals/libusb"] | ||
| 38 | path = externals/libusb | ||
| 39 | url = https://github.com/ameerj/libusb | ||
| 40 | [submodule "opus"] | ||
| 41 | path = externals/opus/opus | ||
| 42 | url = https://github.com/xiph/opus.git | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index 61321bf0a..1c0e49c03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.11) | 1 | cmake_minimum_required(VERSION 3.15) |
| 2 | 2 | ||
| 3 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") | 3 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") |
| 4 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") | 4 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") |
| @@ -13,7 +13,7 @@ project(yuzu) | |||
| 13 | option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) | 13 | option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) |
| 14 | 14 | ||
| 15 | option(ENABLE_QT "Enable the Qt frontend" ON) | 15 | option(ENABLE_QT "Enable the Qt frontend" ON) |
| 16 | CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" OFF "ENABLE_QT;MSVC" OFF) | 16 | CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF) |
| 17 | 17 | ||
| 18 | option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) | 18 | option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) |
| 19 | 19 | ||
| @@ -151,14 +151,11 @@ macro(yuzu_find_packages) | |||
| 151 | # Cmake Pkg Prefix Version Conan Pkg | 151 | # Cmake Pkg Prefix Version Conan Pkg |
| 152 | "Boost 1.71 boost/1.72.0" | 152 | "Boost 1.71 boost/1.72.0" |
| 153 | "Catch2 2.11 catch2/2.11.0" | 153 | "Catch2 2.11 catch2/2.11.0" |
| 154 | "fmt 6.2 fmt/6.2.0" | 154 | "fmt 7.0 fmt/7.0.1" |
| 155 | "OpenSSL 1.1 openssl/1.1.1f" | ||
| 156 | # can't use until https://github.com/bincrafters/community/issues/1173 | 155 | # can't use until https://github.com/bincrafters/community/issues/1173 |
| 157 | #"libzip 1.5 libzip/1.5.2@bincrafters/stable" | 156 | #"libzip 1.5 libzip/1.5.2@bincrafters/stable" |
| 158 | "lz4 1.8 lz4/1.9.2" | 157 | "lz4 1.8 lz4/1.9.2" |
| 159 | "nlohmann_json 3.7 nlohmann_json/3.7.3" | 158 | "nlohmann_json 3.7 nlohmann_json/3.7.3" |
| 160 | # we need to be careful as the version check might be broken https://github.com/xiph/opus/issues/110 | ||
| 161 | "opus 1.3 opus/1.3.1" | ||
| 162 | "ZLIB 1.2 zlib/1.2.11" | 159 | "ZLIB 1.2 zlib/1.2.11" |
| 163 | "zstd 1.4 zstd/1.4.4" | 160 | "zstd 1.4 zstd/1.4.4" |
| 164 | ) | 161 | ) |
| @@ -214,7 +211,10 @@ if(ENABLE_QT) | |||
| 214 | 211 | ||
| 215 | set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") | 212 | set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") |
| 216 | endif() | 213 | endif() |
| 217 | find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) | 214 | find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT}) |
| 215 | if (YUZU_USE_QT_WEB_ENGINE) | ||
| 216 | find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) | ||
| 217 | endif() | ||
| 218 | if (NOT Qt5_FOUND) | 218 | if (NOT Qt5_FOUND) |
| 219 | list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") | 219 | list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") |
| 220 | endif() | 220 | endif() |
| @@ -287,7 +287,7 @@ if (CONAN_REQUIRED_LIBS) | |||
| 287 | if(ENABLE_QT) | 287 | if(ENABLE_QT) |
| 288 | list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") | 288 | list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") |
| 289 | list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}") | 289 | list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}") |
| 290 | find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets OpenGL) | 290 | find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets) |
| 291 | if (YUZU_USE_QT_WEB_ENGINE) | 291 | if (YUZU_USE_QT_WEB_ENGINE) |
| 292 | find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) | 292 | find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) |
| 293 | endif() | 293 | endif() |
| @@ -312,15 +312,6 @@ elseif (TARGET Boost::boost) | |||
| 312 | add_library(boost ALIAS Boost::boost) | 312 | add_library(boost ALIAS Boost::boost) |
| 313 | endif() | 313 | endif() |
| 314 | 314 | ||
| 315 | if (NOT TARGET OpenSSL::SSL) | ||
| 316 | set_target_properties(OpenSSL::OpenSSL PROPERTIES IMPORTED_GLOBAL TRUE) | ||
| 317 | add_library(OpenSSL::SSL ALIAS OpenSSL::OpenSSL) | ||
| 318 | endif() | ||
| 319 | if (NOT TARGET OpenSSL::Crypto) | ||
| 320 | set_target_properties(OpenSSL::OpenSSL PROPERTIES IMPORTED_GLOBAL TRUE) | ||
| 321 | add_library(OpenSSL::Crypto ALIAS OpenSSL::OpenSSL) | ||
| 322 | endif() | ||
| 323 | |||
| 324 | if (TARGET sdl2::sdl2) | 315 | if (TARGET sdl2::sdl2) |
| 325 | # imported from the conan generated sdl2Config.cmake | 316 | # imported from the conan generated sdl2Config.cmake |
| 326 | set_target_properties(sdl2::sdl2 PROPERTIES IMPORTED_GLOBAL TRUE) | 317 | set_target_properties(sdl2::sdl2 PROPERTIES IMPORTED_GLOBAL TRUE) |
| @@ -338,6 +329,15 @@ elseif(SDL2_FOUND) | |||
| 338 | target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") | 329 | target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") |
| 339 | endif() | 330 | endif() |
| 340 | 331 | ||
| 332 | # Ensure libusb is properly configured (based on dolphin libusb include) | ||
| 333 | include(FindPkgConfig) | ||
| 334 | find_package(LibUSB) | ||
| 335 | if (NOT LIBUSB_FOUND) | ||
| 336 | add_subdirectory(externals/libusb) | ||
| 337 | set(LIBUSB_LIBRARIES usb) | ||
| 338 | endif() | ||
| 339 | |||
| 340 | |||
| 341 | # Prefer the -pthread flag on Linux. | 341 | # Prefer the -pthread flag on Linux. |
| 342 | set(THREADS_PREFER_PTHREAD_FLAG ON) | 342 | set(THREADS_PREFER_PTHREAD_FLAG ON) |
| 343 | find_package(Threads REQUIRED) | 343 | find_package(Threads REQUIRED) |
diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake index 2598b9b60..59343b1ca 100644 --- a/CMakeModules/CopyYuzuQt5Deps.cmake +++ b/CMakeModules/CopyYuzuQt5Deps.cmake | |||
| @@ -15,7 +15,6 @@ function(copy_yuzu_Qt5_deps target_dir) | |||
| 15 | icuuc*.dll | 15 | icuuc*.dll |
| 16 | Qt5Core$<$<CONFIG:Debug>:d>.* | 16 | Qt5Core$<$<CONFIG:Debug>:d>.* |
| 17 | Qt5Gui$<$<CONFIG:Debug>:d>.* | 17 | Qt5Gui$<$<CONFIG:Debug>:d>.* |
| 18 | Qt5OpenGL$<$<CONFIG:Debug>:d>.* | ||
| 19 | Qt5Widgets$<$<CONFIG:Debug>:d>.* | 18 | Qt5Widgets$<$<CONFIG:Debug>:d>.* |
| 20 | ) | 19 | ) |
| 21 | 20 | ||
diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake index 83e4e9df2..311ba1c2e 100644 --- a/CMakeModules/GenerateSCMRev.cmake +++ b/CMakeModules/GenerateSCMRev.cmake | |||
| @@ -51,6 +51,8 @@ endif() | |||
| 51 | # The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR) | 51 | # The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR) |
| 52 | set(VIDEO_CORE "${SRC_DIR}/src/video_core") | 52 | set(VIDEO_CORE "${SRC_DIR}/src/video_core") |
| 53 | set(HASH_FILES | 53 | set(HASH_FILES |
| 54 | "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp" | ||
| 55 | "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h" | ||
| 54 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" | 56 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" |
| 55 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" | 57 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" |
| 56 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" | 58 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" |
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index 7d088a719..2d5c9761f 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss | |||
| @@ -673,10 +673,6 @@ QTabWidget::pane { | |||
| 673 | border-bottom-left-radius: 2px; | 673 | border-bottom-left-radius: 2px; |
| 674 | } | 674 | } |
| 675 | 675 | ||
| 676 | QTabWidget::tab-bar { | ||
| 677 | overflow: visible; | ||
| 678 | } | ||
| 679 | |||
| 680 | QTabBar { | 676 | QTabBar { |
| 681 | qproperty-drawBase: 0; | 677 | qproperty-drawBase: 0; |
| 682 | border-radius: 3px; | 678 | border-radius: 3px; |
diff --git a/dist/yuzu.manifest b/dist/yuzu.manifest index fd30b656f..038edff23 100644 --- a/dist/yuzu.manifest +++ b/dist/yuzu.manifest | |||
| @@ -1,24 +1,58 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
| 2 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | 2 | <assembly manifestVersion="1.0" |
| 3 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | 3 | xmlns="urn:schemas-microsoft-com:asm.v1" |
| 4 | <security> | 4 | xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> |
| 5 | <requestedPrivileges> | 5 | <asmv3:application> |
| 6 | <requestedExecutionLevel level="asInvoker" uiAccess="false"/> | 6 | <asmv3:windowsSettings> |
| 7 | </requestedPrivileges> | 7 | <!-- Windows 7/8/8.1/10 --> |
| 8 | </security> | 8 | <dpiAware |
| 9 | </trustInfo> | 9 | xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> |
| 10 | <application xmlns="urn:schemas-microsoft-com:asm.v3"> | 10 | true/pm |
| 11 | <windowsSettings> | 11 | </dpiAware> |
| 12 | <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> | 12 | <!-- Windows 10, version 1607 or later --> |
| 13 | <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> | 13 | <dpiAwareness |
| 14 | </windowsSettings> | 14 | xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> |
| 15 | </application> | 15 | PerMonitorV2 |
| 16 | <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | 16 | </dpiAwareness> |
| 17 | <application> | 17 | <!-- Windows 10, version 1703 or later --> |
| 18 | <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | 18 | <gdiScaling |
| 19 | <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | 19 | xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings"> |
| 20 | <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | 20 | true |
| 21 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | 21 | </gdiScaling> |
| 22 | </application> | 22 | <ws2:longPathAware |
| 23 | </compatibility> | 23 | xmlns:ws3="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> |
| 24 | </assembly> \ No newline at end of file | 24 | true |
| 25 | </ws2:longPathAware> | ||
| 26 | </asmv3:windowsSettings> | ||
| 27 | </asmv3:application> | ||
| 28 | <compatibility | ||
| 29 | xmlns="urn:schemas-microsoft-com:compatibility.v1"> | ||
| 30 | <application> | ||
| 31 | <!-- Windows 10 --> | ||
| 32 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | ||
| 33 | <!-- Windows 8.1 --> | ||
| 34 | <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | ||
| 35 | <!-- Windows 8 --> | ||
| 36 | <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | ||
| 37 | <!-- Windows 7 --> | ||
| 38 | <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | ||
| 39 | </application> | ||
| 40 | </compatibility> | ||
| 41 | <trustInfo | ||
| 42 | xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
| 43 | <security> | ||
| 44 | <requestedPrivileges> | ||
| 45 | <!-- | ||
| 46 | UAC settings: | ||
| 47 | - app should run at same integrity level as calling process | ||
| 48 | - app does not need to manipulate windows belonging to | ||
| 49 | higher-integrity-level processes | ||
| 50 | --> | ||
| 51 | <requestedExecutionLevel | ||
| 52 | level="asInvoker" | ||
| 53 | uiAccess="false" | ||
| 54 | /> | ||
| 55 | </requestedPrivileges> | ||
| 56 | </security> | ||
| 57 | </trustInfo> | ||
| 58 | </assembly> | ||
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index df7a5e0a9..d1dcc403b 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -4,6 +4,13 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules") | |||
| 4 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/externals/find-modules") | 4 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/externals/find-modules") |
| 5 | include(DownloadExternals) | 5 | include(DownloadExternals) |
| 6 | 6 | ||
| 7 | # xbyak | ||
| 8 | if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) | ||
| 9 | add_library(xbyak INTERFACE) | ||
| 10 | target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak) | ||
| 11 | target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) | ||
| 12 | endif() | ||
| 13 | |||
| 7 | # Catch | 14 | # Catch |
| 8 | add_library(catch-single-include INTERFACE) | 15 | add_library(catch-single-include INTERFACE) |
| 9 | target_include_directories(catch-single-include INTERFACE catch/single_include) | 16 | target_include_directories(catch-single-include INTERFACE catch/single_include) |
| @@ -66,6 +73,15 @@ if (NOT LIBZIP_FOUND) | |||
| 66 | endif() | 73 | endif() |
| 67 | 74 | ||
| 68 | if (ENABLE_WEB_SERVICE) | 75 | if (ENABLE_WEB_SERVICE) |
| 76 | # LibreSSL | ||
| 77 | set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") | ||
| 78 | add_subdirectory(libressl EXCLUDE_FROM_ALL) | ||
| 79 | target_include_directories(ssl INTERFACE ./libressl/include) | ||
| 80 | target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP) | ||
| 81 | get_directory_property(OPENSSL_LIBRARIES | ||
| 82 | DIRECTORY libressl | ||
| 83 | DEFINITION OPENSSL_LIBS) | ||
| 84 | |||
| 69 | # lurlparser | 85 | # lurlparser |
| 70 | add_subdirectory(lurlparser EXCLUDE_FROM_ALL) | 86 | add_subdirectory(lurlparser EXCLUDE_FROM_ALL) |
| 71 | 87 | ||
| @@ -73,13 +89,8 @@ if (ENABLE_WEB_SERVICE) | |||
| 73 | add_library(httplib INTERFACE) | 89 | add_library(httplib INTERFACE) |
| 74 | target_include_directories(httplib INTERFACE ./httplib) | 90 | target_include_directories(httplib INTERFACE ./httplib) |
| 75 | target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) | 91 | target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) |
| 76 | target_link_libraries(httplib INTERFACE OpenSSL::SSL OpenSSL::Crypto) | 92 | target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES}) |
| 77 | endif() | 93 | endif() |
| 78 | 94 | ||
| 79 | if (NOT TARGET xbyak) | 95 | # Opus |
| 80 | if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) | 96 | add_subdirectory(opus) |
| 81 | add_library(xbyak INTERFACE) | ||
| 82 | target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak) | ||
| 83 | target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) | ||
| 84 | endif() | ||
| 85 | endif() | ||
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers | |||
| Subproject 9250d5ae8f50202005233dc0512a1d460c8b483 | Subproject 8188e3fbbc105591064093440f88081fb957d4f | ||
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject e7166e8ba74d7b9c85e87afc0aaf667e7e84cfe | Subproject 4f967387c07365b7ea35d2fa3e19b7df8872a09 | ||
diff --git a/externals/find-modules/FindLibUSB.cmake b/externals/find-modules/FindLibUSB.cmake new file mode 100644 index 000000000..dec0b98b0 --- /dev/null +++ b/externals/find-modules/FindLibUSB.cmake | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | # - Find libusb-1.0 library | ||
| 2 | # This module defines | ||
| 3 | # LIBUSB_INCLUDE_DIR, where to find bluetooth.h | ||
| 4 | # LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0. | ||
| 5 | # LIBUSB_FOUND, If false, do not try to use libusb-1.0. | ||
| 6 | # | ||
| 7 | # Copyright (c) 2009, Michal Cihar, <michal@cihar.com> | ||
| 8 | # | ||
| 9 | # vim: expandtab sw=4 ts=4 sts=4: | ||
| 10 | |||
| 11 | if(ANDROID) | ||
| 12 | set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") | ||
| 13 | message(STATUS "libusb-1.0 not found.") | ||
| 14 | elseif (NOT LIBUSB_FOUND) | ||
| 15 | pkg_check_modules (LIBUSB_PKG libusb-1.0) | ||
| 16 | |||
| 17 | find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h | ||
| 18 | PATHS | ||
| 19 | ${LIBUSB_PKG_INCLUDE_DIRS} | ||
| 20 | /usr/include/libusb-1.0 | ||
| 21 | /usr/include | ||
| 22 | /usr/local/include/libusb-1.0 | ||
| 23 | /usr/local/include | ||
| 24 | ) | ||
| 25 | |||
| 26 | find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb | ||
| 27 | PATHS | ||
| 28 | ${LIBUSB_PKG_LIBRARY_DIRS} | ||
| 29 | /usr/lib | ||
| 30 | /usr/local/lib | ||
| 31 | ) | ||
| 32 | |||
| 33 | if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) | ||
| 34 | set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found") | ||
| 35 | message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}") | ||
| 36 | else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) | ||
| 37 | set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") | ||
| 38 | message(STATUS "libusb-1.0 not found.") | ||
| 39 | endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) | ||
| 40 | |||
| 41 | mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES) | ||
| 42 | endif () | ||
| 43 | |||
diff --git a/externals/libressl b/externals/libressl new file mode 160000 | |||
| Subproject 7d01cb01cb1a926ecb4c9c98b107ef3c26f59df | |||
diff --git a/externals/libusb b/externals/libusb new file mode 160000 | |||
| Subproject 3406d72cda879f8792a88bf5f6bd0b7a65636f7 | |||
diff --git a/externals/opus/CMakeLists.txt b/externals/opus/CMakeLists.txt new file mode 100644 index 000000000..94a86551f --- /dev/null +++ b/externals/opus/CMakeLists.txt | |||
| @@ -0,0 +1,254 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.8) | ||
| 2 | |||
| 3 | project(opus) | ||
| 4 | |||
| 5 | option(OPUS_STACK_PROTECTOR "Use stack protection" OFF) | ||
| 6 | option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF) | ||
| 7 | option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF) | ||
| 8 | option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF) | ||
| 9 | option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON) | ||
| 10 | |||
| 11 | include(opus/opus_functions.cmake) | ||
| 12 | |||
| 13 | if(OPUS_STACK_PROTECTOR) | ||
| 14 | if(NOT MSVC) # GC on by default on MSVC | ||
| 15 | check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong) | ||
| 16 | endif() | ||
| 17 | else() | ||
| 18 | if(MSVC) | ||
| 19 | check_and_set_flag(BUFFER_SECURITY_CHECK /GS-) | ||
| 20 | endif() | ||
| 21 | endif() | ||
| 22 | |||
| 23 | add_library(opus STATIC | ||
| 24 | # CELT sources | ||
| 25 | opus/celt/bands.c | ||
| 26 | opus/celt/celt.c | ||
| 27 | opus/celt/celt_decoder.c | ||
| 28 | opus/celt/celt_encoder.c | ||
| 29 | opus/celt/celt_lpc.c | ||
| 30 | opus/celt/cwrs.c | ||
| 31 | opus/celt/entcode.c | ||
| 32 | opus/celt/entdec.c | ||
| 33 | opus/celt/entenc.c | ||
| 34 | opus/celt/kiss_fft.c | ||
| 35 | opus/celt/laplace.c | ||
| 36 | opus/celt/mathops.c | ||
| 37 | opus/celt/mdct.c | ||
| 38 | opus/celt/modes.c | ||
| 39 | opus/celt/pitch.c | ||
| 40 | opus/celt/quant_bands.c | ||
| 41 | opus/celt/rate.c | ||
| 42 | opus/celt/vq.c | ||
| 43 | |||
| 44 | # SILK sources | ||
| 45 | opus/silk/A2NLSF.c | ||
| 46 | opus/silk/CNG.c | ||
| 47 | opus/silk/HP_variable_cutoff.c | ||
| 48 | opus/silk/LPC_analysis_filter.c | ||
| 49 | opus/silk/LPC_fit.c | ||
| 50 | opus/silk/LPC_inv_pred_gain.c | ||
| 51 | opus/silk/LP_variable_cutoff.c | ||
| 52 | opus/silk/NLSF2A.c | ||
| 53 | opus/silk/NLSF_VQ.c | ||
| 54 | opus/silk/NLSF_VQ_weights_laroia.c | ||
| 55 | opus/silk/NLSF_decode.c | ||
| 56 | opus/silk/NLSF_del_dec_quant.c | ||
| 57 | opus/silk/NLSF_encode.c | ||
| 58 | opus/silk/NLSF_stabilize.c | ||
| 59 | opus/silk/NLSF_unpack.c | ||
| 60 | opus/silk/NSQ.c | ||
| 61 | opus/silk/NSQ_del_dec.c | ||
| 62 | opus/silk/PLC.c | ||
| 63 | opus/silk/VAD.c | ||
| 64 | opus/silk/VQ_WMat_EC.c | ||
| 65 | opus/silk/ana_filt_bank_1.c | ||
| 66 | opus/silk/biquad_alt.c | ||
| 67 | opus/silk/bwexpander.c | ||
| 68 | opus/silk/bwexpander_32.c | ||
| 69 | opus/silk/check_control_input.c | ||
| 70 | opus/silk/code_signs.c | ||
| 71 | opus/silk/control_SNR.c | ||
| 72 | opus/silk/control_audio_bandwidth.c | ||
| 73 | opus/silk/control_codec.c | ||
| 74 | opus/silk/dec_API.c | ||
| 75 | opus/silk/decode_core.c | ||
| 76 | opus/silk/decode_frame.c | ||
| 77 | opus/silk/decode_indices.c | ||
| 78 | opus/silk/decode_parameters.c | ||
| 79 | opus/silk/decode_pitch.c | ||
| 80 | opus/silk/decode_pulses.c | ||
| 81 | opus/silk/decoder_set_fs.c | ||
| 82 | opus/silk/enc_API.c | ||
| 83 | opus/silk/encode_indices.c | ||
| 84 | opus/silk/encode_pulses.c | ||
| 85 | opus/silk/gain_quant.c | ||
| 86 | opus/silk/init_decoder.c | ||
| 87 | opus/silk/init_encoder.c | ||
| 88 | opus/silk/inner_prod_aligned.c | ||
| 89 | opus/silk/interpolate.c | ||
| 90 | opus/silk/lin2log.c | ||
| 91 | opus/silk/log2lin.c | ||
| 92 | opus/silk/pitch_est_tables.c | ||
| 93 | opus/silk/process_NLSFs.c | ||
| 94 | opus/silk/quant_LTP_gains.c | ||
| 95 | opus/silk/resampler.c | ||
| 96 | opus/silk/resampler_down2.c | ||
| 97 | opus/silk/resampler_down2_3.c | ||
| 98 | opus/silk/resampler_private_AR2.c | ||
| 99 | opus/silk/resampler_private_IIR_FIR.c | ||
| 100 | opus/silk/resampler_private_down_FIR.c | ||
| 101 | opus/silk/resampler_private_up2_HQ.c | ||
| 102 | opus/silk/resampler_rom.c | ||
| 103 | opus/silk/shell_coder.c | ||
| 104 | opus/silk/sigm_Q15.c | ||
| 105 | opus/silk/sort.c | ||
| 106 | opus/silk/stereo_LR_to_MS.c | ||
| 107 | opus/silk/stereo_MS_to_LR.c | ||
| 108 | opus/silk/stereo_decode_pred.c | ||
| 109 | opus/silk/stereo_encode_pred.c | ||
| 110 | opus/silk/stereo_find_predictor.c | ||
| 111 | opus/silk/stereo_quant_pred.c | ||
| 112 | opus/silk/sum_sqr_shift.c | ||
| 113 | opus/silk/table_LSF_cos.c | ||
| 114 | opus/silk/tables_LTP.c | ||
| 115 | opus/silk/tables_NLSF_CB_NB_MB.c | ||
| 116 | opus/silk/tables_NLSF_CB_WB.c | ||
| 117 | opus/silk/tables_gain.c | ||
| 118 | opus/silk/tables_other.c | ||
| 119 | opus/silk/tables_pitch_lag.c | ||
| 120 | opus/silk/tables_pulses_per_block.c | ||
| 121 | |||
| 122 | # Opus sources | ||
| 123 | opus/src/analysis.c | ||
| 124 | opus/src/mapping_matrix.c | ||
| 125 | opus/src/mlp.c | ||
| 126 | opus/src/mlp_data.c | ||
| 127 | opus/src/opus.c | ||
| 128 | opus/src/opus_decoder.c | ||
| 129 | opus/src/opus_encoder.c | ||
| 130 | opus/src/opus_multistream.c | ||
| 131 | opus/src/opus_multistream_decoder.c | ||
| 132 | opus/src/opus_multistream_encoder.c | ||
| 133 | opus/src/opus_projection_decoder.c | ||
| 134 | opus/src/opus_projection_encoder.c | ||
| 135 | opus/src/repacketizer.c | ||
| 136 | ) | ||
| 137 | |||
| 138 | if (DEBUG) | ||
| 139 | target_sources(opus PRIVATE opus/silk/debug.c) | ||
| 140 | endif() | ||
| 141 | |||
| 142 | if (OPUS_FIXED_POINT) | ||
| 143 | target_sources(opus PRIVATE | ||
| 144 | opus/silk/fixed/LTP_analysis_filter_FIX.c | ||
| 145 | opus/silk/fixed/LTP_scale_ctrl_FIX.c | ||
| 146 | opus/silk/fixed/apply_sine_window_FIX.c | ||
| 147 | opus/silk/fixed/autocorr_FIX.c | ||
| 148 | opus/silk/fixed/burg_modified_FIX.c | ||
| 149 | opus/silk/fixed/corrMatrix_FIX.c | ||
| 150 | opus/silk/fixed/encode_frame_FIX.c | ||
| 151 | opus/silk/fixed/find_LPC_FIX.c | ||
| 152 | opus/silk/fixed/find_LTP_FIX.c | ||
| 153 | opus/silk/fixed/find_pitch_lags_FIX.c | ||
| 154 | opus/silk/fixed/find_pred_coefs_FIX.c | ||
| 155 | opus/silk/fixed/k2a_FIX.c | ||
| 156 | opus/silk/fixed/k2a_Q16_FIX.c | ||
| 157 | opus/silk/fixed/noise_shape_analysis_FIX.c | ||
| 158 | opus/silk/fixed/pitch_analysis_core_FIX.c | ||
| 159 | opus/silk/fixed/prefilter_FIX.c | ||
| 160 | opus/silk/fixed/process_gains_FIX.c | ||
| 161 | opus/silk/fixed/regularize_correlations_FIX.c | ||
| 162 | opus/silk/fixed/residual_energy16_FIX.c | ||
| 163 | opus/silk/fixed/residual_energy_FIX.c | ||
| 164 | opus/silk/fixed/schur64_FIX.c | ||
| 165 | opus/silk/fixed/schur_FIX.c | ||
| 166 | opus/silk/fixed/solve_LS_FIX.c | ||
| 167 | opus/silk/fixed/vector_ops_FIX.c | ||
| 168 | opus/silk/fixed/warped_autocorrelation_FIX.c | ||
| 169 | ) | ||
| 170 | else() | ||
| 171 | target_sources(opus PRIVATE | ||
| 172 | opus/silk/float/LPC_analysis_filter_FLP.c | ||
| 173 | opus/silk/float/LPC_inv_pred_gain_FLP.c | ||
| 174 | opus/silk/float/LTP_analysis_filter_FLP.c | ||
| 175 | opus/silk/float/LTP_scale_ctrl_FLP.c | ||
| 176 | opus/silk/float/apply_sine_window_FLP.c | ||
| 177 | opus/silk/float/autocorrelation_FLP.c | ||
| 178 | opus/silk/float/burg_modified_FLP.c | ||
| 179 | opus/silk/float/bwexpander_FLP.c | ||
| 180 | opus/silk/float/corrMatrix_FLP.c | ||
| 181 | opus/silk/float/encode_frame_FLP.c | ||
| 182 | opus/silk/float/energy_FLP.c | ||
| 183 | opus/silk/float/find_LPC_FLP.c | ||
| 184 | opus/silk/float/find_LTP_FLP.c | ||
| 185 | opus/silk/float/find_pitch_lags_FLP.c | ||
| 186 | opus/silk/float/find_pred_coefs_FLP.c | ||
| 187 | opus/silk/float/inner_product_FLP.c | ||
| 188 | opus/silk/float/k2a_FLP.c | ||
| 189 | opus/silk/float/noise_shape_analysis_FLP.c | ||
| 190 | opus/silk/float/pitch_analysis_core_FLP.c | ||
| 191 | opus/silk/float/process_gains_FLP.c | ||
| 192 | opus/silk/float/regularize_correlations_FLP.c | ||
| 193 | opus/silk/float/residual_energy_FLP.c | ||
| 194 | opus/silk/float/scale_copy_vector_FLP.c | ||
| 195 | opus/silk/float/scale_vector_FLP.c | ||
| 196 | opus/silk/float/schur_FLP.c | ||
| 197 | opus/silk/float/sort_FLP.c | ||
| 198 | opus/silk/float/warped_autocorrelation_FLP.c | ||
| 199 | opus/silk/float/wrappers_FLP.c | ||
| 200 | ) | ||
| 201 | endif() | ||
| 202 | |||
| 203 | target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING) | ||
| 204 | |||
| 205 | if(NOT MSVC) | ||
| 206 | if(MINGW) | ||
| 207 | target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0) | ||
| 208 | else() | ||
| 209 | target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2) | ||
| 210 | endif() | ||
| 211 | endif() | ||
| 212 | |||
| 213 | # It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99 | ||
| 214 | # variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack | ||
| 215 | # allocation If none is defined, then the fallback is a non-threadsafe global | ||
| 216 | # array | ||
| 217 | if(OPUS_USE_ALLOCA OR MSVC) | ||
| 218 | target_compile_definitions(opus PRIVATE USE_ALLOCA) | ||
| 219 | else() | ||
| 220 | target_compile_definitions(opus PRIVATE VAR_ARRAYS) | ||
| 221 | endif() | ||
| 222 | |||
| 223 | if(OPUS_CUSTOM_MODES) | ||
| 224 | target_compile_definitions(opus PRIVATE CUSTOM_MODES) | ||
| 225 | endif() | ||
| 226 | |||
| 227 | if(NOT OPUS_ENABLE_FLOAT_API) | ||
| 228 | target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API) | ||
| 229 | endif() | ||
| 230 | |||
| 231 | target_compile_definitions(opus | ||
| 232 | PUBLIC | ||
| 233 | -DOPUS_VERSION="\\"1.3.1\\"" | ||
| 234 | |||
| 235 | PRIVATE | ||
| 236 | # Use C99 intrinsics to speed up float-to-int conversion | ||
| 237 | HAVE_LRINTF | ||
| 238 | ) | ||
| 239 | |||
| 240 | if (FIXED_POINT) | ||
| 241 | target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API) | ||
| 242 | endif() | ||
| 243 | |||
| 244 | target_include_directories(opus | ||
| 245 | PUBLIC | ||
| 246 | opus/include | ||
| 247 | |||
| 248 | PRIVATE | ||
| 249 | opus/celt | ||
| 250 | opus/silk | ||
| 251 | opus/silk/fixed | ||
| 252 | opus/silk/float | ||
| 253 | opus/src | ||
| 254 | ) | ||
diff --git a/externals/opus/opus b/externals/opus/opus new file mode 160000 | |||
| Subproject ad8fe90db79b7d2a135e3dfd2ed6631b0c5662a | |||
diff --git a/externals/sirit b/externals/sirit | |||
| Subproject a62c5bbc100a5e5a31ea0ccc4a78d8fa6a4167c | Subproject eefca56afd49379bdebc97ded8b480839f93088 | ||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3a57356ab..1e977e8a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -62,6 +62,10 @@ else() | |||
| 62 | -Wno-unused-parameter | 62 | -Wno-unused-parameter |
| 63 | ) | 63 | ) |
| 64 | 64 | ||
| 65 | if (ARCHITECTURE_x86_64) | ||
| 66 | add_compile_options("-mcx16") | ||
| 67 | endif() | ||
| 68 | |||
| 65 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) | 69 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) |
| 66 | add_compile_options("-stdlib=libc++") | 70 | add_compile_options("-stdlib=libc++") |
| 67 | endif() | 71 | endif() |
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 50846a854..d64452617 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -180,11 +180,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector< | |||
| 180 | 180 | ||
| 181 | // Copy output header | 181 | // Copy output header |
| 182 | UpdateDataHeader response_data{worker_params}; | 182 | UpdateDataHeader response_data{worker_params}; |
| 183 | std::vector<u8> output_params(response_data.total_size); | ||
| 184 | if (behavior_info.IsElapsedFrameCountSupported()) { | 183 | if (behavior_info.IsElapsedFrameCountSupported()) { |
| 185 | response_data.frame_count = 0x10; | 184 | response_data.render_info = sizeof(RendererInfo); |
| 186 | response_data.total_size += 0x10; | 185 | response_data.total_size += sizeof(RendererInfo); |
| 187 | } | 186 | } |
| 187 | |||
| 188 | std::vector<u8> output_params(response_data.total_size); | ||
| 188 | std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); | 189 | std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); |
| 189 | 190 | ||
| 190 | // Copy output memory pool entries | 191 | // Copy output memory pool entries |
| @@ -219,6 +220,17 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector< | |||
| 219 | return Audren::ERR_INVALID_PARAMETERS; | 220 | return Audren::ERR_INVALID_PARAMETERS; |
| 220 | } | 221 | } |
| 221 | 222 | ||
| 223 | if (behavior_info.IsElapsedFrameCountSupported()) { | ||
| 224 | const std::size_t renderer_info_offset{ | ||
| 225 | sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + | ||
| 226 | response_data.effects_size + response_data.sinks_size + | ||
| 227 | response_data.performance_manager_size + response_data.behavior_size}; | ||
| 228 | RendererInfo renderer_info{}; | ||
| 229 | renderer_info.elasped_frame_count = elapsed_frame_count; | ||
| 230 | std::memcpy(output_params.data() + renderer_info_offset, &renderer_info, | ||
| 231 | sizeof(RendererInfo)); | ||
| 232 | } | ||
| 233 | |||
| 222 | return MakeResult(output_params); | 234 | return MakeResult(output_params); |
| 223 | } | 235 | } |
| 224 | 236 | ||
| @@ -447,6 +459,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 447 | } | 459 | } |
| 448 | } | 460 | } |
| 449 | audio_out->QueueBuffer(stream, tag, std::move(buffer)); | 461 | audio_out->QueueBuffer(stream, tag, std::move(buffer)); |
| 462 | elapsed_frame_count++; | ||
| 450 | } | 463 | } |
| 451 | 464 | ||
| 452 | void AudioRenderer::ReleaseAndQueueBuffers() { | 465 | void AudioRenderer::ReleaseAndQueueBuffers() { |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 1f9114c07..f0b691a86 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -196,6 +196,12 @@ struct EffectOutStatus { | |||
| 196 | }; | 196 | }; |
| 197 | static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size"); | 197 | static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size"); |
| 198 | 198 | ||
| 199 | struct RendererInfo { | ||
| 200 | u64_le elasped_frame_count{}; | ||
| 201 | INSERT_PADDING_WORDS(2); | ||
| 202 | }; | ||
| 203 | static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size"); | ||
| 204 | |||
| 199 | struct UpdateDataHeader { | 205 | struct UpdateDataHeader { |
| 200 | UpdateDataHeader() {} | 206 | UpdateDataHeader() {} |
| 201 | 207 | ||
| @@ -209,7 +215,7 @@ struct UpdateDataHeader { | |||
| 209 | mixes_size = 0x0; | 215 | mixes_size = 0x0; |
| 210 | sinks_size = config.sink_count * 0x20; | 216 | sinks_size = config.sink_count * 0x20; |
| 211 | performance_manager_size = 0x10; | 217 | performance_manager_size = 0x10; |
| 212 | frame_count = 0; | 218 | render_info = 0; |
| 213 | total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + | 219 | total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + |
| 214 | effects_size + sinks_size + performance_manager_size; | 220 | effects_size + sinks_size + performance_manager_size; |
| 215 | } | 221 | } |
| @@ -223,8 +229,8 @@ struct UpdateDataHeader { | |||
| 223 | u32_le mixes_size{}; | 229 | u32_le mixes_size{}; |
| 224 | u32_le sinks_size{}; | 230 | u32_le sinks_size{}; |
| 225 | u32_le performance_manager_size{}; | 231 | u32_le performance_manager_size{}; |
| 226 | INSERT_PADDING_WORDS(1); | 232 | u32_le splitter_size{}; |
| 227 | u32_le frame_count{}; | 233 | u32_le render_info{}; |
| 228 | INSERT_PADDING_WORDS(4); | 234 | INSERT_PADDING_WORDS(4); |
| 229 | u32_le total_size{}; | 235 | u32_le total_size{}; |
| 230 | }; | 236 | }; |
| @@ -258,6 +264,7 @@ private: | |||
| 258 | std::unique_ptr<AudioOut> audio_out; | 264 | std::unique_ptr<AudioOut> audio_out; |
| 259 | StreamPtr stream; | 265 | StreamPtr stream; |
| 260 | Core::Memory::Memory& memory; | 266 | Core::Memory::Memory& memory; |
| 267 | std::size_t elapsed_frame_count{}; | ||
| 261 | }; | 268 | }; |
| 262 | 269 | ||
| 263 | } // namespace AudioCore | 270 | } // namespace AudioCore |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index c4e0e30fe..41bf5cd4d 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const | |||
| 193 | const std::size_t samples_to_write = num_channels * num_frames; | 193 | const std::size_t samples_to_write = num_channels * num_frames; |
| 194 | std::size_t samples_written; | 194 | std::size_t samples_written; |
| 195 | 195 | ||
| 196 | if (Settings::values.enable_audio_stretching) { | 196 | if (Settings::values.enable_audio_stretching.GetValue()) { |
| 197 | const std::vector<s16> in{impl->queue.Pop()}; | 197 | const std::vector<s16> in{impl->queue.Pop()}; |
| 198 | const std::size_t num_in{in.size() / num_channels}; | 198 | const std::size_t num_in{in.size() / num_channels}; |
| 199 | s16* const out{reinterpret_cast<s16*>(buffer)}; | 199 | s16* const out{reinterpret_cast<s16*>(buffer)}; |
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 4ca98f8ea..aab3e979a 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo | |||
| 38 | sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { | 38 | sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { |
| 39 | 39 | ||
| 40 | release_event = Core::Timing::CreateEvent( | 40 | release_event = Core::Timing::CreateEvent( |
| 41 | name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); }); | 41 | name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); }); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | void Stream::Play() { | 44 | void Stream::Play() { |
| @@ -59,15 +59,15 @@ Stream::State Stream::GetState() const { | |||
| 59 | return state; | 59 | return state; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { | 62 | s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const { |
| 63 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; | 63 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; |
| 64 | const auto us = | 64 | const auto ns = |
| 65 | std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate); | 65 | std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate); |
| 66 | return Core::Timing::usToCycles(us); | 66 | return ns.count(); |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { | 69 | static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { |
| 70 | const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)}; | 70 | const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)}; |
| 71 | 71 | ||
| 72 | if (volume == 1.0f) { | 72 | if (volume == 1.0f) { |
| 73 | return; | 73 | return; |
| @@ -80,7 +80,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { | |||
| 80 | } | 80 | } |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | void Stream::PlayNextBuffer() { | 83 | void Stream::PlayNextBuffer(s64 cycles_late) { |
| 84 | if (!IsPlaying()) { | 84 | if (!IsPlaying()) { |
| 85 | // Ensure we are in playing state before playing the next buffer | 85 | // Ensure we are in playing state before playing the next buffer |
| 86 | sink_stream.Flush(); | 86 | sink_stream.Flush(); |
| @@ -105,14 +105,17 @@ void Stream::PlayNextBuffer() { | |||
| 105 | 105 | ||
| 106 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); | 106 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); |
| 107 | 107 | ||
| 108 | core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {}); | 108 | core_timing.ScheduleEvent( |
| 109 | GetBufferReleaseNS(*active_buffer) - | ||
| 110 | (Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late), | ||
| 111 | release_event, {}); | ||
| 109 | } | 112 | } |
| 110 | 113 | ||
| 111 | void Stream::ReleaseActiveBuffer() { | 114 | void Stream::ReleaseActiveBuffer(s64 cycles_late) { |
| 112 | ASSERT(active_buffer); | 115 | ASSERT(active_buffer); |
| 113 | released_buffers.push(std::move(active_buffer)); | 116 | released_buffers.push(std::move(active_buffer)); |
| 114 | release_callback(); | 117 | release_callback(); |
| 115 | PlayNextBuffer(); | 118 | PlayNextBuffer(cycles_late); |
| 116 | } | 119 | } |
| 117 | 120 | ||
| 118 | bool Stream::QueueBuffer(BufferPtr&& buffer) { | 121 | bool Stream::QueueBuffer(BufferPtr&& buffer) { |
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 1708a4d98..524376257 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h | |||
| @@ -90,13 +90,16 @@ public: | |||
| 90 | 90 | ||
| 91 | private: | 91 | private: |
| 92 | /// Plays the next queued buffer in the audio stream, starting playback if necessary | 92 | /// Plays the next queued buffer in the audio stream, starting playback if necessary |
| 93 | void PlayNextBuffer(); | 93 | void PlayNextBuffer(s64 cycles_late = 0); |
| 94 | 94 | ||
| 95 | /// Releases the actively playing buffer, signalling that it has been completed | 95 | /// Releases the actively playing buffer, signalling that it has been completed |
| 96 | void ReleaseActiveBuffer(); | 96 | void ReleaseActiveBuffer(s64 cycles_late = 0); |
| 97 | 97 | ||
| 98 | /// Gets the number of core cycles when the specified buffer will be released | 98 | /// Gets the number of core cycles when the specified buffer will be released |
| 99 | s64 GetBufferReleaseCycles(const Buffer& buffer) const; | 99 | s64 GetBufferReleaseNS(const Buffer& buffer) const; |
| 100 | |||
| 101 | /// Gets the number of core cycles when the specified buffer will be released | ||
| 102 | s64 GetBufferReleaseNSHostTiming(const Buffer& buffer) const; | ||
| 100 | 103 | ||
| 101 | u32 sample_rate; ///< Sample rate of the stream | 104 | u32 sample_rate; ///< Sample rate of the stream |
| 102 | Format format; ///< Format of the stream | 105 | Format format; ///< Format of the stream |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 24b7a083c..d120c8d3d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -32,6 +32,8 @@ add_custom_command(OUTPUT scm_rev.cpp | |||
| 32 | DEPENDS | 32 | DEPENDS |
| 33 | # WARNING! It was too much work to try and make a common location for this list, | 33 | # WARNING! It was too much work to try and make a common location for this list, |
| 34 | # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well | 34 | # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well |
| 35 | "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp" | ||
| 36 | "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h" | ||
| 35 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" | 37 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" |
| 36 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" | 38 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" |
| 37 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" | 39 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" |
| @@ -96,6 +98,8 @@ add_library(common STATIC | |||
| 96 | algorithm.h | 98 | algorithm.h |
| 97 | alignment.h | 99 | alignment.h |
| 98 | assert.h | 100 | assert.h |
| 101 | atomic_ops.cpp | ||
| 102 | atomic_ops.h | ||
| 99 | detached_tasks.cpp | 103 | detached_tasks.cpp |
| 100 | detached_tasks.h | 104 | detached_tasks.h |
| 101 | bit_field.h | 105 | bit_field.h |
| @@ -108,6 +112,8 @@ add_library(common STATIC | |||
| 108 | common_types.h | 112 | common_types.h |
| 109 | dynamic_library.cpp | 113 | dynamic_library.cpp |
| 110 | dynamic_library.h | 114 | dynamic_library.h |
| 115 | fiber.cpp | ||
| 116 | fiber.h | ||
| 111 | file_util.cpp | 117 | file_util.cpp |
| 112 | file_util.h | 118 | file_util.h |
| 113 | hash.h | 119 | hash.h |
| @@ -141,6 +147,8 @@ add_library(common STATIC | |||
| 141 | scm_rev.cpp | 147 | scm_rev.cpp |
| 142 | scm_rev.h | 148 | scm_rev.h |
| 143 | scope_exit.h | 149 | scope_exit.h |
| 150 | spin_lock.cpp | ||
| 151 | spin_lock.h | ||
| 144 | string_util.cpp | 152 | string_util.cpp |
| 145 | string_util.h | 153 | string_util.h |
| 146 | swap.h | 154 | swap.h |
| @@ -161,6 +169,8 @@ add_library(common STATIC | |||
| 161 | vector_math.h | 169 | vector_math.h |
| 162 | virtual_buffer.cpp | 170 | virtual_buffer.cpp |
| 163 | virtual_buffer.h | 171 | virtual_buffer.h |
| 172 | wall_clock.cpp | ||
| 173 | wall_clock.h | ||
| 164 | web_result.h | 174 | web_result.h |
| 165 | zstd_compression.cpp | 175 | zstd_compression.cpp |
| 166 | zstd_compression.h | 176 | zstd_compression.h |
| @@ -171,12 +181,15 @@ if(ARCHITECTURE_x86_64) | |||
| 171 | PRIVATE | 181 | PRIVATE |
| 172 | x64/cpu_detect.cpp | 182 | x64/cpu_detect.cpp |
| 173 | x64/cpu_detect.h | 183 | x64/cpu_detect.h |
| 184 | x64/native_clock.cpp | ||
| 185 | x64/native_clock.h | ||
| 174 | x64/xbyak_abi.h | 186 | x64/xbyak_abi.h |
| 175 | x64/xbyak_util.h | 187 | x64/xbyak_util.h |
| 176 | ) | 188 | ) |
| 177 | endif() | 189 | endif() |
| 178 | 190 | ||
| 179 | create_target_directory_groups(common) | 191 | create_target_directory_groups(common) |
| 192 | find_package(Boost 1.71 COMPONENTS context headers REQUIRED) | ||
| 180 | 193 | ||
| 181 | target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile) | 194 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) |
| 182 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) | 195 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) |
diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp new file mode 100644 index 000000000..1098e21ff --- /dev/null +++ b/src/common/atomic_ops.cpp | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "common/atomic_ops.h" | ||
| 8 | |||
| 9 | #if _MSC_VER | ||
| 10 | #include <intrin.h> | ||
| 11 | #endif | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | #if _MSC_VER | ||
| 16 | |||
| 17 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { | ||
| 18 | u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected); | ||
| 19 | return result == expected; | ||
| 20 | } | ||
| 21 | |||
| 22 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { | ||
| 23 | u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected); | ||
| 24 | return result == expected; | ||
| 25 | } | ||
| 26 | |||
| 27 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { | ||
| 28 | u32 result = _InterlockedCompareExchange((long*)pointer, value, expected); | ||
| 29 | return result == expected; | ||
| 30 | } | ||
| 31 | |||
| 32 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { | ||
| 33 | u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected); | ||
| 34 | return result == expected; | ||
| 35 | } | ||
| 36 | |||
| 37 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { | ||
| 38 | return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], | ||
| 39 | (__int64*)expected.data()) != 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | #else | ||
| 43 | |||
| 44 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { | ||
| 45 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 46 | } | ||
| 47 | |||
| 48 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { | ||
| 49 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 50 | } | ||
| 51 | |||
| 52 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { | ||
| 53 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 54 | } | ||
| 55 | |||
| 56 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { | ||
| 57 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 58 | } | ||
| 59 | |||
| 60 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { | ||
| 61 | unsigned __int128 value_a; | ||
| 62 | unsigned __int128 expected_a; | ||
| 63 | std::memcpy(&value_a, value.data(), sizeof(u128)); | ||
| 64 | std::memcpy(&expected_a, expected.data(), sizeof(u128)); | ||
| 65 | return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); | ||
| 66 | } | ||
| 67 | |||
| 68 | #endif | ||
| 69 | |||
| 70 | } // namespace Common | ||
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h new file mode 100644 index 000000000..e6181d521 --- /dev/null +++ b/src/common/atomic_ops.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected); | ||
| 12 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); | ||
| 13 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); | ||
| 14 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); | ||
| 15 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected); | ||
| 16 | |||
| 17 | } // namespace Common | ||
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp new file mode 100644 index 000000000..1c1d09ccb --- /dev/null +++ b/src/common/fiber.cpp | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/fiber.h" | ||
| 7 | #if defined(_WIN32) || defined(WIN32) | ||
| 8 | #include <windows.h> | ||
| 9 | #else | ||
| 10 | #include <boost/context/detail/fcontext.hpp> | ||
| 11 | #endif | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | constexpr std::size_t default_stack_size = 256 * 1024; // 256kb | ||
| 16 | |||
| 17 | #if defined(_WIN32) || defined(WIN32) | ||
| 18 | |||
| 19 | struct Fiber::FiberImpl { | ||
| 20 | LPVOID handle = nullptr; | ||
| 21 | LPVOID rewind_handle = nullptr; | ||
| 22 | }; | ||
| 23 | |||
| 24 | void Fiber::Start() { | ||
| 25 | ASSERT(previous_fiber != nullptr); | ||
| 26 | previous_fiber->guard.unlock(); | ||
| 27 | previous_fiber.reset(); | ||
| 28 | entry_point(start_parameter); | ||
| 29 | UNREACHABLE(); | ||
| 30 | } | ||
| 31 | |||
| 32 | void Fiber::OnRewind() { | ||
| 33 | ASSERT(impl->handle != nullptr); | ||
| 34 | DeleteFiber(impl->handle); | ||
| 35 | impl->handle = impl->rewind_handle; | ||
| 36 | impl->rewind_handle = nullptr; | ||
| 37 | rewind_point(rewind_parameter); | ||
| 38 | UNREACHABLE(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void Fiber::FiberStartFunc(void* fiber_parameter) { | ||
| 42 | auto fiber = static_cast<Fiber*>(fiber_parameter); | ||
| 43 | fiber->Start(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Fiber::RewindStartFunc(void* fiber_parameter) { | ||
| 47 | auto fiber = static_cast<Fiber*>(fiber_parameter); | ||
| 48 | fiber->OnRewind(); | ||
| 49 | } | ||
| 50 | |||
| 51 | Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) | ||
| 52 | : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { | ||
| 53 | impl = std::make_unique<FiberImpl>(); | ||
| 54 | impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); | ||
| 55 | } | ||
| 56 | |||
| 57 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} | ||
| 58 | |||
| 59 | Fiber::~Fiber() { | ||
| 60 | if (released) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | // Make sure the Fiber is not being used | ||
| 64 | const bool locked = guard.try_lock(); | ||
| 65 | ASSERT_MSG(locked, "Destroying a fiber that's still running"); | ||
| 66 | if (locked) { | ||
| 67 | guard.unlock(); | ||
| 68 | } | ||
| 69 | DeleteFiber(impl->handle); | ||
| 70 | } | ||
| 71 | |||
| 72 | void Fiber::Exit() { | ||
| 73 | ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); | ||
| 74 | if (!is_thread_fiber) { | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | ConvertFiberToThread(); | ||
| 78 | guard.unlock(); | ||
| 79 | released = true; | ||
| 80 | } | ||
| 81 | |||
| 82 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { | ||
| 83 | rewind_point = std::move(rewind_func); | ||
| 84 | rewind_parameter = start_parameter; | ||
| 85 | } | ||
| 86 | |||
| 87 | void Fiber::Rewind() { | ||
| 88 | ASSERT(rewind_point); | ||
| 89 | ASSERT(impl->rewind_handle == nullptr); | ||
| 90 | impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this); | ||
| 91 | SwitchToFiber(impl->rewind_handle); | ||
| 92 | } | ||
| 93 | |||
| 94 | void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { | ||
| 95 | ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); | ||
| 96 | ASSERT_MSG(to != nullptr, "Next fiber is null!"); | ||
| 97 | to->guard.lock(); | ||
| 98 | to->previous_fiber = from; | ||
| 99 | SwitchToFiber(to->impl->handle); | ||
| 100 | ASSERT(from->previous_fiber != nullptr); | ||
| 101 | from->previous_fiber->guard.unlock(); | ||
| 102 | from->previous_fiber.reset(); | ||
| 103 | } | ||
| 104 | |||
| 105 | std::shared_ptr<Fiber> Fiber::ThreadToFiber() { | ||
| 106 | std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; | ||
| 107 | fiber->guard.lock(); | ||
| 108 | fiber->impl->handle = ConvertThreadToFiber(nullptr); | ||
| 109 | fiber->is_thread_fiber = true; | ||
| 110 | return fiber; | ||
| 111 | } | ||
| 112 | |||
| 113 | #else | ||
| 114 | |||
| 115 | struct Fiber::FiberImpl { | ||
| 116 | alignas(64) std::array<u8, default_stack_size> stack; | ||
| 117 | alignas(64) std::array<u8, default_stack_size> rewind_stack; | ||
| 118 | u8* stack_limit; | ||
| 119 | u8* rewind_stack_limit; | ||
| 120 | boost::context::detail::fcontext_t context; | ||
| 121 | boost::context::detail::fcontext_t rewind_context; | ||
| 122 | }; | ||
| 123 | |||
| 124 | void Fiber::Start(boost::context::detail::transfer_t& transfer) { | ||
| 125 | ASSERT(previous_fiber != nullptr); | ||
| 126 | previous_fiber->impl->context = transfer.fctx; | ||
| 127 | previous_fiber->guard.unlock(); | ||
| 128 | previous_fiber.reset(); | ||
| 129 | entry_point(start_parameter); | ||
| 130 | UNREACHABLE(); | ||
| 131 | } | ||
| 132 | |||
| 133 | void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) { | ||
| 134 | ASSERT(impl->context != nullptr); | ||
| 135 | impl->context = impl->rewind_context; | ||
| 136 | impl->rewind_context = nullptr; | ||
| 137 | u8* tmp = impl->stack_limit; | ||
| 138 | impl->stack_limit = impl->rewind_stack_limit; | ||
| 139 | impl->rewind_stack_limit = tmp; | ||
| 140 | rewind_point(rewind_parameter); | ||
| 141 | UNREACHABLE(); | ||
| 142 | } | ||
| 143 | |||
| 144 | void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { | ||
| 145 | auto fiber = static_cast<Fiber*>(transfer.data); | ||
| 146 | fiber->Start(transfer); | ||
| 147 | } | ||
| 148 | |||
| 149 | void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { | ||
| 150 | auto fiber = static_cast<Fiber*>(transfer.data); | ||
| 151 | fiber->OnRewind(transfer); | ||
| 152 | } | ||
| 153 | |||
| 154 | Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) | ||
| 155 | : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { | ||
| 156 | impl = std::make_unique<FiberImpl>(); | ||
| 157 | impl->stack_limit = impl->stack.data(); | ||
| 158 | impl->rewind_stack_limit = impl->rewind_stack.data(); | ||
| 159 | u8* stack_base = impl->stack_limit + default_stack_size; | ||
| 160 | impl->context = | ||
| 161 | boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); | ||
| 162 | } | ||
| 163 | |||
| 164 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { | ||
| 165 | rewind_point = std::move(rewind_func); | ||
| 166 | rewind_parameter = start_parameter; | ||
| 167 | } | ||
| 168 | |||
| 169 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} | ||
| 170 | |||
| 171 | Fiber::~Fiber() { | ||
| 172 | if (released) { | ||
| 173 | return; | ||
| 174 | } | ||
| 175 | // Make sure the Fiber is not being used | ||
| 176 | const bool locked = guard.try_lock(); | ||
| 177 | ASSERT_MSG(locked, "Destroying a fiber that's still running"); | ||
| 178 | if (locked) { | ||
| 179 | guard.unlock(); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | void Fiber::Exit() { | ||
| 184 | |||
| 185 | ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); | ||
| 186 | if (!is_thread_fiber) { | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | guard.unlock(); | ||
| 190 | released = true; | ||
| 191 | } | ||
| 192 | |||
| 193 | void Fiber::Rewind() { | ||
| 194 | ASSERT(rewind_point); | ||
| 195 | ASSERT(impl->rewind_context == nullptr); | ||
| 196 | u8* stack_base = impl->rewind_stack_limit + default_stack_size; | ||
| 197 | impl->rewind_context = | ||
| 198 | boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc); | ||
| 199 | boost::context::detail::jump_fcontext(impl->rewind_context, this); | ||
| 200 | } | ||
| 201 | |||
| 202 | void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { | ||
| 203 | ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); | ||
| 204 | ASSERT_MSG(to != nullptr, "Next fiber is null!"); | ||
| 205 | to->guard.lock(); | ||
| 206 | to->previous_fiber = from; | ||
| 207 | auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); | ||
| 208 | ASSERT(from->previous_fiber != nullptr); | ||
| 209 | from->previous_fiber->impl->context = transfer.fctx; | ||
| 210 | from->previous_fiber->guard.unlock(); | ||
| 211 | from->previous_fiber.reset(); | ||
| 212 | } | ||
| 213 | |||
| 214 | std::shared_ptr<Fiber> Fiber::ThreadToFiber() { | ||
| 215 | std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; | ||
| 216 | fiber->guard.lock(); | ||
| 217 | fiber->is_thread_fiber = true; | ||
| 218 | return fiber; | ||
| 219 | } | ||
| 220 | |||
| 221 | #endif | ||
| 222 | } // namespace Common | ||
diff --git a/src/common/fiber.h b/src/common/fiber.h new file mode 100644 index 000000000..dafc1100e --- /dev/null +++ b/src/common/fiber.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/spin_lock.h" | ||
| 12 | |||
| 13 | #if !defined(_WIN32) && !defined(WIN32) | ||
| 14 | namespace boost::context::detail { | ||
| 15 | struct transfer_t; | ||
| 16 | } | ||
| 17 | #endif | ||
| 18 | |||
| 19 | namespace Common { | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Fiber class | ||
| 23 | * a fiber is a userspace thread with it's own context. They can be used to | ||
| 24 | * implement coroutines, emulated threading systems and certain asynchronous | ||
| 25 | * patterns. | ||
| 26 | * | ||
| 27 | * This class implements fibers at a low level, thus allowing greater freedom | ||
| 28 | * to implement such patterns. This fiber class is 'threadsafe' only one fiber | ||
| 29 | * can be running at a time and threads will be locked while trying to yield to | ||
| 30 | * a running fiber until it yields. WARNING exchanging two running fibers between | ||
| 31 | * threads will cause a deadlock. In order to prevent a deadlock, each thread should | ||
| 32 | * have an intermediary fiber, you switch to the intermediary fiber of the current | ||
| 33 | * thread and then from it switch to the expected fiber. This way you can exchange | ||
| 34 | * 2 fibers within 2 different threads. | ||
| 35 | */ | ||
| 36 | class Fiber { | ||
| 37 | public: | ||
| 38 | Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); | ||
| 39 | ~Fiber(); | ||
| 40 | |||
| 41 | Fiber(const Fiber&) = delete; | ||
| 42 | Fiber& operator=(const Fiber&) = delete; | ||
| 43 | |||
| 44 | Fiber(Fiber&&) = default; | ||
| 45 | Fiber& operator=(Fiber&&) = default; | ||
| 46 | |||
| 47 | /// Yields control from Fiber 'from' to Fiber 'to' | ||
| 48 | /// Fiber 'from' must be the currently running fiber. | ||
| 49 | static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to); | ||
| 50 | static std::shared_ptr<Fiber> ThreadToFiber(); | ||
| 51 | |||
| 52 | void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter); | ||
| 53 | |||
| 54 | void Rewind(); | ||
| 55 | |||
| 56 | /// Only call from main thread's fiber | ||
| 57 | void Exit(); | ||
| 58 | |||
| 59 | /// Changes the start parameter of the fiber. Has no effect if the fiber already started | ||
| 60 | void SetStartParameter(void* new_parameter) { | ||
| 61 | start_parameter = new_parameter; | ||
| 62 | } | ||
| 63 | |||
| 64 | private: | ||
| 65 | Fiber(); | ||
| 66 | |||
| 67 | #if defined(_WIN32) || defined(WIN32) | ||
| 68 | void OnRewind(); | ||
| 69 | void Start(); | ||
| 70 | static void FiberStartFunc(void* fiber_parameter); | ||
| 71 | static void RewindStartFunc(void* fiber_parameter); | ||
| 72 | #else | ||
| 73 | void OnRewind(boost::context::detail::transfer_t& transfer); | ||
| 74 | void Start(boost::context::detail::transfer_t& transfer); | ||
| 75 | static void FiberStartFunc(boost::context::detail::transfer_t transfer); | ||
| 76 | static void RewindStartFunc(boost::context::detail::transfer_t transfer); | ||
| 77 | #endif | ||
| 78 | |||
| 79 | struct FiberImpl; | ||
| 80 | |||
| 81 | SpinLock guard{}; | ||
| 82 | std::function<void(void*)> entry_point; | ||
| 83 | std::function<void(void*)> rewind_point; | ||
| 84 | void* rewind_parameter{}; | ||
| 85 | void* start_parameter{}; | ||
| 86 | std::shared_ptr<Fiber> previous_fiber; | ||
| 87 | std::unique_ptr<FiberImpl> impl; | ||
| 88 | bool is_thread_fiber{}; | ||
| 89 | bool released{}; | ||
| 90 | }; | ||
| 91 | |||
| 92 | } // namespace Common | ||
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index 3fdc309a2..8cff6ec37 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp | |||
| @@ -9,10 +9,12 @@ | |||
| 9 | // clang-format on | 9 | // clang-format on |
| 10 | #else | 10 | #else |
| 11 | #include <sys/types.h> | 11 | #include <sys/types.h> |
| 12 | #ifdef __APPLE__ | 12 | #if defined(__APPLE__) || defined(__FreeBSD__) |
| 13 | #include <sys/sysctl.h> | 13 | #include <sys/sysctl.h> |
| 14 | #else | 14 | #elif defined(__linux__) |
| 15 | #include <sys/sysinfo.h> | 15 | #include <sys/sysinfo.h> |
| 16 | #else | ||
| 17 | #include <unistd.h> | ||
| 16 | #endif | 18 | #endif |
| 17 | #endif | 19 | #endif |
| 18 | 20 | ||
| @@ -38,15 +40,26 @@ static MemoryInfo Detect() { | |||
| 38 | // hw and vm are defined in sysctl.h | 40 | // hw and vm are defined in sysctl.h |
| 39 | // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 | 41 | // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 |
| 40 | // sysctlbyname(const char *, void *, size_t *, void *, size_t); | 42 | // sysctlbyname(const char *, void *, size_t *, void *, size_t); |
| 41 | sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0); | 43 | sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0); |
| 42 | sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); | 44 | sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0); |
| 43 | mem_info.TotalPhysicalMemory = ramsize; | 45 | mem_info.TotalPhysicalMemory = ramsize; |
| 44 | mem_info.TotalSwapMemory = vmusage.xsu_total; | 46 | mem_info.TotalSwapMemory = vmusage.xsu_total; |
| 45 | #else | 47 | #elif defined(__FreeBSD__) |
| 48 | u_long physmem, swap_total; | ||
| 49 | std::size_t sizeof_u_long = sizeof(u_long); | ||
| 50 | // sysctlbyname(const char *, void *, size_t *, const void *, size_t); | ||
| 51 | sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0); | ||
| 52 | sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0); | ||
| 53 | mem_info.TotalPhysicalMemory = physmem; | ||
| 54 | mem_info.TotalSwapMemory = swap_total; | ||
| 55 | #elif defined(__linux__) | ||
| 46 | struct sysinfo meminfo; | 56 | struct sysinfo meminfo; |
| 47 | sysinfo(&meminfo); | 57 | sysinfo(&meminfo); |
| 48 | mem_info.TotalPhysicalMemory = meminfo.totalram; | 58 | mem_info.TotalPhysicalMemory = meminfo.totalram; |
| 49 | mem_info.TotalSwapMemory = meminfo.totalswap; | 59 | mem_info.TotalSwapMemory = meminfo.totalswap; |
| 60 | #else | ||
| 61 | mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); | ||
| 62 | mem_info.TotalSwapMemory = 0; | ||
| 50 | #endif | 63 | #endif |
| 51 | 64 | ||
| 52 | return mem_info; | 65 | return mem_info; |
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100644 index 000000000..c1524220f --- /dev/null +++ b/src/common/spin_lock.cpp | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/spin_lock.h" | ||
| 6 | |||
| 7 | #if _MSC_VER | ||
| 8 | #include <intrin.h> | ||
| 9 | #if _M_AMD64 | ||
| 10 | #define __x86_64__ 1 | ||
| 11 | #endif | ||
| 12 | #if _M_ARM64 | ||
| 13 | #define __aarch64__ 1 | ||
| 14 | #endif | ||
| 15 | #else | ||
| 16 | #if __x86_64__ | ||
| 17 | #include <xmmintrin.h> | ||
| 18 | #endif | ||
| 19 | #endif | ||
| 20 | |||
| 21 | namespace { | ||
| 22 | |||
| 23 | void ThreadPause() { | ||
| 24 | #if __x86_64__ | ||
| 25 | _mm_pause(); | ||
| 26 | #elif __aarch64__ && _MSC_VER | ||
| 27 | __yield(); | ||
| 28 | #elif __aarch64__ | ||
| 29 | asm("yield"); | ||
| 30 | #endif | ||
| 31 | } | ||
| 32 | |||
| 33 | } // Anonymous namespace | ||
| 34 | |||
| 35 | namespace Common { | ||
| 36 | |||
| 37 | void SpinLock::lock() { | ||
| 38 | while (lck.test_and_set(std::memory_order_acquire)) { | ||
| 39 | ThreadPause(); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | void SpinLock::unlock() { | ||
| 44 | lck.clear(std::memory_order_release); | ||
| 45 | } | ||
| 46 | |||
| 47 | bool SpinLock::try_lock() { | ||
| 48 | if (lck.test_and_set(std::memory_order_acquire)) { | ||
| 49 | return false; | ||
| 50 | } | ||
| 51 | return true; | ||
| 52 | } | ||
| 53 | |||
| 54 | } // namespace Common | ||
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100644 index 000000000..1df5528c4 --- /dev/null +++ b/src/common/spin_lock.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | /** | ||
| 12 | * SpinLock class | ||
| 13 | * a lock similar to mutex that forces a thread to spin wait instead calling the | ||
| 14 | * supervisor. Should be used on short sequences of code. | ||
| 15 | */ | ||
| 16 | class SpinLock { | ||
| 17 | public: | ||
| 18 | void lock(); | ||
| 19 | void unlock(); | ||
| 20 | bool try_lock(); | ||
| 21 | |||
| 22 | private: | ||
| 23 | std::atomic_flag lck = ATOMIC_FLAG_INIT; | ||
| 24 | }; | ||
| 25 | |||
| 26 | } // namespace Common | ||
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index 200c6489a..16d42facd 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp | |||
| @@ -60,6 +60,7 @@ void AppendCPUInfo(FieldCollection& fc) { | |||
| 60 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); | 60 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); |
| 61 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); | 61 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); |
| 62 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); | 62 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); |
| 63 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); | ||
| 63 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); | 64 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); |
| 64 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); | 65 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); |
| 65 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); | 66 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 0cd2d10bf..8e5935e6a 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -25,6 +25,52 @@ | |||
| 25 | 25 | ||
| 26 | namespace Common { | 26 | namespace Common { |
| 27 | 27 | ||
| 28 | #ifdef _WIN32 | ||
| 29 | |||
| 30 | void SetCurrentThreadPriority(ThreadPriority new_priority) { | ||
| 31 | auto handle = GetCurrentThread(); | ||
| 32 | int windows_priority = 0; | ||
| 33 | switch (new_priority) { | ||
| 34 | case ThreadPriority::Low: | ||
| 35 | windows_priority = THREAD_PRIORITY_BELOW_NORMAL; | ||
| 36 | break; | ||
| 37 | case ThreadPriority::Normal: | ||
| 38 | windows_priority = THREAD_PRIORITY_NORMAL; | ||
| 39 | break; | ||
| 40 | case ThreadPriority::High: | ||
| 41 | windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; | ||
| 42 | break; | ||
| 43 | case ThreadPriority::VeryHigh: | ||
| 44 | windows_priority = THREAD_PRIORITY_HIGHEST; | ||
| 45 | break; | ||
| 46 | default: | ||
| 47 | windows_priority = THREAD_PRIORITY_NORMAL; | ||
| 48 | break; | ||
| 49 | } | ||
| 50 | SetThreadPriority(handle, windows_priority); | ||
| 51 | } | ||
| 52 | |||
| 53 | #else | ||
| 54 | |||
| 55 | void SetCurrentThreadPriority(ThreadPriority new_priority) { | ||
| 56 | pthread_t this_thread = pthread_self(); | ||
| 57 | |||
| 58 | s32 max_prio = sched_get_priority_max(SCHED_OTHER); | ||
| 59 | s32 min_prio = sched_get_priority_min(SCHED_OTHER); | ||
| 60 | u32 level = static_cast<u32>(new_priority) + 1; | ||
| 61 | |||
| 62 | struct sched_param params; | ||
| 63 | if (max_prio > min_prio) { | ||
| 64 | params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4; | ||
| 65 | } else { | ||
| 66 | params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; | ||
| 67 | } | ||
| 68 | |||
| 69 | pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); | ||
| 70 | } | ||
| 71 | |||
| 72 | #endif | ||
| 73 | |||
| 28 | #ifdef _MSC_VER | 74 | #ifdef _MSC_VER |
| 29 | 75 | ||
| 30 | // Sets the debugger-visible name of the current thread. | 76 | // Sets the debugger-visible name of the current thread. |
| @@ -70,6 +116,12 @@ void SetCurrentThreadName(const char* name) { | |||
| 70 | } | 116 | } |
| 71 | #endif | 117 | #endif |
| 72 | 118 | ||
| 119 | #if defined(_WIN32) | ||
| 120 | void SetCurrentThreadName(const char* name) { | ||
| 121 | // Do Nothing on MingW | ||
| 122 | } | ||
| 123 | #endif | ||
| 124 | |||
| 73 | #endif | 125 | #endif |
| 74 | 126 | ||
| 75 | } // namespace Common | 127 | } // namespace Common |
diff --git a/src/common/thread.h b/src/common/thread.h index 2fc071685..52b359413 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <cstddef> | 9 | #include <cstddef> |
| 10 | #include <mutex> | 10 | #include <mutex> |
| 11 | #include <thread> | 11 | #include <thread> |
| 12 | #include "common/common_types.h" | ||
| 12 | 13 | ||
| 13 | namespace Common { | 14 | namespace Common { |
| 14 | 15 | ||
| @@ -28,8 +29,7 @@ public: | |||
| 28 | is_set = false; | 29 | is_set = false; |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | template <class Duration> | 32 | bool WaitFor(const std::chrono::nanoseconds& time) { |
| 32 | bool WaitFor(const std::chrono::duration<Duration>& time) { | ||
| 33 | std::unique_lock lk{mutex}; | 33 | std::unique_lock lk{mutex}; |
| 34 | if (!condvar.wait_for(lk, time, [this] { return is_set; })) | 34 | if (!condvar.wait_for(lk, time, [this] { return is_set; })) |
| 35 | return false; | 35 | return false; |
| @@ -86,6 +86,15 @@ private: | |||
| 86 | std::size_t generation = 0; // Incremented once each time the barrier is used | 86 | std::size_t generation = 0; // Incremented once each time the barrier is used |
| 87 | }; | 87 | }; |
| 88 | 88 | ||
| 89 | enum class ThreadPriority : u32 { | ||
| 90 | Low = 0, | ||
| 91 | Normal = 1, | ||
| 92 | High = 2, | ||
| 93 | VeryHigh = 3, | ||
| 94 | }; | ||
| 95 | |||
| 96 | void SetCurrentThreadPriority(ThreadPriority new_priority); | ||
| 97 | |||
| 89 | void SetCurrentThreadName(const char* name); | 98 | void SetCurrentThreadName(const char* name); |
| 90 | 99 | ||
| 91 | } // namespace Common | 100 | } // namespace Common |
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp index 32bf56730..16bf7c828 100644 --- a/src/common/uint128.cpp +++ b/src/common/uint128.cpp | |||
| @@ -6,12 +6,38 @@ | |||
| 6 | #include <intrin.h> | 6 | #include <intrin.h> |
| 7 | 7 | ||
| 8 | #pragma intrinsic(_umul128) | 8 | #pragma intrinsic(_umul128) |
| 9 | #pragma intrinsic(_udiv128) | ||
| 9 | #endif | 10 | #endif |
| 10 | #include <cstring> | 11 | #include <cstring> |
| 11 | #include "common/uint128.h" | 12 | #include "common/uint128.h" |
| 12 | 13 | ||
| 13 | namespace Common { | 14 | namespace Common { |
| 14 | 15 | ||
| 16 | #ifdef _MSC_VER | ||
| 17 | |||
| 18 | u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { | ||
| 19 | u128 r{}; | ||
| 20 | r[0] = _umul128(a, b, &r[1]); | ||
| 21 | u64 remainder; | ||
| 22 | #if _MSC_VER < 1923 | ||
| 23 | return udiv128(r[1], r[0], d, &remainder); | ||
| 24 | #else | ||
| 25 | return _udiv128(r[1], r[0], d, &remainder); | ||
| 26 | #endif | ||
| 27 | } | ||
| 28 | |||
| 29 | #else | ||
| 30 | |||
| 31 | u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { | ||
| 32 | const u64 diva = a / d; | ||
| 33 | const u64 moda = a % d; | ||
| 34 | const u64 divb = b / d; | ||
| 35 | const u64 modb = b % d; | ||
| 36 | return diva * b + moda * divb + moda * modb / d; | ||
| 37 | } | ||
| 38 | |||
| 39 | #endif | ||
| 40 | |||
| 15 | u128 Multiply64Into128(u64 a, u64 b) { | 41 | u128 Multiply64Into128(u64 a, u64 b) { |
| 16 | u128 result; | 42 | u128 result; |
| 17 | #ifdef _MSC_VER | 43 | #ifdef _MSC_VER |
diff --git a/src/common/uint128.h b/src/common/uint128.h index a3be2a2cb..503cd2d0c 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h | |||
| @@ -9,6 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | namespace Common { | 10 | namespace Common { |
| 11 | 11 | ||
| 12 | // This function multiplies 2 u64 values and divides it by a u64 value. | ||
| 13 | u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); | ||
| 14 | |||
| 12 | // This function multiplies 2 u64 values and produces a u128 value; | 15 | // This function multiplies 2 u64 values and produces a u128 value; |
| 13 | u128 Multiply64Into128(u64 a, u64 b); | 16 | u128 Multiply64Into128(u64 a, u64 b); |
| 14 | 17 | ||
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp new file mode 100644 index 000000000..3afbdb898 --- /dev/null +++ b/src/common/wall_clock.cpp | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/uint128.h" | ||
| 6 | #include "common/wall_clock.h" | ||
| 7 | |||
| 8 | #ifdef ARCHITECTURE_x86_64 | ||
| 9 | #include "common/x64/cpu_detect.h" | ||
| 10 | #include "common/x64/native_clock.h" | ||
| 11 | #endif | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | using base_timer = std::chrono::steady_clock; | ||
| 16 | using base_time_point = std::chrono::time_point<base_timer>; | ||
| 17 | |||
| 18 | class StandardWallClock : public WallClock { | ||
| 19 | public: | ||
| 20 | StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency) | ||
| 21 | : WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) { | ||
| 22 | start_time = base_timer::now(); | ||
| 23 | } | ||
| 24 | |||
| 25 | std::chrono::nanoseconds GetTimeNS() override { | ||
| 26 | base_time_point current = base_timer::now(); | ||
| 27 | auto elapsed = current - start_time; | ||
| 28 | return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed); | ||
| 29 | } | ||
| 30 | |||
| 31 | std::chrono::microseconds GetTimeUS() override { | ||
| 32 | base_time_point current = base_timer::now(); | ||
| 33 | auto elapsed = current - start_time; | ||
| 34 | return std::chrono::duration_cast<std::chrono::microseconds>(elapsed); | ||
| 35 | } | ||
| 36 | |||
| 37 | std::chrono::milliseconds GetTimeMS() override { | ||
| 38 | base_time_point current = base_timer::now(); | ||
| 39 | auto elapsed = current - start_time; | ||
| 40 | return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); | ||
| 41 | } | ||
| 42 | |||
| 43 | u64 GetClockCycles() override { | ||
| 44 | std::chrono::nanoseconds time_now = GetTimeNS(); | ||
| 45 | const u128 temporary = | ||
| 46 | Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); | ||
| 47 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 48 | } | ||
| 49 | |||
| 50 | u64 GetCPUCycles() override { | ||
| 51 | std::chrono::nanoseconds time_now = GetTimeNS(); | ||
| 52 | const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); | ||
| 53 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 54 | } | ||
| 55 | |||
| 56 | void Pause(bool is_paused) override { | ||
| 57 | // Do nothing in this clock type. | ||
| 58 | } | ||
| 59 | |||
| 60 | private: | ||
| 61 | base_time_point start_time; | ||
| 62 | }; | ||
| 63 | |||
| 64 | #ifdef ARCHITECTURE_x86_64 | ||
| 65 | |||
| 66 | std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, | ||
| 67 | u32 emulated_clock_frequency) { | ||
| 68 | const auto& caps = GetCPUCaps(); | ||
| 69 | u64 rtsc_frequency = 0; | ||
| 70 | if (caps.invariant_tsc) { | ||
| 71 | rtsc_frequency = EstimateRDTSCFrequency(); | ||
| 72 | } | ||
| 73 | if (rtsc_frequency == 0) { | ||
| 74 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, | ||
| 75 | emulated_clock_frequency); | ||
| 76 | } else { | ||
| 77 | return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, | ||
| 78 | rtsc_frequency); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | #else | ||
| 83 | |||
| 84 | std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, | ||
| 85 | u32 emulated_clock_frequency) { | ||
| 86 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); | ||
| 87 | } | ||
| 88 | |||
| 89 | #endif | ||
| 90 | |||
| 91 | } // namespace Common | ||
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h new file mode 100644 index 000000000..367d72134 --- /dev/null +++ b/src/common/wall_clock.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <chrono> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | class WallClock { | ||
| 15 | public: | ||
| 16 | /// Returns current wall time in nanoseconds | ||
| 17 | virtual std::chrono::nanoseconds GetTimeNS() = 0; | ||
| 18 | |||
| 19 | /// Returns current wall time in microseconds | ||
| 20 | virtual std::chrono::microseconds GetTimeUS() = 0; | ||
| 21 | |||
| 22 | /// Returns current wall time in milliseconds | ||
| 23 | virtual std::chrono::milliseconds GetTimeMS() = 0; | ||
| 24 | |||
| 25 | /// Returns current wall time in emulated clock cycles | ||
| 26 | virtual u64 GetClockCycles() = 0; | ||
| 27 | |||
| 28 | /// Returns current wall time in emulated cpu cycles | ||
| 29 | virtual u64 GetCPUCycles() = 0; | ||
| 30 | |||
| 31 | virtual void Pause(bool is_paused) = 0; | ||
| 32 | |||
| 33 | /// Tells if the wall clock, uses the host CPU's hardware clock | ||
| 34 | bool IsNative() const { | ||
| 35 | return is_native; | ||
| 36 | } | ||
| 37 | |||
| 38 | protected: | ||
| 39 | WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native) | ||
| 40 | : emulated_cpu_frequency{emulated_cpu_frequency}, | ||
| 41 | emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {} | ||
| 42 | |||
| 43 | u64 emulated_cpu_frequency; | ||
| 44 | u64 emulated_clock_frequency; | ||
| 45 | |||
| 46 | private: | ||
| 47 | bool is_native; | ||
| 48 | }; | ||
| 49 | |||
| 50 | std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, | ||
| 51 | u32 emulated_clock_frequency); | ||
| 52 | |||
| 53 | } // namespace Common | ||
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index c9349a6b4..fccd2eee5 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -62,6 +62,17 @@ static CPUCaps Detect() { | |||
| 62 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); | 62 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); |
| 63 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); | 63 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); |
| 64 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); | 64 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); |
| 65 | if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69) | ||
| 66 | caps.manufacturer = Manufacturer::Intel; | ||
| 67 | else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65) | ||
| 68 | caps.manufacturer = Manufacturer::AMD; | ||
| 69 | else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e) | ||
| 70 | caps.manufacturer = Manufacturer::Hygon; | ||
| 71 | else | ||
| 72 | caps.manufacturer = Manufacturer::Unknown; | ||
| 73 | |||
| 74 | u32 family = {}; | ||
| 75 | u32 model = {}; | ||
| 65 | 76 | ||
| 66 | __cpuid(cpu_id, 0x80000000); | 77 | __cpuid(cpu_id, 0x80000000); |
| 67 | 78 | ||
| @@ -73,6 +84,14 @@ static CPUCaps Detect() { | |||
| 73 | // Detect family and other miscellaneous features | 84 | // Detect family and other miscellaneous features |
| 74 | if (max_std_fn >= 1) { | 85 | if (max_std_fn >= 1) { |
| 75 | __cpuid(cpu_id, 0x00000001); | 86 | __cpuid(cpu_id, 0x00000001); |
| 87 | family = (cpu_id[0] >> 8) & 0xf; | ||
| 88 | model = (cpu_id[0] >> 4) & 0xf; | ||
| 89 | if (family == 0xf) { | ||
| 90 | family += (cpu_id[0] >> 20) & 0xff; | ||
| 91 | } | ||
| 92 | if (family >= 6) { | ||
| 93 | model += ((cpu_id[0] >> 16) & 0xf) << 4; | ||
| 94 | } | ||
| 76 | 95 | ||
| 77 | if ((cpu_id[3] >> 25) & 1) | 96 | if ((cpu_id[3] >> 25) & 1) |
| 78 | caps.sse = true; | 97 | caps.sse = true; |
| @@ -110,6 +129,11 @@ static CPUCaps Detect() { | |||
| 110 | caps.bmi1 = true; | 129 | caps.bmi1 = true; |
| 111 | if ((cpu_id[1] >> 8) & 1) | 130 | if ((cpu_id[1] >> 8) & 1) |
| 112 | caps.bmi2 = true; | 131 | caps.bmi2 = true; |
| 132 | // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) | ||
| 133 | if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && | ||
| 134 | (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { | ||
| 135 | caps.avx512 = caps.avx2; | ||
| 136 | } | ||
| 113 | } | 137 | } |
| 114 | } | 138 | } |
| 115 | 139 | ||
| @@ -130,6 +154,20 @@ static CPUCaps Detect() { | |||
| 130 | caps.fma4 = true; | 154 | caps.fma4 = true; |
| 131 | } | 155 | } |
| 132 | 156 | ||
| 157 | if (max_ex_fn >= 0x80000007) { | ||
| 158 | __cpuid(cpu_id, 0x80000007); | ||
| 159 | if (cpu_id[3] & (1 << 8)) { | ||
| 160 | caps.invariant_tsc = true; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | if (max_std_fn >= 0x16) { | ||
| 165 | __cpuid(cpu_id, 0x16); | ||
| 166 | caps.base_frequency = cpu_id[0]; | ||
| 167 | caps.max_frequency = cpu_id[1]; | ||
| 168 | caps.bus_frequency = cpu_id[2]; | ||
| 169 | } | ||
| 170 | |||
| 133 | return caps; | 171 | return caps; |
| 134 | } | 172 | } |
| 135 | 173 | ||
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 20f2ba234..e3b63302e 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h | |||
| @@ -6,8 +6,16 @@ | |||
| 6 | 6 | ||
| 7 | namespace Common { | 7 | namespace Common { |
| 8 | 8 | ||
| 9 | enum class Manufacturer : u32 { | ||
| 10 | Intel = 0, | ||
| 11 | AMD = 1, | ||
| 12 | Hygon = 2, | ||
| 13 | Unknown = 3, | ||
| 14 | }; | ||
| 15 | |||
| 9 | /// x86/x64 CPU capabilities that may be detected by this module | 16 | /// x86/x64 CPU capabilities that may be detected by this module |
| 10 | struct CPUCaps { | 17 | struct CPUCaps { |
| 18 | Manufacturer manufacturer; | ||
| 11 | char cpu_string[0x21]; | 19 | char cpu_string[0x21]; |
| 12 | char brand_string[0x41]; | 20 | char brand_string[0x41]; |
| 13 | bool sse; | 21 | bool sse; |
| @@ -19,11 +27,16 @@ struct CPUCaps { | |||
| 19 | bool lzcnt; | 27 | bool lzcnt; |
| 20 | bool avx; | 28 | bool avx; |
| 21 | bool avx2; | 29 | bool avx2; |
| 30 | bool avx512; | ||
| 22 | bool bmi1; | 31 | bool bmi1; |
| 23 | bool bmi2; | 32 | bool bmi2; |
| 24 | bool fma; | 33 | bool fma; |
| 25 | bool fma4; | 34 | bool fma4; |
| 26 | bool aes; | 35 | bool aes; |
| 36 | bool invariant_tsc; | ||
| 37 | u32 base_frequency; | ||
| 38 | u32 max_frequency; | ||
| 39 | u32 bus_frequency; | ||
| 27 | }; | 40 | }; |
| 28 | 41 | ||
| 29 | /** | 42 | /** |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp new file mode 100644 index 000000000..424b39b1f --- /dev/null +++ b/src/common/x64/native_clock.cpp | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <chrono> | ||
| 6 | #include <mutex> | ||
| 7 | #include <thread> | ||
| 8 | |||
| 9 | #ifdef _MSC_VER | ||
| 10 | #include <intrin.h> | ||
| 11 | #else | ||
| 12 | #include <x86intrin.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #include "common/uint128.h" | ||
| 16 | #include "common/x64/native_clock.h" | ||
| 17 | |||
| 18 | namespace Common { | ||
| 19 | |||
| 20 | u64 EstimateRDTSCFrequency() { | ||
| 21 | const auto milli_10 = std::chrono::milliseconds{10}; | ||
| 22 | // get current time | ||
| 23 | _mm_mfence(); | ||
| 24 | const u64 tscStart = __rdtsc(); | ||
| 25 | const auto startTime = std::chrono::high_resolution_clock::now(); | ||
| 26 | // wait roughly 3 seconds | ||
| 27 | while (true) { | ||
| 28 | auto milli = std::chrono::duration_cast<std::chrono::milliseconds>( | ||
| 29 | std::chrono::high_resolution_clock::now() - startTime); | ||
| 30 | if (milli.count() >= 3000) | ||
| 31 | break; | ||
| 32 | std::this_thread::sleep_for(milli_10); | ||
| 33 | } | ||
| 34 | const auto endTime = std::chrono::high_resolution_clock::now(); | ||
| 35 | _mm_mfence(); | ||
| 36 | const u64 tscEnd = __rdtsc(); | ||
| 37 | // calculate difference | ||
| 38 | const u64 timer_diff = | ||
| 39 | std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count(); | ||
| 40 | const u64 tsc_diff = tscEnd - tscStart; | ||
| 41 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 42 | return tsc_freq; | ||
| 43 | } | ||
| 44 | |||
| 45 | namespace X64 { | ||
| 46 | NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, | ||
| 47 | u64 rtsc_frequency) | ||
| 48 | : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{ | ||
| 49 | rtsc_frequency} { | ||
| 50 | _mm_mfence(); | ||
| 51 | last_measure = __rdtsc(); | ||
| 52 | accumulated_ticks = 0U; | ||
| 53 | } | ||
| 54 | |||
| 55 | u64 NativeClock::GetRTSC() { | ||
| 56 | std::scoped_lock scope{rtsc_serialize}; | ||
| 57 | _mm_mfence(); | ||
| 58 | const u64 current_measure = __rdtsc(); | ||
| 59 | u64 diff = current_measure - last_measure; | ||
| 60 | diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) | ||
| 61 | if (current_measure > last_measure) { | ||
| 62 | last_measure = current_measure; | ||
| 63 | } | ||
| 64 | accumulated_ticks += diff; | ||
| 65 | /// The clock cannot be more precise than the guest timer, remove the lower bits | ||
| 66 | return accumulated_ticks & inaccuracy_mask; | ||
| 67 | } | ||
| 68 | |||
| 69 | void NativeClock::Pause(bool is_paused) { | ||
| 70 | if (!is_paused) { | ||
| 71 | _mm_mfence(); | ||
| 72 | last_measure = __rdtsc(); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | std::chrono::nanoseconds NativeClock::GetTimeNS() { | ||
| 77 | const u64 rtsc_value = GetRTSC(); | ||
| 78 | return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; | ||
| 79 | } | ||
| 80 | |||
| 81 | std::chrono::microseconds NativeClock::GetTimeUS() { | ||
| 82 | const u64 rtsc_value = GetRTSC(); | ||
| 83 | return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)}; | ||
| 84 | } | ||
| 85 | |||
| 86 | std::chrono::milliseconds NativeClock::GetTimeMS() { | ||
| 87 | const u64 rtsc_value = GetRTSC(); | ||
| 88 | return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)}; | ||
| 89 | } | ||
| 90 | |||
| 91 | u64 NativeClock::GetClockCycles() { | ||
| 92 | const u64 rtsc_value = GetRTSC(); | ||
| 93 | return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency); | ||
| 94 | } | ||
| 95 | |||
| 96 | u64 NativeClock::GetCPUCycles() { | ||
| 97 | const u64 rtsc_value = GetRTSC(); | ||
| 98 | return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); | ||
| 99 | } | ||
| 100 | |||
| 101 | } // namespace X64 | ||
| 102 | |||
| 103 | } // namespace Common | ||
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h new file mode 100644 index 000000000..891a3bbfd --- /dev/null +++ b/src/common/x64/native_clock.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <optional> | ||
| 8 | |||
| 9 | #include "common/spin_lock.h" | ||
| 10 | #include "common/wall_clock.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | namespace X64 { | ||
| 15 | class NativeClock : public WallClock { | ||
| 16 | public: | ||
| 17 | NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency); | ||
| 18 | |||
| 19 | std::chrono::nanoseconds GetTimeNS() override; | ||
| 20 | |||
| 21 | std::chrono::microseconds GetTimeUS() override; | ||
| 22 | |||
| 23 | std::chrono::milliseconds GetTimeMS() override; | ||
| 24 | |||
| 25 | u64 GetClockCycles() override; | ||
| 26 | |||
| 27 | u64 GetCPUCycles() override; | ||
| 28 | |||
| 29 | void Pause(bool is_paused) override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | u64 GetRTSC(); | ||
| 33 | |||
| 34 | /// value used to reduce the native clocks accuracy as some apss rely on | ||
| 35 | /// undefined behavior where the level of accuracy in the clock shouldn't | ||
| 36 | /// be higher. | ||
| 37 | static constexpr u64 inaccuracy_mask = ~(0x400 - 1); | ||
| 38 | |||
| 39 | SpinLock rtsc_serialize{}; | ||
| 40 | u64 last_measure{}; | ||
| 41 | u64 accumulated_ticks{}; | ||
| 42 | u64 rtsc_frequency; | ||
| 43 | }; | ||
| 44 | } // namespace X64 | ||
| 45 | |||
| 46 | u64 EstimateRDTSCFrequency(); | ||
| 47 | |||
| 48 | } // namespace Common | ||
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h index 794da8a52..a5f5d4fc1 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak_abi.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | namespace Common::X64 { | 12 | namespace Common::X64 { |
| 13 | 13 | ||
| 14 | inline int RegToIndex(const Xbyak::Reg& reg) { | 14 | inline std::size_t RegToIndex(const Xbyak::Reg& reg) { |
| 15 | using Kind = Xbyak::Reg::Kind; | 15 | using Kind = Xbyak::Reg::Kind; |
| 16 | ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0, | 16 | ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0, |
| 17 | "RegSet only support GPRs and XMM registers."); | 17 | "RegSet only support GPRs and XMM registers."); |
| @@ -19,17 +19,17 @@ inline int RegToIndex(const Xbyak::Reg& reg) { | |||
| 19 | return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16); | 19 | return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | inline Xbyak::Reg64 IndexToReg64(int reg_index) { | 22 | inline Xbyak::Reg64 IndexToReg64(std::size_t reg_index) { |
| 23 | ASSERT(reg_index < 16); | 23 | ASSERT(reg_index < 16); |
| 24 | return Xbyak::Reg64(reg_index); | 24 | return Xbyak::Reg64(static_cast<int>(reg_index)); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | inline Xbyak::Xmm IndexToXmm(int reg_index) { | 27 | inline Xbyak::Xmm IndexToXmm(std::size_t reg_index) { |
| 28 | ASSERT(reg_index >= 16 && reg_index < 32); | 28 | ASSERT(reg_index >= 16 && reg_index < 32); |
| 29 | return Xbyak::Xmm(reg_index - 16); | 29 | return Xbyak::Xmm(static_cast<int>(reg_index - 16)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | inline Xbyak::Reg IndexToReg(int reg_index) { | 32 | inline Xbyak::Reg IndexToReg(std::size_t reg_index) { |
| 33 | if (reg_index < 16) { | 33 | if (reg_index < 16) { |
| 34 | return IndexToReg64(reg_index); | 34 | return IndexToReg64(reg_index); |
| 35 | } else { | 35 | } else { |
| @@ -151,9 +151,13 @@ constexpr size_t ABI_SHADOW_SPACE = 0; | |||
| 151 | 151 | ||
| 152 | #endif | 152 | #endif |
| 153 | 153 | ||
| 154 | inline void ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment, | 154 | struct ABIFrameInfo { |
| 155 | size_t needed_frame_size, s32* out_subtraction, | 155 | s32 subtraction; |
| 156 | s32* out_xmm_offset) { | 156 | s32 xmm_offset; |
| 157 | }; | ||
| 158 | |||
| 159 | inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment, | ||
| 160 | size_t needed_frame_size) { | ||
| 157 | const auto count = (regs & ABI_ALL_GPRS).count(); | 161 | const auto count = (regs & ABI_ALL_GPRS).count(); |
| 158 | rsp_alignment -= count * 8; | 162 | rsp_alignment -= count * 8; |
| 159 | size_t subtraction = 0; | 163 | size_t subtraction = 0; |
| @@ -170,33 +174,28 @@ inline void ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment, | |||
| 170 | rsp_alignment -= subtraction; | 174 | rsp_alignment -= subtraction; |
| 171 | subtraction += rsp_alignment & 0xF; | 175 | subtraction += rsp_alignment & 0xF; |
| 172 | 176 | ||
| 173 | *out_subtraction = (s32)subtraction; | 177 | return ABIFrameInfo{static_cast<s32>(subtraction), |
| 174 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); | 178 | static_cast<s32>(subtraction - xmm_base_subtraction)}; |
| 175 | } | 179 | } |
| 176 | 180 | ||
| 177 | inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, | 181 | inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, |
| 178 | size_t rsp_alignment, size_t needed_frame_size = 0) { | 182 | size_t rsp_alignment, size_t needed_frame_size = 0) { |
| 179 | s32 subtraction, xmm_offset; | 183 | auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size); |
| 180 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | 184 | |
| 181 | for (std::size_t i = 0; i < regs.size(); ++i) { | 185 | for (std::size_t i = 0; i < regs.size(); ++i) { |
| 182 | if (regs[i] && ABI_ALL_GPRS[i]) { | 186 | if (regs[i] && ABI_ALL_GPRS[i]) { |
| 183 | code.push(IndexToReg64(static_cast<int>(i))); | 187 | code.push(IndexToReg64(i)); |
| 184 | } | 188 | } |
| 185 | } | 189 | } |
| 186 | if (subtraction != 0) { | ||
| 187 | code.sub(code.rsp, subtraction); | ||
| 188 | } | ||
| 189 | 190 | ||
| 190 | for (int i = 0; i < regs.count(); i++) { | 191 | if (frame_info.subtraction != 0) { |
| 191 | if (regs.test(i) & ABI_ALL_GPRS.test(i)) { | 192 | code.sub(code.rsp, frame_info.subtraction); |
| 192 | code.push(IndexToReg64(i)); | ||
| 193 | } | ||
| 194 | } | 193 | } |
| 195 | 194 | ||
| 196 | for (std::size_t i = 0; i < regs.size(); ++i) { | 195 | for (std::size_t i = 0; i < regs.size(); ++i) { |
| 197 | if (regs[i] && ABI_ALL_XMMS[i]) { | 196 | if (regs[i] && ABI_ALL_XMMS[i]) { |
| 198 | code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(static_cast<int>(i))); | 197 | code.movaps(code.xword[code.rsp + frame_info.xmm_offset], IndexToXmm(i)); |
| 199 | xmm_offset += 0x10; | 198 | frame_info.xmm_offset += 0x10; |
| 200 | } | 199 | } |
| 201 | } | 200 | } |
| 202 | 201 | ||
| @@ -205,59 +204,23 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::b | |||
| 205 | 204 | ||
| 206 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, | 205 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, |
| 207 | size_t rsp_alignment, size_t needed_frame_size = 0) { | 206 | size_t rsp_alignment, size_t needed_frame_size = 0) { |
| 208 | s32 subtraction, xmm_offset; | 207 | auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size); |
| 209 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | ||
| 210 | 208 | ||
| 211 | for (std::size_t i = 0; i < regs.size(); ++i) { | 209 | for (std::size_t i = 0; i < regs.size(); ++i) { |
| 212 | if (regs[i] && ABI_ALL_XMMS[i]) { | 210 | if (regs[i] && ABI_ALL_XMMS[i]) { |
| 213 | code.movaps(IndexToXmm(static_cast<int>(i)), code.xword[code.rsp + xmm_offset]); | 211 | code.movaps(IndexToXmm(i), code.xword[code.rsp + frame_info.xmm_offset]); |
| 214 | xmm_offset += 0x10; | 212 | frame_info.xmm_offset += 0x10; |
| 215 | } | 213 | } |
| 216 | } | 214 | } |
| 217 | 215 | ||
| 218 | if (subtraction != 0) { | 216 | if (frame_info.subtraction != 0) { |
| 219 | code.add(code.rsp, subtraction); | 217 | code.add(code.rsp, frame_info.subtraction); |
| 220 | } | 218 | } |
| 221 | 219 | ||
| 222 | // GPRs need to be popped in reverse order | 220 | // GPRs need to be popped in reverse order |
| 223 | for (int i = 15; i >= 0; i--) { | 221 | for (std::size_t j = 0; j < regs.size(); ++j) { |
| 224 | if (regs[i]) { | 222 | const std::size_t i = regs.size() - j - 1; |
| 225 | code.pop(IndexToReg64(i)); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | inline size_t ABI_PushRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs, | ||
| 231 | size_t rsp_alignment, | ||
| 232 | size_t needed_frame_size = 0) { | ||
| 233 | s32 subtraction, xmm_offset; | ||
| 234 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | ||
| 235 | |||
| 236 | for (std::size_t i = 0; i < regs.size(); ++i) { | ||
| 237 | if (regs[i] && ABI_ALL_GPRS[i]) { | 223 | if (regs[i] && ABI_ALL_GPRS[i]) { |
| 238 | code.push(IndexToReg64(static_cast<int>(i))); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | if (subtraction != 0) { | ||
| 243 | code.sub(code.rsp, subtraction); | ||
| 244 | } | ||
| 245 | |||
| 246 | return ABI_SHADOW_SPACE; | ||
| 247 | } | ||
| 248 | |||
| 249 | inline void ABI_PopRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs, | ||
| 250 | size_t rsp_alignment, size_t needed_frame_size = 0) { | ||
| 251 | s32 subtraction, xmm_offset; | ||
| 252 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | ||
| 253 | |||
| 254 | if (subtraction != 0) { | ||
| 255 | code.add(code.rsp, subtraction); | ||
| 256 | } | ||
| 257 | |||
| 258 | // GPRs need to be popped in reverse order | ||
| 259 | for (int i = 15; i >= 0; i--) { | ||
| 260 | if (regs[i]) { | ||
| 261 | code.pop(IndexToReg64(i)); | 224 | code.pop(IndexToReg64(i)); |
| 262 | } | 225 | } |
| 263 | } | 226 | } |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 47418006b..d1f173f42 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -7,6 +7,16 @@ endif() | |||
| 7 | add_library(core STATIC | 7 | add_library(core STATIC |
| 8 | arm/arm_interface.h | 8 | arm/arm_interface.h |
| 9 | arm/arm_interface.cpp | 9 | arm/arm_interface.cpp |
| 10 | arm/cpu_interrupt_handler.cpp | ||
| 11 | arm/cpu_interrupt_handler.h | ||
| 12 | arm/dynarmic/arm_dynarmic_32.cpp | ||
| 13 | arm/dynarmic/arm_dynarmic_32.h | ||
| 14 | arm/dynarmic/arm_dynarmic_64.cpp | ||
| 15 | arm/dynarmic/arm_dynarmic_64.h | ||
| 16 | arm/dynarmic/arm_dynarmic_cp15.cpp | ||
| 17 | arm/dynarmic/arm_dynarmic_cp15.h | ||
| 18 | arm/dynarmic/arm_exclusive_monitor.cpp | ||
| 19 | arm/dynarmic/arm_exclusive_monitor.h | ||
| 10 | arm/exclusive_monitor.cpp | 20 | arm/exclusive_monitor.cpp |
| 11 | arm/exclusive_monitor.h | 21 | arm/exclusive_monitor.h |
| 12 | arm/unicorn/arm_unicorn.cpp | 22 | arm/unicorn/arm_unicorn.cpp |
| @@ -15,8 +25,6 @@ add_library(core STATIC | |||
| 15 | constants.h | 25 | constants.h |
| 16 | core.cpp | 26 | core.cpp |
| 17 | core.h | 27 | core.h |
| 18 | core_manager.cpp | ||
| 19 | core_manager.h | ||
| 20 | core_timing.cpp | 28 | core_timing.cpp |
| 21 | core_timing.h | 29 | core_timing.h |
| 22 | core_timing_util.cpp | 30 | core_timing_util.cpp |
| @@ -606,11 +614,11 @@ endif() | |||
| 606 | create_target_directory_groups(core) | 614 | create_target_directory_groups(core) |
| 607 | 615 | ||
| 608 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 616 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 609 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus unicorn) | 617 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip) |
| 610 | 618 | ||
| 611 | if (YUZU_ENABLE_BOXCAT) | 619 | if (YUZU_ENABLE_BOXCAT) |
| 612 | target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) | 620 | target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) |
| 613 | target_link_libraries(core PRIVATE httplib nlohmann_json::nlohmann_json zip) | 621 | target_link_libraries(core PRIVATE httplib nlohmann_json::nlohmann_json) |
| 614 | endif() | 622 | endif() |
| 615 | 623 | ||
| 616 | if (ENABLE_WEB_SERVICE) | 624 | if (ENABLE_WEB_SERVICE) |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index d079a1bc8..d2295ed90 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -139,6 +139,63 @@ std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_addr | |||
| 139 | 139 | ||
| 140 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; | 140 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; |
| 141 | 141 | ||
| 142 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( | ||
| 143 | System& system, const ThreadContext64& ctx) { | ||
| 144 | std::vector<BacktraceEntry> out; | ||
| 145 | auto& memory = system.Memory(); | ||
| 146 | |||
| 147 | auto fp = ctx.cpu_registers[29]; | ||
| 148 | auto lr = ctx.cpu_registers[30]; | ||
| 149 | while (true) { | ||
| 150 | out.push_back({"", 0, lr, 0}); | ||
| 151 | if (!fp) { | ||
| 152 | break; | ||
| 153 | } | ||
| 154 | lr = memory.Read64(fp + 8) - 4; | ||
| 155 | fp = memory.Read64(fp); | ||
| 156 | } | ||
| 157 | |||
| 158 | std::map<VAddr, std::string> modules; | ||
| 159 | auto& loader{system.GetAppLoader()}; | ||
| 160 | if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { | ||
| 161 | return {}; | ||
| 162 | } | ||
| 163 | |||
| 164 | std::map<std::string, Symbols> symbols; | ||
| 165 | for (const auto& module : modules) { | ||
| 166 | symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); | ||
| 167 | } | ||
| 168 | |||
| 169 | for (auto& entry : out) { | ||
| 170 | VAddr base = 0; | ||
| 171 | for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { | ||
| 172 | const auto& module{*iter}; | ||
| 173 | if (entry.original_address >= module.first) { | ||
| 174 | entry.module = module.second; | ||
| 175 | base = module.first; | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | entry.offset = entry.original_address - base; | ||
| 181 | entry.address = SEGMENT_BASE + entry.offset; | ||
| 182 | |||
| 183 | if (entry.module.empty()) | ||
| 184 | entry.module = "unknown"; | ||
| 185 | |||
| 186 | const auto symbol_set = symbols.find(entry.module); | ||
| 187 | if (symbol_set != symbols.end()) { | ||
| 188 | const auto symbol = GetSymbolName(symbol_set->second, entry.offset); | ||
| 189 | if (symbol.has_value()) { | ||
| 190 | // TODO(DarkLordZach): Add demangling of symbol names. | ||
| 191 | entry.name = *symbol; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | return out; | ||
| 197 | } | ||
| 198 | |||
| 142 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | 199 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { |
| 143 | std::vector<BacktraceEntry> out; | 200 | std::vector<BacktraceEntry> out; |
| 144 | auto& memory = system.Memory(); | 201 | auto& memory = system.Memory(); |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index cb2e640e2..1f24051e4 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hardware_properties.h" | ||
| 10 | 11 | ||
| 11 | namespace Common { | 12 | namespace Common { |
| 12 | struct PageTable; | 13 | struct PageTable; |
| @@ -18,25 +19,29 @@ enum class VMAPermission : u8; | |||
| 18 | 19 | ||
| 19 | namespace Core { | 20 | namespace Core { |
| 20 | class System; | 21 | class System; |
| 22 | class CPUInterruptHandler; | ||
| 23 | |||
| 24 | using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; | ||
| 21 | 25 | ||
| 22 | /// Generic ARMv8 CPU interface | 26 | /// Generic ARMv8 CPU interface |
| 23 | class ARM_Interface : NonCopyable { | 27 | class ARM_Interface : NonCopyable { |
| 24 | public: | 28 | public: |
| 25 | explicit ARM_Interface(System& system_) : system{system_} {} | 29 | explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock) |
| 30 | : system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{ | ||
| 31 | uses_wall_clock} {} | ||
| 26 | virtual ~ARM_Interface() = default; | 32 | virtual ~ARM_Interface() = default; |
| 27 | 33 | ||
| 28 | struct ThreadContext32 { | 34 | struct ThreadContext32 { |
| 29 | std::array<u32, 16> cpu_registers{}; | 35 | std::array<u32, 16> cpu_registers{}; |
| 36 | std::array<u32, 64> extension_registers{}; | ||
| 30 | u32 cpsr{}; | 37 | u32 cpsr{}; |
| 31 | std::array<u8, 4> padding{}; | ||
| 32 | std::array<u64, 32> fprs{}; | ||
| 33 | u32 fpscr{}; | 38 | u32 fpscr{}; |
| 34 | u32 fpexc{}; | 39 | u32 fpexc{}; |
| 35 | u32 tpidr{}; | 40 | u32 tpidr{}; |
| 36 | }; | 41 | }; |
| 37 | // Internally within the kernel, it expects the AArch32 version of the | 42 | // Internally within the kernel, it expects the AArch32 version of the |
| 38 | // thread context to be 344 bytes in size. | 43 | // thread context to be 344 bytes in size. |
| 39 | static_assert(sizeof(ThreadContext32) == 0x158); | 44 | static_assert(sizeof(ThreadContext32) == 0x150); |
| 40 | 45 | ||
| 41 | struct ThreadContext64 { | 46 | struct ThreadContext64 { |
| 42 | std::array<u64, 31> cpu_registers{}; | 47 | std::array<u64, 31> cpu_registers{}; |
| @@ -143,6 +148,8 @@ public: | |||
| 143 | */ | 148 | */ |
| 144 | virtual void SetTPIDR_EL0(u64 value) = 0; | 149 | virtual void SetTPIDR_EL0(u64 value) = 0; |
| 145 | 150 | ||
| 151 | virtual void ChangeProcessorID(std::size_t new_core_id) = 0; | ||
| 152 | |||
| 146 | virtual void SaveContext(ThreadContext32& ctx) = 0; | 153 | virtual void SaveContext(ThreadContext32& ctx) = 0; |
| 147 | virtual void SaveContext(ThreadContext64& ctx) = 0; | 154 | virtual void SaveContext(ThreadContext64& ctx) = 0; |
| 148 | virtual void LoadContext(const ThreadContext32& ctx) = 0; | 155 | virtual void LoadContext(const ThreadContext32& ctx) = 0; |
| @@ -162,6 +169,9 @@ public: | |||
| 162 | std::string name; | 169 | std::string name; |
| 163 | }; | 170 | }; |
| 164 | 171 | ||
| 172 | static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, | ||
| 173 | const ThreadContext64& ctx); | ||
| 174 | |||
| 165 | std::vector<BacktraceEntry> GetBacktrace() const; | 175 | std::vector<BacktraceEntry> GetBacktrace() const; |
| 166 | 176 | ||
| 167 | /// fp (= r29) points to the last frame record. | 177 | /// fp (= r29) points to the last frame record. |
| @@ -175,6 +185,8 @@ public: | |||
| 175 | protected: | 185 | protected: |
| 176 | /// System context that this ARM interface is running under. | 186 | /// System context that this ARM interface is running under. |
| 177 | System& system; | 187 | System& system; |
| 188 | CPUInterrupts& interrupt_handlers; | ||
| 189 | bool uses_wall_clock; | ||
| 178 | }; | 190 | }; |
| 179 | 191 | ||
| 180 | } // namespace Core | 192 | } // namespace Core |
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp new file mode 100644 index 000000000..df0350881 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.cpp | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/thread.h" | ||
| 6 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 7 | |||
| 8 | namespace Core { | ||
| 9 | |||
| 10 | CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { | ||
| 11 | interrupt_event = std::make_unique<Common::Event>(); | ||
| 12 | } | ||
| 13 | |||
| 14 | CPUInterruptHandler::~CPUInterruptHandler() = default; | ||
| 15 | |||
| 16 | void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { | ||
| 17 | if (is_interrupted_) { | ||
| 18 | interrupt_event->Set(); | ||
| 19 | } | ||
| 20 | this->is_interrupted = is_interrupted_; | ||
| 21 | } | ||
| 22 | |||
| 23 | void CPUInterruptHandler::AwaitInterrupt() { | ||
| 24 | interrupt_event->Wait(); | ||
| 25 | } | ||
| 26 | |||
| 27 | } // namespace Core | ||
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h new file mode 100644 index 000000000..3d062d326 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | class Event; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | |||
| 15 | class CPUInterruptHandler { | ||
| 16 | public: | ||
| 17 | CPUInterruptHandler(); | ||
| 18 | ~CPUInterruptHandler(); | ||
| 19 | |||
| 20 | CPUInterruptHandler(const CPUInterruptHandler&) = delete; | ||
| 21 | CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; | ||
| 22 | |||
| 23 | CPUInterruptHandler(CPUInterruptHandler&&) = default; | ||
| 24 | CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; | ||
| 25 | |||
| 26 | bool IsInterrupted() const { | ||
| 27 | return is_interrupted; | ||
| 28 | } | ||
| 29 | |||
| 30 | void SetInterrupt(bool is_interrupted); | ||
| 31 | |||
| 32 | void AwaitInterrupt(); | ||
| 33 | |||
| 34 | private: | ||
| 35 | bool is_interrupted{}; | ||
| 36 | std::unique_ptr<Common::Event> interrupt_event; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 9bc86e3b9..0d4ab95b7 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -7,15 +7,17 @@ | |||
| 7 | #include <dynarmic/A32/a32.h> | 7 | #include <dynarmic/A32/a32.h> |
| 8 | #include <dynarmic/A32/config.h> | 8 | #include <dynarmic/A32/config.h> |
| 9 | #include <dynarmic/A32/context.h> | 9 | #include <dynarmic/A32/context.h> |
| 10 | #include "common/microprofile.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/page_table.h" | ||
| 12 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 11 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 13 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 12 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||
| 13 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" | 14 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" |
| 15 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | ||
| 14 | #include "core/core.h" | 16 | #include "core/core.h" |
| 15 | #include "core/core_manager.h" | ||
| 16 | #include "core/core_timing.h" | 17 | #include "core/core_timing.h" |
| 17 | #include "core/hle/kernel/svc.h" | 18 | #include "core/hle/kernel/svc.h" |
| 18 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 20 | #include "core/settings.h" | ||
| 19 | 21 | ||
| 20 | namespace Core { | 22 | namespace Core { |
| 21 | 23 | ||
| @@ -49,8 +51,22 @@ public: | |||
| 49 | parent.system.Memory().Write64(vaddr, value); | 51 | parent.system.Memory().Write64(vaddr, value); |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 54 | bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { | ||
| 55 | return parent.system.Memory().WriteExclusive8(vaddr, value, expected); | ||
| 56 | } | ||
| 57 | bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { | ||
| 58 | return parent.system.Memory().WriteExclusive16(vaddr, value, expected); | ||
| 59 | } | ||
| 60 | bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { | ||
| 61 | return parent.system.Memory().WriteExclusive32(vaddr, value, expected); | ||
| 62 | } | ||
| 63 | bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { | ||
| 64 | return parent.system.Memory().WriteExclusive64(vaddr, value, expected); | ||
| 65 | } | ||
| 66 | |||
| 52 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { | 67 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { |
| 53 | UNIMPLEMENTED(); | 68 | UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, |
| 69 | MemoryReadCode(pc)); | ||
| 54 | } | 70 | } |
| 55 | 71 | ||
| 56 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { | 72 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { |
| @@ -61,7 +77,7 @@ public: | |||
| 61 | case Dynarmic::A32::Exception::Breakpoint: | 77 | case Dynarmic::A32::Exception::Breakpoint: |
| 62 | break; | 78 | break; |
| 63 | } | 79 | } |
| 64 | LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", | 80 | LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", |
| 65 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); | 81 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); |
| 66 | UNIMPLEMENTED(); | 82 | UNIMPLEMENTED(); |
| 67 | } | 83 | } |
| @@ -71,26 +87,36 @@ public: | |||
| 71 | } | 87 | } |
| 72 | 88 | ||
| 73 | void AddTicks(u64 ticks) override { | 89 | void AddTicks(u64 ticks) override { |
| 90 | if (parent.uses_wall_clock) { | ||
| 91 | return; | ||
| 92 | } | ||
| 74 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 93 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 75 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 94 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| 76 | // if not all cores are doing a similar amount of work. Instead of doing this, we should | 95 | // if not all cores are doing a similar amount of work. Instead of doing this, we should |
| 77 | // device a way so that timing is consistent across all cores without increasing the ticks 4 | 96 | // device a way so that timing is consistent across all cores without increasing the ticks 4 |
| 78 | // times. | 97 | // times. |
| 79 | u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; | 98 | u64 amortized_ticks = |
| 99 | (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; | ||
| 80 | // Always execute at least one tick. | 100 | // Always execute at least one tick. |
| 81 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 101 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 82 | 102 | ||
| 83 | parent.system.CoreTiming().AddTicks(amortized_ticks); | 103 | parent.system.CoreTiming().AddTicks(amortized_ticks); |
| 84 | num_interpreted_instructions = 0; | 104 | num_interpreted_instructions = 0; |
| 85 | } | 105 | } |
| 106 | |||
| 86 | u64 GetTicksRemaining() override { | 107 | u64 GetTicksRemaining() override { |
| 87 | return std::max(parent.system.CoreTiming().GetDowncount(), {}); | 108 | if (parent.uses_wall_clock) { |
| 109 | if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { | ||
| 110 | return minimum_run_cycles; | ||
| 111 | } | ||
| 112 | return 0U; | ||
| 113 | } | ||
| 114 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | ||
| 88 | } | 115 | } |
| 89 | 116 | ||
| 90 | ARM_Dynarmic_32& parent; | 117 | ARM_Dynarmic_32& parent; |
| 91 | std::size_t num_interpreted_instructions{}; | 118 | std::size_t num_interpreted_instructions{}; |
| 92 | u64 tpidrro_el0{}; | 119 | static constexpr u64 minimum_run_cycles = 1000U; |
| 93 | u64 tpidr_el0{}; | ||
| 94 | }; | 120 | }; |
| 95 | 121 | ||
| 96 | std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, | 122 | std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, |
| @@ -99,26 +125,46 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& | |||
| 99 | config.callbacks = cb.get(); | 125 | config.callbacks = cb.get(); |
| 100 | // TODO(bunnei): Implement page table for 32-bit | 126 | // TODO(bunnei): Implement page table for 32-bit |
| 101 | // config.page_table = &page_table.pointers; | 127 | // config.page_table = &page_table.pointers; |
| 102 | config.coprocessors[15] = std::make_shared<DynarmicCP15>((u32*)&CP15_regs[0]); | 128 | config.coprocessors[15] = cp15; |
| 103 | config.define_unpredictable_behaviour = true; | 129 | config.define_unpredictable_behaviour = true; |
| 130 | static constexpr std::size_t PAGE_BITS = 12; | ||
| 131 | static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); | ||
| 132 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( | ||
| 133 | page_table.pointers.data()); | ||
| 134 | config.absolute_offset_page_table = true; | ||
| 135 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | ||
| 136 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; | ||
| 137 | |||
| 138 | // Multi-process state | ||
| 139 | config.processor_id = core_index; | ||
| 140 | config.global_monitor = &exclusive_monitor.monitor; | ||
| 141 | |||
| 142 | // Timing | ||
| 143 | config.wall_clock_cntpct = uses_wall_clock; | ||
| 144 | |||
| 145 | // Optimizations | ||
| 146 | if (Settings::values.disable_cpu_opt) { | ||
| 147 | config.enable_optimizations = false; | ||
| 148 | config.enable_fast_dispatch = false; | ||
| 149 | } | ||
| 150 | |||
| 104 | return std::make_unique<Dynarmic::A32::Jit>(config); | 151 | return std::make_unique<Dynarmic::A32::Jit>(config); |
| 105 | } | 152 | } |
| 106 | 153 | ||
| 107 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); | ||
| 108 | |||
| 109 | void ARM_Dynarmic_32::Run() { | 154 | void ARM_Dynarmic_32::Run() { |
| 110 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32); | ||
| 111 | jit->Run(); | 155 | jit->Run(); |
| 112 | } | 156 | } |
| 113 | 157 | ||
| 114 | void ARM_Dynarmic_32::Step() { | 158 | void ARM_Dynarmic_32::Step() { |
| 115 | cb->InterpreterFallback(jit->Regs()[15], 1); | 159 | jit->Step(); |
| 116 | } | 160 | } |
| 117 | 161 | ||
| 118 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, | 162 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, |
| 163 | bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, | ||
| 119 | std::size_t core_index) | 164 | std::size_t core_index) |
| 120 | : ARM_Interface{system}, | 165 | : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, |
| 121 | cb(std::make_unique<DynarmicCallbacks32>(*this)), core_index{core_index}, | 166 | cb(std::make_unique<DynarmicCallbacks32>(*this)), |
| 167 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index}, | ||
| 122 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | 168 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} |
| 123 | 169 | ||
| 124 | ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; | 170 | ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; |
| @@ -154,32 +200,40 @@ void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { | |||
| 154 | } | 200 | } |
| 155 | 201 | ||
| 156 | u64 ARM_Dynarmic_32::GetTlsAddress() const { | 202 | u64 ARM_Dynarmic_32::GetTlsAddress() const { |
| 157 | return CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)]; | 203 | return cp15->uro; |
| 158 | } | 204 | } |
| 159 | 205 | ||
| 160 | void ARM_Dynarmic_32::SetTlsAddress(VAddr address) { | 206 | void ARM_Dynarmic_32::SetTlsAddress(VAddr address) { |
| 161 | CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)] = static_cast<u32>(address); | 207 | cp15->uro = static_cast<u32>(address); |
| 162 | } | 208 | } |
| 163 | 209 | ||
| 164 | u64 ARM_Dynarmic_32::GetTPIDR_EL0() const { | 210 | u64 ARM_Dynarmic_32::GetTPIDR_EL0() const { |
| 165 | return cb->tpidr_el0; | 211 | return cp15->uprw; |
| 166 | } | 212 | } |
| 167 | 213 | ||
| 168 | void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { | 214 | void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { |
| 169 | cb->tpidr_el0 = value; | 215 | cp15->uprw = static_cast<u32>(value); |
| 216 | } | ||
| 217 | |||
| 218 | void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { | ||
| 219 | jit->ChangeProcessorID(new_core_id); | ||
| 170 | } | 220 | } |
| 171 | 221 | ||
| 172 | void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { | 222 | void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { |
| 173 | Dynarmic::A32::Context context; | 223 | Dynarmic::A32::Context context; |
| 174 | jit->SaveContext(context); | 224 | jit->SaveContext(context); |
| 175 | ctx.cpu_registers = context.Regs(); | 225 | ctx.cpu_registers = context.Regs(); |
| 226 | ctx.extension_registers = context.ExtRegs(); | ||
| 176 | ctx.cpsr = context.Cpsr(); | 227 | ctx.cpsr = context.Cpsr(); |
| 228 | ctx.fpscr = context.Fpscr(); | ||
| 177 | } | 229 | } |
| 178 | 230 | ||
| 179 | void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { | 231 | void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { |
| 180 | Dynarmic::A32::Context context; | 232 | Dynarmic::A32::Context context; |
| 181 | context.Regs() = ctx.cpu_registers; | 233 | context.Regs() = ctx.cpu_registers; |
| 234 | context.ExtRegs() = ctx.extension_registers; | ||
| 182 | context.SetCpsr(ctx.cpsr); | 235 | context.SetCpsr(ctx.cpsr); |
| 236 | context.SetFpscr(ctx.fpscr); | ||
| 183 | jit->LoadContext(context); | 237 | jit->LoadContext(context); |
| 184 | } | 238 | } |
| 185 | 239 | ||
| @@ -188,10 +242,15 @@ void ARM_Dynarmic_32::PrepareReschedule() { | |||
| 188 | } | 242 | } |
| 189 | 243 | ||
| 190 | void ARM_Dynarmic_32::ClearInstructionCache() { | 244 | void ARM_Dynarmic_32::ClearInstructionCache() { |
| 245 | if (!jit) { | ||
| 246 | return; | ||
| 247 | } | ||
| 191 | jit->ClearCache(); | 248 | jit->ClearCache(); |
| 192 | } | 249 | } |
| 193 | 250 | ||
| 194 | void ARM_Dynarmic_32::ClearExclusiveState() {} | 251 | void ARM_Dynarmic_32::ClearExclusiveState() { |
| 252 | jit->ClearExclusiveState(); | ||
| 253 | } | ||
| 195 | 254 | ||
| 196 | void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, | 255 | void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, |
| 197 | std::size_t new_address_space_size_in_bits) { | 256 | std::size_t new_address_space_size_in_bits) { |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 8ba9cea8f..2bab31b92 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include <dynarmic/A32/a32.h> | 10 | #include <dynarmic/A32/a32.h> |
| 11 | #include <dynarmic/A64/a64.h> | 11 | #include <dynarmic/A64/a64.h> |
| 12 | #include <dynarmic/A64/exclusive_monitor.h> | 12 | #include <dynarmic/exclusive_monitor.h> |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/hash.h" | 14 | #include "common/hash.h" |
| 15 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| @@ -21,13 +21,16 @@ class Memory; | |||
| 21 | 21 | ||
| 22 | namespace Core { | 22 | namespace Core { |
| 23 | 23 | ||
| 24 | class CPUInterruptHandler; | ||
| 24 | class DynarmicCallbacks32; | 25 | class DynarmicCallbacks32; |
| 26 | class DynarmicCP15; | ||
| 25 | class DynarmicExclusiveMonitor; | 27 | class DynarmicExclusiveMonitor; |
| 26 | class System; | 28 | class System; |
| 27 | 29 | ||
| 28 | class ARM_Dynarmic_32 final : public ARM_Interface { | 30 | class ARM_Dynarmic_32 final : public ARM_Interface { |
| 29 | public: | 31 | public: |
| 30 | ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | 32 | ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 33 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | ||
| 31 | ~ARM_Dynarmic_32() override; | 34 | ~ARM_Dynarmic_32() override; |
| 32 | 35 | ||
| 33 | void SetPC(u64 pc) override; | 36 | void SetPC(u64 pc) override; |
| @@ -44,6 +47,7 @@ public: | |||
| 44 | void SetTlsAddress(VAddr address) override; | 47 | void SetTlsAddress(VAddr address) override; |
| 45 | void SetTPIDR_EL0(u64 value) override; | 48 | void SetTPIDR_EL0(u64 value) override; |
| 46 | u64 GetTPIDR_EL0() const override; | 49 | u64 GetTPIDR_EL0() const override; |
| 50 | void ChangeProcessorID(std::size_t new_core_id) override; | ||
| 47 | 51 | ||
| 48 | void SaveContext(ThreadContext32& ctx) override; | 52 | void SaveContext(ThreadContext32& ctx) override; |
| 49 | void SaveContext(ThreadContext64& ctx) override {} | 53 | void SaveContext(ThreadContext64& ctx) override {} |
| @@ -66,12 +70,14 @@ private: | |||
| 66 | std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; | 70 | std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; |
| 67 | 71 | ||
| 68 | friend class DynarmicCallbacks32; | 72 | friend class DynarmicCallbacks32; |
| 73 | friend class DynarmicCP15; | ||
| 74 | |||
| 69 | std::unique_ptr<DynarmicCallbacks32> cb; | 75 | std::unique_ptr<DynarmicCallbacks32> cb; |
| 70 | JitCacheType jit_cache; | 76 | JitCacheType jit_cache; |
| 71 | std::shared_ptr<Dynarmic::A32::Jit> jit; | 77 | std::shared_ptr<Dynarmic::A32::Jit> jit; |
| 78 | std::shared_ptr<DynarmicCP15> cp15; | ||
| 72 | std::size_t core_index; | 79 | std::size_t core_index; |
| 73 | DynarmicExclusiveMonitor& exclusive_monitor; | 80 | DynarmicExclusiveMonitor& exclusive_monitor; |
| 74 | std::array<u32, 84> CP15_regs{}; | ||
| 75 | }; | 81 | }; |
| 76 | 82 | ||
| 77 | } // namespace Core | 83 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 337b97be9..790981034 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -7,11 +7,11 @@ | |||
| 7 | #include <dynarmic/A64/a64.h> | 7 | #include <dynarmic/A64/a64.h> |
| 8 | #include <dynarmic/A64/config.h> | 8 | #include <dynarmic/A64/config.h> |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/microprofile.h" | ||
| 11 | #include "common/page_table.h" | 10 | #include "common/page_table.h" |
| 11 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 12 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 12 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 13 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | ||
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/core_manager.h" | ||
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/core_timing_util.h" | 16 | #include "core/core_timing_util.h" |
| 17 | #include "core/gdbstub/gdbstub.h" | 17 | #include "core/gdbstub/gdbstub.h" |
| @@ -65,6 +65,22 @@ public: | |||
| 65 | memory.Write64(vaddr + 8, value[1]); | 65 | memory.Write64(vaddr + 8, value[1]); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { | ||
| 69 | return parent.system.Memory().WriteExclusive8(vaddr, value, expected); | ||
| 70 | } | ||
| 71 | bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { | ||
| 72 | return parent.system.Memory().WriteExclusive16(vaddr, value, expected); | ||
| 73 | } | ||
| 74 | bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { | ||
| 75 | return parent.system.Memory().WriteExclusive32(vaddr, value, expected); | ||
| 76 | } | ||
| 77 | bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { | ||
| 78 | return parent.system.Memory().WriteExclusive64(vaddr, value, expected); | ||
| 79 | } | ||
| 80 | bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { | ||
| 81 | return parent.system.Memory().WriteExclusive128(vaddr, value, expected); | ||
| 82 | } | ||
| 83 | |||
| 68 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { | 84 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { |
| 69 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, | 85 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 70 | num_instructions, MemoryReadCode(pc)); | 86 | num_instructions, MemoryReadCode(pc)); |
| @@ -98,8 +114,8 @@ public: | |||
| 98 | } | 114 | } |
| 99 | [[fallthrough]]; | 115 | [[fallthrough]]; |
| 100 | default: | 116 | default: |
| 101 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", | 117 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", |
| 102 | static_cast<std::size_t>(exception), pc); | 118 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); |
| 103 | } | 119 | } |
| 104 | } | 120 | } |
| 105 | 121 | ||
| @@ -108,29 +124,42 @@ public: | |||
| 108 | } | 124 | } |
| 109 | 125 | ||
| 110 | void AddTicks(u64 ticks) override { | 126 | void AddTicks(u64 ticks) override { |
| 127 | if (parent.uses_wall_clock) { | ||
| 128 | return; | ||
| 129 | } | ||
| 111 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 130 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 112 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 131 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| 113 | // if not all cores are doing a similar amount of work. Instead of doing this, we should | 132 | // if not all cores are doing a similar amount of work. Instead of doing this, we should |
| 114 | // device a way so that timing is consistent across all cores without increasing the ticks 4 | 133 | // device a way so that timing is consistent across all cores without increasing the ticks 4 |
| 115 | // times. | 134 | // times. |
| 116 | u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; | 135 | u64 amortized_ticks = |
| 136 | (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; | ||
| 117 | // Always execute at least one tick. | 137 | // Always execute at least one tick. |
| 118 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 138 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 119 | 139 | ||
| 120 | parent.system.CoreTiming().AddTicks(amortized_ticks); | 140 | parent.system.CoreTiming().AddTicks(amortized_ticks); |
| 121 | num_interpreted_instructions = 0; | 141 | num_interpreted_instructions = 0; |
| 122 | } | 142 | } |
| 143 | |||
| 123 | u64 GetTicksRemaining() override { | 144 | u64 GetTicksRemaining() override { |
| 124 | return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); | 145 | if (parent.uses_wall_clock) { |
| 146 | if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { | ||
| 147 | return minimum_run_cycles; | ||
| 148 | } | ||
| 149 | return 0U; | ||
| 150 | } | ||
| 151 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | ||
| 125 | } | 152 | } |
| 153 | |||
| 126 | u64 GetCNTPCT() override { | 154 | u64 GetCNTPCT() override { |
| 127 | return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); | 155 | return parent.system.CoreTiming().GetClockTicks(); |
| 128 | } | 156 | } |
| 129 | 157 | ||
| 130 | ARM_Dynarmic_64& parent; | 158 | ARM_Dynarmic_64& parent; |
| 131 | std::size_t num_interpreted_instructions = 0; | 159 | std::size_t num_interpreted_instructions = 0; |
| 132 | u64 tpidrro_el0 = 0; | 160 | u64 tpidrro_el0 = 0; |
| 133 | u64 tpidr_el0 = 0; | 161 | u64 tpidr_el0 = 0; |
| 162 | static constexpr u64 minimum_run_cycles = 1000U; | ||
| 134 | }; | 163 | }; |
| 135 | 164 | ||
| 136 | std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, | 165 | std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, |
| @@ -168,14 +197,13 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& | |||
| 168 | config.enable_fast_dispatch = false; | 197 | config.enable_fast_dispatch = false; |
| 169 | } | 198 | } |
| 170 | 199 | ||
| 200 | // Timing | ||
| 201 | config.wall_clock_cntpct = uses_wall_clock; | ||
| 202 | |||
| 171 | return std::make_shared<Dynarmic::A64::Jit>(config); | 203 | return std::make_shared<Dynarmic::A64::Jit>(config); |
| 172 | } | 204 | } |
| 173 | 205 | ||
| 174 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); | ||
| 175 | |||
| 176 | void ARM_Dynarmic_64::Run() { | 206 | void ARM_Dynarmic_64::Run() { |
| 177 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64); | ||
| 178 | |||
| 179 | jit->Run(); | 207 | jit->Run(); |
| 180 | } | 208 | } |
| 181 | 209 | ||
| @@ -183,11 +211,16 @@ void ARM_Dynarmic_64::Step() { | |||
| 183 | cb->InterpreterFallback(jit->GetPC(), 1); | 211 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 184 | } | 212 | } |
| 185 | 213 | ||
| 186 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, | 214 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, |
| 215 | bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, | ||
| 187 | std::size_t core_index) | 216 | std::size_t core_index) |
| 188 | : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks64>(*this)), | 217 | : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, |
| 189 | inner_unicorn{system, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, | 218 | cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system, interrupt_handlers, |
| 190 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | 219 | uses_wall_clock, |
| 220 | ARM_Unicorn::Arch::AArch64, | ||
| 221 | core_index}, | ||
| 222 | core_index{core_index}, exclusive_monitor{ | ||
| 223 | dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | ||
| 191 | 224 | ||
| 192 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; | 225 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; |
| 193 | 226 | ||
| @@ -239,6 +272,10 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { | |||
| 239 | cb->tpidr_el0 = value; | 272 | cb->tpidr_el0 = value; |
| 240 | } | 273 | } |
| 241 | 274 | ||
| 275 | void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { | ||
| 276 | jit->ChangeProcessorID(new_core_id); | ||
| 277 | } | ||
| 278 | |||
| 242 | void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { | 279 | void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { |
| 243 | ctx.cpu_registers = jit->GetRegisters(); | 280 | ctx.cpu_registers = jit->GetRegisters(); |
| 244 | ctx.sp = jit->GetSP(); | 281 | ctx.sp = jit->GetSP(); |
| @@ -266,6 +303,9 @@ void ARM_Dynarmic_64::PrepareReschedule() { | |||
| 266 | } | 303 | } |
| 267 | 304 | ||
| 268 | void ARM_Dynarmic_64::ClearInstructionCache() { | 305 | void ARM_Dynarmic_64::ClearInstructionCache() { |
| 306 | if (!jit) { | ||
| 307 | return; | ||
| 308 | } | ||
| 269 | jit->ClearCache(); | 309 | jit->ClearCache(); |
| 270 | } | 310 | } |
| 271 | 311 | ||
| @@ -285,44 +325,4 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, | |||
| 285 | jit_cache.emplace(key, jit); | 325 | jit_cache.emplace(key, jit); |
| 286 | } | 326 | } |
| 287 | 327 | ||
| 288 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) | ||
| 289 | : monitor(core_count), memory{memory} {} | ||
| 290 | |||
| 291 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | ||
| 292 | |||
| 293 | void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { | ||
| 294 | // Size doesn't actually matter. | ||
| 295 | monitor.Mark(core_index, addr, 16); | ||
| 296 | } | ||
| 297 | |||
| 298 | void DynarmicExclusiveMonitor::ClearExclusive() { | ||
| 299 | monitor.Clear(); | ||
| 300 | } | ||
| 301 | |||
| 302 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { | ||
| 303 | return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); }); | ||
| 304 | } | ||
| 305 | |||
| 306 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { | ||
| 307 | return monitor.DoExclusiveOperation(core_index, vaddr, 2, | ||
| 308 | [&] { memory.Write16(vaddr, value); }); | ||
| 309 | } | ||
| 310 | |||
| 311 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { | ||
| 312 | return monitor.DoExclusiveOperation(core_index, vaddr, 4, | ||
| 313 | [&] { memory.Write32(vaddr, value); }); | ||
| 314 | } | ||
| 315 | |||
| 316 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { | ||
| 317 | return monitor.DoExclusiveOperation(core_index, vaddr, 8, | ||
| 318 | [&] { memory.Write64(vaddr, value); }); | ||
| 319 | } | ||
| 320 | |||
| 321 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { | ||
| 322 | return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { | ||
| 323 | memory.Write64(vaddr + 0, value[0]); | ||
| 324 | memory.Write64(vaddr + 8, value[1]); | ||
| 325 | }); | ||
| 326 | } | ||
| 327 | |||
| 328 | } // namespace Core | 328 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 647cecaf0..403c55961 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <unordered_map> | 8 | #include <unordered_map> |
| 9 | 9 | ||
| 10 | #include <dynarmic/A64/a64.h> | 10 | #include <dynarmic/A64/a64.h> |
| 11 | #include <dynarmic/A64/exclusive_monitor.h> | ||
| 12 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 13 | #include "common/hash.h" | 12 | #include "common/hash.h" |
| 14 | #include "core/arm/arm_interface.h" | 13 | #include "core/arm/arm_interface.h" |
| @@ -22,12 +21,14 @@ class Memory; | |||
| 22 | namespace Core { | 21 | namespace Core { |
| 23 | 22 | ||
| 24 | class DynarmicCallbacks64; | 23 | class DynarmicCallbacks64; |
| 24 | class CPUInterruptHandler; | ||
| 25 | class DynarmicExclusiveMonitor; | 25 | class DynarmicExclusiveMonitor; |
| 26 | class System; | 26 | class System; |
| 27 | 27 | ||
| 28 | class ARM_Dynarmic_64 final : public ARM_Interface { | 28 | class ARM_Dynarmic_64 final : public ARM_Interface { |
| 29 | public: | 29 | public: |
| 30 | ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | 30 | ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 31 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | ||
| 31 | ~ARM_Dynarmic_64() override; | 32 | ~ARM_Dynarmic_64() override; |
| 32 | 33 | ||
| 33 | void SetPC(u64 pc) override; | 34 | void SetPC(u64 pc) override; |
| @@ -44,6 +45,7 @@ public: | |||
| 44 | void SetTlsAddress(VAddr address) override; | 45 | void SetTlsAddress(VAddr address) override; |
| 45 | void SetTPIDR_EL0(u64 value) override; | 46 | void SetTPIDR_EL0(u64 value) override; |
| 46 | u64 GetTPIDR_EL0() const override; | 47 | u64 GetTPIDR_EL0() const override; |
| 48 | void ChangeProcessorID(std::size_t new_core_id) override; | ||
| 47 | 49 | ||
| 48 | void SaveContext(ThreadContext32& ctx) override {} | 50 | void SaveContext(ThreadContext32& ctx) override {} |
| 49 | void SaveContext(ThreadContext64& ctx) override; | 51 | void SaveContext(ThreadContext64& ctx) override; |
| @@ -75,24 +77,4 @@ private: | |||
| 75 | DynarmicExclusiveMonitor& exclusive_monitor; | 77 | DynarmicExclusiveMonitor& exclusive_monitor; |
| 76 | }; | 78 | }; |
| 77 | 79 | ||
| 78 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | ||
| 79 | public: | ||
| 80 | explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); | ||
| 81 | ~DynarmicExclusiveMonitor() override; | ||
| 82 | |||
| 83 | void SetExclusive(std::size_t core_index, VAddr addr) override; | ||
| 84 | void ClearExclusive() override; | ||
| 85 | |||
| 86 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; | ||
| 87 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; | ||
| 88 | bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; | ||
| 89 | bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; | ||
| 90 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; | ||
| 91 | |||
| 92 | private: | ||
| 93 | friend class ARM_Dynarmic_64; | ||
| 94 | Dynarmic::A64::ExclusiveMonitor monitor; | ||
| 95 | Core::Memory::Memory& memory; | ||
| 96 | }; | ||
| 97 | |||
| 98 | } // namespace Core | 80 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index 3fdcdebde..54556e0f9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | |||
| @@ -2,79 +2,132 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | ||
| 5 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" | 8 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" |
| 9 | #include "core/core.h" | ||
| 10 | #include "core/core_timing.h" | ||
| 11 | #include "core/core_timing_util.h" | ||
| 6 | 12 | ||
| 7 | using Callback = Dynarmic::A32::Coprocessor::Callback; | 13 | using Callback = Dynarmic::A32::Coprocessor::Callback; |
| 8 | using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; | 14 | using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; |
| 9 | using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; | 15 | using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; |
| 10 | 16 | ||
| 17 | template <> | ||
| 18 | struct fmt::formatter<Dynarmic::A32::CoprocReg> { | ||
| 19 | constexpr auto parse(format_parse_context& ctx) { | ||
| 20 | return ctx.begin(); | ||
| 21 | } | ||
| 22 | template <typename FormatContext> | ||
| 23 | auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) { | ||
| 24 | return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg)); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | |||
| 28 | namespace Core { | ||
| 29 | |||
| 30 | static u32 dummy_value; | ||
| 31 | |||
| 11 | std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1, | 32 | std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1, |
| 12 | CoprocReg CRd, CoprocReg CRn, | 33 | CoprocReg CRd, CoprocReg CRn, |
| 13 | CoprocReg CRm, unsigned opc2) { | 34 | CoprocReg CRm, unsigned opc2) { |
| 35 | LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn, | ||
| 36 | CRm, opc2); | ||
| 14 | return {}; | 37 | return {}; |
| 15 | } | 38 | } |
| 16 | 39 | ||
| 17 | CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, | 40 | CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, |
| 18 | CoprocReg CRm, unsigned opc2) { | 41 | CoprocReg CRm, unsigned opc2) { |
| 19 | // TODO(merry): Privileged CP15 registers | ||
| 20 | |||
| 21 | if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) { | 42 | if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) { |
| 43 | // CP15_FLUSH_PREFETCH_BUFFER | ||
| 22 | // This is a dummy write, we ignore the value written here. | 44 | // This is a dummy write, we ignore the value written here. |
| 23 | return &CP15[static_cast<std::size_t>(CP15Register::CP15_FLUSH_PREFETCH_BUFFER)]; | 45 | return &dummy_value; |
| 24 | } | 46 | } |
| 25 | 47 | ||
| 26 | if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) { | 48 | if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) { |
| 27 | switch (opc2) { | 49 | switch (opc2) { |
| 28 | case 4: | 50 | case 4: |
| 51 | // CP15_DATA_SYNC_BARRIER | ||
| 29 | // This is a dummy write, we ignore the value written here. | 52 | // This is a dummy write, we ignore the value written here. |
| 30 | return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_SYNC_BARRIER)]; | 53 | return &dummy_value; |
| 31 | case 5: | 54 | case 5: |
| 55 | // CP15_DATA_MEMORY_BARRIER | ||
| 32 | // This is a dummy write, we ignore the value written here. | 56 | // This is a dummy write, we ignore the value written here. |
| 33 | return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_MEMORY_BARRIER)]; | 57 | return &dummy_value; |
| 34 | default: | ||
| 35 | return {}; | ||
| 36 | } | 58 | } |
| 37 | } | 59 | } |
| 38 | 60 | ||
| 39 | if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) { | 61 | if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) { |
| 40 | return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)]; | 62 | // CP15_THREAD_UPRW |
| 63 | return &uprw; | ||
| 41 | } | 64 | } |
| 42 | 65 | ||
| 66 | LOG_CRITICAL(Core_ARM, "CP15: mcr{} p15, {}, <Rt>, {}, {}, {}", two ? "2" : "", opc1, CRn, CRm, | ||
| 67 | opc2); | ||
| 43 | return {}; | 68 | return {}; |
| 44 | } | 69 | } |
| 45 | 70 | ||
| 46 | CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) { | 71 | CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) { |
| 72 | LOG_CRITICAL(Core_ARM, "CP15: mcrr{} p15, {}, <Rt>, <Rt2>, {}", two ? "2" : "", opc, CRm); | ||
| 47 | return {}; | 73 | return {}; |
| 48 | } | 74 | } |
| 49 | 75 | ||
| 50 | CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, | 76 | CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, |
| 51 | CoprocReg CRm, unsigned opc2) { | 77 | CoprocReg CRm, unsigned opc2) { |
| 52 | // TODO(merry): Privileged CP15 registers | ||
| 53 | |||
| 54 | if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) { | 78 | if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) { |
| 55 | switch (opc2) { | 79 | switch (opc2) { |
| 56 | case 2: | 80 | case 2: |
| 57 | return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)]; | 81 | // CP15_THREAD_UPRW |
| 82 | return &uprw; | ||
| 58 | case 3: | 83 | case 3: |
| 59 | return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)]; | 84 | // CP15_THREAD_URO |
| 60 | default: | 85 | return &uro; |
| 61 | return {}; | ||
| 62 | } | 86 | } |
| 63 | } | 87 | } |
| 64 | 88 | ||
| 89 | LOG_CRITICAL(Core_ARM, "CP15: mrc{} p15, {}, <Rt>, {}, {}, {}", two ? "2" : "", opc1, CRn, CRm, | ||
| 90 | opc2); | ||
| 65 | return {}; | 91 | return {}; |
| 66 | } | 92 | } |
| 67 | 93 | ||
| 68 | CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { | 94 | CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { |
| 95 | if (!two && opc == 0 && CRm == CoprocReg::C14) { | ||
| 96 | // CNTPCT | ||
| 97 | const auto callback = static_cast<u64 (*)(Dynarmic::A32::Jit*, void*, u32, u32)>( | ||
| 98 | [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { | ||
| 99 | ARM_Dynarmic_32& parent = *(ARM_Dynarmic_32*)arg; | ||
| 100 | return parent.system.CoreTiming().GetClockTicks(); | ||
| 101 | }); | ||
| 102 | return Dynarmic::A32::Coprocessor::Callback{callback, (void*)&parent}; | ||
| 103 | } | ||
| 104 | |||
| 105 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{} p15, {}, <Rt>, <Rt2>, {}", two ? "2" : "", opc, CRm); | ||
| 69 | return {}; | 106 | return {}; |
| 70 | } | 107 | } |
| 71 | 108 | ||
| 72 | std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd, | 109 | std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd, |
| 73 | std::optional<u8> option) { | 110 | std::optional<u8> option) { |
| 111 | if (option) { | ||
| 112 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...], {}", two ? "2" : "", | ||
| 113 | long_transfer ? "l" : "", CRd, *option); | ||
| 114 | } else { | ||
| 115 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", | ||
| 116 | long_transfer ? "l" : "", CRd); | ||
| 117 | } | ||
| 74 | return {}; | 118 | return {}; |
| 75 | } | 119 | } |
| 76 | 120 | ||
| 77 | std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, | 121 | std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, |
| 78 | std::optional<u8> option) { | 122 | std::optional<u8> option) { |
| 123 | if (option) { | ||
| 124 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...], {}", two ? "2" : "", | ||
| 125 | long_transfer ? "l" : "", CRd, *option); | ||
| 126 | } else { | ||
| 127 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", | ||
| 128 | long_transfer ? "l" : "", CRd); | ||
| 129 | } | ||
| 79 | return {}; | 130 | return {}; |
| 80 | } | 131 | } |
| 132 | |||
| 133 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h index 07bcde5f9..7356d252e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h | |||
| @@ -10,128 +10,15 @@ | |||
| 10 | #include <dynarmic/A32/coprocessor.h> | 10 | #include <dynarmic/A32/coprocessor.h> |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | enum class CP15Register { | 13 | namespace Core { |
| 14 | // c0 - Information registers | ||
| 15 | CP15_MAIN_ID, | ||
| 16 | CP15_CACHE_TYPE, | ||
| 17 | CP15_TCM_STATUS, | ||
| 18 | CP15_TLB_TYPE, | ||
| 19 | CP15_CPU_ID, | ||
| 20 | CP15_PROCESSOR_FEATURE_0, | ||
| 21 | CP15_PROCESSOR_FEATURE_1, | ||
| 22 | CP15_DEBUG_FEATURE_0, | ||
| 23 | CP15_AUXILIARY_FEATURE_0, | ||
| 24 | CP15_MEMORY_MODEL_FEATURE_0, | ||
| 25 | CP15_MEMORY_MODEL_FEATURE_1, | ||
| 26 | CP15_MEMORY_MODEL_FEATURE_2, | ||
| 27 | CP15_MEMORY_MODEL_FEATURE_3, | ||
| 28 | CP15_ISA_FEATURE_0, | ||
| 29 | CP15_ISA_FEATURE_1, | ||
| 30 | CP15_ISA_FEATURE_2, | ||
| 31 | CP15_ISA_FEATURE_3, | ||
| 32 | CP15_ISA_FEATURE_4, | ||
| 33 | 14 | ||
| 34 | // c1 - Control registers | 15 | class ARM_Dynarmic_32; |
| 35 | CP15_CONTROL, | ||
| 36 | CP15_AUXILIARY_CONTROL, | ||
| 37 | CP15_COPROCESSOR_ACCESS_CONTROL, | ||
| 38 | |||
| 39 | // c2 - Translation table registers | ||
| 40 | CP15_TRANSLATION_BASE_TABLE_0, | ||
| 41 | CP15_TRANSLATION_BASE_TABLE_1, | ||
| 42 | CP15_TRANSLATION_BASE_CONTROL, | ||
| 43 | CP15_DOMAIN_ACCESS_CONTROL, | ||
| 44 | CP15_RESERVED, | ||
| 45 | |||
| 46 | // c5 - Fault status registers | ||
| 47 | CP15_FAULT_STATUS, | ||
| 48 | CP15_INSTR_FAULT_STATUS, | ||
| 49 | CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS, | ||
| 50 | CP15_INST_FSR, | ||
| 51 | |||
| 52 | // c6 - Fault Address registers | ||
| 53 | CP15_FAULT_ADDRESS, | ||
| 54 | CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS, | ||
| 55 | CP15_WFAR, | ||
| 56 | CP15_IFAR, | ||
| 57 | |||
| 58 | // c7 - Cache operation registers | ||
| 59 | CP15_WAIT_FOR_INTERRUPT, | ||
| 60 | CP15_PHYS_ADDRESS, | ||
| 61 | CP15_INVALIDATE_INSTR_CACHE, | ||
| 62 | CP15_INVALIDATE_INSTR_CACHE_USING_MVA, | ||
| 63 | CP15_INVALIDATE_INSTR_CACHE_USING_INDEX, | ||
| 64 | CP15_FLUSH_PREFETCH_BUFFER, | ||
| 65 | CP15_FLUSH_BRANCH_TARGET_CACHE, | ||
| 66 | CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY, | ||
| 67 | CP15_INVALIDATE_DATA_CACHE, | ||
| 68 | CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA, | ||
| 69 | CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX, | ||
| 70 | CP15_INVALIDATE_DATA_AND_INSTR_CACHE, | ||
| 71 | CP15_CLEAN_DATA_CACHE, | ||
| 72 | CP15_CLEAN_DATA_CACHE_LINE_USING_MVA, | ||
| 73 | CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX, | ||
| 74 | CP15_DATA_SYNC_BARRIER, | ||
| 75 | CP15_DATA_MEMORY_BARRIER, | ||
| 76 | CP15_CLEAN_AND_INVALIDATE_DATA_CACHE, | ||
| 77 | CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA, | ||
| 78 | CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX, | ||
| 79 | |||
| 80 | // c8 - TLB operations | ||
| 81 | CP15_INVALIDATE_ITLB, | ||
| 82 | CP15_INVALIDATE_ITLB_SINGLE_ENTRY, | ||
| 83 | CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH, | ||
| 84 | CP15_INVALIDATE_ITLB_ENTRY_ON_MVA, | ||
| 85 | CP15_INVALIDATE_DTLB, | ||
| 86 | CP15_INVALIDATE_DTLB_SINGLE_ENTRY, | ||
| 87 | CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH, | ||
| 88 | CP15_INVALIDATE_DTLB_ENTRY_ON_MVA, | ||
| 89 | CP15_INVALIDATE_UTLB, | ||
| 90 | CP15_INVALIDATE_UTLB_SINGLE_ENTRY, | ||
| 91 | CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH, | ||
| 92 | CP15_INVALIDATE_UTLB_ENTRY_ON_MVA, | ||
| 93 | |||
| 94 | // c9 - Data cache lockdown register | ||
| 95 | CP15_DATA_CACHE_LOCKDOWN, | ||
| 96 | |||
| 97 | // c10 - TLB/Memory map registers | ||
| 98 | CP15_TLB_LOCKDOWN, | ||
| 99 | CP15_PRIMARY_REGION_REMAP, | ||
| 100 | CP15_NORMAL_REGION_REMAP, | ||
| 101 | |||
| 102 | // c13 - Thread related registers | ||
| 103 | CP15_PID, | ||
| 104 | CP15_CONTEXT_ID, | ||
| 105 | CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write | ||
| 106 | CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W) | ||
| 107 | CP15_THREAD_PRW, // Thread ID register - Privileged R/W only. | ||
| 108 | |||
| 109 | // c15 - Performance and TLB lockdown registers | ||
| 110 | CP15_PERFORMANCE_MONITOR_CONTROL, | ||
| 111 | CP15_CYCLE_COUNTER, | ||
| 112 | CP15_COUNT_0, | ||
| 113 | CP15_COUNT_1, | ||
| 114 | CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY, | ||
| 115 | CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY, | ||
| 116 | CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS, | ||
| 117 | CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS, | ||
| 118 | CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE, | ||
| 119 | CP15_TLB_DEBUG_CONTROL, | ||
| 120 | |||
| 121 | // Skyeye defined | ||
| 122 | CP15_TLB_FAULT_ADDR, | ||
| 123 | CP15_TLB_FAULT_STATUS, | ||
| 124 | |||
| 125 | // Not an actual register. | ||
| 126 | // All registers should be defined above this. | ||
| 127 | CP15_REGISTER_COUNT, | ||
| 128 | }; | ||
| 129 | 16 | ||
| 130 | class DynarmicCP15 final : public Dynarmic::A32::Coprocessor { | 17 | class DynarmicCP15 final : public Dynarmic::A32::Coprocessor { |
| 131 | public: | 18 | public: |
| 132 | using CoprocReg = Dynarmic::A32::CoprocReg; | 19 | using CoprocReg = Dynarmic::A32::CoprocReg; |
| 133 | 20 | ||
| 134 | explicit DynarmicCP15(u32* cp15) : CP15(cp15){}; | 21 | explicit DynarmicCP15(ARM_Dynarmic_32& parent) : parent(parent) {} |
| 135 | 22 | ||
| 136 | std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, | 23 | std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, |
| 137 | CoprocReg CRn, CoprocReg CRm, | 24 | CoprocReg CRn, CoprocReg CRm, |
| @@ -147,6 +34,9 @@ public: | |||
| 147 | std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, | 34 | std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, |
| 148 | std::optional<u8> option) override; | 35 | std::optional<u8> option) override; |
| 149 | 36 | ||
| 150 | private: | 37 | ARM_Dynarmic_32& parent; |
| 151 | u32* CP15{}; | 38 | u32 uprw; |
| 39 | u32 uro; | ||
| 152 | }; | 40 | }; |
| 41 | |||
| 42 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp new file mode 100644 index 000000000..4e209f6a5 --- /dev/null +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cinttypes> | ||
| 6 | #include <memory> | ||
| 7 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | ||
| 8 | #include "core/memory.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | |||
| 12 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) | ||
| 13 | : monitor(core_count), memory{memory} {} | ||
| 14 | |||
| 15 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | ||
| 16 | |||
| 17 | u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) { | ||
| 18 | return monitor.ReadAndMark<u8>(core_index, addr, [&]() -> u8 { return memory.Read8(addr); }); | ||
| 19 | } | ||
| 20 | |||
| 21 | u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) { | ||
| 22 | return monitor.ReadAndMark<u16>(core_index, addr, [&]() -> u16 { return memory.Read16(addr); }); | ||
| 23 | } | ||
| 24 | |||
| 25 | u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) { | ||
| 26 | return monitor.ReadAndMark<u32>(core_index, addr, [&]() -> u32 { return memory.Read32(addr); }); | ||
| 27 | } | ||
| 28 | |||
| 29 | u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) { | ||
| 30 | return monitor.ReadAndMark<u64>(core_index, addr, [&]() -> u64 { return memory.Read64(addr); }); | ||
| 31 | } | ||
| 32 | |||
| 33 | u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr addr) { | ||
| 34 | return monitor.ReadAndMark<u128>(core_index, addr, [&]() -> u128 { | ||
| 35 | u128 result; | ||
| 36 | result[0] = memory.Read64(addr); | ||
| 37 | result[1] = memory.Read64(addr + 8); | ||
| 38 | return result; | ||
| 39 | }); | ||
| 40 | } | ||
| 41 | |||
| 42 | void DynarmicExclusiveMonitor::ClearExclusive() { | ||
| 43 | monitor.Clear(); | ||
| 44 | } | ||
| 45 | |||
| 46 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { | ||
| 47 | return monitor.DoExclusiveOperation<u8>(core_index, vaddr, [&](u8 expected) -> bool { | ||
| 48 | return memory.WriteExclusive8(vaddr, value, expected); | ||
| 49 | }); | ||
| 50 | } | ||
| 51 | |||
| 52 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { | ||
| 53 | return monitor.DoExclusiveOperation<u16>(core_index, vaddr, [&](u16 expected) -> bool { | ||
| 54 | return memory.WriteExclusive16(vaddr, value, expected); | ||
| 55 | }); | ||
| 56 | } | ||
| 57 | |||
| 58 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { | ||
| 59 | return monitor.DoExclusiveOperation<u32>(core_index, vaddr, [&](u32 expected) -> bool { | ||
| 60 | return memory.WriteExclusive32(vaddr, value, expected); | ||
| 61 | }); | ||
| 62 | } | ||
| 63 | |||
| 64 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { | ||
| 65 | return monitor.DoExclusiveOperation<u64>(core_index, vaddr, [&](u64 expected) -> bool { | ||
| 66 | return memory.WriteExclusive64(vaddr, value, expected); | ||
| 67 | }); | ||
| 68 | } | ||
| 69 | |||
| 70 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { | ||
| 71 | return monitor.DoExclusiveOperation<u128>(core_index, vaddr, [&](u128 expected) -> bool { | ||
| 72 | return memory.WriteExclusive128(vaddr, value, expected); | ||
| 73 | }); | ||
| 74 | } | ||
| 75 | |||
| 76 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h new file mode 100644 index 000000000..964f4a55d --- /dev/null +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <unordered_map> | ||
| 9 | |||
| 10 | #include <dynarmic/exclusive_monitor.h> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | ||
| 14 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||
| 15 | #include "core/arm/exclusive_monitor.h" | ||
| 16 | |||
| 17 | namespace Core::Memory { | ||
| 18 | class Memory; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core { | ||
| 22 | |||
| 23 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | ||
| 24 | public: | ||
| 25 | explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); | ||
| 26 | ~DynarmicExclusiveMonitor() override; | ||
| 27 | |||
| 28 | u8 ExclusiveRead8(std::size_t core_index, VAddr addr) override; | ||
| 29 | u16 ExclusiveRead16(std::size_t core_index, VAddr addr) override; | ||
| 30 | u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; | ||
| 31 | u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; | ||
| 32 | u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; | ||
| 33 | void ClearExclusive() override; | ||
| 34 | |||
| 35 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; | ||
| 36 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; | ||
| 37 | bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; | ||
| 38 | bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; | ||
| 39 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; | ||
| 40 | |||
| 41 | private: | ||
| 42 | friend class ARM_Dynarmic_32; | ||
| 43 | friend class ARM_Dynarmic_64; | ||
| 44 | Dynarmic::ExclusiveMonitor monitor; | ||
| 45 | Core::Memory::Memory& memory; | ||
| 46 | }; | ||
| 47 | |||
| 48 | } // namespace Core | ||
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index b32401e0b..d8cba369d 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #ifdef ARCHITECTURE_x86_64 | 5 | #ifdef ARCHITECTURE_x86_64 |
| 6 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 6 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" |
| 7 | #endif | 7 | #endif |
| 8 | #include "core/arm/exclusive_monitor.h" | 8 | #include "core/arm/exclusive_monitor.h" |
| 9 | #include "core/memory.h" | 9 | #include "core/memory.h" |
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index ccd73b80f..62f6e6023 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h | |||
| @@ -18,7 +18,11 @@ class ExclusiveMonitor { | |||
| 18 | public: | 18 | public: |
| 19 | virtual ~ExclusiveMonitor(); | 19 | virtual ~ExclusiveMonitor(); |
| 20 | 20 | ||
| 21 | virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0; | 21 | virtual u8 ExclusiveRead8(std::size_t core_index, VAddr addr) = 0; |
| 22 | virtual u16 ExclusiveRead16(std::size_t core_index, VAddr addr) = 0; | ||
| 23 | virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0; | ||
| 24 | virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0; | ||
| 25 | virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0; | ||
| 22 | virtual void ClearExclusive() = 0; | 26 | virtual void ClearExclusive() = 0; |
| 23 | 27 | ||
| 24 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; | 28 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index e40e9626a..1df3f3ed1 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <unicorn/arm64.h> | 6 | #include <unicorn/arm64.h> |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/microprofile.h" | 8 | #include "common/microprofile.h" |
| 9 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 9 | #include "core/arm/unicorn/arm_unicorn.h" | 10 | #include "core/arm/unicorn/arm_unicorn.h" |
| 10 | #include "core/core.h" | 11 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | 12 | #include "core/core_timing.h" |
| @@ -62,7 +63,9 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si | |||
| 62 | return false; | 63 | return false; |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | ARM_Unicorn::ARM_Unicorn(System& system, Arch architecture) : ARM_Interface{system} { | 66 | ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 67 | Arch architecture, std::size_t core_index) | ||
| 68 | : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} { | ||
| 66 | const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; | 69 | const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; |
| 67 | CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); | 70 | CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); |
| 68 | 71 | ||
| @@ -156,12 +159,20 @@ void ARM_Unicorn::SetTPIDR_EL0(u64 value) { | |||
| 156 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); | 159 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); |
| 157 | } | 160 | } |
| 158 | 161 | ||
| 162 | void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) { | ||
| 163 | core_index = new_core_id; | ||
| 164 | } | ||
| 165 | |||
| 159 | void ARM_Unicorn::Run() { | 166 | void ARM_Unicorn::Run() { |
| 160 | if (GDBStub::IsServerEnabled()) { | 167 | if (GDBStub::IsServerEnabled()) { |
| 161 | ExecuteInstructions(std::max(4000000U, 0U)); | 168 | ExecuteInstructions(std::max(4000000U, 0U)); |
| 162 | } else { | 169 | } else { |
| 163 | ExecuteInstructions( | 170 | while (true) { |
| 164 | std::max(std::size_t(system.CoreTiming().GetDowncount()), std::size_t{0})); | 171 | if (interrupt_handlers[core_index].IsInterrupted()) { |
| 172 | return; | ||
| 173 | } | ||
| 174 | ExecuteInstructions(10); | ||
| 175 | } | ||
| 165 | } | 176 | } |
| 166 | } | 177 | } |
| 167 | 178 | ||
| @@ -183,8 +194,6 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { | |||
| 183 | UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); | 194 | UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); |
| 184 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); | 195 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); |
| 185 | CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); | 196 | CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); |
| 186 | |||
| 187 | system.CoreTiming().AddTicks(num_instructions); | ||
| 188 | if (GDBStub::IsServerEnabled()) { | 197 | if (GDBStub::IsServerEnabled()) { |
| 189 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { | 198 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { |
| 190 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 199 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 725c65085..810aff311 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h | |||
| @@ -20,7 +20,8 @@ public: | |||
| 20 | AArch64, // 64-bit ARM | 20 | AArch64, // 64-bit ARM |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | explicit ARM_Unicorn(System& system, Arch architecture); | 23 | explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 24 | Arch architecture, std::size_t core_index); | ||
| 24 | ~ARM_Unicorn() override; | 25 | ~ARM_Unicorn() override; |
| 25 | 26 | ||
| 26 | void SetPC(u64 pc) override; | 27 | void SetPC(u64 pc) override; |
| @@ -35,6 +36,7 @@ public: | |||
| 35 | void SetTlsAddress(VAddr address) override; | 36 | void SetTlsAddress(VAddr address) override; |
| 36 | void SetTPIDR_EL0(u64 value) override; | 37 | void SetTPIDR_EL0(u64 value) override; |
| 37 | u64 GetTPIDR_EL0() const override; | 38 | u64 GetTPIDR_EL0() const override; |
| 39 | void ChangeProcessorID(std::size_t new_core_id) override; | ||
| 38 | void PrepareReschedule() override; | 40 | void PrepareReschedule() override; |
| 39 | void ClearExclusiveState() override; | 41 | void ClearExclusiveState() override; |
| 40 | void ExecuteInstructions(std::size_t num_instructions); | 42 | void ExecuteInstructions(std::size_t num_instructions); |
| @@ -55,6 +57,7 @@ private: | |||
| 55 | uc_engine* uc{}; | 57 | uc_engine* uc{}; |
| 56 | GDBStub::BreakpointAddress last_bkpt{}; | 58 | GDBStub::BreakpointAddress last_bkpt{}; |
| 57 | bool last_bkpt_hit = false; | 59 | bool last_bkpt_hit = false; |
| 60 | std::size_t core_index; | ||
| 58 | }; | 61 | }; |
| 59 | 62 | ||
| 60 | } // namespace Core | 63 | } // namespace Core |
diff --git a/src/core/core.cpp b/src/core/core.cpp index f9f8a3000..69a1aa0a5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -8,10 +8,10 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/microprofile.h" | ||
| 11 | #include "common/string_util.h" | 12 | #include "common/string_util.h" |
| 12 | #include "core/arm/exclusive_monitor.h" | 13 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/core_manager.h" | ||
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/cpu_manager.h" | 16 | #include "core/cpu_manager.h" |
| 17 | #include "core/device_memory.h" | 17 | #include "core/device_memory.h" |
| @@ -51,6 +51,11 @@ | |||
| 51 | #include "video_core/renderer_base.h" | 51 | #include "video_core/renderer_base.h" |
| 52 | #include "video_core/video_core.h" | 52 | #include "video_core/video_core.h" |
| 53 | 53 | ||
| 54 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU0, "ARM JIT", "Dynarmic CPU 0", MP_RGB(255, 64, 64)); | ||
| 55 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU1, "ARM JIT", "Dynarmic CPU 1", MP_RGB(255, 64, 64)); | ||
| 56 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU2, "ARM JIT", "Dynarmic CPU 2", MP_RGB(255, 64, 64)); | ||
| 57 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU3, "ARM JIT", "Dynarmic CPU 3", MP_RGB(255, 64, 64)); | ||
| 58 | |||
| 54 | namespace Core { | 59 | namespace Core { |
| 55 | 60 | ||
| 56 | namespace { | 61 | namespace { |
| @@ -117,23 +122,22 @@ struct System::Impl { | |||
| 117 | : kernel{system}, fs_controller{system}, memory{system}, | 122 | : kernel{system}, fs_controller{system}, memory{system}, |
| 118 | cpu_manager{system}, reporter{system}, applet_manager{system} {} | 123 | cpu_manager{system}, reporter{system}, applet_manager{system} {} |
| 119 | 124 | ||
| 120 | CoreManager& CurrentCoreManager() { | 125 | ResultStatus Run() { |
| 121 | return cpu_manager.GetCurrentCoreManager(); | 126 | status = ResultStatus::Success; |
| 122 | } | ||
| 123 | 127 | ||
| 124 | Kernel::PhysicalCore& CurrentPhysicalCore() { | 128 | kernel.Suspend(false); |
| 125 | const auto index = cpu_manager.GetActiveCoreIndex(); | 129 | core_timing.SyncPause(false); |
| 126 | return kernel.PhysicalCore(index); | 130 | cpu_manager.Pause(false); |
| 127 | } | ||
| 128 | 131 | ||
| 129 | Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { | 132 | return status; |
| 130 | return kernel.PhysicalCore(index); | ||
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | ResultStatus RunLoop(bool tight_loop) { | 135 | ResultStatus Pause() { |
| 134 | status = ResultStatus::Success; | 136 | status = ResultStatus::Success; |
| 135 | 137 | ||
| 136 | cpu_manager.RunLoop(tight_loop); | 138 | core_timing.SyncPause(true); |
| 139 | kernel.Suspend(true); | ||
| 140 | cpu_manager.Pause(true); | ||
| 137 | 141 | ||
| 138 | return status; | 142 | return status; |
| 139 | } | 143 | } |
| @@ -143,14 +147,22 @@ struct System::Impl { | |||
| 143 | 147 | ||
| 144 | device_memory = std::make_unique<Core::DeviceMemory>(system); | 148 | device_memory = std::make_unique<Core::DeviceMemory>(system); |
| 145 | 149 | ||
| 146 | core_timing.Initialize(); | 150 | is_multicore = Settings::values.use_multi_core.GetValue(); |
| 151 | is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); | ||
| 152 | |||
| 153 | kernel.SetMulticore(is_multicore); | ||
| 154 | cpu_manager.SetMulticore(is_multicore); | ||
| 155 | cpu_manager.SetAsyncGpu(is_async_gpu); | ||
| 156 | core_timing.SetMulticore(is_multicore); | ||
| 157 | |||
| 158 | core_timing.Initialize([&system]() { system.RegisterHostThread(); }); | ||
| 147 | kernel.Initialize(); | 159 | kernel.Initialize(); |
| 148 | cpu_manager.Initialize(); | 160 | cpu_manager.Initialize(); |
| 149 | 161 | ||
| 150 | const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( | 162 | const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( |
| 151 | std::chrono::system_clock::now().time_since_epoch()); | 163 | std::chrono::system_clock::now().time_since_epoch()); |
| 152 | Settings::values.custom_rtc_differential = | 164 | Settings::values.custom_rtc_differential = |
| 153 | Settings::values.custom_rtc.value_or(current_time) - current_time; | 165 | Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time; |
| 154 | 166 | ||
| 155 | // Create a default fs if one doesn't already exist. | 167 | // Create a default fs if one doesn't already exist. |
| 156 | if (virtual_filesystem == nullptr) | 168 | if (virtual_filesystem == nullptr) |
| @@ -180,6 +192,11 @@ struct System::Impl { | |||
| 180 | is_powered_on = true; | 192 | is_powered_on = true; |
| 181 | exit_lock = false; | 193 | exit_lock = false; |
| 182 | 194 | ||
| 195 | microprofile_dynarmic[0] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU0); | ||
| 196 | microprofile_dynarmic[1] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU1); | ||
| 197 | microprofile_dynarmic[2] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU2); | ||
| 198 | microprofile_dynarmic[3] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU3); | ||
| 199 | |||
| 183 | LOG_DEBUG(Core, "Initialized OK"); | 200 | LOG_DEBUG(Core, "Initialized OK"); |
| 184 | 201 | ||
| 185 | return ResultStatus::Success; | 202 | return ResultStatus::Success; |
| @@ -277,8 +294,6 @@ struct System::Impl { | |||
| 277 | service_manager.reset(); | 294 | service_manager.reset(); |
| 278 | cheat_engine.reset(); | 295 | cheat_engine.reset(); |
| 279 | telemetry_session.reset(); | 296 | telemetry_session.reset(); |
| 280 | perf_stats.reset(); | ||
| 281 | gpu_core.reset(); | ||
| 282 | device_memory.reset(); | 297 | device_memory.reset(); |
| 283 | 298 | ||
| 284 | // Close all CPU/threading state | 299 | // Close all CPU/threading state |
| @@ -290,6 +305,8 @@ struct System::Impl { | |||
| 290 | 305 | ||
| 291 | // Close app loader | 306 | // Close app loader |
| 292 | app_loader.reset(); | 307 | app_loader.reset(); |
| 308 | gpu_core.reset(); | ||
| 309 | perf_stats.reset(); | ||
| 293 | 310 | ||
| 294 | // Clear all applets | 311 | // Clear all applets |
| 295 | applet_manager.ClearAll(); | 312 | applet_manager.ClearAll(); |
| @@ -382,25 +399,35 @@ struct System::Impl { | |||
| 382 | 399 | ||
| 383 | std::unique_ptr<Core::PerfStats> perf_stats; | 400 | std::unique_ptr<Core::PerfStats> perf_stats; |
| 384 | Core::FrameLimiter frame_limiter; | 401 | Core::FrameLimiter frame_limiter; |
| 402 | |||
| 403 | bool is_multicore{}; | ||
| 404 | bool is_async_gpu{}; | ||
| 405 | |||
| 406 | std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; | ||
| 407 | std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; | ||
| 385 | }; | 408 | }; |
| 386 | 409 | ||
| 387 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 410 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| 388 | System::~System() = default; | 411 | System::~System() = default; |
| 389 | 412 | ||
| 390 | CoreManager& System::CurrentCoreManager() { | 413 | CpuManager& System::GetCpuManager() { |
| 391 | return impl->CurrentCoreManager(); | 414 | return impl->cpu_manager; |
| 415 | } | ||
| 416 | |||
| 417 | const CpuManager& System::GetCpuManager() const { | ||
| 418 | return impl->cpu_manager; | ||
| 392 | } | 419 | } |
| 393 | 420 | ||
| 394 | const CoreManager& System::CurrentCoreManager() const { | 421 | System::ResultStatus System::Run() { |
| 395 | return impl->CurrentCoreManager(); | 422 | return impl->Run(); |
| 396 | } | 423 | } |
| 397 | 424 | ||
| 398 | System::ResultStatus System::RunLoop(bool tight_loop) { | 425 | System::ResultStatus System::Pause() { |
| 399 | return impl->RunLoop(tight_loop); | 426 | return impl->Pause(); |
| 400 | } | 427 | } |
| 401 | 428 | ||
| 402 | System::ResultStatus System::SingleStep() { | 429 | System::ResultStatus System::SingleStep() { |
| 403 | return RunLoop(false); | 430 | return ResultStatus::Success; |
| 404 | } | 431 | } |
| 405 | 432 | ||
| 406 | void System::InvalidateCpuInstructionCaches() { | 433 | void System::InvalidateCpuInstructionCaches() { |
| @@ -416,7 +443,7 @@ bool System::IsPoweredOn() const { | |||
| 416 | } | 443 | } |
| 417 | 444 | ||
| 418 | void System::PrepareReschedule() { | 445 | void System::PrepareReschedule() { |
| 419 | impl->CurrentPhysicalCore().Stop(); | 446 | // Deprecated, does nothing, kept for backward compatibility. |
| 420 | } | 447 | } |
| 421 | 448 | ||
| 422 | void System::PrepareReschedule(const u32 core_index) { | 449 | void System::PrepareReschedule(const u32 core_index) { |
| @@ -436,31 +463,41 @@ const TelemetrySession& System::TelemetrySession() const { | |||
| 436 | } | 463 | } |
| 437 | 464 | ||
| 438 | ARM_Interface& System::CurrentArmInterface() { | 465 | ARM_Interface& System::CurrentArmInterface() { |
| 439 | return impl->CurrentPhysicalCore().ArmInterface(); | 466 | return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface(); |
| 440 | } | 467 | } |
| 441 | 468 | ||
| 442 | const ARM_Interface& System::CurrentArmInterface() const { | 469 | const ARM_Interface& System::CurrentArmInterface() const { |
| 443 | return impl->CurrentPhysicalCore().ArmInterface(); | 470 | return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface(); |
| 444 | } | 471 | } |
| 445 | 472 | ||
| 446 | std::size_t System::CurrentCoreIndex() const { | 473 | std::size_t System::CurrentCoreIndex() const { |
| 447 | return impl->cpu_manager.GetActiveCoreIndex(); | 474 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); |
| 475 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 476 | return core; | ||
| 448 | } | 477 | } |
| 449 | 478 | ||
| 450 | Kernel::Scheduler& System::CurrentScheduler() { | 479 | Kernel::Scheduler& System::CurrentScheduler() { |
| 451 | return impl->CurrentPhysicalCore().Scheduler(); | 480 | return impl->kernel.CurrentScheduler(); |
| 452 | } | 481 | } |
| 453 | 482 | ||
| 454 | const Kernel::Scheduler& System::CurrentScheduler() const { | 483 | const Kernel::Scheduler& System::CurrentScheduler() const { |
| 455 | return impl->CurrentPhysicalCore().Scheduler(); | 484 | return impl->kernel.CurrentScheduler(); |
| 485 | } | ||
| 486 | |||
| 487 | Kernel::PhysicalCore& System::CurrentPhysicalCore() { | ||
| 488 | return impl->kernel.CurrentPhysicalCore(); | ||
| 489 | } | ||
| 490 | |||
| 491 | const Kernel::PhysicalCore& System::CurrentPhysicalCore() const { | ||
| 492 | return impl->kernel.CurrentPhysicalCore(); | ||
| 456 | } | 493 | } |
| 457 | 494 | ||
| 458 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { | 495 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { |
| 459 | return impl->GetPhysicalCore(core_index).Scheduler(); | 496 | return impl->kernel.Scheduler(core_index); |
| 460 | } | 497 | } |
| 461 | 498 | ||
| 462 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { | 499 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { |
| 463 | return impl->GetPhysicalCore(core_index).Scheduler(); | 500 | return impl->kernel.Scheduler(core_index); |
| 464 | } | 501 | } |
| 465 | 502 | ||
| 466 | /// Gets the global scheduler | 503 | /// Gets the global scheduler |
| @@ -490,20 +527,15 @@ const Kernel::Process* System::CurrentProcess() const { | |||
| 490 | } | 527 | } |
| 491 | 528 | ||
| 492 | ARM_Interface& System::ArmInterface(std::size_t core_index) { | 529 | ARM_Interface& System::ArmInterface(std::size_t core_index) { |
| 493 | return impl->GetPhysicalCore(core_index).ArmInterface(); | 530 | auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread(); |
| 531 | ASSERT(thread && !thread->IsHLEThread()); | ||
| 532 | return thread->ArmInterface(); | ||
| 494 | } | 533 | } |
| 495 | 534 | ||
| 496 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { | 535 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { |
| 497 | return impl->GetPhysicalCore(core_index).ArmInterface(); | 536 | auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread(); |
| 498 | } | 537 | ASSERT(thread && !thread->IsHLEThread()); |
| 499 | 538 | return thread->ArmInterface(); | |
| 500 | CoreManager& System::GetCoreManager(std::size_t core_index) { | ||
| 501 | return impl->cpu_manager.GetCoreManager(core_index); | ||
| 502 | } | ||
| 503 | |||
| 504 | const CoreManager& System::GetCoreManager(std::size_t core_index) const { | ||
| 505 | ASSERT(core_index < NUM_CPU_CORES); | ||
| 506 | return impl->cpu_manager.GetCoreManager(core_index); | ||
| 507 | } | 539 | } |
| 508 | 540 | ||
| 509 | ExclusiveMonitor& System::Monitor() { | 541 | ExclusiveMonitor& System::Monitor() { |
| @@ -722,4 +754,18 @@ void System::RegisterHostThread() { | |||
| 722 | impl->kernel.RegisterHostThread(); | 754 | impl->kernel.RegisterHostThread(); |
| 723 | } | 755 | } |
| 724 | 756 | ||
| 757 | void System::EnterDynarmicProfile() { | ||
| 758 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 759 | impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_dynarmic[core]); | ||
| 760 | } | ||
| 761 | |||
| 762 | void System::ExitDynarmicProfile() { | ||
| 763 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 764 | MicroProfileLeave(impl->microprofile_dynarmic[core], impl->dynarmic_ticks[core]); | ||
| 765 | } | ||
| 766 | |||
| 767 | bool System::IsMulticore() const { | ||
| 768 | return impl->is_multicore; | ||
| 769 | } | ||
| 770 | |||
| 725 | } // namespace Core | 771 | } // namespace Core |
diff --git a/src/core/core.h b/src/core/core.h index acc53d6a1..5c6cfbffe 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -27,6 +27,7 @@ class VfsFilesystem; | |||
| 27 | namespace Kernel { | 27 | namespace Kernel { |
| 28 | class GlobalScheduler; | 28 | class GlobalScheduler; |
| 29 | class KernelCore; | 29 | class KernelCore; |
| 30 | class PhysicalCore; | ||
| 30 | class Process; | 31 | class Process; |
| 31 | class Scheduler; | 32 | class Scheduler; |
| 32 | } // namespace Kernel | 33 | } // namespace Kernel |
| @@ -90,7 +91,7 @@ class InterruptManager; | |||
| 90 | namespace Core { | 91 | namespace Core { |
| 91 | 92 | ||
| 92 | class ARM_Interface; | 93 | class ARM_Interface; |
| 93 | class CoreManager; | 94 | class CpuManager; |
| 94 | class DeviceMemory; | 95 | class DeviceMemory; |
| 95 | class ExclusiveMonitor; | 96 | class ExclusiveMonitor; |
| 96 | class FrameLimiter; | 97 | class FrameLimiter; |
| @@ -136,16 +137,16 @@ public: | |||
| 136 | }; | 137 | }; |
| 137 | 138 | ||
| 138 | /** | 139 | /** |
| 139 | * Run the core CPU loop | 140 | * Run the OS and Application |
| 140 | * This function runs the core for the specified number of CPU instructions before trying to | 141 | * This function will start emulation and run the relevant devices |
| 141 | * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU | 142 | */ |
| 142 | * is not required to do a full dispatch with each instruction. NOTE: the number of instructions | 143 | ResultStatus Run(); |
| 143 | * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware | 144 | |
| 144 | * update is requested (e.g. on a thread switch). | 145 | /** |
| 145 | * @param tight_loop If false, the CPU single-steps. | 146 | * Pause the OS and Application |
| 146 | * @return Result status, indicating whether or not the operation succeeded. | 147 | * This function will pause emulation and stop the relevant devices |
| 147 | */ | 148 | */ |
| 148 | ResultStatus RunLoop(bool tight_loop = true); | 149 | ResultStatus Pause(); |
| 149 | 150 | ||
| 150 | /** | 151 | /** |
| 151 | * Step the CPU one instruction | 152 | * Step the CPU one instruction |
| @@ -209,17 +210,21 @@ public: | |||
| 209 | /// Gets the scheduler for the CPU core that is currently running | 210 | /// Gets the scheduler for the CPU core that is currently running |
| 210 | const Kernel::Scheduler& CurrentScheduler() const; | 211 | const Kernel::Scheduler& CurrentScheduler() const; |
| 211 | 212 | ||
| 213 | /// Gets the physical core for the CPU core that is currently running | ||
| 214 | Kernel::PhysicalCore& CurrentPhysicalCore(); | ||
| 215 | |||
| 216 | /// Gets the physical core for the CPU core that is currently running | ||
| 217 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; | ||
| 218 | |||
| 212 | /// Gets a reference to an ARM interface for the CPU core with the specified index | 219 | /// Gets a reference to an ARM interface for the CPU core with the specified index |
| 213 | ARM_Interface& ArmInterface(std::size_t core_index); | 220 | ARM_Interface& ArmInterface(std::size_t core_index); |
| 214 | 221 | ||
| 215 | /// Gets a const reference to an ARM interface from the CPU core with the specified index | 222 | /// Gets a const reference to an ARM interface from the CPU core with the specified index |
| 216 | const ARM_Interface& ArmInterface(std::size_t core_index) const; | 223 | const ARM_Interface& ArmInterface(std::size_t core_index) const; |
| 217 | 224 | ||
| 218 | /// Gets a CPU interface to the CPU core with the specified index | 225 | CpuManager& GetCpuManager(); |
| 219 | CoreManager& GetCoreManager(std::size_t core_index); | ||
| 220 | 226 | ||
| 221 | /// Gets a CPU interface to the CPU core with the specified index | 227 | const CpuManager& GetCpuManager() const; |
| 222 | const CoreManager& GetCoreManager(std::size_t core_index) const; | ||
| 223 | 228 | ||
| 224 | /// Gets a reference to the exclusive monitor | 229 | /// Gets a reference to the exclusive monitor |
| 225 | ExclusiveMonitor& Monitor(); | 230 | ExclusiveMonitor& Monitor(); |
| @@ -370,14 +375,17 @@ public: | |||
| 370 | /// Register a host thread as an auxiliary thread. | 375 | /// Register a host thread as an auxiliary thread. |
| 371 | void RegisterHostThread(); | 376 | void RegisterHostThread(); |
| 372 | 377 | ||
| 373 | private: | 378 | /// Enter Dynarmic Microprofile |
| 374 | System(); | 379 | void EnterDynarmicProfile(); |
| 380 | |||
| 381 | /// Exit Dynarmic Microprofile | ||
| 382 | void ExitDynarmicProfile(); | ||
| 375 | 383 | ||
| 376 | /// Returns the currently running CPU core | 384 | /// Tells if system is running on multicore. |
| 377 | CoreManager& CurrentCoreManager(); | 385 | bool IsMulticore() const; |
| 378 | 386 | ||
| 379 | /// Returns the currently running CPU core | 387 | private: |
| 380 | const CoreManager& CurrentCoreManager() const; | 388 | System(); |
| 381 | 389 | ||
| 382 | /** | 390 | /** |
| 383 | * Initialize the emulated system. | 391 | * Initialize the emulated system. |
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp deleted file mode 100644 index b6b797c80..000000000 --- a/src/core/core_manager.cpp +++ /dev/null | |||
| @@ -1,67 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <condition_variable> | ||
| 6 | #include <mutex> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/arm/exclusive_monitor.h" | ||
| 10 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/core_manager.h" | ||
| 13 | #include "core/core_timing.h" | ||
| 14 | #include "core/hle/kernel/kernel.h" | ||
| 15 | #include "core/hle/kernel/physical_core.h" | ||
| 16 | #include "core/hle/kernel/scheduler.h" | ||
| 17 | #include "core/hle/kernel/thread.h" | ||
| 18 | #include "core/hle/lock.h" | ||
| 19 | #include "core/settings.h" | ||
| 20 | |||
| 21 | namespace Core { | ||
| 22 | |||
| 23 | CoreManager::CoreManager(System& system, std::size_t core_index) | ||
| 24 | : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore( | ||
| 25 | core_index)}, | ||
| 26 | core_timing{system.CoreTiming()}, core_index{core_index} {} | ||
| 27 | |||
| 28 | CoreManager::~CoreManager() = default; | ||
| 29 | |||
| 30 | void CoreManager::RunLoop(bool tight_loop) { | ||
| 31 | Reschedule(); | ||
| 32 | |||
| 33 | // If we don't have a currently active thread then don't execute instructions, | ||
| 34 | // instead advance to the next event and try to yield to the next thread | ||
| 35 | if (Kernel::GetCurrentThread() == nullptr) { | ||
| 36 | LOG_TRACE(Core, "Core-{} idling", core_index); | ||
| 37 | core_timing.Idle(); | ||
| 38 | } else { | ||
| 39 | if (tight_loop) { | ||
| 40 | physical_core.Run(); | ||
| 41 | } else { | ||
| 42 | physical_core.Step(); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | core_timing.Advance(); | ||
| 46 | |||
| 47 | Reschedule(); | ||
| 48 | } | ||
| 49 | |||
| 50 | void CoreManager::SingleStep() { | ||
| 51 | return RunLoop(false); | ||
| 52 | } | ||
| 53 | |||
| 54 | void CoreManager::PrepareReschedule() { | ||
| 55 | physical_core.Stop(); | ||
| 56 | } | ||
| 57 | |||
| 58 | void CoreManager::Reschedule() { | ||
| 59 | // Lock the global kernel mutex when we manipulate the HLE state | ||
| 60 | std::lock_guard lock(HLE::g_hle_lock); | ||
| 61 | |||
| 62 | global_scheduler.SelectThread(core_index); | ||
| 63 | |||
| 64 | physical_core.Scheduler().TryDoContextSwitch(); | ||
| 65 | } | ||
| 66 | |||
| 67 | } // namespace Core | ||
diff --git a/src/core/core_manager.h b/src/core/core_manager.h deleted file mode 100644 index d525de00a..000000000 --- a/src/core/core_manager.h +++ /dev/null | |||
| @@ -1,63 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <cstddef> | ||
| 9 | #include <memory> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | class GlobalScheduler; | ||
| 14 | class PhysicalCore; | ||
| 15 | } // namespace Kernel | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core::Timing { | ||
| 22 | class CoreTiming; | ||
| 23 | } | ||
| 24 | |||
| 25 | namespace Core::Memory { | ||
| 26 | class Memory; | ||
| 27 | } | ||
| 28 | |||
| 29 | namespace Core { | ||
| 30 | |||
| 31 | constexpr unsigned NUM_CPU_CORES{4}; | ||
| 32 | |||
| 33 | class CoreManager { | ||
| 34 | public: | ||
| 35 | CoreManager(System& system, std::size_t core_index); | ||
| 36 | ~CoreManager(); | ||
| 37 | |||
| 38 | void RunLoop(bool tight_loop = true); | ||
| 39 | |||
| 40 | void SingleStep(); | ||
| 41 | |||
| 42 | void PrepareReschedule(); | ||
| 43 | |||
| 44 | bool IsMainCore() const { | ||
| 45 | return core_index == 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | std::size_t CoreIndex() const { | ||
| 49 | return core_index; | ||
| 50 | } | ||
| 51 | |||
| 52 | private: | ||
| 53 | void Reschedule(); | ||
| 54 | |||
| 55 | Kernel::GlobalScheduler& global_scheduler; | ||
| 56 | Kernel::PhysicalCore& physical_core; | ||
| 57 | Timing::CoreTiming& core_timing; | ||
| 58 | |||
| 59 | std::atomic<bool> reschedule_pending = false; | ||
| 60 | std::size_t core_index; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace Core | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 46d4178c4..a63e60461 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -1,29 +1,27 @@ | |||
| 1 | // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | |||
| 7 | #include <algorithm> | 5 | #include <algorithm> |
| 8 | #include <mutex> | 6 | #include <mutex> |
| 9 | #include <string> | 7 | #include <string> |
| 10 | #include <tuple> | 8 | #include <tuple> |
| 11 | 9 | ||
| 12 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 13 | #include "common/thread.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "core/core_timing.h" | ||
| 14 | #include "core/core_timing_util.h" | 13 | #include "core/core_timing_util.h" |
| 15 | #include "core/hardware_properties.h" | ||
| 16 | 14 | ||
| 17 | namespace Core::Timing { | 15 | namespace Core::Timing { |
| 18 | 16 | ||
| 19 | constexpr int MAX_SLICE_LENGTH = 10000; | 17 | constexpr u64 MAX_SLICE_LENGTH = 4000; |
| 20 | 18 | ||
| 21 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { | 19 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { |
| 22 | return std::make_shared<EventType>(std::move(callback), std::move(name)); | 20 | return std::make_shared<EventType>(std::move(callback), std::move(name)); |
| 23 | } | 21 | } |
| 24 | 22 | ||
| 25 | struct CoreTiming::Event { | 23 | struct CoreTiming::Event { |
| 26 | s64 time; | 24 | u64 time; |
| 27 | u64 fifo_order; | 25 | u64 fifo_order; |
| 28 | u64 userdata; | 26 | u64 userdata; |
| 29 | std::weak_ptr<EventType> type; | 27 | std::weak_ptr<EventType> type; |
| @@ -39,51 +37,90 @@ struct CoreTiming::Event { | |||
| 39 | } | 37 | } |
| 40 | }; | 38 | }; |
| 41 | 39 | ||
| 42 | CoreTiming::CoreTiming() = default; | 40 | CoreTiming::CoreTiming() { |
| 43 | CoreTiming::~CoreTiming() = default; | 41 | clock = |
| 42 | Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); | ||
| 43 | } | ||
| 44 | 44 | ||
| 45 | void CoreTiming::Initialize() { | 45 | CoreTiming::~CoreTiming() = default; |
| 46 | downcounts.fill(MAX_SLICE_LENGTH); | ||
| 47 | time_slice.fill(MAX_SLICE_LENGTH); | ||
| 48 | slice_length = MAX_SLICE_LENGTH; | ||
| 49 | global_timer = 0; | ||
| 50 | idled_cycles = 0; | ||
| 51 | current_context = 0; | ||
| 52 | 46 | ||
| 53 | // The time between CoreTiming being initialized and the first call to Advance() is considered | 47 | void CoreTiming::ThreadEntry(CoreTiming& instance) { |
| 54 | // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before | 48 | constexpr char name[] = "yuzu:HostTiming"; |
| 55 | // executing the first cycle of each slice to prepare the slice length and downcount for | 49 | MicroProfileOnThreadCreate(name); |
| 56 | // that slice. | 50 | Common::SetCurrentThreadName(name); |
| 57 | is_global_timer_sane = true; | 51 | Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); |
| 52 | instance.on_thread_init(); | ||
| 53 | instance.ThreadLoop(); | ||
| 54 | } | ||
| 58 | 55 | ||
| 56 | void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) { | ||
| 57 | on_thread_init = std::move(on_thread_init_); | ||
| 59 | event_fifo_id = 0; | 58 | event_fifo_id = 0; |
| 60 | 59 | shutting_down = false; | |
| 60 | ticks = 0; | ||
| 61 | const auto empty_timed_callback = [](u64, s64) {}; | 61 | const auto empty_timed_callback = [](u64, s64) {}; |
| 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); | 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); |
| 63 | if (is_multicore) { | ||
| 64 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); | ||
| 65 | } | ||
| 63 | } | 66 | } |
| 64 | 67 | ||
| 65 | void CoreTiming::Shutdown() { | 68 | void CoreTiming::Shutdown() { |
| 69 | paused = true; | ||
| 70 | shutting_down = true; | ||
| 71 | pause_event.Set(); | ||
| 72 | event.Set(); | ||
| 73 | if (timer_thread) { | ||
| 74 | timer_thread->join(); | ||
| 75 | } | ||
| 66 | ClearPendingEvents(); | 76 | ClearPendingEvents(); |
| 77 | timer_thread.reset(); | ||
| 78 | has_started = false; | ||
| 67 | } | 79 | } |
| 68 | 80 | ||
| 69 | void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | 81 | void CoreTiming::Pause(bool is_paused) { |
| 70 | u64 userdata) { | 82 | paused = is_paused; |
| 71 | std::lock_guard guard{inner_mutex}; | 83 | pause_event.Set(); |
| 72 | const s64 timeout = GetTicks() + cycles_into_future; | 84 | } |
| 73 | 85 | ||
| 74 | // If this event needs to be scheduled before the next advance(), force one early | 86 | void CoreTiming::SyncPause(bool is_paused) { |
| 75 | if (!is_global_timer_sane) { | 87 | if (is_paused == paused && paused_set == paused) { |
| 76 | ForceExceptionCheck(cycles_into_future); | 88 | return; |
| 89 | } | ||
| 90 | Pause(is_paused); | ||
| 91 | if (timer_thread) { | ||
| 92 | if (!is_paused) { | ||
| 93 | pause_event.Set(); | ||
| 94 | } | ||
| 95 | event.Set(); | ||
| 96 | while (paused_set != is_paused) | ||
| 97 | ; | ||
| 77 | } | 98 | } |
| 99 | } | ||
| 78 | 100 | ||
| 79 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | 101 | bool CoreTiming::IsRunning() const { |
| 102 | return !paused_set; | ||
| 103 | } | ||
| 80 | 104 | ||
| 81 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 105 | bool CoreTiming::HasPendingEvents() const { |
| 106 | return !(wait_set && event_queue.empty()); | ||
| 82 | } | 107 | } |
| 83 | 108 | ||
| 84 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | 109 | void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, |
| 85 | std::lock_guard guard{inner_mutex}; | 110 | u64 userdata) { |
| 111 | { | ||
| 112 | std::scoped_lock scope{basic_lock}; | ||
| 113 | const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future); | ||
| 114 | |||
| 115 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | ||
| 86 | 116 | ||
| 117 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 118 | } | ||
| 119 | event.Set(); | ||
| 120 | } | ||
| 121 | |||
| 122 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | ||
| 123 | std::scoped_lock scope{basic_lock}; | ||
| 87 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 124 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 88 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; | 125 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; |
| 89 | }); | 126 | }); |
| @@ -95,21 +132,39 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u | |||
| 95 | } | 132 | } |
| 96 | } | 133 | } |
| 97 | 134 | ||
| 98 | u64 CoreTiming::GetTicks() const { | 135 | void CoreTiming::AddTicks(u64 ticks) { |
| 99 | u64 ticks = static_cast<u64>(global_timer); | 136 | this->ticks += ticks; |
| 100 | if (!is_global_timer_sane) { | 137 | downcount -= ticks; |
| 101 | ticks += accumulated_ticks; | 138 | } |
| 139 | |||
| 140 | void CoreTiming::Idle() { | ||
| 141 | if (!event_queue.empty()) { | ||
| 142 | const u64 next_event_time = event_queue.front().time; | ||
| 143 | const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; | ||
| 144 | if (next_ticks > ticks) { | ||
| 145 | ticks = next_ticks; | ||
| 146 | } | ||
| 147 | return; | ||
| 102 | } | 148 | } |
| 103 | return ticks; | 149 | ticks += 1000U; |
| 104 | } | 150 | } |
| 105 | 151 | ||
| 106 | u64 CoreTiming::GetIdleTicks() const { | 152 | void CoreTiming::ResetTicks() { |
| 107 | return static_cast<u64>(idled_cycles); | 153 | downcount = MAX_SLICE_LENGTH; |
| 108 | } | 154 | } |
| 109 | 155 | ||
| 110 | void CoreTiming::AddTicks(u64 ticks) { | 156 | u64 CoreTiming::GetCPUTicks() const { |
| 111 | accumulated_ticks += ticks; | 157 | if (is_multicore) { |
| 112 | downcounts[current_context] -= static_cast<s64>(ticks); | 158 | return clock->GetCPUCycles(); |
| 159 | } | ||
| 160 | return ticks; | ||
| 161 | } | ||
| 162 | |||
| 163 | u64 CoreTiming::GetClockTicks() const { | ||
| 164 | if (is_multicore) { | ||
| 165 | return clock->GetClockCycles(); | ||
| 166 | } | ||
| 167 | return CpuCyclesToClockCycles(ticks); | ||
| 113 | } | 168 | } |
| 114 | 169 | ||
| 115 | void CoreTiming::ClearPendingEvents() { | 170 | void CoreTiming::ClearPendingEvents() { |
| @@ -117,7 +172,7 @@ void CoreTiming::ClearPendingEvents() { | |||
| 117 | } | 172 | } |
| 118 | 173 | ||
| 119 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | 174 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { |
| 120 | std::lock_guard guard{inner_mutex}; | 175 | std::scoped_lock lock{basic_lock}; |
| 121 | 176 | ||
| 122 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 177 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 123 | return e.type.lock().get() == event_type.get(); | 178 | return e.type.lock().get() == event_type.get(); |
| @@ -130,97 +185,68 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | |||
| 130 | } | 185 | } |
| 131 | } | 186 | } |
| 132 | 187 | ||
| 133 | void CoreTiming::ForceExceptionCheck(s64 cycles) { | 188 | std::optional<s64> CoreTiming::Advance() { |
| 134 | cycles = std::max<s64>(0, cycles); | 189 | std::scoped_lock lock{advance_lock, basic_lock}; |
| 135 | if (downcounts[current_context] <= cycles) { | 190 | global_timer = GetGlobalTimeNs().count(); |
| 136 | return; | ||
| 137 | } | ||
| 138 | |||
| 139 | // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int | ||
| 140 | // here. Account for cycles already executed by adjusting the g.slice_length | ||
| 141 | downcounts[current_context] = static_cast<int>(cycles); | ||
| 142 | } | ||
| 143 | |||
| 144 | std::optional<u64> CoreTiming::NextAvailableCore(const s64 needed_ticks) const { | ||
| 145 | const u64 original_context = current_context; | ||
| 146 | u64 next_context = (original_context + 1) % num_cpu_cores; | ||
| 147 | while (next_context != original_context) { | ||
| 148 | if (time_slice[next_context] >= needed_ticks) { | ||
| 149 | return {next_context}; | ||
| 150 | } else if (time_slice[next_context] >= 0) { | ||
| 151 | return std::nullopt; | ||
| 152 | } | ||
| 153 | next_context = (next_context + 1) % num_cpu_cores; | ||
| 154 | } | ||
| 155 | return std::nullopt; | ||
| 156 | } | ||
| 157 | |||
| 158 | void CoreTiming::Advance() { | ||
| 159 | std::unique_lock<std::mutex> guard(inner_mutex); | ||
| 160 | |||
| 161 | const u64 cycles_executed = accumulated_ticks; | ||
| 162 | time_slice[current_context] = std::max<s64>(0, time_slice[current_context] - accumulated_ticks); | ||
| 163 | global_timer += cycles_executed; | ||
| 164 | |||
| 165 | is_global_timer_sane = true; | ||
| 166 | 191 | ||
| 167 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { | 192 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { |
| 168 | Event evt = std::move(event_queue.front()); | 193 | Event evt = std::move(event_queue.front()); |
| 169 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 194 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 170 | event_queue.pop_back(); | 195 | event_queue.pop_back(); |
| 171 | inner_mutex.unlock(); | 196 | basic_lock.unlock(); |
| 172 | 197 | ||
| 173 | if (auto event_type{evt.type.lock()}) { | 198 | if (auto event_type{evt.type.lock()}) { |
| 174 | event_type->callback(evt.userdata, global_timer - evt.time); | 199 | event_type->callback(evt.userdata, global_timer - evt.time); |
| 175 | } | 200 | } |
| 176 | 201 | ||
| 177 | inner_mutex.lock(); | 202 | basic_lock.lock(); |
| 203 | global_timer = GetGlobalTimeNs().count(); | ||
| 178 | } | 204 | } |
| 179 | 205 | ||
| 180 | is_global_timer_sane = false; | ||
| 181 | |||
| 182 | // Still events left (scheduled in the future) | ||
| 183 | if (!event_queue.empty()) { | 206 | if (!event_queue.empty()) { |
| 184 | const s64 needed_ticks = | 207 | const s64 next_time = event_queue.front().time - global_timer; |
| 185 | std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); | 208 | return next_time; |
| 186 | const auto next_core = NextAvailableCore(needed_ticks); | 209 | } else { |
| 187 | if (next_core) { | 210 | return std::nullopt; |
| 188 | downcounts[*next_core] = needed_ticks; | ||
| 189 | } | ||
| 190 | } | 211 | } |
| 191 | |||
| 192 | accumulated_ticks = 0; | ||
| 193 | |||
| 194 | downcounts[current_context] = time_slice[current_context]; | ||
| 195 | } | 212 | } |
| 196 | 213 | ||
| 197 | void CoreTiming::ResetRun() { | 214 | void CoreTiming::ThreadLoop() { |
| 198 | downcounts.fill(MAX_SLICE_LENGTH); | 215 | has_started = true; |
| 199 | time_slice.fill(MAX_SLICE_LENGTH); | 216 | while (!shutting_down) { |
| 200 | current_context = 0; | 217 | while (!paused) { |
| 201 | // Still events left (scheduled in the future) | 218 | paused_set = false; |
| 202 | if (!event_queue.empty()) { | 219 | const auto next_time = Advance(); |
| 203 | const s64 needed_ticks = | 220 | if (next_time) { |
| 204 | std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); | 221 | if (*next_time > 0) { |
| 205 | downcounts[current_context] = needed_ticks; | 222 | std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); |
| 223 | event.WaitFor(next_time_ns); | ||
| 224 | } | ||
| 225 | } else { | ||
| 226 | wait_set = true; | ||
| 227 | event.Wait(); | ||
| 228 | } | ||
| 229 | wait_set = false; | ||
| 230 | } | ||
| 231 | paused_set = true; | ||
| 232 | clock->Pause(true); | ||
| 233 | pause_event.Wait(); | ||
| 234 | clock->Pause(false); | ||
| 206 | } | 235 | } |
| 207 | |||
| 208 | is_global_timer_sane = false; | ||
| 209 | accumulated_ticks = 0; | ||
| 210 | } | 236 | } |
| 211 | 237 | ||
| 212 | void CoreTiming::Idle() { | 238 | std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { |
| 213 | accumulated_ticks += downcounts[current_context]; | 239 | if (is_multicore) { |
| 214 | idled_cycles += downcounts[current_context]; | 240 | return clock->GetTimeNS(); |
| 215 | downcounts[current_context] = 0; | 241 | } |
| 242 | return CyclesToNs(ticks); | ||
| 216 | } | 243 | } |
| 217 | 244 | ||
| 218 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { | 245 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { |
| 219 | return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; | 246 | if (is_multicore) { |
| 220 | } | 247 | return clock->GetTimeUS(); |
| 221 | 248 | } | |
| 222 | s64 CoreTiming::GetDowncount() const { | 249 | return CyclesToUs(ticks); |
| 223 | return downcounts[current_context]; | ||
| 224 | } | 250 | } |
| 225 | 251 | ||
| 226 | } // namespace Core::Timing | 252 | } // namespace Core::Timing |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d50f4eb8a..72faaab64 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -1,19 +1,25 @@ | |||
| 1 | // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <chrono> | 8 | #include <chrono> |
| 8 | #include <functional> | 9 | #include <functional> |
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <mutex> | 11 | #include <mutex> |
| 11 | #include <optional> | 12 | #include <optional> |
| 12 | #include <string> | 13 | #include <string> |
| 14 | #include <thread> | ||
| 13 | #include <vector> | 15 | #include <vector> |
| 14 | 16 | ||
| 15 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "common/spin_lock.h" | ||
| 19 | #include "common/thread.h" | ||
| 16 | #include "common/threadsafe_queue.h" | 20 | #include "common/threadsafe_queue.h" |
| 21 | #include "common/wall_clock.h" | ||
| 22 | #include "core/hardware_properties.h" | ||
| 17 | 23 | ||
| 18 | namespace Core::Timing { | 24 | namespace Core::Timing { |
| 19 | 25 | ||
| @@ -56,16 +62,40 @@ public: | |||
| 56 | 62 | ||
| 57 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is | 63 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is |
| 58 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. | 64 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. |
| 59 | void Initialize(); | 65 | void Initialize(std::function<void(void)>&& on_thread_init_); |
| 60 | 66 | ||
| 61 | /// Tears down all timing related functionality. | 67 | /// Tears down all timing related functionality. |
| 62 | void Shutdown(); | 68 | void Shutdown(); |
| 63 | 69 | ||
| 64 | /// After the first Advance, the slice lengths and the downcount will be reduced whenever an | 70 | /// Sets if emulation is multicore or single core, must be set before Initialize |
| 65 | /// event is scheduled earlier than the current values. | 71 | void SetMulticore(bool is_multicore) { |
| 66 | /// | 72 | this->is_multicore = is_multicore; |
| 67 | /// Scheduling from a callback will not update the downcount until the Advance() completes. | 73 | } |
| 68 | void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | 74 | |
| 75 | /// Check if it's using host timing. | ||
| 76 | bool IsHostTiming() const { | ||
| 77 | return is_multicore; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Pauses/Unpauses the execution of the timer thread. | ||
| 81 | void Pause(bool is_paused); | ||
| 82 | |||
| 83 | /// Pauses/Unpauses the execution of the timer thread and waits until paused. | ||
| 84 | void SyncPause(bool is_paused); | ||
| 85 | |||
| 86 | /// Checks if core timing is running. | ||
| 87 | bool IsRunning() const; | ||
| 88 | |||
| 89 | /// Checks if the timer thread has started. | ||
| 90 | bool HasStarted() const { | ||
| 91 | return has_started; | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Checks if there are any pending time events. | ||
| 95 | bool HasPendingEvents() const; | ||
| 96 | |||
| 97 | /// Schedules an event in core timing | ||
| 98 | void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | ||
| 69 | u64 userdata = 0); | 99 | u64 userdata = 0); |
| 70 | 100 | ||
| 71 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); | 101 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); |
| @@ -73,41 +103,30 @@ public: | |||
| 73 | /// We only permit one event of each type in the queue at a time. | 103 | /// We only permit one event of each type in the queue at a time. |
| 74 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); | 104 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); |
| 75 | 105 | ||
| 76 | void ForceExceptionCheck(s64 cycles); | ||
| 77 | |||
| 78 | /// This should only be called from the emu thread, if you are calling it any other thread, | ||
| 79 | /// you are doing something evil | ||
| 80 | u64 GetTicks() const; | ||
| 81 | |||
| 82 | u64 GetIdleTicks() const; | ||
| 83 | |||
| 84 | void AddTicks(u64 ticks); | 106 | void AddTicks(u64 ticks); |
| 85 | 107 | ||
| 86 | /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends | 108 | void ResetTicks(); |
| 87 | /// the previous timing slice and begins the next one, you must Advance from the previous | ||
| 88 | /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an | ||
| 89 | /// Advance() is required to initialize the slice length before the first cycle of emulated | ||
| 90 | /// instructions is executed. | ||
| 91 | void Advance(); | ||
| 92 | 109 | ||
| 93 | /// Pretend that the main CPU has executed enough cycles to reach the next event. | ||
| 94 | void Idle(); | 110 | void Idle(); |
| 95 | 111 | ||
| 96 | std::chrono::microseconds GetGlobalTimeUs() const; | 112 | s64 GetDowncount() const { |
| 113 | return downcount; | ||
| 114 | } | ||
| 97 | 115 | ||
| 98 | void ResetRun(); | 116 | /// Returns current time in emulated CPU cycles |
| 117 | u64 GetCPUTicks() const; | ||
| 99 | 118 | ||
| 100 | s64 GetDowncount() const; | 119 | /// Returns current time in emulated in Clock cycles |
| 120 | u64 GetClockTicks() const; | ||
| 101 | 121 | ||
| 102 | void SwitchContext(u64 new_context) { | 122 | /// Returns current time in microseconds. |
| 103 | current_context = new_context; | 123 | std::chrono::microseconds GetGlobalTimeUs() const; |
| 104 | } | ||
| 105 | 124 | ||
| 106 | bool CanCurrentContextRun() const { | 125 | /// Returns current time in nanoseconds. |
| 107 | return time_slice[current_context] > 0; | 126 | std::chrono::nanoseconds GetGlobalTimeNs() const; |
| 108 | } | ||
| 109 | 127 | ||
| 110 | std::optional<u64> NextAvailableCore(const s64 needed_ticks) const; | 128 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. |
| 129 | std::optional<s64> Advance(); | ||
| 111 | 130 | ||
| 112 | private: | 131 | private: |
| 113 | struct Event; | 132 | struct Event; |
| @@ -115,21 +134,14 @@ private: | |||
| 115 | /// Clear all pending events. This should ONLY be done on exit. | 134 | /// Clear all pending events. This should ONLY be done on exit. |
| 116 | void ClearPendingEvents(); | 135 | void ClearPendingEvents(); |
| 117 | 136 | ||
| 118 | static constexpr u64 num_cpu_cores = 4; | 137 | static void ThreadEntry(CoreTiming& instance); |
| 138 | void ThreadLoop(); | ||
| 119 | 139 | ||
| 120 | s64 global_timer = 0; | 140 | std::unique_ptr<Common::WallClock> clock; |
| 121 | s64 idled_cycles = 0; | ||
| 122 | s64 slice_length = 0; | ||
| 123 | u64 accumulated_ticks = 0; | ||
| 124 | std::array<s64, num_cpu_cores> downcounts{}; | ||
| 125 | // Slice of time assigned to each core per run. | ||
| 126 | std::array<s64, num_cpu_cores> time_slice{}; | ||
| 127 | u64 current_context = 0; | ||
| 128 | 141 | ||
| 129 | // Are we in a function that has been called from Advance() | 142 | u64 global_timer = 0; |
| 130 | // If events are scheduled from a function that gets called from Advance(), | 143 | |
| 131 | // don't change slice_length and downcount. | 144 | std::chrono::nanoseconds start_point; |
| 132 | bool is_global_timer_sane = false; | ||
| 133 | 145 | ||
| 134 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 146 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. |
| 135 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 147 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and |
| @@ -139,8 +151,23 @@ private: | |||
| 139 | u64 event_fifo_id = 0; | 151 | u64 event_fifo_id = 0; |
| 140 | 152 | ||
| 141 | std::shared_ptr<EventType> ev_lost; | 153 | std::shared_ptr<EventType> ev_lost; |
| 142 | 154 | Common::Event event{}; | |
| 143 | std::mutex inner_mutex; | 155 | Common::Event pause_event{}; |
| 156 | Common::SpinLock basic_lock{}; | ||
| 157 | Common::SpinLock advance_lock{}; | ||
| 158 | std::unique_ptr<std::thread> timer_thread; | ||
| 159 | std::atomic<bool> paused{}; | ||
| 160 | std::atomic<bool> paused_set{}; | ||
| 161 | std::atomic<bool> wait_set{}; | ||
| 162 | std::atomic<bool> shutting_down{}; | ||
| 163 | std::atomic<bool> has_started{}; | ||
| 164 | std::function<void(void)> on_thread_init{}; | ||
| 165 | |||
| 166 | bool is_multicore{}; | ||
| 167 | |||
| 168 | /// Cycle timing | ||
| 169 | u64 ticks{}; | ||
| 170 | s64 downcount{}; | ||
| 144 | }; | 171 | }; |
| 145 | 172 | ||
| 146 | /// Creates a core timing event with the given name and callback. | 173 | /// Creates a core timing event with the given name and callback. |
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index de50d3b14..aefc63663 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp | |||
| @@ -38,15 +38,23 @@ s64 usToCycles(std::chrono::microseconds us) { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | s64 nsToCycles(std::chrono::nanoseconds ns) { | 40 | s64 nsToCycles(std::chrono::nanoseconds ns) { |
| 41 | if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) { | 41 | const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE); |
| 42 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | 42 | return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first; |
| 43 | return std::numeric_limits<s64>::max(); | 43 | } |
| 44 | } | 44 | |
| 45 | if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) { | 45 | u64 msToClockCycles(std::chrono::milliseconds ns) { |
| 46 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | 46 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); |
| 47 | return Hardware::BASE_CLOCK_RATE * (ns.count() / 1000000000); | 47 | return Common::Divide128On32(temp, 1000).first; |
| 48 | } | 48 | } |
| 49 | return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; | 49 | |
| 50 | u64 usToClockCycles(std::chrono::microseconds ns) { | ||
| 51 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); | ||
| 52 | return Common::Divide128On32(temp, 1000000).first; | ||
| 53 | } | ||
| 54 | |||
| 55 | u64 nsToClockCycles(std::chrono::nanoseconds ns) { | ||
| 56 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); | ||
| 57 | return Common::Divide128On32(temp, 1000000000).first; | ||
| 50 | } | 58 | } |
| 51 | 59 | ||
| 52 | u64 CpuCyclesToClockCycles(u64 ticks) { | 60 | u64 CpuCyclesToClockCycles(u64 ticks) { |
| @@ -54,4 +62,22 @@ u64 CpuCyclesToClockCycles(u64 ticks) { | |||
| 54 | return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | 62 | return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; |
| 55 | } | 63 | } |
| 56 | 64 | ||
| 65 | std::chrono::milliseconds CyclesToMs(s64 cycles) { | ||
| 66 | const u128 temporal = Common::Multiply64Into128(cycles, 1000); | ||
| 67 | u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | ||
| 68 | return std::chrono::milliseconds(ms); | ||
| 69 | } | ||
| 70 | |||
| 71 | std::chrono::nanoseconds CyclesToNs(s64 cycles) { | ||
| 72 | const u128 temporal = Common::Multiply64Into128(cycles, 1000000000); | ||
| 73 | u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | ||
| 74 | return std::chrono::nanoseconds(ns); | ||
| 75 | } | ||
| 76 | |||
| 77 | std::chrono::microseconds CyclesToUs(s64 cycles) { | ||
| 78 | const u128 temporal = Common::Multiply64Into128(cycles, 1000000); | ||
| 79 | u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | ||
| 80 | return std::chrono::microseconds(us); | ||
| 81 | } | ||
| 82 | |||
| 57 | } // namespace Core::Timing | 83 | } // namespace Core::Timing |
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index addc72b19..2ed979e14 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h | |||
| @@ -13,18 +13,12 @@ namespace Core::Timing { | |||
| 13 | s64 msToCycles(std::chrono::milliseconds ms); | 13 | s64 msToCycles(std::chrono::milliseconds ms); |
| 14 | s64 usToCycles(std::chrono::microseconds us); | 14 | s64 usToCycles(std::chrono::microseconds us); |
| 15 | s64 nsToCycles(std::chrono::nanoseconds ns); | 15 | s64 nsToCycles(std::chrono::nanoseconds ns); |
| 16 | 16 | u64 msToClockCycles(std::chrono::milliseconds ns); | |
| 17 | inline std::chrono::milliseconds CyclesToMs(s64 cycles) { | 17 | u64 usToClockCycles(std::chrono::microseconds ns); |
| 18 | return std::chrono::milliseconds(cycles * 1000 / Hardware::BASE_CLOCK_RATE); | 18 | u64 nsToClockCycles(std::chrono::nanoseconds ns); |
| 19 | } | 19 | std::chrono::milliseconds CyclesToMs(s64 cycles); |
| 20 | 20 | std::chrono::nanoseconds CyclesToNs(s64 cycles); | |
| 21 | inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { | 21 | std::chrono::microseconds CyclesToUs(s64 cycles); |
| 22 | return std::chrono::nanoseconds(cycles * 1000000000 / Hardware::BASE_CLOCK_RATE); | ||
| 23 | } | ||
| 24 | |||
| 25 | inline std::chrono::microseconds CyclesToUs(s64 cycles) { | ||
| 26 | return std::chrono::microseconds(cycles * 1000000 / Hardware::BASE_CLOCK_RATE); | ||
| 27 | } | ||
| 28 | 22 | ||
| 29 | u64 CpuCyclesToClockCycles(u64 ticks); | 23 | u64 CpuCyclesToClockCycles(u64 ticks); |
| 30 | 24 | ||
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 70ddbdcca..32afcf3ae 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -2,80 +2,372 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/fiber.h" | ||
| 6 | #include "common/microprofile.h" | ||
| 7 | #include "common/thread.h" | ||
| 5 | #include "core/arm/exclusive_monitor.h" | 8 | #include "core/arm/exclusive_monitor.h" |
| 6 | #include "core/core.h" | 9 | #include "core/core.h" |
| 7 | #include "core/core_manager.h" | ||
| 8 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 9 | #include "core/cpu_manager.h" | 11 | #include "core/cpu_manager.h" |
| 10 | #include "core/gdbstub/gdbstub.h" | 12 | #include "core/gdbstub/gdbstub.h" |
| 13 | #include "core/hle/kernel/kernel.h" | ||
| 14 | #include "core/hle/kernel/physical_core.h" | ||
| 15 | #include "core/hle/kernel/scheduler.h" | ||
| 16 | #include "core/hle/kernel/thread.h" | ||
| 17 | #include "video_core/gpu.h" | ||
| 11 | 18 | ||
| 12 | namespace Core { | 19 | namespace Core { |
| 13 | 20 | ||
| 14 | CpuManager::CpuManager(System& system) : system{system} {} | 21 | CpuManager::CpuManager(System& system) : system{system} {} |
| 15 | CpuManager::~CpuManager() = default; | 22 | CpuManager::~CpuManager() = default; |
| 16 | 23 | ||
| 24 | void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { | ||
| 25 | cpu_manager.RunThread(core); | ||
| 26 | } | ||
| 27 | |||
| 17 | void CpuManager::Initialize() { | 28 | void CpuManager::Initialize() { |
| 18 | for (std::size_t index = 0; index < core_managers.size(); ++index) { | 29 | running_mode = true; |
| 19 | core_managers[index] = std::make_unique<CoreManager>(system, index); | 30 | if (is_multicore) { |
| 31 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 32 | core_data[core].host_thread = | ||
| 33 | std::make_unique<std::thread>(ThreadStart, std::ref(*this), core); | ||
| 34 | } | ||
| 35 | } else { | ||
| 36 | core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0); | ||
| 20 | } | 37 | } |
| 21 | } | 38 | } |
| 22 | 39 | ||
| 23 | void CpuManager::Shutdown() { | 40 | void CpuManager::Shutdown() { |
| 24 | for (auto& cpu_core : core_managers) { | 41 | running_mode = false; |
| 25 | cpu_core.reset(); | 42 | Pause(false); |
| 43 | if (is_multicore) { | ||
| 44 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 45 | core_data[core].host_thread->join(); | ||
| 46 | core_data[core].host_thread.reset(); | ||
| 47 | } | ||
| 48 | } else { | ||
| 49 | core_data[0].host_thread->join(); | ||
| 50 | core_data[0].host_thread.reset(); | ||
| 26 | } | 51 | } |
| 27 | } | 52 | } |
| 28 | 53 | ||
| 29 | CoreManager& CpuManager::GetCoreManager(std::size_t index) { | 54 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { |
| 30 | return *core_managers.at(index); | 55 | return std::function<void(void*)>(GuestThreadFunction); |
| 31 | } | 56 | } |
| 32 | 57 | ||
| 33 | const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { | 58 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { |
| 34 | return *core_managers.at(index); | 59 | return std::function<void(void*)>(IdleThreadFunction); |
| 35 | } | 60 | } |
| 36 | 61 | ||
| 37 | CoreManager& CpuManager::GetCurrentCoreManager() { | 62 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { |
| 38 | // Otherwise, use single-threaded mode active_core variable | 63 | return std::function<void(void*)>(SuspendThreadFunction); |
| 39 | return *core_managers[active_core]; | ||
| 40 | } | 64 | } |
| 41 | 65 | ||
| 42 | const CoreManager& CpuManager::GetCurrentCoreManager() const { | 66 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { |
| 43 | // Otherwise, use single-threaded mode active_core variable | 67 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 44 | return *core_managers[active_core]; | 68 | if (cpu_manager->is_multicore) { |
| 69 | cpu_manager->MultiCoreRunGuestThread(); | ||
| 70 | } else { | ||
| 71 | cpu_manager->SingleCoreRunGuestThread(); | ||
| 72 | } | ||
| 45 | } | 73 | } |
| 46 | 74 | ||
| 47 | void CpuManager::RunLoop(bool tight_loop) { | 75 | void CpuManager::GuestRewindFunction(void* cpu_manager_) { |
| 48 | if (GDBStub::IsServerEnabled()) { | 76 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 49 | GDBStub::HandlePacket(); | 77 | if (cpu_manager->is_multicore) { |
| 78 | cpu_manager->MultiCoreRunGuestLoop(); | ||
| 79 | } else { | ||
| 80 | cpu_manager->SingleCoreRunGuestLoop(); | ||
| 81 | } | ||
| 82 | } | ||
| 50 | 83 | ||
| 51 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | 84 | void CpuManager::IdleThreadFunction(void* cpu_manager_) { |
| 52 | // execute. Otherwise, get out of the loop function. | 85 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 53 | if (GDBStub::GetCpuHaltFlag()) { | 86 | if (cpu_manager->is_multicore) { |
| 54 | if (GDBStub::GetCpuStepFlag()) { | 87 | cpu_manager->MultiCoreRunIdleThread(); |
| 55 | tight_loop = false; | 88 | } else { |
| 56 | } else { | 89 | cpu_manager->SingleCoreRunIdleThread(); |
| 57 | return; | 90 | } |
| 91 | } | ||
| 92 | |||
| 93 | void CpuManager::SuspendThreadFunction(void* cpu_manager_) { | ||
| 94 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); | ||
| 95 | if (cpu_manager->is_multicore) { | ||
| 96 | cpu_manager->MultiCoreRunSuspendThread(); | ||
| 97 | } else { | ||
| 98 | cpu_manager->SingleCoreRunSuspendThread(); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | void* CpuManager::GetStartFuncParamater() { | ||
| 103 | return static_cast<void*>(this); | ||
| 104 | } | ||
| 105 | |||
| 106 | /////////////////////////////////////////////////////////////////////////////// | ||
| 107 | /// MultiCore /// | ||
| 108 | /////////////////////////////////////////////////////////////////////////////// | ||
| 109 | |||
| 110 | void CpuManager::MultiCoreRunGuestThread() { | ||
| 111 | auto& kernel = system.Kernel(); | ||
| 112 | { | ||
| 113 | auto& sched = kernel.CurrentScheduler(); | ||
| 114 | sched.OnThreadStart(); | ||
| 115 | } | ||
| 116 | MultiCoreRunGuestLoop(); | ||
| 117 | } | ||
| 118 | |||
| 119 | void CpuManager::MultiCoreRunGuestLoop() { | ||
| 120 | auto& kernel = system.Kernel(); | ||
| 121 | auto* thread = kernel.CurrentScheduler().GetCurrentThread(); | ||
| 122 | while (true) { | ||
| 123 | auto* physical_core = &kernel.CurrentPhysicalCore(); | ||
| 124 | auto& arm_interface = thread->ArmInterface(); | ||
| 125 | system.EnterDynarmicProfile(); | ||
| 126 | while (!physical_core->IsInterrupted()) { | ||
| 127 | arm_interface.Run(); | ||
| 128 | physical_core = &kernel.CurrentPhysicalCore(); | ||
| 129 | } | ||
| 130 | system.ExitDynarmicProfile(); | ||
| 131 | arm_interface.ClearExclusiveState(); | ||
| 132 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 133 | scheduler.TryDoContextSwitch(); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | void CpuManager::MultiCoreRunIdleThread() { | ||
| 138 | auto& kernel = system.Kernel(); | ||
| 139 | while (true) { | ||
| 140 | auto& physical_core = kernel.CurrentPhysicalCore(); | ||
| 141 | physical_core.Idle(); | ||
| 142 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 143 | scheduler.TryDoContextSwitch(); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | void CpuManager::MultiCoreRunSuspendThread() { | ||
| 148 | auto& kernel = system.Kernel(); | ||
| 149 | { | ||
| 150 | auto& sched = kernel.CurrentScheduler(); | ||
| 151 | sched.OnThreadStart(); | ||
| 152 | } | ||
| 153 | while (true) { | ||
| 154 | auto core = kernel.GetCurrentHostThreadID(); | ||
| 155 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 156 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 157 | Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); | ||
| 158 | ASSERT(scheduler.ContextSwitchPending()); | ||
| 159 | ASSERT(core == kernel.GetCurrentHostThreadID()); | ||
| 160 | scheduler.TryDoContextSwitch(); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | void CpuManager::MultiCorePause(bool paused) { | ||
| 165 | if (!paused) { | ||
| 166 | bool all_not_barrier = false; | ||
| 167 | while (!all_not_barrier) { | ||
| 168 | all_not_barrier = true; | ||
| 169 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 170 | all_not_barrier &= | ||
| 171 | !core_data[core].is_running.load() && core_data[core].initialized.load(); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 175 | core_data[core].enter_barrier->Set(); | ||
| 176 | } | ||
| 177 | if (paused_state.load()) { | ||
| 178 | bool all_barrier = false; | ||
| 179 | while (!all_barrier) { | ||
| 180 | all_barrier = true; | ||
| 181 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 182 | all_barrier &= | ||
| 183 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 187 | core_data[core].exit_barrier->Set(); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } else { | ||
| 191 | /// Wait until all cores are paused. | ||
| 192 | bool all_barrier = false; | ||
| 193 | while (!all_barrier) { | ||
| 194 | all_barrier = true; | ||
| 195 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 196 | all_barrier &= | ||
| 197 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 58 | } | 198 | } |
| 59 | } | 199 | } |
| 200 | /// Don't release the barrier | ||
| 60 | } | 201 | } |
| 202 | paused_state = paused; | ||
| 203 | } | ||
| 204 | |||
| 205 | /////////////////////////////////////////////////////////////////////////////// | ||
| 206 | /// SingleCore /// | ||
| 207 | /////////////////////////////////////////////////////////////////////////////// | ||
| 61 | 208 | ||
| 62 | auto& core_timing = system.CoreTiming(); | 209 | void CpuManager::SingleCoreRunGuestThread() { |
| 63 | core_timing.ResetRun(); | 210 | auto& kernel = system.Kernel(); |
| 64 | bool keep_running{}; | 211 | { |
| 65 | do { | 212 | auto& sched = kernel.CurrentScheduler(); |
| 66 | keep_running = false; | 213 | sched.OnThreadStart(); |
| 67 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | 214 | } |
| 68 | core_timing.SwitchContext(active_core); | 215 | SingleCoreRunGuestLoop(); |
| 69 | if (core_timing.CanCurrentContextRun()) { | 216 | } |
| 70 | core_managers[active_core]->RunLoop(tight_loop); | 217 | |
| 218 | void CpuManager::SingleCoreRunGuestLoop() { | ||
| 219 | auto& kernel = system.Kernel(); | ||
| 220 | auto* thread = kernel.CurrentScheduler().GetCurrentThread(); | ||
| 221 | while (true) { | ||
| 222 | auto* physical_core = &kernel.CurrentPhysicalCore(); | ||
| 223 | auto& arm_interface = thread->ArmInterface(); | ||
| 224 | system.EnterDynarmicProfile(); | ||
| 225 | if (!physical_core->IsInterrupted()) { | ||
| 226 | arm_interface.Run(); | ||
| 227 | physical_core = &kernel.CurrentPhysicalCore(); | ||
| 228 | } | ||
| 229 | system.ExitDynarmicProfile(); | ||
| 230 | thread->SetPhantomMode(true); | ||
| 231 | system.CoreTiming().Advance(); | ||
| 232 | thread->SetPhantomMode(false); | ||
| 233 | arm_interface.ClearExclusiveState(); | ||
| 234 | PreemptSingleCore(); | ||
| 235 | auto& scheduler = kernel.Scheduler(current_core); | ||
| 236 | scheduler.TryDoContextSwitch(); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | void CpuManager::SingleCoreRunIdleThread() { | ||
| 241 | auto& kernel = system.Kernel(); | ||
| 242 | while (true) { | ||
| 243 | auto& physical_core = kernel.CurrentPhysicalCore(); | ||
| 244 | PreemptSingleCore(false); | ||
| 245 | system.CoreTiming().AddTicks(1000U); | ||
| 246 | idle_count++; | ||
| 247 | auto& scheduler = physical_core.Scheduler(); | ||
| 248 | scheduler.TryDoContextSwitch(); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | void CpuManager::SingleCoreRunSuspendThread() { | ||
| 253 | auto& kernel = system.Kernel(); | ||
| 254 | { | ||
| 255 | auto& sched = kernel.CurrentScheduler(); | ||
| 256 | sched.OnThreadStart(); | ||
| 257 | } | ||
| 258 | while (true) { | ||
| 259 | auto core = kernel.GetCurrentHostThreadID(); | ||
| 260 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 261 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 262 | Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context); | ||
| 263 | ASSERT(scheduler.ContextSwitchPending()); | ||
| 264 | ASSERT(core == kernel.GetCurrentHostThreadID()); | ||
| 265 | scheduler.TryDoContextSwitch(); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | void CpuManager::PreemptSingleCore(bool from_running_enviroment) { | ||
| 270 | std::size_t old_core = current_core; | ||
| 271 | auto& scheduler = system.Kernel().Scheduler(old_core); | ||
| 272 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 273 | if (idle_count >= 4 || from_running_enviroment) { | ||
| 274 | if (!from_running_enviroment) { | ||
| 275 | system.CoreTiming().Idle(); | ||
| 276 | idle_count = 0; | ||
| 277 | } | ||
| 278 | current_thread->SetPhantomMode(true); | ||
| 279 | system.CoreTiming().Advance(); | ||
| 280 | current_thread->SetPhantomMode(false); | ||
| 281 | } | ||
| 282 | current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); | ||
| 283 | system.CoreTiming().ResetTicks(); | ||
| 284 | scheduler.Unload(); | ||
| 285 | auto& next_scheduler = system.Kernel().Scheduler(current_core); | ||
| 286 | Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); | ||
| 287 | /// May have changed scheduler | ||
| 288 | auto& current_scheduler = system.Kernel().Scheduler(current_core); | ||
| 289 | current_scheduler.Reload(); | ||
| 290 | auto* currrent_thread2 = current_scheduler.GetCurrentThread(); | ||
| 291 | if (!currrent_thread2->IsIdleThread()) { | ||
| 292 | idle_count = 0; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | void CpuManager::SingleCorePause(bool paused) { | ||
| 297 | if (!paused) { | ||
| 298 | bool all_not_barrier = false; | ||
| 299 | while (!all_not_barrier) { | ||
| 300 | all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load(); | ||
| 301 | } | ||
| 302 | core_data[0].enter_barrier->Set(); | ||
| 303 | if (paused_state.load()) { | ||
| 304 | bool all_barrier = false; | ||
| 305 | while (!all_barrier) { | ||
| 306 | all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); | ||
| 71 | } | 307 | } |
| 72 | keep_running |= core_timing.CanCurrentContextRun(); | 308 | core_data[0].exit_barrier->Set(); |
| 73 | } | 309 | } |
| 74 | } while (keep_running); | 310 | } else { |
| 311 | /// Wait until all cores are paused. | ||
| 312 | bool all_barrier = false; | ||
| 313 | while (!all_barrier) { | ||
| 314 | all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); | ||
| 315 | } | ||
| 316 | /// Don't release the barrier | ||
| 317 | } | ||
| 318 | paused_state = paused; | ||
| 319 | } | ||
| 320 | |||
| 321 | void CpuManager::Pause(bool paused) { | ||
| 322 | if (is_multicore) { | ||
| 323 | MultiCorePause(paused); | ||
| 324 | } else { | ||
| 325 | SingleCorePause(paused); | ||
| 326 | } | ||
| 327 | } | ||
| 75 | 328 | ||
| 76 | if (GDBStub::IsServerEnabled()) { | 329 | void CpuManager::RunThread(std::size_t core) { |
| 77 | GDBStub::SetCpuStepFlag(false); | 330 | /// Initialization |
| 331 | system.RegisterCoreThread(core); | ||
| 332 | std::string name; | ||
| 333 | if (is_multicore) { | ||
| 334 | name = "yuzu:CoreCPUThread_" + std::to_string(core); | ||
| 335 | } else { | ||
| 336 | name = "yuzu:CPUThread"; | ||
| 337 | } | ||
| 338 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 339 | Common::SetCurrentThreadName(name.c_str()); | ||
| 340 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 341 | auto& data = core_data[core]; | ||
| 342 | data.enter_barrier = std::make_unique<Common::Event>(); | ||
| 343 | data.exit_barrier = std::make_unique<Common::Event>(); | ||
| 344 | data.host_context = Common::Fiber::ThreadToFiber(); | ||
| 345 | data.is_running = false; | ||
| 346 | data.initialized = true; | ||
| 347 | const bool sc_sync = !is_async_gpu && !is_multicore; | ||
| 348 | bool sc_sync_first_use = sc_sync; | ||
| 349 | /// Running | ||
| 350 | while (running_mode) { | ||
| 351 | data.is_running = false; | ||
| 352 | data.enter_barrier->Wait(); | ||
| 353 | if (sc_sync_first_use) { | ||
| 354 | system.GPU().ObtainContext(); | ||
| 355 | sc_sync_first_use = false; | ||
| 356 | } | ||
| 357 | auto& scheduler = system.Kernel().CurrentScheduler(); | ||
| 358 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 359 | data.is_running = true; | ||
| 360 | Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext()); | ||
| 361 | data.is_running = false; | ||
| 362 | data.is_paused = true; | ||
| 363 | data.exit_barrier->Wait(); | ||
| 364 | data.is_paused = false; | ||
| 78 | } | 365 | } |
| 366 | /// Time to cleanup | ||
| 367 | data.host_context->Exit(); | ||
| 368 | data.enter_barrier.reset(); | ||
| 369 | data.exit_barrier.reset(); | ||
| 370 | data.initialized = false; | ||
| 79 | } | 371 | } |
| 80 | 372 | ||
| 81 | } // namespace Core | 373 | } // namespace Core |
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 97554d1bb..35929ed94 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h | |||
| @@ -5,12 +5,19 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | ||
| 9 | #include <functional> | ||
| 8 | #include <memory> | 10 | #include <memory> |
| 11 | #include <thread> | ||
| 9 | #include "core/hardware_properties.h" | 12 | #include "core/hardware_properties.h" |
| 10 | 13 | ||
| 14 | namespace Common { | ||
| 15 | class Event; | ||
| 16 | class Fiber; | ||
| 17 | } // namespace Common | ||
| 18 | |||
| 11 | namespace Core { | 19 | namespace Core { |
| 12 | 20 | ||
| 13 | class CoreManager; | ||
| 14 | class System; | 21 | class System; |
| 15 | 22 | ||
| 16 | class CpuManager { | 23 | class CpuManager { |
| @@ -24,24 +31,75 @@ public: | |||
| 24 | CpuManager& operator=(const CpuManager&) = delete; | 31 | CpuManager& operator=(const CpuManager&) = delete; |
| 25 | CpuManager& operator=(CpuManager&&) = delete; | 32 | CpuManager& operator=(CpuManager&&) = delete; |
| 26 | 33 | ||
| 34 | /// Sets if emulation is multicore or single core, must be set before Initialize | ||
| 35 | void SetMulticore(bool is_multicore) { | ||
| 36 | this->is_multicore = is_multicore; | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Sets if emulation is using an asynchronous GPU. | ||
| 40 | void SetAsyncGpu(bool is_async_gpu) { | ||
| 41 | this->is_async_gpu = is_async_gpu; | ||
| 42 | } | ||
| 43 | |||
| 27 | void Initialize(); | 44 | void Initialize(); |
| 28 | void Shutdown(); | 45 | void Shutdown(); |
| 29 | 46 | ||
| 30 | CoreManager& GetCoreManager(std::size_t index); | 47 | void Pause(bool paused); |
| 31 | const CoreManager& GetCoreManager(std::size_t index) const; | ||
| 32 | 48 | ||
| 33 | CoreManager& GetCurrentCoreManager(); | 49 | std::function<void(void*)> GetGuestThreadStartFunc(); |
| 34 | const CoreManager& GetCurrentCoreManager() const; | 50 | std::function<void(void*)> GetIdleThreadStartFunc(); |
| 51 | std::function<void(void*)> GetSuspendThreadStartFunc(); | ||
| 52 | void* GetStartFuncParamater(); | ||
| 35 | 53 | ||
| 36 | std::size_t GetActiveCoreIndex() const { | 54 | void PreemptSingleCore(bool from_running_enviroment = true); |
| 37 | return active_core; | ||
| 38 | } | ||
| 39 | 55 | ||
| 40 | void RunLoop(bool tight_loop); | 56 | std::size_t CurrentCore() const { |
| 57 | return current_core.load(); | ||
| 58 | } | ||
| 41 | 59 | ||
| 42 | private: | 60 | private: |
| 43 | std::array<std::unique_ptr<CoreManager>, Hardware::NUM_CPU_CORES> core_managers; | 61 | static void GuestThreadFunction(void* cpu_manager); |
| 44 | std::size_t active_core{}; ///< Active core, only used in single thread mode | 62 | static void GuestRewindFunction(void* cpu_manager); |
| 63 | static void IdleThreadFunction(void* cpu_manager); | ||
| 64 | static void SuspendThreadFunction(void* cpu_manager); | ||
| 65 | |||
| 66 | void MultiCoreRunGuestThread(); | ||
| 67 | void MultiCoreRunGuestLoop(); | ||
| 68 | void MultiCoreRunIdleThread(); | ||
| 69 | void MultiCoreRunSuspendThread(); | ||
| 70 | void MultiCorePause(bool paused); | ||
| 71 | |||
| 72 | void SingleCoreRunGuestThread(); | ||
| 73 | void SingleCoreRunGuestLoop(); | ||
| 74 | void SingleCoreRunIdleThread(); | ||
| 75 | void SingleCoreRunSuspendThread(); | ||
| 76 | void SingleCorePause(bool paused); | ||
| 77 | |||
| 78 | static void ThreadStart(CpuManager& cpu_manager, std::size_t core); | ||
| 79 | |||
| 80 | void RunThread(std::size_t core); | ||
| 81 | |||
| 82 | struct CoreData { | ||
| 83 | std::shared_ptr<Common::Fiber> host_context; | ||
| 84 | std::unique_ptr<Common::Event> enter_barrier; | ||
| 85 | std::unique_ptr<Common::Event> exit_barrier; | ||
| 86 | std::atomic<bool> is_running; | ||
| 87 | std::atomic<bool> is_paused; | ||
| 88 | std::atomic<bool> initialized; | ||
| 89 | std::unique_ptr<std::thread> host_thread; | ||
| 90 | }; | ||
| 91 | |||
| 92 | std::atomic<bool> running_mode{}; | ||
| 93 | std::atomic<bool> paused_state{}; | ||
| 94 | |||
| 95 | std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; | ||
| 96 | |||
| 97 | bool is_async_gpu{}; | ||
| 98 | bool is_multicore{}; | ||
| 99 | std::atomic<std::size_t> current_core{}; | ||
| 100 | std::size_t preemption_count{}; | ||
| 101 | std::size_t idle_count{}; | ||
| 102 | static constexpr std::size_t max_cycle_runs = 5; | ||
| 45 | 103 | ||
| 46 | System& system; | 104 | System& system; |
| 47 | }; | 105 | }; |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 8997c7082..f87fe0abc 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -695,8 +695,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, | |||
| 695 | } | 695 | } |
| 696 | 696 | ||
| 697 | void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | 697 | void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { |
| 698 | if (s128_keys.find({id, field1, field2}) != s128_keys.end()) | 698 | if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) { |
| 699 | return; | 699 | return; |
| 700 | } | ||
| 700 | if (id == S128KeyType::Titlekey) { | 701 | if (id == S128KeyType::Titlekey) { |
| 701 | Key128 rights_id; | 702 | Key128 rights_id; |
| 702 | std::memcpy(rights_id.data(), &field2, sizeof(u64)); | 703 | std::memcpy(rights_id.data(), &field2, sizeof(u64)); |
| @@ -716,8 +717,9 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | |||
| 716 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == | 717 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == |
| 717 | std::tie(id, field1, field2); | 718 | std::tie(id, field1, field2); |
| 718 | }); | 719 | }); |
| 719 | if (iter2 != s128_file_id.end()) | 720 | if (iter2 != s128_file_id.end()) { |
| 720 | WriteKeyToFile(category, iter2->first, key); | 721 | WriteKeyToFile(category, iter2->first, key); |
| 722 | } | ||
| 721 | 723 | ||
| 722 | // Variable cases | 724 | // Variable cases |
| 723 | if (id == S128KeyType::KeyArea) { | 725 | if (id == S128KeyType::KeyArea) { |
| @@ -745,16 +747,18 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | |||
| 745 | } | 747 | } |
| 746 | 748 | ||
| 747 | void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { | 749 | void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { |
| 748 | if (s256_keys.find({id, field1, field2}) != s256_keys.end()) | 750 | if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) { |
| 749 | return; | 751 | return; |
| 752 | } | ||
| 750 | const auto iter = std::find_if( | 753 | const auto iter = std::find_if( |
| 751 | s256_file_id.begin(), s256_file_id.end(), | 754 | s256_file_id.begin(), s256_file_id.end(), |
| 752 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { | 755 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { |
| 753 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == | 756 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == |
| 754 | std::tie(id, field1, field2); | 757 | std::tie(id, field1, field2); |
| 755 | }); | 758 | }); |
| 756 | if (iter != s256_file_id.end()) | 759 | if (iter != s256_file_id.end()) { |
| 757 | WriteKeyToFile(KeyCategory::Standard, iter->first, key); | 760 | WriteKeyToFile(KeyCategory::Standard, iter->first, key); |
| 761 | } | ||
| 758 | s256_keys[{id, field1, field2}] = key; | 762 | s256_keys[{id, field1, field2}] = key; |
| 759 | } | 763 | } |
| 760 | 764 | ||
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 7265c4171..9269a73f2 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -223,7 +223,16 @@ bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { | |||
| 223 | 223 | ||
| 224 | class KeyManager { | 224 | class KeyManager { |
| 225 | public: | 225 | public: |
| 226 | KeyManager(); | 226 | static KeyManager& Instance() { |
| 227 | static KeyManager instance; | ||
| 228 | return instance; | ||
| 229 | } | ||
| 230 | |||
| 231 | KeyManager(const KeyManager&) = delete; | ||
| 232 | KeyManager& operator=(const KeyManager&) = delete; | ||
| 233 | |||
| 234 | KeyManager(KeyManager&&) = delete; | ||
| 235 | KeyManager& operator=(KeyManager&&) = delete; | ||
| 227 | 236 | ||
| 228 | bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; | 237 | bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; |
| 229 | bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; | 238 | bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; |
| @@ -257,6 +266,8 @@ public: | |||
| 257 | bool AddTicketPersonalized(Ticket raw); | 266 | bool AddTicketPersonalized(Ticket raw); |
| 258 | 267 | ||
| 259 | private: | 268 | private: |
| 269 | KeyManager(); | ||
| 270 | |||
| 260 | std::map<KeyIndex<S128KeyType>, Key128> s128_keys; | 271 | std::map<KeyIndex<S128KeyType>, Key128> s128_keys; |
| 261 | std::map<KeyIndex<S256KeyType>, Key256> s256_keys; | 272 | std::map<KeyIndex<S256KeyType>, Key256> s256_keys; |
| 262 | 273 | ||
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 0af44f340..8935a62c3 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { | |||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { | 81 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { |
| 82 | Core::Crypto::KeyManager keys; | 82 | auto& keys = Core::Crypto::KeyManager::Instance(); |
| 83 | Core::Crypto::PartitionDataManager pdm{ | 83 | Core::Crypto::PartitionDataManager pdm{ |
| 84 | Core::System::GetInstance().GetFilesystem()->OpenDirectory( | 84 | Core::System::GetInstance().GetFilesystem()->OpenDirectory( |
| 85 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; | 85 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 07d0c8d5d..664a47e7f 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -178,7 +178,7 @@ u32 XCI::GetSystemUpdateVersion() { | |||
| 178 | return 0; | 178 | return 0; |
| 179 | 179 | ||
| 180 | for (const auto& file : update->GetFiles()) { | 180 | for (const auto& file : update->GetFiles()) { |
| 181 | NCA nca{file, nullptr, 0, keys}; | 181 | NCA nca{file, nullptr, 0}; |
| 182 | 182 | ||
| 183 | if (nca.GetStatus() != Loader::ResultStatus::Success) | 183 | if (nca.GetStatus() != Loader::ResultStatus::Success) |
| 184 | continue; | 184 | continue; |
| @@ -286,7 +286,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | |||
| 286 | continue; | 286 | continue; |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | auto nca = std::make_shared<NCA>(file, nullptr, 0, keys); | 289 | auto nca = std::make_shared<NCA>(file, nullptr, 0); |
| 290 | if (nca->IsUpdate()) { | 290 | if (nca->IsUpdate()) { |
| 291 | continue; | 291 | continue; |
| 292 | } | 292 | } |
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index c2ee0ea99..e1b136426 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -140,6 +140,6 @@ private: | |||
| 140 | 140 | ||
| 141 | u64 update_normal_partition_end; | 141 | u64 update_normal_partition_end; |
| 142 | 142 | ||
| 143 | Core::Crypto::KeyManager keys; | 143 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 144 | }; | 144 | }; |
| 145 | } // namespace FileSys | 145 | } // namespace FileSys |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index b8bbdd1ef..473245d5a 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -118,9 +118,8 @@ static bool IsValidNCA(const NCAHeader& header) { | |||
| 118 | return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | 118 | return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset, | 121 | NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) |
| 122 | Core::Crypto::KeyManager keys_) | 122 | : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { |
| 123 | : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) { | ||
| 124 | if (file == nullptr) { | 123 | if (file == nullptr) { |
| 125 | status = Loader::ResultStatus::ErrorNullFile; | 124 | status = Loader::ResultStatus::ErrorNullFile; |
| 126 | return; | 125 | return; |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e249079b5..d25cbcf91 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -99,8 +99,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) { | |||
| 99 | class NCA : public ReadOnlyVfsDirectory { | 99 | class NCA : public ReadOnlyVfsDirectory { |
| 100 | public: | 100 | public: |
| 101 | explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, | 101 | explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, |
| 102 | u64 bktr_base_ivfc_offset = 0, | 102 | u64 bktr_base_ivfc_offset = 0); |
| 103 | Core::Crypto::KeyManager keys = Core::Crypto::KeyManager()); | ||
| 104 | ~NCA() override; | 103 | ~NCA() override; |
| 105 | 104 | ||
| 106 | Loader::ResultStatus GetStatus() const; | 105 | Loader::ResultStatus GetStatus() const; |
| @@ -159,7 +158,7 @@ private: | |||
| 159 | bool encrypted = false; | 158 | bool encrypted = false; |
| 160 | bool is_update = false; | 159 | bool is_update = false; |
| 161 | 160 | ||
| 162 | Core::Crypto::KeyManager keys; | 161 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 163 | }; | 162 | }; |
| 164 | 163 | ||
| 165 | } // namespace FileSys | 164 | } // namespace FileSys |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index ba5f76288..27c1b0233 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -408,7 +408,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) { | |||
| 408 | 408 | ||
| 409 | if (file == nullptr) | 409 | if (file == nullptr) |
| 410 | continue; | 410 | continue; |
| 411 | const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys); | 411 | const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0); |
| 412 | if (nca->GetStatus() != Loader::ResultStatus::Success || | 412 | if (nca->GetStatus() != Loader::ResultStatus::Success || |
| 413 | nca->GetType() != NCAContentType::Meta) { | 413 | nca->GetType() != NCAContentType::Meta) { |
| 414 | continue; | 414 | continue; |
| @@ -486,7 +486,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t | |||
| 486 | const auto raw = GetEntryRaw(title_id, type); | 486 | const auto raw = GetEntryRaw(title_id, type); |
| 487 | if (raw == nullptr) | 487 | if (raw == nullptr) |
| 488 | return nullptr; | 488 | return nullptr; |
| 489 | return std::make_unique<NCA>(raw, nullptr, 0, keys); | 489 | return std::make_unique<NCA>(raw, nullptr, 0); |
| 490 | } | 490 | } |
| 491 | 491 | ||
| 492 | template <typename T> | 492 | template <typename T> |
| @@ -865,7 +865,7 @@ std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecord | |||
| 865 | const auto res = GetEntryRaw(title_id, type); | 865 | const auto res = GetEntryRaw(title_id, type); |
| 866 | if (res == nullptr) | 866 | if (res == nullptr) |
| 867 | return nullptr; | 867 | return nullptr; |
| 868 | return std::make_unique<NCA>(res, nullptr, 0, keys); | 868 | return std::make_unique<NCA>(res, nullptr, 0); |
| 869 | } | 869 | } |
| 870 | 870 | ||
| 871 | std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter( | 871 | std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter( |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index d1eec240e..f339cd17b 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -88,7 +88,7 @@ public: | |||
| 88 | 88 | ||
| 89 | protected: | 89 | protected: |
| 90 | // A single instance of KeyManager to be used by GetEntry() | 90 | // A single instance of KeyManager to be used by GetEntry() |
| 91 | Core::Crypto::KeyManager keys; | 91 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 92 | }; | 92 | }; |
| 93 | 93 | ||
| 94 | class PlaceholderCache { | 94 | class PlaceholderCache { |
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index ef3084681..175a8266a 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | namespace FileSys { | 21 | namespace FileSys { |
| 22 | namespace { | 22 | namespace { |
| 23 | void SetTicketKeys(const std::vector<VirtualFile>& files) { | 23 | void SetTicketKeys(const std::vector<VirtualFile>& files) { |
| 24 | Core::Crypto::KeyManager keys; | 24 | auto& keys = Core::Crypto::KeyManager::Instance(); |
| 25 | 25 | ||
| 26 | for (const auto& ticket_file : files) { | 26 | for (const auto& ticket_file : files) { |
| 27 | if (ticket_file == nullptr) { | 27 | if (ticket_file == nullptr) { |
| @@ -285,7 +285,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | |||
| 285 | continue; | 285 | continue; |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys); | 288 | auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); |
| 289 | if (next_nca->GetType() == NCAContentType::Program) { | 289 | if (next_nca->GetType() == NCAContentType::Program) { |
| 290 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | 290 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); |
| 291 | } | 291 | } |
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index ee9b6ce17..cf89de6a9 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -73,7 +73,7 @@ private: | |||
| 73 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; | 73 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; |
| 74 | std::vector<VirtualFile> ticket_files; | 74 | std::vector<VirtualFile> ticket_files; |
| 75 | 75 | ||
| 76 | Core::Crypto::KeyManager keys; | 76 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 77 | 77 | ||
| 78 | VirtualFile romfs; | 78 | VirtualFile romfs; |
| 79 | VirtualDir exefs; | 79 | VirtualDir exefs; |
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp index 6a9add87c..61bb67945 100644 --- a/src/core/file_sys/system_archive/mii_model.cpp +++ b/src/core/file_sys/system_archive/mii_model.cpp | |||
| @@ -40,7 +40,7 @@ VirtualDir MiiModel() { | |||
| 40 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>( | 40 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>( |
| 41 | MiiModelData::SHAPE_MID, "ShapeMid.dat")); | 41 | MiiModelData::SHAPE_MID, "ShapeMid.dat")); |
| 42 | 42 | ||
| 43 | return std::move(out); | 43 | return out; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | } // namespace FileSys::SystemArchive | 46 | } // namespace FileSys::SystemArchive |
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp index 2c05eb42e..c5cdf7d9b 100644 --- a/src/core/file_sys/system_archive/shared_font.cpp +++ b/src/core/file_sys/system_archive/shared_font.cpp | |||
| @@ -23,7 +23,7 @@ VirtualFile PackBFTTF(const std::array<u8, Size>& data, const std::string& name) | |||
| 23 | 23 | ||
| 24 | std::vector<u8> bfttf(Size + sizeof(u64)); | 24 | std::vector<u8> bfttf(Size + sizeof(u64)); |
| 25 | 25 | ||
| 26 | u64 offset = 0; | 26 | size_t offset = 0; |
| 27 | Service::NS::EncryptSharedFont(vec, bfttf, offset); | 27 | Service::NS::EncryptSharedFont(vec, bfttf, offset); |
| 28 | return std::make_shared<VectorVfsFile>(std::move(bfttf), name); | 28 | return std::make_shared<VectorVfsFile>(std::move(bfttf), name); |
| 29 | } | 29 | } |
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 7704dee90..563531bb6 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h | |||
| @@ -62,6 +62,6 @@ private: | |||
| 62 | 62 | ||
| 63 | VirtualFile dec_file; | 63 | VirtualFile dec_file; |
| 64 | 64 | ||
| 65 | Core::Crypto::KeyManager keys; | 65 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 66 | }; | 66 | }; |
| 67 | } // namespace FileSys | 67 | } // namespace FileSys |
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index d0c43447c..c1fbc235b 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp | |||
| @@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { | |||
| 29 | 29 | ||
| 30 | const float window_aspect_ratio = static_cast<float>(height) / width; | 30 | const float window_aspect_ratio = static_cast<float>(height) / width; |
| 31 | const float emulation_aspect_ratio = EmulationAspectRatio( | 31 | const float emulation_aspect_ratio = EmulationAspectRatio( |
| 32 | static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio); | 32 | static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio); |
| 33 | 33 | ||
| 34 | const Common::Rectangle<u32> screen_window_area{0, 0, width, height}; | 34 | const Common::Rectangle<u32> screen_window_area{0, 0, width, height}; |
| 35 | Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); | 35 | Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 70c0f8b80..79f22a403 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -35,7 +35,6 @@ | |||
| 35 | #include "common/swap.h" | 35 | #include "common/swap.h" |
| 36 | #include "core/arm/arm_interface.h" | 36 | #include "core/arm/arm_interface.h" |
| 37 | #include "core/core.h" | 37 | #include "core/core.h" |
| 38 | #include "core/core_manager.h" | ||
| 39 | #include "core/gdbstub/gdbstub.h" | 38 | #include "core/gdbstub/gdbstub.h" |
| 40 | #include "core/hle/kernel/memory/page_table.h" | 39 | #include "core/hle/kernel/memory/page_table.h" |
| 41 | #include "core/hle/kernel/process.h" | 40 | #include "core/hle/kernel/process.h" |
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index b04e046ed..456b41e1b 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h | |||
| @@ -42,6 +42,10 @@ struct EmuThreadHandle { | |||
| 42 | constexpr u32 invalid_handle = 0xFFFFFFFF; | 42 | constexpr u32 invalid_handle = 0xFFFFFFFF; |
| 43 | return {invalid_handle, invalid_handle}; | 43 | return {invalid_handle, invalid_handle}; |
| 44 | } | 44 | } |
| 45 | |||
| 46 | bool IsInvalid() const { | ||
| 47 | return (*this) == InvalidHandle(); | ||
| 48 | } | ||
| 45 | }; | 49 | }; |
| 46 | 50 | ||
| 47 | } // namespace Core | 51 | } // namespace Core |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 8475b698c..4d2a9b35d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -7,11 +7,15 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/arm/exclusive_monitor.h" | ||
| 10 | #include "core/core.h" | 11 | #include "core/core.h" |
| 11 | #include "core/hle/kernel/address_arbiter.h" | 12 | #include "core/hle/kernel/address_arbiter.h" |
| 12 | #include "core/hle/kernel/errors.h" | 13 | #include "core/hle/kernel/errors.h" |
| 14 | #include "core/hle/kernel/handle_table.h" | ||
| 15 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/scheduler.h" | 16 | #include "core/hle/kernel/scheduler.h" |
| 14 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 18 | #include "core/hle/kernel/time_manager.h" | ||
| 15 | #include "core/hle/result.h" | 19 | #include "core/hle/result.h" |
| 16 | #include "core/memory.h" | 20 | #include "core/memory.h" |
| 17 | 21 | ||
| @@ -20,6 +24,7 @@ namespace Kernel { | |||
| 20 | // Wake up num_to_wake (or all) threads in a vector. | 24 | // Wake up num_to_wake (or all) threads in a vector. |
| 21 | void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, | 25 | void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, |
| 22 | s32 num_to_wake) { | 26 | s32 num_to_wake) { |
| 27 | auto& time_manager = system.Kernel().TimeManager(); | ||
| 23 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process | 28 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process |
| 24 | // them all. | 29 | // them all. |
| 25 | std::size_t last = waiting_threads.size(); | 30 | std::size_t last = waiting_threads.size(); |
| @@ -29,12 +34,10 @@ void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& wai | |||
| 29 | 34 | ||
| 30 | // Signal the waiting threads. | 35 | // Signal the waiting threads. |
| 31 | for (std::size_t i = 0; i < last; i++) { | 36 | for (std::size_t i = 0; i < last; i++) { |
| 32 | ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); | 37 | waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 33 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 34 | RemoveThread(waiting_threads[i]); | 38 | RemoveThread(waiting_threads[i]); |
| 35 | waiting_threads[i]->SetArbiterWaitAddress(0); | 39 | waiting_threads[i]->WaitForArbitration(false); |
| 36 | waiting_threads[i]->ResumeFromWait(); | 40 | waiting_threads[i]->ResumeFromWait(); |
| 37 | system.PrepareReschedule(waiting_threads[i]->GetProcessorID()); | ||
| 38 | } | 41 | } |
| 39 | } | 42 | } |
| 40 | 43 | ||
| @@ -56,6 +59,7 @@ ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 v | |||
| 56 | } | 59 | } |
| 57 | 60 | ||
| 58 | ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { | 61 | ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { |
| 62 | SchedulerLock lock(system.Kernel()); | ||
| 59 | const std::vector<std::shared_ptr<Thread>> waiting_threads = | 63 | const std::vector<std::shared_ptr<Thread>> waiting_threads = |
| 60 | GetThreadsWaitingOnAddress(address); | 64 | GetThreadsWaitingOnAddress(address); |
| 61 | WakeThreads(waiting_threads, num_to_wake); | 65 | WakeThreads(waiting_threads, num_to_wake); |
| @@ -64,6 +68,7 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { | |||
| 64 | 68 | ||
| 65 | ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, | 69 | ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, |
| 66 | s32 num_to_wake) { | 70 | s32 num_to_wake) { |
| 71 | SchedulerLock lock(system.Kernel()); | ||
| 67 | auto& memory = system.Memory(); | 72 | auto& memory = system.Memory(); |
| 68 | 73 | ||
| 69 | // Ensure that we can write to the address. | 74 | // Ensure that we can write to the address. |
| @@ -71,16 +76,24 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 | |||
| 71 | return ERR_INVALID_ADDRESS_STATE; | 76 | return ERR_INVALID_ADDRESS_STATE; |
| 72 | } | 77 | } |
| 73 | 78 | ||
| 74 | if (static_cast<s32>(memory.Read32(address)) != value) { | 79 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 75 | return ERR_INVALID_STATE; | 80 | auto& monitor = system.Monitor(); |
| 76 | } | 81 | u32 current_value; |
| 82 | do { | ||
| 83 | current_value = monitor.ExclusiveRead32(current_core, address); | ||
| 84 | |||
| 85 | if (current_value != value) { | ||
| 86 | return ERR_INVALID_STATE; | ||
| 87 | } | ||
| 88 | current_value++; | ||
| 89 | } while (!monitor.ExclusiveWrite32(current_core, address, current_value)); | ||
| 77 | 90 | ||
| 78 | memory.Write32(address, static_cast<u32>(value + 1)); | ||
| 79 | return SignalToAddressOnly(address, num_to_wake); | 91 | return SignalToAddressOnly(address, num_to_wake); |
| 80 | } | 92 | } |
| 81 | 93 | ||
| 82 | ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, | 94 | ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, |
| 83 | s32 num_to_wake) { | 95 | s32 num_to_wake) { |
| 96 | SchedulerLock lock(system.Kernel()); | ||
| 84 | auto& memory = system.Memory(); | 97 | auto& memory = system.Memory(); |
| 85 | 98 | ||
| 86 | // Ensure that we can write to the address. | 99 | // Ensure that we can write to the address. |
| @@ -92,29 +105,33 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a | |||
| 92 | const std::vector<std::shared_ptr<Thread>> waiting_threads = | 105 | const std::vector<std::shared_ptr<Thread>> waiting_threads = |
| 93 | GetThreadsWaitingOnAddress(address); | 106 | GetThreadsWaitingOnAddress(address); |
| 94 | 107 | ||
| 95 | // Determine the modified value depending on the waiting count. | 108 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 109 | auto& monitor = system.Monitor(); | ||
| 96 | s32 updated_value; | 110 | s32 updated_value; |
| 97 | if (num_to_wake <= 0) { | 111 | do { |
| 98 | if (waiting_threads.empty()) { | 112 | updated_value = monitor.ExclusiveRead32(current_core, address); |
| 99 | updated_value = value + 1; | 113 | |
| 100 | } else { | 114 | if (updated_value != value) { |
| 101 | updated_value = value - 1; | 115 | return ERR_INVALID_STATE; |
| 102 | } | 116 | } |
| 103 | } else { | 117 | // Determine the modified value depending on the waiting count. |
| 104 | if (waiting_threads.empty()) { | 118 | if (num_to_wake <= 0) { |
| 105 | updated_value = value + 1; | 119 | if (waiting_threads.empty()) { |
| 106 | } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { | 120 | updated_value = value + 1; |
| 107 | updated_value = value - 1; | 121 | } else { |
| 122 | updated_value = value - 1; | ||
| 123 | } | ||
| 108 | } else { | 124 | } else { |
| 109 | updated_value = value; | 125 | if (waiting_threads.empty()) { |
| 126 | updated_value = value + 1; | ||
| 127 | } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { | ||
| 128 | updated_value = value - 1; | ||
| 129 | } else { | ||
| 130 | updated_value = value; | ||
| 131 | } | ||
| 110 | } | 132 | } |
| 111 | } | 133 | } while (!monitor.ExclusiveWrite32(current_core, address, updated_value)); |
| 112 | 134 | ||
| 113 | if (static_cast<s32>(memory.Read32(address)) != value) { | ||
| 114 | return ERR_INVALID_STATE; | ||
| 115 | } | ||
| 116 | |||
| 117 | memory.Write32(address, static_cast<u32>(updated_value)); | ||
| 118 | WakeThreads(waiting_threads, num_to_wake); | 135 | WakeThreads(waiting_threads, num_to_wake); |
| 119 | return RESULT_SUCCESS; | 136 | return RESULT_SUCCESS; |
| 120 | } | 137 | } |
| @@ -136,60 +153,127 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s | |||
| 136 | ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, | 153 | ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, |
| 137 | bool should_decrement) { | 154 | bool should_decrement) { |
| 138 | auto& memory = system.Memory(); | 155 | auto& memory = system.Memory(); |
| 156 | auto& kernel = system.Kernel(); | ||
| 157 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 139 | 158 | ||
| 140 | // Ensure that we can read the address. | 159 | Handle event_handle = InvalidHandle; |
| 141 | if (!memory.IsValidVirtualAddress(address)) { | 160 | { |
| 142 | return ERR_INVALID_ADDRESS_STATE; | 161 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
| 143 | } | 162 | |
| 163 | if (current_thread->IsPendingTermination()) { | ||
| 164 | lock.CancelSleep(); | ||
| 165 | return ERR_THREAD_TERMINATING; | ||
| 166 | } | ||
| 167 | |||
| 168 | // Ensure that we can read the address. | ||
| 169 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 170 | lock.CancelSleep(); | ||
| 171 | return ERR_INVALID_ADDRESS_STATE; | ||
| 172 | } | ||
| 173 | |||
| 174 | s32 current_value = static_cast<s32>(memory.Read32(address)); | ||
| 175 | if (current_value >= value) { | ||
| 176 | lock.CancelSleep(); | ||
| 177 | return ERR_INVALID_STATE; | ||
| 178 | } | ||
| 179 | |||
| 180 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 181 | |||
| 182 | s32 decrement_value; | ||
| 183 | |||
| 184 | const std::size_t current_core = system.CurrentCoreIndex(); | ||
| 185 | auto& monitor = system.Monitor(); | ||
| 186 | do { | ||
| 187 | current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); | ||
| 188 | if (should_decrement) { | ||
| 189 | decrement_value = current_value - 1; | ||
| 190 | } else { | ||
| 191 | decrement_value = current_value; | ||
| 192 | } | ||
| 193 | } while ( | ||
| 194 | !monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))); | ||
| 195 | |||
| 196 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 197 | if (timeout == 0) { | ||
| 198 | lock.CancelSleep(); | ||
| 199 | return RESULT_TIMEOUT; | ||
| 200 | } | ||
| 144 | 201 | ||
| 145 | const s32 cur_value = static_cast<s32>(memory.Read32(address)); | 202 | current_thread->SetArbiterWaitAddress(address); |
| 146 | if (cur_value >= value) { | 203 | InsertThread(SharedFrom(current_thread)); |
| 147 | return ERR_INVALID_STATE; | 204 | current_thread->SetStatus(ThreadStatus::WaitArb); |
| 205 | current_thread->WaitForArbitration(true); | ||
| 148 | } | 206 | } |
| 149 | 207 | ||
| 150 | if (should_decrement) { | 208 | if (event_handle != InvalidHandle) { |
| 151 | memory.Write32(address, static_cast<u32>(cur_value - 1)); | 209 | auto& time_manager = kernel.TimeManager(); |
| 210 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 152 | } | 211 | } |
| 153 | 212 | ||
| 154 | // Short-circuit without rescheduling, if timeout is zero. | 213 | { |
| 155 | if (timeout == 0) { | 214 | SchedulerLock lock(kernel); |
| 156 | return RESULT_TIMEOUT; | 215 | if (current_thread->IsWaitingForArbitration()) { |
| 216 | RemoveThread(SharedFrom(current_thread)); | ||
| 217 | current_thread->WaitForArbitration(false); | ||
| 218 | } | ||
| 157 | } | 219 | } |
| 158 | 220 | ||
| 159 | return WaitForAddressImpl(address, timeout); | 221 | return current_thread->GetSignalingResult(); |
| 160 | } | 222 | } |
| 161 | 223 | ||
| 162 | ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { | 224 | ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { |
| 163 | auto& memory = system.Memory(); | 225 | auto& memory = system.Memory(); |
| 226 | auto& kernel = system.Kernel(); | ||
| 227 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 164 | 228 | ||
| 165 | // Ensure that we can read the address. | 229 | Handle event_handle = InvalidHandle; |
| 166 | if (!memory.IsValidVirtualAddress(address)) { | 230 | { |
| 167 | return ERR_INVALID_ADDRESS_STATE; | 231 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
| 168 | } | 232 | |
| 233 | if (current_thread->IsPendingTermination()) { | ||
| 234 | lock.CancelSleep(); | ||
| 235 | return ERR_THREAD_TERMINATING; | ||
| 236 | } | ||
| 237 | |||
| 238 | // Ensure that we can read the address. | ||
| 239 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 240 | lock.CancelSleep(); | ||
| 241 | return ERR_INVALID_ADDRESS_STATE; | ||
| 242 | } | ||
| 169 | 243 | ||
| 170 | // Only wait for the address if equal. | 244 | s32 current_value = static_cast<s32>(memory.Read32(address)); |
| 171 | if (static_cast<s32>(memory.Read32(address)) != value) { | 245 | if (current_value != value) { |
| 172 | return ERR_INVALID_STATE; | 246 | lock.CancelSleep(); |
| 247 | return ERR_INVALID_STATE; | ||
| 248 | } | ||
| 249 | |||
| 250 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 251 | if (timeout == 0) { | ||
| 252 | lock.CancelSleep(); | ||
| 253 | return RESULT_TIMEOUT; | ||
| 254 | } | ||
| 255 | |||
| 256 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 257 | current_thread->SetArbiterWaitAddress(address); | ||
| 258 | InsertThread(SharedFrom(current_thread)); | ||
| 259 | current_thread->SetStatus(ThreadStatus::WaitArb); | ||
| 260 | current_thread->WaitForArbitration(true); | ||
| 173 | } | 261 | } |
| 174 | 262 | ||
| 175 | // Short-circuit without rescheduling if timeout is zero. | 263 | if (event_handle != InvalidHandle) { |
| 176 | if (timeout == 0) { | 264 | auto& time_manager = kernel.TimeManager(); |
| 177 | return RESULT_TIMEOUT; | 265 | time_manager.UnscheduleTimeEvent(event_handle); |
| 178 | } | 266 | } |
| 179 | 267 | ||
| 180 | return WaitForAddressImpl(address, timeout); | 268 | { |
| 181 | } | 269 | SchedulerLock lock(kernel); |
| 270 | if (current_thread->IsWaitingForArbitration()) { | ||
| 271 | RemoveThread(SharedFrom(current_thread)); | ||
| 272 | current_thread->WaitForArbitration(false); | ||
| 273 | } | ||
| 274 | } | ||
| 182 | 275 | ||
| 183 | ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) { | 276 | return current_thread->GetSignalingResult(); |
| 184 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 185 | current_thread->SetArbiterWaitAddress(address); | ||
| 186 | InsertThread(SharedFrom(current_thread)); | ||
| 187 | current_thread->SetStatus(ThreadStatus::WaitArb); | ||
| 188 | current_thread->InvalidateWakeupCallback(); | ||
| 189 | current_thread->WakeAfterDelay(timeout); | ||
| 190 | |||
| 191 | system.PrepareReschedule(current_thread->GetProcessorID()); | ||
| 192 | return RESULT_TIMEOUT; | ||
| 193 | } | 277 | } |
| 194 | 278 | ||
| 195 | void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) { | 279 | void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) { |
| @@ -221,9 +305,9 @@ void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) { | |||
| 221 | const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), | 305 | const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), |
| 222 | [&thread](const auto& entry) { return thread == entry; }); | 306 | [&thread](const auto& entry) { return thread == entry; }); |
| 223 | 307 | ||
| 224 | ASSERT(iter != thread_list.cend()); | 308 | if (iter != thread_list.cend()) { |
| 225 | 309 | thread_list.erase(iter); | |
| 226 | thread_list.erase(iter); | 310 | } |
| 227 | } | 311 | } |
| 228 | 312 | ||
| 229 | std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( | 313 | std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( |
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index f958eee5a..0b05d533c 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -73,9 +73,6 @@ private: | |||
| 73 | /// Waits on an address if the value passed is equal to the argument value. | 73 | /// Waits on an address if the value passed is equal to the argument value. |
| 74 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); | 74 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); |
| 75 | 75 | ||
| 76 | // Waits on the given address with a timeout in nanoseconds | ||
| 77 | ResultCode WaitForAddressImpl(VAddr address, s64 timeout); | ||
| 78 | |||
| 79 | /// Wake up num_to_wake (or all) threads in a vector. | 76 | /// Wake up num_to_wake (or all) threads in a vector. |
| 80 | void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); | 77 | void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); |
| 81 | 78 | ||
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 5498fd313..8aff2227a 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp | |||
| @@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() { | |||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | // Wake the threads waiting on the ServerPort | 36 | // Wake the threads waiting on the ServerPort |
| 37 | server_port->WakeupAllWaitingThreads(); | 37 | server_port->Signal(); |
| 38 | 38 | ||
| 39 | return MakeResult(std::move(client)); | 39 | return MakeResult(std::move(client)); |
| 40 | } | 40 | } |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 29bfa3621..d4e5d88cf 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -12,6 +12,7 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; | 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; |
| 14 | constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; | 14 | constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; |
| 15 | constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; | ||
| 15 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; | 16 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; |
| 16 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; | 17 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; |
| 17 | constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; | 18 | constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index ba0eac4c2..9277b5d08 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -14,14 +14,17 @@ | |||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "core/hle/ipc_helpers.h" | 16 | #include "core/hle/ipc_helpers.h" |
| 17 | #include "core/hle/kernel/errors.h" | ||
| 17 | #include "core/hle/kernel/handle_table.h" | 18 | #include "core/hle/kernel/handle_table.h" |
| 18 | #include "core/hle/kernel/hle_ipc.h" | 19 | #include "core/hle/kernel/hle_ipc.h" |
| 19 | #include "core/hle/kernel/kernel.h" | 20 | #include "core/hle/kernel/kernel.h" |
| 20 | #include "core/hle/kernel/object.h" | 21 | #include "core/hle/kernel/object.h" |
| 21 | #include "core/hle/kernel/process.h" | 22 | #include "core/hle/kernel/process.h" |
| 22 | #include "core/hle/kernel/readable_event.h" | 23 | #include "core/hle/kernel/readable_event.h" |
| 24 | #include "core/hle/kernel/scheduler.h" | ||
| 23 | #include "core/hle/kernel/server_session.h" | 25 | #include "core/hle/kernel/server_session.h" |
| 24 | #include "core/hle/kernel/thread.h" | 26 | #include "core/hle/kernel/thread.h" |
| 27 | #include "core/hle/kernel/time_manager.h" | ||
| 25 | #include "core/hle/kernel/writable_event.h" | 28 | #include "core/hle/kernel/writable_event.h" |
| 26 | #include "core/memory.h" | 29 | #include "core/memory.h" |
| 27 | 30 | ||
| @@ -46,15 +49,6 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 46 | const std::string& reason, u64 timeout, WakeupCallback&& callback, | 49 | const std::string& reason, u64 timeout, WakeupCallback&& callback, |
| 47 | std::shared_ptr<WritableEvent> writable_event) { | 50 | std::shared_ptr<WritableEvent> writable_event) { |
| 48 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | 51 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. |
| 49 | thread->SetWakeupCallback( | ||
| 50 | [context = *this, callback](ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | ||
| 51 | std::shared_ptr<SynchronizationObject> object, | ||
| 52 | std::size_t index) mutable -> bool { | ||
| 53 | ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent); | ||
| 54 | callback(thread, context, reason); | ||
| 55 | context.WriteToOutgoingCommandBuffer(*thread); | ||
| 56 | return true; | ||
| 57 | }); | ||
| 58 | 52 | ||
| 59 | if (!writable_event) { | 53 | if (!writable_event) { |
| 60 | // Create event if not provided | 54 | // Create event if not provided |
| @@ -62,14 +56,26 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 62 | writable_event = pair.writable; | 56 | writable_event = pair.writable; |
| 63 | } | 57 | } |
| 64 | 58 | ||
| 65 | const auto readable_event{writable_event->GetReadableEvent()}; | 59 | { |
| 66 | writable_event->Clear(); | 60 | Handle event_handle = InvalidHandle; |
| 67 | thread->SetStatus(ThreadStatus::WaitHLEEvent); | 61 | SchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout); |
| 68 | thread->SetSynchronizationObjects({readable_event}); | 62 | thread->SetHLECallback( |
| 69 | readable_event->AddWaitingThread(thread); | 63 | [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool { |
| 70 | 64 | ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT | |
| 71 | if (timeout > 0) { | 65 | ? ThreadWakeupReason::Timeout |
| 72 | thread->WakeAfterDelay(timeout); | 66 | : ThreadWakeupReason::Signal; |
| 67 | callback(thread, context, reason); | ||
| 68 | context.WriteToOutgoingCommandBuffer(*thread); | ||
| 69 | return true; | ||
| 70 | }); | ||
| 71 | const auto readable_event{writable_event->GetReadableEvent()}; | ||
| 72 | writable_event->Clear(); | ||
| 73 | thread->SetHLESyncObject(readable_event.get()); | ||
| 74 | thread->SetStatus(ThreadStatus::WaitHLEEvent); | ||
| 75 | thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 76 | readable_event->AddWaitingThread(thread); | ||
| 77 | lock.Release(); | ||
| 78 | thread->SetHLETimeEvent(event_handle); | ||
| 73 | } | 79 | } |
| 74 | 80 | ||
| 75 | is_thread_waiting = true; | 81 | is_thread_waiting = true; |
| @@ -282,18 +288,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { | |||
| 282 | } | 288 | } |
| 283 | 289 | ||
| 284 | std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { | 290 | std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { |
| 285 | std::vector<u8> buffer; | 291 | std::vector<u8> buffer{}; |
| 286 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | 292 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 287 | BufferDescriptorA()[buffer_index].Size()}; | 293 | BufferDescriptorA()[buffer_index].Size()}; |
| 288 | 294 | ||
| 289 | if (is_buffer_a) { | 295 | if (is_buffer_a) { |
| 290 | ASSERT_MSG(BufferDescriptorA().size() > buffer_index, | 296 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, |
| 291 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 297 | "BufferDescriptorA invalid buffer_index {}", buffer_index); |
| 292 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); | 298 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); |
| 293 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); | 299 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 294 | } else { | 300 | } else { |
| 295 | ASSERT_MSG(BufferDescriptorX().size() > buffer_index, | 301 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, |
| 296 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 302 | "BufferDescriptorX invalid buffer_index {}", buffer_index); |
| 297 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); | 303 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); |
| 298 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); | 304 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 299 | } | 305 | } |
| @@ -318,16 +324,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | |||
| 318 | } | 324 | } |
| 319 | 325 | ||
| 320 | if (is_buffer_b) { | 326 | if (is_buffer_b) { |
| 321 | ASSERT_MSG(BufferDescriptorB().size() > buffer_index, | 327 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && |
| 322 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | 328 | BufferDescriptorB()[buffer_index].Size() >= size, |
| 323 | ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, | 329 | { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", |
| 324 | "BufferDescriptorB buffer_index {} is not large enough", buffer_index); | 330 | buffer_index, size); |
| 325 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | 331 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); |
| 326 | } else { | 332 | } else { |
| 327 | ASSERT_MSG(BufferDescriptorC().size() > buffer_index, | 333 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && |
| 328 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | 334 | BufferDescriptorC()[buffer_index].Size() >= size, |
| 329 | ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, | 335 | { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", |
| 330 | "BufferDescriptorC buffer_index {} is not large enough", buffer_index); | 336 | buffer_index, size); |
| 331 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | 337 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); |
| 332 | } | 338 | } |
| 333 | 339 | ||
| @@ -338,16 +344,12 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const | |||
| 338 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | 344 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 339 | BufferDescriptorA()[buffer_index].Size()}; | 345 | BufferDescriptorA()[buffer_index].Size()}; |
| 340 | if (is_buffer_a) { | 346 | if (is_buffer_a) { |
| 341 | ASSERT_MSG(BufferDescriptorA().size() > buffer_index, | 347 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, |
| 342 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 348 | "BufferDescriptorA invalid buffer_index {}", buffer_index); |
| 343 | ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0, | ||
| 344 | "BufferDescriptorA buffer_index {} is empty", buffer_index); | ||
| 345 | return BufferDescriptorA()[buffer_index].Size(); | 349 | return BufferDescriptorA()[buffer_index].Size(); |
| 346 | } else { | 350 | } else { |
| 347 | ASSERT_MSG(BufferDescriptorX().size() > buffer_index, | 351 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, |
| 348 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 352 | "BufferDescriptorX invalid buffer_index {}", buffer_index); |
| 349 | ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0, | ||
| 350 | "BufferDescriptorX buffer_index {} is empty", buffer_index); | ||
| 351 | return BufferDescriptorX()[buffer_index].Size(); | 353 | return BufferDescriptorX()[buffer_index].Size(); |
| 352 | } | 354 | } |
| 353 | } | 355 | } |
| @@ -356,14 +358,15 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons | |||
| 356 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && | 358 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && |
| 357 | BufferDescriptorB()[buffer_index].Size()}; | 359 | BufferDescriptorB()[buffer_index].Size()}; |
| 358 | if (is_buffer_b) { | 360 | if (is_buffer_b) { |
| 359 | ASSERT_MSG(BufferDescriptorB().size() > buffer_index, | 361 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, |
| 360 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | 362 | "BufferDescriptorB invalid buffer_index {}", buffer_index); |
| 361 | return BufferDescriptorB()[buffer_index].Size(); | 363 | return BufferDescriptorB()[buffer_index].Size(); |
| 362 | } else { | 364 | } else { |
| 363 | ASSERT_MSG(BufferDescriptorC().size() > buffer_index, | 365 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, |
| 364 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | 366 | "BufferDescriptorC invalid buffer_index {}", buffer_index); |
| 365 | return BufferDescriptorC()[buffer_index].Size(); | 367 | return BufferDescriptorC()[buffer_index].Size(); |
| 366 | } | 368 | } |
| 369 | return 0; | ||
| 367 | } | 370 | } |
| 368 | 371 | ||
| 369 | std::string HLERequestContext::Description() const { | 372 | std::string HLERequestContext::Description() const { |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7655382fa..1f2af7a1b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 5 | #include <atomic> | 6 | #include <atomic> |
| 6 | #include <bitset> | 7 | #include <bitset> |
| 7 | #include <functional> | 8 | #include <functional> |
| @@ -13,11 +14,15 @@ | |||
| 13 | 14 | ||
| 14 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| 15 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 17 | #include "common/microprofile.h" | ||
| 18 | #include "common/thread.h" | ||
| 16 | #include "core/arm/arm_interface.h" | 19 | #include "core/arm/arm_interface.h" |
| 20 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 17 | #include "core/arm/exclusive_monitor.h" | 21 | #include "core/arm/exclusive_monitor.h" |
| 18 | #include "core/core.h" | 22 | #include "core/core.h" |
| 19 | #include "core/core_timing.h" | 23 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 24 | #include "core/core_timing_util.h" |
| 25 | #include "core/cpu_manager.h" | ||
| 21 | #include "core/device_memory.h" | 26 | #include "core/device_memory.h" |
| 22 | #include "core/hardware_properties.h" | 27 | #include "core/hardware_properties.h" |
| 23 | #include "core/hle/kernel/client_port.h" | 28 | #include "core/hle/kernel/client_port.h" |
| @@ -39,85 +44,28 @@ | |||
| 39 | #include "core/hle/result.h" | 44 | #include "core/hle/result.h" |
| 40 | #include "core/memory.h" | 45 | #include "core/memory.h" |
| 41 | 46 | ||
| 42 | namespace Kernel { | 47 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); |
| 43 | |||
| 44 | /** | ||
| 45 | * Callback that will wake up the thread it was scheduled for | ||
| 46 | * @param thread_handle The handle of the thread that's been awoken | ||
| 47 | * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time | ||
| 48 | */ | ||
| 49 | static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | ||
| 50 | const auto proper_handle = static_cast<Handle>(thread_handle); | ||
| 51 | const auto& system = Core::System::GetInstance(); | ||
| 52 | |||
| 53 | // Lock the global kernel mutex when we enter the kernel HLE. | ||
| 54 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 55 | |||
| 56 | std::shared_ptr<Thread> thread = | ||
| 57 | system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | ||
| 58 | if (thread == nullptr) { | ||
| 59 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | |||
| 63 | bool resume = true; | ||
| 64 | |||
| 65 | if (thread->GetStatus() == ThreadStatus::WaitSynch || | ||
| 66 | thread->GetStatus() == ThreadStatus::WaitHLEEvent) { | ||
| 67 | // Remove the thread from each of its waiting objects' waitlists | ||
| 68 | for (const auto& object : thread->GetSynchronizationObjects()) { | ||
| 69 | object->RemoveWaitingThread(thread); | ||
| 70 | } | ||
| 71 | thread->ClearSynchronizationObjects(); | ||
| 72 | |||
| 73 | // Invoke the wakeup callback before clearing the wait objects | ||
| 74 | if (thread->HasWakeupCallback()) { | ||
| 75 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0); | ||
| 76 | } | ||
| 77 | } else if (thread->GetStatus() == ThreadStatus::WaitMutex || | ||
| 78 | thread->GetStatus() == ThreadStatus::WaitCondVar) { | ||
| 79 | thread->SetMutexWaitAddress(0); | ||
| 80 | thread->SetWaitHandle(0); | ||
| 81 | if (thread->GetStatus() == ThreadStatus::WaitCondVar) { | ||
| 82 | thread->GetOwnerProcess()->RemoveConditionVariableThread(thread); | ||
| 83 | thread->SetCondVarWaitAddress(0); | ||
| 84 | } | ||
| 85 | |||
| 86 | auto* const lock_owner = thread->GetLockOwner(); | ||
| 87 | // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance | ||
| 88 | // and don't have a lock owner unless SignalProcessWideKey was called first and the thread | ||
| 89 | // wasn't awakened due to the mutex already being acquired. | ||
| 90 | if (lock_owner != nullptr) { | ||
| 91 | lock_owner->RemoveMutexWaiter(thread); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | 48 | ||
| 95 | if (thread->GetStatus() == ThreadStatus::WaitArb) { | 49 | namespace Kernel { |
| 96 | auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter(); | ||
| 97 | address_arbiter.HandleWakeupThread(thread); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (resume) { | ||
| 101 | if (thread->GetStatus() == ThreadStatus::WaitCondVar || | ||
| 102 | thread->GetStatus() == ThreadStatus::WaitArb) { | ||
| 103 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 104 | } | ||
| 105 | thread->ResumeFromWait(); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | 50 | ||
| 109 | struct KernelCore::Impl { | 51 | struct KernelCore::Impl { |
| 110 | explicit Impl(Core::System& system, KernelCore& kernel) | 52 | explicit Impl(Core::System& system, KernelCore& kernel) |
| 111 | : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} | 53 | : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} |
| 112 | 54 | ||
| 55 | void SetMulticore(bool is_multicore) { | ||
| 56 | this->is_multicore = is_multicore; | ||
| 57 | } | ||
| 58 | |||
| 113 | void Initialize(KernelCore& kernel) { | 59 | void Initialize(KernelCore& kernel) { |
| 114 | Shutdown(); | 60 | Shutdown(); |
| 61 | RegisterHostThread(); | ||
| 115 | 62 | ||
| 116 | InitializePhysicalCores(); | 63 | InitializePhysicalCores(); |
| 117 | InitializeSystemResourceLimit(kernel); | 64 | InitializeSystemResourceLimit(kernel); |
| 118 | InitializeMemoryLayout(); | 65 | InitializeMemoryLayout(); |
| 119 | InitializeThreads(); | 66 | InitializePreemption(kernel); |
| 120 | InitializePreemption(); | 67 | InitializeSchedulers(); |
| 68 | InitializeSuspendThreads(); | ||
| 121 | } | 69 | } |
| 122 | 70 | ||
| 123 | void Shutdown() { | 71 | void Shutdown() { |
| @@ -126,13 +74,26 @@ struct KernelCore::Impl { | |||
| 126 | next_user_process_id = Process::ProcessIDMin; | 74 | next_user_process_id = Process::ProcessIDMin; |
| 127 | next_thread_id = 1; | 75 | next_thread_id = 1; |
| 128 | 76 | ||
| 77 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 78 | if (suspend_threads[i]) { | ||
| 79 | suspend_threads[i].reset(); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | for (std::size_t i = 0; i < cores.size(); i++) { | ||
| 84 | cores[i].Shutdown(); | ||
| 85 | schedulers[i].reset(); | ||
| 86 | } | ||
| 87 | cores.clear(); | ||
| 88 | |||
| 89 | registered_core_threads.reset(); | ||
| 90 | |||
| 129 | process_list.clear(); | 91 | process_list.clear(); |
| 130 | current_process = nullptr; | 92 | current_process = nullptr; |
| 131 | 93 | ||
| 132 | system_resource_limit = nullptr; | 94 | system_resource_limit = nullptr; |
| 133 | 95 | ||
| 134 | global_handle_table.Clear(); | 96 | global_handle_table.Clear(); |
| 135 | thread_wakeup_event_type = nullptr; | ||
| 136 | preemption_event = nullptr; | 97 | preemption_event = nullptr; |
| 137 | 98 | ||
| 138 | global_scheduler.Shutdown(); | 99 | global_scheduler.Shutdown(); |
| @@ -145,13 +106,21 @@ struct KernelCore::Impl { | |||
| 145 | cores.clear(); | 106 | cores.clear(); |
| 146 | 107 | ||
| 147 | exclusive_monitor.reset(); | 108 | exclusive_monitor.reset(); |
| 109 | host_thread_ids.clear(); | ||
| 148 | } | 110 | } |
| 149 | 111 | ||
| 150 | void InitializePhysicalCores() { | 112 | void InitializePhysicalCores() { |
| 151 | exclusive_monitor = | 113 | exclusive_monitor = |
| 152 | Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); | 114 | Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); |
| 153 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | 115 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { |
| 154 | cores.emplace_back(system, i, *exclusive_monitor); | 116 | schedulers[i] = std::make_unique<Kernel::Scheduler>(system, i); |
| 117 | cores.emplace_back(system, i, *schedulers[i], interrupts[i]); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | void InitializeSchedulers() { | ||
| 122 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 123 | cores[i].Scheduler().Initialize(); | ||
| 155 | } | 124 | } |
| 156 | } | 125 | } |
| 157 | 126 | ||
| @@ -173,15 +142,13 @@ struct KernelCore::Impl { | |||
| 173 | } | 142 | } |
| 174 | } | 143 | } |
| 175 | 144 | ||
| 176 | void InitializeThreads() { | 145 | void InitializePreemption(KernelCore& kernel) { |
| 177 | thread_wakeup_event_type = | 146 | preemption_event = Core::Timing::CreateEvent( |
| 178 | Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); | 147 | "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { |
| 179 | } | 148 | { |
| 180 | 149 | SchedulerLock lock(kernel); | |
| 181 | void InitializePreemption() { | 150 | global_scheduler.PreemptThreads(); |
| 182 | preemption_event = | 151 | } |
| 183 | Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) { | ||
| 184 | global_scheduler.PreemptThreads(); | ||
| 185 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | 152 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); |
| 186 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 153 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 187 | }); | 154 | }); |
| @@ -190,6 +157,20 @@ struct KernelCore::Impl { | |||
| 190 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 157 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 191 | } | 158 | } |
| 192 | 159 | ||
| 160 | void InitializeSuspendThreads() { | ||
| 161 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 162 | std::string name = "Suspend Thread Id:" + std::to_string(i); | ||
| 163 | std::function<void(void*)> init_func = | ||
| 164 | system.GetCpuManager().GetSuspendThreadStartFunc(); | ||
| 165 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 166 | ThreadType type = | ||
| 167 | static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND); | ||
| 168 | auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0, | ||
| 169 | nullptr, std::move(init_func), init_func_parameter); | ||
| 170 | suspend_threads[i] = std::move(thread_res).Unwrap(); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 193 | void MakeCurrentProcess(Process* process) { | 174 | void MakeCurrentProcess(Process* process) { |
| 194 | current_process = process; | 175 | current_process = process; |
| 195 | 176 | ||
| @@ -197,15 +178,17 @@ struct KernelCore::Impl { | |||
| 197 | return; | 178 | return; |
| 198 | } | 179 | } |
| 199 | 180 | ||
| 200 | for (auto& core : cores) { | 181 | u32 core_id = GetCurrentHostThreadID(); |
| 201 | core.SetIs64Bit(process->Is64BitProcess()); | 182 | if (core_id < Core::Hardware::NUM_CPU_CORES) { |
| 183 | system.Memory().SetCurrentPageTable(*process, core_id); | ||
| 202 | } | 184 | } |
| 203 | |||
| 204 | system.Memory().SetCurrentPageTable(*process); | ||
| 205 | } | 185 | } |
| 206 | 186 | ||
| 207 | void RegisterCoreThread(std::size_t core_id) { | 187 | void RegisterCoreThread(std::size_t core_id) { |
| 208 | std::unique_lock lock{register_thread_mutex}; | 188 | std::unique_lock lock{register_thread_mutex}; |
| 189 | if (!is_multicore) { | ||
| 190 | single_core_thread_id = std::this_thread::get_id(); | ||
| 191 | } | ||
| 209 | const std::thread::id this_id = std::this_thread::get_id(); | 192 | const std::thread::id this_id = std::this_thread::get_id(); |
| 210 | const auto it = host_thread_ids.find(this_id); | 193 | const auto it = host_thread_ids.find(this_id); |
| 211 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | 194 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); |
| @@ -219,12 +202,19 @@ struct KernelCore::Impl { | |||
| 219 | std::unique_lock lock{register_thread_mutex}; | 202 | std::unique_lock lock{register_thread_mutex}; |
| 220 | const std::thread::id this_id = std::this_thread::get_id(); | 203 | const std::thread::id this_id = std::this_thread::get_id(); |
| 221 | const auto it = host_thread_ids.find(this_id); | 204 | const auto it = host_thread_ids.find(this_id); |
| 222 | ASSERT(it == host_thread_ids.end()); | 205 | if (it != host_thread_ids.end()) { |
| 206 | return; | ||
| 207 | } | ||
| 223 | host_thread_ids[this_id] = registered_thread_ids++; | 208 | host_thread_ids[this_id] = registered_thread_ids++; |
| 224 | } | 209 | } |
| 225 | 210 | ||
| 226 | u32 GetCurrentHostThreadID() const { | 211 | u32 GetCurrentHostThreadID() const { |
| 227 | const std::thread::id this_id = std::this_thread::get_id(); | 212 | const std::thread::id this_id = std::this_thread::get_id(); |
| 213 | if (!is_multicore) { | ||
| 214 | if (single_core_thread_id == this_id) { | ||
| 215 | return static_cast<u32>(system.GetCpuManager().CurrentCore()); | ||
| 216 | } | ||
| 217 | } | ||
| 228 | const auto it = host_thread_ids.find(this_id); | 218 | const auto it = host_thread_ids.find(this_id); |
| 229 | if (it == host_thread_ids.end()) { | 219 | if (it == host_thread_ids.end()) { |
| 230 | return Core::INVALID_HOST_THREAD_ID; | 220 | return Core::INVALID_HOST_THREAD_ID; |
| @@ -240,7 +230,7 @@ struct KernelCore::Impl { | |||
| 240 | } | 230 | } |
| 241 | const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); | 231 | const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); |
| 242 | const Kernel::Thread* current = sched.GetCurrentThread(); | 232 | const Kernel::Thread* current = sched.GetCurrentThread(); |
| 243 | if (current != nullptr) { | 233 | if (current != nullptr && !current->IsPhantomMode()) { |
| 244 | result.guest_handle = current->GetGlobalHandle(); | 234 | result.guest_handle = current->GetGlobalHandle(); |
| 245 | } else { | 235 | } else { |
| 246 | result.guest_handle = InvalidHandle; | 236 | result.guest_handle = InvalidHandle; |
| @@ -313,7 +303,6 @@ struct KernelCore::Impl { | |||
| 313 | 303 | ||
| 314 | std::shared_ptr<ResourceLimit> system_resource_limit; | 304 | std::shared_ptr<ResourceLimit> system_resource_limit; |
| 315 | 305 | ||
| 316 | std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; | ||
| 317 | std::shared_ptr<Core::Timing::EventType> preemption_event; | 306 | std::shared_ptr<Core::Timing::EventType> preemption_event; |
| 318 | 307 | ||
| 319 | // This is the kernel's handle table or supervisor handle table which | 308 | // This is the kernel's handle table or supervisor handle table which |
| @@ -343,6 +332,15 @@ struct KernelCore::Impl { | |||
| 343 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; | 332 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; |
| 344 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; | 333 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; |
| 345 | 334 | ||
| 335 | std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; | ||
| 336 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; | ||
| 337 | std::array<std::unique_ptr<Kernel::Scheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; | ||
| 338 | |||
| 339 | bool is_multicore{}; | ||
| 340 | std::thread::id single_core_thread_id{}; | ||
| 341 | |||
| 342 | std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; | ||
| 343 | |||
| 346 | // System context | 344 | // System context |
| 347 | Core::System& system; | 345 | Core::System& system; |
| 348 | }; | 346 | }; |
| @@ -352,6 +350,10 @@ KernelCore::~KernelCore() { | |||
| 352 | Shutdown(); | 350 | Shutdown(); |
| 353 | } | 351 | } |
| 354 | 352 | ||
| 353 | void KernelCore::SetMulticore(bool is_multicore) { | ||
| 354 | impl->SetMulticore(is_multicore); | ||
| 355 | } | ||
| 356 | |||
| 355 | void KernelCore::Initialize() { | 357 | void KernelCore::Initialize() { |
| 356 | impl->Initialize(*this); | 358 | impl->Initialize(*this); |
| 357 | } | 359 | } |
| @@ -397,11 +399,11 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { | |||
| 397 | } | 399 | } |
| 398 | 400 | ||
| 399 | Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { | 401 | Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { |
| 400 | return impl->cores[id].Scheduler(); | 402 | return *impl->schedulers[id]; |
| 401 | } | 403 | } |
| 402 | 404 | ||
| 403 | const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { | 405 | const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { |
| 404 | return impl->cores[id].Scheduler(); | 406 | return *impl->schedulers[id]; |
| 405 | } | 407 | } |
| 406 | 408 | ||
| 407 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | 409 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { |
| @@ -412,6 +414,39 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | |||
| 412 | return impl->cores[id]; | 414 | return impl->cores[id]; |
| 413 | } | 415 | } |
| 414 | 416 | ||
| 417 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { | ||
| 418 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 419 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 420 | return impl->cores[core_id]; | ||
| 421 | } | ||
| 422 | |||
| 423 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { | ||
| 424 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 425 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 426 | return impl->cores[core_id]; | ||
| 427 | } | ||
| 428 | |||
| 429 | Kernel::Scheduler& KernelCore::CurrentScheduler() { | ||
| 430 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 431 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 432 | return *impl->schedulers[core_id]; | ||
| 433 | } | ||
| 434 | |||
| 435 | const Kernel::Scheduler& KernelCore::CurrentScheduler() const { | ||
| 436 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 437 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 438 | return *impl->schedulers[core_id]; | ||
| 439 | } | ||
| 440 | |||
| 441 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() { | ||
| 442 | return impl->interrupts; | ||
| 443 | } | ||
| 444 | |||
| 445 | const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() | ||
| 446 | const { | ||
| 447 | return impl->interrupts; | ||
| 448 | } | ||
| 449 | |||
| 415 | Kernel::Synchronization& KernelCore::Synchronization() { | 450 | Kernel::Synchronization& KernelCore::Synchronization() { |
| 416 | return impl->synchronization; | 451 | return impl->synchronization; |
| 417 | } | 452 | } |
| @@ -437,15 +472,17 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { | |||
| 437 | } | 472 | } |
| 438 | 473 | ||
| 439 | void KernelCore::InvalidateAllInstructionCaches() { | 474 | void KernelCore::InvalidateAllInstructionCaches() { |
| 440 | for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) { | 475 | auto& threads = GlobalScheduler().GetThreadList(); |
| 441 | PhysicalCore(i).ArmInterface().ClearInstructionCache(); | 476 | for (auto& thread : threads) { |
| 477 | if (!thread->IsHLEThread()) { | ||
| 478 | auto& arm_interface = thread->ArmInterface(); | ||
| 479 | arm_interface.ClearInstructionCache(); | ||
| 480 | } | ||
| 442 | } | 481 | } |
| 443 | } | 482 | } |
| 444 | 483 | ||
| 445 | void KernelCore::PrepareReschedule(std::size_t id) { | 484 | void KernelCore::PrepareReschedule(std::size_t id) { |
| 446 | if (id < impl->global_scheduler.CpuCoresCount()) { | 485 | // TODO: Reimplement, this |
| 447 | impl->cores[id].Stop(); | ||
| 448 | } | ||
| 449 | } | 486 | } |
| 450 | 487 | ||
| 451 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { | 488 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { |
| @@ -481,10 +518,6 @@ u64 KernelCore::CreateNewUserProcessID() { | |||
| 481 | return impl->next_user_process_id++; | 518 | return impl->next_user_process_id++; |
| 482 | } | 519 | } |
| 483 | 520 | ||
| 484 | const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallbackEventType() const { | ||
| 485 | return impl->thread_wakeup_event_type; | ||
| 486 | } | ||
| 487 | |||
| 488 | Kernel::HandleTable& KernelCore::GlobalHandleTable() { | 521 | Kernel::HandleTable& KernelCore::GlobalHandleTable() { |
| 489 | return impl->global_handle_table; | 522 | return impl->global_handle_table; |
| 490 | } | 523 | } |
| @@ -557,4 +590,34 @@ const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const { | |||
| 557 | return *impl->time_shared_mem; | 590 | return *impl->time_shared_mem; |
| 558 | } | 591 | } |
| 559 | 592 | ||
| 593 | void KernelCore::Suspend(bool in_suspention) { | ||
| 594 | const bool should_suspend = exception_exited || in_suspention; | ||
| 595 | { | ||
| 596 | SchedulerLock lock(*this); | ||
| 597 | ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; | ||
| 598 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 599 | impl->suspend_threads[i]->SetStatus(status); | ||
| 600 | } | ||
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | bool KernelCore::IsMulticore() const { | ||
| 605 | return impl->is_multicore; | ||
| 606 | } | ||
| 607 | |||
| 608 | void KernelCore::ExceptionalExit() { | ||
| 609 | exception_exited = true; | ||
| 610 | Suspend(true); | ||
| 611 | } | ||
| 612 | |||
| 613 | void KernelCore::EnterSVCProfile() { | ||
| 614 | std::size_t core = impl->GetCurrentHostThreadID(); | ||
| 615 | impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); | ||
| 616 | } | ||
| 617 | |||
| 618 | void KernelCore::ExitSVCProfile() { | ||
| 619 | std::size_t core = impl->GetCurrentHostThreadID(); | ||
| 620 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); | ||
| 621 | } | ||
| 622 | |||
| 560 | } // namespace Kernel | 623 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 83de1f542..49bd47e89 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -4,15 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <unordered_map> | 10 | #include <unordered_map> |
| 10 | #include <vector> | 11 | #include <vector> |
| 12 | #include "core/hardware_properties.h" | ||
| 11 | #include "core/hle/kernel/memory/memory_types.h" | 13 | #include "core/hle/kernel/memory/memory_types.h" |
| 12 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| 13 | 15 | ||
| 14 | namespace Core { | 16 | namespace Core { |
| 15 | struct EmuThreadHandle; | 17 | class CPUInterruptHandler; |
| 16 | class ExclusiveMonitor; | 18 | class ExclusiveMonitor; |
| 17 | class System; | 19 | class System; |
| 18 | } // namespace Core | 20 | } // namespace Core |
| @@ -65,6 +67,9 @@ public: | |||
| 65 | KernelCore(KernelCore&&) = delete; | 67 | KernelCore(KernelCore&&) = delete; |
| 66 | KernelCore& operator=(KernelCore&&) = delete; | 68 | KernelCore& operator=(KernelCore&&) = delete; |
| 67 | 69 | ||
| 70 | /// Sets if emulation is multicore or single core, must be set before Initialize | ||
| 71 | void SetMulticore(bool is_multicore); | ||
| 72 | |||
| 68 | /// Resets the kernel to a clean slate for use. | 73 | /// Resets the kernel to a clean slate for use. |
| 69 | void Initialize(); | 74 | void Initialize(); |
| 70 | 75 | ||
| @@ -110,6 +115,18 @@ public: | |||
| 110 | /// Gets the an instance of the respective physical CPU core. | 115 | /// Gets the an instance of the respective physical CPU core. |
| 111 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; | 116 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; |
| 112 | 117 | ||
| 118 | /// Gets the sole instance of the Scheduler at the current running core. | ||
| 119 | Kernel::Scheduler& CurrentScheduler(); | ||
| 120 | |||
| 121 | /// Gets the sole instance of the Scheduler at the current running core. | ||
| 122 | const Kernel::Scheduler& CurrentScheduler() const; | ||
| 123 | |||
| 124 | /// Gets the an instance of the current physical CPU core. | ||
| 125 | Kernel::PhysicalCore& CurrentPhysicalCore(); | ||
| 126 | |||
| 127 | /// Gets the an instance of the current physical CPU core. | ||
| 128 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; | ||
| 129 | |||
| 113 | /// Gets the an instance of the Synchronization Interface. | 130 | /// Gets the an instance of the Synchronization Interface. |
| 114 | Kernel::Synchronization& Synchronization(); | 131 | Kernel::Synchronization& Synchronization(); |
| 115 | 132 | ||
| @@ -129,6 +146,10 @@ public: | |||
| 129 | 146 | ||
| 130 | const Core::ExclusiveMonitor& GetExclusiveMonitor() const; | 147 | const Core::ExclusiveMonitor& GetExclusiveMonitor() const; |
| 131 | 148 | ||
| 149 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts(); | ||
| 150 | |||
| 151 | const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const; | ||
| 152 | |||
| 132 | void InvalidateAllInstructionCaches(); | 153 | void InvalidateAllInstructionCaches(); |
| 133 | 154 | ||
| 134 | /// Adds a port to the named port table | 155 | /// Adds a port to the named port table |
| @@ -191,6 +212,18 @@ public: | |||
| 191 | /// Gets the shared memory object for Time services. | 212 | /// Gets the shared memory object for Time services. |
| 192 | const Kernel::SharedMemory& GetTimeSharedMem() const; | 213 | const Kernel::SharedMemory& GetTimeSharedMem() const; |
| 193 | 214 | ||
| 215 | /// Suspend/unsuspend the OS. | ||
| 216 | void Suspend(bool in_suspention); | ||
| 217 | |||
| 218 | /// Exceptional exit the OS. | ||
| 219 | void ExceptionalExit(); | ||
| 220 | |||
| 221 | bool IsMulticore() const; | ||
| 222 | |||
| 223 | void EnterSVCProfile(); | ||
| 224 | |||
| 225 | void ExitSVCProfile(); | ||
| 226 | |||
| 194 | private: | 227 | private: |
| 195 | friend class Object; | 228 | friend class Object; |
| 196 | friend class Process; | 229 | friend class Process; |
| @@ -208,9 +241,6 @@ private: | |||
| 208 | /// Creates a new thread ID, incrementing the internal thread ID counter. | 241 | /// Creates a new thread ID, incrementing the internal thread ID counter. |
| 209 | u64 CreateNewThreadID(); | 242 | u64 CreateNewThreadID(); |
| 210 | 243 | ||
| 211 | /// Retrieves the event type used for thread wakeup callbacks. | ||
| 212 | const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; | ||
| 213 | |||
| 214 | /// Provides a reference to the global handle table. | 244 | /// Provides a reference to the global handle table. |
| 215 | Kernel::HandleTable& GlobalHandleTable(); | 245 | Kernel::HandleTable& GlobalHandleTable(); |
| 216 | 246 | ||
| @@ -219,6 +249,7 @@ private: | |||
| 219 | 249 | ||
| 220 | struct Impl; | 250 | struct Impl; |
| 221 | std::unique_ptr<Impl> impl; | 251 | std::unique_ptr<Impl> impl; |
| 252 | bool exception_exited{}; | ||
| 222 | }; | 253 | }; |
| 223 | 254 | ||
| 224 | } // namespace Kernel | 255 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/memory/memory_manager.cpp b/src/core/hle/kernel/memory/memory_manager.cpp index 6b432e1b2..acf13585c 100644 --- a/src/core/hle/kernel/memory/memory_manager.cpp +++ b/src/core/hle/kernel/memory/memory_manager.cpp | |||
| @@ -104,7 +104,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa | |||
| 104 | // Ensure that we don't leave anything un-freed | 104 | // Ensure that we don't leave anything un-freed |
| 105 | auto group_guard = detail::ScopeExit([&] { | 105 | auto group_guard = detail::ScopeExit([&] { |
| 106 | for (const auto& it : page_list.Nodes()) { | 106 | for (const auto& it : page_list.Nodes()) { |
| 107 | const auto min_num_pages{std::min( | 107 | const auto min_num_pages{std::min<size_t>( |
| 108 | it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; | 108 | it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; |
| 109 | chosen_manager.Free(it.GetAddress(), min_num_pages); | 109 | chosen_manager.Free(it.GetAddress(), min_num_pages); |
| 110 | } | 110 | } |
| @@ -139,7 +139,6 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa | |||
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | // Only succeed if we allocated as many pages as we wanted | 141 | // Only succeed if we allocated as many pages as we wanted |
| 142 | ASSERT(num_pages >= 0); | ||
| 143 | if (num_pages) { | 142 | if (num_pages) { |
| 144 | return ERR_OUT_OF_MEMORY; | 143 | return ERR_OUT_OF_MEMORY; |
| 145 | } | 144 | } |
| @@ -165,7 +164,7 @@ ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages, | |||
| 165 | 164 | ||
| 166 | // Free all of the pages | 165 | // Free all of the pages |
| 167 | for (const auto& it : page_list.Nodes()) { | 166 | for (const auto& it : page_list.Nodes()) { |
| 168 | const auto min_num_pages{std::min( | 167 | const auto min_num_pages{std::min<size_t>( |
| 169 | it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; | 168 | it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; |
| 170 | chosen_manager.Free(it.GetAddress(), min_num_pages); | 169 | chosen_manager.Free(it.GetAddress(), min_num_pages); |
| 171 | } | 170 | } |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 7869eb32b..8f6c944d1 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -34,8 +34,6 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr | |||
| 34 | if (thread->GetMutexWaitAddress() != mutex_addr) | 34 | if (thread->GetMutexWaitAddress() != mutex_addr) |
| 35 | continue; | 35 | continue; |
| 36 | 36 | ||
| 37 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); | ||
| 38 | |||
| 39 | ++num_waiters; | 37 | ++num_waiters; |
| 40 | if (highest_priority_thread == nullptr || | 38 | if (highest_priority_thread == nullptr || |
| 41 | thread->GetPriority() < highest_priority_thread->GetPriority()) { | 39 | thread->GetPriority() < highest_priority_thread->GetPriority()) { |
| @@ -49,6 +47,7 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr | |||
| 49 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. | 47 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. |
| 50 | static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, | 48 | static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, |
| 51 | std::shared_ptr<Thread> new_owner) { | 49 | std::shared_ptr<Thread> new_owner) { |
| 50 | current_thread->RemoveMutexWaiter(new_owner); | ||
| 52 | const auto threads = current_thread->GetMutexWaitingThreads(); | 51 | const auto threads = current_thread->GetMutexWaitingThreads(); |
| 53 | for (const auto& thread : threads) { | 52 | for (const auto& thread : threads) { |
| 54 | if (thread->GetMutexWaitAddress() != mutex_addr) | 53 | if (thread->GetMutexWaitAddress() != mutex_addr) |
| @@ -72,85 +71,100 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, | |||
| 72 | return ERR_INVALID_ADDRESS; | 71 | return ERR_INVALID_ADDRESS; |
| 73 | } | 72 | } |
| 74 | 73 | ||
| 75 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 74 | auto& kernel = system.Kernel(); |
| 76 | std::shared_ptr<Thread> current_thread = | 75 | std::shared_ptr<Thread> current_thread = |
| 77 | SharedFrom(system.CurrentScheduler().GetCurrentThread()); | 76 | SharedFrom(kernel.CurrentScheduler().GetCurrentThread()); |
| 78 | std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); | 77 | { |
| 79 | std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle); | 78 | SchedulerLock lock(kernel); |
| 79 | // The mutex address must be 4-byte aligned | ||
| 80 | if ((address % sizeof(u32)) != 0) { | ||
| 81 | return ERR_INVALID_ADDRESS; | ||
| 82 | } | ||
| 80 | 83 | ||
| 81 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another | 84 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 82 | // thread. | 85 | std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); |
| 83 | ASSERT(requesting_thread == current_thread); | 86 | std::shared_ptr<Thread> requesting_thread = |
| 87 | handle_table.Get<Thread>(requesting_thread_handle); | ||
| 84 | 88 | ||
| 85 | const u32 addr_value = system.Memory().Read32(address); | 89 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of |
| 90 | // another thread. | ||
| 91 | ASSERT(requesting_thread == current_thread); | ||
| 86 | 92 | ||
| 87 | // If the mutex isn't being held, just return success. | 93 | current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 88 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { | ||
| 89 | return RESULT_SUCCESS; | ||
| 90 | } | ||
| 91 | 94 | ||
| 92 | if (holding_thread == nullptr) { | 95 | const u32 addr_value = system.Memory().Read32(address); |
| 93 | LOG_ERROR(Kernel, "Holding thread does not exist! thread_handle={:08X}", | 96 | |
| 94 | holding_thread_handle); | 97 | // If the mutex isn't being held, just return success. |
| 95 | return ERR_INVALID_HANDLE; | 98 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { |
| 96 | } | 99 | return RESULT_SUCCESS; |
| 100 | } | ||
| 97 | 101 | ||
| 98 | // Wait until the mutex is released | 102 | if (holding_thread == nullptr) { |
| 99 | current_thread->SetMutexWaitAddress(address); | 103 | return ERR_INVALID_HANDLE; |
| 100 | current_thread->SetWaitHandle(requesting_thread_handle); | 104 | } |
| 101 | 105 | ||
| 102 | current_thread->SetStatus(ThreadStatus::WaitMutex); | 106 | // Wait until the mutex is released |
| 103 | current_thread->InvalidateWakeupCallback(); | 107 | current_thread->SetMutexWaitAddress(address); |
| 108 | current_thread->SetWaitHandle(requesting_thread_handle); | ||
| 104 | 109 | ||
| 105 | // Update the lock holder thread's priority to prevent priority inversion. | 110 | current_thread->SetStatus(ThreadStatus::WaitMutex); |
| 106 | holding_thread->AddMutexWaiter(current_thread); | ||
| 107 | 111 | ||
| 108 | system.PrepareReschedule(); | 112 | // Update the lock holder thread's priority to prevent priority inversion. |
| 113 | holding_thread->AddMutexWaiter(current_thread); | ||
| 114 | } | ||
| 109 | 115 | ||
| 110 | return RESULT_SUCCESS; | 116 | { |
| 117 | SchedulerLock lock(kernel); | ||
| 118 | auto* owner = current_thread->GetLockOwner(); | ||
| 119 | if (owner != nullptr) { | ||
| 120 | owner->RemoveMutexWaiter(current_thread); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | return current_thread->GetSignalingResult(); | ||
| 111 | } | 124 | } |
| 112 | 125 | ||
| 113 | ResultCode Mutex::Release(VAddr address) { | 126 | std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner, |
| 127 | VAddr address) { | ||
| 114 | // The mutex address must be 4-byte aligned | 128 | // The mutex address must be 4-byte aligned |
| 115 | if ((address % sizeof(u32)) != 0) { | 129 | if ((address % sizeof(u32)) != 0) { |
| 116 | LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); | 130 | LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); |
| 117 | return ERR_INVALID_ADDRESS; | 131 | return {ERR_INVALID_ADDRESS, nullptr}; |
| 118 | } | 132 | } |
| 119 | 133 | ||
| 120 | std::shared_ptr<Thread> current_thread = | 134 | auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address); |
| 121 | SharedFrom(system.CurrentScheduler().GetCurrentThread()); | 135 | if (new_owner == nullptr) { |
| 122 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address); | ||
| 123 | |||
| 124 | // There are no more threads waiting for the mutex, release it completely. | ||
| 125 | if (thread == nullptr) { | ||
| 126 | system.Memory().Write32(address, 0); | 136 | system.Memory().Write32(address, 0); |
| 127 | return RESULT_SUCCESS; | 137 | return {RESULT_SUCCESS, nullptr}; |
| 128 | } | 138 | } |
| 129 | |||
| 130 | // Transfer the ownership of the mutex from the previous owner to the new one. | 139 | // Transfer the ownership of the mutex from the previous owner to the new one. |
| 131 | TransferMutexOwnership(address, current_thread, thread); | 140 | TransferMutexOwnership(address, owner, new_owner); |
| 132 | 141 | u32 mutex_value = new_owner->GetWaitHandle(); | |
| 133 | u32 mutex_value = thread->GetWaitHandle(); | ||
| 134 | |||
| 135 | if (num_waiters >= 2) { | 142 | if (num_waiters >= 2) { |
| 136 | // Notify the guest that there are still some threads waiting for the mutex | 143 | // Notify the guest that there are still some threads waiting for the mutex |
| 137 | mutex_value |= Mutex::MutexHasWaitersFlag; | 144 | mutex_value |= Mutex::MutexHasWaitersFlag; |
| 138 | } | 145 | } |
| 146 | new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | ||
| 147 | new_owner->SetLockOwner(nullptr); | ||
| 148 | new_owner->ResumeFromWait(); | ||
| 139 | 149 | ||
| 140 | // Grant the mutex to the next waiting thread and resume it. | ||
| 141 | system.Memory().Write32(address, mutex_value); | 150 | system.Memory().Write32(address, mutex_value); |
| 151 | return {RESULT_SUCCESS, new_owner}; | ||
| 152 | } | ||
| 142 | 153 | ||
| 143 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); | 154 | ResultCode Mutex::Release(VAddr address) { |
| 144 | thread->ResumeFromWait(); | 155 | auto& kernel = system.Kernel(); |
| 156 | SchedulerLock lock(kernel); | ||
| 145 | 157 | ||
| 146 | thread->SetLockOwner(nullptr); | 158 | std::shared_ptr<Thread> current_thread = |
| 147 | thread->SetCondVarWaitAddress(0); | 159 | SharedFrom(kernel.CurrentScheduler().GetCurrentThread()); |
| 148 | thread->SetMutexWaitAddress(0); | ||
| 149 | thread->SetWaitHandle(0); | ||
| 150 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 151 | 160 | ||
| 152 | system.PrepareReschedule(); | 161 | auto [result, new_owner] = Unlock(current_thread, address); |
| 153 | 162 | ||
| 154 | return RESULT_SUCCESS; | 163 | if (result != RESULT_SUCCESS && new_owner != nullptr) { |
| 164 | new_owner->SetSynchronizationResults(nullptr, result); | ||
| 165 | } | ||
| 166 | |||
| 167 | return result; | ||
| 155 | } | 168 | } |
| 169 | |||
| 156 | } // namespace Kernel | 170 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index b904de2e8..3b81dc3df 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -28,6 +28,10 @@ public: | |||
| 28 | ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, | 28 | ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, |
| 29 | Handle requesting_thread_handle); | 29 | Handle requesting_thread_handle); |
| 30 | 30 | ||
| 31 | /// Unlocks a mutex for owner at address | ||
| 32 | std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner, | ||
| 33 | VAddr address); | ||
| 34 | |||
| 31 | /// Releases the mutex at the specified address. | 35 | /// Releases the mutex at the specified address. |
| 32 | ResultCode Release(VAddr address); | 36 | ResultCode Release(VAddr address); |
| 33 | 37 | ||
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index a15011076..c6bbdb080 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -2,12 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/assert.h" | ||
| 5 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "common/spin_lock.h" | ||
| 6 | #include "core/arm/arm_interface.h" | 8 | #include "core/arm/arm_interface.h" |
| 7 | #ifdef ARCHITECTURE_x86_64 | 9 | #ifdef ARCHITECTURE_x86_64 |
| 8 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 10 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 9 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 11 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 10 | #endif | 12 | #endif |
| 13 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 11 | #include "core/arm/exclusive_monitor.h" | 14 | #include "core/arm/exclusive_monitor.h" |
| 12 | #include "core/arm/unicorn/arm_unicorn.h" | 15 | #include "core/arm/unicorn/arm_unicorn.h" |
| 13 | #include "core/core.h" | 16 | #include "core/core.h" |
| @@ -17,50 +20,37 @@ | |||
| 17 | 20 | ||
| 18 | namespace Kernel { | 21 | namespace Kernel { |
| 19 | 22 | ||
| 20 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, | 23 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler, |
| 21 | Core::ExclusiveMonitor& exclusive_monitor) | 24 | Core::CPUInterruptHandler& interrupt_handler) |
| 22 | : core_index{id} { | 25 | : interrupt_handler{interrupt_handler}, core_index{id}, scheduler{scheduler} { |
| 23 | #ifdef ARCHITECTURE_x86_64 | ||
| 24 | arm_interface_32 = | ||
| 25 | std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index); | ||
| 26 | arm_interface_64 = | ||
| 27 | std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index); | ||
| 28 | |||
| 29 | #else | ||
| 30 | using Core::ARM_Unicorn; | ||
| 31 | arm_interface_32 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch32); | ||
| 32 | arm_interface_64 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch64); | ||
| 33 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 34 | #endif | ||
| 35 | 26 | ||
| 36 | scheduler = std::make_unique<Kernel::Scheduler>(system, core_index); | 27 | guard = std::make_unique<Common::SpinLock>(); |
| 37 | } | 28 | } |
| 38 | 29 | ||
| 39 | PhysicalCore::~PhysicalCore() = default; | 30 | PhysicalCore::~PhysicalCore() = default; |
| 40 | 31 | ||
| 41 | void PhysicalCore::Run() { | 32 | void PhysicalCore::Idle() { |
| 42 | arm_interface->Run(); | 33 | interrupt_handler.AwaitInterrupt(); |
| 43 | arm_interface->ClearExclusiveState(); | ||
| 44 | } | 34 | } |
| 45 | 35 | ||
| 46 | void PhysicalCore::Step() { | 36 | void PhysicalCore::Shutdown() { |
| 47 | arm_interface->Step(); | 37 | scheduler.Shutdown(); |
| 48 | } | 38 | } |
| 49 | 39 | ||
| 50 | void PhysicalCore::Stop() { | 40 | bool PhysicalCore::IsInterrupted() const { |
| 51 | arm_interface->PrepareReschedule(); | 41 | return interrupt_handler.IsInterrupted(); |
| 52 | } | 42 | } |
| 53 | 43 | ||
| 54 | void PhysicalCore::Shutdown() { | 44 | void PhysicalCore::Interrupt() { |
| 55 | scheduler->Shutdown(); | 45 | guard->lock(); |
| 46 | interrupt_handler.SetInterrupt(true); | ||
| 47 | guard->unlock(); | ||
| 56 | } | 48 | } |
| 57 | 49 | ||
| 58 | void PhysicalCore::SetIs64Bit(bool is_64_bit) { | 50 | void PhysicalCore::ClearInterrupt() { |
| 59 | if (is_64_bit) { | 51 | guard->lock(); |
| 60 | arm_interface = arm_interface_64.get(); | 52 | interrupt_handler.SetInterrupt(false); |
| 61 | } else { | 53 | guard->unlock(); |
| 62 | arm_interface = arm_interface_32.get(); | ||
| 63 | } | ||
| 64 | } | 54 | } |
| 65 | 55 | ||
| 66 | } // namespace Kernel | 56 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 3269166be..d7a7a951c 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h | |||
| @@ -7,12 +7,17 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | 9 | ||
| 10 | namespace Common { | ||
| 11 | class SpinLock; | ||
| 12 | } | ||
| 13 | |||
| 10 | namespace Kernel { | 14 | namespace Kernel { |
| 11 | class Scheduler; | 15 | class Scheduler; |
| 12 | } // namespace Kernel | 16 | } // namespace Kernel |
| 13 | 17 | ||
| 14 | namespace Core { | 18 | namespace Core { |
| 15 | class ARM_Interface; | 19 | class ARM_Interface; |
| 20 | class CPUInterruptHandler; | ||
| 16 | class ExclusiveMonitor; | 21 | class ExclusiveMonitor; |
| 17 | class System; | 22 | class System; |
| 18 | } // namespace Core | 23 | } // namespace Core |
| @@ -21,7 +26,8 @@ namespace Kernel { | |||
| 21 | 26 | ||
| 22 | class PhysicalCore { | 27 | class PhysicalCore { |
| 23 | public: | 28 | public: |
| 24 | PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor); | 29 | PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler, |
| 30 | Core::CPUInterruptHandler& interrupt_handler); | ||
| 25 | ~PhysicalCore(); | 31 | ~PhysicalCore(); |
| 26 | 32 | ||
| 27 | PhysicalCore(const PhysicalCore&) = delete; | 33 | PhysicalCore(const PhysicalCore&) = delete; |
| @@ -30,23 +36,18 @@ public: | |||
| 30 | PhysicalCore(PhysicalCore&&) = default; | 36 | PhysicalCore(PhysicalCore&&) = default; |
| 31 | PhysicalCore& operator=(PhysicalCore&&) = default; | 37 | PhysicalCore& operator=(PhysicalCore&&) = default; |
| 32 | 38 | ||
| 33 | /// Execute current jit state | 39 | void Idle(); |
| 34 | void Run(); | 40 | /// Interrupt this physical core. |
| 35 | /// Execute a single instruction in current jit. | 41 | void Interrupt(); |
| 36 | void Step(); | ||
| 37 | /// Stop JIT execution/exit | ||
| 38 | void Stop(); | ||
| 39 | 42 | ||
| 40 | // Shutdown this physical core. | 43 | /// Clear this core's interrupt |
| 41 | void Shutdown(); | 44 | void ClearInterrupt(); |
| 42 | 45 | ||
| 43 | Core::ARM_Interface& ArmInterface() { | 46 | /// Check if this core is interrupted |
| 44 | return *arm_interface; | 47 | bool IsInterrupted() const; |
| 45 | } | ||
| 46 | 48 | ||
| 47 | const Core::ARM_Interface& ArmInterface() const { | 49 | // Shutdown this physical core. |
| 48 | return *arm_interface; | 50 | void Shutdown(); |
| 49 | } | ||
| 50 | 51 | ||
| 51 | bool IsMainCore() const { | 52 | bool IsMainCore() const { |
| 52 | return core_index == 0; | 53 | return core_index == 0; |
| @@ -61,21 +62,18 @@ public: | |||
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | Kernel::Scheduler& Scheduler() { | 64 | Kernel::Scheduler& Scheduler() { |
| 64 | return *scheduler; | 65 | return scheduler; |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | const Kernel::Scheduler& Scheduler() const { | 68 | const Kernel::Scheduler& Scheduler() const { |
| 68 | return *scheduler; | 69 | return scheduler; |
| 69 | } | 70 | } |
| 70 | 71 | ||
| 71 | void SetIs64Bit(bool is_64_bit); | ||
| 72 | |||
| 73 | private: | 72 | private: |
| 73 | Core::CPUInterruptHandler& interrupt_handler; | ||
| 74 | std::size_t core_index; | 74 | std::size_t core_index; |
| 75 | std::unique_ptr<Core::ARM_Interface> arm_interface_32; | 75 | Kernel::Scheduler& scheduler; |
| 76 | std::unique_ptr<Core::ARM_Interface> arm_interface_64; | 76 | std::unique_ptr<Common::SpinLock> guard; |
| 77 | std::unique_ptr<Kernel::Scheduler> scheduler; | ||
| 78 | Core::ARM_Interface* arm_interface{}; | ||
| 79 | }; | 77 | }; |
| 80 | 78 | ||
| 81 | } // namespace Kernel | 79 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 36724569f..c6fcb56ad 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/hle/kernel/resource_limit.h" | 22 | #include "core/hle/kernel/resource_limit.h" |
| 23 | #include "core/hle/kernel/scheduler.h" | 23 | #include "core/hle/kernel/scheduler.h" |
| 24 | #include "core/hle/kernel/thread.h" | 24 | #include "core/hle/kernel/thread.h" |
| 25 | #include "core/hle/lock.h" | ||
| 25 | #include "core/memory.h" | 26 | #include "core/memory.h" |
| 26 | #include "core/settings.h" | 27 | #include "core/settings.h" |
| 27 | 28 | ||
| @@ -30,14 +31,15 @@ namespace { | |||
| 30 | /** | 31 | /** |
| 31 | * Sets up the primary application thread | 32 | * Sets up the primary application thread |
| 32 | * | 33 | * |
| 34 | * @param system The system instance to create the main thread under. | ||
| 33 | * @param owner_process The parent process for the main thread | 35 | * @param owner_process The parent process for the main thread |
| 34 | * @param kernel The kernel instance to create the main thread under. | ||
| 35 | * @param priority The priority to give the main thread | 36 | * @param priority The priority to give the main thread |
| 36 | */ | 37 | */ |
| 37 | void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { | 38 | void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { |
| 38 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); | 39 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); |
| 39 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, | 40 | ThreadType type = THREADTYPE_USER; |
| 40 | owner_process.GetIdealCore(), stack_top, owner_process); | 41 | auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0, |
| 42 | owner_process.GetIdealCore(), stack_top, &owner_process); | ||
| 41 | 43 | ||
| 42 | std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); | 44 | std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); |
| 43 | 45 | ||
| @@ -48,8 +50,12 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, V | |||
| 48 | thread->GetContext32().cpu_registers[1] = thread_handle; | 50 | thread->GetContext32().cpu_registers[1] = thread_handle; |
| 49 | thread->GetContext64().cpu_registers[1] = thread_handle; | 51 | thread->GetContext64().cpu_registers[1] = thread_handle; |
| 50 | 52 | ||
| 53 | auto& kernel = system.Kernel(); | ||
| 51 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 54 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
| 52 | thread->ResumeFromWait(); | 55 | { |
| 56 | SchedulerLock lock{kernel}; | ||
| 57 | thread->SetStatus(ThreadStatus::Ready); | ||
| 58 | } | ||
| 53 | } | 59 | } |
| 54 | } // Anonymous namespace | 60 | } // Anonymous namespace |
| 55 | 61 | ||
| @@ -117,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name, | |||
| 117 | : kernel.CreateNewUserProcessID(); | 123 | : kernel.CreateNewUserProcessID(); |
| 118 | process->capabilities.InitializeForMetadatalessProcess(); | 124 | process->capabilities.InitializeForMetadatalessProcess(); |
| 119 | 125 | ||
| 120 | std::mt19937 rng(Settings::values.rng_seed.value_or(0)); | 126 | std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0)); |
| 121 | std::uniform_int_distribution<u64> distribution; | 127 | std::uniform_int_distribution<u64> distribution; |
| 122 | std::generate(process->random_entropy.begin(), process->random_entropy.end(), | 128 | std::generate(process->random_entropy.begin(), process->random_entropy.end(), |
| 123 | [&] { return distribution(rng); }); | 129 | [&] { return distribution(rng); }); |
| @@ -132,7 +138,8 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const { | |||
| 132 | 138 | ||
| 133 | u64 Process::GetTotalPhysicalMemoryAvailable() const { | 139 | u64 Process::GetTotalPhysicalMemoryAvailable() const { |
| 134 | const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + | 140 | const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + |
| 135 | page_table->GetTotalHeapSize() + image_size + main_thread_stack_size}; | 141 | page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + |
| 142 | main_thread_stack_size}; | ||
| 136 | 143 | ||
| 137 | if (capacity < memory_usage_capacity) { | 144 | if (capacity < memory_usage_capacity) { |
| 138 | return capacity; | 145 | return capacity; |
| @@ -146,7 +153,8 @@ u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { | |||
| 146 | } | 153 | } |
| 147 | 154 | ||
| 148 | u64 Process::GetTotalPhysicalMemoryUsed() const { | 155 | u64 Process::GetTotalPhysicalMemoryUsed() const { |
| 149 | return image_size + main_thread_stack_size + page_table->GetTotalHeapSize(); | 156 | return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() + |
| 157 | GetSystemResourceSize(); | ||
| 150 | } | 158 | } |
| 151 | 159 | ||
| 152 | u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { | 160 | u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { |
| @@ -180,7 +188,6 @@ void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) { | |||
| 180 | } | 188 | } |
| 181 | ++it; | 189 | ++it; |
| 182 | } | 190 | } |
| 183 | UNREACHABLE(); | ||
| 184 | } | 191 | } |
| 185 | 192 | ||
| 186 | std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( | 193 | std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( |
| @@ -205,6 +212,7 @@ void Process::UnregisterThread(const Thread* thread) { | |||
| 205 | } | 212 | } |
| 206 | 213 | ||
| 207 | ResultCode Process::ClearSignalState() { | 214 | ResultCode Process::ClearSignalState() { |
| 215 | SchedulerLock lock(system.Kernel()); | ||
| 208 | if (status == ProcessStatus::Exited) { | 216 | if (status == ProcessStatus::Exited) { |
| 209 | LOG_ERROR(Kernel, "called on a terminated process instance."); | 217 | LOG_ERROR(Kernel, "called on a terminated process instance."); |
| 210 | return ERR_INVALID_STATE; | 218 | return ERR_INVALID_STATE; |
| @@ -292,7 +300,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { | |||
| 292 | 300 | ||
| 293 | ChangeStatus(ProcessStatus::Running); | 301 | ChangeStatus(ProcessStatus::Running); |
| 294 | 302 | ||
| 295 | SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); | 303 | SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top); |
| 296 | resource_limit->Reserve(ResourceType::Threads, 1); | 304 | resource_limit->Reserve(ResourceType::Threads, 1); |
| 297 | resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); | 305 | resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); |
| 298 | } | 306 | } |
| @@ -338,6 +346,7 @@ static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { | |||
| 338 | } | 346 | } |
| 339 | 347 | ||
| 340 | VAddr Process::CreateTLSRegion() { | 348 | VAddr Process::CreateTLSRegion() { |
| 349 | SchedulerLock lock(system.Kernel()); | ||
| 341 | if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; | 350 | if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; |
| 342 | tls_page_iter != tls_pages.cend()) { | 351 | tls_page_iter != tls_pages.cend()) { |
| 343 | return *tls_page_iter->ReserveSlot(); | 352 | return *tls_page_iter->ReserveSlot(); |
| @@ -368,6 +377,7 @@ VAddr Process::CreateTLSRegion() { | |||
| 368 | } | 377 | } |
| 369 | 378 | ||
| 370 | void Process::FreeTLSRegion(VAddr tls_address) { | 379 | void Process::FreeTLSRegion(VAddr tls_address) { |
| 380 | SchedulerLock lock(system.Kernel()); | ||
| 371 | const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); | 381 | const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); |
| 372 | auto iter = | 382 | auto iter = |
| 373 | std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { | 383 | std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { |
| @@ -382,6 +392,7 @@ void Process::FreeTLSRegion(VAddr tls_address) { | |||
| 382 | } | 392 | } |
| 383 | 393 | ||
| 384 | void Process::LoadModule(CodeSet code_set, VAddr base_addr) { | 394 | void Process::LoadModule(CodeSet code_set, VAddr base_addr) { |
| 395 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 385 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, | 396 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, |
| 386 | Memory::MemoryPermission permission) { | 397 | Memory::MemoryPermission permission) { |
| 387 | page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); | 398 | page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); |
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index 00860fcbd..6e286419e 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp | |||
| @@ -6,8 +6,10 @@ | |||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/hle/kernel/errors.h" | 8 | #include "core/hle/kernel/errors.h" |
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 9 | #include "core/hle/kernel/object.h" | 10 | #include "core/hle/kernel/object.h" |
| 10 | #include "core/hle/kernel/readable_event.h" | 11 | #include "core/hle/kernel/readable_event.h" |
| 12 | #include "core/hle/kernel/scheduler.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | 13 | #include "core/hle/kernel/thread.h" |
| 12 | 14 | ||
| 13 | namespace Kernel { | 15 | namespace Kernel { |
| @@ -37,8 +39,9 @@ void ReadableEvent::Clear() { | |||
| 37 | } | 39 | } |
| 38 | 40 | ||
| 39 | ResultCode ReadableEvent::Reset() { | 41 | ResultCode ReadableEvent::Reset() { |
| 42 | SchedulerLock lock(kernel); | ||
| 40 | if (!is_signaled) { | 43 | if (!is_signaled) { |
| 41 | LOG_ERROR(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}", | 44 | LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}", |
| 42 | GetObjectId(), GetTypeName(), GetName()); | 45 | GetObjectId(), GetTypeName(), GetName()); |
| 43 | return ERR_INVALID_STATE; | 46 | return ERR_INVALID_STATE; |
| 44 | } | 47 | } |
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index d9beaa3a4..212e442f4 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp | |||
| @@ -24,13 +24,9 @@ bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) { | |||
| 24 | const std::size_t index{ResourceTypeToIndex(resource)}; | 24 | const std::size_t index{ResourceTypeToIndex(resource)}; |
| 25 | 25 | ||
| 26 | s64 new_value = current[index] + amount; | 26 | s64 new_value = current[index] + amount; |
| 27 | while (new_value > limit[index] && available[index] + amount <= limit[index]) { | 27 | if (new_value > limit[index] && available[index] + amount <= limit[index]) { |
| 28 | // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout | 28 | // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout |
| 29 | new_value = current[index] + amount; | 29 | new_value = current[index] + amount; |
| 30 | |||
| 31 | if (timeout >= 0) { | ||
| 32 | break; | ||
| 33 | } | ||
| 34 | } | 30 | } |
| 35 | 31 | ||
| 36 | if (new_value <= limit[index]) { | 32 | if (new_value <= limit[index]) { |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1140c72a3..7b929781c 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -6,16 +6,21 @@ | |||
| 6 | // licensed under GPLv2 or later under exception provided by the author. | 6 | // licensed under GPLv2 or later under exception provided by the author. |
| 7 | 7 | ||
| 8 | #include <algorithm> | 8 | #include <algorithm> |
| 9 | #include <mutex> | ||
| 9 | #include <set> | 10 | #include <set> |
| 10 | #include <unordered_set> | 11 | #include <unordered_set> |
| 11 | #include <utility> | 12 | #include <utility> |
| 12 | 13 | ||
| 13 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 15 | #include "common/bit_util.h" | ||
| 16 | #include "common/fiber.h" | ||
| 14 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| 15 | #include "core/arm/arm_interface.h" | 18 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/core.h" | 19 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 20 | #include "core/core_timing.h" |
| 21 | #include "core/cpu_manager.h" | ||
| 18 | #include "core/hle/kernel/kernel.h" | 22 | #include "core/hle/kernel/kernel.h" |
| 23 | #include "core/hle/kernel/physical_core.h" | ||
| 19 | #include "core/hle/kernel/process.h" | 24 | #include "core/hle/kernel/process.h" |
| 20 | #include "core/hle/kernel/scheduler.h" | 25 | #include "core/hle/kernel/scheduler.h" |
| 21 | #include "core/hle/kernel/time_manager.h" | 26 | #include "core/hle/kernel/time_manager.h" |
| @@ -27,103 +32,148 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {} | |||
| 27 | GlobalScheduler::~GlobalScheduler() = default; | 32 | GlobalScheduler::~GlobalScheduler() = default; |
| 28 | 33 | ||
| 29 | void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { | 34 | void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { |
| 35 | std::scoped_lock lock{global_list_guard}; | ||
| 30 | thread_list.push_back(std::move(thread)); | 36 | thread_list.push_back(std::move(thread)); |
| 31 | } | 37 | } |
| 32 | 38 | ||
| 33 | void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { | 39 | void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { |
| 40 | std::scoped_lock lock{global_list_guard}; | ||
| 34 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | 41 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), |
| 35 | thread_list.end()); | 42 | thread_list.end()); |
| 36 | } | 43 | } |
| 37 | 44 | ||
| 38 | void GlobalScheduler::UnloadThread(std::size_t core) { | 45 | u32 GlobalScheduler::SelectThreads() { |
| 39 | Scheduler& sched = kernel.Scheduler(core); | 46 | ASSERT(is_locked); |
| 40 | sched.UnloadThread(); | ||
| 41 | } | ||
| 42 | |||
| 43 | void GlobalScheduler::SelectThread(std::size_t core) { | ||
| 44 | const auto update_thread = [](Thread* thread, Scheduler& sched) { | 47 | const auto update_thread = [](Thread* thread, Scheduler& sched) { |
| 45 | if (thread != sched.selected_thread.get()) { | 48 | std::scoped_lock lock{sched.guard}; |
| 49 | if (thread != sched.selected_thread_set.get()) { | ||
| 46 | if (thread == nullptr) { | 50 | if (thread == nullptr) { |
| 47 | ++sched.idle_selection_count; | 51 | ++sched.idle_selection_count; |
| 48 | } | 52 | } |
| 49 | sched.selected_thread = SharedFrom(thread); | 53 | sched.selected_thread_set = SharedFrom(thread); |
| 50 | } | 54 | } |
| 51 | sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; | 55 | const bool reschedule_pending = |
| 56 | sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread); | ||
| 57 | sched.is_context_switch_pending = reschedule_pending; | ||
| 52 | std::atomic_thread_fence(std::memory_order_seq_cst); | 58 | std::atomic_thread_fence(std::memory_order_seq_cst); |
| 59 | return reschedule_pending; | ||
| 53 | }; | 60 | }; |
| 54 | Scheduler& sched = kernel.Scheduler(core); | 61 | if (!is_reselection_pending.load()) { |
| 55 | Thread* current_thread = nullptr; | 62 | return 0; |
| 56 | // Step 1: Get top thread in schedule queue. | ||
| 57 | current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); | ||
| 58 | if (current_thread) { | ||
| 59 | update_thread(current_thread, sched); | ||
| 60 | return; | ||
| 61 | } | 63 | } |
| 62 | // Step 2: Try selecting a suggested thread. | 64 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{}; |
| 63 | Thread* winner = nullptr; | 65 | |
| 64 | std::set<s32> sug_cores; | 66 | u32 idle_cores{}; |
| 65 | for (auto thread : suggested_queue[core]) { | 67 | |
| 66 | s32 this_core = thread->GetProcessorID(); | 68 | // Step 1: Get top thread in schedule queue. |
| 67 | Thread* thread_on_core = nullptr; | 69 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 68 | if (this_core >= 0) { | 70 | Thread* top_thread = |
| 69 | thread_on_core = scheduled_queue[this_core].front(); | 71 | scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); |
| 70 | } | 72 | if (top_thread != nullptr) { |
| 71 | if (this_core < 0 || thread != thread_on_core) { | 73 | // TODO(Blinkhawk): Implement Thread Pinning |
| 72 | winner = thread; | 74 | } else { |
| 73 | break; | 75 | idle_cores |= (1ul << core); |
| 74 | } | 76 | } |
| 75 | sug_cores.insert(this_core); | 77 | top_threads[core] = top_thread; |
| 76 | } | 78 | } |
| 77 | // if we got a suggested thread, select it, else do a second pass. | 79 | |
| 78 | if (winner && winner->GetPriority() > 2) { | 80 | while (idle_cores != 0) { |
| 79 | if (winner->IsRunning()) { | 81 | u32 core_id = Common::CountTrailingZeroes32(idle_cores); |
| 80 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | 82 | |
| 83 | if (!suggested_queue[core_id].empty()) { | ||
| 84 | std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{}; | ||
| 85 | std::size_t num_candidates = 0; | ||
| 86 | auto iter = suggested_queue[core_id].begin(); | ||
| 87 | Thread* suggested = nullptr; | ||
| 88 | // Step 2: Try selecting a suggested thread. | ||
| 89 | while (iter != suggested_queue[core_id].end()) { | ||
| 90 | suggested = *iter; | ||
| 91 | iter++; | ||
| 92 | s32 suggested_core_id = suggested->GetProcessorID(); | ||
| 93 | Thread* top_thread = | ||
| 94 | suggested_core_id >= 0 ? top_threads[suggested_core_id] : nullptr; | ||
| 95 | if (top_thread != suggested) { | ||
| 96 | if (top_thread != nullptr && | ||
| 97 | top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) { | ||
| 98 | suggested = nullptr; | ||
| 99 | break; | ||
| 100 | // There's a too high thread to do core migration, cancel | ||
| 101 | } | ||
| 102 | TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested); | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | suggested = nullptr; | ||
| 106 | migration_candidates[num_candidates++] = suggested_core_id; | ||
| 107 | } | ||
| 108 | // Step 3: Select a suggested thread from another core | ||
| 109 | if (suggested == nullptr) { | ||
| 110 | for (std::size_t i = 0; i < num_candidates; i++) { | ||
| 111 | s32 candidate_core = migration_candidates[i]; | ||
| 112 | suggested = top_threads[candidate_core]; | ||
| 113 | auto it = scheduled_queue[candidate_core].begin(); | ||
| 114 | it++; | ||
| 115 | Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr; | ||
| 116 | if (next != nullptr) { | ||
| 117 | TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), | ||
| 118 | suggested); | ||
| 119 | top_threads[candidate_core] = next; | ||
| 120 | break; | ||
| 121 | } else { | ||
| 122 | suggested = nullptr; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | top_threads[core_id] = suggested; | ||
| 81 | } | 127 | } |
| 82 | TransferToCore(winner->GetPriority(), static_cast<s32>(core), winner); | 128 | |
| 83 | update_thread(winner, sched); | 129 | idle_cores &= ~(1ul << core_id); |
| 84 | return; | ||
| 85 | } | 130 | } |
| 86 | // Step 3: Select a suggested thread from another core | 131 | u32 cores_needing_context_switch{}; |
| 87 | for (auto& src_core : sug_cores) { | 132 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 88 | auto it = scheduled_queue[src_core].begin(); | 133 | Scheduler& sched = kernel.Scheduler(core); |
| 89 | it++; | 134 | ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); |
| 90 | if (it != scheduled_queue[src_core].end()) { | 135 | if (update_thread(top_threads[core], sched)) { |
| 91 | Thread* thread_on_core = scheduled_queue[src_core].front(); | 136 | cores_needing_context_switch |= (1ul << core); |
| 92 | Thread* to_change = *it; | ||
| 93 | if (thread_on_core->IsRunning() || to_change->IsRunning()) { | ||
| 94 | UnloadThread(static_cast<u32>(src_core)); | ||
| 95 | } | ||
| 96 | TransferToCore(thread_on_core->GetPriority(), static_cast<s32>(core), thread_on_core); | ||
| 97 | current_thread = thread_on_core; | ||
| 98 | break; | ||
| 99 | } | 137 | } |
| 100 | } | 138 | } |
| 101 | update_thread(current_thread, sched); | 139 | return cores_needing_context_switch; |
| 102 | } | 140 | } |
| 103 | 141 | ||
| 104 | bool GlobalScheduler::YieldThread(Thread* yielding_thread) { | 142 | bool GlobalScheduler::YieldThread(Thread* yielding_thread) { |
| 143 | ASSERT(is_locked); | ||
| 105 | // Note: caller should use critical section, etc. | 144 | // Note: caller should use critical section, etc. |
| 145 | if (!yielding_thread->IsRunnable()) { | ||
| 146 | // Normally this case shouldn't happen except for SetThreadActivity. | ||
| 147 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 148 | return false; | ||
| 149 | } | ||
| 106 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); | 150 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); |
| 107 | const u32 priority = yielding_thread->GetPriority(); | 151 | const u32 priority = yielding_thread->GetPriority(); |
| 108 | 152 | ||
| 109 | // Yield the thread | 153 | // Yield the thread |
| 110 | const Thread* const winner = scheduled_queue[core_id].front(priority); | 154 | Reschedule(priority, core_id, yielding_thread); |
| 111 | ASSERT_MSG(yielding_thread == winner, "Thread yielding without being in front"); | 155 | const Thread* const winner = scheduled_queue[core_id].front(); |
| 112 | scheduled_queue[core_id].yield(priority); | 156 | if (kernel.GetCurrentHostThreadID() != core_id) { |
| 157 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 158 | } | ||
| 113 | 159 | ||
| 114 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); | 160 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); |
| 115 | } | 161 | } |
| 116 | 162 | ||
| 117 | bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { | 163 | bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { |
| 164 | ASSERT(is_locked); | ||
| 118 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, | 165 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, |
| 119 | // etc. | 166 | // etc. |
| 167 | if (!yielding_thread->IsRunnable()) { | ||
| 168 | // Normally this case shouldn't happen except for SetThreadActivity. | ||
| 169 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 170 | return false; | ||
| 171 | } | ||
| 120 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); | 172 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); |
| 121 | const u32 priority = yielding_thread->GetPriority(); | 173 | const u32 priority = yielding_thread->GetPriority(); |
| 122 | 174 | ||
| 123 | // Yield the thread | 175 | // Yield the thread |
| 124 | ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority), | 176 | Reschedule(priority, core_id, yielding_thread); |
| 125 | "Thread yielding without being in front"); | ||
| 126 | scheduled_queue[core_id].yield(priority); | ||
| 127 | 177 | ||
| 128 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads; | 178 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads; |
| 129 | for (std::size_t i = 0; i < current_threads.size(); i++) { | 179 | for (std::size_t i = 0; i < current_threads.size(); i++) { |
| @@ -153,21 +203,28 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { | |||
| 153 | 203 | ||
| 154 | if (winner != nullptr) { | 204 | if (winner != nullptr) { |
| 155 | if (winner != yielding_thread) { | 205 | if (winner != yielding_thread) { |
| 156 | if (winner->IsRunning()) { | ||
| 157 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 158 | } | ||
| 159 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 206 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 160 | } | 207 | } |
| 161 | } else { | 208 | } else { |
| 162 | winner = next_thread; | 209 | winner = next_thread; |
| 163 | } | 210 | } |
| 164 | 211 | ||
| 212 | if (kernel.GetCurrentHostThreadID() != core_id) { | ||
| 213 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 214 | } | ||
| 215 | |||
| 165 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); | 216 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); |
| 166 | } | 217 | } |
| 167 | 218 | ||
| 168 | bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) { | 219 | bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) { |
| 220 | ASSERT(is_locked); | ||
| 169 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, | 221 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, |
| 170 | // etc. | 222 | // etc. |
| 223 | if (!yielding_thread->IsRunnable()) { | ||
| 224 | // Normally this case shouldn't happen except for SetThreadActivity. | ||
| 225 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 226 | return false; | ||
| 227 | } | ||
| 171 | Thread* winner = nullptr; | 228 | Thread* winner = nullptr; |
| 172 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); | 229 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); |
| 173 | 230 | ||
| @@ -195,25 +252,31 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread | |||
| 195 | } | 252 | } |
| 196 | if (winner != nullptr) { | 253 | if (winner != nullptr) { |
| 197 | if (winner != yielding_thread) { | 254 | if (winner != yielding_thread) { |
| 198 | if (winner->IsRunning()) { | ||
| 199 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 200 | } | ||
| 201 | TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); | 255 | TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); |
| 202 | } | 256 | } |
| 203 | } else { | 257 | } else { |
| 204 | winner = yielding_thread; | 258 | winner = yielding_thread; |
| 205 | } | 259 | } |
| 260 | } else { | ||
| 261 | winner = scheduled_queue[core_id].front(); | ||
| 262 | } | ||
| 263 | |||
| 264 | if (kernel.GetCurrentHostThreadID() != core_id) { | ||
| 265 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 206 | } | 266 | } |
| 207 | 267 | ||
| 208 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); | 268 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); |
| 209 | } | 269 | } |
| 210 | 270 | ||
| 211 | void GlobalScheduler::PreemptThreads() { | 271 | void GlobalScheduler::PreemptThreads() { |
| 272 | ASSERT(is_locked); | ||
| 212 | for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | 273 | for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 213 | const u32 priority = preemption_priorities[core_id]; | 274 | const u32 priority = preemption_priorities[core_id]; |
| 214 | 275 | ||
| 215 | if (scheduled_queue[core_id].size(priority) > 0) { | 276 | if (scheduled_queue[core_id].size(priority) > 0) { |
| 216 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | 277 | if (scheduled_queue[core_id].size(priority) > 1) { |
| 278 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | ||
| 279 | } | ||
| 217 | scheduled_queue[core_id].yield(priority); | 280 | scheduled_queue[core_id].yield(priority); |
| 218 | if (scheduled_queue[core_id].size(priority) > 1) { | 281 | if (scheduled_queue[core_id].size(priority) > 1) { |
| 219 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | 282 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); |
| @@ -247,9 +310,6 @@ void GlobalScheduler::PreemptThreads() { | |||
| 247 | } | 310 | } |
| 248 | 311 | ||
| 249 | if (winner != nullptr) { | 312 | if (winner != nullptr) { |
| 250 | if (winner->IsRunning()) { | ||
| 251 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 252 | } | ||
| 253 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 313 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 254 | current_thread = | 314 | current_thread = |
| 255 | winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; | 315 | winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; |
| @@ -280,9 +340,6 @@ void GlobalScheduler::PreemptThreads() { | |||
| 280 | } | 340 | } |
| 281 | 341 | ||
| 282 | if (winner != nullptr) { | 342 | if (winner != nullptr) { |
| 283 | if (winner->IsRunning()) { | ||
| 284 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 285 | } | ||
| 286 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 343 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 287 | current_thread = winner; | 344 | current_thread = winner; |
| 288 | } | 345 | } |
| @@ -292,34 +349,65 @@ void GlobalScheduler::PreemptThreads() { | |||
| 292 | } | 349 | } |
| 293 | } | 350 | } |
| 294 | 351 | ||
| 352 | void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule, | ||
| 353 | Core::EmuThreadHandle global_thread) { | ||
| 354 | u32 current_core = global_thread.host_handle; | ||
| 355 | bool must_context_switch = global_thread.guest_handle != InvalidHandle && | ||
| 356 | (current_core < Core::Hardware::NUM_CPU_CORES); | ||
| 357 | while (cores_pending_reschedule != 0) { | ||
| 358 | u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule); | ||
| 359 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 360 | if (!must_context_switch || core != current_core) { | ||
| 361 | auto& phys_core = kernel.PhysicalCore(core); | ||
| 362 | phys_core.Interrupt(); | ||
| 363 | } else { | ||
| 364 | must_context_switch = true; | ||
| 365 | } | ||
| 366 | cores_pending_reschedule &= ~(1ul << core); | ||
| 367 | } | ||
| 368 | if (must_context_switch) { | ||
| 369 | auto& core_scheduler = kernel.CurrentScheduler(); | ||
| 370 | kernel.ExitSVCProfile(); | ||
| 371 | core_scheduler.TryDoContextSwitch(); | ||
| 372 | kernel.EnterSVCProfile(); | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 295 | void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { | 376 | void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { |
| 377 | ASSERT(is_locked); | ||
| 296 | suggested_queue[core].add(thread, priority); | 378 | suggested_queue[core].add(thread, priority); |
| 297 | } | 379 | } |
| 298 | 380 | ||
| 299 | void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) { | 381 | void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) { |
| 382 | ASSERT(is_locked); | ||
| 300 | suggested_queue[core].remove(thread, priority); | 383 | suggested_queue[core].remove(thread, priority); |
| 301 | } | 384 | } |
| 302 | 385 | ||
| 303 | void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) { | 386 | void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) { |
| 387 | ASSERT(is_locked); | ||
| 304 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); | 388 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); |
| 305 | scheduled_queue[core].add(thread, priority); | 389 | scheduled_queue[core].add(thread, priority); |
| 306 | } | 390 | } |
| 307 | 391 | ||
| 308 | void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) { | 392 | void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) { |
| 393 | ASSERT(is_locked); | ||
| 309 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); | 394 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); |
| 310 | scheduled_queue[core].add(thread, priority, false); | 395 | scheduled_queue[core].add(thread, priority, false); |
| 311 | } | 396 | } |
| 312 | 397 | ||
| 313 | void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) { | 398 | void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) { |
| 399 | ASSERT(is_locked); | ||
| 314 | scheduled_queue[core].remove(thread, priority); | 400 | scheduled_queue[core].remove(thread, priority); |
| 315 | scheduled_queue[core].add(thread, priority); | 401 | scheduled_queue[core].add(thread, priority); |
| 316 | } | 402 | } |
| 317 | 403 | ||
| 318 | void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) { | 404 | void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) { |
| 405 | ASSERT(is_locked); | ||
| 319 | scheduled_queue[core].remove(thread, priority); | 406 | scheduled_queue[core].remove(thread, priority); |
| 320 | } | 407 | } |
| 321 | 408 | ||
| 322 | void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) { | 409 | void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) { |
| 410 | ASSERT(is_locked); | ||
| 323 | const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; | 411 | const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; |
| 324 | const s32 source_core = thread->GetProcessorID(); | 412 | const s32 source_core = thread->GetProcessorID(); |
| 325 | if (source_core == destination_core || !schedulable) { | 413 | if (source_core == destination_core || !schedulable) { |
| @@ -349,6 +437,108 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, | |||
| 349 | } | 437 | } |
| 350 | } | 438 | } |
| 351 | 439 | ||
| 440 | void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { | ||
| 441 | if (old_flags == thread->scheduling_state) { | ||
| 442 | return; | ||
| 443 | } | ||
| 444 | ASSERT(is_locked); | ||
| 445 | |||
| 446 | if (old_flags == static_cast<u32>(ThreadSchedStatus::Runnable)) { | ||
| 447 | // In this case the thread was running, now it's pausing/exitting | ||
| 448 | if (thread->processor_id >= 0) { | ||
| 449 | Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 450 | } | ||
| 451 | |||
| 452 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 453 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 454 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 455 | Unsuggest(thread->current_priority, core, thread); | ||
| 456 | } | ||
| 457 | } | ||
| 458 | } else if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | ||
| 459 | // The thread is now set to running from being stopped | ||
| 460 | if (thread->processor_id >= 0) { | ||
| 461 | Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 462 | } | ||
| 463 | |||
| 464 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 465 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 466 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 467 | Suggest(thread->current_priority, core, thread); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | SetReselectionPending(); | ||
| 473 | } | ||
| 474 | |||
| 475 | void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) { | ||
| 476 | if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable)) { | ||
| 477 | return; | ||
| 478 | } | ||
| 479 | ASSERT(is_locked); | ||
| 480 | if (thread->processor_id >= 0) { | ||
| 481 | Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 482 | } | ||
| 483 | |||
| 484 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 485 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 486 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 487 | Unsuggest(old_priority, core, thread); | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | if (thread->processor_id >= 0) { | ||
| 492 | if (thread == kernel.CurrentScheduler().GetCurrentThread()) { | ||
| 493 | SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id), | ||
| 494 | thread); | ||
| 495 | } else { | ||
| 496 | Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 501 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 502 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 503 | Suggest(thread->current_priority, core, thread); | ||
| 504 | } | ||
| 505 | } | ||
| 506 | thread->IncrementYieldCount(); | ||
| 507 | SetReselectionPending(); | ||
| 508 | } | ||
| 509 | |||
| 510 | void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, | ||
| 511 | s32 old_core) { | ||
| 512 | if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable) || | ||
| 513 | thread->current_priority >= THREADPRIO_COUNT) { | ||
| 514 | return; | ||
| 515 | } | ||
| 516 | ASSERT(is_locked); | ||
| 517 | |||
| 518 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 519 | if (((old_affinity_mask >> core) & 1) != 0) { | ||
| 520 | if (core == static_cast<u32>(old_core)) { | ||
| 521 | Unschedule(thread->current_priority, core, thread); | ||
| 522 | } else { | ||
| 523 | Unsuggest(thread->current_priority, core, thread); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 529 | if (((thread->affinity_mask >> core) & 1) != 0) { | ||
| 530 | if (core == static_cast<u32>(thread->processor_id)) { | ||
| 531 | Schedule(thread->current_priority, core, thread); | ||
| 532 | } else { | ||
| 533 | Suggest(thread->current_priority, core, thread); | ||
| 534 | } | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | thread->IncrementYieldCount(); | ||
| 539 | SetReselectionPending(); | ||
| 540 | } | ||
| 541 | |||
| 352 | void GlobalScheduler::Shutdown() { | 542 | void GlobalScheduler::Shutdown() { |
| 353 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 543 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 354 | scheduled_queue[core].clear(); | 544 | scheduled_queue[core].clear(); |
| @@ -359,10 +549,12 @@ void GlobalScheduler::Shutdown() { | |||
| 359 | 549 | ||
| 360 | void GlobalScheduler::Lock() { | 550 | void GlobalScheduler::Lock() { |
| 361 | Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); | 551 | Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); |
| 552 | ASSERT(!current_thread.IsInvalid()); | ||
| 362 | if (current_thread == current_owner) { | 553 | if (current_thread == current_owner) { |
| 363 | ++scope_lock; | 554 | ++scope_lock; |
| 364 | } else { | 555 | } else { |
| 365 | inner_lock.lock(); | 556 | inner_lock.lock(); |
| 557 | is_locked = true; | ||
| 366 | current_owner = current_thread; | 558 | current_owner = current_thread; |
| 367 | ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle()); | 559 | ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle()); |
| 368 | scope_lock = 1; | 560 | scope_lock = 1; |
| @@ -374,17 +566,18 @@ void GlobalScheduler::Unlock() { | |||
| 374 | ASSERT(scope_lock > 0); | 566 | ASSERT(scope_lock > 0); |
| 375 | return; | 567 | return; |
| 376 | } | 568 | } |
| 377 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | 569 | u32 cores_pending_reschedule = SelectThreads(); |
| 378 | SelectThread(i); | 570 | Core::EmuThreadHandle leaving_thread = current_owner; |
| 379 | } | ||
| 380 | current_owner = Core::EmuThreadHandle::InvalidHandle(); | 571 | current_owner = Core::EmuThreadHandle::InvalidHandle(); |
| 381 | scope_lock = 1; | 572 | scope_lock = 1; |
| 573 | is_locked = false; | ||
| 382 | inner_lock.unlock(); | 574 | inner_lock.unlock(); |
| 383 | // TODO(Blinkhawk): Setup the interrupts and change context on current core. | 575 | EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread); |
| 384 | } | 576 | } |
| 385 | 577 | ||
| 386 | Scheduler::Scheduler(Core::System& system, std::size_t core_id) | 578 | Scheduler::Scheduler(Core::System& system, std::size_t core_id) : system(system), core_id(core_id) { |
| 387 | : system{system}, core_id{core_id} {} | 579 | switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this); |
| 580 | } | ||
| 388 | 581 | ||
| 389 | Scheduler::~Scheduler() = default; | 582 | Scheduler::~Scheduler() = default; |
| 390 | 583 | ||
| @@ -393,56 +586,128 @@ bool Scheduler::HaveReadyThreads() const { | |||
| 393 | } | 586 | } |
| 394 | 587 | ||
| 395 | Thread* Scheduler::GetCurrentThread() const { | 588 | Thread* Scheduler::GetCurrentThread() const { |
| 396 | return current_thread.get(); | 589 | if (current_thread) { |
| 590 | return current_thread.get(); | ||
| 591 | } | ||
| 592 | return idle_thread.get(); | ||
| 397 | } | 593 | } |
| 398 | 594 | ||
| 399 | Thread* Scheduler::GetSelectedThread() const { | 595 | Thread* Scheduler::GetSelectedThread() const { |
| 400 | return selected_thread.get(); | 596 | return selected_thread.get(); |
| 401 | } | 597 | } |
| 402 | 598 | ||
| 403 | void Scheduler::SelectThreads() { | ||
| 404 | system.GlobalScheduler().SelectThread(core_id); | ||
| 405 | } | ||
| 406 | |||
| 407 | u64 Scheduler::GetLastContextSwitchTicks() const { | 599 | u64 Scheduler::GetLastContextSwitchTicks() const { |
| 408 | return last_context_switch_time; | 600 | return last_context_switch_time; |
| 409 | } | 601 | } |
| 410 | 602 | ||
| 411 | void Scheduler::TryDoContextSwitch() { | 603 | void Scheduler::TryDoContextSwitch() { |
| 604 | auto& phys_core = system.Kernel().CurrentPhysicalCore(); | ||
| 605 | if (phys_core.IsInterrupted()) { | ||
| 606 | phys_core.ClearInterrupt(); | ||
| 607 | } | ||
| 608 | guard.lock(); | ||
| 412 | if (is_context_switch_pending) { | 609 | if (is_context_switch_pending) { |
| 413 | SwitchContext(); | 610 | SwitchContext(); |
| 611 | } else { | ||
| 612 | guard.unlock(); | ||
| 414 | } | 613 | } |
| 415 | } | 614 | } |
| 416 | 615 | ||
| 417 | void Scheduler::UnloadThread() { | 616 | void Scheduler::OnThreadStart() { |
| 418 | Thread* const previous_thread = GetCurrentThread(); | 617 | SwitchContextStep2(); |
| 419 | Process* const previous_process = system.Kernel().CurrentProcess(); | 618 | } |
| 420 | 619 | ||
| 421 | UpdateLastContextSwitchTime(previous_thread, previous_process); | 620 | void Scheduler::Unload() { |
| 621 | Thread* thread = current_thread.get(); | ||
| 622 | if (thread) { | ||
| 623 | thread->SetContinuousOnSVC(false); | ||
| 624 | thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | ||
| 625 | thread->SetIsRunning(false); | ||
| 626 | if (!thread->IsHLEThread() && !thread->HasExited()) { | ||
| 627 | Core::ARM_Interface& cpu_core = thread->ArmInterface(); | ||
| 628 | cpu_core.SaveContext(thread->GetContext32()); | ||
| 629 | cpu_core.SaveContext(thread->GetContext64()); | ||
| 630 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 631 | thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); | ||
| 632 | cpu_core.ClearExclusiveState(); | ||
| 633 | } | ||
| 634 | thread->context_guard.unlock(); | ||
| 635 | } | ||
| 636 | } | ||
| 422 | 637 | ||
| 423 | // Save context for previous thread | 638 | void Scheduler::Reload() { |
| 424 | if (previous_thread) { | 639 | Thread* thread = current_thread.get(); |
| 425 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); | 640 | if (thread) { |
| 426 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); | 641 | ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, |
| 427 | // Save the TPIDR_EL0 system register in case it was modified. | 642 | "Thread must be runnable."); |
| 428 | previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); | 643 | |
| 644 | // Cancel any outstanding wakeup events for this thread | ||
| 645 | thread->SetIsRunning(true); | ||
| 646 | thread->SetWasRunning(false); | ||
| 647 | thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | ||
| 429 | 648 | ||
| 430 | if (previous_thread->GetStatus() == ThreadStatus::Running) { | 649 | auto* const thread_owner_process = thread->GetOwnerProcess(); |
| 431 | // This is only the case when a reschedule is triggered without the current thread | 650 | if (thread_owner_process != nullptr) { |
| 432 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | 651 | system.Kernel().MakeCurrentProcess(thread_owner_process); |
| 433 | previous_thread->SetStatus(ThreadStatus::Ready); | 652 | } |
| 653 | if (!thread->IsHLEThread()) { | ||
| 654 | Core::ARM_Interface& cpu_core = thread->ArmInterface(); | ||
| 655 | cpu_core.LoadContext(thread->GetContext32()); | ||
| 656 | cpu_core.LoadContext(thread->GetContext64()); | ||
| 657 | cpu_core.SetTlsAddress(thread->GetTLSAddress()); | ||
| 658 | cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); | ||
| 659 | cpu_core.ChangeProcessorID(this->core_id); | ||
| 660 | cpu_core.ClearExclusiveState(); | ||
| 434 | } | 661 | } |
| 435 | previous_thread->SetIsRunning(false); | ||
| 436 | } | 662 | } |
| 437 | current_thread = nullptr; | 663 | } |
| 664 | |||
| 665 | void Scheduler::SwitchContextStep2() { | ||
| 666 | Thread* previous_thread = current_thread_prev.get(); | ||
| 667 | Thread* new_thread = selected_thread.get(); | ||
| 668 | |||
| 669 | // Load context of new thread | ||
| 670 | Process* const previous_process = | ||
| 671 | previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; | ||
| 672 | |||
| 673 | if (new_thread) { | ||
| 674 | ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, | ||
| 675 | "Thread must be runnable."); | ||
| 676 | |||
| 677 | // Cancel any outstanding wakeup events for this thread | ||
| 678 | new_thread->SetIsRunning(true); | ||
| 679 | new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | ||
| 680 | new_thread->SetWasRunning(false); | ||
| 681 | |||
| 682 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | ||
| 683 | if (thread_owner_process != nullptr) { | ||
| 684 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 685 | } | ||
| 686 | if (!new_thread->IsHLEThread()) { | ||
| 687 | Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); | ||
| 688 | cpu_core.LoadContext(new_thread->GetContext32()); | ||
| 689 | cpu_core.LoadContext(new_thread->GetContext64()); | ||
| 690 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); | ||
| 691 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | ||
| 692 | cpu_core.ChangeProcessorID(this->core_id); | ||
| 693 | cpu_core.ClearExclusiveState(); | ||
| 694 | } | ||
| 695 | } | ||
| 696 | |||
| 697 | TryDoContextSwitch(); | ||
| 438 | } | 698 | } |
| 439 | 699 | ||
| 440 | void Scheduler::SwitchContext() { | 700 | void Scheduler::SwitchContext() { |
| 441 | Thread* const previous_thread = GetCurrentThread(); | 701 | current_thread_prev = current_thread; |
| 442 | Thread* const new_thread = GetSelectedThread(); | 702 | selected_thread = selected_thread_set; |
| 703 | Thread* previous_thread = current_thread_prev.get(); | ||
| 704 | Thread* new_thread = selected_thread.get(); | ||
| 705 | current_thread = selected_thread; | ||
| 443 | 706 | ||
| 444 | is_context_switch_pending = false; | 707 | is_context_switch_pending = false; |
| 708 | |||
| 445 | if (new_thread == previous_thread) { | 709 | if (new_thread == previous_thread) { |
| 710 | guard.unlock(); | ||
| 446 | return; | 711 | return; |
| 447 | } | 712 | } |
| 448 | 713 | ||
| @@ -452,51 +717,76 @@ void Scheduler::SwitchContext() { | |||
| 452 | 717 | ||
| 453 | // Save context for previous thread | 718 | // Save context for previous thread |
| 454 | if (previous_thread) { | 719 | if (previous_thread) { |
| 455 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); | 720 | if (new_thread != nullptr && new_thread->IsSuspendThread()) { |
| 456 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); | 721 | previous_thread->SetWasRunning(true); |
| 457 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 458 | previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); | ||
| 459 | |||
| 460 | if (previous_thread->GetStatus() == ThreadStatus::Running) { | ||
| 461 | // This is only the case when a reschedule is triggered without the current thread | ||
| 462 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | ||
| 463 | previous_thread->SetStatus(ThreadStatus::Ready); | ||
| 464 | } | 722 | } |
| 723 | previous_thread->SetContinuousOnSVC(false); | ||
| 724 | previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | ||
| 465 | previous_thread->SetIsRunning(false); | 725 | previous_thread->SetIsRunning(false); |
| 726 | if (!previous_thread->IsHLEThread() && !previous_thread->HasExited()) { | ||
| 727 | Core::ARM_Interface& cpu_core = previous_thread->ArmInterface(); | ||
| 728 | cpu_core.SaveContext(previous_thread->GetContext32()); | ||
| 729 | cpu_core.SaveContext(previous_thread->GetContext64()); | ||
| 730 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 731 | previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); | ||
| 732 | cpu_core.ClearExclusiveState(); | ||
| 733 | } | ||
| 734 | previous_thread->context_guard.unlock(); | ||
| 466 | } | 735 | } |
| 467 | 736 | ||
| 468 | // Load context of new thread | 737 | std::shared_ptr<Common::Fiber>* old_context; |
| 469 | if (new_thread) { | 738 | if (previous_thread != nullptr) { |
| 470 | ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), | 739 | old_context = &previous_thread->GetHostContext(); |
| 471 | "Thread must be assigned to this core."); | 740 | } else { |
| 472 | ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, | 741 | old_context = &idle_thread->GetHostContext(); |
| 473 | "Thread must be ready to become running."); | 742 | } |
| 743 | guard.unlock(); | ||
| 474 | 744 | ||
| 475 | // Cancel any outstanding wakeup events for this thread | 745 | Common::Fiber::YieldTo(*old_context, switch_fiber); |
| 476 | new_thread->CancelWakeupTimer(); | 746 | /// When a thread wakes up, the scheduler may have changed to other in another core. |
| 477 | current_thread = SharedFrom(new_thread); | 747 | auto& next_scheduler = system.Kernel().CurrentScheduler(); |
| 478 | new_thread->SetStatus(ThreadStatus::Running); | 748 | next_scheduler.SwitchContextStep2(); |
| 479 | new_thread->SetIsRunning(true); | 749 | } |
| 480 | 750 | ||
| 481 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | 751 | void Scheduler::OnSwitch(void* this_scheduler) { |
| 482 | if (previous_process != thread_owner_process) { | 752 | Scheduler* sched = static_cast<Scheduler*>(this_scheduler); |
| 483 | system.Kernel().MakeCurrentProcess(thread_owner_process); | 753 | sched->SwitchToCurrent(); |
| 484 | } | 754 | } |
| 485 | 755 | ||
| 486 | system.ArmInterface(core_id).LoadContext(new_thread->GetContext32()); | 756 | void Scheduler::SwitchToCurrent() { |
| 487 | system.ArmInterface(core_id).LoadContext(new_thread->GetContext64()); | 757 | while (true) { |
| 488 | system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress()); | 758 | { |
| 489 | system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | 759 | std::scoped_lock lock{guard}; |
| 490 | } else { | 760 | selected_thread = selected_thread_set; |
| 491 | current_thread = nullptr; | 761 | current_thread = selected_thread; |
| 492 | // Note: We do not reset the current process and current page table when idling because | 762 | is_context_switch_pending = false; |
| 493 | // technically we haven't changed processes, our threads are just paused. | 763 | } |
| 764 | while (!is_context_switch_pending) { | ||
| 765 | if (current_thread != nullptr && !current_thread->IsHLEThread()) { | ||
| 766 | current_thread->context_guard.lock(); | ||
| 767 | if (!current_thread->IsRunnable()) { | ||
| 768 | current_thread->context_guard.unlock(); | ||
| 769 | break; | ||
| 770 | } | ||
| 771 | if (current_thread->GetProcessorID() != core_id) { | ||
| 772 | current_thread->context_guard.unlock(); | ||
| 773 | break; | ||
| 774 | } | ||
| 775 | } | ||
| 776 | std::shared_ptr<Common::Fiber>* next_context; | ||
| 777 | if (current_thread != nullptr) { | ||
| 778 | next_context = ¤t_thread->GetHostContext(); | ||
| 779 | } else { | ||
| 780 | next_context = &idle_thread->GetHostContext(); | ||
| 781 | } | ||
| 782 | Common::Fiber::YieldTo(switch_fiber, *next_context); | ||
| 783 | } | ||
| 494 | } | 784 | } |
| 495 | } | 785 | } |
| 496 | 786 | ||
| 497 | void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { | 787 | void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { |
| 498 | const u64 prev_switch_ticks = last_context_switch_time; | 788 | const u64 prev_switch_ticks = last_context_switch_time; |
| 499 | const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks(); | 789 | const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); |
| 500 | const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; | 790 | const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; |
| 501 | 791 | ||
| 502 | if (thread != nullptr) { | 792 | if (thread != nullptr) { |
| @@ -510,6 +800,16 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { | |||
| 510 | last_context_switch_time = most_recent_switch_ticks; | 800 | last_context_switch_time = most_recent_switch_ticks; |
| 511 | } | 801 | } |
| 512 | 802 | ||
| 803 | void Scheduler::Initialize() { | ||
| 804 | std::string name = "Idle Thread Id:" + std::to_string(core_id); | ||
| 805 | std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc(); | ||
| 806 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 807 | ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE); | ||
| 808 | auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0, | ||
| 809 | nullptr, std::move(init_func), init_func_parameter); | ||
| 810 | idle_thread = std::move(thread_res).Unwrap(); | ||
| 811 | } | ||
| 812 | |||
| 513 | void Scheduler::Shutdown() { | 813 | void Scheduler::Shutdown() { |
| 514 | current_thread = nullptr; | 814 | current_thread = nullptr; |
| 515 | selected_thread = nullptr; | 815 | selected_thread = nullptr; |
| @@ -538,4 +838,13 @@ SchedulerLockAndSleep::~SchedulerLockAndSleep() { | |||
| 538 | time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); | 838 | time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); |
| 539 | } | 839 | } |
| 540 | 840 | ||
| 841 | void SchedulerLockAndSleep::Release() { | ||
| 842 | if (sleep_cancelled) { | ||
| 843 | return; | ||
| 844 | } | ||
| 845 | auto& time_manager = kernel.TimeManager(); | ||
| 846 | time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); | ||
| 847 | sleep_cancelled = true; | ||
| 848 | } | ||
| 849 | |||
| 541 | } // namespace Kernel | 850 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 07df33f9c..b3b4b5169 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -11,9 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/multi_level_queue.h" | 13 | #include "common/multi_level_queue.h" |
| 14 | #include "common/spin_lock.h" | ||
| 14 | #include "core/hardware_properties.h" | 15 | #include "core/hardware_properties.h" |
| 15 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 16 | 17 | ||
| 18 | namespace Common { | ||
| 19 | class Fiber; | ||
| 20 | } | ||
| 21 | |||
| 17 | namespace Core { | 22 | namespace Core { |
| 18 | class ARM_Interface; | 23 | class ARM_Interface; |
| 19 | class System; | 24 | class System; |
| @@ -41,41 +46,17 @@ public: | |||
| 41 | return thread_list; | 46 | return thread_list; |
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | /** | 49 | /// Notify the scheduler a thread's status has changed. |
| 45 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be | 50 | void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags); |
| 46 | * picked if no thread is scheduled to run on the core. | ||
| 47 | */ | ||
| 48 | void Suggest(u32 priority, std::size_t core, Thread* thread); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Remove a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 52 | * picked if no thread is scheduled to run on the core. | ||
| 53 | */ | ||
| 54 | void Unsuggest(u32 priority, std::size_t core, Thread* thread); | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 58 | * back the queue in its priority level. | ||
| 59 | */ | ||
| 60 | void Schedule(u32 priority, std::size_t core, Thread* thread); | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 64 | * front the queue in its priority level. | ||
| 65 | */ | ||
| 66 | void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); | ||
| 67 | 51 | ||
| 68 | /// Reschedule an already scheduled thread based on a new priority | 52 | /// Notify the scheduler a thread's priority has changed. |
| 69 | void Reschedule(u32 priority, std::size_t core, Thread* thread); | 53 | void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority); |
| 70 | |||
| 71 | /// Unschedules a thread. | ||
| 72 | void Unschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 73 | 54 | ||
| 74 | /// Selects a core and forces it to unload its current thread's context | 55 | /// Notify the scheduler a thread's core and/or affinity mask has changed. |
| 75 | void UnloadThread(std::size_t core); | 56 | void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core); |
| 76 | 57 | ||
| 77 | /** | 58 | /** |
| 78 | * Takes care of selecting the new scheduled thread in three steps: | 59 | * Takes care of selecting the new scheduled threads in three steps: |
| 79 | * | 60 | * |
| 80 | * 1. First a thread is selected from the top of the priority queue. If no thread | 61 | * 1. First a thread is selected from the top of the priority queue. If no thread |
| 81 | * is obtained then we move to step two, else we are done. | 62 | * is obtained then we move to step two, else we are done. |
| @@ -85,8 +66,10 @@ public: | |||
| 85 | * | 66 | * |
| 86 | * 3. Third is no suggested thread is found, we do a second pass and pick a running | 67 | * 3. Third is no suggested thread is found, we do a second pass and pick a running |
| 87 | * thread in another core and swap it with its current thread. | 68 | * thread in another core and swap it with its current thread. |
| 69 | * | ||
| 70 | * returns the cores needing scheduling. | ||
| 88 | */ | 71 | */ |
| 89 | void SelectThread(std::size_t core); | 72 | u32 SelectThreads(); |
| 90 | 73 | ||
| 91 | bool HaveReadyThreads(std::size_t core_id) const { | 74 | bool HaveReadyThreads(std::size_t core_id) const { |
| 92 | return !scheduled_queue[core_id].empty(); | 75 | return !scheduled_queue[core_id].empty(); |
| @@ -149,6 +132,40 @@ private: | |||
| 149 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling | 132 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling |
| 150 | /// and reschedules current core if needed. | 133 | /// and reschedules current core if needed. |
| 151 | void Unlock(); | 134 | void Unlock(); |
| 135 | |||
| 136 | void EnableInterruptAndSchedule(u32 cores_pending_reschedule, | ||
| 137 | Core::EmuThreadHandle global_thread); | ||
| 138 | |||
| 139 | /** | ||
| 140 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 141 | * picked if no thread is scheduled to run on the core. | ||
| 142 | */ | ||
| 143 | void Suggest(u32 priority, std::size_t core, Thread* thread); | ||
| 144 | |||
| 145 | /** | ||
| 146 | * Remove a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 147 | * picked if no thread is scheduled to run on the core. | ||
| 148 | */ | ||
| 149 | void Unsuggest(u32 priority, std::size_t core, Thread* thread); | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 153 | * back the queue in its priority level. | ||
| 154 | */ | ||
| 155 | void Schedule(u32 priority, std::size_t core, Thread* thread); | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 159 | * front the queue in its priority level. | ||
| 160 | */ | ||
| 161 | void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); | ||
| 162 | |||
| 163 | /// Reschedule an already scheduled thread based on a new priority | ||
| 164 | void Reschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 165 | |||
| 166 | /// Unschedules a thread. | ||
| 167 | void Unschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 168 | |||
| 152 | /** | 169 | /** |
| 153 | * Transfers a thread into an specific core. If the destination_core is -1 | 170 | * Transfers a thread into an specific core. If the destination_core is -1 |
| 154 | * it will be unscheduled from its source code and added into its suggested | 171 | * it will be unscheduled from its source code and added into its suggested |
| @@ -170,10 +187,13 @@ private: | |||
| 170 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; | 187 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; |
| 171 | 188 | ||
| 172 | /// Scheduler lock mechanisms. | 189 | /// Scheduler lock mechanisms. |
| 173 | std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock | 190 | bool is_locked{}; |
| 191 | Common::SpinLock inner_lock{}; | ||
| 174 | std::atomic<s64> scope_lock{}; | 192 | std::atomic<s64> scope_lock{}; |
| 175 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; | 193 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; |
| 176 | 194 | ||
| 195 | Common::SpinLock global_list_guard{}; | ||
| 196 | |||
| 177 | /// Lists all thread ids that aren't deleted/etc. | 197 | /// Lists all thread ids that aren't deleted/etc. |
| 178 | std::vector<std::shared_ptr<Thread>> thread_list; | 198 | std::vector<std::shared_ptr<Thread>> thread_list; |
| 179 | KernelCore& kernel; | 199 | KernelCore& kernel; |
| @@ -190,11 +210,11 @@ public: | |||
| 190 | /// Reschedules to the next available thread (call after current thread is suspended) | 210 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 191 | void TryDoContextSwitch(); | 211 | void TryDoContextSwitch(); |
| 192 | 212 | ||
| 193 | /// Unloads currently running thread | 213 | /// The next two are for SingleCore Only. |
| 194 | void UnloadThread(); | 214 | /// Unload current thread before preempting core. |
| 195 | 215 | void Unload(); | |
| 196 | /// Select the threads in top of the scheduling multilist. | 216 | /// Reload current thread after core preemption. |
| 197 | void SelectThreads(); | 217 | void Reload(); |
| 198 | 218 | ||
| 199 | /// Gets the current running thread | 219 | /// Gets the current running thread |
| 200 | Thread* GetCurrentThread() const; | 220 | Thread* GetCurrentThread() const; |
| @@ -209,15 +229,30 @@ public: | |||
| 209 | return is_context_switch_pending; | 229 | return is_context_switch_pending; |
| 210 | } | 230 | } |
| 211 | 231 | ||
| 232 | void Initialize(); | ||
| 233 | |||
| 212 | /// Shutdowns the scheduler. | 234 | /// Shutdowns the scheduler. |
| 213 | void Shutdown(); | 235 | void Shutdown(); |
| 214 | 236 | ||
| 237 | void OnThreadStart(); | ||
| 238 | |||
| 239 | std::shared_ptr<Common::Fiber>& ControlContext() { | ||
| 240 | return switch_fiber; | ||
| 241 | } | ||
| 242 | |||
| 243 | const std::shared_ptr<Common::Fiber>& ControlContext() const { | ||
| 244 | return switch_fiber; | ||
| 245 | } | ||
| 246 | |||
| 215 | private: | 247 | private: |
| 216 | friend class GlobalScheduler; | 248 | friend class GlobalScheduler; |
| 217 | 249 | ||
| 218 | /// Switches the CPU's active thread context to that of the specified thread | 250 | /// Switches the CPU's active thread context to that of the specified thread |
| 219 | void SwitchContext(); | 251 | void SwitchContext(); |
| 220 | 252 | ||
| 253 | /// When a thread wakes up, it must run this through it's new scheduler | ||
| 254 | void SwitchContextStep2(); | ||
| 255 | |||
| 221 | /** | 256 | /** |
| 222 | * Called on every context switch to update the internal timestamp | 257 | * Called on every context switch to update the internal timestamp |
| 223 | * This also updates the running time ticks for the given thread and | 258 | * This also updates the running time ticks for the given thread and |
| @@ -231,14 +266,24 @@ private: | |||
| 231 | */ | 266 | */ |
| 232 | void UpdateLastContextSwitchTime(Thread* thread, Process* process); | 267 | void UpdateLastContextSwitchTime(Thread* thread, Process* process); |
| 233 | 268 | ||
| 269 | static void OnSwitch(void* this_scheduler); | ||
| 270 | void SwitchToCurrent(); | ||
| 271 | |||
| 234 | std::shared_ptr<Thread> current_thread = nullptr; | 272 | std::shared_ptr<Thread> current_thread = nullptr; |
| 235 | std::shared_ptr<Thread> selected_thread = nullptr; | 273 | std::shared_ptr<Thread> selected_thread = nullptr; |
| 274 | std::shared_ptr<Thread> current_thread_prev = nullptr; | ||
| 275 | std::shared_ptr<Thread> selected_thread_set = nullptr; | ||
| 276 | std::shared_ptr<Thread> idle_thread = nullptr; | ||
| 277 | |||
| 278 | std::shared_ptr<Common::Fiber> switch_fiber = nullptr; | ||
| 236 | 279 | ||
| 237 | Core::System& system; | 280 | Core::System& system; |
| 238 | u64 last_context_switch_time = 0; | 281 | u64 last_context_switch_time = 0; |
| 239 | u64 idle_selection_count = 0; | 282 | u64 idle_selection_count = 0; |
| 240 | const std::size_t core_id; | 283 | const std::size_t core_id; |
| 241 | 284 | ||
| 285 | Common::SpinLock guard{}; | ||
| 286 | |||
| 242 | bool is_context_switch_pending = false; | 287 | bool is_context_switch_pending = false; |
| 243 | }; | 288 | }; |
| 244 | 289 | ||
| @@ -261,6 +306,8 @@ public: | |||
| 261 | sleep_cancelled = true; | 306 | sleep_cancelled = true; |
| 262 | } | 307 | } |
| 263 | 308 | ||
| 309 | void Release(); | ||
| 310 | |||
| 264 | private: | 311 | private: |
| 265 | Handle& event_handle; | 312 | Handle& event_handle; |
| 266 | Thread* time_task; | 313 | Thread* time_task; |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 25438b86b..7b23a6889 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/hle/kernel/hle_ipc.h" | 17 | #include "core/hle/kernel/hle_ipc.h" |
| 18 | #include "core/hle/kernel/kernel.h" | 18 | #include "core/hle/kernel/kernel.h" |
| 19 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| 20 | #include "core/hle/kernel/scheduler.h" | ||
| 20 | #include "core/hle/kernel/server_session.h" | 21 | #include "core/hle/kernel/server_session.h" |
| 21 | #include "core/hle/kernel/session.h" | 22 | #include "core/hle/kernel/session.h" |
| 22 | #include "core/hle/kernel/thread.h" | 23 | #include "core/hle/kernel/thread.h" |
| @@ -168,9 +169,12 @@ ResultCode ServerSession::CompleteSyncRequest() { | |||
| 168 | } | 169 | } |
| 169 | 170 | ||
| 170 | // Some service requests require the thread to block | 171 | // Some service requests require the thread to block |
| 171 | if (!context.IsThreadWaiting()) { | 172 | { |
| 172 | context.GetThread().ResumeFromWait(); | 173 | SchedulerLock lock(kernel); |
| 173 | context.GetThread().SetWaitSynchronizationResult(result); | 174 | if (!context.IsThreadWaiting()) { |
| 175 | context.GetThread().ResumeFromWait(); | ||
| 176 | context.GetThread().SetSynchronizationResults(nullptr, result); | ||
| 177 | } | ||
| 174 | } | 178 | } |
| 175 | 179 | ||
| 176 | request_queue.Pop(); | 180 | request_queue.Pop(); |
| @@ -180,8 +184,10 @@ ResultCode ServerSession::CompleteSyncRequest() { | |||
| 180 | 184 | ||
| 181 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | 185 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, |
| 182 | Core::Memory::Memory& memory) { | 186 | Core::Memory::Memory& memory) { |
| 183 | Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {}); | 187 | ResultCode result = QueueSyncRequest(std::move(thread), memory); |
| 184 | return QueueSyncRequest(std::move(thread), memory); | 188 | const u64 delay = kernel.IsMulticore() ? 0U : 20000U; |
| 189 | Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); | ||
| 190 | return result; | ||
| 185 | } | 191 | } |
| 186 | 192 | ||
| 187 | } // namespace Kernel | 193 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4ae4529f5..5db19dcf3 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -10,14 +10,15 @@ | |||
| 10 | 10 | ||
| 11 | #include "common/alignment.h" | 11 | #include "common/alignment.h" |
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/fiber.h" | ||
| 13 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 14 | #include "common/microprofile.h" | 15 | #include "common/microprofile.h" |
| 15 | #include "common/string_util.h" | 16 | #include "common/string_util.h" |
| 16 | #include "core/arm/exclusive_monitor.h" | 17 | #include "core/arm/exclusive_monitor.h" |
| 17 | #include "core/core.h" | 18 | #include "core/core.h" |
| 18 | #include "core/core_manager.h" | ||
| 19 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 20 | #include "core/core_timing_util.h" |
| 21 | #include "core/cpu_manager.h" | ||
| 21 | #include "core/hle/kernel/address_arbiter.h" | 22 | #include "core/hle/kernel/address_arbiter.h" |
| 22 | #include "core/hle/kernel/client_port.h" | 23 | #include "core/hle/kernel/client_port.h" |
| 23 | #include "core/hle/kernel/client_session.h" | 24 | #include "core/hle/kernel/client_session.h" |
| @@ -27,6 +28,7 @@ | |||
| 27 | #include "core/hle/kernel/memory/memory_block.h" | 28 | #include "core/hle/kernel/memory/memory_block.h" |
| 28 | #include "core/hle/kernel/memory/page_table.h" | 29 | #include "core/hle/kernel/memory/page_table.h" |
| 29 | #include "core/hle/kernel/mutex.h" | 30 | #include "core/hle/kernel/mutex.h" |
| 31 | #include "core/hle/kernel/physical_core.h" | ||
| 30 | #include "core/hle/kernel/process.h" | 32 | #include "core/hle/kernel/process.h" |
| 31 | #include "core/hle/kernel/readable_event.h" | 33 | #include "core/hle/kernel/readable_event.h" |
| 32 | #include "core/hle/kernel/resource_limit.h" | 34 | #include "core/hle/kernel/resource_limit.h" |
| @@ -37,6 +39,7 @@ | |||
| 37 | #include "core/hle/kernel/svc_wrap.h" | 39 | #include "core/hle/kernel/svc_wrap.h" |
| 38 | #include "core/hle/kernel/synchronization.h" | 40 | #include "core/hle/kernel/synchronization.h" |
| 39 | #include "core/hle/kernel/thread.h" | 41 | #include "core/hle/kernel/thread.h" |
| 42 | #include "core/hle/kernel/time_manager.h" | ||
| 40 | #include "core/hle/kernel/transfer_memory.h" | 43 | #include "core/hle/kernel/transfer_memory.h" |
| 41 | #include "core/hle/kernel/writable_event.h" | 44 | #include "core/hle/kernel/writable_event.h" |
| 42 | #include "core/hle/lock.h" | 45 | #include "core/hle/lock.h" |
| @@ -133,6 +136,7 @@ enum class ResourceLimitValueType { | |||
| 133 | 136 | ||
| 134 | ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, | 137 | ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, |
| 135 | u32 resource_type, ResourceLimitValueType value_type) { | 138 | u32 resource_type, ResourceLimitValueType value_type) { |
| 139 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 136 | const auto type = static_cast<ResourceType>(resource_type); | 140 | const auto type = static_cast<ResourceType>(resource_type); |
| 137 | if (!IsValidResourceType(type)) { | 141 | if (!IsValidResourceType(type)) { |
| 138 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); | 142 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); |
| @@ -160,6 +164,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_ | |||
| 160 | 164 | ||
| 161 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | 165 | /// Set the process heap to a given Size. It can both extend and shrink the heap. |
| 162 | static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { | 166 | static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { |
| 167 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 163 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); | 168 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); |
| 164 | 169 | ||
| 165 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. | 170 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. |
| @@ -190,6 +195,7 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s | |||
| 190 | 195 | ||
| 191 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, | 196 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, |
| 192 | u32 attribute) { | 197 | u32 attribute) { |
| 198 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 193 | LOG_DEBUG(Kernel_SVC, | 199 | LOG_DEBUG(Kernel_SVC, |
| 194 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | 200 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, |
| 195 | size, mask, attribute); | 201 | size, mask, attribute); |
| @@ -226,8 +232,15 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si | |||
| 226 | static_cast<Memory::MemoryAttribute>(attribute)); | 232 | static_cast<Memory::MemoryAttribute>(attribute)); |
| 227 | } | 233 | } |
| 228 | 234 | ||
| 235 | static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, | ||
| 236 | u32 attribute) { | ||
| 237 | return SetMemoryAttribute(system, static_cast<VAddr>(address), static_cast<std::size_t>(size), | ||
| 238 | mask, attribute); | ||
| 239 | } | ||
| 240 | |||
| 229 | /// Maps a memory range into a different range. | 241 | /// Maps a memory range into a different range. |
| 230 | static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | 242 | static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 243 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 231 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 244 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 232 | src_addr, size); | 245 | src_addr, size); |
| 233 | 246 | ||
| @@ -241,8 +254,14 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr | |||
| 241 | return page_table.Map(dst_addr, src_addr, size); | 254 | return page_table.Map(dst_addr, src_addr, size); |
| 242 | } | 255 | } |
| 243 | 256 | ||
| 257 | static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 258 | return MapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr), | ||
| 259 | static_cast<std::size_t>(size)); | ||
| 260 | } | ||
| 261 | |||
| 244 | /// Unmaps a region that was previously mapped with svcMapMemory | 262 | /// Unmaps a region that was previously mapped with svcMapMemory |
| 245 | static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | 263 | static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 264 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 246 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 265 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 247 | src_addr, size); | 266 | src_addr, size); |
| 248 | 267 | ||
| @@ -256,9 +275,15 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad | |||
| 256 | return page_table.Unmap(dst_addr, src_addr, size); | 275 | return page_table.Unmap(dst_addr, src_addr, size); |
| 257 | } | 276 | } |
| 258 | 277 | ||
| 278 | static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 279 | return UnmapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr), | ||
| 280 | static_cast<std::size_t>(size)); | ||
| 281 | } | ||
| 282 | |||
| 259 | /// Connect to an OS service given the port name, returns the handle to the port to out | 283 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 260 | static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, | 284 | static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, |
| 261 | VAddr port_name_address) { | 285 | VAddr port_name_address) { |
| 286 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 262 | auto& memory = system.Memory(); | 287 | auto& memory = system.Memory(); |
| 263 | 288 | ||
| 264 | if (!memory.IsValidVirtualAddress(port_name_address)) { | 289 | if (!memory.IsValidVirtualAddress(port_name_address)) { |
| @@ -317,11 +342,30 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { | |||
| 317 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | 342 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); |
| 318 | 343 | ||
| 319 | auto thread = system.CurrentScheduler().GetCurrentThread(); | 344 | auto thread = system.CurrentScheduler().GetCurrentThread(); |
| 320 | thread->InvalidateWakeupCallback(); | 345 | { |
| 321 | thread->SetStatus(ThreadStatus::WaitIPC); | 346 | SchedulerLock lock(system.Kernel()); |
| 322 | system.PrepareReschedule(thread->GetProcessorID()); | 347 | thread->InvalidateHLECallback(); |
| 348 | thread->SetStatus(ThreadStatus::WaitIPC); | ||
| 349 | session->SendSyncRequest(SharedFrom(thread), system.Memory()); | ||
| 350 | } | ||
| 351 | |||
| 352 | if (thread->HasHLECallback()) { | ||
| 353 | Handle event_handle = thread->GetHLETimeEvent(); | ||
| 354 | if (event_handle != InvalidHandle) { | ||
| 355 | auto& time_manager = system.Kernel().TimeManager(); | ||
| 356 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 357 | } | ||
| 358 | |||
| 359 | { | ||
| 360 | SchedulerLock lock(system.Kernel()); | ||
| 361 | auto* sync_object = thread->GetHLESyncObject(); | ||
| 362 | sync_object->RemoveWaitingThread(SharedFrom(thread)); | ||
| 363 | } | ||
| 364 | |||
| 365 | thread->InvokeHLECallback(SharedFrom(thread)); | ||
| 366 | } | ||
| 323 | 367 | ||
| 324 | return session->SendSyncRequest(SharedFrom(thread), system.Memory()); | 368 | return thread->GetSignalingResult(); |
| 325 | } | 369 | } |
| 326 | 370 | ||
| 327 | static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { | 371 | static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { |
| @@ -383,6 +427,15 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han | |||
| 383 | return ERR_INVALID_HANDLE; | 427 | return ERR_INVALID_HANDLE; |
| 384 | } | 428 | } |
| 385 | 429 | ||
| 430 | static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high, | ||
| 431 | Handle handle) { | ||
| 432 | u64 process_id{}; | ||
| 433 | const auto result = GetProcessId(system, &process_id, handle); | ||
| 434 | *process_id_low = static_cast<u32>(process_id); | ||
| 435 | *process_id_high = static_cast<u32>(process_id >> 32); | ||
| 436 | return result; | ||
| 437 | } | ||
| 438 | |||
| 386 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 439 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 387 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, | 440 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, |
| 388 | u64 handle_count, s64 nano_seconds) { | 441 | u64 handle_count, s64 nano_seconds) { |
| @@ -447,10 +500,13 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand | |||
| 447 | } | 500 | } |
| 448 | 501 | ||
| 449 | thread->CancelWait(); | 502 | thread->CancelWait(); |
| 450 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 451 | return RESULT_SUCCESS; | 503 | return RESULT_SUCCESS; |
| 452 | } | 504 | } |
| 453 | 505 | ||
| 506 | static ResultCode CancelSynchronization32(Core::System& system, Handle thread_handle) { | ||
| 507 | return CancelSynchronization(system, thread_handle); | ||
| 508 | } | ||
| 509 | |||
| 454 | /// Attempts to locks a mutex, creating it if it does not already exist | 510 | /// Attempts to locks a mutex, creating it if it does not already exist |
| 455 | static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, | 511 | static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, |
| 456 | VAddr mutex_addr, Handle requesting_thread_handle) { | 512 | VAddr mutex_addr, Handle requesting_thread_handle) { |
| @@ -475,6 +531,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand | |||
| 475 | requesting_thread_handle); | 531 | requesting_thread_handle); |
| 476 | } | 532 | } |
| 477 | 533 | ||
| 534 | static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, | ||
| 535 | u32 mutex_addr, Handle requesting_thread_handle) { | ||
| 536 | return ArbitrateLock(system, holding_thread_handle, static_cast<VAddr>(mutex_addr), | ||
| 537 | requesting_thread_handle); | ||
| 538 | } | ||
| 539 | |||
| 478 | /// Unlock a mutex | 540 | /// Unlock a mutex |
| 479 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { | 541 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { |
| 480 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | 542 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); |
| @@ -494,6 +556,10 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { | |||
| 494 | return current_process->GetMutex().Release(mutex_addr); | 556 | return current_process->GetMutex().Release(mutex_addr); |
| 495 | } | 557 | } |
| 496 | 558 | ||
| 559 | static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { | ||
| 560 | return ArbitrateUnlock(system, static_cast<VAddr>(mutex_addr)); | ||
| 561 | } | ||
| 562 | |||
| 497 | enum class BreakType : u32 { | 563 | enum class BreakType : u32 { |
| 498 | Panic = 0, | 564 | Panic = 0, |
| 499 | AssertionFailed = 1, | 565 | AssertionFailed = 1, |
| @@ -594,6 +660,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 594 | info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); | 660 | info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); |
| 595 | 661 | ||
| 596 | if (!break_reason.signal_debugger) { | 662 | if (!break_reason.signal_debugger) { |
| 663 | SchedulerLock lock(system.Kernel()); | ||
| 597 | LOG_CRITICAL( | 664 | LOG_CRITICAL( |
| 598 | Debug_Emulated, | 665 | Debug_Emulated, |
| 599 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 666 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| @@ -605,14 +672,16 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 605 | const auto thread_processor_id = current_thread->GetProcessorID(); | 672 | const auto thread_processor_id = current_thread->GetProcessorID(); |
| 606 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); | 673 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); |
| 607 | 674 | ||
| 608 | system.Kernel().CurrentProcess()->PrepareForTermination(); | ||
| 609 | |||
| 610 | // Kill the current thread | 675 | // Kill the current thread |
| 676 | system.Kernel().ExceptionalExit(); | ||
| 611 | current_thread->Stop(); | 677 | current_thread->Stop(); |
| 612 | system.PrepareReschedule(); | ||
| 613 | } | 678 | } |
| 614 | } | 679 | } |
| 615 | 680 | ||
| 681 | static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { | ||
| 682 | Break(system, reason, static_cast<u64>(info1), static_cast<u64>(info2)); | ||
| 683 | } | ||
| 684 | |||
| 616 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 685 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 617 | static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { | 686 | static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { |
| 618 | if (len == 0) { | 687 | if (len == 0) { |
| @@ -627,6 +696,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre | |||
| 627 | /// Gets system/memory information for the current process | 696 | /// Gets system/memory information for the current process |
| 628 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, | 697 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, |
| 629 | u64 info_sub_id) { | 698 | u64 info_sub_id) { |
| 699 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 630 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | 700 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, |
| 631 | info_sub_id, handle); | 701 | info_sub_id, handle); |
| 632 | 702 | ||
| @@ -863,9 +933,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | |||
| 863 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { | 933 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { |
| 864 | const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); | 934 | const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); |
| 865 | 935 | ||
| 866 | out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); | 936 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); |
| 867 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { | 937 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { |
| 868 | out_ticks = core_timing.GetTicks() - prev_ctx_ticks; | 938 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; |
| 869 | } | 939 | } |
| 870 | 940 | ||
| 871 | *result = out_ticks; | 941 | *result = out_ticks; |
| @@ -892,6 +962,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h | |||
| 892 | 962 | ||
| 893 | /// Maps memory at a desired address | 963 | /// Maps memory at a desired address |
| 894 | static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | 964 | static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { |
| 965 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 895 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | 966 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); |
| 896 | 967 | ||
| 897 | if (!Common::Is4KBAligned(addr)) { | 968 | if (!Common::Is4KBAligned(addr)) { |
| @@ -939,8 +1010,13 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) | |||
| 939 | return page_table.MapPhysicalMemory(addr, size); | 1010 | return page_table.MapPhysicalMemory(addr, size); |
| 940 | } | 1011 | } |
| 941 | 1012 | ||
| 1013 | static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 1014 | return MapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size)); | ||
| 1015 | } | ||
| 1016 | |||
| 942 | /// Unmaps memory previously mapped via MapPhysicalMemory | 1017 | /// Unmaps memory previously mapped via MapPhysicalMemory |
| 943 | static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | 1018 | static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { |
| 1019 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 944 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | 1020 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); |
| 945 | 1021 | ||
| 946 | if (!Common::Is4KBAligned(addr)) { | 1022 | if (!Common::Is4KBAligned(addr)) { |
| @@ -988,6 +1064,10 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size | |||
| 988 | return page_table.UnmapPhysicalMemory(addr, size); | 1064 | return page_table.UnmapPhysicalMemory(addr, size); |
| 989 | } | 1065 | } |
| 990 | 1066 | ||
| 1067 | static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 1068 | return UnmapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size)); | ||
| 1069 | } | ||
| 1070 | |||
| 991 | /// Sets the thread activity | 1071 | /// Sets the thread activity |
| 992 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { | 1072 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { |
| 993 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); | 1073 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); |
| @@ -1017,10 +1097,11 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act | |||
| 1017 | return ERR_BUSY; | 1097 | return ERR_BUSY; |
| 1018 | } | 1098 | } |
| 1019 | 1099 | ||
| 1020 | thread->SetActivity(static_cast<ThreadActivity>(activity)); | 1100 | return thread->SetActivity(static_cast<ThreadActivity>(activity)); |
| 1101 | } | ||
| 1021 | 1102 | ||
| 1022 | system.PrepareReschedule(thread->GetProcessorID()); | 1103 | static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) { |
| 1023 | return RESULT_SUCCESS; | 1104 | return SetThreadActivity(system, handle, activity); |
| 1024 | } | 1105 | } |
| 1025 | 1106 | ||
| 1026 | /// Gets the thread context | 1107 | /// Gets the thread context |
| @@ -1064,6 +1145,10 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H | |||
| 1064 | return RESULT_SUCCESS; | 1145 | return RESULT_SUCCESS; |
| 1065 | } | 1146 | } |
| 1066 | 1147 | ||
| 1148 | static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) { | ||
| 1149 | return GetThreadContext(system, static_cast<VAddr>(thread_context), handle); | ||
| 1150 | } | ||
| 1151 | |||
| 1067 | /// Gets the priority for the specified thread | 1152 | /// Gets the priority for the specified thread |
| 1068 | static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { | 1153 | static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { |
| 1069 | LOG_TRACE(Kernel_SVC, "called"); | 1154 | LOG_TRACE(Kernel_SVC, "called"); |
| @@ -1071,6 +1156,7 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle | |||
| 1071 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1156 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1072 | const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); | 1157 | const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); |
| 1073 | if (!thread) { | 1158 | if (!thread) { |
| 1159 | *priority = 0; | ||
| 1074 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 1160 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| 1075 | return ERR_INVALID_HANDLE; | 1161 | return ERR_INVALID_HANDLE; |
| 1076 | } | 1162 | } |
| @@ -1105,18 +1191,26 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri | |||
| 1105 | 1191 | ||
| 1106 | thread->SetPriority(priority); | 1192 | thread->SetPriority(priority); |
| 1107 | 1193 | ||
| 1108 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1109 | return RESULT_SUCCESS; | 1194 | return RESULT_SUCCESS; |
| 1110 | } | 1195 | } |
| 1111 | 1196 | ||
| 1197 | static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) { | ||
| 1198 | return SetThreadPriority(system, handle, priority); | ||
| 1199 | } | ||
| 1200 | |||
| 1112 | /// Get which CPU core is executing the current thread | 1201 | /// Get which CPU core is executing the current thread |
| 1113 | static u32 GetCurrentProcessorNumber(Core::System& system) { | 1202 | static u32 GetCurrentProcessorNumber(Core::System& system) { |
| 1114 | LOG_TRACE(Kernel_SVC, "called"); | 1203 | LOG_TRACE(Kernel_SVC, "called"); |
| 1115 | return system.CurrentScheduler().GetCurrentThread()->GetProcessorID(); | 1204 | return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex()); |
| 1205 | } | ||
| 1206 | |||
| 1207 | static u32 GetCurrentProcessorNumber32(Core::System& system) { | ||
| 1208 | return GetCurrentProcessorNumber(system); | ||
| 1116 | } | 1209 | } |
| 1117 | 1210 | ||
| 1118 | static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, | 1211 | static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, |
| 1119 | u64 size, u32 permissions) { | 1212 | u64 size, u32 permissions) { |
| 1213 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1120 | LOG_TRACE(Kernel_SVC, | 1214 | LOG_TRACE(Kernel_SVC, |
| 1121 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | 1215 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", |
| 1122 | shared_memory_handle, addr, size, permissions); | 1216 | shared_memory_handle, addr, size, permissions); |
| @@ -1187,9 +1281,16 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han | |||
| 1187 | return shared_memory->Map(*current_process, addr, size, permission_type); | 1281 | return shared_memory->Map(*current_process, addr, size, permission_type); |
| 1188 | } | 1282 | } |
| 1189 | 1283 | ||
| 1284 | static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr, | ||
| 1285 | u32 size, u32 permissions) { | ||
| 1286 | return MapSharedMemory(system, shared_memory_handle, static_cast<VAddr>(addr), | ||
| 1287 | static_cast<std::size_t>(size), permissions); | ||
| 1288 | } | ||
| 1289 | |||
| 1190 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, | 1290 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1191 | VAddr page_info_address, Handle process_handle, | 1291 | VAddr page_info_address, Handle process_handle, |
| 1192 | VAddr address) { | 1292 | VAddr address) { |
| 1293 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1193 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); | 1294 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); |
| 1194 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1295 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1195 | std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle); | 1296 | std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle); |
| @@ -1372,6 +1473,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha | |||
| 1372 | /// Exits the current process | 1473 | /// Exits the current process |
| 1373 | static void ExitProcess(Core::System& system) { | 1474 | static void ExitProcess(Core::System& system) { |
| 1374 | auto* current_process = system.Kernel().CurrentProcess(); | 1475 | auto* current_process = system.Kernel().CurrentProcess(); |
| 1476 | UNIMPLEMENTED(); | ||
| 1375 | 1477 | ||
| 1376 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); | 1478 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); |
| 1377 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, | 1479 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, |
| @@ -1381,8 +1483,10 @@ static void ExitProcess(Core::System& system) { | |||
| 1381 | 1483 | ||
| 1382 | // Kill the current thread | 1484 | // Kill the current thread |
| 1383 | system.CurrentScheduler().GetCurrentThread()->Stop(); | 1485 | system.CurrentScheduler().GetCurrentThread()->Stop(); |
| 1486 | } | ||
| 1384 | 1487 | ||
| 1385 | system.PrepareReschedule(); | 1488 | static void ExitProcess32(Core::System& system) { |
| 1489 | ExitProcess(system); | ||
| 1386 | } | 1490 | } |
| 1387 | 1491 | ||
| 1388 | /// Creates a new thread | 1492 | /// Creates a new thread |
| @@ -1428,9 +1532,10 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | |||
| 1428 | 1532 | ||
| 1429 | ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); | 1533 | ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); |
| 1430 | 1534 | ||
| 1535 | ThreadType type = THREADTYPE_USER; | ||
| 1431 | CASCADE_RESULT(std::shared_ptr<Thread> thread, | 1536 | CASCADE_RESULT(std::shared_ptr<Thread> thread, |
| 1432 | Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, | 1537 | Thread::Create(system, type, "", entry_point, priority, arg, processor_id, |
| 1433 | *current_process)); | 1538 | stack_top, current_process)); |
| 1434 | 1539 | ||
| 1435 | const auto new_thread_handle = current_process->GetHandleTable().Create(thread); | 1540 | const auto new_thread_handle = current_process->GetHandleTable().Create(thread); |
| 1436 | if (new_thread_handle.Failed()) { | 1541 | if (new_thread_handle.Failed()) { |
| @@ -1444,11 +1549,15 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | |||
| 1444 | thread->SetName( | 1549 | thread->SetName( |
| 1445 | fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); | 1550 | fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); |
| 1446 | 1551 | ||
| 1447 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1448 | |||
| 1449 | return RESULT_SUCCESS; | 1552 | return RESULT_SUCCESS; |
| 1450 | } | 1553 | } |
| 1451 | 1554 | ||
| 1555 | static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority, | ||
| 1556 | u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { | ||
| 1557 | return CreateThread(system, out_handle, static_cast<VAddr>(entry_point), static_cast<u64>(arg), | ||
| 1558 | static_cast<VAddr>(stack_top), priority, processor_id); | ||
| 1559 | } | ||
| 1560 | |||
| 1452 | /// Starts the thread for the provided handle | 1561 | /// Starts the thread for the provided handle |
| 1453 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { | 1562 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { |
| 1454 | LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 1563 | LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| @@ -1463,13 +1572,11 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { | |||
| 1463 | 1572 | ||
| 1464 | ASSERT(thread->GetStatus() == ThreadStatus::Dormant); | 1573 | ASSERT(thread->GetStatus() == ThreadStatus::Dormant); |
| 1465 | 1574 | ||
| 1466 | thread->ResumeFromWait(); | 1575 | return thread->Start(); |
| 1467 | 1576 | } | |
| 1468 | if (thread->GetStatus() == ThreadStatus::Ready) { | ||
| 1469 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1470 | } | ||
| 1471 | 1577 | ||
| 1472 | return RESULT_SUCCESS; | 1578 | static ResultCode StartThread32(Core::System& system, Handle thread_handle) { |
| 1579 | return StartThread(system, thread_handle); | ||
| 1473 | } | 1580 | } |
| 1474 | 1581 | ||
| 1475 | /// Called when a thread exits | 1582 | /// Called when a thread exits |
| @@ -1477,9 +1584,12 @@ static void ExitThread(Core::System& system) { | |||
| 1477 | LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); | 1584 | LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); |
| 1478 | 1585 | ||
| 1479 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); | 1586 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 1480 | current_thread->Stop(); | ||
| 1481 | system.GlobalScheduler().RemoveThread(SharedFrom(current_thread)); | 1587 | system.GlobalScheduler().RemoveThread(SharedFrom(current_thread)); |
| 1482 | system.PrepareReschedule(); | 1588 | current_thread->Stop(); |
| 1589 | } | ||
| 1590 | |||
| 1591 | static void ExitThread32(Core::System& system) { | ||
| 1592 | ExitThread(system); | ||
| 1483 | } | 1593 | } |
| 1484 | 1594 | ||
| 1485 | /// Sleep the current thread | 1595 | /// Sleep the current thread |
| @@ -1498,15 +1608,21 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { | |||
| 1498 | 1608 | ||
| 1499 | if (nanoseconds <= 0) { | 1609 | if (nanoseconds <= 0) { |
| 1500 | switch (static_cast<SleepType>(nanoseconds)) { | 1610 | switch (static_cast<SleepType>(nanoseconds)) { |
| 1501 | case SleepType::YieldWithoutLoadBalancing: | 1611 | case SleepType::YieldWithoutLoadBalancing: { |
| 1502 | is_redundant = current_thread->YieldSimple(); | 1612 | auto pair = current_thread->YieldSimple(); |
| 1613 | is_redundant = pair.second; | ||
| 1503 | break; | 1614 | break; |
| 1504 | case SleepType::YieldWithLoadBalancing: | 1615 | } |
| 1505 | is_redundant = current_thread->YieldAndBalanceLoad(); | 1616 | case SleepType::YieldWithLoadBalancing: { |
| 1617 | auto pair = current_thread->YieldAndBalanceLoad(); | ||
| 1618 | is_redundant = pair.second; | ||
| 1506 | break; | 1619 | break; |
| 1507 | case SleepType::YieldAndWaitForLoadBalancing: | 1620 | } |
| 1508 | is_redundant = current_thread->YieldAndWaitForLoadBalancing(); | 1621 | case SleepType::YieldAndWaitForLoadBalancing: { |
| 1622 | auto pair = current_thread->YieldAndWaitForLoadBalancing(); | ||
| 1623 | is_redundant = pair.second; | ||
| 1509 | break; | 1624 | break; |
| 1625 | } | ||
| 1510 | default: | 1626 | default: |
| 1511 | UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); | 1627 | UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); |
| 1512 | } | 1628 | } |
| @@ -1514,13 +1630,18 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { | |||
| 1514 | current_thread->Sleep(nanoseconds); | 1630 | current_thread->Sleep(nanoseconds); |
| 1515 | } | 1631 | } |
| 1516 | 1632 | ||
| 1517 | if (is_redundant) { | 1633 | if (is_redundant && !system.Kernel().IsMulticore()) { |
| 1518 | // If it's redundant, the core is pretty much idle. Some games keep idling | 1634 | system.Kernel().ExitSVCProfile(); |
| 1519 | // a core while it's doing nothing, we advance timing to avoid costly continuous | 1635 | system.CoreTiming().AddTicks(1000U); |
| 1520 | // calls. | 1636 | system.GetCpuManager().PreemptSingleCore(); |
| 1521 | system.CoreTiming().AddTicks(2000); | 1637 | system.Kernel().EnterSVCProfile(); |
| 1522 | } | 1638 | } |
| 1523 | system.PrepareReschedule(current_thread->GetProcessorID()); | 1639 | } |
| 1640 | |||
| 1641 | static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { | ||
| 1642 | const s64 nanoseconds = static_cast<s64>(static_cast<u64>(nanoseconds_low) | | ||
| 1643 | (static_cast<u64>(nanoseconds_high) << 32)); | ||
| 1644 | SleepThread(system, nanoseconds); | ||
| 1524 | } | 1645 | } |
| 1525 | 1646 | ||
| 1526 | /// Wait process wide key atomic | 1647 | /// Wait process wide key atomic |
| @@ -1547,31 +1668,69 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add | |||
| 1547 | } | 1668 | } |
| 1548 | 1669 | ||
| 1549 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); | 1670 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); |
| 1550 | 1671 | auto& kernel = system.Kernel(); | |
| 1672 | Handle event_handle; | ||
| 1673 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 1551 | auto* const current_process = system.Kernel().CurrentProcess(); | 1674 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1552 | const auto& handle_table = current_process->GetHandleTable(); | 1675 | { |
| 1553 | std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1676 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds); |
| 1554 | ASSERT(thread); | 1677 | const auto& handle_table = current_process->GetHandleTable(); |
| 1678 | std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); | ||
| 1679 | ASSERT(thread); | ||
| 1680 | |||
| 1681 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 1682 | |||
| 1683 | if (thread->IsPendingTermination()) { | ||
| 1684 | lock.CancelSleep(); | ||
| 1685 | return ERR_THREAD_TERMINATING; | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | const auto release_result = current_process->GetMutex().Release(mutex_addr); | ||
| 1689 | if (release_result.IsError()) { | ||
| 1690 | lock.CancelSleep(); | ||
| 1691 | return release_result; | ||
| 1692 | } | ||
| 1693 | |||
| 1694 | if (nano_seconds == 0) { | ||
| 1695 | lock.CancelSleep(); | ||
| 1696 | return RESULT_TIMEOUT; | ||
| 1697 | } | ||
| 1555 | 1698 | ||
| 1556 | const auto release_result = current_process->GetMutex().Release(mutex_addr); | 1699 | current_thread->SetCondVarWaitAddress(condition_variable_addr); |
| 1557 | if (release_result.IsError()) { | 1700 | current_thread->SetMutexWaitAddress(mutex_addr); |
| 1558 | return release_result; | 1701 | current_thread->SetWaitHandle(thread_handle); |
| 1702 | current_thread->SetStatus(ThreadStatus::WaitCondVar); | ||
| 1703 | current_process->InsertConditionVariableThread(SharedFrom(current_thread)); | ||
| 1559 | } | 1704 | } |
| 1560 | 1705 | ||
| 1561 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | 1706 | if (event_handle != InvalidHandle) { |
| 1562 | current_thread->SetCondVarWaitAddress(condition_variable_addr); | 1707 | auto& time_manager = kernel.TimeManager(); |
| 1563 | current_thread->SetMutexWaitAddress(mutex_addr); | 1708 | time_manager.UnscheduleTimeEvent(event_handle); |
| 1564 | current_thread->SetWaitHandle(thread_handle); | 1709 | } |
| 1565 | current_thread->SetStatus(ThreadStatus::WaitCondVar); | 1710 | |
| 1566 | current_thread->InvalidateWakeupCallback(); | 1711 | { |
| 1567 | current_process->InsertConditionVariableThread(SharedFrom(current_thread)); | 1712 | SchedulerLock lock(kernel); |
| 1568 | 1713 | ||
| 1569 | current_thread->WakeAfterDelay(nano_seconds); | 1714 | auto* owner = current_thread->GetLockOwner(); |
| 1715 | if (owner != nullptr) { | ||
| 1716 | owner->RemoveMutexWaiter(SharedFrom(current_thread)); | ||
| 1717 | } | ||
| 1570 | 1718 | ||
| 1719 | current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); | ||
| 1720 | } | ||
| 1571 | // Note: Deliberately don't attempt to inherit the lock owner's priority. | 1721 | // Note: Deliberately don't attempt to inherit the lock owner's priority. |
| 1572 | 1722 | ||
| 1573 | system.PrepareReschedule(current_thread->GetProcessorID()); | 1723 | return current_thread->GetSignalingResult(); |
| 1574 | return RESULT_SUCCESS; | 1724 | } |
| 1725 | |||
| 1726 | static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, | ||
| 1727 | u32 condition_variable_addr, Handle thread_handle, | ||
| 1728 | u32 nanoseconds_low, u32 nanoseconds_high) { | ||
| 1729 | const s64 nanoseconds = | ||
| 1730 | static_cast<s64>(nanoseconds_low | (static_cast<u64>(nanoseconds_high) << 32)); | ||
| 1731 | return WaitProcessWideKeyAtomic(system, static_cast<VAddr>(mutex_addr), | ||
| 1732 | static_cast<VAddr>(condition_variable_addr), thread_handle, | ||
| 1733 | nanoseconds); | ||
| 1575 | } | 1734 | } |
| 1576 | 1735 | ||
| 1577 | /// Signal process wide key | 1736 | /// Signal process wide key |
| @@ -1582,7 +1741,9 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1582 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); | 1741 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); |
| 1583 | 1742 | ||
| 1584 | // Retrieve a list of all threads that are waiting for this condition variable. | 1743 | // Retrieve a list of all threads that are waiting for this condition variable. |
| 1585 | auto* const current_process = system.Kernel().CurrentProcess(); | 1744 | auto& kernel = system.Kernel(); |
| 1745 | SchedulerLock lock(kernel); | ||
| 1746 | auto* const current_process = kernel.CurrentProcess(); | ||
| 1586 | std::vector<std::shared_ptr<Thread>> waiting_threads = | 1747 | std::vector<std::shared_ptr<Thread>> waiting_threads = |
| 1587 | current_process->GetConditionVariableThreads(condition_variable_addr); | 1748 | current_process->GetConditionVariableThreads(condition_variable_addr); |
| 1588 | 1749 | ||
| @@ -1591,7 +1752,7 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1591 | std::size_t last = waiting_threads.size(); | 1752 | std::size_t last = waiting_threads.size(); |
| 1592 | if (target > 0) | 1753 | if (target > 0) |
| 1593 | last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); | 1754 | last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); |
| 1594 | 1755 | auto& time_manager = kernel.TimeManager(); | |
| 1595 | for (std::size_t index = 0; index < last; ++index) { | 1756 | for (std::size_t index = 0; index < last; ++index) { |
| 1596 | auto& thread = waiting_threads[index]; | 1757 | auto& thread = waiting_threads[index]; |
| 1597 | 1758 | ||
| @@ -1599,7 +1760,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1599 | 1760 | ||
| 1600 | // liberate Cond Var Thread. | 1761 | // liberate Cond Var Thread. |
| 1601 | current_process->RemoveConditionVariableThread(thread); | 1762 | current_process->RemoveConditionVariableThread(thread); |
| 1602 | thread->SetCondVarWaitAddress(0); | ||
| 1603 | 1763 | ||
| 1604 | const std::size_t current_core = system.CurrentCoreIndex(); | 1764 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 1605 | auto& monitor = system.Monitor(); | 1765 | auto& monitor = system.Monitor(); |
| @@ -1610,10 +1770,8 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1610 | u32 update_val = 0; | 1770 | u32 update_val = 0; |
| 1611 | const VAddr mutex_address = thread->GetMutexWaitAddress(); | 1771 | const VAddr mutex_address = thread->GetMutexWaitAddress(); |
| 1612 | do { | 1772 | do { |
| 1613 | monitor.SetExclusive(current_core, mutex_address); | ||
| 1614 | |||
| 1615 | // If the mutex is not yet acquired, acquire it. | 1773 | // If the mutex is not yet acquired, acquire it. |
| 1616 | mutex_val = memory.Read32(mutex_address); | 1774 | mutex_val = monitor.ExclusiveRead32(current_core, mutex_address); |
| 1617 | 1775 | ||
| 1618 | if (mutex_val != 0) { | 1776 | if (mutex_val != 0) { |
| 1619 | update_val = mutex_val | Mutex::MutexHasWaitersFlag; | 1777 | update_val = mutex_val | Mutex::MutexHasWaitersFlag; |
| @@ -1621,33 +1779,28 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1621 | update_val = thread->GetWaitHandle(); | 1779 | update_val = thread->GetWaitHandle(); |
| 1622 | } | 1780 | } |
| 1623 | } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); | 1781 | } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); |
| 1782 | monitor.ClearExclusive(); | ||
| 1624 | if (mutex_val == 0) { | 1783 | if (mutex_val == 0) { |
| 1625 | // We were able to acquire the mutex, resume this thread. | 1784 | // We were able to acquire the mutex, resume this thread. |
| 1626 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); | ||
| 1627 | thread->ResumeFromWait(); | ||
| 1628 | |||
| 1629 | auto* const lock_owner = thread->GetLockOwner(); | 1785 | auto* const lock_owner = thread->GetLockOwner(); |
| 1630 | if (lock_owner != nullptr) { | 1786 | if (lock_owner != nullptr) { |
| 1631 | lock_owner->RemoveMutexWaiter(thread); | 1787 | lock_owner->RemoveMutexWaiter(thread); |
| 1632 | } | 1788 | } |
| 1633 | 1789 | ||
| 1634 | thread->SetLockOwner(nullptr); | 1790 | thread->SetLockOwner(nullptr); |
| 1635 | thread->SetMutexWaitAddress(0); | 1791 | thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 1636 | thread->SetWaitHandle(0); | 1792 | thread->ResumeFromWait(); |
| 1637 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 1638 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1639 | } else { | 1793 | } else { |
| 1640 | // The mutex is already owned by some other thread, make this thread wait on it. | 1794 | // The mutex is already owned by some other thread, make this thread wait on it. |
| 1641 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 1795 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 1642 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1796 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1643 | auto owner = handle_table.Get<Thread>(owner_handle); | 1797 | auto owner = handle_table.Get<Thread>(owner_handle); |
| 1644 | ASSERT(owner); | 1798 | ASSERT(owner); |
| 1645 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); | 1799 | if (thread->GetStatus() == ThreadStatus::WaitCondVar) { |
| 1646 | thread->InvalidateWakeupCallback(); | 1800 | thread->SetStatus(ThreadStatus::WaitMutex); |
| 1647 | thread->SetStatus(ThreadStatus::WaitMutex); | 1801 | } |
| 1648 | 1802 | ||
| 1649 | owner->AddMutexWaiter(thread); | 1803 | owner->AddMutexWaiter(thread); |
| 1650 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1651 | } | 1804 | } |
| 1652 | } | 1805 | } |
| 1653 | } | 1806 | } |
| @@ -1678,12 +1831,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, | |||
| 1678 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); | 1831 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); |
| 1679 | const ResultCode result = | 1832 | const ResultCode result = |
| 1680 | address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); | 1833 | address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); |
| 1681 | if (result == RESULT_SUCCESS) { | ||
| 1682 | system.PrepareReschedule(); | ||
| 1683 | } | ||
| 1684 | return result; | 1834 | return result; |
| 1685 | } | 1835 | } |
| 1686 | 1836 | ||
| 1837 | static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, | ||
| 1838 | u32 timeout_low, u32 timeout_high) { | ||
| 1839 | s64 timeout = static_cast<s64>(timeout_low | (static_cast<u64>(timeout_high) << 32)); | ||
| 1840 | return WaitForAddress(system, static_cast<VAddr>(address), type, value, timeout); | ||
| 1841 | } | ||
| 1842 | |||
| 1687 | // Signals to an address (via Address Arbiter) | 1843 | // Signals to an address (via Address Arbiter) |
| 1688 | static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, | 1844 | static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, |
| 1689 | s32 num_to_wake) { | 1845 | s32 num_to_wake) { |
| @@ -1707,6 +1863,11 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, | |||
| 1707 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); | 1863 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); |
| 1708 | } | 1864 | } |
| 1709 | 1865 | ||
| 1866 | static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, | ||
| 1867 | s32 num_to_wake) { | ||
| 1868 | return SignalToAddress(system, static_cast<VAddr>(address), type, value, num_to_wake); | ||
| 1869 | } | ||
| 1870 | |||
| 1710 | static void KernelDebug([[maybe_unused]] Core::System& system, | 1871 | static void KernelDebug([[maybe_unused]] Core::System& system, |
| 1711 | [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, | 1872 | [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, |
| 1712 | [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { | 1873 | [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { |
| @@ -1725,14 +1886,21 @@ static u64 GetSystemTick(Core::System& system) { | |||
| 1725 | auto& core_timing = system.CoreTiming(); | 1886 | auto& core_timing = system.CoreTiming(); |
| 1726 | 1887 | ||
| 1727 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) | 1888 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) |
| 1728 | const u64 result{Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks())}; | 1889 | const u64 result{system.CoreTiming().GetClockTicks()}; |
| 1729 | 1890 | ||
| 1730 | // Advance time to defeat dumb games that busy-wait for the frame to end. | 1891 | if (!system.Kernel().IsMulticore()) { |
| 1731 | core_timing.AddTicks(400); | 1892 | core_timing.AddTicks(400U); |
| 1893 | } | ||
| 1732 | 1894 | ||
| 1733 | return result; | 1895 | return result; |
| 1734 | } | 1896 | } |
| 1735 | 1897 | ||
| 1898 | static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { | ||
| 1899 | u64 time = GetSystemTick(system); | ||
| 1900 | *time_low = static_cast<u32>(time); | ||
| 1901 | *time_high = static_cast<u32>(time >> 32); | ||
| 1902 | } | ||
| 1903 | |||
| 1736 | /// Close a handle | 1904 | /// Close a handle |
| 1737 | static ResultCode CloseHandle(Core::System& system, Handle handle) { | 1905 | static ResultCode CloseHandle(Core::System& system, Handle handle) { |
| 1738 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | 1906 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); |
| @@ -1765,9 +1933,14 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) { | |||
| 1765 | return ERR_INVALID_HANDLE; | 1933 | return ERR_INVALID_HANDLE; |
| 1766 | } | 1934 | } |
| 1767 | 1935 | ||
| 1936 | static ResultCode ResetSignal32(Core::System& system, Handle handle) { | ||
| 1937 | return ResetSignal(system, handle); | ||
| 1938 | } | ||
| 1939 | |||
| 1768 | /// Creates a TransferMemory object | 1940 | /// Creates a TransferMemory object |
| 1769 | static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, | 1941 | static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, |
| 1770 | u32 permissions) { | 1942 | u32 permissions) { |
| 1943 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1771 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, | 1944 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, |
| 1772 | permissions); | 1945 | permissions); |
| 1773 | 1946 | ||
| @@ -1812,6 +1985,12 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd | |||
| 1812 | return RESULT_SUCCESS; | 1985 | return RESULT_SUCCESS; |
| 1813 | } | 1986 | } |
| 1814 | 1987 | ||
| 1988 | static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u32 addr, u32 size, | ||
| 1989 | u32 permissions) { | ||
| 1990 | return CreateTransferMemory(system, handle, static_cast<VAddr>(addr), | ||
| 1991 | static_cast<std::size_t>(size), permissions); | ||
| 1992 | } | ||
| 1993 | |||
| 1815 | static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, | 1994 | static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, |
| 1816 | u64* mask) { | 1995 | u64* mask) { |
| 1817 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | 1996 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); |
| @@ -1821,6 +2000,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1821 | if (!thread) { | 2000 | if (!thread) { |
| 1822 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 2001 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| 1823 | thread_handle); | 2002 | thread_handle); |
| 2003 | *core = 0; | ||
| 2004 | *mask = 0; | ||
| 1824 | return ERR_INVALID_HANDLE; | 2005 | return ERR_INVALID_HANDLE; |
| 1825 | } | 2006 | } |
| 1826 | 2007 | ||
| @@ -1830,6 +2011,15 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1830 | return RESULT_SUCCESS; | 2011 | return RESULT_SUCCESS; |
| 1831 | } | 2012 | } |
| 1832 | 2013 | ||
| 2014 | static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core, | ||
| 2015 | u32* mask_low, u32* mask_high) { | ||
| 2016 | u64 mask{}; | ||
| 2017 | const auto result = GetThreadCoreMask(system, thread_handle, core, &mask); | ||
| 2018 | *mask_high = static_cast<u32>(mask >> 32); | ||
| 2019 | *mask_low = static_cast<u32>(mask); | ||
| 2020 | return result; | ||
| 2021 | } | ||
| 2022 | |||
| 1833 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, | 2023 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, |
| 1834 | u64 affinity_mask) { | 2024 | u64 affinity_mask) { |
| 1835 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", | 2025 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", |
| @@ -1861,7 +2051,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1861 | return ERR_INVALID_COMBINATION; | 2051 | return ERR_INVALID_COMBINATION; |
| 1862 | } | 2052 | } |
| 1863 | 2053 | ||
| 1864 | if (core < Core::NUM_CPU_CORES) { | 2054 | if (core < Core::Hardware::NUM_CPU_CORES) { |
| 1865 | if ((affinity_mask & (1ULL << core)) == 0) { | 2055 | if ((affinity_mask & (1ULL << core)) == 0) { |
| 1866 | LOG_ERROR(Kernel_SVC, | 2056 | LOG_ERROR(Kernel_SVC, |
| 1867 | "Core is not enabled for the current mask, core={}, mask={:016X}", core, | 2057 | "Core is not enabled for the current mask, core={}, mask={:016X}", core, |
| @@ -1883,11 +2073,14 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1883 | return ERR_INVALID_HANDLE; | 2073 | return ERR_INVALID_HANDLE; |
| 1884 | } | 2074 | } |
| 1885 | 2075 | ||
| 1886 | system.PrepareReschedule(thread->GetProcessorID()); | 2076 | return thread->SetCoreAndAffinityMask(core, affinity_mask); |
| 1887 | thread->ChangeCore(core, affinity_mask); | 2077 | } |
| 1888 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1889 | 2078 | ||
| 1890 | return RESULT_SUCCESS; | 2079 | static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core, |
| 2080 | u32 affinity_mask_low, u32 affinity_mask_high) { | ||
| 2081 | const u64 affinity_mask = | ||
| 2082 | static_cast<u64>(affinity_mask_low) | (static_cast<u64>(affinity_mask_high) << 32); | ||
| 2083 | return SetThreadCoreMask(system, thread_handle, core, affinity_mask); | ||
| 1891 | } | 2084 | } |
| 1892 | 2085 | ||
| 1893 | static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { | 2086 | static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { |
| @@ -1918,6 +2111,10 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle | |||
| 1918 | return RESULT_SUCCESS; | 2111 | return RESULT_SUCCESS; |
| 1919 | } | 2112 | } |
| 1920 | 2113 | ||
| 2114 | static ResultCode CreateEvent32(Core::System& system, Handle* write_handle, Handle* read_handle) { | ||
| 2115 | return CreateEvent(system, write_handle, read_handle); | ||
| 2116 | } | ||
| 2117 | |||
| 1921 | static ResultCode ClearEvent(Core::System& system, Handle handle) { | 2118 | static ResultCode ClearEvent(Core::System& system, Handle handle) { |
| 1922 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); | 2119 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); |
| 1923 | 2120 | ||
| @@ -1939,6 +2136,10 @@ static ResultCode ClearEvent(Core::System& system, Handle handle) { | |||
| 1939 | return ERR_INVALID_HANDLE; | 2136 | return ERR_INVALID_HANDLE; |
| 1940 | } | 2137 | } |
| 1941 | 2138 | ||
| 2139 | static ResultCode ClearEvent32(Core::System& system, Handle handle) { | ||
| 2140 | return ClearEvent(system, handle); | ||
| 2141 | } | ||
| 2142 | |||
| 1942 | static ResultCode SignalEvent(Core::System& system, Handle handle) { | 2143 | static ResultCode SignalEvent(Core::System& system, Handle handle) { |
| 1943 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); | 2144 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); |
| 1944 | 2145 | ||
| @@ -1951,10 +2152,13 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) { | |||
| 1951 | } | 2152 | } |
| 1952 | 2153 | ||
| 1953 | writable_event->Signal(); | 2154 | writable_event->Signal(); |
| 1954 | system.PrepareReschedule(); | ||
| 1955 | return RESULT_SUCCESS; | 2155 | return RESULT_SUCCESS; |
| 1956 | } | 2156 | } |
| 1957 | 2157 | ||
| 2158 | static ResultCode SignalEvent32(Core::System& system, Handle handle) { | ||
| 2159 | return SignalEvent(system, handle); | ||
| 2160 | } | ||
| 2161 | |||
| 1958 | static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { | 2162 | static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { |
| 1959 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); | 2163 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); |
| 1960 | 2164 | ||
| @@ -1982,6 +2186,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_ | |||
| 1982 | } | 2186 | } |
| 1983 | 2187 | ||
| 1984 | static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { | 2188 | static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { |
| 2189 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1985 | LOG_DEBUG(Kernel_SVC, "called"); | 2190 | LOG_DEBUG(Kernel_SVC, "called"); |
| 1986 | 2191 | ||
| 1987 | auto& kernel = system.Kernel(); | 2192 | auto& kernel = system.Kernel(); |
| @@ -2139,6 +2344,15 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd | |||
| 2139 | return RESULT_SUCCESS; | 2344 | return RESULT_SUCCESS; |
| 2140 | } | 2345 | } |
| 2141 | 2346 | ||
| 2347 | static ResultCode FlushProcessDataCache32(Core::System& system, Handle handle, u32 address, | ||
| 2348 | u32 size) { | ||
| 2349 | // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a nope | ||
| 2350 | // as all emulation is done in the same cache level in host architecture, thus data cache | ||
| 2351 | // does not need flushing. | ||
| 2352 | LOG_DEBUG(Kernel_SVC, "called"); | ||
| 2353 | return RESULT_SUCCESS; | ||
| 2354 | } | ||
| 2355 | |||
| 2142 | namespace { | 2356 | namespace { |
| 2143 | struct FunctionDef { | 2357 | struct FunctionDef { |
| 2144 | using Func = void(Core::System&); | 2358 | using Func = void(Core::System&); |
| @@ -2153,57 +2367,57 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2153 | {0x00, nullptr, "Unknown"}, | 2367 | {0x00, nullptr, "Unknown"}, |
| 2154 | {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, | 2368 | {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, |
| 2155 | {0x02, nullptr, "Unknown"}, | 2369 | {0x02, nullptr, "Unknown"}, |
| 2156 | {0x03, nullptr, "SetMemoryAttribute32"}, | 2370 | {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, |
| 2157 | {0x04, nullptr, "MapMemory32"}, | 2371 | {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, |
| 2158 | {0x05, nullptr, "UnmapMemory32"}, | 2372 | {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, |
| 2159 | {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"}, | 2373 | {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"}, |
| 2160 | {0x07, nullptr, "ExitProcess32"}, | 2374 | {0x07, SvcWrap32<ExitProcess32>, "ExitProcess32"}, |
| 2161 | {0x08, nullptr, "CreateThread32"}, | 2375 | {0x08, SvcWrap32<CreateThread32>, "CreateThread32"}, |
| 2162 | {0x09, nullptr, "StartThread32"}, | 2376 | {0x09, SvcWrap32<StartThread32>, "StartThread32"}, |
| 2163 | {0x0a, nullptr, "ExitThread32"}, | 2377 | {0x0a, SvcWrap32<ExitThread32>, "ExitThread32"}, |
| 2164 | {0x0b, nullptr, "SleepThread32"}, | 2378 | {0x0b, SvcWrap32<SleepThread32>, "SleepThread32"}, |
| 2165 | {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"}, | 2379 | {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"}, |
| 2166 | {0x0d, nullptr, "SetThreadPriority32"}, | 2380 | {0x0d, SvcWrap32<SetThreadPriority32>, "SetThreadPriority32"}, |
| 2167 | {0x0e, nullptr, "GetThreadCoreMask32"}, | 2381 | {0x0e, SvcWrap32<GetThreadCoreMask32>, "GetThreadCoreMask32"}, |
| 2168 | {0x0f, nullptr, "SetThreadCoreMask32"}, | 2382 | {0x0f, SvcWrap32<SetThreadCoreMask32>, "SetThreadCoreMask32"}, |
| 2169 | {0x10, nullptr, "GetCurrentProcessorNumber32"}, | 2383 | {0x10, SvcWrap32<GetCurrentProcessorNumber32>, "GetCurrentProcessorNumber32"}, |
| 2170 | {0x11, nullptr, "SignalEvent32"}, | 2384 | {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"}, |
| 2171 | {0x12, nullptr, "ClearEvent32"}, | 2385 | {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"}, |
| 2172 | {0x13, nullptr, "MapSharedMemory32"}, | 2386 | {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"}, |
| 2173 | {0x14, nullptr, "UnmapSharedMemory32"}, | 2387 | {0x14, nullptr, "UnmapSharedMemory32"}, |
| 2174 | {0x15, nullptr, "CreateTransferMemory32"}, | 2388 | {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"}, |
| 2175 | {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"}, | 2389 | {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"}, |
| 2176 | {0x17, nullptr, "ResetSignal32"}, | 2390 | {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"}, |
| 2177 | {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"}, | 2391 | {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"}, |
| 2178 | {0x19, nullptr, "CancelSynchronization32"}, | 2392 | {0x19, SvcWrap32<CancelSynchronization32>, "CancelSynchronization32"}, |
| 2179 | {0x1a, nullptr, "ArbitrateLock32"}, | 2393 | {0x1a, SvcWrap32<ArbitrateLock32>, "ArbitrateLock32"}, |
| 2180 | {0x1b, nullptr, "ArbitrateUnlock32"}, | 2394 | {0x1b, SvcWrap32<ArbitrateUnlock32>, "ArbitrateUnlock32"}, |
| 2181 | {0x1c, nullptr, "WaitProcessWideKeyAtomic32"}, | 2395 | {0x1c, SvcWrap32<WaitProcessWideKeyAtomic32>, "WaitProcessWideKeyAtomic32"}, |
| 2182 | {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, | 2396 | {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, |
| 2183 | {0x1e, nullptr, "GetSystemTick32"}, | 2397 | {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, |
| 2184 | {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, | 2398 | {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, |
| 2185 | {0x20, nullptr, "Unknown"}, | 2399 | {0x20, nullptr, "Unknown"}, |
| 2186 | {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, | 2400 | {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, |
| 2187 | {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, | 2401 | {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, |
| 2188 | {0x23, nullptr, "Unknown"}, | 2402 | {0x23, nullptr, "Unknown"}, |
| 2189 | {0x24, nullptr, "GetProcessId32"}, | 2403 | {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, |
| 2190 | {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, | 2404 | {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, |
| 2191 | {0x26, nullptr, "Break32"}, | 2405 | {0x26, SvcWrap32<Break32>, "Break32"}, |
| 2192 | {0x27, nullptr, "OutputDebugString32"}, | 2406 | {0x27, nullptr, "OutputDebugString32"}, |
| 2193 | {0x28, nullptr, "Unknown"}, | 2407 | {0x28, nullptr, "Unknown"}, |
| 2194 | {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, | 2408 | {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, |
| 2195 | {0x2a, nullptr, "Unknown"}, | 2409 | {0x2a, nullptr, "Unknown"}, |
| 2196 | {0x2b, nullptr, "Unknown"}, | 2410 | {0x2b, nullptr, "Unknown"}, |
| 2197 | {0x2c, nullptr, "MapPhysicalMemory32"}, | 2411 | {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, |
| 2198 | {0x2d, nullptr, "UnmapPhysicalMemory32"}, | 2412 | {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, |
| 2199 | {0x2e, nullptr, "Unknown"}, | 2413 | {0x2e, nullptr, "Unknown"}, |
| 2200 | {0x2f, nullptr, "Unknown"}, | 2414 | {0x2f, nullptr, "Unknown"}, |
| 2201 | {0x30, nullptr, "Unknown"}, | 2415 | {0x30, nullptr, "Unknown"}, |
| 2202 | {0x31, nullptr, "Unknown"}, | 2416 | {0x31, nullptr, "Unknown"}, |
| 2203 | {0x32, nullptr, "SetThreadActivity32"}, | 2417 | {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, |
| 2204 | {0x33, nullptr, "GetThreadContext32"}, | 2418 | {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, |
| 2205 | {0x34, nullptr, "WaitForAddress32"}, | 2419 | {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, |
| 2206 | {0x35, nullptr, "SignalToAddress32"}, | 2420 | {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, |
| 2207 | {0x36, nullptr, "Unknown"}, | 2421 | {0x36, nullptr, "Unknown"}, |
| 2208 | {0x37, nullptr, "Unknown"}, | 2422 | {0x37, nullptr, "Unknown"}, |
| 2209 | {0x38, nullptr, "Unknown"}, | 2423 | {0x38, nullptr, "Unknown"}, |
| @@ -2219,7 +2433,7 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2219 | {0x42, nullptr, "Unknown"}, | 2433 | {0x42, nullptr, "Unknown"}, |
| 2220 | {0x43, nullptr, "ReplyAndReceive32"}, | 2434 | {0x43, nullptr, "ReplyAndReceive32"}, |
| 2221 | {0x44, nullptr, "Unknown"}, | 2435 | {0x44, nullptr, "Unknown"}, |
| 2222 | {0x45, nullptr, "CreateEvent32"}, | 2436 | {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, |
| 2223 | {0x46, nullptr, "Unknown"}, | 2437 | {0x46, nullptr, "Unknown"}, |
| 2224 | {0x47, nullptr, "Unknown"}, | 2438 | {0x47, nullptr, "Unknown"}, |
| 2225 | {0x48, nullptr, "Unknown"}, | 2439 | {0x48, nullptr, "Unknown"}, |
| @@ -2245,7 +2459,7 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2245 | {0x5c, nullptr, "Unknown"}, | 2459 | {0x5c, nullptr, "Unknown"}, |
| 2246 | {0x5d, nullptr, "Unknown"}, | 2460 | {0x5d, nullptr, "Unknown"}, |
| 2247 | {0x5e, nullptr, "Unknown"}, | 2461 | {0x5e, nullptr, "Unknown"}, |
| 2248 | {0x5F, nullptr, "FlushProcessDataCache32"}, | 2462 | {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, |
| 2249 | {0x60, nullptr, "Unknown"}, | 2463 | {0x60, nullptr, "Unknown"}, |
| 2250 | {0x61, nullptr, "Unknown"}, | 2464 | {0x61, nullptr, "Unknown"}, |
| 2251 | {0x62, nullptr, "Unknown"}, | 2465 | {0x62, nullptr, "Unknown"}, |
| @@ -2423,13 +2637,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) { | |||
| 2423 | return &SVC_Table_64[func_num]; | 2637 | return &SVC_Table_64[func_num]; |
| 2424 | } | 2638 | } |
| 2425 | 2639 | ||
| 2426 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | ||
| 2427 | |||
| 2428 | void Call(Core::System& system, u32 immediate) { | 2640 | void Call(Core::System& system, u32 immediate) { |
| 2429 | MICROPROFILE_SCOPE(Kernel_SVC); | 2641 | system.ExitDynarmicProfile(); |
| 2430 | 2642 | auto& kernel = system.Kernel(); | |
| 2431 | // Lock the global kernel mutex when we enter the kernel HLE. | 2643 | kernel.EnterSVCProfile(); |
| 2432 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 2433 | 2644 | ||
| 2434 | const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) | 2645 | const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) |
| 2435 | : GetSVCInfo32(immediate); | 2646 | : GetSVCInfo32(immediate); |
| @@ -2442,6 +2653,9 @@ void Call(Core::System& system, u32 immediate) { | |||
| 2442 | } else { | 2653 | } else { |
| 2443 | LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); | 2654 | LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); |
| 2444 | } | 2655 | } |
| 2656 | |||
| 2657 | kernel.ExitSVCProfile(); | ||
| 2658 | system.EnterDynarmicProfile(); | ||
| 2445 | } | 2659 | } |
| 2446 | 2660 | ||
| 2447 | } // namespace Kernel::Svc | 2661 | } // namespace Kernel::Svc |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 7d735e3fa..0b6dd9df0 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -350,13 +350,50 @@ void SvcWrap64(Core::System& system) { | |||
| 350 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); | 350 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); |
| 351 | } | 351 | } |
| 352 | 352 | ||
| 353 | // Used by QueryMemory32 | 353 | // Used by QueryMemory32, ArbitrateLock32 |
| 354 | template <ResultCode func(Core::System&, u32, u32, u32)> | 354 | template <ResultCode func(Core::System&, u32, u32, u32)> |
| 355 | void SvcWrap32(Core::System& system) { | 355 | void SvcWrap32(Core::System& system) { |
| 356 | FuncReturn32(system, | 356 | FuncReturn32(system, |
| 357 | func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); | 357 | func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); |
| 358 | } | 358 | } |
| 359 | 359 | ||
| 360 | // Used by Break32 | ||
| 361 | template <void func(Core::System&, u32, u32, u32)> | ||
| 362 | void SvcWrap32(Core::System& system) { | ||
| 363 | func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)); | ||
| 364 | } | ||
| 365 | |||
| 366 | // Used by ExitProcess32, ExitThread32 | ||
| 367 | template <void func(Core::System&)> | ||
| 368 | void SvcWrap32(Core::System& system) { | ||
| 369 | func(system); | ||
| 370 | } | ||
| 371 | |||
| 372 | // Used by GetCurrentProcessorNumber32 | ||
| 373 | template <u32 func(Core::System&)> | ||
| 374 | void SvcWrap32(Core::System& system) { | ||
| 375 | FuncReturn32(system, func(system)); | ||
| 376 | } | ||
| 377 | |||
| 378 | // Used by SleepThread32 | ||
| 379 | template <void func(Core::System&, u32, u32)> | ||
| 380 | void SvcWrap32(Core::System& system) { | ||
| 381 | func(system, Param32(system, 0), Param32(system, 1)); | ||
| 382 | } | ||
| 383 | |||
| 384 | // Used by CreateThread32 | ||
| 385 | template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)> | ||
| 386 | void SvcWrap32(Core::System& system) { | ||
| 387 | Handle param_1 = 0; | ||
| 388 | |||
| 389 | const u32 retval = func(system, ¶m_1, Param32(system, 0), Param32(system, 1), | ||
| 390 | Param32(system, 2), Param32(system, 3), Param32(system, 4)) | ||
| 391 | .raw; | ||
| 392 | |||
| 393 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 394 | FuncReturn(system, retval); | ||
| 395 | } | ||
| 396 | |||
| 360 | // Used by GetInfo32 | 397 | // Used by GetInfo32 |
| 361 | template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> | 398 | template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> |
| 362 | void SvcWrap32(Core::System& system) { | 399 | void SvcWrap32(Core::System& system) { |
| @@ -393,18 +430,114 @@ void SvcWrap32(Core::System& system) { | |||
| 393 | FuncReturn(system, retval); | 430 | FuncReturn(system, retval); |
| 394 | } | 431 | } |
| 395 | 432 | ||
| 433 | // Used by GetSystemTick32 | ||
| 434 | template <void func(Core::System&, u32*, u32*)> | ||
| 435 | void SvcWrap32(Core::System& system) { | ||
| 436 | u32 param_1 = 0; | ||
| 437 | u32 param_2 = 0; | ||
| 438 | |||
| 439 | func(system, ¶m_1, ¶m_2); | ||
| 440 | system.CurrentArmInterface().SetReg(0, param_1); | ||
| 441 | system.CurrentArmInterface().SetReg(1, param_2); | ||
| 442 | } | ||
| 443 | |||
| 444 | // Used by CreateEvent32 | ||
| 445 | template <ResultCode func(Core::System&, Handle*, Handle*)> | ||
| 446 | void SvcWrap32(Core::System& system) { | ||
| 447 | Handle param_1 = 0; | ||
| 448 | Handle param_2 = 0; | ||
| 449 | |||
| 450 | const u32 retval = func(system, ¶m_1, ¶m_2).raw; | ||
| 451 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 452 | system.CurrentArmInterface().SetReg(2, param_2); | ||
| 453 | FuncReturn(system, retval); | ||
| 454 | } | ||
| 455 | |||
| 456 | // Used by GetThreadId32 | ||
| 457 | template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)> | ||
| 458 | void SvcWrap32(Core::System& system) { | ||
| 459 | u32 param_1 = 0; | ||
| 460 | u32 param_2 = 0; | ||
| 461 | u32 param_3 = 0; | ||
| 462 | |||
| 463 | const u32 retval = func(system, Param32(system, 2), ¶m_1, ¶m_2, ¶m_3).raw; | ||
| 464 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 465 | system.CurrentArmInterface().SetReg(2, param_2); | ||
| 466 | system.CurrentArmInterface().SetReg(3, param_3); | ||
| 467 | FuncReturn(system, retval); | ||
| 468 | } | ||
| 469 | |||
| 396 | // Used by SignalProcessWideKey32 | 470 | // Used by SignalProcessWideKey32 |
| 397 | template <void func(Core::System&, u32, s32)> | 471 | template <void func(Core::System&, u32, s32)> |
| 398 | void SvcWrap32(Core::System& system) { | 472 | void SvcWrap32(Core::System& system) { |
| 399 | func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); | 473 | func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); |
| 400 | } | 474 | } |
| 401 | 475 | ||
| 402 | // Used by SendSyncRequest32 | 476 | // Used by SetThreadPriority32 |
| 477 | template <ResultCode func(Core::System&, Handle, u32)> | ||
| 478 | void SvcWrap32(Core::System& system) { | ||
| 479 | const u32 retval = | ||
| 480 | func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw; | ||
| 481 | FuncReturn(system, retval); | ||
| 482 | } | ||
| 483 | |||
| 484 | // Used by SetThreadCoreMask32 | ||
| 485 | template <ResultCode func(Core::System&, Handle, u32, u32, u32)> | ||
| 486 | void SvcWrap32(Core::System& system) { | ||
| 487 | const u32 retval = | ||
| 488 | func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), | ||
| 489 | static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) | ||
| 490 | .raw; | ||
| 491 | FuncReturn(system, retval); | ||
| 492 | } | ||
| 493 | |||
| 494 | // Used by WaitProcessWideKeyAtomic32 | ||
| 495 | template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)> | ||
| 496 | void SvcWrap32(Core::System& system) { | ||
| 497 | const u32 retval = | ||
| 498 | func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), | ||
| 499 | static_cast<Handle>(Param(system, 2)), static_cast<u32>(Param(system, 3)), | ||
| 500 | static_cast<u32>(Param(system, 4))) | ||
| 501 | .raw; | ||
| 502 | FuncReturn(system, retval); | ||
| 503 | } | ||
| 504 | |||
| 505 | // Used by WaitForAddress32 | ||
| 506 | template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)> | ||
| 507 | void SvcWrap32(Core::System& system) { | ||
| 508 | const u32 retval = func(system, static_cast<u32>(Param(system, 0)), | ||
| 509 | static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)), | ||
| 510 | static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4))) | ||
| 511 | .raw; | ||
| 512 | FuncReturn(system, retval); | ||
| 513 | } | ||
| 514 | |||
| 515 | // Used by SignalToAddress32 | ||
| 516 | template <ResultCode func(Core::System&, u32, u32, s32, s32)> | ||
| 517 | void SvcWrap32(Core::System& system) { | ||
| 518 | const u32 retval = | ||
| 519 | func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), | ||
| 520 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) | ||
| 521 | .raw; | ||
| 522 | FuncReturn(system, retval); | ||
| 523 | } | ||
| 524 | |||
| 525 | // Used by SendSyncRequest32, ArbitrateUnlock32 | ||
| 403 | template <ResultCode func(Core::System&, u32)> | 526 | template <ResultCode func(Core::System&, u32)> |
| 404 | void SvcWrap32(Core::System& system) { | 527 | void SvcWrap32(Core::System& system) { |
| 405 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); | 528 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); |
| 406 | } | 529 | } |
| 407 | 530 | ||
| 531 | // Used by CreateTransferMemory32 | ||
| 532 | template <ResultCode func(Core::System&, Handle*, u32, u32, u32)> | ||
| 533 | void SvcWrap32(Core::System& system) { | ||
| 534 | Handle handle = 0; | ||
| 535 | const u32 retval = | ||
| 536 | func(system, &handle, Param32(system, 1), Param32(system, 2), Param32(system, 3)).raw; | ||
| 537 | system.CurrentArmInterface().SetReg(1, handle); | ||
| 538 | FuncReturn(system, retval); | ||
| 539 | } | ||
| 540 | |||
| 408 | // Used by WaitSynchronization32 | 541 | // Used by WaitSynchronization32 |
| 409 | template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> | 542 | template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> |
| 410 | void SvcWrap32(Core::System& system) { | 543 | void SvcWrap32(Core::System& system) { |
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index dc37fad1a..851b702a5 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp | |||
| @@ -10,78 +10,107 @@ | |||
| 10 | #include "core/hle/kernel/synchronization.h" | 10 | #include "core/hle/kernel/synchronization.h" |
| 11 | #include "core/hle/kernel/synchronization_object.h" | 11 | #include "core/hle/kernel/synchronization_object.h" |
| 12 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 13 | #include "core/hle/kernel/time_manager.h" | ||
| 13 | 14 | ||
| 14 | namespace Kernel { | 15 | namespace Kernel { |
| 15 | 16 | ||
| 16 | /// Default thread wakeup callback for WaitSynchronization | ||
| 17 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | ||
| 18 | std::shared_ptr<SynchronizationObject> object, | ||
| 19 | std::size_t index) { | ||
| 20 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 21 | |||
| 22 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 23 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 24 | return true; | ||
| 25 | } | ||
| 26 | |||
| 27 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 28 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 29 | thread->SetWaitSynchronizationOutput(static_cast<u32>(index)); | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | Synchronization::Synchronization(Core::System& system) : system{system} {} | 17 | Synchronization::Synchronization(Core::System& system) : system{system} {} |
| 34 | 18 | ||
| 35 | void Synchronization::SignalObject(SynchronizationObject& obj) const { | 19 | void Synchronization::SignalObject(SynchronizationObject& obj) const { |
| 20 | auto& kernel = system.Kernel(); | ||
| 21 | SchedulerLock lock(kernel); | ||
| 22 | auto& time_manager = kernel.TimeManager(); | ||
| 36 | if (obj.IsSignaled()) { | 23 | if (obj.IsSignaled()) { |
| 37 | obj.WakeupAllWaitingThreads(); | 24 | for (auto thread : obj.GetWaitingThreads()) { |
| 25 | if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { | ||
| 26 | if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) { | ||
| 27 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 28 | ASSERT(thread->IsWaitingSync()); | ||
| 29 | } | ||
| 30 | thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); | ||
| 31 | thread->ResumeFromWait(); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | obj.ClearWaitingThreads(); | ||
| 38 | } | 35 | } |
| 39 | } | 36 | } |
| 40 | 37 | ||
| 41 | std::pair<ResultCode, Handle> Synchronization::WaitFor( | 38 | std::pair<ResultCode, Handle> Synchronization::WaitFor( |
| 42 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { | 39 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { |
| 40 | auto& kernel = system.Kernel(); | ||
| 43 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); | 41 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); |
| 44 | // Find the first object that is acquirable in the provided list of objects | 42 | Handle event_handle = InvalidHandle; |
| 45 | const auto itr = std::find_if(sync_objects.begin(), sync_objects.end(), | 43 | { |
| 46 | [thread](const std::shared_ptr<SynchronizationObject>& object) { | 44 | SchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds); |
| 47 | return object->IsSignaled(); | 45 | const auto itr = |
| 48 | }); | 46 | std::find_if(sync_objects.begin(), sync_objects.end(), |
| 49 | 47 | [thread](const std::shared_ptr<SynchronizationObject>& object) { | |
| 50 | if (itr != sync_objects.end()) { | 48 | return object->IsSignaled(); |
| 51 | // We found a ready object, acquire it and set the result value | 49 | }); |
| 52 | SynchronizationObject* object = itr->get(); | 50 | |
| 53 | object->Acquire(thread); | 51 | if (itr != sync_objects.end()) { |
| 54 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | 52 | // We found a ready object, acquire it and set the result value |
| 55 | return {RESULT_SUCCESS, index}; | 53 | SynchronizationObject* object = itr->get(); |
| 54 | object->Acquire(thread); | ||
| 55 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | ||
| 56 | lock.CancelSleep(); | ||
| 57 | return {RESULT_SUCCESS, index}; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (nano_seconds == 0) { | ||
| 61 | lock.CancelSleep(); | ||
| 62 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (thread->IsPendingTermination()) { | ||
| 66 | lock.CancelSleep(); | ||
| 67 | return {ERR_THREAD_TERMINATING, InvalidHandle}; | ||
| 68 | } | ||
| 69 | |||
| 70 | if (thread->IsSyncCancelled()) { | ||
| 71 | thread->SetSyncCancelled(false); | ||
| 72 | lock.CancelSleep(); | ||
| 73 | return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; | ||
| 74 | } | ||
| 75 | |||
| 76 | for (auto& object : sync_objects) { | ||
| 77 | object->AddWaitingThread(SharedFrom(thread)); | ||
| 78 | } | ||
| 79 | |||
| 80 | thread->SetSynchronizationObjects(&sync_objects); | ||
| 81 | thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 82 | thread->SetStatus(ThreadStatus::WaitSynch); | ||
| 83 | thread->SetWaitingSync(true); | ||
| 56 | } | 84 | } |
| 85 | thread->SetWaitingSync(false); | ||
| 57 | 86 | ||
| 58 | // No objects were ready to be acquired, prepare to suspend the thread. | 87 | if (event_handle != InvalidHandle) { |
| 59 | 88 | auto& time_manager = kernel.TimeManager(); | |
| 60 | // If a timeout value of 0 was provided, just return the Timeout error code instead of | 89 | time_manager.UnscheduleTimeEvent(event_handle); |
| 61 | // suspending the thread. | ||
| 62 | if (nano_seconds == 0) { | ||
| 63 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 64 | } | 90 | } |
| 65 | 91 | ||
| 66 | if (thread->IsSyncCancelled()) { | 92 | { |
| 67 | thread->SetSyncCancelled(false); | 93 | SchedulerLock lock(kernel); |
| 68 | return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; | 94 | ResultCode signaling_result = thread->GetSignalingResult(); |
| 95 | SynchronizationObject* signaling_object = thread->GetSignalingObject(); | ||
| 96 | thread->SetSynchronizationObjects(nullptr); | ||
| 97 | auto shared_thread = SharedFrom(thread); | ||
| 98 | for (auto& obj : sync_objects) { | ||
| 99 | obj->RemoveWaitingThread(shared_thread); | ||
| 100 | } | ||
| 101 | if (signaling_object != nullptr) { | ||
| 102 | const auto itr = std::find_if( | ||
| 103 | sync_objects.begin(), sync_objects.end(), | ||
| 104 | [signaling_object](const std::shared_ptr<SynchronizationObject>& object) { | ||
| 105 | return object.get() == signaling_object; | ||
| 106 | }); | ||
| 107 | ASSERT(itr != sync_objects.end()); | ||
| 108 | signaling_object->Acquire(thread); | ||
| 109 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | ||
| 110 | return {signaling_result, index}; | ||
| 111 | } | ||
| 112 | return {signaling_result, -1}; | ||
| 69 | } | 113 | } |
| 70 | |||
| 71 | for (auto& object : sync_objects) { | ||
| 72 | object->AddWaitingThread(SharedFrom(thread)); | ||
| 73 | } | ||
| 74 | |||
| 75 | thread->SetSynchronizationObjects(std::move(sync_objects)); | ||
| 76 | thread->SetStatus(ThreadStatus::WaitSynch); | ||
| 77 | |||
| 78 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 79 | thread->WakeAfterDelay(nano_seconds); | ||
| 80 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); | ||
| 81 | |||
| 82 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 83 | |||
| 84 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 85 | } | 114 | } |
| 86 | 115 | ||
| 87 | } // namespace Kernel | 116 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp index 43f3eef18..ba4d39157 100644 --- a/src/core/hle/kernel/synchronization_object.cpp +++ b/src/core/hle/kernel/synchronization_object.cpp | |||
| @@ -38,68 +38,8 @@ void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) | |||
| 38 | waiting_threads.erase(itr); | 38 | waiting_threads.erase(itr); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | std::shared_ptr<Thread> SynchronizationObject::GetHighestPriorityReadyThread() const { | 41 | void SynchronizationObject::ClearWaitingThreads() { |
| 42 | Thread* candidate = nullptr; | 42 | waiting_threads.clear(); |
| 43 | u32 candidate_priority = THREADPRIO_LOWEST + 1; | ||
| 44 | |||
| 45 | for (const auto& thread : waiting_threads) { | ||
| 46 | const ThreadStatus thread_status = thread->GetStatus(); | ||
| 47 | |||
| 48 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | ||
| 49 | ASSERT_MSG(thread_status == ThreadStatus::WaitSynch || | ||
| 50 | thread_status == ThreadStatus::WaitHLEEvent, | ||
| 51 | "Inconsistent thread statuses in waiting_threads"); | ||
| 52 | |||
| 53 | if (thread->GetPriority() >= candidate_priority) | ||
| 54 | continue; | ||
| 55 | |||
| 56 | if (ShouldWait(thread.get())) | ||
| 57 | continue; | ||
| 58 | |||
| 59 | candidate = thread.get(); | ||
| 60 | candidate_priority = thread->GetPriority(); | ||
| 61 | } | ||
| 62 | |||
| 63 | return SharedFrom(candidate); | ||
| 64 | } | ||
| 65 | |||
| 66 | void SynchronizationObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { | ||
| 67 | ASSERT(!ShouldWait(thread.get())); | ||
| 68 | |||
| 69 | if (!thread) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | if (thread->IsSleepingOnWait()) { | ||
| 74 | for (const auto& object : thread->GetSynchronizationObjects()) { | ||
| 75 | ASSERT(!object->ShouldWait(thread.get())); | ||
| 76 | object->Acquire(thread.get()); | ||
| 77 | } | ||
| 78 | } else { | ||
| 79 | Acquire(thread.get()); | ||
| 80 | } | ||
| 81 | |||
| 82 | const std::size_t index = thread->GetSynchronizationObjectIndex(SharedFrom(this)); | ||
| 83 | |||
| 84 | thread->ClearSynchronizationObjects(); | ||
| 85 | |||
| 86 | thread->CancelWakeupTimer(); | ||
| 87 | |||
| 88 | bool resume = true; | ||
| 89 | if (thread->HasWakeupCallback()) { | ||
| 90 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, SharedFrom(this), | ||
| 91 | index); | ||
| 92 | } | ||
| 93 | if (resume) { | ||
| 94 | thread->ResumeFromWait(); | ||
| 95 | kernel.PrepareReschedule(thread->GetProcessorID()); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | void SynchronizationObject::WakeupAllWaitingThreads() { | ||
| 100 | while (auto thread = GetHighestPriorityReadyThread()) { | ||
| 101 | WakeupWaitingThread(thread); | ||
| 102 | } | ||
| 103 | } | 43 | } |
| 104 | 44 | ||
| 105 | const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { | 45 | const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { |
diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h index 741c31faf..f89b24204 100644 --- a/src/core/hle/kernel/synchronization_object.h +++ b/src/core/hle/kernel/synchronization_object.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| 14 | class KernelCore; | 14 | class KernelCore; |
| 15 | class Synchronization; | ||
| 15 | class Thread; | 16 | class Thread; |
| 16 | 17 | ||
| 17 | /// Class that represents a Kernel object that a thread can be waiting on | 18 | /// Class that represents a Kernel object that a thread can be waiting on |
| @@ -49,24 +50,11 @@ public: | |||
| 49 | */ | 50 | */ |
| 50 | void RemoveWaitingThread(std::shared_ptr<Thread> thread); | 51 | void RemoveWaitingThread(std::shared_ptr<Thread> thread); |
| 51 | 52 | ||
| 52 | /** | ||
| 53 | * Wake up all threads waiting on this object that can be awoken, in priority order, | ||
| 54 | * and set the synchronization result and output of the thread. | ||
| 55 | */ | ||
| 56 | void WakeupAllWaitingThreads(); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Wakes up a single thread waiting on this object. | ||
| 60 | * @param thread Thread that is waiting on this object to wakeup. | ||
| 61 | */ | ||
| 62 | void WakeupWaitingThread(std::shared_ptr<Thread> thread); | ||
| 63 | |||
| 64 | /// Obtains the highest priority thread that is ready to run from this object's waiting list. | ||
| 65 | std::shared_ptr<Thread> GetHighestPriorityReadyThread() const; | ||
| 66 | |||
| 67 | /// Get a const reference to the waiting threads list for debug use | 53 | /// Get a const reference to the waiting threads list for debug use |
| 68 | const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; | 54 | const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; |
| 69 | 55 | ||
| 56 | void ClearWaitingThreads(); | ||
| 57 | |||
| 70 | protected: | 58 | protected: |
| 71 | bool is_signaled{}; // Tells if this sync object is signalled; | 59 | bool is_signaled{}; // Tells if this sync object is signalled; |
| 72 | 60 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index db7f379ac..2b1092697 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -9,12 +9,21 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fiber.h" | ||
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/thread_queue_list.h" | 14 | #include "common/thread_queue_list.h" |
| 14 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 16 | #ifdef ARCHITECTURE_x86_64 | ||
| 17 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | ||
| 18 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||
| 19 | #endif | ||
| 20 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 21 | #include "core/arm/exclusive_monitor.h" | ||
| 22 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 15 | #include "core/core.h" | 23 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 24 | #include "core/core_timing.h" |
| 17 | #include "core/core_timing_util.h" | 25 | #include "core/core_timing_util.h" |
| 26 | #include "core/cpu_manager.h" | ||
| 18 | #include "core/hardware_properties.h" | 27 | #include "core/hardware_properties.h" |
| 19 | #include "core/hle/kernel/errors.h" | 28 | #include "core/hle/kernel/errors.h" |
| 20 | #include "core/hle/kernel/handle_table.h" | 29 | #include "core/hle/kernel/handle_table.h" |
| @@ -23,6 +32,7 @@ | |||
| 23 | #include "core/hle/kernel/process.h" | 32 | #include "core/hle/kernel/process.h" |
| 24 | #include "core/hle/kernel/scheduler.h" | 33 | #include "core/hle/kernel/scheduler.h" |
| 25 | #include "core/hle/kernel/thread.h" | 34 | #include "core/hle/kernel/thread.h" |
| 35 | #include "core/hle/kernel/time_manager.h" | ||
| 26 | #include "core/hle/result.h" | 36 | #include "core/hle/result.h" |
| 27 | #include "core/memory.h" | 37 | #include "core/memory.h" |
| 28 | 38 | ||
| @@ -44,46 +54,26 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} | |||
| 44 | Thread::~Thread() = default; | 54 | Thread::~Thread() = default; |
| 45 | 55 | ||
| 46 | void Thread::Stop() { | 56 | void Thread::Stop() { |
| 47 | // Cancel any outstanding wakeup events for this thread | 57 | { |
| 48 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | 58 | SchedulerLock lock(kernel); |
| 49 | global_handle); | 59 | SetStatus(ThreadStatus::Dead); |
| 50 | kernel.GlobalHandleTable().Close(global_handle); | 60 | Signal(); |
| 51 | global_handle = 0; | 61 | kernel.GlobalHandleTable().Close(global_handle); |
| 52 | SetStatus(ThreadStatus::Dead); | ||
| 53 | Signal(); | ||
| 54 | |||
| 55 | // Clean up any dangling references in objects that this thread was waiting for | ||
| 56 | for (auto& wait_object : wait_objects) { | ||
| 57 | wait_object->RemoveWaitingThread(SharedFrom(this)); | ||
| 58 | } | ||
| 59 | wait_objects.clear(); | ||
| 60 | |||
| 61 | owner_process->UnregisterThread(this); | ||
| 62 | |||
| 63 | // Mark the TLS slot in the thread's page as free. | ||
| 64 | owner_process->FreeTLSRegion(tls_address); | ||
| 65 | } | ||
| 66 | |||
| 67 | void Thread::WakeAfterDelay(s64 nanoseconds) { | ||
| 68 | // Don't schedule a wakeup if the thread wants to wait forever | ||
| 69 | if (nanoseconds == -1) | ||
| 70 | return; | ||
| 71 | 62 | ||
| 72 | // This function might be called from any thread so we have to be cautious and use the | 63 | if (owner_process) { |
| 73 | // thread-safe version of ScheduleEvent. | 64 | owner_process->UnregisterThread(this); |
| 74 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | ||
| 75 | Core::System::GetInstance().CoreTiming().ScheduleEvent( | ||
| 76 | cycles, kernel.ThreadWakeupCallbackEventType(), global_handle); | ||
| 77 | } | ||
| 78 | 65 | ||
| 79 | void Thread::CancelWakeupTimer() { | 66 | // Mark the TLS slot in the thread's page as free. |
| 80 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | 67 | owner_process->FreeTLSRegion(tls_address); |
| 81 | global_handle); | 68 | } |
| 69 | arm_interface.reset(); | ||
| 70 | has_exited = true; | ||
| 71 | } | ||
| 72 | global_handle = 0; | ||
| 82 | } | 73 | } |
| 83 | 74 | ||
| 84 | void Thread::ResumeFromWait() { | 75 | void Thread::ResumeFromWait() { |
| 85 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); | 76 | SchedulerLock lock(kernel); |
| 86 | |||
| 87 | switch (status) { | 77 | switch (status) { |
| 88 | case ThreadStatus::Paused: | 78 | case ThreadStatus::Paused: |
| 89 | case ThreadStatus::WaitSynch: | 79 | case ThreadStatus::WaitSynch: |
| @@ -99,7 +89,7 @@ void Thread::ResumeFromWait() { | |||
| 99 | case ThreadStatus::Ready: | 89 | case ThreadStatus::Ready: |
| 100 | // The thread's wakeup callback must have already been cleared when the thread was first | 90 | // The thread's wakeup callback must have already been cleared when the thread was first |
| 101 | // awoken. | 91 | // awoken. |
| 102 | ASSERT(wakeup_callback == nullptr); | 92 | ASSERT(hle_callback == nullptr); |
| 103 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | 93 | // If the thread is waiting on multiple wait objects, it might be awoken more than once |
| 104 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | 94 | // before actually resuming. We can ignore subsequent wakeups if the thread status has |
| 105 | // already been set to ThreadStatus::Ready. | 95 | // already been set to ThreadStatus::Ready. |
| @@ -115,24 +105,31 @@ void Thread::ResumeFromWait() { | |||
| 115 | return; | 105 | return; |
| 116 | } | 106 | } |
| 117 | 107 | ||
| 118 | wakeup_callback = nullptr; | 108 | SetStatus(ThreadStatus::Ready); |
| 109 | } | ||
| 110 | |||
| 111 | void Thread::OnWakeUp() { | ||
| 112 | SchedulerLock lock(kernel); | ||
| 119 | 113 | ||
| 120 | if (activity == ThreadActivity::Paused) { | 114 | SetStatus(ThreadStatus::Ready); |
| 121 | SetStatus(ThreadStatus::Paused); | 115 | } |
| 122 | return; | ||
| 123 | } | ||
| 124 | 116 | ||
| 117 | ResultCode Thread::Start() { | ||
| 118 | SchedulerLock lock(kernel); | ||
| 125 | SetStatus(ThreadStatus::Ready); | 119 | SetStatus(ThreadStatus::Ready); |
| 120 | return RESULT_SUCCESS; | ||
| 126 | } | 121 | } |
| 127 | 122 | ||
| 128 | void Thread::CancelWait() { | 123 | void Thread::CancelWait() { |
| 129 | if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { | 124 | SchedulerLock lock(kernel); |
| 125 | if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { | ||
| 130 | is_sync_cancelled = true; | 126 | is_sync_cancelled = true; |
| 131 | return; | 127 | return; |
| 132 | } | 128 | } |
| 129 | // TODO(Blinkhawk): Implement cancel of server session | ||
| 133 | is_sync_cancelled = false; | 130 | is_sync_cancelled = false; |
| 134 | SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); | 131 | SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); |
| 135 | ResumeFromWait(); | 132 | SetStatus(ThreadStatus::Ready); |
| 136 | } | 133 | } |
| 137 | 134 | ||
| 138 | static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, | 135 | static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, |
| @@ -153,12 +150,29 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, | |||
| 153 | context.fpcr = 0; | 150 | context.fpcr = 0; |
| 154 | } | 151 | } |
| 155 | 152 | ||
| 156 | ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, | 153 | std::shared_ptr<Common::Fiber>& Thread::GetHostContext() { |
| 157 | VAddr entry_point, u32 priority, u64 arg, | 154 | return host_context; |
| 158 | s32 processor_id, VAddr stack_top, | 155 | } |
| 159 | Process& owner_process) { | 156 | |
| 157 | ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, | ||
| 158 | std::string name, VAddr entry_point, u32 priority, | ||
| 159 | u64 arg, s32 processor_id, VAddr stack_top, | ||
| 160 | Process* owner_process) { | ||
| 161 | std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc(); | ||
| 162 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 163 | return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, | ||
| 164 | owner_process, std::move(init_func), init_func_parameter); | ||
| 165 | } | ||
| 166 | |||
| 167 | ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, | ||
| 168 | std::string name, VAddr entry_point, u32 priority, | ||
| 169 | u64 arg, s32 processor_id, VAddr stack_top, | ||
| 170 | Process* owner_process, | ||
| 171 | std::function<void(void*)>&& thread_start_func, | ||
| 172 | void* thread_start_parameter) { | ||
| 173 | auto& kernel = system.Kernel(); | ||
| 160 | // Check if priority is in ranged. Lowest priority -> highest priority id. | 174 | // Check if priority is in ranged. Lowest priority -> highest priority id. |
| 161 | if (priority > THREADPRIO_LOWEST) { | 175 | if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) { |
| 162 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); | 176 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); |
| 163 | return ERR_INVALID_THREAD_PRIORITY; | 177 | return ERR_INVALID_THREAD_PRIORITY; |
| 164 | } | 178 | } |
| @@ -168,11 +182,12 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 168 | return ERR_INVALID_PROCESSOR_ID; | 182 | return ERR_INVALID_PROCESSOR_ID; |
| 169 | } | 183 | } |
| 170 | 184 | ||
| 171 | auto& system = Core::System::GetInstance(); | 185 | if (owner_process) { |
| 172 | if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) { | 186 | if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) { |
| 173 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); | 187 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); |
| 174 | // TODO (bunnei): Find the correct error code to use here | 188 | // TODO (bunnei): Find the correct error code to use here |
| 175 | return RESULT_UNKNOWN; | 189 | return RESULT_UNKNOWN; |
| 190 | } | ||
| 176 | } | 191 | } |
| 177 | 192 | ||
| 178 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); | 193 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); |
| @@ -183,51 +198,82 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 183 | thread->stack_top = stack_top; | 198 | thread->stack_top = stack_top; |
| 184 | thread->tpidr_el0 = 0; | 199 | thread->tpidr_el0 = 0; |
| 185 | thread->nominal_priority = thread->current_priority = priority; | 200 | thread->nominal_priority = thread->current_priority = priority; |
| 186 | thread->last_running_ticks = system.CoreTiming().GetTicks(); | 201 | thread->last_running_ticks = 0; |
| 187 | thread->processor_id = processor_id; | 202 | thread->processor_id = processor_id; |
| 188 | thread->ideal_core = processor_id; | 203 | thread->ideal_core = processor_id; |
| 189 | thread->affinity_mask = 1ULL << processor_id; | 204 | thread->affinity_mask = 1ULL << processor_id; |
| 190 | thread->wait_objects.clear(); | 205 | thread->wait_objects = nullptr; |
| 191 | thread->mutex_wait_address = 0; | 206 | thread->mutex_wait_address = 0; |
| 192 | thread->condvar_wait_address = 0; | 207 | thread->condvar_wait_address = 0; |
| 193 | thread->wait_handle = 0; | 208 | thread->wait_handle = 0; |
| 194 | thread->name = std::move(name); | 209 | thread->name = std::move(name); |
| 195 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); | 210 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); |
| 196 | thread->owner_process = &owner_process; | 211 | thread->owner_process = owner_process; |
| 197 | auto& scheduler = kernel.GlobalScheduler(); | 212 | thread->type = type_flags; |
| 198 | scheduler.AddThread(thread); | 213 | if ((type_flags & THREADTYPE_IDLE) == 0) { |
| 199 | thread->tls_address = thread->owner_process->CreateTLSRegion(); | 214 | auto& scheduler = kernel.GlobalScheduler(); |
| 200 | 215 | scheduler.AddThread(thread); | |
| 201 | thread->owner_process->RegisterThread(thread.get()); | 216 | } |
| 217 | if (owner_process) { | ||
| 218 | thread->tls_address = thread->owner_process->CreateTLSRegion(); | ||
| 219 | thread->owner_process->RegisterThread(thread.get()); | ||
| 220 | } else { | ||
| 221 | thread->tls_address = 0; | ||
| 222 | } | ||
| 223 | // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used | ||
| 224 | // to initialize the context | ||
| 225 | thread->arm_interface.reset(); | ||
| 226 | if ((type_flags & THREADTYPE_HLE) == 0) { | ||
| 227 | #ifdef ARCHITECTURE_x86_64 | ||
| 228 | if (owner_process && !owner_process->Is64BitProcess()) { | ||
| 229 | thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( | ||
| 230 | system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), | ||
| 231 | processor_id); | ||
| 232 | } else { | ||
| 233 | thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( | ||
| 234 | system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), | ||
| 235 | processor_id); | ||
| 236 | } | ||
| 202 | 237 | ||
| 203 | ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), | 238 | #else |
| 204 | static_cast<u32>(entry_point), static_cast<u32>(arg)); | 239 | if (owner_process && !owner_process->Is64BitProcess()) { |
| 205 | ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); | 240 | thread->arm_interface = std::make_shared<Core::ARM_Unicorn>( |
| 241 | system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32, | ||
| 242 | processor_id); | ||
| 243 | } else { | ||
| 244 | thread->arm_interface = std::make_shared<Core::ARM_Unicorn>( | ||
| 245 | system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64, | ||
| 246 | processor_id); | ||
| 247 | } | ||
| 248 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 249 | #endif | ||
| 250 | ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), | ||
| 251 | static_cast<u32>(entry_point), static_cast<u32>(arg)); | ||
| 252 | ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); | ||
| 253 | } | ||
| 254 | thread->host_context = | ||
| 255 | std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); | ||
| 206 | 256 | ||
| 207 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); | 257 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); |
| 208 | } | 258 | } |
| 209 | 259 | ||
| 210 | void Thread::SetPriority(u32 priority) { | 260 | void Thread::SetPriority(u32 priority) { |
| 261 | SchedulerLock lock(kernel); | ||
| 211 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, | 262 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, |
| 212 | "Invalid priority value."); | 263 | "Invalid priority value."); |
| 213 | nominal_priority = priority; | 264 | nominal_priority = priority; |
| 214 | UpdatePriority(); | 265 | UpdatePriority(); |
| 215 | } | 266 | } |
| 216 | 267 | ||
| 217 | void Thread::SetWaitSynchronizationResult(ResultCode result) { | 268 | void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { |
| 218 | context_32.cpu_registers[0] = result.raw; | 269 | signaling_object = object; |
| 219 | context_64.cpu_registers[0] = result.raw; | 270 | signaling_result = result; |
| 220 | } | ||
| 221 | |||
| 222 | void Thread::SetWaitSynchronizationOutput(s32 output) { | ||
| 223 | context_32.cpu_registers[1] = output; | ||
| 224 | context_64.cpu_registers[1] = output; | ||
| 225 | } | 271 | } |
| 226 | 272 | ||
| 227 | s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { | 273 | s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { |
| 228 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); | 274 | ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything"); |
| 229 | const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); | 275 | const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object); |
| 230 | return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); | 276 | return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1); |
| 231 | } | 277 | } |
| 232 | 278 | ||
| 233 | VAddr Thread::GetCommandBufferAddress() const { | 279 | VAddr Thread::GetCommandBufferAddress() const { |
| @@ -236,6 +282,14 @@ VAddr Thread::GetCommandBufferAddress() const { | |||
| 236 | return GetTLSAddress() + command_header_offset; | 282 | return GetTLSAddress() + command_header_offset; |
| 237 | } | 283 | } |
| 238 | 284 | ||
| 285 | Core::ARM_Interface& Thread::ArmInterface() { | ||
| 286 | return *arm_interface; | ||
| 287 | } | ||
| 288 | |||
| 289 | const Core::ARM_Interface& Thread::ArmInterface() const { | ||
| 290 | return *arm_interface; | ||
| 291 | } | ||
| 292 | |||
| 239 | void Thread::SetStatus(ThreadStatus new_status) { | 293 | void Thread::SetStatus(ThreadStatus new_status) { |
| 240 | if (new_status == status) { | 294 | if (new_status == status) { |
| 241 | return; | 295 | return; |
| @@ -257,10 +311,6 @@ void Thread::SetStatus(ThreadStatus new_status) { | |||
| 257 | break; | 311 | break; |
| 258 | } | 312 | } |
| 259 | 313 | ||
| 260 | if (status == ThreadStatus::Running) { | ||
| 261 | last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); | ||
| 262 | } | ||
| 263 | |||
| 264 | status = new_status; | 314 | status = new_status; |
| 265 | } | 315 | } |
| 266 | 316 | ||
| @@ -341,75 +391,116 @@ void Thread::UpdatePriority() { | |||
| 341 | lock_owner->UpdatePriority(); | 391 | lock_owner->UpdatePriority(); |
| 342 | } | 392 | } |
| 343 | 393 | ||
| 344 | void Thread::ChangeCore(u32 core, u64 mask) { | ||
| 345 | SetCoreAndAffinityMask(core, mask); | ||
| 346 | } | ||
| 347 | |||
| 348 | bool Thread::AllSynchronizationObjectsReady() const { | 394 | bool Thread::AllSynchronizationObjectsReady() const { |
| 349 | return std::none_of(wait_objects.begin(), wait_objects.end(), | 395 | return std::none_of(wait_objects->begin(), wait_objects->end(), |
| 350 | [this](const std::shared_ptr<SynchronizationObject>& object) { | 396 | [this](const std::shared_ptr<SynchronizationObject>& object) { |
| 351 | return object->ShouldWait(this); | 397 | return object->ShouldWait(this); |
| 352 | }); | 398 | }); |
| 353 | } | 399 | } |
| 354 | 400 | ||
| 355 | bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 401 | bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) { |
| 356 | std::shared_ptr<SynchronizationObject> object, | 402 | ASSERT(hle_callback); |
| 357 | std::size_t index) { | 403 | return hle_callback(std::move(thread)); |
| 358 | ASSERT(wakeup_callback); | ||
| 359 | return wakeup_callback(reason, std::move(thread), std::move(object), index); | ||
| 360 | } | 404 | } |
| 361 | 405 | ||
| 362 | void Thread::SetActivity(ThreadActivity value) { | 406 | ResultCode Thread::SetActivity(ThreadActivity value) { |
| 363 | activity = value; | 407 | SchedulerLock lock(kernel); |
| 408 | |||
| 409 | auto sched_status = GetSchedulingStatus(); | ||
| 410 | |||
| 411 | if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { | ||
| 412 | return ERR_INVALID_STATE; | ||
| 413 | } | ||
| 414 | |||
| 415 | if (IsPendingTermination()) { | ||
| 416 | return RESULT_SUCCESS; | ||
| 417 | } | ||
| 364 | 418 | ||
| 365 | if (value == ThreadActivity::Paused) { | 419 | if (value == ThreadActivity::Paused) { |
| 366 | // Set status if not waiting | 420 | if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) { |
| 367 | if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { | 421 | return ERR_INVALID_STATE; |
| 368 | SetStatus(ThreadStatus::Paused); | 422 | } |
| 369 | kernel.PrepareReschedule(processor_id); | 423 | AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); |
| 424 | } else { | ||
| 425 | if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) { | ||
| 426 | return ERR_INVALID_STATE; | ||
| 370 | } | 427 | } |
| 371 | } else if (status == ThreadStatus::Paused) { | 428 | RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); |
| 372 | // Ready to reschedule | ||
| 373 | ResumeFromWait(); | ||
| 374 | } | 429 | } |
| 430 | return RESULT_SUCCESS; | ||
| 375 | } | 431 | } |
| 376 | 432 | ||
| 377 | void Thread::Sleep(s64 nanoseconds) { | 433 | ResultCode Thread::Sleep(s64 nanoseconds) { |
| 378 | // Sleep current thread and check for next thread to schedule | 434 | Handle event_handle{}; |
| 379 | SetStatus(ThreadStatus::WaitSleep); | 435 | { |
| 436 | SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); | ||
| 437 | SetStatus(ThreadStatus::WaitSleep); | ||
| 438 | } | ||
| 380 | 439 | ||
| 381 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 440 | if (event_handle != InvalidHandle) { |
| 382 | WakeAfterDelay(nanoseconds); | 441 | auto& time_manager = kernel.TimeManager(); |
| 442 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 443 | } | ||
| 444 | return RESULT_SUCCESS; | ||
| 445 | } | ||
| 446 | |||
| 447 | std::pair<ResultCode, bool> Thread::YieldSimple() { | ||
| 448 | bool is_redundant = false; | ||
| 449 | { | ||
| 450 | SchedulerLock lock(kernel); | ||
| 451 | is_redundant = kernel.GlobalScheduler().YieldThread(this); | ||
| 452 | } | ||
| 453 | return {RESULT_SUCCESS, is_redundant}; | ||
| 454 | } | ||
| 455 | |||
| 456 | std::pair<ResultCode, bool> Thread::YieldAndBalanceLoad() { | ||
| 457 | bool is_redundant = false; | ||
| 458 | { | ||
| 459 | SchedulerLock lock(kernel); | ||
| 460 | is_redundant = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); | ||
| 461 | } | ||
| 462 | return {RESULT_SUCCESS, is_redundant}; | ||
| 383 | } | 463 | } |
| 384 | 464 | ||
| 385 | bool Thread::YieldSimple() { | 465 | std::pair<ResultCode, bool> Thread::YieldAndWaitForLoadBalancing() { |
| 386 | auto& scheduler = kernel.GlobalScheduler(); | 466 | bool is_redundant = false; |
| 387 | return scheduler.YieldThread(this); | 467 | { |
| 468 | SchedulerLock lock(kernel); | ||
| 469 | is_redundant = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); | ||
| 470 | } | ||
| 471 | return {RESULT_SUCCESS, is_redundant}; | ||
| 388 | } | 472 | } |
| 389 | 473 | ||
| 390 | bool Thread::YieldAndBalanceLoad() { | 474 | void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { |
| 391 | auto& scheduler = kernel.GlobalScheduler(); | 475 | const u32 old_state = scheduling_state; |
| 392 | return scheduler.YieldThreadAndBalanceLoad(this); | 476 | pausing_state |= static_cast<u32>(flag); |
| 477 | const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); | ||
| 478 | scheduling_state = base_scheduling | pausing_state; | ||
| 479 | kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); | ||
| 393 | } | 480 | } |
| 394 | 481 | ||
| 395 | bool Thread::YieldAndWaitForLoadBalancing() { | 482 | void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { |
| 396 | auto& scheduler = kernel.GlobalScheduler(); | 483 | const u32 old_state = scheduling_state; |
| 397 | return scheduler.YieldThreadAndWaitForLoadBalancing(this); | 484 | pausing_state &= ~static_cast<u32>(flag); |
| 485 | const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); | ||
| 486 | scheduling_state = base_scheduling | pausing_state; | ||
| 487 | kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); | ||
| 398 | } | 488 | } |
| 399 | 489 | ||
| 400 | void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { | 490 | void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { |
| 401 | const u32 old_flags = scheduling_state; | 491 | const u32 old_state = scheduling_state; |
| 402 | scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | | 492 | scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | |
| 403 | static_cast<u32>(new_status); | 493 | static_cast<u32>(new_status); |
| 404 | AdjustSchedulingOnStatus(old_flags); | 494 | kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); |
| 405 | } | 495 | } |
| 406 | 496 | ||
| 407 | void Thread::SetCurrentPriority(u32 new_priority) { | 497 | void Thread::SetCurrentPriority(u32 new_priority) { |
| 408 | const u32 old_priority = std::exchange(current_priority, new_priority); | 498 | const u32 old_priority = std::exchange(current_priority, new_priority); |
| 409 | AdjustSchedulingOnPriority(old_priority); | 499 | kernel.GlobalScheduler().AdjustSchedulingOnPriority(this, old_priority); |
| 410 | } | 500 | } |
| 411 | 501 | ||
| 412 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | 502 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { |
| 503 | SchedulerLock lock(kernel); | ||
| 413 | const auto HighestSetCore = [](u64 mask, u32 max_cores) { | 504 | const auto HighestSetCore = [](u64 mask, u32 max_cores) { |
| 414 | for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) { | 505 | for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) { |
| 415 | if (((mask >> core) & 1) != 0) { | 506 | if (((mask >> core) & 1) != 0) { |
| @@ -443,111 +534,12 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | |||
| 443 | processor_id = ideal_core; | 534 | processor_id = ideal_core; |
| 444 | } | 535 | } |
| 445 | } | 536 | } |
| 446 | AdjustSchedulingOnAffinity(old_affinity_mask, old_core); | 537 | kernel.GlobalScheduler().AdjustSchedulingOnAffinity(this, old_affinity_mask, old_core); |
| 447 | } | 538 | } |
| 448 | } | 539 | } |
| 449 | return RESULT_SUCCESS; | 540 | return RESULT_SUCCESS; |
| 450 | } | 541 | } |
| 451 | 542 | ||
| 452 | void Thread::AdjustSchedulingOnStatus(u32 old_flags) { | ||
| 453 | if (old_flags == scheduling_state) { | ||
| 454 | return; | ||
| 455 | } | ||
| 456 | |||
| 457 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 458 | if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) == | ||
| 459 | ThreadSchedStatus::Runnable) { | ||
| 460 | // In this case the thread was running, now it's pausing/exitting | ||
| 461 | if (processor_id >= 0) { | ||
| 462 | scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 463 | } | ||
| 464 | |||
| 465 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 466 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 467 | scheduler.Unsuggest(current_priority, core, this); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) { | ||
| 471 | // The thread is now set to running from being stopped | ||
| 472 | if (processor_id >= 0) { | ||
| 473 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 474 | } | ||
| 475 | |||
| 476 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 477 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 478 | scheduler.Suggest(current_priority, core, this); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | scheduler.SetReselectionPending(); | ||
| 484 | } | ||
| 485 | |||
| 486 | void Thread::AdjustSchedulingOnPriority(u32 old_priority) { | ||
| 487 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) { | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 491 | if (processor_id >= 0) { | ||
| 492 | scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this); | ||
| 493 | } | ||
| 494 | |||
| 495 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 496 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 497 | scheduler.Unsuggest(old_priority, core, this); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | // Add thread to the new priority queues. | ||
| 502 | Thread* current_thread = GetCurrentThread(); | ||
| 503 | |||
| 504 | if (processor_id >= 0) { | ||
| 505 | if (current_thread == this) { | ||
| 506 | scheduler.SchedulePrepend(current_priority, static_cast<u32>(processor_id), this); | ||
| 507 | } else { | ||
| 508 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 513 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 514 | scheduler.Suggest(current_priority, core, this); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | scheduler.SetReselectionPending(); | ||
| 519 | } | ||
| 520 | |||
| 521 | void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) { | ||
| 522 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 523 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable || | ||
| 524 | current_priority >= THREADPRIO_COUNT) { | ||
| 525 | return; | ||
| 526 | } | ||
| 527 | |||
| 528 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 529 | if (((old_affinity_mask >> core) & 1) != 0) { | ||
| 530 | if (core == static_cast<u32>(old_core)) { | ||
| 531 | scheduler.Unschedule(current_priority, core, this); | ||
| 532 | } else { | ||
| 533 | scheduler.Unsuggest(current_priority, core, this); | ||
| 534 | } | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 539 | if (((affinity_mask >> core) & 1) != 0) { | ||
| 540 | if (core == static_cast<u32>(processor_id)) { | ||
| 541 | scheduler.Schedule(current_priority, core, this); | ||
| 542 | } else { | ||
| 543 | scheduler.Suggest(current_priority, core, this); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | scheduler.SetReselectionPending(); | ||
| 549 | } | ||
| 550 | |||
| 551 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 543 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 552 | 544 | ||
| 553 | /** | 545 | /** |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 23fdef8a4..c0342c462 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -6,26 +6,47 @@ | |||
| 6 | 6 | ||
| 7 | #include <functional> | 7 | #include <functional> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <utility> | ||
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | 11 | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/spin_lock.h" | ||
| 12 | #include "core/arm/arm_interface.h" | 14 | #include "core/arm/arm_interface.h" |
| 13 | #include "core/hle/kernel/object.h" | 15 | #include "core/hle/kernel/object.h" |
| 14 | #include "core/hle/kernel/synchronization_object.h" | 16 | #include "core/hle/kernel/synchronization_object.h" |
| 15 | #include "core/hle/result.h" | 17 | #include "core/hle/result.h" |
| 16 | 18 | ||
| 19 | namespace Common { | ||
| 20 | class Fiber; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Core { | ||
| 24 | class ARM_Interface; | ||
| 25 | class System; | ||
| 26 | } // namespace Core | ||
| 27 | |||
| 17 | namespace Kernel { | 28 | namespace Kernel { |
| 18 | 29 | ||
| 30 | class GlobalScheduler; | ||
| 19 | class KernelCore; | 31 | class KernelCore; |
| 20 | class Process; | 32 | class Process; |
| 21 | class Scheduler; | 33 | class Scheduler; |
| 22 | 34 | ||
| 23 | enum ThreadPriority : u32 { | 35 | enum ThreadPriority : u32 { |
| 24 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority | 36 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority |
| 25 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps | 37 | THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration |
| 26 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps | 38 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps |
| 27 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority | 39 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps |
| 28 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | 40 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority |
| 41 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum ThreadType : u32 { | ||
| 45 | THREADTYPE_USER = 0x1, | ||
| 46 | THREADTYPE_KERNEL = 0x2, | ||
| 47 | THREADTYPE_HLE = 0x4, | ||
| 48 | THREADTYPE_IDLE = 0x8, | ||
| 49 | THREADTYPE_SUSPEND = 0x10, | ||
| 29 | }; | 50 | }; |
| 30 | 51 | ||
| 31 | enum ThreadProcessorId : s32 { | 52 | enum ThreadProcessorId : s32 { |
| @@ -107,26 +128,45 @@ public: | |||
| 107 | 128 | ||
| 108 | using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; | 129 | using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; |
| 109 | 130 | ||
| 110 | using WakeupCallback = | 131 | using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>; |
| 111 | std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 132 | |
| 112 | std::shared_ptr<SynchronizationObject> object, std::size_t index)>; | 133 | /** |
| 134 | * Creates and returns a new thread. The new thread is immediately scheduled | ||
| 135 | * @param system The instance of the whole system | ||
| 136 | * @param name The friendly name desired for the thread | ||
| 137 | * @param entry_point The address at which the thread should start execution | ||
| 138 | * @param priority The thread's priority | ||
| 139 | * @param arg User data to pass to the thread | ||
| 140 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | ||
| 141 | * @param stack_top The address of the thread's stack top | ||
| 142 | * @param owner_process The parent process for the thread, if null, it's a kernel thread | ||
| 143 | * @return A shared pointer to the newly created thread | ||
| 144 | */ | ||
| 145 | static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, | ||
| 146 | std::string name, VAddr entry_point, | ||
| 147 | u32 priority, u64 arg, s32 processor_id, | ||
| 148 | VAddr stack_top, Process* owner_process); | ||
| 113 | 149 | ||
| 114 | /** | 150 | /** |
| 115 | * Creates and returns a new thread. The new thread is immediately scheduled | 151 | * Creates and returns a new thread. The new thread is immediately scheduled |
| 116 | * @param kernel The kernel instance this thread will be created under. | 152 | * @param system The instance of the whole system |
| 117 | * @param name The friendly name desired for the thread | 153 | * @param name The friendly name desired for the thread |
| 118 | * @param entry_point The address at which the thread should start execution | 154 | * @param entry_point The address at which the thread should start execution |
| 119 | * @param priority The thread's priority | 155 | * @param priority The thread's priority |
| 120 | * @param arg User data to pass to the thread | 156 | * @param arg User data to pass to the thread |
| 121 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | 157 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run |
| 122 | * @param stack_top The address of the thread's stack top | 158 | * @param stack_top The address of the thread's stack top |
| 123 | * @param owner_process The parent process for the thread | 159 | * @param owner_process The parent process for the thread, if null, it's a kernel thread |
| 160 | * @param thread_start_func The function where the host context will start. | ||
| 161 | * @param thread_start_parameter The parameter which will passed to host context on init | ||
| 124 | * @return A shared pointer to the newly created thread | 162 | * @return A shared pointer to the newly created thread |
| 125 | */ | 163 | */ |
| 126 | static ResultVal<std::shared_ptr<Thread>> Create(KernelCore& kernel, std::string name, | 164 | static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, |
| 127 | VAddr entry_point, u32 priority, u64 arg, | 165 | std::string name, VAddr entry_point, |
| 128 | s32 processor_id, VAddr stack_top, | 166 | u32 priority, u64 arg, s32 processor_id, |
| 129 | Process& owner_process); | 167 | VAddr stack_top, Process* owner_process, |
| 168 | std::function<void(void*)>&& thread_start_func, | ||
| 169 | void* thread_start_parameter); | ||
| 130 | 170 | ||
| 131 | std::string GetName() const override { | 171 | std::string GetName() const override { |
| 132 | return name; | 172 | return name; |
| @@ -181,7 +221,7 @@ public: | |||
| 181 | void UpdatePriority(); | 221 | void UpdatePriority(); |
| 182 | 222 | ||
| 183 | /// Changes the core that the thread is running or scheduled to run on. | 223 | /// Changes the core that the thread is running or scheduled to run on. |
| 184 | void ChangeCore(u32 core, u64 mask); | 224 | ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); |
| 185 | 225 | ||
| 186 | /** | 226 | /** |
| 187 | * Gets the thread's thread ID | 227 | * Gets the thread's thread ID |
| @@ -194,6 +234,10 @@ public: | |||
| 194 | /// Resumes a thread from waiting | 234 | /// Resumes a thread from waiting |
| 195 | void ResumeFromWait(); | 235 | void ResumeFromWait(); |
| 196 | 236 | ||
| 237 | void OnWakeUp(); | ||
| 238 | |||
| 239 | ResultCode Start(); | ||
| 240 | |||
| 197 | /// Cancels a waiting operation that this thread may or may not be within. | 241 | /// Cancels a waiting operation that this thread may or may not be within. |
| 198 | /// | 242 | /// |
| 199 | /// When the thread is within a waiting state, this will set the thread's | 243 | /// When the thread is within a waiting state, this will set the thread's |
| @@ -202,26 +246,19 @@ public: | |||
| 202 | /// | 246 | /// |
| 203 | void CancelWait(); | 247 | void CancelWait(); |
| 204 | 248 | ||
| 205 | /** | 249 | void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); |
| 206 | * Schedules an event to wake up the specified thread after the specified delay | ||
| 207 | * @param nanoseconds The time this thread will be allowed to sleep for | ||
| 208 | */ | ||
| 209 | void WakeAfterDelay(s64 nanoseconds); | ||
| 210 | 250 | ||
| 211 | /// Cancel any outstanding wakeup events for this thread | 251 | Core::ARM_Interface& ArmInterface(); |
| 212 | void CancelWakeupTimer(); | ||
| 213 | 252 | ||
| 214 | /** | 253 | const Core::ARM_Interface& ArmInterface() const; |
| 215 | * Sets the result after the thread awakens (from svcWaitSynchronization) | ||
| 216 | * @param result Value to set to the returned result | ||
| 217 | */ | ||
| 218 | void SetWaitSynchronizationResult(ResultCode result); | ||
| 219 | 254 | ||
| 220 | /** | 255 | SynchronizationObject* GetSignalingObject() const { |
| 221 | * Sets the output parameter value after the thread awakens (from svcWaitSynchronization) | 256 | return signaling_object; |
| 222 | * @param output Value to set to the output parameter | 257 | } |
| 223 | */ | 258 | |
| 224 | void SetWaitSynchronizationOutput(s32 output); | 259 | ResultCode GetSignalingResult() const { |
| 260 | return signaling_result; | ||
| 261 | } | ||
| 225 | 262 | ||
| 226 | /** | 263 | /** |
| 227 | * Retrieves the index that this particular object occupies in the list of objects | 264 | * Retrieves the index that this particular object occupies in the list of objects |
| @@ -269,11 +306,6 @@ public: | |||
| 269 | */ | 306 | */ |
| 270 | VAddr GetCommandBufferAddress() const; | 307 | VAddr GetCommandBufferAddress() const; |
| 271 | 308 | ||
| 272 | /// Returns whether this thread is waiting on objects from a WaitSynchronization call. | ||
| 273 | bool IsSleepingOnWait() const { | ||
| 274 | return status == ThreadStatus::WaitSynch; | ||
| 275 | } | ||
| 276 | |||
| 277 | ThreadContext32& GetContext32() { | 309 | ThreadContext32& GetContext32() { |
| 278 | return context_32; | 310 | return context_32; |
| 279 | } | 311 | } |
| @@ -290,6 +322,28 @@ public: | |||
| 290 | return context_64; | 322 | return context_64; |
| 291 | } | 323 | } |
| 292 | 324 | ||
| 325 | bool IsHLEThread() const { | ||
| 326 | return (type & THREADTYPE_HLE) != 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | bool IsSuspendThread() const { | ||
| 330 | return (type & THREADTYPE_SUSPEND) != 0; | ||
| 331 | } | ||
| 332 | |||
| 333 | bool IsIdleThread() const { | ||
| 334 | return (type & THREADTYPE_IDLE) != 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | bool WasRunning() const { | ||
| 338 | return was_running; | ||
| 339 | } | ||
| 340 | |||
| 341 | void SetWasRunning(bool value) { | ||
| 342 | was_running = value; | ||
| 343 | } | ||
| 344 | |||
| 345 | std::shared_ptr<Common::Fiber>& GetHostContext(); | ||
| 346 | |||
| 293 | ThreadStatus GetStatus() const { | 347 | ThreadStatus GetStatus() const { |
| 294 | return status; | 348 | return status; |
| 295 | } | 349 | } |
| @@ -325,18 +379,18 @@ public: | |||
| 325 | } | 379 | } |
| 326 | 380 | ||
| 327 | const ThreadSynchronizationObjects& GetSynchronizationObjects() const { | 381 | const ThreadSynchronizationObjects& GetSynchronizationObjects() const { |
| 328 | return wait_objects; | 382 | return *wait_objects; |
| 329 | } | 383 | } |
| 330 | 384 | ||
| 331 | void SetSynchronizationObjects(ThreadSynchronizationObjects objects) { | 385 | void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) { |
| 332 | wait_objects = std::move(objects); | 386 | wait_objects = objects; |
| 333 | } | 387 | } |
| 334 | 388 | ||
| 335 | void ClearSynchronizationObjects() { | 389 | void ClearSynchronizationObjects() { |
| 336 | for (const auto& waiting_object : wait_objects) { | 390 | for (const auto& waiting_object : *wait_objects) { |
| 337 | waiting_object->RemoveWaitingThread(SharedFrom(this)); | 391 | waiting_object->RemoveWaitingThread(SharedFrom(this)); |
| 338 | } | 392 | } |
| 339 | wait_objects.clear(); | 393 | wait_objects->clear(); |
| 340 | } | 394 | } |
| 341 | 395 | ||
| 342 | /// Determines whether all the objects this thread is waiting on are ready. | 396 | /// Determines whether all the objects this thread is waiting on are ready. |
| @@ -386,26 +440,35 @@ public: | |||
| 386 | arb_wait_address = address; | 440 | arb_wait_address = address; |
| 387 | } | 441 | } |
| 388 | 442 | ||
| 389 | bool HasWakeupCallback() const { | 443 | bool HasHLECallback() const { |
| 390 | return wakeup_callback != nullptr; | 444 | return hle_callback != nullptr; |
| 391 | } | 445 | } |
| 392 | 446 | ||
| 393 | void SetWakeupCallback(WakeupCallback callback) { | 447 | void SetHLECallback(HLECallback callback) { |
| 394 | wakeup_callback = std::move(callback); | 448 | hle_callback = std::move(callback); |
| 395 | } | 449 | } |
| 396 | 450 | ||
| 397 | void InvalidateWakeupCallback() { | 451 | void SetHLETimeEvent(Handle time_event) { |
| 398 | SetWakeupCallback(nullptr); | 452 | hle_time_event = time_event; |
| 399 | } | 453 | } |
| 400 | 454 | ||
| 401 | /** | 455 | void SetHLESyncObject(SynchronizationObject* object) { |
| 402 | * Invokes the thread's wakeup callback. | 456 | hle_object = object; |
| 403 | * | 457 | } |
| 404 | * @pre A valid wakeup callback has been set. Violating this precondition | 458 | |
| 405 | * will cause an assertion to trigger. | 459 | Handle GetHLETimeEvent() const { |
| 406 | */ | 460 | return hle_time_event; |
| 407 | bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 461 | } |
| 408 | std::shared_ptr<SynchronizationObject> object, std::size_t index); | 462 | |
| 463 | SynchronizationObject* GetHLESyncObject() const { | ||
| 464 | return hle_object; | ||
| 465 | } | ||
| 466 | |||
| 467 | void InvalidateHLECallback() { | ||
| 468 | SetHLECallback(nullptr); | ||
| 469 | } | ||
| 470 | |||
| 471 | bool InvokeHLECallback(std::shared_ptr<Thread> thread); | ||
| 409 | 472 | ||
| 410 | u32 GetIdealCore() const { | 473 | u32 GetIdealCore() const { |
| 411 | return ideal_core; | 474 | return ideal_core; |
| @@ -415,23 +478,19 @@ public: | |||
| 415 | return affinity_mask; | 478 | return affinity_mask; |
| 416 | } | 479 | } |
| 417 | 480 | ||
| 418 | ThreadActivity GetActivity() const { | 481 | ResultCode SetActivity(ThreadActivity value); |
| 419 | return activity; | ||
| 420 | } | ||
| 421 | |||
| 422 | void SetActivity(ThreadActivity value); | ||
| 423 | 482 | ||
| 424 | /// Sleeps this thread for the given amount of nanoseconds. | 483 | /// Sleeps this thread for the given amount of nanoseconds. |
| 425 | void Sleep(s64 nanoseconds); | 484 | ResultCode Sleep(s64 nanoseconds); |
| 426 | 485 | ||
| 427 | /// Yields this thread without rebalancing loads. | 486 | /// Yields this thread without rebalancing loads. |
| 428 | bool YieldSimple(); | 487 | std::pair<ResultCode, bool> YieldSimple(); |
| 429 | 488 | ||
| 430 | /// Yields this thread and does a load rebalancing. | 489 | /// Yields this thread and does a load rebalancing. |
| 431 | bool YieldAndBalanceLoad(); | 490 | std::pair<ResultCode, bool> YieldAndBalanceLoad(); |
| 432 | 491 | ||
| 433 | /// Yields this thread and if the core is left idle, loads are rebalanced | 492 | /// Yields this thread and if the core is left idle, loads are rebalanced |
| 434 | bool YieldAndWaitForLoadBalancing(); | 493 | std::pair<ResultCode, bool> YieldAndWaitForLoadBalancing(); |
| 435 | 494 | ||
| 436 | void IncrementYieldCount() { | 495 | void IncrementYieldCount() { |
| 437 | yield_count++; | 496 | yield_count++; |
| @@ -446,6 +505,10 @@ public: | |||
| 446 | static_cast<u32>(ThreadSchedMasks::LowMask)); | 505 | static_cast<u32>(ThreadSchedMasks::LowMask)); |
| 447 | } | 506 | } |
| 448 | 507 | ||
| 508 | bool IsRunnable() const { | ||
| 509 | return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable); | ||
| 510 | } | ||
| 511 | |||
| 449 | bool IsRunning() const { | 512 | bool IsRunning() const { |
| 450 | return is_running; | 513 | return is_running; |
| 451 | } | 514 | } |
| @@ -466,17 +529,67 @@ public: | |||
| 466 | return global_handle; | 529 | return global_handle; |
| 467 | } | 530 | } |
| 468 | 531 | ||
| 532 | bool IsWaitingForArbitration() const { | ||
| 533 | return waiting_for_arbitration; | ||
| 534 | } | ||
| 535 | |||
| 536 | void WaitForArbitration(bool set) { | ||
| 537 | waiting_for_arbitration = set; | ||
| 538 | } | ||
| 539 | |||
| 540 | bool IsWaitingSync() const { | ||
| 541 | return is_waiting_on_sync; | ||
| 542 | } | ||
| 543 | |||
| 544 | void SetWaitingSync(bool is_waiting) { | ||
| 545 | is_waiting_on_sync = is_waiting; | ||
| 546 | } | ||
| 547 | |||
| 548 | bool IsPendingTermination() const { | ||
| 549 | return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited; | ||
| 550 | } | ||
| 551 | |||
| 552 | bool IsPaused() const { | ||
| 553 | return pausing_state != 0; | ||
| 554 | } | ||
| 555 | |||
| 556 | bool IsContinuousOnSVC() const { | ||
| 557 | return is_continuous_on_svc; | ||
| 558 | } | ||
| 559 | |||
| 560 | void SetContinuousOnSVC(bool is_continuous) { | ||
| 561 | is_continuous_on_svc = is_continuous; | ||
| 562 | } | ||
| 563 | |||
| 564 | bool IsPhantomMode() const { | ||
| 565 | return is_phantom_mode; | ||
| 566 | } | ||
| 567 | |||
| 568 | void SetPhantomMode(bool phantom) { | ||
| 569 | is_phantom_mode = phantom; | ||
| 570 | } | ||
| 571 | |||
| 572 | bool HasExited() const { | ||
| 573 | return has_exited; | ||
| 574 | } | ||
| 575 | |||
| 469 | private: | 576 | private: |
| 577 | friend class GlobalScheduler; | ||
| 578 | friend class Scheduler; | ||
| 579 | |||
| 470 | void SetSchedulingStatus(ThreadSchedStatus new_status); | 580 | void SetSchedulingStatus(ThreadSchedStatus new_status); |
| 581 | void AddSchedulingFlag(ThreadSchedFlags flag); | ||
| 582 | void RemoveSchedulingFlag(ThreadSchedFlags flag); | ||
| 583 | |||
| 471 | void SetCurrentPriority(u32 new_priority); | 584 | void SetCurrentPriority(u32 new_priority); |
| 472 | ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); | ||
| 473 | 585 | ||
| 474 | void AdjustSchedulingOnStatus(u32 old_flags); | ||
| 475 | void AdjustSchedulingOnPriority(u32 old_priority); | ||
| 476 | void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); | 586 | void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); |
| 477 | 587 | ||
| 588 | Common::SpinLock context_guard{}; | ||
| 478 | ThreadContext32 context_32{}; | 589 | ThreadContext32 context_32{}; |
| 479 | ThreadContext64 context_64{}; | 590 | ThreadContext64 context_64{}; |
| 591 | std::unique_ptr<Core::ARM_Interface> arm_interface{}; | ||
| 592 | std::shared_ptr<Common::Fiber> host_context{}; | ||
| 480 | 593 | ||
| 481 | u64 thread_id = 0; | 594 | u64 thread_id = 0; |
| 482 | 595 | ||
| @@ -485,6 +598,8 @@ private: | |||
| 485 | VAddr entry_point = 0; | 598 | VAddr entry_point = 0; |
| 486 | VAddr stack_top = 0; | 599 | VAddr stack_top = 0; |
| 487 | 600 | ||
| 601 | ThreadType type; | ||
| 602 | |||
| 488 | /// Nominal thread priority, as set by the emulated application. | 603 | /// Nominal thread priority, as set by the emulated application. |
| 489 | /// The nominal priority is the thread priority without priority | 604 | /// The nominal priority is the thread priority without priority |
| 490 | /// inheritance taken into account. | 605 | /// inheritance taken into account. |
| @@ -509,7 +624,10 @@ private: | |||
| 509 | 624 | ||
| 510 | /// Objects that the thread is waiting on, in the same order as they were | 625 | /// Objects that the thread is waiting on, in the same order as they were |
| 511 | /// passed to WaitSynchronization. | 626 | /// passed to WaitSynchronization. |
| 512 | ThreadSynchronizationObjects wait_objects; | 627 | ThreadSynchronizationObjects* wait_objects; |
| 628 | |||
| 629 | SynchronizationObject* signaling_object; | ||
| 630 | ResultCode signaling_result{RESULT_SUCCESS}; | ||
| 513 | 631 | ||
| 514 | /// List of threads that are waiting for a mutex that is held by this thread. | 632 | /// List of threads that are waiting for a mutex that is held by this thread. |
| 515 | MutexWaitingThreads wait_mutex_threads; | 633 | MutexWaitingThreads wait_mutex_threads; |
| @@ -526,30 +644,39 @@ private: | |||
| 526 | 644 | ||
| 527 | /// If waiting for an AddressArbiter, this is the address being waited on. | 645 | /// If waiting for an AddressArbiter, this is the address being waited on. |
| 528 | VAddr arb_wait_address{0}; | 646 | VAddr arb_wait_address{0}; |
| 647 | bool waiting_for_arbitration{}; | ||
| 529 | 648 | ||
| 530 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | 649 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. |
| 531 | Handle global_handle = 0; | 650 | Handle global_handle = 0; |
| 532 | 651 | ||
| 533 | /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 652 | /// Callback for HLE Events |
| 534 | /// was waiting via WaitSynchronization then the object will be the last object that became | 653 | HLECallback hle_callback; |
| 535 | /// available. In case of a timeout, the object will be nullptr. | 654 | Handle hle_time_event; |
| 536 | WakeupCallback wakeup_callback; | 655 | SynchronizationObject* hle_object; |
| 537 | 656 | ||
| 538 | Scheduler* scheduler = nullptr; | 657 | Scheduler* scheduler = nullptr; |
| 539 | 658 | ||
| 540 | u32 ideal_core{0xFFFFFFFF}; | 659 | u32 ideal_core{0xFFFFFFFF}; |
| 541 | u64 affinity_mask{0x1}; | 660 | u64 affinity_mask{0x1}; |
| 542 | 661 | ||
| 543 | ThreadActivity activity = ThreadActivity::Normal; | ||
| 544 | |||
| 545 | s32 ideal_core_override = -1; | 662 | s32 ideal_core_override = -1; |
| 546 | u64 affinity_mask_override = 0x1; | 663 | u64 affinity_mask_override = 0x1; |
| 547 | u32 affinity_override_count = 0; | 664 | u32 affinity_override_count = 0; |
| 548 | 665 | ||
| 549 | u32 scheduling_state = 0; | 666 | u32 scheduling_state = 0; |
| 667 | u32 pausing_state = 0; | ||
| 550 | bool is_running = false; | 668 | bool is_running = false; |
| 669 | bool is_waiting_on_sync = false; | ||
| 551 | bool is_sync_cancelled = false; | 670 | bool is_sync_cancelled = false; |
| 552 | 671 | ||
| 672 | bool is_continuous_on_svc = false; | ||
| 673 | |||
| 674 | bool will_be_terminated = false; | ||
| 675 | bool is_phantom_mode = false; | ||
| 676 | bool has_exited = false; | ||
| 677 | |||
| 678 | bool was_running = false; | ||
| 679 | |||
| 553 | std::string name; | 680 | std::string name; |
| 554 | }; | 681 | }; |
| 555 | 682 | ||
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 21b290468..941305e8e 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -8,30 +8,37 @@ | |||
| 8 | #include "core/core_timing_util.h" | 8 | #include "core/core_timing_util.h" |
| 9 | #include "core/hle/kernel/handle_table.h" | 9 | #include "core/hle/kernel/handle_table.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/scheduler.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 12 | #include "core/hle/kernel/time_manager.h" | 13 | #include "core/hle/kernel/time_manager.h" |
| 13 | 14 | ||
| 14 | namespace Kernel { | 15 | namespace Kernel { |
| 15 | 16 | ||
| 16 | TimeManager::TimeManager(Core::System& system) : system{system} { | 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { |
| 17 | time_manager_event_type = Core::Timing::CreateEvent( | 18 | time_manager_event_type = Core::Timing::CreateEvent( |
| 18 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | 19 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { |
| 20 | SchedulerLock lock(system.Kernel()); | ||
| 19 | Handle proper_handle = static_cast<Handle>(thread_handle); | 21 | Handle proper_handle = static_cast<Handle>(thread_handle); |
| 22 | if (cancelled_events[proper_handle]) { | ||
| 23 | return; | ||
| 24 | } | ||
| 20 | std::shared_ptr<Thread> thread = | 25 | std::shared_ptr<Thread> thread = |
| 21 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | 26 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); |
| 22 | thread->ResumeFromWait(); | 27 | thread->OnWakeUp(); |
| 23 | }); | 28 | }); |
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { | 31 | void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { |
| 32 | event_handle = timetask->GetGlobalHandle(); | ||
| 27 | if (nanoseconds > 0) { | 33 | if (nanoseconds > 0) { |
| 28 | ASSERT(timetask); | 34 | ASSERT(timetask); |
| 29 | event_handle = timetask->GetGlobalHandle(); | 35 | ASSERT(timetask->GetStatus() != ThreadStatus::Ready); |
| 30 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | 36 | ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); |
| 31 | system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); | 37 | system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle); |
| 32 | } else { | 38 | } else { |
| 33 | event_handle = InvalidHandle; | 39 | event_handle = InvalidHandle; |
| 34 | } | 40 | } |
| 41 | cancelled_events[event_handle] = false; | ||
| 35 | } | 42 | } |
| 36 | 43 | ||
| 37 | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { | 44 | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { |
| @@ -39,6 +46,12 @@ void TimeManager::UnscheduleTimeEvent(Handle event_handle) { | |||
| 39 | return; | 46 | return; |
| 40 | } | 47 | } |
| 41 | system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); | 48 | system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); |
| 49 | cancelled_events[event_handle] = true; | ||
| 50 | } | ||
| 51 | |||
| 52 | void TimeManager::CancelTimeEvent(Thread* time_task) { | ||
| 53 | Handle event_handle = time_task->GetGlobalHandle(); | ||
| 54 | UnscheduleTimeEvent(event_handle); | ||
| 42 | } | 55 | } |
| 43 | 56 | ||
| 44 | } // namespace Kernel | 57 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index eaec486d1..307a18765 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <unordered_map> | ||
| 8 | 9 | ||
| 9 | #include "core/hle/kernel/object.h" | 10 | #include "core/hle/kernel/object.h" |
| 10 | 11 | ||
| @@ -35,9 +36,12 @@ public: | |||
| 35 | /// Unschedule an existing time event | 36 | /// Unschedule an existing time event |
| 36 | void UnscheduleTimeEvent(Handle event_handle); | 37 | void UnscheduleTimeEvent(Handle event_handle); |
| 37 | 38 | ||
| 39 | void CancelTimeEvent(Thread* time_task); | ||
| 40 | |||
| 38 | private: | 41 | private: |
| 39 | Core::System& system; | 42 | Core::System& system; |
| 40 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; | 43 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; |
| 44 | std::unordered_map<Handle, bool> cancelled_events; | ||
| 41 | }; | 45 | }; |
| 42 | 46 | ||
| 43 | } // namespace Kernel | 47 | } // namespace Kernel |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 630a8b048..8ac856ec3 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -44,6 +44,218 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) { | |||
| 44 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); | 44 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { | ||
| 48 | public: | ||
| 49 | explicit IManagerForSystemService(Common::UUID user_id) | ||
| 50 | : ServiceFramework("IManagerForSystemService") { | ||
| 51 | // clang-format off | ||
| 52 | static const FunctionInfo functions[] = { | ||
| 53 | {0, nullptr, "CheckAvailability"}, | ||
| 54 | {1, nullptr, "GetAccountId"}, | ||
| 55 | {2, nullptr, "EnsureIdTokenCacheAsync"}, | ||
| 56 | {3, nullptr, "LoadIdTokenCache"}, | ||
| 57 | {100, nullptr, "SetSystemProgramIdentification"}, | ||
| 58 | {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ | ||
| 59 | {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 60 | {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 61 | {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 | ||
| 62 | {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ | ||
| 63 | {120, nullptr, "GetNintendoAccountId"}, | ||
| 64 | {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ | ||
| 65 | {130, nullptr, "GetNintendoAccountUserResourceCache"}, | ||
| 66 | {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, | ||
| 67 | {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, | ||
| 68 | {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ | ||
| 69 | {134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+ | ||
| 70 | {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ | ||
| 71 | {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ | ||
| 72 | {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ | ||
| 73 | {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ | ||
| 74 | {150, nullptr, "CreateAuthorizationRequest"}, | ||
| 75 | }; | ||
| 76 | // clang-format on | ||
| 77 | |||
| 78 | RegisterHandlers(functions); | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | |||
| 82 | // 3.0.0+ | ||
| 83 | class IFloatingRegistrationRequest final : public ServiceFramework<IFloatingRegistrationRequest> { | ||
| 84 | public: | ||
| 85 | explicit IFloatingRegistrationRequest(Common::UUID user_id) | ||
| 86 | : ServiceFramework("IFloatingRegistrationRequest") { | ||
| 87 | // clang-format off | ||
| 88 | static const FunctionInfo functions[] = { | ||
| 89 | {0, nullptr, "GetSessionId"}, | ||
| 90 | {12, nullptr, "GetAccountId"}, | ||
| 91 | {13, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 92 | {14, nullptr, "GetNickname"}, | ||
| 93 | {15, nullptr, "GetProfileImage"}, | ||
| 94 | {21, nullptr, "LoadIdTokenCache"}, | ||
| 95 | {100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync | ||
| 96 | {101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync | ||
| 97 | {102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+ | ||
| 98 | {103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+ | ||
| 99 | {110, nullptr, "SetSystemProgramIdentification"}, | ||
| 100 | {111, nullptr, "EnsureIdTokenCacheAsync"}, | ||
| 101 | }; | ||
| 102 | // clang-format on | ||
| 103 | |||
| 104 | RegisterHandlers(functions); | ||
| 105 | } | ||
| 106 | }; | ||
| 107 | |||
| 108 | class IAdministrator final : public ServiceFramework<IAdministrator> { | ||
| 109 | public: | ||
| 110 | explicit IAdministrator(Common::UUID user_id) : ServiceFramework("IAdministrator") { | ||
| 111 | // clang-format off | ||
| 112 | static const FunctionInfo functions[] = { | ||
| 113 | {0, nullptr, "CheckAvailability"}, | ||
| 114 | {1, nullptr, "GetAccountId"}, | ||
| 115 | {2, nullptr, "EnsureIdTokenCacheAsync"}, | ||
| 116 | {3, nullptr, "LoadIdTokenCache"}, | ||
| 117 | {100, nullptr, "SetSystemProgramIdentification"}, | ||
| 118 | {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ | ||
| 119 | {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 120 | {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 121 | {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 | ||
| 122 | {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ | ||
| 123 | {120, nullptr, "GetNintendoAccountId"}, | ||
| 124 | {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ | ||
| 125 | {130, nullptr, "GetNintendoAccountUserResourceCache"}, | ||
| 126 | {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, | ||
| 127 | {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, | ||
| 128 | {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ | ||
| 129 | {134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+ | ||
| 130 | {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ | ||
| 131 | {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ | ||
| 132 | {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ | ||
| 133 | {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ | ||
| 134 | {150, nullptr, "CreateAuthorizationRequest"}, | ||
| 135 | {200, nullptr, "IsRegistered"}, | ||
| 136 | {201, nullptr, "RegisterAsync"}, | ||
| 137 | {202, nullptr, "UnregisterAsync"}, | ||
| 138 | {203, nullptr, "DeleteRegistrationInfoLocally"}, | ||
| 139 | {220, nullptr, "SynchronizeProfileAsync"}, | ||
| 140 | {221, nullptr, "UploadProfileAsync"}, | ||
| 141 | {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"}, | ||
| 142 | {250, nullptr, "IsLinkedWithNintendoAccount"}, | ||
| 143 | {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"}, | ||
| 144 | {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"}, | ||
| 145 | {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"}, | ||
| 146 | {256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"}, | ||
| 147 | {260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ | ||
| 148 | {261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ | ||
| 149 | {280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"}, | ||
| 150 | {290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+ | ||
| 151 | {300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+ | ||
| 152 | {400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+ | ||
| 153 | {401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+ | ||
| 154 | {900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+ | ||
| 155 | {901, nullptr, "ImportAsyncForWin"}, // 9.0.0+ | ||
| 156 | {997, nullptr, "DebugUnlinkNintendoAccountAsync"}, | ||
| 157 | {998, nullptr, "DebugSetAvailabilityErrorDetail"}, | ||
| 158 | }; | ||
| 159 | // clang-format on | ||
| 160 | |||
| 161 | RegisterHandlers(functions); | ||
| 162 | } | ||
| 163 | }; | ||
| 164 | |||
| 165 | class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> { | ||
| 166 | public: | ||
| 167 | explicit IAuthorizationRequest(Common::UUID user_id) | ||
| 168 | : ServiceFramework("IAuthorizationRequest") { | ||
| 169 | // clang-format off | ||
| 170 | static const FunctionInfo functions[] = { | ||
| 171 | {0, nullptr, "GetSessionId"}, | ||
| 172 | {10, nullptr, "InvokeWithoutInteractionAsync"}, | ||
| 173 | {19, nullptr, "IsAuthorized"}, | ||
| 174 | {20, nullptr, "GetAuthorizationCode"}, | ||
| 175 | {21, nullptr, "GetIdToken"}, | ||
| 176 | {22, nullptr, "GetState"}, | ||
| 177 | }; | ||
| 178 | // clang-format on | ||
| 179 | |||
| 180 | RegisterHandlers(functions); | ||
| 181 | } | ||
| 182 | }; | ||
| 183 | |||
| 184 | class IOAuthProcedure final : public ServiceFramework<IOAuthProcedure> { | ||
| 185 | public: | ||
| 186 | explicit IOAuthProcedure(Common::UUID user_id) : ServiceFramework("IOAuthProcedure") { | ||
| 187 | // clang-format off | ||
| 188 | static const FunctionInfo functions[] = { | ||
| 189 | {0, nullptr, "PrepareAsync"}, | ||
| 190 | {1, nullptr, "GetRequest"}, | ||
| 191 | {2, nullptr, "ApplyResponse"}, | ||
| 192 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 193 | {10, nullptr, "Suspend"}, | ||
| 194 | }; | ||
| 195 | // clang-format on | ||
| 196 | |||
| 197 | RegisterHandlers(functions); | ||
| 198 | } | ||
| 199 | }; | ||
| 200 | |||
| 201 | // 3.0.0+ | ||
| 202 | class IOAuthProcedureForExternalNsa final : public ServiceFramework<IOAuthProcedureForExternalNsa> { | ||
| 203 | public: | ||
| 204 | explicit IOAuthProcedureForExternalNsa(Common::UUID user_id) | ||
| 205 | : ServiceFramework("IOAuthProcedureForExternalNsa") { | ||
| 206 | // clang-format off | ||
| 207 | static const FunctionInfo functions[] = { | ||
| 208 | {0, nullptr, "PrepareAsync"}, | ||
| 209 | {1, nullptr, "GetRequest"}, | ||
| 210 | {2, nullptr, "ApplyResponse"}, | ||
| 211 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 212 | {10, nullptr, "Suspend"}, | ||
| 213 | {100, nullptr, "GetAccountId"}, | ||
| 214 | {101, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 215 | {102, nullptr, "GetNickname"}, | ||
| 216 | {103, nullptr, "GetProfileImage"}, | ||
| 217 | }; | ||
| 218 | // clang-format on | ||
| 219 | |||
| 220 | RegisterHandlers(functions); | ||
| 221 | } | ||
| 222 | }; | ||
| 223 | |||
| 224 | class IOAuthProcedureForNintendoAccountLinkage final | ||
| 225 | : public ServiceFramework<IOAuthProcedureForNintendoAccountLinkage> { | ||
| 226 | public: | ||
| 227 | explicit IOAuthProcedureForNintendoAccountLinkage(Common::UUID user_id) | ||
| 228 | : ServiceFramework("IOAuthProcedureForNintendoAccountLinkage") { | ||
| 229 | // clang-format off | ||
| 230 | static const FunctionInfo functions[] = { | ||
| 231 | {0, nullptr, "PrepareAsync"}, | ||
| 232 | {1, nullptr, "GetRequest"}, | ||
| 233 | {2, nullptr, "ApplyResponse"}, | ||
| 234 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 235 | {10, nullptr, "Suspend"}, | ||
| 236 | {100, nullptr, "GetRequestWithTheme"}, | ||
| 237 | {101, nullptr, "IsNetworkServiceAccountReplaced"}, | ||
| 238 | {199, nullptr, "GetUrlForIntroductionOfExtraMembership"}, // 2.0.0 - 5.1.0 | ||
| 239 | }; | ||
| 240 | // clang-format on | ||
| 241 | |||
| 242 | RegisterHandlers(functions); | ||
| 243 | } | ||
| 244 | }; | ||
| 245 | |||
| 246 | class INotifier final : public ServiceFramework<INotifier> { | ||
| 247 | public: | ||
| 248 | explicit INotifier(Common::UUID user_id) : ServiceFramework("INotifier") { | ||
| 249 | // clang-format off | ||
| 250 | static const FunctionInfo functions[] = { | ||
| 251 | {0, nullptr, "GetSystemEvent"}, | ||
| 252 | }; | ||
| 253 | // clang-format on | ||
| 254 | |||
| 255 | RegisterHandlers(functions); | ||
| 256 | } | ||
| 257 | }; | ||
| 258 | |||
| 47 | class IProfileCommon : public ServiceFramework<IProfileCommon> { | 259 | class IProfileCommon : public ServiceFramework<IProfileCommon> { |
| 48 | public: | 260 | public: |
| 49 | explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id, | 261 | explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id, |
| @@ -226,6 +438,54 @@ public: | |||
| 226 | : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {} | 438 | : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {} |
| 227 | }; | 439 | }; |
| 228 | 440 | ||
| 441 | class IAsyncContext final : public ServiceFramework<IAsyncContext> { | ||
| 442 | public: | ||
| 443 | explicit IAsyncContext(Common::UUID user_id) : ServiceFramework("IAsyncContext") { | ||
| 444 | // clang-format off | ||
| 445 | static const FunctionInfo functions[] = { | ||
| 446 | {0, nullptr, "GetSystemEvent"}, | ||
| 447 | {1, nullptr, "Cancel"}, | ||
| 448 | {2, nullptr, "HasDone"}, | ||
| 449 | {3, nullptr, "GetResult"}, | ||
| 450 | }; | ||
| 451 | // clang-format on | ||
| 452 | |||
| 453 | RegisterHandlers(functions); | ||
| 454 | } | ||
| 455 | }; | ||
| 456 | |||
| 457 | class ISessionObject final : public ServiceFramework<ISessionObject> { | ||
| 458 | public: | ||
| 459 | explicit ISessionObject(Common::UUID user_id) : ServiceFramework("ISessionObject") { | ||
| 460 | // clang-format off | ||
| 461 | static const FunctionInfo functions[] = { | ||
| 462 | {999, nullptr, "Dummy"}, | ||
| 463 | }; | ||
| 464 | // clang-format on | ||
| 465 | |||
| 466 | RegisterHandlers(functions); | ||
| 467 | } | ||
| 468 | }; | ||
| 469 | |||
| 470 | class IGuestLoginRequest final : public ServiceFramework<IGuestLoginRequest> { | ||
| 471 | public: | ||
| 472 | explicit IGuestLoginRequest(Common::UUID) : ServiceFramework("IGuestLoginRequest") { | ||
| 473 | // clang-format off | ||
| 474 | static const FunctionInfo functions[] = { | ||
| 475 | {0, nullptr, "GetSessionId"}, | ||
| 476 | {11, nullptr, "Unknown"}, // 1.0.0 - 2.3.0 (the name is blank on Switchbrew) | ||
| 477 | {12, nullptr, "GetAccountId"}, | ||
| 478 | {13, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 479 | {14, nullptr, "GetNickname"}, | ||
| 480 | {15, nullptr, "GetProfileImage"}, | ||
| 481 | {21, nullptr, "LoadIdTokenCache"}, // 3.0.0+ | ||
| 482 | }; | ||
| 483 | // clang-format on | ||
| 484 | |||
| 485 | RegisterHandlers(functions); | ||
| 486 | } | ||
| 487 | }; | ||
| 488 | |||
| 229 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { | 489 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { |
| 230 | public: | 490 | public: |
| 231 | explicit IManagerForApplication(Common::UUID user_id) | 491 | explicit IManagerForApplication(Common::UUID user_id) |
| @@ -265,6 +525,87 @@ private: | |||
| 265 | Common::UUID user_id; | 525 | Common::UUID user_id; |
| 266 | }; | 526 | }; |
| 267 | 527 | ||
| 528 | // 6.0.0+ | ||
| 529 | class IAsyncNetworkServiceLicenseKindContext final | ||
| 530 | : public ServiceFramework<IAsyncNetworkServiceLicenseKindContext> { | ||
| 531 | public: | ||
| 532 | explicit IAsyncNetworkServiceLicenseKindContext(Common::UUID user_id) | ||
| 533 | : ServiceFramework("IAsyncNetworkServiceLicenseKindContext") { | ||
| 534 | // clang-format off | ||
| 535 | static const FunctionInfo functions[] = { | ||
| 536 | {0, nullptr, "GetSystemEvent"}, | ||
| 537 | {1, nullptr, "Cancel"}, | ||
| 538 | {2, nullptr, "HasDone"}, | ||
| 539 | {3, nullptr, "GetResult"}, | ||
| 540 | {4, nullptr, "GetNetworkServiceLicenseKind"}, | ||
| 541 | }; | ||
| 542 | // clang-format on | ||
| 543 | |||
| 544 | RegisterHandlers(functions); | ||
| 545 | } | ||
| 546 | }; | ||
| 547 | |||
| 548 | // 8.0.0+ | ||
| 549 | class IOAuthProcedureForUserRegistration final | ||
| 550 | : public ServiceFramework<IOAuthProcedureForUserRegistration> { | ||
| 551 | public: | ||
| 552 | explicit IOAuthProcedureForUserRegistration(Common::UUID user_id) | ||
| 553 | : ServiceFramework("IOAuthProcedureForUserRegistration") { | ||
| 554 | // clang-format off | ||
| 555 | static const FunctionInfo functions[] = { | ||
| 556 | {0, nullptr, "PrepareAsync"}, | ||
| 557 | {1, nullptr, "GetRequest"}, | ||
| 558 | {2, nullptr, "ApplyResponse"}, | ||
| 559 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 560 | {10, nullptr, "Suspend"}, | ||
| 561 | {100, nullptr, "GetAccountId"}, | ||
| 562 | {101, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 563 | {102, nullptr, "GetNickname"}, | ||
| 564 | {103, nullptr, "GetProfileImage"}, | ||
| 565 | {110, nullptr, "RegisterUserAsync"}, | ||
| 566 | {111, nullptr, "GetUid"}, | ||
| 567 | }; | ||
| 568 | // clang-format on | ||
| 569 | |||
| 570 | RegisterHandlers(functions); | ||
| 571 | } | ||
| 572 | }; | ||
| 573 | |||
| 574 | class DAUTH_O final : public ServiceFramework<DAUTH_O> { | ||
| 575 | public: | ||
| 576 | explicit DAUTH_O(Common::UUID) : ServiceFramework("dauth:o") { | ||
| 577 | // clang-format off | ||
| 578 | static const FunctionInfo functions[] = { | ||
| 579 | {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, // [5.0.0-5.1.0] GeneratePostData | ||
| 580 | {1, nullptr, "LoadAuthenticationTokenCache"}, // 6.0.0+ | ||
| 581 | {2, nullptr, "InvalidateAuthenticationTokenCache"}, // 6.0.0+ | ||
| 582 | {10, nullptr, "EnsureEdgeTokenCacheAsync"}, // 6.0.0+ | ||
| 583 | {11, nullptr, "LoadEdgeTokenCache"}, // 6.0.0+ | ||
| 584 | {12, nullptr, "InvalidateEdgeTokenCache"}, // 6.0.0+ | ||
| 585 | }; | ||
| 586 | // clang-format on | ||
| 587 | |||
| 588 | RegisterHandlers(functions); | ||
| 589 | } | ||
| 590 | }; | ||
| 591 | |||
| 592 | // 6.0.0+ | ||
| 593 | class IAsyncResult final : public ServiceFramework<IAsyncResult> { | ||
| 594 | public: | ||
| 595 | explicit IAsyncResult(Common::UUID user_id) : ServiceFramework("IAsyncResult") { | ||
| 596 | // clang-format off | ||
| 597 | static const FunctionInfo functions[] = { | ||
| 598 | {0, nullptr, "GetResult"}, | ||
| 599 | {1, nullptr, "Cancel"}, | ||
| 600 | {2, nullptr, "IsAvailable"}, | ||
| 601 | {3, nullptr, "GetSystemEvent"}, | ||
| 602 | }; | ||
| 603 | // clang-format on | ||
| 604 | |||
| 605 | RegisterHandlers(functions); | ||
| 606 | } | ||
| 607 | }; | ||
| 608 | |||
| 268 | void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { | 609 | void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { |
| 269 | LOG_DEBUG(Service_ACC, "called"); | 610 | LOG_DEBUG(Service_ACC, "called"); |
| 270 | IPC::ResponseBuilder rb{ctx, 3}; | 611 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -435,6 +776,15 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { | |||
| 435 | rb.Push(RESULT_SUCCESS); | 776 | rb.Push(RESULT_SUCCESS); |
| 436 | } | 777 | } |
| 437 | 778 | ||
| 779 | void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { | ||
| 780 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 781 | |||
| 782 | // TODO(ogniK): Handle open contexts | ||
| 783 | ctx.WriteBuffer(profile_manager->GetOpenUsers()); | ||
| 784 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 785 | rb.Push(RESULT_SUCCESS); | ||
| 786 | } | ||
| 787 | |||
| 438 | void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { | 788 | void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { |
| 439 | LOG_DEBUG(Service_ACC, "called"); | 789 | LOG_DEBUG(Service_ACC, "called"); |
| 440 | // A u8 is passed into this function which we can safely ignore. It's to determine if we have | 790 | // A u8 is passed into this function which we can safely ignore. It's to determine if we have |
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 74ca39d6e..d4c6395c6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h | |||
| @@ -34,6 +34,7 @@ public: | |||
| 34 | void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); | 34 | void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); |
| 35 | void GetProfileEditor(Kernel::HLERequestContext& ctx); | 35 | void GetProfileEditor(Kernel::HLERequestContext& ctx); |
| 36 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); | 36 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); |
| 37 | void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); | ||
| 37 | 38 | ||
| 38 | private: | 39 | private: |
| 39 | ResultCode InitializeApplicationInfoBase(); | 40 | ResultCode InitializeApplicationInfoBase(); |
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index 3bac6bcd1..51f119b12 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp | |||
| @@ -13,8 +13,8 @@ ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 13 | {0, nullptr, "EnsureCacheAsync"}, | 13 | {0, nullptr, "EnsureCacheAsync"}, |
| 14 | {1, nullptr, "LoadCache"}, | 14 | {1, nullptr, "LoadCache"}, |
| 15 | {2, nullptr, "GetDeviceAccountId"}, | 15 | {2, nullptr, "GetDeviceAccountId"}, |
| 16 | {50, nullptr, "RegisterNotificationTokenAsync"}, | 16 | {50, nullptr, "RegisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 |
| 17 | {51, nullptr, "UnregisterNotificationTokenAsync"}, | 17 | {51, nullptr, "UnregisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 |
| 18 | }; | 18 | }; |
| 19 | RegisterHandlers(functions); | 19 | RegisterHandlers(functions); |
| 20 | } | 20 | } |
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 2eefc6df5..d2bb8c2c8 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp | |||
| @@ -17,28 +17,28 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 17 | {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, | 17 | {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, |
| 18 | {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, | 18 | {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, |
| 19 | {5, &ACC_SU::GetProfile, "GetProfile"}, | 19 | {5, &ACC_SU::GetProfile, "GetProfile"}, |
| 20 | {6, nullptr, "GetProfileDigest"}, | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, | 23 | {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, nullptr, "GetUserRegistrationNotifier"}, | 25 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 26 | {101, nullptr, "GetUserStateChangeNotifier"}, | 26 | {101, nullptr, "GetUserStateChangeNotifier"}, |
| 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, | 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, |
| 28 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, | 28 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, |
| 29 | {104, nullptr, "GetProfileUpdateNotifier"}, | 29 | {104, nullptr, "GetProfileUpdateNotifier"}, |
| 30 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, | 30 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ |
| 31 | {106, nullptr, "GetProfileSyncNotifier"}, | 31 | {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ |
| 32 | {110, nullptr, "StoreSaveDataThumbnail"}, | 32 | {110, nullptr, "StoreSaveDataThumbnail"}, |
| 33 | {111, nullptr, "ClearSaveDataThumbnail"}, | 33 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 34 | {112, nullptr, "LoadSaveDataThumbnail"}, | 34 | {112, nullptr, "LoadSaveDataThumbnail"}, |
| 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ |
| 36 | {120, nullptr, "ListOpenUsersInApplication"}, | 36 | {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ |
| 37 | {130, nullptr, "ActivateOpenContextRetention"}, | 37 | {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ |
| 38 | {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, | 38 | {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
| 39 | {150, nullptr, "AuthenticateApplicationAsync"}, | 39 | {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ |
| 40 | {190, nullptr, "GetUserLastOpenedApplication"}, | 40 | {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 |
| 41 | {191, nullptr, "ActivateOpenContextHolder"}, | 41 | {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ |
| 42 | {200, nullptr, "BeginUserRegistration"}, | 42 | {200, nullptr, "BeginUserRegistration"}, |
| 43 | {201, nullptr, "CompleteUserRegistration"}, | 43 | {201, nullptr, "CompleteUserRegistration"}, |
| 44 | {202, nullptr, "CancelUserRegistration"}, | 44 | {202, nullptr, "CancelUserRegistration"}, |
| @@ -46,15 +46,15 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 46 | {204, nullptr, "SetUserPosition"}, | 46 | {204, nullptr, "SetUserPosition"}, |
| 47 | {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, | 47 | {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, |
| 48 | {206, nullptr, "CompleteUserRegistrationForcibly"}, | 48 | {206, nullptr, "CompleteUserRegistrationForcibly"}, |
| 49 | {210, nullptr, "CreateFloatingRegistrationRequest"}, | 49 | {210, nullptr, "CreateFloatingRegistrationRequest"}, // 3.0.0+ |
| 50 | {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, | 50 | {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ |
| 51 | {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, | 51 | {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ |
| 52 | {230, nullptr, "AuthenticateServiceAsync"}, | 52 | {230, nullptr, "AuthenticateServiceAsync"}, |
| 53 | {250, nullptr, "GetBaasAccountAdministrator"}, | 53 | {250, nullptr, "GetBaasAccountAdministrator"}, |
| 54 | {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, | 54 | {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, |
| 55 | {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, | 55 | {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, // 3.0.0+ |
| 56 | {299, nullptr, "SuspendBackgroundDaemon"}, | 56 | {299, nullptr, "SuspendBackgroundDaemon"}, |
| 57 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, | 57 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ |
| 58 | {998, nullptr, "DebugSetUserStateClose"}, | 58 | {998, nullptr, "DebugSetUserStateClose"}, |
| 59 | {999, nullptr, "DebugSetUserStateOpen"}, | 59 | {999, nullptr, "DebugSetUserStateOpen"}, |
| 60 | }; | 60 | }; |
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index fb4e7e772..cb44e06b7 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp | |||
| @@ -17,23 +17,23 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 17 | {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, | 17 | {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, |
| 18 | {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, | 18 | {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, |
| 19 | {5, &ACC_U0::GetProfile, "GetProfile"}, | 19 | {5, &ACC_U0::GetProfile, "GetProfile"}, |
| 20 | {6, nullptr, "GetProfileDigest"}, | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, | 23 | {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, | 25 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, |
| 26 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, | 26 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, |
| 27 | {102, nullptr, "AuthenticateApplicationAsync"}, | 27 | {102, nullptr, "AuthenticateApplicationAsync"}, |
| 28 | {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, | 28 | {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ |
| 29 | {110, nullptr, "StoreSaveDataThumbnail"}, | 29 | {110, nullptr, "StoreSaveDataThumbnail"}, |
| 30 | {111, nullptr, "ClearSaveDataThumbnail"}, | 30 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 31 | {120, nullptr, "CreateGuestLoginRequest"}, | 31 | {120, nullptr, "CreateGuestLoginRequest"}, |
| 32 | {130, nullptr, "LoadOpenContext"}, | 32 | {130, nullptr, "LoadOpenContext"}, // 5.0.0+ |
| 33 | {131, nullptr, "ListOpenContextStoredUsers"}, | 33 | {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ |
| 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, | 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ |
| 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, | 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
| 36 | {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, | 36 | {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ |
| 37 | }; | 37 | }; |
| 38 | // clang-format on | 38 | // clang-format on |
| 39 | 39 | ||
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 9f29cdc82..a4aa5316a 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp | |||
| @@ -17,28 +17,29 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 17 | {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, | 17 | {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, |
| 18 | {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, | 18 | {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, |
| 19 | {5, &ACC_U1::GetProfile, "GetProfile"}, | 19 | {5, &ACC_U1::GetProfile, "GetProfile"}, |
| 20 | {6, nullptr, "GetProfileDigest"}, | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, | 23 | {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, nullptr, "GetUserRegistrationNotifier"}, | 25 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 26 | {101, nullptr, "GetUserStateChangeNotifier"}, | 26 | {101, nullptr, "GetUserStateChangeNotifier"}, |
| 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, | 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, |
| 28 | {103, nullptr, "GetProfileUpdateNotifier"}, | 28 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, |
| 29 | {104, nullptr, "CheckNetworkServiceAvailabilityAsync"}, | 29 | {104, nullptr, "GetProfileUpdateNotifier"}, |
| 30 | {105, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, | 30 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ |
| 31 | {106, nullptr, "GetProfileSyncNotifier"}, | 31 | {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ |
| 32 | {110, nullptr, "StoreSaveDataThumbnail"}, | 32 | {110, nullptr, "StoreSaveDataThumbnail"}, |
| 33 | {111, nullptr, "ClearSaveDataThumbnail"}, | 33 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 34 | {112, nullptr, "LoadSaveDataThumbnail"}, | 34 | {112, nullptr, "LoadSaveDataThumbnail"}, |
| 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ |
| 36 | {130, nullptr, "ActivateOpenContextRetention"}, | 36 | {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ |
| 37 | {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, | 37 | {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ |
| 38 | {150, nullptr, "AuthenticateApplicationAsync"}, | 38 | {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
| 39 | {190, nullptr, "GetUserLastOpenedApplication"}, | 39 | {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ |
| 40 | {191, nullptr, "ActivateOpenContextHolder"}, | 40 | {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 |
| 41 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, | 41 | {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ |
| 42 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ | ||
| 42 | {998, nullptr, "DebugSetUserStateClose"}, | 43 | {998, nullptr, "DebugSetUserStateClose"}, |
| 43 | {999, nullptr, "DebugSetUserStateOpen"}, | 44 | {999, nullptr, "DebugSetUserStateOpen"}, |
| 44 | }; | 45 | }; |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4df74c4f9..256449aa7 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/file_sys/control_metadata.h" | 11 | #include "core/file_sys/control_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | 12 | #include "core/file_sys/patch_manager.h" |
| 13 | #include "core/file_sys/registered_cache.h" | ||
| 13 | #include "core/file_sys/savedata_factory.h" | 14 | #include "core/file_sys/savedata_factory.h" |
| 14 | #include "core/hle/ipc_helpers.h" | 15 | #include "core/hle/ipc_helpers.h" |
| 15 | #include "core/hle/kernel/kernel.h" | 16 | #include "core/hle/kernel/kernel.h" |
| @@ -68,6 +69,7 @@ IWindowController::IWindowController(Core::System& system_) | |||
| 68 | static const FunctionInfo functions[] = { | 69 | static const FunctionInfo functions[] = { |
| 69 | {0, nullptr, "CreateWindow"}, | 70 | {0, nullptr, "CreateWindow"}, |
| 70 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, | 71 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, |
| 72 | {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, | ||
| 71 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, | 73 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, |
| 72 | {11, nullptr, "ReleaseForegroundRights"}, | 74 | {11, nullptr, "ReleaseForegroundRights"}, |
| 73 | {12, nullptr, "RejectToChangeIntoBackground"}, | 75 | {12, nullptr, "RejectToChangeIntoBackground"}, |
| @@ -189,8 +191,8 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" | |||
| 189 | {5, nullptr, "GetLastForegroundCaptureImageEx"}, | 191 | {5, nullptr, "GetLastForegroundCaptureImageEx"}, |
| 190 | {6, nullptr, "GetLastApplicationCaptureImageEx"}, | 192 | {6, nullptr, "GetLastApplicationCaptureImageEx"}, |
| 191 | {7, nullptr, "GetCallerAppletCaptureImageEx"}, | 193 | {7, nullptr, "GetCallerAppletCaptureImageEx"}, |
| 192 | {8, nullptr, "TakeScreenShotOfOwnLayer"}, // 2.0.0+ | 194 | {8, nullptr, "TakeScreenShotOfOwnLayer"}, |
| 193 | {9, nullptr, "CopyBetweenCaptureBuffers"}, // 5.0.0+ | 195 | {9, nullptr, "CopyBetweenCaptureBuffers"}, |
| 194 | {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, | 196 | {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, |
| 195 | {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, | 197 | {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, |
| 196 | {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, | 198 | {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, |
| @@ -200,17 +202,14 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" | |||
| 200 | {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, | 202 | {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, |
| 201 | {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, | 203 | {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, |
| 202 | {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, | 204 | {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, |
| 203 | // 2.0.0+ | ||
| 204 | {20, nullptr, "ClearCaptureBuffer"}, | 205 | {20, nullptr, "ClearCaptureBuffer"}, |
| 205 | {21, nullptr, "ClearAppletTransitionBuffer"}, | 206 | {21, nullptr, "ClearAppletTransitionBuffer"}, |
| 206 | // 4.0.0+ | ||
| 207 | {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, | 207 | {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, |
| 208 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, | 208 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, |
| 209 | {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, | 209 | {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, |
| 210 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, | 210 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, |
| 211 | {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, | 211 | {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, |
| 212 | {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, | 212 | {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, |
| 213 | // 6.0.0+ | ||
| 214 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, | 213 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, |
| 215 | }; | 214 | }; |
| 216 | // clang-format on | 215 | // clang-format on |
| @@ -225,7 +224,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { | |||
| 225 | static const FunctionInfo functions[] = { | 224 | static const FunctionInfo functions[] = { |
| 226 | {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, | 225 | {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, |
| 227 | {1, nullptr, "OpenMainApplication"}, | 226 | {1, nullptr, "OpenMainApplication"}, |
| 228 | {10, nullptr, "EmulateButtonEvent"}, | 227 | {10, nullptr, "PerformSystemButtonPressing"}, |
| 229 | {20, nullptr, "InvalidateTransitionLayer"}, | 228 | {20, nullptr, "InvalidateTransitionLayer"}, |
| 230 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, | 229 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, |
| 231 | {40, nullptr, "GetAppletResourceUsageInfo"}, | 230 | {40, nullptr, "GetAppletResourceUsageInfo"}, |
| @@ -267,13 +266,13 @@ ISelfController::ISelfController(Core::System& system, | |||
| 267 | {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, | 266 | {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, |
| 268 | {17, nullptr, "SetControllerFirmwareUpdateSection"}, | 267 | {17, nullptr, "SetControllerFirmwareUpdateSection"}, |
| 269 | {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, | 268 | {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, |
| 270 | {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"}, | 269 | {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"}, |
| 271 | {20, nullptr, "SetDesirableKeyboardLayout"}, | 270 | {20, nullptr, "SetDesirableKeyboardLayout"}, |
| 272 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, | 271 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, |
| 273 | {41, nullptr, "IsSystemBufferSharingEnabled"}, | 272 | {41, nullptr, "IsSystemBufferSharingEnabled"}, |
| 274 | {42, nullptr, "GetSystemSharedLayerHandle"}, | 273 | {42, nullptr, "GetSystemSharedLayerHandle"}, |
| 275 | {43, nullptr, "GetSystemSharedBufferHandle"}, | 274 | {43, nullptr, "GetSystemSharedBufferHandle"}, |
| 276 | {44, nullptr, "CreateManagedDisplaySeparableLayer"}, | 275 | {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"}, |
| 277 | {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, | 276 | {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, |
| 278 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, | 277 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, |
| 279 | {51, nullptr, "ApproveToDisplay"}, | 278 | {51, nullptr, "ApproveToDisplay"}, |
| @@ -443,7 +442,7 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& | |||
| 443 | rb.Push(RESULT_SUCCESS); | 442 | rb.Push(RESULT_SUCCESS); |
| 444 | } | 443 | } |
| 445 | 444 | ||
| 446 | void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { | 445 | void ISelfController::SetAlbumImageOrientation(Kernel::HLERequestContext& ctx) { |
| 447 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 446 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 448 | 447 | ||
| 449 | IPC::ResponseBuilder rb{ctx, 2}; | 448 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -463,6 +462,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) | |||
| 463 | rb.Push(*layer_id); | 462 | rb.Push(*layer_id); |
| 464 | } | 463 | } |
| 465 | 464 | ||
| 465 | void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) { | ||
| 466 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 467 | |||
| 468 | // TODO(Subv): Find out how AM determines the display to use, for now just | ||
| 469 | // create the layer in the Default display. | ||
| 470 | // This calls nn::vi::CreateRecordingLayer() which creates another layer. | ||
| 471 | // Currently we do not support more than 1 layer per display, output 1 layer id for now. | ||
| 472 | // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse | ||
| 473 | // side effects. | ||
| 474 | // TODO: Support multiple layers | ||
| 475 | const auto display_id = nvflinger->OpenDisplay("Default"); | ||
| 476 | const auto layer_id = nvflinger->CreateLayer(*display_id); | ||
| 477 | |||
| 478 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 479 | rb.Push(RESULT_SUCCESS); | ||
| 480 | rb.Push(*layer_id); | ||
| 481 | } | ||
| 482 | |||
| 466 | void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) { | 483 | void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) { |
| 467 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 484 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 468 | 485 | ||
| @@ -607,6 +624,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system, | |||
| 607 | {20, nullptr, "PushToGeneralChannel"}, | 624 | {20, nullptr, "PushToGeneralChannel"}, |
| 608 | {30, nullptr, "GetHomeButtonReaderLockAccessor"}, | 625 | {30, nullptr, "GetHomeButtonReaderLockAccessor"}, |
| 609 | {31, nullptr, "GetReaderLockAccessorEx"}, | 626 | {31, nullptr, "GetReaderLockAccessorEx"}, |
| 627 | {32, nullptr, "GetWriterLockAccessorEx"}, | ||
| 610 | {40, nullptr, "GetCradleFwVersion"}, | 628 | {40, nullptr, "GetCradleFwVersion"}, |
| 611 | {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, | 629 | {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, |
| 612 | {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, | 630 | {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, |
| @@ -731,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& | |||
| 731 | 749 | ||
| 732 | if (Settings::values.use_docked_mode) { | 750 | if (Settings::values.use_docked_mode) { |
| 733 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * | 751 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * |
| 734 | static_cast<u32>(Settings::values.resolution_factor)); | 752 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 735 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * | 753 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * |
| 736 | static_cast<u32>(Settings::values.resolution_factor)); | 754 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 737 | } else { | 755 | } else { |
| 738 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * | 756 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * |
| 739 | static_cast<u32>(Settings::values.resolution_factor)); | 757 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 740 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * | 758 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * |
| 741 | static_cast<u32>(Settings::values.resolution_factor)); | 759 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 742 | } | 760 | } |
| 743 | } | 761 | } |
| 744 | 762 | ||
| @@ -842,7 +860,7 @@ public: | |||
| 842 | {110, nullptr, "NeedsToExitProcess"}, | 860 | {110, nullptr, "NeedsToExitProcess"}, |
| 843 | {120, nullptr, "GetLibraryAppletInfo"}, | 861 | {120, nullptr, "GetLibraryAppletInfo"}, |
| 844 | {150, nullptr, "RequestForAppletToGetForeground"}, | 862 | {150, nullptr, "RequestForAppletToGetForeground"}, |
| 845 | {160, nullptr, "GetIndirectLayerConsumerHandle"}, | 863 | {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"}, |
| 846 | }; | 864 | }; |
| 847 | // clang-format on | 865 | // clang-format on |
| 848 | 866 | ||
| @@ -961,6 +979,18 @@ private: | |||
| 961 | rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); | 979 | rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); |
| 962 | } | 980 | } |
| 963 | 981 | ||
| 982 | void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) { | ||
| 983 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 984 | |||
| 985 | // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is | ||
| 986 | // actually used anywhere | ||
| 987 | constexpr u64 handle = 0xdeadbeef; | ||
| 988 | |||
| 989 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 990 | rb.Push(RESULT_SUCCESS); | ||
| 991 | rb.Push(handle); | ||
| 992 | } | ||
| 993 | |||
| 964 | std::shared_ptr<Applets::Applet> applet; | 994 | std::shared_ptr<Applets::Applet> applet; |
| 965 | }; | 995 | }; |
| 966 | 996 | ||
| @@ -1132,6 +1162,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1132 | {24, nullptr, "GetLaunchStorageInfoForDebug"}, | 1162 | {24, nullptr, "GetLaunchStorageInfoForDebug"}, |
| 1133 | {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, | 1163 | {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, |
| 1134 | {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, | 1164 | {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, |
| 1165 | {27, nullptr, "CreateCacheStorage"}, | ||
| 1135 | {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, | 1166 | {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, |
| 1136 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, | 1167 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, |
| 1137 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, | 1168 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, |
| @@ -1157,6 +1188,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1157 | {120, nullptr, "ExecuteProgram"}, | 1188 | {120, nullptr, "ExecuteProgram"}, |
| 1158 | {121, nullptr, "ClearUserChannel"}, | 1189 | {121, nullptr, "ClearUserChannel"}, |
| 1159 | {122, nullptr, "UnpopToUserChannel"}, | 1190 | {122, nullptr, "UnpopToUserChannel"}, |
| 1191 | {123, nullptr, "GetPreviousProgramIndex"}, | ||
| 1192 | {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, | ||
| 1160 | {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, | 1193 | {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, |
| 1161 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, | 1194 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, |
| 1162 | {141, nullptr, "TryPopFromFriendInvitationStorageChannel"}, | 1195 | {141, nullptr, "TryPopFromFriendInvitationStorageChannel"}, |
| @@ -1339,14 +1372,25 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) { | |||
| 1339 | 1372 | ||
| 1340 | std::array<u8, 0x10> version_string{}; | 1373 | std::array<u8, 0x10> version_string{}; |
| 1341 | 1374 | ||
| 1342 | FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; | 1375 | const auto res = [this] { |
| 1343 | const auto res = pm.GetControlMetadata(); | 1376 | const auto title_id = system.CurrentProcess()->GetTitleID(); |
| 1377 | |||
| 1378 | FileSys::PatchManager pm{title_id}; | ||
| 1379 | auto res = pm.GetControlMetadata(); | ||
| 1380 | if (res.first != nullptr) { | ||
| 1381 | return res; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; | ||
| 1385 | return pm_update.GetControlMetadata(); | ||
| 1386 | }(); | ||
| 1387 | |||
| 1344 | if (res.first != nullptr) { | 1388 | if (res.first != nullptr) { |
| 1345 | const auto& version = res.first->GetVersionString(); | 1389 | const auto& version = res.first->GetVersionString(); |
| 1346 | std::copy(version.begin(), version.end(), version_string.begin()); | 1390 | std::copy(version.begin(), version.end(), version_string.begin()); |
| 1347 | } else { | 1391 | } else { |
| 1348 | constexpr u128 default_version = {1, 0}; | 1392 | constexpr char default_version[]{"1.0.0"}; |
| 1349 | std::memcpy(version_string.data(), default_version.data(), sizeof(u128)); | 1393 | std::memcpy(version_string.data(), default_version, sizeof(default_version)); |
| 1350 | } | 1394 | } |
| 1351 | 1395 | ||
| 1352 | IPC::ResponseBuilder rb{ctx, 6}; | 1396 | IPC::ResponseBuilder rb{ctx, 6}; |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 469f7f814..6cfb11b48 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -138,8 +138,9 @@ private: | |||
| 138 | void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); | 138 | void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); |
| 139 | void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); | 139 | void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); |
| 140 | void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); | 140 | void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); |
| 141 | void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx); | 141 | void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx); |
| 142 | void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); | 142 | void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); |
| 143 | void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx); | ||
| 143 | void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); | 144 | void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); |
| 144 | void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); | 145 | void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); |
| 145 | void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); | 146 | void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 54e63c138..fbe3686ae 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp | |||
| @@ -30,7 +30,7 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( | |||
| 30 | config.sub_text.size()); | 30 | config.sub_text.size()); |
| 31 | params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), | 31 | params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), |
| 32 | config.guide_text.size()); | 32 | config.guide_text.size()); |
| 33 | params.initial_text = initial_text; | 33 | params.initial_text = std::move(initial_text); |
| 34 | params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; | 34 | params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; |
| 35 | params.password = static_cast<bool>(config.is_password); | 35 | params.password = static_cast<bool>(config.is_password); |
| 36 | params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); | 36 | params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); |
| @@ -60,7 +60,7 @@ void SoftwareKeyboard::Initialize() { | |||
| 60 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); | 60 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); |
| 61 | 61 | ||
| 62 | const auto work_buffer_storage = broker.PopNormalDataToApplet(); | 62 | const auto work_buffer_storage = broker.PopNormalDataToApplet(); |
| 63 | ASSERT(work_buffer_storage != nullptr); | 63 | ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; }); |
| 64 | const auto& work_buffer = work_buffer_storage->GetData(); | 64 | const auto& work_buffer = work_buffer_storage->GetData(); |
| 65 | 65 | ||
| 66 | if (config.initial_string_size == 0) | 66 | if (config.initial_string_size == 0) |
| @@ -109,7 +109,7 @@ void SoftwareKeyboard::Execute() { | |||
| 109 | 109 | ||
| 110 | const auto parameters = ConvertToFrontendParameters(config, initial_text); | 110 | const auto parameters = ConvertToFrontendParameters(config, initial_text); |
| 111 | 111 | ||
| 112 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, | 112 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, |
| 113 | parameters); | 113 | parameters); |
| 114 | } | 114 | } |
| 115 | 115 | ||
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp index 003ee8667..f27729ce7 100644 --- a/src/core/hle/service/am/spsm.cpp +++ b/src/core/hle/service/am/spsm.cpp | |||
| @@ -10,17 +10,17 @@ SPSM::SPSM() : ServiceFramework{"spsm"} { | |||
| 10 | // clang-format off | 10 | // clang-format off |
| 11 | static const FunctionInfo functions[] = { | 11 | static const FunctionInfo functions[] = { |
| 12 | {0, nullptr, "GetState"}, | 12 | {0, nullptr, "GetState"}, |
| 13 | {1, nullptr, "SleepSystemAndWaitAwake"}, | 13 | {1, nullptr, "EnterSleep"}, |
| 14 | {2, nullptr, "Unknown1"}, | 14 | {2, nullptr, "GetLastWakeReason"}, |
| 15 | {3, nullptr, "Unknown2"}, | 15 | {3, nullptr, "Shutdown"}, |
| 16 | {4, nullptr, "GetNotificationMessageEventHandle"}, | 16 | {4, nullptr, "GetNotificationMessageEventHandle"}, |
| 17 | {5, nullptr, "Unknown3"}, | 17 | {5, nullptr, "ReceiveNotificationMessage"}, |
| 18 | {6, nullptr, "Unknown4"}, | 18 | {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"}, |
| 19 | {7, nullptr, "Unknown5"}, | 19 | {7, nullptr, "ResetEventLog"}, |
| 20 | {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, | 20 | {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, |
| 21 | {9, nullptr, "ChangeHomeButtonLongPressingTime"}, | 21 | {9, nullptr, "ChangeHomeButtonLongPressingTime"}, |
| 22 | {10, nullptr, "Unknown6"}, | 22 | {10, nullptr, "PutErrorState"}, |
| 23 | {11, nullptr, "Unknown7"}, | 23 | {11, nullptr, "InvalidateCurrentHomeButtonPressing"}, |
| 24 | }; | 24 | }; |
| 25 | // clang-format on | 25 | // clang-format on |
| 26 | 26 | ||
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 4227a4adf..8e79f707b 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -60,6 +60,7 @@ AOC_U::AOC_U(Core::System& system) | |||
| 60 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, | 60 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, |
| 61 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, | 61 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, |
| 62 | {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, | 62 | {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, |
| 63 | {9, nullptr, "GetAddOnContentLostErrorCode"}, | ||
| 63 | {100, nullptr, "CreateEcPurchasedEventManager"}, | 64 | {100, nullptr, "CreateEcPurchasedEventManager"}, |
| 64 | {101, nullptr, "CreatePermanentEcPurchasedEventManager"}, | 65 | {101, nullptr, "CreatePermanentEcPurchasedEventManager"}, |
| 65 | }; | 66 | }; |
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 8bb2528c9..b31766212 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp | |||
| @@ -14,6 +14,8 @@ BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module, | |||
| 14 | {0, &BCAT::CreateBcatService, "CreateBcatService"}, | 14 | {0, &BCAT::CreateBcatService, "CreateBcatService"}, |
| 15 | {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, | 15 | {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, |
| 16 | {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, | 16 | {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, |
| 17 | {3, nullptr, "CreateDeliveryCacheProgressService"}, | ||
| 18 | {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"}, | ||
| 17 | }; | 19 | }; |
| 18 | // clang-format on | 20 | // clang-format on |
| 19 | RegisterHandlers(functions); | 21 | RegisterHandlers(functions); |
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 34aba7a27..603b64d4f 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -143,10 +143,13 @@ public: | |||
| 143 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, | 143 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, |
| 144 | {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, | 144 | {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, |
| 145 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, | 145 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, |
| 146 | {30101, nullptr, "Unknown"}, | ||
| 147 | {30102, nullptr, "Unknown2"}, | ||
| 146 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, | 148 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, |
| 147 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, | 149 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, |
| 148 | {30202, nullptr, "BlockDeliveryTask"}, | 150 | {30202, nullptr, "BlockDeliveryTask"}, |
| 149 | {30203, nullptr, "UnblockDeliveryTask"}, | 151 | {30203, nullptr, "UnblockDeliveryTask"}, |
| 152 | {30210, nullptr, "SetDeliveryTaskTimer"}, | ||
| 150 | {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, | 153 | {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, |
| 151 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, | 154 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, |
| 152 | {90200, nullptr, "GetDeliveryList"}, | 155 | {90200, nullptr, "GetDeliveryList"}, |
diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp index 1c1ecdb60..fac6b2f9c 100644 --- a/src/core/hle/service/bpc/bpc.cpp +++ b/src/core/hle/service/bpc/bpc.cpp | |||
| @@ -23,9 +23,14 @@ public: | |||
| 23 | {5, nullptr, "GetBoardPowerControlEvent"}, | 23 | {5, nullptr, "GetBoardPowerControlEvent"}, |
| 24 | {6, nullptr, "GetSleepButtonState"}, | 24 | {6, nullptr, "GetSleepButtonState"}, |
| 25 | {7, nullptr, "GetPowerEvent"}, | 25 | {7, nullptr, "GetPowerEvent"}, |
| 26 | {8, nullptr, "Unknown1"}, | 26 | {8, nullptr, "CreateWakeupTimer"}, |
| 27 | {9, nullptr, "Unknown2"}, | 27 | {9, nullptr, "CancelWakeupTimer"}, |
| 28 | {10, nullptr, "Unknown3"}, | 28 | {10, nullptr, "EnableWakeupTimerOnDevice"}, |
| 29 | {11, nullptr, "CreateWakeupTimerEx"}, | ||
| 30 | {12, nullptr, "GetLastEnabledWakeupTimerType"}, | ||
| 31 | {13, nullptr, "CleanAllWakeupTimers"}, | ||
| 32 | {14, nullptr, "Unknown"}, | ||
| 33 | {15, nullptr, "Unknown2"}, | ||
| 29 | }; | 34 | }; |
| 30 | // clang-format on | 35 | // clang-format on |
| 31 | 36 | ||
| @@ -38,10 +43,11 @@ public: | |||
| 38 | explicit BPC_R() : ServiceFramework{"bpc:r"} { | 43 | explicit BPC_R() : ServiceFramework{"bpc:r"} { |
| 39 | // clang-format off | 44 | // clang-format off |
| 40 | static const FunctionInfo functions[] = { | 45 | static const FunctionInfo functions[] = { |
| 41 | {0, nullptr, "GetExternalRtcValue"}, | 46 | {0, nullptr, "GetRtcTime"}, |
| 42 | {1, nullptr, "SetExternalRtcValue"}, | 47 | {1, nullptr, "SetRtcTime"}, |
| 43 | {2, nullptr, "ReadExternalRtcResetFlag"}, | 48 | {2, nullptr, "GetRtcResetDetected"}, |
| 44 | {3, nullptr, "ClearExternalRtcResetFlag"}, | 49 | {3, nullptr, "ClearRtcResetDetected"}, |
| 50 | {4, nullptr, "SetUpRtcResetOnShutdown"}, | ||
| 45 | }; | 51 | }; |
| 46 | // clang-format on | 52 | // clang-format on |
| 47 | 53 | ||
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 40a06c9fd..f311afa2f 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp | |||
| @@ -58,102 +58,103 @@ public: | |||
| 58 | {1, nullptr, "InitializeBluetooth"}, | 58 | {1, nullptr, "InitializeBluetooth"}, |
| 59 | {2, nullptr, "EnableBluetooth"}, | 59 | {2, nullptr, "EnableBluetooth"}, |
| 60 | {3, nullptr, "DisableBluetooth"}, | 60 | {3, nullptr, "DisableBluetooth"}, |
| 61 | {4, nullptr, "CleanupBluetooth"}, | 61 | {4, nullptr, "FinalizeBluetooth"}, |
| 62 | {5, nullptr, "GetAdapterProperties"}, | 62 | {5, nullptr, "GetAdapterProperties"}, |
| 63 | {6, nullptr, "GetAdapterProperty"}, | 63 | {6, nullptr, "GetAdapterProperty"}, |
| 64 | {7, nullptr, "SetAdapterProperty"}, | 64 | {7, nullptr, "SetAdapterProperty"}, |
| 65 | {8, nullptr, "StartDiscovery"}, | 65 | {8, nullptr, "StartInquiry"}, |
| 66 | {9, nullptr, "CancelDiscovery"}, | 66 | {9, nullptr, "StopInquiry"}, |
| 67 | {10, nullptr, "CreateBond"}, | 67 | {10, nullptr, "CreateBond"}, |
| 68 | {11, nullptr, "RemoveBond"}, | 68 | {11, nullptr, "RemoveBond"}, |
| 69 | {12, nullptr, "CancelBond"}, | 69 | {12, nullptr, "CancelBond"}, |
| 70 | {13, nullptr, "PinReply"}, | 70 | {13, nullptr, "RespondToPinRequest"}, |
| 71 | {14, nullptr, "SspReply"}, | 71 | {14, nullptr, "RespondToSspRequest"}, |
| 72 | {15, nullptr, "GetEventInfo"}, | 72 | {15, nullptr, "GetEventInfo"}, |
| 73 | {16, nullptr, "InitializeHid"}, | 73 | {16, nullptr, "InitializeHid"}, |
| 74 | {17, nullptr, "HidConnect"}, | 74 | {17, nullptr, "OpenHidConnection"}, |
| 75 | {18, nullptr, "HidDisconnect"}, | 75 | {18, nullptr, "CloseHidConnection"}, |
| 76 | {19, nullptr, "HidSendData"}, | 76 | {19, nullptr, "WriteHidData"}, |
| 77 | {20, nullptr, "HidSendData2"}, | 77 | {20, nullptr, "WriteHidData2"}, |
| 78 | {21, nullptr, "HidSetReport"}, | 78 | {21, nullptr, "SetHidReport"}, |
| 79 | {22, nullptr, "HidGetReport"}, | 79 | {22, nullptr, "GetHidReport"}, |
| 80 | {23, nullptr, "HidWakeController"}, | 80 | {23, nullptr, "TriggerConnection"}, |
| 81 | {24, nullptr, "HidAddPairedDevice"}, | 81 | {24, nullptr, "AddPairedDeviceInfo"}, |
| 82 | {25, nullptr, "HidGetPairedDevice"}, | 82 | {25, nullptr, "GetPairedDeviceInfo"}, |
| 83 | {26, nullptr, "CleanupHid"}, | 83 | {26, nullptr, "FinalizeHid"}, |
| 84 | {27, nullptr, "HidGetEventInfo"}, | 84 | {27, nullptr, "GetHidEventInfo"}, |
| 85 | {28, nullptr, "ExtSetTsi"}, | 85 | {28, nullptr, "SetTsi"}, |
| 86 | {29, nullptr, "ExtSetBurstMode"}, | 86 | {29, nullptr, "EnableBurstMode"}, |
| 87 | {30, nullptr, "ExtSetZeroRetran"}, | 87 | {30, nullptr, "SetZeroRetransmission"}, |
| 88 | {31, nullptr, "ExtSetMcMode"}, | 88 | {31, nullptr, "EnableMcMode"}, |
| 89 | {32, nullptr, "ExtStartLlrMode"}, | 89 | {32, nullptr, "EnableLlrScan"}, |
| 90 | {33, nullptr, "ExtExitLlrMode"}, | 90 | {33, nullptr, "DisableLlrScan"}, |
| 91 | {34, nullptr, "ExtSetRadio"}, | 91 | {34, nullptr, "EnableRadio"}, |
| 92 | {35, nullptr, "ExtSetVisibility"}, | 92 | {35, nullptr, "SetVisibility"}, |
| 93 | {36, nullptr, "ExtSetTbfcScan"}, | 93 | {36, nullptr, "EnableTbfcScan"}, |
| 94 | {37, nullptr, "RegisterHidReportEvent"}, | 94 | {37, nullptr, "RegisterHidReportEvent"}, |
| 95 | {38, nullptr, "HidGetReportEventInfo"}, | 95 | {38, nullptr, "GetHidReportEventInfo"}, |
| 96 | {39, nullptr, "GetLatestPlr"}, | 96 | {39, nullptr, "GetLatestPlr"}, |
| 97 | {40, nullptr, "ExtGetPendingConnections"}, | 97 | {40, nullptr, "GetPendingConnections"}, |
| 98 | {41, nullptr, "GetChannelMap"}, | 98 | {41, nullptr, "GetChannelMap"}, |
| 99 | {42, nullptr, "EnableBluetoothBoostSetting"}, | 99 | {42, nullptr, "EnableTxPowerBoostSetting"}, |
| 100 | {43, nullptr, "IsBluetoothBoostSettingEnabled"}, | 100 | {43, nullptr, "IsTxPowerBoostSettingEnabled"}, |
| 101 | {44, nullptr, "EnableBluetoothAfhSetting"}, | 101 | {44, nullptr, "EnableAfhSetting"}, |
| 102 | {45, nullptr, "IsBluetoothAfhSettingEnabled"}, | 102 | {45, nullptr, "IsAfhSettingEnabled"}, |
| 103 | {46, nullptr, "InitializeBluetoothLe"}, | 103 | {46, nullptr, "InitializeBle"}, |
| 104 | {47, nullptr, "EnableBluetoothLe"}, | 104 | {47, nullptr, "EnableBle"}, |
| 105 | {48, nullptr, "DisableBluetoothLe"}, | 105 | {48, nullptr, "DisableBle"}, |
| 106 | {49, nullptr, "CleanupBluetoothLe"}, | 106 | {49, nullptr, "FinalizeBle"}, |
| 107 | {50, nullptr, "SetLeVisibility"}, | 107 | {50, nullptr, "SetBleVisibility"}, |
| 108 | {51, nullptr, "SetLeConnectionParameter"}, | 108 | {51, nullptr, "SetBleConnectionParameter"}, |
| 109 | {52, nullptr, "SetLeDefaultConnectionParameter"}, | 109 | {52, nullptr, "SetBleDefaultConnectionParameter"}, |
| 110 | {53, nullptr, "SetLeAdvertiseData"}, | 110 | {53, nullptr, "SetBleAdvertiseData"}, |
| 111 | {54, nullptr, "SetLeAdvertiseParameter"}, | 111 | {54, nullptr, "SetBleAdvertiseParameter"}, |
| 112 | {55, nullptr, "StartLeScan"}, | 112 | {55, nullptr, "StartBleScan"}, |
| 113 | {56, nullptr, "StopLeScan"}, | 113 | {56, nullptr, "StopBleScan"}, |
| 114 | {57, nullptr, "AddLeScanFilterCondition"}, | 114 | {57, nullptr, "AddBleScanFilterCondition"}, |
| 115 | {58, nullptr, "DeleteLeScanFilterCondition"}, | 115 | {58, nullptr, "DeleteBleScanFilterCondition"}, |
| 116 | {59, nullptr, "DeleteLeScanFilter"}, | 116 | {59, nullptr, "DeleteBleScanFilter"}, |
| 117 | {60, nullptr, "ClearLeScanFilters"}, | 117 | {60, nullptr, "ClearBleScanFilters"}, |
| 118 | {61, nullptr, "EnableLeScanFilter"}, | 118 | {61, nullptr, "EnableBleScanFilter"}, |
| 119 | {62, nullptr, "RegisterLeClient"}, | 119 | {62, nullptr, "RegisterGattClient"}, |
| 120 | {63, nullptr, "UnregisterLeClient"}, | 120 | {63, nullptr, "UnregisterGattClient"}, |
| 121 | {64, nullptr, "UnregisterLeClientAll"}, | 121 | {64, nullptr, "UnregisterAllGattClients"}, |
| 122 | {65, nullptr, "LeClientConnect"}, | 122 | {65, nullptr, "ConnectGattServer"}, |
| 123 | {66, nullptr, "LeClientCancelConnection"}, | 123 | {66, nullptr, "CancelConnectGattServer"}, |
| 124 | {67, nullptr, "LeClientDisconnect"}, | 124 | {67, nullptr, "DisconnectGattServer"}, |
| 125 | {68, nullptr, "LeClientGetAttributes"}, | 125 | {68, nullptr, "GetGattAttribute"}, |
| 126 | {69, nullptr, "LeClientDiscoverService"}, | 126 | {69, nullptr, "GetGattService"}, |
| 127 | {70, nullptr, "LeClientConfigureMtu"}, | 127 | {70, nullptr, "ConfigureAttMtu"}, |
| 128 | {71, nullptr, "RegisterLeServer"}, | 128 | {71, nullptr, "RegisterGattServer"}, |
| 129 | {72, nullptr, "UnregisterLeServer"}, | 129 | {72, nullptr, "UnregisterGattServer"}, |
| 130 | {73, nullptr, "LeServerConnect"}, | 130 | {73, nullptr, "ConnectGattClient"}, |
| 131 | {74, nullptr, "LeServerDisconnect"}, | 131 | {74, nullptr, "DisconnectGattClient"}, |
| 132 | {75, nullptr, "CreateLeService"}, | 132 | {75, nullptr, "AddGattService"}, |
| 133 | {76, nullptr, "StartLeService"}, | 133 | {76, nullptr, "EnableGattService"}, |
| 134 | {77, nullptr, "AddLeCharacteristic"}, | 134 | {77, nullptr, "AddGattCharacteristic"}, |
| 135 | {78, nullptr, "AddLeDescriptor"}, | 135 | {78, nullptr, "AddGattDescriptor"}, |
| 136 | {79, nullptr, "GetLeCoreEventInfo"}, | 136 | {79, nullptr, "GetBleManagedEventInfo"}, |
| 137 | {80, nullptr, "LeGetFirstCharacteristic"}, | 137 | {80, nullptr, "GetGattFirstCharacteristic"}, |
| 138 | {81, nullptr, "LeGetNextCharacteristic"}, | 138 | {81, nullptr, "GetGattNextCharacteristic"}, |
| 139 | {82, nullptr, "LeGetFirstDescriptor"}, | 139 | {82, nullptr, "GetGattFirstDescriptor"}, |
| 140 | {83, nullptr, "LeGetNextDescriptor"}, | 140 | {83, nullptr, "GetGattNextDescriptor"}, |
| 141 | {84, nullptr, "RegisterLeCoreDataPath"}, | 141 | {84, nullptr, "RegisterGattManagedDataPath"}, |
| 142 | {85, nullptr, "UnregisterLeCoreDataPath"}, | 142 | {85, nullptr, "UnregisterGattManagedDataPath"}, |
| 143 | {86, nullptr, "RegisterLeHidDataPath"}, | 143 | {86, nullptr, "RegisterGattHidDataPath"}, |
| 144 | {87, nullptr, "UnregisterLeHidDataPath"}, | 144 | {87, nullptr, "UnregisterGattHidDataPath"}, |
| 145 | {88, nullptr, "RegisterLeDataPath"}, | 145 | {88, nullptr, "RegisterGattDataPath"}, |
| 146 | {89, nullptr, "UnregisterLeDataPath"}, | 146 | {89, nullptr, "UnregisterGattDataPath"}, |
| 147 | {90, nullptr, "LeClientReadCharacteristic"}, | 147 | {90, nullptr, "ReadGattCharacteristic"}, |
| 148 | {91, nullptr, "LeClientReadDescriptor"}, | 148 | {91, nullptr, "ReadGattDescriptor"}, |
| 149 | {92, nullptr, "LeClientWriteCharacteristic"}, | 149 | {92, nullptr, "WriteGattCharacteristic"}, |
| 150 | {93, nullptr, "LeClientWriteDescriptor"}, | 150 | {93, nullptr, "WriteGattDescriptor"}, |
| 151 | {94, nullptr, "LeClientRegisterNotification"}, | 151 | {94, nullptr, "RegisterGattNotification"}, |
| 152 | {95, nullptr, "LeClientDeregisterNotification"}, | 152 | {95, nullptr, "UnregisterGattNotification"}, |
| 153 | {96, nullptr, "GetLeHidEventInfo"}, | 153 | {96, nullptr, "GetLeHidEventInfo"}, |
| 154 | {97, nullptr, "RegisterBleHidEvent"}, | 154 | {97, nullptr, "RegisterBleHidEvent"}, |
| 155 | {98, nullptr, "SetLeScanParameter"}, | 155 | {98, nullptr, "SetBleScanParameter"}, |
| 156 | {256, nullptr, "GetIsManufacturingMode"}, | 156 | {99, nullptr, "MoveToSecondaryPiconet"}, |
| 157 | {256, nullptr, "IsManufacturingMode"}, | ||
| 157 | {257, nullptr, "EmulateBluetoothCrash"}, | 158 | {257, nullptr, "EmulateBluetoothCrash"}, |
| 158 | {258, nullptr, "GetBleChannelMap"}, | 159 | {258, nullptr, "GetBleChannelMap"}, |
| 159 | }; | 160 | }; |
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 251b3c9df..0d251c6d0 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp | |||
| @@ -132,66 +132,71 @@ public: | |||
| 132 | explicit BTM() : ServiceFramework{"btm"} { | 132 | explicit BTM() : ServiceFramework{"btm"} { |
| 133 | // clang-format off | 133 | // clang-format off |
| 134 | static const FunctionInfo functions[] = { | 134 | static const FunctionInfo functions[] = { |
| 135 | {0, nullptr, "Unknown1"}, | 135 | {0, nullptr, "GetState"}, |
| 136 | {1, nullptr, "Unknown2"}, | 136 | {1, nullptr, "GetHostDeviceProperty"}, |
| 137 | {2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"}, | 137 | {2, nullptr, "AcquireDeviceConditionEvent"}, |
| 138 | {3, nullptr, "Unknown3"}, | 138 | {3, nullptr, "GetDeviceCondition"}, |
| 139 | {4, nullptr, "Unknown4"}, | 139 | {4, nullptr, "SetBurstMode"}, |
| 140 | {5, nullptr, "Unknown5"}, | 140 | {5, nullptr, "SetSlotMode"}, |
| 141 | {6, nullptr, "Unknown6"}, | 141 | {6, nullptr, "SetBluetoothMode"}, |
| 142 | {7, nullptr, "Unknown7"}, | 142 | {7, nullptr, "SetWlanMode"}, |
| 143 | {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"}, | 143 | {8, nullptr, "AcquireDeviceInfoEvent"}, |
| 144 | {9, nullptr, "Unknown8"}, | 144 | {9, nullptr, "GetDeviceInfo"}, |
| 145 | {10, nullptr, "Unknown9"}, | 145 | {10, nullptr, "AddDeviceInfo"}, |
| 146 | {11, nullptr, "Unknown10"}, | 146 | {11, nullptr, "RemoveDeviceInfo"}, |
| 147 | {12, nullptr, "Unknown11"}, | 147 | {12, nullptr, "IncreaseDeviceInfoOrder"}, |
| 148 | {13, nullptr, "Unknown12"}, | 148 | {13, nullptr, "LlrNotify"}, |
| 149 | {14, nullptr, "EnableRadio"}, | 149 | {14, nullptr, "EnableRadio"}, |
| 150 | {15, nullptr, "DisableRadio"}, | 150 | {15, nullptr, "DisableRadio"}, |
| 151 | {16, nullptr, "Unknown13"}, | 151 | {16, nullptr, "HidDisconnect"}, |
| 152 | {17, nullptr, "Unknown14"}, | 152 | {17, nullptr, "HidSetRetransmissionMode"}, |
| 153 | {18, nullptr, "Unknown15"}, | 153 | {18, nullptr, "AcquireAwakeReqEvent"}, |
| 154 | {19, nullptr, "Unknown16"}, | 154 | {19, nullptr, "AcquireLlrStateEvent"}, |
| 155 | {20, nullptr, "Unknown17"}, | 155 | {20, nullptr, "IsLlrStarted"}, |
| 156 | {21, nullptr, "Unknown18"}, | 156 | {21, nullptr, "EnableSlotSaving"}, |
| 157 | {22, nullptr, "Unknown19"}, | 157 | {22, nullptr, "ProtectDeviceInfo"}, |
| 158 | {23, nullptr, "Unknown20"}, | 158 | {23, nullptr, "AcquireBleScanEvent"}, |
| 159 | {24, nullptr, "Unknown21"}, | 159 | {24, nullptr, "GetBleScanParameterGeneral"}, |
| 160 | {25, nullptr, "Unknown22"}, | 160 | {25, nullptr, "GetBleScanParameterSmartDevice"}, |
| 161 | {26, nullptr, "Unknown23"}, | 161 | {26, nullptr, "StartBleScanForGeneral"}, |
| 162 | {27, nullptr, "Unknown24"}, | 162 | {27, nullptr, "StopBleScanForGeneral"}, |
| 163 | {28, nullptr, "Unknown25"}, | 163 | {28, nullptr, "GetBleScanResultsForGeneral"}, |
| 164 | {29, nullptr, "Unknown26"}, | 164 | {29, nullptr, "StartBleScanForPairedDevice"}, |
| 165 | {30, nullptr, "Unknown27"}, | 165 | {30, nullptr, "StopBleScanForPairedDevice"}, |
| 166 | {31, nullptr, "Unknown28"}, | 166 | {31, nullptr, "StartBleScanForSmartDevice"}, |
| 167 | {32, nullptr, "Unknown29"}, | 167 | {32, nullptr, "StopBleScanForSmartDevice"}, |
| 168 | {33, nullptr, "Unknown30"}, | 168 | {33, nullptr, "GetBleScanResultsForSmartDevice"}, |
| 169 | {34, nullptr, "Unknown31"}, | 169 | {34, nullptr, "AcquireBleConnectionEvent"}, |
| 170 | {35, nullptr, "Unknown32"}, | 170 | {35, nullptr, "BleConnect"}, |
| 171 | {36, nullptr, "Unknown33"}, | 171 | {36, nullptr, "BleOverrideConnection"}, |
| 172 | {37, nullptr, "Unknown34"}, | 172 | {37, nullptr, "BleDisconnect"}, |
| 173 | {38, nullptr, "Unknown35"}, | 173 | {38, nullptr, "BleGetConnectionState"}, |
| 174 | {39, nullptr, "Unknown36"}, | 174 | {39, nullptr, "BleGetGattClientConditionList"}, |
| 175 | {40, nullptr, "Unknown37"}, | 175 | {40, nullptr, "AcquireBlePairingEvent"}, |
| 176 | {41, nullptr, "Unknown38"}, | 176 | {41, nullptr, "BlePairDevice"}, |
| 177 | {42, nullptr, "Unknown39"}, | 177 | {42, nullptr, "BleUnpairDeviceOnBoth"}, |
| 178 | {43, nullptr, "Unknown40"}, | 178 | {43, nullptr, "BleUnpairDevice"}, |
| 179 | {44, nullptr, "Unknown41"}, | 179 | {44, nullptr, "BleGetPairedAddresses"}, |
| 180 | {45, nullptr, "Unknown42"}, | 180 | {45, nullptr, "AcquireBleServiceDiscoveryEvent"}, |
| 181 | {46, nullptr, "Unknown43"}, | 181 | {46, nullptr, "GetGattServices"}, |
| 182 | {47, nullptr, "Unknown44"}, | 182 | {47, nullptr, "GetGattService"}, |
| 183 | {48, nullptr, "Unknown45"}, | 183 | {48, nullptr, "GetGattIncludedServices"}, |
| 184 | {49, nullptr, "Unknown46"}, | 184 | {49, nullptr, "GetBelongingService"}, |
| 185 | {50, nullptr, "Unknown47"}, | 185 | {50, nullptr, "GetGattCharacteristics"}, |
| 186 | {51, nullptr, "Unknown48"}, | 186 | {51, nullptr, "GetGattDescriptors"}, |
| 187 | {52, nullptr, "Unknown49"}, | 187 | {52, nullptr, "AcquireBleMtuConfigEvent"}, |
| 188 | {53, nullptr, "Unknown50"}, | 188 | {53, nullptr, "ConfigureBleMtu"}, |
| 189 | {54, nullptr, "Unknown51"}, | 189 | {54, nullptr, "GetBleMtu"}, |
| 190 | {55, nullptr, "Unknown52"}, | 190 | {55, nullptr, "RegisterBleGattDataPath"}, |
| 191 | {56, nullptr, "Unknown53"}, | 191 | {56, nullptr, "UnregisterBleGattDataPath"}, |
| 192 | {57, nullptr, "Unknown54"}, | 192 | {57, nullptr, "RegisterAppletResourceUserId"}, |
| 193 | {58, nullptr, "Unknown55"}, | 193 | {58, nullptr, "UnregisterAppletResourceUserId"}, |
| 194 | {59, nullptr, "Unknown56"}, | 194 | {59, nullptr, "SetAppletResourceUserId"}, |
| 195 | {60, nullptr, "Unknown60"}, | ||
| 196 | {61, nullptr, "Unknown61"}, | ||
| 197 | {62, nullptr, "Unknown62"}, | ||
| 198 | {63, nullptr, "Unknown63"}, | ||
| 199 | {64, nullptr, "Unknown64"}, | ||
| 195 | }; | 200 | }; |
| 196 | // clang-format on | 201 | // clang-format on |
| 197 | 202 | ||
| @@ -204,19 +209,19 @@ public: | |||
| 204 | explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { | 209 | explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { |
| 205 | // clang-format off | 210 | // clang-format off |
| 206 | static const FunctionInfo functions[] = { | 211 | static const FunctionInfo functions[] = { |
| 207 | {0, nullptr, "RegisterSystemEventForDiscovery"}, | 212 | {0, nullptr, "AcquireDiscoveryEvent"}, |
| 208 | {1, nullptr, "Unknown1"}, | 213 | {1, nullptr, "StartDiscovery"}, |
| 209 | {2, nullptr, "Unknown2"}, | 214 | {2, nullptr, "CancelDiscovery"}, |
| 210 | {3, nullptr, "Unknown3"}, | 215 | {3, nullptr, "GetDeviceProperty"}, |
| 211 | {4, nullptr, "Unknown4"}, | 216 | {4, nullptr, "CreateBond"}, |
| 212 | {5, nullptr, "Unknown5"}, | 217 | {5, nullptr, "CancelBond"}, |
| 213 | {6, nullptr, "Unknown6"}, | 218 | {6, nullptr, "SetTsiMode"}, |
| 214 | {7, nullptr, "Unknown7"}, | 219 | {7, nullptr, "GeneralTest"}, |
| 215 | {8, nullptr, "Unknown8"}, | 220 | {8, nullptr, "HidConnect"}, |
| 216 | {9, nullptr, "Unknown9"}, | 221 | {9, nullptr, "GeneralGet"}, |
| 217 | {10, nullptr, "Unknown10"}, | 222 | {10, nullptr, "GetGattClientDisconnectionReason"}, |
| 218 | {11, nullptr, "Unknown11"}, | 223 | {11, nullptr, "GetBleConnectionParameter"}, |
| 219 | {12, nullptr, "Unknown11"}, | 224 | {12, nullptr, "GetBleConnectionParameterRequest"}, |
| 220 | }; | 225 | }; |
| 221 | // clang-format on | 226 | // clang-format on |
| 222 | 227 | ||
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 26c8a7081..ba5749b84 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | 1 | // Copyright 2018 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index fc70a4c27..b8c67b6e2 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | 1 | // Copyright 2018 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -12,73 +12,79 @@ class ServiceManager; | |||
| 12 | 12 | ||
| 13 | namespace Service::Capture { | 13 | namespace Service::Capture { |
| 14 | 14 | ||
| 15 | enum AlbumImageOrientation { | 15 | enum class AlbumImageOrientation { |
| 16 | Orientation0 = 0, | 16 | Orientation0 = 0, |
| 17 | Orientation1 = 1, | 17 | Orientation1 = 1, |
| 18 | Orientation2 = 2, | 18 | Orientation2 = 2, |
| 19 | Orientation3 = 3, | 19 | Orientation3 = 3, |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | enum AlbumReportOption { | 22 | enum class AlbumReportOption { |
| 23 | Disable = 0, | 23 | Disable = 0, |
| 24 | Enable = 1, | 24 | Enable = 1, |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | enum ContentType : u8 { | 27 | enum class ContentType : u8 { |
| 28 | Screenshot = 0, | 28 | Screenshot = 0, |
| 29 | Movie = 1, | 29 | Movie = 1, |
| 30 | ExtraMovie = 3, | 30 | ExtraMovie = 3, |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | enum AlbumStorage : u8 { | 33 | enum class AlbumStorage : u8 { |
| 34 | NAND = 0, | 34 | NAND = 0, |
| 35 | SD = 1, | 35 | SD = 1, |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | struct AlbumFileDateTime { | 38 | struct AlbumFileDateTime { |
| 39 | u16 year; | 39 | s16 year{}; |
| 40 | u8 month; | 40 | s8 month{}; |
| 41 | u8 day; | 41 | s8 day{}; |
| 42 | u8 hour; | 42 | s8 hour{}; |
| 43 | u8 minute; | 43 | s8 minute{}; |
| 44 | u8 second; | 44 | s8 second{}; |
| 45 | u8 uid; | 45 | s8 uid{}; |
| 46 | }; | 46 | }; |
| 47 | static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size."); | ||
| 47 | 48 | ||
| 48 | struct AlbumEntry { | 49 | struct AlbumEntry { |
| 49 | u64 size; | 50 | u64 size{}; |
| 50 | u64 application_id; | 51 | u64 application_id{}; |
| 51 | AlbumFileDateTime datetime; | 52 | AlbumFileDateTime datetime{}; |
| 52 | AlbumStorage storage; | 53 | AlbumStorage storage{}; |
| 53 | ContentType content; | 54 | ContentType content{}; |
| 54 | u8 padding[6]; | 55 | INSERT_PADDING_BYTES(6); |
| 55 | }; | 56 | }; |
| 57 | static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size."); | ||
| 56 | 58 | ||
| 57 | struct AlbumFileEntry { | 59 | struct AlbumFileEntry { |
| 58 | u64 size; | 60 | u64 size{}; // Size of the entry |
| 59 | u64 hash; | 61 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry |
| 60 | AlbumFileDateTime datetime; | 62 | AlbumFileDateTime datetime{}; |
| 61 | AlbumStorage storage; | 63 | AlbumStorage storage{}; |
| 62 | ContentType content; | 64 | ContentType content{}; |
| 63 | u8 padding[5]; | 65 | INSERT_PADDING_BYTES(5); |
| 64 | u8 unknown; | 66 | u8 unknown{1}; // Set to 1 on official SW |
| 65 | }; | 67 | }; |
| 68 | static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size."); | ||
| 66 | 69 | ||
| 67 | struct ApplicationAlbumEntry { | 70 | struct ApplicationAlbumEntry { |
| 68 | u64 size; | 71 | u64 size{}; // Size of the entry |
| 69 | u64 hash; | 72 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry |
| 70 | AlbumFileDateTime datetime; | 73 | AlbumFileDateTime datetime{}; |
| 71 | AlbumStorage storage; | 74 | AlbumStorage storage{}; |
| 72 | ContentType content; | 75 | ContentType content{}; |
| 73 | u8 padding[5]; | 76 | INSERT_PADDING_BYTES(5); |
| 74 | u8 unknown; | 77 | u8 unknown{1}; // Set to 1 on official SW |
| 75 | }; | 78 | }; |
| 79 | static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size."); | ||
| 76 | 80 | ||
| 77 | struct ApplicationAlbumFileEntry { | 81 | struct ApplicationAlbumFileEntry { |
| 78 | ApplicationAlbumEntry entry; | 82 | ApplicationAlbumEntry entry{}; |
| 79 | AlbumFileDateTime datetime; | 83 | AlbumFileDateTime datetime{}; |
| 80 | u64 unknown; | 84 | u64 unknown{}; |
| 81 | }; | 85 | }; |
| 86 | static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, | ||
| 87 | "ApplicationAlbumFileEntry has incorrect size."); | ||
| 82 | 88 | ||
| 83 | /// Registers all Capture services with the specified service manager. | 89 | /// Registers all Capture services with the specified service manager. |
| 84 | void InstallInterfaces(SM::ServiceManager& sm); | 90 | void InstallInterfaces(SM::ServiceManager& sm); |
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 88a3fdc05..a0a3b2ae3 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 8de832491..cb93aad5b 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index ea6452ffa..ab17a187e 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index d07cdb441..a9d028689 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index d01a8a58e..822ee96c8 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index 9ba372f7a..ac3e929ca 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index eaa3a7494..24dc716e7 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index e258a6925..450686e4f 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index e8b0698e8..fffb2ecf9 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index c494d7c84..62c9603a9 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 78bab6ed8..f36d8de2d 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -58,19 +58,25 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c | |||
| 58 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total | 58 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total |
| 59 | // output entries (which is copied to a s32 by official SW). | 59 | // output entries (which is copied to a s32 by official SW). |
| 60 | IPC::RequestParser rp{ctx}; | 60 | IPC::RequestParser rp{ctx}; |
| 61 | [[maybe_unused]] const auto application_album_file_entries = rp.PopRaw<std::array<u8, 0x30>>(); | 61 | const auto pid{rp.Pop<s32>()}; |
| 62 | const auto pid = rp.Pop<s32>(); | 62 | const auto content_type{rp.PopEnum<ContentType>()}; |
| 63 | const auto content_type = rp.PopRaw<ContentType>(); | 63 | const auto start_posix_time{rp.Pop<s64>()}; |
| 64 | [[maybe_unused]] const auto start_datetime = rp.PopRaw<AlbumFileDateTime>(); | 64 | const auto end_posix_time{rp.Pop<s64>()}; |
| 65 | [[maybe_unused]] const auto end_datetime = rp.PopRaw<AlbumFileDateTime>(); | 65 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 66 | const auto applet_resource_user_id = rp.Pop<u64>(); | 66 | |
| 67 | // TODO: Update this when we implement the album. | ||
| 68 | // Currently we do not have a method of accessing album entries, set this to 0 for now. | ||
| 69 | constexpr s32 total_entries{0}; | ||
| 70 | |||
| 67 | LOG_WARNING(Service_Capture, | 71 | LOG_WARNING(Service_Capture, |
| 68 | "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, | 72 | "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " |
| 69 | content_type, applet_resource_user_id); | 73 | "end_posix_time={}, applet_resource_user_id={}, total_entries={}", |
| 74 | pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, | ||
| 75 | total_entries); | ||
| 70 | 76 | ||
| 71 | IPC::ResponseBuilder rb{ctx, 3}; | 77 | IPC::ResponseBuilder rb{ctx, 3}; |
| 72 | rb.Push(RESULT_SUCCESS); | 78 | rb.Push(RESULT_SUCCESS); |
| 73 | rb.Push<s32>(0); | 79 | rb.Push(total_entries); |
| 74 | } | 80 | } |
| 75 | 81 | ||
| 76 | } // namespace Service::Capture | 82 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e6e0716ff..689364de4 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index f8e9df4b1..a41c73c48 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -27,8 +27,8 @@ public: | |||
| 27 | {8, &ETicket::GetTitleKey, "GetTitleKey"}, | 27 | {8, &ETicket::GetTitleKey, "GetTitleKey"}, |
| 28 | {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, | 28 | {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, |
| 29 | {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, | 29 | {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, |
| 30 | {11, &ETicket::ListCommonTicket, "ListCommonTicket"}, | 30 | {11, &ETicket::ListCommonTicketRightsIds, "ListCommonTicketRightsIds"}, |
| 31 | {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"}, | 31 | {12, &ETicket::ListPersonalizedTicketRightsIds, "ListPersonalizedTicketRightsIds"}, |
| 32 | {13, nullptr, "ListMissingPersonalizedTicket"}, | 32 | {13, nullptr, "ListMissingPersonalizedTicket"}, |
| 33 | {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, | 33 | {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, |
| 34 | {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, | 34 | {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, |
| @@ -55,7 +55,46 @@ public: | |||
| 55 | {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, | 55 | {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, |
| 56 | {37, nullptr, "OwnTicket2"}, | 56 | {37, nullptr, "OwnTicket2"}, |
| 57 | {38, nullptr, "OwnTicket3"}, | 57 | {38, nullptr, "OwnTicket3"}, |
| 58 | {501, nullptr, "Unknown501"}, | ||
| 59 | {502, nullptr, "Unknown502"}, | ||
| 58 | {503, nullptr, "GetTitleKey"}, | 60 | {503, nullptr, "GetTitleKey"}, |
| 61 | {504, nullptr, "Unknown504"}, | ||
| 62 | {508, nullptr, "Unknown508"}, | ||
| 63 | {509, nullptr, "Unknown509"}, | ||
| 64 | {510, nullptr, "Unknown510"}, | ||
| 65 | {511, nullptr, "Unknown511"}, | ||
| 66 | {1001, nullptr, "Unknown1001"}, | ||
| 67 | {1002, nullptr, "Unknown1001"}, | ||
| 68 | {1003, nullptr, "Unknown1003"}, | ||
| 69 | {1004, nullptr, "Unknown1004"}, | ||
| 70 | {1005, nullptr, "Unknown1005"}, | ||
| 71 | {1006, nullptr, "Unknown1006"}, | ||
| 72 | {1007, nullptr, "Unknown1007"}, | ||
| 73 | {1009, nullptr, "Unknown1009"}, | ||
| 74 | {1010, nullptr, "Unknown1010"}, | ||
| 75 | {1011, nullptr, "Unknown1011"}, | ||
| 76 | {1012, nullptr, "Unknown1012"}, | ||
| 77 | {1013, nullptr, "Unknown1013"}, | ||
| 78 | {1014, nullptr, "Unknown1014"}, | ||
| 79 | {1015, nullptr, "Unknown1015"}, | ||
| 80 | {1016, nullptr, "Unknown1016"}, | ||
| 81 | {1017, nullptr, "Unknown1017"}, | ||
| 82 | {1018, nullptr, "Unknown1018"}, | ||
| 83 | {1019, nullptr, "Unknown1019"}, | ||
| 84 | {1020, nullptr, "Unknown1020"}, | ||
| 85 | {1021, nullptr, "Unknown1021"}, | ||
| 86 | {1501, nullptr, "Unknown1501"}, | ||
| 87 | {1502, nullptr, "Unknown1502"}, | ||
| 88 | {1503, nullptr, "Unknown1503"}, | ||
| 89 | {1504, nullptr, "Unknown1504"}, | ||
| 90 | {1505, nullptr, "Unknown1505"}, | ||
| 91 | {2000, nullptr, "Unknown2000"}, | ||
| 92 | {2001, nullptr, "Unknown2001"}, | ||
| 93 | {2100, nullptr, "Unknown2100"}, | ||
| 94 | {2501, nullptr, "Unknown2501"}, | ||
| 95 | {2502, nullptr, "Unknown2502"}, | ||
| 96 | {3001, nullptr, "Unknown3001"}, | ||
| 97 | {3002, nullptr, "Unknown3002"}, | ||
| 59 | }; | 98 | }; |
| 60 | // clang-format on | 99 | // clang-format on |
| 61 | RegisterHandlers(functions); | 100 | RegisterHandlers(functions); |
| @@ -147,7 +186,7 @@ private: | |||
| 147 | rb.Push<u32>(count); | 186 | rb.Push<u32>(count); |
| 148 | } | 187 | } |
| 149 | 188 | ||
| 150 | void ListCommonTicket(Kernel::HLERequestContext& ctx) { | 189 | void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) { |
| 151 | u32 out_entries; | 190 | u32 out_entries; |
| 152 | if (keys.GetCommonTickets().empty()) | 191 | if (keys.GetCommonTickets().empty()) |
| 153 | out_entries = 0; | 192 | out_entries = 0; |
| @@ -170,7 +209,7 @@ private: | |||
| 170 | rb.Push<u32>(out_entries); | 209 | rb.Push<u32>(out_entries); |
| 171 | } | 210 | } |
| 172 | 211 | ||
| 173 | void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) { | 212 | void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) { |
| 174 | u32 out_entries; | 213 | u32 out_entries; |
| 175 | if (keys.GetPersonalizedTickets().empty()) | 214 | if (keys.GetPersonalizedTickets().empty()) |
| 176 | out_entries = 0; | 215 | out_entries = 0; |
| @@ -263,7 +302,7 @@ private: | |||
| 263 | rb.Push<u64>(write_size); | 302 | rb.Push<u64>(write_size); |
| 264 | } | 303 | } |
| 265 | 304 | ||
| 266 | Core::Crypto::KeyManager keys; | 305 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 267 | }; | 306 | }; |
| 268 | 307 | ||
| 269 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 308 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp index 2df30acee..0d6d244f4 100644 --- a/src/core/hle/service/eupld/eupld.cpp +++ b/src/core/hle/service/eupld/eupld.cpp | |||
| @@ -19,6 +19,7 @@ public: | |||
| 19 | {1, nullptr, "ImportCrt"}, | 19 | {1, nullptr, "ImportCrt"}, |
| 20 | {2, nullptr, "ImportPki"}, | 20 | {2, nullptr, "ImportPki"}, |
| 21 | {3, nullptr, "SetAutoUpload"}, | 21 | {3, nullptr, "SetAutoUpload"}, |
| 22 | {4, nullptr, "GetAutoUpload"}, | ||
| 22 | }; | 23 | }; |
| 23 | // clang-format on | 24 | // clang-format on |
| 24 | 25 | ||
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 68f259b70..b7adaffc7 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -25,9 +25,13 @@ public: | |||
| 25 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, | 25 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, |
| 26 | {10102, nullptr, "UpdateFriendInfo"}, | 26 | {10102, nullptr, "UpdateFriendInfo"}, |
| 27 | {10110, nullptr, "GetFriendProfileImage"}, | 27 | {10110, nullptr, "GetFriendProfileImage"}, |
| 28 | {10120, nullptr, "Unknown10120"}, | ||
| 29 | {10121, nullptr, "Unknown10121"}, | ||
| 28 | {10200, nullptr, "SendFriendRequestForApplication"}, | 30 | {10200, nullptr, "SendFriendRequestForApplication"}, |
| 29 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, | 31 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, |
| 30 | {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, | 32 | {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, |
| 33 | {10420, nullptr, "Unknown10420"}, | ||
| 34 | {10421, nullptr, "Unknown10421"}, | ||
| 31 | {10500, nullptr, "GetProfileList"}, | 35 | {10500, nullptr, "GetProfileList"}, |
| 32 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, | 36 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, |
| 33 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, | 37 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, |
| @@ -97,6 +101,8 @@ public: | |||
| 97 | {30900, nullptr, "SendFriendInvitation"}, | 101 | {30900, nullptr, "SendFriendInvitation"}, |
| 98 | {30910, nullptr, "ReadFriendInvitation"}, | 102 | {30910, nullptr, "ReadFriendInvitation"}, |
| 99 | {30911, nullptr, "ReadAllFriendInvitations"}, | 103 | {30911, nullptr, "ReadAllFriendInvitations"}, |
| 104 | {40100, nullptr, "Unknown40100"}, | ||
| 105 | {40400, nullptr, "Unknown40400"}, | ||
| 100 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, | 106 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, |
| 101 | }; | 107 | }; |
| 102 | // clang-format on | 108 | // clang-format on |
diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp index 24910ac6c..401e0b208 100644 --- a/src/core/hle/service/grc/grc.cpp +++ b/src/core/hle/service/grc/grc.cpp | |||
| @@ -17,6 +17,9 @@ public: | |||
| 17 | static const FunctionInfo functions[] = { | 17 | static const FunctionInfo functions[] = { |
| 18 | {1, nullptr, "OpenContinuousRecorder"}, | 18 | {1, nullptr, "OpenContinuousRecorder"}, |
| 19 | {2, nullptr, "OpenGameMovieTrimmer"}, | 19 | {2, nullptr, "OpenGameMovieTrimmer"}, |
| 20 | {3, nullptr, "OpenOffscreenRecorder"}, | ||
| 21 | {101, nullptr, "CreateMovieMaker"}, | ||
| 22 | {9903, nullptr, "SetOffscreenRecordingMarker"} | ||
| 20 | }; | 23 | }; |
| 21 | // clang-format on | 24 | // clang-format on |
| 22 | 25 | ||
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 1f2131ec8..cb35919e9 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -23,7 +23,7 @@ void Controller_DebugPad::OnRelease() {} | |||
| 23 | 23 | ||
| 24 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 24 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 25 | std::size_t size) { | 25 | std::size_t size) { |
| 26 | shared_memory.header.timestamp = core_timing.GetTicks(); | 26 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 27 | shared_memory.header.total_entry_count = 17; | 27 | shared_memory.header.total_entry_count = 17; |
| 28 | 28 | ||
| 29 | if (!IsControllerActivated()) { | 29 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 6e990dd00..b7b7bfeae 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -19,7 +19,7 @@ void Controller_Gesture::OnRelease() {} | |||
| 19 | 19 | ||
| 20 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | shared_memory.header.timestamp = core_timing.GetTicks(); | 22 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 23 | shared_memory.header.total_entry_count = 17; | 23 | shared_memory.header.total_entry_count = 17; |
| 24 | 24 | ||
| 25 | if (!IsControllerActivated()) { | 25 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 9a8d354ba..feae89525 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -21,7 +21,7 @@ void Controller_Keyboard::OnRelease() {} | |||
| 21 | 21 | ||
| 22 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 22 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 23 | std::size_t size) { | 23 | std::size_t size) { |
| 24 | shared_memory.header.timestamp = core_timing.GetTicks(); | 24 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 25 | shared_memory.header.total_entry_count = 17; | 25 | shared_memory.header.total_entry_count = 17; |
| 26 | 26 | ||
| 27 | if (!IsControllerActivated()) { | 27 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 93d88ea50..ac40989c5 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -19,7 +19,7 @@ void Controller_Mouse::OnRelease() {} | |||
| 19 | 19 | ||
| 20 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | shared_memory.header.timestamp = core_timing.GetTicks(); | 22 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 23 | shared_memory.header.total_entry_count = 17; | 23 | shared_memory.header.total_entry_count = 17; |
| 24 | 24 | ||
| 25 | if (!IsControllerActivated()) { | 25 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c55d900e2..ef67ad690 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -328,7 +328,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 328 | const auto& last_entry = | 328 | const auto& last_entry = |
| 329 | main_controller->npad[main_controller->common.last_entry_index]; | 329 | main_controller->npad[main_controller->common.last_entry_index]; |
| 330 | 330 | ||
| 331 | main_controller->common.timestamp = core_timing.GetTicks(); | 331 | main_controller->common.timestamp = core_timing.GetCPUTicks(); |
| 332 | main_controller->common.last_entry_index = | 332 | main_controller->common.last_entry_index = |
| 333 | (main_controller->common.last_entry_index + 1) % 17; | 333 | (main_controller->common.last_entry_index + 1) % 17; |
| 334 | 334 | ||
| @@ -566,6 +566,14 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) { | |||
| 566 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; | 566 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; |
| 567 | } | 567 | } |
| 568 | 568 | ||
| 569 | void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { | ||
| 570 | gyroscope_zero_drift_mode = drift_mode; | ||
| 571 | } | ||
| 572 | |||
| 573 | Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const { | ||
| 574 | return gyroscope_zero_drift_mode; | ||
| 575 | } | ||
| 576 | |||
| 569 | void Controller_NPad::StartLRAssignmentMode() { | 577 | void Controller_NPad::StartLRAssignmentMode() { |
| 570 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the | 578 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the |
| 571 | // controller types from boot, it doesn't really matter about showing a selection screen | 579 | // controller types from boot, it doesn't really matter about showing a selection screen |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 931f03430..5d4c58a43 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -58,6 +58,12 @@ public: | |||
| 58 | }; | 58 | }; |
| 59 | static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); | 59 | static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); |
| 60 | 60 | ||
| 61 | enum class GyroscopeZeroDriftMode : u32 { | ||
| 62 | Loose = 0, | ||
| 63 | Standard = 1, | ||
| 64 | Tight = 2, | ||
| 65 | }; | ||
| 66 | |||
| 61 | enum class NpadHoldType : u64 { | 67 | enum class NpadHoldType : u64 { |
| 62 | Vertical = 0, | 68 | Vertical = 0, |
| 63 | Horizontal = 1, | 69 | Horizontal = 1, |
| @@ -117,6 +123,8 @@ public: | |||
| 117 | 123 | ||
| 118 | void ConnectNPad(u32 npad_id); | 124 | void ConnectNPad(u32 npad_id); |
| 119 | void DisconnectNPad(u32 npad_id); | 125 | void DisconnectNPad(u32 npad_id); |
| 126 | void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); | ||
| 127 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; | ||
| 120 | LedPattern GetLedPattern(u32 npad_id); | 128 | LedPattern GetLedPattern(u32 npad_id); |
| 121 | void SetVibrationEnabled(bool can_vibrate); | 129 | void SetVibrationEnabled(bool can_vibrate); |
| 122 | bool IsVibrationEnabled() const; | 130 | bool IsVibrationEnabled() const; |
| @@ -324,8 +332,8 @@ private: | |||
| 324 | std::array<Kernel::EventPair, 10> styleset_changed_events; | 332 | std::array<Kernel::EventPair, 10> styleset_changed_events; |
| 325 | Vibration last_processed_vibration{}; | 333 | Vibration last_processed_vibration{}; |
| 326 | std::array<ControllerHolder, 10> connected_controllers{}; | 334 | std::array<ControllerHolder, 10> connected_controllers{}; |
| 335 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; | ||
| 327 | bool can_controllers_vibrate{true}; | 336 | bool can_controllers_vibrate{true}; |
| 328 | |||
| 329 | std::array<ControllerPad, 10> npad_pad_states{}; | 337 | std::array<ControllerPad, 10> npad_pad_states{}; |
| 330 | bool is_in_lr_assignment_mode{false}; | 338 | bool is_in_lr_assignment_mode{false}; |
| 331 | Core::System& system; | 339 | Core::System& system; |
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 9e527d176..e7483bfa2 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp | |||
| @@ -23,7 +23,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | CommonHeader header{}; | 25 | CommonHeader header{}; |
| 26 | header.timestamp = core_timing.GetTicks(); | 26 | header.timestamp = core_timing.GetCPUTicks(); |
| 27 | header.total_entry_count = 17; | 27 | header.total_entry_count = 17; |
| 28 | header.entry_count = 0; | 28 | header.entry_count = 0; |
| 29 | header.last_entry_index = 0; | 29 | header.last_entry_index = 0; |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 1c6e55566..e326f8f5c 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -22,7 +22,7 @@ void Controller_Touchscreen::OnRelease() {} | |||
| 22 | 22 | ||
| 23 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 23 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 24 | std::size_t size) { | 24 | std::size_t size) { |
| 25 | shared_memory.header.timestamp = core_timing.GetTicks(); | 25 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 26 | shared_memory.header.total_entry_count = 17; | 26 | shared_memory.header.total_entry_count = 17; |
| 27 | 27 | ||
| 28 | if (!IsControllerActivated()) { | 28 | if (!IsControllerActivated()) { |
| @@ -49,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 49 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | 49 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; |
| 50 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | 50 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; |
| 51 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | 51 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; |
| 52 | const u64 tick = core_timing.GetTicks(); | 52 | const u64 tick = core_timing.GetCPUTicks(); |
| 53 | touch_entry.delta_time = tick - last_touch; | 53 | touch_entry.delta_time = tick - last_touch; |
| 54 | last_touch = tick; | 54 | last_touch = tick; |
| 55 | touch_entry.finger = Settings::values.touchscreen.finger; | 55 | touch_entry.finger = Settings::values.touchscreen.finger; |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 27511b27b..2503ef241 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -20,7 +20,7 @@ void Controller_XPad::OnRelease() {} | |||
| 20 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | for (auto& xpad_entry : shared_memory.shared_memory_entries) { | 22 | for (auto& xpad_entry : shared_memory.shared_memory_entries) { |
| 23 | xpad_entry.header.timestamp = core_timing.GetTicks(); | 23 | xpad_entry.header.timestamp = core_timing.GetCPUTicks(); |
| 24 | xpad_entry.header.total_entry_count = 17; | 24 | xpad_entry.header.total_entry_count = 17; |
| 25 | 25 | ||
| 26 | if (!IsControllerActivated()) { | 26 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index c84cb1483..e9020e0dc 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -39,11 +39,9 @@ namespace Service::HID { | |||
| 39 | 39 | ||
| 40 | // Updating period for each HID device. | 40 | // Updating period for each HID device. |
| 41 | // TODO(ogniK): Find actual polling rate of hid | 41 | // TODO(ogniK): Find actual polling rate of hid |
| 42 | constexpr s64 pad_update_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 66); | 42 | constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66); |
| 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = | 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100); |
| 44 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); | 44 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100); |
| 45 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = | ||
| 46 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); | ||
| 47 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 48 | 46 | ||
| 49 | IAppletResource::IAppletResource(Core::System& system) | 47 | IAppletResource::IAppletResource(Core::System& system) |
| @@ -78,8 +76,8 @@ IAppletResource::IAppletResource(Core::System& system) | |||
| 78 | 76 | ||
| 79 | // Register update callbacks | 77 | // Register update callbacks |
| 80 | pad_update_event = | 78 | pad_update_event = |
| 81 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { | 79 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) { |
| 82 | UpdateControllers(userdata, cycles_late); | 80 | UpdateControllers(userdata, ns_late); |
| 83 | }); | 81 | }); |
| 84 | 82 | ||
| 85 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) | 83 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) |
| @@ -109,7 +107,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 109 | rb.PushCopyObjects(shared_mem); | 107 | rb.PushCopyObjects(shared_mem); |
| 110 | } | 108 | } |
| 111 | 109 | ||
| 112 | void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { | 110 | void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { |
| 113 | auto& core_timing = system.CoreTiming(); | 111 | auto& core_timing = system.CoreTiming(); |
| 114 | 112 | ||
| 115 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | 113 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); |
| @@ -120,7 +118,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { | |||
| 120 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); | 118 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); |
| 121 | } | 119 | } |
| 122 | 120 | ||
| 123 | core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); | 121 | core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event); |
| 124 | } | 122 | } |
| 125 | 123 | ||
| 126 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 124 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { |
| @@ -161,7 +159,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 161 | {40, nullptr, "AcquireXpadIdEventHandle"}, | 159 | {40, nullptr, "AcquireXpadIdEventHandle"}, |
| 162 | {41, nullptr, "ReleaseXpadIdEventHandle"}, | 160 | {41, nullptr, "ReleaseXpadIdEventHandle"}, |
| 163 | {51, &Hid::ActivateXpad, "ActivateXpad"}, | 161 | {51, &Hid::ActivateXpad, "ActivateXpad"}, |
| 164 | {55, nullptr, "GetXpadIds"}, | 162 | {55, &Hid::GetXpadIDs, "GetXpadIds"}, |
| 165 | {56, nullptr, "ActivateJoyXpad"}, | 163 | {56, nullptr, "ActivateJoyXpad"}, |
| 166 | {58, nullptr, "GetJoyXpadLifoHandle"}, | 164 | {58, nullptr, "GetJoyXpadLifoHandle"}, |
| 167 | {59, nullptr, "GetJoyXpadIds"}, | 165 | {59, nullptr, "GetJoyXpadIds"}, |
| @@ -185,8 +183,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 185 | {77, nullptr, "GetAccelerometerPlayMode"}, | 183 | {77, nullptr, "GetAccelerometerPlayMode"}, |
| 186 | {78, nullptr, "ResetAccelerometerPlayMode"}, | 184 | {78, nullptr, "ResetAccelerometerPlayMode"}, |
| 187 | {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, | 185 | {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, |
| 188 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, | 186 | {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, |
| 189 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, | 187 | {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, |
| 190 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, | 188 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, |
| 191 | {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, | 189 | {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, |
| 192 | {91, &Hid::ActivateGesture, "ActivateGesture"}, | 190 | {91, &Hid::ActivateGesture, "ActivateGesture"}, |
| @@ -230,15 +228,15 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 230 | {211, nullptr, "IsVibrationDeviceMounted"}, | 228 | {211, nullptr, "IsVibrationDeviceMounted"}, |
| 231 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, | 229 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, |
| 232 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, | 230 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, |
| 233 | {302, nullptr, "StopConsoleSixAxisSensor"}, | 231 | {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, |
| 234 | {303, nullptr, "ActivateSevenSixAxisSensor"}, | 232 | {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, |
| 235 | {304, nullptr, "StartSevenSixAxisSensor"}, | 233 | {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, |
| 236 | {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, | 234 | {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, |
| 237 | {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, | 235 | {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, |
| 238 | {307, nullptr, "FinalizeSevenSixAxisSensor"}, | 236 | {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, |
| 239 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, | 237 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, |
| 240 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, | 238 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, |
| 241 | {310, nullptr, "ResetSevenSixAxisSensorTimestamp"}, | 239 | {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, |
| 242 | {400, nullptr, "IsUsbFullKeyControllerEnabled"}, | 240 | {400, nullptr, "IsUsbFullKeyControllerEnabled"}, |
| 243 | {401, nullptr, "EnableUsbFullKeyController"}, | 241 | {401, nullptr, "EnableUsbFullKeyController"}, |
| 244 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, | 242 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, |
| @@ -319,6 +317,17 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { | |||
| 319 | rb.Push(RESULT_SUCCESS); | 317 | rb.Push(RESULT_SUCCESS); |
| 320 | } | 318 | } |
| 321 | 319 | ||
| 320 | void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { | ||
| 321 | IPC::RequestParser rp{ctx}; | ||
| 322 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 323 | |||
| 324 | LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 325 | |||
| 326 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 327 | rb.Push(RESULT_SUCCESS); | ||
| 328 | rb.Push(0); | ||
| 329 | } | ||
| 330 | |||
| 322 | void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { | 331 | void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { |
| 323 | IPC::RequestParser rp{ctx}; | 332 | IPC::RequestParser rp{ctx}; |
| 324 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 333 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| @@ -363,6 +372,15 @@ void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { | |||
| 363 | rb.Push(RESULT_SUCCESS); | 372 | rb.Push(RESULT_SUCCESS); |
| 364 | } | 373 | } |
| 365 | 374 | ||
| 375 | void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { | ||
| 376 | IPC::RequestParser rp{ctx}; | ||
| 377 | const auto flags{rp.Pop<u32>()}; | ||
| 378 | LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); | ||
| 379 | |||
| 380 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 381 | rb.Push(RESULT_SUCCESS); | ||
| 382 | } | ||
| 383 | |||
| 366 | void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { | 384 | void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { |
| 367 | IPC::RequestParser rp{ctx}; | 385 | IPC::RequestParser rp{ctx}; |
| 368 | const auto unknown{rp.Pop<u32>()}; | 386 | const auto unknown{rp.Pop<u32>()}; |
| @@ -402,15 +420,59 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 402 | rb.Push(RESULT_SUCCESS); | 420 | rb.Push(RESULT_SUCCESS); |
| 403 | } | 421 | } |
| 404 | 422 | ||
| 423 | void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 424 | IPC::RequestParser rp{ctx}; | ||
| 425 | const auto handle{rp.Pop<u32>()}; | ||
| 426 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 427 | |||
| 428 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, | ||
| 429 | applet_resource_user_id); | ||
| 430 | |||
| 431 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 432 | rb.Push(RESULT_SUCCESS); | ||
| 433 | } | ||
| 434 | |||
| 405 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | 435 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { |
| 406 | IPC::RequestParser rp{ctx}; | 436 | IPC::RequestParser rp{ctx}; |
| 407 | const auto handle{rp.Pop<u32>()}; | 437 | const auto handle{rp.Pop<u32>()}; |
| 408 | const auto drift_mode{rp.Pop<u32>()}; | 438 | const auto drift_mode{rp.Pop<u32>()}; |
| 409 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 439 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 410 | 440 | ||
| 411 | LOG_WARNING(Service_HID, | 441 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 412 | "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, | 442 | .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode}); |
| 413 | drift_mode, applet_resource_user_id); | 443 | |
| 444 | LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, | ||
| 445 | drift_mode, applet_resource_user_id); | ||
| 446 | |||
| 447 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 448 | rb.Push(RESULT_SUCCESS); | ||
| 449 | } | ||
| 450 | |||
| 451 | void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | ||
| 452 | IPC::RequestParser rp{ctx}; | ||
| 453 | const auto handle{rp.Pop<u32>()}; | ||
| 454 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 455 | |||
| 456 | LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, | ||
| 457 | applet_resource_user_id); | ||
| 458 | |||
| 459 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 460 | rb.Push(RESULT_SUCCESS); | ||
| 461 | rb.Push<u32>( | ||
| 462 | static_cast<u32>(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 463 | .GetGyroscopeZeroDriftMode())); | ||
| 464 | } | ||
| 465 | |||
| 466 | void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | ||
| 467 | IPC::RequestParser rp{ctx}; | ||
| 468 | const auto handle{rp.Pop<u32>()}; | ||
| 469 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 470 | |||
| 471 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 472 | .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); | ||
| 473 | |||
| 474 | LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, | ||
| 475 | applet_resource_user_id); | ||
| 414 | 476 | ||
| 415 | IPC::ResponseBuilder rb{ctx, 2}; | 477 | IPC::ResponseBuilder rb{ctx, 2}; |
| 416 | rb.Push(RESULT_SUCCESS); | 478 | rb.Push(RESULT_SUCCESS); |
| @@ -821,33 +883,35 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 821 | rb.Push(RESULT_SUCCESS); | 883 | rb.Push(RESULT_SUCCESS); |
| 822 | } | 884 | } |
| 823 | 885 | ||
| 824 | void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | 886 | void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 825 | IPC::RequestParser rp{ctx}; | 887 | IPC::RequestParser rp{ctx}; |
| 826 | const auto handle{rp.Pop<u32>()}; | 888 | const auto handle{rp.Pop<u32>()}; |
| 889 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 827 | 890 | ||
| 828 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle); | 891 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, |
| 892 | applet_resource_user_id); | ||
| 829 | 893 | ||
| 830 | IPC::ResponseBuilder rb{ctx, 2}; | 894 | IPC::ResponseBuilder rb{ctx, 2}; |
| 831 | rb.Push(RESULT_SUCCESS); | 895 | rb.Push(RESULT_SUCCESS); |
| 832 | } | 896 | } |
| 833 | 897 | ||
| 834 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { | 898 | void Hid::ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 835 | IPC::RequestParser rp{ctx}; | 899 | IPC::RequestParser rp{ctx}; |
| 836 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 900 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 837 | const auto unknown{rp.Pop<u32>()}; | ||
| 838 | 901 | ||
| 839 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}", | 902 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", |
| 840 | applet_resource_user_id, unknown); | 903 | applet_resource_user_id); |
| 841 | 904 | ||
| 842 | IPC::ResponseBuilder rb{ctx, 2}; | 905 | IPC::ResponseBuilder rb{ctx, 2}; |
| 843 | rb.Push(RESULT_SUCCESS); | 906 | rb.Push(RESULT_SUCCESS); |
| 844 | } | 907 | } |
| 845 | 908 | ||
| 846 | void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { | 909 | void Hid::StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 847 | IPC::RequestParser rp{ctx}; | 910 | IPC::RequestParser rp{ctx}; |
| 848 | const auto unknown{rp.Pop<u32>()}; | 911 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 849 | 912 | ||
| 850 | LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown); | 913 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", |
| 914 | applet_resource_user_id); | ||
| 851 | 915 | ||
| 852 | IPC::ResponseBuilder rb{ctx, 2}; | 916 | IPC::ResponseBuilder rb{ctx, 2}; |
| 853 | rb.Push(RESULT_SUCCESS); | 917 | rb.Push(RESULT_SUCCESS); |
| @@ -871,10 +935,46 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 871 | rb.Push(RESULT_SUCCESS); | 935 | rb.Push(RESULT_SUCCESS); |
| 872 | } | 936 | } |
| 873 | 937 | ||
| 874 | void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { | 938 | void Hid::FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 875 | IPC::RequestParser rp{ctx}; | 939 | IPC::RequestParser rp{ctx}; |
| 876 | const auto flags{rp.Pop<u32>()}; | 940 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 877 | LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); | 941 | |
| 942 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 943 | applet_resource_user_id); | ||
| 944 | |||
| 945 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 946 | rb.Push(RESULT_SUCCESS); | ||
| 947 | } | ||
| 948 | |||
| 949 | void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) { | ||
| 950 | IPC::RequestParser rp{ctx}; | ||
| 951 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 952 | |||
| 953 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 954 | applet_resource_user_id); | ||
| 955 | |||
| 956 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 957 | rb.Push(RESULT_SUCCESS); | ||
| 958 | } | ||
| 959 | |||
| 960 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { | ||
| 961 | IPC::RequestParser rp{ctx}; | ||
| 962 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 963 | const auto is_palma_all_connectable{rp.Pop<bool>()}; | ||
| 964 | |||
| 965 | LOG_WARNING(Service_HID, | ||
| 966 | "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", | ||
| 967 | applet_resource_user_id, is_palma_all_connectable); | ||
| 968 | |||
| 969 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 970 | rb.Push(RESULT_SUCCESS); | ||
| 971 | } | ||
| 972 | |||
| 973 | void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { | ||
| 974 | IPC::RequestParser rp{ctx}; | ||
| 975 | const auto palma_boost_mode{rp.Pop<bool>()}; | ||
| 976 | |||
| 977 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); | ||
| 878 | 978 | ||
| 879 | IPC::ResponseBuilder rb{ctx, 2}; | 979 | IPC::ResponseBuilder rb{ctx, 2}; |
| 880 | rb.Push(RESULT_SUCCESS); | 980 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c8ed4ad8b..6fb048360 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -86,14 +86,19 @@ public: | |||
| 86 | private: | 86 | private: |
| 87 | void CreateAppletResource(Kernel::HLERequestContext& ctx); | 87 | void CreateAppletResource(Kernel::HLERequestContext& ctx); |
| 88 | void ActivateXpad(Kernel::HLERequestContext& ctx); | 88 | void ActivateXpad(Kernel::HLERequestContext& ctx); |
| 89 | void GetXpadIDs(Kernel::HLERequestContext& ctx); | ||
| 89 | void ActivateDebugPad(Kernel::HLERequestContext& ctx); | 90 | void ActivateDebugPad(Kernel::HLERequestContext& ctx); |
| 90 | void ActivateTouchScreen(Kernel::HLERequestContext& ctx); | 91 | void ActivateTouchScreen(Kernel::HLERequestContext& ctx); |
| 91 | void ActivateMouse(Kernel::HLERequestContext& ctx); | 92 | void ActivateMouse(Kernel::HLERequestContext& ctx); |
| 92 | void ActivateKeyboard(Kernel::HLERequestContext& ctx); | 93 | void ActivateKeyboard(Kernel::HLERequestContext& ctx); |
| 94 | void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); | ||
| 93 | void ActivateGesture(Kernel::HLERequestContext& ctx); | 95 | void ActivateGesture(Kernel::HLERequestContext& ctx); |
| 94 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); | 96 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); |
| 95 | void StartSixAxisSensor(Kernel::HLERequestContext& ctx); | 97 | void StartSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 98 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); | ||
| 96 | void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | 99 | void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); |
| 100 | void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | ||
| 101 | void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | ||
| 97 | void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); | 102 | void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); |
| 98 | void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); | 103 | void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); |
| 99 | void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); | 104 | void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); |
| @@ -125,12 +130,15 @@ private: | |||
| 125 | void IsVibrationPermitted(Kernel::HLERequestContext& ctx); | 130 | void IsVibrationPermitted(Kernel::HLERequestContext& ctx); |
| 126 | void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); | 131 | void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 127 | void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); | 132 | void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 128 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); | 133 | void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 129 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); | 134 | void ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 130 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | 135 | void StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 131 | void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 136 | void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 132 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 137 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 133 | void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); | 138 | void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 139 | void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); | ||
| 140 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); | ||
| 141 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | ||
| 134 | 142 | ||
| 135 | std::shared_ptr<IAppletResource> applet_resource; | 143 | std::shared_ptr<IAppletResource> applet_resource; |
| 136 | Core::System& system; | 144 | Core::System& system; |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 36ed6f7da..e82fd031b 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { | |||
| 98 | 98 | ||
| 99 | IPC::ResponseBuilder rb{ctx, 5}; | 99 | IPC::ResponseBuilder rb{ctx, 5}; |
| 100 | rb.Push(RESULT_SUCCESS); | 100 | rb.Push(RESULT_SUCCESS); |
| 101 | rb.PushRaw<u64>(system.CoreTiming().GetTicks()); | 101 | rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); |
| 102 | rb.PushRaw<u32>(0); | 102 | rb.PushRaw<u32>(0); |
| 103 | } | 103 | } |
| 104 | 104 | ||
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index e8f9f2d29..17350b403 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp | |||
| @@ -47,6 +47,7 @@ public: | |||
| 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, | 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, |
| 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, | 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, |
| 49 | {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, | 49 | {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, |
| 50 | {29, nullptr, "IsAutoBrightnessControlSupported"}, | ||
| 50 | }; | 51 | }; |
| 51 | // clang-format on | 52 | // clang-format on |
| 52 | 53 | ||
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 92adde6d4..49972cd69 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -69,6 +69,7 @@ public: | |||
| 69 | {101, nullptr, "GetNetworkInfoLatestUpdate"}, | 69 | {101, nullptr, "GetNetworkInfoLatestUpdate"}, |
| 70 | {102, nullptr, "Scan"}, | 70 | {102, nullptr, "Scan"}, |
| 71 | {103, nullptr, "ScanPrivate"}, | 71 | {103, nullptr, "ScanPrivate"}, |
| 72 | {104, nullptr, "SetWirelessControllerRestriction"}, | ||
| 72 | {200, nullptr, "OpenAccessPoint"}, | 73 | {200, nullptr, "OpenAccessPoint"}, |
| 73 | {201, nullptr, "CloseAccessPoint"}, | 74 | {201, nullptr, "CloseAccessPoint"}, |
| 74 | {202, nullptr, "CreateNetwork"}, | 75 | {202, nullptr, "CreateNetwork"}, |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 6ad3be1b3..64a526b9e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -39,42 +39,61 @@ constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; | |||
| 39 | constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; | 39 | constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; |
| 40 | constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; | 40 | constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; |
| 41 | 41 | ||
| 42 | constexpr std::size_t TEXT_INDEX{0}; | ||
| 43 | constexpr std::size_t RO_INDEX{1}; | ||
| 44 | constexpr std::size_t DATA_INDEX{2}; | ||
| 45 | |||
| 46 | struct NRRCertification { | ||
| 47 | u64_le application_id_mask; | ||
| 48 | u64_le application_id_pattern; | ||
| 49 | INSERT_PADDING_BYTES(0x10); | ||
| 50 | std::array<u8, 0x100> public_key; // Also known as modulus | ||
| 51 | std::array<u8, 0x100> signature; | ||
| 52 | }; | ||
| 53 | static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size."); | ||
| 54 | |||
| 42 | struct NRRHeader { | 55 | struct NRRHeader { |
| 43 | u32_le magic; | 56 | u32_le magic; |
| 44 | INSERT_PADDING_BYTES(12); | 57 | u32_le certification_signature_key_generation; // 9.0.0+ |
| 45 | u64_le title_id_mask; | 58 | INSERT_PADDING_WORDS(2); |
| 46 | u64_le title_id_pattern; | 59 | NRRCertification certification; |
| 47 | INSERT_PADDING_BYTES(16); | 60 | std::array<u8, 0x100> signature; |
| 48 | std::array<u8, 0x100> modulus; | 61 | u64_le application_id; |
| 49 | std::array<u8, 0x100> signature_1; | ||
| 50 | std::array<u8, 0x100> signature_2; | ||
| 51 | u64_le title_id; | ||
| 52 | u32_le size; | 62 | u32_le size; |
| 53 | INSERT_PADDING_BYTES(4); | 63 | u8 nrr_kind; // 7.0.0+ |
| 64 | INSERT_PADDING_BYTES(3); | ||
| 54 | u32_le hash_offset; | 65 | u32_le hash_offset; |
| 55 | u32_le hash_count; | 66 | u32_le hash_count; |
| 56 | INSERT_PADDING_BYTES(8); | 67 | INSERT_PADDING_WORDS(2); |
| 68 | }; | ||
| 69 | static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size."); | ||
| 70 | |||
| 71 | struct SegmentHeader { | ||
| 72 | u32_le memory_offset; | ||
| 73 | u32_le memory_size; | ||
| 57 | }; | 74 | }; |
| 58 | static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); | 75 | static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size."); |
| 59 | 76 | ||
| 60 | struct NROHeader { | 77 | struct NROHeader { |
| 78 | // Switchbrew calls this "Start" (0x10) | ||
| 61 | INSERT_PADDING_WORDS(1); | 79 | INSERT_PADDING_WORDS(1); |
| 62 | u32_le mod_offset; | 80 | u32_le mod_offset; |
| 63 | INSERT_PADDING_WORDS(2); | 81 | INSERT_PADDING_WORDS(2); |
| 82 | |||
| 83 | // Switchbrew calls this "Header" (0x70) | ||
| 64 | u32_le magic; | 84 | u32_le magic; |
| 65 | u32_le version; | 85 | u32_le version; |
| 66 | u32_le nro_size; | 86 | u32_le nro_size; |
| 67 | u32_le flags; | 87 | u32_le flags; |
| 68 | u32_le text_offset; | 88 | // .text, .ro, .data |
| 69 | u32_le text_size; | 89 | std::array<SegmentHeader, 3> segment_headers; |
| 70 | u32_le ro_offset; | ||
| 71 | u32_le ro_size; | ||
| 72 | u32_le rw_offset; | ||
| 73 | u32_le rw_size; | ||
| 74 | u32_le bss_size; | 90 | u32_le bss_size; |
| 75 | INSERT_PADDING_WORDS(1); | 91 | INSERT_PADDING_WORDS(1); |
| 76 | std::array<u8, 0x20> build_id; | 92 | std::array<u8, 0x20> build_id; |
| 77 | INSERT_PADDING_BYTES(0x20); | 93 | u32_le dso_handle_offset; |
| 94 | INSERT_PADDING_WORDS(1); | ||
| 95 | // .apiInfo, .dynstr, .dynsym | ||
| 96 | std::array<SegmentHeader, 3> segment_headers_2; | ||
| 78 | }; | 97 | }; |
| 79 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); | 98 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); |
| 80 | 99 | ||
| @@ -91,6 +110,7 @@ struct NROInfo { | |||
| 91 | std::size_t data_size{}; | 110 | std::size_t data_size{}; |
| 92 | VAddr src_addr{}; | 111 | VAddr src_addr{}; |
| 93 | }; | 112 | }; |
| 113 | static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size."); | ||
| 94 | 114 | ||
| 95 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { | 115 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { |
| 96 | public: | 116 | public: |
| @@ -226,11 +246,11 @@ public: | |||
| 226 | return; | 246 | return; |
| 227 | } | 247 | } |
| 228 | 248 | ||
| 229 | if (system.CurrentProcess()->GetTitleID() != header.title_id) { | 249 | if (system.CurrentProcess()->GetTitleID() != header.application_id) { |
| 230 | LOG_ERROR(Service_LDR, | 250 | LOG_ERROR(Service_LDR, |
| 231 | "Attempting to load NRR with title ID other than current process. (actual " | 251 | "Attempting to load NRR with title ID other than current process. (actual " |
| 232 | "{:016X})!", | 252 | "{:016X})!", |
| 233 | header.title_id); | 253 | header.application_id); |
| 234 | IPC::ResponseBuilder rb{ctx, 2}; | 254 | IPC::ResponseBuilder rb{ctx, 2}; |
| 235 | rb.Push(ERROR_INVALID_NRR); | 255 | rb.Push(ERROR_INVALID_NRR); |
| 236 | return; | 256 | return; |
| @@ -348,10 +368,10 @@ public: | |||
| 348 | 368 | ||
| 349 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, | 369 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, |
| 350 | VAddr start) const { | 370 | VAddr start) const { |
| 351 | const VAddr text_start{start + nro_header.text_offset}; | 371 | const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset}; |
| 352 | const VAddr ro_start{start + nro_header.ro_offset}; | 372 | const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset}; |
| 353 | const VAddr data_start{start + nro_header.rw_offset}; | 373 | const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset}; |
| 354 | const VAddr bss_start{data_start + nro_header.rw_size}; | 374 | const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size}; |
| 355 | const VAddr bss_end_addr{ | 375 | const VAddr bss_end_addr{ |
| 356 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; | 376 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; |
| 357 | 377 | ||
| @@ -360,9 +380,12 @@ public: | |||
| 360 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); | 380 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); |
| 361 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); | 381 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); |
| 362 | }}; | 382 | }}; |
| 363 | CopyCode(nro_addr + nro_header.text_offset, text_start, nro_header.text_size); | 383 | CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start, |
| 364 | CopyCode(nro_addr + nro_header.ro_offset, ro_start, nro_header.ro_size); | 384 | nro_header.segment_headers[TEXT_INDEX].memory_size); |
| 365 | CopyCode(nro_addr + nro_header.rw_offset, data_start, nro_header.rw_size); | 385 | CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start, |
| 386 | nro_header.segment_headers[RO_INDEX].memory_size); | ||
| 387 | CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, | ||
| 388 | nro_header.segment_headers[DATA_INDEX].memory_size); | ||
| 366 | 389 | ||
| 367 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( | 390 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( |
| 368 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); | 391 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); |
| @@ -484,9 +507,11 @@ public: | |||
| 484 | } | 507 | } |
| 485 | 508 | ||
| 486 | // Track the loaded NRO | 509 | // Track the loaded NRO |
| 487 | nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, | 510 | nro.insert_or_assign(*map_result, |
| 488 | bss_size, header.text_size, header.ro_size, | 511 | NROInfo{hash, *map_result, nro_size, bss_address, bss_size, |
| 489 | header.rw_size, nro_address}); | 512 | header.segment_headers[TEXT_INDEX].memory_size, |
| 513 | header.segment_headers[RO_INDEX].memory_size, | ||
| 514 | header.segment_headers[DATA_INDEX].memory_size, nro_address}); | ||
| 490 | 515 | ||
| 491 | // Invalidate JIT caches for the newly mapped process code | 516 | // Invalidate JIT caches for the newly mapped process code |
| 492 | system.InvalidateCpuInstructionCaches(); | 517 | system.InvalidateCpuInstructionCaches(); |
| @@ -584,11 +609,21 @@ private: | |||
| 584 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | 609 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { |
| 585 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && | 610 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && |
| 586 | header.nro_size == nro_size && header.bss_size == bss_size && | 611 | header.nro_size == nro_size && header.bss_size == bss_size && |
| 587 | header.ro_offset == header.text_offset + header.text_size && | 612 | |
| 588 | header.rw_offset == header.ro_offset + header.ro_size && | 613 | header.segment_headers[RO_INDEX].memory_offset == |
| 589 | nro_size == header.rw_offset + header.rw_size && | 614 | header.segment_headers[TEXT_INDEX].memory_offset + |
| 590 | Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && | 615 | header.segment_headers[TEXT_INDEX].memory_size && |
| 591 | Common::Is4KBAligned(header.rw_size); | 616 | |
| 617 | header.segment_headers[DATA_INDEX].memory_offset == | ||
| 618 | header.segment_headers[RO_INDEX].memory_offset + | ||
| 619 | header.segment_headers[RO_INDEX].memory_size && | ||
| 620 | |||
| 621 | nro_size == header.segment_headers[DATA_INDEX].memory_offset + | ||
| 622 | header.segment_headers[DATA_INDEX].memory_size && | ||
| 623 | |||
| 624 | Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size) && | ||
| 625 | Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size) && | ||
| 626 | Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); | ||
| 592 | } | 627 | } |
| 593 | Core::System& system; | 628 | Core::System& system; |
| 594 | }; | 629 | }; |
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp index b67081b86..3ee2374e7 100644 --- a/src/core/hle/service/lm/manager.cpp +++ b/src/core/hle/service/lm/manager.cpp | |||
| @@ -86,7 +86,8 @@ std::string FormatField(Field type, const std::vector<u8>& data) { | |||
| 86 | return Common::StringFromFixedZeroTerminatedBuffer( | 86 | return Common::StringFromFixedZeroTerminatedBuffer( |
| 87 | reinterpret_cast<const char*>(data.data()), data.size()); | 87 | reinterpret_cast<const char*>(data.data()), data.size()); |
| 88 | default: | 88 | default: |
| 89 | UNIMPLEMENTED(); | 89 | UNIMPLEMENTED_MSG("Unimplemented field type={}", type); |
| 90 | return ""; | ||
| 90 | } | 91 | } |
| 91 | } | 92 | } |
| 92 | 93 | ||
diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp index d16367f2c..113a4665c 100644 --- a/src/core/hle/service/mig/mig.cpp +++ b/src/core/hle/service/mig/mig.cpp | |||
| @@ -20,6 +20,12 @@ public: | |||
| 20 | {101, nullptr, "ResumeServer"}, | 20 | {101, nullptr, "ResumeServer"}, |
| 21 | {200, nullptr, "CreateClient"}, | 21 | {200, nullptr, "CreateClient"}, |
| 22 | {201, nullptr, "ResumeClient"}, | 22 | {201, nullptr, "ResumeClient"}, |
| 23 | {1001, nullptr, "Unknown1001"}, | ||
| 24 | {1010, nullptr, "Unknown1010"}, | ||
| 25 | {1100, nullptr, "Unknown1100"}, | ||
| 26 | {1101, nullptr, "Unknown1101"}, | ||
| 27 | {1200, nullptr, "Unknown1200"}, | ||
| 28 | {1201, nullptr, "Unknown1201"} | ||
| 23 | }; | 29 | }; |
| 24 | // clang-format on | 30 | // clang-format on |
| 25 | 31 | ||
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index def63dc8a..25c24e537 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp | |||
| @@ -14,14 +14,14 @@ public: | |||
| 14 | explicit MM_U() : ServiceFramework{"mm:u"} { | 14 | explicit MM_U() : ServiceFramework{"mm:u"} { |
| 15 | // clang-format off | 15 | // clang-format off |
| 16 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 17 | {0, &MM_U::Initialize, "Initialize"}, | 17 | {0, &MM_U::InitializeOld, "InitializeOld"}, |
| 18 | {1, &MM_U::Finalize, "Finalize"}, | 18 | {1, &MM_U::FinalizeOld, "FinalizeOld"}, |
| 19 | {2, &MM_U::SetAndWait, "SetAndWait"}, | 19 | {2, &MM_U::SetAndWaitOld, "SetAndWaitOld"}, |
| 20 | {3, &MM_U::Get, "Get"}, | 20 | {3, &MM_U::GetOld, "GetOld"}, |
| 21 | {4, &MM_U::InitializeWithId, "InitializeWithId"}, | 21 | {4, &MM_U::Initialize, "Initialize"}, |
| 22 | {5, &MM_U::FinalizeWithId, "FinalizeWithId"}, | 22 | {5, &MM_U::Finalize, "Finalize"}, |
| 23 | {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"}, | 23 | {6, &MM_U::SetAndWait, "SetAndWait"}, |
| 24 | {7, &MM_U::GetWithId, "GetWithId"}, | 24 | {7, &MM_U::Get, "Get"}, |
| 25 | }; | 25 | }; |
| 26 | // clang-format on | 26 | // clang-format on |
| 27 | 27 | ||
| @@ -29,21 +29,21 @@ public: | |||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | private: | 31 | private: |
| 32 | void Initialize(Kernel::HLERequestContext& ctx) { | 32 | void InitializeOld(Kernel::HLERequestContext& ctx) { |
| 33 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 33 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 34 | 34 | ||
| 35 | IPC::ResponseBuilder rb{ctx, 2}; | 35 | IPC::ResponseBuilder rb{ctx, 2}; |
| 36 | rb.Push(RESULT_SUCCESS); | 36 | rb.Push(RESULT_SUCCESS); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void Finalize(Kernel::HLERequestContext& ctx) { | 39 | void FinalizeOld(Kernel::HLERequestContext& ctx) { |
| 40 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 40 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 41 | 41 | ||
| 42 | IPC::ResponseBuilder rb{ctx, 2}; | 42 | IPC::ResponseBuilder rb{ctx, 2}; |
| 43 | rb.Push(RESULT_SUCCESS); | 43 | rb.Push(RESULT_SUCCESS); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | void SetAndWait(Kernel::HLERequestContext& ctx) { | 46 | void SetAndWaitOld(Kernel::HLERequestContext& ctx) { |
| 47 | IPC::RequestParser rp{ctx}; | 47 | IPC::RequestParser rp{ctx}; |
| 48 | min = rp.Pop<u32>(); | 48 | min = rp.Pop<u32>(); |
| 49 | max = rp.Pop<u32>(); | 49 | max = rp.Pop<u32>(); |
| @@ -54,7 +54,7 @@ private: | |||
| 54 | rb.Push(RESULT_SUCCESS); | 54 | rb.Push(RESULT_SUCCESS); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | void Get(Kernel::HLERequestContext& ctx) { | 57 | void GetOld(Kernel::HLERequestContext& ctx) { |
| 58 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 58 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 59 | 59 | ||
| 60 | IPC::ResponseBuilder rb{ctx, 3}; | 60 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -62,7 +62,7 @@ private: | |||
| 62 | rb.Push(current); | 62 | rb.Push(current); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | void InitializeWithId(Kernel::HLERequestContext& ctx) { | 65 | void Initialize(Kernel::HLERequestContext& ctx) { |
| 66 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 66 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 67 | 67 | ||
| 68 | IPC::ResponseBuilder rb{ctx, 3}; | 68 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -70,14 +70,14 @@ private: | |||
| 70 | rb.Push<u32>(id); // Any non zero value | 70 | rb.Push<u32>(id); // Any non zero value |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void FinalizeWithId(Kernel::HLERequestContext& ctx) { | 73 | void Finalize(Kernel::HLERequestContext& ctx) { |
| 74 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 74 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 75 | 75 | ||
| 76 | IPC::ResponseBuilder rb{ctx, 2}; | 76 | IPC::ResponseBuilder rb{ctx, 2}; |
| 77 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void SetAndWaitWithId(Kernel::HLERequestContext& ctx) { | 80 | void SetAndWait(Kernel::HLERequestContext& ctx) { |
| 81 | IPC::RequestParser rp{ctx}; | 81 | IPC::RequestParser rp{ctx}; |
| 82 | u32 input_id = rp.Pop<u32>(); | 82 | u32 input_id = rp.Pop<u32>(); |
| 83 | min = rp.Pop<u32>(); | 83 | min = rp.Pop<u32>(); |
| @@ -90,7 +90,7 @@ private: | |||
| 90 | rb.Push(RESULT_SUCCESS); | 90 | rb.Push(RESULT_SUCCESS); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | void GetWithId(Kernel::HLERequestContext& ctx) { | 93 | void Get(Kernel::HLERequestContext& ctx) { |
| 94 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 94 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 95 | 95 | ||
| 96 | IPC::ResponseBuilder rb{ctx, 3}; | 96 | IPC::ResponseBuilder rb{ctx, 3}; |
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp index ec9aae04a..e38dea1f4 100644 --- a/src/core/hle/service/ncm/ncm.cpp +++ b/src/core/hle/service/ncm/ncm.cpp | |||
| @@ -28,16 +28,16 @@ public: | |||
| 28 | {7, nullptr, "ResolveApplicationLegalInformationPath"}, | 28 | {7, nullptr, "ResolveApplicationLegalInformationPath"}, |
| 29 | {8, nullptr, "RedirectApplicationLegalInformationPath"}, | 29 | {8, nullptr, "RedirectApplicationLegalInformationPath"}, |
| 30 | {9, nullptr, "Refresh"}, | 30 | {9, nullptr, "Refresh"}, |
| 31 | {10, nullptr, "RedirectProgramPath2"}, | 31 | {10, nullptr, "RedirectApplicationProgramPath"}, |
| 32 | {11, nullptr, "Refresh2"}, | 32 | {11, nullptr, "ClearApplicationRedirection"}, |
| 33 | {12, nullptr, "DeleteProgramPath"}, | 33 | {12, nullptr, "EraseProgramRedirection"}, |
| 34 | {13, nullptr, "DeleteApplicationControlPath"}, | 34 | {13, nullptr, "EraseApplicationControlRedirection"}, |
| 35 | {14, nullptr, "DeleteApplicationHtmlDocumentPath"}, | 35 | {14, nullptr, "EraseApplicationHtmlDocumentRedirection"}, |
| 36 | {15, nullptr, "DeleteApplicationLegalInformationPath"}, | 36 | {15, nullptr, "EraseApplicationLegalInformationRedirection"}, |
| 37 | {16, nullptr, ""}, | 37 | {16, nullptr, "ResolveProgramPathForDebug"}, |
| 38 | {17, nullptr, ""}, | 38 | {17, nullptr, "RedirectProgramPathForDebug"}, |
| 39 | {18, nullptr, ""}, | 39 | {18, nullptr, "RedirectApplicationProgramPathForDebug"}, |
| 40 | {19, nullptr, ""}, | 40 | {19, nullptr, "EraseProgramRedirectionForDebug"}, |
| 41 | }; | 41 | }; |
| 42 | // clang-format on | 42 | // clang-format on |
| 43 | 43 | ||
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index b7b34ce7e..780ea30fe 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp | |||
| @@ -198,9 +198,9 @@ public: | |||
| 198 | static const FunctionInfo functions[] = { | 198 | static const FunctionInfo functions[] = { |
| 199 | {0, nullptr, "Initialize"}, | 199 | {0, nullptr, "Initialize"}, |
| 200 | {1, nullptr, "Finalize"}, | 200 | {1, nullptr, "Finalize"}, |
| 201 | {2, nullptr, "GetState"}, | 201 | {2, nullptr, "GetStateOld"}, |
| 202 | {3, nullptr, "IsNfcEnabled"}, | 202 | {3, nullptr, "IsNfcEnabledOld"}, |
| 203 | {100, nullptr, "SetNfcEnabled"}, | 203 | {100, nullptr, "SetNfcEnabledOld"}, |
| 204 | {400, nullptr, "InitializeSystem"}, | 204 | {400, nullptr, "InitializeSystem"}, |
| 205 | {401, nullptr, "FinalizeSystem"}, | 205 | {401, nullptr, "FinalizeSystem"}, |
| 206 | {402, nullptr, "GetState"}, | 206 | {402, nullptr, "GetState"}, |
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 3e4dd2f7a..886450be2 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage( | |||
| 366 | LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages); | 366 | LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages); |
| 367 | 367 | ||
| 368 | // Get language code from settings | 368 | // Get language code from settings |
| 369 | const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index); | 369 | const auto language_code = |
| 370 | Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue()); | ||
| 370 | 371 | ||
| 371 | // Convert to application language, get priority list | 372 | // Convert to application language, get priority list |
| 372 | const auto application_language = ConvertToApplicationLanguage(language_code); | 373 | const auto application_language = ConvertToApplicationLanguage(language_code); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index cc2192e5c..fba89e7a6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -25,7 +25,7 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, | |||
| 25 | case IoctlCommand::IocGetCharacteristicsCommand: | 25 | case IoctlCommand::IocGetCharacteristicsCommand: |
| 26 | return GetCharacteristics(input, output, output2, version); | 26 | return GetCharacteristics(input, output, output2, version); |
| 27 | case IoctlCommand::IocGetTPCMasksCommand: | 27 | case IoctlCommand::IocGetTPCMasksCommand: |
| 28 | return GetTPCMasks(input, output); | 28 | return GetTPCMasks(input, output, output2, version); |
| 29 | case IoctlCommand::IocGetActiveSlotMaskCommand: | 29 | case IoctlCommand::IocGetActiveSlotMaskCommand: |
| 30 | return GetActiveSlotMask(input, output); | 30 | return GetActiveSlotMask(input, output); |
| 31 | case IoctlCommand::IocZcullGetCtxSizeCommand: | 31 | case IoctlCommand::IocZcullGetCtxSizeCommand: |
| @@ -98,17 +98,22 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto | |||
| 98 | return 0; | 98 | return 0; |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { | 101 | u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, |
| 102 | std::vector<u8>& output2, IoctlVersion version) { | ||
| 102 | IoctlGpuGetTpcMasksArgs params{}; | 103 | IoctlGpuGetTpcMasksArgs params{}; |
| 103 | std::memcpy(¶ms, input.data(), input.size()); | 104 | std::memcpy(¶ms, input.data(), input.size()); |
| 104 | LOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size, | 105 | LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); |
| 105 | params.mask_buf_addr); | 106 | if (params.mask_buffer_size != 0) { |
| 106 | // TODO(ogniK): Confirm value on hardware | 107 | params.tcp_mask = 3; |
| 107 | if (params.mask_buf_size) | 108 | } |
| 108 | params.tpc_mask_size = 4 * 1; // 4 * num_gpc | 109 | |
| 109 | else | 110 | if (version == IoctlVersion::Version3) { |
| 110 | params.tpc_mask_size = 0; | 111 | std::memcpy(output.data(), input.data(), output.size()); |
| 111 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 112 | std::memcpy(output2.data(), ¶ms.tcp_mask, output2.size()); |
| 113 | } else { | ||
| 114 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 115 | } | ||
| 116 | |||
| 112 | return 0; | 117 | return 0; |
| 113 | } | 118 | } |
| 114 | 119 | ||
| @@ -195,8 +200,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o | |||
| 195 | 200 | ||
| 196 | IoctlGetGpuTime params{}; | 201 | IoctlGetGpuTime params{}; |
| 197 | std::memcpy(¶ms, input.data(), input.size()); | 202 | std::memcpy(¶ms, input.data(), input.size()); |
| 198 | const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); | 203 | params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); |
| 199 | params.gpu_time = static_cast<u64_le>(ns.count()); | ||
| 200 | std::memcpy(output.data(), ¶ms, output.size()); | 204 | std::memcpy(output.data(), ¶ms, output.size()); |
| 201 | return 0; | 205 | return 0; |
| 202 | } | 206 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 07b644ec5..ef60f72ce 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | |||
| @@ -92,16 +92,11 @@ private: | |||
| 92 | "IoctlCharacteristics is incorrect size"); | 92 | "IoctlCharacteristics is incorrect size"); |
| 93 | 93 | ||
| 94 | struct IoctlGpuGetTpcMasksArgs { | 94 | struct IoctlGpuGetTpcMasksArgs { |
| 95 | /// [in] TPC mask buffer size reserved by userspace. Should be at least | 95 | u32_le mask_buffer_size{}; |
| 96 | /// sizeof(__u32) * fls(gpc_mask) to receive TPC mask for each GPC. | 96 | INSERT_PADDING_WORDS(1); |
| 97 | /// [out] full kernel buffer size | 97 | u64_le mask_buffer_address{}; |
| 98 | u32_le mask_buf_size; | 98 | u32_le tcp_mask{}; |
| 99 | u32_le reserved; | 99 | INSERT_PADDING_WORDS(1); |
| 100 | |||
| 101 | /// [in] pointer to TPC mask buffer. It will receive one 32-bit TPC mask per GPC or 0 if | ||
| 102 | /// GPC is not enabled or not present. This parameter is ignored if mask_buf_size is 0. | ||
| 103 | u64_le mask_buf_addr; | ||
| 104 | u64_le tpc_mask_size; // Nintendo add this? | ||
| 105 | }; | 100 | }; |
| 106 | static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24, | 101 | static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24, |
| 107 | "IoctlGpuGetTpcMasksArgs is incorrect size"); | 102 | "IoctlGpuGetTpcMasksArgs is incorrect size"); |
| @@ -166,7 +161,8 @@ private: | |||
| 166 | 161 | ||
| 167 | u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, | 162 | u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, |
| 168 | std::vector<u8>& output2, IoctlVersion version); | 163 | std::vector<u8>& output2, IoctlVersion version); |
| 169 | u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); | 164 | u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2, |
| 165 | IoctlVersion version); | ||
| 170 | u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); | 166 | u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); |
| 171 | u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); | 167 | u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); |
| 172 | u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); | 168 | u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 437bc5dee..2f44d3779 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "common/scope_exit.h" | 11 | #include "common/scope_exit.h" |
| 12 | #include "common/thread.h" | ||
| 12 | #include "core/core.h" | 13 | #include "core/core.h" |
| 13 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 14 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| @@ -27,8 +28,35 @@ | |||
| 27 | 28 | ||
| 28 | namespace Service::NVFlinger { | 29 | namespace Service::NVFlinger { |
| 29 | 30 | ||
| 30 | constexpr s64 frame_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); | 31 | constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60); |
| 31 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 30); | 32 | constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30); |
| 33 | |||
| 34 | void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { | ||
| 35 | nv_flinger.SplitVSync(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void NVFlinger::SplitVSync() { | ||
| 39 | system.RegisterHostThread(); | ||
| 40 | std::string name = "yuzu:VSyncThread"; | ||
| 41 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 42 | Common::SetCurrentThreadName(name.c_str()); | ||
| 43 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 44 | s64 delay = 0; | ||
| 45 | while (is_running) { | ||
| 46 | guard->lock(); | ||
| 47 | const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 48 | Compose(); | ||
| 49 | const auto ticks = GetNextTicks(); | ||
| 50 | const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 51 | const s64 time_passed = time_end - time_start; | ||
| 52 | const s64 next_time = std::max<s64>(0, ticks - time_passed - delay); | ||
| 53 | guard->unlock(); | ||
| 54 | if (next_time > 0) { | ||
| 55 | wait_event->WaitFor(std::chrono::nanoseconds{next_time}); | ||
| 56 | } | ||
| 57 | delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time; | ||
| 58 | } | ||
| 59 | } | ||
| 32 | 60 | ||
| 33 | NVFlinger::NVFlinger(Core::System& system) : system(system) { | 61 | NVFlinger::NVFlinger(Core::System& system) : system(system) { |
| 34 | displays.emplace_back(0, "Default", system); | 62 | displays.emplace_back(0, "Default", system); |
| @@ -36,22 +64,36 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { | |||
| 36 | displays.emplace_back(2, "Edid", system); | 64 | displays.emplace_back(2, "Edid", system); |
| 37 | displays.emplace_back(3, "Internal", system); | 65 | displays.emplace_back(3, "Internal", system); |
| 38 | displays.emplace_back(4, "Null", system); | 66 | displays.emplace_back(4, "Null", system); |
| 67 | guard = std::make_shared<std::mutex>(); | ||
| 39 | 68 | ||
| 40 | // Schedule the screen composition events | 69 | // Schedule the screen composition events |
| 41 | composition_event = | 70 | composition_event = |
| 42 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { | 71 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { |
| 72 | Lock(); | ||
| 43 | Compose(); | 73 | Compose(); |
| 44 | const auto ticks = | 74 | const auto ticks = GetNextTicks(); |
| 45 | Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); | 75 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late), |
| 46 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), | ||
| 47 | composition_event); | 76 | composition_event); |
| 48 | }); | 77 | }); |
| 49 | 78 | if (system.IsMulticore()) { | |
| 50 | system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); | 79 | is_running = true; |
| 80 | wait_event = std::make_unique<Common::Event>(); | ||
| 81 | vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); | ||
| 82 | } else { | ||
| 83 | system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); | ||
| 84 | } | ||
| 51 | } | 85 | } |
| 52 | 86 | ||
| 53 | NVFlinger::~NVFlinger() { | 87 | NVFlinger::~NVFlinger() { |
| 54 | system.CoreTiming().UnscheduleEvent(composition_event, 0); | 88 | if (system.IsMulticore()) { |
| 89 | is_running = false; | ||
| 90 | wait_event->Set(); | ||
| 91 | vsync_thread->join(); | ||
| 92 | vsync_thread.reset(); | ||
| 93 | wait_event.reset(); | ||
| 94 | } else { | ||
| 95 | system.CoreTiming().UnscheduleEvent(composition_event, 0); | ||
| 96 | } | ||
| 55 | } | 97 | } |
| 56 | 98 | ||
| 57 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 99 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { |
| @@ -199,10 +241,12 @@ void NVFlinger::Compose() { | |||
| 199 | 241 | ||
| 200 | auto& gpu = system.GPU(); | 242 | auto& gpu = system.GPU(); |
| 201 | const auto& multi_fence = buffer->get().multi_fence; | 243 | const auto& multi_fence = buffer->get().multi_fence; |
| 244 | guard->unlock(); | ||
| 202 | for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { | 245 | for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { |
| 203 | const auto& fence = multi_fence.fences[fence_id]; | 246 | const auto& fence = multi_fence.fences[fence_id]; |
| 204 | gpu.WaitFence(fence.id, fence.value); | 247 | gpu.WaitFence(fence.id, fence.value); |
| 205 | } | 248 | } |
| 249 | guard->lock(); | ||
| 206 | 250 | ||
| 207 | MicroProfileFlip(); | 251 | MicroProfileFlip(); |
| 208 | 252 | ||
| @@ -223,7 +267,7 @@ void NVFlinger::Compose() { | |||
| 223 | 267 | ||
| 224 | s64 NVFlinger::GetNextTicks() const { | 268 | s64 NVFlinger::GetNextTicks() const { |
| 225 | constexpr s64 max_hertz = 120LL; | 269 | constexpr s64 max_hertz = 120LL; |
| 226 | return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; | 270 | return (1000000000 * (1LL << swap_interval)) / max_hertz; |
| 227 | } | 271 | } |
| 228 | 272 | ||
| 229 | } // namespace Service::NVFlinger | 273 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 57a21f33b..e4959a9af 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -4,15 +4,22 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 9 | #include <mutex> | ||
| 8 | #include <optional> | 10 | #include <optional> |
| 9 | #include <string> | 11 | #include <string> |
| 10 | #include <string_view> | 12 | #include <string_view> |
| 13 | #include <thread> | ||
| 11 | #include <vector> | 14 | #include <vector> |
| 12 | 15 | ||
| 13 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 14 | #include "core/hle/kernel/object.h" | 17 | #include "core/hle/kernel/object.h" |
| 15 | 18 | ||
| 19 | namespace Common { | ||
| 20 | class Event; | ||
| 21 | } // namespace Common | ||
| 22 | |||
| 16 | namespace Core::Timing { | 23 | namespace Core::Timing { |
| 17 | class CoreTiming; | 24 | class CoreTiming; |
| 18 | struct EventType; | 25 | struct EventType; |
| @@ -79,6 +86,10 @@ public: | |||
| 79 | 86 | ||
| 80 | s64 GetNextTicks() const; | 87 | s64 GetNextTicks() const; |
| 81 | 88 | ||
| 89 | std::unique_lock<std::mutex> Lock() { | ||
| 90 | return std::unique_lock{*guard}; | ||
| 91 | } | ||
| 92 | |||
| 82 | private: | 93 | private: |
| 83 | /// Finds the display identified by the specified ID. | 94 | /// Finds the display identified by the specified ID. |
| 84 | VI::Display* FindDisplay(u64 display_id); | 95 | VI::Display* FindDisplay(u64 display_id); |
| @@ -92,6 +103,10 @@ private: | |||
| 92 | /// Finds the layer identified by the specified ID in the desired display. | 103 | /// Finds the layer identified by the specified ID in the desired display. |
| 93 | const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; | 104 | const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; |
| 94 | 105 | ||
| 106 | static void VSyncThread(NVFlinger& nv_flinger); | ||
| 107 | |||
| 108 | void SplitVSync(); | ||
| 109 | |||
| 95 | std::shared_ptr<Nvidia::Module> nvdrv; | 110 | std::shared_ptr<Nvidia::Module> nvdrv; |
| 96 | 111 | ||
| 97 | std::vector<VI::Display> displays; | 112 | std::vector<VI::Display> displays; |
| @@ -108,7 +123,13 @@ private: | |||
| 108 | /// Event that handles screen composition. | 123 | /// Event that handles screen composition. |
| 109 | std::shared_ptr<Core::Timing::EventType> composition_event; | 124 | std::shared_ptr<Core::Timing::EventType> composition_event; |
| 110 | 125 | ||
| 126 | std::shared_ptr<std::mutex> guard; | ||
| 127 | |||
| 111 | Core::System& system; | 128 | Core::System& system; |
| 129 | |||
| 130 | std::unique_ptr<std::thread> vsync_thread; | ||
| 131 | std::unique_ptr<Common::Event> wait_event; | ||
| 132 | std::atomic<bool> is_running{}; | ||
| 112 | }; | 133 | }; |
| 113 | 134 | ||
| 114 | } // namespace Service::NVFlinger | 135 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 9d36ea0d0..cde3312da 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -80,8 +80,13 @@ private: | |||
| 80 | const auto user_id = rp.PopRaw<u128>(); | 80 | const auto user_id = rp.PopRaw<u128>(); |
| 81 | const auto process_id = rp.PopRaw<u64>(); | 81 | const auto process_id = rp.PopRaw<u64>(); |
| 82 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; | 82 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; |
| 83 | |||
| 83 | if constexpr (Type == Core::Reporter::PlayReportType::Old2) { | 84 | if constexpr (Type == Core::Reporter::PlayReportType::Old2) { |
| 84 | data.emplace_back(ctx.ReadBuffer(1)); | 85 | const auto read_buffer_count = |
| 86 | ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size(); | ||
| 87 | if (read_buffer_count > 1) { | ||
| 88 | data.emplace_back(ctx.ReadBuffer(1)); | ||
| 89 | } | ||
| 85 | } | 90 | } |
| 86 | 91 | ||
| 87 | LOG_DEBUG( | 92 | LOG_DEBUG( |
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index f3b4b286c..34fe2fd82 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | ||
| 6 | #include <chrono> | 7 | #include <chrono> |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 8 | #include "core/hle/ipc_helpers.h" | 9 | #include "core/hle/ipc_helpers.h" |
| @@ -31,6 +32,44 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ | |||
| 31 | LanguageCode::ZH_HANT, | 32 | LanguageCode::ZH_HANT, |
| 32 | }}; | 33 | }}; |
| 33 | 34 | ||
| 35 | enum class KeyboardLayout : u64 { | ||
| 36 | Japanese = 0, | ||
| 37 | EnglishUs = 1, | ||
| 38 | EnglishUsInternational = 2, | ||
| 39 | EnglishUk = 3, | ||
| 40 | French = 4, | ||
| 41 | FrenchCa = 5, | ||
| 42 | Spanish = 6, | ||
| 43 | SpanishLatin = 7, | ||
| 44 | German = 8, | ||
| 45 | Italian = 9, | ||
| 46 | Portuguese = 10, | ||
| 47 | Russian = 11, | ||
| 48 | Korean = 12, | ||
| 49 | ChineseSimplified = 13, | ||
| 50 | ChineseTraditional = 14, | ||
| 51 | }; | ||
| 52 | |||
| 53 | constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{ | ||
| 54 | {LanguageCode::JA, KeyboardLayout::Japanese}, | ||
| 55 | {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, | ||
| 56 | {LanguageCode::FR, KeyboardLayout::French}, | ||
| 57 | {LanguageCode::DE, KeyboardLayout::German}, | ||
| 58 | {LanguageCode::IT, KeyboardLayout::Italian}, | ||
| 59 | {LanguageCode::ES, KeyboardLayout::Spanish}, | ||
| 60 | {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified}, | ||
| 61 | {LanguageCode::KO, KeyboardLayout::Korean}, | ||
| 62 | {LanguageCode::NL, KeyboardLayout::EnglishUsInternational}, | ||
| 63 | {LanguageCode::PT, KeyboardLayout::Portuguese}, | ||
| 64 | {LanguageCode::RU, KeyboardLayout::Russian}, | ||
| 65 | {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional}, | ||
| 66 | {LanguageCode::EN_GB, KeyboardLayout::EnglishUk}, | ||
| 67 | {LanguageCode::FR_CA, KeyboardLayout::FrenchCa}, | ||
| 68 | {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, | ||
| 69 | {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, | ||
| 70 | {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, | ||
| 71 | }}; | ||
| 72 | |||
| 34 | constexpr std::size_t pre4_0_0_max_entries = 15; | 73 | constexpr std::size_t pre4_0_0_max_entries = 15; |
| 35 | constexpr std::size_t post4_0_0_max_entries = 17; | 74 | constexpr std::size_t post4_0_0_max_entries = 17; |
| 36 | 75 | ||
| @@ -50,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m | |||
| 50 | ctx.WriteBuffer(available_language_codes.data(), copy_size); | 89 | ctx.WriteBuffer(available_language_codes.data(), copy_size); |
| 51 | PushResponseLanguageCode(ctx, copy_amount); | 90 | PushResponseLanguageCode(ctx, copy_amount); |
| 52 | } | 91 | } |
| 92 | |||
| 93 | void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { | ||
| 94 | const auto language_code = available_language_codes[Settings::values.language_index.GetValue()]; | ||
| 95 | const auto key_code = | ||
| 96 | std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), | ||
| 97 | [=](const auto& element) { return element.first == language_code; }); | ||
| 98 | KeyboardLayout layout = KeyboardLayout::EnglishUs; | ||
| 99 | if (key_code == language_to_layout.cend()) { | ||
| 100 | LOG_ERROR(Service_SET, | ||
| 101 | "Could not find keyboard layout for language index {}, defaulting to English us", | ||
| 102 | Settings::values.language_index.GetValue()); | ||
| 103 | } else { | ||
| 104 | layout = key_code->second; | ||
| 105 | } | ||
| 106 | |||
| 107 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 108 | rb.Push(RESULT_SUCCESS); | ||
| 109 | ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); | ||
| 110 | } | ||
| 53 | } // Anonymous namespace | 111 | } // Anonymous namespace |
| 54 | 112 | ||
| 55 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { | 113 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { |
| @@ -105,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) { | |||
| 105 | } | 163 | } |
| 106 | 164 | ||
| 107 | void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { | 165 | void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { |
| 108 | LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); | 166 | LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue()); |
| 109 | 167 | ||
| 110 | IPC::ResponseBuilder rb{ctx, 4}; | 168 | IPC::ResponseBuilder rb{ctx, 4}; |
| 111 | rb.Push(RESULT_SUCCESS); | 169 | rb.Push(RESULT_SUCCESS); |
| 112 | rb.PushEnum(available_language_codes[Settings::values.language_index]); | 170 | rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]); |
| 113 | } | 171 | } |
| 114 | 172 | ||
| 115 | void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { | 173 | void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { |
| @@ -117,7 +175,17 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { | |||
| 117 | 175 | ||
| 118 | IPC::ResponseBuilder rb{ctx, 3}; | 176 | IPC::ResponseBuilder rb{ctx, 3}; |
| 119 | rb.Push(RESULT_SUCCESS); | 177 | rb.Push(RESULT_SUCCESS); |
| 120 | rb.Push(Settings::values.region_index); | 178 | rb.Push(Settings::values.region_index.GetValue()); |
| 179 | } | ||
| 180 | |||
| 181 | void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) { | ||
| 182 | LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); | ||
| 183 | GetKeyCodeMapImpl(ctx); | ||
| 184 | } | ||
| 185 | |||
| 186 | void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { | ||
| 187 | LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); | ||
| 188 | GetKeyCodeMapImpl(ctx); | ||
| 121 | } | 189 | } |
| 122 | 190 | ||
| 123 | SET::SET() : ServiceFramework("set") { | 191 | SET::SET() : ServiceFramework("set") { |
| @@ -130,9 +198,9 @@ SET::SET() : ServiceFramework("set") { | |||
| 130 | {4, &SET::GetRegionCode, "GetRegionCode"}, | 198 | {4, &SET::GetRegionCode, "GetRegionCode"}, |
| 131 | {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, | 199 | {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, |
| 132 | {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, | 200 | {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, |
| 133 | {7, nullptr, "GetKeyCodeMap"}, | 201 | {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"}, |
| 134 | {8, &SET::GetQuestFlag, "GetQuestFlag"}, | 202 | {8, &SET::GetQuestFlag, "GetQuestFlag"}, |
| 135 | {9, nullptr, "GetKeyCodeMap2"}, | 203 | {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, |
| 136 | {10, nullptr, "GetFirmwareVersionForDebug"}, | 204 | {10, nullptr, "GetFirmwareVersionForDebug"}, |
| 137 | }; | 205 | }; |
| 138 | // clang-format on | 206 | // clang-format on |
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 6084b345d..8ac9c169d 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h | |||
| @@ -44,6 +44,8 @@ private: | |||
| 44 | void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); | 44 | void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); |
| 45 | void GetQuestFlag(Kernel::HLERequestContext& ctx); | 45 | void GetQuestFlag(Kernel::HLERequestContext& ctx); |
| 46 | void GetRegionCode(Kernel::HLERequestContext& ctx); | 46 | void GetRegionCode(Kernel::HLERequestContext& ctx); |
| 47 | void GetKeyCodeMap(Kernel::HLERequestContext& ctx); | ||
| 48 | void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); | ||
| 47 | }; | 49 | }; |
| 48 | 50 | ||
| 49 | } // namespace Service::Set | 51 | } // namespace Service::Set |
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 6ada13be4..d872de16c 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -142,7 +142,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | |||
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | // Wake the threads waiting on the ServerPort | 144 | // Wake the threads waiting on the ServerPort |
| 145 | server_port->WakeupAllWaitingThreads(); | 145 | server_port->Signal(); |
| 146 | 146 | ||
| 147 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); | 147 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); |
| 148 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; | 148 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; |
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index e724d4ab8..865ed3b91 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp | |||
| @@ -19,7 +19,7 @@ namespace Service::SPL { | |||
| 19 | 19 | ||
| 20 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 20 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 21 | : ServiceFramework(name), module(std::move(module)), | 21 | : ServiceFramework(name), module(std::move(module)), |
| 22 | rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {} | 22 | rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {} |
| 23 | 23 | ||
| 24 | Module::Interface::~Interface() = default; | 24 | Module::Interface::~Interface() = default; |
| 25 | 25 | ||
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index 1575f0b49..59a272f4a 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp | |||
| @@ -11,9 +11,8 @@ | |||
| 11 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| 12 | 12 | ||
| 13 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { | 13 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { |
| 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{ |
| 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 16 | Core::Hardware::CNTFREQ)}; | ||
| 17 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; | 16 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; |
| 18 | 17 | ||
| 19 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { | 18 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { |
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index 44d5bc651..8baaa2a6a 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp | |||
| @@ -11,9 +11,8 @@ | |||
| 11 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| 12 | 12 | ||
| 13 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { | 13 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { |
| 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{ |
| 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 16 | Core::Hardware::CNTFREQ)}; | ||
| 17 | 16 | ||
| 18 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; | 17 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; |
| 19 | } | 18 | } |
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index cc1dbd575..13e4b3818 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -241,9 +241,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe | |||
| 241 | const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; | 241 | const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; |
| 242 | 242 | ||
| 243 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { | 243 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { |
| 244 | const auto ticks{Clock::TimeSpanType::FromTicks( | 244 | const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), |
| 245 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 245 | Core::Hardware::CNTFREQ)}; |
| 246 | Core::Hardware::CNTFREQ)}; | ||
| 247 | const s64 base_time_point{context.offset + current_time_point.time_point - | 246 | const s64 base_time_point{context.offset + current_time_point.time_point - |
| 248 | ticks.ToSeconds()}; | 247 | ticks.ToSeconds()}; |
| 249 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; | 248 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; |
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index 999ec1e51..e0ae9f874 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp | |||
| @@ -30,8 +30,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system, | |||
| 30 | const Common::UUID& clock_source_id, | 30 | const Common::UUID& clock_source_id, |
| 31 | Clock::TimeSpanType current_time_point) { | 31 | Clock::TimeSpanType current_time_point) { |
| 32 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( | 32 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( |
| 33 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 33 | system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 34 | Core::Hardware::CNTFREQ)}; | ||
| 35 | const Clock::SteadyClockContext context{ | 34 | const Clock::SteadyClockContext context{ |
| 36 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), | 35 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), |
| 37 | clock_source_id}; | 36 | clock_source_id}; |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 67b45e7c0..ea7b4ae13 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -511,6 +511,7 @@ private: | |||
| 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, | 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, |
| 512 | static_cast<u32>(transaction), flags); | 512 | static_cast<u32>(transaction), flags); |
| 513 | 513 | ||
| 514 | nv_flinger->Lock(); | ||
| 514 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 515 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 515 | 516 | ||
| 516 | switch (transaction) { | 517 | switch (transaction) { |
| @@ -518,9 +519,9 @@ private: | |||
| 518 | IGBPConnectRequestParcel request{ctx.ReadBuffer()}; | 519 | IGBPConnectRequestParcel request{ctx.ReadBuffer()}; |
| 519 | IGBPConnectResponseParcel response{ | 520 | IGBPConnectResponseParcel response{ |
| 520 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) * | 521 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) * |
| 521 | Settings::values.resolution_factor), | 522 | Settings::values.resolution_factor.GetValue()), |
| 522 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * | 523 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * |
| 523 | Settings::values.resolution_factor)}; | 524 | Settings::values.resolution_factor.GetValue())}; |
| 524 | ctx.WriteBuffer(response.Serialize()); | 525 | ctx.WriteBuffer(response.Serialize()); |
| 525 | break; | 526 | break; |
| 526 | } | 527 | } |
| @@ -550,6 +551,7 @@ private: | |||
| 550 | [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, | 551 | [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, |
| 551 | Kernel::ThreadWakeupReason reason) { | 552 | Kernel::ThreadWakeupReason reason) { |
| 552 | // Repeat TransactParcel DequeueBuffer when a buffer is available | 553 | // Repeat TransactParcel DequeueBuffer when a buffer is available |
| 554 | nv_flinger->Lock(); | ||
| 553 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 555 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 554 | auto result = buffer_queue.DequeueBuffer(width, height); | 556 | auto result = buffer_queue.DequeueBuffer(width, height); |
| 555 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); | 557 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); |
| @@ -747,14 +749,14 @@ private: | |||
| 747 | 749 | ||
| 748 | if (Settings::values.use_docked_mode) { | 750 | if (Settings::values.use_docked_mode) { |
| 749 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * | 751 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * |
| 750 | static_cast<u32>(Settings::values.resolution_factor)); | 752 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 751 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * | 753 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * |
| 752 | static_cast<u32>(Settings::values.resolution_factor)); | 754 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 753 | } else { | 755 | } else { |
| 754 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * | 756 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * |
| 755 | static_cast<u32>(Settings::values.resolution_factor)); | 757 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 756 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * | 758 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * |
| 757 | static_cast<u32>(Settings::values.resolution_factor)); | 759 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 758 | } | 760 | } |
| 759 | 761 | ||
| 760 | rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. | 762 | rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. |
| @@ -1029,9 +1031,9 @@ private: | |||
| 1029 | // between docked and undocked dimensions. We take the liberty of applying | 1031 | // between docked and undocked dimensions. We take the liberty of applying |
| 1030 | // the resolution scaling factor here. | 1032 | // the resolution scaling factor here. |
| 1031 | rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) * | 1033 | rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) * |
| 1032 | static_cast<u32>(Settings::values.resolution_factor)); | 1034 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 1033 | rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) * | 1035 | rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) * |
| 1034 | static_cast<u32>(Settings::values.resolution_factor)); | 1036 | static_cast<u32>(Settings::values.resolution_factor.GetValue())); |
| 1035 | } | 1037 | } |
| 1036 | 1038 | ||
| 1037 | void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { | 1039 | void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { |
| @@ -1064,8 +1066,8 @@ private: | |||
| 1064 | LOG_WARNING(Service_VI, "(STUBBED) called"); | 1066 | LOG_WARNING(Service_VI, "(STUBBED) called"); |
| 1065 | 1067 | ||
| 1066 | DisplayInfo display_info; | 1068 | DisplayInfo display_info; |
| 1067 | display_info.width *= static_cast<u64>(Settings::values.resolution_factor); | 1069 | display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue()); |
| 1068 | display_info.height *= static_cast<u64>(Settings::values.resolution_factor); | 1070 | display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue()); |
| 1069 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); | 1071 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); |
| 1070 | IPC::ResponseBuilder rb{ctx, 4}; | 1072 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1071 | rb.Push(RESULT_SUCCESS); | 1073 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9d87045a0..2c5588933 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | 9 | ||
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/atomic_ops.h" | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/page_table.h" | 14 | #include "common/page_table.h" |
| @@ -29,15 +30,12 @@ namespace Core::Memory { | |||
| 29 | struct Memory::Impl { | 30 | struct Memory::Impl { |
| 30 | explicit Impl(Core::System& system_) : system{system_} {} | 31 | explicit Impl(Core::System& system_) : system{system_} {} |
| 31 | 32 | ||
| 32 | void SetCurrentPageTable(Kernel::Process& process) { | 33 | void SetCurrentPageTable(Kernel::Process& process, u32 core_id) { |
| 33 | current_page_table = &process.PageTable().PageTableImpl(); | 34 | current_page_table = &process.PageTable().PageTableImpl(); |
| 34 | 35 | ||
| 35 | const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); | 36 | const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); |
| 36 | 37 | ||
| 37 | system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); | 38 | system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width); |
| 38 | system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); | ||
| 39 | system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width); | ||
| 40 | system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); | ||
| 41 | } | 39 | } |
| 42 | 40 | ||
| 43 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 41 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
| @@ -179,6 +177,22 @@ struct Memory::Impl { | |||
| 179 | } | 177 | } |
| 180 | } | 178 | } |
| 181 | 179 | ||
| 180 | bool WriteExclusive8(const VAddr addr, const u8 data, const u8 expected) { | ||
| 181 | return WriteExclusive<u8>(addr, data, expected); | ||
| 182 | } | ||
| 183 | |||
| 184 | bool WriteExclusive16(const VAddr addr, const u16 data, const u16 expected) { | ||
| 185 | return WriteExclusive<u16_le>(addr, data, expected); | ||
| 186 | } | ||
| 187 | |||
| 188 | bool WriteExclusive32(const VAddr addr, const u32 data, const u32 expected) { | ||
| 189 | return WriteExclusive<u32_le>(addr, data, expected); | ||
| 190 | } | ||
| 191 | |||
| 192 | bool WriteExclusive64(const VAddr addr, const u64 data, const u64 expected) { | ||
| 193 | return WriteExclusive<u64_le>(addr, data, expected); | ||
| 194 | } | ||
| 195 | |||
| 182 | std::string ReadCString(VAddr vaddr, std::size_t max_length) { | 196 | std::string ReadCString(VAddr vaddr, std::size_t max_length) { |
| 183 | std::string string; | 197 | std::string string; |
| 184 | string.reserve(max_length); | 198 | string.reserve(max_length); |
| @@ -534,9 +548,9 @@ struct Memory::Impl { | |||
| 534 | // longer exist, and we should just leave the pagetable entry blank. | 548 | // longer exist, and we should just leave the pagetable entry blank. |
| 535 | page_type = Common::PageType::Unmapped; | 549 | page_type = Common::PageType::Unmapped; |
| 536 | } else { | 550 | } else { |
| 537 | page_type = Common::PageType::Memory; | ||
| 538 | current_page_table->pointers[vaddr >> PAGE_BITS] = | 551 | current_page_table->pointers[vaddr >> PAGE_BITS] = |
| 539 | pointer - (vaddr & ~PAGE_MASK); | 552 | pointer - (vaddr & ~PAGE_MASK); |
| 553 | page_type = Common::PageType::Memory; | ||
| 540 | } | 554 | } |
| 541 | break; | 555 | break; |
| 542 | } | 556 | } |
| @@ -577,9 +591,12 @@ struct Memory::Impl { | |||
| 577 | base + page_table.pointers.size()); | 591 | base + page_table.pointers.size()); |
| 578 | 592 | ||
| 579 | if (!target) { | 593 | if (!target) { |
| 594 | ASSERT_MSG(type != Common::PageType::Memory, | ||
| 595 | "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); | ||
| 596 | |||
| 580 | while (base != end) { | 597 | while (base != end) { |
| 581 | page_table.pointers[base] = nullptr; | ||
| 582 | page_table.attributes[base] = type; | 598 | page_table.attributes[base] = type; |
| 599 | page_table.pointers[base] = nullptr; | ||
| 583 | page_table.backing_addr[base] = 0; | 600 | page_table.backing_addr[base] = 0; |
| 584 | 601 | ||
| 585 | base += 1; | 602 | base += 1; |
| @@ -682,6 +699,67 @@ struct Memory::Impl { | |||
| 682 | } | 699 | } |
| 683 | } | 700 | } |
| 684 | 701 | ||
| 702 | template <typename T> | ||
| 703 | bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { | ||
| 704 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||
| 705 | if (page_pointer != nullptr) { | ||
| 706 | // NOTE: Avoid adding any extra logic to this fast-path block | ||
| 707 | T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]); | ||
| 708 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 709 | } | ||
| 710 | |||
| 711 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 712 | switch (type) { | ||
| 713 | case Common::PageType::Unmapped: | ||
| 714 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, | ||
| 715 | static_cast<u32>(data), vaddr); | ||
| 716 | return true; | ||
| 717 | case Common::PageType::Memory: | ||
| 718 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||
| 719 | break; | ||
| 720 | case Common::PageType::RasterizerCachedMemory: { | ||
| 721 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | ||
| 722 | system.GPU().InvalidateRegion(vaddr, sizeof(T)); | ||
| 723 | T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr); | ||
| 724 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 725 | break; | ||
| 726 | } | ||
| 727 | default: | ||
| 728 | UNREACHABLE(); | ||
| 729 | } | ||
| 730 | return true; | ||
| 731 | } | ||
| 732 | |||
| 733 | bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { | ||
| 734 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||
| 735 | if (page_pointer != nullptr) { | ||
| 736 | // NOTE: Avoid adding any extra logic to this fast-path block | ||
| 737 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]); | ||
| 738 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 739 | } | ||
| 740 | |||
| 741 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 742 | switch (type) { | ||
| 743 | case Common::PageType::Unmapped: | ||
| 744 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8, | ||
| 745 | static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr); | ||
| 746 | return true; | ||
| 747 | case Common::PageType::Memory: | ||
| 748 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||
| 749 | break; | ||
| 750 | case Common::PageType::RasterizerCachedMemory: { | ||
| 751 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | ||
| 752 | system.GPU().InvalidateRegion(vaddr, sizeof(u128)); | ||
| 753 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr); | ||
| 754 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 755 | break; | ||
| 756 | } | ||
| 757 | default: | ||
| 758 | UNREACHABLE(); | ||
| 759 | } | ||
| 760 | return true; | ||
| 761 | } | ||
| 762 | |||
| 685 | Common::PageTable* current_page_table = nullptr; | 763 | Common::PageTable* current_page_table = nullptr; |
| 686 | Core::System& system; | 764 | Core::System& system; |
| 687 | }; | 765 | }; |
| @@ -689,8 +767,8 @@ struct Memory::Impl { | |||
| 689 | Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} | 767 | Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} |
| 690 | Memory::~Memory() = default; | 768 | Memory::~Memory() = default; |
| 691 | 769 | ||
| 692 | void Memory::SetCurrentPageTable(Kernel::Process& process) { | 770 | void Memory::SetCurrentPageTable(Kernel::Process& process, u32 core_id) { |
| 693 | impl->SetCurrentPageTable(process); | 771 | impl->SetCurrentPageTable(process, core_id); |
| 694 | } | 772 | } |
| 695 | 773 | ||
| 696 | void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 774 | void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
| @@ -764,6 +842,26 @@ void Memory::Write64(VAddr addr, u64 data) { | |||
| 764 | impl->Write64(addr, data); | 842 | impl->Write64(addr, data); |
| 765 | } | 843 | } |
| 766 | 844 | ||
| 845 | bool Memory::WriteExclusive8(VAddr addr, u8 data, u8 expected) { | ||
| 846 | return impl->WriteExclusive8(addr, data, expected); | ||
| 847 | } | ||
| 848 | |||
| 849 | bool Memory::WriteExclusive16(VAddr addr, u16 data, u16 expected) { | ||
| 850 | return impl->WriteExclusive16(addr, data, expected); | ||
| 851 | } | ||
| 852 | |||
| 853 | bool Memory::WriteExclusive32(VAddr addr, u32 data, u32 expected) { | ||
| 854 | return impl->WriteExclusive32(addr, data, expected); | ||
| 855 | } | ||
| 856 | |||
| 857 | bool Memory::WriteExclusive64(VAddr addr, u64 data, u64 expected) { | ||
| 858 | return impl->WriteExclusive64(addr, data, expected); | ||
| 859 | } | ||
| 860 | |||
| 861 | bool Memory::WriteExclusive128(VAddr addr, u128 data, u128 expected) { | ||
| 862 | return impl->WriteExclusive128(addr, data, expected); | ||
| 863 | } | ||
| 864 | |||
| 767 | std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { | 865 | std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { |
| 768 | return impl->ReadCString(vaddr, max_length); | 866 | return impl->ReadCString(vaddr, max_length); |
| 769 | } | 867 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index 9292f3b0a..4a1cc63f4 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -64,7 +64,7 @@ public: | |||
| 64 | * | 64 | * |
| 65 | * @param process The process to use the page table of. | 65 | * @param process The process to use the page table of. |
| 66 | */ | 66 | */ |
| 67 | void SetCurrentPageTable(Kernel::Process& process); | 67 | void SetCurrentPageTable(Kernel::Process& process, u32 core_id); |
| 68 | 68 | ||
| 69 | /** | 69 | /** |
| 70 | * Maps an allocated buffer onto a region of the emulated process address space. | 70 | * Maps an allocated buffer onto a region of the emulated process address space. |
| @@ -245,6 +245,71 @@ public: | |||
| 245 | void Write64(VAddr addr, u64 data); | 245 | void Write64(VAddr addr, u64 data); |
| 246 | 246 | ||
| 247 | /** | 247 | /** |
| 248 | * Writes a 8-bit unsigned integer to the given virtual address in | ||
| 249 | * the current process' address space if and only if the address contains | ||
| 250 | * the expected value. This operation is atomic. | ||
| 251 | * | ||
| 252 | * @param addr The virtual address to write the 8-bit unsigned integer to. | ||
| 253 | * @param data The 8-bit unsigned integer to write to the given virtual address. | ||
| 254 | * @param expected The 8-bit unsigned integer to check against the given virtual address. | ||
| 255 | * | ||
| 256 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 257 | */ | ||
| 258 | bool WriteExclusive8(VAddr addr, u8 data, u8 expected); | ||
| 259 | |||
| 260 | /** | ||
| 261 | * Writes a 16-bit unsigned integer to the given virtual address in | ||
| 262 | * the current process' address space if and only if the address contains | ||
| 263 | * the expected value. This operation is atomic. | ||
| 264 | * | ||
| 265 | * @param addr The virtual address to write the 16-bit unsigned integer to. | ||
| 266 | * @param data The 16-bit unsigned integer to write to the given virtual address. | ||
| 267 | * @param expected The 16-bit unsigned integer to check against the given virtual address. | ||
| 268 | * | ||
| 269 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 270 | */ | ||
| 271 | bool WriteExclusive16(VAddr addr, u16 data, u16 expected); | ||
| 272 | |||
| 273 | /** | ||
| 274 | * Writes a 32-bit unsigned integer to the given virtual address in | ||
| 275 | * the current process' address space if and only if the address contains | ||
| 276 | * the expected value. This operation is atomic. | ||
| 277 | * | ||
| 278 | * @param addr The virtual address to write the 32-bit unsigned integer to. | ||
| 279 | * @param data The 32-bit unsigned integer to write to the given virtual address. | ||
| 280 | * @param expected The 32-bit unsigned integer to check against the given virtual address. | ||
| 281 | * | ||
| 282 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 283 | */ | ||
| 284 | bool WriteExclusive32(VAddr addr, u32 data, u32 expected); | ||
| 285 | |||
| 286 | /** | ||
| 287 | * Writes a 64-bit unsigned integer to the given virtual address in | ||
| 288 | * the current process' address space if and only if the address contains | ||
| 289 | * the expected value. This operation is atomic. | ||
| 290 | * | ||
| 291 | * @param addr The virtual address to write the 64-bit unsigned integer to. | ||
| 292 | * @param data The 64-bit unsigned integer to write to the given virtual address. | ||
| 293 | * @param expected The 64-bit unsigned integer to check against the given virtual address. | ||
| 294 | * | ||
| 295 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 296 | */ | ||
| 297 | bool WriteExclusive64(VAddr addr, u64 data, u64 expected); | ||
| 298 | |||
| 299 | /** | ||
| 300 | * Writes a 128-bit unsigned integer to the given virtual address in | ||
| 301 | * the current process' address space if and only if the address contains | ||
| 302 | * the expected value. This operation is atomic. | ||
| 303 | * | ||
| 304 | * @param addr The virtual address to write the 128-bit unsigned integer to. | ||
| 305 | * @param data The 128-bit unsigned integer to write to the given virtual address. | ||
| 306 | * @param expected The 128-bit unsigned integer to check against the given virtual address. | ||
| 307 | * | ||
| 308 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 309 | */ | ||
| 310 | bool WriteExclusive128(VAddr addr, u128 data, u128 expected); | ||
| 311 | |||
| 312 | /** | ||
| 248 | * Reads a null-terminated string from the given virtual address. | 313 | * Reads a null-terminated string from the given virtual address. |
| 249 | * This function will continually read characters until either: | 314 | * This function will continually read characters until either: |
| 250 | * | 315 | * |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b139e8465..53d27859b 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace Core::Memory { | 21 | namespace Core::Memory { |
| 22 | 22 | ||
| 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12); | 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12); |
| 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 25 | 25 | ||
| 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) | 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) |
| @@ -190,7 +190,7 @@ CheatEngine::~CheatEngine() { | |||
| 190 | void CheatEngine::Initialize() { | 190 | void CheatEngine::Initialize() { |
| 191 | event = Core::Timing::CreateEvent( | 191 | event = Core::Timing::CreateEvent( |
| 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |
| 193 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | 193 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); |
| 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); |
| 195 | 195 | ||
| 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); |
| @@ -217,7 +217,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | |||
| 217 | 217 | ||
| 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); |
| 219 | 219 | ||
| 220 | void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | 220 | void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { |
| 221 | if (is_pending_reload.exchange(false)) { | 221 | if (is_pending_reload.exchange(false)) { |
| 222 | vm.LoadProgram(cheats); | 222 | vm.LoadProgram(cheats); |
| 223 | } | 223 | } |
| @@ -230,7 +230,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | |||
| 230 | 230 | ||
| 231 | vm.Execute(metadata); | 231 | vm.Execute(metadata); |
| 232 | 232 | ||
| 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); | 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | } // namespace Core::Memory | 236 | } // namespace Core::Memory |
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index f1ae9d4df..29339ead7 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() { | |||
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { | 121 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { |
| 122 | if (!Settings::values.use_frame_limit) { | 122 | if (!Settings::values.use_frame_limit.GetValue() || |
| 123 | Settings::values.use_multi_core.GetValue()) { | ||
| 123 | return; | 124 | return; |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 126 | auto now = Clock::now(); | 127 | auto now = Clock::now(); |
| 127 | 128 | ||
| 128 | const double sleep_scale = Settings::values.frame_limit / 100.0; | 129 | const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0; |
| 129 | 130 | ||
| 130 | // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current | 131 | // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current |
| 131 | // speed percent or it will clamp too much and prevent this from properly limiting to that | 132 | // speed percent or it will clamp too much and prevent this from properly limiting to that |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 4edff9cd8..d3886c4ec 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{ | |||
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | Values values = {}; | 64 | Values values = {}; |
| 65 | bool configuring_global = true; | ||
| 65 | 66 | ||
| 66 | std::string GetTimeZoneString() { | 67 | std::string GetTimeZoneString() { |
| 67 | static constexpr std::array<const char*, 46> timezones{{ | 68 | static constexpr std::array<const char*, 46> timezones{{ |
| @@ -73,9 +74,9 @@ std::string GetTimeZoneString() { | |||
| 73 | "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", | 74 | "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", |
| 74 | }}; | 75 | }}; |
| 75 | 76 | ||
| 76 | ASSERT(Settings::values.time_zone_index < timezones.size()); | 77 | ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size()); |
| 77 | 78 | ||
| 78 | return timezones[Settings::values.time_zone_index]; | 79 | return timezones[Settings::values.time_zone_index.GetValue()]; |
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | void Apply() { | 82 | void Apply() { |
| @@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) { | |||
| 97 | 98 | ||
| 98 | void LogSettings() { | 99 | void LogSettings() { |
| 99 | LOG_INFO(Config, "yuzu Configuration:"); | 100 | LOG_INFO(Config, "yuzu Configuration:"); |
| 100 | LogSetting("System_UseDockedMode", Settings::values.use_docked_mode); | 101 | LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode); |
| 101 | LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); | 102 | LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0)); |
| 102 | LogSetting("System_CurrentUser", Settings::values.current_user); | 103 | LogSetting("System_CurrentUser", Settings::values.current_user); |
| 103 | LogSetting("System_LanguageIndex", Settings::values.language_index); | 104 | LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue()); |
| 104 | LogSetting("System_RegionIndex", Settings::values.region_index); | 105 | LogSetting("System_RegionIndex", Settings::values.region_index.GetValue()); |
| 105 | LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index); | 106 | LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue()); |
| 106 | LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); | 107 | LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); |
| 107 | LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); | 108 | LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue()); |
| 108 | LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); | 109 | LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue()); |
| 109 | LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); | 110 | LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue()); |
| 110 | LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); | 111 | LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue()); |
| 111 | LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy); | 112 | LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue()); |
| 112 | LogSetting("Renderer_UseAsynchronousGpuEmulation", | 113 | LogSetting("Renderer_UseAsynchronousGpuEmulation", |
| 113 | Settings::values.use_asynchronous_gpu_emulation); | 114 | Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 114 | LogSetting("Renderer_UseVsync", Settings::values.use_vsync); | 115 | LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue()); |
| 115 | LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders); | 116 | LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue()); |
| 116 | LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy); | 117 | LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue()); |
| 117 | LogSetting("Audio_OutputEngine", Settings::values.sink_id); | 118 | LogSetting("Audio_OutputEngine", Settings::values.sink_id); |
| 118 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | 119 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue()); |
| 119 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); | 120 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); |
| 120 | LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); | 121 | LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); |
| 121 | LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); | 122 | LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); |
| @@ -127,12 +128,60 @@ void LogSettings() { | |||
| 127 | LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); | 128 | LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); |
| 128 | } | 129 | } |
| 129 | 130 | ||
| 131 | float Volume() { | ||
| 132 | if (values.audio_muted) { | ||
| 133 | return 0.0f; | ||
| 134 | } | ||
| 135 | return values.volume.GetValue(); | ||
| 136 | } | ||
| 137 | |||
| 130 | bool IsGPULevelExtreme() { | 138 | bool IsGPULevelExtreme() { |
| 131 | return values.gpu_accuracy == GPUAccuracy::Extreme; | 139 | return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme; |
| 132 | } | 140 | } |
| 133 | 141 | ||
| 134 | bool IsGPULevelHigh() { | 142 | bool IsGPULevelHigh() { |
| 135 | return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High; | 143 | return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme || |
| 144 | values.gpu_accuracy.GetValue() == GPUAccuracy::High; | ||
| 145 | } | ||
| 146 | |||
| 147 | void RestoreGlobalState() { | ||
| 148 | // If a game is running, DO NOT restore the global settings state | ||
| 149 | if (Core::System::GetInstance().IsPoweredOn()) { | ||
| 150 | return; | ||
| 151 | } | ||
| 152 | |||
| 153 | // Audio | ||
| 154 | values.enable_audio_stretching.SetGlobal(true); | ||
| 155 | values.volume.SetGlobal(true); | ||
| 156 | |||
| 157 | // Core | ||
| 158 | values.use_multi_core.SetGlobal(true); | ||
| 159 | |||
| 160 | // Renderer | ||
| 161 | values.renderer_backend.SetGlobal(true); | ||
| 162 | values.vulkan_device.SetGlobal(true); | ||
| 163 | values.aspect_ratio.SetGlobal(true); | ||
| 164 | values.max_anisotropy.SetGlobal(true); | ||
| 165 | values.use_frame_limit.SetGlobal(true); | ||
| 166 | values.frame_limit.SetGlobal(true); | ||
| 167 | values.use_disk_shader_cache.SetGlobal(true); | ||
| 168 | values.gpu_accuracy.SetGlobal(true); | ||
| 169 | values.use_asynchronous_gpu_emulation.SetGlobal(true); | ||
| 170 | values.use_vsync.SetGlobal(true); | ||
| 171 | values.use_assembly_shaders.SetGlobal(true); | ||
| 172 | values.use_fast_gpu_time.SetGlobal(true); | ||
| 173 | values.force_30fps_mode.SetGlobal(true); | ||
| 174 | values.bg_red.SetGlobal(true); | ||
| 175 | values.bg_green.SetGlobal(true); | ||
| 176 | values.bg_blue.SetGlobal(true); | ||
| 177 | |||
| 178 | // System | ||
| 179 | values.language_index.SetGlobal(true); | ||
| 180 | values.region_index.SetGlobal(true); | ||
| 181 | values.time_zone_index.SetGlobal(true); | ||
| 182 | values.rng_seed.SetGlobal(true); | ||
| 183 | values.custom_rtc.SetGlobal(true); | ||
| 184 | values.sound_index.SetGlobal(true); | ||
| 136 | } | 185 | } |
| 137 | 186 | ||
| 138 | } // namespace Settings | 187 | } // namespace Settings |
diff --git a/src/core/settings.h b/src/core/settings.h index 78eb33737..850ca4072 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -382,20 +382,85 @@ enum class GPUAccuracy : u32 { | |||
| 382 | Extreme = 2, | 382 | Extreme = 2, |
| 383 | }; | 383 | }; |
| 384 | 384 | ||
| 385 | extern bool configuring_global; | ||
| 386 | |||
| 387 | template <typename Type> | ||
| 388 | class Setting final { | ||
| 389 | public: | ||
| 390 | Setting() = default; | ||
| 391 | explicit Setting(Type val) : global{val} {} | ||
| 392 | ~Setting() = default; | ||
| 393 | void SetGlobal(bool to_global) { | ||
| 394 | use_global = to_global; | ||
| 395 | } | ||
| 396 | bool UsingGlobal() const { | ||
| 397 | return use_global; | ||
| 398 | } | ||
| 399 | Type GetValue(bool need_global = false) const { | ||
| 400 | if (use_global || need_global) { | ||
| 401 | return global; | ||
| 402 | } | ||
| 403 | return local; | ||
| 404 | } | ||
| 405 | void SetValue(const Type& value) { | ||
| 406 | if (use_global) { | ||
| 407 | global = value; | ||
| 408 | } else { | ||
| 409 | local = value; | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | private: | ||
| 414 | bool use_global = true; | ||
| 415 | Type global{}; | ||
| 416 | Type local{}; | ||
| 417 | }; | ||
| 418 | |||
| 385 | struct Values { | 419 | struct Values { |
| 420 | // Audio | ||
| 421 | std::string audio_device_id; | ||
| 422 | std::string sink_id; | ||
| 423 | bool audio_muted; | ||
| 424 | Setting<bool> enable_audio_stretching; | ||
| 425 | Setting<float> volume; | ||
| 426 | |||
| 427 | // Core | ||
| 428 | Setting<bool> use_multi_core; | ||
| 429 | |||
| 430 | // Renderer | ||
| 431 | Setting<RendererBackend> renderer_backend; | ||
| 432 | bool renderer_debug; | ||
| 433 | Setting<int> vulkan_device; | ||
| 434 | |||
| 435 | Setting<u16> resolution_factor = Setting(static_cast<u16>(1)); | ||
| 436 | Setting<int> aspect_ratio; | ||
| 437 | Setting<int> max_anisotropy; | ||
| 438 | Setting<bool> use_frame_limit; | ||
| 439 | Setting<u16> frame_limit; | ||
| 440 | Setting<bool> use_disk_shader_cache; | ||
| 441 | Setting<GPUAccuracy> gpu_accuracy; | ||
| 442 | Setting<bool> use_asynchronous_gpu_emulation; | ||
| 443 | Setting<bool> use_vsync; | ||
| 444 | Setting<bool> use_assembly_shaders; | ||
| 445 | Setting<bool> force_30fps_mode; | ||
| 446 | Setting<bool> use_fast_gpu_time; | ||
| 447 | |||
| 448 | Setting<float> bg_red; | ||
| 449 | Setting<float> bg_green; | ||
| 450 | Setting<float> bg_blue; | ||
| 451 | |||
| 386 | // System | 452 | // System |
| 387 | bool use_docked_mode; | 453 | Setting<std::optional<u32>> rng_seed; |
| 388 | std::optional<u32> rng_seed; | ||
| 389 | // Measured in seconds since epoch | 454 | // Measured in seconds since epoch |
| 390 | std::optional<std::chrono::seconds> custom_rtc; | 455 | Setting<std::optional<std::chrono::seconds>> custom_rtc; |
| 391 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` | 456 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` |
| 392 | std::chrono::seconds custom_rtc_differential; | 457 | std::chrono::seconds custom_rtc_differential; |
| 393 | 458 | ||
| 394 | s32 current_user; | 459 | s32 current_user; |
| 395 | s32 language_index; | 460 | Setting<s32> language_index; |
| 396 | s32 region_index; | 461 | Setting<s32> region_index; |
| 397 | s32 time_zone_index; | 462 | Setting<s32> time_zone_index; |
| 398 | s32 sound_index; | 463 | Setting<s32> sound_index; |
| 399 | 464 | ||
| 400 | // Controls | 465 | // Controls |
| 401 | std::array<PlayerInput, 10> players; | 466 | std::array<PlayerInput, 10> players; |
| @@ -419,8 +484,7 @@ struct Values { | |||
| 419 | u16 udp_input_port; | 484 | u16 udp_input_port; |
| 420 | u8 udp_pad_index; | 485 | u8 udp_pad_index; |
| 421 | 486 | ||
| 422 | // Core | 487 | bool use_docked_mode; |
| 423 | bool use_multi_core; | ||
| 424 | 488 | ||
| 425 | // Data Storage | 489 | // Data Storage |
| 426 | bool use_virtual_sd; | 490 | bool use_virtual_sd; |
| @@ -432,38 +496,6 @@ struct Values { | |||
| 432 | NANDUserSize nand_user_size; | 496 | NANDUserSize nand_user_size; |
| 433 | SDMCSize sdmc_size; | 497 | SDMCSize sdmc_size; |
| 434 | 498 | ||
| 435 | // Renderer | ||
| 436 | RendererBackend renderer_backend; | ||
| 437 | bool renderer_debug; | ||
| 438 | int vulkan_device; | ||
| 439 | |||
| 440 | float resolution_factor; | ||
| 441 | int aspect_ratio; | ||
| 442 | int max_anisotropy; | ||
| 443 | bool use_frame_limit; | ||
| 444 | u16 frame_limit; | ||
| 445 | bool use_disk_shader_cache; | ||
| 446 | GPUAccuracy gpu_accuracy; | ||
| 447 | bool use_asynchronous_gpu_emulation; | ||
| 448 | bool use_vsync; | ||
| 449 | bool use_assembly_shaders; | ||
| 450 | bool force_30fps_mode; | ||
| 451 | bool use_fast_gpu_time; | ||
| 452 | |||
| 453 | float bg_red; | ||
| 454 | float bg_green; | ||
| 455 | float bg_blue; | ||
| 456 | |||
| 457 | std::string log_filter; | ||
| 458 | |||
| 459 | bool use_dev_keys; | ||
| 460 | |||
| 461 | // Audio | ||
| 462 | std::string sink_id; | ||
| 463 | bool enable_audio_stretching; | ||
| 464 | std::string audio_device_id; | ||
| 465 | float volume; | ||
| 466 | |||
| 467 | // Debugging | 499 | // Debugging |
| 468 | bool record_frame_times; | 500 | bool record_frame_times; |
| 469 | bool use_gdbstub; | 501 | bool use_gdbstub; |
| @@ -474,8 +506,13 @@ struct Values { | |||
| 474 | bool reporting_services; | 506 | bool reporting_services; |
| 475 | bool quest_flag; | 507 | bool quest_flag; |
| 476 | bool disable_cpu_opt; | 508 | bool disable_cpu_opt; |
| 509 | bool disable_macro_jit; | ||
| 477 | 510 | ||
| 478 | // BCAT | 511 | // Misceallaneous |
| 512 | std::string log_filter; | ||
| 513 | bool use_dev_keys; | ||
| 514 | |||
| 515 | // Services | ||
| 479 | std::string bcat_backend; | 516 | std::string bcat_backend; |
| 480 | bool bcat_boxcat_local; | 517 | bool bcat_boxcat_local; |
| 481 | 518 | ||
| @@ -489,6 +526,8 @@ struct Values { | |||
| 489 | std::map<u64, std::vector<std::string>> disabled_addons; | 526 | std::map<u64, std::vector<std::string>> disabled_addons; |
| 490 | } extern values; | 527 | } extern values; |
| 491 | 528 | ||
| 529 | float Volume(); | ||
| 530 | |||
| 492 | bool IsGPULevelExtreme(); | 531 | bool IsGPULevelExtreme(); |
| 493 | bool IsGPULevelHigh(); | 532 | bool IsGPULevelHigh(); |
| 494 | 533 | ||
| @@ -497,4 +536,7 @@ std::string GetTimeZoneString(); | |||
| 497 | void Apply(); | 536 | void Apply(); |
| 498 | void LogSettings(); | 537 | void LogSettings(); |
| 499 | 538 | ||
| 539 | // Restore the global state of all applicable settings in the Values struct | ||
| 540 | void RestoreGlobalState(); | ||
| 541 | |||
| 500 | } // namespace Settings | 542 | } // namespace Settings |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index c781b3cfc..78915e6db 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | |||
| 189 | // Log user configuration information | 189 | // Log user configuration information |
| 190 | constexpr auto field_type = Telemetry::FieldType::UserConfig; | 190 | constexpr auto field_type = Telemetry::FieldType::UserConfig; |
| 191 | AddField(field_type, "Audio_SinkId", Settings::values.sink_id); | 191 | AddField(field_type, "Audio_SinkId", Settings::values.sink_id); |
| 192 | AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | 192 | AddField(field_type, "Audio_EnableAudioStretching", |
| 193 | AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); | 193 | Settings::values.enable_audio_stretching.GetValue()); |
| 194 | AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend)); | 194 | AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); |
| 195 | AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); | 195 | AddField(field_type, "Renderer_Backend", |
| 196 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); | 196 | TranslateRenderer(Settings::values.renderer_backend.GetValue())); |
| 197 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); | 197 | AddField(field_type, "Renderer_ResolutionFactor", |
| 198 | AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); | 198 | Settings::values.resolution_factor.GetValue()); |
| 199 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue()); | ||
| 200 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue()); | ||
| 201 | AddField(field_type, "Renderer_UseDiskShaderCache", | ||
| 202 | Settings::values.use_disk_shader_cache.GetValue()); | ||
| 199 | AddField(field_type, "Renderer_GPUAccuracyLevel", | 203 | AddField(field_type, "Renderer_GPUAccuracyLevel", |
| 200 | TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy)); | 204 | TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue())); |
| 201 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", | 205 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", |
| 202 | Settings::values.use_asynchronous_gpu_emulation); | 206 | Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 203 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); | 207 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); |
| 204 | AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders); | 208 | AddField(field_type, "Renderer_UseAssemblyShaders", |
| 209 | Settings::values.use_assembly_shaders.GetValue()); | ||
| 205 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); | 210 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); |
| 206 | } | 211 | } |
| 207 | 212 | ||
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index b2c6c537e..8b0c50d11 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | namespace Tools { | 14 | namespace Tools { |
| 15 | namespace { | 15 | namespace { |
| 16 | 16 | ||
| 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); | 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60); |
| 18 | 18 | ||
| 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { | 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { |
| 20 | switch (width) { | 20 | switch (width) { |
| @@ -57,7 +57,7 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m | |||
| 57 | : core_timing{core_timing_}, memory{memory_} { | 57 | : core_timing{core_timing_}, memory{memory_} { |
| 58 | event = Core::Timing::CreateEvent( | 58 | event = Core::Timing::CreateEvent( |
| 59 | "MemoryFreezer::FrameCallback", | 59 | "MemoryFreezer::FrameCallback", |
| 60 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | 60 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); |
| 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| @@ -158,7 +158,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const { | |||
| 158 | return entries; | 158 | return entries; |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { | 161 | void Freezer::FrameCallback(u64 userdata, s64 ns_late) { |
| 162 | if (!IsActive()) { | 162 | if (!IsActive()) { |
| 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); | 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); |
| 164 | return; | 164 | return; |
| @@ -173,7 +173,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { | |||
| 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); | 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); | 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event); |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | void Freezer::FillEntryReads() { | 179 | void Freezer::FillEntryReads() { |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index a9c2392b1..3bd76dd23 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -7,6 +7,10 @@ add_library(input_common STATIC | |||
| 7 | main.h | 7 | main.h |
| 8 | motion_emu.cpp | 8 | motion_emu.cpp |
| 9 | motion_emu.h | 9 | motion_emu.h |
| 10 | gcadapter/gc_adapter.cpp | ||
| 11 | gcadapter/gc_adapter.h | ||
| 12 | gcadapter/gc_poller.cpp | ||
| 13 | gcadapter/gc_poller.h | ||
| 10 | sdl/sdl.cpp | 14 | sdl/sdl.cpp |
| 11 | sdl/sdl.h | 15 | sdl/sdl.h |
| 12 | udp/client.cpp | 16 | udp/client.cpp |
| @@ -26,5 +30,7 @@ if(SDL2_FOUND) | |||
| 26 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) | 30 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) |
| 27 | endif() | 31 | endif() |
| 28 | 32 | ||
| 33 | target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES}) | ||
| 34 | |||
| 29 | create_target_directory_groups(input_common) | 35 | create_target_directory_groups(input_common) |
| 30 | target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) | 36 | target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) |
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp new file mode 100644 index 000000000..6d9f4d9eb --- /dev/null +++ b/src/input_common/gcadapter/gc_adapter.cpp | |||
| @@ -0,0 +1,398 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <chrono> | ||
| 6 | #include <thread> | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 9 | |||
| 10 | namespace GCAdapter { | ||
| 11 | |||
| 12 | /// Used to loop through and assign button in poller | ||
| 13 | constexpr std::array<PadButton, 12> PadButtonArray{ | ||
| 14 | PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN, | ||
| 15 | PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R, | ||
| 16 | PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, | ||
| 17 | PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START, | ||
| 18 | }; | ||
| 19 | |||
| 20 | Adapter::Adapter() { | ||
| 21 | if (usb_adapter_handle != nullptr) { | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | LOG_INFO(Input, "GC Adapter Initialization started"); | ||
| 25 | |||
| 26 | current_status = NO_ADAPTER_DETECTED; | ||
| 27 | |||
| 28 | const int init_res = libusb_init(&libusb_ctx); | ||
| 29 | if (init_res == LIBUSB_SUCCESS) { | ||
| 30 | StartScanThread(); | ||
| 31 | } else { | ||
| 32 | LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) { | ||
| 37 | GCPadStatus pad = {}; | ||
| 38 | bool get_origin = false; | ||
| 39 | |||
| 40 | ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4); | ||
| 41 | if (type != ControllerTypes::None) { | ||
| 42 | get_origin = true; | ||
| 43 | } | ||
| 44 | |||
| 45 | adapter_controllers_status[port] = type; | ||
| 46 | |||
| 47 | static constexpr std::array<PadButton, 8> b1_buttons{ | ||
| 48 | PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, | ||
| 49 | PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, | ||
| 50 | PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP, | ||
| 51 | }; | ||
| 52 | |||
| 53 | static constexpr std::array<PadButton, 4> b2_buttons{ | ||
| 54 | PadButton::PAD_BUTTON_START, | ||
| 55 | PadButton::PAD_TRIGGER_Z, | ||
| 56 | PadButton::PAD_TRIGGER_R, | ||
| 57 | PadButton::PAD_TRIGGER_L, | ||
| 58 | }; | ||
| 59 | |||
| 60 | if (adapter_controllers_status[port] != ControllerTypes::None) { | ||
| 61 | const u8 b1 = adapter_payload[1 + (9 * port) + 1]; | ||
| 62 | const u8 b2 = adapter_payload[1 + (9 * port) + 2]; | ||
| 63 | |||
| 64 | for (std::size_t i = 0; i < b1_buttons.size(); ++i) { | ||
| 65 | if ((b1 & (1U << i)) != 0) { | ||
| 66 | pad.button |= static_cast<u16>(b1_buttons[i]); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | for (std::size_t j = 0; j < b2_buttons.size(); ++j) { | ||
| 71 | if ((b2 & (1U << j)) != 0) { | ||
| 72 | pad.button |= static_cast<u16>(b2_buttons[j]); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | if (get_origin) { | ||
| 77 | pad.button |= PAD_GET_ORIGIN; | ||
| 78 | } | ||
| 79 | |||
| 80 | pad.stick_x = adapter_payload[1 + (9 * port) + 3]; | ||
| 81 | pad.stick_y = adapter_payload[1 + (9 * port) + 4]; | ||
| 82 | pad.substick_x = adapter_payload[1 + (9 * port) + 5]; | ||
| 83 | pad.substick_y = adapter_payload[1 + (9 * port) + 6]; | ||
| 84 | pad.trigger_left = adapter_payload[1 + (9 * port) + 7]; | ||
| 85 | pad.trigger_right = adapter_payload[1 + (9 * port) + 8]; | ||
| 86 | } | ||
| 87 | return pad; | ||
| 88 | } | ||
| 89 | |||
| 90 | void Adapter::PadToState(const GCPadStatus& pad, GCState& state) { | ||
| 91 | for (const auto& button : PadButtonArray) { | ||
| 92 | const u16 button_value = static_cast<u16>(button); | ||
| 93 | state.buttons.insert_or_assign(button_value, pad.button & button_value); | ||
| 94 | } | ||
| 95 | |||
| 96 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x); | ||
| 97 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y); | ||
| 98 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x); | ||
| 99 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y); | ||
| 100 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left); | ||
| 101 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right); | ||
| 102 | } | ||
| 103 | |||
| 104 | void Adapter::Read() { | ||
| 105 | LOG_DEBUG(Input, "GC Adapter Read() thread started"); | ||
| 106 | |||
| 107 | int payload_size_in, payload_size_copy; | ||
| 108 | std::array<u8, 37> adapter_payload; | ||
| 109 | std::array<u8, 37> adapter_payload_copy; | ||
| 110 | std::array<GCPadStatus, 4> pads; | ||
| 111 | |||
| 112 | while (adapter_thread_running) { | ||
| 113 | libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), | ||
| 114 | sizeof(adapter_payload), &payload_size_in, 16); | ||
| 115 | payload_size_copy = 0; | ||
| 116 | // this mutex might be redundant? | ||
| 117 | { | ||
| 118 | std::lock_guard<std::mutex> lk(s_mutex); | ||
| 119 | std::copy(std::begin(adapter_payload), std::end(adapter_payload), | ||
| 120 | std::begin(adapter_payload_copy)); | ||
| 121 | payload_size_copy = payload_size_in; | ||
| 122 | } | ||
| 123 | |||
| 124 | if (payload_size_copy != sizeof(adapter_payload_copy) || | ||
| 125 | adapter_payload_copy[0] != LIBUSB_DT_HID) { | ||
| 126 | LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy, | ||
| 127 | adapter_payload_copy[0]); | ||
| 128 | adapter_thread_running = false; // error reading from adapter, stop reading. | ||
| 129 | break; | ||
| 130 | } | ||
| 131 | for (std::size_t port = 0; port < pads.size(); ++port) { | ||
| 132 | pads[port] = GetPadStatus(port, adapter_payload_copy); | ||
| 133 | if (DeviceConnected(port) && configuring) { | ||
| 134 | if (pads[port].button != PAD_GET_ORIGIN) { | ||
| 135 | pad_queue[port].Push(pads[port]); | ||
| 136 | } | ||
| 137 | |||
| 138 | // Accounting for a threshold here because of some controller variance | ||
| 139 | if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD || | ||
| 140 | pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) { | ||
| 141 | pads[port].axis = GCAdapter::PadAxes::StickX; | ||
| 142 | pads[port].axis_value = pads[port].stick_x; | ||
| 143 | pad_queue[port].Push(pads[port]); | ||
| 144 | } | ||
| 145 | if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD || | ||
| 146 | pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) { | ||
| 147 | pads[port].axis = GCAdapter::PadAxes::StickY; | ||
| 148 | pads[port].axis_value = pads[port].stick_y; | ||
| 149 | pad_queue[port].Push(pads[port]); | ||
| 150 | } | ||
| 151 | if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD || | ||
| 152 | pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) { | ||
| 153 | pads[port].axis = GCAdapter::PadAxes::SubstickX; | ||
| 154 | pads[port].axis_value = pads[port].substick_x; | ||
| 155 | pad_queue[port].Push(pads[port]); | ||
| 156 | } | ||
| 157 | if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD || | ||
| 158 | pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) { | ||
| 159 | pads[port].axis = GCAdapter::PadAxes::SubstickY; | ||
| 160 | pads[port].axis_value = pads[port].substick_y; | ||
| 161 | pad_queue[port].Push(pads[port]); | ||
| 162 | } | ||
| 163 | if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) { | ||
| 164 | pads[port].axis = GCAdapter::PadAxes::TriggerLeft; | ||
| 165 | pads[port].axis_value = pads[port].trigger_left; | ||
| 166 | pad_queue[port].Push(pads[port]); | ||
| 167 | } | ||
| 168 | if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) { | ||
| 169 | pads[port].axis = GCAdapter::PadAxes::TriggerRight; | ||
| 170 | pads[port].axis_value = pads[port].trigger_right; | ||
| 171 | pad_queue[port].Push(pads[port]); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | PadToState(pads[port], state[port]); | ||
| 175 | } | ||
| 176 | std::this_thread::yield(); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | void Adapter::ScanThreadFunc() { | ||
| 181 | LOG_INFO(Input, "GC Adapter scanning thread started"); | ||
| 182 | |||
| 183 | while (detect_thread_running) { | ||
| 184 | if (usb_adapter_handle == nullptr) { | ||
| 185 | std::lock_guard<std::mutex> lk(initialization_mutex); | ||
| 186 | Setup(); | ||
| 187 | } | ||
| 188 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | void Adapter::StartScanThread() { | ||
| 193 | if (detect_thread_running) { | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | if (!libusb_ctx) { | ||
| 197 | return; | ||
| 198 | } | ||
| 199 | |||
| 200 | detect_thread_running = true; | ||
| 201 | detect_thread = std::thread([=] { ScanThreadFunc(); }); | ||
| 202 | } | ||
| 203 | |||
| 204 | void Adapter::StopScanThread() { | ||
| 205 | detect_thread_running = false; | ||
| 206 | detect_thread.join(); | ||
| 207 | } | ||
| 208 | |||
| 209 | void Adapter::Setup() { | ||
| 210 | // Reset the error status in case the adapter gets unplugged | ||
| 211 | if (current_status < 0) { | ||
| 212 | current_status = NO_ADAPTER_DETECTED; | ||
| 213 | } | ||
| 214 | |||
| 215 | adapter_controllers_status.fill(ControllerTypes::None); | ||
| 216 | |||
| 217 | // pointer to list of connected usb devices | ||
| 218 | libusb_device** devices{}; | ||
| 219 | |||
| 220 | // populate the list of devices, get the count | ||
| 221 | const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices); | ||
| 222 | if (device_count < 0) { | ||
| 223 | LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count); | ||
| 224 | detect_thread_running = false; // Stop the loop constantly checking for gc adapter | ||
| 225 | // TODO: For hotplug+gc adapter checkbox implementation, revert this. | ||
| 226 | return; | ||
| 227 | } | ||
| 228 | |||
| 229 | if (devices != nullptr) { | ||
| 230 | for (std::size_t index = 0; index < device_count; ++index) { | ||
| 231 | if (CheckDeviceAccess(devices[index])) { | ||
| 232 | // GC Adapter found and accessible, registering it | ||
| 233 | GetGCEndpoint(devices[index]); | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | libusb_free_device_list(devices, 1); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | bool Adapter::CheckDeviceAccess(libusb_device* device) { | ||
| 242 | libusb_device_descriptor desc; | ||
| 243 | const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); | ||
| 244 | if (get_descriptor_error) { | ||
| 245 | // could not acquire the descriptor, no point in trying to use it. | ||
| 246 | LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", | ||
| 247 | get_descriptor_error); | ||
| 248 | return false; | ||
| 249 | } | ||
| 250 | |||
| 251 | if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { | ||
| 252 | // This isn't the device we are looking for. | ||
| 253 | return false; | ||
| 254 | } | ||
| 255 | const int open_error = libusb_open(device, &usb_adapter_handle); | ||
| 256 | |||
| 257 | if (open_error == LIBUSB_ERROR_ACCESS) { | ||
| 258 | LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", | ||
| 259 | desc.idVendor, desc.idProduct); | ||
| 260 | return false; | ||
| 261 | } | ||
| 262 | if (open_error) { | ||
| 263 | LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); | ||
| 264 | return false; | ||
| 265 | } | ||
| 266 | |||
| 267 | int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); | ||
| 268 | if (kernel_driver_error == 1) { | ||
| 269 | kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); | ||
| 270 | if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { | ||
| 271 | LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", | ||
| 272 | kernel_driver_error); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { | ||
| 277 | libusb_close(usb_adapter_handle); | ||
| 278 | usb_adapter_handle = nullptr; | ||
| 279 | return false; | ||
| 280 | } | ||
| 281 | |||
| 282 | const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); | ||
| 283 | if (interface_claim_error) { | ||
| 284 | LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); | ||
| 285 | libusb_close(usb_adapter_handle); | ||
| 286 | usb_adapter_handle = nullptr; | ||
| 287 | return false; | ||
| 288 | } | ||
| 289 | |||
| 290 | return true; | ||
| 291 | } | ||
| 292 | |||
| 293 | void Adapter::GetGCEndpoint(libusb_device* device) { | ||
| 294 | libusb_config_descriptor* config = nullptr; | ||
| 295 | const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); | ||
| 296 | if (config_descriptor_return != LIBUSB_SUCCESS) { | ||
| 297 | LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}", | ||
| 298 | config_descriptor_return); | ||
| 299 | return; | ||
| 300 | } | ||
| 301 | |||
| 302 | for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { | ||
| 303 | const libusb_interface* interfaceContainer = &config->interface[ic]; | ||
| 304 | for (int i = 0; i < interfaceContainer->num_altsetting; i++) { | ||
| 305 | const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i]; | ||
| 306 | for (u8 e = 0; e < interface->bNumEndpoints; e++) { | ||
| 307 | const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e]; | ||
| 308 | if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { | ||
| 309 | input_endpoint = endpoint->bEndpointAddress; | ||
| 310 | } else { | ||
| 311 | output_endpoint = endpoint->bEndpointAddress; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | } | ||
| 315 | } | ||
| 316 | // This transfer seems to be responsible for clearing the state of the adapter | ||
| 317 | // Used to clear the "busy" state of when the device is unexpectedly unplugged | ||
| 318 | unsigned char clear_payload = 0x13; | ||
| 319 | libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, | ||
| 320 | sizeof(clear_payload), nullptr, 16); | ||
| 321 | |||
| 322 | adapter_thread_running = true; | ||
| 323 | current_status = ADAPTER_DETECTED; | ||
| 324 | adapter_input_thread = std::thread([=] { Read(); }); // Read input | ||
| 325 | } | ||
| 326 | |||
| 327 | Adapter::~Adapter() { | ||
| 328 | StopScanThread(); | ||
| 329 | Reset(); | ||
| 330 | } | ||
| 331 | |||
| 332 | void Adapter::Reset() { | ||
| 333 | std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock); | ||
| 334 | if (!lock.try_lock()) { | ||
| 335 | return; | ||
| 336 | } | ||
| 337 | if (current_status != ADAPTER_DETECTED) { | ||
| 338 | return; | ||
| 339 | } | ||
| 340 | |||
| 341 | if (adapter_thread_running) { | ||
| 342 | adapter_thread_running = false; | ||
| 343 | } | ||
| 344 | adapter_input_thread.join(); | ||
| 345 | |||
| 346 | adapter_controllers_status.fill(ControllerTypes::None); | ||
| 347 | current_status = NO_ADAPTER_DETECTED; | ||
| 348 | |||
| 349 | if (usb_adapter_handle) { | ||
| 350 | libusb_release_interface(usb_adapter_handle, 1); | ||
| 351 | libusb_close(usb_adapter_handle); | ||
| 352 | usb_adapter_handle = nullptr; | ||
| 353 | } | ||
| 354 | |||
| 355 | if (libusb_ctx) { | ||
| 356 | libusb_exit(libusb_ctx); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | bool Adapter::DeviceConnected(int port) { | ||
| 361 | return adapter_controllers_status[port] != ControllerTypes::None; | ||
| 362 | } | ||
| 363 | |||
| 364 | void Adapter::ResetDeviceType(int port) { | ||
| 365 | adapter_controllers_status[port] = ControllerTypes::None; | ||
| 366 | } | ||
| 367 | |||
| 368 | void Adapter::BeginConfiguration() { | ||
| 369 | for (auto& pq : pad_queue) { | ||
| 370 | pq.Clear(); | ||
| 371 | } | ||
| 372 | configuring = true; | ||
| 373 | } | ||
| 374 | |||
| 375 | void Adapter::EndConfiguration() { | ||
| 376 | for (auto& pq : pad_queue) { | ||
| 377 | pq.Clear(); | ||
| 378 | } | ||
| 379 | configuring = false; | ||
| 380 | } | ||
| 381 | |||
| 382 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { | ||
| 383 | return pad_queue; | ||
| 384 | } | ||
| 385 | |||
| 386 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { | ||
| 387 | return pad_queue; | ||
| 388 | } | ||
| 389 | |||
| 390 | std::array<GCState, 4>& Adapter::GetPadState() { | ||
| 391 | return state; | ||
| 392 | } | ||
| 393 | |||
| 394 | const std::array<GCState, 4>& Adapter::GetPadState() const { | ||
| 395 | return state; | ||
| 396 | } | ||
| 397 | |||
| 398 | } // namespace GCAdapter | ||
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h new file mode 100644 index 000000000..b1c2a1958 --- /dev/null +++ b/src/input_common/gcadapter/gc_adapter.h | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | #include <algorithm> | ||
| 7 | #include <functional> | ||
| 8 | #include <mutex> | ||
| 9 | #include <thread> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include <libusb.h> | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/threadsafe_queue.h" | ||
| 14 | |||
| 15 | namespace GCAdapter { | ||
| 16 | |||
| 17 | enum { | ||
| 18 | PAD_USE_ORIGIN = 0x0080, | ||
| 19 | PAD_GET_ORIGIN = 0x2000, | ||
| 20 | PAD_ERR_STATUS = 0x8000, | ||
| 21 | }; | ||
| 22 | |||
| 23 | enum class PadButton { | ||
| 24 | PAD_BUTTON_LEFT = 0x0001, | ||
| 25 | PAD_BUTTON_RIGHT = 0x0002, | ||
| 26 | PAD_BUTTON_DOWN = 0x0004, | ||
| 27 | PAD_BUTTON_UP = 0x0008, | ||
| 28 | PAD_TRIGGER_Z = 0x0010, | ||
| 29 | PAD_TRIGGER_R = 0x0020, | ||
| 30 | PAD_TRIGGER_L = 0x0040, | ||
| 31 | PAD_BUTTON_A = 0x0100, | ||
| 32 | PAD_BUTTON_B = 0x0200, | ||
| 33 | PAD_BUTTON_X = 0x0400, | ||
| 34 | PAD_BUTTON_Y = 0x0800, | ||
| 35 | PAD_BUTTON_START = 0x1000, | ||
| 36 | // Below is for compatibility with "AxisButton" type | ||
| 37 | PAD_STICK = 0x2000, | ||
| 38 | }; | ||
| 39 | |||
| 40 | extern const std::array<PadButton, 12> PadButtonArray; | ||
| 41 | |||
| 42 | enum class PadAxes : u8 { | ||
| 43 | StickX, | ||
| 44 | StickY, | ||
| 45 | SubstickX, | ||
| 46 | SubstickY, | ||
| 47 | TriggerLeft, | ||
| 48 | TriggerRight, | ||
| 49 | Undefined, | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct GCPadStatus { | ||
| 53 | u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits | ||
| 54 | u8 stick_x{}; // 0 <= stick_x <= 255 | ||
| 55 | u8 stick_y{}; // 0 <= stick_y <= 255 | ||
| 56 | u8 substick_x{}; // 0 <= substick_x <= 255 | ||
| 57 | u8 substick_y{}; // 0 <= substick_y <= 255 | ||
| 58 | u8 trigger_left{}; // 0 <= trigger_left <= 255 | ||
| 59 | u8 trigger_right{}; // 0 <= trigger_right <= 255 | ||
| 60 | |||
| 61 | static constexpr u8 MAIN_STICK_CENTER_X = 0x80; | ||
| 62 | static constexpr u8 MAIN_STICK_CENTER_Y = 0x80; | ||
| 63 | static constexpr u8 MAIN_STICK_RADIUS = 0x7f; | ||
| 64 | static constexpr u8 C_STICK_CENTER_X = 0x80; | ||
| 65 | static constexpr u8 C_STICK_CENTER_Y = 0x80; | ||
| 66 | static constexpr u8 C_STICK_RADIUS = 0x7f; | ||
| 67 | static constexpr u8 THRESHOLD = 10; | ||
| 68 | |||
| 69 | // 256/4, at least a quarter press to count as a press. For polling mostly | ||
| 70 | static constexpr u8 TRIGGER_THRESHOLD = 64; | ||
| 71 | |||
| 72 | u8 port{}; | ||
| 73 | PadAxes axis{PadAxes::Undefined}; | ||
| 74 | u8 axis_value{255}; | ||
| 75 | }; | ||
| 76 | |||
| 77 | struct GCState { | ||
| 78 | std::unordered_map<int, bool> buttons; | ||
| 79 | std::unordered_map<int, u16> axes; | ||
| 80 | }; | ||
| 81 | |||
| 82 | enum class ControllerTypes { None, Wired, Wireless }; | ||
| 83 | |||
| 84 | enum { | ||
| 85 | NO_ADAPTER_DETECTED = 0, | ||
| 86 | ADAPTER_DETECTED = 1, | ||
| 87 | }; | ||
| 88 | |||
| 89 | class Adapter { | ||
| 90 | public: | ||
| 91 | /// Initialize the GC Adapter capture and read sequence | ||
| 92 | Adapter(); | ||
| 93 | |||
| 94 | /// Close the adapter read thread and release the adapter | ||
| 95 | ~Adapter(); | ||
| 96 | /// Used for polling | ||
| 97 | void BeginConfiguration(); | ||
| 98 | void EndConfiguration(); | ||
| 99 | |||
| 100 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); | ||
| 101 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; | ||
| 102 | |||
| 103 | std::array<GCState, 4>& GetPadState(); | ||
| 104 | const std::array<GCState, 4>& GetPadState() const; | ||
| 105 | |||
| 106 | private: | ||
| 107 | GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload); | ||
| 108 | |||
| 109 | void PadToState(const GCPadStatus& pad, GCState& state); | ||
| 110 | |||
| 111 | void Read(); | ||
| 112 | void ScanThreadFunc(); | ||
| 113 | /// Begin scanning for the GC Adapter. | ||
| 114 | void StartScanThread(); | ||
| 115 | |||
| 116 | /// Stop scanning for the adapter | ||
| 117 | void StopScanThread(); | ||
| 118 | |||
| 119 | /// Returns true if there is a device connected to port | ||
| 120 | bool DeviceConnected(int port); | ||
| 121 | |||
| 122 | /// Resets status of device connected to port | ||
| 123 | void ResetDeviceType(int port); | ||
| 124 | |||
| 125 | /// Returns true if we successfully gain access to GC Adapter | ||
| 126 | bool CheckDeviceAccess(libusb_device* device); | ||
| 127 | |||
| 128 | /// Captures GC Adapter endpoint address, | ||
| 129 | void GetGCEndpoint(libusb_device* device); | ||
| 130 | |||
| 131 | /// For shutting down, clear all data, join all threads, release usb | ||
| 132 | void Reset(); | ||
| 133 | |||
| 134 | /// For use in initialization, querying devices to find the adapter | ||
| 135 | void Setup(); | ||
| 136 | |||
| 137 | int current_status = NO_ADAPTER_DETECTED; | ||
| 138 | libusb_device_handle* usb_adapter_handle = nullptr; | ||
| 139 | std::array<ControllerTypes, 4> adapter_controllers_status{}; | ||
| 140 | |||
| 141 | std::mutex s_mutex; | ||
| 142 | |||
| 143 | std::thread adapter_input_thread; | ||
| 144 | bool adapter_thread_running; | ||
| 145 | |||
| 146 | std::mutex initialization_mutex; | ||
| 147 | std::thread detect_thread; | ||
| 148 | bool detect_thread_running = false; | ||
| 149 | |||
| 150 | libusb_context* libusb_ctx; | ||
| 151 | |||
| 152 | u8 input_endpoint = 0; | ||
| 153 | u8 output_endpoint = 0; | ||
| 154 | |||
| 155 | bool configuring = false; | ||
| 156 | |||
| 157 | std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue; | ||
| 158 | std::array<GCState, 4> state; | ||
| 159 | }; | ||
| 160 | |||
| 161 | } // namespace GCAdapter | ||
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp new file mode 100644 index 000000000..385ce8430 --- /dev/null +++ b/src/input_common/gcadapter/gc_poller.cpp | |||
| @@ -0,0 +1,272 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <atomic> | ||
| 6 | #include <list> | ||
| 7 | #include <mutex> | ||
| 8 | #include <utility> | ||
| 9 | #include "common/threadsafe_queue.h" | ||
| 10 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 11 | #include "input_common/gcadapter/gc_poller.h" | ||
| 12 | |||
| 13 | namespace InputCommon { | ||
| 14 | |||
| 15 | class GCButton final : public Input::ButtonDevice { | ||
| 16 | public: | ||
| 17 | explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter) | ||
| 18 | : port(port_), button(button_), gcadapter(adapter) {} | ||
| 19 | |||
| 20 | ~GCButton() override; | ||
| 21 | |||
| 22 | bool GetStatus() const override { | ||
| 23 | return gcadapter->GetPadState()[port].buttons.at(button); | ||
| 24 | } | ||
| 25 | |||
| 26 | private: | ||
| 27 | const int port; | ||
| 28 | const int button; | ||
| 29 | GCAdapter::Adapter* gcadapter; | ||
| 30 | }; | ||
| 31 | |||
| 32 | class GCAxisButton final : public Input::ButtonDevice { | ||
| 33 | public: | ||
| 34 | explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, | ||
| 35 | GCAdapter::Adapter* adapter) | ||
| 36 | : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), | ||
| 37 | gcadapter(adapter) { | ||
| 38 | // L/R triggers range is only in positive direction beginning near 0 | ||
| 39 | // 0.0 threshold equates to near half trigger press, but threshold accounts for variability. | ||
| 40 | if (axis > 3) { | ||
| 41 | threshold *= -0.5; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | bool GetStatus() const override { | ||
| 46 | const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f; | ||
| 47 | if (trigger_if_greater) { | ||
| 48 | // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently | ||
| 49 | // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick | ||
| 50 | return axis_value > threshold; | ||
| 51 | } | ||
| 52 | return axis_value < -threshold; | ||
| 53 | } | ||
| 54 | |||
| 55 | private: | ||
| 56 | const int port; | ||
| 57 | const int axis; | ||
| 58 | float threshold; | ||
| 59 | bool trigger_if_greater; | ||
| 60 | GCAdapter::Adapter* gcadapter; | ||
| 61 | }; | ||
| 62 | |||
| 63 | GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | ||
| 64 | : adapter(std::move(adapter_)) {} | ||
| 65 | |||
| 66 | GCButton::~GCButton() = default; | ||
| 67 | |||
| 68 | std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) { | ||
| 69 | const int button_id = params.Get("button", 0); | ||
| 70 | const int port = params.Get("port", 0); | ||
| 71 | |||
| 72 | constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); | ||
| 73 | |||
| 74 | // button is not an axis/stick button | ||
| 75 | if (button_id != PAD_STICK_ID) { | ||
| 76 | auto button = std::make_unique<GCButton>(port, button_id, adapter.get()); | ||
| 77 | return std::move(button); | ||
| 78 | } | ||
| 79 | |||
| 80 | // For Axis buttons, used by the binary sticks. | ||
| 81 | if (button_id == PAD_STICK_ID) { | ||
| 82 | const int axis = params.Get("axis", 0); | ||
| 83 | const float threshold = params.Get("threshold", 0.25f); | ||
| 84 | const std::string direction_name = params.Get("direction", ""); | ||
| 85 | bool trigger_if_greater; | ||
| 86 | if (direction_name == "+") { | ||
| 87 | trigger_if_greater = true; | ||
| 88 | } else if (direction_name == "-") { | ||
| 89 | trigger_if_greater = false; | ||
| 90 | } else { | ||
| 91 | trigger_if_greater = true; | ||
| 92 | LOG_ERROR(Input, "Unknown direction {}", direction_name); | ||
| 93 | } | ||
| 94 | return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater, | ||
| 95 | adapter.get()); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | Common::ParamPackage GCButtonFactory::GetNextInput() { | ||
| 100 | Common::ParamPackage params; | ||
| 101 | GCAdapter::GCPadStatus pad; | ||
| 102 | auto& queue = adapter->GetPadQueue(); | ||
| 103 | for (std::size_t port = 0; port < queue.size(); ++port) { | ||
| 104 | while (queue[port].Pop(pad)) { | ||
| 105 | // This while loop will break on the earliest detected button | ||
| 106 | params.Set("engine", "gcpad"); | ||
| 107 | params.Set("port", static_cast<int>(port)); | ||
| 108 | for (const auto& button : GCAdapter::PadButtonArray) { | ||
| 109 | const u16 button_value = static_cast<u16>(button); | ||
| 110 | if (pad.button & button_value) { | ||
| 111 | params.Set("button", button_value); | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | // For Axis button implementation | ||
| 117 | if (pad.axis != GCAdapter::PadAxes::Undefined) { | ||
| 118 | params.Set("axis", static_cast<u8>(pad.axis)); | ||
| 119 | params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); | ||
| 120 | if (pad.axis_value > 128) { | ||
| 121 | params.Set("direction", "+"); | ||
| 122 | params.Set("threshold", "0.25"); | ||
| 123 | } else { | ||
| 124 | params.Set("direction", "-"); | ||
| 125 | params.Set("threshold", "-0.25"); | ||
| 126 | } | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | return params; | ||
| 132 | } | ||
| 133 | |||
| 134 | void GCButtonFactory::BeginConfiguration() { | ||
| 135 | polling = true; | ||
| 136 | adapter->BeginConfiguration(); | ||
| 137 | } | ||
| 138 | |||
| 139 | void GCButtonFactory::EndConfiguration() { | ||
| 140 | polling = false; | ||
| 141 | adapter->EndConfiguration(); | ||
| 142 | } | ||
| 143 | |||
| 144 | class GCAnalog final : public Input::AnalogDevice { | ||
| 145 | public: | ||
| 146 | GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter) | ||
| 147 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {} | ||
| 148 | |||
| 149 | float GetAxis(int axis) const { | ||
| 150 | std::lock_guard lock{mutex}; | ||
| 151 | // division is not by a perfect 128 to account for some variance in center location | ||
| 152 | // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range | ||
| 153 | // [20-230] | ||
| 154 | return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f; | ||
| 155 | } | ||
| 156 | |||
| 157 | std::pair<float, float> GetAnalog(int axis_x, int axis_y) const { | ||
| 158 | float x = GetAxis(axis_x); | ||
| 159 | float y = GetAxis(axis_y); | ||
| 160 | |||
| 161 | // Make sure the coordinates are in the unit circle, | ||
| 162 | // otherwise normalize it. | ||
| 163 | float r = x * x + y * y; | ||
| 164 | if (r > 1.0f) { | ||
| 165 | r = std::sqrt(r); | ||
| 166 | x /= r; | ||
| 167 | y /= r; | ||
| 168 | } | ||
| 169 | |||
| 170 | return {x, y}; | ||
| 171 | } | ||
| 172 | |||
| 173 | std::tuple<float, float> GetStatus() const override { | ||
| 174 | const auto [x, y] = GetAnalog(axis_x, axis_y); | ||
| 175 | const float r = std::sqrt((x * x) + (y * y)); | ||
| 176 | if (r > deadzone) { | ||
| 177 | return {x / r * (r - deadzone) / (1 - deadzone), | ||
| 178 | y / r * (r - deadzone) / (1 - deadzone)}; | ||
| 179 | } | ||
| 180 | return {0.0f, 0.0f}; | ||
| 181 | } | ||
| 182 | |||
| 183 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 184 | const auto [x, y] = GetStatus(); | ||
| 185 | const float directional_deadzone = 0.4f; | ||
| 186 | switch (direction) { | ||
| 187 | case Input::AnalogDirection::RIGHT: | ||
| 188 | return x > directional_deadzone; | ||
| 189 | case Input::AnalogDirection::LEFT: | ||
| 190 | return x < -directional_deadzone; | ||
| 191 | case Input::AnalogDirection::UP: | ||
| 192 | return y > directional_deadzone; | ||
| 193 | case Input::AnalogDirection::DOWN: | ||
| 194 | return y < -directional_deadzone; | ||
| 195 | } | ||
| 196 | return false; | ||
| 197 | } | ||
| 198 | |||
| 199 | private: | ||
| 200 | const int port; | ||
| 201 | const int axis_x; | ||
| 202 | const int axis_y; | ||
| 203 | const float deadzone; | ||
| 204 | mutable std::mutex mutex; | ||
| 205 | GCAdapter::Adapter* gcadapter; | ||
| 206 | }; | ||
| 207 | |||
| 208 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 209 | GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | ||
| 210 | : adapter(std::move(adapter_)) {} | ||
| 211 | |||
| 212 | /** | ||
| 213 | * Creates analog device from joystick axes | ||
| 214 | * @param params contains parameters for creating the device: | ||
| 215 | * - "port": the nth gcpad on the adapter | ||
| 216 | * - "axis_x": the index of the axis to be bind as x-axis | ||
| 217 | * - "axis_y": the index of the axis to be bind as y-axis | ||
| 218 | */ | ||
| 219 | std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) { | ||
| 220 | const int port = params.Get("port", 0); | ||
| 221 | const int axis_x = params.Get("axis_x", 0); | ||
| 222 | const int axis_y = params.Get("axis_y", 1); | ||
| 223 | const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); | ||
| 224 | |||
| 225 | return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); | ||
| 226 | } | ||
| 227 | |||
| 228 | void GCAnalogFactory::BeginConfiguration() { | ||
| 229 | polling = true; | ||
| 230 | adapter->BeginConfiguration(); | ||
| 231 | } | ||
| 232 | |||
| 233 | void GCAnalogFactory::EndConfiguration() { | ||
| 234 | polling = false; | ||
| 235 | adapter->EndConfiguration(); | ||
| 236 | } | ||
| 237 | |||
| 238 | Common::ParamPackage GCAnalogFactory::GetNextInput() { | ||
| 239 | GCAdapter::GCPadStatus pad; | ||
| 240 | auto& queue = adapter->GetPadQueue(); | ||
| 241 | for (std::size_t port = 0; port < queue.size(); ++port) { | ||
| 242 | while (queue[port].Pop(pad)) { | ||
| 243 | if (pad.axis == GCAdapter::PadAxes::Undefined || | ||
| 244 | std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) { | ||
| 245 | continue; | ||
| 246 | } | ||
| 247 | // An analog device needs two axes, so we need to store the axis for later and wait for | ||
| 248 | // a second input event. The axes also must be from the same joystick. | ||
| 249 | const u8 axis = static_cast<u8>(pad.axis); | ||
| 250 | if (analog_x_axis == -1) { | ||
| 251 | analog_x_axis = axis; | ||
| 252 | controller_number = port; | ||
| 253 | } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { | ||
| 254 | analog_y_axis = axis; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | Common::ParamPackage params; | ||
| 259 | if (analog_x_axis != -1 && analog_y_axis != -1) { | ||
| 260 | params.Set("engine", "gcpad"); | ||
| 261 | params.Set("port", controller_number); | ||
| 262 | params.Set("axis_x", analog_x_axis); | ||
| 263 | params.Set("axis_y", analog_y_axis); | ||
| 264 | analog_x_axis = -1; | ||
| 265 | analog_y_axis = -1; | ||
| 266 | controller_number = -1; | ||
| 267 | return params; | ||
| 268 | } | ||
| 269 | return params; | ||
| 270 | } | ||
| 271 | |||
| 272 | } // namespace InputCommon | ||
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h new file mode 100644 index 000000000..e96af7d51 --- /dev/null +++ b/src/input_common/gcadapter/gc_poller.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "core/frontend/input.h" | ||
| 9 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A button device factory representing a gcpad. It receives gcpad events and forward them | ||
| 15 | * to all button devices it created. | ||
| 16 | */ | ||
| 17 | class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> { | ||
| 18 | public: | ||
| 19 | explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Creates a button device from a button press | ||
| 23 | * @param params contains parameters for creating the device: | ||
| 24 | * - "code": the code of the key to bind with the button | ||
| 25 | */ | ||
| 26 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | ||
| 27 | |||
| 28 | Common::ParamPackage GetNextInput(); | ||
| 29 | |||
| 30 | /// For device input configuration/polling | ||
| 31 | void BeginConfiguration(); | ||
| 32 | void EndConfiguration(); | ||
| 33 | |||
| 34 | bool IsPolling() const { | ||
| 35 | return polling; | ||
| 36 | } | ||
| 37 | |||
| 38 | private: | ||
| 39 | std::shared_ptr<GCAdapter::Adapter> adapter; | ||
| 40 | bool polling = false; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 44 | class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> { | ||
| 45 | public: | ||
| 46 | explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); | ||
| 47 | |||
| 48 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; | ||
| 49 | Common::ParamPackage GetNextInput(); | ||
| 50 | |||
| 51 | /// For device input configuration/polling | ||
| 52 | void BeginConfiguration(); | ||
| 53 | void EndConfiguration(); | ||
| 54 | |||
| 55 | bool IsPolling() const { | ||
| 56 | return polling; | ||
| 57 | } | ||
| 58 | |||
| 59 | private: | ||
| 60 | std::shared_ptr<GCAdapter::Adapter> adapter; | ||
| 61 | int analog_x_axis = -1; | ||
| 62 | int analog_y_axis = -1; | ||
| 63 | int controller_number = -1; | ||
| 64 | bool polling = false; | ||
| 65 | }; | ||
| 66 | |||
| 67 | } // namespace InputCommon | ||
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index 078374be5..afb8e6612 100644 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp | |||
| @@ -76,7 +76,7 @@ std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage | |||
| 76 | int key_code = params.Get("code", 0); | 76 | int key_code = params.Get("code", 0); |
| 77 | std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); | 77 | std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); |
| 78 | key_button_list->AddKeyButton(key_code, button.get()); | 78 | key_button_list->AddKeyButton(key_code, button.get()); |
| 79 | return std::move(button); | 79 | return button; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | void Keyboard::PressKey(int key_code) { | 82 | void Keyboard::PressKey(int key_code) { |
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 95e351e24..fd0af1019 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -4,8 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | #include <libusb.h> | ||
| 7 | #include "common/param_package.h" | 8 | #include "common/param_package.h" |
| 8 | #include "input_common/analog_from_button.h" | 9 | #include "input_common/analog_from_button.h" |
| 10 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 11 | #include "input_common/gcadapter/gc_poller.h" | ||
| 9 | #include "input_common/keyboard.h" | 12 | #include "input_common/keyboard.h" |
| 10 | #include "input_common/main.h" | 13 | #include "input_common/main.h" |
| 11 | #include "input_common/motion_emu.h" | 14 | #include "input_common/motion_emu.h" |
| @@ -22,8 +25,16 @@ static std::shared_ptr<MotionEmu> motion_emu; | |||
| 22 | static std::unique_ptr<SDL::State> sdl; | 25 | static std::unique_ptr<SDL::State> sdl; |
| 23 | #endif | 26 | #endif |
| 24 | static std::unique_ptr<CemuhookUDP::State> udp; | 27 | static std::unique_ptr<CemuhookUDP::State> udp; |
| 28 | static std::shared_ptr<GCButtonFactory> gcbuttons; | ||
| 29 | static std::shared_ptr<GCAnalogFactory> gcanalog; | ||
| 25 | 30 | ||
| 26 | void Init() { | 31 | void Init() { |
| 32 | auto gcadapter = std::make_shared<GCAdapter::Adapter>(); | ||
| 33 | gcbuttons = std::make_shared<GCButtonFactory>(gcadapter); | ||
| 34 | Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); | ||
| 35 | gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); | ||
| 36 | Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); | ||
| 37 | |||
| 27 | keyboard = std::make_shared<Keyboard>(); | 38 | keyboard = std::make_shared<Keyboard>(); |
| 28 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); | 39 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); |
| 29 | Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", | 40 | Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", |
| @@ -48,6 +59,11 @@ void Shutdown() { | |||
| 48 | sdl.reset(); | 59 | sdl.reset(); |
| 49 | #endif | 60 | #endif |
| 50 | udp.reset(); | 61 | udp.reset(); |
| 62 | Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); | ||
| 63 | Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); | ||
| 64 | |||
| 65 | gcbuttons.reset(); | ||
| 66 | gcanalog.reset(); | ||
| 51 | } | 67 | } |
| 52 | 68 | ||
| 53 | Keyboard* GetKeyboard() { | 69 | Keyboard* GetKeyboard() { |
| @@ -58,6 +74,14 @@ MotionEmu* GetMotionEmu() { | |||
| 58 | return motion_emu.get(); | 74 | return motion_emu.get(); |
| 59 | } | 75 | } |
| 60 | 76 | ||
| 77 | GCButtonFactory* GetGCButtons() { | ||
| 78 | return gcbuttons.get(); | ||
| 79 | } | ||
| 80 | |||
| 81 | GCAnalogFactory* GetGCAnalogs() { | ||
| 82 | return gcanalog.get(); | ||
| 83 | } | ||
| 84 | |||
| 61 | std::string GenerateKeyboardParam(int key_code) { | 85 | std::string GenerateKeyboardParam(int key_code) { |
| 62 | Common::ParamPackage param{ | 86 | Common::ParamPackage param{ |
| 63 | {"engine", "keyboard"}, | 87 | {"engine", "keyboard"}, |
diff --git a/src/input_common/main.h b/src/input_common/main.h index 77a0ce90b..0e32856f6 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "input_common/gcadapter/gc_poller.h" | ||
| 10 | 11 | ||
| 11 | namespace Common { | 12 | namespace Common { |
| 12 | class ParamPackage; | 13 | class ParamPackage; |
| @@ -30,6 +31,10 @@ class MotionEmu; | |||
| 30 | /// Gets the motion emulation factory. | 31 | /// Gets the motion emulation factory. |
| 31 | MotionEmu* GetMotionEmu(); | 32 | MotionEmu* GetMotionEmu(); |
| 32 | 33 | ||
| 34 | GCButtonFactory* GetGCButtons(); | ||
| 35 | |||
| 36 | GCAnalogFactory* GetGCAnalogs(); | ||
| 37 | |||
| 33 | /// Generates a serialized param package for creating a keyboard button device | 38 | /// Generates a serialized param package for creating a keyboard button device |
| 34 | std::string GenerateKeyboardParam(int key_code); | 39 | std::string GenerateKeyboardParam(int key_code); |
| 35 | 40 | ||
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp index 868251628..d4cdf76a3 100644 --- a/src/input_common/motion_emu.cpp +++ b/src/input_common/motion_emu.cpp | |||
| @@ -145,7 +145,7 @@ std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackag | |||
| 145 | // Previously created device is disconnected here. Having two motion devices for 3DS is not | 145 | // Previously created device is disconnected here. Having two motion devices for 3DS is not |
| 146 | // expected. | 146 | // expected. |
| 147 | current_device = device_wrapper->device; | 147 | current_device = device_wrapper->device; |
| 148 | return std::move(device_wrapper); | 148 | return device_wrapper; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | void MotionEmu::BeginTilt(int x, int y) { | 151 | void MotionEmu::BeginTilt(int x, int y) { |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c7038b217..47ef30aa9 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | add_executable(tests | 1 | add_executable(tests |
| 2 | common/bit_field.cpp | 2 | common/bit_field.cpp |
| 3 | common/bit_utils.cpp | 3 | common/bit_utils.cpp |
| 4 | common/fibers.cpp | ||
| 4 | common/multi_level_queue.cpp | 5 | common/multi_level_queue.cpp |
| 5 | common/param_package.cpp | 6 | common/param_package.cpp |
| 6 | common/ring_buffer.cpp | 7 | common/ring_buffer.cpp |
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp new file mode 100644 index 000000000..4fd92428f --- /dev/null +++ b/src/tests/common/fibers.cpp | |||
| @@ -0,0 +1,358 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <atomic> | ||
| 6 | #include <cstdlib> | ||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <thread> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include <catch2/catch.hpp> | ||
| 14 | #include <math.h> | ||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "common/fiber.h" | ||
| 17 | #include "common/spin_lock.h" | ||
| 18 | |||
| 19 | namespace Common { | ||
| 20 | |||
| 21 | class TestControl1 { | ||
| 22 | public: | ||
| 23 | TestControl1() = default; | ||
| 24 | |||
| 25 | void DoWork(); | ||
| 26 | |||
| 27 | void ExecuteThread(u32 id); | ||
| 28 | |||
| 29 | std::unordered_map<std::thread::id, u32> ids; | ||
| 30 | std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; | ||
| 31 | std::vector<std::shared_ptr<Common::Fiber>> work_fibers; | ||
| 32 | std::vector<u32> items; | ||
| 33 | std::vector<u32> results; | ||
| 34 | }; | ||
| 35 | |||
| 36 | static void WorkControl1(void* control) { | ||
| 37 | auto* test_control = static_cast<TestControl1*>(control); | ||
| 38 | test_control->DoWork(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void TestControl1::DoWork() { | ||
| 42 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 43 | u32 id = ids[this_id]; | ||
| 44 | u32 value = items[id]; | ||
| 45 | for (u32 i = 0; i < id; i++) { | ||
| 46 | value++; | ||
| 47 | } | ||
| 48 | results[id] = value; | ||
| 49 | Fiber::YieldTo(work_fibers[id], thread_fibers[id]); | ||
| 50 | } | ||
| 51 | |||
| 52 | void TestControl1::ExecuteThread(u32 id) { | ||
| 53 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 54 | ids[this_id] = id; | ||
| 55 | auto thread_fiber = Fiber::ThreadToFiber(); | ||
| 56 | thread_fibers[id] = thread_fiber; | ||
| 57 | work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); | ||
| 58 | items[id] = rand() % 256; | ||
| 59 | Fiber::YieldTo(thread_fibers[id], work_fibers[id]); | ||
| 60 | thread_fibers[id]->Exit(); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void ThreadStart1(u32 id, TestControl1& test_control) { | ||
| 64 | test_control.ExecuteThread(id); | ||
| 65 | } | ||
| 66 | |||
| 67 | /** This test checks for fiber setup configuration and validates that fibers are | ||
| 68 | * doing all the work required. | ||
| 69 | */ | ||
| 70 | TEST_CASE("Fibers::Setup", "[common]") { | ||
| 71 | constexpr std::size_t num_threads = 7; | ||
| 72 | TestControl1 test_control{}; | ||
| 73 | test_control.thread_fibers.resize(num_threads); | ||
| 74 | test_control.work_fibers.resize(num_threads); | ||
| 75 | test_control.items.resize(num_threads, 0); | ||
| 76 | test_control.results.resize(num_threads, 0); | ||
| 77 | std::vector<std::thread> threads; | ||
| 78 | for (u32 i = 0; i < num_threads; i++) { | ||
| 79 | threads.emplace_back(ThreadStart1, i, std::ref(test_control)); | ||
| 80 | } | ||
| 81 | for (u32 i = 0; i < num_threads; i++) { | ||
| 82 | threads[i].join(); | ||
| 83 | } | ||
| 84 | for (u32 i = 0; i < num_threads; i++) { | ||
| 85 | REQUIRE(test_control.items[i] + i == test_control.results[i]); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | class TestControl2 { | ||
| 90 | public: | ||
| 91 | TestControl2() = default; | ||
| 92 | |||
| 93 | void DoWork1() { | ||
| 94 | trap2 = false; | ||
| 95 | while (trap.load()) | ||
| 96 | ; | ||
| 97 | for (u32 i = 0; i < 12000; i++) { | ||
| 98 | value1 += i; | ||
| 99 | } | ||
| 100 | Fiber::YieldTo(fiber1, fiber3); | ||
| 101 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 102 | u32 id = ids[this_id]; | ||
| 103 | assert1 = id == 1; | ||
| 104 | value2 += 5000; | ||
| 105 | Fiber::YieldTo(fiber1, thread_fibers[id]); | ||
| 106 | } | ||
| 107 | |||
| 108 | void DoWork2() { | ||
| 109 | while (trap2.load()) | ||
| 110 | ; | ||
| 111 | value2 = 2000; | ||
| 112 | trap = false; | ||
| 113 | Fiber::YieldTo(fiber2, fiber1); | ||
| 114 | assert3 = false; | ||
| 115 | } | ||
| 116 | |||
| 117 | void DoWork3() { | ||
| 118 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 119 | u32 id = ids[this_id]; | ||
| 120 | assert2 = id == 0; | ||
| 121 | value1 += 1000; | ||
| 122 | Fiber::YieldTo(fiber3, thread_fibers[id]); | ||
| 123 | } | ||
| 124 | |||
| 125 | void ExecuteThread(u32 id); | ||
| 126 | |||
| 127 | void CallFiber1() { | ||
| 128 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 129 | u32 id = ids[this_id]; | ||
| 130 | Fiber::YieldTo(thread_fibers[id], fiber1); | ||
| 131 | } | ||
| 132 | |||
| 133 | void CallFiber2() { | ||
| 134 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 135 | u32 id = ids[this_id]; | ||
| 136 | Fiber::YieldTo(thread_fibers[id], fiber2); | ||
| 137 | } | ||
| 138 | |||
| 139 | void Exit(); | ||
| 140 | |||
| 141 | bool assert1{}; | ||
| 142 | bool assert2{}; | ||
| 143 | bool assert3{true}; | ||
| 144 | u32 value1{}; | ||
| 145 | u32 value2{}; | ||
| 146 | std::atomic<bool> trap{true}; | ||
| 147 | std::atomic<bool> trap2{true}; | ||
| 148 | std::unordered_map<std::thread::id, u32> ids; | ||
| 149 | std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; | ||
| 150 | std::shared_ptr<Common::Fiber> fiber1; | ||
| 151 | std::shared_ptr<Common::Fiber> fiber2; | ||
| 152 | std::shared_ptr<Common::Fiber> fiber3; | ||
| 153 | }; | ||
| 154 | |||
| 155 | static void WorkControl2_1(void* control) { | ||
| 156 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 157 | test_control->DoWork1(); | ||
| 158 | } | ||
| 159 | |||
| 160 | static void WorkControl2_2(void* control) { | ||
| 161 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 162 | test_control->DoWork2(); | ||
| 163 | } | ||
| 164 | |||
| 165 | static void WorkControl2_3(void* control) { | ||
| 166 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 167 | test_control->DoWork3(); | ||
| 168 | } | ||
| 169 | |||
| 170 | void TestControl2::ExecuteThread(u32 id) { | ||
| 171 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 172 | ids[this_id] = id; | ||
| 173 | auto thread_fiber = Fiber::ThreadToFiber(); | ||
| 174 | thread_fibers[id] = thread_fiber; | ||
| 175 | } | ||
| 176 | |||
| 177 | void TestControl2::Exit() { | ||
| 178 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 179 | u32 id = ids[this_id]; | ||
| 180 | thread_fibers[id]->Exit(); | ||
| 181 | } | ||
| 182 | |||
| 183 | static void ThreadStart2_1(u32 id, TestControl2& test_control) { | ||
| 184 | test_control.ExecuteThread(id); | ||
| 185 | test_control.CallFiber1(); | ||
| 186 | test_control.Exit(); | ||
| 187 | } | ||
| 188 | |||
| 189 | static void ThreadStart2_2(u32 id, TestControl2& test_control) { | ||
| 190 | test_control.ExecuteThread(id); | ||
| 191 | test_control.CallFiber2(); | ||
| 192 | test_control.Exit(); | ||
| 193 | } | ||
| 194 | |||
| 195 | /** This test checks for fiber thread exchange configuration and validates that fibers are | ||
| 196 | * that a fiber has been succesfully transfered from one thread to another and that the TLS | ||
| 197 | * region of the thread is kept while changing fibers. | ||
| 198 | */ | ||
| 199 | TEST_CASE("Fibers::InterExchange", "[common]") { | ||
| 200 | TestControl2 test_control{}; | ||
| 201 | test_control.thread_fibers.resize(2); | ||
| 202 | test_control.fiber1 = | ||
| 203 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_1}, &test_control); | ||
| 204 | test_control.fiber2 = | ||
| 205 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_2}, &test_control); | ||
| 206 | test_control.fiber3 = | ||
| 207 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_3}, &test_control); | ||
| 208 | std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); | ||
| 209 | std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); | ||
| 210 | thread1.join(); | ||
| 211 | thread2.join(); | ||
| 212 | REQUIRE(test_control.assert1); | ||
| 213 | REQUIRE(test_control.assert2); | ||
| 214 | REQUIRE(test_control.assert3); | ||
| 215 | REQUIRE(test_control.value2 == 7000); | ||
| 216 | u32 cal_value = 0; | ||
| 217 | for (u32 i = 0; i < 12000; i++) { | ||
| 218 | cal_value += i; | ||
| 219 | } | ||
| 220 | cal_value += 1000; | ||
| 221 | REQUIRE(test_control.value1 == cal_value); | ||
| 222 | } | ||
| 223 | |||
| 224 | class TestControl3 { | ||
| 225 | public: | ||
| 226 | TestControl3() = default; | ||
| 227 | |||
| 228 | void DoWork1() { | ||
| 229 | value1 += 1; | ||
| 230 | Fiber::YieldTo(fiber1, fiber2); | ||
| 231 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 232 | u32 id = ids[this_id]; | ||
| 233 | value3 += 1; | ||
| 234 | Fiber::YieldTo(fiber1, thread_fibers[id]); | ||
| 235 | } | ||
| 236 | |||
| 237 | void DoWork2() { | ||
| 238 | value2 += 1; | ||
| 239 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 240 | u32 id = ids[this_id]; | ||
| 241 | Fiber::YieldTo(fiber2, thread_fibers[id]); | ||
| 242 | } | ||
| 243 | |||
| 244 | void ExecuteThread(u32 id); | ||
| 245 | |||
| 246 | void CallFiber1() { | ||
| 247 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 248 | u32 id = ids[this_id]; | ||
| 249 | Fiber::YieldTo(thread_fibers[id], fiber1); | ||
| 250 | } | ||
| 251 | |||
| 252 | void Exit(); | ||
| 253 | |||
| 254 | u32 value1{}; | ||
| 255 | u32 value2{}; | ||
| 256 | u32 value3{}; | ||
| 257 | std::unordered_map<std::thread::id, u32> ids; | ||
| 258 | std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; | ||
| 259 | std::shared_ptr<Common::Fiber> fiber1; | ||
| 260 | std::shared_ptr<Common::Fiber> fiber2; | ||
| 261 | }; | ||
| 262 | |||
| 263 | static void WorkControl3_1(void* control) { | ||
| 264 | auto* test_control = static_cast<TestControl3*>(control); | ||
| 265 | test_control->DoWork1(); | ||
| 266 | } | ||
| 267 | |||
| 268 | static void WorkControl3_2(void* control) { | ||
| 269 | auto* test_control = static_cast<TestControl3*>(control); | ||
| 270 | test_control->DoWork2(); | ||
| 271 | } | ||
| 272 | |||
| 273 | void TestControl3::ExecuteThread(u32 id) { | ||
| 274 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 275 | ids[this_id] = id; | ||
| 276 | auto thread_fiber = Fiber::ThreadToFiber(); | ||
| 277 | thread_fibers[id] = thread_fiber; | ||
| 278 | } | ||
| 279 | |||
| 280 | void TestControl3::Exit() { | ||
| 281 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 282 | u32 id = ids[this_id]; | ||
| 283 | thread_fibers[id]->Exit(); | ||
| 284 | } | ||
| 285 | |||
| 286 | static void ThreadStart3(u32 id, TestControl3& test_control) { | ||
| 287 | test_control.ExecuteThread(id); | ||
| 288 | test_control.CallFiber1(); | ||
| 289 | test_control.Exit(); | ||
| 290 | } | ||
| 291 | |||
| 292 | /** This test checks for one two threads racing for starting the same fiber. | ||
| 293 | * It checks execution occured in an ordered manner and by no time there were | ||
| 294 | * two contexts at the same time. | ||
| 295 | */ | ||
| 296 | TEST_CASE("Fibers::StartRace", "[common]") { | ||
| 297 | TestControl3 test_control{}; | ||
| 298 | test_control.thread_fibers.resize(2); | ||
| 299 | test_control.fiber1 = | ||
| 300 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_1}, &test_control); | ||
| 301 | test_control.fiber2 = | ||
| 302 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_2}, &test_control); | ||
| 303 | std::thread thread1(ThreadStart3, 0, std::ref(test_control)); | ||
| 304 | std::thread thread2(ThreadStart3, 1, std::ref(test_control)); | ||
| 305 | thread1.join(); | ||
| 306 | thread2.join(); | ||
| 307 | REQUIRE(test_control.value1 == 1); | ||
| 308 | REQUIRE(test_control.value2 == 1); | ||
| 309 | REQUIRE(test_control.value3 == 1); | ||
| 310 | } | ||
| 311 | |||
| 312 | class TestControl4; | ||
| 313 | |||
| 314 | static void WorkControl4(void* control); | ||
| 315 | |||
| 316 | class TestControl4 { | ||
| 317 | public: | ||
| 318 | TestControl4() { | ||
| 319 | fiber1 = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl4}, this); | ||
| 320 | goal_reached = false; | ||
| 321 | rewinded = false; | ||
| 322 | } | ||
| 323 | |||
| 324 | void Execute() { | ||
| 325 | thread_fiber = Fiber::ThreadToFiber(); | ||
| 326 | Fiber::YieldTo(thread_fiber, fiber1); | ||
| 327 | thread_fiber->Exit(); | ||
| 328 | } | ||
| 329 | |||
| 330 | void DoWork() { | ||
| 331 | fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); | ||
| 332 | if (rewinded) { | ||
| 333 | goal_reached = true; | ||
| 334 | Fiber::YieldTo(fiber1, thread_fiber); | ||
| 335 | } | ||
| 336 | rewinded = true; | ||
| 337 | fiber1->Rewind(); | ||
| 338 | } | ||
| 339 | |||
| 340 | std::shared_ptr<Common::Fiber> fiber1; | ||
| 341 | std::shared_ptr<Common::Fiber> thread_fiber; | ||
| 342 | bool goal_reached; | ||
| 343 | bool rewinded; | ||
| 344 | }; | ||
| 345 | |||
| 346 | static void WorkControl4(void* control) { | ||
| 347 | auto* test_control = static_cast<TestControl4*>(control); | ||
| 348 | test_control->DoWork(); | ||
| 349 | } | ||
| 350 | |||
| 351 | TEST_CASE("Fibers::Rewind", "[common]") { | ||
| 352 | TestControl4 test_control{}; | ||
| 353 | test_control.Execute(); | ||
| 354 | REQUIRE(test_control.goal_reached); | ||
| 355 | REQUIRE(test_control.rewinded); | ||
| 356 | } | ||
| 357 | |||
| 358 | } // namespace Common | ||
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index ff2d11cc8..e66db1940 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -18,29 +18,26 @@ namespace { | |||
| 18 | // Numbers are chosen randomly to make sure the correct one is given. | 18 | // Numbers are chosen randomly to make sure the correct one is given. |
| 19 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; | 19 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; |
| 20 | constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals | 20 | constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals |
| 21 | constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}}; | ||
| 22 | std::array<s64, 5> delays{}; | ||
| 21 | 23 | ||
| 22 | std::bitset<CB_IDS.size()> callbacks_ran_flags; | 24 | std::bitset<CB_IDS.size()> callbacks_ran_flags; |
| 23 | u64 expected_callback = 0; | 25 | u64 expected_callback = 0; |
| 24 | s64 lateness = 0; | ||
| 25 | 26 | ||
| 26 | template <unsigned int IDX> | 27 | template <unsigned int IDX> |
| 27 | void CallbackTemplate(u64 userdata, s64 cycles_late) { | 28 | void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { |
| 28 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); | 29 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); |
| 29 | callbacks_ran_flags.set(IDX); | 30 | callbacks_ran_flags.set(IDX); |
| 30 | REQUIRE(CB_IDS[IDX] == userdata); | 31 | REQUIRE(CB_IDS[IDX] == userdata); |
| 31 | REQUIRE(CB_IDS[IDX] == expected_callback); | 32 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); |
| 32 | REQUIRE(lateness == cycles_late); | 33 | delays[IDX] = nanoseconds_late; |
| 33 | } | 34 | ++expected_callback; |
| 34 | |||
| 35 | u64 callbacks_done = 0; | ||
| 36 | |||
| 37 | void EmptyCallback(u64 userdata, s64 cycles_late) { | ||
| 38 | ++callbacks_done; | ||
| 39 | } | 35 | } |
| 40 | 36 | ||
| 41 | struct ScopeInit final { | 37 | struct ScopeInit final { |
| 42 | ScopeInit() { | 38 | ScopeInit() { |
| 43 | core_timing.Initialize(); | 39 | core_timing.SetMulticore(true); |
| 40 | core_timing.Initialize([]() {}); | ||
| 44 | } | 41 | } |
| 45 | ~ScopeInit() { | 42 | ~ScopeInit() { |
| 46 | core_timing.Shutdown(); | 43 | core_timing.Shutdown(); |
| @@ -49,110 +46,101 @@ struct ScopeInit final { | |||
| 49 | Core::Timing::CoreTiming core_timing; | 46 | Core::Timing::CoreTiming core_timing; |
| 50 | }; | 47 | }; |
| 51 | 48 | ||
| 52 | void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, | 49 | #pragma optimize("", off) |
| 53 | int expected_lateness = 0, int cpu_downcount = 0) { | ||
| 54 | callbacks_ran_flags = 0; | ||
| 55 | expected_callback = CB_IDS[idx]; | ||
| 56 | lateness = expected_lateness; | ||
| 57 | |||
| 58 | // Pretend we executed X cycles of instructions. | ||
| 59 | core_timing.SwitchContext(context); | ||
| 60 | core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount); | ||
| 61 | core_timing.Advance(); | ||
| 62 | core_timing.SwitchContext((context + 1) % 4); | ||
| 63 | 50 | ||
| 64 | REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); | 51 | u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { |
| 52 | u64 start = core_timing.GetGlobalTimeNs().count(); | ||
| 53 | u64 placebo = 0; | ||
| 54 | for (std::size_t i = 0; i < 1000; i++) { | ||
| 55 | placebo += core_timing.GetGlobalTimeNs().count(); | ||
| 56 | } | ||
| 57 | u64 end = core_timing.GetGlobalTimeNs().count(); | ||
| 58 | return (end - start); | ||
| 65 | } | 59 | } |
| 60 | |||
| 61 | #pragma optimize("", on) | ||
| 62 | |||
| 66 | } // Anonymous namespace | 63 | } // Anonymous namespace |
| 67 | 64 | ||
| 68 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | 65 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { |
| 69 | ScopeInit guard; | 66 | ScopeInit guard; |
| 70 | auto& core_timing = guard.core_timing; | 67 | auto& core_timing = guard.core_timing; |
| 68 | std::vector<std::shared_ptr<Core::Timing::EventType>> events{ | ||
| 69 | Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), | ||
| 70 | Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), | ||
| 71 | Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), | ||
| 72 | Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), | ||
| 73 | Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), | ||
| 74 | }; | ||
| 75 | |||
| 76 | expected_callback = 0; | ||
| 77 | |||
| 78 | core_timing.SyncPause(true); | ||
| 79 | |||
| 80 | u64 one_micro = 1000U; | ||
| 81 | for (std::size_t i = 0; i < events.size(); i++) { | ||
| 82 | u64 order = calls_order[i]; | ||
| 83 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | ||
| 84 | } | ||
| 85 | /// test pause | ||
| 86 | REQUIRE(callbacks_ran_flags.none()); | ||
| 71 | 87 | ||
| 72 | std::shared_ptr<Core::Timing::EventType> cb_a = | 88 | core_timing.Pause(false); // No need to sync |
| 73 | Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | ||
| 74 | std::shared_ptr<Core::Timing::EventType> cb_b = | ||
| 75 | Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||
| 76 | std::shared_ptr<Core::Timing::EventType> cb_c = | ||
| 77 | Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>); | ||
| 78 | std::shared_ptr<Core::Timing::EventType> cb_d = | ||
| 79 | Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>); | ||
| 80 | std::shared_ptr<Core::Timing::EventType> cb_e = | ||
| 81 | Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>); | ||
| 82 | |||
| 83 | // Enter slice 0 | ||
| 84 | core_timing.ResetRun(); | ||
| 85 | |||
| 86 | // D -> B -> C -> A -> E | ||
| 87 | core_timing.SwitchContext(0); | ||
| 88 | core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); | ||
| 89 | REQUIRE(1000 == core_timing.GetDowncount()); | ||
| 90 | core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]); | ||
| 91 | REQUIRE(500 == core_timing.GetDowncount()); | ||
| 92 | core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]); | ||
| 93 | REQUIRE(500 == core_timing.GetDowncount()); | ||
| 94 | core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]); | ||
| 95 | REQUIRE(100 == core_timing.GetDowncount()); | ||
| 96 | core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); | ||
| 97 | REQUIRE(100 == core_timing.GetDowncount()); | ||
| 98 | |||
| 99 | AdvanceAndCheck(core_timing, 3, 0); | ||
| 100 | AdvanceAndCheck(core_timing, 1, 1); | ||
| 101 | AdvanceAndCheck(core_timing, 2, 2); | ||
| 102 | AdvanceAndCheck(core_timing, 0, 3); | ||
| 103 | AdvanceAndCheck(core_timing, 4, 0); | ||
| 104 | } | ||
| 105 | |||
| 106 | TEST_CASE("CoreTiming[FairSharing]", "[core]") { | ||
| 107 | 89 | ||
| 108 | ScopeInit guard; | 90 | while (core_timing.HasPendingEvents()) |
| 109 | auto& core_timing = guard.core_timing; | 91 | ; |
| 110 | 92 | ||
| 111 | std::shared_ptr<Core::Timing::EventType> empty_callback = | 93 | REQUIRE(callbacks_ran_flags.all()); |
| 112 | Core::Timing::CreateEvent("empty_callback", EmptyCallback); | ||
| 113 | 94 | ||
| 114 | callbacks_done = 0; | 95 | for (std::size_t i = 0; i < delays.size(); i++) { |
| 115 | u64 MAX_CALLBACKS = 10; | 96 | const double delay = static_cast<double>(delays[i]); |
| 116 | for (std::size_t i = 0; i < 10; i++) { | 97 | const double micro = delay / 1000.0f; |
| 117 | core_timing.ScheduleEvent(i * 3333U, empty_callback, 0); | 98 | const double mili = micro / 1000.0f; |
| 99 | printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); | ||
| 118 | } | 100 | } |
| 119 | |||
| 120 | const s64 advances = MAX_SLICE_LENGTH / 10; | ||
| 121 | core_timing.ResetRun(); | ||
| 122 | u64 current_time = core_timing.GetTicks(); | ||
| 123 | bool keep_running{}; | ||
| 124 | do { | ||
| 125 | keep_running = false; | ||
| 126 | for (u32 active_core = 0; active_core < 4; ++active_core) { | ||
| 127 | core_timing.SwitchContext(active_core); | ||
| 128 | if (core_timing.CanCurrentContextRun()) { | ||
| 129 | core_timing.AddTicks(std::min<s64>(advances, core_timing.GetDowncount())); | ||
| 130 | core_timing.Advance(); | ||
| 131 | } | ||
| 132 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 133 | } | ||
| 134 | } while (keep_running); | ||
| 135 | u64 current_time_2 = core_timing.GetTicks(); | ||
| 136 | |||
| 137 | REQUIRE(MAX_CALLBACKS == callbacks_done); | ||
| 138 | REQUIRE(current_time_2 == current_time + MAX_SLICE_LENGTH * 4); | ||
| 139 | } | 101 | } |
| 140 | 102 | ||
| 141 | TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { | 103 | TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") { |
| 142 | ScopeInit guard; | 104 | ScopeInit guard; |
| 143 | auto& core_timing = guard.core_timing; | 105 | auto& core_timing = guard.core_timing; |
| 106 | std::vector<std::shared_ptr<Core::Timing::EventType>> events{ | ||
| 107 | Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), | ||
| 108 | Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), | ||
| 109 | Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), | ||
| 110 | Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), | ||
| 111 | Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), | ||
| 112 | }; | ||
| 113 | |||
| 114 | core_timing.SyncPause(true); | ||
| 115 | core_timing.SyncPause(false); | ||
| 116 | |||
| 117 | expected_callback = 0; | ||
| 118 | |||
| 119 | u64 start = core_timing.GetGlobalTimeNs().count(); | ||
| 120 | u64 one_micro = 1000U; | ||
| 121 | for (std::size_t i = 0; i < events.size(); i++) { | ||
| 122 | u64 order = calls_order[i]; | ||
| 123 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | ||
| 124 | } | ||
| 125 | u64 end = core_timing.GetGlobalTimeNs().count(); | ||
| 126 | const double scheduling_time = static_cast<double>(end - start); | ||
| 127 | const double timer_time = static_cast<double>(TestTimerSpeed(core_timing)); | ||
| 144 | 128 | ||
| 145 | std::shared_ptr<Core::Timing::EventType> cb_a = | 129 | while (core_timing.HasPendingEvents()) |
| 146 | Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | 130 | ; |
| 147 | std::shared_ptr<Core::Timing::EventType> cb_b = | ||
| 148 | Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||
| 149 | 131 | ||
| 150 | // Enter slice 0 | 132 | REQUIRE(callbacks_ran_flags.all()); |
| 151 | core_timing.ResetRun(); | ||
| 152 | 133 | ||
| 153 | core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]); | 134 | for (std::size_t i = 0; i < delays.size(); i++) { |
| 154 | core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]); | 135 | const double delay = static_cast<double>(delays[i]); |
| 136 | const double micro = delay / 1000.0f; | ||
| 137 | const double mili = micro / 1000.0f; | ||
| 138 | printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); | ||
| 139 | } | ||
| 155 | 140 | ||
| 156 | AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) | 141 | const double micro = scheduling_time / 1000.0f; |
| 157 | AdvanceAndCheck(core_timing, 1, 1, 50, -50); | 142 | const double mili = micro / 1000.0f; |
| 143 | printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili); | ||
| 144 | printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, | ||
| 145 | timer_time / 1000000.f); | ||
| 158 | } | 146 | } |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index d6ee82836..21c46a567 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -3,6 +3,8 @@ add_library(video_core STATIC | |||
| 3 | buffer_cache/buffer_cache.h | 3 | buffer_cache/buffer_cache.h |
| 4 | buffer_cache/map_interval.cpp | 4 | buffer_cache/map_interval.cpp |
| 5 | buffer_cache/map_interval.h | 5 | buffer_cache/map_interval.h |
| 6 | compatible_formats.cpp | ||
| 7 | compatible_formats.h | ||
| 6 | dirty_flags.cpp | 8 | dirty_flags.cpp |
| 7 | dirty_flags.h | 9 | dirty_flags.h |
| 8 | dma_pusher.cpp | 10 | dma_pusher.cpp |
| @@ -25,6 +27,14 @@ add_library(video_core STATIC | |||
| 25 | engines/shader_bytecode.h | 27 | engines/shader_bytecode.h |
| 26 | engines/shader_header.h | 28 | engines/shader_header.h |
| 27 | engines/shader_type.h | 29 | engines/shader_type.h |
| 30 | macro/macro.cpp | ||
| 31 | macro/macro.h | ||
| 32 | macro/macro_hle.cpp | ||
| 33 | macro/macro_hle.h | ||
| 34 | macro/macro_interpreter.cpp | ||
| 35 | macro/macro_interpreter.h | ||
| 36 | macro/macro_jit_x64.cpp | ||
| 37 | macro/macro_jit_x64.h | ||
| 28 | fence_manager.h | 38 | fence_manager.h |
| 29 | gpu.cpp | 39 | gpu.cpp |
| 30 | gpu.h | 40 | gpu.h |
| @@ -36,8 +46,6 @@ add_library(video_core STATIC | |||
| 36 | gpu_thread.h | 46 | gpu_thread.h |
| 37 | guest_driver.cpp | 47 | guest_driver.cpp |
| 38 | guest_driver.h | 48 | guest_driver.h |
| 39 | macro_interpreter.cpp | ||
| 40 | macro_interpreter.h | ||
| 41 | memory_manager.cpp | 49 | memory_manager.cpp |
| 42 | memory_manager.h | 50 | memory_manager.h |
| 43 | morton.cpp | 51 | morton.cpp |
| @@ -45,11 +53,11 @@ add_library(video_core STATIC | |||
| 45 | query_cache.h | 53 | query_cache.h |
| 46 | rasterizer_accelerated.cpp | 54 | rasterizer_accelerated.cpp |
| 47 | rasterizer_accelerated.h | 55 | rasterizer_accelerated.h |
| 48 | rasterizer_cache.cpp | ||
| 49 | rasterizer_cache.h | ||
| 50 | rasterizer_interface.h | 56 | rasterizer_interface.h |
| 51 | renderer_base.cpp | 57 | renderer_base.cpp |
| 52 | renderer_base.h | 58 | renderer_base.h |
| 59 | renderer_opengl/gl_arb_decompiler.cpp | ||
| 60 | renderer_opengl/gl_arb_decompiler.h | ||
| 53 | renderer_opengl/gl_buffer_cache.cpp | 61 | renderer_opengl/gl_buffer_cache.cpp |
| 54 | renderer_opengl/gl_buffer_cache.h | 62 | renderer_opengl/gl_buffer_cache.h |
| 55 | renderer_opengl/gl_device.cpp | 63 | renderer_opengl/gl_device.cpp |
| @@ -89,6 +97,7 @@ add_library(video_core STATIC | |||
| 89 | renderer_opengl/utils.h | 97 | renderer_opengl/utils.h |
| 90 | sampler_cache.cpp | 98 | sampler_cache.cpp |
| 91 | sampler_cache.h | 99 | sampler_cache.h |
| 100 | shader_cache.h | ||
| 92 | shader/decode/arithmetic.cpp | 101 | shader/decode/arithmetic.cpp |
| 93 | shader/decode/arithmetic_immediate.cpp | 102 | shader/decode/arithmetic_immediate.cpp |
| 94 | shader/decode/bfe.cpp | 103 | shader/decode/bfe.cpp |
diff --git a/src/video_core/buffer_cache/buffer_block.h b/src/video_core/buffer_cache/buffer_block.h index e35ee0b67..e64170e66 100644 --- a/src/video_core/buffer_cache/buffer_block.h +++ b/src/video_core/buffer_cache/buffer_block.h | |||
| @@ -15,48 +15,47 @@ namespace VideoCommon { | |||
| 15 | 15 | ||
| 16 | class BufferBlock { | 16 | class BufferBlock { |
| 17 | public: | 17 | public: |
| 18 | bool Overlaps(const VAddr start, const VAddr end) const { | 18 | bool Overlaps(VAddr start, VAddr end) const { |
| 19 | return (cpu_addr < end) && (cpu_addr_end > start); | 19 | return (cpu_addr < end) && (cpu_addr_end > start); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | bool IsInside(const VAddr other_start, const VAddr other_end) const { | 22 | bool IsInside(VAddr other_start, VAddr other_end) const { |
| 23 | return cpu_addr <= other_start && other_end <= cpu_addr_end; | 23 | return cpu_addr <= other_start && other_end <= cpu_addr_end; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | std::size_t GetOffset(const VAddr in_addr) { | 26 | std::size_t Offset(VAddr in_addr) const { |
| 27 | return static_cast<std::size_t>(in_addr - cpu_addr); | 27 | return static_cast<std::size_t>(in_addr - cpu_addr); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | VAddr GetCpuAddr() const { | 30 | VAddr CpuAddr() const { |
| 31 | return cpu_addr; | 31 | return cpu_addr; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | VAddr GetCpuAddrEnd() const { | 34 | VAddr CpuAddrEnd() const { |
| 35 | return cpu_addr_end; | 35 | return cpu_addr_end; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | void SetCpuAddr(const VAddr new_addr) { | 38 | void SetCpuAddr(VAddr new_addr) { |
| 39 | cpu_addr = new_addr; | 39 | cpu_addr = new_addr; |
| 40 | cpu_addr_end = new_addr + size; | 40 | cpu_addr_end = new_addr + size; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | std::size_t GetSize() const { | 43 | std::size_t Size() const { |
| 44 | return size; | 44 | return size; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | void SetEpoch(u64 new_epoch) { | 47 | u64 Epoch() const { |
| 48 | epoch = new_epoch; | 48 | return epoch; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | u64 GetEpoch() { | 51 | void SetEpoch(u64 new_epoch) { |
| 52 | return epoch; | 52 | epoch = new_epoch; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | protected: | 55 | protected: |
| 56 | explicit BufferBlock(VAddr cpu_addr, const std::size_t size) : size{size} { | 56 | explicit BufferBlock(VAddr cpu_addr_, std::size_t size_) : size{size_} { |
| 57 | SetCpuAddr(cpu_addr); | 57 | SetCpuAddr(cpu_addr_); |
| 58 | } | 58 | } |
| 59 | ~BufferBlock() = default; | ||
| 60 | 59 | ||
| 61 | private: | 60 | private: |
| 62 | VAddr cpu_addr{}; | 61 | VAddr cpu_addr{}; |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index d9a4a1b4d..dd7ce8c99 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -30,23 +30,31 @@ | |||
| 30 | 30 | ||
| 31 | namespace VideoCommon { | 31 | namespace VideoCommon { |
| 32 | 32 | ||
| 33 | template <typename OwnerBuffer, typename BufferType, typename StreamBuffer> | 33 | template <typename Buffer, typename BufferType, typename StreamBuffer> |
| 34 | class BufferCache { | 34 | class BufferCache { |
| 35 | using IntervalSet = boost::icl::interval_set<VAddr>; | 35 | using IntervalSet = boost::icl::interval_set<VAddr>; |
| 36 | using IntervalType = typename IntervalSet::interval_type; | 36 | using IntervalType = typename IntervalSet::interval_type; |
| 37 | using VectorMapInterval = boost::container::small_vector<MapInterval*, 1>; | 37 | using VectorMapInterval = boost::container::small_vector<MapInterval*, 1>; |
| 38 | 38 | ||
| 39 | static constexpr u64 WRITE_PAGE_BIT = 11; | ||
| 40 | static constexpr u64 BLOCK_PAGE_BITS = 21; | ||
| 41 | static constexpr u64 BLOCK_PAGE_SIZE = 1ULL << BLOCK_PAGE_BITS; | ||
| 42 | |||
| 39 | public: | 43 | public: |
| 40 | using BufferInfo = std::pair<BufferType, u64>; | 44 | struct BufferInfo { |
| 45 | BufferType handle; | ||
| 46 | u64 offset; | ||
| 47 | u64 address; | ||
| 48 | }; | ||
| 41 | 49 | ||
| 42 | BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, | 50 | BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, |
| 43 | bool is_written = false, bool use_fast_cbuf = false) { | 51 | bool is_written = false, bool use_fast_cbuf = false) { |
| 44 | std::lock_guard lock{mutex}; | 52 | std::lock_guard lock{mutex}; |
| 45 | 53 | ||
| 46 | const auto& memory_manager = system.GPU().MemoryManager(); | 54 | auto& memory_manager = system.GPU().MemoryManager(); |
| 47 | const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); | 55 | const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); |
| 48 | if (!cpu_addr_opt) { | 56 | if (!cpu_addr_opt) { |
| 49 | return {GetEmptyBuffer(size), 0}; | 57 | return GetEmptyBuffer(size); |
| 50 | } | 58 | } |
| 51 | const VAddr cpu_addr = *cpu_addr_opt; | 59 | const VAddr cpu_addr = *cpu_addr_opt; |
| 52 | 60 | ||
| @@ -55,37 +63,41 @@ public: | |||
| 55 | constexpr std::size_t max_stream_size = 0x800; | 63 | constexpr std::size_t max_stream_size = 0x800; |
| 56 | if (use_fast_cbuf || size < max_stream_size) { | 64 | if (use_fast_cbuf || size < max_stream_size) { |
| 57 | if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) { | 65 | if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) { |
| 58 | auto& memory_manager = system.GPU().MemoryManager(); | 66 | const bool is_granular = memory_manager.IsGranularRange(gpu_addr, size); |
| 59 | if (use_fast_cbuf) { | 67 | if (use_fast_cbuf) { |
| 60 | if (memory_manager.IsGranularRange(gpu_addr, size)) { | 68 | u8* dest; |
| 61 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | 69 | if (is_granular) { |
| 62 | return ConstBufferUpload(host_ptr, size); | 70 | dest = memory_manager.GetPointer(gpu_addr); |
| 63 | } else { | 71 | } else { |
| 64 | staging_buffer.resize(size); | 72 | staging_buffer.resize(size); |
| 65 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); | 73 | dest = staging_buffer.data(); |
| 66 | return ConstBufferUpload(staging_buffer.data(), size); | 74 | memory_manager.ReadBlockUnsafe(gpu_addr, dest, size); |
| 67 | } | 75 | } |
| 76 | return ConstBufferUpload(dest, size); | ||
| 77 | } | ||
| 78 | if (is_granular) { | ||
| 79 | u8* const host_ptr = memory_manager.GetPointer(gpu_addr); | ||
| 80 | return StreamBufferUpload(size, alignment, [host_ptr, size](u8* dest) { | ||
| 81 | std::memcpy(dest, host_ptr, size); | ||
| 82 | }); | ||
| 68 | } else { | 83 | } else { |
| 69 | if (memory_manager.IsGranularRange(gpu_addr, size)) { | 84 | return StreamBufferUpload( |
| 70 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | 85 | size, alignment, [&memory_manager, gpu_addr, size](u8* dest) { |
| 71 | return StreamBufferUpload(host_ptr, size, alignment); | 86 | memory_manager.ReadBlockUnsafe(gpu_addr, dest, size); |
| 72 | } else { | 87 | }); |
| 73 | staging_buffer.resize(size); | ||
| 74 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); | ||
| 75 | return StreamBufferUpload(staging_buffer.data(), size, alignment); | ||
| 76 | } | ||
| 77 | } | 88 | } |
| 78 | } | 89 | } |
| 79 | } | 90 | } |
| 80 | 91 | ||
| 81 | OwnerBuffer block = GetBlock(cpu_addr, size); | 92 | Buffer* const block = GetBlock(cpu_addr, size); |
| 82 | MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); | 93 | MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); |
| 83 | if (!map) { | 94 | if (!map) { |
| 84 | return {GetEmptyBuffer(size), 0}; | 95 | return GetEmptyBuffer(size); |
| 85 | } | 96 | } |
| 86 | if (is_written) { | 97 | if (is_written) { |
| 87 | map->MarkAsModified(true, GetModifiedTicks()); | 98 | map->MarkAsModified(true, GetModifiedTicks()); |
| 88 | if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { | 99 | if (Settings::IsGPULevelHigh() && |
| 100 | Settings::values.use_asynchronous_gpu_emulation.GetValue()) { | ||
| 89 | MarkForAsyncFlush(map); | 101 | MarkForAsyncFlush(map); |
| 90 | } | 102 | } |
| 91 | if (!map->is_written) { | 103 | if (!map->is_written) { |
| @@ -94,41 +106,49 @@ public: | |||
| 94 | } | 106 | } |
| 95 | } | 107 | } |
| 96 | 108 | ||
| 97 | return {ToHandle(block), static_cast<u64>(block->GetOffset(cpu_addr))}; | 109 | return BufferInfo{block->Handle(), block->Offset(cpu_addr), block->Address()}; |
| 98 | } | 110 | } |
| 99 | 111 | ||
| 100 | /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. | 112 | /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. |
| 101 | BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size, | 113 | BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size, |
| 102 | std::size_t alignment = 4) { | 114 | std::size_t alignment = 4) { |
| 103 | std::lock_guard lock{mutex}; | 115 | std::lock_guard lock{mutex}; |
| 104 | return StreamBufferUpload(raw_pointer, size, alignment); | 116 | return StreamBufferUpload(size, alignment, [raw_pointer, size](u8* dest) { |
| 117 | std::memcpy(dest, raw_pointer, size); | ||
| 118 | }); | ||
| 105 | } | 119 | } |
| 106 | 120 | ||
| 107 | void Map(std::size_t max_size) { | 121 | /// Prepares the buffer cache for data uploading |
| 122 | /// @param max_size Maximum number of bytes that will be uploaded | ||
| 123 | /// @return True when a stream buffer invalidation was required, false otherwise | ||
| 124 | bool Map(std::size_t max_size) { | ||
| 108 | std::lock_guard lock{mutex}; | 125 | std::lock_guard lock{mutex}; |
| 109 | 126 | ||
| 127 | bool invalidated; | ||
| 110 | std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4); | 128 | std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4); |
| 111 | buffer_offset = buffer_offset_base; | 129 | buffer_offset = buffer_offset_base; |
| 130 | |||
| 131 | return invalidated; | ||
| 112 | } | 132 | } |
| 113 | 133 | ||
| 114 | /// Finishes the upload stream, returns true on bindings invalidation. | 134 | /// Finishes the upload stream |
| 115 | bool Unmap() { | 135 | void Unmap() { |
| 116 | std::lock_guard lock{mutex}; | 136 | std::lock_guard lock{mutex}; |
| 117 | |||
| 118 | stream_buffer->Unmap(buffer_offset - buffer_offset_base); | 137 | stream_buffer->Unmap(buffer_offset - buffer_offset_base); |
| 119 | return std::exchange(invalidated, false); | ||
| 120 | } | 138 | } |
| 121 | 139 | ||
| 140 | /// Function called at the end of each frame, inteded for deferred operations | ||
| 122 | void TickFrame() { | 141 | void TickFrame() { |
| 123 | ++epoch; | 142 | ++epoch; |
| 143 | |||
| 124 | while (!pending_destruction.empty()) { | 144 | while (!pending_destruction.empty()) { |
| 125 | // Delay at least 4 frames before destruction. | 145 | // Delay at least 4 frames before destruction. |
| 126 | // This is due to triple buffering happening on some drivers. | 146 | // This is due to triple buffering happening on some drivers. |
| 127 | static constexpr u64 epochs_to_destroy = 5; | 147 | static constexpr u64 epochs_to_destroy = 5; |
| 128 | if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) { | 148 | if (pending_destruction.front()->Epoch() + epochs_to_destroy > epoch) { |
| 129 | break; | 149 | break; |
| 130 | } | 150 | } |
| 131 | pending_destruction.pop_front(); | 151 | pending_destruction.pop(); |
| 132 | } | 152 | } |
| 133 | } | 153 | } |
| 134 | 154 | ||
| @@ -239,28 +259,16 @@ public: | |||
| 239 | committed_flushes.pop_front(); | 259 | committed_flushes.pop_front(); |
| 240 | } | 260 | } |
| 241 | 261 | ||
| 242 | virtual BufferType GetEmptyBuffer(std::size_t size) = 0; | 262 | virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0; |
| 243 | 263 | ||
| 244 | protected: | 264 | protected: |
| 245 | explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | 265 | explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, |
| 246 | std::unique_ptr<StreamBuffer> stream_buffer) | 266 | std::unique_ptr<StreamBuffer> stream_buffer) |
| 247 | : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)}, | 267 | : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)} {} |
| 248 | stream_buffer_handle{this->stream_buffer->GetHandle()} {} | ||
| 249 | 268 | ||
| 250 | ~BufferCache() = default; | 269 | ~BufferCache() = default; |
| 251 | 270 | ||
| 252 | virtual BufferType ToHandle(const OwnerBuffer& storage) = 0; | 271 | virtual std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) = 0; |
| 253 | |||
| 254 | virtual OwnerBuffer CreateBlock(VAddr cpu_addr, std::size_t size) = 0; | ||
| 255 | |||
| 256 | virtual void UploadBlockData(const OwnerBuffer& buffer, std::size_t offset, std::size_t size, | ||
| 257 | const u8* data) = 0; | ||
| 258 | |||
| 259 | virtual void DownloadBlockData(const OwnerBuffer& buffer, std::size_t offset, std::size_t size, | ||
| 260 | u8* data) = 0; | ||
| 261 | |||
| 262 | virtual void CopyBlock(const OwnerBuffer& src, const OwnerBuffer& dst, std::size_t src_offset, | ||
| 263 | std::size_t dst_offset, std::size_t size) = 0; | ||
| 264 | 272 | ||
| 265 | virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) { | 273 | virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) { |
| 266 | return {}; | 274 | return {}; |
| @@ -315,19 +323,18 @@ protected: | |||
| 315 | } | 323 | } |
| 316 | 324 | ||
| 317 | private: | 325 | private: |
| 318 | MapInterval* MapAddress(const OwnerBuffer& block, GPUVAddr gpu_addr, VAddr cpu_addr, | 326 | MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) { |
| 319 | std::size_t size) { | ||
| 320 | const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); | 327 | const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); |
| 321 | if (overlaps.empty()) { | 328 | if (overlaps.empty()) { |
| 322 | auto& memory_manager = system.GPU().MemoryManager(); | 329 | auto& memory_manager = system.GPU().MemoryManager(); |
| 323 | const VAddr cpu_addr_end = cpu_addr + size; | 330 | const VAddr cpu_addr_end = cpu_addr + size; |
| 324 | if (memory_manager.IsGranularRange(gpu_addr, size)) { | 331 | if (memory_manager.IsGranularRange(gpu_addr, size)) { |
| 325 | u8* host_ptr = memory_manager.GetPointer(gpu_addr); | 332 | u8* host_ptr = memory_manager.GetPointer(gpu_addr); |
| 326 | UploadBlockData(block, block->GetOffset(cpu_addr), size, host_ptr); | 333 | block->Upload(block->Offset(cpu_addr), size, host_ptr); |
| 327 | } else { | 334 | } else { |
| 328 | staging_buffer.resize(size); | 335 | staging_buffer.resize(size); |
| 329 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); | 336 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); |
| 330 | UploadBlockData(block, block->GetOffset(cpu_addr), size, staging_buffer.data()); | 337 | block->Upload(block->Offset(cpu_addr), size, staging_buffer.data()); |
| 331 | } | 338 | } |
| 332 | return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); | 339 | return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); |
| 333 | } | 340 | } |
| @@ -363,15 +370,15 @@ private: | |||
| 363 | } | 370 | } |
| 364 | if (modified_inheritance) { | 371 | if (modified_inheritance) { |
| 365 | map->MarkAsModified(true, GetModifiedTicks()); | 372 | map->MarkAsModified(true, GetModifiedTicks()); |
| 366 | if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { | 373 | if (Settings::IsGPULevelHigh() && |
| 374 | Settings::values.use_asynchronous_gpu_emulation.GetValue()) { | ||
| 367 | MarkForAsyncFlush(map); | 375 | MarkForAsyncFlush(map); |
| 368 | } | 376 | } |
| 369 | } | 377 | } |
| 370 | return map; | 378 | return map; |
| 371 | } | 379 | } |
| 372 | 380 | ||
| 373 | void UpdateBlock(const OwnerBuffer& block, VAddr start, VAddr end, | 381 | void UpdateBlock(Buffer* block, VAddr start, VAddr end, const VectorMapInterval& overlaps) { |
| 374 | const VectorMapInterval& overlaps) { | ||
| 375 | const IntervalType base_interval{start, end}; | 382 | const IntervalType base_interval{start, end}; |
| 376 | IntervalSet interval_set{}; | 383 | IntervalSet interval_set{}; |
| 377 | interval_set.add(base_interval); | 384 | interval_set.add(base_interval); |
| @@ -380,13 +387,13 @@ private: | |||
| 380 | interval_set.subtract(subtract); | 387 | interval_set.subtract(subtract); |
| 381 | } | 388 | } |
| 382 | for (auto& interval : interval_set) { | 389 | for (auto& interval : interval_set) { |
| 383 | std::size_t size = interval.upper() - interval.lower(); | 390 | const std::size_t size = interval.upper() - interval.lower(); |
| 384 | if (size > 0) { | 391 | if (size == 0) { |
| 385 | staging_buffer.resize(size); | 392 | continue; |
| 386 | system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size); | ||
| 387 | UploadBlockData(block, block->GetOffset(interval.lower()), size, | ||
| 388 | staging_buffer.data()); | ||
| 389 | } | 393 | } |
| 394 | staging_buffer.resize(size); | ||
| 395 | system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size); | ||
| 396 | block->Upload(block->Offset(interval.lower()), size, staging_buffer.data()); | ||
| 390 | } | 397 | } |
| 391 | } | 398 | } |
| 392 | 399 | ||
| @@ -416,23 +423,27 @@ private: | |||
| 416 | } | 423 | } |
| 417 | 424 | ||
| 418 | void FlushMap(MapInterval* map) { | 425 | void FlushMap(MapInterval* map) { |
| 426 | const auto it = blocks.find(map->start >> BLOCK_PAGE_BITS); | ||
| 427 | ASSERT_OR_EXECUTE(it != blocks.end(), return;); | ||
| 428 | |||
| 429 | std::shared_ptr<Buffer> block = it->second; | ||
| 430 | |||
| 419 | const std::size_t size = map->end - map->start; | 431 | const std::size_t size = map->end - map->start; |
| 420 | OwnerBuffer block = blocks[map->start >> block_page_bits]; | ||
| 421 | staging_buffer.resize(size); | 432 | staging_buffer.resize(size); |
| 422 | DownloadBlockData(block, block->GetOffset(map->start), size, staging_buffer.data()); | 433 | block->Download(block->Offset(map->start), size, staging_buffer.data()); |
| 423 | system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); | 434 | system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); |
| 424 | map->MarkAsModified(false, 0); | 435 | map->MarkAsModified(false, 0); |
| 425 | } | 436 | } |
| 426 | 437 | ||
| 427 | BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size, | 438 | template <typename Callable> |
| 428 | std::size_t alignment) { | 439 | BufferInfo StreamBufferUpload(std::size_t size, std::size_t alignment, Callable&& callable) { |
| 429 | AlignBuffer(alignment); | 440 | AlignBuffer(alignment); |
| 430 | const std::size_t uploaded_offset = buffer_offset; | 441 | const std::size_t uploaded_offset = buffer_offset; |
| 431 | std::memcpy(buffer_ptr, raw_pointer, size); | 442 | callable(buffer_ptr); |
| 432 | 443 | ||
| 433 | buffer_ptr += size; | 444 | buffer_ptr += size; |
| 434 | buffer_offset += size; | 445 | buffer_offset += size; |
| 435 | return {stream_buffer_handle, uploaded_offset}; | 446 | return BufferInfo{stream_buffer->Handle(), uploaded_offset, stream_buffer->Address()}; |
| 436 | } | 447 | } |
| 437 | 448 | ||
| 438 | void AlignBuffer(std::size_t alignment) { | 449 | void AlignBuffer(std::size_t alignment) { |
| @@ -442,97 +453,89 @@ private: | |||
| 442 | buffer_offset = offset_aligned; | 453 | buffer_offset = offset_aligned; |
| 443 | } | 454 | } |
| 444 | 455 | ||
| 445 | OwnerBuffer EnlargeBlock(OwnerBuffer buffer) { | 456 | std::shared_ptr<Buffer> EnlargeBlock(std::shared_ptr<Buffer> buffer) { |
| 446 | const std::size_t old_size = buffer->GetSize(); | 457 | const std::size_t old_size = buffer->Size(); |
| 447 | const std::size_t new_size = old_size + block_page_size; | 458 | const std::size_t new_size = old_size + BLOCK_PAGE_SIZE; |
| 448 | const VAddr cpu_addr = buffer->GetCpuAddr(); | 459 | const VAddr cpu_addr = buffer->CpuAddr(); |
| 449 | OwnerBuffer new_buffer = CreateBlock(cpu_addr, new_size); | 460 | std::shared_ptr<Buffer> new_buffer = CreateBlock(cpu_addr, new_size); |
| 450 | CopyBlock(buffer, new_buffer, 0, 0, old_size); | 461 | new_buffer->CopyFrom(*buffer, 0, 0, old_size); |
| 451 | buffer->SetEpoch(epoch); | 462 | QueueDestruction(std::move(buffer)); |
| 452 | pending_destruction.push_back(buffer); | 463 | |
| 453 | const VAddr cpu_addr_end = cpu_addr + new_size - 1; | 464 | const VAddr cpu_addr_end = cpu_addr + new_size - 1; |
| 454 | u64 page_start = cpu_addr >> block_page_bits; | 465 | const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS; |
| 455 | const u64 page_end = cpu_addr_end >> block_page_bits; | 466 | for (u64 page_start = cpu_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) { |
| 456 | while (page_start <= page_end) { | 467 | blocks.insert_or_assign(page_start, new_buffer); |
| 457 | blocks[page_start] = new_buffer; | ||
| 458 | ++page_start; | ||
| 459 | } | 468 | } |
| 469 | |||
| 460 | return new_buffer; | 470 | return new_buffer; |
| 461 | } | 471 | } |
| 462 | 472 | ||
| 463 | OwnerBuffer MergeBlocks(OwnerBuffer first, OwnerBuffer second) { | 473 | std::shared_ptr<Buffer> MergeBlocks(std::shared_ptr<Buffer> first, |
| 464 | const std::size_t size_1 = first->GetSize(); | 474 | std::shared_ptr<Buffer> second) { |
| 465 | const std::size_t size_2 = second->GetSize(); | 475 | const std::size_t size_1 = first->Size(); |
| 466 | const VAddr first_addr = first->GetCpuAddr(); | 476 | const std::size_t size_2 = second->Size(); |
| 467 | const VAddr second_addr = second->GetCpuAddr(); | 477 | const VAddr first_addr = first->CpuAddr(); |
| 478 | const VAddr second_addr = second->CpuAddr(); | ||
| 468 | const VAddr new_addr = std::min(first_addr, second_addr); | 479 | const VAddr new_addr = std::min(first_addr, second_addr); |
| 469 | const std::size_t new_size = size_1 + size_2; | 480 | const std::size_t new_size = size_1 + size_2; |
| 470 | OwnerBuffer new_buffer = CreateBlock(new_addr, new_size); | 481 | |
| 471 | CopyBlock(first, new_buffer, 0, new_buffer->GetOffset(first_addr), size_1); | 482 | std::shared_ptr<Buffer> new_buffer = CreateBlock(new_addr, new_size); |
| 472 | CopyBlock(second, new_buffer, 0, new_buffer->GetOffset(second_addr), size_2); | 483 | new_buffer->CopyFrom(*first, 0, new_buffer->Offset(first_addr), size_1); |
| 473 | first->SetEpoch(epoch); | 484 | new_buffer->CopyFrom(*second, 0, new_buffer->Offset(second_addr), size_2); |
| 474 | second->SetEpoch(epoch); | 485 | QueueDestruction(std::move(first)); |
| 475 | pending_destruction.push_back(first); | 486 | QueueDestruction(std::move(second)); |
| 476 | pending_destruction.push_back(second); | 487 | |
| 477 | const VAddr cpu_addr_end = new_addr + new_size - 1; | 488 | const VAddr cpu_addr_end = new_addr + new_size - 1; |
| 478 | u64 page_start = new_addr >> block_page_bits; | 489 | const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS; |
| 479 | const u64 page_end = cpu_addr_end >> block_page_bits; | 490 | for (u64 page_start = new_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) { |
| 480 | while (page_start <= page_end) { | 491 | blocks.insert_or_assign(page_start, new_buffer); |
| 481 | blocks[page_start] = new_buffer; | ||
| 482 | ++page_start; | ||
| 483 | } | 492 | } |
| 484 | return new_buffer; | 493 | return new_buffer; |
| 485 | } | 494 | } |
| 486 | 495 | ||
| 487 | OwnerBuffer GetBlock(const VAddr cpu_addr, const std::size_t size) { | 496 | Buffer* GetBlock(VAddr cpu_addr, std::size_t size) { |
| 488 | OwnerBuffer found; | 497 | std::shared_ptr<Buffer> found; |
| 498 | |||
| 489 | const VAddr cpu_addr_end = cpu_addr + size - 1; | 499 | const VAddr cpu_addr_end = cpu_addr + size - 1; |
| 490 | u64 page_start = cpu_addr >> block_page_bits; | 500 | const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS; |
| 491 | const u64 page_end = cpu_addr_end >> block_page_bits; | 501 | for (u64 page_start = cpu_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) { |
| 492 | while (page_start <= page_end) { | ||
| 493 | auto it = blocks.find(page_start); | 502 | auto it = blocks.find(page_start); |
| 494 | if (it == blocks.end()) { | 503 | if (it == blocks.end()) { |
| 495 | if (found) { | 504 | if (found) { |
| 496 | found = EnlargeBlock(found); | 505 | found = EnlargeBlock(found); |
| 497 | } else { | 506 | continue; |
| 498 | const VAddr start_addr = (page_start << block_page_bits); | ||
| 499 | found = CreateBlock(start_addr, block_page_size); | ||
| 500 | blocks[page_start] = found; | ||
| 501 | } | ||
| 502 | } else { | ||
| 503 | if (found) { | ||
| 504 | if (found == it->second) { | ||
| 505 | ++page_start; | ||
| 506 | continue; | ||
| 507 | } | ||
| 508 | found = MergeBlocks(found, it->second); | ||
| 509 | } else { | ||
| 510 | found = it->second; | ||
| 511 | } | 507 | } |
| 508 | const VAddr start_addr = page_start << BLOCK_PAGE_BITS; | ||
| 509 | found = CreateBlock(start_addr, BLOCK_PAGE_SIZE); | ||
| 510 | blocks.insert_or_assign(page_start, found); | ||
| 511 | continue; | ||
| 512 | } | ||
| 513 | if (!found) { | ||
| 514 | found = it->second; | ||
| 515 | continue; | ||
| 516 | } | ||
| 517 | if (found != it->second) { | ||
| 518 | found = MergeBlocks(std::move(found), it->second); | ||
| 512 | } | 519 | } |
| 513 | ++page_start; | ||
| 514 | } | 520 | } |
| 515 | return found; | 521 | return found.get(); |
| 516 | } | 522 | } |
| 517 | 523 | ||
| 518 | void MarkRegionAsWritten(const VAddr start, const VAddr end) { | 524 | void MarkRegionAsWritten(VAddr start, VAddr end) { |
| 519 | u64 page_start = start >> write_page_bit; | 525 | const u64 page_end = end >> WRITE_PAGE_BIT; |
| 520 | const u64 page_end = end >> write_page_bit; | 526 | for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) { |
| 521 | while (page_start <= page_end) { | ||
| 522 | auto it = written_pages.find(page_start); | 527 | auto it = written_pages.find(page_start); |
| 523 | if (it != written_pages.end()) { | 528 | if (it != written_pages.end()) { |
| 524 | it->second = it->second + 1; | 529 | it->second = it->second + 1; |
| 525 | } else { | 530 | } else { |
| 526 | written_pages[page_start] = 1; | 531 | written_pages.insert_or_assign(page_start, 1); |
| 527 | } | 532 | } |
| 528 | ++page_start; | ||
| 529 | } | 533 | } |
| 530 | } | 534 | } |
| 531 | 535 | ||
| 532 | void UnmarkRegionAsWritten(const VAddr start, const VAddr end) { | 536 | void UnmarkRegionAsWritten(VAddr start, VAddr end) { |
| 533 | u64 page_start = start >> write_page_bit; | 537 | const u64 page_end = end >> WRITE_PAGE_BIT; |
| 534 | const u64 page_end = end >> write_page_bit; | 538 | for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) { |
| 535 | while (page_start <= page_end) { | ||
| 536 | auto it = written_pages.find(page_start); | 539 | auto it = written_pages.find(page_start); |
| 537 | if (it != written_pages.end()) { | 540 | if (it != written_pages.end()) { |
| 538 | if (it->second > 1) { | 541 | if (it->second > 1) { |
| @@ -541,22 +544,24 @@ private: | |||
| 541 | written_pages.erase(it); | 544 | written_pages.erase(it); |
| 542 | } | 545 | } |
| 543 | } | 546 | } |
| 544 | ++page_start; | ||
| 545 | } | 547 | } |
| 546 | } | 548 | } |
| 547 | 549 | ||
| 548 | bool IsRegionWritten(const VAddr start, const VAddr end) const { | 550 | bool IsRegionWritten(VAddr start, VAddr end) const { |
| 549 | u64 page_start = start >> write_page_bit; | 551 | const u64 page_end = end >> WRITE_PAGE_BIT; |
| 550 | const u64 page_end = end >> write_page_bit; | 552 | for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) { |
| 551 | while (page_start <= page_end) { | ||
| 552 | if (written_pages.count(page_start) > 0) { | 553 | if (written_pages.count(page_start) > 0) { |
| 553 | return true; | 554 | return true; |
| 554 | } | 555 | } |
| 555 | ++page_start; | ||
| 556 | } | 556 | } |
| 557 | return false; | 557 | return false; |
| 558 | } | 558 | } |
| 559 | 559 | ||
| 560 | void QueueDestruction(std::shared_ptr<Buffer> buffer) { | ||
| 561 | buffer->SetEpoch(epoch); | ||
| 562 | pending_destruction.push(std::move(buffer)); | ||
| 563 | } | ||
| 564 | |||
| 560 | void MarkForAsyncFlush(MapInterval* map) { | 565 | void MarkForAsyncFlush(MapInterval* map) { |
| 561 | if (!uncommitted_flushes) { | 566 | if (!uncommitted_flushes) { |
| 562 | uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval*>>(); | 567 | uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval*>>(); |
| @@ -568,9 +573,7 @@ private: | |||
| 568 | Core::System& system; | 573 | Core::System& system; |
| 569 | 574 | ||
| 570 | std::unique_ptr<StreamBuffer> stream_buffer; | 575 | std::unique_ptr<StreamBuffer> stream_buffer; |
| 571 | BufferType stream_buffer_handle{}; | 576 | BufferType stream_buffer_handle; |
| 572 | |||
| 573 | bool invalidated = false; | ||
| 574 | 577 | ||
| 575 | u8* buffer_ptr = nullptr; | 578 | u8* buffer_ptr = nullptr; |
| 576 | u64 buffer_offset = 0; | 579 | u64 buffer_offset = 0; |
| @@ -580,18 +583,15 @@ private: | |||
| 580 | boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>> | 583 | boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>> |
| 581 | mapped_addresses; | 584 | mapped_addresses; |
| 582 | 585 | ||
| 583 | static constexpr u64 write_page_bit = 11; | ||
| 584 | std::unordered_map<u64, u32> written_pages; | 586 | std::unordered_map<u64, u32> written_pages; |
| 587 | std::unordered_map<u64, std::shared_ptr<Buffer>> blocks; | ||
| 585 | 588 | ||
| 586 | static constexpr u64 block_page_bits = 21; | 589 | std::queue<std::shared_ptr<Buffer>> pending_destruction; |
| 587 | static constexpr u64 block_page_size = 1ULL << block_page_bits; | ||
| 588 | std::unordered_map<u64, OwnerBuffer> blocks; | ||
| 589 | |||
| 590 | std::list<OwnerBuffer> pending_destruction; | ||
| 591 | u64 epoch = 0; | 590 | u64 epoch = 0; |
| 592 | u64 modified_ticks = 0; | 591 | u64 modified_ticks = 0; |
| 593 | 592 | ||
| 594 | std::vector<u8> staging_buffer; | 593 | std::vector<u8> staging_buffer; |
| 594 | |||
| 595 | std::list<MapInterval*> marked_for_unregister; | 595 | std::list<MapInterval*> marked_for_unregister; |
| 596 | 596 | ||
| 597 | std::shared_ptr<std::unordered_set<MapInterval*>> uncommitted_flushes; | 597 | std::shared_ptr<std::unordered_set<MapInterval*>> uncommitted_flushes; |
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp new file mode 100644 index 000000000..6c426b035 --- /dev/null +++ b/src/video_core/compatible_formats.cpp | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <bitset> | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 9 | #include "video_core/compatible_formats.h" | ||
| 10 | #include "video_core/surface.h" | ||
| 11 | |||
| 12 | namespace VideoCore::Surface { | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | // Compatibility table taken from Table 3.X.2 in: | ||
| 17 | // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_view.txt | ||
| 18 | |||
| 19 | constexpr std::array VIEW_CLASS_128_BITS = { | ||
| 20 | PixelFormat::RGBA32F, | ||
| 21 | PixelFormat::RGBA32UI, | ||
| 22 | }; | ||
| 23 | // Missing formats: | ||
| 24 | // PixelFormat::RGBA32I | ||
| 25 | |||
| 26 | constexpr std::array VIEW_CLASS_96_BITS = { | ||
| 27 | PixelFormat::RGB32F, | ||
| 28 | }; | ||
| 29 | // Missing formats: | ||
| 30 | // PixelFormat::RGB32UI, | ||
| 31 | // PixelFormat::RGB32I, | ||
| 32 | |||
| 33 | constexpr std::array VIEW_CLASS_64_BITS = { | ||
| 34 | PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI, | ||
| 35 | PixelFormat::RGBA16U, PixelFormat::RGBA16F, PixelFormat::RGBA16S, | ||
| 36 | }; | ||
| 37 | // Missing formats: | ||
| 38 | // PixelFormat::RGBA16I | ||
| 39 | // PixelFormat::RG32I | ||
| 40 | |||
| 41 | // TODO: How should we handle 48 bits? | ||
| 42 | |||
| 43 | constexpr std::array VIEW_CLASS_32_BITS = { | ||
| 44 | PixelFormat::RG16F, PixelFormat::R11FG11FB10F, PixelFormat::R32F, | ||
| 45 | PixelFormat::A2B10G10R10U, PixelFormat::RG16UI, PixelFormat::R32UI, | ||
| 46 | PixelFormat::RG16I, PixelFormat::R32I, PixelFormat::ABGR8U, | ||
| 47 | PixelFormat::RG16, PixelFormat::ABGR8S, PixelFormat::RG16S, | ||
| 48 | PixelFormat::RGBA8_SRGB, PixelFormat::E5B9G9R9F, PixelFormat::BGRA8, | ||
| 49 | PixelFormat::BGRA8_SRGB, | ||
| 50 | }; | ||
| 51 | // Missing formats: | ||
| 52 | // PixelFormat::RGBA8UI | ||
| 53 | // PixelFormat::RGBA8I | ||
| 54 | // PixelFormat::RGB10_A2_UI | ||
| 55 | |||
| 56 | // TODO: How should we handle 24 bits? | ||
| 57 | |||
| 58 | constexpr std::array VIEW_CLASS_16_BITS = { | ||
| 59 | PixelFormat::R16F, PixelFormat::RG8UI, PixelFormat::R16UI, PixelFormat::R16I, | ||
| 60 | PixelFormat::RG8U, PixelFormat::R16U, PixelFormat::RG8S, PixelFormat::R16S, | ||
| 61 | }; | ||
| 62 | // Missing formats: | ||
| 63 | // PixelFormat::RG8I | ||
| 64 | |||
| 65 | constexpr std::array VIEW_CLASS_8_BITS = { | ||
| 66 | PixelFormat::R8UI, | ||
| 67 | PixelFormat::R8U, | ||
| 68 | }; | ||
| 69 | // Missing formats: | ||
| 70 | // PixelFormat::R8I | ||
| 71 | // PixelFormat::R8S | ||
| 72 | |||
| 73 | constexpr std::array VIEW_CLASS_RGTC1_RED = { | ||
| 74 | PixelFormat::DXN1, | ||
| 75 | }; | ||
| 76 | // Missing formats: | ||
| 77 | // COMPRESSED_SIGNED_RED_RGTC1 | ||
| 78 | |||
| 79 | constexpr std::array VIEW_CLASS_RGTC2_RG = { | ||
| 80 | PixelFormat::DXN2UNORM, | ||
| 81 | PixelFormat::DXN2SNORM, | ||
| 82 | }; | ||
| 83 | |||
| 84 | constexpr std::array VIEW_CLASS_BPTC_UNORM = { | ||
| 85 | PixelFormat::BC7U, | ||
| 86 | PixelFormat::BC7U_SRGB, | ||
| 87 | }; | ||
| 88 | |||
| 89 | constexpr std::array VIEW_CLASS_BPTC_FLOAT = { | ||
| 90 | PixelFormat::BC6H_SF16, | ||
| 91 | PixelFormat::BC6H_UF16, | ||
| 92 | }; | ||
| 93 | |||
| 94 | // Compatibility table taken from Table 4.X.1 in: | ||
| 95 | // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_copy_image.txt | ||
| 96 | |||
| 97 | constexpr std::array COPY_CLASS_128_BITS = { | ||
| 98 | PixelFormat::RGBA32UI, PixelFormat::RGBA32F, PixelFormat::DXT23, | ||
| 99 | PixelFormat::DXT23_SRGB, PixelFormat::DXT45, PixelFormat::DXT45_SRGB, | ||
| 100 | PixelFormat::DXN2SNORM, PixelFormat::BC7U, PixelFormat::BC7U_SRGB, | ||
| 101 | PixelFormat::BC6H_SF16, PixelFormat::BC6H_UF16, | ||
| 102 | }; | ||
| 103 | // Missing formats: | ||
| 104 | // PixelFormat::RGBA32I | ||
| 105 | // COMPRESSED_RG_RGTC2 | ||
| 106 | |||
| 107 | constexpr std::array COPY_CLASS_64_BITS = { | ||
| 108 | PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI, | ||
| 109 | PixelFormat::RGBA16U, PixelFormat::RGBA16S, PixelFormat::DXT1_SRGB, PixelFormat::DXT1, | ||
| 110 | |||
| 111 | }; | ||
| 112 | // Missing formats: | ||
| 113 | // PixelFormat::RGBA16I | ||
| 114 | // PixelFormat::RG32I, | ||
| 115 | // COMPRESSED_RGB_S3TC_DXT1_EXT | ||
| 116 | // COMPRESSED_SRGB_S3TC_DXT1_EXT | ||
| 117 | // COMPRESSED_RGBA_S3TC_DXT1_EXT | ||
| 118 | // COMPRESSED_SIGNED_RED_RGTC1 | ||
| 119 | |||
| 120 | void Enable(FormatCompatibility::Table& compatiblity, size_t format_a, size_t format_b) { | ||
| 121 | compatiblity[format_a][format_b] = true; | ||
| 122 | compatiblity[format_b][format_a] = true; | ||
| 123 | } | ||
| 124 | |||
| 125 | void Enable(FormatCompatibility::Table& compatibility, PixelFormat format_a, PixelFormat format_b) { | ||
| 126 | Enable(compatibility, static_cast<size_t>(format_a), static_cast<size_t>(format_b)); | ||
| 127 | } | ||
| 128 | |||
| 129 | template <typename Range> | ||
| 130 | void EnableRange(FormatCompatibility::Table& compatibility, const Range& range) { | ||
| 131 | for (auto it_a = range.begin(); it_a != range.end(); ++it_a) { | ||
| 132 | for (auto it_b = it_a; it_b != range.end(); ++it_b) { | ||
| 133 | Enable(compatibility, *it_a, *it_b); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | } // Anonymous namespace | ||
| 139 | |||
| 140 | FormatCompatibility::FormatCompatibility() { | ||
| 141 | for (size_t i = 0; i < MaxPixelFormat; ++i) { | ||
| 142 | // Identity is allowed | ||
| 143 | Enable(view, i, i); | ||
| 144 | } | ||
| 145 | |||
| 146 | EnableRange(view, VIEW_CLASS_128_BITS); | ||
| 147 | EnableRange(view, VIEW_CLASS_96_BITS); | ||
| 148 | EnableRange(view, VIEW_CLASS_64_BITS); | ||
| 149 | EnableRange(view, VIEW_CLASS_32_BITS); | ||
| 150 | EnableRange(view, VIEW_CLASS_16_BITS); | ||
| 151 | EnableRange(view, VIEW_CLASS_8_BITS); | ||
| 152 | EnableRange(view, VIEW_CLASS_RGTC1_RED); | ||
| 153 | EnableRange(view, VIEW_CLASS_RGTC2_RG); | ||
| 154 | EnableRange(view, VIEW_CLASS_BPTC_UNORM); | ||
| 155 | EnableRange(view, VIEW_CLASS_BPTC_FLOAT); | ||
| 156 | |||
| 157 | copy = view; | ||
| 158 | EnableRange(copy, COPY_CLASS_128_BITS); | ||
| 159 | EnableRange(copy, COPY_CLASS_64_BITS); | ||
| 160 | } | ||
| 161 | |||
| 162 | } // namespace VideoCore::Surface | ||
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h new file mode 100644 index 000000000..d1082566d --- /dev/null +++ b/src/video_core/compatible_formats.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <bitset> | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 9 | #include "video_core/surface.h" | ||
| 10 | |||
| 11 | namespace VideoCore::Surface { | ||
| 12 | |||
| 13 | class FormatCompatibility { | ||
| 14 | public: | ||
| 15 | using Table = std::array<std::bitset<MaxPixelFormat>, MaxPixelFormat>; | ||
| 16 | |||
| 17 | explicit FormatCompatibility(); | ||
| 18 | |||
| 19 | bool TestView(PixelFormat format_a, PixelFormat format_b) const noexcept { | ||
| 20 | return view[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)]; | ||
| 21 | } | ||
| 22 | |||
| 23 | bool TestCopy(PixelFormat format_a, PixelFormat format_b) const noexcept { | ||
| 24 | return copy[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)]; | ||
| 25 | } | ||
| 26 | |||
| 27 | private: | ||
| 28 | Table view; | ||
| 29 | Table copy; | ||
| 30 | }; | ||
| 31 | |||
| 32 | } // namespace VideoCore::Surface | ||
diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h index ebe139504..f46e81bb7 100644 --- a/src/video_core/engines/const_buffer_engine_interface.h +++ b/src/video_core/engines/const_buffer_engine_interface.h | |||
| @@ -93,6 +93,7 @@ public: | |||
| 93 | virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0; | 93 | virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0; |
| 94 | virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, | 94 | virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, |
| 95 | u64 offset) const = 0; | 95 | u64 offset) const = 0; |
| 96 | virtual SamplerDescriptor AccessSampler(u32 handle) const = 0; | ||
| 96 | virtual u32 GetBoundBuffer() const = 0; | 97 | virtual u32 GetBoundBuffer() const = 0; |
| 97 | 98 | ||
| 98 | virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0; | 99 | virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0; |
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index f6237fc6a..a82b06a38 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -92,8 +92,11 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con | |||
| 92 | ASSERT(stage == ShaderType::Compute); | 92 | ASSERT(stage == ShaderType::Compute); |
| 93 | const auto& tex_info_buffer = launch_description.const_buffer_config[const_buffer]; | 93 | const auto& tex_info_buffer = launch_description.const_buffer_config[const_buffer]; |
| 94 | const GPUVAddr tex_info_address = tex_info_buffer.Address() + offset; | 94 | const GPUVAddr tex_info_address = tex_info_buffer.Address() + offset; |
| 95 | return AccessSampler(memory_manager.Read<u32>(tex_info_address)); | ||
| 96 | } | ||
| 95 | 97 | ||
| 96 | const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)}; | 98 | SamplerDescriptor KeplerCompute::AccessSampler(u32 handle) const { |
| 99 | const Texture::TextureHandle tex_handle{handle}; | ||
| 97 | const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle); | 100 | const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle); |
| 98 | SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic); | 101 | SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic); |
| 99 | result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); | 102 | result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 18ceedfaf..b7f668d88 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -219,6 +219,8 @@ public: | |||
| 219 | SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, | 219 | SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, |
| 220 | u64 offset) const override; | 220 | u64 offset) const override; |
| 221 | 221 | ||
| 222 | SamplerDescriptor AccessSampler(u32 handle) const override; | ||
| 223 | |||
| 222 | u32 GetBoundBuffer() const override { | 224 | u32 GetBoundBuffer() const override { |
| 223 | return regs.tex_cb_index; | 225 | return regs.tex_cb_index; |
| 224 | } | 226 | } |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 004f6b261..c01436295 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -25,9 +25,8 @@ constexpr u32 MacroRegistersStart = 0xE00; | |||
| 25 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 25 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 26 | MemoryManager& memory_manager) | 26 | MemoryManager& memory_manager) |
| 27 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, | 27 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, |
| 28 | macro_interpreter{*this}, upload_state{memory_manager, regs.upload} { | 28 | macro_engine{GetMacroEngine(*this)}, upload_state{memory_manager, regs.upload} { |
| 29 | dirty.flags.flip(); | 29 | dirty.flags.flip(); |
| 30 | |||
| 31 | InitializeRegisterDefaults(); | 30 | InitializeRegisterDefaults(); |
| 32 | } | 31 | } |
| 33 | 32 | ||
| @@ -106,7 +105,11 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 106 | regs.rasterize_enable = 1; | 105 | regs.rasterize_enable = 1; |
| 107 | regs.rt_separate_frag_data = 1; | 106 | regs.rt_separate_frag_data = 1; |
| 108 | regs.framebuffer_srgb = 1; | 107 | regs.framebuffer_srgb = 1; |
| 108 | regs.line_width_aliased = 1.0f; | ||
| 109 | regs.line_width_smooth = 1.0f; | ||
| 109 | regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise; | 110 | regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise; |
| 111 | regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill; | ||
| 112 | regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill; | ||
| 110 | 113 | ||
| 111 | shadow_state = regs; | 114 | shadow_state = regs; |
| 112 | 115 | ||
| @@ -116,7 +119,7 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 116 | mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; | 119 | mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; |
| 117 | } | 120 | } |
| 118 | 121 | ||
| 119 | void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) { | 122 | void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) { |
| 120 | // Reset the current macro. | 123 | // Reset the current macro. |
| 121 | executing_macro = 0; | 124 | executing_macro = 0; |
| 122 | 125 | ||
| @@ -125,7 +128,7 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3 | |||
| 125 | ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); | 128 | ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); |
| 126 | 129 | ||
| 127 | // Execute the current macro. | 130 | // Execute the current macro. |
| 128 | macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); | 131 | macro_engine->Execute(*this, macro_positions[entry], parameters); |
| 129 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { | 132 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { |
| 130 | FlushMMEInlineDraw(); | 133 | FlushMMEInlineDraw(); |
| 131 | } | 134 | } |
| @@ -161,7 +164,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | |||
| 161 | 164 | ||
| 162 | // Call the macro when there are no more parameters in the command buffer | 165 | // Call the macro when there are no more parameters in the command buffer |
| 163 | if (is_last_call) { | 166 | if (is_last_call) { |
| 164 | CallMacroMethod(executing_macro, macro_params.size(), macro_params.data()); | 167 | CallMacroMethod(executing_macro, macro_params); |
| 165 | macro_params.clear(); | 168 | macro_params.clear(); |
| 166 | } | 169 | } |
| 167 | return; | 170 | return; |
| @@ -197,7 +200,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | |||
| 197 | break; | 200 | break; |
| 198 | } | 201 | } |
| 199 | case MAXWELL3D_REG_INDEX(macros.data): { | 202 | case MAXWELL3D_REG_INDEX(macros.data): { |
| 200 | ProcessMacroUpload(arg); | 203 | macro_engine->AddCode(regs.macros.upload_address, arg); |
| 201 | break; | 204 | break; |
| 202 | } | 205 | } |
| 203 | case MAXWELL3D_REG_INDEX(macros.bind): { | 206 | case MAXWELL3D_REG_INDEX(macros.bind): { |
| @@ -306,7 +309,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | |||
| 306 | 309 | ||
| 307 | // Call the macro when there are no more parameters in the command buffer | 310 | // Call the macro when there are no more parameters in the command buffer |
| 308 | if (amount == methods_pending) { | 311 | if (amount == methods_pending) { |
| 309 | CallMacroMethod(executing_macro, macro_params.size(), macro_params.data()); | 312 | CallMacroMethod(executing_macro, macro_params); |
| 310 | macro_params.clear(); | 313 | macro_params.clear(); |
| 311 | } | 314 | } |
| 312 | return; | 315 | return; |
| @@ -420,9 +423,7 @@ void Maxwell3D::FlushMMEInlineDraw() { | |||
| 420 | } | 423 | } |
| 421 | 424 | ||
| 422 | void Maxwell3D::ProcessMacroUpload(u32 data) { | 425 | void Maxwell3D::ProcessMacroUpload(u32 data) { |
| 423 | ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), | 426 | macro_engine->AddCode(regs.macros.upload_address++, data); |
| 424 | "upload_address exceeded macro_memory size!"); | ||
| 425 | macro_memory[regs.macros.upload_address++] = data; | ||
| 426 | } | 427 | } |
| 427 | 428 | ||
| 428 | void Maxwell3D::ProcessMacroBind(u32 data) { | 429 | void Maxwell3D::ProcessMacroBind(u32 data) { |
| @@ -739,8 +740,11 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b | |||
| 739 | const auto& shader = state.shader_stages[static_cast<std::size_t>(stage)]; | 740 | const auto& shader = state.shader_stages[static_cast<std::size_t>(stage)]; |
| 740 | const auto& tex_info_buffer = shader.const_buffers[const_buffer]; | 741 | const auto& tex_info_buffer = shader.const_buffers[const_buffer]; |
| 741 | const GPUVAddr tex_info_address = tex_info_buffer.address + offset; | 742 | const GPUVAddr tex_info_address = tex_info_buffer.address + offset; |
| 743 | return AccessSampler(memory_manager.Read<u32>(tex_info_address)); | ||
| 744 | } | ||
| 742 | 745 | ||
| 743 | const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)}; | 746 | SamplerDescriptor Maxwell3D::AccessSampler(u32 handle) const { |
| 747 | const Texture::TextureHandle tex_handle{handle}; | ||
| 744 | const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle); | 748 | const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle); |
| 745 | SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic); | 749 | SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic); |
| 746 | result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); | 750 | result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 05dd6b39b..ef1618990 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | #include "video_core/engines/engine_upload.h" | 23 | #include "video_core/engines/engine_upload.h" |
| 24 | #include "video_core/engines/shader_type.h" | 24 | #include "video_core/engines/shader_type.h" |
| 25 | #include "video_core/gpu.h" | 25 | #include "video_core/gpu.h" |
| 26 | #include "video_core/macro_interpreter.h" | 26 | #include "video_core/macro/macro.h" |
| 27 | #include "video_core/textures/texture.h" | 27 | #include "video_core/textures/texture.h" |
| 28 | 28 | ||
| 29 | namespace Core { | 29 | namespace Core { |
| @@ -598,6 +598,7 @@ public: | |||
| 598 | BitField<4, 3, u32> block_height; | 598 | BitField<4, 3, u32> block_height; |
| 599 | BitField<8, 3, u32> block_depth; | 599 | BitField<8, 3, u32> block_depth; |
| 600 | BitField<12, 1, InvMemoryLayout> type; | 600 | BitField<12, 1, InvMemoryLayout> type; |
| 601 | BitField<16, 1, u32> is_3d; | ||
| 601 | } memory_layout; | 602 | } memory_layout; |
| 602 | union { | 603 | union { |
| 603 | BitField<0, 16, u32> layers; | 604 | BitField<0, 16, u32> layers; |
| @@ -1403,6 +1404,8 @@ public: | |||
| 1403 | SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, | 1404 | SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, |
| 1404 | u64 offset) const override; | 1405 | u64 offset) const override; |
| 1405 | 1406 | ||
| 1407 | SamplerDescriptor AccessSampler(u32 handle) const override; | ||
| 1408 | |||
| 1406 | u32 GetBoundBuffer() const override { | 1409 | u32 GetBoundBuffer() const override { |
| 1407 | return regs.tex_cb_index; | 1410 | return regs.tex_cb_index; |
| 1408 | } | 1411 | } |
| @@ -1411,17 +1414,16 @@ public: | |||
| 1411 | 1414 | ||
| 1412 | const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; | 1415 | const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; |
| 1413 | 1416 | ||
| 1414 | /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than | 1417 | bool ShouldExecute() const { |
| 1415 | /// we've seen used. | 1418 | return execute_on; |
| 1416 | using MacroMemory = std::array<u32, 0x40000>; | 1419 | } |
| 1417 | 1420 | ||
| 1418 | /// Gets a reference to macro memory. | 1421 | VideoCore::RasterizerInterface& GetRasterizer() { |
| 1419 | const MacroMemory& GetMacroMemory() const { | 1422 | return rasterizer; |
| 1420 | return macro_memory; | ||
| 1421 | } | 1423 | } |
| 1422 | 1424 | ||
| 1423 | bool ShouldExecute() const { | 1425 | const VideoCore::RasterizerInterface& GetRasterizer() const { |
| 1424 | return execute_on; | 1426 | return rasterizer; |
| 1425 | } | 1427 | } |
| 1426 | 1428 | ||
| 1427 | /// Notify a memory write has happened. | 1429 | /// Notify a memory write has happened. |
| @@ -1468,16 +1470,13 @@ private: | |||
| 1468 | 1470 | ||
| 1469 | std::array<bool, Regs::NUM_REGS> mme_inline{}; | 1471 | std::array<bool, Regs::NUM_REGS> mme_inline{}; |
| 1470 | 1472 | ||
| 1471 | /// Memory for macro code | ||
| 1472 | MacroMemory macro_memory; | ||
| 1473 | |||
| 1474 | /// Macro method that is currently being executed / being fed parameters. | 1473 | /// Macro method that is currently being executed / being fed parameters. |
| 1475 | u32 executing_macro = 0; | 1474 | u32 executing_macro = 0; |
| 1476 | /// Parameters that have been submitted to the macro call so far. | 1475 | /// Parameters that have been submitted to the macro call so far. |
| 1477 | std::vector<u32> macro_params; | 1476 | std::vector<u32> macro_params; |
| 1478 | 1477 | ||
| 1479 | /// Interpreter for the macro codes uploaded to the GPU. | 1478 | /// Interpreter for the macro codes uploaded to the GPU. |
| 1480 | MacroInterpreter macro_interpreter; | 1479 | std::unique_ptr<MacroEngine> macro_engine; |
| 1481 | 1480 | ||
| 1482 | static constexpr u32 null_cb_data = 0xFFFFFFFF; | 1481 | static constexpr u32 null_cb_data = 0xFFFFFFFF; |
| 1483 | struct { | 1482 | struct { |
| @@ -1506,7 +1505,7 @@ private: | |||
| 1506 | * @param num_parameters Number of arguments | 1505 | * @param num_parameters Number of arguments |
| 1507 | * @param parameters Arguments to the method call | 1506 | * @param parameters Arguments to the method call |
| 1508 | */ | 1507 | */ |
| 1509 | void CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters); | 1508 | void CallMacroMethod(u32 method, const std::vector<u32>& parameters); |
| 1510 | 1509 | ||
| 1511 | /// Handles writes to the macro uploading register. | 1510 | /// Handles writes to the macro uploading register. |
| 1512 | void ProcessMacroUpload(u32 data); | 1511 | void ProcessMacroUpload(u32 data); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e7cb87589..d374b73cf 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -661,6 +661,10 @@ union Instruction { | |||
| 661 | constexpr Instruction(u64 value) : value{value} {} | 661 | constexpr Instruction(u64 value) : value{value} {} |
| 662 | constexpr Instruction(const Instruction& instr) : value(instr.value) {} | 662 | constexpr Instruction(const Instruction& instr) : value(instr.value) {} |
| 663 | 663 | ||
| 664 | constexpr bool Bit(u64 offset) const { | ||
| 665 | return ((value >> offset) & 1) != 0; | ||
| 666 | } | ||
| 667 | |||
| 664 | BitField<0, 8, Register> gpr0; | 668 | BitField<0, 8, Register> gpr0; |
| 665 | BitField<8, 8, Register> gpr8; | 669 | BitField<8, 8, Register> gpr8; |
| 666 | union { | 670 | union { |
| @@ -1874,7 +1878,9 @@ public: | |||
| 1874 | HSETP2_C, | 1878 | HSETP2_C, |
| 1875 | HSETP2_R, | 1879 | HSETP2_R, |
| 1876 | HSETP2_IMM, | 1880 | HSETP2_IMM, |
| 1881 | HSET2_C, | ||
| 1877 | HSET2_R, | 1882 | HSET2_R, |
| 1883 | HSET2_IMM, | ||
| 1878 | POPC_C, | 1884 | POPC_C, |
| 1879 | POPC_R, | 1885 | POPC_R, |
| 1880 | POPC_IMM, | 1886 | POPC_IMM, |
| @@ -2194,7 +2200,9 @@ private: | |||
| 2194 | INST("0111111-1-------", Id::HSETP2_C, Type::HalfSetPredicate, "HSETP2_C"), | 2200 | INST("0111111-1-------", Id::HSETP2_C, Type::HalfSetPredicate, "HSETP2_C"), |
| 2195 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), | 2201 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), |
| 2196 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), | 2202 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), |
| 2203 | INST("0111110-1-------", Id::HSET2_C, Type::HalfSet, "HSET2_C"), | ||
| 2197 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), | 2204 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), |
| 2205 | INST("0111110-0-------", Id::HSET2_IMM, Type::HalfSet, "HSET2_IMM"), | ||
| 2198 | INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"), | 2206 | INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"), |
| 2199 | INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"), | 2207 | INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"), |
| 2200 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | 2208 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8eb017f65..758bfe148 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 5 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 8 | #include "common/microprofile.h" |
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| @@ -154,9 +156,8 @@ u64 GPU::GetTicks() const { | |||
| 154 | constexpr u64 gpu_ticks_num = 384; | 156 | constexpr u64 gpu_ticks_num = 384; |
| 155 | constexpr u64 gpu_ticks_den = 625; | 157 | constexpr u64 gpu_ticks_den = 625; |
| 156 | 158 | ||
| 157 | const u64 cpu_ticks = system.CoreTiming().GetTicks(); | 159 | u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); |
| 158 | u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); | 160 | if (Settings::values.use_fast_gpu_time.GetValue()) { |
| 159 | if (Settings::values.use_fast_gpu_time) { | ||
| 160 | nanoseconds /= 256; | 161 | nanoseconds /= 256; |
| 161 | } | 162 | } |
| 162 | const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; | 163 | const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index a1b4c305c..2c42483bd 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -284,6 +284,12 @@ public: | |||
| 284 | /// core timing events. | 284 | /// core timing events. |
| 285 | virtual void Start() = 0; | 285 | virtual void Start() = 0; |
| 286 | 286 | ||
| 287 | /// Obtain the CPU Context | ||
| 288 | virtual void ObtainContext() = 0; | ||
| 289 | |||
| 290 | /// Release the CPU Context | ||
| 291 | virtual void ReleaseContext() = 0; | ||
| 292 | |||
| 287 | /// Push GPU command entries to be processed | 293 | /// Push GPU command entries to be processed |
| 288 | virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; | 294 | virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; |
| 289 | 295 | ||
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 53305ab43..7b855f63e 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp | |||
| @@ -19,10 +19,17 @@ GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBa | |||
| 19 | GPUAsynch::~GPUAsynch() = default; | 19 | GPUAsynch::~GPUAsynch() = default; |
| 20 | 20 | ||
| 21 | void GPUAsynch::Start() { | 21 | void GPUAsynch::Start() { |
| 22 | cpu_context->MakeCurrent(); | ||
| 23 | gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); | 22 | gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); |
| 24 | } | 23 | } |
| 25 | 24 | ||
| 25 | void GPUAsynch::ObtainContext() { | ||
| 26 | cpu_context->MakeCurrent(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void GPUAsynch::ReleaseContext() { | ||
| 30 | cpu_context->DoneCurrent(); | ||
| 31 | } | ||
| 32 | |||
| 26 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { | 33 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { |
| 27 | gpu_thread.SubmitList(std::move(entries)); | 34 | gpu_thread.SubmitList(std::move(entries)); |
| 28 | } | 35 | } |
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 517658612..15e9f1d38 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h | |||
| @@ -25,6 +25,8 @@ public: | |||
| 25 | ~GPUAsynch() override; | 25 | ~GPUAsynch() override; |
| 26 | 26 | ||
| 27 | void Start() override; | 27 | void Start() override; |
| 28 | void ObtainContext() override; | ||
| 29 | void ReleaseContext() override; | ||
| 28 | void PushGPUEntries(Tegra::CommandList&& entries) override; | 30 | void PushGPUEntries(Tegra::CommandList&& entries) override; |
| 29 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 31 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 30 | void FlushRegion(VAddr addr, u64 size) override; | 32 | void FlushRegion(VAddr addr, u64 size) override; |
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index 6f38a672a..aaeb9811d 100644 --- a/src/video_core/gpu_synch.cpp +++ b/src/video_core/gpu_synch.cpp | |||
| @@ -13,10 +13,16 @@ GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase | |||
| 13 | 13 | ||
| 14 | GPUSynch::~GPUSynch() = default; | 14 | GPUSynch::~GPUSynch() = default; |
| 15 | 15 | ||
| 16 | void GPUSynch::Start() { | 16 | void GPUSynch::Start() {} |
| 17 | |||
| 18 | void GPUSynch::ObtainContext() { | ||
| 17 | context->MakeCurrent(); | 19 | context->MakeCurrent(); |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 22 | void GPUSynch::ReleaseContext() { | ||
| 23 | context->DoneCurrent(); | ||
| 24 | } | ||
| 25 | |||
| 20 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | 26 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { |
| 21 | dma_pusher->Push(std::move(entries)); | 27 | dma_pusher->Push(std::move(entries)); |
| 22 | dma_pusher->DispatchCalls(); | 28 | dma_pusher->DispatchCalls(); |
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index 4a6e9a01d..762c20aa5 100644 --- a/src/video_core/gpu_synch.h +++ b/src/video_core/gpu_synch.h | |||
| @@ -24,6 +24,8 @@ public: | |||
| 24 | ~GPUSynch() override; | 24 | ~GPUSynch() override; |
| 25 | 25 | ||
| 26 | void Start() override; | 26 | void Start() override; |
| 27 | void ObtainContext() override; | ||
| 28 | void ReleaseContext() override; | ||
| 27 | void PushGPUEntries(Tegra::CommandList&& entries) override; | 29 | void PushGPUEntries(Tegra::CommandList&& entries) override; |
| 28 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 30 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 29 | void FlushRegion(VAddr addr, u64 size) override; | 31 | void FlushRegion(VAddr addr, u64 size) override; |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index c3bb4fe06..738c6f0c1 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "common/thread.h" | ||
| 7 | #include "core/core.h" | 8 | #include "core/core.h" |
| 8 | #include "core/frontend/emu_window.h" | 9 | #include "core/frontend/emu_window.h" |
| 9 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| @@ -18,7 +19,11 @@ namespace VideoCommon::GPUThread { | |||
| 18 | static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, | 19 | static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, |
| 19 | Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, | 20 | Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, |
| 20 | SynchState& state) { | 21 | SynchState& state) { |
| 21 | MicroProfileOnThreadCreate("GpuThread"); | 22 | std::string name = "yuzu:GPU"; |
| 23 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 24 | Common::SetCurrentThreadName(name.c_str()); | ||
| 25 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 26 | system.RegisterHostThread(); | ||
| 22 | 27 | ||
| 23 | // Wait for first GPU command before acquiring the window context | 28 | // Wait for first GPU command before acquiring the window context |
| 24 | while (state.queue.Empty()) | 29 | while (state.queue.Empty()) |
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp new file mode 100644 index 000000000..a50e7b4e0 --- /dev/null +++ b/src/video_core/macro/macro.cpp | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <optional> | ||
| 6 | #include <boost/container_hash/hash.hpp> | ||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/settings.h" | ||
| 10 | #include "video_core/engines/maxwell_3d.h" | ||
| 11 | #include "video_core/macro/macro.h" | ||
| 12 | #include "video_core/macro/macro_hle.h" | ||
| 13 | #include "video_core/macro/macro_interpreter.h" | ||
| 14 | #include "video_core/macro/macro_jit_x64.h" | ||
| 15 | |||
| 16 | namespace Tegra { | ||
| 17 | |||
| 18 | MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) | ||
| 19 | : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {} | ||
| 20 | |||
| 21 | MacroEngine::~MacroEngine() = default; | ||
| 22 | |||
| 23 | void MacroEngine::AddCode(u32 method, u32 data) { | ||
| 24 | uploaded_macro_code[method].push_back(data); | ||
| 25 | } | ||
| 26 | |||
| 27 | void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, | ||
| 28 | const std::vector<u32>& parameters) { | ||
| 29 | auto compiled_macro = macro_cache.find(method); | ||
| 30 | if (compiled_macro != macro_cache.end()) { | ||
| 31 | const auto& cache_info = compiled_macro->second; | ||
| 32 | if (cache_info.has_hle_program) { | ||
| 33 | cache_info.hle_program->Execute(parameters, method); | ||
| 34 | } else { | ||
| 35 | cache_info.lle_program->Execute(parameters, method); | ||
| 36 | } | ||
| 37 | } else { | ||
| 38 | // Macro not compiled, check if it's uploaded and if so, compile it | ||
| 39 | std::optional<u32> mid_method = std::nullopt; | ||
| 40 | const auto macro_code = uploaded_macro_code.find(method); | ||
| 41 | if (macro_code == uploaded_macro_code.end()) { | ||
| 42 | for (const auto& [method_base, code] : uploaded_macro_code) { | ||
| 43 | if (method >= method_base && (method - method_base) < code.size()) { | ||
| 44 | mid_method = method_base; | ||
| 45 | break; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | if (!mid_method.has_value()) { | ||
| 49 | UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); | ||
| 50 | return; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | auto& cache_info = macro_cache[method]; | ||
| 54 | |||
| 55 | if (!mid_method.has_value()) { | ||
| 56 | cache_info.lle_program = Compile(macro_code->second); | ||
| 57 | cache_info.hash = boost::hash_value(macro_code->second); | ||
| 58 | } else { | ||
| 59 | const auto& macro_cached = uploaded_macro_code[mid_method.value()]; | ||
| 60 | const auto rebased_method = method - mid_method.value(); | ||
| 61 | auto& code = uploaded_macro_code[method]; | ||
| 62 | code.resize(macro_cached.size() - rebased_method); | ||
| 63 | std::memcpy(code.data(), macro_cached.data() + rebased_method, | ||
| 64 | code.size() * sizeof(u32)); | ||
| 65 | cache_info.hash = boost::hash_value(code); | ||
| 66 | cache_info.lle_program = Compile(code); | ||
| 67 | } | ||
| 68 | |||
| 69 | auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); | ||
| 70 | if (hle_program.has_value()) { | ||
| 71 | cache_info.has_hle_program = true; | ||
| 72 | cache_info.hle_program = std::move(hle_program.value()); | ||
| 73 | cache_info.hle_program->Execute(parameters, method); | ||
| 74 | } else { | ||
| 75 | cache_info.lle_program->Execute(parameters, method); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d) { | ||
| 81 | if (Settings::values.disable_macro_jit) { | ||
| 82 | return std::make_unique<MacroInterpreter>(maxwell3d); | ||
| 83 | } | ||
| 84 | #ifdef ARCHITECTURE_x86_64 | ||
| 85 | return std::make_unique<MacroJITx64>(maxwell3d); | ||
| 86 | #else | ||
| 87 | return std::make_unique<MacroInterpreter>(maxwell3d); | ||
| 88 | #endif | ||
| 89 | } | ||
| 90 | |||
| 91 | } // namespace Tegra | ||
diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h new file mode 100644 index 000000000..4d00b84b0 --- /dev/null +++ b/src/video_core/macro/macro.h | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <unordered_map> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Tegra { | ||
| 14 | |||
| 15 | namespace Engines { | ||
| 16 | class Maxwell3D; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Macro { | ||
| 20 | constexpr std::size_t NUM_MACRO_REGISTERS = 8; | ||
| 21 | enum class Operation : u32 { | ||
| 22 | ALU = 0, | ||
| 23 | AddImmediate = 1, | ||
| 24 | ExtractInsert = 2, | ||
| 25 | ExtractShiftLeftImmediate = 3, | ||
| 26 | ExtractShiftLeftRegister = 4, | ||
| 27 | Read = 5, | ||
| 28 | Unused = 6, // This operation doesn't seem to be a valid encoding. | ||
| 29 | Branch = 7, | ||
| 30 | }; | ||
| 31 | |||
| 32 | enum class ALUOperation : u32 { | ||
| 33 | Add = 0, | ||
| 34 | AddWithCarry = 1, | ||
| 35 | Subtract = 2, | ||
| 36 | SubtractWithBorrow = 3, | ||
| 37 | // Operations 4-7 don't seem to be valid encodings. | ||
| 38 | Xor = 8, | ||
| 39 | Or = 9, | ||
| 40 | And = 10, | ||
| 41 | AndNot = 11, | ||
| 42 | Nand = 12 | ||
| 43 | }; | ||
| 44 | |||
| 45 | enum class ResultOperation : u32 { | ||
| 46 | IgnoreAndFetch = 0, | ||
| 47 | Move = 1, | ||
| 48 | MoveAndSetMethod = 2, | ||
| 49 | FetchAndSend = 3, | ||
| 50 | MoveAndSend = 4, | ||
| 51 | FetchAndSetMethod = 5, | ||
| 52 | MoveAndSetMethodFetchAndSend = 6, | ||
| 53 | MoveAndSetMethodSend = 7 | ||
| 54 | }; | ||
| 55 | |||
| 56 | enum class BranchCondition : u32 { | ||
| 57 | Zero = 0, | ||
| 58 | NotZero = 1, | ||
| 59 | }; | ||
| 60 | |||
| 61 | union Opcode { | ||
| 62 | u32 raw; | ||
| 63 | BitField<0, 3, Operation> operation; | ||
| 64 | BitField<4, 3, ResultOperation> result_operation; | ||
| 65 | BitField<4, 1, BranchCondition> branch_condition; | ||
| 66 | // If set on a branch, then the branch doesn't have a delay slot. | ||
| 67 | BitField<5, 1, u32> branch_annul; | ||
| 68 | BitField<7, 1, u32> is_exit; | ||
| 69 | BitField<8, 3, u32> dst; | ||
| 70 | BitField<11, 3, u32> src_a; | ||
| 71 | BitField<14, 3, u32> src_b; | ||
| 72 | // The signed immediate overlaps the second source operand and the alu operation. | ||
| 73 | BitField<14, 18, s32> immediate; | ||
| 74 | |||
| 75 | BitField<17, 5, ALUOperation> alu_operation; | ||
| 76 | |||
| 77 | // Bitfield instructions data | ||
| 78 | BitField<17, 5, u32> bf_src_bit; | ||
| 79 | BitField<22, 5, u32> bf_size; | ||
| 80 | BitField<27, 5, u32> bf_dst_bit; | ||
| 81 | |||
| 82 | u32 GetBitfieldMask() const { | ||
| 83 | return (1 << bf_size) - 1; | ||
| 84 | } | ||
| 85 | |||
| 86 | s32 GetBranchTarget() const { | ||
| 87 | return static_cast<s32>(immediate * sizeof(u32)); | ||
| 88 | } | ||
| 89 | }; | ||
| 90 | |||
| 91 | union MethodAddress { | ||
| 92 | u32 raw; | ||
| 93 | BitField<0, 12, u32> address; | ||
| 94 | BitField<12, 6, u32> increment; | ||
| 95 | }; | ||
| 96 | |||
| 97 | } // namespace Macro | ||
| 98 | |||
| 99 | class HLEMacro; | ||
| 100 | |||
| 101 | class CachedMacro { | ||
| 102 | public: | ||
| 103 | virtual ~CachedMacro() = default; | ||
| 104 | /** | ||
| 105 | * Executes the macro code with the specified input parameters. | ||
| 106 | * @param code The macro byte code to execute | ||
| 107 | * @param parameters The parameters of the macro | ||
| 108 | */ | ||
| 109 | virtual void Execute(const std::vector<u32>& parameters, u32 method) = 0; | ||
| 110 | }; | ||
| 111 | |||
| 112 | class MacroEngine { | ||
| 113 | public: | ||
| 114 | explicit MacroEngine(Engines::Maxwell3D& maxwell3d); | ||
| 115 | virtual ~MacroEngine(); | ||
| 116 | |||
| 117 | // Store the uploaded macro code to compile them when they're called. | ||
| 118 | void AddCode(u32 method, u32 data); | ||
| 119 | |||
| 120 | // Compiles the macro if its not in the cache, and executes the compiled macro | ||
| 121 | void Execute(Engines::Maxwell3D& maxwell3d, u32 method, const std::vector<u32>& parameters); | ||
| 122 | |||
| 123 | protected: | ||
| 124 | virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0; | ||
| 125 | |||
| 126 | private: | ||
| 127 | struct CacheInfo { | ||
| 128 | std::unique_ptr<CachedMacro> lle_program{}; | ||
| 129 | std::unique_ptr<CachedMacro> hle_program{}; | ||
| 130 | u64 hash{}; | ||
| 131 | bool has_hle_program{}; | ||
| 132 | }; | ||
| 133 | |||
| 134 | std::unordered_map<u32, CacheInfo> macro_cache; | ||
| 135 | std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; | ||
| 136 | std::unique_ptr<HLEMacro> hle_macros; | ||
| 137 | }; | ||
| 138 | |||
| 139 | std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); | ||
| 140 | |||
| 141 | } // namespace Tegra | ||
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp new file mode 100644 index 000000000..410f99018 --- /dev/null +++ b/src/video_core/macro/macro_hle.cpp | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <vector> | ||
| 7 | #include "video_core/engines/maxwell_3d.h" | ||
| 8 | #include "video_core/macro/macro_hle.h" | ||
| 9 | #include "video_core/rasterizer_interface.h" | ||
| 10 | |||
| 11 | namespace Tegra { | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | // HLE'd functions | ||
| 15 | static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, | ||
| 16 | const std::vector<u32>& parameters) { | ||
| 17 | const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B); | ||
| 18 | |||
| 19 | maxwell3d.regs.draw.topology.Assign( | ||
| 20 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & | ||
| 21 | ~(0x3ffffff << 26))); | ||
| 22 | maxwell3d.regs.vb_base_instance = parameters[5]; | ||
| 23 | maxwell3d.mme_draw.instance_count = instance_count; | ||
| 24 | maxwell3d.regs.vb_element_base = parameters[3]; | ||
| 25 | maxwell3d.regs.index_array.count = parameters[1]; | ||
| 26 | maxwell3d.regs.index_array.first = parameters[4]; | ||
| 27 | |||
| 28 | if (maxwell3d.ShouldExecute()) { | ||
| 29 | maxwell3d.GetRasterizer().Draw(true, true); | ||
| 30 | } | ||
| 31 | maxwell3d.regs.index_array.count = 0; | ||
| 32 | maxwell3d.mme_draw.instance_count = 0; | ||
| 33 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 34 | } | ||
| 35 | |||
| 36 | static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, | ||
| 37 | const std::vector<u32>& parameters) { | ||
| 38 | const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); | ||
| 39 | |||
| 40 | maxwell3d.regs.vertex_buffer.first = parameters[3]; | ||
| 41 | maxwell3d.regs.vertex_buffer.count = parameters[1]; | ||
| 42 | maxwell3d.regs.vb_base_instance = parameters[4]; | ||
| 43 | maxwell3d.regs.draw.topology.Assign( | ||
| 44 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); | ||
| 45 | maxwell3d.mme_draw.instance_count = count; | ||
| 46 | |||
| 47 | if (maxwell3d.ShouldExecute()) { | ||
| 48 | maxwell3d.GetRasterizer().Draw(false, true); | ||
| 49 | } | ||
| 50 | maxwell3d.regs.vertex_buffer.count = 0; | ||
| 51 | maxwell3d.mme_draw.instance_count = 0; | ||
| 52 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 53 | } | ||
| 54 | |||
| 55 | static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, | ||
| 56 | const std::vector<u32>& parameters) { | ||
| 57 | const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); | ||
| 58 | const u32 element_base = parameters[4]; | ||
| 59 | const u32 base_instance = parameters[5]; | ||
| 60 | maxwell3d.regs.index_array.first = parameters[3]; | ||
| 61 | maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? | ||
| 62 | maxwell3d.regs.index_array.count = parameters[1]; | ||
| 63 | maxwell3d.regs.vb_element_base = element_base; | ||
| 64 | maxwell3d.regs.vb_base_instance = base_instance; | ||
| 65 | maxwell3d.mme_draw.instance_count = instance_count; | ||
| 66 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | ||
| 67 | maxwell3d.CallMethodFromMME(0x8e4, element_base); | ||
| 68 | maxwell3d.CallMethodFromMME(0x8e5, base_instance); | ||
| 69 | maxwell3d.regs.draw.topology.Assign( | ||
| 70 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); | ||
| 71 | if (maxwell3d.ShouldExecute()) { | ||
| 72 | maxwell3d.GetRasterizer().Draw(true, true); | ||
| 73 | } | ||
| 74 | maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? | ||
| 75 | maxwell3d.regs.index_array.count = 0; | ||
| 76 | maxwell3d.regs.vb_element_base = 0x0; | ||
| 77 | maxwell3d.regs.vb_base_instance = 0x0; | ||
| 78 | maxwell3d.mme_draw.instance_count = 0; | ||
| 79 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | ||
| 80 | maxwell3d.CallMethodFromMME(0x8e4, 0x0); | ||
| 81 | maxwell3d.CallMethodFromMME(0x8e5, 0x0); | ||
| 82 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 83 | } | ||
| 84 | } // namespace | ||
| 85 | |||
| 86 | constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{ | ||
| 87 | std::make_pair<u64, HLEFunction>(0x771BB18C62444DA0, &HLE_771BB18C62444DA0), | ||
| 88 | std::make_pair<u64, HLEFunction>(0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD), | ||
| 89 | std::make_pair<u64, HLEFunction>(0x0217920100488FF7, &HLE_0217920100488FF7), | ||
| 90 | }}; | ||
| 91 | |||
| 92 | HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | ||
| 93 | HLEMacro::~HLEMacro() = default; | ||
| 94 | |||
| 95 | std::optional<std::unique_ptr<CachedMacro>> HLEMacro::GetHLEProgram(u64 hash) const { | ||
| 96 | const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), | ||
| 97 | [hash](const auto& pair) { return pair.first == hash; }); | ||
| 98 | if (it == hle_funcs.end()) { | ||
| 99 | return std::nullopt; | ||
| 100 | } | ||
| 101 | return std::make_unique<HLEMacroImpl>(maxwell3d, it->second); | ||
| 102 | } | ||
| 103 | |||
| 104 | HLEMacroImpl::~HLEMacroImpl() = default; | ||
| 105 | |||
| 106 | HLEMacroImpl::HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func) | ||
| 107 | : maxwell3d(maxwell3d), func(func) {} | ||
| 108 | |||
| 109 | void HLEMacroImpl::Execute(const std::vector<u32>& parameters, u32 method) { | ||
| 110 | func(maxwell3d, parameters); | ||
| 111 | } | ||
| 112 | |||
| 113 | } // namespace Tegra | ||
diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h new file mode 100644 index 000000000..37af875a0 --- /dev/null +++ b/src/video_core/macro/macro_hle.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "video_core/macro/macro.h" | ||
| 12 | |||
| 13 | namespace Tegra { | ||
| 14 | |||
| 15 | namespace Engines { | ||
| 16 | class Maxwell3D; | ||
| 17 | } | ||
| 18 | |||
| 19 | using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); | ||
| 20 | |||
| 21 | class HLEMacro { | ||
| 22 | public: | ||
| 23 | explicit HLEMacro(Engines::Maxwell3D& maxwell3d); | ||
| 24 | ~HLEMacro(); | ||
| 25 | |||
| 26 | std::optional<std::unique_ptr<CachedMacro>> GetHLEProgram(u64 hash) const; | ||
| 27 | |||
| 28 | private: | ||
| 29 | Engines::Maxwell3D& maxwell3d; | ||
| 30 | }; | ||
| 31 | |||
| 32 | class HLEMacroImpl : public CachedMacro { | ||
| 33 | public: | ||
| 34 | explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func); | ||
| 35 | ~HLEMacroImpl(); | ||
| 36 | |||
| 37 | void Execute(const std::vector<u32>& parameters, u32 method) override; | ||
| 38 | |||
| 39 | private: | ||
| 40 | Engines::Maxwell3D& maxwell3d; | ||
| 41 | HLEFunction func; | ||
| 42 | }; | ||
| 43 | |||
| 44 | } // namespace Tegra | ||
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index 947364928..aa5256419 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -6,109 +6,47 @@ | |||
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "common/microprofile.h" | 7 | #include "common/microprofile.h" |
| 8 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 9 | #include "video_core/macro_interpreter.h" | 9 | #include "video_core/macro/macro_interpreter.h" |
| 10 | 10 | ||
| 11 | MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); | 11 | MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); |
| 12 | 12 | ||
| 13 | namespace Tegra { | 13 | namespace Tegra { |
| 14 | namespace { | 14 | MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) |
| 15 | enum class Operation : u32 { | 15 | : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {} |
| 16 | ALU = 0, | ||
| 17 | AddImmediate = 1, | ||
| 18 | ExtractInsert = 2, | ||
| 19 | ExtractShiftLeftImmediate = 3, | ||
| 20 | ExtractShiftLeftRegister = 4, | ||
| 21 | Read = 5, | ||
| 22 | Unused = 6, // This operation doesn't seem to be a valid encoding. | ||
| 23 | Branch = 7, | ||
| 24 | }; | ||
| 25 | } // Anonymous namespace | ||
| 26 | |||
| 27 | enum class MacroInterpreter::ALUOperation : u32 { | ||
| 28 | Add = 0, | ||
| 29 | AddWithCarry = 1, | ||
| 30 | Subtract = 2, | ||
| 31 | SubtractWithBorrow = 3, | ||
| 32 | // Operations 4-7 don't seem to be valid encodings. | ||
| 33 | Xor = 8, | ||
| 34 | Or = 9, | ||
| 35 | And = 10, | ||
| 36 | AndNot = 11, | ||
| 37 | Nand = 12 | ||
| 38 | }; | ||
| 39 | |||
| 40 | enum class MacroInterpreter::ResultOperation : u32 { | ||
| 41 | IgnoreAndFetch = 0, | ||
| 42 | Move = 1, | ||
| 43 | MoveAndSetMethod = 2, | ||
| 44 | FetchAndSend = 3, | ||
| 45 | MoveAndSend = 4, | ||
| 46 | FetchAndSetMethod = 5, | ||
| 47 | MoveAndSetMethodFetchAndSend = 6, | ||
| 48 | MoveAndSetMethodSend = 7 | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum class MacroInterpreter::BranchCondition : u32 { | ||
| 52 | Zero = 0, | ||
| 53 | NotZero = 1, | ||
| 54 | }; | ||
| 55 | |||
| 56 | union MacroInterpreter::Opcode { | ||
| 57 | u32 raw; | ||
| 58 | BitField<0, 3, Operation> operation; | ||
| 59 | BitField<4, 3, ResultOperation> result_operation; | ||
| 60 | BitField<4, 1, BranchCondition> branch_condition; | ||
| 61 | // If set on a branch, then the branch doesn't have a delay slot. | ||
| 62 | BitField<5, 1, u32> branch_annul; | ||
| 63 | BitField<7, 1, u32> is_exit; | ||
| 64 | BitField<8, 3, u32> dst; | ||
| 65 | BitField<11, 3, u32> src_a; | ||
| 66 | BitField<14, 3, u32> src_b; | ||
| 67 | // The signed immediate overlaps the second source operand and the alu operation. | ||
| 68 | BitField<14, 18, s32> immediate; | ||
| 69 | |||
| 70 | BitField<17, 5, ALUOperation> alu_operation; | ||
| 71 | |||
| 72 | // Bitfield instructions data | ||
| 73 | BitField<17, 5, u32> bf_src_bit; | ||
| 74 | BitField<22, 5, u32> bf_size; | ||
| 75 | BitField<27, 5, u32> bf_dst_bit; | ||
| 76 | |||
| 77 | u32 GetBitfieldMask() const { | ||
| 78 | return (1 << bf_size) - 1; | ||
| 79 | } | ||
| 80 | 16 | ||
| 81 | s32 GetBranchTarget() const { | 17 | std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) { |
| 82 | return static_cast<s32>(immediate * sizeof(u32)); | 18 | return std::make_unique<MacroInterpreterImpl>(maxwell3d, code); |
| 83 | } | 19 | } |
| 84 | }; | ||
| 85 | 20 | ||
| 86 | MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | 21 | MacroInterpreterImpl::MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d, |
| 22 | const std::vector<u32>& code) | ||
| 23 | : maxwell3d(maxwell3d), code(code) {} | ||
| 87 | 24 | ||
| 88 | void MacroInterpreter::Execute(u32 offset, std::size_t num_parameters, const u32* parameters) { | 25 | void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 method) { |
| 89 | MICROPROFILE_SCOPE(MacroInterp); | 26 | MICROPROFILE_SCOPE(MacroInterp); |
| 90 | Reset(); | 27 | Reset(); |
| 91 | 28 | ||
| 92 | registers[1] = parameters[0]; | 29 | registers[1] = parameters[0]; |
| 30 | num_parameters = parameters.size(); | ||
| 93 | 31 | ||
| 94 | if (num_parameters > parameters_capacity) { | 32 | if (num_parameters > parameters_capacity) { |
| 95 | parameters_capacity = num_parameters; | 33 | parameters_capacity = num_parameters; |
| 96 | this->parameters = std::make_unique<u32[]>(num_parameters); | 34 | this->parameters = std::make_unique<u32[]>(num_parameters); |
| 97 | } | 35 | } |
| 98 | std::memcpy(this->parameters.get(), parameters, num_parameters * sizeof(u32)); | 36 | std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32)); |
| 99 | this->num_parameters = num_parameters; | 37 | this->num_parameters = num_parameters; |
| 100 | 38 | ||
| 101 | // Execute the code until we hit an exit condition. | 39 | // Execute the code until we hit an exit condition. |
| 102 | bool keep_executing = true; | 40 | bool keep_executing = true; |
| 103 | while (keep_executing) { | 41 | while (keep_executing) { |
| 104 | keep_executing = Step(offset, false); | 42 | keep_executing = Step(false); |
| 105 | } | 43 | } |
| 106 | 44 | ||
| 107 | // Assert the the macro used all the input parameters | 45 | // Assert the the macro used all the input parameters |
| 108 | ASSERT(next_parameter_index == num_parameters); | 46 | ASSERT(next_parameter_index == num_parameters); |
| 109 | } | 47 | } |
| 110 | 48 | ||
| 111 | void MacroInterpreter::Reset() { | 49 | void MacroInterpreterImpl::Reset() { |
| 112 | registers = {}; | 50 | registers = {}; |
| 113 | pc = 0; | 51 | pc = 0; |
| 114 | delayed_pc = {}; | 52 | delayed_pc = {}; |
| @@ -120,10 +58,10 @@ void MacroInterpreter::Reset() { | |||
| 120 | carry_flag = false; | 58 | carry_flag = false; |
| 121 | } | 59 | } |
| 122 | 60 | ||
| 123 | bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | 61 | bool MacroInterpreterImpl::Step(bool is_delay_slot) { |
| 124 | u32 base_address = pc; | 62 | u32 base_address = pc; |
| 125 | 63 | ||
| 126 | Opcode opcode = GetOpcode(offset); | 64 | Macro::Opcode opcode = GetOpcode(); |
| 127 | pc += 4; | 65 | pc += 4; |
| 128 | 66 | ||
| 129 | // Update the program counter if we were delayed | 67 | // Update the program counter if we were delayed |
| @@ -134,18 +72,18 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||
| 134 | } | 72 | } |
| 135 | 73 | ||
| 136 | switch (opcode.operation) { | 74 | switch (opcode.operation) { |
| 137 | case Operation::ALU: { | 75 | case Macro::Operation::ALU: { |
| 138 | u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), | 76 | u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), |
| 139 | GetRegister(opcode.src_b)); | 77 | GetRegister(opcode.src_b)); |
| 140 | ProcessResult(opcode.result_operation, opcode.dst, result); | 78 | ProcessResult(opcode.result_operation, opcode.dst, result); |
| 141 | break; | 79 | break; |
| 142 | } | 80 | } |
| 143 | case Operation::AddImmediate: { | 81 | case Macro::Operation::AddImmediate: { |
| 144 | ProcessResult(opcode.result_operation, opcode.dst, | 82 | ProcessResult(opcode.result_operation, opcode.dst, |
| 145 | GetRegister(opcode.src_a) + opcode.immediate); | 83 | GetRegister(opcode.src_a) + opcode.immediate); |
| 146 | break; | 84 | break; |
| 147 | } | 85 | } |
| 148 | case Operation::ExtractInsert: { | 86 | case Macro::Operation::ExtractInsert: { |
| 149 | u32 dst = GetRegister(opcode.src_a); | 87 | u32 dst = GetRegister(opcode.src_a); |
| 150 | u32 src = GetRegister(opcode.src_b); | 88 | u32 src = GetRegister(opcode.src_b); |
| 151 | 89 | ||
| @@ -155,7 +93,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||
| 155 | ProcessResult(opcode.result_operation, opcode.dst, dst); | 93 | ProcessResult(opcode.result_operation, opcode.dst, dst); |
| 156 | break; | 94 | break; |
| 157 | } | 95 | } |
| 158 | case Operation::ExtractShiftLeftImmediate: { | 96 | case Macro::Operation::ExtractShiftLeftImmediate: { |
| 159 | u32 dst = GetRegister(opcode.src_a); | 97 | u32 dst = GetRegister(opcode.src_a); |
| 160 | u32 src = GetRegister(opcode.src_b); | 98 | u32 src = GetRegister(opcode.src_b); |
| 161 | 99 | ||
| @@ -164,7 +102,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||
| 164 | ProcessResult(opcode.result_operation, opcode.dst, result); | 102 | ProcessResult(opcode.result_operation, opcode.dst, result); |
| 165 | break; | 103 | break; |
| 166 | } | 104 | } |
| 167 | case Operation::ExtractShiftLeftRegister: { | 105 | case Macro::Operation::ExtractShiftLeftRegister: { |
| 168 | u32 dst = GetRegister(opcode.src_a); | 106 | u32 dst = GetRegister(opcode.src_a); |
| 169 | u32 src = GetRegister(opcode.src_b); | 107 | u32 src = GetRegister(opcode.src_b); |
| 170 | 108 | ||
| @@ -173,12 +111,12 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||
| 173 | ProcessResult(opcode.result_operation, opcode.dst, result); | 111 | ProcessResult(opcode.result_operation, opcode.dst, result); |
| 174 | break; | 112 | break; |
| 175 | } | 113 | } |
| 176 | case Operation::Read: { | 114 | case Macro::Operation::Read: { |
| 177 | u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate); | 115 | u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate); |
| 178 | ProcessResult(opcode.result_operation, opcode.dst, result); | 116 | ProcessResult(opcode.result_operation, opcode.dst, result); |
| 179 | break; | 117 | break; |
| 180 | } | 118 | } |
| 181 | case Operation::Branch: { | 119 | case Macro::Operation::Branch: { |
| 182 | ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); | 120 | ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); |
| 183 | u32 value = GetRegister(opcode.src_a); | 121 | u32 value = GetRegister(opcode.src_a); |
| 184 | bool taken = EvaluateBranchCondition(opcode.branch_condition, value); | 122 | bool taken = EvaluateBranchCondition(opcode.branch_condition, value); |
| @@ -191,7 +129,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||
| 191 | 129 | ||
| 192 | delayed_pc = base_address + opcode.GetBranchTarget(); | 130 | delayed_pc = base_address + opcode.GetBranchTarget(); |
| 193 | // Execute one more instruction due to the delay slot. | 131 | // Execute one more instruction due to the delay slot. |
| 194 | return Step(offset, true); | 132 | return Step(true); |
| 195 | } | 133 | } |
| 196 | break; | 134 | break; |
| 197 | } | 135 | } |
| @@ -204,51 +142,44 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||
| 204 | // cause an exit if it's executed inside a delay slot. | 142 | // cause an exit if it's executed inside a delay slot. |
| 205 | if (opcode.is_exit && !is_delay_slot) { | 143 | if (opcode.is_exit && !is_delay_slot) { |
| 206 | // Exit has a delay slot, execute the next instruction | 144 | // Exit has a delay slot, execute the next instruction |
| 207 | Step(offset, true); | 145 | Step(true); |
| 208 | return false; | 146 | return false; |
| 209 | } | 147 | } |
| 210 | 148 | ||
| 211 | return true; | 149 | return true; |
| 212 | } | 150 | } |
| 213 | 151 | ||
| 214 | MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const { | 152 | u32 MacroInterpreterImpl::GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b) { |
| 215 | const auto& macro_memory{maxwell3d.GetMacroMemory()}; | ||
| 216 | ASSERT((pc % sizeof(u32)) == 0); | ||
| 217 | ASSERT((pc + offset) < macro_memory.size() * sizeof(u32)); | ||
| 218 | return {macro_memory[offset + pc / sizeof(u32)]}; | ||
| 219 | } | ||
| 220 | |||
| 221 | u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) { | ||
| 222 | switch (operation) { | 153 | switch (operation) { |
| 223 | case ALUOperation::Add: { | 154 | case Macro::ALUOperation::Add: { |
| 224 | const u64 result{static_cast<u64>(src_a) + src_b}; | 155 | const u64 result{static_cast<u64>(src_a) + src_b}; |
| 225 | carry_flag = result > 0xffffffff; | 156 | carry_flag = result > 0xffffffff; |
| 226 | return static_cast<u32>(result); | 157 | return static_cast<u32>(result); |
| 227 | } | 158 | } |
| 228 | case ALUOperation::AddWithCarry: { | 159 | case Macro::ALUOperation::AddWithCarry: { |
| 229 | const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)}; | 160 | const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)}; |
| 230 | carry_flag = result > 0xffffffff; | 161 | carry_flag = result > 0xffffffff; |
| 231 | return static_cast<u32>(result); | 162 | return static_cast<u32>(result); |
| 232 | } | 163 | } |
| 233 | case ALUOperation::Subtract: { | 164 | case Macro::ALUOperation::Subtract: { |
| 234 | const u64 result{static_cast<u64>(src_a) - src_b}; | 165 | const u64 result{static_cast<u64>(src_a) - src_b}; |
| 235 | carry_flag = result < 0x100000000; | 166 | carry_flag = result < 0x100000000; |
| 236 | return static_cast<u32>(result); | 167 | return static_cast<u32>(result); |
| 237 | } | 168 | } |
| 238 | case ALUOperation::SubtractWithBorrow: { | 169 | case Macro::ALUOperation::SubtractWithBorrow: { |
| 239 | const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)}; | 170 | const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)}; |
| 240 | carry_flag = result < 0x100000000; | 171 | carry_flag = result < 0x100000000; |
| 241 | return static_cast<u32>(result); | 172 | return static_cast<u32>(result); |
| 242 | } | 173 | } |
| 243 | case ALUOperation::Xor: | 174 | case Macro::ALUOperation::Xor: |
| 244 | return src_a ^ src_b; | 175 | return src_a ^ src_b; |
| 245 | case ALUOperation::Or: | 176 | case Macro::ALUOperation::Or: |
| 246 | return src_a | src_b; | 177 | return src_a | src_b; |
| 247 | case ALUOperation::And: | 178 | case Macro::ALUOperation::And: |
| 248 | return src_a & src_b; | 179 | return src_a & src_b; |
| 249 | case ALUOperation::AndNot: | 180 | case Macro::ALUOperation::AndNot: |
| 250 | return src_a & ~src_b; | 181 | return src_a & ~src_b; |
| 251 | case ALUOperation::Nand: | 182 | case Macro::ALUOperation::Nand: |
| 252 | return ~(src_a & src_b); | 183 | return ~(src_a & src_b); |
| 253 | 184 | ||
| 254 | default: | 185 | default: |
| @@ -257,43 +188,43 @@ u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) | |||
| 257 | } | 188 | } |
| 258 | } | 189 | } |
| 259 | 190 | ||
| 260 | void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 result) { | 191 | void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result) { |
| 261 | switch (operation) { | 192 | switch (operation) { |
| 262 | case ResultOperation::IgnoreAndFetch: | 193 | case Macro::ResultOperation::IgnoreAndFetch: |
| 263 | // Fetch parameter and ignore result. | 194 | // Fetch parameter and ignore result. |
| 264 | SetRegister(reg, FetchParameter()); | 195 | SetRegister(reg, FetchParameter()); |
| 265 | break; | 196 | break; |
| 266 | case ResultOperation::Move: | 197 | case Macro::ResultOperation::Move: |
| 267 | // Move result. | 198 | // Move result. |
| 268 | SetRegister(reg, result); | 199 | SetRegister(reg, result); |
| 269 | break; | 200 | break; |
| 270 | case ResultOperation::MoveAndSetMethod: | 201 | case Macro::ResultOperation::MoveAndSetMethod: |
| 271 | // Move result and use as Method Address. | 202 | // Move result and use as Method Address. |
| 272 | SetRegister(reg, result); | 203 | SetRegister(reg, result); |
| 273 | SetMethodAddress(result); | 204 | SetMethodAddress(result); |
| 274 | break; | 205 | break; |
| 275 | case ResultOperation::FetchAndSend: | 206 | case Macro::ResultOperation::FetchAndSend: |
| 276 | // Fetch parameter and send result. | 207 | // Fetch parameter and send result. |
| 277 | SetRegister(reg, FetchParameter()); | 208 | SetRegister(reg, FetchParameter()); |
| 278 | Send(result); | 209 | Send(result); |
| 279 | break; | 210 | break; |
| 280 | case ResultOperation::MoveAndSend: | 211 | case Macro::ResultOperation::MoveAndSend: |
| 281 | // Move and send result. | 212 | // Move and send result. |
| 282 | SetRegister(reg, result); | 213 | SetRegister(reg, result); |
| 283 | Send(result); | 214 | Send(result); |
| 284 | break; | 215 | break; |
| 285 | case ResultOperation::FetchAndSetMethod: | 216 | case Macro::ResultOperation::FetchAndSetMethod: |
| 286 | // Fetch parameter and use result as Method Address. | 217 | // Fetch parameter and use result as Method Address. |
| 287 | SetRegister(reg, FetchParameter()); | 218 | SetRegister(reg, FetchParameter()); |
| 288 | SetMethodAddress(result); | 219 | SetMethodAddress(result); |
| 289 | break; | 220 | break; |
| 290 | case ResultOperation::MoveAndSetMethodFetchAndSend: | 221 | case Macro::ResultOperation::MoveAndSetMethodFetchAndSend: |
| 291 | // Move result and use as Method Address, then fetch and send parameter. | 222 | // Move result and use as Method Address, then fetch and send parameter. |
| 292 | SetRegister(reg, result); | 223 | SetRegister(reg, result); |
| 293 | SetMethodAddress(result); | 224 | SetMethodAddress(result); |
| 294 | Send(FetchParameter()); | 225 | Send(FetchParameter()); |
| 295 | break; | 226 | break; |
| 296 | case ResultOperation::MoveAndSetMethodSend: | 227 | case Macro::ResultOperation::MoveAndSetMethodSend: |
| 297 | // Move result and use as Method Address, then send bits 12:17 of result. | 228 | // Move result and use as Method Address, then send bits 12:17 of result. |
| 298 | SetRegister(reg, result); | 229 | SetRegister(reg, result); |
| 299 | SetMethodAddress(result); | 230 | SetMethodAddress(result); |
| @@ -304,16 +235,28 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res | |||
| 304 | } | 235 | } |
| 305 | } | 236 | } |
| 306 | 237 | ||
| 307 | u32 MacroInterpreter::FetchParameter() { | 238 | bool MacroInterpreterImpl::EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const { |
| 308 | ASSERT(next_parameter_index < num_parameters); | 239 | switch (cond) { |
| 309 | return parameters[next_parameter_index++]; | 240 | case Macro::BranchCondition::Zero: |
| 241 | return value == 0; | ||
| 242 | case Macro::BranchCondition::NotZero: | ||
| 243 | return value != 0; | ||
| 244 | } | ||
| 245 | UNREACHABLE(); | ||
| 246 | return true; | ||
| 247 | } | ||
| 248 | |||
| 249 | Macro::Opcode MacroInterpreterImpl::GetOpcode() const { | ||
| 250 | ASSERT((pc % sizeof(u32)) == 0); | ||
| 251 | ASSERT(pc < code.size() * sizeof(u32)); | ||
| 252 | return {code[pc / sizeof(u32)]}; | ||
| 310 | } | 253 | } |
| 311 | 254 | ||
| 312 | u32 MacroInterpreter::GetRegister(u32 register_id) const { | 255 | u32 MacroInterpreterImpl::GetRegister(u32 register_id) const { |
| 313 | return registers.at(register_id); | 256 | return registers.at(register_id); |
| 314 | } | 257 | } |
| 315 | 258 | ||
| 316 | void MacroInterpreter::SetRegister(u32 register_id, u32 value) { | 259 | void MacroInterpreterImpl::SetRegister(u32 register_id, u32 value) { |
| 317 | // Register 0 is hardwired as the zero register. | 260 | // Register 0 is hardwired as the zero register. |
| 318 | // Ensure no writes to it actually occur. | 261 | // Ensure no writes to it actually occur. |
| 319 | if (register_id == 0) { | 262 | if (register_id == 0) { |
| @@ -323,30 +266,24 @@ void MacroInterpreter::SetRegister(u32 register_id, u32 value) { | |||
| 323 | registers.at(register_id) = value; | 266 | registers.at(register_id) = value; |
| 324 | } | 267 | } |
| 325 | 268 | ||
| 326 | void MacroInterpreter::SetMethodAddress(u32 address) { | 269 | void MacroInterpreterImpl::SetMethodAddress(u32 address) { |
| 327 | method_address.raw = address; | 270 | method_address.raw = address; |
| 328 | } | 271 | } |
| 329 | 272 | ||
| 330 | void MacroInterpreter::Send(u32 value) { | 273 | void MacroInterpreterImpl::Send(u32 value) { |
| 331 | maxwell3d.CallMethodFromMME(method_address.address, value); | 274 | maxwell3d.CallMethodFromMME(method_address.address, value); |
| 332 | // Increment the method address by the method increment. | 275 | // Increment the method address by the method increment. |
| 333 | method_address.address.Assign(method_address.address.Value() + | 276 | method_address.address.Assign(method_address.address.Value() + |
| 334 | method_address.increment.Value()); | 277 | method_address.increment.Value()); |
| 335 | } | 278 | } |
| 336 | 279 | ||
| 337 | u32 MacroInterpreter::Read(u32 method) const { | 280 | u32 MacroInterpreterImpl::Read(u32 method) const { |
| 338 | return maxwell3d.GetRegisterValue(method); | 281 | return maxwell3d.GetRegisterValue(method); |
| 339 | } | 282 | } |
| 340 | 283 | ||
| 341 | bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value) const { | 284 | u32 MacroInterpreterImpl::FetchParameter() { |
| 342 | switch (cond) { | 285 | ASSERT(next_parameter_index < num_parameters); |
| 343 | case BranchCondition::Zero: | 286 | return parameters[next_parameter_index++]; |
| 344 | return value == 0; | ||
| 345 | case BranchCondition::NotZero: | ||
| 346 | return value != 0; | ||
| 347 | } | ||
| 348 | UNREACHABLE(); | ||
| 349 | return true; | ||
| 350 | } | 287 | } |
| 351 | 288 | ||
| 352 | } // namespace Tegra | 289 | } // namespace Tegra |
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro/macro_interpreter.h index 631146d89..90217fc89 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro/macro_interpreter.h | |||
| @@ -1,44 +1,37 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | |||
| 7 | #include <array> | 6 | #include <array> |
| 8 | #include <optional> | 7 | #include <optional> |
| 9 | 8 | #include <vector> | |
| 10 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/macro/macro.h" | ||
| 12 | 12 | ||
| 13 | namespace Tegra { | 13 | namespace Tegra { |
| 14 | namespace Engines { | 14 | namespace Engines { |
| 15 | class Maxwell3D; | 15 | class Maxwell3D; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | class MacroInterpreter final { | 18 | class MacroInterpreter final : public MacroEngine { |
| 19 | public: | 19 | public: |
| 20 | explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d); | 20 | explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d); |
| 21 | 21 | ||
| 22 | /** | 22 | protected: |
| 23 | * Executes the macro code with the specified input parameters. | 23 | std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) override; |
| 24 | * @param offset Offset to start execution at. | ||
| 25 | * @param parameters The parameters of the macro. | ||
| 26 | */ | ||
| 27 | void Execute(u32 offset, std::size_t num_parameters, const u32* parameters); | ||
| 28 | 24 | ||
| 29 | private: | 25 | private: |
| 30 | enum class ALUOperation : u32; | 26 | Engines::Maxwell3D& maxwell3d; |
| 31 | enum class BranchCondition : u32; | 27 | }; |
| 32 | enum class ResultOperation : u32; | ||
| 33 | |||
| 34 | union Opcode; | ||
| 35 | 28 | ||
| 36 | union MethodAddress { | 29 | class MacroInterpreterImpl : public CachedMacro { |
| 37 | u32 raw; | 30 | public: |
| 38 | BitField<0, 12, u32> address; | 31 | MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code); |
| 39 | BitField<12, 6, u32> increment; | 32 | void Execute(const std::vector<u32>& parameters, u32 method) override; |
| 40 | }; | ||
| 41 | 33 | ||
| 34 | private: | ||
| 42 | /// Resets the execution engine state, zeroing registers, etc. | 35 | /// Resets the execution engine state, zeroing registers, etc. |
| 43 | void Reset(); | 36 | void Reset(); |
| 44 | 37 | ||
| @@ -49,20 +42,20 @@ private: | |||
| 49 | * @param is_delay_slot Whether the current step is being executed due to a delay slot in a | 42 | * @param is_delay_slot Whether the current step is being executed due to a delay slot in a |
| 50 | * previous instruction. | 43 | * previous instruction. |
| 51 | */ | 44 | */ |
| 52 | bool Step(u32 offset, bool is_delay_slot); | 45 | bool Step(bool is_delay_slot); |
| 53 | 46 | ||
| 54 | /// Calculates the result of an ALU operation. src_a OP src_b; | 47 | /// Calculates the result of an ALU operation. src_a OP src_b; |
| 55 | u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b); | 48 | u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b); |
| 56 | 49 | ||
| 57 | /// Performs the result operation on the input result and stores it in the specified register | 50 | /// Performs the result operation on the input result and stores it in the specified register |
| 58 | /// (if necessary). | 51 | /// (if necessary). |
| 59 | void ProcessResult(ResultOperation operation, u32 reg, u32 result); | 52 | void ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result); |
| 60 | 53 | ||
| 61 | /// Evaluates the branch condition and returns whether the branch should be taken or not. | 54 | /// Evaluates the branch condition and returns whether the branch should be taken or not. |
| 62 | bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; | 55 | bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const; |
| 63 | 56 | ||
| 64 | /// Reads an opcode at the current program counter location. | 57 | /// Reads an opcode at the current program counter location. |
| 65 | Opcode GetOpcode(u32 offset) const; | 58 | Macro::Opcode GetOpcode() const; |
| 66 | 59 | ||
| 67 | /// Returns the specified register's value. Register 0 is hardcoded to always return 0. | 60 | /// Returns the specified register's value. Register 0 is hardcoded to always return 0. |
| 68 | u32 GetRegister(u32 register_id) const; | 61 | u32 GetRegister(u32 register_id) const; |
| @@ -89,13 +82,11 @@ private: | |||
| 89 | /// Program counter to execute at after the delay slot is executed. | 82 | /// Program counter to execute at after the delay slot is executed. |
| 90 | std::optional<u32> delayed_pc; | 83 | std::optional<u32> delayed_pc; |
| 91 | 84 | ||
| 92 | static constexpr std::size_t NumMacroRegisters = 8; | ||
| 93 | |||
| 94 | /// General purpose macro registers. | 85 | /// General purpose macro registers. |
| 95 | std::array<u32, NumMacroRegisters> registers = {}; | 86 | std::array<u32, Macro::NUM_MACRO_REGISTERS> registers = {}; |
| 96 | 87 | ||
| 97 | /// Method address to use for the next Send instruction. | 88 | /// Method address to use for the next Send instruction. |
| 98 | MethodAddress method_address = {}; | 89 | Macro::MethodAddress method_address = {}; |
| 99 | 90 | ||
| 100 | /// Input parameters of the current macro. | 91 | /// Input parameters of the current macro. |
| 101 | std::unique_ptr<u32[]> parameters; | 92 | std::unique_ptr<u32[]> parameters; |
| @@ -105,5 +96,7 @@ private: | |||
| 105 | u32 next_parameter_index = 0; | 96 | u32 next_parameter_index = 0; |
| 106 | 97 | ||
| 107 | bool carry_flag = false; | 98 | bool carry_flag = false; |
| 99 | const std::vector<u32>& code; | ||
| 108 | }; | 100 | }; |
| 101 | |||
| 109 | } // namespace Tegra | 102 | } // namespace Tegra |
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp new file mode 100644 index 000000000..07292702f --- /dev/null +++ b/src/video_core/macro/macro_jit_x64.cpp | |||
| @@ -0,0 +1,621 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "common/microprofile.h" | ||
| 8 | #include "common/x64/xbyak_util.h" | ||
| 9 | #include "video_core/engines/maxwell_3d.h" | ||
| 10 | #include "video_core/macro/macro_interpreter.h" | ||
| 11 | #include "video_core/macro/macro_jit_x64.h" | ||
| 12 | |||
| 13 | MICROPROFILE_DEFINE(MacroJitCompile, "GPU", "Compile macro JIT", MP_RGB(173, 255, 47)); | ||
| 14 | MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255, 0)); | ||
| 15 | |||
| 16 | namespace Tegra { | ||
| 17 | static const Xbyak::Reg64 STATE = Xbyak::util::rbx; | ||
| 18 | static const Xbyak::Reg32 RESULT = Xbyak::util::ebp; | ||
| 19 | static const Xbyak::Reg64 PARAMETERS = Xbyak::util::r12; | ||
| 20 | static const Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d; | ||
| 21 | static const Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15; | ||
| 22 | |||
| 23 | static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ | ||
| 24 | STATE, | ||
| 25 | RESULT, | ||
| 26 | PARAMETERS, | ||
| 27 | METHOD_ADDRESS, | ||
| 28 | BRANCH_HOLDER, | ||
| 29 | }); | ||
| 30 | |||
| 31 | MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) | ||
| 32 | : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {} | ||
| 33 | |||
| 34 | std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) { | ||
| 35 | return std::make_unique<MacroJITx64Impl>(maxwell3d, code); | ||
| 36 | } | ||
| 37 | |||
| 38 | MacroJITx64Impl::MacroJITx64Impl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code) | ||
| 39 | : Xbyak::CodeGenerator(MAX_CODE_SIZE), code(code), maxwell3d(maxwell3d) { | ||
| 40 | Compile(); | ||
| 41 | } | ||
| 42 | |||
| 43 | MacroJITx64Impl::~MacroJITx64Impl() = default; | ||
| 44 | |||
| 45 | void MacroJITx64Impl::Execute(const std::vector<u32>& parameters, u32 method) { | ||
| 46 | MICROPROFILE_SCOPE(MacroJitExecute); | ||
| 47 | ASSERT_OR_EXECUTE(program != nullptr, { return; }); | ||
| 48 | JITState state{}; | ||
| 49 | state.maxwell3d = &maxwell3d; | ||
| 50 | state.registers = {}; | ||
| 51 | program(&state, parameters.data()); | ||
| 52 | } | ||
| 53 | |||
| 54 | void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) { | ||
| 55 | const bool is_a_zero = opcode.src_a == 0; | ||
| 56 | const bool is_b_zero = opcode.src_b == 0; | ||
| 57 | const bool valid_operation = !is_a_zero && !is_b_zero; | ||
| 58 | [[maybe_unused]] const bool is_move_operation = !is_a_zero && is_b_zero; | ||
| 59 | const bool has_zero_register = is_a_zero || is_b_zero; | ||
| 60 | const bool no_zero_reg_skip = opcode.alu_operation == Macro::ALUOperation::AddWithCarry || | ||
| 61 | opcode.alu_operation == Macro::ALUOperation::SubtractWithBorrow; | ||
| 62 | |||
| 63 | Xbyak::Reg32 src_a; | ||
| 64 | Xbyak::Reg32 src_b; | ||
| 65 | |||
| 66 | if (!optimizer.zero_reg_skip || no_zero_reg_skip) { | ||
| 67 | src_a = Compile_GetRegister(opcode.src_a, RESULT); | ||
| 68 | src_b = Compile_GetRegister(opcode.src_b, eax); | ||
| 69 | } else { | ||
| 70 | if (!is_a_zero) { | ||
| 71 | src_a = Compile_GetRegister(opcode.src_a, RESULT); | ||
| 72 | } | ||
| 73 | if (!is_b_zero) { | ||
| 74 | src_b = Compile_GetRegister(opcode.src_b, eax); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | bool has_emitted = false; | ||
| 79 | |||
| 80 | switch (opcode.alu_operation) { | ||
| 81 | case Macro::ALUOperation::Add: | ||
| 82 | if (optimizer.zero_reg_skip) { | ||
| 83 | if (valid_operation) { | ||
| 84 | add(src_a, src_b); | ||
| 85 | } | ||
| 86 | } else { | ||
| 87 | add(src_a, src_b); | ||
| 88 | } | ||
| 89 | |||
| 90 | if (!optimizer.can_skip_carry) { | ||
| 91 | setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||
| 92 | } | ||
| 93 | break; | ||
| 94 | case Macro::ALUOperation::AddWithCarry: | ||
| 95 | bt(dword[STATE + offsetof(JITState, carry_flag)], 0); | ||
| 96 | adc(src_a, src_b); | ||
| 97 | setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||
| 98 | break; | ||
| 99 | case Macro::ALUOperation::Subtract: | ||
| 100 | if (optimizer.zero_reg_skip) { | ||
| 101 | if (valid_operation) { | ||
| 102 | sub(src_a, src_b); | ||
| 103 | has_emitted = true; | ||
| 104 | } | ||
| 105 | } else { | ||
| 106 | sub(src_a, src_b); | ||
| 107 | has_emitted = true; | ||
| 108 | } | ||
| 109 | if (!optimizer.can_skip_carry && has_emitted) { | ||
| 110 | setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||
| 111 | } | ||
| 112 | break; | ||
| 113 | case Macro::ALUOperation::SubtractWithBorrow: | ||
| 114 | bt(dword[STATE + offsetof(JITState, carry_flag)], 0); | ||
| 115 | sbb(src_a, src_b); | ||
| 116 | setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||
| 117 | break; | ||
| 118 | case Macro::ALUOperation::Xor: | ||
| 119 | if (optimizer.zero_reg_skip) { | ||
| 120 | if (valid_operation) { | ||
| 121 | xor_(src_a, src_b); | ||
| 122 | } | ||
| 123 | } else { | ||
| 124 | xor_(src_a, src_b); | ||
| 125 | } | ||
| 126 | break; | ||
| 127 | case Macro::ALUOperation::Or: | ||
| 128 | if (optimizer.zero_reg_skip) { | ||
| 129 | if (valid_operation) { | ||
| 130 | or_(src_a, src_b); | ||
| 131 | } | ||
| 132 | } else { | ||
| 133 | or_(src_a, src_b); | ||
| 134 | } | ||
| 135 | break; | ||
| 136 | case Macro::ALUOperation::And: | ||
| 137 | if (optimizer.zero_reg_skip) { | ||
| 138 | if (!has_zero_register) { | ||
| 139 | and_(src_a, src_b); | ||
| 140 | } | ||
| 141 | } else { | ||
| 142 | and_(src_a, src_b); | ||
| 143 | } | ||
| 144 | break; | ||
| 145 | case Macro::ALUOperation::AndNot: | ||
| 146 | if (optimizer.zero_reg_skip) { | ||
| 147 | if (!is_a_zero) { | ||
| 148 | not_(src_b); | ||
| 149 | and_(src_a, src_b); | ||
| 150 | } | ||
| 151 | } else { | ||
| 152 | not_(src_b); | ||
| 153 | and_(src_a, src_b); | ||
| 154 | } | ||
| 155 | break; | ||
| 156 | case Macro::ALUOperation::Nand: | ||
| 157 | if (optimizer.zero_reg_skip) { | ||
| 158 | if (!is_a_zero) { | ||
| 159 | and_(src_a, src_b); | ||
| 160 | not_(src_a); | ||
| 161 | } | ||
| 162 | } else { | ||
| 163 | and_(src_a, src_b); | ||
| 164 | not_(src_a); | ||
| 165 | } | ||
| 166 | break; | ||
| 167 | default: | ||
| 168 | UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", | ||
| 169 | static_cast<std::size_t>(opcode.alu_operation.Value())); | ||
| 170 | break; | ||
| 171 | } | ||
| 172 | Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||
| 173 | } | ||
| 174 | |||
| 175 | void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) { | ||
| 176 | if (optimizer.skip_dummy_addimmediate) { | ||
| 177 | // Games tend to use this as an exit instruction placeholder. It's to encode an instruction | ||
| 178 | // without doing anything. In our case we can just not emit anything. | ||
| 179 | if (opcode.result_operation == Macro::ResultOperation::Move && opcode.dst == 0) { | ||
| 180 | return; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | // Check for redundant moves | ||
| 184 | if (optimizer.optimize_for_method_move && | ||
| 185 | opcode.result_operation == Macro::ResultOperation::MoveAndSetMethod) { | ||
| 186 | if (next_opcode.has_value()) { | ||
| 187 | const auto next = *next_opcode; | ||
| 188 | if (next.result_operation == Macro::ResultOperation::MoveAndSetMethod && | ||
| 189 | opcode.dst == next.dst) { | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | if (optimizer.zero_reg_skip && opcode.src_a == 0) { | ||
| 195 | if (opcode.immediate == 0) { | ||
| 196 | xor_(RESULT, RESULT); | ||
| 197 | } else { | ||
| 198 | mov(RESULT, opcode.immediate); | ||
| 199 | } | ||
| 200 | } else { | ||
| 201 | auto result = Compile_GetRegister(opcode.src_a, RESULT); | ||
| 202 | if (opcode.immediate > 2) { | ||
| 203 | add(result, opcode.immediate); | ||
| 204 | } else if (opcode.immediate == 1) { | ||
| 205 | inc(result); | ||
| 206 | } else if (opcode.immediate < 0) { | ||
| 207 | sub(result, opcode.immediate * -1); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||
| 211 | } | ||
| 212 | |||
| 213 | void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) { | ||
| 214 | auto dst = Compile_GetRegister(opcode.src_a, RESULT); | ||
| 215 | auto src = Compile_GetRegister(opcode.src_b, eax); | ||
| 216 | |||
| 217 | if (opcode.bf_src_bit != 0 && opcode.bf_src_bit != 31) { | ||
| 218 | shr(src, opcode.bf_src_bit); | ||
| 219 | } else if (opcode.bf_src_bit == 31) { | ||
| 220 | xor_(src, src); | ||
| 221 | } | ||
| 222 | // Don't bother masking the whole register since we're using a 32 bit register | ||
| 223 | if (opcode.bf_size != 31 && opcode.bf_size != 0) { | ||
| 224 | and_(src, opcode.GetBitfieldMask()); | ||
| 225 | } else if (opcode.bf_size == 0) { | ||
| 226 | xor_(src, src); | ||
| 227 | } | ||
| 228 | if (opcode.bf_dst_bit != 31 && opcode.bf_dst_bit != 0) { | ||
| 229 | shl(src, opcode.bf_dst_bit); | ||
| 230 | } else if (opcode.bf_dst_bit == 31) { | ||
| 231 | xor_(src, src); | ||
| 232 | } | ||
| 233 | |||
| 234 | const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit); | ||
| 235 | if (mask != 0xffffffff) { | ||
| 236 | and_(dst, mask); | ||
| 237 | } | ||
| 238 | or_(dst, src); | ||
| 239 | Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||
| 240 | } | ||
| 241 | |||
| 242 | void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) { | ||
| 243 | const auto dst = Compile_GetRegister(opcode.src_a, ecx); | ||
| 244 | const auto src = Compile_GetRegister(opcode.src_b, RESULT); | ||
| 245 | |||
| 246 | shr(src, dst.cvt8()); | ||
| 247 | if (opcode.bf_size != 0 && opcode.bf_size != 31) { | ||
| 248 | and_(src, opcode.GetBitfieldMask()); | ||
| 249 | } else if (opcode.bf_size == 0) { | ||
| 250 | xor_(src, src); | ||
| 251 | } | ||
| 252 | |||
| 253 | if (opcode.bf_dst_bit != 0 && opcode.bf_dst_bit != 31) { | ||
| 254 | shl(src, opcode.bf_dst_bit); | ||
| 255 | } else if (opcode.bf_dst_bit == 31) { | ||
| 256 | xor_(src, src); | ||
| 257 | } | ||
| 258 | Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||
| 259 | } | ||
| 260 | |||
| 261 | void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) { | ||
| 262 | const auto dst = Compile_GetRegister(opcode.src_a, ecx); | ||
| 263 | const auto src = Compile_GetRegister(opcode.src_b, RESULT); | ||
| 264 | |||
| 265 | if (opcode.bf_src_bit != 0) { | ||
| 266 | shr(src, opcode.bf_src_bit); | ||
| 267 | } | ||
| 268 | |||
| 269 | if (opcode.bf_size != 31) { | ||
| 270 | and_(src, opcode.GetBitfieldMask()); | ||
| 271 | } | ||
| 272 | shl(src, dst.cvt8()); | ||
| 273 | |||
| 274 | Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||
| 275 | } | ||
| 276 | |||
| 277 | void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { | ||
| 278 | if (optimizer.zero_reg_skip && opcode.src_a == 0) { | ||
| 279 | if (opcode.immediate == 0) { | ||
| 280 | xor_(RESULT, RESULT); | ||
| 281 | } else { | ||
| 282 | mov(RESULT, opcode.immediate); | ||
| 283 | } | ||
| 284 | } else { | ||
| 285 | auto result = Compile_GetRegister(opcode.src_a, RESULT); | ||
| 286 | if (opcode.immediate > 2) { | ||
| 287 | add(result, opcode.immediate); | ||
| 288 | } else if (opcode.immediate == 1) { | ||
| 289 | inc(result); | ||
| 290 | } else if (opcode.immediate < 0) { | ||
| 291 | sub(result, opcode.immediate * -1); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | // Equivalent to Engines::Maxwell3D::GetRegisterValue: | ||
| 296 | if (optimizer.enable_asserts) { | ||
| 297 | Xbyak::Label pass_range_check; | ||
| 298 | cmp(RESULT, static_cast<u32>(Engines::Maxwell3D::Regs::NUM_REGS)); | ||
| 299 | jb(pass_range_check); | ||
| 300 | int3(); | ||
| 301 | L(pass_range_check); | ||
| 302 | } | ||
| 303 | mov(rax, qword[STATE]); | ||
| 304 | mov(RESULT, | ||
| 305 | dword[rax + offsetof(Engines::Maxwell3D, regs) + | ||
| 306 | offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]); | ||
| 307 | |||
| 308 | Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||
| 309 | } | ||
| 310 | |||
| 311 | static void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { | ||
| 312 | maxwell3d->CallMethodFromMME(method_address.address, value); | ||
| 313 | } | ||
| 314 | |||
| 315 | void Tegra::MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { | ||
| 316 | Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); | ||
| 317 | mov(Common::X64::ABI_PARAM1, qword[STATE]); | ||
| 318 | mov(Common::X64::ABI_PARAM2, METHOD_ADDRESS); | ||
| 319 | mov(Common::X64::ABI_PARAM3, value); | ||
| 320 | Common::X64::CallFarFunction(*this, &Send); | ||
| 321 | Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); | ||
| 322 | |||
| 323 | Xbyak::Label dont_process{}; | ||
| 324 | // Get increment | ||
| 325 | test(METHOD_ADDRESS, 0x3f000); | ||
| 326 | // If zero, method address doesn't update | ||
| 327 | je(dont_process); | ||
| 328 | |||
| 329 | mov(ecx, METHOD_ADDRESS); | ||
| 330 | and_(METHOD_ADDRESS, 0xfff); | ||
| 331 | shr(ecx, 12); | ||
| 332 | and_(ecx, 0x3f); | ||
| 333 | lea(eax, ptr[rcx + METHOD_ADDRESS.cvt64()]); | ||
| 334 | sal(ecx, 12); | ||
| 335 | or_(eax, ecx); | ||
| 336 | |||
| 337 | mov(METHOD_ADDRESS, eax); | ||
| 338 | |||
| 339 | L(dont_process); | ||
| 340 | } | ||
| 341 | |||
| 342 | void Tegra::MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { | ||
| 343 | ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); | ||
| 344 | const s32 jump_address = | ||
| 345 | static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32)); | ||
| 346 | |||
| 347 | Xbyak::Label end; | ||
| 348 | auto value = Compile_GetRegister(opcode.src_a, eax); | ||
| 349 | test(value, value); | ||
| 350 | if (optimizer.has_delayed_pc) { | ||
| 351 | switch (opcode.branch_condition) { | ||
| 352 | case Macro::BranchCondition::Zero: | ||
| 353 | jne(end, T_NEAR); | ||
| 354 | break; | ||
| 355 | case Macro::BranchCondition::NotZero: | ||
| 356 | je(end, T_NEAR); | ||
| 357 | break; | ||
| 358 | } | ||
| 359 | |||
| 360 | if (opcode.branch_annul) { | ||
| 361 | xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||
| 362 | jmp(labels[jump_address], T_NEAR); | ||
| 363 | } else { | ||
| 364 | Xbyak::Label handle_post_exit{}; | ||
| 365 | Xbyak::Label skip{}; | ||
| 366 | jmp(skip, T_NEAR); | ||
| 367 | if (opcode.is_exit) { | ||
| 368 | L(handle_post_exit); | ||
| 369 | // Execute 1 instruction | ||
| 370 | mov(BRANCH_HOLDER, end_of_code); | ||
| 371 | // Jump to next instruction to skip delay slot check | ||
| 372 | jmp(labels[jump_address], T_NEAR); | ||
| 373 | } else { | ||
| 374 | L(handle_post_exit); | ||
| 375 | xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||
| 376 | jmp(labels[jump_address], T_NEAR); | ||
| 377 | } | ||
| 378 | L(skip); | ||
| 379 | mov(BRANCH_HOLDER, handle_post_exit); | ||
| 380 | jmp(delay_skip[pc], T_NEAR); | ||
| 381 | } | ||
| 382 | } else { | ||
| 383 | switch (opcode.branch_condition) { | ||
| 384 | case Macro::BranchCondition::Zero: | ||
| 385 | je(labels[jump_address], T_NEAR); | ||
| 386 | break; | ||
| 387 | case Macro::BranchCondition::NotZero: | ||
| 388 | jne(labels[jump_address], T_NEAR); | ||
| 389 | break; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | L(end); | ||
| 394 | } | ||
| 395 | |||
| 396 | void Tegra::MacroJITx64Impl::Optimizer_ScanFlags() { | ||
| 397 | optimizer.can_skip_carry = true; | ||
| 398 | optimizer.has_delayed_pc = false; | ||
| 399 | for (auto raw_op : code) { | ||
| 400 | Macro::Opcode op{}; | ||
| 401 | op.raw = raw_op; | ||
| 402 | |||
| 403 | if (op.operation == Macro::Operation::ALU) { | ||
| 404 | // Scan for any ALU operations which actually use the carry flag, if they don't exist in | ||
| 405 | // our current code we can skip emitting the carry flag handling operations | ||
| 406 | if (op.alu_operation == Macro::ALUOperation::AddWithCarry || | ||
| 407 | op.alu_operation == Macro::ALUOperation::SubtractWithBorrow) { | ||
| 408 | optimizer.can_skip_carry = false; | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | if (op.operation == Macro::Operation::Branch) { | ||
| 413 | if (!op.branch_annul) { | ||
| 414 | optimizer.has_delayed_pc = true; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | void MacroJITx64Impl::Compile() { | ||
| 421 | MICROPROFILE_SCOPE(MacroJitCompile); | ||
| 422 | bool keep_executing = true; | ||
| 423 | labels.fill(Xbyak::Label()); | ||
| 424 | |||
| 425 | Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); | ||
| 426 | // JIT state | ||
| 427 | mov(STATE, Common::X64::ABI_PARAM1); | ||
| 428 | mov(PARAMETERS, Common::X64::ABI_PARAM2); | ||
| 429 | xor_(RESULT, RESULT); | ||
| 430 | xor_(METHOD_ADDRESS, METHOD_ADDRESS); | ||
| 431 | xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||
| 432 | |||
| 433 | mov(dword[STATE + offsetof(JITState, registers) + 4], Compile_FetchParameter()); | ||
| 434 | |||
| 435 | // Track get register for zero registers and mark it as no-op | ||
| 436 | optimizer.zero_reg_skip = true; | ||
| 437 | |||
| 438 | // AddImmediate tends to be used as a NOP instruction, if we detect this we can | ||
| 439 | // completely skip the entire code path and no emit anything | ||
| 440 | optimizer.skip_dummy_addimmediate = true; | ||
| 441 | |||
| 442 | // SMO tends to emit a lot of unnecessary method moves, we can mitigate this by only emitting | ||
| 443 | // one if our register isn't "dirty" | ||
| 444 | optimizer.optimize_for_method_move = true; | ||
| 445 | |||
| 446 | // Enable run-time assertions in JITted code | ||
| 447 | optimizer.enable_asserts = false; | ||
| 448 | |||
| 449 | // Check to see if we can skip emitting certain instructions | ||
| 450 | Optimizer_ScanFlags(); | ||
| 451 | |||
| 452 | const u32 op_count = static_cast<u32>(code.size()); | ||
| 453 | for (u32 i = 0; i < op_count; i++) { | ||
| 454 | if (i < op_count - 1) { | ||
| 455 | pc = i + 1; | ||
| 456 | next_opcode = GetOpCode(); | ||
| 457 | } else { | ||
| 458 | next_opcode = {}; | ||
| 459 | } | ||
| 460 | pc = i; | ||
| 461 | Compile_NextInstruction(); | ||
| 462 | } | ||
| 463 | |||
| 464 | L(end_of_code); | ||
| 465 | |||
| 466 | Common::X64::ABI_PopRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); | ||
| 467 | ret(); | ||
| 468 | ready(); | ||
| 469 | program = getCode<ProgramType>(); | ||
| 470 | } | ||
| 471 | |||
| 472 | bool MacroJITx64Impl::Compile_NextInstruction() { | ||
| 473 | const auto opcode = GetOpCode(); | ||
| 474 | if (labels[pc].getAddress()) { | ||
| 475 | return false; | ||
| 476 | } | ||
| 477 | |||
| 478 | L(labels[pc]); | ||
| 479 | |||
| 480 | switch (opcode.operation) { | ||
| 481 | case Macro::Operation::ALU: | ||
| 482 | Compile_ALU(opcode); | ||
| 483 | break; | ||
| 484 | case Macro::Operation::AddImmediate: | ||
| 485 | Compile_AddImmediate(opcode); | ||
| 486 | break; | ||
| 487 | case Macro::Operation::ExtractInsert: | ||
| 488 | Compile_ExtractInsert(opcode); | ||
| 489 | break; | ||
| 490 | case Macro::Operation::ExtractShiftLeftImmediate: | ||
| 491 | Compile_ExtractShiftLeftImmediate(opcode); | ||
| 492 | break; | ||
| 493 | case Macro::Operation::ExtractShiftLeftRegister: | ||
| 494 | Compile_ExtractShiftLeftRegister(opcode); | ||
| 495 | break; | ||
| 496 | case Macro::Operation::Read: | ||
| 497 | Compile_Read(opcode); | ||
| 498 | break; | ||
| 499 | case Macro::Operation::Branch: | ||
| 500 | Compile_Branch(opcode); | ||
| 501 | break; | ||
| 502 | default: | ||
| 503 | UNIMPLEMENTED_MSG("Unimplemented opcode {}", opcode.operation.Value()); | ||
| 504 | break; | ||
| 505 | } | ||
| 506 | |||
| 507 | if (optimizer.has_delayed_pc) { | ||
| 508 | if (opcode.is_exit) { | ||
| 509 | mov(rax, end_of_code); | ||
| 510 | test(BRANCH_HOLDER, BRANCH_HOLDER); | ||
| 511 | cmove(BRANCH_HOLDER, rax); | ||
| 512 | // Jump to next instruction to skip delay slot check | ||
| 513 | je(labels[pc + 1], T_NEAR); | ||
| 514 | } else { | ||
| 515 | // TODO(ogniK): Optimize delay slot branching | ||
| 516 | Xbyak::Label no_delay_slot{}; | ||
| 517 | test(BRANCH_HOLDER, BRANCH_HOLDER); | ||
| 518 | je(no_delay_slot, T_NEAR); | ||
| 519 | mov(rax, BRANCH_HOLDER); | ||
| 520 | xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||
| 521 | jmp(rax); | ||
| 522 | L(no_delay_slot); | ||
| 523 | } | ||
| 524 | L(delay_skip[pc]); | ||
| 525 | if (opcode.is_exit) { | ||
| 526 | return false; | ||
| 527 | } | ||
| 528 | } else { | ||
| 529 | test(BRANCH_HOLDER, BRANCH_HOLDER); | ||
| 530 | jne(end_of_code, T_NEAR); | ||
| 531 | if (opcode.is_exit) { | ||
| 532 | inc(BRANCH_HOLDER); | ||
| 533 | return false; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | return true; | ||
| 537 | } | ||
| 538 | |||
| 539 | Xbyak::Reg32 Tegra::MacroJITx64Impl::Compile_FetchParameter() { | ||
| 540 | mov(eax, dword[PARAMETERS]); | ||
| 541 | add(PARAMETERS, sizeof(u32)); | ||
| 542 | return eax; | ||
| 543 | } | ||
| 544 | |||
| 545 | Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) { | ||
| 546 | if (index == 0) { | ||
| 547 | // Register 0 is always zero | ||
| 548 | xor_(dst, dst); | ||
| 549 | } else { | ||
| 550 | mov(dst, dword[STATE + offsetof(JITState, registers) + index * sizeof(u32)]); | ||
| 551 | } | ||
| 552 | |||
| 553 | return dst; | ||
| 554 | } | ||
| 555 | |||
| 556 | void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) { | ||
| 557 | const auto SetRegister = [this](u32 reg, const Xbyak::Reg32& result) { | ||
| 558 | // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero | ||
| 559 | // register. | ||
| 560 | if (reg == 0) { | ||
| 561 | return; | ||
| 562 | } | ||
| 563 | mov(dword[STATE + offsetof(JITState, registers) + reg * sizeof(u32)], result); | ||
| 564 | }; | ||
| 565 | const auto SetMethodAddress = [this](const Xbyak::Reg32& reg) { mov(METHOD_ADDRESS, reg); }; | ||
| 566 | |||
| 567 | switch (operation) { | ||
| 568 | case Macro::ResultOperation::IgnoreAndFetch: | ||
| 569 | SetRegister(reg, Compile_FetchParameter()); | ||
| 570 | break; | ||
| 571 | case Macro::ResultOperation::Move: | ||
| 572 | SetRegister(reg, RESULT); | ||
| 573 | break; | ||
| 574 | case Macro::ResultOperation::MoveAndSetMethod: | ||
| 575 | SetRegister(reg, RESULT); | ||
| 576 | SetMethodAddress(RESULT); | ||
| 577 | break; | ||
| 578 | case Macro::ResultOperation::FetchAndSend: | ||
| 579 | // Fetch parameter and send result. | ||
| 580 | SetRegister(reg, Compile_FetchParameter()); | ||
| 581 | Compile_Send(RESULT); | ||
| 582 | break; | ||
| 583 | case Macro::ResultOperation::MoveAndSend: | ||
| 584 | // Move and send result. | ||
| 585 | SetRegister(reg, RESULT); | ||
| 586 | Compile_Send(RESULT); | ||
| 587 | break; | ||
| 588 | case Macro::ResultOperation::FetchAndSetMethod: | ||
| 589 | // Fetch parameter and use result as Method Address. | ||
| 590 | SetRegister(reg, Compile_FetchParameter()); | ||
| 591 | SetMethodAddress(RESULT); | ||
| 592 | break; | ||
| 593 | case Macro::ResultOperation::MoveAndSetMethodFetchAndSend: | ||
| 594 | // Move result and use as Method Address, then fetch and send parameter. | ||
| 595 | SetRegister(reg, RESULT); | ||
| 596 | SetMethodAddress(RESULT); | ||
| 597 | Compile_Send(Compile_FetchParameter()); | ||
| 598 | break; | ||
| 599 | case Macro::ResultOperation::MoveAndSetMethodSend: | ||
| 600 | // Move result and use as Method Address, then send bits 12:17 of result. | ||
| 601 | SetRegister(reg, RESULT); | ||
| 602 | SetMethodAddress(RESULT); | ||
| 603 | shr(RESULT, 12); | ||
| 604 | and_(RESULT, 0b111111); | ||
| 605 | Compile_Send(RESULT); | ||
| 606 | break; | ||
| 607 | default: | ||
| 608 | UNIMPLEMENTED_MSG("Unimplemented macro operation {}", static_cast<std::size_t>(operation)); | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | Macro::Opcode MacroJITx64Impl::GetOpCode() const { | ||
| 613 | ASSERT(pc < code.size()); | ||
| 614 | return {code[pc]}; | ||
| 615 | } | ||
| 616 | |||
| 617 | std::bitset<32> MacroJITx64Impl::PersistentCallerSavedRegs() const { | ||
| 618 | return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED; | ||
| 619 | } | ||
| 620 | |||
| 621 | } // namespace Tegra | ||
diff --git a/src/video_core/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h new file mode 100644 index 000000000..a180e7428 --- /dev/null +++ b/src/video_core/macro/macro_jit_x64.h | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <bitset> | ||
| 9 | #include <xbyak.h> | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/x64/xbyak_abi.h" | ||
| 13 | #include "video_core/macro/macro.h" | ||
| 14 | |||
| 15 | namespace Tegra { | ||
| 16 | |||
| 17 | namespace Engines { | ||
| 18 | class Maxwell3D; | ||
| 19 | } | ||
| 20 | |||
| 21 | /// MAX_CODE_SIZE is arbitrarily chosen based on current booting games | ||
| 22 | constexpr size_t MAX_CODE_SIZE = 0x10000; | ||
| 23 | |||
| 24 | class MacroJITx64 final : public MacroEngine { | ||
| 25 | public: | ||
| 26 | explicit MacroJITx64(Engines::Maxwell3D& maxwell3d); | ||
| 27 | |||
| 28 | protected: | ||
| 29 | std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | Engines::Maxwell3D& maxwell3d; | ||
| 33 | }; | ||
| 34 | |||
| 35 | class MacroJITx64Impl : public Xbyak::CodeGenerator, public CachedMacro { | ||
| 36 | public: | ||
| 37 | MacroJITx64Impl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code); | ||
| 38 | ~MacroJITx64Impl(); | ||
| 39 | |||
| 40 | void Execute(const std::vector<u32>& parameters, u32 method) override; | ||
| 41 | |||
| 42 | void Compile_ALU(Macro::Opcode opcode); | ||
| 43 | void Compile_AddImmediate(Macro::Opcode opcode); | ||
| 44 | void Compile_ExtractInsert(Macro::Opcode opcode); | ||
| 45 | void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode); | ||
| 46 | void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode); | ||
| 47 | void Compile_Read(Macro::Opcode opcode); | ||
| 48 | void Compile_Branch(Macro::Opcode opcode); | ||
| 49 | |||
| 50 | private: | ||
| 51 | void Optimizer_ScanFlags(); | ||
| 52 | |||
| 53 | void Compile(); | ||
| 54 | bool Compile_NextInstruction(); | ||
| 55 | |||
| 56 | Xbyak::Reg32 Compile_FetchParameter(); | ||
| 57 | Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst); | ||
| 58 | |||
| 59 | void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg); | ||
| 60 | void Compile_Send(Xbyak::Reg32 value); | ||
| 61 | |||
| 62 | Macro::Opcode GetOpCode() const; | ||
| 63 | std::bitset<32> PersistentCallerSavedRegs() const; | ||
| 64 | |||
| 65 | struct JITState { | ||
| 66 | Engines::Maxwell3D* maxwell3d{}; | ||
| 67 | std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{}; | ||
| 68 | u32 carry_flag{}; | ||
| 69 | }; | ||
| 70 | static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0"); | ||
| 71 | using ProgramType = void (*)(JITState*, const u32*); | ||
| 72 | |||
| 73 | struct OptimizerState { | ||
| 74 | bool can_skip_carry{}; | ||
| 75 | bool has_delayed_pc{}; | ||
| 76 | bool zero_reg_skip{}; | ||
| 77 | bool skip_dummy_addimmediate{}; | ||
| 78 | bool optimize_for_method_move{}; | ||
| 79 | bool enable_asserts{}; | ||
| 80 | }; | ||
| 81 | OptimizerState optimizer{}; | ||
| 82 | |||
| 83 | std::optional<Macro::Opcode> next_opcode{}; | ||
| 84 | ProgramType program{nullptr}; | ||
| 85 | |||
| 86 | std::array<Xbyak::Label, MAX_CODE_SIZE> labels; | ||
| 87 | std::array<Xbyak::Label, MAX_CODE_SIZE> delay_skip; | ||
| 88 | Xbyak::Label end_of_code{}; | ||
| 89 | |||
| 90 | bool is_delay_slot{}; | ||
| 91 | u32 pc{}; | ||
| 92 | std::optional<u32> delayed_pc; | ||
| 93 | |||
| 94 | const std::vector<u32>& code; | ||
| 95 | Engines::Maxwell3D& maxwell3d; | ||
| 96 | }; | ||
| 97 | |||
| 98 | } // namespace Tegra | ||
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index dbee9f634..ff5505d12 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -210,10 +210,11 @@ bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t si | |||
| 210 | return range == inner_size; | 210 | return range == inner_size; |
| 211 | } | 211 | } |
| 212 | 212 | ||
| 213 | void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const { | 213 | void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, |
| 214 | const std::size_t size) const { | ||
| 214 | std::size_t remaining_size{size}; | 215 | std::size_t remaining_size{size}; |
| 215 | std::size_t page_index{src_addr >> page_bits}; | 216 | std::size_t page_index{gpu_src_addr >> page_bits}; |
| 216 | std::size_t page_offset{src_addr & page_mask}; | 217 | std::size_t page_offset{gpu_src_addr & page_mask}; |
| 217 | 218 | ||
| 218 | auto& memory = system.Memory(); | 219 | auto& memory = system.Memory(); |
| 219 | 220 | ||
| @@ -234,11 +235,11 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s | |||
| 234 | } | 235 | } |
| 235 | } | 236 | } |
| 236 | 237 | ||
| 237 | void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, | 238 | void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, |
| 238 | const std::size_t size) const { | 239 | const std::size_t size) const { |
| 239 | std::size_t remaining_size{size}; | 240 | std::size_t remaining_size{size}; |
| 240 | std::size_t page_index{src_addr >> page_bits}; | 241 | std::size_t page_index{gpu_src_addr >> page_bits}; |
| 241 | std::size_t page_offset{src_addr & page_mask}; | 242 | std::size_t page_offset{gpu_src_addr & page_mask}; |
| 242 | 243 | ||
| 243 | auto& memory = system.Memory(); | 244 | auto& memory = system.Memory(); |
| 244 | 245 | ||
| @@ -259,10 +260,11 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, | |||
| 259 | } | 260 | } |
| 260 | } | 261 | } |
| 261 | 262 | ||
| 262 | void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size) { | 263 | void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, |
| 264 | const std::size_t size) { | ||
| 263 | std::size_t remaining_size{size}; | 265 | std::size_t remaining_size{size}; |
| 264 | std::size_t page_index{dest_addr >> page_bits}; | 266 | std::size_t page_index{gpu_dest_addr >> page_bits}; |
| 265 | std::size_t page_offset{dest_addr & page_mask}; | 267 | std::size_t page_offset{gpu_dest_addr & page_mask}; |
| 266 | 268 | ||
| 267 | auto& memory = system.Memory(); | 269 | auto& memory = system.Memory(); |
| 268 | 270 | ||
| @@ -283,11 +285,11 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const | |||
| 283 | } | 285 | } |
| 284 | } | 286 | } |
| 285 | 287 | ||
| 286 | void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, | 288 | void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, |
| 287 | const std::size_t size) { | 289 | const std::size_t size) { |
| 288 | std::size_t remaining_size{size}; | 290 | std::size_t remaining_size{size}; |
| 289 | std::size_t page_index{dest_addr >> page_bits}; | 291 | std::size_t page_index{gpu_dest_addr >> page_bits}; |
| 290 | std::size_t page_offset{dest_addr & page_mask}; | 292 | std::size_t page_offset{gpu_dest_addr & page_mask}; |
| 291 | 293 | ||
| 292 | auto& memory = system.Memory(); | 294 | auto& memory = system.Memory(); |
| 293 | 295 | ||
| @@ -306,16 +308,18 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, | |||
| 306 | } | 308 | } |
| 307 | } | 309 | } |
| 308 | 310 | ||
| 309 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { | 311 | void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, |
| 312 | const std::size_t size) { | ||
| 310 | std::vector<u8> tmp_buffer(size); | 313 | std::vector<u8> tmp_buffer(size); |
| 311 | ReadBlock(src_addr, tmp_buffer.data(), size); | 314 | ReadBlock(gpu_src_addr, tmp_buffer.data(), size); |
| 312 | WriteBlock(dest_addr, tmp_buffer.data(), size); | 315 | WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); |
| 313 | } | 316 | } |
| 314 | 317 | ||
| 315 | void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { | 318 | void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, |
| 319 | const std::size_t size) { | ||
| 316 | std::vector<u8> tmp_buffer(size); | 320 | std::vector<u8> tmp_buffer(size); |
| 317 | ReadBlockUnsafe(src_addr, tmp_buffer.data(), size); | 321 | ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size); |
| 318 | WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size); | 322 | WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); |
| 319 | } | 323 | } |
| 320 | 324 | ||
| 321 | bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { | 325 | bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { |
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 0ddd52d5a..87658e87a 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -79,9 +79,9 @@ public: | |||
| 79 | * in the Host Memory counterpart. Note: This functions cause Host GPU Memory | 79 | * in the Host Memory counterpart. Note: This functions cause Host GPU Memory |
| 80 | * Flushes and Invalidations, respectively to each operation. | 80 | * Flushes and Invalidations, respectively to each operation. |
| 81 | */ | 81 | */ |
| 82 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; | 82 | void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; |
| 83 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); | 83 | void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); |
| 84 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); | 84 | void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); |
| 85 | 85 | ||
| 86 | /** | 86 | /** |
| 87 | * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and | 87 | * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and |
| @@ -93,9 +93,9 @@ public: | |||
| 93 | * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture | 93 | * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture |
| 94 | * being flushed. | 94 | * being flushed. |
| 95 | */ | 95 | */ |
| 96 | void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; | 96 | void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; |
| 97 | void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); | 97 | void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); |
| 98 | void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); | 98 | void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); |
| 99 | 99 | ||
| 100 | /** | 100 | /** |
| 101 | * IsGranularRange checks if a gpu region can be simply read with a pointer | 101 | * IsGranularRange checks if a gpu region can be simply read with a pointer |
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index 2f75f8801..0d3a88765 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h | |||
| @@ -132,7 +132,7 @@ public: | |||
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | query->BindCounter(Stream(type).Current(), timestamp); | 134 | query->BindCounter(Stream(type).Current(), timestamp); |
| 135 | if (Settings::values.use_asynchronous_gpu_emulation) { | 135 | if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { |
| 136 | AsyncFlushQuery(cpu_addr); | 136 | AsyncFlushQuery(cpu_addr); |
| 137 | } | 137 | } |
| 138 | } | 138 | } |
| @@ -220,8 +220,8 @@ private: | |||
| 220 | return cache_begin < addr_end && addr_begin < cache_end; | 220 | return cache_begin < addr_end && addr_begin < cache_end; |
| 221 | }; | 221 | }; |
| 222 | 222 | ||
| 223 | const u64 page_end = addr_end >> PAGE_SHIFT; | 223 | const u64 page_end = addr_end >> PAGE_BITS; |
| 224 | for (u64 page = addr_begin >> PAGE_SHIFT; page <= page_end; ++page) { | 224 | for (u64 page = addr_begin >> PAGE_BITS; page <= page_end; ++page) { |
| 225 | const auto& it = cached_queries.find(page); | 225 | const auto& it = cached_queries.find(page); |
| 226 | if (it == std::end(cached_queries)) { | 226 | if (it == std::end(cached_queries)) { |
| 227 | continue; | 227 | continue; |
| @@ -242,14 +242,14 @@ private: | |||
| 242 | /// Registers the passed parameters as cached and returns a pointer to the stored cached query. | 242 | /// Registers the passed parameters as cached and returns a pointer to the stored cached query. |
| 243 | CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { | 243 | CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { |
| 244 | rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); | 244 | rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); |
| 245 | const u64 page = static_cast<u64>(cpu_addr) >> PAGE_SHIFT; | 245 | const u64 page = static_cast<u64>(cpu_addr) >> PAGE_BITS; |
| 246 | return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, | 246 | return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, |
| 247 | host_ptr); | 247 | host_ptr); |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | /// Tries to a get a cached query. Returns nullptr on failure. | 250 | /// Tries to a get a cached query. Returns nullptr on failure. |
| 251 | CachedQuery* TryGet(VAddr addr) { | 251 | CachedQuery* TryGet(VAddr addr) { |
| 252 | const u64 page = static_cast<u64>(addr) >> PAGE_SHIFT; | 252 | const u64 page = static_cast<u64>(addr) >> PAGE_BITS; |
| 253 | const auto it = cached_queries.find(page); | 253 | const auto it = cached_queries.find(page); |
| 254 | if (it == std::end(cached_queries)) { | 254 | if (it == std::end(cached_queries)) { |
| 255 | return nullptr; | 255 | return nullptr; |
| @@ -268,7 +268,7 @@ private: | |||
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | static constexpr std::uintptr_t PAGE_SIZE = 4096; | 270 | static constexpr std::uintptr_t PAGE_SIZE = 4096; |
| 271 | static constexpr unsigned PAGE_SHIFT = 12; | 271 | static constexpr unsigned PAGE_BITS = 12; |
| 272 | 272 | ||
| 273 | Core::System& system; | 273 | Core::System& system; |
| 274 | VideoCore::RasterizerInterface& rasterizer; | 274 | VideoCore::RasterizerInterface& rasterizer; |
diff --git a/src/video_core/rasterizer_cache.cpp b/src/video_core/rasterizer_cache.cpp deleted file mode 100644 index 093b2cdf4..000000000 --- a/src/video_core/rasterizer_cache.cpp +++ /dev/null | |||
| @@ -1,7 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/rasterizer_cache.h" | ||
| 6 | |||
| 7 | RasterizerCacheObject::~RasterizerCacheObject() = default; | ||
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h deleted file mode 100644 index 096ee337c..000000000 --- a/src/video_core/rasterizer_cache.h +++ /dev/null | |||
| @@ -1,253 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <mutex> | ||
| 8 | #include <set> | ||
| 9 | #include <unordered_map> | ||
| 10 | |||
| 11 | #include <boost/icl/interval_map.hpp> | ||
| 12 | #include <boost/range/iterator_range_core.hpp> | ||
| 13 | |||
| 14 | #include "common/common_types.h" | ||
| 15 | #include "core/settings.h" | ||
| 16 | #include "video_core/gpu.h" | ||
| 17 | #include "video_core/rasterizer_interface.h" | ||
| 18 | |||
| 19 | class RasterizerCacheObject { | ||
| 20 | public: | ||
| 21 | explicit RasterizerCacheObject(const VAddr cpu_addr) : cpu_addr{cpu_addr} {} | ||
| 22 | |||
| 23 | virtual ~RasterizerCacheObject(); | ||
| 24 | |||
| 25 | VAddr GetCpuAddr() const { | ||
| 26 | return cpu_addr; | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Gets the size of the shader in guest memory, required for cache management | ||
| 30 | virtual std::size_t GetSizeInBytes() const = 0; | ||
| 31 | |||
| 32 | /// Sets whether the cached object should be considered registered | ||
| 33 | void SetIsRegistered(bool registered) { | ||
| 34 | is_registered = registered; | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Returns true if the cached object is registered | ||
| 38 | bool IsRegistered() const { | ||
| 39 | return is_registered; | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Returns true if the cached object is dirty | ||
| 43 | bool IsDirty() const { | ||
| 44 | return is_dirty; | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Returns ticks from when this cached object was last modified | ||
| 48 | u64 GetLastModifiedTicks() const { | ||
| 49 | return last_modified_ticks; | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Marks an object as recently modified, used to specify whether it is clean or dirty | ||
| 53 | template <class T> | ||
| 54 | void MarkAsModified(bool dirty, T& cache) { | ||
| 55 | is_dirty = dirty; | ||
| 56 | last_modified_ticks = cache.GetModifiedTicks(); | ||
| 57 | } | ||
| 58 | |||
| 59 | void SetMemoryMarked(bool is_memory_marked_) { | ||
| 60 | is_memory_marked = is_memory_marked_; | ||
| 61 | } | ||
| 62 | |||
| 63 | bool IsMemoryMarked() const { | ||
| 64 | return is_memory_marked; | ||
| 65 | } | ||
| 66 | |||
| 67 | void SetSyncPending(bool is_sync_pending_) { | ||
| 68 | is_sync_pending = is_sync_pending_; | ||
| 69 | } | ||
| 70 | |||
| 71 | bool IsSyncPending() const { | ||
| 72 | return is_sync_pending; | ||
| 73 | } | ||
| 74 | |||
| 75 | private: | ||
| 76 | bool is_registered{}; ///< Whether the object is currently registered with the cache | ||
| 77 | bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory) | ||
| 78 | bool is_memory_marked{}; ///< Whether the object is marking rasterizer memory. | ||
| 79 | bool is_sync_pending{}; ///< Whether the object is pending deletion. | ||
| 80 | u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing | ||
| 81 | VAddr cpu_addr{}; ///< Cpu address memory, unique from emulated virtual address space | ||
| 82 | }; | ||
| 83 | |||
| 84 | template <class T> | ||
| 85 | class RasterizerCache : NonCopyable { | ||
| 86 | friend class RasterizerCacheObject; | ||
| 87 | |||
| 88 | public: | ||
| 89 | explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} | ||
| 90 | |||
| 91 | /// Write any cached resources overlapping the specified region back to memory | ||
| 92 | void FlushRegion(VAddr addr, std::size_t size) { | ||
| 93 | std::lock_guard lock{mutex}; | ||
| 94 | |||
| 95 | const auto& objects{GetSortedObjectsFromRegion(addr, size)}; | ||
| 96 | for (auto& object : objects) { | ||
| 97 | FlushObject(object); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Mark the specified region as being invalidated | ||
| 102 | void InvalidateRegion(VAddr addr, u64 size) { | ||
| 103 | std::lock_guard lock{mutex}; | ||
| 104 | |||
| 105 | const auto& objects{GetSortedObjectsFromRegion(addr, size)}; | ||
| 106 | for (auto& object : objects) { | ||
| 107 | if (!object->IsRegistered()) { | ||
| 108 | // Skip duplicates | ||
| 109 | continue; | ||
| 110 | } | ||
| 111 | Unregister(object); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | void OnCPUWrite(VAddr addr, std::size_t size) { | ||
| 116 | std::lock_guard lock{mutex}; | ||
| 117 | |||
| 118 | for (const auto& object : GetSortedObjectsFromRegion(addr, size)) { | ||
| 119 | if (object->IsRegistered()) { | ||
| 120 | UnmarkMemory(object); | ||
| 121 | object->SetSyncPending(true); | ||
| 122 | marked_for_unregister.emplace_back(object); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | void SyncGuestHost() { | ||
| 128 | std::lock_guard lock{mutex}; | ||
| 129 | |||
| 130 | for (const auto& object : marked_for_unregister) { | ||
| 131 | if (object->IsRegistered()) { | ||
| 132 | object->SetSyncPending(false); | ||
| 133 | Unregister(object); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | marked_for_unregister.clear(); | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Invalidates everything in the cache | ||
| 140 | void InvalidateAll() { | ||
| 141 | std::lock_guard lock{mutex}; | ||
| 142 | |||
| 143 | while (interval_cache.begin() != interval_cache.end()) { | ||
| 144 | Unregister(*interval_cache.begin()->second.begin()); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | protected: | ||
| 149 | /// Tries to get an object from the cache with the specified cache address | ||
| 150 | T TryGet(VAddr addr) const { | ||
| 151 | const auto iter = map_cache.find(addr); | ||
| 152 | if (iter != map_cache.end()) | ||
| 153 | return iter->second; | ||
| 154 | return nullptr; | ||
| 155 | } | ||
| 156 | |||
| 157 | /// Register an object into the cache | ||
| 158 | virtual void Register(const T& object) { | ||
| 159 | std::lock_guard lock{mutex}; | ||
| 160 | |||
| 161 | object->SetIsRegistered(true); | ||
| 162 | interval_cache.add({GetInterval(object), ObjectSet{object}}); | ||
| 163 | map_cache.insert({object->GetCpuAddr(), object}); | ||
| 164 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), 1); | ||
| 165 | object->SetMemoryMarked(true); | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Unregisters an object from the cache | ||
| 169 | virtual void Unregister(const T& object) { | ||
| 170 | std::lock_guard lock{mutex}; | ||
| 171 | |||
| 172 | UnmarkMemory(object); | ||
| 173 | object->SetIsRegistered(false); | ||
| 174 | if (object->IsSyncPending()) { | ||
| 175 | marked_for_unregister.remove(object); | ||
| 176 | object->SetSyncPending(false); | ||
| 177 | } | ||
| 178 | const VAddr addr = object->GetCpuAddr(); | ||
| 179 | interval_cache.subtract({GetInterval(object), ObjectSet{object}}); | ||
| 180 | map_cache.erase(addr); | ||
| 181 | } | ||
| 182 | |||
| 183 | void UnmarkMemory(const T& object) { | ||
| 184 | if (!object->IsMemoryMarked()) { | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1); | ||
| 188 | object->SetMemoryMarked(false); | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Returns a ticks counter used for tracking when cached objects were last modified | ||
| 192 | u64 GetModifiedTicks() { | ||
| 193 | std::lock_guard lock{mutex}; | ||
| 194 | |||
| 195 | return ++modified_ticks; | ||
| 196 | } | ||
| 197 | |||
| 198 | virtual void FlushObjectInner(const T& object) = 0; | ||
| 199 | |||
| 200 | /// Flushes the specified object, updating appropriate cache state as needed | ||
| 201 | void FlushObject(const T& object) { | ||
| 202 | std::lock_guard lock{mutex}; | ||
| 203 | |||
| 204 | if (!object->IsDirty()) { | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | FlushObjectInner(object); | ||
| 208 | object->MarkAsModified(false, *this); | ||
| 209 | } | ||
| 210 | |||
| 211 | std::recursive_mutex mutex; | ||
| 212 | |||
| 213 | private: | ||
| 214 | /// Returns a list of cached objects from the specified memory region, ordered by access time | ||
| 215 | std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) { | ||
| 216 | if (size == 0) { | ||
| 217 | return {}; | ||
| 218 | } | ||
| 219 | |||
| 220 | std::vector<T> objects; | ||
| 221 | const ObjectInterval interval{addr, addr + size}; | ||
| 222 | for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) { | ||
| 223 | for (auto& cached_object : pair.second) { | ||
| 224 | if (!cached_object) { | ||
| 225 | continue; | ||
| 226 | } | ||
| 227 | objects.push_back(cached_object); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool { | ||
| 232 | return a->GetLastModifiedTicks() < b->GetLastModifiedTicks(); | ||
| 233 | }); | ||
| 234 | |||
| 235 | return objects; | ||
| 236 | } | ||
| 237 | |||
| 238 | using ObjectSet = std::set<T>; | ||
| 239 | using ObjectCache = std::unordered_map<VAddr, T>; | ||
| 240 | using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>; | ||
| 241 | using ObjectInterval = typename IntervalCache::interval_type; | ||
| 242 | |||
| 243 | static auto GetInterval(const T& object) { | ||
| 244 | return ObjectInterval::right_open(object->GetCpuAddr(), | ||
| 245 | object->GetCpuAddr() + object->GetSizeInBytes()); | ||
| 246 | } | ||
| 247 | |||
| 248 | ObjectCache map_cache; | ||
| 249 | IntervalCache interval_cache; ///< Cache of objects | ||
| 250 | u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing | ||
| 251 | VideoCore::RasterizerInterface& rasterizer; | ||
| 252 | std::list<T> marked_for_unregister; | ||
| 253 | }; | ||
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 919d1f2d4..dfb06e87e 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp | |||
| @@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default; | |||
| 18 | void RendererBase::RefreshBaseSettings() { | 18 | void RendererBase::RefreshBaseSettings() { |
| 19 | UpdateCurrentFramebufferLayout(); | 19 | UpdateCurrentFramebufferLayout(); |
| 20 | 20 | ||
| 21 | renderer_settings.use_framelimiter = Settings::values.use_frame_limit; | 21 | renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue(); |
| 22 | renderer_settings.set_background_color = true; | 22 | renderer_settings.set_background_color = true; |
| 23 | } | 23 | } |
| 24 | 24 | ||
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp new file mode 100644 index 000000000..eb5158407 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp | |||
| @@ -0,0 +1,2073 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <cstddef> | ||
| 8 | #include <string> | ||
| 9 | #include <string_view> | ||
| 10 | #include <utility> | ||
| 11 | #include <variant> | ||
| 12 | |||
| 13 | #include <fmt/format.h> | ||
| 14 | |||
| 15 | #include "common/alignment.h" | ||
| 16 | #include "common/assert.h" | ||
| 17 | #include "common/common_types.h" | ||
| 18 | #include "video_core/renderer_opengl/gl_arb_decompiler.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_device.h" | ||
| 20 | #include "video_core/shader/registry.h" | ||
| 21 | #include "video_core/shader/shader_ir.h" | ||
| 22 | |||
| 23 | // Predicates in the decompiled code follow the convention that -1 means true and 0 means false. | ||
| 24 | // GLASM lacks booleans, so they have to be implemented as integers. | ||
| 25 | // Using -1 for true is useful because both CMP.S and NOT.U can negate it, and CMP.S can be used to | ||
| 26 | // select between two values, because -1 will be evaluated as true and 0 as false. | ||
| 27 | |||
| 28 | namespace OpenGL { | ||
| 29 | |||
| 30 | namespace { | ||
| 31 | |||
| 32 | using Tegra::Engines::ShaderType; | ||
| 33 | using Tegra::Shader::Attribute; | ||
| 34 | using Tegra::Shader::PixelImap; | ||
| 35 | using Tegra::Shader::Register; | ||
| 36 | using namespace VideoCommon::Shader; | ||
| 37 | using Operation = const OperationNode&; | ||
| 38 | |||
| 39 | constexpr std::array INTERNAL_FLAG_NAMES = {"ZERO", "SIGN", "CARRY", "OVERFLOW"}; | ||
| 40 | |||
| 41 | char Swizzle(std::size_t component) { | ||
| 42 | ASSERT(component < 4); | ||
| 43 | return component["xyzw"]; | ||
| 44 | } | ||
| 45 | |||
| 46 | constexpr bool IsGenericAttribute(Attribute::Index index) { | ||
| 47 | return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31; | ||
| 48 | } | ||
| 49 | |||
| 50 | u32 GetGenericAttributeIndex(Attribute::Index index) { | ||
| 51 | ASSERT(IsGenericAttribute(index)); | ||
| 52 | return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0); | ||
| 53 | } | ||
| 54 | |||
| 55 | std::string_view Modifiers(Operation operation) { | ||
| 56 | const auto meta = std::get_if<MetaArithmetic>(&operation.GetMeta()); | ||
| 57 | if (meta && meta->precise) { | ||
| 58 | return ".PREC"; | ||
| 59 | } | ||
| 60 | return ""; | ||
| 61 | } | ||
| 62 | |||
| 63 | std::string_view GetInputFlags(PixelImap attribute) { | ||
| 64 | switch (attribute) { | ||
| 65 | case PixelImap::Perspective: | ||
| 66 | return ""; | ||
| 67 | case PixelImap::Constant: | ||
| 68 | return "FLAT "; | ||
| 69 | case PixelImap::ScreenLinear: | ||
| 70 | return "NOPERSPECTIVE "; | ||
| 71 | case PixelImap::Unused: | ||
| 72 | break; | ||
| 73 | } | ||
| 74 | UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<int>(attribute)); | ||
| 75 | return {}; | ||
| 76 | } | ||
| 77 | |||
| 78 | std::string_view ImageType(Tegra::Shader::ImageType image_type) { | ||
| 79 | switch (image_type) { | ||
| 80 | case Tegra::Shader::ImageType::Texture1D: | ||
| 81 | return "1D"; | ||
| 82 | case Tegra::Shader::ImageType::TextureBuffer: | ||
| 83 | return "BUFFER"; | ||
| 84 | case Tegra::Shader::ImageType::Texture1DArray: | ||
| 85 | return "ARRAY1D"; | ||
| 86 | case Tegra::Shader::ImageType::Texture2D: | ||
| 87 | return "2D"; | ||
| 88 | case Tegra::Shader::ImageType::Texture2DArray: | ||
| 89 | return "ARRAY2D"; | ||
| 90 | case Tegra::Shader::ImageType::Texture3D: | ||
| 91 | return "3D"; | ||
| 92 | } | ||
| 93 | UNREACHABLE(); | ||
| 94 | return {}; | ||
| 95 | } | ||
| 96 | |||
| 97 | std::string_view StackName(MetaStackClass stack) { | ||
| 98 | switch (stack) { | ||
| 99 | case MetaStackClass::Ssy: | ||
| 100 | return "SSY"; | ||
| 101 | case MetaStackClass::Pbk: | ||
| 102 | return "PBK"; | ||
| 103 | } | ||
| 104 | UNREACHABLE(); | ||
| 105 | return ""; | ||
| 106 | }; | ||
| 107 | |||
| 108 | std::string_view PrimitiveDescription(Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology topology) { | ||
| 109 | switch (topology) { | ||
| 110 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::Points: | ||
| 111 | return "POINTS"; | ||
| 112 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::Lines: | ||
| 113 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::LineStrip: | ||
| 114 | return "LINES"; | ||
| 115 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency: | ||
| 116 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency: | ||
| 117 | return "LINES_ADJACENCY"; | ||
| 118 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::Triangles: | ||
| 119 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TriangleStrip: | ||
| 120 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TriangleFan: | ||
| 121 | return "TRIANGLES"; | ||
| 122 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency: | ||
| 123 | case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency: | ||
| 124 | return "TRIANGLES_ADJACENCY"; | ||
| 125 | default: | ||
| 126 | UNIMPLEMENTED_MSG("topology={}", static_cast<int>(topology)); | ||
| 127 | return "POINTS"; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | std::string_view TopologyName(Tegra::Shader::OutputTopology topology) { | ||
| 132 | switch (topology) { | ||
| 133 | case Tegra::Shader::OutputTopology::PointList: | ||
| 134 | return "POINTS"; | ||
| 135 | case Tegra::Shader::OutputTopology::LineStrip: | ||
| 136 | return "LINE_STRIP"; | ||
| 137 | case Tegra::Shader::OutputTopology::TriangleStrip: | ||
| 138 | return "TRIANGLE_STRIP"; | ||
| 139 | default: | ||
| 140 | UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology)); | ||
| 141 | return "points"; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | std::string_view StageInputName(ShaderType stage) { | ||
| 146 | switch (stage) { | ||
| 147 | case ShaderType::Vertex: | ||
| 148 | case ShaderType::Geometry: | ||
| 149 | return "vertex"; | ||
| 150 | case ShaderType::Fragment: | ||
| 151 | return "fragment"; | ||
| 152 | case ShaderType::Compute: | ||
| 153 | return "invocation"; | ||
| 154 | default: | ||
| 155 | UNREACHABLE(); | ||
| 156 | return ""; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | std::string TextureType(const MetaTexture& meta) { | ||
| 161 | if (meta.sampler.is_buffer) { | ||
| 162 | return "BUFFER"; | ||
| 163 | } | ||
| 164 | std::string type; | ||
| 165 | if (meta.sampler.is_shadow) { | ||
| 166 | type += "SHADOW"; | ||
| 167 | } | ||
| 168 | if (meta.sampler.is_array) { | ||
| 169 | type += "ARRAY"; | ||
| 170 | } | ||
| 171 | type += [&meta] { | ||
| 172 | switch (meta.sampler.type) { | ||
| 173 | case Tegra::Shader::TextureType::Texture1D: | ||
| 174 | return "1D"; | ||
| 175 | case Tegra::Shader::TextureType::Texture2D: | ||
| 176 | return "2D"; | ||
| 177 | case Tegra::Shader::TextureType::Texture3D: | ||
| 178 | return "3D"; | ||
| 179 | case Tegra::Shader::TextureType::TextureCube: | ||
| 180 | return "CUBE"; | ||
| 181 | } | ||
| 182 | UNREACHABLE(); | ||
| 183 | return "2D"; | ||
| 184 | }(); | ||
| 185 | return type; | ||
| 186 | } | ||
| 187 | |||
| 188 | std::string GlobalMemoryName(const GlobalMemoryBase& base) { | ||
| 189 | return fmt::format("gmem{}_{}", base.cbuf_index, base.cbuf_offset); | ||
| 190 | } | ||
| 191 | |||
| 192 | class ARBDecompiler final { | ||
| 193 | public: | ||
| 194 | explicit ARBDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry, | ||
| 195 | ShaderType stage, std::string_view identifier); | ||
| 196 | |||
| 197 | std::string Code() const { | ||
| 198 | return shader_source; | ||
| 199 | } | ||
| 200 | |||
| 201 | private: | ||
| 202 | void DeclareHeader(); | ||
| 203 | void DeclareVertex(); | ||
| 204 | void DeclareGeometry(); | ||
| 205 | void DeclareFragment(); | ||
| 206 | void DeclareCompute(); | ||
| 207 | void DeclareInputAttributes(); | ||
| 208 | void DeclareOutputAttributes(); | ||
| 209 | void DeclareLocalMemory(); | ||
| 210 | void DeclareGlobalMemory(); | ||
| 211 | void DeclareConstantBuffers(); | ||
| 212 | void DeclareRegisters(); | ||
| 213 | void DeclareTemporaries(); | ||
| 214 | void DeclarePredicates(); | ||
| 215 | void DeclareInternalFlags(); | ||
| 216 | |||
| 217 | void InitializeVariables(); | ||
| 218 | |||
| 219 | void DecompileAST(); | ||
| 220 | void DecompileBranchMode(); | ||
| 221 | |||
| 222 | void VisitAST(const ASTNode& node); | ||
| 223 | std::string VisitExpression(const Expr& node); | ||
| 224 | |||
| 225 | void VisitBlock(const NodeBlock& bb); | ||
| 226 | |||
| 227 | std::string Visit(const Node& node); | ||
| 228 | |||
| 229 | std::pair<std::string, std::size_t> BuildCoords(Operation); | ||
| 230 | std::string BuildAoffi(Operation); | ||
| 231 | void Exit(); | ||
| 232 | |||
| 233 | std::string Assign(Operation); | ||
| 234 | std::string Select(Operation); | ||
| 235 | std::string FClamp(Operation); | ||
| 236 | std::string FCastHalf0(Operation); | ||
| 237 | std::string FCastHalf1(Operation); | ||
| 238 | std::string FSqrt(Operation); | ||
| 239 | std::string FSwizzleAdd(Operation); | ||
| 240 | std::string HAdd2(Operation); | ||
| 241 | std::string HMul2(Operation); | ||
| 242 | std::string HFma2(Operation); | ||
| 243 | std::string HAbsolute(Operation); | ||
| 244 | std::string HNegate(Operation); | ||
| 245 | std::string HClamp(Operation); | ||
| 246 | std::string HCastFloat(Operation); | ||
| 247 | std::string HUnpack(Operation); | ||
| 248 | std::string HMergeF32(Operation); | ||
| 249 | std::string HMergeH0(Operation); | ||
| 250 | std::string HMergeH1(Operation); | ||
| 251 | std::string HPack2(Operation); | ||
| 252 | std::string LogicalAssign(Operation); | ||
| 253 | std::string LogicalPick2(Operation); | ||
| 254 | std::string LogicalAnd2(Operation); | ||
| 255 | std::string FloatOrdered(Operation); | ||
| 256 | std::string FloatUnordered(Operation); | ||
| 257 | std::string LogicalAddCarry(Operation); | ||
| 258 | std::string Texture(Operation); | ||
| 259 | std::string TextureGather(Operation); | ||
| 260 | std::string TextureQueryDimensions(Operation); | ||
| 261 | std::string TextureQueryLod(Operation); | ||
| 262 | std::string TexelFetch(Operation); | ||
| 263 | std::string TextureGradient(Operation); | ||
| 264 | std::string ImageLoad(Operation); | ||
| 265 | std::string ImageStore(Operation); | ||
| 266 | std::string Branch(Operation); | ||
| 267 | std::string BranchIndirect(Operation); | ||
| 268 | std::string PushFlowStack(Operation); | ||
| 269 | std::string PopFlowStack(Operation); | ||
| 270 | std::string Exit(Operation); | ||
| 271 | std::string Discard(Operation); | ||
| 272 | std::string EmitVertex(Operation); | ||
| 273 | std::string EndPrimitive(Operation); | ||
| 274 | std::string InvocationId(Operation); | ||
| 275 | std::string YNegate(Operation); | ||
| 276 | std::string ThreadId(Operation); | ||
| 277 | std::string ShuffleIndexed(Operation); | ||
| 278 | std::string Barrier(Operation); | ||
| 279 | std::string MemoryBarrierGroup(Operation); | ||
| 280 | std::string MemoryBarrierGlobal(Operation); | ||
| 281 | |||
| 282 | template <const std::string_view& op> | ||
| 283 | std::string Unary(Operation operation) { | ||
| 284 | std::string temporary = AllocTemporary(); | ||
| 285 | AddLine("{}{} {}, {};", op, Modifiers(operation), temporary, Visit(operation[0])); | ||
| 286 | return temporary; | ||
| 287 | } | ||
| 288 | |||
| 289 | template <const std::string_view& op> | ||
| 290 | std::string Binary(Operation operation) { | ||
| 291 | std::string temporary = AllocTemporary(); | ||
| 292 | AddLine("{}{} {}, {}, {};", op, Modifiers(operation), temporary, Visit(operation[0]), | ||
| 293 | Visit(operation[1])); | ||
| 294 | return temporary; | ||
| 295 | } | ||
| 296 | |||
| 297 | template <const std::string_view& op> | ||
| 298 | std::string Trinary(Operation operation) { | ||
| 299 | std::string temporary = AllocTemporary(); | ||
| 300 | AddLine("{}{} {}, {}, {}, {};", op, Modifiers(operation), temporary, Visit(operation[0]), | ||
| 301 | Visit(operation[1]), Visit(operation[2])); | ||
| 302 | return temporary; | ||
| 303 | } | ||
| 304 | |||
| 305 | template <const std::string_view& op, bool unordered> | ||
| 306 | std::string FloatComparison(Operation operation) { | ||
| 307 | std::string temporary = AllocTemporary(); | ||
| 308 | AddLine("TRUNC.U.CC RC.x, {};", Binary<op>(operation)); | ||
| 309 | AddLine("MOV.S {}, 0;", temporary); | ||
| 310 | AddLine("MOV.S {} (NE.x), -1;", temporary); | ||
| 311 | |||
| 312 | const std::string op_a = Visit(operation[0]); | ||
| 313 | const std::string op_b = Visit(operation[1]); | ||
| 314 | if constexpr (unordered) { | ||
| 315 | AddLine("SNE.F RC.x, {}, {};", op_a, op_a); | ||
| 316 | AddLine("TRUNC.U.CC RC.x, RC.x;"); | ||
| 317 | AddLine("MOV.S {} (NE.x), -1;", temporary); | ||
| 318 | AddLine("SNE.F RC.x, {}, {};", op_b, op_b); | ||
| 319 | AddLine("TRUNC.U.CC RC.x, RC.x;"); | ||
| 320 | AddLine("MOV.S {} (NE.x), -1;", temporary); | ||
| 321 | } else if (op == SNE_F) { | ||
| 322 | AddLine("SNE.F RC.x, {}, {};", op_a, op_a); | ||
| 323 | AddLine("TRUNC.U.CC RC.x, RC.x;"); | ||
| 324 | AddLine("MOV.S {} (NE.x), 0;", temporary); | ||
| 325 | AddLine("SNE.F RC.x, {}, {};", op_b, op_b); | ||
| 326 | AddLine("TRUNC.U.CC RC.x, RC.x;"); | ||
| 327 | AddLine("MOV.S {} (NE.x), 0;", temporary); | ||
| 328 | } | ||
| 329 | return temporary; | ||
| 330 | } | ||
| 331 | |||
| 332 | template <const std::string_view& op, bool is_nan> | ||
| 333 | std::string HalfComparison(Operation operation) { | ||
| 334 | std::string tmp1 = AllocVectorTemporary(); | ||
| 335 | const std::string tmp2 = AllocVectorTemporary(); | ||
| 336 | const std::string op_a = Visit(operation[0]); | ||
| 337 | const std::string op_b = Visit(operation[1]); | ||
| 338 | AddLine("UP2H.F {}, {};", tmp1, op_a); | ||
| 339 | AddLine("UP2H.F {}, {};", tmp2, op_b); | ||
| 340 | AddLine("{} {}, {}, {};", op, tmp1, tmp1, tmp2); | ||
| 341 | AddLine("TRUNC.U.CC RC.xy, {};", tmp1); | ||
| 342 | AddLine("MOV.S {}.xy, {{0, 0, 0, 0}};", tmp1); | ||
| 343 | AddLine("MOV.S {}.x (NE.x), -1;", tmp1); | ||
| 344 | AddLine("MOV.S {}.y (NE.y), -1;", tmp1); | ||
| 345 | if constexpr (is_nan) { | ||
| 346 | AddLine("MOVC.F RC.x, {};", op_a); | ||
| 347 | AddLine("MOV.S {}.x (NAN.x), -1;", tmp1); | ||
| 348 | AddLine("MOVC.F RC.x, {};", op_b); | ||
| 349 | AddLine("MOV.S {}.y (NAN.x), -1;", tmp1); | ||
| 350 | } | ||
| 351 | return tmp1; | ||
| 352 | } | ||
| 353 | |||
| 354 | template <const std::string_view& op, const std::string_view& type> | ||
| 355 | std::string AtomicImage(Operation operation) { | ||
| 356 | const auto& meta = std::get<MetaImage>(operation.GetMeta()); | ||
| 357 | const u32 image_id = device.GetBaseBindings(stage).image + meta.image.index; | ||
| 358 | const std::size_t num_coords = operation.GetOperandsCount(); | ||
| 359 | const std::size_t num_values = meta.values.size(); | ||
| 360 | |||
| 361 | const std::string coord = AllocVectorTemporary(); | ||
| 362 | const std::string value = AllocVectorTemporary(); | ||
| 363 | for (std::size_t i = 0; i < num_coords; ++i) { | ||
| 364 | AddLine("MOV.S {}.{}, {};", coord, Swizzle(i), Visit(operation[i])); | ||
| 365 | } | ||
| 366 | for (std::size_t i = 0; i < num_values; ++i) { | ||
| 367 | AddLine("MOV.F {}.{}, {};", value, Swizzle(i), Visit(meta.values[i])); | ||
| 368 | } | ||
| 369 | |||
| 370 | AddLine("ATOMIM.{}.{} {}.x, {}, {}, image[{}], {};", op, type, coord, value, coord, | ||
| 371 | image_id, ImageType(meta.image.type)); | ||
| 372 | return fmt::format("{}.x", coord); | ||
| 373 | } | ||
| 374 | |||
| 375 | template <const std::string_view& op, const std::string_view& type> | ||
| 376 | std::string Atomic(Operation operation) { | ||
| 377 | std::string temporary = AllocTemporary(); | ||
| 378 | std::string address; | ||
| 379 | std::string_view opname; | ||
| 380 | if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) { | ||
| 381 | AddLine("SUB.U {}, {}, {};", temporary, Visit(gmem->GetRealAddress()), | ||
| 382 | Visit(gmem->GetBaseAddress())); | ||
| 383 | address = fmt::format("{}[{}]", GlobalMemoryName(gmem->GetDescriptor()), temporary); | ||
| 384 | opname = "ATOMB"; | ||
| 385 | } else if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { | ||
| 386 | address = fmt::format("shared_mem[{}]", Visit(smem->GetAddress())); | ||
| 387 | opname = "ATOMS"; | ||
| 388 | } else { | ||
| 389 | UNREACHABLE(); | ||
| 390 | return "{0, 0, 0, 0}"; | ||
| 391 | } | ||
| 392 | AddLine("{}.{}.{} {}, {}, {};", opname, op, type, temporary, Visit(operation[1]), address); | ||
| 393 | return temporary; | ||
| 394 | } | ||
| 395 | |||
| 396 | template <char type> | ||
| 397 | std::string Negate(Operation operation) { | ||
| 398 | std::string temporary = AllocTemporary(); | ||
| 399 | if constexpr (type == 'F') { | ||
| 400 | AddLine("MOV.F32 {}, -{};", temporary, Visit(operation[0])); | ||
| 401 | } else { | ||
| 402 | AddLine("MOV.{} {}, -{};", type, temporary, Visit(operation[0])); | ||
| 403 | } | ||
| 404 | return temporary; | ||
| 405 | } | ||
| 406 | |||
| 407 | template <char type> | ||
| 408 | std::string Absolute(Operation operation) { | ||
| 409 | std::string temporary = AllocTemporary(); | ||
| 410 | AddLine("MOV.{} {}, |{}|;", type, temporary, Visit(operation[0])); | ||
| 411 | return temporary; | ||
| 412 | } | ||
| 413 | |||
| 414 | template <char type> | ||
| 415 | std::string BitfieldInsert(Operation operation) { | ||
| 416 | const std::string temporary = AllocVectorTemporary(); | ||
| 417 | AddLine("MOV.{} {}.x, {};", type, temporary, Visit(operation[3])); | ||
| 418 | AddLine("MOV.{} {}.y, {};", type, temporary, Visit(operation[2])); | ||
| 419 | AddLine("BFI.{} {}.x, {}, {}, {};", type, temporary, temporary, Visit(operation[1]), | ||
| 420 | Visit(operation[0])); | ||
| 421 | return fmt::format("{}.x", temporary); | ||
| 422 | } | ||
| 423 | |||
| 424 | template <char type> | ||
| 425 | std::string BitfieldExtract(Operation operation) { | ||
| 426 | const std::string temporary = AllocVectorTemporary(); | ||
| 427 | AddLine("MOV.{} {}.x, {};", type, temporary, Visit(operation[2])); | ||
| 428 | AddLine("MOV.{} {}.y, {};", type, temporary, Visit(operation[1])); | ||
| 429 | AddLine("BFE.{} {}.x, {}, {};", type, temporary, temporary, Visit(operation[0])); | ||
| 430 | return fmt::format("{}.x", temporary); | ||
| 431 | } | ||
| 432 | |||
| 433 | template <char swizzle> | ||
| 434 | std::string LocalInvocationId(Operation) { | ||
| 435 | return fmt::format("invocation.localid.{}", swizzle); | ||
| 436 | } | ||
| 437 | |||
| 438 | template <char swizzle> | ||
| 439 | std::string WorkGroupId(Operation) { | ||
| 440 | return fmt::format("invocation.groupid.{}", swizzle); | ||
| 441 | } | ||
| 442 | |||
| 443 | template <char c1, char c2> | ||
| 444 | std::string ThreadMask(Operation) { | ||
| 445 | return fmt::format("{}.thread{}{}mask", StageInputName(stage), c1, c2); | ||
| 446 | } | ||
| 447 | |||
| 448 | template <typename... Args> | ||
| 449 | void AddExpression(std::string_view text, Args&&... args) { | ||
| 450 | shader_source += fmt::format(text, std::forward<Args>(args)...); | ||
| 451 | } | ||
| 452 | |||
| 453 | template <typename... Args> | ||
| 454 | void AddLine(std::string_view text, Args&&... args) { | ||
| 455 | AddExpression(text, std::forward<Args>(args)...); | ||
| 456 | shader_source += '\n'; | ||
| 457 | } | ||
| 458 | |||
| 459 | std::string AllocTemporary() { | ||
| 460 | max_temporaries = std::max(max_temporaries, num_temporaries + 1); | ||
| 461 | return fmt::format("T{}.x", num_temporaries++); | ||
| 462 | } | ||
| 463 | |||
| 464 | std::string AllocVectorTemporary() { | ||
| 465 | max_temporaries = std::max(max_temporaries, num_temporaries + 1); | ||
| 466 | return fmt::format("T{}", num_temporaries++); | ||
| 467 | } | ||
| 468 | |||
| 469 | void ResetTemporaries() noexcept { | ||
| 470 | num_temporaries = 0; | ||
| 471 | } | ||
| 472 | |||
| 473 | const Device& device; | ||
| 474 | const ShaderIR& ir; | ||
| 475 | const Registry& registry; | ||
| 476 | const ShaderType stage; | ||
| 477 | |||
| 478 | std::size_t num_temporaries = 0; | ||
| 479 | std::size_t max_temporaries = 0; | ||
| 480 | |||
| 481 | std::string shader_source; | ||
| 482 | |||
| 483 | static constexpr std::string_view ADD_F32 = "ADD.F32"; | ||
| 484 | static constexpr std::string_view ADD_S = "ADD.S"; | ||
| 485 | static constexpr std::string_view ADD_U = "ADD.U"; | ||
| 486 | static constexpr std::string_view MUL_F32 = "MUL.F32"; | ||
| 487 | static constexpr std::string_view MUL_S = "MUL.S"; | ||
| 488 | static constexpr std::string_view MUL_U = "MUL.U"; | ||
| 489 | static constexpr std::string_view DIV_F32 = "DIV.F32"; | ||
| 490 | static constexpr std::string_view DIV_S = "DIV.S"; | ||
| 491 | static constexpr std::string_view DIV_U = "DIV.U"; | ||
| 492 | static constexpr std::string_view MAD_F32 = "MAD.F32"; | ||
| 493 | static constexpr std::string_view RSQ_F32 = "RSQ.F32"; | ||
| 494 | static constexpr std::string_view COS_F32 = "COS.F32"; | ||
| 495 | static constexpr std::string_view SIN_F32 = "SIN.F32"; | ||
| 496 | static constexpr std::string_view EX2_F32 = "EX2.F32"; | ||
| 497 | static constexpr std::string_view LG2_F32 = "LG2.F32"; | ||
| 498 | static constexpr std::string_view SLT_F = "SLT.F32"; | ||
| 499 | static constexpr std::string_view SLT_S = "SLT.S"; | ||
| 500 | static constexpr std::string_view SLT_U = "SLT.U"; | ||
| 501 | static constexpr std::string_view SEQ_F = "SEQ.F32"; | ||
| 502 | static constexpr std::string_view SEQ_S = "SEQ.S"; | ||
| 503 | static constexpr std::string_view SEQ_U = "SEQ.U"; | ||
| 504 | static constexpr std::string_view SLE_F = "SLE.F32"; | ||
| 505 | static constexpr std::string_view SLE_S = "SLE.S"; | ||
| 506 | static constexpr std::string_view SLE_U = "SLE.U"; | ||
| 507 | static constexpr std::string_view SGT_F = "SGT.F32"; | ||
| 508 | static constexpr std::string_view SGT_S = "SGT.S"; | ||
| 509 | static constexpr std::string_view SGT_U = "SGT.U"; | ||
| 510 | static constexpr std::string_view SNE_F = "SNE.F32"; | ||
| 511 | static constexpr std::string_view SNE_S = "SNE.S"; | ||
| 512 | static constexpr std::string_view SNE_U = "SNE.U"; | ||
| 513 | static constexpr std::string_view SGE_F = "SGE.F32"; | ||
| 514 | static constexpr std::string_view SGE_S = "SGE.S"; | ||
| 515 | static constexpr std::string_view SGE_U = "SGE.U"; | ||
| 516 | static constexpr std::string_view AND_S = "AND.S"; | ||
| 517 | static constexpr std::string_view AND_U = "AND.U"; | ||
| 518 | static constexpr std::string_view TRUNC_F = "TRUNC.F"; | ||
| 519 | static constexpr std::string_view TRUNC_S = "TRUNC.S"; | ||
| 520 | static constexpr std::string_view TRUNC_U = "TRUNC.U"; | ||
| 521 | static constexpr std::string_view SHL_S = "SHL.S"; | ||
| 522 | static constexpr std::string_view SHL_U = "SHL.U"; | ||
| 523 | static constexpr std::string_view SHR_S = "SHR.S"; | ||
| 524 | static constexpr std::string_view SHR_U = "SHR.U"; | ||
| 525 | static constexpr std::string_view OR_S = "OR.S"; | ||
| 526 | static constexpr std::string_view OR_U = "OR.U"; | ||
| 527 | static constexpr std::string_view XOR_S = "XOR.S"; | ||
| 528 | static constexpr std::string_view XOR_U = "XOR.U"; | ||
| 529 | static constexpr std::string_view NOT_S = "NOT.S"; | ||
| 530 | static constexpr std::string_view NOT_U = "NOT.U"; | ||
| 531 | static constexpr std::string_view BTC_S = "BTC.S"; | ||
| 532 | static constexpr std::string_view BTC_U = "BTC.U"; | ||
| 533 | static constexpr std::string_view BTFM_S = "BTFM.S"; | ||
| 534 | static constexpr std::string_view BTFM_U = "BTFM.U"; | ||
| 535 | static constexpr std::string_view ROUND_F = "ROUND.F"; | ||
| 536 | static constexpr std::string_view CEIL_F = "CEIL.F"; | ||
| 537 | static constexpr std::string_view FLR_F = "FLR.F"; | ||
| 538 | static constexpr std::string_view I2F_S = "I2F.S"; | ||
| 539 | static constexpr std::string_view I2F_U = "I2F.U"; | ||
| 540 | static constexpr std::string_view MIN_F = "MIN.F"; | ||
| 541 | static constexpr std::string_view MIN_S = "MIN.S"; | ||
| 542 | static constexpr std::string_view MIN_U = "MIN.U"; | ||
| 543 | static constexpr std::string_view MAX_F = "MAX.F"; | ||
| 544 | static constexpr std::string_view MAX_S = "MAX.S"; | ||
| 545 | static constexpr std::string_view MAX_U = "MAX.U"; | ||
| 546 | static constexpr std::string_view MOV_U = "MOV.U"; | ||
| 547 | static constexpr std::string_view TGBALLOT_U = "TGBALLOT.U"; | ||
| 548 | static constexpr std::string_view TGALL_U = "TGALL.U"; | ||
| 549 | static constexpr std::string_view TGANY_U = "TGANY.U"; | ||
| 550 | static constexpr std::string_view TGEQ_U = "TGEQ.U"; | ||
| 551 | static constexpr std::string_view EXCH = "EXCH"; | ||
| 552 | static constexpr std::string_view ADD = "ADD"; | ||
| 553 | static constexpr std::string_view MIN = "MIN"; | ||
| 554 | static constexpr std::string_view MAX = "MAX"; | ||
| 555 | static constexpr std::string_view AND = "AND"; | ||
| 556 | static constexpr std::string_view OR = "OR"; | ||
| 557 | static constexpr std::string_view XOR = "XOR"; | ||
| 558 | static constexpr std::string_view U32 = "U32"; | ||
| 559 | static constexpr std::string_view S32 = "S32"; | ||
| 560 | |||
| 561 | static constexpr std::size_t NUM_ENTRIES = static_cast<std::size_t>(OperationCode::Amount); | ||
| 562 | using DecompilerType = std::string (ARBDecompiler::*)(Operation); | ||
| 563 | static constexpr std::array<DecompilerType, NUM_ENTRIES> OPERATION_DECOMPILERS = { | ||
| 564 | &ARBDecompiler::Assign, | ||
| 565 | |||
| 566 | &ARBDecompiler::Select, | ||
| 567 | |||
| 568 | &ARBDecompiler::Binary<ADD_F32>, | ||
| 569 | &ARBDecompiler::Binary<MUL_F32>, | ||
| 570 | &ARBDecompiler::Binary<DIV_F32>, | ||
| 571 | &ARBDecompiler::Trinary<MAD_F32>, | ||
| 572 | &ARBDecompiler::Negate<'F'>, | ||
| 573 | &ARBDecompiler::Absolute<'F'>, | ||
| 574 | &ARBDecompiler::FClamp, | ||
| 575 | &ARBDecompiler::FCastHalf0, | ||
| 576 | &ARBDecompiler::FCastHalf1, | ||
| 577 | &ARBDecompiler::Binary<MIN_F>, | ||
| 578 | &ARBDecompiler::Binary<MAX_F>, | ||
| 579 | &ARBDecompiler::Unary<COS_F32>, | ||
| 580 | &ARBDecompiler::Unary<SIN_F32>, | ||
| 581 | &ARBDecompiler::Unary<EX2_F32>, | ||
| 582 | &ARBDecompiler::Unary<LG2_F32>, | ||
| 583 | &ARBDecompiler::Unary<RSQ_F32>, | ||
| 584 | &ARBDecompiler::FSqrt, | ||
| 585 | &ARBDecompiler::Unary<ROUND_F>, | ||
| 586 | &ARBDecompiler::Unary<FLR_F>, | ||
| 587 | &ARBDecompiler::Unary<CEIL_F>, | ||
| 588 | &ARBDecompiler::Unary<TRUNC_F>, | ||
| 589 | &ARBDecompiler::Unary<I2F_S>, | ||
| 590 | &ARBDecompiler::Unary<I2F_U>, | ||
| 591 | &ARBDecompiler::FSwizzleAdd, | ||
| 592 | |||
| 593 | &ARBDecompiler::Binary<ADD_S>, | ||
| 594 | &ARBDecompiler::Binary<MUL_S>, | ||
| 595 | &ARBDecompiler::Binary<DIV_S>, | ||
| 596 | &ARBDecompiler::Negate<'S'>, | ||
| 597 | &ARBDecompiler::Absolute<'S'>, | ||
| 598 | &ARBDecompiler::Binary<MIN_S>, | ||
| 599 | &ARBDecompiler::Binary<MAX_S>, | ||
| 600 | |||
| 601 | &ARBDecompiler::Unary<TRUNC_S>, | ||
| 602 | &ARBDecompiler::Unary<MOV_U>, | ||
| 603 | &ARBDecompiler::Binary<SHL_S>, | ||
| 604 | &ARBDecompiler::Binary<SHR_U>, | ||
| 605 | &ARBDecompiler::Binary<SHR_S>, | ||
| 606 | &ARBDecompiler::Binary<AND_S>, | ||
| 607 | &ARBDecompiler::Binary<OR_S>, | ||
| 608 | &ARBDecompiler::Binary<XOR_S>, | ||
| 609 | &ARBDecompiler::Unary<NOT_S>, | ||
| 610 | &ARBDecompiler::BitfieldInsert<'S'>, | ||
| 611 | &ARBDecompiler::BitfieldExtract<'S'>, | ||
| 612 | &ARBDecompiler::Unary<BTC_S>, | ||
| 613 | &ARBDecompiler::Unary<BTFM_S>, | ||
| 614 | |||
| 615 | &ARBDecompiler::Binary<ADD_U>, | ||
| 616 | &ARBDecompiler::Binary<MUL_U>, | ||
| 617 | &ARBDecompiler::Binary<DIV_U>, | ||
| 618 | &ARBDecompiler::Binary<MIN_U>, | ||
| 619 | &ARBDecompiler::Binary<MAX_U>, | ||
| 620 | &ARBDecompiler::Unary<TRUNC_U>, | ||
| 621 | &ARBDecompiler::Unary<MOV_U>, | ||
| 622 | &ARBDecompiler::Binary<SHL_U>, | ||
| 623 | &ARBDecompiler::Binary<SHR_U>, | ||
| 624 | &ARBDecompiler::Binary<SHR_U>, | ||
| 625 | &ARBDecompiler::Binary<AND_U>, | ||
| 626 | &ARBDecompiler::Binary<OR_U>, | ||
| 627 | &ARBDecompiler::Binary<XOR_U>, | ||
| 628 | &ARBDecompiler::Unary<NOT_U>, | ||
| 629 | &ARBDecompiler::BitfieldInsert<'U'>, | ||
| 630 | &ARBDecompiler::BitfieldExtract<'U'>, | ||
| 631 | &ARBDecompiler::Unary<BTC_U>, | ||
| 632 | &ARBDecompiler::Unary<BTFM_U>, | ||
| 633 | |||
| 634 | &ARBDecompiler::HAdd2, | ||
| 635 | &ARBDecompiler::HMul2, | ||
| 636 | &ARBDecompiler::HFma2, | ||
| 637 | &ARBDecompiler::HAbsolute, | ||
| 638 | &ARBDecompiler::HNegate, | ||
| 639 | &ARBDecompiler::HClamp, | ||
| 640 | &ARBDecompiler::HCastFloat, | ||
| 641 | &ARBDecompiler::HUnpack, | ||
| 642 | &ARBDecompiler::HMergeF32, | ||
| 643 | &ARBDecompiler::HMergeH0, | ||
| 644 | &ARBDecompiler::HMergeH1, | ||
| 645 | &ARBDecompiler::HPack2, | ||
| 646 | |||
| 647 | &ARBDecompiler::LogicalAssign, | ||
| 648 | &ARBDecompiler::Binary<AND_U>, | ||
| 649 | &ARBDecompiler::Binary<OR_U>, | ||
| 650 | &ARBDecompiler::Binary<XOR_U>, | ||
| 651 | &ARBDecompiler::Unary<NOT_U>, | ||
| 652 | &ARBDecompiler::LogicalPick2, | ||
| 653 | &ARBDecompiler::LogicalAnd2, | ||
| 654 | |||
| 655 | &ARBDecompiler::FloatComparison<SLT_F, false>, | ||
| 656 | &ARBDecompiler::FloatComparison<SEQ_F, false>, | ||
| 657 | &ARBDecompiler::FloatComparison<SLE_F, false>, | ||
| 658 | &ARBDecompiler::FloatComparison<SGT_F, false>, | ||
| 659 | &ARBDecompiler::FloatComparison<SNE_F, false>, | ||
| 660 | &ARBDecompiler::FloatComparison<SGE_F, false>, | ||
| 661 | &ARBDecompiler::FloatOrdered, | ||
| 662 | &ARBDecompiler::FloatUnordered, | ||
| 663 | &ARBDecompiler::FloatComparison<SLT_F, true>, | ||
| 664 | &ARBDecompiler::FloatComparison<SEQ_F, true>, | ||
| 665 | &ARBDecompiler::FloatComparison<SLE_F, true>, | ||
| 666 | &ARBDecompiler::FloatComparison<SGT_F, true>, | ||
| 667 | &ARBDecompiler::FloatComparison<SNE_F, true>, | ||
| 668 | &ARBDecompiler::FloatComparison<SGE_F, true>, | ||
| 669 | |||
| 670 | &ARBDecompiler::Binary<SLT_S>, | ||
| 671 | &ARBDecompiler::Binary<SEQ_S>, | ||
| 672 | &ARBDecompiler::Binary<SLE_S>, | ||
| 673 | &ARBDecompiler::Binary<SGT_S>, | ||
| 674 | &ARBDecompiler::Binary<SNE_S>, | ||
| 675 | &ARBDecompiler::Binary<SGE_S>, | ||
| 676 | |||
| 677 | &ARBDecompiler::Binary<SLT_U>, | ||
| 678 | &ARBDecompiler::Binary<SEQ_U>, | ||
| 679 | &ARBDecompiler::Binary<SLE_U>, | ||
| 680 | &ARBDecompiler::Binary<SGT_U>, | ||
| 681 | &ARBDecompiler::Binary<SNE_U>, | ||
| 682 | &ARBDecompiler::Binary<SGE_U>, | ||
| 683 | |||
| 684 | &ARBDecompiler::LogicalAddCarry, | ||
| 685 | |||
| 686 | &ARBDecompiler::HalfComparison<SLT_F, false>, | ||
| 687 | &ARBDecompiler::HalfComparison<SEQ_F, false>, | ||
| 688 | &ARBDecompiler::HalfComparison<SLE_F, false>, | ||
| 689 | &ARBDecompiler::HalfComparison<SGT_F, false>, | ||
| 690 | &ARBDecompiler::HalfComparison<SNE_F, false>, | ||
| 691 | &ARBDecompiler::HalfComparison<SGE_F, false>, | ||
| 692 | &ARBDecompiler::HalfComparison<SLT_F, true>, | ||
| 693 | &ARBDecompiler::HalfComparison<SEQ_F, true>, | ||
| 694 | &ARBDecompiler::HalfComparison<SLE_F, true>, | ||
| 695 | &ARBDecompiler::HalfComparison<SGT_F, true>, | ||
| 696 | &ARBDecompiler::HalfComparison<SNE_F, true>, | ||
| 697 | &ARBDecompiler::HalfComparison<SGE_F, true>, | ||
| 698 | |||
| 699 | &ARBDecompiler::Texture, | ||
| 700 | &ARBDecompiler::Texture, | ||
| 701 | &ARBDecompiler::TextureGather, | ||
| 702 | &ARBDecompiler::TextureQueryDimensions, | ||
| 703 | &ARBDecompiler::TextureQueryLod, | ||
| 704 | &ARBDecompiler::TexelFetch, | ||
| 705 | &ARBDecompiler::TextureGradient, | ||
| 706 | |||
| 707 | &ARBDecompiler::ImageLoad, | ||
| 708 | &ARBDecompiler::ImageStore, | ||
| 709 | |||
| 710 | &ARBDecompiler::AtomicImage<ADD, U32>, | ||
| 711 | &ARBDecompiler::AtomicImage<AND, U32>, | ||
| 712 | &ARBDecompiler::AtomicImage<OR, U32>, | ||
| 713 | &ARBDecompiler::AtomicImage<XOR, U32>, | ||
| 714 | &ARBDecompiler::AtomicImage<EXCH, U32>, | ||
| 715 | |||
| 716 | &ARBDecompiler::Atomic<EXCH, U32>, | ||
| 717 | &ARBDecompiler::Atomic<ADD, U32>, | ||
| 718 | &ARBDecompiler::Atomic<MIN, U32>, | ||
| 719 | &ARBDecompiler::Atomic<MAX, U32>, | ||
| 720 | &ARBDecompiler::Atomic<AND, U32>, | ||
| 721 | &ARBDecompiler::Atomic<OR, U32>, | ||
| 722 | &ARBDecompiler::Atomic<XOR, U32>, | ||
| 723 | |||
| 724 | &ARBDecompiler::Atomic<EXCH, S32>, | ||
| 725 | &ARBDecompiler::Atomic<ADD, S32>, | ||
| 726 | &ARBDecompiler::Atomic<MIN, S32>, | ||
| 727 | &ARBDecompiler::Atomic<MAX, S32>, | ||
| 728 | &ARBDecompiler::Atomic<AND, S32>, | ||
| 729 | &ARBDecompiler::Atomic<OR, S32>, | ||
| 730 | &ARBDecompiler::Atomic<XOR, S32>, | ||
| 731 | |||
| 732 | &ARBDecompiler::Atomic<ADD, U32>, | ||
| 733 | &ARBDecompiler::Atomic<MIN, U32>, | ||
| 734 | &ARBDecompiler::Atomic<MAX, U32>, | ||
| 735 | &ARBDecompiler::Atomic<AND, U32>, | ||
| 736 | &ARBDecompiler::Atomic<OR, U32>, | ||
| 737 | &ARBDecompiler::Atomic<XOR, U32>, | ||
| 738 | |||
| 739 | &ARBDecompiler::Atomic<ADD, S32>, | ||
| 740 | &ARBDecompiler::Atomic<MIN, S32>, | ||
| 741 | &ARBDecompiler::Atomic<MAX, S32>, | ||
| 742 | &ARBDecompiler::Atomic<AND, S32>, | ||
| 743 | &ARBDecompiler::Atomic<OR, S32>, | ||
| 744 | &ARBDecompiler::Atomic<XOR, S32>, | ||
| 745 | |||
| 746 | &ARBDecompiler::Branch, | ||
| 747 | &ARBDecompiler::BranchIndirect, | ||
| 748 | &ARBDecompiler::PushFlowStack, | ||
| 749 | &ARBDecompiler::PopFlowStack, | ||
| 750 | &ARBDecompiler::Exit, | ||
| 751 | &ARBDecompiler::Discard, | ||
| 752 | |||
| 753 | &ARBDecompiler::EmitVertex, | ||
| 754 | &ARBDecompiler::EndPrimitive, | ||
| 755 | |||
| 756 | &ARBDecompiler::InvocationId, | ||
| 757 | &ARBDecompiler::YNegate, | ||
| 758 | &ARBDecompiler::LocalInvocationId<'x'>, | ||
| 759 | &ARBDecompiler::LocalInvocationId<'y'>, | ||
| 760 | &ARBDecompiler::LocalInvocationId<'z'>, | ||
| 761 | &ARBDecompiler::WorkGroupId<'x'>, | ||
| 762 | &ARBDecompiler::WorkGroupId<'y'>, | ||
| 763 | &ARBDecompiler::WorkGroupId<'z'>, | ||
| 764 | |||
| 765 | &ARBDecompiler::Unary<TGBALLOT_U>, | ||
| 766 | &ARBDecompiler::Unary<TGALL_U>, | ||
| 767 | &ARBDecompiler::Unary<TGANY_U>, | ||
| 768 | &ARBDecompiler::Unary<TGEQ_U>, | ||
| 769 | |||
| 770 | &ARBDecompiler::ThreadId, | ||
| 771 | &ARBDecompiler::ThreadMask<'e', 'q'>, | ||
| 772 | &ARBDecompiler::ThreadMask<'g', 'e'>, | ||
| 773 | &ARBDecompiler::ThreadMask<'g', 't'>, | ||
| 774 | &ARBDecompiler::ThreadMask<'l', 'e'>, | ||
| 775 | &ARBDecompiler::ThreadMask<'l', 't'>, | ||
| 776 | &ARBDecompiler::ShuffleIndexed, | ||
| 777 | |||
| 778 | &ARBDecompiler::Barrier, | ||
| 779 | &ARBDecompiler::MemoryBarrierGroup, | ||
| 780 | &ARBDecompiler::MemoryBarrierGlobal, | ||
| 781 | }; | ||
| 782 | }; | ||
| 783 | |||
| 784 | ARBDecompiler::ARBDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry, | ||
| 785 | ShaderType stage, std::string_view identifier) | ||
| 786 | : device{device}, ir{ir}, registry{registry}, stage{stage} { | ||
| 787 | AddLine("TEMP RC;"); | ||
| 788 | AddLine("TEMP FSWZA[4];"); | ||
| 789 | AddLine("TEMP FSWZB[4];"); | ||
| 790 | if (ir.IsDecompiled()) { | ||
| 791 | DecompileAST(); | ||
| 792 | } else { | ||
| 793 | DecompileBranchMode(); | ||
| 794 | } | ||
| 795 | AddLine("END"); | ||
| 796 | |||
| 797 | const std::string code = std::move(shader_source); | ||
| 798 | DeclareHeader(); | ||
| 799 | DeclareVertex(); | ||
| 800 | DeclareGeometry(); | ||
| 801 | DeclareFragment(); | ||
| 802 | DeclareCompute(); | ||
| 803 | DeclareInputAttributes(); | ||
| 804 | DeclareOutputAttributes(); | ||
| 805 | DeclareLocalMemory(); | ||
| 806 | DeclareGlobalMemory(); | ||
| 807 | DeclareConstantBuffers(); | ||
| 808 | DeclareRegisters(); | ||
| 809 | DeclareTemporaries(); | ||
| 810 | DeclarePredicates(); | ||
| 811 | DeclareInternalFlags(); | ||
| 812 | |||
| 813 | shader_source += code; | ||
| 814 | } | ||
| 815 | |||
| 816 | std::string_view HeaderStageName(ShaderType stage) { | ||
| 817 | switch (stage) { | ||
| 818 | case ShaderType::Vertex: | ||
| 819 | return "vp"; | ||
| 820 | case ShaderType::Geometry: | ||
| 821 | return "gp"; | ||
| 822 | case ShaderType::Fragment: | ||
| 823 | return "fp"; | ||
| 824 | case ShaderType::Compute: | ||
| 825 | return "cp"; | ||
| 826 | default: | ||
| 827 | UNREACHABLE(); | ||
| 828 | return ""; | ||
| 829 | } | ||
| 830 | } | ||
| 831 | |||
| 832 | void ARBDecompiler::DeclareHeader() { | ||
| 833 | AddLine("!!NV{}5.0", HeaderStageName(stage)); | ||
| 834 | // Enabling this allows us to cheat on some instructions like TXL with SHADOWARRAY2D | ||
| 835 | AddLine("OPTION NV_internal;"); | ||
| 836 | AddLine("OPTION NV_gpu_program_fp64;"); | ||
| 837 | AddLine("OPTION NV_shader_storage_buffer;"); | ||
| 838 | AddLine("OPTION NV_shader_thread_group;"); | ||
| 839 | if (ir.UsesWarps() && device.HasWarpIntrinsics()) { | ||
| 840 | AddLine("OPTION NV_shader_thread_shuffle;"); | ||
| 841 | } | ||
| 842 | if (stage == ShaderType::Vertex) { | ||
| 843 | if (device.HasNvViewportArray2()) { | ||
| 844 | AddLine("OPTION NV_viewport_array2;"); | ||
| 845 | } | ||
| 846 | } | ||
| 847 | if (stage == ShaderType::Fragment) { | ||
| 848 | AddLine("OPTION ARB_draw_buffers;"); | ||
| 849 | } | ||
| 850 | if (device.HasImageLoadFormatted()) { | ||
| 851 | AddLine("OPTION EXT_shader_image_load_formatted;"); | ||
| 852 | } | ||
| 853 | } | ||
| 854 | |||
| 855 | void ARBDecompiler::DeclareVertex() { | ||
| 856 | if (stage != ShaderType::Vertex) { | ||
| 857 | return; | ||
| 858 | } | ||
| 859 | AddLine("OUTPUT result_clip[] = {{ result.clip[0..7] }};"); | ||
| 860 | } | ||
| 861 | |||
| 862 | void ARBDecompiler::DeclareGeometry() { | ||
| 863 | if (stage != ShaderType::Geometry) { | ||
| 864 | return; | ||
| 865 | } | ||
| 866 | const auto& info = registry.GetGraphicsInfo(); | ||
| 867 | const auto& header = ir.GetHeader(); | ||
| 868 | AddLine("PRIMITIVE_IN {};", PrimitiveDescription(info.primitive_topology)); | ||
| 869 | AddLine("PRIMITIVE_OUT {};", TopologyName(header.common3.output_topology)); | ||
| 870 | AddLine("VERTICES_OUT {};", header.common4.max_output_vertices.Value()); | ||
| 871 | AddLine("ATTRIB vertex_position = vertex.position;"); | ||
| 872 | } | ||
| 873 | |||
| 874 | void ARBDecompiler::DeclareFragment() { | ||
| 875 | if (stage != ShaderType::Fragment) { | ||
| 876 | return; | ||
| 877 | } | ||
| 878 | AddLine("OUTPUT result_color7 = result.color[7];"); | ||
| 879 | AddLine("OUTPUT result_color6 = result.color[6];"); | ||
| 880 | AddLine("OUTPUT result_color5 = result.color[5];"); | ||
| 881 | AddLine("OUTPUT result_color4 = result.color[4];"); | ||
| 882 | AddLine("OUTPUT result_color3 = result.color[3];"); | ||
| 883 | AddLine("OUTPUT result_color2 = result.color[2];"); | ||
| 884 | AddLine("OUTPUT result_color1 = result.color[1];"); | ||
| 885 | AddLine("OUTPUT result_color0 = result.color;"); | ||
| 886 | } | ||
| 887 | |||
| 888 | void ARBDecompiler::DeclareCompute() { | ||
| 889 | if (stage != ShaderType::Compute) { | ||
| 890 | return; | ||
| 891 | } | ||
| 892 | const ComputeInfo& info = registry.GetComputeInfo(); | ||
| 893 | AddLine("GROUP_SIZE {} {} {};", info.workgroup_size[0], info.workgroup_size[1], | ||
| 894 | info.workgroup_size[2]); | ||
| 895 | if (info.shared_memory_size_in_words > 0) { | ||
| 896 | const u32 size_in_bytes = info.shared_memory_size_in_words * 4; | ||
| 897 | AddLine("SHARED_MEMORY {};", size_in_bytes); | ||
| 898 | AddLine("SHARED shared_mem[] = {{program.sharedmem}};"); | ||
| 899 | } | ||
| 900 | } | ||
| 901 | |||
| 902 | void ARBDecompiler::DeclareInputAttributes() { | ||
| 903 | if (stage == ShaderType::Compute) { | ||
| 904 | return; | ||
| 905 | } | ||
| 906 | const std::string_view stage_name = StageInputName(stage); | ||
| 907 | for (const auto attribute : ir.GetInputAttributes()) { | ||
| 908 | if (!IsGenericAttribute(attribute)) { | ||
| 909 | continue; | ||
| 910 | } | ||
| 911 | const u32 index = GetGenericAttributeIndex(attribute); | ||
| 912 | |||
| 913 | std::string_view suffix; | ||
| 914 | if (stage == ShaderType::Fragment) { | ||
| 915 | const auto input_mode{ir.GetHeader().ps.GetPixelImap(index)}; | ||
| 916 | if (input_mode == PixelImap::Unused) { | ||
| 917 | return; | ||
| 918 | } | ||
| 919 | suffix = GetInputFlags(input_mode); | ||
| 920 | } | ||
| 921 | AddLine("{}ATTRIB in_attr{}[] = {{ {}.attrib[{}..{}] }};", suffix, index, stage_name, index, | ||
| 922 | index); | ||
| 923 | } | ||
| 924 | } | ||
| 925 | |||
| 926 | void ARBDecompiler::DeclareOutputAttributes() { | ||
| 927 | if (stage == ShaderType::Compute) { | ||
| 928 | return; | ||
| 929 | } | ||
| 930 | for (const auto attribute : ir.GetOutputAttributes()) { | ||
| 931 | if (!IsGenericAttribute(attribute)) { | ||
| 932 | continue; | ||
| 933 | } | ||
| 934 | const u32 index = GetGenericAttributeIndex(attribute); | ||
| 935 | AddLine("OUTPUT out_attr{}[] = {{ result.attrib[{}..{}] }};", index, index, index); | ||
| 936 | } | ||
| 937 | } | ||
| 938 | |||
| 939 | void ARBDecompiler::DeclareLocalMemory() { | ||
| 940 | u64 size = 0; | ||
| 941 | if (stage == ShaderType::Compute) { | ||
| 942 | size = registry.GetComputeInfo().local_memory_size_in_words * 4ULL; | ||
| 943 | } else { | ||
| 944 | size = ir.GetHeader().GetLocalMemorySize(); | ||
| 945 | } | ||
| 946 | if (size == 0) { | ||
| 947 | return; | ||
| 948 | } | ||
| 949 | const u64 element_count = Common::AlignUp(size, 4) / 4; | ||
| 950 | AddLine("TEMP lmem[{}];", element_count); | ||
| 951 | } | ||
| 952 | |||
| 953 | void ARBDecompiler::DeclareGlobalMemory() { | ||
| 954 | u32 binding = 0; // device.GetBaseBindings(stage).shader_storage_buffer; | ||
| 955 | for (const auto& pair : ir.GetGlobalMemory()) { | ||
| 956 | const auto& base = pair.first; | ||
| 957 | AddLine("STORAGE {}[] = {{ program.storage[{}] }};", GlobalMemoryName(base), binding); | ||
| 958 | ++binding; | ||
| 959 | } | ||
| 960 | } | ||
| 961 | |||
| 962 | void ARBDecompiler::DeclareConstantBuffers() { | ||
| 963 | u32 binding = 0; | ||
| 964 | for (const auto& cbuf : ir.GetConstantBuffers()) { | ||
| 965 | AddLine("CBUFFER cbuf{}[] = {{ program.buffer[{}] }};", cbuf.first, binding); | ||
| 966 | ++binding; | ||
| 967 | } | ||
| 968 | } | ||
| 969 | |||
| 970 | void ARBDecompiler::DeclareRegisters() { | ||
| 971 | for (const u32 gpr : ir.GetRegisters()) { | ||
| 972 | AddLine("TEMP R{};", gpr); | ||
| 973 | } | ||
| 974 | } | ||
| 975 | |||
| 976 | void ARBDecompiler::DeclareTemporaries() { | ||
| 977 | for (std::size_t i = 0; i < max_temporaries; ++i) { | ||
| 978 | AddLine("TEMP T{};", i); | ||
| 979 | } | ||
| 980 | } | ||
| 981 | |||
| 982 | void ARBDecompiler::DeclarePredicates() { | ||
| 983 | for (const Tegra::Shader::Pred pred : ir.GetPredicates()) { | ||
| 984 | AddLine("TEMP P{};", static_cast<u64>(pred)); | ||
| 985 | } | ||
| 986 | } | ||
| 987 | |||
| 988 | void ARBDecompiler::DeclareInternalFlags() { | ||
| 989 | for (const char* name : INTERNAL_FLAG_NAMES) { | ||
| 990 | AddLine("TEMP {};", name); | ||
| 991 | } | ||
| 992 | } | ||
| 993 | |||
| 994 | void ARBDecompiler::InitializeVariables() { | ||
| 995 | AddLine("MOV.F32 FSWZA[0], -1;"); | ||
| 996 | AddLine("MOV.F32 FSWZA[1], 1;"); | ||
| 997 | AddLine("MOV.F32 FSWZA[2], -1;"); | ||
| 998 | AddLine("MOV.F32 FSWZA[3], 0;"); | ||
| 999 | AddLine("MOV.F32 FSWZB[0], -1;"); | ||
| 1000 | AddLine("MOV.F32 FSWZB[1], -1;"); | ||
| 1001 | AddLine("MOV.F32 FSWZB[2], 1;"); | ||
| 1002 | AddLine("MOV.F32 FSWZB[3], -1;"); | ||
| 1003 | |||
| 1004 | if (stage == ShaderType::Vertex || stage == ShaderType::Geometry) { | ||
| 1005 | AddLine("MOV.F result.position, {{0, 0, 0, 1}};"); | ||
| 1006 | } | ||
| 1007 | for (const auto attribute : ir.GetOutputAttributes()) { | ||
| 1008 | if (!IsGenericAttribute(attribute)) { | ||
| 1009 | continue; | ||
| 1010 | } | ||
| 1011 | const u32 index = GetGenericAttributeIndex(attribute); | ||
| 1012 | AddLine("MOV.F result.attrib[{}], {{0, 0, 0, 1}};", index); | ||
| 1013 | } | ||
| 1014 | for (const u32 gpr : ir.GetRegisters()) { | ||
| 1015 | AddLine("MOV.F R{}, {{0, 0, 0, 0}};", gpr); | ||
| 1016 | } | ||
| 1017 | for (const Tegra::Shader::Pred pred : ir.GetPredicates()) { | ||
| 1018 | AddLine("MOV.U P{}, {{0, 0, 0, 0}};", static_cast<u64>(pred)); | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | void ARBDecompiler::DecompileAST() { | ||
| 1023 | const u32 num_flow_variables = ir.GetASTNumVariables(); | ||
| 1024 | for (u32 i = 0; i < num_flow_variables; ++i) { | ||
| 1025 | AddLine("TEMP F{};", i); | ||
| 1026 | } | ||
| 1027 | for (u32 i = 0; i < num_flow_variables; ++i) { | ||
| 1028 | AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | InitializeVariables(); | ||
| 1032 | |||
| 1033 | VisitAST(ir.GetASTProgram()); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | void ARBDecompiler::DecompileBranchMode() { | ||
| 1037 | static constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 1038 | if (!ir.IsFlowStackDisabled()) { | ||
| 1039 | AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE); | ||
| 1040 | AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE); | ||
| 1041 | AddLine("TEMP SSY_TOP;"); | ||
| 1042 | AddLine("TEMP PBK_TOP;"); | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | AddLine("TEMP PC;"); | ||
| 1046 | |||
| 1047 | if (!ir.IsFlowStackDisabled()) { | ||
| 1048 | AddLine("MOV.U SSY_TOP.x, 0;"); | ||
| 1049 | AddLine("MOV.U PBK_TOP.x, 0;"); | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | InitializeVariables(); | ||
| 1053 | |||
| 1054 | const auto basic_block_end = ir.GetBasicBlocks().end(); | ||
| 1055 | auto basic_block_it = ir.GetBasicBlocks().begin(); | ||
| 1056 | const u32 first_address = basic_block_it->first; | ||
| 1057 | AddLine("MOV.U PC.x, {};", first_address); | ||
| 1058 | |||
| 1059 | AddLine("REP;"); | ||
| 1060 | |||
| 1061 | std::size_t num_blocks = 0; | ||
| 1062 | while (basic_block_it != basic_block_end) { | ||
| 1063 | const auto& [address, bb] = *basic_block_it; | ||
| 1064 | ++num_blocks; | ||
| 1065 | |||
| 1066 | AddLine("SEQ.S.CC RC.x, PC.x, {};", address); | ||
| 1067 | AddLine("IF NE.x;"); | ||
| 1068 | |||
| 1069 | VisitBlock(bb); | ||
| 1070 | |||
| 1071 | ++basic_block_it; | ||
| 1072 | |||
| 1073 | if (basic_block_it != basic_block_end) { | ||
| 1074 | const auto op = std::get_if<OperationNode>(&*bb[bb.size() - 1]); | ||
| 1075 | if (!op || op->GetCode() != OperationCode::Branch) { | ||
| 1076 | const u32 next_address = basic_block_it->first; | ||
| 1077 | AddLine("MOV.U PC.x, {};", next_address); | ||
| 1078 | AddLine("CONT;"); | ||
| 1079 | } | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | AddLine("ELSE;"); | ||
| 1083 | } | ||
| 1084 | AddLine("RET;"); | ||
| 1085 | while (num_blocks--) { | ||
| 1086 | AddLine("ENDIF;"); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | AddLine("ENDREP;"); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | void ARBDecompiler::VisitAST(const ASTNode& node) { | ||
| 1093 | if (const auto ast = std::get_if<ASTProgram>(&*node->GetInnerData())) { | ||
| 1094 | for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) { | ||
| 1095 | VisitAST(current); | ||
| 1096 | } | ||
| 1097 | } else if (const auto ast = std::get_if<ASTIfThen>(&*node->GetInnerData())) { | ||
| 1098 | const std::string condition = VisitExpression(ast->condition); | ||
| 1099 | ResetTemporaries(); | ||
| 1100 | |||
| 1101 | AddLine("MOVC.U RC.x, {};", condition); | ||
| 1102 | AddLine("IF NE.x;"); | ||
| 1103 | for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) { | ||
| 1104 | VisitAST(current); | ||
| 1105 | } | ||
| 1106 | AddLine("ENDIF;"); | ||
| 1107 | } else if (const auto ast = std::get_if<ASTIfElse>(&*node->GetInnerData())) { | ||
| 1108 | AddLine("ELSE;"); | ||
| 1109 | for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) { | ||
| 1110 | VisitAST(current); | ||
| 1111 | } | ||
| 1112 | } else if (const auto ast = std::get_if<ASTBlockDecoded>(&*node->GetInnerData())) { | ||
| 1113 | VisitBlock(ast->nodes); | ||
| 1114 | } else if (const auto ast = std::get_if<ASTVarSet>(&*node->GetInnerData())) { | ||
| 1115 | AddLine("MOV.U F{}, {};", ast->index, VisitExpression(ast->condition)); | ||
| 1116 | ResetTemporaries(); | ||
| 1117 | } else if (const auto ast = std::get_if<ASTDoWhile>(&*node->GetInnerData())) { | ||
| 1118 | const std::string condition = VisitExpression(ast->condition); | ||
| 1119 | ResetTemporaries(); | ||
| 1120 | AddLine("REP;"); | ||
| 1121 | for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) { | ||
| 1122 | VisitAST(current); | ||
| 1123 | } | ||
| 1124 | AddLine("MOVC.U RC.x, {};", condition); | ||
| 1125 | AddLine("BRK (NE.x);"); | ||
| 1126 | AddLine("ENDREP;"); | ||
| 1127 | } else if (const auto ast = std::get_if<ASTReturn>(&*node->GetInnerData())) { | ||
| 1128 | const bool is_true = ExprIsTrue(ast->condition); | ||
| 1129 | if (!is_true) { | ||
| 1130 | AddLine("MOVC.U RC.x, {};", VisitExpression(ast->condition)); | ||
| 1131 | AddLine("IF NE.x;"); | ||
| 1132 | ResetTemporaries(); | ||
| 1133 | } | ||
| 1134 | if (ast->kills) { | ||
| 1135 | AddLine("KIL TR;"); | ||
| 1136 | } else { | ||
| 1137 | Exit(); | ||
| 1138 | } | ||
| 1139 | if (!is_true) { | ||
| 1140 | AddLine("ENDIF;"); | ||
| 1141 | } | ||
| 1142 | } else if (const auto ast = std::get_if<ASTBreak>(&*node->GetInnerData())) { | ||
| 1143 | if (ExprIsTrue(ast->condition)) { | ||
| 1144 | AddLine("BRK;"); | ||
| 1145 | } else { | ||
| 1146 | AddLine("MOVC.U RC.x, {};", VisitExpression(ast->condition)); | ||
| 1147 | AddLine("BRK (NE.x);"); | ||
| 1148 | ResetTemporaries(); | ||
| 1149 | } | ||
| 1150 | } else if (std::holds_alternative<ASTLabel>(*node->GetInnerData())) { | ||
| 1151 | // Nothing to do | ||
| 1152 | } else { | ||
| 1153 | UNREACHABLE(); | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | std::string ARBDecompiler::VisitExpression(const Expr& node) { | ||
| 1158 | if (const auto expr = std::get_if<ExprAnd>(&*node)) { | ||
| 1159 | std::string result = AllocTemporary(); | ||
| 1160 | AddLine("AND.U {}, {}, {};", result, VisitExpression(expr->operand1), | ||
| 1161 | VisitExpression(expr->operand2)); | ||
| 1162 | return result; | ||
| 1163 | } | ||
| 1164 | if (const auto expr = std::get_if<ExprOr>(&*node)) { | ||
| 1165 | std::string result = AllocTemporary(); | ||
| 1166 | AddLine("OR.U {}, {}, {};", result, VisitExpression(expr->operand1), | ||
| 1167 | VisitExpression(expr->operand2)); | ||
| 1168 | return result; | ||
| 1169 | } | ||
| 1170 | if (const auto expr = std::get_if<ExprNot>(&*node)) { | ||
| 1171 | std::string result = AllocTemporary(); | ||
| 1172 | AddLine("CMP.S {}, {}, 0, -1;", result, VisitExpression(expr->operand1)); | ||
| 1173 | return result; | ||
| 1174 | } | ||
| 1175 | if (const auto expr = std::get_if<ExprPredicate>(&*node)) { | ||
| 1176 | return fmt::format("P{}.x", static_cast<u64>(expr->predicate)); | ||
| 1177 | } | ||
| 1178 | if (const auto expr = std::get_if<ExprCondCode>(&*node)) { | ||
| 1179 | return Visit(ir.GetConditionCode(expr->cc)); | ||
| 1180 | } | ||
| 1181 | if (const auto expr = std::get_if<ExprVar>(&*node)) { | ||
| 1182 | return fmt::format("F{}.x", expr->var_index); | ||
| 1183 | } | ||
| 1184 | if (const auto expr = std::get_if<ExprBoolean>(&*node)) { | ||
| 1185 | return expr->value ? "0xffffffff" : "0"; | ||
| 1186 | } | ||
| 1187 | if (const auto expr = std::get_if<ExprGprEqual>(&*node)) { | ||
| 1188 | std::string result = AllocTemporary(); | ||
| 1189 | AddLine("SEQ.U {}, R{}.x, {};", result, expr->gpr, expr->value); | ||
| 1190 | return result; | ||
| 1191 | } | ||
| 1192 | UNREACHABLE(); | ||
| 1193 | return "0"; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | void ARBDecompiler::VisitBlock(const NodeBlock& bb) { | ||
| 1197 | for (const auto& node : bb) { | ||
| 1198 | Visit(node); | ||
| 1199 | } | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | std::string ARBDecompiler::Visit(const Node& node) { | ||
| 1203 | if (const auto operation = std::get_if<OperationNode>(&*node)) { | ||
| 1204 | if (const auto amend_index = operation->GetAmendIndex()) { | ||
| 1205 | Visit(ir.GetAmendNode(*amend_index)); | ||
| 1206 | } | ||
| 1207 | const std::size_t index = static_cast<std::size_t>(operation->GetCode()); | ||
| 1208 | if (index >= OPERATION_DECOMPILERS.size()) { | ||
| 1209 | UNREACHABLE_MSG("Out of bounds operation: {}", index); | ||
| 1210 | return {}; | ||
| 1211 | } | ||
| 1212 | const auto decompiler = OPERATION_DECOMPILERS[index]; | ||
| 1213 | if (decompiler == nullptr) { | ||
| 1214 | UNREACHABLE_MSG("Undefined operation: {}", index); | ||
| 1215 | return {}; | ||
| 1216 | } | ||
| 1217 | return (this->*decompiler)(*operation); | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | if (const auto gpr = std::get_if<GprNode>(&*node)) { | ||
| 1221 | const u32 index = gpr->GetIndex(); | ||
| 1222 | if (index == Register::ZeroIndex) { | ||
| 1223 | return "{0, 0, 0, 0}.x"; | ||
| 1224 | } | ||
| 1225 | return fmt::format("R{}.x", index); | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | if (const auto cv = std::get_if<CustomVarNode>(&*node)) { | ||
| 1229 | return fmt::format("CV{}.x", cv->GetIndex()); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { | ||
| 1233 | std::string temporary = AllocTemporary(); | ||
| 1234 | AddLine("MOV.U {}, {};", temporary, immediate->GetValue()); | ||
| 1235 | return temporary; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | if (const auto predicate = std::get_if<PredicateNode>(&*node)) { | ||
| 1239 | std::string temporary = AllocTemporary(); | ||
| 1240 | switch (const auto index = predicate->GetIndex(); index) { | ||
| 1241 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 1242 | AddLine("MOV.S {}, -1;", temporary); | ||
| 1243 | break; | ||
| 1244 | case Tegra::Shader::Pred::NeverExecute: | ||
| 1245 | AddLine("MOV.S {}, 0;", temporary); | ||
| 1246 | break; | ||
| 1247 | default: | ||
| 1248 | AddLine("MOV.S {}, P{}.x;", temporary, static_cast<u64>(index)); | ||
| 1249 | break; | ||
| 1250 | } | ||
| 1251 | if (predicate->IsNegated()) { | ||
| 1252 | AddLine("CMP.S {}, {}, 0, -1;", temporary, temporary); | ||
| 1253 | } | ||
| 1254 | return temporary; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | if (const auto abuf = std::get_if<AbufNode>(&*node)) { | ||
| 1258 | if (abuf->IsPhysicalBuffer()) { | ||
| 1259 | UNIMPLEMENTED_MSG("Physical buffers are not implemented"); | ||
| 1260 | return "{0, 0, 0, 0}.x"; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | const auto buffer_index = [this, &abuf]() -> std::string { | ||
| 1264 | if (stage != ShaderType::Geometry) { | ||
| 1265 | return ""; | ||
| 1266 | } | ||
| 1267 | return fmt::format("[{}]", Visit(abuf->GetBuffer())); | ||
| 1268 | }; | ||
| 1269 | |||
| 1270 | const Attribute::Index index = abuf->GetIndex(); | ||
| 1271 | const u32 element = abuf->GetElement(); | ||
| 1272 | const char swizzle = Swizzle(element); | ||
| 1273 | switch (index) { | ||
| 1274 | case Attribute::Index::Position: { | ||
| 1275 | if (stage == ShaderType::Geometry) { | ||
| 1276 | return fmt::format("{}_position[{}].{}", StageInputName(stage), | ||
| 1277 | Visit(abuf->GetBuffer()), swizzle); | ||
| 1278 | } else { | ||
| 1279 | return fmt::format("{}.position.{}", StageInputName(stage), swizzle); | ||
| 1280 | } | ||
| 1281 | } | ||
| 1282 | case Attribute::Index::TessCoordInstanceIDVertexID: | ||
| 1283 | ASSERT(stage == ShaderType::Vertex); | ||
| 1284 | switch (element) { | ||
| 1285 | case 2: | ||
| 1286 | return "vertex.instance"; | ||
| 1287 | case 3: | ||
| 1288 | return "vertex.id"; | ||
| 1289 | } | ||
| 1290 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); | ||
| 1291 | break; | ||
| 1292 | case Attribute::Index::PointCoord: | ||
| 1293 | switch (element) { | ||
| 1294 | case 0: | ||
| 1295 | return "fragment.pointcoord.x"; | ||
| 1296 | case 1: | ||
| 1297 | return "fragment.pointcoord.y"; | ||
| 1298 | } | ||
| 1299 | UNIMPLEMENTED(); | ||
| 1300 | break; | ||
| 1301 | case Attribute::Index::FrontFacing: { | ||
| 1302 | ASSERT(stage == ShaderType::Fragment); | ||
| 1303 | ASSERT(element == 3); | ||
| 1304 | const std::string temporary = AllocVectorTemporary(); | ||
| 1305 | AddLine("SGT.S RC.x, fragment.facing, {{0, 0, 0, 0}};"); | ||
| 1306 | AddLine("MOV.U.CC RC.x, -RC;"); | ||
| 1307 | AddLine("MOV.S {}.x, 0;", temporary); | ||
| 1308 | AddLine("MOV.S {}.x (NE.x), -1;", temporary); | ||
| 1309 | return fmt::format("{}.x", temporary); | ||
| 1310 | } | ||
| 1311 | default: | ||
| 1312 | if (IsGenericAttribute(index)) { | ||
| 1313 | if (stage == ShaderType::Geometry) { | ||
| 1314 | return fmt::format("in_attr{}[{}][0].{}", GetGenericAttributeIndex(index), | ||
| 1315 | Visit(abuf->GetBuffer()), swizzle); | ||
| 1316 | } else { | ||
| 1317 | return fmt::format("{}.attrib[{}].{}", StageInputName(stage), | ||
| 1318 | GetGenericAttributeIndex(index), swizzle); | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | UNIMPLEMENTED_MSG("Unimplemented input attribute={}", static_cast<int>(index)); | ||
| 1322 | break; | ||
| 1323 | } | ||
| 1324 | return "{0, 0, 0, 0}.x"; | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | if (const auto cbuf = std::get_if<CbufNode>(&*node)) { | ||
| 1328 | std::string offset_string; | ||
| 1329 | const auto& offset = cbuf->GetOffset(); | ||
| 1330 | if (const auto imm = std::get_if<ImmediateNode>(&*offset)) { | ||
| 1331 | offset_string = std::to_string(imm->GetValue()); | ||
| 1332 | } else { | ||
| 1333 | offset_string = Visit(offset); | ||
| 1334 | } | ||
| 1335 | std::string temporary = AllocTemporary(); | ||
| 1336 | AddLine("LDC.F32 {}, cbuf{}[{}];", temporary, cbuf->GetIndex(), offset_string); | ||
| 1337 | return temporary; | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | if (const auto gmem = std::get_if<GmemNode>(&*node)) { | ||
| 1341 | std::string temporary = AllocTemporary(); | ||
| 1342 | AddLine("SUB.U {}, {}, {};", temporary, Visit(gmem->GetRealAddress()), | ||
| 1343 | Visit(gmem->GetBaseAddress())); | ||
| 1344 | AddLine("LDB.U32 {}, {}[{}];", temporary, GlobalMemoryName(gmem->GetDescriptor()), | ||
| 1345 | temporary); | ||
| 1346 | return temporary; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | if (const auto lmem = std::get_if<LmemNode>(&*node)) { | ||
| 1350 | std::string temporary = Visit(lmem->GetAddress()); | ||
| 1351 | AddLine("SHR.U {}, {}, 2;", temporary, temporary); | ||
| 1352 | AddLine("MOV.U {}, lmem[{}].x;", temporary, temporary); | ||
| 1353 | return temporary; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | if (const auto smem = std::get_if<SmemNode>(&*node)) { | ||
| 1357 | std::string temporary = Visit(smem->GetAddress()); | ||
| 1358 | AddLine("LDS.U32 {}, shared_mem[{}];", temporary, temporary); | ||
| 1359 | return temporary; | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { | ||
| 1363 | const std::size_t index = static_cast<std::size_t>(internal_flag->GetFlag()); | ||
| 1364 | return fmt::format("{}.x", INTERNAL_FLAG_NAMES[index]); | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { | ||
| 1368 | if (const auto amend_index = conditional->GetAmendIndex()) { | ||
| 1369 | Visit(ir.GetAmendNode(*amend_index)); | ||
| 1370 | } | ||
| 1371 | AddLine("MOVC.U RC.x, {};", Visit(conditional->GetCondition())); | ||
| 1372 | AddLine("IF NE.x;"); | ||
| 1373 | VisitBlock(conditional->GetCode()); | ||
| 1374 | AddLine("ENDIF;"); | ||
| 1375 | return {}; | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | if (const auto cmt = std::get_if<CommentNode>(&*node)) { | ||
| 1379 | // Uncommenting this will generate invalid code. GLASM lacks comments. | ||
| 1380 | // AddLine("// {}", cmt->GetText()); | ||
| 1381 | return {}; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | UNIMPLEMENTED(); | ||
| 1385 | return {}; | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | std::pair<std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operation) { | ||
| 1389 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1390 | UNIMPLEMENTED_IF(meta.sampler.is_indexed); | ||
| 1391 | UNIMPLEMENTED_IF(meta.sampler.is_shadow && meta.sampler.is_array && | ||
| 1392 | meta.sampler.type == Tegra::Shader::TextureType::TextureCube); | ||
| 1393 | |||
| 1394 | const std::size_t count = operation.GetOperandsCount(); | ||
| 1395 | std::string temporary = AllocVectorTemporary(); | ||
| 1396 | std::size_t i = 0; | ||
| 1397 | for (; i < count; ++i) { | ||
| 1398 | AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), Visit(operation[i])); | ||
| 1399 | } | ||
| 1400 | if (meta.sampler.is_array) { | ||
| 1401 | AddLine("I2F.S {}.{}, {};", temporary, Swizzle(i++), Visit(meta.array)); | ||
| 1402 | } | ||
| 1403 | if (meta.sampler.is_shadow) { | ||
| 1404 | AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i++), Visit(meta.depth_compare)); | ||
| 1405 | } | ||
| 1406 | return {std::move(temporary), i}; | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | std::string ARBDecompiler::BuildAoffi(Operation operation) { | ||
| 1410 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1411 | if (meta.aoffi.empty()) { | ||
| 1412 | return {}; | ||
| 1413 | } | ||
| 1414 | const std::string temporary = AllocVectorTemporary(); | ||
| 1415 | std::size_t i = 0; | ||
| 1416 | for (auto& node : meta.aoffi) { | ||
| 1417 | AddLine("MOV.S {}.{}, {};", temporary, Swizzle(i++), Visit(node)); | ||
| 1418 | } | ||
| 1419 | return fmt::format(", offset({})", temporary); | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | void ARBDecompiler::Exit() { | ||
| 1423 | if (stage != ShaderType::Fragment) { | ||
| 1424 | AddLine("RET;"); | ||
| 1425 | return; | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | const auto safe_get_register = [this](u32 reg) -> std::string { | ||
| 1429 | // TODO(Rodrigo): Replace with contains once C++20 releases | ||
| 1430 | const auto& used_registers = ir.GetRegisters(); | ||
| 1431 | if (used_registers.find(reg) != used_registers.end()) { | ||
| 1432 | return fmt::format("R{}.x", reg); | ||
| 1433 | } | ||
| 1434 | return "{0, 0, 0, 0}.x"; | ||
| 1435 | }; | ||
| 1436 | |||
| 1437 | const auto& header = ir.GetHeader(); | ||
| 1438 | u32 current_reg = 0; | ||
| 1439 | for (u32 rt = 0; rt < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; ++rt) { | ||
| 1440 | for (u32 component = 0; component < 4; ++component) { | ||
| 1441 | if (!header.ps.IsColorComponentOutputEnabled(rt, component)) { | ||
| 1442 | continue; | ||
| 1443 | } | ||
| 1444 | AddLine("MOV.F result_color{}.{}, {};", rt, Swizzle(component), | ||
| 1445 | safe_get_register(current_reg)); | ||
| 1446 | ++current_reg; | ||
| 1447 | } | ||
| 1448 | } | ||
| 1449 | if (header.ps.omap.depth) { | ||
| 1450 | AddLine("MOV.F result.depth.z, {};", safe_get_register(current_reg + 1)); | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | AddLine("RET;"); | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | std::string ARBDecompiler::Assign(Operation operation) { | ||
| 1457 | const Node& dest = operation[0]; | ||
| 1458 | const Node& src = operation[1]; | ||
| 1459 | |||
| 1460 | std::string dest_name; | ||
| 1461 | if (const auto gpr = std::get_if<GprNode>(&*dest)) { | ||
| 1462 | if (gpr->GetIndex() == Register::ZeroIndex) { | ||
| 1463 | // Writing to Register::ZeroIndex is a no op | ||
| 1464 | return {}; | ||
| 1465 | } | ||
| 1466 | dest_name = fmt::format("R{}.x", gpr->GetIndex()); | ||
| 1467 | } else if (const auto abuf = std::get_if<AbufNode>(&*dest)) { | ||
| 1468 | const u32 element = abuf->GetElement(); | ||
| 1469 | const char swizzle = Swizzle(element); | ||
| 1470 | switch (const Attribute::Index index = abuf->GetIndex()) { | ||
| 1471 | case Attribute::Index::Position: | ||
| 1472 | dest_name = fmt::format("result.position.{}", swizzle); | ||
| 1473 | break; | ||
| 1474 | case Attribute::Index::LayerViewportPointSize: | ||
| 1475 | switch (element) { | ||
| 1476 | case 0: | ||
| 1477 | UNIMPLEMENTED(); | ||
| 1478 | return {}; | ||
| 1479 | case 1: | ||
| 1480 | case 2: | ||
| 1481 | if (!device.HasNvViewportArray2()) { | ||
| 1482 | LOG_ERROR( | ||
| 1483 | Render_OpenGL, | ||
| 1484 | "NV_viewport_array2 is missing. Maxwell gen 2 or better is required."); | ||
| 1485 | return {}; | ||
| 1486 | } | ||
| 1487 | dest_name = element == 1 ? "result.layer.x" : "result.viewport.x"; | ||
| 1488 | break; | ||
| 1489 | case 3: | ||
| 1490 | dest_name = "result.pointsize.x"; | ||
| 1491 | break; | ||
| 1492 | } | ||
| 1493 | break; | ||
| 1494 | case Attribute::Index::ClipDistances0123: | ||
| 1495 | dest_name = fmt::format("result.clip[{}].x", element); | ||
| 1496 | break; | ||
| 1497 | case Attribute::Index::ClipDistances4567: | ||
| 1498 | dest_name = fmt::format("result.clip[{}].x", element + 4); | ||
| 1499 | break; | ||
| 1500 | default: | ||
| 1501 | if (!IsGenericAttribute(index)) { | ||
| 1502 | UNREACHABLE(); | ||
| 1503 | return {}; | ||
| 1504 | } | ||
| 1505 | dest_name = | ||
| 1506 | fmt::format("result.attrib[{}].{}", GetGenericAttributeIndex(index), swizzle); | ||
| 1507 | break; | ||
| 1508 | } | ||
| 1509 | } else if (const auto lmem = std::get_if<LmemNode>(&*dest)) { | ||
| 1510 | const std::string address = Visit(lmem->GetAddress()); | ||
| 1511 | AddLine("SHR.U {}, {}, 2;", address, address); | ||
| 1512 | dest_name = fmt::format("lmem[{}].x", address); | ||
| 1513 | } else if (const auto smem = std::get_if<SmemNode>(&*dest)) { | ||
| 1514 | AddLine("STS.U32 {}, shared_mem[{}];", Visit(src), Visit(smem->GetAddress())); | ||
| 1515 | ResetTemporaries(); | ||
| 1516 | return {}; | ||
| 1517 | } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { | ||
| 1518 | const std::string temporary = AllocTemporary(); | ||
| 1519 | AddLine("SUB.U {}, {}, {};", temporary, Visit(gmem->GetRealAddress()), | ||
| 1520 | Visit(gmem->GetBaseAddress())); | ||
| 1521 | AddLine("STB.U32 {}, {}[{}];", Visit(src), GlobalMemoryName(gmem->GetDescriptor()), | ||
| 1522 | temporary); | ||
| 1523 | ResetTemporaries(); | ||
| 1524 | return {}; | ||
| 1525 | } else { | ||
| 1526 | UNREACHABLE(); | ||
| 1527 | ResetTemporaries(); | ||
| 1528 | return {}; | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | AddLine("MOV.U {}, {};", dest_name, Visit(src)); | ||
| 1532 | ResetTemporaries(); | ||
| 1533 | return {}; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | std::string ARBDecompiler::Select(Operation operation) { | ||
| 1537 | std::string temporary = AllocTemporary(); | ||
| 1538 | AddLine("CMP.S {}, {}, {}, {};", temporary, Visit(operation[0]), Visit(operation[1]), | ||
| 1539 | Visit(operation[2])); | ||
| 1540 | return temporary; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | std::string ARBDecompiler::FClamp(Operation operation) { | ||
| 1544 | // 1.0f in hex, replace with std::bit_cast on C++20 | ||
| 1545 | static constexpr u32 POSITIVE_ONE = 0x3f800000; | ||
| 1546 | |||
| 1547 | std::string temporary = AllocTemporary(); | ||
| 1548 | const Node& value = operation[0]; | ||
| 1549 | const Node& low = operation[1]; | ||
| 1550 | const Node& high = operation[2]; | ||
| 1551 | const auto* const imm_low = std::get_if<ImmediateNode>(&*low); | ||
| 1552 | const auto* const imm_high = std::get_if<ImmediateNode>(&*high); | ||
| 1553 | if (imm_low && imm_high && imm_low->GetValue() == 0 && imm_high->GetValue() == POSITIVE_ONE) { | ||
| 1554 | AddLine("MOV.F32.SAT {}, {};", temporary, Visit(value)); | ||
| 1555 | } else { | ||
| 1556 | AddLine("MIN.F {}, {}, {};", temporary, Visit(value), Visit(high)); | ||
| 1557 | AddLine("MAX.F {}, {}, {};", temporary, temporary, Visit(low)); | ||
| 1558 | } | ||
| 1559 | return temporary; | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | std::string ARBDecompiler::FCastHalf0(Operation operation) { | ||
| 1563 | const std::string temporary = AllocVectorTemporary(); | ||
| 1564 | AddLine("UP2H.F {}.x, {};", temporary, Visit(operation[0])); | ||
| 1565 | return fmt::format("{}.x", temporary); | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | std::string ARBDecompiler::FCastHalf1(Operation operation) { | ||
| 1569 | const std::string temporary = AllocVectorTemporary(); | ||
| 1570 | AddLine("UP2H.F {}.y, {};", temporary, Visit(operation[0])); | ||
| 1571 | AddLine("MOV {}.x, {}.y;", temporary, temporary); | ||
| 1572 | return fmt::format("{}.x", temporary); | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | std::string ARBDecompiler::FSqrt(Operation operation) { | ||
| 1576 | std::string temporary = AllocTemporary(); | ||
| 1577 | AddLine("RSQ.F32 {}, {};", temporary, Visit(operation[0])); | ||
| 1578 | AddLine("RCP.F32 {}, {};", temporary, temporary); | ||
| 1579 | return temporary; | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | std::string ARBDecompiler::FSwizzleAdd(Operation operation) { | ||
| 1583 | const std::string temporary = AllocVectorTemporary(); | ||
| 1584 | if (!device.HasWarpIntrinsics()) { | ||
| 1585 | LOG_ERROR(Render_OpenGL, | ||
| 1586 | "NV_shader_thread_shuffle is missing. Kepler or better is required."); | ||
| 1587 | AddLine("ADD.F {}.x, {}, {};", temporary, Visit(operation[0]), Visit(operation[1])); | ||
| 1588 | return fmt::format("{}.x", temporary); | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | AddLine("AND.U {}.z, {}.threadid, 3;", temporary, StageInputName(stage)); | ||
| 1592 | AddLine("SHL.U {}.z, {}.z, 1;", temporary, temporary); | ||
| 1593 | AddLine("SHR.U {}.z, {}, {}.z;", temporary, Visit(operation[2]), temporary); | ||
| 1594 | AddLine("AND.U {}.z, {}.z, 3;", temporary, temporary); | ||
| 1595 | AddLine("MUL.F32 {}.x, {}, FSWZA[{}.z];", temporary, Visit(operation[0]), temporary); | ||
| 1596 | AddLine("MUL.F32 {}.y, {}, FSWZB[{}.z];", temporary, Visit(operation[1]), temporary); | ||
| 1597 | AddLine("ADD.F32 {}.x, {}.x, {}.y;", temporary, temporary, temporary); | ||
| 1598 | return fmt::format("{}.x", temporary); | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | std::string ARBDecompiler::HAdd2(Operation operation) { | ||
| 1602 | const std::string tmp1 = AllocVectorTemporary(); | ||
| 1603 | const std::string tmp2 = AllocVectorTemporary(); | ||
| 1604 | AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0])); | ||
| 1605 | AddLine("UP2H.F {}.xy, {};", tmp2, Visit(operation[1])); | ||
| 1606 | AddLine("ADD.F16 {}, {}, {};", tmp1, tmp1, tmp2); | ||
| 1607 | AddLine("PK2H.F {}.x, {};", tmp1, tmp1); | ||
| 1608 | return fmt::format("{}.x", tmp1); | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | std::string ARBDecompiler::HMul2(Operation operation) { | ||
| 1612 | const std::string tmp1 = AllocVectorTemporary(); | ||
| 1613 | const std::string tmp2 = AllocVectorTemporary(); | ||
| 1614 | AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0])); | ||
| 1615 | AddLine("UP2H.F {}.xy, {};", tmp2, Visit(operation[1])); | ||
| 1616 | AddLine("MUL.F16 {}, {}, {};", tmp1, tmp1, tmp2); | ||
| 1617 | AddLine("PK2H.F {}.x, {};", tmp1, tmp1); | ||
| 1618 | return fmt::format("{}.x", tmp1); | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | std::string ARBDecompiler::HFma2(Operation operation) { | ||
| 1622 | const std::string tmp1 = AllocVectorTemporary(); | ||
| 1623 | const std::string tmp2 = AllocVectorTemporary(); | ||
| 1624 | const std::string tmp3 = AllocVectorTemporary(); | ||
| 1625 | AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0])); | ||
| 1626 | AddLine("UP2H.F {}.xy, {};", tmp2, Visit(operation[1])); | ||
| 1627 | AddLine("UP2H.F {}.xy, {};", tmp3, Visit(operation[2])); | ||
| 1628 | AddLine("MAD.F16 {}, {}, {}, {};", tmp1, tmp1, tmp2, tmp3); | ||
| 1629 | AddLine("PK2H.F {}.x, {};", tmp1, tmp1); | ||
| 1630 | return fmt::format("{}.x", tmp1); | ||
| 1631 | } | ||
| 1632 | |||
| 1633 | std::string ARBDecompiler::HAbsolute(Operation operation) { | ||
| 1634 | const std::string temporary = AllocVectorTemporary(); | ||
| 1635 | AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0])); | ||
| 1636 | AddLine("PK2H.F {}.x, |{}|;", temporary, temporary); | ||
| 1637 | return fmt::format("{}.x", temporary); | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | std::string ARBDecompiler::HNegate(Operation operation) { | ||
| 1641 | const std::string temporary = AllocVectorTemporary(); | ||
| 1642 | AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0])); | ||
| 1643 | AddLine("MOVC.S RC.x, {};", Visit(operation[1])); | ||
| 1644 | AddLine("MOV.F {}.x (NE.x), -{}.x;", temporary, temporary); | ||
| 1645 | AddLine("MOVC.S RC.x, {};", Visit(operation[2])); | ||
| 1646 | AddLine("MOV.F {}.y (NE.x), -{}.y;", temporary, temporary); | ||
| 1647 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1648 | return fmt::format("{}.x", temporary); | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | std::string ARBDecompiler::HClamp(Operation operation) { | ||
| 1652 | const std::string tmp1 = AllocVectorTemporary(); | ||
| 1653 | const std::string tmp2 = AllocVectorTemporary(); | ||
| 1654 | AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0])); | ||
| 1655 | AddLine("MOV.U {}.x, {};", tmp2, Visit(operation[1])); | ||
| 1656 | AddLine("MOV.U {}.y, {}.x;", tmp2, tmp2); | ||
| 1657 | AddLine("MAX.F {}, {}, {};", tmp1, tmp1, tmp2); | ||
| 1658 | AddLine("MOV.U {}.x, {};", tmp2, Visit(operation[2])); | ||
| 1659 | AddLine("MOV.U {}.y, {}.x;", tmp2, tmp2); | ||
| 1660 | AddLine("MIN.F {}, {}, {};", tmp1, tmp1, tmp2); | ||
| 1661 | AddLine("PK2H.F {}.x, {};", tmp1, tmp1); | ||
| 1662 | return fmt::format("{}.x", tmp1); | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | std::string ARBDecompiler::HCastFloat(Operation operation) { | ||
| 1666 | const std::string temporary = AllocVectorTemporary(); | ||
| 1667 | AddLine("MOV.F {}.y, {{0, 0, 0, 0}};", temporary); | ||
| 1668 | AddLine("MOV.F {}.x, {};", temporary, Visit(operation[0])); | ||
| 1669 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1670 | return fmt::format("{}.x", temporary); | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | std::string ARBDecompiler::HUnpack(Operation operation) { | ||
| 1674 | const std::string operand = Visit(operation[0]); | ||
| 1675 | switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) { | ||
| 1676 | case Tegra::Shader::HalfType::H0_H1: | ||
| 1677 | return operand; | ||
| 1678 | case Tegra::Shader::HalfType::F32: { | ||
| 1679 | const std::string temporary = AllocVectorTemporary(); | ||
| 1680 | AddLine("MOV.U {}.x, {};", temporary, operand); | ||
| 1681 | AddLine("MOV.U {}.y, {}.x;", temporary, temporary); | ||
| 1682 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1683 | return fmt::format("{}.x", temporary); | ||
| 1684 | } | ||
| 1685 | case Tegra::Shader::HalfType::H0_H0: { | ||
| 1686 | const std::string temporary = AllocVectorTemporary(); | ||
| 1687 | AddLine("UP2H.F {}.xy, {};", temporary, operand); | ||
| 1688 | AddLine("MOV.U {}.y, {}.x;", temporary, temporary); | ||
| 1689 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1690 | return fmt::format("{}.x", temporary); | ||
| 1691 | } | ||
| 1692 | case Tegra::Shader::HalfType::H1_H1: { | ||
| 1693 | const std::string temporary = AllocVectorTemporary(); | ||
| 1694 | AddLine("UP2H.F {}.xy, {};", temporary, operand); | ||
| 1695 | AddLine("MOV.U {}.x, {}.y;", temporary, temporary); | ||
| 1696 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1697 | return fmt::format("{}.x", temporary); | ||
| 1698 | } | ||
| 1699 | } | ||
| 1700 | UNREACHABLE(); | ||
| 1701 | return "{0, 0, 0, 0}.x"; | ||
| 1702 | } | ||
| 1703 | |||
| 1704 | std::string ARBDecompiler::HMergeF32(Operation operation) { | ||
| 1705 | const std::string temporary = AllocVectorTemporary(); | ||
| 1706 | AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0])); | ||
| 1707 | return fmt::format("{}.x", temporary); | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | std::string ARBDecompiler::HMergeH0(Operation operation) { | ||
| 1711 | const std::string temporary = AllocVectorTemporary(); | ||
| 1712 | AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0])); | ||
| 1713 | AddLine("UP2H.F {}.zw, {};", temporary, Visit(operation[1])); | ||
| 1714 | AddLine("MOV.U {}.x, {}.z;", temporary, temporary); | ||
| 1715 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1716 | return fmt::format("{}.x", temporary); | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | std::string ARBDecompiler::HMergeH1(Operation operation) { | ||
| 1720 | const std::string temporary = AllocVectorTemporary(); | ||
| 1721 | AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0])); | ||
| 1722 | AddLine("UP2H.F {}.zw, {};", temporary, Visit(operation[1])); | ||
| 1723 | AddLine("MOV.U {}.y, {}.w;", temporary, temporary); | ||
| 1724 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1725 | return fmt::format("{}.x", temporary); | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | std::string ARBDecompiler::HPack2(Operation operation) { | ||
| 1729 | const std::string temporary = AllocVectorTemporary(); | ||
| 1730 | AddLine("MOV.U {}.x, {};", temporary, Visit(operation[0])); | ||
| 1731 | AddLine("MOV.U {}.y, {};", temporary, Visit(operation[1])); | ||
| 1732 | AddLine("PK2H.F {}.x, {};", temporary, temporary); | ||
| 1733 | return fmt::format("{}.x", temporary); | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | std::string ARBDecompiler::LogicalAssign(Operation operation) { | ||
| 1737 | const Node& dest = operation[0]; | ||
| 1738 | const Node& src = operation[1]; | ||
| 1739 | |||
| 1740 | std::string target; | ||
| 1741 | |||
| 1742 | if (const auto pred = std::get_if<PredicateNode>(&*dest)) { | ||
| 1743 | ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment"); | ||
| 1744 | |||
| 1745 | const Tegra::Shader::Pred index = pred->GetIndex(); | ||
| 1746 | switch (index) { | ||
| 1747 | case Tegra::Shader::Pred::NeverExecute: | ||
| 1748 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 1749 | // Writing to these predicates is a no-op | ||
| 1750 | return {}; | ||
| 1751 | } | ||
| 1752 | target = fmt::format("P{}.x", static_cast<u64>(index)); | ||
| 1753 | } else if (const auto internal_flag = std::get_if<InternalFlagNode>(&*dest)) { | ||
| 1754 | const std::size_t index = static_cast<std::size_t>(internal_flag->GetFlag()); | ||
| 1755 | target = fmt::format("{}.x", INTERNAL_FLAG_NAMES[index]); | ||
| 1756 | } else { | ||
| 1757 | UNREACHABLE(); | ||
| 1758 | ResetTemporaries(); | ||
| 1759 | return {}; | ||
| 1760 | } | ||
| 1761 | |||
| 1762 | AddLine("MOV.U {}, {};", target, Visit(src)); | ||
| 1763 | ResetTemporaries(); | ||
| 1764 | return {}; | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | std::string ARBDecompiler::LogicalPick2(Operation operation) { | ||
| 1768 | std::string temporary = AllocTemporary(); | ||
| 1769 | const u32 index = std::get<ImmediateNode>(*operation[1]).GetValue(); | ||
| 1770 | AddLine("MOV.U {}, {}.{};", temporary, Visit(operation[0]), Swizzle(index)); | ||
| 1771 | return temporary; | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | std::string ARBDecompiler::LogicalAnd2(Operation operation) { | ||
| 1775 | std::string temporary = AllocTemporary(); | ||
| 1776 | const std::string op = Visit(operation[0]); | ||
| 1777 | AddLine("AND.U {}, {}.x, {}.y;", temporary, op, op); | ||
| 1778 | return temporary; | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | std::string ARBDecompiler::FloatOrdered(Operation operation) { | ||
| 1782 | std::string temporary = AllocTemporary(); | ||
| 1783 | AddLine("MOVC.F32 RC.x, {};", Visit(operation[0])); | ||
| 1784 | AddLine("MOVC.F32 RC.y, {};", Visit(operation[1])); | ||
| 1785 | AddLine("MOV.S {}, -1;", temporary); | ||
| 1786 | AddLine("MOV.S {} (NAN.x), 0;", temporary); | ||
| 1787 | AddLine("MOV.S {} (NAN.y), 0;", temporary); | ||
| 1788 | return temporary; | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | std::string ARBDecompiler::FloatUnordered(Operation operation) { | ||
| 1792 | std::string temporary = AllocTemporary(); | ||
| 1793 | AddLine("MOVC.F32 RC.x, {};", Visit(operation[0])); | ||
| 1794 | AddLine("MOVC.F32 RC.y, {};", Visit(operation[1])); | ||
| 1795 | AddLine("MOV.S {}, 0;", temporary); | ||
| 1796 | AddLine("MOV.S {} (NAN.x), -1;", temporary); | ||
| 1797 | AddLine("MOV.S {} (NAN.y), -1;", temporary); | ||
| 1798 | return temporary; | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | std::string ARBDecompiler::LogicalAddCarry(Operation operation) { | ||
| 1802 | std::string temporary = AllocTemporary(); | ||
| 1803 | AddLine("ADDC.U RC, {}, {};", Visit(operation[0]), Visit(operation[1])); | ||
| 1804 | AddLine("MOV.S {}, 0;", temporary); | ||
| 1805 | AddLine("IF CF.x;"); | ||
| 1806 | AddLine("MOV.S {}, -1;", temporary); | ||
| 1807 | AddLine("ENDIF;"); | ||
| 1808 | return temporary; | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | std::string ARBDecompiler::Texture(Operation operation) { | ||
| 1812 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1813 | const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; | ||
| 1814 | const auto [temporary, swizzle] = BuildCoords(operation); | ||
| 1815 | |||
| 1816 | std::string_view opcode = "TEX"; | ||
| 1817 | std::string extra; | ||
| 1818 | if (meta.bias) { | ||
| 1819 | ASSERT(!meta.lod); | ||
| 1820 | opcode = "TXB"; | ||
| 1821 | |||
| 1822 | if (swizzle < 4) { | ||
| 1823 | AddLine("MOV.F {}.w, {};", temporary, Visit(meta.bias)); | ||
| 1824 | } else { | ||
| 1825 | const std::string bias = AllocTemporary(); | ||
| 1826 | AddLine("MOV.F {}, {};", bias, Visit(meta.bias)); | ||
| 1827 | extra = fmt::format(" {},", bias); | ||
| 1828 | } | ||
| 1829 | } | ||
| 1830 | if (meta.lod) { | ||
| 1831 | ASSERT(!meta.bias); | ||
| 1832 | opcode = "TXL"; | ||
| 1833 | |||
| 1834 | if (swizzle < 4) { | ||
| 1835 | AddLine("MOV.F {}.w, {};", temporary, Visit(meta.lod)); | ||
| 1836 | } else { | ||
| 1837 | const std::string lod = AllocTemporary(); | ||
| 1838 | AddLine("MOV.F {}, {};", lod, Visit(meta.lod)); | ||
| 1839 | extra = fmt::format(" {},", lod); | ||
| 1840 | } | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | AddLine("{}.F {}, {},{} texture[{}], {}{};", opcode, temporary, temporary, extra, sampler_id, | ||
| 1844 | TextureType(meta), BuildAoffi(operation)); | ||
| 1845 | AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); | ||
| 1846 | return fmt::format("{}.x", temporary); | ||
| 1847 | } | ||
| 1848 | |||
| 1849 | std::string ARBDecompiler::TextureGather(Operation operation) { | ||
| 1850 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1851 | const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; | ||
| 1852 | const auto [temporary, swizzle] = BuildCoords(operation); | ||
| 1853 | |||
| 1854 | std::string comp; | ||
| 1855 | if (!meta.sampler.is_shadow) { | ||
| 1856 | const auto& immediate = std::get<ImmediateNode>(*meta.component); | ||
| 1857 | comp = fmt::format(".{}", Swizzle(immediate.GetValue())); | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | AddLine("TXG.F {}, {}, texture[{}]{}, {}{};", temporary, temporary, sampler_id, comp, | ||
| 1861 | TextureType(meta), BuildAoffi(operation)); | ||
| 1862 | AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); | ||
| 1863 | return fmt::format("{}.x", temporary); | ||
| 1864 | } | ||
| 1865 | |||
| 1866 | std::string ARBDecompiler::TextureQueryDimensions(Operation operation) { | ||
| 1867 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1868 | const std::string temporary = AllocVectorTemporary(); | ||
| 1869 | const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; | ||
| 1870 | |||
| 1871 | ASSERT(!meta.sampler.is_array); | ||
| 1872 | |||
| 1873 | const std::string lod = operation.GetOperandsCount() > 0 ? Visit(operation[0]) : "0"; | ||
| 1874 | AddLine("TXQ {}, {}, texture[{}], {};", temporary, lod, sampler_id, TextureType(meta)); | ||
| 1875 | AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); | ||
| 1876 | return fmt::format("{}.x", temporary); | ||
| 1877 | } | ||
| 1878 | |||
| 1879 | std::string ARBDecompiler::TextureQueryLod(Operation operation) { | ||
| 1880 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1881 | const std::string temporary = AllocVectorTemporary(); | ||
| 1882 | const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; | ||
| 1883 | |||
| 1884 | ASSERT(!meta.sampler.is_array); | ||
| 1885 | |||
| 1886 | const std::size_t count = operation.GetOperandsCount(); | ||
| 1887 | for (std::size_t i = 0; i < count; ++i) { | ||
| 1888 | AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), Visit(operation[i])); | ||
| 1889 | } | ||
| 1890 | AddLine("LOD.F {}, {}, texture[{}], {};", temporary, temporary, sampler_id, TextureType(meta)); | ||
| 1891 | AddLine("MUL.F32 {}, {}, {{256, 256, 0, 0}};", temporary, temporary); | ||
| 1892 | AddLine("TRUNC.S {}, {};", temporary, temporary); | ||
| 1893 | AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); | ||
| 1894 | return fmt::format("{}.x", temporary); | ||
| 1895 | } | ||
| 1896 | |||
| 1897 | std::string ARBDecompiler::TexelFetch(Operation operation) { | ||
| 1898 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1899 | const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; | ||
| 1900 | const auto [temporary, swizzle] = BuildCoords(operation); | ||
| 1901 | |||
| 1902 | if (!meta.sampler.is_buffer) { | ||
| 1903 | ASSERT(swizzle < 4); | ||
| 1904 | AddLine("MOV.F {}.w, {};", temporary, Visit(meta.lod)); | ||
| 1905 | } | ||
| 1906 | AddLine("TXF.F {}, {}, texture[{}], {}{};", temporary, temporary, sampler_id, TextureType(meta), | ||
| 1907 | BuildAoffi(operation)); | ||
| 1908 | AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); | ||
| 1909 | return fmt::format("{}.x", temporary); | ||
| 1910 | } | ||
| 1911 | |||
| 1912 | std::string ARBDecompiler::TextureGradient(Operation operation) { | ||
| 1913 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1914 | const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; | ||
| 1915 | const std::string ddx = AllocVectorTemporary(); | ||
| 1916 | const std::string ddy = AllocVectorTemporary(); | ||
| 1917 | const std::string coord = BuildCoords(operation).first; | ||
| 1918 | |||
| 1919 | const std::size_t num_components = meta.derivates.size() / 2; | ||
| 1920 | for (std::size_t index = 0; index < num_components; ++index) { | ||
| 1921 | const char swizzle = Swizzle(index); | ||
| 1922 | AddLine("MOV.F {}.{}, {};", ddx, swizzle, Visit(meta.derivates[index * 2])); | ||
| 1923 | AddLine("MOV.F {}.{}, {};", ddy, swizzle, Visit(meta.derivates[index * 2 + 1])); | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | const std::string_view result = coord; | ||
| 1927 | AddLine("TXD.F {}, {}, {}, {}, texture[{}], {}{};", result, coord, ddx, ddy, sampler_id, | ||
| 1928 | TextureType(meta), BuildAoffi(operation)); | ||
| 1929 | AddLine("MOV.F {}.x, {}.{};", result, result, Swizzle(meta.element)); | ||
| 1930 | return fmt::format("{}.x", result); | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | std::string ARBDecompiler::ImageLoad(Operation operation) { | ||
| 1934 | const auto& meta = std::get<MetaImage>(operation.GetMeta()); | ||
| 1935 | const u32 image_id = device.GetBaseBindings(stage).image + meta.image.index; | ||
| 1936 | const std::size_t count = operation.GetOperandsCount(); | ||
| 1937 | const std::string_view type = ImageType(meta.image.type); | ||
| 1938 | |||
| 1939 | const std::string temporary = AllocVectorTemporary(); | ||
| 1940 | for (std::size_t i = 0; i < count; ++i) { | ||
| 1941 | AddLine("MOV.S {}.{}, {};", temporary, Swizzle(i), Visit(operation[i])); | ||
| 1942 | } | ||
| 1943 | AddLine("LOADIM.F {}, {}, image[{}], {};", temporary, temporary, image_id, type); | ||
| 1944 | AddLine("MOV.F {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); | ||
| 1945 | return fmt::format("{}.x", temporary); | ||
| 1946 | } | ||
| 1947 | |||
| 1948 | std::string ARBDecompiler::ImageStore(Operation operation) { | ||
| 1949 | const auto& meta = std::get<MetaImage>(operation.GetMeta()); | ||
| 1950 | const u32 image_id = device.GetBaseBindings(stage).image + meta.image.index; | ||
| 1951 | const std::size_t num_coords = operation.GetOperandsCount(); | ||
| 1952 | const std::size_t num_values = meta.values.size(); | ||
| 1953 | const std::string_view type = ImageType(meta.image.type); | ||
| 1954 | |||
| 1955 | const std::string coord = AllocVectorTemporary(); | ||
| 1956 | const std::string value = AllocVectorTemporary(); | ||
| 1957 | for (std::size_t i = 0; i < num_coords; ++i) { | ||
| 1958 | AddLine("MOV.S {}.{}, {};", coord, Swizzle(i), Visit(operation[i])); | ||
| 1959 | } | ||
| 1960 | for (std::size_t i = 0; i < num_values; ++i) { | ||
| 1961 | AddLine("MOV.F {}.{}, {};", value, Swizzle(i), Visit(meta.values[i])); | ||
| 1962 | } | ||
| 1963 | AddLine("STOREIM.F image[{}], {}, {}, {};", image_id, value, coord, type); | ||
| 1964 | return {}; | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | std::string ARBDecompiler::Branch(Operation operation) { | ||
| 1968 | const auto target = std::get<ImmediateNode>(*operation[0]); | ||
| 1969 | AddLine("MOV.U PC.x, {};", target.GetValue()); | ||
| 1970 | AddLine("CONT;"); | ||
| 1971 | return {}; | ||
| 1972 | } | ||
| 1973 | |||
| 1974 | std::string ARBDecompiler::BranchIndirect(Operation operation) { | ||
| 1975 | AddLine("MOV.U PC.x, {};", Visit(operation[0])); | ||
| 1976 | AddLine("CONT;"); | ||
| 1977 | return {}; | ||
| 1978 | } | ||
| 1979 | |||
| 1980 | std::string ARBDecompiler::PushFlowStack(Operation operation) { | ||
| 1981 | const auto stack = std::get<MetaStackClass>(operation.GetMeta()); | ||
| 1982 | const u32 target = std::get<ImmediateNode>(*operation[0]).GetValue(); | ||
| 1983 | const std::string_view stack_name = StackName(stack); | ||
| 1984 | AddLine("MOV.U {}[{}_TOP.x].x, {};", stack_name, stack_name, target); | ||
| 1985 | AddLine("ADD.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name); | ||
| 1986 | return {}; | ||
| 1987 | } | ||
| 1988 | |||
| 1989 | std::string ARBDecompiler::PopFlowStack(Operation operation) { | ||
| 1990 | const auto stack = std::get<MetaStackClass>(operation.GetMeta()); | ||
| 1991 | const std::string_view stack_name = StackName(stack); | ||
| 1992 | AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name); | ||
| 1993 | AddLine("MOV.U PC.x, {}[{}_TOP.x].x;", stack_name, stack_name); | ||
| 1994 | AddLine("CONT;"); | ||
| 1995 | return {}; | ||
| 1996 | } | ||
| 1997 | |||
| 1998 | std::string ARBDecompiler::Exit(Operation) { | ||
| 1999 | Exit(); | ||
| 2000 | return {}; | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | std::string ARBDecompiler::Discard(Operation) { | ||
| 2004 | AddLine("KIL TR;"); | ||
| 2005 | return {}; | ||
| 2006 | } | ||
| 2007 | |||
| 2008 | std::string ARBDecompiler::EmitVertex(Operation) { | ||
| 2009 | AddLine("EMIT;"); | ||
| 2010 | return {}; | ||
| 2011 | } | ||
| 2012 | |||
| 2013 | std::string ARBDecompiler::EndPrimitive(Operation) { | ||
| 2014 | AddLine("ENDPRIM;"); | ||
| 2015 | return {}; | ||
| 2016 | } | ||
| 2017 | |||
| 2018 | std::string ARBDecompiler::InvocationId(Operation) { | ||
| 2019 | return "primitive.invocation"; | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | std::string ARBDecompiler::YNegate(Operation) { | ||
| 2023 | LOG_WARNING(Render_OpenGL, "(STUBBED)"); | ||
| 2024 | const std::string temporary = AllocTemporary(); | ||
| 2025 | AddLine("MOV.F {}, 1;", temporary); | ||
| 2026 | return temporary; | ||
| 2027 | } | ||
| 2028 | |||
| 2029 | std::string ARBDecompiler::ThreadId(Operation) { | ||
| 2030 | return fmt::format("{}.threadid", StageInputName(stage)); | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | std::string ARBDecompiler::ShuffleIndexed(Operation operation) { | ||
| 2034 | if (!device.HasWarpIntrinsics()) { | ||
| 2035 | LOG_ERROR(Render_OpenGL, | ||
| 2036 | "NV_shader_thread_shuffle is missing. Kepler or better is required."); | ||
| 2037 | return Visit(operation[0]); | ||
| 2038 | } | ||
| 2039 | const std::string temporary = AllocVectorTemporary(); | ||
| 2040 | AddLine("SHFIDX.U {}, {}, {}, {{31, 0, 0, 0}};", temporary, Visit(operation[0]), | ||
| 2041 | Visit(operation[1])); | ||
| 2042 | AddLine("MOV.U {}.x, {}.y;", temporary, temporary); | ||
| 2043 | return fmt::format("{}.x", temporary); | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | std::string ARBDecompiler::Barrier(Operation) { | ||
| 2047 | if (!ir.IsDecompiled()) { | ||
| 2048 | LOG_ERROR(Render_OpenGL, "BAR used but shader is not decompiled"); | ||
| 2049 | return {}; | ||
| 2050 | } | ||
| 2051 | AddLine("BAR;"); | ||
| 2052 | return {}; | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | std::string ARBDecompiler::MemoryBarrierGroup(Operation) { | ||
| 2056 | AddLine("MEMBAR.CTA;"); | ||
| 2057 | return {}; | ||
| 2058 | } | ||
| 2059 | |||
| 2060 | std::string ARBDecompiler::MemoryBarrierGlobal(Operation) { | ||
| 2061 | AddLine("MEMBAR;"); | ||
| 2062 | return {}; | ||
| 2063 | } | ||
| 2064 | |||
| 2065 | } // Anonymous namespace | ||
| 2066 | |||
| 2067 | std::string DecompileAssemblyShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir, | ||
| 2068 | const VideoCommon::Shader::Registry& registry, | ||
| 2069 | Tegra::Engines::ShaderType stage, std::string_view identifier) { | ||
| 2070 | return ARBDecompiler(device, ir, registry, stage, identifier).Code(); | ||
| 2071 | } | ||
| 2072 | |||
| 2073 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.h b/src/video_core/renderer_opengl/gl_arb_decompiler.h new file mode 100644 index 000000000..6afc87220 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_arb_decompiler.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include <string_view> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Tegra::Engines { | ||
| 13 | enum class ShaderType : u32; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace VideoCommon::Shader { | ||
| 17 | class ShaderIR; | ||
| 18 | class Registry; | ||
| 19 | } // namespace VideoCommon::Shader | ||
| 20 | |||
| 21 | namespace OpenGL { | ||
| 22 | |||
| 23 | class Device; | ||
| 24 | |||
| 25 | std::string DecompileAssemblyShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir, | ||
| 26 | const VideoCommon::Shader::Registry& registry, | ||
| 27 | Tegra::Engines::ShaderType stage, std::string_view identifier); | ||
| 28 | |||
| 29 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 9964ea894..e461e4c70 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -22,22 +22,53 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; | |||
| 22 | 22 | ||
| 23 | MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); | 23 | MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); |
| 24 | 24 | ||
| 25 | CachedBufferBlock::CachedBufferBlock(VAddr cpu_addr, const std::size_t size) | 25 | Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size) |
| 26 | : VideoCommon::BufferBlock{cpu_addr, size} { | 26 | : VideoCommon::BufferBlock{cpu_addr, size} { |
| 27 | gl_buffer.Create(); | 27 | gl_buffer.Create(); |
| 28 | glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); | 28 | glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); |
| 29 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 30 | glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_WRITE); | ||
| 31 | glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | Buffer::~Buffer() = default; | ||
| 36 | |||
| 37 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) { | ||
| 38 | glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), | ||
| 39 | data); | ||
| 40 | } | ||
| 41 | |||
| 42 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) { | ||
| 43 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); | ||
| 44 | const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size); | ||
| 45 | const GLintptr gl_offset = static_cast<GLintptr>(offset); | ||
| 46 | if (read_buffer.handle == 0) { | ||
| 47 | read_buffer.Create(); | ||
| 48 | glNamedBufferData(read_buffer.handle, static_cast<GLsizeiptr>(Size()), nullptr, | ||
| 49 | GL_STREAM_READ); | ||
| 50 | } | ||
| 51 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); | ||
| 52 | glCopyNamedBufferSubData(gl_buffer.handle, read_buffer.handle, gl_offset, gl_offset, gl_size); | ||
| 53 | glGetNamedBufferSubData(read_buffer.handle, gl_offset, gl_size, data); | ||
| 29 | } | 54 | } |
| 30 | 55 | ||
| 31 | CachedBufferBlock::~CachedBufferBlock() = default; | 56 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 57 | std::size_t size) { | ||
| 58 | glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset), | ||
| 59 | static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); | ||
| 60 | } | ||
| 32 | 61 | ||
| 33 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, | 62 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, |
| 34 | const Device& device, std::size_t stream_size) | 63 | const Device& device_, std::size_t stream_size) |
| 35 | : GenericBufferCache{rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} { | 64 | : GenericBufferCache{rasterizer, system, |
| 65 | std::make_unique<OGLStreamBuffer>(device_, stream_size, true)}, | ||
| 66 | device{device_} { | ||
| 36 | if (!device.HasFastBufferSubData()) { | 67 | if (!device.HasFastBufferSubData()) { |
| 37 | return; | 68 | return; |
| 38 | } | 69 | } |
| 39 | 70 | ||
| 40 | static constexpr auto size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize); | 71 | static constexpr GLsizeiptr size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize); |
| 41 | glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); | 72 | glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); |
| 42 | for (const GLuint cbuf : cbufs) { | 73 | for (const GLuint cbuf : cbufs) { |
| 43 | glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW); | 74 | glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW); |
| @@ -48,44 +79,21 @@ OGLBufferCache::~OGLBufferCache() { | |||
| 48 | glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); | 79 | glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); |
| 49 | } | 80 | } |
| 50 | 81 | ||
| 51 | Buffer OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | 82 | std::shared_ptr<Buffer> OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { |
| 52 | return std::make_shared<CachedBufferBlock>(cpu_addr, size); | 83 | return std::make_shared<Buffer>(device, cpu_addr, size); |
| 53 | } | 84 | } |
| 54 | 85 | ||
| 55 | GLuint OGLBufferCache::ToHandle(const Buffer& buffer) { | 86 | OGLBufferCache::BufferInfo OGLBufferCache::GetEmptyBuffer(std::size_t) { |
| 56 | return buffer->GetHandle(); | 87 | return {0, 0, 0}; |
| 57 | } | ||
| 58 | |||
| 59 | GLuint OGLBufferCache::GetEmptyBuffer(std::size_t) { | ||
| 60 | return 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 64 | const u8* data) { | ||
| 65 | glNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset), | ||
| 66 | static_cast<GLsizeiptr>(size), data); | ||
| 67 | } | ||
| 68 | |||
| 69 | void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 70 | u8* data) { | ||
| 71 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); | ||
| 72 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); | ||
| 73 | glGetNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset), | ||
| 74 | static_cast<GLsizeiptr>(size), data); | ||
| 75 | } | ||
| 76 | |||
| 77 | void OGLBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 78 | std::size_t dst_offset, std::size_t size) { | ||
| 79 | glCopyNamedBufferSubData(src->GetHandle(), dst->GetHandle(), static_cast<GLintptr>(src_offset), | ||
| 80 | static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); | ||
| 81 | } | 88 | } |
| 82 | 89 | ||
| 83 | OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer, | 90 | OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer, |
| 84 | std::size_t size) { | 91 | std::size_t size) { |
| 85 | DEBUG_ASSERT(cbuf_cursor < std::size(cbufs)); | 92 | DEBUG_ASSERT(cbuf_cursor < std::size(cbufs)); |
| 86 | const GLuint& cbuf = cbufs[cbuf_cursor++]; | 93 | const GLuint cbuf = cbufs[cbuf_cursor++]; |
| 94 | |||
| 87 | glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer); | 95 | glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer); |
| 88 | return {cbuf, 0}; | 96 | return {cbuf, 0, 0}; |
| 89 | } | 97 | } |
| 90 | 98 | ||
| 91 | } // namespace OpenGL | 99 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index a9e86cfc7..88fdc0536 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/buffer_cache/buffer_cache.h" | 11 | #include "video_core/buffer_cache/buffer_cache.h" |
| 12 | #include "video_core/engines/maxwell_3d.h" | 12 | #include "video_core/engines/maxwell_3d.h" |
| 13 | #include "video_core/rasterizer_cache.h" | ||
| 14 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 13 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 15 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | 14 | #include "video_core/renderer_opengl/gl_stream_buffer.h" |
| 16 | 15 | ||
| @@ -24,57 +23,58 @@ class Device; | |||
| 24 | class OGLStreamBuffer; | 23 | class OGLStreamBuffer; |
| 25 | class RasterizerOpenGL; | 24 | class RasterizerOpenGL; |
| 26 | 25 | ||
| 27 | class CachedBufferBlock; | 26 | class Buffer : public VideoCommon::BufferBlock { |
| 27 | public: | ||
| 28 | explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); | ||
| 29 | ~Buffer(); | ||
| 28 | 30 | ||
| 29 | using Buffer = std::shared_ptr<CachedBufferBlock>; | 31 | void Upload(std::size_t offset, std::size_t size, const u8* data); |
| 30 | using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>; | ||
| 31 | 32 | ||
| 32 | class CachedBufferBlock : public VideoCommon::BufferBlock { | 33 | void Download(std::size_t offset, std::size_t size, u8* data); |
| 33 | public: | 34 | |
| 34 | explicit CachedBufferBlock(VAddr cpu_addr, const std::size_t size); | 35 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 35 | ~CachedBufferBlock(); | 36 | std::size_t size); |
| 36 | 37 | ||
| 37 | GLuint GetHandle() const { | 38 | GLuint Handle() const noexcept { |
| 38 | return gl_buffer.handle; | 39 | return gl_buffer.handle; |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 42 | u64 Address() const noexcept { | ||
| 43 | return gpu_address; | ||
| 44 | } | ||
| 45 | |||
| 41 | private: | 46 | private: |
| 42 | OGLBuffer gl_buffer; | 47 | OGLBuffer gl_buffer; |
| 48 | OGLBuffer read_buffer; | ||
| 49 | u64 gpu_address = 0; | ||
| 43 | }; | 50 | }; |
| 44 | 51 | ||
| 52 | using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>; | ||
| 45 | class OGLBufferCache final : public GenericBufferCache { | 53 | class OGLBufferCache final : public GenericBufferCache { |
| 46 | public: | 54 | public: |
| 47 | explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, | 55 | explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, |
| 48 | const Device& device, std::size_t stream_size); | 56 | const Device& device, std::size_t stream_size); |
| 49 | ~OGLBufferCache(); | 57 | ~OGLBufferCache(); |
| 50 | 58 | ||
| 51 | GLuint GetEmptyBuffer(std::size_t) override; | 59 | BufferInfo GetEmptyBuffer(std::size_t) override; |
| 52 | 60 | ||
| 53 | void Acquire() noexcept { | 61 | void Acquire() noexcept { |
| 54 | cbuf_cursor = 0; | 62 | cbuf_cursor = 0; |
| 55 | } | 63 | } |
| 56 | 64 | ||
| 57 | protected: | 65 | protected: |
| 58 | Buffer CreateBlock(VAddr cpu_addr, std::size_t size) override; | 66 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; |
| 59 | |||
| 60 | GLuint ToHandle(const Buffer& buffer) override; | ||
| 61 | |||
| 62 | void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 63 | const u8* data) override; | ||
| 64 | |||
| 65 | void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 66 | u8* data) override; | ||
| 67 | |||
| 68 | void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 69 | std::size_t dst_offset, std::size_t size) override; | ||
| 70 | 67 | ||
| 71 | BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override; | 68 | BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override; |
| 72 | 69 | ||
| 73 | private: | 70 | private: |
| 71 | static constexpr std::size_t NUM_CBUFS = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers * | ||
| 72 | Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; | ||
| 73 | |||
| 74 | const Device& device; | ||
| 75 | |||
| 74 | std::size_t cbuf_cursor = 0; | 76 | std::size_t cbuf_cursor = 0; |
| 75 | std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers * | 77 | std::array<GLuint, NUM_CBUFS> cbufs{}; |
| 76 | Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram> | ||
| 77 | cbufs; | ||
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | } // namespace OpenGL | 80 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 466a911db..c1f20f0ab 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <cstring> | 8 | #include <cstring> |
| 9 | #include <limits> | ||
| 9 | #include <optional> | 10 | #include <optional> |
| 10 | #include <vector> | 11 | #include <vector> |
| 11 | 12 | ||
| @@ -26,24 +27,27 @@ constexpr u32 ReservedUniformBlocks = 1; | |||
| 26 | 27 | ||
| 27 | constexpr u32 NumStages = 5; | 28 | constexpr u32 NumStages = 5; |
| 28 | 29 | ||
| 29 | constexpr std::array LimitUBOs = {GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, | 30 | constexpr std::array LimitUBOs = { |
| 30 | GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, | 31 | GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, |
| 31 | GL_MAX_GEOMETRY_UNIFORM_BLOCKS, GL_MAX_FRAGMENT_UNIFORM_BLOCKS}; | 32 | GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, GL_MAX_GEOMETRY_UNIFORM_BLOCKS, |
| 33 | GL_MAX_FRAGMENT_UNIFORM_BLOCKS, GL_MAX_COMPUTE_UNIFORM_BLOCKS}; | ||
| 32 | 34 | ||
| 33 | constexpr std::array LimitSSBOs = { | 35 | constexpr std::array LimitSSBOs = { |
| 34 | GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, | 36 | GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, |
| 35 | GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS, | 37 | GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS, |
| 36 | GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS}; | 38 | GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS}; |
| 37 | 39 | ||
| 38 | constexpr std::array LimitSamplers = { | 40 | constexpr std::array LimitSamplers = {GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, |
| 39 | GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, | 41 | GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, |
| 40 | GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, | 42 | GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, |
| 41 | GL_MAX_TEXTURE_IMAGE_UNITS}; | 43 | GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, |
| 44 | GL_MAX_TEXTURE_IMAGE_UNITS, | ||
| 45 | GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS}; | ||
| 42 | 46 | ||
| 43 | constexpr std::array LimitImages = {GL_MAX_VERTEX_IMAGE_UNIFORMS, | 47 | constexpr std::array LimitImages = { |
| 44 | GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, | 48 | GL_MAX_VERTEX_IMAGE_UNIFORMS, GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, |
| 45 | GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, | 49 | GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, GL_MAX_GEOMETRY_IMAGE_UNIFORMS, |
| 46 | GL_MAX_GEOMETRY_IMAGE_UNIFORMS, GL_MAX_FRAGMENT_IMAGE_UNIFORMS}; | 50 | GL_MAX_FRAGMENT_IMAGE_UNIFORMS, GL_MAX_COMPUTE_IMAGE_UNIFORMS}; |
| 47 | 51 | ||
| 48 | template <typename T> | 52 | template <typename T> |
| 49 | T GetInteger(GLenum pname) { | 53 | T GetInteger(GLenum pname) { |
| @@ -85,6 +89,13 @@ u32 Extract(u32& base, u32& num, u32 amount, std::optional<GLenum> limit = {}) { | |||
| 85 | return std::exchange(base, base + amount); | 89 | return std::exchange(base, base + amount); |
| 86 | } | 90 | } |
| 87 | 91 | ||
| 92 | std::array<u32, Tegra::Engines::MaxShaderTypes> BuildMaxUniformBuffers() noexcept { | ||
| 93 | std::array<u32, Tegra::Engines::MaxShaderTypes> max; | ||
| 94 | std::transform(LimitUBOs.begin(), LimitUBOs.end(), max.begin(), | ||
| 95 | [](GLenum pname) { return GetInteger<u32>(pname); }); | ||
| 96 | return max; | ||
| 97 | } | ||
| 98 | |||
| 88 | std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindings() noexcept { | 99 | std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindings() noexcept { |
| 89 | std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> bindings; | 100 | std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> bindings; |
| 90 | 101 | ||
| @@ -112,16 +123,24 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin | |||
| 112 | u32 num_images = GetInteger<u32>(GL_MAX_IMAGE_UNITS); | 123 | u32 num_images = GetInteger<u32>(GL_MAX_IMAGE_UNITS); |
| 113 | u32 base_images = 0; | 124 | u32 base_images = 0; |
| 114 | 125 | ||
| 115 | // Reserve more image bindings on fragment and vertex stages. | 126 | // GL_MAX_IMAGE_UNITS is guaranteed by the spec to have a minimum value of 8. |
| 127 | // Due to the limitation of GL_MAX_IMAGE_UNITS, reserve at least 4 image bindings on the | ||
| 128 | // fragment stage, and at least 1 for the rest of the stages. | ||
| 129 | // So far games are observed to use 1 image binding on vertex and 4 on fragment stages. | ||
| 130 | |||
| 131 | // Reserve at least 4 image bindings on the fragment stage. | ||
| 116 | bindings[4].image = | 132 | bindings[4].image = |
| 117 | Extract(base_images, num_images, num_images / NumStages + 2, LimitImages[4]); | 133 | Extract(base_images, num_images, std::max(4U, num_images / NumStages), LimitImages[4]); |
| 118 | bindings[0].image = | 134 | |
| 119 | Extract(base_images, num_images, num_images / NumStages + 1, LimitImages[0]); | 135 | // This is guaranteed to be at least 1. |
| 136 | const u32 total_extracted_images = num_images / (NumStages - 1); | ||
| 120 | 137 | ||
| 121 | // Reserve the other image bindings. | 138 | // Reserve the other image bindings. |
| 122 | const u32 total_extracted_images = num_images / (NumStages - 2); | 139 | for (std::size_t i = 0; i < NumStages; ++i) { |
| 123 | for (std::size_t i = 2; i < NumStages; ++i) { | ||
| 124 | const std::size_t stage = stage_swizzle[i]; | 140 | const std::size_t stage = stage_swizzle[i]; |
| 141 | if (stage == 4) { | ||
| 142 | continue; | ||
| 143 | } | ||
| 125 | bindings[stage].image = | 144 | bindings[stage].image = |
| 126 | Extract(base_images, num_images, total_extracted_images, LimitImages[stage]); | 145 | Extract(base_images, num_images, total_extracted_images, LimitImages[stage]); |
| 127 | } | 146 | } |
| @@ -133,6 +152,7 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin | |||
| 133 | } | 152 | } |
| 134 | 153 | ||
| 135 | bool IsASTCSupported() { | 154 | bool IsASTCSupported() { |
| 155 | static constexpr std::array targets = {GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY}; | ||
| 136 | static constexpr std::array formats = { | 156 | static constexpr std::array formats = { |
| 137 | GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR, | 157 | GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR, |
| 138 | GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR, | 158 | GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR, |
| @@ -149,25 +169,44 @@ bool IsASTCSupported() { | |||
| 149 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, | 169 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, |
| 150 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, | 170 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, |
| 151 | }; | 171 | }; |
| 152 | return std::find_if_not(formats.begin(), formats.end(), [](GLenum format) { | 172 | static constexpr std::array required_support = { |
| 153 | GLint supported; | 173 | GL_VERTEX_TEXTURE, GL_TESS_CONTROL_TEXTURE, GL_TESS_EVALUATION_TEXTURE, |
| 154 | glGetInternalformativ(GL_TEXTURE_2D, format, GL_INTERNALFORMAT_SUPPORTED, 1, | 174 | GL_GEOMETRY_TEXTURE, GL_FRAGMENT_TEXTURE, GL_COMPUTE_TEXTURE, |
| 155 | &supported); | 175 | }; |
| 156 | return supported == GL_TRUE; | 176 | |
| 157 | }) == formats.end(); | 177 | for (const GLenum target : targets) { |
| 178 | for (const GLenum format : formats) { | ||
| 179 | for (const GLenum support : required_support) { | ||
| 180 | GLint value; | ||
| 181 | glGetInternalformativ(target, format, support, 1, &value); | ||
| 182 | if (value != GL_FULL_SUPPORT) { | ||
| 183 | return false; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | return true; | ||
| 158 | } | 189 | } |
| 159 | 190 | ||
| 160 | } // Anonymous namespace | 191 | } // Anonymous namespace |
| 161 | 192 | ||
| 162 | Device::Device() : base_bindings{BuildBaseBindings()} { | 193 | Device::Device() |
| 194 | : max_uniform_buffers{BuildMaxUniformBuffers()}, base_bindings{BuildBaseBindings()} { | ||
| 163 | const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); | 195 | const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); |
| 164 | const auto renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); | 196 | const std::string_view renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); |
| 197 | const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); | ||
| 165 | const std::vector extensions = GetExtensions(); | 198 | const std::vector extensions = GetExtensions(); |
| 166 | 199 | ||
| 167 | const bool is_nvidia = vendor == "NVIDIA Corporation"; | 200 | const bool is_nvidia = vendor == "NVIDIA Corporation"; |
| 168 | const bool is_amd = vendor == "ATI Technologies Inc."; | 201 | const bool is_amd = vendor == "ATI Technologies Inc."; |
| 169 | const bool is_intel = vendor == "Intel"; | 202 | |
| 170 | const bool is_intel_proprietary = is_intel && std::strstr(renderer, "Mesa") == nullptr; | 203 | bool disable_fast_buffer_sub_data = false; |
| 204 | if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { | ||
| 205 | LOG_WARNING( | ||
| 206 | Render_OpenGL, | ||
| 207 | "Beta driver 443.24 is known to have issues. There might be performance issues."); | ||
| 208 | disable_fast_buffer_sub_data = true; | ||
| 209 | } | ||
| 171 | 210 | ||
| 172 | uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); | 211 | uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); |
| 173 | shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); | 212 | shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); |
| @@ -178,36 +217,43 @@ Device::Device() : base_bindings{BuildBaseBindings()} { | |||
| 178 | has_shader_ballot = GLAD_GL_ARB_shader_ballot; | 217 | has_shader_ballot = GLAD_GL_ARB_shader_ballot; |
| 179 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; | 218 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; |
| 180 | has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); | 219 | has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); |
| 220 | has_texture_shadow_lod = HasExtension(extensions, "GL_EXT_texture_shadow_lod"); | ||
| 181 | has_astc = IsASTCSupported(); | 221 | has_astc = IsASTCSupported(); |
| 182 | has_variable_aoffi = TestVariableAoffi(); | 222 | has_variable_aoffi = TestVariableAoffi(); |
| 183 | has_component_indexing_bug = is_amd; | 223 | has_component_indexing_bug = is_amd; |
| 184 | has_precise_bug = TestPreciseBug(); | 224 | has_precise_bug = TestPreciseBug(); |
| 185 | has_broken_compute = is_intel_proprietary; | 225 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; |
| 186 | has_fast_buffer_sub_data = is_nvidia; | 226 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; |
| 187 | use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && | 227 | |
| 188 | GLAD_GL_NV_compute_program5; | 228 | // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive |
| 229 | // uniform buffers as "push constants" | ||
| 230 | has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; | ||
| 231 | |||
| 232 | use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() && | ||
| 233 | GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 && | ||
| 234 | GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2; | ||
| 189 | 235 | ||
| 190 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); | 236 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); |
| 191 | LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); | 237 | LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); |
| 192 | LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); | 238 | LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); |
| 193 | 239 | ||
| 194 | if (Settings::values.use_assembly_shaders && !use_assembly_shaders) { | 240 | if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) { |
| 195 | LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); | 241 | LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); |
| 196 | } | 242 | } |
| 197 | } | 243 | } |
| 198 | 244 | ||
| 199 | Device::Device(std::nullptr_t) { | 245 | Device::Device(std::nullptr_t) { |
| 200 | uniform_buffer_alignment = 0; | 246 | max_uniform_buffers.fill(std::numeric_limits<u32>::max()); |
| 247 | uniform_buffer_alignment = 4; | ||
| 248 | shader_storage_alignment = 4; | ||
| 201 | max_vertex_attributes = 16; | 249 | max_vertex_attributes = 16; |
| 202 | max_varyings = 15; | 250 | max_varyings = 15; |
| 203 | has_warp_intrinsics = true; | 251 | has_warp_intrinsics = true; |
| 204 | has_shader_ballot = true; | 252 | has_shader_ballot = true; |
| 205 | has_vertex_viewport_layer = true; | 253 | has_vertex_viewport_layer = true; |
| 206 | has_image_load_formatted = true; | 254 | has_image_load_formatted = true; |
| 255 | has_texture_shadow_lod = true; | ||
| 207 | has_variable_aoffi = true; | 256 | has_variable_aoffi = true; |
| 208 | has_component_indexing_bug = false; | ||
| 209 | has_broken_compute = false; | ||
| 210 | has_precise_bug = false; | ||
| 211 | } | 257 | } |
| 212 | 258 | ||
| 213 | bool Device::TestVariableAoffi() { | 259 | bool Device::TestVariableAoffi() { |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index e915dbd86..e1d811966 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -24,6 +24,10 @@ public: | |||
| 24 | explicit Device(); | 24 | explicit Device(); |
| 25 | explicit Device(std::nullptr_t); | 25 | explicit Device(std::nullptr_t); |
| 26 | 26 | ||
| 27 | u32 GetMaxUniformBuffers(Tegra::Engines::ShaderType shader_type) const noexcept { | ||
| 28 | return max_uniform_buffers[static_cast<std::size_t>(shader_type)]; | ||
| 29 | } | ||
| 30 | |||
| 27 | const BaseBindings& GetBaseBindings(std::size_t stage_index) const noexcept { | 31 | const BaseBindings& GetBaseBindings(std::size_t stage_index) const noexcept { |
| 28 | return base_bindings[stage_index]; | 32 | return base_bindings[stage_index]; |
| 29 | } | 33 | } |
| @@ -64,6 +68,14 @@ public: | |||
| 64 | return has_image_load_formatted; | 68 | return has_image_load_formatted; |
| 65 | } | 69 | } |
| 66 | 70 | ||
| 71 | bool HasTextureShadowLod() const { | ||
| 72 | return has_texture_shadow_lod; | ||
| 73 | } | ||
| 74 | |||
| 75 | bool HasVertexBufferUnifiedMemory() const { | ||
| 76 | return has_vertex_buffer_unified_memory; | ||
| 77 | } | ||
| 78 | |||
| 67 | bool HasASTC() const { | 79 | bool HasASTC() const { |
| 68 | return has_astc; | 80 | return has_astc; |
| 69 | } | 81 | } |
| @@ -80,14 +92,14 @@ public: | |||
| 80 | return has_precise_bug; | 92 | return has_precise_bug; |
| 81 | } | 93 | } |
| 82 | 94 | ||
| 83 | bool HasBrokenCompute() const { | ||
| 84 | return has_broken_compute; | ||
| 85 | } | ||
| 86 | |||
| 87 | bool HasFastBufferSubData() const { | 95 | bool HasFastBufferSubData() const { |
| 88 | return has_fast_buffer_sub_data; | 96 | return has_fast_buffer_sub_data; |
| 89 | } | 97 | } |
| 90 | 98 | ||
| 99 | bool HasNvViewportArray2() const { | ||
| 100 | return has_nv_viewport_array2; | ||
| 101 | } | ||
| 102 | |||
| 91 | bool UseAssemblyShaders() const { | 103 | bool UseAssemblyShaders() const { |
| 92 | return use_assembly_shaders; | 104 | return use_assembly_shaders; |
| 93 | } | 105 | } |
| @@ -96,7 +108,8 @@ private: | |||
| 96 | static bool TestVariableAoffi(); | 108 | static bool TestVariableAoffi(); |
| 97 | static bool TestPreciseBug(); | 109 | static bool TestPreciseBug(); |
| 98 | 110 | ||
| 99 | std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings; | 111 | std::array<u32, Tegra::Engines::MaxShaderTypes> max_uniform_buffers{}; |
| 112 | std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings{}; | ||
| 100 | std::size_t uniform_buffer_alignment{}; | 113 | std::size_t uniform_buffer_alignment{}; |
| 101 | std::size_t shader_storage_alignment{}; | 114 | std::size_t shader_storage_alignment{}; |
| 102 | u32 max_vertex_attributes{}; | 115 | u32 max_vertex_attributes{}; |
| @@ -105,12 +118,14 @@ private: | |||
| 105 | bool has_shader_ballot{}; | 118 | bool has_shader_ballot{}; |
| 106 | bool has_vertex_viewport_layer{}; | 119 | bool has_vertex_viewport_layer{}; |
| 107 | bool has_image_load_formatted{}; | 120 | bool has_image_load_formatted{}; |
| 121 | bool has_texture_shadow_lod{}; | ||
| 122 | bool has_vertex_buffer_unified_memory{}; | ||
| 108 | bool has_astc{}; | 123 | bool has_astc{}; |
| 109 | bool has_variable_aoffi{}; | 124 | bool has_variable_aoffi{}; |
| 110 | bool has_component_indexing_bug{}; | 125 | bool has_component_indexing_bug{}; |
| 111 | bool has_precise_bug{}; | 126 | bool has_precise_bug{}; |
| 112 | bool has_broken_compute{}; | ||
| 113 | bool has_fast_buffer_sub_data{}; | 127 | bool has_fast_buffer_sub_data{}; |
| 128 | bool has_nv_viewport_array2{}; | ||
| 114 | bool use_assembly_shaders{}; | 129 | bool use_assembly_shaders{}; |
| 115 | }; | 130 | }; |
| 116 | 131 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 716d43e65..e960a0ef1 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 30 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 31 | #include "video_core/renderer_opengl/maxwell_to_gl.h" | 31 | #include "video_core/renderer_opengl/maxwell_to_gl.h" |
| 32 | #include "video_core/renderer_opengl/renderer_opengl.h" | 32 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 33 | #include "video_core/shader_cache.h" | ||
| 33 | 34 | ||
| 34 | namespace OpenGL { | 35 | namespace OpenGL { |
| 35 | 36 | ||
| @@ -54,15 +55,34 @@ MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255 | |||
| 54 | 55 | ||
| 55 | namespace { | 56 | namespace { |
| 56 | 57 | ||
| 57 | constexpr std::size_t NumSupportedVertexAttributes = 16; | 58 | constexpr std::size_t NUM_CONST_BUFFERS_PER_STAGE = 18; |
| 59 | constexpr std::size_t NUM_CONST_BUFFERS_BYTES_PER_STAGE = | ||
| 60 | NUM_CONST_BUFFERS_PER_STAGE * Maxwell::MaxConstBufferSize; | ||
| 61 | constexpr std::size_t TOTAL_CONST_BUFFER_BYTES = | ||
| 62 | NUM_CONST_BUFFERS_BYTES_PER_STAGE * Maxwell::MaxShaderStage; | ||
| 63 | |||
| 64 | constexpr std::size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16; | ||
| 65 | constexpr std::size_t NUM_SUPPORTED_VERTEX_BINDINGS = 16; | ||
| 58 | 66 | ||
| 59 | template <typename Engine, typename Entry> | 67 | template <typename Engine, typename Entry> |
| 60 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 68 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, |
| 61 | ShaderType shader_type, std::size_t index = 0) { | 69 | ShaderType shader_type, std::size_t index = 0) { |
| 70 | if constexpr (std::is_same_v<Entry, SamplerEntry>) { | ||
| 71 | if (entry.is_separated) { | ||
| 72 | const u32 buffer_1 = entry.buffer; | ||
| 73 | const u32 buffer_2 = entry.secondary_buffer; | ||
| 74 | const u32 offset_1 = entry.offset; | ||
| 75 | const u32 offset_2 = entry.secondary_offset; | ||
| 76 | const u32 handle_1 = engine.AccessConstBuffer32(shader_type, buffer_1, offset_1); | ||
| 77 | const u32 handle_2 = engine.AccessConstBuffer32(shader_type, buffer_2, offset_2); | ||
| 78 | return engine.GetTextureInfo(handle_1 | handle_2); | ||
| 79 | } | ||
| 80 | } | ||
| 62 | if (entry.is_bindless) { | 81 | if (entry.is_bindless) { |
| 63 | const auto tex_handle = engine.AccessConstBuffer32(shader_type, entry.buffer, entry.offset); | 82 | const u32 handle = engine.AccessConstBuffer32(shader_type, entry.buffer, entry.offset); |
| 64 | return engine.GetTextureInfo(tex_handle); | 83 | return engine.GetTextureInfo(handle); |
| 65 | } | 84 | } |
| 85 | |||
| 66 | const auto& gpu_profile = engine.AccessGuestDriverProfile(); | 86 | const auto& gpu_profile = engine.AccessGuestDriverProfile(); |
| 67 | const u32 offset = entry.offset + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize()); | 87 | const u32 offset = entry.offset + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize()); |
| 68 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { | 88 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { |
| @@ -87,6 +107,34 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, | |||
| 87 | return buffer.size; | 107 | return buffer.size; |
| 88 | } | 108 | } |
| 89 | 109 | ||
| 110 | /// Translates hardware transform feedback indices | ||
| 111 | /// @param location Hardware location | ||
| 112 | /// @return Pair of ARB_transform_feedback3 token stream first and third arguments | ||
| 113 | /// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt | ||
| 114 | std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) { | ||
| 115 | const u8 index = location / 4; | ||
| 116 | if (index >= 8 && index <= 39) { | ||
| 117 | return {GL_GENERIC_ATTRIB_NV, index - 8}; | ||
| 118 | } | ||
| 119 | if (index >= 48 && index <= 55) { | ||
| 120 | return {GL_TEXTURE_COORD_NV, index - 48}; | ||
| 121 | } | ||
| 122 | switch (index) { | ||
| 123 | case 7: | ||
| 124 | return {GL_POSITION, 0}; | ||
| 125 | case 40: | ||
| 126 | return {GL_PRIMARY_COLOR_NV, 0}; | ||
| 127 | case 41: | ||
| 128 | return {GL_SECONDARY_COLOR_NV, 0}; | ||
| 129 | case 42: | ||
| 130 | return {GL_BACK_PRIMARY_COLOR_NV, 0}; | ||
| 131 | case 43: | ||
| 132 | return {GL_BACK_SECONDARY_COLOR_NV, 0}; | ||
| 133 | } | ||
| 134 | UNIMPLEMENTED_MSG("index={}", static_cast<int>(index)); | ||
| 135 | return {GL_POSITION, 0}; | ||
| 136 | } | ||
| 137 | |||
| 90 | void oglEnable(GLenum cap, bool state) { | 138 | void oglEnable(GLenum cap, bool state) { |
| 91 | (state ? glEnable : glDisable)(cap); | 139 | (state ? glEnable : glDisable)(cap); |
| 92 | } | 140 | } |
| @@ -104,6 +152,9 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind | |||
| 104 | screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} { | 152 | screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} { |
| 105 | CheckExtensions(); | 153 | CheckExtensions(); |
| 106 | 154 | ||
| 155 | unified_uniform_buffer.Create(); | ||
| 156 | glNamedBufferStorage(unified_uniform_buffer.handle, TOTAL_CONST_BUFFER_BYTES, nullptr, 0); | ||
| 157 | |||
| 107 | if (device.UseAssemblyShaders()) { | 158 | if (device.UseAssemblyShaders()) { |
| 108 | glCreateBuffers(static_cast<GLsizei>(staging_cbufs.size()), staging_cbufs.data()); | 159 | glCreateBuffers(static_cast<GLsizei>(staging_cbufs.size()), staging_cbufs.data()); |
| 109 | for (const GLuint cbuf : staging_cbufs) { | 160 | for (const GLuint cbuf : staging_cbufs) { |
| @@ -143,7 +194,7 @@ void RasterizerOpenGL::SetupVertexFormat() { | |||
| 143 | // avoid OpenGL errors. | 194 | // avoid OpenGL errors. |
| 144 | // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't | 195 | // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't |
| 145 | // assume every shader uses them all. | 196 | // assume every shader uses them all. |
| 146 | for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { | 197 | for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { |
| 147 | if (!flags[Dirty::VertexFormat0 + index]) { | 198 | if (!flags[Dirty::VertexFormat0 + index]) { |
| 148 | continue; | 199 | continue; |
| 149 | } | 200 | } |
| @@ -162,9 +213,10 @@ void RasterizerOpenGL::SetupVertexFormat() { | |||
| 162 | if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || | 213 | if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || |
| 163 | attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { | 214 | attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { |
| 164 | glVertexAttribIFormat(gl_index, attrib.ComponentCount(), | 215 | glVertexAttribIFormat(gl_index, attrib.ComponentCount(), |
| 165 | MaxwellToGL::VertexType(attrib), attrib.offset); | 216 | MaxwellToGL::VertexFormat(attrib), attrib.offset); |
| 166 | } else { | 217 | } else { |
| 167 | glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), | 218 | glVertexAttribFormat(gl_index, attrib.ComponentCount(), |
| 219 | MaxwellToGL::VertexFormat(attrib), | ||
| 168 | attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); | 220 | attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); |
| 169 | } | 221 | } |
| 170 | glVertexAttribBinding(gl_index, attrib.buffer); | 222 | glVertexAttribBinding(gl_index, attrib.buffer); |
| @@ -181,9 +233,11 @@ void RasterizerOpenGL::SetupVertexBuffer() { | |||
| 181 | 233 | ||
| 182 | MICROPROFILE_SCOPE(OpenGL_VB); | 234 | MICROPROFILE_SCOPE(OpenGL_VB); |
| 183 | 235 | ||
| 236 | const bool use_unified_memory = device.HasVertexBufferUnifiedMemory(); | ||
| 237 | |||
| 184 | // Upload all guest vertex arrays sequentially to our buffer | 238 | // Upload all guest vertex arrays sequentially to our buffer |
| 185 | const auto& regs = gpu.regs; | 239 | const auto& regs = gpu.regs; |
| 186 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 240 | for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) { |
| 187 | if (!flags[Dirty::VertexBuffer0 + index]) { | 241 | if (!flags[Dirty::VertexBuffer0 + index]) { |
| 188 | continue; | 242 | continue; |
| 189 | } | 243 | } |
| @@ -196,16 +250,25 @@ void RasterizerOpenGL::SetupVertexBuffer() { | |||
| 196 | 250 | ||
| 197 | const GPUVAddr start = vertex_array.StartAddress(); | 251 | const GPUVAddr start = vertex_array.StartAddress(); |
| 198 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); | 252 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); |
| 199 | |||
| 200 | ASSERT(end >= start); | 253 | ASSERT(end >= start); |
| 254 | |||
| 255 | const GLuint gl_index = static_cast<GLuint>(index); | ||
| 201 | const u64 size = end - start; | 256 | const u64 size = end - start; |
| 202 | if (size == 0) { | 257 | if (size == 0) { |
| 203 | glBindVertexBuffer(static_cast<GLuint>(index), 0, 0, vertex_array.stride); | 258 | glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride); |
| 259 | if (use_unified_memory) { | ||
| 260 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, 0, 0); | ||
| 261 | } | ||
| 204 | continue; | 262 | continue; |
| 205 | } | 263 | } |
| 206 | const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); | 264 | const auto info = buffer_cache.UploadMemory(start, size); |
| 207 | glBindVertexBuffer(static_cast<GLuint>(index), vertex_buffer, vertex_buffer_offset, | 265 | if (use_unified_memory) { |
| 208 | vertex_array.stride); | 266 | glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride); |
| 267 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, | ||
| 268 | info.address + info.offset, size); | ||
| 269 | } else { | ||
| 270 | glBindVertexBuffer(gl_index, info.handle, info.offset, vertex_array.stride); | ||
| 271 | } | ||
| 209 | } | 272 | } |
| 210 | } | 273 | } |
| 211 | 274 | ||
| @@ -218,7 +281,7 @@ void RasterizerOpenGL::SetupVertexInstances() { | |||
| 218 | flags[Dirty::VertexInstances] = false; | 281 | flags[Dirty::VertexInstances] = false; |
| 219 | 282 | ||
| 220 | const auto& regs = gpu.regs; | 283 | const auto& regs = gpu.regs; |
| 221 | for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { | 284 | for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { |
| 222 | if (!flags[Dirty::VertexInstance0 + index]) { | 285 | if (!flags[Dirty::VertexInstance0 + index]) { |
| 223 | continue; | 286 | continue; |
| 224 | } | 287 | } |
| @@ -235,9 +298,9 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() { | |||
| 235 | MICROPROFILE_SCOPE(OpenGL_Index); | 298 | MICROPROFILE_SCOPE(OpenGL_Index); |
| 236 | const auto& regs = system.GPU().Maxwell3D().regs; | 299 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 237 | const std::size_t size = CalculateIndexBufferSize(); | 300 | const std::size_t size = CalculateIndexBufferSize(); |
| 238 | const auto [buffer, offset] = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); | 301 | const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); |
| 239 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); | 302 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle); |
| 240 | return offset; | 303 | return info.offset; |
| 241 | } | 304 | } |
| 242 | 305 | ||
| 243 | void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | 306 | void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { |
| @@ -273,7 +336,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 273 | continue; | 336 | continue; |
| 274 | } | 337 | } |
| 275 | 338 | ||
| 276 | Shader shader{shader_cache.GetStageProgram(program)}; | 339 | Shader* const shader = shader_cache.GetStageProgram(program); |
| 277 | 340 | ||
| 278 | if (device.UseAssemblyShaders()) { | 341 | if (device.UseAssemblyShaders()) { |
| 279 | // Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this | 342 | // Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this |
| @@ -567,7 +630,16 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 567 | (Maxwell::MaxConstBufferSize + device.GetUniformBufferAlignment()); | 630 | (Maxwell::MaxConstBufferSize + device.GetUniformBufferAlignment()); |
| 568 | 631 | ||
| 569 | // Prepare the vertex array. | 632 | // Prepare the vertex array. |
| 570 | buffer_cache.Map(buffer_size); | 633 | const bool invalidated = buffer_cache.Map(buffer_size); |
| 634 | |||
| 635 | if (invalidated) { | ||
| 636 | // When the stream buffer has been invalidated, we have to consider vertex buffers as dirty | ||
| 637 | auto& dirty = gpu.dirty.flags; | ||
| 638 | dirty[Dirty::VertexBuffers] = true; | ||
| 639 | for (int index = Dirty::VertexBuffer0; index <= Dirty::VertexBuffer31; ++index) { | ||
| 640 | dirty[index] = true; | ||
| 641 | } | ||
| 642 | } | ||
| 571 | 643 | ||
| 572 | // Prepare vertex array format. | 644 | // Prepare vertex array format. |
| 573 | SetupVertexFormat(); | 645 | SetupVertexFormat(); |
| @@ -584,9 +656,9 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 584 | if (!device.UseAssemblyShaders()) { | 656 | if (!device.UseAssemblyShaders()) { |
| 585 | MaxwellUniformData ubo; | 657 | MaxwellUniformData ubo; |
| 586 | ubo.SetFromRegs(gpu); | 658 | ubo.SetFromRegs(gpu); |
| 587 | const auto [buffer, offset] = | 659 | const auto info = |
| 588 | buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); | 660 | buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); |
| 589 | glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, buffer, offset, | 661 | glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset, |
| 590 | static_cast<GLsizeiptr>(sizeof(ubo))); | 662 | static_cast<GLsizeiptr>(sizeof(ubo))); |
| 591 | } | 663 | } |
| 592 | 664 | ||
| @@ -655,10 +727,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 655 | } | 727 | } |
| 656 | 728 | ||
| 657 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { | 729 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { |
| 658 | if (device.HasBrokenCompute()) { | ||
| 659 | return; | ||
| 660 | } | ||
| 661 | |||
| 662 | buffer_cache.Acquire(); | 730 | buffer_cache.Acquire(); |
| 663 | current_cbuf = 0; | 731 | current_cbuf = 0; |
| 664 | 732 | ||
| @@ -837,7 +905,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, | |||
| 837 | return true; | 905 | return true; |
| 838 | } | 906 | } |
| 839 | 907 | ||
| 840 | void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader) { | 908 | void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* shader) { |
| 841 | static constexpr std::array PARAMETER_LUT = { | 909 | static constexpr std::array PARAMETER_LUT = { |
| 842 | GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, | 910 | GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, |
| 843 | GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV, | 911 | GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV, |
| @@ -846,41 +914,62 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, const Shad | |||
| 846 | MICROPROFILE_SCOPE(OpenGL_UBO); | 914 | MICROPROFILE_SCOPE(OpenGL_UBO); |
| 847 | const auto& stages = system.GPU().Maxwell3D().state.shader_stages; | 915 | const auto& stages = system.GPU().Maxwell3D().state.shader_stages; |
| 848 | const auto& shader_stage = stages[stage_index]; | 916 | const auto& shader_stage = stages[stage_index]; |
| 917 | const auto& entries = shader->GetEntries(); | ||
| 918 | const bool use_unified = entries.use_unified_uniforms; | ||
| 919 | const std::size_t base_unified_offset = stage_index * NUM_CONST_BUFFERS_BYTES_PER_STAGE; | ||
| 849 | 920 | ||
| 850 | u32 binding = | 921 | const auto base_bindings = device.GetBaseBindings(stage_index); |
| 851 | device.UseAssemblyShaders() ? 0 : device.GetBaseBindings(stage_index).uniform_buffer; | 922 | u32 binding = device.UseAssemblyShaders() ? 0 : base_bindings.uniform_buffer; |
| 852 | for (const auto& entry : shader->GetEntries().const_buffers) { | 923 | for (const auto& entry : entries.const_buffers) { |
| 853 | const auto& buffer = shader_stage.const_buffers[entry.GetIndex()]; | 924 | const u32 index = entry.GetIndex(); |
| 854 | SetupConstBuffer(PARAMETER_LUT[stage_index], binding++, buffer, entry); | 925 | const auto& buffer = shader_stage.const_buffers[index]; |
| 926 | SetupConstBuffer(PARAMETER_LUT[stage_index], binding, buffer, entry, use_unified, | ||
| 927 | base_unified_offset + index * Maxwell::MaxConstBufferSize); | ||
| 928 | ++binding; | ||
| 929 | } | ||
| 930 | if (use_unified) { | ||
| 931 | const u32 index = static_cast<u32>(base_bindings.shader_storage_buffer + | ||
| 932 | entries.global_memory_entries.size()); | ||
| 933 | glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, unified_uniform_buffer.handle, | ||
| 934 | base_unified_offset, NUM_CONST_BUFFERS_BYTES_PER_STAGE); | ||
| 855 | } | 935 | } |
| 856 | } | 936 | } |
| 857 | 937 | ||
| 858 | void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) { | 938 | void RasterizerOpenGL::SetupComputeConstBuffers(Shader* kernel) { |
| 859 | MICROPROFILE_SCOPE(OpenGL_UBO); | 939 | MICROPROFILE_SCOPE(OpenGL_UBO); |
| 860 | const auto& launch_desc = system.GPU().KeplerCompute().launch_description; | 940 | const auto& launch_desc = system.GPU().KeplerCompute().launch_description; |
| 941 | const auto& entries = kernel->GetEntries(); | ||
| 942 | const bool use_unified = entries.use_unified_uniforms; | ||
| 861 | 943 | ||
| 862 | u32 binding = 0; | 944 | u32 binding = 0; |
| 863 | for (const auto& entry : kernel->GetEntries().const_buffers) { | 945 | for (const auto& entry : entries.const_buffers) { |
| 864 | const auto& config = launch_desc.const_buffer_config[entry.GetIndex()]; | 946 | const auto& config = launch_desc.const_buffer_config[entry.GetIndex()]; |
| 865 | const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value(); | 947 | const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value(); |
| 866 | Tegra::Engines::ConstBufferInfo buffer; | 948 | Tegra::Engines::ConstBufferInfo buffer; |
| 867 | buffer.address = config.Address(); | 949 | buffer.address = config.Address(); |
| 868 | buffer.size = config.size; | 950 | buffer.size = config.size; |
| 869 | buffer.enabled = mask[entry.GetIndex()]; | 951 | buffer.enabled = mask[entry.GetIndex()]; |
| 870 | SetupConstBuffer(GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV, binding++, buffer, entry); | 952 | SetupConstBuffer(GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV, binding, buffer, entry, |
| 953 | use_unified, entry.GetIndex() * Maxwell::MaxConstBufferSize); | ||
| 954 | ++binding; | ||
| 955 | } | ||
| 956 | if (use_unified) { | ||
| 957 | const GLuint index = static_cast<GLuint>(entries.global_memory_entries.size()); | ||
| 958 | glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, unified_uniform_buffer.handle, 0, | ||
| 959 | NUM_CONST_BUFFERS_BYTES_PER_STAGE); | ||
| 871 | } | 960 | } |
| 872 | } | 961 | } |
| 873 | 962 | ||
| 874 | void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, | 963 | void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, |
| 875 | const Tegra::Engines::ConstBufferInfo& buffer, | 964 | const Tegra::Engines::ConstBufferInfo& buffer, |
| 876 | const ConstBufferEntry& entry) { | 965 | const ConstBufferEntry& entry, bool use_unified, |
| 966 | std::size_t unified_offset) { | ||
| 877 | if (!buffer.enabled) { | 967 | if (!buffer.enabled) { |
| 878 | // Set values to zero to unbind buffers | 968 | // Set values to zero to unbind buffers |
| 879 | if (device.UseAssemblyShaders()) { | 969 | if (device.UseAssemblyShaders()) { |
| 880 | glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0); | 970 | glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0); |
| 881 | } else { | 971 | } else { |
| 882 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, | 972 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, 0, 0, sizeof(float)); |
| 883 | buffer_cache.GetEmptyBuffer(sizeof(float)), 0, sizeof(float)); | ||
| 884 | } | 973 | } |
| 885 | return; | 974 | return; |
| 886 | } | 975 | } |
| @@ -889,23 +978,33 @@ void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, | |||
| 889 | // UBO alignment requirements. | 978 | // UBO alignment requirements. |
| 890 | const std::size_t size = Common::AlignUp(GetConstBufferSize(buffer, entry), sizeof(GLvec4)); | 979 | const std::size_t size = Common::AlignUp(GetConstBufferSize(buffer, entry), sizeof(GLvec4)); |
| 891 | 980 | ||
| 892 | const auto alignment = device.GetUniformBufferAlignment(); | 981 | const bool fast_upload = !use_unified && device.HasFastBufferSubData(); |
| 893 | auto [cbuf, offset] = buffer_cache.UploadMemory(buffer.address, size, alignment, false, | 982 | |
| 894 | device.HasFastBufferSubData()); | 983 | const std::size_t alignment = use_unified ? 4 : device.GetUniformBufferAlignment(); |
| 895 | if (!device.UseAssemblyShaders()) { | 984 | const GPUVAddr gpu_addr = buffer.address; |
| 896 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, cbuf, offset, size); | 985 | auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, false, fast_upload); |
| 986 | |||
| 987 | if (device.UseAssemblyShaders()) { | ||
| 988 | UNIMPLEMENTED_IF(use_unified); | ||
| 989 | if (info.offset != 0) { | ||
| 990 | const GLuint staging_cbuf = staging_cbufs[current_cbuf++]; | ||
| 991 | glCopyNamedBufferSubData(info.handle, staging_cbuf, info.offset, 0, size); | ||
| 992 | info.handle = staging_cbuf; | ||
| 993 | info.offset = 0; | ||
| 994 | } | ||
| 995 | glBindBufferRangeNV(stage, binding, info.handle, info.offset, size); | ||
| 897 | return; | 996 | return; |
| 898 | } | 997 | } |
| 899 | if (offset != 0) { | 998 | |
| 900 | const GLuint staging_cbuf = staging_cbufs[current_cbuf++]; | 999 | if (use_unified) { |
| 901 | glCopyNamedBufferSubData(cbuf, staging_cbuf, offset, 0, size); | 1000 | glCopyNamedBufferSubData(info.handle, unified_uniform_buffer.handle, info.offset, |
| 902 | cbuf = staging_cbuf; | 1001 | unified_offset, size); |
| 903 | offset = 0; | 1002 | } else { |
| 1003 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, info.handle, info.offset, size); | ||
| 904 | } | 1004 | } |
| 905 | glBindBufferRangeNV(stage, binding, cbuf, offset, size); | ||
| 906 | } | 1005 | } |
| 907 | 1006 | ||
| 908 | void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader) { | 1007 | void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* shader) { |
| 909 | auto& gpu{system.GPU()}; | 1008 | auto& gpu{system.GPU()}; |
| 910 | auto& memory_manager{gpu.MemoryManager()}; | 1009 | auto& memory_manager{gpu.MemoryManager()}; |
| 911 | const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]}; | 1010 | const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]}; |
| @@ -920,7 +1019,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, const Shad | |||
| 920 | } | 1019 | } |
| 921 | } | 1020 | } |
| 922 | 1021 | ||
| 923 | void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) { | 1022 | void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) { |
| 924 | auto& gpu{system.GPU()}; | 1023 | auto& gpu{system.GPU()}; |
| 925 | auto& memory_manager{gpu.MemoryManager()}; | 1024 | auto& memory_manager{gpu.MemoryManager()}; |
| 926 | const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config}; | 1025 | const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config}; |
| @@ -937,13 +1036,12 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) { | |||
| 937 | void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, | 1036 | void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, |
| 938 | GPUVAddr gpu_addr, std::size_t size) { | 1037 | GPUVAddr gpu_addr, std::size_t size) { |
| 939 | const auto alignment{device.GetShaderStorageBufferAlignment()}; | 1038 | const auto alignment{device.GetShaderStorageBufferAlignment()}; |
| 940 | const auto [ssbo, buffer_offset] = | 1039 | const auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written); |
| 941 | buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written); | 1040 | glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, info.handle, info.offset, |
| 942 | glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, ssbo, buffer_offset, | ||
| 943 | static_cast<GLsizeiptr>(size)); | 1041 | static_cast<GLsizeiptr>(size)); |
| 944 | } | 1042 | } |
| 945 | 1043 | ||
| 946 | void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader& shader) { | 1044 | void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader) { |
| 947 | MICROPROFILE_SCOPE(OpenGL_Texture); | 1045 | MICROPROFILE_SCOPE(OpenGL_Texture); |
| 948 | const auto& maxwell3d = system.GPU().Maxwell3D(); | 1046 | const auto& maxwell3d = system.GPU().Maxwell3D(); |
| 949 | u32 binding = device.GetBaseBindings(stage_index).sampler; | 1047 | u32 binding = device.GetBaseBindings(stage_index).sampler; |
| @@ -956,7 +1054,7 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader& | |||
| 956 | } | 1054 | } |
| 957 | } | 1055 | } |
| 958 | 1056 | ||
| 959 | void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) { | 1057 | void RasterizerOpenGL::SetupComputeTextures(Shader* kernel) { |
| 960 | MICROPROFILE_SCOPE(OpenGL_Texture); | 1058 | MICROPROFILE_SCOPE(OpenGL_Texture); |
| 961 | const auto& compute = system.GPU().KeplerCompute(); | 1059 | const auto& compute = system.GPU().KeplerCompute(); |
| 962 | u32 binding = 0; | 1060 | u32 binding = 0; |
| @@ -985,7 +1083,7 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu | |||
| 985 | } | 1083 | } |
| 986 | } | 1084 | } |
| 987 | 1085 | ||
| 988 | void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) { | 1086 | void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, Shader* shader) { |
| 989 | const auto& maxwell3d = system.GPU().Maxwell3D(); | 1087 | const auto& maxwell3d = system.GPU().Maxwell3D(); |
| 990 | u32 binding = device.GetBaseBindings(stage_index).image; | 1088 | u32 binding = device.GetBaseBindings(stage_index).image; |
| 991 | for (const auto& entry : shader->GetEntries().images) { | 1089 | for (const auto& entry : shader->GetEntries().images) { |
| @@ -995,7 +1093,7 @@ void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& sh | |||
| 995 | } | 1093 | } |
| 996 | } | 1094 | } |
| 997 | 1095 | ||
| 998 | void RasterizerOpenGL::SetupComputeImages(const Shader& shader) { | 1096 | void RasterizerOpenGL::SetupComputeImages(Shader* shader) { |
| 999 | const auto& compute = system.GPU().KeplerCompute(); | 1097 | const auto& compute = system.GPU().KeplerCompute(); |
| 1000 | u32 binding = 0; | 1098 | u32 binding = 0; |
| 1001 | for (const auto& entry : shader->GetEntries().images) { | 1099 | for (const auto& entry : shader->GetEntries().images) { |
| @@ -1024,6 +1122,26 @@ void RasterizerOpenGL::SyncViewport() { | |||
| 1024 | const auto& regs = gpu.regs; | 1122 | const auto& regs = gpu.regs; |
| 1025 | 1123 | ||
| 1026 | const bool dirty_viewport = flags[Dirty::Viewports]; | 1124 | const bool dirty_viewport = flags[Dirty::Viewports]; |
| 1125 | const bool dirty_clip_control = flags[Dirty::ClipControl]; | ||
| 1126 | |||
| 1127 | if (dirty_clip_control || flags[Dirty::FrontFace]) { | ||
| 1128 | flags[Dirty::FrontFace] = false; | ||
| 1129 | |||
| 1130 | GLenum mode = MaxwellToGL::FrontFace(regs.front_face); | ||
| 1131 | if (regs.screen_y_control.triangle_rast_flip != 0 && | ||
| 1132 | regs.viewport_transform[0].scale_y < 0.0f) { | ||
| 1133 | switch (mode) { | ||
| 1134 | case GL_CW: | ||
| 1135 | mode = GL_CCW; | ||
| 1136 | break; | ||
| 1137 | case GL_CCW: | ||
| 1138 | mode = GL_CW; | ||
| 1139 | break; | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | glFrontFace(mode); | ||
| 1143 | } | ||
| 1144 | |||
| 1027 | if (dirty_viewport || flags[Dirty::ClipControl]) { | 1145 | if (dirty_viewport || flags[Dirty::ClipControl]) { |
| 1028 | flags[Dirty::ClipControl] = false; | 1146 | flags[Dirty::ClipControl] = false; |
| 1029 | 1147 | ||
| @@ -1121,11 +1239,6 @@ void RasterizerOpenGL::SyncCullMode() { | |||
| 1121 | glDisable(GL_CULL_FACE); | 1239 | glDisable(GL_CULL_FACE); |
| 1122 | } | 1240 | } |
| 1123 | } | 1241 | } |
| 1124 | |||
| 1125 | if (flags[Dirty::FrontFace]) { | ||
| 1126 | flags[Dirty::FrontFace] = false; | ||
| 1127 | glFrontFace(MaxwellToGL::FrontFace(regs.front_face)); | ||
| 1128 | } | ||
| 1129 | } | 1242 | } |
| 1130 | 1243 | ||
| 1131 | void RasterizerOpenGL::SyncPrimitiveRestart() { | 1244 | void RasterizerOpenGL::SyncPrimitiveRestart() { |
| @@ -1496,12 +1609,70 @@ void RasterizerOpenGL::SyncFramebufferSRGB() { | |||
| 1496 | oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb); | 1609 | oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb); |
| 1497 | } | 1610 | } |
| 1498 | 1611 | ||
| 1612 | void RasterizerOpenGL::SyncTransformFeedback() { | ||
| 1613 | // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal | ||
| 1614 | // when this is required. | ||
| 1615 | const auto& regs = system.GPU().Maxwell3D().regs; | ||
| 1616 | |||
| 1617 | static constexpr std::size_t STRIDE = 3; | ||
| 1618 | std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs; | ||
| 1619 | std::array<GLint, Maxwell::NumTransformFeedbackBuffers> streams; | ||
| 1620 | |||
| 1621 | GLint* cursor = attribs.data(); | ||
| 1622 | GLint* current_stream = streams.data(); | ||
| 1623 | |||
| 1624 | for (std::size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { | ||
| 1625 | const auto& layout = regs.tfb_layouts[feedback]; | ||
| 1626 | UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding"); | ||
| 1627 | if (layout.varying_count == 0) { | ||
| 1628 | continue; | ||
| 1629 | } | ||
| 1630 | |||
| 1631 | *current_stream = static_cast<GLint>(feedback); | ||
| 1632 | if (current_stream != streams.data()) { | ||
| 1633 | // When stepping one stream, push the expected token | ||
| 1634 | cursor[0] = GL_NEXT_BUFFER_NV; | ||
| 1635 | cursor[1] = 0; | ||
| 1636 | cursor[2] = 0; | ||
| 1637 | cursor += STRIDE; | ||
| 1638 | } | ||
| 1639 | ++current_stream; | ||
| 1640 | |||
| 1641 | const auto& locations = regs.tfb_varying_locs[feedback]; | ||
| 1642 | std::optional<u8> current_index; | ||
| 1643 | for (u32 offset = 0; offset < layout.varying_count; ++offset) { | ||
| 1644 | const u8 location = locations[offset]; | ||
| 1645 | const u8 index = location / 4; | ||
| 1646 | |||
| 1647 | if (current_index == index) { | ||
| 1648 | // Increase number of components of the previous attachment | ||
| 1649 | ++cursor[-2]; | ||
| 1650 | continue; | ||
| 1651 | } | ||
| 1652 | current_index = index; | ||
| 1653 | |||
| 1654 | std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location); | ||
| 1655 | cursor[1] = 1; | ||
| 1656 | cursor += STRIDE; | ||
| 1657 | } | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | const GLsizei num_attribs = static_cast<GLsizei>((cursor - attribs.data()) / STRIDE); | ||
| 1661 | const GLsizei num_strides = static_cast<GLsizei>(current_stream - streams.data()); | ||
| 1662 | glTransformFeedbackStreamAttribsNV(num_attribs, attribs.data(), num_strides, streams.data(), | ||
| 1663 | GL_INTERLEAVED_ATTRIBS); | ||
| 1664 | } | ||
| 1665 | |||
| 1499 | void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { | 1666 | void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { |
| 1500 | const auto& regs = system.GPU().Maxwell3D().regs; | 1667 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 1501 | if (regs.tfb_enabled == 0) { | 1668 | if (regs.tfb_enabled == 0) { |
| 1502 | return; | 1669 | return; |
| 1503 | } | 1670 | } |
| 1504 | 1671 | ||
| 1672 | if (device.UseAssemblyShaders()) { | ||
| 1673 | SyncTransformFeedback(); | ||
| 1674 | } | ||
| 1675 | |||
| 1505 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || | 1676 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || |
| 1506 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || | 1677 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || |
| 1507 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); | 1678 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); |
| @@ -1528,6 +1699,10 @@ void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { | |||
| 1528 | static_cast<GLsizeiptr>(size)); | 1699 | static_cast<GLsizeiptr>(size)); |
| 1529 | } | 1700 | } |
| 1530 | 1701 | ||
| 1702 | // We may have to call BeginTransformFeedbackNV here since they seem to call different | ||
| 1703 | // implementations on Nvidia's driver (the pointer is different) but we are using | ||
| 1704 | // ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB | ||
| 1705 | // extension doesn't define BeginTransformFeedback (without NV) interactions. It just works. | ||
| 1531 | glBeginTransformFeedback(GL_POINTS); | 1706 | glBeginTransformFeedback(GL_POINTS); |
| 1532 | } | 1707 | } |
| 1533 | 1708 | ||
| @@ -1549,8 +1724,9 @@ void RasterizerOpenGL::EndTransformFeedback() { | |||
| 1549 | const GLuint handle = transform_feedback_buffers[index].handle; | 1724 | const GLuint handle = transform_feedback_buffers[index].handle; |
| 1550 | const GPUVAddr gpu_addr = binding.Address(); | 1725 | const GPUVAddr gpu_addr = binding.Address(); |
| 1551 | const std::size_t size = binding.buffer_size; | 1726 | const std::size_t size = binding.buffer_size; |
| 1552 | const auto [dest_buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); | 1727 | const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true); |
| 1553 | glCopyNamedBufferSubData(handle, dest_buffer, 0, offset, static_cast<GLsizeiptr>(size)); | 1728 | glCopyNamedBufferSubData(handle, info.handle, 0, info.offset, |
| 1729 | static_cast<GLsizeiptr>(size)); | ||
| 1554 | } | 1730 | } |
| 1555 | } | 1731 | } |
| 1556 | 1732 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 87f7fe159..4f082592f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -19,7 +19,6 @@ | |||
| 19 | #include "video_core/engines/const_buffer_info.h" | 19 | #include "video_core/engines/const_buffer_info.h" |
| 20 | #include "video_core/engines/maxwell_3d.h" | 20 | #include "video_core/engines/maxwell_3d.h" |
| 21 | #include "video_core/rasterizer_accelerated.h" | 21 | #include "video_core/rasterizer_accelerated.h" |
| 22 | #include "video_core/rasterizer_cache.h" | ||
| 23 | #include "video_core/rasterizer_interface.h" | 22 | #include "video_core/rasterizer_interface.h" |
| 24 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 23 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 25 | #include "video_core/renderer_opengl/gl_device.h" | 24 | #include "video_core/renderer_opengl/gl_device.h" |
| @@ -100,40 +99,41 @@ private: | |||
| 100 | void ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil); | 99 | void ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil); |
| 101 | 100 | ||
| 102 | /// Configures the current constbuffers to use for the draw command. | 101 | /// Configures the current constbuffers to use for the draw command. |
| 103 | void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader); | 102 | void SetupDrawConstBuffers(std::size_t stage_index, Shader* shader); |
| 104 | 103 | ||
| 105 | /// Configures the current constbuffers to use for the kernel invocation. | 104 | /// Configures the current constbuffers to use for the kernel invocation. |
| 106 | void SetupComputeConstBuffers(const Shader& kernel); | 105 | void SetupComputeConstBuffers(Shader* kernel); |
| 107 | 106 | ||
| 108 | /// Configures a constant buffer. | 107 | /// Configures a constant buffer. |
| 109 | void SetupConstBuffer(GLenum stage, u32 binding, const Tegra::Engines::ConstBufferInfo& buffer, | 108 | void SetupConstBuffer(GLenum stage, u32 binding, const Tegra::Engines::ConstBufferInfo& buffer, |
| 110 | const ConstBufferEntry& entry); | 109 | const ConstBufferEntry& entry, bool use_unified, |
| 110 | std::size_t unified_offset); | ||
| 111 | 111 | ||
| 112 | /// Configures the current global memory entries to use for the draw command. | 112 | /// Configures the current global memory entries to use for the draw command. |
| 113 | void SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader); | 113 | void SetupDrawGlobalMemory(std::size_t stage_index, Shader* shader); |
| 114 | 114 | ||
| 115 | /// Configures the current global memory entries to use for the kernel invocation. | 115 | /// Configures the current global memory entries to use for the kernel invocation. |
| 116 | void SetupComputeGlobalMemory(const Shader& kernel); | 116 | void SetupComputeGlobalMemory(Shader* kernel); |
| 117 | 117 | ||
| 118 | /// Configures a constant buffer. | 118 | /// Configures a constant buffer. |
| 119 | void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr, | 119 | void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr, |
| 120 | std::size_t size); | 120 | std::size_t size); |
| 121 | 121 | ||
| 122 | /// Configures the current textures to use for the draw command. | 122 | /// Configures the current textures to use for the draw command. |
| 123 | void SetupDrawTextures(std::size_t stage_index, const Shader& shader); | 123 | void SetupDrawTextures(std::size_t stage_index, Shader* shader); |
| 124 | 124 | ||
| 125 | /// Configures the textures used in a compute shader. | 125 | /// Configures the textures used in a compute shader. |
| 126 | void SetupComputeTextures(const Shader& kernel); | 126 | void SetupComputeTextures(Shader* kernel); |
| 127 | 127 | ||
| 128 | /// Configures a texture. | 128 | /// Configures a texture. |
| 129 | void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture, | 129 | void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture, |
| 130 | const SamplerEntry& entry); | 130 | const SamplerEntry& entry); |
| 131 | 131 | ||
| 132 | /// Configures images in a graphics shader. | 132 | /// Configures images in a graphics shader. |
| 133 | void SetupDrawImages(std::size_t stage_index, const Shader& shader); | 133 | void SetupDrawImages(std::size_t stage_index, Shader* shader); |
| 134 | 134 | ||
| 135 | /// Configures images in a compute shader. | 135 | /// Configures images in a compute shader. |
| 136 | void SetupComputeImages(const Shader& shader); | 136 | void SetupComputeImages(Shader* shader); |
| 137 | 137 | ||
| 138 | /// Configures an image. | 138 | /// Configures an image. |
| 139 | void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry); | 139 | void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry); |
| @@ -201,6 +201,10 @@ private: | |||
| 201 | /// Syncs the framebuffer sRGB state to match the guest state | 201 | /// Syncs the framebuffer sRGB state to match the guest state |
| 202 | void SyncFramebufferSRGB(); | 202 | void SyncFramebufferSRGB(); |
| 203 | 203 | ||
| 204 | /// Syncs transform feedback state to match guest state | ||
| 205 | /// @note Only valid on assembly shaders | ||
| 206 | void SyncTransformFeedback(); | ||
| 207 | |||
| 204 | /// Begin a transform feedback | 208 | /// Begin a transform feedback |
| 205 | void BeginTransformFeedback(GLenum primitive_mode); | 209 | void BeginTransformFeedback(GLenum primitive_mode); |
| 206 | 210 | ||
| @@ -253,6 +257,7 @@ private: | |||
| 253 | Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; | 257 | Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; |
| 254 | std::array<GLuint, NUM_CONSTANT_BUFFERS> staging_cbufs{}; | 258 | std::array<GLuint, NUM_CONSTANT_BUFFERS> staging_cbufs{}; |
| 255 | std::size_t current_cbuf = 0; | 259 | std::size_t current_cbuf = 0; |
| 260 | OGLBuffer unified_uniform_buffer; | ||
| 256 | 261 | ||
| 257 | /// Number of commands queued to the OpenGL driver. Reseted on flush. | 262 | /// Number of commands queued to the OpenGL driver. Reseted on flush. |
| 258 | std::size_t num_queued_commands = 0; | 263 | std::size_t num_queued_commands = 0; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 4cd0f36cf..c6a3bf3a1 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include "video_core/engines/maxwell_3d.h" | 20 | #include "video_core/engines/maxwell_3d.h" |
| 21 | #include "video_core/engines/shader_type.h" | 21 | #include "video_core/engines/shader_type.h" |
| 22 | #include "video_core/memory_manager.h" | 22 | #include "video_core/memory_manager.h" |
| 23 | #include "video_core/renderer_opengl/gl_arb_decompiler.h" | ||
| 23 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 24 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 24 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 25 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 25 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 26 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| @@ -29,6 +30,7 @@ | |||
| 29 | #include "video_core/shader/memory_util.h" | 30 | #include "video_core/shader/memory_util.h" |
| 30 | #include "video_core/shader/registry.h" | 31 | #include "video_core/shader/registry.h" |
| 31 | #include "video_core/shader/shader_ir.h" | 32 | #include "video_core/shader/shader_ir.h" |
| 33 | #include "video_core/shader_cache.h" | ||
| 32 | 34 | ||
| 33 | namespace OpenGL { | 35 | namespace OpenGL { |
| 34 | 36 | ||
| @@ -147,7 +149,8 @@ ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 u | |||
| 147 | auto program = std::make_shared<ProgramHandle>(); | 149 | auto program = std::make_shared<ProgramHandle>(); |
| 148 | 150 | ||
| 149 | if (device.UseAssemblyShaders()) { | 151 | if (device.UseAssemblyShaders()) { |
| 150 | const std::string arb = "Not implemented"; | 152 | const std::string arb = |
| 153 | DecompileAssemblyShader(device, ir, registry, shader_type, shader_id); | ||
| 151 | 154 | ||
| 152 | GLuint& arb_prog = program->assembly_program.handle; | 155 | GLuint& arb_prog = program->assembly_program.handle; |
| 153 | 156 | ||
| @@ -194,12 +197,9 @@ std::unordered_set<GLenum> GetSupportedFormats() { | |||
| 194 | 197 | ||
| 195 | } // Anonymous namespace | 198 | } // Anonymous namespace |
| 196 | 199 | ||
| 197 | CachedShader::CachedShader(VAddr cpu_addr, std::size_t size_in_bytes, | 200 | Shader::Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry_, ShaderEntries entries_, |
| 198 | std::shared_ptr<VideoCommon::Shader::Registry> registry, | 201 | ProgramSharedPtr program_) |
| 199 | ShaderEntries entries, ProgramSharedPtr program_) | 202 | : registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)} { |
| 200 | : RasterizerCacheObject{cpu_addr}, registry{std::move(registry)}, entries{std::move(entries)}, | ||
| 201 | size_in_bytes{size_in_bytes}, program{std::move(program_)} { | ||
| 202 | // Assign either the assembly program or source program. We can't have both. | ||
| 203 | handle = program->assembly_program.handle; | 203 | handle = program->assembly_program.handle; |
| 204 | if (handle == 0) { | 204 | if (handle == 0) { |
| 205 | handle = program->source_program.handle; | 205 | handle = program->source_program.handle; |
| @@ -207,16 +207,16 @@ CachedShader::CachedShader(VAddr cpu_addr, std::size_t size_in_bytes, | |||
| 207 | ASSERT(handle != 0); | 207 | ASSERT(handle != 0); |
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | CachedShader::~CachedShader() = default; | 210 | Shader::~Shader() = default; |
| 211 | 211 | ||
| 212 | GLuint CachedShader::GetHandle() const { | 212 | GLuint Shader::GetHandle() const { |
| 213 | DEBUG_ASSERT(registry->IsConsistent()); | 213 | DEBUG_ASSERT(registry->IsConsistent()); |
| 214 | return handle; | 214 | return handle; |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params, | 217 | std::unique_ptr<Shader> Shader::CreateStageFromMemory(const ShaderParameters& params, |
| 218 | Maxwell::ShaderProgram program_type, ProgramCode code, | 218 | Maxwell::ShaderProgram program_type, |
| 219 | ProgramCode code_b) { | 219 | ProgramCode code, ProgramCode code_b) { |
| 220 | const auto shader_type = GetShaderType(program_type); | 220 | const auto shader_type = GetShaderType(program_type); |
| 221 | const std::size_t size_in_bytes = code.size() * sizeof(u64); | 221 | const std::size_t size_in_bytes = code.size() * sizeof(u64); |
| 222 | 222 | ||
| @@ -241,11 +241,12 @@ Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params, | |||
| 241 | entry.bindless_samplers = registry->GetBindlessSamplers(); | 241 | entry.bindless_samplers = registry->GetBindlessSamplers(); |
| 242 | params.disk_cache.SaveEntry(std::move(entry)); | 242 | params.disk_cache.SaveEntry(std::move(entry)); |
| 243 | 243 | ||
| 244 | return std::shared_ptr<CachedShader>(new CachedShader( | 244 | return std::unique_ptr<Shader>(new Shader( |
| 245 | params.cpu_addr, size_in_bytes, std::move(registry), MakeEntries(ir), std::move(program))); | 245 | std::move(registry), MakeEntries(params.device, ir, shader_type), std::move(program))); |
| 246 | } | 246 | } |
| 247 | 247 | ||
| 248 | Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) { | 248 | std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params, |
| 249 | ProgramCode code) { | ||
| 249 | const std::size_t size_in_bytes = code.size() * sizeof(u64); | 250 | const std::size_t size_in_bytes = code.size() * sizeof(u64); |
| 250 | 251 | ||
| 251 | auto& engine = params.system.GPU().KeplerCompute(); | 252 | auto& engine = params.system.GPU().KeplerCompute(); |
| @@ -265,22 +266,23 @@ Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, Prog | |||
| 265 | entry.bindless_samplers = registry->GetBindlessSamplers(); | 266 | entry.bindless_samplers = registry->GetBindlessSamplers(); |
| 266 | params.disk_cache.SaveEntry(std::move(entry)); | 267 | params.disk_cache.SaveEntry(std::move(entry)); |
| 267 | 268 | ||
| 268 | return std::shared_ptr<CachedShader>(new CachedShader( | 269 | return std::unique_ptr<Shader>(new Shader(std::move(registry), |
| 269 | params.cpu_addr, size_in_bytes, std::move(registry), MakeEntries(ir), std::move(program))); | 270 | MakeEntries(params.device, ir, ShaderType::Compute), |
| 271 | std::move(program))); | ||
| 270 | } | 272 | } |
| 271 | 273 | ||
| 272 | Shader CachedShader::CreateFromCache(const ShaderParameters& params, | 274 | std::unique_ptr<Shader> Shader::CreateFromCache(const ShaderParameters& params, |
| 273 | const PrecompiledShader& precompiled_shader, | 275 | const PrecompiledShader& precompiled_shader) { |
| 274 | std::size_t size_in_bytes) { | 276 | return std::unique_ptr<Shader>(new Shader( |
| 275 | return std::shared_ptr<CachedShader>( | 277 | precompiled_shader.registry, precompiled_shader.entries, precompiled_shader.program)); |
| 276 | new CachedShader(params.cpu_addr, size_in_bytes, precompiled_shader.registry, | ||
| 277 | precompiled_shader.entries, precompiled_shader.program)); | ||
| 278 | } | 278 | } |
| 279 | 279 | ||
| 280 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, | 280 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, |
| 281 | Core::Frontend::EmuWindow& emu_window, const Device& device) | 281 | Core::Frontend::EmuWindow& emu_window, const Device& device) |
| 282 | : RasterizerCache{rasterizer}, system{system}, emu_window{emu_window}, device{device}, | 282 | : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system}, |
| 283 | disk_cache{system} {} | 283 | emu_window{emu_window}, device{device}, disk_cache{system} {} |
| 284 | |||
| 285 | ShaderCacheOpenGL::~ShaderCacheOpenGL() = default; | ||
| 284 | 286 | ||
| 285 | void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | 287 | void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, |
| 286 | const VideoCore::DiskResourceLoadCallback& callback) { | 288 | const VideoCore::DiskResourceLoadCallback& callback) { |
| @@ -348,7 +350,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 348 | PrecompiledShader shader; | 350 | PrecompiledShader shader; |
| 349 | shader.program = std::move(program); | 351 | shader.program = std::move(program); |
| 350 | shader.registry = std::move(registry); | 352 | shader.registry = std::move(registry); |
| 351 | shader.entries = MakeEntries(ir); | 353 | shader.entries = MakeEntries(device, ir, entry.type); |
| 352 | 354 | ||
| 353 | std::scoped_lock lock{mutex}; | 355 | std::scoped_lock lock{mutex}; |
| 354 | if (callback) { | 356 | if (callback) { |
| @@ -434,7 +436,7 @@ ProgramSharedPtr ShaderCacheOpenGL::GeneratePrecompiledProgram( | |||
| 434 | return program; | 436 | return program; |
| 435 | } | 437 | } |
| 436 | 438 | ||
| 437 | Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | 439 | Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { |
| 438 | if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) { | 440 | if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) { |
| 439 | return last_shaders[static_cast<std::size_t>(program)]; | 441 | return last_shaders[static_cast<std::size_t>(program)]; |
| 440 | } | 442 | } |
| @@ -444,8 +446,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 444 | 446 | ||
| 445 | // Look up shader in the cache based on address | 447 | // Look up shader in the cache based on address |
| 446 | const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; | 448 | const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; |
| 447 | Shader shader{cpu_addr ? TryGet(*cpu_addr) : null_shader}; | 449 | if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) { |
| 448 | if (shader) { | ||
| 449 | return last_shaders[static_cast<std::size_t>(program)] = shader; | 450 | return last_shaders[static_cast<std::size_t>(program)] = shader; |
| 450 | } | 451 | } |
| 451 | 452 | ||
| @@ -459,62 +460,64 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 459 | const u8* host_ptr_b = memory_manager.GetPointer(address_b); | 460 | const u8* host_ptr_b = memory_manager.GetPointer(address_b); |
| 460 | code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false); | 461 | code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false); |
| 461 | } | 462 | } |
| 463 | const std::size_t code_size = code.size() * sizeof(u64); | ||
| 462 | 464 | ||
| 463 | const auto unique_identifier = GetUniqueIdentifier( | 465 | const u64 unique_identifier = GetUniqueIdentifier( |
| 464 | GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); | 466 | GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); |
| 465 | 467 | ||
| 466 | const ShaderParameters params{system, disk_cache, device, | 468 | const ShaderParameters params{system, disk_cache, device, |
| 467 | *cpu_addr, host_ptr, unique_identifier}; | 469 | *cpu_addr, host_ptr, unique_identifier}; |
| 468 | 470 | ||
| 471 | std::unique_ptr<Shader> shader; | ||
| 469 | const auto found = runtime_cache.find(unique_identifier); | 472 | const auto found = runtime_cache.find(unique_identifier); |
| 470 | if (found == runtime_cache.end()) { | 473 | if (found == runtime_cache.end()) { |
| 471 | shader = CachedShader::CreateStageFromMemory(params, program, std::move(code), | 474 | shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b)); |
| 472 | std::move(code_b)); | ||
| 473 | } else { | 475 | } else { |
| 474 | const std::size_t size_in_bytes = code.size() * sizeof(u64); | 476 | shader = Shader::CreateFromCache(params, found->second); |
| 475 | shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes); | ||
| 476 | } | 477 | } |
| 477 | 478 | ||
| 479 | Shader* const result = shader.get(); | ||
| 478 | if (cpu_addr) { | 480 | if (cpu_addr) { |
| 479 | Register(shader); | 481 | Register(std::move(shader), *cpu_addr, code_size); |
| 480 | } else { | 482 | } else { |
| 481 | null_shader = shader; | 483 | null_shader = std::move(shader); |
| 482 | } | 484 | } |
| 483 | 485 | ||
| 484 | return last_shaders[static_cast<std::size_t>(program)] = shader; | 486 | return last_shaders[static_cast<std::size_t>(program)] = result; |
| 485 | } | 487 | } |
| 486 | 488 | ||
| 487 | Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { | 489 | Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { |
| 488 | auto& memory_manager{system.GPU().MemoryManager()}; | 490 | auto& memory_manager{system.GPU().MemoryManager()}; |
| 489 | const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)}; | 491 | const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)}; |
| 490 | 492 | ||
| 491 | auto kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel; | 493 | if (Shader* const kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get()) { |
| 492 | if (kernel) { | ||
| 493 | return kernel; | 494 | return kernel; |
| 494 | } | 495 | } |
| 495 | 496 | ||
| 496 | const auto host_ptr{memory_manager.GetPointer(code_addr)}; | 497 | const auto host_ptr{memory_manager.GetPointer(code_addr)}; |
| 497 | // No kernel found, create a new one | 498 | // No kernel found, create a new one |
| 498 | auto code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; | 499 | ProgramCode code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; |
| 499 | const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; | 500 | const std::size_t code_size{code.size() * sizeof(u64)}; |
| 501 | const u64 unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; | ||
| 500 | 502 | ||
| 501 | const ShaderParameters params{system, disk_cache, device, | 503 | const ShaderParameters params{system, disk_cache, device, |
| 502 | *cpu_addr, host_ptr, unique_identifier}; | 504 | *cpu_addr, host_ptr, unique_identifier}; |
| 503 | 505 | ||
| 506 | std::unique_ptr<Shader> kernel; | ||
| 504 | const auto found = runtime_cache.find(unique_identifier); | 507 | const auto found = runtime_cache.find(unique_identifier); |
| 505 | if (found == runtime_cache.end()) { | 508 | if (found == runtime_cache.end()) { |
| 506 | kernel = CachedShader::CreateKernelFromMemory(params, std::move(code)); | 509 | kernel = Shader::CreateKernelFromMemory(params, std::move(code)); |
| 507 | } else { | 510 | } else { |
| 508 | const std::size_t size_in_bytes = code.size() * sizeof(u64); | 511 | kernel = Shader::CreateFromCache(params, found->second); |
| 509 | kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes); | ||
| 510 | } | 512 | } |
| 511 | 513 | ||
| 514 | Shader* const result = kernel.get(); | ||
| 512 | if (cpu_addr) { | 515 | if (cpu_addr) { |
| 513 | Register(kernel); | 516 | Register(std::move(kernel), *cpu_addr, code_size); |
| 514 | } else { | 517 | } else { |
| 515 | null_kernel = kernel; | 518 | null_kernel = std::move(kernel); |
| 516 | } | 519 | } |
| 517 | return kernel; | 520 | return result; |
| 518 | } | 521 | } |
| 519 | 522 | ||
| 520 | } // namespace OpenGL | 523 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index b2ae8d7f9..994aaeaf2 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -18,12 +18,12 @@ | |||
| 18 | 18 | ||
| 19 | #include "common/common_types.h" | 19 | #include "common/common_types.h" |
| 20 | #include "video_core/engines/shader_type.h" | 20 | #include "video_core/engines/shader_type.h" |
| 21 | #include "video_core/rasterizer_cache.h" | ||
| 22 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 21 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 23 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 22 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 24 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" | 23 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" |
| 25 | #include "video_core/shader/registry.h" | 24 | #include "video_core/shader/registry.h" |
| 26 | #include "video_core/shader/shader_ir.h" | 25 | #include "video_core/shader/shader_ir.h" |
| 26 | #include "video_core/shader_cache.h" | ||
| 27 | 27 | ||
| 28 | namespace Core { | 28 | namespace Core { |
| 29 | class System; | 29 | class System; |
| @@ -35,12 +35,9 @@ class EmuWindow; | |||
| 35 | 35 | ||
| 36 | namespace OpenGL { | 36 | namespace OpenGL { |
| 37 | 37 | ||
| 38 | class CachedShader; | ||
| 39 | class Device; | 38 | class Device; |
| 40 | class RasterizerOpenGL; | 39 | class RasterizerOpenGL; |
| 41 | struct UnspecializedShader; | ||
| 42 | 40 | ||
| 43 | using Shader = std::shared_ptr<CachedShader>; | ||
| 44 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 41 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 45 | 42 | ||
| 46 | struct ProgramHandle { | 43 | struct ProgramHandle { |
| @@ -64,62 +61,53 @@ struct ShaderParameters { | |||
| 64 | u64 unique_identifier; | 61 | u64 unique_identifier; |
| 65 | }; | 62 | }; |
| 66 | 63 | ||
| 67 | class CachedShader final : public RasterizerCacheObject { | 64 | class Shader final { |
| 68 | public: | 65 | public: |
| 69 | ~CachedShader(); | 66 | ~Shader(); |
| 70 | 67 | ||
| 71 | /// Gets the GL program handle for the shader | 68 | /// Gets the GL program handle for the shader |
| 72 | GLuint GetHandle() const; | 69 | GLuint GetHandle() const; |
| 73 | 70 | ||
| 74 | /// Returns the size in bytes of the shader | ||
| 75 | std::size_t GetSizeInBytes() const override { | ||
| 76 | return size_in_bytes; | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Gets the shader entries for the shader | 71 | /// Gets the shader entries for the shader |
| 80 | const ShaderEntries& GetEntries() const { | 72 | const ShaderEntries& GetEntries() const { |
| 81 | return entries; | 73 | return entries; |
| 82 | } | 74 | } |
| 83 | 75 | ||
| 84 | static Shader CreateStageFromMemory(const ShaderParameters& params, | 76 | static std::unique_ptr<Shader> CreateStageFromMemory(const ShaderParameters& params, |
| 85 | Maxwell::ShaderProgram program_type, | 77 | Maxwell::ShaderProgram program_type, |
| 86 | ProgramCode program_code, ProgramCode program_code_b); | 78 | ProgramCode program_code, |
| 87 | static Shader CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code); | 79 | ProgramCode program_code_b); |
| 80 | static std::unique_ptr<Shader> CreateKernelFromMemory(const ShaderParameters& params, | ||
| 81 | ProgramCode code); | ||
| 88 | 82 | ||
| 89 | static Shader CreateFromCache(const ShaderParameters& params, | 83 | static std::unique_ptr<Shader> CreateFromCache(const ShaderParameters& params, |
| 90 | const PrecompiledShader& precompiled_shader, | 84 | const PrecompiledShader& precompiled_shader); |
| 91 | std::size_t size_in_bytes); | ||
| 92 | 85 | ||
| 93 | private: | 86 | private: |
| 94 | explicit CachedShader(VAddr cpu_addr, std::size_t size_in_bytes, | 87 | explicit Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry, ShaderEntries entries, |
| 95 | std::shared_ptr<VideoCommon::Shader::Registry> registry, | 88 | ProgramSharedPtr program); |
| 96 | ShaderEntries entries, ProgramSharedPtr program); | ||
| 97 | 89 | ||
| 98 | std::shared_ptr<VideoCommon::Shader::Registry> registry; | 90 | std::shared_ptr<VideoCommon::Shader::Registry> registry; |
| 99 | ShaderEntries entries; | 91 | ShaderEntries entries; |
| 100 | std::size_t size_in_bytes = 0; | ||
| 101 | ProgramSharedPtr program; | 92 | ProgramSharedPtr program; |
| 102 | GLuint handle = 0; | 93 | GLuint handle = 0; |
| 103 | }; | 94 | }; |
| 104 | 95 | ||
| 105 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { | 96 | class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> { |
| 106 | public: | 97 | public: |
| 107 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, | 98 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, |
| 108 | Core::Frontend::EmuWindow& emu_window, const Device& device); | 99 | Core::Frontend::EmuWindow& emu_window, const Device& device); |
| 100 | ~ShaderCacheOpenGL() override; | ||
| 109 | 101 | ||
| 110 | /// Loads disk cache for the current game | 102 | /// Loads disk cache for the current game |
| 111 | void LoadDiskCache(const std::atomic_bool& stop_loading, | 103 | void LoadDiskCache(const std::atomic_bool& stop_loading, |
| 112 | const VideoCore::DiskResourceLoadCallback& callback); | 104 | const VideoCore::DiskResourceLoadCallback& callback); |
| 113 | 105 | ||
| 114 | /// Gets the current specified shader stage program | 106 | /// Gets the current specified shader stage program |
| 115 | Shader GetStageProgram(Maxwell::ShaderProgram program); | 107 | Shader* GetStageProgram(Maxwell::ShaderProgram program); |
| 116 | 108 | ||
| 117 | /// Gets a compute kernel in the passed address | 109 | /// Gets a compute kernel in the passed address |
| 118 | Shader GetComputeKernel(GPUVAddr code_addr); | 110 | Shader* GetComputeKernel(GPUVAddr code_addr); |
| 119 | |||
| 120 | protected: | ||
| 121 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 122 | void FlushObjectInner(const Shader& object) override {} | ||
| 123 | 111 | ||
| 124 | private: | 112 | private: |
| 125 | ProgramSharedPtr GeneratePrecompiledProgram( | 113 | ProgramSharedPtr GeneratePrecompiledProgram( |
| @@ -132,10 +120,10 @@ private: | |||
| 132 | ShaderDiskCacheOpenGL disk_cache; | 120 | ShaderDiskCacheOpenGL disk_cache; |
| 133 | std::unordered_map<u64, PrecompiledShader> runtime_cache; | 121 | std::unordered_map<u64, PrecompiledShader> runtime_cache; |
| 134 | 122 | ||
| 135 | Shader null_shader{}; | 123 | std::unique_ptr<Shader> null_shader; |
| 136 | Shader null_kernel{}; | 124 | std::unique_ptr<Shader> null_kernel; |
| 137 | 125 | ||
| 138 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | 126 | std::array<Shader*, Maxwell::MaxShaderProgram> last_shaders{}; |
| 139 | }; | 127 | }; |
| 140 | 128 | ||
| 141 | } // namespace OpenGL | 129 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 9cb115959..2c49aeaac 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -37,6 +37,7 @@ using Tegra::Shader::IpaMode; | |||
| 37 | using Tegra::Shader::IpaSampleMode; | 37 | using Tegra::Shader::IpaSampleMode; |
| 38 | using Tegra::Shader::PixelImap; | 38 | using Tegra::Shader::PixelImap; |
| 39 | using Tegra::Shader::Register; | 39 | using Tegra::Shader::Register; |
| 40 | using Tegra::Shader::TextureType; | ||
| 40 | using VideoCommon::Shader::BuildTransformFeedback; | 41 | using VideoCommon::Shader::BuildTransformFeedback; |
| 41 | using VideoCommon::Shader::Registry; | 42 | using VideoCommon::Shader::Registry; |
| 42 | 43 | ||
| @@ -61,8 +62,8 @@ struct TextureDerivates {}; | |||
| 61 | using TextureArgument = std::pair<Type, Node>; | 62 | using TextureArgument = std::pair<Type, Node>; |
| 62 | using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>; | 63 | using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>; |
| 63 | 64 | ||
| 64 | constexpr u32 MAX_CONSTBUFFER_ELEMENTS = | 65 | constexpr u32 MAX_CONSTBUFFER_SCALARS = static_cast<u32>(Maxwell::MaxConstBufferSize) / sizeof(u32); |
| 65 | static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float)); | 66 | constexpr u32 MAX_CONSTBUFFER_ELEMENTS = MAX_CONSTBUFFER_SCALARS / sizeof(u32); |
| 66 | 67 | ||
| 67 | constexpr std::string_view CommonDeclarations = R"(#define ftoi floatBitsToInt | 68 | constexpr std::string_view CommonDeclarations = R"(#define ftoi floatBitsToInt |
| 68 | #define ftou floatBitsToUint | 69 | #define ftou floatBitsToUint |
| @@ -402,6 +403,13 @@ std::string FlowStackTopName(MetaStackClass stack) { | |||
| 402 | return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack)); | 403 | return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack)); |
| 403 | } | 404 | } |
| 404 | 405 | ||
| 406 | bool UseUnifiedUniforms(const Device& device, const ShaderIR& ir, ShaderType stage) { | ||
| 407 | const u32 num_ubos = static_cast<u32>(ir.GetConstantBuffers().size()); | ||
| 408 | // We waste one UBO for emulation | ||
| 409 | const u32 num_available_ubos = device.GetMaxUniformBuffers(stage) - 1; | ||
| 410 | return num_ubos > num_available_ubos; | ||
| 411 | } | ||
| 412 | |||
| 405 | struct GenericVaryingDescription { | 413 | struct GenericVaryingDescription { |
| 406 | std::string name; | 414 | std::string name; |
| 407 | u8 first_element = 0; | 415 | u8 first_element = 0; |
| @@ -412,8 +420,9 @@ class GLSLDecompiler final { | |||
| 412 | public: | 420 | public: |
| 413 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry, | 421 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry, |
| 414 | ShaderType stage, std::string_view identifier, std::string_view suffix) | 422 | ShaderType stage, std::string_view identifier, std::string_view suffix) |
| 415 | : device{device}, ir{ir}, registry{registry}, stage{stage}, | 423 | : device{device}, ir{ir}, registry{registry}, stage{stage}, identifier{identifier}, |
| 416 | identifier{identifier}, suffix{suffix}, header{ir.GetHeader()} { | 424 | suffix{suffix}, header{ir.GetHeader()}, use_unified_uniforms{ |
| 425 | UseUnifiedUniforms(device, ir, stage)} { | ||
| 417 | if (stage != ShaderType::Compute) { | 426 | if (stage != ShaderType::Compute) { |
| 418 | transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo()); | 427 | transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo()); |
| 419 | } | 428 | } |
| @@ -518,6 +527,9 @@ private: | |||
| 518 | if (device.HasImageLoadFormatted()) { | 527 | if (device.HasImageLoadFormatted()) { |
| 519 | code.AddLine("#extension GL_EXT_shader_image_load_formatted : require"); | 528 | code.AddLine("#extension GL_EXT_shader_image_load_formatted : require"); |
| 520 | } | 529 | } |
| 530 | if (device.HasTextureShadowLod()) { | ||
| 531 | code.AddLine("#extension GL_EXT_texture_shadow_lod : require"); | ||
| 532 | } | ||
| 521 | if (device.HasWarpIntrinsics()) { | 533 | if (device.HasWarpIntrinsics()) { |
| 522 | code.AddLine("#extension GL_NV_gpu_shader5 : require"); | 534 | code.AddLine("#extension GL_NV_gpu_shader5 : require"); |
| 523 | code.AddLine("#extension GL_NV_shader_thread_group : require"); | 535 | code.AddLine("#extension GL_NV_shader_thread_group : require"); |
| @@ -618,7 +630,9 @@ private: | |||
| 618 | break; | 630 | break; |
| 619 | } | 631 | } |
| 620 | } | 632 | } |
| 621 | if (stage != ShaderType::Vertex || device.HasVertexViewportLayer()) { | 633 | |
| 634 | if (stage != ShaderType::Geometry && | ||
| 635 | (stage != ShaderType::Vertex || device.HasVertexViewportLayer())) { | ||
| 622 | if (ir.UsesLayer()) { | 636 | if (ir.UsesLayer()) { |
| 623 | code.AddLine("int gl_Layer;"); | 637 | code.AddLine("int gl_Layer;"); |
| 624 | } | 638 | } |
| @@ -647,6 +661,16 @@ private: | |||
| 647 | --code.scope; | 661 | --code.scope; |
| 648 | code.AddLine("}};"); | 662 | code.AddLine("}};"); |
| 649 | code.AddNewLine(); | 663 | code.AddNewLine(); |
| 664 | |||
| 665 | if (stage == ShaderType::Geometry) { | ||
| 666 | if (ir.UsesLayer()) { | ||
| 667 | code.AddLine("out int gl_Layer;"); | ||
| 668 | } | ||
| 669 | if (ir.UsesViewportIndex()) { | ||
| 670 | code.AddLine("out int gl_ViewportIndex;"); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | code.AddNewLine(); | ||
| 650 | } | 674 | } |
| 651 | 675 | ||
| 652 | void DeclareRegisters() { | 676 | void DeclareRegisters() { |
| @@ -834,12 +858,24 @@ private: | |||
| 834 | } | 858 | } |
| 835 | 859 | ||
| 836 | void DeclareConstantBuffers() { | 860 | void DeclareConstantBuffers() { |
| 861 | if (use_unified_uniforms) { | ||
| 862 | const u32 binding = device.GetBaseBindings(stage).shader_storage_buffer + | ||
| 863 | static_cast<u32>(ir.GetGlobalMemory().size()); | ||
| 864 | code.AddLine("layout (std430, binding = {}) readonly buffer UnifiedUniforms {{", | ||
| 865 | binding); | ||
| 866 | code.AddLine(" uint cbufs[];"); | ||
| 867 | code.AddLine("}};"); | ||
| 868 | code.AddNewLine(); | ||
| 869 | return; | ||
| 870 | } | ||
| 871 | |||
| 837 | u32 binding = device.GetBaseBindings(stage).uniform_buffer; | 872 | u32 binding = device.GetBaseBindings(stage).uniform_buffer; |
| 838 | for (const auto& buffers : ir.GetConstantBuffers()) { | 873 | for (const auto [index, info] : ir.GetConstantBuffers()) { |
| 839 | const auto index = buffers.first; | 874 | const u32 num_elements = Common::AlignUp(info.GetSize(), 4) / 4; |
| 875 | const u32 size = info.IsIndirect() ? MAX_CONSTBUFFER_ELEMENTS : num_elements; | ||
| 840 | code.AddLine("layout (std140, binding = {}) uniform {} {{", binding++, | 876 | code.AddLine("layout (std140, binding = {}) uniform {} {{", binding++, |
| 841 | GetConstBufferBlock(index)); | 877 | GetConstBufferBlock(index)); |
| 842 | code.AddLine(" uvec4 {}[{}];", GetConstBuffer(index), MAX_CONSTBUFFER_ELEMENTS); | 878 | code.AddLine(" uvec4 {}[{}];", GetConstBuffer(index), size); |
| 843 | code.AddLine("}};"); | 879 | code.AddLine("}};"); |
| 844 | code.AddNewLine(); | 880 | code.AddNewLine(); |
| 845 | } | 881 | } |
| @@ -877,13 +913,13 @@ private: | |||
| 877 | return "samplerBuffer"; | 913 | return "samplerBuffer"; |
| 878 | } | 914 | } |
| 879 | switch (sampler.type) { | 915 | switch (sampler.type) { |
| 880 | case Tegra::Shader::TextureType::Texture1D: | 916 | case TextureType::Texture1D: |
| 881 | return "sampler1D"; | 917 | return "sampler1D"; |
| 882 | case Tegra::Shader::TextureType::Texture2D: | 918 | case TextureType::Texture2D: |
| 883 | return "sampler2D"; | 919 | return "sampler2D"; |
| 884 | case Tegra::Shader::TextureType::Texture3D: | 920 | case TextureType::Texture3D: |
| 885 | return "sampler3D"; | 921 | return "sampler3D"; |
| 886 | case Tegra::Shader::TextureType::TextureCube: | 922 | case TextureType::TextureCube: |
| 887 | return "samplerCube"; | 923 | return "samplerCube"; |
| 888 | default: | 924 | default: |
| 889 | UNREACHABLE(); | 925 | UNREACHABLE(); |
| @@ -1038,42 +1074,51 @@ private: | |||
| 1038 | 1074 | ||
| 1039 | if (const auto cbuf = std::get_if<CbufNode>(&*node)) { | 1075 | if (const auto cbuf = std::get_if<CbufNode>(&*node)) { |
| 1040 | const Node offset = cbuf->GetOffset(); | 1076 | const Node offset = cbuf->GetOffset(); |
| 1077 | const u32 base_unified_offset = cbuf->GetIndex() * MAX_CONSTBUFFER_SCALARS; | ||
| 1078 | |||
| 1041 | if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { | 1079 | if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { |
| 1042 | // Direct access | 1080 | // Direct access |
| 1043 | const u32 offset_imm = immediate->GetValue(); | 1081 | const u32 offset_imm = immediate->GetValue(); |
| 1044 | ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); | 1082 | ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); |
| 1045 | return {fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), | 1083 | if (use_unified_uniforms) { |
| 1046 | offset_imm / (4 * 4), (offset_imm / 4) % 4), | 1084 | return {fmt::format("cbufs[{}]", base_unified_offset + offset_imm / 4), |
| 1047 | Type::Uint}; | 1085 | Type::Uint}; |
| 1086 | } else { | ||
| 1087 | return {fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), | ||
| 1088 | offset_imm / (4 * 4), (offset_imm / 4) % 4), | ||
| 1089 | Type::Uint}; | ||
| 1090 | } | ||
| 1048 | } | 1091 | } |
| 1049 | 1092 | ||
| 1050 | if (std::holds_alternative<OperationNode>(*offset)) { | 1093 | // Indirect access |
| 1051 | // Indirect access | 1094 | if (use_unified_uniforms) { |
| 1052 | const std::string final_offset = code.GenerateTemporary(); | 1095 | return {fmt::format("cbufs[{} + ({} >> 2)]", base_unified_offset, |
| 1053 | code.AddLine("uint {} = {} >> 2;", final_offset, Visit(offset).AsUint()); | 1096 | Visit(offset).AsUint()), |
| 1097 | Type::Uint}; | ||
| 1098 | } | ||
| 1054 | 1099 | ||
| 1055 | if (!device.HasComponentIndexingBug()) { | 1100 | const std::string final_offset = code.GenerateTemporary(); |
| 1056 | return {fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()), | 1101 | code.AddLine("uint {} = {} >> 2;", final_offset, Visit(offset).AsUint()); |
| 1057 | final_offset, final_offset), | ||
| 1058 | Type::Uint}; | ||
| 1059 | } | ||
| 1060 | 1102 | ||
| 1061 | // AMD's proprietary GLSL compiler emits ill code for variable component access. | 1103 | if (!device.HasComponentIndexingBug()) { |
| 1062 | // To bypass this driver bug generate 4 ifs, one per each component. | 1104 | return {fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()), |
| 1063 | const std::string pack = code.GenerateTemporary(); | 1105 | final_offset, final_offset), |
| 1064 | code.AddLine("uvec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()), | 1106 | Type::Uint}; |
| 1065 | final_offset); | ||
| 1066 | |||
| 1067 | const std::string result = code.GenerateTemporary(); | ||
| 1068 | code.AddLine("uint {};", result); | ||
| 1069 | for (u32 swizzle = 0; swizzle < 4; ++swizzle) { | ||
| 1070 | code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result, | ||
| 1071 | pack, GetSwizzle(swizzle)); | ||
| 1072 | } | ||
| 1073 | return {result, Type::Uint}; | ||
| 1074 | } | 1107 | } |
| 1075 | 1108 | ||
| 1076 | UNREACHABLE_MSG("Unmanaged offset node type"); | 1109 | // AMD's proprietary GLSL compiler emits ill code for variable component access. |
| 1110 | // To bypass this driver bug generate 4 ifs, one per each component. | ||
| 1111 | const std::string pack = code.GenerateTemporary(); | ||
| 1112 | code.AddLine("uvec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()), | ||
| 1113 | final_offset); | ||
| 1114 | |||
| 1115 | const std::string result = code.GenerateTemporary(); | ||
| 1116 | code.AddLine("uint {};", result); | ||
| 1117 | for (u32 swizzle = 0; swizzle < 4; ++swizzle) { | ||
| 1118 | code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result, pack, | ||
| 1119 | GetSwizzle(swizzle)); | ||
| 1120 | } | ||
| 1121 | return {result, Type::Uint}; | ||
| 1077 | } | 1122 | } |
| 1078 | 1123 | ||
| 1079 | if (const auto gmem = std::get_if<GmemNode>(&*node)) { | 1124 | if (const auto gmem = std::get_if<GmemNode>(&*node)) { |
| @@ -1339,8 +1384,19 @@ private: | |||
| 1339 | const std::size_t count = operation.GetOperandsCount(); | 1384 | const std::size_t count = operation.GetOperandsCount(); |
| 1340 | const bool has_array = meta->sampler.is_array; | 1385 | const bool has_array = meta->sampler.is_array; |
| 1341 | const bool has_shadow = meta->sampler.is_shadow; | 1386 | const bool has_shadow = meta->sampler.is_shadow; |
| 1387 | const bool workaround_lod_array_shadow_as_grad = | ||
| 1388 | !device.HasTextureShadowLod() && function_suffix == "Lod" && meta->sampler.is_shadow && | ||
| 1389 | ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) || | ||
| 1390 | meta->sampler.type == TextureType::TextureCube); | ||
| 1391 | |||
| 1392 | std::string expr = "texture"; | ||
| 1393 | |||
| 1394 | if (workaround_lod_array_shadow_as_grad) { | ||
| 1395 | expr += "Grad"; | ||
| 1396 | } else { | ||
| 1397 | expr += function_suffix; | ||
| 1398 | } | ||
| 1342 | 1399 | ||
| 1343 | std::string expr = "texture" + function_suffix; | ||
| 1344 | if (!meta->aoffi.empty()) { | 1400 | if (!meta->aoffi.empty()) { |
| 1345 | expr += "Offset"; | 1401 | expr += "Offset"; |
| 1346 | } else if (!meta->ptp.empty()) { | 1402 | } else if (!meta->ptp.empty()) { |
| @@ -1374,6 +1430,16 @@ private: | |||
| 1374 | expr += ')'; | 1430 | expr += ')'; |
| 1375 | } | 1431 | } |
| 1376 | 1432 | ||
| 1433 | if (workaround_lod_array_shadow_as_grad) { | ||
| 1434 | switch (meta->sampler.type) { | ||
| 1435 | case TextureType::Texture2D: | ||
| 1436 | return expr + ", vec2(0.0), vec2(0.0))"; | ||
| 1437 | case TextureType::TextureCube: | ||
| 1438 | return expr + ", vec3(0.0), vec3(0.0))"; | ||
| 1439 | } | ||
| 1440 | UNREACHABLE(); | ||
| 1441 | } | ||
| 1442 | |||
| 1377 | for (const auto& variant : extras) { | 1443 | for (const auto& variant : extras) { |
| 1378 | if (const auto argument = std::get_if<TextureArgument>(&variant)) { | 1444 | if (const auto argument = std::get_if<TextureArgument>(&variant)) { |
| 1379 | expr += GenerateTextureArgument(*argument); | 1445 | expr += GenerateTextureArgument(*argument); |
| @@ -2000,8 +2066,19 @@ private: | |||
| 2000 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | 2066 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); |
| 2001 | ASSERT(meta); | 2067 | ASSERT(meta); |
| 2002 | 2068 | ||
| 2003 | std::string expr = GenerateTexture( | 2069 | std::string expr{}; |
| 2004 | operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureOffset{}}); | 2070 | |
| 2071 | if (!device.HasTextureShadowLod() && meta->sampler.is_shadow && | ||
| 2072 | ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) || | ||
| 2073 | meta->sampler.type == TextureType::TextureCube)) { | ||
| 2074 | LOG_ERROR(Render_OpenGL, | ||
| 2075 | "Device lacks GL_EXT_texture_shadow_lod, using textureGrad as a workaround"); | ||
| 2076 | expr = GenerateTexture(operation, "Lod", {}); | ||
| 2077 | } else { | ||
| 2078 | expr = GenerateTexture(operation, "Lod", | ||
| 2079 | {TextureArgument{Type::Float, meta->lod}, TextureOffset{}}); | ||
| 2080 | } | ||
| 2081 | |||
| 2005 | if (meta->sampler.is_shadow) { | 2082 | if (meta->sampler.is_shadow) { |
| 2006 | expr = "vec4(" + expr + ')'; | 2083 | expr = "vec4(" + expr + ')'; |
| 2007 | } | 2084 | } |
| @@ -2710,6 +2787,7 @@ private: | |||
| 2710 | const std::string_view identifier; | 2787 | const std::string_view identifier; |
| 2711 | const std::string_view suffix; | 2788 | const std::string_view suffix; |
| 2712 | const Header header; | 2789 | const Header header; |
| 2790 | const bool use_unified_uniforms; | ||
| 2713 | std::unordered_map<u8, VaryingTFB> transform_feedback; | 2791 | std::unordered_map<u8, VaryingTFB> transform_feedback; |
| 2714 | 2792 | ||
| 2715 | ShaderWriter code; | 2793 | ShaderWriter code; |
| @@ -2905,7 +2983,7 @@ void GLSLDecompiler::DecompileAST() { | |||
| 2905 | 2983 | ||
| 2906 | } // Anonymous namespace | 2984 | } // Anonymous namespace |
| 2907 | 2985 | ||
| 2908 | ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir) { | 2986 | ShaderEntries MakeEntries(const Device& device, const ShaderIR& ir, ShaderType stage) { |
| 2909 | ShaderEntries entries; | 2987 | ShaderEntries entries; |
| 2910 | for (const auto& cbuf : ir.GetConstantBuffers()) { | 2988 | for (const auto& cbuf : ir.GetConstantBuffers()) { |
| 2911 | entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(), | 2989 | entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(), |
| @@ -2926,6 +3004,7 @@ ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir) { | |||
| 2926 | entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i; | 3004 | entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i; |
| 2927 | } | 3005 | } |
| 2928 | entries.shader_length = ir.GetLength(); | 3006 | entries.shader_length = ir.GetLength(); |
| 3007 | entries.use_unified_uniforms = UseUnifiedUniforms(device, ir, stage); | ||
| 2929 | return entries; | 3008 | return entries; |
| 2930 | } | 3009 | } |
| 2931 | 3010 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index e8a178764..451c9689a 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h | |||
| @@ -53,11 +53,13 @@ struct ShaderEntries { | |||
| 53 | std::vector<GlobalMemoryEntry> global_memory_entries; | 53 | std::vector<GlobalMemoryEntry> global_memory_entries; |
| 54 | std::vector<SamplerEntry> samplers; | 54 | std::vector<SamplerEntry> samplers; |
| 55 | std::vector<ImageEntry> images; | 55 | std::vector<ImageEntry> images; |
| 56 | u32 clip_distances{}; | ||
| 57 | std::size_t shader_length{}; | 56 | std::size_t shader_length{}; |
| 57 | u32 clip_distances{}; | ||
| 58 | bool use_unified_uniforms{}; | ||
| 58 | }; | 59 | }; |
| 59 | 60 | ||
| 60 | ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir); | 61 | ShaderEntries MakeEntries(const Device& device, const VideoCommon::Shader::ShaderIR& ir, |
| 62 | Tegra::Engines::ShaderType stage); | ||
| 61 | 63 | ||
| 62 | std::string DecompileShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir, | 64 | std::string DecompileShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir, |
| 63 | const VideoCommon::Shader::Registry& registry, | 65 | const VideoCommon::Shader::Registry& registry, |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 9e95a122b..2dcc2b0eb 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -29,6 +29,8 @@ using VideoCommon::Shader::KeyMap; | |||
| 29 | 29 | ||
| 30 | namespace { | 30 | namespace { |
| 31 | 31 | ||
| 32 | using VideoCommon::Shader::SeparateSamplerKey; | ||
| 33 | |||
| 32 | using ShaderCacheVersionHash = std::array<u8, 64>; | 34 | using ShaderCacheVersionHash = std::array<u8, 64>; |
| 33 | 35 | ||
| 34 | struct ConstBufferKey { | 36 | struct ConstBufferKey { |
| @@ -37,18 +39,26 @@ struct ConstBufferKey { | |||
| 37 | u32 value = 0; | 39 | u32 value = 0; |
| 38 | }; | 40 | }; |
| 39 | 41 | ||
| 40 | struct BoundSamplerKey { | 42 | struct BoundSamplerEntry { |
| 41 | u32 offset = 0; | 43 | u32 offset = 0; |
| 42 | Tegra::Engines::SamplerDescriptor sampler; | 44 | Tegra::Engines::SamplerDescriptor sampler; |
| 43 | }; | 45 | }; |
| 44 | 46 | ||
| 45 | struct BindlessSamplerKey { | 47 | struct SeparateSamplerEntry { |
| 48 | u32 cbuf1 = 0; | ||
| 49 | u32 cbuf2 = 0; | ||
| 50 | u32 offset1 = 0; | ||
| 51 | u32 offset2 = 0; | ||
| 52 | Tegra::Engines::SamplerDescriptor sampler; | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct BindlessSamplerEntry { | ||
| 46 | u32 cbuf = 0; | 56 | u32 cbuf = 0; |
| 47 | u32 offset = 0; | 57 | u32 offset = 0; |
| 48 | Tegra::Engines::SamplerDescriptor sampler; | 58 | Tegra::Engines::SamplerDescriptor sampler; |
| 49 | }; | 59 | }; |
| 50 | 60 | ||
| 51 | constexpr u32 NativeVersion = 20; | 61 | constexpr u32 NativeVersion = 21; |
| 52 | 62 | ||
| 53 | ShaderCacheVersionHash GetShaderCacheVersionHash() { | 63 | ShaderCacheVersionHash GetShaderCacheVersionHash() { |
| 54 | ShaderCacheVersionHash hash{}; | 64 | ShaderCacheVersionHash hash{}; |
| @@ -87,12 +97,14 @@ bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) { | |||
| 87 | u32 texture_handler_size_value; | 97 | u32 texture_handler_size_value; |
| 88 | u32 num_keys; | 98 | u32 num_keys; |
| 89 | u32 num_bound_samplers; | 99 | u32 num_bound_samplers; |
| 100 | u32 num_separate_samplers; | ||
| 90 | u32 num_bindless_samplers; | 101 | u32 num_bindless_samplers; |
| 91 | if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 || | 102 | if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 || |
| 92 | file.ReadArray(&is_texture_handler_size_known, 1) != 1 || | 103 | file.ReadArray(&is_texture_handler_size_known, 1) != 1 || |
| 93 | file.ReadArray(&texture_handler_size_value, 1) != 1 || | 104 | file.ReadArray(&texture_handler_size_value, 1) != 1 || |
| 94 | file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 || | 105 | file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 || |
| 95 | file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 || | 106 | file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 || |
| 107 | file.ReadArray(&num_separate_samplers, 1) != 1 || | ||
| 96 | file.ReadArray(&num_bindless_samplers, 1) != 1) { | 108 | file.ReadArray(&num_bindless_samplers, 1) != 1) { |
| 97 | return false; | 109 | return false; |
| 98 | } | 110 | } |
| @@ -101,23 +113,32 @@ bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) { | |||
| 101 | } | 113 | } |
| 102 | 114 | ||
| 103 | std::vector<ConstBufferKey> flat_keys(num_keys); | 115 | std::vector<ConstBufferKey> flat_keys(num_keys); |
| 104 | std::vector<BoundSamplerKey> flat_bound_samplers(num_bound_samplers); | 116 | std::vector<BoundSamplerEntry> flat_bound_samplers(num_bound_samplers); |
| 105 | std::vector<BindlessSamplerKey> flat_bindless_samplers(num_bindless_samplers); | 117 | std::vector<SeparateSamplerEntry> flat_separate_samplers(num_separate_samplers); |
| 118 | std::vector<BindlessSamplerEntry> flat_bindless_samplers(num_bindless_samplers); | ||
| 106 | if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() || | 119 | if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() || |
| 107 | file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) != | 120 | file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) != |
| 108 | flat_bound_samplers.size() || | 121 | flat_bound_samplers.size() || |
| 122 | file.ReadArray(flat_separate_samplers.data(), flat_separate_samplers.size()) != | ||
| 123 | flat_separate_samplers.size() || | ||
| 109 | file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) != | 124 | file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) != |
| 110 | flat_bindless_samplers.size()) { | 125 | flat_bindless_samplers.size()) { |
| 111 | return false; | 126 | return false; |
| 112 | } | 127 | } |
| 113 | for (const auto& key : flat_keys) { | 128 | for (const auto& entry : flat_keys) { |
| 114 | keys.insert({{key.cbuf, key.offset}, key.value}); | 129 | keys.insert({{entry.cbuf, entry.offset}, entry.value}); |
| 115 | } | 130 | } |
| 116 | for (const auto& key : flat_bound_samplers) { | 131 | for (const auto& entry : flat_bound_samplers) { |
| 117 | bound_samplers.emplace(key.offset, key.sampler); | 132 | bound_samplers.emplace(entry.offset, entry.sampler); |
| 118 | } | 133 | } |
| 119 | for (const auto& key : flat_bindless_samplers) { | 134 | for (const auto& entry : flat_separate_samplers) { |
| 120 | bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler}); | 135 | SeparateSamplerKey key; |
| 136 | key.buffers = {entry.cbuf1, entry.cbuf2}; | ||
| 137 | key.offsets = {entry.offset1, entry.offset2}; | ||
| 138 | separate_samplers.emplace(key, entry.sampler); | ||
| 139 | } | ||
| 140 | for (const auto& entry : flat_bindless_samplers) { | ||
| 141 | bindless_samplers.insert({{entry.cbuf, entry.offset}, entry.sampler}); | ||
| 121 | } | 142 | } |
| 122 | 143 | ||
| 123 | return true; | 144 | return true; |
| @@ -142,6 +163,7 @@ bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const { | |||
| 142 | file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 || | 163 | file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 || |
| 143 | file.WriteObject(static_cast<u32>(keys.size())) != 1 || | 164 | file.WriteObject(static_cast<u32>(keys.size())) != 1 || |
| 144 | file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 || | 165 | file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 || |
| 166 | file.WriteObject(static_cast<u32>(separate_samplers.size())) != 1 || | ||
| 145 | file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) { | 167 | file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) { |
| 146 | return false; | 168 | return false; |
| 147 | } | 169 | } |
| @@ -152,22 +174,34 @@ bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const { | |||
| 152 | flat_keys.push_back(ConstBufferKey{address.first, address.second, value}); | 174 | flat_keys.push_back(ConstBufferKey{address.first, address.second, value}); |
| 153 | } | 175 | } |
| 154 | 176 | ||
| 155 | std::vector<BoundSamplerKey> flat_bound_samplers; | 177 | std::vector<BoundSamplerEntry> flat_bound_samplers; |
| 156 | flat_bound_samplers.reserve(bound_samplers.size()); | 178 | flat_bound_samplers.reserve(bound_samplers.size()); |
| 157 | for (const auto& [address, sampler] : bound_samplers) { | 179 | for (const auto& [address, sampler] : bound_samplers) { |
| 158 | flat_bound_samplers.push_back(BoundSamplerKey{address, sampler}); | 180 | flat_bound_samplers.push_back(BoundSamplerEntry{address, sampler}); |
| 181 | } | ||
| 182 | |||
| 183 | std::vector<SeparateSamplerEntry> flat_separate_samplers; | ||
| 184 | flat_separate_samplers.reserve(separate_samplers.size()); | ||
| 185 | for (const auto& [key, sampler] : separate_samplers) { | ||
| 186 | SeparateSamplerEntry entry; | ||
| 187 | std::tie(entry.cbuf1, entry.cbuf2) = key.buffers; | ||
| 188 | std::tie(entry.offset1, entry.offset2) = key.offsets; | ||
| 189 | entry.sampler = sampler; | ||
| 190 | flat_separate_samplers.push_back(entry); | ||
| 159 | } | 191 | } |
| 160 | 192 | ||
| 161 | std::vector<BindlessSamplerKey> flat_bindless_samplers; | 193 | std::vector<BindlessSamplerEntry> flat_bindless_samplers; |
| 162 | flat_bindless_samplers.reserve(bindless_samplers.size()); | 194 | flat_bindless_samplers.reserve(bindless_samplers.size()); |
| 163 | for (const auto& [address, sampler] : bindless_samplers) { | 195 | for (const auto& [address, sampler] : bindless_samplers) { |
| 164 | flat_bindless_samplers.push_back( | 196 | flat_bindless_samplers.push_back( |
| 165 | BindlessSamplerKey{address.first, address.second, sampler}); | 197 | BindlessSamplerEntry{address.first, address.second, sampler}); |
| 166 | } | 198 | } |
| 167 | 199 | ||
| 168 | return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() && | 200 | return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() && |
| 169 | file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) == | 201 | file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) == |
| 170 | flat_bound_samplers.size() && | 202 | flat_bound_samplers.size() && |
| 203 | file.WriteArray(flat_separate_samplers.data(), flat_separate_samplers.size()) == | ||
| 204 | flat_separate_samplers.size() && | ||
| 171 | file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) == | 205 | file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) == |
| 172 | flat_bindless_samplers.size(); | 206 | flat_bindless_samplers.size(); |
| 173 | } | 207 | } |
| @@ -179,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default; | |||
| 179 | std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() { | 213 | std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() { |
| 180 | // Skip games without title id | 214 | // Skip games without title id |
| 181 | const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; | 215 | const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; |
| 182 | if (!Settings::values.use_disk_shader_cache || !has_title_id) { | 216 | if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) { |
| 183 | return {}; | 217 | return {}; |
| 184 | } | 218 | } |
| 185 | 219 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index d5be52e40..a79cef0e9 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -57,6 +57,7 @@ struct ShaderDiskCacheEntry { | |||
| 57 | VideoCommon::Shader::ComputeInfo compute_info; | 57 | VideoCommon::Shader::ComputeInfo compute_info; |
| 58 | VideoCommon::Shader::KeyMap keys; | 58 | VideoCommon::Shader::KeyMap keys; |
| 59 | VideoCommon::Shader::BoundSamplerMap bound_samplers; | 59 | VideoCommon::Shader::BoundSamplerMap bound_samplers; |
| 60 | VideoCommon::Shader::SeparateSamplerMap separate_samplers; | ||
| 60 | VideoCommon::Shader::BindlessSamplerMap bindless_samplers; | 61 | VideoCommon::Shader::BindlessSamplerMap bindless_samplers; |
| 61 | }; | 62 | }; |
| 62 | 63 | ||
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index 6ec328c53..3655ff629 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp | |||
| @@ -2,11 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <deque> | 5 | #include <tuple> |
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | |||
| 7 | #include "common/alignment.h" | 8 | #include "common/alignment.h" |
| 8 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 9 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "video_core/renderer_opengl/gl_device.h" | ||
| 10 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | 12 | #include "video_core/renderer_opengl/gl_stream_buffer.h" |
| 11 | 13 | ||
| 12 | MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", | 14 | MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", |
| @@ -14,8 +16,7 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", | |||
| 14 | 16 | ||
| 15 | namespace OpenGL { | 17 | namespace OpenGL { |
| 16 | 18 | ||
| 17 | OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent, | 19 | OGLStreamBuffer::OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage) |
| 18 | bool use_persistent) | ||
| 19 | : buffer_size(size) { | 20 | : buffer_size(size) { |
| 20 | gl_buffer.Create(); | 21 | gl_buffer.Create(); |
| 21 | 22 | ||
| @@ -29,34 +30,22 @@ OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool p | |||
| 29 | allocate_size *= 2; | 30 | allocate_size *= 2; |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | if (use_persistent) { | 33 | static constexpr GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; |
| 33 | persistent = true; | 34 | glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); |
| 34 | coherent = prefer_coherent; | 35 | mapped_ptr = static_cast<u8*>( |
| 35 | const GLbitfield flags = | 36 | glMapNamedBufferRange(gl_buffer.handle, 0, buffer_size, flags | GL_MAP_FLUSH_EXPLICIT_BIT)); |
| 36 | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); | 37 | |
| 37 | glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); | 38 | if (device.HasVertexBufferUnifiedMemory()) { |
| 38 | mapped_ptr = static_cast<u8*>(glMapNamedBufferRange( | 39 | glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_ONLY); |
| 39 | gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); | 40 | glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address); |
| 40 | } else { | ||
| 41 | glNamedBufferData(gl_buffer.handle, allocate_size, nullptr, GL_STREAM_DRAW); | ||
| 42 | } | 41 | } |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | OGLStreamBuffer::~OGLStreamBuffer() { | 44 | OGLStreamBuffer::~OGLStreamBuffer() { |
| 46 | if (persistent) { | 45 | glUnmapNamedBuffer(gl_buffer.handle); |
| 47 | glUnmapNamedBuffer(gl_buffer.handle); | ||
| 48 | } | ||
| 49 | gl_buffer.Release(); | 46 | gl_buffer.Release(); |
| 50 | } | 47 | } |
| 51 | 48 | ||
| 52 | GLuint OGLStreamBuffer::GetHandle() const { | ||
| 53 | return gl_buffer.handle; | ||
| 54 | } | ||
| 55 | |||
| 56 | GLsizeiptr OGLStreamBuffer::GetSize() const { | ||
| 57 | return buffer_size; | ||
| 58 | } | ||
| 59 | |||
| 60 | std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) { | 49 | std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) { |
| 61 | ASSERT(size <= buffer_size); | 50 | ASSERT(size <= buffer_size); |
| 62 | ASSERT(alignment <= buffer_size); | 51 | ASSERT(alignment <= buffer_size); |
| @@ -68,36 +57,21 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a | |||
| 68 | 57 | ||
| 69 | bool invalidate = false; | 58 | bool invalidate = false; |
| 70 | if (buffer_pos + size > buffer_size) { | 59 | if (buffer_pos + size > buffer_size) { |
| 60 | MICROPROFILE_SCOPE(OpenGL_StreamBuffer); | ||
| 61 | glInvalidateBufferData(gl_buffer.handle); | ||
| 62 | |||
| 71 | buffer_pos = 0; | 63 | buffer_pos = 0; |
| 72 | invalidate = true; | 64 | invalidate = true; |
| 73 | |||
| 74 | if (persistent) { | ||
| 75 | glUnmapNamedBuffer(gl_buffer.handle); | ||
| 76 | } | ||
| 77 | } | 65 | } |
| 78 | 66 | ||
| 79 | if (invalidate || !persistent) { | 67 | return std::make_tuple(mapped_ptr + buffer_pos, buffer_pos, invalidate); |
| 80 | MICROPROFILE_SCOPE(OpenGL_StreamBuffer); | ||
| 81 | GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | | ||
| 82 | (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | | ||
| 83 | (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); | ||
| 84 | mapped_ptr = static_cast<u8*>( | ||
| 85 | glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags)); | ||
| 86 | mapped_offset = buffer_pos; | ||
| 87 | } | ||
| 88 | |||
| 89 | return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate); | ||
| 90 | } | 68 | } |
| 91 | 69 | ||
| 92 | void OGLStreamBuffer::Unmap(GLsizeiptr size) { | 70 | void OGLStreamBuffer::Unmap(GLsizeiptr size) { |
| 93 | ASSERT(size <= mapped_size); | 71 | ASSERT(size <= mapped_size); |
| 94 | 72 | ||
| 95 | if (!coherent && size > 0) { | 73 | if (size > 0) { |
| 96 | glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size); | 74 | glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos, size); |
| 97 | } | ||
| 98 | |||
| 99 | if (!persistent) { | ||
| 100 | glUnmapNamedBuffer(gl_buffer.handle); | ||
| 101 | } | 75 | } |
| 102 | 76 | ||
| 103 | buffer_pos += size; | 77 | buffer_pos += size; |
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index f8383cbd4..307a67113 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h | |||
| @@ -11,15 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | namespace OpenGL { | 12 | namespace OpenGL { |
| 13 | 13 | ||
| 14 | class Device; | ||
| 15 | |||
| 14 | class OGLStreamBuffer : private NonCopyable { | 16 | class OGLStreamBuffer : private NonCopyable { |
| 15 | public: | 17 | public: |
| 16 | explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false, | 18 | explicit OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage); |
| 17 | bool use_persistent = true); | ||
| 18 | ~OGLStreamBuffer(); | 19 | ~OGLStreamBuffer(); |
| 19 | 20 | ||
| 20 | GLuint GetHandle() const; | ||
| 21 | GLsizeiptr GetSize() const; | ||
| 22 | |||
| 23 | /* | 21 | /* |
| 24 | * Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes | 22 | * Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes |
| 25 | * and the optional alignment requirement. | 23 | * and the optional alignment requirement. |
| @@ -32,15 +30,24 @@ public: | |||
| 32 | 30 | ||
| 33 | void Unmap(GLsizeiptr size); | 31 | void Unmap(GLsizeiptr size); |
| 34 | 32 | ||
| 33 | GLuint Handle() const { | ||
| 34 | return gl_buffer.handle; | ||
| 35 | } | ||
| 36 | |||
| 37 | u64 Address() const { | ||
| 38 | return gpu_address; | ||
| 39 | } | ||
| 40 | |||
| 41 | GLsizeiptr Size() const noexcept { | ||
| 42 | return buffer_size; | ||
| 43 | } | ||
| 44 | |||
| 35 | private: | 45 | private: |
| 36 | OGLBuffer gl_buffer; | 46 | OGLBuffer gl_buffer; |
| 37 | 47 | ||
| 38 | bool coherent = false; | 48 | GLuint64EXT gpu_address = 0; |
| 39 | bool persistent = false; | ||
| 40 | |||
| 41 | GLintptr buffer_pos = 0; | 49 | GLintptr buffer_pos = 0; |
| 42 | GLsizeiptr buffer_size = 0; | 50 | GLsizeiptr buffer_size = 0; |
| 43 | GLintptr mapped_offset = 0; | ||
| 44 | GLsizeiptr mapped_size = 0; | 51 | GLsizeiptr mapped_size = 0; |
| 45 | u8* mapped_ptr = nullptr; | 52 | u8* mapped_ptr = nullptr; |
| 46 | }; | 53 | }; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 4faa8b90c..61505879b 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -263,9 +263,14 @@ CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& param | |||
| 263 | target = GetTextureTarget(params.target); | 263 | target = GetTextureTarget(params.target); |
| 264 | texture = CreateTexture(params, target, internal_format, texture_buffer); | 264 | texture = CreateTexture(params, target, internal_format, texture_buffer); |
| 265 | DecorateSurfaceName(); | 265 | DecorateSurfaceName(); |
| 266 | main_view = CreateViewInner( | 266 | |
| 267 | ViewParams(params.target, 0, params.is_layered ? params.depth : 1, 0, params.num_levels), | 267 | u32 num_layers = 1; |
| 268 | true); | 268 | if (params.is_layered || params.target == SurfaceTarget::Texture3D) { |
| 269 | num_layers = params.depth; | ||
| 270 | } | ||
| 271 | |||
| 272 | main_view = | ||
| 273 | CreateViewInner(ViewParams(params.target, 0, num_layers, 0, params.num_levels), true); | ||
| 269 | } | 274 | } |
| 270 | 275 | ||
| 271 | CachedSurface::~CachedSurface() = default; | 276 | CachedSurface::~CachedSurface() = default; |
| @@ -404,8 +409,7 @@ View CachedSurface::CreateViewInner(const ViewParams& view_key, const bool is_pr | |||
| 404 | 409 | ||
| 405 | CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params, | 410 | CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params, |
| 406 | bool is_proxy) | 411 | bool is_proxy) |
| 407 | : VideoCommon::ViewBase(params), surface{surface}, | 412 | : VideoCommon::ViewBase(params), surface{surface}, format{surface.internal_format}, |
| 408 | format{GetFormatTuple(surface.GetSurfaceParams().pixel_format).internal_format}, | ||
| 409 | target{GetTextureTarget(params.target)}, is_proxy{is_proxy} { | 413 | target{GetTextureTarget(params.target)}, is_proxy{is_proxy} { |
| 410 | if (!is_proxy) { | 414 | if (!is_proxy) { |
| 411 | main_view = CreateTextureView(); | 415 | main_view = CreateTextureView(); |
| @@ -414,20 +418,23 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p | |||
| 414 | 418 | ||
| 415 | CachedSurfaceView::~CachedSurfaceView() = default; | 419 | CachedSurfaceView::~CachedSurfaceView() = default; |
| 416 | 420 | ||
| 417 | void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const { | 421 | void CachedSurfaceView::Attach(GLenum attachment, GLenum fb_target) const { |
| 418 | ASSERT(params.num_levels == 1); | 422 | ASSERT(params.num_levels == 1); |
| 419 | 423 | ||
| 424 | if (params.target == SurfaceTarget::Texture3D) { | ||
| 425 | if (params.num_layers > 1) { | ||
| 426 | ASSERT(params.base_layer == 0); | ||
| 427 | glFramebufferTexture(fb_target, attachment, surface.texture.handle, params.base_level); | ||
| 428 | } else { | ||
| 429 | glFramebufferTexture3D(fb_target, attachment, target, surface.texture.handle, | ||
| 430 | params.base_level, params.base_layer); | ||
| 431 | } | ||
| 432 | return; | ||
| 433 | } | ||
| 434 | |||
| 420 | if (params.num_layers > 1) { | 435 | if (params.num_layers > 1) { |
| 421 | // Layered framebuffer attachments | ||
| 422 | UNIMPLEMENTED_IF(params.base_layer != 0); | 436 | UNIMPLEMENTED_IF(params.base_layer != 0); |
| 423 | 437 | glFramebufferTexture(fb_target, attachment, GetTexture(), 0); | |
| 424 | switch (params.target) { | ||
| 425 | case SurfaceTarget::Texture2DArray: | ||
| 426 | glFramebufferTexture(target, attachment, GetTexture(), 0); | ||
| 427 | break; | ||
| 428 | default: | ||
| 429 | UNIMPLEMENTED(); | ||
| 430 | } | ||
| 431 | return; | 438 | return; |
| 432 | } | 439 | } |
| 433 | 440 | ||
| @@ -435,16 +442,16 @@ void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const { | |||
| 435 | const GLuint texture = surface.GetTexture(); | 442 | const GLuint texture = surface.GetTexture(); |
| 436 | switch (surface.GetSurfaceParams().target) { | 443 | switch (surface.GetSurfaceParams().target) { |
| 437 | case SurfaceTarget::Texture1D: | 444 | case SurfaceTarget::Texture1D: |
| 438 | glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level); | 445 | glFramebufferTexture1D(fb_target, attachment, view_target, texture, params.base_level); |
| 439 | break; | 446 | break; |
| 440 | case SurfaceTarget::Texture2D: | 447 | case SurfaceTarget::Texture2D: |
| 441 | glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level); | 448 | glFramebufferTexture2D(fb_target, attachment, view_target, texture, params.base_level); |
| 442 | break; | 449 | break; |
| 443 | case SurfaceTarget::Texture1DArray: | 450 | case SurfaceTarget::Texture1DArray: |
| 444 | case SurfaceTarget::Texture2DArray: | 451 | case SurfaceTarget::Texture2DArray: |
| 445 | case SurfaceTarget::TextureCubemap: | 452 | case SurfaceTarget::TextureCubemap: |
| 446 | case SurfaceTarget::TextureCubeArray: | 453 | case SurfaceTarget::TextureCubeArray: |
| 447 | glFramebufferTextureLayer(target, attachment, texture, params.base_level, | 454 | glFramebufferTextureLayer(fb_target, attachment, texture, params.base_level, |
| 448 | params.base_layer); | 455 | params.base_layer); |
| 449 | break; | 456 | break; |
| 450 | default: | 457 | default: |
| @@ -501,8 +508,13 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const { | |||
| 501 | OGLTextureView texture_view; | 508 | OGLTextureView texture_view; |
| 502 | texture_view.Create(); | 509 | texture_view.Create(); |
| 503 | 510 | ||
| 504 | glTextureView(texture_view.handle, target, surface.texture.handle, format, params.base_level, | 511 | if (target == GL_TEXTURE_3D) { |
| 505 | params.num_levels, params.base_layer, params.num_layers); | 512 | glTextureView(texture_view.handle, target, surface.texture.handle, format, |
| 513 | params.base_level, params.num_levels, 0, 1); | ||
| 514 | } else { | ||
| 515 | glTextureView(texture_view.handle, target, surface.texture.handle, format, | ||
| 516 | params.base_level, params.num_levels, params.base_layer, params.num_layers); | ||
| 517 | } | ||
| 506 | ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle); | 518 | ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle); |
| 507 | 519 | ||
| 508 | return texture_view; | 520 | return texture_view; |
| @@ -545,8 +557,8 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view, | |||
| 545 | const Tegra::Engines::Fermi2D::Config& copy_config) { | 557 | const Tegra::Engines::Fermi2D::Config& copy_config) { |
| 546 | const auto& src_params{src_view->GetSurfaceParams()}; | 558 | const auto& src_params{src_view->GetSurfaceParams()}; |
| 547 | const auto& dst_params{dst_view->GetSurfaceParams()}; | 559 | const auto& dst_params{dst_view->GetSurfaceParams()}; |
| 548 | UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D); | 560 | UNIMPLEMENTED_IF(src_params.depth != 1); |
| 549 | UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D); | 561 | UNIMPLEMENTED_IF(dst_params.depth != 1); |
| 550 | 562 | ||
| 551 | state_tracker.NotifyScissor0(); | 563 | state_tracker.NotifyScissor0(); |
| 552 | state_tracker.NotifyFramebuffer(); | 564 | state_tracker.NotifyFramebuffer(); |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 8a2ac8603..bfc4ddf5d 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -80,8 +80,10 @@ public: | |||
| 80 | explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params, bool is_proxy); | 80 | explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params, bool is_proxy); |
| 81 | ~CachedSurfaceView(); | 81 | ~CachedSurfaceView(); |
| 82 | 82 | ||
| 83 | /// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER | 83 | /// @brief Attaches this texture view to the currently bound fb_target framebuffer |
| 84 | void Attach(GLenum attachment, GLenum target) const; | 84 | /// @param attachment Attachment to bind textures to |
| 85 | /// @param fb_target Framebuffer target to attach to (e.g. DRAW_FRAMEBUFFER) | ||
| 86 | void Attach(GLenum attachment, GLenum fb_target) const; | ||
| 85 | 87 | ||
| 86 | GLuint GetTexture(Tegra::Texture::SwizzleSource x_source, | 88 | GLuint GetTexture(Tegra::Texture::SwizzleSource x_source, |
| 87 | Tegra::Texture::SwizzleSource y_source, | 89 | Tegra::Texture::SwizzleSource y_source, |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 994ae98eb..fe9bd4b5a 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -24,10 +24,11 @@ namespace MaxwellToGL { | |||
| 24 | 24 | ||
| 25 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 25 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 26 | 26 | ||
| 27 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | 27 | inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { |
| 28 | switch (attrib.type) { | 28 | switch (attrib.type) { |
| 29 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 30 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | 29 | case Maxwell::VertexAttribute::Type::UnsignedNorm: |
| 30 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||
| 31 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 31 | switch (attrib.size) { | 32 | switch (attrib.size) { |
| 32 | case Maxwell::VertexAttribute::Size::Size_8: | 33 | case Maxwell::VertexAttribute::Size::Size_8: |
| 33 | case Maxwell::VertexAttribute::Size::Size_8_8: | 34 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -46,12 +47,11 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 46 | return GL_UNSIGNED_INT; | 47 | return GL_UNSIGNED_INT; |
| 47 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 48 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 48 | return GL_UNSIGNED_INT_2_10_10_10_REV; | 49 | return GL_UNSIGNED_INT_2_10_10_10_REV; |
| 49 | default: | ||
| 50 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 51 | return {}; | ||
| 52 | } | 50 | } |
| 53 | case Maxwell::VertexAttribute::Type::SignedInt: | 51 | break; |
| 54 | case Maxwell::VertexAttribute::Type::SignedNorm: | 52 | case Maxwell::VertexAttribute::Type::SignedNorm: |
| 53 | case Maxwell::VertexAttribute::Type::SignedScaled: | ||
| 54 | case Maxwell::VertexAttribute::Type::SignedInt: | ||
| 55 | switch (attrib.size) { | 55 | switch (attrib.size) { |
| 56 | case Maxwell::VertexAttribute::Size::Size_8: | 56 | case Maxwell::VertexAttribute::Size::Size_8: |
| 57 | case Maxwell::VertexAttribute::Size::Size_8_8: | 57 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -70,10 +70,8 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 70 | return GL_INT; | 70 | return GL_INT; |
| 71 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 71 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 72 | return GL_INT_2_10_10_10_REV; | 72 | return GL_INT_2_10_10_10_REV; |
| 73 | default: | ||
| 74 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 75 | return {}; | ||
| 76 | } | 73 | } |
| 74 | break; | ||
| 77 | case Maxwell::VertexAttribute::Type::Float: | 75 | case Maxwell::VertexAttribute::Type::Float: |
| 78 | switch (attrib.size) { | 76 | switch (attrib.size) { |
| 79 | case Maxwell::VertexAttribute::Size::Size_16: | 77 | case Maxwell::VertexAttribute::Size::Size_16: |
| @@ -86,46 +84,12 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 86 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 84 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 87 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 85 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 88 | return GL_FLOAT; | 86 | return GL_FLOAT; |
| 89 | default: | ||
| 90 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 91 | return {}; | ||
| 92 | } | ||
| 93 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||
| 94 | switch (attrib.size) { | ||
| 95 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 96 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 97 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 98 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 99 | return GL_UNSIGNED_BYTE; | ||
| 100 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 101 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 102 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 103 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 104 | return GL_UNSIGNED_SHORT; | ||
| 105 | default: | ||
| 106 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 107 | return {}; | ||
| 108 | } | 87 | } |
| 109 | case Maxwell::VertexAttribute::Type::SignedScaled: | 88 | break; |
| 110 | switch (attrib.size) { | ||
| 111 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 112 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 113 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 114 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 115 | return GL_BYTE; | ||
| 116 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 117 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 118 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 119 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 120 | return GL_SHORT; | ||
| 121 | default: | ||
| 122 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 123 | return {}; | ||
| 124 | } | ||
| 125 | default: | ||
| 126 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); | ||
| 127 | return {}; | ||
| 128 | } | 89 | } |
| 90 | UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", attrib.TypeString(), | ||
| 91 | attrib.SizeString()); | ||
| 92 | return {}; | ||
| 129 | } | 93 | } |
| 130 | 94 | ||
| 131 | inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { | 95 | inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { |
| @@ -137,8 +101,7 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { | |||
| 137 | case Maxwell::IndexFormat::UnsignedInt: | 101 | case Maxwell::IndexFormat::UnsignedInt: |
| 138 | return GL_UNSIGNED_INT; | 102 | return GL_UNSIGNED_INT; |
| 139 | } | 103 | } |
| 140 | LOG_CRITICAL(Render_OpenGL, "Unimplemented index_format={}", static_cast<u32>(index_format)); | 104 | UNREACHABLE_MSG("Invalid index_format={}", static_cast<u32>(index_format)); |
| 141 | UNREACHABLE(); | ||
| 142 | return {}; | 105 | return {}; |
| 143 | } | 106 | } |
| 144 | 107 | ||
| @@ -180,33 +143,32 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | |||
| 180 | } | 143 | } |
| 181 | 144 | ||
| 182 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, | 145 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, |
| 183 | Tegra::Texture::TextureMipmapFilter mip_filter_mode) { | 146 | Tegra::Texture::TextureMipmapFilter mipmap_filter_mode) { |
| 184 | switch (filter_mode) { | 147 | switch (filter_mode) { |
| 185 | case Tegra::Texture::TextureFilter::Linear: { | 148 | case Tegra::Texture::TextureFilter::Nearest: |
| 186 | switch (mip_filter_mode) { | 149 | switch (mipmap_filter_mode) { |
| 187 | case Tegra::Texture::TextureMipmapFilter::None: | 150 | case Tegra::Texture::TextureMipmapFilter::None: |
| 188 | return GL_LINEAR; | 151 | return GL_NEAREST; |
| 189 | case Tegra::Texture::TextureMipmapFilter::Nearest: | 152 | case Tegra::Texture::TextureMipmapFilter::Nearest: |
| 190 | return GL_LINEAR_MIPMAP_NEAREST; | 153 | return GL_NEAREST_MIPMAP_NEAREST; |
| 191 | case Tegra::Texture::TextureMipmapFilter::Linear: | 154 | case Tegra::Texture::TextureMipmapFilter::Linear: |
| 192 | return GL_LINEAR_MIPMAP_LINEAR; | 155 | return GL_NEAREST_MIPMAP_LINEAR; |
| 193 | } | 156 | } |
| 194 | break; | 157 | break; |
| 195 | } | 158 | case Tegra::Texture::TextureFilter::Linear: |
| 196 | case Tegra::Texture::TextureFilter::Nearest: { | 159 | switch (mipmap_filter_mode) { |
| 197 | switch (mip_filter_mode) { | ||
| 198 | case Tegra::Texture::TextureMipmapFilter::None: | 160 | case Tegra::Texture::TextureMipmapFilter::None: |
| 199 | return GL_NEAREST; | 161 | return GL_LINEAR; |
| 200 | case Tegra::Texture::TextureMipmapFilter::Nearest: | 162 | case Tegra::Texture::TextureMipmapFilter::Nearest: |
| 201 | return GL_NEAREST_MIPMAP_NEAREST; | 163 | return GL_LINEAR_MIPMAP_NEAREST; |
| 202 | case Tegra::Texture::TextureMipmapFilter::Linear: | 164 | case Tegra::Texture::TextureMipmapFilter::Linear: |
| 203 | return GL_NEAREST_MIPMAP_LINEAR; | 165 | return GL_LINEAR_MIPMAP_LINEAR; |
| 204 | } | 166 | } |
| 205 | break; | 167 | break; |
| 206 | } | 168 | } |
| 207 | } | 169 | UNREACHABLE_MSG("Invalid texture filter mode={} and mipmap filter mode={}", |
| 208 | LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode)); | 170 | static_cast<u32>(filter_mode), static_cast<u32>(mipmap_filter_mode)); |
| 209 | return GL_LINEAR; | 171 | return GL_NEAREST; |
| 210 | } | 172 | } |
| 211 | 173 | ||
| 212 | inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { | 174 | inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { |
| @@ -229,10 +191,15 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { | |||
| 229 | } else { | 191 | } else { |
| 230 | return GL_MIRROR_CLAMP_TO_EDGE; | 192 | return GL_MIRROR_CLAMP_TO_EDGE; |
| 231 | } | 193 | } |
| 232 | default: | 194 | case Tegra::Texture::WrapMode::MirrorOnceClampOGL: |
| 233 | LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | 195 | if (GL_EXT_texture_mirror_clamp) { |
| 234 | return GL_REPEAT; | 196 | return GL_MIRROR_CLAMP_EXT; |
| 197 | } else { | ||
| 198 | return GL_MIRROR_CLAMP_TO_EDGE; | ||
| 199 | } | ||
| 235 | } | 200 | } |
| 201 | UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 202 | return GL_REPEAT; | ||
| 236 | } | 203 | } |
| 237 | 204 | ||
| 238 | inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { | 205 | inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { |
| @@ -254,8 +221,7 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { | |||
| 254 | case Tegra::Texture::DepthCompareFunc::Always: | 221 | case Tegra::Texture::DepthCompareFunc::Always: |
| 255 | return GL_ALWAYS; | 222 | return GL_ALWAYS; |
| 256 | } | 223 | } |
| 257 | LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}", | 224 | UNIMPLEMENTED_MSG("Unimplemented texture depth compare function={}", static_cast<u32>(func)); |
| 258 | static_cast<u32>(func)); | ||
| 259 | return GL_GREATER; | 225 | return GL_GREATER; |
| 260 | } | 226 | } |
| 261 | 227 | ||
| @@ -277,7 +243,7 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { | |||
| 277 | case Maxwell::Blend::Equation::MaxGL: | 243 | case Maxwell::Blend::Equation::MaxGL: |
| 278 | return GL_MAX; | 244 | return GL_MAX; |
| 279 | } | 245 | } |
| 280 | LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); | 246 | UNIMPLEMENTED_MSG("Unimplemented blend equation={}", static_cast<u32>(equation)); |
| 281 | return GL_FUNC_ADD; | 247 | return GL_FUNC_ADD; |
| 282 | } | 248 | } |
| 283 | 249 | ||
| @@ -341,7 +307,7 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { | |||
| 341 | case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: | 307 | case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: |
| 342 | return GL_ONE_MINUS_CONSTANT_ALPHA; | 308 | return GL_ONE_MINUS_CONSTANT_ALPHA; |
| 343 | } | 309 | } |
| 344 | LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); | 310 | UNIMPLEMENTED_MSG("Unimplemented blend factor={}", static_cast<u32>(factor)); |
| 345 | return GL_ZERO; | 311 | return GL_ZERO; |
| 346 | } | 312 | } |
| 347 | 313 | ||
| @@ -361,7 +327,7 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { | |||
| 361 | case Tegra::Texture::SwizzleSource::OneFloat: | 327 | case Tegra::Texture::SwizzleSource::OneFloat: |
| 362 | return GL_ONE; | 328 | return GL_ONE; |
| 363 | } | 329 | } |
| 364 | LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); | 330 | UNIMPLEMENTED_MSG("Unimplemented swizzle source={}", static_cast<u32>(source)); |
| 365 | return GL_ZERO; | 331 | return GL_ZERO; |
| 366 | } | 332 | } |
| 367 | 333 | ||
| @@ -392,7 +358,7 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { | |||
| 392 | case Maxwell::ComparisonOp::AlwaysOld: | 358 | case Maxwell::ComparisonOp::AlwaysOld: |
| 393 | return GL_ALWAYS; | 359 | return GL_ALWAYS; |
| 394 | } | 360 | } |
| 395 | LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); | 361 | UNIMPLEMENTED_MSG("Unimplemented comparison op={}", static_cast<u32>(comparison)); |
| 396 | return GL_ALWAYS; | 362 | return GL_ALWAYS; |
| 397 | } | 363 | } |
| 398 | 364 | ||
| @@ -423,7 +389,7 @@ inline GLenum StencilOp(Maxwell::StencilOp stencil) { | |||
| 423 | case Maxwell::StencilOp::DecrWrapOGL: | 389 | case Maxwell::StencilOp::DecrWrapOGL: |
| 424 | return GL_DECR_WRAP; | 390 | return GL_DECR_WRAP; |
| 425 | } | 391 | } |
| 426 | LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); | 392 | UNIMPLEMENTED_MSG("Unimplemented stencil op={}", static_cast<u32>(stencil)); |
| 427 | return GL_KEEP; | 393 | return GL_KEEP; |
| 428 | } | 394 | } |
| 429 | 395 | ||
| @@ -434,7 +400,7 @@ inline GLenum FrontFace(Maxwell::FrontFace front_face) { | |||
| 434 | case Maxwell::FrontFace::CounterClockWise: | 400 | case Maxwell::FrontFace::CounterClockWise: |
| 435 | return GL_CCW; | 401 | return GL_CCW; |
| 436 | } | 402 | } |
| 437 | LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); | 403 | UNIMPLEMENTED_MSG("Unimplemented front face cull={}", static_cast<u32>(front_face)); |
| 438 | return GL_CCW; | 404 | return GL_CCW; |
| 439 | } | 405 | } |
| 440 | 406 | ||
| @@ -447,7 +413,7 @@ inline GLenum CullFace(Maxwell::CullFace cull_face) { | |||
| 447 | case Maxwell::CullFace::FrontAndBack: | 413 | case Maxwell::CullFace::FrontAndBack: |
| 448 | return GL_FRONT_AND_BACK; | 414 | return GL_FRONT_AND_BACK; |
| 449 | } | 415 | } |
| 450 | LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); | 416 | UNIMPLEMENTED_MSG("Unimplemented cull face={}", static_cast<u32>(cull_face)); |
| 451 | return GL_BACK; | 417 | return GL_BACK; |
| 452 | } | 418 | } |
| 453 | 419 | ||
| @@ -486,7 +452,7 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) { | |||
| 486 | case Maxwell::LogicOperation::Set: | 452 | case Maxwell::LogicOperation::Set: |
| 487 | return GL_SET; | 453 | return GL_SET; |
| 488 | } | 454 | } |
| 489 | LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); | 455 | UNIMPLEMENTED_MSG("Unimplemented logic operation={}", static_cast<u32>(operation)); |
| 490 | return GL_COPY; | 456 | return GL_COPY; |
| 491 | } | 457 | } |
| 492 | 458 | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 6b489e6db..e66cdc083 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||
| 455 | void RendererOpenGL::InitOpenGLObjects() { | 455 | void RendererOpenGL::InitOpenGLObjects() { |
| 456 | frame_mailbox = std::make_unique<FrameMailbox>(); | 456 | frame_mailbox = std::make_unique<FrameMailbox>(); |
| 457 | 457 | ||
| 458 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, | 458 | glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), |
| 459 | 0.0f); | 459 | Settings::values.bg_blue.GetValue(), 0.0f); |
| 460 | 460 | ||
| 461 | // Create shader programs | 461 | // Create shader programs |
| 462 | OGLShader vertex_shader; | 462 | OGLShader vertex_shader; |
| @@ -488,6 +488,15 @@ void RendererOpenGL::InitOpenGLObjects() { | |||
| 488 | 488 | ||
| 489 | // Clear screen to black | 489 | // Clear screen to black |
| 490 | LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); | 490 | LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); |
| 491 | |||
| 492 | // Enable unified vertex attributes and query vertex buffer address when the driver supports it | ||
| 493 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 494 | glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); | ||
| 495 | |||
| 496 | glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); | ||
| 497 | glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, | ||
| 498 | &vertex_buffer_address); | ||
| 499 | } | ||
| 491 | } | 500 | } |
| 492 | 501 | ||
| 493 | void RendererOpenGL::AddTelemetryFields() { | 502 | void RendererOpenGL::AddTelemetryFields() { |
| @@ -552,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | |||
| 552 | void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | 561 | void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { |
| 553 | if (renderer_settings.set_background_color) { | 562 | if (renderer_settings.set_background_color) { |
| 554 | // Update background color before drawing | 563 | // Update background color before drawing |
| 555 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, | 564 | glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), |
| 556 | 0.0f); | 565 | Settings::values.bg_blue.GetValue(), 0.0f); |
| 557 | } | 566 | } |
| 558 | 567 | ||
| 559 | // Set projection matrix | 568 | // Set projection matrix |
| @@ -656,7 +665,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 656 | offsetof(ScreenRectVertex, tex_coord)); | 665 | offsetof(ScreenRectVertex, tex_coord)); |
| 657 | glVertexAttribBinding(PositionLocation, 0); | 666 | glVertexAttribBinding(PositionLocation, 0); |
| 658 | glVertexAttribBinding(TexCoordLocation, 0); | 667 | glVertexAttribBinding(TexCoordLocation, 0); |
| 659 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | 668 | if (device.HasVertexBufferUnifiedMemory()) { |
| 669 | glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); | ||
| 670 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, | ||
| 671 | sizeof(vertices)); | ||
| 672 | } else { | ||
| 673 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | ||
| 674 | } | ||
| 660 | 675 | ||
| 661 | glBindTextureUnit(0, screen_info.display_texture); | 676 | glBindTextureUnit(0, screen_info.display_texture); |
| 662 | glBindSampler(0, 0); | 677 | glBindSampler(0, 0); |
| @@ -751,8 +766,9 @@ void RendererOpenGL::RenderScreenshot() { | |||
| 751 | } | 766 | } |
| 752 | 767 | ||
| 753 | bool RendererOpenGL::Init() { | 768 | bool RendererOpenGL::Init() { |
| 754 | if (GLAD_GL_KHR_debug) { | 769 | if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { |
| 755 | glEnable(GL_DEBUG_OUTPUT); | 770 | glEnable(GL_DEBUG_OUTPUT); |
| 771 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); | ||
| 756 | glDebugMessageCallback(DebugHandler, nullptr); | 772 | glDebugMessageCallback(DebugHandler, nullptr); |
| 757 | } | 773 | } |
| 758 | 774 | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 61bf507f4..8b18d32e6 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -107,6 +107,9 @@ private: | |||
| 107 | OGLPipeline pipeline; | 107 | OGLPipeline pipeline; |
| 108 | OGLFramebuffer screenshot_framebuffer; | 108 | OGLFramebuffer screenshot_framebuffer; |
| 109 | 109 | ||
| 110 | // GPU address of the vertex buffer | ||
| 111 | GLuint64EXT vertex_buffer_address = 0; | ||
| 112 | |||
| 110 | /// Display information for Switch screen | 113 | /// Display information for Switch screen |
| 111 | ScreenInfo screen_info; | 114 | ScreenInfo screen_info; |
| 112 | 115 | ||
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 568744e3c..d1f0ea932 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | |||
| @@ -39,53 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { | |||
| 39 | 39 | ||
| 40 | } // Anonymous namespace | 40 | } // Anonymous namespace |
| 41 | 41 | ||
| 42 | void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { | 42 | void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) { |
| 43 | raw = 0; | ||
| 44 | front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); | ||
| 45 | front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); | ||
| 46 | front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); | ||
| 47 | front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); | ||
| 48 | if (regs.stencil_two_side_enable) { | ||
| 49 | back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); | ||
| 50 | back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); | ||
| 51 | back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); | ||
| 52 | back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); | ||
| 53 | } else { | ||
| 54 | back.action_stencil_fail.Assign(front.action_stencil_fail); | ||
| 55 | back.action_depth_fail.Assign(front.action_depth_fail); | ||
| 56 | back.action_depth_pass.Assign(front.action_depth_pass); | ||
| 57 | back.test_func.Assign(front.test_func); | ||
| 58 | } | ||
| 59 | depth_test_enable.Assign(regs.depth_test_enable); | ||
| 60 | depth_write_enable.Assign(regs.depth_write_enabled); | ||
| 61 | depth_bounds_enable.Assign(regs.depth_bounds_enable); | ||
| 62 | stencil_enable.Assign(regs.stencil_enable); | ||
| 63 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); | ||
| 64 | } | ||
| 65 | |||
| 66 | void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept { | ||
| 67 | const auto& clip = regs.view_volume_clip_control; | 43 | const auto& clip = regs.view_volume_clip_control; |
| 68 | const std::array enabled_lut = {regs.polygon_offset_point_enable, | 44 | const std::array enabled_lut = {regs.polygon_offset_point_enable, |
| 69 | regs.polygon_offset_line_enable, | 45 | regs.polygon_offset_line_enable, |
| 70 | regs.polygon_offset_fill_enable}; | 46 | regs.polygon_offset_fill_enable}; |
| 71 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); | 47 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); |
| 72 | 48 | ||
| 73 | u32 packed_front_face = PackFrontFace(regs.front_face); | ||
| 74 | if (regs.screen_y_control.triangle_rast_flip != 0 && | ||
| 75 | regs.viewport_transform[0].scale_y > 0.0f) { | ||
| 76 | // Flip front face | ||
| 77 | packed_front_face = 1 - packed_front_face; | ||
| 78 | } | ||
| 79 | |||
| 80 | raw = 0; | 49 | raw = 0; |
| 81 | topology.Assign(topology_index); | ||
| 82 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); | 50 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); |
| 83 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); | ||
| 84 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); | 51 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); |
| 85 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); | 52 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); |
| 86 | ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); | 53 | ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); |
| 87 | cull_face.Assign(PackCullFace(regs.cull_face)); | ||
| 88 | front_face.Assign(packed_front_face); | ||
| 89 | polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); | 54 | polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); |
| 90 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); | 55 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); |
| 91 | tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); | 56 | tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); |
| @@ -94,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept { | |||
| 94 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); | 59 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); |
| 95 | logic_op.Assign(PackLogicOp(regs.logic_op.operation)); | 60 | logic_op.Assign(PackLogicOp(regs.logic_op.operation)); |
| 96 | rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); | 61 | rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); |
| 62 | |||
| 97 | std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast | 63 | std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast |
| 98 | } | ||
| 99 | 64 | ||
| 100 | void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept { | 65 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 66 | binding_divisors[index] = | ||
| 67 | regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { | ||
| 71 | const auto& input = regs.vertex_attrib_format[index]; | ||
| 72 | auto& attribute = attributes[index]; | ||
| 73 | attribute.raw = 0; | ||
| 74 | attribute.enabled.Assign(input.IsConstant() ? 0 : 1); | ||
| 75 | attribute.buffer.Assign(input.buffer); | ||
| 76 | attribute.offset.Assign(input.offset); | ||
| 77 | attribute.type.Assign(static_cast<u32>(input.type.Value())); | ||
| 78 | attribute.size.Assign(static_cast<u32>(input.size.Value())); | ||
| 79 | } | ||
| 80 | |||
| 101 | for (std::size_t index = 0; index < std::size(attachments); ++index) { | 81 | for (std::size_t index = 0; index < std::size(attachments); ++index) { |
| 102 | attachments[index].Fill(regs, index); | 82 | attachments[index].Fill(regs, index); |
| 103 | } | 83 | } |
| 104 | } | ||
| 105 | 84 | ||
| 106 | void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept { | ||
| 107 | const auto& transform = regs.viewport_transform; | 85 | const auto& transform = regs.viewport_transform; |
| 108 | std::transform(transform.begin(), transform.end(), swizzles.begin(), | 86 | std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(), |
| 109 | [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); | 87 | [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); |
| 88 | |||
| 89 | if (!has_extended_dynamic_state) { | ||
| 90 | no_extended_dynamic_state.Assign(1); | ||
| 91 | dynamic_state.Fill(regs); | ||
| 92 | } | ||
| 110 | } | 93 | } |
| 111 | 94 | ||
| 112 | void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { | 95 | void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { |
| @@ -148,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size | |||
| 148 | enable.Assign(1); | 131 | enable.Assign(1); |
| 149 | } | 132 | } |
| 150 | 133 | ||
| 151 | void FixedPipelineState::Fill(const Maxwell& regs) { | 134 | void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) { |
| 152 | rasterizer.Fill(regs); | 135 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); |
| 153 | depth_stencil.Fill(regs); | 136 | u32 packed_front_face = PackFrontFace(regs.front_face); |
| 154 | color_blending.Fill(regs); | 137 | if (regs.screen_y_control.triangle_rast_flip != 0) { |
| 155 | viewport_swizzles.Fill(regs); | 138 | // Flip front face |
| 139 | packed_front_face = 1 - packed_front_face; | ||
| 140 | } | ||
| 141 | |||
| 142 | raw1 = 0; | ||
| 143 | raw2 = 0; | ||
| 144 | front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); | ||
| 145 | front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); | ||
| 146 | front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); | ||
| 147 | front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); | ||
| 148 | if (regs.stencil_two_side_enable) { | ||
| 149 | back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); | ||
| 150 | back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); | ||
| 151 | back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); | ||
| 152 | back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); | ||
| 153 | } else { | ||
| 154 | back.action_stencil_fail.Assign(front.action_stencil_fail); | ||
| 155 | back.action_depth_fail.Assign(front.action_depth_fail); | ||
| 156 | back.action_depth_pass.Assign(front.action_depth_pass); | ||
| 157 | back.test_func.Assign(front.test_func); | ||
| 158 | } | ||
| 159 | stencil_enable.Assign(regs.stencil_enable); | ||
| 160 | depth_write_enable.Assign(regs.depth_write_enabled); | ||
| 161 | depth_bounds_enable.Assign(regs.depth_bounds_enable); | ||
| 162 | depth_test_enable.Assign(regs.depth_test_enable); | ||
| 163 | front_face.Assign(packed_front_face); | ||
| 164 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); | ||
| 165 | topology.Assign(topology_index); | ||
| 166 | cull_face.Assign(PackCullFace(regs.cull_face)); | ||
| 167 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); | ||
| 168 | |||
| 169 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | ||
| 170 | const auto& input = regs.vertex_array[index]; | ||
| 171 | VertexBinding& binding = vertex_bindings[index]; | ||
| 172 | binding.raw = 0; | ||
| 173 | binding.enabled.Assign(input.IsEnabled() ? 1 : 0); | ||
| 174 | binding.stride.Assign(static_cast<u16>(input.stride.Value())); | ||
| 175 | } | ||
| 156 | } | 176 | } |
| 157 | 177 | ||
| 158 | std::size_t FixedPipelineState::Hash() const noexcept { | 178 | std::size_t FixedPipelineState::Hash() const noexcept { |
| 159 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); | 179 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size()); |
| 160 | return static_cast<std::size_t>(hash); | 180 | return static_cast<std::size_t>(hash); |
| 161 | } | 181 | } |
| 162 | 182 | ||
| 163 | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { | 183 | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { |
| 164 | return std::memcmp(this, &rhs, sizeof *this) == 0; | 184 | return std::memcmp(this, &rhs, Size()) == 0; |
| 165 | } | 185 | } |
| 166 | 186 | ||
| 167 | u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { | 187 | u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 31a6398f2..cdcbb65f5 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h | |||
| @@ -60,14 +60,6 @@ struct FixedPipelineState { | |||
| 60 | 60 | ||
| 61 | void Fill(const Maxwell& regs, std::size_t index); | 61 | void Fill(const Maxwell& regs, std::size_t index); |
| 62 | 62 | ||
| 63 | std::size_t Hash() const noexcept; | ||
| 64 | |||
| 65 | bool operator==(const BlendingAttachment& rhs) const noexcept; | ||
| 66 | |||
| 67 | bool operator!=(const BlendingAttachment& rhs) const noexcept { | ||
| 68 | return !operator==(rhs); | ||
| 69 | } | ||
| 70 | |||
| 71 | constexpr std::array<bool, 4> Mask() const noexcept { | 63 | constexpr std::array<bool, 4> Mask() const noexcept { |
| 72 | return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; | 64 | return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; |
| 73 | } | 65 | } |
| @@ -97,156 +89,116 @@ struct FixedPipelineState { | |||
| 97 | } | 89 | } |
| 98 | }; | 90 | }; |
| 99 | 91 | ||
| 100 | struct VertexInput { | 92 | union VertexAttribute { |
| 101 | union Binding { | 93 | u32 raw; |
| 102 | u16 raw; | 94 | BitField<0, 1, u32> enabled; |
| 103 | BitField<0, 1, u16> enabled; | 95 | BitField<1, 5, u32> buffer; |
| 104 | BitField<1, 12, u16> stride; | 96 | BitField<6, 14, u32> offset; |
| 105 | }; | 97 | BitField<20, 3, u32> type; |
| 98 | BitField<23, 6, u32> size; | ||
| 106 | 99 | ||
| 107 | union Attribute { | 100 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { |
| 108 | u32 raw; | 101 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); |
| 109 | BitField<0, 1, u32> enabled; | ||
| 110 | BitField<1, 5, u32> buffer; | ||
| 111 | BitField<6, 14, u32> offset; | ||
| 112 | BitField<20, 3, u32> type; | ||
| 113 | BitField<23, 6, u32> size; | ||
| 114 | |||
| 115 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { | ||
| 116 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); | ||
| 117 | } | ||
| 118 | |||
| 119 | constexpr Maxwell::VertexAttribute::Size Size() const noexcept { | ||
| 120 | return static_cast<Maxwell::VertexAttribute::Size>(size.Value()); | ||
| 121 | } | ||
| 122 | }; | ||
| 123 | |||
| 124 | std::array<Binding, Maxwell::NumVertexArrays> bindings; | ||
| 125 | std::array<u32, Maxwell::NumVertexArrays> binding_divisors; | ||
| 126 | std::array<Attribute, Maxwell::NumVertexAttributes> attributes; | ||
| 127 | |||
| 128 | void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept { | ||
| 129 | auto& binding = bindings[index]; | ||
| 130 | binding.raw = 0; | ||
| 131 | binding.enabled.Assign(enabled ? 1 : 0); | ||
| 132 | binding.stride.Assign(static_cast<u16>(stride)); | ||
| 133 | binding_divisors[index] = divisor; | ||
| 134 | } | 102 | } |
| 135 | 103 | ||
| 136 | void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset, | 104 | constexpr Maxwell::VertexAttribute::Size Size() const noexcept { |
| 137 | Maxwell::VertexAttribute::Type type, | 105 | return static_cast<Maxwell::VertexAttribute::Size>(size.Value()); |
| 138 | Maxwell::VertexAttribute::Size size) noexcept { | ||
| 139 | auto& attribute = attributes[index]; | ||
| 140 | attribute.raw = 0; | ||
| 141 | attribute.enabled.Assign(enabled ? 1 : 0); | ||
| 142 | attribute.buffer.Assign(buffer); | ||
| 143 | attribute.offset.Assign(offset); | ||
| 144 | attribute.type.Assign(static_cast<u32>(type)); | ||
| 145 | attribute.size.Assign(static_cast<u32>(size)); | ||
| 146 | } | 106 | } |
| 147 | }; | 107 | }; |
| 148 | 108 | ||
| 149 | struct Rasterizer { | 109 | template <std::size_t Position> |
| 150 | union { | 110 | union StencilFace { |
| 151 | u32 raw; | 111 | BitField<Position + 0, 3, u32> action_stencil_fail; |
| 152 | BitField<0, 4, u32> topology; | 112 | BitField<Position + 3, 3, u32> action_depth_fail; |
| 153 | BitField<4, 1, u32> primitive_restart_enable; | 113 | BitField<Position + 6, 3, u32> action_depth_pass; |
| 154 | BitField<5, 1, u32> cull_enable; | 114 | BitField<Position + 9, 3, u32> test_func; |
| 155 | BitField<6, 1, u32> depth_bias_enable; | ||
| 156 | BitField<7, 1, u32> depth_clamp_disabled; | ||
| 157 | BitField<8, 1, u32> ndc_minus_one_to_one; | ||
| 158 | BitField<9, 2, u32> cull_face; | ||
| 159 | BitField<11, 1, u32> front_face; | ||
| 160 | BitField<12, 2, u32> polygon_mode; | ||
| 161 | BitField<14, 5, u32> patch_control_points_minus_one; | ||
| 162 | BitField<19, 2, u32> tessellation_primitive; | ||
| 163 | BitField<21, 2, u32> tessellation_spacing; | ||
| 164 | BitField<23, 1, u32> tessellation_clockwise; | ||
| 165 | BitField<24, 1, u32> logic_op_enable; | ||
| 166 | BitField<25, 4, u32> logic_op; | ||
| 167 | BitField<29, 1, u32> rasterize_enable; | ||
| 168 | }; | ||
| 169 | |||
| 170 | // TODO(Rodrigo): Move this to push constants | ||
| 171 | u32 point_size; | ||
| 172 | 115 | ||
| 173 | void Fill(const Maxwell& regs) noexcept; | 116 | Maxwell::StencilOp ActionStencilFail() const noexcept { |
| 117 | return UnpackStencilOp(action_stencil_fail); | ||
| 118 | } | ||
| 174 | 119 | ||
| 175 | constexpr Maxwell::PrimitiveTopology Topology() const noexcept { | 120 | Maxwell::StencilOp ActionDepthFail() const noexcept { |
| 176 | return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); | 121 | return UnpackStencilOp(action_depth_fail); |
| 177 | } | 122 | } |
| 178 | 123 | ||
| 179 | Maxwell::CullFace CullFace() const noexcept { | 124 | Maxwell::StencilOp ActionDepthPass() const noexcept { |
| 180 | return UnpackCullFace(cull_face.Value()); | 125 | return UnpackStencilOp(action_depth_pass); |
| 181 | } | 126 | } |
| 182 | 127 | ||
| 183 | Maxwell::FrontFace FrontFace() const noexcept { | 128 | Maxwell::ComparisonOp TestFunc() const noexcept { |
| 184 | return UnpackFrontFace(front_face.Value()); | 129 | return UnpackComparisonOp(test_func); |
| 185 | } | 130 | } |
| 186 | }; | 131 | }; |
| 187 | 132 | ||
| 188 | struct DepthStencil { | 133 | union VertexBinding { |
| 189 | template <std::size_t Position> | 134 | u16 raw; |
| 190 | union StencilFace { | 135 | BitField<0, 12, u16> stride; |
| 191 | BitField<Position + 0, 3, u32> action_stencil_fail; | 136 | BitField<12, 1, u16> enabled; |
| 192 | BitField<Position + 3, 3, u32> action_depth_fail; | 137 | }; |
| 193 | BitField<Position + 6, 3, u32> action_depth_pass; | ||
| 194 | BitField<Position + 9, 3, u32> test_func; | ||
| 195 | |||
| 196 | Maxwell::StencilOp ActionStencilFail() const noexcept { | ||
| 197 | return UnpackStencilOp(action_stencil_fail); | ||
| 198 | } | ||
| 199 | |||
| 200 | Maxwell::StencilOp ActionDepthFail() const noexcept { | ||
| 201 | return UnpackStencilOp(action_depth_fail); | ||
| 202 | } | ||
| 203 | |||
| 204 | Maxwell::StencilOp ActionDepthPass() const noexcept { | ||
| 205 | return UnpackStencilOp(action_depth_pass); | ||
| 206 | } | ||
| 207 | |||
| 208 | Maxwell::ComparisonOp TestFunc() const noexcept { | ||
| 209 | return UnpackComparisonOp(test_func); | ||
| 210 | } | ||
| 211 | }; | ||
| 212 | 138 | ||
| 139 | struct DynamicState { | ||
| 213 | union { | 140 | union { |
| 214 | u32 raw; | 141 | u32 raw1; |
| 215 | StencilFace<0> front; | 142 | StencilFace<0> front; |
| 216 | StencilFace<12> back; | 143 | StencilFace<12> back; |
| 217 | BitField<24, 1, u32> depth_test_enable; | 144 | BitField<24, 1, u32> stencil_enable; |
| 218 | BitField<25, 1, u32> depth_write_enable; | 145 | BitField<25, 1, u32> depth_write_enable; |
| 219 | BitField<26, 1, u32> depth_bounds_enable; | 146 | BitField<26, 1, u32> depth_bounds_enable; |
| 220 | BitField<27, 1, u32> stencil_enable; | 147 | BitField<27, 1, u32> depth_test_enable; |
| 221 | BitField<28, 3, u32> depth_test_func; | 148 | BitField<28, 1, u32> front_face; |
| 149 | BitField<29, 3, u32> depth_test_func; | ||
| 150 | }; | ||
| 151 | union { | ||
| 152 | u32 raw2; | ||
| 153 | BitField<0, 4, u32> topology; | ||
| 154 | BitField<4, 2, u32> cull_face; | ||
| 155 | BitField<6, 1, u32> cull_enable; | ||
| 222 | }; | 156 | }; |
| 157 | std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings; | ||
| 223 | 158 | ||
| 224 | void Fill(const Maxwell& regs) noexcept; | 159 | void Fill(const Maxwell& regs); |
| 225 | 160 | ||
| 226 | Maxwell::ComparisonOp DepthTestFunc() const noexcept { | 161 | Maxwell::ComparisonOp DepthTestFunc() const noexcept { |
| 227 | return UnpackComparisonOp(depth_test_func); | 162 | return UnpackComparisonOp(depth_test_func); |
| 228 | } | 163 | } |
| 229 | }; | ||
| 230 | |||
| 231 | struct ColorBlending { | ||
| 232 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | ||
| 233 | 164 | ||
| 234 | void Fill(const Maxwell& regs) noexcept; | 165 | Maxwell::CullFace CullFace() const noexcept { |
| 235 | }; | 166 | return UnpackCullFace(cull_face.Value()); |
| 167 | } | ||
| 236 | 168 | ||
| 237 | struct ViewportSwizzles { | 169 | Maxwell::FrontFace FrontFace() const noexcept { |
| 238 | std::array<u16, Maxwell::NumViewports> swizzles; | 170 | return UnpackFrontFace(front_face.Value()); |
| 171 | } | ||
| 239 | 172 | ||
| 240 | void Fill(const Maxwell& regs) noexcept; | 173 | constexpr Maxwell::PrimitiveTopology Topology() const noexcept { |
| 174 | return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); | ||
| 175 | } | ||
| 241 | }; | 176 | }; |
| 242 | 177 | ||
| 243 | VertexInput vertex_input; | 178 | union { |
| 244 | Rasterizer rasterizer; | 179 | u32 raw; |
| 245 | DepthStencil depth_stencil; | 180 | BitField<0, 1, u32> no_extended_dynamic_state; |
| 246 | ColorBlending color_blending; | 181 | BitField<2, 1, u32> primitive_restart_enable; |
| 247 | ViewportSwizzles viewport_swizzles; | 182 | BitField<3, 1, u32> depth_bias_enable; |
| 183 | BitField<4, 1, u32> depth_clamp_disabled; | ||
| 184 | BitField<5, 1, u32> ndc_minus_one_to_one; | ||
| 185 | BitField<6, 2, u32> polygon_mode; | ||
| 186 | BitField<8, 5, u32> patch_control_points_minus_one; | ||
| 187 | BitField<13, 2, u32> tessellation_primitive; | ||
| 188 | BitField<15, 2, u32> tessellation_spacing; | ||
| 189 | BitField<17, 1, u32> tessellation_clockwise; | ||
| 190 | BitField<18, 1, u32> logic_op_enable; | ||
| 191 | BitField<19, 4, u32> logic_op; | ||
| 192 | BitField<23, 1, u32> rasterize_enable; | ||
| 193 | }; | ||
| 194 | u32 point_size; | ||
| 195 | std::array<u32, Maxwell::NumVertexArrays> binding_divisors; | ||
| 196 | std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes; | ||
| 197 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | ||
| 198 | std::array<u16, Maxwell::NumViewports> viewport_swizzles; | ||
| 199 | DynamicState dynamic_state; | ||
| 248 | 200 | ||
| 249 | void Fill(const Maxwell& regs); | 201 | void Fill(const Maxwell& regs, bool has_extended_dynamic_state); |
| 250 | 202 | ||
| 251 | std::size_t Hash() const noexcept; | 203 | std::size_t Hash() const noexcept; |
| 252 | 204 | ||
| @@ -255,6 +207,11 @@ struct FixedPipelineState { | |||
| 255 | bool operator!=(const FixedPipelineState& rhs) const noexcept { | 207 | bool operator!=(const FixedPipelineState& rhs) const noexcept { |
| 256 | return !operator==(rhs); | 208 | return !operator==(rhs); |
| 257 | } | 209 | } |
| 210 | |||
| 211 | std::size_t Size() const noexcept { | ||
| 212 | const std::size_t total_size = sizeof *this; | ||
| 213 | return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState)); | ||
| 214 | } | ||
| 258 | }; | 215 | }; |
| 259 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); | 216 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); |
| 260 | static_assert(std::is_trivially_copyable_v<FixedPipelineState>); | 217 | static_assert(std::is_trivially_copyable_v<FixedPipelineState>); |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 2871035f5..d7f1ae89f 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -21,29 +21,29 @@ namespace Sampler { | |||
| 21 | 21 | ||
| 22 | VkFilter Filter(Tegra::Texture::TextureFilter filter) { | 22 | VkFilter Filter(Tegra::Texture::TextureFilter filter) { |
| 23 | switch (filter) { | 23 | switch (filter) { |
| 24 | case Tegra::Texture::TextureFilter::Linear: | ||
| 25 | return VK_FILTER_LINEAR; | ||
| 26 | case Tegra::Texture::TextureFilter::Nearest: | 24 | case Tegra::Texture::TextureFilter::Nearest: |
| 27 | return VK_FILTER_NEAREST; | 25 | return VK_FILTER_NEAREST; |
| 26 | case Tegra::Texture::TextureFilter::Linear: | ||
| 27 | return VK_FILTER_LINEAR; | ||
| 28 | } | 28 | } |
| 29 | UNIMPLEMENTED_MSG("Unimplemented sampler filter={}", static_cast<u32>(filter)); | 29 | UNREACHABLE_MSG("Invalid sampler filter={}", static_cast<u32>(filter)); |
| 30 | return {}; | 30 | return {}; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter) { | 33 | VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter) { |
| 34 | switch (mipmap_filter) { | 34 | switch (mipmap_filter) { |
| 35 | case Tegra::Texture::TextureMipmapFilter::None: | 35 | case Tegra::Texture::TextureMipmapFilter::None: |
| 36 | // TODO(Rodrigo): None seems to be mapped to OpenGL's mag and min filters without mipmapping | 36 | // There are no Vulkan filter modes that directly correspond to OpenGL minification filters |
| 37 | // (e.g. GL_NEAREST and GL_LINEAR). Vulkan doesn't have such a thing, find out if we have to | 37 | // of GL_LINEAR or GL_NEAREST, but they can be emulated using |
| 38 | // use an image view with a single mipmap level to emulate this. | 38 | // VK_SAMPLER_MIPMAP_MODE_NEAREST, minLod = 0, and maxLod = 0.25, and using minFilter = |
| 39 | return VK_SAMPLER_MIPMAP_MODE_LINEAR; | 39 | // VK_FILTER_LINEAR or minFilter = VK_FILTER_NEAREST, respectively. |
| 40 | ; | 40 | return VK_SAMPLER_MIPMAP_MODE_NEAREST; |
| 41 | case Tegra::Texture::TextureMipmapFilter::Linear: | ||
| 42 | return VK_SAMPLER_MIPMAP_MODE_LINEAR; | ||
| 43 | case Tegra::Texture::TextureMipmapFilter::Nearest: | 41 | case Tegra::Texture::TextureMipmapFilter::Nearest: |
| 44 | return VK_SAMPLER_MIPMAP_MODE_NEAREST; | 42 | return VK_SAMPLER_MIPMAP_MODE_NEAREST; |
| 43 | case Tegra::Texture::TextureMipmapFilter::Linear: | ||
| 44 | return VK_SAMPLER_MIPMAP_MODE_LINEAR; | ||
| 45 | } | 45 | } |
| 46 | UNIMPLEMENTED_MSG("Unimplemented sampler mipmap mode={}", static_cast<u32>(mipmap_filter)); | 46 | UNREACHABLE_MSG("Invalid sampler mipmap mode={}", static_cast<u32>(mipmap_filter)); |
| 47 | return {}; | 47 | return {}; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| @@ -78,10 +78,9 @@ VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode w | |||
| 78 | case Tegra::Texture::WrapMode::MirrorOnceBorder: | 78 | case Tegra::Texture::WrapMode::MirrorOnceBorder: |
| 79 | UNIMPLEMENTED(); | 79 | UNIMPLEMENTED(); |
| 80 | return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; | 80 | return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; |
| 81 | default: | ||
| 82 | UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 83 | return {}; | ||
| 84 | } | 81 | } |
| 82 | UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 83 | return {}; | ||
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { | 86 | VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { |
| @@ -149,7 +148,7 @@ struct FormatTuple { | |||
| 149 | {VK_FORMAT_R16_SFLOAT, Attachable | Storage}, // R16F | 148 | {VK_FORMAT_R16_SFLOAT, Attachable | Storage}, // R16F |
| 150 | {VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16U | 149 | {VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16U |
| 151 | {VK_FORMAT_UNDEFINED}, // R16S | 150 | {VK_FORMAT_UNDEFINED}, // R16S |
| 152 | {VK_FORMAT_UNDEFINED}, // R16UI | 151 | {VK_FORMAT_R16_UINT, Attachable | Storage}, // R16UI |
| 153 | {VK_FORMAT_UNDEFINED}, // R16I | 152 | {VK_FORMAT_UNDEFINED}, // R16I |
| 154 | {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // RG16 | 153 | {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // RG16 |
| 155 | {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // RG16F | 154 | {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // RG16F |
| @@ -288,14 +287,35 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device, | |||
| 288 | return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | 287 | return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
| 289 | case Maxwell::PrimitiveTopology::Patches: | 288 | case Maxwell::PrimitiveTopology::Patches: |
| 290 | return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; | 289 | return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; |
| 291 | default: | ||
| 292 | UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 293 | return {}; | ||
| 294 | } | 290 | } |
| 291 | UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 292 | return {}; | ||
| 295 | } | 293 | } |
| 296 | 294 | ||
| 297 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { | 295 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { |
| 298 | switch (type) { | 296 | switch (type) { |
| 297 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | ||
| 298 | switch (size) { | ||
| 299 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 300 | return VK_FORMAT_R8_UNORM; | ||
| 301 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 302 | return VK_FORMAT_R8G8_UNORM; | ||
| 303 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 304 | return VK_FORMAT_R8G8B8_UNORM; | ||
| 305 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 306 | return VK_FORMAT_R8G8B8A8_UNORM; | ||
| 307 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 308 | return VK_FORMAT_R16_UNORM; | ||
| 309 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 310 | return VK_FORMAT_R16G16_UNORM; | ||
| 311 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 312 | return VK_FORMAT_R16G16B16_UNORM; | ||
| 313 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 314 | return VK_FORMAT_R16G16B16A16_UNORM; | ||
| 315 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | ||
| 316 | return VK_FORMAT_A2B10G10R10_UNORM_PACK32; | ||
| 317 | } | ||
| 318 | break; | ||
| 299 | case Maxwell::VertexAttribute::Type::SignedNorm: | 319 | case Maxwell::VertexAttribute::Type::SignedNorm: |
| 300 | switch (size) { | 320 | switch (size) { |
| 301 | case Maxwell::VertexAttribute::Size::Size_8: | 321 | case Maxwell::VertexAttribute::Size::Size_8: |
| @@ -316,62 +336,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 316 | return VK_FORMAT_R16G16B16A16_SNORM; | 336 | return VK_FORMAT_R16G16B16A16_SNORM; |
| 317 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 337 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 318 | return VK_FORMAT_A2B10G10R10_SNORM_PACK32; | 338 | return VK_FORMAT_A2B10G10R10_SNORM_PACK32; |
| 319 | default: | ||
| 320 | break; | ||
| 321 | } | 339 | } |
| 322 | break; | 340 | break; |
| 323 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | 341 | case Maxwell::VertexAttribute::Type::UnsignedScaled: |
| 324 | switch (size) { | 342 | switch (size) { |
| 325 | case Maxwell::VertexAttribute::Size::Size_8: | 343 | case Maxwell::VertexAttribute::Size::Size_8: |
| 326 | return VK_FORMAT_R8_UNORM; | 344 | return VK_FORMAT_R8_USCALED; |
| 327 | case Maxwell::VertexAttribute::Size::Size_8_8: | 345 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 328 | return VK_FORMAT_R8G8_UNORM; | 346 | return VK_FORMAT_R8G8_USCALED; |
| 329 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 347 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 330 | return VK_FORMAT_R8G8B8_UNORM; | 348 | return VK_FORMAT_R8G8B8_USCALED; |
| 331 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 349 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 332 | return VK_FORMAT_R8G8B8A8_UNORM; | 350 | return VK_FORMAT_R8G8B8A8_USCALED; |
| 333 | case Maxwell::VertexAttribute::Size::Size_16: | 351 | case Maxwell::VertexAttribute::Size::Size_16: |
| 334 | return VK_FORMAT_R16_UNORM; | 352 | return VK_FORMAT_R16_USCALED; |
| 335 | case Maxwell::VertexAttribute::Size::Size_16_16: | 353 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 336 | return VK_FORMAT_R16G16_UNORM; | 354 | return VK_FORMAT_R16G16_USCALED; |
| 337 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 355 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 338 | return VK_FORMAT_R16G16B16_UNORM; | 356 | return VK_FORMAT_R16G16B16_USCALED; |
| 339 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 357 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 340 | return VK_FORMAT_R16G16B16A16_UNORM; | 358 | return VK_FORMAT_R16G16B16A16_USCALED; |
| 341 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 359 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 342 | return VK_FORMAT_A2B10G10R10_UNORM_PACK32; | 360 | return VK_FORMAT_A2B10G10R10_USCALED_PACK32; |
| 343 | default: | ||
| 344 | break; | ||
| 345 | } | 361 | } |
| 346 | break; | 362 | break; |
| 347 | case Maxwell::VertexAttribute::Type::SignedInt: | 363 | case Maxwell::VertexAttribute::Type::SignedScaled: |
| 348 | switch (size) { | 364 | switch (size) { |
| 349 | case Maxwell::VertexAttribute::Size::Size_8: | 365 | case Maxwell::VertexAttribute::Size::Size_8: |
| 350 | return VK_FORMAT_R8_SINT; | 366 | return VK_FORMAT_R8_SSCALED; |
| 351 | case Maxwell::VertexAttribute::Size::Size_8_8: | 367 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 352 | return VK_FORMAT_R8G8_SINT; | 368 | return VK_FORMAT_R8G8_SSCALED; |
| 353 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 369 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 354 | return VK_FORMAT_R8G8B8_SINT; | 370 | return VK_FORMAT_R8G8B8_SSCALED; |
| 355 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 371 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 356 | return VK_FORMAT_R8G8B8A8_SINT; | 372 | return VK_FORMAT_R8G8B8A8_SSCALED; |
| 357 | case Maxwell::VertexAttribute::Size::Size_16: | 373 | case Maxwell::VertexAttribute::Size::Size_16: |
| 358 | return VK_FORMAT_R16_SINT; | 374 | return VK_FORMAT_R16_SSCALED; |
| 359 | case Maxwell::VertexAttribute::Size::Size_16_16: | 375 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 360 | return VK_FORMAT_R16G16_SINT; | 376 | return VK_FORMAT_R16G16_SSCALED; |
| 361 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 377 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 362 | return VK_FORMAT_R16G16B16_SINT; | 378 | return VK_FORMAT_R16G16B16_SSCALED; |
| 363 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 379 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 364 | return VK_FORMAT_R16G16B16A16_SINT; | 380 | return VK_FORMAT_R16G16B16A16_SSCALED; |
| 365 | case Maxwell::VertexAttribute::Size::Size_32: | 381 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 366 | return VK_FORMAT_R32_SINT; | 382 | return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; |
| 367 | case Maxwell::VertexAttribute::Size::Size_32_32: | ||
| 368 | return VK_FORMAT_R32G32_SINT; | ||
| 369 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||
| 370 | return VK_FORMAT_R32G32B32_SINT; | ||
| 371 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 372 | return VK_FORMAT_R32G32B32A32_SINT; | ||
| 373 | default: | ||
| 374 | break; | ||
| 375 | } | 383 | } |
| 376 | break; | 384 | break; |
| 377 | case Maxwell::VertexAttribute::Type::UnsignedInt: | 385 | case Maxwell::VertexAttribute::Type::UnsignedInt: |
| @@ -400,56 +408,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 400 | return VK_FORMAT_R32G32B32_UINT; | 408 | return VK_FORMAT_R32G32B32_UINT; |
| 401 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 409 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 402 | return VK_FORMAT_R32G32B32A32_UINT; | 410 | return VK_FORMAT_R32G32B32A32_UINT; |
| 403 | default: | 411 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 404 | break; | 412 | return VK_FORMAT_A2B10G10R10_UINT_PACK32; |
| 405 | } | 413 | } |
| 406 | break; | 414 | break; |
| 407 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | 415 | case Maxwell::VertexAttribute::Type::SignedInt: |
| 408 | switch (size) { | 416 | switch (size) { |
| 409 | case Maxwell::VertexAttribute::Size::Size_8: | 417 | case Maxwell::VertexAttribute::Size::Size_8: |
| 410 | return VK_FORMAT_R8_USCALED; | 418 | return VK_FORMAT_R8_SINT; |
| 411 | case Maxwell::VertexAttribute::Size::Size_8_8: | 419 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 412 | return VK_FORMAT_R8G8_USCALED; | 420 | return VK_FORMAT_R8G8_SINT; |
| 413 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 421 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 414 | return VK_FORMAT_R8G8B8_USCALED; | 422 | return VK_FORMAT_R8G8B8_SINT; |
| 415 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 423 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 416 | return VK_FORMAT_R8G8B8A8_USCALED; | 424 | return VK_FORMAT_R8G8B8A8_SINT; |
| 417 | case Maxwell::VertexAttribute::Size::Size_16: | 425 | case Maxwell::VertexAttribute::Size::Size_16: |
| 418 | return VK_FORMAT_R16_USCALED; | 426 | return VK_FORMAT_R16_SINT; |
| 419 | case Maxwell::VertexAttribute::Size::Size_16_16: | 427 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 420 | return VK_FORMAT_R16G16_USCALED; | 428 | return VK_FORMAT_R16G16_SINT; |
| 421 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 429 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 422 | return VK_FORMAT_R16G16B16_USCALED; | 430 | return VK_FORMAT_R16G16B16_SINT; |
| 423 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 431 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 424 | return VK_FORMAT_R16G16B16A16_USCALED; | 432 | return VK_FORMAT_R16G16B16A16_SINT; |
| 425 | default: | 433 | case Maxwell::VertexAttribute::Size::Size_32: |
| 426 | break; | 434 | return VK_FORMAT_R32_SINT; |
| 435 | case Maxwell::VertexAttribute::Size::Size_32_32: | ||
| 436 | return VK_FORMAT_R32G32_SINT; | ||
| 437 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||
| 438 | return VK_FORMAT_R32G32B32_SINT; | ||
| 439 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 440 | return VK_FORMAT_R32G32B32A32_SINT; | ||
| 441 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | ||
| 442 | return VK_FORMAT_A2B10G10R10_SINT_PACK32; | ||
| 427 | } | 443 | } |
| 428 | break; | 444 | break; |
| 429 | case Maxwell::VertexAttribute::Type::SignedScaled: | 445 | case Maxwell::VertexAttribute::Type::Float: |
| 430 | switch (size) { | 446 | switch (size) { |
| 431 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 432 | return VK_FORMAT_R8_SSCALED; | ||
| 433 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 434 | return VK_FORMAT_R8G8_SSCALED; | ||
| 435 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 436 | return VK_FORMAT_R8G8B8_SSCALED; | ||
| 437 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 438 | return VK_FORMAT_R8G8B8A8_SSCALED; | ||
| 439 | case Maxwell::VertexAttribute::Size::Size_16: | 447 | case Maxwell::VertexAttribute::Size::Size_16: |
| 440 | return VK_FORMAT_R16_SSCALED; | 448 | return VK_FORMAT_R16_SFLOAT; |
| 441 | case Maxwell::VertexAttribute::Size::Size_16_16: | 449 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 442 | return VK_FORMAT_R16G16_SSCALED; | 450 | return VK_FORMAT_R16G16_SFLOAT; |
| 443 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 451 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 444 | return VK_FORMAT_R16G16B16_SSCALED; | 452 | return VK_FORMAT_R16G16B16_SFLOAT; |
| 445 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 453 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 446 | return VK_FORMAT_R16G16B16A16_SSCALED; | 454 | return VK_FORMAT_R16G16B16A16_SFLOAT; |
| 447 | default: | ||
| 448 | break; | ||
| 449 | } | ||
| 450 | break; | ||
| 451 | case Maxwell::VertexAttribute::Type::Float: | ||
| 452 | switch (size) { | ||
| 453 | case Maxwell::VertexAttribute::Size::Size_32: | 455 | case Maxwell::VertexAttribute::Size::Size_32: |
| 454 | return VK_FORMAT_R32_SFLOAT; | 456 | return VK_FORMAT_R32_SFLOAT; |
| 455 | case Maxwell::VertexAttribute::Size::Size_32_32: | 457 | case Maxwell::VertexAttribute::Size::Size_32_32: |
| @@ -458,16 +460,6 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 458 | return VK_FORMAT_R32G32B32_SFLOAT; | 460 | return VK_FORMAT_R32G32B32_SFLOAT; |
| 459 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 461 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 460 | return VK_FORMAT_R32G32B32A32_SFLOAT; | 462 | return VK_FORMAT_R32G32B32A32_SFLOAT; |
| 461 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 462 | return VK_FORMAT_R16_SFLOAT; | ||
| 463 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 464 | return VK_FORMAT_R16G16_SFLOAT; | ||
| 465 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 466 | return VK_FORMAT_R16G16B16_SFLOAT; | ||
| 467 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 468 | return VK_FORMAT_R16G16B16A16_SFLOAT; | ||
| 469 | default: | ||
| 470 | break; | ||
| 471 | } | 463 | } |
| 472 | break; | 464 | break; |
| 473 | } | 465 | } |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 59b441943..2258479f5 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| 14 | 14 | ||
| 15 | #include "common/dynamic_library.h" | 15 | #include "common/dynamic_library.h" |
| 16 | #include "common/file_util.h" | ||
| 16 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| 17 | #include "common/telemetry.h" | 18 | #include "common/telemetry.h" |
| 18 | #include "core/core.h" | 19 | #include "core/core.h" |
| @@ -76,7 +77,8 @@ Common::DynamicLibrary OpenVulkanLibrary() { | |||
| 76 | char* libvulkan_env = getenv("LIBVULKAN_PATH"); | 77 | char* libvulkan_env = getenv("LIBVULKAN_PATH"); |
| 77 | if (!libvulkan_env || !library.Open(libvulkan_env)) { | 78 | if (!libvulkan_env || !library.Open(libvulkan_env)) { |
| 78 | // Use the libvulkan.dylib from the application bundle. | 79 | // Use the libvulkan.dylib from the application bundle. |
| 79 | std::string filename = File::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; | 80 | const std::string filename = |
| 81 | FileUtil::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; | ||
| 80 | library.Open(filename.c_str()); | 82 | library.Open(filename.c_str()); |
| 81 | } | 83 | } |
| 82 | #else | 84 | #else |
| @@ -153,11 +155,31 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc | |||
| 153 | } | 155 | } |
| 154 | } | 156 | } |
| 155 | 157 | ||
| 156 | static constexpr std::array layers_data{"VK_LAYER_LUNARG_standard_validation"}; | 158 | std::vector<const char*> layers; |
| 157 | vk::Span<const char*> layers = layers_data; | 159 | layers.reserve(1); |
| 158 | if (!enable_layers) { | 160 | if (enable_layers) { |
| 159 | layers = {}; | 161 | layers.push_back("VK_LAYER_KHRONOS_validation"); |
| 162 | } | ||
| 163 | |||
| 164 | const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld); | ||
| 165 | if (!layer_properties) { | ||
| 166 | LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers"); | ||
| 167 | layers.clear(); | ||
| 168 | } | ||
| 169 | |||
| 170 | for (auto layer_it = layers.begin(); layer_it != layers.end();) { | ||
| 171 | const char* const layer = *layer_it; | ||
| 172 | const auto it = std::find_if( | ||
| 173 | layer_properties->begin(), layer_properties->end(), | ||
| 174 | [layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); }); | ||
| 175 | if (it == layer_properties->end()) { | ||
| 176 | LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer); | ||
| 177 | layer_it = layers.erase(layer_it); | ||
| 178 | } else { | ||
| 179 | ++layer_it; | ||
| 180 | } | ||
| 160 | } | 181 | } |
| 182 | |||
| 161 | vk::Instance instance = vk::Instance::Create(layers, extensions, dld); | 183 | vk::Instance instance = vk::Instance::Create(layers, extensions, dld); |
| 162 | if (!instance) { | 184 | if (!instance) { |
| 163 | LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); | 185 | LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); |
| @@ -387,7 +409,7 @@ bool RendererVulkan::PickDevices() { | |||
| 387 | return false; | 409 | return false; |
| 388 | } | 410 | } |
| 389 | 411 | ||
| 390 | const s32 device_index = Settings::values.vulkan_device; | 412 | const s32 device_index = Settings::values.vulkan_device.GetValue(); |
| 391 | if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) { | 413 | if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) { |
| 392 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); | 414 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); |
| 393 | return false; | 415 | return false; |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 5f33d9e40..2be38d419 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -37,9 +37,9 @@ std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const VKDevice& device, VKSch | |||
| 37 | 37 | ||
| 38 | } // Anonymous namespace | 38 | } // Anonymous namespace |
| 39 | 39 | ||
| 40 | CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, | 40 | Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler_, |
| 41 | VAddr cpu_addr, std::size_t size) | 41 | VKStagingBufferPool& staging_pool_, VAddr cpu_addr, std::size_t size) |
| 42 | : VideoCommon::BufferBlock{cpu_addr, size} { | 42 | : VideoCommon::BufferBlock{cpu_addr, size}, scheduler{scheduler_}, staging_pool{staging_pool_} { |
| 43 | VkBufferCreateInfo ci; | 43 | VkBufferCreateInfo ci; |
| 44 | ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | 44 | ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| 45 | ci.pNext = nullptr; | 45 | ci.pNext = nullptr; |
| @@ -54,46 +54,17 @@ CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& me | |||
| 54 | buffer.commit = memory_manager.Commit(buffer.handle, false); | 54 | buffer.commit = memory_manager.Commit(buffer.handle, false); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | CachedBufferBlock::~CachedBufferBlock() = default; | 57 | Buffer::~Buffer() = default; |
| 58 | 58 | ||
| 59 | VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | 59 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) { |
| 60 | const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 61 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool) | ||
| 62 | : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system, | ||
| 63 | CreateStreamBuffer(device, | ||
| 64 | scheduler)}, | ||
| 65 | device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ | ||
| 66 | staging_pool} {} | ||
| 67 | |||
| 68 | VKBufferCache::~VKBufferCache() = default; | ||
| 69 | |||
| 70 | Buffer VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | ||
| 71 | return std::make_shared<CachedBufferBlock>(device, memory_manager, cpu_addr, size); | ||
| 72 | } | ||
| 73 | |||
| 74 | VkBuffer VKBufferCache::ToHandle(const Buffer& buffer) { | ||
| 75 | return buffer->GetHandle(); | ||
| 76 | } | ||
| 77 | |||
| 78 | VkBuffer VKBufferCache::GetEmptyBuffer(std::size_t size) { | ||
| 79 | size = std::max(size, std::size_t(4)); | ||
| 80 | const auto& empty = staging_pool.GetUnusedBuffer(size, false); | ||
| 81 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 82 | scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { | ||
| 83 | cmdbuf.FillBuffer(buffer, 0, size, 0); | ||
| 84 | }); | ||
| 85 | return *empty.handle; | ||
| 86 | } | ||
| 87 | |||
| 88 | void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 89 | const u8* data) { | ||
| 90 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | 60 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); |
| 91 | std::memcpy(staging.commit->Map(size), data, size); | 61 | std::memcpy(staging.commit->Map(size), data, size); |
| 92 | 62 | ||
| 93 | scheduler.RequestOutsideRenderPassOperationContext(); | 63 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 94 | scheduler.Record([staging = *staging.handle, buffer = buffer->GetHandle(), offset, | 64 | |
| 95 | size](vk::CommandBuffer cmdbuf) { | 65 | const VkBuffer handle = Handle(); |
| 96 | cmdbuf.CopyBuffer(staging, buffer, VkBufferCopy{0, offset, size}); | 66 | scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) { |
| 67 | cmdbuf.CopyBuffer(staging, handle, VkBufferCopy{0, offset, size}); | ||
| 97 | 68 | ||
| 98 | VkBufferMemoryBarrier barrier; | 69 | VkBufferMemoryBarrier barrier; |
| 99 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; | 70 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; |
| @@ -102,7 +73,7 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st | |||
| 102 | barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS; | 73 | barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS; |
| 103 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 74 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 104 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 75 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 105 | barrier.buffer = buffer; | 76 | barrier.buffer = handle; |
| 106 | barrier.offset = offset; | 77 | barrier.offset = offset; |
| 107 | barrier.size = size; | 78 | barrier.size = size; |
| 108 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {}, | 79 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {}, |
| @@ -110,12 +81,12 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st | |||
| 110 | }); | 81 | }); |
| 111 | } | 82 | } |
| 112 | 83 | ||
| 113 | void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | 84 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) { |
| 114 | u8* data) { | ||
| 115 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | 85 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); |
| 116 | scheduler.RequestOutsideRenderPassOperationContext(); | 86 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 117 | scheduler.Record([staging = *staging.handle, buffer = buffer->GetHandle(), offset, | 87 | |
| 118 | size](vk::CommandBuffer cmdbuf) { | 88 | const VkBuffer handle = Handle(); |
| 89 | scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) { | ||
| 119 | VkBufferMemoryBarrier barrier; | 90 | VkBufferMemoryBarrier barrier; |
| 120 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; | 91 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; |
| 121 | barrier.pNext = nullptr; | 92 | barrier.pNext = nullptr; |
| @@ -123,7 +94,7 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, | |||
| 123 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | 94 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; |
| 124 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 95 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 125 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 96 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 126 | barrier.buffer = buffer; | 97 | barrier.buffer = handle; |
| 127 | barrier.offset = offset; | 98 | barrier.offset = offset; |
| 128 | barrier.size = size; | 99 | barrier.size = size; |
| 129 | 100 | ||
| @@ -131,18 +102,20 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, | |||
| 131 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | | 102 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | |
| 132 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | 103 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| 133 | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {}); | 104 | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {}); |
| 134 | cmdbuf.CopyBuffer(buffer, staging, VkBufferCopy{offset, 0, size}); | 105 | cmdbuf.CopyBuffer(handle, staging, VkBufferCopy{offset, 0, size}); |
| 135 | }); | 106 | }); |
| 136 | scheduler.Finish(); | 107 | scheduler.Finish(); |
| 137 | 108 | ||
| 138 | std::memcpy(data, staging.commit->Map(size), size); | 109 | std::memcpy(data, staging.commit->Map(size), size); |
| 139 | } | 110 | } |
| 140 | 111 | ||
| 141 | void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | 112 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 142 | std::size_t dst_offset, std::size_t size) { | 113 | std::size_t size) { |
| 143 | scheduler.RequestOutsideRenderPassOperationContext(); | 114 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 144 | scheduler.Record([src_buffer = src->GetHandle(), dst_buffer = dst->GetHandle(), src_offset, | 115 | |
| 145 | dst_offset, size](vk::CommandBuffer cmdbuf) { | 116 | const VkBuffer dst_buffer = Handle(); |
| 117 | scheduler.Record([src_buffer = src.Handle(), dst_buffer, src_offset, dst_offset, | ||
| 118 | size](vk::CommandBuffer cmdbuf) { | ||
| 146 | cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, size}); | 119 | cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, size}); |
| 147 | 120 | ||
| 148 | std::array<VkBufferMemoryBarrier, 2> barriers; | 121 | std::array<VkBufferMemoryBarrier, 2> barriers; |
| @@ -169,4 +142,30 @@ void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t | |||
| 169 | }); | 142 | }); |
| 170 | } | 143 | } |
| 171 | 144 | ||
| 145 | VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | ||
| 146 | const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 147 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool) | ||
| 148 | : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system, | ||
| 149 | CreateStreamBuffer(device, | ||
| 150 | scheduler)}, | ||
| 151 | device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ | ||
| 152 | staging_pool} {} | ||
| 153 | |||
| 154 | VKBufferCache::~VKBufferCache() = default; | ||
| 155 | |||
| 156 | std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | ||
| 157 | return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr, | ||
| 158 | size); | ||
| 159 | } | ||
| 160 | |||
| 161 | VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { | ||
| 162 | size = std::max(size, std::size_t(4)); | ||
| 163 | const auto& empty = staging_pool.GetUnusedBuffer(size, false); | ||
| 164 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 165 | scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { | ||
| 166 | cmdbuf.FillBuffer(buffer, 0, size, 0); | ||
| 167 | }); | ||
| 168 | return {*empty.handle, 0, 0}; | ||
| 169 | } | ||
| 170 | |||
| 172 | } // namespace Vulkan | 171 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index a54583e7d..991ee451c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/buffer_cache/buffer_cache.h" | 10 | #include "video_core/buffer_cache/buffer_cache.h" |
| 11 | #include "video_core/rasterizer_cache.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | 11 | #include "video_core/renderer_vulkan/vk_memory_manager.h" |
| 13 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | 12 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" |
| 14 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | 13 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" |
| @@ -24,22 +23,34 @@ class VKDevice; | |||
| 24 | class VKMemoryManager; | 23 | class VKMemoryManager; |
| 25 | class VKScheduler; | 24 | class VKScheduler; |
| 26 | 25 | ||
| 27 | class CachedBufferBlock final : public VideoCommon::BufferBlock { | 26 | class Buffer final : public VideoCommon::BufferBlock { |
| 28 | public: | 27 | public: |
| 29 | explicit CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, | 28 | explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, |
| 30 | VAddr cpu_addr, std::size_t size); | 29 | VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size); |
| 31 | ~CachedBufferBlock(); | 30 | ~Buffer(); |
| 32 | 31 | ||
| 33 | VkBuffer GetHandle() const { | 32 | void Upload(std::size_t offset, std::size_t size, const u8* data); |
| 33 | |||
| 34 | void Download(std::size_t offset, std::size_t size, u8* data); | ||
| 35 | |||
| 36 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | ||
| 37 | std::size_t size); | ||
| 38 | |||
| 39 | VkBuffer Handle() const { | ||
| 34 | return *buffer.handle; | 40 | return *buffer.handle; |
| 35 | } | 41 | } |
| 36 | 42 | ||
| 43 | u64 Address() const { | ||
| 44 | return 0; | ||
| 45 | } | ||
| 46 | |||
| 37 | private: | 47 | private: |
| 48 | VKScheduler& scheduler; | ||
| 49 | VKStagingBufferPool& staging_pool; | ||
| 50 | |||
| 38 | VKBuffer buffer; | 51 | VKBuffer buffer; |
| 39 | }; | 52 | }; |
| 40 | 53 | ||
| 41 | using Buffer = std::shared_ptr<CachedBufferBlock>; | ||
| 42 | |||
| 43 | class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { | 54 | class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { |
| 44 | public: | 55 | public: |
| 45 | explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | 56 | explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, |
| @@ -47,21 +58,10 @@ public: | |||
| 47 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool); | 58 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool); |
| 48 | ~VKBufferCache(); | 59 | ~VKBufferCache(); |
| 49 | 60 | ||
| 50 | VkBuffer GetEmptyBuffer(std::size_t size) override; | 61 | BufferInfo GetEmptyBuffer(std::size_t size) override; |
| 51 | 62 | ||
| 52 | protected: | 63 | protected: |
| 53 | VkBuffer ToHandle(const Buffer& buffer) override; | 64 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; |
| 54 | |||
| 55 | Buffer CreateBlock(VAddr cpu_addr, std::size_t size) override; | ||
| 56 | |||
| 57 | void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 58 | const u8* data) override; | ||
| 59 | |||
| 60 | void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 61 | u8* data) override; | ||
| 62 | |||
| 63 | void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 64 | std::size_t dst_offset, std::size_t size) override; | ||
| 65 | 65 | ||
| 66 | private: | 66 | private: |
| 67 | const VKDevice& device; | 67 | const VKDevice& device; |
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 8e1b46277..281bf9ac3 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp | |||
| @@ -53,8 +53,9 @@ vk::DescriptorSetLayout VKComputePipeline::CreateDescriptorSetLayout() const { | |||
| 53 | }; | 53 | }; |
| 54 | add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, entries.const_buffers.size()); | 54 | add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, entries.const_buffers.size()); |
| 55 | add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, entries.global_buffers.size()); | 55 | add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, entries.global_buffers.size()); |
| 56 | add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, entries.texel_buffers.size()); | 56 | add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, entries.uniform_texels.size()); |
| 57 | add_bindings(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, entries.samplers.size()); | 57 | add_bindings(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, entries.samplers.size()); |
| 58 | add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, entries.storage_texels.size()); | ||
| 58 | add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, entries.images.size()); | 59 | add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, entries.images.size()); |
| 59 | 60 | ||
| 60 | VkDescriptorSetLayoutCreateInfo ci; | 61 | VkDescriptorSetLayoutCreateInfo ci; |
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index 890fd52cf..9259b618d 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp | |||
| @@ -42,6 +42,7 @@ vk::DescriptorPool* VKDescriptorPool::AllocateNewPool() { | |||
| 42 | {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, num_sets * 60}, | 42 | {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, num_sets * 60}, |
| 43 | {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, num_sets * 64}, | 43 | {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, num_sets * 64}, |
| 44 | {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sets * 64}, | 44 | {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sets * 64}, |
| 45 | {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, num_sets * 64}, | ||
| 45 | {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, num_sets * 40}}; | 46 | {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, num_sets * 40}}; |
| 46 | 47 | ||
| 47 | VkDescriptorPoolCreateInfo ci; | 48 | VkDescriptorPoolCreateInfo ci; |
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 750e5a0ca..fdaea4210 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -73,76 +73,79 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType | |||
| 73 | 73 | ||
| 74 | std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties( | 74 | std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties( |
| 75 | vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) { | 75 | vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) { |
| 76 | static constexpr std::array formats{VK_FORMAT_A8B8G8R8_UNORM_PACK32, | 76 | static constexpr std::array formats{ |
| 77 | VK_FORMAT_A8B8G8R8_UINT_PACK32, | 77 | VK_FORMAT_A8B8G8R8_UNORM_PACK32, |
| 78 | VK_FORMAT_A8B8G8R8_SNORM_PACK32, | 78 | VK_FORMAT_A8B8G8R8_UINT_PACK32, |
| 79 | VK_FORMAT_A8B8G8R8_SRGB_PACK32, | 79 | VK_FORMAT_A8B8G8R8_SNORM_PACK32, |
| 80 | VK_FORMAT_B5G6R5_UNORM_PACK16, | 80 | VK_FORMAT_A8B8G8R8_SRGB_PACK32, |
| 81 | VK_FORMAT_A2B10G10R10_UNORM_PACK32, | 81 | VK_FORMAT_B5G6R5_UNORM_PACK16, |
| 82 | VK_FORMAT_A1R5G5B5_UNORM_PACK16, | 82 | VK_FORMAT_A2B10G10R10_UNORM_PACK32, |
| 83 | VK_FORMAT_R32G32B32A32_SFLOAT, | 83 | VK_FORMAT_A1R5G5B5_UNORM_PACK16, |
| 84 | VK_FORMAT_R32G32B32A32_UINT, | 84 | VK_FORMAT_R32G32B32A32_SFLOAT, |
| 85 | VK_FORMAT_R32G32_SFLOAT, | 85 | VK_FORMAT_R32G32B32A32_UINT, |
| 86 | VK_FORMAT_R32G32_UINT, | 86 | VK_FORMAT_R32G32_SFLOAT, |
| 87 | VK_FORMAT_R16G16B16A16_UINT, | 87 | VK_FORMAT_R32G32_UINT, |
| 88 | VK_FORMAT_R16G16B16A16_SNORM, | 88 | VK_FORMAT_R16G16B16A16_UINT, |
| 89 | VK_FORMAT_R16G16B16A16_UNORM, | 89 | VK_FORMAT_R16G16B16A16_SNORM, |
| 90 | VK_FORMAT_R16G16_UNORM, | 90 | VK_FORMAT_R16G16B16A16_UNORM, |
| 91 | VK_FORMAT_R16G16_SNORM, | 91 | VK_FORMAT_R16G16_UNORM, |
| 92 | VK_FORMAT_R16G16_SFLOAT, | 92 | VK_FORMAT_R16G16_SNORM, |
| 93 | VK_FORMAT_R16_UNORM, | 93 | VK_FORMAT_R16G16_SFLOAT, |
| 94 | VK_FORMAT_R8G8B8A8_SRGB, | 94 | VK_FORMAT_R16_UNORM, |
| 95 | VK_FORMAT_R8G8_UNORM, | 95 | VK_FORMAT_R16_UINT, |
| 96 | VK_FORMAT_R8G8_SNORM, | 96 | VK_FORMAT_R8G8B8A8_SRGB, |
| 97 | VK_FORMAT_R8G8_UINT, | 97 | VK_FORMAT_R8G8_UNORM, |
| 98 | VK_FORMAT_R8_UNORM, | 98 | VK_FORMAT_R8G8_SNORM, |
| 99 | VK_FORMAT_R8_UINT, | 99 | VK_FORMAT_R8G8_UINT, |
| 100 | VK_FORMAT_B10G11R11_UFLOAT_PACK32, | 100 | VK_FORMAT_R8_UNORM, |
| 101 | VK_FORMAT_R32_SFLOAT, | 101 | VK_FORMAT_R8_UINT, |
| 102 | VK_FORMAT_R32_UINT, | 102 | VK_FORMAT_B10G11R11_UFLOAT_PACK32, |
| 103 | VK_FORMAT_R32_SINT, | 103 | VK_FORMAT_R32_SFLOAT, |
| 104 | VK_FORMAT_R16_SFLOAT, | 104 | VK_FORMAT_R32_UINT, |
| 105 | VK_FORMAT_R16G16B16A16_SFLOAT, | 105 | VK_FORMAT_R32_SINT, |
| 106 | VK_FORMAT_B8G8R8A8_UNORM, | 106 | VK_FORMAT_R16_SFLOAT, |
| 107 | VK_FORMAT_B8G8R8A8_SRGB, | 107 | VK_FORMAT_R16G16B16A16_SFLOAT, |
| 108 | VK_FORMAT_R4G4B4A4_UNORM_PACK16, | 108 | VK_FORMAT_B8G8R8A8_UNORM, |
| 109 | VK_FORMAT_D32_SFLOAT, | 109 | VK_FORMAT_B8G8R8A8_SRGB, |
| 110 | VK_FORMAT_D16_UNORM, | 110 | VK_FORMAT_R4G4B4A4_UNORM_PACK16, |
| 111 | VK_FORMAT_D16_UNORM_S8_UINT, | 111 | VK_FORMAT_D32_SFLOAT, |
| 112 | VK_FORMAT_D24_UNORM_S8_UINT, | 112 | VK_FORMAT_D16_UNORM, |
| 113 | VK_FORMAT_D32_SFLOAT_S8_UINT, | 113 | VK_FORMAT_D16_UNORM_S8_UINT, |
| 114 | VK_FORMAT_BC1_RGBA_UNORM_BLOCK, | 114 | VK_FORMAT_D24_UNORM_S8_UINT, |
| 115 | VK_FORMAT_BC2_UNORM_BLOCK, | 115 | VK_FORMAT_D32_SFLOAT_S8_UINT, |
| 116 | VK_FORMAT_BC3_UNORM_BLOCK, | 116 | VK_FORMAT_BC1_RGBA_UNORM_BLOCK, |
| 117 | VK_FORMAT_BC4_UNORM_BLOCK, | 117 | VK_FORMAT_BC2_UNORM_BLOCK, |
| 118 | VK_FORMAT_BC5_UNORM_BLOCK, | 118 | VK_FORMAT_BC3_UNORM_BLOCK, |
| 119 | VK_FORMAT_BC5_SNORM_BLOCK, | 119 | VK_FORMAT_BC4_UNORM_BLOCK, |
| 120 | VK_FORMAT_BC7_UNORM_BLOCK, | 120 | VK_FORMAT_BC5_UNORM_BLOCK, |
| 121 | VK_FORMAT_BC6H_UFLOAT_BLOCK, | 121 | VK_FORMAT_BC5_SNORM_BLOCK, |
| 122 | VK_FORMAT_BC6H_SFLOAT_BLOCK, | 122 | VK_FORMAT_BC7_UNORM_BLOCK, |
| 123 | VK_FORMAT_BC1_RGBA_SRGB_BLOCK, | 123 | VK_FORMAT_BC6H_UFLOAT_BLOCK, |
| 124 | VK_FORMAT_BC2_SRGB_BLOCK, | 124 | VK_FORMAT_BC6H_SFLOAT_BLOCK, |
| 125 | VK_FORMAT_BC3_SRGB_BLOCK, | 125 | VK_FORMAT_BC1_RGBA_SRGB_BLOCK, |
| 126 | VK_FORMAT_BC7_SRGB_BLOCK, | 126 | VK_FORMAT_BC2_SRGB_BLOCK, |
| 127 | VK_FORMAT_ASTC_4x4_SRGB_BLOCK, | 127 | VK_FORMAT_BC3_SRGB_BLOCK, |
| 128 | VK_FORMAT_ASTC_8x8_SRGB_BLOCK, | 128 | VK_FORMAT_BC7_SRGB_BLOCK, |
| 129 | VK_FORMAT_ASTC_8x5_SRGB_BLOCK, | 129 | VK_FORMAT_ASTC_4x4_SRGB_BLOCK, |
| 130 | VK_FORMAT_ASTC_5x4_SRGB_BLOCK, | 130 | VK_FORMAT_ASTC_8x8_SRGB_BLOCK, |
| 131 | VK_FORMAT_ASTC_5x5_UNORM_BLOCK, | 131 | VK_FORMAT_ASTC_8x5_SRGB_BLOCK, |
| 132 | VK_FORMAT_ASTC_5x5_SRGB_BLOCK, | 132 | VK_FORMAT_ASTC_5x4_SRGB_BLOCK, |
| 133 | VK_FORMAT_ASTC_10x8_UNORM_BLOCK, | 133 | VK_FORMAT_ASTC_5x5_UNORM_BLOCK, |
| 134 | VK_FORMAT_ASTC_10x8_SRGB_BLOCK, | 134 | VK_FORMAT_ASTC_5x5_SRGB_BLOCK, |
| 135 | VK_FORMAT_ASTC_6x6_UNORM_BLOCK, | 135 | VK_FORMAT_ASTC_10x8_UNORM_BLOCK, |
| 136 | VK_FORMAT_ASTC_6x6_SRGB_BLOCK, | 136 | VK_FORMAT_ASTC_10x8_SRGB_BLOCK, |
| 137 | VK_FORMAT_ASTC_10x10_UNORM_BLOCK, | 137 | VK_FORMAT_ASTC_6x6_UNORM_BLOCK, |
| 138 | VK_FORMAT_ASTC_10x10_SRGB_BLOCK, | 138 | VK_FORMAT_ASTC_6x6_SRGB_BLOCK, |
| 139 | VK_FORMAT_ASTC_12x12_UNORM_BLOCK, | 139 | VK_FORMAT_ASTC_10x10_UNORM_BLOCK, |
| 140 | VK_FORMAT_ASTC_12x12_SRGB_BLOCK, | 140 | VK_FORMAT_ASTC_10x10_SRGB_BLOCK, |
| 141 | VK_FORMAT_ASTC_8x6_UNORM_BLOCK, | 141 | VK_FORMAT_ASTC_12x12_UNORM_BLOCK, |
| 142 | VK_FORMAT_ASTC_8x6_SRGB_BLOCK, | 142 | VK_FORMAT_ASTC_12x12_SRGB_BLOCK, |
| 143 | VK_FORMAT_ASTC_6x5_UNORM_BLOCK, | 143 | VK_FORMAT_ASTC_8x6_UNORM_BLOCK, |
| 144 | VK_FORMAT_ASTC_6x5_SRGB_BLOCK, | 144 | VK_FORMAT_ASTC_8x6_SRGB_BLOCK, |
| 145 | VK_FORMAT_E5B9G9R9_UFLOAT_PACK32}; | 145 | VK_FORMAT_ASTC_6x5_UNORM_BLOCK, |
| 146 | VK_FORMAT_ASTC_6x5_SRGB_BLOCK, | ||
| 147 | VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, | ||
| 148 | }; | ||
| 146 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; | 149 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; |
| 147 | for (const auto format : formats) { | 150 | for (const auto format : formats) { |
| 148 | format_properties.emplace(format, physical.GetFormatProperties(format)); | 151 | format_properties.emplace(format, physical.GetFormatProperties(format)); |
| @@ -310,6 +313,16 @@ bool VKDevice::Create() { | |||
| 310 | LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); | 313 | LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); |
| 311 | } | 314 | } |
| 312 | 315 | ||
| 316 | VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state; | ||
| 317 | if (ext_extended_dynamic_state) { | ||
| 318 | dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; | ||
| 319 | dynamic_state.pNext = nullptr; | ||
| 320 | dynamic_state.extendedDynamicState = VK_TRUE; | ||
| 321 | SetNext(next, dynamic_state); | ||
| 322 | } else { | ||
| 323 | LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state"); | ||
| 324 | } | ||
| 325 | |||
| 313 | if (!ext_depth_range_unrestricted) { | 326 | if (!ext_depth_range_unrestricted) { |
| 314 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); | 327 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); |
| 315 | } | 328 | } |
| @@ -538,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 538 | bool has_ext_subgroup_size_control{}; | 551 | bool has_ext_subgroup_size_control{}; |
| 539 | bool has_ext_transform_feedback{}; | 552 | bool has_ext_transform_feedback{}; |
| 540 | bool has_ext_custom_border_color{}; | 553 | bool has_ext_custom_border_color{}; |
| 554 | bool has_ext_extended_dynamic_state{}; | ||
| 541 | for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { | 555 | for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { |
| 542 | Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); | 556 | Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); |
| 543 | Test(extension, khr_uniform_buffer_standard_layout, | 557 | Test(extension, khr_uniform_buffer_standard_layout, |
| @@ -555,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 555 | false); | 569 | false); |
| 556 | Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, | 570 | Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, |
| 557 | false); | 571 | false); |
| 572 | Test(extension, has_ext_extended_dynamic_state, | ||
| 573 | VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); | ||
| 558 | if (Settings::values.renderer_debug) { | 574 | if (Settings::values.renderer_debug) { |
| 559 | Test(extension, nv_device_diagnostics_config, | 575 | Test(extension, nv_device_diagnostics_config, |
| 560 | VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); | 576 | VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); |
| @@ -640,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 640 | } | 656 | } |
| 641 | } | 657 | } |
| 642 | 658 | ||
| 659 | if (has_ext_extended_dynamic_state) { | ||
| 660 | VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state; | ||
| 661 | dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; | ||
| 662 | dynamic_state.pNext = nullptr; | ||
| 663 | features.pNext = &dynamic_state; | ||
| 664 | physical.GetFeatures2KHR(features); | ||
| 665 | |||
| 666 | if (dynamic_state.extendedDynamicState) { | ||
| 667 | extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); | ||
| 668 | ext_extended_dynamic_state = true; | ||
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 643 | return extensions; | 672 | return extensions; |
| 644 | } | 673 | } |
| 645 | 674 | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index 6b9227b09..ae5c21baa 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h | |||
| @@ -182,6 +182,11 @@ public: | |||
| 182 | return ext_custom_border_color; | 182 | return ext_custom_border_color; |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | /// Returns true if the device supports VK_EXT_extended_dynamic_state. | ||
| 186 | bool IsExtExtendedDynamicStateSupported() const { | ||
| 187 | return ext_extended_dynamic_state; | ||
| 188 | } | ||
| 189 | |||
| 185 | /// Returns the vendor name reported from Vulkan. | 190 | /// Returns the vendor name reported from Vulkan. |
| 186 | std::string_view GetVendorName() const { | 191 | std::string_view GetVendorName() const { |
| 187 | return vendor_name; | 192 | return vendor_name; |
| @@ -239,6 +244,7 @@ private: | |||
| 239 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 244 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 240 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. | 245 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. |
| 241 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. | 246 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. |
| 247 | bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. | ||
| 242 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. | 248 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. |
| 243 | 249 | ||
| 244 | // Telemetry parameters | 250 | // Telemetry parameters |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 69b6bba00..844445105 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules( | |||
| 176 | 176 | ||
| 177 | vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, | 177 | vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, |
| 178 | const SPIRVProgram& program) const { | 178 | const SPIRVProgram& program) const { |
| 179 | const auto& vi = fixed_state.vertex_input; | 179 | const auto& state = fixed_state; |
| 180 | const auto& ds = fixed_state.depth_stencil; | 180 | const auto& viewport_swizzles = state.viewport_swizzles; |
| 181 | const auto& cd = fixed_state.color_blending; | 181 | |
| 182 | const auto& rs = fixed_state.rasterizer; | 182 | FixedPipelineState::DynamicState dynamic; |
| 183 | const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles; | 183 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 184 | // Insert dummy values, as long as they are valid they don't matter as extended dynamic | ||
| 185 | // state is ignored | ||
| 186 | dynamic.raw1 = 0; | ||
| 187 | dynamic.raw2 = 0; | ||
| 188 | for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) { | ||
| 189 | // Enable all vertex bindings | ||
| 190 | binding.raw = 0; | ||
| 191 | binding.enabled.Assign(1); | ||
| 192 | } | ||
| 193 | } else { | ||
| 194 | dynamic = state.dynamic_state; | ||
| 195 | } | ||
| 184 | 196 | ||
| 185 | std::vector<VkVertexInputBindingDescription> vertex_bindings; | 197 | std::vector<VkVertexInputBindingDescription> vertex_bindings; |
| 186 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; | 198 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; |
| 187 | for (std::size_t index = 0; index < std::size(vi.bindings); ++index) { | 199 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 188 | const auto& binding = vi.bindings[index]; | 200 | const auto& binding = dynamic.vertex_bindings[index]; |
| 189 | if (!binding.enabled) { | 201 | if (!binding.enabled) { |
| 190 | continue; | 202 | continue; |
| 191 | } | 203 | } |
| 192 | const bool instanced = vi.binding_divisors[index] != 0; | 204 | const bool instanced = state.binding_divisors[index] != 0; |
| 193 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; | 205 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; |
| 194 | 206 | ||
| 195 | auto& vertex_binding = vertex_bindings.emplace_back(); | 207 | auto& vertex_binding = vertex_bindings.emplace_back(); |
| @@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 200 | if (instanced) { | 212 | if (instanced) { |
| 201 | auto& binding_divisor = vertex_binding_divisors.emplace_back(); | 213 | auto& binding_divisor = vertex_binding_divisors.emplace_back(); |
| 202 | binding_divisor.binding = static_cast<u32>(index); | 214 | binding_divisor.binding = static_cast<u32>(index); |
| 203 | binding_divisor.divisor = vi.binding_divisors[index]; | 215 | binding_divisor.divisor = state.binding_divisors[index]; |
| 204 | } | 216 | } |
| 205 | } | 217 | } |
| 206 | 218 | ||
| 207 | std::vector<VkVertexInputAttributeDescription> vertex_attributes; | 219 | std::vector<VkVertexInputAttributeDescription> vertex_attributes; |
| 208 | const auto& input_attributes = program[0]->entries.attributes; | 220 | const auto& input_attributes = program[0]->entries.attributes; |
| 209 | for (std::size_t index = 0; index < std::size(vi.attributes); ++index) { | 221 | for (std::size_t index = 0; index < state.attributes.size(); ++index) { |
| 210 | const auto& attribute = vi.attributes[index]; | 222 | const auto& attribute = state.attributes[index]; |
| 211 | if (!attribute.enabled) { | 223 | if (!attribute.enabled) { |
| 212 | continue; | 224 | continue; |
| 213 | } | 225 | } |
| @@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 244 | input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | 256 | input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
| 245 | input_assembly_ci.pNext = nullptr; | 257 | input_assembly_ci.pNext = nullptr; |
| 246 | input_assembly_ci.flags = 0; | 258 | input_assembly_ci.flags = 0; |
| 247 | input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology()); | 259 | input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology()); |
| 248 | input_assembly_ci.primitiveRestartEnable = | 260 | input_assembly_ci.primitiveRestartEnable = |
| 249 | rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); | 261 | state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); |
| 250 | 262 | ||
| 251 | VkPipelineTessellationStateCreateInfo tessellation_ci; | 263 | VkPipelineTessellationStateCreateInfo tessellation_ci; |
| 252 | tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; | 264 | tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; |
| 253 | tessellation_ci.pNext = nullptr; | 265 | tessellation_ci.pNext = nullptr; |
| 254 | tessellation_ci.flags = 0; | 266 | tessellation_ci.flags = 0; |
| 255 | tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1; | 267 | tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1; |
| 256 | 268 | ||
| 257 | VkPipelineViewportStateCreateInfo viewport_ci; | 269 | VkPipelineViewportStateCreateInfo viewport_ci; |
| 258 | viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | 270 | viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
| @@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 280 | rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | 292 | rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
| 281 | rasterization_ci.pNext = nullptr; | 293 | rasterization_ci.pNext = nullptr; |
| 282 | rasterization_ci.flags = 0; | 294 | rasterization_ci.flags = 0; |
| 283 | rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; | 295 | rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; |
| 284 | rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; | 296 | rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; |
| 285 | rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; | 297 | rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; |
| 286 | rasterization_ci.cullMode = | 298 | rasterization_ci.cullMode = |
| 287 | rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; | 299 | dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE; |
| 288 | rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace()); | 300 | rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()); |
| 289 | rasterization_ci.depthBiasEnable = rs.depth_bias_enable; | 301 | rasterization_ci.depthBiasEnable = state.depth_bias_enable; |
| 290 | rasterization_ci.depthBiasConstantFactor = 0.0f; | 302 | rasterization_ci.depthBiasConstantFactor = 0.0f; |
| 291 | rasterization_ci.depthBiasClamp = 0.0f; | 303 | rasterization_ci.depthBiasClamp = 0.0f; |
| 292 | rasterization_ci.depthBiasSlopeFactor = 0.0f; | 304 | rasterization_ci.depthBiasSlopeFactor = 0.0f; |
| @@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 307 | depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; | 319 | depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
| 308 | depth_stencil_ci.pNext = nullptr; | 320 | depth_stencil_ci.pNext = nullptr; |
| 309 | depth_stencil_ci.flags = 0; | 321 | depth_stencil_ci.flags = 0; |
| 310 | depth_stencil_ci.depthTestEnable = ds.depth_test_enable; | 322 | depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable; |
| 311 | depth_stencil_ci.depthWriteEnable = ds.depth_write_enable; | 323 | depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable; |
| 312 | depth_stencil_ci.depthCompareOp = | 324 | depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable |
| 313 | ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS; | 325 | ? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc()) |
| 314 | depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable; | 326 | : VK_COMPARE_OP_ALWAYS; |
| 315 | depth_stencil_ci.stencilTestEnable = ds.stencil_enable; | 327 | depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable; |
| 316 | depth_stencil_ci.front = GetStencilFaceState(ds.front); | 328 | depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable; |
| 317 | depth_stencil_ci.back = GetStencilFaceState(ds.back); | 329 | depth_stencil_ci.front = GetStencilFaceState(dynamic.front); |
| 330 | depth_stencil_ci.back = GetStencilFaceState(dynamic.back); | ||
| 318 | depth_stencil_ci.minDepthBounds = 0.0f; | 331 | depth_stencil_ci.minDepthBounds = 0.0f; |
| 319 | depth_stencil_ci.maxDepthBounds = 0.0f; | 332 | depth_stencil_ci.maxDepthBounds = 0.0f; |
| 320 | 333 | ||
| @@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 324 | static constexpr std::array COMPONENT_TABLE = { | 337 | static constexpr std::array COMPONENT_TABLE = { |
| 325 | VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, | 338 | VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, |
| 326 | VK_COLOR_COMPONENT_A_BIT}; | 339 | VK_COLOR_COMPONENT_A_BIT}; |
| 327 | const auto& blend = cd.attachments[index]; | 340 | const auto& blend = state.attachments[index]; |
| 328 | 341 | ||
| 329 | VkColorComponentFlags color_components = 0; | 342 | VkColorComponentFlags color_components = 0; |
| 330 | for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { | 343 | for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { |
| @@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 354 | color_blend_ci.pAttachments = cb_attachments.data(); | 367 | color_blend_ci.pAttachments = cb_attachments.data(); |
| 355 | std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); | 368 | std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); |
| 356 | 369 | ||
| 357 | static constexpr std::array dynamic_states = { | 370 | std::vector dynamic_states = { |
| 358 | VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, | 371 | VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, |
| 359 | VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, | 372 | VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, |
| 360 | VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, | 373 | VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, |
| 361 | VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE}; | 374 | VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, |
| 375 | }; | ||
| 376 | if (device.IsExtExtendedDynamicStateSupported()) { | ||
| 377 | static constexpr std::array extended = { | ||
| 378 | VK_DYNAMIC_STATE_CULL_MODE_EXT, | ||
| 379 | VK_DYNAMIC_STATE_FRONT_FACE_EXT, | ||
| 380 | VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT, | ||
| 381 | VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT, | ||
| 382 | VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, | ||
| 383 | VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, | ||
| 384 | VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, | ||
| 385 | VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, | ||
| 386 | VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, | ||
| 387 | VK_DYNAMIC_STATE_STENCIL_OP_EXT, | ||
| 388 | }; | ||
| 389 | dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); | ||
| 390 | } | ||
| 362 | 391 | ||
| 363 | VkPipelineDynamicStateCreateInfo dynamic_state_ci; | 392 | VkPipelineDynamicStateCreateInfo dynamic_state_ci; |
| 364 | dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | 393 | dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a5c7b7945..3da835324 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include "video_core/renderer_vulkan/wrapper.h" | 27 | #include "video_core/renderer_vulkan/wrapper.h" |
| 28 | #include "video_core/shader/compiler_settings.h" | 28 | #include "video_core/shader/compiler_settings.h" |
| 29 | #include "video_core/shader/memory_util.h" | 29 | #include "video_core/shader/memory_util.h" |
| 30 | #include "video_core/shader_cache.h" | ||
| 30 | 31 | ||
| 31 | namespace Vulkan { | 32 | namespace Vulkan { |
| 32 | 33 | ||
| @@ -45,6 +46,7 @@ constexpr VkDescriptorType UNIFORM_BUFFER = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; | |||
| 45 | constexpr VkDescriptorType STORAGE_BUFFER = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; | 46 | constexpr VkDescriptorType STORAGE_BUFFER = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
| 46 | constexpr VkDescriptorType UNIFORM_TEXEL_BUFFER = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; | 47 | constexpr VkDescriptorType UNIFORM_TEXEL_BUFFER = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; |
| 47 | constexpr VkDescriptorType COMBINED_IMAGE_SAMPLER = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; | 48 | constexpr VkDescriptorType COMBINED_IMAGE_SAMPLER = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; |
| 49 | constexpr VkDescriptorType STORAGE_TEXEL_BUFFER = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; | ||
| 48 | constexpr VkDescriptorType STORAGE_IMAGE = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; | 50 | constexpr VkDescriptorType STORAGE_IMAGE = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; |
| 49 | 51 | ||
| 50 | constexpr VideoCommon::Shader::CompilerSettings compiler_settings{ | 52 | constexpr VideoCommon::Shader::CompilerSettings compiler_settings{ |
| @@ -104,8 +106,9 @@ u32 FillDescriptorLayout(const ShaderEntries& entries, | |||
| 104 | u32 binding = base_binding; | 106 | u32 binding = base_binding; |
| 105 | AddBindings<UNIFORM_BUFFER>(bindings, binding, flags, entries.const_buffers); | 107 | AddBindings<UNIFORM_BUFFER>(bindings, binding, flags, entries.const_buffers); |
| 106 | AddBindings<STORAGE_BUFFER>(bindings, binding, flags, entries.global_buffers); | 108 | AddBindings<STORAGE_BUFFER>(bindings, binding, flags, entries.global_buffers); |
| 107 | AddBindings<UNIFORM_TEXEL_BUFFER>(bindings, binding, flags, entries.texel_buffers); | 109 | AddBindings<UNIFORM_TEXEL_BUFFER>(bindings, binding, flags, entries.uniform_texels); |
| 108 | AddBindings<COMBINED_IMAGE_SAMPLER>(bindings, binding, flags, entries.samplers); | 110 | AddBindings<COMBINED_IMAGE_SAMPLER>(bindings, binding, flags, entries.samplers); |
| 111 | AddBindings<STORAGE_TEXEL_BUFFER>(bindings, binding, flags, entries.storage_texels); | ||
| 109 | AddBindings<STORAGE_IMAGE>(bindings, binding, flags, entries.images); | 112 | AddBindings<STORAGE_IMAGE>(bindings, binding, flags, entries.images); |
| 110 | return binding; | 113 | return binding; |
| 111 | } | 114 | } |
| @@ -113,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries, | |||
| 113 | } // Anonymous namespace | 116 | } // Anonymous namespace |
| 114 | 117 | ||
| 115 | std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { | 118 | std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { |
| 116 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); | 119 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size()); |
| 117 | return static_cast<std::size_t>(hash); | 120 | return static_cast<std::size_t>(hash); |
| 118 | } | 121 | } |
| 119 | 122 | ||
| 120 | bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { | 123 | bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { |
| 121 | return std::memcmp(&rhs, this, sizeof *this) == 0; | 124 | return std::memcmp(&rhs, this, Size()) == 0; |
| 122 | } | 125 | } |
| 123 | 126 | ||
| 124 | std::size_t ComputePipelineCacheKey::Hash() const noexcept { | 127 | std::size_t ComputePipelineCacheKey::Hash() const noexcept { |
| @@ -130,19 +133,18 @@ bool ComputePipelineCacheKey::operator==(const ComputePipelineCacheKey& rhs) con | |||
| 130 | return std::memcmp(&rhs, this, sizeof *this) == 0; | 133 | return std::memcmp(&rhs, this, sizeof *this) == 0; |
| 131 | } | 134 | } |
| 132 | 135 | ||
| 133 | CachedShader::CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, | 136 | Shader::Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, |
| 134 | GPUVAddr gpu_addr, VAddr cpu_addr, ProgramCode program_code, | 137 | VideoCommon::Shader::ProgramCode program_code, u32 main_offset) |
| 135 | u32 main_offset) | 138 | : gpu_addr{gpu_addr}, program_code{std::move(program_code)}, |
| 136 | : RasterizerCacheObject{cpu_addr}, gpu_addr{gpu_addr}, program_code{std::move(program_code)}, | ||
| 137 | registry{stage, GetEngine(system, stage)}, shader_ir{this->program_code, main_offset, | 139 | registry{stage, GetEngine(system, stage)}, shader_ir{this->program_code, main_offset, |
| 138 | compiler_settings, registry}, | 140 | compiler_settings, registry}, |
| 139 | entries{GenerateShaderEntries(shader_ir)} {} | 141 | entries{GenerateShaderEntries(shader_ir)} {} |
| 140 | 142 | ||
| 141 | CachedShader::~CachedShader() = default; | 143 | Shader::~Shader() = default; |
| 142 | 144 | ||
| 143 | Tegra::Engines::ConstBufferEngineInterface& CachedShader::GetEngine( | 145 | Tegra::Engines::ConstBufferEngineInterface& Shader::GetEngine(Core::System& system, |
| 144 | Core::System& system, Tegra::Engines::ShaderType stage) { | 146 | Tegra::Engines::ShaderType stage) { |
| 145 | if (stage == Tegra::Engines::ShaderType::Compute) { | 147 | if (stage == ShaderType::Compute) { |
| 146 | return system.GPU().KeplerCompute(); | 148 | return system.GPU().KeplerCompute(); |
| 147 | } else { | 149 | } else { |
| 148 | return system.GPU().Maxwell3D(); | 150 | return system.GPU().Maxwell3D(); |
| @@ -154,16 +156,16 @@ VKPipelineCache::VKPipelineCache(Core::System& system, RasterizerVulkan& rasteri | |||
| 154 | VKDescriptorPool& descriptor_pool, | 156 | VKDescriptorPool& descriptor_pool, |
| 155 | VKUpdateDescriptorQueue& update_descriptor_queue, | 157 | VKUpdateDescriptorQueue& update_descriptor_queue, |
| 156 | VKRenderPassCache& renderpass_cache) | 158 | VKRenderPassCache& renderpass_cache) |
| 157 | : RasterizerCache{rasterizer}, system{system}, device{device}, scheduler{scheduler}, | 159 | : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system}, device{device}, |
| 158 | descriptor_pool{descriptor_pool}, update_descriptor_queue{update_descriptor_queue}, | 160 | scheduler{scheduler}, descriptor_pool{descriptor_pool}, |
| 159 | renderpass_cache{renderpass_cache} {} | 161 | update_descriptor_queue{update_descriptor_queue}, renderpass_cache{renderpass_cache} {} |
| 160 | 162 | ||
| 161 | VKPipelineCache::~VKPipelineCache() = default; | 163 | VKPipelineCache::~VKPipelineCache() = default; |
| 162 | 164 | ||
| 163 | std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { | 165 | std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { |
| 164 | const auto& gpu = system.GPU().Maxwell3D(); | 166 | const auto& gpu = system.GPU().Maxwell3D(); |
| 165 | 167 | ||
| 166 | std::array<Shader, Maxwell::MaxShaderProgram> shaders; | 168 | std::array<Shader*, Maxwell::MaxShaderProgram> shaders{}; |
| 167 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | 169 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
| 168 | const auto program{static_cast<Maxwell::ShaderProgram>(index)}; | 170 | const auto program{static_cast<Maxwell::ShaderProgram>(index)}; |
| 169 | 171 | ||
| @@ -176,24 +178,28 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { | |||
| 176 | const GPUVAddr program_addr{GetShaderAddress(system, program)}; | 178 | const GPUVAddr program_addr{GetShaderAddress(system, program)}; |
| 177 | const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); | 179 | const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); |
| 178 | ASSERT(cpu_addr); | 180 | ASSERT(cpu_addr); |
| 179 | auto shader = cpu_addr ? TryGet(*cpu_addr) : null_shader; | 181 | |
| 180 | if (!shader) { | 182 | Shader* result = cpu_addr ? TryGet(*cpu_addr) : null_shader.get(); |
| 183 | if (!result) { | ||
| 181 | const auto host_ptr{memory_manager.GetPointer(program_addr)}; | 184 | const auto host_ptr{memory_manager.GetPointer(program_addr)}; |
| 182 | 185 | ||
| 183 | // No shader found - create a new one | 186 | // No shader found - create a new one |
| 184 | constexpr u32 stage_offset = STAGE_MAIN_OFFSET; | 187 | constexpr u32 stage_offset = STAGE_MAIN_OFFSET; |
| 185 | const auto stage = static_cast<Tegra::Engines::ShaderType>(index == 0 ? 0 : index - 1); | 188 | const auto stage = static_cast<ShaderType>(index == 0 ? 0 : index - 1); |
| 186 | ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, false); | 189 | ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, false); |
| 190 | const std::size_t size_in_bytes = code.size() * sizeof(u64); | ||
| 191 | |||
| 192 | auto shader = std::make_unique<Shader>(system, stage, program_addr, std::move(code), | ||
| 193 | stage_offset); | ||
| 194 | result = shader.get(); | ||
| 187 | 195 | ||
| 188 | shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr, | ||
| 189 | std::move(code), stage_offset); | ||
| 190 | if (cpu_addr) { | 196 | if (cpu_addr) { |
| 191 | Register(shader); | 197 | Register(std::move(shader), *cpu_addr, size_in_bytes); |
| 192 | } else { | 198 | } else { |
| 193 | null_shader = shader; | 199 | null_shader = std::move(shader); |
| 194 | } | 200 | } |
| 195 | } | 201 | } |
| 196 | shaders[index] = std::move(shader); | 202 | shaders[index] = result; |
| 197 | } | 203 | } |
| 198 | return last_shaders = shaders; | 204 | return last_shaders = shaders; |
| 199 | } | 205 | } |
| @@ -234,19 +240,22 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach | |||
| 234 | const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); | 240 | const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); |
| 235 | ASSERT(cpu_addr); | 241 | ASSERT(cpu_addr); |
| 236 | 242 | ||
| 237 | auto shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel; | 243 | Shader* shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get(); |
| 238 | if (!shader) { | 244 | if (!shader) { |
| 239 | // No shader found - create a new one | 245 | // No shader found - create a new one |
| 240 | const auto host_ptr = memory_manager.GetPointer(program_addr); | 246 | const auto host_ptr = memory_manager.GetPointer(program_addr); |
| 241 | 247 | ||
| 242 | ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, true); | 248 | ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, true); |
| 243 | shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, | 249 | const std::size_t size_in_bytes = code.size() * sizeof(u64); |
| 244 | program_addr, *cpu_addr, std::move(code), | 250 | |
| 245 | KERNEL_MAIN_OFFSET); | 251 | auto shader_info = std::make_unique<Shader>(system, ShaderType::Compute, program_addr, |
| 252 | std::move(code), KERNEL_MAIN_OFFSET); | ||
| 253 | shader = shader_info.get(); | ||
| 254 | |||
| 246 | if (cpu_addr) { | 255 | if (cpu_addr) { |
| 247 | Register(shader); | 256 | Register(std::move(shader_info), *cpu_addr, size_in_bytes); |
| 248 | } else { | 257 | } else { |
| 249 | null_kernel = shader; | 258 | null_kernel = std::move(shader_info); |
| 250 | } | 259 | } |
| 251 | } | 260 | } |
| 252 | 261 | ||
| @@ -262,7 +271,7 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach | |||
| 262 | return *entry; | 271 | return *entry; |
| 263 | } | 272 | } |
| 264 | 273 | ||
| 265 | void VKPipelineCache::Unregister(const Shader& shader) { | 274 | void VKPipelineCache::OnShaderRemoval(Shader* shader) { |
| 266 | bool finished = false; | 275 | bool finished = false; |
| 267 | const auto Finish = [&] { | 276 | const auto Finish = [&] { |
| 268 | // TODO(Rodrigo): Instead of finishing here, wait for the fences that use this pipeline and | 277 | // TODO(Rodrigo): Instead of finishing here, wait for the fences that use this pipeline and |
| @@ -294,8 +303,6 @@ void VKPipelineCache::Unregister(const Shader& shader) { | |||
| 294 | Finish(); | 303 | Finish(); |
| 295 | it = compute_cache.erase(it); | 304 | it = compute_cache.erase(it); |
| 296 | } | 305 | } |
| 297 | |||
| 298 | RasterizerCache::Unregister(shader); | ||
| 299 | } | 306 | } |
| 300 | 307 | ||
| 301 | std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> | 308 | std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> |
| @@ -305,16 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 305 | const auto& gpu = system.GPU().Maxwell3D(); | 312 | const auto& gpu = system.GPU().Maxwell3D(); |
| 306 | 313 | ||
| 307 | Specialization specialization; | 314 | Specialization specialization; |
| 308 | if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { | 315 | if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points || |
| 316 | device.IsExtExtendedDynamicStateSupported()) { | ||
| 309 | float point_size; | 317 | float point_size; |
| 310 | std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float)); | 318 | std::memcpy(&point_size, &fixed_state.point_size, sizeof(float)); |
| 311 | specialization.point_size = point_size; | 319 | specialization.point_size = point_size; |
| 312 | ASSERT(point_size != 0.0f); | 320 | ASSERT(point_size != 0.0f); |
| 313 | } | 321 | } |
| 314 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { | 322 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { |
| 315 | specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type(); | 323 | const auto& attribute = fixed_state.attributes[i]; |
| 324 | specialization.enabled_attributes[i] = attribute.enabled.Value() != 0; | ||
| 325 | specialization.attribute_types[i] = attribute.Type(); | ||
| 316 | } | 326 | } |
| 317 | specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; | 327 | specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one; |
| 318 | 328 | ||
| 319 | SPIRVProgram program; | 329 | SPIRVProgram program; |
| 320 | std::vector<VkDescriptorSetLayoutBinding> bindings; | 330 | std::vector<VkDescriptorSetLayoutBinding> bindings; |
| @@ -328,12 +338,11 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 328 | } | 338 | } |
| 329 | 339 | ||
| 330 | const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum); | 340 | const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum); |
| 331 | const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr); | 341 | const std::optional<VAddr> cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr); |
| 332 | const auto shader = cpu_addr ? TryGet(*cpu_addr) : null_shader; | 342 | Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get(); |
| 333 | ASSERT(shader); | ||
| 334 | 343 | ||
| 335 | const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 | 344 | const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 |
| 336 | const auto program_type = GetShaderType(program_enum); | 345 | const ShaderType program_type = GetShaderType(program_enum); |
| 337 | const auto& entries = shader->GetEntries(); | 346 | const auto& entries = shader->GetEntries(); |
| 338 | program[stage] = { | 347 | program[stage] = { |
| 339 | Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization), | 348 | Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization), |
| @@ -375,16 +384,17 @@ void AddEntry(std::vector<VkDescriptorUpdateTemplateEntry>& template_entries, u3 | |||
| 375 | return; | 384 | return; |
| 376 | } | 385 | } |
| 377 | 386 | ||
| 378 | if constexpr (descriptor_type == UNIFORM_TEXEL_BUFFER) { | 387 | if constexpr (descriptor_type == UNIFORM_TEXEL_BUFFER || |
| 379 | // Nvidia has a bug where updating multiple uniform texels at once causes the driver to | 388 | descriptor_type == STORAGE_TEXEL_BUFFER) { |
| 380 | // crash. | 389 | // Nvidia has a bug where updating multiple texels at once causes the driver to crash. |
| 390 | // Note: Fixed in driver Windows 443.24, Linux 440.66.15 | ||
| 381 | for (u32 i = 0; i < count; ++i) { | 391 | for (u32 i = 0; i < count; ++i) { |
| 382 | VkDescriptorUpdateTemplateEntry& entry = template_entries.emplace_back(); | 392 | VkDescriptorUpdateTemplateEntry& entry = template_entries.emplace_back(); |
| 383 | entry.dstBinding = binding + i; | 393 | entry.dstBinding = binding + i; |
| 384 | entry.dstArrayElement = 0; | 394 | entry.dstArrayElement = 0; |
| 385 | entry.descriptorCount = 1; | 395 | entry.descriptorCount = 1; |
| 386 | entry.descriptorType = descriptor_type; | 396 | entry.descriptorType = descriptor_type; |
| 387 | entry.offset = offset + i * entry_size; | 397 | entry.offset = static_cast<std::size_t>(offset + i * entry_size); |
| 388 | entry.stride = entry_size; | 398 | entry.stride = entry_size; |
| 389 | } | 399 | } |
| 390 | } else if (count > 0) { | 400 | } else if (count > 0) { |
| @@ -405,8 +415,9 @@ void FillDescriptorUpdateTemplateEntries( | |||
| 405 | std::vector<VkDescriptorUpdateTemplateEntryKHR>& template_entries) { | 415 | std::vector<VkDescriptorUpdateTemplateEntryKHR>& template_entries) { |
| 406 | AddEntry<UNIFORM_BUFFER>(template_entries, offset, binding, entries.const_buffers); | 416 | AddEntry<UNIFORM_BUFFER>(template_entries, offset, binding, entries.const_buffers); |
| 407 | AddEntry<STORAGE_BUFFER>(template_entries, offset, binding, entries.global_buffers); | 417 | AddEntry<STORAGE_BUFFER>(template_entries, offset, binding, entries.global_buffers); |
| 408 | AddEntry<UNIFORM_TEXEL_BUFFER>(template_entries, offset, binding, entries.texel_buffers); | 418 | AddEntry<UNIFORM_TEXEL_BUFFER>(template_entries, offset, binding, entries.uniform_texels); |
| 409 | AddEntry<COMBINED_IMAGE_SAMPLER>(template_entries, offset, binding, entries.samplers); | 419 | AddEntry<COMBINED_IMAGE_SAMPLER>(template_entries, offset, binding, entries.samplers); |
| 420 | AddEntry<STORAGE_TEXEL_BUFFER>(template_entries, offset, binding, entries.storage_texels); | ||
| 410 | AddEntry<STORAGE_IMAGE>(template_entries, offset, binding, entries.images); | 421 | AddEntry<STORAGE_IMAGE>(template_entries, offset, binding, entries.images); |
| 411 | } | 422 | } |
| 412 | 423 | ||
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 0b5796fef..0a3fe65fb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -17,7 +17,6 @@ | |||
| 17 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "video_core/engines/const_buffer_engine_interface.h" | 18 | #include "video_core/engines/const_buffer_engine_interface.h" |
| 19 | #include "video_core/engines/maxwell_3d.h" | 19 | #include "video_core/engines/maxwell_3d.h" |
| 20 | #include "video_core/rasterizer_cache.h" | ||
| 21 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" | 20 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" |
| 22 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" | 21 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" |
| 23 | #include "video_core/renderer_vulkan/vk_renderpass_cache.h" | 22 | #include "video_core/renderer_vulkan/vk_renderpass_cache.h" |
| @@ -26,6 +25,7 @@ | |||
| 26 | #include "video_core/shader/memory_util.h" | 25 | #include "video_core/shader/memory_util.h" |
| 27 | #include "video_core/shader/registry.h" | 26 | #include "video_core/shader/registry.h" |
| 28 | #include "video_core/shader/shader_ir.h" | 27 | #include "video_core/shader/shader_ir.h" |
| 28 | #include "video_core/shader_cache.h" | ||
| 29 | 29 | ||
| 30 | namespace Core { | 30 | namespace Core { |
| 31 | class System; | 31 | class System; |
| @@ -41,15 +41,13 @@ class VKFence; | |||
| 41 | class VKScheduler; | 41 | class VKScheduler; |
| 42 | class VKUpdateDescriptorQueue; | 42 | class VKUpdateDescriptorQueue; |
| 43 | 43 | ||
| 44 | class CachedShader; | ||
| 45 | using Shader = std::shared_ptr<CachedShader>; | ||
| 46 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 44 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 47 | 45 | ||
| 48 | struct GraphicsPipelineCacheKey { | 46 | struct GraphicsPipelineCacheKey { |
| 49 | FixedPipelineState fixed_state; | ||
| 50 | RenderPassParams renderpass_params; | 47 | RenderPassParams renderpass_params; |
| 48 | u32 padding; | ||
| 51 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; | 49 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; |
| 52 | u64 padding; // This is necessary for unique object representations | 50 | FixedPipelineState fixed_state; |
| 53 | 51 | ||
| 54 | std::size_t Hash() const noexcept; | 52 | std::size_t Hash() const noexcept; |
| 55 | 53 | ||
| @@ -58,6 +56,10 @@ struct GraphicsPipelineCacheKey { | |||
| 58 | bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { | 56 | bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { |
| 59 | return !operator==(rhs); | 57 | return !operator==(rhs); |
| 60 | } | 58 | } |
| 59 | |||
| 60 | std::size_t Size() const noexcept { | ||
| 61 | return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size(); | ||
| 62 | } | ||
| 61 | }; | 63 | }; |
| 62 | static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); | 64 | static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); |
| 63 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); | 65 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); |
| @@ -102,21 +104,16 @@ struct hash<Vulkan::ComputePipelineCacheKey> { | |||
| 102 | 104 | ||
| 103 | namespace Vulkan { | 105 | namespace Vulkan { |
| 104 | 106 | ||
| 105 | class CachedShader final : public RasterizerCacheObject { | 107 | class Shader { |
| 106 | public: | 108 | public: |
| 107 | explicit CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, | 109 | explicit Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, |
| 108 | VAddr cpu_addr, VideoCommon::Shader::ProgramCode program_code, | 110 | VideoCommon::Shader::ProgramCode program_code, u32 main_offset); |
| 109 | u32 main_offset); | 111 | ~Shader(); |
| 110 | ~CachedShader(); | ||
| 111 | 112 | ||
| 112 | GPUVAddr GetGpuAddr() const { | 113 | GPUVAddr GetGpuAddr() const { |
| 113 | return gpu_addr; | 114 | return gpu_addr; |
| 114 | } | 115 | } |
| 115 | 116 | ||
| 116 | std::size_t GetSizeInBytes() const override { | ||
| 117 | return program_code.size() * sizeof(u64); | ||
| 118 | } | ||
| 119 | |||
| 120 | VideoCommon::Shader::ShaderIR& GetIR() { | 117 | VideoCommon::Shader::ShaderIR& GetIR() { |
| 121 | return shader_ir; | 118 | return shader_ir; |
| 122 | } | 119 | } |
| @@ -144,25 +141,23 @@ private: | |||
| 144 | ShaderEntries entries; | 141 | ShaderEntries entries; |
| 145 | }; | 142 | }; |
| 146 | 143 | ||
| 147 | class VKPipelineCache final : public RasterizerCache<Shader> { | 144 | class VKPipelineCache final : public VideoCommon::ShaderCache<Shader> { |
| 148 | public: | 145 | public: |
| 149 | explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer, | 146 | explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer, |
| 150 | const VKDevice& device, VKScheduler& scheduler, | 147 | const VKDevice& device, VKScheduler& scheduler, |
| 151 | VKDescriptorPool& descriptor_pool, | 148 | VKDescriptorPool& descriptor_pool, |
| 152 | VKUpdateDescriptorQueue& update_descriptor_queue, | 149 | VKUpdateDescriptorQueue& update_descriptor_queue, |
| 153 | VKRenderPassCache& renderpass_cache); | 150 | VKRenderPassCache& renderpass_cache); |
| 154 | ~VKPipelineCache(); | 151 | ~VKPipelineCache() override; |
| 155 | 152 | ||
| 156 | std::array<Shader, Maxwell::MaxShaderProgram> GetShaders(); | 153 | std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders(); |
| 157 | 154 | ||
| 158 | VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key); | 155 | VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key); |
| 159 | 156 | ||
| 160 | VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key); | 157 | VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key); |
| 161 | 158 | ||
| 162 | protected: | 159 | protected: |
| 163 | void Unregister(const Shader& shader) override; | 160 | void OnShaderRemoval(Shader* shader) final; |
| 164 | |||
| 165 | void FlushObjectInner(const Shader& object) override {} | ||
| 166 | 161 | ||
| 167 | private: | 162 | private: |
| 168 | std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders( | 163 | std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders( |
| @@ -175,10 +170,10 @@ private: | |||
| 175 | VKUpdateDescriptorQueue& update_descriptor_queue; | 170 | VKUpdateDescriptorQueue& update_descriptor_queue; |
| 176 | VKRenderPassCache& renderpass_cache; | 171 | VKRenderPassCache& renderpass_cache; |
| 177 | 172 | ||
| 178 | Shader null_shader{}; | 173 | std::unique_ptr<Shader> null_shader; |
| 179 | Shader null_kernel{}; | 174 | std::unique_ptr<Shader> null_kernel; |
| 180 | 175 | ||
| 181 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | 176 | std::array<Shader*, Maxwell::MaxShaderProgram> last_shaders{}; |
| 182 | 177 | ||
| 183 | GraphicsPipelineCacheKey last_graphics_key; | 178 | GraphicsPipelineCacheKey last_graphics_key; |
| 184 | VKGraphicsPipeline* last_graphics_pipeline = nullptr; | 179 | VKGraphicsPipeline* last_graphics_pipeline = nullptr; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index be5b77fae..380ed532b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 38 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 39 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 39 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 40 | #include "video_core/renderer_vulkan/wrapper.h" | 40 | #include "video_core/renderer_vulkan/wrapper.h" |
| 41 | #include "video_core/shader_cache.h" | ||
| 41 | 42 | ||
| 42 | namespace Vulkan { | 43 | namespace Vulkan { |
| 43 | 44 | ||
| @@ -98,7 +99,7 @@ VkRect2D GetScissorState(const Maxwell& regs, std::size_t index) { | |||
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> GetShaderAddresses( | 101 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> GetShaderAddresses( |
| 101 | const std::array<Shader, Maxwell::MaxShaderProgram>& shaders) { | 102 | const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders) { |
| 102 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> addresses; | 103 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> addresses; |
| 103 | for (std::size_t i = 0; i < std::size(addresses); ++i) { | 104 | for (std::size_t i = 0; i < std::size(addresses); ++i) { |
| 104 | addresses[i] = shaders[i] ? shaders[i]->GetGpuAddr() : 0; | 105 | addresses[i] = shaders[i] ? shaders[i]->GetGpuAddr() : 0; |
| @@ -117,6 +118,17 @@ template <typename Engine, typename Entry> | |||
| 117 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 118 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, |
| 118 | std::size_t stage, std::size_t index = 0) { | 119 | std::size_t stage, std::size_t index = 0) { |
| 119 | const auto stage_type = static_cast<Tegra::Engines::ShaderType>(stage); | 120 | const auto stage_type = static_cast<Tegra::Engines::ShaderType>(stage); |
| 121 | if constexpr (std::is_same_v<Entry, SamplerEntry>) { | ||
| 122 | if (entry.is_separated) { | ||
| 123 | const u32 buffer_1 = entry.buffer; | ||
| 124 | const u32 buffer_2 = entry.secondary_buffer; | ||
| 125 | const u32 offset_1 = entry.offset; | ||
| 126 | const u32 offset_2 = entry.secondary_offset; | ||
| 127 | const u32 handle_1 = engine.AccessConstBuffer32(stage_type, buffer_1, offset_1); | ||
| 128 | const u32 handle_2 = engine.AccessConstBuffer32(stage_type, buffer_2, offset_2); | ||
| 129 | return engine.GetTextureInfo(handle_1 | handle_2); | ||
| 130 | } | ||
| 131 | } | ||
| 120 | if (entry.is_bindless) { | 132 | if (entry.is_bindless) { |
| 121 | const auto tex_handle = engine.AccessConstBuffer32(stage_type, entry.buffer, entry.offset); | 133 | const auto tex_handle = engine.AccessConstBuffer32(stage_type, entry.buffer, entry.offset); |
| 122 | return engine.GetTextureInfo(tex_handle); | 134 | return engine.GetTextureInfo(tex_handle); |
| @@ -131,13 +143,65 @@ Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry | |||
| 131 | } | 143 | } |
| 132 | } | 144 | } |
| 133 | 145 | ||
| 146 | /// @brief Determine if an attachment to be updated has to preserve contents | ||
| 147 | /// @param is_clear True when a clear is being executed | ||
| 148 | /// @param regs 3D registers | ||
| 149 | /// @return True when the contents have to be preserved | ||
| 150 | bool HasToPreserveColorContents(bool is_clear, const Maxwell& regs) { | ||
| 151 | if (!is_clear) { | ||
| 152 | return true; | ||
| 153 | } | ||
| 154 | // First we have to make sure all clear masks are enabled. | ||
| 155 | if (!regs.clear_buffers.R || !regs.clear_buffers.G || !regs.clear_buffers.B || | ||
| 156 | !regs.clear_buffers.A) { | ||
| 157 | return true; | ||
| 158 | } | ||
| 159 | // If scissors are disabled, the whole screen is cleared | ||
| 160 | if (!regs.clear_flags.scissor) { | ||
| 161 | return false; | ||
| 162 | } | ||
| 163 | // Then we have to confirm scissor testing clears the whole image | ||
| 164 | const std::size_t index = regs.clear_buffers.RT; | ||
| 165 | const auto& scissor = regs.scissor_test[0]; | ||
| 166 | return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.rt[index].width || | ||
| 167 | scissor.max_y < regs.rt[index].height; | ||
| 168 | } | ||
| 169 | |||
| 170 | /// @brief Determine if an attachment to be updated has to preserve contents | ||
| 171 | /// @param is_clear True when a clear is being executed | ||
| 172 | /// @param regs 3D registers | ||
| 173 | /// @return True when the contents have to be preserved | ||
| 174 | bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) { | ||
| 175 | // If we are not clearing, the contents have to be preserved | ||
| 176 | if (!is_clear) { | ||
| 177 | return true; | ||
| 178 | } | ||
| 179 | // For depth stencil clears we only have to confirm scissor test covers the whole image | ||
| 180 | if (!regs.clear_flags.scissor) { | ||
| 181 | return false; | ||
| 182 | } | ||
| 183 | // Make sure the clear cover the whole image | ||
| 184 | const auto& scissor = regs.scissor_test[0]; | ||
| 185 | return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.zeta_width || | ||
| 186 | scissor.max_y < regs.zeta_height; | ||
| 187 | } | ||
| 188 | |||
| 189 | template <std::size_t N> | ||
| 190 | std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) { | ||
| 191 | std::array<VkDeviceSize, N> expanded; | ||
| 192 | std::copy(strides.begin(), strides.end(), expanded.begin()); | ||
| 193 | return expanded; | ||
| 194 | } | ||
| 195 | |||
| 134 | } // Anonymous namespace | 196 | } // Anonymous namespace |
| 135 | 197 | ||
| 136 | class BufferBindings final { | 198 | class BufferBindings final { |
| 137 | public: | 199 | public: |
| 138 | void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) { | 200 | void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) { |
| 139 | vertex.buffers[vertex.num_buffers] = buffer; | 201 | vertex.buffers[vertex.num_buffers] = buffer; |
| 140 | vertex.offsets[vertex.num_buffers] = offset; | 202 | vertex.offsets[vertex.num_buffers] = offset; |
| 203 | vertex.sizes[vertex.num_buffers] = size; | ||
| 204 | vertex.strides[vertex.num_buffers] = static_cast<u16>(stride); | ||
| 141 | ++vertex.num_buffers; | 205 | ++vertex.num_buffers; |
| 142 | } | 206 | } |
| 143 | 207 | ||
| @@ -147,76 +211,76 @@ public: | |||
| 147 | index.type = type; | 211 | index.type = type; |
| 148 | } | 212 | } |
| 149 | 213 | ||
| 150 | void Bind(VKScheduler& scheduler) const { | 214 | void Bind(const VKDevice& device, VKScheduler& scheduler) const { |
| 151 | // Use this large switch case to avoid dispatching more memory in the record lambda than | 215 | // Use this large switch case to avoid dispatching more memory in the record lambda than |
| 152 | // what we need. It looks horrible, but it's the best we can do on standard C++. | 216 | // what we need. It looks horrible, but it's the best we can do on standard C++. |
| 153 | switch (vertex.num_buffers) { | 217 | switch (vertex.num_buffers) { |
| 154 | case 0: | 218 | case 0: |
| 155 | return BindStatic<0>(scheduler); | 219 | return BindStatic<0>(device, scheduler); |
| 156 | case 1: | 220 | case 1: |
| 157 | return BindStatic<1>(scheduler); | 221 | return BindStatic<1>(device, scheduler); |
| 158 | case 2: | 222 | case 2: |
| 159 | return BindStatic<2>(scheduler); | 223 | return BindStatic<2>(device, scheduler); |
| 160 | case 3: | 224 | case 3: |
| 161 | return BindStatic<3>(scheduler); | 225 | return BindStatic<3>(device, scheduler); |
| 162 | case 4: | 226 | case 4: |
| 163 | return BindStatic<4>(scheduler); | 227 | return BindStatic<4>(device, scheduler); |
| 164 | case 5: | 228 | case 5: |
| 165 | return BindStatic<5>(scheduler); | 229 | return BindStatic<5>(device, scheduler); |
| 166 | case 6: | 230 | case 6: |
| 167 | return BindStatic<6>(scheduler); | 231 | return BindStatic<6>(device, scheduler); |
| 168 | case 7: | 232 | case 7: |
| 169 | return BindStatic<7>(scheduler); | 233 | return BindStatic<7>(device, scheduler); |
| 170 | case 8: | 234 | case 8: |
| 171 | return BindStatic<8>(scheduler); | 235 | return BindStatic<8>(device, scheduler); |
| 172 | case 9: | 236 | case 9: |
| 173 | return BindStatic<9>(scheduler); | 237 | return BindStatic<9>(device, scheduler); |
| 174 | case 10: | 238 | case 10: |
| 175 | return BindStatic<10>(scheduler); | 239 | return BindStatic<10>(device, scheduler); |
| 176 | case 11: | 240 | case 11: |
| 177 | return BindStatic<11>(scheduler); | 241 | return BindStatic<11>(device, scheduler); |
| 178 | case 12: | 242 | case 12: |
| 179 | return BindStatic<12>(scheduler); | 243 | return BindStatic<12>(device, scheduler); |
| 180 | case 13: | 244 | case 13: |
| 181 | return BindStatic<13>(scheduler); | 245 | return BindStatic<13>(device, scheduler); |
| 182 | case 14: | 246 | case 14: |
| 183 | return BindStatic<14>(scheduler); | 247 | return BindStatic<14>(device, scheduler); |
| 184 | case 15: | 248 | case 15: |
| 185 | return BindStatic<15>(scheduler); | 249 | return BindStatic<15>(device, scheduler); |
| 186 | case 16: | 250 | case 16: |
| 187 | return BindStatic<16>(scheduler); | 251 | return BindStatic<16>(device, scheduler); |
| 188 | case 17: | 252 | case 17: |
| 189 | return BindStatic<17>(scheduler); | 253 | return BindStatic<17>(device, scheduler); |
| 190 | case 18: | 254 | case 18: |
| 191 | return BindStatic<18>(scheduler); | 255 | return BindStatic<18>(device, scheduler); |
| 192 | case 19: | 256 | case 19: |
| 193 | return BindStatic<19>(scheduler); | 257 | return BindStatic<19>(device, scheduler); |
| 194 | case 20: | 258 | case 20: |
| 195 | return BindStatic<20>(scheduler); | 259 | return BindStatic<20>(device, scheduler); |
| 196 | case 21: | 260 | case 21: |
| 197 | return BindStatic<21>(scheduler); | 261 | return BindStatic<21>(device, scheduler); |
| 198 | case 22: | 262 | case 22: |
| 199 | return BindStatic<22>(scheduler); | 263 | return BindStatic<22>(device, scheduler); |
| 200 | case 23: | 264 | case 23: |
| 201 | return BindStatic<23>(scheduler); | 265 | return BindStatic<23>(device, scheduler); |
| 202 | case 24: | 266 | case 24: |
| 203 | return BindStatic<24>(scheduler); | 267 | return BindStatic<24>(device, scheduler); |
| 204 | case 25: | 268 | case 25: |
| 205 | return BindStatic<25>(scheduler); | 269 | return BindStatic<25>(device, scheduler); |
| 206 | case 26: | 270 | case 26: |
| 207 | return BindStatic<26>(scheduler); | 271 | return BindStatic<26>(device, scheduler); |
| 208 | case 27: | 272 | case 27: |
| 209 | return BindStatic<27>(scheduler); | 273 | return BindStatic<27>(device, scheduler); |
| 210 | case 28: | 274 | case 28: |
| 211 | return BindStatic<28>(scheduler); | 275 | return BindStatic<28>(device, scheduler); |
| 212 | case 29: | 276 | case 29: |
| 213 | return BindStatic<29>(scheduler); | 277 | return BindStatic<29>(device, scheduler); |
| 214 | case 30: | 278 | case 30: |
| 215 | return BindStatic<30>(scheduler); | 279 | return BindStatic<30>(device, scheduler); |
| 216 | case 31: | 280 | case 31: |
| 217 | return BindStatic<31>(scheduler); | 281 | return BindStatic<31>(device, scheduler); |
| 218 | case 32: | 282 | case 32: |
| 219 | return BindStatic<32>(scheduler); | 283 | return BindStatic<32>(device, scheduler); |
| 220 | } | 284 | } |
| 221 | UNREACHABLE(); | 285 | UNREACHABLE(); |
| 222 | } | 286 | } |
| @@ -227,6 +291,8 @@ private: | |||
| 227 | std::size_t num_buffers = 0; | 291 | std::size_t num_buffers = 0; |
| 228 | std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; | 292 | std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; |
| 229 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; | 293 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; |
| 294 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes; | ||
| 295 | std::array<u16, Maxwell::NumVertexArrays> strides; | ||
| 230 | } vertex; | 296 | } vertex; |
| 231 | 297 | ||
| 232 | struct { | 298 | struct { |
| @@ -236,15 +302,23 @@ private: | |||
| 236 | } index; | 302 | } index; |
| 237 | 303 | ||
| 238 | template <std::size_t N> | 304 | template <std::size_t N> |
| 239 | void BindStatic(VKScheduler& scheduler) const { | 305 | void BindStatic(const VKDevice& device, VKScheduler& scheduler) const { |
| 240 | if (index.buffer) { | 306 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 241 | BindStatic<N, true>(scheduler); | 307 | if (index.buffer) { |
| 308 | BindStatic<N, true, true>(scheduler); | ||
| 309 | } else { | ||
| 310 | BindStatic<N, false, true>(scheduler); | ||
| 311 | } | ||
| 242 | } else { | 312 | } else { |
| 243 | BindStatic<N, false>(scheduler); | 313 | if (index.buffer) { |
| 314 | BindStatic<N, true, false>(scheduler); | ||
| 315 | } else { | ||
| 316 | BindStatic<N, false, false>(scheduler); | ||
| 317 | } | ||
| 244 | } | 318 | } |
| 245 | } | 319 | } |
| 246 | 320 | ||
| 247 | template <std::size_t N, bool is_indexed> | 321 | template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state> |
| 248 | void BindStatic(VKScheduler& scheduler) const { | 322 | void BindStatic(VKScheduler& scheduler) const { |
| 249 | static_assert(N <= Maxwell::NumVertexArrays); | 323 | static_assert(N <= Maxwell::NumVertexArrays); |
| 250 | if constexpr (N == 0) { | 324 | if constexpr (N == 0) { |
| @@ -256,6 +330,31 @@ private: | |||
| 256 | std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); | 330 | std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); |
| 257 | std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); | 331 | std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); |
| 258 | 332 | ||
| 333 | if constexpr (has_extended_dynamic_state) { | ||
| 334 | // With extended dynamic states we can specify the length and stride of a vertex buffer | ||
| 335 | // std::array<VkDeviceSize, N> sizes; | ||
| 336 | std::array<u16, N> strides; | ||
| 337 | // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin()); | ||
| 338 | std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin()); | ||
| 339 | |||
| 340 | if constexpr (is_indexed) { | ||
| 341 | scheduler.Record( | ||
| 342 | [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) { | ||
| 343 | cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type); | ||
| 344 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | ||
| 345 | offsets.data(), nullptr, | ||
| 346 | ExpandStrides(strides).data()); | ||
| 347 | }); | ||
| 348 | } else { | ||
| 349 | scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) { | ||
| 350 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | ||
| 351 | offsets.data(), nullptr, | ||
| 352 | ExpandStrides(strides).data()); | ||
| 353 | }); | ||
| 354 | } | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | |||
| 259 | if constexpr (is_indexed) { | 358 | if constexpr (is_indexed) { |
| 260 | // Indexed draw | 359 | // Indexed draw |
| 261 | scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { | 360 | scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { |
| @@ -314,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 314 | 413 | ||
| 315 | const auto& gpu = system.GPU().Maxwell3D(); | 414 | const auto& gpu = system.GPU().Maxwell3D(); |
| 316 | GraphicsPipelineCacheKey key; | 415 | GraphicsPipelineCacheKey key; |
| 317 | key.fixed_state.Fill(gpu.regs); | 416 | key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported()); |
| 318 | 417 | ||
| 319 | buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); | 418 | buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); |
| 320 | 419 | ||
| @@ -332,7 +431,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 332 | 431 | ||
| 333 | buffer_cache.Unmap(); | 432 | buffer_cache.Unmap(); |
| 334 | 433 | ||
| 335 | const Texceptions texceptions = UpdateAttachments(); | 434 | const Texceptions texceptions = UpdateAttachments(false); |
| 336 | SetupImageTransitions(texceptions, color_attachments, zeta_attachment); | 435 | SetupImageTransitions(texceptions, color_attachments, zeta_attachment); |
| 337 | 436 | ||
| 338 | key.renderpass_params = GetRenderPassParams(texceptions); | 437 | key.renderpass_params = GetRenderPassParams(texceptions); |
| @@ -347,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 347 | 446 | ||
| 348 | UpdateDynamicStates(); | 447 | UpdateDynamicStates(); |
| 349 | 448 | ||
| 350 | buffer_bindings.Bind(scheduler); | 449 | buffer_bindings.Bind(device, scheduler); |
| 351 | 450 | ||
| 352 | BeginTransformFeedback(); | 451 | BeginTransformFeedback(); |
| 353 | 452 | ||
| @@ -388,7 +487,7 @@ void RasterizerVulkan::Clear() { | |||
| 388 | return; | 487 | return; |
| 389 | } | 488 | } |
| 390 | 489 | ||
| 391 | [[maybe_unused]] const auto texceptions = UpdateAttachments(); | 490 | [[maybe_unused]] const auto texceptions = UpdateAttachments(true); |
| 392 | DEBUG_ASSERT(texceptions.none()); | 491 | DEBUG_ASSERT(texceptions.none()); |
| 393 | SetupImageTransitions(0, color_attachments, zeta_attachment); | 492 | SetupImageTransitions(0, color_attachments, zeta_attachment); |
| 394 | 493 | ||
| @@ -468,8 +567,9 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) { | |||
| 468 | const auto& entries = pipeline.GetEntries(); | 567 | const auto& entries = pipeline.GetEntries(); |
| 469 | SetupComputeConstBuffers(entries); | 568 | SetupComputeConstBuffers(entries); |
| 470 | SetupComputeGlobalBuffers(entries); | 569 | SetupComputeGlobalBuffers(entries); |
| 471 | SetupComputeTexelBuffers(entries); | 570 | SetupComputeUniformTexels(entries); |
| 472 | SetupComputeTextures(entries); | 571 | SetupComputeTextures(entries); |
| 572 | SetupComputeStorageTexels(entries); | ||
| 473 | SetupComputeImages(entries); | 573 | SetupComputeImages(entries); |
| 474 | 574 | ||
| 475 | buffer_cache.Unmap(); | 575 | buffer_cache.Unmap(); |
| @@ -664,9 +764,12 @@ void RasterizerVulkan::FlushWork() { | |||
| 664 | draw_counter = 0; | 764 | draw_counter = 0; |
| 665 | } | 765 | } |
| 666 | 766 | ||
| 667 | RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | 767 | RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments(bool is_clear) { |
| 668 | MICROPROFILE_SCOPE(Vulkan_RenderTargets); | 768 | MICROPROFILE_SCOPE(Vulkan_RenderTargets); |
| 669 | auto& dirty = system.GPU().Maxwell3D().dirty.flags; | 769 | auto& maxwell3d = system.GPU().Maxwell3D(); |
| 770 | auto& dirty = maxwell3d.dirty.flags; | ||
| 771 | auto& regs = maxwell3d.regs; | ||
| 772 | |||
| 670 | const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets]; | 773 | const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets]; |
| 671 | dirty[VideoCommon::Dirty::RenderTargets] = false; | 774 | dirty[VideoCommon::Dirty::RenderTargets] = false; |
| 672 | 775 | ||
| @@ -675,7 +778,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 675 | Texceptions texceptions; | 778 | Texceptions texceptions; |
| 676 | for (std::size_t rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { | 779 | for (std::size_t rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { |
| 677 | if (update_rendertargets) { | 780 | if (update_rendertargets) { |
| 678 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); | 781 | const bool preserve_contents = HasToPreserveColorContents(is_clear, regs); |
| 782 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, preserve_contents); | ||
| 679 | } | 783 | } |
| 680 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { | 784 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { |
| 681 | texceptions[rt] = true; | 785 | texceptions[rt] = true; |
| @@ -683,7 +787,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 683 | } | 787 | } |
| 684 | 788 | ||
| 685 | if (update_rendertargets) { | 789 | if (update_rendertargets) { |
| 686 | zeta_attachment = texture_cache.GetDepthBufferSurface(true); | 790 | const bool preserve_contents = HasToPreserveDepthContents(is_clear, regs); |
| 791 | zeta_attachment = texture_cache.GetDepthBufferSurface(preserve_contents); | ||
| 687 | } | 792 | } |
| 688 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { | 793 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { |
| 689 | texceptions[ZETA_TEXCEPTION_INDEX] = true; | 794 | texceptions[ZETA_TEXCEPTION_INDEX] = true; |
| @@ -715,7 +820,7 @@ std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers( | |||
| 715 | if (!view) { | 820 | if (!view) { |
| 716 | return false; | 821 | return false; |
| 717 | } | 822 | } |
| 718 | key.views.push_back(view->GetHandle()); | 823 | key.views.push_back(view->GetAttachment()); |
| 719 | key.width = std::min(key.width, view->GetWidth()); | 824 | key.width = std::min(key.width, view->GetWidth()); |
| 720 | key.height = std::min(key.height, view->GetHeight()); | 825 | key.height = std::min(key.height, view->GetHeight()); |
| 721 | key.layers = std::min(key.layers, view->GetNumLayers()); | 826 | key.layers = std::min(key.layers, view->GetNumLayers()); |
| @@ -761,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt | |||
| 761 | const auto& gpu = system.GPU().Maxwell3D(); | 866 | const auto& gpu = system.GPU().Maxwell3D(); |
| 762 | const auto& regs = gpu.regs; | 867 | const auto& regs = gpu.regs; |
| 763 | 868 | ||
| 764 | SetupVertexArrays(fixed_state.vertex_input, buffer_bindings); | 869 | SetupVertexArrays(buffer_bindings); |
| 765 | 870 | ||
| 766 | const u32 base_instance = regs.vb_base_instance; | 871 | const u32 base_instance = regs.vb_base_instance; |
| 767 | const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; | 872 | const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; |
| @@ -775,20 +880,21 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt | |||
| 775 | } | 880 | } |
| 776 | 881 | ||
| 777 | void RasterizerVulkan::SetupShaderDescriptors( | 882 | void RasterizerVulkan::SetupShaderDescriptors( |
| 778 | const std::array<Shader, Maxwell::MaxShaderProgram>& shaders) { | 883 | const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders) { |
| 779 | texture_cache.GuardSamplers(true); | 884 | texture_cache.GuardSamplers(true); |
| 780 | 885 | ||
| 781 | for (std::size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) { | 886 | for (std::size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) { |
| 782 | // Skip VertexA stage | 887 | // Skip VertexA stage |
| 783 | const auto& shader = shaders[stage + 1]; | 888 | Shader* const shader = shaders[stage + 1]; |
| 784 | if (!shader) { | 889 | if (!shader) { |
| 785 | continue; | 890 | continue; |
| 786 | } | 891 | } |
| 787 | const auto& entries = shader->GetEntries(); | 892 | const auto& entries = shader->GetEntries(); |
| 788 | SetupGraphicsConstBuffers(entries, stage); | 893 | SetupGraphicsConstBuffers(entries, stage); |
| 789 | SetupGraphicsGlobalBuffers(entries, stage); | 894 | SetupGraphicsGlobalBuffers(entries, stage); |
| 790 | SetupGraphicsTexelBuffers(entries, stage); | 895 | SetupGraphicsUniformTexels(entries, stage); |
| 791 | SetupGraphicsTextures(entries, stage); | 896 | SetupGraphicsTextures(entries, stage); |
| 897 | SetupGraphicsStorageTexels(entries, stage); | ||
| 792 | SetupGraphicsImages(entries, stage); | 898 | SetupGraphicsImages(entries, stage); |
| 793 | } | 899 | } |
| 794 | texture_cache.GuardSamplers(false); | 900 | texture_cache.GuardSamplers(false); |
| @@ -831,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() { | |||
| 831 | UpdateBlendConstants(regs); | 937 | UpdateBlendConstants(regs); |
| 832 | UpdateDepthBounds(regs); | 938 | UpdateDepthBounds(regs); |
| 833 | UpdateStencilFaces(regs); | 939 | UpdateStencilFaces(regs); |
| 940 | if (device.IsExtExtendedDynamicStateSupported()) { | ||
| 941 | UpdateCullMode(regs); | ||
| 942 | UpdateDepthBoundsTestEnable(regs); | ||
| 943 | UpdateDepthTestEnable(regs); | ||
| 944 | UpdateDepthWriteEnable(regs); | ||
| 945 | UpdateDepthCompareOp(regs); | ||
| 946 | UpdateFrontFace(regs); | ||
| 947 | UpdatePrimitiveTopology(regs); | ||
| 948 | UpdateStencilOp(regs); | ||
| 949 | UpdateStencilTestEnable(regs); | ||
| 950 | } | ||
| 834 | } | 951 | } |
| 835 | 952 | ||
| 836 | void RasterizerVulkan::BeginTransformFeedback() { | 953 | void RasterizerVulkan::BeginTransformFeedback() { |
| @@ -838,6 +955,10 @@ void RasterizerVulkan::BeginTransformFeedback() { | |||
| 838 | if (regs.tfb_enabled == 0) { | 955 | if (regs.tfb_enabled == 0) { |
| 839 | return; | 956 | return; |
| 840 | } | 957 | } |
| 958 | if (!device.IsExtTransformFeedbackSupported()) { | ||
| 959 | LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); | ||
| 960 | return; | ||
| 961 | } | ||
| 841 | 962 | ||
| 842 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || | 963 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || |
| 843 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || | 964 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || |
| @@ -852,10 +973,10 @@ void RasterizerVulkan::BeginTransformFeedback() { | |||
| 852 | UNIMPLEMENTED_IF(binding.buffer_offset != 0); | 973 | UNIMPLEMENTED_IF(binding.buffer_offset != 0); |
| 853 | 974 | ||
| 854 | const GPUVAddr gpu_addr = binding.Address(); | 975 | const GPUVAddr gpu_addr = binding.Address(); |
| 855 | const std::size_t size = binding.buffer_size; | 976 | const VkDeviceSize size = static_cast<VkDeviceSize>(binding.buffer_size); |
| 856 | const auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); | 977 | const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true); |
| 857 | 978 | ||
| 858 | scheduler.Record([buffer = buffer, offset = offset, size](vk::CommandBuffer cmdbuf) { | 979 | scheduler.Record([buffer = info.handle, offset = info.offset, size](vk::CommandBuffer cmdbuf) { |
| 859 | cmdbuf.BindTransformFeedbackBuffersEXT(0, 1, &buffer, &offset, &size); | 980 | cmdbuf.BindTransformFeedbackBuffersEXT(0, 1, &buffer, &offset, &size); |
| 860 | cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); | 981 | cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); |
| 861 | }); | 982 | }); |
| @@ -866,50 +987,33 @@ void RasterizerVulkan::EndTransformFeedback() { | |||
| 866 | if (regs.tfb_enabled == 0) { | 987 | if (regs.tfb_enabled == 0) { |
| 867 | return; | 988 | return; |
| 868 | } | 989 | } |
| 990 | if (!device.IsExtTransformFeedbackSupported()) { | ||
| 991 | return; | ||
| 992 | } | ||
| 869 | 993 | ||
| 870 | scheduler.Record( | 994 | scheduler.Record( |
| 871 | [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); | 995 | [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); |
| 872 | } | 996 | } |
| 873 | 997 | ||
| 874 | void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 998 | void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) { |
| 875 | BufferBindings& buffer_bindings) { | ||
| 876 | const auto& regs = system.GPU().Maxwell3D().regs; | 999 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 877 | 1000 | ||
| 878 | for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { | ||
| 879 | const auto& attrib = regs.vertex_attrib_format[index]; | ||
| 880 | if (!attrib.IsValid()) { | ||
| 881 | vertex_input.SetAttribute(index, false, 0, 0, {}, {}); | ||
| 882 | continue; | ||
| 883 | } | ||
| 884 | |||
| 885 | [[maybe_unused]] const auto& buffer = regs.vertex_array[attrib.buffer]; | ||
| 886 | ASSERT(buffer.IsEnabled()); | ||
| 887 | |||
| 888 | vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(), | ||
| 889 | attrib.size.Value()); | ||
| 890 | } | ||
| 891 | |||
| 892 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 1001 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 893 | const auto& vertex_array = regs.vertex_array[index]; | 1002 | const auto& vertex_array = regs.vertex_array[index]; |
| 894 | if (!vertex_array.IsEnabled()) { | 1003 | if (!vertex_array.IsEnabled()) { |
| 895 | vertex_input.SetBinding(index, false, 0, 0); | ||
| 896 | continue; | 1004 | continue; |
| 897 | } | 1005 | } |
| 898 | vertex_input.SetBinding( | ||
| 899 | index, true, vertex_array.stride, | ||
| 900 | regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0); | ||
| 901 | |||
| 902 | const GPUVAddr start{vertex_array.StartAddress()}; | 1006 | const GPUVAddr start{vertex_array.StartAddress()}; |
| 903 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; | 1007 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; |
| 904 | 1008 | ||
| 905 | ASSERT(end >= start); | 1009 | ASSERT(end >= start); |
| 906 | const std::size_t size{end - start}; | 1010 | const std::size_t size = end - start; |
| 907 | if (size == 0) { | 1011 | if (size == 0) { |
| 908 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); | 1012 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0); |
| 909 | continue; | 1013 | continue; |
| 910 | } | 1014 | } |
| 911 | const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); | 1015 | const auto info = buffer_cache.UploadMemory(start, size); |
| 912 | buffer_bindings.AddVertexBinding(buffer, offset); | 1016 | buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride); |
| 913 | } | 1017 | } |
| 914 | } | 1018 | } |
| 915 | 1019 | ||
| @@ -931,7 +1035,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar | |||
| 931 | break; | 1035 | break; |
| 932 | } | 1036 | } |
| 933 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); | 1037 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); |
| 934 | auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); | 1038 | const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); |
| 1039 | VkBuffer buffer = info.handle; | ||
| 1040 | u64 offset = info.offset; | ||
| 935 | std::tie(buffer, offset) = quad_indexed_pass.Assemble( | 1041 | std::tie(buffer, offset) = quad_indexed_pass.Assemble( |
| 936 | regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset); | 1042 | regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset); |
| 937 | 1043 | ||
| @@ -945,7 +1051,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar | |||
| 945 | break; | 1051 | break; |
| 946 | } | 1052 | } |
| 947 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); | 1053 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); |
| 948 | auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); | 1054 | const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); |
| 1055 | VkBuffer buffer = info.handle; | ||
| 1056 | u64 offset = info.offset; | ||
| 949 | 1057 | ||
| 950 | auto format = regs.index_array.format; | 1058 | auto format = regs.index_array.format; |
| 951 | const bool is_uint8 = format == Maxwell::IndexFormat::UnsignedByte; | 1059 | const bool is_uint8 = format == Maxwell::IndexFormat::UnsignedByte; |
| @@ -980,12 +1088,12 @@ void RasterizerVulkan::SetupGraphicsGlobalBuffers(const ShaderEntries& entries, | |||
| 980 | } | 1088 | } |
| 981 | } | 1089 | } |
| 982 | 1090 | ||
| 983 | void RasterizerVulkan::SetupGraphicsTexelBuffers(const ShaderEntries& entries, std::size_t stage) { | 1091 | void RasterizerVulkan::SetupGraphicsUniformTexels(const ShaderEntries& entries, std::size_t stage) { |
| 984 | MICROPROFILE_SCOPE(Vulkan_Textures); | 1092 | MICROPROFILE_SCOPE(Vulkan_Textures); |
| 985 | const auto& gpu = system.GPU().Maxwell3D(); | 1093 | const auto& gpu = system.GPU().Maxwell3D(); |
| 986 | for (const auto& entry : entries.texel_buffers) { | 1094 | for (const auto& entry : entries.uniform_texels) { |
| 987 | const auto image = GetTextureInfo(gpu, entry, stage).tic; | 1095 | const auto image = GetTextureInfo(gpu, entry, stage).tic; |
| 988 | SetupTexelBuffer(image, entry); | 1096 | SetupUniformTexels(image, entry); |
| 989 | } | 1097 | } |
| 990 | } | 1098 | } |
| 991 | 1099 | ||
| @@ -1000,6 +1108,15 @@ void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std:: | |||
| 1000 | } | 1108 | } |
| 1001 | } | 1109 | } |
| 1002 | 1110 | ||
| 1111 | void RasterizerVulkan::SetupGraphicsStorageTexels(const ShaderEntries& entries, std::size_t stage) { | ||
| 1112 | MICROPROFILE_SCOPE(Vulkan_Textures); | ||
| 1113 | const auto& gpu = system.GPU().Maxwell3D(); | ||
| 1114 | for (const auto& entry : entries.storage_texels) { | ||
| 1115 | const auto image = GetTextureInfo(gpu, entry, stage).tic; | ||
| 1116 | SetupStorageTexel(image, entry); | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | |||
| 1003 | void RasterizerVulkan::SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage) { | 1120 | void RasterizerVulkan::SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage) { |
| 1004 | MICROPROFILE_SCOPE(Vulkan_Images); | 1121 | MICROPROFILE_SCOPE(Vulkan_Images); |
| 1005 | const auto& gpu = system.GPU().Maxwell3D(); | 1122 | const auto& gpu = system.GPU().Maxwell3D(); |
| @@ -1032,12 +1149,12 @@ void RasterizerVulkan::SetupComputeGlobalBuffers(const ShaderEntries& entries) { | |||
| 1032 | } | 1149 | } |
| 1033 | } | 1150 | } |
| 1034 | 1151 | ||
| 1035 | void RasterizerVulkan::SetupComputeTexelBuffers(const ShaderEntries& entries) { | 1152 | void RasterizerVulkan::SetupComputeUniformTexels(const ShaderEntries& entries) { |
| 1036 | MICROPROFILE_SCOPE(Vulkan_Textures); | 1153 | MICROPROFILE_SCOPE(Vulkan_Textures); |
| 1037 | const auto& gpu = system.GPU().KeplerCompute(); | 1154 | const auto& gpu = system.GPU().KeplerCompute(); |
| 1038 | for (const auto& entry : entries.texel_buffers) { | 1155 | for (const auto& entry : entries.uniform_texels) { |
| 1039 | const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic; | 1156 | const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic; |
| 1040 | SetupTexelBuffer(image, entry); | 1157 | SetupUniformTexels(image, entry); |
| 1041 | } | 1158 | } |
| 1042 | } | 1159 | } |
| 1043 | 1160 | ||
| @@ -1052,6 +1169,15 @@ void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) { | |||
| 1052 | } | 1169 | } |
| 1053 | } | 1170 | } |
| 1054 | 1171 | ||
| 1172 | void RasterizerVulkan::SetupComputeStorageTexels(const ShaderEntries& entries) { | ||
| 1173 | MICROPROFILE_SCOPE(Vulkan_Textures); | ||
| 1174 | const auto& gpu = system.GPU().KeplerCompute(); | ||
| 1175 | for (const auto& entry : entries.storage_texels) { | ||
| 1176 | const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic; | ||
| 1177 | SetupStorageTexel(image, entry); | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | |||
| 1055 | void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) { | 1181 | void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) { |
| 1056 | MICROPROFILE_SCOPE(Vulkan_Images); | 1182 | MICROPROFILE_SCOPE(Vulkan_Images); |
| 1057 | const auto& gpu = system.GPU().KeplerCompute(); | 1183 | const auto& gpu = system.GPU().KeplerCompute(); |
| @@ -1074,10 +1200,9 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry, | |||
| 1074 | Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float)); | 1200 | Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float)); |
| 1075 | ASSERT(size <= MaxConstbufferSize); | 1201 | ASSERT(size <= MaxConstbufferSize); |
| 1076 | 1202 | ||
| 1077 | const auto [buffer_handle, offset] = | 1203 | const auto info = |
| 1078 | buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); | 1204 | buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); |
| 1079 | 1205 | update_descriptor_queue.AddBuffer(info.handle, info.offset, size); | |
| 1080 | update_descriptor_queue.AddBuffer(buffer_handle, offset, size); | ||
| 1081 | } | 1206 | } |
| 1082 | 1207 | ||
| 1083 | void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) { | 1208 | void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) { |
| @@ -1091,18 +1216,18 @@ void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAdd | |||
| 1091 | // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the | 1216 | // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the |
| 1092 | // default buffer. | 1217 | // default buffer. |
| 1093 | static constexpr std::size_t dummy_size = 4; | 1218 | static constexpr std::size_t dummy_size = 4; |
| 1094 | const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size); | 1219 | const auto info = buffer_cache.GetEmptyBuffer(dummy_size); |
| 1095 | update_descriptor_queue.AddBuffer(buffer, 0, dummy_size); | 1220 | update_descriptor_queue.AddBuffer(info.handle, info.offset, dummy_size); |
| 1096 | return; | 1221 | return; |
| 1097 | } | 1222 | } |
| 1098 | 1223 | ||
| 1099 | const auto [buffer, offset] = buffer_cache.UploadMemory( | 1224 | const auto info = buffer_cache.UploadMemory( |
| 1100 | actual_addr, size, device.GetStorageBufferAlignment(), entry.IsWritten()); | 1225 | actual_addr, size, device.GetStorageBufferAlignment(), entry.IsWritten()); |
| 1101 | update_descriptor_queue.AddBuffer(buffer, offset, size); | 1226 | update_descriptor_queue.AddBuffer(info.handle, info.offset, size); |
| 1102 | } | 1227 | } |
| 1103 | 1228 | ||
| 1104 | void RasterizerVulkan::SetupTexelBuffer(const Tegra::Texture::TICEntry& tic, | 1229 | void RasterizerVulkan::SetupUniformTexels(const Tegra::Texture::TICEntry& tic, |
| 1105 | const TexelBufferEntry& entry) { | 1230 | const UniformTexelEntry& entry) { |
| 1106 | const auto view = texture_cache.GetTextureSurface(tic, entry); | 1231 | const auto view = texture_cache.GetTextureSurface(tic, entry); |
| 1107 | ASSERT(view->IsBufferView()); | 1232 | ASSERT(view->IsBufferView()); |
| 1108 | 1233 | ||
| @@ -1114,16 +1239,24 @@ void RasterizerVulkan::SetupTexture(const Tegra::Texture::FullTextureInfo& textu | |||
| 1114 | auto view = texture_cache.GetTextureSurface(texture.tic, entry); | 1239 | auto view = texture_cache.GetTextureSurface(texture.tic, entry); |
| 1115 | ASSERT(!view->IsBufferView()); | 1240 | ASSERT(!view->IsBufferView()); |
| 1116 | 1241 | ||
| 1117 | const auto image_view = view->GetHandle(texture.tic.x_source, texture.tic.y_source, | 1242 | const VkImageView image_view = view->GetImageView(texture.tic.x_source, texture.tic.y_source, |
| 1118 | texture.tic.z_source, texture.tic.w_source); | 1243 | texture.tic.z_source, texture.tic.w_source); |
| 1119 | const auto sampler = sampler_cache.GetSampler(texture.tsc); | 1244 | const auto sampler = sampler_cache.GetSampler(texture.tsc); |
| 1120 | update_descriptor_queue.AddSampledImage(sampler, image_view); | 1245 | update_descriptor_queue.AddSampledImage(sampler, image_view); |
| 1121 | 1246 | ||
| 1122 | const auto image_layout = update_descriptor_queue.GetLastImageLayout(); | 1247 | VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout(); |
| 1123 | *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | 1248 | *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| 1124 | sampled_views.push_back(ImageView{std::move(view), image_layout}); | 1249 | sampled_views.push_back(ImageView{std::move(view), image_layout}); |
| 1125 | } | 1250 | } |
| 1126 | 1251 | ||
| 1252 | void RasterizerVulkan::SetupStorageTexel(const Tegra::Texture::TICEntry& tic, | ||
| 1253 | const StorageTexelEntry& entry) { | ||
| 1254 | const auto view = texture_cache.GetImageSurface(tic, entry); | ||
| 1255 | ASSERT(view->IsBufferView()); | ||
| 1256 | |||
| 1257 | update_descriptor_queue.AddTexelBuffer(view->GetBufferView()); | ||
| 1258 | } | ||
| 1259 | |||
| 1127 | void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry) { | 1260 | void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry) { |
| 1128 | auto view = texture_cache.GetImageSurface(tic, entry); | 1261 | auto view = texture_cache.GetImageSurface(tic, entry); |
| 1129 | 1262 | ||
| @@ -1133,10 +1266,11 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima | |||
| 1133 | 1266 | ||
| 1134 | UNIMPLEMENTED_IF(tic.IsBuffer()); | 1267 | UNIMPLEMENTED_IF(tic.IsBuffer()); |
| 1135 | 1268 | ||
| 1136 | const auto image_view = view->GetHandle(tic.x_source, tic.y_source, tic.z_source, tic.w_source); | 1269 | const VkImageView image_view = |
| 1270 | view->GetImageView(tic.x_source, tic.y_source, tic.z_source, tic.w_source); | ||
| 1137 | update_descriptor_queue.AddImage(image_view); | 1271 | update_descriptor_queue.AddImage(image_view); |
| 1138 | 1272 | ||
| 1139 | const auto image_layout = update_descriptor_queue.GetLastImageLayout(); | 1273 | VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout(); |
| 1140 | *image_layout = VK_IMAGE_LAYOUT_GENERAL; | 1274 | *image_layout = VK_IMAGE_LAYOUT_GENERAL; |
| 1141 | image_views.push_back(ImageView{std::move(view), image_layout}); | 1275 | image_views.push_back(ImageView{std::move(view), image_layout}); |
| 1142 | } | 1276 | } |
| @@ -1231,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) | |||
| 1231 | } | 1365 | } |
| 1232 | } | 1366 | } |
| 1233 | 1367 | ||
| 1368 | void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1369 | if (!state_tracker.TouchCullMode()) { | ||
| 1370 | return; | ||
| 1371 | } | ||
| 1372 | scheduler.Record( | ||
| 1373 | [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) { | ||
| 1374 | cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); | ||
| 1375 | }); | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1379 | if (!state_tracker.TouchDepthBoundsTestEnable()) { | ||
| 1380 | return; | ||
| 1381 | } | ||
| 1382 | scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) { | ||
| 1383 | cmdbuf.SetDepthBoundsTestEnableEXT(enable); | ||
| 1384 | }); | ||
| 1385 | } | ||
| 1386 | |||
| 1387 | void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1388 | if (!state_tracker.TouchDepthTestEnable()) { | ||
| 1389 | return; | ||
| 1390 | } | ||
| 1391 | scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) { | ||
| 1392 | cmdbuf.SetDepthTestEnableEXT(enable); | ||
| 1393 | }); | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1397 | if (!state_tracker.TouchDepthWriteEnable()) { | ||
| 1398 | return; | ||
| 1399 | } | ||
| 1400 | scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) { | ||
| 1401 | cmdbuf.SetDepthWriteEnableEXT(enable); | ||
| 1402 | }); | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1406 | if (!state_tracker.TouchDepthCompareOp()) { | ||
| 1407 | return; | ||
| 1408 | } | ||
| 1409 | scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) { | ||
| 1410 | cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func)); | ||
| 1411 | }); | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1415 | if (!state_tracker.TouchFrontFace()) { | ||
| 1416 | return; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face); | ||
| 1420 | if (regs.screen_y_control.triangle_rast_flip != 0) { | ||
| 1421 | front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE | ||
| 1422 | : VK_FRONT_FACE_CLOCKWISE; | ||
| 1423 | } | ||
| 1424 | scheduler.Record( | ||
| 1425 | [front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); }); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1429 | if (!state_tracker.TouchPrimitiveTopology()) { | ||
| 1430 | return; | ||
| 1431 | } | ||
| 1432 | const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value(); | ||
| 1433 | scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) { | ||
| 1434 | cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology)); | ||
| 1435 | }); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1439 | if (!state_tracker.TouchStencilOp()) { | ||
| 1440 | return; | ||
| 1441 | } | ||
| 1442 | const Maxwell::StencilOp fail = regs.stencil_front_op_fail; | ||
| 1443 | const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail; | ||
| 1444 | const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; | ||
| 1445 | const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; | ||
| 1446 | if (regs.stencil_two_side_enable) { | ||
| 1447 | scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) { | ||
| 1448 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail), | ||
| 1449 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 1450 | MaxwellToVK::ComparisonOp(compare)); | ||
| 1451 | }); | ||
| 1452 | } else { | ||
| 1453 | const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; | ||
| 1454 | const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; | ||
| 1455 | const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; | ||
| 1456 | const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func; | ||
| 1457 | scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass, | ||
| 1458 | back_compare](vk::CommandBuffer cmdbuf) { | ||
| 1459 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail), | ||
| 1460 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 1461 | MaxwellToVK::ComparisonOp(compare)); | ||
| 1462 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail), | ||
| 1463 | MaxwellToVK::StencilOp(back_zpass), | ||
| 1464 | MaxwellToVK::StencilOp(back_zfail), | ||
| 1465 | MaxwellToVK::ComparisonOp(back_compare)); | ||
| 1466 | }); | ||
| 1467 | } | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1471 | if (!state_tracker.TouchStencilTestEnable()) { | ||
| 1472 | return; | ||
| 1473 | } | ||
| 1474 | scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) { | ||
| 1475 | cmdbuf.SetStencilTestEnableEXT(enable); | ||
| 1476 | }); | ||
| 1477 | } | ||
| 1478 | |||
| 1234 | std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { | 1479 | std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { |
| 1235 | std::size_t size = CalculateVertexArraysSize(); | 1480 | std::size_t size = CalculateVertexArraysSize(); |
| 1236 | if (is_indexed) { | 1481 | if (is_indexed) { |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 0ed0e48c6..923178b0b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -159,7 +159,10 @@ private: | |||
| 159 | 159 | ||
| 160 | void FlushWork(); | 160 | void FlushWork(); |
| 161 | 161 | ||
| 162 | Texceptions UpdateAttachments(); | 162 | /// @brief Updates the currently bound attachments |
| 163 | /// @param is_clear True when the framebuffer is updated as a clear | ||
| 164 | /// @return Bitfield of attachments being used as sampled textures | ||
| 165 | Texceptions UpdateAttachments(bool is_clear); | ||
| 163 | 166 | ||
| 164 | std::tuple<VkFramebuffer, VkExtent2D> ConfigureFramebuffers(VkRenderPass renderpass); | 167 | std::tuple<VkFramebuffer, VkExtent2D> ConfigureFramebuffers(VkRenderPass renderpass); |
| 165 | 168 | ||
| @@ -168,7 +171,7 @@ private: | |||
| 168 | bool is_indexed, bool is_instanced); | 171 | bool is_indexed, bool is_instanced); |
| 169 | 172 | ||
| 170 | /// Setup descriptors in the graphics pipeline. | 173 | /// Setup descriptors in the graphics pipeline. |
| 171 | void SetupShaderDescriptors(const std::array<Shader, Maxwell::MaxShaderProgram>& shaders); | 174 | void SetupShaderDescriptors(const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders); |
| 172 | 175 | ||
| 173 | void SetupImageTransitions(Texceptions texceptions, | 176 | void SetupImageTransitions(Texceptions texceptions, |
| 174 | const std::array<View, Maxwell::NumRenderTargets>& color_attachments, | 177 | const std::array<View, Maxwell::NumRenderTargets>& color_attachments, |
| @@ -182,8 +185,7 @@ private: | |||
| 182 | 185 | ||
| 183 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); | 186 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); |
| 184 | 187 | ||
| 185 | void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 188 | void SetupVertexArrays(BufferBindings& buffer_bindings); |
| 186 | BufferBindings& buffer_bindings); | ||
| 187 | 189 | ||
| 188 | void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); | 190 | void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); |
| 189 | 191 | ||
| @@ -193,12 +195,15 @@ private: | |||
| 193 | /// Setup global buffers in the graphics pipeline. | 195 | /// Setup global buffers in the graphics pipeline. |
| 194 | void SetupGraphicsGlobalBuffers(const ShaderEntries& entries, std::size_t stage); | 196 | void SetupGraphicsGlobalBuffers(const ShaderEntries& entries, std::size_t stage); |
| 195 | 197 | ||
| 196 | /// Setup texel buffers in the graphics pipeline. | 198 | /// Setup uniform texels in the graphics pipeline. |
| 197 | void SetupGraphicsTexelBuffers(const ShaderEntries& entries, std::size_t stage); | 199 | void SetupGraphicsUniformTexels(const ShaderEntries& entries, std::size_t stage); |
| 198 | 200 | ||
| 199 | /// Setup textures in the graphics pipeline. | 201 | /// Setup textures in the graphics pipeline. |
| 200 | void SetupGraphicsTextures(const ShaderEntries& entries, std::size_t stage); | 202 | void SetupGraphicsTextures(const ShaderEntries& entries, std::size_t stage); |
| 201 | 203 | ||
| 204 | /// Setup storage texels in the graphics pipeline. | ||
| 205 | void SetupGraphicsStorageTexels(const ShaderEntries& entries, std::size_t stage); | ||
| 206 | |||
| 202 | /// Setup images in the graphics pipeline. | 207 | /// Setup images in the graphics pipeline. |
| 203 | void SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage); | 208 | void SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage); |
| 204 | 209 | ||
| @@ -209,11 +214,14 @@ private: | |||
| 209 | void SetupComputeGlobalBuffers(const ShaderEntries& entries); | 214 | void SetupComputeGlobalBuffers(const ShaderEntries& entries); |
| 210 | 215 | ||
| 211 | /// Setup texel buffers in the compute pipeline. | 216 | /// Setup texel buffers in the compute pipeline. |
| 212 | void SetupComputeTexelBuffers(const ShaderEntries& entries); | 217 | void SetupComputeUniformTexels(const ShaderEntries& entries); |
| 213 | 218 | ||
| 214 | /// Setup textures in the compute pipeline. | 219 | /// Setup textures in the compute pipeline. |
| 215 | void SetupComputeTextures(const ShaderEntries& entries); | 220 | void SetupComputeTextures(const ShaderEntries& entries); |
| 216 | 221 | ||
| 222 | /// Setup storage texels in the compute pipeline. | ||
| 223 | void SetupComputeStorageTexels(const ShaderEntries& entries); | ||
| 224 | |||
| 217 | /// Setup images in the compute pipeline. | 225 | /// Setup images in the compute pipeline. |
| 218 | void SetupComputeImages(const ShaderEntries& entries); | 226 | void SetupComputeImages(const ShaderEntries& entries); |
| 219 | 227 | ||
| @@ -222,10 +230,12 @@ private: | |||
| 222 | 230 | ||
| 223 | void SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address); | 231 | void SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address); |
| 224 | 232 | ||
| 225 | void SetupTexelBuffer(const Tegra::Texture::TICEntry& image, const TexelBufferEntry& entry); | 233 | void SetupUniformTexels(const Tegra::Texture::TICEntry& image, const UniformTexelEntry& entry); |
| 226 | 234 | ||
| 227 | void SetupTexture(const Tegra::Texture::FullTextureInfo& texture, const SamplerEntry& entry); | 235 | void SetupTexture(const Tegra::Texture::FullTextureInfo& texture, const SamplerEntry& entry); |
| 228 | 236 | ||
| 237 | void SetupStorageTexel(const Tegra::Texture::TICEntry& tic, const StorageTexelEntry& entry); | ||
| 238 | |||
| 229 | void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry); | 239 | void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry); |
| 230 | 240 | ||
| 231 | void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs); | 241 | void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs); |
| @@ -235,6 +245,16 @@ private: | |||
| 235 | void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); | 245 | void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); |
| 236 | void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); | 246 | void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); |
| 237 | 247 | ||
| 248 | void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 249 | void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 250 | void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 251 | void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 252 | void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 253 | void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 254 | void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 255 | void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 256 | void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 257 | |||
| 238 | std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; | 258 | std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; |
| 239 | 259 | ||
| 240 | std::size_t CalculateComputeStreamBufferSize() const; | 260 | std::size_t CalculateComputeStreamBufferSize() const; |
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp index e6f2fa553..616eacc36 100644 --- a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp | |||
| @@ -9,6 +9,8 @@ | |||
| 9 | #include "video_core/renderer_vulkan/wrapper.h" | 9 | #include "video_core/renderer_vulkan/wrapper.h" |
| 10 | #include "video_core/textures/texture.h" | 10 | #include "video_core/textures/texture.h" |
| 11 | 11 | ||
| 12 | using Tegra::Texture::TextureMipmapFilter; | ||
| 13 | |||
| 12 | namespace Vulkan { | 14 | namespace Vulkan { |
| 13 | 15 | ||
| 14 | namespace { | 16 | namespace { |
| @@ -63,8 +65,8 @@ vk::Sampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) c | |||
| 63 | ci.maxAnisotropy = tsc.GetMaxAnisotropy(); | 65 | ci.maxAnisotropy = tsc.GetMaxAnisotropy(); |
| 64 | ci.compareEnable = tsc.depth_compare_enabled; | 66 | ci.compareEnable = tsc.depth_compare_enabled; |
| 65 | ci.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func); | 67 | ci.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func); |
| 66 | ci.minLod = tsc.GetMinLod(); | 68 | ci.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.GetMinLod(); |
| 67 | ci.maxLod = tsc.GetMaxLod(); | 69 | ci.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.GetMaxLod(); |
| 68 | ci.borderColor = arbitrary_borders ? VK_BORDER_COLOR_INT_CUSTOM_EXT : ConvertBorderColor(color); | 70 | ci.borderColor = arbitrary_borders ? VK_BORDER_COLOR_INT_CUSTOM_EXT : ConvertBorderColor(color); |
| 69 | ci.unnormalizedCoordinates = VK_FALSE; | 71 | ci.unnormalizedCoordinates = VK_FALSE; |
| 70 | return device.GetLogical().CreateSampler(ci); | 72 | return device.GetLogical().CreateSampler(ci); |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 82ec9180e..56524e6f3 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/thread.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_device.h" | 13 | #include "video_core/renderer_vulkan/vk_device.h" |
| 13 | #include "video_core/renderer_vulkan/vk_query_cache.h" | 14 | #include "video_core/renderer_vulkan/vk_query_cache.h" |
| 14 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | 15 | #include "video_core/renderer_vulkan/vk_resource_manager.h" |
| @@ -133,6 +134,7 @@ void VKScheduler::BindGraphicsPipeline(VkPipeline pipeline) { | |||
| 133 | } | 134 | } |
| 134 | 135 | ||
| 135 | void VKScheduler::WorkerThread() { | 136 | void VKScheduler::WorkerThread() { |
| 137 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 136 | std::unique_lock lock{mutex}; | 138 | std::unique_lock lock{mutex}; |
| 137 | do { | 139 | do { |
| 138 | cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; }); | 140 | cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; }); |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 6f6dedd82..97429cc59 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -400,8 +400,9 @@ private: | |||
| 400 | u32 binding = specialization.base_binding; | 400 | u32 binding = specialization.base_binding; |
| 401 | binding = DeclareConstantBuffers(binding); | 401 | binding = DeclareConstantBuffers(binding); |
| 402 | binding = DeclareGlobalBuffers(binding); | 402 | binding = DeclareGlobalBuffers(binding); |
| 403 | binding = DeclareTexelBuffers(binding); | 403 | binding = DeclareUniformTexels(binding); |
| 404 | binding = DeclareSamplers(binding); | 404 | binding = DeclareSamplers(binding); |
| 405 | binding = DeclareStorageTexels(binding); | ||
| 405 | binding = DeclareImages(binding); | 406 | binding = DeclareImages(binding); |
| 406 | 407 | ||
| 407 | const Id main = OpFunction(t_void, {}, TypeFunction(t_void)); | 408 | const Id main = OpFunction(t_void, {}, TypeFunction(t_void)); |
| @@ -741,8 +742,10 @@ private: | |||
| 741 | if (!IsGenericAttribute(index)) { | 742 | if (!IsGenericAttribute(index)) { |
| 742 | continue; | 743 | continue; |
| 743 | } | 744 | } |
| 744 | |||
| 745 | const u32 location = GetGenericAttributeLocation(index); | 745 | const u32 location = GetGenericAttributeLocation(index); |
| 746 | if (!IsAttributeEnabled(location)) { | ||
| 747 | continue; | ||
| 748 | } | ||
| 746 | const auto type_descriptor = GetAttributeType(location); | 749 | const auto type_descriptor = GetAttributeType(location); |
| 747 | Id type; | 750 | Id type; |
| 748 | if (IsInputAttributeArray()) { | 751 | if (IsInputAttributeArray()) { |
| @@ -887,7 +890,7 @@ private: | |||
| 887 | return binding; | 890 | return binding; |
| 888 | } | 891 | } |
| 889 | 892 | ||
| 890 | u32 DeclareTexelBuffers(u32 binding) { | 893 | u32 DeclareUniformTexels(u32 binding) { |
| 891 | for (const auto& sampler : ir.GetSamplers()) { | 894 | for (const auto& sampler : ir.GetSamplers()) { |
| 892 | if (!sampler.is_buffer) { | 895 | if (!sampler.is_buffer) { |
| 893 | continue; | 896 | continue; |
| @@ -908,7 +911,7 @@ private: | |||
| 908 | Decorate(id, spv::Decoration::Binding, binding++); | 911 | Decorate(id, spv::Decoration::Binding, binding++); |
| 909 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | 912 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); |
| 910 | 913 | ||
| 911 | texel_buffers.emplace(sampler.index, TexelBuffer{image_type, id}); | 914 | uniform_texels.emplace(sampler.index, TexelBuffer{image_type, id}); |
| 912 | } | 915 | } |
| 913 | return binding; | 916 | return binding; |
| 914 | } | 917 | } |
| @@ -943,31 +946,48 @@ private: | |||
| 943 | return binding; | 946 | return binding; |
| 944 | } | 947 | } |
| 945 | 948 | ||
| 946 | u32 DeclareImages(u32 binding) { | 949 | u32 DeclareStorageTexels(u32 binding) { |
| 947 | for (const auto& image : ir.GetImages()) { | 950 | for (const auto& image : ir.GetImages()) { |
| 948 | const auto [dim, arrayed] = GetImageDim(image); | 951 | if (image.type != Tegra::Shader::ImageType::TextureBuffer) { |
| 949 | constexpr int depth = 0; | 952 | continue; |
| 950 | constexpr bool ms = false; | ||
| 951 | constexpr int sampled = 2; // This won't be accessed with a sampler | ||
| 952 | constexpr auto format = spv::ImageFormat::Unknown; | ||
| 953 | const Id image_type = TypeImage(t_uint, dim, depth, arrayed, ms, sampled, format, {}); | ||
| 954 | const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, image_type); | ||
| 955 | const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant); | ||
| 956 | AddGlobalVariable(Name(id, fmt::format("image_{}", image.index))); | ||
| 957 | |||
| 958 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 959 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 960 | if (image.is_read && !image.is_written) { | ||
| 961 | Decorate(id, spv::Decoration::NonWritable); | ||
| 962 | } else if (image.is_written && !image.is_read) { | ||
| 963 | Decorate(id, spv::Decoration::NonReadable); | ||
| 964 | } | 953 | } |
| 954 | DeclareImage(image, binding); | ||
| 955 | } | ||
| 956 | return binding; | ||
| 957 | } | ||
| 965 | 958 | ||
| 966 | images.emplace(image.index, StorageImage{image_type, id}); | 959 | u32 DeclareImages(u32 binding) { |
| 960 | for (const auto& image : ir.GetImages()) { | ||
| 961 | if (image.type == Tegra::Shader::ImageType::TextureBuffer) { | ||
| 962 | continue; | ||
| 963 | } | ||
| 964 | DeclareImage(image, binding); | ||
| 967 | } | 965 | } |
| 968 | return binding; | 966 | return binding; |
| 969 | } | 967 | } |
| 970 | 968 | ||
| 969 | void DeclareImage(const Image& image, u32& binding) { | ||
| 970 | const auto [dim, arrayed] = GetImageDim(image); | ||
| 971 | constexpr int depth = 0; | ||
| 972 | constexpr bool ms = false; | ||
| 973 | constexpr int sampled = 2; // This won't be accessed with a sampler | ||
| 974 | const auto format = image.is_atomic ? spv::ImageFormat::R32ui : spv::ImageFormat::Unknown; | ||
| 975 | const Id image_type = TypeImage(t_uint, dim, depth, arrayed, ms, sampled, format, {}); | ||
| 976 | const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, image_type); | ||
| 977 | const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant); | ||
| 978 | AddGlobalVariable(Name(id, fmt::format("image_{}", image.index))); | ||
| 979 | |||
| 980 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 981 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 982 | if (image.is_read && !image.is_written) { | ||
| 983 | Decorate(id, spv::Decoration::NonWritable); | ||
| 984 | } else if (image.is_written && !image.is_read) { | ||
| 985 | Decorate(id, spv::Decoration::NonReadable); | ||
| 986 | } | ||
| 987 | |||
| 988 | images.emplace(image.index, StorageImage{image_type, id}); | ||
| 989 | } | ||
| 990 | |||
| 971 | bool IsRenderTargetEnabled(u32 rt) const { | 991 | bool IsRenderTargetEnabled(u32 rt) const { |
| 972 | for (u32 component = 0; component < 4; ++component) { | 992 | for (u32 component = 0; component < 4; ++component) { |
| 973 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { | 993 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { |
| @@ -986,6 +1006,10 @@ private: | |||
| 986 | return stage == ShaderType::TesselationControl; | 1006 | return stage == ShaderType::TesselationControl; |
| 987 | } | 1007 | } |
| 988 | 1008 | ||
| 1009 | bool IsAttributeEnabled(u32 location) const { | ||
| 1010 | return stage != ShaderType::Vertex || specialization.enabled_attributes[location]; | ||
| 1011 | } | ||
| 1012 | |||
| 989 | u32 GetNumInputVertices() const { | 1013 | u32 GetNumInputVertices() const { |
| 990 | switch (stage) { | 1014 | switch (stage) { |
| 991 | case ShaderType::Geometry: | 1015 | case ShaderType::Geometry: |
| @@ -1201,16 +1225,20 @@ private: | |||
| 1201 | UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); | 1225 | UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); |
| 1202 | return {v_float_zero, Type::Float}; | 1226 | return {v_float_zero, Type::Float}; |
| 1203 | default: | 1227 | default: |
| 1204 | if (IsGenericAttribute(attribute)) { | 1228 | if (!IsGenericAttribute(attribute)) { |
| 1205 | const u32 location = GetGenericAttributeLocation(attribute); | 1229 | break; |
| 1206 | const auto type_descriptor = GetAttributeType(location); | ||
| 1207 | const Type type = type_descriptor.type; | ||
| 1208 | const Id attribute_id = input_attributes.at(attribute); | ||
| 1209 | const std::vector elements = {element}; | ||
| 1210 | const Id pointer = ArrayPass(type_descriptor.scalar, attribute_id, elements); | ||
| 1211 | return {OpLoad(GetTypeDefinition(type), pointer), type}; | ||
| 1212 | } | 1230 | } |
| 1213 | break; | 1231 | const u32 location = GetGenericAttributeLocation(attribute); |
| 1232 | if (!IsAttributeEnabled(location)) { | ||
| 1233 | // Disabled attributes (also known as constant attributes) always return zero. | ||
| 1234 | return {v_float_zero, Type::Float}; | ||
| 1235 | } | ||
| 1236 | const auto type_descriptor = GetAttributeType(location); | ||
| 1237 | const Type type = type_descriptor.type; | ||
| 1238 | const Id attribute_id = input_attributes.at(attribute); | ||
| 1239 | const std::vector elements = {element}; | ||
| 1240 | const Id pointer = ArrayPass(type_descriptor.scalar, attribute_id, elements); | ||
| 1241 | return {OpLoad(GetTypeDefinition(type), pointer), type}; | ||
| 1214 | } | 1242 | } |
| 1215 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); | 1243 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); |
| 1216 | return {v_float_zero, Type::Float}; | 1244 | return {v_float_zero, Type::Float}; |
| @@ -1246,7 +1274,7 @@ private: | |||
| 1246 | } else { | 1274 | } else { |
| 1247 | UNREACHABLE_MSG("Unmanaged offset node type"); | 1275 | UNREACHABLE_MSG("Unmanaged offset node type"); |
| 1248 | } | 1276 | } |
| 1249 | pointer = OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0), buffer_index, | 1277 | pointer = OpAccessChain(t_cbuf_float, buffer_id, v_uint_zero, buffer_index, |
| 1250 | buffer_element); | 1278 | buffer_element); |
| 1251 | } | 1279 | } |
| 1252 | return {OpLoad(t_float, pointer), Type::Float}; | 1280 | return {OpLoad(t_float, pointer), Type::Float}; |
| @@ -1601,7 +1629,7 @@ private: | |||
| 1601 | 1629 | ||
| 1602 | const Id result = OpIAddCarry(TypeStruct({t_uint, t_uint}), op_a, op_b); | 1630 | const Id result = OpIAddCarry(TypeStruct({t_uint, t_uint}), op_a, op_b); |
| 1603 | const Id carry = OpCompositeExtract(t_uint, result, 1); | 1631 | const Id carry = OpCompositeExtract(t_uint, result, 1); |
| 1604 | return {OpINotEqual(t_bool, carry, Constant(t_uint, 0)), Type::Bool}; | 1632 | return {OpINotEqual(t_bool, carry, v_uint_zero), Type::Bool}; |
| 1605 | } | 1633 | } |
| 1606 | 1634 | ||
| 1607 | Expression LogicalAssign(Operation operation) { | 1635 | Expression LogicalAssign(Operation operation) { |
| @@ -1664,7 +1692,7 @@ private: | |||
| 1664 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | 1692 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); |
| 1665 | const u32 index = meta.sampler.index; | 1693 | const u32 index = meta.sampler.index; |
| 1666 | if (meta.sampler.is_buffer) { | 1694 | if (meta.sampler.is_buffer) { |
| 1667 | const auto& entry = texel_buffers.at(index); | 1695 | const auto& entry = uniform_texels.at(index); |
| 1668 | return OpLoad(entry.image_type, entry.image); | 1696 | return OpLoad(entry.image_type, entry.image); |
| 1669 | } else { | 1697 | } else { |
| 1670 | const auto& entry = sampled_images.at(index); | 1698 | const auto& entry = sampled_images.at(index); |
| @@ -1941,39 +1969,20 @@ private: | |||
| 1941 | return {}; | 1969 | return {}; |
| 1942 | } | 1970 | } |
| 1943 | 1971 | ||
| 1944 | Expression AtomicImageAdd(Operation operation) { | 1972 | template <Id (Module::*func)(Id, Id, Id, Id, Id)> |
| 1945 | UNIMPLEMENTED(); | 1973 | Expression AtomicImage(Operation operation) { |
| 1946 | return {}; | 1974 | const auto& meta{std::get<MetaImage>(operation.GetMeta())}; |
| 1947 | } | 1975 | ASSERT(meta.values.size() == 1); |
| 1948 | |||
| 1949 | Expression AtomicImageMin(Operation operation) { | ||
| 1950 | UNIMPLEMENTED(); | ||
| 1951 | return {}; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | Expression AtomicImageMax(Operation operation) { | ||
| 1955 | UNIMPLEMENTED(); | ||
| 1956 | return {}; | ||
| 1957 | } | ||
| 1958 | |||
| 1959 | Expression AtomicImageAnd(Operation operation) { | ||
| 1960 | UNIMPLEMENTED(); | ||
| 1961 | return {}; | ||
| 1962 | } | ||
| 1963 | |||
| 1964 | Expression AtomicImageOr(Operation operation) { | ||
| 1965 | UNIMPLEMENTED(); | ||
| 1966 | return {}; | ||
| 1967 | } | ||
| 1968 | 1976 | ||
| 1969 | Expression AtomicImageXor(Operation operation) { | 1977 | const Id coordinate = GetCoordinates(operation, Type::Int); |
| 1970 | UNIMPLEMENTED(); | 1978 | const Id image = images.at(meta.image.index).image; |
| 1971 | return {}; | 1979 | const Id sample = v_uint_zero; |
| 1972 | } | 1980 | const Id pointer = OpImageTexelPointer(t_image_uint, image, coordinate, sample); |
| 1973 | 1981 | ||
| 1974 | Expression AtomicImageExchange(Operation operation) { | 1982 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); |
| 1975 | UNIMPLEMENTED(); | 1983 | const Id semantics = v_uint_zero; |
| 1976 | return {}; | 1984 | const Id value = AsUint(Visit(meta.values[0])); |
| 1985 | return {(this->*func)(t_uint, pointer, scope, semantics, value), Type::Uint}; | ||
| 1977 | } | 1986 | } |
| 1978 | 1987 | ||
| 1979 | template <Id (Module::*func)(Id, Id, Id, Id, Id)> | 1988 | template <Id (Module::*func)(Id, Id, Id, Id, Id)> |
| @@ -1988,7 +1997,7 @@ private: | |||
| 1988 | return {v_float_zero, Type::Float}; | 1997 | return {v_float_zero, Type::Float}; |
| 1989 | } | 1998 | } |
| 1990 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); | 1999 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); |
| 1991 | const Id semantics = Constant(t_uint, 0); | 2000 | const Id semantics = v_uint_zero; |
| 1992 | const Id value = AsUint(Visit(operation[1])); | 2001 | const Id value = AsUint(Visit(operation[1])); |
| 1993 | 2002 | ||
| 1994 | return {(this->*func)(t_uint, pointer, scope, semantics, value), Type::Uint}; | 2003 | return {(this->*func)(t_uint, pointer, scope, semantics, value), Type::Uint}; |
| @@ -2612,11 +2621,11 @@ private: | |||
| 2612 | 2621 | ||
| 2613 | &SPIRVDecompiler::ImageLoad, | 2622 | &SPIRVDecompiler::ImageLoad, |
| 2614 | &SPIRVDecompiler::ImageStore, | 2623 | &SPIRVDecompiler::ImageStore, |
| 2615 | &SPIRVDecompiler::AtomicImageAdd, | 2624 | &SPIRVDecompiler::AtomicImage<&Module::OpAtomicIAdd>, |
| 2616 | &SPIRVDecompiler::AtomicImageAnd, | 2625 | &SPIRVDecompiler::AtomicImage<&Module::OpAtomicAnd>, |
| 2617 | &SPIRVDecompiler::AtomicImageOr, | 2626 | &SPIRVDecompiler::AtomicImage<&Module::OpAtomicOr>, |
| 2618 | &SPIRVDecompiler::AtomicImageXor, | 2627 | &SPIRVDecompiler::AtomicImage<&Module::OpAtomicXor>, |
| 2619 | &SPIRVDecompiler::AtomicImageExchange, | 2628 | &SPIRVDecompiler::AtomicImage<&Module::OpAtomicExchange>, |
| 2620 | 2629 | ||
| 2621 | &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange>, | 2630 | &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange>, |
| 2622 | &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd>, | 2631 | &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd>, |
| @@ -2758,8 +2767,11 @@ private: | |||
| 2758 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); | 2767 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); |
| 2759 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); | 2768 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); |
| 2760 | 2769 | ||
| 2770 | const Id t_image_uint = TypePointer(spv::StorageClass::Image, t_uint); | ||
| 2771 | |||
| 2761 | const Id v_float_zero = Constant(t_float, 0.0f); | 2772 | const Id v_float_zero = Constant(t_float, 0.0f); |
| 2762 | const Id v_float_one = Constant(t_float, 1.0f); | 2773 | const Id v_float_one = Constant(t_float, 1.0f); |
| 2774 | const Id v_uint_zero = Constant(t_uint, 0); | ||
| 2763 | 2775 | ||
| 2764 | // Nvidia uses these defaults for varyings (e.g. position and generic attributes) | 2776 | // Nvidia uses these defaults for varyings (e.g. position and generic attributes) |
| 2765 | const Id v_varying_default = | 2777 | const Id v_varying_default = |
| @@ -2784,15 +2796,16 @@ private: | |||
| 2784 | std::unordered_map<u8, GenericVaryingDescription> output_attributes; | 2796 | std::unordered_map<u8, GenericVaryingDescription> output_attributes; |
| 2785 | std::map<u32, Id> constant_buffers; | 2797 | std::map<u32, Id> constant_buffers; |
| 2786 | std::map<GlobalMemoryBase, Id> global_buffers; | 2798 | std::map<GlobalMemoryBase, Id> global_buffers; |
| 2787 | std::map<u32, TexelBuffer> texel_buffers; | 2799 | std::map<u32, TexelBuffer> uniform_texels; |
| 2788 | std::map<u32, SampledImage> sampled_images; | 2800 | std::map<u32, SampledImage> sampled_images; |
| 2801 | std::map<u32, TexelBuffer> storage_texels; | ||
| 2789 | std::map<u32, StorageImage> images; | 2802 | std::map<u32, StorageImage> images; |
| 2790 | 2803 | ||
| 2804 | std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; | ||
| 2791 | Id instance_index{}; | 2805 | Id instance_index{}; |
| 2792 | Id vertex_index{}; | 2806 | Id vertex_index{}; |
| 2793 | Id base_instance{}; | 2807 | Id base_instance{}; |
| 2794 | Id base_vertex{}; | 2808 | Id base_vertex{}; |
| 2795 | std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; | ||
| 2796 | Id frag_depth{}; | 2809 | Id frag_depth{}; |
| 2797 | Id frag_coord{}; | 2810 | Id frag_coord{}; |
| 2798 | Id front_facing{}; | 2811 | Id front_facing{}; |
| @@ -3048,13 +3061,17 @@ ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) { | |||
| 3048 | } | 3061 | } |
| 3049 | for (const auto& sampler : ir.GetSamplers()) { | 3062 | for (const auto& sampler : ir.GetSamplers()) { |
| 3050 | if (sampler.is_buffer) { | 3063 | if (sampler.is_buffer) { |
| 3051 | entries.texel_buffers.emplace_back(sampler); | 3064 | entries.uniform_texels.emplace_back(sampler); |
| 3052 | } else { | 3065 | } else { |
| 3053 | entries.samplers.emplace_back(sampler); | 3066 | entries.samplers.emplace_back(sampler); |
| 3054 | } | 3067 | } |
| 3055 | } | 3068 | } |
| 3056 | for (const auto& image : ir.GetImages()) { | 3069 | for (const auto& image : ir.GetImages()) { |
| 3057 | entries.images.emplace_back(image); | 3070 | if (image.type == Tegra::Shader::ImageType::TextureBuffer) { |
| 3071 | entries.storage_texels.emplace_back(image); | ||
| 3072 | } else { | ||
| 3073 | entries.images.emplace_back(image); | ||
| 3074 | } | ||
| 3058 | } | 3075 | } |
| 3059 | for (const auto& attribute : ir.GetInputAttributes()) { | 3076 | for (const auto& attribute : ir.GetInputAttributes()) { |
| 3060 | if (IsGenericAttribute(attribute)) { | 3077 | if (IsGenericAttribute(attribute)) { |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h index f4c05ac3c..2b0e90396 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h | |||
| @@ -21,8 +21,9 @@ class VKDevice; | |||
| 21 | namespace Vulkan { | 21 | namespace Vulkan { |
| 22 | 22 | ||
| 23 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 23 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 24 | using TexelBufferEntry = VideoCommon::Shader::Sampler; | 24 | using UniformTexelEntry = VideoCommon::Shader::Sampler; |
| 25 | using SamplerEntry = VideoCommon::Shader::Sampler; | 25 | using SamplerEntry = VideoCommon::Shader::Sampler; |
| 26 | using StorageTexelEntry = VideoCommon::Shader::Image; | ||
| 26 | using ImageEntry = VideoCommon::Shader::Image; | 27 | using ImageEntry = VideoCommon::Shader::Image; |
| 27 | 28 | ||
| 28 | constexpr u32 DESCRIPTOR_SET = 0; | 29 | constexpr u32 DESCRIPTOR_SET = 0; |
| @@ -66,13 +67,15 @@ private: | |||
| 66 | struct ShaderEntries { | 67 | struct ShaderEntries { |
| 67 | u32 NumBindings() const { | 68 | u32 NumBindings() const { |
| 68 | return static_cast<u32>(const_buffers.size() + global_buffers.size() + | 69 | return static_cast<u32>(const_buffers.size() + global_buffers.size() + |
| 69 | texel_buffers.size() + samplers.size() + images.size()); | 70 | uniform_texels.size() + samplers.size() + storage_texels.size() + |
| 71 | images.size()); | ||
| 70 | } | 72 | } |
| 71 | 73 | ||
| 72 | std::vector<ConstBufferEntry> const_buffers; | 74 | std::vector<ConstBufferEntry> const_buffers; |
| 73 | std::vector<GlobalBufferEntry> global_buffers; | 75 | std::vector<GlobalBufferEntry> global_buffers; |
| 74 | std::vector<TexelBufferEntry> texel_buffers; | 76 | std::vector<UniformTexelEntry> uniform_texels; |
| 75 | std::vector<SamplerEntry> samplers; | 77 | std::vector<SamplerEntry> samplers; |
| 78 | std::vector<StorageTexelEntry> storage_texels; | ||
| 76 | std::vector<ImageEntry> images; | 79 | std::vector<ImageEntry> images; |
| 77 | std::set<u32> attributes; | 80 | std::set<u32> attributes; |
| 78 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | 81 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; |
| @@ -88,7 +91,8 @@ struct Specialization final { | |||
| 88 | u32 shared_memory_size{}; | 91 | u32 shared_memory_size{}; |
| 89 | 92 | ||
| 90 | // Graphics specific | 93 | // Graphics specific |
| 91 | std::optional<float> point_size{}; | 94 | std::optional<float> point_size; |
| 95 | std::bitset<Maxwell::NumVertexAttributes> enabled_attributes; | ||
| 92 | std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{}; | 96 | std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{}; |
| 93 | bool ndc_minus_one_to_one{}; | 97 | bool ndc_minus_one_to_one{}; |
| 94 | }; | 98 | }; |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 94a89e388..e5a583dd5 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp | |||
| @@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() { | |||
| 36 | flags[BlendConstants] = true; | 36 | flags[BlendConstants] = true; |
| 37 | flags[DepthBounds] = true; | 37 | flags[DepthBounds] = true; |
| 38 | flags[StencilProperties] = true; | 38 | flags[StencilProperties] = true; |
| 39 | flags[CullMode] = true; | ||
| 40 | flags[DepthBoundsEnable] = true; | ||
| 41 | flags[DepthTestEnable] = true; | ||
| 42 | flags[DepthWriteEnable] = true; | ||
| 43 | flags[DepthCompareOp] = true; | ||
| 44 | flags[FrontFace] = true; | ||
| 45 | flags[PrimitiveTopology] = true; | ||
| 46 | flags[StencilOp] = true; | ||
| 47 | flags[StencilTestEnable] = true; | ||
| 39 | return flags; | 48 | return flags; |
| 40 | } | 49 | } |
| 41 | 50 | ||
| @@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) { | |||
| 75 | table[OFF(stencil_back_func_mask)] = StencilProperties; | 84 | table[OFF(stencil_back_func_mask)] = StencilProperties; |
| 76 | } | 85 | } |
| 77 | 86 | ||
| 87 | void SetupDirtyCullMode(Tables& tables) { | ||
| 88 | auto& table = tables[0]; | ||
| 89 | table[OFF(cull_face)] = CullMode; | ||
| 90 | table[OFF(cull_test_enabled)] = CullMode; | ||
| 91 | } | ||
| 92 | |||
| 93 | void SetupDirtyDepthBoundsEnable(Tables& tables) { | ||
| 94 | tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable; | ||
| 95 | } | ||
| 96 | |||
| 97 | void SetupDirtyDepthTestEnable(Tables& tables) { | ||
| 98 | tables[0][OFF(depth_test_enable)] = DepthTestEnable; | ||
| 99 | } | ||
| 100 | |||
| 101 | void SetupDirtyDepthWriteEnable(Tables& tables) { | ||
| 102 | tables[0][OFF(depth_write_enabled)] = DepthWriteEnable; | ||
| 103 | } | ||
| 104 | |||
| 105 | void SetupDirtyDepthCompareOp(Tables& tables) { | ||
| 106 | tables[0][OFF(depth_test_func)] = DepthCompareOp; | ||
| 107 | } | ||
| 108 | |||
| 109 | void SetupDirtyFrontFace(Tables& tables) { | ||
| 110 | auto& table = tables[0]; | ||
| 111 | table[OFF(front_face)] = FrontFace; | ||
| 112 | table[OFF(screen_y_control)] = FrontFace; | ||
| 113 | } | ||
| 114 | |||
| 115 | void SetupDirtyPrimitiveTopology(Tables& tables) { | ||
| 116 | tables[0][OFF(draw.topology)] = PrimitiveTopology; | ||
| 117 | } | ||
| 118 | |||
| 119 | void SetupDirtyStencilOp(Tables& tables) { | ||
| 120 | auto& table = tables[0]; | ||
| 121 | table[OFF(stencil_front_op_fail)] = StencilOp; | ||
| 122 | table[OFF(stencil_front_op_zfail)] = StencilOp; | ||
| 123 | table[OFF(stencil_front_op_zpass)] = StencilOp; | ||
| 124 | table[OFF(stencil_front_func_func)] = StencilOp; | ||
| 125 | table[OFF(stencil_back_op_fail)] = StencilOp; | ||
| 126 | table[OFF(stencil_back_op_zfail)] = StencilOp; | ||
| 127 | table[OFF(stencil_back_op_zpass)] = StencilOp; | ||
| 128 | table[OFF(stencil_back_func_func)] = StencilOp; | ||
| 129 | |||
| 130 | // Table 0 is used by StencilProperties | ||
| 131 | tables[1][OFF(stencil_two_side_enable)] = StencilOp; | ||
| 132 | } | ||
| 133 | |||
| 134 | void SetupDirtyStencilTestEnable(Tables& tables) { | ||
| 135 | tables[0][OFF(stencil_enable)] = StencilTestEnable; | ||
| 136 | } | ||
| 137 | |||
| 78 | } // Anonymous namespace | 138 | } // Anonymous namespace |
| 79 | 139 | ||
| 80 | StateTracker::StateTracker(Core::System& system) | 140 | StateTracker::StateTracker(Core::System& system) |
| @@ -90,6 +150,14 @@ void StateTracker::Initialize() { | |||
| 90 | SetupDirtyBlendConstants(tables); | 150 | SetupDirtyBlendConstants(tables); |
| 91 | SetupDirtyDepthBounds(tables); | 151 | SetupDirtyDepthBounds(tables); |
| 92 | SetupDirtyStencilProperties(tables); | 152 | SetupDirtyStencilProperties(tables); |
| 153 | SetupDirtyCullMode(tables); | ||
| 154 | SetupDirtyDepthBoundsEnable(tables); | ||
| 155 | SetupDirtyDepthTestEnable(tables); | ||
| 156 | SetupDirtyDepthWriteEnable(tables); | ||
| 157 | SetupDirtyDepthCompareOp(tables); | ||
| 158 | SetupDirtyFrontFace(tables); | ||
| 159 | SetupDirtyPrimitiveTopology(tables); | ||
| 160 | SetupDirtyStencilOp(tables); | ||
| 93 | } | 161 | } |
| 94 | 162 | ||
| 95 | void StateTracker::InvalidateCommandBufferState() { | 163 | void StateTracker::InvalidateCommandBufferState() { |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 03bc415b2..54ca0d6c6 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h | |||
| @@ -26,6 +26,16 @@ enum : u8 { | |||
| 26 | DepthBounds, | 26 | DepthBounds, |
| 27 | StencilProperties, | 27 | StencilProperties, |
| 28 | 28 | ||
| 29 | CullMode, | ||
| 30 | DepthBoundsEnable, | ||
| 31 | DepthTestEnable, | ||
| 32 | DepthWriteEnable, | ||
| 33 | DepthCompareOp, | ||
| 34 | FrontFace, | ||
| 35 | PrimitiveTopology, | ||
| 36 | StencilOp, | ||
| 37 | StencilTestEnable, | ||
| 38 | |||
| 29 | Last | 39 | Last |
| 30 | }; | 40 | }; |
| 31 | static_assert(Last <= std::numeric_limits<u8>::max()); | 41 | static_assert(Last <= std::numeric_limits<u8>::max()); |
| @@ -64,6 +74,46 @@ public: | |||
| 64 | return Exchange(Dirty::StencilProperties, false); | 74 | return Exchange(Dirty::StencilProperties, false); |
| 65 | } | 75 | } |
| 66 | 76 | ||
| 77 | bool TouchCullMode() { | ||
| 78 | return Exchange(Dirty::CullMode, false); | ||
| 79 | } | ||
| 80 | |||
| 81 | bool TouchDepthBoundsTestEnable() { | ||
| 82 | return Exchange(Dirty::DepthBoundsEnable, false); | ||
| 83 | } | ||
| 84 | |||
| 85 | bool TouchDepthTestEnable() { | ||
| 86 | return Exchange(Dirty::DepthTestEnable, false); | ||
| 87 | } | ||
| 88 | |||
| 89 | bool TouchDepthBoundsEnable() { | ||
| 90 | return Exchange(Dirty::DepthBoundsEnable, false); | ||
| 91 | } | ||
| 92 | |||
| 93 | bool TouchDepthWriteEnable() { | ||
| 94 | return Exchange(Dirty::DepthWriteEnable, false); | ||
| 95 | } | ||
| 96 | |||
| 97 | bool TouchDepthCompareOp() { | ||
| 98 | return Exchange(Dirty::DepthCompareOp, false); | ||
| 99 | } | ||
| 100 | |||
| 101 | bool TouchFrontFace() { | ||
| 102 | return Exchange(Dirty::FrontFace, false); | ||
| 103 | } | ||
| 104 | |||
| 105 | bool TouchPrimitiveTopology() { | ||
| 106 | return Exchange(Dirty::PrimitiveTopology, false); | ||
| 107 | } | ||
| 108 | |||
| 109 | bool TouchStencilOp() { | ||
| 110 | return Exchange(Dirty::StencilOp, false); | ||
| 111 | } | ||
| 112 | |||
| 113 | bool TouchStencilTestEnable() { | ||
| 114 | return Exchange(Dirty::StencilTestEnable, false); | ||
| 115 | } | ||
| 116 | |||
| 67 | private: | 117 | private: |
| 68 | bool Exchange(std::size_t id, bool new_value) const noexcept { | 118 | bool Exchange(std::size_t id, bool new_value) const noexcept { |
| 69 | auto& flags = system.GPU().Maxwell3D().dirty.flags; | 119 | auto& flags = system.GPU().Maxwell3D().dirty.flags; |
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp index 868447af2..2d28a6c47 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp | |||
| @@ -121,7 +121,7 @@ void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) { | |||
| 121 | 121 | ||
| 122 | // Substract from the preferred heap size some bytes to avoid getting out of memory. | 122 | // Substract from the preferred heap size some bytes to avoid getting out of memory. |
| 123 | const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size; | 123 | const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size; |
| 124 | const VkDeviceSize allocable_size = heap_size - 4 * 1024 * 1024; | 124 | const VkDeviceSize allocable_size = heap_size - 9 * 1024 * 1024; |
| 125 | 125 | ||
| 126 | VkBufferCreateInfo buffer_ci; | 126 | VkBufferCreateInfo buffer_ci; |
| 127 | buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | 127 | buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h index dfddf7ad6..689f0d276 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.h +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h | |||
| @@ -35,10 +35,14 @@ public: | |||
| 35 | /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. | 35 | /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. |
| 36 | void Unmap(u64 size); | 36 | void Unmap(u64 size); |
| 37 | 37 | ||
| 38 | VkBuffer GetHandle() const { | 38 | VkBuffer Handle() const noexcept { |
| 39 | return *buffer; | 39 | return *buffer; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | u64 Address() const noexcept { | ||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 42 | private: | 46 | private: |
| 43 | struct Watch final { | 47 | struct Watch final { |
| 44 | VKFenceWatch fence; | 48 | VKFenceWatch fence; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 2f1d5021d..430031665 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -100,8 +100,8 @@ vk::Buffer CreateBuffer(const VKDevice& device, const SurfaceParams& params, | |||
| 100 | ci.pNext = nullptr; | 100 | ci.pNext = nullptr; |
| 101 | ci.flags = 0; | 101 | ci.flags = 0; |
| 102 | ci.size = static_cast<VkDeviceSize>(host_memory_size); | 102 | ci.size = static_cast<VkDeviceSize>(host_memory_size); |
| 103 | ci.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | | 103 | ci.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | |
| 104 | VK_BUFFER_USAGE_TRANSFER_DST_BIT; | 104 | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| 105 | ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | 105 | ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| 106 | ci.queueFamilyIndexCount = 0; | 106 | ci.queueFamilyIndexCount = 0; |
| 107 | ci.pQueueFamilyIndices = nullptr; | 107 | ci.pQueueFamilyIndices = nullptr; |
| @@ -167,6 +167,7 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP | |||
| 167 | ci.extent = {params.width, params.height, 1}; | 167 | ci.extent = {params.width, params.height, 1}; |
| 168 | break; | 168 | break; |
| 169 | case SurfaceTarget::Texture3D: | 169 | case SurfaceTarget::Texture3D: |
| 170 | ci.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; | ||
| 170 | ci.extent = {params.width, params.height, params.depth}; | 171 | ci.extent = {params.width, params.height, params.depth}; |
| 171 | break; | 172 | break; |
| 172 | case SurfaceTarget::TextureBuffer: | 173 | case SurfaceTarget::TextureBuffer: |
| @@ -176,6 +177,12 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP | |||
| 176 | return ci; | 177 | return ci; |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 180 | u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::SwizzleSource y_source, | ||
| 181 | Tegra::Texture::SwizzleSource z_source, Tegra::Texture::SwizzleSource w_source) { | ||
| 182 | return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) | | ||
| 183 | (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source); | ||
| 184 | } | ||
| 185 | |||
| 179 | } // Anonymous namespace | 186 | } // Anonymous namespace |
| 180 | 187 | ||
| 181 | CachedSurface::CachedSurface(Core::System& system, const VKDevice& device, | 188 | CachedSurface::CachedSurface(Core::System& system, const VKDevice& device, |
| @@ -203,9 +210,11 @@ CachedSurface::CachedSurface(Core::System& system, const VKDevice& device, | |||
| 203 | } | 210 | } |
| 204 | 211 | ||
| 205 | // TODO(Rodrigo): Move this to a virtual function. | 212 | // TODO(Rodrigo): Move this to a virtual function. |
| 206 | main_view = CreateViewInner( | 213 | u32 num_layers = 1; |
| 207 | ViewParams(params.target, 0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels), | 214 | if (params.is_layered || params.target == SurfaceTarget::Texture3D) { |
| 208 | true); | 215 | num_layers = params.depth; |
| 216 | } | ||
| 217 | main_view = CreateView(ViewParams(params.target, 0, num_layers, 0, params.num_levels)); | ||
| 209 | } | 218 | } |
| 210 | 219 | ||
| 211 | CachedSurface::~CachedSurface() = default; | 220 | CachedSurface::~CachedSurface() = default; |
| @@ -253,12 +262,8 @@ void CachedSurface::DecorateSurfaceName() { | |||
| 253 | } | 262 | } |
| 254 | 263 | ||
| 255 | View CachedSurface::CreateView(const ViewParams& params) { | 264 | View CachedSurface::CreateView(const ViewParams& params) { |
| 256 | return CreateViewInner(params, false); | ||
| 257 | } | ||
| 258 | |||
| 259 | View CachedSurface::CreateViewInner(const ViewParams& params, bool is_proxy) { | ||
| 260 | // TODO(Rodrigo): Add name decorations | 265 | // TODO(Rodrigo): Add name decorations |
| 261 | return views[params] = std::make_shared<CachedSurfaceView>(device, *this, params, is_proxy); | 266 | return views[params] = std::make_shared<CachedSurfaceView>(device, *this, params); |
| 262 | } | 267 | } |
| 263 | 268 | ||
| 264 | void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) { | 269 | void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) { |
| @@ -342,18 +347,27 @@ VkImageSubresourceRange CachedSurface::GetImageSubresourceRange() const { | |||
| 342 | } | 347 | } |
| 343 | 348 | ||
| 344 | CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface, | 349 | CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface, |
| 345 | const ViewParams& params, bool is_proxy) | 350 | const ViewParams& params) |
| 346 | : VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()}, | 351 | : VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()}, |
| 347 | image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()}, | 352 | image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()}, |
| 348 | aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface}, | 353 | aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface}, |
| 349 | base_layer{params.base_layer}, num_layers{params.num_layers}, base_level{params.base_level}, | 354 | base_level{params.base_level}, num_levels{params.num_levels}, |
| 350 | num_levels{params.num_levels}, image_view_type{image ? GetImageViewType(params.target) | 355 | image_view_type{image ? GetImageViewType(params.target) : VK_IMAGE_VIEW_TYPE_1D} { |
| 351 | : VK_IMAGE_VIEW_TYPE_1D} {} | 356 | if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) { |
| 357 | base_layer = 0; | ||
| 358 | num_layers = 1; | ||
| 359 | base_slice = params.base_layer; | ||
| 360 | num_slices = params.num_layers; | ||
| 361 | } else { | ||
| 362 | base_layer = params.base_layer; | ||
| 363 | num_layers = params.num_layers; | ||
| 364 | } | ||
| 365 | } | ||
| 352 | 366 | ||
| 353 | CachedSurfaceView::~CachedSurfaceView() = default; | 367 | CachedSurfaceView::~CachedSurfaceView() = default; |
| 354 | 368 | ||
| 355 | VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source, | 369 | VkImageView CachedSurfaceView::GetImageView(SwizzleSource x_source, SwizzleSource y_source, |
| 356 | SwizzleSource z_source, SwizzleSource w_source) { | 370 | SwizzleSource z_source, SwizzleSource w_source) { |
| 357 | const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source); | 371 | const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source); |
| 358 | if (last_image_view && last_swizzle == new_swizzle) { | 372 | if (last_image_view && last_swizzle == new_swizzle) { |
| 359 | return last_image_view; | 373 | return last_image_view; |
| @@ -399,6 +413,11 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y | |||
| 399 | }); | 413 | }); |
| 400 | } | 414 | } |
| 401 | 415 | ||
| 416 | if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) { | ||
| 417 | ASSERT(base_slice == 0); | ||
| 418 | ASSERT(num_slices == params.depth); | ||
| 419 | } | ||
| 420 | |||
| 402 | VkImageViewCreateInfo ci; | 421 | VkImageViewCreateInfo ci; |
| 403 | ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | 422 | ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
| 404 | ci.pNext = nullptr; | 423 | ci.pNext = nullptr; |
| @@ -417,6 +436,35 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y | |||
| 417 | return last_image_view = *image_view; | 436 | return last_image_view = *image_view; |
| 418 | } | 437 | } |
| 419 | 438 | ||
| 439 | VkImageView CachedSurfaceView::GetAttachment() { | ||
| 440 | if (render_target) { | ||
| 441 | return *render_target; | ||
| 442 | } | ||
| 443 | |||
| 444 | VkImageViewCreateInfo ci; | ||
| 445 | ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | ||
| 446 | ci.pNext = nullptr; | ||
| 447 | ci.flags = 0; | ||
| 448 | ci.image = surface.GetImageHandle(); | ||
| 449 | ci.format = surface.GetImage().GetFormat(); | ||
| 450 | ci.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 451 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}; | ||
| 452 | ci.subresourceRange.aspectMask = aspect_mask; | ||
| 453 | ci.subresourceRange.baseMipLevel = base_level; | ||
| 454 | ci.subresourceRange.levelCount = num_levels; | ||
| 455 | if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) { | ||
| 456 | ci.viewType = num_slices > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; | ||
| 457 | ci.subresourceRange.baseArrayLayer = base_slice; | ||
| 458 | ci.subresourceRange.layerCount = num_slices; | ||
| 459 | } else { | ||
| 460 | ci.viewType = image_view_type; | ||
| 461 | ci.subresourceRange.baseArrayLayer = base_layer; | ||
| 462 | ci.subresourceRange.layerCount = num_layers; | ||
| 463 | } | ||
| 464 | render_target = device.GetLogical().CreateImageView(ci); | ||
| 465 | return *render_target; | ||
| 466 | } | ||
| 467 | |||
| 420 | VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 468 | VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 421 | const VKDevice& device, VKResourceManager& resource_manager, | 469 | const VKDevice& device, VKResourceManager& resource_manager, |
| 422 | VKMemoryManager& memory_manager, VKScheduler& scheduler, | 470 | VKMemoryManager& memory_manager, VKScheduler& scheduler, |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index f211ccb1e..807e26c8a 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -91,7 +91,6 @@ protected: | |||
| 91 | void DecorateSurfaceName(); | 91 | void DecorateSurfaceName(); |
| 92 | 92 | ||
| 93 | View CreateView(const ViewParams& params) override; | 93 | View CreateView(const ViewParams& params) override; |
| 94 | View CreateViewInner(const ViewParams& params, bool is_proxy); | ||
| 95 | 94 | ||
| 96 | private: | 95 | private: |
| 97 | void UploadBuffer(const std::vector<u8>& staging_buffer); | 96 | void UploadBuffer(const std::vector<u8>& staging_buffer); |
| @@ -120,23 +119,20 @@ private: | |||
| 120 | class CachedSurfaceView final : public VideoCommon::ViewBase { | 119 | class CachedSurfaceView final : public VideoCommon::ViewBase { |
| 121 | public: | 120 | public: |
| 122 | explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface, | 121 | explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface, |
| 123 | const ViewParams& params, bool is_proxy); | 122 | const ViewParams& params); |
| 124 | ~CachedSurfaceView(); | 123 | ~CachedSurfaceView(); |
| 125 | 124 | ||
| 126 | VkImageView GetHandle(Tegra::Texture::SwizzleSource x_source, | 125 | VkImageView GetImageView(Tegra::Texture::SwizzleSource x_source, |
| 127 | Tegra::Texture::SwizzleSource y_source, | 126 | Tegra::Texture::SwizzleSource y_source, |
| 128 | Tegra::Texture::SwizzleSource z_source, | 127 | Tegra::Texture::SwizzleSource z_source, |
| 129 | Tegra::Texture::SwizzleSource w_source); | 128 | Tegra::Texture::SwizzleSource w_source); |
| 129 | |||
| 130 | VkImageView GetAttachment(); | ||
| 130 | 131 | ||
| 131 | bool IsSameSurface(const CachedSurfaceView& rhs) const { | 132 | bool IsSameSurface(const CachedSurfaceView& rhs) const { |
| 132 | return &surface == &rhs.surface; | 133 | return &surface == &rhs.surface; |
| 133 | } | 134 | } |
| 134 | 135 | ||
| 135 | VkImageView GetHandle() { | ||
| 136 | return GetHandle(Tegra::Texture::SwizzleSource::R, Tegra::Texture::SwizzleSource::G, | ||
| 137 | Tegra::Texture::SwizzleSource::B, Tegra::Texture::SwizzleSource::A); | ||
| 138 | } | ||
| 139 | |||
| 140 | u32 GetWidth() const { | 136 | u32 GetWidth() const { |
| 141 | return params.GetMipWidth(base_level); | 137 | return params.GetMipWidth(base_level); |
| 142 | } | 138 | } |
| @@ -180,14 +176,6 @@ public: | |||
| 180 | } | 176 | } |
| 181 | 177 | ||
| 182 | private: | 178 | private: |
| 183 | static u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, | ||
| 184 | Tegra::Texture::SwizzleSource y_source, | ||
| 185 | Tegra::Texture::SwizzleSource z_source, | ||
| 186 | Tegra::Texture::SwizzleSource w_source) { | ||
| 187 | return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) | | ||
| 188 | (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source); | ||
| 189 | } | ||
| 190 | |||
| 191 | // Store a copy of these values to avoid double dereference when reading them | 179 | // Store a copy of these values to avoid double dereference when reading them |
| 192 | const SurfaceParams params; | 180 | const SurfaceParams params; |
| 193 | const VkImage image; | 181 | const VkImage image; |
| @@ -196,15 +184,18 @@ private: | |||
| 196 | 184 | ||
| 197 | const VKDevice& device; | 185 | const VKDevice& device; |
| 198 | CachedSurface& surface; | 186 | CachedSurface& surface; |
| 199 | const u32 base_layer; | ||
| 200 | const u32 num_layers; | ||
| 201 | const u32 base_level; | 187 | const u32 base_level; |
| 202 | const u32 num_levels; | 188 | const u32 num_levels; |
| 203 | const VkImageViewType image_view_type; | 189 | const VkImageViewType image_view_type; |
| 190 | u32 base_layer = 0; | ||
| 191 | u32 num_layers = 0; | ||
| 192 | u32 base_slice = 0; | ||
| 193 | u32 num_slices = 0; | ||
| 204 | 194 | ||
| 205 | VkImageView last_image_view = nullptr; | 195 | VkImageView last_image_view = nullptr; |
| 206 | u32 last_swizzle = 0; | 196 | u32 last_swizzle = 0; |
| 207 | 197 | ||
| 198 | vk::ImageView render_target; | ||
| 208 | std::unordered_map<u32, vk::ImageView> view_cache; | 199 | std::unordered_map<u32, vk::ImageView> view_cache; |
| 209 | }; | 200 | }; |
| 210 | 201 | ||
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp index 681ecde98..351c048d2 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp | |||
| @@ -24,35 +24,25 @@ void VKUpdateDescriptorQueue::TickFrame() { | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | void VKUpdateDescriptorQueue::Acquire() { | 26 | void VKUpdateDescriptorQueue::Acquire() { |
| 27 | entries.clear(); | 27 | // Minimum number of entries required. |
| 28 | } | 28 | // This is the maximum number of entries a single draw call migth use. |
| 29 | static constexpr std::size_t MIN_ENTRIES = 0x400; | ||
| 29 | 30 | ||
| 30 | void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, | 31 | if (payload.size() + MIN_ENTRIES >= payload.max_size()) { |
| 31 | VkDescriptorSet set) { | ||
| 32 | if (payload.size() + entries.size() >= payload.max_size()) { | ||
| 33 | LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); | 32 | LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); |
| 34 | scheduler.WaitWorker(); | 33 | scheduler.WaitWorker(); |
| 35 | payload.clear(); | 34 | payload.clear(); |
| 36 | } | 35 | } |
| 36 | upload_start = &*payload.end(); | ||
| 37 | } | ||
| 37 | 38 | ||
| 38 | // TODO(Rodrigo): Rework to write the payload directly | 39 | void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, |
| 39 | const auto payload_start = payload.data() + payload.size(); | 40 | VkDescriptorSet set) { |
| 40 | for (const auto& entry : entries) { | 41 | const void* const data = upload_start; |
| 41 | if (const auto image = std::get_if<VkDescriptorImageInfo>(&entry)) { | 42 | const vk::Device* const logical = &device.GetLogical(); |
| 42 | payload.push_back(*image); | 43 | scheduler.Record([data, logical, set, update_template](vk::CommandBuffer) { |
| 43 | } else if (const auto buffer = std::get_if<VkDescriptorBufferInfo>(&entry)) { | 44 | logical->UpdateDescriptorSet(set, update_template, data); |
| 44 | payload.push_back(*buffer); | 45 | }); |
| 45 | } else if (const auto texel = std::get_if<VkBufferView>(&entry)) { | ||
| 46 | payload.push_back(*texel); | ||
| 47 | } else { | ||
| 48 | UNREACHABLE(); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | scheduler.Record( | ||
| 53 | [payload_start, set, update_template, logical = &device.GetLogical()](vk::CommandBuffer) { | ||
| 54 | logical->UpdateDescriptorSet(set, update_template, payload_start); | ||
| 55 | }); | ||
| 56 | } | 46 | } |
| 57 | 47 | ||
| 58 | } // namespace Vulkan | 48 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index cc7e3dff4..945320c72 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h | |||
| @@ -15,17 +15,13 @@ namespace Vulkan { | |||
| 15 | class VKDevice; | 15 | class VKDevice; |
| 16 | class VKScheduler; | 16 | class VKScheduler; |
| 17 | 17 | ||
| 18 | class DescriptorUpdateEntry { | 18 | struct DescriptorUpdateEntry { |
| 19 | public: | 19 | DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} |
| 20 | explicit DescriptorUpdateEntry() {} | ||
| 21 | |||
| 22 | DescriptorUpdateEntry(VkDescriptorImageInfo image) : image{image} {} | ||
| 23 | 20 | ||
| 24 | DescriptorUpdateEntry(VkDescriptorBufferInfo buffer) : buffer{buffer} {} | 21 | DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} |
| 25 | 22 | ||
| 26 | DescriptorUpdateEntry(VkBufferView texel_buffer) : texel_buffer{texel_buffer} {} | 23 | DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} |
| 27 | 24 | ||
| 28 | private: | ||
| 29 | union { | 25 | union { |
| 30 | VkDescriptorImageInfo image; | 26 | VkDescriptorImageInfo image; |
| 31 | VkDescriptorBufferInfo buffer; | 27 | VkDescriptorBufferInfo buffer; |
| @@ -45,32 +41,34 @@ public: | |||
| 45 | void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); | 41 | void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); |
| 46 | 42 | ||
| 47 | void AddSampledImage(VkSampler sampler, VkImageView image_view) { | 43 | void AddSampledImage(VkSampler sampler, VkImageView image_view) { |
| 48 | entries.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}}); | 44 | payload.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}}); |
| 49 | } | 45 | } |
| 50 | 46 | ||
| 51 | void AddImage(VkImageView image_view) { | 47 | void AddImage(VkImageView image_view) { |
| 52 | entries.emplace_back(VkDescriptorImageInfo{{}, image_view, {}}); | 48 | payload.emplace_back(VkDescriptorImageInfo{{}, image_view, {}}); |
| 53 | } | 49 | } |
| 54 | 50 | ||
| 55 | void AddBuffer(VkBuffer buffer, u64 offset, std::size_t size) { | 51 | void AddBuffer(VkBuffer buffer, u64 offset, std::size_t size) { |
| 56 | entries.emplace_back(VkDescriptorBufferInfo{buffer, offset, size}); | 52 | payload.emplace_back(VkDescriptorBufferInfo{buffer, offset, size}); |
| 57 | } | 53 | } |
| 58 | 54 | ||
| 59 | void AddTexelBuffer(VkBufferView texel_buffer) { | 55 | void AddTexelBuffer(VkBufferView texel_buffer) { |
| 60 | entries.emplace_back(texel_buffer); | 56 | payload.emplace_back(texel_buffer); |
| 61 | } | 57 | } |
| 62 | 58 | ||
| 63 | VkImageLayout* GetLastImageLayout() { | 59 | VkImageLayout* LastImageLayout() { |
| 64 | return &std::get<VkDescriptorImageInfo>(entries.back()).imageLayout; | 60 | return &payload.back().image.imageLayout; |
| 65 | } | 61 | } |
| 66 | 62 | ||
| 67 | private: | 63 | const VkImageLayout* LastImageLayout() const { |
| 68 | using Variant = std::variant<VkDescriptorImageInfo, VkDescriptorBufferInfo, VkBufferView>; | 64 | return &payload.back().image.imageLayout; |
| 65 | } | ||
| 69 | 66 | ||
| 67 | private: | ||
| 70 | const VKDevice& device; | 68 | const VKDevice& device; |
| 71 | VKScheduler& scheduler; | 69 | VKScheduler& scheduler; |
| 72 | 70 | ||
| 73 | boost::container::static_vector<Variant, 0x400> entries; | 71 | const DescriptorUpdateEntry* upload_start = nullptr; |
| 74 | boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload; | 72 | boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload; |
| 75 | }; | 73 | }; |
| 76 | 74 | ||
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp index 2ce9b0626..051298cc8 100644 --- a/src/video_core/renderer_vulkan/wrapper.cpp +++ b/src/video_core/renderer_vulkan/wrapper.cpp | |||
| @@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 88 | X(vkCmdSetStencilWriteMask); | 88 | X(vkCmdSetStencilWriteMask); |
| 89 | X(vkCmdSetViewport); | 89 | X(vkCmdSetViewport); |
| 90 | X(vkCmdWaitEvents); | 90 | X(vkCmdWaitEvents); |
| 91 | X(vkCmdBindVertexBuffers2EXT); | ||
| 92 | X(vkCmdSetCullModeEXT); | ||
| 93 | X(vkCmdSetDepthBoundsTestEnableEXT); | ||
| 94 | X(vkCmdSetDepthCompareOpEXT); | ||
| 95 | X(vkCmdSetDepthTestEnableEXT); | ||
| 96 | X(vkCmdSetDepthWriteEnableEXT); | ||
| 97 | X(vkCmdSetFrontFaceEXT); | ||
| 98 | X(vkCmdSetPrimitiveTopologyEXT); | ||
| 99 | X(vkCmdSetStencilOpEXT); | ||
| 100 | X(vkCmdSetStencilTestEnableEXT); | ||
| 91 | X(vkCreateBuffer); | 101 | X(vkCreateBuffer); |
| 92 | X(vkCreateBufferView); | 102 | X(vkCreateBufferView); |
| 93 | X(vkCreateCommandPool); | 103 | X(vkCreateCommandPool); |
| @@ -153,7 +163,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 153 | 163 | ||
| 154 | bool Load(InstanceDispatch& dld) noexcept { | 164 | bool Load(InstanceDispatch& dld) noexcept { |
| 155 | #define X(name) Proc(dld.name, dld, #name) | 165 | #define X(name) Proc(dld.name, dld, #name) |
| 156 | return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties); | 166 | return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) && |
| 167 | X(vkEnumerateInstanceLayerProperties); | ||
| 157 | #undef X | 168 | #undef X |
| 158 | } | 169 | } |
| 159 | 170 | ||
| @@ -725,8 +736,7 @@ bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR s | |||
| 725 | return supported == VK_TRUE; | 736 | return supported == VK_TRUE; |
| 726 | } | 737 | } |
| 727 | 738 | ||
| 728 | VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const | 739 | VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const { |
| 729 | noexcept { | ||
| 730 | VkSurfaceCapabilitiesKHR capabilities; | 740 | VkSurfaceCapabilitiesKHR capabilities; |
| 731 | Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities)); | 741 | Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities)); |
| 732 | return capabilities; | 742 | return capabilities; |
| @@ -771,4 +781,17 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp | |||
| 771 | return properties; | 781 | return properties; |
| 772 | } | 782 | } |
| 773 | 783 | ||
| 784 | std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( | ||
| 785 | const InstanceDispatch& dld) { | ||
| 786 | u32 num; | ||
| 787 | if (dld.vkEnumerateInstanceLayerProperties(&num, nullptr) != VK_SUCCESS) { | ||
| 788 | return std::nullopt; | ||
| 789 | } | ||
| 790 | std::vector<VkLayerProperties> properties(num); | ||
| 791 | if (dld.vkEnumerateInstanceLayerProperties(&num, properties.data()) != VK_SUCCESS) { | ||
| 792 | return std::nullopt; | ||
| 793 | } | ||
| 794 | return properties; | ||
| 795 | } | ||
| 796 | |||
| 774 | } // namespace Vulkan::vk | 797 | } // namespace Vulkan::vk |
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h index 98937a77a..71daac9d7 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/renderer_vulkan/wrapper.h | |||
| @@ -141,6 +141,7 @@ struct InstanceDispatch { | |||
| 141 | PFN_vkCreateInstance vkCreateInstance; | 141 | PFN_vkCreateInstance vkCreateInstance; |
| 142 | PFN_vkDestroyInstance vkDestroyInstance; | 142 | PFN_vkDestroyInstance vkDestroyInstance; |
| 143 | PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; | 143 | PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; |
| 144 | PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; | ||
| 144 | 145 | ||
| 145 | PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; | 146 | PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; |
| 146 | PFN_vkCreateDevice vkCreateDevice; | 147 | PFN_vkCreateDevice vkCreateDevice; |
| @@ -206,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch { | |||
| 206 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; | 207 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; |
| 207 | PFN_vkCmdSetViewport vkCmdSetViewport; | 208 | PFN_vkCmdSetViewport vkCmdSetViewport; |
| 208 | PFN_vkCmdWaitEvents vkCmdWaitEvents; | 209 | PFN_vkCmdWaitEvents vkCmdWaitEvents; |
| 210 | PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; | ||
| 211 | PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; | ||
| 212 | PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; | ||
| 213 | PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; | ||
| 214 | PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; | ||
| 215 | PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; | ||
| 216 | PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; | ||
| 217 | PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; | ||
| 218 | PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; | ||
| 219 | PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; | ||
| 209 | PFN_vkCreateBuffer vkCreateBuffer; | 220 | PFN_vkCreateBuffer vkCreateBuffer; |
| 210 | PFN_vkCreateBufferView vkCreateBufferView; | 221 | PFN_vkCreateBufferView vkCreateBufferView; |
| 211 | PFN_vkCreateCommandPool vkCreateCommandPool; | 222 | PFN_vkCreateCommandPool vkCreateCommandPool; |
| @@ -779,7 +790,7 @@ public: | |||
| 779 | 790 | ||
| 780 | bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; | 791 | bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; |
| 781 | 792 | ||
| 782 | VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const noexcept; | 793 | VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const; |
| 783 | 794 | ||
| 784 | std::vector<VkSurfaceFormatKHR> GetSurfaceFormatsKHR(VkSurfaceKHR) const; | 795 | std::vector<VkSurfaceFormatKHR> GetSurfaceFormatsKHR(VkSurfaceKHR) const; |
| 785 | 796 | ||
| @@ -968,6 +979,50 @@ public: | |||
| 968 | buffer_barriers.data(), image_barriers.size(), image_barriers.data()); | 979 | buffer_barriers.data(), image_barriers.size(), image_barriers.data()); |
| 969 | } | 980 | } |
| 970 | 981 | ||
| 982 | void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers, | ||
| 983 | const VkDeviceSize* offsets, const VkDeviceSize* sizes, | ||
| 984 | const VkDeviceSize* strides) const noexcept { | ||
| 985 | dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets, | ||
| 986 | sizes, strides); | ||
| 987 | } | ||
| 988 | |||
| 989 | void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept { | ||
| 990 | dld->vkCmdSetCullModeEXT(handle, cull_mode); | ||
| 991 | } | ||
| 992 | |||
| 993 | void SetDepthBoundsTestEnableEXT(bool enable) const noexcept { | ||
| 994 | dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 995 | } | ||
| 996 | |||
| 997 | void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept { | ||
| 998 | dld->vkCmdSetDepthCompareOpEXT(handle, compare_op); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | void SetDepthTestEnableEXT(bool enable) const noexcept { | ||
| 1002 | dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | void SetDepthWriteEnableEXT(bool enable) const noexcept { | ||
| 1006 | dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { | ||
| 1010 | dld->vkCmdSetFrontFaceEXT(handle, front_face); | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept { | ||
| 1014 | dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op, | ||
| 1018 | VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept { | ||
| 1019 | dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | void SetStencilTestEnableEXT(bool enable) const noexcept { | ||
| 1023 | dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1024 | } | ||
| 1025 | |||
| 971 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, | 1026 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, |
| 972 | const VkDeviceSize* offsets, | 1027 | const VkDeviceSize* offsets, |
| 973 | const VkDeviceSize* sizes) const noexcept { | 1028 | const VkDeviceSize* sizes) const noexcept { |
| @@ -996,4 +1051,7 @@ private: | |||
| 996 | std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( | 1051 | std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( |
| 997 | const InstanceDispatch& dld); | 1052 | const InstanceDispatch& dld); |
| 998 | 1053 | ||
| 1054 | std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( | ||
| 1055 | const InstanceDispatch& dld); | ||
| 1056 | |||
| 999 | } // namespace Vulkan::vk | 1057 | } // namespace Vulkan::vk |
diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp index 848e46874..b2e88fa20 100644 --- a/src/video_core/shader/decode/half_set.cpp +++ b/src/video_core/shader/decode/half_set.cpp | |||
| @@ -13,55 +13,101 @@ | |||
| 13 | 13 | ||
| 14 | namespace VideoCommon::Shader { | 14 | namespace VideoCommon::Shader { |
| 15 | 15 | ||
| 16 | using std::move; | ||
| 16 | using Tegra::Shader::Instruction; | 17 | using Tegra::Shader::Instruction; |
| 17 | using Tegra::Shader::OpCode; | 18 | using Tegra::Shader::OpCode; |
| 19 | using Tegra::Shader::PredCondition; | ||
| 18 | 20 | ||
| 19 | u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { | 21 | u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { |
| 20 | const Instruction instr = {program_code[pc]}; | 22 | const Instruction instr = {program_code[pc]}; |
| 21 | const auto opcode = OpCode::Decode(instr); | 23 | const auto opcode = OpCode::Decode(instr); |
| 22 | 24 | ||
| 23 | if (instr.hset2.ftz == 0) { | 25 | PredCondition cond; |
| 24 | LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); | 26 | bool bf; |
| 27 | bool ftz; | ||
| 28 | bool neg_a; | ||
| 29 | bool abs_a; | ||
| 30 | bool neg_b; | ||
| 31 | bool abs_b; | ||
| 32 | switch (opcode->get().GetId()) { | ||
| 33 | case OpCode::Id::HSET2_C: | ||
| 34 | case OpCode::Id::HSET2_IMM: | ||
| 35 | cond = instr.hsetp2.cbuf_and_imm.cond; | ||
| 36 | bf = instr.Bit(53); | ||
| 37 | ftz = instr.Bit(54); | ||
| 38 | neg_a = instr.Bit(43); | ||
| 39 | abs_a = instr.Bit(44); | ||
| 40 | neg_b = instr.Bit(56); | ||
| 41 | abs_b = instr.Bit(54); | ||
| 42 | break; | ||
| 43 | case OpCode::Id::HSET2_R: | ||
| 44 | cond = instr.hsetp2.reg.cond; | ||
| 45 | bf = instr.Bit(49); | ||
| 46 | ftz = instr.Bit(50); | ||
| 47 | neg_a = instr.Bit(43); | ||
| 48 | abs_a = instr.Bit(44); | ||
| 49 | neg_b = instr.Bit(31); | ||
| 50 | abs_b = instr.Bit(30); | ||
| 51 | break; | ||
| 52 | default: | ||
| 53 | UNREACHABLE(); | ||
| 25 | } | 54 | } |
| 26 | 55 | ||
| 27 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); | 56 | Node op_b = [this, instr, opcode] { |
| 28 | op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a); | ||
| 29 | |||
| 30 | Node op_b = [&]() { | ||
| 31 | switch (opcode->get().GetId()) { | 57 | switch (opcode->get().GetId()) { |
| 58 | case OpCode::Id::HSET2_C: | ||
| 59 | // Inform as unimplemented as this is not tested. | ||
| 60 | UNIMPLEMENTED_MSG("HSET2_C is not implemented"); | ||
| 61 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | ||
| 32 | case OpCode::Id::HSET2_R: | 62 | case OpCode::Id::HSET2_R: |
| 33 | return GetRegister(instr.gpr20); | 63 | return GetRegister(instr.gpr20); |
| 64 | case OpCode::Id::HSET2_IMM: | ||
| 65 | return UnpackHalfImmediate(instr, true); | ||
| 34 | default: | 66 | default: |
| 35 | UNREACHABLE(); | 67 | UNREACHABLE(); |
| 36 | return Immediate(0); | 68 | return Node{}; |
| 37 | } | 69 | } |
| 38 | }(); | 70 | }(); |
| 39 | op_b = UnpackHalfFloat(op_b, instr.hset2.type_b); | ||
| 40 | op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b); | ||
| 41 | 71 | ||
| 42 | const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); | 72 | if (!ftz) { |
| 73 | LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); | ||
| 74 | } | ||
| 75 | |||
| 76 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); | ||
| 77 | op_a = GetOperandAbsNegHalf(op_a, abs_a, neg_a); | ||
| 78 | |||
| 79 | switch (opcode->get().GetId()) { | ||
| 80 | case OpCode::Id::HSET2_R: | ||
| 81 | op_b = GetOperandAbsNegHalf(move(op_b), abs_b, neg_b); | ||
| 82 | [[fallthrough]]; | ||
| 83 | case OpCode::Id::HSET2_C: | ||
| 84 | op_b = UnpackHalfFloat(move(op_b), instr.hset2.type_b); | ||
| 85 | break; | ||
| 86 | default: | ||
| 87 | break; | ||
| 88 | } | ||
| 43 | 89 | ||
| 44 | const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, op_a, op_b); | 90 | Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); |
| 91 | |||
| 92 | Node comparison_pair = GetPredicateComparisonHalf(cond, op_a, op_b); | ||
| 45 | 93 | ||
| 46 | const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); | 94 | const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); |
| 47 | 95 | ||
| 48 | // HSET2 operates on each half float in the pack. | 96 | // HSET2 operates on each half float in the pack. |
| 49 | std::array<Node, 2> values; | 97 | std::array<Node, 2> values; |
| 50 | for (u32 i = 0; i < 2; ++i) { | 98 | for (u32 i = 0; i < 2; ++i) { |
| 51 | const u32 raw_value = instr.hset2.bf ? 0x3c00 : 0xffff; | 99 | const u32 raw_value = bf ? 0x3c00 : 0xffff; |
| 52 | const Node true_value = Immediate(raw_value << (i * 16)); | 100 | Node true_value = Immediate(raw_value << (i * 16)); |
| 53 | const Node false_value = Immediate(0); | 101 | Node false_value = Immediate(0); |
| 54 | |||
| 55 | const Node comparison = | ||
| 56 | Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i)); | ||
| 57 | const Node predicate = Operation(combiner, comparison, second_pred); | ||
| 58 | 102 | ||
| 103 | Node comparison = Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i)); | ||
| 104 | Node predicate = Operation(combiner, comparison, second_pred); | ||
| 59 | values[i] = | 105 | values[i] = |
| 60 | Operation(OperationCode::Select, NO_PRECISE, predicate, true_value, false_value); | 106 | Operation(OperationCode::Select, predicate, move(true_value), move(false_value)); |
| 61 | } | 107 | } |
| 62 | 108 | ||
| 63 | const Node value = Operation(OperationCode::UBitwiseOr, NO_PRECISE, values[0], values[1]); | 109 | Node value = Operation(OperationCode::UBitwiseOr, values[0], values[1]); |
| 64 | SetRegister(bb, instr.gpr0, value); | 110 | SetRegister(bb, instr.gpr0, move(value)); |
| 65 | 111 | ||
| 66 | return pc; | 112 | return pc; |
| 67 | } | 113 | } |
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index 60b6ad72a..07778dc3e 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp | |||
| @@ -97,6 +97,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, | |||
| 97 | break; | 97 | break; |
| 98 | case TextureFormat::B5G6R5: | 98 | case TextureFormat::B5G6R5: |
| 99 | case TextureFormat::B6G5R5: | 99 | case TextureFormat::B6G5R5: |
| 100 | case TextureFormat::BF10GF11RF11: | ||
| 100 | if (component == 0) { | 101 | if (component == 0) { |
| 101 | return descriptor.b_type; | 102 | return descriptor.b_type; |
| 102 | } | 103 | } |
| @@ -119,7 +120,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, | |||
| 119 | } | 120 | } |
| 120 | break; | 121 | break; |
| 121 | } | 122 | } |
| 122 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | 123 | UNIMPLEMENTED_MSG("Texture format not implemented={}", format); |
| 123 | return ComponentType::FLOAT; | 124 | return ComponentType::FLOAT; |
| 124 | } | 125 | } |
| 125 | 126 | ||
| @@ -191,6 +192,14 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) { | |||
| 191 | return 6; | 192 | return 6; |
| 192 | } | 193 | } |
| 193 | return 0; | 194 | return 0; |
| 195 | case TextureFormat::BF10GF11RF11: | ||
| 196 | if (component == 1 || component == 2) { | ||
| 197 | return 11; | ||
| 198 | } | ||
| 199 | if (component == 0) { | ||
| 200 | return 10; | ||
| 201 | } | ||
| 202 | return 0; | ||
| 194 | case TextureFormat::G8R24: | 203 | case TextureFormat::G8R24: |
| 195 | if (component == 0) { | 204 | if (component == 0) { |
| 196 | return 8; | 205 | return 8; |
| @@ -211,10 +220,9 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) { | |||
| 211 | return (component == 0 || component == 1) ? 8 : 0; | 220 | return (component == 0 || component == 1) ? 8 : 0; |
| 212 | case TextureFormat::G4R4: | 221 | case TextureFormat::G4R4: |
| 213 | return (component == 0 || component == 1) ? 4 : 0; | 222 | return (component == 0 || component == 1) ? 4 : 0; |
| 214 | default: | ||
| 215 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | ||
| 216 | return 0; | ||
| 217 | } | 223 | } |
| 224 | UNIMPLEMENTED_MSG("Texture format not implemented={}", format); | ||
| 225 | return 0; | ||
| 218 | } | 226 | } |
| 219 | 227 | ||
| 220 | std::size_t GetImageComponentMask(TextureFormat format) { | 228 | std::size_t GetImageComponentMask(TextureFormat format) { |
| @@ -235,6 +243,7 @@ std::size_t GetImageComponentMask(TextureFormat format) { | |||
| 235 | case TextureFormat::R32_B24G8: | 243 | case TextureFormat::R32_B24G8: |
| 236 | case TextureFormat::B5G6R5: | 244 | case TextureFormat::B5G6R5: |
| 237 | case TextureFormat::B6G5R5: | 245 | case TextureFormat::B6G5R5: |
| 246 | case TextureFormat::BF10GF11RF11: | ||
| 238 | return std::size_t{R | G | B}; | 247 | return std::size_t{R | G | B}; |
| 239 | case TextureFormat::R32_G32: | 248 | case TextureFormat::R32_G32: |
| 240 | case TextureFormat::R16_G16: | 249 | case TextureFormat::R16_G16: |
| @@ -248,10 +257,9 @@ std::size_t GetImageComponentMask(TextureFormat format) { | |||
| 248 | case TextureFormat::R8: | 257 | case TextureFormat::R8: |
| 249 | case TextureFormat::R1: | 258 | case TextureFormat::R1: |
| 250 | return std::size_t{R}; | 259 | return std::size_t{R}; |
| 251 | default: | ||
| 252 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | ||
| 253 | return std::size_t{R | G | B | A}; | ||
| 254 | } | 260 | } |
| 261 | UNIMPLEMENTED_MSG("Texture format not implemented={}", format); | ||
| 262 | return std::size_t{R | G | B | A}; | ||
| 255 | } | 263 | } |
| 256 | 264 | ||
| 257 | std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { | 265 | std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { |
| @@ -299,7 +307,7 @@ std::pair<Node, bool> ShaderIR::GetComponentValue(ComponentType component_type, | |||
| 299 | return {std::move(original_value), true}; | 307 | return {std::move(original_value), true}; |
| 300 | } | 308 | } |
| 301 | default: | 309 | default: |
| 302 | UNIMPLEMENTED_MSG("Unimplement component type={}", component_type); | 310 | UNIMPLEMENTED_MSG("Unimplemented component type={}", component_type); |
| 303 | return {std::move(original_value), true}; | 311 | return {std::move(original_value), true}; |
| 304 | } | 312 | } |
| 305 | } | 313 | } |
| @@ -459,7 +467,7 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | |||
| 459 | default: | 467 | default: |
| 460 | break; | 468 | break; |
| 461 | } | 469 | } |
| 462 | UNIMPLEMENTED_MSG("Unimplemented operation={} type={}", | 470 | UNIMPLEMENTED_MSG("Unimplemented operation={}, type={}", |
| 463 | static_cast<u64>(instr.suatom_d.operation.Value()), | 471 | static_cast<u64>(instr.suatom_d.operation.Value()), |
| 464 | static_cast<u64>(instr.suatom_d.operation_type.Value())); | 472 | static_cast<u64>(instr.suatom_d.operation_type.Value())); |
| 465 | return OperationCode::AtomicImageAdd; | 473 | return OperationCode::AtomicImageAdd; |
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index d00e10913..c0a8f233f 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp | |||
| @@ -83,7 +83,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 83 | return Operation(OperationCode::YNegate); | 83 | return Operation(OperationCode::YNegate); |
| 84 | case SystemVariable::InvocationInfo: | 84 | case SystemVariable::InvocationInfo: |
| 85 | LOG_WARNING(HW_GPU, "S2R instruction with InvocationInfo is incomplete"); | 85 | LOG_WARNING(HW_GPU, "S2R instruction with InvocationInfo is incomplete"); |
| 86 | return Immediate(0U); | 86 | return Immediate(0x00ff'0000U); |
| 87 | case SystemVariable::WscaleFactorXY: | 87 | case SystemVariable::WscaleFactorXY: |
| 88 | UNIMPLEMENTED_MSG("S2R WscaleFactorXY is not implemented"); | 88 | UNIMPLEMENTED_MSG("S2R WscaleFactorXY is not implemented"); |
| 89 | return Immediate(0U); | 89 | return Immediate(0U); |
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index 8f0bb996e..29ebf65ba 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp | |||
| @@ -357,13 +357,11 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 357 | return pc; | 357 | return pc; |
| 358 | } | 358 | } |
| 359 | 359 | ||
| 360 | ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(SamplerInfo info, u32 offset, | 360 | ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo( |
| 361 | std::optional<u32> buffer) { | 361 | SamplerInfo info, std::optional<Tegra::Engines::SamplerDescriptor> sampler) { |
| 362 | if (info.IsComplete()) { | 362 | if (info.IsComplete()) { |
| 363 | return info; | 363 | return info; |
| 364 | } | 364 | } |
| 365 | const auto sampler = buffer ? registry.ObtainBindlessSampler(*buffer, offset) | ||
| 366 | : registry.ObtainBoundSampler(offset); | ||
| 367 | if (!sampler) { | 365 | if (!sampler) { |
| 368 | LOG_WARNING(HW_GPU, "Unknown sampler info"); | 366 | LOG_WARNING(HW_GPU, "Unknown sampler info"); |
| 369 | info.type = info.type.value_or(Tegra::Shader::TextureType::Texture2D); | 367 | info.type = info.type.value_or(Tegra::Shader::TextureType::Texture2D); |
| @@ -381,8 +379,8 @@ ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(SamplerInfo info, u32 offset, | |||
| 381 | 379 | ||
| 382 | std::optional<Sampler> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler, | 380 | std::optional<Sampler> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler, |
| 383 | SamplerInfo sampler_info) { | 381 | SamplerInfo sampler_info) { |
| 384 | const auto offset = static_cast<u32>(sampler.index.Value()); | 382 | const u32 offset = static_cast<u32>(sampler.index.Value()); |
| 385 | const auto info = GetSamplerInfo(sampler_info, offset); | 383 | const auto info = GetSamplerInfo(sampler_info, registry.ObtainBoundSampler(offset)); |
| 386 | 384 | ||
| 387 | // If this sampler has already been used, return the existing mapping. | 385 | // If this sampler has already been used, return the existing mapping. |
| 388 | const auto it = std::find_if(used_samplers.begin(), used_samplers.end(), | 386 | const auto it = std::find_if(used_samplers.begin(), used_samplers.end(), |
| @@ -404,20 +402,19 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, | |||
| 404 | const Node sampler_register = GetRegister(reg); | 402 | const Node sampler_register = GetRegister(reg); |
| 405 | const auto [base_node, tracked_sampler_info] = | 403 | const auto [base_node, tracked_sampler_info] = |
| 406 | TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size())); | 404 | TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size())); |
| 407 | ASSERT(base_node != nullptr); | 405 | if (!base_node) { |
| 408 | if (base_node == nullptr) { | 406 | UNREACHABLE(); |
| 409 | return std::nullopt; | 407 | return std::nullopt; |
| 410 | } | 408 | } |
| 411 | 409 | ||
| 412 | if (const auto bindless_sampler_info = | 410 | if (const auto sampler_info = std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) { |
| 413 | std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) { | 411 | const u32 buffer = sampler_info->index; |
| 414 | const u32 buffer = bindless_sampler_info->GetIndex(); | 412 | const u32 offset = sampler_info->offset; |
| 415 | const u32 offset = bindless_sampler_info->GetOffset(); | 413 | info = GetSamplerInfo(info, registry.ObtainBindlessSampler(buffer, offset)); |
| 416 | info = GetSamplerInfo(info, offset, buffer); | ||
| 417 | 414 | ||
| 418 | // If this sampler has already been used, return the existing mapping. | 415 | // If this sampler has already been used, return the existing mapping. |
| 419 | const auto it = std::find_if(used_samplers.begin(), used_samplers.end(), | 416 | const auto it = std::find_if(used_samplers.begin(), used_samplers.end(), |
| 420 | [buffer = buffer, offset = offset](const Sampler& entry) { | 417 | [buffer, offset](const Sampler& entry) { |
| 421 | return entry.buffer == buffer && entry.offset == offset; | 418 | return entry.buffer == buffer && entry.offset == offset; |
| 422 | }); | 419 | }); |
| 423 | if (it != used_samplers.end()) { | 420 | if (it != used_samplers.end()) { |
| @@ -431,10 +428,32 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, | |||
| 431 | return used_samplers.emplace_back(next_index, offset, buffer, *info.type, *info.is_array, | 428 | return used_samplers.emplace_back(next_index, offset, buffer, *info.type, *info.is_array, |
| 432 | *info.is_shadow, *info.is_buffer, false); | 429 | *info.is_shadow, *info.is_buffer, false); |
| 433 | } | 430 | } |
| 434 | if (const auto array_sampler_info = std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) { | 431 | if (const auto sampler_info = std::get_if<SeparateSamplerNode>(&*tracked_sampler_info)) { |
| 435 | const u32 base_offset = array_sampler_info->GetBaseOffset() / 4; | 432 | const std::pair indices = sampler_info->indices; |
| 436 | index_var = GetCustomVariable(array_sampler_info->GetIndexVar()); | 433 | const std::pair offsets = sampler_info->offsets; |
| 437 | info = GetSamplerInfo(info, base_offset); | 434 | info = GetSamplerInfo(info, registry.ObtainSeparateSampler(indices, offsets)); |
| 435 | |||
| 436 | // Try to use an already created sampler if it exists | ||
| 437 | const auto it = std::find_if( | ||
| 438 | used_samplers.begin(), used_samplers.end(), [indices, offsets](const Sampler& entry) { | ||
| 439 | return offsets == std::pair{entry.offset, entry.secondary_offset} && | ||
| 440 | indices == std::pair{entry.buffer, entry.secondary_buffer}; | ||
| 441 | }); | ||
| 442 | if (it != used_samplers.end()) { | ||
| 443 | ASSERT(it->is_separated && it->type == info.type && it->is_array == info.is_array && | ||
| 444 | it->is_shadow == info.is_shadow && it->is_buffer == info.is_buffer); | ||
| 445 | return *it; | ||
| 446 | } | ||
| 447 | |||
| 448 | // Otherwise create a new mapping for this sampler | ||
| 449 | const u32 next_index = static_cast<u32>(used_samplers.size()); | ||
| 450 | return used_samplers.emplace_back(next_index, offsets, indices, *info.type, *info.is_array, | ||
| 451 | *info.is_shadow, *info.is_buffer); | ||
| 452 | } | ||
| 453 | if (const auto sampler_info = std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) { | ||
| 454 | const u32 base_offset = sampler_info->base_offset / 4; | ||
| 455 | index_var = GetCustomVariable(sampler_info->bindless_var); | ||
| 456 | info = GetSamplerInfo(info, registry.ObtainBoundSampler(base_offset)); | ||
| 438 | 457 | ||
| 439 | // If this sampler has already been used, return the existing mapping. | 458 | // If this sampler has already been used, return the existing mapping. |
| 440 | const auto it = std::find_if( | 459 | const auto it = std::find_if( |
diff --git a/src/video_core/shader/memory_util.cpp b/src/video_core/shader/memory_util.cpp index 074f21691..5071c83ca 100644 --- a/src/video_core/shader/memory_util.cpp +++ b/src/video_core/shader/memory_util.cpp | |||
| @@ -66,12 +66,12 @@ ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, GPUVAddr gpu_add | |||
| 66 | 66 | ||
| 67 | u64 GetUniqueIdentifier(Tegra::Engines::ShaderType shader_type, bool is_a, const ProgramCode& code, | 67 | u64 GetUniqueIdentifier(Tegra::Engines::ShaderType shader_type, bool is_a, const ProgramCode& code, |
| 68 | const ProgramCode& code_b) { | 68 | const ProgramCode& code_b) { |
| 69 | u64 unique_identifier = boost::hash_value(code); | 69 | size_t unique_identifier = boost::hash_value(code); |
| 70 | if (is_a) { | 70 | if (is_a) { |
| 71 | // VertexA programs include two programs | 71 | // VertexA programs include two programs |
| 72 | boost::hash_combine(unique_identifier, boost::hash_value(code_b)); | 72 | boost::hash_combine(unique_identifier, boost::hash_value(code_b)); |
| 73 | } | 73 | } |
| 74 | return unique_identifier; | 74 | return static_cast<u64>(unique_identifier); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | } // namespace VideoCommon::Shader | 77 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index c5e5165ff..8f230d57a 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h | |||
| @@ -275,10 +275,11 @@ using Node = std::shared_ptr<NodeData>; | |||
| 275 | using Node4 = std::array<Node, 4>; | 275 | using Node4 = std::array<Node, 4>; |
| 276 | using NodeBlock = std::vector<Node>; | 276 | using NodeBlock = std::vector<Node>; |
| 277 | 277 | ||
| 278 | class BindlessSamplerNode; | 278 | struct ArraySamplerNode; |
| 279 | class ArraySamplerNode; | 279 | struct BindlessSamplerNode; |
| 280 | struct SeparateSamplerNode; | ||
| 280 | 281 | ||
| 281 | using TrackSamplerData = std::variant<BindlessSamplerNode, ArraySamplerNode>; | 282 | using TrackSamplerData = std::variant<BindlessSamplerNode, SeparateSamplerNode, ArraySamplerNode>; |
| 282 | using TrackSampler = std::shared_ptr<TrackSamplerData>; | 283 | using TrackSampler = std::shared_ptr<TrackSamplerData>; |
| 283 | 284 | ||
| 284 | struct Sampler { | 285 | struct Sampler { |
| @@ -288,63 +289,51 @@ struct Sampler { | |||
| 288 | : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, | 289 | : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, |
| 289 | is_buffer{is_buffer}, is_indexed{is_indexed} {} | 290 | is_buffer{is_buffer}, is_indexed{is_indexed} {} |
| 290 | 291 | ||
| 292 | /// Separate sampler constructor | ||
| 293 | constexpr explicit Sampler(u32 index, std::pair<u32, u32> offsets, std::pair<u32, u32> buffers, | ||
| 294 | Tegra::Shader::TextureType type, bool is_array, bool is_shadow, | ||
| 295 | bool is_buffer) | ||
| 296 | : index{index}, offset{offsets.first}, secondary_offset{offsets.second}, | ||
| 297 | buffer{buffers.first}, secondary_buffer{buffers.second}, type{type}, is_array{is_array}, | ||
| 298 | is_shadow{is_shadow}, is_buffer{is_buffer}, is_separated{true} {} | ||
| 299 | |||
| 291 | /// Bindless samplers constructor | 300 | /// Bindless samplers constructor |
| 292 | constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, | 301 | constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, |
| 293 | bool is_array, bool is_shadow, bool is_buffer, bool is_indexed) | 302 | bool is_array, bool is_shadow, bool is_buffer, bool is_indexed) |
| 294 | : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, | 303 | : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, |
| 295 | is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {} | 304 | is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {} |
| 296 | 305 | ||
| 297 | u32 index = 0; ///< Emulated index given for the this sampler. | 306 | u32 index = 0; ///< Emulated index given for the this sampler. |
| 298 | u32 offset = 0; ///< Offset in the const buffer from where the sampler is being read. | 307 | u32 offset = 0; ///< Offset in the const buffer from where the sampler is being read. |
| 299 | u32 buffer = 0; ///< Buffer where the bindless sampler is being read (unused on bound samplers). | 308 | u32 secondary_offset = 0; ///< Secondary offset in the const buffer. |
| 300 | u32 size = 1; ///< Size of the sampler. | 309 | u32 buffer = 0; ///< Buffer where the bindless sampler is read. |
| 310 | u32 secondary_buffer = 0; ///< Secondary buffer where the bindless sampler is read. | ||
| 311 | u32 size = 1; ///< Size of the sampler. | ||
| 301 | 312 | ||
| 302 | Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) | 313 | Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) |
| 303 | bool is_array = false; ///< Whether the texture is being sampled as an array texture or not. | 314 | bool is_array = false; ///< Whether the texture is being sampled as an array texture or not. |
| 304 | bool is_shadow = false; ///< Whether the texture is being sampled as a depth texture or not. | 315 | bool is_shadow = false; ///< Whether the texture is being sampled as a depth texture or not. |
| 305 | bool is_buffer = false; ///< Whether the texture is a texture buffer without sampler. | 316 | bool is_buffer = false; ///< Whether the texture is a texture buffer without sampler. |
| 306 | bool is_bindless = false; ///< Whether this sampler belongs to a bindless texture or not. | 317 | bool is_bindless = false; ///< Whether this sampler belongs to a bindless texture or not. |
| 307 | bool is_indexed = false; ///< Whether this sampler is an indexed array of textures. | 318 | bool is_indexed = false; ///< Whether this sampler is an indexed array of textures. |
| 319 | bool is_separated = false; ///< Whether the image and sampler is separated or not. | ||
| 308 | }; | 320 | }; |
| 309 | 321 | ||
| 310 | /// Represents a tracked bindless sampler into a direct const buffer | 322 | /// Represents a tracked bindless sampler into a direct const buffer |
| 311 | class ArraySamplerNode final { | 323 | struct ArraySamplerNode { |
| 312 | public: | ||
| 313 | explicit ArraySamplerNode(u32 index, u32 base_offset, u32 bindless_var) | ||
| 314 | : index{index}, base_offset{base_offset}, bindless_var{bindless_var} {} | ||
| 315 | |||
| 316 | constexpr u32 GetIndex() const { | ||
| 317 | return index; | ||
| 318 | } | ||
| 319 | |||
| 320 | constexpr u32 GetBaseOffset() const { | ||
| 321 | return base_offset; | ||
| 322 | } | ||
| 323 | |||
| 324 | constexpr u32 GetIndexVar() const { | ||
| 325 | return bindless_var; | ||
| 326 | } | ||
| 327 | |||
| 328 | private: | ||
| 329 | u32 index; | 324 | u32 index; |
| 330 | u32 base_offset; | 325 | u32 base_offset; |
| 331 | u32 bindless_var; | 326 | u32 bindless_var; |
| 332 | }; | 327 | }; |
| 333 | 328 | ||
| 334 | /// Represents a tracked bindless sampler into a direct const buffer | 329 | /// Represents a tracked separate sampler image pair that was folded statically |
| 335 | class BindlessSamplerNode final { | 330 | struct SeparateSamplerNode { |
| 336 | public: | 331 | std::pair<u32, u32> indices; |
| 337 | explicit BindlessSamplerNode(u32 index, u32 offset) : index{index}, offset{offset} {} | 332 | std::pair<u32, u32> offsets; |
| 338 | 333 | }; | |
| 339 | constexpr u32 GetIndex() const { | ||
| 340 | return index; | ||
| 341 | } | ||
| 342 | |||
| 343 | constexpr u32 GetOffset() const { | ||
| 344 | return offset; | ||
| 345 | } | ||
| 346 | 334 | ||
| 347 | private: | 335 | /// Represents a tracked bindless sampler into a direct const buffer |
| 336 | struct BindlessSamplerNode { | ||
| 348 | u32 index; | 337 | u32 index; |
| 349 | u32 offset; | 338 | u32 offset; |
| 350 | }; | 339 | }; |
diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h index 11231bbea..1e0886185 100644 --- a/src/video_core/shader/node_helper.h +++ b/src/video_core/shader/node_helper.h | |||
| @@ -48,7 +48,7 @@ Node MakeNode(Args&&... args) { | |||
| 48 | template <typename T, typename... Args> | 48 | template <typename T, typename... Args> |
| 49 | TrackSampler MakeTrackSampler(Args&&... args) { | 49 | TrackSampler MakeTrackSampler(Args&&... args) { |
| 50 | static_assert(std::is_convertible_v<T, TrackSamplerData>); | 50 | static_assert(std::is_convertible_v<T, TrackSamplerData>); |
| 51 | return std::make_shared<TrackSamplerData>(T(std::forward<Args>(args)...)); | 51 | return std::make_shared<TrackSamplerData>(T{std::forward<Args>(args)...}); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | template <typename... Args> | 54 | template <typename... Args> |
diff --git a/src/video_core/shader/registry.cpp b/src/video_core/shader/registry.cpp index af70b3f35..cdf274e54 100644 --- a/src/video_core/shader/registry.cpp +++ b/src/video_core/shader/registry.cpp | |||
| @@ -93,6 +93,26 @@ std::optional<SamplerDescriptor> Registry::ObtainBoundSampler(u32 offset) { | |||
| 93 | return value; | 93 | return value; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainSeparateSampler( | ||
| 97 | std::pair<u32, u32> buffers, std::pair<u32, u32> offsets) { | ||
| 98 | SeparateSamplerKey key; | ||
| 99 | key.buffers = buffers; | ||
| 100 | key.offsets = offsets; | ||
| 101 | const auto iter = separate_samplers.find(key); | ||
| 102 | if (iter != separate_samplers.end()) { | ||
| 103 | return iter->second; | ||
| 104 | } | ||
| 105 | if (!engine) { | ||
| 106 | return std::nullopt; | ||
| 107 | } | ||
| 108 | |||
| 109 | const u32 handle_1 = engine->AccessConstBuffer32(stage, key.buffers.first, key.offsets.first); | ||
| 110 | const u32 handle_2 = engine->AccessConstBuffer32(stage, key.buffers.second, key.offsets.second); | ||
| 111 | const SamplerDescriptor value = engine->AccessSampler(handle_1 | handle_2); | ||
| 112 | separate_samplers.emplace(key, value); | ||
| 113 | return value; | ||
| 114 | } | ||
| 115 | |||
| 96 | std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer, | 116 | std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer, |
| 97 | u32 offset) { | 117 | u32 offset) { |
| 98 | const std::pair key = {buffer, offset}; | 118 | const std::pair key = {buffer, offset}; |
diff --git a/src/video_core/shader/registry.h b/src/video_core/shader/registry.h index 0c80d35fd..231206765 100644 --- a/src/video_core/shader/registry.h +++ b/src/video_core/shader/registry.h | |||
| @@ -19,8 +19,39 @@ | |||
| 19 | 19 | ||
| 20 | namespace VideoCommon::Shader { | 20 | namespace VideoCommon::Shader { |
| 21 | 21 | ||
| 22 | struct SeparateSamplerKey { | ||
| 23 | std::pair<u32, u32> buffers; | ||
| 24 | std::pair<u32, u32> offsets; | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace VideoCommon::Shader | ||
| 28 | |||
| 29 | namespace std { | ||
| 30 | |||
| 31 | template <> | ||
| 32 | struct hash<VideoCommon::Shader::SeparateSamplerKey> { | ||
| 33 | std::size_t operator()(const VideoCommon::Shader::SeparateSamplerKey& key) const noexcept { | ||
| 34 | return std::hash<u32>{}(key.buffers.first ^ key.buffers.second ^ key.offsets.first ^ | ||
| 35 | key.offsets.second); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | |||
| 39 | template <> | ||
| 40 | struct equal_to<VideoCommon::Shader::SeparateSamplerKey> { | ||
| 41 | bool operator()(const VideoCommon::Shader::SeparateSamplerKey& lhs, | ||
| 42 | const VideoCommon::Shader::SeparateSamplerKey& rhs) const noexcept { | ||
| 43 | return lhs.buffers == rhs.buffers && lhs.offsets == rhs.offsets; | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // namespace std | ||
| 48 | |||
| 49 | namespace VideoCommon::Shader { | ||
| 50 | |||
| 22 | using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>; | 51 | using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>; |
| 23 | using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>; | 52 | using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>; |
| 53 | using SeparateSamplerMap = | ||
| 54 | std::unordered_map<SeparateSamplerKey, Tegra::Engines::SamplerDescriptor>; | ||
| 24 | using BindlessSamplerMap = | 55 | using BindlessSamplerMap = |
| 25 | std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>; | 56 | std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>; |
| 26 | 57 | ||
| @@ -73,6 +104,9 @@ public: | |||
| 73 | 104 | ||
| 74 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset); | 105 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset); |
| 75 | 106 | ||
| 107 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainSeparateSampler( | ||
| 108 | std::pair<u32, u32> buffers, std::pair<u32, u32> offsets); | ||
| 109 | |||
| 76 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); | 110 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); |
| 77 | 111 | ||
| 78 | /// Inserts a key. | 112 | /// Inserts a key. |
| @@ -128,6 +162,7 @@ private: | |||
| 128 | Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; | 162 | Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; |
| 129 | KeyMap keys; | 163 | KeyMap keys; |
| 130 | BoundSamplerMap bound_samplers; | 164 | BoundSamplerMap bound_samplers; |
| 165 | SeparateSamplerMap separate_samplers; | ||
| 131 | BindlessSamplerMap bindless_samplers; | 166 | BindlessSamplerMap bindless_samplers; |
| 132 | u32 bound_buffer; | 167 | u32 bound_buffer; |
| 133 | GraphicsInfo graphics_info; | 168 | GraphicsInfo graphics_info; |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 15ae152f2..3a98b2104 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -330,8 +330,8 @@ private: | |||
| 330 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); | 330 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); |
| 331 | 331 | ||
| 332 | /// Queries the missing sampler info from the execution context. | 332 | /// Queries the missing sampler info from the execution context. |
| 333 | SamplerInfo GetSamplerInfo(SamplerInfo info, u32 offset, | 333 | SamplerInfo GetSamplerInfo(SamplerInfo info, |
| 334 | std::optional<u32> buffer = std::nullopt); | 334 | std::optional<Tegra::Engines::SamplerDescriptor> sampler); |
| 335 | 335 | ||
| 336 | /// Accesses a texture sampler. | 336 | /// Accesses a texture sampler. |
| 337 | std::optional<Sampler> GetSampler(Tegra::Shader::Sampler sampler, SamplerInfo info); | 337 | std::optional<Sampler> GetSampler(Tegra::Shader::Sampler sampler, SamplerInfo info); |
| @@ -409,8 +409,14 @@ private: | |||
| 409 | 409 | ||
| 410 | std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; | 410 | std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 411 | 411 | ||
| 412 | std::tuple<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code, | 412 | std::pair<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code, |
| 413 | s64 cursor); | 413 | s64 cursor); |
| 414 | |||
| 415 | std::pair<Node, TrackSampler> HandleBindlessIndirectRead(const CbufNode& cbuf, | ||
| 416 | const OperationNode& operation, | ||
| 417 | Node gpr, Node base_offset, | ||
| 418 | Node tracked, const NodeBlock& code, | ||
| 419 | s64 cursor); | ||
| 414 | 420 | ||
| 415 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; | 421 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 416 | 422 | ||
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp index eb97bfd41..d5ed81442 100644 --- a/src/video_core/shader/track.cpp +++ b/src/video_core/shader/track.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | namespace VideoCommon::Shader { | 14 | namespace VideoCommon::Shader { |
| 15 | 15 | ||
| 16 | namespace { | 16 | namespace { |
| 17 | |||
| 17 | std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, | 18 | std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, |
| 18 | OperationCode operation_code) { | 19 | OperationCode operation_code) { |
| 19 | for (; cursor >= 0; --cursor) { | 20 | for (; cursor >= 0; --cursor) { |
| @@ -63,7 +64,8 @@ bool AmendNodeCv(std::size_t amend_index, Node node) { | |||
| 63 | if (const auto operation = std::get_if<OperationNode>(&*node)) { | 64 | if (const auto operation = std::get_if<OperationNode>(&*node)) { |
| 64 | operation->SetAmendIndex(amend_index); | 65 | operation->SetAmendIndex(amend_index); |
| 65 | return true; | 66 | return true; |
| 66 | } else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { | 67 | } |
| 68 | if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { | ||
| 67 | conditional->SetAmendIndex(amend_index); | 69 | conditional->SetAmendIndex(amend_index); |
| 68 | return true; | 70 | return true; |
| 69 | } | 71 | } |
| @@ -72,40 +74,27 @@ bool AmendNodeCv(std::size_t amend_index, Node node) { | |||
| 72 | 74 | ||
| 73 | } // Anonymous namespace | 75 | } // Anonymous namespace |
| 74 | 76 | ||
| 75 | std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code, | 77 | std::pair<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code, |
| 76 | s64 cursor) { | 78 | s64 cursor) { |
| 77 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | 79 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { |
| 80 | const u32 cbuf_index = cbuf->GetIndex(); | ||
| 81 | |||
| 78 | // Constant buffer found, test if it's an immediate | 82 | // Constant buffer found, test if it's an immediate |
| 79 | const auto& offset = cbuf->GetOffset(); | 83 | const auto& offset = cbuf->GetOffset(); |
| 80 | if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { | 84 | if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { |
| 81 | auto track = | 85 | auto track = MakeTrackSampler<BindlessSamplerNode>(cbuf_index, immediate->GetValue()); |
| 82 | MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue()); | ||
| 83 | return {tracked, track}; | 86 | return {tracked, track}; |
| 84 | } | 87 | } |
| 85 | if (const auto operation = std::get_if<OperationNode>(&*offset)) { | 88 | if (const auto operation = std::get_if<OperationNode>(&*offset)) { |
| 86 | const u32 bound_buffer = registry.GetBoundBuffer(); | 89 | const u32 bound_buffer = registry.GetBoundBuffer(); |
| 87 | if (bound_buffer != cbuf->GetIndex()) { | 90 | if (bound_buffer != cbuf_index) { |
| 88 | return {}; | 91 | return {}; |
| 89 | } | 92 | } |
| 90 | const auto pair = DecoupleIndirectRead(*operation); | 93 | if (const std::optional pair = DecoupleIndirectRead(*operation)) { |
| 91 | if (!pair) { | 94 | auto [gpr, base_offset] = *pair; |
| 92 | return {}; | 95 | return HandleBindlessIndirectRead(*cbuf, *operation, gpr, base_offset, tracked, |
| 96 | code, cursor); | ||
| 93 | } | 97 | } |
| 94 | auto [gpr, base_offset] = *pair; | ||
| 95 | const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset); | ||
| 96 | const auto& gpu_driver = registry.AccessGuestDriverProfile(); | ||
| 97 | const u32 bindless_cv = NewCustomVariable(); | ||
| 98 | Node op = | ||
| 99 | Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize())); | ||
| 100 | |||
| 101 | const Node cv_node = GetCustomVariable(bindless_cv); | ||
| 102 | Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op)); | ||
| 103 | const std::size_t amend_index = DeclareAmend(std::move(amend_op)); | ||
| 104 | AmendNodeCv(amend_index, code[cursor]); | ||
| 105 | // TODO Implement Bindless Index custom variable | ||
| 106 | auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(), | ||
| 107 | offset_inm->GetValue(), bindless_cv); | ||
| 108 | return {tracked, track}; | ||
| 109 | } | 98 | } |
| 110 | return {}; | 99 | return {}; |
| 111 | } | 100 | } |
| @@ -122,10 +111,23 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons | |||
| 122 | return TrackBindlessSampler(source, code, new_cursor); | 111 | return TrackBindlessSampler(source, code, new_cursor); |
| 123 | } | 112 | } |
| 124 | if (const auto operation = std::get_if<OperationNode>(&*tracked)) { | 113 | if (const auto operation = std::get_if<OperationNode>(&*tracked)) { |
| 125 | for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) { | 114 | const OperationNode& op = *operation; |
| 126 | if (auto found = TrackBindlessSampler((*operation)[i - 1], code, cursor); | 115 | |
| 127 | std::get<0>(found)) { | 116 | const OperationCode opcode = operation->GetCode(); |
| 128 | // Cbuf found in operand. | 117 | if (opcode == OperationCode::IBitwiseOr || opcode == OperationCode::UBitwiseOr) { |
| 118 | ASSERT(op.GetOperandsCount() == 2); | ||
| 119 | auto [node_a, index_a, offset_a] = TrackCbuf(op[0], code, cursor); | ||
| 120 | auto [node_b, index_b, offset_b] = TrackCbuf(op[1], code, cursor); | ||
| 121 | if (node_a && node_b) { | ||
| 122 | auto track = MakeTrackSampler<SeparateSamplerNode>(std::pair{index_a, index_b}, | ||
| 123 | std::pair{offset_a, offset_b}); | ||
| 124 | return {tracked, std::move(track)}; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | std::size_t i = op.GetOperandsCount(); | ||
| 128 | while (i--) { | ||
| 129 | if (auto found = TrackBindlessSampler(op[i - 1], code, cursor); std::get<0>(found)) { | ||
| 130 | // Constant buffer found in operand. | ||
| 129 | return found; | 131 | return found; |
| 130 | } | 132 | } |
| 131 | } | 133 | } |
| @@ -139,6 +141,26 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons | |||
| 139 | return {}; | 141 | return {}; |
| 140 | } | 142 | } |
| 141 | 143 | ||
| 144 | std::pair<Node, TrackSampler> ShaderIR::HandleBindlessIndirectRead( | ||
| 145 | const CbufNode& cbuf, const OperationNode& operation, Node gpr, Node base_offset, Node tracked, | ||
| 146 | const NodeBlock& code, s64 cursor) { | ||
| 147 | const auto offset_imm = std::get<ImmediateNode>(*base_offset); | ||
| 148 | const auto& gpu_driver = registry.AccessGuestDriverProfile(); | ||
| 149 | const u32 bindless_cv = NewCustomVariable(); | ||
| 150 | const u32 texture_handler_size = gpu_driver.GetTextureHandlerSize(); | ||
| 151 | Node op = Operation(OperationCode::UDiv, gpr, Immediate(texture_handler_size)); | ||
| 152 | |||
| 153 | Node cv_node = GetCustomVariable(bindless_cv); | ||
| 154 | Node amend_op = Operation(OperationCode::Assign, std::move(cv_node), std::move(op)); | ||
| 155 | const std::size_t amend_index = DeclareAmend(std::move(amend_op)); | ||
| 156 | AmendNodeCv(amend_index, code[cursor]); | ||
| 157 | |||
| 158 | // TODO: Implement bindless index custom variable | ||
| 159 | auto track = | ||
| 160 | MakeTrackSampler<ArraySamplerNode>(cbuf.GetIndex(), offset_imm.GetValue(), bindless_cv); | ||
| 161 | return {tracked, track}; | ||
| 162 | } | ||
| 163 | |||
| 142 | std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, | 164 | std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, |
| 143 | s64 cursor) const { | 165 | s64 cursor) const { |
| 144 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | 166 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { |
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h new file mode 100644 index 000000000..b7608fc7b --- /dev/null +++ b/src/video_core/shader_cache.h | |||
| @@ -0,0 +1,240 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <memory> | ||
| 9 | #include <mutex> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include <utility> | ||
| 12 | #include <vector> | ||
| 13 | |||
| 14 | #include "common/assert.h" | ||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "video_core/rasterizer_interface.h" | ||
| 17 | |||
| 18 | namespace VideoCommon { | ||
| 19 | |||
| 20 | template <class T> | ||
| 21 | class ShaderCache { | ||
| 22 | static constexpr u64 PAGE_BITS = 14; | ||
| 23 | static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS; | ||
| 24 | |||
| 25 | struct Entry { | ||
| 26 | VAddr addr_start; | ||
| 27 | VAddr addr_end; | ||
| 28 | T* data; | ||
| 29 | |||
| 30 | bool is_memory_marked = true; | ||
| 31 | |||
| 32 | constexpr bool Overlaps(VAddr start, VAddr end) const noexcept { | ||
| 33 | return start < addr_end && addr_start < end; | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | public: | ||
| 38 | virtual ~ShaderCache() = default; | ||
| 39 | |||
| 40 | /// @brief Removes shaders inside a given region | ||
| 41 | /// @note Checks for ranges | ||
| 42 | /// @param addr Start address of the invalidation | ||
| 43 | /// @param size Number of bytes of the invalidation | ||
| 44 | void InvalidateRegion(VAddr addr, std::size_t size) { | ||
| 45 | std::scoped_lock lock{invalidation_mutex}; | ||
| 46 | InvalidatePagesInRegion(addr, size); | ||
| 47 | RemovePendingShaders(); | ||
| 48 | } | ||
| 49 | |||
| 50 | /// @brief Unmarks a memory region as cached and marks it for removal | ||
| 51 | /// @param addr Start address of the CPU write operation | ||
| 52 | /// @param size Number of bytes of the CPU write operation | ||
| 53 | void OnCPUWrite(VAddr addr, std::size_t size) { | ||
| 54 | std::lock_guard lock{invalidation_mutex}; | ||
| 55 | InvalidatePagesInRegion(addr, size); | ||
| 56 | } | ||
| 57 | |||
| 58 | /// @brief Flushes delayed removal operations | ||
| 59 | void SyncGuestHost() { | ||
| 60 | std::scoped_lock lock{invalidation_mutex}; | ||
| 61 | RemovePendingShaders(); | ||
| 62 | } | ||
| 63 | |||
| 64 | /// @brief Tries to obtain a cached shader starting in a given address | ||
| 65 | /// @note Doesn't check for ranges, the given address has to be the start of the shader | ||
| 66 | /// @param addr Start address of the shader, this doesn't cache for region | ||
| 67 | /// @return Pointer to a valid shader, nullptr when nothing is found | ||
| 68 | T* TryGet(VAddr addr) const { | ||
| 69 | std::scoped_lock lock{lookup_mutex}; | ||
| 70 | |||
| 71 | const auto it = lookup_cache.find(addr); | ||
| 72 | if (it == lookup_cache.end()) { | ||
| 73 | return nullptr; | ||
| 74 | } | ||
| 75 | return it->second->data; | ||
| 76 | } | ||
| 77 | |||
| 78 | protected: | ||
| 79 | explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_) : rasterizer{rasterizer_} {} | ||
| 80 | |||
| 81 | /// @brief Register in the cache a given entry | ||
| 82 | /// @param data Shader to store in the cache | ||
| 83 | /// @param addr Start address of the shader that will be registered | ||
| 84 | /// @param size Size in bytes of the shader | ||
| 85 | void Register(std::unique_ptr<T> data, VAddr addr, std::size_t size) { | ||
| 86 | std::scoped_lock lock{invalidation_mutex, lookup_mutex}; | ||
| 87 | |||
| 88 | const VAddr addr_end = addr + size; | ||
| 89 | Entry* const entry = NewEntry(addr, addr_end, data.get()); | ||
| 90 | |||
| 91 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | ||
| 92 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { | ||
| 93 | invalidation_cache[page].push_back(entry); | ||
| 94 | } | ||
| 95 | |||
| 96 | storage.push_back(std::move(data)); | ||
| 97 | |||
| 98 | rasterizer.UpdatePagesCachedCount(addr, size, 1); | ||
| 99 | } | ||
| 100 | |||
| 101 | /// @brief Called when a shader is going to be removed | ||
| 102 | /// @param shader Shader that will be removed | ||
| 103 | /// @pre invalidation_cache is locked | ||
| 104 | /// @pre lookup_mutex is locked | ||
| 105 | virtual void OnShaderRemoval([[maybe_unused]] T* shader) {} | ||
| 106 | |||
| 107 | private: | ||
| 108 | /// @brief Invalidate pages in a given region | ||
| 109 | /// @pre invalidation_mutex is locked | ||
| 110 | void InvalidatePagesInRegion(VAddr addr, std::size_t size) { | ||
| 111 | const VAddr addr_end = addr + size; | ||
| 112 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | ||
| 113 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { | ||
| 114 | auto it = invalidation_cache.find(page); | ||
| 115 | if (it == invalidation_cache.end()) { | ||
| 116 | continue; | ||
| 117 | } | ||
| 118 | InvalidatePageEntries(it->second, addr, addr_end); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /// @brief Remove shaders marked for deletion | ||
| 123 | /// @pre invalidation_mutex is locked | ||
| 124 | void RemovePendingShaders() { | ||
| 125 | if (marked_for_removal.empty()) { | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | // Remove duplicates | ||
| 129 | std::sort(marked_for_removal.begin(), marked_for_removal.end()); | ||
| 130 | marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()), | ||
| 131 | marked_for_removal.end()); | ||
| 132 | |||
| 133 | std::vector<T*> removed_shaders; | ||
| 134 | removed_shaders.reserve(marked_for_removal.size()); | ||
| 135 | |||
| 136 | std::scoped_lock lock{lookup_mutex}; | ||
| 137 | |||
| 138 | for (Entry* const entry : marked_for_removal) { | ||
| 139 | removed_shaders.push_back(entry->data); | ||
| 140 | |||
| 141 | const auto it = lookup_cache.find(entry->addr_start); | ||
| 142 | ASSERT(it != lookup_cache.end()); | ||
| 143 | lookup_cache.erase(it); | ||
| 144 | } | ||
| 145 | marked_for_removal.clear(); | ||
| 146 | |||
| 147 | if (!removed_shaders.empty()) { | ||
| 148 | RemoveShadersFromStorage(std::move(removed_shaders)); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | /// @brief Invalidates entries in a given range for the passed page | ||
| 153 | /// @param entries Vector of entries in the page, it will be modified on overlaps | ||
| 154 | /// @param addr Start address of the invalidation | ||
| 155 | /// @param addr_end Non-inclusive end address of the invalidation | ||
| 156 | /// @pre invalidation_mutex is locked | ||
| 157 | void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) { | ||
| 158 | std::size_t index = 0; | ||
| 159 | while (index < entries.size()) { | ||
| 160 | Entry* const entry = entries[index]; | ||
| 161 | if (!entry->Overlaps(addr, addr_end)) { | ||
| 162 | ++index; | ||
| 163 | continue; | ||
| 164 | } | ||
| 165 | |||
| 166 | UnmarkMemory(entry); | ||
| 167 | RemoveEntryFromInvalidationCache(entry); | ||
| 168 | marked_for_removal.push_back(entry); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | /// @brief Removes all references to an entry in the invalidation cache | ||
| 173 | /// @param entry Entry to remove from the invalidation cache | ||
| 174 | /// @pre invalidation_mutex is locked | ||
| 175 | void RemoveEntryFromInvalidationCache(const Entry* entry) { | ||
| 176 | const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | ||
| 177 | for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) { | ||
| 178 | const auto entries_it = invalidation_cache.find(page); | ||
| 179 | ASSERT(entries_it != invalidation_cache.end()); | ||
| 180 | std::vector<Entry*>& entries = entries_it->second; | ||
| 181 | |||
| 182 | const auto entry_it = std::find(entries.begin(), entries.end(), entry); | ||
| 183 | ASSERT(entry_it != entries.end()); | ||
| 184 | entries.erase(entry_it); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /// @brief Unmarks an entry from the rasterizer cache | ||
| 189 | /// @param entry Entry to unmark from memory | ||
| 190 | void UnmarkMemory(Entry* entry) { | ||
| 191 | if (!entry->is_memory_marked) { | ||
| 192 | return; | ||
| 193 | } | ||
| 194 | entry->is_memory_marked = false; | ||
| 195 | |||
| 196 | const VAddr addr = entry->addr_start; | ||
| 197 | const std::size_t size = entry->addr_end - addr; | ||
| 198 | rasterizer.UpdatePagesCachedCount(addr, size, -1); | ||
| 199 | } | ||
| 200 | |||
| 201 | /// @brief Removes a vector of shaders from a list | ||
| 202 | /// @param removed_shaders Shaders to be removed from the storage | ||
| 203 | /// @pre invalidation_mutex is locked | ||
| 204 | /// @pre lookup_mutex is locked | ||
| 205 | void RemoveShadersFromStorage(std::vector<T*> removed_shaders) { | ||
| 206 | // Notify removals | ||
| 207 | for (T* const shader : removed_shaders) { | ||
| 208 | OnShaderRemoval(shader); | ||
| 209 | } | ||
| 210 | |||
| 211 | // Remove them from the cache | ||
| 212 | const auto is_removed = [&removed_shaders](std::unique_ptr<T>& shader) { | ||
| 213 | return std::find(removed_shaders.begin(), removed_shaders.end(), shader.get()) != | ||
| 214 | removed_shaders.end(); | ||
| 215 | }; | ||
| 216 | storage.erase(std::remove_if(storage.begin(), storage.end(), is_removed), storage.end()); | ||
| 217 | } | ||
| 218 | |||
| 219 | /// @brief Creates a new entry in the lookup cache and returns its pointer | ||
| 220 | /// @pre lookup_mutex is locked | ||
| 221 | Entry* NewEntry(VAddr addr, VAddr addr_end, T* data) { | ||
| 222 | auto entry = std::make_unique<Entry>(Entry{addr, addr_end, data}); | ||
| 223 | Entry* const entry_pointer = entry.get(); | ||
| 224 | |||
| 225 | lookup_cache.emplace(addr, std::move(entry)); | ||
| 226 | return entry_pointer; | ||
| 227 | } | ||
| 228 | |||
| 229 | VideoCore::RasterizerInterface& rasterizer; | ||
| 230 | |||
| 231 | mutable std::mutex lookup_mutex; | ||
| 232 | std::mutex invalidation_mutex; | ||
| 233 | |||
| 234 | std::unordered_map<u64, std::unique_ptr<Entry>> lookup_cache; | ||
| 235 | std::unordered_map<u64, std::vector<Entry*>> invalidation_cache; | ||
| 236 | std::vector<std::unique_ptr<T>> storage; | ||
| 237 | std::vector<Entry*> marked_for_removal; | ||
| 238 | }; | ||
| 239 | |||
| 240 | } // namespace VideoCommon | ||
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index 7032e0059..f476f03b0 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp | |||
| @@ -41,7 +41,7 @@ struct Table { | |||
| 41 | ComponentType alpha_component; | 41 | ComponentType alpha_component; |
| 42 | bool is_srgb; | 42 | bool is_srgb; |
| 43 | }; | 43 | }; |
| 44 | constexpr std::array<Table, 77> DefinitionTable = {{ | 44 | constexpr std::array<Table, 78> DefinitionTable = {{ |
| 45 | {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U}, | 45 | {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U}, |
| 46 | {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S}, | 46 | {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S}, |
| 47 | {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI}, | 47 | {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI}, |
| @@ -98,6 +98,7 @@ constexpr std::array<Table, 77> DefinitionTable = {{ | |||
| 98 | {TextureFormat::ZF32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::Z32F}, | 98 | {TextureFormat::ZF32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::Z32F}, |
| 99 | {TextureFormat::Z16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z16}, | 99 | {TextureFormat::Z16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z16}, |
| 100 | {TextureFormat::S8Z24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24}, | 100 | {TextureFormat::S8Z24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24}, |
| 101 | {TextureFormat::G24R8, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24}, | ||
| 101 | {TextureFormat::ZF32_X24S8, C, FLOAT, UINT, UNORM, UNORM, PixelFormat::Z32FS8}, | 102 | {TextureFormat::ZF32_X24S8, C, FLOAT, UINT, UNORM, UNORM, PixelFormat::Z32FS8}, |
| 102 | 103 | ||
| 103 | {TextureFormat::DXT1, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1}, | 104 | {TextureFormat::DXT1, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1}, |
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 715f39d0d..0caf3b4f0 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp | |||
| @@ -120,6 +120,9 @@ std::optional<std::pair<u32, u32>> SurfaceBaseImpl::GetLayerMipmap( | |||
| 120 | } | 120 | } |
| 121 | const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)}; | 121 | const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)}; |
| 122 | const auto layer{static_cast<u32>(relative_address / layer_size)}; | 122 | const auto layer{static_cast<u32>(relative_address / layer_size)}; |
| 123 | if (layer >= params.depth) { | ||
| 124 | return {}; | ||
| 125 | } | ||
| 123 | const GPUVAddr mipmap_address = relative_address - layer_size * layer; | 126 | const GPUVAddr mipmap_address = relative_address - layer_size * layer; |
| 124 | const auto mipmap_it = | 127 | const auto mipmap_it = |
| 125 | Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); | 128 | Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); |
| @@ -248,12 +251,11 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager, | |||
| 248 | 251 | ||
| 249 | // Use an extra temporal buffer | 252 | // Use an extra temporal buffer |
| 250 | auto& tmp_buffer = staging_cache.GetBuffer(1); | 253 | auto& tmp_buffer = staging_cache.GetBuffer(1); |
| 251 | // Special case for 3D Texture Segments | ||
| 252 | const bool must_read_current_data = | ||
| 253 | params.block_depth > 0 && params.target == VideoCore::Surface::SurfaceTarget::Texture2D; | ||
| 254 | tmp_buffer.resize(guest_memory_size); | 254 | tmp_buffer.resize(guest_memory_size); |
| 255 | host_ptr = tmp_buffer.data(); | 255 | host_ptr = tmp_buffer.data(); |
| 256 | if (must_read_current_data) { | 256 | |
| 257 | if (params.target == SurfaceTarget::Texture3D) { | ||
| 258 | // Special case for 3D texture segments | ||
| 257 | memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); | 259 | memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); |
| 258 | } | 260 | } |
| 259 | 261 | ||
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h index 79e10ffbb..173f2edba 100644 --- a/src/video_core/texture_cache/surface_base.h +++ b/src/video_core/texture_cache/surface_base.h | |||
| @@ -217,8 +217,8 @@ public: | |||
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | bool IsProtected() const { | 219 | bool IsProtected() const { |
| 220 | // Only 3D Slices are to be protected | 220 | // Only 3D slices are to be protected |
| 221 | return is_target && params.block_depth > 0; | 221 | return is_target && params.target == SurfaceTarget::Texture3D; |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | bool IsRenderTarget() const { | 224 | bool IsRenderTarget() const { |
| @@ -250,6 +250,11 @@ public: | |||
| 250 | return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels)); | 250 | return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels)); |
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | TView Emplace3DView(u32 slice, u32 depth, u32 base_level, u32 num_levels) { | ||
| 254 | return GetView(ViewParams(VideoCore::Surface::SurfaceTarget::Texture3D, slice, depth, | ||
| 255 | base_level, num_levels)); | ||
| 256 | } | ||
| 257 | |||
| 253 | std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params, | 258 | std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params, |
| 254 | const GPUVAddr view_addr, | 259 | const GPUVAddr view_addr, |
| 255 | const std::size_t candidate_size, const u32 mipmap, | 260 | const std::size_t candidate_size, const u32 mipmap, |
| @@ -272,8 +277,8 @@ public: | |||
| 272 | std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr, | 277 | std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr, |
| 273 | const std::size_t candidate_size) { | 278 | const std::size_t candidate_size) { |
| 274 | if (params.target == SurfaceTarget::Texture3D || | 279 | if (params.target == SurfaceTarget::Texture3D || |
| 275 | (params.num_levels == 1 && !params.is_layered) || | 280 | view_params.target == SurfaceTarget::Texture3D || |
| 276 | view_params.target == SurfaceTarget::Texture3D) { | 281 | (params.num_levels == 1 && !params.is_layered)) { |
| 277 | return {}; | 282 | return {}; |
| 278 | } | 283 | } |
| 279 | const auto layer_mipmap{GetLayerMipmap(view_addr)}; | 284 | const auto layer_mipmap{GetLayerMipmap(view_addr)}; |
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index 884fabffe..0b2b2b8c4 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp | |||
| @@ -215,10 +215,19 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz | |||
| 215 | params.num_levels = 1; | 215 | params.num_levels = 1; |
| 216 | params.emulated_levels = 1; | 216 | params.emulated_levels = 1; |
| 217 | 217 | ||
| 218 | const bool is_layered = config.layers > 1 && params.block_depth == 0; | 218 | if (config.memory_layout.is_3d != 0) { |
| 219 | params.is_layered = is_layered; | 219 | params.depth = config.layers.Value(); |
| 220 | params.depth = is_layered ? config.layers.Value() : 1; | 220 | params.is_layered = false; |
| 221 | params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D; | 221 | params.target = SurfaceTarget::Texture3D; |
| 222 | } else if (config.layers > 1) { | ||
| 223 | params.depth = config.layers.Value(); | ||
| 224 | params.is_layered = true; | ||
| 225 | params.target = SurfaceTarget::Texture2DArray; | ||
| 226 | } else { | ||
| 227 | params.depth = 1; | ||
| 228 | params.is_layered = false; | ||
| 229 | params.target = SurfaceTarget::Texture2D; | ||
| 230 | } | ||
| 222 | return params; | 231 | return params; |
| 223 | } | 232 | } |
| 224 | 233 | ||
| @@ -237,7 +246,7 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface( | |||
| 237 | params.width = config.width; | 246 | params.width = config.width; |
| 238 | params.height = config.height; | 247 | params.height = config.height; |
| 239 | params.pitch = config.pitch; | 248 | params.pitch = config.pitch; |
| 240 | // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters | 249 | // TODO(Rodrigo): Try to guess texture arrays from parameters |
| 241 | params.target = SurfaceTarget::Texture2D; | 250 | params.target = SurfaceTarget::Texture2D; |
| 242 | params.depth = 1; | 251 | params.depth = 1; |
| 243 | params.num_levels = 1; | 252 | params.num_levels = 1; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8bfc541d4..cdcddb225 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <unordered_map> | 14 | #include <unordered_map> |
| 15 | #include <vector> | 15 | #include <vector> |
| 16 | 16 | ||
| 17 | #include <boost/container/small_vector.hpp> | ||
| 17 | #include <boost/icl/interval_map.hpp> | 18 | #include <boost/icl/interval_map.hpp> |
| 18 | #include <boost/range/iterator_range.hpp> | 19 | #include <boost/range/iterator_range.hpp> |
| 19 | 20 | ||
| @@ -23,6 +24,7 @@ | |||
| 23 | #include "core/core.h" | 24 | #include "core/core.h" |
| 24 | #include "core/memory.h" | 25 | #include "core/memory.h" |
| 25 | #include "core/settings.h" | 26 | #include "core/settings.h" |
| 27 | #include "video_core/compatible_formats.h" | ||
| 26 | #include "video_core/dirty_flags.h" | 28 | #include "video_core/dirty_flags.h" |
| 27 | #include "video_core/engines/fermi_2d.h" | 29 | #include "video_core/engines/fermi_2d.h" |
| 28 | #include "video_core/engines/maxwell_3d.h" | 30 | #include "video_core/engines/maxwell_3d.h" |
| @@ -46,13 +48,14 @@ class RasterizerInterface; | |||
| 46 | 48 | ||
| 47 | namespace VideoCommon { | 49 | namespace VideoCommon { |
| 48 | 50 | ||
| 51 | using VideoCore::Surface::FormatCompatibility; | ||
| 49 | using VideoCore::Surface::PixelFormat; | 52 | using VideoCore::Surface::PixelFormat; |
| 50 | |||
| 51 | using VideoCore::Surface::SurfaceTarget; | 53 | using VideoCore::Surface::SurfaceTarget; |
| 52 | using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; | 54 | using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; |
| 53 | 55 | ||
| 54 | template <typename TSurface, typename TView> | 56 | template <typename TSurface, typename TView> |
| 55 | class TextureCache { | 57 | class TextureCache { |
| 58 | using VectorSurface = boost::container::small_vector<TSurface, 1>; | ||
| 56 | 59 | ||
| 57 | public: | 60 | public: |
| 58 | void InvalidateRegion(VAddr addr, std::size_t size) { | 61 | void InvalidateRegion(VAddr addr, std::size_t size) { |
| @@ -246,7 +249,7 @@ public: | |||
| 246 | auto& surface = render_targets[index].target; | 249 | auto& surface = render_targets[index].target; |
| 247 | surface->MarkAsRenderTarget(false, NO_RT); | 250 | surface->MarkAsRenderTarget(false, NO_RT); |
| 248 | const auto& cr_params = surface->GetSurfaceParams(); | 251 | const auto& cr_params = surface->GetSurfaceParams(); |
| 249 | if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) { | 252 | if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation.GetValue()) { |
| 250 | AsyncFlushSurface(surface); | 253 | AsyncFlushSurface(surface); |
| 251 | } | 254 | } |
| 252 | } | 255 | } |
| @@ -296,30 +299,30 @@ public: | |||
| 296 | const GPUVAddr src_gpu_addr = src_config.Address(); | 299 | const GPUVAddr src_gpu_addr = src_config.Address(); |
| 297 | const GPUVAddr dst_gpu_addr = dst_config.Address(); | 300 | const GPUVAddr dst_gpu_addr = dst_config.Address(); |
| 298 | DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); | 301 | DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); |
| 299 | const std::optional<VAddr> dst_cpu_addr = | 302 | |
| 300 | system.GPU().MemoryManager().GpuToCpuAddress(dst_gpu_addr); | 303 | const auto& memory_manager = system.GPU().MemoryManager(); |
| 301 | const std::optional<VAddr> src_cpu_addr = | 304 | const std::optional<VAddr> dst_cpu_addr = memory_manager.GpuToCpuAddress(dst_gpu_addr); |
| 302 | system.GPU().MemoryManager().GpuToCpuAddress(src_gpu_addr); | 305 | const std::optional<VAddr> src_cpu_addr = memory_manager.GpuToCpuAddress(src_gpu_addr); |
| 303 | std::pair<TSurface, TView> dst_surface = | 306 | std::pair dst_surface = GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false); |
| 304 | GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false); | 307 | TView src_surface = GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false).second; |
| 305 | std::pair<TSurface, TView> src_surface = | 308 | ImageBlit(src_surface, dst_surface.second, copy_config); |
| 306 | GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false); | ||
| 307 | ImageBlit(src_surface.second, dst_surface.second, copy_config); | ||
| 308 | dst_surface.first->MarkAsModified(true, Tick()); | 309 | dst_surface.first->MarkAsModified(true, Tick()); |
| 309 | } | 310 | } |
| 310 | 311 | ||
| 311 | TSurface TryFindFramebufferSurface(VAddr addr) { | 312 | TSurface TryFindFramebufferSurface(VAddr addr) const { |
| 312 | if (!addr) { | 313 | if (!addr) { |
| 313 | return nullptr; | 314 | return nullptr; |
| 314 | } | 315 | } |
| 315 | const VAddr page = addr >> registry_page_bits; | 316 | const VAddr page = addr >> registry_page_bits; |
| 316 | std::vector<TSurface>& list = registry[page]; | 317 | const auto it = registry.find(page); |
| 317 | for (auto& surface : list) { | 318 | if (it == registry.end()) { |
| 318 | if (surface->GetCpuAddr() == addr) { | 319 | return nullptr; |
| 319 | return surface; | ||
| 320 | } | ||
| 321 | } | 320 | } |
| 322 | return nullptr; | 321 | const auto& list = it->second; |
| 322 | const auto found = std::find_if(list.begin(), list.end(), [addr](const auto& surface) { | ||
| 323 | return surface->GetCpuAddr() == addr; | ||
| 324 | }); | ||
| 325 | return found != list.end() ? *found : nullptr; | ||
| 323 | } | 326 | } |
| 324 | 327 | ||
| 325 | u64 Tick() { | 328 | u64 Tick() { |
| @@ -498,18 +501,18 @@ private: | |||
| 498 | * @param untopological Indicates to the recycler that the texture has no way | 501 | * @param untopological Indicates to the recycler that the texture has no way |
| 499 | * to match the overlaps due to topological reasons. | 502 | * to match the overlaps due to topological reasons. |
| 500 | **/ | 503 | **/ |
| 501 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, | 504 | RecycleStrategy PickStrategy(VectorSurface& overlaps, const SurfaceParams& params, |
| 502 | const GPUVAddr gpu_addr, const MatchTopologyResult untopological) { | 505 | const GPUVAddr gpu_addr, const MatchTopologyResult untopological) { |
| 503 | if (Settings::IsGPULevelExtreme()) { | 506 | if (Settings::IsGPULevelExtreme()) { |
| 504 | return RecycleStrategy::Flush; | 507 | return RecycleStrategy::Flush; |
| 505 | } | 508 | } |
| 506 | // 3D Textures decision | 509 | // 3D Textures decision |
| 507 | if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) { | 510 | if (params.target == SurfaceTarget::Texture3D) { |
| 508 | return RecycleStrategy::Flush; | 511 | return RecycleStrategy::Flush; |
| 509 | } | 512 | } |
| 510 | for (const auto& s : overlaps) { | 513 | for (const auto& s : overlaps) { |
| 511 | const auto& s_params = s->GetSurfaceParams(); | 514 | const auto& s_params = s->GetSurfaceParams(); |
| 512 | if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) { | 515 | if (s_params.target == SurfaceTarget::Texture3D) { |
| 513 | return RecycleStrategy::Flush; | 516 | return RecycleStrategy::Flush; |
| 514 | } | 517 | } |
| 515 | } | 518 | } |
| @@ -538,9 +541,8 @@ private: | |||
| 538 | * @param untopological Indicates to the recycler that the texture has no way to match the | 541 | * @param untopological Indicates to the recycler that the texture has no way to match the |
| 539 | * overlaps due to topological reasons. | 542 | * overlaps due to topological reasons. |
| 540 | **/ | 543 | **/ |
| 541 | std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, | 544 | std::pair<TSurface, TView> RecycleSurface(VectorSurface& overlaps, const SurfaceParams& params, |
| 542 | const SurfaceParams& params, const GPUVAddr gpu_addr, | 545 | const GPUVAddr gpu_addr, const bool preserve_contents, |
| 543 | const bool preserve_contents, | ||
| 544 | const MatchTopologyResult untopological) { | 546 | const MatchTopologyResult untopological) { |
| 545 | const bool do_load = preserve_contents && Settings::IsGPULevelExtreme(); | 547 | const bool do_load = preserve_contents && Settings::IsGPULevelExtreme(); |
| 546 | for (auto& surface : overlaps) { | 548 | for (auto& surface : overlaps) { |
| @@ -594,7 +596,7 @@ private: | |||
| 594 | } else { | 596 | } else { |
| 595 | new_surface = GetUncachedSurface(gpu_addr, params); | 597 | new_surface = GetUncachedSurface(gpu_addr, params); |
| 596 | } | 598 | } |
| 597 | const auto& final_params = new_surface->GetSurfaceParams(); | 599 | const SurfaceParams& final_params = new_surface->GetSurfaceParams(); |
| 598 | if (cr_params.type != final_params.type) { | 600 | if (cr_params.type != final_params.type) { |
| 599 | if (Settings::IsGPULevelExtreme()) { | 601 | if (Settings::IsGPULevelExtreme()) { |
| 600 | BufferCopy(current_surface, new_surface); | 602 | BufferCopy(current_surface, new_surface); |
| @@ -602,7 +604,7 @@ private: | |||
| 602 | } else { | 604 | } else { |
| 603 | std::vector<CopyParams> bricks = current_surface->BreakDown(final_params); | 605 | std::vector<CopyParams> bricks = current_surface->BreakDown(final_params); |
| 604 | for (auto& brick : bricks) { | 606 | for (auto& brick : bricks) { |
| 605 | ImageCopy(current_surface, new_surface, brick); | 607 | TryCopyImage(current_surface, new_surface, brick); |
| 606 | } | 608 | } |
| 607 | } | 609 | } |
| 608 | Unregister(current_surface); | 610 | Unregister(current_surface); |
| @@ -650,47 +652,65 @@ private: | |||
| 650 | * @param params The parameters on the new surface. | 652 | * @param params The parameters on the new surface. |
| 651 | * @param gpu_addr The starting address of the new surface. | 653 | * @param gpu_addr The starting address of the new surface. |
| 652 | **/ | 654 | **/ |
| 653 | std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps, | 655 | std::optional<std::pair<TSurface, TView>> TryReconstructSurface(VectorSurface& overlaps, |
| 654 | const SurfaceParams& params, | 656 | const SurfaceParams& params, |
| 655 | const GPUVAddr gpu_addr) { | 657 | GPUVAddr gpu_addr) { |
| 656 | if (params.target == SurfaceTarget::Texture3D) { | 658 | if (params.target == SurfaceTarget::Texture3D) { |
| 657 | return {}; | 659 | return std::nullopt; |
| 658 | } | 660 | } |
| 659 | bool modified = false; | 661 | const auto test_modified = [](TSurface& surface) { return surface->IsModified(); }; |
| 660 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | 662 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); |
| 661 | u32 passed_tests = 0; | 663 | |
| 664 | if (std::none_of(overlaps.begin(), overlaps.end(), test_modified)) { | ||
| 665 | LoadSurface(new_surface); | ||
| 666 | for (const auto& surface : overlaps) { | ||
| 667 | Unregister(surface); | ||
| 668 | } | ||
| 669 | Register(new_surface); | ||
| 670 | return {{new_surface, new_surface->GetMainView()}}; | ||
| 671 | } | ||
| 672 | |||
| 673 | std::size_t passed_tests = 0; | ||
| 662 | for (auto& surface : overlaps) { | 674 | for (auto& surface : overlaps) { |
| 663 | const SurfaceParams& src_params = surface->GetSurfaceParams(); | 675 | const SurfaceParams& src_params = surface->GetSurfaceParams(); |
| 664 | if (src_params.is_layered || src_params.num_levels > 1) { | 676 | const auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())}; |
| 665 | // We send this cases to recycle as they are more complex to handle | ||
| 666 | return {}; | ||
| 667 | } | ||
| 668 | const std::size_t candidate_size = surface->GetSizeInBytes(); | ||
| 669 | auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())}; | ||
| 670 | if (!mipmap_layer) { | 677 | if (!mipmap_layer) { |
| 671 | continue; | 678 | continue; |
| 672 | } | 679 | } |
| 673 | const auto [layer, mipmap] = *mipmap_layer; | 680 | const auto [base_layer, base_mipmap] = *mipmap_layer; |
| 674 | if (new_surface->GetMipmapSize(mipmap) != candidate_size) { | 681 | if (new_surface->GetMipmapSize(base_mipmap) != surface->GetMipmapSize(0)) { |
| 675 | continue; | 682 | continue; |
| 676 | } | 683 | } |
| 677 | modified |= surface->IsModified(); | 684 | ++passed_tests; |
| 678 | // Now we got all the data set up | 685 | |
| 679 | const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap); | 686 | // Copy all mipmaps and layers |
| 680 | const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap); | 687 | const u32 block_width = params.GetDefaultBlockWidth(); |
| 681 | const CopyParams copy_params(0, 0, 0, 0, 0, layer, 0, mipmap, width, height, 1); | 688 | const u32 block_height = params.GetDefaultBlockHeight(); |
| 682 | passed_tests++; | 689 | for (u32 mipmap = base_mipmap; mipmap < base_mipmap + src_params.num_levels; ++mipmap) { |
| 683 | ImageCopy(surface, new_surface, copy_params); | 690 | const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap); |
| 691 | const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap); | ||
| 692 | if (width < block_width || height < block_height) { | ||
| 693 | // Current APIs forbid copying small compressed textures, avoid errors | ||
| 694 | break; | ||
| 695 | } | ||
| 696 | const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height, | ||
| 697 | src_params.depth); | ||
| 698 | TryCopyImage(surface, new_surface, copy_params); | ||
| 699 | } | ||
| 684 | } | 700 | } |
| 685 | if (passed_tests == 0) { | 701 | if (passed_tests == 0) { |
| 686 | return {}; | 702 | return std::nullopt; |
| 703 | } | ||
| 704 | if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) { | ||
| 687 | // In Accurate GPU all tests should pass, else we recycle | 705 | // In Accurate GPU all tests should pass, else we recycle |
| 688 | } else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) { | 706 | return std::nullopt; |
| 689 | return {}; | ||
| 690 | } | 707 | } |
| 708 | |||
| 709 | const bool modified = std::any_of(overlaps.begin(), overlaps.end(), test_modified); | ||
| 691 | for (const auto& surface : overlaps) { | 710 | for (const auto& surface : overlaps) { |
| 692 | Unregister(surface); | 711 | Unregister(surface); |
| 693 | } | 712 | } |
| 713 | |||
| 694 | new_surface->MarkAsModified(modified, Tick()); | 714 | new_surface->MarkAsModified(modified, Tick()); |
| 695 | Register(new_surface); | 715 | Register(new_surface); |
| 696 | return {{new_surface, new_surface->GetMainView()}}; | 716 | return {{new_surface, new_surface->GetMainView()}}; |
| @@ -708,53 +728,11 @@ private: | |||
| 708 | * @param preserve_contents Indicates that the new surface should be loaded from memory or | 728 | * @param preserve_contents Indicates that the new surface should be loaded from memory or |
| 709 | * left blank. | 729 | * left blank. |
| 710 | */ | 730 | */ |
| 711 | std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, | 731 | std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(VectorSurface& overlaps, |
| 712 | const SurfaceParams& params, | 732 | const SurfaceParams& params, |
| 713 | const GPUVAddr gpu_addr, | 733 | GPUVAddr gpu_addr, VAddr cpu_addr, |
| 714 | const VAddr cpu_addr, | ||
| 715 | bool preserve_contents) { | 734 | bool preserve_contents) { |
| 716 | if (params.target == SurfaceTarget::Texture3D) { | 735 | if (params.target != SurfaceTarget::Texture3D) { |
| 717 | bool failed = false; | ||
| 718 | if (params.num_levels > 1) { | ||
| 719 | // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach | ||
| 720 | return std::nullopt; | ||
| 721 | } | ||
| 722 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | ||
| 723 | bool modified = false; | ||
| 724 | for (auto& surface : overlaps) { | ||
| 725 | const SurfaceParams& src_params = surface->GetSurfaceParams(); | ||
| 726 | if (src_params.target != SurfaceTarget::Texture2D) { | ||
| 727 | failed = true; | ||
| 728 | break; | ||
| 729 | } | ||
| 730 | if (src_params.height != params.height) { | ||
| 731 | failed = true; | ||
| 732 | break; | ||
| 733 | } | ||
| 734 | if (src_params.block_depth != params.block_depth || | ||
| 735 | src_params.block_height != params.block_height) { | ||
| 736 | failed = true; | ||
| 737 | break; | ||
| 738 | } | ||
| 739 | const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr); | ||
| 740 | const auto offsets = params.GetBlockOffsetXYZ(offset); | ||
| 741 | const auto z = std::get<2>(offsets); | ||
| 742 | modified |= surface->IsModified(); | ||
| 743 | const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height, | ||
| 744 | 1); | ||
| 745 | ImageCopy(surface, new_surface, copy_params); | ||
| 746 | } | ||
| 747 | if (failed) { | ||
| 748 | return std::nullopt; | ||
| 749 | } | ||
| 750 | for (const auto& surface : overlaps) { | ||
| 751 | Unregister(surface); | ||
| 752 | } | ||
| 753 | new_surface->MarkAsModified(modified, Tick()); | ||
| 754 | Register(new_surface); | ||
| 755 | auto view = new_surface->GetMainView(); | ||
| 756 | return {{std::move(new_surface), view}}; | ||
| 757 | } else { | ||
| 758 | for (const auto& surface : overlaps) { | 736 | for (const auto& surface : overlaps) { |
| 759 | if (!surface->MatchTarget(params.target)) { | 737 | if (!surface->MatchTarget(params.target)) { |
| 760 | if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { | 738 | if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { |
| @@ -770,11 +748,60 @@ private: | |||
| 770 | continue; | 748 | continue; |
| 771 | } | 749 | } |
| 772 | if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { | 750 | if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { |
| 773 | return {{surface, surface->GetMainView()}}; | 751 | return std::make_pair(surface, surface->GetMainView()); |
| 774 | } | 752 | } |
| 775 | } | 753 | } |
| 776 | return InitializeSurface(gpu_addr, params, preserve_contents); | 754 | return InitializeSurface(gpu_addr, params, preserve_contents); |
| 777 | } | 755 | } |
| 756 | |||
| 757 | if (params.num_levels > 1) { | ||
| 758 | // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach | ||
| 759 | return std::nullopt; | ||
| 760 | } | ||
| 761 | |||
| 762 | if (overlaps.size() == 1) { | ||
| 763 | const auto& surface = overlaps[0]; | ||
| 764 | const SurfaceParams& overlap_params = surface->GetSurfaceParams(); | ||
| 765 | // Don't attempt to render to textures with more than one level for now | ||
| 766 | // The texture has to be to the right or the sample address if we want to render to it | ||
| 767 | if (overlap_params.num_levels == 1 && cpu_addr >= surface->GetCpuAddr()) { | ||
| 768 | const u32 offset = static_cast<u32>(cpu_addr - surface->GetCpuAddr()); | ||
| 769 | const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset)); | ||
| 770 | if (slice < overlap_params.depth) { | ||
| 771 | auto view = surface->Emplace3DView(slice, params.depth, 0, 1); | ||
| 772 | return std::make_pair(std::move(surface), std::move(view)); | ||
| 773 | } | ||
| 774 | } | ||
| 775 | } | ||
| 776 | |||
| 777 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | ||
| 778 | bool modified = false; | ||
| 779 | |||
| 780 | for (auto& surface : overlaps) { | ||
| 781 | const SurfaceParams& src_params = surface->GetSurfaceParams(); | ||
| 782 | if (src_params.target != SurfaceTarget::Texture2D || | ||
| 783 | src_params.height != params.height || | ||
| 784 | src_params.block_depth != params.block_depth || | ||
| 785 | src_params.block_height != params.block_height) { | ||
| 786 | return std::nullopt; | ||
| 787 | } | ||
| 788 | modified |= surface->IsModified(); | ||
| 789 | |||
| 790 | const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr); | ||
| 791 | const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset)); | ||
| 792 | const u32 width = params.width; | ||
| 793 | const u32 height = params.height; | ||
| 794 | const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1); | ||
| 795 | TryCopyImage(surface, new_surface, copy_params); | ||
| 796 | } | ||
| 797 | for (const auto& surface : overlaps) { | ||
| 798 | Unregister(surface); | ||
| 799 | } | ||
| 800 | new_surface->MarkAsModified(modified, Tick()); | ||
| 801 | Register(new_surface); | ||
| 802 | |||
| 803 | TView view = new_surface->GetMainView(); | ||
| 804 | return std::make_pair(std::move(new_surface), std::move(view)); | ||
| 778 | } | 805 | } |
| 779 | 806 | ||
| 780 | /** | 807 | /** |
| @@ -810,7 +837,7 @@ private: | |||
| 810 | TSurface& current_surface = iter->second; | 837 | TSurface& current_surface = iter->second; |
| 811 | const auto topological_result = current_surface->MatchesTopology(params); | 838 | const auto topological_result = current_surface->MatchesTopology(params); |
| 812 | if (topological_result != MatchTopologyResult::FullMatch) { | 839 | if (topological_result != MatchTopologyResult::FullMatch) { |
| 813 | std::vector<TSurface> overlaps{current_surface}; | 840 | VectorSurface overlaps{current_surface}; |
| 814 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, | 841 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
| 815 | topological_result); | 842 | topological_result); |
| 816 | } | 843 | } |
| @@ -852,7 +879,7 @@ private: | |||
| 852 | } | 879 | } |
| 853 | } | 880 | } |
| 854 | 881 | ||
| 855 | // Check if it's a 3D texture | 882 | // Manage 3D textures |
| 856 | if (params.block_depth > 0) { | 883 | if (params.block_depth > 0) { |
| 857 | auto surface = | 884 | auto surface = |
| 858 | Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents); | 885 | Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents); |
| @@ -868,12 +895,9 @@ private: | |||
| 868 | // two things either the candidate surface is a supertexture of the overlap | 895 | // two things either the candidate surface is a supertexture of the overlap |
| 869 | // or they don't match in any known way. | 896 | // or they don't match in any known way. |
| 870 | if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) { | 897 | if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) { |
| 871 | if (current_surface->GetGpuAddr() == gpu_addr) { | 898 | const std::optional view = TryReconstructSurface(overlaps, params, gpu_addr); |
| 872 | std::optional<std::pair<TSurface, TView>> view = | 899 | if (view) { |
| 873 | TryReconstructSurface(overlaps, params, gpu_addr); | 900 | return *view; |
| 874 | if (view) { | ||
| 875 | return *view; | ||
| 876 | } | ||
| 877 | } | 901 | } |
| 878 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, | 902 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
| 879 | MatchTopologyResult::FullMatch); | 903 | MatchTopologyResult::FullMatch); |
| @@ -1030,7 +1054,7 @@ private: | |||
| 1030 | void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params, | 1054 | void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params, |
| 1031 | const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) { | 1055 | const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) { |
| 1032 | auto deduced_src = DeduceSurface(src_gpu_addr, src_params); | 1056 | auto deduced_src = DeduceSurface(src_gpu_addr, src_params); |
| 1033 | auto deduced_dst = DeduceSurface(src_gpu_addr, src_params); | 1057 | auto deduced_dst = DeduceSurface(dst_gpu_addr, dst_params); |
| 1034 | if (deduced_src.Failed() || deduced_dst.Failed()) { | 1058 | if (deduced_src.Failed() || deduced_dst.Failed()) { |
| 1035 | return; | 1059 | return; |
| 1036 | } | 1060 | } |
| @@ -1126,23 +1150,25 @@ private: | |||
| 1126 | } | 1150 | } |
| 1127 | } | 1151 | } |
| 1128 | 1152 | ||
| 1129 | std::vector<TSurface> GetSurfacesInRegion(const VAddr cpu_addr, const std::size_t size) { | 1153 | VectorSurface GetSurfacesInRegion(const VAddr cpu_addr, const std::size_t size) { |
| 1130 | if (size == 0) { | 1154 | if (size == 0) { |
| 1131 | return {}; | 1155 | return {}; |
| 1132 | } | 1156 | } |
| 1133 | const VAddr cpu_addr_end = cpu_addr + size; | 1157 | const VAddr cpu_addr_end = cpu_addr + size; |
| 1134 | VAddr start = cpu_addr >> registry_page_bits; | ||
| 1135 | const VAddr end = (cpu_addr_end - 1) >> registry_page_bits; | 1158 | const VAddr end = (cpu_addr_end - 1) >> registry_page_bits; |
| 1136 | std::vector<TSurface> surfaces; | 1159 | VectorSurface surfaces; |
| 1137 | while (start <= end) { | 1160 | for (VAddr start = cpu_addr >> registry_page_bits; start <= end; ++start) { |
| 1138 | std::vector<TSurface>& list = registry[start]; | 1161 | const auto it = registry.find(start); |
| 1139 | for (auto& surface : list) { | 1162 | if (it == registry.end()) { |
| 1140 | if (!surface->IsPicked() && surface->Overlaps(cpu_addr, cpu_addr_end)) { | 1163 | continue; |
| 1141 | surface->MarkAsPicked(true); | 1164 | } |
| 1142 | surfaces.push_back(surface); | 1165 | for (auto& surface : it->second) { |
| 1166 | if (surface->IsPicked() || !surface->Overlaps(cpu_addr, cpu_addr_end)) { | ||
| 1167 | continue; | ||
| 1143 | } | 1168 | } |
| 1169 | surface->MarkAsPicked(true); | ||
| 1170 | surfaces.push_back(surface); | ||
| 1144 | } | 1171 | } |
| 1145 | start++; | ||
| 1146 | } | 1172 | } |
| 1147 | for (auto& surface : surfaces) { | 1173 | for (auto& surface : surfaces) { |
| 1148 | surface->MarkAsPicked(false); | 1174 | surface->MarkAsPicked(false); |
| @@ -1167,6 +1193,19 @@ private: | |||
| 1167 | return {}; | 1193 | return {}; |
| 1168 | } | 1194 | } |
| 1169 | 1195 | ||
| 1196 | /// Try to do an image copy logging when formats are incompatible. | ||
| 1197 | void TryCopyImage(TSurface& src, TSurface& dst, const CopyParams& copy) { | ||
| 1198 | const SurfaceParams& src_params = src->GetSurfaceParams(); | ||
| 1199 | const SurfaceParams& dst_params = dst->GetSurfaceParams(); | ||
| 1200 | if (!format_compatibility.TestCopy(src_params.pixel_format, dst_params.pixel_format)) { | ||
| 1201 | LOG_ERROR(HW_GPU, "Illegal copy between formats={{{}, {}}}", | ||
| 1202 | static_cast<int>(dst_params.pixel_format), | ||
| 1203 | static_cast<int>(src_params.pixel_format)); | ||
| 1204 | return; | ||
| 1205 | } | ||
| 1206 | ImageCopy(src, dst, copy); | ||
| 1207 | } | ||
| 1208 | |||
| 1170 | constexpr PixelFormat GetSiblingFormat(PixelFormat format) const { | 1209 | constexpr PixelFormat GetSiblingFormat(PixelFormat format) const { |
| 1171 | return siblings_table[static_cast<std::size_t>(format)]; | 1210 | return siblings_table[static_cast<std::size_t>(format)]; |
| 1172 | } | 1211 | } |
| @@ -1216,6 +1255,7 @@ private: | |||
| 1216 | VideoCore::RasterizerInterface& rasterizer; | 1255 | VideoCore::RasterizerInterface& rasterizer; |
| 1217 | 1256 | ||
| 1218 | FormatLookupTable format_lookup_table; | 1257 | FormatLookupTable format_lookup_table; |
| 1258 | FormatCompatibility format_compatibility; | ||
| 1219 | 1259 | ||
| 1220 | u64 ticks{}; | 1260 | u64 ticks{}; |
| 1221 | 1261 | ||
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index d1939d744..4171e3ef2 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp | |||
| @@ -48,7 +48,7 @@ constexpr std::array<float, 256> SRGB_CONVERSION_LUT = { | |||
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | unsigned SettingsMinimumAnisotropy() noexcept { | 50 | unsigned SettingsMinimumAnisotropy() noexcept { |
| 51 | switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) { | 51 | switch (static_cast<Anisotropy>(Settings::values.max_anisotropy.GetValue())) { |
| 52 | default: | 52 | default: |
| 53 | case Anisotropy::Default: | 53 | case Anisotropy::Default: |
| 54 | return 1U; | 54 | return 1U; |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index f60bdc60a..45f360bdd 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -19,7 +19,7 @@ namespace { | |||
| 19 | std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | 19 | std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, |
| 20 | Core::System& system, | 20 | Core::System& system, |
| 21 | Core::Frontend::GraphicsContext& context) { | 21 | Core::Frontend::GraphicsContext& context) { |
| 22 | switch (Settings::values.renderer_backend) { | 22 | switch (Settings::values.renderer_backend.GetValue()) { |
| 23 | case Settings::RendererBackend::OpenGL: | 23 | case Settings::RendererBackend::OpenGL: |
| 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); | 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); |
| 25 | #ifdef HAS_VULKAN | 25 | #ifdef HAS_VULKAN |
| @@ -42,7 +42,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor | |||
| 42 | return nullptr; | 42 | return nullptr; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | if (Settings::values.use_asynchronous_gpu_emulation) { | 45 | if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { |
| 46 | return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), | 46 | return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), |
| 47 | std::move(context)); | 47 | std::move(context)); |
| 48 | } | 48 | } |
| @@ -51,8 +51,8 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor | |||
| 51 | 51 | ||
| 52 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { | 52 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { |
| 53 | return static_cast<u16>( | 53 | return static_cast<u16>( |
| 54 | Settings::values.resolution_factor != 0 | 54 | Settings::values.resolution_factor.GetValue() != 0 |
| 55 | ? Settings::values.resolution_factor | 55 | ? Settings::values.resolution_factor.GetValue() |
| 56 | : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()); | 56 | : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()); |
| 57 | } | 57 | } |
| 58 | 58 | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 8b9404718..6b25a7fa0 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -24,6 +24,8 @@ add_executable(yuzu | |||
| 24 | compatibility_list.h | 24 | compatibility_list.h |
| 25 | configuration/config.cpp | 25 | configuration/config.cpp |
| 26 | configuration/config.h | 26 | configuration/config.h |
| 27 | configuration/configuration_shared.cpp | ||
| 28 | configuration/configuration_shared.h | ||
| 27 | configuration/configure.ui | 29 | configuration/configure.ui |
| 28 | configuration/configure_audio.cpp | 30 | configuration/configure_audio.cpp |
| 29 | configuration/configure_audio.h | 31 | configuration/configure_audio.h |
| @@ -60,9 +62,12 @@ add_executable(yuzu | |||
| 60 | configuration/configure_mouse_advanced.cpp | 62 | configuration/configure_mouse_advanced.cpp |
| 61 | configuration/configure_mouse_advanced.h | 63 | configuration/configure_mouse_advanced.h |
| 62 | configuration/configure_mouse_advanced.ui | 64 | configuration/configure_mouse_advanced.ui |
| 63 | configuration/configure_per_general.cpp | 65 | configuration/configure_per_game.cpp |
| 64 | configuration/configure_per_general.h | 66 | configuration/configure_per_game.h |
| 65 | configuration/configure_per_general.ui | 67 | configuration/configure_per_game.ui |
| 68 | configuration/configure_per_game_addons.cpp | ||
| 69 | configuration/configure_per_game_addons.h | ||
| 70 | configuration/configure_per_game_addons.ui | ||
| 66 | configuration/configure_profile_manager.cpp | 71 | configuration/configure_profile_manager.cpp |
| 67 | configuration/configure_profile_manager.h | 72 | configuration/configure_profile_manager.h |
| 68 | configuration/configure_profile_manager.ui | 73 | configuration/configure_profile_manager.ui |
| @@ -147,7 +152,7 @@ endif() | |||
| 147 | create_target_directory_groups(yuzu) | 152 | create_target_directory_groups(yuzu) |
| 148 | 153 | ||
| 149 | target_link_libraries(yuzu PRIVATE common core input_common video_core) | 154 | target_link_libraries(yuzu PRIVATE common core input_common video_core) |
| 150 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) | 155 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) |
| 151 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | 156 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) |
| 152 | 157 | ||
| 153 | if (ENABLE_VULKAN AND NOT WIN32) | 158 | if (ENABLE_VULKAN AND NOT WIN32) |
| @@ -208,6 +213,10 @@ if (MSVC) | |||
| 208 | copy_yuzu_unicorn_deps(yuzu) | 213 | copy_yuzu_unicorn_deps(yuzu) |
| 209 | endif() | 214 | endif() |
| 210 | 215 | ||
| 216 | if (NOT APPLE) | ||
| 217 | target_compile_definitions(yuzu PRIVATE HAS_OPENGL) | ||
| 218 | endif() | ||
| 219 | |||
| 211 | if (ENABLE_VULKAN) | 220 | if (ENABLE_VULKAN) |
| 212 | target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) | 221 | target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) |
| 213 | target_compile_definitions(yuzu PRIVATE HAS_VULKAN) | 222 | target_compile_definitions(yuzu PRIVATE HAS_VULKAN) |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 1adf8932b..5738787ac 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -8,13 +8,16 @@ | |||
| 8 | #include <QHBoxLayout> | 8 | #include <QHBoxLayout> |
| 9 | #include <QKeyEvent> | 9 | #include <QKeyEvent> |
| 10 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 11 | #include <QOffscreenSurface> | ||
| 12 | #include <QOpenGLContext> | ||
| 13 | #include <QPainter> | 11 | #include <QPainter> |
| 14 | #include <QScreen> | 12 | #include <QScreen> |
| 15 | #include <QStringList> | 13 | #include <QStringList> |
| 16 | #include <QWindow> | 14 | #include <QWindow> |
| 17 | 15 | ||
| 16 | #ifdef HAS_OPENGL | ||
| 17 | #include <QOffscreenSurface> | ||
| 18 | #include <QOpenGLContext> | ||
| 19 | #endif | ||
| 20 | |||
| 18 | #if !defined(WIN32) && HAS_VULKAN | 21 | #if !defined(WIN32) && HAS_VULKAN |
| 19 | #include <qpa/qplatformnativeinterface.h> | 22 | #include <qpa/qplatformnativeinterface.h> |
| 20 | #endif | 23 | #endif |
| @@ -41,49 +44,65 @@ EmuThread::EmuThread() = default; | |||
| 41 | EmuThread::~EmuThread() = default; | 44 | EmuThread::~EmuThread() = default; |
| 42 | 45 | ||
| 43 | void EmuThread::run() { | 46 | void EmuThread::run() { |
| 44 | MicroProfileOnThreadCreate("EmuThread"); | 47 | std::string name = "yuzu:EmuControlThread"; |
| 48 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 49 | Common::SetCurrentThreadName(name.c_str()); | ||
| 50 | |||
| 51 | auto& system = Core::System::GetInstance(); | ||
| 52 | |||
| 53 | system.RegisterHostThread(); | ||
| 54 | |||
| 55 | auto& gpu = system.GPU(); | ||
| 45 | 56 | ||
| 46 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU | 57 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 47 | // execution. | 58 | // execution. |
| 48 | Core::System::GetInstance().GPU().Start(); | 59 | gpu.Start(); |
| 60 | |||
| 61 | gpu.ObtainContext(); | ||
| 49 | 62 | ||
| 50 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 51 | 64 | ||
| 52 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( | 65 | system.Renderer().Rasterizer().LoadDiskResources( |
| 53 | stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { | 66 | stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { |
| 54 | emit LoadProgress(stage, value, total); | 67 | emit LoadProgress(stage, value, total); |
| 55 | }); | 68 | }); |
| 56 | 69 | ||
| 57 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 70 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 58 | 71 | ||
| 72 | gpu.ReleaseContext(); | ||
| 73 | |||
| 59 | // Holds whether the cpu was running during the last iteration, | 74 | // Holds whether the cpu was running during the last iteration, |
| 60 | // so that the DebugModeLeft signal can be emitted before the | 75 | // so that the DebugModeLeft signal can be emitted before the |
| 61 | // next execution step | 76 | // next execution step |
| 62 | bool was_active = false; | 77 | bool was_active = false; |
| 63 | while (!stop_run) { | 78 | while (!stop_run) { |
| 64 | if (running) { | 79 | if (running) { |
| 65 | if (!was_active) | 80 | if (was_active) { |
| 66 | emit DebugModeLeft(); | 81 | emit DebugModeLeft(); |
| 82 | } | ||
| 67 | 83 | ||
| 68 | Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); | 84 | running_guard = true; |
| 85 | Core::System::ResultStatus result = system.Run(); | ||
| 69 | if (result != Core::System::ResultStatus::Success) { | 86 | if (result != Core::System::ResultStatus::Success) { |
| 87 | running_guard = false; | ||
| 70 | this->SetRunning(false); | 88 | this->SetRunning(false); |
| 71 | emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); | 89 | emit ErrorThrown(result, system.GetStatusDetails()); |
| 72 | } | 90 | } |
| 91 | running_wait.Wait(); | ||
| 92 | result = system.Pause(); | ||
| 93 | if (result != Core::System::ResultStatus::Success) { | ||
| 94 | running_guard = false; | ||
| 95 | this->SetRunning(false); | ||
| 96 | emit ErrorThrown(result, system.GetStatusDetails()); | ||
| 97 | } | ||
| 98 | running_guard = false; | ||
| 73 | 99 | ||
| 74 | was_active = running || exec_step; | 100 | if (!stop_run) { |
| 75 | if (!was_active && !stop_run) | 101 | was_active = true; |
| 76 | emit DebugModeEntered(); | 102 | emit DebugModeEntered(); |
| 103 | } | ||
| 77 | } else if (exec_step) { | 104 | } else if (exec_step) { |
| 78 | if (!was_active) | 105 | UNIMPLEMENTED(); |
| 79 | emit DebugModeLeft(); | ||
| 80 | |||
| 81 | exec_step = false; | ||
| 82 | Core::System::GetInstance().SingleStep(); | ||
| 83 | emit DebugModeEntered(); | ||
| 84 | yieldCurrentThread(); | ||
| 85 | |||
| 86 | was_active = false; | ||
| 87 | } else { | 106 | } else { |
| 88 | std::unique_lock lock{running_mutex}; | 107 | std::unique_lock lock{running_mutex}; |
| 89 | running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); | 108 | running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); |
| @@ -91,13 +110,14 @@ void EmuThread::run() { | |||
| 91 | } | 110 | } |
| 92 | 111 | ||
| 93 | // Shutdown the core emulation | 112 | // Shutdown the core emulation |
| 94 | Core::System::GetInstance().Shutdown(); | 113 | system.Shutdown(); |
| 95 | 114 | ||
| 96 | #if MICROPROFILE_ENABLED | 115 | #if MICROPROFILE_ENABLED |
| 97 | MicroProfileOnThreadExit(); | 116 | MicroProfileOnThreadExit(); |
| 98 | #endif | 117 | #endif |
| 99 | } | 118 | } |
| 100 | 119 | ||
| 120 | #ifdef HAS_OPENGL | ||
| 101 | class OpenGLSharedContext : public Core::Frontend::GraphicsContext { | 121 | class OpenGLSharedContext : public Core::Frontend::GraphicsContext { |
| 102 | public: | 122 | public: |
| 103 | /// Create the original context that should be shared from | 123 | /// Create the original context that should be shared from |
| @@ -106,6 +126,9 @@ public: | |||
| 106 | format.setVersion(4, 3); | 126 | format.setVersion(4, 3); |
| 107 | format.setProfile(QSurfaceFormat::CompatibilityProfile); | 127 | format.setProfile(QSurfaceFormat::CompatibilityProfile); |
| 108 | format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 128 | format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); |
| 129 | if (Settings::values.renderer_debug) { | ||
| 130 | format.setOption(QSurfaceFormat::FormatOption::DebugContext); | ||
| 131 | } | ||
| 109 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 132 | // TODO: expose a setting for buffer value (ie default/single/double/triple) |
| 110 | format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 133 | format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); |
| 111 | format.setSwapInterval(0); | 134 | format.setSwapInterval(0); |
| @@ -122,7 +145,7 @@ public: | |||
| 122 | 145 | ||
| 123 | // disable vsync for any shared contexts | 146 | // disable vsync for any shared contexts |
| 124 | auto format = share_context->format(); | 147 | auto format = share_context->format(); |
| 125 | format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); | 148 | format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0); |
| 126 | 149 | ||
| 127 | context = std::make_unique<QOpenGLContext>(); | 150 | context = std::make_unique<QOpenGLContext>(); |
| 128 | context->setShareContext(share_context); | 151 | context->setShareContext(share_context); |
| @@ -180,6 +203,7 @@ private: | |||
| 180 | std::unique_ptr<QOffscreenSurface> offscreen_surface{}; | 203 | std::unique_ptr<QOffscreenSurface> offscreen_surface{}; |
| 181 | QSurface* surface; | 204 | QSurface* surface; |
| 182 | }; | 205 | }; |
| 206 | #endif | ||
| 183 | 207 | ||
| 184 | class DummyContext : public Core::Frontend::GraphicsContext {}; | 208 | class DummyContext : public Core::Frontend::GraphicsContext {}; |
| 185 | 209 | ||
| @@ -352,7 +376,7 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 352 | } | 376 | } |
| 353 | 377 | ||
| 354 | qreal GRenderWindow::windowPixelRatio() const { | 378 | qreal GRenderWindow::windowPixelRatio() const { |
| 355 | return devicePixelRatio(); | 379 | return devicePixelRatioF(); |
| 356 | } | 380 | } |
| 357 | 381 | ||
| 358 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { | 382 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { |
| @@ -470,13 +494,15 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | |||
| 470 | } | 494 | } |
| 471 | 495 | ||
| 472 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 496 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 473 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 497 | #ifdef HAS_OPENGL |
| 498 | if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) { | ||
| 474 | auto c = static_cast<OpenGLSharedContext*>(main_context.get()); | 499 | auto c = static_cast<OpenGLSharedContext*>(main_context.get()); |
| 475 | // Bind the shared contexts to the main surface in case the backend wants to take over | 500 | // Bind the shared contexts to the main surface in case the backend wants to take over |
| 476 | // presentation | 501 | // presentation |
| 477 | return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), | 502 | return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), |
| 478 | child_widget->windowHandle()); | 503 | child_widget->windowHandle()); |
| 479 | } | 504 | } |
| 505 | #endif | ||
| 480 | return std::make_unique<DummyContext>(); | 506 | return std::make_unique<DummyContext>(); |
| 481 | } | 507 | } |
| 482 | 508 | ||
| @@ -485,7 +511,7 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 485 | 511 | ||
| 486 | first_frame = false; | 512 | first_frame = false; |
| 487 | 513 | ||
| 488 | switch (Settings::values.renderer_backend) { | 514 | switch (Settings::values.renderer_backend.GetValue()) { |
| 489 | case Settings::RendererBackend::OpenGL: | 515 | case Settings::RendererBackend::OpenGL: |
| 490 | if (!InitializeOpenGL()) { | 516 | if (!InitializeOpenGL()) { |
| 491 | return false; | 517 | return false; |
| @@ -512,7 +538,7 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 512 | OnFramebufferSizeChanged(); | 538 | OnFramebufferSizeChanged(); |
| 513 | BackupGeometry(); | 539 | BackupGeometry(); |
| 514 | 540 | ||
| 515 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 541 | if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) { |
| 516 | if (!LoadOpenGL()) { | 542 | if (!LoadOpenGL()) { |
| 517 | return false; | 543 | return false; |
| 518 | } | 544 | } |
| @@ -557,6 +583,7 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 557 | } | 583 | } |
| 558 | 584 | ||
| 559 | bool GRenderWindow::InitializeOpenGL() { | 585 | bool GRenderWindow::InitializeOpenGL() { |
| 586 | #ifdef HAS_OPENGL | ||
| 560 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 587 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 561 | // WA_DontShowOnScreen, WA_DeleteOnClose | 588 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 562 | auto child = new OpenGLRenderWidget(this); | 589 | auto child = new OpenGLRenderWidget(this); |
| @@ -568,6 +595,11 @@ bool GRenderWindow::InitializeOpenGL() { | |||
| 568 | std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle())); | 595 | std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle())); |
| 569 | 596 | ||
| 570 | return true; | 597 | return true; |
| 598 | #else | ||
| 599 | QMessageBox::warning(this, tr("OpenGL not available!"), | ||
| 600 | tr("yuzu has not been compiled with OpenGL support.")); | ||
| 601 | return false; | ||
| 602 | #endif | ||
| 571 | } | 603 | } |
| 572 | 604 | ||
| 573 | bool GRenderWindow::InitializeVulkan() { | 605 | bool GRenderWindow::InitializeVulkan() { |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3626604ca..6c59b4d5c 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -59,6 +59,12 @@ public: | |||
| 59 | this->running = running; | 59 | this->running = running; |
| 60 | lock.unlock(); | 60 | lock.unlock(); |
| 61 | running_cv.notify_all(); | 61 | running_cv.notify_all(); |
| 62 | if (!running) { | ||
| 63 | running_wait.Set(); | ||
| 64 | /// Wait until effectively paused | ||
| 65 | while (running_guard) | ||
| 66 | ; | ||
| 67 | } | ||
| 62 | } | 68 | } |
| 63 | 69 | ||
| 64 | /** | 70 | /** |
| @@ -84,6 +90,8 @@ private: | |||
| 84 | std::atomic_bool stop_run{false}; | 90 | std::atomic_bool stop_run{false}; |
| 85 | std::mutex running_mutex; | 91 | std::mutex running_mutex; |
| 86 | std::condition_variable running_cv; | 92 | std::condition_variable running_cv; |
| 93 | Common::Event running_wait{}; | ||
| 94 | std::atomic_bool running_guard{false}; | ||
| 87 | 95 | ||
| 88 | signals: | 96 | signals: |
| 89 | /** | 97 | /** |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index b08b87426..1b2b1b2bb 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -13,17 +13,20 @@ | |||
| 13 | #include "input_common/udp/client.h" | 13 | #include "input_common/udp/client.h" |
| 14 | #include "yuzu/configuration/config.h" | 14 | #include "yuzu/configuration/config.h" |
| 15 | 15 | ||
| 16 | Config::Config() { | 16 | Config::Config(const std::string& config_file, bool is_global) { |
| 17 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 17 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| 18 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; | 18 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file; |
| 19 | FileUtil::CreateFullPath(qt_config_loc); | 19 | FileUtil::CreateFullPath(qt_config_loc); |
| 20 | qt_config = | 20 | qt_config = |
| 21 | std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | 21 | std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); |
| 22 | global = is_global; | ||
| 22 | Reload(); | 23 | Reload(); |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | Config::~Config() { | 26 | Config::~Config() { |
| 26 | Save(); | 27 | if (global) { |
| 28 | Save(); | ||
| 29 | } | ||
| 27 | } | 30 | } |
| 28 | 31 | ||
| 29 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { | 32 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { |
| @@ -211,8 +214,8 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default | |||
| 211 | // This must be in alphabetical order according to action name as it must have the same order as | 214 | // This must be in alphabetical order according to action name as it must have the same order as |
| 212 | // UISetting::values.shortcuts, which is alphabetically ordered. | 215 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 213 | // clang-format off | 216 | // clang-format off |
| 214 | const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{ | 217 | const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ |
| 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, | 218 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, |
| 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | 219 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, |
| 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, | 220 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, |
| 218 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, | 221 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, |
| @@ -220,8 +223,9 @@ const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{ | |||
| 220 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, | 223 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, |
| 221 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, | 224 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, |
| 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, | 225 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, |
| 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, | 226 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, |
| 224 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, | 227 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, |
| 228 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, | ||
| 225 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, | 229 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, |
| 226 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, | 230 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, |
| 227 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, | 231 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, |
| @@ -401,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() { | |||
| 401 | void Config::ReadAudioValues() { | 405 | void Config::ReadAudioValues() { |
| 402 | qt_config->beginGroup(QStringLiteral("Audio")); | 406 | qt_config->beginGroup(QStringLiteral("Audio")); |
| 403 | 407 | ||
| 404 | Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) | 408 | if (global) { |
| 405 | .toString() | 409 | Settings::values.sink_id = |
| 406 | .toStdString(); | 410 | ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) |
| 407 | Settings::values.enable_audio_stretching = | 411 | .toString() |
| 408 | ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool(); | 412 | .toStdString(); |
| 409 | Settings::values.audio_device_id = | 413 | Settings::values.audio_device_id = |
| 410 | ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) | 414 | ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) |
| 411 | .toString() | 415 | .toString() |
| 412 | .toStdString(); | 416 | .toStdString(); |
| 413 | Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat(); | 417 | } |
| 418 | ReadSettingGlobal(Settings::values.enable_audio_stretching, | ||
| 419 | QStringLiteral("enable_audio_stretching"), true); | ||
| 420 | ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1); | ||
| 414 | 421 | ||
| 415 | qt_config->endGroup(); | 422 | qt_config->endGroup(); |
| 416 | } | 423 | } |
| @@ -439,6 +446,8 @@ void Config::ReadControlValues() { | |||
| 439 | .toInt()); | 446 | .toInt()); |
| 440 | Settings::values.udp_pad_index = | 447 | Settings::values.udp_pad_index = |
| 441 | static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); | 448 | static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); |
| 449 | Settings::values.use_docked_mode = | ||
| 450 | ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); | ||
| 442 | 451 | ||
| 443 | qt_config->endGroup(); | 452 | qt_config->endGroup(); |
| 444 | } | 453 | } |
| @@ -446,7 +455,7 @@ void Config::ReadControlValues() { | |||
| 446 | void Config::ReadCoreValues() { | 455 | void Config::ReadCoreValues() { |
| 447 | qt_config->beginGroup(QStringLiteral("Core")); | 456 | qt_config->beginGroup(QStringLiteral("Core")); |
| 448 | 457 | ||
| 449 | Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool(); | 458 | ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false); |
| 450 | 459 | ||
| 451 | qt_config->endGroup(); | 460 | qt_config->endGroup(); |
| 452 | } | 461 | } |
| @@ -533,6 +542,8 @@ void Config::ReadDebuggingValues() { | |||
| 533 | Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); | 542 | Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); |
| 534 | Settings::values.disable_cpu_opt = | 543 | Settings::values.disable_cpu_opt = |
| 535 | ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool(); | 544 | ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool(); |
| 545 | Settings::values.disable_macro_jit = | ||
| 546 | ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); | ||
| 536 | 547 | ||
| 537 | qt_config->endGroup(); | 548 | qt_config->endGroup(); |
| 538 | } | 549 | } |
| @@ -625,34 +636,28 @@ void Config::ReadPathValues() { | |||
| 625 | void Config::ReadRendererValues() { | 636 | void Config::ReadRendererValues() { |
| 626 | qt_config->beginGroup(QStringLiteral("Renderer")); | 637 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 627 | 638 | ||
| 628 | Settings::values.renderer_backend = | 639 | ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0); |
| 629 | static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); | 640 | ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false); |
| 630 | Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); | 641 | ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0); |
| 631 | Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); | 642 | ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0); |
| 632 | Settings::values.resolution_factor = | 643 | ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0); |
| 633 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); | 644 | ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true); |
| 634 | Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt(); | 645 | ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100); |
| 635 | Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt(); | 646 | ReadSettingGlobal(Settings::values.use_disk_shader_cache, |
| 636 | Settings::values.use_frame_limit = | 647 | QStringLiteral("use_disk_shader_cache"), true); |
| 637 | ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); | 648 | ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0); |
| 638 | Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); | 649 | ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation, |
| 639 | Settings::values.use_disk_shader_cache = | 650 | QStringLiteral("use_asynchronous_gpu_emulation"), false); |
| 640 | ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); | 651 | ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); |
| 641 | const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt(); | 652 | ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), |
| 642 | Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); | 653 | false); |
| 643 | Settings::values.use_asynchronous_gpu_emulation = | 654 | ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), |
| 644 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); | 655 | true); |
| 645 | Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); | 656 | ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false); |
| 646 | Settings::values.use_assembly_shaders = | 657 | |
| 647 | ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool(); | 658 | ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); |
| 648 | Settings::values.use_fast_gpu_time = | 659 | ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); |
| 649 | ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool(); | 660 | ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); |
| 650 | Settings::values.force_30fps_mode = | ||
| 651 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); | ||
| 652 | |||
| 653 | Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat(); | ||
| 654 | Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat(); | ||
| 655 | Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat(); | ||
| 656 | 661 | ||
| 657 | qt_config->endGroup(); | 662 | qt_config->endGroup(); |
| 658 | } | 663 | } |
| @@ -664,11 +669,13 @@ void Config::ReadShortcutValues() { | |||
| 664 | const auto& [keyseq, context] = shortcut; | 669 | const auto& [keyseq, context] = shortcut; |
| 665 | qt_config->beginGroup(group); | 670 | qt_config->beginGroup(group); |
| 666 | qt_config->beginGroup(name); | 671 | qt_config->beginGroup(name); |
| 672 | // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 | ||
| 673 | // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open | ||
| 674 | // a file dialog in windowed mode | ||
| 667 | UISettings::values.shortcuts.push_back( | 675 | UISettings::values.shortcuts.push_back( |
| 668 | {name, | 676 | {name, |
| 669 | group, | 677 | group, |
| 670 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), | 678 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}}); |
| 671 | ReadSetting(QStringLiteral("Context"), context).toInt()}}); | ||
| 672 | qt_config->endGroup(); | 679 | qt_config->endGroup(); |
| 673 | qt_config->endGroup(); | 680 | qt_config->endGroup(); |
| 674 | } | 681 | } |
| @@ -679,35 +686,45 @@ void Config::ReadShortcutValues() { | |||
| 679 | void Config::ReadSystemValues() { | 686 | void Config::ReadSystemValues() { |
| 680 | qt_config->beginGroup(QStringLiteral("System")); | 687 | qt_config->beginGroup(QStringLiteral("System")); |
| 681 | 688 | ||
| 682 | Settings::values.use_docked_mode = | 689 | ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0); |
| 683 | ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); | 690 | Settings::values.current_user = |
| 684 | 691 | std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1); | |
| 685 | Settings::values.current_user = std::clamp<int>( | ||
| 686 | ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1); | ||
| 687 | 692 | ||
| 688 | Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt(); | 693 | ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1); |
| 689 | 694 | ||
| 690 | Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt(); | 695 | ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1); |
| 691 | 696 | ||
| 692 | Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt(); | 697 | ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0); |
| 693 | 698 | ||
| 694 | const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); | 699 | bool rng_seed_enabled; |
| 695 | if (rng_seed_enabled) { | 700 | ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false); |
| 696 | Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); | 701 | bool rng_seed_global = |
| 697 | } else { | 702 | global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool(); |
| 698 | Settings::values.rng_seed = std::nullopt; | 703 | Settings::values.rng_seed.SetGlobal(rng_seed_global); |
| 704 | if (global || !rng_seed_global) { | ||
| 705 | if (rng_seed_enabled) { | ||
| 706 | Settings::values.rng_seed.SetValue( | ||
| 707 | ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong()); | ||
| 708 | } else { | ||
| 709 | Settings::values.rng_seed.SetValue(std::nullopt); | ||
| 710 | } | ||
| 699 | } | 711 | } |
| 700 | 712 | ||
| 701 | const auto custom_rtc_enabled = | 713 | bool custom_rtc_enabled; |
| 702 | ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); | 714 | ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false); |
| 703 | if (custom_rtc_enabled) { | 715 | bool custom_rtc_global = |
| 704 | Settings::values.custom_rtc = | 716 | global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool(); |
| 705 | std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()); | 717 | Settings::values.custom_rtc.SetGlobal(custom_rtc_global); |
| 706 | } else { | 718 | if (global || !custom_rtc_global) { |
| 707 | Settings::values.custom_rtc = std::nullopt; | 719 | if (custom_rtc_enabled) { |
| 720 | Settings::values.custom_rtc.SetValue( | ||
| 721 | std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong())); | ||
| 722 | } else { | ||
| 723 | Settings::values.custom_rtc.SetValue(std::nullopt); | ||
| 724 | } | ||
| 708 | } | 725 | } |
| 709 | 726 | ||
| 710 | Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt(); | 727 | ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1); |
| 711 | 728 | ||
| 712 | qt_config->endGroup(); | 729 | qt_config->endGroup(); |
| 713 | } | 730 | } |
| @@ -720,8 +737,6 @@ void Config::ReadUIValues() { | |||
| 720 | .toString(); | 737 | .toString(); |
| 721 | UISettings::values.enable_discord_presence = | 738 | UISettings::values.enable_discord_presence = |
| 722 | ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool(); | 739 | ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool(); |
| 723 | UISettings::values.screenshot_resolution_factor = | ||
| 724 | static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt()); | ||
| 725 | UISettings::values.select_user_on_boot = | 740 | UISettings::values.select_user_on_boot = |
| 726 | ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool(); | 741 | ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool(); |
| 727 | 742 | ||
| @@ -803,18 +818,20 @@ void Config::ReadWebServiceValues() { | |||
| 803 | } | 818 | } |
| 804 | 819 | ||
| 805 | void Config::ReadValues() { | 820 | void Config::ReadValues() { |
| 806 | ReadControlValues(); | 821 | if (global) { |
| 822 | ReadControlValues(); | ||
| 823 | ReadDataStorageValues(); | ||
| 824 | ReadDebuggingValues(); | ||
| 825 | ReadDisabledAddOnValues(); | ||
| 826 | ReadServiceValues(); | ||
| 827 | ReadUIValues(); | ||
| 828 | ReadWebServiceValues(); | ||
| 829 | ReadMiscellaneousValues(); | ||
| 830 | } | ||
| 807 | ReadCoreValues(); | 831 | ReadCoreValues(); |
| 808 | ReadRendererValues(); | 832 | ReadRendererValues(); |
| 809 | ReadAudioValues(); | 833 | ReadAudioValues(); |
| 810 | ReadDataStorageValues(); | ||
| 811 | ReadSystemValues(); | 834 | ReadSystemValues(); |
| 812 | ReadMiscellaneousValues(); | ||
| 813 | ReadDebuggingValues(); | ||
| 814 | ReadWebServiceValues(); | ||
| 815 | ReadServiceValues(); | ||
| 816 | ReadDisabledAddOnValues(); | ||
| 817 | ReadUIValues(); | ||
| 818 | } | 835 | } |
| 819 | 836 | ||
| 820 | void Config::SavePlayerValues() { | 837 | void Config::SavePlayerValues() { |
| @@ -901,30 +918,35 @@ void Config::SaveTouchscreenValues() { | |||
| 901 | } | 918 | } |
| 902 | 919 | ||
| 903 | void Config::SaveValues() { | 920 | void Config::SaveValues() { |
| 904 | SaveControlValues(); | 921 | if (global) { |
| 922 | SaveControlValues(); | ||
| 923 | SaveDataStorageValues(); | ||
| 924 | SaveDebuggingValues(); | ||
| 925 | SaveDisabledAddOnValues(); | ||
| 926 | SaveServiceValues(); | ||
| 927 | SaveUIValues(); | ||
| 928 | SaveWebServiceValues(); | ||
| 929 | SaveMiscellaneousValues(); | ||
| 930 | } | ||
| 905 | SaveCoreValues(); | 931 | SaveCoreValues(); |
| 906 | SaveRendererValues(); | 932 | SaveRendererValues(); |
| 907 | SaveAudioValues(); | 933 | SaveAudioValues(); |
| 908 | SaveDataStorageValues(); | ||
| 909 | SaveSystemValues(); | 934 | SaveSystemValues(); |
| 910 | SaveMiscellaneousValues(); | ||
| 911 | SaveDebuggingValues(); | ||
| 912 | SaveWebServiceValues(); | ||
| 913 | SaveServiceValues(); | ||
| 914 | SaveDisabledAddOnValues(); | ||
| 915 | SaveUIValues(); | ||
| 916 | } | 935 | } |
| 917 | 936 | ||
| 918 | void Config::SaveAudioValues() { | 937 | void Config::SaveAudioValues() { |
| 919 | qt_config->beginGroup(QStringLiteral("Audio")); | 938 | qt_config->beginGroup(QStringLiteral("Audio")); |
| 920 | 939 | ||
| 921 | WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id), | 940 | if (global) { |
| 922 | QStringLiteral("auto")); | 941 | WriteSetting(QStringLiteral("output_engine"), |
| 923 | WriteSetting(QStringLiteral("enable_audio_stretching"), | 942 | QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto")); |
| 924 | Settings::values.enable_audio_stretching, true); | 943 | WriteSetting(QStringLiteral("output_device"), |
| 925 | WriteSetting(QStringLiteral("output_device"), | 944 | QString::fromStdString(Settings::values.audio_device_id), |
| 926 | QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto")); | 945 | QStringLiteral("auto")); |
| 927 | WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f); | 946 | } |
| 947 | WriteSettingGlobal(QStringLiteral("enable_audio_stretching"), | ||
| 948 | Settings::values.enable_audio_stretching, true); | ||
| 949 | WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f); | ||
| 928 | 950 | ||
| 929 | qt_config->endGroup(); | 951 | qt_config->endGroup(); |
| 930 | } | 952 | } |
| @@ -947,6 +969,7 @@ void Config::SaveControlValues() { | |||
| 947 | WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port, | 969 | WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port, |
| 948 | InputCommon::CemuhookUDP::DEFAULT_PORT); | 970 | InputCommon::CemuhookUDP::DEFAULT_PORT); |
| 949 | WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); | 971 | WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); |
| 972 | WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); | ||
| 950 | 973 | ||
| 951 | qt_config->endGroup(); | 974 | qt_config->endGroup(); |
| 952 | } | 975 | } |
| @@ -954,7 +977,7 @@ void Config::SaveControlValues() { | |||
| 954 | void Config::SaveCoreValues() { | 977 | void Config::SaveCoreValues() { |
| 955 | qt_config->beginGroup(QStringLiteral("Core")); | 978 | qt_config->beginGroup(QStringLiteral("Core")); |
| 956 | 979 | ||
| 957 | WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); | 980 | WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); |
| 958 | 981 | ||
| 959 | qt_config->endGroup(); | 982 | qt_config->endGroup(); |
| 960 | } | 983 | } |
| @@ -1011,6 +1034,7 @@ void Config::SaveDebuggingValues() { | |||
| 1011 | WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); | 1034 | WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); |
| 1012 | WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); | 1035 | WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); |
| 1013 | WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false); | 1036 | WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false); |
| 1037 | WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false); | ||
| 1014 | 1038 | ||
| 1015 | qt_config->endGroup(); | 1039 | qt_config->endGroup(); |
| 1016 | } | 1040 | } |
| @@ -1076,31 +1100,34 @@ void Config::SavePathValues() { | |||
| 1076 | void Config::SaveRendererValues() { | 1100 | void Config::SaveRendererValues() { |
| 1077 | qt_config->beginGroup(QStringLiteral("Renderer")); | 1101 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 1078 | 1102 | ||
| 1079 | WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); | 1103 | WriteSettingGlobal(QStringLiteral("backend"), |
| 1104 | static_cast<int>(Settings::values.renderer_backend.GetValue(global)), | ||
| 1105 | Settings::values.renderer_backend.UsingGlobal(), 0); | ||
| 1080 | WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); | 1106 | WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); |
| 1081 | WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); | 1107 | WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); |
| 1082 | WriteSetting(QStringLiteral("resolution_factor"), | 1108 | WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); |
| 1083 | static_cast<double>(Settings::values.resolution_factor), 1.0); | 1109 | WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); |
| 1084 | WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); | 1110 | WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); |
| 1085 | WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); | 1111 | WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); |
| 1086 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); | 1112 | WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"), |
| 1087 | WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); | 1113 | Settings::values.use_disk_shader_cache, true); |
| 1088 | WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, | 1114 | WriteSettingGlobal(QStringLiteral("gpu_accuracy"), |
| 1089 | true); | 1115 | static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)), |
| 1090 | WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy), | 1116 | Settings::values.gpu_accuracy.UsingGlobal(), 0); |
| 1091 | 0); | 1117 | WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"), |
| 1092 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), | 1118 | Settings::values.use_asynchronous_gpu_emulation, false); |
| 1093 | Settings::values.use_asynchronous_gpu_emulation, false); | 1119 | WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); |
| 1094 | WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); | 1120 | WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), |
| 1095 | WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders, | 1121 | Settings::values.use_assembly_shaders, false); |
| 1096 | false); | 1122 | WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, |
| 1097 | WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true); | 1123 | true); |
| 1098 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); | 1124 | WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, |
| 1125 | false); | ||
| 1099 | 1126 | ||
| 1100 | // Cast to double because Qt's written float values are not human-readable | 1127 | // Cast to double because Qt's written float values are not human-readable |
| 1101 | WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0); | 1128 | WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); |
| 1102 | WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0); | 1129 | WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); |
| 1103 | WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0); | 1130 | WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0); |
| 1104 | 1131 | ||
| 1105 | qt_config->endGroup(); | 1132 | qt_config->endGroup(); |
| 1106 | } | 1133 | } |
| @@ -1128,23 +1155,28 @@ void Config::SaveShortcutValues() { | |||
| 1128 | void Config::SaveSystemValues() { | 1155 | void Config::SaveSystemValues() { |
| 1129 | qt_config->beginGroup(QStringLiteral("System")); | 1156 | qt_config->beginGroup(QStringLiteral("System")); |
| 1130 | 1157 | ||
| 1131 | WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); | ||
| 1132 | WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); | 1158 | WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); |
| 1133 | WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); | 1159 | WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1); |
| 1134 | WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1); | 1160 | WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1); |
| 1135 | WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); | 1161 | WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); |
| 1136 | 1162 | ||
| 1137 | WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); | 1163 | WriteSettingGlobal(QStringLiteral("rng_seed_enabled"), |
| 1138 | WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); | 1164 | Settings::values.rng_seed.GetValue(global).has_value(), |
| 1139 | 1165 | Settings::values.rng_seed.UsingGlobal(), false); | |
| 1140 | WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), | 1166 | WriteSettingGlobal(QStringLiteral("rng_seed"), |
| 1141 | false); | 1167 | Settings::values.rng_seed.GetValue(global).value_or(0), |
| 1142 | WriteSetting(QStringLiteral("custom_rtc"), | 1168 | Settings::values.rng_seed.UsingGlobal(), 0); |
| 1143 | QVariant::fromValue<long long>( | 1169 | |
| 1144 | Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), | 1170 | WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"), |
| 1145 | 0); | 1171 | Settings::values.custom_rtc.GetValue(global).has_value(), |
| 1146 | 1172 | Settings::values.custom_rtc.UsingGlobal(), false); | |
| 1147 | WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1); | 1173 | WriteSettingGlobal( |
| 1174 | QStringLiteral("custom_rtc"), | ||
| 1175 | QVariant::fromValue<long long>( | ||
| 1176 | Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()), | ||
| 1177 | Settings::values.custom_rtc.UsingGlobal(), 0); | ||
| 1178 | |||
| 1179 | WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1); | ||
| 1148 | 1180 | ||
| 1149 | qt_config->endGroup(); | 1181 | qt_config->endGroup(); |
| 1150 | } | 1182 | } |
| @@ -1156,8 +1188,6 @@ void Config::SaveUIValues() { | |||
| 1156 | QString::fromUtf8(UISettings::themes[0].second)); | 1188 | QString::fromUtf8(UISettings::themes[0].second)); |
| 1157 | WriteSetting(QStringLiteral("enable_discord_presence"), | 1189 | WriteSetting(QStringLiteral("enable_discord_presence"), |
| 1158 | UISettings::values.enable_discord_presence, true); | 1190 | UISettings::values.enable_discord_presence, true); |
| 1159 | WriteSetting(QStringLiteral("screenshot_resolution_factor"), | ||
| 1160 | UISettings::values.screenshot_resolution_factor, 0); | ||
| 1161 | WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot, | 1191 | WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot, |
| 1162 | false); | 1192 | false); |
| 1163 | 1193 | ||
| @@ -1238,6 +1268,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) | |||
| 1238 | return result; | 1268 | return result; |
| 1239 | } | 1269 | } |
| 1240 | 1270 | ||
| 1271 | template <typename Type> | ||
| 1272 | void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) { | ||
| 1273 | const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); | ||
| 1274 | setting.SetGlobal(use_global); | ||
| 1275 | if (global || !use_global) { | ||
| 1276 | setting.SetValue(ReadSetting(name).value<Type>()); | ||
| 1277 | } | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | template <typename Type> | ||
| 1281 | void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name, | ||
| 1282 | const QVariant& default_value) { | ||
| 1283 | const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); | ||
| 1284 | setting.SetGlobal(use_global); | ||
| 1285 | if (global || !use_global) { | ||
| 1286 | setting.SetValue(ReadSetting(name, default_value).value<Type>()); | ||
| 1287 | } | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | template <typename Type> | ||
| 1291 | void Config::ReadSettingGlobal(Type& setting, const QString& name, | ||
| 1292 | const QVariant& default_value) const { | ||
| 1293 | const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); | ||
| 1294 | if (global || !use_global) { | ||
| 1295 | setting = ReadSetting(name, default_value).value<Type>(); | ||
| 1296 | } | ||
| 1297 | } | ||
| 1298 | |||
| 1241 | void Config::WriteSetting(const QString& name, const QVariant& value) { | 1299 | void Config::WriteSetting(const QString& name, const QVariant& value) { |
| 1242 | qt_config->setValue(name, value); | 1300 | qt_config->setValue(name, value); |
| 1243 | } | 1301 | } |
| @@ -1248,6 +1306,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value, | |||
| 1248 | qt_config->setValue(name, value); | 1306 | qt_config->setValue(name, value); |
| 1249 | } | 1307 | } |
| 1250 | 1308 | ||
| 1309 | template <typename Type> | ||
| 1310 | void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) { | ||
| 1311 | if (!global) { | ||
| 1312 | qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); | ||
| 1313 | } | ||
| 1314 | if (global || !setting.UsingGlobal()) { | ||
| 1315 | qt_config->setValue(name, setting.GetValue(global)); | ||
| 1316 | } | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | template <typename Type> | ||
| 1320 | void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting, | ||
| 1321 | const QVariant& default_value) { | ||
| 1322 | if (!global) { | ||
| 1323 | qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); | ||
| 1324 | } | ||
| 1325 | if (global || !setting.UsingGlobal()) { | ||
| 1326 | qt_config->setValue(name + QStringLiteral("/default"), | ||
| 1327 | setting.GetValue(global) == default_value.value<Type>()); | ||
| 1328 | qt_config->setValue(name, setting.GetValue(global)); | ||
| 1329 | } | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, | ||
| 1333 | const QVariant& default_value) { | ||
| 1334 | if (!global) { | ||
| 1335 | qt_config->setValue(name + QStringLiteral("/use_global"), use_global); | ||
| 1336 | } | ||
| 1337 | if (global || !use_global) { | ||
| 1338 | qt_config->setValue(name + QStringLiteral("/default"), value == default_value); | ||
| 1339 | qt_config->setValue(name, value); | ||
| 1340 | } | ||
| 1341 | } | ||
| 1342 | |||
| 1251 | void Config::Reload() { | 1343 | void Config::Reload() { |
| 1252 | ReadValues(); | 1344 | ReadValues(); |
| 1253 | // To apply default value changes | 1345 | // To apply default value changes |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 5cd2a5feb..681f0bca5 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <QMetaType> | ||
| 10 | #include <QVariant> | 11 | #include <QVariant> |
| 11 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 12 | #include "yuzu/uisettings.h" | 13 | #include "yuzu/uisettings.h" |
| @@ -15,7 +16,7 @@ class QSettings; | |||
| 15 | 16 | ||
| 16 | class Config { | 17 | class Config { |
| 17 | public: | 18 | public: |
| 18 | Config(); | 19 | explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true); |
| 19 | ~Config(); | 20 | ~Config(); |
| 20 | 21 | ||
| 21 | void Reload(); | 22 | void Reload(); |
| @@ -27,7 +28,7 @@ public: | |||
| 27 | default_mouse_buttons; | 28 | default_mouse_buttons; |
| 28 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; | 29 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; |
| 29 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; | 30 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; |
| 30 | static const std::array<UISettings::Shortcut, 15> default_hotkeys; | 31 | static const std::array<UISettings::Shortcut, 16> default_hotkeys; |
| 31 | 32 | ||
| 32 | private: | 33 | private: |
| 33 | void ReadValues(); | 34 | void ReadValues(); |
| @@ -82,9 +83,33 @@ private: | |||
| 82 | 83 | ||
| 83 | QVariant ReadSetting(const QString& name) const; | 84 | QVariant ReadSetting(const QString& name) const; |
| 84 | QVariant ReadSetting(const QString& name, const QVariant& default_value) const; | 85 | QVariant ReadSetting(const QString& name, const QVariant& default_value) const; |
| 86 | // Templated ReadSettingGlobal functions will also look for the use_global setting and set | ||
| 87 | // both the value and the global state properly | ||
| 88 | template <typename Type> | ||
| 89 | void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name); | ||
| 90 | template <typename Type> | ||
| 91 | void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name, | ||
| 92 | const QVariant& default_value); | ||
| 93 | template <typename Type> | ||
| 94 | void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const; | ||
| 95 | // Templated WriteSettingGlobal functions will also write the global state if needed and will | ||
| 96 | // skip writing the actual setting if it defers to the global value | ||
| 85 | void WriteSetting(const QString& name, const QVariant& value); | 97 | void WriteSetting(const QString& name, const QVariant& value); |
| 86 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); | 98 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); |
| 99 | template <typename Type> | ||
| 100 | void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting); | ||
| 101 | template <typename Type> | ||
| 102 | void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting, | ||
| 103 | const QVariant& default_value); | ||
| 104 | void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, | ||
| 105 | const QVariant& default_value); | ||
| 87 | 106 | ||
| 88 | std::unique_ptr<QSettings> qt_config; | 107 | std::unique_ptr<QSettings> qt_config; |
| 89 | std::string qt_config_loc; | 108 | std::string qt_config_loc; |
| 109 | |||
| 110 | bool global; | ||
| 90 | }; | 111 | }; |
| 112 | |||
| 113 | // These metatype declarations cannot be in core/settings.h because core is devoid of QT | ||
| 114 | Q_DECLARE_METATYPE(Settings::RendererBackend); | ||
| 115 | Q_DECLARE_METATYPE(Settings::GPUAccuracy); | ||
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp new file mode 100644 index 000000000..bb47c3933 --- /dev/null +++ b/src/yuzu/configuration/configuration_shared.cpp | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QCheckBox> | ||
| 6 | #include <QComboBox> | ||
| 7 | #include "core/settings.h" | ||
| 8 | #include "yuzu/configuration/configuration_shared.h" | ||
| 9 | #include "yuzu/configuration/configure_per_game.h" | ||
| 10 | |||
| 11 | void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, | ||
| 12 | const QCheckBox* checkbox) { | ||
| 13 | if (checkbox->checkState() == Qt::PartiallyChecked) { | ||
| 14 | setting->SetGlobal(true); | ||
| 15 | } else { | ||
| 16 | setting->SetGlobal(false); | ||
| 17 | setting->SetValue(checkbox->checkState() == Qt::Checked); | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting, | ||
| 22 | const QComboBox* combobox) { | ||
| 23 | if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 24 | setting->SetGlobal(true); | ||
| 25 | } else { | ||
| 26 | setting->SetGlobal(false); | ||
| 27 | setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting, | ||
| 32 | const QComboBox* combobox) { | ||
| 33 | if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 34 | setting->SetGlobal(true); | ||
| 35 | } else { | ||
| 36 | setting->SetGlobal(false); | ||
| 37 | setting->SetValue(static_cast<Settings::RendererBackend>( | ||
| 38 | combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET)); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, | ||
| 43 | const Settings::Setting<bool>* setting) { | ||
| 44 | if (setting->UsingGlobal()) { | ||
| 45 | checkbox->setCheckState(Qt::PartiallyChecked); | ||
| 46 | } else { | ||
| 47 | checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void ConfigurationShared::SetPerGameSetting(QComboBox* combobox, | ||
| 52 | const Settings::Setting<int>* setting) { | ||
| 53 | combobox->setCurrentIndex(setting->UsingGlobal() | ||
| 54 | ? ConfigurationShared::USE_GLOBAL_INDEX | ||
| 55 | : setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET); | ||
| 56 | } | ||
| 57 | |||
| 58 | void ConfigurationShared::SetPerGameSetting( | ||
| 59 | QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) { | ||
| 60 | combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX | ||
| 61 | : static_cast<int>(setting->GetValue()) + | ||
| 62 | ConfigurationShared::USE_GLOBAL_OFFSET); | ||
| 63 | } | ||
| 64 | |||
| 65 | void ConfigurationShared::SetPerGameSetting( | ||
| 66 | QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) { | ||
| 67 | combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX | ||
| 68 | : static_cast<int>(setting->GetValue()) + | ||
| 69 | ConfigurationShared::USE_GLOBAL_OFFSET); | ||
| 70 | } | ||
| 71 | |||
| 72 | void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) { | ||
| 73 | const QString use_global_text = ConfigurePerGame::tr("Use global configuration"); | ||
| 74 | combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text); | ||
| 75 | combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX); | ||
| 76 | } | ||
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h new file mode 100644 index 000000000..b11b1b950 --- /dev/null +++ b/src/yuzu/configuration/configuration_shared.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QCheckBox> | ||
| 8 | #include <QComboBox> | ||
| 9 | #include <QString> | ||
| 10 | #include "core/settings.h" | ||
| 11 | |||
| 12 | namespace ConfigurationShared { | ||
| 13 | |||
| 14 | constexpr int USE_GLOBAL_INDEX = 0; | ||
| 15 | constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1; | ||
| 16 | constexpr int USE_GLOBAL_OFFSET = 2; | ||
| 17 | |||
| 18 | // Global-aware apply and set functions | ||
| 19 | |||
| 20 | void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox); | ||
| 21 | void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox); | ||
| 22 | void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting, | ||
| 23 | const QComboBox* combobox); | ||
| 24 | void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting, | ||
| 25 | const QComboBox* combobox); | ||
| 26 | |||
| 27 | void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); | ||
| 28 | void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting); | ||
| 29 | void SetPerGameSetting(QComboBox* combobox, | ||
| 30 | const Settings::Setting<Settings::RendererBackend>* setting); | ||
| 31 | void SetPerGameSetting(QComboBox* combobox, | ||
| 32 | const Settings::Setting<Settings::GPUAccuracy>* setting); | ||
| 33 | |||
| 34 | void InsertGlobalItem(QComboBox* combobox); | ||
| 35 | |||
| 36 | } // namespace ConfigurationShared | ||
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index f370c690f..cc021beec 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 13 | #include "ui_configure_audio.h" | 13 | #include "ui_configure_audio.h" |
| 14 | #include "yuzu/configuration/configuration_shared.h" | ||
| 14 | #include "yuzu/configuration/configure_audio.h" | 15 | #include "yuzu/configuration/configure_audio.h" |
| 15 | 16 | ||
| 16 | ConfigureAudio::ConfigureAudio(QWidget* parent) | 17 | ConfigureAudio::ConfigureAudio(QWidget* parent) |
| @@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) | |||
| 24 | connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, | 25 | connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, |
| 25 | &ConfigureAudio::UpdateAudioDevices); | 26 | &ConfigureAudio::UpdateAudioDevices); |
| 26 | 27 | ||
| 28 | ui->volume_label->setVisible(Settings::configuring_global); | ||
| 29 | ui->volume_combo_box->setVisible(!Settings::configuring_global); | ||
| 30 | |||
| 31 | SetupPerGameUI(); | ||
| 32 | |||
| 27 | SetConfiguration(); | 33 | SetConfiguration(); |
| 28 | 34 | ||
| 29 | const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); | 35 | const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); |
| @@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() { | |||
| 41 | 47 | ||
| 42 | SetAudioDeviceFromDeviceID(); | 48 | SetAudioDeviceFromDeviceID(); |
| 43 | 49 | ||
| 44 | ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); | 50 | ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum()); |
| 45 | ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); | 51 | |
| 52 | if (Settings::configuring_global) { | ||
| 53 | ui->toggle_audio_stretching->setChecked( | ||
| 54 | Settings::values.enable_audio_stretching.GetValue()); | ||
| 55 | } else { | ||
| 56 | ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching, | ||
| 57 | &Settings::values.enable_audio_stretching); | ||
| 58 | if (Settings::values.volume.UsingGlobal()) { | ||
| 59 | ui->volume_combo_box->setCurrentIndex(0); | ||
| 60 | ui->volume_slider->setEnabled(false); | ||
| 61 | } else { | ||
| 62 | ui->volume_combo_box->setCurrentIndex(1); | ||
| 63 | ui->volume_slider->setEnabled(true); | ||
| 64 | } | ||
| 65 | } | ||
| 46 | SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); | 66 | SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); |
| 47 | } | 67 | } |
| 48 | 68 | ||
| @@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) { | |||
| 80 | } | 100 | } |
| 81 | 101 | ||
| 82 | void ConfigureAudio::ApplyConfiguration() { | 102 | void ConfigureAudio::ApplyConfiguration() { |
| 83 | Settings::values.sink_id = | 103 | if (Settings::configuring_global) { |
| 84 | ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) | 104 | Settings::values.sink_id = |
| 85 | .toStdString(); | 105 | ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) |
| 86 | Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); | 106 | .toStdString(); |
| 87 | Settings::values.audio_device_id = | 107 | Settings::values.audio_device_id = |
| 88 | ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) | 108 | ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) |
| 89 | .toStdString(); | 109 | .toStdString(); |
| 90 | Settings::values.volume = | 110 | |
| 91 | static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); | 111 | // Guard if during game and set to game-specific value |
| 112 | if (Settings::values.enable_audio_stretching.UsingGlobal()) { | ||
| 113 | Settings::values.enable_audio_stretching.SetValue( | ||
| 114 | ui->toggle_audio_stretching->isChecked()); | ||
| 115 | } | ||
| 116 | if (Settings::values.volume.UsingGlobal()) { | ||
| 117 | Settings::values.volume.SetValue( | ||
| 118 | static_cast<float>(ui->volume_slider->sliderPosition()) / | ||
| 119 | ui->volume_slider->maximum()); | ||
| 120 | } | ||
| 121 | } else { | ||
| 122 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching, | ||
| 123 | ui->toggle_audio_stretching); | ||
| 124 | if (ui->volume_combo_box->currentIndex() == 0) { | ||
| 125 | Settings::values.volume.SetGlobal(true); | ||
| 126 | } else { | ||
| 127 | Settings::values.volume.SetGlobal(false); | ||
| 128 | Settings::values.volume.SetValue( | ||
| 129 | static_cast<float>(ui->volume_slider->sliderPosition()) / | ||
| 130 | ui->volume_slider->maximum()); | ||
| 131 | } | ||
| 132 | } | ||
| 92 | } | 133 | } |
| 93 | 134 | ||
| 94 | void ConfigureAudio::changeEvent(QEvent* event) { | 135 | void ConfigureAudio::changeEvent(QEvent* event) { |
| @@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() { | |||
| 122 | ui->retranslateUi(this); | 163 | ui->retranslateUi(this); |
| 123 | SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); | 164 | SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); |
| 124 | } | 165 | } |
| 166 | |||
| 167 | void ConfigureAudio::SetupPerGameUI() { | ||
| 168 | if (Settings::configuring_global) { | ||
| 169 | ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal()); | ||
| 170 | ui->toggle_audio_stretching->setEnabled( | ||
| 171 | Settings::values.enable_audio_stretching.UsingGlobal()); | ||
| 172 | |||
| 173 | return; | ||
| 174 | } | ||
| 175 | |||
| 176 | ui->toggle_audio_stretching->setTristate(true); | ||
| 177 | connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), | ||
| 178 | this, [this](int index) { ui->volume_slider->setEnabled(index == 1); }); | ||
| 179 | |||
| 180 | ui->output_sink_combo_box->setVisible(false); | ||
| 181 | ui->output_sink_label->setVisible(false); | ||
| 182 | ui->audio_device_combo_box->setVisible(false); | ||
| 183 | ui->audio_device_label->setVisible(false); | ||
| 184 | } | ||
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h index ea83bd72d..d84f4a682 100644 --- a/src/yuzu/configuration/configure_audio.h +++ b/src/yuzu/configuration/configure_audio.h | |||
| @@ -34,5 +34,7 @@ private: | |||
| 34 | void SetAudioDeviceFromDeviceID(); | 34 | void SetAudioDeviceFromDeviceID(); |
| 35 | void SetVolumeIndicatorText(int percentage); | 35 | void SetVolumeIndicatorText(int percentage); |
| 36 | 36 | ||
| 37 | void SetupPerGameUI(); | ||
| 38 | |||
| 37 | std::unique_ptr<Ui::ConfigureAudio> ui; | 39 | std::unique_ptr<Ui::ConfigureAudio> ui; |
| 38 | }; | 40 | }; |
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui index a098b9acc..862ccb988 100644 --- a/src/yuzu/configuration/configure_audio.ui +++ b/src/yuzu/configuration/configure_audio.ui | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>188</width> | 9 | <width>367</width> |
| 10 | <height>246</height> | 10 | <height>368</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <layout class="QVBoxLayout"> | 13 | <layout class="QVBoxLayout"> |
| @@ -18,9 +18,9 @@ | |||
| 18 | </property> | 18 | </property> |
| 19 | <layout class="QVBoxLayout"> | 19 | <layout class="QVBoxLayout"> |
| 20 | <item> | 20 | <item> |
| 21 | <layout class="QHBoxLayout"> | 21 | <layout class="QHBoxLayout" name="_3"> |
| 22 | <item> | 22 | <item> |
| 23 | <widget class="QLabel" name="label_1"> | 23 | <widget class="QLabel" name="output_sink_label"> |
| 24 | <property name="text"> | 24 | <property name="text"> |
| 25 | <string>Output Engine:</string> | 25 | <string>Output Engine:</string> |
| 26 | </property> | 26 | </property> |
| @@ -31,20 +31,20 @@ | |||
| 31 | </item> | 31 | </item> |
| 32 | </layout> | 32 | </layout> |
| 33 | </item> | 33 | </item> |
| 34 | <item> | ||
| 35 | <widget class="QCheckBox" name="toggle_audio_stretching"> | ||
| 36 | <property name="toolTip"> | ||
| 37 | <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string> | ||
| 38 | </property> | ||
| 39 | <property name="text"> | ||
| 40 | <string>Enable audio stretching</string> | ||
| 41 | </property> | ||
| 42 | </widget> | ||
| 43 | </item> | ||
| 44 | <item> | 34 | <item> |
| 45 | <layout class="QHBoxLayout"> | 35 | <widget class="QCheckBox" name="toggle_audio_stretching"> |
| 36 | <property name="toolTip"> | ||
| 37 | <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string> | ||
| 38 | </property> | ||
| 39 | <property name="text"> | ||
| 40 | <string>Enable audio stretching</string> | ||
| 41 | </property> | ||
| 42 | </widget> | ||
| 43 | </item> | ||
| 44 | <item> | ||
| 45 | <layout class="QHBoxLayout" name="_2"> | ||
| 46 | <item> | 46 | <item> |
| 47 | <widget class="QLabel" name="label_2"> | 47 | <widget class="QLabel" name="audio_device_label"> |
| 48 | <property name="text"> | 48 | <property name="text"> |
| 49 | <string>Audio Device:</string> | 49 | <string>Audio Device:</string> |
| 50 | </property> | 50 | </property> |
| @@ -61,7 +61,21 @@ | |||
| 61 | <number>0</number> | 61 | <number>0</number> |
| 62 | </property> | 62 | </property> |
| 63 | <item> | 63 | <item> |
| 64 | <widget class="QLabel" name="label_3"> | 64 | <widget class="QComboBox" name="volume_combo_box"> |
| 65 | <item> | ||
| 66 | <property name="text"> | ||
| 67 | <string>Use global volume</string> | ||
| 68 | </property> | ||
| 69 | </item> | ||
| 70 | <item> | ||
| 71 | <property name="text"> | ||
| 72 | <string>Set volume:</string> | ||
| 73 | </property> | ||
| 74 | </item> | ||
| 75 | </widget> | ||
| 76 | </item> | ||
| 77 | <item> | ||
| 78 | <widget class="QLabel" name="volume_label"> | ||
| 65 | <property name="text"> | 79 | <property name="text"> |
| 66 | <string>Volume:</string> | 80 | <string>Volume:</string> |
| 67 | </property> | 81 | </property> |
| @@ -74,7 +88,7 @@ | |||
| 74 | </property> | 88 | </property> |
| 75 | <property name="sizeHint" stdset="0"> | 89 | <property name="sizeHint" stdset="0"> |
| 76 | <size> | 90 | <size> |
| 77 | <width>40</width> | 91 | <width>30</width> |
| 78 | <height>20</height> | 92 | <height>20</height> |
| 79 | </size> | 93 | </size> |
| 80 | </property> | 94 | </property> |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index c2026763e..2c77441fd 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -39,6 +39,8 @@ void ConfigureDebug::SetConfiguration() { | |||
| 39 | ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt); | 39 | ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt); |
| 40 | ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 40 | ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 41 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); | 41 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); |
| 42 | ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||
| 43 | ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); | ||
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | void ConfigureDebug::ApplyConfiguration() { | 46 | void ConfigureDebug::ApplyConfiguration() { |
| @@ -51,6 +53,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||
| 51 | Settings::values.quest_flag = ui->quest_flag->isChecked(); | 53 | Settings::values.quest_flag = ui->quest_flag->isChecked(); |
| 52 | Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked(); | 54 | Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked(); |
| 53 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); | 55 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); |
| 56 | Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); | ||
| 54 | Debugger::ToggleConsole(); | 57 | Debugger::ToggleConsole(); |
| 55 | Log::Filter filter; | 58 | Log::Filter filter; |
| 56 | filter.ParseFilterString(Settings::values.log_filter); | 59 | filter.ParseFilterString(Settings::values.log_filter); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index e0d4c4a44..46f0208c6 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -148,6 +148,19 @@ | |||
| 148 | </property> | 148 | </property> |
| 149 | </widget> | 149 | </widget> |
| 150 | </item> | 150 | </item> |
| 151 | <item> | ||
| 152 | <widget class="QCheckBox" name="disable_macro_jit"> | ||
| 153 | <property name="enabled"> | ||
| 154 | <bool>true</bool> | ||
| 155 | </property> | ||
| 156 | <property name="whatsThis"> | ||
| 157 | <string>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</string> | ||
| 158 | </property> | ||
| 159 | <property name="text"> | ||
| 160 | <string>Disable Macro JIT</string> | ||
| 161 | </property> | ||
| 162 | </widget> | ||
| 163 | </item> | ||
| 151 | </layout> | 164 | </layout> |
| 152 | </widget> | 165 | </widget> |
| 153 | </item> | 166 | </item> |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index df4473b46..5918e9972 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | 14 | ||
| 15 | ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) | 15 | ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) |
| 16 | : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { | 16 | : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { |
| 17 | Settings::configuring_global = true; | ||
| 18 | |||
| 17 | ui->setupUi(this); | 19 | ui->setupUi(this); |
| 18 | ui->hotkeysTab->Populate(registry); | 20 | ui->hotkeysTab->Populate(registry); |
| 19 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | 21 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index cb95423e0..1fb62d1cf 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -7,40 +7,79 @@ | |||
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/settings.h" | 8 | #include "core/settings.h" |
| 9 | #include "ui_configure_general.h" | 9 | #include "ui_configure_general.h" |
| 10 | #include "yuzu/configuration/configuration_shared.h" | ||
| 10 | #include "yuzu/configuration/configure_general.h" | 11 | #include "yuzu/configuration/configure_general.h" |
| 11 | #include "yuzu/uisettings.h" | 12 | #include "yuzu/uisettings.h" |
| 12 | 13 | ||
| 13 | ConfigureGeneral::ConfigureGeneral(QWidget* parent) | 14 | ConfigureGeneral::ConfigureGeneral(QWidget* parent) |
| 14 | : QWidget(parent), ui(new Ui::ConfigureGeneral) { | 15 | : QWidget(parent), ui(new Ui::ConfigureGeneral) { |
| 15 | |||
| 16 | ui->setupUi(this); | 16 | ui->setupUi(this); |
| 17 | 17 | ||
| 18 | SetupPerGameUI(); | ||
| 19 | |||
| 18 | SetConfiguration(); | 20 | SetConfiguration(); |
| 19 | 21 | ||
| 20 | connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); | 22 | connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() { |
| 23 | ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked); | ||
| 24 | }); | ||
| 21 | } | 25 | } |
| 22 | 26 | ||
| 23 | ConfigureGeneral::~ConfigureGeneral() = default; | 27 | ConfigureGeneral::~ConfigureGeneral() = default; |
| 24 | 28 | ||
| 25 | void ConfigureGeneral::SetConfiguration() { | 29 | void ConfigureGeneral::SetConfiguration() { |
| 30 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | ||
| 31 | |||
| 32 | ui->use_multi_core->setEnabled(runtime_lock); | ||
| 33 | ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); | ||
| 34 | |||
| 26 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 35 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 27 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 36 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 28 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); | 37 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); |
| 29 | ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); | 38 | ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); |
| 30 | 39 | ||
| 31 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | 40 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); |
| 32 | ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); | 41 | ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); |
| 33 | ui->frame_limit->setValue(Settings::values.frame_limit); | 42 | |
| 43 | if (!Settings::configuring_global) { | ||
| 44 | if (Settings::values.use_multi_core.UsingGlobal()) { | ||
| 45 | ui->use_multi_core->setCheckState(Qt::PartiallyChecked); | ||
| 46 | } | ||
| 47 | if (Settings::values.use_frame_limit.UsingGlobal()) { | ||
| 48 | ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked && | ||
| 53 | ui->toggle_frame_limit->isEnabled()); | ||
| 34 | } | 54 | } |
| 35 | 55 | ||
| 36 | void ConfigureGeneral::ApplyConfiguration() { | 56 | void ConfigureGeneral::ApplyConfiguration() { |
| 37 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | 57 | if (Settings::configuring_global) { |
| 38 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 58 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); |
| 39 | UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); | 59 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 40 | UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); | 60 | UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); |
| 61 | UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); | ||
| 62 | |||
| 63 | // Guard if during game and set to game-specific value | ||
| 64 | if (Settings::values.use_frame_limit.UsingGlobal()) { | ||
| 65 | Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == | ||
| 66 | Qt::Checked); | ||
| 67 | Settings::values.frame_limit.SetValue(ui->frame_limit->value()); | ||
| 68 | Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked()); | ||
| 69 | } | ||
| 70 | } else { | ||
| 71 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, | ||
| 72 | ui->use_multi_core); | ||
| 41 | 73 | ||
| 42 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | 74 | bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked; |
| 43 | Settings::values.frame_limit = ui->frame_limit->value(); | 75 | Settings::values.use_frame_limit.SetGlobal(global_frame_limit); |
| 76 | Settings::values.frame_limit.SetGlobal(global_frame_limit); | ||
| 77 | if (!global_frame_limit) { | ||
| 78 | Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == | ||
| 79 | Qt::Checked); | ||
| 80 | Settings::values.frame_limit.SetValue(ui->frame_limit->value()); | ||
| 81 | } | ||
| 82 | } | ||
| 44 | } | 83 | } |
| 45 | 84 | ||
| 46 | void ConfigureGeneral::changeEvent(QEvent* event) { | 85 | void ConfigureGeneral::changeEvent(QEvent* event) { |
| @@ -54,3 +93,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) { | |||
| 54 | void ConfigureGeneral::RetranslateUI() { | 93 | void ConfigureGeneral::RetranslateUI() { |
| 55 | ui->retranslateUi(this); | 94 | ui->retranslateUi(this); |
| 56 | } | 95 | } |
| 96 | |||
| 97 | void ConfigureGeneral::SetupPerGameUI() { | ||
| 98 | if (Settings::configuring_global) { | ||
| 99 | ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal()); | ||
| 100 | ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); | ||
| 101 | |||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | ui->toggle_check_exit->setVisible(false); | ||
| 106 | ui->toggle_user_on_boot->setVisible(false); | ||
| 107 | ui->toggle_background_pause->setVisible(false); | ||
| 108 | ui->toggle_hide_mouse->setVisible(false); | ||
| 109 | |||
| 110 | ui->toggle_frame_limit->setTristate(true); | ||
| 111 | ui->use_multi_core->setTristate(true); | ||
| 112 | } | ||
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index ef05ce065..9c785c22e 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h | |||
| @@ -28,5 +28,7 @@ private: | |||
| 28 | 28 | ||
| 29 | void SetConfiguration(); | 29 | void SetConfiguration(); |
| 30 | 30 | ||
| 31 | void SetupPerGameUI(); | ||
| 32 | |||
| 31 | std::unique_ptr<Ui::ConfigureGeneral> ui; | 33 | std::unique_ptr<Ui::ConfigureGeneral> ui; |
| 32 | }; | 34 | }; |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index fc3b7e65a..2711116a2 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -52,6 +52,13 @@ | |||
| 52 | </layout> | 52 | </layout> |
| 53 | </item> | 53 | </item> |
| 54 | <item> | 54 | <item> |
| 55 | <widget class="QCheckBox" name="use_multi_core"> | ||
| 56 | <property name="text"> | ||
| 57 | <string>Multicore CPU Emulation</string> | ||
| 58 | </property> | ||
| 59 | </widget> | ||
| 60 | </item> | ||
| 61 | <item> | ||
| 55 | <widget class="QCheckBox" name="toggle_check_exit"> | 62 | <widget class="QCheckBox" name="toggle_check_exit"> |
| 56 | <property name="text"> | 63 | <property name="text"> |
| 57 | <string>Confirm exit while emulation is running</string> | 64 | <string>Confirm exit while emulation is running</string> |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index ea667caef..cb4706bd6 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -13,65 +13,27 @@ | |||
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
| 14 | #include "core/settings.h" | 14 | #include "core/settings.h" |
| 15 | #include "ui_configure_graphics.h" | 15 | #include "ui_configure_graphics.h" |
| 16 | #include "yuzu/configuration/configuration_shared.h" | ||
| 16 | #include "yuzu/configuration/configure_graphics.h" | 17 | #include "yuzu/configuration/configure_graphics.h" |
| 17 | 18 | ||
| 18 | #ifdef HAS_VULKAN | 19 | #ifdef HAS_VULKAN |
| 19 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 20 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| 20 | #endif | 21 | #endif |
| 21 | 22 | ||
| 22 | namespace { | ||
| 23 | enum class Resolution : int { | ||
| 24 | Auto, | ||
| 25 | Scale1x, | ||
| 26 | Scale2x, | ||
| 27 | Scale3x, | ||
| 28 | Scale4x, | ||
| 29 | }; | ||
| 30 | |||
| 31 | float ToResolutionFactor(Resolution option) { | ||
| 32 | switch (option) { | ||
| 33 | case Resolution::Auto: | ||
| 34 | return 0.f; | ||
| 35 | case Resolution::Scale1x: | ||
| 36 | return 1.f; | ||
| 37 | case Resolution::Scale2x: | ||
| 38 | return 2.f; | ||
| 39 | case Resolution::Scale3x: | ||
| 40 | return 3.f; | ||
| 41 | case Resolution::Scale4x: | ||
| 42 | return 4.f; | ||
| 43 | } | ||
| 44 | return 0.f; | ||
| 45 | } | ||
| 46 | |||
| 47 | Resolution FromResolutionFactor(float factor) { | ||
| 48 | if (factor == 0.f) { | ||
| 49 | return Resolution::Auto; | ||
| 50 | } else if (factor == 1.f) { | ||
| 51 | return Resolution::Scale1x; | ||
| 52 | } else if (factor == 2.f) { | ||
| 53 | return Resolution::Scale2x; | ||
| 54 | } else if (factor == 3.f) { | ||
| 55 | return Resolution::Scale3x; | ||
| 56 | } else if (factor == 4.f) { | ||
| 57 | return Resolution::Scale4x; | ||
| 58 | } | ||
| 59 | return Resolution::Auto; | ||
| 60 | } | ||
| 61 | } // Anonymous namespace | ||
| 62 | |||
| 63 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) | 23 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) |
| 64 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { | 24 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { |
| 65 | vulkan_device = Settings::values.vulkan_device; | 25 | vulkan_device = Settings::values.vulkan_device.GetValue(); |
| 66 | RetrieveVulkanDevices(); | 26 | RetrieveVulkanDevices(); |
| 67 | 27 | ||
| 68 | ui->setupUi(this); | 28 | ui->setupUi(this); |
| 69 | 29 | ||
| 30 | SetupPerGameUI(); | ||
| 31 | |||
| 70 | SetConfiguration(); | 32 | SetConfiguration(); |
| 71 | 33 | ||
| 72 | connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | 34 | connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, |
| 73 | [this] { UpdateDeviceComboBox(); }); | 35 | [this] { UpdateDeviceComboBox(); }); |
| 74 | connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, | 36 | connect(ui->device, qOverload<int>(&QComboBox::activated), this, |
| 75 | [this](int device) { UpdateDeviceSelection(device); }); | 37 | [this](int device) { UpdateDeviceSelection(device); }); |
| 76 | 38 | ||
| 77 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { | 39 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { |
| @@ -81,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
| 81 | } | 43 | } |
| 82 | UpdateBackgroundColorButton(new_bg_color); | 44 | UpdateBackgroundColorButton(new_bg_color); |
| 83 | }); | 45 | }); |
| 46 | |||
| 47 | ui->bg_label->setVisible(Settings::configuring_global); | ||
| 48 | ui->bg_combobox->setVisible(!Settings::configuring_global); | ||
| 84 | } | 49 | } |
| 85 | 50 | ||
| 86 | void ConfigureGraphics::UpdateDeviceSelection(int device) { | 51 | void ConfigureGraphics::UpdateDeviceSelection(int device) { |
| @@ -98,31 +63,95 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 98 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | 63 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); |
| 99 | 64 | ||
| 100 | ui->api->setEnabled(runtime_lock); | 65 | ui->api->setEnabled(runtime_lock); |
| 101 | ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend)); | ||
| 102 | ui->resolution_factor_combobox->setCurrentIndex( | ||
| 103 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | ||
| 104 | ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio); | ||
| 105 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | ||
| 106 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); | ||
| 107 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); | 66 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); |
| 108 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 67 | ui->use_disk_shader_cache->setEnabled(runtime_lock); |
| 109 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 68 | |
| 110 | Settings::values.bg_blue)); | 69 | if (Settings::configuring_global) { |
| 70 | ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); | ||
| 71 | ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); | ||
| 72 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); | ||
| 73 | ui->use_asynchronous_gpu_emulation->setChecked( | ||
| 74 | Settings::values.use_asynchronous_gpu_emulation.GetValue()); | ||
| 75 | } else { | ||
| 76 | ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache, | ||
| 77 | &Settings::values.use_disk_shader_cache); | ||
| 78 | ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation, | ||
| 79 | &Settings::values.use_asynchronous_gpu_emulation); | ||
| 80 | |||
| 81 | ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); | ||
| 82 | ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, | ||
| 83 | &Settings::values.aspect_ratio); | ||
| 84 | |||
| 85 | ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); | ||
| 86 | ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); | ||
| 87 | } | ||
| 88 | |||
| 89 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(), | ||
| 90 | Settings::values.bg_green.GetValue(), | ||
| 91 | Settings::values.bg_blue.GetValue())); | ||
| 111 | UpdateDeviceComboBox(); | 92 | UpdateDeviceComboBox(); |
| 112 | } | 93 | } |
| 113 | 94 | ||
| 114 | void ConfigureGraphics::ApplyConfiguration() { | 95 | void ConfigureGraphics::ApplyConfiguration() { |
| 115 | Settings::values.renderer_backend = GetCurrentGraphicsBackend(); | 96 | if (Settings::configuring_global) { |
| 116 | Settings::values.vulkan_device = vulkan_device; | 97 | // Guard if during game and set to game-specific value |
| 117 | Settings::values.resolution_factor = | 98 | if (Settings::values.renderer_backend.UsingGlobal()) { |
| 118 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | 99 | Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); |
| 119 | Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex(); | 100 | } |
| 120 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | 101 | if (Settings::values.vulkan_device.UsingGlobal()) { |
| 121 | Settings::values.use_asynchronous_gpu_emulation = | 102 | Settings::values.vulkan_device.SetValue(vulkan_device); |
| 122 | ui->use_asynchronous_gpu_emulation->isChecked(); | 103 | } |
| 123 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | 104 | if (Settings::values.aspect_ratio.UsingGlobal()) { |
| 124 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | 105 | Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex()); |
| 125 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); | 106 | } |
| 107 | if (Settings::values.use_disk_shader_cache.UsingGlobal()) { | ||
| 108 | Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked()); | ||
| 109 | } | ||
| 110 | if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) { | ||
| 111 | Settings::values.use_asynchronous_gpu_emulation.SetValue( | ||
| 112 | ui->use_asynchronous_gpu_emulation->isChecked()); | ||
| 113 | } | ||
| 114 | if (Settings::values.bg_red.UsingGlobal()) { | ||
| 115 | Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF())); | ||
| 116 | Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF())); | ||
| 117 | Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF())); | ||
| 118 | } | ||
| 119 | } else { | ||
| 120 | if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 121 | Settings::values.renderer_backend.SetGlobal(true); | ||
| 122 | Settings::values.vulkan_device.SetGlobal(true); | ||
| 123 | } else { | ||
| 124 | Settings::values.renderer_backend.SetGlobal(false); | ||
| 125 | Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); | ||
| 126 | if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) { | ||
| 127 | Settings::values.vulkan_device.SetGlobal(false); | ||
| 128 | Settings::values.vulkan_device.SetValue(vulkan_device); | ||
| 129 | } else { | ||
| 130 | Settings::values.vulkan_device.SetGlobal(true); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, | ||
| 135 | ui->aspect_ratio_combobox); | ||
| 136 | |||
| 137 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache, | ||
| 138 | ui->use_disk_shader_cache); | ||
| 139 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, | ||
| 140 | ui->use_asynchronous_gpu_emulation); | ||
| 141 | |||
| 142 | if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 143 | Settings::values.bg_red.SetGlobal(true); | ||
| 144 | Settings::values.bg_green.SetGlobal(true); | ||
| 145 | Settings::values.bg_blue.SetGlobal(true); | ||
| 146 | } else { | ||
| 147 | Settings::values.bg_red.SetGlobal(false); | ||
| 148 | Settings::values.bg_green.SetGlobal(false); | ||
| 149 | Settings::values.bg_blue.SetGlobal(false); | ||
| 150 | Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF())); | ||
| 151 | Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF())); | ||
| 152 | Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF())); | ||
| 153 | } | ||
| 154 | } | ||
| 126 | } | 155 | } |
| 127 | 156 | ||
| 128 | void ConfigureGraphics::changeEvent(QEvent* event) { | 157 | void ConfigureGraphics::changeEvent(QEvent* event) { |
| @@ -151,19 +180,27 @@ void ConfigureGraphics::UpdateDeviceComboBox() { | |||
| 151 | ui->device->clear(); | 180 | ui->device->clear(); |
| 152 | 181 | ||
| 153 | bool enabled = false; | 182 | bool enabled = false; |
| 183 | |||
| 184 | if (!Settings::configuring_global && | ||
| 185 | ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 186 | vulkan_device = Settings::values.vulkan_device.GetValue(); | ||
| 187 | } | ||
| 154 | switch (GetCurrentGraphicsBackend()) { | 188 | switch (GetCurrentGraphicsBackend()) { |
| 155 | case Settings::RendererBackend::OpenGL: | 189 | case Settings::RendererBackend::OpenGL: |
| 156 | ui->device->addItem(tr("OpenGL Graphics Device")); | 190 | ui->device->addItem(tr("OpenGL Graphics Device")); |
| 157 | enabled = false; | 191 | enabled = false; |
| 158 | break; | 192 | break; |
| 159 | case Settings::RendererBackend::Vulkan: | 193 | case Settings::RendererBackend::Vulkan: |
| 160 | for (const auto device : vulkan_devices) { | 194 | for (const auto& device : vulkan_devices) { |
| 161 | ui->device->addItem(device); | 195 | ui->device->addItem(device); |
| 162 | } | 196 | } |
| 163 | ui->device->setCurrentIndex(vulkan_device); | 197 | ui->device->setCurrentIndex(vulkan_device); |
| 164 | enabled = !vulkan_devices.empty(); | 198 | enabled = !vulkan_devices.empty(); |
| 165 | break; | 199 | break; |
| 166 | } | 200 | } |
| 201 | // If in per-game config and use global is selected, don't enable. | ||
| 202 | enabled &= !(!Settings::configuring_global && | ||
| 203 | ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX); | ||
| 167 | ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); | 204 | ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); |
| 168 | } | 205 | } |
| 169 | 206 | ||
| @@ -177,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() { | |||
| 177 | } | 214 | } |
| 178 | 215 | ||
| 179 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | 216 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { |
| 180 | return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); | 217 | if (Settings::configuring_global) { |
| 218 | return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); | ||
| 219 | } | ||
| 220 | |||
| 221 | if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 222 | Settings::values.renderer_backend.SetGlobal(true); | ||
| 223 | return Settings::values.renderer_backend.GetValue(); | ||
| 224 | } | ||
| 225 | Settings::values.renderer_backend.SetGlobal(false); | ||
| 226 | return static_cast<Settings::RendererBackend>(ui->api->currentIndex() - | ||
| 227 | ConfigurationShared::USE_GLOBAL_OFFSET); | ||
| 228 | } | ||
| 229 | |||
| 230 | void ConfigureGraphics::SetupPerGameUI() { | ||
| 231 | if (Settings::configuring_global) { | ||
| 232 | ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); | ||
| 233 | ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal()); | ||
| 234 | ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); | ||
| 235 | ui->use_asynchronous_gpu_emulation->setEnabled( | ||
| 236 | Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); | ||
| 237 | ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); | ||
| 238 | ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); | ||
| 239 | |||
| 240 | return; | ||
| 241 | } | ||
| 242 | |||
| 243 | connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, | ||
| 244 | [this](int index) { ui->bg_button->setEnabled(index == 1); }); | ||
| 245 | |||
| 246 | ui->use_disk_shader_cache->setTristate(true); | ||
| 247 | ui->use_asynchronous_gpu_emulation->setTristate(true); | ||
| 248 | ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox); | ||
| 249 | ConfigurationShared::InsertGlobalItem(ui->api); | ||
| 181 | } | 250 | } |
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 7e0596d9c..24f01c739 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h | |||
| @@ -35,6 +35,8 @@ private: | |||
| 35 | 35 | ||
| 36 | void RetrieveVulkanDevices(); | 36 | void RetrieveVulkanDevices(); |
| 37 | 37 | ||
| 38 | void SetupPerGameUI(); | ||
| 39 | |||
| 38 | Settings::RendererBackend GetCurrentGraphicsBackend() const; | 40 | Settings::RendererBackend GetCurrentGraphicsBackend() const; |
| 39 | 41 | ||
| 40 | std::unique_ptr<Ui::ConfigureGraphics> ui; | 42 | std::unique_ptr<Ui::ConfigureGraphics> ui; |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index c816d6108..62418fc14 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -85,39 +85,34 @@ | |||
| 85 | </widget> | 85 | </widget> |
| 86 | </item> | 86 | </item> |
| 87 | <item> | 87 | <item> |
| 88 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | 88 | <layout class="QHBoxLayout" name="horizontalLayout_6"> |
| 89 | <item> | 89 | <item> |
| 90 | <widget class="QLabel" name="label"> | 90 | <widget class="QLabel" name="ar_label"> |
| 91 | <property name="text"> | 91 | <property name="text"> |
| 92 | <string>Internal Resolution:</string> | 92 | <string>Aspect Ratio:</string> |
| 93 | </property> | 93 | </property> |
| 94 | </widget> | 94 | </widget> |
| 95 | </item> | 95 | </item> |
| 96 | <item> | 96 | <item> |
| 97 | <widget class="QComboBox" name="resolution_factor_combobox"> | 97 | <widget class="QComboBox" name="aspect_ratio_combobox"> |
| 98 | <item> | ||
| 99 | <property name="text"> | ||
| 100 | <string>Auto (Window Size)</string> | ||
| 101 | </property> | ||
| 102 | </item> | ||
| 103 | <item> | 98 | <item> |
| 104 | <property name="text"> | 99 | <property name="text"> |
| 105 | <string>Native (1280x720)</string> | 100 | <string>Default (16:9)</string> |
| 106 | </property> | 101 | </property> |
| 107 | </item> | 102 | </item> |
| 108 | <item> | 103 | <item> |
| 109 | <property name="text"> | 104 | <property name="text"> |
| 110 | <string>2x Native (2560x1440)</string> | 105 | <string>Force 4:3</string> |
| 111 | </property> | 106 | </property> |
| 112 | </item> | 107 | </item> |
| 113 | <item> | 108 | <item> |
| 114 | <property name="text"> | 109 | <property name="text"> |
| 115 | <string>3x Native (3840x2160)</string> | 110 | <string>Force 21:9</string> |
| 116 | </property> | 111 | </property> |
| 117 | </item> | 112 | </item> |
| 118 | <item> | 113 | <item> |
| 119 | <property name="text"> | 114 | <property name="text"> |
| 120 | <string>4x Native (5120x2880)</string> | 115 | <string>Stretch to Window</string> |
| 121 | </property> | 116 | </property> |
| 122 | </item> | 117 | </item> |
| 123 | </widget> | 118 | </widget> |
| @@ -125,42 +120,30 @@ | |||
| 125 | </layout> | 120 | </layout> |
| 126 | </item> | 121 | </item> |
| 127 | <item> | 122 | <item> |
| 128 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | 123 | <layout class="QHBoxLayout" name="horizontalLayout_3"> |
| 129 | <item> | 124 | <item> |
| 130 | <widget class="QLabel" name="ar_label"> | 125 | <widget class="QComboBox" name="bg_combobox"> |
| 131 | <property name="text"> | 126 | <property name="currentText"> |
| 132 | <string>Aspect Ratio:</string> | 127 | <string>Use global background color</string> |
| 128 | </property> | ||
| 129 | <property name="currentIndex"> | ||
| 130 | <number>0</number> | ||
| 131 | </property> | ||
| 132 | <property name="maxVisibleItems"> | ||
| 133 | <number>10</number> | ||
| 133 | </property> | 134 | </property> |
| 134 | </widget> | ||
| 135 | </item> | ||
| 136 | <item> | ||
| 137 | <widget class="QComboBox" name="aspect_ratio_combobox"> | ||
| 138 | <item> | ||
| 139 | <property name="text"> | ||
| 140 | <string>Default (16:9)</string> | ||
| 141 | </property> | ||
| 142 | </item> | ||
| 143 | <item> | ||
| 144 | <property name="text"> | ||
| 145 | <string>Force 4:3</string> | ||
| 146 | </property> | ||
| 147 | </item> | ||
| 148 | <item> | 135 | <item> |
| 149 | <property name="text"> | 136 | <property name="text"> |
| 150 | <string>Force 21:9</string> | 137 | <string>Use global background color</string> |
| 151 | </property> | 138 | </property> |
| 152 | </item> | 139 | </item> |
| 153 | <item> | 140 | <item> |
| 154 | <property name="text"> | 141 | <property name="text"> |
| 155 | <string>Stretch to Window</string> | 142 | <string>Set background color:</string> |
| 156 | </property> | 143 | </property> |
| 157 | </item> | 144 | </item> |
| 158 | </widget> | 145 | </widget> |
| 159 | </item> | 146 | </item> |
| 160 | </layout> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 164 | <item> | 147 | <item> |
| 165 | <widget class="QLabel" name="bg_label"> | 148 | <widget class="QLabel" name="bg_label"> |
| 166 | <property name="text"> | 149 | <property name="text"> |
| @@ -169,6 +152,19 @@ | |||
| 169 | </widget> | 152 | </widget> |
| 170 | </item> | 153 | </item> |
| 171 | <item> | 154 | <item> |
| 155 | <spacer name="horizontalSpacer"> | ||
| 156 | <property name="orientation"> | ||
| 157 | <enum>Qt::Horizontal</enum> | ||
| 158 | </property> | ||
| 159 | <property name="sizeHint" stdset="0"> | ||
| 160 | <size> | ||
| 161 | <width>40</width> | ||
| 162 | <height>20</height> | ||
| 163 | </size> | ||
| 164 | </property> | ||
| 165 | </spacer> | ||
| 166 | </item> | ||
| 167 | <item> | ||
| 172 | <widget class="QPushButton" name="bg_button"> | 168 | <widget class="QPushButton" name="bg_button"> |
| 173 | <property name="maximumSize"> | 169 | <property name="maximumSize"> |
| 174 | <size> | 170 | <size> |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 37aadf7f8..7c0fa7ec5 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/settings.h" | 6 | #include "core/settings.h" |
| 7 | #include "ui_configure_graphics_advanced.h" | 7 | #include "ui_configure_graphics_advanced.h" |
| 8 | #include "yuzu/configuration/configuration_shared.h" | ||
| 8 | #include "yuzu/configuration/configure_graphics_advanced.h" | 9 | #include "yuzu/configuration/configure_graphics_advanced.h" |
| 9 | 10 | ||
| 10 | ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent) | 11 | ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent) |
| @@ -12,8 +13,7 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent) | |||
| 12 | 13 | ||
| 13 | ui->setupUi(this); | 14 | ui->setupUi(this); |
| 14 | 15 | ||
| 15 | // TODO: Remove this after assembly shaders are fully integrated | 16 | SetupPerGameUI(); |
| 16 | ui->use_assembly_shaders->setVisible(false); | ||
| 17 | 17 | ||
| 18 | SetConfiguration(); | 18 | SetConfiguration(); |
| 19 | } | 19 | } |
| @@ -22,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default; | |||
| 22 | 22 | ||
| 23 | void ConfigureGraphicsAdvanced::SetConfiguration() { | 23 | void ConfigureGraphicsAdvanced::SetConfiguration() { |
| 24 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | 24 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); |
| 25 | ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy)); | ||
| 26 | ui->use_vsync->setEnabled(runtime_lock); | 25 | ui->use_vsync->setEnabled(runtime_lock); |
| 27 | ui->use_vsync->setChecked(Settings::values.use_vsync); | ||
| 28 | ui->use_assembly_shaders->setEnabled(runtime_lock); | 26 | ui->use_assembly_shaders->setEnabled(runtime_lock); |
| 29 | ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders); | ||
| 30 | ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time); | ||
| 31 | ui->force_30fps_mode->setEnabled(runtime_lock); | 27 | ui->force_30fps_mode->setEnabled(runtime_lock); |
| 32 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | ||
| 33 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); | 28 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); |
| 34 | ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy); | 29 | |
| 30 | if (Settings::configuring_global) { | ||
| 31 | ui->gpu_accuracy->setCurrentIndex( | ||
| 32 | static_cast<int>(Settings::values.gpu_accuracy.GetValue())); | ||
| 33 | ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); | ||
| 34 | ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); | ||
| 35 | ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); | ||
| 36 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue()); | ||
| 37 | ui->anisotropic_filtering_combobox->setCurrentIndex( | ||
| 38 | Settings::values.max_anisotropy.GetValue()); | ||
| 39 | } else { | ||
| 40 | ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); | ||
| 41 | ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync); | ||
| 42 | ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders, | ||
| 43 | &Settings::values.use_assembly_shaders); | ||
| 44 | ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time, | ||
| 45 | &Settings::values.use_fast_gpu_time); | ||
| 46 | ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode, | ||
| 47 | &Settings::values.force_30fps_mode); | ||
| 48 | ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, | ||
| 49 | &Settings::values.max_anisotropy); | ||
| 50 | } | ||
| 35 | } | 51 | } |
| 36 | 52 | ||
| 37 | void ConfigureGraphicsAdvanced::ApplyConfiguration() { | 53 | void ConfigureGraphicsAdvanced::ApplyConfiguration() { |
| 38 | auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex()); | 54 | // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots) |
| 39 | Settings::values.gpu_accuracy = gpu_accuracy; | 55 | const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>( |
| 40 | Settings::values.use_vsync = ui->use_vsync->isChecked(); | 56 | ui->gpu_accuracy->currentIndex() - |
| 41 | Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked(); | 57 | ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); |
| 42 | Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked(); | 58 | |
| 43 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); | 59 | if (Settings::configuring_global) { |
| 44 | Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex(); | 60 | // Must guard in case of a during-game configuration when set to be game-specific. |
| 61 | if (Settings::values.gpu_accuracy.UsingGlobal()) { | ||
| 62 | Settings::values.gpu_accuracy.SetValue(gpu_accuracy); | ||
| 63 | } | ||
| 64 | if (Settings::values.use_vsync.UsingGlobal()) { | ||
| 65 | Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked()); | ||
| 66 | } | ||
| 67 | if (Settings::values.use_assembly_shaders.UsingGlobal()) { | ||
| 68 | Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked()); | ||
| 69 | } | ||
| 70 | if (Settings::values.use_fast_gpu_time.UsingGlobal()) { | ||
| 71 | Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked()); | ||
| 72 | } | ||
| 73 | if (Settings::values.force_30fps_mode.UsingGlobal()) { | ||
| 74 | Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked()); | ||
| 75 | } | ||
| 76 | if (Settings::values.max_anisotropy.UsingGlobal()) { | ||
| 77 | Settings::values.max_anisotropy.SetValue( | ||
| 78 | ui->anisotropic_filtering_combobox->currentIndex()); | ||
| 79 | } | ||
| 80 | } else { | ||
| 81 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, | ||
| 82 | ui->anisotropic_filtering_combobox); | ||
| 83 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync); | ||
| 84 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, | ||
| 85 | ui->use_assembly_shaders); | ||
| 86 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, | ||
| 87 | ui->use_fast_gpu_time); | ||
| 88 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode, | ||
| 89 | ui->force_30fps_mode); | ||
| 90 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, | ||
| 91 | ui->anisotropic_filtering_combobox); | ||
| 92 | |||
| 93 | if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 94 | Settings::values.gpu_accuracy.SetGlobal(true); | ||
| 95 | } else { | ||
| 96 | Settings::values.gpu_accuracy.SetGlobal(false); | ||
| 97 | Settings::values.gpu_accuracy.SetValue(gpu_accuracy); | ||
| 98 | } | ||
| 99 | } | ||
| 45 | } | 100 | } |
| 46 | 101 | ||
| 47 | void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { | 102 | void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { |
| @@ -55,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { | |||
| 55 | void ConfigureGraphicsAdvanced::RetranslateUI() { | 110 | void ConfigureGraphicsAdvanced::RetranslateUI() { |
| 56 | ui->retranslateUi(this); | 111 | ui->retranslateUi(this); |
| 57 | } | 112 | } |
| 113 | |||
| 114 | void ConfigureGraphicsAdvanced::SetupPerGameUI() { | ||
| 115 | // Disable if not global (only happens during game) | ||
| 116 | if (Settings::configuring_global) { | ||
| 117 | ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); | ||
| 118 | ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); | ||
| 119 | ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); | ||
| 120 | ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); | ||
| 121 | ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal()); | ||
| 122 | ui->anisotropic_filtering_combobox->setEnabled( | ||
| 123 | Settings::values.max_anisotropy.UsingGlobal()); | ||
| 124 | |||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy); | ||
| 129 | ui->use_vsync->setTristate(true); | ||
| 130 | ui->use_assembly_shaders->setTristate(true); | ||
| 131 | ui->use_fast_gpu_time->setTristate(true); | ||
| 132 | ui->force_30fps_mode->setTristate(true); | ||
| 133 | ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox); | ||
| 134 | } | ||
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index bbc9d4355..c043588ff 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h | |||
| @@ -26,5 +26,7 @@ private: | |||
| 26 | 26 | ||
| 27 | void SetConfiguration(); | 27 | void SetConfiguration(); |
| 28 | 28 | ||
| 29 | void SetupPerGameUI(); | ||
| 30 | |||
| 29 | std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; | 31 | std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; |
| 30 | }; | 32 | }; |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index e4eb5594b..00433926d 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -70,6 +70,20 @@ static QString ButtonToText(const Common::ParamPackage& param) { | |||
| 70 | return GetKeyName(param.Get("code", 0)); | 70 | return GetKeyName(param.Get("code", 0)); |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | if (param.Get("engine", "") == "gcpad") { | ||
| 74 | if (param.Has("axis")) { | ||
| 75 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); | ||
| 76 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); | ||
| 77 | |||
| 78 | return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str); | ||
| 79 | } | ||
| 80 | if (param.Has("button")) { | ||
| 81 | const QString button_str = QString::number(int(std::log2(param.Get("button", 0)))); | ||
| 82 | return QObject::tr("GC Button %1").arg(button_str); | ||
| 83 | } | ||
| 84 | return GetKeyName(param.Get("code", 0)); | ||
| 85 | } | ||
| 86 | |||
| 73 | if (param.Get("engine", "") == "sdl") { | 87 | if (param.Get("engine", "") == "sdl") { |
| 74 | if (param.Has("hat")) { | 88 | if (param.Has("hat")) { |
| 75 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); | 89 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); |
| @@ -126,6 +140,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string | |||
| 126 | return {}; | 140 | return {}; |
| 127 | } | 141 | } |
| 128 | 142 | ||
| 143 | if (param.Get("engine", "") == "gcpad") { | ||
| 144 | if (dir == "modifier") { | ||
| 145 | return QObject::tr("[unused]"); | ||
| 146 | } | ||
| 147 | |||
| 148 | if (dir == "left" || dir == "right") { | ||
| 149 | const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); | ||
| 150 | |||
| 151 | return QObject::tr("GC Axis %1").arg(axis_x_str); | ||
| 152 | } | ||
| 153 | |||
| 154 | if (dir == "up" || dir == "down") { | ||
| 155 | const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); | ||
| 156 | |||
| 157 | return QObject::tr("GC Axis %1").arg(axis_y_str); | ||
| 158 | } | ||
| 159 | |||
| 160 | return {}; | ||
| 161 | } | ||
| 129 | return QObject::tr("[unknown]"); | 162 | return QObject::tr("[unknown]"); |
| 130 | } | 163 | } |
| 131 | 164 | ||
| @@ -332,7 +365,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 332 | 365 | ||
| 333 | connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { | 366 | connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { |
| 334 | const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); | 367 | const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); |
| 335 | if (analogs_param[analog_id].Get("engine", "") == "sdl") { | 368 | if (analogs_param[analog_id].Get("engine", "") == "sdl" || |
| 369 | analogs_param[analog_id].Get("engine", "") == "gcpad") { | ||
| 336 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( | 370 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( |
| 337 | tr("Deadzone: %1%").arg(slider_value)); | 371 | tr("Deadzone: %1%").arg(slider_value)); |
| 338 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); | 372 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); |
| @@ -352,6 +386,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 352 | 386 | ||
| 353 | connect(poll_timer.get(), &QTimer::timeout, [this] { | 387 | connect(poll_timer.get(), &QTimer::timeout, [this] { |
| 354 | Common::ParamPackage params; | 388 | Common::ParamPackage params; |
| 389 | if (InputCommon::GetGCButtons()->IsPolling()) { | ||
| 390 | params = InputCommon::GetGCButtons()->GetNextInput(); | ||
| 391 | if (params.Has("engine")) { | ||
| 392 | SetPollingResult(params, false); | ||
| 393 | return; | ||
| 394 | } | ||
| 395 | } | ||
| 396 | if (InputCommon::GetGCAnalogs()->IsPolling()) { | ||
| 397 | params = InputCommon::GetGCAnalogs()->GetNextInput(); | ||
| 398 | if (params.Has("engine")) { | ||
| 399 | SetPollingResult(params, false); | ||
| 400 | return; | ||
| 401 | } | ||
| 402 | } | ||
| 355 | for (auto& poller : device_pollers) { | 403 | for (auto& poller : device_pollers) { |
| 356 | params = poller->GetNextInput(); | 404 | params = poller->GetNextInput(); |
| 357 | if (params.Has("engine")) { | 405 | if (params.Has("engine")) { |
| @@ -480,7 +528,9 @@ void ConfigureInputPlayer::RestoreDefaults() { | |||
| 480 | SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); | 528 | SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); |
| 481 | } | 529 | } |
| 482 | } | 530 | } |
| 531 | |||
| 483 | UpdateButtonLabels(); | 532 | UpdateButtonLabels(); |
| 533 | ApplyConfiguration(); | ||
| 484 | } | 534 | } |
| 485 | 535 | ||
| 486 | void ConfigureInputPlayer::ClearAll() { | 536 | void ConfigureInputPlayer::ClearAll() { |
| @@ -505,6 +555,7 @@ void ConfigureInputPlayer::ClearAll() { | |||
| 505 | } | 555 | } |
| 506 | 556 | ||
| 507 | UpdateButtonLabels(); | 557 | UpdateButtonLabels(); |
| 558 | ApplyConfiguration(); | ||
| 508 | } | 559 | } |
| 509 | 560 | ||
| 510 | void ConfigureInputPlayer::UpdateButtonLabels() { | 561 | void ConfigureInputPlayer::UpdateButtonLabels() { |
| @@ -531,7 +582,7 @@ void ConfigureInputPlayer::UpdateButtonLabels() { | |||
| 531 | analog_map_deadzone_and_modifier_slider_label[analog_id]; | 582 | analog_map_deadzone_and_modifier_slider_label[analog_id]; |
| 532 | 583 | ||
| 533 | if (param.Has("engine")) { | 584 | if (param.Has("engine")) { |
| 534 | if (param.Get("engine", "") == "sdl") { | 585 | if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") { |
| 535 | if (!param.Has("deadzone")) { | 586 | if (!param.Has("deadzone")) { |
| 536 | param.Set("deadzone", 0.1f); | 587 | param.Set("deadzone", 0.1f); |
| 537 | } | 588 | } |
| @@ -580,6 +631,11 @@ void ConfigureInputPlayer::HandleClick( | |||
| 580 | 631 | ||
| 581 | grabKeyboard(); | 632 | grabKeyboard(); |
| 582 | grabMouse(); | 633 | grabMouse(); |
| 634 | if (type == InputCommon::Polling::DeviceType::Button) { | ||
| 635 | InputCommon::GetGCButtons()->BeginConfiguration(); | ||
| 636 | } else { | ||
| 637 | InputCommon::GetGCAnalogs()->BeginConfiguration(); | ||
| 638 | } | ||
| 583 | timeout_timer->start(5000); // Cancel after 5 seconds | 639 | timeout_timer->start(5000); // Cancel after 5 seconds |
| 584 | poll_timer->start(200); // Check for new inputs every 200ms | 640 | poll_timer->start(200); // Check for new inputs every 200ms |
| 585 | } | 641 | } |
| @@ -593,6 +649,9 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, | |||
| 593 | poller->Stop(); | 649 | poller->Stop(); |
| 594 | } | 650 | } |
| 595 | 651 | ||
| 652 | InputCommon::GetGCButtons()->EndConfiguration(); | ||
| 653 | InputCommon::GetGCAnalogs()->EndConfiguration(); | ||
| 654 | |||
| 596 | if (!abort) { | 655 | if (!abort) { |
| 597 | (*input_setter)(params); | 656 | (*input_setter)(params); |
| 598 | } | 657 | } |
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp new file mode 100644 index 000000000..1e49f0787 --- /dev/null +++ b/src/yuzu/configuration/configure_per_game.cpp | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include <utility> | ||
| 8 | |||
| 9 | #include <QCheckBox> | ||
| 10 | #include <QHeaderView> | ||
| 11 | #include <QMenu> | ||
| 12 | #include <QStandardItemModel> | ||
| 13 | #include <QString> | ||
| 14 | #include <QTimer> | ||
| 15 | #include <QTreeView> | ||
| 16 | |||
| 17 | #include "common/common_paths.h" | ||
| 18 | #include "common/file_util.h" | ||
| 19 | #include "core/file_sys/control_metadata.h" | ||
| 20 | #include "core/file_sys/patch_manager.h" | ||
| 21 | #include "core/file_sys/xts_archive.h" | ||
| 22 | #include "core/loader/loader.h" | ||
| 23 | #include "ui_configure_per_game.h" | ||
| 24 | #include "yuzu/configuration/config.h" | ||
| 25 | #include "yuzu/configuration/configure_input.h" | ||
| 26 | #include "yuzu/configuration/configure_per_game.h" | ||
| 27 | #include "yuzu/uisettings.h" | ||
| 28 | #include "yuzu/util/util.h" | ||
| 29 | |||
| 30 | ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) | ||
| 31 | : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { | ||
| 32 | game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); | ||
| 33 | |||
| 34 | Settings::configuring_global = false; | ||
| 35 | |||
| 36 | ui->setupUi(this); | ||
| 37 | setFocusPolicy(Qt::ClickFocus); | ||
| 38 | setWindowTitle(tr("Properties")); | ||
| 39 | |||
| 40 | ui->addonsTab->SetTitleId(title_id); | ||
| 41 | |||
| 42 | scene = new QGraphicsScene; | ||
| 43 | ui->icon_view->setScene(scene); | ||
| 44 | |||
| 45 | LoadConfiguration(); | ||
| 46 | } | ||
| 47 | |||
| 48 | ConfigurePerGame::~ConfigurePerGame() = default; | ||
| 49 | |||
| 50 | void ConfigurePerGame::ApplyConfiguration() { | ||
| 51 | ui->addonsTab->ApplyConfiguration(); | ||
| 52 | ui->generalTab->ApplyConfiguration(); | ||
| 53 | ui->systemTab->ApplyConfiguration(); | ||
| 54 | ui->graphicsTab->ApplyConfiguration(); | ||
| 55 | ui->graphicsAdvancedTab->ApplyConfiguration(); | ||
| 56 | ui->audioTab->ApplyConfiguration(); | ||
| 57 | |||
| 58 | Settings::Apply(); | ||
| 59 | Settings::LogSettings(); | ||
| 60 | |||
| 61 | game_config->Save(); | ||
| 62 | } | ||
| 63 | |||
| 64 | void ConfigurePerGame::changeEvent(QEvent* event) { | ||
| 65 | if (event->type() == QEvent::LanguageChange) { | ||
| 66 | RetranslateUI(); | ||
| 67 | } | ||
| 68 | |||
| 69 | QDialog::changeEvent(event); | ||
| 70 | } | ||
| 71 | |||
| 72 | void ConfigurePerGame::RetranslateUI() { | ||
| 73 | ui->retranslateUi(this); | ||
| 74 | } | ||
| 75 | |||
| 76 | void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) { | ||
| 77 | this->file = std::move(file); | ||
| 78 | LoadConfiguration(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void ConfigurePerGame::LoadConfiguration() { | ||
| 82 | if (file == nullptr) { | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | ui->addonsTab->LoadFromFile(file); | ||
| 87 | |||
| 88 | ui->display_title_id->setText( | ||
| 89 | QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); | ||
| 90 | |||
| 91 | FileSys::PatchManager pm{title_id}; | ||
| 92 | const auto control = pm.GetControlMetadata(); | ||
| 93 | const auto loader = Loader::GetLoader(file); | ||
| 94 | |||
| 95 | if (control.first != nullptr) { | ||
| 96 | ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); | ||
| 97 | ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName())); | ||
| 98 | ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName())); | ||
| 99 | } else { | ||
| 100 | std::string title; | ||
| 101 | if (loader->ReadTitle(title) == Loader::ResultStatus::Success) | ||
| 102 | ui->display_name->setText(QString::fromStdString(title)); | ||
| 103 | |||
| 104 | FileSys::NACP nacp; | ||
| 105 | if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) | ||
| 106 | ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName())); | ||
| 107 | |||
| 108 | ui->display_version->setText(QStringLiteral("1.0.0")); | ||
| 109 | } | ||
| 110 | |||
| 111 | if (control.second != nullptr) { | ||
| 112 | scene->clear(); | ||
| 113 | |||
| 114 | QPixmap map; | ||
| 115 | const auto bytes = control.second->ReadAllBytes(); | ||
| 116 | map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); | ||
| 117 | |||
| 118 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 119 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 120 | } else { | ||
| 121 | std::vector<u8> bytes; | ||
| 122 | if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { | ||
| 123 | scene->clear(); | ||
| 124 | |||
| 125 | QPixmap map; | ||
| 126 | map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); | ||
| 127 | |||
| 128 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 129 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | ui->display_filename->setText(QString::fromStdString(file->GetName())); | ||
| 134 | |||
| 135 | ui->display_format->setText( | ||
| 136 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); | ||
| 137 | |||
| 138 | const auto valueText = ReadableByteSize(file->GetSize()); | ||
| 139 | ui->display_size->setText(valueText); | ||
| 140 | } | ||
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h new file mode 100644 index 000000000..5f9a08cef --- /dev/null +++ b/src/yuzu/configuration/configure_per_game.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include <QDialog> | ||
| 11 | #include <QList> | ||
| 12 | |||
| 13 | #include "core/file_sys/vfs_types.h" | ||
| 14 | #include "yuzu/configuration/config.h" | ||
| 15 | |||
| 16 | class QGraphicsScene; | ||
| 17 | class QStandardItem; | ||
| 18 | class QStandardItemModel; | ||
| 19 | class QTreeView; | ||
| 20 | class QVBoxLayout; | ||
| 21 | |||
| 22 | namespace Ui { | ||
| 23 | class ConfigurePerGame; | ||
| 24 | } | ||
| 25 | |||
| 26 | class ConfigurePerGame : public QDialog { | ||
| 27 | Q_OBJECT | ||
| 28 | |||
| 29 | public: | ||
| 30 | explicit ConfigurePerGame(QWidget* parent, u64 title_id); | ||
| 31 | ~ConfigurePerGame() override; | ||
| 32 | |||
| 33 | /// Save all button configurations to settings file | ||
| 34 | void ApplyConfiguration(); | ||
| 35 | |||
| 36 | void LoadFromFile(FileSys::VirtualFile file); | ||
| 37 | |||
| 38 | private: | ||
| 39 | void changeEvent(QEvent* event) override; | ||
| 40 | void RetranslateUI(); | ||
| 41 | |||
| 42 | void LoadConfiguration(); | ||
| 43 | |||
| 44 | std::unique_ptr<Ui::ConfigurePerGame> ui; | ||
| 45 | FileSys::VirtualFile file; | ||
| 46 | u64 title_id; | ||
| 47 | |||
| 48 | QGraphicsScene* scene; | ||
| 49 | |||
| 50 | std::unique_ptr<Config> game_config; | ||
| 51 | }; | ||
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui new file mode 100644 index 000000000..d2057c4ab --- /dev/null +++ b/src/yuzu/configuration/configure_per_game.ui | |||
| @@ -0,0 +1,350 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigurePerGame</class> | ||
| 4 | <widget class="QDialog" name="ConfigurePerGame"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>800</width> | ||
| 10 | <height>600</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Dialog</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 17 | <item> | ||
| 18 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="groupBox"> | ||
| 21 | <property name="sizePolicy"> | ||
| 22 | <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> | ||
| 23 | <horstretch>0</horstretch> | ||
| 24 | <verstretch>0</verstretch> | ||
| 25 | </sizepolicy> | ||
| 26 | </property> | ||
| 27 | <property name="title"> | ||
| 28 | <string>Info</string> | ||
| 29 | </property> | ||
| 30 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 31 | <item alignment="Qt::AlignHCenter"> | ||
| 32 | <widget class="QGraphicsView" name="icon_view"> | ||
| 33 | <property name="sizePolicy"> | ||
| 34 | <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> | ||
| 35 | <horstretch>0</horstretch> | ||
| 36 | <verstretch>0</verstretch> | ||
| 37 | </sizepolicy> | ||
| 38 | </property> | ||
| 39 | <property name="minimumSize"> | ||
| 40 | <size> | ||
| 41 | <width>256</width> | ||
| 42 | <height>256</height> | ||
| 43 | </size> | ||
| 44 | </property> | ||
| 45 | <property name="maximumSize"> | ||
| 46 | <size> | ||
| 47 | <width>256</width> | ||
| 48 | <height>256</height> | ||
| 49 | </size> | ||
| 50 | </property> | ||
| 51 | <property name="verticalScrollBarPolicy"> | ||
| 52 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 53 | </property> | ||
| 54 | <property name="horizontalScrollBarPolicy"> | ||
| 55 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 56 | </property> | ||
| 57 | <property name="interactive"> | ||
| 58 | <bool>false</bool> | ||
| 59 | </property> | ||
| 60 | </widget> | ||
| 61 | </item> | ||
| 62 | <item> | ||
| 63 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 64 | <item row="6" column="1"> | ||
| 65 | <widget class="QLineEdit" name="display_size"> | ||
| 66 | <property name="enabled"> | ||
| 67 | <bool>true</bool> | ||
| 68 | </property> | ||
| 69 | <property name="readOnly"> | ||
| 70 | <bool>true</bool> | ||
| 71 | </property> | ||
| 72 | </widget> | ||
| 73 | </item> | ||
| 74 | <item row="3" column="1"> | ||
| 75 | <widget class="QLineEdit" name="display_version"> | ||
| 76 | <property name="enabled"> | ||
| 77 | <bool>true</bool> | ||
| 78 | </property> | ||
| 79 | <property name="readOnly"> | ||
| 80 | <bool>true</bool> | ||
| 81 | </property> | ||
| 82 | </widget> | ||
| 83 | </item> | ||
| 84 | <item row="1" column="0"> | ||
| 85 | <widget class="QLabel" name="label"> | ||
| 86 | <property name="text"> | ||
| 87 | <string>Name</string> | ||
| 88 | </property> | ||
| 89 | </widget> | ||
| 90 | </item> | ||
| 91 | <item row="4" column="0"> | ||
| 92 | <widget class="QLabel" name="label_4"> | ||
| 93 | <property name="text"> | ||
| 94 | <string>Title ID</string> | ||
| 95 | </property> | ||
| 96 | </widget> | ||
| 97 | </item> | ||
| 98 | <item row="4" column="1"> | ||
| 99 | <widget class="QLineEdit" name="display_title_id"> | ||
| 100 | <property name="enabled"> | ||
| 101 | <bool>true</bool> | ||
| 102 | </property> | ||
| 103 | <property name="readOnly"> | ||
| 104 | <bool>true</bool> | ||
| 105 | </property> | ||
| 106 | </widget> | ||
| 107 | </item> | ||
| 108 | <item row="7" column="1"> | ||
| 109 | <widget class="QLineEdit" name="display_filename"> | ||
| 110 | <property name="enabled"> | ||
| 111 | <bool>true</bool> | ||
| 112 | </property> | ||
| 113 | <property name="readOnly"> | ||
| 114 | <bool>true</bool> | ||
| 115 | </property> | ||
| 116 | </widget> | ||
| 117 | </item> | ||
| 118 | <item row="5" column="1"> | ||
| 119 | <widget class="QLineEdit" name="display_format"> | ||
| 120 | <property name="enabled"> | ||
| 121 | <bool>true</bool> | ||
| 122 | </property> | ||
| 123 | <property name="readOnly"> | ||
| 124 | <bool>true</bool> | ||
| 125 | </property> | ||
| 126 | </widget> | ||
| 127 | </item> | ||
| 128 | <item row="7" column="0"> | ||
| 129 | <widget class="QLabel" name="label_7"> | ||
| 130 | <property name="text"> | ||
| 131 | <string>Filename</string> | ||
| 132 | </property> | ||
| 133 | </widget> | ||
| 134 | </item> | ||
| 135 | <item row="1" column="1"> | ||
| 136 | <widget class="QLineEdit" name="display_name"> | ||
| 137 | <property name="enabled"> | ||
| 138 | <bool>true</bool> | ||
| 139 | </property> | ||
| 140 | <property name="readOnly"> | ||
| 141 | <bool>true</bool> | ||
| 142 | </property> | ||
| 143 | </widget> | ||
| 144 | </item> | ||
| 145 | <item row="2" column="1"> | ||
| 146 | <widget class="QLineEdit" name="display_developer"> | ||
| 147 | <property name="enabled"> | ||
| 148 | <bool>true</bool> | ||
| 149 | </property> | ||
| 150 | <property name="readOnly"> | ||
| 151 | <bool>true</bool> | ||
| 152 | </property> | ||
| 153 | </widget> | ||
| 154 | </item> | ||
| 155 | <item row="5" column="0"> | ||
| 156 | <widget class="QLabel" name="label_5"> | ||
| 157 | <property name="text"> | ||
| 158 | <string>Format</string> | ||
| 159 | </property> | ||
| 160 | </widget> | ||
| 161 | </item> | ||
| 162 | <item row="3" column="0"> | ||
| 163 | <widget class="QLabel" name="label_3"> | ||
| 164 | <property name="text"> | ||
| 165 | <string>Version</string> | ||
| 166 | </property> | ||
| 167 | </widget> | ||
| 168 | </item> | ||
| 169 | <item row="6" column="0"> | ||
| 170 | <widget class="QLabel" name="label_6"> | ||
| 171 | <property name="text"> | ||
| 172 | <string>Size</string> | ||
| 173 | </property> | ||
| 174 | </widget> | ||
| 175 | </item> | ||
| 176 | <item row="2" column="0"> | ||
| 177 | <widget class="QLabel" name="label_2"> | ||
| 178 | <property name="text"> | ||
| 179 | <string>Developer</string> | ||
| 180 | </property> | ||
| 181 | </widget> | ||
| 182 | </item> | ||
| 183 | </layout> | ||
| 184 | </item> | ||
| 185 | <item> | ||
| 186 | <spacer name="verticalSpacer"> | ||
| 187 | <property name="orientation"> | ||
| 188 | <enum>Qt::Vertical</enum> | ||
| 189 | </property> | ||
| 190 | <property name="sizeHint" stdset="0"> | ||
| 191 | <size> | ||
| 192 | <width>20</width> | ||
| 193 | <height>40</height> | ||
| 194 | </size> | ||
| 195 | </property> | ||
| 196 | </spacer> | ||
| 197 | </item> | ||
| 198 | </layout> | ||
| 199 | </widget> | ||
| 200 | </item> | ||
| 201 | <item> | ||
| 202 | <layout class="QVBoxLayout" name="VerticalLayout"> | ||
| 203 | <item> | ||
| 204 | <layout class="QVBoxLayout" name="verticalLayout_2"/> | ||
| 205 | </item> | ||
| 206 | <item> | ||
| 207 | <widget class="QTabWidget" name="tabWidget"> | ||
| 208 | <property name="enabled"> | ||
| 209 | <bool>true</bool> | ||
| 210 | </property> | ||
| 211 | <property name="currentIndex"> | ||
| 212 | <number>0</number> | ||
| 213 | </property> | ||
| 214 | <property name="usesScrollButtons"> | ||
| 215 | <bool>true</bool> | ||
| 216 | </property> | ||
| 217 | <property name="documentMode"> | ||
| 218 | <bool>false</bool> | ||
| 219 | </property> | ||
| 220 | <property name="tabsClosable"> | ||
| 221 | <bool>false</bool> | ||
| 222 | </property> | ||
| 223 | <widget class="ConfigurePerGameAddons" name="addonsTab"> | ||
| 224 | <attribute name="title"> | ||
| 225 | <string>Add-Ons</string> | ||
| 226 | </attribute> | ||
| 227 | </widget> | ||
| 228 | <widget class="ConfigureGeneral" name="generalTab"> | ||
| 229 | <attribute name="title"> | ||
| 230 | <string>General</string> | ||
| 231 | </attribute> | ||
| 232 | </widget> | ||
| 233 | <widget class="ConfigureSystem" name="systemTab"> | ||
| 234 | <attribute name="title"> | ||
| 235 | <string>System</string> | ||
| 236 | </attribute> | ||
| 237 | </widget> | ||
| 238 | <widget class="ConfigureGraphics" name="graphicsTab"> | ||
| 239 | <attribute name="title"> | ||
| 240 | <string>Graphics</string> | ||
| 241 | </attribute> | ||
| 242 | </widget> | ||
| 243 | <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab"> | ||
| 244 | <attribute name="title"> | ||
| 245 | <string>Adv. Graphics</string> | ||
| 246 | </attribute> | ||
| 247 | </widget> | ||
| 248 | <widget class="ConfigureAudio" name="audioTab"> | ||
| 249 | <attribute name="title"> | ||
| 250 | <string>Audio</string> | ||
| 251 | </attribute> | ||
| 252 | </widget> | ||
| 253 | </widget> | ||
| 254 | </item> | ||
| 255 | </layout> | ||
| 256 | </item> | ||
| 257 | </layout> | ||
| 258 | </item> | ||
| 259 | <item> | ||
| 260 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 261 | <property name="sizePolicy"> | ||
| 262 | <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||
| 263 | <horstretch>0</horstretch> | ||
| 264 | <verstretch>0</verstretch> | ||
| 265 | </sizepolicy> | ||
| 266 | </property> | ||
| 267 | <property name="orientation"> | ||
| 268 | <enum>Qt::Horizontal</enum> | ||
| 269 | </property> | ||
| 270 | <property name="standardButtons"> | ||
| 271 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 272 | </property> | ||
| 273 | </widget> | ||
| 274 | </item> | ||
| 275 | </layout> | ||
| 276 | </widget> | ||
| 277 | <customwidgets> | ||
| 278 | <customwidget> | ||
| 279 | <class>ConfigureGeneral</class> | ||
| 280 | <extends>QWidget</extends> | ||
| 281 | <header>configuration/configure_general.h</header> | ||
| 282 | <container>1</container> | ||
| 283 | </customwidget> | ||
| 284 | <customwidget> | ||
| 285 | <class>ConfigureSystem</class> | ||
| 286 | <extends>QWidget</extends> | ||
| 287 | <header>configuration/configure_system.h</header> | ||
| 288 | <container>1</container> | ||
| 289 | </customwidget> | ||
| 290 | <customwidget> | ||
| 291 | <class>ConfigureAudio</class> | ||
| 292 | <extends>QWidget</extends> | ||
| 293 | <header>configuration/configure_audio.h</header> | ||
| 294 | <container>1</container> | ||
| 295 | </customwidget> | ||
| 296 | <customwidget> | ||
| 297 | <class>ConfigureGraphics</class> | ||
| 298 | <extends>QWidget</extends> | ||
| 299 | <header>configuration/configure_graphics.h</header> | ||
| 300 | <container>1</container> | ||
| 301 | </customwidget> | ||
| 302 | <customwidget> | ||
| 303 | <class>ConfigureGraphicsAdvanced</class> | ||
| 304 | <extends>QWidget</extends> | ||
| 305 | <header>configuration/configure_graphics_advanced.h</header> | ||
| 306 | <container>1</container> | ||
| 307 | </customwidget> | ||
| 308 | <customwidget> | ||
| 309 | <class>ConfigurePerGameAddons</class> | ||
| 310 | <extends>QWidget</extends> | ||
| 311 | <header>configuration/configure_per_game_addons.h</header> | ||
| 312 | <container>1</container> | ||
| 313 | </customwidget> | ||
| 314 | </customwidgets> | ||
| 315 | <resources/> | ||
| 316 | <connections> | ||
| 317 | <connection> | ||
| 318 | <sender>buttonBox</sender> | ||
| 319 | <signal>accepted()</signal> | ||
| 320 | <receiver>ConfigurePerGame</receiver> | ||
| 321 | <slot>accept()</slot> | ||
| 322 | <hints> | ||
| 323 | <hint type="sourcelabel"> | ||
| 324 | <x>248</x> | ||
| 325 | <y>254</y> | ||
| 326 | </hint> | ||
| 327 | <hint type="destinationlabel"> | ||
| 328 | <x>157</x> | ||
| 329 | <y>274</y> | ||
| 330 | </hint> | ||
| 331 | </hints> | ||
| 332 | </connection> | ||
| 333 | <connection> | ||
| 334 | <sender>buttonBox</sender> | ||
| 335 | <signal>rejected()</signal> | ||
| 336 | <receiver>ConfigurePerGame</receiver> | ||
| 337 | <slot>reject()</slot> | ||
| 338 | <hints> | ||
| 339 | <hint type="sourcelabel"> | ||
| 340 | <x>316</x> | ||
| 341 | <y>260</y> | ||
| 342 | </hint> | ||
| 343 | <hint type="destinationlabel"> | ||
| 344 | <x>286</x> | ||
| 345 | <y>274</y> | ||
| 346 | </hint> | ||
| 347 | </hints> | ||
| 348 | </connection> | ||
| 349 | </connections> | ||
| 350 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index d7f259f12..478d5d3a1 100644 --- a/src/yuzu/configuration/configure_per_general.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp | |||
| @@ -15,23 +15,20 @@ | |||
| 15 | 15 | ||
| 16 | #include "common/common_paths.h" | 16 | #include "common/common_paths.h" |
| 17 | #include "common/file_util.h" | 17 | #include "common/file_util.h" |
| 18 | #include "core/file_sys/control_metadata.h" | 18 | #include "core/core.h" |
| 19 | #include "core/file_sys/patch_manager.h" | 19 | #include "core/file_sys/patch_manager.h" |
| 20 | #include "core/file_sys/xts_archive.h" | 20 | #include "core/file_sys/xts_archive.h" |
| 21 | #include "core/loader/loader.h" | 21 | #include "core/loader/loader.h" |
| 22 | #include "ui_configure_per_general.h" | 22 | #include "ui_configure_per_game_addons.h" |
| 23 | #include "yuzu/configuration/config.h" | 23 | #include "yuzu/configuration/config.h" |
| 24 | #include "yuzu/configuration/configure_input.h" | 24 | #include "yuzu/configuration/configure_input.h" |
| 25 | #include "yuzu/configuration/configure_per_general.h" | 25 | #include "yuzu/configuration/configure_per_game_addons.h" |
| 26 | #include "yuzu/uisettings.h" | 26 | #include "yuzu/uisettings.h" |
| 27 | #include "yuzu/util/util.h" | 27 | #include "yuzu/util/util.h" |
| 28 | 28 | ||
| 29 | ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) | 29 | ConfigurePerGameAddons::ConfigurePerGameAddons(QWidget* parent) |
| 30 | : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) { | 30 | : QWidget(parent), ui(new Ui::ConfigurePerGameAddons) { |
| 31 | |||
| 32 | ui->setupUi(this); | 31 | ui->setupUi(this); |
| 33 | setFocusPolicy(Qt::ClickFocus); | ||
| 34 | setWindowTitle(tr("Properties")); | ||
| 35 | 32 | ||
| 36 | layout = new QVBoxLayout; | 33 | layout = new QVBoxLayout; |
| 37 | tree_view = new QTreeView; | 34 | tree_view = new QTreeView; |
| @@ -52,7 +49,7 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) | |||
| 52 | item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); | 49 | item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); |
| 53 | 50 | ||
| 54 | // We must register all custom types with the Qt Automoc system so that we are able to use it | 51 | // We must register all custom types with the Qt Automoc system so that we are able to use it |
| 55 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | 52 | // with signals/slots. In this case, QList falls under the umbrella of custom types. |
| 56 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | 53 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); |
| 57 | 54 | ||
| 58 | layout->setContentsMargins(0, 0, 0, 0); | 55 | layout->setContentsMargins(0, 0, 0, 0); |
| @@ -61,18 +58,15 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) | |||
| 61 | 58 | ||
| 62 | ui->scrollArea->setLayout(layout); | 59 | ui->scrollArea->setLayout(layout); |
| 63 | 60 | ||
| 64 | scene = new QGraphicsScene; | 61 | ui->scrollArea->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 65 | ui->icon_view->setScene(scene); | ||
| 66 | 62 | ||
| 67 | connect(item_model, &QStandardItemModel::itemChanged, | 63 | connect(item_model, &QStandardItemModel::itemChanged, |
| 68 | [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); | 64 | [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); |
| 69 | |||
| 70 | LoadConfiguration(); | ||
| 71 | } | 65 | } |
| 72 | 66 | ||
| 73 | ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; | 67 | ConfigurePerGameAddons::~ConfigurePerGameAddons() = default; |
| 74 | 68 | ||
| 75 | void ConfigurePerGameGeneral::ApplyConfiguration() { | 69 | void ConfigurePerGameAddons::ApplyConfiguration() { |
| 76 | std::vector<std::string> disabled_addons; | 70 | std::vector<std::string> disabled_addons; |
| 77 | 71 | ||
| 78 | for (const auto& item : list_items) { | 72 | for (const auto& item : list_items) { |
| @@ -92,72 +86,35 @@ void ConfigurePerGameGeneral::ApplyConfiguration() { | |||
| 92 | Settings::values.disabled_addons[title_id] = disabled_addons; | 86 | Settings::values.disabled_addons[title_id] = disabled_addons; |
| 93 | } | 87 | } |
| 94 | 88 | ||
| 95 | void ConfigurePerGameGeneral::changeEvent(QEvent* event) { | 89 | void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file) { |
| 90 | this->file = std::move(file); | ||
| 91 | LoadConfiguration(); | ||
| 92 | } | ||
| 93 | |||
| 94 | void ConfigurePerGameAddons::SetTitleId(u64 id) { | ||
| 95 | this->title_id = id; | ||
| 96 | } | ||
| 97 | |||
| 98 | void ConfigurePerGameAddons::changeEvent(QEvent* event) { | ||
| 96 | if (event->type() == QEvent::LanguageChange) { | 99 | if (event->type() == QEvent::LanguageChange) { |
| 97 | RetranslateUI(); | 100 | RetranslateUI(); |
| 98 | } | 101 | } |
| 99 | 102 | ||
| 100 | QDialog::changeEvent(event); | 103 | QWidget::changeEvent(event); |
| 101 | } | 104 | } |
| 102 | 105 | ||
| 103 | void ConfigurePerGameGeneral::RetranslateUI() { | 106 | void ConfigurePerGameAddons::RetranslateUI() { |
| 104 | ui->retranslateUi(this); | 107 | ui->retranslateUi(this); |
| 105 | } | 108 | } |
| 106 | 109 | ||
| 107 | void ConfigurePerGameGeneral::LoadFromFile(FileSys::VirtualFile file) { | 110 | void ConfigurePerGameAddons::LoadConfiguration() { |
| 108 | this->file = std::move(file); | ||
| 109 | LoadConfiguration(); | ||
| 110 | } | ||
| 111 | |||
| 112 | void ConfigurePerGameGeneral::LoadConfiguration() { | ||
| 113 | if (file == nullptr) { | 111 | if (file == nullptr) { |
| 114 | return; | 112 | return; |
| 115 | } | 113 | } |
| 116 | 114 | ||
| 117 | ui->display_title_id->setText(QString::fromStdString(fmt::format("{:016X}", title_id))); | ||
| 118 | |||
| 119 | FileSys::PatchManager pm{title_id}; | 115 | FileSys::PatchManager pm{title_id}; |
| 120 | const auto control = pm.GetControlMetadata(); | ||
| 121 | const auto loader = Loader::GetLoader(file); | 116 | const auto loader = Loader::GetLoader(file); |
| 122 | 117 | ||
| 123 | if (control.first != nullptr) { | ||
| 124 | ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); | ||
| 125 | ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName())); | ||
| 126 | ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName())); | ||
| 127 | } else { | ||
| 128 | std::string title; | ||
| 129 | if (loader->ReadTitle(title) == Loader::ResultStatus::Success) | ||
| 130 | ui->display_name->setText(QString::fromStdString(title)); | ||
| 131 | |||
| 132 | FileSys::NACP nacp; | ||
| 133 | if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) | ||
| 134 | ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName())); | ||
| 135 | |||
| 136 | ui->display_version->setText(QStringLiteral("1.0.0")); | ||
| 137 | } | ||
| 138 | |||
| 139 | if (control.second != nullptr) { | ||
| 140 | scene->clear(); | ||
| 141 | |||
| 142 | QPixmap map; | ||
| 143 | const auto bytes = control.second->ReadAllBytes(); | ||
| 144 | map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); | ||
| 145 | |||
| 146 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 147 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 148 | } else { | ||
| 149 | std::vector<u8> bytes; | ||
| 150 | if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { | ||
| 151 | scene->clear(); | ||
| 152 | |||
| 153 | QPixmap map; | ||
| 154 | map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); | ||
| 155 | |||
| 156 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 157 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | FileSys::VirtualFile update_raw; | 118 | FileSys::VirtualFile update_raw; |
| 162 | loader->ReadUpdateRaw(update_raw); | 119 | loader->ReadUpdateRaw(update_raw); |
| 163 | 120 | ||
| @@ -182,12 +139,4 @@ void ConfigurePerGameGeneral::LoadConfiguration() { | |||
| 182 | } | 139 | } |
| 183 | 140 | ||
| 184 | tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); | 141 | tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); |
| 185 | |||
| 186 | ui->display_filename->setText(QString::fromStdString(file->GetName())); | ||
| 187 | |||
| 188 | ui->display_format->setText( | ||
| 189 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); | ||
| 190 | |||
| 191 | const auto valueText = ReadableByteSize(file->GetSize()); | ||
| 192 | ui->display_size->setText(valueText); | ||
| 193 | } | 142 | } |
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_game_addons.h index a3b2cdeff..a00ec3539 100644 --- a/src/yuzu/configuration/configure_per_general.h +++ b/src/yuzu/configuration/configure_per_game_addons.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | 1 | // Copyright 2016 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include <QDialog> | ||
| 11 | #include <QList> | 10 | #include <QList> |
| 12 | 11 | ||
| 13 | #include "core/file_sys/vfs_types.h" | 12 | #include "core/file_sys/vfs_types.h" |
| @@ -19,35 +18,36 @@ class QTreeView; | |||
| 19 | class QVBoxLayout; | 18 | class QVBoxLayout; |
| 20 | 19 | ||
| 21 | namespace Ui { | 20 | namespace Ui { |
| 22 | class ConfigurePerGameGeneral; | 21 | class ConfigurePerGameAddons; |
| 23 | } | 22 | } |
| 24 | 23 | ||
| 25 | class ConfigurePerGameGeneral : public QDialog { | 24 | class ConfigurePerGameAddons : public QWidget { |
| 26 | Q_OBJECT | 25 | Q_OBJECT |
| 27 | 26 | ||
| 28 | public: | 27 | public: |
| 29 | explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id); | 28 | explicit ConfigurePerGameAddons(QWidget* parent = nullptr); |
| 30 | ~ConfigurePerGameGeneral() override; | 29 | ~ConfigurePerGameAddons() override; |
| 31 | 30 | ||
| 32 | /// Save all button configurations to settings file | 31 | /// Save all button configurations to settings file |
| 33 | void ApplyConfiguration(); | 32 | void ApplyConfiguration(); |
| 34 | 33 | ||
| 35 | void LoadFromFile(FileSys::VirtualFile file); | 34 | void LoadFromFile(FileSys::VirtualFile file); |
| 36 | 35 | ||
| 36 | void SetTitleId(u64 id); | ||
| 37 | |||
| 37 | private: | 38 | private: |
| 38 | void changeEvent(QEvent* event) override; | 39 | void changeEvent(QEvent* event) override; |
| 39 | void RetranslateUI(); | 40 | void RetranslateUI(); |
| 40 | 41 | ||
| 41 | void LoadConfiguration(); | 42 | void LoadConfiguration(); |
| 42 | 43 | ||
| 43 | std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; | 44 | std::unique_ptr<Ui::ConfigurePerGameAddons> ui; |
| 44 | FileSys::VirtualFile file; | 45 | FileSys::VirtualFile file; |
| 45 | u64 title_id; | 46 | u64 title_id; |
| 46 | 47 | ||
| 47 | QVBoxLayout* layout; | 48 | QVBoxLayout* layout; |
| 48 | QTreeView* tree_view; | 49 | QTreeView* tree_view; |
| 49 | QStandardItemModel* item_model; | 50 | QStandardItemModel* item_model; |
| 50 | QGraphicsScene* scene; | ||
| 51 | 51 | ||
| 52 | std::vector<QList<QStandardItem*>> list_items; | 52 | std::vector<QList<QStandardItem*>> list_items; |
| 53 | }; | 53 | }; |
diff --git a/src/yuzu/configuration/configure_per_game_addons.ui b/src/yuzu/configuration/configure_per_game_addons.ui new file mode 100644 index 000000000..aefdebfcd --- /dev/null +++ b/src/yuzu/configuration/configure_per_game_addons.ui | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigurePerGameAddons</class> | ||
| 4 | <widget class="QWidget" name="ConfigurePerGameAddons"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>400</width> | ||
| 10 | <height>300</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Form</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QGridLayout" name="gridLayout"> | ||
| 17 | <item row="0" column="0"> | ||
| 18 | <widget class="QScrollArea" name="scrollArea"> | ||
| 19 | <property name="widgetResizable"> | ||
| 20 | <bool>true</bool> | ||
| 21 | </property> | ||
| 22 | <widget class="QWidget" name="scrollAreaWidgetContents"> | ||
| 23 | <property name="geometry"> | ||
| 24 | <rect> | ||
| 25 | <x>0</x> | ||
| 26 | <y>0</y> | ||
| 27 | <width>380</width> | ||
| 28 | <height>280</height> | ||
| 29 | </rect> | ||
| 30 | </property> | ||
| 31 | </widget> | ||
| 32 | </widget> | ||
| 33 | </item> | ||
| 34 | </layout> | ||
| 35 | </widget> | ||
| 36 | <resources/> | ||
| 37 | <connections/> | ||
| 38 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui deleted file mode 100644 index 8fdd96fa4..000000000 --- a/src/yuzu/configuration/configure_per_general.ui +++ /dev/null | |||
| @@ -1,276 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigurePerGameGeneral</class> | ||
| 4 | <widget class="QDialog" name="ConfigurePerGameGeneral"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>400</width> | ||
| 10 | <height>520</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>ConfigurePerGameGeneral</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QHBoxLayout" name="HorizontalLayout"> | ||
| 17 | <item> | ||
| 18 | <layout class="QVBoxLayout" name="VerticalLayout"> | ||
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="GeneralGroupBox"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>Info</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> | ||
| 25 | <item> | ||
| 26 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 27 | <item row="6" column="1" colspan="2"> | ||
| 28 | <widget class="QLineEdit" name="display_filename"> | ||
| 29 | <property name="enabled"> | ||
| 30 | <bool>true</bool> | ||
| 31 | </property> | ||
| 32 | <property name="readOnly"> | ||
| 33 | <bool>true</bool> | ||
| 34 | </property> | ||
| 35 | </widget> | ||
| 36 | </item> | ||
| 37 | <item row="0" column="1"> | ||
| 38 | <widget class="QLineEdit" name="display_name"> | ||
| 39 | <property name="enabled"> | ||
| 40 | <bool>true</bool> | ||
| 41 | </property> | ||
| 42 | <property name="readOnly"> | ||
| 43 | <bool>true</bool> | ||
| 44 | </property> | ||
| 45 | </widget> | ||
| 46 | </item> | ||
| 47 | <item row="1" column="0"> | ||
| 48 | <widget class="QLabel" name="label_2"> | ||
| 49 | <property name="text"> | ||
| 50 | <string>Developer</string> | ||
| 51 | </property> | ||
| 52 | </widget> | ||
| 53 | </item> | ||
| 54 | <item row="5" column="1" colspan="2"> | ||
| 55 | <widget class="QLineEdit" name="display_size"> | ||
| 56 | <property name="enabled"> | ||
| 57 | <bool>true</bool> | ||
| 58 | </property> | ||
| 59 | <property name="readOnly"> | ||
| 60 | <bool>true</bool> | ||
| 61 | </property> | ||
| 62 | </widget> | ||
| 63 | </item> | ||
| 64 | <item row="0" column="0"> | ||
| 65 | <widget class="QLabel" name="label"> | ||
| 66 | <property name="text"> | ||
| 67 | <string>Name</string> | ||
| 68 | </property> | ||
| 69 | </widget> | ||
| 70 | </item> | ||
| 71 | <item row="6" column="0"> | ||
| 72 | <widget class="QLabel" name="label_7"> | ||
| 73 | <property name="text"> | ||
| 74 | <string>Filename</string> | ||
| 75 | </property> | ||
| 76 | </widget> | ||
| 77 | </item> | ||
| 78 | <item row="2" column="0"> | ||
| 79 | <widget class="QLabel" name="label_3"> | ||
| 80 | <property name="text"> | ||
| 81 | <string>Version</string> | ||
| 82 | </property> | ||
| 83 | </widget> | ||
| 84 | </item> | ||
| 85 | <item row="4" column="0"> | ||
| 86 | <widget class="QLabel" name="label_5"> | ||
| 87 | <property name="text"> | ||
| 88 | <string>Format</string> | ||
| 89 | </property> | ||
| 90 | </widget> | ||
| 91 | </item> | ||
| 92 | <item row="2" column="1"> | ||
| 93 | <widget class="QLineEdit" name="display_version"> | ||
| 94 | <property name="enabled"> | ||
| 95 | <bool>true</bool> | ||
| 96 | </property> | ||
| 97 | <property name="readOnly"> | ||
| 98 | <bool>true</bool> | ||
| 99 | </property> | ||
| 100 | </widget> | ||
| 101 | </item> | ||
| 102 | <item row="4" column="1"> | ||
| 103 | <widget class="QLineEdit" name="display_format"> | ||
| 104 | <property name="enabled"> | ||
| 105 | <bool>true</bool> | ||
| 106 | </property> | ||
| 107 | <property name="readOnly"> | ||
| 108 | <bool>true</bool> | ||
| 109 | </property> | ||
| 110 | </widget> | ||
| 111 | </item> | ||
| 112 | <item row="5" column="0"> | ||
| 113 | <widget class="QLabel" name="label_6"> | ||
| 114 | <property name="text"> | ||
| 115 | <string>Size</string> | ||
| 116 | </property> | ||
| 117 | </widget> | ||
| 118 | </item> | ||
| 119 | <item row="1" column="1"> | ||
| 120 | <widget class="QLineEdit" name="display_developer"> | ||
| 121 | <property name="enabled"> | ||
| 122 | <bool>true</bool> | ||
| 123 | </property> | ||
| 124 | <property name="readOnly"> | ||
| 125 | <bool>true</bool> | ||
| 126 | </property> | ||
| 127 | </widget> | ||
| 128 | </item> | ||
| 129 | <item row="3" column="0"> | ||
| 130 | <widget class="QLabel" name="label_4"> | ||
| 131 | <property name="text"> | ||
| 132 | <string>Title ID</string> | ||
| 133 | </property> | ||
| 134 | </widget> | ||
| 135 | </item> | ||
| 136 | <item row="3" column="1"> | ||
| 137 | <widget class="QLineEdit" name="display_title_id"> | ||
| 138 | <property name="enabled"> | ||
| 139 | <bool>true</bool> | ||
| 140 | </property> | ||
| 141 | <property name="readOnly"> | ||
| 142 | <bool>true</bool> | ||
| 143 | </property> | ||
| 144 | </widget> | ||
| 145 | </item> | ||
| 146 | <item row="0" column="2" rowspan="5"> | ||
| 147 | <widget class="QGraphicsView" name="icon_view"> | ||
| 148 | <property name="sizePolicy"> | ||
| 149 | <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> | ||
| 150 | <horstretch>0</horstretch> | ||
| 151 | <verstretch>0</verstretch> | ||
| 152 | </sizepolicy> | ||
| 153 | </property> | ||
| 154 | <property name="minimumSize"> | ||
| 155 | <size> | ||
| 156 | <width>128</width> | ||
| 157 | <height>128</height> | ||
| 158 | </size> | ||
| 159 | </property> | ||
| 160 | <property name="maximumSize"> | ||
| 161 | <size> | ||
| 162 | <width>128</width> | ||
| 163 | <height>128</height> | ||
| 164 | </size> | ||
| 165 | </property> | ||
| 166 | <property name="verticalScrollBarPolicy"> | ||
| 167 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 168 | </property> | ||
| 169 | <property name="horizontalScrollBarPolicy"> | ||
| 170 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 171 | </property> | ||
| 172 | <property name="sizeAdjustPolicy"> | ||
| 173 | <enum>QAbstractScrollArea::AdjustToContents</enum> | ||
| 174 | </property> | ||
| 175 | <property name="interactive"> | ||
| 176 | <bool>false</bool> | ||
| 177 | </property> | ||
| 178 | </widget> | ||
| 179 | </item> | ||
| 180 | </layout> | ||
| 181 | </item> | ||
| 182 | </layout> | ||
| 183 | </widget> | ||
| 184 | </item> | ||
| 185 | <item> | ||
| 186 | <widget class="QGroupBox" name="PerformanceGroupBox"> | ||
| 187 | <property name="title"> | ||
| 188 | <string>Add-Ons</string> | ||
| 189 | </property> | ||
| 190 | <layout class="QHBoxLayout" name="PerformanceHorizontalLayout"> | ||
| 191 | <item> | ||
| 192 | <widget class="QScrollArea" name="scrollArea"> | ||
| 193 | <property name="widgetResizable"> | ||
| 194 | <bool>true</bool> | ||
| 195 | </property> | ||
| 196 | <widget class="QWidget" name="scrollAreaWidgetContents"> | ||
| 197 | <property name="geometry"> | ||
| 198 | <rect> | ||
| 199 | <x>0</x> | ||
| 200 | <y>0</y> | ||
| 201 | <width>350</width> | ||
| 202 | <height>169</height> | ||
| 203 | </rect> | ||
| 204 | </property> | ||
| 205 | </widget> | ||
| 206 | </widget> | ||
| 207 | </item> | ||
| 208 | <item> | ||
| 209 | <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/> | ||
| 210 | </item> | ||
| 211 | </layout> | ||
| 212 | </widget> | ||
| 213 | </item> | ||
| 214 | <item> | ||
| 215 | <spacer name="verticalSpacer"> | ||
| 216 | <property name="orientation"> | ||
| 217 | <enum>Qt::Vertical</enum> | ||
| 218 | </property> | ||
| 219 | <property name="sizeType"> | ||
| 220 | <enum>QSizePolicy::Fixed</enum> | ||
| 221 | </property> | ||
| 222 | <property name="sizeHint" stdset="0"> | ||
| 223 | <size> | ||
| 224 | <width>20</width> | ||
| 225 | <height>40</height> | ||
| 226 | </size> | ||
| 227 | </property> | ||
| 228 | </spacer> | ||
| 229 | </item> | ||
| 230 | <item> | ||
| 231 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 232 | <property name="standardButtons"> | ||
| 233 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 234 | </property> | ||
| 235 | </widget> | ||
| 236 | </item> | ||
| 237 | </layout> | ||
| 238 | </item> | ||
| 239 | </layout> | ||
| 240 | </widget> | ||
| 241 | <resources/> | ||
| 242 | <connections> | ||
| 243 | <connection> | ||
| 244 | <sender>buttonBox</sender> | ||
| 245 | <signal>accepted()</signal> | ||
| 246 | <receiver>ConfigurePerGameGeneral</receiver> | ||
| 247 | <slot>accept()</slot> | ||
| 248 | <hints> | ||
| 249 | <hint type="sourcelabel"> | ||
| 250 | <x>269</x> | ||
| 251 | <y>567</y> | ||
| 252 | </hint> | ||
| 253 | <hint type="destinationlabel"> | ||
| 254 | <x>269</x> | ||
| 255 | <y>294</y> | ||
| 256 | </hint> | ||
| 257 | </hints> | ||
| 258 | </connection> | ||
| 259 | <connection> | ||
| 260 | <sender>buttonBox</sender> | ||
| 261 | <signal>rejected()</signal> | ||
| 262 | <receiver>ConfigurePerGameGeneral</receiver> | ||
| 263 | <slot>reject()</slot> | ||
| 264 | <hints> | ||
| 265 | <hint type="sourcelabel"> | ||
| 266 | <x>269</x> | ||
| 267 | <y>567</y> | ||
| 268 | </hint> | ||
| 269 | <hint type="destinationlabel"> | ||
| 270 | <x>269</x> | ||
| 271 | <y>294</y> | ||
| 272 | </hint> | ||
| 273 | </hints> | ||
| 274 | </connection> | ||
| 275 | </connections> | ||
| 276 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp index 06566e981..0de7a4f0b 100644 --- a/src/yuzu/configuration/configure_service.cpp +++ b/src/yuzu/configuration/configure_service.cpp | |||
| @@ -68,6 +68,7 @@ void ConfigureService::SetConfiguration() { | |||
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { | 70 | std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { |
| 71 | #ifdef YUZU_ENABLE_BOXCAT | ||
| 71 | std::optional<std::string> global; | 72 | std::optional<std::string> global; |
| 72 | std::map<std::string, Service::BCAT::EventStatus> map; | 73 | std::map<std::string, Service::BCAT::EventStatus> map; |
| 73 | const auto res = Service::BCAT::Boxcat::GetStatus(global, map); | 74 | const auto res = Service::BCAT::Boxcat::GetStatus(global, map); |
| @@ -105,7 +106,10 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { | |||
| 105 | .arg(QString::fromStdString(key)) | 106 | .arg(QString::fromStdString(key)) |
| 106 | .arg(FormatEventStatusString(value)); | 107 | .arg(FormatEventStatusString(value)); |
| 107 | } | 108 | } |
| 108 | return {QStringLiteral("Current Boxcat Events"), std::move(out)}; | 109 | return {tr("Current Boxcat Events"), std::move(out)}; |
| 110 | #else | ||
| 111 | return {tr("Current Boxcat Events"), tr("There are currently no events on boxcat.")}; | ||
| 112 | #endif | ||
| 109 | } | 113 | } |
| 110 | 114 | ||
| 111 | void ConfigureService::OnBCATImplChanged() { | 115 | void ConfigureService::OnBCATImplChanged() { |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 10315e7a6..68e02738b 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 16 | #include "ui_configure_system.h" | 16 | #include "ui_configure_system.h" |
| 17 | #include "yuzu/configuration/configuration_shared.h" | ||
| 17 | #include "yuzu/configuration/configure_system.h" | 18 | #include "yuzu/configuration/configure_system.h" |
| 18 | 19 | ||
| 19 | ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { | 20 | ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { |
| @@ -21,20 +22,25 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui:: | |||
| 21 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, | 22 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, |
| 22 | &ConfigureSystem::RefreshConsoleID); | 23 | &ConfigureSystem::RefreshConsoleID); |
| 23 | 24 | ||
| 24 | connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { | 25 | connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) { |
| 25 | ui->rng_seed_edit->setEnabled(checked); | 26 | ui->rng_seed_edit->setEnabled(state == Qt::Checked); |
| 26 | if (!checked) { | 27 | if (state != Qt::Checked) { |
| 27 | ui->rng_seed_edit->setText(QStringLiteral("00000000")); | 28 | ui->rng_seed_edit->setText(QStringLiteral("00000000")); |
| 28 | } | 29 | } |
| 29 | }); | 30 | }); |
| 30 | 31 | ||
| 31 | connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { | 32 | connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) { |
| 32 | ui->custom_rtc_edit->setEnabled(checked); | 33 | ui->custom_rtc_edit->setEnabled(state == Qt::Checked); |
| 33 | if (!checked) { | 34 | if (state != Qt::Checked) { |
| 34 | ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime()); | 35 | ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime()); |
| 35 | } | 36 | } |
| 36 | }); | 37 | }); |
| 37 | 38 | ||
| 39 | ui->label_console_id->setVisible(Settings::configuring_global); | ||
| 40 | ui->button_regenerate_console_id->setVisible(Settings::configuring_global); | ||
| 41 | |||
| 42 | SetupPerGameUI(); | ||
| 43 | |||
| 38 | SetConfiguration(); | 44 | SetConfiguration(); |
| 39 | } | 45 | } |
| 40 | 46 | ||
| @@ -54,26 +60,58 @@ void ConfigureSystem::RetranslateUI() { | |||
| 54 | 60 | ||
| 55 | void ConfigureSystem::SetConfiguration() { | 61 | void ConfigureSystem::SetConfiguration() { |
| 56 | enabled = !Core::System::GetInstance().IsPoweredOn(); | 62 | enabled = !Core::System::GetInstance().IsPoweredOn(); |
| 63 | const auto rng_seed = | ||
| 64 | QStringLiteral("%1") | ||
| 65 | .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'}) | ||
| 66 | .toUpper(); | ||
| 67 | const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or( | ||
| 68 | std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); | ||
| 57 | 69 | ||
| 58 | ui->combo_language->setCurrentIndex(Settings::values.language_index); | 70 | if (Settings::configuring_global) { |
| 59 | ui->combo_region->setCurrentIndex(Settings::values.region_index); | 71 | ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); |
| 60 | ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index); | 72 | ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue()); |
| 61 | ui->combo_sound->setCurrentIndex(Settings::values.sound_index); | 73 | ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue()); |
| 62 | 74 | ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue()); | |
| 63 | ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); | 75 | |
| 64 | ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); | 76 | ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value()); |
| 65 | 77 | ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() && | |
| 66 | const auto rng_seed = QStringLiteral("%1") | 78 | Settings::values.rng_seed.UsingGlobal()); |
| 67 | .arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}) | 79 | ui->rng_seed_edit->setText(rng_seed); |
| 68 | .toUpper(); | 80 | |
| 69 | ui->rng_seed_edit->setText(rng_seed); | 81 | ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value()); |
| 70 | 82 | ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() && | |
| 71 | ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); | 83 | Settings::values.rng_seed.UsingGlobal()); |
| 72 | ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); | 84 | ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); |
| 85 | } else { | ||
| 86 | ConfigurationShared::SetPerGameSetting(ui->combo_language, | ||
| 87 | &Settings::values.language_index); | ||
| 88 | ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index); | ||
| 89 | ConfigurationShared::SetPerGameSetting(ui->combo_time_zone, | ||
| 90 | &Settings::values.time_zone_index); | ||
| 91 | ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index); | ||
| 92 | |||
| 93 | if (Settings::values.rng_seed.UsingGlobal()) { | ||
| 94 | ui->rng_seed_checkbox->setCheckState(Qt::PartiallyChecked); | ||
| 95 | } else { | ||
| 96 | ui->rng_seed_checkbox->setCheckState( | ||
| 97 | Settings::values.rng_seed.GetValue().has_value() ? Qt::Checked : Qt::Unchecked); | ||
| 98 | ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value()); | ||
| 99 | if (Settings::values.rng_seed.GetValue().has_value()) { | ||
| 100 | ui->rng_seed_edit->setText(rng_seed); | ||
| 101 | } | ||
| 102 | } | ||
| 73 | 103 | ||
| 74 | const auto rtc_time = Settings::values.custom_rtc.value_or( | 104 | if (Settings::values.custom_rtc.UsingGlobal()) { |
| 75 | std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); | 105 | ui->custom_rtc_checkbox->setCheckState(Qt::PartiallyChecked); |
| 76 | ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); | 106 | } else { |
| 107 | ui->custom_rtc_checkbox->setCheckState( | ||
| 108 | Settings::values.custom_rtc.GetValue().has_value() ? Qt::Checked : Qt::Unchecked); | ||
| 109 | ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value()); | ||
| 110 | if (Settings::values.custom_rtc.GetValue().has_value()) { | ||
| 111 | ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 77 | } | 115 | } |
| 78 | 116 | ||
| 79 | void ConfigureSystem::ReadSystemSettings() {} | 117 | void ConfigureSystem::ReadSystemSettings() {} |
| @@ -83,22 +121,78 @@ void ConfigureSystem::ApplyConfiguration() { | |||
| 83 | return; | 121 | return; |
| 84 | } | 122 | } |
| 85 | 123 | ||
| 86 | Settings::values.language_index = ui->combo_language->currentIndex(); | 124 | if (Settings::configuring_global) { |
| 87 | Settings::values.region_index = ui->combo_region->currentIndex(); | 125 | // Guard if during game and set to game-specific value |
| 88 | Settings::values.time_zone_index = ui->combo_time_zone->currentIndex(); | 126 | if (Settings::values.language_index.UsingGlobal()) { |
| 89 | Settings::values.sound_index = ui->combo_sound->currentIndex(); | 127 | Settings::values.language_index.SetValue(ui->combo_language->currentIndex()); |
| 128 | } | ||
| 129 | if (Settings::values.region_index.UsingGlobal()) { | ||
| 130 | Settings::values.region_index.SetValue(ui->combo_region->currentIndex()); | ||
| 131 | } | ||
| 132 | if (Settings::values.time_zone_index.UsingGlobal()) { | ||
| 133 | Settings::values.time_zone_index.SetValue(ui->combo_time_zone->currentIndex()); | ||
| 134 | } | ||
| 135 | if (Settings::values.sound_index.UsingGlobal()) { | ||
| 136 | Settings::values.sound_index.SetValue(ui->combo_sound->currentIndex()); | ||
| 137 | } | ||
| 138 | |||
| 139 | if (Settings::values.rng_seed.UsingGlobal()) { | ||
| 140 | if (ui->rng_seed_checkbox->isChecked()) { | ||
| 141 | Settings::values.rng_seed.SetValue( | ||
| 142 | ui->rng_seed_edit->text().toULongLong(nullptr, 16)); | ||
| 143 | } else { | ||
| 144 | Settings::values.rng_seed.SetValue(std::nullopt); | ||
| 145 | } | ||
| 146 | } | ||
| 90 | 147 | ||
| 91 | if (ui->rng_seed_checkbox->isChecked()) { | 148 | if (Settings::values.custom_rtc.UsingGlobal()) { |
| 92 | Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16); | 149 | if (ui->custom_rtc_checkbox->isChecked()) { |
| 150 | Settings::values.custom_rtc.SetValue( | ||
| 151 | std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); | ||
| 152 | } else { | ||
| 153 | Settings::values.custom_rtc.SetValue(std::nullopt); | ||
| 154 | } | ||
| 155 | } | ||
| 93 | } else { | 156 | } else { |
| 94 | Settings::values.rng_seed = std::nullopt; | 157 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, |
| 95 | } | 158 | ui->combo_language); |
| 159 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); | ||
| 160 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, | ||
| 161 | ui->combo_time_zone); | ||
| 162 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); | ||
| 163 | |||
| 164 | switch (ui->rng_seed_checkbox->checkState()) { | ||
| 165 | case Qt::Checked: | ||
| 166 | Settings::values.rng_seed.SetGlobal(false); | ||
| 167 | Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toULongLong(nullptr, 16)); | ||
| 168 | break; | ||
| 169 | case Qt::Unchecked: | ||
| 170 | Settings::values.rng_seed.SetGlobal(false); | ||
| 171 | Settings::values.rng_seed.SetValue(std::nullopt); | ||
| 172 | break; | ||
| 173 | case Qt::PartiallyChecked: | ||
| 174 | Settings::values.rng_seed.SetGlobal(false); | ||
| 175 | Settings::values.rng_seed.SetValue(std::nullopt); | ||
| 176 | Settings::values.rng_seed.SetGlobal(true); | ||
| 177 | break; | ||
| 178 | } | ||
| 96 | 179 | ||
| 97 | if (ui->custom_rtc_checkbox->isChecked()) { | 180 | switch (ui->custom_rtc_checkbox->checkState()) { |
| 98 | Settings::values.custom_rtc = | 181 | case Qt::Checked: |
| 99 | std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()); | 182 | Settings::values.custom_rtc.SetGlobal(false); |
| 100 | } else { | 183 | Settings::values.custom_rtc.SetValue( |
| 101 | Settings::values.custom_rtc = std::nullopt; | 184 | std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); |
| 185 | break; | ||
| 186 | case Qt::Unchecked: | ||
| 187 | Settings::values.custom_rtc.SetGlobal(false); | ||
| 188 | Settings::values.custom_rtc.SetValue(std::nullopt); | ||
| 189 | break; | ||
| 190 | case Qt::PartiallyChecked: | ||
| 191 | Settings::values.custom_rtc.SetGlobal(false); | ||
| 192 | Settings::values.custom_rtc.SetValue(std::nullopt); | ||
| 193 | Settings::values.custom_rtc.SetGlobal(true); | ||
| 194 | break; | ||
| 195 | } | ||
| 102 | } | 196 | } |
| 103 | 197 | ||
| 104 | Settings::Apply(); | 198 | Settings::Apply(); |
| @@ -120,3 +214,25 @@ void ConfigureSystem::RefreshConsoleID() { | |||
| 120 | ui->label_console_id->setText( | 214 | ui->label_console_id->setText( |
| 121 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); | 215 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); |
| 122 | } | 216 | } |
| 217 | |||
| 218 | void ConfigureSystem::SetupPerGameUI() { | ||
| 219 | if (Settings::configuring_global) { | ||
| 220 | ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal()); | ||
| 221 | ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal()); | ||
| 222 | ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal()); | ||
| 223 | ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal()); | ||
| 224 | ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal()); | ||
| 225 | ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal()); | ||
| 226 | ui->custom_rtc_checkbox->setEnabled(Settings::values.custom_rtc.UsingGlobal()); | ||
| 227 | ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.UsingGlobal()); | ||
| 228 | |||
| 229 | return; | ||
| 230 | } | ||
| 231 | |||
| 232 | ConfigurationShared::InsertGlobalItem(ui->combo_language); | ||
| 233 | ConfigurationShared::InsertGlobalItem(ui->combo_region); | ||
| 234 | ConfigurationShared::InsertGlobalItem(ui->combo_time_zone); | ||
| 235 | ConfigurationShared::InsertGlobalItem(ui->combo_sound); | ||
| 236 | ui->rng_seed_checkbox->setTristate(true); | ||
| 237 | ui->custom_rtc_checkbox->setTristate(true); | ||
| 238 | } | ||
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index 26d42d5c5..f317ef8b5 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h | |||
| @@ -32,6 +32,8 @@ private: | |||
| 32 | 32 | ||
| 33 | void RefreshConsoleID(); | 33 | void RefreshConsoleID(); |
| 34 | 34 | ||
| 35 | void SetupPerGameUI(); | ||
| 36 | |||
| 35 | std::unique_ptr<Ui::ConfigureSystem> ui; | 37 | std::unique_ptr<Ui::ConfigureSystem> ui; |
| 36 | bool enabled = false; | 38 | bool enabled = false; |
| 37 | 39 | ||
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index c1ea25fb8..9bb0a0109 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -2,10 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | ||
| 6 | |||
| 5 | #include "yuzu/debugger/wait_tree.h" | 7 | #include "yuzu/debugger/wait_tree.h" |
| 6 | #include "yuzu/util/util.h" | 8 | #include "yuzu/util/util.h" |
| 7 | 9 | ||
| 8 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "core/arm/arm_interface.h" | ||
| 9 | #include "core/core.h" | 12 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/handle_table.h" | 13 | #include "core/hle/kernel/handle_table.h" |
| 11 | #include "core/hle/kernel/mutex.h" | 14 | #include "core/hle/kernel/mutex.h" |
| @@ -59,8 +62,10 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() | |||
| 59 | std::size_t row = 0; | 62 | std::size_t row = 0; |
| 60 | auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { | 63 | auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { |
| 61 | for (std::size_t i = 0; i < threads.size(); ++i) { | 64 | for (std::size_t i = 0; i < threads.size(); ++i) { |
| 62 | item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); | 65 | if (!threads[i]->IsHLEThread()) { |
| 63 | item_list.back()->row = row; | 66 | item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); |
| 67 | item_list.back()->row = row; | ||
| 68 | } | ||
| 64 | ++row; | 69 | ++row; |
| 65 | } | 70 | } |
| 66 | }; | 71 | }; |
| @@ -114,20 +119,21 @@ QString WaitTreeCallstack::GetText() const { | |||
| 114 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { | 119 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { |
| 115 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 120 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 116 | 121 | ||
| 117 | constexpr std::size_t BaseRegister = 29; | 122 | if (thread.IsHLEThread()) { |
| 118 | auto& memory = Core::System::GetInstance().Memory(); | 123 | return list; |
| 119 | u64 base_pointer = thread.GetContext64().cpu_registers[BaseRegister]; | 124 | } |
| 120 | 125 | ||
| 121 | while (base_pointer != 0) { | 126 | if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) { |
| 122 | const u64 lr = memory.Read64(base_pointer + sizeof(u64)); | 127 | return list; |
| 123 | if (lr == 0) { | 128 | } |
| 124 | break; | ||
| 125 | } | ||
| 126 | 129 | ||
| 127 | list.push_back(std::make_unique<WaitTreeText>( | 130 | auto backtrace = Core::ARM_Interface::GetBacktraceFromContext(Core::System::GetInstance(), |
| 128 | tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); | 131 | thread.GetContext64()); |
| 129 | 132 | ||
| 130 | base_pointer = memory.Read64(base_pointer); | 133 | for (auto& entry : backtrace) { |
| 134 | std::string s = fmt::format("{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, | ||
| 135 | entry.original_address, entry.offset, entry.name); | ||
| 136 | list.push_back(std::make_unique<WaitTreeText>(QString::fromStdString(s))); | ||
| 131 | } | 137 | } |
| 132 | 138 | ||
| 133 | return list; | 139 | return list; |
| @@ -206,7 +212,15 @@ QString WaitTreeThread::GetText() const { | |||
| 206 | status = tr("running"); | 212 | status = tr("running"); |
| 207 | break; | 213 | break; |
| 208 | case Kernel::ThreadStatus::Ready: | 214 | case Kernel::ThreadStatus::Ready: |
| 209 | status = tr("ready"); | 215 | if (!thread.IsPaused()) { |
| 216 | if (thread.WasRunning()) { | ||
| 217 | status = tr("running"); | ||
| 218 | } else { | ||
| 219 | status = tr("ready"); | ||
| 220 | } | ||
| 221 | } else { | ||
| 222 | status = tr("paused"); | ||
| 223 | } | ||
| 210 | break; | 224 | break; |
| 211 | case Kernel::ThreadStatus::Paused: | 225 | case Kernel::ThreadStatus::Paused: |
| 212 | status = tr("paused"); | 226 | status = tr("paused"); |
| @@ -254,7 +268,15 @@ QColor WaitTreeThread::GetColor() const { | |||
| 254 | case Kernel::ThreadStatus::Running: | 268 | case Kernel::ThreadStatus::Running: |
| 255 | return QColor(Qt::GlobalColor::darkGreen); | 269 | return QColor(Qt::GlobalColor::darkGreen); |
| 256 | case Kernel::ThreadStatus::Ready: | 270 | case Kernel::ThreadStatus::Ready: |
| 257 | return QColor(Qt::GlobalColor::darkBlue); | 271 | if (!thread.IsPaused()) { |
| 272 | if (thread.WasRunning()) { | ||
| 273 | return QColor(Qt::GlobalColor::darkGreen); | ||
| 274 | } else { | ||
| 275 | return QColor(Qt::GlobalColor::darkBlue); | ||
| 276 | } | ||
| 277 | } else { | ||
| 278 | return QColor(Qt::GlobalColor::lightGray); | ||
| 279 | } | ||
| 258 | case Kernel::ThreadStatus::Paused: | 280 | case Kernel::ThreadStatus::Paused: |
| 259 | return QColor(Qt::GlobalColor::lightGray); | 281 | return QColor(Qt::GlobalColor::lightGray); |
| 260 | case Kernel::ThreadStatus::WaitHLEEvent: | 282 | case Kernel::ThreadStatus::WaitHLEEvent: |
| @@ -319,7 +341,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 319 | 341 | ||
| 320 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { | 342 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { |
| 321 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), | 343 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), |
| 322 | thread.IsSleepingOnWait())); | 344 | thread.IsWaitingSync())); |
| 323 | } | 345 | } |
| 324 | 346 | ||
| 325 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); | 347 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 270cccc77..4d501a8f9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | #include "applets/software_keyboard.h" | 16 | #include "applets/software_keyboard.h" |
| 17 | #include "applets/web_browser.h" | 17 | #include "applets/web_browser.h" |
| 18 | #include "configuration/configure_input.h" | 18 | #include "configuration/configure_input.h" |
| 19 | #include "configuration/configure_per_general.h" | 19 | #include "configuration/configure_per_game.h" |
| 20 | #include "core/file_sys/vfs.h" | 20 | #include "core/file_sys/vfs.h" |
| 21 | #include "core/file_sys/vfs_real.h" | 21 | #include "core/file_sys/vfs_real.h" |
| 22 | #include "core/frontend/applets/general_frontend.h" | 22 | #include "core/frontend/applets/general_frontend.h" |
| @@ -56,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 56 | #include <QShortcut> | 56 | #include <QShortcut> |
| 57 | #include <QStatusBar> | 57 | #include <QStatusBar> |
| 58 | #include <QSysInfo> | 58 | #include <QSysInfo> |
| 59 | #include <QUrl> | ||
| 59 | #include <QtConcurrent/QtConcurrent> | 60 | #include <QtConcurrent/QtConcurrent> |
| 60 | 61 | ||
| 61 | #include <fmt/format.h> | 62 | #include <fmt/format.h> |
| @@ -217,7 +218,20 @@ GMainWindow::GMainWindow() | |||
| 217 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, | 218 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, |
| 218 | Common::g_scm_desc); | 219 | Common::g_scm_desc); |
| 219 | #ifdef ARCHITECTURE_x86_64 | 220 | #ifdef ARCHITECTURE_x86_64 |
| 220 | LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); | 221 | const auto& caps = Common::GetCPUCaps(); |
| 222 | std::string cpu_string = caps.cpu_string; | ||
| 223 | if (caps.avx || caps.avx2 || caps.avx512) { | ||
| 224 | cpu_string += " | AVX"; | ||
| 225 | if (caps.avx512) { | ||
| 226 | cpu_string += "512"; | ||
| 227 | } else if (caps.avx2) { | ||
| 228 | cpu_string += '2'; | ||
| 229 | } | ||
| 230 | if (caps.fma || caps.fma4) { | ||
| 231 | cpu_string += " | FMA"; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | LOG_INFO(Frontend, "Host CPU: {}", cpu_string); | ||
| 221 | #endif | 235 | #endif |
| 222 | LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); | 236 | LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); |
| 223 | LOG_INFO(Frontend, "Host RAM: {:.2f} GB", | 237 | LOG_INFO(Frontend, "Host RAM: {:.2f} GB", |
| @@ -520,14 +534,36 @@ void GMainWindow::InitializeWidgets() { | |||
| 520 | if (emulation_running) { | 534 | if (emulation_running) { |
| 521 | return; | 535 | return; |
| 522 | } | 536 | } |
| 523 | Settings::values.use_asynchronous_gpu_emulation = | 537 | bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() || |
| 524 | !Settings::values.use_asynchronous_gpu_emulation; | 538 | Settings::values.use_multi_core.GetValue(); |
| 525 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 539 | Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); |
| 540 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); | ||
| 526 | Settings::Apply(); | 541 | Settings::Apply(); |
| 527 | }); | 542 | }); |
| 528 | async_status_button->setText(tr("ASYNC")); | 543 | async_status_button->setText(tr("ASYNC")); |
| 529 | async_status_button->setCheckable(true); | 544 | async_status_button->setCheckable(true); |
| 530 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 545 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 546 | |||
| 547 | // Setup Multicore button | ||
| 548 | multicore_status_button = new QPushButton(); | ||
| 549 | multicore_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||
| 550 | multicore_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 551 | connect(multicore_status_button, &QPushButton::clicked, [&] { | ||
| 552 | if (emulation_running) { | ||
| 553 | return; | ||
| 554 | } | ||
| 555 | Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); | ||
| 556 | bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() || | ||
| 557 | Settings::values.use_multi_core.GetValue(); | ||
| 558 | Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); | ||
| 559 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); | ||
| 560 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); | ||
| 561 | Settings::Apply(); | ||
| 562 | }); | ||
| 563 | multicore_status_button->setText(tr("MULTICORE")); | ||
| 564 | multicore_status_button->setCheckable(true); | ||
| 565 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); | ||
| 566 | statusBar()->insertPermanentWidget(0, multicore_status_button); | ||
| 531 | statusBar()->insertPermanentWidget(0, async_status_button); | 567 | statusBar()->insertPermanentWidget(0, async_status_button); |
| 532 | 568 | ||
| 533 | // Setup Renderer API button | 569 | // Setup Renderer API button |
| @@ -545,16 +581,16 @@ void GMainWindow::InitializeWidgets() { | |||
| 545 | renderer_status_button->setCheckable(false); | 581 | renderer_status_button->setCheckable(false); |
| 546 | renderer_status_button->setDisabled(true); | 582 | renderer_status_button->setDisabled(true); |
| 547 | #else | 583 | #else |
| 548 | renderer_status_button->setChecked(Settings::values.renderer_backend == | 584 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == |
| 549 | Settings::RendererBackend::Vulkan); | 585 | Settings::RendererBackend::Vulkan); |
| 550 | connect(renderer_status_button, &QPushButton::clicked, [=] { | 586 | connect(renderer_status_button, &QPushButton::clicked, [=] { |
| 551 | if (emulation_running) { | 587 | if (emulation_running) { |
| 552 | return; | 588 | return; |
| 553 | } | 589 | } |
| 554 | if (renderer_status_button->isChecked()) { | 590 | if (renderer_status_button->isChecked()) { |
| 555 | Settings::values.renderer_backend = Settings::RendererBackend::Vulkan; | 591 | Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan); |
| 556 | } else { | 592 | } else { |
| 557 | Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; | 593 | Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL); |
| 558 | } | 594 | } |
| 559 | 595 | ||
| 560 | Settings::Apply(); | 596 | Settings::Apply(); |
| @@ -653,6 +689,11 @@ void GMainWindow::InitializeHotkeys() { | |||
| 653 | ui.action_Capture_Screenshot->setShortcutContext( | 689 | ui.action_Capture_Screenshot->setShortcutContext( |
| 654 | hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); | 690 | hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); |
| 655 | 691 | ||
| 692 | ui.action_Fullscreen->setShortcut( | ||
| 693 | hotkey_registry.GetHotkey(main_window, fullscreen, this)->key()); | ||
| 694 | ui.action_Fullscreen->setShortcutContext( | ||
| 695 | hotkey_registry.GetShortcutContext(main_window, fullscreen)); | ||
| 696 | |||
| 656 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), | 697 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), |
| 657 | &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); | 698 | &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); |
| 658 | connect( | 699 | connect( |
| @@ -686,24 +727,24 @@ void GMainWindow::InitializeHotkeys() { | |||
| 686 | }); | 727 | }); |
| 687 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), | 728 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), |
| 688 | &QShortcut::activated, this, [&] { | 729 | &QShortcut::activated, this, [&] { |
| 689 | Settings::values.use_frame_limit = !Settings::values.use_frame_limit; | 730 | Settings::values.use_frame_limit.SetValue( |
| 731 | !Settings::values.use_frame_limit.GetValue()); | ||
| 690 | UpdateStatusBar(); | 732 | UpdateStatusBar(); |
| 691 | }); | 733 | }); |
| 692 | // TODO: Remove this comment/static whenever the next major release of | 734 | constexpr u16 SPEED_LIMIT_STEP = 5; |
| 693 | // MSVC occurs and we make it a requirement (see: | ||
| 694 | // https://developercommunity.visualstudio.com/content/problem/93922/constexprs-are-trying-to-be-captured-in-lambda-fun.html) | ||
| 695 | static constexpr u16 SPEED_LIMIT_STEP = 5; | ||
| 696 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), | 735 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), |
| 697 | &QShortcut::activated, this, [&] { | 736 | &QShortcut::activated, this, [&] { |
| 698 | if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) { | 737 | if (Settings::values.frame_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) { |
| 699 | Settings::values.frame_limit += SPEED_LIMIT_STEP; | 738 | Settings::values.frame_limit.SetValue(SPEED_LIMIT_STEP + |
| 739 | Settings::values.frame_limit.GetValue()); | ||
| 700 | UpdateStatusBar(); | 740 | UpdateStatusBar(); |
| 701 | } | 741 | } |
| 702 | }); | 742 | }); |
| 703 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), | 743 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), |
| 704 | &QShortcut::activated, this, [&] { | 744 | &QShortcut::activated, this, [&] { |
| 705 | if (Settings::values.frame_limit > SPEED_LIMIT_STEP) { | 745 | if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) { |
| 706 | Settings::values.frame_limit -= SPEED_LIMIT_STEP; | 746 | Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() - |
| 747 | SPEED_LIMIT_STEP); | ||
| 707 | UpdateStatusBar(); | 748 | UpdateStatusBar(); |
| 708 | } | 749 | } |
| 709 | }); | 750 | }); |
| @@ -715,7 +756,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 715 | }); | 756 | }); |
| 716 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), | 757 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), |
| 717 | &QShortcut::activated, this, [&] { | 758 | &QShortcut::activated, this, [&] { |
| 718 | if (emu_thread->IsRunning()) { | 759 | if (emu_thread != nullptr && emu_thread->IsRunning()) { |
| 719 | OnCaptureScreenshot(); | 760 | OnCaptureScreenshot(); |
| 720 | } | 761 | } |
| 721 | }); | 762 | }); |
| @@ -726,6 +767,9 @@ void GMainWindow::InitializeHotkeys() { | |||
| 726 | Settings::values.use_docked_mode); | 767 | Settings::values.use_docked_mode); |
| 727 | dock_status_button->setChecked(Settings::values.use_docked_mode); | 768 | dock_status_button->setChecked(Settings::values.use_docked_mode); |
| 728 | }); | 769 | }); |
| 770 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), | ||
| 771 | &QShortcut::activated, this, | ||
| 772 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); | ||
| 729 | } | 773 | } |
| 730 | 774 | ||
| 731 | void GMainWindow::SetDefaultUIGeometry() { | 775 | void GMainWindow::SetDefaultUIGeometry() { |
| @@ -826,6 +870,10 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 826 | connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); | 870 | connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); |
| 827 | connect(ui.action_Report_Compatibility, &QAction::triggered, this, | 871 | connect(ui.action_Report_Compatibility, &QAction::triggered, this, |
| 828 | &GMainWindow::OnMenuReportCompatibility); | 872 | &GMainWindow::OnMenuReportCompatibility); |
| 873 | connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); | ||
| 874 | connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, | ||
| 875 | &GMainWindow::OnOpenQuickstartGuide); | ||
| 876 | connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); | ||
| 829 | connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); | 877 | connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); |
| 830 | connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); | 878 | connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); |
| 831 | 879 | ||
| @@ -839,10 +887,6 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 839 | connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize); | 887 | connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize); |
| 840 | 888 | ||
| 841 | // Fullscreen | 889 | // Fullscreen |
| 842 | ui.action_Fullscreen->setShortcut( | ||
| 843 | hotkey_registry | ||
| 844 | .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this) | ||
| 845 | ->key()); | ||
| 846 | connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); | 890 | connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); |
| 847 | 891 | ||
| 848 | // Movie | 892 | // Movie |
| @@ -910,6 +954,8 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 910 | nullptr, // E-Commerce | 954 | nullptr, // E-Commerce |
| 911 | }); | 955 | }); |
| 912 | 956 | ||
| 957 | system.RegisterHostThread(); | ||
| 958 | |||
| 913 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; | 959 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; |
| 914 | 960 | ||
| 915 | const auto drd_callout = | 961 | const auto drd_callout = |
| @@ -996,6 +1042,17 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 996 | LOG_INFO(Frontend, "yuzu starting..."); | 1042 | LOG_INFO(Frontend, "yuzu starting..."); |
| 997 | StoreRecentFile(filename); // Put the filename on top of the list | 1043 | StoreRecentFile(filename); // Put the filename on top of the list |
| 998 | 1044 | ||
| 1045 | u64 title_id{0}; | ||
| 1046 | |||
| 1047 | const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); | ||
| 1048 | const auto loader = Loader::GetLoader(v_file); | ||
| 1049 | if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { | ||
| 1050 | // Load per game settings | ||
| 1051 | Config per_game_config(fmt::format("{:016X}.ini", title_id), false); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | Settings::LogSettings(); | ||
| 1055 | |||
| 999 | if (UISettings::values.select_user_on_boot) { | 1056 | if (UISettings::values.select_user_on_boot) { |
| 1000 | SelectAndSetCurrentUser(); | 1057 | SelectAndSetCurrentUser(); |
| 1001 | } | 1058 | } |
| @@ -1020,12 +1077,14 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 1020 | &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); | 1077 | &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); |
| 1021 | 1078 | ||
| 1022 | // Update the GUI | 1079 | // Update the GUI |
| 1080 | UpdateStatusButtons(); | ||
| 1023 | if (ui.action_Single_Window_Mode->isChecked()) { | 1081 | if (ui.action_Single_Window_Mode->isChecked()) { |
| 1024 | game_list->hide(); | 1082 | game_list->hide(); |
| 1025 | game_list_placeholder->hide(); | 1083 | game_list_placeholder->hide(); |
| 1026 | } | 1084 | } |
| 1027 | status_bar_update_timer.start(2000); | 1085 | status_bar_update_timer.start(2000); |
| 1028 | async_status_button->setDisabled(true); | 1086 | async_status_button->setDisabled(true); |
| 1087 | multicore_status_button->setDisabled(true); | ||
| 1029 | renderer_status_button->setDisabled(true); | 1088 | renderer_status_button->setDisabled(true); |
| 1030 | 1089 | ||
| 1031 | if (UISettings::values.hide_mouse) { | 1090 | if (UISettings::values.hide_mouse) { |
| @@ -1034,20 +1093,20 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 1034 | ui.centralwidget->setMouseTracking(true); | 1093 | ui.centralwidget->setMouseTracking(true); |
| 1035 | } | 1094 | } |
| 1036 | 1095 | ||
| 1037 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | ||
| 1038 | |||
| 1039 | std::string title_name; | 1096 | std::string title_name; |
| 1097 | std::string title_version; | ||
| 1040 | const auto res = Core::System::GetInstance().GetGameName(title_name); | 1098 | const auto res = Core::System::GetInstance().GetGameName(title_name); |
| 1041 | if (res != Loader::ResultStatus::Success) { | ||
| 1042 | const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); | ||
| 1043 | if (metadata.first != nullptr) | ||
| 1044 | title_name = metadata.first->GetApplicationName(); | ||
| 1045 | 1099 | ||
| 1046 | if (title_name.empty()) | 1100 | const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); |
| 1047 | title_name = FileUtil::GetFilename(filename.toStdString()); | 1101 | if (metadata.first != nullptr) { |
| 1102 | title_version = metadata.first->GetVersionString(); | ||
| 1103 | title_name = metadata.first->GetApplicationName(); | ||
| 1104 | } | ||
| 1105 | if (res != Loader::ResultStatus::Success || title_name.empty()) { | ||
| 1106 | title_name = FileUtil::GetFilename(filename.toStdString()); | ||
| 1048 | } | 1107 | } |
| 1049 | LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name); | 1108 | LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); |
| 1050 | UpdateWindowTitle(QString::fromStdString(title_name)); | 1109 | UpdateWindowTitle(title_name, title_version); |
| 1051 | 1110 | ||
| 1052 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); | 1111 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); |
| 1053 | loading_screen->show(); | 1112 | loading_screen->show(); |
| @@ -1113,6 +1172,7 @@ void GMainWindow::ShutdownGame() { | |||
| 1113 | game_fps_label->setVisible(false); | 1172 | game_fps_label->setVisible(false); |
| 1114 | emu_frametime_label->setVisible(false); | 1173 | emu_frametime_label->setVisible(false); |
| 1115 | async_status_button->setEnabled(true); | 1174 | async_status_button->setEnabled(true); |
| 1175 | multicore_status_button->setEnabled(true); | ||
| 1116 | #ifdef HAS_VULKAN | 1176 | #ifdef HAS_VULKAN |
| 1117 | renderer_status_button->setEnabled(true); | 1177 | renderer_status_button->setEnabled(true); |
| 1118 | #endif | 1178 | #endif |
| @@ -1474,7 +1534,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | |||
| 1474 | return; | 1534 | return; |
| 1475 | } | 1535 | } |
| 1476 | 1536 | ||
| 1477 | ConfigurePerGameGeneral dialog(this, title_id); | 1537 | ConfigurePerGame dialog(this, title_id); |
| 1478 | dialog.LoadFromFile(v_file); | 1538 | dialog.LoadFromFile(v_file); |
| 1479 | auto result = dialog.exec(); | 1539 | auto result = dialog.exec(); |
| 1480 | if (result == QDialog::Accepted) { | 1540 | if (result == QDialog::Accepted) { |
| @@ -1485,7 +1545,14 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | |||
| 1485 | game_list->PopulateAsync(UISettings::values.game_dirs); | 1545 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 1486 | } | 1546 | } |
| 1487 | 1547 | ||
| 1488 | config->Save(); | 1548 | // Do not cause the global config to write local settings into the config file |
| 1549 | Settings::RestoreGlobalState(); | ||
| 1550 | |||
| 1551 | if (!Core::System::GetInstance().IsPoweredOn()) { | ||
| 1552 | config->Save(); | ||
| 1553 | } | ||
| 1554 | } else { | ||
| 1555 | Settings::RestoreGlobalState(); | ||
| 1489 | } | 1556 | } |
| 1490 | } | 1557 | } |
| 1491 | 1558 | ||
| @@ -1772,6 +1839,9 @@ void GMainWindow::OnStopGame() { | |||
| 1772 | } | 1839 | } |
| 1773 | 1840 | ||
| 1774 | ShutdownGame(); | 1841 | ShutdownGame(); |
| 1842 | |||
| 1843 | Settings::RestoreGlobalState(); | ||
| 1844 | UpdateStatusButtons(); | ||
| 1775 | } | 1845 | } |
| 1776 | 1846 | ||
| 1777 | void GMainWindow::OnLoadComplete() { | 1847 | void GMainWindow::OnLoadComplete() { |
| @@ -1797,6 +1867,26 @@ void GMainWindow::OnMenuReportCompatibility() { | |||
| 1797 | } | 1867 | } |
| 1798 | } | 1868 | } |
| 1799 | 1869 | ||
| 1870 | void GMainWindow::OpenURL(const QUrl& url) { | ||
| 1871 | const bool open = QDesktopServices::openUrl(url); | ||
| 1872 | if (!open) { | ||
| 1873 | QMessageBox::warning(this, tr("Error opening URL"), | ||
| 1874 | tr("Unable to open the URL \"%1\".").arg(url.toString())); | ||
| 1875 | } | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | void GMainWindow::OnOpenModsPage() { | ||
| 1879 | OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"))); | ||
| 1880 | } | ||
| 1881 | |||
| 1882 | void GMainWindow::OnOpenQuickstartGuide() { | ||
| 1883 | OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/"))); | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | void GMainWindow::OnOpenFAQ() { | ||
| 1887 | OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/"))); | ||
| 1888 | } | ||
| 1889 | |||
| 1800 | void GMainWindow::ToggleFullscreen() { | 1890 | void GMainWindow::ToggleFullscreen() { |
| 1801 | if (!emulation_running) { | 1891 | if (!emulation_running) { |
| 1802 | return; | 1892 | return; |
| @@ -1859,7 +1949,7 @@ void GMainWindow::ToggleWindowMode() { | |||
| 1859 | 1949 | ||
| 1860 | void GMainWindow::ResetWindowSize() { | 1950 | void GMainWindow::ResetWindowSize() { |
| 1861 | const auto aspect_ratio = Layout::EmulationAspectRatio( | 1951 | const auto aspect_ratio = Layout::EmulationAspectRatio( |
| 1862 | static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio), | 1952 | static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio.GetValue()), |
| 1863 | static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width); | 1953 | static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width); |
| 1864 | if (!ui.action_Single_Window_Mode->isChecked()) { | 1954 | if (!ui.action_Single_Window_Mode->isChecked()) { |
| 1865 | render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio, | 1955 | render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio, |
| @@ -1907,12 +1997,7 @@ void GMainWindow::OnConfigure() { | |||
| 1907 | ui.centralwidget->setMouseTracking(false); | 1997 | ui.centralwidget->setMouseTracking(false); |
| 1908 | } | 1998 | } |
| 1909 | 1999 | ||
| 1910 | dock_status_button->setChecked(Settings::values.use_docked_mode); | 2000 | UpdateStatusButtons(); |
| 1911 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 1912 | #ifdef HAS_VULKAN | ||
| 1913 | renderer_status_button->setChecked(Settings::values.renderer_backend == | ||
| 1914 | Settings::RendererBackend::Vulkan); | ||
| 1915 | #endif | ||
| 1916 | } | 2001 | } |
| 1917 | 2002 | ||
| 1918 | void GMainWindow::OnLoadAmiibo() { | 2003 | void GMainWindow::OnLoadAmiibo() { |
| @@ -1995,7 +2080,8 @@ void GMainWindow::OnCaptureScreenshot() { | |||
| 1995 | OnStartGame(); | 2080 | OnStartGame(); |
| 1996 | } | 2081 | } |
| 1997 | 2082 | ||
| 1998 | void GMainWindow::UpdateWindowTitle(const QString& title_name) { | 2083 | void GMainWindow::UpdateWindowTitle(const std::string& title_name, |
| 2084 | const std::string& title_version) { | ||
| 1999 | const auto full_name = std::string(Common::g_build_fullname); | 2085 | const auto full_name = std::string(Common::g_build_fullname); |
| 2000 | const auto branch_name = std::string(Common::g_scm_branch); | 2086 | const auto branch_name = std::string(Common::g_scm_branch); |
| 2001 | const auto description = std::string(Common::g_scm_desc); | 2087 | const auto description = std::string(Common::g_scm_desc); |
| @@ -2004,7 +2090,7 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) { | |||
| 2004 | const auto date = | 2090 | const auto date = |
| 2005 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); | 2091 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); |
| 2006 | 2092 | ||
| 2007 | if (title_name.isEmpty()) { | 2093 | if (title_name.empty()) { |
| 2008 | const auto fmt = std::string(Common::g_title_bar_format_idle); | 2094 | const auto fmt = std::string(Common::g_title_bar_format_idle); |
| 2009 | setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, | 2095 | setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, |
| 2010 | full_name, branch_name, description, | 2096 | full_name, branch_name, description, |
| @@ -2012,8 +2098,8 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) { | |||
| 2012 | } else { | 2098 | } else { |
| 2013 | const auto fmt = std::string(Common::g_title_bar_format_running); | 2099 | const auto fmt = std::string(Common::g_title_bar_format_running); |
| 2014 | setWindowTitle(QString::fromStdString( | 2100 | setWindowTitle(QString::fromStdString( |
| 2015 | fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name, | 2101 | fmt::format(fmt.empty() ? "yuzu {0}| {3} | {6} | {1}-{2}" : fmt, full_name, branch_name, |
| 2016 | description, title_name.toStdString(), date, build_id))); | 2102 | description, title_name, date, build_id, title_version))); |
| 2017 | } | 2103 | } |
| 2018 | } | 2104 | } |
| 2019 | 2105 | ||
| @@ -2025,21 +2111,34 @@ void GMainWindow::UpdateStatusBar() { | |||
| 2025 | 2111 | ||
| 2026 | auto results = Core::System::GetInstance().GetAndResetPerfStats(); | 2112 | auto results = Core::System::GetInstance().GetAndResetPerfStats(); |
| 2027 | 2113 | ||
| 2028 | if (Settings::values.use_frame_limit) { | 2114 | if (Settings::values.use_frame_limit.GetValue()) { |
| 2029 | emu_speed_label->setText(tr("Speed: %1% / %2%") | 2115 | emu_speed_label->setText(tr("Speed: %1% / %2%") |
| 2030 | .arg(results.emulation_speed * 100.0, 0, 'f', 0) | 2116 | .arg(results.emulation_speed * 100.0, 0, 'f', 0) |
| 2031 | .arg(Settings::values.frame_limit)); | 2117 | .arg(Settings::values.frame_limit.GetValue())); |
| 2032 | } else { | 2118 | } else { |
| 2033 | emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); | 2119 | emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); |
| 2034 | } | 2120 | } |
| 2035 | game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); | 2121 | game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); |
| 2036 | emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); | 2122 | emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); |
| 2037 | 2123 | ||
| 2038 | emu_speed_label->setVisible(true); | 2124 | emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); |
| 2039 | game_fps_label->setVisible(true); | 2125 | game_fps_label->setVisible(true); |
| 2040 | emu_frametime_label->setVisible(true); | 2126 | emu_frametime_label->setVisible(true); |
| 2041 | } | 2127 | } |
| 2042 | 2128 | ||
| 2129 | void GMainWindow::UpdateStatusButtons() { | ||
| 2130 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 2131 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); | ||
| 2132 | Settings::values.use_asynchronous_gpu_emulation.SetValue( | ||
| 2133 | Settings::values.use_asynchronous_gpu_emulation.GetValue() || | ||
| 2134 | Settings::values.use_multi_core.GetValue()); | ||
| 2135 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); | ||
| 2136 | #ifdef HAS_VULKAN | ||
| 2137 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == | ||
| 2138 | Settings::RendererBackend::Vulkan); | ||
| 2139 | #endif | ||
| 2140 | } | ||
| 2141 | |||
| 2043 | void GMainWindow::HideMouseCursor() { | 2142 | void GMainWindow::HideMouseCursor() { |
| 2044 | if (emu_thread == nullptr || UISettings::values.hide_mouse == false) { | 2143 | if (emu_thread == nullptr || UISettings::values.hide_mouse == false) { |
| 2045 | mouse_hide_timer.stop(); | 2144 | mouse_hide_timer.stop(); |
| @@ -2123,6 +2222,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 2123 | if (answer == QMessageBox::Yes) { | 2222 | if (answer == QMessageBox::Yes) { |
| 2124 | if (emu_thread) { | 2223 | if (emu_thread) { |
| 2125 | ShutdownGame(); | 2224 | ShutdownGame(); |
| 2225 | |||
| 2226 | Settings::RestoreGlobalState(); | ||
| 2227 | UpdateStatusButtons(); | ||
| 2126 | } | 2228 | } |
| 2127 | } else { | 2229 | } else { |
| 2128 | // Only show the message if the game is still running. | 2230 | // Only show the message if the game is still running. |
| @@ -2154,7 +2256,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 2154 | "title.keys_autogenerated"); | 2256 | "title.keys_autogenerated"); |
| 2155 | } | 2257 | } |
| 2156 | 2258 | ||
| 2157 | Core::Crypto::KeyManager keys{}; | 2259 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 2158 | if (keys.BaseDeriveNecessary()) { | 2260 | if (keys.BaseDeriveNecessary()) { |
| 2159 | Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( | 2261 | Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( |
| 2160 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; | 2262 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; |
| @@ -2285,9 +2387,13 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 2285 | hotkey_registry.SaveHotkeys(); | 2387 | hotkey_registry.SaveHotkeys(); |
| 2286 | 2388 | ||
| 2287 | // Shutdown session if the emu thread is active... | 2389 | // Shutdown session if the emu thread is active... |
| 2288 | if (emu_thread != nullptr) | 2390 | if (emu_thread != nullptr) { |
| 2289 | ShutdownGame(); | 2391 | ShutdownGame(); |
| 2290 | 2392 | ||
| 2393 | Settings::RestoreGlobalState(); | ||
| 2394 | UpdateStatusButtons(); | ||
| 2395 | } | ||
| 2396 | |||
| 2291 | render_window->close(); | 2397 | render_window->close(); |
| 2292 | 2398 | ||
| 2293 | QWidget::closeEvent(event); | 2399 | QWidget::closeEvent(event); |
| @@ -2467,8 +2573,6 @@ int main(int argc, char* argv[]) { | |||
| 2467 | QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, | 2573 | QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, |
| 2468 | &GMainWindow::OnAppFocusStateChanged); | 2574 | &GMainWindow::OnAppFocusStateChanged); |
| 2469 | 2575 | ||
| 2470 | Settings::LogSettings(); | ||
| 2471 | |||
| 2472 | int result = app.exec(); | 2576 | int result = app.exec(); |
| 2473 | detached_tasks.WaitForAllTasks(); | 2577 | detached_tasks.WaitForAllTasks(); |
| 2474 | return result; | 2578 | return result; |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 4f4c8ddbe..8e3d39c38 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -181,6 +181,9 @@ private slots: | |||
| 181 | void OnPauseGame(); | 181 | void OnPauseGame(); |
| 182 | void OnStopGame(); | 182 | void OnStopGame(); |
| 183 | void OnMenuReportCompatibility(); | 183 | void OnMenuReportCompatibility(); |
| 184 | void OnOpenModsPage(); | ||
| 185 | void OnOpenQuickstartGuide(); | ||
| 186 | void OnOpenFAQ(); | ||
| 184 | /// Called whenever a user selects a game in the game list widget. | 187 | /// Called whenever a user selects a game in the game list widget. |
| 185 | void OnGameListLoadFile(QString game_path); | 188 | void OnGameListLoadFile(QString game_path); |
| 186 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); | 189 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); |
| @@ -215,10 +218,13 @@ private slots: | |||
| 215 | 218 | ||
| 216 | private: | 219 | private: |
| 217 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 220 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| 218 | void UpdateWindowTitle(const QString& title_name = {}); | 221 | void UpdateWindowTitle(const std::string& title_name = {}, |
| 222 | const std::string& title_version = {}); | ||
| 219 | void UpdateStatusBar(); | 223 | void UpdateStatusBar(); |
| 224 | void UpdateStatusButtons(); | ||
| 220 | void HideMouseCursor(); | 225 | void HideMouseCursor(); |
| 221 | void ShowMouseCursor(); | 226 | void ShowMouseCursor(); |
| 227 | void OpenURL(const QUrl& url); | ||
| 222 | 228 | ||
| 223 | Ui::MainWindow ui; | 229 | Ui::MainWindow ui; |
| 224 | 230 | ||
| @@ -234,6 +240,7 @@ private: | |||
| 234 | QLabel* game_fps_label = nullptr; | 240 | QLabel* game_fps_label = nullptr; |
| 235 | QLabel* emu_frametime_label = nullptr; | 241 | QLabel* emu_frametime_label = nullptr; |
| 236 | QPushButton* async_status_button = nullptr; | 242 | QPushButton* async_status_button = nullptr; |
| 243 | QPushButton* multicore_status_button = nullptr; | ||
| 237 | QPushButton* renderer_status_button = nullptr; | 244 | QPushButton* renderer_status_button = nullptr; |
| 238 | QPushButton* dock_status_button = nullptr; | 245 | QPushButton* dock_status_button = nullptr; |
| 239 | QTimer status_bar_update_timer; | 246 | QTimer status_bar_update_timer; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 97c90f50b..bee6e107e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -113,6 +113,9 @@ | |||
| 113 | <string>&Help</string> | 113 | <string>&Help</string> |
| 114 | </property> | 114 | </property> |
| 115 | <addaction name="action_Report_Compatibility"/> | 115 | <addaction name="action_Report_Compatibility"/> |
| 116 | <addaction name="action_Open_Mods_Page"/> | ||
| 117 | <addaction name="action_Open_Quickstart_Guide"/> | ||
| 118 | <addaction name="action_Open_FAQ"/> | ||
| 116 | <addaction name="separator"/> | 119 | <addaction name="separator"/> |
| 117 | <addaction name="action_About"/> | 120 | <addaction name="action_About"/> |
| 118 | </widget> | 121 | </widget> |
| @@ -256,6 +259,21 @@ | |||
| 256 | <bool>false</bool> | 259 | <bool>false</bool> |
| 257 | </property> | 260 | </property> |
| 258 | </action> | 261 | </action> |
| 262 | <action name="action_Open_Mods_Page"> | ||
| 263 | <property name="text"> | ||
| 264 | <string>Open Mods Page</string> | ||
| 265 | </property> | ||
| 266 | </action> | ||
| 267 | <action name="action_Open_Quickstart_Guide"> | ||
| 268 | <property name="text"> | ||
| 269 | <string>Open Quickstart Guide</string> | ||
| 270 | </property> | ||
| 271 | </action> | ||
| 272 | <action name="action_Open_FAQ"> | ||
| 273 | <property name="text"> | ||
| 274 | <string>FAQ</string> | ||
| 275 | </property> | ||
| 276 | </action> | ||
| 259 | <action name="action_Open_yuzu_Folder"> | 277 | <action name="action_Open_yuzu_Folder"> |
| 260 | <property name="text"> | 278 | <property name="text"> |
| 261 | <string>Open yuzu Folder</string> | 279 | <string>Open yuzu Folder</string> |
diff --git a/src/yuzu/yuzu.rc b/src/yuzu/yuzu.rc index 1b253653f..4a3645a71 100644 --- a/src/yuzu/yuzu.rc +++ b/src/yuzu/yuzu.rc | |||
| @@ -16,4 +16,4 @@ IDI_ICON1 ICON "../../dist/yuzu.ico" | |||
| 16 | // RT_MANIFEST | 16 | // RT_MANIFEST |
| 17 | // | 17 | // |
| 18 | 18 | ||
| 19 | 1 RT_MANIFEST "../../dist/yuzu.manifest" | 19 | 0 RT_MANIFEST "../../dist/yuzu.manifest" |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index c20d48c42..23763144f 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -354,65 +354,72 @@ void Config::ReadValues() { | |||
| 354 | 354 | ||
| 355 | const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); | 355 | const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); |
| 356 | if (rng_seed_enabled) { | 356 | if (rng_seed_enabled) { |
| 357 | Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); | 357 | Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0)); |
| 358 | } else { | 358 | } else { |
| 359 | Settings::values.rng_seed = std::nullopt; | 359 | Settings::values.rng_seed.SetValue(std::nullopt); |
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); | 362 | const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); |
| 363 | if (custom_rtc_enabled) { | 363 | if (custom_rtc_enabled) { |
| 364 | Settings::values.custom_rtc = | 364 | Settings::values.custom_rtc.SetValue( |
| 365 | std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); | 365 | std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0))); |
| 366 | } else { | 366 | } else { |
| 367 | Settings::values.custom_rtc = std::nullopt; | 367 | Settings::values.custom_rtc.SetValue(std::nullopt); |
| 368 | } | 368 | } |
| 369 | 369 | ||
| 370 | Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); | 370 | Settings::values.language_index.SetValue( |
| 371 | Settings::values.time_zone_index = sdl2_config->GetInteger("System", "time_zone_index", 0); | 371 | sdl2_config->GetInteger("System", "language_index", 1)); |
| 372 | Settings::values.time_zone_index.SetValue( | ||
| 373 | sdl2_config->GetInteger("System", "time_zone_index", 0)); | ||
| 372 | 374 | ||
| 373 | // Core | 375 | // Core |
| 374 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); | 376 | Settings::values.use_multi_core.SetValue( |
| 377 | sdl2_config->GetBoolean("Core", "use_multi_core", false)); | ||
| 375 | 378 | ||
| 376 | // Renderer | 379 | // Renderer |
| 377 | const int renderer_backend = sdl2_config->GetInteger( | 380 | const int renderer_backend = sdl2_config->GetInteger( |
| 378 | "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); | 381 | "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); |
| 379 | Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend); | 382 | Settings::values.renderer_backend.SetValue( |
| 383 | static_cast<Settings::RendererBackend>(renderer_backend)); | ||
| 380 | Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); | 384 | Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); |
| 381 | Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0); | 385 | Settings::values.vulkan_device.SetValue( |
| 382 | 386 | sdl2_config->GetInteger("Renderer", "vulkan_device", 0)); | |
| 383 | Settings::values.resolution_factor = | 387 | |
| 384 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); | 388 | Settings::values.aspect_ratio.SetValue( |
| 385 | Settings::values.aspect_ratio = | 389 | static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0))); |
| 386 | static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); | 390 | Settings::values.max_anisotropy.SetValue( |
| 387 | Settings::values.max_anisotropy = | 391 | static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0))); |
| 388 | static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)); | 392 | Settings::values.use_frame_limit.SetValue( |
| 389 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | 393 | sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)); |
| 390 | Settings::values.frame_limit = | 394 | Settings::values.frame_limit.SetValue( |
| 391 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); | 395 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100))); |
| 392 | Settings::values.use_disk_shader_cache = | 396 | Settings::values.use_disk_shader_cache.SetValue( |
| 393 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | 397 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false)); |
| 394 | const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); | 398 | const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); |
| 395 | Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); | 399 | Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level)); |
| 396 | Settings::values.use_asynchronous_gpu_emulation = | 400 | Settings::values.use_asynchronous_gpu_emulation.SetValue( |
| 397 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | 401 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false)); |
| 398 | Settings::values.use_vsync = | 402 | Settings::values.use_vsync.SetValue( |
| 399 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); | 403 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1))); |
| 400 | Settings::values.use_assembly_shaders = | 404 | Settings::values.use_assembly_shaders.SetValue( |
| 401 | sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false); | 405 | sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false)); |
| 402 | Settings::values.use_fast_gpu_time = | 406 | Settings::values.use_fast_gpu_time.SetValue( |
| 403 | sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); | 407 | sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true)); |
| 404 | 408 | ||
| 405 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); | 409 | Settings::values.bg_red.SetValue( |
| 406 | Settings::values.bg_green = | 410 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0))); |
| 407 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); | 411 | Settings::values.bg_green.SetValue( |
| 408 | Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); | 412 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0))); |
| 413 | Settings::values.bg_blue.SetValue( | ||
| 414 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0))); | ||
| 409 | 415 | ||
| 410 | // Audio | 416 | // Audio |
| 411 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); | 417 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); |
| 412 | Settings::values.enable_audio_stretching = | 418 | Settings::values.enable_audio_stretching.SetValue( |
| 413 | sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); | 419 | sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true)); |
| 414 | Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); | 420 | Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); |
| 415 | Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)); | 421 | Settings::values.volume.SetValue( |
| 422 | static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1))); | ||
| 416 | 423 | ||
| 417 | // Miscellaneous | 424 | // Miscellaneous |
| 418 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | 425 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); |
| @@ -432,6 +439,8 @@ void Config::ReadValues() { | |||
| 432 | Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); | 439 | Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); |
| 433 | Settings::values.disable_cpu_opt = | 440 | Settings::values.disable_cpu_opt = |
| 434 | sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false); | 441 | sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false); |
| 442 | Settings::values.disable_macro_jit = | ||
| 443 | sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false); | ||
| 435 | 444 | ||
| 436 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | 445 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); |
| 437 | std::stringstream ss(title_list); | 446 | std::stringstream ss(title_list); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index abc6e6e65..45c07ed5d 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -117,11 +117,6 @@ use_hw_renderer = | |||
| 117 | # 0: Interpreter (slow), 1 (default): JIT (fast) | 117 | # 0: Interpreter (slow), 1 (default): JIT (fast) |
| 118 | use_shader_jit = | 118 | use_shader_jit = |
| 119 | 119 | ||
| 120 | # Resolution scale factor | ||
| 121 | # 0: Auto (scales resolution to window size), 1: Native Switch screen resolution, Otherwise a scale | ||
| 122 | # factor for the Switch resolution | ||
| 123 | resolution_factor = | ||
| 124 | |||
| 125 | # Aspect ratio | 120 | # Aspect ratio |
| 126 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window | 121 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window |
| 127 | aspect_ratio = | 122 | aspect_ratio = |
| @@ -291,6 +286,8 @@ quest_flag = | |||
| 291 | # Determines whether or not JIT CPU optimizations are enabled | 286 | # Determines whether or not JIT CPU optimizations are enabled |
| 292 | # false: Optimizations Enabled, true: Optimizations Disabled | 287 | # false: Optimizations Enabled, true: Optimizations Disabled |
| 293 | disable_cpu_opt = | 288 | disable_cpu_opt = |
| 289 | # Enables/Disables the macro JIT compiler | ||
| 290 | disable_macro_jit=false | ||
| 294 | 291 | ||
| 295 | [WebService] | 292 | [WebService] |
| 296 | # Whether or not to enable telemetry | 293 | # Whether or not to enable telemetry |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 411e7e647..e78025737 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -98,6 +98,9 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen) | |||
| 98 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 98 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
| 99 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | 99 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |
| 100 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | 100 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); |
| 101 | if (Settings::values.renderer_debug) { | ||
| 102 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); | ||
| 103 | } | ||
| 101 | SDL_GL_SetSwapInterval(0); | 104 | SDL_GL_SetSwapInterval(0); |
| 102 | 105 | ||
| 103 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | 106 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, |
| @@ -162,7 +165,7 @@ std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateShared | |||
| 162 | 165 | ||
| 163 | void EmuWindow_SDL2_GL::Present() { | 166 | void EmuWindow_SDL2_GL::Present() { |
| 164 | SDL_GL_MakeCurrent(render_window, window_context); | 167 | SDL_GL_MakeCurrent(render_window, window_context); |
| 165 | SDL_GL_SetSwapInterval(Settings::values.use_vsync ? 1 : 0); | 168 | SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0); |
| 166 | while (IsOpen()) { | 169 | while (IsOpen()) { |
| 167 | system.Renderer().TryPresent(100); | 170 | system.Renderer().TryPresent(100); |
| 168 | SDL_GL_SwapWindow(render_window); | 171 | SDL_GL_SwapWindow(render_window); |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 4d2ea7e9e..512b060a7 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 5 | #include <iostream> | 6 | #include <iostream> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <string> | 8 | #include <string> |
| @@ -180,7 +181,7 @@ int main(int argc, char** argv) { | |||
| 180 | Core::System& system{Core::System::GetInstance()}; | 181 | Core::System& system{Core::System::GetInstance()}; |
| 181 | 182 | ||
| 182 | std::unique_ptr<EmuWindow_SDL2> emu_window; | 183 | std::unique_ptr<EmuWindow_SDL2> emu_window; |
| 183 | switch (Settings::values.renderer_backend) { | 184 | switch (Settings::values.renderer_backend.GetValue()) { |
| 184 | case Settings::RendererBackend::OpenGL: | 185 | case Settings::RendererBackend::OpenGL: |
| 185 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); | 186 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); |
| 186 | break; | 187 | break; |
| @@ -236,9 +237,11 @@ int main(int argc, char** argv) { | |||
| 236 | system.Renderer().Rasterizer().LoadDiskResources(); | 237 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 237 | 238 | ||
| 238 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | 239 | std::thread render_thread([&emu_window] { emu_window->Present(); }); |
| 240 | system.Run(); | ||
| 239 | while (emu_window->IsOpen()) { | 241 | while (emu_window->IsOpen()) { |
| 240 | system.RunLoop(); | 242 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 241 | } | 243 | } |
| 244 | system.Pause(); | ||
| 242 | render_thread.join(); | 245 | render_thread.join(); |
| 243 | 246 | ||
| 244 | system.Shutdown(); | 247 | system.Shutdown(); |
diff --git a/src/yuzu_cmd/yuzu.rc b/src/yuzu_cmd/yuzu.rc index 7de8ef3d9..0cde75e2f 100644 --- a/src/yuzu_cmd/yuzu.rc +++ b/src/yuzu_cmd/yuzu.rc | |||
| @@ -14,4 +14,4 @@ YUZU_ICON ICON "../../dist/yuzu.ico" | |||
| 14 | // RT_MANIFEST | 14 | // RT_MANIFEST |
| 15 | // | 15 | // |
| 16 | 16 | ||
| 17 | 1 RT_MANIFEST "../../dist/yuzu.manifest" | 17 | 0 RT_MANIFEST "../../dist/yuzu.manifest" |
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 3be58b15d..acb22885e 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp | |||
| @@ -81,6 +81,9 @@ void Config::ReadValues() { | |||
| 81 | Settings::values.touchscreen.diameter_x = 15; | 81 | Settings::values.touchscreen.diameter_x = 15; |
| 82 | Settings::values.touchscreen.diameter_y = 15; | 82 | Settings::values.touchscreen.diameter_y = 15; |
| 83 | 83 | ||
| 84 | Settings::values.use_docked_mode = | ||
| 85 | sdl2_config->GetBoolean("Controls", "use_docked_mode", false); | ||
| 86 | |||
| 84 | // Data Storage | 87 | // Data Storage |
| 85 | Settings::values.use_virtual_sd = | 88 | Settings::values.use_virtual_sd = |
| 86 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); | 89 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); |
| @@ -92,59 +95,59 @@ void Config::ReadValues() { | |||
| 92 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | 95 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); |
| 93 | 96 | ||
| 94 | // System | 97 | // System |
| 95 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | ||
| 96 | |||
| 97 | Settings::values.current_user = std::clamp<int>( | 98 | Settings::values.current_user = std::clamp<int>( |
| 98 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); | 99 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); |
| 99 | 100 | ||
| 100 | const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); | 101 | const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); |
| 101 | if (rng_seed_enabled) { | 102 | if (rng_seed_enabled) { |
| 102 | Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); | 103 | Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0)); |
| 103 | } else { | 104 | } else { |
| 104 | Settings::values.rng_seed = std::nullopt; | 105 | Settings::values.rng_seed.SetValue(std::nullopt); |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); | 108 | const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); |
| 108 | if (custom_rtc_enabled) { | 109 | if (custom_rtc_enabled) { |
| 109 | Settings::values.custom_rtc = | 110 | Settings::values.custom_rtc.SetValue( |
| 110 | std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); | 111 | std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0))); |
| 111 | } else { | 112 | } else { |
| 112 | Settings::values.custom_rtc = std::nullopt; | 113 | Settings::values.custom_rtc.SetValue(std::nullopt); |
| 113 | } | 114 | } |
| 114 | 115 | ||
| 115 | // Core | 116 | // Core |
| 116 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); | 117 | Settings::values.use_multi_core.SetValue( |
| 118 | sdl2_config->GetBoolean("Core", "use_multi_core", false)); | ||
| 117 | 119 | ||
| 118 | // Renderer | 120 | // Renderer |
| 119 | Settings::values.resolution_factor = | 121 | Settings::values.aspect_ratio.SetValue( |
| 120 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); | 122 | static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0))); |
| 121 | Settings::values.aspect_ratio = | 123 | Settings::values.max_anisotropy.SetValue( |
| 122 | static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); | 124 | static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0))); |
| 123 | Settings::values.max_anisotropy = | 125 | Settings::values.use_frame_limit.SetValue(false); |
| 124 | static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)); | 126 | Settings::values.frame_limit.SetValue(100); |
| 125 | Settings::values.use_frame_limit = false; | 127 | Settings::values.use_disk_shader_cache.SetValue( |
| 126 | Settings::values.frame_limit = 100; | 128 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false)); |
| 127 | Settings::values.use_disk_shader_cache = | ||
| 128 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | ||
| 129 | const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); | 129 | const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); |
| 130 | Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); | 130 | Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level)); |
| 131 | Settings::values.use_asynchronous_gpu_emulation = | 131 | Settings::values.use_asynchronous_gpu_emulation.SetValue( |
| 132 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | 132 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false)); |
| 133 | Settings::values.use_fast_gpu_time = | 133 | Settings::values.use_fast_gpu_time.SetValue( |
| 134 | sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); | 134 | sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true)); |
| 135 | 135 | ||
| 136 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); | 136 | Settings::values.bg_red.SetValue( |
| 137 | Settings::values.bg_green = | 137 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0))); |
| 138 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); | 138 | Settings::values.bg_green.SetValue( |
| 139 | Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); | 139 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0))); |
| 140 | Settings::values.bg_blue.SetValue( | ||
| 141 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0))); | ||
| 140 | 142 | ||
| 141 | // Audio | 143 | // Audio |
| 142 | Settings::values.sink_id = "null"; | 144 | Settings::values.sink_id = "null"; |
| 143 | Settings::values.enable_audio_stretching = false; | 145 | Settings::values.enable_audio_stretching.SetValue(false); |
| 144 | Settings::values.audio_device_id = "auto"; | 146 | Settings::values.audio_device_id = "auto"; |
| 145 | Settings::values.volume = 0; | 147 | Settings::values.volume.SetValue(0); |
| 146 | 148 | ||
| 147 | Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); | 149 | Settings::values.language_index.SetValue( |
| 150 | sdl2_config->GetInteger("System", "language_index", 1)); | ||
| 148 | 151 | ||
| 149 | // Miscellaneous | 152 | // Miscellaneous |
| 150 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | 153 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); |
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h index ca203b64d..41bbbbf60 100644 --- a/src/yuzu_tester/default_ini.h +++ b/src/yuzu_tester/default_ini.h | |||
| @@ -21,11 +21,6 @@ use_hw_renderer = | |||
| 21 | # 0: Interpreter (slow), 1 (default): JIT (fast) | 21 | # 0: Interpreter (slow), 1 (default): JIT (fast) |
| 22 | use_shader_jit = | 22 | use_shader_jit = |
| 23 | 23 | ||
| 24 | # Resolution scale factor | ||
| 25 | # 0: Auto (scales resolution to window size), 1: Native Switch screen resolution, Otherwise a scale | ||
| 26 | # factor for the Switch resolution | ||
| 27 | resolution_factor = | ||
| 28 | |||
| 29 | # Aspect ratio | 24 | # Aspect ratio |
| 30 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window | 25 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window |
| 31 | aspect_ratio = | 26 | aspect_ratio = |
diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp index 85d3f436b..2d3f6e3a7 100644 --- a/src/yuzu_tester/service/yuzutest.cpp +++ b/src/yuzu_tester/service/yuzutest.cpp | |||
| @@ -53,7 +53,7 @@ private: | |||
| 53 | 53 | ||
| 54 | IPC::ResponseBuilder rb{ctx, 3}; | 54 | IPC::ResponseBuilder rb{ctx, 3}; |
| 55 | rb.Push(RESULT_SUCCESS); | 55 | rb.Push(RESULT_SUCCESS); |
| 56 | rb.Push<u32>(write_size); | 56 | rb.Push<u32>(static_cast<u32>(write_size)); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | void StartIndividual(Kernel::HLERequestContext& ctx) { | 59 | void StartIndividual(Kernel::HLERequestContext& ctx) { |
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 676e70ebd..083667baf 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 5 | #include <iostream> | 6 | #include <iostream> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <string> | 8 | #include <string> |
| @@ -255,9 +256,11 @@ int main(int argc, char** argv) { | |||
| 255 | system.GPU().Start(); | 256 | system.GPU().Start(); |
| 256 | system.Renderer().Rasterizer().LoadDiskResources(); | 257 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 257 | 258 | ||
| 259 | system.Run(); | ||
| 258 | while (!finished) { | 260 | while (!finished) { |
| 259 | system.RunLoop(); | 261 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 260 | } | 262 | } |
| 263 | system.Pause(); | ||
| 261 | 264 | ||
| 262 | detached_tasks.WaitForAllTasks(); | 265 | detached_tasks.WaitForAllTasks(); |
| 263 | return return_value; | 266 | return return_value; |
diff --git a/src/yuzu_tester/yuzu.rc b/src/yuzu_tester/yuzu.rc index 7de8ef3d9..0cde75e2f 100644 --- a/src/yuzu_tester/yuzu.rc +++ b/src/yuzu_tester/yuzu.rc | |||
| @@ -14,4 +14,4 @@ YUZU_ICON ICON "../../dist/yuzu.ico" | |||
| 14 | // RT_MANIFEST | 14 | // RT_MANIFEST |
| 15 | // | 15 | // |
| 16 | 16 | ||
| 17 | 1 RT_MANIFEST "../../dist/yuzu.manifest" | 17 | 0 RT_MANIFEST "../../dist/yuzu.manifest" |