diff options
Diffstat (limited to 'src')
128 files changed, 5306 insertions, 742 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index dfed8b51d..906c486fd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -15,11 +15,23 @@ if (DEFINED ENV{CI}) | |||
| 15 | set(BUILD_TAG $ENV{AZURE_REPO_TAG}) | 15 | set(BUILD_TAG $ENV{AZURE_REPO_TAG}) |
| 16 | endif() | 16 | endif() |
| 17 | endif() | 17 | endif() |
| 18 | if (DEFINED ENV{TITLEBARFORMATIDLE}) | ||
| 19 | set(TITLE_BAR_FORMAT_IDLE $ENV{TITLEBARFORMATIDLE}) | ||
| 20 | endif () | ||
| 21 | if (DEFINED ENV{TITLEBARFORMATRUNNING}) | ||
| 22 | set(TITLE_BAR_FORMAT_RUNNING $ENV{TITLEBARFORMATRUNNING}) | ||
| 23 | endif () | ||
| 24 | if (DEFINED ENV{DISPLAYVERSION}) | ||
| 25 | set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) | ||
| 26 | endif () | ||
| 18 | add_custom_command(OUTPUT scm_rev.cpp | 27 | add_custom_command(OUTPUT scm_rev.cpp |
| 19 | COMMAND ${CMAKE_COMMAND} | 28 | COMMAND ${CMAKE_COMMAND} |
| 20 | -DSRC_DIR="${CMAKE_SOURCE_DIR}" | 29 | -DSRC_DIR="${CMAKE_SOURCE_DIR}" |
| 21 | -DBUILD_REPOSITORY="${BUILD_REPOSITORY}" | 30 | -DBUILD_REPOSITORY="${BUILD_REPOSITORY}" |
| 31 | -DTITLE_BAR_FORMAT_IDLE="${TITLE_BAR_FORMAT_IDLE}" | ||
| 32 | -DTITLE_BAR_FORMAT_RUNNING="${TITLE_BAR_FORMAT_RUNNING}" | ||
| 22 | -DBUILD_TAG="${BUILD_TAG}" | 33 | -DBUILD_TAG="${BUILD_TAG}" |
| 34 | -DBUILD_ID="${DISPLAY_VERSION}" | ||
| 23 | -P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" | 35 | -P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" |
| 24 | DEPENDS | 36 | DEPENDS |
| 25 | # WARNING! It was too much work to try and make a common location for this list, | 37 | # WARNING! It was too much work to try and make a common location for this list, |
| @@ -60,9 +72,15 @@ add_custom_command(OUTPUT scm_rev.cpp | |||
| 60 | "${VIDEO_CORE}/shader/decode/video.cpp" | 72 | "${VIDEO_CORE}/shader/decode/video.cpp" |
| 61 | "${VIDEO_CORE}/shader/decode/warp.cpp" | 73 | "${VIDEO_CORE}/shader/decode/warp.cpp" |
| 62 | "${VIDEO_CORE}/shader/decode/xmad.cpp" | 74 | "${VIDEO_CORE}/shader/decode/xmad.cpp" |
| 75 | "${VIDEO_CORE}/shader/ast.cpp" | ||
| 76 | "${VIDEO_CORE}/shader/ast.h" | ||
| 63 | "${VIDEO_CORE}/shader/control_flow.cpp" | 77 | "${VIDEO_CORE}/shader/control_flow.cpp" |
| 64 | "${VIDEO_CORE}/shader/control_flow.h" | 78 | "${VIDEO_CORE}/shader/control_flow.h" |
| 79 | "${VIDEO_CORE}/shader/compiler_settings.cpp" | ||
| 80 | "${VIDEO_CORE}/shader/compiler_settings.h" | ||
| 65 | "${VIDEO_CORE}/shader/decode.cpp" | 81 | "${VIDEO_CORE}/shader/decode.cpp" |
| 82 | "${VIDEO_CORE}/shader/expr.cpp" | ||
| 83 | "${VIDEO_CORE}/shader/expr.h" | ||
| 66 | "${VIDEO_CORE}/shader/node.h" | 84 | "${VIDEO_CORE}/shader/node.h" |
| 67 | "${VIDEO_CORE}/shader/node_helper.cpp" | 85 | "${VIDEO_CORE}/shader/node_helper.cpp" |
| 68 | "${VIDEO_CORE}/shader/node_helper.h" | 86 | "${VIDEO_CORE}/shader/node_helper.h" |
diff --git a/src/common/alignment.h b/src/common/alignment.h index 88d5d3a65..cdd4833f8 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -51,7 +51,17 @@ public: | |||
| 51 | using reference = T&; | 51 | using reference = T&; |
| 52 | using const_reference = const T&; | 52 | using const_reference = const T&; |
| 53 | 53 | ||
| 54 | using propagate_on_container_copy_assignment = std::true_type; | ||
| 55 | using propagate_on_container_move_assignment = std::true_type; | ||
| 56 | using propagate_on_container_swap = std::true_type; | ||
| 57 | using is_always_equal = std::true_type; | ||
| 58 | |||
| 54 | public: | 59 | public: |
| 60 | constexpr AlignmentAllocator() noexcept = default; | ||
| 61 | |||
| 62 | template <typename T2> | ||
| 63 | constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} | ||
| 64 | |||
| 55 | pointer address(reference r) noexcept { | 65 | pointer address(reference r) noexcept { |
| 56 | return std::addressof(r); | 66 | return std::addressof(r); |
| 57 | } | 67 | } |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 2d9374783..41167f57a 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -713,7 +713,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { | |||
| 713 | case UserPath::RootDir: | 713 | case UserPath::RootDir: |
| 714 | user_path = paths[UserPath::RootDir] + DIR_SEP; | 714 | user_path = paths[UserPath::RootDir] + DIR_SEP; |
| 715 | break; | 715 | break; |
| 716 | |||
| 717 | case UserPath::UserDir: | 716 | case UserPath::UserDir: |
| 718 | user_path = paths[UserPath::RootDir] + DIR_SEP; | 717 | user_path = paths[UserPath::RootDir] + DIR_SEP; |
| 719 | paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP; | 718 | paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP; |
| @@ -721,6 +720,8 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { | |||
| 721 | paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP; | 720 | paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP; |
| 722 | paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP; | 721 | paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP; |
| 723 | break; | 722 | break; |
| 723 | default: | ||
| 724 | break; | ||
| 724 | } | 725 | } |
| 725 | } | 726 | } |
| 726 | 727 | ||
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index d69038f65..5f126f324 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in | |||
| @@ -11,6 +11,9 @@ | |||
| 11 | #define BUILD_DATE "@BUILD_DATE@" | 11 | #define BUILD_DATE "@BUILD_DATE@" |
| 12 | #define BUILD_FULLNAME "@BUILD_FULLNAME@" | 12 | #define BUILD_FULLNAME "@BUILD_FULLNAME@" |
| 13 | #define BUILD_VERSION "@BUILD_VERSION@" | 13 | #define BUILD_VERSION "@BUILD_VERSION@" |
| 14 | #define BUILD_ID "@BUILD_ID@" | ||
| 15 | #define TITLE_BAR_FORMAT_IDLE "@TITLE_BAR_FORMAT_IDLE@" | ||
| 16 | #define TITLE_BAR_FORMAT_RUNNING "@TITLE_BAR_FORMAT_RUNNING@" | ||
| 14 | #define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@" | 17 | #define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@" |
| 15 | 18 | ||
| 16 | namespace Common { | 19 | namespace Common { |
| @@ -22,6 +25,9 @@ const char g_build_name[] = BUILD_NAME; | |||
| 22 | const char g_build_date[] = BUILD_DATE; | 25 | const char g_build_date[] = BUILD_DATE; |
| 23 | const char g_build_fullname[] = BUILD_FULLNAME; | 26 | const char g_build_fullname[] = BUILD_FULLNAME; |
| 24 | const char g_build_version[] = BUILD_VERSION; | 27 | const char g_build_version[] = BUILD_VERSION; |
| 28 | const char g_build_id[] = BUILD_ID; | ||
| 29 | const char g_title_bar_format_idle[] = TITLE_BAR_FORMAT_IDLE; | ||
| 30 | const char g_title_bar_format_running[] = TITLE_BAR_FORMAT_RUNNING; | ||
| 25 | const char g_shader_cache_version[] = SHADER_CACHE_VERSION; | 31 | const char g_shader_cache_version[] = SHADER_CACHE_VERSION; |
| 26 | 32 | ||
| 27 | } // namespace | 33 | } // namespace |
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index 666bf0367..563015ec9 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h | |||
| @@ -13,6 +13,9 @@ extern const char g_build_name[]; | |||
| 13 | extern const char g_build_date[]; | 13 | extern const char g_build_date[]; |
| 14 | extern const char g_build_fullname[]; | 14 | extern const char g_build_fullname[]; |
| 15 | extern const char g_build_version[]; | 15 | extern const char g_build_version[]; |
| 16 | extern const char g_build_id[]; | ||
| 17 | extern const char g_title_bar_format_idle[]; | ||
| 18 | extern const char g_title_bar_format_running[]; | ||
| 16 | extern const char g_shader_cache_version[]; | 19 | extern const char g_shader_cache_version[]; |
| 17 | 20 | ||
| 18 | } // namespace Common | 21 | } // namespace Common |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a6b56c9c6..3b1d72cf9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -1,3 +1,9 @@ | |||
| 1 | if (YUZU_ENABLE_BOXCAT) | ||
| 2 | set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h) | ||
| 3 | else() | ||
| 4 | set(BCAT_BOXCAT_ADDITIONAL_SOURCES) | ||
| 5 | endif() | ||
| 6 | |||
| 1 | add_library(core STATIC | 7 | add_library(core STATIC |
| 2 | arm/arm_interface.h | 8 | arm/arm_interface.h |
| 3 | arm/arm_interface.cpp | 9 | arm/arm_interface.cpp |
| @@ -82,6 +88,8 @@ add_library(core STATIC | |||
| 82 | file_sys/vfs_concat.h | 88 | file_sys/vfs_concat.h |
| 83 | file_sys/vfs_layered.cpp | 89 | file_sys/vfs_layered.cpp |
| 84 | file_sys/vfs_layered.h | 90 | file_sys/vfs_layered.h |
| 91 | file_sys/vfs_libzip.cpp | ||
| 92 | file_sys/vfs_libzip.h | ||
| 85 | file_sys/vfs_offset.cpp | 93 | file_sys/vfs_offset.cpp |
| 86 | file_sys/vfs_offset.h | 94 | file_sys/vfs_offset.h |
| 87 | file_sys/vfs_real.cpp | 95 | file_sys/vfs_real.cpp |
| @@ -241,6 +249,9 @@ add_library(core STATIC | |||
| 241 | hle/service/audio/errors.h | 249 | hle/service/audio/errors.h |
| 242 | hle/service/audio/hwopus.cpp | 250 | hle/service/audio/hwopus.cpp |
| 243 | hle/service/audio/hwopus.h | 251 | hle/service/audio/hwopus.h |
| 252 | hle/service/bcat/backend/backend.cpp | ||
| 253 | hle/service/bcat/backend/backend.h | ||
| 254 | ${BCAT_BOXCAT_ADDITIONAL_SOURCES} | ||
| 244 | hle/service/bcat/bcat.cpp | 255 | hle/service/bcat/bcat.cpp |
| 245 | hle/service/bcat/bcat.h | 256 | hle/service/bcat/bcat.h |
| 246 | hle/service/bcat/module.cpp | 257 | hle/service/bcat/module.cpp |
| @@ -324,6 +335,8 @@ add_library(core STATIC | |||
| 324 | hle/service/ldr/ldr.h | 335 | hle/service/ldr/ldr.h |
| 325 | hle/service/lm/lm.cpp | 336 | hle/service/lm/lm.cpp |
| 326 | hle/service/lm/lm.h | 337 | hle/service/lm/lm.h |
| 338 | hle/service/lm/manager.cpp | ||
| 339 | hle/service/lm/manager.h | ||
| 327 | hle/service/mig/mig.cpp | 340 | hle/service/mig/mig.cpp |
| 328 | hle/service/mig/mig.h | 341 | hle/service/mig/mig.h |
| 329 | hle/service/mii/mii.cpp | 342 | hle/service/mii/mii.cpp |
| @@ -499,6 +512,15 @@ create_target_directory_groups(core) | |||
| 499 | 512 | ||
| 500 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 513 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 501 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives) | 514 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives) |
| 515 | |||
| 516 | if (YUZU_ENABLE_BOXCAT) | ||
| 517 | get_directory_property(OPENSSL_LIBS | ||
| 518 | DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl | ||
| 519 | DEFINITION OPENSSL_LIBS) | ||
| 520 | target_compile_definitions(core PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT -DYUZU_ENABLE_BOXCAT) | ||
| 521 | target_link_libraries(core PRIVATE httplib json-headers ${OPENSSL_LIBS} zip) | ||
| 522 | endif() | ||
| 523 | |||
| 502 | if (ENABLE_WEB_SERVICE) | 524 | if (ENABLE_WEB_SERVICE) |
| 503 | target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) | 525 | target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) |
| 504 | target_link_libraries(core PRIVATE web_service) | 526 | target_link_libraries(core PRIVATE web_service) |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 92ba42fb9..4d0ac72a5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include "core/hle/service/apm/controller.h" | 35 | #include "core/hle/service/apm/controller.h" |
| 36 | #include "core/hle/service/filesystem/filesystem.h" | 36 | #include "core/hle/service/filesystem/filesystem.h" |
| 37 | #include "core/hle/service/glue/manager.h" | 37 | #include "core/hle/service/glue/manager.h" |
| 38 | #include "core/hle/service/lm/manager.h" | ||
| 38 | #include "core/hle/service/service.h" | 39 | #include "core/hle/service/service.h" |
| 39 | #include "core/hle/service/sm/sm.h" | 40 | #include "core/hle/service/sm/sm.h" |
| 40 | #include "core/loader/loader.h" | 41 | #include "core/loader/loader.h" |
| @@ -111,7 +112,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 111 | } | 112 | } |
| 112 | struct System::Impl { | 113 | struct System::Impl { |
| 113 | explicit Impl(System& system) | 114 | explicit Impl(System& system) |
| 114 | : kernel{system}, cpu_core_manager{system}, applet_manager{system}, reporter{system} {} | 115 | : kernel{system}, fs_controller{system}, cpu_core_manager{system}, |
| 116 | applet_manager{system}, reporter{system} {} | ||
| 115 | 117 | ||
| 116 | Cpu& CurrentCpuCore() { | 118 | Cpu& CurrentCpuCore() { |
| 117 | return cpu_core_manager.GetCurrentCore(); | 119 | return cpu_core_manager.GetCurrentCore(); |
| @@ -249,6 +251,8 @@ struct System::Impl { | |||
| 249 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", | 251 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", |
| 250 | perf_stats->GetMeanFrametime()); | 252 | perf_stats->GetMeanFrametime()); |
| 251 | 253 | ||
| 254 | lm_manager.Flush(); | ||
| 255 | |||
| 252 | is_powered_on = false; | 256 | is_powered_on = false; |
| 253 | exit_lock = false; | 257 | exit_lock = false; |
| 254 | 258 | ||
| @@ -337,8 +341,10 @@ struct System::Impl { | |||
| 337 | bool is_powered_on = false; | 341 | bool is_powered_on = false; |
| 338 | bool exit_lock = false; | 342 | bool exit_lock = false; |
| 339 | 343 | ||
| 344 | Reporter reporter; | ||
| 340 | std::unique_ptr<Memory::CheatEngine> cheat_engine; | 345 | std::unique_ptr<Memory::CheatEngine> cheat_engine; |
| 341 | std::unique_ptr<Tools::Freezer> memory_freezer; | 346 | std::unique_ptr<Tools::Freezer> memory_freezer; |
| 347 | std::array<u8, 0x20> build_id{}; | ||
| 342 | 348 | ||
| 343 | /// Frontend applets | 349 | /// Frontend applets |
| 344 | Service::AM::Applets::AppletManager applet_manager; | 350 | Service::AM::Applets::AppletManager applet_manager; |
| @@ -346,8 +352,9 @@ struct System::Impl { | |||
| 346 | /// APM (Performance) services | 352 | /// APM (Performance) services |
| 347 | Service::APM::Controller apm_controller{core_timing}; | 353 | Service::APM::Controller apm_controller{core_timing}; |
| 348 | 354 | ||
| 349 | /// Glue services | 355 | /// Service State |
| 350 | Service::Glue::ARPManager arp_manager; | 356 | Service::Glue::ARPManager arp_manager; |
| 357 | Service::LM::Manager lm_manager{reporter}; | ||
| 351 | 358 | ||
| 352 | /// Service manager | 359 | /// Service manager |
| 353 | std::shared_ptr<Service::SM::ServiceManager> service_manager; | 360 | std::shared_ptr<Service::SM::ServiceManager> service_manager; |
| @@ -355,8 +362,6 @@ struct System::Impl { | |||
| 355 | /// Telemetry session for this emulation session | 362 | /// Telemetry session for this emulation session |
| 356 | std::unique_ptr<Core::TelemetrySession> telemetry_session; | 363 | std::unique_ptr<Core::TelemetrySession> telemetry_session; |
| 357 | 364 | ||
| 358 | Reporter reporter; | ||
| 359 | |||
| 360 | ResultStatus status = ResultStatus::Success; | 365 | ResultStatus status = ResultStatus::Success; |
| 361 | std::string status_details = ""; | 366 | std::string status_details = ""; |
| 362 | 367 | ||
| @@ -632,6 +637,14 @@ const Service::APM::Controller& System::GetAPMController() const { | |||
| 632 | return impl->apm_controller; | 637 | return impl->apm_controller; |
| 633 | } | 638 | } |
| 634 | 639 | ||
| 640 | Service::LM::Manager& System::GetLogManager() { | ||
| 641 | return impl->lm_manager; | ||
| 642 | } | ||
| 643 | |||
| 644 | const Service::LM::Manager& System::GetLogManager() const { | ||
| 645 | return impl->lm_manager; | ||
| 646 | } | ||
| 647 | |||
| 635 | void System::SetExitLock(bool locked) { | 648 | void System::SetExitLock(bool locked) { |
| 636 | impl->exit_lock = locked; | 649 | impl->exit_lock = locked; |
| 637 | } | 650 | } |
| @@ -640,6 +653,14 @@ bool System::GetExitLock() const { | |||
| 640 | return impl->exit_lock; | 653 | return impl->exit_lock; |
| 641 | } | 654 | } |
| 642 | 655 | ||
| 656 | void System::SetCurrentProcessBuildID(const CurrentBuildProcessID& id) { | ||
| 657 | impl->build_id = id; | ||
| 658 | } | ||
| 659 | |||
| 660 | const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const { | ||
| 661 | return impl->build_id; | ||
| 662 | } | ||
| 663 | |||
| 643 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 664 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { |
| 644 | return impl->Init(*this, emu_window); | 665 | return impl->Init(*this, emu_window); |
| 645 | } | 666 | } |
diff --git a/src/core/core.h b/src/core/core.h index ff10ebe12..90e7ac607 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | 10 | ||
| 11 | #include <map> | ||
| 12 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 13 | #include "core/file_sys/vfs_types.h" | 12 | #include "core/file_sys/vfs_types.h" |
| 14 | #include "core/hle/kernel/object.h" | 13 | #include "core/hle/kernel/object.h" |
| @@ -58,6 +57,10 @@ namespace Glue { | |||
| 58 | class ARPManager; | 57 | class ARPManager; |
| 59 | } | 58 | } |
| 60 | 59 | ||
| 60 | namespace LM { | ||
| 61 | class Manager; | ||
| 62 | } // namespace LM | ||
| 63 | |||
| 61 | namespace SM { | 64 | namespace SM { |
| 62 | class ServiceManager; | 65 | class ServiceManager; |
| 63 | } // namespace SM | 66 | } // namespace SM |
| @@ -98,6 +101,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 98 | 101 | ||
| 99 | class System { | 102 | class System { |
| 100 | public: | 103 | public: |
| 104 | using CurrentBuildProcessID = std::array<u8, 0x20>; | ||
| 105 | |||
| 101 | System(const System&) = delete; | 106 | System(const System&) = delete; |
| 102 | System& operator=(const System&) = delete; | 107 | System& operator=(const System&) = delete; |
| 103 | 108 | ||
| @@ -326,10 +331,18 @@ public: | |||
| 326 | 331 | ||
| 327 | const Service::APM::Controller& GetAPMController() const; | 332 | const Service::APM::Controller& GetAPMController() const; |
| 328 | 333 | ||
| 334 | Service::LM::Manager& GetLogManager(); | ||
| 335 | |||
| 336 | const Service::LM::Manager& GetLogManager() const; | ||
| 337 | |||
| 329 | void SetExitLock(bool locked); | 338 | void SetExitLock(bool locked); |
| 330 | 339 | ||
| 331 | bool GetExitLock() const; | 340 | bool GetExitLock() const; |
| 332 | 341 | ||
| 342 | void SetCurrentProcessBuildID(const CurrentBuildProcessID& id); | ||
| 343 | |||
| 344 | const CurrentBuildProcessID& GetCurrentProcessBuildID() const; | ||
| 345 | |||
| 333 | private: | 346 | private: |
| 334 | System(); | 347 | System(); |
| 335 | 348 | ||
| @@ -353,8 +366,4 @@ private: | |||
| 353 | static System s_instance; | 366 | static System s_instance; |
| 354 | }; | 367 | }; |
| 355 | 368 | ||
| 356 | inline Kernel::Process* CurrentProcess() { | ||
| 357 | return System::GetInstance().CurrentProcess(); | ||
| 358 | } | ||
| 359 | |||
| 360 | } // namespace Core | 369 | } // namespace Core |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 46aceec3d..222fc95ba 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -423,7 +423,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { | |||
| 423 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | 423 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, |
| 424 | const RSAKeyPair<2048>& key) { | 424 | const RSAKeyPair<2048>& key) { |
| 425 | const auto issuer = ticket.GetData().issuer; | 425 | const auto issuer = ticket.GetData().issuer; |
| 426 | if (issuer == std::array<u8, 0x40>{}) | 426 | if (IsAllZeroArray(issuer)) |
| 427 | return {}; | 427 | return {}; |
| 428 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { | 428 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { |
| 429 | LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); | 429 | LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); |
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 8f758d6d9..0af44f340 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -136,4 +136,9 @@ u64 BISFactory::GetFullNANDTotalSpace() const { | |||
| 136 | return static_cast<u64>(Settings::values.nand_total_size); | 136 | return static_cast<u64>(Settings::values.nand_total_size); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const { | ||
| 140 | return GetOrCreateDirectoryRelative(nand_root, | ||
| 141 | fmt::format("/system/save/bcat/{:016X}", title_id)); | ||
| 142 | } | ||
| 143 | |||
| 139 | } // namespace FileSys | 144 | } // namespace FileSys |
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index bdfe728c9..8f0451c98 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h | |||
| @@ -61,6 +61,8 @@ public: | |||
| 61 | u64 GetUserNANDTotalSpace() const; | 61 | u64 GetUserNANDTotalSpace() const; |
| 62 | u64 GetFullNANDTotalSpace() const; | 62 | u64 GetFullNANDTotalSpace() const; |
| 63 | 63 | ||
| 64 | VirtualDir GetBCATDirectory(u64 title_id) const; | ||
| 65 | |||
| 64 | private: | 66 | private: |
| 65 | VirtualDir nand_root; | 67 | VirtualDir nand_root; |
| 66 | VirtualDir load_root; | 68 | VirtualDir load_root; |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 84cd4684c..4bd2e6183 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -35,11 +35,11 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) { | |||
| 35 | this->update_raw = std::move(update_raw); | 35 | this->update_raw = std::move(update_raw); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const { | 38 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const { |
| 39 | if (!updatable) | 39 | if (!updatable) |
| 40 | return MakeResult<VirtualFile>(file); | 40 | return MakeResult<VirtualFile>(file); |
| 41 | 41 | ||
| 42 | const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID()); | 42 | const PatchManager patch_manager(current_process_title_id); |
| 43 | return MakeResult<VirtualFile>( | 43 | return MakeResult<VirtualFile>( |
| 44 | patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); | 44 | patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); |
| 45 | } | 45 | } |
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index da63a313a..c5d40285c 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -33,7 +33,7 @@ public: | |||
| 33 | ~RomFSFactory(); | 33 | ~RomFSFactory(); |
| 34 | 34 | ||
| 35 | void SetPackedUpdate(VirtualFile update_raw); | 35 | void SetPackedUpdate(VirtualFile update_raw); |
| 36 | ResultVal<VirtualFile> OpenCurrentProcess() const; | 36 | ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; |
| 37 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; | 37 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; |
| 38 | 38 | ||
| 39 | private: | 39 | private: |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index f77cc02ac..fc8755c78 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -127,8 +127,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 127 | u128 user_id, u64 save_id) { | 127 | u128 user_id, u64 save_id) { |
| 128 | // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should | 128 | // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should |
| 129 | // be interpreted as the title id of the current process. | 129 | // be interpreted as the title id of the current process. |
| 130 | if (type == SaveDataType::SaveData && title_id == 0) | 130 | if (type == SaveDataType::SaveData && title_id == 0) { |
| 131 | title_id = Core::CurrentProcess()->GetTitleID(); | 131 | title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 132 | } | ||
| 132 | 133 | ||
| 133 | std::string out = GetSaveDataSpaceIdPath(space); | 134 | std::string out = GetSaveDataSpaceIdPath(space); |
| 134 | 135 | ||
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp new file mode 100644 index 000000000..8bdaa7e4a --- /dev/null +++ b/src/core/file_sys/vfs_libzip.cpp | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | #include <zip.h> | ||
| 7 | #include "common/logging/backend.h" | ||
| 8 | #include "core/file_sys/vfs.h" | ||
| 9 | #include "core/file_sys/vfs_libzip.h" | ||
| 10 | #include "core/file_sys/vfs_vector.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | VirtualDir ExtractZIP(VirtualFile file) { | ||
| 15 | zip_error_t error{}; | ||
| 16 | |||
| 17 | const auto data = file->ReadAllBytes(); | ||
| 18 | std::unique_ptr<zip_source_t, decltype(&zip_source_close)> src{ | ||
| 19 | zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_close}; | ||
| 20 | if (src == nullptr) | ||
| 21 | return nullptr; | ||
| 22 | |||
| 23 | std::unique_ptr<zip_t, decltype(&zip_close)> zip{zip_open_from_source(src.get(), 0, &error), | ||
| 24 | zip_close}; | ||
| 25 | if (zip == nullptr) | ||
| 26 | return nullptr; | ||
| 27 | |||
| 28 | std::shared_ptr<VectorVfsDirectory> out = std::make_shared<VectorVfsDirectory>(); | ||
| 29 | |||
| 30 | const auto num_entries = zip_get_num_entries(zip.get(), 0); | ||
| 31 | |||
| 32 | zip_stat_t stat{}; | ||
| 33 | zip_stat_init(&stat); | ||
| 34 | |||
| 35 | for (std::size_t i = 0; i < num_entries; ++i) { | ||
| 36 | const auto stat_res = zip_stat_index(zip.get(), i, 0, &stat); | ||
| 37 | if (stat_res == -1) | ||
| 38 | return nullptr; | ||
| 39 | |||
| 40 | const std::string name(stat.name); | ||
| 41 | if (name.empty()) | ||
| 42 | continue; | ||
| 43 | |||
| 44 | if (name.back() != '/') { | ||
| 45 | std::unique_ptr<zip_file_t, decltype(&zip_fclose)> file{ | ||
| 46 | zip_fopen_index(zip.get(), i, 0), zip_fclose}; | ||
| 47 | |||
| 48 | std::vector<u8> buf(stat.size); | ||
| 49 | if (zip_fread(file.get(), buf.data(), buf.size()) != buf.size()) | ||
| 50 | return nullptr; | ||
| 51 | |||
| 52 | const auto parts = FileUtil::SplitPathComponents(stat.name); | ||
| 53 | const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); | ||
| 54 | |||
| 55 | std::shared_ptr<VectorVfsDirectory> dtrv = out; | ||
| 56 | for (std::size_t j = 0; j < parts.size() - 1; ++j) { | ||
| 57 | if (dtrv == nullptr) | ||
| 58 | return nullptr; | ||
| 59 | const auto subdir = dtrv->GetSubdirectory(parts[j]); | ||
| 60 | if (subdir == nullptr) { | ||
| 61 | const auto temp = std::make_shared<VectorVfsDirectory>( | ||
| 62 | std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parts[j]); | ||
| 63 | dtrv->AddDirectory(temp); | ||
| 64 | dtrv = temp; | ||
| 65 | } else { | ||
| 66 | dtrv = std::dynamic_pointer_cast<VectorVfsDirectory>(subdir); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | if (dtrv == nullptr) | ||
| 71 | return nullptr; | ||
| 72 | dtrv->AddFile(new_file); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | return out; | ||
| 77 | } | ||
| 78 | |||
| 79 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_libzip.h b/src/core/file_sys/vfs_libzip.h new file mode 100644 index 000000000..f68af576a --- /dev/null +++ b/src/core/file_sys/vfs_libzip.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // Copyright 2019 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 "core/file_sys/vfs_types.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | VirtualDir ExtractZIP(VirtualFile zip); | ||
| 12 | |||
| 13 | } // namespace FileSys | ||
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index afa812598..db51d722f 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -641,7 +641,8 @@ static void HandleQuery() { | |||
| 641 | strlen("Xfer:features:read:target.xml:")) == 0) { | 641 | strlen("Xfer:features:read:target.xml:")) == 0) { |
| 642 | SendReply(target_xml); | 642 | SendReply(target_xml); |
| 643 | } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { | 643 | } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { |
| 644 | const VAddr base_address = Core::CurrentProcess()->VMManager().GetCodeRegionBaseAddress(); | 644 | const VAddr base_address = |
| 645 | Core::System::GetInstance().CurrentProcess()->VMManager().GetCodeRegionBaseAddress(); | ||
| 645 | std::string buffer = fmt::format("TextSeg={:0x}", base_address); | 646 | std::string buffer = fmt::format("TextSeg={:0x}", base_address); |
| 646 | SendReply(buffer.c_str()); | 647 | SendReply(buffer.c_str()); |
| 647 | } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { | 648 | } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { |
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index bdfaa977f..2cc5d536b 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp | |||
| @@ -103,7 +103,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { | |||
| 103 | if (handle == CurrentThread) { | 103 | if (handle == CurrentThread) { |
| 104 | return GetCurrentThread(); | 104 | return GetCurrentThread(); |
| 105 | } else if (handle == CurrentProcess) { | 105 | } else if (handle == CurrentProcess) { |
| 106 | return Core::CurrentProcess(); | 106 | return Core::System::GetInstance().CurrentProcess(); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | if (!IsValid(handle)) { | 109 | if (!IsValid(handle)) { |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 797c9a06f..941ebc93a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include "core/hle/service/am/tcap.h" | 31 | #include "core/hle/service/am/tcap.h" |
| 32 | #include "core/hle/service/apm/controller.h" | 32 | #include "core/hle/service/apm/controller.h" |
| 33 | #include "core/hle/service/apm/interface.h" | 33 | #include "core/hle/service/apm/interface.h" |
| 34 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 34 | #include "core/hle/service/filesystem/filesystem.h" | 35 | #include "core/hle/service/filesystem/filesystem.h" |
| 35 | #include "core/hle/service/ns/ns.h" | 36 | #include "core/hle/service/ns/ns.h" |
| 36 | #include "core/hle/service/nvflinger/nvflinger.h" | 37 | #include "core/hle/service/nvflinger/nvflinger.h" |
| @@ -46,15 +47,20 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; | |||
| 46 | constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; | 47 | constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; |
| 47 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; | 48 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; |
| 48 | 49 | ||
| 49 | constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; | 50 | enum class LaunchParameterKind : u32 { |
| 51 | ApplicationSpecific = 1, | ||
| 52 | AccountPreselectedUser = 2, | ||
| 53 | }; | ||
| 54 | |||
| 55 | constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA; | ||
| 50 | 56 | ||
| 51 | struct LaunchParameters { | 57 | struct LaunchParameterAccountPreselectedUser { |
| 52 | u32_le magic; | 58 | u32_le magic; |
| 53 | u32_le is_account_selected; | 59 | u32_le is_account_selected; |
| 54 | u128 current_user; | 60 | u128 current_user; |
| 55 | INSERT_PADDING_BYTES(0x70); | 61 | INSERT_PADDING_BYTES(0x70); |
| 56 | }; | 62 | }; |
| 57 | static_assert(sizeof(LaunchParameters) == 0x88); | 63 | static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); |
| 58 | 64 | ||
| 59 | IWindowController::IWindowController(Core::System& system_) | 65 | IWindowController::IWindowController(Core::System& system_) |
| 60 | : ServiceFramework("IWindowController"), system{system_} { | 66 | : ServiceFramework("IWindowController"), system{system_} { |
| @@ -1128,26 +1134,55 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx | |||
| 1128 | } | 1134 | } |
| 1129 | 1135 | ||
| 1130 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | 1136 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { |
| 1131 | LOG_DEBUG(Service_AM, "called"); | 1137 | IPC::RequestParser rp{ctx}; |
| 1138 | const auto kind = rp.PopEnum<LaunchParameterKind>(); | ||
| 1132 | 1139 | ||
| 1133 | LaunchParameters params{}; | 1140 | LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast<u8>(kind)); |
| 1134 | 1141 | ||
| 1135 | params.magic = POP_LAUNCH_PARAMETER_MAGIC; | 1142 | if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) { |
| 1136 | params.is_account_selected = 1; | 1143 | const auto backend = BCAT::CreateBackendFromSettings( |
| 1144 | [this](u64 tid) { return system.GetFileSystemController().GetBCATDirectory(tid); }); | ||
| 1145 | const auto build_id_full = system.GetCurrentProcessBuildID(); | ||
| 1146 | u64 build_id{}; | ||
| 1147 | std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); | ||
| 1137 | 1148 | ||
| 1138 | Account::ProfileManager profile_manager{}; | 1149 | const auto data = |
| 1139 | const auto uuid = profile_manager.GetUser(Settings::values.current_user); | 1150 | backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id}); |
| 1140 | ASSERT(uuid); | ||
| 1141 | params.current_user = uuid->uuid; | ||
| 1142 | 1151 | ||
| 1143 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 1152 | if (data.has_value()) { |
| 1153 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 1154 | rb.Push(RESULT_SUCCESS); | ||
| 1155 | rb.PushIpcInterface<AM::IStorage>(*data); | ||
| 1156 | launch_popped_application_specific = true; | ||
| 1157 | return; | ||
| 1158 | } | ||
| 1159 | } else if (kind == LaunchParameterKind::AccountPreselectedUser && | ||
| 1160 | !launch_popped_account_preselect) { | ||
| 1161 | LaunchParameterAccountPreselectedUser params{}; | ||
| 1144 | 1162 | ||
| 1145 | rb.Push(RESULT_SUCCESS); | 1163 | params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC; |
| 1164 | params.is_account_selected = 1; | ||
| 1146 | 1165 | ||
| 1147 | std::vector<u8> buffer(sizeof(LaunchParameters)); | 1166 | Account::ProfileManager profile_manager{}; |
| 1148 | std::memcpy(buffer.data(), ¶ms, buffer.size()); | 1167 | const auto uuid = profile_manager.GetUser(Settings::values.current_user); |
| 1168 | ASSERT(uuid); | ||
| 1169 | params.current_user = uuid->uuid; | ||
| 1149 | 1170 | ||
| 1150 | rb.PushIpcInterface<AM::IStorage>(buffer); | 1171 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 1172 | |||
| 1173 | rb.Push(RESULT_SUCCESS); | ||
| 1174 | |||
| 1175 | std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); | ||
| 1176 | std::memcpy(buffer.data(), ¶ms, buffer.size()); | ||
| 1177 | |||
| 1178 | rb.PushIpcInterface<AM::IStorage>(buffer); | ||
| 1179 | launch_popped_account_preselect = true; | ||
| 1180 | return; | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!"); | ||
| 1184 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1185 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | ||
| 1151 | } | 1186 | } |
| 1152 | 1187 | ||
| 1153 | void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( | 1188 | void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( |
| @@ -1165,7 +1200,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | |||
| 1165 | LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); | 1200 | LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); |
| 1166 | 1201 | ||
| 1167 | FileSys::SaveDataDescriptor descriptor{}; | 1202 | FileSys::SaveDataDescriptor descriptor{}; |
| 1168 | descriptor.title_id = Core::CurrentProcess()->GetTitleID(); | 1203 | descriptor.title_id = system.CurrentProcess()->GetTitleID(); |
| 1169 | descriptor.user_id = user_id; | 1204 | descriptor.user_id = user_id; |
| 1170 | descriptor.type = FileSys::SaveDataType::SaveData; | 1205 | descriptor.type = FileSys::SaveDataType::SaveData; |
| 1171 | const auto res = system.GetFileSystemController().CreateSaveData( | 1206 | const auto res = system.GetFileSystemController().CreateSaveData( |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index a3baeb673..ccd053c13 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -147,6 +147,7 @@ private: | |||
| 147 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); | 147 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); |
| 148 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); | 148 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); |
| 149 | 149 | ||
| 150 | Core::System& system; | ||
| 150 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger; | 151 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger; |
| 151 | Kernel::EventPair launchable_event; | 152 | Kernel::EventPair launchable_event; |
| 152 | Kernel::EventPair accumulated_suspended_tick_changed_event; | 153 | Kernel::EventPair accumulated_suspended_tick_changed_event; |
| @@ -154,8 +155,6 @@ private: | |||
| 154 | u32 idle_time_detection_extension = 0; | 155 | u32 idle_time_detection_extension = 0; |
| 155 | u64 num_fatal_sections_entered = 0; | 156 | u64 num_fatal_sections_entered = 0; |
| 156 | bool is_auto_sleep_disabled = false; | 157 | bool is_auto_sleep_disabled = false; |
| 157 | |||
| 158 | Core::System& system; | ||
| 159 | }; | 158 | }; |
| 160 | 159 | ||
| 161 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { | 160 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { |
| @@ -255,6 +254,8 @@ private: | |||
| 255 | void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); | 254 | void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); |
| 256 | void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); | 255 | void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); |
| 257 | 256 | ||
| 257 | bool launch_popped_application_specific = false; | ||
| 258 | bool launch_popped_account_preselect = false; | ||
| 258 | Kernel::EventPair gpu_error_detected_event; | 259 | Kernel::EventPair gpu_error_detected_event; |
| 259 | Core::System& system; | 260 | Core::System& system; |
| 260 | }; | 261 | }; |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index d2e35362f..720fe766f 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -157,6 +157,10 @@ AppletManager::AppletManager(Core::System& system_) : system{system_} {} | |||
| 157 | 157 | ||
| 158 | AppletManager::~AppletManager() = default; | 158 | AppletManager::~AppletManager() = default; |
| 159 | 159 | ||
| 160 | const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const { | ||
| 161 | return frontend; | ||
| 162 | } | ||
| 163 | |||
| 160 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | 164 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { |
| 161 | if (set.parental_controls != nullptr) | 165 | if (set.parental_controls != nullptr) |
| 162 | frontend.parental_controls = std::move(set.parental_controls); | 166 | frontend.parental_controls = std::move(set.parental_controls); |
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 764c3418c..226be88b1 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -190,6 +190,8 @@ public: | |||
| 190 | explicit AppletManager(Core::System& system_); | 190 | explicit AppletManager(Core::System& system_); |
| 191 | ~AppletManager(); | 191 | ~AppletManager(); |
| 192 | 192 | ||
| 193 | const AppletFrontendSet& GetAppletFrontendSet() const; | ||
| 194 | |||
| 193 | void SetAppletFrontendSet(AppletFrontendSet set); | 195 | void SetAppletFrontendSet(AppletFrontendSet set); |
| 194 | void SetDefaultAppletFrontendSet(); | 196 | void SetDefaultAppletFrontendSet(); |
| 195 | void SetDefaultAppletsIfMissing(); | 197 | void SetDefaultAppletsIfMissing(); |
diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp index 4376612eb..073d0f6fa 100644 --- a/src/core/hle/service/apm/controller.cpp +++ b/src/core/hle/service/apm/controller.cpp | |||
| @@ -13,7 +13,7 @@ constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION = | |||
| 13 | PerformanceConfiguration::Config7; | 13 | PerformanceConfiguration::Config7; |
| 14 | 14 | ||
| 15 | Controller::Controller(Core::Timing::CoreTiming& core_timing) | 15 | Controller::Controller(Core::Timing::CoreTiming& core_timing) |
| 16 | : core_timing(core_timing), configs{ | 16 | : core_timing{core_timing}, configs{ |
| 17 | {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, | 17 | {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, |
| 18 | {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, | 18 | {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, |
| 19 | } {} | 19 | } {} |
| @@ -63,6 +63,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa | |||
| 63 | void Controller::SetClockSpeed(u32 mhz) { | 63 | void Controller::SetClockSpeed(u32 mhz) { |
| 64 | LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); | 64 | LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); |
| 65 | // TODO(DarkLordZach): Actually signal core_timing to change clock speed. | 65 | // TODO(DarkLordZach): Actually signal core_timing to change clock speed. |
| 66 | // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. | ||
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | } // namespace Service::APM | 69 | } // namespace Service::APM |
diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/controller.h index 8ac80eaea..454caa6eb 100644 --- a/src/core/hle/service/apm/controller.h +++ b/src/core/hle/service/apm/controller.h | |||
| @@ -50,7 +50,7 @@ enum class PerformanceMode : u8 { | |||
| 50 | // system during times of high load -- this simply maps to different PerformanceConfigs to use. | 50 | // system during times of high load -- this simply maps to different PerformanceConfigs to use. |
| 51 | class Controller { | 51 | class Controller { |
| 52 | public: | 52 | public: |
| 53 | Controller(Core::Timing::CoreTiming& core_timing); | 53 | explicit Controller(Core::Timing::CoreTiming& core_timing); |
| 54 | ~Controller(); | 54 | ~Controller(); |
| 55 | 55 | ||
| 56 | void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config); | 56 | void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config); |
| @@ -62,9 +62,9 @@ public: | |||
| 62 | private: | 62 | private: |
| 63 | void SetClockSpeed(u32 mhz); | 63 | void SetClockSpeed(u32 mhz); |
| 64 | 64 | ||
| 65 | std::map<PerformanceMode, PerformanceConfiguration> configs; | 65 | [[maybe_unused]] Core::Timing::CoreTiming& core_timing; |
| 66 | 66 | ||
| 67 | Core::Timing::CoreTiming& core_timing; | 67 | std::map<PerformanceMode, PerformanceConfiguration> configs; |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | } // namespace Service::APM | 70 | } // namespace Service::APM |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index fb84a8f13..9afefb5c6 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -205,7 +205,7 @@ private: | |||
| 205 | AudioCore::StreamPtr stream; | 205 | AudioCore::StreamPtr stream; |
| 206 | std::string device_name; | 206 | std::string device_name; |
| 207 | 207 | ||
| 208 | AudoutParams audio_params{}; | 208 | [[maybe_unused]] AudoutParams audio_params {}; |
| 209 | 209 | ||
| 210 | /// This is the event handle used to check if the audio buffer was released | 210 | /// This is the event handle used to check if the audio buffer was released |
| 211 | Kernel::EventPair buffer_event; | 211 | Kernel::EventPair buffer_event; |
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp new file mode 100644 index 000000000..9d6946bc5 --- /dev/null +++ b/src/core/hle/service/bcat/backend/backend.cpp | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/hex_util.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/hle/lock.h" | ||
| 9 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 10 | |||
| 11 | namespace Service::BCAT { | ||
| 12 | |||
| 13 | ProgressServiceBackend::ProgressServiceBackend(std::string_view event_name) { | ||
| 14 | auto& kernel{Core::System::GetInstance().Kernel()}; | ||
| 15 | event = Kernel::WritableEvent::CreateEventPair( | ||
| 16 | kernel, Kernel::ResetType::Automatic, | ||
| 17 | std::string("ProgressServiceBackend:UpdateEvent:").append(event_name)); | ||
| 18 | } | ||
| 19 | |||
| 20 | Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const { | ||
| 21 | return event.readable; | ||
| 22 | } | ||
| 23 | |||
| 24 | DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() { | ||
| 25 | return impl; | ||
| 26 | } | ||
| 27 | |||
| 28 | void ProgressServiceBackend::SetNeedHLELock(bool need) { | ||
| 29 | need_hle_lock = need; | ||
| 30 | } | ||
| 31 | |||
| 32 | void ProgressServiceBackend::SetTotalSize(u64 size) { | ||
| 33 | impl.total_bytes = size; | ||
| 34 | SignalUpdate(); | ||
| 35 | } | ||
| 36 | |||
| 37 | void ProgressServiceBackend::StartConnecting() { | ||
| 38 | impl.status = DeliveryCacheProgressImpl::Status::Connecting; | ||
| 39 | SignalUpdate(); | ||
| 40 | } | ||
| 41 | |||
| 42 | void ProgressServiceBackend::StartProcessingDataList() { | ||
| 43 | impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList; | ||
| 44 | SignalUpdate(); | ||
| 45 | } | ||
| 46 | |||
| 47 | void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, | ||
| 48 | std::string_view file_name, u64 file_size) { | ||
| 49 | impl.status = DeliveryCacheProgressImpl::Status::Downloading; | ||
| 50 | impl.current_downloaded_bytes = 0; | ||
| 51 | impl.current_total_bytes = file_size; | ||
| 52 | std::memcpy(impl.current_directory.data(), dir_name.data(), | ||
| 53 | std::min<u64>(dir_name.size(), 0x31ull)); | ||
| 54 | std::memcpy(impl.current_file.data(), file_name.data(), | ||
| 55 | std::min<u64>(file_name.size(), 0x31ull)); | ||
| 56 | SignalUpdate(); | ||
| 57 | } | ||
| 58 | |||
| 59 | void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) { | ||
| 60 | impl.current_downloaded_bytes = downloaded; | ||
| 61 | SignalUpdate(); | ||
| 62 | } | ||
| 63 | |||
| 64 | void ProgressServiceBackend::FinishDownloadingFile() { | ||
| 65 | impl.total_downloaded_bytes += impl.current_total_bytes; | ||
| 66 | SignalUpdate(); | ||
| 67 | } | ||
| 68 | |||
| 69 | void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { | ||
| 70 | impl.status = DeliveryCacheProgressImpl::Status::Committing; | ||
| 71 | impl.current_file.fill(0); | ||
| 72 | impl.current_downloaded_bytes = 0; | ||
| 73 | impl.current_total_bytes = 0; | ||
| 74 | std::memcpy(impl.current_directory.data(), dir_name.data(), | ||
| 75 | std::min<u64>(dir_name.size(), 0x31ull)); | ||
| 76 | SignalUpdate(); | ||
| 77 | } | ||
| 78 | |||
| 79 | void ProgressServiceBackend::FinishDownload(ResultCode result) { | ||
| 80 | impl.total_downloaded_bytes = impl.total_bytes; | ||
| 81 | impl.status = DeliveryCacheProgressImpl::Status::Done; | ||
| 82 | impl.result = result; | ||
| 83 | SignalUpdate(); | ||
| 84 | } | ||
| 85 | |||
| 86 | void ProgressServiceBackend::SignalUpdate() const { | ||
| 87 | if (need_hle_lock) { | ||
| 88 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 89 | event.writable->Signal(); | ||
| 90 | } else { | ||
| 91 | event.writable->Signal(); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} | ||
| 96 | |||
| 97 | Backend::~Backend() = default; | ||
| 98 | |||
| 99 | NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {} | ||
| 100 | |||
| 101 | NullBackend::~NullBackend() = default; | ||
| 102 | |||
| 103 | bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { | ||
| 104 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, | ||
| 105 | title.build_id); | ||
| 106 | |||
| 107 | progress.FinishDownload(RESULT_SUCCESS); | ||
| 108 | return true; | ||
| 109 | } | ||
| 110 | |||
| 111 | bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, | ||
| 112 | ProgressServiceBackend& progress) { | ||
| 113 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, | ||
| 114 | title.build_id, name); | ||
| 115 | |||
| 116 | progress.FinishDownload(RESULT_SUCCESS); | ||
| 117 | return true; | ||
| 118 | } | ||
| 119 | |||
| 120 | bool NullBackend::Clear(u64 title_id) { | ||
| 121 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}"); | ||
| 122 | |||
| 123 | return true; | ||
| 124 | } | ||
| 125 | |||
| 126 | void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { | ||
| 127 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id, | ||
| 128 | Common::HexToString(passphrase)); | ||
| 129 | } | ||
| 130 | |||
| 131 | std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) { | ||
| 132 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, | ||
| 133 | title.build_id); | ||
| 134 | return std::nullopt; | ||
| 135 | } | ||
| 136 | |||
| 137 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h new file mode 100644 index 000000000..51dbd3316 --- /dev/null +++ b/src/core/hle/service/bcat/backend/backend.h | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | // Copyright 2019 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 <functional> | ||
| 8 | #include <optional> | ||
| 9 | #include <string> | ||
| 10 | #include <string_view> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "core/file_sys/vfs_types.h" | ||
| 14 | #include "core/hle/kernel/readable_event.h" | ||
| 15 | #include "core/hle/kernel/writable_event.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | |||
| 18 | namespace Service::BCAT { | ||
| 19 | |||
| 20 | struct DeliveryCacheProgressImpl; | ||
| 21 | |||
| 22 | using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>; | ||
| 23 | using Passphrase = std::array<u8, 0x20>; | ||
| 24 | |||
| 25 | struct TitleIDVersion { | ||
| 26 | u64 title_id; | ||
| 27 | u64 build_id; | ||
| 28 | }; | ||
| 29 | |||
| 30 | using DirectoryName = std::array<char, 0x20>; | ||
| 31 | using FileName = std::array<char, 0x20>; | ||
| 32 | |||
| 33 | struct DeliveryCacheProgressImpl { | ||
| 34 | enum class Status : s32 { | ||
| 35 | None = 0x0, | ||
| 36 | Queued = 0x1, | ||
| 37 | Connecting = 0x2, | ||
| 38 | ProcessingDataList = 0x3, | ||
| 39 | Downloading = 0x4, | ||
| 40 | Committing = 0x5, | ||
| 41 | Done = 0x9, | ||
| 42 | }; | ||
| 43 | |||
| 44 | Status status; | ||
| 45 | ResultCode result = RESULT_SUCCESS; | ||
| 46 | DirectoryName current_directory; | ||
| 47 | FileName current_file; | ||
| 48 | s64 current_downloaded_bytes; ///< Bytes downloaded on current file. | ||
| 49 | s64 current_total_bytes; ///< Bytes total on current file. | ||
| 50 | s64 total_downloaded_bytes; ///< Bytes downloaded on overall download. | ||
| 51 | s64 total_bytes; ///< Bytes total on overall download. | ||
| 52 | INSERT_PADDING_BYTES( | ||
| 53 | 0x198); ///< Appears to be unused in official code, possibly reserved for future use. | ||
| 54 | }; | ||
| 55 | static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, | ||
| 56 | "DeliveryCacheProgressImpl has incorrect size."); | ||
| 57 | |||
| 58 | // A class to manage the signalling to the game about BCAT download progress. | ||
| 59 | // Some of this class is implemented in module.cpp to avoid exposing the implementation structure. | ||
| 60 | class ProgressServiceBackend { | ||
| 61 | friend class IBcatService; | ||
| 62 | |||
| 63 | public: | ||
| 64 | // Clients should call this with true if any of the functions are going to be called from a | ||
| 65 | // non-HLE thread and this class need to lock the hle mutex. (default is false) | ||
| 66 | void SetNeedHLELock(bool need); | ||
| 67 | |||
| 68 | // Sets the number of bytes total in the entire download. | ||
| 69 | void SetTotalSize(u64 size); | ||
| 70 | |||
| 71 | // Notifies the application that the backend has started connecting to the server. | ||
| 72 | void StartConnecting(); | ||
| 73 | // Notifies the application that the backend has begun accumulating and processing metadata. | ||
| 74 | void StartProcessingDataList(); | ||
| 75 | |||
| 76 | // Notifies the application that a file is starting to be downloaded. | ||
| 77 | void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size); | ||
| 78 | // Updates the progress of the current file to the size passed. | ||
| 79 | void UpdateFileProgress(u64 downloaded); | ||
| 80 | // Notifies the application that the current file has completed download. | ||
| 81 | void FinishDownloadingFile(); | ||
| 82 | |||
| 83 | // Notifies the application that all files in this directory have completed and are being | ||
| 84 | // finalized. | ||
| 85 | void CommitDirectory(std::string_view dir_name); | ||
| 86 | |||
| 87 | // Notifies the application that the operation completed with result code result. | ||
| 88 | void FinishDownload(ResultCode result); | ||
| 89 | |||
| 90 | private: | ||
| 91 | explicit ProgressServiceBackend(std::string_view event_name); | ||
| 92 | |||
| 93 | Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent() const; | ||
| 94 | DeliveryCacheProgressImpl& GetImpl(); | ||
| 95 | |||
| 96 | void SignalUpdate() const; | ||
| 97 | |||
| 98 | DeliveryCacheProgressImpl impl{}; | ||
| 99 | Kernel::EventPair event; | ||
| 100 | bool need_hle_lock = false; | ||
| 101 | }; | ||
| 102 | |||
| 103 | // A class representing an abstract backend for BCAT functionality. | ||
| 104 | class Backend { | ||
| 105 | public: | ||
| 106 | explicit Backend(DirectoryGetter getter); | ||
| 107 | virtual ~Backend(); | ||
| 108 | |||
| 109 | // Called when the backend is needed to synchronize the data for the game with title ID and | ||
| 110 | // version in title. A ProgressServiceBackend object is provided to alert the application of | ||
| 111 | // status. | ||
| 112 | virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0; | ||
| 113 | // Very similar to Synchronize, but only for the directory provided. Backends should not alter | ||
| 114 | // the data for any other directories. | ||
| 115 | virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name, | ||
| 116 | ProgressServiceBackend& progress) = 0; | ||
| 117 | |||
| 118 | // Removes all cached data associated with title id provided. | ||
| 119 | virtual bool Clear(u64 title_id) = 0; | ||
| 120 | |||
| 121 | // Sets the BCAT Passphrase to be used with the associated title ID. | ||
| 122 | virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0; | ||
| 123 | |||
| 124 | // Gets the launch parameter used by AM associated with the title ID and version provided. | ||
| 125 | virtual std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0; | ||
| 126 | |||
| 127 | protected: | ||
| 128 | DirectoryGetter dir_getter; | ||
| 129 | }; | ||
| 130 | |||
| 131 | // A backend of BCAT that provides no operation. | ||
| 132 | class NullBackend : public Backend { | ||
| 133 | public: | ||
| 134 | explicit NullBackend(DirectoryGetter getter); | ||
| 135 | ~NullBackend() override; | ||
| 136 | |||
| 137 | bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; | ||
| 138 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, | ||
| 139 | ProgressServiceBackend& progress) override; | ||
| 140 | |||
| 141 | bool Clear(u64 title_id) override; | ||
| 142 | |||
| 143 | void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; | ||
| 144 | |||
| 145 | std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override; | ||
| 146 | }; | ||
| 147 | |||
| 148 | std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter); | ||
| 149 | |||
| 150 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp new file mode 100644 index 000000000..64022982b --- /dev/null +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -0,0 +1,504 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <fmt/ostream.h> | ||
| 6 | #include <httplib.h> | ||
| 7 | #include <json.hpp> | ||
| 8 | #include <mbedtls/sha256.h> | ||
| 9 | #include "common/hex_util.h" | ||
| 10 | #include "common/logging/backend.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "core/core.h" | ||
| 13 | #include "core/file_sys/vfs.h" | ||
| 14 | #include "core/file_sys/vfs_libzip.h" | ||
| 15 | #include "core/file_sys/vfs_vector.h" | ||
| 16 | #include "core/frontend/applets/error.h" | ||
| 17 | #include "core/hle/service/am/applets/applets.h" | ||
| 18 | #include "core/hle/service/bcat/backend/boxcat.h" | ||
| 19 | #include "core/settings.h" | ||
| 20 | |||
| 21 | namespace { | ||
| 22 | |||
| 23 | // Prevents conflicts with windows macro called CreateFile | ||
| 24 | FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) { | ||
| 25 | return dir->CreateFile(name); | ||
| 26 | } | ||
| 27 | |||
| 28 | // Prevents conflicts with windows macro called DeleteFile | ||
| 29 | bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) { | ||
| 30 | return dir->DeleteFile(name); | ||
| 31 | } | ||
| 32 | |||
| 33 | } // Anonymous namespace | ||
| 34 | |||
| 35 | namespace Service::BCAT { | ||
| 36 | |||
| 37 | constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; | ||
| 38 | |||
| 39 | constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; | ||
| 40 | |||
| 41 | // Formatted using fmt with arg[0] = hex title id | ||
| 42 | constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat"; | ||
| 43 | constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam"; | ||
| 44 | |||
| 45 | constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events"; | ||
| 46 | |||
| 47 | constexpr char BOXCAT_API_VERSION[] = "1"; | ||
| 48 | constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu"; | ||
| 49 | |||
| 50 | // HTTP status codes for Boxcat | ||
| 51 | enum class ResponseStatus { | ||
| 52 | Ok = 200, ///< Operation completed successfully. | ||
| 53 | BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server. | ||
| 54 | NoUpdate = 304, ///< The digest provided would match the new data, no need to update. | ||
| 55 | NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation. | ||
| 56 | NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format | ||
| 57 | ///< issues or whatnot) and has no data. | ||
| 58 | }; | ||
| 59 | |||
| 60 | enum class DownloadResult { | ||
| 61 | Success = 0, | ||
| 62 | NoResponse, | ||
| 63 | GeneralWebError, | ||
| 64 | NoMatchTitleId, | ||
| 65 | NoMatchBuildId, | ||
| 66 | InvalidContentType, | ||
| 67 | GeneralFSError, | ||
| 68 | BadClientVersion, | ||
| 69 | }; | ||
| 70 | |||
| 71 | constexpr std::array<const char*, 8> DOWNLOAD_RESULT_LOG_MESSAGES{ | ||
| 72 | "Success", | ||
| 73 | "There was no response from the server.", | ||
| 74 | "There was a general web error code returned from the server.", | ||
| 75 | "The title ID of the current game doesn't have a boxcat implementation. If you believe an " | ||
| 76 | "implementation should be added, contact yuzu support.", | ||
| 77 | "The build ID of the current version of the game is marked as incompatible with the current " | ||
| 78 | "BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.", | ||
| 79 | "The content type of the web response was invalid.", | ||
| 80 | "There was a general filesystem error while saving the zip file.", | ||
| 81 | "The server is either too new or too old to serve the request. Try using the latest version of " | ||
| 82 | "an official release of yuzu.", | ||
| 83 | }; | ||
| 84 | |||
| 85 | std::ostream& operator<<(std::ostream& os, DownloadResult result) { | ||
| 86 | return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast<std::size_t>(result)); | ||
| 87 | } | ||
| 88 | |||
| 89 | constexpr u32 PORT = 443; | ||
| 90 | constexpr u32 TIMEOUT_SECONDS = 30; | ||
| 91 | [[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB | ||
| 92 | |||
| 93 | namespace { | ||
| 94 | |||
| 95 | std::string GetBINFilePath(u64 title_id) { | ||
| 96 | return fmt::format("{}bcat/{:016X}/launchparam.bin", | ||
| 97 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string GetZIPFilePath(u64 title_id) { | ||
| 101 | return fmt::format("{}bcat/{:016X}/data.zip", | ||
| 102 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); | ||
| 103 | } | ||
| 104 | |||
| 105 | // If the error is something the user should know about (build ID mismatch, bad client version), | ||
| 106 | // display an error. | ||
| 107 | void HandleDownloadDisplayResult(DownloadResult res) { | ||
| 108 | if (res == DownloadResult::Success || res == DownloadResult::NoResponse || | ||
| 109 | res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError || | ||
| 110 | res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) { | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | const auto& frontend{Core::System::GetInstance().GetAppletManager().GetAppletFrontendSet()}; | ||
| 115 | frontend.error->ShowCustomErrorText( | ||
| 116 | ResultCode(-1), "There was an error while attempting to use Boxcat.", | ||
| 117 | DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {}); | ||
| 118 | } | ||
| 119 | |||
| 120 | bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest, | ||
| 121 | std::string_view dir_name, ProgressServiceBackend& progress, | ||
| 122 | std::size_t block_size = 0x1000) { | ||
| 123 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 124 | return false; | ||
| 125 | if (!dest->Resize(src->GetSize())) | ||
| 126 | return false; | ||
| 127 | |||
| 128 | progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize()); | ||
| 129 | |||
| 130 | std::vector<u8> temp(std::min(block_size, src->GetSize())); | ||
| 131 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { | ||
| 132 | const auto read = std::min(block_size, src->GetSize() - i); | ||
| 133 | |||
| 134 | if (src->Read(temp.data(), read, i) != read) { | ||
| 135 | return false; | ||
| 136 | } | ||
| 137 | |||
| 138 | if (dest->Write(temp.data(), read, i) != read) { | ||
| 139 | return false; | ||
| 140 | } | ||
| 141 | |||
| 142 | progress.UpdateFileProgress(i); | ||
| 143 | } | ||
| 144 | |||
| 145 | progress.FinishDownloadingFile(); | ||
| 146 | |||
| 147 | return true; | ||
| 148 | } | ||
| 149 | |||
| 150 | bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest, | ||
| 151 | ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { | ||
| 152 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 153 | return false; | ||
| 154 | |||
| 155 | for (const auto& file : src->GetFiles()) { | ||
| 156 | const auto out_file = VfsCreateFileWrap(dest, file->GetName()); | ||
| 157 | if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) { | ||
| 158 | return false; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | progress.CommitDirectory(src->GetName()); | ||
| 162 | |||
| 163 | return true; | ||
| 164 | } | ||
| 165 | |||
| 166 | bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest, | ||
| 167 | ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { | ||
| 168 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 169 | return false; | ||
| 170 | |||
| 171 | for (const auto& dir : src->GetSubdirectories()) { | ||
| 172 | const auto out = dest->CreateSubdirectory(dir->GetName()); | ||
| 173 | if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) { | ||
| 174 | return false; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | return true; | ||
| 179 | } | ||
| 180 | |||
| 181 | } // Anonymous namespace | ||
| 182 | |||
| 183 | class Boxcat::Client { | ||
| 184 | public: | ||
| 185 | Client(std::string path, u64 title_id, u64 build_id) | ||
| 186 | : path(std::move(path)), title_id(title_id), build_id(build_id) {} | ||
| 187 | |||
| 188 | DownloadResult DownloadDataZip() { | ||
| 189 | return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS, | ||
| 190 | "application/zip"); | ||
| 191 | } | ||
| 192 | |||
| 193 | DownloadResult DownloadLaunchParam() { | ||
| 194 | return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id), | ||
| 195 | TIMEOUT_SECONDS / 3, "application/octet-stream"); | ||
| 196 | } | ||
| 197 | |||
| 198 | private: | ||
| 199 | DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds, | ||
| 200 | const std::string& content_type_name) { | ||
| 201 | if (client == nullptr) { | ||
| 202 | client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds); | ||
| 203 | } | ||
| 204 | |||
| 205 | httplib::Headers headers{ | ||
| 206 | {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, | ||
| 207 | {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, | ||
| 208 | {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, | ||
| 209 | }; | ||
| 210 | |||
| 211 | if (FileUtil::Exists(path)) { | ||
| 212 | FileUtil::IOFile file{path, "rb"}; | ||
| 213 | if (file.IsOpen()) { | ||
| 214 | std::vector<u8> bytes(file.GetSize()); | ||
| 215 | file.ReadBytes(bytes.data(), bytes.size()); | ||
| 216 | const auto digest = DigestFile(bytes); | ||
| 217 | headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)}); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | const auto response = client->Get(resolved_path.c_str(), headers); | ||
| 222 | if (response == nullptr) | ||
| 223 | return DownloadResult::NoResponse; | ||
| 224 | |||
| 225 | if (response->status == static_cast<int>(ResponseStatus::NoUpdate)) | ||
| 226 | return DownloadResult::Success; | ||
| 227 | if (response->status == static_cast<int>(ResponseStatus::BadClientVersion)) | ||
| 228 | return DownloadResult::BadClientVersion; | ||
| 229 | if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId)) | ||
| 230 | return DownloadResult::NoMatchTitleId; | ||
| 231 | if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId)) | ||
| 232 | return DownloadResult::NoMatchBuildId; | ||
| 233 | if (response->status != static_cast<int>(ResponseStatus::Ok)) | ||
| 234 | return DownloadResult::GeneralWebError; | ||
| 235 | |||
| 236 | const auto content_type = response->headers.find("content-type"); | ||
| 237 | if (content_type == response->headers.end() || | ||
| 238 | content_type->second.find(content_type_name) == std::string::npos) { | ||
| 239 | return DownloadResult::InvalidContentType; | ||
| 240 | } | ||
| 241 | |||
| 242 | FileUtil::CreateFullPath(path); | ||
| 243 | FileUtil::IOFile file{path, "wb"}; | ||
| 244 | if (!file.IsOpen()) | ||
| 245 | return DownloadResult::GeneralFSError; | ||
| 246 | if (!file.Resize(response->body.size())) | ||
| 247 | return DownloadResult::GeneralFSError; | ||
| 248 | if (file.WriteBytes(response->body.data(), response->body.size()) != response->body.size()) | ||
| 249 | return DownloadResult::GeneralFSError; | ||
| 250 | |||
| 251 | return DownloadResult::Success; | ||
| 252 | } | ||
| 253 | |||
| 254 | using Digest = std::array<u8, 0x20>; | ||
| 255 | static Digest DigestFile(std::vector<u8> bytes) { | ||
| 256 | Digest out{}; | ||
| 257 | mbedtls_sha256(bytes.data(), bytes.size(), out.data(), 0); | ||
| 258 | return out; | ||
| 259 | } | ||
| 260 | |||
| 261 | std::unique_ptr<httplib::Client> client; | ||
| 262 | std::string path; | ||
| 263 | u64 title_id; | ||
| 264 | u64 build_id; | ||
| 265 | }; | ||
| 266 | |||
| 267 | Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {} | ||
| 268 | |||
| 269 | Boxcat::~Boxcat() = default; | ||
| 270 | |||
| 271 | void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, | ||
| 272 | ProgressServiceBackend& progress, | ||
| 273 | std::optional<std::string> dir_name = {}) { | ||
| 274 | progress.SetNeedHLELock(true); | ||
| 275 | |||
| 276 | if (Settings::values.bcat_boxcat_local) { | ||
| 277 | LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); | ||
| 278 | const auto dir = dir_getter(title.title_id); | ||
| 279 | if (dir) | ||
| 280 | progress.SetTotalSize(dir->GetSize()); | ||
| 281 | progress.FinishDownload(RESULT_SUCCESS); | ||
| 282 | return; | ||
| 283 | } | ||
| 284 | |||
| 285 | const auto zip_path{GetZIPFilePath(title.title_id)}; | ||
| 286 | Boxcat::Client client{zip_path, title.title_id, title.build_id}; | ||
| 287 | |||
| 288 | progress.StartConnecting(); | ||
| 289 | |||
| 290 | const auto res = client.DownloadDataZip(); | ||
| 291 | if (res != DownloadResult::Success) { | ||
| 292 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); | ||
| 293 | |||
| 294 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { | ||
| 295 | FileUtil::Delete(zip_path); | ||
| 296 | } | ||
| 297 | |||
| 298 | HandleDownloadDisplayResult(res); | ||
| 299 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | |||
| 303 | progress.StartProcessingDataList(); | ||
| 304 | |||
| 305 | FileUtil::IOFile zip{zip_path, "rb"}; | ||
| 306 | const auto size = zip.GetSize(); | ||
| 307 | std::vector<u8> bytes(size); | ||
| 308 | if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { | ||
| 309 | LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path); | ||
| 310 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); | ||
| 311 | return; | ||
| 312 | } | ||
| 313 | |||
| 314 | const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes)); | ||
| 315 | if (extracted == nullptr) { | ||
| 316 | LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!"); | ||
| 317 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | |||
| 321 | if (dir_name == std::nullopt) { | ||
| 322 | progress.SetTotalSize(extracted->GetSize()); | ||
| 323 | |||
| 324 | const auto target_dir = dir_getter(title.title_id); | ||
| 325 | if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) { | ||
| 326 | LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); | ||
| 327 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); | ||
| 328 | return; | ||
| 329 | } | ||
| 330 | } else { | ||
| 331 | const auto target_dir = dir_getter(title.title_id); | ||
| 332 | if (target_dir == nullptr) { | ||
| 333 | LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!"); | ||
| 334 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); | ||
| 335 | return; | ||
| 336 | } | ||
| 337 | |||
| 338 | const auto target_sub = target_dir->GetSubdirectory(*dir_name); | ||
| 339 | const auto source_sub = extracted->GetSubdirectory(*dir_name); | ||
| 340 | |||
| 341 | progress.SetTotalSize(source_sub->GetSize()); | ||
| 342 | |||
| 343 | std::vector<std::string> filenames; | ||
| 344 | { | ||
| 345 | const auto files = target_sub->GetFiles(); | ||
| 346 | std::transform(files.begin(), files.end(), std::back_inserter(filenames), | ||
| 347 | [](const auto& vfile) { return vfile->GetName(); }); | ||
| 348 | } | ||
| 349 | |||
| 350 | for (const auto& filename : filenames) { | ||
| 351 | VfsDeleteFileWrap(target_sub, filename); | ||
| 352 | } | ||
| 353 | |||
| 354 | if (target_sub == nullptr || source_sub == nullptr || | ||
| 355 | !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) { | ||
| 356 | LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); | ||
| 357 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); | ||
| 358 | return; | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | progress.FinishDownload(RESULT_SUCCESS); | ||
| 363 | } | ||
| 364 | |||
| 365 | bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { | ||
| 366 | is_syncing.exchange(true); | ||
| 367 | std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); }) | ||
| 368 | .detach(); | ||
| 369 | return true; | ||
| 370 | } | ||
| 371 | |||
| 372 | bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, | ||
| 373 | ProgressServiceBackend& progress) { | ||
| 374 | is_syncing.exchange(true); | ||
| 375 | std::thread( | ||
| 376 | [this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); }) | ||
| 377 | .detach(); | ||
| 378 | return true; | ||
| 379 | } | ||
| 380 | |||
| 381 | bool Boxcat::Clear(u64 title_id) { | ||
| 382 | if (Settings::values.bcat_boxcat_local) { | ||
| 383 | LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear."); | ||
| 384 | return true; | ||
| 385 | } | ||
| 386 | |||
| 387 | const auto dir = dir_getter(title_id); | ||
| 388 | |||
| 389 | std::vector<std::string> dirnames; | ||
| 390 | |||
| 391 | for (const auto& subdir : dir->GetSubdirectories()) | ||
| 392 | dirnames.push_back(subdir->GetName()); | ||
| 393 | |||
| 394 | for (const auto& subdir : dirnames) { | ||
| 395 | if (!dir->DeleteSubdirectoryRecursive(subdir)) | ||
| 396 | return false; | ||
| 397 | } | ||
| 398 | |||
| 399 | return true; | ||
| 400 | } | ||
| 401 | |||
| 402 | void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) { | ||
| 403 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, | ||
| 404 | Common::HexToString(passphrase)); | ||
| 405 | } | ||
| 406 | |||
| 407 | std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) { | ||
| 408 | const auto path{GetBINFilePath(title.title_id)}; | ||
| 409 | |||
| 410 | if (Settings::values.bcat_boxcat_local) { | ||
| 411 | LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); | ||
| 412 | } else { | ||
| 413 | Boxcat::Client client{path, title.title_id, title.build_id}; | ||
| 414 | |||
| 415 | const auto res = client.DownloadLaunchParam(); | ||
| 416 | if (res != DownloadResult::Success) { | ||
| 417 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); | ||
| 418 | |||
| 419 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { | ||
| 420 | FileUtil::Delete(path); | ||
| 421 | } | ||
| 422 | |||
| 423 | HandleDownloadDisplayResult(res); | ||
| 424 | return std::nullopt; | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | FileUtil::IOFile bin{path, "rb"}; | ||
| 429 | const auto size = bin.GetSize(); | ||
| 430 | std::vector<u8> bytes(size); | ||
| 431 | if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { | ||
| 432 | LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!", | ||
| 433 | path); | ||
| 434 | return std::nullopt; | ||
| 435 | } | ||
| 436 | |||
| 437 | return bytes; | ||
| 438 | } | ||
| 439 | |||
| 440 | Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global, | ||
| 441 | std::map<std::string, EventStatus>& games) { | ||
| 442 | httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT), | ||
| 443 | static_cast<int>(TIMEOUT_SECONDS)}; | ||
| 444 | |||
| 445 | httplib::Headers headers{ | ||
| 446 | {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, | ||
| 447 | {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, | ||
| 448 | }; | ||
| 449 | |||
| 450 | const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers); | ||
| 451 | if (response == nullptr) | ||
| 452 | return StatusResult::Offline; | ||
| 453 | |||
| 454 | if (response->status == static_cast<int>(ResponseStatus::BadClientVersion)) | ||
| 455 | return StatusResult::BadClientVersion; | ||
| 456 | |||
| 457 | try { | ||
| 458 | nlohmann::json json = nlohmann::json::parse(response->body); | ||
| 459 | |||
| 460 | if (!json["online"].get<bool>()) | ||
| 461 | return StatusResult::Offline; | ||
| 462 | |||
| 463 | if (json["global"].is_null()) | ||
| 464 | global = std::nullopt; | ||
| 465 | else | ||
| 466 | global = json["global"].get<std::string>(); | ||
| 467 | |||
| 468 | if (json["games"].is_array()) { | ||
| 469 | for (const auto object : json["games"]) { | ||
| 470 | if (object.is_object() && object.find("name") != object.end()) { | ||
| 471 | EventStatus detail{}; | ||
| 472 | if (object["header"].is_string()) { | ||
| 473 | detail.header = object["header"].get<std::string>(); | ||
| 474 | } else { | ||
| 475 | detail.header = std::nullopt; | ||
| 476 | } | ||
| 477 | |||
| 478 | if (object["footer"].is_string()) { | ||
| 479 | detail.footer = object["footer"].get<std::string>(); | ||
| 480 | } else { | ||
| 481 | detail.footer = std::nullopt; | ||
| 482 | } | ||
| 483 | |||
| 484 | if (object["events"].is_array()) { | ||
| 485 | for (const auto& event : object["events"]) { | ||
| 486 | if (!event.is_string()) | ||
| 487 | continue; | ||
| 488 | detail.events.push_back(event.get<std::string>()); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | games.insert_or_assign(object["name"], std::move(detail)); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | return StatusResult::Success; | ||
| 498 | } catch (const nlohmann::json::parse_error& error) { | ||
| 499 | LOG_ERROR(Service_BCAT, "{}", error.what()); | ||
| 500 | return StatusResult::ParseError; | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h new file mode 100644 index 000000000..601151189 --- /dev/null +++ b/src/core/hle/service/bcat/backend/boxcat.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2019 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 <map> | ||
| 9 | #include <optional> | ||
| 10 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 11 | |||
| 12 | namespace Service::BCAT { | ||
| 13 | |||
| 14 | struct EventStatus { | ||
| 15 | std::optional<std::string> header; | ||
| 16 | std::optional<std::string> footer; | ||
| 17 | std::vector<std::string> events; | ||
| 18 | }; | ||
| 19 | |||
| 20 | /// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and | ||
| 21 | /// doesn't require a switch or nintendo account. The content is controlled by the yuzu team. | ||
| 22 | class Boxcat final : public Backend { | ||
| 23 | friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, | ||
| 24 | ProgressServiceBackend& progress, | ||
| 25 | std::optional<std::string> dir_name); | ||
| 26 | |||
| 27 | public: | ||
| 28 | explicit Boxcat(DirectoryGetter getter); | ||
| 29 | ~Boxcat() override; | ||
| 30 | |||
| 31 | bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; | ||
| 32 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, | ||
| 33 | ProgressServiceBackend& progress) override; | ||
| 34 | |||
| 35 | bool Clear(u64 title_id) override; | ||
| 36 | |||
| 37 | void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; | ||
| 38 | |||
| 39 | std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override; | ||
| 40 | |||
| 41 | enum class StatusResult { | ||
| 42 | Success, | ||
| 43 | Offline, | ||
| 44 | ParseError, | ||
| 45 | BadClientVersion, | ||
| 46 | }; | ||
| 47 | |||
| 48 | static StatusResult GetStatus(std::optional<std::string>& global, | ||
| 49 | std::map<std::string, EventStatus>& games); | ||
| 50 | |||
| 51 | private: | ||
| 52 | std::atomic_bool is_syncing{false}; | ||
| 53 | |||
| 54 | class Client; | ||
| 55 | std::unique_ptr<Client> client; | ||
| 56 | }; | ||
| 57 | |||
| 58 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 179aa4949..8bb2528c9 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp | |||
| @@ -6,11 +6,16 @@ | |||
| 6 | 6 | ||
| 7 | namespace Service::BCAT { | 7 | namespace Service::BCAT { |
| 8 | 8 | ||
| 9 | BCAT::BCAT(std::shared_ptr<Module> module, const char* name) | 9 | BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module, |
| 10 | : Module::Interface(std::move(module), name) { | 10 | FileSystem::FileSystemController& fsc, const char* name) |
| 11 | : Interface(system, std::move(module), fsc, name) { | ||
| 12 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | 13 | static const FunctionInfo functions[] = { |
| 12 | {0, &BCAT::CreateBcatService, "CreateBcatService"}, | 14 | {0, &BCAT::CreateBcatService, "CreateBcatService"}, |
| 15 | {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, | ||
| 16 | {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, | ||
| 13 | }; | 17 | }; |
| 18 | // clang-format on | ||
| 14 | RegisterHandlers(functions); | 19 | RegisterHandlers(functions); |
| 15 | } | 20 | } |
| 16 | 21 | ||
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index 802bd689a..6354465fc 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h | |||
| @@ -6,11 +6,16 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/hle/service/bcat/module.h" | 7 | #include "core/hle/service/bcat/module.h" |
| 8 | 8 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Service::BCAT { | 13 | namespace Service::BCAT { |
| 10 | 14 | ||
| 11 | class BCAT final : public Module::Interface { | 15 | class BCAT final : public Module::Interface { |
| 12 | public: | 16 | public: |
| 13 | explicit BCAT(std::shared_ptr<Module> module, const char* name); | 17 | explicit BCAT(Core::System& system, std::shared_ptr<Module> module, |
| 18 | FileSystem::FileSystemController& fsc, const char* name); | ||
| 14 | ~BCAT() override; | 19 | ~BCAT() override; |
| 15 | }; | 20 | }; |
| 16 | 21 | ||
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index b7bd738fc..4e4aa758b 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -2,34 +2,257 @@ | |||
| 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 <cctype> | ||
| 6 | #include <mbedtls/md5.h> | ||
| 7 | #include "backend/boxcat.h" | ||
| 8 | #include "common/hex_util.h" | ||
| 5 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | ||
| 11 | #include "core/file_sys/vfs.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | 12 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/process.h" | ||
| 14 | #include "core/hle/kernel/readable_event.h" | ||
| 15 | #include "core/hle/kernel/writable_event.h" | ||
| 16 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 7 | #include "core/hle/service/bcat/bcat.h" | 17 | #include "core/hle/service/bcat/bcat.h" |
| 8 | #include "core/hle/service/bcat/module.h" | 18 | #include "core/hle/service/bcat/module.h" |
| 19 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 20 | #include "core/settings.h" | ||
| 9 | 21 | ||
| 10 | namespace Service::BCAT { | 22 | namespace Service::BCAT { |
| 11 | 23 | ||
| 24 | constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; | ||
| 25 | constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; | ||
| 26 | constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; | ||
| 27 | constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; | ||
| 28 | |||
| 29 | // The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files | ||
| 30 | // and if any of them have a non-zero result it just forwards that result. This is the FS error code | ||
| 31 | // for permission denied, which is the closest approximation of this scenario. | ||
| 32 | constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; | ||
| 33 | |||
| 34 | using BCATDigest = std::array<u8, 0x10>; | ||
| 35 | |||
| 36 | namespace { | ||
| 37 | |||
| 38 | u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) { | ||
| 39 | u64 out{}; | ||
| 40 | std::memcpy(&out, id.data(), sizeof(u64)); | ||
| 41 | return out; | ||
| 42 | } | ||
| 43 | |||
| 44 | // The digest is only used to determine if a file is unique compared to others of the same name. | ||
| 45 | // Since the algorithm isn't ever checked in game, MD5 is safe. | ||
| 46 | BCATDigest DigestFile(const FileSys::VirtualFile& file) { | ||
| 47 | BCATDigest out{}; | ||
| 48 | const auto bytes = file->ReadAllBytes(); | ||
| 49 | mbedtls_md5(bytes.data(), bytes.size(), out.data()); | ||
| 50 | return out; | ||
| 51 | } | ||
| 52 | |||
| 53 | // For a name to be valid it must be non-empty, must have a null terminating character as the final | ||
| 54 | // char, can only contain numbers, letters, underscores and a hyphen if directory and a period if | ||
| 55 | // file. | ||
| 56 | bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name, | ||
| 57 | char match_char) { | ||
| 58 | const auto null_chars = std::count(name.begin(), name.end(), 0); | ||
| 59 | const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) { | ||
| 60 | return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0'; | ||
| 61 | }); | ||
| 62 | if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') { | ||
| 63 | LOG_ERROR(Service_BCAT, "Name passed was invalid!"); | ||
| 64 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 65 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 66 | return false; | ||
| 67 | } | ||
| 68 | |||
| 69 | return true; | ||
| 70 | } | ||
| 71 | |||
| 72 | bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) { | ||
| 73 | return VerifyNameValidInternal(ctx, name, '-'); | ||
| 74 | } | ||
| 75 | |||
| 76 | bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) { | ||
| 77 | return VerifyNameValidInternal(ctx, name, '.'); | ||
| 78 | } | ||
| 79 | |||
| 80 | } // Anonymous namespace | ||
| 81 | |||
| 82 | struct DeliveryCacheDirectoryEntry { | ||
| 83 | FileName name; | ||
| 84 | u64 size; | ||
| 85 | BCATDigest digest; | ||
| 86 | }; | ||
| 87 | |||
| 88 | class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> { | ||
| 89 | public: | ||
| 90 | IDeliveryCacheProgressService(Kernel::SharedPtr<Kernel::ReadableEvent> event, | ||
| 91 | const DeliveryCacheProgressImpl& impl) | ||
| 92 | : ServiceFramework{"IDeliveryCacheProgressService"}, event(std::move(event)), impl(impl) { | ||
| 93 | // clang-format off | ||
| 94 | static const FunctionInfo functions[] = { | ||
| 95 | {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"}, | ||
| 96 | {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"}, | ||
| 97 | }; | ||
| 98 | // clang-format on | ||
| 99 | |||
| 100 | RegisterHandlers(functions); | ||
| 101 | } | ||
| 102 | |||
| 103 | private: | ||
| 104 | void GetEvent(Kernel::HLERequestContext& ctx) { | ||
| 105 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 106 | |||
| 107 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 108 | rb.Push(RESULT_SUCCESS); | ||
| 109 | rb.PushCopyObjects(event); | ||
| 110 | } | ||
| 111 | |||
| 112 | void GetImpl(Kernel::HLERequestContext& ctx) { | ||
| 113 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 114 | |||
| 115 | ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl)); | ||
| 116 | |||
| 117 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 118 | rb.Push(RESULT_SUCCESS); | ||
| 119 | } | ||
| 120 | |||
| 121 | Kernel::SharedPtr<Kernel::ReadableEvent> event; | ||
| 122 | const DeliveryCacheProgressImpl& impl; | ||
| 123 | }; | ||
| 124 | |||
| 12 | class IBcatService final : public ServiceFramework<IBcatService> { | 125 | class IBcatService final : public ServiceFramework<IBcatService> { |
| 13 | public: | 126 | public: |
| 14 | IBcatService() : ServiceFramework("IBcatService") { | 127 | explicit IBcatService(Core::System& system_, Backend& backend_) |
| 128 | : ServiceFramework("IBcatService"), system{system_}, backend{backend_} { | ||
| 129 | // clang-format off | ||
| 15 | static const FunctionInfo functions[] = { | 130 | static const FunctionInfo functions[] = { |
| 16 | {10100, nullptr, "RequestSyncDeliveryCache"}, | 131 | {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"}, |
| 17 | {10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"}, | 132 | {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"}, |
| 18 | {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, | 133 | {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, |
| 19 | {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, | 134 | {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, |
| 20 | {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, | 135 | {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, |
| 21 | {30100, nullptr, "SetPassphrase"}, | 136 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, |
| 22 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, | 137 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, |
| 23 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, | 138 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, |
| 24 | {30202, nullptr, "BlockDeliveryTask"}, | 139 | {30202, nullptr, "BlockDeliveryTask"}, |
| 25 | {30203, nullptr, "UnblockDeliveryTask"}, | 140 | {30203, nullptr, "UnblockDeliveryTask"}, |
| 26 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, | 141 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, |
| 27 | {90200, nullptr, "GetDeliveryList"}, | 142 | {90200, nullptr, "GetDeliveryList"}, |
| 28 | {90201, nullptr, "ClearDeliveryCacheStorage"}, | 143 | {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"}, |
| 29 | {90300, nullptr, "GetPushNotificationLog"}, | 144 | {90300, nullptr, "GetPushNotificationLog"}, |
| 30 | }; | 145 | }; |
| 146 | // clang-format on | ||
| 31 | RegisterHandlers(functions); | 147 | RegisterHandlers(functions); |
| 32 | } | 148 | } |
| 149 | |||
| 150 | private: | ||
| 151 | enum class SyncType { | ||
| 152 | Normal, | ||
| 153 | Directory, | ||
| 154 | Count, | ||
| 155 | }; | ||
| 156 | |||
| 157 | std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) { | ||
| 158 | auto& backend{progress.at(static_cast<std::size_t>(type))}; | ||
| 159 | return std::make_shared<IDeliveryCacheProgressService>(backend.GetEvent(), | ||
| 160 | backend.GetImpl()); | ||
| 161 | } | ||
| 162 | |||
| 163 | void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) { | ||
| 164 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 165 | |||
| 166 | backend.Synchronize({system.CurrentProcess()->GetTitleID(), | ||
| 167 | GetCurrentBuildID(system.GetCurrentProcessBuildID())}, | ||
| 168 | progress.at(static_cast<std::size_t>(SyncType::Normal))); | ||
| 169 | |||
| 170 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 171 | rb.Push(RESULT_SUCCESS); | ||
| 172 | rb.PushIpcInterface(CreateProgressService(SyncType::Normal)); | ||
| 173 | } | ||
| 174 | |||
| 175 | void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) { | ||
| 176 | IPC::RequestParser rp{ctx}; | ||
| 177 | const auto name_raw = rp.PopRaw<DirectoryName>(); | ||
| 178 | const auto name = | ||
| 179 | Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); | ||
| 180 | |||
| 181 | LOG_DEBUG(Service_BCAT, "called, name={}", name); | ||
| 182 | |||
| 183 | backend.SynchronizeDirectory({system.CurrentProcess()->GetTitleID(), | ||
| 184 | GetCurrentBuildID(system.GetCurrentProcessBuildID())}, | ||
| 185 | name, | ||
| 186 | progress.at(static_cast<std::size_t>(SyncType::Directory))); | ||
| 187 | |||
| 188 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 189 | rb.Push(RESULT_SUCCESS); | ||
| 190 | rb.PushIpcInterface(CreateProgressService(SyncType::Directory)); | ||
| 191 | } | ||
| 192 | |||
| 193 | void SetPassphrase(Kernel::HLERequestContext& ctx) { | ||
| 194 | IPC::RequestParser rp{ctx}; | ||
| 195 | const auto title_id = rp.PopRaw<u64>(); | ||
| 196 | |||
| 197 | const auto passphrase_raw = ctx.ReadBuffer(); | ||
| 198 | |||
| 199 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, | ||
| 200 | Common::HexToString(passphrase_raw)); | ||
| 201 | |||
| 202 | if (title_id == 0) { | ||
| 203 | LOG_ERROR(Service_BCAT, "Invalid title ID!"); | ||
| 204 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 205 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 206 | } | ||
| 207 | |||
| 208 | if (passphrase_raw.size() > 0x40) { | ||
| 209 | LOG_ERROR(Service_BCAT, "Passphrase too large!"); | ||
| 210 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 211 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 212 | return; | ||
| 213 | } | ||
| 214 | |||
| 215 | Passphrase passphrase{}; | ||
| 216 | std::memcpy(passphrase.data(), passphrase_raw.data(), | ||
| 217 | std::min(passphrase.size(), passphrase_raw.size())); | ||
| 218 | |||
| 219 | backend.SetPassphrase(title_id, passphrase); | ||
| 220 | |||
| 221 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 222 | rb.Push(RESULT_SUCCESS); | ||
| 223 | } | ||
| 224 | |||
| 225 | void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) { | ||
| 226 | IPC::RequestParser rp{ctx}; | ||
| 227 | const auto title_id = rp.PopRaw<u64>(); | ||
| 228 | |||
| 229 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); | ||
| 230 | |||
| 231 | if (title_id == 0) { | ||
| 232 | LOG_ERROR(Service_BCAT, "Invalid title ID!"); | ||
| 233 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 234 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | |||
| 238 | if (!backend.Clear(title_id)) { | ||
| 239 | LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!"); | ||
| 240 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 241 | rb.Push(ERROR_FAILED_CLEAR_CACHE); | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 246 | rb.Push(RESULT_SUCCESS); | ||
| 247 | } | ||
| 248 | |||
| 249 | Core::System& system; | ||
| 250 | Backend& backend; | ||
| 251 | |||
| 252 | std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress{ | ||
| 253 | ProgressServiceBackend{"Normal"}, | ||
| 254 | ProgressServiceBackend{"Directory"}, | ||
| 255 | }; | ||
| 33 | }; | 256 | }; |
| 34 | 257 | ||
| 35 | void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { | 258 | void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { |
| @@ -37,20 +260,332 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { | |||
| 37 | 260 | ||
| 38 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 261 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 39 | rb.Push(RESULT_SUCCESS); | 262 | rb.Push(RESULT_SUCCESS); |
| 40 | rb.PushIpcInterface<IBcatService>(); | 263 | rb.PushIpcInterface<IBcatService>(system, *backend); |
| 264 | } | ||
| 265 | |||
| 266 | class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> { | ||
| 267 | public: | ||
| 268 | IDeliveryCacheFileService(FileSys::VirtualDir root_) | ||
| 269 | : ServiceFramework{"IDeliveryCacheFileService"}, root(std::move(root_)) { | ||
| 270 | // clang-format off | ||
| 271 | static const FunctionInfo functions[] = { | ||
| 272 | {0, &IDeliveryCacheFileService::Open, "Open"}, | ||
| 273 | {1, &IDeliveryCacheFileService::Read, "Read"}, | ||
| 274 | {2, &IDeliveryCacheFileService::GetSize, "GetSize"}, | ||
| 275 | {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"}, | ||
| 276 | }; | ||
| 277 | // clang-format on | ||
| 278 | |||
| 279 | RegisterHandlers(functions); | ||
| 280 | } | ||
| 281 | |||
| 282 | private: | ||
| 283 | void Open(Kernel::HLERequestContext& ctx) { | ||
| 284 | IPC::RequestParser rp{ctx}; | ||
| 285 | const auto dir_name_raw = rp.PopRaw<DirectoryName>(); | ||
| 286 | const auto file_name_raw = rp.PopRaw<FileName>(); | ||
| 287 | |||
| 288 | const auto dir_name = | ||
| 289 | Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size()); | ||
| 290 | const auto file_name = | ||
| 291 | Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size()); | ||
| 292 | |||
| 293 | LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name); | ||
| 294 | |||
| 295 | if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw)) | ||
| 296 | return; | ||
| 297 | |||
| 298 | if (current_file != nullptr) { | ||
| 299 | LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); | ||
| 300 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 301 | rb.Push(ERROR_ENTITY_ALREADY_OPEN); | ||
| 302 | return; | ||
| 303 | } | ||
| 304 | |||
| 305 | const auto dir = root->GetSubdirectory(dir_name); | ||
| 306 | |||
| 307 | if (dir == nullptr) { | ||
| 308 | LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name); | ||
| 309 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 310 | rb.Push(ERROR_FAILED_OPEN_ENTITY); | ||
| 311 | return; | ||
| 312 | } | ||
| 313 | |||
| 314 | current_file = dir->GetFile(file_name); | ||
| 315 | |||
| 316 | if (current_file == nullptr) { | ||
| 317 | LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name); | ||
| 318 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 319 | rb.Push(ERROR_FAILED_OPEN_ENTITY); | ||
| 320 | return; | ||
| 321 | } | ||
| 322 | |||
| 323 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 324 | rb.Push(RESULT_SUCCESS); | ||
| 325 | } | ||
| 326 | |||
| 327 | void Read(Kernel::HLERequestContext& ctx) { | ||
| 328 | IPC::RequestParser rp{ctx}; | ||
| 329 | const auto offset{rp.PopRaw<u64>()}; | ||
| 330 | |||
| 331 | auto size = ctx.GetWriteBufferSize(); | ||
| 332 | |||
| 333 | LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size); | ||
| 334 | |||
| 335 | if (current_file == nullptr) { | ||
| 336 | LOG_ERROR(Service_BCAT, "There is no file currently open!"); | ||
| 337 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 338 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 339 | } | ||
| 340 | |||
| 341 | size = std::min<u64>(current_file->GetSize() - offset, size); | ||
| 342 | const auto buffer = current_file->ReadBytes(size, offset); | ||
| 343 | ctx.WriteBuffer(buffer); | ||
| 344 | |||
| 345 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 346 | rb.Push(RESULT_SUCCESS); | ||
| 347 | rb.Push<u64>(buffer.size()); | ||
| 348 | } | ||
| 349 | |||
| 350 | void GetSize(Kernel::HLERequestContext& ctx) { | ||
| 351 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 352 | |||
| 353 | if (current_file == nullptr) { | ||
| 354 | LOG_ERROR(Service_BCAT, "There is no file currently open!"); | ||
| 355 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 356 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 357 | } | ||
| 358 | |||
| 359 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 360 | rb.Push(RESULT_SUCCESS); | ||
| 361 | rb.Push<u64>(current_file->GetSize()); | ||
| 362 | } | ||
| 363 | |||
| 364 | void GetDigest(Kernel::HLERequestContext& ctx) { | ||
| 365 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 366 | |||
| 367 | if (current_file == nullptr) { | ||
| 368 | LOG_ERROR(Service_BCAT, "There is no file currently open!"); | ||
| 369 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 370 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 371 | } | ||
| 372 | |||
| 373 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 374 | rb.Push(RESULT_SUCCESS); | ||
| 375 | rb.PushRaw(DigestFile(current_file)); | ||
| 376 | } | ||
| 377 | |||
| 378 | FileSys::VirtualDir root; | ||
| 379 | FileSys::VirtualFile current_file; | ||
| 380 | }; | ||
| 381 | |||
| 382 | class IDeliveryCacheDirectoryService final | ||
| 383 | : public ServiceFramework<IDeliveryCacheDirectoryService> { | ||
| 384 | public: | ||
| 385 | IDeliveryCacheDirectoryService(FileSys::VirtualDir root_) | ||
| 386 | : ServiceFramework{"IDeliveryCacheDirectoryService"}, root(std::move(root_)) { | ||
| 387 | // clang-format off | ||
| 388 | static const FunctionInfo functions[] = { | ||
| 389 | {0, &IDeliveryCacheDirectoryService::Open, "Open"}, | ||
| 390 | {1, &IDeliveryCacheDirectoryService::Read, "Read"}, | ||
| 391 | {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"}, | ||
| 392 | }; | ||
| 393 | // clang-format on | ||
| 394 | |||
| 395 | RegisterHandlers(functions); | ||
| 396 | } | ||
| 397 | |||
| 398 | private: | ||
| 399 | void Open(Kernel::HLERequestContext& ctx) { | ||
| 400 | IPC::RequestParser rp{ctx}; | ||
| 401 | const auto name_raw = rp.PopRaw<DirectoryName>(); | ||
| 402 | const auto name = | ||
| 403 | Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); | ||
| 404 | |||
| 405 | LOG_DEBUG(Service_BCAT, "called, name={}", name); | ||
| 406 | |||
| 407 | if (!VerifyNameValidDir(ctx, name_raw)) | ||
| 408 | return; | ||
| 409 | |||
| 410 | if (current_dir != nullptr) { | ||
| 411 | LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); | ||
| 412 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 413 | rb.Push(ERROR_ENTITY_ALREADY_OPEN); | ||
| 414 | return; | ||
| 415 | } | ||
| 416 | |||
| 417 | current_dir = root->GetSubdirectory(name); | ||
| 418 | |||
| 419 | if (current_dir == nullptr) { | ||
| 420 | LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name); | ||
| 421 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 422 | rb.Push(ERROR_FAILED_OPEN_ENTITY); | ||
| 423 | return; | ||
| 424 | } | ||
| 425 | |||
| 426 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 427 | rb.Push(RESULT_SUCCESS); | ||
| 428 | } | ||
| 429 | |||
| 430 | void Read(Kernel::HLERequestContext& ctx) { | ||
| 431 | auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry); | ||
| 432 | |||
| 433 | LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size); | ||
| 434 | |||
| 435 | if (current_dir == nullptr) { | ||
| 436 | LOG_ERROR(Service_BCAT, "There is no open directory!"); | ||
| 437 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 438 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 439 | return; | ||
| 440 | } | ||
| 441 | |||
| 442 | const auto files = current_dir->GetFiles(); | ||
| 443 | write_size = std::min<u64>(write_size, files.size()); | ||
| 444 | std::vector<DeliveryCacheDirectoryEntry> entries(write_size); | ||
| 445 | std::transform( | ||
| 446 | files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) { | ||
| 447 | FileName name{}; | ||
| 448 | std::memcpy(name.data(), file->GetName().data(), | ||
| 449 | std::min(file->GetName().size(), name.size())); | ||
| 450 | return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)}; | ||
| 451 | }); | ||
| 452 | |||
| 453 | ctx.WriteBuffer(entries); | ||
| 454 | |||
| 455 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 456 | rb.Push(RESULT_SUCCESS); | ||
| 457 | rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry))); | ||
| 458 | } | ||
| 459 | |||
| 460 | void GetCount(Kernel::HLERequestContext& ctx) { | ||
| 461 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 462 | |||
| 463 | if (current_dir == nullptr) { | ||
| 464 | LOG_ERROR(Service_BCAT, "There is no open directory!"); | ||
| 465 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 466 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 467 | return; | ||
| 468 | } | ||
| 469 | |||
| 470 | const auto files = current_dir->GetFiles(); | ||
| 471 | |||
| 472 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 473 | rb.Push(RESULT_SUCCESS); | ||
| 474 | rb.Push(static_cast<u32>(files.size())); | ||
| 475 | } | ||
| 476 | |||
| 477 | FileSys::VirtualDir root; | ||
| 478 | FileSys::VirtualDir current_dir; | ||
| 479 | }; | ||
| 480 | |||
| 481 | class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> { | ||
| 482 | public: | ||
| 483 | IDeliveryCacheStorageService(FileSys::VirtualDir root_) | ||
| 484 | : ServiceFramework{"IDeliveryCacheStorageService"}, root(std::move(root_)) { | ||
| 485 | // clang-format off | ||
| 486 | static const FunctionInfo functions[] = { | ||
| 487 | {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"}, | ||
| 488 | {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"}, | ||
| 489 | {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"}, | ||
| 490 | }; | ||
| 491 | // clang-format on | ||
| 492 | |||
| 493 | RegisterHandlers(functions); | ||
| 494 | |||
| 495 | for (const auto& subdir : root->GetSubdirectories()) { | ||
| 496 | DirectoryName name{}; | ||
| 497 | std::memcpy(name.data(), subdir->GetName().data(), | ||
| 498 | std::min(sizeof(DirectoryName) - 1, subdir->GetName().size())); | ||
| 499 | entries.push_back(name); | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | private: | ||
| 504 | void CreateFileService(Kernel::HLERequestContext& ctx) { | ||
| 505 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 506 | |||
| 507 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 508 | rb.Push(RESULT_SUCCESS); | ||
| 509 | rb.PushIpcInterface<IDeliveryCacheFileService>(root); | ||
| 510 | } | ||
| 511 | |||
| 512 | void CreateDirectoryService(Kernel::HLERequestContext& ctx) { | ||
| 513 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 514 | |||
| 515 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 516 | rb.Push(RESULT_SUCCESS); | ||
| 517 | rb.PushIpcInterface<IDeliveryCacheDirectoryService>(root); | ||
| 518 | } | ||
| 519 | |||
| 520 | void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) { | ||
| 521 | auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName); | ||
| 522 | |||
| 523 | LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); | ||
| 524 | |||
| 525 | size = std::min<u64>(size, entries.size() - next_read_index); | ||
| 526 | ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName)); | ||
| 527 | next_read_index += size; | ||
| 528 | |||
| 529 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 530 | rb.Push(RESULT_SUCCESS); | ||
| 531 | rb.Push(static_cast<u32>(size)); | ||
| 532 | } | ||
| 533 | |||
| 534 | FileSys::VirtualDir root; | ||
| 535 | std::vector<DirectoryName> entries; | ||
| 536 | u64 next_read_index = 0; | ||
| 537 | }; | ||
| 538 | |||
| 539 | void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) { | ||
| 540 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 541 | |||
| 542 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 543 | rb.Push(RESULT_SUCCESS); | ||
| 544 | rb.PushIpcInterface<IDeliveryCacheStorageService>( | ||
| 545 | fsc.GetBCATDirectory(system.CurrentProcess()->GetTitleID())); | ||
| 546 | } | ||
| 547 | |||
| 548 | void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( | ||
| 549 | Kernel::HLERequestContext& ctx) { | ||
| 550 | IPC::RequestParser rp{ctx}; | ||
| 551 | const auto title_id = rp.PopRaw<u64>(); | ||
| 552 | |||
| 553 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); | ||
| 554 | |||
| 555 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 556 | rb.Push(RESULT_SUCCESS); | ||
| 557 | rb.PushIpcInterface<IDeliveryCacheStorageService>(fsc.GetBCATDirectory(title_id)); | ||
| 558 | } | ||
| 559 | |||
| 560 | std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) { | ||
| 561 | const auto backend = Settings::values.bcat_backend; | ||
| 562 | |||
| 563 | #ifdef YUZU_ENABLE_BOXCAT | ||
| 564 | if (backend == "boxcat") | ||
| 565 | return std::make_unique<Boxcat>(std::move(getter)); | ||
| 566 | #endif | ||
| 567 | |||
| 568 | return std::make_unique<NullBackend>(std::move(getter)); | ||
| 41 | } | 569 | } |
| 42 | 570 | ||
| 43 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 571 | Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, |
| 44 | : ServiceFramework(name), module(std::move(module)) {} | 572 | FileSystem::FileSystemController& fsc_, const char* name) |
| 573 | : ServiceFramework(name), fsc{fsc_}, module{std::move(module_)}, | ||
| 574 | backend{CreateBackendFromSettings([&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })}, | ||
| 575 | system{system_} {} | ||
| 45 | 576 | ||
| 46 | Module::Interface::~Interface() = default; | 577 | Module::Interface::~Interface() = default; |
| 47 | 578 | ||
| 48 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 579 | void InstallInterfaces(Core::System& system) { |
| 49 | auto module = std::make_shared<Module>(); | 580 | auto module = std::make_shared<Module>(); |
| 50 | std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager); | 581 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a") |
| 51 | std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager); | 582 | ->InstallAsService(system.ServiceManager()); |
| 52 | std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager); | 583 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m") |
| 53 | std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager); | 584 | ->InstallAsService(system.ServiceManager()); |
| 585 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u") | ||
| 586 | ->InstallAsService(system.ServiceManager()); | ||
| 587 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s") | ||
| 588 | ->InstallAsService(system.ServiceManager()); | ||
| 54 | } | 589 | } |
| 55 | 590 | ||
| 56 | } // namespace Service::BCAT | 591 | } // namespace Service::BCAT |
diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h index f0d63cab0..e4ba23ba0 100644 --- a/src/core/hle/service/bcat/module.h +++ b/src/core/hle/service/bcat/module.h | |||
| @@ -6,23 +6,46 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 8 | 8 | ||
| 9 | namespace Service::BCAT { | 9 | namespace Core { |
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service { | ||
| 14 | |||
| 15 | namespace FileSystem { | ||
| 16 | class FileSystemController; | ||
| 17 | } // namespace FileSystem | ||
| 18 | |||
| 19 | namespace BCAT { | ||
| 20 | |||
| 21 | class Backend; | ||
| 10 | 22 | ||
| 11 | class Module final { | 23 | class Module final { |
| 12 | public: | 24 | public: |
| 13 | class Interface : public ServiceFramework<Interface> { | 25 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 26 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 27 | explicit Interface(Core::System& system_, std::shared_ptr<Module> module_, |
| 28 | FileSystem::FileSystemController& fsc_, const char* name); | ||
| 16 | ~Interface() override; | 29 | ~Interface() override; |
| 17 | 30 | ||
| 18 | void CreateBcatService(Kernel::HLERequestContext& ctx); | 31 | void CreateBcatService(Kernel::HLERequestContext& ctx); |
| 32 | void CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx); | ||
| 33 | void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx); | ||
| 19 | 34 | ||
| 20 | protected: | 35 | protected: |
| 36 | FileSystem::FileSystemController& fsc; | ||
| 37 | |||
| 21 | std::shared_ptr<Module> module; | 38 | std::shared_ptr<Module> module; |
| 39 | std::unique_ptr<Backend> backend; | ||
| 40 | |||
| 41 | private: | ||
| 42 | Core::System& system; | ||
| 22 | }; | 43 | }; |
| 23 | }; | 44 | }; |
| 24 | 45 | ||
| 25 | /// Registers all BCAT services with the specified service manager. | 46 | /// Registers all BCAT services with the specified service manager. |
| 26 | void InstallInterfaces(SM::ServiceManager& service_manager); | 47 | void InstallInterfaces(Core::System& system); |
| 48 | |||
| 49 | } // namespace BCAT | ||
| 27 | 50 | ||
| 28 | } // namespace Service::BCAT | 51 | } // namespace Service |
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index af70d174d..f77ddd739 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -128,7 +128,7 @@ private: | |||
| 128 | void CountCommonTicket(Kernel::HLERequestContext& ctx) { | 128 | void CountCommonTicket(Kernel::HLERequestContext& ctx) { |
| 129 | LOG_DEBUG(Service_ETicket, "called"); | 129 | LOG_DEBUG(Service_ETicket, "called"); |
| 130 | 130 | ||
| 131 | const auto count = keys.GetCommonTickets().size(); | 131 | const u32 count = static_cast<u32>(keys.GetCommonTickets().size()); |
| 132 | 132 | ||
| 133 | IPC::ResponseBuilder rb{ctx, 3}; | 133 | IPC::ResponseBuilder rb{ctx, 3}; |
| 134 | rb.Push(RESULT_SUCCESS); | 134 | rb.Push(RESULT_SUCCESS); |
| @@ -138,7 +138,7 @@ private: | |||
| 138 | void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) { | 138 | void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) { |
| 139 | LOG_DEBUG(Service_ETicket, "called"); | 139 | LOG_DEBUG(Service_ETicket, "called"); |
| 140 | 140 | ||
| 141 | const auto count = keys.GetPersonalizedTickets().size(); | 141 | const u32 count = static_cast<u32>(keys.GetPersonalizedTickets().size()); |
| 142 | 142 | ||
| 143 | IPC::ResponseBuilder rb{ctx, 3}; | 143 | IPC::ResponseBuilder rb{ctx, 3}; |
| 144 | rb.Push(RESULT_SUCCESS); | 144 | rb.Push(RESULT_SUCCESS); |
| @@ -150,7 +150,7 @@ private: | |||
| 150 | if (keys.GetCommonTickets().empty()) | 150 | if (keys.GetCommonTickets().empty()) |
| 151 | out_entries = 0; | 151 | out_entries = 0; |
| 152 | else | 152 | else |
| 153 | out_entries = ctx.GetWriteBufferSize() / sizeof(u128); | 153 | out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128)); |
| 154 | 154 | ||
| 155 | LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); | 155 | LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); |
| 156 | 156 | ||
| @@ -160,7 +160,7 @@ private: | |||
| 160 | std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), | 160 | std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), |
| 161 | [](const auto& pair) { return pair.first; }); | 161 | [](const auto& pair) { return pair.first; }); |
| 162 | 162 | ||
| 163 | out_entries = std::min<u32>(ids.size(), out_entries); | 163 | out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries)); |
| 164 | ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); | 164 | ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); |
| 165 | 165 | ||
| 166 | IPC::ResponseBuilder rb{ctx, 3}; | 166 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -173,7 +173,7 @@ private: | |||
| 173 | if (keys.GetPersonalizedTickets().empty()) | 173 | if (keys.GetPersonalizedTickets().empty()) |
| 174 | out_entries = 0; | 174 | out_entries = 0; |
| 175 | else | 175 | else |
| 176 | out_entries = ctx.GetWriteBufferSize() / sizeof(u128); | 176 | out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128)); |
| 177 | 177 | ||
| 178 | LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); | 178 | LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); |
| 179 | 179 | ||
| @@ -183,7 +183,7 @@ private: | |||
| 183 | std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), | 183 | std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), |
| 184 | [](const auto& pair) { return pair.first; }); | 184 | [](const auto& pair) { return pair.first; }); |
| 185 | 185 | ||
| 186 | out_entries = std::min<u32>(ids.size(), out_entries); | 186 | out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries)); |
| 187 | ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); | 187 | ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); |
| 188 | 188 | ||
| 189 | IPC::ResponseBuilder rb{ctx, 3}; | 189 | IPC::ResponseBuilder rb{ctx, 3}; |
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index b2ebf6240..2546d7595 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp | |||
| @@ -66,7 +66,7 @@ enum class FatalType : u32 { | |||
| 66 | 66 | ||
| 67 | static void GenerateErrorReport(Core::System& system, ResultCode error_code, | 67 | static void GenerateErrorReport(Core::System& system, ResultCode error_code, |
| 68 | const FatalInfo& info) { | 68 | const FatalInfo& info) { |
| 69 | const auto title_id = Core::CurrentProcess()->GetTitleID(); | 69 | const auto title_id = system.CurrentProcess()->GetTitleID(); |
| 70 | std::string crash_report = fmt::format( | 70 | std::string crash_report = fmt::format( |
| 71 | "Yuzu {}-{} crash report\n" | 71 | "Yuzu {}-{} crash report\n" |
| 72 | "Title ID: {:016x}\n" | 72 | "Title ID: {:016x}\n" |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 14cd0e322..11e5c56b7 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -241,7 +241,7 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | |||
| 241 | return FileSys::ERROR_PATH_NOT_FOUND; | 241 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 242 | } | 242 | } |
| 243 | 243 | ||
| 244 | FileSystemController::FileSystemController() = default; | 244 | FileSystemController::FileSystemController(Core::System& system_) : system{system_} {} |
| 245 | 245 | ||
| 246 | FileSystemController::~FileSystemController() = default; | 246 | FileSystemController::~FileSystemController() = default; |
| 247 | 247 | ||
| @@ -290,7 +290,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() | |||
| 290 | return ResultCode(-1); | 290 | return ResultCode(-1); |
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | return romfs_factory->OpenCurrentProcess(); | 293 | return romfs_factory->OpenCurrentProcess(system.CurrentProcess()->GetTitleID()); |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( | 296 | ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( |
| @@ -447,10 +447,10 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy | |||
| 447 | FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; | 447 | FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; |
| 448 | 448 | ||
| 449 | FileSys::NACP nacp; | 449 | FileSys::NACP nacp; |
| 450 | const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp); | 450 | const auto res = system.GetAppLoader().ReadControlData(nacp); |
| 451 | 451 | ||
| 452 | if (res != Loader::ResultStatus::Success) { | 452 | if (res != Loader::ResultStatus::Success) { |
| 453 | FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()}; | 453 | FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; |
| 454 | auto [nacp_unique, discard] = pm.GetControlMetadata(); | 454 | auto [nacp_unique, discard] = pm.GetControlMetadata(); |
| 455 | 455 | ||
| 456 | if (nacp_unique != nullptr) { | 456 | if (nacp_unique != nullptr) { |
| @@ -674,6 +674,15 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) | |||
| 674 | return bis_factory->GetModificationDumpRoot(title_id); | 674 | return bis_factory->GetModificationDumpRoot(title_id); |
| 675 | } | 675 | } |
| 676 | 676 | ||
| 677 | FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const { | ||
| 678 | LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id); | ||
| 679 | |||
| 680 | if (bis_factory == nullptr) | ||
| 681 | return nullptr; | ||
| 682 | |||
| 683 | return bis_factory->GetBCATDirectory(title_id); | ||
| 684 | } | ||
| 685 | |||
| 677 | void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | 686 | void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { |
| 678 | if (overwrite) { | 687 | if (overwrite) { |
| 679 | bis_factory = nullptr; | 688 | bis_factory = nullptr; |
| @@ -693,10 +702,10 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove | |||
| 693 | if (bis_factory == nullptr) { | 702 | if (bis_factory == nullptr) { |
| 694 | bis_factory = | 703 | bis_factory = |
| 695 | std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); | 704 | std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); |
| 696 | Core::System::GetInstance().RegisterContentProvider( | 705 | system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND, |
| 697 | FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents()); | 706 | bis_factory->GetSystemNANDContents()); |
| 698 | Core::System::GetInstance().RegisterContentProvider( | 707 | system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND, |
| 699 | FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents()); | 708 | bis_factory->GetUserNANDContents()); |
| 700 | } | 709 | } |
| 701 | 710 | ||
| 702 | if (save_data_factory == nullptr) { | 711 | if (save_data_factory == nullptr) { |
| @@ -705,8 +714,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove | |||
| 705 | 714 | ||
| 706 | if (sdmc_factory == nullptr) { | 715 | if (sdmc_factory == nullptr) { |
| 707 | sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); | 716 | sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); |
| 708 | Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, | 717 | system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, |
| 709 | sdmc_factory->GetSDMCContents()); | 718 | sdmc_factory->GetSDMCContents()); |
| 710 | } | 719 | } |
| 711 | } | 720 | } |
| 712 | 721 | ||
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 3e0c03ec0..1b0a6a949 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -10,6 +10,10 @@ | |||
| 10 | #include "core/file_sys/vfs.h" | 10 | #include "core/file_sys/vfs.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | 12 | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace FileSys { | 17 | namespace FileSys { |
| 14 | class BISFactory; | 18 | class BISFactory; |
| 15 | class RegisteredCache; | 19 | class RegisteredCache; |
| @@ -52,7 +56,7 @@ enum class ImageDirectoryId : u32 { | |||
| 52 | 56 | ||
| 53 | class FileSystemController { | 57 | class FileSystemController { |
| 54 | public: | 58 | public: |
| 55 | FileSystemController(); | 59 | explicit FileSystemController(Core::System& system_); |
| 56 | ~FileSystemController(); | 60 | ~FileSystemController(); |
| 57 | 61 | ||
| 58 | ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); | 62 | ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); |
| @@ -110,6 +114,8 @@ public: | |||
| 110 | FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; | 114 | FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; |
| 111 | FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; | 115 | FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; |
| 112 | 116 | ||
| 117 | FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; | ||
| 118 | |||
| 113 | // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function | 119 | // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function |
| 114 | // above is called. | 120 | // above is called. |
| 115 | void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); | 121 | void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); |
| @@ -123,6 +129,8 @@ private: | |||
| 123 | std::unique_ptr<FileSys::XCI> gamecard; | 129 | std::unique_ptr<FileSys::XCI> gamecard; |
| 124 | std::unique_ptr<FileSys::RegisteredCache> gamecard_registered; | 130 | std::unique_ptr<FileSys::RegisteredCache> gamecard_registered; |
| 125 | std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder; | 131 | std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder; |
| 132 | |||
| 133 | Core::System& system; | ||
| 126 | }; | 134 | }; |
| 127 | 135 | ||
| 128 | void InstallInterfaces(Core::System& system); | 136 | void InstallInterfaces(Core::System& system); |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index eb982ad49..cbd5466c1 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -803,7 +803,7 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 803 | IPC::RequestParser rp{ctx}; | 803 | IPC::RequestParser rp{ctx}; |
| 804 | 804 | ||
| 805 | auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); | 805 | auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); |
| 806 | auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); | 806 | [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); |
| 807 | u128 uid = rp.PopRaw<u128>(); | 807 | u128 uid = rp.PopRaw<u128>(); |
| 808 | 808 | ||
| 809 | LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), | 809 | LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 42b4ee861..75dd9043b 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -237,7 +237,6 @@ private: | |||
| 237 | }; | 237 | }; |
| 238 | 238 | ||
| 239 | Common::UUID uuid; | 239 | Common::UUID uuid; |
| 240 | bool is_event_created = false; | ||
| 241 | Kernel::EventPair notification_event; | 240 | Kernel::EventPair notification_event; |
| 242 | std::queue<SizedNotificationInfo> notifications; | 241 | std::queue<SizedNotificationInfo> notifications; |
| 243 | States states{}; | 242 | States states{}; |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 8e8263f5b..1f2131ec8 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -11,11 +11,10 @@ | |||
| 11 | namespace Service::HID { | 11 | namespace Service::HID { |
| 12 | 12 | ||
| 13 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | 13 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; |
| 14 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | 14 | [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; |
| 15 | enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; | 15 | enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; |
| 16 | 16 | ||
| 17 | Controller_DebugPad::Controller_DebugPad(Core::System& system) | 17 | Controller_DebugPad::Controller_DebugPad(Core::System& system) : ControllerBase(system) {} |
| 18 | : ControllerBase(system), system(system) {} | ||
| 19 | Controller_DebugPad::~Controller_DebugPad() = default; | 18 | Controller_DebugPad::~Controller_DebugPad() = default; |
| 20 | 19 | ||
| 21 | void Controller_DebugPad::OnInit() {} | 20 | void Controller_DebugPad::OnInit() {} |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 6c4de817e..555b29d76 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h | |||
| @@ -89,6 +89,5 @@ private: | |||
| 89 | buttons; | 89 | buttons; |
| 90 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> | 90 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> |
| 91 | analogs; | 91 | analogs; |
| 92 | Core::System& system; | ||
| 93 | }; | 92 | }; |
| 94 | } // namespace Service::HID | 93 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 80da0a0d3..6e990dd00 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -10,8 +10,7 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; |
| 12 | 12 | ||
| 13 | Controller_Gesture::Controller_Gesture(Core::System& system) | 13 | Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {} |
| 14 | : ControllerBase(system), system(system) {} | ||
| 15 | Controller_Gesture::~Controller_Gesture() = default; | 14 | Controller_Gesture::~Controller_Gesture() = default; |
| 16 | 15 | ||
| 17 | void Controller_Gesture::OnInit() {} | 16 | void Controller_Gesture::OnInit() {} |
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 396897527..f650b8338 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h | |||
| @@ -59,6 +59,5 @@ private: | |||
| 59 | std::array<GestureState, 17> gesture_states; | 59 | std::array<GestureState, 17> gesture_states; |
| 60 | }; | 60 | }; |
| 61 | SharedMemory shared_memory{}; | 61 | SharedMemory shared_memory{}; |
| 62 | Core::System& system; | ||
| 63 | }; | 62 | }; |
| 64 | } // namespace Service::HID | 63 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index e587b2e15..358cb9329 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -12,8 +12,7 @@ namespace Service::HID { | |||
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; | 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; |
| 13 | constexpr u8 KEYS_PER_BYTE = 8; | 13 | constexpr u8 KEYS_PER_BYTE = 8; |
| 14 | 14 | ||
| 15 | Controller_Keyboard::Controller_Keyboard(Core::System& system) | 15 | Controller_Keyboard::Controller_Keyboard(Core::System& system) : ControllerBase(system) {} |
| 16 | : ControllerBase(system), system(system) {} | ||
| 17 | Controller_Keyboard::~Controller_Keyboard() = default; | 16 | Controller_Keyboard::~Controller_Keyboard() = default; |
| 18 | 17 | ||
| 19 | void Controller_Keyboard::OnInit() {} | 18 | void Controller_Keyboard::OnInit() {} |
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index ef586f7eb..f3eef5936 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h | |||
| @@ -53,6 +53,5 @@ private: | |||
| 53 | keyboard_keys; | 53 | keyboard_keys; |
| 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> | 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> |
| 55 | keyboard_mods; | 55 | keyboard_mods; |
| 56 | Core::System& system; | ||
| 57 | }; | 56 | }; |
| 58 | } // namespace Service::HID | 57 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 88f2ca4c1..93d88ea50 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | namespace Service::HID { | 11 | namespace Service::HID { |
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; | 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; |
| 13 | 13 | ||
| 14 | Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {} | 14 | Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system) {} |
| 15 | Controller_Mouse::~Controller_Mouse() = default; | 15 | Controller_Mouse::~Controller_Mouse() = default; |
| 16 | 16 | ||
| 17 | void Controller_Mouse::OnInit() {} | 17 | void Controller_Mouse::OnInit() {} |
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index df2da6ae3..357ab7107 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h | |||
| @@ -53,6 +53,5 @@ private: | |||
| 53 | std::unique_ptr<Input::MouseDevice> mouse_device; | 53 | std::unique_ptr<Input::MouseDevice> mouse_device; |
| 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> | 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> |
| 55 | mouse_button_devices; | 55 | mouse_button_devices; |
| 56 | Core::System& system; | ||
| 57 | }; | 56 | }; |
| 58 | } // namespace Service::HID | 57 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 44b668fbf..a2b25a796 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace Service::HID { | 21 | namespace Service::HID { |
| 22 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | 22 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; |
| 23 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | 23 | [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; |
| 24 | constexpr std::size_t NPAD_OFFSET = 0x9A00; | 24 | constexpr std::size_t NPAD_OFFSET = 0x9A00; |
| 25 | constexpr u32 BATTERY_FULL = 2; | 25 | constexpr u32 BATTERY_FULL = 2; |
| 26 | constexpr u32 MAX_NPAD_ID = 7; | 26 | constexpr u32 MAX_NPAD_ID = 7; |
| @@ -105,6 +105,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 105 | controller.joy_styles.raw = 0; // Zero out | 105 | controller.joy_styles.raw = 0; // Zero out |
| 106 | controller.device_type.raw = 0; | 106 | controller.device_type.raw = 0; |
| 107 | switch (controller_type) { | 107 | switch (controller_type) { |
| 108 | case NPadControllerType::None: | ||
| 109 | UNREACHABLE(); | ||
| 108 | case NPadControllerType::Handheld: | 110 | case NPadControllerType::Handheld: |
| 109 | controller.joy_styles.handheld.Assign(1); | 111 | controller.joy_styles.handheld.Assign(1); |
| 110 | controller.device_type.handheld.Assign(1); | 112 | controller.device_type.handheld.Assign(1); |
| @@ -165,13 +167,14 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 165 | controller.battery_level[0] = BATTERY_FULL; | 167 | controller.battery_level[0] = BATTERY_FULL; |
| 166 | controller.battery_level[1] = BATTERY_FULL; | 168 | controller.battery_level[1] = BATTERY_FULL; |
| 167 | controller.battery_level[2] = BATTERY_FULL; | 169 | controller.battery_level[2] = BATTERY_FULL; |
| 170 | styleset_changed_events[controller_idx].writable->Signal(); | ||
| 168 | } | 171 | } |
| 169 | 172 | ||
| 170 | void Controller_NPad::OnInit() { | 173 | void Controller_NPad::OnInit() { |
| 171 | auto& kernel = system.Kernel(); | 174 | auto& kernel = system.Kernel(); |
| 172 | for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { | 175 | for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { |
| 173 | styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( | 176 | styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( |
| 174 | kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i)); | 177 | kernel, Kernel::ResetType::Manual, fmt::format("npad:NpadStyleSetChanged_{}", i)); |
| 175 | } | 178 | } |
| 176 | 179 | ||
| 177 | if (!IsControllerActivated()) { | 180 | if (!IsControllerActivated()) { |
| @@ -238,7 +241,7 @@ void Controller_NPad::OnRelease() {} | |||
| 238 | 241 | ||
| 239 | void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | 242 | void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { |
| 240 | const auto controller_idx = NPadIdToIndex(npad_id); | 243 | const auto controller_idx = NPadIdToIndex(npad_id); |
| 241 | const auto controller_type = connected_controllers[controller_idx].type; | 244 | [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type; |
| 242 | if (!connected_controllers[controller_idx].is_connected) { | 245 | if (!connected_controllers[controller_idx].is_connected) { |
| 243 | return; | 246 | return; |
| 244 | } | 247 | } |
| @@ -345,6 +348,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 345 | libnx_entry.connection_status.raw = 0; | 348 | libnx_entry.connection_status.raw = 0; |
| 346 | 349 | ||
| 347 | switch (controller_type) { | 350 | switch (controller_type) { |
| 351 | case NPadControllerType::None: | ||
| 352 | UNREACHABLE(); | ||
| 348 | case NPadControllerType::Handheld: | 353 | case NPadControllerType::Handheld: |
| 349 | handheld_entry.connection_status.raw = 0; | 354 | handheld_entry.connection_status.raw = 0; |
| 350 | handheld_entry.connection_status.IsWired.Assign(1); | 355 | handheld_entry.connection_status.IsWired.Assign(1); |
| @@ -433,7 +438,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { | |||
| 433 | supported_npad_id_types.clear(); | 438 | supported_npad_id_types.clear(); |
| 434 | supported_npad_id_types.resize(length / sizeof(u32)); | 439 | supported_npad_id_types.resize(length / sizeof(u32)); |
| 435 | std::memcpy(supported_npad_id_types.data(), data, length); | 440 | std::memcpy(supported_npad_id_types.data(), data, length); |
| 436 | bool had_controller_update = false; | ||
| 437 | for (std::size_t i = 0; i < connected_controllers.size(); i++) { | 441 | for (std::size_t i = 0; i < connected_controllers.size(); i++) { |
| 438 | auto& controller = connected_controllers[i]; | 442 | auto& controller = connected_controllers[i]; |
| 439 | if (!controller.is_connected) { | 443 | if (!controller.is_connected) { |
| @@ -452,10 +456,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { | |||
| 452 | controller.type = requested_controller; | 456 | controller.type = requested_controller; |
| 453 | InitNewlyAddedControler(i); | 457 | InitNewlyAddedControler(i); |
| 454 | } | 458 | } |
| 455 | had_controller_update = true; | ||
| 456 | } | ||
| 457 | if (had_controller_update) { | ||
| 458 | styleset_changed_events[i].writable->Signal(); | ||
| 459 | } | 459 | } |
| 460 | } | 460 | } |
| 461 | } | 461 | } |
| @@ -481,7 +481,6 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) | |||
| 481 | const std::size_t npad_index = NPadIdToIndex(npad_id); | 481 | const std::size_t npad_index = NPadIdToIndex(npad_id); |
| 482 | ASSERT(npad_index < shared_memory_entries.size()); | 482 | ASSERT(npad_index < shared_memory_entries.size()); |
| 483 | if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { | 483 | if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { |
| 484 | styleset_changed_events[npad_index].writable->Signal(); | ||
| 485 | shared_memory_entries[npad_index].pad_assignment = assignment_mode; | 484 | shared_memory_entries[npad_index].pad_assignment = assignment_mode; |
| 486 | } | 485 | } |
| 487 | } | 486 | } |
| @@ -507,7 +506,6 @@ Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEven | |||
| 507 | // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should | 506 | // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should |
| 508 | // be signalled at least once, and signaled after a new controller is connected? | 507 | // be signalled at least once, and signaled after a new controller is connected? |
| 509 | const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; | 508 | const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; |
| 510 | styleset_event.writable->Signal(); | ||
| 511 | return styleset_event.readable; | 509 | return styleset_event.readable; |
| 512 | } | 510 | } |
| 513 | 511 | ||
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 9b829341e..9e527d176 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp | |||
| @@ -9,8 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | 11 | ||
| 12 | Controller_Stubbed::Controller_Stubbed(Core::System& system) | 12 | Controller_Stubbed::Controller_Stubbed(Core::System& system) : ControllerBase(system) {} |
| 13 | : ControllerBase(system), system(system) {} | ||
| 14 | Controller_Stubbed::~Controller_Stubbed() = default; | 13 | Controller_Stubbed::~Controller_Stubbed() = default; |
| 15 | 14 | ||
| 16 | void Controller_Stubbed::OnInit() {} | 15 | void Controller_Stubbed::OnInit() {} |
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 37d7d8538..4fa83ac85 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h | |||
| @@ -30,6 +30,5 @@ public: | |||
| 30 | private: | 30 | private: |
| 31 | bool smart_update{}; | 31 | bool smart_update{}; |
| 32 | std::size_t common_offset{}; | 32 | std::size_t common_offset{}; |
| 33 | Core::System& system; | ||
| 34 | }; | 33 | }; |
| 35 | } // namespace Service::HID | 34 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 25912fd69..1c6e55566 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -13,8 +13,7 @@ | |||
| 13 | namespace Service::HID { | 13 | namespace Service::HID { |
| 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; | 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; |
| 15 | 15 | ||
| 16 | Controller_Touchscreen::Controller_Touchscreen(Core::System& system) | 16 | Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {} |
| 17 | : ControllerBase(system), system(system) {} | ||
| 18 | Controller_Touchscreen::~Controller_Touchscreen() = default; | 17 | Controller_Touchscreen::~Controller_Touchscreen() = default; |
| 19 | 18 | ||
| 20 | void Controller_Touchscreen::OnInit() {} | 19 | void Controller_Touchscreen::OnInit() {} |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 3429c84db..a1d97269e 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -69,6 +69,5 @@ private: | |||
| 69 | TouchScreenSharedMemory shared_memory{}; | 69 | TouchScreenSharedMemory shared_memory{}; |
| 70 | std::unique_ptr<Input::TouchDevice> touch_device; | 70 | std::unique_ptr<Input::TouchDevice> touch_device; |
| 71 | s64_le last_touch{}; | 71 | s64_le last_touch{}; |
| 72 | Core::System& system; | ||
| 73 | }; | 72 | }; |
| 74 | } // namespace Service::HID | 73 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 1bce044b4..27511b27b 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; |
| 12 | 12 | ||
| 13 | Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {} | 13 | Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system) {} |
| 14 | Controller_XPad::~Controller_XPad() = default; | 14 | Controller_XPad::~Controller_XPad() = default; |
| 15 | 15 | ||
| 16 | void Controller_XPad::OnInit() {} | 16 | void Controller_XPad::OnInit() {} |
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index c445ebec0..ad229787c 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h | |||
| @@ -56,6 +56,5 @@ private: | |||
| 56 | }; | 56 | }; |
| 57 | static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); | 57 | static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); |
| 58 | SharedMemory shared_memory{}; | 58 | SharedMemory shared_memory{}; |
| 59 | Core::System& system; | ||
| 60 | }; | 59 | }; |
| 61 | } // namespace Service::HID | 60 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 8d76ba746..ba1da4181 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -38,8 +38,10 @@ namespace Service::HID { | |||
| 38 | // Updating period for each HID device. | 38 | // Updating period for each HID device. |
| 39 | // TODO(ogniK): Find actual polling rate of hid | 39 | // TODO(ogniK): Find actual polling rate of hid |
| 40 | constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66); | 40 | constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66); |
| 41 | constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); | 41 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = |
| 42 | constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); | 42 | static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); |
| 43 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = | ||
| 44 | static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); | ||
| 43 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 44 | 46 | ||
| 45 | IAppletResource::IAppletResource(Core::System& system) | 47 | IAppletResource::IAppletResource(Core::System& system) |
| @@ -193,7 +195,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 193 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, | 195 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, |
| 194 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, | 196 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, |
| 195 | {103, &Hid::ActivateNpad, "ActivateNpad"}, | 197 | {103, &Hid::ActivateNpad, "ActivateNpad"}, |
| 196 | {104, nullptr, "DeactivateNpad"}, | 198 | {104, &Hid::DeactivateNpad, "DeactivateNpad"}, |
| 197 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, | 199 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, |
| 198 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, | 200 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, |
| 199 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, | 201 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, |
| @@ -468,6 +470,17 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) { | |||
| 468 | applet_resource->ActivateController(HidController::NPad); | 470 | applet_resource->ActivateController(HidController::NPad); |
| 469 | } | 471 | } |
| 470 | 472 | ||
| 473 | void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) { | ||
| 474 | IPC::RequestParser rp{ctx}; | ||
| 475 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 476 | |||
| 477 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 478 | |||
| 479 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 480 | rb.Push(RESULT_SUCCESS); | ||
| 481 | applet_resource->DeactivateController(HidController::NPad); | ||
| 482 | } | ||
| 483 | |||
| 471 | void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { | 484 | void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { |
| 472 | IPC::RequestParser rp{ctx}; | 485 | IPC::RequestParser rp{ctx}; |
| 473 | const auto npad_id{rp.Pop<u32>()}; | 486 | const auto npad_id{rp.Pop<u32>()}; |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 35b663679..01852e019 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -99,6 +99,7 @@ private: | |||
| 99 | void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); | 99 | void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); |
| 100 | void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx); | 100 | void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx); |
| 101 | void ActivateNpad(Kernel::HLERequestContext& ctx); | 101 | void ActivateNpad(Kernel::HLERequestContext& ctx); |
| 102 | void DeactivateNpad(Kernel::HLERequestContext& ctx); | ||
| 102 | void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx); | 103 | void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx); |
| 103 | void DisconnectNpad(Kernel::HLERequestContext& ctx); | 104 | void DisconnectNpad(Kernel::HLERequestContext& ctx); |
| 104 | void GetPlayerLedPattern(Kernel::HLERequestContext& ctx); | 105 | void GetPlayerLedPattern(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 3164ca26e..499376bfc 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -163,7 +163,7 @@ public: | |||
| 163 | return; | 163 | return; |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | if (Core::CurrentProcess()->GetTitleID() != header.title_id) { | 166 | if (system.CurrentProcess()->GetTitleID() != header.title_id) { |
| 167 | LOG_ERROR(Service_LDR, | 167 | LOG_ERROR(Service_LDR, |
| 168 | "Attempting to load NRR with title ID other than current process. (actual " | 168 | "Attempting to load NRR with title ID other than current process. (actual " |
| 169 | "{:016X})!", | 169 | "{:016X})!", |
| @@ -327,7 +327,7 @@ public: | |||
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | // Load NRO as new executable module | 329 | // Load NRO as new executable module |
| 330 | auto* process = Core::CurrentProcess(); | 330 | auto* process = system.CurrentProcess(); |
| 331 | auto& vm_manager = process->VMManager(); | 331 | auto& vm_manager = process->VMManager(); |
| 332 | auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); | 332 | auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); |
| 333 | 333 | ||
| @@ -411,7 +411,7 @@ public: | |||
| 411 | return; | 411 | return; |
| 412 | } | 412 | } |
| 413 | 413 | ||
| 414 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 414 | auto& vm_manager = system.CurrentProcess()->VMManager(); |
| 415 | const auto& nro_info = iter->second; | 415 | const auto& nro_info = iter->second; |
| 416 | 416 | ||
| 417 | // Unmap the mirrored memory | 417 | // Unmap the mirrored memory |
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 2a61593e2..435f2d286 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -6,8 +6,10 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | 7 | ||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/scope_exit.h" | ||
| 9 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 10 | #include "core/hle/service/lm/lm.h" | 11 | #include "core/hle/service/lm/lm.h" |
| 12 | #include "core/hle/service/lm/manager.h" | ||
| 11 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 12 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 13 | 15 | ||
| @@ -15,65 +17,16 @@ namespace Service::LM { | |||
| 15 | 17 | ||
| 16 | class ILogger final : public ServiceFramework<ILogger> { | 18 | class ILogger final : public ServiceFramework<ILogger> { |
| 17 | public: | 19 | public: |
| 18 | ILogger() : ServiceFramework("ILogger") { | 20 | ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) { |
| 19 | static const FunctionInfo functions[] = { | 21 | static const FunctionInfo functions[] = { |
| 20 | {0x00000000, &ILogger::Initialize, "Initialize"}, | 22 | {0, &ILogger::Log, "Log"}, |
| 21 | {0x00000001, &ILogger::SetDestination, "SetDestination"}, | 23 | {1, &ILogger::SetDestination, "SetDestination"}, |
| 22 | }; | 24 | }; |
| 23 | RegisterHandlers(functions); | 25 | RegisterHandlers(functions); |
| 24 | } | 26 | } |
| 25 | 27 | ||
| 26 | private: | 28 | private: |
| 27 | struct MessageHeader { | 29 | void Log(Kernel::HLERequestContext& ctx) { |
| 28 | enum Flags : u32_le { | ||
| 29 | IsHead = 1, | ||
| 30 | IsTail = 2, | ||
| 31 | }; | ||
| 32 | enum Severity : u32_le { | ||
| 33 | Trace, | ||
| 34 | Info, | ||
| 35 | Warning, | ||
| 36 | Error, | ||
| 37 | Critical, | ||
| 38 | }; | ||
| 39 | |||
| 40 | u64_le pid; | ||
| 41 | u64_le threadContext; | ||
| 42 | union { | ||
| 43 | BitField<0, 16, Flags> flags; | ||
| 44 | BitField<16, 8, Severity> severity; | ||
| 45 | BitField<24, 8, u32> verbosity; | ||
| 46 | }; | ||
| 47 | u32_le payload_size; | ||
| 48 | |||
| 49 | bool IsHeadLog() const { | ||
| 50 | return flags & Flags::IsHead; | ||
| 51 | } | ||
| 52 | bool IsTailLog() const { | ||
| 53 | return flags & Flags::IsTail; | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); | ||
| 57 | |||
| 58 | /// Log field type | ||
| 59 | enum class Field : u8 { | ||
| 60 | Skip = 1, | ||
| 61 | Message = 2, | ||
| 62 | Line = 3, | ||
| 63 | Filename = 4, | ||
| 64 | Function = 5, | ||
| 65 | Module = 6, | ||
| 66 | Thread = 7, | ||
| 67 | }; | ||
| 68 | |||
| 69 | /** | ||
| 70 | * ILogger::Initialize service function | ||
| 71 | * Inputs: | ||
| 72 | * 0: 0x00000000 | ||
| 73 | * Outputs: | ||
| 74 | * 0: ResultCode | ||
| 75 | */ | ||
| 76 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 77 | // This function only succeeds - Get that out of the way | 30 | // This function only succeeds - Get that out of the way |
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | 31 | IPC::ResponseBuilder rb{ctx, 2}; |
| 79 | rb.Push(RESULT_SUCCESS); | 32 | rb.Push(RESULT_SUCCESS); |
| @@ -85,140 +38,70 @@ private: | |||
| 85 | Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); | 38 | Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); |
| 86 | addr += sizeof(MessageHeader); | 39 | addr += sizeof(MessageHeader); |
| 87 | 40 | ||
| 88 | if (header.IsHeadLog()) { | 41 | FieldMap fields; |
| 89 | log_stream.str(""); | ||
| 90 | log_stream.clear(); | ||
| 91 | } | ||
| 92 | |||
| 93 | // Parse out log metadata | ||
| 94 | u32 line{}; | ||
| 95 | std::string module; | ||
| 96 | std::string message; | ||
| 97 | std::string filename; | ||
| 98 | std::string function; | ||
| 99 | std::string thread; | ||
| 100 | while (addr < end_addr) { | 42 | while (addr < end_addr) { |
| 101 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; | 43 | const auto field = static_cast<Field>(Memory::Read8(addr++)); |
| 102 | const std::size_t length{Memory::Read8(addr++)}; | 44 | const auto length = Memory::Read8(addr++); |
| 103 | 45 | ||
| 104 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { | 46 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { |
| 105 | ++addr; | 47 | ++addr; |
| 106 | } | 48 | } |
| 107 | 49 | ||
| 108 | switch (field) { | 50 | SCOPE_EXIT({ addr += length; }); |
| 109 | case Field::Skip: | ||
| 110 | break; | ||
| 111 | case Field::Message: | ||
| 112 | message = Memory::ReadCString(addr, length); | ||
| 113 | break; | ||
| 114 | case Field::Line: | ||
| 115 | line = Memory::Read32(addr); | ||
| 116 | break; | ||
| 117 | case Field::Filename: | ||
| 118 | filename = Memory::ReadCString(addr, length); | ||
| 119 | break; | ||
| 120 | case Field::Function: | ||
| 121 | function = Memory::ReadCString(addr, length); | ||
| 122 | break; | ||
| 123 | case Field::Module: | ||
| 124 | module = Memory::ReadCString(addr, length); | ||
| 125 | break; | ||
| 126 | case Field::Thread: | ||
| 127 | thread = Memory::ReadCString(addr, length); | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | 51 | ||
| 131 | addr += length; | 52 | if (field == Field::Skip) { |
| 132 | } | 53 | continue; |
| 54 | } | ||
| 133 | 55 | ||
| 134 | // Empty log - nothing to do here | 56 | std::vector<u8> data(length); |
| 135 | if (log_stream.str().empty() && message.empty()) { | 57 | Memory::ReadBlock(addr, data.data(), length); |
| 136 | return; | 58 | fields.emplace(field, std::move(data)); |
| 137 | } | 59 | } |
| 138 | 60 | ||
| 139 | // Format a nicely printable string out of the log metadata | 61 | manager.Log({header, std::move(fields)}); |
| 140 | if (!filename.empty()) { | ||
| 141 | log_stream << filename << ':'; | ||
| 142 | } | ||
| 143 | if (!module.empty()) { | ||
| 144 | log_stream << module << ':'; | ||
| 145 | } | ||
| 146 | if (!function.empty()) { | ||
| 147 | log_stream << function << ':'; | ||
| 148 | } | ||
| 149 | if (line) { | ||
| 150 | log_stream << std::to_string(line) << ':'; | ||
| 151 | } | ||
| 152 | if (!thread.empty()) { | ||
| 153 | log_stream << thread << ':'; | ||
| 154 | } | ||
| 155 | if (log_stream.str().length() > 0 && log_stream.str().back() == ':') { | ||
| 156 | log_stream << ' '; | ||
| 157 | } | ||
| 158 | log_stream << message; | ||
| 159 | |||
| 160 | if (header.IsTailLog()) { | ||
| 161 | switch (header.severity) { | ||
| 162 | case MessageHeader::Severity::Trace: | ||
| 163 | LOG_DEBUG(Debug_Emulated, "{}", log_stream.str()); | ||
| 164 | break; | ||
| 165 | case MessageHeader::Severity::Info: | ||
| 166 | LOG_INFO(Debug_Emulated, "{}", log_stream.str()); | ||
| 167 | break; | ||
| 168 | case MessageHeader::Severity::Warning: | ||
| 169 | LOG_WARNING(Debug_Emulated, "{}", log_stream.str()); | ||
| 170 | break; | ||
| 171 | case MessageHeader::Severity::Error: | ||
| 172 | LOG_ERROR(Debug_Emulated, "{}", log_stream.str()); | ||
| 173 | break; | ||
| 174 | case MessageHeader::Severity::Critical: | ||
| 175 | LOG_CRITICAL(Debug_Emulated, "{}", log_stream.str()); | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | 62 | } |
| 180 | 63 | ||
| 181 | // This service function is intended to be used as a way to | ||
| 182 | // redirect logging output to different destinations, however, | ||
| 183 | // given we always want to see the logging output, it's sufficient | ||
| 184 | // to do nothing and return success here. | ||
| 185 | void SetDestination(Kernel::HLERequestContext& ctx) { | 64 | void SetDestination(Kernel::HLERequestContext& ctx) { |
| 186 | LOG_DEBUG(Service_LM, "called"); | 65 | IPC::RequestParser rp{ctx}; |
| 66 | const auto destination = rp.PopEnum<DestinationFlag>(); | ||
| 67 | |||
| 68 | LOG_DEBUG(Service_LM, "called, destination={:08X}", static_cast<u32>(destination)); | ||
| 69 | |||
| 70 | manager.SetDestination(destination); | ||
| 187 | 71 | ||
| 188 | IPC::ResponseBuilder rb{ctx, 2}; | 72 | IPC::ResponseBuilder rb{ctx, 2}; |
| 189 | rb.Push(RESULT_SUCCESS); | 73 | rb.Push(RESULT_SUCCESS); |
| 190 | } | 74 | } |
| 191 | 75 | ||
| 192 | std::ostringstream log_stream; | 76 | Manager& manager; |
| 193 | }; | 77 | }; |
| 194 | 78 | ||
| 195 | class LM final : public ServiceFramework<LM> { | 79 | class LM final : public ServiceFramework<LM> { |
| 196 | public: | 80 | public: |
| 197 | explicit LM() : ServiceFramework{"lm"} { | 81 | explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) { |
| 82 | // clang-format off | ||
| 198 | static const FunctionInfo functions[] = { | 83 | static const FunctionInfo functions[] = { |
| 199 | {0x00000000, &LM::OpenLogger, "OpenLogger"}, | 84 | {0, &LM::OpenLogger, "OpenLogger"}, |
| 200 | }; | 85 | }; |
| 86 | // clang-format on | ||
| 87 | |||
| 201 | RegisterHandlers(functions); | 88 | RegisterHandlers(functions); |
| 202 | } | 89 | } |
| 203 | 90 | ||
| 204 | /** | 91 | private: |
| 205 | * LM::OpenLogger service function | ||
| 206 | * Inputs: | ||
| 207 | * 0: 0x00000000 | ||
| 208 | * Outputs: | ||
| 209 | * 0: ResultCode | ||
| 210 | */ | ||
| 211 | void OpenLogger(Kernel::HLERequestContext& ctx) { | 92 | void OpenLogger(Kernel::HLERequestContext& ctx) { |
| 212 | LOG_DEBUG(Service_LM, "called"); | 93 | LOG_DEBUG(Service_LM, "called"); |
| 213 | 94 | ||
| 214 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 95 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 215 | rb.Push(RESULT_SUCCESS); | 96 | rb.Push(RESULT_SUCCESS); |
| 216 | rb.PushIpcInterface<ILogger>(); | 97 | rb.PushIpcInterface<ILogger>(manager); |
| 217 | } | 98 | } |
| 99 | |||
| 100 | Manager& manager; | ||
| 218 | }; | 101 | }; |
| 219 | 102 | ||
| 220 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 103 | void InstallInterfaces(Core::System& system) { |
| 221 | std::make_shared<LM>()->InstallAsService(service_manager); | 104 | std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager()); |
| 222 | } | 105 | } |
| 223 | 106 | ||
| 224 | } // namespace Service::LM | 107 | } // namespace Service::LM |
diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index 7806ae27b..d40410b5c 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h | |||
| @@ -4,13 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | namespace Service::SM { | 7 | namespace Core { |
| 8 | class ServiceManager; | 8 | class System; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | namespace Service::LM { | 11 | namespace Service::LM { |
| 12 | 12 | ||
| 13 | /// Registers all LM services with the specified service manager. | 13 | /// Registers all LM services with the specified service manager. |
| 14 | void InstallInterfaces(SM::ServiceManager& service_manager); | 14 | void InstallInterfaces(Core::System& system); |
| 15 | 15 | ||
| 16 | } // namespace Service::LM | 16 | } // namespace Service::LM |
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp new file mode 100644 index 000000000..b67081b86 --- /dev/null +++ b/src/core/hle/service/lm/manager.cpp | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 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/string_util.h" | ||
| 8 | #include "core/hle/service/lm/manager.h" | ||
| 9 | #include "core/reporter.h" | ||
| 10 | |||
| 11 | namespace Service::LM { | ||
| 12 | |||
| 13 | std::ostream& operator<<(std::ostream& os, DestinationFlag dest) { | ||
| 14 | std::vector<std::string> array; | ||
| 15 | const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) { | ||
| 16 | if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) { | ||
| 17 | array.emplace_back(std::move(name)); | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | |||
| 21 | check_single_flag(DestinationFlag::Default, "Default"); | ||
| 22 | check_single_flag(DestinationFlag::UART, "UART"); | ||
| 23 | check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)"); | ||
| 24 | |||
| 25 | os << "["; | ||
| 26 | for (const auto& entry : array) { | ||
| 27 | os << entry << ", "; | ||
| 28 | } | ||
| 29 | return os << "]"; | ||
| 30 | } | ||
| 31 | |||
| 32 | std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) { | ||
| 33 | switch (severity) { | ||
| 34 | case MessageHeader::Severity::Trace: | ||
| 35 | return os << "Trace"; | ||
| 36 | case MessageHeader::Severity::Info: | ||
| 37 | return os << "Info"; | ||
| 38 | case MessageHeader::Severity::Warning: | ||
| 39 | return os << "Warning"; | ||
| 40 | case MessageHeader::Severity::Error: | ||
| 41 | return os << "Error"; | ||
| 42 | case MessageHeader::Severity::Critical: | ||
| 43 | return os << "Critical"; | ||
| 44 | default: | ||
| 45 | return os << fmt::format("{:08X}", static_cast<u32>(severity)); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | std::ostream& operator<<(std::ostream& os, Field field) { | ||
| 50 | switch (field) { | ||
| 51 | case Field::Skip: | ||
| 52 | return os << "Skip"; | ||
| 53 | case Field::Message: | ||
| 54 | return os << "Message"; | ||
| 55 | case Field::Line: | ||
| 56 | return os << "Line"; | ||
| 57 | case Field::Filename: | ||
| 58 | return os << "Filename"; | ||
| 59 | case Field::Function: | ||
| 60 | return os << "Function"; | ||
| 61 | case Field::Module: | ||
| 62 | return os << "Module"; | ||
| 63 | case Field::Thread: | ||
| 64 | return os << "Thread"; | ||
| 65 | default: | ||
| 66 | return os << fmt::format("{:08X}", static_cast<u32>(field)); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | std::string FormatField(Field type, const std::vector<u8>& data) { | ||
| 71 | switch (type) { | ||
| 72 | case Field::Skip: | ||
| 73 | return ""; | ||
| 74 | case Field::Line: | ||
| 75 | if (data.size() >= sizeof(u32)) { | ||
| 76 | u32 line; | ||
| 77 | std::memcpy(&line, data.data(), sizeof(u32)); | ||
| 78 | return fmt::format("{}", line); | ||
| 79 | } | ||
| 80 | return "[ERROR DECODING LINE NUMBER]"; | ||
| 81 | case Field::Message: | ||
| 82 | case Field::Filename: | ||
| 83 | case Field::Function: | ||
| 84 | case Field::Module: | ||
| 85 | case Field::Thread: | ||
| 86 | return Common::StringFromFixedZeroTerminatedBuffer( | ||
| 87 | reinterpret_cast<const char*>(data.data()), data.size()); | ||
| 88 | default: | ||
| 89 | UNIMPLEMENTED(); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {} | ||
| 94 | |||
| 95 | Manager::~Manager() = default; | ||
| 96 | |||
| 97 | void Manager::SetEnabled(bool enabled) { | ||
| 98 | this->enabled = enabled; | ||
| 99 | } | ||
| 100 | |||
| 101 | void Manager::SetDestination(DestinationFlag destination) { | ||
| 102 | this->destination = destination; | ||
| 103 | } | ||
| 104 | |||
| 105 | void Manager::Log(LogMessage message) { | ||
| 106 | if (message.header.IsHeadLog()) { | ||
| 107 | InitializeLog(); | ||
| 108 | } | ||
| 109 | |||
| 110 | current_log.emplace_back(std::move(message)); | ||
| 111 | |||
| 112 | if (current_log.back().header.IsTailLog()) { | ||
| 113 | FinalizeLog(); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | void Manager::Flush() { | ||
| 118 | FinalizeLog(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void Manager::InitializeLog() { | ||
| 122 | current_log.clear(); | ||
| 123 | |||
| 124 | LOG_INFO(Service_LM, "Initialized new log session"); | ||
| 125 | } | ||
| 126 | |||
| 127 | void Manager::FinalizeLog() { | ||
| 128 | reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log)); | ||
| 129 | |||
| 130 | LOG_INFO(Service_LM, "Finalized current log session"); | ||
| 131 | } | ||
| 132 | |||
| 133 | } // namespace Service::LM | ||
diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h new file mode 100644 index 000000000..544e636ba --- /dev/null +++ b/src/core/hle/service/lm/manager.h | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | // Copyright 2019 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 <map> | ||
| 8 | #include <ostream> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/swap.h" | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class Reporter; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Service::LM { | ||
| 19 | |||
| 20 | enum class DestinationFlag : u32 { | ||
| 21 | Default = 1, | ||
| 22 | UART = 2, | ||
| 23 | UARTSleeping = 4, | ||
| 24 | |||
| 25 | All = 0xFFFF, | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct MessageHeader { | ||
| 29 | enum Flags : u32_le { | ||
| 30 | IsHead = 1, | ||
| 31 | IsTail = 2, | ||
| 32 | }; | ||
| 33 | enum Severity : u32_le { | ||
| 34 | Trace, | ||
| 35 | Info, | ||
| 36 | Warning, | ||
| 37 | Error, | ||
| 38 | Critical, | ||
| 39 | }; | ||
| 40 | |||
| 41 | u64_le pid; | ||
| 42 | u64_le thread_context; | ||
| 43 | union { | ||
| 44 | BitField<0, 16, Flags> flags; | ||
| 45 | BitField<16, 8, Severity> severity; | ||
| 46 | BitField<24, 8, u32> verbosity; | ||
| 47 | }; | ||
| 48 | u32_le payload_size; | ||
| 49 | |||
| 50 | bool IsHeadLog() const { | ||
| 51 | return flags & IsHead; | ||
| 52 | } | ||
| 53 | bool IsTailLog() const { | ||
| 54 | return flags & IsTail; | ||
| 55 | } | ||
| 56 | }; | ||
| 57 | static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); | ||
| 58 | |||
| 59 | enum class Field : u8 { | ||
| 60 | Skip = 1, | ||
| 61 | Message = 2, | ||
| 62 | Line = 3, | ||
| 63 | Filename = 4, | ||
| 64 | Function = 5, | ||
| 65 | Module = 6, | ||
| 66 | Thread = 7, | ||
| 67 | }; | ||
| 68 | |||
| 69 | std::ostream& operator<<(std::ostream& os, DestinationFlag dest); | ||
| 70 | std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity); | ||
| 71 | std::ostream& operator<<(std::ostream& os, Field field); | ||
| 72 | |||
| 73 | using FieldMap = std::map<Field, std::vector<u8>>; | ||
| 74 | |||
| 75 | struct LogMessage { | ||
| 76 | MessageHeader header; | ||
| 77 | FieldMap fields; | ||
| 78 | }; | ||
| 79 | |||
| 80 | std::string FormatField(Field type, const std::vector<u8>& data); | ||
| 81 | |||
| 82 | class Manager { | ||
| 83 | public: | ||
| 84 | explicit Manager(Core::Reporter& reporter); | ||
| 85 | ~Manager(); | ||
| 86 | |||
| 87 | void SetEnabled(bool enabled); | ||
| 88 | void SetDestination(DestinationFlag destination); | ||
| 89 | |||
| 90 | void Log(LogMessage message); | ||
| 91 | |||
| 92 | void Flush(); | ||
| 93 | |||
| 94 | private: | ||
| 95 | void InitializeLog(); | ||
| 96 | void FinalizeLog(); | ||
| 97 | |||
| 98 | bool enabled = true; | ||
| 99 | DestinationFlag destination = DestinationFlag::All; | ||
| 100 | |||
| 101 | std::vector<LogMessage> current_log; | ||
| 102 | |||
| 103 | Core::Reporter& reporter; | ||
| 104 | }; | ||
| 105 | |||
| 106 | } // namespace Service::LM | ||
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index a42c22d44..aa886cd3e 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -18,8 +18,8 @@ | |||
| 18 | namespace Service::NFP { | 18 | namespace Service::NFP { |
| 19 | 19 | ||
| 20 | namespace ErrCodes { | 20 | namespace ErrCodes { |
| 21 | constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, | 21 | [[maybe_unused]] constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, |
| 22 | -1); // TODO(ogniK): Find the actual error code | 22 | -1); // TODO(ogniK): Find the actual error code |
| 23 | constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); | 23 | constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); |
| 24 | } // namespace ErrCodes | 24 | } // namespace ErrCodes |
| 25 | 25 | ||
| @@ -35,7 +35,7 @@ Module::Interface::~Interface() = default; | |||
| 35 | class IUser final : public ServiceFramework<IUser> { | 35 | class IUser final : public ServiceFramework<IUser> { |
| 36 | public: | 36 | public: |
| 37 | IUser(Module::Interface& nfp_interface, Core::System& system) | 37 | IUser(Module::Interface& nfp_interface, Core::System& system) |
| 38 | : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) { | 38 | : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { |
| 39 | static const FunctionInfo functions[] = { | 39 | static const FunctionInfo functions[] = { |
| 40 | {0, &IUser::Initialize, "Initialize"}, | 40 | {0, &IUser::Initialize, "Initialize"}, |
| 41 | {1, &IUser::Finalize, "Finalize"}, | 41 | {1, &IUser::Finalize, "Finalize"}, |
| @@ -183,6 +183,8 @@ private: | |||
| 183 | case DeviceState::TagRemoved: | 183 | case DeviceState::TagRemoved: |
| 184 | device_state = DeviceState::Initialized; | 184 | device_state = DeviceState::Initialized; |
| 185 | break; | 185 | break; |
| 186 | default: | ||
| 187 | break; | ||
| 186 | } | 188 | } |
| 187 | IPC::ResponseBuilder rb{ctx, 2}; | 189 | IPC::ResponseBuilder rb{ctx, 2}; |
| 188 | rb.Push(RESULT_SUCCESS); | 190 | rb.Push(RESULT_SUCCESS); |
| @@ -324,7 +326,6 @@ private: | |||
| 324 | Kernel::EventPair deactivate_event; | 326 | Kernel::EventPair deactivate_event; |
| 325 | Kernel::EventPair availability_change_event; | 327 | Kernel::EventPair availability_change_event; |
| 326 | const Module::Interface& nfp_interface; | 328 | const Module::Interface& nfp_interface; |
| 327 | Core::System& system; | ||
| 328 | }; | 329 | }; |
| 329 | 330 | ||
| 330 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | 331 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 24d1813a7..756a2af57 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -12,6 +12,13 @@ | |||
| 12 | 12 | ||
| 13 | namespace Service::NIFM { | 13 | namespace Service::NIFM { |
| 14 | 14 | ||
| 15 | enum class RequestState : u32 { | ||
| 16 | NotSubmitted = 1, | ||
| 17 | Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW. | ||
| 18 | Pending = 2, | ||
| 19 | Connected = 3, | ||
| 20 | }; | ||
| 21 | |||
| 15 | class IScanRequest final : public ServiceFramework<IScanRequest> { | 22 | class IScanRequest final : public ServiceFramework<IScanRequest> { |
| 16 | public: | 23 | public: |
| 17 | explicit IScanRequest() : ServiceFramework("IScanRequest") { | 24 | explicit IScanRequest() : ServiceFramework("IScanRequest") { |
| @@ -81,7 +88,7 @@ private: | |||
| 81 | 88 | ||
| 82 | IPC::ResponseBuilder rb{ctx, 3}; | 89 | IPC::ResponseBuilder rb{ctx, 3}; |
| 83 | rb.Push(RESULT_SUCCESS); | 90 | rb.Push(RESULT_SUCCESS); |
| 84 | rb.Push<u32>(0); | 91 | rb.PushEnum(RequestState::Connected); |
| 85 | } | 92 | } |
| 86 | 93 | ||
| 87 | void GetResult(Kernel::HLERequestContext& ctx) { | 94 | void GetResult(Kernel::HLERequestContext& ctx) { |
| @@ -189,14 +196,14 @@ private: | |||
| 189 | 196 | ||
| 190 | IPC::ResponseBuilder rb{ctx, 3}; | 197 | IPC::ResponseBuilder rb{ctx, 3}; |
| 191 | rb.Push(RESULT_SUCCESS); | 198 | rb.Push(RESULT_SUCCESS); |
| 192 | rb.Push<u8>(0); | 199 | rb.Push<u8>(1); |
| 193 | } | 200 | } |
| 194 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { | 201 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { |
| 195 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 202 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 196 | 203 | ||
| 197 | IPC::ResponseBuilder rb{ctx, 3}; | 204 | IPC::ResponseBuilder rb{ctx, 3}; |
| 198 | rb.Push(RESULT_SUCCESS); | 205 | rb.Push(RESULT_SUCCESS); |
| 199 | rb.Push<u8>(0); | 206 | rb.Push<u8>(1); |
| 200 | } | 207 | } |
| 201 | Core::System& system; | 208 | Core::System& system; |
| 202 | }; | 209 | }; |
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 7dcdb4a07..f64535237 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp | |||
| @@ -324,14 +324,14 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | |||
| 324 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | 324 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { |
| 325 | // Map backing memory for the font data | 325 | // Map backing memory for the font data |
| 326 | LOG_DEBUG(Service_NS, "called"); | 326 | LOG_DEBUG(Service_NS, "called"); |
| 327 | Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, | 327 | system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, |
| 328 | SHARED_FONT_MEM_SIZE, | 328 | SHARED_FONT_MEM_SIZE, |
| 329 | Kernel::MemoryState::Shared); | 329 | Kernel::MemoryState::Shared); |
| 330 | 330 | ||
| 331 | // Create shared font memory object | 331 | // Create shared font memory object |
| 332 | auto& kernel = system.Kernel(); | 332 | auto& kernel = system.Kernel(); |
| 333 | impl->shared_font_mem = Kernel::SharedMemory::Create( | 333 | impl->shared_font_mem = Kernel::SharedMemory::Create( |
| 334 | kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, | 334 | kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, |
| 335 | Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, | 335 | Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, |
| 336 | "PL_U:shared_font_mem"); | 336 | "PL_U:shared_font_mem"); |
| 337 | 337 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 6bc053f27..07c88465e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -45,6 +45,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std: | |||
| 45 | return GetVARegions(input, output); | 45 | return GetVARegions(input, output); |
| 46 | case IoctlCommand::IocUnmapBufferCommand: | 46 | case IoctlCommand::IocUnmapBufferCommand: |
| 47 | return UnmapBuffer(input, output); | 47 | return UnmapBuffer(input, output); |
| 48 | default: | ||
| 49 | break; | ||
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) | 52 | if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index ff6b1abae..eb88fee1b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | |||
| @@ -38,9 +38,10 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v | |||
| 38 | return IocCtrlEventUnregister(input, output); | 38 | return IocCtrlEventUnregister(input, output); |
| 39 | case IoctlCommand::IocCtrlEventSignalCommand: | 39 | case IoctlCommand::IocCtrlEventSignalCommand: |
| 40 | return IocCtrlEventSignal(input, output); | 40 | return IocCtrlEventSignal(input, output); |
| 41 | default: | ||
| 42 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||
| 43 | return 0; | ||
| 41 | } | 44 | } |
| 42 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||
| 43 | return 0; | ||
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { | 47 | u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { |
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 389ace76f..cc2192e5c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -40,9 +40,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, | |||
| 40 | return FlushL2(input, output); | 40 | return FlushL2(input, output); |
| 41 | case IoctlCommand::IocGetGpuTime: | 41 | case IoctlCommand::IocGetGpuTime: |
| 42 | return GetGpuTime(input, output); | 42 | return GetGpuTime(input, output); |
| 43 | default: | ||
| 44 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||
| 45 | return 0; | ||
| 43 | } | 46 | } |
| 44 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||
| 45 | return 0; | ||
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, | 49 | u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 2b8d1bef6..9de0ace22 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve | |||
| 44 | return GetWaitbase(input, output); | 44 | return GetWaitbase(input, output); |
| 45 | case IoctlCommand::IocChannelSetTimeoutCommand: | 45 | case IoctlCommand::IocChannelSetTimeoutCommand: |
| 46 | return ChannelSetTimeout(input, output); | 46 | return ChannelSetTimeout(input, output); |
| 47 | default: | ||
| 48 | break; | ||
| 47 | } | 49 | } |
| 48 | 50 | ||
| 49 | if (command.group == NVGPU_IOCTL_MAGIC) { | 51 | if (command.group == NVGPU_IOCTL_MAGIC) { |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 831a427de..7c5302017 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -208,7 +208,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | |||
| 208 | AOC::InstallInterfaces(*sm, system); | 208 | AOC::InstallInterfaces(*sm, system); |
| 209 | APM::InstallInterfaces(system); | 209 | APM::InstallInterfaces(system); |
| 210 | Audio::InstallInterfaces(*sm, system); | 210 | Audio::InstallInterfaces(*sm, system); |
| 211 | BCAT::InstallInterfaces(*sm); | 211 | BCAT::InstallInterfaces(system); |
| 212 | BPC::InstallInterfaces(*sm); | 212 | BPC::InstallInterfaces(*sm); |
| 213 | BtDrv::InstallInterfaces(*sm, system); | 213 | BtDrv::InstallInterfaces(*sm, system); |
| 214 | BTM::InstallInterfaces(*sm, system); | 214 | BTM::InstallInterfaces(*sm, system); |
| @@ -226,7 +226,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | |||
| 226 | LBL::InstallInterfaces(*sm); | 226 | LBL::InstallInterfaces(*sm); |
| 227 | LDN::InstallInterfaces(*sm); | 227 | LDN::InstallInterfaces(*sm); |
| 228 | LDR::InstallInterfaces(*sm, system); | 228 | LDR::InstallInterfaces(*sm, system); |
| 229 | LM::InstallInterfaces(*sm); | 229 | LM::InstallInterfaces(system); |
| 230 | Migration::InstallInterfaces(*sm); | 230 | Migration::InstallInterfaces(*sm); |
| 231 | Mii::InstallInterfaces(*sm); | 231 | Mii::InstallInterfaces(*sm); |
| 232 | MM::InstallInterfaces(*sm); | 232 | MM::InstallInterfaces(*sm); |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index e75c700ad..f629892ae 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -150,6 +150,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 150 | // Apply cheats if they exist and the program has a valid title ID | 150 | // Apply cheats if they exist and the program has a valid title ID |
| 151 | if (pm) { | 151 | if (pm) { |
| 152 | auto& system = Core::System::GetInstance(); | 152 | auto& system = Core::System::GetInstance(); |
| 153 | system.SetCurrentProcessBuildID(nso_header.build_id); | ||
| 153 | const auto cheats = pm->CreateCheatList(system, nso_header.build_id); | 154 | const auto cheats = pm->CreateCheatList(system, nso_header.build_id); |
| 154 | if (!cheats.empty()) { | 155 | if (!cheats.empty()) { |
| 155 | system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); | 156 | system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9e030789d..fa49f3dd0 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -146,7 +146,7 @@ static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { | |||
| 146 | * using a VMA from the current process. | 146 | * using a VMA from the current process. |
| 147 | */ | 147 | */ |
| 148 | static u8* GetPointerFromVMA(VAddr vaddr) { | 148 | static u8* GetPointerFromVMA(VAddr vaddr) { |
| 149 | return GetPointerFromVMA(*Core::CurrentProcess(), vaddr); | 149 | return GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | template <typename T> | 152 | template <typename T> |
| @@ -226,7 +226,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { | |||
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | bool IsValidVirtualAddress(const VAddr vaddr) { | 228 | bool IsValidVirtualAddress(const VAddr vaddr) { |
| 229 | return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr); | 229 | return IsValidVirtualAddress(*Core::System::GetInstance().CurrentProcess(), vaddr); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | bool IsKernelVirtualAddress(const VAddr vaddr) { | 232 | bool IsKernelVirtualAddress(const VAddr vaddr) { |
| @@ -387,7 +387,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_ | |||
| 387 | } | 387 | } |
| 388 | 388 | ||
| 389 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { | 389 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { |
| 390 | ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); | 390 | ReadBlock(*Core::System::GetInstance().CurrentProcess(), src_addr, dest_buffer, size); |
| 391 | } | 391 | } |
| 392 | 392 | ||
| 393 | void Write8(const VAddr addr, const u8 data) { | 393 | void Write8(const VAddr addr, const u8 data) { |
| @@ -450,7 +450,7 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi | |||
| 450 | } | 450 | } |
| 451 | 451 | ||
| 452 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { | 452 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { |
| 453 | WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); | 453 | WriteBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_buffer, size); |
| 454 | } | 454 | } |
| 455 | 455 | ||
| 456 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { | 456 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { |
| @@ -539,7 +539,7 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, | |||
| 539 | } | 539 | } |
| 540 | 540 | ||
| 541 | void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { | 541 | void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { |
| 542 | CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); | 542 | CopyBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_addr, size); |
| 543 | } | 543 | } |
| 544 | 544 | ||
| 545 | } // namespace Memory | 545 | } // namespace Memory |
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 9c657929e..6f4af77fd 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include <fmt/chrono.h> | 8 | #include <fmt/chrono.h> |
| 9 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| 10 | #include <fmt/ostream.h> | ||
| 10 | #include <json.hpp> | 11 | #include <json.hpp> |
| 11 | 12 | ||
| 12 | #include "common/file_util.h" | 13 | #include "common/file_util.h" |
| @@ -17,6 +18,7 @@ | |||
| 17 | #include "core/hle/kernel/hle_ipc.h" | 18 | #include "core/hle/kernel/hle_ipc.h" |
| 18 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| 19 | #include "core/hle/result.h" | 20 | #include "core/hle/result.h" |
| 21 | #include "core/hle/service/lm/manager.h" | ||
| 20 | #include "core/reporter.h" | 22 | #include "core/reporter.h" |
| 21 | #include "core/settings.h" | 23 | #include "core/settings.h" |
| 22 | 24 | ||
| @@ -354,6 +356,55 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result, | |||
| 354 | SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); | 356 | SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); |
| 355 | } | 357 | } |
| 356 | 358 | ||
| 359 | void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const { | ||
| 360 | if (!IsReportingEnabled()) { | ||
| 361 | return; | ||
| 362 | } | ||
| 363 | |||
| 364 | const auto timestamp = GetTimestamp(); | ||
| 365 | json out; | ||
| 366 | |||
| 367 | out["yuzu_version"] = GetYuzuVersionData(); | ||
| 368 | out["report_common"] = | ||
| 369 | GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp); | ||
| 370 | |||
| 371 | out["log_destination"] = | ||
| 372 | fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination)); | ||
| 373 | |||
| 374 | auto json_messages = json::array(); | ||
| 375 | std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages), | ||
| 376 | [](const Service::LM::LogMessage& message) { | ||
| 377 | json out; | ||
| 378 | out["is_head"] = fmt::format("{}", message.header.IsHeadLog()); | ||
| 379 | out["is_tail"] = fmt::format("{}", message.header.IsTailLog()); | ||
| 380 | out["pid"] = fmt::format("{:016X}", message.header.pid); | ||
| 381 | out["thread_context"] = | ||
| 382 | fmt::format("{:016X}", message.header.thread_context); | ||
| 383 | out["payload_size"] = fmt::format("{:016X}", message.header.payload_size); | ||
| 384 | out["flags"] = fmt::format("{:04X}", message.header.flags.Value()); | ||
| 385 | out["severity"] = fmt::format("{}", message.header.severity.Value()); | ||
| 386 | out["verbosity"] = fmt::format("{:02X}", message.header.verbosity); | ||
| 387 | |||
| 388 | auto fields = json::array(); | ||
| 389 | std::transform(message.fields.begin(), message.fields.end(), | ||
| 390 | std::back_inserter(fields), [](const auto& kv) { | ||
| 391 | json out; | ||
| 392 | out["type"] = fmt::format("{}", kv.first); | ||
| 393 | out["data"] = | ||
| 394 | Service::LM::FormatField(kv.first, kv.second); | ||
| 395 | return out; | ||
| 396 | }); | ||
| 397 | |||
| 398 | out["fields"] = std::move(fields); | ||
| 399 | return out; | ||
| 400 | }); | ||
| 401 | |||
| 402 | out["log_messages"] = std::move(json_messages); | ||
| 403 | |||
| 404 | SaveToFile(std::move(out), | ||
| 405 | GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp)); | ||
| 406 | } | ||
| 407 | |||
| 357 | void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | 408 | void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, |
| 358 | std::string log_message) const { | 409 | std::string log_message) const { |
| 359 | if (!IsReportingEnabled()) | 410 | if (!IsReportingEnabled()) |
diff --git a/src/core/reporter.h b/src/core/reporter.h index f08aa11fb..380941b1b 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h | |||
| @@ -20,6 +20,10 @@ namespace Service::FileSystem { | |||
| 20 | enum class LogMode : u32; | 20 | enum class LogMode : u32; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | namespace Service::LM { | ||
| 24 | struct LogMessage; | ||
| 25 | } // namespace Service::LM | ||
| 26 | |||
| 23 | namespace Core { | 27 | namespace Core { |
| 24 | 28 | ||
| 25 | class System; | 29 | class System; |
| @@ -29,18 +33,22 @@ public: | |||
| 29 | explicit Reporter(System& system); | 33 | explicit Reporter(System& system); |
| 30 | ~Reporter(); | 34 | ~Reporter(); |
| 31 | 35 | ||
| 36 | // Used by fatal services | ||
| 32 | void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, | 37 | void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, |
| 33 | u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, | 38 | u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, |
| 34 | const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, | 39 | const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, |
| 35 | u32 backtrace_size, const std::string& arch, u32 unk10) const; | 40 | u32 backtrace_size, const std::string& arch, u32 unk10) const; |
| 36 | 41 | ||
| 42 | // Used by syscall svcBreak | ||
| 37 | void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, | 43 | void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, |
| 38 | std::optional<std::vector<u8>> resolved_buffer = {}) const; | 44 | std::optional<std::vector<u8>> resolved_buffer = {}) const; |
| 39 | 45 | ||
| 46 | // Used by HLE service handler | ||
| 40 | void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, | 47 | void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, |
| 41 | const std::string& name, | 48 | const std::string& name, |
| 42 | const std::string& service_name) const; | 49 | const std::string& service_name) const; |
| 43 | 50 | ||
| 51 | // Used by stub applet implementation | ||
| 44 | void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, | 52 | void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, |
| 45 | u32 theme_color, bool startup_sound, u64 system_tick, | 53 | u32 theme_color, bool startup_sound, u64 system_tick, |
| 46 | std::vector<std::vector<u8>> normal_channel, | 54 | std::vector<std::vector<u8>> normal_channel, |
| @@ -55,6 +63,7 @@ public: | |||
| 55 | void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, | 63 | void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, |
| 56 | std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; | 64 | std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; |
| 57 | 65 | ||
| 66 | // Used by error applet | ||
| 58 | void SaveErrorReport(u64 title_id, ResultCode result, | 67 | void SaveErrorReport(u64 title_id, ResultCode result, |
| 59 | std::optional<std::string> custom_text_main = {}, | 68 | std::optional<std::string> custom_text_main = {}, |
| 60 | std::optional<std::string> custom_text_detail = {}) const; | 69 | std::optional<std::string> custom_text_detail = {}) const; |
| @@ -62,6 +71,11 @@ public: | |||
| 62 | void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | 71 | void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, |
| 63 | std::string log_message) const; | 72 | std::string log_message) const; |
| 64 | 73 | ||
| 74 | // Used by lm services | ||
| 75 | void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const; | ||
| 76 | |||
| 77 | // Can be used anywhere to generate a backtrace and general info report at any point during | ||
| 78 | // execution. Not intended to be used for anything other than debugging or testing. | ||
| 65 | void SaveUserReport() const; | 79 | void SaveUserReport() const; |
| 66 | 80 | ||
| 67 | private: | 81 | private: |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 7de3fd1e5..d1fc94060 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -103,6 +103,8 @@ void LogSettings() { | |||
| 103 | LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); | 103 | LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); |
| 104 | LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); | 104 | LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); |
| 105 | LogSetting("Debugging_ProgramArgs", Settings::values.program_args); | 105 | LogSetting("Debugging_ProgramArgs", Settings::values.program_args); |
| 106 | LogSetting("Services_BCATBackend", Settings::values.bcat_backend); | ||
| 107 | LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); | ||
| 106 | } | 108 | } |
| 107 | 109 | ||
| 108 | } // namespace Settings | 110 | } // namespace Settings |
diff --git a/src/core/settings.h b/src/core/settings.h index 47bddfb30..9c98a9287 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -448,6 +448,10 @@ struct Values { | |||
| 448 | bool reporting_services; | 448 | bool reporting_services; |
| 449 | bool quest_flag; | 449 | bool quest_flag; |
| 450 | 450 | ||
| 451 | // BCAT | ||
| 452 | std::string bcat_backend; | ||
| 453 | bool bcat_boxcat_local; | ||
| 454 | |||
| 451 | // WebService | 455 | // WebService |
| 452 | bool enable_telemetry; | 456 | bool enable_telemetry; |
| 453 | std::string web_api_url; | 457 | std::string web_api_url; |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e2f85c5f1..eaa694ff8 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -105,9 +105,15 @@ add_library(video_core STATIC | |||
| 105 | shader/decode/warp.cpp | 105 | shader/decode/warp.cpp |
| 106 | shader/decode/xmad.cpp | 106 | shader/decode/xmad.cpp |
| 107 | shader/decode/other.cpp | 107 | shader/decode/other.cpp |
| 108 | shader/ast.cpp | ||
| 109 | shader/ast.h | ||
| 108 | shader/control_flow.cpp | 110 | shader/control_flow.cpp |
| 109 | shader/control_flow.h | 111 | shader/control_flow.h |
| 112 | shader/compiler_settings.cpp | ||
| 113 | shader/compiler_settings.h | ||
| 110 | shader/decode.cpp | 114 | shader/decode.cpp |
| 115 | shader/expr.cpp | ||
| 116 | shader/expr.h | ||
| 111 | shader/node_helper.cpp | 117 | shader/node_helper.cpp |
| 112 | shader/node_helper.h | 118 | shader/node_helper.h |
| 113 | shader/node.h | 119 | shader/node.h |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index b318aedb8..7802fd808 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -249,6 +249,11 @@ void Maxwell3D::InitDirtySettings() { | |||
| 249 | dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_units)] = polygon_offset_dirty_reg; | 249 | dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_units)] = polygon_offset_dirty_reg; |
| 250 | dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_factor)] = polygon_offset_dirty_reg; | 250 | dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_factor)] = polygon_offset_dirty_reg; |
| 251 | dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg; | 251 | dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg; |
| 252 | |||
| 253 | // Depth bounds | ||
| 254 | constexpr u32 depth_bounds_values_dirty_reg = DIRTY_REGS_POS(depth_bounds_values); | ||
| 255 | dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[0])] = depth_bounds_values_dirty_reg; | ||
| 256 | dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[1])] = depth_bounds_values_dirty_reg; | ||
| 252 | } | 257 | } |
| 253 | 258 | ||
| 254 | void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) { | 259 | void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) { |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 4c97759ed..e3f1047d5 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -687,7 +687,9 @@ public: | |||
| 687 | 687 | ||
| 688 | u32 rt_separate_frag_data; | 688 | u32 rt_separate_frag_data; |
| 689 | 689 | ||
| 690 | INSERT_PADDING_WORDS(0xC); | 690 | f32 depth_bounds[2]; |
| 691 | |||
| 692 | INSERT_PADDING_WORDS(0xA); | ||
| 691 | 693 | ||
| 692 | struct { | 694 | struct { |
| 693 | u32 address_high; | 695 | u32 address_high; |
| @@ -1201,6 +1203,7 @@ public: | |||
| 1201 | bool transform_feedback; | 1203 | bool transform_feedback; |
| 1202 | bool color_mask; | 1204 | bool color_mask; |
| 1203 | bool polygon_offset; | 1205 | bool polygon_offset; |
| 1206 | bool depth_bounds_values; | ||
| 1204 | 1207 | ||
| 1205 | // Complementary | 1208 | // Complementary |
| 1206 | bool viewport_transform; | 1209 | bool viewport_transform; |
| @@ -1400,6 +1403,7 @@ ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); | |||
| 1400 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); | 1403 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); |
| 1401 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); | 1404 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); |
| 1402 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); | 1405 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); |
| 1406 | ASSERT_REG_POSITION(depth_bounds, 0x3EC); | ||
| 1403 | ASSERT_REG_POSITION(zeta, 0x3F8); | 1407 | ASSERT_REG_POSITION(zeta, 0x3F8); |
| 1404 | ASSERT_REG_POSITION(clear_flags, 0x43E); | 1408 | ASSERT_REG_POSITION(clear_flags, 0x43E); |
| 1405 | ASSERT_REG_POSITION(vertex_attrib_format, 0x458); | 1409 | ASSERT_REG_POSITION(vertex_attrib_format, 0x458); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 28272ef6f..7a6355ce2 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -544,7 +544,7 @@ enum class VoteOperation : u64 { | |||
| 544 | Eq = 2, // allThreadsEqualNV | 544 | Eq = 2, // allThreadsEqualNV |
| 545 | }; | 545 | }; |
| 546 | 546 | ||
| 547 | enum class ImageAtomicSize : u64 { | 547 | enum class ImageAtomicOperationType : u64 { |
| 548 | U32 = 0, | 548 | U32 = 0, |
| 549 | S32 = 1, | 549 | S32 = 1, |
| 550 | U64 = 2, | 550 | U64 = 2, |
| @@ -1432,11 +1432,11 @@ union Instruction { | |||
| 1432 | ASSERT(mode == SurfaceDataMode::D_BA); | 1432 | ASSERT(mode == SurfaceDataMode::D_BA); |
| 1433 | return store_data_layout; | 1433 | return store_data_layout; |
| 1434 | } | 1434 | } |
| 1435 | } sust; | 1435 | } suldst; |
| 1436 | 1436 | ||
| 1437 | union { | 1437 | union { |
| 1438 | BitField<28, 1, u64> is_ba; | 1438 | BitField<28, 1, u64> is_ba; |
| 1439 | BitField<51, 3, ImageAtomicSize> size; | 1439 | BitField<51, 3, ImageAtomicOperationType> operation_type; |
| 1440 | BitField<33, 3, ImageType> image_type; | 1440 | BitField<33, 3, ImageType> image_type; |
| 1441 | BitField<29, 4, ImageAtomicOperation> operation; | 1441 | BitField<29, 4, ImageAtomicOperation> operation; |
| 1442 | BitField<49, 2, OutOfBoundsStore> out_of_bounds_store; | 1442 | BitField<49, 2, OutOfBoundsStore> out_of_bounds_store; |
| @@ -1595,6 +1595,7 @@ public: | |||
| 1595 | TMML_B, // Texture Mip Map Level | 1595 | TMML_B, // Texture Mip Map Level |
| 1596 | TMML, // Texture Mip Map Level | 1596 | TMML, // Texture Mip Map Level |
| 1597 | SUST, // Surface Store | 1597 | SUST, // Surface Store |
| 1598 | SULD, // Surface Load | ||
| 1598 | SUATOM, // Surface Atomic Operation | 1599 | SUATOM, // Surface Atomic Operation |
| 1599 | EXIT, | 1600 | EXIT, |
| 1600 | NOP, | 1601 | NOP, |
| @@ -1884,6 +1885,7 @@ private: | |||
| 1884 | INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"), | 1885 | INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"), |
| 1885 | INST("1101111101011---", Id::TMML, Type::Texture, "TMML"), | 1886 | INST("1101111101011---", Id::TMML, Type::Texture, "TMML"), |
| 1886 | INST("11101011001-----", Id::SUST, Type::Image, "SUST"), | 1887 | INST("11101011001-----", Id::SUST, Type::Image, "SUST"), |
| 1888 | INST("11101011000-----", Id::SULD, Type::Image, "SULD"), | ||
| 1887 | INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"), | 1889 | INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"), |
| 1888 | INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"), | 1890 | INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"), |
| 1889 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), | 1891 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 4f59a87b4..64de7e425 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -2,8 +2,10 @@ | |||
| 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 <algorithm> | ||
| 5 | #include <array> | 6 | #include <array> |
| 6 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <vector> | ||
| 7 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 8 | 10 | ||
| 9 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| @@ -30,9 +32,27 @@ bool TestProgram(const GLchar* glsl) { | |||
| 30 | return link_status == GL_TRUE; | 32 | return link_status == GL_TRUE; |
| 31 | } | 33 | } |
| 32 | 34 | ||
| 35 | std::vector<std::string_view> GetExtensions() { | ||
| 36 | GLint num_extensions; | ||
| 37 | glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); | ||
| 38 | std::vector<std::string_view> extensions; | ||
| 39 | extensions.reserve(num_extensions); | ||
| 40 | for (GLint index = 0; index < num_extensions; ++index) { | ||
| 41 | extensions.push_back( | ||
| 42 | reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(index)))); | ||
| 43 | } | ||
| 44 | return extensions; | ||
| 45 | } | ||
| 46 | |||
| 47 | bool HasExtension(const std::vector<std::string_view>& images, std::string_view extension) { | ||
| 48 | return std::find(images.begin(), images.end(), extension) != images.end(); | ||
| 49 | } | ||
| 50 | |||
| 33 | } // Anonymous namespace | 51 | } // Anonymous namespace |
| 34 | 52 | ||
| 35 | Device::Device() { | 53 | Device::Device() { |
| 54 | const std::vector extensions = GetExtensions(); | ||
| 55 | |||
| 36 | uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); | 56 | uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); |
| 37 | shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); | 57 | shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); |
| 38 | max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS); | 58 | max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS); |
| @@ -40,6 +60,7 @@ Device::Device() { | |||
| 40 | has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group && | 60 | has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group && |
| 41 | GLAD_GL_NV_shader_thread_shuffle; | 61 | GLAD_GL_NV_shader_thread_shuffle; |
| 42 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; | 62 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; |
| 63 | has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); | ||
| 43 | has_variable_aoffi = TestVariableAoffi(); | 64 | has_variable_aoffi = TestVariableAoffi(); |
| 44 | has_component_indexing_bug = TestComponentIndexingBug(); | 65 | has_component_indexing_bug = TestComponentIndexingBug(); |
| 45 | has_precise_bug = TestPreciseBug(); | 66 | has_precise_bug = TestPreciseBug(); |
| @@ -55,6 +76,7 @@ Device::Device(std::nullptr_t) { | |||
| 55 | max_varyings = 15; | 76 | max_varyings = 15; |
| 56 | has_warp_intrinsics = true; | 77 | has_warp_intrinsics = true; |
| 57 | has_vertex_viewport_layer = true; | 78 | has_vertex_viewport_layer = true; |
| 79 | has_image_load_formatted = true; | ||
| 58 | has_variable_aoffi = true; | 80 | has_variable_aoffi = true; |
| 59 | has_component_indexing_bug = false; | 81 | has_component_indexing_bug = false; |
| 60 | has_precise_bug = false; | 82 | has_precise_bug = false; |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index ba6dcd3be..bb273c3d6 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -38,6 +38,10 @@ public: | |||
| 38 | return has_vertex_viewport_layer; | 38 | return has_vertex_viewport_layer; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | bool HasImageLoadFormatted() const { | ||
| 42 | return has_image_load_formatted; | ||
| 43 | } | ||
| 44 | |||
| 41 | bool HasVariableAoffi() const { | 45 | bool HasVariableAoffi() const { |
| 42 | return has_variable_aoffi; | 46 | return has_variable_aoffi; |
| 43 | } | 47 | } |
| @@ -61,6 +65,7 @@ private: | |||
| 61 | u32 max_varyings{}; | 65 | u32 max_varyings{}; |
| 62 | bool has_warp_intrinsics{}; | 66 | bool has_warp_intrinsics{}; |
| 63 | bool has_vertex_viewport_layer{}; | 67 | bool has_vertex_viewport_layer{}; |
| 68 | bool has_image_load_formatted{}; | ||
| 64 | bool has_variable_aoffi{}; | 69 | bool has_variable_aoffi{}; |
| 65 | bool has_component_indexing_bug{}; | 70 | bool has_component_indexing_bug{}; |
| 66 | bool has_precise_bug{}; | 71 | bool has_precise_bug{}; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 6a17bed72..a85f730a8 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -1340,7 +1340,9 @@ void RasterizerOpenGL::SyncPolygonOffset() { | |||
| 1340 | state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0; | 1340 | state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0; |
| 1341 | state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0; | 1341 | state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0; |
| 1342 | state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0; | 1342 | state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0; |
| 1343 | state.polygon_offset.units = regs.polygon_offset_units; | 1343 | |
| 1344 | // Hardware divides polygon offset units by two | ||
| 1345 | state.polygon_offset.units = regs.polygon_offset_units / 2.0f; | ||
| 1344 | state.polygon_offset.factor = regs.polygon_offset_factor; | 1346 | state.polygon_offset.factor = regs.polygon_offset_factor; |
| 1345 | state.polygon_offset.clamp = regs.polygon_offset_clamp; | 1347 | state.polygon_offset.clamp = regs.polygon_offset_clamp; |
| 1346 | 1348 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 0dbc4c02f..42ca3b1bd 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -211,14 +211,14 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn | |||
| 211 | const auto primitive_mode{variant.primitive_mode}; | 211 | const auto primitive_mode{variant.primitive_mode}; |
| 212 | const auto texture_buffer_usage{variant.texture_buffer_usage}; | 212 | const auto texture_buffer_usage{variant.texture_buffer_usage}; |
| 213 | 213 | ||
| 214 | std::string source = "#version 430 core\n" | 214 | std::string source = R"(#version 430 core |
| 215 | "#extension GL_ARB_separate_shader_objects : enable\n" | 215 | #extension GL_ARB_separate_shader_objects : enable |
| 216 | "#extension GL_NV_gpu_shader5 : enable\n" | 216 | #extension GL_ARB_shader_viewport_layer_array : enable |
| 217 | "#extension GL_NV_shader_thread_group : enable\n" | 217 | #extension GL_EXT_shader_image_load_formatted : enable |
| 218 | "#extension GL_NV_shader_thread_shuffle : enable\n"; | 218 | #extension GL_NV_gpu_shader5 : enable |
| 219 | if (entries.shader_viewport_layer_array) { | 219 | #extension GL_NV_shader_thread_group : enable |
| 220 | source += "#extension GL_ARB_shader_viewport_layer_array : enable\n"; | 220 | #extension GL_NV_shader_thread_shuffle : enable |
| 221 | } | 221 | )"; |
| 222 | if (program_type == ProgramType::Compute) { | 222 | if (program_type == ProgramType::Compute) { |
| 223 | source += "#extension GL_ARB_compute_variable_group_size : require\n"; | 223 | source += "#extension GL_ARB_compute_variable_group_size : require\n"; |
| 224 | } | 224 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 74cb59bc1..6a610a3bc 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -19,6 +19,8 @@ | |||
| 19 | #include "video_core/renderer_opengl/gl_device.h" | 19 | #include "video_core/renderer_opengl/gl_device.h" |
| 20 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 20 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 21 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 21 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 22 | #include "video_core/shader/ast.h" | ||
| 23 | #include "video_core/shader/node.h" | ||
| 22 | #include "video_core/shader/shader_ir.h" | 24 | #include "video_core/shader/shader_ir.h" |
| 23 | 25 | ||
| 24 | namespace OpenGL::GLShader { | 26 | namespace OpenGL::GLShader { |
| @@ -241,6 +243,26 @@ constexpr const char* GetTypeString(Type type) { | |||
| 241 | } | 243 | } |
| 242 | } | 244 | } |
| 243 | 245 | ||
| 246 | constexpr const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) { | ||
| 247 | switch (image_type) { | ||
| 248 | case Tegra::Shader::ImageType::Texture1D: | ||
| 249 | return "1D"; | ||
| 250 | case Tegra::Shader::ImageType::TextureBuffer: | ||
| 251 | return "Buffer"; | ||
| 252 | case Tegra::Shader::ImageType::Texture1DArray: | ||
| 253 | return "1DArray"; | ||
| 254 | case Tegra::Shader::ImageType::Texture2D: | ||
| 255 | return "2D"; | ||
| 256 | case Tegra::Shader::ImageType::Texture2DArray: | ||
| 257 | return "2DArray"; | ||
| 258 | case Tegra::Shader::ImageType::Texture3D: | ||
| 259 | return "3D"; | ||
| 260 | default: | ||
| 261 | UNREACHABLE(); | ||
| 262 | return "1D"; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 244 | /// Generates code to use for a swizzle operation. | 266 | /// Generates code to use for a swizzle operation. |
| 245 | constexpr const char* GetSwizzle(u32 element) { | 267 | constexpr const char* GetSwizzle(u32 element) { |
| 246 | constexpr std::array swizzle = {".x", ".y", ".z", ".w"}; | 268 | constexpr std::array swizzle = {".x", ".y", ".z", ".w"}; |
| @@ -313,39 +335,24 @@ constexpr bool IsVertexShader(ProgramType stage) { | |||
| 313 | return stage == ProgramType::VertexA || stage == ProgramType::VertexB; | 335 | return stage == ProgramType::VertexA || stage == ProgramType::VertexB; |
| 314 | } | 336 | } |
| 315 | 337 | ||
| 338 | class ASTDecompiler; | ||
| 339 | class ExprDecompiler; | ||
| 340 | |||
| 316 | class GLSLDecompiler final { | 341 | class GLSLDecompiler final { |
| 317 | public: | 342 | public: |
| 318 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage, | 343 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage, |
| 319 | std::string suffix) | 344 | std::string suffix) |
| 320 | : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} | 345 | : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} |
| 321 | 346 | ||
| 322 | void Decompile() { | 347 | void DecompileBranchMode() { |
| 323 | DeclareVertex(); | ||
| 324 | DeclareGeometry(); | ||
| 325 | DeclareRegisters(); | ||
| 326 | DeclarePredicates(); | ||
| 327 | DeclareLocalMemory(); | ||
| 328 | DeclareSharedMemory(); | ||
| 329 | DeclareInternalFlags(); | ||
| 330 | DeclareInputAttributes(); | ||
| 331 | DeclareOutputAttributes(); | ||
| 332 | DeclareConstantBuffers(); | ||
| 333 | DeclareGlobalMemory(); | ||
| 334 | DeclareSamplers(); | ||
| 335 | DeclarePhysicalAttributeReader(); | ||
| 336 | DeclareImages(); | ||
| 337 | |||
| 338 | code.AddLine("void execute_{}() {{", suffix); | ||
| 339 | ++code.scope; | ||
| 340 | |||
| 341 | // VM's program counter | 348 | // VM's program counter |
| 342 | const auto first_address = ir.GetBasicBlocks().begin()->first; | 349 | const auto first_address = ir.GetBasicBlocks().begin()->first; |
| 343 | code.AddLine("uint jmp_to = {}U;", first_address); | 350 | code.AddLine("uint jmp_to = {}U;", first_address); |
| 344 | 351 | ||
| 345 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems | 352 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems |
| 346 | // unlikely that shaders will use 20 nested SSYs and PBKs. | 353 | // unlikely that shaders will use 20 nested SSYs and PBKs. |
| 354 | constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 347 | if (!ir.IsFlowStackDisabled()) { | 355 | if (!ir.IsFlowStackDisabled()) { |
| 348 | constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 349 | for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { | 356 | for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { |
| 350 | code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); | 357 | code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); |
| 351 | code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); | 358 | code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); |
| @@ -371,10 +378,37 @@ public: | |||
| 371 | code.AddLine("default: return;"); | 378 | code.AddLine("default: return;"); |
| 372 | code.AddLine("}}"); | 379 | code.AddLine("}}"); |
| 373 | 380 | ||
| 374 | for (std::size_t i = 0; i < 2; ++i) { | 381 | --code.scope; |
| 375 | --code.scope; | 382 | code.AddLine("}}"); |
| 376 | code.AddLine("}}"); | 383 | } |
| 384 | |||
| 385 | void DecompileAST(); | ||
| 386 | |||
| 387 | void Decompile() { | ||
| 388 | DeclareVertex(); | ||
| 389 | DeclareGeometry(); | ||
| 390 | DeclareRegisters(); | ||
| 391 | DeclarePredicates(); | ||
| 392 | DeclareLocalMemory(); | ||
| 393 | DeclareInternalFlags(); | ||
| 394 | DeclareInputAttributes(); | ||
| 395 | DeclareOutputAttributes(); | ||
| 396 | DeclareConstantBuffers(); | ||
| 397 | DeclareGlobalMemory(); | ||
| 398 | DeclareSamplers(); | ||
| 399 | DeclarePhysicalAttributeReader(); | ||
| 400 | |||
| 401 | code.AddLine("void execute_{}() {{", suffix); | ||
| 402 | ++code.scope; | ||
| 403 | |||
| 404 | if (ir.IsDecompiled()) { | ||
| 405 | DecompileAST(); | ||
| 406 | } else { | ||
| 407 | DecompileBranchMode(); | ||
| 377 | } | 408 | } |
| 409 | |||
| 410 | --code.scope; | ||
| 411 | code.AddLine("}}"); | ||
| 378 | } | 412 | } |
| 379 | 413 | ||
| 380 | std::string GetResult() { | 414 | std::string GetResult() { |
| @@ -398,13 +432,14 @@ public: | |||
| 398 | usage.is_read, usage.is_written); | 432 | usage.is_read, usage.is_written); |
| 399 | } | 433 | } |
| 400 | entries.clip_distances = ir.GetClipDistances(); | 434 | entries.clip_distances = ir.GetClipDistances(); |
| 401 | entries.shader_viewport_layer_array = | ||
| 402 | IsVertexShader(stage) && (ir.UsesLayer() || ir.UsesViewportIndex()); | ||
| 403 | entries.shader_length = ir.GetLength(); | 435 | entries.shader_length = ir.GetLength(); |
| 404 | return entries; | 436 | return entries; |
| 405 | } | 437 | } |
| 406 | 438 | ||
| 407 | private: | 439 | private: |
| 440 | friend class ASTDecompiler; | ||
| 441 | friend class ExprDecompiler; | ||
| 442 | |||
| 408 | void DeclareVertex() { | 443 | void DeclareVertex() { |
| 409 | if (!IsVertexShader(stage)) | 444 | if (!IsVertexShader(stage)) |
| 410 | return; | 445 | return; |
| @@ -722,42 +757,6 @@ private: | |||
| 722 | void DeclareImages() { | 757 | void DeclareImages() { |
| 723 | const auto& images{ir.GetImages()}; | 758 | const auto& images{ir.GetImages()}; |
| 724 | for (const auto& [offset, image] : images) { | 759 | for (const auto& [offset, image] : images) { |
| 725 | const char* image_type = [&] { | ||
| 726 | switch (image.GetType()) { | ||
| 727 | case Tegra::Shader::ImageType::Texture1D: | ||
| 728 | return "image1D"; | ||
| 729 | case Tegra::Shader::ImageType::TextureBuffer: | ||
| 730 | return "imageBuffer"; | ||
| 731 | case Tegra::Shader::ImageType::Texture1DArray: | ||
| 732 | return "image1DArray"; | ||
| 733 | case Tegra::Shader::ImageType::Texture2D: | ||
| 734 | return "image2D"; | ||
| 735 | case Tegra::Shader::ImageType::Texture2DArray: | ||
| 736 | return "image2DArray"; | ||
| 737 | case Tegra::Shader::ImageType::Texture3D: | ||
| 738 | return "image3D"; | ||
| 739 | default: | ||
| 740 | UNREACHABLE(); | ||
| 741 | return "image1D"; | ||
| 742 | } | ||
| 743 | }(); | ||
| 744 | |||
| 745 | const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> { | ||
| 746 | if (!image.IsSizeKnown()) { | ||
| 747 | return {"", ""}; | ||
| 748 | } | ||
| 749 | switch (image.GetSize()) { | ||
| 750 | case Tegra::Shader::ImageAtomicSize::U32: | ||
| 751 | return {"u", "r32ui, "}; | ||
| 752 | case Tegra::Shader::ImageAtomicSize::S32: | ||
| 753 | return {"i", "r32i, "}; | ||
| 754 | default: | ||
| 755 | UNIMPLEMENTED_MSG("Unimplemented atomic size={}", | ||
| 756 | static_cast<u32>(image.GetSize())); | ||
| 757 | return {"", ""}; | ||
| 758 | } | ||
| 759 | }(); | ||
| 760 | |||
| 761 | std::string qualifier = "coherent volatile"; | 760 | std::string qualifier = "coherent volatile"; |
| 762 | if (image.IsRead() && !image.IsWritten()) { | 761 | if (image.IsRead() && !image.IsWritten()) { |
| 763 | qualifier += " readonly"; | 762 | qualifier += " readonly"; |
| @@ -765,9 +764,10 @@ private: | |||
| 765 | qualifier += " writeonly"; | 764 | qualifier += " writeonly"; |
| 766 | } | 765 | } |
| 767 | 766 | ||
| 768 | code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform " | 767 | const char* format = image.IsAtomic() ? "r32ui, " : ""; |
| 769 | "{} {};", | 768 | const char* type_declaration = GetImageTypeDeclaration(image.GetType()); |
| 770 | image.GetIndex(), qualifier, image_type, GetImage(image)); | 769 | code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format, |
| 770 | image.GetIndex(), qualifier, type_declaration, GetImage(image)); | ||
| 771 | } | 771 | } |
| 772 | if (!images.empty()) { | 772 | if (!images.empty()) { |
| 773 | code.AddNewLine(); | 773 | code.AddNewLine(); |
| @@ -1234,28 +1234,13 @@ private: | |||
| 1234 | } | 1234 | } |
| 1235 | 1235 | ||
| 1236 | std::string BuildImageValues(Operation operation) { | 1236 | std::string BuildImageValues(Operation operation) { |
| 1237 | constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"}; | ||
| 1237 | const auto meta{std::get<MetaImage>(operation.GetMeta())}; | 1238 | const auto meta{std::get<MetaImage>(operation.GetMeta())}; |
| 1238 | const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> { | ||
| 1239 | constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"}; | ||
| 1240 | if (!meta.image.IsSizeKnown()) { | ||
| 1241 | return {float_constructors, Type::Float}; | ||
| 1242 | } | ||
| 1243 | switch (meta.image.GetSize()) { | ||
| 1244 | case Tegra::Shader::ImageAtomicSize::U32: | ||
| 1245 | return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint}; | ||
| 1246 | case Tegra::Shader::ImageAtomicSize::S32: | ||
| 1247 | return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint}; | ||
| 1248 | default: | ||
| 1249 | UNIMPLEMENTED_MSG("Unimplemented image size={}", | ||
| 1250 | static_cast<u32>(meta.image.GetSize())); | ||
| 1251 | return {float_constructors, Type::Float}; | ||
| 1252 | } | ||
| 1253 | }(); | ||
| 1254 | 1239 | ||
| 1255 | const std::size_t values_count{meta.values.size()}; | 1240 | const std::size_t values_count{meta.values.size()}; |
| 1256 | std::string expr = fmt::format("{}(", constructors.at(values_count - 1)); | 1241 | std::string expr = fmt::format("{}(", constructors.at(values_count - 1)); |
| 1257 | for (std::size_t i = 0; i < values_count; ++i) { | 1242 | for (std::size_t i = 0; i < values_count; ++i) { |
| 1258 | expr += Visit(meta.values.at(i)).As(type); | 1243 | expr += Visit(meta.values.at(i)).AsUint(); |
| 1259 | if (i + 1 < values_count) { | 1244 | if (i + 1 < values_count) { |
| 1260 | expr += ", "; | 1245 | expr += ", "; |
| 1261 | } | 1246 | } |
| @@ -1264,29 +1249,6 @@ private: | |||
| 1264 | return expr; | 1249 | return expr; |
| 1265 | } | 1250 | } |
| 1266 | 1251 | ||
| 1267 | Expression AtomicImage(Operation operation, const char* opname) { | ||
| 1268 | constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("}; | ||
| 1269 | const auto meta{std::get<MetaImage>(operation.GetMeta())}; | ||
| 1270 | ASSERT(meta.values.size() == 1); | ||
| 1271 | ASSERT(meta.image.IsSizeKnown()); | ||
| 1272 | |||
| 1273 | const auto type = [&]() { | ||
| 1274 | switch (const auto size = meta.image.GetSize()) { | ||
| 1275 | case Tegra::Shader::ImageAtomicSize::U32: | ||
| 1276 | return Type::Uint; | ||
| 1277 | case Tegra::Shader::ImageAtomicSize::S32: | ||
| 1278 | return Type::Int; | ||
| 1279 | default: | ||
| 1280 | UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size)); | ||
| 1281 | return Type::Uint; | ||
| 1282 | } | ||
| 1283 | }(); | ||
| 1284 | |||
| 1285 | return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image), | ||
| 1286 | BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)), | ||
| 1287 | type}; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | Expression Assign(Operation operation) { | 1252 | Expression Assign(Operation operation) { |
| 1291 | const Node& dest = operation[0]; | 1253 | const Node& dest = operation[0]; |
| 1292 | const Node& src = operation[1]; | 1254 | const Node& src = operation[1]; |
| @@ -1545,6 +1507,8 @@ private: | |||
| 1545 | case Tegra::Shader::HalfType::H1_H1: | 1507 | case Tegra::Shader::HalfType::H1_H1: |
| 1546 | return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat}; | 1508 | return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat}; |
| 1547 | } | 1509 | } |
| 1510 | UNREACHABLE(); | ||
| 1511 | return {"0", Type::Int}; | ||
| 1548 | } | 1512 | } |
| 1549 | 1513 | ||
| 1550 | Expression HMergeF32(Operation operation) { | 1514 | Expression HMergeF32(Operation operation) { |
| @@ -1809,6 +1773,19 @@ private: | |||
| 1809 | return {tmp, Type::Float}; | 1773 | return {tmp, Type::Float}; |
| 1810 | } | 1774 | } |
| 1811 | 1775 | ||
| 1776 | Expression ImageLoad(Operation operation) { | ||
| 1777 | if (!device.HasImageLoadFormatted()) { | ||
| 1778 | LOG_ERROR(Render_OpenGL, | ||
| 1779 | "Device lacks GL_EXT_shader_image_load_formatted, stubbing image load"); | ||
| 1780 | return {"0", Type::Int}; | ||
| 1781 | } | ||
| 1782 | |||
| 1783 | const auto meta{std::get<MetaImage>(operation.GetMeta())}; | ||
| 1784 | return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image), | ||
| 1785 | BuildIntegerCoordinates(operation), GetSwizzle(meta.element)), | ||
| 1786 | Type::Uint}; | ||
| 1787 | } | ||
| 1788 | |||
| 1812 | Expression ImageStore(Operation operation) { | 1789 | Expression ImageStore(Operation operation) { |
| 1813 | const auto meta{std::get<MetaImage>(operation.GetMeta())}; | 1790 | const auto meta{std::get<MetaImage>(operation.GetMeta())}; |
| 1814 | code.AddLine("imageStore({}, {}, {});", GetImage(meta.image), | 1791 | code.AddLine("imageStore({}, {}, {});", GetImage(meta.image), |
| @@ -1816,31 +1793,14 @@ private: | |||
| 1816 | return {}; | 1793 | return {}; |
| 1817 | } | 1794 | } |
| 1818 | 1795 | ||
| 1819 | Expression AtomicImageAdd(Operation operation) { | 1796 | template <const std::string_view& opname> |
| 1820 | return AtomicImage(operation, "imageAtomicAdd"); | 1797 | Expression AtomicImage(Operation operation) { |
| 1821 | } | 1798 | const auto meta{std::get<MetaImage>(operation.GetMeta())}; |
| 1822 | 1799 | ASSERT(meta.values.size() == 1); | |
| 1823 | Expression AtomicImageMin(Operation operation) { | ||
| 1824 | return AtomicImage(operation, "imageAtomicMin"); | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | Expression AtomicImageMax(Operation operation) { | ||
| 1828 | return AtomicImage(operation, "imageAtomicMax"); | ||
| 1829 | } | ||
| 1830 | Expression AtomicImageAnd(Operation operation) { | ||
| 1831 | return AtomicImage(operation, "imageAtomicAnd"); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | Expression AtomicImageOr(Operation operation) { | ||
| 1835 | return AtomicImage(operation, "imageAtomicOr"); | ||
| 1836 | } | ||
| 1837 | |||
| 1838 | Expression AtomicImageXor(Operation operation) { | ||
| 1839 | return AtomicImage(operation, "imageAtomicXor"); | ||
| 1840 | } | ||
| 1841 | 1800 | ||
| 1842 | Expression AtomicImageExchange(Operation operation) { | 1801 | return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image), |
| 1843 | return AtomicImage(operation, "imageAtomicExchange"); | 1802 | BuildIntegerCoordinates(operation), Visit(meta.values[0]).AsUint()), |
| 1803 | Type::Uint}; | ||
| 1844 | } | 1804 | } |
| 1845 | 1805 | ||
| 1846 | Expression Branch(Operation operation) { | 1806 | Expression Branch(Operation operation) { |
| @@ -1877,10 +1837,9 @@ private: | |||
| 1877 | return {}; | 1837 | return {}; |
| 1878 | } | 1838 | } |
| 1879 | 1839 | ||
| 1880 | Expression Exit(Operation operation) { | 1840 | void PreExit() { |
| 1881 | if (stage != ProgramType::Fragment) { | 1841 | if (stage != ProgramType::Fragment) { |
| 1882 | code.AddLine("return;"); | 1842 | return; |
| 1883 | return {}; | ||
| 1884 | } | 1843 | } |
| 1885 | const auto& used_registers = ir.GetRegisters(); | 1844 | const auto& used_registers = ir.GetRegisters(); |
| 1886 | const auto SafeGetRegister = [&](u32 reg) -> Expression { | 1845 | const auto SafeGetRegister = [&](u32 reg) -> Expression { |
| @@ -1912,7 +1871,10 @@ private: | |||
| 1912 | // already contains one past the last color register. | 1871 | // already contains one past the last color register. |
| 1913 | code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat()); | 1872 | code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat()); |
| 1914 | } | 1873 | } |
| 1874 | } | ||
| 1915 | 1875 | ||
| 1876 | Expression Exit(Operation operation) { | ||
| 1877 | PreExit(); | ||
| 1916 | code.AddLine("return;"); | 1878 | code.AddLine("return;"); |
| 1917 | return {}; | 1879 | return {}; |
| 1918 | } | 1880 | } |
| @@ -2035,6 +1997,12 @@ private: | |||
| 2035 | Func() = delete; | 1997 | Func() = delete; |
| 2036 | ~Func() = delete; | 1998 | ~Func() = delete; |
| 2037 | 1999 | ||
| 2000 | static constexpr std::string_view Add = "Add"; | ||
| 2001 | static constexpr std::string_view And = "And"; | ||
| 2002 | static constexpr std::string_view Or = "Or"; | ||
| 2003 | static constexpr std::string_view Xor = "Xor"; | ||
| 2004 | static constexpr std::string_view Exchange = "Exchange"; | ||
| 2005 | |||
| 2038 | static constexpr std::string_view ShuffleIndexed = "shuffleNV"; | 2006 | static constexpr std::string_view ShuffleIndexed = "shuffleNV"; |
| 2039 | static constexpr std::string_view ShuffleUp = "shuffleUpNV"; | 2007 | static constexpr std::string_view ShuffleUp = "shuffleUpNV"; |
| 2040 | static constexpr std::string_view ShuffleDown = "shuffleDownNV"; | 2008 | static constexpr std::string_view ShuffleDown = "shuffleDownNV"; |
| @@ -2172,14 +2140,14 @@ private: | |||
| 2172 | &GLSLDecompiler::TextureQueryLod, | 2140 | &GLSLDecompiler::TextureQueryLod, |
| 2173 | &GLSLDecompiler::TexelFetch, | 2141 | &GLSLDecompiler::TexelFetch, |
| 2174 | 2142 | ||
| 2143 | &GLSLDecompiler::ImageLoad, | ||
| 2175 | &GLSLDecompiler::ImageStore, | 2144 | &GLSLDecompiler::ImageStore, |
| 2176 | &GLSLDecompiler::AtomicImageAdd, | 2145 | |
| 2177 | &GLSLDecompiler::AtomicImageMin, | 2146 | &GLSLDecompiler::AtomicImage<Func::Add>, |
| 2178 | &GLSLDecompiler::AtomicImageMax, | 2147 | &GLSLDecompiler::AtomicImage<Func::And>, |
| 2179 | &GLSLDecompiler::AtomicImageAnd, | 2148 | &GLSLDecompiler::AtomicImage<Func::Or>, |
| 2180 | &GLSLDecompiler::AtomicImageOr, | 2149 | &GLSLDecompiler::AtomicImage<Func::Xor>, |
| 2181 | &GLSLDecompiler::AtomicImageXor, | 2150 | &GLSLDecompiler::AtomicImage<Func::Exchange>, |
| 2182 | &GLSLDecompiler::AtomicImageExchange, | ||
| 2183 | 2151 | ||
| 2184 | &GLSLDecompiler::Branch, | 2152 | &GLSLDecompiler::Branch, |
| 2185 | &GLSLDecompiler::BranchIndirect, | 2153 | &GLSLDecompiler::BranchIndirect, |
| @@ -2303,6 +2271,208 @@ private: | |||
| 2303 | ShaderWriter code; | 2271 | ShaderWriter code; |
| 2304 | }; | 2272 | }; |
| 2305 | 2273 | ||
| 2274 | static constexpr std::string_view flow_var = "flow_var_"; | ||
| 2275 | |||
| 2276 | std::string GetFlowVariable(u32 i) { | ||
| 2277 | return fmt::format("{}{}", flow_var, i); | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | class ExprDecompiler { | ||
| 2281 | public: | ||
| 2282 | explicit ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} | ||
| 2283 | |||
| 2284 | void operator()(VideoCommon::Shader::ExprAnd& expr) { | ||
| 2285 | inner += "( "; | ||
| 2286 | std::visit(*this, *expr.operand1); | ||
| 2287 | inner += " && "; | ||
| 2288 | std::visit(*this, *expr.operand2); | ||
| 2289 | inner += ')'; | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | void operator()(VideoCommon::Shader::ExprOr& expr) { | ||
| 2293 | inner += "( "; | ||
| 2294 | std::visit(*this, *expr.operand1); | ||
| 2295 | inner += " || "; | ||
| 2296 | std::visit(*this, *expr.operand2); | ||
| 2297 | inner += ')'; | ||
| 2298 | } | ||
| 2299 | |||
| 2300 | void operator()(VideoCommon::Shader::ExprNot& expr) { | ||
| 2301 | inner += '!'; | ||
| 2302 | std::visit(*this, *expr.operand1); | ||
| 2303 | } | ||
| 2304 | |||
| 2305 | void operator()(VideoCommon::Shader::ExprPredicate& expr) { | ||
| 2306 | const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate); | ||
| 2307 | inner += decomp.GetPredicate(pred); | ||
| 2308 | } | ||
| 2309 | |||
| 2310 | void operator()(VideoCommon::Shader::ExprCondCode& expr) { | ||
| 2311 | const Node cc = decomp.ir.GetConditionCode(expr.cc); | ||
| 2312 | std::string target; | ||
| 2313 | |||
| 2314 | if (const auto pred = std::get_if<PredicateNode>(&*cc)) { | ||
| 2315 | const auto index = pred->GetIndex(); | ||
| 2316 | switch (index) { | ||
| 2317 | case Tegra::Shader::Pred::NeverExecute: | ||
| 2318 | target = "false"; | ||
| 2319 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 2320 | target = "true"; | ||
| 2321 | default: | ||
| 2322 | target = decomp.GetPredicate(index); | ||
| 2323 | } | ||
| 2324 | } else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) { | ||
| 2325 | target = decomp.GetInternalFlag(flag->GetFlag()); | ||
| 2326 | } else { | ||
| 2327 | UNREACHABLE(); | ||
| 2328 | } | ||
| 2329 | inner += target; | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | void operator()(VideoCommon::Shader::ExprVar& expr) { | ||
| 2333 | inner += GetFlowVariable(expr.var_index); | ||
| 2334 | } | ||
| 2335 | |||
| 2336 | void operator()(VideoCommon::Shader::ExprBoolean& expr) { | ||
| 2337 | inner += expr.value ? "true" : "false"; | ||
| 2338 | } | ||
| 2339 | |||
| 2340 | std::string& GetResult() { | ||
| 2341 | return inner; | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | private: | ||
| 2345 | std::string inner; | ||
| 2346 | GLSLDecompiler& decomp; | ||
| 2347 | }; | ||
| 2348 | |||
| 2349 | class ASTDecompiler { | ||
| 2350 | public: | ||
| 2351 | explicit ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} | ||
| 2352 | |||
| 2353 | void operator()(VideoCommon::Shader::ASTProgram& ast) { | ||
| 2354 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2355 | while (current) { | ||
| 2356 | Visit(current); | ||
| 2357 | current = current->GetNext(); | ||
| 2358 | } | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | void operator()(VideoCommon::Shader::ASTIfThen& ast) { | ||
| 2362 | ExprDecompiler expr_parser{decomp}; | ||
| 2363 | std::visit(expr_parser, *ast.condition); | ||
| 2364 | decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); | ||
| 2365 | decomp.code.scope++; | ||
| 2366 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2367 | while (current) { | ||
| 2368 | Visit(current); | ||
| 2369 | current = current->GetNext(); | ||
| 2370 | } | ||
| 2371 | decomp.code.scope--; | ||
| 2372 | decomp.code.AddLine("}}"); | ||
| 2373 | } | ||
| 2374 | |||
| 2375 | void operator()(VideoCommon::Shader::ASTIfElse& ast) { | ||
| 2376 | decomp.code.AddLine("else {{"); | ||
| 2377 | decomp.code.scope++; | ||
| 2378 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2379 | while (current) { | ||
| 2380 | Visit(current); | ||
| 2381 | current = current->GetNext(); | ||
| 2382 | } | ||
| 2383 | decomp.code.scope--; | ||
| 2384 | decomp.code.AddLine("}}"); | ||
| 2385 | } | ||
| 2386 | |||
| 2387 | void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) { | ||
| 2388 | UNREACHABLE(); | ||
| 2389 | } | ||
| 2390 | |||
| 2391 | void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) { | ||
| 2392 | decomp.VisitBlock(ast.nodes); | ||
| 2393 | } | ||
| 2394 | |||
| 2395 | void operator()(VideoCommon::Shader::ASTVarSet& ast) { | ||
| 2396 | ExprDecompiler expr_parser{decomp}; | ||
| 2397 | std::visit(expr_parser, *ast.condition); | ||
| 2398 | decomp.code.AddLine("{} = {};", GetFlowVariable(ast.index), expr_parser.GetResult()); | ||
| 2399 | } | ||
| 2400 | |||
| 2401 | void operator()(VideoCommon::Shader::ASTLabel& ast) { | ||
| 2402 | decomp.code.AddLine("// Label_{}:", ast.index); | ||
| 2403 | } | ||
| 2404 | |||
| 2405 | void operator()(VideoCommon::Shader::ASTGoto& ast) { | ||
| 2406 | UNREACHABLE(); | ||
| 2407 | } | ||
| 2408 | |||
| 2409 | void operator()(VideoCommon::Shader::ASTDoWhile& ast) { | ||
| 2410 | ExprDecompiler expr_parser{decomp}; | ||
| 2411 | std::visit(expr_parser, *ast.condition); | ||
| 2412 | decomp.code.AddLine("do {{"); | ||
| 2413 | decomp.code.scope++; | ||
| 2414 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2415 | while (current) { | ||
| 2416 | Visit(current); | ||
| 2417 | current = current->GetNext(); | ||
| 2418 | } | ||
| 2419 | decomp.code.scope--; | ||
| 2420 | decomp.code.AddLine("}} while({});", expr_parser.GetResult()); | ||
| 2421 | } | ||
| 2422 | |||
| 2423 | void operator()(VideoCommon::Shader::ASTReturn& ast) { | ||
| 2424 | const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); | ||
| 2425 | if (!is_true) { | ||
| 2426 | ExprDecompiler expr_parser{decomp}; | ||
| 2427 | std::visit(expr_parser, *ast.condition); | ||
| 2428 | decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); | ||
| 2429 | decomp.code.scope++; | ||
| 2430 | } | ||
| 2431 | if (ast.kills) { | ||
| 2432 | decomp.code.AddLine("discard;"); | ||
| 2433 | } else { | ||
| 2434 | decomp.PreExit(); | ||
| 2435 | decomp.code.AddLine("return;"); | ||
| 2436 | } | ||
| 2437 | if (!is_true) { | ||
| 2438 | decomp.code.scope--; | ||
| 2439 | decomp.code.AddLine("}}"); | ||
| 2440 | } | ||
| 2441 | } | ||
| 2442 | |||
| 2443 | void operator()(VideoCommon::Shader::ASTBreak& ast) { | ||
| 2444 | const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); | ||
| 2445 | if (!is_true) { | ||
| 2446 | ExprDecompiler expr_parser{decomp}; | ||
| 2447 | std::visit(expr_parser, *ast.condition); | ||
| 2448 | decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); | ||
| 2449 | decomp.code.scope++; | ||
| 2450 | } | ||
| 2451 | decomp.code.AddLine("break;"); | ||
| 2452 | if (!is_true) { | ||
| 2453 | decomp.code.scope--; | ||
| 2454 | decomp.code.AddLine("}}"); | ||
| 2455 | } | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | void Visit(VideoCommon::Shader::ASTNode& node) { | ||
| 2459 | std::visit(*this, *node->GetInnerData()); | ||
| 2460 | } | ||
| 2461 | |||
| 2462 | private: | ||
| 2463 | GLSLDecompiler& decomp; | ||
| 2464 | }; | ||
| 2465 | |||
| 2466 | void GLSLDecompiler::DecompileAST() { | ||
| 2467 | const u32 num_flow_variables = ir.GetASTNumVariables(); | ||
| 2468 | for (u32 i = 0; i < num_flow_variables; i++) { | ||
| 2469 | code.AddLine("bool {} = false;", GetFlowVariable(i)); | ||
| 2470 | } | ||
| 2471 | ASTDecompiler decompiler{*this}; | ||
| 2472 | VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); | ||
| 2473 | decompiler.Visit(program); | ||
| 2474 | } | ||
| 2475 | |||
| 2306 | } // Anonymous namespace | 2476 | } // Anonymous namespace |
| 2307 | 2477 | ||
| 2308 | std::string GetCommonDeclarations() { | 2478 | std::string GetCommonDeclarations() { |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 2ea02f5bf..e538dc001 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h | |||
| @@ -90,7 +90,6 @@ struct ShaderEntries { | |||
| 90 | std::vector<ImageEntry> images; | 90 | std::vector<ImageEntry> images; |
| 91 | std::vector<GlobalMemoryEntry> global_memory_entries; | 91 | std::vector<GlobalMemoryEntry> global_memory_entries; |
| 92 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | 92 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; |
| 93 | bool shader_viewport_layer_array{}; | ||
| 94 | std::size_t shader_length{}; | 93 | std::size_t shader_length{}; |
| 95 | }; | 94 | }; |
| 96 | 95 | ||
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 f141c4e3b..74cc33476 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -112,14 +112,15 @@ std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskC | |||
| 112 | ShaderDiskCacheOpenGL::LoadTransferable() { | 112 | ShaderDiskCacheOpenGL::LoadTransferable() { |
| 113 | // Skip games without title id | 113 | // Skip games without title id |
| 114 | const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; | 114 | const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; |
| 115 | if (!Settings::values.use_disk_shader_cache || !has_title_id) | 115 | if (!Settings::values.use_disk_shader_cache || !has_title_id) { |
| 116 | return {}; | 116 | return {}; |
| 117 | tried_to_load = true; | 117 | } |
| 118 | 118 | ||
| 119 | FileUtil::IOFile file(GetTransferablePath(), "rb"); | 119 | FileUtil::IOFile file(GetTransferablePath(), "rb"); |
| 120 | if (!file.IsOpen()) { | 120 | if (!file.IsOpen()) { |
| 121 | LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}", | 121 | LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}", |
| 122 | GetTitleID()); | 122 | GetTitleID()); |
| 123 | is_usable = true; | ||
| 123 | return {}; | 124 | return {}; |
| 124 | } | 125 | } |
| 125 | 126 | ||
| @@ -135,6 +136,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() { | |||
| 135 | LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing"); | 136 | LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing"); |
| 136 | file.Close(); | 137 | file.Close(); |
| 137 | InvalidateTransferable(); | 138 | InvalidateTransferable(); |
| 139 | is_usable = true; | ||
| 138 | return {}; | 140 | return {}; |
| 139 | } | 141 | } |
| 140 | if (version > NativeVersion) { | 142 | if (version > NativeVersion) { |
| @@ -180,13 +182,15 @@ ShaderDiskCacheOpenGL::LoadTransferable() { | |||
| 180 | } | 182 | } |
| 181 | } | 183 | } |
| 182 | 184 | ||
| 183 | return {{raws, usages}}; | 185 | is_usable = true; |
| 186 | return {{std::move(raws), std::move(usages)}}; | ||
| 184 | } | 187 | } |
| 185 | 188 | ||
| 186 | std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap> | 189 | std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap> |
| 187 | ShaderDiskCacheOpenGL::LoadPrecompiled() { | 190 | ShaderDiskCacheOpenGL::LoadPrecompiled() { |
| 188 | if (!IsUsable()) | 191 | if (!is_usable) { |
| 189 | return {}; | 192 | return {}; |
| 193 | } | ||
| 190 | 194 | ||
| 191 | FileUtil::IOFile file(GetPrecompiledPath(), "rb"); | 195 | FileUtil::IOFile file(GetPrecompiledPath(), "rb"); |
| 192 | if (!file.IsOpen()) { | 196 | if (!file.IsOpen()) { |
| @@ -343,20 +347,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn | |||
| 343 | u8 is_bindless{}; | 347 | u8 is_bindless{}; |
| 344 | u8 is_written{}; | 348 | u8 is_written{}; |
| 345 | u8 is_read{}; | 349 | u8 is_read{}; |
| 346 | u8 is_size_known{}; | 350 | u8 is_atomic{}; |
| 347 | u32 size{}; | ||
| 348 | if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || | 351 | if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || |
| 349 | !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) || | 352 | !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) || |
| 350 | !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) || | 353 | !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) || |
| 351 | !LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) { | 354 | !LoadObjectFromPrecompiled(is_atomic)) { |
| 352 | return {}; | 355 | return {}; |
| 353 | } | 356 | } |
| 354 | entry.entries.images.emplace_back( | 357 | entry.entries.images.emplace_back( |
| 355 | static_cast<std::size_t>(offset), static_cast<std::size_t>(index), | 358 | static_cast<std::size_t>(offset), static_cast<std::size_t>(index), |
| 356 | static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0, | 359 | static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0, |
| 357 | is_read != 0, | 360 | is_read != 0, is_atomic != 0); |
| 358 | is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size)) | ||
| 359 | : std::nullopt); | ||
| 360 | } | 361 | } |
| 361 | 362 | ||
| 362 | u32 global_memory_count{}; | 363 | u32 global_memory_count{}; |
| @@ -382,12 +383,6 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn | |||
| 382 | } | 383 | } |
| 383 | } | 384 | } |
| 384 | 385 | ||
| 385 | bool shader_viewport_layer_array{}; | ||
| 386 | if (!LoadObjectFromPrecompiled(shader_viewport_layer_array)) { | ||
| 387 | return {}; | ||
| 388 | } | ||
| 389 | entry.entries.shader_viewport_layer_array = shader_viewport_layer_array; | ||
| 390 | |||
| 391 | u64 shader_length{}; | 386 | u64 shader_length{}; |
| 392 | if (!LoadObjectFromPrecompiled(shader_length)) { | 387 | if (!LoadObjectFromPrecompiled(shader_length)) { |
| 393 | return {}; | 388 | return {}; |
| @@ -435,14 +430,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std: | |||
| 435 | return false; | 430 | return false; |
| 436 | } | 431 | } |
| 437 | for (const auto& image : entries.images) { | 432 | for (const auto& image : entries.images) { |
| 438 | const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U; | ||
| 439 | if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) || | 433 | if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) || |
| 440 | !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) || | 434 | !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) || |
| 441 | !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) || | 435 | !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) || |
| 442 | !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) || | 436 | !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) || |
| 443 | !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) || | 437 | !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) || |
| 444 | !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) || | 438 | !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) || |
| 445 | !SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) { | 439 | !SaveObjectToPrecompiled(static_cast<u8>(image.IsAtomic() ? 1 : 0))) { |
| 446 | return false; | 440 | return false; |
| 447 | } | 441 | } |
| 448 | } | 442 | } |
| @@ -464,10 +458,6 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std: | |||
| 464 | } | 458 | } |
| 465 | } | 459 | } |
| 466 | 460 | ||
| 467 | if (!SaveObjectToPrecompiled(entries.shader_viewport_layer_array)) { | ||
| 468 | return false; | ||
| 469 | } | ||
| 470 | |||
| 471 | if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) { | 461 | if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) { |
| 472 | return false; | 462 | return false; |
| 473 | } | 463 | } |
| @@ -493,8 +483,9 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() { | |||
| 493 | } | 483 | } |
| 494 | 484 | ||
| 495 | void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { | 485 | void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { |
| 496 | if (!IsUsable()) | 486 | if (!is_usable) { |
| 497 | return; | 487 | return; |
| 488 | } | ||
| 498 | 489 | ||
| 499 | const u64 id = entry.GetUniqueIdentifier(); | 490 | const u64 id = entry.GetUniqueIdentifier(); |
| 500 | if (transferable.find(id) != transferable.end()) { | 491 | if (transferable.find(id) != transferable.end()) { |
| @@ -515,8 +506,9 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { | |||
| 515 | } | 506 | } |
| 516 | 507 | ||
| 517 | void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { | 508 | void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { |
| 518 | if (!IsUsable()) | 509 | if (!is_usable) { |
| 519 | return; | 510 | return; |
| 511 | } | ||
| 520 | 512 | ||
| 521 | const auto it = transferable.find(usage.unique_identifier); | 513 | const auto it = transferable.find(usage.unique_identifier); |
| 522 | ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); | 514 | ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); |
| @@ -542,8 +534,9 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { | |||
| 542 | 534 | ||
| 543 | void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::string& code, | 535 | void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::string& code, |
| 544 | const GLShader::ShaderEntries& entries) { | 536 | const GLShader::ShaderEntries& entries) { |
| 545 | if (!IsUsable()) | 537 | if (!is_usable) { |
| 546 | return; | 538 | return; |
| 539 | } | ||
| 547 | 540 | ||
| 548 | if (precompiled_cache_virtual_file.GetSize() == 0) { | 541 | if (precompiled_cache_virtual_file.GetSize() == 0) { |
| 549 | SavePrecompiledHeaderToVirtualPrecompiledCache(); | 542 | SavePrecompiledHeaderToVirtualPrecompiledCache(); |
| @@ -557,8 +550,9 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str | |||
| 557 | } | 550 | } |
| 558 | 551 | ||
| 559 | void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) { | 552 | void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) { |
| 560 | if (!IsUsable()) | 553 | if (!is_usable) { |
| 561 | return; | 554 | return; |
| 555 | } | ||
| 562 | 556 | ||
| 563 | GLint binary_length{}; | 557 | GLint binary_length{}; |
| 564 | glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length); | 558 | glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length); |
| @@ -579,10 +573,6 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p | |||
| 579 | } | 573 | } |
| 580 | } | 574 | } |
| 581 | 575 | ||
| 582 | bool ShaderDiskCacheOpenGL::IsUsable() const { | ||
| 583 | return tried_to_load && Settings::values.use_disk_shader_cache; | ||
| 584 | } | ||
| 585 | |||
| 586 | FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { | 576 | FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { |
| 587 | if (!EnsureDirectories()) | 577 | if (!EnsureDirectories()) |
| 588 | return {}; | 578 | return {}; |
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 cc8bbd61e..9595bd71b 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -224,9 +224,6 @@ private: | |||
| 224 | bool SaveDecompiledFile(u64 unique_identifier, const std::string& code, | 224 | bool SaveDecompiledFile(u64 unique_identifier, const std::string& code, |
| 225 | const GLShader::ShaderEntries& entries); | 225 | const GLShader::ShaderEntries& entries); |
| 226 | 226 | ||
| 227 | /// Returns if the cache can be used | ||
| 228 | bool IsUsable() const; | ||
| 229 | |||
| 230 | /// Opens current game's transferable file and write it's header if it doesn't exist | 227 | /// Opens current game's transferable file and write it's header if it doesn't exist |
| 231 | FileUtil::IOFile AppendTransferableFile() const; | 228 | FileUtil::IOFile AppendTransferableFile() const; |
| 232 | 229 | ||
| @@ -297,7 +294,7 @@ private: | |||
| 297 | std::unordered_map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; | 294 | std::unordered_map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; |
| 298 | 295 | ||
| 299 | // The cache has been loaded at boot | 296 | // The cache has been loaded at boot |
| 300 | bool tried_to_load{}; | 297 | bool is_usable{}; |
| 301 | }; | 298 | }; |
| 302 | 299 | ||
| 303 | } // namespace OpenGL | 300 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 3a8d9e1da..b5a43e79e 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -11,12 +11,16 @@ | |||
| 11 | namespace OpenGL::GLShader { | 11 | namespace OpenGL::GLShader { |
| 12 | 12 | ||
| 13 | using Tegra::Engines::Maxwell3D; | 13 | using Tegra::Engines::Maxwell3D; |
| 14 | using VideoCommon::Shader::CompileDepth; | ||
| 15 | using VideoCommon::Shader::CompilerSettings; | ||
| 14 | using VideoCommon::Shader::ProgramCode; | 16 | using VideoCommon::Shader::ProgramCode; |
| 15 | using VideoCommon::Shader::ShaderIR; | 17 | using VideoCommon::Shader::ShaderIR; |
| 16 | 18 | ||
| 17 | static constexpr u32 PROGRAM_OFFSET = 10; | 19 | static constexpr u32 PROGRAM_OFFSET = 10; |
| 18 | static constexpr u32 COMPUTE_OFFSET = 0; | 20 | static constexpr u32 COMPUTE_OFFSET = 0; |
| 19 | 21 | ||
| 22 | static constexpr CompilerSettings settings{CompileDepth::NoFlowStack, true}; | ||
| 23 | |||
| 20 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { | 24 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { |
| 21 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 25 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 22 | 26 | ||
| @@ -31,13 +35,14 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { | |||
| 31 | 35 | ||
| 32 | )"; | 36 | )"; |
| 33 | 37 | ||
| 34 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); | 38 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); |
| 35 | const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; | 39 | const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; |
| 36 | ProgramResult program = Decompile(device, program_ir, stage, "vertex"); | 40 | ProgramResult program = Decompile(device, program_ir, stage, "vertex"); |
| 37 | out += program.first; | 41 | out += program.first; |
| 38 | 42 | ||
| 39 | if (setup.IsDualProgram()) { | 43 | if (setup.IsDualProgram()) { |
| 40 | const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b); | 44 | const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b, |
| 45 | settings); | ||
| 41 | ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b"); | 46 | ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b"); |
| 42 | out += program_b.first; | 47 | out += program_b.first; |
| 43 | } | 48 | } |
| @@ -80,7 +85,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { | |||
| 80 | 85 | ||
| 81 | )"; | 86 | )"; |
| 82 | 87 | ||
| 83 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); | 88 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); |
| 84 | ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); | 89 | ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); |
| 85 | out += program.first; | 90 | out += program.first; |
| 86 | 91 | ||
| @@ -114,7 +119,8 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config { | |||
| 114 | }; | 119 | }; |
| 115 | 120 | ||
| 116 | )"; | 121 | )"; |
| 117 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); | 122 | |
| 123 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); | ||
| 118 | ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); | 124 | ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); |
| 119 | out += program.first; | 125 | out += program.first; |
| 120 | 126 | ||
| @@ -133,7 +139,7 @@ ProgramResult GenerateComputeShader(const Device& device, const ShaderSetup& set | |||
| 133 | std::string out = "// Shader Unique Id: CS" + id + "\n\n"; | 139 | std::string out = "// Shader Unique Id: CS" + id + "\n\n"; |
| 134 | out += GetCommonDeclarations(); | 140 | out += GetCommonDeclarations(); |
| 135 | 141 | ||
| 136 | const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a); | 142 | const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a, settings); |
| 137 | ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); | 143 | ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); |
| 138 | out += program.first; | 144 | out += program.first; |
| 139 | 145 | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index f7fbbb6e4..8bcd04221 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "video_core/engines/shader_header.h" | 19 | #include "video_core/engines/shader_header.h" |
| 20 | #include "video_core/renderer_vulkan/vk_device.h" | 20 | #include "video_core/renderer_vulkan/vk_device.h" |
| 21 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | 21 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" |
| 22 | #include "video_core/shader/node.h" | ||
| 22 | #include "video_core/shader/shader_ir.h" | 23 | #include "video_core/shader/shader_ir.h" |
| 23 | 24 | ||
| 24 | namespace Vulkan::VKShader { | 25 | namespace Vulkan::VKShader { |
| @@ -87,6 +88,9 @@ bool IsPrecise(Operation operand) { | |||
| 87 | 88 | ||
| 88 | } // namespace | 89 | } // namespace |
| 89 | 90 | ||
| 91 | class ASTDecompiler; | ||
| 92 | class ExprDecompiler; | ||
| 93 | |||
| 90 | class SPIRVDecompiler : public Sirit::Module { | 94 | class SPIRVDecompiler : public Sirit::Module { |
| 91 | public: | 95 | public: |
| 92 | explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage) | 96 | explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage) |
| @@ -96,27 +100,7 @@ public: | |||
| 96 | AddExtension("SPV_KHR_variable_pointers"); | 100 | AddExtension("SPV_KHR_variable_pointers"); |
| 97 | } | 101 | } |
| 98 | 102 | ||
| 99 | void Decompile() { | 103 | void DecompileBranchMode() { |
| 100 | AllocateBindings(); | ||
| 101 | AllocateLabels(); | ||
| 102 | |||
| 103 | DeclareVertex(); | ||
| 104 | DeclareGeometry(); | ||
| 105 | DeclareFragment(); | ||
| 106 | DeclareRegisters(); | ||
| 107 | DeclarePredicates(); | ||
| 108 | DeclareLocalMemory(); | ||
| 109 | DeclareInternalFlags(); | ||
| 110 | DeclareInputAttributes(); | ||
| 111 | DeclareOutputAttributes(); | ||
| 112 | DeclareConstantBuffers(); | ||
| 113 | DeclareGlobalBuffers(); | ||
| 114 | DeclareSamplers(); | ||
| 115 | |||
| 116 | execute_function = | ||
| 117 | Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); | ||
| 118 | Emit(OpLabel()); | ||
| 119 | |||
| 120 | const u32 first_address = ir.GetBasicBlocks().begin()->first; | 104 | const u32 first_address = ir.GetBasicBlocks().begin()->first; |
| 121 | const Id loop_label = OpLabel("loop"); | 105 | const Id loop_label = OpLabel("loop"); |
| 122 | const Id merge_label = OpLabel("merge"); | 106 | const Id merge_label = OpLabel("merge"); |
| @@ -173,6 +157,43 @@ public: | |||
| 173 | Emit(continue_label); | 157 | Emit(continue_label); |
| 174 | Emit(OpBranch(loop_label)); | 158 | Emit(OpBranch(loop_label)); |
| 175 | Emit(merge_label); | 159 | Emit(merge_label); |
| 160 | } | ||
| 161 | |||
| 162 | void DecompileAST(); | ||
| 163 | |||
| 164 | void Decompile() { | ||
| 165 | const bool is_fully_decompiled = ir.IsDecompiled(); | ||
| 166 | AllocateBindings(); | ||
| 167 | if (!is_fully_decompiled) { | ||
| 168 | AllocateLabels(); | ||
| 169 | } | ||
| 170 | |||
| 171 | DeclareVertex(); | ||
| 172 | DeclareGeometry(); | ||
| 173 | DeclareFragment(); | ||
| 174 | DeclareRegisters(); | ||
| 175 | DeclarePredicates(); | ||
| 176 | if (is_fully_decompiled) { | ||
| 177 | DeclareFlowVariables(); | ||
| 178 | } | ||
| 179 | DeclareLocalMemory(); | ||
| 180 | DeclareInternalFlags(); | ||
| 181 | DeclareInputAttributes(); | ||
| 182 | DeclareOutputAttributes(); | ||
| 183 | DeclareConstantBuffers(); | ||
| 184 | DeclareGlobalBuffers(); | ||
| 185 | DeclareSamplers(); | ||
| 186 | |||
| 187 | execute_function = | ||
| 188 | Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); | ||
| 189 | Emit(OpLabel()); | ||
| 190 | |||
| 191 | if (is_fully_decompiled) { | ||
| 192 | DecompileAST(); | ||
| 193 | } else { | ||
| 194 | DecompileBranchMode(); | ||
| 195 | } | ||
| 196 | |||
| 176 | Emit(OpReturn()); | 197 | Emit(OpReturn()); |
| 177 | Emit(OpFunctionEnd()); | 198 | Emit(OpFunctionEnd()); |
| 178 | } | 199 | } |
| @@ -205,6 +226,9 @@ public: | |||
| 205 | } | 226 | } |
| 206 | 227 | ||
| 207 | private: | 228 | private: |
| 229 | friend class ASTDecompiler; | ||
| 230 | friend class ExprDecompiler; | ||
| 231 | |||
| 208 | static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount); | 232 | static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount); |
| 209 | 233 | ||
| 210 | void AllocateBindings() { | 234 | void AllocateBindings() { |
| @@ -293,6 +317,14 @@ private: | |||
| 293 | } | 317 | } |
| 294 | } | 318 | } |
| 295 | 319 | ||
| 320 | void DeclareFlowVariables() { | ||
| 321 | for (u32 i = 0; i < ir.GetASTNumVariables(); i++) { | ||
| 322 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 323 | Name(id, fmt::format("flow_var_{}", static_cast<u32>(i))); | ||
| 324 | flow_variables.emplace(i, AddGlobalVariable(id)); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 296 | void DeclareLocalMemory() { | 328 | void DeclareLocalMemory() { |
| 297 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { | 329 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { |
| 298 | const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4); | 330 | const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4); |
| @@ -614,9 +646,15 @@ private: | |||
| 614 | Emit(OpBranchConditional(condition, true_label, skip_label)); | 646 | Emit(OpBranchConditional(condition, true_label, skip_label)); |
| 615 | Emit(true_label); | 647 | Emit(true_label); |
| 616 | 648 | ||
| 649 | ++conditional_nest_count; | ||
| 617 | VisitBasicBlock(conditional->GetCode()); | 650 | VisitBasicBlock(conditional->GetCode()); |
| 651 | --conditional_nest_count; | ||
| 618 | 652 | ||
| 619 | Emit(OpBranch(skip_label)); | 653 | if (inside_branch == 0) { |
| 654 | Emit(OpBranch(skip_label)); | ||
| 655 | } else { | ||
| 656 | inside_branch--; | ||
| 657 | } | ||
| 620 | Emit(skip_label); | 658 | Emit(skip_label); |
| 621 | return {}; | 659 | return {}; |
| 622 | 660 | ||
| @@ -939,22 +977,17 @@ private: | |||
| 939 | return {}; | 977 | return {}; |
| 940 | } | 978 | } |
| 941 | 979 | ||
| 942 | Id ImageStore(Operation operation) { | 980 | Id ImageLoad(Operation operation) { |
| 943 | UNIMPLEMENTED(); | ||
| 944 | return {}; | ||
| 945 | } | ||
| 946 | |||
| 947 | Id AtomicImageAdd(Operation operation) { | ||
| 948 | UNIMPLEMENTED(); | 981 | UNIMPLEMENTED(); |
| 949 | return {}; | 982 | return {}; |
| 950 | } | 983 | } |
| 951 | 984 | ||
| 952 | Id AtomicImageMin(Operation operation) { | 985 | Id ImageStore(Operation operation) { |
| 953 | UNIMPLEMENTED(); | 986 | UNIMPLEMENTED(); |
| 954 | return {}; | 987 | return {}; |
| 955 | } | 988 | } |
| 956 | 989 | ||
| 957 | Id AtomicImageMax(Operation operation) { | 990 | Id AtomicImageAdd(Operation operation) { |
| 958 | UNIMPLEMENTED(); | 991 | UNIMPLEMENTED(); |
| 959 | return {}; | 992 | return {}; |
| 960 | } | 993 | } |
| @@ -984,7 +1017,11 @@ private: | |||
| 984 | UNIMPLEMENTED_IF(!target); | 1017 | UNIMPLEMENTED_IF(!target); |
| 985 | 1018 | ||
| 986 | Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); | 1019 | Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); |
| 987 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | 1020 | Emit(OpBranch(continue_label)); |
| 1021 | inside_branch = conditional_nest_count; | ||
| 1022 | if (conditional_nest_count == 0) { | ||
| 1023 | Emit(OpLabel()); | ||
| 1024 | } | ||
| 988 | return {}; | 1025 | return {}; |
| 989 | } | 1026 | } |
| 990 | 1027 | ||
| @@ -992,7 +1029,11 @@ private: | |||
| 992 | const Id op_a = VisitOperand<Type::Uint>(operation, 0); | 1029 | const Id op_a = VisitOperand<Type::Uint>(operation, 0); |
| 993 | 1030 | ||
| 994 | Emit(OpStore(jmp_to, op_a)); | 1031 | Emit(OpStore(jmp_to, op_a)); |
| 995 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | 1032 | Emit(OpBranch(continue_label)); |
| 1033 | inside_branch = conditional_nest_count; | ||
| 1034 | if (conditional_nest_count == 0) { | ||
| 1035 | Emit(OpLabel()); | ||
| 1036 | } | ||
| 996 | return {}; | 1037 | return {}; |
| 997 | } | 1038 | } |
| 998 | 1039 | ||
| @@ -1019,11 +1060,15 @@ private: | |||
| 1019 | 1060 | ||
| 1020 | Emit(OpStore(flow_stack_top, previous)); | 1061 | Emit(OpStore(flow_stack_top, previous)); |
| 1021 | Emit(OpStore(jmp_to, target)); | 1062 | Emit(OpStore(jmp_to, target)); |
| 1022 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | 1063 | Emit(OpBranch(continue_label)); |
| 1064 | inside_branch = conditional_nest_count; | ||
| 1065 | if (conditional_nest_count == 0) { | ||
| 1066 | Emit(OpLabel()); | ||
| 1067 | } | ||
| 1023 | return {}; | 1068 | return {}; |
| 1024 | } | 1069 | } |
| 1025 | 1070 | ||
| 1026 | Id Exit(Operation operation) { | 1071 | Id PreExit() { |
| 1027 | switch (stage) { | 1072 | switch (stage) { |
| 1028 | case ShaderStage::Vertex: { | 1073 | case ShaderStage::Vertex: { |
| 1029 | // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't | 1074 | // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't |
| @@ -1071,12 +1116,35 @@ private: | |||
| 1071 | } | 1116 | } |
| 1072 | } | 1117 | } |
| 1073 | 1118 | ||
| 1074 | BranchingOp([&]() { Emit(OpReturn()); }); | 1119 | return {}; |
| 1120 | } | ||
| 1121 | |||
| 1122 | Id Exit(Operation operation) { | ||
| 1123 | PreExit(); | ||
| 1124 | inside_branch = conditional_nest_count; | ||
| 1125 | if (conditional_nest_count > 0) { | ||
| 1126 | Emit(OpReturn()); | ||
| 1127 | } else { | ||
| 1128 | const Id dummy = OpLabel(); | ||
| 1129 | Emit(OpBranch(dummy)); | ||
| 1130 | Emit(dummy); | ||
| 1131 | Emit(OpReturn()); | ||
| 1132 | Emit(OpLabel()); | ||
| 1133 | } | ||
| 1075 | return {}; | 1134 | return {}; |
| 1076 | } | 1135 | } |
| 1077 | 1136 | ||
| 1078 | Id Discard(Operation operation) { | 1137 | Id Discard(Operation operation) { |
| 1079 | BranchingOp([&]() { Emit(OpKill()); }); | 1138 | inside_branch = conditional_nest_count; |
| 1139 | if (conditional_nest_count > 0) { | ||
| 1140 | Emit(OpKill()); | ||
| 1141 | } else { | ||
| 1142 | const Id dummy = OpLabel(); | ||
| 1143 | Emit(OpBranch(dummy)); | ||
| 1144 | Emit(dummy); | ||
| 1145 | Emit(OpKill()); | ||
| 1146 | Emit(OpLabel()); | ||
| 1147 | } | ||
| 1080 | return {}; | 1148 | return {}; |
| 1081 | } | 1149 | } |
| 1082 | 1150 | ||
| @@ -1271,17 +1339,6 @@ private: | |||
| 1271 | return {}; | 1339 | return {}; |
| 1272 | } | 1340 | } |
| 1273 | 1341 | ||
| 1274 | void BranchingOp(std::function<void()> call) { | ||
| 1275 | const Id true_label = OpLabel(); | ||
| 1276 | const Id skip_label = OpLabel(); | ||
| 1277 | Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten)); | ||
| 1278 | Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0)); | ||
| 1279 | Emit(true_label); | ||
| 1280 | call(); | ||
| 1281 | |||
| 1282 | Emit(skip_label); | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | std::tuple<Id, Id> CreateFlowStack() { | 1342 | std::tuple<Id, Id> CreateFlowStack() { |
| 1286 | // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely | 1343 | // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely |
| 1287 | // that shaders will use 20 nested SSYs and PBKs. | 1344 | // that shaders will use 20 nested SSYs and PBKs. |
| @@ -1440,10 +1497,9 @@ private: | |||
| 1440 | &SPIRVDecompiler::TextureQueryLod, | 1497 | &SPIRVDecompiler::TextureQueryLod, |
| 1441 | &SPIRVDecompiler::TexelFetch, | 1498 | &SPIRVDecompiler::TexelFetch, |
| 1442 | 1499 | ||
| 1500 | &SPIRVDecompiler::ImageLoad, | ||
| 1443 | &SPIRVDecompiler::ImageStore, | 1501 | &SPIRVDecompiler::ImageStore, |
| 1444 | &SPIRVDecompiler::AtomicImageAdd, | 1502 | &SPIRVDecompiler::AtomicImageAdd, |
| 1445 | &SPIRVDecompiler::AtomicImageMin, | ||
| 1446 | &SPIRVDecompiler::AtomicImageMax, | ||
| 1447 | &SPIRVDecompiler::AtomicImageAnd, | 1503 | &SPIRVDecompiler::AtomicImageAnd, |
| 1448 | &SPIRVDecompiler::AtomicImageOr, | 1504 | &SPIRVDecompiler::AtomicImageOr, |
| 1449 | &SPIRVDecompiler::AtomicImageXor, | 1505 | &SPIRVDecompiler::AtomicImageXor, |
| @@ -1488,6 +1544,8 @@ private: | |||
| 1488 | const ShaderIR& ir; | 1544 | const ShaderIR& ir; |
| 1489 | const ShaderStage stage; | 1545 | const ShaderStage stage; |
| 1490 | const Tegra::Shader::Header header; | 1546 | const Tegra::Shader::Header header; |
| 1547 | u64 conditional_nest_count{}; | ||
| 1548 | u64 inside_branch{}; | ||
| 1491 | 1549 | ||
| 1492 | const Id t_void = Name(TypeVoid(), "void"); | 1550 | const Id t_void = Name(TypeVoid(), "void"); |
| 1493 | 1551 | ||
| @@ -1550,6 +1608,7 @@ private: | |||
| 1550 | Id per_vertex{}; | 1608 | Id per_vertex{}; |
| 1551 | std::map<u32, Id> registers; | 1609 | std::map<u32, Id> registers; |
| 1552 | std::map<Tegra::Shader::Pred, Id> predicates; | 1610 | std::map<Tegra::Shader::Pred, Id> predicates; |
| 1611 | std::map<u32, Id> flow_variables; | ||
| 1553 | Id local_memory{}; | 1612 | Id local_memory{}; |
| 1554 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; | 1613 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; |
| 1555 | std::map<Attribute::Index, Id> input_attributes; | 1614 | std::map<Attribute::Index, Id> input_attributes; |
| @@ -1585,6 +1644,223 @@ private: | |||
| 1585 | std::map<u32, Id> labels; | 1644 | std::map<u32, Id> labels; |
| 1586 | }; | 1645 | }; |
| 1587 | 1646 | ||
| 1647 | class ExprDecompiler { | ||
| 1648 | public: | ||
| 1649 | explicit ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} | ||
| 1650 | |||
| 1651 | Id operator()(VideoCommon::Shader::ExprAnd& expr) { | ||
| 1652 | const Id type_def = decomp.GetTypeDefinition(Type::Bool); | ||
| 1653 | const Id op1 = Visit(expr.operand1); | ||
| 1654 | const Id op2 = Visit(expr.operand2); | ||
| 1655 | return decomp.Emit(decomp.OpLogicalAnd(type_def, op1, op2)); | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | Id operator()(VideoCommon::Shader::ExprOr& expr) { | ||
| 1659 | const Id type_def = decomp.GetTypeDefinition(Type::Bool); | ||
| 1660 | const Id op1 = Visit(expr.operand1); | ||
| 1661 | const Id op2 = Visit(expr.operand2); | ||
| 1662 | return decomp.Emit(decomp.OpLogicalOr(type_def, op1, op2)); | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | Id operator()(VideoCommon::Shader::ExprNot& expr) { | ||
| 1666 | const Id type_def = decomp.GetTypeDefinition(Type::Bool); | ||
| 1667 | const Id op1 = Visit(expr.operand1); | ||
| 1668 | return decomp.Emit(decomp.OpLogicalNot(type_def, op1)); | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | Id operator()(VideoCommon::Shader::ExprPredicate& expr) { | ||
| 1672 | const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate); | ||
| 1673 | return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred))); | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | Id operator()(VideoCommon::Shader::ExprCondCode& expr) { | ||
| 1677 | const Node cc = decomp.ir.GetConditionCode(expr.cc); | ||
| 1678 | Id target; | ||
| 1679 | |||
| 1680 | if (const auto pred = std::get_if<PredicateNode>(&*cc)) { | ||
| 1681 | const auto index = pred->GetIndex(); | ||
| 1682 | switch (index) { | ||
| 1683 | case Tegra::Shader::Pred::NeverExecute: | ||
| 1684 | target = decomp.v_false; | ||
| 1685 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 1686 | target = decomp.v_true; | ||
| 1687 | default: | ||
| 1688 | target = decomp.predicates.at(index); | ||
| 1689 | } | ||
| 1690 | } else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) { | ||
| 1691 | target = decomp.internal_flags.at(static_cast<u32>(flag->GetFlag())); | ||
| 1692 | } | ||
| 1693 | return decomp.Emit(decomp.OpLoad(decomp.t_bool, target)); | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | Id operator()(VideoCommon::Shader::ExprVar& expr) { | ||
| 1697 | return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index))); | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | Id operator()(VideoCommon::Shader::ExprBoolean& expr) { | ||
| 1701 | return expr.value ? decomp.v_true : decomp.v_false; | ||
| 1702 | } | ||
| 1703 | |||
| 1704 | Id Visit(VideoCommon::Shader::Expr& node) { | ||
| 1705 | return std::visit(*this, *node); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | private: | ||
| 1709 | SPIRVDecompiler& decomp; | ||
| 1710 | }; | ||
| 1711 | |||
| 1712 | class ASTDecompiler { | ||
| 1713 | public: | ||
| 1714 | explicit ASTDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} | ||
| 1715 | |||
| 1716 | void operator()(VideoCommon::Shader::ASTProgram& ast) { | ||
| 1717 | ASTNode current = ast.nodes.GetFirst(); | ||
| 1718 | while (current) { | ||
| 1719 | Visit(current); | ||
| 1720 | current = current->GetNext(); | ||
| 1721 | } | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | void operator()(VideoCommon::Shader::ASTIfThen& ast) { | ||
| 1725 | ExprDecompiler expr_parser{decomp}; | ||
| 1726 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1727 | const Id then_label = decomp.OpLabel(); | ||
| 1728 | const Id endif_label = decomp.OpLabel(); | ||
| 1729 | decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); | ||
| 1730 | decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); | ||
| 1731 | decomp.Emit(then_label); | ||
| 1732 | ASTNode current = ast.nodes.GetFirst(); | ||
| 1733 | while (current) { | ||
| 1734 | Visit(current); | ||
| 1735 | current = current->GetNext(); | ||
| 1736 | } | ||
| 1737 | decomp.Emit(decomp.OpBranch(endif_label)); | ||
| 1738 | decomp.Emit(endif_label); | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | void operator()(VideoCommon::Shader::ASTIfElse& ast) { | ||
| 1742 | UNREACHABLE(); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) { | ||
| 1746 | UNREACHABLE(); | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) { | ||
| 1750 | decomp.VisitBasicBlock(ast.nodes); | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | void operator()(VideoCommon::Shader::ASTVarSet& ast) { | ||
| 1754 | ExprDecompiler expr_parser{decomp}; | ||
| 1755 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1756 | decomp.Emit(decomp.OpStore(decomp.flow_variables.at(ast.index), condition)); | ||
| 1757 | } | ||
| 1758 | |||
| 1759 | void operator()(VideoCommon::Shader::ASTLabel& ast) { | ||
| 1760 | // Do nothing | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | void operator()(VideoCommon::Shader::ASTGoto& ast) { | ||
| 1764 | UNREACHABLE(); | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | void operator()(VideoCommon::Shader::ASTDoWhile& ast) { | ||
| 1768 | const Id loop_label = decomp.OpLabel(); | ||
| 1769 | const Id endloop_label = decomp.OpLabel(); | ||
| 1770 | const Id loop_start_block = decomp.OpLabel(); | ||
| 1771 | const Id loop_end_block = decomp.OpLabel(); | ||
| 1772 | current_loop_exit = endloop_label; | ||
| 1773 | decomp.Emit(decomp.OpBranch(loop_label)); | ||
| 1774 | decomp.Emit(loop_label); | ||
| 1775 | decomp.Emit( | ||
| 1776 | decomp.OpLoopMerge(endloop_label, loop_end_block, spv::LoopControlMask::MaskNone)); | ||
| 1777 | decomp.Emit(decomp.OpBranch(loop_start_block)); | ||
| 1778 | decomp.Emit(loop_start_block); | ||
| 1779 | ASTNode current = ast.nodes.GetFirst(); | ||
| 1780 | while (current) { | ||
| 1781 | Visit(current); | ||
| 1782 | current = current->GetNext(); | ||
| 1783 | } | ||
| 1784 | ExprDecompiler expr_parser{decomp}; | ||
| 1785 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1786 | decomp.Emit(decomp.OpBranchConditional(condition, loop_label, endloop_label)); | ||
| 1787 | decomp.Emit(endloop_label); | ||
| 1788 | } | ||
| 1789 | |||
| 1790 | void operator()(VideoCommon::Shader::ASTReturn& ast) { | ||
| 1791 | if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) { | ||
| 1792 | ExprDecompiler expr_parser{decomp}; | ||
| 1793 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1794 | const Id then_label = decomp.OpLabel(); | ||
| 1795 | const Id endif_label = decomp.OpLabel(); | ||
| 1796 | decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); | ||
| 1797 | decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); | ||
| 1798 | decomp.Emit(then_label); | ||
| 1799 | if (ast.kills) { | ||
| 1800 | decomp.Emit(decomp.OpKill()); | ||
| 1801 | } else { | ||
| 1802 | decomp.PreExit(); | ||
| 1803 | decomp.Emit(decomp.OpReturn()); | ||
| 1804 | } | ||
| 1805 | decomp.Emit(endif_label); | ||
| 1806 | } else { | ||
| 1807 | const Id next_block = decomp.OpLabel(); | ||
| 1808 | decomp.Emit(decomp.OpBranch(next_block)); | ||
| 1809 | decomp.Emit(next_block); | ||
| 1810 | if (ast.kills) { | ||
| 1811 | decomp.Emit(decomp.OpKill()); | ||
| 1812 | } else { | ||
| 1813 | decomp.PreExit(); | ||
| 1814 | decomp.Emit(decomp.OpReturn()); | ||
| 1815 | } | ||
| 1816 | decomp.Emit(decomp.OpLabel()); | ||
| 1817 | } | ||
| 1818 | } | ||
| 1819 | |||
| 1820 | void operator()(VideoCommon::Shader::ASTBreak& ast) { | ||
| 1821 | if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) { | ||
| 1822 | ExprDecompiler expr_parser{decomp}; | ||
| 1823 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1824 | const Id then_label = decomp.OpLabel(); | ||
| 1825 | const Id endif_label = decomp.OpLabel(); | ||
| 1826 | decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); | ||
| 1827 | decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); | ||
| 1828 | decomp.Emit(then_label); | ||
| 1829 | decomp.Emit(decomp.OpBranch(current_loop_exit)); | ||
| 1830 | decomp.Emit(endif_label); | ||
| 1831 | } else { | ||
| 1832 | const Id next_block = decomp.OpLabel(); | ||
| 1833 | decomp.Emit(decomp.OpBranch(next_block)); | ||
| 1834 | decomp.Emit(next_block); | ||
| 1835 | decomp.Emit(decomp.OpBranch(current_loop_exit)); | ||
| 1836 | decomp.Emit(decomp.OpLabel()); | ||
| 1837 | } | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | void Visit(VideoCommon::Shader::ASTNode& node) { | ||
| 1841 | std::visit(*this, *node->GetInnerData()); | ||
| 1842 | } | ||
| 1843 | |||
| 1844 | private: | ||
| 1845 | SPIRVDecompiler& decomp; | ||
| 1846 | Id current_loop_exit{}; | ||
| 1847 | }; | ||
| 1848 | |||
| 1849 | void SPIRVDecompiler::DecompileAST() { | ||
| 1850 | const u32 num_flow_variables = ir.GetASTNumVariables(); | ||
| 1851 | for (u32 i = 0; i < num_flow_variables; i++) { | ||
| 1852 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 1853 | Name(id, fmt::format("flow_var_{}", i)); | ||
| 1854 | flow_variables.emplace(i, AddGlobalVariable(id)); | ||
| 1855 | } | ||
| 1856 | ASTDecompiler decompiler{*this}; | ||
| 1857 | VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); | ||
| 1858 | decompiler.Visit(program); | ||
| 1859 | const Id next_block = OpLabel(); | ||
| 1860 | Emit(OpBranch(next_block)); | ||
| 1861 | Emit(next_block); | ||
| 1862 | } | ||
| 1863 | |||
| 1588 | DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, | 1864 | DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, |
| 1589 | Maxwell::ShaderStage stage) { | 1865 | Maxwell::ShaderStage stage) { |
| 1590 | auto decompiler = std::make_unique<SPIRVDecompiler>(device, ir, stage); | 1866 | auto decompiler = std::make_unique<SPIRVDecompiler>(device, ir, stage); |
diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp new file mode 100644 index 000000000..436d45f4b --- /dev/null +++ b/src/video_core/shader/ast.cpp | |||
| @@ -0,0 +1,738 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "video_core/shader/ast.h" | ||
| 10 | #include "video_core/shader/expr.h" | ||
| 11 | |||
| 12 | namespace VideoCommon::Shader { | ||
| 13 | |||
| 14 | ASTZipper::ASTZipper() = default; | ||
| 15 | |||
| 16 | void ASTZipper::Init(const ASTNode new_first, const ASTNode parent) { | ||
| 17 | ASSERT(new_first->manager == nullptr); | ||
| 18 | first = new_first; | ||
| 19 | last = new_first; | ||
| 20 | |||
| 21 | ASTNode current = first; | ||
| 22 | while (current) { | ||
| 23 | current->manager = this; | ||
| 24 | current->parent = parent; | ||
| 25 | last = current; | ||
| 26 | current = current->next; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | void ASTZipper::PushBack(const ASTNode new_node) { | ||
| 31 | ASSERT(new_node->manager == nullptr); | ||
| 32 | new_node->previous = last; | ||
| 33 | if (last) { | ||
| 34 | last->next = new_node; | ||
| 35 | } | ||
| 36 | new_node->next.reset(); | ||
| 37 | last = new_node; | ||
| 38 | if (!first) { | ||
| 39 | first = new_node; | ||
| 40 | } | ||
| 41 | new_node->manager = this; | ||
| 42 | } | ||
| 43 | |||
| 44 | void ASTZipper::PushFront(const ASTNode new_node) { | ||
| 45 | ASSERT(new_node->manager == nullptr); | ||
| 46 | new_node->previous.reset(); | ||
| 47 | new_node->next = first; | ||
| 48 | if (first) { | ||
| 49 | first->previous = new_node; | ||
| 50 | } | ||
| 51 | if (last == first) { | ||
| 52 | last = new_node; | ||
| 53 | } | ||
| 54 | first = new_node; | ||
| 55 | new_node->manager = this; | ||
| 56 | } | ||
| 57 | |||
| 58 | void ASTZipper::InsertAfter(const ASTNode new_node, const ASTNode at_node) { | ||
| 59 | ASSERT(new_node->manager == nullptr); | ||
| 60 | if (!at_node) { | ||
| 61 | PushFront(new_node); | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | const ASTNode next = at_node->next; | ||
| 65 | if (next) { | ||
| 66 | next->previous = new_node; | ||
| 67 | } | ||
| 68 | new_node->previous = at_node; | ||
| 69 | if (at_node == last) { | ||
| 70 | last = new_node; | ||
| 71 | } | ||
| 72 | new_node->next = next; | ||
| 73 | at_node->next = new_node; | ||
| 74 | new_node->manager = this; | ||
| 75 | } | ||
| 76 | |||
| 77 | void ASTZipper::InsertBefore(const ASTNode new_node, const ASTNode at_node) { | ||
| 78 | ASSERT(new_node->manager == nullptr); | ||
| 79 | if (!at_node) { | ||
| 80 | PushBack(new_node); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | const ASTNode previous = at_node->previous; | ||
| 84 | if (previous) { | ||
| 85 | previous->next = new_node; | ||
| 86 | } | ||
| 87 | new_node->next = at_node; | ||
| 88 | if (at_node == first) { | ||
| 89 | first = new_node; | ||
| 90 | } | ||
| 91 | new_node->previous = previous; | ||
| 92 | at_node->previous = new_node; | ||
| 93 | new_node->manager = this; | ||
| 94 | } | ||
| 95 | |||
| 96 | void ASTZipper::DetachTail(ASTNode node) { | ||
| 97 | ASSERT(node->manager == this); | ||
| 98 | if (node == first) { | ||
| 99 | first.reset(); | ||
| 100 | last.reset(); | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | last = node->previous; | ||
| 105 | last->next.reset(); | ||
| 106 | node->previous.reset(); | ||
| 107 | |||
| 108 | ASTNode current = std::move(node); | ||
| 109 | while (current) { | ||
| 110 | current->manager = nullptr; | ||
| 111 | current->parent.reset(); | ||
| 112 | current = current->next; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | void ASTZipper::DetachSegment(const ASTNode start, const ASTNode end) { | ||
| 117 | ASSERT(start->manager == this && end->manager == this); | ||
| 118 | if (start == end) { | ||
| 119 | DetachSingle(start); | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | const ASTNode prev = start->previous; | ||
| 123 | const ASTNode post = end->next; | ||
| 124 | if (!prev) { | ||
| 125 | first = post; | ||
| 126 | } else { | ||
| 127 | prev->next = post; | ||
| 128 | } | ||
| 129 | if (!post) { | ||
| 130 | last = prev; | ||
| 131 | } else { | ||
| 132 | post->previous = prev; | ||
| 133 | } | ||
| 134 | start->previous.reset(); | ||
| 135 | end->next.reset(); | ||
| 136 | ASTNode current = start; | ||
| 137 | bool found = false; | ||
| 138 | while (current) { | ||
| 139 | current->manager = nullptr; | ||
| 140 | current->parent.reset(); | ||
| 141 | found |= current == end; | ||
| 142 | current = current->next; | ||
| 143 | } | ||
| 144 | ASSERT(found); | ||
| 145 | } | ||
| 146 | |||
| 147 | void ASTZipper::DetachSingle(const ASTNode node) { | ||
| 148 | ASSERT(node->manager == this); | ||
| 149 | const ASTNode prev = node->previous; | ||
| 150 | const ASTNode post = node->next; | ||
| 151 | node->previous.reset(); | ||
| 152 | node->next.reset(); | ||
| 153 | if (!prev) { | ||
| 154 | first = post; | ||
| 155 | } else { | ||
| 156 | prev->next = post; | ||
| 157 | } | ||
| 158 | if (!post) { | ||
| 159 | last = prev; | ||
| 160 | } else { | ||
| 161 | post->previous = prev; | ||
| 162 | } | ||
| 163 | |||
| 164 | node->manager = nullptr; | ||
| 165 | node->parent.reset(); | ||
| 166 | } | ||
| 167 | |||
| 168 | void ASTZipper::Remove(const ASTNode node) { | ||
| 169 | ASSERT(node->manager == this); | ||
| 170 | const ASTNode next = node->next; | ||
| 171 | const ASTNode previous = node->previous; | ||
| 172 | if (previous) { | ||
| 173 | previous->next = next; | ||
| 174 | } | ||
| 175 | if (next) { | ||
| 176 | next->previous = previous; | ||
| 177 | } | ||
| 178 | node->parent.reset(); | ||
| 179 | node->manager = nullptr; | ||
| 180 | if (node == last) { | ||
| 181 | last = previous; | ||
| 182 | } | ||
| 183 | if (node == first) { | ||
| 184 | first = next; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | class ExprPrinter final { | ||
| 189 | public: | ||
| 190 | void operator()(const ExprAnd& expr) { | ||
| 191 | inner += "( "; | ||
| 192 | std::visit(*this, *expr.operand1); | ||
| 193 | inner += " && "; | ||
| 194 | std::visit(*this, *expr.operand2); | ||
| 195 | inner += ')'; | ||
| 196 | } | ||
| 197 | |||
| 198 | void operator()(const ExprOr& expr) { | ||
| 199 | inner += "( "; | ||
| 200 | std::visit(*this, *expr.operand1); | ||
| 201 | inner += " || "; | ||
| 202 | std::visit(*this, *expr.operand2); | ||
| 203 | inner += ')'; | ||
| 204 | } | ||
| 205 | |||
| 206 | void operator()(const ExprNot& expr) { | ||
| 207 | inner += "!"; | ||
| 208 | std::visit(*this, *expr.operand1); | ||
| 209 | } | ||
| 210 | |||
| 211 | void operator()(const ExprPredicate& expr) { | ||
| 212 | inner += "P" + std::to_string(expr.predicate); | ||
| 213 | } | ||
| 214 | |||
| 215 | void operator()(const ExprCondCode& expr) { | ||
| 216 | u32 cc = static_cast<u32>(expr.cc); | ||
| 217 | inner += "CC" + std::to_string(cc); | ||
| 218 | } | ||
| 219 | |||
| 220 | void operator()(const ExprVar& expr) { | ||
| 221 | inner += "V" + std::to_string(expr.var_index); | ||
| 222 | } | ||
| 223 | |||
| 224 | void operator()(const ExprBoolean& expr) { | ||
| 225 | inner += expr.value ? "true" : "false"; | ||
| 226 | } | ||
| 227 | |||
| 228 | const std::string& GetResult() const { | ||
| 229 | return inner; | ||
| 230 | } | ||
| 231 | |||
| 232 | std::string inner{}; | ||
| 233 | }; | ||
| 234 | |||
| 235 | class ASTPrinter { | ||
| 236 | public: | ||
| 237 | void operator()(const ASTProgram& ast) { | ||
| 238 | scope++; | ||
| 239 | inner += "program {\n"; | ||
| 240 | ASTNode current = ast.nodes.GetFirst(); | ||
| 241 | while (current) { | ||
| 242 | Visit(current); | ||
| 243 | current = current->GetNext(); | ||
| 244 | } | ||
| 245 | inner += "}\n"; | ||
| 246 | scope--; | ||
| 247 | } | ||
| 248 | |||
| 249 | void operator()(const ASTIfThen& ast) { | ||
| 250 | ExprPrinter expr_parser{}; | ||
| 251 | std::visit(expr_parser, *ast.condition); | ||
| 252 | inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n"; | ||
| 253 | scope++; | ||
| 254 | ASTNode current = ast.nodes.GetFirst(); | ||
| 255 | while (current) { | ||
| 256 | Visit(current); | ||
| 257 | current = current->GetNext(); | ||
| 258 | } | ||
| 259 | scope--; | ||
| 260 | inner += Ident() + "}\n"; | ||
| 261 | } | ||
| 262 | |||
| 263 | void operator()(const ASTIfElse& ast) { | ||
| 264 | inner += Ident() + "else {\n"; | ||
| 265 | scope++; | ||
| 266 | ASTNode current = ast.nodes.GetFirst(); | ||
| 267 | while (current) { | ||
| 268 | Visit(current); | ||
| 269 | current = current->GetNext(); | ||
| 270 | } | ||
| 271 | scope--; | ||
| 272 | inner += Ident() + "}\n"; | ||
| 273 | } | ||
| 274 | |||
| 275 | void operator()(const ASTBlockEncoded& ast) { | ||
| 276 | inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) + | ||
| 277 | ");\n"; | ||
| 278 | } | ||
| 279 | |||
| 280 | void operator()(const ASTBlockDecoded& ast) { | ||
| 281 | inner += Ident() + "Block;\n"; | ||
| 282 | } | ||
| 283 | |||
| 284 | void operator()(const ASTVarSet& ast) { | ||
| 285 | ExprPrinter expr_parser{}; | ||
| 286 | std::visit(expr_parser, *ast.condition); | ||
| 287 | inner += | ||
| 288 | Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n"; | ||
| 289 | } | ||
| 290 | |||
| 291 | void operator()(const ASTLabel& ast) { | ||
| 292 | inner += "Label_" + std::to_string(ast.index) + ":\n"; | ||
| 293 | } | ||
| 294 | |||
| 295 | void operator()(const ASTGoto& ast) { | ||
| 296 | ExprPrinter expr_parser{}; | ||
| 297 | std::visit(expr_parser, *ast.condition); | ||
| 298 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" + | ||
| 299 | std::to_string(ast.label) + ";\n"; | ||
| 300 | } | ||
| 301 | |||
| 302 | void operator()(const ASTDoWhile& ast) { | ||
| 303 | ExprPrinter expr_parser{}; | ||
| 304 | std::visit(expr_parser, *ast.condition); | ||
| 305 | inner += Ident() + "do {\n"; | ||
| 306 | scope++; | ||
| 307 | ASTNode current = ast.nodes.GetFirst(); | ||
| 308 | while (current) { | ||
| 309 | Visit(current); | ||
| 310 | current = current->GetNext(); | ||
| 311 | } | ||
| 312 | scope--; | ||
| 313 | inner += Ident() + "} while (" + expr_parser.GetResult() + ");\n"; | ||
| 314 | } | ||
| 315 | |||
| 316 | void operator()(const ASTReturn& ast) { | ||
| 317 | ExprPrinter expr_parser{}; | ||
| 318 | std::visit(expr_parser, *ast.condition); | ||
| 319 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> " + | ||
| 320 | (ast.kills ? "discard" : "exit") + ";\n"; | ||
| 321 | } | ||
| 322 | |||
| 323 | void operator()(const ASTBreak& ast) { | ||
| 324 | ExprPrinter expr_parser{}; | ||
| 325 | std::visit(expr_parser, *ast.condition); | ||
| 326 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> break;\n"; | ||
| 327 | } | ||
| 328 | |||
| 329 | std::string& Ident() { | ||
| 330 | if (memo_scope == scope) { | ||
| 331 | return tabs_memo; | ||
| 332 | } | ||
| 333 | tabs_memo = tabs.substr(0, scope * 2); | ||
| 334 | memo_scope = scope; | ||
| 335 | return tabs_memo; | ||
| 336 | } | ||
| 337 | |||
| 338 | void Visit(ASTNode& node) { | ||
| 339 | std::visit(*this, *node->GetInnerData()); | ||
| 340 | } | ||
| 341 | |||
| 342 | const std::string& GetResult() const { | ||
| 343 | return inner; | ||
| 344 | } | ||
| 345 | |||
| 346 | private: | ||
| 347 | std::string inner{}; | ||
| 348 | u32 scope{}; | ||
| 349 | |||
| 350 | std::string tabs_memo{}; | ||
| 351 | u32 memo_scope{}; | ||
| 352 | |||
| 353 | static constexpr std::string_view tabs{" "}; | ||
| 354 | }; | ||
| 355 | |||
| 356 | std::string ASTManager::Print() { | ||
| 357 | ASTPrinter printer{}; | ||
| 358 | printer.Visit(main_node); | ||
| 359 | return printer.GetResult(); | ||
| 360 | } | ||
| 361 | |||
| 362 | ASTManager::ASTManager(bool full_decompile, bool disable_else_derivation) | ||
| 363 | : full_decompile{full_decompile}, disable_else_derivation{disable_else_derivation} {}; | ||
| 364 | |||
| 365 | ASTManager::~ASTManager() { | ||
| 366 | Clear(); | ||
| 367 | } | ||
| 368 | |||
| 369 | void ASTManager::Init() { | ||
| 370 | main_node = ASTBase::Make<ASTProgram>(ASTNode{}); | ||
| 371 | program = std::get_if<ASTProgram>(main_node->GetInnerData()); | ||
| 372 | false_condition = MakeExpr<ExprBoolean>(false); | ||
| 373 | } | ||
| 374 | |||
| 375 | void ASTManager::DeclareLabel(u32 address) { | ||
| 376 | const auto pair = labels_map.emplace(address, labels_count); | ||
| 377 | if (pair.second) { | ||
| 378 | labels_count++; | ||
| 379 | labels.resize(labels_count); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | void ASTManager::InsertLabel(u32 address) { | ||
| 384 | const u32 index = labels_map[address]; | ||
| 385 | const ASTNode label = ASTBase::Make<ASTLabel>(main_node, index); | ||
| 386 | labels[index] = label; | ||
| 387 | program->nodes.PushBack(label); | ||
| 388 | } | ||
| 389 | |||
| 390 | void ASTManager::InsertGoto(Expr condition, u32 address) { | ||
| 391 | const u32 index = labels_map[address]; | ||
| 392 | const ASTNode goto_node = ASTBase::Make<ASTGoto>(main_node, std::move(condition), index); | ||
| 393 | gotos.push_back(goto_node); | ||
| 394 | program->nodes.PushBack(goto_node); | ||
| 395 | } | ||
| 396 | |||
| 397 | void ASTManager::InsertBlock(u32 start_address, u32 end_address) { | ||
| 398 | ASTNode block = ASTBase::Make<ASTBlockEncoded>(main_node, start_address, end_address); | ||
| 399 | program->nodes.PushBack(std::move(block)); | ||
| 400 | } | ||
| 401 | |||
| 402 | void ASTManager::InsertReturn(Expr condition, bool kills) { | ||
| 403 | ASTNode node = ASTBase::Make<ASTReturn>(main_node, std::move(condition), kills); | ||
| 404 | program->nodes.PushBack(std::move(node)); | ||
| 405 | } | ||
| 406 | |||
| 407 | // The decompile algorithm is based on | ||
| 408 | // "Taming control flow: A structured approach to eliminating goto statements" | ||
| 409 | // by AM Erosa, LJ Hendren 1994. In general, the idea is to get gotos to be | ||
| 410 | // on the same structured level as the label which they jump to. This is done, | ||
| 411 | // through outward/inward movements and lifting. Once they are at the same | ||
| 412 | // level, you can enclose them in an "if" structure or a "do-while" structure. | ||
| 413 | void ASTManager::Decompile() { | ||
| 414 | auto it = gotos.begin(); | ||
| 415 | while (it != gotos.end()) { | ||
| 416 | const ASTNode goto_node = *it; | ||
| 417 | const auto label_index = goto_node->GetGotoLabel(); | ||
| 418 | if (!label_index) { | ||
| 419 | return; | ||
| 420 | } | ||
| 421 | const ASTNode label = labels[*label_index]; | ||
| 422 | if (!full_decompile) { | ||
| 423 | // We only decompile backward jumps | ||
| 424 | if (!IsBackwardsJump(goto_node, label)) { | ||
| 425 | it++; | ||
| 426 | continue; | ||
| 427 | } | ||
| 428 | } | ||
| 429 | if (IndirectlyRelated(goto_node, label)) { | ||
| 430 | while (!DirectlyRelated(goto_node, label)) { | ||
| 431 | MoveOutward(goto_node); | ||
| 432 | } | ||
| 433 | } | ||
| 434 | if (DirectlyRelated(goto_node, label)) { | ||
| 435 | u32 goto_level = goto_node->GetLevel(); | ||
| 436 | const u32 label_level = label->GetLevel(); | ||
| 437 | while (label_level < goto_level) { | ||
| 438 | MoveOutward(goto_node); | ||
| 439 | goto_level--; | ||
| 440 | } | ||
| 441 | // TODO(Blinkhawk): Implement Lifting and Inward Movements | ||
| 442 | } | ||
| 443 | if (label->GetParent() == goto_node->GetParent()) { | ||
| 444 | bool is_loop = false; | ||
| 445 | ASTNode current = goto_node->GetPrevious(); | ||
| 446 | while (current) { | ||
| 447 | if (current == label) { | ||
| 448 | is_loop = true; | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | current = current->GetPrevious(); | ||
| 452 | } | ||
| 453 | |||
| 454 | if (is_loop) { | ||
| 455 | EncloseDoWhile(goto_node, label); | ||
| 456 | } else { | ||
| 457 | EncloseIfThen(goto_node, label); | ||
| 458 | } | ||
| 459 | it = gotos.erase(it); | ||
| 460 | continue; | ||
| 461 | } | ||
| 462 | it++; | ||
| 463 | } | ||
| 464 | if (full_decompile) { | ||
| 465 | for (const ASTNode& label : labels) { | ||
| 466 | auto& manager = label->GetManager(); | ||
| 467 | manager.Remove(label); | ||
| 468 | } | ||
| 469 | labels.clear(); | ||
| 470 | } else { | ||
| 471 | auto label_it = labels.begin(); | ||
| 472 | while (label_it != labels.end()) { | ||
| 473 | bool can_remove = true; | ||
| 474 | ASTNode label = *label_it; | ||
| 475 | for (const ASTNode& goto_node : gotos) { | ||
| 476 | const auto label_index = goto_node->GetGotoLabel(); | ||
| 477 | if (!label_index) { | ||
| 478 | return; | ||
| 479 | } | ||
| 480 | ASTNode& glabel = labels[*label_index]; | ||
| 481 | if (glabel == label) { | ||
| 482 | can_remove = false; | ||
| 483 | break; | ||
| 484 | } | ||
| 485 | } | ||
| 486 | if (can_remove) { | ||
| 487 | label->MarkLabelUnused(); | ||
| 488 | } | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const { | ||
| 494 | u32 goto_level = goto_node->GetLevel(); | ||
| 495 | u32 label_level = label_node->GetLevel(); | ||
| 496 | while (goto_level > label_level) { | ||
| 497 | goto_level--; | ||
| 498 | goto_node = goto_node->GetParent(); | ||
| 499 | } | ||
| 500 | while (label_level > goto_level) { | ||
| 501 | label_level--; | ||
| 502 | label_node = label_node->GetParent(); | ||
| 503 | } | ||
| 504 | while (goto_node->GetParent() != label_node->GetParent()) { | ||
| 505 | goto_node = goto_node->GetParent(); | ||
| 506 | label_node = label_node->GetParent(); | ||
| 507 | } | ||
| 508 | ASTNode current = goto_node->GetPrevious(); | ||
| 509 | while (current) { | ||
| 510 | if (current == label_node) { | ||
| 511 | return true; | ||
| 512 | } | ||
| 513 | current = current->GetPrevious(); | ||
| 514 | } | ||
| 515 | return false; | ||
| 516 | } | ||
| 517 | |||
| 518 | bool ASTManager::IndirectlyRelated(const ASTNode& first, const ASTNode& second) const { | ||
| 519 | return !(first->GetParent() == second->GetParent() || DirectlyRelated(first, second)); | ||
| 520 | } | ||
| 521 | |||
| 522 | bool ASTManager::DirectlyRelated(const ASTNode& first, const ASTNode& second) const { | ||
| 523 | if (first->GetParent() == second->GetParent()) { | ||
| 524 | return false; | ||
| 525 | } | ||
| 526 | const u32 first_level = first->GetLevel(); | ||
| 527 | const u32 second_level = second->GetLevel(); | ||
| 528 | u32 min_level; | ||
| 529 | u32 max_level; | ||
| 530 | ASTNode max; | ||
| 531 | ASTNode min; | ||
| 532 | if (first_level > second_level) { | ||
| 533 | min_level = second_level; | ||
| 534 | min = second; | ||
| 535 | max_level = first_level; | ||
| 536 | max = first; | ||
| 537 | } else { | ||
| 538 | min_level = first_level; | ||
| 539 | min = first; | ||
| 540 | max_level = second_level; | ||
| 541 | max = second; | ||
| 542 | } | ||
| 543 | |||
| 544 | while (max_level > min_level) { | ||
| 545 | max_level--; | ||
| 546 | max = max->GetParent(); | ||
| 547 | } | ||
| 548 | |||
| 549 | return min->GetParent() == max->GetParent(); | ||
| 550 | } | ||
| 551 | |||
| 552 | void ASTManager::ShowCurrentState(std::string_view state) { | ||
| 553 | LOG_CRITICAL(HW_GPU, "\nState {}:\n\n{}\n", state, Print()); | ||
| 554 | SanityCheck(); | ||
| 555 | } | ||
| 556 | |||
| 557 | void ASTManager::SanityCheck() { | ||
| 558 | for (auto& label : labels) { | ||
| 559 | if (!label->GetParent()) { | ||
| 560 | LOG_CRITICAL(HW_GPU, "Sanity Check Failed"); | ||
| 561 | } | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { | ||
| 566 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 567 | const ASTNode loop_start = label->GetNext(); | ||
| 568 | if (loop_start == goto_node) { | ||
| 569 | zipper.Remove(goto_node); | ||
| 570 | return; | ||
| 571 | } | ||
| 572 | const ASTNode parent = label->GetParent(); | ||
| 573 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 574 | zipper.DetachSegment(loop_start, goto_node); | ||
| 575 | const ASTNode do_while_node = ASTBase::Make<ASTDoWhile>(parent, condition); | ||
| 576 | ASTZipper* sub_zipper = do_while_node->GetSubNodes(); | ||
| 577 | sub_zipper->Init(loop_start, do_while_node); | ||
| 578 | zipper.InsertAfter(do_while_node, label); | ||
| 579 | sub_zipper->Remove(goto_node); | ||
| 580 | } | ||
| 581 | |||
| 582 | void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { | ||
| 583 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 584 | const ASTNode if_end = label->GetPrevious(); | ||
| 585 | if (if_end == goto_node) { | ||
| 586 | zipper.Remove(goto_node); | ||
| 587 | return; | ||
| 588 | } | ||
| 589 | const ASTNode prev = goto_node->GetPrevious(); | ||
| 590 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 591 | bool do_else = false; | ||
| 592 | if (!disable_else_derivation && prev->IsIfThen()) { | ||
| 593 | const Expr if_condition = prev->GetIfCondition(); | ||
| 594 | do_else = ExprAreEqual(if_condition, condition); | ||
| 595 | } | ||
| 596 | const ASTNode parent = label->GetParent(); | ||
| 597 | zipper.DetachSegment(goto_node, if_end); | ||
| 598 | ASTNode if_node; | ||
| 599 | if (do_else) { | ||
| 600 | if_node = ASTBase::Make<ASTIfElse>(parent); | ||
| 601 | } else { | ||
| 602 | Expr neg_condition = MakeExprNot(condition); | ||
| 603 | if_node = ASTBase::Make<ASTIfThen>(parent, neg_condition); | ||
| 604 | } | ||
| 605 | ASTZipper* sub_zipper = if_node->GetSubNodes(); | ||
| 606 | sub_zipper->Init(goto_node, if_node); | ||
| 607 | zipper.InsertAfter(if_node, prev); | ||
| 608 | sub_zipper->Remove(goto_node); | ||
| 609 | } | ||
| 610 | |||
| 611 | void ASTManager::MoveOutward(ASTNode goto_node) { | ||
| 612 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 613 | const ASTNode parent = goto_node->GetParent(); | ||
| 614 | ASTZipper& zipper2 = parent->GetManager(); | ||
| 615 | const ASTNode grandpa = parent->GetParent(); | ||
| 616 | const bool is_loop = parent->IsLoop(); | ||
| 617 | const bool is_else = parent->IsIfElse(); | ||
| 618 | const bool is_if = parent->IsIfThen(); | ||
| 619 | |||
| 620 | const ASTNode prev = goto_node->GetPrevious(); | ||
| 621 | const ASTNode post = goto_node->GetNext(); | ||
| 622 | |||
| 623 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 624 | zipper.DetachSingle(goto_node); | ||
| 625 | if (is_loop) { | ||
| 626 | const u32 var_index = NewVariable(); | ||
| 627 | const Expr var_condition = MakeExpr<ExprVar>(var_index); | ||
| 628 | const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition); | ||
| 629 | const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition); | ||
| 630 | zipper2.InsertBefore(var_node_init, parent); | ||
| 631 | zipper.InsertAfter(var_node, prev); | ||
| 632 | goto_node->SetGotoCondition(var_condition); | ||
| 633 | const ASTNode break_node = ASTBase::Make<ASTBreak>(parent, var_condition); | ||
| 634 | zipper.InsertAfter(break_node, var_node); | ||
| 635 | } else if (is_if || is_else) { | ||
| 636 | const u32 var_index = NewVariable(); | ||
| 637 | const Expr var_condition = MakeExpr<ExprVar>(var_index); | ||
| 638 | const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition); | ||
| 639 | const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition); | ||
| 640 | if (is_if) { | ||
| 641 | zipper2.InsertBefore(var_node_init, parent); | ||
| 642 | } else { | ||
| 643 | zipper2.InsertBefore(var_node_init, parent->GetPrevious()); | ||
| 644 | } | ||
| 645 | zipper.InsertAfter(var_node, prev); | ||
| 646 | goto_node->SetGotoCondition(var_condition); | ||
| 647 | if (post) { | ||
| 648 | zipper.DetachTail(post); | ||
| 649 | const ASTNode if_node = ASTBase::Make<ASTIfThen>(parent, MakeExprNot(var_condition)); | ||
| 650 | ASTZipper* sub_zipper = if_node->GetSubNodes(); | ||
| 651 | sub_zipper->Init(post, if_node); | ||
| 652 | zipper.InsertAfter(if_node, var_node); | ||
| 653 | } | ||
| 654 | } else { | ||
| 655 | UNREACHABLE(); | ||
| 656 | } | ||
| 657 | const ASTNode next = parent->GetNext(); | ||
| 658 | if (is_if && next && next->IsIfElse()) { | ||
| 659 | zipper2.InsertAfter(goto_node, next); | ||
| 660 | goto_node->SetParent(grandpa); | ||
| 661 | return; | ||
| 662 | } | ||
| 663 | zipper2.InsertAfter(goto_node, parent); | ||
| 664 | goto_node->SetParent(grandpa); | ||
| 665 | } | ||
| 666 | |||
| 667 | class ASTClearer { | ||
| 668 | public: | ||
| 669 | ASTClearer() = default; | ||
| 670 | |||
| 671 | void operator()(const ASTProgram& ast) { | ||
| 672 | ASTNode current = ast.nodes.GetFirst(); | ||
| 673 | while (current) { | ||
| 674 | Visit(current); | ||
| 675 | current = current->GetNext(); | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | void operator()(const ASTIfThen& ast) { | ||
| 680 | ASTNode current = ast.nodes.GetFirst(); | ||
| 681 | while (current) { | ||
| 682 | Visit(current); | ||
| 683 | current = current->GetNext(); | ||
| 684 | } | ||
| 685 | } | ||
| 686 | |||
| 687 | void operator()(const ASTIfElse& ast) { | ||
| 688 | ASTNode current = ast.nodes.GetFirst(); | ||
| 689 | while (current) { | ||
| 690 | Visit(current); | ||
| 691 | current = current->GetNext(); | ||
| 692 | } | ||
| 693 | } | ||
| 694 | |||
| 695 | void operator()([[maybe_unused]] const ASTBlockEncoded& ast) {} | ||
| 696 | |||
| 697 | void operator()(ASTBlockDecoded& ast) { | ||
| 698 | ast.nodes.clear(); | ||
| 699 | } | ||
| 700 | |||
| 701 | void operator()([[maybe_unused]] const ASTVarSet& ast) {} | ||
| 702 | |||
| 703 | void operator()([[maybe_unused]] const ASTLabel& ast) {} | ||
| 704 | |||
| 705 | void operator()([[maybe_unused]] const ASTGoto& ast) {} | ||
| 706 | |||
| 707 | void operator()(const ASTDoWhile& ast) { | ||
| 708 | ASTNode current = ast.nodes.GetFirst(); | ||
| 709 | while (current) { | ||
| 710 | Visit(current); | ||
| 711 | current = current->GetNext(); | ||
| 712 | } | ||
| 713 | } | ||
| 714 | |||
| 715 | void operator()([[maybe_unused]] const ASTReturn& ast) {} | ||
| 716 | |||
| 717 | void operator()([[maybe_unused]] const ASTBreak& ast) {} | ||
| 718 | |||
| 719 | void Visit(const ASTNode& node) { | ||
| 720 | std::visit(*this, *node->GetInnerData()); | ||
| 721 | node->Clear(); | ||
| 722 | } | ||
| 723 | }; | ||
| 724 | |||
| 725 | void ASTManager::Clear() { | ||
| 726 | if (!main_node) { | ||
| 727 | return; | ||
| 728 | } | ||
| 729 | ASTClearer clearer{}; | ||
| 730 | clearer.Visit(main_node); | ||
| 731 | main_node.reset(); | ||
| 732 | program = nullptr; | ||
| 733 | labels_map.clear(); | ||
| 734 | labels.clear(); | ||
| 735 | gotos.clear(); | ||
| 736 | } | ||
| 737 | |||
| 738 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h new file mode 100644 index 000000000..d7bf11821 --- /dev/null +++ b/src/video_core/shader/ast.h | |||
| @@ -0,0 +1,400 @@ | |||
| 1 | // Copyright 2019 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 <list> | ||
| 9 | #include <memory> | ||
| 10 | #include <optional> | ||
| 11 | #include <string> | ||
| 12 | #include <unordered_map> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include "video_core/shader/expr.h" | ||
| 16 | #include "video_core/shader/node.h" | ||
| 17 | |||
| 18 | namespace VideoCommon::Shader { | ||
| 19 | |||
| 20 | class ASTBase; | ||
| 21 | class ASTBlockDecoded; | ||
| 22 | class ASTBlockEncoded; | ||
| 23 | class ASTBreak; | ||
| 24 | class ASTDoWhile; | ||
| 25 | class ASTGoto; | ||
| 26 | class ASTIfElse; | ||
| 27 | class ASTIfThen; | ||
| 28 | class ASTLabel; | ||
| 29 | class ASTProgram; | ||
| 30 | class ASTReturn; | ||
| 31 | class ASTVarSet; | ||
| 32 | |||
| 33 | using ASTData = std::variant<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTBlockDecoded, | ||
| 34 | ASTVarSet, ASTGoto, ASTLabel, ASTDoWhile, ASTReturn, ASTBreak>; | ||
| 35 | |||
| 36 | using ASTNode = std::shared_ptr<ASTBase>; | ||
| 37 | |||
| 38 | enum class ASTZipperType : u32 { | ||
| 39 | Program, | ||
| 40 | IfThen, | ||
| 41 | IfElse, | ||
| 42 | Loop, | ||
| 43 | }; | ||
| 44 | |||
| 45 | class ASTZipper final { | ||
| 46 | public: | ||
| 47 | explicit ASTZipper(); | ||
| 48 | |||
| 49 | void Init(ASTNode first, ASTNode parent); | ||
| 50 | |||
| 51 | ASTNode GetFirst() const { | ||
| 52 | return first; | ||
| 53 | } | ||
| 54 | |||
| 55 | ASTNode GetLast() const { | ||
| 56 | return last; | ||
| 57 | } | ||
| 58 | |||
| 59 | void PushBack(ASTNode new_node); | ||
| 60 | void PushFront(ASTNode new_node); | ||
| 61 | void InsertAfter(ASTNode new_node, ASTNode at_node); | ||
| 62 | void InsertBefore(ASTNode new_node, ASTNode at_node); | ||
| 63 | void DetachTail(ASTNode node); | ||
| 64 | void DetachSingle(ASTNode node); | ||
| 65 | void DetachSegment(ASTNode start, ASTNode end); | ||
| 66 | void Remove(ASTNode node); | ||
| 67 | |||
| 68 | ASTNode first{}; | ||
| 69 | ASTNode last{}; | ||
| 70 | }; | ||
| 71 | |||
| 72 | class ASTProgram { | ||
| 73 | public: | ||
| 74 | ASTZipper nodes{}; | ||
| 75 | }; | ||
| 76 | |||
| 77 | class ASTIfThen { | ||
| 78 | public: | ||
| 79 | explicit ASTIfThen(Expr condition) : condition{std::move(condition)} {} | ||
| 80 | Expr condition; | ||
| 81 | ASTZipper nodes{}; | ||
| 82 | }; | ||
| 83 | |||
| 84 | class ASTIfElse { | ||
| 85 | public: | ||
| 86 | ASTZipper nodes{}; | ||
| 87 | }; | ||
| 88 | |||
| 89 | class ASTBlockEncoded { | ||
| 90 | public: | ||
| 91 | explicit ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {} | ||
| 92 | u32 start; | ||
| 93 | u32 end; | ||
| 94 | }; | ||
| 95 | |||
| 96 | class ASTBlockDecoded { | ||
| 97 | public: | ||
| 98 | explicit ASTBlockDecoded(NodeBlock&& new_nodes) : nodes(std::move(new_nodes)) {} | ||
| 99 | NodeBlock nodes; | ||
| 100 | }; | ||
| 101 | |||
| 102 | class ASTVarSet { | ||
| 103 | public: | ||
| 104 | explicit ASTVarSet(u32 index, Expr condition) : index{index}, condition{std::move(condition)} {} | ||
| 105 | u32 index; | ||
| 106 | Expr condition; | ||
| 107 | }; | ||
| 108 | |||
| 109 | class ASTLabel { | ||
| 110 | public: | ||
| 111 | explicit ASTLabel(u32 index) : index{index} {} | ||
| 112 | u32 index; | ||
| 113 | bool unused{}; | ||
| 114 | }; | ||
| 115 | |||
| 116 | class ASTGoto { | ||
| 117 | public: | ||
| 118 | explicit ASTGoto(Expr condition, u32 label) : condition{std::move(condition)}, label{label} {} | ||
| 119 | Expr condition; | ||
| 120 | u32 label; | ||
| 121 | }; | ||
| 122 | |||
| 123 | class ASTDoWhile { | ||
| 124 | public: | ||
| 125 | explicit ASTDoWhile(Expr condition) : condition{std::move(condition)} {} | ||
| 126 | Expr condition; | ||
| 127 | ASTZipper nodes{}; | ||
| 128 | }; | ||
| 129 | |||
| 130 | class ASTReturn { | ||
| 131 | public: | ||
| 132 | explicit ASTReturn(Expr condition, bool kills) | ||
| 133 | : condition{std::move(condition)}, kills{kills} {} | ||
| 134 | Expr condition; | ||
| 135 | bool kills; | ||
| 136 | }; | ||
| 137 | |||
| 138 | class ASTBreak { | ||
| 139 | public: | ||
| 140 | explicit ASTBreak(Expr condition) : condition{std::move(condition)} {} | ||
| 141 | Expr condition; | ||
| 142 | }; | ||
| 143 | |||
| 144 | class ASTBase { | ||
| 145 | public: | ||
| 146 | explicit ASTBase(ASTNode parent, ASTData data) | ||
| 147 | : data{std::move(data)}, parent{std::move(parent)} {} | ||
| 148 | |||
| 149 | template <class U, class... Args> | ||
| 150 | static ASTNode Make(ASTNode parent, Args&&... args) { | ||
| 151 | return std::make_shared<ASTBase>(std::move(parent), | ||
| 152 | ASTData(U(std::forward<Args>(args)...))); | ||
| 153 | } | ||
| 154 | |||
| 155 | void SetParent(ASTNode new_parent) { | ||
| 156 | parent = std::move(new_parent); | ||
| 157 | } | ||
| 158 | |||
| 159 | ASTNode& GetParent() { | ||
| 160 | return parent; | ||
| 161 | } | ||
| 162 | |||
| 163 | const ASTNode& GetParent() const { | ||
| 164 | return parent; | ||
| 165 | } | ||
| 166 | |||
| 167 | u32 GetLevel() const { | ||
| 168 | u32 level = 0; | ||
| 169 | auto next_parent = parent; | ||
| 170 | while (next_parent) { | ||
| 171 | next_parent = next_parent->GetParent(); | ||
| 172 | level++; | ||
| 173 | } | ||
| 174 | return level; | ||
| 175 | } | ||
| 176 | |||
| 177 | ASTData* GetInnerData() { | ||
| 178 | return &data; | ||
| 179 | } | ||
| 180 | |||
| 181 | const ASTData* GetInnerData() const { | ||
| 182 | return &data; | ||
| 183 | } | ||
| 184 | |||
| 185 | ASTNode GetNext() const { | ||
| 186 | return next; | ||
| 187 | } | ||
| 188 | |||
| 189 | ASTNode GetPrevious() const { | ||
| 190 | return previous; | ||
| 191 | } | ||
| 192 | |||
| 193 | ASTZipper& GetManager() { | ||
| 194 | return *manager; | ||
| 195 | } | ||
| 196 | |||
| 197 | const ASTZipper& GetManager() const { | ||
| 198 | return *manager; | ||
| 199 | } | ||
| 200 | |||
| 201 | std::optional<u32> GetGotoLabel() const { | ||
| 202 | auto inner = std::get_if<ASTGoto>(&data); | ||
| 203 | if (inner) { | ||
| 204 | return {inner->label}; | ||
| 205 | } | ||
| 206 | return {}; | ||
| 207 | } | ||
| 208 | |||
| 209 | Expr GetGotoCondition() const { | ||
| 210 | auto inner = std::get_if<ASTGoto>(&data); | ||
| 211 | if (inner) { | ||
| 212 | return inner->condition; | ||
| 213 | } | ||
| 214 | return nullptr; | ||
| 215 | } | ||
| 216 | |||
| 217 | void MarkLabelUnused() { | ||
| 218 | auto inner = std::get_if<ASTLabel>(&data); | ||
| 219 | if (inner) { | ||
| 220 | inner->unused = true; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | bool IsLabelUnused() const { | ||
| 225 | auto inner = std::get_if<ASTLabel>(&data); | ||
| 226 | if (inner) { | ||
| 227 | return inner->unused; | ||
| 228 | } | ||
| 229 | return true; | ||
| 230 | } | ||
| 231 | |||
| 232 | std::optional<u32> GetLabelIndex() const { | ||
| 233 | auto inner = std::get_if<ASTLabel>(&data); | ||
| 234 | if (inner) { | ||
| 235 | return {inner->index}; | ||
| 236 | } | ||
| 237 | return {}; | ||
| 238 | } | ||
| 239 | |||
| 240 | Expr GetIfCondition() const { | ||
| 241 | auto inner = std::get_if<ASTIfThen>(&data); | ||
| 242 | if (inner) { | ||
| 243 | return inner->condition; | ||
| 244 | } | ||
| 245 | return nullptr; | ||
| 246 | } | ||
| 247 | |||
| 248 | void SetGotoCondition(Expr new_condition) { | ||
| 249 | auto inner = std::get_if<ASTGoto>(&data); | ||
| 250 | if (inner) { | ||
| 251 | inner->condition = std::move(new_condition); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | bool IsIfThen() const { | ||
| 256 | return std::holds_alternative<ASTIfThen>(data); | ||
| 257 | } | ||
| 258 | |||
| 259 | bool IsIfElse() const { | ||
| 260 | return std::holds_alternative<ASTIfElse>(data); | ||
| 261 | } | ||
| 262 | |||
| 263 | bool IsBlockEncoded() const { | ||
| 264 | return std::holds_alternative<ASTBlockEncoded>(data); | ||
| 265 | } | ||
| 266 | |||
| 267 | void TransformBlockEncoded(NodeBlock&& nodes) { | ||
| 268 | data = ASTBlockDecoded(std::move(nodes)); | ||
| 269 | } | ||
| 270 | |||
| 271 | bool IsLoop() const { | ||
| 272 | return std::holds_alternative<ASTDoWhile>(data); | ||
| 273 | } | ||
| 274 | |||
| 275 | ASTZipper* GetSubNodes() { | ||
| 276 | if (std::holds_alternative<ASTProgram>(data)) { | ||
| 277 | return &std::get_if<ASTProgram>(&data)->nodes; | ||
| 278 | } | ||
| 279 | if (std::holds_alternative<ASTIfThen>(data)) { | ||
| 280 | return &std::get_if<ASTIfThen>(&data)->nodes; | ||
| 281 | } | ||
| 282 | if (std::holds_alternative<ASTIfElse>(data)) { | ||
| 283 | return &std::get_if<ASTIfElse>(&data)->nodes; | ||
| 284 | } | ||
| 285 | if (std::holds_alternative<ASTDoWhile>(data)) { | ||
| 286 | return &std::get_if<ASTDoWhile>(&data)->nodes; | ||
| 287 | } | ||
| 288 | return nullptr; | ||
| 289 | } | ||
| 290 | |||
| 291 | void Clear() { | ||
| 292 | next.reset(); | ||
| 293 | previous.reset(); | ||
| 294 | parent.reset(); | ||
| 295 | manager = nullptr; | ||
| 296 | } | ||
| 297 | |||
| 298 | private: | ||
| 299 | friend class ASTZipper; | ||
| 300 | |||
| 301 | ASTData data; | ||
| 302 | ASTNode parent{}; | ||
| 303 | ASTNode next{}; | ||
| 304 | ASTNode previous{}; | ||
| 305 | ASTZipper* manager{}; | ||
| 306 | }; | ||
| 307 | |||
| 308 | class ASTManager final { | ||
| 309 | public: | ||
| 310 | ASTManager(bool full_decompile, bool disable_else_derivation); | ||
| 311 | ~ASTManager(); | ||
| 312 | |||
| 313 | ASTManager(const ASTManager& o) = delete; | ||
| 314 | ASTManager& operator=(const ASTManager& other) = delete; | ||
| 315 | |||
| 316 | ASTManager(ASTManager&& other) noexcept = default; | ||
| 317 | ASTManager& operator=(ASTManager&& other) noexcept = default; | ||
| 318 | |||
| 319 | void Init(); | ||
| 320 | |||
| 321 | void DeclareLabel(u32 address); | ||
| 322 | |||
| 323 | void InsertLabel(u32 address); | ||
| 324 | |||
| 325 | void InsertGoto(Expr condition, u32 address); | ||
| 326 | |||
| 327 | void InsertBlock(u32 start_address, u32 end_address); | ||
| 328 | |||
| 329 | void InsertReturn(Expr condition, bool kills); | ||
| 330 | |||
| 331 | std::string Print(); | ||
| 332 | |||
| 333 | void Decompile(); | ||
| 334 | |||
| 335 | void ShowCurrentState(std::string_view state); | ||
| 336 | |||
| 337 | void SanityCheck(); | ||
| 338 | |||
| 339 | void Clear(); | ||
| 340 | |||
| 341 | bool IsFullyDecompiled() const { | ||
| 342 | if (full_decompile) { | ||
| 343 | return gotos.empty(); | ||
| 344 | } | ||
| 345 | |||
| 346 | for (ASTNode goto_node : gotos) { | ||
| 347 | auto label_index = goto_node->GetGotoLabel(); | ||
| 348 | if (!label_index) { | ||
| 349 | return false; | ||
| 350 | } | ||
| 351 | ASTNode glabel = labels[*label_index]; | ||
| 352 | if (IsBackwardsJump(goto_node, glabel)) { | ||
| 353 | return false; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | return true; | ||
| 357 | } | ||
| 358 | |||
| 359 | ASTNode GetProgram() const { | ||
| 360 | return main_node; | ||
| 361 | } | ||
| 362 | |||
| 363 | u32 GetVariables() const { | ||
| 364 | return variables; | ||
| 365 | } | ||
| 366 | |||
| 367 | const std::vector<ASTNode>& GetLabels() const { | ||
| 368 | return labels; | ||
| 369 | } | ||
| 370 | |||
| 371 | private: | ||
| 372 | bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const; | ||
| 373 | |||
| 374 | bool IndirectlyRelated(const ASTNode& first, const ASTNode& second) const; | ||
| 375 | |||
| 376 | bool DirectlyRelated(const ASTNode& first, const ASTNode& second) const; | ||
| 377 | |||
| 378 | void EncloseDoWhile(ASTNode goto_node, ASTNode label); | ||
| 379 | |||
| 380 | void EncloseIfThen(ASTNode goto_node, ASTNode label); | ||
| 381 | |||
| 382 | void MoveOutward(ASTNode goto_node); | ||
| 383 | |||
| 384 | u32 NewVariable() { | ||
| 385 | return variables++; | ||
| 386 | } | ||
| 387 | |||
| 388 | bool full_decompile{}; | ||
| 389 | bool disable_else_derivation{}; | ||
| 390 | std::unordered_map<u32, u32> labels_map{}; | ||
| 391 | u32 labels_count{}; | ||
| 392 | std::vector<ASTNode> labels{}; | ||
| 393 | std::list<ASTNode> gotos{}; | ||
| 394 | u32 variables{}; | ||
| 395 | ASTProgram* program{}; | ||
| 396 | ASTNode main_node{}; | ||
| 397 | Expr false_condition{}; | ||
| 398 | }; | ||
| 399 | |||
| 400 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/compiler_settings.cpp b/src/video_core/shader/compiler_settings.cpp new file mode 100644 index 000000000..cddcbd4f0 --- /dev/null +++ b/src/video_core/shader/compiler_settings.cpp | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/shader/compiler_settings.h" | ||
| 6 | |||
| 7 | namespace VideoCommon::Shader { | ||
| 8 | |||
| 9 | std::string CompileDepthAsString(const CompileDepth cd) { | ||
| 10 | switch (cd) { | ||
| 11 | case CompileDepth::BruteForce: | ||
| 12 | return "Brute Force Compile"; | ||
| 13 | case CompileDepth::FlowStack: | ||
| 14 | return "Simple Flow Stack Mode"; | ||
| 15 | case CompileDepth::NoFlowStack: | ||
| 16 | return "Remove Flow Stack"; | ||
| 17 | case CompileDepth::DecompileBackwards: | ||
| 18 | return "Decompile Backward Jumps"; | ||
| 19 | case CompileDepth::FullDecompile: | ||
| 20 | return "Full Decompilation"; | ||
| 21 | default: | ||
| 22 | return "Unknown Compiler Process"; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/compiler_settings.h b/src/video_core/shader/compiler_settings.h new file mode 100644 index 000000000..916018c01 --- /dev/null +++ b/src/video_core/shader/compiler_settings.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2019 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 "video_core/engines/shader_bytecode.h" | ||
| 8 | |||
| 9 | namespace VideoCommon::Shader { | ||
| 10 | |||
| 11 | enum class CompileDepth : u32 { | ||
| 12 | BruteForce = 0, | ||
| 13 | FlowStack = 1, | ||
| 14 | NoFlowStack = 2, | ||
| 15 | DecompileBackwards = 3, | ||
| 16 | FullDecompile = 4, | ||
| 17 | }; | ||
| 18 | |||
| 19 | std::string CompileDepthAsString(CompileDepth cd); | ||
| 20 | |||
| 21 | struct CompilerSettings { | ||
| 22 | CompileDepth depth{CompileDepth::NoFlowStack}; | ||
| 23 | bool disable_else_derivation{true}; | ||
| 24 | }; | ||
| 25 | |||
| 26 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index ec3a76690..268d1aed0 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp | |||
| @@ -4,13 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #include <list> | 5 | #include <list> |
| 6 | #include <map> | 6 | #include <map> |
| 7 | #include <set> | ||
| 7 | #include <stack> | 8 | #include <stack> |
| 8 | #include <unordered_map> | 9 | #include <unordered_map> |
| 9 | #include <unordered_set> | ||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | 11 | ||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "video_core/shader/ast.h" | ||
| 14 | #include "video_core/shader/control_flow.h" | 15 | #include "video_core/shader/control_flow.h" |
| 15 | #include "video_core/shader/shader_ir.h" | 16 | #include "video_core/shader/shader_ir.h" |
| 16 | 17 | ||
| @@ -64,12 +65,13 @@ struct CFGRebuildState { | |||
| 64 | std::list<u32> inspect_queries{}; | 65 | std::list<u32> inspect_queries{}; |
| 65 | std::list<Query> queries{}; | 66 | std::list<Query> queries{}; |
| 66 | std::unordered_map<u32, u32> registered{}; | 67 | std::unordered_map<u32, u32> registered{}; |
| 67 | std::unordered_set<u32> labels{}; | 68 | std::set<u32> labels{}; |
| 68 | std::map<u32, u32> ssy_labels{}; | 69 | std::map<u32, u32> ssy_labels{}; |
| 69 | std::map<u32, u32> pbk_labels{}; | 70 | std::map<u32, u32> pbk_labels{}; |
| 70 | std::unordered_map<u32, BlockStack> stacks{}; | 71 | std::unordered_map<u32, BlockStack> stacks{}; |
| 71 | const ProgramCode& program_code; | 72 | const ProgramCode& program_code; |
| 72 | const std::size_t program_size; | 73 | const std::size_t program_size; |
| 74 | ASTManager* manager; | ||
| 73 | }; | 75 | }; |
| 74 | 76 | ||
| 75 | enum class BlockCollision : u32 { None, Found, Inside }; | 77 | enum class BlockCollision : u32 { None, Found, Inside }; |
| @@ -415,38 +417,133 @@ bool TryQuery(CFGRebuildState& state) { | |||
| 415 | } | 417 | } |
| 416 | } // Anonymous namespace | 418 | } // Anonymous namespace |
| 417 | 419 | ||
| 418 | std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, | 420 | void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { |
| 419 | std::size_t program_size, u32 start_address) { | 421 | const auto get_expr = ([&](const Condition& cond) -> Expr { |
| 420 | CFGRebuildState state{program_code, program_size, start_address}; | 422 | Expr result{}; |
| 423 | if (cond.cc != ConditionCode::T) { | ||
| 424 | result = MakeExpr<ExprCondCode>(cond.cc); | ||
| 425 | } | ||
| 426 | if (cond.predicate != Pred::UnusedIndex) { | ||
| 427 | u32 pred = static_cast<u32>(cond.predicate); | ||
| 428 | bool negate = false; | ||
| 429 | if (pred > 7) { | ||
| 430 | negate = true; | ||
| 431 | pred -= 8; | ||
| 432 | } | ||
| 433 | Expr extra = MakeExpr<ExprPredicate>(pred); | ||
| 434 | if (negate) { | ||
| 435 | extra = MakeExpr<ExprNot>(extra); | ||
| 436 | } | ||
| 437 | if (result) { | ||
| 438 | return MakeExpr<ExprAnd>(extra, result); | ||
| 439 | } | ||
| 440 | return extra; | ||
| 441 | } | ||
| 442 | if (result) { | ||
| 443 | return result; | ||
| 444 | } | ||
| 445 | return MakeExpr<ExprBoolean>(true); | ||
| 446 | }); | ||
| 447 | if (branch.address < 0) { | ||
| 448 | if (branch.kill) { | ||
| 449 | mm.InsertReturn(get_expr(branch.condition), true); | ||
| 450 | return; | ||
| 451 | } | ||
| 452 | mm.InsertReturn(get_expr(branch.condition), false); | ||
| 453 | return; | ||
| 454 | } | ||
| 455 | mm.InsertGoto(get_expr(branch.condition), branch.address); | ||
| 456 | } | ||
| 457 | |||
| 458 | void DecompileShader(CFGRebuildState& state) { | ||
| 459 | state.manager->Init(); | ||
| 460 | for (auto label : state.labels) { | ||
| 461 | state.manager->DeclareLabel(label); | ||
| 462 | } | ||
| 463 | for (auto& block : state.block_info) { | ||
| 464 | if (state.labels.count(block.start) != 0) { | ||
| 465 | state.manager->InsertLabel(block.start); | ||
| 466 | } | ||
| 467 | u32 end = block.branch.ignore ? block.end + 1 : block.end; | ||
| 468 | state.manager->InsertBlock(block.start, end); | ||
| 469 | if (!block.branch.ignore) { | ||
| 470 | InsertBranch(*state.manager, block.branch); | ||
| 471 | } | ||
| 472 | } | ||
| 473 | state.manager->Decompile(); | ||
| 474 | } | ||
| 475 | |||
| 476 | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, | ||
| 477 | u32 start_address, | ||
| 478 | const CompilerSettings& settings) { | ||
| 479 | auto result_out = std::make_unique<ShaderCharacteristics>(); | ||
| 480 | if (settings.depth == CompileDepth::BruteForce) { | ||
| 481 | result_out->settings.depth = CompileDepth::BruteForce; | ||
| 482 | return result_out; | ||
| 483 | } | ||
| 421 | 484 | ||
| 485 | CFGRebuildState state{program_code, program_size, start_address}; | ||
| 422 | // Inspect Code and generate blocks | 486 | // Inspect Code and generate blocks |
| 423 | state.labels.clear(); | 487 | state.labels.clear(); |
| 424 | state.labels.emplace(start_address); | 488 | state.labels.emplace(start_address); |
| 425 | state.inspect_queries.push_back(state.start); | 489 | state.inspect_queries.push_back(state.start); |
| 426 | while (!state.inspect_queries.empty()) { | 490 | while (!state.inspect_queries.empty()) { |
| 427 | if (!TryInspectAddress(state)) { | 491 | if (!TryInspectAddress(state)) { |
| 428 | return {}; | 492 | result_out->settings.depth = CompileDepth::BruteForce; |
| 493 | return result_out; | ||
| 429 | } | 494 | } |
| 430 | } | 495 | } |
| 431 | 496 | ||
| 432 | // Decompile Stacks | 497 | bool use_flow_stack = true; |
| 433 | state.queries.push_back(Query{state.start, {}, {}}); | 498 | |
| 434 | bool decompiled = true; | 499 | bool decompiled = false; |
| 435 | while (!state.queries.empty()) { | 500 | |
| 436 | if (!TryQuery(state)) { | 501 | if (settings.depth != CompileDepth::FlowStack) { |
| 437 | decompiled = false; | 502 | // Decompile Stacks |
| 438 | break; | 503 | state.queries.push_back(Query{state.start, {}, {}}); |
| 504 | decompiled = true; | ||
| 505 | while (!state.queries.empty()) { | ||
| 506 | if (!TryQuery(state)) { | ||
| 507 | decompiled = false; | ||
| 508 | break; | ||
| 509 | } | ||
| 439 | } | 510 | } |
| 440 | } | 511 | } |
| 441 | 512 | ||
| 513 | use_flow_stack = !decompiled; | ||
| 514 | |||
| 442 | // Sort and organize results | 515 | // Sort and organize results |
| 443 | std::sort(state.block_info.begin(), state.block_info.end(), | 516 | std::sort(state.block_info.begin(), state.block_info.end(), |
| 444 | [](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; }); | 517 | [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); |
| 445 | ShaderCharacteristics result_out{}; | 518 | if (decompiled && settings.depth != CompileDepth::NoFlowStack) { |
| 446 | result_out.decompilable = decompiled; | 519 | ASTManager manager{settings.depth != CompileDepth::DecompileBackwards, |
| 447 | result_out.start = start_address; | 520 | settings.disable_else_derivation}; |
| 448 | result_out.end = start_address; | 521 | state.manager = &manager; |
| 449 | for (const auto& block : state.block_info) { | 522 | DecompileShader(state); |
| 523 | decompiled = state.manager->IsFullyDecompiled(); | ||
| 524 | if (!decompiled) { | ||
| 525 | if (settings.depth == CompileDepth::FullDecompile) { | ||
| 526 | LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:"); | ||
| 527 | } else { | ||
| 528 | LOG_CRITICAL(HW_GPU, "Failed to remove all backward gotos!:"); | ||
| 529 | } | ||
| 530 | state.manager->ShowCurrentState("Of Shader"); | ||
| 531 | state.manager->Clear(); | ||
| 532 | } else { | ||
| 533 | auto characteristics = std::make_unique<ShaderCharacteristics>(); | ||
| 534 | characteristics->start = start_address; | ||
| 535 | characteristics->settings.depth = settings.depth; | ||
| 536 | characteristics->manager = std::move(manager); | ||
| 537 | characteristics->end = state.block_info.back().end + 1; | ||
| 538 | return characteristics; | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | result_out->start = start_address; | ||
| 543 | result_out->settings.depth = | ||
| 544 | use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack; | ||
| 545 | result_out->blocks.clear(); | ||
| 546 | for (auto& block : state.block_info) { | ||
| 450 | ShaderBlock new_block{}; | 547 | ShaderBlock new_block{}; |
| 451 | new_block.start = block.start; | 548 | new_block.start = block.start; |
| 452 | new_block.end = block.end; | 549 | new_block.end = block.end; |
| @@ -456,26 +553,26 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, | |||
| 456 | new_block.branch.kills = block.branch.kill; | 553 | new_block.branch.kills = block.branch.kill; |
| 457 | new_block.branch.address = block.branch.address; | 554 | new_block.branch.address = block.branch.address; |
| 458 | } | 555 | } |
| 459 | result_out.end = std::max(result_out.end, block.end); | 556 | result_out->end = std::max(result_out->end, block.end); |
| 460 | result_out.blocks.push_back(new_block); | 557 | result_out->blocks.push_back(new_block); |
| 461 | } | 558 | } |
| 462 | if (result_out.decompilable) { | 559 | if (!use_flow_stack) { |
| 463 | result_out.labels = std::move(state.labels); | 560 | result_out->labels = std::move(state.labels); |
| 464 | return {std::move(result_out)}; | 561 | return result_out; |
| 465 | } | 562 | } |
| 466 | 563 | ||
| 467 | // If it's not decompilable, merge the unlabelled blocks together | 564 | auto back = result_out->blocks.begin(); |
| 468 | auto back = result_out.blocks.begin(); | ||
| 469 | auto next = std::next(back); | 565 | auto next = std::next(back); |
| 470 | while (next != result_out.blocks.end()) { | 566 | while (next != result_out->blocks.end()) { |
| 471 | if (state.labels.count(next->start) == 0 && next->start == back->end + 1) { | 567 | if (state.labels.count(next->start) == 0 && next->start == back->end + 1) { |
| 472 | back->end = next->end; | 568 | back->end = next->end; |
| 473 | next = result_out.blocks.erase(next); | 569 | next = result_out->blocks.erase(next); |
| 474 | continue; | 570 | continue; |
| 475 | } | 571 | } |
| 476 | back = next; | 572 | back = next; |
| 477 | ++next; | 573 | ++next; |
| 478 | } | 574 | } |
| 479 | return {std::move(result_out)}; | 575 | |
| 576 | return result_out; | ||
| 480 | } | 577 | } |
| 481 | } // namespace VideoCommon::Shader | 578 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index b0a5e4f8c..74e54a5c7 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h | |||
| @@ -6,9 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | #include <list> | 7 | #include <list> |
| 8 | #include <optional> | 8 | #include <optional> |
| 9 | #include <unordered_set> | 9 | #include <set> |
| 10 | 10 | ||
| 11 | #include "video_core/engines/shader_bytecode.h" | 11 | #include "video_core/engines/shader_bytecode.h" |
| 12 | #include "video_core/shader/ast.h" | ||
| 13 | #include "video_core/shader/compiler_settings.h" | ||
| 12 | #include "video_core/shader/shader_ir.h" | 14 | #include "video_core/shader/shader_ir.h" |
| 13 | 15 | ||
| 14 | namespace VideoCommon::Shader { | 16 | namespace VideoCommon::Shader { |
| @@ -67,13 +69,15 @@ struct ShaderBlock { | |||
| 67 | 69 | ||
| 68 | struct ShaderCharacteristics { | 70 | struct ShaderCharacteristics { |
| 69 | std::list<ShaderBlock> blocks{}; | 71 | std::list<ShaderBlock> blocks{}; |
| 70 | bool decompilable{}; | 72 | std::set<u32> labels{}; |
| 71 | u32 start{}; | 73 | u32 start{}; |
| 72 | u32 end{}; | 74 | u32 end{}; |
| 73 | std::unordered_set<u32> labels{}; | 75 | ASTManager manager{true, true}; |
| 76 | CompilerSettings settings{}; | ||
| 74 | }; | 77 | }; |
| 75 | 78 | ||
| 76 | std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, | 79 | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, |
| 77 | std::size_t program_size, u32 start_address); | 80 | u32 start_address, |
| 81 | const CompilerSettings& settings); | ||
| 78 | 82 | ||
| 79 | } // namespace VideoCommon::Shader | 83 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index 47a9fd961..2626b1616 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp | |||
| @@ -35,58 +35,138 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) { | |||
| 35 | 35 | ||
| 36 | } // namespace | 36 | } // namespace |
| 37 | 37 | ||
| 38 | class ASTDecoder { | ||
| 39 | public: | ||
| 40 | ASTDecoder(ShaderIR& ir) : ir(ir) {} | ||
| 41 | |||
| 42 | void operator()(ASTProgram& ast) { | ||
| 43 | ASTNode current = ast.nodes.GetFirst(); | ||
| 44 | while (current) { | ||
| 45 | Visit(current); | ||
| 46 | current = current->GetNext(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void operator()(ASTIfThen& ast) { | ||
| 51 | ASTNode current = ast.nodes.GetFirst(); | ||
| 52 | while (current) { | ||
| 53 | Visit(current); | ||
| 54 | current = current->GetNext(); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | void operator()(ASTIfElse& ast) { | ||
| 59 | ASTNode current = ast.nodes.GetFirst(); | ||
| 60 | while (current) { | ||
| 61 | Visit(current); | ||
| 62 | current = current->GetNext(); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void operator()(ASTBlockEncoded& ast) {} | ||
| 67 | |||
| 68 | void operator()(ASTBlockDecoded& ast) {} | ||
| 69 | |||
| 70 | void operator()(ASTVarSet& ast) {} | ||
| 71 | |||
| 72 | void operator()(ASTLabel& ast) {} | ||
| 73 | |||
| 74 | void operator()(ASTGoto& ast) {} | ||
| 75 | |||
| 76 | void operator()(ASTDoWhile& ast) { | ||
| 77 | ASTNode current = ast.nodes.GetFirst(); | ||
| 78 | while (current) { | ||
| 79 | Visit(current); | ||
| 80 | current = current->GetNext(); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | void operator()(ASTReturn& ast) {} | ||
| 85 | |||
| 86 | void operator()(ASTBreak& ast) {} | ||
| 87 | |||
| 88 | void Visit(ASTNode& node) { | ||
| 89 | std::visit(*this, *node->GetInnerData()); | ||
| 90 | if (node->IsBlockEncoded()) { | ||
| 91 | auto block = std::get_if<ASTBlockEncoded>(node->GetInnerData()); | ||
| 92 | NodeBlock bb = ir.DecodeRange(block->start, block->end); | ||
| 93 | node->TransformBlockEncoded(std::move(bb)); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | private: | ||
| 98 | ShaderIR& ir; | ||
| 99 | }; | ||
| 100 | |||
| 38 | void ShaderIR::Decode() { | 101 | void ShaderIR::Decode() { |
| 39 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); | 102 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); |
| 40 | 103 | ||
| 41 | disable_flow_stack = false; | 104 | decompiled = false; |
| 42 | const auto info = ScanFlow(program_code, program_size, main_offset); | 105 | auto info = ScanFlow(program_code, program_size, main_offset, settings); |
| 43 | if (info) { | 106 | auto& shader_info = *info; |
| 44 | const auto& shader_info = *info; | 107 | coverage_begin = shader_info.start; |
| 45 | coverage_begin = shader_info.start; | 108 | coverage_end = shader_info.end; |
| 46 | coverage_end = shader_info.end; | 109 | switch (shader_info.settings.depth) { |
| 47 | if (shader_info.decompilable) { | 110 | case CompileDepth::FlowStack: { |
| 48 | disable_flow_stack = true; | ||
| 49 | const auto insert_block = [this](NodeBlock& nodes, u32 label) { | ||
| 50 | if (label == static_cast<u32>(exit_branch)) { | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | basic_blocks.insert({label, nodes}); | ||
| 54 | }; | ||
| 55 | const auto& blocks = shader_info.blocks; | ||
| 56 | NodeBlock current_block; | ||
| 57 | u32 current_label = static_cast<u32>(exit_branch); | ||
| 58 | for (auto& block : blocks) { | ||
| 59 | if (shader_info.labels.count(block.start) != 0) { | ||
| 60 | insert_block(current_block, current_label); | ||
| 61 | current_block.clear(); | ||
| 62 | current_label = block.start; | ||
| 63 | } | ||
| 64 | if (!block.ignore_branch) { | ||
| 65 | DecodeRangeInner(current_block, block.start, block.end); | ||
| 66 | InsertControlFlow(current_block, block); | ||
| 67 | } else { | ||
| 68 | DecodeRangeInner(current_block, block.start, block.end + 1); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | insert_block(current_block, current_label); | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method"); | ||
| 75 | // we can't decompile it, fallback to standard method | ||
| 76 | for (const auto& block : shader_info.blocks) { | 111 | for (const auto& block : shader_info.blocks) { |
| 77 | basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); | 112 | basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); |
| 78 | } | 113 | } |
| 79 | return; | 114 | break; |
| 80 | } | 115 | } |
| 81 | LOG_WARNING(HW_GPU, "Flow Analysis Failed! Falling back to brute force compiling"); | 116 | case CompileDepth::NoFlowStack: { |
| 82 | 117 | disable_flow_stack = true; | |
| 83 | // Now we need to deal with an undecompilable shader. We need to brute force | 118 | const auto insert_block = [this](NodeBlock& nodes, u32 label) { |
| 84 | // a shader that captures every position. | 119 | if (label == static_cast<u32>(exit_branch)) { |
| 85 | coverage_begin = main_offset; | 120 | return; |
| 86 | const u32 shader_end = static_cast<u32>(program_size / sizeof(u64)); | 121 | } |
| 87 | coverage_end = shader_end; | 122 | basic_blocks.insert({label, nodes}); |
| 88 | for (u32 label = main_offset; label < shader_end; label++) { | 123 | }; |
| 89 | basic_blocks.insert({label, DecodeRange(label, label + 1)}); | 124 | const auto& blocks = shader_info.blocks; |
| 125 | NodeBlock current_block; | ||
| 126 | u32 current_label = static_cast<u32>(exit_branch); | ||
| 127 | for (auto& block : blocks) { | ||
| 128 | if (shader_info.labels.count(block.start) != 0) { | ||
| 129 | insert_block(current_block, current_label); | ||
| 130 | current_block.clear(); | ||
| 131 | current_label = block.start; | ||
| 132 | } | ||
| 133 | if (!block.ignore_branch) { | ||
| 134 | DecodeRangeInner(current_block, block.start, block.end); | ||
| 135 | InsertControlFlow(current_block, block); | ||
| 136 | } else { | ||
| 137 | DecodeRangeInner(current_block, block.start, block.end + 1); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | insert_block(current_block, current_label); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | case CompileDepth::DecompileBackwards: | ||
| 144 | case CompileDepth::FullDecompile: { | ||
| 145 | program_manager = std::move(shader_info.manager); | ||
| 146 | disable_flow_stack = true; | ||
| 147 | decompiled = true; | ||
| 148 | ASTDecoder decoder{*this}; | ||
| 149 | ASTNode program = GetASTProgram(); | ||
| 150 | decoder.Visit(program); | ||
| 151 | break; | ||
| 152 | } | ||
| 153 | default: | ||
| 154 | LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!"); | ||
| 155 | [[fallthrough]]; | ||
| 156 | case CompileDepth::BruteForce: { | ||
| 157 | coverage_begin = main_offset; | ||
| 158 | const u32 shader_end = static_cast<u32>(program_size / sizeof(u64)); | ||
| 159 | coverage_end = shader_end; | ||
| 160 | for (u32 label = main_offset; label < shader_end; label++) { | ||
| 161 | basic_blocks.insert({label, DecodeRange(label, label + 1)}); | ||
| 162 | } | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | if (settings.depth != shader_info.settings.depth) { | ||
| 167 | LOG_WARNING( | ||
| 168 | HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"", | ||
| 169 | CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth)); | ||
| 90 | } | 170 | } |
| 91 | } | 171 | } |
| 92 | 172 | ||
diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp index 840694527..fec8f2dbe 100644 --- a/src/video_core/shader/decode/half_set_predicate.cpp +++ b/src/video_core/shader/decode/half_set_predicate.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | ||
| 7 | #include "video_core/engines/shader_bytecode.h" | 8 | #include "video_core/engines/shader_bytecode.h" |
| 8 | #include "video_core/shader/node_helper.h" | 9 | #include "video_core/shader/node_helper.h" |
| 9 | #include "video_core/shader/shader_ir.h" | 10 | #include "video_core/shader/shader_ir.h" |
| @@ -18,7 +19,7 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { | |||
| 18 | const Instruction instr = {program_code[pc]}; | 19 | const Instruction instr = {program_code[pc]}; |
| 19 | const auto opcode = OpCode::Decode(instr); | 20 | const auto opcode = OpCode::Decode(instr); |
| 20 | 21 | ||
| 21 | DEBUG_ASSERT(instr.hsetp2.ftz == 0); | 22 | LOG_DEBUG(HW_GPU, "ftz={}", static_cast<u32>(instr.hsetp2.ftz)); |
| 22 | 23 | ||
| 23 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hsetp2.type_a); | 24 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hsetp2.type_a); |
| 24 | op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a); | 25 | op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a); |
| @@ -32,6 +33,8 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { | |||
| 32 | h_and = instr.hsetp2.cbuf_and_imm.h_and; | 33 | h_and = instr.hsetp2.cbuf_and_imm.h_and; |
| 33 | op_b = GetOperandAbsNegHalf(GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), | 34 | op_b = GetOperandAbsNegHalf(GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), |
| 34 | instr.hsetp2.cbuf.abs_b, instr.hsetp2.cbuf.negate_b); | 35 | instr.hsetp2.cbuf.abs_b, instr.hsetp2.cbuf.negate_b); |
| 36 | // F32 is hardcoded in hardware | ||
| 37 | op_b = UnpackHalfFloat(std::move(op_b), Tegra::Shader::HalfType::F32); | ||
| 35 | break; | 38 | break; |
| 36 | case OpCode::Id::HSETP2_IMM: | 39 | case OpCode::Id::HSETP2_IMM: |
| 37 | cond = instr.hsetp2.cbuf_and_imm.cond; | 40 | cond = instr.hsetp2.cbuf_and_imm.cond; |
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index d54fb88c9..95ec1cdd9 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp | |||
| @@ -41,11 +41,46 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | |||
| 41 | const Instruction instr = {program_code[pc]}; | 41 | const Instruction instr = {program_code[pc]}; |
| 42 | const auto opcode = OpCode::Decode(instr); | 42 | const auto opcode = OpCode::Decode(instr); |
| 43 | 43 | ||
| 44 | const auto GetCoordinates = [this, instr](Tegra::Shader::ImageType image_type) { | ||
| 45 | std::vector<Node> coords; | ||
| 46 | const std::size_t num_coords{GetImageTypeNumCoordinates(image_type)}; | ||
| 47 | coords.reserve(num_coords); | ||
| 48 | for (std::size_t i = 0; i < num_coords; ++i) { | ||
| 49 | coords.push_back(GetRegister(instr.gpr8.Value() + i)); | ||
| 50 | } | ||
| 51 | return coords; | ||
| 52 | }; | ||
| 53 | |||
| 44 | switch (opcode->get().GetId()) { | 54 | switch (opcode->get().GetId()) { |
| 55 | case OpCode::Id::SULD: { | ||
| 56 | UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P); | ||
| 57 | UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store != | ||
| 58 | Tegra::Shader::OutOfBoundsStore::Ignore); | ||
| 59 | |||
| 60 | const auto type{instr.suldst.image_type}; | ||
| 61 | auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type) | ||
| 62 | : GetBindlessImage(instr.gpr39, type)}; | ||
| 63 | image.MarkRead(); | ||
| 64 | |||
| 65 | u32 indexer = 0; | ||
| 66 | for (u32 element = 0; element < 4; ++element) { | ||
| 67 | if (!instr.suldst.IsComponentEnabled(element)) { | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | MetaImage meta{image, {}, element}; | ||
| 71 | Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type)); | ||
| 72 | SetTemporary(bb, indexer++, std::move(value)); | ||
| 73 | } | ||
| 74 | for (u32 i = 0; i < indexer; ++i) { | ||
| 75 | SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i)); | ||
| 76 | } | ||
| 77 | break; | ||
| 78 | } | ||
| 45 | case OpCode::Id::SUST: { | 79 | case OpCode::Id::SUST: { |
| 46 | UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P); | 80 | UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P); |
| 47 | UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore); | 81 | UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store != |
| 48 | UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store | 82 | Tegra::Shader::OutOfBoundsStore::Ignore); |
| 83 | UNIMPLEMENTED_IF(instr.suldst.component_mask_selector != 0xf); // Ensure we have RGBA | ||
| 49 | 84 | ||
| 50 | std::vector<Node> values; | 85 | std::vector<Node> values; |
| 51 | constexpr std::size_t hardcoded_size{4}; | 86 | constexpr std::size_t hardcoded_size{4}; |
| @@ -53,58 +88,51 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | |||
| 53 | values.push_back(GetRegister(instr.gpr0.Value() + i)); | 88 | values.push_back(GetRegister(instr.gpr0.Value() + i)); |
| 54 | } | 89 | } |
| 55 | 90 | ||
| 56 | std::vector<Node> coords; | 91 | const auto type{instr.suldst.image_type}; |
| 57 | const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)}; | 92 | auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type) |
| 58 | for (std::size_t i = 0; i < num_coords; ++i) { | 93 | : GetBindlessImage(instr.gpr39, type)}; |
| 59 | coords.push_back(GetRegister(instr.gpr8.Value() + i)); | ||
| 60 | } | ||
| 61 | |||
| 62 | const auto type{instr.sust.image_type}; | ||
| 63 | auto& image{instr.sust.is_immediate ? GetImage(instr.image, type) | ||
| 64 | : GetBindlessImage(instr.gpr39, type)}; | ||
| 65 | image.MarkWrite(); | 94 | image.MarkWrite(); |
| 66 | 95 | ||
| 67 | MetaImage meta{image, values}; | 96 | MetaImage meta{image, std::move(values)}; |
| 68 | bb.push_back(Operation(OperationCode::ImageStore, meta, std::move(coords))); | 97 | bb.push_back(Operation(OperationCode::ImageStore, meta, GetCoordinates(type))); |
| 69 | break; | 98 | break; |
| 70 | } | 99 | } |
| 71 | case OpCode::Id::SUATOM: { | 100 | case OpCode::Id::SUATOM: { |
| 72 | UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0); | 101 | UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0); |
| 73 | 102 | ||
| 74 | Node value = GetRegister(instr.gpr0); | ||
| 75 | |||
| 76 | std::vector<Node> coords; | ||
| 77 | const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)}; | ||
| 78 | for (std::size_t i = 0; i < num_coords; ++i) { | ||
| 79 | coords.push_back(GetRegister(instr.gpr8.Value() + i)); | ||
| 80 | } | ||
| 81 | |||
| 82 | const OperationCode operation_code = [instr] { | 103 | const OperationCode operation_code = [instr] { |
| 83 | switch (instr.suatom_d.operation) { | 104 | switch (instr.suatom_d.operation_type) { |
| 84 | case Tegra::Shader::ImageAtomicOperation::Add: | 105 | case Tegra::Shader::ImageAtomicOperationType::S32: |
| 85 | return OperationCode::AtomicImageAdd; | 106 | case Tegra::Shader::ImageAtomicOperationType::U32: |
| 86 | case Tegra::Shader::ImageAtomicOperation::Min: | 107 | switch (instr.suatom_d.operation) { |
| 87 | return OperationCode::AtomicImageMin; | 108 | case Tegra::Shader::ImageAtomicOperation::Add: |
| 88 | case Tegra::Shader::ImageAtomicOperation::Max: | 109 | return OperationCode::AtomicImageAdd; |
| 89 | return OperationCode::AtomicImageMax; | 110 | case Tegra::Shader::ImageAtomicOperation::And: |
| 90 | case Tegra::Shader::ImageAtomicOperation::And: | 111 | return OperationCode::AtomicImageAnd; |
| 91 | return OperationCode::AtomicImageAnd; | 112 | case Tegra::Shader::ImageAtomicOperation::Or: |
| 92 | case Tegra::Shader::ImageAtomicOperation::Or: | 113 | return OperationCode::AtomicImageOr; |
| 93 | return OperationCode::AtomicImageOr; | 114 | case Tegra::Shader::ImageAtomicOperation::Xor: |
| 94 | case Tegra::Shader::ImageAtomicOperation::Xor: | 115 | return OperationCode::AtomicImageXor; |
| 95 | return OperationCode::AtomicImageXor; | 116 | case Tegra::Shader::ImageAtomicOperation::Exch: |
| 96 | case Tegra::Shader::ImageAtomicOperation::Exch: | 117 | return OperationCode::AtomicImageExchange; |
| 97 | return OperationCode::AtomicImageExchange; | 118 | } |
| 98 | default: | 119 | default: |
| 99 | UNIMPLEMENTED_MSG("Unimplemented operation={}", | 120 | break; |
| 100 | static_cast<u32>(instr.suatom_d.operation.Value())); | ||
| 101 | return OperationCode::AtomicImageAdd; | ||
| 102 | } | 121 | } |
| 122 | UNIMPLEMENTED_MSG("Unimplemented operation={} type={}", | ||
| 123 | static_cast<u64>(instr.suatom_d.operation.Value()), | ||
| 124 | static_cast<u64>(instr.suatom_d.operation_type.Value())); | ||
| 125 | return OperationCode::AtomicImageAdd; | ||
| 103 | }(); | 126 | }(); |
| 104 | 127 | ||
| 105 | const auto& image{GetImage(instr.image, instr.suatom_d.image_type, instr.suatom_d.size)}; | 128 | Node value = GetRegister(instr.gpr0); |
| 129 | |||
| 130 | const auto type = instr.suatom_d.image_type; | ||
| 131 | auto& image = GetImage(instr.image, type); | ||
| 132 | image.MarkAtomic(); | ||
| 133 | |||
| 106 | MetaImage meta{image, {std::move(value)}}; | 134 | MetaImage meta{image, {std::move(value)}}; |
| 107 | SetRegister(bb, instr.gpr0, Operation(operation_code, meta, std::move(coords))); | 135 | SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type))); |
| 108 | break; | 136 | break; |
| 109 | } | 137 | } |
| 110 | default: | 138 | default: |
| @@ -114,35 +142,32 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | |||
| 114 | return pc; | 142 | return pc; |
| 115 | } | 143 | } |
| 116 | 144 | ||
| 117 | Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, | 145 | Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) { |
| 118 | std::optional<Tegra::Shader::ImageAtomicSize> size) { | ||
| 119 | const auto offset{static_cast<std::size_t>(image.index.Value())}; | 146 | const auto offset{static_cast<std::size_t>(image.index.Value())}; |
| 120 | if (const auto image = TryUseExistingImage(offset, type, size)) { | 147 | if (const auto image = TryUseExistingImage(offset, type)) { |
| 121 | return *image; | 148 | return *image; |
| 122 | } | 149 | } |
| 123 | 150 | ||
| 124 | const std::size_t next_index{used_images.size()}; | 151 | const std::size_t next_index{used_images.size()}; |
| 125 | return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second; | 152 | return used_images.emplace(offset, Image{offset, next_index, type}).first->second; |
| 126 | } | 153 | } |
| 127 | 154 | ||
| 128 | Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, | 155 | Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) { |
| 129 | std::optional<Tegra::Shader::ImageAtomicSize> size) { | ||
| 130 | const Node image_register{GetRegister(reg)}; | 156 | const Node image_register{GetRegister(reg)}; |
| 131 | const auto [base_image, cbuf_index, cbuf_offset]{ | 157 | const auto [base_image, cbuf_index, cbuf_offset]{ |
| 132 | TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; | 158 | TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; |
| 133 | const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)}; | 159 | const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)}; |
| 134 | 160 | ||
| 135 | if (const auto image = TryUseExistingImage(cbuf_key, type, size)) { | 161 | if (const auto image = TryUseExistingImage(cbuf_key, type)) { |
| 136 | return *image; | 162 | return *image; |
| 137 | } | 163 | } |
| 138 | 164 | ||
| 139 | const std::size_t next_index{used_images.size()}; | 165 | const std::size_t next_index{used_images.size()}; |
| 140 | return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size}) | 166 | return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type}) |
| 141 | .first->second; | 167 | .first->second; |
| 142 | } | 168 | } |
| 143 | 169 | ||
| 144 | Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, | 170 | Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) { |
| 145 | std::optional<Tegra::Shader::ImageAtomicSize> size) { | ||
| 146 | auto it = used_images.find(offset); | 171 | auto it = used_images.find(offset); |
| 147 | if (it == used_images.end()) { | 172 | if (it == used_images.end()) { |
| 148 | return nullptr; | 173 | return nullptr; |
| @@ -150,14 +175,6 @@ Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, | |||
| 150 | auto& image = it->second; | 175 | auto& image = it->second; |
| 151 | ASSERT(image.GetType() == type); | 176 | ASSERT(image.GetType() == type); |
| 152 | 177 | ||
| 153 | if (size) { | ||
| 154 | // We know the size, if it's known it has to be the same as before, otherwise we can set it. | ||
| 155 | if (image.IsSizeKnown()) { | ||
| 156 | ASSERT(image.GetSize() == size); | ||
| 157 | } else { | ||
| 158 | image.SetSize(*size); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | return ℑ | 178 | return ℑ |
| 162 | } | 179 | } |
| 163 | 180 | ||
diff --git a/src/video_core/shader/expr.cpp b/src/video_core/shader/expr.cpp new file mode 100644 index 000000000..2647865d4 --- /dev/null +++ b/src/video_core/shader/expr.cpp | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <variant> | ||
| 7 | |||
| 8 | #include "video_core/shader/expr.h" | ||
| 9 | |||
| 10 | namespace VideoCommon::Shader { | ||
| 11 | namespace { | ||
| 12 | bool ExprIsBoolean(const Expr& expr) { | ||
| 13 | return std::holds_alternative<ExprBoolean>(*expr); | ||
| 14 | } | ||
| 15 | |||
| 16 | bool ExprBooleanGet(const Expr& expr) { | ||
| 17 | return std::get_if<ExprBoolean>(expr.get())->value; | ||
| 18 | } | ||
| 19 | } // Anonymous namespace | ||
| 20 | |||
| 21 | bool ExprAnd::operator==(const ExprAnd& b) const { | ||
| 22 | return (*operand1 == *b.operand1) && (*operand2 == *b.operand2); | ||
| 23 | } | ||
| 24 | |||
| 25 | bool ExprAnd::operator!=(const ExprAnd& b) const { | ||
| 26 | return !operator==(b); | ||
| 27 | } | ||
| 28 | |||
| 29 | bool ExprOr::operator==(const ExprOr& b) const { | ||
| 30 | return (*operand1 == *b.operand1) && (*operand2 == *b.operand2); | ||
| 31 | } | ||
| 32 | |||
| 33 | bool ExprOr::operator!=(const ExprOr& b) const { | ||
| 34 | return !operator==(b); | ||
| 35 | } | ||
| 36 | |||
| 37 | bool ExprNot::operator==(const ExprNot& b) const { | ||
| 38 | return *operand1 == *b.operand1; | ||
| 39 | } | ||
| 40 | |||
| 41 | bool ExprNot::operator!=(const ExprNot& b) const { | ||
| 42 | return !operator==(b); | ||
| 43 | } | ||
| 44 | |||
| 45 | Expr MakeExprNot(Expr first) { | ||
| 46 | if (std::holds_alternative<ExprNot>(*first)) { | ||
| 47 | return std::get_if<ExprNot>(first.get())->operand1; | ||
| 48 | } | ||
| 49 | return MakeExpr<ExprNot>(std::move(first)); | ||
| 50 | } | ||
| 51 | |||
| 52 | Expr MakeExprAnd(Expr first, Expr second) { | ||
| 53 | if (ExprIsBoolean(first)) { | ||
| 54 | return ExprBooleanGet(first) ? second : first; | ||
| 55 | } | ||
| 56 | if (ExprIsBoolean(second)) { | ||
| 57 | return ExprBooleanGet(second) ? first : second; | ||
| 58 | } | ||
| 59 | return MakeExpr<ExprAnd>(std::move(first), std::move(second)); | ||
| 60 | } | ||
| 61 | |||
| 62 | Expr MakeExprOr(Expr first, Expr second) { | ||
| 63 | if (ExprIsBoolean(first)) { | ||
| 64 | return ExprBooleanGet(first) ? first : second; | ||
| 65 | } | ||
| 66 | if (ExprIsBoolean(second)) { | ||
| 67 | return ExprBooleanGet(second) ? second : first; | ||
| 68 | } | ||
| 69 | return MakeExpr<ExprOr>(std::move(first), std::move(second)); | ||
| 70 | } | ||
| 71 | |||
| 72 | bool ExprAreEqual(const Expr& first, const Expr& second) { | ||
| 73 | return (*first) == (*second); | ||
| 74 | } | ||
| 75 | |||
| 76 | bool ExprAreOpposite(const Expr& first, const Expr& second) { | ||
| 77 | if (std::holds_alternative<ExprNot>(*first)) { | ||
| 78 | return ExprAreEqual(std::get_if<ExprNot>(first.get())->operand1, second); | ||
| 79 | } | ||
| 80 | if (std::holds_alternative<ExprNot>(*second)) { | ||
| 81 | return ExprAreEqual(std::get_if<ExprNot>(second.get())->operand1, first); | ||
| 82 | } | ||
| 83 | return false; | ||
| 84 | } | ||
| 85 | |||
| 86 | bool ExprIsTrue(const Expr& first) { | ||
| 87 | if (ExprIsBoolean(first)) { | ||
| 88 | return ExprBooleanGet(first); | ||
| 89 | } | ||
| 90 | return false; | ||
| 91 | } | ||
| 92 | |||
| 93 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h new file mode 100644 index 000000000..d3dcd00ec --- /dev/null +++ b/src/video_core/shader/expr.h | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | // Copyright 2019 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 <variant> | ||
| 9 | |||
| 10 | #include "video_core/engines/shader_bytecode.h" | ||
| 11 | |||
| 12 | namespace VideoCommon::Shader { | ||
| 13 | |||
| 14 | using Tegra::Shader::ConditionCode; | ||
| 15 | using Tegra::Shader::Pred; | ||
| 16 | |||
| 17 | class ExprAnd; | ||
| 18 | class ExprBoolean; | ||
| 19 | class ExprCondCode; | ||
| 20 | class ExprNot; | ||
| 21 | class ExprOr; | ||
| 22 | class ExprPredicate; | ||
| 23 | class ExprVar; | ||
| 24 | |||
| 25 | using ExprData = | ||
| 26 | std::variant<ExprVar, ExprCondCode, ExprPredicate, ExprNot, ExprOr, ExprAnd, ExprBoolean>; | ||
| 27 | using Expr = std::shared_ptr<ExprData>; | ||
| 28 | |||
| 29 | class ExprAnd final { | ||
| 30 | public: | ||
| 31 | explicit ExprAnd(Expr a, Expr b) : operand1{std::move(a)}, operand2{std::move(b)} {} | ||
| 32 | |||
| 33 | bool operator==(const ExprAnd& b) const; | ||
| 34 | bool operator!=(const ExprAnd& b) const; | ||
| 35 | |||
| 36 | Expr operand1; | ||
| 37 | Expr operand2; | ||
| 38 | }; | ||
| 39 | |||
| 40 | class ExprOr final { | ||
| 41 | public: | ||
| 42 | explicit ExprOr(Expr a, Expr b) : operand1{std::move(a)}, operand2{std::move(b)} {} | ||
| 43 | |||
| 44 | bool operator==(const ExprOr& b) const; | ||
| 45 | bool operator!=(const ExprOr& b) const; | ||
| 46 | |||
| 47 | Expr operand1; | ||
| 48 | Expr operand2; | ||
| 49 | }; | ||
| 50 | |||
| 51 | class ExprNot final { | ||
| 52 | public: | ||
| 53 | explicit ExprNot(Expr a) : operand1{std::move(a)} {} | ||
| 54 | |||
| 55 | bool operator==(const ExprNot& b) const; | ||
| 56 | bool operator!=(const ExprNot& b) const; | ||
| 57 | |||
| 58 | Expr operand1; | ||
| 59 | }; | ||
| 60 | |||
| 61 | class ExprVar final { | ||
| 62 | public: | ||
| 63 | explicit ExprVar(u32 index) : var_index{index} {} | ||
| 64 | |||
| 65 | bool operator==(const ExprVar& b) const { | ||
| 66 | return var_index == b.var_index; | ||
| 67 | } | ||
| 68 | |||
| 69 | bool operator!=(const ExprVar& b) const { | ||
| 70 | return !operator==(b); | ||
| 71 | } | ||
| 72 | |||
| 73 | u32 var_index; | ||
| 74 | }; | ||
| 75 | |||
| 76 | class ExprPredicate final { | ||
| 77 | public: | ||
| 78 | explicit ExprPredicate(u32 predicate) : predicate{predicate} {} | ||
| 79 | |||
| 80 | bool operator==(const ExprPredicate& b) const { | ||
| 81 | return predicate == b.predicate; | ||
| 82 | } | ||
| 83 | |||
| 84 | bool operator!=(const ExprPredicate& b) const { | ||
| 85 | return !operator==(b); | ||
| 86 | } | ||
| 87 | |||
| 88 | u32 predicate; | ||
| 89 | }; | ||
| 90 | |||
| 91 | class ExprCondCode final { | ||
| 92 | public: | ||
| 93 | explicit ExprCondCode(ConditionCode cc) : cc{cc} {} | ||
| 94 | |||
| 95 | bool operator==(const ExprCondCode& b) const { | ||
| 96 | return cc == b.cc; | ||
| 97 | } | ||
| 98 | |||
| 99 | bool operator!=(const ExprCondCode& b) const { | ||
| 100 | return !operator==(b); | ||
| 101 | } | ||
| 102 | |||
| 103 | ConditionCode cc; | ||
| 104 | }; | ||
| 105 | |||
| 106 | class ExprBoolean final { | ||
| 107 | public: | ||
| 108 | explicit ExprBoolean(bool val) : value{val} {} | ||
| 109 | |||
| 110 | bool operator==(const ExprBoolean& b) const { | ||
| 111 | return value == b.value; | ||
| 112 | } | ||
| 113 | |||
| 114 | bool operator!=(const ExprBoolean& b) const { | ||
| 115 | return !operator==(b); | ||
| 116 | } | ||
| 117 | |||
| 118 | bool value; | ||
| 119 | }; | ||
| 120 | |||
| 121 | template <typename T, typename... Args> | ||
| 122 | Expr MakeExpr(Args&&... args) { | ||
| 123 | static_assert(std::is_convertible_v<T, ExprData>); | ||
| 124 | return std::make_shared<ExprData>(T(std::forward<Args>(args)...)); | ||
| 125 | } | ||
| 126 | |||
| 127 | bool ExprAreEqual(const Expr& first, const Expr& second); | ||
| 128 | |||
| 129 | bool ExprAreOpposite(const Expr& first, const Expr& second); | ||
| 130 | |||
| 131 | Expr MakeExprNot(Expr first); | ||
| 132 | |||
| 133 | Expr MakeExprAnd(Expr first, Expr second); | ||
| 134 | |||
| 135 | Expr MakeExprOr(Expr first, Expr second); | ||
| 136 | |||
| 137 | bool ExprIsTrue(const Expr& first); | ||
| 138 | |||
| 139 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index abf2cb1ab..338bab17c 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h | |||
| @@ -149,10 +149,10 @@ enum class OperationCode { | |||
| 149 | TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 | 149 | TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 |
| 150 | TexelFetch, /// (MetaTexture, int[N], int) -> float4 | 150 | TexelFetch, /// (MetaTexture, int[N], int) -> float4 |
| 151 | 151 | ||
| 152 | ImageStore, /// (MetaImage, int[N] values) -> void | 152 | ImageLoad, /// (MetaImage, int[N] coords) -> void |
| 153 | ImageStore, /// (MetaImage, int[N] coords) -> void | ||
| 154 | |||
| 153 | AtomicImageAdd, /// (MetaImage, int[N] coords) -> void | 155 | AtomicImageAdd, /// (MetaImage, int[N] coords) -> void |
| 154 | AtomicImageMin, /// (MetaImage, int[N] coords) -> void | ||
| 155 | AtomicImageMax, /// (MetaImage, int[N] coords) -> void | ||
| 156 | AtomicImageAnd, /// (MetaImage, int[N] coords) -> void | 156 | AtomicImageAnd, /// (MetaImage, int[N] coords) -> void |
| 157 | AtomicImageOr, /// (MetaImage, int[N] coords) -> void | 157 | AtomicImageOr, /// (MetaImage, int[N] coords) -> void |
| 158 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void | 158 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void |
| @@ -294,21 +294,18 @@ private: | |||
| 294 | 294 | ||
| 295 | class Image final { | 295 | class Image final { |
| 296 | public: | 296 | public: |
| 297 | constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, | 297 | constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type) |
| 298 | std::optional<Tegra::Shader::ImageAtomicSize> size) | 298 | : offset{offset}, index{index}, type{type}, is_bindless{false} {} |
| 299 | : offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {} | ||
| 300 | 299 | ||
| 301 | constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, | 300 | constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, |
| 302 | Tegra::Shader::ImageType type, | 301 | Tegra::Shader::ImageType type) |
| 303 | std::optional<Tegra::Shader::ImageAtomicSize> size) | ||
| 304 | : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, | 302 | : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, |
| 305 | is_bindless{true}, size{size} {} | 303 | is_bindless{true} {} |
| 306 | 304 | ||
| 307 | constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, | 305 | constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, |
| 308 | bool is_bindless, bool is_written, bool is_read, | 306 | bool is_bindless, bool is_written, bool is_read, bool is_atomic) |
| 309 | std::optional<Tegra::Shader::ImageAtomicSize> size) | ||
| 310 | : offset{offset}, index{index}, type{type}, is_bindless{is_bindless}, | 307 | : offset{offset}, index{index}, type{type}, is_bindless{is_bindless}, |
| 311 | is_written{is_written}, is_read{is_read}, size{size} {} | 308 | is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {} |
| 312 | 309 | ||
| 313 | void MarkWrite() { | 310 | void MarkWrite() { |
| 314 | is_written = true; | 311 | is_written = true; |
| @@ -318,8 +315,10 @@ public: | |||
| 318 | is_read = true; | 315 | is_read = true; |
| 319 | } | 316 | } |
| 320 | 317 | ||
| 321 | void SetSize(Tegra::Shader::ImageAtomicSize size_) { | 318 | void MarkAtomic() { |
| 322 | size = size_; | 319 | MarkWrite(); |
| 320 | MarkRead(); | ||
| 321 | is_atomic = true; | ||
| 323 | } | 322 | } |
| 324 | 323 | ||
| 325 | constexpr std::size_t GetOffset() const { | 324 | constexpr std::size_t GetOffset() const { |
| @@ -346,21 +345,17 @@ public: | |||
| 346 | return is_read; | 345 | return is_read; |
| 347 | } | 346 | } |
| 348 | 347 | ||
| 349 | constexpr std::pair<u32, u32> GetBindlessCBuf() const { | 348 | constexpr bool IsAtomic() const { |
| 350 | return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)}; | 349 | return is_atomic; |
| 351 | } | 350 | } |
| 352 | 351 | ||
| 353 | constexpr bool IsSizeKnown() const { | 352 | constexpr std::pair<u32, u32> GetBindlessCBuf() const { |
| 354 | return size.has_value(); | 353 | return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)}; |
| 355 | } | ||
| 356 | |||
| 357 | constexpr Tegra::Shader::ImageAtomicSize GetSize() const { | ||
| 358 | return size.value(); | ||
| 359 | } | 354 | } |
| 360 | 355 | ||
| 361 | constexpr bool operator<(const Image& rhs) const { | 356 | constexpr bool operator<(const Image& rhs) const { |
| 362 | return std::tie(offset, index, type, size, is_bindless) < | 357 | return std::tie(offset, index, type, is_bindless) < |
| 363 | std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless); | 358 | std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless); |
| 364 | } | 359 | } |
| 365 | 360 | ||
| 366 | private: | 361 | private: |
| @@ -370,7 +365,7 @@ private: | |||
| 370 | bool is_bindless{}; | 365 | bool is_bindless{}; |
| 371 | bool is_written{}; | 366 | bool is_written{}; |
| 372 | bool is_read{}; | 367 | bool is_read{}; |
| 373 | std::optional<Tegra::Shader::ImageAtomicSize> size{}; | 368 | bool is_atomic{}; |
| 374 | }; | 369 | }; |
| 375 | 370 | ||
| 376 | struct GlobalMemoryBase { | 371 | struct GlobalMemoryBase { |
| @@ -402,6 +397,7 @@ struct MetaTexture { | |||
| 402 | struct MetaImage { | 397 | struct MetaImage { |
| 403 | const Image& image; | 398 | const Image& image; |
| 404 | std::vector<Node> values; | 399 | std::vector<Node> values; |
| 400 | u32 element{}; | ||
| 405 | }; | 401 | }; |
| 406 | 402 | ||
| 407 | /// Parameters that modify an operation but are not part of any particular operand | 403 | /// Parameters that modify an operation but are not part of any particular operand |
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 2c357f310..c1f2b88c8 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -22,8 +22,10 @@ using Tegra::Shader::PredCondition; | |||
| 22 | using Tegra::Shader::PredOperation; | 22 | using Tegra::Shader::PredOperation; |
| 23 | using Tegra::Shader::Register; | 23 | using Tegra::Shader::Register; |
| 24 | 24 | ||
| 25 | ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size) | 25 | ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size, |
| 26 | : program_code{program_code}, main_offset{main_offset}, program_size{size} { | 26 | CompilerSettings settings) |
| 27 | : program_code{program_code}, main_offset{main_offset}, program_size{size}, basic_blocks{}, | ||
| 28 | program_manager{true, true}, settings{settings} { | ||
| 27 | Decode(); | 29 | Decode(); |
| 28 | } | 30 | } |
| 29 | 31 | ||
| @@ -137,7 +139,7 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff | |||
| 137 | return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer)); | 139 | return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer)); |
| 138 | } | 140 | } |
| 139 | 141 | ||
| 140 | Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) { | 142 | Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const { |
| 141 | const Node node = MakeNode<InternalFlagNode>(flag); | 143 | const Node node = MakeNode<InternalFlagNode>(flag); |
| 142 | if (negated) { | 144 | if (negated) { |
| 143 | return Operation(OperationCode::LogicalNegate, node); | 145 | return Operation(OperationCode::LogicalNegate, node); |
| @@ -367,13 +369,13 @@ OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) { | |||
| 367 | return op->second; | 369 | return op->second; |
| 368 | } | 370 | } |
| 369 | 371 | ||
| 370 | Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) { | 372 | Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) const { |
| 371 | switch (cc) { | 373 | switch (cc) { |
| 372 | case Tegra::Shader::ConditionCode::NEU: | 374 | case Tegra::Shader::ConditionCode::NEU: |
| 373 | return GetInternalFlag(InternalFlag::Zero, true); | 375 | return GetInternalFlag(InternalFlag::Zero, true); |
| 374 | default: | 376 | default: |
| 375 | UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); | 377 | UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); |
| 376 | return GetPredicate(static_cast<u64>(Pred::NeverExecute)); | 378 | return MakeNode<PredicateNode>(Pred::NeverExecute, false); |
| 377 | } | 379 | } |
| 378 | } | 380 | } |
| 379 | 381 | ||
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 2f03d83ba..105981d67 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #include "video_core/engines/maxwell_3d.h" | 15 | #include "video_core/engines/maxwell_3d.h" |
| 16 | #include "video_core/engines/shader_bytecode.h" | 16 | #include "video_core/engines/shader_bytecode.h" |
| 17 | #include "video_core/engines/shader_header.h" | 17 | #include "video_core/engines/shader_header.h" |
| 18 | #include "video_core/shader/ast.h" | ||
| 19 | #include "video_core/shader/compiler_settings.h" | ||
| 18 | #include "video_core/shader/node.h" | 20 | #include "video_core/shader/node.h" |
| 19 | 21 | ||
| 20 | namespace VideoCommon::Shader { | 22 | namespace VideoCommon::Shader { |
| @@ -64,7 +66,8 @@ struct GlobalMemoryUsage { | |||
| 64 | 66 | ||
| 65 | class ShaderIR final { | 67 | class ShaderIR final { |
| 66 | public: | 68 | public: |
| 67 | explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size); | 69 | explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size, |
| 70 | CompilerSettings settings); | ||
| 68 | ~ShaderIR(); | 71 | ~ShaderIR(); |
| 69 | 72 | ||
| 70 | const std::map<u32, NodeBlock>& GetBasicBlocks() const { | 73 | const std::map<u32, NodeBlock>& GetBasicBlocks() const { |
| @@ -144,11 +147,31 @@ public: | |||
| 144 | return disable_flow_stack; | 147 | return disable_flow_stack; |
| 145 | } | 148 | } |
| 146 | 149 | ||
| 150 | bool IsDecompiled() const { | ||
| 151 | return decompiled; | ||
| 152 | } | ||
| 153 | |||
| 154 | const ASTManager& GetASTManager() const { | ||
| 155 | return program_manager; | ||
| 156 | } | ||
| 157 | |||
| 158 | ASTNode GetASTProgram() const { | ||
| 159 | return program_manager.GetProgram(); | ||
| 160 | } | ||
| 161 | |||
| 162 | u32 GetASTNumVariables() const { | ||
| 163 | return program_manager.GetVariables(); | ||
| 164 | } | ||
| 165 | |||
| 147 | u32 ConvertAddressToNvidiaSpace(const u32 address) const { | 166 | u32 ConvertAddressToNvidiaSpace(const u32 address) const { |
| 148 | return (address - main_offset) * sizeof(Tegra::Shader::Instruction); | 167 | return (address - main_offset) * sizeof(Tegra::Shader::Instruction); |
| 149 | } | 168 | } |
| 150 | 169 | ||
| 170 | /// Returns a condition code evaluated from internal flags | ||
| 171 | Node GetConditionCode(Tegra::Shader::ConditionCode cc) const; | ||
| 172 | |||
| 151 | private: | 173 | private: |
| 174 | friend class ASTDecoder; | ||
| 152 | void Decode(); | 175 | void Decode(); |
| 153 | 176 | ||
| 154 | NodeBlock DecodeRange(u32 begin, u32 end); | 177 | NodeBlock DecodeRange(u32 begin, u32 end); |
| @@ -213,7 +236,7 @@ private: | |||
| 213 | /// Generates a node representing an output attribute. Keeps track of used attributes. | 236 | /// Generates a node representing an output attribute. Keeps track of used attributes. |
| 214 | Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); | 237 | Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); |
| 215 | /// Generates a node representing an internal flag | 238 | /// Generates a node representing an internal flag |
| 216 | Node GetInternalFlag(InternalFlag flag, bool negated = false); | 239 | Node GetInternalFlag(InternalFlag flag, bool negated = false) const; |
| 217 | /// Generates a node representing a local memory address | 240 | /// Generates a node representing a local memory address |
| 218 | Node GetLocalMemory(Node address); | 241 | Node GetLocalMemory(Node address); |
| 219 | /// Generates a node representing a shared memory address | 242 | /// Generates a node representing a shared memory address |
| @@ -271,9 +294,6 @@ private: | |||
| 271 | /// Returns a predicate combiner operation | 294 | /// Returns a predicate combiner operation |
| 272 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); | 295 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); |
| 273 | 296 | ||
| 274 | /// Returns a condition code evaluated from internal flags | ||
| 275 | Node GetConditionCode(Tegra::Shader::ConditionCode cc); | ||
| 276 | |||
| 277 | /// Accesses a texture sampler | 297 | /// Accesses a texture sampler |
| 278 | const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler, | 298 | const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler, |
| 279 | Tegra::Shader::TextureType type, bool is_array, bool is_shadow); | 299 | Tegra::Shader::TextureType type, bool is_array, bool is_shadow); |
| @@ -284,16 +304,13 @@ private: | |||
| 284 | bool is_shadow); | 304 | bool is_shadow); |
| 285 | 305 | ||
| 286 | /// Accesses an image. | 306 | /// Accesses an image. |
| 287 | Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, | 307 | Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type); |
| 288 | std::optional<Tegra::Shader::ImageAtomicSize> size = {}); | ||
| 289 | 308 | ||
| 290 | /// Access a bindless image sampler. | 309 | /// Access a bindless image sampler. |
| 291 | Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, | 310 | Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type); |
| 292 | std::optional<Tegra::Shader::ImageAtomicSize> size = {}); | ||
| 293 | 311 | ||
| 294 | /// Tries to access an existing image, updating it's state as needed | 312 | /// Tries to access an existing image, updating it's state as needed |
| 295 | Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, | 313 | Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type); |
| 296 | std::optional<Tegra::Shader::ImageAtomicSize> size); | ||
| 297 | 314 | ||
| 298 | /// Extracts a sequence of bits from a node | 315 | /// Extracts a sequence of bits from a node |
| 299 | Node BitfieldExtract(Node value, u32 offset, u32 bits); | 316 | Node BitfieldExtract(Node value, u32 offset, u32 bits); |
| @@ -360,6 +377,7 @@ private: | |||
| 360 | const ProgramCode& program_code; | 377 | const ProgramCode& program_code; |
| 361 | const u32 main_offset; | 378 | const u32 main_offset; |
| 362 | const std::size_t program_size; | 379 | const std::size_t program_size; |
| 380 | bool decompiled{}; | ||
| 363 | bool disable_flow_stack{}; | 381 | bool disable_flow_stack{}; |
| 364 | 382 | ||
| 365 | u32 coverage_begin{}; | 383 | u32 coverage_begin{}; |
| @@ -367,6 +385,8 @@ private: | |||
| 367 | 385 | ||
| 368 | std::map<u32, NodeBlock> basic_blocks; | 386 | std::map<u32, NodeBlock> basic_blocks; |
| 369 | NodeBlock global_code; | 387 | NodeBlock global_code; |
| 388 | ASTManager program_manager; | ||
| 389 | CompilerSettings settings{}; | ||
| 370 | 390 | ||
| 371 | std::set<u32> used_registers; | 391 | std::set<u32> used_registers; |
| 372 | std::set<Tegra::Shader::Pred> used_predicates; | 392 | std::set<Tegra::Shader::Pred> used_predicates; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 877c6635d..ca2da8f97 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -224,8 +224,13 @@ public: | |||
| 224 | const Tegra::Engines::Fermi2D::Regs::Surface& dst_config, | 224 | const Tegra::Engines::Fermi2D::Regs::Surface& dst_config, |
| 225 | const Tegra::Engines::Fermi2D::Config& copy_config) { | 225 | const Tegra::Engines::Fermi2D::Config& copy_config) { |
| 226 | std::lock_guard lock{mutex}; | 226 | std::lock_guard lock{mutex}; |
| 227 | std::pair<TSurface, TView> dst_surface = GetFermiSurface(dst_config); | 227 | SurfaceParams src_params = SurfaceParams::CreateForFermiCopySurface(src_config); |
| 228 | std::pair<TSurface, TView> src_surface = GetFermiSurface(src_config); | 228 | SurfaceParams dst_params = SurfaceParams::CreateForFermiCopySurface(dst_config); |
| 229 | const GPUVAddr src_gpu_addr = src_config.Address(); | ||
| 230 | const GPUVAddr dst_gpu_addr = dst_config.Address(); | ||
| 231 | DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); | ||
| 232 | std::pair<TSurface, TView> dst_surface = GetSurface(dst_gpu_addr, dst_params, true, false); | ||
| 233 | std::pair<TSurface, TView> src_surface = GetSurface(src_gpu_addr, src_params, true, false); | ||
| 229 | ImageBlit(src_surface.second, dst_surface.second, copy_config); | 234 | ImageBlit(src_surface.second, dst_surface.second, copy_config); |
| 230 | dst_surface.first->MarkAsModified(true, Tick()); | 235 | dst_surface.first->MarkAsModified(true, Tick()); |
| 231 | } | 236 | } |
| @@ -357,6 +362,29 @@ private: | |||
| 357 | BufferCopy = 3, | 362 | BufferCopy = 3, |
| 358 | }; | 363 | }; |
| 359 | 364 | ||
| 365 | enum class DeductionType : u32 { | ||
| 366 | DeductionComplete, | ||
| 367 | DeductionIncomplete, | ||
| 368 | DeductionFailed, | ||
| 369 | }; | ||
| 370 | |||
| 371 | struct Deduction { | ||
| 372 | DeductionType type{DeductionType::DeductionFailed}; | ||
| 373 | TSurface surface{}; | ||
| 374 | |||
| 375 | bool Failed() const { | ||
| 376 | return type == DeductionType::DeductionFailed; | ||
| 377 | } | ||
| 378 | |||
| 379 | bool Incomplete() const { | ||
| 380 | return type == DeductionType::DeductionIncomplete; | ||
| 381 | } | ||
| 382 | |||
| 383 | bool IsDepth() const { | ||
| 384 | return surface->GetSurfaceParams().IsPixelFormatZeta(); | ||
| 385 | } | ||
| 386 | }; | ||
| 387 | |||
| 360 | /** | 388 | /** |
| 361 | * `PickStrategy` takes care of selecting a proper strategy to deal with a texture recycle. | 389 | * `PickStrategy` takes care of selecting a proper strategy to deal with a texture recycle. |
| 362 | * @param overlaps, the overlapping surfaces registered in the cache. | 390 | * @param overlaps, the overlapping surfaces registered in the cache. |
| @@ -691,6 +719,120 @@ private: | |||
| 691 | MatchTopologyResult::FullMatch); | 719 | MatchTopologyResult::FullMatch); |
| 692 | } | 720 | } |
| 693 | 721 | ||
| 722 | /** | ||
| 723 | * `DeduceSurface` gets the starting address and parameters of a candidate surface and tries | ||
| 724 | * to find a matching surface within the cache that's similar to it. If there are many textures | ||
| 725 | * or the texture found if entirely incompatible, it will fail. If no texture is found, the | ||
| 726 | * blit will be unsuccessful. | ||
| 727 | * @param gpu_addr, the starting address of the candidate surface. | ||
| 728 | * @param params, the paremeters on the candidate surface. | ||
| 729 | **/ | ||
| 730 | Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) { | ||
| 731 | const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; | ||
| 732 | const auto cache_addr{ToCacheAddr(host_ptr)}; | ||
| 733 | |||
| 734 | if (!cache_addr) { | ||
| 735 | Deduction result{}; | ||
| 736 | result.type = DeductionType::DeductionFailed; | ||
| 737 | return result; | ||
| 738 | } | ||
| 739 | |||
| 740 | if (const auto iter = l1_cache.find(cache_addr); iter != l1_cache.end()) { | ||
| 741 | TSurface& current_surface = iter->second; | ||
| 742 | const auto topological_result = current_surface->MatchesTopology(params); | ||
| 743 | if (topological_result != MatchTopologyResult::FullMatch) { | ||
| 744 | Deduction result{}; | ||
| 745 | result.type = DeductionType::DeductionFailed; | ||
| 746 | return result; | ||
| 747 | } | ||
| 748 | const auto struct_result = current_surface->MatchesStructure(params); | ||
| 749 | if (struct_result != MatchStructureResult::None && | ||
| 750 | current_surface->MatchTarget(params.target)) { | ||
| 751 | Deduction result{}; | ||
| 752 | result.type = DeductionType::DeductionComplete; | ||
| 753 | result.surface = current_surface; | ||
| 754 | return result; | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 758 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); | ||
| 759 | auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)}; | ||
| 760 | |||
| 761 | if (overlaps.empty()) { | ||
| 762 | Deduction result{}; | ||
| 763 | result.type = DeductionType::DeductionIncomplete; | ||
| 764 | return result; | ||
| 765 | } | ||
| 766 | |||
| 767 | if (overlaps.size() > 1) { | ||
| 768 | Deduction result{}; | ||
| 769 | result.type = DeductionType::DeductionFailed; | ||
| 770 | return result; | ||
| 771 | } else { | ||
| 772 | Deduction result{}; | ||
| 773 | result.type = DeductionType::DeductionComplete; | ||
| 774 | result.surface = overlaps[0]; | ||
| 775 | return result; | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | /** | ||
| 780 | * `DeduceBestBlit` gets the a source and destination starting address and parameters, | ||
| 781 | * and tries to deduce if they are supposed to be depth textures. If so, their | ||
| 782 | * parameters are modified and fixed into so. | ||
| 783 | * @param gpu_addr, the starting address of the candidate surface. | ||
| 784 | * @param params, the parameters on the candidate surface. | ||
| 785 | **/ | ||
| 786 | void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params, | ||
| 787 | const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) { | ||
| 788 | auto deduced_src = DeduceSurface(src_gpu_addr, src_params); | ||
| 789 | auto deduced_dst = DeduceSurface(src_gpu_addr, src_params); | ||
| 790 | if (deduced_src.Failed() || deduced_dst.Failed()) { | ||
| 791 | return; | ||
| 792 | } | ||
| 793 | |||
| 794 | const bool incomplete_src = deduced_src.Incomplete(); | ||
| 795 | const bool incomplete_dst = deduced_dst.Incomplete(); | ||
| 796 | |||
| 797 | if (incomplete_src && incomplete_dst) { | ||
| 798 | return; | ||
| 799 | } | ||
| 800 | |||
| 801 | const bool any_incomplete = incomplete_src || incomplete_dst; | ||
| 802 | |||
| 803 | if (!any_incomplete) { | ||
| 804 | if (!(deduced_src.IsDepth() && deduced_dst.IsDepth())) { | ||
| 805 | return; | ||
| 806 | } | ||
| 807 | } else { | ||
| 808 | if (incomplete_src && !(deduced_dst.IsDepth())) { | ||
| 809 | return; | ||
| 810 | } | ||
| 811 | |||
| 812 | if (incomplete_dst && !(deduced_src.IsDepth())) { | ||
| 813 | return; | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | const auto inherit_format = ([](SurfaceParams& to, TSurface from) { | ||
| 818 | const SurfaceParams& params = from->GetSurfaceParams(); | ||
| 819 | to.pixel_format = params.pixel_format; | ||
| 820 | to.component_type = params.component_type; | ||
| 821 | to.type = params.type; | ||
| 822 | }); | ||
| 823 | // Now we got the cases where one or both is Depth and the other is not known | ||
| 824 | if (!incomplete_src) { | ||
| 825 | inherit_format(src_params, deduced_src.surface); | ||
| 826 | } else { | ||
| 827 | inherit_format(src_params, deduced_dst.surface); | ||
| 828 | } | ||
| 829 | if (!incomplete_dst) { | ||
| 830 | inherit_format(dst_params, deduced_dst.surface); | ||
| 831 | } else { | ||
| 832 | inherit_format(dst_params, deduced_src.surface); | ||
| 833 | } | ||
| 834 | } | ||
| 835 | |||
| 694 | std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, | 836 | std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, |
| 695 | bool preserve_contents) { | 837 | bool preserve_contents) { |
| 696 | auto new_surface{GetUncachedSurface(gpu_addr, params)}; | 838 | auto new_surface{GetUncachedSurface(gpu_addr, params)}; |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index dc6fa07fc..ff1c1d985 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -66,6 +66,9 @@ add_executable(yuzu | |||
| 66 | configuration/configure_profile_manager.cpp | 66 | configuration/configure_profile_manager.cpp |
| 67 | configuration/configure_profile_manager.h | 67 | configuration/configure_profile_manager.h |
| 68 | configuration/configure_profile_manager.ui | 68 | configuration/configure_profile_manager.ui |
| 69 | configuration/configure_service.cpp | ||
| 70 | configuration/configure_service.h | ||
| 71 | configuration/configure_service.ui | ||
| 69 | configuration/configure_system.cpp | 72 | configuration/configure_system.cpp |
| 70 | configuration/configure_system.h | 73 | configuration/configure_system.h |
| 71 | configuration/configure_system.ui | 74 | configuration/configure_system.ui |
| @@ -186,6 +189,10 @@ if (YUZU_USE_QT_WEB_ENGINE) | |||
| 186 | target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) | 189 | target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) |
| 187 | endif () | 190 | endif () |
| 188 | 191 | ||
| 192 | if (YUZU_ENABLE_BOXCAT) | ||
| 193 | target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_BOXCAT) | ||
| 194 | endif () | ||
| 195 | |||
| 189 | if(UNIX AND NOT APPLE) | 196 | if(UNIX AND NOT APPLE) |
| 190 | install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | 197 | install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") |
| 191 | endif() | 198 | endif() |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 92d9fb161..f92a4b3c3 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -525,6 +525,17 @@ void Config::ReadDebuggingValues() { | |||
| 525 | qt_config->endGroup(); | 525 | qt_config->endGroup(); |
| 526 | } | 526 | } |
| 527 | 527 | ||
| 528 | void Config::ReadServiceValues() { | ||
| 529 | qt_config->beginGroup(QStringLiteral("Services")); | ||
| 530 | Settings::values.bcat_backend = | ||
| 531 | ReadSetting(QStringLiteral("bcat_backend"), QStringLiteral("boxcat")) | ||
| 532 | .toString() | ||
| 533 | .toStdString(); | ||
| 534 | Settings::values.bcat_boxcat_local = | ||
| 535 | ReadSetting(QStringLiteral("bcat_boxcat_local"), false).toBool(); | ||
| 536 | qt_config->endGroup(); | ||
| 537 | } | ||
| 538 | |||
| 528 | void Config::ReadDisabledAddOnValues() { | 539 | void Config::ReadDisabledAddOnValues() { |
| 529 | const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); | 540 | const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); |
| 530 | 541 | ||
| @@ -705,6 +716,8 @@ void Config::ReadUIValues() { | |||
| 705 | UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); | 716 | UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); |
| 706 | UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); | 717 | UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); |
| 707 | UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt(); | 718 | UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt(); |
| 719 | UISettings::values.pause_when_in_background = | ||
| 720 | ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool(); | ||
| 708 | 721 | ||
| 709 | ApplyDefaultProfileIfInputInvalid(); | 722 | ApplyDefaultProfileIfInputInvalid(); |
| 710 | 723 | ||
| @@ -769,6 +782,7 @@ void Config::ReadValues() { | |||
| 769 | ReadMiscellaneousValues(); | 782 | ReadMiscellaneousValues(); |
| 770 | ReadDebuggingValues(); | 783 | ReadDebuggingValues(); |
| 771 | ReadWebServiceValues(); | 784 | ReadWebServiceValues(); |
| 785 | ReadServiceValues(); | ||
| 772 | ReadDisabledAddOnValues(); | 786 | ReadDisabledAddOnValues(); |
| 773 | ReadUIValues(); | 787 | ReadUIValues(); |
| 774 | } | 788 | } |
| @@ -866,6 +880,7 @@ void Config::SaveValues() { | |||
| 866 | SaveMiscellaneousValues(); | 880 | SaveMiscellaneousValues(); |
| 867 | SaveDebuggingValues(); | 881 | SaveDebuggingValues(); |
| 868 | SaveWebServiceValues(); | 882 | SaveWebServiceValues(); |
| 883 | SaveServiceValues(); | ||
| 869 | SaveDisabledAddOnValues(); | 884 | SaveDisabledAddOnValues(); |
| 870 | SaveUIValues(); | 885 | SaveUIValues(); |
| 871 | } | 886 | } |
| @@ -963,6 +978,14 @@ void Config::SaveDebuggingValues() { | |||
| 963 | qt_config->endGroup(); | 978 | qt_config->endGroup(); |
| 964 | } | 979 | } |
| 965 | 980 | ||
| 981 | void Config::SaveServiceValues() { | ||
| 982 | qt_config->beginGroup(QStringLiteral("Services")); | ||
| 983 | WriteSetting(QStringLiteral("bcat_backend"), | ||
| 984 | QString::fromStdString(Settings::values.bcat_backend), QStringLiteral("null")); | ||
| 985 | WriteSetting(QStringLiteral("bcat_boxcat_local"), Settings::values.bcat_boxcat_local, false); | ||
| 986 | qt_config->endGroup(); | ||
| 987 | } | ||
| 988 | |||
| 966 | void Config::SaveDisabledAddOnValues() { | 989 | void Config::SaveDisabledAddOnValues() { |
| 967 | qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); | 990 | qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); |
| 968 | 991 | ||
| @@ -1103,6 +1126,8 @@ void Config::SaveUIValues() { | |||
| 1103 | WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); | 1126 | WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); |
| 1104 | WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); | 1127 | WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); |
| 1105 | WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0); | 1128 | WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0); |
| 1129 | WriteSetting(QStringLiteral("pauseWhenInBackground"), | ||
| 1130 | UISettings::values.pause_when_in_background, false); | ||
| 1106 | 1131 | ||
| 1107 | qt_config->endGroup(); | 1132 | qt_config->endGroup(); |
| 1108 | } | 1133 | } |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 6b523ecdd..ba6888004 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -42,6 +42,7 @@ private: | |||
| 42 | void ReadCoreValues(); | 42 | void ReadCoreValues(); |
| 43 | void ReadDataStorageValues(); | 43 | void ReadDataStorageValues(); |
| 44 | void ReadDebuggingValues(); | 44 | void ReadDebuggingValues(); |
| 45 | void ReadServiceValues(); | ||
| 45 | void ReadDisabledAddOnValues(); | 46 | void ReadDisabledAddOnValues(); |
| 46 | void ReadMiscellaneousValues(); | 47 | void ReadMiscellaneousValues(); |
| 47 | void ReadPathValues(); | 48 | void ReadPathValues(); |
| @@ -65,6 +66,7 @@ private: | |||
| 65 | void SaveCoreValues(); | 66 | void SaveCoreValues(); |
| 66 | void SaveDataStorageValues(); | 67 | void SaveDataStorageValues(); |
| 67 | void SaveDebuggingValues(); | 68 | void SaveDebuggingValues(); |
| 69 | void SaveServiceValues(); | ||
| 68 | void SaveDisabledAddOnValues(); | 70 | void SaveDisabledAddOnValues(); |
| 69 | void SaveMiscellaneousValues(); | 71 | void SaveMiscellaneousValues(); |
| 70 | void SavePathValues(); | 72 | void SavePathValues(); |
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 49fadd0ef..372427ae2 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui | |||
| @@ -98,6 +98,11 @@ | |||
| 98 | <string>Web</string> | 98 | <string>Web</string> |
| 99 | </attribute> | 99 | </attribute> |
| 100 | </widget> | 100 | </widget> |
| 101 | <widget class="ConfigureService" name="serviceTab"> | ||
| 102 | <attribute name="title"> | ||
| 103 | <string>Services</string> | ||
| 104 | </attribute> | ||
| 105 | </widget> | ||
| 101 | </widget> | 106 | </widget> |
| 102 | </item> | 107 | </item> |
| 103 | </layout> | 108 | </layout> |
| @@ -178,6 +183,12 @@ | |||
| 178 | <header>configuration/configure_hotkeys.h</header> | 183 | <header>configuration/configure_hotkeys.h</header> |
| 179 | <container>1</container> | 184 | <container>1</container> |
| 180 | </customwidget> | 185 | </customwidget> |
| 186 | <customwidget> | ||
| 187 | <class>ConfigureService</class> | ||
| 188 | <extends>QWidget</extends> | ||
| 189 | <header>configuration/configure_service.h</header> | ||
| 190 | <container>1</container> | ||
| 191 | </customwidget> | ||
| 181 | </customwidgets> | 192 | </customwidgets> |
| 182 | <resources/> | 193 | <resources/> |
| 183 | <connections> | 194 | <connections> |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 7c875ae87..25b2e1b05 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -44,6 +44,7 @@ void ConfigureDialog::ApplyConfiguration() { | |||
| 44 | ui->audioTab->ApplyConfiguration(); | 44 | ui->audioTab->ApplyConfiguration(); |
| 45 | ui->debugTab->ApplyConfiguration(); | 45 | ui->debugTab->ApplyConfiguration(); |
| 46 | ui->webTab->ApplyConfiguration(); | 46 | ui->webTab->ApplyConfiguration(); |
| 47 | ui->serviceTab->ApplyConfiguration(); | ||
| 47 | Settings::Apply(); | 48 | Settings::Apply(); |
| 48 | Settings::LogSettings(); | 49 | Settings::LogSettings(); |
| 49 | } | 50 | } |
| @@ -74,7 +75,8 @@ Q_DECLARE_METATYPE(QList<QWidget*>); | |||
| 74 | void ConfigureDialog::PopulateSelectionList() { | 75 | void ConfigureDialog::PopulateSelectionList() { |
| 75 | const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ | 76 | const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ |
| 76 | {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, | 77 | {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, |
| 77 | {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}}, | 78 | {tr("System"), |
| 79 | {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab, ui->audioTab}}, | ||
| 78 | {tr("Graphics"), {ui->graphicsTab}}, | 80 | {tr("Graphics"), {ui->graphicsTab}}, |
| 79 | {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, | 81 | {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, |
| 80 | }; | 82 | }; |
| @@ -108,6 +110,7 @@ void ConfigureDialog::UpdateVisibleTabs() { | |||
| 108 | {ui->webTab, tr("Web")}, | 110 | {ui->webTab, tr("Web")}, |
| 109 | {ui->gameListTab, tr("Game List")}, | 111 | {ui->gameListTab, tr("Game List")}, |
| 110 | {ui->filesystemTab, tr("Filesystem")}, | 112 | {ui->filesystemTab, tr("Filesystem")}, |
| 113 | {ui->serviceTab, tr("Services")}, | ||
| 111 | }; | 114 | }; |
| 112 | 115 | ||
| 113 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); | 116 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 98bc9b391..34e1d7fea 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -31,6 +31,7 @@ void ConfigureGeneral::SetConfiguration() { | |||
| 31 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 31 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 32 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 32 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 33 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 33 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 34 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); | ||
| 34 | 35 | ||
| 35 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | 36 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); |
| 36 | ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); | 37 | ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); |
| @@ -42,6 +43,7 @@ void ConfigureGeneral::ApplyConfiguration() { | |||
| 42 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 43 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 43 | UISettings::values.theme = | 44 | UISettings::values.theme = |
| 44 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | 45 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); |
| 46 | UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); | ||
| 45 | 47 | ||
| 46 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | 48 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); |
| 47 | Settings::values.frame_limit = ui->frame_limit->value(); | 49 | Settings::values.frame_limit = ui->frame_limit->value(); |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 0bb91d64b..26b3486ff 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -65,6 +65,13 @@ | |||
| 65 | </property> | 65 | </property> |
| 66 | </widget> | 66 | </widget> |
| 67 | </item> | 67 | </item> |
| 68 | <item> | ||
| 69 | <widget class="QCheckBox" name="toggle_background_pause"> | ||
| 70 | <property name="text"> | ||
| 71 | <string>Pause emulation when in background</string> | ||
| 72 | </property> | ||
| 73 | </widget> | ||
| 74 | </item> | ||
| 68 | </layout> | 75 | </layout> |
| 69 | </item> | 76 | </item> |
| 70 | </layout> | 77 | </layout> |
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp new file mode 100644 index 000000000..06566e981 --- /dev/null +++ b/src/yuzu/configuration/configure_service.cpp | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QGraphicsItem> | ||
| 6 | #include <QtConcurrent/QtConcurrent> | ||
| 7 | #include "core/hle/service/bcat/backend/boxcat.h" | ||
| 8 | #include "core/settings.h" | ||
| 9 | #include "ui_configure_service.h" | ||
| 10 | #include "yuzu/configuration/configure_service.h" | ||
| 11 | |||
| 12 | namespace { | ||
| 13 | QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { | ||
| 14 | QString out; | ||
| 15 | |||
| 16 | if (status.header.has_value()) { | ||
| 17 | out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.header)); | ||
| 18 | } | ||
| 19 | |||
| 20 | if (status.events.size() == 1) { | ||
| 21 | out += QStringLiteral("%1<br>").arg(QString::fromStdString(status.events.front())); | ||
| 22 | } else { | ||
| 23 | for (const auto& event : status.events) { | ||
| 24 | out += QStringLiteral("- %1<br>").arg(QString::fromStdString(event)); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | if (status.footer.has_value()) { | ||
| 29 | out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.footer)); | ||
| 30 | } | ||
| 31 | |||
| 32 | return out; | ||
| 33 | } | ||
| 34 | } // Anonymous namespace | ||
| 35 | |||
| 36 | ConfigureService::ConfigureService(QWidget* parent) | ||
| 37 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { | ||
| 38 | ui->setupUi(this); | ||
| 39 | |||
| 40 | ui->bcat_source->addItem(QStringLiteral("None")); | ||
| 41 | ui->bcat_empty_label->setHidden(true); | ||
| 42 | ui->bcat_empty_header->setHidden(true); | ||
| 43 | |||
| 44 | #ifdef YUZU_ENABLE_BOXCAT | ||
| 45 | ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat")); | ||
| 46 | #endif | ||
| 47 | |||
| 48 | connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | ||
| 49 | &ConfigureService::OnBCATImplChanged); | ||
| 50 | |||
| 51 | this->SetConfiguration(); | ||
| 52 | } | ||
| 53 | |||
| 54 | ConfigureService::~ConfigureService() = default; | ||
| 55 | |||
| 56 | void ConfigureService::ApplyConfiguration() { | ||
| 57 | Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); | ||
| 58 | } | ||
| 59 | |||
| 60 | void ConfigureService::RetranslateUi() { | ||
| 61 | ui->retranslateUi(this); | ||
| 62 | } | ||
| 63 | |||
| 64 | void ConfigureService::SetConfiguration() { | ||
| 65 | const int index = | ||
| 66 | ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend)); | ||
| 67 | ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); | ||
| 68 | } | ||
| 69 | |||
| 70 | std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { | ||
| 71 | std::optional<std::string> global; | ||
| 72 | std::map<std::string, Service::BCAT::EventStatus> map; | ||
| 73 | const auto res = Service::BCAT::Boxcat::GetStatus(global, map); | ||
| 74 | |||
| 75 | switch (res) { | ||
| 76 | case Service::BCAT::Boxcat::StatusResult::Success: | ||
| 77 | break; | ||
| 78 | case Service::BCAT::Boxcat::StatusResult::Offline: | ||
| 79 | return {QString{}, | ||
| 80 | tr("The boxcat service is offline or you are not connected to the internet.")}; | ||
| 81 | case Service::BCAT::Boxcat::StatusResult::ParseError: | ||
| 82 | return {QString{}, | ||
| 83 | tr("There was an error while processing the boxcat event data. Contact the yuzu " | ||
| 84 | "developers.")}; | ||
| 85 | case Service::BCAT::Boxcat::StatusResult::BadClientVersion: | ||
| 86 | return {QString{}, | ||
| 87 | tr("The version of yuzu you are using is either too new or too old for the server. " | ||
| 88 | "Try updating to the latest official release of yuzu.")}; | ||
| 89 | } | ||
| 90 | |||
| 91 | if (map.empty()) { | ||
| 92 | return {QStringLiteral("Current Boxcat Events"), | ||
| 93 | tr("There are currently no events on boxcat.")}; | ||
| 94 | } | ||
| 95 | |||
| 96 | QString out; | ||
| 97 | |||
| 98 | if (global.has_value()) { | ||
| 99 | out += QStringLiteral("%1<br>").arg(QString::fromStdString(*global)); | ||
| 100 | } | ||
| 101 | |||
| 102 | for (const auto& [key, value] : map) { | ||
| 103 | out += QStringLiteral("%1<b>%2</b><br>%3") | ||
| 104 | .arg(out.isEmpty() ? QString{} : QStringLiteral("<br>")) | ||
| 105 | .arg(QString::fromStdString(key)) | ||
| 106 | .arg(FormatEventStatusString(value)); | ||
| 107 | } | ||
| 108 | return {QStringLiteral("Current Boxcat Events"), std::move(out)}; | ||
| 109 | } | ||
| 110 | |||
| 111 | void ConfigureService::OnBCATImplChanged() { | ||
| 112 | #ifdef YUZU_ENABLE_BOXCAT | ||
| 113 | const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); | ||
| 114 | ui->bcat_empty_header->setHidden(!boxcat); | ||
| 115 | ui->bcat_empty_label->setHidden(!boxcat); | ||
| 116 | ui->bcat_empty_header->setText(QString{}); | ||
| 117 | ui->bcat_empty_label->setText(tr("Yuzu is retrieving the latest boxcat status...")); | ||
| 118 | |||
| 119 | if (!boxcat) | ||
| 120 | return; | ||
| 121 | |||
| 122 | const auto future = QtConcurrent::run([this] { return BCATDownloadEvents(); }); | ||
| 123 | |||
| 124 | watcher.setFuture(future); | ||
| 125 | connect(&watcher, &QFutureWatcher<std::pair<QString, QString>>::finished, this, | ||
| 126 | [this] { OnUpdateBCATEmptyLabel(watcher.result()); }); | ||
| 127 | #endif | ||
| 128 | } | ||
| 129 | |||
| 130 | void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { | ||
| 131 | #ifdef YUZU_ENABLE_BOXCAT | ||
| 132 | const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); | ||
| 133 | if (boxcat) { | ||
| 134 | ui->bcat_empty_header->setText(string.first); | ||
| 135 | ui->bcat_empty_label->setText(string.second); | ||
| 136 | } | ||
| 137 | #endif | ||
| 138 | } | ||
diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_service.h new file mode 100644 index 000000000..f5c1b703a --- /dev/null +++ b/src/yuzu/configuration/configure_service.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2019 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 <QFutureWatcher> | ||
| 9 | #include <QWidget> | ||
| 10 | |||
| 11 | namespace Ui { | ||
| 12 | class ConfigureService; | ||
| 13 | } | ||
| 14 | |||
| 15 | class ConfigureService : public QWidget { | ||
| 16 | Q_OBJECT | ||
| 17 | |||
| 18 | public: | ||
| 19 | explicit ConfigureService(QWidget* parent = nullptr); | ||
| 20 | ~ConfigureService() override; | ||
| 21 | |||
| 22 | void ApplyConfiguration(); | ||
| 23 | void RetranslateUi(); | ||
| 24 | |||
| 25 | private: | ||
| 26 | void SetConfiguration(); | ||
| 27 | |||
| 28 | std::pair<QString, QString> BCATDownloadEvents(); | ||
| 29 | void OnBCATImplChanged(); | ||
| 30 | void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); | ||
| 31 | |||
| 32 | std::unique_ptr<Ui::ConfigureService> ui; | ||
| 33 | QFutureWatcher<std::pair<QString, QString>> watcher{this}; | ||
| 34 | }; | ||
diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_service.ui new file mode 100644 index 000000000..9668dd557 --- /dev/null +++ b/src/yuzu/configuration/configure_service.ui | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureService</class> | ||
| 4 | <widget class="QWidget" name="ConfigureService"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>433</width> | ||
| 10 | <height>561</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Form</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 17 | <item> | ||
| 18 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="groupBox"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>BCAT</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QGridLayout" name="gridLayout"> | ||
| 25 | <item row="1" column="1" colspan="2"> | ||
| 26 | <widget class="QLabel" name="label_2"> | ||
| 27 | <property name="maximumSize"> | ||
| 28 | <size> | ||
| 29 | <width>260</width> | ||
| 30 | <height>16777215</height> | ||
| 31 | </size> | ||
| 32 | </property> | ||
| 33 | <property name="text"> | ||
| 34 | <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> | ||
| 35 | </property> | ||
| 36 | <property name="wordWrap"> | ||
| 37 | <bool>true</bool> | ||
| 38 | </property> | ||
| 39 | </widget> | ||
| 40 | </item> | ||
| 41 | <item row="0" column="0"> | ||
| 42 | <widget class="QLabel" name="label"> | ||
| 43 | <property name="maximumSize"> | ||
| 44 | <size> | ||
| 45 | <width>16777215</width> | ||
| 46 | <height>16777215</height> | ||
| 47 | </size> | ||
| 48 | </property> | ||
| 49 | <property name="text"> | ||
| 50 | <string>BCAT Backend</string> | ||
| 51 | </property> | ||
| 52 | </widget> | ||
| 53 | </item> | ||
| 54 | <item row="3" column="1" colspan="2"> | ||
| 55 | <widget class="QLabel" name="bcat_empty_label"> | ||
| 56 | <property name="enabled"> | ||
| 57 | <bool>true</bool> | ||
| 58 | </property> | ||
| 59 | <property name="maximumSize"> | ||
| 60 | <size> | ||
| 61 | <width>260</width> | ||
| 62 | <height>16777215</height> | ||
| 63 | </size> | ||
| 64 | </property> | ||
| 65 | <property name="text"> | ||
| 66 | <string/> | ||
| 67 | </property> | ||
| 68 | <property name="alignment"> | ||
| 69 | <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> | ||
| 70 | </property> | ||
| 71 | <property name="wordWrap"> | ||
| 72 | <bool>true</bool> | ||
| 73 | </property> | ||
| 74 | </widget> | ||
| 75 | </item> | ||
| 76 | <item row="2" column="1" colspan="2"> | ||
| 77 | <widget class="QLabel" name="label_3"> | ||
| 78 | <property name="text"> | ||
| 79 | <string><html><head/><body><p><a href="https://yuzu-emu.org/help/feature/boxcat"><span style=" text-decoration: underline; color:#0000ff;">Learn more about BCAT, Boxcat, and Current Events</span></a></p></body></html></string> | ||
| 80 | </property> | ||
| 81 | <property name="openExternalLinks"> | ||
| 82 | <bool>true</bool> | ||
| 83 | </property> | ||
| 84 | </widget> | ||
| 85 | </item> | ||
| 86 | <item row="0" column="1" colspan="2"> | ||
| 87 | <widget class="QComboBox" name="bcat_source"/> | ||
| 88 | </item> | ||
| 89 | <item row="3" column="0"> | ||
| 90 | <widget class="QLabel" name="bcat_empty_header"> | ||
| 91 | <property name="text"> | ||
| 92 | <string/> | ||
| 93 | </property> | ||
| 94 | <property name="alignment"> | ||
| 95 | <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> | ||
| 96 | </property> | ||
| 97 | <property name="wordWrap"> | ||
| 98 | <bool>true</bool> | ||
| 99 | </property> | ||
| 100 | </widget> | ||
| 101 | </item> | ||
| 102 | </layout> | ||
| 103 | </widget> | ||
| 104 | </item> | ||
| 105 | </layout> | ||
| 106 | </item> | ||
| 107 | <item> | ||
| 108 | <spacer name="verticalSpacer"> | ||
| 109 | <property name="orientation"> | ||
| 110 | <enum>Qt::Vertical</enum> | ||
| 111 | </property> | ||
| 112 | <property name="sizeHint" stdset="0"> | ||
| 113 | <size> | ||
| 114 | <width>20</width> | ||
| 115 | <height>40</height> | ||
| 116 | </size> | ||
| 117 | </property> | ||
| 118 | </spacer> | ||
| 119 | </item> | ||
| 120 | </layout> | ||
| 121 | </widget> | ||
| 122 | <resources/> | ||
| 123 | <connections/> | ||
| 124 | </ui> | ||
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index d5fab2f1f..a2b88c787 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -172,9 +172,7 @@ void GameList::onTextChanged(const QString& new_text) { | |||
| 172 | const int folder_count = tree_view->model()->rowCount(); | 172 | const int folder_count = tree_view->model()->rowCount(); |
| 173 | QString edit_filter_text = new_text.toLower(); | 173 | QString edit_filter_text = new_text.toLower(); |
| 174 | QStandardItem* folder; | 174 | QStandardItem* folder; |
| 175 | QStandardItem* child; | ||
| 176 | int children_total = 0; | 175 | int children_total = 0; |
| 177 | QModelIndex root_index = item_model->invisibleRootItem()->index(); | ||
| 178 | 176 | ||
| 179 | // If the searchfield is empty every item is visible | 177 | // If the searchfield is empty every item is visible |
| 180 | // Otherwise the filter gets applied | 178 | // Otherwise the filter gets applied |
| @@ -272,6 +270,8 @@ void GameList::onUpdateThemedIcons() { | |||
| 272 | .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | 270 | .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), |
| 273 | Qt::DecorationRole); | 271 | Qt::DecorationRole); |
| 274 | break; | 272 | break; |
| 273 | default: | ||
| 274 | break; | ||
| 275 | } | 275 | } |
| 276 | } | 276 | } |
| 277 | } | 277 | } |
| @@ -392,6 +392,8 @@ void GameList::ValidateEntry(const QModelIndex& item) { | |||
| 392 | case GameListItemType::AddDir: | 392 | case GameListItemType::AddDir: |
| 393 | emit AddDirectory(); | 393 | emit AddDirectory(); |
| 394 | break; | 394 | break; |
| 395 | default: | ||
| 396 | break; | ||
| 395 | } | 397 | } |
| 396 | } | 398 | } |
| 397 | 399 | ||
| @@ -462,6 +464,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 462 | case GameListItemType::SysNandDir: | 464 | case GameListItemType::SysNandDir: |
| 463 | AddPermDirPopup(context_menu, selected); | 465 | AddPermDirPopup(context_menu, selected); |
| 464 | break; | 466 | break; |
| 467 | default: | ||
| 468 | break; | ||
| 465 | } | 469 | } |
| 466 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); | 470 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); |
| 467 | } | 471 | } |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index a8d888fee..1c2b37afd 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -247,7 +247,7 @@ public: | |||
| 247 | Qt::DecorationRole); | 247 | Qt::DecorationRole); |
| 248 | setData(QObject::tr("System Titles"), Qt::DisplayRole); | 248 | setData(QObject::tr("System Titles"), Qt::DisplayRole); |
| 249 | break; | 249 | break; |
| 250 | case GameListItemType::CustomDir: | 250 | case GameListItemType::CustomDir: { |
| 251 | const QString icon_name = QFileInfo::exists(game_dir->path) | 251 | const QString icon_name = QFileInfo::exists(game_dir->path) |
| 252 | ? QStringLiteral("folder") | 252 | ? QStringLiteral("folder") |
| 253 | : QStringLiteral("bad_folder"); | 253 | : QStringLiteral("bad_folder"); |
| @@ -256,8 +256,11 @@ public: | |||
| 256 | Qt::DecorationRole); | 256 | Qt::DecorationRole); |
| 257 | setData(game_dir->path, Qt::DisplayRole); | 257 | setData(game_dir->path, Qt::DisplayRole); |
| 258 | break; | 258 | break; |
| 259 | }; | 259 | } |
| 260 | }; | 260 | default: |
| 261 | break; | ||
| 262 | } | ||
| 263 | } | ||
| 261 | 264 | ||
| 262 | int type() const override { | 265 | int type() const override { |
| 263 | return static_cast<int>(dir_type); | 266 | return static_cast<int>(dir_type); |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index fd21a9761..4c81ef12b 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -326,10 +326,10 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa | |||
| 326 | } | 326 | } |
| 327 | } else { | 327 | } else { |
| 328 | std::vector<u8> icon; | 328 | std::vector<u8> icon; |
| 329 | const auto res1 = loader->ReadIcon(icon); | 329 | [[maybe_unused]] const auto res1 = loader->ReadIcon(icon); |
| 330 | 330 | ||
| 331 | std::string name = " "; | 331 | std::string name = " "; |
| 332 | const auto res3 = loader->ReadTitle(name); | 332 | [[maybe_unused]] const auto res3 = loader->ReadTitle(name); |
| 333 | 333 | ||
| 334 | const FileSys::PatchManager patch{program_id}; | 334 | const FileSys::PatchManager patch{program_id}; |
| 335 | 335 | ||
| @@ -354,20 +354,20 @@ void GameListWorker::run() { | |||
| 354 | for (UISettings::GameDir& game_dir : game_dirs) { | 354 | for (UISettings::GameDir& game_dir : game_dirs) { |
| 355 | if (game_dir.path == QStringLiteral("SDMC")) { | 355 | if (game_dir.path == QStringLiteral("SDMC")) { |
| 356 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); | 356 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); |
| 357 | emit DirEntryReady({game_list_dir}); | 357 | emit DirEntryReady(game_list_dir); |
| 358 | AddTitlesToGameList(game_list_dir); | 358 | AddTitlesToGameList(game_list_dir); |
| 359 | } else if (game_dir.path == QStringLiteral("UserNAND")) { | 359 | } else if (game_dir.path == QStringLiteral("UserNAND")) { |
| 360 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); | 360 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); |
| 361 | emit DirEntryReady({game_list_dir}); | 361 | emit DirEntryReady(game_list_dir); |
| 362 | AddTitlesToGameList(game_list_dir); | 362 | AddTitlesToGameList(game_list_dir); |
| 363 | } else if (game_dir.path == QStringLiteral("SysNAND")) { | 363 | } else if (game_dir.path == QStringLiteral("SysNAND")) { |
| 364 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); | 364 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); |
| 365 | emit DirEntryReady({game_list_dir}); | 365 | emit DirEntryReady(game_list_dir); |
| 366 | AddTitlesToGameList(game_list_dir); | 366 | AddTitlesToGameList(game_list_dir); |
| 367 | } else { | 367 | } else { |
| 368 | watch_list.append(game_dir.path); | 368 | watch_list.append(game_dir.path); |
| 369 | auto* const game_list_dir = new GameListDir(game_dir); | 369 | auto* const game_list_dir = new GameListDir(game_dir); |
| 370 | emit DirEntryReady({game_list_dir}); | 370 | emit DirEntryReady(game_list_dir); |
| 371 | provider->ClearAllEntries(); | 371 | provider->ClearAllEntries(); |
| 372 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 2, | 372 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 2, |
| 373 | game_list_dir); | 373 | game_list_dir); |
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 6e52fca89..84e4e1b42 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h | |||
| @@ -75,8 +75,9 @@ private: | |||
| 75 | 75 | ||
| 76 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 76 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 77 | FileSys::ManualContentProvider* provider; | 77 | FileSys::ManualContentProvider* provider; |
| 78 | QStringList watch_list; | ||
| 79 | const CompatibilityList& compatibility_list; | ||
| 80 | QVector<UISettings::GameDir>& game_dirs; | 78 | QVector<UISettings::GameDir>& game_dirs; |
| 79 | const CompatibilityList& compatibility_list; | ||
| 80 | |||
| 81 | QStringList watch_list; | ||
| 81 | std::atomic_bool stop_processing; | 82 | std::atomic_bool stop_processing; |
| 82 | }; | 83 | }; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 2d82df739..d6bb18d24 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -675,6 +675,24 @@ void GMainWindow::RestoreUIState() { | |||
| 675 | Debugger::ToggleConsole(); | 675 | Debugger::ToggleConsole(); |
| 676 | } | 676 | } |
| 677 | 677 | ||
| 678 | void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { | ||
| 679 | if (!UISettings::values.pause_when_in_background) { | ||
| 680 | return; | ||
| 681 | } | ||
| 682 | if (state != Qt::ApplicationHidden && state != Qt::ApplicationInactive && | ||
| 683 | state != Qt::ApplicationActive) { | ||
| 684 | LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); | ||
| 685 | } | ||
| 686 | if (ui.action_Pause->isEnabled() && | ||
| 687 | (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { | ||
| 688 | auto_paused = true; | ||
| 689 | OnPauseGame(); | ||
| 690 | } else if (ui.action_Start->isEnabled() && auto_paused && state == Qt::ApplicationActive) { | ||
| 691 | auto_paused = false; | ||
| 692 | OnStartGame(); | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 678 | void GMainWindow::ConnectWidgetEvents() { | 696 | void GMainWindow::ConnectWidgetEvents() { |
| 679 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); | 697 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); |
| 680 | connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory); | 698 | connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory); |
| @@ -1889,15 +1907,24 @@ void GMainWindow::OnCaptureScreenshot() { | |||
| 1889 | } | 1907 | } |
| 1890 | 1908 | ||
| 1891 | void GMainWindow::UpdateWindowTitle(const QString& title_name) { | 1909 | void GMainWindow::UpdateWindowTitle(const QString& title_name) { |
| 1892 | const QString full_name = QString::fromUtf8(Common::g_build_fullname); | 1910 | const auto full_name = std::string(Common::g_build_fullname); |
| 1893 | const QString branch_name = QString::fromUtf8(Common::g_scm_branch); | 1911 | const auto branch_name = std::string(Common::g_scm_branch); |
| 1894 | const QString description = QString::fromUtf8(Common::g_scm_desc); | 1912 | const auto description = std::string(Common::g_scm_desc); |
| 1913 | const auto build_id = std::string(Common::g_build_id); | ||
| 1914 | |||
| 1915 | const auto date = | ||
| 1916 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); | ||
| 1895 | 1917 | ||
| 1896 | if (title_name.isEmpty()) { | 1918 | if (title_name.isEmpty()) { |
| 1897 | setWindowTitle(QStringLiteral("yuzu %1| %2-%3").arg(full_name, branch_name, description)); | 1919 | const auto fmt = std::string(Common::g_title_bar_format_idle); |
| 1920 | setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, | ||
| 1921 | full_name, branch_name, description, | ||
| 1922 | std::string{}, date, build_id))); | ||
| 1898 | } else { | 1923 | } else { |
| 1899 | setWindowTitle(QStringLiteral("yuzu %1| %4 | %2-%3") | 1924 | const auto fmt = std::string(Common::g_title_bar_format_running); |
| 1900 | .arg(full_name, branch_name, description, title_name)); | 1925 | setWindowTitle(QString::fromStdString( |
| 1926 | fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name, | ||
| 1927 | description, title_name.toStdString(), date, build_id))); | ||
| 1901 | } | 1928 | } |
| 1902 | } | 1929 | } |
| 1903 | 1930 | ||
| @@ -2311,6 +2338,9 @@ int main(int argc, char* argv[]) { | |||
| 2311 | // After settings have been loaded by GMainWindow, apply the filter | 2338 | // After settings have been loaded by GMainWindow, apply the filter |
| 2312 | main_window.show(); | 2339 | main_window.show(); |
| 2313 | 2340 | ||
| 2341 | QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, | ||
| 2342 | &GMainWindow::OnAppFocusStateChanged); | ||
| 2343 | |||
| 2314 | Settings::LogSettings(); | 2344 | Settings::LogSettings(); |
| 2315 | 2345 | ||
| 2316 | int result = app.exec(); | 2346 | int result = app.exec(); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e942d1248..fd4b9ccf5 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -119,6 +119,7 @@ public slots: | |||
| 119 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); | 119 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); |
| 120 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); | 120 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); |
| 121 | void WebBrowserOpenPage(std::string_view filename, std::string_view arguments); | 121 | void WebBrowserOpenPage(std::string_view filename, std::string_view arguments); |
| 122 | void OnAppFocusStateChanged(Qt::ApplicationState state); | ||
| 122 | 123 | ||
| 123 | private: | 124 | private: |
| 124 | void InitializeWidgets(); | 125 | void InitializeWidgets(); |
| @@ -244,6 +245,8 @@ private: | |||
| 244 | // The path to the game currently running | 245 | // The path to the game currently running |
| 245 | QString game_path; | 246 | QString game_path; |
| 246 | 247 | ||
| 248 | bool auto_paused = false; | ||
| 249 | |||
| 247 | // FS | 250 | // FS |
| 248 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 251 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 249 | std::unique_ptr<FileSys::ManualContentProvider> provider; | 252 | std::unique_ptr<FileSys::ManualContentProvider> provider; |
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp index 7f7d247a3..43bad9678 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/yuzu/uisettings.cpp | |||
| @@ -9,6 +9,8 @@ namespace UISettings { | |||
| 9 | const Themes themes{{ | 9 | const Themes themes{{ |
| 10 | {"Default", "default"}, | 10 | {"Default", "default"}, |
| 11 | {"Dark", "qdarkstyle"}, | 11 | {"Dark", "qdarkstyle"}, |
| 12 | {"Colorful", "colorful"}, | ||
| 13 | {"Colorful Dark", "colorful_dark"}, | ||
| 12 | }}; | 14 | }}; |
| 13 | 15 | ||
| 14 | Values values = {}; | 16 | Values values = {}; |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index c57290006..bc7725a01 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -24,7 +24,7 @@ struct Shortcut { | |||
| 24 | ContextualShortcut shortcut; | 24 | ContextualShortcut shortcut; |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | using Themes = std::array<std::pair<const char*, const char*>, 2>; | 27 | using Themes = std::array<std::pair<const char*, const char*>, 4>; |
| 28 | extern const Themes themes; | 28 | extern const Themes themes; |
| 29 | 29 | ||
| 30 | struct GameDir { | 30 | struct GameDir { |
| @@ -58,6 +58,7 @@ struct Values { | |||
| 58 | 58 | ||
| 59 | bool confirm_before_closing; | 59 | bool confirm_before_closing; |
| 60 | bool first_start; | 60 | bool first_start; |
| 61 | bool pause_when_in_background; | ||
| 61 | 62 | ||
| 62 | bool select_user_on_boot; | 63 | bool select_user_on_boot; |
| 63 | 64 | ||
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index d82438502..1a812cb87 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -433,6 +433,11 @@ void Config::ReadValues() { | |||
| 433 | sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org"); | 433 | sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org"); |
| 434 | Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", ""); | 434 | Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", ""); |
| 435 | Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", ""); | 435 | Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", ""); |
| 436 | |||
| 437 | // Services | ||
| 438 | Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "boxcat"); | ||
| 439 | Settings::values.bcat_boxcat_local = | ||
| 440 | sdl2_config->GetBoolean("Services", "bcat_boxcat_local", false); | ||
| 436 | } | 441 | } |
| 437 | 442 | ||
| 438 | void Config::Reload() { | 443 | void Config::Reload() { |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index a6171c3ed..8d18a4a5a 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -251,6 +251,11 @@ web_api_url = https://api.yuzu-emu.org | |||
| 251 | yuzu_username = | 251 | yuzu_username = |
| 252 | yuzu_token = | 252 | yuzu_token = |
| 253 | 253 | ||
| 254 | [Services] | ||
| 255 | # The name of the backend to use for BCAT | ||
| 256 | # If this is set to 'boxcat' boxcat will be used, otherwise a null implementation will be used | ||
| 257 | bcat_backend = | ||
| 258 | |||
| 254 | [AddOns] | 259 | [AddOns] |
| 255 | # Used to disable add-ons | 260 | # Used to disable add-ons |
| 256 | # List of title IDs of games that will have add-ons disabled (separated by '|'): | 261 | # List of title IDs of games that will have add-ons disabled (separated by '|'): |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index a6edc089a..b1c512db1 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #include <SDL.h> | 5 | #include <SDL.h> |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "common/scm_rev.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/perf_stats.h" | ||
| 7 | #include "input_common/keyboard.h" | 10 | #include "input_common/keyboard.h" |
| 8 | #include "input_common/main.h" | 11 | #include "input_common/main.h" |
| 9 | #include "input_common/motion_emu.h" | 12 | #include "input_common/motion_emu.h" |
| @@ -170,6 +173,16 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 170 | break; | 173 | break; |
| 171 | } | 174 | } |
| 172 | } | 175 | } |
| 176 | |||
| 177 | const u32 current_time = SDL_GetTicks(); | ||
| 178 | if (current_time > last_time + 2000) { | ||
| 179 | const auto results = Core::System::GetInstance().GetAndResetPerfStats(); | ||
| 180 | const auto title = fmt::format( | ||
| 181 | "yuzu {} | {}-{} | FPS: {:.0f} ({:.0%})", Common::g_build_fullname, | ||
| 182 | Common::g_scm_branch, Common::g_scm_desc, results.game_fps, results.emulation_speed); | ||
| 183 | SDL_SetWindowTitle(render_window, title.c_str()); | ||
| 184 | last_time = current_time; | ||
| 185 | } | ||
| 173 | } | 186 | } |
| 174 | 187 | ||
| 175 | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) { | 188 | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) { |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index d8051ebdf..eaa971f77 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -60,4 +60,7 @@ protected: | |||
| 60 | 60 | ||
| 61 | /// Internal SDL2 render window | 61 | /// Internal SDL2 render window |
| 62 | SDL_Window* render_window; | 62 | SDL_Window* render_window; |
| 63 | |||
| 64 | /// Keeps track of how often to update the title bar during gameplay | ||
| 65 | u32 last_time = 0; | ||
| 63 | }; | 66 | }; |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index bac05b959..3ee088a91 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -186,8 +186,6 @@ int main(int argc, char** argv) { | |||
| 186 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 186 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 187 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | 187 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |
| 188 | 188 | ||
| 189 | SCOPE_EXIT({ system.Shutdown(); }); | ||
| 190 | |||
| 191 | const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)}; | 189 | const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)}; |
| 192 | 190 | ||
| 193 | switch (load_result) { | 191 | switch (load_result) { |
| @@ -227,6 +225,8 @@ int main(int argc, char** argv) { | |||
| 227 | system.RunLoop(); | 225 | system.RunLoop(); |
| 228 | } | 226 | } |
| 229 | 227 | ||
| 228 | system.Shutdown(); | ||
| 229 | |||
| 230 | detached_tasks.WaitForAllTasks(); | 230 | detached_tasks.WaitForAllTasks(); |
| 231 | return 0; | 231 | return 0; |
| 232 | } | 232 | } |
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 9a11dc6c3..84ab4d687 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp | |||
| @@ -93,7 +93,6 @@ void Config::ReadValues() { | |||
| 93 | 93 | ||
| 94 | // System | 94 | // System |
| 95 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | 95 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); |
| 96 | const auto size = sdl2_config->GetInteger("System", "users_size", 0); | ||
| 97 | 96 | ||
| 98 | Settings::values.current_user = std::clamp<int>( | 97 | Settings::values.current_user = std::clamp<int>( |
| 99 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); | 98 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); |