diff options
211 files changed, 6511 insertions, 2815 deletions
diff --git a/.gitmodules b/.gitmodules index 2558a5ebc..3a49c4874 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -40,3 +40,9 @@ | |||
| 40 | [submodule "Vulkan-Headers"] | 40 | [submodule "Vulkan-Headers"] |
| 41 | path = externals/Vulkan-Headers | 41 | path = externals/Vulkan-Headers |
| 42 | url = https://github.com/KhronosGroup/Vulkan-Headers.git | 42 | url = https://github.com/KhronosGroup/Vulkan-Headers.git |
| 43 | [submodule "externals/zstd"] | ||
| 44 | path = externals/zstd | ||
| 45 | url = https://github.com/facebook/zstd | ||
| 46 | [submodule "sirit"] | ||
| 47 | path = externals/sirit | ||
| 48 | url = https://github.com/ReinUsesLisp/sirit | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index ab18275d3..6a417017c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -309,7 +309,7 @@ if (CLANG_FORMAT) | |||
| 309 | set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") | 309 | set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") |
| 310 | if (WIN32) | 310 | if (WIN32) |
| 311 | add_custom_target(clang-format | 311 | add_custom_target(clang-format |
| 312 | COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}" | 312 | COMMAND powershell.exe -Command "Get-ChildItem '${SRCS}/*' -Include *.cpp,*.h -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}" |
| 313 | COMMENT ${CCOMMENT}) | 313 | COMMENT ${CCOMMENT}) |
| 314 | elseif(MINGW) | 314 | elseif(MINGW) |
| 315 | add_custom_target(clang-format | 315 | add_custom_target(clang-format |
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index e156bbece..3f8b6cda8 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -49,6 +49,10 @@ add_subdirectory(open_source_archives EXCLUDE_FROM_ALL) | |||
| 49 | add_library(unicorn-headers INTERFACE) | 49 | add_library(unicorn-headers INTERFACE) |
| 50 | target_include_directories(unicorn-headers INTERFACE ./unicorn/include) | 50 | target_include_directories(unicorn-headers INTERFACE ./unicorn/include) |
| 51 | 51 | ||
| 52 | # Zstandard | ||
| 53 | add_subdirectory(zstd/build/cmake EXCLUDE_FROM_ALL) | ||
| 54 | target_include_directories(libzstd_static INTERFACE ./zstd/lib) | ||
| 55 | |||
| 52 | # SoundTouch | 56 | # SoundTouch |
| 53 | add_subdirectory(soundtouch) | 57 | add_subdirectory(soundtouch) |
| 54 | 58 | ||
| @@ -68,6 +72,11 @@ if (USE_DISCORD_PRESENCE) | |||
| 68 | target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) | 72 | target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) |
| 69 | endif() | 73 | endif() |
| 70 | 74 | ||
| 75 | # Sirit | ||
| 76 | if (ENABLE_VULKAN) | ||
| 77 | add_subdirectory(sirit) | ||
| 78 | endif() | ||
| 79 | |||
| 71 | if (ENABLE_WEB_SERVICE) | 80 | if (ENABLE_WEB_SERVICE) |
| 72 | # LibreSSL | 81 | # LibreSSL |
| 73 | set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") | 82 | set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") |
diff --git a/externals/sirit b/externals/sirit new file mode 160000 | |||
| Subproject f7c4b07a7e14edb1dcd93bc9879c823423705c2 | |||
diff --git a/externals/zstd b/externals/zstd new file mode 160000 | |||
| Subproject 470344d33e1d52a2ada75d278466da8d4ee2faf | |||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 850ce8006..1e8e1b215 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -91,6 +91,8 @@ add_library(common STATIC | |||
| 91 | logging/log.h | 91 | logging/log.h |
| 92 | logging/text_formatter.cpp | 92 | logging/text_formatter.cpp |
| 93 | logging/text_formatter.h | 93 | logging/text_formatter.h |
| 94 | lz4_compression.cpp | ||
| 95 | lz4_compression.h | ||
| 94 | math_util.h | 96 | math_util.h |
| 95 | memory_hook.cpp | 97 | memory_hook.cpp |
| 96 | memory_hook.h | 98 | memory_hook.h |
| @@ -123,6 +125,8 @@ add_library(common STATIC | |||
| 123 | uint128.h | 125 | uint128.h |
| 124 | vector_math.h | 126 | vector_math.h |
| 125 | web_result.h | 127 | web_result.h |
| 128 | zstd_compression.cpp | ||
| 129 | zstd_compression.h | ||
| 126 | ) | 130 | ) |
| 127 | 131 | ||
| 128 | if(ARCHITECTURE_x86_64) | 132 | if(ARCHITECTURE_x86_64) |
| @@ -136,3 +140,4 @@ endif() | |||
| 136 | create_target_directory_groups(common) | 140 | create_target_directory_groups(common) |
| 137 | 141 | ||
| 138 | target_link_libraries(common PUBLIC Boost::boost fmt microprofile) | 142 | target_link_libraries(common PUBLIC Boost::boost fmt microprofile) |
| 143 | target_link_libraries(common PRIVATE lz4_static libzstd_static) | ||
diff --git a/src/common/assert.h b/src/common/assert.h index 6002f7ab1..4b0e3f64e 100644 --- a/src/common/assert.h +++ b/src/common/assert.h | |||
| @@ -57,3 +57,21 @@ __declspec(noinline, noreturn) | |||
| 57 | 57 | ||
| 58 | #define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!") | 58 | #define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!") |
| 59 | #define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__) | 59 | #define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__) |
| 60 | |||
| 61 | // If the assert is ignored, execute _b_ | ||
| 62 | #define ASSERT_OR_EXECUTE(_a_, _b_) \ | ||
| 63 | do { \ | ||
| 64 | ASSERT(_a_); \ | ||
| 65 | if (!(_a_)) { \ | ||
| 66 | _b_ \ | ||
| 67 | } \ | ||
| 68 | } while (0) | ||
| 69 | |||
| 70 | // If the assert is ignored, execute _b_ | ||
| 71 | #define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ | ||
| 72 | do { \ | ||
| 73 | ASSERT_MSG(_a_, __VA_ARGS__); \ | ||
| 74 | if (!(_a_)) { \ | ||
| 75 | _b_ \ | ||
| 76 | } \ | ||
| 77 | } while (0) | ||
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index a4f9ed4aa..d032df413 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h | |||
| @@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) { | |||
| 32 | return 32; | 32 | return 32; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | inline u64 CountLeadingZeroes64(u64 value) { | 35 | inline u32 CountLeadingZeroes64(u64 value) { |
| 36 | unsigned long leading_zero = 0; | 36 | unsigned long leading_zero = 0; |
| 37 | 37 | ||
| 38 | if (_BitScanReverse64(&leading_zero, value) != 0) { | 38 | if (_BitScanReverse64(&leading_zero, value) != 0) { |
| @@ -47,15 +47,15 @@ inline u32 CountLeadingZeroes32(u32 value) { | |||
| 47 | return 32; | 47 | return 32; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | return __builtin_clz(value); | 50 | return static_cast<u32>(__builtin_clz(value)); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | inline u64 CountLeadingZeroes64(u64 value) { | 53 | inline u32 CountLeadingZeroes64(u64 value) { |
| 54 | if (value == 0) { | 54 | if (value == 0) { |
| 55 | return 64; | 55 | return 64; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | return __builtin_clzll(value); | 58 | return static_cast<u32>(__builtin_clzll(value)); |
| 59 | } | 59 | } |
| 60 | #endif | 60 | #endif |
| 61 | 61 | ||
| @@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) { | |||
| 70 | return 32; | 70 | return 32; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | inline u64 CountTrailingZeroes64(u64 value) { | 73 | inline u32 CountTrailingZeroes64(u64 value) { |
| 74 | unsigned long trailing_zero = 0; | 74 | unsigned long trailing_zero = 0; |
| 75 | 75 | ||
| 76 | if (_BitScanForward64(&trailing_zero, value) != 0) { | 76 | if (_BitScanForward64(&trailing_zero, value) != 0) { |
| @@ -85,15 +85,15 @@ inline u32 CountTrailingZeroes32(u32 value) { | |||
| 85 | return 32; | 85 | return 32; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | return __builtin_ctz(value); | 88 | return static_cast<u32>(__builtin_ctz(value)); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | inline u64 CountTrailingZeroes64(u64 value) { | 91 | inline u32 CountTrailingZeroes64(u64 value) { |
| 92 | if (value == 0) { | 92 | if (value == 0) { |
| 93 | return 64; | 93 | return 64; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | return __builtin_ctzll(value); | 96 | return static_cast<u32>(__builtin_ctzll(value)); |
| 97 | } | 97 | } |
| 98 | #endif | 98 | #endif |
| 99 | 99 | ||
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp new file mode 100644 index 000000000..ade6759bb --- /dev/null +++ b/src/common/lz4_compression.cpp | |||
| @@ -0,0 +1,76 @@ | |||
| 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 <algorithm> | ||
| 6 | #include <lz4hc.h> | ||
| 7 | |||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/lz4_compression.h" | ||
| 10 | |||
| 11 | namespace Common::Compression { | ||
| 12 | |||
| 13 | std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) { | ||
| 14 | ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); | ||
| 15 | |||
| 16 | const auto source_size_int = static_cast<int>(source_size); | ||
| 17 | const int max_compressed_size = LZ4_compressBound(source_size_int); | ||
| 18 | std::vector<u8> compressed(max_compressed_size); | ||
| 19 | |||
| 20 | const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), | ||
| 21 | reinterpret_cast<char*>(compressed.data()), | ||
| 22 | source_size_int, max_compressed_size); | ||
| 23 | |||
| 24 | if (compressed_size <= 0) { | ||
| 25 | // Compression failed | ||
| 26 | return {}; | ||
| 27 | } | ||
| 28 | |||
| 29 | compressed.resize(compressed_size); | ||
| 30 | |||
| 31 | return compressed; | ||
| 32 | } | ||
| 33 | |||
| 34 | std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, | ||
| 35 | s32 compression_level) { | ||
| 36 | ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); | ||
| 37 | |||
| 38 | compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); | ||
| 39 | |||
| 40 | const auto source_size_int = static_cast<int>(source_size); | ||
| 41 | const int max_compressed_size = LZ4_compressBound(source_size_int); | ||
| 42 | std::vector<u8> compressed(max_compressed_size); | ||
| 43 | |||
| 44 | const int compressed_size = LZ4_compress_HC( | ||
| 45 | reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()), | ||
| 46 | source_size_int, max_compressed_size, compression_level); | ||
| 47 | |||
| 48 | if (compressed_size <= 0) { | ||
| 49 | // Compression failed | ||
| 50 | return {}; | ||
| 51 | } | ||
| 52 | |||
| 53 | compressed.resize(compressed_size); | ||
| 54 | |||
| 55 | return compressed; | ||
| 56 | } | ||
| 57 | |||
| 58 | std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size) { | ||
| 59 | return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX); | ||
| 60 | } | ||
| 61 | |||
| 62 | std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, | ||
| 63 | std::size_t uncompressed_size) { | ||
| 64 | std::vector<u8> uncompressed(uncompressed_size); | ||
| 65 | const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()), | ||
| 66 | reinterpret_cast<char*>(uncompressed.data()), | ||
| 67 | static_cast<int>(compressed.size()), | ||
| 68 | static_cast<int>(uncompressed.size())); | ||
| 69 | if (static_cast<int>(uncompressed_size) != size_check) { | ||
| 70 | // Decompression failed | ||
| 71 | return {}; | ||
| 72 | } | ||
| 73 | return uncompressed; | ||
| 74 | } | ||
| 75 | |||
| 76 | } // namespace Common::Compression | ||
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h new file mode 100644 index 000000000..4c16f6e03 --- /dev/null +++ b/src/common/lz4_compression.h | |||
| @@ -0,0 +1,57 @@ | |||
| 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 <vector> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Common::Compression { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Compresses a source memory region with LZ4 and returns the compressed data in a vector. | ||
| 15 | * | ||
| 16 | * @param source the uncompressed source memory region. | ||
| 17 | * @param source_size the size in bytes of the uncompressed source memory region. | ||
| 18 | * | ||
| 19 | * @return the compressed data. | ||
| 20 | */ | ||
| 21 | std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size); | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression | ||
| 25 | * levels result in a smaller compressed size, but require more CPU time for compression. The | ||
| 26 | * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can | ||
| 27 | * also be decompressed with the default LZ4 decompression. | ||
| 28 | * | ||
| 29 | * @param source the uncompressed source memory region. | ||
| 30 | * @param source_size the size in bytes of the uncompressed source memory region. | ||
| 31 | * @param compression_level the used compression level. Should be between 3 and 12. | ||
| 32 | * | ||
| 33 | * @return the compressed data. | ||
| 34 | */ | ||
| 35 | std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level. | ||
| 39 | * | ||
| 40 | * @param source the uncompressed source memory region. | ||
| 41 | * @param source_size the size in bytes of the uncompressed source memory region. | ||
| 42 | * | ||
| 43 | * @return the compressed data. | ||
| 44 | */ | ||
| 45 | std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector. | ||
| 49 | * | ||
| 50 | * @param compressed the compressed source memory region. | ||
| 51 | * @param uncompressed_size the size in bytes of the uncompressed data. | ||
| 52 | * | ||
| 53 | * @return the decompressed data. | ||
| 54 | */ | ||
| 55 | std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size); | ||
| 56 | |||
| 57 | } // namespace Common::Compression \ No newline at end of file | ||
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h index 2b61b91e0..9cb448f56 100644 --- a/src/common/multi_level_queue.h +++ b/src/common/multi_level_queue.h | |||
| @@ -72,7 +72,7 @@ public: | |||
| 72 | u64 prios = mlq.used_priorities; | 72 | u64 prios = mlq.used_priorities; |
| 73 | prios &= ~((1ULL << (current_priority + 1)) - 1); | 73 | prios &= ~((1ULL << (current_priority + 1)) - 1); |
| 74 | if (prios == 0) { | 74 | if (prios == 0) { |
| 75 | current_priority = mlq.depth(); | 75 | current_priority = static_cast<u32>(mlq.depth()); |
| 76 | } else { | 76 | } else { |
| 77 | current_priority = CountTrailingZeroes64(prios); | 77 | current_priority = CountTrailingZeroes64(prios); |
| 78 | it = GetBeginItForPrio(); | 78 | it = GetBeginItForPrio(); |
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h index baf1f1c9e..1176a72b1 100644 --- a/src/common/scope_exit.h +++ b/src/common/scope_exit.h | |||
| @@ -20,7 +20,7 @@ struct ScopeExitHelper { | |||
| 20 | 20 | ||
| 21 | template <typename Func> | 21 | template <typename Func> |
| 22 | ScopeExitHelper<Func> ScopeExit(Func&& func) { | 22 | ScopeExitHelper<Func> ScopeExit(Func&& func) { |
| 23 | return ScopeExitHelper<Func>(std::move(func)); | 23 | return ScopeExitHelper<Func>(std::forward<Func>(func)); |
| 24 | } | 24 | } |
| 25 | } // namespace detail | 25 | } // namespace detail |
| 26 | 26 | ||
diff --git a/src/common/swap.h b/src/common/swap.h index b3eab1324..71932c2bb 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -21,11 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | #if defined(_MSC_VER) | 22 | #if defined(_MSC_VER) |
| 23 | #include <cstdlib> | 23 | #include <cstdlib> |
| 24 | #elif defined(__linux__) | ||
| 25 | #include <byteswap.h> | ||
| 26 | #elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \ | ||
| 27 | defined(__NetBSD__) || defined(__OpenBSD__) | ||
| 28 | #include <sys/endian.h> | ||
| 29 | #endif | 24 | #endif |
| 30 | #include <cstring> | 25 | #include <cstring> |
| 31 | #include "common/common_types.h" | 26 | #include "common/common_types.h" |
| @@ -62,86 +57,49 @@ | |||
| 62 | namespace Common { | 57 | namespace Common { |
| 63 | 58 | ||
| 64 | #ifdef _MSC_VER | 59 | #ifdef _MSC_VER |
| 65 | inline u16 swap16(u16 _data) { | 60 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 66 | return _byteswap_ushort(_data); | 61 | return _byteswap_ushort(data); |
| 67 | } | 62 | } |
| 68 | inline u32 swap32(u32 _data) { | 63 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 69 | return _byteswap_ulong(_data); | 64 | return _byteswap_ulong(data); |
| 70 | } | 65 | } |
| 71 | inline u64 swap64(u64 _data) { | 66 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 72 | return _byteswap_uint64(_data); | 67 | return _byteswap_uint64(data); |
| 73 | } | 68 | } |
| 74 | #elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6) | 69 | #elif defined(__clang__) || defined(__GNUC__) |
| 75 | inline u16 swap16(u16 _data) { | 70 | #if defined(__Bitrig__) || defined(__OpenBSD__) |
| 76 | u32 data = _data; | ||
| 77 | __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); | ||
| 78 | return (u16)data; | ||
| 79 | } | ||
| 80 | inline u32 swap32(u32 _data) { | ||
| 81 | __asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data)); | ||
| 82 | return _data; | ||
| 83 | } | ||
| 84 | inline u64 swap64(u64 _data) { | ||
| 85 | return ((u64)swap32(_data) << 32) | swap32(_data >> 32); | ||
| 86 | } | ||
| 87 | #elif __linux__ | ||
| 88 | inline u16 swap16(u16 _data) { | ||
| 89 | return bswap_16(_data); | ||
| 90 | } | ||
| 91 | inline u32 swap32(u32 _data) { | ||
| 92 | return bswap_32(_data); | ||
| 93 | } | ||
| 94 | inline u64 swap64(u64 _data) { | ||
| 95 | return bswap_64(_data); | ||
| 96 | } | ||
| 97 | #elif __APPLE__ | ||
| 98 | inline __attribute__((always_inline)) u16 swap16(u16 _data) { | ||
| 99 | return (_data >> 8) | (_data << 8); | ||
| 100 | } | ||
| 101 | inline __attribute__((always_inline)) u32 swap32(u32 _data) { | ||
| 102 | return __builtin_bswap32(_data); | ||
| 103 | } | ||
| 104 | inline __attribute__((always_inline)) u64 swap64(u64 _data) { | ||
| 105 | return __builtin_bswap64(_data); | ||
| 106 | } | ||
| 107 | #elif defined(__Bitrig__) || defined(__OpenBSD__) | ||
| 108 | // redefine swap16, swap32, swap64 as inline functions | 71 | // redefine swap16, swap32, swap64 as inline functions |
| 109 | #undef swap16 | 72 | #undef swap16 |
| 110 | #undef swap32 | 73 | #undef swap32 |
| 111 | #undef swap64 | 74 | #undef swap64 |
| 112 | inline u16 swap16(u16 _data) { | 75 | #endif |
| 113 | return __swap16(_data); | 76 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 114 | } | 77 | return __builtin_bswap16(data); |
| 115 | inline u32 swap32(u32 _data) { | ||
| 116 | return __swap32(_data); | ||
| 117 | } | ||
| 118 | inline u64 swap64(u64 _data) { | ||
| 119 | return __swap64(_data); | ||
| 120 | } | ||
| 121 | #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) | ||
| 122 | inline u16 swap16(u16 _data) { | ||
| 123 | return bswap16(_data); | ||
| 124 | } | 78 | } |
| 125 | inline u32 swap32(u32 _data) { | 79 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 126 | return bswap32(_data); | 80 | return __builtin_bswap32(data); |
| 127 | } | 81 | } |
| 128 | inline u64 swap64(u64 _data) { | 82 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 129 | return bswap64(_data); | 83 | return __builtin_bswap64(data); |
| 130 | } | 84 | } |
| 131 | #else | 85 | #else |
| 132 | // Slow generic implementation. | 86 | // Generic implementation. |
| 133 | inline u16 swap16(u16 data) { | 87 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 134 | return (data >> 8) | (data << 8); | 88 | return (data >> 8) | (data << 8); |
| 135 | } | 89 | } |
| 136 | inline u32 swap32(u32 data) { | 90 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 137 | return (swap16(data) << 16) | swap16(data >> 16); | 91 | return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) | |
| 92 | ((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24); | ||
| 138 | } | 93 | } |
| 139 | inline u64 swap64(u64 data) { | 94 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 140 | return ((u64)swap32(data) << 32) | swap32(data >> 32); | 95 | return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) | |
| 96 | ((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) | | ||
| 97 | ((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) | | ||
| 98 | ((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56); | ||
| 141 | } | 99 | } |
| 142 | #endif | 100 | #endif |
| 143 | 101 | ||
| 144 | inline float swapf(float f) { | 102 | [[nodiscard]] inline float swapf(float f) noexcept { |
| 145 | static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); | 103 | static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); |
| 146 | 104 | ||
| 147 | u32 value; | 105 | u32 value; |
| @@ -153,7 +111,7 @@ inline float swapf(float f) { | |||
| 153 | return f; | 111 | return f; |
| 154 | } | 112 | } |
| 155 | 113 | ||
| 156 | inline double swapd(double f) { | 114 | [[nodiscard]] inline double swapd(double f) noexcept { |
| 157 | static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); | 115 | static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); |
| 158 | 116 | ||
| 159 | u64 value; | 117 | u64 value; |
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp new file mode 100644 index 000000000..60a35c67c --- /dev/null +++ b/src/common/zstd_compression.cpp | |||
| @@ -0,0 +1,53 @@ | |||
| 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 <algorithm> | ||
| 8 | #include <zstd.h> | ||
| 9 | |||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/zstd_compression.h" | ||
| 12 | |||
| 13 | namespace Common::Compression { | ||
| 14 | |||
| 15 | std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level) { | ||
| 16 | compression_level = std::clamp(compression_level, 1, ZSTD_maxCLevel()); | ||
| 17 | |||
| 18 | const std::size_t max_compressed_size = ZSTD_compressBound(source_size); | ||
| 19 | std::vector<u8> compressed(max_compressed_size); | ||
| 20 | |||
| 21 | const std::size_t compressed_size = | ||
| 22 | ZSTD_compress(compressed.data(), compressed.size(), source, source_size, compression_level); | ||
| 23 | |||
| 24 | if (ZSTD_isError(compressed_size)) { | ||
| 25 | // Compression failed | ||
| 26 | return {}; | ||
| 27 | } | ||
| 28 | |||
| 29 | compressed.resize(compressed_size); | ||
| 30 | |||
| 31 | return compressed; | ||
| 32 | } | ||
| 33 | |||
| 34 | std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size) { | ||
| 35 | return CompressDataZSTD(source, source_size, ZSTD_CLEVEL_DEFAULT); | ||
| 36 | } | ||
| 37 | |||
| 38 | std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed) { | ||
| 39 | const std::size_t decompressed_size = | ||
| 40 | ZSTD_getDecompressedSize(compressed.data(), compressed.size()); | ||
| 41 | std::vector<u8> decompressed(decompressed_size); | ||
| 42 | |||
| 43 | const std::size_t uncompressed_result_size = ZSTD_decompress( | ||
| 44 | decompressed.data(), decompressed.size(), compressed.data(), compressed.size()); | ||
| 45 | |||
| 46 | if (decompressed_size != uncompressed_result_size || ZSTD_isError(uncompressed_result_size)) { | ||
| 47 | // Decompression failed | ||
| 48 | return {}; | ||
| 49 | } | ||
| 50 | return decompressed; | ||
| 51 | } | ||
| 52 | |||
| 53 | } // namespace Common::Compression | ||
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h new file mode 100644 index 000000000..e9de941c8 --- /dev/null +++ b/src/common/zstd_compression.h | |||
| @@ -0,0 +1,44 @@ | |||
| 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 <vector> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Common::Compression { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Compresses a source memory region with Zstandard and returns the compressed data in a vector. | ||
| 15 | * | ||
| 16 | * @param source the uncompressed source memory region. | ||
| 17 | * @param source_size the size in bytes of the uncompressed source memory region. | ||
| 18 | * @param compression_level the used compression level. Should be between 1 and 22. | ||
| 19 | * | ||
| 20 | * @return the compressed data. | ||
| 21 | */ | ||
| 22 | std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level); | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Compresses a source memory region with Zstandard with the default compression level and returns | ||
| 26 | * the compressed data in a vector. | ||
| 27 | * | ||
| 28 | * @param source the uncompressed source memory region. | ||
| 29 | * @param source_size the size in bytes of the uncompressed source memory region. | ||
| 30 | * | ||
| 31 | * @return the compressed data. | ||
| 32 | */ | ||
| 33 | std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector. | ||
| 37 | * | ||
| 38 | * @param compressed the compressed source memory region. | ||
| 39 | * | ||
| 40 | * @return the decompressed data. | ||
| 41 | */ | ||
| 42 | std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed); | ||
| 43 | |||
| 44 | } // namespace Common::Compression \ No newline at end of file | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9e23afe85..c59107102 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -458,7 +458,7 @@ add_library(core STATIC | |||
| 458 | create_target_directory_groups(core) | 458 | create_target_directory_groups(core) |
| 459 | 459 | ||
| 460 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 460 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 461 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) | 461 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives) |
| 462 | if (ENABLE_WEB_SERVICE) | 462 | if (ENABLE_WEB_SERVICE) |
| 463 | target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) | 463 | target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) |
| 464 | target_link_libraries(core PRIVATE web_service) | 464 | target_link_libraries(core PRIVATE web_service) |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 4fdc12f11..dc96e35d5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 15 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| 16 | #include "core/gdbstub/gdbstub.h" | 16 | #include "core/gdbstub/gdbstub.h" |
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 17 | #include "core/hle/kernel/process.h" | 18 | #include "core/hle/kernel/process.h" |
| 18 | #include "core/hle/kernel/svc.h" | 19 | #include "core/hle/kernel/svc.h" |
| 19 | #include "core/hle/kernel/vm_manager.h" | 20 | #include "core/hle/kernel/vm_manager.h" |
| @@ -26,7 +27,6 @@ using Vector = Dynarmic::A64::Vector; | |||
| 26 | class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { | 27 | class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { |
| 27 | public: | 28 | public: |
| 28 | explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} | 29 | explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} |
| 29 | ~ARM_Dynarmic_Callbacks() = default; | ||
| 30 | 30 | ||
| 31 | u8 MemoryRead8(u64 vaddr) override { | 31 | u8 MemoryRead8(u64 vaddr) override { |
| 32 | return Memory::Read8(vaddr); | 32 | return Memory::Read8(vaddr); |
| @@ -100,7 +100,7 @@ public: | |||
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | void CallSVC(u32 swi) override { | 102 | void CallSVC(u32 swi) override { |
| 103 | Kernel::CallSVC(swi); | 103 | Kernel::CallSVC(parent.system, swi); |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | void AddTicks(u64 ticks) override { | 106 | void AddTicks(u64 ticks) override { |
| @@ -113,14 +113,14 @@ public: | |||
| 113 | // Always execute at least one tick. | 113 | // Always execute at least one tick. |
| 114 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 114 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 115 | 115 | ||
| 116 | parent.core_timing.AddTicks(amortized_ticks); | 116 | parent.system.CoreTiming().AddTicks(amortized_ticks); |
| 117 | num_interpreted_instructions = 0; | 117 | num_interpreted_instructions = 0; |
| 118 | } | 118 | } |
| 119 | u64 GetTicksRemaining() override { | 119 | u64 GetTicksRemaining() override { |
| 120 | return std::max(parent.core_timing.GetDowncount(), 0); | 120 | return std::max(parent.system.CoreTiming().GetDowncount(), 0); |
| 121 | } | 121 | } |
| 122 | u64 GetCNTPCT() override { | 122 | u64 GetCNTPCT() override { |
| 123 | return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks()); | 123 | return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | ARM_Dynarmic& parent; | 126 | ARM_Dynarmic& parent; |
| @@ -130,7 +130,7 @@ public: | |||
| 130 | }; | 130 | }; |
| 131 | 131 | ||
| 132 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { | 132 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { |
| 133 | auto* current_process = Core::CurrentProcess(); | 133 | auto* current_process = system.Kernel().CurrentProcess(); |
| 134 | auto** const page_table = current_process->VMManager().page_table.pointers.data(); | 134 | auto** const page_table = current_process->VMManager().page_table.pointers.data(); |
| 135 | 135 | ||
| 136 | Dynarmic::A64::UserConfig config; | 136 | Dynarmic::A64::UserConfig config; |
| @@ -164,7 +164,6 @@ MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64) | |||
| 164 | 164 | ||
| 165 | void ARM_Dynarmic::Run() { | 165 | void ARM_Dynarmic::Run() { |
| 166 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic); | 166 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic); |
| 167 | ASSERT(Memory::GetCurrentPageTable() == current_page_table); | ||
| 168 | 167 | ||
| 169 | jit->Run(); | 168 | jit->Run(); |
| 170 | } | 169 | } |
| @@ -173,10 +172,10 @@ void ARM_Dynarmic::Step() { | |||
| 173 | cb->InterpreterFallback(jit->GetPC(), 1); | 172 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 174 | } | 173 | } |
| 175 | 174 | ||
| 176 | ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, | 175 | ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, |
| 177 | std::size_t core_index) | 176 | std::size_t core_index) |
| 178 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing}, | 177 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, |
| 179 | core_index{core_index}, core_timing{core_timing}, | 178 | core_index{core_index}, system{system}, |
| 180 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { | 179 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { |
| 181 | ThreadContext ctx{}; | 180 | ThreadContext ctx{}; |
| 182 | inner_unicorn.SaveContext(ctx); | 181 | inner_unicorn.SaveContext(ctx); |
| @@ -279,7 +278,6 @@ void ARM_Dynarmic::ClearExclusiveState() { | |||
| 279 | 278 | ||
| 280 | void ARM_Dynarmic::PageTableChanged() { | 279 | void ARM_Dynarmic::PageTableChanged() { |
| 281 | jit = MakeJit(); | 280 | jit = MakeJit(); |
| 282 | current_page_table = Memory::GetCurrentPageTable(); | ||
| 283 | } | 281 | } |
| 284 | 282 | ||
| 285 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} | 283 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index aada1e862..c1db254e8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h | |||
| @@ -12,24 +12,16 @@ | |||
| 12 | #include "core/arm/exclusive_monitor.h" | 12 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | 13 | #include "core/arm/unicorn/arm_unicorn.h" |
| 14 | 14 | ||
| 15 | namespace Common { | ||
| 16 | struct PageTable; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Core::Timing { | ||
| 20 | class CoreTiming; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Core { | 15 | namespace Core { |
| 24 | 16 | ||
| 25 | class ARM_Dynarmic_Callbacks; | 17 | class ARM_Dynarmic_Callbacks; |
| 26 | class DynarmicExclusiveMonitor; | 18 | class DynarmicExclusiveMonitor; |
| 19 | class System; | ||
| 27 | 20 | ||
| 28 | class ARM_Dynarmic final : public ARM_Interface { | 21 | class ARM_Dynarmic final : public ARM_Interface { |
| 29 | public: | 22 | public: |
| 30 | ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, | 23 | ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); |
| 31 | std::size_t core_index); | 24 | ~ARM_Dynarmic() override; |
| 32 | ~ARM_Dynarmic(); | ||
| 33 | 25 | ||
| 34 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, | 26 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 35 | Kernel::VMAPermission perms) override; | 27 | Kernel::VMAPermission perms) override; |
| @@ -67,16 +59,14 @@ private: | |||
| 67 | ARM_Unicorn inner_unicorn; | 59 | ARM_Unicorn inner_unicorn; |
| 68 | 60 | ||
| 69 | std::size_t core_index; | 61 | std::size_t core_index; |
| 70 | Timing::CoreTiming& core_timing; | 62 | System& system; |
| 71 | DynarmicExclusiveMonitor& exclusive_monitor; | 63 | DynarmicExclusiveMonitor& exclusive_monitor; |
| 72 | |||
| 73 | Common::PageTable* current_page_table = nullptr; | ||
| 74 | }; | 64 | }; |
| 75 | 65 | ||
| 76 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | 66 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { |
| 77 | public: | 67 | public: |
| 78 | explicit DynarmicExclusiveMonitor(std::size_t core_count); | 68 | explicit DynarmicExclusiveMonitor(std::size_t core_count); |
| 79 | ~DynarmicExclusiveMonitor(); | 69 | ~DynarmicExclusiveMonitor() override; |
| 80 | 70 | ||
| 81 | void SetExclusive(std::size_t core_index, VAddr addr) override; | 71 | void SetExclusive(std::size_t core_index, VAddr addr) override; |
| 82 | void ClearExclusive() override; | 72 | void ClearExclusive() override; |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index a542a098b..4e07fe8b5 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 12 | #include "core/hle/kernel/svc.h" | 12 | #include "core/hle/kernel/svc.h" |
| 13 | #include "core/memory.h" | ||
| 14 | 13 | ||
| 15 | namespace Core { | 14 | namespace Core { |
| 16 | 15 | ||
| @@ -49,20 +48,6 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_ | |||
| 49 | } | 48 | } |
| 50 | } | 49 | } |
| 51 | 50 | ||
| 52 | static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) { | ||
| 53 | u32 esr{}; | ||
| 54 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); | ||
| 55 | |||
| 56 | auto ec = esr >> 26; | ||
| 57 | auto iss = esr & 0xFFFFFF; | ||
| 58 | |||
| 59 | switch (ec) { | ||
| 60 | case 0x15: // SVC | ||
| 61 | Kernel::CallSVC(iss); | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, | 51 | static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, |
| 67 | void* user_data) { | 52 | void* user_data) { |
| 68 | ARM_Interface::ThreadContext ctx{}; | 53 | ARM_Interface::ThreadContext ctx{}; |
| @@ -72,7 +57,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si | |||
| 72 | return {}; | 57 | return {}; |
| 73 | } | 58 | } |
| 74 | 59 | ||
| 75 | ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} { | 60 | ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { |
| 76 | CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); | 61 | CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); |
| 77 | 62 | ||
| 78 | auto fpv = 3 << 20; | 63 | auto fpv = 3 << 20; |
| @@ -177,7 +162,7 @@ void ARM_Unicorn::Run() { | |||
| 177 | if (GDBStub::IsServerEnabled()) { | 162 | if (GDBStub::IsServerEnabled()) { |
| 178 | ExecuteInstructions(std::max(4000000, 0)); | 163 | ExecuteInstructions(std::max(4000000, 0)); |
| 179 | } else { | 164 | } else { |
| 180 | ExecuteInstructions(std::max(core_timing.GetDowncount(), 0)); | 165 | ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0)); |
| 181 | } | 166 | } |
| 182 | } | 167 | } |
| 183 | 168 | ||
| @@ -190,14 +175,15 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); | |||
| 190 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { | 175 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { |
| 191 | MICROPROFILE_SCOPE(ARM_Jit_Unicorn); | 176 | MICROPROFILE_SCOPE(ARM_Jit_Unicorn); |
| 192 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); | 177 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); |
| 193 | core_timing.AddTicks(num_instructions); | 178 | system.CoreTiming().AddTicks(num_instructions); |
| 194 | if (GDBStub::IsServerEnabled()) { | 179 | if (GDBStub::IsServerEnabled()) { |
| 195 | if (last_bkpt_hit) { | 180 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { |
| 196 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 181 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
| 197 | } | 182 | } |
| 183 | |||
| 198 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 184 | Kernel::Thread* thread = Kernel::GetCurrentThread(); |
| 199 | SaveContext(thread->GetContext()); | 185 | SaveContext(thread->GetContext()); |
| 200 | if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { | 186 | if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { |
| 201 | last_bkpt_hit = false; | 187 | last_bkpt_hit = false; |
| 202 | GDBStub::Break(); | 188 | GDBStub::Break(); |
| 203 | GDBStub::SendTrap(thread, 5); | 189 | GDBStub::SendTrap(thread, 5); |
| @@ -272,4 +258,20 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) { | |||
| 272 | last_bkpt_hit = true; | 258 | last_bkpt_hit = true; |
| 273 | } | 259 | } |
| 274 | 260 | ||
| 261 | void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) { | ||
| 262 | u32 esr{}; | ||
| 263 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); | ||
| 264 | |||
| 265 | const auto ec = esr >> 26; | ||
| 266 | const auto iss = esr & 0xFFFFFF; | ||
| 267 | |||
| 268 | auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data); | ||
| 269 | |||
| 270 | switch (ec) { | ||
| 271 | case 0x15: // SVC | ||
| 272 | Kernel::CallSVC(arm_instance->system, iss); | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 275 | } // namespace Core | 277 | } // namespace Core |
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index dbd6955ea..209fc16ad 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h | |||
| @@ -9,16 +9,14 @@ | |||
| 9 | #include "core/arm/arm_interface.h" | 9 | #include "core/arm/arm_interface.h" |
| 10 | #include "core/gdbstub/gdbstub.h" | 10 | #include "core/gdbstub/gdbstub.h" |
| 11 | 11 | ||
| 12 | namespace Core::Timing { | ||
| 13 | class CoreTiming; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Core { | 12 | namespace Core { |
| 17 | 13 | ||
| 14 | class System; | ||
| 15 | |||
| 18 | class ARM_Unicorn final : public ARM_Interface { | 16 | class ARM_Unicorn final : public ARM_Interface { |
| 19 | public: | 17 | public: |
| 20 | explicit ARM_Unicorn(Timing::CoreTiming& core_timing); | 18 | explicit ARM_Unicorn(System& system); |
| 21 | ~ARM_Unicorn(); | 19 | ~ARM_Unicorn() override; |
| 22 | 20 | ||
| 23 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, | 21 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 24 | Kernel::VMAPermission perms) override; | 22 | Kernel::VMAPermission perms) override; |
| @@ -47,10 +45,12 @@ public: | |||
| 47 | void RecordBreak(GDBStub::BreakpointAddress bkpt); | 45 | void RecordBreak(GDBStub::BreakpointAddress bkpt); |
| 48 | 46 | ||
| 49 | private: | 47 | private: |
| 48 | static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data); | ||
| 49 | |||
| 50 | uc_engine* uc{}; | 50 | uc_engine* uc{}; |
| 51 | Timing::CoreTiming& core_timing; | 51 | System& system; |
| 52 | GDBStub::BreakpointAddress last_bkpt{}; | 52 | GDBStub::BreakpointAddress last_bkpt{}; |
| 53 | bool last_bkpt_hit; | 53 | bool last_bkpt_hit = false; |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | } // namespace Core | 56 | } // namespace Core |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 4fe77c25b..bc9e887b6 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/core_timing.h" | 17 | #include "core/core_timing.h" |
| 18 | #include "core/cpu_core_manager.h" | 18 | #include "core/cpu_core_manager.h" |
| 19 | #include "core/file_sys/mode.h" | 19 | #include "core/file_sys/mode.h" |
| 20 | #include "core/file_sys/registered_cache.h" | ||
| 20 | #include "core/file_sys/vfs_concat.h" | 21 | #include "core/file_sys/vfs_concat.h" |
| 21 | #include "core/file_sys/vfs_real.h" | 22 | #include "core/file_sys/vfs_real.h" |
| 22 | #include "core/gdbstub/gdbstub.h" | 23 | #include "core/gdbstub/gdbstub.h" |
| @@ -108,6 +109,8 @@ struct System::Impl { | |||
| 108 | // Create a default fs if one doesn't already exist. | 109 | // Create a default fs if one doesn't already exist. |
| 109 | if (virtual_filesystem == nullptr) | 110 | if (virtual_filesystem == nullptr) |
| 110 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); | 111 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); |
| 112 | if (content_provider == nullptr) | ||
| 113 | content_provider = std::make_unique<FileSys::ContentProviderUnion>(); | ||
| 111 | 114 | ||
| 112 | /// Create default implementations of applets if one is not provided. | 115 | /// Create default implementations of applets if one is not provided. |
| 113 | if (profile_selector == nullptr) | 116 | if (profile_selector == nullptr) |
| @@ -249,6 +252,8 @@ struct System::Impl { | |||
| 249 | Kernel::KernelCore kernel; | 252 | Kernel::KernelCore kernel; |
| 250 | /// RealVfsFilesystem instance | 253 | /// RealVfsFilesystem instance |
| 251 | FileSys::VirtualFilesystem virtual_filesystem; | 254 | FileSys::VirtualFilesystem virtual_filesystem; |
| 255 | /// ContentProviderUnion instance | ||
| 256 | std::unique_ptr<FileSys::ContentProviderUnion> content_provider; | ||
| 252 | /// AppLoader used to load the current executing application | 257 | /// AppLoader used to load the current executing application |
| 253 | std::unique_ptr<Loader::AppLoader> app_loader; | 258 | std::unique_ptr<Loader::AppLoader> app_loader; |
| 254 | std::unique_ptr<VideoCore::RendererBase> renderer; | 259 | std::unique_ptr<VideoCore::RendererBase> renderer; |
| @@ -488,6 +493,27 @@ const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const { | |||
| 488 | return *impl->software_keyboard; | 493 | return *impl->software_keyboard; |
| 489 | } | 494 | } |
| 490 | 495 | ||
| 496 | void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { | ||
| 497 | impl->content_provider = std::move(provider); | ||
| 498 | } | ||
| 499 | |||
| 500 | FileSys::ContentProvider& System::GetContentProvider() { | ||
| 501 | return *impl->content_provider; | ||
| 502 | } | ||
| 503 | |||
| 504 | const FileSys::ContentProvider& System::GetContentProvider() const { | ||
| 505 | return *impl->content_provider; | ||
| 506 | } | ||
| 507 | |||
| 508 | void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, | ||
| 509 | FileSys::ContentProvider* provider) { | ||
| 510 | impl->content_provider->SetSlot(slot, provider); | ||
| 511 | } | ||
| 512 | |||
| 513 | void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { | ||
| 514 | impl->content_provider->ClearSlot(slot); | ||
| 515 | } | ||
| 516 | |||
| 491 | void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) { | 517 | void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) { |
| 492 | impl->web_browser = std::move(applet); | 518 | impl->web_browser = std::move(applet); |
| 493 | } | 519 | } |
diff --git a/src/core/core.h b/src/core/core.h index 4d83b93cc..82b2e087e 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -21,6 +21,9 @@ class WebBrowserApplet; | |||
| 21 | 21 | ||
| 22 | namespace FileSys { | 22 | namespace FileSys { |
| 23 | class CheatList; | 23 | class CheatList; |
| 24 | class ContentProvider; | ||
| 25 | class ContentProviderUnion; | ||
| 26 | enum class ContentProviderUnionSlot; | ||
| 24 | class VfsFilesystem; | 27 | class VfsFilesystem; |
| 25 | } // namespace FileSys | 28 | } // namespace FileSys |
| 26 | 29 | ||
| @@ -270,6 +273,17 @@ public: | |||
| 270 | Frontend::WebBrowserApplet& GetWebBrowser(); | 273 | Frontend::WebBrowserApplet& GetWebBrowser(); |
| 271 | const Frontend::WebBrowserApplet& GetWebBrowser() const; | 274 | const Frontend::WebBrowserApplet& GetWebBrowser() const; |
| 272 | 275 | ||
| 276 | void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); | ||
| 277 | |||
| 278 | FileSys::ContentProvider& GetContentProvider(); | ||
| 279 | |||
| 280 | const FileSys::ContentProvider& GetContentProvider() const; | ||
| 281 | |||
| 282 | void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, | ||
| 283 | FileSys::ContentProvider* provider); | ||
| 284 | |||
| 285 | void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); | ||
| 286 | |||
| 273 | private: | 287 | private: |
| 274 | System(); | 288 | System(); |
| 275 | 289 | ||
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index e75741db0..ba63c3e61 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp | |||
| @@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba | |||
| 55 | : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { | 55 | : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { |
| 56 | if (Settings::values.use_cpu_jit) { | 56 | if (Settings::values.use_cpu_jit) { |
| 57 | #ifdef ARCHITECTURE_x86_64 | 57 | #ifdef ARCHITECTURE_x86_64 |
| 58 | arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index); | 58 | arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); |
| 59 | #else | 59 | #else |
| 60 | arm_interface = std::make_unique<ARM_Unicorn>(); | 60 | arm_interface = std::make_unique<ARM_Unicorn>(system); |
| 61 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | 61 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); |
| 62 | #endif | 62 | #endif |
| 63 | } else { | 63 | } else { |
| 64 | arm_interface = std::make_unique<ARM_Unicorn>(core_timing); | 64 | arm_interface = std::make_unique<ARM_Unicorn>(system); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); | 67 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index dfac9a4b3..dc006e2bb 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "common/file_util.h" | 22 | #include "common/file_util.h" |
| 23 | #include "common/hex_util.h" | 23 | #include "common/hex_util.h" |
| 24 | #include "common/logging/log.h" | 24 | #include "common/logging/log.h" |
| 25 | #include "core/core.h" | ||
| 25 | #include "core/crypto/aes_util.h" | 26 | #include "core/crypto/aes_util.h" |
| 26 | #include "core/crypto/key_manager.h" | 27 | #include "core/crypto/key_manager.h" |
| 27 | #include "core/crypto/partition_data_manager.h" | 28 | #include "core/crypto/partition_data_manager.h" |
| @@ -794,7 +795,7 @@ void KeyManager::DeriveBase() { | |||
| 794 | 795 | ||
| 795 | void KeyManager::DeriveETicket(PartitionDataManager& data) { | 796 | void KeyManager::DeriveETicket(PartitionDataManager& data) { |
| 796 | // ETicket keys | 797 | // ETicket keys |
| 797 | const auto es = Service::FileSystem::GetUnionContents().GetEntry( | 798 | const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( |
| 798 | 0x0100000000000033, FileSys::ContentRecordType::Program); | 799 | 0x0100000000000033, FileSys::ContentRecordType::Program); |
| 799 | 800 | ||
| 800 | if (es == nullptr) | 801 | if (es == nullptr) |
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 83c184750..60ea9ad12 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp | |||
| @@ -67,7 +67,7 @@ std::string NACP::GetDeveloperName(Language language) const { | |||
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | u64 NACP::GetTitleId() const { | 69 | u64 NACP::GetTitleId() const { |
| 70 | return raw.title_id; | 70 | return raw.save_data_owner_id; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | u64 NACP::GetDLCBaseTitleId() const { | 73 | u64 NACP::GetDLCBaseTitleId() const { |
| @@ -80,11 +80,11 @@ std::string NACP::GetVersionString() const { | |||
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | u64 NACP::GetDefaultNormalSaveSize() const { | 82 | u64 NACP::GetDefaultNormalSaveSize() const { |
| 83 | return raw.normal_save_data_size; | 83 | return raw.user_account_save_data_size; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | u64 NACP::GetDefaultJournalSaveSize() const { | 86 | u64 NACP::GetDefaultJournalSaveSize() const { |
| 87 | return raw.journal_sava_data_size; | 87 | return raw.user_account_save_data_journal_size; |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | std::vector<u8> NACP::GetRawBytes() const { | 90 | std::vector<u8> NACP::GetRawBytes() const { |
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 7b9cdc910..280710ddf 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h | |||
| @@ -38,23 +38,35 @@ struct RawNACP { | |||
| 38 | u8 video_capture_mode; | 38 | u8 video_capture_mode; |
| 39 | bool data_loss_confirmation; | 39 | bool data_loss_confirmation; |
| 40 | INSERT_PADDING_BYTES(1); | 40 | INSERT_PADDING_BYTES(1); |
| 41 | u64_le title_id; | 41 | u64_le presence_group_id; |
| 42 | std::array<u8, 0x20> rating_age; | 42 | std::array<u8, 0x20> rating_age; |
| 43 | std::array<char, 0x10> version_string; | 43 | std::array<char, 0x10> version_string; |
| 44 | u64_le dlc_base_title_id; | 44 | u64_le dlc_base_title_id; |
| 45 | u64_le title_id_2; | 45 | u64_le save_data_owner_id; |
| 46 | u64_le normal_save_data_size; | 46 | u64_le user_account_save_data_size; |
| 47 | u64_le journal_sava_data_size; | 47 | u64_le user_account_save_data_journal_size; |
| 48 | INSERT_PADDING_BYTES(0x18); | 48 | u64_le device_save_data_size; |
| 49 | u64_le product_code; | 49 | u64_le device_save_data_journal_size; |
| 50 | u64_le bcat_delivery_cache_storage_size; | ||
| 51 | char application_error_code_category[8]; | ||
| 50 | std::array<u64_le, 0x8> local_communication; | 52 | std::array<u64_le, 0x8> local_communication; |
| 51 | u8 logo_type; | 53 | u8 logo_type; |
| 52 | u8 logo_handling; | 54 | u8 logo_handling; |
| 53 | bool runtime_add_on_content_install; | 55 | bool runtime_add_on_content_install; |
| 54 | INSERT_PADDING_BYTES(5); | 56 | INSERT_PADDING_BYTES(5); |
| 55 | u64_le title_id_update; | 57 | u64_le seed_for_pseudo_device_id; |
| 56 | std::array<u8, 0x40> bcat_passphrase; | 58 | std::array<u8, 0x41> bcat_passphrase; |
| 57 | INSERT_PADDING_BYTES(0xEC0); | 59 | INSERT_PADDING_BYTES(7); |
| 60 | u64_le user_account_save_data_max_size; | ||
| 61 | u64_le user_account_save_data_max_journal_size; | ||
| 62 | u64_le device_save_data_max_size; | ||
| 63 | u64_le device_save_data_max_journal_size; | ||
| 64 | u64_le temporary_storage_size; | ||
| 65 | u64_le cache_storage_size; | ||
| 66 | u64_le cache_storage_journal_size; | ||
| 67 | u64_le cache_storage_data_and_journal_max_size; | ||
| 68 | u64_le cache_storage_max_index; | ||
| 69 | INSERT_PADDING_BYTES(0xE70); | ||
| 58 | }; | 70 | }; |
| 59 | static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); | 71 | static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); |
| 60 | 72 | ||
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 47b7526c7..d126ae8dd 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | #include <cstring> | 25 | #include <cstring> |
| 26 | #include <string_view> | ||
| 26 | #include "common/alignment.h" | 27 | #include "common/alignment.h" |
| 27 | #include "common/assert.h" | 28 | #include "common/assert.h" |
| 28 | #include "core/file_sys/fsmitm_romfsbuild.h" | 29 | #include "core/file_sys/fsmitm_romfsbuild.h" |
| @@ -97,7 +98,8 @@ struct RomFSBuildFileContext { | |||
| 97 | VirtualFile source; | 98 | VirtualFile source; |
| 98 | }; | 99 | }; |
| 99 | 100 | ||
| 100 | static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) { | 101 | static u32 romfs_calc_path_hash(u32 parent, std::string_view path, u32 start, |
| 102 | std::size_t path_len) { | ||
| 101 | u32 hash = parent ^ 123456789; | 103 | u32 hash = parent ^ 123456789; |
| 102 | for (u32 i = 0; i < path_len; i++) { | 104 | for (u32 i = 0; i < path_len; i++) { |
| 103 | hash = (hash >> 5) | (hash << 27); | 105 | hash = (hash >> 5) | (hash << 27); |
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 6f34b7836..93d0df6b9 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp | |||
| @@ -10,14 +10,6 @@ | |||
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | bool operator>=(TitleType lhs, TitleType rhs) { | ||
| 14 | return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs); | ||
| 15 | } | ||
| 16 | |||
| 17 | bool operator<=(TitleType lhs, TitleType rhs) { | ||
| 18 | return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs); | ||
| 19 | } | ||
| 20 | |||
| 21 | CNMT::CNMT(VirtualFile file) { | 13 | CNMT::CNMT(VirtualFile file) { |
| 22 | if (file->ReadObject(&header) != sizeof(CNMTHeader)) | 14 | if (file->ReadObject(&header) != sizeof(CNMTHeader)) |
| 23 | return; | 15 | return; |
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index a05d155f4..50bf38471 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h | |||
| @@ -29,9 +29,6 @@ enum class TitleType : u8 { | |||
| 29 | DeltaTitle = 0x83, | 29 | DeltaTitle = 0x83, |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | bool operator>=(TitleType lhs, TitleType rhs); | ||
| 33 | bool operator<=(TitleType lhs, TitleType rhs); | ||
| 34 | |||
| 35 | enum class ContentRecordType : u8 { | 32 | enum class ContentRecordType : u8 { |
| 36 | Meta = 0, | 33 | Meta = 0, |
| 37 | Program = 1, | 34 | Program = 1, |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index e11217708..78dbadee3 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/file_util.h" | 10 | #include "common/file_util.h" |
| 11 | #include "common/hex_util.h" | 11 | #include "common/hex_util.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "core/core.h" | ||
| 13 | #include "core/file_sys/content_archive.h" | 14 | #include "core/file_sys/content_archive.h" |
| 14 | #include "core/file_sys/control_metadata.h" | 15 | #include "core/file_sys/control_metadata.h" |
| 15 | #include "core/file_sys/ips_layer.h" | 16 | #include "core/file_sys/ips_layer.h" |
| @@ -69,7 +70,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 69 | } | 70 | } |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | const auto installed = Service::FileSystem::GetUnionContents(); | 73 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 73 | 74 | ||
| 74 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 75 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| 75 | const auto update_disabled = | 76 | const auto update_disabled = |
| @@ -155,7 +156,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD | |||
| 155 | return out; | 156 | return out; |
| 156 | } | 157 | } |
| 157 | 158 | ||
| 158 | std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { | 159 | std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::string& name) const { |
| 159 | if (nso.size() < sizeof(Loader::NSOHeader)) { | 160 | if (nso.size() < sizeof(Loader::NSOHeader)) { |
| 160 | return nso; | 161 | return nso; |
| 161 | } | 162 | } |
| @@ -171,18 +172,19 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { | |||
| 171 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | 172 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); |
| 172 | 173 | ||
| 173 | if (Settings::values.dump_nso) { | 174 | if (Settings::values.dump_nso) { |
| 174 | LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id); | 175 | LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, |
| 176 | title_id); | ||
| 175 | const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | 177 | const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); |
| 176 | if (dump_dir != nullptr) { | 178 | if (dump_dir != nullptr) { |
| 177 | const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); | 179 | const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); |
| 178 | const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id)); | 180 | const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); |
| 179 | 181 | ||
| 180 | file->Resize(nso.size()); | 182 | file->Resize(nso.size()); |
| 181 | file->WriteBytes(nso); | 183 | file->WriteBytes(nso); |
| 182 | } | 184 | } |
| 183 | } | 185 | } |
| 184 | 186 | ||
| 185 | LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); | 187 | LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); |
| 186 | 188 | ||
| 187 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 189 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); |
| 188 | auto patch_dirs = load_dir->GetSubdirectories(); | 190 | auto patch_dirs = load_dir->GetSubdirectories(); |
| @@ -345,7 +347,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content | |||
| 345 | if (romfs == nullptr) | 347 | if (romfs == nullptr) |
| 346 | return romfs; | 348 | return romfs; |
| 347 | 349 | ||
| 348 | const auto installed = Service::FileSystem::GetUnionContents(); | 350 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 349 | 351 | ||
| 350 | // Game Updates | 352 | // Game Updates |
| 351 | const auto update_tid = GetUpdateTitleID(title_id); | 353 | const auto update_tid = GetUpdateTitleID(title_id); |
| @@ -392,7 +394,7 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | |||
| 392 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( | 394 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( |
| 393 | VirtualFile update_raw) const { | 395 | VirtualFile update_raw) const { |
| 394 | std::map<std::string, std::string, std::less<>> out; | 396 | std::map<std::string, std::string, std::less<>> out; |
| 395 | const auto installed = Service::FileSystem::GetUnionContents(); | 397 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 396 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 398 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| 397 | 399 | ||
| 398 | // Game Updates | 400 | // Game Updates |
| @@ -466,10 +468,10 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 466 | 468 | ||
| 467 | // DLC | 469 | // DLC |
| 468 | const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); | 470 | const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); |
| 469 | std::vector<RegisteredCacheEntry> dlc_match; | 471 | std::vector<ContentProviderEntry> dlc_match; |
| 470 | dlc_match.reserve(dlc_entries.size()); | 472 | dlc_match.reserve(dlc_entries.size()); |
| 471 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), | 473 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), |
| 472 | [this, &installed](const RegisteredCacheEntry& entry) { | 474 | [this, &installed](const ContentProviderEntry& entry) { |
| 473 | return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && | 475 | return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && |
| 474 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; | 476 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; |
| 475 | }); | 477 | }); |
| @@ -492,7 +494,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 492 | } | 494 | } |
| 493 | 495 | ||
| 494 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 496 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { |
| 495 | const auto installed{Service::FileSystem::GetUnionContents()}; | 497 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 496 | 498 | ||
| 497 | const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); | 499 | const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); |
| 498 | if (base_control_nca == nullptr) | 500 | if (base_control_nca == nullptr) |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index de2672c76..769f8c6f0 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -44,7 +44,7 @@ public: | |||
| 44 | // Currently tracked NSO patches: | 44 | // Currently tracked NSO patches: |
| 45 | // - IPS | 45 | // - IPS |
| 46 | // - IPSwitch | 46 | // - IPSwitch |
| 47 | std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; | 47 | std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const; |
| 48 | 48 | ||
| 49 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. | 49 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. |
| 50 | // Used to prevent expensive copies in NSO loader. | 50 | // Used to prevent expensive copies in NSO loader. |
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index d3e00437f..d863253f8 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstddef> | 5 | #include <cstddef> |
| 6 | #include <cstring> | ||
| 7 | #include <vector> | 6 | #include <vector> |
| 8 | 7 | ||
| 9 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -17,28 +16,30 @@ ProgramMetadata::ProgramMetadata() = default; | |||
| 17 | ProgramMetadata::~ProgramMetadata() = default; | 16 | ProgramMetadata::~ProgramMetadata() = default; |
| 18 | 17 | ||
| 19 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | 18 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { |
| 20 | std::size_t total_size = static_cast<std::size_t>(file->GetSize()); | 19 | const std::size_t total_size = file->GetSize(); |
| 21 | if (total_size < sizeof(Header)) | 20 | if (total_size < sizeof(Header)) { |
| 22 | return Loader::ResultStatus::ErrorBadNPDMHeader; | 21 | return Loader::ResultStatus::ErrorBadNPDMHeader; |
| 22 | } | ||
| 23 | 23 | ||
| 24 | // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. | 24 | if (sizeof(Header) != file->ReadObject(&npdm_header)) { |
| 25 | std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); | ||
| 26 | if (sizeof(Header) != npdm_header_data.size()) | ||
| 27 | return Loader::ResultStatus::ErrorBadNPDMHeader; | 25 | return Loader::ResultStatus::ErrorBadNPDMHeader; |
| 28 | std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); | 26 | } |
| 29 | 27 | ||
| 30 | std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); | 28 | if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) { |
| 31 | if (sizeof(AcidHeader) != acid_header_data.size()) | ||
| 32 | return Loader::ResultStatus::ErrorBadACIDHeader; | 29 | return Loader::ResultStatus::ErrorBadACIDHeader; |
| 33 | std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); | 30 | } |
| 34 | 31 | ||
| 35 | if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) | 32 | if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) { |
| 36 | return Loader::ResultStatus::ErrorBadACIHeader; | 33 | return Loader::ResultStatus::ErrorBadACIHeader; |
| 34 | } | ||
| 37 | 35 | ||
| 38 | if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) | 36 | if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) { |
| 39 | return Loader::ResultStatus::ErrorBadFileAccessControl; | 37 | return Loader::ResultStatus::ErrorBadFileAccessControl; |
| 40 | if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) | 38 | } |
| 39 | |||
| 40 | if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) { | ||
| 41 | return Loader::ResultStatus::ErrorBadFileAccessHeader; | 41 | return Loader::ResultStatus::ErrorBadFileAccessHeader; |
| 42 | } | ||
| 42 | 43 | ||
| 43 | aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); | 44 | aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); |
| 44 | const u64 read_size = aci_header.kac_size; | 45 | const u64 read_size = aci_header.kac_size; |
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 0033ba347..7de5b9cf9 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -58,7 +58,6 @@ public: | |||
| 58 | void Print() const; | 58 | void Print() const; |
| 59 | 59 | ||
| 60 | private: | 60 | private: |
| 61 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 62 | struct Header { | 61 | struct Header { |
| 63 | std::array<char, 4> magic; | 62 | std::array<char, 4> magic; |
| 64 | std::array<u8, 8> reserved; | 63 | std::array<u8, 8> reserved; |
| @@ -85,7 +84,6 @@ private: | |||
| 85 | 84 | ||
| 86 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); | 85 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); |
| 87 | 86 | ||
| 88 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 89 | struct AcidHeader { | 87 | struct AcidHeader { |
| 90 | std::array<u8, 0x100> signature; | 88 | std::array<u8, 0x100> signature; |
| 91 | std::array<u8, 0x100> nca_modulus; | 89 | std::array<u8, 0x100> nca_modulus; |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 1c6bacace..3946ff871 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -23,19 +23,19 @@ namespace FileSys { | |||
| 23 | // The size of blocks to use when vfs raw copying into nand. | 23 | // The size of blocks to use when vfs raw copying into nand. |
| 24 | constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000; | 24 | constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000; |
| 25 | 25 | ||
| 26 | std::string RegisteredCacheEntry::DebugInfo() const { | 26 | std::string ContentProviderEntry::DebugInfo() const { |
| 27 | return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); | 27 | return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { | 30 | bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) { |
| 31 | return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); | 31 | return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { | 34 | bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) { |
| 35 | return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); | 35 | return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { | 38 | bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) { |
| 39 | return !operator==(lhs, rhs); | 39 | return !operator==(lhs, rhs); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| @@ -84,7 +84,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) { | |||
| 84 | return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); | 84 | return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { | 87 | ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { |
| 88 | switch (type) { | 88 | switch (type) { |
| 89 | case NCAContentType::Program: | 89 | case NCAContentType::Program: |
| 90 | // TODO(DarkLordZach): Differentiate between Program and Patch | 90 | // TODO(DarkLordZach): Differentiate between Program and Patch |
| @@ -104,6 +104,28 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { | |||
| 104 | } | 104 | } |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | ContentProvider::~ContentProvider() = default; | ||
| 108 | |||
| 109 | bool ContentProvider::HasEntry(ContentProviderEntry entry) const { | ||
| 110 | return HasEntry(entry.title_id, entry.type); | ||
| 111 | } | ||
| 112 | |||
| 113 | VirtualFile ContentProvider::GetEntryUnparsed(ContentProviderEntry entry) const { | ||
| 114 | return GetEntryUnparsed(entry.title_id, entry.type); | ||
| 115 | } | ||
| 116 | |||
| 117 | VirtualFile ContentProvider::GetEntryRaw(ContentProviderEntry entry) const { | ||
| 118 | return GetEntryRaw(entry.title_id, entry.type); | ||
| 119 | } | ||
| 120 | |||
| 121 | std::unique_ptr<NCA> ContentProvider::GetEntry(ContentProviderEntry entry) const { | ||
| 122 | return GetEntry(entry.title_id, entry.type); | ||
| 123 | } | ||
| 124 | |||
| 125 | std::vector<ContentProviderEntry> ContentProvider::ListEntries() const { | ||
| 126 | return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); | ||
| 127 | } | ||
| 128 | |||
| 107 | VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | 129 | VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, |
| 108 | std::string_view path) const { | 130 | std::string_view path) const { |
| 109 | const auto file = dir->GetFileRelative(path); | 131 | const auto file = dir->GetFileRelative(path); |
| @@ -161,8 +183,8 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { | |||
| 161 | return file; | 183 | return file; |
| 162 | } | 184 | } |
| 163 | 185 | ||
| 164 | static std::optional<NcaID> CheckMapForContentRecord( | 186 | static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id, |
| 165 | const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { | 187 | ContentRecordType type) { |
| 166 | if (map.find(title_id) == map.end()) | 188 | if (map.find(title_id) == map.end()) |
| 167 | return {}; | 189 | return {}; |
| 168 | 190 | ||
| @@ -268,7 +290,7 @@ void RegisteredCache::Refresh() { | |||
| 268 | AccumulateYuzuMeta(); | 290 | AccumulateYuzuMeta(); |
| 269 | } | 291 | } |
| 270 | 292 | ||
| 271 | RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function) | 293 | RegisteredCache::RegisteredCache(VirtualDir dir_, ContentProviderParsingFunction parsing_function) |
| 272 | : dir(std::move(dir_)), parser(std::move(parsing_function)) { | 294 | : dir(std::move(dir_)), parser(std::move(parsing_function)) { |
| 273 | Refresh(); | 295 | Refresh(); |
| 274 | } | 296 | } |
| @@ -279,19 +301,11 @@ bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const { | |||
| 279 | return GetEntryRaw(title_id, type) != nullptr; | 301 | return GetEntryRaw(title_id, type) != nullptr; |
| 280 | } | 302 | } |
| 281 | 303 | ||
| 282 | bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const { | ||
| 283 | return GetEntryRaw(entry) != nullptr; | ||
| 284 | } | ||
| 285 | |||
| 286 | VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { | 304 | VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { |
| 287 | const auto id = GetNcaIDFromMetadata(title_id, type); | 305 | const auto id = GetNcaIDFromMetadata(title_id, type); |
| 288 | return id ? GetFileAtID(*id) : nullptr; | 306 | return id ? GetFileAtID(*id) : nullptr; |
| 289 | } | 307 | } |
| 290 | 308 | ||
| 291 | VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const { | ||
| 292 | return GetEntryUnparsed(entry.title_id, entry.type); | ||
| 293 | } | ||
| 294 | |||
| 295 | std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { | 309 | std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { |
| 296 | const auto meta_iter = meta.find(title_id); | 310 | const auto meta_iter = meta.find(title_id); |
| 297 | if (meta_iter != meta.end()) | 311 | if (meta_iter != meta.end()) |
| @@ -309,10 +323,6 @@ VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) c | |||
| 309 | return id ? parser(GetFileAtID(*id), *id) : nullptr; | 323 | return id ? parser(GetFileAtID(*id), *id) : nullptr; |
| 310 | } | 324 | } |
| 311 | 325 | ||
| 312 | VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const { | ||
| 313 | return GetEntryRaw(entry.title_id, entry.type); | ||
| 314 | } | ||
| 315 | |||
| 316 | std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { | 326 | std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { |
| 317 | const auto raw = GetEntryRaw(title_id, type); | 327 | const auto raw = GetEntryRaw(title_id, type); |
| 318 | if (raw == nullptr) | 328 | if (raw == nullptr) |
| @@ -320,10 +330,6 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t | |||
| 320 | return std::make_unique<NCA>(raw, nullptr, 0, keys); | 330 | return std::make_unique<NCA>(raw, nullptr, 0, keys); |
| 321 | } | 331 | } |
| 322 | 332 | ||
| 323 | std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { | ||
| 324 | return GetEntry(entry.title_id, entry.type); | ||
| 325 | } | ||
| 326 | |||
| 327 | template <typename T> | 333 | template <typename T> |
| 328 | void RegisteredCache::IterateAllMetadata( | 334 | void RegisteredCache::IterateAllMetadata( |
| 329 | std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc, | 335 | std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc, |
| @@ -348,25 +354,14 @@ void RegisteredCache::IterateAllMetadata( | |||
| 348 | } | 354 | } |
| 349 | } | 355 | } |
| 350 | 356 | ||
| 351 | std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const { | 357 | std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter( |
| 352 | std::vector<RegisteredCacheEntry> out; | ||
| 353 | IterateAllMetadata<RegisteredCacheEntry>( | ||
| 354 | out, | ||
| 355 | [](const CNMT& c, const ContentRecord& r) { | ||
| 356 | return RegisteredCacheEntry{c.GetTitleID(), r.type}; | ||
| 357 | }, | ||
| 358 | [](const CNMT& c, const ContentRecord& r) { return true; }); | ||
| 359 | return out; | ||
| 360 | } | ||
| 361 | |||
| 362 | std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( | ||
| 363 | std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, | 358 | std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, |
| 364 | std::optional<u64> title_id) const { | 359 | std::optional<u64> title_id) const { |
| 365 | std::vector<RegisteredCacheEntry> out; | 360 | std::vector<ContentProviderEntry> out; |
| 366 | IterateAllMetadata<RegisteredCacheEntry>( | 361 | IterateAllMetadata<ContentProviderEntry>( |
| 367 | out, | 362 | out, |
| 368 | [](const CNMT& c, const ContentRecord& r) { | 363 | [](const CNMT& c, const ContentRecord& r) { |
| 369 | return RegisteredCacheEntry{c.GetTitleID(), r.type}; | 364 | return ContentProviderEntry{c.GetTitleID(), r.type}; |
| 370 | }, | 365 | }, |
| 371 | [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { | 366 | [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { |
| 372 | if (title_type && *title_type != c.GetType()) | 367 | if (title_type && *title_type != c.GetType()) |
| @@ -521,37 +516,56 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { | |||
| 521 | }) != yuzu_meta.end(); | 516 | }) != yuzu_meta.end(); |
| 522 | } | 517 | } |
| 523 | 518 | ||
| 524 | RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches) | 519 | ContentProviderUnion::~ContentProviderUnion() = default; |
| 525 | : caches(std::move(caches)) {} | ||
| 526 | 520 | ||
| 527 | void RegisteredCacheUnion::Refresh() { | 521 | void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) { |
| 528 | for (const auto& c : caches) | 522 | providers[slot] = provider; |
| 529 | c->Refresh(); | ||
| 530 | } | 523 | } |
| 531 | 524 | ||
| 532 | bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const { | 525 | void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) { |
| 533 | return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) { | 526 | providers[slot] = nullptr; |
| 534 | return cache->HasEntry(title_id, type); | ||
| 535 | }); | ||
| 536 | } | 527 | } |
| 537 | 528 | ||
| 538 | bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { | 529 | void ContentProviderUnion::Refresh() { |
| 539 | return HasEntry(entry.title_id, entry.type); | 530 | for (auto& provider : providers) { |
| 531 | if (provider.second == nullptr) | ||
| 532 | continue; | ||
| 533 | |||
| 534 | provider.second->Refresh(); | ||
| 535 | } | ||
| 540 | } | 536 | } |
| 541 | 537 | ||
| 542 | std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { | 538 | bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const { |
| 543 | for (const auto& c : caches) { | 539 | for (const auto& provider : providers) { |
| 544 | const auto res = c->GetEntryVersion(title_id); | 540 | if (provider.second == nullptr) |
| 545 | if (res) | 541 | continue; |
| 542 | |||
| 543 | if (provider.second->HasEntry(title_id, type)) | ||
| 544 | return true; | ||
| 545 | } | ||
| 546 | |||
| 547 | return false; | ||
| 548 | } | ||
| 549 | |||
| 550 | std::optional<u32> ContentProviderUnion::GetEntryVersion(u64 title_id) const { | ||
| 551 | for (const auto& provider : providers) { | ||
| 552 | if (provider.second == nullptr) | ||
| 553 | continue; | ||
| 554 | |||
| 555 | const auto res = provider.second->GetEntryVersion(title_id); | ||
| 556 | if (res != std::nullopt) | ||
| 546 | return res; | 557 | return res; |
| 547 | } | 558 | } |
| 548 | 559 | ||
| 549 | return {}; | 560 | return std::nullopt; |
| 550 | } | 561 | } |
| 551 | 562 | ||
| 552 | VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { | 563 | VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { |
| 553 | for (const auto& c : caches) { | 564 | for (const auto& provider : providers) { |
| 554 | const auto res = c->GetEntryUnparsed(title_id, type); | 565 | if (provider.second == nullptr) |
| 566 | continue; | ||
| 567 | |||
| 568 | const auto res = provider.second->GetEntryUnparsed(title_id, type); | ||
| 555 | if (res != nullptr) | 569 | if (res != nullptr) |
| 556 | return res; | 570 | return res; |
| 557 | } | 571 | } |
| @@ -559,13 +573,12 @@ VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordTy | |||
| 559 | return nullptr; | 573 | return nullptr; |
| 560 | } | 574 | } |
| 561 | 575 | ||
| 562 | VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const { | 576 | VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { |
| 563 | return GetEntryUnparsed(entry.title_id, entry.type); | 577 | for (const auto& provider : providers) { |
| 564 | } | 578 | if (provider.second == nullptr) |
| 579 | continue; | ||
| 565 | 580 | ||
| 566 | VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { | 581 | const auto res = provider.second->GetEntryRaw(title_id, type); |
| 567 | for (const auto& c : caches) { | ||
| 568 | const auto res = c->GetEntryRaw(title_id, type); | ||
| 569 | if (res != nullptr) | 582 | if (res != nullptr) |
| 570 | return res; | 583 | return res; |
| 571 | } | 584 | } |
| @@ -573,30 +586,56 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType ty | |||
| 573 | return nullptr; | 586 | return nullptr; |
| 574 | } | 587 | } |
| 575 | 588 | ||
| 576 | VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const { | 589 | std::unique_ptr<NCA> ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const { |
| 577 | return GetEntryRaw(entry.title_id, entry.type); | 590 | for (const auto& provider : providers) { |
| 578 | } | 591 | if (provider.second == nullptr) |
| 592 | continue; | ||
| 579 | 593 | ||
| 580 | std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { | 594 | auto res = provider.second->GetEntry(title_id, type); |
| 581 | const auto raw = GetEntryRaw(title_id, type); | 595 | if (res != nullptr) |
| 582 | if (raw == nullptr) | 596 | return res; |
| 583 | return nullptr; | 597 | } |
| 584 | return std::make_unique<NCA>(raw); | 598 | |
| 599 | return nullptr; | ||
| 585 | } | 600 | } |
| 586 | 601 | ||
| 587 | std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { | 602 | std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter( |
| 588 | return GetEntry(entry.title_id, entry.type); | 603 | std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, |
| 604 | std::optional<u64> title_id) const { | ||
| 605 | std::vector<ContentProviderEntry> out; | ||
| 606 | |||
| 607 | for (const auto& provider : providers) { | ||
| 608 | if (provider.second == nullptr) | ||
| 609 | continue; | ||
| 610 | |||
| 611 | const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id); | ||
| 612 | std::copy(vec.begin(), vec.end(), std::back_inserter(out)); | ||
| 613 | } | ||
| 614 | |||
| 615 | std::sort(out.begin(), out.end()); | ||
| 616 | out.erase(std::unique(out.begin(), out.end()), out.end()); | ||
| 617 | return out; | ||
| 589 | } | 618 | } |
| 590 | 619 | ||
| 591 | std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { | 620 | std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> |
| 592 | std::vector<RegisteredCacheEntry> out; | 621 | ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnionSlot> origin, |
| 593 | for (const auto& c : caches) { | 622 | std::optional<TitleType> title_type, |
| 594 | c->IterateAllMetadata<RegisteredCacheEntry>( | 623 | std::optional<ContentRecordType> record_type, |
| 595 | out, | 624 | std::optional<u64> title_id) const { |
| 596 | [](const CNMT& c, const ContentRecord& r) { | 625 | std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> out; |
| 597 | return RegisteredCacheEntry{c.GetTitleID(), r.type}; | 626 | |
| 598 | }, | 627 | for (const auto& provider : providers) { |
| 599 | [](const CNMT& c, const ContentRecord& r) { return true; }); | 628 | if (provider.second == nullptr) |
| 629 | continue; | ||
| 630 | |||
| 631 | if (origin.has_value() && *origin != provider.first) | ||
| 632 | continue; | ||
| 633 | |||
| 634 | const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id); | ||
| 635 | std::transform(vec.begin(), vec.end(), std::back_inserter(out), | ||
| 636 | [&provider](const ContentProviderEntry& entry) { | ||
| 637 | return std::make_pair(provider.first, entry); | ||
| 638 | }); | ||
| 600 | } | 639 | } |
| 601 | 640 | ||
| 602 | std::sort(out.begin(), out.end()); | 641 | std::sort(out.begin(), out.end()); |
| @@ -604,25 +643,61 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { | |||
| 604 | return out; | 643 | return out; |
| 605 | } | 644 | } |
| 606 | 645 | ||
| 607 | std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( | 646 | ManualContentProvider::~ManualContentProvider() = default; |
| 647 | |||
| 648 | void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type, | ||
| 649 | u64 title_id, VirtualFile file) { | ||
| 650 | entries.insert_or_assign({title_type, content_type, title_id}, file); | ||
| 651 | } | ||
| 652 | |||
| 653 | void ManualContentProvider::ClearAllEntries() { | ||
| 654 | entries.clear(); | ||
| 655 | } | ||
| 656 | |||
| 657 | void ManualContentProvider::Refresh() {} | ||
| 658 | |||
| 659 | bool ManualContentProvider::HasEntry(u64 title_id, ContentRecordType type) const { | ||
| 660 | return GetEntryRaw(title_id, type) != nullptr; | ||
| 661 | } | ||
| 662 | |||
| 663 | std::optional<u32> ManualContentProvider::GetEntryVersion(u64 title_id) const { | ||
| 664 | return std::nullopt; | ||
| 665 | } | ||
| 666 | |||
| 667 | VirtualFile ManualContentProvider::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { | ||
| 668 | return GetEntryRaw(title_id, type); | ||
| 669 | } | ||
| 670 | |||
| 671 | VirtualFile ManualContentProvider::GetEntryRaw(u64 title_id, ContentRecordType type) const { | ||
| 672 | const auto iter = | ||
| 673 | std::find_if(entries.begin(), entries.end(), [title_id, type](const auto& entry) { | ||
| 674 | const auto [title_type, content_type, e_title_id] = entry.first; | ||
| 675 | return content_type == type && e_title_id == title_id; | ||
| 676 | }); | ||
| 677 | if (iter == entries.end()) | ||
| 678 | return nullptr; | ||
| 679 | return iter->second; | ||
| 680 | } | ||
| 681 | |||
| 682 | std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecordType type) const { | ||
| 683 | const auto res = GetEntryRaw(title_id, type); | ||
| 684 | if (res == nullptr) | ||
| 685 | return nullptr; | ||
| 686 | return std::make_unique<NCA>(res, nullptr, 0, keys); | ||
| 687 | } | ||
| 688 | |||
| 689 | std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter( | ||
| 608 | std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, | 690 | std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, |
| 609 | std::optional<u64> title_id) const { | 691 | std::optional<u64> title_id) const { |
| 610 | std::vector<RegisteredCacheEntry> out; | 692 | std::vector<ContentProviderEntry> out; |
| 611 | for (const auto& c : caches) { | 693 | |
| 612 | c->IterateAllMetadata<RegisteredCacheEntry>( | 694 | for (const auto& entry : entries) { |
| 613 | out, | 695 | const auto [e_title_type, e_content_type, e_title_id] = entry.first; |
| 614 | [](const CNMT& c, const ContentRecord& r) { | 696 | if ((title_type == std::nullopt || e_title_type == *title_type) && |
| 615 | return RegisteredCacheEntry{c.GetTitleID(), r.type}; | 697 | (record_type == std::nullopt || e_content_type == *record_type) && |
| 616 | }, | 698 | (title_id == std::nullopt || e_title_id == *title_id)) { |
| 617 | [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { | 699 | out.emplace_back(ContentProviderEntry{e_title_id, e_content_type}); |
| 618 | if (title_type && *title_type != c.GetType()) | 700 | } |
| 619 | return false; | ||
| 620 | if (record_type && *record_type != r.type) | ||
| 621 | return false; | ||
| 622 | if (title_id && *title_id != c.GetTitleID()) | ||
| 623 | return false; | ||
| 624 | return true; | ||
| 625 | }); | ||
| 626 | } | 701 | } |
| 627 | 702 | ||
| 628 | std::sort(out.begin(), out.end()); | 703 | std::sort(out.begin(), out.end()); |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 3b77af4e0..ec9052653 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -21,12 +21,13 @@ class NSP; | |||
| 21 | class XCI; | 21 | class XCI; |
| 22 | 22 | ||
| 23 | enum class ContentRecordType : u8; | 23 | enum class ContentRecordType : u8; |
| 24 | enum class NCAContentType : u8; | ||
| 24 | enum class TitleType : u8; | 25 | enum class TitleType : u8; |
| 25 | 26 | ||
| 26 | struct ContentRecord; | 27 | struct ContentRecord; |
| 27 | 28 | ||
| 28 | using NcaID = std::array<u8, 0x10>; | 29 | using NcaID = std::array<u8, 0x10>; |
| 29 | using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; | 30 | using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; |
| 30 | using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>; | 31 | using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>; |
| 31 | 32 | ||
| 32 | enum class InstallResult { | 33 | enum class InstallResult { |
| @@ -36,7 +37,7 @@ enum class InstallResult { | |||
| 36 | ErrorMetaFailed, | 37 | ErrorMetaFailed, |
| 37 | }; | 38 | }; |
| 38 | 39 | ||
| 39 | struct RegisteredCacheEntry { | 40 | struct ContentProviderEntry { |
| 40 | u64 title_id; | 41 | u64 title_id; |
| 41 | ContentRecordType type; | 42 | ContentRecordType type; |
| 42 | 43 | ||
| @@ -47,12 +48,46 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) { | |||
| 47 | return base_title_id | 0x800; | 48 | return base_title_id | 0x800; |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 51 | ContentRecordType GetCRTypeFromNCAType(NCAContentType type); | ||
| 52 | |||
| 50 | // boost flat_map requires operator< for O(log(n)) lookups. | 53 | // boost flat_map requires operator< for O(log(n)) lookups. |
| 51 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | 54 | bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs); |
| 52 | 55 | ||
| 53 | // std unique requires operator== to identify duplicates. | 56 | // std unique requires operator== to identify duplicates. |
| 54 | bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | 57 | bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs); |
| 55 | bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | 58 | bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs); |
| 59 | |||
| 60 | class ContentProvider { | ||
| 61 | public: | ||
| 62 | virtual ~ContentProvider(); | ||
| 63 | |||
| 64 | virtual void Refresh() = 0; | ||
| 65 | |||
| 66 | virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0; | ||
| 67 | virtual bool HasEntry(ContentProviderEntry entry) const; | ||
| 68 | |||
| 69 | virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0; | ||
| 70 | |||
| 71 | virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0; | ||
| 72 | virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const; | ||
| 73 | |||
| 74 | virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0; | ||
| 75 | virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const; | ||
| 76 | |||
| 77 | virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0; | ||
| 78 | virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const; | ||
| 79 | |||
| 80 | virtual std::vector<ContentProviderEntry> ListEntries() const; | ||
| 81 | |||
| 82 | // If a parameter is not std::nullopt, it will be filtered for from all entries. | ||
| 83 | virtual std::vector<ContentProviderEntry> ListEntriesFilter( | ||
| 84 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, | ||
| 85 | std::optional<u64> title_id = {}) const = 0; | ||
| 86 | |||
| 87 | protected: | ||
| 88 | // A single instance of KeyManager to be used by GetEntry() | ||
| 89 | Core::Crypto::KeyManager keys; | ||
| 90 | }; | ||
| 56 | 91 | ||
| 57 | /* | 92 | /* |
| 58 | * A class that catalogues NCAs in the registered directory structure. | 93 | * A class that catalogues NCAs in the registered directory structure. |
| @@ -67,39 +102,32 @@ bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs | |||
| 67 | * (This impl also supports substituting the nca dir for an nca file, as that's more convenient | 102 | * (This impl also supports substituting the nca dir for an nca file, as that's more convenient |
| 68 | * when 4GB splitting can be ignored.) | 103 | * when 4GB splitting can be ignored.) |
| 69 | */ | 104 | */ |
| 70 | class RegisteredCache { | 105 | class RegisteredCache : public ContentProvider { |
| 71 | friend class RegisteredCacheUnion; | ||
| 72 | |||
| 73 | public: | 106 | public: |
| 74 | // Parsing function defines the conversion from raw file to NCA. If there are other steps | 107 | // Parsing function defines the conversion from raw file to NCA. If there are other steps |
| 75 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom | 108 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom |
| 76 | // parsing function. | 109 | // parsing function. |
| 77 | explicit RegisteredCache(VirtualDir dir, | 110 | explicit RegisteredCache(VirtualDir dir, |
| 78 | RegisteredCacheParsingFunction parsing_function = | 111 | ContentProviderParsingFunction parsing_function = |
| 79 | [](const VirtualFile& file, const NcaID& id) { return file; }); | 112 | [](const VirtualFile& file, const NcaID& id) { return file; }); |
| 80 | ~RegisteredCache(); | 113 | ~RegisteredCache() override; |
| 81 | 114 | ||
| 82 | void Refresh(); | 115 | void Refresh() override; |
| 83 | 116 | ||
| 84 | bool HasEntry(u64 title_id, ContentRecordType type) const; | 117 | bool HasEntry(u64 title_id, ContentRecordType type) const override; |
| 85 | bool HasEntry(RegisteredCacheEntry entry) const; | ||
| 86 | 118 | ||
| 87 | std::optional<u32> GetEntryVersion(u64 title_id) const; | 119 | std::optional<u32> GetEntryVersion(u64 title_id) const override; |
| 88 | 120 | ||
| 89 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; | 121 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override; |
| 90 | VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; | ||
| 91 | 122 | ||
| 92 | VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; | 123 | VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override; |
| 93 | VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; | ||
| 94 | 124 | ||
| 95 | std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; | 125 | std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override; |
| 96 | std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; | ||
| 97 | 126 | ||
| 98 | std::vector<RegisteredCacheEntry> ListEntries() const; | ||
| 99 | // If a parameter is not std::nullopt, it will be filtered for from all entries. | 127 | // If a parameter is not std::nullopt, it will be filtered for from all entries. |
| 100 | std::vector<RegisteredCacheEntry> ListEntriesFilter( | 128 | std::vector<ContentProviderEntry> ListEntriesFilter( |
| 101 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, | 129 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, |
| 102 | std::optional<u64> title_id = {}) const; | 130 | std::optional<u64> title_id = {}) const override; |
| 103 | 131 | ||
| 104 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure | 132 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure |
| 105 | // there is a meta NCA and all of them are accessible. | 133 | // there is a meta NCA and all of them are accessible. |
| @@ -131,46 +159,70 @@ private: | |||
| 131 | bool RawInstallYuzuMeta(const CNMT& cnmt); | 159 | bool RawInstallYuzuMeta(const CNMT& cnmt); |
| 132 | 160 | ||
| 133 | VirtualDir dir; | 161 | VirtualDir dir; |
| 134 | RegisteredCacheParsingFunction parser; | 162 | ContentProviderParsingFunction parser; |
| 135 | Core::Crypto::KeyManager keys; | ||
| 136 | 163 | ||
| 137 | // maps tid -> NcaID of meta | 164 | // maps tid -> NcaID of meta |
| 138 | boost::container::flat_map<u64, NcaID> meta_id; | 165 | std::map<u64, NcaID> meta_id; |
| 139 | // maps tid -> meta | 166 | // maps tid -> meta |
| 140 | boost::container::flat_map<u64, CNMT> meta; | 167 | std::map<u64, CNMT> meta; |
| 141 | // maps tid -> meta for CNMT in yuzu_meta | 168 | // maps tid -> meta for CNMT in yuzu_meta |
| 142 | boost::container::flat_map<u64, CNMT> yuzu_meta; | 169 | std::map<u64, CNMT> yuzu_meta; |
| 143 | }; | 170 | }; |
| 144 | 171 | ||
| 145 | // Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. | 172 | enum class ContentProviderUnionSlot { |
| 146 | class RegisteredCacheUnion { | 173 | SysNAND, ///< System NAND |
| 147 | public: | 174 | UserNAND, ///< User NAND |
| 148 | explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches); | 175 | SDMC, ///< SD Card |
| 149 | 176 | FrontendManual, ///< Frontend-defined game list or similar | |
| 150 | void Refresh(); | 177 | }; |
| 151 | |||
| 152 | bool HasEntry(u64 title_id, ContentRecordType type) const; | ||
| 153 | bool HasEntry(RegisteredCacheEntry entry) const; | ||
| 154 | |||
| 155 | std::optional<u32> GetEntryVersion(u64 title_id) const; | ||
| 156 | |||
| 157 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; | ||
| 158 | VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; | ||
| 159 | |||
| 160 | VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; | ||
| 161 | VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; | ||
| 162 | |||
| 163 | std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; | ||
| 164 | std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; | ||
| 165 | 178 | ||
| 166 | std::vector<RegisteredCacheEntry> ListEntries() const; | 179 | // Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface. |
| 167 | // If a parameter is not std::nullopt, it will be filtered for from all entries. | 180 | class ContentProviderUnion : public ContentProvider { |
| 168 | std::vector<RegisteredCacheEntry> ListEntriesFilter( | 181 | public: |
| 182 | ~ContentProviderUnion() override; | ||
| 183 | |||
| 184 | void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider); | ||
| 185 | void ClearSlot(ContentProviderUnionSlot slot); | ||
| 186 | |||
| 187 | void Refresh() override; | ||
| 188 | bool HasEntry(u64 title_id, ContentRecordType type) const override; | ||
| 189 | std::optional<u32> GetEntryVersion(u64 title_id) const override; | ||
| 190 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override; | ||
| 191 | VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override; | ||
| 192 | std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override; | ||
| 193 | std::vector<ContentProviderEntry> ListEntriesFilter( | ||
| 194 | std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, | ||
| 195 | std::optional<u64> title_id) const override; | ||
| 196 | |||
| 197 | std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin( | ||
| 198 | std::optional<ContentProviderUnionSlot> origin = {}, | ||
| 169 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, | 199 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, |
| 170 | std::optional<u64> title_id = {}) const; | 200 | std::optional<u64> title_id = {}) const; |
| 171 | 201 | ||
| 172 | private: | 202 | private: |
| 173 | std::vector<RegisteredCache*> caches; | 203 | std::map<ContentProviderUnionSlot, ContentProvider*> providers; |
| 204 | }; | ||
| 205 | |||
| 206 | class ManualContentProvider : public ContentProvider { | ||
| 207 | public: | ||
| 208 | ~ManualContentProvider() override; | ||
| 209 | |||
| 210 | void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id, | ||
| 211 | VirtualFile file); | ||
| 212 | void ClearAllEntries(); | ||
| 213 | |||
| 214 | void Refresh() override; | ||
| 215 | bool HasEntry(u64 title_id, ContentRecordType type) const override; | ||
| 216 | std::optional<u32> GetEntryVersion(u64 title_id) const override; | ||
| 217 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override; | ||
| 218 | VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override; | ||
| 219 | std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override; | ||
| 220 | std::vector<ContentProviderEntry> ListEntriesFilter( | ||
| 221 | std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, | ||
| 222 | std::optional<u64> title_id) const override; | ||
| 223 | |||
| 224 | private: | ||
| 225 | std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries; | ||
| 174 | }; | 226 | }; |
| 175 | 227 | ||
| 176 | } // namespace FileSys | 228 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 6ad1e4f86..b2ccb2926 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte | |||
| 48 | 48 | ||
| 49 | switch (storage) { | 49 | switch (storage) { |
| 50 | case StorageId::None: | 50 | case StorageId::None: |
| 51 | res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type); | 51 | res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); |
| 52 | break; | 52 | break; |
| 53 | case StorageId::NandSystem: | 53 | case StorageId::NandSystem: |
| 54 | res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); | 54 | res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 1913dc956..7974b031d 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -16,8 +16,10 @@ namespace FileSys { | |||
| 16 | constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; | 16 | constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; |
| 17 | 17 | ||
| 18 | std::string SaveDataDescriptor::DebugInfo() const { | 18 | std::string SaveDataDescriptor::DebugInfo() const { |
| 19 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]", | 19 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, " |
| 20 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); | 20 | "rank={}, index={}]", |
| 21 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | ||
| 22 | static_cast<u8>(rank), index); | ||
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { | 25 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { |
| @@ -28,7 +30,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save | |||
| 28 | 30 | ||
| 29 | SaveDataFactory::~SaveDataFactory() = default; | 31 | SaveDataFactory::~SaveDataFactory() = default; |
| 30 | 32 | ||
| 31 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { | 33 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) { |
| 32 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 34 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 33 | if (meta.zero_1 != 0) { | 35 | if (meta.zero_1 != 0) { |
| 34 | LOG_WARNING(Service_FS, | 36 | LOG_WARNING(Service_FS, |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 3a1caf292..b73654571 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -32,12 +32,19 @@ enum class SaveDataType : u8 { | |||
| 32 | CacheStorage = 5, | 32 | CacheStorage = 5, |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | enum class SaveDataRank : u8 { | ||
| 36 | Primary, | ||
| 37 | Secondary, | ||
| 38 | }; | ||
| 39 | |||
| 35 | struct SaveDataDescriptor { | 40 | struct SaveDataDescriptor { |
| 36 | u64_le title_id; | 41 | u64_le title_id; |
| 37 | u128 user_id; | 42 | u128 user_id; |
| 38 | u64_le save_id; | 43 | u64_le save_id; |
| 39 | SaveDataType type; | 44 | SaveDataType type; |
| 40 | INSERT_PADDING_BYTES(7); | 45 | SaveDataRank rank; |
| 46 | u16_le index; | ||
| 47 | INSERT_PADDING_BYTES(4); | ||
| 41 | u64_le zero_1; | 48 | u64_le zero_1; |
| 42 | u64_le zero_2; | 49 | u64_le zero_2; |
| 43 | u64_le zero_3; | 50 | u64_le zero_3; |
| @@ -57,7 +64,7 @@ public: | |||
| 57 | explicit SaveDataFactory(VirtualDir dir); | 64 | explicit SaveDataFactory(VirtualDir dir); |
| 58 | ~SaveDataFactory(); | 65 | ~SaveDataFactory(); |
| 59 | 66 | ||
| 60 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); | 67 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta); |
| 61 | 68 | ||
| 62 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; | 69 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; |
| 63 | 70 | ||
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index e1a4210db..c69caae0f 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -143,11 +143,12 @@ std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const { | |||
| 143 | return out; | 143 | return out; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const { | 146 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> |
| 147 | NSP::GetNCAs() const { | ||
| 147 | return ncas; | 148 | return ncas; |
| 148 | } | 149 | } |
| 149 | 150 | ||
| 150 | std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const { | 151 | std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const { |
| 151 | if (extracted) | 152 | if (extracted) |
| 152 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | 153 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); |
| 153 | 154 | ||
| @@ -155,14 +156,14 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const { | |||
| 155 | if (title_id_iter == ncas.end()) | 156 | if (title_id_iter == ncas.end()) |
| 156 | return nullptr; | 157 | return nullptr; |
| 157 | 158 | ||
| 158 | const auto type_iter = title_id_iter->second.find(type); | 159 | const auto type_iter = title_id_iter->second.find({title_type, type}); |
| 159 | if (type_iter == title_id_iter->second.end()) | 160 | if (type_iter == title_id_iter->second.end()) |
| 160 | return nullptr; | 161 | return nullptr; |
| 161 | 162 | ||
| 162 | return type_iter->second; | 163 | return type_iter->second; |
| 163 | } | 164 | } |
| 164 | 165 | ||
| 165 | VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const { | 166 | VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const { |
| 166 | if (extracted) | 167 | if (extracted) |
| 167 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | 168 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); |
| 168 | const auto nca = GetNCA(title_id, type); | 169 | const auto nca = GetNCA(title_id, type); |
| @@ -240,7 +241,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | |||
| 240 | const CNMT cnmt(inner_file); | 241 | const CNMT cnmt(inner_file); |
| 241 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | 242 | auto& ncas_title = ncas[cnmt.GetTitleID()]; |
| 242 | 243 | ||
| 243 | ncas_title[ContentRecordType::Meta] = nca; | 244 | ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca; |
| 244 | for (const auto& rec : cnmt.GetContentRecords()) { | 245 | for (const auto& rec : cnmt.GetContentRecords()) { |
| 245 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); | 246 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); |
| 246 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | 247 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); |
| @@ -258,7 +259,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | |||
| 258 | if (next_nca->GetStatus() == Loader::ResultStatus::Success || | 259 | if (next_nca->GetStatus() == Loader::ResultStatus::Success || |
| 259 | (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && | 260 | (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && |
| 260 | (cnmt.GetTitleID() & 0x800) != 0)) { | 261 | (cnmt.GetTitleID() & 0x800) != 0)) { |
| 261 | ncas_title[rec.type] = std::move(next_nca); | 262 | ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca); |
| 262 | } | 263 | } |
| 263 | } | 264 | } |
| 264 | 265 | ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 9a28ed5bb..ee9b6ce17 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -42,9 +42,12 @@ public: | |||
| 42 | // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML) | 42 | // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML) |
| 43 | std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const; | 43 | std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const; |
| 44 | std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const; | 44 | std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const; |
| 45 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const; | 45 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> GetNCAs() |
| 46 | std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const; | 46 | const; |
| 47 | VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const; | 47 | std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type, |
| 48 | TitleType title_type = TitleType::Application) const; | ||
| 49 | VirtualFile GetNCAFile(u64 title_id, ContentRecordType type, | ||
| 50 | TitleType title_type = TitleType::Application) const; | ||
| 48 | std::vector<Core::Crypto::Key128> GetTitlekey() const; | 51 | std::vector<Core::Crypto::Key128> GetTitlekey() const; |
| 49 | 52 | ||
| 50 | std::vector<VirtualFile> GetFiles() const override; | 53 | std::vector<VirtualFile> GetFiles() const override; |
| @@ -67,7 +70,7 @@ private: | |||
| 67 | 70 | ||
| 68 | std::shared_ptr<PartitionFilesystem> pfs; | 71 | std::shared_ptr<PartitionFilesystem> pfs; |
| 69 | // Map title id -> {map type -> NCA} | 72 | // Map title id -> {map type -> NCA} |
| 70 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; | 73 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; |
| 71 | std::vector<VirtualFile> ticket_files; | 74 | std::vector<VirtualFile> ticket_files; |
| 72 | 75 | ||
| 73 | Core::Crypto::KeyManager keys; | 76 | Core::Crypto::KeyManager keys; |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index d0bcb4660..70a522556 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -13,6 +13,23 @@ | |||
| 13 | namespace Core::Frontend { | 13 | namespace Core::Frontend { |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Represents a graphics context that can be used for background computation or drawing. If the | ||
| 17 | * graphics backend doesn't require the context, then the implementation of these methods can be | ||
| 18 | * stubs | ||
| 19 | */ | ||
| 20 | class GraphicsContext { | ||
| 21 | public: | ||
| 22 | /// Makes the graphics context current for the caller thread | ||
| 23 | virtual void MakeCurrent() = 0; | ||
| 24 | |||
| 25 | /// Releases (dunno if this is the "right" word) the context from the caller thread | ||
| 26 | virtual void DoneCurrent() = 0; | ||
| 27 | |||
| 28 | /// Swap buffers to display the next frame | ||
| 29 | virtual void SwapBuffers() = 0; | ||
| 30 | }; | ||
| 31 | |||
| 32 | /** | ||
| 16 | * Abstraction class used to provide an interface between emulation code and the frontend | 33 | * Abstraction class used to provide an interface between emulation code and the frontend |
| 17 | * (e.g. SDL, QGLWidget, GLFW, etc...). | 34 | * (e.g. SDL, QGLWidget, GLFW, etc...). |
| 18 | * | 35 | * |
| @@ -30,7 +47,7 @@ namespace Core::Frontend { | |||
| 30 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | 47 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please |
| 31 | * re-read the upper points again and think about it if you don't see this. | 48 | * re-read the upper points again and think about it if you don't see this. |
| 32 | */ | 49 | */ |
| 33 | class EmuWindow { | 50 | class EmuWindow : public GraphicsContext { |
| 34 | public: | 51 | public: |
| 35 | /// Data structure to store emuwindow configuration | 52 | /// Data structure to store emuwindow configuration |
| 36 | struct WindowConfig { | 53 | struct WindowConfig { |
| @@ -40,17 +57,21 @@ public: | |||
| 40 | std::pair<unsigned, unsigned> min_client_area_size; | 57 | std::pair<unsigned, unsigned> min_client_area_size; |
| 41 | }; | 58 | }; |
| 42 | 59 | ||
| 43 | /// Swap buffers to display the next frame | ||
| 44 | virtual void SwapBuffers() = 0; | ||
| 45 | |||
| 46 | /// Polls window events | 60 | /// Polls window events |
| 47 | virtual void PollEvents() = 0; | 61 | virtual void PollEvents() = 0; |
| 48 | 62 | ||
| 49 | /// Makes the graphics context current for the caller thread | 63 | /** |
| 50 | virtual void MakeCurrent() = 0; | 64 | * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This |
| 51 | 65 | * context can be used from other threads for background graphics computation. If the frontend | |
| 52 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread | 66 | * is using a graphics backend that doesn't need anything specific to run on a different thread, |
| 53 | virtual void DoneCurrent() = 0; | 67 | * then it can use a stubbed implemenation for GraphicsContext. |
| 68 | * | ||
| 69 | * If the return value is null, then the core should assume that the frontend cannot provide a | ||
| 70 | * Shared Context | ||
| 71 | */ | ||
| 72 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { | ||
| 73 | return nullptr; | ||
| 74 | } | ||
| 54 | 75 | ||
| 55 | /** | 76 | /** |
| 56 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 77 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index dafb32aae..afa812598 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -1030,7 +1030,7 @@ static void Step() { | |||
| 1030 | 1030 | ||
| 1031 | /// Tell the CPU if we hit a memory breakpoint. | 1031 | /// Tell the CPU if we hit a memory breakpoint. |
| 1032 | bool IsMemoryBreak() { | 1032 | bool IsMemoryBreak() { |
| 1033 | if (IsConnected()) { | 1033 | if (!IsConnected()) { |
| 1034 | return false; | 1034 | return false; |
| 1035 | } | 1035 | } |
| 1036 | 1036 | ||
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 68406eb63..ac0e1d796 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -139,10 +139,8 @@ public: | |||
| 139 | context->AddDomainObject(std::move(iface)); | 139 | context->AddDomainObject(std::move(iface)); |
| 140 | } else { | 140 | } else { |
| 141 | auto& kernel = Core::System::GetInstance().Kernel(); | 141 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 142 | auto sessions = | 142 | auto [server, client] = |
| 143 | Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName()); | 143 | Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName()); |
| 144 | auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions); | ||
| 145 | auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions); | ||
| 146 | iface->ClientConnected(server); | 144 | iface->ClientConnected(server); |
| 147 | context->AddMoveObject(std::move(client)); | 145 | context->AddMoveObject(std::move(client)); |
| 148 | } | 146 | } |
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index aa432658e..744b1697d 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp | |||
| @@ -2,8 +2,6 @@ | |||
| 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 <tuple> | ||
| 6 | |||
| 7 | #include "core/hle/kernel/client_port.h" | 5 | #include "core/hle/kernel/client_port.h" |
| 8 | #include "core/hle/kernel/client_session.h" | 6 | #include "core/hle/kernel/client_session.h" |
| 9 | #include "core/hle/kernel/errors.h" | 7 | #include "core/hle/kernel/errors.h" |
| @@ -31,18 +29,18 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { | |||
| 31 | active_sessions++; | 29 | active_sessions++; |
| 32 | 30 | ||
| 33 | // Create a new session pair, let the created sessions inherit the parent port's HLE handler. | 31 | // Create a new session pair, let the created sessions inherit the parent port's HLE handler. |
| 34 | auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this); | 32 | auto [server, client] = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this); |
| 35 | 33 | ||
| 36 | if (server_port->HasHLEHandler()) { | 34 | if (server_port->HasHLEHandler()) { |
| 37 | server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); | 35 | server_port->GetHLEHandler()->ClientConnected(server); |
| 38 | } else { | 36 | } else { |
| 39 | server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions)); | 37 | server_port->AppendPendingSession(server); |
| 40 | } | 38 | } |
| 41 | 39 | ||
| 42 | // Wake the threads waiting on the ServerPort | 40 | // Wake the threads waiting on the ServerPort |
| 43 | server_port->WakeupAllWaitingThreads(); | 41 | server_port->WakeupAllWaitingThreads(); |
| 44 | 42 | ||
| 45 | return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); | 43 | return MakeResult(client); |
| 46 | } | 44 | } |
| 47 | 45 | ||
| 48 | void ClientPort::ConnectionClosed() { | 46 | void ClientPort::ConnectionClosed() { |
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 6cd607206..4921ad4f0 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | return name; | 25 | return name; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | static const HandleType HANDLE_TYPE = HandleType::ClientPort; | 28 | static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort; |
| 29 | HandleType GetHandleType() const override { | 29 | HandleType GetHandleType() const override { |
| 30 | return HANDLE_TYPE; | 30 | return HANDLE_TYPE; |
| 31 | } | 31 | } |
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index b1f39aad7..09cdff588 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h | |||
| @@ -29,7 +29,7 @@ public: | |||
| 29 | return name; | 29 | return name; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | static const HandleType HANDLE_TYPE = HandleType::ClientSession; | 32 | static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession; |
| 33 | HandleType GetHandleType() const override { | 33 | HandleType GetHandleType() const override { |
| 34 | return HANDLE_TYPE; | 34 | return HANDLE_TYPE; |
| 35 | } | 35 | } |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 3f14bfa86..4d58e7c69 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include "core/hle/kernel/thread.h" | 21 | #include "core/hle/kernel/thread.h" |
| 22 | #include "core/hle/lock.h" | 22 | #include "core/hle/lock.h" |
| 23 | #include "core/hle/result.h" | 23 | #include "core/hle/result.h" |
| 24 | #include "core/memory.h" | ||
| 24 | 25 | ||
| 25 | namespace Kernel { | 26 | namespace Kernel { |
| 26 | 27 | ||
| @@ -181,6 +182,7 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) { | |||
| 181 | 182 | ||
| 182 | void KernelCore::MakeCurrentProcess(Process* process) { | 183 | void KernelCore::MakeCurrentProcess(Process* process) { |
| 183 | impl->current_process = process; | 184 | impl->current_process = process; |
| 185 | Memory::SetCurrentPageTable(&process->VMManager().page_table); | ||
| 184 | } | 186 | } |
| 185 | 187 | ||
| 186 | Process* KernelCore::CurrentProcess() { | 188 | Process* KernelCore::CurrentProcess() { |
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp index 217144efc..10431e94c 100644 --- a/src/core/hle/kernel/object.cpp +++ b/src/core/hle/kernel/object.cpp | |||
| @@ -24,7 +24,6 @@ bool Object::IsWaitable() const { | |||
| 24 | case HandleType::WritableEvent: | 24 | case HandleType::WritableEvent: |
| 25 | case HandleType::SharedMemory: | 25 | case HandleType::SharedMemory: |
| 26 | case HandleType::TransferMemory: | 26 | case HandleType::TransferMemory: |
| 27 | case HandleType::AddressArbiter: | ||
| 28 | case HandleType::ResourceLimit: | 27 | case HandleType::ResourceLimit: |
| 29 | case HandleType::ClientPort: | 28 | case HandleType::ClientPort: |
| 30 | case HandleType::ClientSession: | 29 | case HandleType::ClientSession: |
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 3f6baa094..332876c27 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h | |||
| @@ -25,7 +25,6 @@ enum class HandleType : u32 { | |||
| 25 | TransferMemory, | 25 | TransferMemory, |
| 26 | Thread, | 26 | Thread, |
| 27 | Process, | 27 | Process, |
| 28 | AddressArbiter, | ||
| 29 | ResourceLimit, | 28 | ResourceLimit, |
| 30 | ClientPort, | 29 | ClientPort, |
| 31 | ServerPort, | 30 | ServerPort, |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 041267318..4e94048da 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -32,9 +32,6 @@ namespace { | |||
| 32 | * @param priority The priority to give the main thread | 32 | * @param priority The priority to give the main thread |
| 33 | */ | 33 | */ |
| 34 | void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { | 34 | void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { |
| 35 | // Setup page table so we can write to memory | ||
| 36 | Memory::SetCurrentPageTable(&owner_process.VMManager().page_table); | ||
| 37 | |||
| 38 | // Initialize new "main" thread | 35 | // Initialize new "main" thread |
| 39 | const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); | 36 | const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); |
| 40 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, | 37 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, |
| @@ -109,6 +106,8 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { | |||
| 109 | is_64bit_process = metadata.Is64BitProgram(); | 106 | is_64bit_process = metadata.Is64BitProgram(); |
| 110 | 107 | ||
| 111 | vm_manager.Reset(metadata.GetAddressSpaceType()); | 108 | vm_manager.Reset(metadata.GetAddressSpaceType()); |
| 109 | // Ensure that the potentially resized page table is seen by CPU backends. | ||
| 110 | Memory::SetCurrentPageTable(&vm_manager.page_table); | ||
| 112 | 111 | ||
| 113 | const auto& caps = metadata.GetKernelCapabilities(); | 112 | const auto& caps = metadata.GetKernelCapabilities(); |
| 114 | const auto capability_init_result = | 113 | const auto capability_init_result = |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index f060f2a3b..dda52f4c0 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -85,7 +85,7 @@ public: | |||
| 85 | return name; | 85 | return name; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | static const HandleType HANDLE_TYPE = HandleType::Process; | 88 | static constexpr HandleType HANDLE_TYPE = HandleType::Process; |
| 89 | HandleType GetHandleType() const override { | 89 | HandleType GetHandleType() const override { |
| 90 | return HANDLE_TYPE; | 90 | return HANDLE_TYPE; |
| 91 | } | 91 | } |
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 2eb9dcbb7..84215f572 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | return reset_type; | 31 | return reset_type; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | static const HandleType HANDLE_TYPE = HandleType::ReadableEvent; | 34 | static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent; |
| 35 | HandleType GetHandleType() const override { | 35 | HandleType GetHandleType() const override { |
| 36 | return HANDLE_TYPE; | 36 | return HANDLE_TYPE; |
| 37 | } | 37 | } |
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 70e09858a..2613a6bb5 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h | |||
| @@ -41,7 +41,7 @@ public: | |||
| 41 | return GetTypeName(); | 41 | return GetTypeName(); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | static const HandleType HANDLE_TYPE = HandleType::ResourceLimit; | 44 | static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit; |
| 45 | HandleType GetHandleType() const override { | 45 | HandleType GetHandleType() const override { |
| 46 | return HANDLE_TYPE; | 46 | return HANDLE_TYPE; |
| 47 | } | 47 | } |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index ac501bf7f..e8447b69a 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -101,7 +101,6 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 101 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | 101 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); |
| 102 | if (previous_process != thread_owner_process) { | 102 | if (previous_process != thread_owner_process) { |
| 103 | system.Kernel().MakeCurrentProcess(thread_owner_process); | 103 | system.Kernel().MakeCurrentProcess(thread_owner_process); |
| 104 | Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table); | ||
| 105 | } | 104 | } |
| 106 | 105 | ||
| 107 | cpu_core.LoadContext(new_thread->GetContext()); | 106 | cpu_core.LoadContext(new_thread->GetContext()); |
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 708fdf9e1..02e7c60e6 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -39,9 +39,8 @@ void ServerPort::Acquire(Thread* thread) { | |||
| 39 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 39 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( | 42 | ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions, |
| 43 | KernelCore& kernel, u32 max_sessions, std::string name) { | 43 | std::string name) { |
| 44 | |||
| 45 | SharedPtr<ServerPort> server_port(new ServerPort(kernel)); | 44 | SharedPtr<ServerPort> server_port(new ServerPort(kernel)); |
| 46 | SharedPtr<ClientPort> client_port(new ClientPort(kernel)); | 45 | SharedPtr<ClientPort> client_port(new ClientPort(kernel)); |
| 47 | 46 | ||
| @@ -51,7 +50,7 @@ std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortP | |||
| 51 | client_port->max_sessions = max_sessions; | 50 | client_port->max_sessions = max_sessions; |
| 52 | client_port->active_sessions = 0; | 51 | client_port->active_sessions = 0; |
| 53 | 52 | ||
| 54 | return std::make_tuple(std::move(server_port), std::move(client_port)); | 53 | return std::make_pair(std::move(server_port), std::move(client_port)); |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | } // namespace Kernel | 56 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 76293cb8b..dc88a1ebd 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <tuple> | 9 | #include <utility> |
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/kernel/object.h" | 12 | #include "core/hle/kernel/object.h" |
| @@ -23,6 +23,7 @@ class SessionRequestHandler; | |||
| 23 | class ServerPort final : public WaitObject { | 23 | class ServerPort final : public WaitObject { |
| 24 | public: | 24 | public: |
| 25 | using HLEHandler = std::shared_ptr<SessionRequestHandler>; | 25 | using HLEHandler = std::shared_ptr<SessionRequestHandler>; |
| 26 | using PortPair = std::pair<SharedPtr<ServerPort>, SharedPtr<ClientPort>>; | ||
| 26 | 27 | ||
| 27 | /** | 28 | /** |
| 28 | * Creates a pair of ServerPort and an associated ClientPort. | 29 | * Creates a pair of ServerPort and an associated ClientPort. |
| @@ -32,8 +33,8 @@ public: | |||
| 32 | * @param name Optional name of the ports | 33 | * @param name Optional name of the ports |
| 33 | * @return The created port tuple | 34 | * @return The created port tuple |
| 34 | */ | 35 | */ |
| 35 | static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( | 36 | static PortPair CreatePortPair(KernelCore& kernel, u32 max_sessions, |
| 36 | KernelCore& kernel, u32 max_sessions, std::string name = "UnknownPort"); | 37 | std::string name = "UnknownPort"); |
| 37 | 38 | ||
| 38 | std::string GetTypeName() const override { | 39 | std::string GetTypeName() const override { |
| 39 | return "ServerPort"; | 40 | return "ServerPort"; |
| @@ -42,7 +43,7 @@ public: | |||
| 42 | return name; | 43 | return name; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 45 | static const HandleType HANDLE_TYPE = HandleType::ServerPort; | 46 | static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort; |
| 46 | HandleType GetHandleType() const override { | 47 | HandleType GetHandleType() const override { |
| 47 | return HANDLE_TYPE; | 48 | return HANDLE_TYPE; |
| 48 | } | 49 | } |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 40cec143e..696a82cd9 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -28,11 +28,9 @@ ServerSession::~ServerSession() { | |||
| 28 | // the emulated application. | 28 | // the emulated application. |
| 29 | 29 | ||
| 30 | // Decrease the port's connection count. | 30 | // Decrease the port's connection count. |
| 31 | if (parent->port) | 31 | if (parent->port) { |
| 32 | parent->port->ConnectionClosed(); | 32 | parent->port->ConnectionClosed(); |
| 33 | 33 | } | |
| 34 | // TODO(Subv): Wake up all the ClientSession's waiting threads and set | ||
| 35 | // the SendSyncRequest result to 0xC920181A. | ||
| 36 | 34 | ||
| 37 | parent->server = nullptr; | 35 | parent->server = nullptr; |
| 38 | } | 36 | } |
| @@ -74,9 +72,6 @@ void ServerSession::ClientDisconnected() { | |||
| 74 | handler->ClientDisconnected(this); | 72 | handler->ClientDisconnected(this); |
| 75 | } | 73 | } |
| 76 | 74 | ||
| 77 | // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set | ||
| 78 | // their WaitSynchronization result to 0xC920181A. | ||
| 79 | |||
| 80 | // Clean up the list of client threads with pending requests, they are unneeded now that the | 75 | // Clean up the list of client threads with pending requests, they are unneeded now that the |
| 81 | // client endpoint is closed. | 76 | // client endpoint is closed. |
| 82 | pending_requesting_threads.clear(); | 77 | pending_requesting_threads.clear(); |
| @@ -204,6 +199,6 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel, | |||
| 204 | client_session->parent = parent; | 199 | client_session->parent = parent; |
| 205 | server_session->parent = parent; | 200 | server_session->parent = parent; |
| 206 | 201 | ||
| 207 | return std::make_tuple(std::move(server_session), std::move(client_session)); | 202 | return std::make_pair(std::move(server_session), std::move(client_session)); |
| 208 | } | 203 | } |
| 209 | } // namespace Kernel | 204 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 79b84bade..738df30f8 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <utility> | ||
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | 11 | ||
| 11 | #include "core/hle/kernel/object.h" | 12 | #include "core/hle/kernel/object.h" |
| @@ -41,7 +42,11 @@ public: | |||
| 41 | return "ServerSession"; | 42 | return "ServerSession"; |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | static const HandleType HANDLE_TYPE = HandleType::ServerSession; | 45 | std::string GetName() const override { |
| 46 | return name; | ||
| 47 | } | ||
| 48 | |||
| 49 | static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession; | ||
| 45 | HandleType GetHandleType() const override { | 50 | HandleType GetHandleType() const override { |
| 46 | return HANDLE_TYPE; | 51 | return HANDLE_TYPE; |
| 47 | } | 52 | } |
| @@ -54,7 +59,7 @@ public: | |||
| 54 | return parent.get(); | 59 | return parent.get(); |
| 55 | } | 60 | } |
| 56 | 61 | ||
| 57 | using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; | 62 | using SessionPair = std::pair<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; |
| 58 | 63 | ||
| 59 | /** | 64 | /** |
| 60 | * Creates a pair of ServerSession and an associated ClientSession. | 65 | * Creates a pair of ServerSession and an associated ClientSession. |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 37e18c443..c2b6155e1 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -76,7 +76,7 @@ public: | |||
| 76 | return name; | 76 | return name; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | static const HandleType HANDLE_TYPE = HandleType::SharedMemory; | 79 | static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory; |
| 80 | HandleType GetHandleType() const override { | 80 | HandleType GetHandleType() const override { |
| 81 | return HANDLE_TYPE; | 81 | return HANDLE_TYPE; |
| 82 | } | 82 | } |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index ab10db3df..d48a2203a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -131,16 +131,15 @@ enum class ResourceLimitValueType { | |||
| 131 | LimitValue, | 131 | LimitValue, |
| 132 | }; | 132 | }; |
| 133 | 133 | ||
| 134 | ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type, | 134 | ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, |
| 135 | ResourceLimitValueType value_type) { | 135 | u32 resource_type, ResourceLimitValueType value_type) { |
| 136 | const auto type = static_cast<ResourceType>(resource_type); | 136 | const auto type = static_cast<ResourceType>(resource_type); |
| 137 | if (!IsValidResourceType(type)) { | 137 | if (!IsValidResourceType(type)) { |
| 138 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); | 138 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); |
| 139 | return ERR_INVALID_ENUM_VALUE; | 139 | return ERR_INVALID_ENUM_VALUE; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | const auto& kernel = Core::System::GetInstance().Kernel(); | 142 | const auto* const current_process = system.Kernel().CurrentProcess(); |
| 143 | const auto* const current_process = kernel.CurrentProcess(); | ||
| 144 | ASSERT(current_process != nullptr); | 143 | ASSERT(current_process != nullptr); |
| 145 | 144 | ||
| 146 | const auto resource_limit_object = | 145 | const auto resource_limit_object = |
| @@ -160,7 +159,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_ty | |||
| 160 | } // Anonymous namespace | 159 | } // Anonymous namespace |
| 161 | 160 | ||
| 162 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | 161 | /// Set the process heap to a given Size. It can both extend and shrink the heap. |
| 163 | static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | 162 | static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { |
| 164 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); | 163 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); |
| 165 | 164 | ||
| 166 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. | 165 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. |
| @@ -175,7 +174,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | |||
| 175 | return ERR_INVALID_SIZE; | 174 | return ERR_INVALID_SIZE; |
| 176 | } | 175 | } |
| 177 | 176 | ||
| 178 | auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager(); | 177 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 179 | const auto alloc_result = vm_manager.SetHeapSize(heap_size); | 178 | const auto alloc_result = vm_manager.SetHeapSize(heap_size); |
| 180 | if (alloc_result.Failed()) { | 179 | if (alloc_result.Failed()) { |
| 181 | return alloc_result.Code(); | 180 | return alloc_result.Code(); |
| @@ -185,7 +184,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | |||
| 185 | return RESULT_SUCCESS; | 184 | return RESULT_SUCCESS; |
| 186 | } | 185 | } |
| 187 | 186 | ||
| 188 | static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | 187 | static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) { |
| 189 | LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); | 188 | LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); |
| 190 | 189 | ||
| 191 | if (!Common::Is4KBAligned(addr)) { | 190 | if (!Common::Is4KBAligned(addr)) { |
| @@ -217,7 +216,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | |||
| 217 | return ERR_INVALID_MEMORY_PERMISSIONS; | 216 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 218 | } | 217 | } |
| 219 | 218 | ||
| 220 | auto* const current_process = Core::CurrentProcess(); | 219 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 221 | auto& vm_manager = current_process->VMManager(); | 220 | auto& vm_manager = current_process->VMManager(); |
| 222 | 221 | ||
| 223 | if (!vm_manager.IsWithinAddressSpace(addr, size)) { | 222 | if (!vm_manager.IsWithinAddressSpace(addr, size)) { |
| @@ -242,7 +241,8 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | |||
| 242 | return vm_manager.ReprotectRange(addr, size, converted_permissions); | 241 | return vm_manager.ReprotectRange(addr, size, converted_permissions); |
| 243 | } | 242 | } |
| 244 | 243 | ||
| 245 | static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) { | 244 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, |
| 245 | u32 attribute) { | ||
| 246 | LOG_DEBUG(Kernel_SVC, | 246 | LOG_DEBUG(Kernel_SVC, |
| 247 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | 247 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, |
| 248 | size, mask, attribute); | 248 | size, mask, attribute); |
| @@ -280,7 +280,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr | |||
| 280 | return ERR_INVALID_COMBINATION; | 280 | return ERR_INVALID_COMBINATION; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 283 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 284 | if (!vm_manager.IsWithinAddressSpace(address, size)) { | 284 | if (!vm_manager.IsWithinAddressSpace(address, size)) { |
| 285 | LOG_ERROR(Kernel_SVC, | 285 | LOG_ERROR(Kernel_SVC, |
| 286 | "Given address (0x{:016X}) is outside the bounds of the address space.", address); | 286 | "Given address (0x{:016X}) is outside the bounds of the address space.", address); |
| @@ -291,11 +291,11 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr | |||
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | /// Maps a memory range into a different range. | 293 | /// Maps a memory range into a different range. |
| 294 | static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 294 | static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 295 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 295 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 296 | src_addr, size); | 296 | src_addr, size); |
| 297 | 297 | ||
| 298 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 298 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 299 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); | 299 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); |
| 300 | 300 | ||
| 301 | if (result.IsError()) { | 301 | if (result.IsError()) { |
| @@ -306,11 +306,11 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | /// Unmaps a region that was previously mapped with svcMapMemory | 308 | /// Unmaps a region that was previously mapped with svcMapMemory |
| 309 | static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 309 | static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 310 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 310 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 311 | src_addr, size); | 311 | src_addr, size); |
| 312 | 312 | ||
| 313 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 313 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 314 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); | 314 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); |
| 315 | 315 | ||
| 316 | if (result.IsError()) { | 316 | if (result.IsError()) { |
| @@ -321,7 +321,8 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | /// Connect to an OS service given the port name, returns the handle to the port to out | 323 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 324 | static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { | 324 | static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, |
| 325 | VAddr port_name_address) { | ||
| 325 | if (!Memory::IsValidVirtualAddress(port_name_address)) { | 326 | if (!Memory::IsValidVirtualAddress(port_name_address)) { |
| 326 | LOG_ERROR(Kernel_SVC, | 327 | LOG_ERROR(Kernel_SVC, |
| 327 | "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", | 328 | "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", |
| @@ -340,8 +341,8 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address | |||
| 340 | 341 | ||
| 341 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); | 342 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); |
| 342 | 343 | ||
| 343 | auto& kernel = Core::System::GetInstance().Kernel(); | 344 | auto& kernel = system.Kernel(); |
| 344 | auto it = kernel.FindNamedPort(port_name); | 345 | const auto it = kernel.FindNamedPort(port_name); |
| 345 | if (!kernel.IsValidNamedPort(it)) { | 346 | if (!kernel.IsValidNamedPort(it)) { |
| 346 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); | 347 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); |
| 347 | return ERR_NOT_FOUND; | 348 | return ERR_NOT_FOUND; |
| @@ -353,14 +354,14 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address | |||
| 353 | CASCADE_RESULT(client_session, client_port->Connect()); | 354 | CASCADE_RESULT(client_session, client_port->Connect()); |
| 354 | 355 | ||
| 355 | // Return the client session | 356 | // Return the client session |
| 356 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 357 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 357 | CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); | 358 | CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); |
| 358 | return RESULT_SUCCESS; | 359 | return RESULT_SUCCESS; |
| 359 | } | 360 | } |
| 360 | 361 | ||
| 361 | /// Makes a blocking IPC call to an OS service. | 362 | /// Makes a blocking IPC call to an OS service. |
| 362 | static ResultCode SendSyncRequest(Handle handle) { | 363 | static ResultCode SendSyncRequest(Core::System& system, Handle handle) { |
| 363 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 364 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 364 | SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); | 365 | SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); |
| 365 | if (!session) { | 366 | if (!session) { |
| 366 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); | 367 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); |
| @@ -369,18 +370,18 @@ static ResultCode SendSyncRequest(Handle handle) { | |||
| 369 | 370 | ||
| 370 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | 371 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); |
| 371 | 372 | ||
| 372 | Core::System::GetInstance().PrepareReschedule(); | 373 | system.PrepareReschedule(); |
| 373 | 374 | ||
| 374 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server | 375 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server |
| 375 | // responds and cause a reschedule. | 376 | // responds and cause a reschedule. |
| 376 | return session->SendSyncRequest(GetCurrentThread()); | 377 | return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread()); |
| 377 | } | 378 | } |
| 378 | 379 | ||
| 379 | /// Get the ID for the specified thread. | 380 | /// Get the ID for the specified thread. |
| 380 | static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { | 381 | static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) { |
| 381 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 382 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 382 | 383 | ||
| 383 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 384 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 384 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 385 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 385 | if (!thread) { | 386 | if (!thread) { |
| 386 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); | 387 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); |
| @@ -392,10 +393,10 @@ static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { | |||
| 392 | } | 393 | } |
| 393 | 394 | ||
| 394 | /// Gets the ID of the specified process or a specified thread's owning process. | 395 | /// Gets the ID of the specified process or a specified thread's owning process. |
| 395 | static ResultCode GetProcessId(u64* process_id, Handle handle) { | 396 | static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) { |
| 396 | LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); | 397 | LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); |
| 397 | 398 | ||
| 398 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 399 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 399 | const SharedPtr<Process> process = handle_table.Get<Process>(handle); | 400 | const SharedPtr<Process> process = handle_table.Get<Process>(handle); |
| 400 | if (process) { | 401 | if (process) { |
| 401 | *process_id = process->GetProcessID(); | 402 | *process_id = process->GetProcessID(); |
| @@ -437,8 +438,8 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr | |||
| 437 | }; | 438 | }; |
| 438 | 439 | ||
| 439 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 440 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 440 | static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, | 441 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, |
| 441 | s64 nano_seconds) { | 442 | u64 handle_count, s64 nano_seconds) { |
| 442 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", | 443 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", |
| 443 | handles_address, handle_count, nano_seconds); | 444 | handles_address, handle_count, nano_seconds); |
| 444 | 445 | ||
| @@ -457,11 +458,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 457 | return ERR_OUT_OF_RANGE; | 458 | return ERR_OUT_OF_RANGE; |
| 458 | } | 459 | } |
| 459 | 460 | ||
| 460 | auto* const thread = GetCurrentThread(); | 461 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); |
| 461 | 462 | ||
| 462 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; | 463 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; |
| 463 | Thread::ThreadWaitObjects objects(handle_count); | 464 | Thread::ThreadWaitObjects objects(handle_count); |
| 464 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 465 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 465 | 466 | ||
| 466 | for (u64 i = 0; i < handle_count; ++i) { | 467 | for (u64 i = 0; i < handle_count; ++i) { |
| 467 | const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); | 468 | const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); |
| @@ -507,16 +508,16 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 507 | thread->WakeAfterDelay(nano_seconds); | 508 | thread->WakeAfterDelay(nano_seconds); |
| 508 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); | 509 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); |
| 509 | 510 | ||
| 510 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 511 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 511 | 512 | ||
| 512 | return RESULT_TIMEOUT; | 513 | return RESULT_TIMEOUT; |
| 513 | } | 514 | } |
| 514 | 515 | ||
| 515 | /// Resumes a thread waiting on WaitSynchronization | 516 | /// Resumes a thread waiting on WaitSynchronization |
| 516 | static ResultCode CancelSynchronization(Handle thread_handle) { | 517 | static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) { |
| 517 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); | 518 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); |
| 518 | 519 | ||
| 519 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 520 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 520 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 521 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 521 | if (!thread) { | 522 | if (!thread) { |
| 522 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 523 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -531,8 +532,8 @@ static ResultCode CancelSynchronization(Handle thread_handle) { | |||
| 531 | } | 532 | } |
| 532 | 533 | ||
| 533 | /// Attempts to locks a mutex, creating it if it does not already exist | 534 | /// Attempts to locks a mutex, creating it if it does not already exist |
| 534 | static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | 535 | static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, |
| 535 | Handle requesting_thread_handle) { | 536 | VAddr mutex_addr, Handle requesting_thread_handle) { |
| 536 | LOG_TRACE(Kernel_SVC, | 537 | LOG_TRACE(Kernel_SVC, |
| 537 | "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " | 538 | "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " |
| 538 | "requesting_current_thread_handle=0x{:08X}", | 539 | "requesting_current_thread_handle=0x{:08X}", |
| @@ -549,13 +550,13 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 549 | return ERR_INVALID_ADDRESS; | 550 | return ERR_INVALID_ADDRESS; |
| 550 | } | 551 | } |
| 551 | 552 | ||
| 552 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 553 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 553 | return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, | 554 | return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, |
| 554 | requesting_thread_handle); | 555 | requesting_thread_handle); |
| 555 | } | 556 | } |
| 556 | 557 | ||
| 557 | /// Unlock a mutex | 558 | /// Unlock a mutex |
| 558 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | 559 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { |
| 559 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | 560 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); |
| 560 | 561 | ||
| 561 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { | 562 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { |
| @@ -569,7 +570,7 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | |||
| 569 | return ERR_INVALID_ADDRESS; | 570 | return ERR_INVALID_ADDRESS; |
| 570 | } | 571 | } |
| 571 | 572 | ||
| 572 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 573 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 573 | return current_process->GetMutex().Release(mutex_addr); | 574 | return current_process->GetMutex().Release(mutex_addr); |
| 574 | } | 575 | } |
| 575 | 576 | ||
| @@ -592,7 +593,7 @@ struct BreakReason { | |||
| 592 | }; | 593 | }; |
| 593 | 594 | ||
| 594 | /// Break program execution | 595 | /// Break program execution |
| 595 | static void Break(u32 reason, u64 info1, u64 info2) { | 596 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { |
| 596 | BreakReason break_reason{reason}; | 597 | BreakReason break_reason{reason}; |
| 597 | bool has_dumped_buffer{}; | 598 | bool has_dumped_buffer{}; |
| 598 | 599 | ||
| @@ -670,22 +671,24 @@ static void Break(u32 reason, u64 info1, u64 info2) { | |||
| 670 | Debug_Emulated, | 671 | Debug_Emulated, |
| 671 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 672 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| 672 | reason, info1, info2); | 673 | reason, info1, info2); |
| 674 | |||
| 673 | handle_debug_buffer(info1, info2); | 675 | handle_debug_buffer(info1, info2); |
| 674 | Core::System::GetInstance() | 676 | |
| 675 | .ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID())) | 677 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 676 | .LogBacktrace(); | 678 | const auto thread_processor_id = current_thread->GetProcessorID(); |
| 679 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); | ||
| 677 | ASSERT(false); | 680 | ASSERT(false); |
| 678 | 681 | ||
| 679 | Core::CurrentProcess()->PrepareForTermination(); | 682 | system.Kernel().CurrentProcess()->PrepareForTermination(); |
| 680 | 683 | ||
| 681 | // Kill the current thread | 684 | // Kill the current thread |
| 682 | GetCurrentThread()->Stop(); | 685 | current_thread->Stop(); |
| 683 | Core::System::GetInstance().PrepareReschedule(); | 686 | system.PrepareReschedule(); |
| 684 | } | 687 | } |
| 685 | } | 688 | } |
| 686 | 689 | ||
| 687 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 690 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 688 | static void OutputDebugString(VAddr address, u64 len) { | 691 | static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { |
| 689 | if (len == 0) { | 692 | if (len == 0) { |
| 690 | return; | 693 | return; |
| 691 | } | 694 | } |
| @@ -696,7 +699,8 @@ static void OutputDebugString(VAddr address, u64 len) { | |||
| 696 | } | 699 | } |
| 697 | 700 | ||
| 698 | /// Gets system/memory information for the current process | 701 | /// Gets system/memory information for the current process |
| 699 | static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { | 702 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, |
| 703 | u64 info_sub_id) { | ||
| 700 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | 704 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, |
| 701 | info_sub_id, handle); | 705 | info_sub_id, handle); |
| 702 | 706 | ||
| @@ -754,7 +758,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 754 | return ERR_INVALID_ENUM_VALUE; | 758 | return ERR_INVALID_ENUM_VALUE; |
| 755 | } | 759 | } |
| 756 | 760 | ||
| 757 | const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable(); | 761 | const auto& current_process_handle_table = |
| 762 | system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 758 | const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle)); | 763 | const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle)); |
| 759 | if (!process) { | 764 | if (!process) { |
| 760 | return ERR_INVALID_HANDLE; | 765 | return ERR_INVALID_HANDLE; |
| @@ -844,7 +849,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 844 | return ERR_INVALID_COMBINATION; | 849 | return ERR_INVALID_COMBINATION; |
| 845 | } | 850 | } |
| 846 | 851 | ||
| 847 | Process* const current_process = Core::CurrentProcess(); | 852 | Process* const current_process = system.Kernel().CurrentProcess(); |
| 848 | HandleTable& handle_table = current_process->GetHandleTable(); | 853 | HandleTable& handle_table = current_process->GetHandleTable(); |
| 849 | const auto resource_limit = current_process->GetResourceLimit(); | 854 | const auto resource_limit = current_process->GetResourceLimit(); |
| 850 | if (!resource_limit) { | 855 | if (!resource_limit) { |
| @@ -875,7 +880,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 875 | return ERR_INVALID_COMBINATION; | 880 | return ERR_INVALID_COMBINATION; |
| 876 | } | 881 | } |
| 877 | 882 | ||
| 878 | *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id); | 883 | *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); |
| 879 | return RESULT_SUCCESS; | 884 | return RESULT_SUCCESS; |
| 880 | 885 | ||
| 881 | case GetInfoType::PrivilegedProcessId: | 886 | case GetInfoType::PrivilegedProcessId: |
| @@ -892,15 +897,14 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 892 | return ERR_INVALID_COMBINATION; | 897 | return ERR_INVALID_COMBINATION; |
| 893 | } | 898 | } |
| 894 | 899 | ||
| 895 | const auto thread = | 900 | const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>( |
| 896 | Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); | 901 | static_cast<Handle>(handle)); |
| 897 | if (!thread) { | 902 | if (!thread) { |
| 898 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", | 903 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", |
| 899 | static_cast<Handle>(handle)); | 904 | static_cast<Handle>(handle)); |
| 900 | return ERR_INVALID_HANDLE; | 905 | return ERR_INVALID_HANDLE; |
| 901 | } | 906 | } |
| 902 | 907 | ||
| 903 | const auto& system = Core::System::GetInstance(); | ||
| 904 | const auto& core_timing = system.CoreTiming(); | 908 | const auto& core_timing = system.CoreTiming(); |
| 905 | const auto& scheduler = system.CurrentScheduler(); | 909 | const auto& scheduler = system.CurrentScheduler(); |
| 906 | const auto* const current_thread = scheduler.GetCurrentThread(); | 910 | const auto* const current_thread = scheduler.GetCurrentThread(); |
| @@ -927,13 +931,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 927 | } | 931 | } |
| 928 | 932 | ||
| 929 | /// Sets the thread activity | 933 | /// Sets the thread activity |
| 930 | static ResultCode SetThreadActivity(Handle handle, u32 activity) { | 934 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { |
| 931 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); | 935 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); |
| 932 | if (activity > static_cast<u32>(ThreadActivity::Paused)) { | 936 | if (activity > static_cast<u32>(ThreadActivity::Paused)) { |
| 933 | return ERR_INVALID_ENUM_VALUE; | 937 | return ERR_INVALID_ENUM_VALUE; |
| 934 | } | 938 | } |
| 935 | 939 | ||
| 936 | const auto* current_process = Core::CurrentProcess(); | 940 | const auto* current_process = system.Kernel().CurrentProcess(); |
| 937 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | 941 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 938 | if (!thread) { | 942 | if (!thread) { |
| 939 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 943 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| @@ -950,7 +954,7 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) { | |||
| 950 | return ERR_INVALID_HANDLE; | 954 | return ERR_INVALID_HANDLE; |
| 951 | } | 955 | } |
| 952 | 956 | ||
| 953 | if (thread == GetCurrentThread()) { | 957 | if (thread == system.CurrentScheduler().GetCurrentThread()) { |
| 954 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); | 958 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); |
| 955 | return ERR_BUSY; | 959 | return ERR_BUSY; |
| 956 | } | 960 | } |
| @@ -960,10 +964,10 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) { | |||
| 960 | } | 964 | } |
| 961 | 965 | ||
| 962 | /// Gets the thread context | 966 | /// Gets the thread context |
| 963 | static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | 967 | static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) { |
| 964 | LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); | 968 | LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); |
| 965 | 969 | ||
| 966 | const auto* current_process = Core::CurrentProcess(); | 970 | const auto* current_process = system.Kernel().CurrentProcess(); |
| 967 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | 971 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 968 | if (!thread) { | 972 | if (!thread) { |
| 969 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 973 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| @@ -980,7 +984,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 980 | return ERR_INVALID_HANDLE; | 984 | return ERR_INVALID_HANDLE; |
| 981 | } | 985 | } |
| 982 | 986 | ||
| 983 | if (thread == GetCurrentThread()) { | 987 | if (thread == system.CurrentScheduler().GetCurrentThread()) { |
| 984 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); | 988 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); |
| 985 | return ERR_BUSY; | 989 | return ERR_BUSY; |
| 986 | } | 990 | } |
| @@ -1001,10 +1005,10 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 1001 | } | 1005 | } |
| 1002 | 1006 | ||
| 1003 | /// Gets the priority for the specified thread | 1007 | /// Gets the priority for the specified thread |
| 1004 | static ResultCode GetThreadPriority(u32* priority, Handle handle) { | 1008 | static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { |
| 1005 | LOG_TRACE(Kernel_SVC, "called"); | 1009 | LOG_TRACE(Kernel_SVC, "called"); |
| 1006 | 1010 | ||
| 1007 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1011 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1008 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); | 1012 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); |
| 1009 | if (!thread) { | 1013 | if (!thread) { |
| 1010 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 1014 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| @@ -1016,7 +1020,7 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) { | |||
| 1016 | } | 1020 | } |
| 1017 | 1021 | ||
| 1018 | /// Sets the priority for the specified thread | 1022 | /// Sets the priority for the specified thread |
| 1019 | static ResultCode SetThreadPriority(Handle handle, u32 priority) { | 1023 | static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { |
| 1020 | LOG_TRACE(Kernel_SVC, "called"); | 1024 | LOG_TRACE(Kernel_SVC, "called"); |
| 1021 | 1025 | ||
| 1022 | if (priority > THREADPRIO_LOWEST) { | 1026 | if (priority > THREADPRIO_LOWEST) { |
| @@ -1027,7 +1031,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 1027 | return ERR_INVALID_THREAD_PRIORITY; | 1031 | return ERR_INVALID_THREAD_PRIORITY; |
| 1028 | } | 1032 | } |
| 1029 | 1033 | ||
| 1030 | const auto* const current_process = Core::CurrentProcess(); | 1034 | const auto* const current_process = system.Kernel().CurrentProcess(); |
| 1031 | 1035 | ||
| 1032 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | 1036 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 1033 | if (!thread) { | 1037 | if (!thread) { |
| @@ -1037,18 +1041,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 1037 | 1041 | ||
| 1038 | thread->SetPriority(priority); | 1042 | thread->SetPriority(priority); |
| 1039 | 1043 | ||
| 1040 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1044 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1041 | return RESULT_SUCCESS; | 1045 | return RESULT_SUCCESS; |
| 1042 | } | 1046 | } |
| 1043 | 1047 | ||
| 1044 | /// Get which CPU core is executing the current thread | 1048 | /// Get which CPU core is executing the current thread |
| 1045 | static u32 GetCurrentProcessorNumber() { | 1049 | static u32 GetCurrentProcessorNumber(Core::System& system) { |
| 1046 | LOG_TRACE(Kernel_SVC, "called"); | 1050 | LOG_TRACE(Kernel_SVC, "called"); |
| 1047 | return GetCurrentThread()->GetProcessorID(); | 1051 | return system.CurrentScheduler().GetCurrentThread()->GetProcessorID(); |
| 1048 | } | 1052 | } |
| 1049 | 1053 | ||
| 1050 | static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, | 1054 | static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, |
| 1051 | u32 permissions) { | 1055 | u64 size, u32 permissions) { |
| 1052 | LOG_TRACE(Kernel_SVC, | 1056 | LOG_TRACE(Kernel_SVC, |
| 1053 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | 1057 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", |
| 1054 | shared_memory_handle, addr, size, permissions); | 1058 | shared_memory_handle, addr, size, permissions); |
| @@ -1082,7 +1086,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 1082 | return ERR_INVALID_MEMORY_PERMISSIONS; | 1086 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 1083 | } | 1087 | } |
| 1084 | 1088 | ||
| 1085 | auto* const current_process = Core::CurrentProcess(); | 1089 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1086 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); | 1090 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); |
| 1087 | if (!shared_memory) { | 1091 | if (!shared_memory) { |
| 1088 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", | 1092 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", |
| @@ -1100,7 +1104,8 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 1100 | return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); | 1104 | return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); |
| 1101 | } | 1105 | } |
| 1102 | 1106 | ||
| 1103 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { | 1107 | static ResultCode UnmapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, |
| 1108 | u64 size) { | ||
| 1104 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", | 1109 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", |
| 1105 | shared_memory_handle, addr, size); | 1110 | shared_memory_handle, addr, size); |
| 1106 | 1111 | ||
| @@ -1125,7 +1130,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 1125 | return ERR_INVALID_ADDRESS_STATE; | 1130 | return ERR_INVALID_ADDRESS_STATE; |
| 1126 | } | 1131 | } |
| 1127 | 1132 | ||
| 1128 | auto* const current_process = Core::CurrentProcess(); | 1133 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1129 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); | 1134 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); |
| 1130 | if (!shared_memory) { | 1135 | if (!shared_memory) { |
| 1131 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", | 1136 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", |
| @@ -1143,10 +1148,11 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 1143 | return shared_memory->Unmap(*current_process, addr, size); | 1148 | return shared_memory->Unmap(*current_process, addr, size); |
| 1144 | } | 1149 | } |
| 1145 | 1150 | ||
| 1146 | static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, | 1151 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1147 | Handle process_handle, VAddr address) { | 1152 | VAddr page_info_address, Handle process_handle, |
| 1153 | VAddr address) { | ||
| 1148 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); | 1154 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); |
| 1149 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1155 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1150 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); | 1156 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); |
| 1151 | if (!process) { | 1157 | if (!process) { |
| 1152 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | 1158 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", |
| @@ -1172,20 +1178,156 @@ static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_ | |||
| 1172 | return RESULT_SUCCESS; | 1178 | return RESULT_SUCCESS; |
| 1173 | } | 1179 | } |
| 1174 | 1180 | ||
| 1175 | static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address, | 1181 | static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, |
| 1176 | VAddr query_address) { | 1182 | VAddr page_info_address, VAddr query_address) { |
| 1177 | LOG_TRACE(Kernel_SVC, | 1183 | LOG_TRACE(Kernel_SVC, |
| 1178 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " | 1184 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " |
| 1179 | "query_address=0x{:016X}", | 1185 | "query_address=0x{:016X}", |
| 1180 | memory_info_address, page_info_address, query_address); | 1186 | memory_info_address, page_info_address, query_address); |
| 1181 | 1187 | ||
| 1182 | return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess, | 1188 | return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, |
| 1183 | query_address); | 1189 | query_address); |
| 1184 | } | 1190 | } |
| 1185 | 1191 | ||
| 1192 | static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 1193 | u64 src_address, u64 size) { | ||
| 1194 | LOG_DEBUG(Kernel_SVC, | ||
| 1195 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " | ||
| 1196 | "src_address=0x{:016X}, size=0x{:016X}", | ||
| 1197 | process_handle, dst_address, src_address, size); | ||
| 1198 | |||
| 1199 | if (!Common::Is4KBAligned(src_address)) { | ||
| 1200 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 1201 | src_address); | ||
| 1202 | return ERR_INVALID_ADDRESS; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 1206 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 1207 | dst_address); | ||
| 1208 | return ERR_INVALID_ADDRESS; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | if (size == 0 || !Common::Is4KBAligned(size)) { | ||
| 1212 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); | ||
| 1213 | return ERR_INVALID_SIZE; | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 1217 | LOG_ERROR(Kernel_SVC, | ||
| 1218 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 1219 | "size=0x{:016X}).", | ||
| 1220 | dst_address, size); | ||
| 1221 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | if (!IsValidAddressRange(src_address, size)) { | ||
| 1225 | LOG_ERROR(Kernel_SVC, | ||
| 1226 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 1227 | "size=0x{:016X}).", | ||
| 1228 | src_address, size); | ||
| 1229 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1233 | auto process = handle_table.Get<Process>(process_handle); | ||
| 1234 | if (!process) { | ||
| 1235 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 1236 | process_handle); | ||
| 1237 | return ERR_INVALID_HANDLE; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | auto& vm_manager = process->VMManager(); | ||
| 1241 | if (!vm_manager.IsWithinAddressSpace(src_address, size)) { | ||
| 1242 | LOG_ERROR(Kernel_SVC, | ||
| 1243 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 1244 | "size=0x{:016X}).", | ||
| 1245 | src_address, size); | ||
| 1246 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { | ||
| 1250 | LOG_ERROR(Kernel_SVC, | ||
| 1251 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 1252 | "size=0x{:016X}).", | ||
| 1253 | dst_address, size); | ||
| 1254 | return ERR_INVALID_MEMORY_RANGE; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | return vm_manager.MapCodeMemory(dst_address, src_address, size); | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 1261 | u64 src_address, u64 size) { | ||
| 1262 | LOG_DEBUG(Kernel_SVC, | ||
| 1263 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " | ||
| 1264 | "size=0x{:016X}", | ||
| 1265 | process_handle, dst_address, src_address, size); | ||
| 1266 | |||
| 1267 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 1268 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 1269 | dst_address); | ||
| 1270 | return ERR_INVALID_ADDRESS; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | if (!Common::Is4KBAligned(src_address)) { | ||
| 1274 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 1275 | src_address); | ||
| 1276 | return ERR_INVALID_ADDRESS; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | if (size == 0 || Common::Is4KBAligned(size)) { | ||
| 1280 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); | ||
| 1281 | return ERR_INVALID_SIZE; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 1285 | LOG_ERROR(Kernel_SVC, | ||
| 1286 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 1287 | "size=0x{:016X}).", | ||
| 1288 | dst_address, size); | ||
| 1289 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | if (!IsValidAddressRange(src_address, size)) { | ||
| 1293 | LOG_ERROR(Kernel_SVC, | ||
| 1294 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 1295 | "size=0x{:016X}).", | ||
| 1296 | src_address, size); | ||
| 1297 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1301 | auto process = handle_table.Get<Process>(process_handle); | ||
| 1302 | if (!process) { | ||
| 1303 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 1304 | process_handle); | ||
| 1305 | return ERR_INVALID_HANDLE; | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | auto& vm_manager = process->VMManager(); | ||
| 1309 | if (!vm_manager.IsWithinAddressSpace(src_address, size)) { | ||
| 1310 | LOG_ERROR(Kernel_SVC, | ||
| 1311 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 1312 | "size=0x{:016X}).", | ||
| 1313 | src_address, size); | ||
| 1314 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { | ||
| 1318 | LOG_ERROR(Kernel_SVC, | ||
| 1319 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 1320 | "size=0x{:016X}).", | ||
| 1321 | dst_address, size); | ||
| 1322 | return ERR_INVALID_MEMORY_RANGE; | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | return vm_manager.UnmapCodeMemory(dst_address, src_address, size); | ||
| 1326 | } | ||
| 1327 | |||
| 1186 | /// Exits the current process | 1328 | /// Exits the current process |
| 1187 | static void ExitProcess() { | 1329 | static void ExitProcess(Core::System& system) { |
| 1188 | auto* current_process = Core::CurrentProcess(); | 1330 | auto* current_process = system.Kernel().CurrentProcess(); |
| 1189 | 1331 | ||
| 1190 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); | 1332 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); |
| 1191 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, | 1333 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, |
| @@ -1194,20 +1336,20 @@ static void ExitProcess() { | |||
| 1194 | current_process->PrepareForTermination(); | 1336 | current_process->PrepareForTermination(); |
| 1195 | 1337 | ||
| 1196 | // Kill the current thread | 1338 | // Kill the current thread |
| 1197 | GetCurrentThread()->Stop(); | 1339 | system.CurrentScheduler().GetCurrentThread()->Stop(); |
| 1198 | 1340 | ||
| 1199 | Core::System::GetInstance().PrepareReschedule(); | 1341 | system.PrepareReschedule(); |
| 1200 | } | 1342 | } |
| 1201 | 1343 | ||
| 1202 | /// Creates a new thread | 1344 | /// Creates a new thread |
| 1203 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, | 1345 | static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, |
| 1204 | u32 priority, s32 processor_id) { | 1346 | VAddr stack_top, u32 priority, s32 processor_id) { |
| 1205 | LOG_TRACE(Kernel_SVC, | 1347 | LOG_TRACE(Kernel_SVC, |
| 1206 | "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " | 1348 | "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " |
| 1207 | "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", | 1349 | "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", |
| 1208 | entry_point, arg, stack_top, priority, processor_id, *out_handle); | 1350 | entry_point, arg, stack_top, priority, processor_id, *out_handle); |
| 1209 | 1351 | ||
| 1210 | auto* const current_process = Core::CurrentProcess(); | 1352 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1211 | 1353 | ||
| 1212 | if (processor_id == THREADPROCESSORID_IDEAL) { | 1354 | if (processor_id == THREADPROCESSORID_IDEAL) { |
| 1213 | // Set the target CPU to the one specified by the process. | 1355 | // Set the target CPU to the one specified by the process. |
| @@ -1239,7 +1381,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 1239 | } | 1381 | } |
| 1240 | 1382 | ||
| 1241 | const std::string name = fmt::format("thread-{:X}", entry_point); | 1383 | const std::string name = fmt::format("thread-{:X}", entry_point); |
| 1242 | auto& kernel = Core::System::GetInstance().Kernel(); | 1384 | auto& kernel = system.Kernel(); |
| 1243 | CASCADE_RESULT(SharedPtr<Thread> thread, | 1385 | CASCADE_RESULT(SharedPtr<Thread> thread, |
| 1244 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, | 1386 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, |
| 1245 | *current_process)); | 1387 | *current_process)); |
| @@ -1253,16 +1395,16 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 1253 | thread->SetGuestHandle(*new_guest_handle); | 1395 | thread->SetGuestHandle(*new_guest_handle); |
| 1254 | *out_handle = *new_guest_handle; | 1396 | *out_handle = *new_guest_handle; |
| 1255 | 1397 | ||
| 1256 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1398 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1257 | 1399 | ||
| 1258 | return RESULT_SUCCESS; | 1400 | return RESULT_SUCCESS; |
| 1259 | } | 1401 | } |
| 1260 | 1402 | ||
| 1261 | /// Starts the thread for the provided handle | 1403 | /// Starts the thread for the provided handle |
| 1262 | static ResultCode StartThread(Handle thread_handle) { | 1404 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { |
| 1263 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 1405 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 1264 | 1406 | ||
| 1265 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1407 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1266 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1408 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1267 | if (!thread) { | 1409 | if (!thread) { |
| 1268 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 1410 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -1275,16 +1417,14 @@ static ResultCode StartThread(Handle thread_handle) { | |||
| 1275 | thread->ResumeFromWait(); | 1417 | thread->ResumeFromWait(); |
| 1276 | 1418 | ||
| 1277 | if (thread->GetStatus() == ThreadStatus::Ready) { | 1419 | if (thread->GetStatus() == ThreadStatus::Ready) { |
| 1278 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1420 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1279 | } | 1421 | } |
| 1280 | 1422 | ||
| 1281 | return RESULT_SUCCESS; | 1423 | return RESULT_SUCCESS; |
| 1282 | } | 1424 | } |
| 1283 | 1425 | ||
| 1284 | /// Called when a thread exits | 1426 | /// Called when a thread exits |
| 1285 | static void ExitThread() { | 1427 | static void ExitThread(Core::System& system) { |
| 1286 | auto& system = Core::System::GetInstance(); | ||
| 1287 | |||
| 1288 | LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); | 1428 | LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); |
| 1289 | 1429 | ||
| 1290 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); | 1430 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| @@ -1294,7 +1434,7 @@ static void ExitThread() { | |||
| 1294 | } | 1434 | } |
| 1295 | 1435 | ||
| 1296 | /// Sleep the current thread | 1436 | /// Sleep the current thread |
| 1297 | static void SleepThread(s64 nanoseconds) { | 1437 | static void SleepThread(Core::System& system, s64 nanoseconds) { |
| 1298 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); | 1438 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); |
| 1299 | 1439 | ||
| 1300 | enum class SleepType : s64 { | 1440 | enum class SleepType : s64 { |
| @@ -1303,7 +1443,6 @@ static void SleepThread(s64 nanoseconds) { | |||
| 1303 | YieldAndWaitForLoadBalancing = -2, | 1443 | YieldAndWaitForLoadBalancing = -2, |
| 1304 | }; | 1444 | }; |
| 1305 | 1445 | ||
| 1306 | auto& system = Core::System::GetInstance(); | ||
| 1307 | auto& scheduler = system.CurrentScheduler(); | 1446 | auto& scheduler = system.CurrentScheduler(); |
| 1308 | auto* const current_thread = scheduler.GetCurrentThread(); | 1447 | auto* const current_thread = scheduler.GetCurrentThread(); |
| 1309 | 1448 | ||
| @@ -1332,14 +1471,29 @@ static void SleepThread(s64 nanoseconds) { | |||
| 1332 | } | 1471 | } |
| 1333 | 1472 | ||
| 1334 | /// Wait process wide key atomic | 1473 | /// Wait process wide key atomic |
| 1335 | static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, | 1474 | static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, |
| 1336 | Handle thread_handle, s64 nano_seconds) { | 1475 | VAddr condition_variable_addr, Handle thread_handle, |
| 1476 | s64 nano_seconds) { | ||
| 1337 | LOG_TRACE( | 1477 | LOG_TRACE( |
| 1338 | Kernel_SVC, | 1478 | Kernel_SVC, |
| 1339 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", | 1479 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", |
| 1340 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); | 1480 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); |
| 1341 | 1481 | ||
| 1342 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 1482 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { |
| 1483 | LOG_ERROR( | ||
| 1484 | Kernel_SVC, | ||
| 1485 | "Given mutex address must not be within the kernel address space. address=0x{:016X}", | ||
| 1486 | mutex_addr); | ||
| 1487 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | if (!Common::IsWordAligned(mutex_addr)) { | ||
| 1491 | LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", | ||
| 1492 | mutex_addr); | ||
| 1493 | return ERR_INVALID_ADDRESS; | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | auto* const current_process = system.Kernel().CurrentProcess(); | ||
| 1343 | const auto& handle_table = current_process->GetHandleTable(); | 1497 | const auto& handle_table = current_process->GetHandleTable(); |
| 1344 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1498 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1345 | ASSERT(thread); | 1499 | ASSERT(thread); |
| @@ -1349,7 +1503,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 1349 | return release_result; | 1503 | return release_result; |
| 1350 | } | 1504 | } |
| 1351 | 1505 | ||
| 1352 | SharedPtr<Thread> current_thread = GetCurrentThread(); | 1506 | SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 1353 | current_thread->SetCondVarWaitAddress(condition_variable_addr); | 1507 | current_thread->SetCondVarWaitAddress(condition_variable_addr); |
| 1354 | current_thread->SetMutexWaitAddress(mutex_addr); | 1508 | current_thread->SetMutexWaitAddress(mutex_addr); |
| 1355 | current_thread->SetWaitHandle(thread_handle); | 1509 | current_thread->SetWaitHandle(thread_handle); |
| @@ -1360,19 +1514,20 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 1360 | 1514 | ||
| 1361 | // Note: Deliberately don't attempt to inherit the lock owner's priority. | 1515 | // Note: Deliberately don't attempt to inherit the lock owner's priority. |
| 1362 | 1516 | ||
| 1363 | Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); | 1517 | system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); |
| 1364 | return RESULT_SUCCESS; | 1518 | return RESULT_SUCCESS; |
| 1365 | } | 1519 | } |
| 1366 | 1520 | ||
| 1367 | /// Signal process wide key | 1521 | /// Signal process wide key |
| 1368 | static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { | 1522 | static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, |
| 1523 | s32 target) { | ||
| 1369 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", | 1524 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", |
| 1370 | condition_variable_addr, target); | 1525 | condition_variable_addr, target); |
| 1371 | 1526 | ||
| 1372 | const auto RetrieveWaitingThreads = [](std::size_t core_index, | 1527 | const auto RetrieveWaitingThreads = [&system](std::size_t core_index, |
| 1373 | std::vector<SharedPtr<Thread>>& waiting_threads, | 1528 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 1374 | VAddr condvar_addr) { | 1529 | VAddr condvar_addr) { |
| 1375 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 1530 | const auto& scheduler = system.Scheduler(core_index); |
| 1376 | const auto& thread_list = scheduler.GetThreadList(); | 1531 | const auto& thread_list = scheduler.GetThreadList(); |
| 1377 | 1532 | ||
| 1378 | for (const auto& thread : thread_list) { | 1533 | for (const auto& thread : thread_list) { |
| @@ -1411,9 +1566,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1411 | // liberate Cond Var Thread. | 1566 | // liberate Cond Var Thread. |
| 1412 | thread->SetCondVarWaitAddress(0); | 1567 | thread->SetCondVarWaitAddress(0); |
| 1413 | 1568 | ||
| 1414 | std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); | 1569 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 1415 | 1570 | auto& monitor = system.Monitor(); | |
| 1416 | auto& monitor = Core::System::GetInstance().Monitor(); | ||
| 1417 | 1571 | ||
| 1418 | // Atomically read the value of the mutex. | 1572 | // Atomically read the value of the mutex. |
| 1419 | u32 mutex_val = 0; | 1573 | u32 mutex_val = 0; |
| @@ -1442,7 +1596,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1442 | thread->SetLockOwner(nullptr); | 1596 | thread->SetLockOwner(nullptr); |
| 1443 | thread->SetMutexWaitAddress(0); | 1597 | thread->SetMutexWaitAddress(0); |
| 1444 | thread->SetWaitHandle(0); | 1598 | thread->SetWaitHandle(0); |
| 1445 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1599 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1446 | } else { | 1600 | } else { |
| 1447 | // Atomically signal that the mutex now has a waiting thread. | 1601 | // Atomically signal that the mutex now has a waiting thread. |
| 1448 | do { | 1602 | do { |
| @@ -1458,7 +1612,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1458 | 1612 | ||
| 1459 | // The mutex is already owned by some other thread, make this thread wait on it. | 1613 | // The mutex is already owned by some other thread, make this thread wait on it. |
| 1460 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 1614 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 1461 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1615 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1462 | auto owner = handle_table.Get<Thread>(owner_handle); | 1616 | auto owner = handle_table.Get<Thread>(owner_handle); |
| 1463 | ASSERT(owner); | 1617 | ASSERT(owner); |
| 1464 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); | 1618 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); |
| @@ -1473,14 +1627,17 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1473 | } | 1627 | } |
| 1474 | 1628 | ||
| 1475 | // Wait for an address (via Address Arbiter) | 1629 | // Wait for an address (via Address Arbiter) |
| 1476 | static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { | 1630 | static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, |
| 1631 | s64 timeout) { | ||
| 1477 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", | 1632 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", |
| 1478 | address, type, value, timeout); | 1633 | address, type, value, timeout); |
| 1634 | |||
| 1479 | // If the passed address is a kernel virtual address, return invalid memory state. | 1635 | // If the passed address is a kernel virtual address, return invalid memory state. |
| 1480 | if (Memory::IsKernelVirtualAddress(address)) { | 1636 | if (Memory::IsKernelVirtualAddress(address)) { |
| 1481 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | 1637 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); |
| 1482 | return ERR_INVALID_ADDRESS_STATE; | 1638 | return ERR_INVALID_ADDRESS_STATE; |
| 1483 | } | 1639 | } |
| 1640 | |||
| 1484 | // If the address is not properly aligned to 4 bytes, return invalid address. | 1641 | // If the address is not properly aligned to 4 bytes, return invalid address. |
| 1485 | if (!Common::IsWordAligned(address)) { | 1642 | if (!Common::IsWordAligned(address)) { |
| 1486 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); | 1643 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); |
| @@ -1488,20 +1645,22 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout | |||
| 1488 | } | 1645 | } |
| 1489 | 1646 | ||
| 1490 | const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); | 1647 | const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); |
| 1491 | auto& address_arbiter = | 1648 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); |
| 1492 | Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); | ||
| 1493 | return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); | 1649 | return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); |
| 1494 | } | 1650 | } |
| 1495 | 1651 | ||
| 1496 | // Signals to an address (via Address Arbiter) | 1652 | // Signals to an address (via Address Arbiter) |
| 1497 | static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) { | 1653 | static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, |
| 1654 | s32 num_to_wake) { | ||
| 1498 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", | 1655 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", |
| 1499 | address, type, value, num_to_wake); | 1656 | address, type, value, num_to_wake); |
| 1657 | |||
| 1500 | // If the passed address is a kernel virtual address, return invalid memory state. | 1658 | // If the passed address is a kernel virtual address, return invalid memory state. |
| 1501 | if (Memory::IsKernelVirtualAddress(address)) { | 1659 | if (Memory::IsKernelVirtualAddress(address)) { |
| 1502 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | 1660 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); |
| 1503 | return ERR_INVALID_ADDRESS_STATE; | 1661 | return ERR_INVALID_ADDRESS_STATE; |
| 1504 | } | 1662 | } |
| 1663 | |||
| 1505 | // If the address is not properly aligned to 4 bytes, return invalid address. | 1664 | // If the address is not properly aligned to 4 bytes, return invalid address. |
| 1506 | if (!Common::IsWordAligned(address)) { | 1665 | if (!Common::IsWordAligned(address)) { |
| 1507 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); | 1666 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); |
| @@ -1509,16 +1668,15 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to | |||
| 1509 | } | 1668 | } |
| 1510 | 1669 | ||
| 1511 | const auto signal_type = static_cast<AddressArbiter::SignalType>(type); | 1670 | const auto signal_type = static_cast<AddressArbiter::SignalType>(type); |
| 1512 | auto& address_arbiter = | 1671 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); |
| 1513 | Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); | ||
| 1514 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); | 1672 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); |
| 1515 | } | 1673 | } |
| 1516 | 1674 | ||
| 1517 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 1675 | /// This returns the total CPU ticks elapsed since the CPU was powered-on |
| 1518 | static u64 GetSystemTick() { | 1676 | static u64 GetSystemTick(Core::System& system) { |
| 1519 | LOG_TRACE(Kernel_SVC, "called"); | 1677 | LOG_TRACE(Kernel_SVC, "called"); |
| 1520 | 1678 | ||
| 1521 | auto& core_timing = Core::System::GetInstance().CoreTiming(); | 1679 | auto& core_timing = system.CoreTiming(); |
| 1522 | const u64 result{core_timing.GetTicks()}; | 1680 | const u64 result{core_timing.GetTicks()}; |
| 1523 | 1681 | ||
| 1524 | // Advance time to defeat dumb games that busy-wait for the frame to end. | 1682 | // Advance time to defeat dumb games that busy-wait for the frame to end. |
| @@ -1528,18 +1686,18 @@ static u64 GetSystemTick() { | |||
| 1528 | } | 1686 | } |
| 1529 | 1687 | ||
| 1530 | /// Close a handle | 1688 | /// Close a handle |
| 1531 | static ResultCode CloseHandle(Handle handle) { | 1689 | static ResultCode CloseHandle(Core::System& system, Handle handle) { |
| 1532 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | 1690 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); |
| 1533 | 1691 | ||
| 1534 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1692 | auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1535 | return handle_table.Close(handle); | 1693 | return handle_table.Close(handle); |
| 1536 | } | 1694 | } |
| 1537 | 1695 | ||
| 1538 | /// Clears the signaled state of an event or process. | 1696 | /// Clears the signaled state of an event or process. |
| 1539 | static ResultCode ResetSignal(Handle handle) { | 1697 | static ResultCode ResetSignal(Core::System& system, Handle handle) { |
| 1540 | LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); | 1698 | LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); |
| 1541 | 1699 | ||
| 1542 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1700 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1543 | 1701 | ||
| 1544 | auto event = handle_table.Get<ReadableEvent>(handle); | 1702 | auto event = handle_table.Get<ReadableEvent>(handle); |
| 1545 | if (event) { | 1703 | if (event) { |
| @@ -1556,7 +1714,8 @@ static ResultCode ResetSignal(Handle handle) { | |||
| 1556 | } | 1714 | } |
| 1557 | 1715 | ||
| 1558 | /// Creates a TransferMemory object | 1716 | /// Creates a TransferMemory object |
| 1559 | static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { | 1717 | static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, |
| 1718 | u32 permissions) { | ||
| 1560 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, | 1719 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, |
| 1561 | permissions); | 1720 | permissions); |
| 1562 | 1721 | ||
| @@ -1584,7 +1743,7 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 | |||
| 1584 | return ERR_INVALID_MEMORY_PERMISSIONS; | 1743 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 1585 | } | 1744 | } |
| 1586 | 1745 | ||
| 1587 | auto& kernel = Core::System::GetInstance().Kernel(); | 1746 | auto& kernel = system.Kernel(); |
| 1588 | auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); | 1747 | auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); |
| 1589 | 1748 | ||
| 1590 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | 1749 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| @@ -1597,7 +1756,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 | |||
| 1597 | return RESULT_SUCCESS; | 1756 | return RESULT_SUCCESS; |
| 1598 | } | 1757 | } |
| 1599 | 1758 | ||
| 1600 | static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) { | 1759 | static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size, |
| 1760 | u32 permission_raw) { | ||
| 1601 | LOG_DEBUG(Kernel_SVC, | 1761 | LOG_DEBUG(Kernel_SVC, |
| 1602 | "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", | 1762 | "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", |
| 1603 | handle, address, size, permission_raw); | 1763 | handle, address, size, permission_raw); |
| @@ -1631,7 +1791,7 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 | |||
| 1631 | return ERR_INVALID_STATE; | 1791 | return ERR_INVALID_STATE; |
| 1632 | } | 1792 | } |
| 1633 | 1793 | ||
| 1634 | const auto& kernel = Core::System::GetInstance().Kernel(); | 1794 | const auto& kernel = system.Kernel(); |
| 1635 | const auto* const current_process = kernel.CurrentProcess(); | 1795 | const auto* const current_process = kernel.CurrentProcess(); |
| 1636 | const auto& handle_table = current_process->GetHandleTable(); | 1796 | const auto& handle_table = current_process->GetHandleTable(); |
| 1637 | 1797 | ||
| @@ -1653,7 +1813,8 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 | |||
| 1653 | return transfer_memory->MapMemory(address, size, permissions); | 1813 | return transfer_memory->MapMemory(address, size, permissions); |
| 1654 | } | 1814 | } |
| 1655 | 1815 | ||
| 1656 | static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { | 1816 | static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address, |
| 1817 | u64 size) { | ||
| 1657 | LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, | 1818 | LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, |
| 1658 | address, size); | 1819 | address, size); |
| 1659 | 1820 | ||
| @@ -1678,7 +1839,7 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { | |||
| 1678 | return ERR_INVALID_ADDRESS_STATE; | 1839 | return ERR_INVALID_ADDRESS_STATE; |
| 1679 | } | 1840 | } |
| 1680 | 1841 | ||
| 1681 | const auto& kernel = Core::System::GetInstance().Kernel(); | 1842 | const auto& kernel = system.Kernel(); |
| 1682 | const auto* const current_process = kernel.CurrentProcess(); | 1843 | const auto* const current_process = kernel.CurrentProcess(); |
| 1683 | const auto& handle_table = current_process->GetHandleTable(); | 1844 | const auto& handle_table = current_process->GetHandleTable(); |
| 1684 | 1845 | ||
| @@ -1700,10 +1861,11 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { | |||
| 1700 | return transfer_memory->UnmapMemory(address, size); | 1861 | return transfer_memory->UnmapMemory(address, size); |
| 1701 | } | 1862 | } |
| 1702 | 1863 | ||
| 1703 | static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { | 1864 | static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, |
| 1865 | u64* mask) { | ||
| 1704 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | 1866 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); |
| 1705 | 1867 | ||
| 1706 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1868 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1707 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1869 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1708 | if (!thread) { | 1870 | if (!thread) { |
| 1709 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 1871 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -1717,11 +1879,12 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) | |||
| 1717 | return RESULT_SUCCESS; | 1879 | return RESULT_SUCCESS; |
| 1718 | } | 1880 | } |
| 1719 | 1881 | ||
| 1720 | static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | 1882 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, |
| 1883 | u64 mask) { | ||
| 1721 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, | 1884 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, |
| 1722 | mask, core); | 1885 | mask, core); |
| 1723 | 1886 | ||
| 1724 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1887 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1725 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1888 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1726 | if (!thread) { | 1889 | if (!thread) { |
| 1727 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 1890 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -1766,8 +1929,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | |||
| 1766 | return RESULT_SUCCESS; | 1929 | return RESULT_SUCCESS; |
| 1767 | } | 1930 | } |
| 1768 | 1931 | ||
| 1769 | static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, | 1932 | static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size, |
| 1770 | u32 remote_permissions) { | 1933 | u32 local_permissions, u32 remote_permissions) { |
| 1771 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, | 1934 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, |
| 1772 | local_permissions, remote_permissions); | 1935 | local_permissions, remote_permissions); |
| 1773 | if (size == 0) { | 1936 | if (size == 0) { |
| @@ -1803,7 +1966,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1803 | return ERR_INVALID_MEMORY_PERMISSIONS; | 1966 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 1804 | } | 1967 | } |
| 1805 | 1968 | ||
| 1806 | auto& kernel = Core::System::GetInstance().Kernel(); | 1969 | auto& kernel = system.Kernel(); |
| 1807 | auto process = kernel.CurrentProcess(); | 1970 | auto process = kernel.CurrentProcess(); |
| 1808 | auto& handle_table = process->GetHandleTable(); | 1971 | auto& handle_table = process->GetHandleTable(); |
| 1809 | auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); | 1972 | auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); |
| @@ -1812,10 +1975,10 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1812 | return RESULT_SUCCESS; | 1975 | return RESULT_SUCCESS; |
| 1813 | } | 1976 | } |
| 1814 | 1977 | ||
| 1815 | static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { | 1978 | static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { |
| 1816 | LOG_DEBUG(Kernel_SVC, "called"); | 1979 | LOG_DEBUG(Kernel_SVC, "called"); |
| 1817 | 1980 | ||
| 1818 | auto& kernel = Core::System::GetInstance().Kernel(); | 1981 | auto& kernel = system.Kernel(); |
| 1819 | const auto [readable_event, writable_event] = | 1982 | const auto [readable_event, writable_event] = |
| 1820 | WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); | 1983 | WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); |
| 1821 | 1984 | ||
| @@ -1840,10 +2003,10 @@ static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { | |||
| 1840 | return RESULT_SUCCESS; | 2003 | return RESULT_SUCCESS; |
| 1841 | } | 2004 | } |
| 1842 | 2005 | ||
| 1843 | static ResultCode ClearEvent(Handle handle) { | 2006 | static ResultCode ClearEvent(Core::System& system, Handle handle) { |
| 1844 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); | 2007 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); |
| 1845 | 2008 | ||
| 1846 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 2009 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1847 | 2010 | ||
| 1848 | auto writable_event = handle_table.Get<WritableEvent>(handle); | 2011 | auto writable_event = handle_table.Get<WritableEvent>(handle); |
| 1849 | if (writable_event) { | 2012 | if (writable_event) { |
| @@ -1861,10 +2024,10 @@ static ResultCode ClearEvent(Handle handle) { | |||
| 1861 | return ERR_INVALID_HANDLE; | 2024 | return ERR_INVALID_HANDLE; |
| 1862 | } | 2025 | } |
| 1863 | 2026 | ||
| 1864 | static ResultCode SignalEvent(Handle handle) { | 2027 | static ResultCode SignalEvent(Core::System& system, Handle handle) { |
| 1865 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); | 2028 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); |
| 1866 | 2029 | ||
| 1867 | HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable(); | 2030 | HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1868 | auto writable_event = handle_table.Get<WritableEvent>(handle); | 2031 | auto writable_event = handle_table.Get<WritableEvent>(handle); |
| 1869 | 2032 | ||
| 1870 | if (!writable_event) { | 2033 | if (!writable_event) { |
| @@ -1876,7 +2039,7 @@ static ResultCode SignalEvent(Handle handle) { | |||
| 1876 | return RESULT_SUCCESS; | 2039 | return RESULT_SUCCESS; |
| 1877 | } | 2040 | } |
| 1878 | 2041 | ||
| 1879 | static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | 2042 | static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { |
| 1880 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); | 2043 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); |
| 1881 | 2044 | ||
| 1882 | // This function currently only allows retrieving a process' status. | 2045 | // This function currently only allows retrieving a process' status. |
| @@ -1884,7 +2047,7 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | |||
| 1884 | Status, | 2047 | Status, |
| 1885 | }; | 2048 | }; |
| 1886 | 2049 | ||
| 1887 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 2050 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1888 | const auto process = handle_table.Get<Process>(process_handle); | 2051 | const auto process = handle_table.Get<Process>(process_handle); |
| 1889 | if (!process) { | 2052 | if (!process) { |
| 1890 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | 2053 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", |
| @@ -1902,10 +2065,10 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | |||
| 1902 | return RESULT_SUCCESS; | 2065 | return RESULT_SUCCESS; |
| 1903 | } | 2066 | } |
| 1904 | 2067 | ||
| 1905 | static ResultCode CreateResourceLimit(Handle* out_handle) { | 2068 | static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { |
| 1906 | LOG_DEBUG(Kernel_SVC, "called"); | 2069 | LOG_DEBUG(Kernel_SVC, "called"); |
| 1907 | 2070 | ||
| 1908 | auto& kernel = Core::System::GetInstance().Kernel(); | 2071 | auto& kernel = system.Kernel(); |
| 1909 | auto resource_limit = ResourceLimit::Create(kernel); | 2072 | auto resource_limit = ResourceLimit::Create(kernel); |
| 1910 | 2073 | ||
| 1911 | auto* const current_process = kernel.CurrentProcess(); | 2074 | auto* const current_process = kernel.CurrentProcess(); |
| @@ -1920,11 +2083,11 @@ static ResultCode CreateResourceLimit(Handle* out_handle) { | |||
| 1920 | return RESULT_SUCCESS; | 2083 | return RESULT_SUCCESS; |
| 1921 | } | 2084 | } |
| 1922 | 2085 | ||
| 1923 | static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit, | 2086 | static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_value, |
| 1924 | u32 resource_type) { | 2087 | Handle resource_limit, u32 resource_type) { |
| 1925 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); | 2088 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); |
| 1926 | 2089 | ||
| 1927 | const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type, | 2090 | const auto limit_value = RetrieveResourceLimitValue(system, resource_limit, resource_type, |
| 1928 | ResourceLimitValueType::LimitValue); | 2091 | ResourceLimitValueType::LimitValue); |
| 1929 | if (limit_value.Failed()) { | 2092 | if (limit_value.Failed()) { |
| 1930 | return limit_value.Code(); | 2093 | return limit_value.Code(); |
| @@ -1934,11 +2097,11 @@ static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_lim | |||
| 1934 | return RESULT_SUCCESS; | 2097 | return RESULT_SUCCESS; |
| 1935 | } | 2098 | } |
| 1936 | 2099 | ||
| 1937 | static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit, | 2100 | static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_value, |
| 1938 | u32 resource_type) { | 2101 | Handle resource_limit, u32 resource_type) { |
| 1939 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); | 2102 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); |
| 1940 | 2103 | ||
| 1941 | const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type, | 2104 | const auto current_value = RetrieveResourceLimitValue(system, resource_limit, resource_type, |
| 1942 | ResourceLimitValueType::CurrentValue); | 2105 | ResourceLimitValueType::CurrentValue); |
| 1943 | if (current_value.Failed()) { | 2106 | if (current_value.Failed()) { |
| 1944 | return current_value.Code(); | 2107 | return current_value.Code(); |
| @@ -1948,7 +2111,8 @@ static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_l | |||
| 1948 | return RESULT_SUCCESS; | 2111 | return RESULT_SUCCESS; |
| 1949 | } | 2112 | } |
| 1950 | 2113 | ||
| 1951 | static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) { | 2114 | static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit, |
| 2115 | u32 resource_type, u64 value) { | ||
| 1952 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, | 2116 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, |
| 1953 | resource_type, value); | 2117 | resource_type, value); |
| 1954 | 2118 | ||
| @@ -1958,8 +2122,7 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource | |||
| 1958 | return ERR_INVALID_ENUM_VALUE; | 2122 | return ERR_INVALID_ENUM_VALUE; |
| 1959 | } | 2123 | } |
| 1960 | 2124 | ||
| 1961 | auto& kernel = Core::System::GetInstance().Kernel(); | 2125 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1962 | auto* const current_process = kernel.CurrentProcess(); | ||
| 1963 | ASSERT(current_process != nullptr); | 2126 | ASSERT(current_process != nullptr); |
| 1964 | 2127 | ||
| 1965 | auto resource_limit_object = | 2128 | auto resource_limit_object = |
| @@ -1983,8 +2146,8 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource | |||
| 1983 | return RESULT_SUCCESS; | 2146 | return RESULT_SUCCESS; |
| 1984 | } | 2147 | } |
| 1985 | 2148 | ||
| 1986 | static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, | 2149 | static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, |
| 1987 | u32 out_process_ids_size) { | 2150 | VAddr out_process_ids, u32 out_process_ids_size) { |
| 1988 | LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", | 2151 | LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", |
| 1989 | out_process_ids, out_process_ids_size); | 2152 | out_process_ids, out_process_ids_size); |
| 1990 | 2153 | ||
| @@ -1996,7 +2159,7 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, | |||
| 1996 | return ERR_OUT_OF_RANGE; | 2159 | return ERR_OUT_OF_RANGE; |
| 1997 | } | 2160 | } |
| 1998 | 2161 | ||
| 1999 | const auto& kernel = Core::System::GetInstance().Kernel(); | 2162 | const auto& kernel = system.Kernel(); |
| 2000 | const auto& vm_manager = kernel.CurrentProcess()->VMManager(); | 2163 | const auto& vm_manager = kernel.CurrentProcess()->VMManager(); |
| 2001 | const auto total_copy_size = out_process_ids_size * sizeof(u64); | 2164 | const auto total_copy_size = out_process_ids_size * sizeof(u64); |
| 2002 | 2165 | ||
| @@ -2020,8 +2183,8 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, | |||
| 2020 | return RESULT_SUCCESS; | 2183 | return RESULT_SUCCESS; |
| 2021 | } | 2184 | } |
| 2022 | 2185 | ||
| 2023 | ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size, | 2186 | ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, |
| 2024 | Handle debug_handle) { | 2187 | u32 out_thread_ids_size, Handle debug_handle) { |
| 2025 | // TODO: Handle this case when debug events are supported. | 2188 | // TODO: Handle this case when debug events are supported. |
| 2026 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); | 2189 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); |
| 2027 | 2190 | ||
| @@ -2035,7 +2198,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr | |||
| 2035 | return ERR_OUT_OF_RANGE; | 2198 | return ERR_OUT_OF_RANGE; |
| 2036 | } | 2199 | } |
| 2037 | 2200 | ||
| 2038 | const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 2201 | const auto* const current_process = system.Kernel().CurrentProcess(); |
| 2039 | const auto& vm_manager = current_process->VMManager(); | 2202 | const auto& vm_manager = current_process->VMManager(); |
| 2040 | const auto total_copy_size = out_thread_ids_size * sizeof(u64); | 2203 | const auto total_copy_size = out_thread_ids_size * sizeof(u64); |
| 2041 | 2204 | ||
| @@ -2062,7 +2225,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr | |||
| 2062 | 2225 | ||
| 2063 | namespace { | 2226 | namespace { |
| 2064 | struct FunctionDef { | 2227 | struct FunctionDef { |
| 2065 | using Func = void(); | 2228 | using Func = void(Core::System&); |
| 2066 | 2229 | ||
| 2067 | u32 id; | 2230 | u32 id; |
| 2068 | Func* func; | 2231 | Func* func; |
| @@ -2190,8 +2353,8 @@ static const FunctionDef SVC_Table[] = { | |||
| 2190 | {0x74, nullptr, "MapProcessMemory"}, | 2353 | {0x74, nullptr, "MapProcessMemory"}, |
| 2191 | {0x75, nullptr, "UnmapProcessMemory"}, | 2354 | {0x75, nullptr, "UnmapProcessMemory"}, |
| 2192 | {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, | 2355 | {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, |
| 2193 | {0x77, nullptr, "MapProcessCodeMemory"}, | 2356 | {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"}, |
| 2194 | {0x78, nullptr, "UnmapProcessCodeMemory"}, | 2357 | {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, |
| 2195 | {0x79, nullptr, "CreateProcess"}, | 2358 | {0x79, nullptr, "CreateProcess"}, |
| 2196 | {0x7A, nullptr, "StartProcess"}, | 2359 | {0x7A, nullptr, "StartProcess"}, |
| 2197 | {0x7B, nullptr, "TerminateProcess"}, | 2360 | {0x7B, nullptr, "TerminateProcess"}, |
| @@ -2211,7 +2374,7 @@ static const FunctionDef* GetSVCInfo(u32 func_num) { | |||
| 2211 | 2374 | ||
| 2212 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | 2375 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); |
| 2213 | 2376 | ||
| 2214 | void CallSVC(u32 immediate) { | 2377 | void CallSVC(Core::System& system, u32 immediate) { |
| 2215 | MICROPROFILE_SCOPE(Kernel_SVC); | 2378 | MICROPROFILE_SCOPE(Kernel_SVC); |
| 2216 | 2379 | ||
| 2217 | // Lock the global kernel mutex when we enter the kernel HLE. | 2380 | // Lock the global kernel mutex when we enter the kernel HLE. |
| @@ -2220,7 +2383,7 @@ void CallSVC(u32 immediate) { | |||
| 2220 | const FunctionDef* info = GetSVCInfo(immediate); | 2383 | const FunctionDef* info = GetSVCInfo(immediate); |
| 2221 | if (info) { | 2384 | if (info) { |
| 2222 | if (info->func) { | 2385 | if (info->func) { |
| 2223 | info->func(); | 2386 | info->func(system); |
| 2224 | } else { | 2387 | } else { |
| 2225 | LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); | 2388 | LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); |
| 2226 | } | 2389 | } |
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index c37ae0f98..c5539ac1c 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h | |||
| @@ -6,8 +6,12 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Kernel { | 13 | namespace Kernel { |
| 10 | 14 | ||
| 11 | void CallSVC(u32 immediate); | 15 | void CallSVC(Core::System& system, u32 immediate); |
| 12 | 16 | ||
| 13 | } // namespace Kernel | 17 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index b3733680f..865473c6f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -11,278 +11,319 @@ | |||
| 11 | 11 | ||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| 14 | static inline u64 Param(int n) { | 14 | static inline u64 Param(const Core::System& system, int n) { |
| 15 | return Core::CurrentArmInterface().GetReg(n); | 15 | return system.CurrentArmInterface().GetReg(n); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | * HLE a function return from the current ARM userland process | 19 | * HLE a function return from the current ARM userland process |
| 20 | * @param res Result to return | 20 | * @param system System context |
| 21 | * @param result Result to return | ||
| 21 | */ | 22 | */ |
| 22 | static inline void FuncReturn(u64 res) { | 23 | static inline void FuncReturn(Core::System& system, u64 result) { |
| 23 | Core::CurrentArmInterface().SetReg(0, res); | 24 | system.CurrentArmInterface().SetReg(0, result); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 27 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 27 | // Function wrappers that return type ResultCode | 28 | // Function wrappers that return type ResultCode |
| 28 | 29 | ||
| 29 | template <ResultCode func(u64)> | 30 | template <ResultCode func(Core::System&, u64)> |
| 30 | void SvcWrap() { | 31 | void SvcWrap(Core::System& system) { |
| 31 | FuncReturn(func(Param(0)).raw); | 32 | FuncReturn(system, func(system, Param(system, 0)).raw); |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | template <ResultCode func(u32)> | 35 | template <ResultCode func(Core::System&, u32)> |
| 35 | void SvcWrap() { | 36 | void SvcWrap(Core::System& system) { |
| 36 | FuncReturn(func(static_cast<u32>(Param(0))).raw); | 37 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); |
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | template <ResultCode func(u32, u32)> | 40 | template <ResultCode func(Core::System&, u32, u32)> |
| 40 | void SvcWrap() { | 41 | void SvcWrap(Core::System& system) { |
| 41 | FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw); | 42 | FuncReturn( |
| 43 | system, | ||
| 44 | func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); | ||
| 45 | } | ||
| 46 | |||
| 47 | template <ResultCode func(Core::System&, u32, u64, u64, u64)> | ||
| 48 | void SvcWrap(Core::System& system) { | ||
| 49 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), | ||
| 50 | Param(system, 2), Param(system, 3)) | ||
| 51 | .raw); | ||
| 42 | } | 52 | } |
| 43 | 53 | ||
| 44 | template <ResultCode func(u32*)> | 54 | template <ResultCode func(Core::System&, u32*)> |
| 45 | void SvcWrap() { | 55 | void SvcWrap(Core::System& system) { |
| 46 | u32 param = 0; | 56 | u32 param = 0; |
| 47 | const u32 retval = func(¶m).raw; | 57 | const u32 retval = func(system, ¶m).raw; |
| 48 | Core::CurrentArmInterface().SetReg(1, param); | 58 | system.CurrentArmInterface().SetReg(1, param); |
| 49 | FuncReturn(retval); | 59 | FuncReturn(system, retval); |
| 50 | } | 60 | } |
| 51 | 61 | ||
| 52 | template <ResultCode func(u32*, u32)> | 62 | template <ResultCode func(Core::System&, u32*, u32)> |
| 53 | void SvcWrap() { | 63 | void SvcWrap(Core::System& system) { |
| 54 | u32 param_1 = 0; | 64 | u32 param_1 = 0; |
| 55 | u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw; | 65 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; |
| 56 | Core::CurrentArmInterface().SetReg(1, param_1); | 66 | system.CurrentArmInterface().SetReg(1, param_1); |
| 57 | FuncReturn(retval); | 67 | FuncReturn(system, retval); |
| 58 | } | 68 | } |
| 59 | 69 | ||
| 60 | template <ResultCode func(u32*, u32*)> | 70 | template <ResultCode func(Core::System&, u32*, u32*)> |
| 61 | void SvcWrap() { | 71 | void SvcWrap(Core::System& system) { |
| 62 | u32 param_1 = 0; | 72 | u32 param_1 = 0; |
| 63 | u32 param_2 = 0; | 73 | u32 param_2 = 0; |
| 64 | const u32 retval = func(¶m_1, ¶m_2).raw; | 74 | const u32 retval = func(system, ¶m_1, ¶m_2).raw; |
| 65 | 75 | ||
| 66 | auto& arm_interface = Core::CurrentArmInterface(); | 76 | auto& arm_interface = system.CurrentArmInterface(); |
| 67 | arm_interface.SetReg(1, param_1); | 77 | arm_interface.SetReg(1, param_1); |
| 68 | arm_interface.SetReg(2, param_2); | 78 | arm_interface.SetReg(2, param_2); |
| 69 | 79 | ||
| 70 | FuncReturn(retval); | 80 | FuncReturn(system, retval); |
| 71 | } | 81 | } |
| 72 | 82 | ||
| 73 | template <ResultCode func(u32*, u64)> | 83 | template <ResultCode func(Core::System&, u32*, u64)> |
| 74 | void SvcWrap() { | 84 | void SvcWrap(Core::System& system) { |
| 75 | u32 param_1 = 0; | 85 | u32 param_1 = 0; |
| 76 | const u32 retval = func(¶m_1, Param(1)).raw; | 86 | const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; |
| 77 | Core::CurrentArmInterface().SetReg(1, param_1); | 87 | system.CurrentArmInterface().SetReg(1, param_1); |
| 78 | FuncReturn(retval); | 88 | FuncReturn(system, retval); |
| 79 | } | 89 | } |
| 80 | 90 | ||
| 81 | template <ResultCode func(u32*, u64, u32)> | 91 | template <ResultCode func(Core::System&, u32*, u64, u32)> |
| 82 | void SvcWrap() { | 92 | void SvcWrap(Core::System& system) { |
| 83 | u32 param_1 = 0; | 93 | u32 param_1 = 0; |
| 84 | const u32 retval = func(¶m_1, Param(1), static_cast<u32>(Param(2))).raw; | 94 | const u32 retval = |
| 85 | Core::CurrentArmInterface().SetReg(1, param_1); | 95 | func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw; |
| 86 | FuncReturn(retval); | 96 | |
| 97 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 98 | FuncReturn(system, retval); | ||
| 87 | } | 99 | } |
| 88 | 100 | ||
| 89 | template <ResultCode func(u64*, u32)> | 101 | template <ResultCode func(Core::System&, u64*, u32)> |
| 90 | void SvcWrap() { | 102 | void SvcWrap(Core::System& system) { |
| 91 | u64 param_1 = 0; | 103 | u64 param_1 = 0; |
| 92 | const u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw; | 104 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; |
| 93 | Core::CurrentArmInterface().SetReg(1, param_1); | 105 | |
| 94 | FuncReturn(retval); | 106 | system.CurrentArmInterface().SetReg(1, param_1); |
| 107 | FuncReturn(system, retval); | ||
| 95 | } | 108 | } |
| 96 | 109 | ||
| 97 | template <ResultCode func(u64, s32)> | 110 | template <ResultCode func(Core::System&, u64, s32)> |
| 98 | void SvcWrap() { | 111 | void SvcWrap(Core::System& system) { |
| 99 | FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw); | 112 | FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw); |
| 100 | } | 113 | } |
| 101 | 114 | ||
| 102 | template <ResultCode func(u64, u32)> | 115 | template <ResultCode func(Core::System&, u64, u32)> |
| 103 | void SvcWrap() { | 116 | void SvcWrap(Core::System& system) { |
| 104 | FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw); | 117 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw); |
| 105 | } | 118 | } |
| 106 | 119 | ||
| 107 | template <ResultCode func(u64*, u64)> | 120 | template <ResultCode func(Core::System&, u64*, u64)> |
| 108 | void SvcWrap() { | 121 | void SvcWrap(Core::System& system) { |
| 109 | u64 param_1 = 0; | 122 | u64 param_1 = 0; |
| 110 | u32 retval = func(¶m_1, Param(1)).raw; | 123 | const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; |
| 111 | Core::CurrentArmInterface().SetReg(1, param_1); | 124 | |
| 112 | FuncReturn(retval); | 125 | system.CurrentArmInterface().SetReg(1, param_1); |
| 126 | FuncReturn(system, retval); | ||
| 113 | } | 127 | } |
| 114 | 128 | ||
| 115 | template <ResultCode func(u64*, u32, u32)> | 129 | template <ResultCode func(Core::System&, u64*, u32, u32)> |
| 116 | void SvcWrap() { | 130 | void SvcWrap(Core::System& system) { |
| 117 | u64 param_1 = 0; | 131 | u64 param_1 = 0; |
| 118 | u32 retval = func(¶m_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw; | 132 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)), |
| 119 | Core::CurrentArmInterface().SetReg(1, param_1); | 133 | static_cast<u32>(Param(system, 2))) |
| 120 | FuncReturn(retval); | 134 | .raw; |
| 135 | |||
| 136 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 137 | FuncReturn(system, retval); | ||
| 121 | } | 138 | } |
| 122 | 139 | ||
| 123 | template <ResultCode func(u32, u64)> | 140 | template <ResultCode func(Core::System&, u32, u64)> |
| 124 | void SvcWrap() { | 141 | void SvcWrap(Core::System& system) { |
| 125 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); | 142 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw); |
| 126 | } | 143 | } |
| 127 | 144 | ||
| 128 | template <ResultCode func(u32, u32, u64)> | 145 | template <ResultCode func(Core::System&, u32, u32, u64)> |
| 129 | void SvcWrap() { | 146 | void SvcWrap(Core::System& system) { |
| 130 | FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw); | 147 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), |
| 148 | static_cast<u32>(Param(system, 1)), Param(system, 2)) | ||
| 149 | .raw); | ||
| 131 | } | 150 | } |
| 132 | 151 | ||
| 133 | template <ResultCode func(u32, u32*, u64*)> | 152 | template <ResultCode func(Core::System&, u32, u32*, u64*)> |
| 134 | void SvcWrap() { | 153 | void SvcWrap(Core::System& system) { |
| 135 | u32 param_1 = 0; | 154 | u32 param_1 = 0; |
| 136 | u64 param_2 = 0; | 155 | u64 param_2 = 0; |
| 137 | ResultCode retval = func(static_cast<u32>(Param(2)), ¶m_1, ¶m_2); | 156 | const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); |
| 138 | Core::CurrentArmInterface().SetReg(1, param_1); | ||
| 139 | Core::CurrentArmInterface().SetReg(2, param_2); | ||
| 140 | FuncReturn(retval.raw); | ||
| 141 | } | ||
| 142 | 157 | ||
| 143 | template <ResultCode func(u64, u64, u32, u32)> | 158 | system.CurrentArmInterface().SetReg(1, param_1); |
| 144 | void SvcWrap() { | 159 | system.CurrentArmInterface().SetReg(2, param_2); |
| 145 | FuncReturn( | 160 | FuncReturn(system, retval.raw); |
| 146 | func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw); | ||
| 147 | } | 161 | } |
| 148 | 162 | ||
| 149 | template <ResultCode func(u64, u64, u32, u64)> | 163 | template <ResultCode func(Core::System&, u64, u64, u32, u32)> |
| 150 | void SvcWrap() { | 164 | void SvcWrap(Core::System& system) { |
| 151 | FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw); | 165 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), |
| 166 | static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) | ||
| 167 | .raw); | ||
| 152 | } | 168 | } |
| 153 | 169 | ||
| 154 | template <ResultCode func(u32, u64, u32)> | 170 | template <ResultCode func(Core::System&, u64, u64, u32, u64)> |
| 155 | void SvcWrap() { | 171 | void SvcWrap(Core::System& system) { |
| 156 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw); | 172 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), |
| 173 | static_cast<u32>(Param(system, 2)), Param(system, 3)) | ||
| 174 | .raw); | ||
| 157 | } | 175 | } |
| 158 | 176 | ||
| 159 | template <ResultCode func(u64, u64, u64)> | 177 | template <ResultCode func(Core::System&, u32, u64, u32)> |
| 160 | void SvcWrap() { | 178 | void SvcWrap(Core::System& system) { |
| 161 | FuncReturn(func(Param(0), Param(1), Param(2)).raw); | 179 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), |
| 180 | static_cast<u32>(Param(system, 2))) | ||
| 181 | .raw); | ||
| 162 | } | 182 | } |
| 163 | 183 | ||
| 164 | template <ResultCode func(u64, u64, u32)> | 184 | template <ResultCode func(Core::System&, u64, u64, u64)> |
| 165 | void SvcWrap() { | 185 | void SvcWrap(Core::System& system) { |
| 166 | FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw); | 186 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw); |
| 167 | } | 187 | } |
| 168 | 188 | ||
| 169 | template <ResultCode func(u32, u64, u64, u32)> | 189 | template <ResultCode func(Core::System&, u64, u64, u32)> |
| 170 | void SvcWrap() { | 190 | void SvcWrap(Core::System& system) { |
| 171 | FuncReturn( | 191 | FuncReturn( |
| 172 | func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw); | 192 | system, |
| 193 | func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw); | ||
| 173 | } | 194 | } |
| 174 | 195 | ||
| 175 | template <ResultCode func(u32, u64, u64)> | 196 | template <ResultCode func(Core::System&, u32, u64, u64, u32)> |
| 176 | void SvcWrap() { | 197 | void SvcWrap(Core::System& system) { |
| 177 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw); | 198 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), |
| 199 | Param(system, 2), static_cast<u32>(Param(system, 3))) | ||
| 200 | .raw); | ||
| 178 | } | 201 | } |
| 179 | 202 | ||
| 180 | template <ResultCode func(u32*, u64, u64, s64)> | 203 | template <ResultCode func(Core::System&, u32, u64, u64)> |
| 181 | void SvcWrap() { | 204 | void SvcWrap(Core::System& system) { |
| 205 | FuncReturn( | ||
| 206 | system, | ||
| 207 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); | ||
| 208 | } | ||
| 209 | |||
| 210 | template <ResultCode func(Core::System&, u32*, u64, u64, s64)> | ||
| 211 | void SvcWrap(Core::System& system) { | ||
| 182 | u32 param_1 = 0; | 212 | u32 param_1 = 0; |
| 183 | ResultCode retval = | 213 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), |
| 184 | func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))); | 214 | static_cast<s64>(Param(system, 3))) |
| 185 | Core::CurrentArmInterface().SetReg(1, param_1); | 215 | .raw; |
| 186 | FuncReturn(retval.raw); | 216 | |
| 217 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 218 | FuncReturn(system, retval); | ||
| 187 | } | 219 | } |
| 188 | 220 | ||
| 189 | template <ResultCode func(u64, u64, u32, s64)> | 221 | template <ResultCode func(Core::System&, u64, u64, u32, s64)> |
| 190 | void SvcWrap() { | 222 | void SvcWrap(Core::System& system) { |
| 191 | FuncReturn( | 223 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), |
| 192 | func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw); | 224 | static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) |
| 225 | .raw); | ||
| 193 | } | 226 | } |
| 194 | 227 | ||
| 195 | template <ResultCode func(u64*, u64, u64, u64)> | 228 | template <ResultCode func(Core::System&, u64*, u64, u64, u64)> |
| 196 | void SvcWrap() { | 229 | void SvcWrap(Core::System& system) { |
| 197 | u64 param_1 = 0; | 230 | u64 param_1 = 0; |
| 198 | u32 retval = func(¶m_1, Param(1), Param(2), Param(3)).raw; | 231 | const u32 retval = |
| 199 | Core::CurrentArmInterface().SetReg(1, param_1); | 232 | func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw; |
| 200 | FuncReturn(retval); | 233 | |
| 234 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 235 | FuncReturn(system, retval); | ||
| 201 | } | 236 | } |
| 202 | 237 | ||
| 203 | template <ResultCode func(u32*, u64, u64, u64, u32, s32)> | 238 | template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> |
| 204 | void SvcWrap() { | 239 | void SvcWrap(Core::System& system) { |
| 205 | u32 param_1 = 0; | 240 | u32 param_1 = 0; |
| 206 | u32 retval = func(¶m_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)), | 241 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3), |
| 207 | static_cast<s32>(Param(5))) | 242 | static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5))) |
| 208 | .raw; | 243 | .raw; |
| 209 | Core::CurrentArmInterface().SetReg(1, param_1); | 244 | |
| 210 | FuncReturn(retval); | 245 | system.CurrentArmInterface().SetReg(1, param_1); |
| 246 | FuncReturn(system, retval); | ||
| 211 | } | 247 | } |
| 212 | 248 | ||
| 213 | template <ResultCode func(u32*, u64, u64, u32)> | 249 | template <ResultCode func(Core::System&, u32*, u64, u64, u32)> |
| 214 | void SvcWrap() { | 250 | void SvcWrap(Core::System& system) { |
| 215 | u32 param_1 = 0; | 251 | u32 param_1 = 0; |
| 216 | u32 retval = func(¶m_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw; | 252 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), |
| 217 | Core::CurrentArmInterface().SetReg(1, param_1); | 253 | static_cast<u32>(Param(system, 3))) |
| 218 | FuncReturn(retval); | 254 | .raw; |
| 255 | |||
| 256 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 257 | FuncReturn(system, retval); | ||
| 219 | } | 258 | } |
| 220 | 259 | ||
| 221 | template <ResultCode func(Handle*, u64, u32, u32)> | 260 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> |
| 222 | void SvcWrap() { | 261 | void SvcWrap(Core::System& system) { |
| 223 | u32 param_1 = 0; | 262 | u32 param_1 = 0; |
| 224 | u32 retval = | 263 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), |
| 225 | func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw; | 264 | static_cast<u32>(Param(system, 3))) |
| 226 | Core::CurrentArmInterface().SetReg(1, param_1); | 265 | .raw; |
| 227 | FuncReturn(retval); | 266 | |
| 267 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 268 | FuncReturn(system, retval); | ||
| 228 | } | 269 | } |
| 229 | 270 | ||
| 230 | template <ResultCode func(u64, u32, s32, s64)> | 271 | template <ResultCode func(Core::System&, u64, u32, s32, s64)> |
| 231 | void SvcWrap() { | 272 | void SvcWrap(Core::System& system) { |
| 232 | FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), | 273 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), |
| 233 | static_cast<s64>(Param(3))) | 274 | static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) |
| 234 | .raw); | 275 | .raw); |
| 235 | } | 276 | } |
| 236 | 277 | ||
| 237 | template <ResultCode func(u64, u32, s32, s32)> | 278 | template <ResultCode func(Core::System&, u64, u32, s32, s32)> |
| 238 | void SvcWrap() { | 279 | void SvcWrap(Core::System& system) { |
| 239 | FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), | 280 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), |
| 240 | static_cast<s32>(Param(3))) | 281 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) |
| 241 | .raw); | 282 | .raw); |
| 242 | } | 283 | } |
| 243 | 284 | ||
| 244 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 285 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 245 | // Function wrappers that return type u32 | 286 | // Function wrappers that return type u32 |
| 246 | 287 | ||
| 247 | template <u32 func()> | 288 | template <u32 func(Core::System&)> |
| 248 | void SvcWrap() { | 289 | void SvcWrap(Core::System& system) { |
| 249 | FuncReturn(func()); | 290 | FuncReturn(system, func(system)); |
| 250 | } | 291 | } |
| 251 | 292 | ||
| 252 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 293 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 253 | // Function wrappers that return type u64 | 294 | // Function wrappers that return type u64 |
| 254 | 295 | ||
| 255 | template <u64 func()> | 296 | template <u64 func(Core::System&)> |
| 256 | void SvcWrap() { | 297 | void SvcWrap(Core::System& system) { |
| 257 | FuncReturn(func()); | 298 | FuncReturn(system, func(system)); |
| 258 | } | 299 | } |
| 259 | 300 | ||
| 260 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 301 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 261 | /// Function wrappers that return type void | 302 | /// Function wrappers that return type void |
| 262 | 303 | ||
| 263 | template <void func()> | 304 | template <void func(Core::System&)> |
| 264 | void SvcWrap() { | 305 | void SvcWrap(Core::System& system) { |
| 265 | func(); | 306 | func(system); |
| 266 | } | 307 | } |
| 267 | 308 | ||
| 268 | template <void func(s64)> | 309 | template <void func(Core::System&, s64)> |
| 269 | void SvcWrap() { | 310 | void SvcWrap(Core::System& system) { |
| 270 | func(static_cast<s64>(Param(0))); | 311 | func(system, static_cast<s64>(Param(system, 0))); |
| 271 | } | 312 | } |
| 272 | 313 | ||
| 273 | template <void func(u64, u64 len)> | 314 | template <void func(Core::System&, u64, u64)> |
| 274 | void SvcWrap() { | 315 | void SvcWrap(Core::System& system) { |
| 275 | func(Param(0), Param(1)); | 316 | func(system, Param(system, 0), Param(system, 1)); |
| 276 | } | 317 | } |
| 277 | 318 | ||
| 278 | template <void func(u64, u64, u64)> | 319 | template <void func(Core::System&, u64, u64, u64)> |
| 279 | void SvcWrap() { | 320 | void SvcWrap(Core::System& system) { |
| 280 | func(Param(0), Param(1), Param(2)); | 321 | func(system, Param(system, 0), Param(system, 1), Param(system, 2)); |
| 281 | } | 322 | } |
| 282 | 323 | ||
| 283 | template <void func(u32, u64, u64)> | 324 | template <void func(Core::System&, u32, u64, u64)> |
| 284 | void SvcWrap() { | 325 | void SvcWrap(Core::System& system) { |
| 285 | func(static_cast<u32>(Param(0)), Param(1), Param(2)); | 326 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); |
| 286 | } | 327 | } |
| 287 | 328 | ||
| 288 | } // namespace Kernel | 329 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1b891f632..ca52267b2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -220,11 +220,6 @@ void Thread::SetPriority(u32 priority) { | |||
| 220 | UpdatePriority(); | 220 | UpdatePriority(); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | void Thread::BoostPriority(u32 priority) { | ||
| 224 | scheduler->SetThreadPriority(this, priority); | ||
| 225 | current_priority = priority; | ||
| 226 | } | ||
| 227 | |||
| 228 | void Thread::SetWaitSynchronizationResult(ResultCode result) { | 223 | void Thread::SetWaitSynchronizationResult(ResultCode result) { |
| 229 | context.cpu_registers[0] = result.raw; | 224 | context.cpu_registers[0] = result.raw; |
| 230 | } | 225 | } |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 73e5d1bb4..32026d7f0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -106,7 +106,7 @@ public: | |||
| 106 | return "Thread"; | 106 | return "Thread"; |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | static const HandleType HANDLE_TYPE = HandleType::Thread; | 109 | static constexpr HandleType HANDLE_TYPE = HandleType::Thread; |
| 110 | HandleType GetHandleType() const override { | 110 | HandleType GetHandleType() const override { |
| 111 | return HANDLE_TYPE; | 111 | return HANDLE_TYPE; |
| 112 | } | 112 | } |
| @@ -136,12 +136,6 @@ public: | |||
| 136 | */ | 136 | */ |
| 137 | void SetPriority(u32 priority); | 137 | void SetPriority(u32 priority); |
| 138 | 138 | ||
| 139 | /** | ||
| 140 | * Temporarily boosts the thread's priority until the next time it is scheduled | ||
| 141 | * @param priority The new priority | ||
| 142 | */ | ||
| 143 | void BoostPriority(u32 priority); | ||
| 144 | |||
| 145 | /// Adds a thread to the list of threads that are waiting for a lock held by this thread. | 139 | /// Adds a thread to the list of threads that are waiting for a lock held by this thread. |
| 146 | void AddMutexWaiter(SharedPtr<Thread> thread); | 140 | void AddMutexWaiter(SharedPtr<Thread> thread); |
| 147 | 141 | ||
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index 23228e1b5..26c4e5e67 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp | |||
| @@ -14,8 +14,8 @@ namespace Kernel { | |||
| 14 | TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} | 14 | TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} |
| 15 | TransferMemory::~TransferMemory() = default; | 15 | TransferMemory::~TransferMemory() = default; |
| 16 | 16 | ||
| 17 | SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, | 17 | SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, u64 size, |
| 18 | size_t size, MemoryPermission permissions) { | 18 | MemoryPermission permissions) { |
| 19 | SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)}; | 19 | SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)}; |
| 20 | 20 | ||
| 21 | transfer_memory->base_address = base_address; | 21 | transfer_memory->base_address = base_address; |
| @@ -26,7 +26,15 @@ SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_ | |||
| 26 | return transfer_memory; | 26 | return transfer_memory; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermission permissions) { | 29 | const u8* TransferMemory::GetPointer() const { |
| 30 | return backing_block.get()->data(); | ||
| 31 | } | ||
| 32 | |||
| 33 | u64 TransferMemory::GetSize() const { | ||
| 34 | return memory_size; | ||
| 35 | } | ||
| 36 | |||
| 37 | ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) { | ||
| 30 | if (memory_size != size) { | 38 | if (memory_size != size) { |
| 31 | return ERR_INVALID_SIZE; | 39 | return ERR_INVALID_SIZE; |
| 32 | } | 40 | } |
| @@ -39,13 +47,13 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio | |||
| 39 | return ERR_INVALID_STATE; | 47 | return ERR_INVALID_STATE; |
| 40 | } | 48 | } |
| 41 | 49 | ||
| 50 | backing_block = std::make_shared<std::vector<u8>>(size); | ||
| 51 | |||
| 42 | const auto map_state = owner_permissions == MemoryPermission::None | 52 | const auto map_state = owner_permissions == MemoryPermission::None |
| 43 | ? MemoryState::TransferMemoryIsolated | 53 | ? MemoryState::TransferMemoryIsolated |
| 44 | : MemoryState::TransferMemory; | 54 | : MemoryState::TransferMemory; |
| 45 | auto& vm_manager = owner_process->VMManager(); | 55 | auto& vm_manager = owner_process->VMManager(); |
| 46 | const auto map_result = vm_manager.MapMemoryBlock( | 56 | const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state); |
| 47 | address, std::make_shared<std::vector<u8>>(size), 0, size, map_state); | ||
| 48 | |||
| 49 | if (map_result.Failed()) { | 57 | if (map_result.Failed()) { |
| 50 | return map_result.Code(); | 58 | return map_result.Code(); |
| 51 | } | 59 | } |
| @@ -54,7 +62,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio | |||
| 54 | return RESULT_SUCCESS; | 62 | return RESULT_SUCCESS; |
| 55 | } | 63 | } |
| 56 | 64 | ||
| 57 | ResultCode TransferMemory::UnmapMemory(VAddr address, size_t size) { | 65 | ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { |
| 58 | if (memory_size != size) { | 66 | if (memory_size != size) { |
| 59 | return ERR_INVALID_SIZE; | 67 | return ERR_INVALID_SIZE; |
| 60 | } | 68 | } |
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index ec294951e..a140b1e2b 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 7 | #include "core/hle/kernel/object.h" | 10 | #include "core/hle/kernel/object.h" |
| 8 | 11 | ||
| 9 | union ResultCode; | 12 | union ResultCode; |
| @@ -25,7 +28,7 @@ class TransferMemory final : public Object { | |||
| 25 | public: | 28 | public: |
| 26 | static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; | 29 | static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; |
| 27 | 30 | ||
| 28 | static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, size_t size, | 31 | static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size, |
| 29 | MemoryPermission permissions); | 32 | MemoryPermission permissions); |
| 30 | 33 | ||
| 31 | TransferMemory(const TransferMemory&) = delete; | 34 | TransferMemory(const TransferMemory&) = delete; |
| @@ -46,6 +49,12 @@ public: | |||
| 46 | return HANDLE_TYPE; | 49 | return HANDLE_TYPE; |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 52 | /// Gets a pointer to the backing block of this instance. | ||
| 53 | const u8* GetPointer() const; | ||
| 54 | |||
| 55 | /// Gets the size of the memory backing this instance in bytes. | ||
| 56 | u64 GetSize() const; | ||
| 57 | |||
| 49 | /// Attempts to map transfer memory with the given range and memory permissions. | 58 | /// Attempts to map transfer memory with the given range and memory permissions. |
| 50 | /// | 59 | /// |
| 51 | /// @param address The base address to being mapping memory at. | 60 | /// @param address The base address to being mapping memory at. |
| @@ -56,7 +65,7 @@ public: | |||
| 56 | /// the same values that were given when creating the transfer memory | 65 | /// the same values that were given when creating the transfer memory |
| 57 | /// instance. | 66 | /// instance. |
| 58 | /// | 67 | /// |
| 59 | ResultCode MapMemory(VAddr address, size_t size, MemoryPermission permissions); | 68 | ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions); |
| 60 | 69 | ||
| 61 | /// Unmaps the transfer memory with the given range | 70 | /// Unmaps the transfer memory with the given range |
| 62 | /// | 71 | /// |
| @@ -66,17 +75,20 @@ public: | |||
| 66 | /// @pre The given address and size must be the same as the ones used | 75 | /// @pre The given address and size must be the same as the ones used |
| 67 | /// to create the transfer memory instance. | 76 | /// to create the transfer memory instance. |
| 68 | /// | 77 | /// |
| 69 | ResultCode UnmapMemory(VAddr address, size_t size); | 78 | ResultCode UnmapMemory(VAddr address, u64 size); |
| 70 | 79 | ||
| 71 | private: | 80 | private: |
| 72 | explicit TransferMemory(KernelCore& kernel); | 81 | explicit TransferMemory(KernelCore& kernel); |
| 73 | ~TransferMemory() override; | 82 | ~TransferMemory() override; |
| 74 | 83 | ||
| 84 | /// Memory block backing this instance. | ||
| 85 | std::shared_ptr<std::vector<u8>> backing_block; | ||
| 86 | |||
| 75 | /// The base address for the memory managed by this instance. | 87 | /// The base address for the memory managed by this instance. |
| 76 | VAddr base_address = 0; | 88 | VAddr base_address = 0; |
| 77 | 89 | ||
| 78 | /// Size of the memory, in bytes, that this instance manages. | 90 | /// Size of the memory, in bytes, that this instance manages. |
| 79 | size_t memory_size = 0; | 91 | u64 memory_size = 0; |
| 80 | 92 | ||
| 81 | /// The memory permissions that are applied to this instance. | 93 | /// The memory permissions that are applied to this instance. |
| 82 | MemoryPermission owner_permissions{}; | 94 | MemoryPermission owner_permissions{}; |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index ec0a480ce..f0c0c12fc 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -302,6 +302,86 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { | |||
| 302 | return MakeResult<VAddr>(heap_region_base); | 302 | return MakeResult<VAddr>(heap_region_base); |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { | ||
| 306 | constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; | ||
| 307 | const auto src_check_result = CheckRangeState( | ||
| 308 | src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All, | ||
| 309 | VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); | ||
| 310 | |||
| 311 | if (src_check_result.Failed()) { | ||
| 312 | return src_check_result.Code(); | ||
| 313 | } | ||
| 314 | |||
| 315 | const auto mirror_result = | ||
| 316 | MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode); | ||
| 317 | if (mirror_result.IsError()) { | ||
| 318 | return mirror_result; | ||
| 319 | } | ||
| 320 | |||
| 321 | // Ensure we lock the source memory region. | ||
| 322 | const auto src_vma_result = CarveVMARange(src_address, size); | ||
| 323 | if (src_vma_result.Failed()) { | ||
| 324 | return src_vma_result.Code(); | ||
| 325 | } | ||
| 326 | auto src_vma_iter = *src_vma_result; | ||
| 327 | src_vma_iter->second.attribute = MemoryAttribute::Locked; | ||
| 328 | Reprotect(src_vma_iter, VMAPermission::Read); | ||
| 329 | |||
| 330 | // The destination memory region is fine as is, however we need to make it read-only. | ||
| 331 | return ReprotectRange(dst_address, size, VMAPermission::Read); | ||
| 332 | } | ||
| 333 | |||
| 334 | ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { | ||
| 335 | constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; | ||
| 336 | const auto src_check_result = CheckRangeState( | ||
| 337 | src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None, | ||
| 338 | VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute); | ||
| 339 | |||
| 340 | if (src_check_result.Failed()) { | ||
| 341 | return src_check_result.Code(); | ||
| 342 | } | ||
| 343 | |||
| 344 | // Yes, the kernel only checks the first page of the region. | ||
| 345 | const auto dst_check_result = | ||
| 346 | CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule, | ||
| 347 | MemoryState::FlagModule, VMAPermission::None, VMAPermission::None, | ||
| 348 | MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); | ||
| 349 | |||
| 350 | if (dst_check_result.Failed()) { | ||
| 351 | return dst_check_result.Code(); | ||
| 352 | } | ||
| 353 | |||
| 354 | const auto dst_memory_state = std::get<MemoryState>(*dst_check_result); | ||
| 355 | const auto dst_contiguous_check_result = CheckRangeState( | ||
| 356 | dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None, | ||
| 357 | VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); | ||
| 358 | |||
| 359 | if (dst_contiguous_check_result.Failed()) { | ||
| 360 | return dst_contiguous_check_result.Code(); | ||
| 361 | } | ||
| 362 | |||
| 363 | const auto unmap_result = UnmapRange(dst_address, size); | ||
| 364 | if (unmap_result.IsError()) { | ||
| 365 | return unmap_result; | ||
| 366 | } | ||
| 367 | |||
| 368 | // With the mirrored portion unmapped, restore the original region's traits. | ||
| 369 | const auto src_vma_result = CarveVMARange(src_address, size); | ||
| 370 | if (src_vma_result.Failed()) { | ||
| 371 | return src_vma_result.Code(); | ||
| 372 | } | ||
| 373 | auto src_vma_iter = *src_vma_result; | ||
| 374 | src_vma_iter->second.state = MemoryState::Heap; | ||
| 375 | src_vma_iter->second.attribute = MemoryAttribute::None; | ||
| 376 | Reprotect(src_vma_iter, VMAPermission::ReadWrite); | ||
| 377 | |||
| 378 | if (dst_memory_state == MemoryState::ModuleCode) { | ||
| 379 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | ||
| 380 | } | ||
| 381 | |||
| 382 | return unmap_result; | ||
| 383 | } | ||
| 384 | |||
| 305 | MemoryInfo VMManager::QueryMemory(VAddr address) const { | 385 | MemoryInfo VMManager::QueryMemory(VAddr address) const { |
| 306 | const auto vma = FindVMA(address); | 386 | const auto vma = FindVMA(address); |
| 307 | MemoryInfo memory_info{}; | 387 | MemoryInfo memory_info{}; |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 6f484b7bf..288eb9450 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -43,6 +43,9 @@ enum class VMAPermission : u8 { | |||
| 43 | ReadExecute = Read | Execute, | 43 | ReadExecute = Read | Execute, |
| 44 | WriteExecute = Write | Execute, | 44 | WriteExecute = Write | Execute, |
| 45 | ReadWriteExecute = Read | Write | Execute, | 45 | ReadWriteExecute = Read | Write | Execute, |
| 46 | |||
| 47 | // Used as a wildcard when checking permissions across memory ranges | ||
| 48 | All = 0xFF, | ||
| 46 | }; | 49 | }; |
| 47 | 50 | ||
| 48 | constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { | 51 | constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { |
| @@ -152,6 +155,9 @@ enum class MemoryState : u32 { | |||
| 152 | FlagUncached = 1U << 24, | 155 | FlagUncached = 1U << 24, |
| 153 | FlagCodeMemory = 1U << 25, | 156 | FlagCodeMemory = 1U << 25, |
| 154 | 157 | ||
| 158 | // Wildcard used in range checking to indicate all states. | ||
| 159 | All = 0xFFFFFFFF, | ||
| 160 | |||
| 155 | // Convenience flag sets to reduce repetition | 161 | // Convenience flag sets to reduce repetition |
| 156 | IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, | 162 | IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, |
| 157 | 163 | ||
| @@ -415,6 +421,49 @@ public: | |||
| 415 | /// | 421 | /// |
| 416 | ResultVal<VAddr> SetHeapSize(u64 size); | 422 | ResultVal<VAddr> SetHeapSize(u64 size); |
| 417 | 423 | ||
| 424 | /// Maps a region of memory as code memory. | ||
| 425 | /// | ||
| 426 | /// @param dst_address The base address of the region to create the aliasing memory region. | ||
| 427 | /// @param src_address The base address of the region to be aliased. | ||
| 428 | /// @param size The total amount of memory to map in bytes. | ||
| 429 | /// | ||
| 430 | /// @pre Both memory regions lie within the actual addressable address space. | ||
| 431 | /// | ||
| 432 | /// @post After this function finishes execution, assuming success, then the address range | ||
| 433 | /// [dst_address, dst_address+size) will alias the memory region, | ||
| 434 | /// [src_address, src_address+size). | ||
| 435 | /// <p> | ||
| 436 | /// What this also entails is as follows: | ||
| 437 | /// 1. The aliased region gains the Locked memory attribute. | ||
| 438 | /// 2. The aliased region becomes read-only. | ||
| 439 | /// 3. The aliasing region becomes read-only. | ||
| 440 | /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule. | ||
| 441 | /// | ||
| 442 | ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); | ||
| 443 | |||
| 444 | /// Unmaps a region of memory designated as code module memory. | ||
| 445 | /// | ||
| 446 | /// @param dst_address The base address of the memory region aliasing the source memory region. | ||
| 447 | /// @param src_address The base address of the memory region being aliased. | ||
| 448 | /// @param size The size of the memory region to unmap in bytes. | ||
| 449 | /// | ||
| 450 | /// @pre Both memory ranges lie within the actual addressable address space. | ||
| 451 | /// | ||
| 452 | /// @pre The memory region being unmapped has been previously been mapped | ||
| 453 | /// by a call to MapCodeMemory. | ||
| 454 | /// | ||
| 455 | /// @post After execution of the function, if successful. the aliasing memory region | ||
| 456 | /// will be unmapped and the aliased region will have various traits about it | ||
| 457 | /// restored to what they were prior to the original mapping call preceding | ||
| 458 | /// this function call. | ||
| 459 | /// <p> | ||
| 460 | /// What this also entails is as follows: | ||
| 461 | /// 1. The state of the memory region will now indicate a general heap region. | ||
| 462 | /// 2. All memory attributes for the memory region are cleared. | ||
| 463 | /// 3. Memory permissions for the region are restored to user read/write. | ||
| 464 | /// | ||
| 465 | ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); | ||
| 466 | |||
| 418 | /// Queries the memory manager for information about the given address. | 467 | /// Queries the memory manager for information about the given address. |
| 419 | /// | 468 | /// |
| 420 | /// @param address The address to query the memory manager about for information. | 469 | /// @param address The address to query the memory manager about for information. |
diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h index c9068dd3d..d00c92a6b 100644 --- a/src/core/hle/kernel/writable_event.h +++ b/src/core/hle/kernel/writable_event.h | |||
| @@ -37,7 +37,7 @@ public: | |||
| 37 | return name; | 37 | return name; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | static const HandleType HANDLE_TYPE = HandleType::WritableEvent; | 40 | static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent; |
| 41 | HandleType GetHandleType() const override { | 41 | HandleType GetHandleType() const override { |
| 42 | return HANDLE_TYPE; | 42 | return HANDLE_TYPE; |
| 43 | } | 43 | } |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index ab84f5ddc..8a3701151 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -119,10 +119,6 @@ union ResultCode { | |||
| 119 | BitField<0, 9, ErrorModule> module; | 119 | BitField<0, 9, ErrorModule> module; |
| 120 | BitField<9, 13, u32> description; | 120 | BitField<9, 13, u32> description; |
| 121 | 121 | ||
| 122 | // The last bit of `level` is checked by apps and the kernel to determine if a result code is an | ||
| 123 | // error | ||
| 124 | BitField<31, 1, u32> is_error; | ||
| 125 | |||
| 126 | constexpr explicit ResultCode(u32 raw) : raw(raw) {} | 122 | constexpr explicit ResultCode(u32 raw) : raw(raw) {} |
| 127 | 123 | ||
| 128 | constexpr ResultCode(ErrorModule module_, u32 description_) | 124 | constexpr ResultCode(ErrorModule module_, u32 description_) |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 1f8ed265e..ba7d7acbd 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -137,6 +137,7 @@ private: | |||
| 137 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { | 137 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { |
| 138 | public: | 138 | public: |
| 139 | IManagerForApplication() : ServiceFramework("IManagerForApplication") { | 139 | IManagerForApplication() : ServiceFramework("IManagerForApplication") { |
| 140 | // clang-format off | ||
| 140 | static const FunctionInfo functions[] = { | 141 | static const FunctionInfo functions[] = { |
| 141 | {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, | 142 | {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, |
| 142 | {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, | 143 | {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, |
| @@ -145,7 +146,10 @@ public: | |||
| 145 | {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, | 146 | {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, |
| 146 | {150, nullptr, "CreateAuthorizationRequest"}, | 147 | {150, nullptr, "CreateAuthorizationRequest"}, |
| 147 | {160, nullptr, "StoreOpenContext"}, | 148 | {160, nullptr, "StoreOpenContext"}, |
| 149 | {170, nullptr, "LoadNetworkServiceLicenseKindAsync"}, | ||
| 148 | }; | 150 | }; |
| 151 | // clang-format on | ||
| 152 | |||
| 149 | RegisterHandlers(functions); | 153 | RegisterHandlers(functions); |
| 150 | } | 154 | } |
| 151 | 155 | ||
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 5e2030355..d66233cad 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp | |||
| @@ -8,6 +8,7 @@ namespace Service::Account { | |||
| 8 | 8 | ||
| 9 | ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) | 9 | ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) |
| 10 | : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") { | 10 | : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") { |
| 11 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | 12 | static const FunctionInfo functions[] = { |
| 12 | {0, &ACC_SU::GetUserCount, "GetUserCount"}, | 13 | {0, &ACC_SU::GetUserCount, "GetUserCount"}, |
| 13 | {1, &ACC_SU::GetUserExistence, "GetUserExistence"}, | 14 | {1, &ACC_SU::GetUserExistence, "GetUserExistence"}, |
| @@ -19,6 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 19 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 20 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 20 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 21 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 21 | {60, nullptr, "ListOpenContextStoredUsers"}, | 22 | {60, nullptr, "ListOpenContextStoredUsers"}, |
| 23 | {99, nullptr, "DebugActivateOpenContextRetention"}, | ||
| 22 | {100, nullptr, "GetUserRegistrationNotifier"}, | 24 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 23 | {101, nullptr, "GetUserStateChangeNotifier"}, | 25 | {101, nullptr, "GetUserStateChangeNotifier"}, |
| 24 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, | 26 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, |
| @@ -29,6 +31,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 29 | {111, nullptr, "ClearSaveDataThumbnail"}, | 31 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 30 | {112, nullptr, "LoadSaveDataThumbnail"}, | 32 | {112, nullptr, "LoadSaveDataThumbnail"}, |
| 31 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 33 | {113, nullptr, "GetSaveDataThumbnailExistence"}, |
| 34 | {130, nullptr, "ActivateOpenContextRetention"}, | ||
| 35 | {140, nullptr, "ListQualifiedUsers"}, | ||
| 32 | {190, nullptr, "GetUserLastOpenedApplication"}, | 36 | {190, nullptr, "GetUserLastOpenedApplication"}, |
| 33 | {191, nullptr, "ActivateOpenContextHolder"}, | 37 | {191, nullptr, "ActivateOpenContextHolder"}, |
| 34 | {200, nullptr, "BeginUserRegistration"}, | 38 | {200, nullptr, "BeginUserRegistration"}, |
| @@ -48,6 +52,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 48 | {998, nullptr, "DebugSetUserStateClose"}, | 52 | {998, nullptr, "DebugSetUserStateClose"}, |
| 49 | {999, nullptr, "DebugSetUserStateOpen"}, | 53 | {999, nullptr, "DebugSetUserStateOpen"}, |
| 50 | }; | 54 | }; |
| 55 | // clang-format on | ||
| 56 | |||
| 51 | RegisterHandlers(functions); | 57 | RegisterHandlers(functions); |
| 52 | } | 58 | } |
| 53 | 59 | ||
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index a4d705b45..182f7c7e5 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp | |||
| @@ -8,6 +8,7 @@ namespace Service::Account { | |||
| 8 | 8 | ||
| 9 | ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) | 9 | ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) |
| 10 | : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") { | 10 | : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") { |
| 11 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | 12 | static const FunctionInfo functions[] = { |
| 12 | {0, &ACC_U0::GetUserCount, "GetUserCount"}, | 13 | {0, &ACC_U0::GetUserCount, "GetUserCount"}, |
| 13 | {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, | 14 | {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, |
| @@ -19,6 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 19 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 20 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 20 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 21 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 21 | {60, nullptr, "ListOpenContextStoredUsers"}, | 22 | {60, nullptr, "ListOpenContextStoredUsers"}, |
| 23 | {99, nullptr, "DebugActivateOpenContextRetention"}, | ||
| 22 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, | 24 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, |
| 23 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, | 25 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, |
| 24 | {102, nullptr, "AuthenticateApplicationAsync"}, | 26 | {102, nullptr, "AuthenticateApplicationAsync"}, |
| @@ -27,7 +29,13 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 27 | {111, nullptr, "ClearSaveDataThumbnail"}, | 29 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 28 | {120, nullptr, "CreateGuestLoginRequest"}, | 30 | {120, nullptr, "CreateGuestLoginRequest"}, |
| 29 | {130, nullptr, "LoadOpenContext"}, | 31 | {130, nullptr, "LoadOpenContext"}, |
| 32 | {131, nullptr, "ListOpenContextStoredUsers"}, | ||
| 33 | {140, nullptr, "InitializeApplicationInfo"}, | ||
| 34 | {141, nullptr, "ListQualifiedUsers"}, | ||
| 35 | {150, nullptr, "IsUserAccountSwitchLocked"}, | ||
| 30 | }; | 36 | }; |
| 37 | // clang-format on | ||
| 38 | |||
| 31 | RegisterHandlers(functions); | 39 | RegisterHandlers(functions); |
| 32 | } | 40 | } |
| 33 | 41 | ||
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 8fffc93b5..2dd17d935 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp | |||
| @@ -8,6 +8,7 @@ namespace Service::Account { | |||
| 8 | 8 | ||
| 9 | ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) | 9 | ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) |
| 10 | : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") { | 10 | : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") { |
| 11 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | 12 | static const FunctionInfo functions[] = { |
| 12 | {0, &ACC_U1::GetUserCount, "GetUserCount"}, | 13 | {0, &ACC_U1::GetUserCount, "GetUserCount"}, |
| 13 | {1, &ACC_U1::GetUserExistence, "GetUserExistence"}, | 14 | {1, &ACC_U1::GetUserExistence, "GetUserExistence"}, |
| @@ -19,6 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 19 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 20 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 20 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 21 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 21 | {60, nullptr, "ListOpenContextStoredUsers"}, | 22 | {60, nullptr, "ListOpenContextStoredUsers"}, |
| 23 | {99, nullptr, "DebugActivateOpenContextRetention"}, | ||
| 22 | {100, nullptr, "GetUserRegistrationNotifier"}, | 24 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 23 | {101, nullptr, "GetUserStateChangeNotifier"}, | 25 | {101, nullptr, "GetUserStateChangeNotifier"}, |
| 24 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, | 26 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, |
| @@ -29,12 +31,16 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 29 | {111, nullptr, "ClearSaveDataThumbnail"}, | 31 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 30 | {112, nullptr, "LoadSaveDataThumbnail"}, | 32 | {112, nullptr, "LoadSaveDataThumbnail"}, |
| 31 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 33 | {113, nullptr, "GetSaveDataThumbnailExistence"}, |
| 34 | {130, nullptr, "ActivateOpenContextRetention"}, | ||
| 35 | {140, nullptr, "ListQualifiedUsers"}, | ||
| 32 | {190, nullptr, "GetUserLastOpenedApplication"}, | 36 | {190, nullptr, "GetUserLastOpenedApplication"}, |
| 33 | {191, nullptr, "ActivateOpenContextHolder"}, | 37 | {191, nullptr, "ActivateOpenContextHolder"}, |
| 34 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, | 38 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, |
| 35 | {998, nullptr, "DebugSetUserStateClose"}, | 39 | {998, nullptr, "DebugSetUserStateClose"}, |
| 36 | {999, nullptr, "DebugSetUserStateOpen"}, | 40 | {999, nullptr, "DebugSetUserStateOpen"}, |
| 37 | }; | 41 | }; |
| 42 | // clang-format on | ||
| 43 | |||
| 38 | RegisterHandlers(functions); | 44 | RegisterHandlers(functions); |
| 39 | } | 45 | } |
| 40 | 46 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d31ab7970..1aa4ce1ac 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include "core/hle/kernel/kernel.h" | 13 | #include "core/hle/kernel/kernel.h" |
| 14 | #include "core/hle/kernel/process.h" | 14 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/hle/kernel/readable_event.h" | 15 | #include "core/hle/kernel/readable_event.h" |
| 16 | #include "core/hle/kernel/shared_memory.h" | 16 | #include "core/hle/kernel/transfer_memory.h" |
| 17 | #include "core/hle/kernel/writable_event.h" | 17 | #include "core/hle/kernel/writable_event.h" |
| 18 | #include "core/hle/service/acc/profile_manager.h" | 18 | #include "core/hle/service/acc/profile_manager.h" |
| 19 | #include "core/hle/service/am/am.h" | 19 | #include "core/hle/service/am/am.h" |
| @@ -224,6 +224,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { | |||
| 224 | {20, nullptr, "InvalidateTransitionLayer"}, | 224 | {20, nullptr, "InvalidateTransitionLayer"}, |
| 225 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, | 225 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, |
| 226 | {40, nullptr, "GetAppletResourceUsageInfo"}, | 226 | {40, nullptr, "GetAppletResourceUsageInfo"}, |
| 227 | {41, nullptr, "SetCpuBoostModeForApplet"}, | ||
| 227 | }; | 228 | }; |
| 228 | // clang-format on | 229 | // clang-format on |
| 229 | 230 | ||
| @@ -256,6 +257,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger | |||
| 256 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, | 257 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, |
| 257 | {41, nullptr, "IsSystemBufferSharingEnabled"}, | 258 | {41, nullptr, "IsSystemBufferSharingEnabled"}, |
| 258 | {42, nullptr, "GetSystemSharedLayerHandle"}, | 259 | {42, nullptr, "GetSystemSharedLayerHandle"}, |
| 260 | {43, nullptr, "GetSystemSharedBufferHandle"}, | ||
| 259 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, | 261 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, |
| 260 | {51, nullptr, "ApproveToDisplay"}, | 262 | {51, nullptr, "ApproveToDisplay"}, |
| 261 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, | 263 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, |
| @@ -269,9 +271,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger | |||
| 269 | {68, nullptr, "SetAutoSleepDisabled"}, | 271 | {68, nullptr, "SetAutoSleepDisabled"}, |
| 270 | {69, nullptr, "IsAutoSleepDisabled"}, | 272 | {69, nullptr, "IsAutoSleepDisabled"}, |
| 271 | {70, nullptr, "ReportMultimediaError"}, | 273 | {70, nullptr, "ReportMultimediaError"}, |
| 274 | {71, nullptr, "GetCurrentIlluminanceEx"}, | ||
| 272 | {80, nullptr, "SetWirelessPriorityMode"}, | 275 | {80, nullptr, "SetWirelessPriorityMode"}, |
| 273 | {90, nullptr, "GetAccumulatedSuspendedTickValue"}, | 276 | {90, nullptr, "GetAccumulatedSuspendedTickValue"}, |
| 274 | {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, | 277 | {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, |
| 278 | {100, nullptr, "SetAlbumImageTakenNotificationEnabled"}, | ||
| 275 | {1000, nullptr, "GetDebugStorageChannel"}, | 279 | {1000, nullptr, "GetDebugStorageChannel"}, |
| 276 | }; | 280 | }; |
| 277 | // clang-format on | 281 | // clang-format on |
| @@ -363,8 +367,6 @@ void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestCo | |||
| 363 | void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { | 367 | void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { |
| 364 | // Takes 3 input u8s with each field located immediately after the previous | 368 | // Takes 3 input u8s with each field located immediately after the previous |
| 365 | // u8, these are bool flags. No output. | 369 | // u8, these are bool flags. No output. |
| 366 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 367 | |||
| 368 | IPC::RequestParser rp{ctx}; | 370 | IPC::RequestParser rp{ctx}; |
| 369 | 371 | ||
| 370 | struct FocusHandlingModeParams { | 372 | struct FocusHandlingModeParams { |
| @@ -372,7 +374,10 @@ void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { | |||
| 372 | u8 unknown1; | 374 | u8 unknown1; |
| 373 | u8 unknown2; | 375 | u8 unknown2; |
| 374 | }; | 376 | }; |
| 375 | auto flags = rp.PopRaw<FocusHandlingModeParams>(); | 377 | const auto flags = rp.PopRaw<FocusHandlingModeParams>(); |
| 378 | |||
| 379 | LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}", | ||
| 380 | flags.unknown0, flags.unknown1, flags.unknown2); | ||
| 376 | 381 | ||
| 377 | IPC::ResponseBuilder rb{ctx, 2}; | 382 | IPC::ResponseBuilder rb{ctx, 2}; |
| 378 | rb.Push(RESULT_SUCCESS); | 383 | rb.Push(RESULT_SUCCESS); |
| @@ -515,11 +520,20 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q | |||
| 515 | {50, nullptr, "IsVrModeEnabled"}, | 520 | {50, nullptr, "IsVrModeEnabled"}, |
| 516 | {51, nullptr, "SetVrModeEnabled"}, | 521 | {51, nullptr, "SetVrModeEnabled"}, |
| 517 | {52, nullptr, "SwitchLcdBacklight"}, | 522 | {52, nullptr, "SwitchLcdBacklight"}, |
| 523 | {53, nullptr, "BeginVrModeEx"}, | ||
| 524 | {54, nullptr, "EndVrModeEx"}, | ||
| 518 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, | 525 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, |
| 519 | {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, | 526 | {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, |
| 520 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, | 527 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, |
| 521 | {62, nullptr, "GetHdcpAuthenticationState"}, | 528 | {62, nullptr, "GetHdcpAuthenticationState"}, |
| 522 | {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, | 529 | {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, |
| 530 | {64, nullptr, "SetTvPowerStateMatchingMode"}, | ||
| 531 | {65, nullptr, "GetApplicationIdByContentActionName"}, | ||
| 532 | {66, nullptr, "SetCpuBoostMode"}, | ||
| 533 | {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, | ||
| 534 | {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, | ||
| 535 | {91, nullptr, "GetCurrentPerformanceConfiguration"}, | ||
| 536 | {200, nullptr, "GetOperationModeSystemInfo"}, | ||
| 523 | }; | 537 | }; |
| 524 | // clang-format on | 538 | // clang-format on |
| 525 | 539 | ||
| @@ -931,19 +945,19 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex | |||
| 931 | rp.SetCurrentOffset(3); | 945 | rp.SetCurrentOffset(3); |
| 932 | const auto handle{rp.Pop<Kernel::Handle>()}; | 946 | const auto handle{rp.Pop<Kernel::Handle>()}; |
| 933 | 947 | ||
| 934 | const auto shared_mem = | 948 | const auto transfer_mem = |
| 935 | Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>( | 949 | Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>( |
| 936 | handle); | 950 | handle); |
| 937 | 951 | ||
| 938 | if (shared_mem == nullptr) { | 952 | if (transfer_mem == nullptr) { |
| 939 | LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); | 953 | LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); |
| 940 | IPC::ResponseBuilder rb{ctx, 2}; | 954 | IPC::ResponseBuilder rb{ctx, 2}; |
| 941 | rb.Push(ResultCode(-1)); | 955 | rb.Push(ResultCode(-1)); |
| 942 | return; | 956 | return; |
| 943 | } | 957 | } |
| 944 | 958 | ||
| 945 | const u8* mem_begin = shared_mem->GetPointer(); | 959 | const u8* const mem_begin = transfer_mem->GetPointer(); |
| 946 | const u8* mem_end = mem_begin + shared_mem->GetSize(); | 960 | const u8* const mem_end = mem_begin + transfer_mem->GetSize(); |
| 947 | std::vector<u8> memory{mem_begin, mem_end}; | 961 | std::vector<u8> memory{mem_begin, mem_end}; |
| 948 | 962 | ||
| 949 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 963 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| @@ -959,6 +973,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF | |||
| 959 | {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, | 973 | {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, |
| 960 | {12, nullptr, "CreateApplicationAndRequestToStart"}, | 974 | {12, nullptr, "CreateApplicationAndRequestToStart"}, |
| 961 | {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, | 975 | {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, |
| 976 | {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"}, | ||
| 977 | {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"}, | ||
| 962 | {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, | 978 | {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, |
| 963 | {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, | 979 | {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, |
| 964 | {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, | 980 | {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, |
| @@ -1232,6 +1248,7 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat | |||
| 1232 | {2, nullptr, "StartSleepSequence"}, | 1248 | {2, nullptr, "StartSleepSequence"}, |
| 1233 | {3, nullptr, "StartShutdownSequence"}, | 1249 | {3, nullptr, "StartShutdownSequence"}, |
| 1234 | {4, nullptr, "StartRebootSequence"}, | 1250 | {4, nullptr, "StartRebootSequence"}, |
| 1251 | {9, nullptr, "IsAutoPowerDownRequested"}, | ||
| 1235 | {10, nullptr, "LoadAndApplyIdlePolicySettings"}, | 1252 | {10, nullptr, "LoadAndApplyIdlePolicySettings"}, |
| 1236 | {11, nullptr, "NotifyCecSettingsChanged"}, | 1253 | {11, nullptr, "NotifyCecSettingsChanged"}, |
| 1237 | {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, | 1254 | {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, |
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index b888f861d..488add8e7 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp | |||
| @@ -16,6 +16,7 @@ public: | |||
| 16 | std::shared_ptr<AppletMessageQueue> msg_queue) | 16 | std::shared_ptr<AppletMessageQueue> msg_queue) |
| 17 | : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), | 17 | : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), |
| 18 | msg_queue(std::move(msg_queue)) { | 18 | msg_queue(std::move(msg_queue)) { |
| 19 | // clang-format off | ||
| 19 | static const FunctionInfo functions[] = { | 20 | static const FunctionInfo functions[] = { |
| 20 | {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, | 21 | {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, |
| 21 | {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, | 22 | {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, |
| @@ -25,8 +26,11 @@ public: | |||
| 25 | {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, | 26 | {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, |
| 26 | {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, | 27 | {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, |
| 27 | {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"}, | 28 | {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"}, |
| 29 | {21, nullptr, "GetAppletCommonFunctions"}, | ||
| 28 | {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, | 30 | {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, |
| 29 | }; | 31 | }; |
| 32 | // clang-format on | ||
| 33 | |||
| 30 | RegisterHandlers(functions); | 34 | RegisterHandlers(functions); |
| 31 | } | 35 | } |
| 32 | 36 | ||
| @@ -113,6 +117,7 @@ public: | |||
| 113 | std::shared_ptr<AppletMessageQueue> msg_queue) | 117 | std::shared_ptr<AppletMessageQueue> msg_queue) |
| 114 | : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), | 118 | : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), |
| 115 | msg_queue(std::move(msg_queue)) { | 119 | msg_queue(std::move(msg_queue)) { |
| 120 | // clang-format off | ||
| 116 | static const FunctionInfo functions[] = { | 121 | static const FunctionInfo functions[] = { |
| 117 | {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, | 122 | {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, |
| 118 | {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, | 123 | {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, |
| @@ -124,8 +129,11 @@ public: | |||
| 124 | {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, | 129 | {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, |
| 125 | {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, | 130 | {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, |
| 126 | {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, | 131 | {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, |
| 132 | {23, nullptr, "GetAppletCommonFunctions"}, | ||
| 127 | {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, | 133 | {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, |
| 128 | }; | 134 | }; |
| 135 | // clang-format on | ||
| 136 | |||
| 129 | RegisterHandlers(functions); | 137 | RegisterHandlers(functions); |
| 130 | } | 138 | } |
| 131 | 139 | ||
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 9b0aa7f5f..7e17df98a 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp | |||
| @@ -86,7 +86,7 @@ static FileSys::VirtualFile GetManualRomFS() { | |||
| 86 | if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) | 86 | if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) |
| 87 | return out; | 87 | return out; |
| 88 | 88 | ||
| 89 | const auto& installed{FileSystem::GetUnionContents()}; | 89 | const auto& installed{Core::System::GetInstance().GetContentProvider()}; |
| 90 | const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(), | 90 | const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(), |
| 91 | FileSys::ContentRecordType::Manual); | 91 | FileSys::ContentRecordType::Manual); |
| 92 | 92 | ||
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index b506bc3dd..51d8c26b4 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -33,11 +33,11 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { | |||
| 33 | 33 | ||
| 34 | static std::vector<u64> AccumulateAOCTitleIDs() { | 34 | static std::vector<u64> AccumulateAOCTitleIDs() { |
| 35 | std::vector<u64> add_on_content; | 35 | std::vector<u64> add_on_content; |
| 36 | const auto rcu = FileSystem::GetUnionContents(); | 36 | const auto& rcu = Core::System::GetInstance().GetContentProvider(); |
| 37 | const auto list = | 37 | const auto list = |
| 38 | rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | 38 | rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); |
| 39 | std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), | 39 | std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), |
| 40 | [](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; }); | 40 | [](const FileSys::ContentProviderEntry& rce) { return rce.title_id; }); |
| 41 | add_on_content.erase( | 41 | add_on_content.erase( |
| 42 | std::remove_if( | 42 | std::remove_if( |
| 43 | add_on_content.begin(), add_on_content.end(), | 43 | add_on_content.begin(), add_on_content.end(), |
| @@ -50,6 +50,7 @@ static std::vector<u64> AccumulateAOCTitleIDs() { | |||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { | 52 | AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { |
| 53 | // clang-format off | ||
| 53 | static const FunctionInfo functions[] = { | 54 | static const FunctionInfo functions[] = { |
| 54 | {0, nullptr, "CountAddOnContentByApplicationId"}, | 55 | {0, nullptr, "CountAddOnContentByApplicationId"}, |
| 55 | {1, nullptr, "ListAddOnContentByApplicationId"}, | 56 | {1, nullptr, "ListAddOnContentByApplicationId"}, |
| @@ -60,7 +61,10 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs | |||
| 60 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, | 61 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, |
| 61 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, | 62 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, |
| 62 | {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, | 63 | {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, |
| 64 | {100, nullptr, "CreateEcPurchasedEventManager"}, | ||
| 63 | }; | 65 | }; |
| 66 | // clang-format on | ||
| 67 | |||
| 64 | RegisterHandlers(functions); | 68 | RegisterHandlers(functions); |
| 65 | 69 | ||
| 66 | auto& kernel = Core::System::GetInstance().Kernel(); | 70 | auto& kernel = Core::System::GetInstance().Kernel(); |
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index fcacbab72..d058c0245 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp | |||
| @@ -87,6 +87,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { | |||
| 87 | {3, nullptr, "GetLastThrottlingState"}, | 87 | {3, nullptr, "GetLastThrottlingState"}, |
| 88 | {4, nullptr, "ClearLastThrottlingState"}, | 88 | {4, nullptr, "ClearLastThrottlingState"}, |
| 89 | {5, nullptr, "LoadAndApplySettings"}, | 89 | {5, nullptr, "LoadAndApplySettings"}, |
| 90 | {6, nullptr, "SetCpuBoostMode"}, | ||
| 91 | {7, nullptr, "GetCurrentPerformanceConfiguration"}, | ||
| 90 | }; | 92 | }; |
| 91 | // clang-format on | 93 | // clang-format on |
| 92 | 94 | ||
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 088410564..d7f1d348d 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/kernel/hle_ipc.h" | ||
| 8 | #include "core/hle/service/audio/audin_u.h" | 5 | #include "core/hle/service/audio/audin_u.h" |
| 9 | 6 | ||
| 10 | namespace Service::Audio { | 7 | namespace Service::Audio { |
| @@ -28,12 +25,12 @@ public: | |||
| 28 | {11, nullptr, "GetAudioInBufferCount"}, | 25 | {11, nullptr, "GetAudioInBufferCount"}, |
| 29 | {12, nullptr, "SetAudioInDeviceGain"}, | 26 | {12, nullptr, "SetAudioInDeviceGain"}, |
| 30 | {13, nullptr, "GetAudioInDeviceGain"}, | 27 | {13, nullptr, "GetAudioInDeviceGain"}, |
| 28 | {14, nullptr, "FlushAudioInBuffers"}, | ||
| 31 | }; | 29 | }; |
| 32 | // clang-format on | 30 | // clang-format on |
| 33 | 31 | ||
| 34 | RegisterHandlers(functions); | 32 | RegisterHandlers(functions); |
| 35 | } | 33 | } |
| 36 | ~IAudioIn() = default; | ||
| 37 | }; | 34 | }; |
| 38 | 35 | ||
| 39 | AudInU::AudInU() : ServiceFramework("audin:u") { | 36 | AudInU::AudInU() : ServiceFramework("audin:u") { |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 21f5e64c7..12875fb42 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -44,7 +44,7 @@ public: | |||
| 44 | std::string&& unique_name) | 44 | std::string&& unique_name) |
| 45 | : ServiceFramework("IAudioOut"), audio_core(audio_core), | 45 | : ServiceFramework("IAudioOut"), audio_core(audio_core), |
| 46 | device_name(std::move(device_name)), audio_params(audio_params) { | 46 | device_name(std::move(device_name)), audio_params(audio_params) { |
| 47 | 47 | // clang-format off | |
| 48 | static const FunctionInfo functions[] = { | 48 | static const FunctionInfo functions[] = { |
| 49 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, | 49 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, |
| 50 | {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, | 50 | {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, |
| @@ -58,7 +58,10 @@ public: | |||
| 58 | {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, | 58 | {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, |
| 59 | {10, nullptr, "GetAudioOutPlayedSampleCount"}, | 59 | {10, nullptr, "GetAudioOutPlayedSampleCount"}, |
| 60 | {11, nullptr, "FlushAudioOutBuffers"}, | 60 | {11, nullptr, "FlushAudioOutBuffers"}, |
| 61 | {12, nullptr, "SetAudioOutVolume"}, | ||
| 62 | {13, nullptr, "GetAudioOutVolume"}, | ||
| 61 | }; | 63 | }; |
| 64 | // clang-format on | ||
| 62 | RegisterHandlers(functions); | 65 | RegisterHandlers(functions); |
| 63 | 66 | ||
| 64 | // This is the event handle used to check if the audio buffer was released | 67 | // This is the event handle used to check if the audio buffer was released |
| @@ -150,7 +153,6 @@ private: | |||
| 150 | void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { | 153 | void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { |
| 151 | LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); | 154 | LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); |
| 152 | 155 | ||
| 153 | IPC::RequestParser rp{ctx}; | ||
| 154 | const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; | 156 | const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; |
| 155 | const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; | 157 | const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; |
| 156 | 158 | ||
| @@ -194,12 +196,9 @@ private: | |||
| 194 | void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { | 196 | void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { |
| 195 | LOG_DEBUG(Service_Audio, "called"); | 197 | LOG_DEBUG(Service_Audio, "called"); |
| 196 | 198 | ||
| 197 | IPC::RequestParser rp{ctx}; | ||
| 198 | |||
| 199 | ctx.WriteBuffer(DefaultDevice); | 199 | ctx.WriteBuffer(DefaultDevice); |
| 200 | 200 | ||
| 201 | IPC::ResponseBuilder rb{ctx, 3}; | 201 | IPC::ResponseBuilder rb{ctx, 3}; |
| 202 | |||
| 203 | rb.Push(RESULT_SUCCESS); | 202 | rb.Push(RESULT_SUCCESS); |
| 204 | rb.Push<u32>(1); // Amount of audio devices | 203 | rb.Push<u32>(1); // Amount of audio devices |
| 205 | } | 204 | } |
diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp index 6956a2e64..1a5aed9ed 100644 --- a/src/core/hle/service/audio/audrec_u.cpp +++ b/src/core/hle/service/audio/audrec_u.cpp | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/kernel/hle_ipc.h" | ||
| 8 | #include "core/hle/service/audio/audrec_u.h" | 5 | #include "core/hle/service/audio/audrec_u.h" |
| 9 | 6 | ||
| 10 | namespace Service::Audio { | 7 | namespace Service::Audio { |
| @@ -30,7 +27,6 @@ public: | |||
| 30 | 27 | ||
| 31 | RegisterHandlers(functions); | 28 | RegisterHandlers(functions); |
| 32 | } | 29 | } |
| 33 | ~IFinalOutputRecorder() = default; | ||
| 34 | }; | 30 | }; |
| 35 | 31 | ||
| 36 | AudRecU::AudRecU() : ServiceFramework("audrec:u") { | 32 | AudRecU::AudRecU() : ServiceFramework("audrec:u") { |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index c9de10a24..1dde6edb7 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 11 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "common/string_util.h" | ||
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/hle/ipc_helpers.h" | 15 | #include "core/hle/ipc_helpers.h" |
| 15 | #include "core/hle/kernel/hle_ipc.h" | 16 | #include "core/hle/kernel/hle_ipc.h" |
| @@ -184,7 +185,6 @@ public: | |||
| 184 | private: | 185 | private: |
| 185 | void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { | 186 | void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { |
| 186 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 187 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 187 | IPC::RequestParser rp{ctx}; | ||
| 188 | 188 | ||
| 189 | constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; | 189 | constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; |
| 190 | ctx.WriteBuffer(audio_interface); | 190 | ctx.WriteBuffer(audio_interface); |
| @@ -195,13 +195,13 @@ private: | |||
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { | 197 | void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { |
| 198 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||
| 199 | |||
| 200 | IPC::RequestParser rp{ctx}; | 198 | IPC::RequestParser rp{ctx}; |
| 201 | f32 volume = static_cast<f32>(rp.Pop<u32>()); | 199 | const f32 volume = rp.Pop<f32>(); |
| 202 | 200 | ||
| 203 | auto file_buffer = ctx.ReadBuffer(); | 201 | const auto device_name_buffer = ctx.ReadBuffer(); |
| 204 | auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); | 202 | const std::string name = Common::StringFromBuffer(device_name_buffer); |
| 203 | |||
| 204 | LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume); | ||
| 205 | 205 | ||
| 206 | IPC::ResponseBuilder rb{ctx, 2}; | 206 | IPC::ResponseBuilder rb{ctx, 2}; |
| 207 | rb.Push(RESULT_SUCCESS); | 207 | rb.Push(RESULT_SUCCESS); |
| @@ -209,7 +209,6 @@ private: | |||
| 209 | 209 | ||
| 210 | void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { | 210 | void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { |
| 211 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 211 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 212 | IPC::RequestParser rp{ctx}; | ||
| 213 | 212 | ||
| 214 | constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; | 213 | constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; |
| 215 | ctx.WriteBuffer(audio_interface); | 214 | ctx.WriteBuffer(audio_interface); |
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 59ef603e1..974ff8e1a 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp | |||
| @@ -154,7 +154,8 @@ public: | |||
| 154 | {96, nullptr, "GetLeHidEventInfo"}, | 154 | {96, nullptr, "GetLeHidEventInfo"}, |
| 155 | {97, nullptr, "RegisterBleHidEvent"}, | 155 | {97, nullptr, "RegisterBleHidEvent"}, |
| 156 | {98, nullptr, "SetLeScanParameter"}, | 156 | {98, nullptr, "SetLeScanParameter"}, |
| 157 | {256, nullptr, "GetIsManufacturingMode"} | 157 | {256, nullptr, "GetIsManufacturingMode"}, |
| 158 | {257, nullptr, "EmulateBluetoothCrash"}, | ||
| 158 | }; | 159 | }; |
| 159 | // clang-format on | 160 | // clang-format on |
| 160 | 161 | ||
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index ae7b0720b..907f464ab 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp | |||
| @@ -15,32 +15,41 @@ public: | |||
| 15 | explicit CAPS_A() : ServiceFramework{"caps:a"} { | 15 | explicit CAPS_A() : ServiceFramework{"caps:a"} { |
| 16 | // clang-format off | 16 | // clang-format off |
| 17 | static const FunctionInfo functions[] = { | 17 | static const FunctionInfo functions[] = { |
| 18 | {0, nullptr, "Unknown1"}, | 18 | {0, nullptr, "GetAlbumFileCount"}, |
| 19 | {1, nullptr, "Unknown2"}, | 19 | {1, nullptr, "GetAlbumFileList"}, |
| 20 | {2, nullptr, "Unknown3"}, | 20 | {2, nullptr, "LoadAlbumFile"}, |
| 21 | {3, nullptr, "Unknown4"}, | 21 | {3, nullptr, "DeleteAlbumFile"}, |
| 22 | {4, nullptr, "Unknown5"}, | 22 | {4, nullptr, "StorageCopyAlbumFile"}, |
| 23 | {5, nullptr, "Unknown6"}, | 23 | {5, nullptr, "IsAlbumMounted"}, |
| 24 | {6, nullptr, "Unknown7"}, | 24 | {6, nullptr, "GetAlbumUsage"}, |
| 25 | {7, nullptr, "Unknown8"}, | 25 | {7, nullptr, "GetAlbumFileSize"}, |
| 26 | {8, nullptr, "Unknown9"}, | 26 | {8, nullptr, "LoadAlbumFileThumbnail"}, |
| 27 | {9, nullptr, "Unknown10"}, | 27 | {9, nullptr, "LoadAlbumScreenShotImage"}, |
| 28 | {10, nullptr, "Unknown11"}, | 28 | {10, nullptr, "LoadAlbumScreenShotThumbnailImage"}, |
| 29 | {11, nullptr, "Unknown12"}, | 29 | {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"}, |
| 30 | {12, nullptr, "Unknown13"}, | 30 | {12, nullptr, "Unknown12"}, |
| 31 | {13, nullptr, "Unknown14"}, | 31 | {13, nullptr, "Unknown13"}, |
| 32 | {14, nullptr, "Unknown15"}, | 32 | {14, nullptr, "Unknown14"}, |
| 33 | {301, nullptr, "Unknown16"}, | 33 | {15, nullptr, "Unknown15"}, |
| 34 | {401, nullptr, "Unknown17"}, | 34 | {16, nullptr, "Unknown16"}, |
| 35 | {501, nullptr, "Unknown18"}, | 35 | {17, nullptr, "Unknown17"}, |
| 36 | {1001, nullptr, "Unknown19"}, | 36 | {18, nullptr, "Unknown18"}, |
| 37 | {1002, nullptr, "Unknown20"}, | 37 | {202, nullptr, "SaveEditedScreenShot"}, |
| 38 | {8001, nullptr, "Unknown21"}, | 38 | {301, nullptr, "GetLastThumbnail"}, |
| 39 | {8002, nullptr, "Unknown22"}, | 39 | {401, nullptr, "GetAutoSavingStorage"}, |
| 40 | {8011, nullptr, "Unknown23"}, | 40 | {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, |
| 41 | {8012, nullptr, "Unknown24"}, | 41 | {1001, nullptr, "Unknown1001"}, |
| 42 | {8021, nullptr, "Unknown25"}, | 42 | {1002, nullptr, "Unknown1002"}, |
| 43 | {10011, nullptr, "Unknown26"}, | 43 | {1003, nullptr, "Unknown1003"}, |
| 44 | {8001, nullptr, "ForceAlbumUnmounted"}, | ||
| 45 | {8002, nullptr, "ResetAlbumMountStatus"}, | ||
| 46 | {8011, nullptr, "RefreshAlbumCache"}, | ||
| 47 | {8012, nullptr, "GetAlbumCache"}, | ||
| 48 | {8013, nullptr, "Unknown8013"}, | ||
| 49 | {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"}, | ||
| 50 | {10011, nullptr, "SetInternalErrorConversionEnabled"}, | ||
| 51 | {50000, nullptr, "Unknown50000"}, | ||
| 52 | {60002, nullptr, "Unknown60002"}, | ||
| 44 | }; | 53 | }; |
| 45 | // clang-format on | 54 | // clang-format on |
| 46 | 55 | ||
| @@ -53,16 +62,17 @@ public: | |||
| 53 | explicit CAPS_C() : ServiceFramework{"caps:c"} { | 62 | explicit CAPS_C() : ServiceFramework{"caps:c"} { |
| 54 | // clang-format off | 63 | // clang-format off |
| 55 | static const FunctionInfo functions[] = { | 64 | static const FunctionInfo functions[] = { |
| 56 | {2001, nullptr, "Unknown1"}, | 65 | {33, nullptr, "Unknown33"}, |
| 57 | {2002, nullptr, "Unknown2"}, | 66 | {2001, nullptr, "Unknown2001"}, |
| 58 | {2011, nullptr, "Unknown3"}, | 67 | {2002, nullptr, "Unknown2002"}, |
| 59 | {2012, nullptr, "Unknown4"}, | 68 | {2011, nullptr, "Unknown2011"}, |
| 60 | {2013, nullptr, "Unknown5"}, | 69 | {2012, nullptr, "Unknown2012"}, |
| 61 | {2014, nullptr, "Unknown6"}, | 70 | {2013, nullptr, "Unknown2013"}, |
| 62 | {2101, nullptr, "Unknown7"}, | 71 | {2014, nullptr, "Unknown2014"}, |
| 63 | {2102, nullptr, "Unknown8"}, | 72 | {2101, nullptr, "Unknown2101"}, |
| 64 | {2201, nullptr, "Unknown9"}, | 73 | {2102, nullptr, "Unknown2102"}, |
| 65 | {2301, nullptr, "Unknown10"}, | 74 | {2201, nullptr, "Unknown2201"}, |
| 75 | {2301, nullptr, "Unknown2301"}, | ||
| 66 | }; | 76 | }; |
| 67 | // clang-format on | 77 | // clang-format on |
| 68 | 78 | ||
| @@ -127,11 +137,18 @@ public: | |||
| 127 | explicit CAPS_U() : ServiceFramework{"caps:u"} { | 137 | explicit CAPS_U() : ServiceFramework{"caps:u"} { |
| 128 | // clang-format off | 138 | // clang-format off |
| 129 | static const FunctionInfo functions[] = { | 139 | static const FunctionInfo functions[] = { |
| 140 | {32, nullptr, "SetShimLibraryVersion"}, | ||
| 130 | {102, nullptr, "GetAlbumFileListByAruid"}, | 141 | {102, nullptr, "GetAlbumFileListByAruid"}, |
| 131 | {103, nullptr, "DeleteAlbumFileByAruid"}, | 142 | {103, nullptr, "DeleteAlbumFileByAruid"}, |
| 132 | {104, nullptr, "GetAlbumFileSizeByAruid"}, | 143 | {104, nullptr, "GetAlbumFileSizeByAruid"}, |
| 144 | {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, | ||
| 133 | {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, | 145 | {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, |
| 134 | {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, | 146 | {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, |
| 147 | {130, nullptr, "PrecheckToCreateContentsByAruid"}, | ||
| 148 | {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, | ||
| 149 | {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, | ||
| 150 | {142, nullptr, "GetAlbumFileList3AaeAruid"}, | ||
| 151 | {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, | ||
| 135 | {60002, nullptr, "OpenAccessorSessionForApplication"}, | 152 | {60002, nullptr, "OpenAccessorSessionForApplication"}, |
| 136 | }; | 153 | }; |
| 137 | // clang-format on | 154 | // clang-format on |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index c6da2df43..1ebfeb4bf 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -197,13 +197,16 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa | |||
| 197 | 197 | ||
| 198 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, | 198 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, |
| 199 | FileSys::Mode mode) const { | 199 | FileSys::Mode mode) const { |
| 200 | std::string path(FileUtil::SanitizePath(path_)); | 200 | const std::string path(FileUtil::SanitizePath(path_)); |
| 201 | auto npath = path; | 201 | std::string_view npath = path; |
| 202 | while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\')) | 202 | while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { |
| 203 | npath = npath.substr(1); | 203 | npath.remove_prefix(1); |
| 204 | } | ||
| 205 | |||
| 204 | auto file = backing->GetFileRelative(npath); | 206 | auto file = backing->GetFileRelative(npath); |
| 205 | if (file == nullptr) | 207 | if (file == nullptr) { |
| 206 | return FileSys::ERROR_PATH_NOT_FOUND; | 208 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 209 | } | ||
| 207 | 210 | ||
| 208 | if (mode == FileSys::Mode::Append) { | 211 | if (mode == FileSys::Mode::Append) { |
| 209 | return MakeResult<FileSys::VirtualFile>( | 212 | return MakeResult<FileSys::VirtualFile>( |
| @@ -319,15 +322,15 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora | |||
| 319 | } | 322 | } |
| 320 | 323 | ||
| 321 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | 324 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, |
| 322 | FileSys::SaveDataDescriptor save_struct) { | 325 | const FileSys::SaveDataDescriptor& descriptor) { |
| 323 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", | 326 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", |
| 324 | static_cast<u8>(space), save_struct.DebugInfo()); | 327 | static_cast<u8>(space), descriptor.DebugInfo()); |
| 325 | 328 | ||
| 326 | if (save_data_factory == nullptr) { | 329 | if (save_data_factory == nullptr) { |
| 327 | return FileSys::ERROR_ENTITY_NOT_FOUND; | 330 | return FileSys::ERROR_ENTITY_NOT_FOUND; |
| 328 | } | 331 | } |
| 329 | 332 | ||
| 330 | return save_data_factory->Open(space, save_struct); | 333 | return save_data_factory->Open(space, descriptor); |
| 331 | } | 334 | } |
| 332 | 335 | ||
| 333 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { | 336 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { |
| @@ -388,11 +391,6 @@ void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | |||
| 388 | save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); | 391 | save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); |
| 389 | } | 392 | } |
| 390 | 393 | ||
| 391 | FileSys::RegisteredCacheUnion GetUnionContents() { | ||
| 392 | return FileSys::RegisteredCacheUnion{ | ||
| 393 | {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}}; | ||
| 394 | } | ||
| 395 | |||
| 396 | FileSys::RegisteredCache* GetSystemNANDContents() { | 394 | FileSys::RegisteredCache* GetSystemNANDContents() { |
| 397 | LOG_TRACE(Service_FS, "Opening System NAND Contents"); | 395 | LOG_TRACE(Service_FS, "Opening System NAND Contents"); |
| 398 | 396 | ||
| @@ -457,6 +455,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | |||
| 457 | if (bis_factory == nullptr) { | 455 | if (bis_factory == nullptr) { |
| 458 | bis_factory = | 456 | bis_factory = |
| 459 | std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); | 457 | std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); |
| 458 | Core::System::GetInstance().RegisterContentProvider( | ||
| 459 | FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents()); | ||
| 460 | Core::System::GetInstance().RegisterContentProvider( | ||
| 461 | FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents()); | ||
| 460 | } | 462 | } |
| 461 | 463 | ||
| 462 | if (save_data_factory == nullptr) { | 464 | if (save_data_factory == nullptr) { |
| @@ -465,6 +467,8 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | |||
| 465 | 467 | ||
| 466 | if (sdmc_factory == nullptr) { | 468 | if (sdmc_factory == nullptr) { |
| 467 | sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); | 469 | sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); |
| 470 | Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, | ||
| 471 | sdmc_factory->GetSDMCContents()); | ||
| 468 | } | 472 | } |
| 469 | } | 473 | } |
| 470 | 474 | ||
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6fd5e7b23..6481f237c 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -46,7 +46,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); | |||
| 46 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | 46 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, |
| 47 | FileSys::ContentRecordType type); | 47 | FileSys::ContentRecordType type); |
| 48 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | 48 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, |
| 49 | FileSys::SaveDataDescriptor save_struct); | 49 | const FileSys::SaveDataDescriptor& descriptor); |
| 50 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); | 50 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); |
| 51 | ResultVal<FileSys::VirtualDir> OpenSDMC(); | 51 | ResultVal<FileSys::VirtualDir> OpenSDMC(); |
| 52 | 52 | ||
| @@ -54,8 +54,6 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, | |||
| 54 | void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | 54 | void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, |
| 55 | FileSys::SaveDataSize new_value); | 55 | FileSys::SaveDataSize new_value); |
| 56 | 56 | ||
| 57 | FileSys::RegisteredCacheUnion GetUnionContents(); | ||
| 58 | |||
| 59 | FileSys::RegisteredCache* GetSystemNANDContents(); | 57 | FileSys::RegisteredCache* GetSystemNANDContents(); |
| 60 | FileSys::RegisteredCache* GetUserNANDContents(); | 58 | FileSys::RegisteredCache* GetUserNANDContents(); |
| 61 | FileSys::RegisteredCache* GetSDMCContents(); | 59 | FileSys::RegisteredCache* GetSDMCContents(); |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index f03fb629c..e7df8fd98 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -115,11 +115,12 @@ private: | |||
| 115 | 115 | ||
| 116 | void Read(Kernel::HLERequestContext& ctx) { | 116 | void Read(Kernel::HLERequestContext& ctx) { |
| 117 | IPC::RequestParser rp{ctx}; | 117 | IPC::RequestParser rp{ctx}; |
| 118 | const u64 unk = rp.Pop<u64>(); | 118 | const u64 option = rp.Pop<u64>(); |
| 119 | const s64 offset = rp.Pop<s64>(); | 119 | const s64 offset = rp.Pop<s64>(); |
| 120 | const s64 length = rp.Pop<s64>(); | 120 | const s64 length = rp.Pop<s64>(); |
| 121 | 121 | ||
| 122 | LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); | 122 | LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, |
| 123 | length); | ||
| 123 | 124 | ||
| 124 | // Error checking | 125 | // Error checking |
| 125 | if (length < 0) { | 126 | if (length < 0) { |
| @@ -148,11 +149,12 @@ private: | |||
| 148 | 149 | ||
| 149 | void Write(Kernel::HLERequestContext& ctx) { | 150 | void Write(Kernel::HLERequestContext& ctx) { |
| 150 | IPC::RequestParser rp{ctx}; | 151 | IPC::RequestParser rp{ctx}; |
| 151 | const u64 unk = rp.Pop<u64>(); | 152 | const u64 option = rp.Pop<u64>(); |
| 152 | const s64 offset = rp.Pop<s64>(); | 153 | const s64 offset = rp.Pop<s64>(); |
| 153 | const s64 length = rp.Pop<s64>(); | 154 | const s64 length = rp.Pop<s64>(); |
| 154 | 155 | ||
| 155 | LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); | 156 | LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, |
| 157 | length); | ||
| 156 | 158 | ||
| 157 | // Error checking | 159 | // Error checking |
| 158 | if (length < 0) { | 160 | if (length < 0) { |
| @@ -250,10 +252,7 @@ private: | |||
| 250 | u64 next_entry_index = 0; | 252 | u64 next_entry_index = 0; |
| 251 | 253 | ||
| 252 | void Read(Kernel::HLERequestContext& ctx) { | 254 | void Read(Kernel::HLERequestContext& ctx) { |
| 253 | IPC::RequestParser rp{ctx}; | 255 | LOG_DEBUG(Service_FS, "called."); |
| 254 | const u64 unk = rp.Pop<u64>(); | ||
| 255 | |||
| 256 | LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk); | ||
| 257 | 256 | ||
| 258 | // Calculate how many entries we can fit in the output buffer | 257 | // Calculate how many entries we can fit in the output buffer |
| 259 | const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); | 258 | const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); |
| @@ -315,61 +314,53 @@ public: | |||
| 315 | void CreateFile(Kernel::HLERequestContext& ctx) { | 314 | void CreateFile(Kernel::HLERequestContext& ctx) { |
| 316 | IPC::RequestParser rp{ctx}; | 315 | IPC::RequestParser rp{ctx}; |
| 317 | 316 | ||
| 318 | auto file_buffer = ctx.ReadBuffer(); | 317 | const auto file_buffer = ctx.ReadBuffer(); |
| 319 | std::string name = Common::StringFromBuffer(file_buffer); | 318 | const std::string name = Common::StringFromBuffer(file_buffer); |
| 320 | 319 | ||
| 321 | u64 mode = rp.Pop<u64>(); | 320 | const u64 mode = rp.Pop<u64>(); |
| 322 | u32 size = rp.Pop<u32>(); | 321 | const u32 size = rp.Pop<u32>(); |
| 323 | 322 | ||
| 324 | LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); | 323 | LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, mode, size); |
| 325 | 324 | ||
| 326 | IPC::ResponseBuilder rb{ctx, 2}; | 325 | IPC::ResponseBuilder rb{ctx, 2}; |
| 327 | rb.Push(backend.CreateFile(name, size)); | 326 | rb.Push(backend.CreateFile(name, size)); |
| 328 | } | 327 | } |
| 329 | 328 | ||
| 330 | void DeleteFile(Kernel::HLERequestContext& ctx) { | 329 | void DeleteFile(Kernel::HLERequestContext& ctx) { |
| 331 | IPC::RequestParser rp{ctx}; | 330 | const auto file_buffer = ctx.ReadBuffer(); |
| 332 | 331 | const std::string name = Common::StringFromBuffer(file_buffer); | |
| 333 | auto file_buffer = ctx.ReadBuffer(); | ||
| 334 | std::string name = Common::StringFromBuffer(file_buffer); | ||
| 335 | 332 | ||
| 336 | LOG_DEBUG(Service_FS, "called file {}", name); | 333 | LOG_DEBUG(Service_FS, "called. file={}", name); |
| 337 | 334 | ||
| 338 | IPC::ResponseBuilder rb{ctx, 2}; | 335 | IPC::ResponseBuilder rb{ctx, 2}; |
| 339 | rb.Push(backend.DeleteFile(name)); | 336 | rb.Push(backend.DeleteFile(name)); |
| 340 | } | 337 | } |
| 341 | 338 | ||
| 342 | void CreateDirectory(Kernel::HLERequestContext& ctx) { | 339 | void CreateDirectory(Kernel::HLERequestContext& ctx) { |
| 343 | IPC::RequestParser rp{ctx}; | 340 | const auto file_buffer = ctx.ReadBuffer(); |
| 344 | 341 | const std::string name = Common::StringFromBuffer(file_buffer); | |
| 345 | auto file_buffer = ctx.ReadBuffer(); | ||
| 346 | std::string name = Common::StringFromBuffer(file_buffer); | ||
| 347 | 342 | ||
| 348 | LOG_DEBUG(Service_FS, "called directory {}", name); | 343 | LOG_DEBUG(Service_FS, "called. directory={}", name); |
| 349 | 344 | ||
| 350 | IPC::ResponseBuilder rb{ctx, 2}; | 345 | IPC::ResponseBuilder rb{ctx, 2}; |
| 351 | rb.Push(backend.CreateDirectory(name)); | 346 | rb.Push(backend.CreateDirectory(name)); |
| 352 | } | 347 | } |
| 353 | 348 | ||
| 354 | void DeleteDirectory(Kernel::HLERequestContext& ctx) { | 349 | void DeleteDirectory(Kernel::HLERequestContext& ctx) { |
| 355 | const IPC::RequestParser rp{ctx}; | ||
| 356 | |||
| 357 | const auto file_buffer = ctx.ReadBuffer(); | 350 | const auto file_buffer = ctx.ReadBuffer(); |
| 358 | std::string name = Common::StringFromBuffer(file_buffer); | 351 | const std::string name = Common::StringFromBuffer(file_buffer); |
| 359 | 352 | ||
| 360 | LOG_DEBUG(Service_FS, "called directory {}", name); | 353 | LOG_DEBUG(Service_FS, "called. directory={}", name); |
| 361 | 354 | ||
| 362 | IPC::ResponseBuilder rb{ctx, 2}; | 355 | IPC::ResponseBuilder rb{ctx, 2}; |
| 363 | rb.Push(backend.DeleteDirectory(name)); | 356 | rb.Push(backend.DeleteDirectory(name)); |
| 364 | } | 357 | } |
| 365 | 358 | ||
| 366 | void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { | 359 | void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { |
| 367 | const IPC::RequestParser rp{ctx}; | ||
| 368 | |||
| 369 | const auto file_buffer = ctx.ReadBuffer(); | 360 | const auto file_buffer = ctx.ReadBuffer(); |
| 370 | std::string name = Common::StringFromBuffer(file_buffer); | 361 | const std::string name = Common::StringFromBuffer(file_buffer); |
| 371 | 362 | ||
| 372 | LOG_DEBUG(Service_FS, "called directory {}", name); | 363 | LOG_DEBUG(Service_FS, "called. directory={}", name); |
| 373 | 364 | ||
| 374 | IPC::ResponseBuilder rb{ctx, 2}; | 365 | IPC::ResponseBuilder rb{ctx, 2}; |
| 375 | rb.Push(backend.DeleteDirectoryRecursively(name)); | 366 | rb.Push(backend.DeleteDirectoryRecursively(name)); |
| @@ -386,18 +377,16 @@ public: | |||
| 386 | } | 377 | } |
| 387 | 378 | ||
| 388 | void RenameFile(Kernel::HLERequestContext& ctx) { | 379 | void RenameFile(Kernel::HLERequestContext& ctx) { |
| 389 | IPC::RequestParser rp{ctx}; | ||
| 390 | |||
| 391 | std::vector<u8> buffer; | 380 | std::vector<u8> buffer; |
| 392 | buffer.resize(ctx.BufferDescriptorX()[0].Size()); | 381 | buffer.resize(ctx.BufferDescriptorX()[0].Size()); |
| 393 | Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); | 382 | Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); |
| 394 | std::string src_name = Common::StringFromBuffer(buffer); | 383 | const std::string src_name = Common::StringFromBuffer(buffer); |
| 395 | 384 | ||
| 396 | buffer.resize(ctx.BufferDescriptorX()[1].Size()); | 385 | buffer.resize(ctx.BufferDescriptorX()[1].Size()); |
| 397 | Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); | 386 | Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); |
| 398 | std::string dst_name = Common::StringFromBuffer(buffer); | 387 | const std::string dst_name = Common::StringFromBuffer(buffer); |
| 399 | 388 | ||
| 400 | LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); | 389 | LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); |
| 401 | 390 | ||
| 402 | IPC::ResponseBuilder rb{ctx, 2}; | 391 | IPC::ResponseBuilder rb{ctx, 2}; |
| 403 | rb.Push(backend.RenameFile(src_name, dst_name)); | 392 | rb.Push(backend.RenameFile(src_name, dst_name)); |
| @@ -406,12 +395,12 @@ public: | |||
| 406 | void OpenFile(Kernel::HLERequestContext& ctx) { | 395 | void OpenFile(Kernel::HLERequestContext& ctx) { |
| 407 | IPC::RequestParser rp{ctx}; | 396 | IPC::RequestParser rp{ctx}; |
| 408 | 397 | ||
| 409 | auto file_buffer = ctx.ReadBuffer(); | 398 | const auto file_buffer = ctx.ReadBuffer(); |
| 410 | std::string name = Common::StringFromBuffer(file_buffer); | 399 | const std::string name = Common::StringFromBuffer(file_buffer); |
| 411 | 400 | ||
| 412 | auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); | 401 | const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); |
| 413 | 402 | ||
| 414 | LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); | 403 | LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, static_cast<u32>(mode)); |
| 415 | 404 | ||
| 416 | auto result = backend.OpenFile(name, mode); | 405 | auto result = backend.OpenFile(name, mode); |
| 417 | if (result.Failed()) { | 406 | if (result.Failed()) { |
| @@ -430,13 +419,13 @@ public: | |||
| 430 | void OpenDirectory(Kernel::HLERequestContext& ctx) { | 419 | void OpenDirectory(Kernel::HLERequestContext& ctx) { |
| 431 | IPC::RequestParser rp{ctx}; | 420 | IPC::RequestParser rp{ctx}; |
| 432 | 421 | ||
| 433 | auto file_buffer = ctx.ReadBuffer(); | 422 | const auto file_buffer = ctx.ReadBuffer(); |
| 434 | std::string name = Common::StringFromBuffer(file_buffer); | 423 | const std::string name = Common::StringFromBuffer(file_buffer); |
| 435 | 424 | ||
| 436 | // TODO(Subv): Implement this filter. | 425 | // TODO(Subv): Implement this filter. |
| 437 | u32 filter_flags = rp.Pop<u32>(); | 426 | const u32 filter_flags = rp.Pop<u32>(); |
| 438 | 427 | ||
| 439 | LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); | 428 | LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags); |
| 440 | 429 | ||
| 441 | auto result = backend.OpenDirectory(name); | 430 | auto result = backend.OpenDirectory(name); |
| 442 | if (result.Failed()) { | 431 | if (result.Failed()) { |
| @@ -453,12 +442,10 @@ public: | |||
| 453 | } | 442 | } |
| 454 | 443 | ||
| 455 | void GetEntryType(Kernel::HLERequestContext& ctx) { | 444 | void GetEntryType(Kernel::HLERequestContext& ctx) { |
| 456 | IPC::RequestParser rp{ctx}; | 445 | const auto file_buffer = ctx.ReadBuffer(); |
| 457 | 446 | const std::string name = Common::StringFromBuffer(file_buffer); | |
| 458 | auto file_buffer = ctx.ReadBuffer(); | ||
| 459 | std::string name = Common::StringFromBuffer(file_buffer); | ||
| 460 | 447 | ||
| 461 | LOG_DEBUG(Service_FS, "called file {}", name); | 448 | LOG_DEBUG(Service_FS, "called. file={}", name); |
| 462 | 449 | ||
| 463 | auto result = backend.GetEntryType(name); | 450 | auto result = backend.GetEntryType(name); |
| 464 | if (result.Failed()) { | 451 | if (result.Failed()) { |
| @@ -616,7 +603,9 @@ private: | |||
| 616 | u64_le save_id; | 603 | u64_le save_id; |
| 617 | u64_le title_id; | 604 | u64_le title_id; |
| 618 | u64_le save_image_size; | 605 | u64_le save_image_size; |
| 619 | INSERT_PADDING_BYTES(0x28); | 606 | u16_le index; |
| 607 | FileSys::SaveDataRank rank; | ||
| 608 | INSERT_PADDING_BYTES(0x25); | ||
| 620 | }; | 609 | }; |
| 621 | static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); | 610 | static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); |
| 622 | 611 | ||
| @@ -675,10 +664,13 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 675 | {100, nullptr, "OpenImageDirectoryFileSystem"}, | 664 | {100, nullptr, "OpenImageDirectoryFileSystem"}, |
| 676 | {110, nullptr, "OpenContentStorageFileSystem"}, | 665 | {110, nullptr, "OpenContentStorageFileSystem"}, |
| 677 | {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, | 666 | {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, |
| 667 | {130, nullptr, "OpenCustomStorageFileSystem"}, | ||
| 678 | {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, | 668 | {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, |
| 679 | {201, nullptr, "OpenDataStorageByProgramId"}, | 669 | {201, nullptr, "OpenDataStorageByProgramId"}, |
| 680 | {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"}, | 670 | {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"}, |
| 681 | {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"}, | 671 | {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"}, |
| 672 | {204, nullptr, "OpenDataFileSystemByProgramIndex"}, | ||
| 673 | {205, nullptr, "OpenDataStorageByProgramIndex"}, | ||
| 682 | {400, nullptr, "OpenDeviceOperator"}, | 674 | {400, nullptr, "OpenDeviceOperator"}, |
| 683 | {500, nullptr, "OpenSdCardDetectionEventNotifier"}, | 675 | {500, nullptr, "OpenSdCardDetectionEventNotifier"}, |
| 684 | {501, nullptr, "OpenGameCardDetectionEventNotifier"}, | 676 | {501, nullptr, "OpenGameCardDetectionEventNotifier"}, |
| @@ -702,6 +694,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 702 | {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, | 694 | {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, |
| 703 | {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, | 695 | {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, |
| 704 | {616, nullptr, "GetSaveDataCommitId"}, | 696 | {616, nullptr, "GetSaveDataCommitId"}, |
| 697 | {617, nullptr, "UnregisterExternalKey"}, | ||
| 705 | {620, nullptr, "SetSdCardEncryptionSeed"}, | 698 | {620, nullptr, "SetSdCardEncryptionSeed"}, |
| 706 | {630, nullptr, "SetSdCardAccessibility"}, | 699 | {630, nullptr, "SetSdCardAccessibility"}, |
| 707 | {631, nullptr, "IsSdCardAccessible"}, | 700 | {631, nullptr, "IsSdCardAccessible"}, |
| @@ -712,6 +705,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 712 | {710, nullptr, "ResolveAccessFailure"}, | 705 | {710, nullptr, "ResolveAccessFailure"}, |
| 713 | {720, nullptr, "AbandonAccessFailure"}, | 706 | {720, nullptr, "AbandonAccessFailure"}, |
| 714 | {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, | 707 | {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, |
| 708 | {810, nullptr, "RegisterProgramIndexMapInfo"}, | ||
| 715 | {1000, nullptr, "SetBisRootForHost"}, | 709 | {1000, nullptr, "SetBisRootForHost"}, |
| 716 | {1001, nullptr, "SetSaveDataSize"}, | 710 | {1001, nullptr, "SetSaveDataSize"}, |
| 717 | {1002, nullptr, "SetSaveDataRootPath"}, | 711 | {1002, nullptr, "SetSaveDataRootPath"}, |
| @@ -722,6 +716,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 722 | {1007, nullptr, "RegisterUpdatePartition"}, | 716 | {1007, nullptr, "RegisterUpdatePartition"}, |
| 723 | {1008, nullptr, "OpenRegisteredUpdatePartition"}, | 717 | {1008, nullptr, "OpenRegisteredUpdatePartition"}, |
| 724 | {1009, nullptr, "GetAndClearMemoryReportInfo"}, | 718 | {1009, nullptr, "GetAndClearMemoryReportInfo"}, |
| 719 | {1010, nullptr, "SetDataStorageRedirectTarget"}, | ||
| 720 | {1011, nullptr, "OutputAccessLogToSdCard2"}, | ||
| 725 | {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, | 721 | {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, |
| 726 | {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, | 722 | {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, |
| 727 | {1200, nullptr, "OpenMultiCommitManager"}, | 723 | {1200, nullptr, "OpenMultiCommitManager"}, |
| @@ -779,16 +775,17 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 779 | } | 775 | } |
| 780 | 776 | ||
| 781 | void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | 777 | void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { |
| 782 | IPC::RequestParser rp{ctx}; | 778 | LOG_INFO(Service_FS, "called."); |
| 783 | 779 | ||
| 784 | auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>(); | 780 | struct Parameters { |
| 785 | auto unk = rp.Pop<u32>(); | 781 | FileSys::SaveDataSpaceId save_data_space_id; |
| 786 | LOG_INFO(Service_FS, "called with unknown={:08X}", unk); | 782 | FileSys::SaveDataDescriptor descriptor; |
| 787 | 783 | }; | |
| 788 | auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); | ||
| 789 | 784 | ||
| 790 | auto dir = OpenSaveData(space_id, save_struct); | 785 | IPC::RequestParser rp{ctx}; |
| 786 | const auto parameters = rp.PopRaw<Parameters>(); | ||
| 791 | 787 | ||
| 788 | auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor); | ||
| 792 | if (dir.Failed()) { | 789 | if (dir.Failed()) { |
| 793 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | 790 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; |
| 794 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); | 791 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index d9225d624..5100e376c 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -12,6 +12,7 @@ namespace Service::Friend { | |||
| 12 | class IFriendService final : public ServiceFramework<IFriendService> { | 12 | class IFriendService final : public ServiceFramework<IFriendService> { |
| 13 | public: | 13 | public: |
| 14 | IFriendService() : ServiceFramework("IFriendService") { | 14 | IFriendService() : ServiceFramework("IFriendService") { |
| 15 | // clang-format off | ||
| 15 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 16 | {0, nullptr, "GetCompletionEvent"}, | 17 | {0, nullptr, "GetCompletionEvent"}, |
| 17 | {1, nullptr, "Cancel"}, | 18 | {1, nullptr, "Cancel"}, |
| @@ -24,8 +25,7 @@ public: | |||
| 24 | {10400, nullptr, "GetBlockedUserListIds"}, | 25 | {10400, nullptr, "GetBlockedUserListIds"}, |
| 25 | {10500, nullptr, "GetProfileList"}, | 26 | {10500, nullptr, "GetProfileList"}, |
| 26 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, | 27 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, |
| 27 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, | 28 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, |
| 28 | "DeclareCloseOnlinePlaySession"}, | ||
| 29 | {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, | 29 | {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, |
| 30 | {10700, nullptr, "GetPlayHistoryRegistrationKey"}, | 30 | {10700, nullptr, "GetPlayHistoryRegistrationKey"}, |
| 31 | {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, | 31 | {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, |
| @@ -88,6 +88,7 @@ public: | |||
| 88 | {30830, nullptr, "ClearPlayLog"}, | 88 | {30830, nullptr, "ClearPlayLog"}, |
| 89 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, | 89 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, |
| 90 | }; | 90 | }; |
| 91 | // clang-format on | ||
| 91 | 92 | ||
| 92 | RegisterHandlers(functions); | 93 | RegisterHandlers(functions); |
| 93 | } | 94 | } |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 63b55758b..a4ad95d96 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -210,6 +210,7 @@ Hid::Hid() : ServiceFramework("hid") { | |||
| 210 | {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, | 210 | {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, |
| 211 | {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, | 211 | {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, |
| 212 | {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, | 212 | {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, |
| 213 | {134, nullptr, "SetNpadAnalogStickUseCenterClamp"}, | ||
| 213 | {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, | 214 | {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, |
| 214 | {201, &Hid::SendVibrationValue, "SendVibrationValue"}, | 215 | {201, &Hid::SendVibrationValue, "SendVibrationValue"}, |
| 215 | {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, | 216 | {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, |
| @@ -221,6 +222,7 @@ Hid::Hid() : ServiceFramework("hid") { | |||
| 221 | {208, nullptr, "GetActualVibrationGcErmCommand"}, | 222 | {208, nullptr, "GetActualVibrationGcErmCommand"}, |
| 222 | {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, | 223 | {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, |
| 223 | {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, | 224 | {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, |
| 225 | {211, nullptr, "IsVibrationDeviceMounted"}, | ||
| 224 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, | 226 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, |
| 225 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, | 227 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, |
| 226 | {302, nullptr, "StopConsoleSixAxisSensor"}, | 228 | {302, nullptr, "StopConsoleSixAxisSensor"}, |
| @@ -265,6 +267,7 @@ Hid::Hid() : ServiceFramework("hid") { | |||
| 265 | {523, nullptr, "SetIsPalmaPairedConnectable"}, | 267 | {523, nullptr, "SetIsPalmaPairedConnectable"}, |
| 266 | {524, nullptr, "PairPalma"}, | 268 | {524, nullptr, "PairPalma"}, |
| 267 | {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, | 269 | {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, |
| 270 | {526, nullptr, "CancelWritePalmaWaveEntry"}, | ||
| 268 | {1000, nullptr, "SetNpadCommunicationMode"}, | 271 | {1000, nullptr, "SetNpadCommunicationMode"}, |
| 269 | {1001, nullptr, "GetNpadCommunicationMode"}, | 272 | {1001, nullptr, "GetNpadCommunicationMode"}, |
| 270 | }; | 273 | }; |
| @@ -797,12 +800,22 @@ public: | |||
| 797 | {232, nullptr, "EnableShipmentMode"}, | 800 | {232, nullptr, "EnableShipmentMode"}, |
| 798 | {233, nullptr, "ClearPairingInfo"}, | 801 | {233, nullptr, "ClearPairingInfo"}, |
| 799 | {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, | 802 | {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, |
| 803 | {235, nullptr, "EnableAnalogStickPower"}, | ||
| 800 | {301, nullptr, "GetAbstractedPadHandles"}, | 804 | {301, nullptr, "GetAbstractedPadHandles"}, |
| 801 | {302, nullptr, "GetAbstractedPadState"}, | 805 | {302, nullptr, "GetAbstractedPadState"}, |
| 802 | {303, nullptr, "GetAbstractedPadsState"}, | 806 | {303, nullptr, "GetAbstractedPadsState"}, |
| 803 | {321, nullptr, "SetAutoPilotVirtualPadState"}, | 807 | {321, nullptr, "SetAutoPilotVirtualPadState"}, |
| 804 | {322, nullptr, "UnsetAutoPilotVirtualPadState"}, | 808 | {322, nullptr, "UnsetAutoPilotVirtualPadState"}, |
| 805 | {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, | 809 | {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, |
| 810 | {324, nullptr, "AttachHdlsWorkBuffer"}, | ||
| 811 | {325, nullptr, "ReleaseHdlsWorkBuffer"}, | ||
| 812 | {326, nullptr, "DumpHdlsNpadAssignmentState"}, | ||
| 813 | {327, nullptr, "DumpHdlsStates"}, | ||
| 814 | {328, nullptr, "ApplyHdlsNpadAssignmentState"}, | ||
| 815 | {329, nullptr, "ApplyHdlsStateList"}, | ||
| 816 | {330, nullptr, "AttachHdlsVirtualDevice"}, | ||
| 817 | {331, nullptr, "DetachHdlsVirtualDevice"}, | ||
| 818 | {332, nullptr, "SetHdlsState"}, | ||
| 806 | {350, nullptr, "AddRegisteredDevice"}, | 819 | {350, nullptr, "AddRegisteredDevice"}, |
| 807 | {400, nullptr, "DisableExternalMcuOnNxDevice"}, | 820 | {400, nullptr, "DisableExternalMcuOnNxDevice"}, |
| 808 | {401, nullptr, "DisableRailDeviceFiltering"}, | 821 | {401, nullptr, "DisableRailDeviceFiltering"}, |
| @@ -825,6 +838,7 @@ public: | |||
| 825 | {131, nullptr, "ActivateSleepButton"}, | 838 | {131, nullptr, "ActivateSleepButton"}, |
| 826 | {141, nullptr, "AcquireCaptureButtonEventHandle"}, | 839 | {141, nullptr, "AcquireCaptureButtonEventHandle"}, |
| 827 | {151, nullptr, "ActivateCaptureButton"}, | 840 | {151, nullptr, "ActivateCaptureButton"}, |
| 841 | {161, nullptr, "GetPlatformConfig"}, | ||
| 828 | {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, | 842 | {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, |
| 829 | {211, nullptr, "GetNpadsWithNfc"}, | 843 | {211, nullptr, "GetNpadsWithNfc"}, |
| 830 | {212, nullptr, "AcquireNfcActivateEventHandle"}, | 844 | {212, nullptr, "AcquireNfcActivateEventHandle"}, |
| @@ -894,6 +908,7 @@ public: | |||
| 894 | {827, nullptr, "IsAnalogStickButtonPressed"}, | 908 | {827, nullptr, "IsAnalogStickButtonPressed"}, |
| 895 | {828, nullptr, "IsAnalogStickInReleasePosition"}, | 909 | {828, nullptr, "IsAnalogStickInReleasePosition"}, |
| 896 | {829, nullptr, "IsAnalogStickInCircumference"}, | 910 | {829, nullptr, "IsAnalogStickInCircumference"}, |
| 911 | {830, nullptr, "SetNotificationLedPattern"}, | ||
| 897 | {850, nullptr, "IsUsbFullKeyControllerEnabled"}, | 912 | {850, nullptr, "IsUsbFullKeyControllerEnabled"}, |
| 898 | {851, nullptr, "EnableUsbFullKeyController"}, | 913 | {851, nullptr, "EnableUsbFullKeyController"}, |
| 899 | {852, nullptr, "IsUsbConnected"}, | 914 | {852, nullptr, "IsUsbConnected"}, |
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index e250595e3..ed5059047 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -52,9 +52,11 @@ public: | |||
| 52 | } | 52 | } |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | class ILocalCommunicationService final : public ServiceFramework<ILocalCommunicationService> { | 55 | class ISystemLocalCommunicationService final |
| 56 | : public ServiceFramework<ISystemLocalCommunicationService> { | ||
| 56 | public: | 57 | public: |
| 57 | explicit ILocalCommunicationService(const char* name) : ServiceFramework{name} { | 58 | explicit ISystemLocalCommunicationService() |
| 59 | : ServiceFramework{"ISystemLocalCommunicationService"} { | ||
| 58 | // clang-format off | 60 | // clang-format off |
| 59 | static const FunctionInfo functions[] = { | 61 | static const FunctionInfo functions[] = { |
| 60 | {0, nullptr, "GetState"}, | 62 | {0, nullptr, "GetState"}, |
| @@ -84,6 +86,50 @@ public: | |||
| 84 | {304, nullptr, "Disconnect"}, | 86 | {304, nullptr, "Disconnect"}, |
| 85 | {400, nullptr, "InitializeSystem"}, | 87 | {400, nullptr, "InitializeSystem"}, |
| 86 | {401, nullptr, "FinalizeSystem"}, | 88 | {401, nullptr, "FinalizeSystem"}, |
| 89 | {402, nullptr, "SetOperationMode"}, | ||
| 90 | {403, nullptr, "InitializeSystem2"}, | ||
| 91 | }; | ||
| 92 | // clang-format on | ||
| 93 | |||
| 94 | RegisterHandlers(functions); | ||
| 95 | } | ||
| 96 | }; | ||
| 97 | |||
| 98 | class IUserLocalCommunicationService final | ||
| 99 | : public ServiceFramework<IUserLocalCommunicationService> { | ||
| 100 | public: | ||
| 101 | explicit IUserLocalCommunicationService() : ServiceFramework{"IUserLocalCommunicationService"} { | ||
| 102 | // clang-format off | ||
| 103 | static const FunctionInfo functions[] = { | ||
| 104 | {0, nullptr, "GetState"}, | ||
| 105 | {1, nullptr, "GetNetworkInfo"}, | ||
| 106 | {2, nullptr, "GetIpv4Address"}, | ||
| 107 | {3, nullptr, "GetDisconnectReason"}, | ||
| 108 | {4, nullptr, "GetSecurityParameter"}, | ||
| 109 | {5, nullptr, "GetNetworkConfig"}, | ||
| 110 | {100, nullptr, "AttachStateChangeEvent"}, | ||
| 111 | {101, nullptr, "GetNetworkInfoLatestUpdate"}, | ||
| 112 | {102, nullptr, "Scan"}, | ||
| 113 | {103, nullptr, "ScanPrivate"}, | ||
| 114 | {104, nullptr, "SetWirelessControllerRestriction"}, | ||
| 115 | {200, nullptr, "OpenAccessPoint"}, | ||
| 116 | {201, nullptr, "CloseAccessPoint"}, | ||
| 117 | {202, nullptr, "CreateNetwork"}, | ||
| 118 | {203, nullptr, "CreateNetworkPrivate"}, | ||
| 119 | {204, nullptr, "DestroyNetwork"}, | ||
| 120 | {205, nullptr, "Reject"}, | ||
| 121 | {206, nullptr, "SetAdvertiseData"}, | ||
| 122 | {207, nullptr, "SetStationAcceptPolicy"}, | ||
| 123 | {208, nullptr, "AddAcceptFilterEntry"}, | ||
| 124 | {209, nullptr, "ClearAcceptFilter"}, | ||
| 125 | {300, nullptr, "OpenStation"}, | ||
| 126 | {301, nullptr, "CloseStation"}, | ||
| 127 | {302, nullptr, "Connect"}, | ||
| 128 | {303, nullptr, "ConnectPrivate"}, | ||
| 129 | {304, nullptr, "Disconnect"}, | ||
| 130 | {400, nullptr, "Initialize"}, | ||
| 131 | {401, nullptr, "Finalize"}, | ||
| 132 | {402, nullptr, "SetOperationMode"}, | ||
| 87 | }; | 133 | }; |
| 88 | // clang-format on | 134 | // clang-format on |
| 89 | 135 | ||
| @@ -108,7 +154,7 @@ public: | |||
| 108 | 154 | ||
| 109 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 155 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 110 | rb.Push(RESULT_SUCCESS); | 156 | rb.Push(RESULT_SUCCESS); |
| 111 | rb.PushIpcInterface<ILocalCommunicationService>("ISystemLocalCommunicationService"); | 157 | rb.PushIpcInterface<ISystemLocalCommunicationService>(); |
| 112 | } | 158 | } |
| 113 | }; | 159 | }; |
| 114 | 160 | ||
| @@ -129,7 +175,7 @@ public: | |||
| 129 | 175 | ||
| 130 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 176 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 131 | rb.Push(RESULT_SUCCESS); | 177 | rb.Push(RESULT_SUCCESS); |
| 132 | rb.PushIpcInterface<ILocalCommunicationService>("IUserLocalCommunicationService"); | 178 | rb.PushIpcInterface<IUserLocalCommunicationService>(); |
| 133 | } | 179 | } |
| 134 | }; | 180 | }; |
| 135 | 181 | ||
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d65693fc7..5af925515 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -86,6 +86,7 @@ public: | |||
| 86 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, | 86 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, |
| 87 | {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, | 87 | {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, |
| 88 | {4, &RelocatableObject::Initialize, "Initialize"}, | 88 | {4, &RelocatableObject::Initialize, "Initialize"}, |
| 89 | {10, nullptr, "LoadNrrEx"}, | ||
| 89 | }; | 90 | }; |
| 90 | // clang-format on | 91 | // clang-format on |
| 91 | 92 | ||
| @@ -93,12 +94,18 @@ public: | |||
| 93 | } | 94 | } |
| 94 | 95 | ||
| 95 | void LoadNrr(Kernel::HLERequestContext& ctx) { | 96 | void LoadNrr(Kernel::HLERequestContext& ctx) { |
| 97 | struct Parameters { | ||
| 98 | u64_le process_id; | ||
| 99 | u64_le nrr_address; | ||
| 100 | u64_le nrr_size; | ||
| 101 | }; | ||
| 102 | |||
| 96 | IPC::RequestParser rp{ctx}; | 103 | IPC::RequestParser rp{ctx}; |
| 97 | rp.Skip(2, false); | 104 | const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>(); |
| 98 | const VAddr nrr_addr{rp.Pop<VAddr>()}; | 105 | |
| 99 | const u64 nrr_size{rp.Pop<u64>()}; | 106 | LOG_DEBUG(Service_LDR, |
| 100 | LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr, | 107 | "called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}", |
| 101 | nrr_size); | 108 | process_id, nrr_address, nrr_size); |
| 102 | 109 | ||
| 103 | if (!initialized) { | 110 | if (!initialized) { |
| 104 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 111 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| @@ -116,24 +123,26 @@ public: | |||
| 116 | } | 123 | } |
| 117 | 124 | ||
| 118 | // NRR Address does not fall on 0x1000 byte boundary | 125 | // NRR Address does not fall on 0x1000 byte boundary |
| 119 | if (!Common::Is4KBAligned(nrr_addr)) { | 126 | if (!Common::Is4KBAligned(nrr_address)) { |
| 120 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 127 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", |
| 128 | nrr_address); | ||
| 121 | IPC::ResponseBuilder rb{ctx, 2}; | 129 | IPC::ResponseBuilder rb{ctx, 2}; |
| 122 | rb.Push(ERROR_INVALID_ALIGNMENT); | 130 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 123 | return; | 131 | return; |
| 124 | } | 132 | } |
| 125 | 133 | ||
| 126 | // NRR Size is zero or causes overflow | 134 | // NRR Size is zero or causes overflow |
| 127 | if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { | 135 | if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 || |
| 136 | !Common::Is4KBAligned(nrr_size)) { | ||
| 128 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", | 137 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", |
| 129 | nrr_addr, nrr_size); | 138 | nrr_address, nrr_size); |
| 130 | IPC::ResponseBuilder rb{ctx, 2}; | 139 | IPC::ResponseBuilder rb{ctx, 2}; |
| 131 | rb.Push(ERROR_INVALID_SIZE); | 140 | rb.Push(ERROR_INVALID_SIZE); |
| 132 | return; | 141 | return; |
| 133 | } | 142 | } |
| 134 | // Read NRR data from memory | 143 | // Read NRR data from memory |
| 135 | std::vector<u8> nrr_data(nrr_size); | 144 | std::vector<u8> nrr_data(nrr_size); |
| 136 | Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); | 145 | Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size); |
| 137 | NRRHeader header; | 146 | NRRHeader header; |
| 138 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); | 147 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); |
| 139 | 148 | ||
| @@ -174,7 +183,7 @@ public: | |||
| 174 | hashes.emplace_back(hash); | 183 | hashes.emplace_back(hash); |
| 175 | } | 184 | } |
| 176 | 185 | ||
| 177 | nrr.insert_or_assign(nrr_addr, std::move(hashes)); | 186 | nrr.insert_or_assign(nrr_address, std::move(hashes)); |
| 178 | 187 | ||
| 179 | IPC::ResponseBuilder rb{ctx, 2}; | 188 | IPC::ResponseBuilder rb{ctx, 2}; |
| 180 | rb.Push(RESULT_SUCCESS); | 189 | rb.Push(RESULT_SUCCESS); |
| @@ -188,23 +197,30 @@ public: | |||
| 188 | return; | 197 | return; |
| 189 | } | 198 | } |
| 190 | 199 | ||
| 200 | struct Parameters { | ||
| 201 | u64_le process_id; | ||
| 202 | u64_le nrr_address; | ||
| 203 | }; | ||
| 204 | |||
| 191 | IPC::RequestParser rp{ctx}; | 205 | IPC::RequestParser rp{ctx}; |
| 192 | rp.Skip(2, false); | 206 | const auto [process_id, nrr_address] = rp.PopRaw<Parameters>(); |
| 193 | const auto nrr_addr{rp.Pop<VAddr>()}; | 207 | |
| 194 | LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr); | 208 | LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id, |
| 209 | nrr_address); | ||
| 195 | 210 | ||
| 196 | if (!Common::Is4KBAligned(nrr_addr)) { | 211 | if (!Common::Is4KBAligned(nrr_address)) { |
| 197 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 212 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", |
| 213 | nrr_address); | ||
| 198 | IPC::ResponseBuilder rb{ctx, 2}; | 214 | IPC::ResponseBuilder rb{ctx, 2}; |
| 199 | rb.Push(ERROR_INVALID_ALIGNMENT); | 215 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 200 | return; | 216 | return; |
| 201 | } | 217 | } |
| 202 | 218 | ||
| 203 | const auto iter = nrr.find(nrr_addr); | 219 | const auto iter = nrr.find(nrr_address); |
| 204 | if (iter == nrr.end()) { | 220 | if (iter == nrr.end()) { |
| 205 | LOG_ERROR(Service_LDR, | 221 | LOG_ERROR(Service_LDR, |
| 206 | "Attempting to unload NRR which has not been loaded! (addr={:016X})", | 222 | "Attempting to unload NRR which has not been loaded! (addr={:016X})", |
| 207 | nrr_addr); | 223 | nrr_address); |
| 208 | IPC::ResponseBuilder rb{ctx, 2}; | 224 | IPC::ResponseBuilder rb{ctx, 2}; |
| 209 | rb.Push(ERROR_INVALID_NRR_ADDRESS); | 225 | rb.Push(ERROR_INVALID_NRR_ADDRESS); |
| 210 | return; | 226 | return; |
| @@ -216,16 +232,22 @@ public: | |||
| 216 | } | 232 | } |
| 217 | 233 | ||
| 218 | void LoadNro(Kernel::HLERequestContext& ctx) { | 234 | void LoadNro(Kernel::HLERequestContext& ctx) { |
| 235 | struct Parameters { | ||
| 236 | u64_le process_id; | ||
| 237 | u64_le image_address; | ||
| 238 | u64_le image_size; | ||
| 239 | u64_le bss_address; | ||
| 240 | u64_le bss_size; | ||
| 241 | }; | ||
| 242 | |||
| 219 | IPC::RequestParser rp{ctx}; | 243 | IPC::RequestParser rp{ctx}; |
| 220 | rp.Skip(2, false); | 244 | const auto [process_id, nro_address, nro_size, bss_address, bss_size] = |
| 221 | const VAddr nro_addr{rp.Pop<VAddr>()}; | 245 | rp.PopRaw<Parameters>(); |
| 222 | const u64 nro_size{rp.Pop<u64>()}; | 246 | |
| 223 | const VAddr bss_addr{rp.Pop<VAddr>()}; | 247 | LOG_DEBUG(Service_LDR, |
| 224 | const u64 bss_size{rp.Pop<u64>()}; | 248 | "called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, " |
| 225 | LOG_DEBUG( | 249 | "bss_size={:016X}", |
| 226 | Service_LDR, | 250 | process_id, nro_address, nro_size, bss_address, bss_size); |
| 227 | "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}", | ||
| 228 | nro_addr, nro_size, bss_addr, bss_size); | ||
| 229 | 251 | ||
| 230 | if (!initialized) { | 252 | if (!initialized) { |
| 231 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 253 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| @@ -243,8 +265,9 @@ public: | |||
| 243 | } | 265 | } |
| 244 | 266 | ||
| 245 | // NRO Address does not fall on 0x1000 byte boundary | 267 | // NRO Address does not fall on 0x1000 byte boundary |
| 246 | if (!Common::Is4KBAligned(nro_addr)) { | 268 | if (!Common::Is4KBAligned(nro_address)) { |
| 247 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); | 269 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", |
| 270 | nro_address); | ||
| 248 | IPC::ResponseBuilder rb{ctx, 2}; | 271 | IPC::ResponseBuilder rb{ctx, 2}; |
| 249 | rb.Push(ERROR_INVALID_ALIGNMENT); | 272 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 250 | return; | 273 | return; |
| @@ -252,15 +275,15 @@ public: | |||
| 252 | 275 | ||
| 253 | // NRO Size or BSS Size is zero or causes overflow | 276 | // NRO Size or BSS Size is zero or causes overflow |
| 254 | const auto nro_size_valid = | 277 | const auto nro_size_valid = |
| 255 | nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); | 278 | nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size); |
| 256 | const auto bss_size_valid = | 279 | const auto bss_size_valid = nro_size + bss_size >= nro_size && |
| 257 | nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); | 280 | (bss_size == 0 || bss_address + bss_size > bss_address); |
| 258 | 281 | ||
| 259 | if (!nro_size_valid || !bss_size_valid) { | 282 | if (!nro_size_valid || !bss_size_valid) { |
| 260 | LOG_ERROR(Service_LDR, | 283 | LOG_ERROR(Service_LDR, |
| 261 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " | 284 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " |
| 262 | "bss_address={:016X}, bss_size={:016X})", | 285 | "bss_address={:016X}, bss_size={:016X})", |
| 263 | nro_addr, nro_size, bss_addr, bss_size); | 286 | nro_address, nro_size, bss_address, bss_size); |
| 264 | IPC::ResponseBuilder rb{ctx, 2}; | 287 | IPC::ResponseBuilder rb{ctx, 2}; |
| 265 | rb.Push(ERROR_INVALID_SIZE); | 288 | rb.Push(ERROR_INVALID_SIZE); |
| 266 | return; | 289 | return; |
| @@ -268,7 +291,7 @@ public: | |||
| 268 | 291 | ||
| 269 | // Read NRO data from memory | 292 | // Read NRO data from memory |
| 270 | std::vector<u8> nro_data(nro_size); | 293 | std::vector<u8> nro_data(nro_size); |
| 271 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); | 294 | Memory::ReadBlock(nro_address, nro_data.data(), nro_size); |
| 272 | 295 | ||
| 273 | SHA256Hash hash{}; | 296 | SHA256Hash hash{}; |
| 274 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); | 297 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); |
| @@ -318,17 +341,18 @@ public: | |||
| 318 | return; | 341 | return; |
| 319 | } | 342 | } |
| 320 | 343 | ||
| 321 | ASSERT(vm_manager | 344 | ASSERT( |
| 322 | .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode) | 345 | vm_manager |
| 323 | .IsSuccess()); | 346 | .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) |
| 324 | ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); | 347 | .IsSuccess()); |
| 348 | ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); | ||
| 325 | 349 | ||
| 326 | if (bss_size > 0) { | 350 | if (bss_size > 0) { |
| 327 | ASSERT(vm_manager | 351 | ASSERT(vm_manager |
| 328 | .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, | 352 | .MirrorMemory(*map_address + nro_size, bss_address, bss_size, |
| 329 | Kernel::MemoryState::ModuleCode) | 353 | Kernel::MemoryState::ModuleCode) |
| 330 | .IsSuccess()); | 354 | .IsSuccess()); |
| 331 | ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); | 355 | ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); |
| 332 | } | 356 | } |
| 333 | 357 | ||
| 334 | vm_manager.ReprotectRange(*map_address, header.text_size, | 358 | vm_manager.ReprotectRange(*map_address, header.text_size, |
| @@ -348,13 +372,6 @@ public: | |||
| 348 | } | 372 | } |
| 349 | 373 | ||
| 350 | void UnloadNro(Kernel::HLERequestContext& ctx) { | 374 | void UnloadNro(Kernel::HLERequestContext& ctx) { |
| 351 | IPC::RequestParser rp{ctx}; | ||
| 352 | rp.Skip(2, false); | ||
| 353 | const VAddr mapped_addr{rp.PopRaw<VAddr>()}; | ||
| 354 | const VAddr heap_addr{rp.PopRaw<VAddr>()}; | ||
| 355 | LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr, | ||
| 356 | heap_addr); | ||
| 357 | |||
| 358 | if (!initialized) { | 375 | if (!initialized) { |
| 359 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 376 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| 360 | IPC::ResponseBuilder rb{ctx, 2}; | 377 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -362,22 +379,30 @@ public: | |||
| 362 | return; | 379 | return; |
| 363 | } | 380 | } |
| 364 | 381 | ||
| 365 | if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { | 382 | struct Parameters { |
| 366 | LOG_ERROR(Service_LDR, | 383 | u64_le process_id; |
| 367 | "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " | 384 | u64_le nro_address; |
| 368 | "bss_addr={:016X})!", | 385 | }; |
| 369 | mapped_addr, heap_addr); | 386 | |
| 387 | IPC::RequestParser rp{ctx}; | ||
| 388 | const auto [process_id, nro_address] = rp.PopRaw<Parameters>(); | ||
| 389 | LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id, | ||
| 390 | nro_address); | ||
| 391 | |||
| 392 | if (!Common::Is4KBAligned(nro_address)) { | ||
| 393 | LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})", | ||
| 394 | nro_address); | ||
| 370 | IPC::ResponseBuilder rb{ctx, 2}; | 395 | IPC::ResponseBuilder rb{ctx, 2}; |
| 371 | rb.Push(ERROR_INVALID_ALIGNMENT); | 396 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 372 | return; | 397 | return; |
| 373 | } | 398 | } |
| 374 | 399 | ||
| 375 | const auto iter = nro.find(mapped_addr); | 400 | const auto iter = nro.find(nro_address); |
| 376 | if (iter == nro.end()) { | 401 | if (iter == nro.end()) { |
| 377 | LOG_ERROR(Service_LDR, | 402 | LOG_ERROR(Service_LDR, |
| 378 | "The NRO attempting to unmap was not mapped or has an invalid address " | 403 | "The NRO attempting to be unmapped was not mapped or has an invalid address " |
| 379 | "(actual {:016X})!", | 404 | "(nro_address=0x{:016X})!", |
| 380 | mapped_addr); | 405 | nro_address); |
| 381 | IPC::ResponseBuilder rb{ctx, 2}; | 406 | IPC::ResponseBuilder rb{ctx, 2}; |
| 382 | rb.Push(ERROR_INVALID_NRO_ADDRESS); | 407 | rb.Push(ERROR_INVALID_NRO_ADDRESS); |
| 383 | return; | 408 | return; |
| @@ -386,10 +411,7 @@ public: | |||
| 386 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 411 | auto& vm_manager = Core::CurrentProcess()->VMManager(); |
| 387 | const auto& nro_size = iter->second.size; | 412 | const auto& nro_size = iter->second.size; |
| 388 | 413 | ||
| 389 | ASSERT(vm_manager | 414 | ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); |
| 390 | .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode) | ||
| 391 | .IsSuccess()); | ||
| 392 | ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); | ||
| 393 | 415 | ||
| 394 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 416 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); |
| 395 | 417 | ||
| @@ -459,11 +481,10 @@ private: | |||
| 459 | std::map<VAddr, NROInfo> nro; | 481 | std::map<VAddr, NROInfo> nro; |
| 460 | std::map<VAddr, std::vector<SHA256Hash>> nrr; | 482 | std::map<VAddr, std::vector<SHA256Hash>> nrr; |
| 461 | 483 | ||
| 462 | bool IsValidNROHash(const SHA256Hash& hash) { | 484 | bool IsValidNROHash(const SHA256Hash& hash) const { |
| 463 | return std::any_of( | 485 | return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { |
| 464 | nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { | 486 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); |
| 465 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); | 487 | }); |
| 466 | }); | ||
| 467 | } | 488 | } |
| 468 | 489 | ||
| 469 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | 490 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 60479bb45..f92571008 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -15,12 +15,16 @@ namespace Service::NIFM { | |||
| 15 | class IScanRequest final : public ServiceFramework<IScanRequest> { | 15 | class IScanRequest final : public ServiceFramework<IScanRequest> { |
| 16 | public: | 16 | public: |
| 17 | explicit IScanRequest() : ServiceFramework("IScanRequest") { | 17 | explicit IScanRequest() : ServiceFramework("IScanRequest") { |
| 18 | // clang-format off | ||
| 18 | static const FunctionInfo functions[] = { | 19 | static const FunctionInfo functions[] = { |
| 19 | {0, nullptr, "Submit"}, | 20 | {0, nullptr, "Submit"}, |
| 20 | {1, nullptr, "IsProcessing"}, | 21 | {1, nullptr, "IsProcessing"}, |
| 21 | {2, nullptr, "GetResult"}, | 22 | {2, nullptr, "GetResult"}, |
| 22 | {3, nullptr, "GetSystemEventReadableHandle"}, | 23 | {3, nullptr, "GetSystemEventReadableHandle"}, |
| 24 | {4, nullptr, "SetChannels"}, | ||
| 23 | }; | 25 | }; |
| 26 | // clang-format on | ||
| 27 | |||
| 24 | RegisterHandlers(functions); | 28 | RegisterHandlers(functions); |
| 25 | } | 29 | } |
| 26 | }; | 30 | }; |
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index ccb6f9da9..8751522ca 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp | |||
| @@ -45,7 +45,7 @@ public: | |||
| 45 | {114, nullptr, "AttachJid"}, | 45 | {114, nullptr, "AttachJid"}, |
| 46 | {115, nullptr, "DetachJid"}, | 46 | {115, nullptr, "DetachJid"}, |
| 47 | {201, nullptr, "RequestChangeStateForceTimed"}, | 47 | {201, nullptr, "RequestChangeStateForceTimed"}, |
| 48 | {102, nullptr, "RequestChangeStateForceAsync"}, | 48 | {202, nullptr, "RequestChangeStateForceAsync"}, |
| 49 | }; | 49 | }; |
| 50 | // clang-format on | 50 | // clang-format on |
| 51 | 51 | ||
| @@ -73,6 +73,7 @@ public: | |||
| 73 | {103, nullptr, "GetState"}, | 73 | {103, nullptr, "GetState"}, |
| 74 | {104, nullptr, "GetStatistics"}, | 74 | {104, nullptr, "GetStatistics"}, |
| 75 | {111, nullptr, "GetJid"}, | 75 | {111, nullptr, "GetJid"}, |
| 76 | {120, nullptr, "CreateNotificationReceiver"}, | ||
| 76 | }; | 77 | }; |
| 77 | // clang-format on | 78 | // clang-format on |
| 78 | 79 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index ace71169f..12f3ef825 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h | |||
| @@ -18,7 +18,7 @@ class nvmap; | |||
| 18 | class nvdisp_disp0 final : public nvdevice { | 18 | class nvdisp_disp0 final : public nvdevice { |
| 19 | public: | 19 | public: |
| 20 | explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); | 20 | explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); |
| 21 | ~nvdisp_disp0(); | 21 | ~nvdisp_disp0() override; |
| 22 | 22 | ||
| 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 24 | 24 | ||
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index fe311b069..5b4889910 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h | |||
| @@ -17,7 +17,7 @@ namespace Service::Nvidia { | |||
| 17 | class NVDRV final : public ServiceFramework<NVDRV> { | 17 | class NVDRV final : public ServiceFramework<NVDRV> { |
| 18 | public: | 18 | public: |
| 19 | NVDRV(std::shared_ptr<Module> nvdrv, const char* name); | 19 | NVDRV(std::shared_ptr<Module> nvdrv, const char* name); |
| 20 | ~NVDRV(); | 20 | ~NVDRV() override; |
| 21 | 21 | ||
| 22 | private: | 22 | private: |
| 23 | void Open(Kernel::HLERequestContext& ctx); | 23 | void Open(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h index 5a4dfc1f9..6eafb1346 100644 --- a/src/core/hle/service/nvdrv/nvmemp.h +++ b/src/core/hle/service/nvdrv/nvmemp.h | |||
| @@ -11,7 +11,7 @@ namespace Service::Nvidia { | |||
| 11 | class NVMEMP final : public ServiceFramework<NVMEMP> { | 11 | class NVMEMP final : public ServiceFramework<NVMEMP> { |
| 12 | public: | 12 | public: |
| 13 | NVMEMP(); | 13 | NVMEMP(); |
| 14 | ~NVMEMP(); | 14 | ~NVMEMP() override; |
| 15 | 15 | ||
| 16 | private: | 16 | private: |
| 17 | void Cmd0(Kernel::HLERequestContext& ctx); | 17 | void Cmd0(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index c7f5bbf28..3c5c53e24 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -21,12 +21,13 @@ | |||
| 21 | #include "core/hle/service/vi/display/vi_display.h" | 21 | #include "core/hle/service/vi/display/vi_display.h" |
| 22 | #include "core/hle/service/vi/layer/vi_layer.h" | 22 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 23 | #include "core/perf_stats.h" | 23 | #include "core/perf_stats.h" |
| 24 | #include "core/settings.h" | ||
| 24 | #include "video_core/renderer_base.h" | 25 | #include "video_core/renderer_base.h" |
| 25 | 26 | ||
| 26 | namespace Service::NVFlinger { | 27 | namespace Service::NVFlinger { |
| 27 | 28 | ||
| 28 | constexpr std::size_t SCREEN_REFRESH_RATE = 60; | 29 | constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); |
| 29 | constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); | 30 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); |
| 30 | 31 | ||
| 31 | NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { | 32 | NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { |
| 32 | displays.emplace_back(0, "Default"); | 33 | displays.emplace_back(0, "Default"); |
| @@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t | |||
| 36 | displays.emplace_back(4, "Null"); | 37 | displays.emplace_back(4, "Null"); |
| 37 | 38 | ||
| 38 | // Schedule the screen composition events | 39 | // Schedule the screen composition events |
| 39 | composition_event = | 40 | const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks; |
| 40 | core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { | 41 | |
| 42 | composition_event = core_timing.RegisterEvent( | ||
| 43 | "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) { | ||
| 41 | Compose(); | 44 | Compose(); |
| 42 | this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); | 45 | this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event); |
| 43 | }); | 46 | }); |
| 44 | 47 | ||
| 45 | core_timing.ScheduleEvent(frame_ticks, composition_event); | 48 | core_timing.ScheduleEvent(ticks, composition_event); |
| 46 | } | 49 | } |
| 47 | 50 | ||
| 48 | NVFlinger::~NVFlinger() { | 51 | NVFlinger::~NVFlinger() { |
| @@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | |||
| 62 | const auto itr = | 65 | const auto itr = |
| 63 | std::find_if(displays.begin(), displays.end(), | 66 | std::find_if(displays.begin(), displays.end(), |
| 64 | [&](const VI::Display& display) { return display.GetName() == name; }); | 67 | [&](const VI::Display& display) { return display.GetName() == name; }); |
| 68 | |||
| 65 | if (itr == displays.end()) { | 69 | if (itr == displays.end()) { |
| 66 | return {}; | 70 | return {}; |
| 67 | } | 71 | } |
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index 6081f41e1..c75b4ee34 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp | |||
| @@ -12,10 +12,10 @@ namespace Service::PCTL { | |||
| 12 | class IParentalControlService final : public ServiceFramework<IParentalControlService> { | 12 | class IParentalControlService final : public ServiceFramework<IParentalControlService> { |
| 13 | public: | 13 | public: |
| 14 | IParentalControlService() : ServiceFramework("IParentalControlService") { | 14 | IParentalControlService() : ServiceFramework("IParentalControlService") { |
| 15 | // clang-format off | ||
| 15 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 16 | {1, &IParentalControlService::Initialize, "Initialize"}, | 17 | {1, &IParentalControlService::Initialize, "Initialize"}, |
| 17 | {1001, &IParentalControlService::CheckFreeCommunicationPermission, | 18 | {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"}, |
| 18 | "CheckFreeCommunicationPermission"}, | ||
| 19 | {1002, nullptr, "ConfirmLaunchApplicationPermission"}, | 19 | {1002, nullptr, "ConfirmLaunchApplicationPermission"}, |
| 20 | {1003, nullptr, "ConfirmResumeApplicationPermission"}, | 20 | {1003, nullptr, "ConfirmResumeApplicationPermission"}, |
| 21 | {1004, nullptr, "ConfirmSnsPostPermission"}, | 21 | {1004, nullptr, "ConfirmSnsPostPermission"}, |
| @@ -30,6 +30,7 @@ public: | |||
| 30 | {1013, nullptr, "ConfirmStereoVisionPermission"}, | 30 | {1013, nullptr, "ConfirmStereoVisionPermission"}, |
| 31 | {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, | 31 | {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, |
| 32 | {1015, nullptr, "ConfirmPlayableApplicationVideo"}, | 32 | {1015, nullptr, "ConfirmPlayableApplicationVideo"}, |
| 33 | {1016, nullptr, "ConfirmShowNewsPermission"}, | ||
| 33 | {1031, nullptr, "IsRestrictionEnabled"}, | 34 | {1031, nullptr, "IsRestrictionEnabled"}, |
| 34 | {1032, nullptr, "GetSafetyLevel"}, | 35 | {1032, nullptr, "GetSafetyLevel"}, |
| 35 | {1033, nullptr, "SetSafetyLevel"}, | 36 | {1033, nullptr, "SetSafetyLevel"}, |
| @@ -45,6 +46,7 @@ public: | |||
| 45 | {1045, nullptr, "UpdateFreeCommunicationApplicationList"}, | 46 | {1045, nullptr, "UpdateFreeCommunicationApplicationList"}, |
| 46 | {1046, nullptr, "DisableFeaturesForReset"}, | 47 | {1046, nullptr, "DisableFeaturesForReset"}, |
| 47 | {1047, nullptr, "NotifyApplicationDownloadStarted"}, | 48 | {1047, nullptr, "NotifyApplicationDownloadStarted"}, |
| 49 | {1048, nullptr, "NotifyNetworkProfileCreated"}, | ||
| 48 | {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"}, | 50 | {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"}, |
| 49 | {1062, nullptr, "GetStereoVisionRestriction"}, | 51 | {1062, nullptr, "GetStereoVisionRestriction"}, |
| 50 | {1063, nullptr, "SetStereoVisionRestriction"}, | 52 | {1063, nullptr, "SetStereoVisionRestriction"}, |
| @@ -63,6 +65,7 @@ public: | |||
| 63 | {1411, nullptr, "GetPairingAccountInfo"}, | 65 | {1411, nullptr, "GetPairingAccountInfo"}, |
| 64 | {1421, nullptr, "GetAccountNickname"}, | 66 | {1421, nullptr, "GetAccountNickname"}, |
| 65 | {1424, nullptr, "GetAccountState"}, | 67 | {1424, nullptr, "GetAccountState"}, |
| 68 | {1425, nullptr, "RequestPostEvents"}, | ||
| 66 | {1432, nullptr, "GetSynchronizationEvent"}, | 69 | {1432, nullptr, "GetSynchronizationEvent"}, |
| 67 | {1451, nullptr, "StartPlayTimer"}, | 70 | {1451, nullptr, "StartPlayTimer"}, |
| 68 | {1452, nullptr, "StopPlayTimer"}, | 71 | {1452, nullptr, "StopPlayTimer"}, |
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 6b27dc4a3..ebcc41a43 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp | |||
| @@ -42,15 +42,18 @@ private: | |||
| 42 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { | 42 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { |
| 43 | public: | 43 | public: |
| 44 | explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { | 44 | explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { |
| 45 | // clang-format off | ||
| 45 | static const FunctionInfo functions[] = { | 46 | static const FunctionInfo functions[] = { |
| 46 | {0, nullptr, "IsDebugMode"}, | 47 | {0, nullptr, "GetDebugProcesses"}, |
| 47 | {1, nullptr, "GetDebugProcesses"}, | 48 | {1, nullptr, "StartDebugProcess"}, |
| 48 | {2, nullptr, "StartDebugProcess"}, | 49 | {2, nullptr, "GetTitlePid"}, |
| 49 | {3, nullptr, "GetTitlePid"}, | 50 | {3, nullptr, "EnableDebugForTitleId"}, |
| 50 | {4, nullptr, "EnableDebugForTitleId"}, | 51 | {4, nullptr, "GetApplicationPid"}, |
| 51 | {5, nullptr, "GetApplicationPid"}, | 52 | {5, nullptr, "EnableDebugForApplication"}, |
| 52 | {6, nullptr, "EnableDebugForApplication"}, | 53 | {6, nullptr, "DisableDebug"}, |
| 53 | }; | 54 | }; |
| 55 | // clang-format on | ||
| 56 | |||
| 54 | RegisterHandlers(functions); | 57 | RegisterHandlers(functions); |
| 55 | } | 58 | } |
| 56 | }; | 59 | }; |
| @@ -68,6 +71,7 @@ public: | |||
| 68 | class Shell final : public ServiceFramework<Shell> { | 71 | class Shell final : public ServiceFramework<Shell> { |
| 69 | public: | 72 | public: |
| 70 | explicit Shell() : ServiceFramework{"pm:shell"} { | 73 | explicit Shell() : ServiceFramework{"pm:shell"} { |
| 74 | // clang-format off | ||
| 71 | static const FunctionInfo functions[] = { | 75 | static const FunctionInfo functions[] = { |
| 72 | {0, nullptr, "LaunchProcess"}, | 76 | {0, nullptr, "LaunchProcess"}, |
| 73 | {1, nullptr, "TerminateProcessByPid"}, | 77 | {1, nullptr, "TerminateProcessByPid"}, |
| @@ -77,7 +81,10 @@ public: | |||
| 77 | {5, nullptr, "NotifyBootFinished"}, | 81 | {5, nullptr, "NotifyBootFinished"}, |
| 78 | {6, nullptr, "GetApplicationPid"}, | 82 | {6, nullptr, "GetApplicationPid"}, |
| 79 | {7, nullptr, "BoostSystemMemoryResourceLimit"}, | 83 | {7, nullptr, "BoostSystemMemoryResourceLimit"}, |
| 84 | {8, nullptr, "EnableAdditionalSystemThreads"}, | ||
| 80 | }; | 85 | }; |
| 86 | // clang-format on | ||
| 87 | |||
| 81 | RegisterHandlers(functions); | 88 | RegisterHandlers(functions); |
| 82 | } | 89 | } |
| 83 | }; | 90 | }; |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 830790269..abbfe5524 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -90,7 +90,7 @@ private: | |||
| 90 | Kernel::HLERequestContext& ctx); | 90 | Kernel::HLERequestContext& ctx); |
| 91 | 91 | ||
| 92 | ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); | 92 | ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); |
| 93 | ~ServiceFrameworkBase(); | 93 | ~ServiceFrameworkBase() override; |
| 94 | 94 | ||
| 95 | void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); | 95 | void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); |
| 96 | void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); | 96 | void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); |
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 1afc43f75..4ecb6bcef 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -116,6 +116,7 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { | |||
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | SET::SET() : ServiceFramework("set") { | 118 | SET::SET() : ServiceFramework("set") { |
| 119 | // clang-format off | ||
| 119 | static const FunctionInfo functions[] = { | 120 | static const FunctionInfo functions[] = { |
| 120 | {0, &SET::GetLanguageCode, "GetLanguageCode"}, | 121 | {0, &SET::GetLanguageCode, "GetLanguageCode"}, |
| 121 | {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, | 122 | {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, |
| @@ -126,7 +127,10 @@ SET::SET() : ServiceFramework("set") { | |||
| 126 | {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, | 127 | {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, |
| 127 | {7, nullptr, "GetKeyCodeMap"}, | 128 | {7, nullptr, "GetKeyCodeMap"}, |
| 128 | {8, nullptr, "GetQuestFlag"}, | 129 | {8, nullptr, "GetQuestFlag"}, |
| 130 | {9, nullptr, "GetKeyCodeMap2"}, | ||
| 129 | }; | 131 | }; |
| 132 | // clang-format on | ||
| 133 | |||
| 130 | RegisterHandlers(functions); | 134 | RegisterHandlers(functions); |
| 131 | } | 135 | } |
| 132 | 136 | ||
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index 34654bb07..5981c575c 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp | |||
| @@ -40,7 +40,7 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") { | |||
| 40 | {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, | 40 | {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, |
| 41 | {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, | 41 | {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, |
| 42 | {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, | 42 | {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, |
| 43 | {33, nullptr, "GetBatteryVersion"}, | 43 | {41, nullptr, "GetBatteryVersion"}, |
| 44 | }; | 44 | }; |
| 45 | RegisterHandlers(functions); | 45 | RegisterHandlers(functions); |
| 46 | } | 46 | } |
diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h index 583036eac..a0677e815 100644 --- a/src/core/hle/service/set/set_cal.h +++ b/src/core/hle/service/set/set_cal.h | |||
| @@ -11,7 +11,7 @@ namespace Service::Set { | |||
| 11 | class SET_CAL final : public ServiceFramework<SET_CAL> { | 11 | class SET_CAL final : public ServiceFramework<SET_CAL> { |
| 12 | public: | 12 | public: |
| 13 | explicit SET_CAL(); | 13 | explicit SET_CAL(); |
| 14 | ~SET_CAL(); | 14 | ~SET_CAL() override; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | } // namespace Service::Set | 17 | } // namespace Service::Set |
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index ecee554bf..98d0cfdfd 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp | |||
| @@ -104,6 +104,7 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) { | |||
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | SET_SYS::SET_SYS() : ServiceFramework("set:sys") { | 106 | SET_SYS::SET_SYS() : ServiceFramework("set:sys") { |
| 107 | // clang-format off | ||
| 107 | static const FunctionInfo functions[] = { | 108 | static const FunctionInfo functions[] = { |
| 108 | {0, nullptr, "SetLanguageCode"}, | 109 | {0, nullptr, "SetLanguageCode"}, |
| 109 | {1, nullptr, "SetNetworkSettings"}, | 110 | {1, nullptr, "SetNetworkSettings"}, |
| @@ -252,7 +253,33 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") { | |||
| 252 | {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, | 253 | {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, |
| 253 | {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, | 254 | {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, |
| 254 | {149, nullptr, "GetRebootlessSystemUpdateVersion"}, | 255 | {149, nullptr, "GetRebootlessSystemUpdateVersion"}, |
| 256 | {150, nullptr, "GetDeviceTimeZoneLocationUpdatedTime"}, | ||
| 257 | {151, nullptr, "SetDeviceTimeZoneLocationUpdatedTime"}, | ||
| 258 | {152, nullptr, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, | ||
| 259 | {153, nullptr, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, | ||
| 260 | {154, nullptr, "GetAccountOnlineStorageSettings"}, | ||
| 261 | {155, nullptr, "SetAccountOnlineStorageSettings"}, | ||
| 262 | {156, nullptr, "GetPctlReadyFlag"}, | ||
| 263 | {157, nullptr, "SetPctlReadyFlag"}, | ||
| 264 | {162, nullptr, "GetPtmBatteryVersion"}, | ||
| 265 | {163, nullptr, "SetPtmBatteryVersion"}, | ||
| 266 | {164, nullptr, "GetUsb30HostEnableFlag"}, | ||
| 267 | {165, nullptr, "SetUsb30HostEnableFlag"}, | ||
| 268 | {166, nullptr, "GetUsb30DeviceEnableFlag"}, | ||
| 269 | {167, nullptr, "SetUsb30DeviceEnableFlag"}, | ||
| 270 | {168, nullptr, "GetThemeId"}, | ||
| 271 | {169, nullptr, "SetThemeId"}, | ||
| 272 | {170, nullptr, "GetChineseTraditionalInputMethod"}, | ||
| 273 | {171, nullptr, "SetChineseTraditionalInputMethod"}, | ||
| 274 | {172, nullptr, "GetPtmCycleCountReliability"}, | ||
| 275 | {173, nullptr, "SetPtmCycleCountReliability"}, | ||
| 276 | {175, nullptr, "GetThemeSettings"}, | ||
| 277 | {176, nullptr, "SetThemeSettings"}, | ||
| 278 | {177, nullptr, "GetThemeKey"}, | ||
| 279 | {178, nullptr, "SetThemeKey"}, | ||
| 255 | }; | 280 | }; |
| 281 | // clang-format on | ||
| 282 | |||
| 256 | RegisterHandlers(functions); | 283 | RegisterHandlers(functions); |
| 257 | } | 284 | } |
| 258 | 285 | ||
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 4342f3b2d..884ad173b 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -73,6 +73,7 @@ void BSD::Close(Kernel::HLERequestContext& ctx) { | |||
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | BSD::BSD(const char* name) : ServiceFramework(name) { | 75 | BSD::BSD(const char* name) : ServiceFramework(name) { |
| 76 | // clang-format off | ||
| 76 | static const FunctionInfo functions[] = { | 77 | static const FunctionInfo functions[] = { |
| 77 | {0, &BSD::RegisterClient, "RegisterClient"}, | 78 | {0, &BSD::RegisterClient, "RegisterClient"}, |
| 78 | {1, &BSD::StartMonitoring, "StartMonitoring"}, | 79 | {1, &BSD::StartMonitoring, "StartMonitoring"}, |
| @@ -105,7 +106,11 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 105 | {28, nullptr, "GetResourceStatistics"}, | 106 | {28, nullptr, "GetResourceStatistics"}, |
| 106 | {29, nullptr, "RecvMMsg"}, | 107 | {29, nullptr, "RecvMMsg"}, |
| 107 | {30, nullptr, "SendMMsg"}, | 108 | {30, nullptr, "SendMMsg"}, |
| 109 | {31, nullptr, "EventFd"}, | ||
| 110 | {32, nullptr, "RegisterResourceStatisticsName"}, | ||
| 108 | }; | 111 | }; |
| 112 | // clang-format on | ||
| 113 | |||
| 109 | RegisterHandlers(functions); | 114 | RegisterHandlers(functions); |
| 110 | } | 115 | } |
| 111 | 116 | ||
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index 13ab1d31e..852e71e4b 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp | |||
| @@ -8,12 +8,20 @@ | |||
| 8 | namespace Service::Sockets { | 8 | namespace Service::Sockets { |
| 9 | 9 | ||
| 10 | void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { | 10 | void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { |
| 11 | struct Parameters { | ||
| 12 | u8 use_nsd_resolve; | ||
| 13 | u32 unknown; | ||
| 14 | u64 process_id; | ||
| 15 | }; | ||
| 16 | |||
| 11 | IPC::RequestParser rp{ctx}; | 17 | IPC::RequestParser rp{ctx}; |
| 18 | const auto parameters = rp.PopRaw<Parameters>(); | ||
| 12 | 19 | ||
| 13 | LOG_WARNING(Service, "(STUBBED) called"); | 20 | LOG_WARNING(Service, |
| 21 | "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", | ||
| 22 | parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); | ||
| 14 | 23 | ||
| 15 | IPC::ResponseBuilder rb{ctx, 2}; | 24 | IPC::ResponseBuilder rb{ctx, 2}; |
| 16 | |||
| 17 | rb.Push(RESULT_SUCCESS); | 25 | rb.Push(RESULT_SUCCESS); |
| 18 | } | 26 | } |
| 19 | 27 | ||
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 8db0c2f13..e724d4ab8 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp | |||
| @@ -26,9 +26,7 @@ Module::Interface::~Interface() = default; | |||
| 26 | void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { | 26 | void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { |
| 27 | LOG_DEBUG(Service_SPL, "called"); | 27 | LOG_DEBUG(Service_SPL, "called"); |
| 28 | 28 | ||
| 29 | IPC::RequestParser rp{ctx}; | 29 | const std::size_t size = ctx.GetWriteBufferSize(); |
| 30 | |||
| 31 | std::size_t size = ctx.GetWriteBufferSize(); | ||
| 32 | 30 | ||
| 33 | std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); | 31 | std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); |
| 34 | std::vector<u8> data(size); | 32 | std::vector<u8> data(size); |
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index af40a1815..65040c077 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp | |||
| @@ -64,13 +64,19 @@ public: | |||
| 64 | }; | 64 | }; |
| 65 | RegisterHandlers(functions); | 65 | RegisterHandlers(functions); |
| 66 | } | 66 | } |
| 67 | ~ISslContext() = default; | ||
| 68 | 67 | ||
| 69 | private: | 68 | private: |
| 70 | void SetOption(Kernel::HLERequestContext& ctx) { | 69 | void SetOption(Kernel::HLERequestContext& ctx) { |
| 71 | LOG_WARNING(Service_SSL, "(STUBBED) called"); | 70 | struct Parameters { |
| 71 | u8 enable; | ||
| 72 | u32 option; | ||
| 73 | }; | ||
| 72 | 74 | ||
| 73 | IPC::RequestParser rp{ctx}; | 75 | IPC::RequestParser rp{ctx}; |
| 76 | const auto parameters = rp.PopRaw<Parameters>(); | ||
| 77 | |||
| 78 | LOG_WARNING(Service_SSL, "(STUBBED) called. enable={}, option={}", parameters.enable, | ||
| 79 | parameters.option); | ||
| 74 | 80 | ||
| 75 | IPC::ResponseBuilder rb{ctx, 2}; | 81 | IPC::ResponseBuilder rb{ctx, 2}; |
| 76 | rb.Push(RESULT_SUCCESS); | 82 | rb.Push(RESULT_SUCCESS); |
| @@ -97,6 +103,8 @@ public: | |||
| 97 | {4, nullptr, "DebugIoctl"}, | 103 | {4, nullptr, "DebugIoctl"}, |
| 98 | {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"}, | 104 | {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"}, |
| 99 | {6, nullptr, "FlushSessionCache"}, | 105 | {6, nullptr, "FlushSessionCache"}, |
| 106 | {7, nullptr, "SetDebugOption"}, | ||
| 107 | {8, nullptr, "GetDebugOption"}, | ||
| 100 | }; | 108 | }; |
| 101 | // clang-format on | 109 | // clang-format on |
| 102 | 110 | ||
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index b3a196f65..8d122ae33 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp | |||
| @@ -8,6 +8,7 @@ namespace Service::Time { | |||
| 8 | 8 | ||
| 9 | Time::Time(std::shared_ptr<Module> time, const char* name) | 9 | Time::Time(std::shared_ptr<Module> time, const char* name) |
| 10 | : Module::Interface(std::move(time), name) { | 10 | : Module::Interface(std::move(time), name) { |
| 11 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | 12 | static const FunctionInfo functions[] = { |
| 12 | {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, | 13 | {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, |
| 13 | {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, | 14 | {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, |
| @@ -15,18 +16,23 @@ Time::Time(std::shared_ptr<Module> time, const char* name) | |||
| 15 | {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, | 16 | {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, |
| 16 | {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, | 17 | {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, |
| 17 | {5, nullptr, "GetEphemeralNetworkSystemClock"}, | 18 | {5, nullptr, "GetEphemeralNetworkSystemClock"}, |
| 19 | {20, nullptr, "GetSharedMemoryNativeHandle"}, | ||
| 20 | {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, | ||
| 21 | {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, | ||
| 18 | {50, nullptr, "SetStandardSteadyClockInternalOffset"}, | 22 | {50, nullptr, "SetStandardSteadyClockInternalOffset"}, |
| 19 | {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, | 23 | {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, |
| 20 | {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, | 24 | {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, |
| 21 | {102, nullptr, "GetStandardUserSystemClockInitialYear"}, | 25 | {102, nullptr, "GetStandardUserSystemClockInitialYear"}, |
| 22 | {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, | 26 | {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, |
| 27 | {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, | ||
| 23 | {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, | 28 | {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, |
| 24 | {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, | 29 | {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, |
| 25 | {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, | 30 | {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, |
| 26 | {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, | 31 | {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, |
| 27 | "CalculateStandardUserSystemClockDifferenceByUser"}, | ||
| 28 | {501, nullptr, "CalculateSpanBetween"}, | 32 | {501, nullptr, "CalculateSpanBetween"}, |
| 29 | }; | 33 | }; |
| 34 | // clang-format on | ||
| 35 | |||
| 30 | RegisterHandlers(functions); | 36 | RegisterHandlers(functions); |
| 31 | } | 37 | } |
| 32 | 38 | ||
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 566cd6006..4e17249a9 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -498,7 +498,6 @@ public: | |||
| 498 | }; | 498 | }; |
| 499 | RegisterHandlers(functions); | 499 | RegisterHandlers(functions); |
| 500 | } | 500 | } |
| 501 | ~IHOSBinderDriver() = default; | ||
| 502 | 501 | ||
| 503 | private: | 502 | private: |
| 504 | enum class TransactionId { | 503 | enum class TransactionId { |
| @@ -692,7 +691,6 @@ public: | |||
| 692 | }; | 691 | }; |
| 693 | RegisterHandlers(functions); | 692 | RegisterHandlers(functions); |
| 694 | } | 693 | } |
| 695 | ~ISystemDisplayService() = default; | ||
| 696 | 694 | ||
| 697 | private: | 695 | private: |
| 698 | void SetLayerZ(Kernel::HLERequestContext& ctx) { | 696 | void SetLayerZ(Kernel::HLERequestContext& ctx) { |
| @@ -818,7 +816,6 @@ public: | |||
| 818 | }; | 816 | }; |
| 819 | RegisterHandlers(functions); | 817 | RegisterHandlers(functions); |
| 820 | } | 818 | } |
| 821 | ~IManagerDisplayService() = default; | ||
| 822 | 819 | ||
| 823 | private: | 820 | private: |
| 824 | void CloseDisplay(Kernel::HLERequestContext& ctx) { | 821 | void CloseDisplay(Kernel::HLERequestContext& ctx) { |
| @@ -884,7 +881,6 @@ private: | |||
| 884 | class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { | 881 | class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { |
| 885 | public: | 882 | public: |
| 886 | explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); | 883 | explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); |
| 887 | ~IApplicationDisplayService() = default; | ||
| 888 | 884 | ||
| 889 | private: | 885 | private: |
| 890 | enum class ConvertedScaleMode : u64 { | 886 | enum class ConvertedScaleMode : u64 { |
| @@ -1037,7 +1033,6 @@ private: | |||
| 1037 | void ListDisplays(Kernel::HLERequestContext& ctx) { | 1033 | void ListDisplays(Kernel::HLERequestContext& ctx) { |
| 1038 | LOG_WARNING(Service_VI, "(STUBBED) called"); | 1034 | LOG_WARNING(Service_VI, "(STUBBED) called"); |
| 1039 | 1035 | ||
| 1040 | IPC::RequestParser rp{ctx}; | ||
| 1041 | DisplayInfo display_info; | 1036 | DisplayInfo display_info; |
| 1042 | display_info.width *= static_cast<u64>(Settings::values.resolution_factor); | 1037 | display_info.width *= static_cast<u64>(Settings::values.resolution_factor); |
| 1043 | display_info.height *= static_cast<u64>(Settings::values.resolution_factor); | 1038 | display_info.height *= static_cast<u64>(Settings::values.resolution_factor); |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index babc7e646..d7c47c197 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -4,11 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | #include <lz4.h> | 7 | |
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 10 | #include "common/hex_util.h" | 10 | #include "common/hex_util.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/lz4_compression.h" | ||
| 12 | #include "common/swap.h" | 13 | #include "common/swap.h" |
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/file_sys/patch_manager.h" | 15 | #include "core/file_sys/patch_manager.h" |
| @@ -20,6 +21,8 @@ | |||
| 20 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 21 | #include "core/settings.h" | 22 | #include "core/settings.h" |
| 22 | 23 | ||
| 24 | #pragma optimize("", off) | ||
| 25 | |||
| 23 | namespace Loader { | 26 | namespace Loader { |
| 24 | namespace { | 27 | namespace { |
| 25 | struct MODHeader { | 28 | struct MODHeader { |
| @@ -35,15 +38,11 @@ static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); | |||
| 35 | 38 | ||
| 36 | std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, | 39 | std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, |
| 37 | const NSOSegmentHeader& header) { | 40 | const NSOSegmentHeader& header) { |
| 38 | std::vector<u8> uncompressed_data(header.size); | 41 | const std::vector<u8> uncompressed_data = |
| 39 | const int bytes_uncompressed = | 42 | Common::Compression::DecompressDataLZ4(compressed_data, header.size); |
| 40 | LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), | ||
| 41 | reinterpret_cast<char*>(uncompressed_data.data()), | ||
| 42 | static_cast<int>(compressed_data.size()), header.size); | ||
| 43 | 43 | ||
| 44 | ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) && | 44 | ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size, |
| 45 | bytes_uncompressed == static_cast<int>(uncompressed_data.size()), | 45 | uncompressed_data.size()); |
| 46 | "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size()); | ||
| 47 | 46 | ||
| 48 | return uncompressed_data; | 47 | return uncompressed_data; |
| 49 | } | 48 | } |
| @@ -139,13 +138,13 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 139 | 138 | ||
| 140 | // Apply patches if necessary | 139 | // Apply patches if necessary |
| 141 | if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { | 140 | if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { |
| 142 | std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); | 141 | std::vector<u8> pi_header; |
| 143 | pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), | 142 | pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), |
| 144 | reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); | 143 | reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); |
| 145 | pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(), | 144 | pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(), |
| 146 | program_image.end()); | 145 | program_image.end()); |
| 147 | 146 | ||
| 148 | pi_header = pm->PatchNSO(pi_header); | 147 | pi_header = pm->PatchNSO(pi_header, file.GetName()); |
| 149 | 148 | ||
| 150 | std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin()); | 149 | std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin()); |
| 151 | } | 150 | } |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index d6995b61e..436f7387c 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -22,7 +22,7 @@ class AppLoader_NCA; | |||
| 22 | class AppLoader_XCI final : public AppLoader { | 22 | class AppLoader_XCI final : public AppLoader { |
| 23 | public: | 23 | public: |
| 24 | explicit AppLoader_XCI(FileSys::VirtualFile file); | 24 | explicit AppLoader_XCI(FileSys::VirtualFile file); |
| 25 | ~AppLoader_XCI(); | 25 | ~AppLoader_XCI() override; |
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 28 | * Returns the type of the file | 28 | * Returns the type of the file |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 332c1037c..4e0538bc2 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -38,10 +38,6 @@ void SetCurrentPageTable(Common::PageTable* page_table) { | |||
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | Common::PageTable* GetCurrentPageTable() { | ||
| 42 | return current_page_table; | ||
| 43 | } | ||
| 44 | |||
| 45 | static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, | 41 | static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, |
| 46 | Common::PageType type) { | 42 | Common::PageType type) { |
| 47 | LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, | 43 | LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, |
diff --git a/src/core/memory.h b/src/core/memory.h index 1d38cdca8..6845f5fe1 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -28,16 +28,6 @@ constexpr u64 PAGE_MASK = PAGE_SIZE - 1; | |||
| 28 | 28 | ||
| 29 | /// Virtual user-space memory regions | 29 | /// Virtual user-space memory regions |
| 30 | enum : VAddr { | 30 | enum : VAddr { |
| 31 | /// Read-only page containing kernel and system configuration values. | ||
| 32 | CONFIG_MEMORY_VADDR = 0x1FF80000, | ||
| 33 | CONFIG_MEMORY_SIZE = 0x00001000, | ||
| 34 | CONFIG_MEMORY_VADDR_END = CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE, | ||
| 35 | |||
| 36 | /// Usually read-only page containing mostly values read from hardware. | ||
| 37 | SHARED_PAGE_VADDR = 0x1FF81000, | ||
| 38 | SHARED_PAGE_SIZE = 0x00001000, | ||
| 39 | SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, | ||
| 40 | |||
| 41 | /// TLS (Thread-Local Storage) related. | 31 | /// TLS (Thread-Local Storage) related. |
| 42 | TLS_ENTRY_SIZE = 0x200, | 32 | TLS_ENTRY_SIZE = 0x200, |
| 43 | 33 | ||
| @@ -50,9 +40,8 @@ enum : VAddr { | |||
| 50 | KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, | 40 | KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, |
| 51 | }; | 41 | }; |
| 52 | 42 | ||
| 53 | /// Currently active page table | 43 | /// Changes the currently active page table. |
| 54 | void SetCurrentPageTable(Common::PageTable* page_table); | 44 | void SetCurrentPageTable(Common::PageTable* page_table); |
| 55 | Common::PageTable* GetCurrentPageTable(); | ||
| 56 | 45 | ||
| 57 | /// Determines if the given VAddr is valid for the specified process. | 46 | /// Determines if the given VAddr is valid for the specified process. |
| 58 | bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); | 47 | bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); |
diff --git a/src/core/settings.h b/src/core/settings.h index d543eb32f..b84390745 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -393,6 +393,7 @@ struct Values { | |||
| 393 | bool use_disk_shader_cache; | 393 | bool use_disk_shader_cache; |
| 394 | bool use_accurate_gpu_emulation; | 394 | bool use_accurate_gpu_emulation; |
| 395 | bool use_asynchronous_gpu_emulation; | 395 | bool use_asynchronous_gpu_emulation; |
| 396 | bool force_30fps_mode; | ||
| 396 | 397 | ||
| 397 | float bg_red; | 398 | float bg_red; |
| 398 | float bg_green; | 399 | float bg_green; |
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 3e1a735c3..58af41f6e 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp | |||
| @@ -17,7 +17,6 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) | |||
| 17 | : mutable_memory(mutable_memory_), | 17 | : mutable_memory(mutable_memory_), |
| 18 | test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} { | 18 | test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} { |
| 19 | auto process = Kernel::Process::Create(Core::System::GetInstance(), ""); | 19 | auto process = Kernel::Process::Create(Core::System::GetInstance(), ""); |
| 20 | kernel.MakeCurrentProcess(process.get()); | ||
| 21 | page_table = &process->VMManager().page_table; | 20 | page_table = &process->VMManager().page_table; |
| 22 | 21 | ||
| 23 | std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); | 22 | std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); |
| @@ -28,7 +27,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) | |||
| 28 | Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); | 27 | Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); |
| 29 | Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); | 28 | Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); |
| 30 | 29 | ||
| 31 | Memory::SetCurrentPageTable(page_table); | 30 | kernel.MakeCurrentProcess(process.get()); |
| 32 | } | 31 | } |
| 33 | 32 | ||
| 34 | TestEnvironment::~TestEnvironment() { | 33 | TestEnvironment::~TestEnvironment() { |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 44c761d3e..114bed20d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -106,6 +106,8 @@ add_library(video_core STATIC | |||
| 106 | textures/decoders.cpp | 106 | textures/decoders.cpp |
| 107 | textures/decoders.h | 107 | textures/decoders.h |
| 108 | textures/texture.h | 108 | textures/texture.h |
| 109 | texture_cache.cpp | ||
| 110 | texture_cache.h | ||
| 109 | video_core.cpp | 111 | video_core.cpp |
| 110 | video_core.h | 112 | video_core.h |
| 111 | ) | 113 | ) |
| @@ -127,16 +129,21 @@ if (ENABLE_VULKAN) | |||
| 127 | renderer_vulkan/vk_sampler_cache.h | 129 | renderer_vulkan/vk_sampler_cache.h |
| 128 | renderer_vulkan/vk_scheduler.cpp | 130 | renderer_vulkan/vk_scheduler.cpp |
| 129 | renderer_vulkan/vk_scheduler.h | 131 | renderer_vulkan/vk_scheduler.h |
| 132 | renderer_vulkan/vk_shader_decompiler.cpp | ||
| 133 | renderer_vulkan/vk_shader_decompiler.h | ||
| 130 | renderer_vulkan/vk_stream_buffer.cpp | 134 | renderer_vulkan/vk_stream_buffer.cpp |
| 131 | renderer_vulkan/vk_stream_buffer.h | 135 | renderer_vulkan/vk_stream_buffer.h |
| 132 | renderer_vulkan/vk_swapchain.cpp | 136 | renderer_vulkan/vk_swapchain.cpp |
| 133 | renderer_vulkan/vk_swapchain.h) | 137 | renderer_vulkan/vk_swapchain.h) |
| 134 | 138 | ||
| 135 | target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) | 139 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) |
| 136 | target_compile_definitions(video_core PRIVATE HAS_VULKAN) | 140 | target_compile_definitions(video_core PRIVATE HAS_VULKAN) |
| 137 | endif() | 141 | endif() |
| 138 | 142 | ||
| 139 | create_target_directory_groups(video_core) | 143 | create_target_directory_groups(video_core) |
| 140 | 144 | ||
| 141 | target_link_libraries(video_core PUBLIC common core) | 145 | target_link_libraries(video_core PUBLIC common core) |
| 142 | target_link_libraries(video_core PRIVATE glad lz4_static) | 146 | target_link_libraries(video_core PRIVATE glad) |
| 147 | if (ENABLE_VULKAN) | ||
| 148 | target_link_libraries(video_core PRIVATE sirit) | ||
| 149 | endif() | ||
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 8b1bea1ae..046d047cb 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "video_core/dma_pusher.h" | 8 | #include "video_core/dma_pusher.h" |
| 9 | #include "video_core/engines/maxwell_3d.h" | 9 | #include "video_core/engines/maxwell_3d.h" |
| 10 | #include "video_core/gpu.h" | 10 | #include "video_core/gpu.h" |
| 11 | #include "video_core/memory_manager.h" | ||
| 11 | 12 | ||
| 12 | namespace Tegra { | 13 | namespace Tegra { |
| 13 | 14 | ||
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 03b7ee5d8..55966eef1 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp | |||
| @@ -6,12 +6,13 @@ | |||
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "common/math_util.h" | 7 | #include "common/math_util.h" |
| 8 | #include "video_core/engines/fermi_2d.h" | 8 | #include "video_core/engines/fermi_2d.h" |
| 9 | #include "video_core/memory_manager.h" | ||
| 9 | #include "video_core/rasterizer_interface.h" | 10 | #include "video_core/rasterizer_interface.h" |
| 10 | 11 | ||
| 11 | namespace Tegra::Engines { | 12 | namespace Tegra::Engines { |
| 12 | 13 | ||
| 13 | Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) | 14 | Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) |
| 14 | : memory_manager(memory_manager), rasterizer{rasterizer} {} | 15 | : rasterizer{rasterizer}, memory_manager{memory_manager} {} |
| 15 | 16 | ||
| 16 | void Fermi2D::CallMethod(const GPU::MethodCall& method_call) { | 17 | void Fermi2D::CallMethod(const GPU::MethodCall& method_call) { |
| 17 | ASSERT_MSG(method_call.method < Regs::NUM_REGS, | 18 | ASSERT_MSG(method_call.method < Regs::NUM_REGS, |
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 80523e320..2e51b7f13 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -10,7 +10,10 @@ | |||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 13 | #include "video_core/memory_manager.h" | 13 | |
| 14 | namespace Tegra { | ||
| 15 | class MemoryManager; | ||
| 16 | } | ||
| 14 | 17 | ||
| 15 | namespace VideoCore { | 18 | namespace VideoCore { |
| 16 | class RasterizerInterface; | 19 | class RasterizerInterface; |
| @@ -115,10 +118,9 @@ public: | |||
| 115 | }; | 118 | }; |
| 116 | } regs{}; | 119 | } regs{}; |
| 117 | 120 | ||
| 118 | MemoryManager& memory_manager; | ||
| 119 | |||
| 120 | private: | 121 | private: |
| 121 | VideoCore::RasterizerInterface& rasterizer; | 122 | VideoCore::RasterizerInterface& rasterizer; |
| 123 | MemoryManager& memory_manager; | ||
| 122 | 124 | ||
| 123 | /// Performs the copy from the source surface to the destination surface as configured in the | 125 | /// Performs the copy from the source surface to the destination surface as configured in the |
| 124 | /// registers. | 126 | /// registers. |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 6575afd0f..fb6cdf432 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -9,7 +9,10 @@ | |||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/gpu.h" | 11 | #include "video_core/gpu.h" |
| 12 | #include "video_core/memory_manager.h" | 12 | |
| 13 | namespace Tegra { | ||
| 14 | class MemoryManager; | ||
| 15 | } | ||
| 13 | 16 | ||
| 14 | namespace Tegra::Engines { | 17 | namespace Tegra::Engines { |
| 15 | 18 | ||
| @@ -40,10 +43,11 @@ public: | |||
| 40 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), | 43 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), |
| 41 | "KeplerCompute Regs has wrong size"); | 44 | "KeplerCompute Regs has wrong size"); |
| 42 | 45 | ||
| 43 | MemoryManager& memory_manager; | ||
| 44 | |||
| 45 | /// Write the value to the register identified by method. | 46 | /// Write the value to the register identified by method. |
| 46 | void CallMethod(const GPU::MethodCall& method_call); | 47 | void CallMethod(const GPU::MethodCall& method_call); |
| 48 | |||
| 49 | private: | ||
| 50 | MemoryManager& memory_manager; | ||
| 47 | }; | 51 | }; |
| 48 | 52 | ||
| 49 | #define ASSERT_REG_POSITION(field_name, position) \ | 53 | #define ASSERT_REG_POSITION(field_name, position) \ |
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index e259bf46b..cd51a31d7 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -5,9 +5,9 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/memory.h" | ||
| 9 | #include "video_core/engines/kepler_memory.h" | 8 | #include "video_core/engines/kepler_memory.h" |
| 10 | #include "video_core/engines/maxwell_3d.h" | 9 | #include "video_core/engines/maxwell_3d.h" |
| 10 | #include "video_core/memory_manager.h" | ||
| 11 | #include "video_core/rasterizer_interface.h" | 11 | #include "video_core/rasterizer_interface.h" |
| 12 | #include "video_core/renderer_base.h" | 12 | #include "video_core/renderer_base.h" |
| 13 | 13 | ||
| @@ -15,7 +15,7 @@ namespace Tegra::Engines { | |||
| 15 | 15 | ||
| 16 | KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 16 | KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 17 | MemoryManager& memory_manager) | 17 | MemoryManager& memory_manager) |
| 18 | : system{system}, memory_manager(memory_manager), rasterizer{rasterizer} {} | 18 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {} |
| 19 | 19 | ||
| 20 | KeplerMemory::~KeplerMemory() = default; | 20 | KeplerMemory::~KeplerMemory() = default; |
| 21 | 21 | ||
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 9181e9d80..78b6c3e45 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -10,12 +10,15 @@ | |||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 13 | #include "video_core/memory_manager.h" | ||
| 14 | 13 | ||
| 15 | namespace Core { | 14 | namespace Core { |
| 16 | class System; | 15 | class System; |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 18 | namespace Tegra { | ||
| 19 | class MemoryManager; | ||
| 20 | } | ||
| 21 | |||
| 19 | namespace VideoCore { | 22 | namespace VideoCore { |
| 20 | class RasterizerInterface; | 23 | class RasterizerInterface; |
| 21 | } | 24 | } |
| @@ -82,8 +85,8 @@ public: | |||
| 82 | 85 | ||
| 83 | private: | 86 | private: |
| 84 | Core::System& system; | 87 | Core::System& system; |
| 85 | MemoryManager& memory_manager; | ||
| 86 | VideoCore::RasterizerInterface& rasterizer; | 88 | VideoCore::RasterizerInterface& rasterizer; |
| 89 | MemoryManager& memory_manager; | ||
| 87 | 90 | ||
| 88 | void ProcessData(u32 data); | 91 | void ProcessData(u32 data); |
| 89 | }; | 92 | }; |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index defcfbd3f..74403eed4 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -7,11 +7,10 @@ | |||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 10 | #include "core/memory.h" | ||
| 11 | #include "video_core/debug_utils/debug_utils.h" | 10 | #include "video_core/debug_utils/debug_utils.h" |
| 12 | #include "video_core/engines/maxwell_3d.h" | 11 | #include "video_core/engines/maxwell_3d.h" |
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | #include "video_core/rasterizer_interface.h" | 13 | #include "video_core/rasterizer_interface.h" |
| 14 | #include "video_core/renderer_base.h" | ||
| 15 | #include "video_core/textures/texture.h" | 14 | #include "video_core/textures/texture.h" |
| 16 | 15 | ||
| 17 | namespace Tegra::Engines { | 16 | namespace Tegra::Engines { |
| @@ -21,8 +20,8 @@ constexpr u32 MacroRegistersStart = 0xE00; | |||
| 21 | 20 | ||
| 22 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 21 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 23 | MemoryManager& memory_manager) | 22 | MemoryManager& memory_manager) |
| 24 | : memory_manager(memory_manager), system{system}, rasterizer{rasterizer}, | 23 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, macro_interpreter{ |
| 25 | macro_interpreter(*this) { | 24 | *this} { |
| 26 | InitializeRegisterDefaults(); | 25 | InitializeRegisterDefaults(); |
| 27 | } | 26 | } |
| 28 | 27 | ||
| @@ -250,6 +249,10 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 250 | ProcessQueryGet(); | 249 | ProcessQueryGet(); |
| 251 | break; | 250 | break; |
| 252 | } | 251 | } |
| 252 | case MAXWELL3D_REG_INDEX(sync_info): { | ||
| 253 | ProcessSyncPoint(); | ||
| 254 | break; | ||
| 255 | } | ||
| 253 | default: | 256 | default: |
| 254 | break; | 257 | break; |
| 255 | } | 258 | } |
| @@ -327,6 +330,14 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 327 | } | 330 | } |
| 328 | } | 331 | } |
| 329 | 332 | ||
| 333 | void Maxwell3D::ProcessSyncPoint() { | ||
| 334 | const u32 sync_point = regs.sync_info.sync_point.Value(); | ||
| 335 | const u32 increment = regs.sync_info.increment.Value(); | ||
| 336 | const u32 cache_flush = regs.sync_info.unknown.Value(); | ||
| 337 | LOG_DEBUG(HW_GPU, "Syncpoint set {}, increment: {}, unk: {}", sync_point, increment, | ||
| 338 | cache_flush); | ||
| 339 | } | ||
| 340 | |||
| 330 | void Maxwell3D::DrawArrays() { | 341 | void Maxwell3D::DrawArrays() { |
| 331 | LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), | 342 | LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), |
| 332 | regs.vertex_buffer.count); | 343 | regs.vertex_buffer.count); |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 7fbf1026e..321af3297 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -16,13 +16,16 @@ | |||
| 16 | #include "common/math_util.h" | 16 | #include "common/math_util.h" |
| 17 | #include "video_core/gpu.h" | 17 | #include "video_core/gpu.h" |
| 18 | #include "video_core/macro_interpreter.h" | 18 | #include "video_core/macro_interpreter.h" |
| 19 | #include "video_core/memory_manager.h" | ||
| 20 | #include "video_core/textures/texture.h" | 19 | #include "video_core/textures/texture.h" |
| 21 | 20 | ||
| 22 | namespace Core { | 21 | namespace Core { |
| 23 | class System; | 22 | class System; |
| 24 | } | 23 | } |
| 25 | 24 | ||
| 25 | namespace Tegra { | ||
| 26 | class MemoryManager; | ||
| 27 | } | ||
| 28 | |||
| 26 | namespace VideoCore { | 29 | namespace VideoCore { |
| 27 | class RasterizerInterface; | 30 | class RasterizerInterface; |
| 28 | } | 31 | } |
| @@ -576,7 +579,17 @@ public: | |||
| 576 | u32 bind; | 579 | u32 bind; |
| 577 | } macros; | 580 | } macros; |
| 578 | 581 | ||
| 579 | INSERT_PADDING_WORDS(0x188); | 582 | INSERT_PADDING_WORDS(0x69); |
| 583 | |||
| 584 | struct { | ||
| 585 | union { | ||
| 586 | BitField<0, 16, u32> sync_point; | ||
| 587 | BitField<16, 1, u32> unknown; | ||
| 588 | BitField<20, 1, u32> increment; | ||
| 589 | }; | ||
| 590 | } sync_info; | ||
| 591 | |||
| 592 | INSERT_PADDING_WORDS(0x11E); | ||
| 580 | 593 | ||
| 581 | u32 tfb_enabled; | 594 | u32 tfb_enabled; |
| 582 | 595 | ||
| @@ -1093,7 +1106,6 @@ public: | |||
| 1093 | }; | 1106 | }; |
| 1094 | 1107 | ||
| 1095 | State state{}; | 1108 | State state{}; |
| 1096 | MemoryManager& memory_manager; | ||
| 1097 | 1109 | ||
| 1098 | struct DirtyFlags { | 1110 | struct DirtyFlags { |
| 1099 | std::bitset<8> color_buffer{0xFF}; | 1111 | std::bitset<8> color_buffer{0xFF}; |
| @@ -1141,6 +1153,8 @@ private: | |||
| 1141 | 1153 | ||
| 1142 | VideoCore::RasterizerInterface& rasterizer; | 1154 | VideoCore::RasterizerInterface& rasterizer; |
| 1143 | 1155 | ||
| 1156 | MemoryManager& memory_manager; | ||
| 1157 | |||
| 1144 | /// Start offsets of each macro in macro_memory | 1158 | /// Start offsets of each macro in macro_memory |
| 1145 | std::unordered_map<u32, u32> macro_offsets; | 1159 | std::unordered_map<u32, u32> macro_offsets; |
| 1146 | 1160 | ||
| @@ -1180,6 +1194,9 @@ private: | |||
| 1180 | /// Handles a write to the QUERY_GET register. | 1194 | /// Handles a write to the QUERY_GET register. |
| 1181 | void ProcessQueryGet(); | 1195 | void ProcessQueryGet(); |
| 1182 | 1196 | ||
| 1197 | /// Handles writes to syncing register. | ||
| 1198 | void ProcessSyncPoint(); | ||
| 1199 | |||
| 1183 | /// Handles a write to the CB_DATA[i] register. | 1200 | /// Handles a write to the CB_DATA[i] register. |
| 1184 | void ProcessCBData(u32 value); | 1201 | void ProcessCBData(u32 value); |
| 1185 | 1202 | ||
| @@ -1195,6 +1212,7 @@ private: | |||
| 1195 | "Field " #field_name " has invalid position") | 1212 | "Field " #field_name " has invalid position") |
| 1196 | 1213 | ||
| 1197 | ASSERT_REG_POSITION(macros, 0x45); | 1214 | ASSERT_REG_POSITION(macros, 0x45); |
| 1215 | ASSERT_REG_POSITION(sync_info, 0xB2); | ||
| 1198 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); | 1216 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); |
| 1199 | ASSERT_REG_POSITION(rt, 0x200); | 1217 | ASSERT_REG_POSITION(rt, 0x200); |
| 1200 | ASSERT_REG_POSITION(viewport_transform, 0x280); | 1218 | ASSERT_REG_POSITION(viewport_transform, 0x280); |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 5cca5c29a..2426d0067 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -5,9 +5,9 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/memory.h" | ||
| 9 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 10 | #include "video_core/engines/maxwell_dma.h" | 9 | #include "video_core/engines/maxwell_dma.h" |
| 10 | #include "video_core/memory_manager.h" | ||
| 11 | #include "video_core/rasterizer_interface.h" | 11 | #include "video_core/rasterizer_interface.h" |
| 12 | #include "video_core/renderer_base.h" | 12 | #include "video_core/renderer_base.h" |
| 13 | #include "video_core/textures/decoders.h" | 13 | #include "video_core/textures/decoders.h" |
| @@ -16,7 +16,7 @@ namespace Tegra::Engines { | |||
| 16 | 16 | ||
| 17 | MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 17 | MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 18 | MemoryManager& memory_manager) | 18 | MemoryManager& memory_manager) |
| 19 | : memory_manager(memory_manager), system{system}, rasterizer{rasterizer} {} | 19 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {} |
| 20 | 20 | ||
| 21 | void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { | 21 | void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { |
| 22 | ASSERT_MSG(method_call.method < Regs::NUM_REGS, | 22 | ASSERT_MSG(method_call.method < Regs::NUM_REGS, |
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 34c369320..c6b649842 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -10,12 +10,15 @@ | |||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 13 | #include "video_core/memory_manager.h" | ||
| 14 | 13 | ||
| 15 | namespace Core { | 14 | namespace Core { |
| 16 | class System; | 15 | class System; |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 18 | namespace Tegra { | ||
| 19 | class MemoryManager; | ||
| 20 | } | ||
| 21 | |||
| 19 | namespace VideoCore { | 22 | namespace VideoCore { |
| 20 | class RasterizerInterface; | 23 | class RasterizerInterface; |
| 21 | } | 24 | } |
| @@ -139,13 +142,13 @@ public: | |||
| 139 | }; | 142 | }; |
| 140 | } regs{}; | 143 | } regs{}; |
| 141 | 144 | ||
| 142 | MemoryManager& memory_manager; | ||
| 143 | |||
| 144 | private: | 145 | private: |
| 145 | Core::System& system; | 146 | Core::System& system; |
| 146 | 147 | ||
| 147 | VideoCore::RasterizerInterface& rasterizer; | 148 | VideoCore::RasterizerInterface& rasterizer; |
| 148 | 149 | ||
| 150 | MemoryManager& memory_manager; | ||
| 151 | |||
| 149 | /// Performs the copy from the source buffer to the destination buffer as configured in the | 152 | /// Performs the copy from the source buffer to the destination buffer as configured in the |
| 150 | /// registers. | 153 | /// registers. |
| 151 | void HandleCopy(); | 154 | void HandleCopy(); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index c41e3f4f0..38db4addd 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -806,6 +806,12 @@ union Instruction { | |||
| 806 | } ldg; | 806 | } ldg; |
| 807 | 807 | ||
| 808 | union { | 808 | union { |
| 809 | BitField<48, 3, UniformType> type; | ||
| 810 | BitField<46, 2, u64> cache_mode; | ||
| 811 | BitField<20, 24, s64> immediate_offset; | ||
| 812 | } stg; | ||
| 813 | |||
| 814 | union { | ||
| 809 | BitField<0, 3, u64> pred0; | 815 | BitField<0, 3, u64> pred0; |
| 810 | BitField<3, 3, u64> pred3; | 816 | BitField<3, 3, u64> pred3; |
| 811 | BitField<7, 1, u64> abs_a; | 817 | BitField<7, 1, u64> abs_a; |
| @@ -1252,13 +1258,16 @@ union Instruction { | |||
| 1252 | 1258 | ||
| 1253 | union { | 1259 | union { |
| 1254 | BitField<20, 16, u64> imm20_16; | 1260 | BitField<20, 16, u64> imm20_16; |
| 1261 | BitField<35, 1, u64> high_b_rr; // used on RR | ||
| 1255 | BitField<36, 1, u64> product_shift_left; | 1262 | BitField<36, 1, u64> product_shift_left; |
| 1256 | BitField<37, 1, u64> merge_37; | 1263 | BitField<37, 1, u64> merge_37; |
| 1257 | BitField<48, 1, u64> sign_a; | 1264 | BitField<48, 1, u64> sign_a; |
| 1258 | BitField<49, 1, u64> sign_b; | 1265 | BitField<49, 1, u64> sign_b; |
| 1266 | BitField<50, 2, XmadMode> mode_cbf; // used by CR, RC | ||
| 1259 | BitField<50, 3, XmadMode> mode; | 1267 | BitField<50, 3, XmadMode> mode; |
| 1260 | BitField<52, 1, u64> high_b; | 1268 | BitField<52, 1, u64> high_b; |
| 1261 | BitField<53, 1, u64> high_a; | 1269 | BitField<53, 1, u64> high_a; |
| 1270 | BitField<55, 1, u64> product_shift_left_second; // used on CR | ||
| 1262 | BitField<56, 1, u64> merge_56; | 1271 | BitField<56, 1, u64> merge_56; |
| 1263 | } xmad; | 1272 | } xmad; |
| 1264 | 1273 | ||
| @@ -1676,7 +1685,7 @@ private: | |||
| 1676 | INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), | 1685 | INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), |
| 1677 | INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), | 1686 | INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), |
| 1678 | INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), | 1687 | INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), |
| 1679 | INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"), | 1688 | INST("0011100-01000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"), |
| 1680 | INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"), | 1689 | INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"), |
| 1681 | INST("0000001---------", Id::LOP3_C, Type::ArithmeticInteger, "LOP3_C"), | 1690 | INST("0000001---------", Id::LOP3_C, Type::ArithmeticInteger, "LOP3_C"), |
| 1682 | INST("0101101111100---", Id::LOP3_R, Type::ArithmeticInteger, "LOP3_R"), | 1691 | INST("0101101111100---", Id::LOP3_R, Type::ArithmeticInteger, "LOP3_R"), |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 30b29e14d..4461083ff 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -31,7 +31,7 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) { | |||
| 31 | 31 | ||
| 32 | GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} { | 32 | GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} { |
| 33 | auto& rasterizer{renderer.Rasterizer()}; | 33 | auto& rasterizer{renderer.Rasterizer()}; |
| 34 | memory_manager = std::make_unique<Tegra::MemoryManager>(); | 34 | memory_manager = std::make_unique<Tegra::MemoryManager>(rasterizer); |
| 35 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); | 35 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); |
| 36 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); | 36 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); |
| 37 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); | 37 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); |
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 8b355cf7b..db507cf04 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | namespace VideoCommon { | 9 | namespace VideoCommon { |
| 10 | 10 | ||
| 11 | GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) | 11 | GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) |
| 12 | : Tegra::GPU(system, renderer), gpu_thread{renderer, *dma_pusher} {} | 12 | : Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {} |
| 13 | 13 | ||
| 14 | GPUAsynch::~GPUAsynch() = default; | 14 | GPUAsynch::~GPUAsynch() = default; |
| 15 | 15 | ||
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index c5dc199c5..cc56cf467 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "core/core.h" | ||
| 8 | #include "core/core_timing.h" | ||
| 9 | #include "core/core_timing_util.h" | ||
| 7 | #include "core/frontend/scope_acquire_window_context.h" | 10 | #include "core/frontend/scope_acquire_window_context.h" |
| 8 | #include "video_core/dma_pusher.h" | 11 | #include "video_core/dma_pusher.h" |
| 9 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| @@ -36,7 +39,6 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p | |||
| 36 | dma_pusher.Push(std::move(submit_list->entries)); | 39 | dma_pusher.Push(std::move(submit_list->entries)); |
| 37 | dma_pusher.DispatchCalls(); | 40 | dma_pusher.DispatchCalls(); |
| 38 | } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { | 41 | } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { |
| 39 | state.DecrementFramesCounter(); | ||
| 40 | renderer.SwapBuffers(std::move(data->framebuffer)); | 42 | renderer.SwapBuffers(std::move(data->framebuffer)); |
| 41 | } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { | 43 | } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { |
| 42 | renderer.Rasterizer().FlushRegion(data->addr, data->size); | 44 | renderer.Rasterizer().FlushRegion(data->addr, data->size); |
| @@ -47,13 +49,18 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p | |||
| 47 | } else { | 49 | } else { |
| 48 | UNREACHABLE(); | 50 | UNREACHABLE(); |
| 49 | } | 51 | } |
| 52 | state.signaled_fence = next.fence; | ||
| 53 | state.TrySynchronize(); | ||
| 50 | } | 54 | } |
| 51 | } | 55 | } |
| 52 | } | 56 | } |
| 53 | 57 | ||
| 54 | ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) | 58 | ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, |
| 55 | : renderer{renderer}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), | 59 | Tegra::DmaPusher& dma_pusher) |
| 56 | std::ref(state)} {} | 60 | : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} { |
| 61 | synchronization_event = system.CoreTiming().RegisterEvent( | ||
| 62 | "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); }); | ||
| 63 | } | ||
| 57 | 64 | ||
| 58 | ThreadManager::~ThreadManager() { | 65 | ThreadManager::~ThreadManager() { |
| 59 | // Notify GPU thread that a shutdown is pending | 66 | // Notify GPU thread that a shutdown is pending |
| @@ -62,14 +69,14 @@ ThreadManager::~ThreadManager() { | |||
| 62 | } | 69 | } |
| 63 | 70 | ||
| 64 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { | 71 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { |
| 65 | PushCommand(SubmitListCommand(std::move(entries))); | 72 | const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))}; |
| 73 | const s64 synchronization_ticks{Core::Timing::usToCycles(9000)}; | ||
| 74 | system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence); | ||
| 66 | } | 75 | } |
| 67 | 76 | ||
| 68 | void ThreadManager::SwapBuffers( | 77 | void ThreadManager::SwapBuffers( |
| 69 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { | 78 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { |
| 70 | state.IncrementFramesCounter(); | ||
| 71 | PushCommand(SwapBuffersCommand(std::move(framebuffer))); | 79 | PushCommand(SwapBuffersCommand(std::move(framebuffer))); |
| 72 | state.WaitForFrames(); | ||
| 73 | } | 80 | } |
| 74 | 81 | ||
| 75 | void ThreadManager::FlushRegion(CacheAddr addr, u64 size) { | 82 | void ThreadManager::FlushRegion(CacheAddr addr, u64 size) { |
| @@ -79,7 +86,7 @@ void ThreadManager::FlushRegion(CacheAddr addr, u64 size) { | |||
| 79 | void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) { | 86 | void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) { |
| 80 | if (state.queue.Empty()) { | 87 | if (state.queue.Empty()) { |
| 81 | // It's quicker to invalidate a single region on the CPU if the queue is already empty | 88 | // It's quicker to invalidate a single region on the CPU if the queue is already empty |
| 82 | renderer.Rasterizer().InvalidateRegion(addr, size); | 89 | system.Renderer().Rasterizer().InvalidateRegion(addr, size); |
| 83 | } else { | 90 | } else { |
| 84 | PushCommand(InvalidateRegionCommand(addr, size)); | 91 | PushCommand(InvalidateRegionCommand(addr, size)); |
| 85 | } | 92 | } |
| @@ -90,9 +97,25 @@ void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | |||
| 90 | InvalidateRegion(addr, size); | 97 | InvalidateRegion(addr, size); |
| 91 | } | 98 | } |
| 92 | 99 | ||
| 93 | void ThreadManager::PushCommand(CommandData&& command_data) { | 100 | u64 ThreadManager::PushCommand(CommandData&& command_data) { |
| 94 | state.queue.Push(CommandDataContainer(std::move(command_data))); | 101 | const u64 fence{++state.last_fence}; |
| 102 | state.queue.Push(CommandDataContainer(std::move(command_data), fence)); | ||
| 95 | state.SignalCommands(); | 103 | state.SignalCommands(); |
| 104 | return fence; | ||
| 105 | } | ||
| 106 | |||
| 107 | MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); | ||
| 108 | void SynchState::WaitForSynchronization(u64 fence) { | ||
| 109 | if (signaled_fence >= fence) { | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | |||
| 113 | // Wait for the GPU to be idle (all commands to be executed) | ||
| 114 | { | ||
| 115 | MICROPROFILE_SCOPE(GPU_wait); | ||
| 116 | std::unique_lock<std::mutex> lock{synchronization_mutex}; | ||
| 117 | synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; }); | ||
| 118 | } | ||
| 96 | } | 119 | } |
| 97 | 120 | ||
| 98 | } // namespace VideoCommon::GPUThread | 121 | } // namespace VideoCommon::GPUThread |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 70acb2e79..62bcea5bb 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -19,9 +19,12 @@ struct FramebufferConfig; | |||
| 19 | class DmaPusher; | 19 | class DmaPusher; |
| 20 | } // namespace Tegra | 20 | } // namespace Tegra |
| 21 | 21 | ||
| 22 | namespace VideoCore { | 22 | namespace Core { |
| 23 | class RendererBase; | 23 | class System; |
| 24 | } // namespace VideoCore | 24 | namespace Timing { |
| 25 | struct EventType; | ||
| 26 | } // namespace Timing | ||
| 27 | } // namespace Core | ||
| 25 | 28 | ||
| 26 | namespace VideoCommon::GPUThread { | 29 | namespace VideoCommon::GPUThread { |
| 27 | 30 | ||
| @@ -75,63 +78,47 @@ using CommandData = | |||
| 75 | struct CommandDataContainer { | 78 | struct CommandDataContainer { |
| 76 | CommandDataContainer() = default; | 79 | CommandDataContainer() = default; |
| 77 | 80 | ||
| 78 | CommandDataContainer(CommandData&& data) : data{std::move(data)} {} | 81 | CommandDataContainer(CommandData&& data, u64 next_fence) |
| 82 | : data{std::move(data)}, fence{next_fence} {} | ||
| 79 | 83 | ||
| 80 | CommandDataContainer& operator=(const CommandDataContainer& t) { | 84 | CommandDataContainer& operator=(const CommandDataContainer& t) { |
| 81 | data = std::move(t.data); | 85 | data = std::move(t.data); |
| 86 | fence = t.fence; | ||
| 82 | return *this; | 87 | return *this; |
| 83 | } | 88 | } |
| 84 | 89 | ||
| 85 | CommandData data; | 90 | CommandData data; |
| 91 | u64 fence{}; | ||
| 86 | }; | 92 | }; |
| 87 | 93 | ||
| 88 | /// Struct used to synchronize the GPU thread | 94 | /// Struct used to synchronize the GPU thread |
| 89 | struct SynchState final { | 95 | struct SynchState final { |
| 90 | std::atomic_bool is_running{true}; | 96 | std::atomic_bool is_running{true}; |
| 91 | std::atomic_int queued_frame_count{}; | 97 | std::atomic_int queued_frame_count{}; |
| 92 | std::mutex frames_mutex; | 98 | std::mutex synchronization_mutex; |
| 93 | std::mutex commands_mutex; | 99 | std::mutex commands_mutex; |
| 94 | std::condition_variable commands_condition; | 100 | std::condition_variable commands_condition; |
| 95 | std::condition_variable frames_condition; | 101 | std::condition_variable synchronization_condition; |
| 96 | 102 | ||
| 97 | void IncrementFramesCounter() { | 103 | /// Returns true if the gap in GPU commands is small enough that we can consider the CPU and GPU |
| 98 | std::lock_guard lock{frames_mutex}; | 104 | /// synchronized. This is entirely empirical. |
| 99 | ++queued_frame_count; | 105 | bool IsSynchronized() const { |
| 106 | constexpr std::size_t max_queue_gap{5}; | ||
| 107 | return queue.Size() <= max_queue_gap; | ||
| 100 | } | 108 | } |
| 101 | 109 | ||
| 102 | void DecrementFramesCounter() { | 110 | void TrySynchronize() { |
| 103 | { | 111 | if (IsSynchronized()) { |
| 104 | std::lock_guard lock{frames_mutex}; | 112 | std::lock_guard<std::mutex> lock{synchronization_mutex}; |
| 105 | --queued_frame_count; | 113 | synchronization_condition.notify_one(); |
| 106 | |||
| 107 | if (queued_frame_count) { | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | } | 114 | } |
| 111 | frames_condition.notify_one(); | ||
| 112 | } | 115 | } |
| 113 | 116 | ||
| 114 | void WaitForFrames() { | 117 | void WaitForSynchronization(u64 fence); |
| 115 | { | ||
| 116 | std::lock_guard lock{frames_mutex}; | ||
| 117 | if (!queued_frame_count) { | ||
| 118 | return; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | // Wait for the GPU to be idle (all commands to be executed) | ||
| 123 | { | ||
| 124 | std::unique_lock lock{frames_mutex}; | ||
| 125 | frames_condition.wait(lock, [this] { return !queued_frame_count; }); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | 118 | ||
| 129 | void SignalCommands() { | 119 | void SignalCommands() { |
| 130 | { | 120 | if (queue.Empty()) { |
| 131 | std::unique_lock lock{commands_mutex}; | 121 | return; |
| 132 | if (queue.Empty()) { | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | } | 122 | } |
| 136 | 123 | ||
| 137 | commands_condition.notify_one(); | 124 | commands_condition.notify_one(); |
| @@ -144,12 +131,15 @@ struct SynchState final { | |||
| 144 | 131 | ||
| 145 | using CommandQueue = Common::SPSCQueue<CommandDataContainer>; | 132 | using CommandQueue = Common::SPSCQueue<CommandDataContainer>; |
| 146 | CommandQueue queue; | 133 | CommandQueue queue; |
| 134 | u64 last_fence{}; | ||
| 135 | std::atomic<u64> signaled_fence{}; | ||
| 147 | }; | 136 | }; |
| 148 | 137 | ||
| 149 | /// Class used to manage the GPU thread | 138 | /// Class used to manage the GPU thread |
| 150 | class ThreadManager final { | 139 | class ThreadManager final { |
| 151 | public: | 140 | public: |
| 152 | explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); | 141 | explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, |
| 142 | Tegra::DmaPusher& dma_pusher); | ||
| 153 | ~ThreadManager(); | 143 | ~ThreadManager(); |
| 154 | 144 | ||
| 155 | /// Push GPU command entries to be processed | 145 | /// Push GPU command entries to be processed |
| @@ -170,11 +160,12 @@ public: | |||
| 170 | 160 | ||
| 171 | private: | 161 | private: |
| 172 | /// Pushes a command to be executed by the GPU thread | 162 | /// Pushes a command to be executed by the GPU thread |
| 173 | void PushCommand(CommandData&& command_data); | 163 | u64 PushCommand(CommandData&& command_data); |
| 174 | 164 | ||
| 175 | private: | 165 | private: |
| 176 | SynchState state; | 166 | SynchState state; |
| 177 | VideoCore::RendererBase& renderer; | 167 | Core::System& system; |
| 168 | Core::Timing::EventType* synchronization_event{}; | ||
| 178 | std::thread thread; | 169 | std::thread thread; |
| 179 | std::thread::id thread_id; | 170 | std::thread::id thread_id; |
| 180 | }; | 171 | }; |
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 64f75db43..524d9ea5a 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp | |||
| @@ -223,27 +223,21 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res | |||
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | u32 MacroInterpreter::FetchParameter() { | 225 | u32 MacroInterpreter::FetchParameter() { |
| 226 | ASSERT(next_parameter_index < parameters.size()); | 226 | return parameters.at(next_parameter_index++); |
| 227 | return parameters[next_parameter_index++]; | ||
| 228 | } | 227 | } |
| 229 | 228 | ||
| 230 | u32 MacroInterpreter::GetRegister(u32 register_id) const { | 229 | u32 MacroInterpreter::GetRegister(u32 register_id) const { |
| 231 | // Register 0 is supposed to always return 0. | 230 | return registers.at(register_id); |
| 232 | if (register_id == 0) | ||
| 233 | return 0; | ||
| 234 | |||
| 235 | ASSERT(register_id < registers.size()); | ||
| 236 | return registers[register_id]; | ||
| 237 | } | 231 | } |
| 238 | 232 | ||
| 239 | void MacroInterpreter::SetRegister(u32 register_id, u32 value) { | 233 | void MacroInterpreter::SetRegister(u32 register_id, u32 value) { |
| 240 | // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero | 234 | // Register 0 is hardwired as the zero register. |
| 241 | // register. | 235 | // Ensure no writes to it actually occur. |
| 242 | if (register_id == 0) | 236 | if (register_id == 0) { |
| 243 | return; | 237 | return; |
| 238 | } | ||
| 244 | 239 | ||
| 245 | ASSERT(register_id < registers.size()); | 240 | registers.at(register_id) = value; |
| 246 | registers[register_id] = value; | ||
| 247 | } | 241 | } |
| 248 | 242 | ||
| 249 | void MacroInterpreter::SetMethodAddress(u32 address) { | 243 | void MacroInterpreter::SetMethodAddress(u32 address) { |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index e76b59842..0f4e820aa 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -5,16 +5,13 @@ | |||
| 5 | #include "common/alignment.h" | 5 | #include "common/alignment.h" |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | ||
| 9 | #include "core/memory.h" | 8 | #include "core/memory.h" |
| 10 | #include "video_core/gpu.h" | ||
| 11 | #include "video_core/memory_manager.h" | 9 | #include "video_core/memory_manager.h" |
| 12 | #include "video_core/rasterizer_interface.h" | 10 | #include "video_core/rasterizer_interface.h" |
| 13 | #include "video_core/renderer_base.h" | ||
| 14 | 11 | ||
| 15 | namespace Tegra { | 12 | namespace Tegra { |
| 16 | 13 | ||
| 17 | MemoryManager::MemoryManager() { | 14 | MemoryManager::MemoryManager(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} { |
| 18 | std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); | 15 | std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); |
| 19 | std::fill(page_table.attributes.begin(), page_table.attributes.end(), | 16 | std::fill(page_table.attributes.begin(), page_table.attributes.end(), |
| 20 | Common::PageType::Unmapped); | 17 | Common::PageType::Unmapped); |
| @@ -70,23 +67,23 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | |||
| 70 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 67 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 71 | const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))}; | 68 | const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))}; |
| 72 | 69 | ||
| 73 | Core::System::GetInstance().Renderer().Rasterizer().FlushAndInvalidateRegion(cache_addr, | 70 | rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size); |
| 74 | aligned_size); | ||
| 75 | UnmapRange(gpu_addr, aligned_size); | 71 | UnmapRange(gpu_addr, aligned_size); |
| 76 | 72 | ||
| 77 | return gpu_addr; | 73 | return gpu_addr; |
| 78 | } | 74 | } |
| 79 | 75 | ||
| 80 | GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) { | 76 | GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const { |
| 81 | // Find the first Free VMA. | 77 | // Find the first Free VMA. |
| 82 | const VMAHandle vma_handle{std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { | 78 | const VMAHandle vma_handle{ |
| 83 | if (vma.second.type != VirtualMemoryArea::Type::Unmapped) { | 79 | std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) { |
| 84 | return false; | 80 | if (vma.second.type != VirtualMemoryArea::Type::Unmapped) { |
| 85 | } | 81 | return false; |
| 82 | } | ||
| 86 | 83 | ||
| 87 | const VAddr vma_end{vma.second.base + vma.second.size}; | 84 | const VAddr vma_end{vma.second.base + vma.second.size}; |
| 88 | return vma_end > region_start && vma_end >= region_start + size; | 85 | return vma_end > region_start && vma_end >= region_start + size; |
| 89 | })}; | 86 | })}; |
| 90 | 87 | ||
| 91 | if (vma_handle == vma_map.end()) { | 88 | if (vma_handle == vma_map.end()) { |
| 92 | return {}; | 89 | return {}; |
| @@ -99,12 +96,12 @@ bool MemoryManager::IsAddressValid(GPUVAddr addr) const { | |||
| 99 | return (addr >> page_bits) < page_table.pointers.size(); | 96 | return (addr >> page_bits) < page_table.pointers.size(); |
| 100 | } | 97 | } |
| 101 | 98 | ||
| 102 | std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) { | 99 | std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const { |
| 103 | if (!IsAddressValid(addr)) { | 100 | if (!IsAddressValid(addr)) { |
| 104 | return {}; | 101 | return {}; |
| 105 | } | 102 | } |
| 106 | 103 | ||
| 107 | VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]}; | 104 | const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]}; |
| 108 | if (cpu_addr) { | 105 | if (cpu_addr) { |
| 109 | return cpu_addr + (addr & page_mask); | 106 | return cpu_addr + (addr & page_mask); |
| 110 | } | 107 | } |
| @@ -113,7 +110,7 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) { | |||
| 113 | } | 110 | } |
| 114 | 111 | ||
| 115 | template <typename T> | 112 | template <typename T> |
| 116 | T MemoryManager::Read(GPUVAddr addr) { | 113 | T MemoryManager::Read(GPUVAddr addr) const { |
| 117 | if (!IsAddressValid(addr)) { | 114 | if (!IsAddressValid(addr)) { |
| 118 | return {}; | 115 | return {}; |
| 119 | } | 116 | } |
| @@ -165,10 +162,10 @@ void MemoryManager::Write(GPUVAddr addr, T data) { | |||
| 165 | } | 162 | } |
| 166 | } | 163 | } |
| 167 | 164 | ||
| 168 | template u8 MemoryManager::Read<u8>(GPUVAddr addr); | 165 | template u8 MemoryManager::Read<u8>(GPUVAddr addr) const; |
| 169 | template u16 MemoryManager::Read<u16>(GPUVAddr addr); | 166 | template u16 MemoryManager::Read<u16>(GPUVAddr addr) const; |
| 170 | template u32 MemoryManager::Read<u32>(GPUVAddr addr); | 167 | template u32 MemoryManager::Read<u32>(GPUVAddr addr) const; |
| 171 | template u64 MemoryManager::Read<u64>(GPUVAddr addr); | 168 | template u64 MemoryManager::Read<u64>(GPUVAddr addr) const; |
| 172 | template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data); | 169 | template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data); |
| 173 | template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data); | 170 | template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data); |
| 174 | template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data); | 171 | template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data); |
| @@ -179,8 +176,22 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) { | |||
| 179 | return {}; | 176 | return {}; |
| 180 | } | 177 | } |
| 181 | 178 | ||
| 182 | u8* page_pointer{page_table.pointers[addr >> page_bits]}; | 179 | u8* const page_pointer{page_table.pointers[addr >> page_bits]}; |
| 183 | if (page_pointer) { | 180 | if (page_pointer != nullptr) { |
| 181 | return page_pointer + (addr & page_mask); | ||
| 182 | } | ||
| 183 | |||
| 184 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); | ||
| 185 | return {}; | ||
| 186 | } | ||
| 187 | |||
| 188 | const u8* MemoryManager::GetPointer(GPUVAddr addr) const { | ||
| 189 | if (!IsAddressValid(addr)) { | ||
| 190 | return {}; | ||
| 191 | } | ||
| 192 | |||
| 193 | const u8* const page_pointer{page_table.pointers[addr >> page_bits]}; | ||
| 194 | if (page_pointer != nullptr) { | ||
| 184 | return page_pointer + (addr & page_mask); | 195 | return page_pointer + (addr & page_mask); |
| 185 | } | 196 | } |
| 186 | 197 | ||
| @@ -188,15 +199,86 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) { | |||
| 188 | return {}; | 199 | return {}; |
| 189 | } | 200 | } |
| 190 | 201 | ||
| 191 | void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) { | 202 | void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const { |
| 192 | std::memcpy(dest_buffer, GetPointer(src_addr), size); | 203 | std::size_t remaining_size{size}; |
| 204 | std::size_t page_index{src_addr >> page_bits}; | ||
| 205 | std::size_t page_offset{src_addr & page_mask}; | ||
| 206 | |||
| 207 | while (remaining_size > 0) { | ||
| 208 | const std::size_t copy_amount{ | ||
| 209 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||
| 210 | |||
| 211 | switch (page_table.attributes[page_index]) { | ||
| 212 | case Common::PageType::Memory: { | ||
| 213 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | ||
| 214 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | ||
| 215 | std::memcpy(dest_buffer, src_ptr, copy_amount); | ||
| 216 | break; | ||
| 217 | } | ||
| 218 | default: | ||
| 219 | UNREACHABLE(); | ||
| 220 | } | ||
| 221 | |||
| 222 | page_index++; | ||
| 223 | page_offset = 0; | ||
| 224 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | ||
| 225 | remaining_size -= copy_amount; | ||
| 226 | } | ||
| 193 | } | 227 | } |
| 228 | |||
| 194 | void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { | 229 | void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { |
| 195 | std::memcpy(GetPointer(dest_addr), src_buffer, size); | 230 | std::size_t remaining_size{size}; |
| 231 | std::size_t page_index{dest_addr >> page_bits}; | ||
| 232 | std::size_t page_offset{dest_addr & page_mask}; | ||
| 233 | |||
| 234 | while (remaining_size > 0) { | ||
| 235 | const std::size_t copy_amount{ | ||
| 236 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||
| 237 | |||
| 238 | switch (page_table.attributes[page_index]) { | ||
| 239 | case Common::PageType::Memory: { | ||
| 240 | u8* dest_ptr{page_table.pointers[page_index] + page_offset}; | ||
| 241 | rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); | ||
| 242 | std::memcpy(dest_ptr, src_buffer, copy_amount); | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | default: | ||
| 246 | UNREACHABLE(); | ||
| 247 | } | ||
| 248 | |||
| 249 | page_index++; | ||
| 250 | page_offset = 0; | ||
| 251 | src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; | ||
| 252 | remaining_size -= copy_amount; | ||
| 253 | } | ||
| 196 | } | 254 | } |
| 197 | 255 | ||
| 198 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { | 256 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { |
| 199 | std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size); | 257 | std::size_t remaining_size{size}; |
| 258 | std::size_t page_index{src_addr >> page_bits}; | ||
| 259 | std::size_t page_offset{src_addr & page_mask}; | ||
| 260 | |||
| 261 | while (remaining_size > 0) { | ||
| 262 | const std::size_t copy_amount{ | ||
| 263 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||
| 264 | |||
| 265 | switch (page_table.attributes[page_index]) { | ||
| 266 | case Common::PageType::Memory: { | ||
| 267 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | ||
| 268 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | ||
| 269 | WriteBlock(dest_addr, src_ptr, copy_amount); | ||
| 270 | break; | ||
| 271 | } | ||
| 272 | default: | ||
| 273 | UNREACHABLE(); | ||
| 274 | } | ||
| 275 | |||
| 276 | page_index++; | ||
| 277 | page_offset = 0; | ||
| 278 | dest_addr += static_cast<VAddr>(copy_amount); | ||
| 279 | src_addr += static_cast<VAddr>(copy_amount); | ||
| 280 | remaining_size -= copy_amount; | ||
| 281 | } | ||
| 200 | } | 282 | } |
| 201 | 283 | ||
| 202 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, | 284 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, |
| @@ -336,7 +418,7 @@ MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) { | |||
| 336 | const VirtualMemoryArea& vma{vma_handle->second}; | 418 | const VirtualMemoryArea& vma{vma_handle->second}; |
| 337 | if (vma.type == VirtualMemoryArea::Type::Mapped) { | 419 | if (vma.type == VirtualMemoryArea::Type::Mapped) { |
| 338 | // Region is already allocated | 420 | // Region is already allocated |
| 339 | return {}; | 421 | return vma_handle; |
| 340 | } | 422 | } |
| 341 | 423 | ||
| 342 | const VAddr start_in_vma{base - vma.base}; | 424 | const VAddr start_in_vma{base - vma.base}; |
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 34744bb27..647cbf93a 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -10,6 +10,10 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/page_table.h" | 11 | #include "common/page_table.h" |
| 12 | 12 | ||
| 13 | namespace VideoCore { | ||
| 14 | class RasterizerInterface; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace Tegra { | 17 | namespace Tegra { |
| 14 | 18 | ||
| 15 | /** | 19 | /** |
| @@ -43,24 +47,25 @@ struct VirtualMemoryArea { | |||
| 43 | 47 | ||
| 44 | class MemoryManager final { | 48 | class MemoryManager final { |
| 45 | public: | 49 | public: |
| 46 | MemoryManager(); | 50 | MemoryManager(VideoCore::RasterizerInterface& rasterizer); |
| 47 | 51 | ||
| 48 | GPUVAddr AllocateSpace(u64 size, u64 align); | 52 | GPUVAddr AllocateSpace(u64 size, u64 align); |
| 49 | GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); | 53 | GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); |
| 50 | GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); | 54 | GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); |
| 51 | GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size); | 55 | GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size); |
| 52 | GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); | 56 | GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); |
| 53 | std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr); | 57 | std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; |
| 54 | 58 | ||
| 55 | template <typename T> | 59 | template <typename T> |
| 56 | T Read(GPUVAddr addr); | 60 | T Read(GPUVAddr addr) const; |
| 57 | 61 | ||
| 58 | template <typename T> | 62 | template <typename T> |
| 59 | void Write(GPUVAddr addr, T data); | 63 | void Write(GPUVAddr addr, T data); |
| 60 | 64 | ||
| 61 | u8* GetPointer(GPUVAddr addr); | 65 | u8* GetPointer(GPUVAddr addr); |
| 66 | const u8* GetPointer(GPUVAddr addr) const; | ||
| 62 | 67 | ||
| 63 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); | 68 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; |
| 64 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); | 69 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); |
| 65 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); | 70 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); |
| 66 | 71 | ||
| @@ -127,7 +132,7 @@ private: | |||
| 127 | void UpdatePageTableForVMA(const VirtualMemoryArea& vma); | 132 | void UpdatePageTableForVMA(const VirtualMemoryArea& vma); |
| 128 | 133 | ||
| 129 | /// Finds a free (unmapped region) of the specified size starting at the specified address. | 134 | /// Finds a free (unmapped region) of the specified size starting at the specified address. |
| 130 | GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size); | 135 | GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const; |
| 131 | 136 | ||
| 132 | private: | 137 | private: |
| 133 | static constexpr u64 page_bits{16}; | 138 | static constexpr u64 page_bits{16}; |
| @@ -143,6 +148,7 @@ private: | |||
| 143 | 148 | ||
| 144 | Common::PageTable page_table{page_bits}; | 149 | Common::PageTable page_table{page_bits}; |
| 145 | VMAMap vma_map; | 150 | VMAMap vma_map; |
| 151 | VideoCore::RasterizerInterface& rasterizer; | ||
| 146 | }; | 152 | }; |
| 147 | 153 | ||
| 148 | } // namespace Tegra | 154 | } // namespace Tegra |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index fd091c84c..25652e794 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/alignment.h" | 8 | #include "common/alignment.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/memory.h" | 10 | #include "video_core/memory_manager.h" |
| 11 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 11 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 12 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 12 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 13 | 13 | ||
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp index da9326253..ea4a593af 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.cpp +++ b/src/video_core/renderer_opengl/gl_global_cache.cpp | |||
| @@ -4,9 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #include <glad/glad.h> | 5 | #include <glad/glad.h> |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "video_core/memory_manager.h" | ||
| 10 | #include "video_core/renderer_opengl/gl_global_cache.h" | 10 | #include "video_core/renderer_opengl/gl_global_cache.h" |
| 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 12 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 12 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| @@ -14,28 +14,28 @@ | |||
| 14 | 14 | ||
| 15 | namespace OpenGL { | 15 | namespace OpenGL { |
| 16 | 16 | ||
| 17 | CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr) | 17 | CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size) |
| 18 | : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size} { | 18 | : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, host_ptr{host_ptr}, size{size}, |
| 19 | max_size{max_size} { | ||
| 19 | buffer.Create(); | 20 | buffer.Create(); |
| 20 | // Bind and unbind the buffer so it gets allocated by the driver | ||
| 21 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); | ||
| 22 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); | ||
| 23 | LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory"); | 21 | LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory"); |
| 24 | } | 22 | } |
| 25 | 23 | ||
| 26 | void CachedGlobalRegion::Reload(u32 size_) { | 24 | CachedGlobalRegion::~CachedGlobalRegion() = default; |
| 27 | constexpr auto max_size = static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize); | ||
| 28 | 25 | ||
| 26 | void CachedGlobalRegion::Reload(u32 size_) { | ||
| 29 | size = size_; | 27 | size = size_; |
| 30 | if (size > max_size) { | 28 | if (size > max_size) { |
| 31 | size = max_size; | 29 | size = max_size; |
| 32 | LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the expected size {}!", size_, | 30 | LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the supported size {}!", size_, |
| 33 | max_size); | 31 | max_size); |
| 34 | } | 32 | } |
| 33 | glNamedBufferData(buffer.handle, size, host_ptr, GL_STREAM_DRAW); | ||
| 34 | } | ||
| 35 | 35 | ||
| 36 | // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer | 36 | void CachedGlobalRegion::Flush() { |
| 37 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); | 37 | LOG_DEBUG(Render_OpenGL, "Flushing {} bytes to CPU memory address 0x{:16}", size, cpu_addr); |
| 38 | glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW); | 38 | glGetNamedBufferSubData(buffer.handle, 0, static_cast<GLsizeiptr>(size), host_ptr); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const { | 41 | GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const { |
| @@ -46,14 +46,16 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, | |||
| 46 | return search->second; | 46 | return search->second; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u32 size, | 49 | GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, |
| 50 | u8* host_ptr) { | 50 | u32 size) { |
| 51 | GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; | 51 | GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; |
| 52 | if (!region) { | 52 | if (!region) { |
| 53 | // No reserved surface available, create a new one and reserve it | 53 | // No reserved surface available, create a new one and reserve it |
| 54 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; | 54 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; |
| 55 | const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr); | 55 | const auto cpu_addr{memory_manager.GpuToCpuAddress(addr)}; |
| 56 | region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr); | 56 | ASSERT(cpu_addr); |
| 57 | |||
| 58 | region = std::make_shared<CachedGlobalRegion>(*cpu_addr, host_ptr, size, max_ssbo_size); | ||
| 57 | ReserveGlobalRegion(region); | 59 | ReserveGlobalRegion(region); |
| 58 | } | 60 | } |
| 59 | region->Reload(size); | 61 | region->Reload(size); |
| @@ -65,7 +67,11 @@ void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) { | |||
| 65 | } | 67 | } |
| 66 | 68 | ||
| 67 | GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) | 69 | GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) |
| 68 | : RasterizerCache{rasterizer} {} | 70 | : RasterizerCache{rasterizer} { |
| 71 | GLint max_ssbo_size_; | ||
| 72 | glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size_); | ||
| 73 | max_ssbo_size = static_cast<u32>(max_ssbo_size_); | ||
| 74 | } | ||
| 69 | 75 | ||
| 70 | GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( | 76 | GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( |
| 71 | const GLShader::GlobalMemoryEntry& global_region, | 77 | const GLShader::GlobalMemoryEntry& global_region, |
| @@ -73,7 +79,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( | |||
| 73 | 79 | ||
| 74 | auto& gpu{Core::System::GetInstance().GPU()}; | 80 | auto& gpu{Core::System::GetInstance().GPU()}; |
| 75 | auto& memory_manager{gpu.MemoryManager()}; | 81 | auto& memory_manager{gpu.MemoryManager()}; |
| 76 | const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]}; | 82 | const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<std::size_t>(stage)]}; |
| 77 | const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + | 83 | const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + |
| 78 | global_region.GetCbufOffset()}; | 84 | global_region.GetCbufOffset()}; |
| 79 | const auto actual_addr{memory_manager.Read<u64>(addr)}; | 85 | const auto actual_addr{memory_manager.Read<u64>(addr)}; |
| @@ -85,7 +91,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( | |||
| 85 | 91 | ||
| 86 | if (!region) { | 92 | if (!region) { |
| 87 | // No global region found - create a new one | 93 | // No global region found - create a new one |
| 88 | region = GetUncachedGlobalRegion(actual_addr, size, host_ptr); | 94 | region = GetUncachedGlobalRegion(actual_addr, host_ptr, size); |
| 89 | Register(region); | 95 | Register(region); |
| 90 | } | 96 | } |
| 91 | 97 | ||
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h index 5a21ab66f..196e6e278 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.h +++ b/src/video_core/renderer_opengl/gl_global_cache.h | |||
| @@ -19,7 +19,7 @@ namespace OpenGL { | |||
| 19 | 19 | ||
| 20 | namespace GLShader { | 20 | namespace GLShader { |
| 21 | class GlobalMemoryEntry; | 21 | class GlobalMemoryEntry; |
| 22 | } // namespace GLShader | 22 | } |
| 23 | 23 | ||
| 24 | class RasterizerOpenGL; | 24 | class RasterizerOpenGL; |
| 25 | class CachedGlobalRegion; | 25 | class CachedGlobalRegion; |
| @@ -27,7 +27,8 @@ using GlobalRegion = std::shared_ptr<CachedGlobalRegion>; | |||
| 27 | 27 | ||
| 28 | class CachedGlobalRegion final : public RasterizerCacheObject { | 28 | class CachedGlobalRegion final : public RasterizerCacheObject { |
| 29 | public: | 29 | public: |
| 30 | explicit CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr); | 30 | explicit CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size); |
| 31 | ~CachedGlobalRegion(); | ||
| 31 | 32 | ||
| 32 | VAddr GetCpuAddr() const override { | 33 | VAddr GetCpuAddr() const override { |
| 33 | return cpu_addr; | 34 | return cpu_addr; |
| @@ -45,14 +46,14 @@ public: | |||
| 45 | /// Reloads the global region from guest memory | 46 | /// Reloads the global region from guest memory |
| 46 | void Reload(u32 size_); | 47 | void Reload(u32 size_); |
| 47 | 48 | ||
| 48 | // TODO(Rodrigo): When global memory is written (STG), implement flushing | 49 | void Flush() override; |
| 49 | void Flush() override { | ||
| 50 | UNIMPLEMENTED(); | ||
| 51 | } | ||
| 52 | 50 | ||
| 53 | private: | 51 | private: |
| 54 | VAddr cpu_addr{}; | 52 | VAddr cpu_addr{}; |
| 53 | u8* host_ptr{}; | ||
| 55 | u32 size{}; | 54 | u32 size{}; |
| 55 | u32 max_size{}; | ||
| 56 | |||
| 56 | OGLBuffer buffer; | 57 | OGLBuffer buffer; |
| 57 | }; | 58 | }; |
| 58 | 59 | ||
| @@ -66,10 +67,11 @@ public: | |||
| 66 | 67 | ||
| 67 | private: | 68 | private: |
| 68 | GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; | 69 | GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; |
| 69 | GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u32 size, u8* host_ptr); | 70 | GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, u32 size); |
| 70 | void ReserveGlobalRegion(GlobalRegion region); | 71 | void ReserveGlobalRegion(GlobalRegion region); |
| 71 | 72 | ||
| 72 | std::unordered_map<CacheAddr, GlobalRegion> reserve; | 73 | std::unordered_map<CacheAddr, GlobalRegion> reserve; |
| 74 | u32 max_ssbo_size{}; | ||
| 73 | }; | 75 | }; |
| 74 | 76 | ||
| 75 | } // namespace OpenGL | 77 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp index 2bcbd3da2..c3e94d917 100644 --- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/memory.h" | 10 | #include "video_core/memory_manager.h" |
| 11 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 11 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 12 | #include "video_core/renderer_opengl/gl_primitive_assembler.h" | 12 | #include "video_core/renderer_opengl/gl_primitive_assembler.h" |
| 13 | 13 | ||
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.h b/src/video_core/renderer_opengl/gl_primitive_assembler.h index 0e2e7dc36..4e87ce4d6 100644 --- a/src/video_core/renderer_opengl/gl_primitive_assembler.h +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.h | |||
| @@ -4,11 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vector> | ||
| 8 | #include <glad/glad.h> | 7 | #include <glad/glad.h> |
| 9 | 8 | ||
| 10 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 11 | #include "video_core/memory_manager.h" | ||
| 12 | 10 | ||
| 13 | namespace OpenGL { | 11 | namespace OpenGL { |
| 14 | 12 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 046fc935b..0b1fe3494 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -17,7 +17,6 @@ | |||
| 17 | #include "common/microprofile.h" | 17 | #include "common/microprofile.h" |
| 18 | #include "common/scope_exit.h" | 18 | #include "common/scope_exit.h" |
| 19 | #include "core/core.h" | 19 | #include "core/core.h" |
| 20 | #include "core/frontend/emu_window.h" | ||
| 21 | #include "core/hle/kernel/process.h" | 20 | #include "core/hle/kernel/process.h" |
| 22 | #include "core/settings.h" | 21 | #include "core/settings.h" |
| 23 | #include "video_core/engines/maxwell_3d.h" | 22 | #include "video_core/engines/maxwell_3d.h" |
| @@ -26,7 +25,6 @@ | |||
| 26 | #include "video_core/renderer_opengl/gl_shader_gen.h" | 25 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
| 27 | #include "video_core/renderer_opengl/maxwell_to_gl.h" | 26 | #include "video_core/renderer_opengl/maxwell_to_gl.h" |
| 28 | #include "video_core/renderer_opengl/renderer_opengl.h" | 27 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 29 | #include "video_core/video_core.h" | ||
| 30 | 28 | ||
| 31 | namespace OpenGL { | 29 | namespace OpenGL { |
| 32 | 30 | ||
| @@ -301,6 +299,10 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 301 | BaseBindings base_bindings; | 299 | BaseBindings base_bindings; |
| 302 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | 300 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; |
| 303 | 301 | ||
| 302 | // Prepare packed bindings | ||
| 303 | bind_ubo_pushbuffer.Setup(base_bindings.cbuf); | ||
| 304 | bind_ssbo_pushbuffer.Setup(base_bindings.gmem); | ||
| 305 | |||
| 304 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | 306 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
| 305 | const auto& shader_config = gpu.regs.shader_config[index]; | 307 | const auto& shader_config = gpu.regs.shader_config[index]; |
| 306 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; | 308 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; |
| @@ -318,13 +320,13 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 318 | const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 | 320 | const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 |
| 319 | 321 | ||
| 320 | GLShader::MaxwellUniformData ubo{}; | 322 | GLShader::MaxwellUniformData ubo{}; |
| 321 | ubo.SetFromRegs(gpu.state.shader_stages[stage]); | 323 | ubo.SetFromRegs(gpu, stage); |
| 322 | const GLintptr offset = buffer_cache.UploadHostMemory( | 324 | const GLintptr offset = buffer_cache.UploadHostMemory( |
| 323 | &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); | 325 | &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); |
| 324 | 326 | ||
| 325 | // Bind the emulation info buffer | 327 | // Bind the emulation info buffer |
| 326 | glBindBufferRange(GL_UNIFORM_BUFFER, base_bindings.cbuf, buffer_cache.GetHandle(), offset, | 328 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, |
| 327 | static_cast<GLsizeiptr>(sizeof(ubo))); | 329 | static_cast<GLsizeiptr>(sizeof(ubo))); |
| 328 | 330 | ||
| 329 | Shader shader{shader_cache.GetStageProgram(program)}; | 331 | Shader shader{shader_cache.GetStageProgram(program)}; |
| 330 | const auto [program_handle, next_bindings] = | 332 | const auto [program_handle, next_bindings] = |
| @@ -368,6 +370,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 368 | base_bindings = next_bindings; | 370 | base_bindings = next_bindings; |
| 369 | } | 371 | } |
| 370 | 372 | ||
| 373 | bind_ubo_pushbuffer.Bind(); | ||
| 374 | bind_ssbo_pushbuffer.Bind(); | ||
| 375 | |||
| 371 | SyncClipEnabled(clip_distances); | 376 | SyncClipEnabled(clip_distances); |
| 372 | 377 | ||
| 373 | gpu.dirty_flags.shaders = false; | 378 | gpu.dirty_flags.shaders = false; |
| @@ -577,9 +582,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( | |||
| 577 | } | 582 | } |
| 578 | 583 | ||
| 579 | void RasterizerOpenGL::Clear() { | 584 | void RasterizerOpenGL::Clear() { |
| 580 | const auto prev_state{state}; | ||
| 581 | SCOPE_EXIT({ prev_state.Apply(); }); | ||
| 582 | |||
| 583 | const auto& regs = system.GPU().Maxwell3D().regs; | 585 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 584 | bool use_color{}; | 586 | bool use_color{}; |
| 585 | bool use_depth{}; | 587 | bool use_depth{}; |
| @@ -651,7 +653,10 @@ void RasterizerOpenGL::Clear() { | |||
| 651 | clear_state.EmulateViewportWithScissor(); | 653 | clear_state.EmulateViewportWithScissor(); |
| 652 | } | 654 | } |
| 653 | 655 | ||
| 654 | clear_state.Apply(); | 656 | clear_state.ApplyColorMask(); |
| 657 | clear_state.ApplyDepth(); | ||
| 658 | clear_state.ApplyStencilTest(); | ||
| 659 | clear_state.ApplyViewport(); | ||
| 655 | 660 | ||
| 656 | if (use_color) { | 661 | if (use_color) { |
| 657 | glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); | 662 | glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); |
| @@ -751,6 +756,7 @@ void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) { | |||
| 751 | return; | 756 | return; |
| 752 | } | 757 | } |
| 753 | res_cache.FlushRegion(addr, size); | 758 | res_cache.FlushRegion(addr, size); |
| 759 | global_cache.FlushRegion(addr, size); | ||
| 754 | } | 760 | } |
| 755 | 761 | ||
| 756 | void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { | 762 | void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { |
| @@ -902,23 +908,14 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader | |||
| 902 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; | 908 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; |
| 903 | const auto& entries = shader->GetShaderEntries().const_buffers; | 909 | const auto& entries = shader->GetShaderEntries().const_buffers; |
| 904 | 910 | ||
| 905 | constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers; | ||
| 906 | std::array<GLuint, max_binds> bind_buffers; | ||
| 907 | std::array<GLintptr, max_binds> bind_offsets; | ||
| 908 | std::array<GLsizeiptr, max_binds> bind_sizes; | ||
| 909 | |||
| 910 | ASSERT_MSG(entries.size() <= max_binds, "Exceeded expected number of binding points."); | ||
| 911 | |||
| 912 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage | 911 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage |
| 913 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | 912 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { |
| 914 | const auto& used_buffer = entries[bindpoint]; | 913 | const auto& used_buffer = entries[bindpoint]; |
| 915 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; | 914 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; |
| 916 | 915 | ||
| 917 | if (!buffer.enabled) { | 916 | if (!buffer.enabled) { |
| 918 | // With disabled buffers set values as zero to unbind them | 917 | // Set values to zero to unbind buffers |
| 919 | bind_buffers[bindpoint] = 0; | 918 | bind_ubo_pushbuffer.Push(0, 0, 0); |
| 920 | bind_offsets[bindpoint] = 0; | ||
| 921 | bind_sizes[bindpoint] = 0; | ||
| 922 | continue; | 919 | continue; |
| 923 | } | 920 | } |
| 924 | 921 | ||
| @@ -946,30 +943,22 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader | |||
| 946 | const GLintptr const_buffer_offset = buffer_cache.UploadMemory( | 943 | const GLintptr const_buffer_offset = buffer_cache.UploadMemory( |
| 947 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); | 944 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); |
| 948 | 945 | ||
| 949 | // Prepare values for multibind | 946 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size); |
| 950 | bind_buffers[bindpoint] = buffer_cache.GetHandle(); | ||
| 951 | bind_offsets[bindpoint] = const_buffer_offset; | ||
| 952 | bind_sizes[bindpoint] = size; | ||
| 953 | } | 947 | } |
| 954 | |||
| 955 | // The first binding is reserved for emulation values | ||
| 956 | const GLuint ubo_base_binding = base_bindings.cbuf + 1; | ||
| 957 | glBindBuffersRange(GL_UNIFORM_BUFFER, ubo_base_binding, static_cast<GLsizei>(entries.size()), | ||
| 958 | bind_buffers.data(), bind_offsets.data(), bind_sizes.data()); | ||
| 959 | } | 948 | } |
| 960 | 949 | ||
| 961 | void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, | 950 | void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, |
| 962 | const Shader& shader, GLenum primitive_mode, | 951 | const Shader& shader, GLenum primitive_mode, |
| 963 | BaseBindings base_bindings) { | 952 | BaseBindings base_bindings) { |
| 964 | // TODO(Rodrigo): Use ARB_multi_bind here | ||
| 965 | const auto& entries = shader->GetShaderEntries().global_memory_entries; | 953 | const auto& entries = shader->GetShaderEntries().global_memory_entries; |
| 966 | 954 | for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | |
| 967 | for (u32 bindpoint = 0; bindpoint < static_cast<u32>(entries.size()); ++bindpoint) { | 955 | const auto& entry{entries[bindpoint]}; |
| 968 | const auto& entry = entries[bindpoint]; | 956 | const auto& region{global_cache.GetGlobalRegion(entry, stage)}; |
| 969 | const u32 current_bindpoint = base_bindings.gmem + bindpoint; | 957 | if (entry.IsWritten()) { |
| 970 | const auto& region = global_cache.GetGlobalRegion(entry, stage); | 958 | region->MarkAsModified(true, global_cache); |
| 971 | 959 | } | |
| 972 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, current_bindpoint, region->GetBufferHandle()); | 960 | bind_ssbo_pushbuffer.Push(region->GetBufferHandle(), 0, |
| 961 | static_cast<GLsizeiptr>(region->GetSizeInBytes())); | ||
| 973 | } | 962 | } |
| 974 | } | 963 | } |
| 975 | 964 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 4de565321..d4c2cf80e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -12,15 +12,12 @@ | |||
| 12 | #include <optional> | 12 | #include <optional> |
| 13 | #include <tuple> | 13 | #include <tuple> |
| 14 | #include <utility> | 14 | #include <utility> |
| 15 | #include <vector> | ||
| 16 | 15 | ||
| 17 | #include <boost/icl/interval_map.hpp> | 16 | #include <boost/icl/interval_map.hpp> |
| 18 | #include <boost/range/iterator_range.hpp> | ||
| 19 | #include <glad/glad.h> | 17 | #include <glad/glad.h> |
| 20 | 18 | ||
| 21 | #include "common/common_types.h" | 19 | #include "common/common_types.h" |
| 22 | #include "video_core/engines/maxwell_3d.h" | 20 | #include "video_core/engines/maxwell_3d.h" |
| 23 | #include "video_core/memory_manager.h" | ||
| 24 | #include "video_core/rasterizer_cache.h" | 21 | #include "video_core/rasterizer_cache.h" |
| 25 | #include "video_core/rasterizer_interface.h" | 22 | #include "video_core/rasterizer_interface.h" |
| 26 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 23 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| @@ -29,10 +26,9 @@ | |||
| 29 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | 26 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" |
| 30 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 27 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 31 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 28 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 32 | #include "video_core/renderer_opengl/gl_shader_gen.h" | ||
| 33 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 29 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 34 | #include "video_core/renderer_opengl/gl_state.h" | 30 | #include "video_core/renderer_opengl/gl_state.h" |
| 35 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | 31 | #include "video_core/renderer_opengl/utils.h" |
| 36 | 32 | ||
| 37 | namespace Core { | 33 | namespace Core { |
| 38 | class System; | 34 | class System; |
| @@ -75,10 +71,6 @@ public: | |||
| 75 | static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, | 71 | static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, |
| 76 | "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); | 72 | "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); |
| 77 | 73 | ||
| 78 | static constexpr std::size_t MaxGlobalMemorySize = 0x10000; | ||
| 79 | static_assert(MaxGlobalMemorySize % sizeof(float) == 0, | ||
| 80 | "The maximum size of a global memory must be a multiple of the size of float"); | ||
| 81 | |||
| 82 | private: | 74 | private: |
| 83 | class SamplerInfo { | 75 | class SamplerInfo { |
| 84 | public: | 76 | public: |
| @@ -234,6 +226,9 @@ private: | |||
| 234 | PrimitiveAssembler primitive_assembler{buffer_cache}; | 226 | PrimitiveAssembler primitive_assembler{buffer_cache}; |
| 235 | GLint uniform_buffer_alignment; | 227 | GLint uniform_buffer_alignment; |
| 236 | 228 | ||
| 229 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; | ||
| 230 | BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; | ||
| 231 | |||
| 237 | std::size_t CalculateVertexArraysSize() const; | 232 | std::size_t CalculateVertexArraysSize() const; |
| 238 | 233 | ||
| 239 | std::size_t CalculateIndexBufferSize() const; | 234 | std::size_t CalculateIndexBufferSize() const; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index aba6ce731..7a68b8738 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -13,9 +13,9 @@ | |||
| 13 | #include "common/scope_exit.h" | 13 | #include "common/scope_exit.h" |
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| 16 | #include "core/memory.h" | ||
| 17 | #include "core/settings.h" | 16 | #include "core/settings.h" |
| 18 | #include "video_core/engines/maxwell_3d.h" | 17 | #include "video_core/engines/maxwell_3d.h" |
| 18 | #include "video_core/memory_manager.h" | ||
| 19 | #include "video_core/morton.h" | 19 | #include "video_core/morton.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_rasterizer_cache.h" | 21 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" |
| @@ -112,11 +112,26 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, | |||
| 112 | params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), | 112 | params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), |
| 113 | params.srgb_conversion); | 113 | params.srgb_conversion); |
| 114 | 114 | ||
| 115 | if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) { | 115 | if (config.tsc.depth_compare_enabled) { |
| 116 | // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled, | 116 | // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled, |
| 117 | // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also | 117 | // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also |
| 118 | // causes GetFormatType to properly return 'Depth' below). | 118 | // causes GetFormatType to properly return 'Depth' below). |
| 119 | params.pixel_format = PixelFormat::Z16; | 119 | if (GetFormatType(params.pixel_format) == SurfaceType::ColorTexture) { |
| 120 | switch (params.pixel_format) { | ||
| 121 | case PixelFormat::R16S: | ||
| 122 | case PixelFormat::R16U: | ||
| 123 | case PixelFormat::R16F: | ||
| 124 | params.pixel_format = PixelFormat::Z16; | ||
| 125 | break; | ||
| 126 | case PixelFormat::R32F: | ||
| 127 | params.pixel_format = PixelFormat::Z32F; | ||
| 128 | break; | ||
| 129 | default: | ||
| 130 | LOG_WARNING(HW_GPU, "Color texture format being used with depth compare: {}", | ||
| 131 | static_cast<u32>(params.pixel_format)); | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | } | ||
| 120 | } | 135 | } |
| 121 | 136 | ||
| 122 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); | 137 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); |
| @@ -266,6 +281,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, | |||
| 266 | params.component_type = ComponentTypeFromRenderTarget(config.format); | 281 | params.component_type = ComponentTypeFromRenderTarget(config.format); |
| 267 | params.type = GetFormatType(params.pixel_format); | 282 | params.type = GetFormatType(params.pixel_format); |
| 268 | params.width = config.width; | 283 | params.width = config.width; |
| 284 | params.pitch = config.pitch; | ||
| 269 | params.height = config.height; | 285 | params.height = config.height; |
| 270 | params.unaligned_height = config.height; | 286 | params.unaligned_height = config.height; |
| 271 | params.target = SurfaceTarget::Texture2D; | 287 | params.target = SurfaceTarget::Texture2D; |
| @@ -662,8 +678,8 @@ void CachedSurface::FlushGLBuffer() { | |||
| 662 | gl_buffer[0].resize(GetSizeInBytes()); | 678 | gl_buffer[0].resize(GetSizeInBytes()); |
| 663 | 679 | ||
| 664 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | 680 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); |
| 665 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT | 681 | const u32 align = std::clamp(params.RowAlign(0), 1U, 8U); |
| 666 | ASSERT(params.width * GetBytesPerPixel(params.pixel_format) % 4 == 0); | 682 | glPixelStorei(GL_PACK_ALIGNMENT, align); |
| 667 | glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); | 683 | glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); |
| 668 | ASSERT(!tuple.compressed); | 684 | ASSERT(!tuple.compressed); |
| 669 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | 685 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| @@ -708,8 +724,8 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | |||
| 708 | 724 | ||
| 709 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | 725 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); |
| 710 | 726 | ||
| 711 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT | 727 | const u32 align = std::clamp(params.RowAlign(mip_map), 1U, 8U); |
| 712 | ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0); | 728 | glPixelStorei(GL_UNPACK_ALIGNMENT, align); |
| 713 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); | 729 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); |
| 714 | 730 | ||
| 715 | const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); | 731 | const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); |
| @@ -1175,10 +1191,16 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1175 | return new_surface; | 1191 | return new_surface; |
| 1176 | } | 1192 | } |
| 1177 | 1193 | ||
| 1194 | const bool old_compressed = | ||
| 1195 | GetFormatTuple(old_params.pixel_format, old_params.component_type).compressed; | ||
| 1196 | const bool new_compressed = | ||
| 1197 | GetFormatTuple(new_params.pixel_format, new_params.component_type).compressed; | ||
| 1198 | const bool compatible_formats = | ||
| 1199 | GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format) && | ||
| 1200 | !(old_compressed || new_compressed); | ||
| 1178 | // For compatible surfaces, we can just do fast glCopyImageSubData based copy | 1201 | // For compatible surfaces, we can just do fast glCopyImageSubData based copy |
| 1179 | if (old_params.target == new_params.target && old_params.type == new_params.type && | 1202 | if (old_params.target == new_params.target && old_params.depth == new_params.depth && |
| 1180 | old_params.depth == new_params.depth && old_params.depth == 1 && | 1203 | old_params.depth == 1 && compatible_formats) { |
| 1181 | GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) { | ||
| 1182 | FastCopySurface(old_surface, new_surface); | 1204 | FastCopySurface(old_surface, new_surface); |
| 1183 | return new_surface; | 1205 | return new_surface; |
| 1184 | } | 1206 | } |
| @@ -1193,7 +1215,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1193 | case SurfaceTarget::TextureCubemap: | 1215 | case SurfaceTarget::TextureCubemap: |
| 1194 | case SurfaceTarget::Texture2DArray: | 1216 | case SurfaceTarget::Texture2DArray: |
| 1195 | case SurfaceTarget::TextureCubeArray: | 1217 | case SurfaceTarget::TextureCubeArray: |
| 1196 | if (old_params.pixel_format == new_params.pixel_format) | 1218 | if (compatible_formats) |
| 1197 | FastLayeredCopySurface(old_surface, new_surface); | 1219 | FastLayeredCopySurface(old_surface, new_surface); |
| 1198 | else { | 1220 | else { |
| 1199 | AccurateCopySurface(old_surface, new_surface); | 1221 | AccurateCopySurface(old_surface, new_surface); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index e8073579f..db280dbb3 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -5,13 +5,13 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <map> | ||
| 9 | #include <memory> | 8 | #include <memory> |
| 10 | #include <string> | 9 | #include <string> |
| 11 | #include <unordered_set> | 10 | #include <tuple> |
| 12 | #include <vector> | 11 | #include <vector> |
| 13 | 12 | ||
| 14 | #include "common/alignment.h" | 13 | #include "common/alignment.h" |
| 14 | #include "common/bit_util.h" | ||
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "common/hash.h" | 16 | #include "common/hash.h" |
| 17 | #include "common/math_util.h" | 17 | #include "common/math_util.h" |
| @@ -206,6 +206,13 @@ struct SurfaceParams { | |||
| 206 | return bd; | 206 | return bd; |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | u32 RowAlign(u32 mip_level) const { | ||
| 210 | const u32 m_width = MipWidth(mip_level); | ||
| 211 | const u32 bytes_per_pixel = GetBytesPerPixel(pixel_format); | ||
| 212 | const u32 l2 = Common::CountTrailingZeroes32(m_width * bytes_per_pixel); | ||
| 213 | return (1U << l2); | ||
| 214 | } | ||
| 215 | |||
| 209 | /// Creates SurfaceParams from a texture configuration | 216 | /// Creates SurfaceParams from a texture configuration |
| 210 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, | 217 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, |
| 211 | const GLShader::SamplerEntry& entry); | 218 | const GLShader::SamplerEntry& entry); |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 290e654bc..99f67494c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -6,13 +6,12 @@ | |||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/hash.h" | 7 | #include "common/hash.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/memory.h" | ||
| 10 | #include "video_core/engines/maxwell_3d.h" | 9 | #include "video_core/engines/maxwell_3d.h" |
| 10 | #include "video_core/memory_manager.h" | ||
| 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 12 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 12 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 13 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 13 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 14 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" | 14 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" |
| 15 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 16 | #include "video_core/renderer_opengl/utils.h" | 15 | #include "video_core/renderer_opengl/utils.h" |
| 17 | #include "video_core/shader/shader_ir.h" | 16 | #include "video_core/shader/shader_ir.h" |
| 18 | 17 | ||
| @@ -41,6 +40,10 @@ GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) { | |||
| 41 | /// Gets the shader program code from memory for the specified address | 40 | /// Gets the shader program code from memory for the specified address |
| 42 | ProgramCode GetShaderCode(const u8* host_ptr) { | 41 | ProgramCode GetShaderCode(const u8* host_ptr) { |
| 43 | ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); | 42 | ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); |
| 43 | ASSERT_OR_EXECUTE(host_ptr != nullptr, { | ||
| 44 | std::fill(program_code.begin(), program_code.end(), 0); | ||
| 45 | return program_code; | ||
| 46 | }); | ||
| 44 | std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64)); | 47 | std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64)); |
| 45 | return program_code; | 48 | return program_code; |
| 46 | } | 49 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index fd1c85115..0cf8e0b3d 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -5,21 +5,20 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <set> | 10 | #include <set> |
| 10 | #include <tuple> | 11 | #include <tuple> |
| 11 | #include <unordered_map> | 12 | #include <unordered_map> |
| 13 | #include <vector> | ||
| 12 | 14 | ||
| 13 | #include <glad/glad.h> | 15 | #include <glad/glad.h> |
| 14 | 16 | ||
| 15 | #include "common/assert.h" | ||
| 16 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 17 | #include "video_core/rasterizer_cache.h" | 18 | #include "video_core/rasterizer_cache.h" |
| 18 | #include "video_core/renderer_base.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 19 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 20 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 20 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 21 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" | 21 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" |
| 22 | #include "video_core/renderer_opengl/gl_shader_gen.h" | ||
| 23 | 22 | ||
| 24 | namespace Core { | 23 | namespace Core { |
| 25 | class System; | 24 | class System; |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 11d1169f0..445048daf 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -21,6 +21,8 @@ | |||
| 21 | 21 | ||
| 22 | namespace OpenGL::GLShader { | 22 | namespace OpenGL::GLShader { |
| 23 | 23 | ||
| 24 | namespace { | ||
| 25 | |||
| 24 | using Tegra::Shader::Attribute; | 26 | using Tegra::Shader::Attribute; |
| 25 | using Tegra::Shader::AttributeUse; | 27 | using Tegra::Shader::AttributeUse; |
| 26 | using Tegra::Shader::Header; | 28 | using Tegra::Shader::Header; |
| @@ -34,13 +36,15 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; | |||
| 34 | using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage; | 36 | using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage; |
| 35 | using Operation = const OperationNode&; | 37 | using Operation = const OperationNode&; |
| 36 | 38 | ||
| 39 | enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat }; | ||
| 40 | |||
| 41 | struct TextureAoffi {}; | ||
| 42 | using TextureArgument = std::pair<Type, Node>; | ||
| 43 | using TextureIR = std::variant<TextureAoffi, TextureArgument>; | ||
| 44 | |||
| 37 | enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; | 45 | enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; |
| 38 | constexpr u32 MAX_CONSTBUFFER_ELEMENTS = | 46 | constexpr u32 MAX_CONSTBUFFER_ELEMENTS = |
| 39 | static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float)); | 47 | static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float)); |
| 40 | constexpr u32 MAX_GLOBALMEMORY_ELEMENTS = | ||
| 41 | static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize) / sizeof(float); | ||
| 42 | |||
| 43 | enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat }; | ||
| 44 | 48 | ||
| 45 | class ShaderWriter { | 49 | class ShaderWriter { |
| 46 | public: | 50 | public: |
| @@ -69,10 +73,10 @@ public: | |||
| 69 | shader_source += '\n'; | 73 | shader_source += '\n'; |
| 70 | } | 74 | } |
| 71 | 75 | ||
| 72 | std::string GenerateTemporal() { | 76 | std::string GenerateTemporary() { |
| 73 | std::string temporal = "tmp"; | 77 | std::string temporary = "tmp"; |
| 74 | temporal += std::to_string(temporal_index++); | 78 | temporary += std::to_string(temporary_index++); |
| 75 | return temporal; | 79 | return temporary; |
| 76 | } | 80 | } |
| 77 | 81 | ||
| 78 | std::string GetResult() { | 82 | std::string GetResult() { |
| @@ -87,11 +91,11 @@ private: | |||
| 87 | } | 91 | } |
| 88 | 92 | ||
| 89 | std::string shader_source; | 93 | std::string shader_source; |
| 90 | u32 temporal_index = 1; | 94 | u32 temporary_index = 1; |
| 91 | }; | 95 | }; |
| 92 | 96 | ||
| 93 | /// Generates code to use for a swizzle operation. | 97 | /// Generates code to use for a swizzle operation. |
| 94 | static std::string GetSwizzle(u32 elem) { | 98 | std::string GetSwizzle(u32 elem) { |
| 95 | ASSERT(elem <= 3); | 99 | ASSERT(elem <= 3); |
| 96 | std::string swizzle = "."; | 100 | std::string swizzle = "."; |
| 97 | swizzle += "xyzw"[elem]; | 101 | swizzle += "xyzw"[elem]; |
| @@ -99,7 +103,7 @@ static std::string GetSwizzle(u32 elem) { | |||
| 99 | } | 103 | } |
| 100 | 104 | ||
| 101 | /// Translate topology | 105 | /// Translate topology |
| 102 | static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { | 106 | std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { |
| 103 | switch (topology) { | 107 | switch (topology) { |
| 104 | case Tegra::Shader::OutputTopology::PointList: | 108 | case Tegra::Shader::OutputTopology::PointList: |
| 105 | return "points"; | 109 | return "points"; |
| @@ -114,7 +118,7 @@ static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { | |||
| 114 | } | 118 | } |
| 115 | 119 | ||
| 116 | /// Returns true if an object has to be treated as precise | 120 | /// Returns true if an object has to be treated as precise |
| 117 | static bool IsPrecise(Operation operand) { | 121 | bool IsPrecise(Operation operand) { |
| 118 | const auto& meta = operand.GetMeta(); | 122 | const auto& meta = operand.GetMeta(); |
| 119 | 123 | ||
| 120 | if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) { | 124 | if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) { |
| @@ -126,7 +130,7 @@ static bool IsPrecise(Operation operand) { | |||
| 126 | return false; | 130 | return false; |
| 127 | } | 131 | } |
| 128 | 132 | ||
| 129 | static bool IsPrecise(Node node) { | 133 | bool IsPrecise(Node node) { |
| 130 | if (const auto operation = std::get_if<OperationNode>(node)) { | 134 | if (const auto operation = std::get_if<OperationNode>(node)) { |
| 131 | return IsPrecise(*operation); | 135 | return IsPrecise(*operation); |
| 132 | } | 136 | } |
| @@ -202,8 +206,10 @@ public: | |||
| 202 | for (const auto& sampler : ir.GetSamplers()) { | 206 | for (const auto& sampler : ir.GetSamplers()) { |
| 203 | entries.samplers.emplace_back(sampler); | 207 | entries.samplers.emplace_back(sampler); |
| 204 | } | 208 | } |
| 205 | for (const auto& gmem : ir.GetGlobalMemoryBases()) { | 209 | for (const auto& gmem_pair : ir.GetGlobalMemory()) { |
| 206 | entries.global_memory_entries.emplace_back(gmem.cbuf_index, gmem.cbuf_offset); | 210 | const auto& [base, usage] = gmem_pair; |
| 211 | entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset, | ||
| 212 | usage.is_read, usage.is_written); | ||
| 207 | } | 213 | } |
| 208 | entries.clip_distances = ir.GetClipDistances(); | 214 | entries.clip_distances = ir.GetClipDistances(); |
| 209 | entries.shader_length = ir.GetLength(); | 215 | entries.shader_length = ir.GetLength(); |
| @@ -374,12 +380,22 @@ private: | |||
| 374 | } | 380 | } |
| 375 | 381 | ||
| 376 | void DeclareGlobalMemory() { | 382 | void DeclareGlobalMemory() { |
| 377 | for (const auto& entry : ir.GetGlobalMemoryBases()) { | 383 | for (const auto& gmem : ir.GetGlobalMemory()) { |
| 384 | const auto& [base, usage] = gmem; | ||
| 385 | |||
| 386 | // Since we don't know how the shader will use the shader, hint the driver to disable as | ||
| 387 | // much optimizations as possible | ||
| 388 | std::string qualifier = "coherent volatile"; | ||
| 389 | if (usage.is_read && !usage.is_written) | ||
| 390 | qualifier += " readonly"; | ||
| 391 | else if (usage.is_written && !usage.is_read) | ||
| 392 | qualifier += " writeonly"; | ||
| 393 | |||
| 378 | const std::string binding = | 394 | const std::string binding = |
| 379 | fmt::format("GMEM_BINDING_{}_{}", entry.cbuf_index, entry.cbuf_offset); | 395 | fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset); |
| 380 | code.AddLine("layout (std430, binding = " + binding + ") buffer " + | 396 | code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " + |
| 381 | GetGlobalMemoryBlock(entry) + " {"); | 397 | GetGlobalMemoryBlock(base) + " {"); |
| 382 | code.AddLine(" float " + GetGlobalMemory(entry) + "[MAX_GLOBALMEMORY_ELEMENTS];"); | 398 | code.AddLine(" float " + GetGlobalMemory(base) + "[];"); |
| 383 | code.AddLine("};"); | 399 | code.AddLine("};"); |
| 384 | code.AddNewLine(); | 400 | code.AddNewLine(); |
| 385 | } | 401 | } |
| @@ -426,9 +442,14 @@ private: | |||
| 426 | std::string Visit(Node node) { | 442 | std::string Visit(Node node) { |
| 427 | if (const auto operation = std::get_if<OperationNode>(node)) { | 443 | if (const auto operation = std::get_if<OperationNode>(node)) { |
| 428 | const auto operation_index = static_cast<std::size_t>(operation->GetCode()); | 444 | const auto operation_index = static_cast<std::size_t>(operation->GetCode()); |
| 445 | if (operation_index >= operation_decompilers.size()) { | ||
| 446 | UNREACHABLE_MSG("Out of bounds operation: {}", operation_index); | ||
| 447 | return {}; | ||
| 448 | } | ||
| 429 | const auto decompiler = operation_decompilers[operation_index]; | 449 | const auto decompiler = operation_decompilers[operation_index]; |
| 430 | if (decompiler == nullptr) { | 450 | if (decompiler == nullptr) { |
| 431 | UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); | 451 | UNREACHABLE_MSG("Undefined operation: {}", operation_index); |
| 452 | return {}; | ||
| 432 | } | 453 | } |
| 433 | return (this->*decompiler)(*operation); | 454 | return (this->*decompiler)(*operation); |
| 434 | 455 | ||
| @@ -540,9 +561,8 @@ private: | |||
| 540 | 561 | ||
| 541 | } else if (std::holds_alternative<OperationNode>(*offset)) { | 562 | } else if (std::holds_alternative<OperationNode>(*offset)) { |
| 542 | // Indirect access | 563 | // Indirect access |
| 543 | const std::string final_offset = code.GenerateTemporal(); | 564 | const std::string final_offset = code.GenerateTemporary(); |
| 544 | code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " + | 565 | code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);"); |
| 545 | std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';'); | ||
| 546 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), | 566 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), |
| 547 | final_offset, final_offset); | 567 | final_offset, final_offset); |
| 548 | 568 | ||
| @@ -587,9 +607,9 @@ private: | |||
| 587 | // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders | 607 | // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders |
| 588 | const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; | 608 | const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; |
| 589 | 609 | ||
| 590 | const std::string temporal = code.GenerateTemporal(); | 610 | const std::string temporary = code.GenerateTemporary(); |
| 591 | code.AddLine(precise + "float " + temporal + " = " + value + ';'); | 611 | code.AddLine(precise + "float " + temporary + " = " + value + ';'); |
| 592 | return temporal; | 612 | return temporary; |
| 593 | } | 613 | } |
| 594 | 614 | ||
| 595 | std::string VisitOperand(Operation operation, std::size_t operand_index) { | 615 | std::string VisitOperand(Operation operation, std::size_t operand_index) { |
| @@ -601,9 +621,9 @@ private: | |||
| 601 | return Visit(operand); | 621 | return Visit(operand); |
| 602 | } | 622 | } |
| 603 | 623 | ||
| 604 | const std::string temporal = code.GenerateTemporal(); | 624 | const std::string temporary = code.GenerateTemporary(); |
| 605 | code.AddLine("float " + temporal + " = " + Visit(operand) + ';'); | 625 | code.AddLine("float " + temporary + " = " + Visit(operand) + ';'); |
| 606 | return temporal; | 626 | return temporary; |
| 607 | } | 627 | } |
| 608 | 628 | ||
| 609 | std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { | 629 | std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { |
| @@ -718,8 +738,8 @@ private: | |||
| 718 | result_type)); | 738 | result_type)); |
| 719 | } | 739 | } |
| 720 | 740 | ||
| 721 | std::string GenerateTexture(Operation operation, const std::string& func, | 741 | std::string GenerateTexture(Operation operation, const std::string& function_suffix, |
| 722 | const std::vector<std::pair<Type, Node>>& extras) { | 742 | const std::vector<TextureIR>& extras) { |
| 723 | constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"}; | 743 | constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"}; |
| 724 | 744 | ||
| 725 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | 745 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); |
| @@ -729,11 +749,11 @@ private: | |||
| 729 | const bool has_array = meta->sampler.IsArray(); | 749 | const bool has_array = meta->sampler.IsArray(); |
| 730 | const bool has_shadow = meta->sampler.IsShadow(); | 750 | const bool has_shadow = meta->sampler.IsShadow(); |
| 731 | 751 | ||
| 732 | std::string expr = func; | 752 | std::string expr = "texture" + function_suffix; |
| 733 | expr += '('; | 753 | if (!meta->aoffi.empty()) { |
| 734 | expr += GetSampler(meta->sampler); | 754 | expr += "Offset"; |
| 735 | expr += ", "; | 755 | } |
| 736 | 756 | expr += '(' + GetSampler(meta->sampler) + ", "; | |
| 737 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1); | 757 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1); |
| 738 | expr += '('; | 758 | expr += '('; |
| 739 | for (std::size_t i = 0; i < count; ++i) { | 759 | for (std::size_t i = 0; i < count; ++i) { |
| @@ -751,36 +771,74 @@ private: | |||
| 751 | } | 771 | } |
| 752 | expr += ')'; | 772 | expr += ')'; |
| 753 | 773 | ||
| 754 | for (const auto& extra_pair : extras) { | 774 | for (const auto& variant : extras) { |
| 755 | const auto [type, operand] = extra_pair; | 775 | if (const auto argument = std::get_if<TextureArgument>(&variant)) { |
| 756 | if (operand == nullptr) { | 776 | expr += GenerateTextureArgument(*argument); |
| 757 | continue; | 777 | } else if (std::get_if<TextureAoffi>(&variant)) { |
| 778 | expr += GenerateTextureAoffi(meta->aoffi); | ||
| 779 | } else { | ||
| 780 | UNREACHABLE(); | ||
| 758 | } | 781 | } |
| 759 | expr += ", "; | 782 | } |
| 760 | 783 | ||
| 761 | switch (type) { | 784 | return expr + ')'; |
| 762 | case Type::Int: | 785 | } |
| 763 | if (const auto immediate = std::get_if<ImmediateNode>(operand)) { | 786 | |
| 764 | // Inline the string as an immediate integer in GLSL (some extra arguments are | 787 | std::string GenerateTextureArgument(TextureArgument argument) { |
| 765 | // required to be constant) | 788 | const auto [type, operand] = argument; |
| 766 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); | 789 | if (operand == nullptr) { |
| 767 | } else { | 790 | return {}; |
| 768 | expr += "ftoi(" + Visit(operand) + ')'; | 791 | } |
| 769 | } | 792 | |
| 770 | break; | 793 | std::string expr = ", "; |
| 771 | case Type::Float: | 794 | switch (type) { |
| 772 | expr += Visit(operand); | 795 | case Type::Int: |
| 773 | break; | 796 | if (const auto immediate = std::get_if<ImmediateNode>(operand)) { |
| 774 | default: { | 797 | // Inline the string as an immediate integer in GLSL (some extra arguments are |
| 775 | const auto type_int = static_cast<u32>(type); | 798 | // required to be constant) |
| 776 | UNIMPLEMENTED_MSG("Unimplemented extra type={}", type_int); | 799 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); |
| 777 | expr += '0'; | 800 | } else { |
| 778 | break; | 801 | expr += "ftoi(" + Visit(operand) + ')'; |
| 802 | } | ||
| 803 | break; | ||
| 804 | case Type::Float: | ||
| 805 | expr += Visit(operand); | ||
| 806 | break; | ||
| 807 | default: { | ||
| 808 | const auto type_int = static_cast<u32>(type); | ||
| 809 | UNIMPLEMENTED_MSG("Unimplemented extra type={}", type_int); | ||
| 810 | expr += '0'; | ||
| 811 | break; | ||
| 812 | } | ||
| 813 | } | ||
| 814 | return expr; | ||
| 815 | } | ||
| 816 | |||
| 817 | std::string GenerateTextureAoffi(const std::vector<Node>& aoffi) { | ||
| 818 | if (aoffi.empty()) { | ||
| 819 | return {}; | ||
| 820 | } | ||
| 821 | constexpr std::array<const char*, 3> coord_constructors = {"int", "ivec2", "ivec3"}; | ||
| 822 | std::string expr = ", "; | ||
| 823 | expr += coord_constructors.at(aoffi.size() - 1); | ||
| 824 | expr += '('; | ||
| 825 | |||
| 826 | for (std::size_t index = 0; index < aoffi.size(); ++index) { | ||
| 827 | const auto operand{aoffi.at(index)}; | ||
| 828 | if (const auto immediate = std::get_if<ImmediateNode>(operand)) { | ||
| 829 | // Inline the string as an immediate integer in GLSL (AOFFI arguments are required | ||
| 830 | // to be constant by the standard). | ||
| 831 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); | ||
| 832 | } else { | ||
| 833 | expr += "ftoi(" + Visit(operand) + ')'; | ||
| 779 | } | 834 | } |
| 835 | if (index + 1 < aoffi.size()) { | ||
| 836 | expr += ", "; | ||
| 780 | } | 837 | } |
| 781 | } | 838 | } |
| 839 | expr += ')'; | ||
| 782 | 840 | ||
| 783 | return expr + ')'; | 841 | return expr; |
| 784 | } | 842 | } |
| 785 | 843 | ||
| 786 | std::string Assign(Operation operation) { | 844 | std::string Assign(Operation operation) { |
| @@ -820,6 +878,12 @@ private: | |||
| 820 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { | 878 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { |
| 821 | target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; | 879 | target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; |
| 822 | 880 | ||
| 881 | } else if (const auto gmem = std::get_if<GmemNode>(dest)) { | ||
| 882 | const std::string real = Visit(gmem->GetRealAddress()); | ||
| 883 | const std::string base = Visit(gmem->GetBaseAddress()); | ||
| 884 | const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; | ||
| 885 | target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); | ||
| 886 | |||
| 823 | } else { | 887 | } else { |
| 824 | UNREACHABLE_MSG("Assign called without a proper target"); | 888 | UNREACHABLE_MSG("Assign called without a proper target"); |
| 825 | } | 889 | } |
| @@ -1159,7 +1223,8 @@ private: | |||
| 1159 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | 1223 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); |
| 1160 | ASSERT(meta); | 1224 | ASSERT(meta); |
| 1161 | 1225 | ||
| 1162 | std::string expr = GenerateTexture(operation, "texture", {{Type::Float, meta->bias}}); | 1226 | std::string expr = GenerateTexture( |
| 1227 | operation, "", {TextureAoffi{}, TextureArgument{Type::Float, meta->bias}}); | ||
| 1163 | if (meta->sampler.IsShadow()) { | 1228 | if (meta->sampler.IsShadow()) { |
| 1164 | expr = "vec4(" + expr + ')'; | 1229 | expr = "vec4(" + expr + ')'; |
| 1165 | } | 1230 | } |
| @@ -1170,7 +1235,8 @@ private: | |||
| 1170 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | 1235 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); |
| 1171 | ASSERT(meta); | 1236 | ASSERT(meta); |
| 1172 | 1237 | ||
| 1173 | std::string expr = GenerateTexture(operation, "textureLod", {{Type::Float, meta->lod}}); | 1238 | std::string expr = GenerateTexture( |
| 1239 | operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureAoffi{}}); | ||
| 1174 | if (meta->sampler.IsShadow()) { | 1240 | if (meta->sampler.IsShadow()) { |
| 1175 | expr = "vec4(" + expr + ')'; | 1241 | expr = "vec4(" + expr + ')'; |
| 1176 | } | 1242 | } |
| @@ -1182,7 +1248,8 @@ private: | |||
| 1182 | ASSERT(meta); | 1248 | ASSERT(meta); |
| 1183 | 1249 | ||
| 1184 | const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int; | 1250 | const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int; |
| 1185 | return GenerateTexture(operation, "textureGather", {{type, meta->component}}) + | 1251 | return GenerateTexture(operation, "Gather", |
| 1252 | {TextureArgument{type, meta->component}, TextureAoffi{}}) + | ||
| 1186 | GetSwizzle(meta->element); | 1253 | GetSwizzle(meta->element); |
| 1187 | } | 1254 | } |
| 1188 | 1255 | ||
| @@ -1196,11 +1263,12 @@ private: | |||
| 1196 | switch (meta->element) { | 1263 | switch (meta->element) { |
| 1197 | case 0: | 1264 | case 0: |
| 1198 | case 1: | 1265 | case 1: |
| 1199 | return "textureSize(" + sampler + ", " + lod + ')' + GetSwizzle(meta->element); | 1266 | return "itof(int(textureSize(" + sampler + ", " + lod + ')' + |
| 1267 | GetSwizzle(meta->element) + "))"; | ||
| 1200 | case 2: | 1268 | case 2: |
| 1201 | return "0"; | 1269 | return "0"; |
| 1202 | case 3: | 1270 | case 3: |
| 1203 | return "textureQueryLevels(" + sampler + ')'; | 1271 | return "itof(textureQueryLevels(" + sampler + "))"; |
| 1204 | } | 1272 | } |
| 1205 | UNREACHABLE(); | 1273 | UNREACHABLE(); |
| 1206 | return "0"; | 1274 | return "0"; |
| @@ -1211,8 +1279,8 @@ private: | |||
| 1211 | ASSERT(meta); | 1279 | ASSERT(meta); |
| 1212 | 1280 | ||
| 1213 | if (meta->element < 2) { | 1281 | if (meta->element < 2) { |
| 1214 | return "itof(int((" + GenerateTexture(operation, "textureQueryLod", {}) + | 1282 | return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" + |
| 1215 | " * vec2(256))" + GetSwizzle(meta->element) + "))"; | 1283 | GetSwizzle(meta->element) + "))"; |
| 1216 | } | 1284 | } |
| 1217 | return "0"; | 1285 | return "0"; |
| 1218 | } | 1286 | } |
| @@ -1565,11 +1633,11 @@ private: | |||
| 1565 | ShaderWriter code; | 1633 | ShaderWriter code; |
| 1566 | }; | 1634 | }; |
| 1567 | 1635 | ||
| 1636 | } // Anonymous namespace | ||
| 1637 | |||
| 1568 | std::string GetCommonDeclarations() { | 1638 | std::string GetCommonDeclarations() { |
| 1569 | const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); | 1639 | const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); |
| 1570 | const auto gmem = std::to_string(MAX_GLOBALMEMORY_ELEMENTS); | ||
| 1571 | return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + | 1640 | return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + |
| 1572 | "#define MAX_GLOBALMEMORY_ELEMENTS " + gmem + "\n" + | ||
| 1573 | "#define ftoi floatBitsToInt\n" | 1641 | "#define ftoi floatBitsToInt\n" |
| 1574 | "#define ftou floatBitsToUint\n" | 1642 | "#define ftou floatBitsToUint\n" |
| 1575 | "#define itof intBitsToFloat\n" | 1643 | "#define itof intBitsToFloat\n" |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 72aca4938..55b3d4d7b 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <set> | ||
| 9 | #include <string> | 8 | #include <string> |
| 10 | #include <utility> | 9 | #include <utility> |
| 11 | #include <vector> | 10 | #include <vector> |
| @@ -40,8 +39,9 @@ private: | |||
| 40 | 39 | ||
| 41 | class GlobalMemoryEntry { | 40 | class GlobalMemoryEntry { |
| 42 | public: | 41 | public: |
| 43 | explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset) | 42 | explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset, bool is_read, bool is_written) |
| 44 | : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} | 43 | : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset}, is_read{is_read}, is_written{ |
| 44 | is_written} {} | ||
| 45 | 45 | ||
| 46 | u32 GetCbufIndex() const { | 46 | u32 GetCbufIndex() const { |
| 47 | return cbuf_index; | 47 | return cbuf_index; |
| @@ -51,9 +51,19 @@ public: | |||
| 51 | return cbuf_offset; | 51 | return cbuf_offset; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | bool IsRead() const { | ||
| 55 | return is_read; | ||
| 56 | } | ||
| 57 | |||
| 58 | bool IsWritten() const { | ||
| 59 | return is_written; | ||
| 60 | } | ||
| 61 | |||
| 54 | private: | 62 | private: |
| 55 | u32 cbuf_index{}; | 63 | u32 cbuf_index{}; |
| 56 | u32 cbuf_offset{}; | 64 | u32 cbuf_offset{}; |
| 65 | bool is_read{}; | ||
| 66 | bool is_written{}; | ||
| 57 | }; | 67 | }; |
| 58 | 68 | ||
| 59 | struct ShaderEntries { | 69 | struct ShaderEntries { |
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 82fc4d44b..d5890a375 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <fmt/format.h> | 6 | #include <fmt/format.h> |
| 7 | #include <lz4.h> | ||
| 8 | 7 | ||
| 9 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 10 | #include "common/common_paths.h" | 9 | #include "common/common_paths.h" |
| @@ -12,6 +11,7 @@ | |||
| 12 | #include "common/file_util.h" | 11 | #include "common/file_util.h" |
| 13 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 14 | #include "common/scm_rev.h" | 13 | #include "common/scm_rev.h" |
| 14 | #include "common/zstd_compression.h" | ||
| 15 | 15 | ||
| 16 | #include "core/core.h" | 16 | #include "core/core.h" |
| 17 | #include "core/hle/kernel/process.h" | 17 | #include "core/hle/kernel/process.h" |
| @@ -49,39 +49,6 @@ ShaderCacheVersionHash GetShaderCacheVersionHash() { | |||
| 49 | return hash; | 49 | return hash; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | template <typename T> | ||
| 53 | std::vector<u8> CompressData(const T* source, std::size_t source_size) { | ||
| 54 | if (source_size > LZ4_MAX_INPUT_SIZE) { | ||
| 55 | // Source size exceeds LZ4 maximum input size | ||
| 56 | return {}; | ||
| 57 | } | ||
| 58 | const auto source_size_int = static_cast<int>(source_size); | ||
| 59 | const int max_compressed_size = LZ4_compressBound(source_size_int); | ||
| 60 | std::vector<u8> compressed(max_compressed_size); | ||
| 61 | const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), | ||
| 62 | reinterpret_cast<char*>(compressed.data()), | ||
| 63 | source_size_int, max_compressed_size); | ||
| 64 | if (compressed_size <= 0) { | ||
| 65 | // Compression failed | ||
| 66 | return {}; | ||
| 67 | } | ||
| 68 | compressed.resize(compressed_size); | ||
| 69 | return compressed; | ||
| 70 | } | ||
| 71 | |||
| 72 | std::vector<u8> DecompressData(const std::vector<u8>& compressed, std::size_t uncompressed_size) { | ||
| 73 | std::vector<u8> uncompressed(uncompressed_size); | ||
| 74 | const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()), | ||
| 75 | reinterpret_cast<char*>(uncompressed.data()), | ||
| 76 | static_cast<int>(compressed.size()), | ||
| 77 | static_cast<int>(uncompressed.size())); | ||
| 78 | if (static_cast<int>(uncompressed_size) != size_check) { | ||
| 79 | // Decompression failed | ||
| 80 | return {}; | ||
| 81 | } | ||
| 82 | return uncompressed; | ||
| 83 | } | ||
| 84 | |||
| 85 | } // namespace | 52 | } // namespace |
| 86 | 53 | ||
| 87 | ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, | 54 | ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, |
| @@ -292,7 +259,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | |||
| 292 | return {}; | 259 | return {}; |
| 293 | } | 260 | } |
| 294 | 261 | ||
| 295 | dump.binary = DecompressData(compressed_binary, binary_length); | 262 | dump.binary = Common::Compression::DecompressDataZSTD(compressed_binary); |
| 296 | if (dump.binary.empty()) { | 263 | if (dump.binary.empty()) { |
| 297 | return {}; | 264 | return {}; |
| 298 | } | 265 | } |
| @@ -321,7 +288,7 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn | |||
| 321 | return {}; | 288 | return {}; |
| 322 | } | 289 | } |
| 323 | 290 | ||
| 324 | const std::vector<u8> code = DecompressData(compressed_code, code_size); | 291 | const std::vector<u8> code = Common::Compression::DecompressDataZSTD(compressed_code); |
| 325 | if (code.empty()) { | 292 | if (code.empty()) { |
| 326 | return {}; | 293 | return {}; |
| 327 | } | 294 | } |
| @@ -370,11 +337,16 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn | |||
| 370 | for (u32 i = 0; i < global_memory_count; ++i) { | 337 | for (u32 i = 0; i < global_memory_count; ++i) { |
| 371 | u32 cbuf_index{}; | 338 | u32 cbuf_index{}; |
| 372 | u32 cbuf_offset{}; | 339 | u32 cbuf_offset{}; |
| 340 | u8 is_read{}; | ||
| 341 | u8 is_written{}; | ||
| 373 | if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || | 342 | if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || |
| 374 | file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32)) { | 343 | file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32) || |
| 344 | file.ReadBytes(&is_read, sizeof(u8)) != sizeof(u8) || | ||
| 345 | file.ReadBytes(&is_written, sizeof(u8)) != sizeof(u8)) { | ||
| 375 | return {}; | 346 | return {}; |
| 376 | } | 347 | } |
| 377 | entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset); | 348 | entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0, |
| 349 | is_written != 0); | ||
| 378 | } | 350 | } |
| 379 | 351 | ||
| 380 | for (auto& clip_distance : entry.entries.clip_distances) { | 352 | for (auto& clip_distance : entry.entries.clip_distances) { |
| @@ -430,7 +402,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 uniqu | |||
| 430 | return false; | 402 | return false; |
| 431 | for (const auto& gmem : entries.global_memory_entries) { | 403 | for (const auto& gmem : entries.global_memory_entries) { |
| 432 | if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || | 404 | if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || |
| 433 | file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1) { | 405 | file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1 || |
| 406 | file.WriteObject(static_cast<u8>(gmem.IsRead() ? 1 : 0)) != 1 || | ||
| 407 | file.WriteObject(static_cast<u8>(gmem.IsWritten() ? 1 : 0)) != 1) { | ||
| 434 | return false; | 408 | return false; |
| 435 | } | 409 | } |
| 436 | } | 410 | } |
| @@ -507,7 +481,8 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str | |||
| 507 | if (!IsUsable()) | 481 | if (!IsUsable()) |
| 508 | return; | 482 | return; |
| 509 | 483 | ||
| 510 | const std::vector<u8> compressed_code{CompressData(code.data(), code.size())}; | 484 | const std::vector<u8> compressed_code{Common::Compression::CompressDataZSTDDefault( |
| 485 | reinterpret_cast<const u8*>(code.data()), code.size())}; | ||
| 511 | if (compressed_code.empty()) { | 486 | if (compressed_code.empty()) { |
| 512 | LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", | 487 | LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", |
| 513 | unique_identifier); | 488 | unique_identifier); |
| @@ -537,7 +512,9 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p | |||
| 537 | std::vector<u8> binary(binary_length); | 512 | std::vector<u8> binary(binary_length); |
| 538 | glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); | 513 | glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); |
| 539 | 514 | ||
| 540 | const std::vector<u8> compressed_binary = CompressData(binary.data(), binary.size()); | 515 | const std::vector<u8> compressed_binary = |
| 516 | Common::Compression::CompressDataZSTDDefault(binary.data(), binary.size()); | ||
| 517 | |||
| 541 | if (compressed_binary.empty()) { | 518 | if (compressed_binary.empty()) { |
| 542 | LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", | 519 | LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", |
| 543 | usage.unique_identifier); | 520 | usage.unique_identifier); |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 7d96649af..8763d9c71 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | 5 | #include <fmt/format.h> |
| 6 | #include "common/assert.h" | ||
| 7 | #include "video_core/engines/maxwell_3d.h" | 6 | #include "video_core/engines/maxwell_3d.h" |
| 8 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 7 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 9 | #include "video_core/renderer_opengl/gl_shader_gen.h" | 8 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index fba8e681b..fad346b48 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -4,12 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | 7 | #include <vector> |
| 10 | 8 | ||
| 11 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 12 | #include "video_core/engines/shader_bytecode.h" | ||
| 13 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 10 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 14 | #include "video_core/shader/shader_ir.h" | 11 | #include "video_core/shader/shader_ir.h" |
| 15 | 12 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 6a30c28d2..05ab01dcb 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -2,23 +2,55 @@ | |||
| 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 "core/core.h" | 5 | #include "common/common_types.h" |
| 6 | #include "video_core/engines/maxwell_3d.h" | ||
| 6 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 7 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 7 | 8 | ||
| 8 | namespace OpenGL::GLShader { | 9 | namespace OpenGL::GLShader { |
| 9 | 10 | ||
| 10 | void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { | 11 | using Tegra::Engines::Maxwell3D; |
| 11 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 12 | |
| 12 | const auto& regs = gpu.regs; | 13 | ProgramManager::ProgramManager() { |
| 13 | const auto& state = gpu.state; | 14 | pipeline.Create(); |
| 15 | } | ||
| 16 | |||
| 17 | ProgramManager::~ProgramManager() = default; | ||
| 18 | |||
| 19 | void ProgramManager::ApplyTo(OpenGLState& state) { | ||
| 20 | UpdatePipeline(); | ||
| 21 | state.draw.shader_program = 0; | ||
| 22 | state.draw.program_pipeline = pipeline.handle; | ||
| 23 | } | ||
| 24 | |||
| 25 | void ProgramManager::UpdatePipeline() { | ||
| 26 | // Avoid updating the pipeline when values have no changed | ||
| 27 | if (old_state == current_state) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | |||
| 31 | // Workaround for AMD bug | ||
| 32 | constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | | ||
| 33 | GL_FRAGMENT_SHADER_BIT}; | ||
| 34 | glUseProgramStages(pipeline.handle, all_used_stages, 0); | ||
| 35 | |||
| 36 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader); | ||
| 37 | glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader); | ||
| 38 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader); | ||
| 39 | |||
| 40 | old_state = current_state; | ||
| 41 | } | ||
| 42 | |||
| 43 | void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { | ||
| 44 | const auto& regs = maxwell.regs; | ||
| 45 | const auto& state = maxwell.state; | ||
| 14 | 46 | ||
| 15 | // TODO(bunnei): Support more than one viewport | 47 | // TODO(bunnei): Support more than one viewport |
| 16 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; | 48 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; |
| 17 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; | 49 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; |
| 18 | 50 | ||
| 19 | u32 func = static_cast<u32>(regs.alpha_test_func); | 51 | auto func{static_cast<u32>(regs.alpha_test_func)}; |
| 20 | // Normalize the gl variants of opCompare to be the same as the normal variants | 52 | // Normalize the gl variants of opCompare to be the same as the normal variants |
| 21 | u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never); | 53 | const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never); |
| 22 | if (func >= op_gl_variant_base) { | 54 | if (func >= op_gl_variant_base) { |
| 23 | func = func - op_gl_variant_base + 1U; | 55 | func = func - op_gl_variant_base + 1U; |
| 24 | } | 56 | } |
| @@ -31,8 +63,9 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh | |||
| 31 | 63 | ||
| 32 | // Assign in which stage the position has to be flipped | 64 | // Assign in which stage the position has to be flipped |
| 33 | // (the last stage before the fragment shader). | 65 | // (the last stage before the fragment shader). |
| 34 | if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { | 66 | constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); |
| 35 | flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); | 67 | if (maxwell.regs.shader_config[geometry_index].enable) { |
| 68 | flip_stage = geometry_index; | ||
| 36 | } else { | 69 | } else { |
| 37 | flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); | 70 | flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); |
| 38 | } | 71 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 4970aafed..cec18a832 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 7 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 8 | 10 | ||
| 9 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 11 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| @@ -12,14 +14,13 @@ | |||
| 12 | 14 | ||
| 13 | namespace OpenGL::GLShader { | 15 | namespace OpenGL::GLShader { |
| 14 | 16 | ||
| 15 | using Tegra::Engines::Maxwell3D; | ||
| 16 | |||
| 17 | /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned | 17 | /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned |
| 18 | // NOTE: Always keep a vec4 at the end. The GL spec is not clear whether the alignment at | 18 | /// @note Always keep a vec4 at the end. The GL spec is not clear whether the alignment at |
| 19 | // the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. | 19 | /// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. |
| 20 | // Not following that rule will cause problems on some AMD drivers. | 20 | /// Not following that rule will cause problems on some AMD drivers. |
| 21 | struct MaxwellUniformData { | 21 | struct MaxwellUniformData { |
| 22 | void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); | 22 | void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage); |
| 23 | |||
| 23 | alignas(16) GLvec4 viewport_flip; | 24 | alignas(16) GLvec4 viewport_flip; |
| 24 | struct alignas(16) { | 25 | struct alignas(16) { |
| 25 | GLuint instance_id; | 26 | GLuint instance_id; |
| @@ -39,56 +40,48 @@ static_assert(sizeof(MaxwellUniformData) < 16384, | |||
| 39 | 40 | ||
| 40 | class ProgramManager { | 41 | class ProgramManager { |
| 41 | public: | 42 | public: |
| 42 | ProgramManager() { | 43 | explicit ProgramManager(); |
| 43 | pipeline.Create(); | 44 | ~ProgramManager(); |
| 44 | } | 45 | |
| 46 | void ApplyTo(OpenGLState& state); | ||
| 45 | 47 | ||
| 46 | void UseProgrammableVertexShader(GLuint program) { | 48 | void UseProgrammableVertexShader(GLuint program) { |
| 47 | vs = program; | 49 | current_state.vertex_shader = program; |
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | void UseProgrammableGeometryShader(GLuint program) { | 52 | void UseProgrammableGeometryShader(GLuint program) { |
| 51 | gs = program; | 53 | current_state.geometry_shader = program; |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | void UseProgrammableFragmentShader(GLuint program) { | 56 | void UseProgrammableFragmentShader(GLuint program) { |
| 55 | fs = program; | 57 | current_state.fragment_shader = program; |
| 56 | } | 58 | } |
| 57 | 59 | ||
| 58 | void UseTrivialGeometryShader() { | 60 | void UseTrivialGeometryShader() { |
| 59 | gs = 0; | 61 | current_state.geometry_shader = 0; |
| 60 | } | ||
| 61 | |||
| 62 | void ApplyTo(OpenGLState& state) { | ||
| 63 | UpdatePipeline(); | ||
| 64 | state.draw.shader_program = 0; | ||
| 65 | state.draw.program_pipeline = pipeline.handle; | ||
| 66 | state.geometry_shaders.enabled = (gs != 0); | ||
| 67 | } | 62 | } |
| 68 | 63 | ||
| 69 | private: | 64 | private: |
| 70 | void UpdatePipeline() { | 65 | struct PipelineState { |
| 71 | // Avoid updating the pipeline when values have no changed | 66 | bool operator==(const PipelineState& rhs) const { |
| 72 | if (old_vs == vs && old_fs == fs && old_gs == gs) | 67 | return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader && |
| 73 | return; | 68 | geometry_shader == rhs.geometry_shader; |
| 74 | // Workaround for AMD bug | 69 | } |
| 75 | glUseProgramStages(pipeline.handle, | 70 | |
| 76 | GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, | 71 | bool operator!=(const PipelineState& rhs) const { |
| 77 | 0); | 72 | return !operator==(rhs); |
| 78 | 73 | } | |
| 79 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vs); | 74 | |
| 80 | glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, gs); | 75 | GLuint vertex_shader{}; |
| 81 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); | 76 | GLuint fragment_shader{}; |
| 82 | 77 | GLuint geometry_shader{}; | |
| 83 | // Update the old values | 78 | }; |
| 84 | old_vs = vs; | 79 | |
| 85 | old_fs = fs; | 80 | void UpdatePipeline(); |
| 86 | old_gs = gs; | ||
| 87 | } | ||
| 88 | 81 | ||
| 89 | OGLPipeline pipeline; | 82 | OGLPipeline pipeline; |
| 90 | GLuint vs{}, fs{}, gs{}; | 83 | PipelineState current_state; |
| 91 | GLuint old_vs{}, old_fs{}, old_gs{}; | 84 | PipelineState old_state; |
| 92 | }; | 85 | }; |
| 93 | 86 | ||
| 94 | } // namespace OpenGL::GLShader | 87 | } // namespace OpenGL::GLShader |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 9419326a3..52d569a1b 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -10,16 +10,62 @@ | |||
| 10 | 10 | ||
| 11 | namespace OpenGL { | 11 | namespace OpenGL { |
| 12 | 12 | ||
| 13 | OpenGLState OpenGLState::cur_state; | 13 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 14 | 14 | ||
| 15 | OpenGLState OpenGLState::cur_state; | ||
| 15 | bool OpenGLState::s_rgb_used; | 16 | bool OpenGLState::s_rgb_used; |
| 16 | 17 | ||
| 18 | namespace { | ||
| 19 | |||
| 20 | template <typename T> | ||
| 21 | bool UpdateValue(T& current_value, const T new_value) { | ||
| 22 | const bool changed = current_value != new_value; | ||
| 23 | current_value = new_value; | ||
| 24 | return changed; | ||
| 25 | } | ||
| 26 | |||
| 27 | template <typename T1, typename T2> | ||
| 28 | bool UpdateTie(T1 current_value, const T2 new_value) { | ||
| 29 | const bool changed = current_value != new_value; | ||
| 30 | current_value = new_value; | ||
| 31 | return changed; | ||
| 32 | } | ||
| 33 | |||
| 34 | void Enable(GLenum cap, bool enable) { | ||
| 35 | if (enable) { | ||
| 36 | glEnable(cap); | ||
| 37 | } else { | ||
| 38 | glDisable(cap); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | void Enable(GLenum cap, GLuint index, bool enable) { | ||
| 43 | if (enable) { | ||
| 44 | glEnablei(cap, index); | ||
| 45 | } else { | ||
| 46 | glDisablei(cap, index); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void Enable(GLenum cap, bool& current_value, bool new_value) { | ||
| 51 | if (UpdateValue(current_value, new_value)) | ||
| 52 | Enable(cap, new_value); | ||
| 53 | } | ||
| 54 | |||
| 55 | void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) { | ||
| 56 | if (UpdateValue(current_value, new_value)) | ||
| 57 | Enable(cap, index, new_value); | ||
| 58 | } | ||
| 59 | |||
| 60 | } // namespace | ||
| 61 | |||
| 17 | OpenGLState::OpenGLState() { | 62 | OpenGLState::OpenGLState() { |
| 18 | // These all match default OpenGL values | 63 | // These all match default OpenGL values |
| 19 | geometry_shaders.enabled = false; | ||
| 20 | framebuffer_srgb.enabled = false; | 64 | framebuffer_srgb.enabled = false; |
| 65 | |||
| 21 | multisample_control.alpha_to_coverage = false; | 66 | multisample_control.alpha_to_coverage = false; |
| 22 | multisample_control.alpha_to_one = false; | 67 | multisample_control.alpha_to_one = false; |
| 68 | |||
| 23 | cull.enabled = false; | 69 | cull.enabled = false; |
| 24 | cull.mode = GL_BACK; | 70 | cull.mode = GL_BACK; |
| 25 | cull.front_face = GL_CCW; | 71 | cull.front_face = GL_CCW; |
| @@ -30,14 +76,15 @@ OpenGLState::OpenGLState() { | |||
| 30 | 76 | ||
| 31 | primitive_restart.enabled = false; | 77 | primitive_restart.enabled = false; |
| 32 | primitive_restart.index = 0; | 78 | primitive_restart.index = 0; |
| 79 | |||
| 33 | for (auto& item : color_mask) { | 80 | for (auto& item : color_mask) { |
| 34 | item.red_enabled = GL_TRUE; | 81 | item.red_enabled = GL_TRUE; |
| 35 | item.green_enabled = GL_TRUE; | 82 | item.green_enabled = GL_TRUE; |
| 36 | item.blue_enabled = GL_TRUE; | 83 | item.blue_enabled = GL_TRUE; |
| 37 | item.alpha_enabled = GL_TRUE; | 84 | item.alpha_enabled = GL_TRUE; |
| 38 | } | 85 | } |
| 39 | stencil.test_enabled = false; | 86 | |
| 40 | auto reset_stencil = [](auto& config) { | 87 | const auto ResetStencil = [](auto& config) { |
| 41 | config.test_func = GL_ALWAYS; | 88 | config.test_func = GL_ALWAYS; |
| 42 | config.test_ref = 0; | 89 | config.test_ref = 0; |
| 43 | config.test_mask = 0xFFFFFFFF; | 90 | config.test_mask = 0xFFFFFFFF; |
| @@ -46,8 +93,10 @@ OpenGLState::OpenGLState() { | |||
| 46 | config.action_depth_pass = GL_KEEP; | 93 | config.action_depth_pass = GL_KEEP; |
| 47 | config.action_stencil_fail = GL_KEEP; | 94 | config.action_stencil_fail = GL_KEEP; |
| 48 | }; | 95 | }; |
| 49 | reset_stencil(stencil.front); | 96 | stencil.test_enabled = false; |
| 50 | reset_stencil(stencil.back); | 97 | ResetStencil(stencil.front); |
| 98 | ResetStencil(stencil.back); | ||
| 99 | |||
| 51 | for (auto& item : viewports) { | 100 | for (auto& item : viewports) { |
| 52 | item.x = 0; | 101 | item.x = 0; |
| 53 | item.y = 0; | 102 | item.y = 0; |
| @@ -61,6 +110,7 @@ OpenGLState::OpenGLState() { | |||
| 61 | item.scissor.width = 0; | 110 | item.scissor.width = 0; |
| 62 | item.scissor.height = 0; | 111 | item.scissor.height = 0; |
| 63 | } | 112 | } |
| 113 | |||
| 64 | for (auto& item : blend) { | 114 | for (auto& item : blend) { |
| 65 | item.enabled = true; | 115 | item.enabled = true; |
| 66 | item.rgb_equation = GL_FUNC_ADD; | 116 | item.rgb_equation = GL_FUNC_ADD; |
| @@ -70,11 +120,14 @@ OpenGLState::OpenGLState() { | |||
| 70 | item.src_a_func = GL_ONE; | 120 | item.src_a_func = GL_ONE; |
| 71 | item.dst_a_func = GL_ZERO; | 121 | item.dst_a_func = GL_ZERO; |
| 72 | } | 122 | } |
| 123 | |||
| 73 | independant_blend.enabled = false; | 124 | independant_blend.enabled = false; |
| 125 | |||
| 74 | blend_color.red = 0.0f; | 126 | blend_color.red = 0.0f; |
| 75 | blend_color.green = 0.0f; | 127 | blend_color.green = 0.0f; |
| 76 | blend_color.blue = 0.0f; | 128 | blend_color.blue = 0.0f; |
| 77 | blend_color.alpha = 0.0f; | 129 | blend_color.alpha = 0.0f; |
| 130 | |||
| 78 | logic_op.enabled = false; | 131 | logic_op.enabled = false; |
| 79 | logic_op.operation = GL_COPY; | 132 | logic_op.operation = GL_COPY; |
| 80 | 133 | ||
| @@ -91,9 +144,12 @@ OpenGLState::OpenGLState() { | |||
| 91 | clip_distance = {}; | 144 | clip_distance = {}; |
| 92 | 145 | ||
| 93 | point.size = 1; | 146 | point.size = 1; |
| 147 | |||
| 94 | fragment_color_clamp.enabled = false; | 148 | fragment_color_clamp.enabled = false; |
| 149 | |||
| 95 | depth_clamp.far_plane = false; | 150 | depth_clamp.far_plane = false; |
| 96 | depth_clamp.near_plane = false; | 151 | depth_clamp.near_plane = false; |
| 152 | |||
| 97 | polygon_offset.fill_enable = false; | 153 | polygon_offset.fill_enable = false; |
| 98 | polygon_offset.line_enable = false; | 154 | polygon_offset.line_enable = false; |
| 99 | polygon_offset.point_enable = false; | 155 | polygon_offset.point_enable = false; |
| @@ -103,260 +159,255 @@ OpenGLState::OpenGLState() { | |||
| 103 | } | 159 | } |
| 104 | 160 | ||
| 105 | void OpenGLState::ApplyDefaultState() { | 161 | void OpenGLState::ApplyDefaultState() { |
| 162 | glEnable(GL_BLEND); | ||
| 106 | glDisable(GL_FRAMEBUFFER_SRGB); | 163 | glDisable(GL_FRAMEBUFFER_SRGB); |
| 107 | glDisable(GL_CULL_FACE); | 164 | glDisable(GL_CULL_FACE); |
| 108 | glDisable(GL_DEPTH_TEST); | 165 | glDisable(GL_DEPTH_TEST); |
| 109 | glDisable(GL_PRIMITIVE_RESTART); | 166 | glDisable(GL_PRIMITIVE_RESTART); |
| 110 | glDisable(GL_STENCIL_TEST); | 167 | glDisable(GL_STENCIL_TEST); |
| 111 | glEnable(GL_BLEND); | ||
| 112 | glDisable(GL_COLOR_LOGIC_OP); | 168 | glDisable(GL_COLOR_LOGIC_OP); |
| 113 | glDisable(GL_SCISSOR_TEST); | 169 | glDisable(GL_SCISSOR_TEST); |
| 114 | } | 170 | } |
| 115 | 171 | ||
| 172 | void OpenGLState::ApplyFramebufferState() const { | ||
| 173 | if (UpdateValue(cur_state.draw.read_framebuffer, draw.read_framebuffer)) { | ||
| 174 | glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); | ||
| 175 | } | ||
| 176 | if (UpdateValue(cur_state.draw.draw_framebuffer, draw.draw_framebuffer)) { | ||
| 177 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | void OpenGLState::ApplyVertexArrayState() const { | ||
| 182 | if (UpdateValue(cur_state.draw.vertex_array, draw.vertex_array)) { | ||
| 183 | glBindVertexArray(draw.vertex_array); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | void OpenGLState::ApplyShaderProgram() const { | ||
| 188 | if (UpdateValue(cur_state.draw.shader_program, draw.shader_program)) { | ||
| 189 | glUseProgram(draw.shader_program); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | void OpenGLState::ApplyProgramPipeline() const { | ||
| 194 | if (UpdateValue(cur_state.draw.program_pipeline, draw.program_pipeline)) { | ||
| 195 | glBindProgramPipeline(draw.program_pipeline); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | void OpenGLState::ApplyClipDistances() const { | ||
| 200 | for (std::size_t i = 0; i < clip_distance.size(); ++i) { | ||
| 201 | Enable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i), cur_state.clip_distance[i], | ||
| 202 | clip_distance[i]); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | void OpenGLState::ApplyPointSize() const { | ||
| 207 | if (UpdateValue(cur_state.point.size, point.size)) { | ||
| 208 | glPointSize(point.size); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | void OpenGLState::ApplyFragmentColorClamp() const { | ||
| 213 | if (UpdateValue(cur_state.fragment_color_clamp.enabled, fragment_color_clamp.enabled)) { | ||
| 214 | glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB, | ||
| 215 | fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE); | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | void OpenGLState::ApplyMultisample() const { | ||
| 220 | Enable(GL_SAMPLE_ALPHA_TO_COVERAGE, cur_state.multisample_control.alpha_to_coverage, | ||
| 221 | multisample_control.alpha_to_coverage); | ||
| 222 | Enable(GL_SAMPLE_ALPHA_TO_ONE, cur_state.multisample_control.alpha_to_one, | ||
| 223 | multisample_control.alpha_to_one); | ||
| 224 | } | ||
| 225 | |||
| 226 | void OpenGLState::ApplyDepthClamp() const { | ||
| 227 | if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane && | ||
| 228 | depth_clamp.near_plane == cur_state.depth_clamp.near_plane) { | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | cur_state.depth_clamp = depth_clamp; | ||
| 232 | |||
| 233 | UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane, | ||
| 234 | "Unimplemented Depth Clamp Separation!"); | ||
| 235 | |||
| 236 | Enable(GL_DEPTH_CLAMP, depth_clamp.far_plane || depth_clamp.near_plane); | ||
| 237 | } | ||
| 238 | |||
| 116 | void OpenGLState::ApplySRgb() const { | 239 | void OpenGLState::ApplySRgb() const { |
| 117 | if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { | 240 | if (cur_state.framebuffer_srgb.enabled == framebuffer_srgb.enabled) |
| 118 | if (framebuffer_srgb.enabled) { | 241 | return; |
| 119 | // Track if sRGB is used | 242 | cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled; |
| 120 | s_rgb_used = true; | 243 | if (framebuffer_srgb.enabled) { |
| 121 | glEnable(GL_FRAMEBUFFER_SRGB); | 244 | // Track if sRGB is used |
| 122 | } else { | 245 | s_rgb_used = true; |
| 123 | glDisable(GL_FRAMEBUFFER_SRGB); | 246 | glEnable(GL_FRAMEBUFFER_SRGB); |
| 124 | } | 247 | } else { |
| 248 | glDisable(GL_FRAMEBUFFER_SRGB); | ||
| 125 | } | 249 | } |
| 126 | } | 250 | } |
| 127 | 251 | ||
| 128 | void OpenGLState::ApplyCulling() const { | 252 | void OpenGLState::ApplyCulling() const { |
| 129 | if (cull.enabled != cur_state.cull.enabled) { | 253 | Enable(GL_CULL_FACE, cur_state.cull.enabled, cull.enabled); |
| 130 | if (cull.enabled) { | ||
| 131 | glEnable(GL_CULL_FACE); | ||
| 132 | } else { | ||
| 133 | glDisable(GL_CULL_FACE); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | 254 | ||
| 137 | if (cull.mode != cur_state.cull.mode) { | 255 | if (UpdateValue(cur_state.cull.mode, cull.mode)) { |
| 138 | glCullFace(cull.mode); | 256 | glCullFace(cull.mode); |
| 139 | } | 257 | } |
| 140 | 258 | ||
| 141 | if (cull.front_face != cur_state.cull.front_face) { | 259 | if (UpdateValue(cur_state.cull.front_face, cull.front_face)) { |
| 142 | glFrontFace(cull.front_face); | 260 | glFrontFace(cull.front_face); |
| 143 | } | 261 | } |
| 144 | } | 262 | } |
| 145 | 263 | ||
| 146 | void OpenGLState::ApplyColorMask() const { | 264 | void OpenGLState::ApplyColorMask() const { |
| 147 | if (independant_blend.enabled) { | 265 | for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) { |
| 148 | for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 266 | const auto& updated = color_mask[i]; |
| 149 | const auto& updated = color_mask[i]; | 267 | auto& current = cur_state.color_mask[i]; |
| 150 | const auto& current = cur_state.color_mask[i]; | ||
| 151 | if (updated.red_enabled != current.red_enabled || | ||
| 152 | updated.green_enabled != current.green_enabled || | ||
| 153 | updated.blue_enabled != current.blue_enabled || | ||
| 154 | updated.alpha_enabled != current.alpha_enabled) { | ||
| 155 | glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled, | ||
| 156 | updated.blue_enabled, updated.alpha_enabled); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } else { | ||
| 160 | const auto& updated = color_mask[0]; | ||
| 161 | const auto& current = cur_state.color_mask[0]; | ||
| 162 | if (updated.red_enabled != current.red_enabled || | 268 | if (updated.red_enabled != current.red_enabled || |
| 163 | updated.green_enabled != current.green_enabled || | 269 | updated.green_enabled != current.green_enabled || |
| 164 | updated.blue_enabled != current.blue_enabled || | 270 | updated.blue_enabled != current.blue_enabled || |
| 165 | updated.alpha_enabled != current.alpha_enabled) { | 271 | updated.alpha_enabled != current.alpha_enabled) { |
| 166 | glColorMask(updated.red_enabled, updated.green_enabled, updated.blue_enabled, | 272 | current = updated; |
| 167 | updated.alpha_enabled); | 273 | glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled, |
| 274 | updated.blue_enabled, updated.alpha_enabled); | ||
| 168 | } | 275 | } |
| 169 | } | 276 | } |
| 170 | } | 277 | } |
| 171 | 278 | ||
| 172 | void OpenGLState::ApplyDepth() const { | 279 | void OpenGLState::ApplyDepth() const { |
| 173 | if (depth.test_enabled != cur_state.depth.test_enabled) { | 280 | Enable(GL_DEPTH_TEST, cur_state.depth.test_enabled, depth.test_enabled); |
| 174 | if (depth.test_enabled) { | ||
| 175 | glEnable(GL_DEPTH_TEST); | ||
| 176 | } else { | ||
| 177 | glDisable(GL_DEPTH_TEST); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | 281 | ||
| 181 | if (depth.test_func != cur_state.depth.test_func) { | 282 | if (cur_state.depth.test_func != depth.test_func) { |
| 283 | cur_state.depth.test_func = depth.test_func; | ||
| 182 | glDepthFunc(depth.test_func); | 284 | glDepthFunc(depth.test_func); |
| 183 | } | 285 | } |
| 184 | 286 | ||
| 185 | if (depth.write_mask != cur_state.depth.write_mask) { | 287 | if (cur_state.depth.write_mask != depth.write_mask) { |
| 288 | cur_state.depth.write_mask = depth.write_mask; | ||
| 186 | glDepthMask(depth.write_mask); | 289 | glDepthMask(depth.write_mask); |
| 187 | } | 290 | } |
| 188 | } | 291 | } |
| 189 | 292 | ||
| 190 | void OpenGLState::ApplyPrimitiveRestart() const { | 293 | void OpenGLState::ApplyPrimitiveRestart() const { |
| 191 | if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { | 294 | Enable(GL_PRIMITIVE_RESTART, cur_state.primitive_restart.enabled, primitive_restart.enabled); |
| 192 | if (primitive_restart.enabled) { | ||
| 193 | glEnable(GL_PRIMITIVE_RESTART); | ||
| 194 | } else { | ||
| 195 | glDisable(GL_PRIMITIVE_RESTART); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | 295 | ||
| 199 | if (primitive_restart.index != cur_state.primitive_restart.index) { | 296 | if (cur_state.primitive_restart.index != primitive_restart.index) { |
| 297 | cur_state.primitive_restart.index = primitive_restart.index; | ||
| 200 | glPrimitiveRestartIndex(primitive_restart.index); | 298 | glPrimitiveRestartIndex(primitive_restart.index); |
| 201 | } | 299 | } |
| 202 | } | 300 | } |
| 203 | 301 | ||
| 204 | void OpenGLState::ApplyStencilTest() const { | 302 | void OpenGLState::ApplyStencilTest() const { |
| 205 | if (stencil.test_enabled != cur_state.stencil.test_enabled) { | 303 | Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled); |
| 206 | if (stencil.test_enabled) { | 304 | |
| 207 | glEnable(GL_STENCIL_TEST); | 305 | const auto ConfigStencil = [](GLenum face, const auto& config, auto& current) { |
| 208 | } else { | 306 | if (current.test_func != config.test_func || current.test_ref != config.test_ref || |
| 209 | glDisable(GL_STENCIL_TEST); | 307 | current.test_mask != config.test_mask) { |
| 210 | } | 308 | current.test_func = config.test_func; |
| 211 | } | 309 | current.test_ref = config.test_ref; |
| 212 | 310 | current.test_mask = config.test_mask; | |
| 213 | const auto ConfigStencil = [](GLenum face, const auto& config, const auto& prev_config) { | ||
| 214 | if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || | ||
| 215 | config.test_mask != prev_config.test_mask) { | ||
| 216 | glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); | 311 | glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); |
| 217 | } | 312 | } |
| 218 | if (config.action_depth_fail != prev_config.action_depth_fail || | 313 | if (current.action_depth_fail != config.action_depth_fail || |
| 219 | config.action_depth_pass != prev_config.action_depth_pass || | 314 | current.action_depth_pass != config.action_depth_pass || |
| 220 | config.action_stencil_fail != prev_config.action_stencil_fail) { | 315 | current.action_stencil_fail != config.action_stencil_fail) { |
| 316 | current.action_depth_fail = config.action_depth_fail; | ||
| 317 | current.action_depth_pass = config.action_depth_pass; | ||
| 318 | current.action_stencil_fail = config.action_stencil_fail; | ||
| 221 | glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, | 319 | glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, |
| 222 | config.action_depth_pass); | 320 | config.action_depth_pass); |
| 223 | } | 321 | } |
| 224 | if (config.write_mask != prev_config.write_mask) { | 322 | if (current.write_mask != config.write_mask) { |
| 323 | current.write_mask = config.write_mask; | ||
| 225 | glStencilMaskSeparate(face, config.write_mask); | 324 | glStencilMaskSeparate(face, config.write_mask); |
| 226 | } | 325 | } |
| 227 | }; | 326 | }; |
| 228 | ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front); | 327 | ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front); |
| 229 | ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back); | 328 | ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back); |
| 230 | } | 329 | } |
| 231 | // Viewport does not affects glClearBuffer so emulate viewport using scissor test | ||
| 232 | void OpenGLState::EmulateViewportWithScissor() { | ||
| 233 | auto& current = viewports[0]; | ||
| 234 | if (current.scissor.enabled) { | ||
| 235 | const GLint left = std::max(current.x, current.scissor.x); | ||
| 236 | const GLint right = | ||
| 237 | std::max(current.x + current.width, current.scissor.x + current.scissor.width); | ||
| 238 | const GLint bottom = std::max(current.y, current.scissor.y); | ||
| 239 | const GLint top = | ||
| 240 | std::max(current.y + current.height, current.scissor.y + current.scissor.height); | ||
| 241 | current.scissor.x = std::max(left, 0); | ||
| 242 | current.scissor.y = std::max(bottom, 0); | ||
| 243 | current.scissor.width = std::max(right - left, 0); | ||
| 244 | current.scissor.height = std::max(top - bottom, 0); | ||
| 245 | } else { | ||
| 246 | current.scissor.enabled = true; | ||
| 247 | current.scissor.x = current.x; | ||
| 248 | current.scissor.y = current.y; | ||
| 249 | current.scissor.width = current.width; | ||
| 250 | current.scissor.height = current.height; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | 330 | ||
| 254 | void OpenGLState::ApplyViewport() const { | 331 | void OpenGLState::ApplyViewport() const { |
| 255 | if (geometry_shaders.enabled) { | 332 | for (GLuint i = 0; i < static_cast<GLuint>(Maxwell::NumViewports); ++i) { |
| 256 | for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports); | 333 | const auto& updated = viewports[i]; |
| 257 | i++) { | 334 | auto& current = cur_state.viewports[i]; |
| 258 | const auto& current = cur_state.viewports[i]; | 335 | |
| 259 | const auto& updated = viewports[i]; | 336 | if (current.x != updated.x || current.y != updated.y || current.width != updated.width || |
| 260 | if (updated.x != current.x || updated.y != current.y || | 337 | current.height != updated.height) { |
| 261 | updated.width != current.width || updated.height != current.height) { | 338 | current.x = updated.x; |
| 262 | glViewportIndexedf( | 339 | current.y = updated.y; |
| 263 | i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y), | 340 | current.width = updated.width; |
| 264 | static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height)); | 341 | current.height = updated.height; |
| 265 | } | 342 | glViewportIndexedf(i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y), |
| 266 | if (updated.depth_range_near != current.depth_range_near || | 343 | static_cast<GLfloat>(updated.width), |
| 267 | updated.depth_range_far != current.depth_range_far) { | 344 | static_cast<GLfloat>(updated.height)); |
| 268 | glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); | ||
| 269 | } | ||
| 270 | |||
| 271 | if (updated.scissor.enabled != current.scissor.enabled) { | ||
| 272 | if (updated.scissor.enabled) { | ||
| 273 | glEnablei(GL_SCISSOR_TEST, i); | ||
| 274 | } else { | ||
| 275 | glDisablei(GL_SCISSOR_TEST, i); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y || | ||
| 280 | updated.scissor.width != current.scissor.width || | ||
| 281 | updated.scissor.height != current.scissor.height) { | ||
| 282 | glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width, | ||
| 283 | updated.scissor.height); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | } else { | ||
| 287 | const auto& current = cur_state.viewports[0]; | ||
| 288 | const auto& updated = viewports[0]; | ||
| 289 | if (updated.x != current.x || updated.y != current.y || updated.width != current.width || | ||
| 290 | updated.height != current.height) { | ||
| 291 | glViewport(updated.x, updated.y, updated.width, updated.height); | ||
| 292 | } | ||
| 293 | |||
| 294 | if (updated.depth_range_near != current.depth_range_near || | ||
| 295 | updated.depth_range_far != current.depth_range_far) { | ||
| 296 | glDepthRange(updated.depth_range_near, updated.depth_range_far); | ||
| 297 | } | 345 | } |
| 298 | 346 | if (current.depth_range_near != updated.depth_range_near || | |
| 299 | if (updated.scissor.enabled != current.scissor.enabled) { | 347 | current.depth_range_far != updated.depth_range_far) { |
| 300 | if (updated.scissor.enabled) { | 348 | current.depth_range_near = updated.depth_range_near; |
| 301 | glEnable(GL_SCISSOR_TEST); | 349 | current.depth_range_far = updated.depth_range_far; |
| 302 | } else { | 350 | glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); |
| 303 | glDisable(GL_SCISSOR_TEST); | ||
| 304 | } | ||
| 305 | } | 351 | } |
| 306 | 352 | ||
| 307 | if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y || | 353 | Enable(GL_SCISSOR_TEST, i, current.scissor.enabled, updated.scissor.enabled); |
| 308 | updated.scissor.width != current.scissor.width || | 354 | |
| 309 | updated.scissor.height != current.scissor.height) { | 355 | if (current.scissor.x != updated.scissor.x || current.scissor.y != updated.scissor.y || |
| 310 | glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width, | 356 | current.scissor.width != updated.scissor.width || |
| 311 | updated.scissor.height); | 357 | current.scissor.height != updated.scissor.height) { |
| 358 | current.scissor.x = updated.scissor.x; | ||
| 359 | current.scissor.y = updated.scissor.y; | ||
| 360 | current.scissor.width = updated.scissor.width; | ||
| 361 | current.scissor.height = updated.scissor.height; | ||
| 362 | glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width, | ||
| 363 | updated.scissor.height); | ||
| 312 | } | 364 | } |
| 313 | } | 365 | } |
| 314 | } | 366 | } |
| 315 | 367 | ||
| 316 | void OpenGLState::ApplyGlobalBlending() const { | 368 | void OpenGLState::ApplyGlobalBlending() const { |
| 317 | const Blend& current = cur_state.blend[0]; | ||
| 318 | const Blend& updated = blend[0]; | 369 | const Blend& updated = blend[0]; |
| 319 | if (updated.enabled != current.enabled) { | 370 | Blend& current = cur_state.blend[0]; |
| 320 | if (updated.enabled) { | 371 | |
| 321 | glEnable(GL_BLEND); | 372 | Enable(GL_BLEND, current.enabled, updated.enabled); |
| 322 | } else { | 373 | |
| 323 | glDisable(GL_BLEND); | 374 | if (current.src_rgb_func != updated.src_rgb_func || |
| 324 | } | 375 | current.dst_rgb_func != updated.dst_rgb_func || current.src_a_func != updated.src_a_func || |
| 325 | } | 376 | current.dst_a_func != updated.dst_a_func) { |
| 326 | if (!updated.enabled) { | 377 | current.src_rgb_func = updated.src_rgb_func; |
| 327 | return; | 378 | current.dst_rgb_func = updated.dst_rgb_func; |
| 328 | } | 379 | current.src_a_func = updated.src_a_func; |
| 329 | if (updated.src_rgb_func != current.src_rgb_func || | 380 | current.dst_a_func = updated.dst_a_func; |
| 330 | updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || | ||
| 331 | updated.dst_a_func != current.dst_a_func) { | ||
| 332 | glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, | 381 | glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, |
| 333 | updated.dst_a_func); | 382 | updated.dst_a_func); |
| 334 | } | 383 | } |
| 335 | 384 | ||
| 336 | if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) { | 385 | if (current.rgb_equation != updated.rgb_equation || current.a_equation != updated.a_equation) { |
| 386 | current.rgb_equation = updated.rgb_equation; | ||
| 387 | current.a_equation = updated.a_equation; | ||
| 337 | glBlendEquationSeparate(updated.rgb_equation, updated.a_equation); | 388 | glBlendEquationSeparate(updated.rgb_equation, updated.a_equation); |
| 338 | } | 389 | } |
| 339 | } | 390 | } |
| 340 | 391 | ||
| 341 | void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { | 392 | void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { |
| 342 | const Blend& updated = blend[target]; | 393 | const Blend& updated = blend[target]; |
| 343 | const Blend& current = cur_state.blend[target]; | 394 | Blend& current = cur_state.blend[target]; |
| 344 | if (updated.enabled != current.enabled || force) { | 395 | |
| 345 | if (updated.enabled) { | 396 | if (current.enabled != updated.enabled || force) { |
| 346 | glEnablei(GL_BLEND, static_cast<GLuint>(target)); | 397 | current.enabled = updated.enabled; |
| 347 | } else { | 398 | Enable(GL_BLEND, static_cast<GLuint>(target), updated.enabled); |
| 348 | glDisablei(GL_BLEND, static_cast<GLuint>(target)); | ||
| 349 | } | ||
| 350 | } | 399 | } |
| 351 | 400 | ||
| 352 | if (updated.src_rgb_func != current.src_rgb_func || | 401 | if (UpdateTie(std::tie(current.src_rgb_func, current.dst_rgb_func, current.src_a_func, |
| 353 | updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || | 402 | current.dst_a_func), |
| 354 | updated.dst_a_func != current.dst_a_func) { | 403 | std::tie(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, |
| 404 | updated.dst_a_func))) { | ||
| 355 | glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func, | 405 | glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func, |
| 356 | updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); | 406 | updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); |
| 357 | } | 407 | } |
| 358 | 408 | ||
| 359 | if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) { | 409 | if (UpdateTie(std::tie(current.rgb_equation, current.a_equation), |
| 410 | std::tie(updated.rgb_equation, updated.a_equation))) { | ||
| 360 | glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation, | 411 | glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation, |
| 361 | updated.a_equation); | 412 | updated.a_equation); |
| 362 | } | 413 | } |
| @@ -364,77 +415,48 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { | |||
| 364 | 415 | ||
| 365 | void OpenGLState::ApplyBlending() const { | 416 | void OpenGLState::ApplyBlending() const { |
| 366 | if (independant_blend.enabled) { | 417 | if (independant_blend.enabled) { |
| 367 | for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 418 | const bool force = independant_blend.enabled != cur_state.independant_blend.enabled; |
| 368 | ApplyTargetBlending(i, | 419 | for (std::size_t target = 0; target < Maxwell::NumRenderTargets; ++target) { |
| 369 | independant_blend.enabled != cur_state.independant_blend.enabled); | 420 | ApplyTargetBlending(target, force); |
| 370 | } | 421 | } |
| 371 | } else { | 422 | } else { |
| 372 | ApplyGlobalBlending(); | 423 | ApplyGlobalBlending(); |
| 373 | } | 424 | } |
| 374 | if (blend_color.red != cur_state.blend_color.red || | 425 | cur_state.independant_blend.enabled = independant_blend.enabled; |
| 375 | blend_color.green != cur_state.blend_color.green || | 426 | |
| 376 | blend_color.blue != cur_state.blend_color.blue || | 427 | if (UpdateTie( |
| 377 | blend_color.alpha != cur_state.blend_color.alpha) { | 428 | std::tie(cur_state.blend_color.red, cur_state.blend_color.green, |
| 429 | cur_state.blend_color.blue, cur_state.blend_color.alpha), | ||
| 430 | std::tie(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha))) { | ||
| 378 | glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha); | 431 | glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha); |
| 379 | } | 432 | } |
| 380 | } | 433 | } |
| 381 | 434 | ||
| 382 | void OpenGLState::ApplyLogicOp() const { | 435 | void OpenGLState::ApplyLogicOp() const { |
| 383 | if (logic_op.enabled != cur_state.logic_op.enabled) { | 436 | Enable(GL_COLOR_LOGIC_OP, cur_state.logic_op.enabled, logic_op.enabled); |
| 384 | if (logic_op.enabled) { | ||
| 385 | glEnable(GL_COLOR_LOGIC_OP); | ||
| 386 | } else { | ||
| 387 | glDisable(GL_COLOR_LOGIC_OP); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | 437 | ||
| 391 | if (logic_op.operation != cur_state.logic_op.operation) { | 438 | if (UpdateValue(cur_state.logic_op.operation, logic_op.operation)) { |
| 392 | glLogicOp(logic_op.operation); | 439 | glLogicOp(logic_op.operation); |
| 393 | } | 440 | } |
| 394 | } | 441 | } |
| 395 | 442 | ||
| 396 | void OpenGLState::ApplyPolygonOffset() const { | 443 | void OpenGLState::ApplyPolygonOffset() const { |
| 397 | const bool fill_enable_changed = | 444 | Enable(GL_POLYGON_OFFSET_FILL, cur_state.polygon_offset.fill_enable, |
| 398 | polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable; | 445 | polygon_offset.fill_enable); |
| 399 | const bool line_enable_changed = | 446 | Enable(GL_POLYGON_OFFSET_LINE, cur_state.polygon_offset.line_enable, |
| 400 | polygon_offset.line_enable != cur_state.polygon_offset.line_enable; | 447 | polygon_offset.line_enable); |
| 401 | const bool point_enable_changed = | 448 | Enable(GL_POLYGON_OFFSET_POINT, cur_state.polygon_offset.point_enable, |
| 402 | polygon_offset.point_enable != cur_state.polygon_offset.point_enable; | 449 | polygon_offset.point_enable); |
| 403 | const bool factor_changed = polygon_offset.factor != cur_state.polygon_offset.factor; | 450 | |
| 404 | const bool units_changed = polygon_offset.units != cur_state.polygon_offset.units; | 451 | if (UpdateTie(std::tie(cur_state.polygon_offset.factor, cur_state.polygon_offset.units, |
| 405 | const bool clamp_changed = polygon_offset.clamp != cur_state.polygon_offset.clamp; | 452 | cur_state.polygon_offset.clamp), |
| 406 | 453 | std::tie(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp))) { | |
| 407 | if (fill_enable_changed) { | ||
| 408 | if (polygon_offset.fill_enable) { | ||
| 409 | glEnable(GL_POLYGON_OFFSET_FILL); | ||
| 410 | } else { | ||
| 411 | glDisable(GL_POLYGON_OFFSET_FILL); | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | if (line_enable_changed) { | ||
| 416 | if (polygon_offset.line_enable) { | ||
| 417 | glEnable(GL_POLYGON_OFFSET_LINE); | ||
| 418 | } else { | ||
| 419 | glDisable(GL_POLYGON_OFFSET_LINE); | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | if (point_enable_changed) { | ||
| 424 | if (polygon_offset.point_enable) { | ||
| 425 | glEnable(GL_POLYGON_OFFSET_POINT); | ||
| 426 | } else { | ||
| 427 | glDisable(GL_POLYGON_OFFSET_POINT); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | if (factor_changed || units_changed || clamp_changed) { | ||
| 432 | if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) { | 454 | if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) { |
| 433 | glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp); | 455 | glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp); |
| 434 | } else { | 456 | } else { |
| 435 | glPolygonOffset(polygon_offset.factor, polygon_offset.units); | ||
| 436 | UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0, | 457 | UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0, |
| 437 | "Unimplemented Depth polygon offset clamp."); | 458 | "Unimplemented Depth polygon offset clamp."); |
| 459 | glPolygonOffset(polygon_offset.factor, polygon_offset.units); | ||
| 438 | } | 460 | } |
| 439 | } | 461 | } |
| 440 | } | 462 | } |
| @@ -443,22 +465,21 @@ void OpenGLState::ApplyTextures() const { | |||
| 443 | bool has_delta{}; | 465 | bool has_delta{}; |
| 444 | std::size_t first{}; | 466 | std::size_t first{}; |
| 445 | std::size_t last{}; | 467 | std::size_t last{}; |
| 446 | std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> textures; | 468 | std::array<GLuint, Maxwell::NumTextureSamplers> textures; |
| 447 | 469 | ||
| 448 | for (std::size_t i = 0; i < std::size(texture_units); ++i) { | 470 | for (std::size_t i = 0; i < std::size(texture_units); ++i) { |
| 449 | const auto& texture_unit = texture_units[i]; | 471 | const auto& texture_unit = texture_units[i]; |
| 450 | const auto& cur_state_texture_unit = cur_state.texture_units[i]; | 472 | auto& cur_state_texture_unit = cur_state.texture_units[i]; |
| 451 | textures[i] = texture_unit.texture; | 473 | textures[i] = texture_unit.texture; |
| 452 | 474 | if (cur_state_texture_unit.texture == textures[i]) | |
| 453 | if (textures[i] != cur_state_texture_unit.texture) { | 475 | continue; |
| 454 | if (!has_delta) { | 476 | cur_state_texture_unit.texture = textures[i]; |
| 455 | first = i; | 477 | if (!has_delta) { |
| 456 | has_delta = true; | 478 | first = i; |
| 457 | } | 479 | has_delta = true; |
| 458 | last = i; | ||
| 459 | } | 480 | } |
| 481 | last = i; | ||
| 460 | } | 482 | } |
| 461 | |||
| 462 | if (has_delta) { | 483 | if (has_delta) { |
| 463 | glBindTextures(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), | 484 | glBindTextures(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), |
| 464 | textures.data() + first); | 485 | textures.data() + first); |
| @@ -469,16 +490,18 @@ void OpenGLState::ApplySamplers() const { | |||
| 469 | bool has_delta{}; | 490 | bool has_delta{}; |
| 470 | std::size_t first{}; | 491 | std::size_t first{}; |
| 471 | std::size_t last{}; | 492 | std::size_t last{}; |
| 472 | std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; | 493 | std::array<GLuint, Maxwell::NumTextureSamplers> samplers; |
| 494 | |||
| 473 | for (std::size_t i = 0; i < std::size(samplers); ++i) { | 495 | for (std::size_t i = 0; i < std::size(samplers); ++i) { |
| 496 | if (cur_state.texture_units[i].sampler == texture_units[i].sampler) | ||
| 497 | continue; | ||
| 498 | cur_state.texture_units[i].sampler = texture_units[i].sampler; | ||
| 474 | samplers[i] = texture_units[i].sampler; | 499 | samplers[i] = texture_units[i].sampler; |
| 475 | if (samplers[i] != cur_state.texture_units[i].sampler) { | 500 | if (!has_delta) { |
| 476 | if (!has_delta) { | 501 | first = i; |
| 477 | first = i; | 502 | has_delta = true; |
| 478 | has_delta = true; | ||
| 479 | } | ||
| 480 | last = i; | ||
| 481 | } | 503 | } |
| 504 | last = i; | ||
| 482 | } | 505 | } |
| 483 | if (has_delta) { | 506 | if (has_delta) { |
| 484 | glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), | 507 | glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), |
| @@ -486,81 +509,15 @@ void OpenGLState::ApplySamplers() const { | |||
| 486 | } | 509 | } |
| 487 | } | 510 | } |
| 488 | 511 | ||
| 489 | void OpenGLState::ApplyFramebufferState() const { | ||
| 490 | if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { | ||
| 491 | glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); | ||
| 492 | } | ||
| 493 | if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) { | ||
| 494 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | void OpenGLState::ApplyVertexArrayState() const { | ||
| 499 | if (draw.vertex_array != cur_state.draw.vertex_array) { | ||
| 500 | glBindVertexArray(draw.vertex_array); | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | void OpenGLState::ApplyDepthClamp() const { | ||
| 505 | if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane && | ||
| 506 | depth_clamp.near_plane == cur_state.depth_clamp.near_plane) { | ||
| 507 | return; | ||
| 508 | } | ||
| 509 | UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane, | ||
| 510 | "Unimplemented Depth Clamp Separation!"); | ||
| 511 | |||
| 512 | if (depth_clamp.far_plane || depth_clamp.near_plane) { | ||
| 513 | glEnable(GL_DEPTH_CLAMP); | ||
| 514 | } else { | ||
| 515 | glDisable(GL_DEPTH_CLAMP); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | void OpenGLState::Apply() const { | 512 | void OpenGLState::Apply() const { |
| 520 | ApplyFramebufferState(); | 513 | ApplyFramebufferState(); |
| 521 | ApplyVertexArrayState(); | 514 | ApplyVertexArrayState(); |
| 522 | 515 | ApplyShaderProgram(); | |
| 523 | // Shader program | 516 | ApplyProgramPipeline(); |
| 524 | if (draw.shader_program != cur_state.draw.shader_program) { | 517 | ApplyClipDistances(); |
| 525 | glUseProgram(draw.shader_program); | 518 | ApplyPointSize(); |
| 526 | } | 519 | ApplyFragmentColorClamp(); |
| 527 | 520 | ApplyMultisample(); | |
| 528 | // Program pipeline | ||
| 529 | if (draw.program_pipeline != cur_state.draw.program_pipeline) { | ||
| 530 | glBindProgramPipeline(draw.program_pipeline); | ||
| 531 | } | ||
| 532 | // Clip distance | ||
| 533 | for (std::size_t i = 0; i < clip_distance.size(); ++i) { | ||
| 534 | if (clip_distance[i] != cur_state.clip_distance[i]) { | ||
| 535 | if (clip_distance[i]) { | ||
| 536 | glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); | ||
| 537 | } else { | ||
| 538 | glDisable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | } | ||
| 542 | // Point | ||
| 543 | if (point.size != cur_state.point.size) { | ||
| 544 | glPointSize(point.size); | ||
| 545 | } | ||
| 546 | if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) { | ||
| 547 | glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB, | ||
| 548 | fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE); | ||
| 549 | } | ||
| 550 | if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) { | ||
| 551 | if (multisample_control.alpha_to_coverage) { | ||
| 552 | glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); | ||
| 553 | } else { | ||
| 554 | glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) { | ||
| 558 | if (multisample_control.alpha_to_one) { | ||
| 559 | glEnable(GL_SAMPLE_ALPHA_TO_ONE); | ||
| 560 | } else { | ||
| 561 | glDisable(GL_SAMPLE_ALPHA_TO_ONE); | ||
| 562 | } | ||
| 563 | } | ||
| 564 | ApplyDepthClamp(); | 521 | ApplyDepthClamp(); |
| 565 | ApplyColorMask(); | 522 | ApplyColorMask(); |
| 566 | ApplyViewport(); | 523 | ApplyViewport(); |
| @@ -574,7 +531,28 @@ void OpenGLState::Apply() const { | |||
| 574 | ApplyTextures(); | 531 | ApplyTextures(); |
| 575 | ApplySamplers(); | 532 | ApplySamplers(); |
| 576 | ApplyPolygonOffset(); | 533 | ApplyPolygonOffset(); |
| 577 | cur_state = *this; | 534 | } |
| 535 | |||
| 536 | void OpenGLState::EmulateViewportWithScissor() { | ||
| 537 | auto& current = viewports[0]; | ||
| 538 | if (current.scissor.enabled) { | ||
| 539 | const GLint left = std::max(current.x, current.scissor.x); | ||
| 540 | const GLint right = | ||
| 541 | std::max(current.x + current.width, current.scissor.x + current.scissor.width); | ||
| 542 | const GLint bottom = std::max(current.y, current.scissor.y); | ||
| 543 | const GLint top = | ||
| 544 | std::max(current.y + current.height, current.scissor.y + current.scissor.height); | ||
| 545 | current.scissor.x = std::max(left, 0); | ||
| 546 | current.scissor.y = std::max(bottom, 0); | ||
| 547 | current.scissor.width = std::max(right - left, 0); | ||
| 548 | current.scissor.height = std::max(top - bottom, 0); | ||
| 549 | } else { | ||
| 550 | current.scissor.enabled = true; | ||
| 551 | current.scissor.x = current.x; | ||
| 552 | current.scissor.y = current.y; | ||
| 553 | current.scissor.width = current.width; | ||
| 554 | current.scissor.height = current.height; | ||
| 555 | } | ||
| 578 | } | 556 | } |
| 579 | 557 | ||
| 580 | OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { | 558 | OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 9e1eda5b1..41418a7b8 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -54,10 +54,6 @@ public: | |||
| 54 | } depth_clamp; // GL_DEPTH_CLAMP | 54 | } depth_clamp; // GL_DEPTH_CLAMP |
| 55 | 55 | ||
| 56 | struct { | 56 | struct { |
| 57 | bool enabled; // viewports arrays are only supported when geometry shaders are enabled. | ||
| 58 | } geometry_shaders; | ||
| 59 | |||
| 60 | struct { | ||
| 61 | bool enabled; // GL_CULL_FACE | 57 | bool enabled; // GL_CULL_FACE |
| 62 | GLenum mode; // GL_CULL_FACE_MODE | 58 | GLenum mode; // GL_CULL_FACE_MODE |
| 63 | GLenum front_face; // GL_FRONT_FACE | 59 | GLenum front_face; // GL_FRONT_FACE |
| @@ -184,34 +180,26 @@ public: | |||
| 184 | static OpenGLState GetCurState() { | 180 | static OpenGLState GetCurState() { |
| 185 | return cur_state; | 181 | return cur_state; |
| 186 | } | 182 | } |
| 183 | |||
| 187 | static bool GetsRGBUsed() { | 184 | static bool GetsRGBUsed() { |
| 188 | return s_rgb_used; | 185 | return s_rgb_used; |
| 189 | } | 186 | } |
| 187 | |||
| 190 | static void ClearsRGBUsed() { | 188 | static void ClearsRGBUsed() { |
| 191 | s_rgb_used = false; | 189 | s_rgb_used = false; |
| 192 | } | 190 | } |
| 191 | |||
| 193 | /// Apply this state as the current OpenGL state | 192 | /// Apply this state as the current OpenGL state |
| 194 | void Apply() const; | 193 | void Apply() const; |
| 195 | /// Apply only the state affecting the framebuffer | 194 | |
| 196 | void ApplyFramebufferState() const; | 195 | void ApplyFramebufferState() const; |
| 197 | /// Apply only the state affecting the vertex array | ||
| 198 | void ApplyVertexArrayState() const; | 196 | void ApplyVertexArrayState() const; |
| 199 | /// Set the initial OpenGL state | 197 | void ApplyShaderProgram() const; |
| 200 | static void ApplyDefaultState(); | 198 | void ApplyProgramPipeline() const; |
| 201 | /// Resets any references to the given resource | 199 | void ApplyClipDistances() const; |
| 202 | OpenGLState& UnbindTexture(GLuint handle); | 200 | void ApplyPointSize() const; |
| 203 | OpenGLState& ResetSampler(GLuint handle); | 201 | void ApplyFragmentColorClamp() const; |
| 204 | OpenGLState& ResetProgram(GLuint handle); | 202 | void ApplyMultisample() const; |
| 205 | OpenGLState& ResetPipeline(GLuint handle); | ||
| 206 | OpenGLState& ResetVertexArray(GLuint handle); | ||
| 207 | OpenGLState& ResetFramebuffer(GLuint handle); | ||
| 208 | void EmulateViewportWithScissor(); | ||
| 209 | |||
| 210 | private: | ||
| 211 | static OpenGLState cur_state; | ||
| 212 | // Workaround for sRGB problems caused by | ||
| 213 | // QT not supporting srgb output | ||
| 214 | static bool s_rgb_used; | ||
| 215 | void ApplySRgb() const; | 203 | void ApplySRgb() const; |
| 216 | void ApplyCulling() const; | 204 | void ApplyCulling() const; |
| 217 | void ApplyColorMask() const; | 205 | void ApplyColorMask() const; |
| @@ -227,6 +215,26 @@ private: | |||
| 227 | void ApplySamplers() const; | 215 | void ApplySamplers() const; |
| 228 | void ApplyDepthClamp() const; | 216 | void ApplyDepthClamp() const; |
| 229 | void ApplyPolygonOffset() const; | 217 | void ApplyPolygonOffset() const; |
| 218 | |||
| 219 | /// Set the initial OpenGL state | ||
| 220 | static void ApplyDefaultState(); | ||
| 221 | |||
| 222 | /// Resets any references to the given resource | ||
| 223 | OpenGLState& UnbindTexture(GLuint handle); | ||
| 224 | OpenGLState& ResetSampler(GLuint handle); | ||
| 225 | OpenGLState& ResetProgram(GLuint handle); | ||
| 226 | OpenGLState& ResetPipeline(GLuint handle); | ||
| 227 | OpenGLState& ResetVertexArray(GLuint handle); | ||
| 228 | OpenGLState& ResetFramebuffer(GLuint handle); | ||
| 229 | |||
| 230 | /// Viewport does not affects glClearBuffer so emulate viewport using scissor test | ||
| 231 | void EmulateViewportWithScissor(); | ||
| 232 | |||
| 233 | private: | ||
| 234 | static OpenGLState cur_state; | ||
| 235 | |||
| 236 | // Workaround for sRGB problems caused by QT not supporting srgb output | ||
| 237 | static bool s_rgb_used; | ||
| 230 | }; | 238 | }; |
| 231 | 239 | ||
| 232 | } // namespace OpenGL | 240 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index a01efeb05..d69cba9c3 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstddef> | 6 | #include <cstddef> |
| 7 | #include <cstdlib> | 7 | #include <cstdlib> |
| 8 | #include <cstring> | ||
| 9 | #include <memory> | 8 | #include <memory> |
| 10 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 11 | #include "common/assert.h" | 10 | #include "common/assert.h" |
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp index d84634cb3..84a987371 100644 --- a/src/video_core/renderer_opengl/utils.cpp +++ b/src/video_core/renderer_opengl/utils.cpp | |||
| @@ -5,11 +5,39 @@ | |||
| 5 | #include <string> | 5 | #include <string> |
| 6 | #include <fmt/format.h> | 6 | #include <fmt/format.h> |
| 7 | #include <glad/glad.h> | 7 | #include <glad/glad.h> |
| 8 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "video_core/renderer_opengl/utils.h" | 10 | #include "video_core/renderer_opengl/utils.h" |
| 10 | 11 | ||
| 11 | namespace OpenGL { | 12 | namespace OpenGL { |
| 12 | 13 | ||
| 14 | BindBuffersRangePushBuffer::BindBuffersRangePushBuffer(GLenum target) : target{target} {} | ||
| 15 | |||
| 16 | BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default; | ||
| 17 | |||
| 18 | void BindBuffersRangePushBuffer::Setup(GLuint first_) { | ||
| 19 | first = first_; | ||
| 20 | buffers.clear(); | ||
| 21 | offsets.clear(); | ||
| 22 | sizes.clear(); | ||
| 23 | } | ||
| 24 | |||
| 25 | void BindBuffersRangePushBuffer::Push(GLuint buffer, GLintptr offset, GLsizeiptr size) { | ||
| 26 | buffers.push_back(buffer); | ||
| 27 | offsets.push_back(offset); | ||
| 28 | sizes.push_back(size); | ||
| 29 | } | ||
| 30 | |||
| 31 | void BindBuffersRangePushBuffer::Bind() const { | ||
| 32 | const std::size_t count{buffers.size()}; | ||
| 33 | DEBUG_ASSERT(count == offsets.size() && count == sizes.size()); | ||
| 34 | if (count == 0) { | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | glBindBuffersRange(target, first, static_cast<GLsizei>(count), buffers.data(), offsets.data(), | ||
| 38 | sizes.data()); | ||
| 39 | } | ||
| 40 | |||
| 13 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { | 41 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { |
| 14 | if (!GLAD_GL_KHR_debug) { | 42 | if (!GLAD_GL_KHR_debug) { |
| 15 | return; // We don't need to throw an error as this is just for debugging | 43 | return; // We don't need to throw an error as this is just for debugging |
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h index 1fcb6fc11..aef45c9dc 100644 --- a/src/video_core/renderer_opengl/utils.h +++ b/src/video_core/renderer_opengl/utils.h | |||
| @@ -5,11 +5,31 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <vector> | ||
| 8 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| 11 | namespace OpenGL { | 12 | namespace OpenGL { |
| 12 | 13 | ||
| 14 | class BindBuffersRangePushBuffer { | ||
| 15 | public: | ||
| 16 | BindBuffersRangePushBuffer(GLenum target); | ||
| 17 | ~BindBuffersRangePushBuffer(); | ||
| 18 | |||
| 19 | void Setup(GLuint first_); | ||
| 20 | |||
| 21 | void Push(GLuint buffer, GLintptr offset, GLsizeiptr size); | ||
| 22 | |||
| 23 | void Bind() const; | ||
| 24 | |||
| 25 | private: | ||
| 26 | GLenum target; | ||
| 27 | GLuint first; | ||
| 28 | std::vector<GLuint> buffers; | ||
| 29 | std::vector<GLintptr> offsets; | ||
| 30 | std::vector<GLsizeiptr> sizes; | ||
| 31 | }; | ||
| 32 | |||
| 13 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); | 33 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); |
| 14 | 34 | ||
| 15 | } // namespace OpenGL \ No newline at end of file | 35 | } // namespace OpenGL \ No newline at end of file |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 388b5ffd5..02a9f5ecb 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 11 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 12 | #include "core/memory.h" | 12 | #include "core/memory.h" |
| 13 | #include "video_core/memory_manager.h" | ||
| 13 | #include "video_core/renderer_vulkan/declarations.h" | 14 | #include "video_core/renderer_vulkan/declarations.h" |
| 14 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | 15 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" |
| 15 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 16 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp new file mode 100644 index 000000000..25500f9a3 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -0,0 +1,1381 @@ | |||
| 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 <functional> | ||
| 6 | #include <map> | ||
| 7 | #include <set> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include <sirit/sirit.h> | ||
| 12 | |||
| 13 | #include "common/alignment.h" | ||
| 14 | #include "common/assert.h" | ||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "common/logging/log.h" | ||
| 17 | #include "video_core/engines/maxwell_3d.h" | ||
| 18 | #include "video_core/engines/shader_bytecode.h" | ||
| 19 | #include "video_core/engines/shader_header.h" | ||
| 20 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | ||
| 21 | #include "video_core/shader/shader_ir.h" | ||
| 22 | |||
| 23 | namespace Vulkan::VKShader { | ||
| 24 | |||
| 25 | using Sirit::Id; | ||
| 26 | using Tegra::Shader::Attribute; | ||
| 27 | using Tegra::Shader::AttributeUse; | ||
| 28 | using Tegra::Shader::Register; | ||
| 29 | using namespace VideoCommon::Shader; | ||
| 30 | |||
| 31 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 32 | using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage; | ||
| 33 | using Operation = const OperationNode&; | ||
| 34 | |||
| 35 | // TODO(Rodrigo): Use rasterizer's value | ||
| 36 | constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 0x1000; | ||
| 37 | constexpr u32 STAGE_BINDING_STRIDE = 0x100; | ||
| 38 | |||
| 39 | enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat }; | ||
| 40 | |||
| 41 | struct SamplerImage { | ||
| 42 | Id image_type; | ||
| 43 | Id sampled_image_type; | ||
| 44 | Id sampler; | ||
| 45 | }; | ||
| 46 | |||
| 47 | namespace { | ||
| 48 | |||
| 49 | spv::Dim GetSamplerDim(const Sampler& sampler) { | ||
| 50 | switch (sampler.GetType()) { | ||
| 51 | case Tegra::Shader::TextureType::Texture1D: | ||
| 52 | return spv::Dim::Dim1D; | ||
| 53 | case Tegra::Shader::TextureType::Texture2D: | ||
| 54 | return spv::Dim::Dim2D; | ||
| 55 | case Tegra::Shader::TextureType::Texture3D: | ||
| 56 | return spv::Dim::Dim3D; | ||
| 57 | case Tegra::Shader::TextureType::TextureCube: | ||
| 58 | return spv::Dim::Cube; | ||
| 59 | default: | ||
| 60 | UNIMPLEMENTED_MSG("Unimplemented sampler type={}", static_cast<u32>(sampler.GetType())); | ||
| 61 | return spv::Dim::Dim2D; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Returns true if an attribute index is one of the 32 generic attributes | ||
| 66 | constexpr bool IsGenericAttribute(Attribute::Index attribute) { | ||
| 67 | return attribute >= Attribute::Index::Attribute_0 && | ||
| 68 | attribute <= Attribute::Index::Attribute_31; | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Returns the location of a generic attribute | ||
| 72 | constexpr u32 GetGenericAttributeLocation(Attribute::Index attribute) { | ||
| 73 | ASSERT(IsGenericAttribute(attribute)); | ||
| 74 | return static_cast<u32>(attribute) - static_cast<u32>(Attribute::Index::Attribute_0); | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Returns true if an object has to be treated as precise | ||
| 78 | bool IsPrecise(Operation operand) { | ||
| 79 | const auto& meta = operand.GetMeta(); | ||
| 80 | |||
| 81 | if (std::holds_alternative<MetaArithmetic>(meta)) { | ||
| 82 | return std::get<MetaArithmetic>(meta).precise; | ||
| 83 | } | ||
| 84 | if (std::holds_alternative<MetaHalfArithmetic>(meta)) { | ||
| 85 | return std::get<MetaHalfArithmetic>(meta).precise; | ||
| 86 | } | ||
| 87 | return false; | ||
| 88 | } | ||
| 89 | |||
| 90 | } // namespace | ||
| 91 | |||
| 92 | class SPIRVDecompiler : public Sirit::Module { | ||
| 93 | public: | ||
| 94 | explicit SPIRVDecompiler(const ShaderIR& ir, ShaderStage stage) | ||
| 95 | : Module(0x00010300), ir{ir}, stage{stage}, header{ir.GetHeader()} { | ||
| 96 | AddCapability(spv::Capability::Shader); | ||
| 97 | AddExtension("SPV_KHR_storage_buffer_storage_class"); | ||
| 98 | AddExtension("SPV_KHR_variable_pointers"); | ||
| 99 | } | ||
| 100 | |||
| 101 | void Decompile() { | ||
| 102 | AllocateBindings(); | ||
| 103 | AllocateLabels(); | ||
| 104 | |||
| 105 | DeclareVertex(); | ||
| 106 | DeclareGeometry(); | ||
| 107 | DeclareFragment(); | ||
| 108 | DeclareRegisters(); | ||
| 109 | DeclarePredicates(); | ||
| 110 | DeclareLocalMemory(); | ||
| 111 | DeclareInternalFlags(); | ||
| 112 | DeclareInputAttributes(); | ||
| 113 | DeclareOutputAttributes(); | ||
| 114 | DeclareConstantBuffers(); | ||
| 115 | DeclareGlobalBuffers(); | ||
| 116 | DeclareSamplers(); | ||
| 117 | |||
| 118 | execute_function = | ||
| 119 | Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); | ||
| 120 | Emit(OpLabel()); | ||
| 121 | |||
| 122 | const u32 first_address = ir.GetBasicBlocks().begin()->first; | ||
| 123 | const Id loop_label = OpLabel("loop"); | ||
| 124 | const Id merge_label = OpLabel("merge"); | ||
| 125 | const Id dummy_label = OpLabel(); | ||
| 126 | const Id jump_label = OpLabel(); | ||
| 127 | continue_label = OpLabel("continue"); | ||
| 128 | |||
| 129 | std::vector<Sirit::Literal> literals; | ||
| 130 | std::vector<Id> branch_labels; | ||
| 131 | for (const auto& pair : labels) { | ||
| 132 | const auto [literal, label] = pair; | ||
| 133 | literals.push_back(literal); | ||
| 134 | branch_labels.push_back(label); | ||
| 135 | } | ||
| 136 | |||
| 137 | // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely | ||
| 138 | // that shaders will use 20 nested SSYs and PBKs. | ||
| 139 | constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 140 | const Id flow_stack_type = TypeArray(t_uint, Constant(t_uint, FLOW_STACK_SIZE)); | ||
| 141 | jmp_to = Emit(OpVariable(TypePointer(spv::StorageClass::Function, t_uint), | ||
| 142 | spv::StorageClass::Function, Constant(t_uint, first_address))); | ||
| 143 | flow_stack = Emit(OpVariable(TypePointer(spv::StorageClass::Function, flow_stack_type), | ||
| 144 | spv::StorageClass::Function, ConstantNull(flow_stack_type))); | ||
| 145 | flow_stack_top = | ||
| 146 | Emit(OpVariable(t_func_uint, spv::StorageClass::Function, Constant(t_uint, 0))); | ||
| 147 | |||
| 148 | Name(jmp_to, "jmp_to"); | ||
| 149 | Name(flow_stack, "flow_stack"); | ||
| 150 | Name(flow_stack_top, "flow_stack_top"); | ||
| 151 | |||
| 152 | Emit(OpBranch(loop_label)); | ||
| 153 | Emit(loop_label); | ||
| 154 | Emit(OpLoopMerge(merge_label, continue_label, spv::LoopControlMask::Unroll)); | ||
| 155 | Emit(OpBranch(dummy_label)); | ||
| 156 | |||
| 157 | Emit(dummy_label); | ||
| 158 | const Id default_branch = OpLabel(); | ||
| 159 | const Id jmp_to_load = Emit(OpLoad(t_uint, jmp_to)); | ||
| 160 | Emit(OpSelectionMerge(jump_label, spv::SelectionControlMask::MaskNone)); | ||
| 161 | Emit(OpSwitch(jmp_to_load, default_branch, literals, branch_labels)); | ||
| 162 | |||
| 163 | Emit(default_branch); | ||
| 164 | Emit(OpReturn()); | ||
| 165 | |||
| 166 | for (const auto& pair : ir.GetBasicBlocks()) { | ||
| 167 | const auto& [address, bb] = pair; | ||
| 168 | Emit(labels.at(address)); | ||
| 169 | |||
| 170 | VisitBasicBlock(bb); | ||
| 171 | |||
| 172 | const auto next_it = labels.lower_bound(address + 1); | ||
| 173 | const Id next_label = next_it != labels.end() ? next_it->second : default_branch; | ||
| 174 | Emit(OpBranch(next_label)); | ||
| 175 | } | ||
| 176 | |||
| 177 | Emit(jump_label); | ||
| 178 | Emit(OpBranch(continue_label)); | ||
| 179 | Emit(continue_label); | ||
| 180 | Emit(OpBranch(loop_label)); | ||
| 181 | Emit(merge_label); | ||
| 182 | Emit(OpReturn()); | ||
| 183 | Emit(OpFunctionEnd()); | ||
| 184 | } | ||
| 185 | |||
| 186 | ShaderEntries GetShaderEntries() const { | ||
| 187 | ShaderEntries entries; | ||
| 188 | entries.const_buffers_base_binding = const_buffers_base_binding; | ||
| 189 | entries.global_buffers_base_binding = global_buffers_base_binding; | ||
| 190 | entries.samplers_base_binding = samplers_base_binding; | ||
| 191 | for (const auto& cbuf : ir.GetConstantBuffers()) { | ||
| 192 | entries.const_buffers.emplace_back(cbuf.second, cbuf.first); | ||
| 193 | } | ||
| 194 | for (const auto& gmem_pair : ir.GetGlobalMemory()) { | ||
| 195 | const auto& [base, usage] = gmem_pair; | ||
| 196 | entries.global_buffers.emplace_back(base.cbuf_index, base.cbuf_offset); | ||
| 197 | } | ||
| 198 | for (const auto& sampler : ir.GetSamplers()) { | ||
| 199 | entries.samplers.emplace_back(sampler); | ||
| 200 | } | ||
| 201 | for (const auto& attr : ir.GetInputAttributes()) { | ||
| 202 | entries.attributes.insert(GetGenericAttributeLocation(attr.first)); | ||
| 203 | } | ||
| 204 | entries.clip_distances = ir.GetClipDistances(); | ||
| 205 | entries.shader_length = ir.GetLength(); | ||
| 206 | entries.entry_function = execute_function; | ||
| 207 | entries.interfaces = interfaces; | ||
| 208 | return entries; | ||
| 209 | } | ||
| 210 | |||
| 211 | private: | ||
| 212 | using OperationDecompilerFn = Id (SPIRVDecompiler::*)(Operation); | ||
| 213 | using OperationDecompilersArray = | ||
| 214 | std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>; | ||
| 215 | |||
| 216 | static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount); | ||
| 217 | static constexpr u32 CBUF_STRIDE = 16; | ||
| 218 | |||
| 219 | void AllocateBindings() { | ||
| 220 | const u32 binding_base = static_cast<u32>(stage) * STAGE_BINDING_STRIDE; | ||
| 221 | u32 binding_iterator = binding_base; | ||
| 222 | |||
| 223 | const auto Allocate = [&binding_iterator](std::size_t count) { | ||
| 224 | const u32 current_binding = binding_iterator; | ||
| 225 | binding_iterator += static_cast<u32>(count); | ||
| 226 | return current_binding; | ||
| 227 | }; | ||
| 228 | const_buffers_base_binding = Allocate(ir.GetConstantBuffers().size()); | ||
| 229 | global_buffers_base_binding = Allocate(ir.GetGlobalMemory().size()); | ||
| 230 | samplers_base_binding = Allocate(ir.GetSamplers().size()); | ||
| 231 | |||
| 232 | ASSERT_MSG(binding_iterator - binding_base < STAGE_BINDING_STRIDE, | ||
| 233 | "Stage binding stride is too small"); | ||
| 234 | } | ||
| 235 | |||
| 236 | void AllocateLabels() { | ||
| 237 | for (const auto& pair : ir.GetBasicBlocks()) { | ||
| 238 | const u32 address = pair.first; | ||
| 239 | labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address))); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | void DeclareVertex() { | ||
| 244 | if (stage != ShaderStage::Vertex) | ||
| 245 | return; | ||
| 246 | |||
| 247 | DeclareVertexRedeclarations(); | ||
| 248 | } | ||
| 249 | |||
| 250 | void DeclareGeometry() { | ||
| 251 | if (stage != ShaderStage::Geometry) | ||
| 252 | return; | ||
| 253 | |||
| 254 | UNIMPLEMENTED(); | ||
| 255 | } | ||
| 256 | |||
| 257 | void DeclareFragment() { | ||
| 258 | if (stage != ShaderStage::Fragment) | ||
| 259 | return; | ||
| 260 | |||
| 261 | for (u32 rt = 0; rt < static_cast<u32>(frag_colors.size()); ++rt) { | ||
| 262 | if (!IsRenderTargetUsed(rt)) { | ||
| 263 | continue; | ||
| 264 | } | ||
| 265 | |||
| 266 | const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output)); | ||
| 267 | Name(id, fmt::format("frag_color{}", rt)); | ||
| 268 | Decorate(id, spv::Decoration::Location, rt); | ||
| 269 | |||
| 270 | frag_colors[rt] = id; | ||
| 271 | interfaces.push_back(id); | ||
| 272 | } | ||
| 273 | |||
| 274 | if (header.ps.omap.depth) { | ||
| 275 | frag_depth = AddGlobalVariable(OpVariable(t_out_float, spv::StorageClass::Output)); | ||
| 276 | Name(frag_depth, "frag_depth"); | ||
| 277 | Decorate(frag_depth, spv::Decoration::BuiltIn, | ||
| 278 | static_cast<u32>(spv::BuiltIn::FragDepth)); | ||
| 279 | |||
| 280 | interfaces.push_back(frag_depth); | ||
| 281 | } | ||
| 282 | |||
| 283 | frag_coord = DeclareBuiltIn(spv::BuiltIn::FragCoord, spv::StorageClass::Input, t_in_float4, | ||
| 284 | "frag_coord"); | ||
| 285 | front_facing = DeclareBuiltIn(spv::BuiltIn::FrontFacing, spv::StorageClass::Input, | ||
| 286 | t_in_bool, "front_facing"); | ||
| 287 | } | ||
| 288 | |||
| 289 | void DeclareRegisters() { | ||
| 290 | for (const u32 gpr : ir.GetRegisters()) { | ||
| 291 | const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero); | ||
| 292 | Name(id, fmt::format("gpr_{}", gpr)); | ||
| 293 | registers.emplace(gpr, AddGlobalVariable(id)); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | void DeclarePredicates() { | ||
| 298 | for (const auto pred : ir.GetPredicates()) { | ||
| 299 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 300 | Name(id, fmt::format("pred_{}", static_cast<u32>(pred))); | ||
| 301 | predicates.emplace(pred, AddGlobalVariable(id)); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | void DeclareLocalMemory() { | ||
| 306 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { | ||
| 307 | const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4); | ||
| 308 | const Id type_array = TypeArray(t_float, Constant(t_uint, element_count)); | ||
| 309 | const Id type_pointer = TypePointer(spv::StorageClass::Private, type_array); | ||
| 310 | Name(type_pointer, "LocalMemory"); | ||
| 311 | |||
| 312 | local_memory = | ||
| 313 | OpVariable(type_pointer, spv::StorageClass::Private, ConstantNull(type_array)); | ||
| 314 | AddGlobalVariable(Name(local_memory, "local_memory")); | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | void DeclareInternalFlags() { | ||
| 319 | constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry", | ||
| 320 | "overflow"}; | ||
| 321 | for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) { | ||
| 322 | const auto flag_code = static_cast<InternalFlag>(flag); | ||
| 323 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 324 | internal_flags[flag] = AddGlobalVariable(Name(id, names[flag])); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | void DeclareInputAttributes() { | ||
| 329 | for (const auto element : ir.GetInputAttributes()) { | ||
| 330 | const Attribute::Index index = element.first; | ||
| 331 | if (!IsGenericAttribute(index)) { | ||
| 332 | continue; | ||
| 333 | } | ||
| 334 | |||
| 335 | UNIMPLEMENTED_IF(stage == ShaderStage::Geometry); | ||
| 336 | |||
| 337 | const u32 location = GetGenericAttributeLocation(index); | ||
| 338 | const Id id = OpVariable(t_in_float4, spv::StorageClass::Input); | ||
| 339 | Name(AddGlobalVariable(id), fmt::format("in_attr{}", location)); | ||
| 340 | input_attributes.emplace(index, id); | ||
| 341 | interfaces.push_back(id); | ||
| 342 | |||
| 343 | Decorate(id, spv::Decoration::Location, location); | ||
| 344 | |||
| 345 | if (stage != ShaderStage::Fragment) { | ||
| 346 | continue; | ||
| 347 | } | ||
| 348 | switch (header.ps.GetAttributeUse(location)) { | ||
| 349 | case AttributeUse::Constant: | ||
| 350 | Decorate(id, spv::Decoration::Flat); | ||
| 351 | break; | ||
| 352 | case AttributeUse::ScreenLinear: | ||
| 353 | Decorate(id, spv::Decoration::NoPerspective); | ||
| 354 | break; | ||
| 355 | case AttributeUse::Perspective: | ||
| 356 | // Default | ||
| 357 | break; | ||
| 358 | default: | ||
| 359 | UNREACHABLE_MSG("Unused attribute being fetched"); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | } | ||
| 363 | |||
| 364 | void DeclareOutputAttributes() { | ||
| 365 | for (const auto index : ir.GetOutputAttributes()) { | ||
| 366 | if (!IsGenericAttribute(index)) { | ||
| 367 | continue; | ||
| 368 | } | ||
| 369 | const auto location = GetGenericAttributeLocation(index); | ||
| 370 | const Id id = OpVariable(t_out_float4, spv::StorageClass::Output); | ||
| 371 | Name(AddGlobalVariable(id), fmt::format("out_attr{}", location)); | ||
| 372 | output_attributes.emplace(index, id); | ||
| 373 | interfaces.push_back(id); | ||
| 374 | |||
| 375 | Decorate(id, spv::Decoration::Location, location); | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | void DeclareConstantBuffers() { | ||
| 380 | u32 binding = const_buffers_base_binding; | ||
| 381 | for (const auto& entry : ir.GetConstantBuffers()) { | ||
| 382 | const auto [index, size] = entry; | ||
| 383 | const Id id = OpVariable(t_cbuf_ubo, spv::StorageClass::Uniform); | ||
| 384 | AddGlobalVariable(Name(id, fmt::format("cbuf_{}", index))); | ||
| 385 | |||
| 386 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 387 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 388 | constant_buffers.emplace(index, id); | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 392 | void DeclareGlobalBuffers() { | ||
| 393 | u32 binding = global_buffers_base_binding; | ||
| 394 | for (const auto& entry : ir.GetGlobalMemory()) { | ||
| 395 | const auto [base, usage] = entry; | ||
| 396 | const Id id = OpVariable(t_gmem_ssbo, spv::StorageClass::StorageBuffer); | ||
| 397 | AddGlobalVariable( | ||
| 398 | Name(id, fmt::format("gmem_{}_{}", base.cbuf_index, base.cbuf_offset))); | ||
| 399 | |||
| 400 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 401 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 402 | global_buffers.emplace(base, id); | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | void DeclareSamplers() { | ||
| 407 | u32 binding = samplers_base_binding; | ||
| 408 | for (const auto& sampler : ir.GetSamplers()) { | ||
| 409 | const auto dim = GetSamplerDim(sampler); | ||
| 410 | const int depth = sampler.IsShadow() ? 1 : 0; | ||
| 411 | const int arrayed = sampler.IsArray() ? 1 : 0; | ||
| 412 | // TODO(Rodrigo): Sampled 1 indicates that the image will be used with a sampler. When | ||
| 413 | // SULD and SUST instructions are implemented, replace this value. | ||
| 414 | const int sampled = 1; | ||
| 415 | const Id image_type = | ||
| 416 | TypeImage(t_float, dim, depth, arrayed, false, sampled, spv::ImageFormat::Unknown); | ||
| 417 | const Id sampled_image_type = TypeSampledImage(image_type); | ||
| 418 | const Id pointer_type = | ||
| 419 | TypePointer(spv::StorageClass::UniformConstant, sampled_image_type); | ||
| 420 | const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant); | ||
| 421 | AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex()))); | ||
| 422 | |||
| 423 | sampler_images.insert( | ||
| 424 | {static_cast<u32>(sampler.GetIndex()), {image_type, sampled_image_type, id}}); | ||
| 425 | |||
| 426 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 427 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | void DeclareVertexRedeclarations() { | ||
| 432 | vertex_index = DeclareBuiltIn(spv::BuiltIn::VertexIndex, spv::StorageClass::Input, | ||
| 433 | t_in_uint, "vertex_index"); | ||
| 434 | instance_index = DeclareBuiltIn(spv::BuiltIn::InstanceIndex, spv::StorageClass::Input, | ||
| 435 | t_in_uint, "instance_index"); | ||
| 436 | |||
| 437 | bool is_point_size_declared = false; | ||
| 438 | bool is_clip_distances_declared = false; | ||
| 439 | for (const auto index : ir.GetOutputAttributes()) { | ||
| 440 | if (index == Attribute::Index::PointSize) { | ||
| 441 | is_point_size_declared = true; | ||
| 442 | } else if (index == Attribute::Index::ClipDistances0123 || | ||
| 443 | index == Attribute::Index::ClipDistances4567) { | ||
| 444 | is_clip_distances_declared = true; | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | std::vector<Id> members; | ||
| 449 | members.push_back(t_float4); | ||
| 450 | if (is_point_size_declared) { | ||
| 451 | members.push_back(t_float); | ||
| 452 | } | ||
| 453 | if (is_clip_distances_declared) { | ||
| 454 | members.push_back(TypeArray(t_float, Constant(t_uint, 8))); | ||
| 455 | } | ||
| 456 | |||
| 457 | const Id gl_per_vertex_struct = Name(TypeStruct(members), "PerVertex"); | ||
| 458 | Decorate(gl_per_vertex_struct, spv::Decoration::Block); | ||
| 459 | |||
| 460 | u32 declaration_index = 0; | ||
| 461 | const auto MemberDecorateBuiltIn = [&](spv::BuiltIn builtin, std::string name, | ||
| 462 | bool condition) { | ||
| 463 | if (!condition) | ||
| 464 | return u32{}; | ||
| 465 | MemberName(gl_per_vertex_struct, declaration_index, name); | ||
| 466 | MemberDecorate(gl_per_vertex_struct, declaration_index, spv::Decoration::BuiltIn, | ||
| 467 | static_cast<u32>(builtin)); | ||
| 468 | return declaration_index++; | ||
| 469 | }; | ||
| 470 | |||
| 471 | position_index = MemberDecorateBuiltIn(spv::BuiltIn::Position, "position", true); | ||
| 472 | point_size_index = | ||
| 473 | MemberDecorateBuiltIn(spv::BuiltIn::PointSize, "point_size", is_point_size_declared); | ||
| 474 | clip_distances_index = MemberDecorateBuiltIn(spv::BuiltIn::ClipDistance, "clip_distances", | ||
| 475 | is_clip_distances_declared); | ||
| 476 | |||
| 477 | const Id type_pointer = TypePointer(spv::StorageClass::Output, gl_per_vertex_struct); | ||
| 478 | per_vertex = OpVariable(type_pointer, spv::StorageClass::Output); | ||
| 479 | AddGlobalVariable(Name(per_vertex, "per_vertex")); | ||
| 480 | interfaces.push_back(per_vertex); | ||
| 481 | } | ||
| 482 | |||
| 483 | void VisitBasicBlock(const NodeBlock& bb) { | ||
| 484 | for (const Node node : bb) { | ||
| 485 | static_cast<void>(Visit(node)); | ||
| 486 | } | ||
| 487 | } | ||
| 488 | |||
| 489 | Id Visit(Node node) { | ||
| 490 | if (const auto operation = std::get_if<OperationNode>(node)) { | ||
| 491 | const auto operation_index = static_cast<std::size_t>(operation->GetCode()); | ||
| 492 | const auto decompiler = operation_decompilers[operation_index]; | ||
| 493 | if (decompiler == nullptr) { | ||
| 494 | UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); | ||
| 495 | } | ||
| 496 | return (this->*decompiler)(*operation); | ||
| 497 | |||
| 498 | } else if (const auto gpr = std::get_if<GprNode>(node)) { | ||
| 499 | const u32 index = gpr->GetIndex(); | ||
| 500 | if (index == Register::ZeroIndex) { | ||
| 501 | return Constant(t_float, 0.0f); | ||
| 502 | } | ||
| 503 | return Emit(OpLoad(t_float, registers.at(index))); | ||
| 504 | |||
| 505 | } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { | ||
| 506 | return BitcastTo<Type::Float>(Constant(t_uint, immediate->GetValue())); | ||
| 507 | |||
| 508 | } else if (const auto predicate = std::get_if<PredicateNode>(node)) { | ||
| 509 | const auto value = [&]() -> Id { | ||
| 510 | switch (const auto index = predicate->GetIndex(); index) { | ||
| 511 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 512 | return v_true; | ||
| 513 | case Tegra::Shader::Pred::NeverExecute: | ||
| 514 | return v_false; | ||
| 515 | default: | ||
| 516 | return Emit(OpLoad(t_bool, predicates.at(index))); | ||
| 517 | } | ||
| 518 | }(); | ||
| 519 | if (predicate->IsNegated()) { | ||
| 520 | return Emit(OpLogicalNot(t_bool, value)); | ||
| 521 | } | ||
| 522 | return value; | ||
| 523 | |||
| 524 | } else if (const auto abuf = std::get_if<AbufNode>(node)) { | ||
| 525 | const auto attribute = abuf->GetIndex(); | ||
| 526 | const auto element = abuf->GetElement(); | ||
| 527 | |||
| 528 | switch (attribute) { | ||
| 529 | case Attribute::Index::Position: | ||
| 530 | if (stage != ShaderStage::Fragment) { | ||
| 531 | UNIMPLEMENTED(); | ||
| 532 | break; | ||
| 533 | } else { | ||
| 534 | if (element == 3) { | ||
| 535 | return Constant(t_float, 1.0f); | ||
| 536 | } | ||
| 537 | return Emit(OpLoad(t_float, AccessElement(t_in_float, frag_coord, element))); | ||
| 538 | } | ||
| 539 | case Attribute::Index::TessCoordInstanceIDVertexID: | ||
| 540 | // TODO(Subv): Find out what the values are for the first two elements when inside a | ||
| 541 | // vertex shader, and what's the value of the fourth element when inside a Tess Eval | ||
| 542 | // shader. | ||
| 543 | ASSERT(stage == ShaderStage::Vertex); | ||
| 544 | switch (element) { | ||
| 545 | case 2: | ||
| 546 | return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, instance_index))); | ||
| 547 | case 3: | ||
| 548 | return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, vertex_index))); | ||
| 549 | } | ||
| 550 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); | ||
| 551 | return Constant(t_float, 0); | ||
| 552 | case Attribute::Index::FrontFacing: | ||
| 553 | // TODO(Subv): Find out what the values are for the other elements. | ||
| 554 | ASSERT(stage == ShaderStage::Fragment); | ||
| 555 | if (element == 3) { | ||
| 556 | const Id is_front_facing = Emit(OpLoad(t_bool, front_facing)); | ||
| 557 | const Id true_value = | ||
| 558 | BitcastTo<Type::Float>(Constant(t_int, static_cast<s32>(-1))); | ||
| 559 | const Id false_value = BitcastTo<Type::Float>(Constant(t_int, 0)); | ||
| 560 | return Emit(OpSelect(t_float, is_front_facing, true_value, false_value)); | ||
| 561 | } | ||
| 562 | UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); | ||
| 563 | return Constant(t_float, 0.0f); | ||
| 564 | default: | ||
| 565 | if (IsGenericAttribute(attribute)) { | ||
| 566 | const Id pointer = | ||
| 567 | AccessElement(t_in_float, input_attributes.at(attribute), element); | ||
| 568 | return Emit(OpLoad(t_float, pointer)); | ||
| 569 | } | ||
| 570 | break; | ||
| 571 | } | ||
| 572 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); | ||
| 573 | |||
| 574 | } else if (const auto cbuf = std::get_if<CbufNode>(node)) { | ||
| 575 | const Node offset = cbuf->GetOffset(); | ||
| 576 | const Id buffer_id = constant_buffers.at(cbuf->GetIndex()); | ||
| 577 | |||
| 578 | Id buffer_index{}; | ||
| 579 | Id buffer_element{}; | ||
| 580 | |||
| 581 | if (const auto immediate = std::get_if<ImmediateNode>(offset)) { | ||
| 582 | // Direct access | ||
| 583 | const u32 offset_imm = immediate->GetValue(); | ||
| 584 | ASSERT(offset_imm % 4 == 0); | ||
| 585 | buffer_index = Constant(t_uint, offset_imm / 16); | ||
| 586 | buffer_element = Constant(t_uint, (offset_imm / 4) % 4); | ||
| 587 | |||
| 588 | } else if (std::holds_alternative<OperationNode>(*offset)) { | ||
| 589 | // Indirect access | ||
| 590 | // TODO(Rodrigo): Use a uniform buffer stride of 4 and drop this slow math (which | ||
| 591 | // emits sub-optimal code on GLSL from my testing). | ||
| 592 | const Id offset_id = BitcastTo<Type::Uint>(Visit(offset)); | ||
| 593 | const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4))); | ||
| 594 | const Id final_offset = Emit( | ||
| 595 | OpUMod(t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1))); | ||
| 596 | buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4))); | ||
| 597 | buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4))); | ||
| 598 | |||
| 599 | } else { | ||
| 600 | UNREACHABLE_MSG("Unmanaged offset node type"); | ||
| 601 | } | ||
| 602 | |||
| 603 | const Id pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0), | ||
| 604 | buffer_index, buffer_element)); | ||
| 605 | return Emit(OpLoad(t_float, pointer)); | ||
| 606 | |||
| 607 | } else if (const auto gmem = std::get_if<GmemNode>(node)) { | ||
| 608 | const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); | ||
| 609 | const Id real = BitcastTo<Type::Uint>(Visit(gmem->GetRealAddress())); | ||
| 610 | const Id base = BitcastTo<Type::Uint>(Visit(gmem->GetBaseAddress())); | ||
| 611 | |||
| 612 | Id offset = Emit(OpISub(t_uint, real, base)); | ||
| 613 | offset = Emit(OpUDiv(t_uint, offset, Constant(t_uint, 4u))); | ||
| 614 | return Emit(OpLoad(t_float, Emit(OpAccessChain(t_gmem_float, gmem_buffer, | ||
| 615 | Constant(t_uint, 0u), offset)))); | ||
| 616 | |||
| 617 | } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { | ||
| 618 | // It's invalid to call conditional on nested nodes, use an operation instead | ||
| 619 | const Id true_label = OpLabel(); | ||
| 620 | const Id skip_label = OpLabel(); | ||
| 621 | Emit(OpBranchConditional(Visit(conditional->GetCondition()), true_label, skip_label)); | ||
| 622 | Emit(true_label); | ||
| 623 | |||
| 624 | VisitBasicBlock(conditional->GetCode()); | ||
| 625 | |||
| 626 | Emit(OpBranch(skip_label)); | ||
| 627 | Emit(skip_label); | ||
| 628 | return {}; | ||
| 629 | |||
| 630 | } else if (const auto comment = std::get_if<CommentNode>(node)) { | ||
| 631 | Name(Emit(OpUndef(t_void)), comment->GetText()); | ||
| 632 | return {}; | ||
| 633 | } | ||
| 634 | |||
| 635 | UNREACHABLE(); | ||
| 636 | return {}; | ||
| 637 | } | ||
| 638 | |||
| 639 | template <Id (Module::*func)(Id, Id), Type result_type, Type type_a = result_type> | ||
| 640 | Id Unary(Operation operation) { | ||
| 641 | const Id type_def = GetTypeDefinition(result_type); | ||
| 642 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 643 | |||
| 644 | const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a))); | ||
| 645 | if (IsPrecise(operation)) { | ||
| 646 | Decorate(value, spv::Decoration::NoContraction); | ||
| 647 | } | ||
| 648 | return value; | ||
| 649 | } | ||
| 650 | |||
| 651 | template <Id (Module::*func)(Id, Id, Id), Type result_type, Type type_a = result_type, | ||
| 652 | Type type_b = type_a> | ||
| 653 | Id Binary(Operation operation) { | ||
| 654 | const Id type_def = GetTypeDefinition(result_type); | ||
| 655 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 656 | const Id op_b = VisitOperand<type_b>(operation, 1); | ||
| 657 | |||
| 658 | const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b))); | ||
| 659 | if (IsPrecise(operation)) { | ||
| 660 | Decorate(value, spv::Decoration::NoContraction); | ||
| 661 | } | ||
| 662 | return value; | ||
| 663 | } | ||
| 664 | |||
| 665 | template <Id (Module::*func)(Id, Id, Id, Id), Type result_type, Type type_a = result_type, | ||
| 666 | Type type_b = type_a, Type type_c = type_b> | ||
| 667 | Id Ternary(Operation operation) { | ||
| 668 | const Id type_def = GetTypeDefinition(result_type); | ||
| 669 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 670 | const Id op_b = VisitOperand<type_b>(operation, 1); | ||
| 671 | const Id op_c = VisitOperand<type_c>(operation, 2); | ||
| 672 | |||
| 673 | const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c))); | ||
| 674 | if (IsPrecise(operation)) { | ||
| 675 | Decorate(value, spv::Decoration::NoContraction); | ||
| 676 | } | ||
| 677 | return value; | ||
| 678 | } | ||
| 679 | |||
| 680 | template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, Type type_a = result_type, | ||
| 681 | Type type_b = type_a, Type type_c = type_b, Type type_d = type_c> | ||
| 682 | Id Quaternary(Operation operation) { | ||
| 683 | const Id type_def = GetTypeDefinition(result_type); | ||
| 684 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 685 | const Id op_b = VisitOperand<type_b>(operation, 1); | ||
| 686 | const Id op_c = VisitOperand<type_c>(operation, 2); | ||
| 687 | const Id op_d = VisitOperand<type_d>(operation, 3); | ||
| 688 | |||
| 689 | const Id value = | ||
| 690 | BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c, op_d))); | ||
| 691 | if (IsPrecise(operation)) { | ||
| 692 | Decorate(value, spv::Decoration::NoContraction); | ||
| 693 | } | ||
| 694 | return value; | ||
| 695 | } | ||
| 696 | |||
| 697 | Id Assign(Operation operation) { | ||
| 698 | const Node dest = operation[0]; | ||
| 699 | const Node src = operation[1]; | ||
| 700 | |||
| 701 | Id target{}; | ||
| 702 | if (const auto gpr = std::get_if<GprNode>(dest)) { | ||
| 703 | if (gpr->GetIndex() == Register::ZeroIndex) { | ||
| 704 | // Writing to Register::ZeroIndex is a no op | ||
| 705 | return {}; | ||
| 706 | } | ||
| 707 | target = registers.at(gpr->GetIndex()); | ||
| 708 | |||
| 709 | } else if (const auto abuf = std::get_if<AbufNode>(dest)) { | ||
| 710 | target = [&]() -> Id { | ||
| 711 | switch (const auto attribute = abuf->GetIndex(); attribute) { | ||
| 712 | case Attribute::Index::Position: | ||
| 713 | return AccessElement(t_out_float, per_vertex, position_index, | ||
| 714 | abuf->GetElement()); | ||
| 715 | case Attribute::Index::PointSize: | ||
| 716 | return AccessElement(t_out_float, per_vertex, point_size_index); | ||
| 717 | case Attribute::Index::ClipDistances0123: | ||
| 718 | return AccessElement(t_out_float, per_vertex, clip_distances_index, | ||
| 719 | abuf->GetElement()); | ||
| 720 | case Attribute::Index::ClipDistances4567: | ||
| 721 | return AccessElement(t_out_float, per_vertex, clip_distances_index, | ||
| 722 | abuf->GetElement() + 4); | ||
| 723 | default: | ||
| 724 | if (IsGenericAttribute(attribute)) { | ||
| 725 | return AccessElement(t_out_float, output_attributes.at(attribute), | ||
| 726 | abuf->GetElement()); | ||
| 727 | } | ||
| 728 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", | ||
| 729 | static_cast<u32>(attribute)); | ||
| 730 | return {}; | ||
| 731 | } | ||
| 732 | }(); | ||
| 733 | |||
| 734 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { | ||
| 735 | Id address = BitcastTo<Type::Uint>(Visit(lmem->GetAddress())); | ||
| 736 | address = Emit(OpUDiv(t_uint, address, Constant(t_uint, 4))); | ||
| 737 | target = Emit(OpAccessChain(t_prv_float, local_memory, {address})); | ||
| 738 | } | ||
| 739 | |||
| 740 | Emit(OpStore(target, Visit(src))); | ||
| 741 | return {}; | ||
| 742 | } | ||
| 743 | |||
| 744 | Id HNegate(Operation operation) { | ||
| 745 | UNIMPLEMENTED(); | ||
| 746 | return {}; | ||
| 747 | } | ||
| 748 | |||
| 749 | Id HMergeF32(Operation operation) { | ||
| 750 | UNIMPLEMENTED(); | ||
| 751 | return {}; | ||
| 752 | } | ||
| 753 | |||
| 754 | Id HMergeH0(Operation operation) { | ||
| 755 | UNIMPLEMENTED(); | ||
| 756 | return {}; | ||
| 757 | } | ||
| 758 | |||
| 759 | Id HMergeH1(Operation operation) { | ||
| 760 | UNIMPLEMENTED(); | ||
| 761 | return {}; | ||
| 762 | } | ||
| 763 | |||
| 764 | Id HPack2(Operation operation) { | ||
| 765 | UNIMPLEMENTED(); | ||
| 766 | return {}; | ||
| 767 | } | ||
| 768 | |||
| 769 | Id LogicalAssign(Operation operation) { | ||
| 770 | const Node dest = operation[0]; | ||
| 771 | const Node src = operation[1]; | ||
| 772 | |||
| 773 | Id target{}; | ||
| 774 | if (const auto pred = std::get_if<PredicateNode>(dest)) { | ||
| 775 | ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment"); | ||
| 776 | |||
| 777 | const auto index = pred->GetIndex(); | ||
| 778 | switch (index) { | ||
| 779 | case Tegra::Shader::Pred::NeverExecute: | ||
| 780 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 781 | // Writing to these predicates is a no-op | ||
| 782 | return {}; | ||
| 783 | } | ||
| 784 | target = predicates.at(index); | ||
| 785 | |||
| 786 | } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) { | ||
| 787 | target = internal_flags.at(static_cast<u32>(flag->GetFlag())); | ||
| 788 | } | ||
| 789 | |||
| 790 | Emit(OpStore(target, Visit(src))); | ||
| 791 | return {}; | ||
| 792 | } | ||
| 793 | |||
| 794 | Id LogicalPick2(Operation operation) { | ||
| 795 | UNIMPLEMENTED(); | ||
| 796 | return {}; | ||
| 797 | } | ||
| 798 | |||
| 799 | Id LogicalAll2(Operation operation) { | ||
| 800 | UNIMPLEMENTED(); | ||
| 801 | return {}; | ||
| 802 | } | ||
| 803 | |||
| 804 | Id LogicalAny2(Operation operation) { | ||
| 805 | UNIMPLEMENTED(); | ||
| 806 | return {}; | ||
| 807 | } | ||
| 808 | |||
| 809 | Id GetTextureSampler(Operation operation) { | ||
| 810 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 811 | const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex())); | ||
| 812 | return Emit(OpLoad(entry.sampled_image_type, entry.sampler)); | ||
| 813 | } | ||
| 814 | |||
| 815 | Id GetTextureImage(Operation operation) { | ||
| 816 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 817 | const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex())); | ||
| 818 | return Emit(OpImage(entry.image_type, GetTextureSampler(operation))); | ||
| 819 | } | ||
| 820 | |||
| 821 | Id GetTextureCoordinates(Operation operation) { | ||
| 822 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 823 | std::vector<Id> coords; | ||
| 824 | for (std::size_t i = 0; i < operation.GetOperandsCount(); ++i) { | ||
| 825 | coords.push_back(Visit(operation[i])); | ||
| 826 | } | ||
| 827 | if (meta->sampler.IsArray()) { | ||
| 828 | const Id array_integer = BitcastTo<Type::Int>(Visit(meta->array)); | ||
| 829 | coords.push_back(Emit(OpConvertSToF(t_float, array_integer))); | ||
| 830 | } | ||
| 831 | if (meta->sampler.IsShadow()) { | ||
| 832 | coords.push_back(Visit(meta->depth_compare)); | ||
| 833 | } | ||
| 834 | |||
| 835 | const std::array<Id, 4> t_float_lut = {nullptr, t_float2, t_float3, t_float4}; | ||
| 836 | return coords.size() == 1 | ||
| 837 | ? coords[0] | ||
| 838 | : Emit(OpCompositeConstruct(t_float_lut.at(coords.size() - 1), coords)); | ||
| 839 | } | ||
| 840 | |||
| 841 | Id GetTextureElement(Operation operation, Id sample_value) { | ||
| 842 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 843 | ASSERT(meta); | ||
| 844 | return Emit(OpCompositeExtract(t_float, sample_value, meta->element)); | ||
| 845 | } | ||
| 846 | |||
| 847 | Id Texture(Operation operation) { | ||
| 848 | const Id texture = Emit(OpImageSampleImplicitLod(t_float4, GetTextureSampler(operation), | ||
| 849 | GetTextureCoordinates(operation))); | ||
| 850 | return GetTextureElement(operation, texture); | ||
| 851 | } | ||
| 852 | |||
| 853 | Id TextureLod(Operation operation) { | ||
| 854 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 855 | const Id texture = Emit(OpImageSampleExplicitLod( | ||
| 856 | t_float4, GetTextureSampler(operation), GetTextureCoordinates(operation), | ||
| 857 | spv::ImageOperandsMask::Lod, Visit(meta->lod))); | ||
| 858 | return GetTextureElement(operation, texture); | ||
| 859 | } | ||
| 860 | |||
| 861 | Id TextureGather(Operation operation) { | ||
| 862 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 863 | const auto coords = GetTextureCoordinates(operation); | ||
| 864 | |||
| 865 | Id texture; | ||
| 866 | if (meta->sampler.IsShadow()) { | ||
| 867 | texture = Emit(OpImageDrefGather(t_float4, GetTextureSampler(operation), coords, | ||
| 868 | Visit(meta->component))); | ||
| 869 | } else { | ||
| 870 | u32 component_value = 0; | ||
| 871 | if (meta->component) { | ||
| 872 | const auto component = std::get_if<ImmediateNode>(meta->component); | ||
| 873 | ASSERT_MSG(component, "Component is not an immediate value"); | ||
| 874 | component_value = component->GetValue(); | ||
| 875 | } | ||
| 876 | texture = Emit(OpImageGather(t_float4, GetTextureSampler(operation), coords, | ||
| 877 | Constant(t_uint, component_value))); | ||
| 878 | } | ||
| 879 | |||
| 880 | return GetTextureElement(operation, texture); | ||
| 881 | } | ||
| 882 | |||
| 883 | Id TextureQueryDimensions(Operation operation) { | ||
| 884 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 885 | const auto image_id = GetTextureImage(operation); | ||
| 886 | AddCapability(spv::Capability::ImageQuery); | ||
| 887 | |||
| 888 | if (meta->element == 3) { | ||
| 889 | return BitcastTo<Type::Float>(Emit(OpImageQueryLevels(t_int, image_id))); | ||
| 890 | } | ||
| 891 | |||
| 892 | const Id lod = VisitOperand<Type::Uint>(operation, 0); | ||
| 893 | const std::size_t coords_count = [&]() { | ||
| 894 | switch (const auto type = meta->sampler.GetType(); type) { | ||
| 895 | case Tegra::Shader::TextureType::Texture1D: | ||
| 896 | return 1; | ||
| 897 | case Tegra::Shader::TextureType::Texture2D: | ||
| 898 | case Tegra::Shader::TextureType::TextureCube: | ||
| 899 | return 2; | ||
| 900 | case Tegra::Shader::TextureType::Texture3D: | ||
| 901 | return 3; | ||
| 902 | default: | ||
| 903 | UNREACHABLE_MSG("Invalid texture type={}", static_cast<u32>(type)); | ||
| 904 | return 2; | ||
| 905 | } | ||
| 906 | }(); | ||
| 907 | |||
| 908 | if (meta->element >= coords_count) { | ||
| 909 | return Constant(t_float, 0.0f); | ||
| 910 | } | ||
| 911 | |||
| 912 | const std::array<Id, 3> types = {t_int, t_int2, t_int3}; | ||
| 913 | const Id sizes = Emit(OpImageQuerySizeLod(types.at(coords_count - 1), image_id, lod)); | ||
| 914 | const Id size = Emit(OpCompositeExtract(t_int, sizes, meta->element)); | ||
| 915 | return BitcastTo<Type::Float>(size); | ||
| 916 | } | ||
| 917 | |||
| 918 | Id TextureQueryLod(Operation operation) { | ||
| 919 | UNIMPLEMENTED(); | ||
| 920 | return {}; | ||
| 921 | } | ||
| 922 | |||
| 923 | Id TexelFetch(Operation operation) { | ||
| 924 | UNIMPLEMENTED(); | ||
| 925 | return {}; | ||
| 926 | } | ||
| 927 | |||
| 928 | Id Branch(Operation operation) { | ||
| 929 | const auto target = std::get_if<ImmediateNode>(operation[0]); | ||
| 930 | UNIMPLEMENTED_IF(!target); | ||
| 931 | |||
| 932 | Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); | ||
| 933 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | ||
| 934 | return {}; | ||
| 935 | } | ||
| 936 | |||
| 937 | Id PushFlowStack(Operation operation) { | ||
| 938 | const auto target = std::get_if<ImmediateNode>(operation[0]); | ||
| 939 | ASSERT(target); | ||
| 940 | |||
| 941 | const Id current = Emit(OpLoad(t_uint, flow_stack_top)); | ||
| 942 | const Id next = Emit(OpIAdd(t_uint, current, Constant(t_uint, 1))); | ||
| 943 | const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, current)); | ||
| 944 | |||
| 945 | Emit(OpStore(access, Constant(t_uint, target->GetValue()))); | ||
| 946 | Emit(OpStore(flow_stack_top, next)); | ||
| 947 | return {}; | ||
| 948 | } | ||
| 949 | |||
| 950 | Id PopFlowStack(Operation operation) { | ||
| 951 | const Id current = Emit(OpLoad(t_uint, flow_stack_top)); | ||
| 952 | const Id previous = Emit(OpISub(t_uint, current, Constant(t_uint, 1))); | ||
| 953 | const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, previous)); | ||
| 954 | const Id target = Emit(OpLoad(t_uint, access)); | ||
| 955 | |||
| 956 | Emit(OpStore(flow_stack_top, previous)); | ||
| 957 | Emit(OpStore(jmp_to, target)); | ||
| 958 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | ||
| 959 | return {}; | ||
| 960 | } | ||
| 961 | |||
| 962 | Id Exit(Operation operation) { | ||
| 963 | switch (stage) { | ||
| 964 | case ShaderStage::Vertex: { | ||
| 965 | // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't | ||
| 966 | // seem to be working on Nvidia's drivers and Intel (mesa and blob) doesn't support it. | ||
| 967 | const Id position = AccessElement(t_float4, per_vertex, position_index); | ||
| 968 | Id depth = Emit(OpLoad(t_float, AccessElement(t_out_float, position, 2))); | ||
| 969 | depth = Emit(OpFAdd(t_float, depth, Constant(t_float, 1.0f))); | ||
| 970 | depth = Emit(OpFMul(t_float, depth, Constant(t_float, 0.5f))); | ||
| 971 | Emit(OpStore(AccessElement(t_out_float, position, 2), depth)); | ||
| 972 | break; | ||
| 973 | } | ||
| 974 | case ShaderStage::Fragment: { | ||
| 975 | const auto SafeGetRegister = [&](u32 reg) { | ||
| 976 | // TODO(Rodrigo): Replace with contains once C++20 releases | ||
| 977 | if (const auto it = registers.find(reg); it != registers.end()) { | ||
| 978 | return Emit(OpLoad(t_float, it->second)); | ||
| 979 | } | ||
| 980 | return Constant(t_float, 0.0f); | ||
| 981 | }; | ||
| 982 | |||
| 983 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, | ||
| 984 | "Sample mask write is unimplemented"); | ||
| 985 | |||
| 986 | // TODO(Rodrigo): Alpha testing | ||
| 987 | |||
| 988 | // Write the color outputs using the data in the shader registers, disabled | ||
| 989 | // rendertargets/components are skipped in the register assignment. | ||
| 990 | u32 current_reg = 0; | ||
| 991 | for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { | ||
| 992 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | ||
| 993 | for (u32 component = 0; component < 4; ++component) { | ||
| 994 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { | ||
| 995 | Emit(OpStore(AccessElement(t_out_float, frag_colors.at(rt), component), | ||
| 996 | SafeGetRegister(current_reg))); | ||
| 997 | ++current_reg; | ||
| 998 | } | ||
| 999 | } | ||
| 1000 | } | ||
| 1001 | if (header.ps.omap.depth) { | ||
| 1002 | // The depth output is always 2 registers after the last color output, and | ||
| 1003 | // current_reg already contains one past the last color register. | ||
| 1004 | Emit(OpStore(frag_depth, SafeGetRegister(current_reg + 1))); | ||
| 1005 | } | ||
| 1006 | break; | ||
| 1007 | } | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | BranchingOp([&]() { Emit(OpReturn()); }); | ||
| 1011 | return {}; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | Id Discard(Operation operation) { | ||
| 1015 | BranchingOp([&]() { Emit(OpKill()); }); | ||
| 1016 | return {}; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | Id EmitVertex(Operation operation) { | ||
| 1020 | UNIMPLEMENTED(); | ||
| 1021 | return {}; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | Id EndPrimitive(Operation operation) { | ||
| 1025 | UNIMPLEMENTED(); | ||
| 1026 | return {}; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | Id YNegate(Operation operation) { | ||
| 1030 | UNIMPLEMENTED(); | ||
| 1031 | return {}; | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type, | ||
| 1035 | const std::string& name) { | ||
| 1036 | const Id id = OpVariable(type, storage); | ||
| 1037 | Decorate(id, spv::Decoration::BuiltIn, static_cast<u32>(builtin)); | ||
| 1038 | AddGlobalVariable(Name(id, name)); | ||
| 1039 | interfaces.push_back(id); | ||
| 1040 | return id; | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | bool IsRenderTargetUsed(u32 rt) const { | ||
| 1044 | for (u32 component = 0; component < 4; ++component) { | ||
| 1045 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { | ||
| 1046 | return true; | ||
| 1047 | } | ||
| 1048 | } | ||
| 1049 | return false; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | template <typename... Args> | ||
| 1053 | Id AccessElement(Id pointer_type, Id composite, Args... elements_) { | ||
| 1054 | std::vector<Id> members; | ||
| 1055 | auto elements = {elements_...}; | ||
| 1056 | for (const auto element : elements) { | ||
| 1057 | members.push_back(Constant(t_uint, element)); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | return Emit(OpAccessChain(pointer_type, composite, members)); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | template <Type type> | ||
| 1064 | Id VisitOperand(Operation operation, std::size_t operand_index) { | ||
| 1065 | const Id value = Visit(operation[operand_index]); | ||
| 1066 | |||
| 1067 | switch (type) { | ||
| 1068 | case Type::Bool: | ||
| 1069 | case Type::Bool2: | ||
| 1070 | case Type::Float: | ||
| 1071 | return value; | ||
| 1072 | case Type::Int: | ||
| 1073 | return Emit(OpBitcast(t_int, value)); | ||
| 1074 | case Type::Uint: | ||
| 1075 | return Emit(OpBitcast(t_uint, value)); | ||
| 1076 | case Type::HalfFloat: | ||
| 1077 | UNIMPLEMENTED(); | ||
| 1078 | } | ||
| 1079 | UNREACHABLE(); | ||
| 1080 | return value; | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | template <Type type> | ||
| 1084 | Id BitcastFrom(Id value) { | ||
| 1085 | switch (type) { | ||
| 1086 | case Type::Bool: | ||
| 1087 | case Type::Bool2: | ||
| 1088 | case Type::Float: | ||
| 1089 | return value; | ||
| 1090 | case Type::Int: | ||
| 1091 | case Type::Uint: | ||
| 1092 | return Emit(OpBitcast(t_float, value)); | ||
| 1093 | case Type::HalfFloat: | ||
| 1094 | UNIMPLEMENTED(); | ||
| 1095 | } | ||
| 1096 | UNREACHABLE(); | ||
| 1097 | return value; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | template <Type type> | ||
| 1101 | Id BitcastTo(Id value) { | ||
| 1102 | switch (type) { | ||
| 1103 | case Type::Bool: | ||
| 1104 | case Type::Bool2: | ||
| 1105 | UNREACHABLE(); | ||
| 1106 | case Type::Float: | ||
| 1107 | return Emit(OpBitcast(t_float, value)); | ||
| 1108 | case Type::Int: | ||
| 1109 | return Emit(OpBitcast(t_int, value)); | ||
| 1110 | case Type::Uint: | ||
| 1111 | return Emit(OpBitcast(t_uint, value)); | ||
| 1112 | case Type::HalfFloat: | ||
| 1113 | UNIMPLEMENTED(); | ||
| 1114 | } | ||
| 1115 | UNREACHABLE(); | ||
| 1116 | return value; | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | Id GetTypeDefinition(Type type) { | ||
| 1120 | switch (type) { | ||
| 1121 | case Type::Bool: | ||
| 1122 | return t_bool; | ||
| 1123 | case Type::Bool2: | ||
| 1124 | return t_bool2; | ||
| 1125 | case Type::Float: | ||
| 1126 | return t_float; | ||
| 1127 | case Type::Int: | ||
| 1128 | return t_int; | ||
| 1129 | case Type::Uint: | ||
| 1130 | return t_uint; | ||
| 1131 | case Type::HalfFloat: | ||
| 1132 | UNIMPLEMENTED(); | ||
| 1133 | } | ||
| 1134 | UNREACHABLE(); | ||
| 1135 | return {}; | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | void BranchingOp(std::function<void()> call) { | ||
| 1139 | const Id true_label = OpLabel(); | ||
| 1140 | const Id skip_label = OpLabel(); | ||
| 1141 | Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten)); | ||
| 1142 | Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0)); | ||
| 1143 | Emit(true_label); | ||
| 1144 | call(); | ||
| 1145 | |||
| 1146 | Emit(skip_label); | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | static constexpr OperationDecompilersArray operation_decompilers = { | ||
| 1150 | &SPIRVDecompiler::Assign, | ||
| 1151 | |||
| 1152 | &SPIRVDecompiler::Ternary<&Module::OpSelect, Type::Float, Type::Bool, Type::Float, | ||
| 1153 | Type::Float>, | ||
| 1154 | |||
| 1155 | &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::Float>, | ||
| 1156 | &SPIRVDecompiler::Binary<&Module::OpFMul, Type::Float>, | ||
| 1157 | &SPIRVDecompiler::Binary<&Module::OpFDiv, Type::Float>, | ||
| 1158 | &SPIRVDecompiler::Ternary<&Module::OpFma, Type::Float>, | ||
| 1159 | &SPIRVDecompiler::Unary<&Module::OpFNegate, Type::Float>, | ||
| 1160 | &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::Float>, | ||
| 1161 | &SPIRVDecompiler::Ternary<&Module::OpFClamp, Type::Float>, | ||
| 1162 | &SPIRVDecompiler::Binary<&Module::OpFMin, Type::Float>, | ||
| 1163 | &SPIRVDecompiler::Binary<&Module::OpFMax, Type::Float>, | ||
| 1164 | &SPIRVDecompiler::Unary<&Module::OpCos, Type::Float>, | ||
| 1165 | &SPIRVDecompiler::Unary<&Module::OpSin, Type::Float>, | ||
| 1166 | &SPIRVDecompiler::Unary<&Module::OpExp2, Type::Float>, | ||
| 1167 | &SPIRVDecompiler::Unary<&Module::OpLog2, Type::Float>, | ||
| 1168 | &SPIRVDecompiler::Unary<&Module::OpInverseSqrt, Type::Float>, | ||
| 1169 | &SPIRVDecompiler::Unary<&Module::OpSqrt, Type::Float>, | ||
| 1170 | &SPIRVDecompiler::Unary<&Module::OpRoundEven, Type::Float>, | ||
| 1171 | &SPIRVDecompiler::Unary<&Module::OpFloor, Type::Float>, | ||
| 1172 | &SPIRVDecompiler::Unary<&Module::OpCeil, Type::Float>, | ||
| 1173 | &SPIRVDecompiler::Unary<&Module::OpTrunc, Type::Float>, | ||
| 1174 | &SPIRVDecompiler::Unary<&Module::OpConvertSToF, Type::Float, Type::Int>, | ||
| 1175 | &SPIRVDecompiler::Unary<&Module::OpConvertUToF, Type::Float, Type::Uint>, | ||
| 1176 | |||
| 1177 | &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Int>, | ||
| 1178 | &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Int>, | ||
| 1179 | &SPIRVDecompiler::Binary<&Module::OpSDiv, Type::Int>, | ||
| 1180 | &SPIRVDecompiler::Unary<&Module::OpSNegate, Type::Int>, | ||
| 1181 | &SPIRVDecompiler::Unary<&Module::OpSAbs, Type::Int>, | ||
| 1182 | &SPIRVDecompiler::Binary<&Module::OpSMin, Type::Int>, | ||
| 1183 | &SPIRVDecompiler::Binary<&Module::OpSMax, Type::Int>, | ||
| 1184 | |||
| 1185 | &SPIRVDecompiler::Unary<&Module::OpConvertFToS, Type::Int, Type::Float>, | ||
| 1186 | &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Int, Type::Uint>, | ||
| 1187 | &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Int, Type::Int, Type::Uint>, | ||
| 1188 | &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Int, Type::Int, Type::Uint>, | ||
| 1189 | &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Int, Type::Int, Type::Uint>, | ||
| 1190 | &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Int>, | ||
| 1191 | &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Int>, | ||
| 1192 | &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Int>, | ||
| 1193 | &SPIRVDecompiler::Unary<&Module::OpNot, Type::Int>, | ||
| 1194 | &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Int>, | ||
| 1195 | &SPIRVDecompiler::Ternary<&Module::OpBitFieldSExtract, Type::Int>, | ||
| 1196 | &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Int>, | ||
| 1197 | |||
| 1198 | &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Uint>, | ||
| 1199 | &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Uint>, | ||
| 1200 | &SPIRVDecompiler::Binary<&Module::OpUDiv, Type::Uint>, | ||
| 1201 | &SPIRVDecompiler::Binary<&Module::OpUMin, Type::Uint>, | ||
| 1202 | &SPIRVDecompiler::Binary<&Module::OpUMax, Type::Uint>, | ||
| 1203 | &SPIRVDecompiler::Unary<&Module::OpConvertFToU, Type::Uint, Type::Float>, | ||
| 1204 | &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Uint, Type::Int>, | ||
| 1205 | &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Uint>, | ||
| 1206 | &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Uint>, | ||
| 1207 | &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Uint>, | ||
| 1208 | &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Uint>, | ||
| 1209 | &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Uint>, | ||
| 1210 | &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Uint>, | ||
| 1211 | &SPIRVDecompiler::Unary<&Module::OpNot, Type::Uint>, | ||
| 1212 | &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Uint>, | ||
| 1213 | &SPIRVDecompiler::Ternary<&Module::OpBitFieldUExtract, Type::Uint>, | ||
| 1214 | &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Uint>, | ||
| 1215 | |||
| 1216 | &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::HalfFloat>, | ||
| 1217 | &SPIRVDecompiler::Binary<&Module::OpFMul, Type::HalfFloat>, | ||
| 1218 | &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>, | ||
| 1219 | &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>, | ||
| 1220 | &SPIRVDecompiler::HNegate, | ||
| 1221 | &SPIRVDecompiler::HMergeF32, | ||
| 1222 | &SPIRVDecompiler::HMergeH0, | ||
| 1223 | &SPIRVDecompiler::HMergeH1, | ||
| 1224 | &SPIRVDecompiler::HPack2, | ||
| 1225 | |||
| 1226 | &SPIRVDecompiler::LogicalAssign, | ||
| 1227 | &SPIRVDecompiler::Binary<&Module::OpLogicalAnd, Type::Bool>, | ||
| 1228 | &SPIRVDecompiler::Binary<&Module::OpLogicalOr, Type::Bool>, | ||
| 1229 | &SPIRVDecompiler::Binary<&Module::OpLogicalNotEqual, Type::Bool>, | ||
| 1230 | &SPIRVDecompiler::Unary<&Module::OpLogicalNot, Type::Bool>, | ||
| 1231 | &SPIRVDecompiler::LogicalPick2, | ||
| 1232 | &SPIRVDecompiler::LogicalAll2, | ||
| 1233 | &SPIRVDecompiler::LogicalAny2, | ||
| 1234 | |||
| 1235 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::Float>, | ||
| 1236 | &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::Float>, | ||
| 1237 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::Float>, | ||
| 1238 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>, | ||
| 1239 | &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>, | ||
| 1240 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>, | ||
| 1241 | &SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool>, | ||
| 1242 | |||
| 1243 | &SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>, | ||
| 1244 | &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>, | ||
| 1245 | &SPIRVDecompiler::Binary<&Module::OpSLessThanEqual, Type::Bool, Type::Int>, | ||
| 1246 | &SPIRVDecompiler::Binary<&Module::OpSGreaterThan, Type::Bool, Type::Int>, | ||
| 1247 | &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Int>, | ||
| 1248 | &SPIRVDecompiler::Binary<&Module::OpSGreaterThanEqual, Type::Bool, Type::Int>, | ||
| 1249 | |||
| 1250 | &SPIRVDecompiler::Binary<&Module::OpULessThan, Type::Bool, Type::Uint>, | ||
| 1251 | &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Uint>, | ||
| 1252 | &SPIRVDecompiler::Binary<&Module::OpULessThanEqual, Type::Bool, Type::Uint>, | ||
| 1253 | &SPIRVDecompiler::Binary<&Module::OpUGreaterThan, Type::Bool, Type::Uint>, | ||
| 1254 | &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Uint>, | ||
| 1255 | &SPIRVDecompiler::Binary<&Module::OpUGreaterThanEqual, Type::Bool, Type::Uint>, | ||
| 1256 | |||
| 1257 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>, | ||
| 1258 | &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>, | ||
| 1259 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>, | ||
| 1260 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>, | ||
| 1261 | &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>, | ||
| 1262 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>, | ||
| 1263 | |||
| 1264 | &SPIRVDecompiler::Texture, | ||
| 1265 | &SPIRVDecompiler::TextureLod, | ||
| 1266 | &SPIRVDecompiler::TextureGather, | ||
| 1267 | &SPIRVDecompiler::TextureQueryDimensions, | ||
| 1268 | &SPIRVDecompiler::TextureQueryLod, | ||
| 1269 | &SPIRVDecompiler::TexelFetch, | ||
| 1270 | |||
| 1271 | &SPIRVDecompiler::Branch, | ||
| 1272 | &SPIRVDecompiler::PushFlowStack, | ||
| 1273 | &SPIRVDecompiler::PopFlowStack, | ||
| 1274 | &SPIRVDecompiler::Exit, | ||
| 1275 | &SPIRVDecompiler::Discard, | ||
| 1276 | |||
| 1277 | &SPIRVDecompiler::EmitVertex, | ||
| 1278 | &SPIRVDecompiler::EndPrimitive, | ||
| 1279 | |||
| 1280 | &SPIRVDecompiler::YNegate, | ||
| 1281 | }; | ||
| 1282 | |||
| 1283 | const ShaderIR& ir; | ||
| 1284 | const ShaderStage stage; | ||
| 1285 | const Tegra::Shader::Header header; | ||
| 1286 | |||
| 1287 | const Id t_void = Name(TypeVoid(), "void"); | ||
| 1288 | |||
| 1289 | const Id t_bool = Name(TypeBool(), "bool"); | ||
| 1290 | const Id t_bool2 = Name(TypeVector(t_bool, 2), "bool2"); | ||
| 1291 | |||
| 1292 | const Id t_int = Name(TypeInt(32, true), "int"); | ||
| 1293 | const Id t_int2 = Name(TypeVector(t_int, 2), "int2"); | ||
| 1294 | const Id t_int3 = Name(TypeVector(t_int, 3), "int3"); | ||
| 1295 | const Id t_int4 = Name(TypeVector(t_int, 4), "int4"); | ||
| 1296 | |||
| 1297 | const Id t_uint = Name(TypeInt(32, false), "uint"); | ||
| 1298 | const Id t_uint2 = Name(TypeVector(t_uint, 2), "uint2"); | ||
| 1299 | const Id t_uint3 = Name(TypeVector(t_uint, 3), "uint3"); | ||
| 1300 | const Id t_uint4 = Name(TypeVector(t_uint, 4), "uint4"); | ||
| 1301 | |||
| 1302 | const Id t_float = Name(TypeFloat(32), "float"); | ||
| 1303 | const Id t_float2 = Name(TypeVector(t_float, 2), "float2"); | ||
| 1304 | const Id t_float3 = Name(TypeVector(t_float, 3), "float3"); | ||
| 1305 | const Id t_float4 = Name(TypeVector(t_float, 4), "float4"); | ||
| 1306 | |||
| 1307 | const Id t_prv_bool = Name(TypePointer(spv::StorageClass::Private, t_bool), "prv_bool"); | ||
| 1308 | const Id t_prv_float = Name(TypePointer(spv::StorageClass::Private, t_float), "prv_float"); | ||
| 1309 | |||
| 1310 | const Id t_func_uint = Name(TypePointer(spv::StorageClass::Function, t_uint), "func_uint"); | ||
| 1311 | |||
| 1312 | const Id t_in_bool = Name(TypePointer(spv::StorageClass::Input, t_bool), "in_bool"); | ||
| 1313 | const Id t_in_uint = Name(TypePointer(spv::StorageClass::Input, t_uint), "in_uint"); | ||
| 1314 | const Id t_in_float = Name(TypePointer(spv::StorageClass::Input, t_float), "in_float"); | ||
| 1315 | const Id t_in_float4 = Name(TypePointer(spv::StorageClass::Input, t_float4), "in_float4"); | ||
| 1316 | |||
| 1317 | const Id t_out_float = Name(TypePointer(spv::StorageClass::Output, t_float), "out_float"); | ||
| 1318 | const Id t_out_float4 = Name(TypePointer(spv::StorageClass::Output, t_float4), "out_float4"); | ||
| 1319 | |||
| 1320 | const Id t_cbuf_float = TypePointer(spv::StorageClass::Uniform, t_float); | ||
| 1321 | const Id t_cbuf_array = | ||
| 1322 | Decorate(Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufArray"), | ||
| 1323 | spv::Decoration::ArrayStride, CBUF_STRIDE); | ||
| 1324 | const Id t_cbuf_struct = MemberDecorate( | ||
| 1325 | Decorate(TypeStruct(t_cbuf_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); | ||
| 1326 | const Id t_cbuf_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_struct); | ||
| 1327 | |||
| 1328 | const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float); | ||
| 1329 | const Id t_gmem_array = | ||
| 1330 | Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4u), "GmemArray"); | ||
| 1331 | const Id t_gmem_struct = MemberDecorate( | ||
| 1332 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); | ||
| 1333 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); | ||
| 1334 | |||
| 1335 | const Id v_float_zero = Constant(t_float, 0.0f); | ||
| 1336 | const Id v_true = ConstantTrue(t_bool); | ||
| 1337 | const Id v_false = ConstantFalse(t_bool); | ||
| 1338 | |||
| 1339 | Id per_vertex{}; | ||
| 1340 | std::map<u32, Id> registers; | ||
| 1341 | std::map<Tegra::Shader::Pred, Id> predicates; | ||
| 1342 | Id local_memory{}; | ||
| 1343 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; | ||
| 1344 | std::map<Attribute::Index, Id> input_attributes; | ||
| 1345 | std::map<Attribute::Index, Id> output_attributes; | ||
| 1346 | std::map<u32, Id> constant_buffers; | ||
| 1347 | std::map<GlobalMemoryBase, Id> global_buffers; | ||
| 1348 | std::map<u32, SamplerImage> sampler_images; | ||
| 1349 | |||
| 1350 | Id instance_index{}; | ||
| 1351 | Id vertex_index{}; | ||
| 1352 | std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; | ||
| 1353 | Id frag_depth{}; | ||
| 1354 | Id frag_coord{}; | ||
| 1355 | Id front_facing{}; | ||
| 1356 | |||
| 1357 | u32 position_index{}; | ||
| 1358 | u32 point_size_index{}; | ||
| 1359 | u32 clip_distances_index{}; | ||
| 1360 | |||
| 1361 | std::vector<Id> interfaces; | ||
| 1362 | |||
| 1363 | u32 const_buffers_base_binding{}; | ||
| 1364 | u32 global_buffers_base_binding{}; | ||
| 1365 | u32 samplers_base_binding{}; | ||
| 1366 | |||
| 1367 | Id execute_function{}; | ||
| 1368 | Id jmp_to{}; | ||
| 1369 | Id flow_stack_top{}; | ||
| 1370 | Id flow_stack{}; | ||
| 1371 | Id continue_label{}; | ||
| 1372 | std::map<u32, Id> labels; | ||
| 1373 | }; | ||
| 1374 | |||
| 1375 | DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage) { | ||
| 1376 | auto decompiler = std::make_unique<SPIRVDecompiler>(ir, stage); | ||
| 1377 | decompiler->Decompile(); | ||
| 1378 | return {std::move(decompiler), decompiler->GetShaderEntries()}; | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | } // namespace Vulkan::VKShader | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h new file mode 100644 index 000000000..329d8fa38 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h | |||
| @@ -0,0 +1,80 @@ | |||
| 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 <array> | ||
| 8 | #include <memory> | ||
| 9 | #include <set> | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include <sirit/sirit.h> | ||
| 14 | |||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "video_core/engines/maxwell_3d.h" | ||
| 17 | #include "video_core/shader/shader_ir.h" | ||
| 18 | |||
| 19 | namespace VideoCommon::Shader { | ||
| 20 | class ShaderIR; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Vulkan::VKShader { | ||
| 24 | |||
| 25 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 26 | |||
| 27 | using SamplerEntry = VideoCommon::Shader::Sampler; | ||
| 28 | |||
| 29 | constexpr u32 DESCRIPTOR_SET = 0; | ||
| 30 | |||
| 31 | class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer { | ||
| 32 | public: | ||
| 33 | explicit constexpr ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, u32 index) | ||
| 34 | : VideoCommon::Shader::ConstBuffer{entry}, index{index} {} | ||
| 35 | |||
| 36 | constexpr u32 GetIndex() const { | ||
| 37 | return index; | ||
| 38 | } | ||
| 39 | |||
| 40 | private: | ||
| 41 | u32 index{}; | ||
| 42 | }; | ||
| 43 | |||
| 44 | class GlobalBufferEntry { | ||
| 45 | public: | ||
| 46 | explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset) | ||
| 47 | : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} | ||
| 48 | |||
| 49 | u32 GetCbufIndex() const { | ||
| 50 | return cbuf_index; | ||
| 51 | } | ||
| 52 | |||
| 53 | u32 GetCbufOffset() const { | ||
| 54 | return cbuf_offset; | ||
| 55 | } | ||
| 56 | |||
| 57 | private: | ||
| 58 | u32 cbuf_index{}; | ||
| 59 | u32 cbuf_offset{}; | ||
| 60 | }; | ||
| 61 | |||
| 62 | struct ShaderEntries { | ||
| 63 | u32 const_buffers_base_binding{}; | ||
| 64 | u32 global_buffers_base_binding{}; | ||
| 65 | u32 samplers_base_binding{}; | ||
| 66 | std::vector<ConstBufferEntry> const_buffers; | ||
| 67 | std::vector<GlobalBufferEntry> global_buffers; | ||
| 68 | std::vector<SamplerEntry> samplers; | ||
| 69 | std::set<u32> attributes; | ||
| 70 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | ||
| 71 | std::size_t shader_length{}; | ||
| 72 | Sirit::Id entry_function{}; | ||
| 73 | std::vector<Sirit::Id> interfaces; | ||
| 74 | }; | ||
| 75 | |||
| 76 | using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>; | ||
| 77 | |||
| 78 | DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage); | ||
| 79 | |||
| 80 | } // namespace Vulkan::VKShader | ||
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index b2b706cb8..ea1092db1 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp | |||
| @@ -19,6 +19,23 @@ using Tegra::Shader::Instruction; | |||
| 19 | using Tegra::Shader::OpCode; | 19 | using Tegra::Shader::OpCode; |
| 20 | using Tegra::Shader::Register; | 20 | using Tegra::Shader::Register; |
| 21 | 21 | ||
| 22 | namespace { | ||
| 23 | u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) { | ||
| 24 | switch (uniform_type) { | ||
| 25 | case Tegra::Shader::UniformType::Single: | ||
| 26 | return 1; | ||
| 27 | case Tegra::Shader::UniformType::Double: | ||
| 28 | return 2; | ||
| 29 | case Tegra::Shader::UniformType::Quad: | ||
| 30 | case Tegra::Shader::UniformType::UnsignedQuad: | ||
| 31 | return 4; | ||
| 32 | default: | ||
| 33 | UNIMPLEMENTED_MSG("Unimplemented size={}!", static_cast<u32>(uniform_type)); | ||
| 34 | return 1; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } // namespace | ||
| 38 | |||
| 22 | u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | 39 | u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { |
| 23 | const Instruction instr = {program_code[pc]}; | 40 | const Instruction instr = {program_code[pc]}; |
| 24 | const auto opcode = OpCode::Decode(instr); | 41 | const auto opcode = OpCode::Decode(instr); |
| @@ -127,45 +144,15 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 127 | break; | 144 | break; |
| 128 | } | 145 | } |
| 129 | case OpCode::Id::LDG: { | 146 | case OpCode::Id::LDG: { |
| 130 | const u32 count = [&]() { | 147 | const auto [real_address_base, base_address, descriptor] = |
| 131 | switch (instr.ldg.type) { | 148 | TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), |
| 132 | case Tegra::Shader::UniformType::Single: | 149 | static_cast<u32>(instr.ldg.immediate_offset.Value()), false); |
| 133 | return 1; | ||
| 134 | case Tegra::Shader::UniformType::Double: | ||
| 135 | return 2; | ||
| 136 | case Tegra::Shader::UniformType::Quad: | ||
| 137 | case Tegra::Shader::UniformType::UnsignedQuad: | ||
| 138 | return 4; | ||
| 139 | default: | ||
| 140 | UNIMPLEMENTED_MSG("Unimplemented LDG size!"); | ||
| 141 | return 1; | ||
| 142 | } | ||
| 143 | }(); | ||
| 144 | |||
| 145 | const Node addr_register = GetRegister(instr.gpr8); | ||
| 146 | const Node base_address = | ||
| 147 | TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size())); | ||
| 148 | const auto cbuf = std::get_if<CbufNode>(base_address); | ||
| 149 | ASSERT(cbuf != nullptr); | ||
| 150 | const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset()); | ||
| 151 | ASSERT(cbuf_offset_imm != nullptr); | ||
| 152 | const auto cbuf_offset = cbuf_offset_imm->GetValue(); | ||
| 153 | |||
| 154 | bb.push_back(Comment( | ||
| 155 | fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset))); | ||
| 156 | |||
| 157 | const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset}; | ||
| 158 | used_global_memory_bases.insert(descriptor); | ||
| 159 | |||
| 160 | const Node immediate_offset = | ||
| 161 | Immediate(static_cast<u32>(instr.ldg.immediate_offset.Value())); | ||
| 162 | const Node base_real_address = | ||
| 163 | Operation(OperationCode::UAdd, NO_PRECISE, immediate_offset, addr_register); | ||
| 164 | 150 | ||
| 151 | const u32 count = GetUniformTypeElementsCount(instr.ldg.type); | ||
| 165 | for (u32 i = 0; i < count; ++i) { | 152 | for (u32 i = 0; i < count; ++i) { |
| 166 | const Node it_offset = Immediate(i * 4); | 153 | const Node it_offset = Immediate(i * 4); |
| 167 | const Node real_address = | 154 | const Node real_address = |
| 168 | Operation(OperationCode::UAdd, NO_PRECISE, base_real_address, it_offset); | 155 | Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); |
| 169 | const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); | 156 | const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); |
| 170 | 157 | ||
| 171 | SetTemporal(bb, i, gmem); | 158 | SetTemporal(bb, i, gmem); |
| @@ -175,6 +162,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 175 | } | 162 | } |
| 176 | break; | 163 | break; |
| 177 | } | 164 | } |
| 165 | case OpCode::Id::STG: { | ||
| 166 | const auto [real_address_base, base_address, descriptor] = | ||
| 167 | TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), | ||
| 168 | static_cast<u32>(instr.stg.immediate_offset.Value()), true); | ||
| 169 | |||
| 170 | // Encode in temporary registers like this: real_base_address, {registers_to_be_written...} | ||
| 171 | SetTemporal(bb, 0, real_address_base); | ||
| 172 | |||
| 173 | const u32 count = GetUniformTypeElementsCount(instr.stg.type); | ||
| 174 | for (u32 i = 0; i < count; ++i) { | ||
| 175 | SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i)); | ||
| 176 | } | ||
| 177 | for (u32 i = 0; i < count; ++i) { | ||
| 178 | const Node it_offset = Immediate(i * 4); | ||
| 179 | const Node real_address = | ||
| 180 | Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); | ||
| 181 | const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); | ||
| 182 | |||
| 183 | bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1))); | ||
| 184 | } | ||
| 185 | break; | ||
| 186 | } | ||
| 178 | case OpCode::Id::ST_A: { | 187 | case OpCode::Id::ST_A: { |
| 179 | UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, | 188 | UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, |
| 180 | "Indirect attribute loads are not supported"); | 189 | "Indirect attribute loads are not supported"); |
| @@ -237,4 +246,34 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 237 | return pc; | 246 | return pc; |
| 238 | } | 247 | } |
| 239 | 248 | ||
| 249 | std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb, | ||
| 250 | Node addr_register, | ||
| 251 | u32 immediate_offset, | ||
| 252 | bool is_write) { | ||
| 253 | const Node base_address{ | ||
| 254 | TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))}; | ||
| 255 | const auto cbuf = std::get_if<CbufNode>(base_address); | ||
| 256 | ASSERT(cbuf != nullptr); | ||
| 257 | const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset()); | ||
| 258 | ASSERT(cbuf_offset_imm != nullptr); | ||
| 259 | const auto cbuf_offset = cbuf_offset_imm->GetValue(); | ||
| 260 | |||
| 261 | bb.push_back( | ||
| 262 | Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset))); | ||
| 263 | |||
| 264 | const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset}; | ||
| 265 | const auto& [entry, is_new] = used_global_memory.try_emplace(descriptor); | ||
| 266 | auto& usage = entry->second; | ||
| 267 | if (is_write) { | ||
| 268 | usage.is_written = true; | ||
| 269 | } else { | ||
| 270 | usage.is_read = true; | ||
| 271 | } | ||
| 272 | |||
| 273 | const auto real_address = | ||
| 274 | Operation(OperationCode::UAdd, NO_PRECISE, Immediate(immediate_offset), addr_register); | ||
| 275 | |||
| 276 | return {real_address, base_address, descriptor}; | ||
| 277 | } | ||
| 278 | |||
| 240 | } // namespace VideoCommon::Shader | 279 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index a99ae19bf..a775b402b 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp | |||
| @@ -7,7 +7,9 @@ | |||
| 7 | #include <fmt/format.h> | 7 | #include <fmt/format.h> |
| 8 | 8 | ||
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/logging/log.h" | ||
| 11 | #include "video_core/engines/shader_bytecode.h" | 13 | #include "video_core/engines/shader_bytecode.h" |
| 12 | #include "video_core/shader/shader_ir.h" | 14 | #include "video_core/shader/shader_ir.h" |
| 13 | 15 | ||
| @@ -41,19 +43,18 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 41 | 43 | ||
| 42 | switch (opcode->get().GetId()) { | 44 | switch (opcode->get().GetId()) { |
| 43 | case OpCode::Id::TEX: { | 45 | case OpCode::Id::TEX: { |
| 44 | UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI), | ||
| 45 | "AOFFI is not implemented"); | ||
| 46 | |||
| 47 | if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) { | 46 | if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) { |
| 48 | LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete"); | 47 | LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete"); |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | const TextureType texture_type{instr.tex.texture_type}; | 50 | const TextureType texture_type{instr.tex.texture_type}; |
| 52 | const bool is_array = instr.tex.array != 0; | 51 | const bool is_array = instr.tex.array != 0; |
| 52 | const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI); | ||
| 53 | const bool depth_compare = instr.tex.UsesMiscMode(TextureMiscMode::DC); | 53 | const bool depth_compare = instr.tex.UsesMiscMode(TextureMiscMode::DC); |
| 54 | const auto process_mode = instr.tex.GetTextureProcessMode(); | 54 | const auto process_mode = instr.tex.GetTextureProcessMode(); |
| 55 | WriteTexInstructionFloat( | 55 | WriteTexInstructionFloat( |
| 56 | bb, instr, GetTexCode(instr, texture_type, process_mode, depth_compare, is_array)); | 56 | bb, instr, |
| 57 | GetTexCode(instr, texture_type, process_mode, depth_compare, is_array, is_aoffi)); | ||
| 57 | break; | 58 | break; |
| 58 | } | 59 | } |
| 59 | case OpCode::Id::TEXS: { | 60 | case OpCode::Id::TEXS: { |
| @@ -78,8 +79,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 78 | } | 79 | } |
| 79 | case OpCode::Id::TLD4: { | 80 | case OpCode::Id::TLD4: { |
| 80 | ASSERT(instr.tld4.array == 0); | 81 | ASSERT(instr.tld4.array == 0); |
| 81 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI), | ||
| 82 | "AOFFI is not implemented"); | ||
| 83 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV), | 82 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV), |
| 84 | "NDV is not implemented"); | 83 | "NDV is not implemented"); |
| 85 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP), | 84 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP), |
| @@ -92,8 +91,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 92 | const auto texture_type = instr.tld4.texture_type.Value(); | 91 | const auto texture_type = instr.tld4.texture_type.Value(); |
| 93 | const bool depth_compare = instr.tld4.UsesMiscMode(TextureMiscMode::DC); | 92 | const bool depth_compare = instr.tld4.UsesMiscMode(TextureMiscMode::DC); |
| 94 | const bool is_array = instr.tld4.array != 0; | 93 | const bool is_array = instr.tld4.array != 0; |
| 95 | WriteTexInstructionFloat(bb, instr, | 94 | const bool is_aoffi = instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI); |
| 96 | GetTld4Code(instr, texture_type, depth_compare, is_array)); | 95 | WriteTexInstructionFloat( |
| 96 | bb, instr, GetTld4Code(instr, texture_type, depth_compare, is_array, is_aoffi)); | ||
| 97 | break; | 97 | break; |
| 98 | } | 98 | } |
| 99 | case OpCode::Id::TLD4S: { | 99 | case OpCode::Id::TLD4S: { |
| @@ -127,7 +127,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 127 | Node4 values; | 127 | Node4 values; |
| 128 | for (u32 element = 0; element < values.size(); ++element) { | 128 | for (u32 element = 0; element < values.size(); ++element) { |
| 129 | auto coords_copy = coords; | 129 | auto coords_copy = coords; |
| 130 | MetaTexture meta{sampler, {}, {}, {}, {}, component, element}; | 130 | MetaTexture meta{sampler, {}, {}, {}, {}, {}, component, element}; |
| 131 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); | 131 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); |
| 132 | } | 132 | } |
| 133 | 133 | ||
| @@ -152,7 +152,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 152 | if (!instr.txq.IsComponentEnabled(element)) { | 152 | if (!instr.txq.IsComponentEnabled(element)) { |
| 153 | continue; | 153 | continue; |
| 154 | } | 154 | } |
| 155 | MetaTexture meta{sampler, {}, {}, {}, {}, {}, element}; | 155 | MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element}; |
| 156 | const Node value = | 156 | const Node value = |
| 157 | Operation(OperationCode::TextureQueryDimensions, meta, GetRegister(instr.gpr8)); | 157 | Operation(OperationCode::TextureQueryDimensions, meta, GetRegister(instr.gpr8)); |
| 158 | SetTemporal(bb, indexer++, value); | 158 | SetTemporal(bb, indexer++, value); |
| @@ -202,7 +202,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 202 | 202 | ||
| 203 | for (u32 element = 0; element < 2; ++element) { | 203 | for (u32 element = 0; element < 2; ++element) { |
| 204 | auto params = coords; | 204 | auto params = coords; |
| 205 | MetaTexture meta{sampler, {}, {}, {}, {}, {}, element}; | 205 | MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element}; |
| 206 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); | 206 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); |
| 207 | SetTemporal(bb, element, value); | 207 | SetTemporal(bb, element, value); |
| 208 | } | 208 | } |
| @@ -325,7 +325,8 @@ void ShaderIR::WriteTexsInstructionHalfFloat(NodeBlock& bb, Instruction instr, | |||
| 325 | 325 | ||
| 326 | Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | 326 | Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, |
| 327 | TextureProcessMode process_mode, std::vector<Node> coords, | 327 | TextureProcessMode process_mode, std::vector<Node> coords, |
| 328 | Node array, Node depth_compare, u32 bias_offset) { | 328 | Node array, Node depth_compare, u32 bias_offset, |
| 329 | std::vector<Node> aoffi) { | ||
| 329 | const bool is_array = array; | 330 | const bool is_array = array; |
| 330 | const bool is_shadow = depth_compare; | 331 | const bool is_shadow = depth_compare; |
| 331 | 332 | ||
| @@ -374,7 +375,7 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | |||
| 374 | Node4 values; | 375 | Node4 values; |
| 375 | for (u32 element = 0; element < values.size(); ++element) { | 376 | for (u32 element = 0; element < values.size(); ++element) { |
| 376 | auto copy_coords = coords; | 377 | auto copy_coords = coords; |
| 377 | MetaTexture meta{sampler, array, depth_compare, bias, lod, {}, element}; | 378 | MetaTexture meta{sampler, array, depth_compare, aoffi, bias, lod, {}, element}; |
| 378 | values[element] = Operation(read_method, meta, std::move(copy_coords)); | 379 | values[element] = Operation(read_method, meta, std::move(copy_coords)); |
| 379 | } | 380 | } |
| 380 | 381 | ||
| @@ -382,9 +383,15 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | |||
| 382 | } | 383 | } |
| 383 | 384 | ||
| 384 | Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, | 385 | Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, |
| 385 | TextureProcessMode process_mode, bool depth_compare, bool is_array) { | 386 | TextureProcessMode process_mode, bool depth_compare, bool is_array, |
| 386 | const bool lod_bias_enabled = | 387 | bool is_aoffi) { |
| 387 | (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ); | 388 | const bool lod_bias_enabled{ |
| 389 | (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ)}; | ||
| 390 | |||
| 391 | u64 parameter_register = instr.gpr20.Value(); | ||
| 392 | if (lod_bias_enabled) { | ||
| 393 | ++parameter_register; | ||
| 394 | } | ||
| 388 | 395 | ||
| 389 | const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement( | 396 | const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement( |
| 390 | texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5); | 397 | texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5); |
| @@ -404,15 +411,19 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, | |||
| 404 | 411 | ||
| 405 | const Node array = is_array ? GetRegister(array_register) : nullptr; | 412 | const Node array = is_array ? GetRegister(array_register) : nullptr; |
| 406 | 413 | ||
| 414 | std::vector<Node> aoffi; | ||
| 415 | if (is_aoffi) { | ||
| 416 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); | ||
| 417 | } | ||
| 418 | |||
| 407 | Node dc{}; | 419 | Node dc{}; |
| 408 | if (depth_compare) { | 420 | if (depth_compare) { |
| 409 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod | 421 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod |
| 410 | // or bias are used | 422 | // or bias are used |
| 411 | const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); | 423 | dc = GetRegister(parameter_register++); |
| 412 | dc = GetRegister(depth_register); | ||
| 413 | } | 424 | } |
| 414 | 425 | ||
| 415 | return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, 0); | 426 | return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, 0, aoffi); |
| 416 | } | 427 | } |
| 417 | 428 | ||
| 418 | Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, | 429 | Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, |
| @@ -448,11 +459,11 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, | |||
| 448 | dc = GetRegister(depth_register); | 459 | dc = GetRegister(depth_register); |
| 449 | } | 460 | } |
| 450 | 461 | ||
| 451 | return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset); | 462 | return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset, {}); |
| 452 | } | 463 | } |
| 453 | 464 | ||
| 454 | Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare, | 465 | Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare, |
| 455 | bool is_array) { | 466 | bool is_array, bool is_aoffi) { |
| 456 | const std::size_t coord_count = GetCoordCount(texture_type); | 467 | const std::size_t coord_count = GetCoordCount(texture_type); |
| 457 | const std::size_t total_coord_count = coord_count + (is_array ? 1 : 0); | 468 | const std::size_t total_coord_count = coord_count + (is_array ? 1 : 0); |
| 458 | const std::size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0); | 469 | const std::size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0); |
| @@ -463,15 +474,27 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 463 | const u64 coord_register = array_register + (is_array ? 1 : 0); | 474 | const u64 coord_register = array_register + (is_array ? 1 : 0); |
| 464 | 475 | ||
| 465 | std::vector<Node> coords; | 476 | std::vector<Node> coords; |
| 466 | for (size_t i = 0; i < coord_count; ++i) | 477 | for (std::size_t i = 0; i < coord_count; ++i) { |
| 467 | coords.push_back(GetRegister(coord_register + i)); | 478 | coords.push_back(GetRegister(coord_register + i)); |
| 479 | } | ||
| 480 | |||
| 481 | u64 parameter_register = instr.gpr20.Value(); | ||
| 482 | std::vector<Node> aoffi; | ||
| 483 | if (is_aoffi) { | ||
| 484 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, true); | ||
| 485 | } | ||
| 486 | |||
| 487 | Node dc{}; | ||
| 488 | if (depth_compare) { | ||
| 489 | dc = GetRegister(parameter_register++); | ||
| 490 | } | ||
| 468 | 491 | ||
| 469 | const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare); | 492 | const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare); |
| 470 | 493 | ||
| 471 | Node4 values; | 494 | Node4 values; |
| 472 | for (u32 element = 0; element < values.size(); ++element) { | 495 | for (u32 element = 0; element < values.size(); ++element) { |
| 473 | auto coords_copy = coords; | 496 | auto coords_copy = coords; |
| 474 | MetaTexture meta{sampler, GetRegister(array_register), {}, {}, {}, {}, element}; | 497 | MetaTexture meta{sampler, GetRegister(array_register), dc, aoffi, {}, {}, {}, element}; |
| 475 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); | 498 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); |
| 476 | } | 499 | } |
| 477 | 500 | ||
| @@ -507,7 +530,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is | |||
| 507 | Node4 values; | 530 | Node4 values; |
| 508 | for (u32 element = 0; element < values.size(); ++element) { | 531 | for (u32 element = 0; element < values.size(); ++element) { |
| 509 | auto coords_copy = coords; | 532 | auto coords_copy = coords; |
| 510 | MetaTexture meta{sampler, array, {}, {}, lod, {}, element}; | 533 | MetaTexture meta{sampler, array, {}, {}, {}, lod, {}, element}; |
| 511 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 534 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 512 | } | 535 | } |
| 513 | return values; | 536 | return values; |
| @@ -531,4 +554,45 @@ std::tuple<std::size_t, std::size_t> ShaderIR::ValidateAndGetCoordinateElement( | |||
| 531 | return {coord_count, total_coord_count}; | 554 | return {coord_count, total_coord_count}; |
| 532 | } | 555 | } |
| 533 | 556 | ||
| 534 | } // namespace VideoCommon::Shader \ No newline at end of file | 557 | std::vector<Node> ShaderIR::GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count, |
| 558 | bool is_tld4) { | ||
| 559 | const auto [coord_offsets, size, wrap_value, | ||
| 560 | diff_value] = [is_tld4]() -> std::tuple<std::array<u32, 3>, u32, s32, s32> { | ||
| 561 | if (is_tld4) { | ||
| 562 | return {{0, 8, 16}, 6, 32, 64}; | ||
| 563 | } else { | ||
| 564 | return {{0, 4, 8}, 4, 8, 16}; | ||
| 565 | } | ||
| 566 | }(); | ||
| 567 | const u32 mask = (1U << size) - 1; | ||
| 568 | |||
| 569 | std::vector<Node> aoffi; | ||
| 570 | aoffi.reserve(coord_count); | ||
| 571 | |||
| 572 | const auto aoffi_immediate{ | ||
| 573 | TrackImmediate(aoffi_reg, global_code, static_cast<s64>(global_code.size()))}; | ||
| 574 | if (!aoffi_immediate) { | ||
| 575 | // Variable access, not supported on AMD. | ||
| 576 | LOG_WARNING(HW_GPU, | ||
| 577 | "AOFFI constant folding failed, some hardware might have graphical issues"); | ||
| 578 | for (std::size_t coord = 0; coord < coord_count; ++coord) { | ||
| 579 | const Node value = BitfieldExtract(aoffi_reg, coord_offsets.at(coord), size); | ||
| 580 | const Node condition = | ||
| 581 | Operation(OperationCode::LogicalIGreaterEqual, value, Immediate(wrap_value)); | ||
| 582 | const Node negative = Operation(OperationCode::IAdd, value, Immediate(-diff_value)); | ||
| 583 | aoffi.push_back(Operation(OperationCode::Select, condition, negative, value)); | ||
| 584 | } | ||
| 585 | return aoffi; | ||
| 586 | } | ||
| 587 | |||
| 588 | for (std::size_t coord = 0; coord < coord_count; ++coord) { | ||
| 589 | s32 value = (*aoffi_immediate >> coord_offsets.at(coord)) & mask; | ||
| 590 | if (value >= wrap_value) { | ||
| 591 | value -= diff_value; | ||
| 592 | } | ||
| 593 | aoffi.push_back(Immediate(value)); | ||
| 594 | } | ||
| 595 | return aoffi; | ||
| 596 | } | ||
| 597 | |||
| 598 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp index c34843307..db15c0718 100644 --- a/src/video_core/shader/decode/xmad.cpp +++ b/src/video_core/shader/decode/xmad.cpp | |||
| @@ -29,39 +29,55 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) { | |||
| 29 | const bool is_signed_b = instr.xmad.sign_b == 1; | 29 | const bool is_signed_b = instr.xmad.sign_b == 1; |
| 30 | const bool is_signed_c = is_signed_a; | 30 | const bool is_signed_c = is_signed_a; |
| 31 | 31 | ||
| 32 | auto [is_merge, op_b, op_c] = [&]() -> std::tuple<bool, Node, Node> { | 32 | auto [is_merge, is_psl, is_high_b, mode, op_b, |
| 33 | op_c] = [&]() -> std::tuple<bool, bool, bool, Tegra::Shader::XmadMode, Node, Node> { | ||
| 33 | switch (opcode->get().GetId()) { | 34 | switch (opcode->get().GetId()) { |
| 34 | case OpCode::Id::XMAD_CR: | 35 | case OpCode::Id::XMAD_CR: |
| 35 | return {instr.xmad.merge_56, | 36 | return {instr.xmad.merge_56, |
| 37 | instr.xmad.product_shift_left_second, | ||
| 38 | instr.xmad.high_b, | ||
| 39 | instr.xmad.mode_cbf, | ||
| 36 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), | 40 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), |
| 37 | GetRegister(instr.gpr39)}; | 41 | GetRegister(instr.gpr39)}; |
| 38 | case OpCode::Id::XMAD_RR: | 42 | case OpCode::Id::XMAD_RR: |
| 39 | return {instr.xmad.merge_37, GetRegister(instr.gpr20), GetRegister(instr.gpr39)}; | 43 | return {instr.xmad.merge_37, instr.xmad.product_shift_left, instr.xmad.high_b_rr, |
| 44 | instr.xmad.mode, GetRegister(instr.gpr20), GetRegister(instr.gpr39)}; | ||
| 40 | case OpCode::Id::XMAD_RC: | 45 | case OpCode::Id::XMAD_RC: |
| 41 | return {false, GetRegister(instr.gpr39), | 46 | return {false, |
| 47 | false, | ||
| 48 | instr.xmad.high_b, | ||
| 49 | instr.xmad.mode_cbf, | ||
| 50 | GetRegister(instr.gpr39), | ||
| 42 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; | 51 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; |
| 43 | case OpCode::Id::XMAD_IMM: | 52 | case OpCode::Id::XMAD_IMM: |
| 44 | return {instr.xmad.merge_37, Immediate(static_cast<u32>(instr.xmad.imm20_16)), | 53 | return {instr.xmad.merge_37, |
| 54 | instr.xmad.product_shift_left, | ||
| 55 | false, | ||
| 56 | instr.xmad.mode, | ||
| 57 | Immediate(static_cast<u32>(instr.xmad.imm20_16)), | ||
| 45 | GetRegister(instr.gpr39)}; | 58 | GetRegister(instr.gpr39)}; |
| 46 | } | 59 | } |
| 47 | UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); | 60 | UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); |
| 48 | return {false, Immediate(0), Immediate(0)}; | 61 | return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)}; |
| 49 | }(); | 62 | }(); |
| 50 | 63 | ||
| 51 | op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); | 64 | op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); |
| 52 | 65 | ||
| 53 | const Node original_b = op_b; | 66 | const Node original_b = op_b; |
| 54 | op_b = BitfieldExtract(op_b, instr.xmad.high_b ? 16 : 0, 16); | 67 | op_b = BitfieldExtract(op_b, is_high_b ? 16 : 0, 16); |
| 55 | 68 | ||
| 56 | // TODO(Rodrigo): Use an appropiate sign for this operation | 69 | // TODO(Rodrigo): Use an appropiate sign for this operation |
| 57 | Node product = Operation(OperationCode::IMul, NO_PRECISE, op_a, op_b); | 70 | Node product = Operation(OperationCode::IMul, NO_PRECISE, op_a, op_b); |
| 58 | if (instr.xmad.product_shift_left) { | 71 | if (is_psl) { |
| 59 | product = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, product, Immediate(16)); | 72 | product = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, product, Immediate(16)); |
| 60 | } | 73 | } |
| 74 | SetTemporal(bb, 0, product); | ||
| 75 | product = GetTemporal(0); | ||
| 61 | 76 | ||
| 62 | const Node original_c = op_c; | 77 | const Node original_c = op_c; |
| 78 | const Tegra::Shader::XmadMode set_mode = mode; // Workaround to clang compile error | ||
| 63 | op_c = [&]() { | 79 | op_c = [&]() { |
| 64 | switch (instr.xmad.mode) { | 80 | switch (set_mode) { |
| 65 | case Tegra::Shader::XmadMode::None: | 81 | case Tegra::Shader::XmadMode::None: |
| 66 | return original_c; | 82 | return original_c; |
| 67 | case Tegra::Shader::XmadMode::CLo: | 83 | case Tegra::Shader::XmadMode::CLo: |
| @@ -80,8 +96,13 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) { | |||
| 80 | } | 96 | } |
| 81 | }(); | 97 | }(); |
| 82 | 98 | ||
| 99 | SetTemporal(bb, 1, op_c); | ||
| 100 | op_c = GetTemporal(1); | ||
| 101 | |||
| 83 | // TODO(Rodrigo): Use an appropiate sign for this operation | 102 | // TODO(Rodrigo): Use an appropiate sign for this operation |
| 84 | Node sum = Operation(OperationCode::IAdd, product, op_c); | 103 | Node sum = Operation(OperationCode::IAdd, product, op_c); |
| 104 | SetTemporal(bb, 2, sum); | ||
| 105 | sum = GetTemporal(2); | ||
| 85 | if (is_merge) { | 106 | if (is_merge) { |
| 86 | const Node a = BitfieldExtract(sum, 0, 16); | 107 | const Node a = BitfieldExtract(sum, 0, 16); |
| 87 | const Node b = | 108 | const Node b = |
| @@ -95,4 +116,4 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) { | |||
| 95 | return pc; | 116 | return pc; |
| 96 | } | 117 | } |
| 97 | 118 | ||
| 98 | } // namespace VideoCommon::Shader \ No newline at end of file | 119 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 5bc3a3900..1afab08c0 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstring> | 8 | #include <cstring> |
| 9 | #include <map> | 9 | #include <map> |
| 10 | #include <optional> | ||
| 10 | #include <set> | 11 | #include <set> |
| 11 | #include <string> | 12 | #include <string> |
| 12 | #include <tuple> | 13 | #include <tuple> |
| @@ -275,6 +276,11 @@ struct GlobalMemoryBase { | |||
| 275 | } | 276 | } |
| 276 | }; | 277 | }; |
| 277 | 278 | ||
| 279 | struct GlobalMemoryUsage { | ||
| 280 | bool is_read{}; | ||
| 281 | bool is_written{}; | ||
| 282 | }; | ||
| 283 | |||
| 278 | struct MetaArithmetic { | 284 | struct MetaArithmetic { |
| 279 | bool precise{}; | 285 | bool precise{}; |
| 280 | }; | 286 | }; |
| @@ -290,6 +296,7 @@ struct MetaTexture { | |||
| 290 | const Sampler& sampler; | 296 | const Sampler& sampler; |
| 291 | Node array{}; | 297 | Node array{}; |
| 292 | Node depth_compare{}; | 298 | Node depth_compare{}; |
| 299 | std::vector<Node> aoffi; | ||
| 293 | Node bias{}; | 300 | Node bias{}; |
| 294 | Node lod{}; | 301 | Node lod{}; |
| 295 | Node component{}; | 302 | Node component{}; |
| @@ -576,8 +583,8 @@ public: | |||
| 576 | return used_clip_distances; | 583 | return used_clip_distances; |
| 577 | } | 584 | } |
| 578 | 585 | ||
| 579 | const std::set<GlobalMemoryBase>& GetGlobalMemoryBases() const { | 586 | const std::map<GlobalMemoryBase, GlobalMemoryUsage>& GetGlobalMemory() const { |
| 580 | return used_global_memory_bases; | 587 | return used_global_memory; |
| 581 | } | 588 | } |
| 582 | 589 | ||
| 583 | std::size_t GetLength() const { | 590 | std::size_t GetLength() const { |
| @@ -741,14 +748,14 @@ private: | |||
| 741 | 748 | ||
| 742 | Node4 GetTexCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, | 749 | Node4 GetTexCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, |
| 743 | Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, | 750 | Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, |
| 744 | bool is_array); | 751 | bool is_array, bool is_aoffi); |
| 745 | 752 | ||
| 746 | Node4 GetTexsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, | 753 | Node4 GetTexsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, |
| 747 | Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, | 754 | Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, |
| 748 | bool is_array); | 755 | bool is_array); |
| 749 | 756 | ||
| 750 | Node4 GetTld4Code(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, | 757 | Node4 GetTld4Code(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, |
| 751 | bool depth_compare, bool is_array); | 758 | bool depth_compare, bool is_array, bool is_aoffi); |
| 752 | 759 | ||
| 753 | Node4 GetTldsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, | 760 | Node4 GetTldsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, |
| 754 | bool is_array); | 761 | bool is_array); |
| @@ -757,9 +764,11 @@ private: | |||
| 757 | Tegra::Shader::TextureType texture_type, bool depth_compare, bool is_array, | 764 | Tegra::Shader::TextureType texture_type, bool depth_compare, bool is_array, |
| 758 | bool lod_bias_enabled, std::size_t max_coords, std::size_t max_inputs); | 765 | bool lod_bias_enabled, std::size_t max_coords, std::size_t max_inputs); |
| 759 | 766 | ||
| 767 | std::vector<Node> GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count, bool is_tld4); | ||
| 768 | |||
| 760 | Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, | 769 | Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, |
| 761 | Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords, | 770 | Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords, |
| 762 | Node array, Node depth_compare, u32 bias_offset); | 771 | Node array, Node depth_compare, u32 bias_offset, std::vector<Node> aoffi); |
| 763 | 772 | ||
| 764 | Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type, | 773 | Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type, |
| 765 | u64 byte_height); | 774 | u64 byte_height); |
| @@ -773,8 +782,15 @@ private: | |||
| 773 | 782 | ||
| 774 | Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor); | 783 | Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor); |
| 775 | 784 | ||
| 785 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor); | ||
| 786 | |||
| 776 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor); | 787 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor); |
| 777 | 788 | ||
| 789 | std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb, | ||
| 790 | Node addr_register, | ||
| 791 | u32 immediate_offset, | ||
| 792 | bool is_write); | ||
| 793 | |||
| 778 | template <typename... T> | 794 | template <typename... T> |
| 779 | Node Operation(OperationCode code, const T*... operands) { | 795 | Node Operation(OperationCode code, const T*... operands) { |
| 780 | return StoreNode(OperationNode(code, operands...)); | 796 | return StoreNode(OperationNode(code, operands...)); |
| @@ -828,7 +844,7 @@ private: | |||
| 828 | std::map<u32, ConstBuffer> used_cbufs; | 844 | std::map<u32, ConstBuffer> used_cbufs; |
| 829 | std::set<Sampler> used_samplers; | 845 | std::set<Sampler> used_samplers; |
| 830 | std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; | 846 | std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; |
| 831 | std::set<GlobalMemoryBase> used_global_memory_bases; | 847 | std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; |
| 832 | 848 | ||
| 833 | Tegra::Shader::Header header; | 849 | Tegra::Shader::Header header; |
| 834 | }; | 850 | }; |
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp index 33b071747..4505667ff 100644 --- a/src/video_core/shader/track.cpp +++ b/src/video_core/shader/track.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <utility> | 6 | #include <utility> |
| 7 | #include <variant> | 7 | #include <variant> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | ||
| 9 | #include "video_core/shader/shader_ir.h" | 10 | #include "video_core/shader/shader_ir.h" |
| 10 | 11 | ||
| 11 | namespace VideoCommon::Shader { | 12 | namespace VideoCommon::Shader { |
| @@ -14,7 +15,7 @@ namespace { | |||
| 14 | std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, | 15 | std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, |
| 15 | OperationCode operation_code) { | 16 | OperationCode operation_code) { |
| 16 | for (; cursor >= 0; --cursor) { | 17 | for (; cursor >= 0; --cursor) { |
| 17 | const Node node = code[cursor]; | 18 | const Node node = code.at(cursor); |
| 18 | if (const auto operation = std::get_if<OperationNode>(node)) { | 19 | if (const auto operation = std::get_if<OperationNode>(node)) { |
| 19 | if (operation->GetCode() == operation_code) | 20 | if (operation->GetCode() == operation_code) |
| 20 | return {node, cursor}; | 21 | return {node, cursor}; |
| @@ -64,6 +65,20 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) { | |||
| 64 | return nullptr; | 65 | return nullptr; |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 68 | std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) { | ||
| 69 | // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register | ||
| 70 | // that it uses as operand | ||
| 71 | const auto [found, found_cursor] = | ||
| 72 | TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1); | ||
| 73 | if (!found) { | ||
| 74 | return {}; | ||
| 75 | } | ||
| 76 | if (const auto immediate = std::get_if<ImmediateNode>(found)) { | ||
| 77 | return immediate->GetValue(); | ||
| 78 | } | ||
| 79 | return {}; | ||
| 80 | } | ||
| 81 | |||
| 67 | std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, | 82 | std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, |
| 68 | s64 cursor) { | 83 | s64 cursor) { |
| 69 | for (; cursor >= 0; --cursor) { | 84 | for (; cursor >= 0; --cursor) { |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index a7ac26d71..3b022a456 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -294,6 +294,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 294 | return PixelFormat::Z16; | 294 | return PixelFormat::Z16; |
| 295 | case Tegra::Texture::TextureFormat::Z24S8: | 295 | case Tegra::Texture::TextureFormat::Z24S8: |
| 296 | return PixelFormat::Z24S8; | 296 | return PixelFormat::Z24S8; |
| 297 | case Tegra::Texture::TextureFormat::ZF32_X24S8: | ||
| 298 | return PixelFormat::Z32FS8; | ||
| 297 | case Tegra::Texture::TextureFormat::DXT1: | 299 | case Tegra::Texture::TextureFormat::DXT1: |
| 298 | return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; | 300 | return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; |
| 299 | case Tegra::Texture::TextureFormat::DXT23: | 301 | case Tegra::Texture::TextureFormat::DXT23: |
diff --git a/src/video_core/texture_cache.cpp b/src/video_core/texture_cache.cpp new file mode 100644 index 000000000..e96eba7cc --- /dev/null +++ b/src/video_core/texture_cache.cpp | |||
| @@ -0,0 +1,386 @@ | |||
| 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 "common/alignment.h" | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/cityhash.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "video_core/surface.h" | ||
| 11 | #include "video_core/texture_cache.h" | ||
| 12 | #include "video_core/textures/decoders.h" | ||
| 13 | #include "video_core/textures/texture.h" | ||
| 14 | |||
| 15 | namespace VideoCommon { | ||
| 16 | |||
| 17 | using VideoCore::Surface::SurfaceTarget; | ||
| 18 | |||
| 19 | using VideoCore::Surface::ComponentTypeFromDepthFormat; | ||
| 20 | using VideoCore::Surface::ComponentTypeFromRenderTarget; | ||
| 21 | using VideoCore::Surface::ComponentTypeFromTexture; | ||
| 22 | using VideoCore::Surface::PixelFormatFromDepthFormat; | ||
| 23 | using VideoCore::Surface::PixelFormatFromRenderTargetFormat; | ||
| 24 | using VideoCore::Surface::PixelFormatFromTextureFormat; | ||
| 25 | using VideoCore::Surface::SurfaceTargetFromTextureType; | ||
| 26 | |||
| 27 | constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) { | ||
| 28 | return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile); | ||
| 29 | } | ||
| 30 | |||
| 31 | SurfaceParams SurfaceParams::CreateForTexture(Core::System& system, | ||
| 32 | const Tegra::Texture::FullTextureInfo& config) { | ||
| 33 | SurfaceParams params; | ||
| 34 | params.is_tiled = config.tic.IsTiled(); | ||
| 35 | params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, | ||
| 36 | params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, | ||
| 37 | params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, | ||
| 38 | params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1; | ||
| 39 | params.pixel_format = | ||
| 40 | PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), false); | ||
| 41 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); | ||
| 42 | params.type = GetFormatType(params.pixel_format); | ||
| 43 | params.target = SurfaceTargetFromTextureType(config.tic.texture_type); | ||
| 44 | params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); | ||
| 45 | params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); | ||
| 46 | params.depth = config.tic.Depth(); | ||
| 47 | if (params.target == SurfaceTarget::TextureCubemap || | ||
| 48 | params.target == SurfaceTarget::TextureCubeArray) { | ||
| 49 | params.depth *= 6; | ||
| 50 | } | ||
| 51 | params.pitch = params.is_tiled ? 0 : config.tic.Pitch(); | ||
| 52 | params.unaligned_height = config.tic.Height(); | ||
| 53 | params.num_levels = config.tic.max_mip_level + 1; | ||
| 54 | |||
| 55 | params.CalculateCachedValues(); | ||
| 56 | return params; | ||
| 57 | } | ||
| 58 | |||
| 59 | SurfaceParams SurfaceParams::CreateForDepthBuffer( | ||
| 60 | Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | ||
| 61 | u32 block_width, u32 block_height, u32 block_depth, | ||
| 62 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { | ||
| 63 | SurfaceParams params; | ||
| 64 | params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; | ||
| 65 | params.block_width = 1 << std::min(block_width, 5U); | ||
| 66 | params.block_height = 1 << std::min(block_height, 5U); | ||
| 67 | params.block_depth = 1 << std::min(block_depth, 5U); | ||
| 68 | params.tile_width_spacing = 1; | ||
| 69 | params.pixel_format = PixelFormatFromDepthFormat(format); | ||
| 70 | params.component_type = ComponentTypeFromDepthFormat(format); | ||
| 71 | params.type = GetFormatType(params.pixel_format); | ||
| 72 | params.width = zeta_width; | ||
| 73 | params.height = zeta_height; | ||
| 74 | params.unaligned_height = zeta_height; | ||
| 75 | params.target = SurfaceTarget::Texture2D; | ||
| 76 | params.depth = 1; | ||
| 77 | params.num_levels = 1; | ||
| 78 | |||
| 79 | params.CalculateCachedValues(); | ||
| 80 | return params; | ||
| 81 | } | ||
| 82 | |||
| 83 | SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) { | ||
| 84 | const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; | ||
| 85 | SurfaceParams params; | ||
| 86 | params.is_tiled = | ||
| 87 | config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; | ||
| 88 | params.block_width = 1 << config.memory_layout.block_width; | ||
| 89 | params.block_height = 1 << config.memory_layout.block_height; | ||
| 90 | params.block_depth = 1 << config.memory_layout.block_depth; | ||
| 91 | params.tile_width_spacing = 1; | ||
| 92 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); | ||
| 93 | params.component_type = ComponentTypeFromRenderTarget(config.format); | ||
| 94 | params.type = GetFormatType(params.pixel_format); | ||
| 95 | if (params.is_tiled) { | ||
| 96 | params.width = config.width; | ||
| 97 | } else { | ||
| 98 | const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT; | ||
| 99 | params.pitch = config.width; | ||
| 100 | params.width = params.pitch / bpp; | ||
| 101 | } | ||
| 102 | params.height = config.height; | ||
| 103 | params.depth = 1; | ||
| 104 | params.unaligned_height = config.height; | ||
| 105 | params.target = SurfaceTarget::Texture2D; | ||
| 106 | params.num_levels = 1; | ||
| 107 | |||
| 108 | params.CalculateCachedValues(); | ||
| 109 | return params; | ||
| 110 | } | ||
| 111 | |||
| 112 | SurfaceParams SurfaceParams::CreateForFermiCopySurface( | ||
| 113 | const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||
| 114 | SurfaceParams params{}; | ||
| 115 | params.is_tiled = !config.linear; | ||
| 116 | params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, | ||
| 117 | params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, | ||
| 118 | params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, | ||
| 119 | params.tile_width_spacing = 1; | ||
| 120 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); | ||
| 121 | params.component_type = ComponentTypeFromRenderTarget(config.format); | ||
| 122 | params.type = GetFormatType(params.pixel_format); | ||
| 123 | params.width = config.width; | ||
| 124 | params.height = config.height; | ||
| 125 | params.unaligned_height = config.height; | ||
| 126 | // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters | ||
| 127 | params.target = SurfaceTarget::Texture2D; | ||
| 128 | params.depth = 1; | ||
| 129 | params.num_levels = 1; | ||
| 130 | |||
| 131 | params.CalculateCachedValues(); | ||
| 132 | return params; | ||
| 133 | } | ||
| 134 | |||
| 135 | u32 SurfaceParams::GetMipWidth(u32 level) const { | ||
| 136 | return std::max(1U, width >> level); | ||
| 137 | } | ||
| 138 | |||
| 139 | u32 SurfaceParams::GetMipHeight(u32 level) const { | ||
| 140 | return std::max(1U, height >> level); | ||
| 141 | } | ||
| 142 | |||
| 143 | u32 SurfaceParams::GetMipDepth(u32 level) const { | ||
| 144 | return IsLayered() ? depth : std::max(1U, depth >> level); | ||
| 145 | } | ||
| 146 | |||
| 147 | bool SurfaceParams::IsLayered() const { | ||
| 148 | switch (target) { | ||
| 149 | case SurfaceTarget::Texture1DArray: | ||
| 150 | case SurfaceTarget::Texture2DArray: | ||
| 151 | case SurfaceTarget::TextureCubeArray: | ||
| 152 | case SurfaceTarget::TextureCubemap: | ||
| 153 | return true; | ||
| 154 | default: | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | u32 SurfaceParams::GetMipBlockHeight(u32 level) const { | ||
| 160 | // Auto block resizing algorithm from: | ||
| 161 | // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c | ||
| 162 | if (level == 0) { | ||
| 163 | return block_height; | ||
| 164 | } | ||
| 165 | const u32 height{GetMipHeight(level)}; | ||
| 166 | const u32 default_block_height{GetDefaultBlockHeight(pixel_format)}; | ||
| 167 | const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height}; | ||
| 168 | u32 block_height = 16; | ||
| 169 | while (block_height > 1 && blocks_in_y <= block_height * 4) { | ||
| 170 | block_height >>= 1; | ||
| 171 | } | ||
| 172 | return block_height; | ||
| 173 | } | ||
| 174 | |||
| 175 | u32 SurfaceParams::GetMipBlockDepth(u32 level) const { | ||
| 176 | if (level == 0) | ||
| 177 | return block_depth; | ||
| 178 | if (target != SurfaceTarget::Texture3D) | ||
| 179 | return 1; | ||
| 180 | |||
| 181 | const u32 depth{GetMipDepth(level)}; | ||
| 182 | u32 block_depth = 32; | ||
| 183 | while (block_depth > 1 && depth * 2 <= block_depth) { | ||
| 184 | block_depth >>= 1; | ||
| 185 | } | ||
| 186 | if (block_depth == 32 && GetMipBlockHeight(level) >= 4) { | ||
| 187 | return 16; | ||
| 188 | } | ||
| 189 | return block_depth; | ||
| 190 | } | ||
| 191 | |||
| 192 | std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const { | ||
| 193 | std::size_t offset = 0; | ||
| 194 | for (u32 i = 0; i < level; i++) { | ||
| 195 | offset += GetInnerMipmapMemorySize(i, false, IsLayered(), false); | ||
| 196 | } | ||
| 197 | return offset; | ||
| 198 | } | ||
| 199 | |||
| 200 | std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const { | ||
| 201 | std::size_t offset = 0; | ||
| 202 | for (u32 i = 0; i < level; i++) { | ||
| 203 | offset += GetInnerMipmapMemorySize(i, true, false, false); | ||
| 204 | } | ||
| 205 | return offset; | ||
| 206 | } | ||
| 207 | |||
| 208 | std::size_t SurfaceParams::GetGuestLayerSize() const { | ||
| 209 | return GetInnerMemorySize(false, true, false); | ||
| 210 | } | ||
| 211 | |||
| 212 | std::size_t SurfaceParams::GetHostLayerSize(u32 level) const { | ||
| 213 | return GetInnerMipmapMemorySize(level, true, IsLayered(), false); | ||
| 214 | } | ||
| 215 | |||
| 216 | bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const { | ||
| 217 | if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) != | ||
| 218 | std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format, | ||
| 219 | view_params.component_type, view_params.type)) { | ||
| 220 | return false; | ||
| 221 | } | ||
| 222 | |||
| 223 | const SurfaceTarget view_target{view_params.target}; | ||
| 224 | if (view_target == target) { | ||
| 225 | return true; | ||
| 226 | } | ||
| 227 | |||
| 228 | switch (target) { | ||
| 229 | case SurfaceTarget::Texture1D: | ||
| 230 | case SurfaceTarget::Texture2D: | ||
| 231 | case SurfaceTarget::Texture3D: | ||
| 232 | return false; | ||
| 233 | case SurfaceTarget::Texture1DArray: | ||
| 234 | return view_target == SurfaceTarget::Texture1D; | ||
| 235 | case SurfaceTarget::Texture2DArray: | ||
| 236 | return view_target == SurfaceTarget::Texture2D; | ||
| 237 | case SurfaceTarget::TextureCubemap: | ||
| 238 | return view_target == SurfaceTarget::Texture2D || | ||
| 239 | view_target == SurfaceTarget::Texture2DArray; | ||
| 240 | case SurfaceTarget::TextureCubeArray: | ||
| 241 | return view_target == SurfaceTarget::Texture2D || | ||
| 242 | view_target == SurfaceTarget::Texture2DArray || | ||
| 243 | view_target == SurfaceTarget::TextureCubemap; | ||
| 244 | default: | ||
| 245 | UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target)); | ||
| 246 | return false; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | bool SurfaceParams::IsPixelFormatZeta() const { | ||
| 251 | return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat && | ||
| 252 | pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat; | ||
| 253 | } | ||
| 254 | |||
| 255 | void SurfaceParams::CalculateCachedValues() { | ||
| 256 | guest_size_in_bytes = GetInnerMemorySize(false, false, false); | ||
| 257 | |||
| 258 | // ASTC is uncompressed in software, in emulated as RGBA8 | ||
| 259 | if (IsPixelFormatASTC(pixel_format)) { | ||
| 260 | host_size_in_bytes = width * height * depth * 4; | ||
| 261 | } else { | ||
| 262 | host_size_in_bytes = GetInnerMemorySize(true, false, false); | ||
| 263 | } | ||
| 264 | |||
| 265 | switch (target) { | ||
| 266 | case SurfaceTarget::Texture1D: | ||
| 267 | case SurfaceTarget::Texture2D: | ||
| 268 | case SurfaceTarget::Texture3D: | ||
| 269 | num_layers = 1; | ||
| 270 | break; | ||
| 271 | case SurfaceTarget::Texture1DArray: | ||
| 272 | case SurfaceTarget::Texture2DArray: | ||
| 273 | case SurfaceTarget::TextureCubemap: | ||
| 274 | case SurfaceTarget::TextureCubeArray: | ||
| 275 | num_layers = depth; | ||
| 276 | break; | ||
| 277 | default: | ||
| 278 | UNREACHABLE(); | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only, | ||
| 283 | bool uncompressed) const { | ||
| 284 | const bool tiled{as_host_size ? false : is_tiled}; | ||
| 285 | const u32 tile_x{GetDefaultBlockWidth(pixel_format)}; | ||
| 286 | const u32 tile_y{GetDefaultBlockHeight(pixel_format)}; | ||
| 287 | const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), tile_x)}; | ||
| 288 | const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), tile_y)}; | ||
| 289 | const u32 depth{layer_only ? 1U : GetMipDepth(level)}; | ||
| 290 | return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(pixel_format), width, height, | ||
| 291 | depth, GetMipBlockHeight(level), GetMipBlockDepth(level)); | ||
| 292 | } | ||
| 293 | |||
| 294 | std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only, | ||
| 295 | bool uncompressed) const { | ||
| 296 | std::size_t size = 0; | ||
| 297 | for (u32 level = 0; level < num_levels; ++level) { | ||
| 298 | size += GetInnerMipmapMemorySize(level, as_host_size, layer_only, uncompressed); | ||
| 299 | } | ||
| 300 | if (!as_host_size && is_tiled) { | ||
| 301 | size = Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth); | ||
| 302 | } | ||
| 303 | return size; | ||
| 304 | } | ||
| 305 | |||
| 306 | std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const { | ||
| 307 | std::map<u64, std::pair<u32, u32>> view_offset_map; | ||
| 308 | switch (target) { | ||
| 309 | case SurfaceTarget::Texture1D: | ||
| 310 | case SurfaceTarget::Texture2D: | ||
| 311 | case SurfaceTarget::Texture3D: { | ||
| 312 | constexpr u32 layer = 0; | ||
| 313 | for (u32 level = 0; level < num_levels; ++level) { | ||
| 314 | const std::size_t offset{GetGuestMipmapLevelOffset(level)}; | ||
| 315 | view_offset_map.insert({offset, {layer, level}}); | ||
| 316 | } | ||
| 317 | break; | ||
| 318 | } | ||
| 319 | case SurfaceTarget::Texture1DArray: | ||
| 320 | case SurfaceTarget::Texture2DArray: | ||
| 321 | case SurfaceTarget::TextureCubemap: | ||
| 322 | case SurfaceTarget::TextureCubeArray: { | ||
| 323 | const std::size_t layer_size{GetGuestLayerSize()}; | ||
| 324 | for (u32 level = 0; level < num_levels; ++level) { | ||
| 325 | const std::size_t level_offset{GetGuestMipmapLevelOffset(level)}; | ||
| 326 | for (u32 layer = 0; layer < num_layers; ++layer) { | ||
| 327 | const auto layer_offset{static_cast<std::size_t>(layer_size * layer)}; | ||
| 328 | const std::size_t offset{level_offset + layer_offset}; | ||
| 329 | view_offset_map.insert({offset, {layer, level}}); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | default: | ||
| 335 | UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target)); | ||
| 336 | } | ||
| 337 | return view_offset_map; | ||
| 338 | } | ||
| 339 | |||
| 340 | bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const { | ||
| 341 | return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) && | ||
| 342 | IsInBounds(view_params, layer, level); | ||
| 343 | } | ||
| 344 | |||
| 345 | bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const { | ||
| 346 | return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level); | ||
| 347 | } | ||
| 348 | |||
| 349 | bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const { | ||
| 350 | if (view_params.target != SurfaceTarget::Texture3D) { | ||
| 351 | return true; | ||
| 352 | } | ||
| 353 | return view_params.depth == GetMipDepth(level); | ||
| 354 | } | ||
| 355 | |||
| 356 | bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const { | ||
| 357 | return layer + view_params.num_layers <= num_layers && | ||
| 358 | level + view_params.num_levels <= num_levels; | ||
| 359 | } | ||
| 360 | |||
| 361 | std::size_t HasheableSurfaceParams::Hash() const { | ||
| 362 | return static_cast<std::size_t>( | ||
| 363 | Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); | ||
| 364 | } | ||
| 365 | |||
| 366 | bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const { | ||
| 367 | return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width, | ||
| 368 | height, depth, pitch, unaligned_height, num_levels, pixel_format, | ||
| 369 | component_type, type, target) == | ||
| 370 | std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth, | ||
| 371 | rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch, | ||
| 372 | rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type, | ||
| 373 | rhs.type, rhs.target); | ||
| 374 | } | ||
| 375 | |||
| 376 | std::size_t ViewKey::Hash() const { | ||
| 377 | return static_cast<std::size_t>( | ||
| 378 | Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); | ||
| 379 | } | ||
| 380 | |||
| 381 | bool ViewKey::operator==(const ViewKey& rhs) const { | ||
| 382 | return std::tie(base_layer, num_layers, base_level, num_levels) == | ||
| 383 | std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels); | ||
| 384 | } | ||
| 385 | |||
| 386 | } // namespace VideoCommon | ||
diff --git a/src/video_core/texture_cache.h b/src/video_core/texture_cache.h new file mode 100644 index 000000000..041551691 --- /dev/null +++ b/src/video_core/texture_cache.h | |||
| @@ -0,0 +1,586 @@ | |||
| 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 <list> | ||
| 8 | #include <memory> | ||
| 9 | #include <set> | ||
| 10 | #include <tuple> | ||
| 11 | #include <type_traits> | ||
| 12 | #include <unordered_map> | ||
| 13 | |||
| 14 | #include <boost/icl/interval_map.hpp> | ||
| 15 | #include <boost/range/iterator_range.hpp> | ||
| 16 | |||
| 17 | #include "common/assert.h" | ||
| 18 | #include "common/common_types.h" | ||
| 19 | #include "core/memory.h" | ||
| 20 | #include "video_core/engines/fermi_2d.h" | ||
| 21 | #include "video_core/engines/maxwell_3d.h" | ||
| 22 | #include "video_core/gpu.h" | ||
| 23 | #include "video_core/rasterizer_interface.h" | ||
| 24 | #include "video_core/surface.h" | ||
| 25 | |||
| 26 | namespace Core { | ||
| 27 | class System; | ||
| 28 | } | ||
| 29 | |||
| 30 | namespace Tegra::Texture { | ||
| 31 | struct FullTextureInfo; | ||
| 32 | } | ||
| 33 | |||
| 34 | namespace VideoCore { | ||
| 35 | class RasterizerInterface; | ||
| 36 | } | ||
| 37 | |||
| 38 | namespace VideoCommon { | ||
| 39 | |||
| 40 | class HasheableSurfaceParams { | ||
| 41 | public: | ||
| 42 | std::size_t Hash() const; | ||
| 43 | |||
| 44 | bool operator==(const HasheableSurfaceParams& rhs) const; | ||
| 45 | |||
| 46 | protected: | ||
| 47 | // Avoid creation outside of a managed environment. | ||
| 48 | HasheableSurfaceParams() = default; | ||
| 49 | |||
| 50 | bool is_tiled; | ||
| 51 | u32 block_width; | ||
| 52 | u32 block_height; | ||
| 53 | u32 block_depth; | ||
| 54 | u32 tile_width_spacing; | ||
| 55 | u32 width; | ||
| 56 | u32 height; | ||
| 57 | u32 depth; | ||
| 58 | u32 pitch; | ||
| 59 | u32 unaligned_height; | ||
| 60 | u32 num_levels; | ||
| 61 | VideoCore::Surface::PixelFormat pixel_format; | ||
| 62 | VideoCore::Surface::ComponentType component_type; | ||
| 63 | VideoCore::Surface::SurfaceType type; | ||
| 64 | VideoCore::Surface::SurfaceTarget target; | ||
| 65 | }; | ||
| 66 | |||
| 67 | class SurfaceParams final : public HasheableSurfaceParams { | ||
| 68 | public: | ||
| 69 | /// Creates SurfaceCachedParams from a texture configuration. | ||
| 70 | static SurfaceParams CreateForTexture(Core::System& system, | ||
| 71 | const Tegra::Texture::FullTextureInfo& config); | ||
| 72 | |||
| 73 | /// Creates SurfaceCachedParams for a depth buffer configuration. | ||
| 74 | static SurfaceParams CreateForDepthBuffer( | ||
| 75 | Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | ||
| 76 | u32 block_width, u32 block_height, u32 block_depth, | ||
| 77 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); | ||
| 78 | |||
| 79 | /// Creates SurfaceCachedParams from a framebuffer configuration. | ||
| 80 | static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); | ||
| 81 | |||
| 82 | /// Creates SurfaceCachedParams from a Fermi2D surface configuration. | ||
| 83 | static SurfaceParams CreateForFermiCopySurface( | ||
| 84 | const Tegra::Engines::Fermi2D::Regs::Surface& config); | ||
| 85 | |||
| 86 | bool IsTiled() const { | ||
| 87 | return is_tiled; | ||
| 88 | } | ||
| 89 | |||
| 90 | u32 GetBlockWidth() const { | ||
| 91 | return block_width; | ||
| 92 | } | ||
| 93 | |||
| 94 | u32 GetTileWidthSpacing() const { | ||
| 95 | return tile_width_spacing; | ||
| 96 | } | ||
| 97 | |||
| 98 | u32 GetWidth() const { | ||
| 99 | return width; | ||
| 100 | } | ||
| 101 | |||
| 102 | u32 GetHeight() const { | ||
| 103 | return height; | ||
| 104 | } | ||
| 105 | |||
| 106 | u32 GetDepth() const { | ||
| 107 | return depth; | ||
| 108 | } | ||
| 109 | |||
| 110 | u32 GetPitch() const { | ||
| 111 | return pitch; | ||
| 112 | } | ||
| 113 | |||
| 114 | u32 GetNumLevels() const { | ||
| 115 | return num_levels; | ||
| 116 | } | ||
| 117 | |||
| 118 | VideoCore::Surface::PixelFormat GetPixelFormat() const { | ||
| 119 | return pixel_format; | ||
| 120 | } | ||
| 121 | |||
| 122 | VideoCore::Surface::ComponentType GetComponentType() const { | ||
| 123 | return component_type; | ||
| 124 | } | ||
| 125 | |||
| 126 | VideoCore::Surface::SurfaceTarget GetTarget() const { | ||
| 127 | return target; | ||
| 128 | } | ||
| 129 | |||
| 130 | VideoCore::Surface::SurfaceType GetType() const { | ||
| 131 | return type; | ||
| 132 | } | ||
| 133 | |||
| 134 | std::size_t GetGuestSizeInBytes() const { | ||
| 135 | return guest_size_in_bytes; | ||
| 136 | } | ||
| 137 | |||
| 138 | std::size_t GetHostSizeInBytes() const { | ||
| 139 | return host_size_in_bytes; | ||
| 140 | } | ||
| 141 | |||
| 142 | u32 GetNumLayers() const { | ||
| 143 | return num_layers; | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Returns the width of a given mipmap level. | ||
| 147 | u32 GetMipWidth(u32 level) const; | ||
| 148 | |||
| 149 | /// Returns the height of a given mipmap level. | ||
| 150 | u32 GetMipHeight(u32 level) const; | ||
| 151 | |||
| 152 | /// Returns the depth of a given mipmap level. | ||
| 153 | u32 GetMipDepth(u32 level) const; | ||
| 154 | |||
| 155 | /// Returns true if these parameters are from a layered surface. | ||
| 156 | bool IsLayered() const; | ||
| 157 | |||
| 158 | /// Returns the block height of a given mipmap level. | ||
| 159 | u32 GetMipBlockHeight(u32 level) const; | ||
| 160 | |||
| 161 | /// Returns the block depth of a given mipmap level. | ||
| 162 | u32 GetMipBlockDepth(u32 level) const; | ||
| 163 | |||
| 164 | /// Returns the offset in bytes in guest memory of a given mipmap level. | ||
| 165 | std::size_t GetGuestMipmapLevelOffset(u32 level) const; | ||
| 166 | |||
| 167 | /// Returns the offset in bytes in host memory (linear) of a given mipmap level. | ||
| 168 | std::size_t GetHostMipmapLevelOffset(u32 level) const; | ||
| 169 | |||
| 170 | /// Returns the size of a layer in bytes in guest memory. | ||
| 171 | std::size_t GetGuestLayerSize() const; | ||
| 172 | |||
| 173 | /// Returns the size of a layer in bytes in host memory for a given mipmap level. | ||
| 174 | std::size_t GetHostLayerSize(u32 level) const; | ||
| 175 | |||
| 176 | /// Returns true if another surface can be familiar with this. This is a loosely defined term | ||
| 177 | /// that reflects the possibility of these two surface parameters potentially being part of a | ||
| 178 | /// bigger superset. | ||
| 179 | bool IsFamiliar(const SurfaceParams& view_params) const; | ||
| 180 | |||
| 181 | /// Returns true if the pixel format is a depth and/or stencil format. | ||
| 182 | bool IsPixelFormatZeta() const; | ||
| 183 | |||
| 184 | /// Creates a map that redirects an address difference to a layer and mipmap level. | ||
| 185 | std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const; | ||
| 186 | |||
| 187 | /// Returns true if the passed surface view parameters is equal or a valid subset of this. | ||
| 188 | bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||
| 189 | |||
| 190 | private: | ||
| 191 | /// Calculates values that can be deduced from HasheableSurfaceParams. | ||
| 192 | void CalculateCachedValues(); | ||
| 193 | |||
| 194 | /// Returns the size of a given mipmap level. | ||
| 195 | std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only, | ||
| 196 | bool uncompressed) const; | ||
| 197 | |||
| 198 | /// Returns the size of all mipmap levels and aligns as needed. | ||
| 199 | std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const; | ||
| 200 | |||
| 201 | /// Returns true if the passed view width and height match the size of this params in a given | ||
| 202 | /// mipmap level. | ||
| 203 | bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const; | ||
| 204 | |||
| 205 | /// Returns true if the passed view depth match the size of this params in a given mipmap level. | ||
| 206 | bool IsDepthValid(const SurfaceParams& view_params, u32 level) const; | ||
| 207 | |||
| 208 | /// Returns true if the passed view layers and mipmap levels are in bounds. | ||
| 209 | bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||
| 210 | |||
| 211 | std::size_t guest_size_in_bytes; | ||
| 212 | std::size_t host_size_in_bytes; | ||
| 213 | u32 num_layers; | ||
| 214 | }; | ||
| 215 | |||
| 216 | struct ViewKey { | ||
| 217 | std::size_t Hash() const; | ||
| 218 | |||
| 219 | bool operator==(const ViewKey& rhs) const; | ||
| 220 | |||
| 221 | u32 base_layer{}; | ||
| 222 | u32 num_layers{}; | ||
| 223 | u32 base_level{}; | ||
| 224 | u32 num_levels{}; | ||
| 225 | }; | ||
| 226 | |||
| 227 | } // namespace VideoCommon | ||
| 228 | |||
| 229 | namespace std { | ||
| 230 | |||
| 231 | template <> | ||
| 232 | struct hash<VideoCommon::SurfaceParams> { | ||
| 233 | std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { | ||
| 234 | return k.Hash(); | ||
| 235 | } | ||
| 236 | }; | ||
| 237 | |||
| 238 | template <> | ||
| 239 | struct hash<VideoCommon::ViewKey> { | ||
| 240 | std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept { | ||
| 241 | return k.Hash(); | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | |||
| 245 | } // namespace std | ||
| 246 | |||
| 247 | namespace VideoCommon { | ||
| 248 | |||
| 249 | template <typename TView, typename TExecutionContext> | ||
| 250 | class SurfaceBase { | ||
| 251 | static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||
| 252 | |||
| 253 | public: | ||
| 254 | virtual void LoadBuffer() = 0; | ||
| 255 | |||
| 256 | virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0; | ||
| 257 | |||
| 258 | virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0; | ||
| 259 | |||
| 260 | TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) { | ||
| 261 | if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) { | ||
| 262 | // It can't be a view if it's in a prior address. | ||
| 263 | return {}; | ||
| 264 | } | ||
| 265 | |||
| 266 | const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)}; | ||
| 267 | const auto it{view_offset_map.find(relative_offset)}; | ||
| 268 | if (it == view_offset_map.end()) { | ||
| 269 | // Couldn't find an aligned view. | ||
| 270 | return {}; | ||
| 271 | } | ||
| 272 | const auto [layer, level] = it->second; | ||
| 273 | |||
| 274 | if (!params.IsViewValid(view_params, layer, level)) { | ||
| 275 | return {}; | ||
| 276 | } | ||
| 277 | |||
| 278 | return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels()); | ||
| 279 | } | ||
| 280 | |||
| 281 | VAddr GetCpuAddr() const { | ||
| 282 | ASSERT(is_registered); | ||
| 283 | return cpu_addr; | ||
| 284 | } | ||
| 285 | |||
| 286 | u8* GetHostPtr() const { | ||
| 287 | ASSERT(is_registered); | ||
| 288 | return host_ptr; | ||
| 289 | } | ||
| 290 | |||
| 291 | CacheAddr GetCacheAddr() const { | ||
| 292 | ASSERT(is_registered); | ||
| 293 | return cache_addr; | ||
| 294 | } | ||
| 295 | |||
| 296 | std::size_t GetSizeInBytes() const { | ||
| 297 | return params.GetGuestSizeInBytes(); | ||
| 298 | } | ||
| 299 | |||
| 300 | void MarkAsModified(bool is_modified_) { | ||
| 301 | is_modified = is_modified_; | ||
| 302 | } | ||
| 303 | |||
| 304 | const SurfaceParams& GetSurfaceParams() const { | ||
| 305 | return params; | ||
| 306 | } | ||
| 307 | |||
| 308 | TView* GetView(VAddr view_addr, const SurfaceParams& view_params) { | ||
| 309 | TView* view{TryGetView(view_addr, view_params)}; | ||
| 310 | ASSERT(view != nullptr); | ||
| 311 | return view; | ||
| 312 | } | ||
| 313 | |||
| 314 | void Register(VAddr cpu_addr_, u8* host_ptr_) { | ||
| 315 | ASSERT(!is_registered); | ||
| 316 | is_registered = true; | ||
| 317 | cpu_addr = cpu_addr_; | ||
| 318 | host_ptr = host_ptr_; | ||
| 319 | cache_addr = ToCacheAddr(host_ptr_); | ||
| 320 | } | ||
| 321 | |||
| 322 | void Register(VAddr cpu_addr_) { | ||
| 323 | Register(cpu_addr_, Memory::GetPointer(cpu_addr_)); | ||
| 324 | } | ||
| 325 | |||
| 326 | void Unregister() { | ||
| 327 | ASSERT(is_registered); | ||
| 328 | is_registered = false; | ||
| 329 | } | ||
| 330 | |||
| 331 | bool IsRegistered() const { | ||
| 332 | return is_registered; | ||
| 333 | } | ||
| 334 | |||
| 335 | protected: | ||
| 336 | explicit SurfaceBase(const SurfaceParams& params) | ||
| 337 | : params{params}, view_offset_map{params.CreateViewOffsetMap()} {} | ||
| 338 | |||
| 339 | ~SurfaceBase() = default; | ||
| 340 | |||
| 341 | virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0; | ||
| 342 | |||
| 343 | bool IsModified() const { | ||
| 344 | return is_modified; | ||
| 345 | } | ||
| 346 | |||
| 347 | const SurfaceParams params; | ||
| 348 | |||
| 349 | private: | ||
| 350 | TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) { | ||
| 351 | const ViewKey key{base_layer, num_layers, base_level, num_levels}; | ||
| 352 | const auto [entry, is_cache_miss] = views.try_emplace(key); | ||
| 353 | auto& view{entry->second}; | ||
| 354 | if (is_cache_miss) { | ||
| 355 | view = CreateView(key); | ||
| 356 | } | ||
| 357 | return view.get(); | ||
| 358 | } | ||
| 359 | |||
| 360 | const std::map<u64, std::pair<u32, u32>> view_offset_map; | ||
| 361 | |||
| 362 | VAddr cpu_addr{}; | ||
| 363 | u8* host_ptr{}; | ||
| 364 | CacheAddr cache_addr{}; | ||
| 365 | bool is_modified{}; | ||
| 366 | bool is_registered{}; | ||
| 367 | std::unordered_map<ViewKey, std::unique_ptr<TView>> views; | ||
| 368 | }; | ||
| 369 | |||
| 370 | template <typename TSurface, typename TView, typename TExecutionContext> | ||
| 371 | class TextureCache { | ||
| 372 | static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||
| 373 | using ResultType = std::tuple<TView*, TExecutionContext>; | ||
| 374 | using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>; | ||
| 375 | using IntervalType = typename IntervalMap::interval_type; | ||
| 376 | |||
| 377 | public: | ||
| 378 | void InvalidateRegion(CacheAddr addr, std::size_t size) { | ||
| 379 | for (TSurface* surface : GetSurfacesInRegion(addr, size)) { | ||
| 380 | if (!surface->IsRegistered()) { | ||
| 381 | // Skip duplicates | ||
| 382 | continue; | ||
| 383 | } | ||
| 384 | Unregister(surface); | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | ResultType GetTextureSurface(TExecutionContext exctx, | ||
| 389 | const Tegra::Texture::FullTextureInfo& config) { | ||
| 390 | auto& memory_manager{system.GPU().MemoryManager()}; | ||
| 391 | const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())}; | ||
| 392 | if (!cpu_addr) { | ||
| 393 | return {{}, exctx}; | ||
| 394 | } | ||
| 395 | const auto params{SurfaceParams::CreateForTexture(system, config)}; | ||
| 396 | return GetSurfaceView(exctx, *cpu_addr, params, true); | ||
| 397 | } | ||
| 398 | |||
| 399 | ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) { | ||
| 400 | const auto& regs{system.GPU().Maxwell3D().regs}; | ||
| 401 | if (!regs.zeta.Address() || !regs.zeta_enable) { | ||
| 402 | return {{}, exctx}; | ||
| 403 | } | ||
| 404 | |||
| 405 | auto& memory_manager{system.GPU().MemoryManager()}; | ||
| 406 | const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())}; | ||
| 407 | if (!cpu_addr) { | ||
| 408 | return {{}, exctx}; | ||
| 409 | } | ||
| 410 | |||
| 411 | const auto depth_params{SurfaceParams::CreateForDepthBuffer( | ||
| 412 | system, regs.zeta_width, regs.zeta_height, regs.zeta.format, | ||
| 413 | regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, | ||
| 414 | regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; | ||
| 415 | return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents); | ||
| 416 | } | ||
| 417 | |||
| 418 | ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index, | ||
| 419 | bool preserve_contents) { | ||
| 420 | ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); | ||
| 421 | |||
| 422 | const auto& regs{system.GPU().Maxwell3D().regs}; | ||
| 423 | if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || | ||
| 424 | regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { | ||
| 425 | return {{}, exctx}; | ||
| 426 | } | ||
| 427 | |||
| 428 | auto& memory_manager{system.GPU().MemoryManager()}; | ||
| 429 | const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; | ||
| 430 | const auto cpu_addr{memory_manager.GpuToCpuAddress( | ||
| 431 | config.Address() + config.base_layer * config.layer_stride * sizeof(u32))}; | ||
| 432 | if (!cpu_addr) { | ||
| 433 | return {{}, exctx}; | ||
| 434 | } | ||
| 435 | |||
| 436 | return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index), | ||
| 437 | preserve_contents); | ||
| 438 | } | ||
| 439 | |||
| 440 | ResultType GetFermiSurface(TExecutionContext exctx, | ||
| 441 | const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||
| 442 | const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())}; | ||
| 443 | ASSERT(cpu_addr); | ||
| 444 | return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config), | ||
| 445 | true); | ||
| 446 | } | ||
| 447 | |||
| 448 | TSurface* TryFindFramebufferSurface(const u8* host_ptr) const { | ||
| 449 | const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; | ||
| 450 | return it != registered_surfaces.end() ? *it->second.begin() : nullptr; | ||
| 451 | } | ||
| 452 | |||
| 453 | protected: | ||
| 454 | TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | ||
| 455 | : system{system}, rasterizer{rasterizer} {} | ||
| 456 | |||
| 457 | ~TextureCache() = default; | ||
| 458 | |||
| 459 | virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr, | ||
| 460 | const SurfaceParams& params, bool preserve_contents, | ||
| 461 | const std::vector<TSurface*>& overlaps) = 0; | ||
| 462 | |||
| 463 | virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0; | ||
| 464 | |||
| 465 | void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) { | ||
| 466 | surface->Register(cpu_addr, host_ptr); | ||
| 467 | registered_surfaces.add({GetSurfaceInterval(surface), {surface}}); | ||
| 468 | rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1); | ||
| 469 | } | ||
| 470 | |||
| 471 | void Unregister(TSurface* surface) { | ||
| 472 | registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); | ||
| 473 | rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); | ||
| 474 | surface->Unregister(); | ||
| 475 | } | ||
| 476 | |||
| 477 | TSurface* GetUncachedSurface(const SurfaceParams& params) { | ||
| 478 | if (TSurface* surface = TryGetReservedSurface(params); surface) | ||
| 479 | return surface; | ||
| 480 | // No reserved surface available, create a new one and reserve it | ||
| 481 | auto new_surface{CreateSurface(params)}; | ||
| 482 | TSurface* surface{new_surface.get()}; | ||
| 483 | ReserveSurface(params, std::move(new_surface)); | ||
| 484 | return surface; | ||
| 485 | } | ||
| 486 | |||
| 487 | Core::System& system; | ||
| 488 | |||
| 489 | private: | ||
| 490 | ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params, | ||
| 491 | bool preserve_contents) { | ||
| 492 | const auto host_ptr{Memory::GetPointer(cpu_addr)}; | ||
| 493 | const auto cache_addr{ToCacheAddr(host_ptr)}; | ||
| 494 | const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())}; | ||
| 495 | if (overlaps.empty()) { | ||
| 496 | return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents); | ||
| 497 | } | ||
| 498 | |||
| 499 | if (overlaps.size() == 1) { | ||
| 500 | if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view) | ||
| 501 | return {view, exctx}; | ||
| 502 | } | ||
| 503 | |||
| 504 | TView* fast_view; | ||
| 505 | std::tie(fast_view, exctx) = | ||
| 506 | TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps); | ||
| 507 | |||
| 508 | for (TSurface* surface : overlaps) { | ||
| 509 | if (!fast_view) { | ||
| 510 | // Flush even when we don't care about the contents, to preserve memory not written | ||
| 511 | // by the new surface. | ||
| 512 | exctx = surface->FlushBuffer(exctx); | ||
| 513 | } | ||
| 514 | Unregister(surface); | ||
| 515 | } | ||
| 516 | |||
| 517 | if (fast_view) { | ||
| 518 | return {fast_view, exctx}; | ||
| 519 | } | ||
| 520 | |||
| 521 | return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents); | ||
| 522 | } | ||
| 523 | |||
| 524 | ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr, | ||
| 525 | const SurfaceParams& params, bool preserve_contents) { | ||
| 526 | TSurface* new_surface{GetUncachedSurface(params)}; | ||
| 527 | Register(new_surface, cpu_addr, host_ptr); | ||
| 528 | if (preserve_contents) { | ||
| 529 | exctx = LoadSurface(exctx, new_surface); | ||
| 530 | } | ||
| 531 | return {new_surface->GetView(cpu_addr, params), exctx}; | ||
| 532 | } | ||
| 533 | |||
| 534 | TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) { | ||
| 535 | surface->LoadBuffer(); | ||
| 536 | exctx = surface->UploadTexture(exctx); | ||
| 537 | surface->MarkAsModified(false); | ||
| 538 | return exctx; | ||
| 539 | } | ||
| 540 | |||
| 541 | std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const { | ||
| 542 | if (size == 0) { | ||
| 543 | return {}; | ||
| 544 | } | ||
| 545 | const IntervalType interval{cache_addr, cache_addr + size}; | ||
| 546 | |||
| 547 | std::vector<TSurface*> surfaces; | ||
| 548 | for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { | ||
| 549 | surfaces.push_back(*pair.second.begin()); | ||
| 550 | } | ||
| 551 | return surfaces; | ||
| 552 | } | ||
| 553 | |||
| 554 | void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) { | ||
| 555 | surface_reserve[params].push_back(std::move(surface)); | ||
| 556 | } | ||
| 557 | |||
| 558 | TSurface* TryGetReservedSurface(const SurfaceParams& params) { | ||
| 559 | auto search{surface_reserve.find(params)}; | ||
| 560 | if (search == surface_reserve.end()) { | ||
| 561 | return {}; | ||
| 562 | } | ||
| 563 | for (auto& surface : search->second) { | ||
| 564 | if (!surface->IsRegistered()) { | ||
| 565 | return surface.get(); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | return {}; | ||
| 569 | } | ||
| 570 | |||
| 571 | IntervalType GetSurfaceInterval(TSurface* surface) const { | ||
| 572 | return IntervalType::right_open(surface->GetCacheAddr(), | ||
| 573 | surface->GetCacheAddr() + surface->GetSizeInBytes()); | ||
| 574 | } | ||
| 575 | |||
| 576 | VideoCore::RasterizerInterface& rasterizer; | ||
| 577 | |||
| 578 | IntervalMap registered_surfaces; | ||
| 579 | |||
| 580 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have | ||
| 581 | /// previously been used. This is to prevent surfaces from being constantly created and | ||
| 582 | /// destroyed when used with different surface parameters. | ||
| 583 | std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve; | ||
| 584 | }; | ||
| 585 | |||
| 586 | } // namespace VideoCommon | ||
diff --git a/src/video_core/textures/convert.cpp b/src/video_core/textures/convert.cpp index 5e439f036..82050bd51 100644 --- a/src/video_core/textures/convert.cpp +++ b/src/video_core/textures/convert.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "video_core/surface.h" | ||
| 13 | #include "video_core/textures/astc.h" | 14 | #include "video_core/textures/astc.h" |
| 14 | #include "video_core/textures/convert.h" | 15 | #include "video_core/textures/convert.h" |
| 15 | 16 | ||
diff --git a/src/video_core/textures/convert.h b/src/video_core/textures/convert.h index 07cd8b5da..12542e71c 100644 --- a/src/video_core/textures/convert.h +++ b/src/video_core/textures/convert.h | |||
| @@ -5,7 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "video_core/surface.h" | 8 | |
| 9 | namespace VideoCore::Surface { | ||
| 10 | enum class PixelFormat; | ||
| 11 | } | ||
| 9 | 12 | ||
| 10 | namespace Tegra::Texture { | 13 | namespace Tegra::Texture { |
| 11 | 14 | ||
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 93ecc6e31..bea0d5bc2 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h | |||
| @@ -7,9 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | 11 | ||
| 14 | namespace Tegra::Texture { | 12 | namespace Tegra::Texture { |
| 15 | 13 | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 4cab599b4..31b65c04c 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -31,6 +31,8 @@ add_executable(yuzu | |||
| 31 | configuration/configure_general.h | 31 | configuration/configure_general.h |
| 32 | configuration/configure_graphics.cpp | 32 | configuration/configure_graphics.cpp |
| 33 | configuration/configure_graphics.h | 33 | configuration/configure_graphics.h |
| 34 | configuration/configure_hotkeys.cpp | ||
| 35 | configuration/configure_hotkeys.h | ||
| 34 | configuration/configure_input.cpp | 36 | configuration/configure_input.cpp |
| 35 | configuration/configure_input.h | 37 | configuration/configure_input.h |
| 36 | configuration/configure_input_player.cpp | 38 | configuration/configure_input_player.cpp |
| @@ -54,8 +56,6 @@ add_executable(yuzu | |||
| 54 | debugger/graphics/graphics_breakpoints.cpp | 56 | debugger/graphics/graphics_breakpoints.cpp |
| 55 | debugger/graphics/graphics_breakpoints.h | 57 | debugger/graphics/graphics_breakpoints.h |
| 56 | debugger/graphics/graphics_breakpoints_p.h | 58 | debugger/graphics/graphics_breakpoints_p.h |
| 57 | debugger/graphics/graphics_surface.cpp | ||
| 58 | debugger/graphics/graphics_surface.h | ||
| 59 | debugger/console.cpp | 59 | debugger/console.cpp |
| 60 | debugger/console.h | 60 | debugger/console.h |
| 61 | debugger/profiler.cpp | 61 | debugger/profiler.cpp |
| @@ -78,6 +78,8 @@ add_executable(yuzu | |||
| 78 | ui_settings.h | 78 | ui_settings.h |
| 79 | util/limitable_input_dialog.cpp | 79 | util/limitable_input_dialog.cpp |
| 80 | util/limitable_input_dialog.h | 80 | util/limitable_input_dialog.h |
| 81 | util/sequence_dialog/sequence_dialog.cpp | ||
| 82 | util/sequence_dialog/sequence_dialog.h | ||
| 81 | util/spinbox.cpp | 83 | util/spinbox.cpp |
| 82 | util/spinbox.h | 84 | util/spinbox.h |
| 83 | util/util.cpp | 85 | util/util.cpp |
| @@ -95,6 +97,7 @@ set(UIS | |||
| 95 | configuration/configure_gamelist.ui | 97 | configuration/configure_gamelist.ui |
| 96 | configuration/configure_general.ui | 98 | configuration/configure_general.ui |
| 97 | configuration/configure_graphics.ui | 99 | configuration/configure_graphics.ui |
| 100 | configuration/configure_hotkeys.ui | ||
| 98 | configuration/configure_input.ui | 101 | configuration/configure_input.ui |
| 99 | configuration/configure_input_player.ui | 102 | configuration/configure_input_player.ui |
| 100 | configuration/configure_input_simple.ui | 103 | configuration/configure_input_simple.ui |
| @@ -105,7 +108,6 @@ set(UIS | |||
| 105 | configuration/configure_touchscreen_advanced.ui | 108 | configuration/configure_touchscreen_advanced.ui |
| 106 | configuration/configure_web.ui | 109 | configuration/configure_web.ui |
| 107 | compatdb.ui | 110 | compatdb.ui |
| 108 | hotkeys.ui | ||
| 109 | loading_screen.ui | 111 | loading_screen.ui |
| 110 | main.ui | 112 | main.ui |
| 111 | ) | 113 | ) |
| @@ -149,6 +151,12 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core) | |||
| 149 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) | 151 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) |
| 150 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | 152 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) |
| 151 | 153 | ||
| 154 | target_compile_definitions(yuzu PRIVATE | ||
| 155 | # Use QStringBuilder for string concatenation to reduce | ||
| 156 | # the overall number of temporary strings created. | ||
| 157 | -DQT_USE_QSTRINGBUILDER | ||
| 158 | ) | ||
| 159 | |||
| 152 | if (YUZU_ENABLE_COMPATIBILITY_REPORTING) | 160 | if (YUZU_ENABLE_COMPATIBILITY_REPORTING) |
| 153 | target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING) | 161 | target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING) |
| 154 | endif() | 162 | endif() |
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index f95f7fe3c..743b24d76 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <mutex> | 5 | #include <mutex> |
| 6 | #include <QDialogButtonBox> | 6 | #include <QDialogButtonBox> |
| 7 | #include <QHeaderView> | ||
| 7 | #include <QLabel> | 8 | #include <QLabel> |
| 8 | #include <QLineEdit> | 9 | #include <QLineEdit> |
| 9 | #include <QScrollArea> | 10 | #include <QScrollArea> |
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h index 868573324..1c2922e54 100644 --- a/src/yuzu/applets/profile_select.h +++ b/src/yuzu/applets/profile_select.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | #include <QDialog> | 8 | #include <QDialog> |
| 9 | #include <QList> | 9 | #include <QList> |
| 10 | #include <QTreeView> | ||
| 10 | #include "core/frontend/applets/profile_select.h" | 11 | #include "core/frontend/applets/profile_select.h" |
| 11 | 12 | ||
| 12 | class GMainWindow; | 13 | class GMainWindow; |
| @@ -16,7 +17,6 @@ class QLabel; | |||
| 16 | class QScrollArea; | 17 | class QScrollArea; |
| 17 | class QStandardItem; | 18 | class QStandardItem; |
| 18 | class QStandardItemModel; | 19 | class QStandardItemModel; |
| 19 | class QTreeView; | ||
| 20 | class QVBoxLayout; | 20 | class QVBoxLayout; |
| 21 | 21 | ||
| 22 | class QtProfileSelectionDialog final : public QDialog { | 22 | class QtProfileSelectionDialog final : public QDialog { |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7438fbc0a..c29f2d2dc 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -1,6 +1,13 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #include <QApplication> | 5 | #include <QApplication> |
| 2 | #include <QHBoxLayout> | 6 | #include <QHBoxLayout> |
| 3 | #include <QKeyEvent> | 7 | #include <QKeyEvent> |
| 8 | #include <QOffscreenSurface> | ||
| 9 | #include <QOpenGLWindow> | ||
| 10 | #include <QPainter> | ||
| 4 | #include <QScreen> | 11 | #include <QScreen> |
| 5 | #include <QWindow> | 12 | #include <QWindow> |
| 6 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| @@ -82,13 +89,36 @@ void EmuThread::run() { | |||
| 82 | render_window->moveContext(); | 89 | render_window->moveContext(); |
| 83 | } | 90 | } |
| 84 | 91 | ||
| 92 | class GGLContext : public Core::Frontend::GraphicsContext { | ||
| 93 | public: | ||
| 94 | explicit GGLContext(QOpenGLContext* shared_context) : surface() { | ||
| 95 | context = std::make_unique<QOpenGLContext>(shared_context); | ||
| 96 | surface.setFormat(shared_context->format()); | ||
| 97 | surface.create(); | ||
| 98 | } | ||
| 99 | |||
| 100 | void MakeCurrent() override { | ||
| 101 | context->makeCurrent(&surface); | ||
| 102 | } | ||
| 103 | |||
| 104 | void DoneCurrent() override { | ||
| 105 | context->doneCurrent(); | ||
| 106 | } | ||
| 107 | |||
| 108 | void SwapBuffers() override {} | ||
| 109 | |||
| 110 | private: | ||
| 111 | std::unique_ptr<QOpenGLContext> context; | ||
| 112 | QOffscreenSurface surface; | ||
| 113 | }; | ||
| 114 | |||
| 85 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | 115 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL |
| 86 | // context. | 116 | // context. |
| 87 | // The corresponding functionality is handled in EmuThread instead | 117 | // The corresponding functionality is handled in EmuThread instead |
| 88 | class GGLWidgetInternal : public QGLWidget { | 118 | class GGLWidgetInternal : public QOpenGLWindow { |
| 89 | public: | 119 | public: |
| 90 | GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) | 120 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) |
| 91 | : QGLWidget(fmt, parent), parent(parent) {} | 121 | : QOpenGLWindow(shared_context), parent(parent) {} |
| 92 | 122 | ||
| 93 | void paintEvent(QPaintEvent* ev) override { | 123 | void paintEvent(QPaintEvent* ev) override { |
| 94 | if (do_painting) { | 124 | if (do_painting) { |
| @@ -101,9 +131,51 @@ public: | |||
| 101 | parent->OnFramebufferSizeChanged(); | 131 | parent->OnFramebufferSizeChanged(); |
| 102 | } | 132 | } |
| 103 | 133 | ||
| 134 | void keyPressEvent(QKeyEvent* event) override { | ||
| 135 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 136 | } | ||
| 137 | |||
| 138 | void keyReleaseEvent(QKeyEvent* event) override { | ||
| 139 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 140 | } | ||
| 141 | |||
| 142 | void mousePressEvent(QMouseEvent* event) override { | ||
| 143 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 144 | return; // touch input is handled in TouchBeginEvent | ||
| 145 | |||
| 146 | const auto pos{event->pos()}; | ||
| 147 | if (event->button() == Qt::LeftButton) { | ||
| 148 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 149 | parent->TouchPressed(x, y); | ||
| 150 | } else if (event->button() == Qt::RightButton) { | ||
| 151 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | void mouseMoveEvent(QMouseEvent* event) override { | ||
| 156 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 157 | return; // touch input is handled in TouchUpdateEvent | ||
| 158 | |||
| 159 | const auto pos{event->pos()}; | ||
| 160 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 161 | parent->TouchMoved(x, y); | ||
| 162 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 163 | } | ||
| 164 | |||
| 165 | void mouseReleaseEvent(QMouseEvent* event) override { | ||
| 166 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 167 | return; // touch input is handled in TouchEndEvent | ||
| 168 | |||
| 169 | if (event->button() == Qt::LeftButton) | ||
| 170 | parent->TouchReleased(); | ||
| 171 | else if (event->button() == Qt::RightButton) | ||
| 172 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 173 | } | ||
| 174 | |||
| 104 | void DisablePainting() { | 175 | void DisablePainting() { |
| 105 | do_painting = false; | 176 | do_painting = false; |
| 106 | } | 177 | } |
| 178 | |||
| 107 | void EnablePainting() { | 179 | void EnablePainting() { |
| 108 | do_painting = true; | 180 | do_painting = true; |
| 109 | } | 181 | } |
| @@ -114,7 +186,7 @@ private: | |||
| 114 | }; | 186 | }; |
| 115 | 187 | ||
| 116 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 188 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 117 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { | 189 | : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) { |
| 118 | 190 | ||
| 119 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 191 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 120 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | 192 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); |
| @@ -137,19 +209,19 @@ void GRenderWindow::moveContext() { | |||
| 137 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) | 209 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) |
| 138 | ? emu_thread | 210 | ? emu_thread |
| 139 | : qApp->thread(); | 211 | : qApp->thread(); |
| 140 | child->context()->moveToThread(thread); | 212 | context->moveToThread(thread); |
| 141 | } | 213 | } |
| 142 | 214 | ||
| 143 | void GRenderWindow::SwapBuffers() { | 215 | void GRenderWindow::SwapBuffers() { |
| 144 | // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, | 216 | // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`, |
| 145 | // since we never call `doneCurrent` in this thread. | 217 | // since we never call `doneCurrent` in this thread. |
| 146 | // However: | 218 | // However: |
| 147 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called | 219 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called |
| 148 | // since the last time `swapBuffers` was executed; | 220 | // since the last time `swapBuffers` was executed; |
| 149 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. | 221 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. |
| 150 | child->makeCurrent(); | 222 | context->makeCurrent(child); |
| 151 | 223 | ||
| 152 | child->swapBuffers(); | 224 | context->swapBuffers(child); |
| 153 | if (!first_frame) { | 225 | if (!first_frame) { |
| 154 | emit FirstFrameDisplayed(); | 226 | emit FirstFrameDisplayed(); |
| 155 | first_frame = true; | 227 | first_frame = true; |
| @@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() { | |||
| 157 | } | 229 | } |
| 158 | 230 | ||
| 159 | void GRenderWindow::MakeCurrent() { | 231 | void GRenderWindow::MakeCurrent() { |
| 160 | child->makeCurrent(); | 232 | context->makeCurrent(child); |
| 161 | } | 233 | } |
| 162 | 234 | ||
| 163 | void GRenderWindow::DoneCurrent() { | 235 | void GRenderWindow::DoneCurrent() { |
| 164 | child->doneCurrent(); | 236 | context->doneCurrent(); |
| 165 | } | 237 | } |
| 166 | 238 | ||
| 167 | void GRenderWindow::PollEvents() {} | 239 | void GRenderWindow::PollEvents() {} |
| @@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {} | |||
| 174 | void GRenderWindow::OnFramebufferSizeChanged() { | 246 | void GRenderWindow::OnFramebufferSizeChanged() { |
| 175 | // Screen changes potentially incur a change in screen DPI, hence we should update the | 247 | // Screen changes potentially incur a change in screen DPI, hence we should update the |
| 176 | // framebuffer size | 248 | // framebuffer size |
| 177 | qreal pixelRatio = windowPixelRatio(); | 249 | qreal pixelRatio = GetWindowPixelRatio(); |
| 178 | unsigned width = child->QPaintDevice::width() * pixelRatio; | 250 | unsigned width = child->QPaintDevice::width() * pixelRatio; |
| 179 | unsigned height = child->QPaintDevice::height() * pixelRatio; | 251 | unsigned height = child->QPaintDevice::height() * pixelRatio; |
| 180 | UpdateCurrentFramebufferLayout(width, height); | 252 | UpdateCurrentFramebufferLayout(width, height); |
| 181 | } | 253 | } |
| 182 | 254 | ||
| 255 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | ||
| 256 | if (child) { | ||
| 257 | child->keyPressEvent(event); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { | ||
| 262 | if (child) { | ||
| 263 | child->keyReleaseEvent(event); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 183 | void GRenderWindow::BackupGeometry() { | 267 | void GRenderWindow::BackupGeometry() { |
| 184 | geometry = ((QGLWidget*)this)->saveGeometry(); | 268 | geometry = ((QWidget*)this)->saveGeometry(); |
| 185 | } | 269 | } |
| 186 | 270 | ||
| 187 | void GRenderWindow::RestoreGeometry() { | 271 | void GRenderWindow::RestoreGeometry() { |
| @@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 199 | // If we are a top-level widget, store the current geometry | 283 | // If we are a top-level widget, store the current geometry |
| 200 | // otherwise, store the last backup | 284 | // otherwise, store the last backup |
| 201 | if (parent() == nullptr) | 285 | if (parent() == nullptr) |
| 202 | return ((QGLWidget*)this)->saveGeometry(); | 286 | return ((QWidget*)this)->saveGeometry(); |
| 203 | else | 287 | else |
| 204 | return geometry; | 288 | return geometry; |
| 205 | } | 289 | } |
| 206 | 290 | ||
| 207 | qreal GRenderWindow::windowPixelRatio() const { | 291 | qreal GRenderWindow::GetWindowPixelRatio() const { |
| 208 | // windowHandle() might not be accessible until the window is displayed to screen. | 292 | // windowHandle() might not be accessible until the window is displayed to screen. |
| 209 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | 293 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; |
| 210 | } | 294 | } |
| 211 | 295 | ||
| 212 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { | 296 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { |
| 213 | const qreal pixel_ratio = windowPixelRatio(); | 297 | const qreal pixel_ratio = GetWindowPixelRatio(); |
| 214 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 298 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 215 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 299 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| 216 | } | 300 | } |
| @@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 220 | QWidget::closeEvent(event); | 304 | QWidget::closeEvent(event); |
| 221 | } | 305 | } |
| 222 | 306 | ||
| 223 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||
| 224 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 225 | } | ||
| 226 | |||
| 227 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 228 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 229 | } | ||
| 230 | |||
| 231 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||
| 232 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 233 | return; // touch input is handled in TouchBeginEvent | ||
| 234 | |||
| 235 | auto pos = event->pos(); | ||
| 236 | if (event->button() == Qt::LeftButton) { | ||
| 237 | const auto [x, y] = ScaleTouch(pos); | ||
| 238 | this->TouchPressed(x, y); | ||
| 239 | } else if (event->button() == Qt::RightButton) { | ||
| 240 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||
| 245 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 246 | return; // touch input is handled in TouchUpdateEvent | ||
| 247 | |||
| 248 | auto pos = event->pos(); | ||
| 249 | const auto [x, y] = ScaleTouch(pos); | ||
| 250 | this->TouchMoved(x, y); | ||
| 251 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 252 | } | ||
| 253 | |||
| 254 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||
| 255 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 256 | return; // touch input is handled in TouchEndEvent | ||
| 257 | |||
| 258 | if (event->button() == Qt::LeftButton) | ||
| 259 | this->TouchReleased(); | ||
| 260 | else if (event->button() == Qt::RightButton) | ||
| 261 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 262 | } | ||
| 263 | |||
| 264 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 307 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| 265 | // TouchBegin always has exactly one touch point, so take the .first() | 308 | // TouchBegin always has exactly one touch point, so take the .first() |
| 266 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | 309 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); |
| @@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { | |||
| 313 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | 356 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); |
| 314 | } | 357 | } |
| 315 | 358 | ||
| 359 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | ||
| 360 | return std::make_unique<GGLContext>(shared_context.get()); | ||
| 361 | } | ||
| 362 | |||
| 316 | void GRenderWindow::InitRenderTarget() { | 363 | void GRenderWindow::InitRenderTarget() { |
| 317 | if (child) { | 364 | shared_context.reset(); |
| 318 | delete child; | 365 | context.reset(); |
| 319 | } | ||
| 320 | 366 | ||
| 321 | if (layout()) { | 367 | delete child; |
| 322 | delete layout(); | 368 | child = nullptr; |
| 323 | } | 369 | |
| 370 | delete container; | ||
| 371 | container = nullptr; | ||
| 372 | |||
| 373 | delete layout(); | ||
| 324 | 374 | ||
| 325 | first_frame = false; | 375 | first_frame = false; |
| 326 | 376 | ||
| 327 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 377 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 328 | // WA_DontShowOnScreen, WA_DeleteOnClose | 378 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 329 | QGLFormat fmt; | 379 | QSurfaceFormat fmt; |
| 330 | fmt.setVersion(4, 3); | 380 | fmt.setVersion(4, 3); |
| 331 | fmt.setProfile(QGLFormat::CoreProfile); | 381 | fmt.setProfile(QSurfaceFormat::CoreProfile); |
| 382 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 383 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 384 | shared_context = std::make_unique<QOpenGLContext>(); | ||
| 385 | shared_context->setFormat(fmt); | ||
| 386 | shared_context->create(); | ||
| 387 | context = std::make_unique<QOpenGLContext>(); | ||
| 388 | context->setShareContext(shared_context.get()); | ||
| 389 | context->setFormat(fmt); | ||
| 390 | context->create(); | ||
| 332 | fmt.setSwapInterval(false); | 391 | fmt.setSwapInterval(false); |
| 333 | 392 | ||
| 334 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X | 393 | child = new GGLWidgetInternal(this, shared_context.get()); |
| 335 | fmt.setOption(QGL::NoDeprecatedFunctions); | 394 | container = QWidget::createWindowContainer(child, this); |
| 336 | 395 | ||
| 337 | child = new GGLWidgetInternal(fmt, this); | ||
| 338 | QBoxLayout* layout = new QHBoxLayout(this); | 396 | QBoxLayout* layout = new QHBoxLayout(this); |
| 339 | 397 | layout->addWidget(container); | |
| 340 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 341 | layout->addWidget(child); | ||
| 342 | layout->setMargin(0); | 398 | layout->setMargin(0); |
| 343 | setLayout(layout); | 399 | setLayout(layout); |
| 344 | 400 | ||
| 401 | // Reset minimum size to avoid unwanted resizes when this function is called for a second time. | ||
| 402 | setMinimumSize(1, 1); | ||
| 403 | |||
| 404 | // Show causes the window to actually be created and the OpenGL context as well, but we don't | ||
| 405 | // want the widget to be shown yet, so immediately hide it. | ||
| 406 | show(); | ||
| 407 | hide(); | ||
| 408 | |||
| 409 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 410 | child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 411 | container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 412 | |||
| 345 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 413 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 346 | 414 | ||
| 347 | OnFramebufferSizeChanged(); | 415 | OnFramebufferSizeChanged(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3183621bc..9608b959f 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <mutex> | 9 | #include <mutex> |
| 10 | #include <QGLWidget> | ||
| 11 | #include <QImage> | 10 | #include <QImage> |
| 12 | #include <QThread> | 11 | #include <QThread> |
| 12 | #include <QWidget> | ||
| 13 | #include "common/thread.h" | 13 | #include "common/thread.h" |
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/frontend/emu_window.h" | 15 | #include "core/frontend/emu_window.h" |
| @@ -21,6 +21,8 @@ class QTouchEvent; | |||
| 21 | class GGLWidgetInternal; | 21 | class GGLWidgetInternal; |
| 22 | class GMainWindow; | 22 | class GMainWindow; |
| 23 | class GRenderWindow; | 23 | class GRenderWindow; |
| 24 | class QSurface; | ||
| 25 | class QOpenGLContext; | ||
| 24 | 26 | ||
| 25 | namespace VideoCore { | 27 | namespace VideoCore { |
| 26 | enum class LoadCallbackStage; | 28 | enum class LoadCallbackStage; |
| @@ -121,25 +123,21 @@ public: | |||
| 121 | void MakeCurrent() override; | 123 | void MakeCurrent() override; |
| 122 | void DoneCurrent() override; | 124 | void DoneCurrent() override; |
| 123 | void PollEvents() override; | 125 | void PollEvents() override; |
| 126 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 127 | |||
| 128 | void ForwardKeyPressEvent(QKeyEvent* event); | ||
| 129 | void ForwardKeyReleaseEvent(QKeyEvent* event); | ||
| 124 | 130 | ||
| 125 | void BackupGeometry(); | 131 | void BackupGeometry(); |
| 126 | void RestoreGeometry(); | 132 | void RestoreGeometry(); |
| 127 | void restoreGeometry(const QByteArray& geometry); // overridden | 133 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 128 | QByteArray saveGeometry(); // overridden | 134 | QByteArray saveGeometry(); // overridden |
| 129 | 135 | ||
| 130 | qreal windowPixelRatio() const; | 136 | qreal GetWindowPixelRatio() const; |
| 137 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 131 | 138 | ||
| 132 | void closeEvent(QCloseEvent* event) override; | 139 | void closeEvent(QCloseEvent* event) override; |
| 133 | |||
| 134 | void keyPressEvent(QKeyEvent* event) override; | ||
| 135 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 136 | |||
| 137 | void mousePressEvent(QMouseEvent* event) override; | ||
| 138 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 139 | void mouseReleaseEvent(QMouseEvent* event) override; | ||
| 140 | |||
| 141 | bool event(QEvent* event) override; | 140 | bool event(QEvent* event) override; |
| 142 | |||
| 143 | void focusOutEvent(QFocusEvent* event) override; | 141 | void focusOutEvent(QFocusEvent* event) override; |
| 144 | 142 | ||
| 145 | void OnClientAreaResized(unsigned width, unsigned height); | 143 | void OnClientAreaResized(unsigned width, unsigned height); |
| @@ -161,7 +159,6 @@ signals: | |||
| 161 | void FirstFrameDisplayed(); | 159 | void FirstFrameDisplayed(); |
| 162 | 160 | ||
| 163 | private: | 161 | private: |
| 164 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 165 | void TouchBeginEvent(const QTouchEvent* event); | 162 | void TouchBeginEvent(const QTouchEvent* event); |
| 166 | void TouchUpdateEvent(const QTouchEvent* event); | 163 | void TouchUpdateEvent(const QTouchEvent* event); |
| 167 | void TouchEndEvent(); | 164 | void TouchEndEvent(); |
| @@ -169,11 +166,17 @@ private: | |||
| 169 | void OnMinimalClientAreaChangeRequest( | 166 | void OnMinimalClientAreaChangeRequest( |
| 170 | const std::pair<unsigned, unsigned>& minimal_size) override; | 167 | const std::pair<unsigned, unsigned>& minimal_size) override; |
| 171 | 168 | ||
| 172 | GGLWidgetInternal* child; | 169 | QWidget* container = nullptr; |
| 170 | GGLWidgetInternal* child = nullptr; | ||
| 173 | 171 | ||
| 174 | QByteArray geometry; | 172 | QByteArray geometry; |
| 175 | 173 | ||
| 176 | EmuThread* emu_thread; | 174 | EmuThread* emu_thread; |
| 175 | // Context that backs the GGLWidgetInternal (and will be used by core to render) | ||
| 176 | std::unique_ptr<QOpenGLContext> context; | ||
| 177 | // Context that will be shared between all newly created contexts. This should never be made | ||
| 178 | // current | ||
| 179 | std::unique_ptr<QOpenGLContext> shared_context; | ||
| 177 | 180 | ||
| 178 | /// Temporary storage of the screenshot taken | 181 | /// Temporary storage of the screenshot taken |
| 179 | QImage screenshot_image; | 182 | QImage screenshot_image; |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index dead9f807..8725a78dc 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 6 | #include <QKeySequence> | ||
| 5 | #include <QSettings> | 7 | #include <QSettings> |
| 6 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 7 | #include "configure_input_simple.h" | 9 | #include "configure_input_simple.h" |
| @@ -9,7 +11,6 @@ | |||
| 9 | #include "core/hle/service/hid/controllers/npad.h" | 11 | #include "core/hle/service/hid/controllers/npad.h" |
| 10 | #include "input_common/main.h" | 12 | #include "input_common/main.h" |
| 11 | #include "yuzu/configuration/config.h" | 13 | #include "yuzu/configuration/config.h" |
| 12 | #include "yuzu/ui_settings.h" | ||
| 13 | 14 | ||
| 14 | Config::Config() { | 15 | Config::Config() { |
| 15 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 16 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| @@ -17,7 +18,6 @@ Config::Config() { | |||
| 17 | FileUtil::CreateFullPath(qt_config_loc); | 18 | FileUtil::CreateFullPath(qt_config_loc); |
| 18 | qt_config = | 19 | qt_config = |
| 19 | std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | 20 | std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); |
| 20 | |||
| 21 | Reload(); | 21 | Reload(); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| @@ -205,6 +205,27 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default | |||
| 205 | Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight, | 205 | Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight, |
| 206 | }; | 206 | }; |
| 207 | 207 | ||
| 208 | // This shouldn't have anything except static initializers (no functions). So | ||
| 209 | // QKeySequnce(...).toString() is NOT ALLOWED HERE. | ||
| 210 | // This must be in alphabetical order according to action name as it must have the same order as | ||
| 211 | // UISetting::values.shortcuts, which is alphabetically ordered. | ||
| 212 | const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{ | ||
| 213 | {{"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}}, | ||
| 214 | {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}}, | ||
| 215 | {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}}, | ||
| 216 | {"Exit yuzu", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}}, | ||
| 217 | {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}}, | ||
| 218 | {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}}, | ||
| 219 | {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}}, | ||
| 220 | {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}}, | ||
| 221 | {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}}, | ||
| 222 | {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}}, | ||
| 223 | {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}}, | ||
| 224 | {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}}, | ||
| 225 | {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}}, | ||
| 226 | {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}}, | ||
| 227 | {"Change Docked Mode", "Main Window", {"F10", Qt::ApplicationShortcut}}}}; | ||
| 228 | |||
| 208 | void Config::ReadPlayerValues() { | 229 | void Config::ReadPlayerValues() { |
| 209 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { | 230 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { |
| 210 | auto& player = Settings::values.players[p]; | 231 | auto& player = Settings::values.players[p]; |
| @@ -373,6 +394,7 @@ void Config::ReadValues() { | |||
| 373 | ReadSetting("use_accurate_gpu_emulation", false).toBool(); | 394 | ReadSetting("use_accurate_gpu_emulation", false).toBool(); |
| 374 | Settings::values.use_asynchronous_gpu_emulation = | 395 | Settings::values.use_asynchronous_gpu_emulation = |
| 375 | ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); | 396 | ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); |
| 397 | Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool(); | ||
| 376 | 398 | ||
| 377 | Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); | 399 | Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); |
| 378 | Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); | 400 | Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); |
| @@ -502,26 +524,21 @@ void Config::ReadValues() { | |||
| 502 | qt_config->beginGroup("Paths"); | 524 | qt_config->beginGroup("Paths"); |
| 503 | UISettings::values.roms_path = ReadSetting("romsPath").toString(); | 525 | UISettings::values.roms_path = ReadSetting("romsPath").toString(); |
| 504 | UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); | 526 | UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); |
| 505 | UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString(); | 527 | UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString(); |
| 506 | UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool(); | 528 | UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool(); |
| 507 | UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); | 529 | UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); |
| 508 | qt_config->endGroup(); | 530 | qt_config->endGroup(); |
| 509 | 531 | ||
| 510 | qt_config->beginGroup("Shortcuts"); | 532 | qt_config->beginGroup("Shortcuts"); |
| 511 | QStringList groups = qt_config->childGroups(); | 533 | for (auto [name, group, shortcut] : default_hotkeys) { |
| 512 | for (auto group : groups) { | 534 | auto [keyseq, context] = shortcut; |
| 513 | qt_config->beginGroup(group); | 535 | qt_config->beginGroup(group); |
| 514 | 536 | qt_config->beginGroup(name); | |
| 515 | QStringList hotkeys = qt_config->childGroups(); | 537 | UISettings::values.shortcuts.push_back( |
| 516 | for (auto hotkey : hotkeys) { | 538 | {name, |
| 517 | qt_config->beginGroup(hotkey); | 539 | group, |
| 518 | UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( | 540 | {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}}); |
| 519 | group + "/" + hotkey, | 541 | qt_config->endGroup(); |
| 520 | UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(), | ||
| 521 | ReadSetting("Context").toInt()))); | ||
| 522 | qt_config->endGroup(); | ||
| 523 | } | ||
| 524 | |||
| 525 | qt_config->endGroup(); | 542 | qt_config->endGroup(); |
| 526 | } | 543 | } |
| 527 | qt_config->endGroup(); | 544 | qt_config->endGroup(); |
| @@ -648,6 +665,7 @@ void Config::SaveValues() { | |||
| 648 | WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); | 665 | WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); |
| 649 | WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, | 666 | WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, |
| 650 | false); | 667 | false); |
| 668 | WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false); | ||
| 651 | 669 | ||
| 652 | // Cast to double because Qt's written float values are not human-readable | 670 | // Cast to double because Qt's written float values are not human-readable |
| 653 | WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); | 671 | WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); |
| @@ -752,15 +770,22 @@ void Config::SaveValues() { | |||
| 752 | WriteSetting("romsPath", UISettings::values.roms_path); | 770 | WriteSetting("romsPath", UISettings::values.roms_path); |
| 753 | WriteSetting("symbolsPath", UISettings::values.symbols_path); | 771 | WriteSetting("symbolsPath", UISettings::values.symbols_path); |
| 754 | WriteSetting("screenshotPath", UISettings::values.screenshot_path); | 772 | WriteSetting("screenshotPath", UISettings::values.screenshot_path); |
| 755 | WriteSetting("gameListRootDir", UISettings::values.gamedir, "."); | 773 | WriteSetting("gameListRootDir", UISettings::values.game_directory_path, "."); |
| 756 | WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false); | 774 | WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false); |
| 757 | WriteSetting("recentFiles", UISettings::values.recent_files); | 775 | WriteSetting("recentFiles", UISettings::values.recent_files); |
| 758 | qt_config->endGroup(); | 776 | qt_config->endGroup(); |
| 759 | 777 | ||
| 760 | qt_config->beginGroup("Shortcuts"); | 778 | qt_config->beginGroup("Shortcuts"); |
| 761 | for (auto shortcut : UISettings::values.shortcuts) { | 779 | // Lengths of UISettings::values.shortcuts & default_hotkeys are same. |
| 762 | WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first); | 780 | // However, their ordering must also be the same. |
| 763 | WriteSetting(shortcut.first + "/Context", shortcut.second.second); | 781 | for (std::size_t i = 0; i < default_hotkeys.size(); i++) { |
| 782 | auto [name, group, shortcut] = UISettings::values.shortcuts[i]; | ||
| 783 | qt_config->beginGroup(group); | ||
| 784 | qt_config->beginGroup(name); | ||
| 785 | WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first); | ||
| 786 | WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second); | ||
| 787 | qt_config->endGroup(); | ||
| 788 | qt_config->endGroup(); | ||
| 764 | } | 789 | } |
| 765 | qt_config->endGroup(); | 790 | qt_config->endGroup(); |
| 766 | 791 | ||
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index f4185db18..221d2364c 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <QVariant> | 10 | #include <QVariant> |
| 11 | #include "core/settings.h" | 11 | #include "core/settings.h" |
| 12 | #include "yuzu/ui_settings.h" | ||
| 12 | 13 | ||
| 13 | class QSettings; | 14 | class QSettings; |
| 14 | 15 | ||
| @@ -47,6 +48,8 @@ private: | |||
| 47 | void WriteSetting(const QString& name, const QVariant& value); | 48 | void WriteSetting(const QString& name, const QVariant& value); |
| 48 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); | 49 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); |
| 49 | 50 | ||
| 51 | static const std::array<UISettings::Shortcut, 15> default_hotkeys; | ||
| 52 | |||
| 50 | std::unique_ptr<QSettings> qt_config; | 53 | std::unique_ptr<QSettings> qt_config; |
| 51 | std::string qt_config_loc; | 54 | std::string qt_config_loc; |
| 52 | }; | 55 | }; |
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 3f03f0b77..267717bc9 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui | |||
| @@ -7,9 +7,15 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>382</width> | 9 | <width>382</width> |
| 10 | <height>241</height> | 10 | <height>650</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="minimumSize"> | ||
| 14 | <size> | ||
| 15 | <width>0</width> | ||
| 16 | <height>650</height> | ||
| 17 | </size> | ||
| 18 | </property> | ||
| 13 | <property name="windowTitle"> | 19 | <property name="windowTitle"> |
| 14 | <string>yuzu Configuration</string> | 20 | <string>yuzu Configuration</string> |
| 15 | </property> | 21 | </property> |
| @@ -62,6 +68,11 @@ | |||
| 62 | <string>Input</string> | 68 | <string>Input</string> |
| 63 | </attribute> | 69 | </attribute> |
| 64 | </widget> | 70 | </widget> |
| 71 | <widget class="ConfigureHotkeys" name="hotkeysTab"> | ||
| 72 | <attribute name="title"> | ||
| 73 | <string>Hotkeys</string> | ||
| 74 | </attribute> | ||
| 75 | </widget> | ||
| 65 | <widget class="ConfigureGraphics" name="graphicsTab"> | 76 | <widget class="ConfigureGraphics" name="graphicsTab"> |
| 66 | <attribute name="title"> | 77 | <attribute name="title"> |
| 67 | <string>Graphics</string> | 78 | <string>Graphics</string> |
| @@ -150,6 +161,12 @@ | |||
| 150 | <header>configuration/configure_input_simple.h</header> | 161 | <header>configuration/configure_input_simple.h</header> |
| 151 | <container>1</container> | 162 | <container>1</container> |
| 152 | </customwidget> | 163 | </customwidget> |
| 164 | <customwidget> | ||
| 165 | <class>ConfigureHotkeys</class> | ||
| 166 | <extends>QWidget</extends> | ||
| 167 | <header>configuration/configure_hotkeys.h</header> | ||
| 168 | <container>1</container> | ||
| 169 | </customwidget> | ||
| 153 | </customwidgets> | 170 | </customwidgets> |
| 154 | <resources/> | 171 | <resources/> |
| 155 | <connections> | 172 | <connections> |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 777050405..a5218b051 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -8,20 +8,22 @@ | |||
| 8 | #include "ui_configure.h" | 8 | #include "ui_configure.h" |
| 9 | #include "yuzu/configuration/config.h" | 9 | #include "yuzu/configuration/config.h" |
| 10 | #include "yuzu/configuration/configure_dialog.h" | 10 | #include "yuzu/configuration/configure_dialog.h" |
| 11 | #include "yuzu/configuration/configure_input_player.h" | ||
| 11 | #include "yuzu/hotkeys.h" | 12 | #include "yuzu/hotkeys.h" |
| 12 | 13 | ||
| 13 | ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry) | 14 | ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) |
| 14 | : QDialog(parent), ui(new Ui::ConfigureDialog) { | 15 | : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { |
| 15 | ui->setupUi(this); | 16 | ui->setupUi(this); |
| 16 | ui->generalTab->PopulateHotkeyList(registry); | 17 | ui->hotkeysTab->Populate(registry); |
| 17 | this->setConfiguration(); | 18 | this->setConfiguration(); |
| 18 | this->PopulateSelectionList(); | 19 | this->PopulateSelectionList(); |
| 19 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, | 20 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, |
| 20 | &ConfigureDialog::UpdateVisibleTabs); | 21 | &ConfigureDialog::UpdateVisibleTabs); |
| 21 | |||
| 22 | adjustSize(); | 22 | adjustSize(); |
| 23 | |||
| 24 | ui->selectorList->setCurrentRow(0); | 23 | ui->selectorList->setCurrentRow(0); |
| 24 | |||
| 25 | // Synchronise lists upon initialisation | ||
| 26 | ui->hotkeysTab->EmitHotkeysChanged(); | ||
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | ConfigureDialog::~ConfigureDialog() = default; | 29 | ConfigureDialog::~ConfigureDialog() = default; |
| @@ -34,6 +36,7 @@ void ConfigureDialog::applyConfiguration() { | |||
| 34 | ui->systemTab->applyConfiguration(); | 36 | ui->systemTab->applyConfiguration(); |
| 35 | ui->profileManagerTab->applyConfiguration(); | 37 | ui->profileManagerTab->applyConfiguration(); |
| 36 | ui->inputTab->applyConfiguration(); | 38 | ui->inputTab->applyConfiguration(); |
| 39 | ui->hotkeysTab->applyConfiguration(registry); | ||
| 37 | ui->graphicsTab->applyConfiguration(); | 40 | ui->graphicsTab->applyConfiguration(); |
| 38 | ui->audioTab->applyConfiguration(); | 41 | ui->audioTab->applyConfiguration(); |
| 39 | ui->debugTab->applyConfiguration(); | 42 | ui->debugTab->applyConfiguration(); |
| @@ -47,7 +50,7 @@ void ConfigureDialog::PopulateSelectionList() { | |||
| 47 | {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}}, | 50 | {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}}, |
| 48 | {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}}, | 51 | {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}}, |
| 49 | {tr("Graphics"), {tr("Graphics")}}, | 52 | {tr("Graphics"), {tr("Graphics")}}, |
| 50 | {tr("Controls"), {tr("Input")}}}}; | 53 | {tr("Controls"), {tr("Input"), tr("Hotkeys")}}}}; |
| 51 | 54 | ||
| 52 | for (const auto& entry : items) { | 55 | for (const auto& entry : items) { |
| 53 | auto* const item = new QListWidgetItem(entry.first); | 56 | auto* const item = new QListWidgetItem(entry.first); |
| @@ -66,6 +69,7 @@ void ConfigureDialog::UpdateVisibleTabs() { | |||
| 66 | {tr("System"), ui->systemTab}, | 69 | {tr("System"), ui->systemTab}, |
| 67 | {tr("Profiles"), ui->profileManagerTab}, | 70 | {tr("Profiles"), ui->profileManagerTab}, |
| 68 | {tr("Input"), ui->inputTab}, | 71 | {tr("Input"), ui->inputTab}, |
| 72 | {tr("Hotkeys"), ui->hotkeysTab}, | ||
| 69 | {tr("Graphics"), ui->graphicsTab}, | 73 | {tr("Graphics"), ui->graphicsTab}, |
| 70 | {tr("Audio"), ui->audioTab}, | 74 | {tr("Audio"), ui->audioTab}, |
| 71 | {tr("Debug"), ui->debugTab}, | 75 | {tr("Debug"), ui->debugTab}, |
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 243d9fa09..2363ba584 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h | |||
| @@ -17,7 +17,7 @@ class ConfigureDialog : public QDialog { | |||
| 17 | Q_OBJECT | 17 | Q_OBJECT |
| 18 | 18 | ||
| 19 | public: | 19 | public: |
| 20 | explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry); | 20 | explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); |
| 21 | ~ConfigureDialog() override; | 21 | ~ConfigureDialog() override; |
| 22 | 22 | ||
| 23 | void applyConfiguration(); | 23 | void applyConfiguration(); |
| @@ -28,4 +28,5 @@ private: | |||
| 28 | void PopulateSelectionList(); | 28 | void PopulateSelectionList(); |
| 29 | 29 | ||
| 30 | std::unique_ptr<Ui::ConfigureDialog> ui; | 30 | std::unique_ptr<Ui::ConfigureDialog> ui; |
| 31 | HotkeyRegistry& registry; | ||
| 31 | }; | 32 | }; |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 389fcf667..e48f4f5a3 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -28,19 +28,15 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 28 | ConfigureGeneral::~ConfigureGeneral() = default; | 28 | ConfigureGeneral::~ConfigureGeneral() = default; |
| 29 | 29 | ||
| 30 | void ConfigureGeneral::setConfiguration() { | 30 | void ConfigureGeneral::setConfiguration() { |
| 31 | ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); | 31 | ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan); |
| 32 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 32 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 33 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 33 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 34 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 34 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 35 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); | 35 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { | ||
| 39 | ui->widget->Populate(registry); | ||
| 40 | } | ||
| 41 | |||
| 42 | void ConfigureGeneral::applyConfiguration() { | 38 | void ConfigureGeneral::applyConfiguration() { |
| 43 | UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); | 39 | UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked(); |
| 44 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | 40 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); |
| 45 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 41 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 46 | UISettings::values.theme = | 42 | UISettings::values.theme = |
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 59738af40..df41d995b 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h | |||
| @@ -20,7 +20,6 @@ public: | |||
| 20 | explicit ConfigureGeneral(QWidget* parent = nullptr); | 20 | explicit ConfigureGeneral(QWidget* parent = nullptr); |
| 21 | ~ConfigureGeneral() override; | 21 | ~ConfigureGeneral() override; |
| 22 | 22 | ||
| 23 | void PopulateHotkeyList(const HotkeyRegistry& registry); | ||
| 24 | void applyConfiguration(); | 23 | void applyConfiguration(); |
| 25 | 24 | ||
| 26 | private: | 25 | private: |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 01d1c0b8e..1a5721fe7 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -98,22 +98,6 @@ | |||
| 98 | </widget> | 98 | </widget> |
| 99 | </item> | 99 | </item> |
| 100 | <item> | 100 | <item> |
| 101 | <widget class="QGroupBox" name="HotKeysGroupBox"> | ||
| 102 | <property name="title"> | ||
| 103 | <string>Hotkeys</string> | ||
| 104 | </property> | ||
| 105 | <layout class="QHBoxLayout" name="HotKeysHorizontalLayout"> | ||
| 106 | <item> | ||
| 107 | <layout class="QVBoxLayout" name="HotKeysVerticalLayout"> | ||
| 108 | <item> | ||
| 109 | <widget class="GHotkeysDialog" name="widget" native="true"/> | ||
| 110 | </item> | ||
| 111 | </layout> | ||
| 112 | </item> | ||
| 113 | </layout> | ||
| 114 | </widget> | ||
| 115 | </item> | ||
| 116 | <item> | ||
| 117 | <spacer name="verticalSpacer"> | 101 | <spacer name="verticalSpacer"> |
| 118 | <property name="orientation"> | 102 | <property name="orientation"> |
| 119 | <enum>Qt::Vertical</enum> | 103 | <enum>Qt::Vertical</enum> |
| @@ -130,14 +114,6 @@ | |||
| 130 | </item> | 114 | </item> |
| 131 | </layout> | 115 | </layout> |
| 132 | </widget> | 116 | </widget> |
| 133 | <customwidgets> | ||
| 134 | <customwidget> | ||
| 135 | <class>GHotkeysDialog</class> | ||
| 136 | <extends>QWidget</extends> | ||
| 137 | <header>hotkeys.h</header> | ||
| 138 | <container>1</container> | ||
| 139 | </customwidget> | ||
| 140 | </customwidgets> | ||
| 141 | <resources/> | 117 | <resources/> |
| 142 | <connections/> | 118 | <connections/> |
| 143 | </ui> | 119 | </ui> |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index dd1d67488..0a9883d37 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -77,6 +77,8 @@ void ConfigureGraphics::setConfiguration() { | |||
| 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 78 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 78 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 79 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 79 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 80 | ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||
| 81 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | ||
| 80 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 82 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 81 | Settings::values.bg_blue)); | 83 | Settings::values.bg_blue)); |
| 82 | } | 84 | } |
| @@ -90,6 +92,7 @@ void ConfigureGraphics::applyConfiguration() { | |||
| 90 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 92 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 91 | Settings::values.use_asynchronous_gpu_emulation = | 93 | Settings::values.use_asynchronous_gpu_emulation = |
| 92 | ui->use_asynchronous_gpu_emulation->isChecked(); | 94 | ui->use_asynchronous_gpu_emulation->isChecked(); |
| 95 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); | ||
| 93 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | 96 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); |
| 94 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | 97 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); |
| 95 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); | 98 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index c6767e0ca..15ab18ecd 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -71,6 +71,13 @@ | |||
| 71 | </widget> | 71 | </widget> |
| 72 | </item> | 72 | </item> |
| 73 | <item> | 73 | <item> |
| 74 | <widget class="QCheckBox" name="force_30fps_mode"> | ||
| 75 | <property name="text"> | ||
| 76 | <string>Force 30 FPS mode</string> | ||
| 77 | </property> | ||
| 78 | </widget> | ||
| 79 | </item> | ||
| 80 | <item> | ||
| 74 | <layout class="QHBoxLayout" name="horizontalLayout"> | 81 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 75 | <item> | 82 | <item> |
| 76 | <widget class="QLabel" name="label"> | 83 | <widget class="QLabel" name="label"> |
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp new file mode 100644 index 000000000..a7a8752e5 --- /dev/null +++ b/src/yuzu/configuration/configure_hotkeys.cpp | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QMessageBox> | ||
| 6 | #include <QStandardItemModel> | ||
| 7 | #include "core/settings.h" | ||
| 8 | #include "ui_configure_hotkeys.h" | ||
| 9 | #include "yuzu/configuration/configure_hotkeys.h" | ||
| 10 | #include "yuzu/hotkeys.h" | ||
| 11 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" | ||
| 12 | |||
| 13 | ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) | ||
| 14 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) { | ||
| 15 | ui->setupUi(this); | ||
| 16 | setFocusPolicy(Qt::ClickFocus); | ||
| 17 | |||
| 18 | model = new QStandardItemModel(this); | ||
| 19 | model->setColumnCount(3); | ||
| 20 | model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")}); | ||
| 21 | |||
| 22 | connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure); | ||
| 23 | ui->hotkey_list->setModel(model); | ||
| 24 | |||
| 25 | // TODO(Kloen): Make context configurable as well (hiding the column for now) | ||
| 26 | ui->hotkey_list->hideColumn(2); | ||
| 27 | |||
| 28 | ui->hotkey_list->setColumnWidth(0, 200); | ||
| 29 | ui->hotkey_list->resizeColumnToContents(1); | ||
| 30 | } | ||
| 31 | |||
| 32 | ConfigureHotkeys::~ConfigureHotkeys() = default; | ||
| 33 | |||
| 34 | void ConfigureHotkeys::EmitHotkeysChanged() { | ||
| 35 | emit HotkeysChanged(GetUsedKeyList()); | ||
| 36 | } | ||
| 37 | |||
| 38 | QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const { | ||
| 39 | QList<QKeySequence> list; | ||
| 40 | for (int r = 0; r < model->rowCount(); r++) { | ||
| 41 | const QStandardItem* parent = model->item(r, 0); | ||
| 42 | for (int r2 = 0; r2 < parent->rowCount(); r2++) { | ||
| 43 | const QStandardItem* keyseq = parent->child(r2, 1); | ||
| 44 | list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | return list; | ||
| 48 | } | ||
| 49 | |||
| 50 | void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { | ||
| 51 | for (const auto& group : registry.hotkey_groups) { | ||
| 52 | auto* parent_item = new QStandardItem(group.first); | ||
| 53 | parent_item->setEditable(false); | ||
| 54 | for (const auto& hotkey : group.second) { | ||
| 55 | auto* action = new QStandardItem(hotkey.first); | ||
| 56 | auto* keyseq = | ||
| 57 | new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); | ||
| 58 | action->setEditable(false); | ||
| 59 | keyseq->setEditable(false); | ||
| 60 | parent_item->appendRow({action, keyseq}); | ||
| 61 | } | ||
| 62 | model->appendRow(parent_item); | ||
| 63 | } | ||
| 64 | |||
| 65 | ui->hotkey_list->expandAll(); | ||
| 66 | } | ||
| 67 | |||
| 68 | void ConfigureHotkeys::Configure(QModelIndex index) { | ||
| 69 | if (!index.parent().isValid()) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | index = index.sibling(index.row(), 1); | ||
| 74 | auto* const model = ui->hotkey_list->model(); | ||
| 75 | const auto previous_key = model->data(index); | ||
| 76 | |||
| 77 | SequenceDialog hotkey_dialog{this}; | ||
| 78 | |||
| 79 | const int return_code = hotkey_dialog.exec(); | ||
| 80 | const auto key_sequence = hotkey_dialog.GetSequence(); | ||
| 81 | if (return_code == QDialog::Rejected || key_sequence.isEmpty()) { | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | |||
| 85 | if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { | ||
| 86 | QMessageBox::critical(this, tr("Error in inputted key"), | ||
| 87 | tr("You're using a key that's already bound.")); | ||
| 88 | } else { | ||
| 89 | model->setData(index, key_sequence.toString(QKeySequence::NativeText)); | ||
| 90 | EmitHotkeysChanged(); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { | ||
| 95 | return GetUsedKeyList().contains(key_sequence); | ||
| 96 | } | ||
| 97 | |||
| 98 | void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { | ||
| 99 | for (int key_id = 0; key_id < model->rowCount(); key_id++) { | ||
| 100 | const QStandardItem* parent = model->item(key_id, 0); | ||
| 101 | for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { | ||
| 102 | const QStandardItem* action = parent->child(key_column_id, 0); | ||
| 103 | const QStandardItem* keyseq = parent->child(key_column_id, 1); | ||
| 104 | for (auto& [group, sub_actions] : registry.hotkey_groups) { | ||
| 105 | if (group != parent->text()) | ||
| 106 | continue; | ||
| 107 | for (auto& [action_name, hotkey] : sub_actions) { | ||
| 108 | if (action_name != action->text()) | ||
| 109 | continue; | ||
| 110 | hotkey.keyseq = QKeySequence(keyseq->text()); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | registry.SaveHotkeys(); | ||
| 117 | Settings::Apply(); | ||
| 118 | } | ||
| 119 | |||
| 120 | void ConfigureHotkeys::retranslateUi() { | ||
| 121 | ui->retranslateUi(this); | ||
| 122 | } | ||
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h new file mode 100644 index 000000000..73fb8a175 --- /dev/null +++ b/src/yuzu/configuration/configure_hotkeys.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <QWidget> | ||
| 9 | |||
| 10 | namespace Ui { | ||
| 11 | class ConfigureHotkeys; | ||
| 12 | } | ||
| 13 | |||
| 14 | class HotkeyRegistry; | ||
| 15 | class QStandardItemModel; | ||
| 16 | |||
| 17 | class ConfigureHotkeys : public QWidget { | ||
| 18 | Q_OBJECT | ||
| 19 | |||
| 20 | public: | ||
| 21 | explicit ConfigureHotkeys(QWidget* parent = nullptr); | ||
| 22 | ~ConfigureHotkeys() override; | ||
| 23 | |||
| 24 | void applyConfiguration(HotkeyRegistry& registry); | ||
| 25 | void retranslateUi(); | ||
| 26 | |||
| 27 | void EmitHotkeysChanged(); | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Populates the hotkey list widget using data from the provided registry. | ||
| 31 | * Called everytime the Configure dialog is opened. | ||
| 32 | * @param registry The HotkeyRegistry whose data is used to populate the list. | ||
| 33 | */ | ||
| 34 | void Populate(const HotkeyRegistry& registry); | ||
| 35 | |||
| 36 | signals: | ||
| 37 | void HotkeysChanged(QList<QKeySequence> new_key_list); | ||
| 38 | |||
| 39 | private: | ||
| 40 | void Configure(QModelIndex index); | ||
| 41 | bool IsUsedKey(QKeySequence key_sequence) const; | ||
| 42 | QList<QKeySequence> GetUsedKeyList() const; | ||
| 43 | |||
| 44 | std::unique_ptr<Ui::ConfigureHotkeys> ui; | ||
| 45 | |||
| 46 | QStandardItemModel* model; | ||
| 47 | }; | ||
diff --git a/src/yuzu/configuration/configure_hotkeys.ui b/src/yuzu/configuration/configure_hotkeys.ui new file mode 100644 index 000000000..0d0b70f38 --- /dev/null +++ b/src/yuzu/configuration/configure_hotkeys.ui | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureHotkeys</class> | ||
| 4 | <widget class="QWidget" name="ConfigureHotkeys"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>363</width> | ||
| 10 | <height>388</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Hotkey Settings</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 17 | <item> | ||
| 18 | <layout class="QVBoxLayout" name="verticalLayout_2"> | ||
| 19 | <item> | ||
| 20 | <widget class="QLabel" name="label_2"> | ||
| 21 | <property name="text"> | ||
| 22 | <string>Double-click on a binding to change it.</string> | ||
| 23 | </property> | ||
| 24 | </widget> | ||
| 25 | </item> | ||
| 26 | <item> | ||
| 27 | <widget class="QTreeView" name="hotkey_list"> | ||
| 28 | <property name="editTriggers"> | ||
| 29 | <set>QAbstractItemView::NoEditTriggers</set> | ||
| 30 | </property> | ||
| 31 | <property name="sortingEnabled"> | ||
| 32 | <bool>false</bool> | ||
| 33 | </property> | ||
| 34 | </widget> | ||
| 35 | </item> | ||
| 36 | </layout> | ||
| 37 | </item> | ||
| 38 | </layout> | ||
| 39 | </widget> | ||
| 40 | <resources/> | ||
| 41 | <connections/> | ||
| 42 | </ui> \ No newline at end of file | ||
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp deleted file mode 100644 index 11023ed63..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ /dev/null | |||
| @@ -1,460 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QBoxLayout> | ||
| 6 | #include <QComboBox> | ||
| 7 | #include <QDebug> | ||
| 8 | #include <QFileDialog> | ||
| 9 | #include <QLabel> | ||
| 10 | #include <QMouseEvent> | ||
| 11 | #include <QPushButton> | ||
| 12 | #include <QScrollArea> | ||
| 13 | #include <QSpinBox> | ||
| 14 | #include "common/vector_math.h" | ||
| 15 | #include "core/core.h" | ||
| 16 | #include "core/memory.h" | ||
| 17 | #include "video_core/engines/maxwell_3d.h" | ||
| 18 | #include "video_core/gpu.h" | ||
| 19 | #include "video_core/textures/decoders.h" | ||
| 20 | #include "video_core/textures/texture.h" | ||
| 21 | #include "yuzu/debugger/graphics/graphics_surface.h" | ||
| 22 | #include "yuzu/util/spinbox.h" | ||
| 23 | |||
| 24 | static Tegra::Texture::TextureFormat ConvertToTextureFormat( | ||
| 25 | Tegra::RenderTargetFormat render_target_format) { | ||
| 26 | switch (render_target_format) { | ||
| 27 | case Tegra::RenderTargetFormat::RGBA8_UNORM: | ||
| 28 | return Tegra::Texture::TextureFormat::A8R8G8B8; | ||
| 29 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | ||
| 30 | return Tegra::Texture::TextureFormat::A2B10G10R10; | ||
| 31 | default: | ||
| 32 | UNIMPLEMENTED_MSG("Unimplemented RT format"); | ||
| 33 | return Tegra::Texture::TextureFormat::A8R8G8B8; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) | ||
| 38 | : QLabel(parent), surface_widget(surface_widget_) {} | ||
| 39 | |||
| 40 | SurfacePicture::~SurfacePicture() = default; | ||
| 41 | |||
| 42 | void SurfacePicture::mousePressEvent(QMouseEvent* event) { | ||
| 43 | // Only do something while the left mouse button is held down | ||
| 44 | if (!(event->buttons() & Qt::LeftButton)) | ||
| 45 | return; | ||
| 46 | |||
| 47 | if (pixmap() == nullptr) | ||
| 48 | return; | ||
| 49 | |||
| 50 | if (surface_widget) | ||
| 51 | surface_widget->Pick(event->x() * pixmap()->width() / width(), | ||
| 52 | event->y() * pixmap()->height() / height()); | ||
| 53 | } | ||
| 54 | |||
| 55 | void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { | ||
| 56 | // We also want to handle the event if the user moves the mouse while holding down the LMB | ||
| 57 | mousePressEvent(event); | ||
| 58 | } | ||
| 59 | |||
| 60 | GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||
| 61 | QWidget* parent) | ||
| 62 | : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent), | ||
| 63 | surface_source(Source::RenderTarget0) { | ||
| 64 | setObjectName("MaxwellSurface"); | ||
| 65 | |||
| 66 | surface_source_list = new QComboBox; | ||
| 67 | surface_source_list->addItem(tr("Render Target 0")); | ||
| 68 | surface_source_list->addItem(tr("Render Target 1")); | ||
| 69 | surface_source_list->addItem(tr("Render Target 2")); | ||
| 70 | surface_source_list->addItem(tr("Render Target 3")); | ||
| 71 | surface_source_list->addItem(tr("Render Target 4")); | ||
| 72 | surface_source_list->addItem(tr("Render Target 5")); | ||
| 73 | surface_source_list->addItem(tr("Render Target 6")); | ||
| 74 | surface_source_list->addItem(tr("Render Target 7")); | ||
| 75 | surface_source_list->addItem(tr("Z Buffer")); | ||
| 76 | surface_source_list->addItem(tr("Custom")); | ||
| 77 | surface_source_list->setCurrentIndex(static_cast<int>(surface_source)); | ||
| 78 | |||
| 79 | surface_address_control = new CSpinBox; | ||
| 80 | surface_address_control->SetBase(16); | ||
| 81 | surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF); | ||
| 82 | surface_address_control->SetPrefix("0x"); | ||
| 83 | |||
| 84 | unsigned max_dimension = 16384; // TODO: Find actual maximum | ||
| 85 | |||
| 86 | surface_width_control = new QSpinBox; | ||
| 87 | surface_width_control->setRange(0, max_dimension); | ||
| 88 | |||
| 89 | surface_height_control = new QSpinBox; | ||
| 90 | surface_height_control->setRange(0, max_dimension); | ||
| 91 | |||
| 92 | surface_picker_x_control = new QSpinBox; | ||
| 93 | surface_picker_x_control->setRange(0, max_dimension - 1); | ||
| 94 | |||
| 95 | surface_picker_y_control = new QSpinBox; | ||
| 96 | surface_picker_y_control->setRange(0, max_dimension - 1); | ||
| 97 | |||
| 98 | surface_format_control = new QComboBox; | ||
| 99 | |||
| 100 | // Color formats sorted by Maxwell texture format index | ||
| 101 | surface_format_control->addItem(tr("None")); | ||
| 102 | surface_format_control->addItem(tr("Unknown")); | ||
| 103 | surface_format_control->addItem(tr("Unknown")); | ||
| 104 | surface_format_control->addItem(tr("Unknown")); | ||
| 105 | surface_format_control->addItem(tr("Unknown")); | ||
| 106 | surface_format_control->addItem(tr("Unknown")); | ||
| 107 | surface_format_control->addItem(tr("Unknown")); | ||
| 108 | surface_format_control->addItem(tr("Unknown")); | ||
| 109 | surface_format_control->addItem(tr("A8R8G8B8")); | ||
| 110 | surface_format_control->addItem(tr("Unknown")); | ||
| 111 | surface_format_control->addItem(tr("Unknown")); | ||
| 112 | surface_format_control->addItem(tr("Unknown")); | ||
| 113 | surface_format_control->addItem(tr("Unknown")); | ||
| 114 | surface_format_control->addItem(tr("Unknown")); | ||
| 115 | surface_format_control->addItem(tr("Unknown")); | ||
| 116 | surface_format_control->addItem(tr("Unknown")); | ||
| 117 | surface_format_control->addItem(tr("Unknown")); | ||
| 118 | surface_format_control->addItem(tr("Unknown")); | ||
| 119 | surface_format_control->addItem(tr("Unknown")); | ||
| 120 | surface_format_control->addItem(tr("Unknown")); | ||
| 121 | surface_format_control->addItem(tr("Unknown")); | ||
| 122 | surface_format_control->addItem(tr("Unknown")); | ||
| 123 | surface_format_control->addItem(tr("Unknown")); | ||
| 124 | surface_format_control->addItem(tr("Unknown")); | ||
| 125 | surface_format_control->addItem(tr("Unknown")); | ||
| 126 | surface_format_control->addItem(tr("Unknown")); | ||
| 127 | surface_format_control->addItem(tr("Unknown")); | ||
| 128 | surface_format_control->addItem(tr("Unknown")); | ||
| 129 | surface_format_control->addItem(tr("Unknown")); | ||
| 130 | surface_format_control->addItem(tr("Unknown")); | ||
| 131 | surface_format_control->addItem(tr("Unknown")); | ||
| 132 | surface_format_control->addItem(tr("Unknown")); | ||
| 133 | surface_format_control->addItem(tr("Unknown")); | ||
| 134 | surface_format_control->addItem(tr("Unknown")); | ||
| 135 | surface_format_control->addItem(tr("Unknown")); | ||
| 136 | surface_format_control->addItem(tr("Unknown")); | ||
| 137 | surface_format_control->addItem(tr("DXT1")); | ||
| 138 | surface_format_control->addItem(tr("DXT23")); | ||
| 139 | surface_format_control->addItem(tr("DXT45")); | ||
| 140 | surface_format_control->addItem(tr("DXN1")); | ||
| 141 | surface_format_control->addItem(tr("DXN2")); | ||
| 142 | |||
| 143 | surface_info_label = new QLabel(); | ||
| 144 | surface_info_label->setWordWrap(true); | ||
| 145 | |||
| 146 | surface_picture_label = new SurfacePicture(0, this); | ||
| 147 | surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | ||
| 148 | surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); | ||
| 149 | surface_picture_label->setScaledContents(false); | ||
| 150 | |||
| 151 | auto scroll_area = new QScrollArea(); | ||
| 152 | scroll_area->setBackgroundRole(QPalette::Dark); | ||
| 153 | scroll_area->setWidgetResizable(false); | ||
| 154 | scroll_area->setWidget(surface_picture_label); | ||
| 155 | |||
| 156 | save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); | ||
| 157 | |||
| 158 | // Connections | ||
| 159 | connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); | ||
| 160 | connect(surface_source_list, | ||
| 161 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||
| 162 | &GraphicsSurfaceWidget::OnSurfaceSourceChanged); | ||
| 163 | connect(surface_address_control, &CSpinBox::ValueChanged, this, | ||
| 164 | &GraphicsSurfaceWidget::OnSurfaceAddressChanged); | ||
| 165 | connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), | ||
| 166 | this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged); | ||
| 167 | connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), | ||
| 168 | this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged); | ||
| 169 | connect(surface_format_control, | ||
| 170 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||
| 171 | &GraphicsSurfaceWidget::OnSurfaceFormatChanged); | ||
| 172 | connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), | ||
| 173 | this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged); | ||
| 174 | connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), | ||
| 175 | this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged); | ||
| 176 | connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); | ||
| 177 | |||
| 178 | auto main_widget = new QWidget; | ||
| 179 | auto main_layout = new QVBoxLayout; | ||
| 180 | { | ||
| 181 | auto sub_layout = new QHBoxLayout; | ||
| 182 | sub_layout->addWidget(new QLabel(tr("Source:"))); | ||
| 183 | sub_layout->addWidget(surface_source_list); | ||
| 184 | main_layout->addLayout(sub_layout); | ||
| 185 | } | ||
| 186 | { | ||
| 187 | auto sub_layout = new QHBoxLayout; | ||
| 188 | sub_layout->addWidget(new QLabel(tr("GPU Address:"))); | ||
| 189 | sub_layout->addWidget(surface_address_control); | ||
| 190 | main_layout->addLayout(sub_layout); | ||
| 191 | } | ||
| 192 | { | ||
| 193 | auto sub_layout = new QHBoxLayout; | ||
| 194 | sub_layout->addWidget(new QLabel(tr("Width:"))); | ||
| 195 | sub_layout->addWidget(surface_width_control); | ||
| 196 | main_layout->addLayout(sub_layout); | ||
| 197 | } | ||
| 198 | { | ||
| 199 | auto sub_layout = new QHBoxLayout; | ||
| 200 | sub_layout->addWidget(new QLabel(tr("Height:"))); | ||
| 201 | sub_layout->addWidget(surface_height_control); | ||
| 202 | main_layout->addLayout(sub_layout); | ||
| 203 | } | ||
| 204 | { | ||
| 205 | auto sub_layout = new QHBoxLayout; | ||
| 206 | sub_layout->addWidget(new QLabel(tr("Format:"))); | ||
| 207 | sub_layout->addWidget(surface_format_control); | ||
| 208 | main_layout->addLayout(sub_layout); | ||
| 209 | } | ||
| 210 | main_layout->addWidget(scroll_area); | ||
| 211 | |||
| 212 | auto info_layout = new QHBoxLayout; | ||
| 213 | { | ||
| 214 | auto xy_layout = new QVBoxLayout; | ||
| 215 | { | ||
| 216 | { | ||
| 217 | auto sub_layout = new QHBoxLayout; | ||
| 218 | sub_layout->addWidget(new QLabel(tr("X:"))); | ||
| 219 | sub_layout->addWidget(surface_picker_x_control); | ||
| 220 | xy_layout->addLayout(sub_layout); | ||
| 221 | } | ||
| 222 | { | ||
| 223 | auto sub_layout = new QHBoxLayout; | ||
| 224 | sub_layout->addWidget(new QLabel(tr("Y:"))); | ||
| 225 | sub_layout->addWidget(surface_picker_y_control); | ||
| 226 | xy_layout->addLayout(sub_layout); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | info_layout->addLayout(xy_layout); | ||
| 230 | surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); | ||
| 231 | info_layout->addWidget(surface_info_label); | ||
| 232 | } | ||
| 233 | main_layout->addLayout(info_layout); | ||
| 234 | |||
| 235 | main_layout->addWidget(save_surface); | ||
| 236 | main_widget->setLayout(main_layout); | ||
| 237 | setWidget(main_widget); | ||
| 238 | |||
| 239 | // Load current data - TODO: Make sure this works when emulation is not running | ||
| 240 | if (debug_context && debug_context->at_breakpoint) { | ||
| 241 | emit Update(); | ||
| 242 | widget()->setEnabled(debug_context->at_breakpoint); | ||
| 243 | } else { | ||
| 244 | widget()->setEnabled(false); | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { | ||
| 249 | emit Update(); | ||
| 250 | widget()->setEnabled(true); | ||
| 251 | } | ||
| 252 | |||
| 253 | void GraphicsSurfaceWidget::OnResumed() { | ||
| 254 | widget()->setEnabled(false); | ||
| 255 | } | ||
| 256 | |||
| 257 | void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { | ||
| 258 | surface_source = static_cast<Source>(new_value); | ||
| 259 | emit Update(); | ||
| 260 | } | ||
| 261 | |||
| 262 | void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { | ||
| 263 | if (surface_address != new_value) { | ||
| 264 | surface_address = static_cast<GPUVAddr>(new_value); | ||
| 265 | |||
| 266 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 267 | emit Update(); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) { | ||
| 272 | if (surface_width != static_cast<unsigned>(new_value)) { | ||
| 273 | surface_width = static_cast<unsigned>(new_value); | ||
| 274 | |||
| 275 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 276 | emit Update(); | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) { | ||
| 281 | if (surface_height != static_cast<unsigned>(new_value)) { | ||
| 282 | surface_height = static_cast<unsigned>(new_value); | ||
| 283 | |||
| 284 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 285 | emit Update(); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) { | ||
| 290 | if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) { | ||
| 291 | surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value); | ||
| 292 | |||
| 293 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 294 | emit Update(); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) { | ||
| 299 | if (surface_picker_x != new_value) { | ||
| 300 | surface_picker_x = new_value; | ||
| 301 | Pick(surface_picker_x, surface_picker_y); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) { | ||
| 306 | if (surface_picker_y != new_value) { | ||
| 307 | surface_picker_y = new_value; | ||
| 308 | Pick(surface_picker_x, surface_picker_y); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | void GraphicsSurfaceWidget::Pick(int x, int y) { | ||
| 313 | surface_picker_x_control->setValue(x); | ||
| 314 | surface_picker_y_control->setValue(y); | ||
| 315 | |||
| 316 | if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 || | ||
| 317 | y >= static_cast<int>(surface_height)) { | ||
| 318 | surface_info_label->setText(tr("Pixel out of bounds")); | ||
| 319 | surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||
| 320 | return; | ||
| 321 | } | ||
| 322 | |||
| 323 | surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>")); | ||
| 324 | surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||
| 325 | } | ||
| 326 | |||
| 327 | void GraphicsSurfaceWidget::OnUpdate() { | ||
| 328 | auto& gpu = Core::System::GetInstance().GPU(); | ||
| 329 | |||
| 330 | QPixmap pixmap; | ||
| 331 | |||
| 332 | switch (surface_source) { | ||
| 333 | case Source::RenderTarget0: | ||
| 334 | case Source::RenderTarget1: | ||
| 335 | case Source::RenderTarget2: | ||
| 336 | case Source::RenderTarget3: | ||
| 337 | case Source::RenderTarget4: | ||
| 338 | case Source::RenderTarget5: | ||
| 339 | case Source::RenderTarget6: | ||
| 340 | case Source::RenderTarget7: { | ||
| 341 | // TODO: Store a reference to the registers in the debug context instead of accessing them | ||
| 342 | // directly... | ||
| 343 | |||
| 344 | const auto& registers = gpu.Maxwell3D().regs; | ||
| 345 | const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - | ||
| 346 | static_cast<std::size_t>(Source::RenderTarget0)]; | ||
| 347 | |||
| 348 | surface_address = rt.Address(); | ||
| 349 | surface_width = rt.width; | ||
| 350 | surface_height = rt.height; | ||
| 351 | if (rt.format != Tegra::RenderTargetFormat::NONE) { | ||
| 352 | surface_format = ConvertToTextureFormat(rt.format); | ||
| 353 | } | ||
| 354 | |||
| 355 | break; | ||
| 356 | } | ||
| 357 | |||
| 358 | case Source::Custom: { | ||
| 359 | // Keep user-specified values | ||
| 360 | break; | ||
| 361 | } | ||
| 362 | |||
| 363 | default: | ||
| 364 | qDebug() << "Unknown surface source " << static_cast<int>(surface_source); | ||
| 365 | break; | ||
| 366 | } | ||
| 367 | |||
| 368 | surface_address_control->SetValue(surface_address); | ||
| 369 | surface_width_control->setValue(surface_width); | ||
| 370 | surface_height_control->setValue(surface_height); | ||
| 371 | surface_format_control->setCurrentIndex(static_cast<int>(surface_format)); | ||
| 372 | |||
| 373 | if (surface_address == 0) { | ||
| 374 | surface_picture_label->hide(); | ||
| 375 | surface_info_label->setText(tr("(invalid surface address)")); | ||
| 376 | surface_info_label->setAlignment(Qt::AlignCenter); | ||
| 377 | surface_picker_x_control->setEnabled(false); | ||
| 378 | surface_picker_y_control->setEnabled(false); | ||
| 379 | save_surface->setEnabled(false); | ||
| 380 | return; | ||
| 381 | } | ||
| 382 | |||
| 383 | // TODO: Implement a good way to visualize alpha components! | ||
| 384 | |||
| 385 | QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); | ||
| 386 | |||
| 387 | // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. | ||
| 388 | // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. | ||
| 389 | auto unswizzled_data = Tegra::Texture::UnswizzleTexture( | ||
| 390 | gpu.MemoryManager().GetPointer(surface_address), 1, 1, | ||
| 391 | Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U); | ||
| 392 | |||
| 393 | auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, | ||
| 394 | surface_width, surface_height); | ||
| 395 | |||
| 396 | surface_picture_label->show(); | ||
| 397 | |||
| 398 | for (unsigned int y = 0; y < surface_height; ++y) { | ||
| 399 | for (unsigned int x = 0; x < surface_width; ++x) { | ||
| 400 | Common::Vec4<u8> color; | ||
| 401 | color[0] = texture_data[x + y * surface_width + 0]; | ||
| 402 | color[1] = texture_data[x + y * surface_width + 1]; | ||
| 403 | color[2] = texture_data[x + y * surface_width + 2]; | ||
| 404 | color[3] = texture_data[x + y * surface_width + 3]; | ||
| 405 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | pixmap = QPixmap::fromImage(decoded_image); | ||
| 410 | surface_picture_label->setPixmap(pixmap); | ||
| 411 | surface_picture_label->resize(pixmap.size()); | ||
| 412 | |||
| 413 | // Update the info with pixel data | ||
| 414 | surface_picker_x_control->setEnabled(true); | ||
| 415 | surface_picker_y_control->setEnabled(true); | ||
| 416 | Pick(surface_picker_x, surface_picker_y); | ||
| 417 | |||
| 418 | // Enable saving the converted pixmap to file | ||
| 419 | save_surface->setEnabled(true); | ||
| 420 | } | ||
| 421 | |||
| 422 | void GraphicsSurfaceWidget::SaveSurface() { | ||
| 423 | QString png_filter = tr("Portable Network Graphic (*.png)"); | ||
| 424 | QString bin_filter = tr("Binary data (*.bin)"); | ||
| 425 | |||
| 426 | QString selectedFilter; | ||
| 427 | QString filename = QFileDialog::getSaveFileName( | ||
| 428 | this, tr("Save Surface"), | ||
| 429 | QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), | ||
| 430 | QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); | ||
| 431 | |||
| 432 | if (filename.isEmpty()) { | ||
| 433 | // If the user canceled the dialog, don't save anything. | ||
| 434 | return; | ||
| 435 | } | ||
| 436 | |||
| 437 | if (selectedFilter == png_filter) { | ||
| 438 | const QPixmap* pixmap = surface_picture_label->pixmap(); | ||
| 439 | ASSERT_MSG(pixmap != nullptr, "No pixmap set"); | ||
| 440 | |||
| 441 | QFile file(filename); | ||
| 442 | file.open(QIODevice::WriteOnly); | ||
| 443 | if (pixmap) | ||
| 444 | pixmap->save(&file, "PNG"); | ||
| 445 | } else if (selectedFilter == bin_filter) { | ||
| 446 | auto& gpu = Core::System::GetInstance().GPU(); | ||
| 447 | std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); | ||
| 448 | |||
| 449 | const u8* buffer = Memory::GetPointer(*address); | ||
| 450 | ASSERT_MSG(buffer != nullptr, "Memory not accessible"); | ||
| 451 | |||
| 452 | QFile file(filename); | ||
| 453 | file.open(QIODevice::WriteOnly); | ||
| 454 | int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); | ||
| 455 | QByteArray data(reinterpret_cast<const char*>(buffer), size); | ||
| 456 | file.write(data); | ||
| 457 | } else { | ||
| 458 | UNREACHABLE_MSG("Unhandled filter selected"); | ||
| 459 | } | ||
| 460 | } | ||
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h deleted file mode 100644 index 89445b18f..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.h +++ /dev/null | |||
| @@ -1,96 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QLabel> | ||
| 8 | #include <QPushButton> | ||
| 9 | #include "video_core/memory_manager.h" | ||
| 10 | #include "video_core/textures/texture.h" | ||
| 11 | #include "yuzu/debugger/graphics/graphics_breakpoint_observer.h" | ||
| 12 | |||
| 13 | class QComboBox; | ||
| 14 | class QSpinBox; | ||
| 15 | class CSpinBox; | ||
| 16 | |||
| 17 | class GraphicsSurfaceWidget; | ||
| 18 | |||
| 19 | class SurfacePicture : public QLabel { | ||
| 20 | Q_OBJECT | ||
| 21 | |||
| 22 | public: | ||
| 23 | explicit SurfacePicture(QWidget* parent = nullptr, | ||
| 24 | GraphicsSurfaceWidget* surface_widget = nullptr); | ||
| 25 | ~SurfacePicture() override; | ||
| 26 | |||
| 27 | protected slots: | ||
| 28 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 29 | void mousePressEvent(QMouseEvent* event) override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | GraphicsSurfaceWidget* surface_widget; | ||
| 33 | }; | ||
| 34 | |||
| 35 | class GraphicsSurfaceWidget : public BreakPointObserverDock { | ||
| 36 | Q_OBJECT | ||
| 37 | |||
| 38 | using Event = Tegra::DebugContext::Event; | ||
| 39 | |||
| 40 | enum class Source { | ||
| 41 | RenderTarget0 = 0, | ||
| 42 | RenderTarget1 = 1, | ||
| 43 | RenderTarget2 = 2, | ||
| 44 | RenderTarget3 = 3, | ||
| 45 | RenderTarget4 = 4, | ||
| 46 | RenderTarget5 = 5, | ||
| 47 | RenderTarget6 = 6, | ||
| 48 | RenderTarget7 = 7, | ||
| 49 | ZBuffer = 8, | ||
| 50 | Custom = 9, | ||
| 51 | }; | ||
| 52 | |||
| 53 | public: | ||
| 54 | explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||
| 55 | QWidget* parent = nullptr); | ||
| 56 | void Pick(int x, int y); | ||
| 57 | |||
| 58 | public slots: | ||
| 59 | void OnSurfaceSourceChanged(int new_value); | ||
| 60 | void OnSurfaceAddressChanged(qint64 new_value); | ||
| 61 | void OnSurfaceWidthChanged(int new_value); | ||
| 62 | void OnSurfaceHeightChanged(int new_value); | ||
| 63 | void OnSurfaceFormatChanged(int new_value); | ||
| 64 | void OnSurfacePickerXChanged(int new_value); | ||
| 65 | void OnSurfacePickerYChanged(int new_value); | ||
| 66 | void OnUpdate(); | ||
| 67 | |||
| 68 | signals: | ||
| 69 | void Update(); | ||
| 70 | |||
| 71 | private: | ||
| 72 | void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; | ||
| 73 | void OnResumed() override; | ||
| 74 | |||
| 75 | void SaveSurface(); | ||
| 76 | |||
| 77 | QComboBox* surface_source_list; | ||
| 78 | CSpinBox* surface_address_control; | ||
| 79 | QSpinBox* surface_width_control; | ||
| 80 | QSpinBox* surface_height_control; | ||
| 81 | QComboBox* surface_format_control; | ||
| 82 | |||
| 83 | SurfacePicture* surface_picture_label; | ||
| 84 | QSpinBox* surface_picker_x_control; | ||
| 85 | QSpinBox* surface_picker_y_control; | ||
| 86 | QLabel* surface_info_label; | ||
| 87 | QPushButton* save_surface; | ||
| 88 | |||
| 89 | Source surface_source; | ||
| 90 | GPUVAddr surface_address; | ||
| 91 | unsigned surface_width; | ||
| 92 | unsigned surface_height; | ||
| 93 | Tegra::Texture::TextureFormat surface_format; | ||
| 94 | int surface_picker_x = 0; | ||
| 95 | int surface_picker_y = 0; | ||
| 96 | }; | ||
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index c0e3c5fa9..b0ca766ec 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "common/common_types.h" | 18 | #include "common/common_types.h" |
| 19 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| 20 | #include "core/file_sys/patch_manager.h" | 20 | #include "core/file_sys/patch_manager.h" |
| 21 | #include "core/file_sys/registered_cache.h" | ||
| 21 | #include "yuzu/compatibility_list.h" | 22 | #include "yuzu/compatibility_list.h" |
| 22 | #include "yuzu/game_list.h" | 23 | #include "yuzu/game_list.h" |
| 23 | #include "yuzu/game_list_p.h" | 24 | #include "yuzu/game_list_p.h" |
| @@ -193,8 +194,9 @@ void GameList::onFilterCloseClicked() { | |||
| 193 | main_window->filterBarSetChecked(false); | 194 | main_window->filterBarSetChecked(false); |
| 194 | } | 195 | } |
| 195 | 196 | ||
| 196 | GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | 197 | GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider, |
| 197 | : QWidget{parent}, vfs(std::move(vfs)) { | 198 | GMainWindow* parent) |
| 199 | : QWidget{parent}, vfs(std::move(vfs)), provider(provider) { | ||
| 198 | watcher = new QFileSystemWatcher(this); | 200 | watcher = new QFileSystemWatcher(this); |
| 199 | connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); | 201 | connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); |
| 200 | 202 | ||
| @@ -329,6 +331,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 329 | QMenu context_menu; | 331 | QMenu context_menu; |
| 330 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); | 332 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); |
| 331 | QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); | 333 | QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); |
| 334 | QAction* open_transferable_shader_cache = | ||
| 335 | context_menu.addAction(tr("Open Transferable Shader Cache")); | ||
| 332 | context_menu.addSeparator(); | 336 | context_menu.addSeparator(); |
| 333 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); | 337 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); |
| 334 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 338 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| @@ -344,6 +348,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 344 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); | 348 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); |
| 345 | connect(open_lfs_location, &QAction::triggered, | 349 | connect(open_lfs_location, &QAction::triggered, |
| 346 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); | 350 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); |
| 351 | connect(open_transferable_shader_cache, &QAction::triggered, | ||
| 352 | [&]() { emit OpenTransferableShaderCacheRequested(program_id); }); | ||
| 347 | connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); | 353 | connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); |
| 348 | connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); | 354 | connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); |
| 349 | connect(navigate_to_gamedb_entry, &QAction::triggered, | 355 | connect(navigate_to_gamedb_entry, &QAction::triggered, |
| @@ -428,7 +434,8 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | |||
| 428 | 434 | ||
| 429 | emit ShouldCancelWorker(); | 435 | emit ShouldCancelWorker(); |
| 430 | 436 | ||
| 431 | GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list); | 437 | GameListWorker* worker = |
| 438 | new GameListWorker(vfs, provider, dir_path, deep_scan, compatibility_list); | ||
| 432 | 439 | ||
| 433 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); | 440 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); |
| 434 | connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, | 441 | connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, |
| @@ -460,9 +467,10 @@ void GameList::LoadInterfaceLayout() { | |||
| 460 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; | 467 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; |
| 461 | 468 | ||
| 462 | void GameList::RefreshGameDirectory() { | 469 | void GameList::RefreshGameDirectory() { |
| 463 | if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { | 470 | if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { |
| 464 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | 471 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); |
| 465 | search_field->clear(); | 472 | search_field->clear(); |
| 466 | PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 473 | PopulateAsync(UISettings::values.game_directory_path, |
| 474 | UISettings::values.game_directory_deepscan); | ||
| 467 | } | 475 | } |
| 468 | } | 476 | } |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index b317eb2fc..56007eef8 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -26,8 +26,9 @@ class GameListSearchField; | |||
| 26 | class GMainWindow; | 26 | class GMainWindow; |
| 27 | 27 | ||
| 28 | namespace FileSys { | 28 | namespace FileSys { |
| 29 | class ManualContentProvider; | ||
| 29 | class VfsFilesystem; | 30 | class VfsFilesystem; |
| 30 | } | 31 | } // namespace FileSys |
| 31 | 32 | ||
| 32 | enum class GameListOpenTarget { | 33 | enum class GameListOpenTarget { |
| 33 | SaveData, | 34 | SaveData, |
| @@ -47,7 +48,8 @@ public: | |||
| 47 | COLUMN_COUNT, // Number of columns | 48 | COLUMN_COUNT, // Number of columns |
| 48 | }; | 49 | }; |
| 49 | 50 | ||
| 50 | explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); | 51 | explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, |
| 52 | FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr); | ||
| 51 | ~GameList() override; | 53 | ~GameList() override; |
| 52 | 54 | ||
| 53 | void clearFilter(); | 55 | void clearFilter(); |
| @@ -66,6 +68,7 @@ signals: | |||
| 66 | void GameChosen(QString game_path); | 68 | void GameChosen(QString game_path); |
| 67 | void ShouldCancelWorker(); | 69 | void ShouldCancelWorker(); |
| 68 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); | 70 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); |
| 71 | void OpenTransferableShaderCacheRequested(u64 program_id); | ||
| 69 | void DumpRomFSRequested(u64 program_id, const std::string& game_path); | 72 | void DumpRomFSRequested(u64 program_id, const std::string& game_path); |
| 70 | void CopyTIDRequested(u64 program_id); | 73 | void CopyTIDRequested(u64 program_id); |
| 71 | void NavigateToGamedbEntryRequested(u64 program_id, | 74 | void NavigateToGamedbEntryRequested(u64 program_id, |
| @@ -85,6 +88,7 @@ private: | |||
| 85 | void RefreshGameDirectory(); | 88 | void RefreshGameDirectory(); |
| 86 | 89 | ||
| 87 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 90 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 91 | FileSys::ManualContentProvider* provider; | ||
| 88 | GameListSearchField* search_field; | 92 | GameListSearchField* search_field; |
| 89 | GMainWindow* main_window = nullptr; | 93 | GMainWindow* main_window = nullptr; |
| 90 | QVBoxLayout* layout = nullptr; | 94 | QVBoxLayout* layout = nullptr; |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 3db0e90da..2cf5c58a0 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -95,7 +95,7 @@ public: | |||
| 95 | if (row2.isEmpty()) | 95 | if (row2.isEmpty()) |
| 96 | return row1; | 96 | return row1; |
| 97 | 97 | ||
| 98 | return row1 + "\n " + row2; | 98 | return QString(row1 + "\n " + row2); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | return GameListItem::data(role); | 101 | return GameListItem::data(role); |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index b37710f59..8687e7c5a 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -12,12 +12,15 @@ | |||
| 12 | 12 | ||
| 13 | #include "common/common_paths.h" | 13 | #include "common/common_paths.h" |
| 14 | #include "common/file_util.h" | 14 | #include "common/file_util.h" |
| 15 | #include "core/core.h" | ||
| 16 | #include "core/file_sys/card_image.h" | ||
| 15 | #include "core/file_sys/content_archive.h" | 17 | #include "core/file_sys/content_archive.h" |
| 16 | #include "core/file_sys/control_metadata.h" | 18 | #include "core/file_sys/control_metadata.h" |
| 17 | #include "core/file_sys/mode.h" | 19 | #include "core/file_sys/mode.h" |
| 18 | #include "core/file_sys/nca_metadata.h" | 20 | #include "core/file_sys/nca_metadata.h" |
| 19 | #include "core/file_sys/patch_manager.h" | 21 | #include "core/file_sys/patch_manager.h" |
| 20 | #include "core/file_sys/registered_cache.h" | 22 | #include "core/file_sys/registered_cache.h" |
| 23 | #include "core/file_sys/submission_package.h" | ||
| 21 | #include "core/hle/service/filesystem/filesystem.h" | 24 | #include "core/hle/service/filesystem/filesystem.h" |
| 22 | #include "core/loader/loader.h" | 25 | #include "core/loader/loader.h" |
| 23 | #include "yuzu/compatibility_list.h" | 26 | #include "yuzu/compatibility_list.h" |
| @@ -119,20 +122,25 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri | |||
| 119 | } | 122 | } |
| 120 | } // Anonymous namespace | 123 | } // Anonymous namespace |
| 121 | 124 | ||
| 122 | GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, | 125 | GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, |
| 123 | const CompatibilityList& compatibility_list) | 126 | FileSys::ManualContentProvider* provider, QString dir_path, |
| 124 | : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), | 127 | bool deep_scan, const CompatibilityList& compatibility_list) |
| 128 | : vfs(std::move(vfs)), provider(provider), dir_path(std::move(dir_path)), deep_scan(deep_scan), | ||
| 125 | compatibility_list(compatibility_list) {} | 129 | compatibility_list(compatibility_list) {} |
| 126 | 130 | ||
| 127 | GameListWorker::~GameListWorker() = default; | 131 | GameListWorker::~GameListWorker() = default; |
| 128 | 132 | ||
| 129 | void GameListWorker::AddInstalledTitlesToGameList() { | 133 | void GameListWorker::AddTitlesToGameList() { |
| 130 | const auto cache = Service::FileSystem::GetUnionContents(); | 134 | const auto& cache = dynamic_cast<FileSys::ContentProviderUnion&>( |
| 131 | const auto installed_games = cache.ListEntriesFilter(FileSys::TitleType::Application, | 135 | Core::System::GetInstance().GetContentProvider()); |
| 132 | FileSys::ContentRecordType::Program); | 136 | const auto installed_games = cache.ListEntriesFilterOrigin( |
| 137 | std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program); | ||
| 133 | 138 | ||
| 134 | for (const auto& game : installed_games) { | 139 | for (const auto& [slot, game] : installed_games) { |
| 135 | const auto file = cache.GetEntryUnparsed(game); | 140 | if (slot == FileSys::ContentProviderUnionSlot::FrontendManual) |
| 141 | continue; | ||
| 142 | |||
| 143 | const auto file = cache.GetEntryUnparsed(game.title_id, game.type); | ||
| 136 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); | 144 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); |
| 137 | if (!loader) | 145 | if (!loader) |
| 138 | continue; | 146 | continue; |
| @@ -150,45 +158,13 @@ void GameListWorker::AddInstalledTitlesToGameList() { | |||
| 150 | emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, | 158 | emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, |
| 151 | compatibility_list, patch)); | 159 | compatibility_list, patch)); |
| 152 | } | 160 | } |
| 153 | |||
| 154 | const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application, | ||
| 155 | FileSys::ContentRecordType::Control); | ||
| 156 | |||
| 157 | for (const auto& entry : control_data) { | ||
| 158 | auto nca = cache.GetEntry(entry); | ||
| 159 | if (nca != nullptr) { | ||
| 160 | nca_control_map.insert_or_assign(entry.title_id, std::move(nca)); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | 161 | } |
| 164 | 162 | ||
| 165 | void GameListWorker::FillControlMap(const std::string& dir_path) { | 163 | void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, |
| 166 | const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, | 164 | unsigned int recursion) { |
| 167 | const std::string& virtual_name) -> bool { | 165 | const auto callback = [this, target, recursion](u64* num_entries_out, |
| 168 | if (stop_processing) { | 166 | const std::string& directory, |
| 169 | // Breaks the callback loop | 167 | const std::string& virtual_name) -> bool { |
| 170 | return false; | ||
| 171 | } | ||
| 172 | |||
| 173 | const std::string physical_name = directory + DIR_SEP + virtual_name; | ||
| 174 | const QFileInfo file_info(QString::fromStdString(physical_name)); | ||
| 175 | if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) { | ||
| 176 | auto nca = | ||
| 177 | std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | ||
| 178 | if (nca->GetType() == FileSys::NCAContentType::Control) { | ||
| 179 | const u64 title_id = nca->GetTitleId(); | ||
| 180 | nca_control_map.insert_or_assign(title_id, std::move(nca)); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | return true; | ||
| 184 | }; | ||
| 185 | |||
| 186 | FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); | ||
| 187 | } | ||
| 188 | |||
| 189 | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | ||
| 190 | const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, | ||
| 191 | const std::string& virtual_name) -> bool { | ||
| 192 | if (stop_processing) { | 168 | if (stop_processing) { |
| 193 | // Breaks the callback loop. | 169 | // Breaks the callback loop. |
| 194 | return false; | 170 | return false; |
| @@ -198,7 +174,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 198 | const bool is_dir = FileUtil::IsDirectory(physical_name); | 174 | const bool is_dir = FileUtil::IsDirectory(physical_name); |
| 199 | if (!is_dir && | 175 | if (!is_dir && |
| 200 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | 176 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { |
| 201 | auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | 177 | const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); |
| 178 | auto loader = Loader::GetLoader(file); | ||
| 202 | if (!loader) { | 179 | if (!loader) { |
| 203 | return true; | 180 | return true; |
| 204 | } | 181 | } |
| @@ -209,31 +186,42 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 209 | return true; | 186 | return true; |
| 210 | } | 187 | } |
| 211 | 188 | ||
| 212 | std::vector<u8> icon; | ||
| 213 | const auto res1 = loader->ReadIcon(icon); | ||
| 214 | |||
| 215 | u64 program_id = 0; | 189 | u64 program_id = 0; |
| 216 | const auto res2 = loader->ReadProgramId(program_id); | 190 | const auto res2 = loader->ReadProgramId(program_id); |
| 217 | 191 | ||
| 218 | std::string name = " "; | 192 | if (target == ScanTarget::FillManualContentProvider) { |
| 219 | const auto res3 = loader->ReadTitle(name); | 193 | if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { |
| 194 | provider->AddEntry(FileSys::TitleType::Application, | ||
| 195 | FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), | ||
| 196 | program_id, file); | ||
| 197 | } else if (res2 == Loader::ResultStatus::Success && | ||
| 198 | (file_type == Loader::FileType::XCI || | ||
| 199 | file_type == Loader::FileType::NSP)) { | ||
| 200 | const auto nsp = file_type == Loader::FileType::NSP | ||
| 201 | ? std::make_shared<FileSys::NSP>(file) | ||
| 202 | : FileSys::XCI{file}.GetSecurePartitionNSP(); | ||
| 203 | for (const auto& title : nsp->GetNCAs()) { | ||
| 204 | for (const auto& entry : title.second) { | ||
| 205 | provider->AddEntry(entry.first.first, entry.first.second, title.first, | ||
| 206 | entry.second->GetBaseFile()); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
| 210 | } else { | ||
| 211 | std::vector<u8> icon; | ||
| 212 | const auto res1 = loader->ReadIcon(icon); | ||
| 220 | 213 | ||
| 221 | const FileSys::PatchManager patch{program_id}; | 214 | std::string name = " "; |
| 215 | const auto res3 = loader->ReadTitle(name); | ||
| 222 | 216 | ||
| 223 | if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && | 217 | const FileSys::PatchManager patch{program_id}; |
| 224 | res2 == Loader::ResultStatus::Success) { | ||
| 225 | // Use from metadata pool. | ||
| 226 | if (nca_control_map.find(program_id) != nca_control_map.end()) { | ||
| 227 | const auto& nca = nca_control_map[program_id]; | ||
| 228 | GetMetadataFromControlNCA(patch, *nca, icon, name); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | 218 | ||
| 232 | emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, | 219 | emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, |
| 233 | compatibility_list, patch)); | 220 | compatibility_list, patch)); |
| 221 | } | ||
| 234 | } else if (is_dir && recursion > 0) { | 222 | } else if (is_dir && recursion > 0) { |
| 235 | watch_list.append(QString::fromStdString(physical_name)); | 223 | watch_list.append(QString::fromStdString(physical_name)); |
| 236 | AddFstEntriesToGameList(physical_name, recursion - 1); | 224 | ScanFileSystem(target, physical_name, recursion - 1); |
| 237 | } | 225 | } |
| 238 | 226 | ||
| 239 | return true; | 227 | return true; |
| @@ -245,10 +233,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 245 | void GameListWorker::run() { | 233 | void GameListWorker::run() { |
| 246 | stop_processing = false; | 234 | stop_processing = false; |
| 247 | watch_list.append(dir_path); | 235 | watch_list.append(dir_path); |
| 248 | FillControlMap(dir_path.toStdString()); | 236 | provider->ClearAllEntries(); |
| 249 | AddInstalledTitlesToGameList(); | 237 | ScanFileSystem(ScanTarget::FillManualContentProvider, dir_path.toStdString(), |
| 250 | AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); | 238 | deep_scan ? 256 : 0); |
| 251 | nca_control_map.clear(); | 239 | AddTitlesToGameList(); |
| 240 | ScanFileSystem(ScanTarget::PopulateGameList, dir_path.toStdString(), deep_scan ? 256 : 0); | ||
| 252 | emit Finished(watch_list); | 241 | emit Finished(watch_list); |
| 253 | } | 242 | } |
| 254 | 243 | ||
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 0e42d0bde..7c3074af9 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h | |||
| @@ -33,7 +33,8 @@ class GameListWorker : public QObject, public QRunnable { | |||
| 33 | Q_OBJECT | 33 | Q_OBJECT |
| 34 | 34 | ||
| 35 | public: | 35 | public: |
| 36 | GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, | 36 | GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, |
| 37 | FileSys::ManualContentProvider* provider, QString dir_path, bool deep_scan, | ||
| 37 | const CompatibilityList& compatibility_list); | 38 | const CompatibilityList& compatibility_list); |
| 38 | ~GameListWorker() override; | 39 | ~GameListWorker() override; |
| 39 | 40 | ||
| @@ -58,12 +59,17 @@ signals: | |||
| 58 | void Finished(QStringList watch_list); | 59 | void Finished(QStringList watch_list); |
| 59 | 60 | ||
| 60 | private: | 61 | private: |
| 61 | void AddInstalledTitlesToGameList(); | 62 | void AddTitlesToGameList(); |
| 62 | void FillControlMap(const std::string& dir_path); | 63 | |
| 63 | void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); | 64 | enum class ScanTarget { |
| 65 | FillManualContentProvider, | ||
| 66 | PopulateGameList, | ||
| 67 | }; | ||
| 68 | |||
| 69 | void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion = 0); | ||
| 64 | 70 | ||
| 65 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 71 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 66 | std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map; | 72 | FileSys::ManualContentProvider* provider; |
| 67 | QStringList watch_list; | 73 | QStringList watch_list; |
| 68 | QString dir_path; | 74 | QString dir_path; |
| 69 | bool deep_scan; | 75 | bool deep_scan; |
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index dce399774..4582e7f21 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp | |||
| @@ -2,7 +2,6 @@ | |||
| 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 <map> | ||
| 6 | #include <QKeySequence> | 5 | #include <QKeySequence> |
| 7 | #include <QShortcut> | 6 | #include <QShortcut> |
| 8 | #include <QTreeWidgetItem> | 7 | #include <QTreeWidgetItem> |
| @@ -13,47 +12,32 @@ | |||
| 13 | HotkeyRegistry::HotkeyRegistry() = default; | 12 | HotkeyRegistry::HotkeyRegistry() = default; |
| 14 | HotkeyRegistry::~HotkeyRegistry() = default; | 13 | HotkeyRegistry::~HotkeyRegistry() = default; |
| 15 | 14 | ||
| 16 | void HotkeyRegistry::LoadHotkeys() { | ||
| 17 | // Make sure NOT to use a reference here because it would become invalid once we call | ||
| 18 | // beginGroup() | ||
| 19 | for (auto shortcut : UISettings::values.shortcuts) { | ||
| 20 | const QStringList cat = shortcut.first.split('/'); | ||
| 21 | Q_ASSERT(cat.size() >= 2); | ||
| 22 | |||
| 23 | // RegisterHotkey assigns default keybindings, so use old values as default parameters | ||
| 24 | Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; | ||
| 25 | if (!shortcut.second.first.isEmpty()) { | ||
| 26 | hk.keyseq = QKeySequence::fromString(shortcut.second.first); | ||
| 27 | hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second); | ||
| 28 | } | ||
| 29 | if (hk.shortcut) | ||
| 30 | hk.shortcut->setKey(hk.keyseq); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | void HotkeyRegistry::SaveHotkeys() { | 15 | void HotkeyRegistry::SaveHotkeys() { |
| 35 | UISettings::values.shortcuts.clear(); | 16 | UISettings::values.shortcuts.clear(); |
| 36 | for (const auto& group : hotkey_groups) { | 17 | for (const auto& group : hotkey_groups) { |
| 37 | for (const auto& hotkey : group.second) { | 18 | for (const auto& hotkey : group.second) { |
| 38 | UISettings::values.shortcuts.emplace_back( | 19 | UISettings::values.shortcuts.push_back( |
| 39 | UISettings::Shortcut(group.first + '/' + hotkey.first, | 20 | {hotkey.first, group.first, |
| 40 | UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), | 21 | UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), |
| 41 | hotkey.second.context))); | 22 | hotkey.second.context)}); |
| 42 | } | 23 | } |
| 43 | } | 24 | } |
| 44 | } | 25 | } |
| 45 | 26 | ||
| 46 | void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action, | 27 | void HotkeyRegistry::LoadHotkeys() { |
| 47 | const QKeySequence& default_keyseq, | 28 | // Make sure NOT to use a reference here because it would become invalid once we call |
| 48 | Qt::ShortcutContext default_context) { | 29 | // beginGroup() |
| 49 | auto& hotkey_group = hotkey_groups[group]; | 30 | for (auto shortcut : UISettings::values.shortcuts) { |
| 50 | if (hotkey_group.find(action) != hotkey_group.end()) { | 31 | Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; |
| 51 | return; | 32 | if (!shortcut.shortcut.first.isEmpty()) { |
| 33 | hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText); | ||
| 34 | hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second); | ||
| 35 | } | ||
| 36 | if (hk.shortcut) { | ||
| 37 | hk.shortcut->disconnect(); | ||
| 38 | hk.shortcut->setKey(hk.keyseq); | ||
| 39 | } | ||
| 52 | } | 40 | } |
| 53 | |||
| 54 | auto& hotkey_action = hotkey_groups[group][action]; | ||
| 55 | hotkey_action.keyseq = default_keyseq; | ||
| 56 | hotkey_action.context = default_context; | ||
| 57 | } | 41 | } |
| 58 | 42 | ||
| 59 | QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { | 43 | QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { |
| @@ -65,24 +49,11 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action | |||
| 65 | return hk.shortcut; | 49 | return hk.shortcut; |
| 66 | } | 50 | } |
| 67 | 51 | ||
| 68 | GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { | 52 | QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { |
| 69 | ui.setupUi(this); | 53 | return hotkey_groups[group][action].keyseq; |
| 70 | } | 54 | } |
| 71 | 55 | ||
| 72 | void GHotkeysDialog::Populate(const HotkeyRegistry& registry) { | 56 | Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, |
| 73 | for (const auto& group : registry.hotkey_groups) { | 57 | const QString& action) { |
| 74 | QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); | 58 | return hotkey_groups[group][action].context; |
| 75 | for (const auto& hotkey : group.second) { | ||
| 76 | QStringList columns; | ||
| 77 | columns << hotkey.first << hotkey.second.keyseq.toString(); | ||
| 78 | QTreeWidgetItem* item = new QTreeWidgetItem(columns); | ||
| 79 | toplevel_item->addChild(item); | ||
| 80 | } | ||
| 81 | ui.treeWidget->addTopLevelItem(toplevel_item); | ||
| 82 | } | ||
| 83 | // TODO: Make context configurable as well (hiding the column for now) | ||
| 84 | ui.treeWidget->setColumnCount(2); | ||
| 85 | |||
| 86 | ui.treeWidget->resizeColumnToContents(0); | ||
| 87 | ui.treeWidget->resizeColumnToContents(1); | ||
| 88 | } | 59 | } |
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index f38e6c002..4f526dc7e 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | 7 | #include <map> |
| 8 | #include "ui_hotkeys.h" | ||
| 9 | 8 | ||
| 10 | class QDialog; | 9 | class QDialog; |
| 11 | class QKeySequence; | 10 | class QKeySequence; |
| @@ -14,7 +13,7 @@ class QShortcut; | |||
| 14 | 13 | ||
| 15 | class HotkeyRegistry final { | 14 | class HotkeyRegistry final { |
| 16 | public: | 15 | public: |
| 17 | friend class GHotkeysDialog; | 16 | friend class ConfigureHotkeys; |
| 18 | 17 | ||
| 19 | explicit HotkeyRegistry(); | 18 | explicit HotkeyRegistry(); |
| 20 | ~HotkeyRegistry(); | 19 | ~HotkeyRegistry(); |
| @@ -49,22 +48,27 @@ public: | |||
| 49 | QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); | 48 | QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); |
| 50 | 49 | ||
| 51 | /** | 50 | /** |
| 52 | * Register a hotkey. | 51 | * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut. |
| 53 | * | 52 | * |
| 54 | * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") | 53 | * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). |
| 55 | * @param action Name of the action (e.g. "Start Emulation", "Load Image") | 54 | * @param action Name of the action (e.g. "Start Emulation", "Load Image"). |
| 56 | * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the | 55 | */ |
| 57 | * settings file before | 56 | QKeySequence GetKeySequence(const QString& group, const QString& action); |
| 58 | * @param default_context Default context to assign if the hotkey wasn't present in the settings | 57 | |
| 59 | * file before | 58 | /** |
| 60 | * @warning Both the group and action strings will be displayed in the hotkey settings dialog | 59 | * Returns a Qt::ShortcutContext object who can be connected to other |
| 60 | * QAction::setShortcutContext. | ||
| 61 | * | ||
| 62 | * @param group General group this shortcut context belongs to (e.g. "Main Window", | ||
| 63 | * "Debugger"). | ||
| 64 | * @param action Name of the action (e.g. "Start Emulation", "Load Image"). | ||
| 61 | */ | 65 | */ |
| 62 | void RegisterHotkey(const QString& group, const QString& action, | 66 | Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); |
| 63 | const QKeySequence& default_keyseq = {}, | ||
| 64 | Qt::ShortcutContext default_context = Qt::WindowShortcut); | ||
| 65 | 67 | ||
| 66 | private: | 68 | private: |
| 67 | struct Hotkey { | 69 | struct Hotkey { |
| 70 | Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} | ||
| 71 | |||
| 68 | QKeySequence keyseq; | 72 | QKeySequence keyseq; |
| 69 | QShortcut* shortcut = nullptr; | 73 | QShortcut* shortcut = nullptr; |
| 70 | Qt::ShortcutContext context = Qt::WindowShortcut; | 74 | Qt::ShortcutContext context = Qt::WindowShortcut; |
| @@ -75,15 +79,3 @@ private: | |||
| 75 | 79 | ||
| 76 | HotkeyGroupMap hotkey_groups; | 80 | HotkeyGroupMap hotkey_groups; |
| 77 | }; | 81 | }; |
| 78 | |||
| 79 | class GHotkeysDialog : public QWidget { | ||
| 80 | Q_OBJECT | ||
| 81 | |||
| 82 | public: | ||
| 83 | explicit GHotkeysDialog(QWidget* parent = nullptr); | ||
| 84 | |||
| 85 | void Populate(const HotkeyRegistry& registry); | ||
| 86 | |||
| 87 | private: | ||
| 88 | Ui::hotkeys ui; | ||
| 89 | }; | ||
diff --git a/src/yuzu/hotkeys.ui b/src/yuzu/hotkeys.ui deleted file mode 100644 index 050fe064e..000000000 --- a/src/yuzu/hotkeys.ui +++ /dev/null | |||
| @@ -1,46 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>hotkeys</class> | ||
| 4 | <widget class="QWidget" name="hotkeys"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>363</width> | ||
| 10 | <height>388</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Hotkey Settings</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 17 | <item> | ||
| 18 | <widget class="QTreeWidget" name="treeWidget"> | ||
| 19 | <property name="selectionBehavior"> | ||
| 20 | <enum>QAbstractItemView::SelectItems</enum> | ||
| 21 | </property> | ||
| 22 | <property name="headerHidden"> | ||
| 23 | <bool>false</bool> | ||
| 24 | </property> | ||
| 25 | <column> | ||
| 26 | <property name="text"> | ||
| 27 | <string>Action</string> | ||
| 28 | </property> | ||
| 29 | </column> | ||
| 30 | <column> | ||
| 31 | <property name="text"> | ||
| 32 | <string>Hotkey</string> | ||
| 33 | </property> | ||
| 34 | </column> | ||
| 35 | <column> | ||
| 36 | <property name="text"> | ||
| 37 | <string>Context</string> | ||
| 38 | </property> | ||
| 39 | </column> | ||
| 40 | </widget> | ||
| 41 | </item> | ||
| 42 | </layout> | ||
| 43 | </widget> | ||
| 44 | <resources/> | ||
| 45 | <connections/> | ||
| 46 | </ui> | ||
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index 86f6d0165..4e2d988cd 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp | |||
| @@ -192,7 +192,12 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size | |||
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | // update labels and progress bar | 194 | // update labels and progress bar |
| 195 | ui->stage->setText(stage_translations[stage].arg(value).arg(total)); | 195 | if (stage == VideoCore::LoadCallbackStage::Decompile || |
| 196 | stage == VideoCore::LoadCallbackStage::Build) { | ||
| 197 | ui->stage->setText(stage_translations[stage].arg(value).arg(total)); | ||
| 198 | } else { | ||
| 199 | ui->stage->setText(stage_translations[stage]); | ||
| 200 | } | ||
| 196 | ui->value->setText(estimate); | 201 | ui->value->setText(estimate); |
| 197 | ui->progress_bar->setValue(static_cast<int>(value)); | 202 | ui->progress_bar->setValue(static_cast<int>(value)); |
| 198 | previous_time = now; | 203 | previous_time = now; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 41ba3c4c6..bdee44b04 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -37,14 +37,20 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 37 | #include <glad/glad.h> | 37 | #include <glad/glad.h> |
| 38 | 38 | ||
| 39 | #define QT_NO_OPENGL | 39 | #define QT_NO_OPENGL |
| 40 | #include <QClipboard> | ||
| 41 | #include <QDesktopServices> | ||
| 40 | #include <QDesktopWidget> | 42 | #include <QDesktopWidget> |
| 41 | #include <QDialogButtonBox> | 43 | #include <QDialogButtonBox> |
| 42 | #include <QFile> | 44 | #include <QFile> |
| 43 | #include <QFileDialog> | 45 | #include <QFileDialog> |
| 46 | #include <QInputDialog> | ||
| 44 | #include <QMessageBox> | 47 | #include <QMessageBox> |
| 48 | #include <QProgressBar> | ||
| 49 | #include <QProgressDialog> | ||
| 50 | #include <QShortcut> | ||
| 51 | #include <QStatusBar> | ||
| 45 | #include <QtConcurrent/QtConcurrent> | 52 | #include <QtConcurrent/QtConcurrent> |
| 46 | #include <QtGui> | 53 | |
| 47 | #include <QtWidgets> | ||
| 48 | #include <fmt/format.h> | 54 | #include <fmt/format.h> |
| 49 | #include "common/common_paths.h" | 55 | #include "common/common_paths.h" |
| 50 | #include "common/detached_tasks.h" | 56 | #include "common/detached_tasks.h" |
| @@ -55,11 +61,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 55 | #include "common/microprofile.h" | 61 | #include "common/microprofile.h" |
| 56 | #include "common/scm_rev.h" | 62 | #include "common/scm_rev.h" |
| 57 | #include "common/scope_exit.h" | 63 | #include "common/scope_exit.h" |
| 58 | #include "common/string_util.h" | ||
| 59 | #include "common/telemetry.h" | 64 | #include "common/telemetry.h" |
| 60 | #include "core/core.h" | 65 | #include "core/core.h" |
| 61 | #include "core/crypto/key_manager.h" | 66 | #include "core/crypto/key_manager.h" |
| 62 | #include "core/file_sys/bis_factory.h" | ||
| 63 | #include "core/file_sys/card_image.h" | 67 | #include "core/file_sys/card_image.h" |
| 64 | #include "core/file_sys/content_archive.h" | 68 | #include "core/file_sys/content_archive.h" |
| 65 | #include "core/file_sys/control_metadata.h" | 69 | #include "core/file_sys/control_metadata.h" |
| @@ -71,7 +75,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 71 | #include "core/frontend/applets/software_keyboard.h" | 75 | #include "core/frontend/applets/software_keyboard.h" |
| 72 | #include "core/hle/kernel/process.h" | 76 | #include "core/hle/kernel/process.h" |
| 73 | #include "core/hle/service/filesystem/filesystem.h" | 77 | #include "core/hle/service/filesystem/filesystem.h" |
| 74 | #include "core/hle/service/filesystem/fsp_ldr.h" | ||
| 75 | #include "core/hle/service/nfp/nfp.h" | 78 | #include "core/hle/service/nfp/nfp.h" |
| 76 | #include "core/hle/service/sm/sm.h" | 79 | #include "core/hle/service/sm/sm.h" |
| 77 | #include "core/loader/loader.h" | 80 | #include "core/loader/loader.h" |
| @@ -87,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 87 | #include "yuzu/configuration/configure_dialog.h" | 90 | #include "yuzu/configuration/configure_dialog.h" |
| 88 | #include "yuzu/debugger/console.h" | 91 | #include "yuzu/debugger/console.h" |
| 89 | #include "yuzu/debugger/graphics/graphics_breakpoints.h" | 92 | #include "yuzu/debugger/graphics/graphics_breakpoints.h" |
| 90 | #include "yuzu/debugger/graphics/graphics_surface.h" | ||
| 91 | #include "yuzu/debugger/profiler.h" | 93 | #include "yuzu/debugger/profiler.h" |
| 92 | #include "yuzu/debugger/wait_tree.h" | 94 | #include "yuzu/debugger/wait_tree.h" |
| 93 | #include "yuzu/discord.h" | 95 | #include "yuzu/discord.h" |
| @@ -168,7 +170,8 @@ static void InitializeLogging() { | |||
| 168 | 170 | ||
| 169 | GMainWindow::GMainWindow() | 171 | GMainWindow::GMainWindow() |
| 170 | : config(new Config()), emu_thread(nullptr), | 172 | : config(new Config()), emu_thread(nullptr), |
| 171 | vfs(std::make_shared<FileSys::RealVfsFilesystem>()) { | 173 | vfs(std::make_shared<FileSys::RealVfsFilesystem>()), |
| 174 | provider(std::make_unique<FileSys::ManualContentProvider>()) { | ||
| 172 | InitializeLogging(); | 175 | InitializeLogging(); |
| 173 | 176 | ||
| 174 | debug_context = Tegra::DebugContext::Construct(); | 177 | debug_context = Tegra::DebugContext::Construct(); |
| @@ -200,13 +203,18 @@ GMainWindow::GMainWindow() | |||
| 200 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); | 203 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); |
| 201 | show(); | 204 | show(); |
| 202 | 205 | ||
| 206 | Core::System::GetInstance().SetContentProvider( | ||
| 207 | std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 208 | Core::System::GetInstance().RegisterContentProvider( | ||
| 209 | FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); | ||
| 210 | Service::FileSystem::CreateFactories(*vfs); | ||
| 211 | |||
| 203 | // Gen keys if necessary | 212 | // Gen keys if necessary |
| 204 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); | 213 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); |
| 205 | 214 | ||
| 206 | // Necessary to load titles from nand in gamelist. | ||
| 207 | Service::FileSystem::CreateFactories(*vfs); | ||
| 208 | game_list->LoadCompatibilityList(); | 215 | game_list->LoadCompatibilityList(); |
| 209 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 216 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 217 | UISettings::values.game_directory_deepscan); | ||
| 210 | 218 | ||
| 211 | // Show one-time "callout" messages to the user | 219 | // Show one-time "callout" messages to the user |
| 212 | ShowTelemetryCallout(); | 220 | ShowTelemetryCallout(); |
| @@ -416,7 +424,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 416 | render_window = new GRenderWindow(this, emu_thread.get()); | 424 | render_window = new GRenderWindow(this, emu_thread.get()); |
| 417 | render_window->hide(); | 425 | render_window->hide(); |
| 418 | 426 | ||
| 419 | game_list = new GameList(vfs, this); | 427 | game_list = new GameList(vfs, provider.get(), this); |
| 420 | ui.horizontalLayout->addWidget(game_list); | 428 | ui.horizontalLayout->addWidget(game_list); |
| 421 | 429 | ||
| 422 | loading_screen = new LoadingScreen(this); | 430 | loading_screen = new LoadingScreen(this); |
| @@ -475,11 +483,6 @@ void GMainWindow::InitializeDebugWidgets() { | |||
| 475 | graphicsBreakpointsWidget->hide(); | 483 | graphicsBreakpointsWidget->hide(); |
| 476 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | 484 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); |
| 477 | 485 | ||
| 478 | graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this); | ||
| 479 | addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget); | ||
| 480 | graphicsSurfaceWidget->hide(); | ||
| 481 | debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction()); | ||
| 482 | |||
| 483 | waitTreeWidget = new WaitTreeWidget(this); | 486 | waitTreeWidget = new WaitTreeWidget(this); |
| 484 | addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); | 487 | addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); |
| 485 | waitTreeWidget->hide(); | 488 | waitTreeWidget->hide(); |
| @@ -511,33 +514,34 @@ void GMainWindow::InitializeRecentFileMenuActions() { | |||
| 511 | } | 514 | } |
| 512 | 515 | ||
| 513 | void GMainWindow::InitializeHotkeys() { | 516 | void GMainWindow::InitializeHotkeys() { |
| 514 | hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open); | ||
| 515 | hotkey_registry.RegisterHotkey("Main Window", "Start Emulation"); | ||
| 516 | hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); | ||
| 517 | hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5)); | ||
| 518 | hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); | ||
| 519 | hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), | ||
| 520 | Qt::ApplicationShortcut); | ||
| 521 | hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"), | ||
| 522 | Qt::ApplicationShortcut); | ||
| 523 | hotkey_registry.RegisterHotkey("Main Window", "Increase Speed Limit", QKeySequence("+"), | ||
| 524 | Qt::ApplicationShortcut); | ||
| 525 | hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), | ||
| 526 | Qt::ApplicationShortcut); | ||
| 527 | hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2), | ||
| 528 | Qt::ApplicationShortcut); | ||
| 529 | hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot", | ||
| 530 | QKeySequence(QKeySequence::Print)); | ||
| 531 | hotkey_registry.RegisterHotkey("Main Window", "Change Docked Mode", QKeySequence(Qt::Key_F10)); | ||
| 532 | |||
| 533 | hotkey_registry.LoadHotkeys(); | 517 | hotkey_registry.LoadHotkeys(); |
| 534 | 518 | ||
| 519 | ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File")); | ||
| 520 | ui.action_Load_File->setShortcutContext( | ||
| 521 | hotkey_registry.GetShortcutContext("Main Window", "Load File")); | ||
| 522 | |||
| 523 | ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit yuzu")); | ||
| 524 | ui.action_Exit->setShortcutContext( | ||
| 525 | hotkey_registry.GetShortcutContext("Main Window", "Exit yuzu")); | ||
| 526 | |||
| 527 | ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation")); | ||
| 528 | ui.action_Stop->setShortcutContext( | ||
| 529 | hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation")); | ||
| 530 | |||
| 531 | ui.action_Show_Filter_Bar->setShortcut( | ||
| 532 | hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar")); | ||
| 533 | ui.action_Show_Filter_Bar->setShortcutContext( | ||
| 534 | hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar")); | ||
| 535 | |||
| 536 | ui.action_Show_Status_Bar->setShortcut( | ||
| 537 | hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar")); | ||
| 538 | ui.action_Show_Status_Bar->setShortcutContext( | ||
| 539 | hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar")); | ||
| 540 | |||
| 535 | connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, | 541 | connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, |
| 536 | this, &GMainWindow::OnMenuLoadFile); | 542 | this, &GMainWindow::OnMenuLoadFile); |
| 537 | connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this), | 543 | connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this), |
| 538 | &QShortcut::activated, this, &GMainWindow::OnStartGame); | 544 | &QShortcut::activated, this, [&] { |
| 539 | connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, | ||
| 540 | this, [&] { | ||
| 541 | if (emulation_running) { | 545 | if (emulation_running) { |
| 542 | if (emu_thread->IsRunning()) { | 546 | if (emu_thread->IsRunning()) { |
| 543 | OnPauseGame(); | 547 | OnPauseGame(); |
| @@ -546,8 +550,8 @@ void GMainWindow::InitializeHotkeys() { | |||
| 546 | } | 550 | } |
| 547 | } | 551 | } |
| 548 | }); | 552 | }); |
| 549 | connect(hotkey_registry.GetHotkey("Main Window", "Restart", this), &QShortcut::activated, this, | 553 | connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this), |
| 550 | [this] { | 554 | &QShortcut::activated, this, [this] { |
| 551 | if (!Core::System::GetInstance().IsPoweredOn()) | 555 | if (!Core::System::GetInstance().IsPoweredOn()) |
| 552 | return; | 556 | return; |
| 553 | BootGame(QString(game_path)); | 557 | BootGame(QString(game_path)); |
| @@ -648,6 +652,8 @@ void GMainWindow::RestoreUIState() { | |||
| 648 | void GMainWindow::ConnectWidgetEvents() { | 652 | void GMainWindow::ConnectWidgetEvents() { |
| 649 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); | 653 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); |
| 650 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); | 654 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); |
| 655 | connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, | ||
| 656 | &GMainWindow::OnTransferableShaderCacheOpenFile); | ||
| 651 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); | 657 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); |
| 652 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | 658 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); |
| 653 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 659 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| @@ -692,7 +698,6 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 692 | &GMainWindow::ToggleWindowMode); | 698 | &GMainWindow::ToggleWindowMode); |
| 693 | connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this, | 699 | connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this, |
| 694 | &GMainWindow::OnDisplayTitleBars); | 700 | &GMainWindow::OnDisplayTitleBars); |
| 695 | ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F")); | ||
| 696 | connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); | 701 | connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); |
| 697 | connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); | 702 | connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); |
| 698 | 703 | ||
| @@ -1082,6 +1087,39 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1082 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); | 1087 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); |
| 1083 | } | 1088 | } |
| 1084 | 1089 | ||
| 1090 | void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { | ||
| 1091 | ASSERT(program_id != 0); | ||
| 1092 | |||
| 1093 | const QString tranferable_shader_cache_folder_path = | ||
| 1094 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" + | ||
| 1095 | DIR_SEP + "transferable"; | ||
| 1096 | |||
| 1097 | const QString transferable_shader_cache_file_path = | ||
| 1098 | tranferable_shader_cache_folder_path + DIR_SEP + | ||
| 1099 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); | ||
| 1100 | |||
| 1101 | if (!QFile::exists(transferable_shader_cache_file_path)) { | ||
| 1102 | QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), | ||
| 1103 | tr("A shader cache for this title does not exist.")); | ||
| 1104 | return; | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | // Windows supports opening a folder with selecting a specified file in explorer. On every other | ||
| 1108 | // OS we just open the transferable shader cache folder without preselecting the transferable | ||
| 1109 | // shader cache file for the selected game. | ||
| 1110 | #if defined(Q_OS_WIN) | ||
| 1111 | const QString explorer = QStringLiteral("explorer"); | ||
| 1112 | QStringList param; | ||
| 1113 | if (!QFileInfo(transferable_shader_cache_file_path).isDir()) { | ||
| 1114 | param << QStringLiteral("/select,"); | ||
| 1115 | } | ||
| 1116 | param << QDir::toNativeSeparators(transferable_shader_cache_file_path); | ||
| 1117 | QProcess::startDetached(explorer, param); | ||
| 1118 | #else | ||
| 1119 | QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); | ||
| 1120 | #endif | ||
| 1121 | } | ||
| 1122 | |||
| 1085 | static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { | 1123 | static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { |
| 1086 | std::size_t out = 0; | 1124 | std::size_t out = 0; |
| 1087 | 1125 | ||
| @@ -1141,7 +1179,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 1141 | return; | 1179 | return; |
| 1142 | } | 1180 | } |
| 1143 | 1181 | ||
| 1144 | const auto installed = Service::FileSystem::GetUnionContents(); | 1182 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 1145 | const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); | 1183 | const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); |
| 1146 | 1184 | ||
| 1147 | if (!romfs_title_id) { | 1185 | if (!romfs_title_id) { |
| @@ -1241,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | |||
| 1241 | 1279 | ||
| 1242 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | 1280 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); |
| 1243 | if (reload) { | 1281 | if (reload) { |
| 1244 | game_list->PopulateAsync(UISettings::values.gamedir, | 1282 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1245 | UISettings::values.gamedir_deepscan); | 1283 | UISettings::values.game_directory_deepscan); |
| 1246 | } | 1284 | } |
| 1247 | 1285 | ||
| 1248 | config->Save(); | 1286 | config->Save(); |
| @@ -1330,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1330 | const auto success = [this]() { | 1368 | const auto success = [this]() { |
| 1331 | QMessageBox::information(this, tr("Successfully Installed"), | 1369 | QMessageBox::information(this, tr("Successfully Installed"), |
| 1332 | tr("The file was successfully installed.")); | 1370 | tr("The file was successfully installed.")); |
| 1333 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1371 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1372 | UISettings::values.game_directory_deepscan); | ||
| 1334 | }; | 1373 | }; |
| 1335 | 1374 | ||
| 1336 | const auto failed = [this]() { | 1375 | const auto failed = [this]() { |
| @@ -1457,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1457 | void GMainWindow::OnMenuSelectGameListRoot() { | 1496 | void GMainWindow::OnMenuSelectGameListRoot() { |
| 1458 | QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); | 1497 | QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); |
| 1459 | if (!dir_path.isEmpty()) { | 1498 | if (!dir_path.isEmpty()) { |
| 1460 | UISettings::values.gamedir = dir_path; | 1499 | UISettings::values.game_directory_path = dir_path; |
| 1461 | game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); | 1500 | game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan); |
| 1462 | } | 1501 | } |
| 1463 | } | 1502 | } |
| 1464 | 1503 | ||
| @@ -1480,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) | |||
| 1480 | : FileUtil::UserPath::NANDDir, | 1519 | : FileUtil::UserPath::NANDDir, |
| 1481 | dir_path.toStdString()); | 1520 | dir_path.toStdString()); |
| 1482 | Service::FileSystem::CreateFactories(*vfs); | 1521 | Service::FileSystem::CreateFactories(*vfs); |
| 1483 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1522 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1523 | UISettings::values.game_directory_deepscan); | ||
| 1484 | } | 1524 | } |
| 1485 | } | 1525 | } |
| 1486 | 1526 | ||
| @@ -1624,6 +1664,7 @@ void GMainWindow::OnConfigure() { | |||
| 1624 | auto result = configureDialog.exec(); | 1664 | auto result = configureDialog.exec(); |
| 1625 | if (result == QDialog::Accepted) { | 1665 | if (result == QDialog::Accepted) { |
| 1626 | configureDialog.applyConfiguration(); | 1666 | configureDialog.applyConfiguration(); |
| 1667 | InitializeHotkeys(); | ||
| 1627 | if (UISettings::values.theme != old_theme) | 1668 | if (UISettings::values.theme != old_theme) |
| 1628 | UpdateUITheme(); | 1669 | UpdateUITheme(); |
| 1629 | if (UISettings::values.enable_discord_presence != old_discord_presence) | 1670 | if (UISettings::values.enable_discord_presence != old_discord_presence) |
| @@ -1631,8 +1672,8 @@ void GMainWindow::OnConfigure() { | |||
| 1631 | 1672 | ||
| 1632 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | 1673 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); |
| 1633 | if (reload) { | 1674 | if (reload) { |
| 1634 | game_list->PopulateAsync(UISettings::values.gamedir, | 1675 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1635 | UISettings::values.gamedir_deepscan); | 1676 | UISettings::values.game_directory_deepscan); |
| 1636 | } | 1677 | } |
| 1637 | 1678 | ||
| 1638 | config->Save(); | 1679 | config->Save(); |
| @@ -1882,18 +1923,19 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 1882 | Service::FileSystem::CreateFactories(*vfs); | 1923 | Service::FileSystem::CreateFactories(*vfs); |
| 1883 | 1924 | ||
| 1884 | if (behavior == ReinitializeKeyBehavior::Warning) { | 1925 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 1885 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1926 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1927 | UISettings::values.game_directory_deepscan); | ||
| 1886 | } | 1928 | } |
| 1887 | } | 1929 | } |
| 1888 | 1930 | ||
| 1889 | std::optional<u64> GMainWindow::SelectRomFSDumpTarget( | 1931 | std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, |
| 1890 | const FileSys::RegisteredCacheUnion& installed, u64 program_id) { | 1932 | u64 program_id) { |
| 1891 | const auto dlc_entries = | 1933 | const auto dlc_entries = |
| 1892 | installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | 1934 | installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); |
| 1893 | std::vector<FileSys::RegisteredCacheEntry> dlc_match; | 1935 | std::vector<FileSys::ContentProviderEntry> dlc_match; |
| 1894 | dlc_match.reserve(dlc_entries.size()); | 1936 | dlc_match.reserve(dlc_entries.size()); |
| 1895 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), | 1937 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), |
| 1896 | [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) { | 1938 | [&program_id, &installed](const FileSys::ContentProviderEntry& entry) { |
| 1897 | return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id && | 1939 | return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id && |
| 1898 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; | 1940 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; |
| 1899 | }); | 1941 | }); |
| @@ -1989,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | |||
| 1989 | event->acceptProposedAction(); | 2031 | event->acceptProposedAction(); |
| 1990 | } | 2032 | } |
| 1991 | 2033 | ||
| 2034 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2035 | if (render_window) { | ||
| 2036 | render_window->ForwardKeyPressEvent(event); | ||
| 2037 | } | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2041 | if (render_window) { | ||
| 2042 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 1992 | bool GMainWindow::ConfirmChangeGame() { | 2046 | bool GMainWindow::ConfirmChangeGame() { |
| 1993 | if (emu_thread == nullptr) | 2047 | if (emu_thread == nullptr) |
| 1994 | return true; | 2048 | return true; |
| @@ -2056,7 +2110,8 @@ int main(int argc, char* argv[]) { | |||
| 2056 | QCoreApplication::setOrganizationName("yuzu team"); | 2110 | QCoreApplication::setOrganizationName("yuzu team"); |
| 2057 | QCoreApplication::setApplicationName("yuzu"); | 2111 | QCoreApplication::setApplicationName("yuzu"); |
| 2058 | 2112 | ||
| 2059 | QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2113 | // Enables the core to make the qt created contexts current on std::threads |
| 2114 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | ||
| 2060 | QApplication app(argc, argv); | 2115 | QApplication app(argc, argv); |
| 2061 | 2116 | ||
| 2062 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2117 | // Qt changes the locale and causes issues in float conversion using std::to_string() when |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e07c892cf..ce5045819 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -23,7 +23,6 @@ class EmuThread; | |||
| 23 | class GameList; | 23 | class GameList; |
| 24 | class GImageInfo; | 24 | class GImageInfo; |
| 25 | class GraphicsBreakPointsWidget; | 25 | class GraphicsBreakPointsWidget; |
| 26 | class GraphicsSurfaceWidget; | ||
| 27 | class GRenderWindow; | 26 | class GRenderWindow; |
| 28 | class LoadingScreen; | 27 | class LoadingScreen; |
| 29 | class MicroProfileDialog; | 28 | class MicroProfileDialog; |
| @@ -37,7 +36,8 @@ struct SoftwareKeyboardParameters; | |||
| 37 | } // namespace Core::Frontend | 36 | } // namespace Core::Frontend |
| 38 | 37 | ||
| 39 | namespace FileSys { | 38 | namespace FileSys { |
| 40 | class RegisteredCacheUnion; | 39 | class ContentProvider; |
| 40 | class ManualContentProvider; | ||
| 41 | class VfsFilesystem; | 41 | class VfsFilesystem; |
| 42 | } // namespace FileSys | 42 | } // namespace FileSys |
| 43 | 43 | ||
| @@ -120,7 +120,6 @@ private: | |||
| 120 | void InitializeWidgets(); | 120 | void InitializeWidgets(); |
| 121 | void InitializeDebugWidgets(); | 121 | void InitializeDebugWidgets(); |
| 122 | void InitializeRecentFileMenuActions(); | 122 | void InitializeRecentFileMenuActions(); |
| 123 | void InitializeHotkeys(); | ||
| 124 | 123 | ||
| 125 | void SetDefaultUIGeometry(); | 124 | void SetDefaultUIGeometry(); |
| 126 | void RestoreUIState(); | 125 | void RestoreUIState(); |
| @@ -176,6 +175,7 @@ private slots: | |||
| 176 | /// Called whenever a user selects a game in the game list widget. | 175 | /// Called whenever a user selects a game in the game list widget. |
| 177 | void OnGameListLoadFile(QString game_path); | 176 | void OnGameListLoadFile(QString game_path); |
| 178 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); | 177 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); |
| 178 | void OnTransferableShaderCacheOpenFile(u64 program_id); | ||
| 179 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); | 179 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); |
| 180 | void OnGameListCopyTID(u64 program_id); | 180 | void OnGameListCopyTID(u64 program_id); |
| 181 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 181 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| @@ -195,6 +195,7 @@ private slots: | |||
| 195 | void OnAbout(); | 195 | void OnAbout(); |
| 196 | void OnToggleFilterBar(); | 196 | void OnToggleFilterBar(); |
| 197 | void OnDisplayTitleBars(bool); | 197 | void OnDisplayTitleBars(bool); |
| 198 | void InitializeHotkeys(); | ||
| 198 | void ToggleFullscreen(); | 199 | void ToggleFullscreen(); |
| 199 | void ShowFullscreen(); | 200 | void ShowFullscreen(); |
| 200 | void HideFullscreen(); | 201 | void HideFullscreen(); |
| @@ -204,7 +205,7 @@ private slots: | |||
| 204 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 205 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 205 | 206 | ||
| 206 | private: | 207 | private: |
| 207 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id); | 208 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| 208 | void UpdateStatusBar(); | 209 | void UpdateStatusBar(); |
| 209 | 210 | ||
| 210 | Ui::MainWindow ui; | 211 | Ui::MainWindow ui; |
| @@ -232,12 +233,12 @@ private: | |||
| 232 | 233 | ||
| 233 | // FS | 234 | // FS |
| 234 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 235 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 236 | std::unique_ptr<FileSys::ManualContentProvider> provider; | ||
| 235 | 237 | ||
| 236 | // Debugger panes | 238 | // Debugger panes |
| 237 | ProfilerWidget* profilerWidget; | 239 | ProfilerWidget* profilerWidget; |
| 238 | MicroProfileDialog* microProfileDialog; | 240 | MicroProfileDialog* microProfileDialog; |
| 239 | GraphicsBreakPointsWidget* graphicsBreakpointsWidget; | 241 | GraphicsBreakPointsWidget* graphicsBreakpointsWidget; |
| 240 | GraphicsSurfaceWidget* graphicsSurfaceWidget; | ||
| 241 | WaitTreeWidget* waitTreeWidget; | 242 | WaitTreeWidget* waitTreeWidget; |
| 242 | 243 | ||
| 243 | QAction* actions_recent_files[max_recent_files_item]; | 244 | QAction* actions_recent_files[max_recent_files_item]; |
| @@ -251,4 +252,8 @@ protected: | |||
| 251 | void dropEvent(QDropEvent* event) override; | 252 | void dropEvent(QDropEvent* event) override; |
| 252 | void dragEnterEvent(QDragEnterEvent* event) override; | 253 | void dragEnterEvent(QDragEnterEvent* event) override; |
| 253 | void dragMoveEvent(QDragMoveEvent* event) override; | 254 | void dragMoveEvent(QDragMoveEvent* event) override; |
| 255 | |||
| 256 | // Overrides used to forward signals to the render window when the focus moves out. | ||
| 257 | void keyPressEvent(QKeyEvent* event) override; | ||
| 258 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 254 | }; | 259 | }; |
diff --git a/src/yuzu/ui_settings.cpp b/src/yuzu/ui_settings.cpp index a314493fc..4bdc302e0 100644 --- a/src/yuzu/ui_settings.cpp +++ b/src/yuzu/ui_settings.cpp | |||
| @@ -12,5 +12,4 @@ const Themes themes{{ | |||
| 12 | }}; | 12 | }}; |
| 13 | 13 | ||
| 14 | Values values = {}; | 14 | Values values = {}; |
| 15 | |||
| 16 | } // namespace UISettings | 15 | } // namespace UISettings |
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 82aaeedb0..dbd318e20 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h | |||
| @@ -15,7 +15,12 @@ | |||
| 15 | namespace UISettings { | 15 | namespace UISettings { |
| 16 | 16 | ||
| 17 | using ContextualShortcut = std::pair<QString, int>; | 17 | using ContextualShortcut = std::pair<QString, int>; |
| 18 | using Shortcut = std::pair<QString, ContextualShortcut>; | 18 | |
| 19 | struct Shortcut { | ||
| 20 | QString name; | ||
| 21 | QString group; | ||
| 22 | ContextualShortcut shortcut; | ||
| 23 | }; | ||
| 19 | 24 | ||
| 20 | using Themes = std::array<std::pair<const char*, const char*>, 2>; | 25 | using Themes = std::array<std::pair<const char*, const char*>, 2>; |
| 21 | extern const Themes themes; | 26 | extern const Themes themes; |
| @@ -50,8 +55,8 @@ struct Values { | |||
| 50 | QString roms_path; | 55 | QString roms_path; |
| 51 | QString symbols_path; | 56 | QString symbols_path; |
| 52 | QString screenshot_path; | 57 | QString screenshot_path; |
| 53 | QString gamedir; | 58 | QString game_directory_path; |
| 54 | bool gamedir_deepscan; | 59 | bool game_directory_deepscan; |
| 55 | QStringList recent_files; | 60 | QStringList recent_files; |
| 56 | 61 | ||
| 57 | QString theme; | 62 | QString theme; |
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp new file mode 100644 index 000000000..d3edf6ec3 --- /dev/null +++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QDialogButtonBox> | ||
| 6 | #include <QKeySequenceEdit> | ||
| 7 | #include <QVBoxLayout> | ||
| 8 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" | ||
| 9 | |||
| 10 | SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { | ||
| 11 | setWindowTitle(tr("Enter a hotkey")); | ||
| 12 | auto* layout = new QVBoxLayout(this); | ||
| 13 | key_sequence = new QKeySequenceEdit; | ||
| 14 | layout->addWidget(key_sequence); | ||
| 15 | auto* buttons = | ||
| 16 | new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); | ||
| 17 | buttons->setCenterButtons(true); | ||
| 18 | layout->addWidget(buttons); | ||
| 19 | connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); | ||
| 20 | connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); | ||
| 21 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 22 | } | ||
| 23 | |||
| 24 | SequenceDialog::~SequenceDialog() = default; | ||
| 25 | |||
| 26 | QKeySequence SequenceDialog::GetSequence() const { | ||
| 27 | // Only the first key is returned. The other 3, if present, are ignored. | ||
| 28 | return QKeySequence(key_sequence->keySequence()[0]); | ||
| 29 | } | ||
| 30 | |||
| 31 | bool SequenceDialog::focusNextPrevChild(bool next) { | ||
| 32 | return false; | ||
| 33 | } | ||
| 34 | |||
| 35 | void SequenceDialog::closeEvent(QCloseEvent*) { | ||
| 36 | reject(); | ||
| 37 | } | ||
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.h b/src/yuzu/util/sequence_dialog/sequence_dialog.h new file mode 100644 index 000000000..969c77740 --- /dev/null +++ b/src/yuzu/util/sequence_dialog/sequence_dialog.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QDialog> | ||
| 8 | |||
| 9 | class QKeySequenceEdit; | ||
| 10 | |||
| 11 | class SequenceDialog : public QDialog { | ||
| 12 | Q_OBJECT | ||
| 13 | |||
| 14 | public: | ||
| 15 | explicit SequenceDialog(QWidget* parent = nullptr); | ||
| 16 | ~SequenceDialog() override; | ||
| 17 | |||
| 18 | QKeySequence GetSequence() const; | ||
| 19 | void closeEvent(QCloseEvent*) override; | ||
| 20 | |||
| 21 | private: | ||
| 22 | QKeySequenceEdit* key_sequence; | ||
| 23 | bool focusNextPrevChild(bool next) override; | ||
| 24 | }; | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index de7a26e14..68a176032 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -19,6 +19,37 @@ | |||
| 19 | #include "input_common/sdl/sdl.h" | 19 | #include "input_common/sdl/sdl.h" |
| 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 21 | 21 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 23 | public: | ||
| 24 | explicit SDLGLContext() { | ||
| 25 | // create a hidden window to make the shared context against | ||
| 26 | window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 27 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 28 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 29 | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); | ||
| 30 | context = SDL_GL_CreateContext(window); | ||
| 31 | } | ||
| 32 | |||
| 33 | ~SDLGLContext() { | ||
| 34 | SDL_GL_DeleteContext(context); | ||
| 35 | SDL_DestroyWindow(window); | ||
| 36 | } | ||
| 37 | |||
| 38 | void MakeCurrent() override { | ||
| 39 | SDL_GL_MakeCurrent(window, context); | ||
| 40 | } | ||
| 41 | |||
| 42 | void DoneCurrent() override { | ||
| 43 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 44 | } | ||
| 45 | |||
| 46 | void SwapBuffers() override {} | ||
| 47 | |||
| 48 | private: | ||
| 49 | SDL_Window* window; | ||
| 50 | SDL_GLContext context; | ||
| 51 | }; | ||
| 52 | |||
| 22 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 53 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| 23 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | 54 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); |
| 24 | InputCommon::GetMotionEmu()->Tilt(x, y); | 55 | InputCommon::GetMotionEmu()->Tilt(x, y); |
| @@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 153 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | 184 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); |
| 154 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 185 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
| 155 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | 186 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |
| 187 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | ||
| 156 | 188 | ||
| 157 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | 189 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, |
| 158 | Common::g_scm_branch, Common::g_scm_desc); | 190 | Common::g_scm_branch, Common::g_scm_desc); |
| @@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 171 | if (fullscreen) { | 203 | if (fullscreen) { |
| 172 | Fullscreen(); | 204 | Fullscreen(); |
| 173 | } | 205 | } |
| 174 | |||
| 175 | gl_context = SDL_GL_CreateContext(render_window); | 206 | gl_context = SDL_GL_CreateContext(render_window); |
| 176 | 207 | ||
| 177 | if (gl_context == nullptr) { | 208 | if (gl_context == nullptr) { |
| @@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( | |||
| 278 | 309 | ||
| 279 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); | 310 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); |
| 280 | } | 311 | } |
| 312 | |||
| 313 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { | ||
| 314 | return std::make_unique<SDLGLContext>(); | ||
| 315 | } | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index b0d4116cc..17e98227f 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -27,6 +27,8 @@ public: | |||
| 27 | /// Releases the GL context from the caller thread | 27 | /// Releases the GL context from the caller thread |
| 28 | void DoneCurrent() override; | 28 | void DoneCurrent() override; |
| 29 | 29 | ||
| 30 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 31 | |||
| 30 | /// Whether the window is still open, and a close request hasn't yet been sent | 32 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 31 | bool IsOpen() const; | 33 | bool IsOpen() const; |
| 32 | 34 | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 245f25847..7ea4a1b18 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 34 | 34 | ||
| 35 | #include <getopt.h> | 35 | #include <getopt.h> |
| 36 | #include "core/file_sys/registered_cache.h" | ||
| 36 | #ifndef _MSC_VER | 37 | #ifndef _MSC_VER |
| 37 | #include <unistd.h> | 38 | #include <unistd.h> |
| 38 | #endif | 39 | #endif |
| @@ -178,6 +179,7 @@ int main(int argc, char** argv) { | |||
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | Core::System& system{Core::System::GetInstance()}; | 181 | Core::System& system{Core::System::GetInstance()}; |
| 182 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 181 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 183 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 182 | Service::FileSystem::CreateFactories(*system.GetFilesystem()); | 184 | Service::FileSystem::CreateFactories(*system.GetFilesystem()); |
| 183 | 185 | ||