diff options
67 files changed, 1390 insertions, 1442 deletions
diff --git a/.gitmodules b/.gitmodules index 2c044ed7d..a33a04167 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -13,9 +13,6 @@ | |||
| 13 | [submodule "dynarmic"] | 13 | [submodule "dynarmic"] |
| 14 | path = externals/dynarmic | 14 | path = externals/dynarmic |
| 15 | url = https://github.com/MerryMage/dynarmic.git | 15 | url = https://github.com/MerryMage/dynarmic.git |
| 16 | [submodule "xbyak"] | ||
| 17 | path = externals/xbyak | ||
| 18 | url = https://github.com/herumi/xbyak.git | ||
| 19 | [submodule "fmt"] | 16 | [submodule "fmt"] |
| 20 | path = externals/fmt | 17 | path = externals/fmt |
| 21 | url = https://github.com/fmtlib/fmt.git | 18 | url = https://github.com/fmtlib/fmt.git |
diff --git a/CMakeLists.txt b/CMakeLists.txt index 918cf5372..1f71f9fd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -377,7 +377,7 @@ if (CLANG_FORMAT) | |||
| 377 | set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") | 377 | set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") |
| 378 | if (WIN32) | 378 | if (WIN32) |
| 379 | add_custom_target(clang-format | 379 | add_custom_target(clang-format |
| 380 | COMMAND powershell.exe -Command "${CLANG_FORMAT} -i @(Get-ChildItem -Recurse ${SRCS}/* -Include \'*.h\', \'*.cpp\')" | 380 | COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}" |
| 381 | COMMENT ${CCOMMENT}) | 381 | COMMENT ${CCOMMENT}) |
| 382 | elseif(MINGW) | 382 | elseif(MINGW) |
| 383 | add_custom_target(clang-format | 383 | add_custom_target(clang-format |
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 1261062e8..e156bbece 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -9,7 +9,6 @@ target_include_directories(catch-single-include INTERFACE catch/single_include) | |||
| 9 | 9 | ||
| 10 | # Dynarmic | 10 | # Dynarmic |
| 11 | if (ARCHITECTURE_x86_64) | 11 | if (ARCHITECTURE_x86_64) |
| 12 | add_library(xbyak INTERFACE) | ||
| 13 | set(DYNARMIC_TESTS OFF) | 12 | set(DYNARMIC_TESTS OFF) |
| 14 | set(DYNARMIC_NO_BUNDLED_FMT ON) | 13 | set(DYNARMIC_NO_BUNDLED_FMT ON) |
| 15 | add_subdirectory(dynarmic) | 14 | add_subdirectory(dynarmic) |
| @@ -53,14 +52,6 @@ target_include_directories(unicorn-headers INTERFACE ./unicorn/include) | |||
| 53 | # SoundTouch | 52 | # SoundTouch |
| 54 | add_subdirectory(soundtouch) | 53 | add_subdirectory(soundtouch) |
| 55 | 54 | ||
| 56 | # Xbyak | ||
| 57 | if (ARCHITECTURE_x86_64) | ||
| 58 | # Defined before "dynarmic" above | ||
| 59 | # add_library(xbyak INTERFACE) | ||
| 60 | target_include_directories(xbyak INTERFACE ./xbyak/xbyak) | ||
| 61 | target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) | ||
| 62 | endif() | ||
| 63 | |||
| 64 | # Opus | 55 | # Opus |
| 65 | add_subdirectory(opus) | 56 | add_subdirectory(opus) |
| 66 | target_include_directories(opus INTERFACE ./opus/include) | 57 | target_include_directories(opus INTERFACE ./opus/include) |
diff --git a/externals/xbyak b/externals/xbyak deleted file mode 160000 | |||
| Subproject 1de435ed04c8e74775804da944d176baf0ce56e | |||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index eccd8f64a..a5e71d879 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -44,7 +44,6 @@ add_library(common STATIC | |||
| 44 | detached_tasks.cpp | 44 | detached_tasks.cpp |
| 45 | detached_tasks.h | 45 | detached_tasks.h |
| 46 | bit_field.h | 46 | bit_field.h |
| 47 | bit_set.h | ||
| 48 | cityhash.cpp | 47 | cityhash.cpp |
| 49 | cityhash.h | 48 | cityhash.h |
| 50 | color.h | 49 | color.h |
| @@ -95,14 +94,9 @@ if(ARCHITECTURE_x86_64) | |||
| 95 | PRIVATE | 94 | PRIVATE |
| 96 | x64/cpu_detect.cpp | 95 | x64/cpu_detect.cpp |
| 97 | x64/cpu_detect.h | 96 | x64/cpu_detect.h |
| 98 | x64/xbyak_abi.h | ||
| 99 | x64/xbyak_util.h | ||
| 100 | ) | 97 | ) |
| 101 | endif() | 98 | endif() |
| 102 | 99 | ||
| 103 | create_target_directory_groups(common) | 100 | create_target_directory_groups(common) |
| 104 | 101 | ||
| 105 | target_link_libraries(common PUBLIC Boost::boost fmt microprofile) | 102 | target_link_libraries(common PUBLIC Boost::boost fmt microprofile) |
| 106 | if (ARCHITECTURE_x86_64) | ||
| 107 | target_link_libraries(common PRIVATE xbyak) | ||
| 108 | endif() | ||
diff --git a/src/common/assert.h b/src/common/assert.h index 0d4eddc19..6002f7ab1 100644 --- a/src/common/assert.h +++ b/src/common/assert.h | |||
| @@ -52,5 +52,8 @@ __declspec(noinline, noreturn) | |||
| 52 | #define DEBUG_ASSERT_MSG(_a_, _desc_, ...) | 52 | #define DEBUG_ASSERT_MSG(_a_, _desc_, ...) |
| 53 | #endif | 53 | #endif |
| 54 | 54 | ||
| 55 | #define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!") | 55 | #define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!") |
| 56 | #define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) | 56 | #define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) |
| 57 | |||
| 58 | #define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!") | ||
| 59 | #define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__) | ||
diff --git a/src/common/bit_set.h b/src/common/bit_set.h deleted file mode 100644 index 5cd1352b2..000000000 --- a/src/common/bit_set.h +++ /dev/null | |||
| @@ -1,244 +0,0 @@ | |||
| 1 | // This file is under the public domain. | ||
| 2 | |||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include <cstddef> | ||
| 6 | #ifdef _WIN32 | ||
| 7 | #include <intrin.h> | ||
| 8 | #endif | ||
| 9 | #include <initializer_list> | ||
| 10 | #include <new> | ||
| 11 | #include <type_traits> | ||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | // namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly | ||
| 15 | namespace Common { | ||
| 16 | |||
| 17 | // Helper functions: | ||
| 18 | |||
| 19 | #ifdef _MSC_VER | ||
| 20 | template <typename T> | ||
| 21 | static inline int CountSetBits(T v) { | ||
| 22 | // from https://graphics.stanford.edu/~seander/bithacks.html | ||
| 23 | // GCC has this built in, but MSVC's intrinsic will only emit the actual | ||
| 24 | // POPCNT instruction, which we're not depending on | ||
| 25 | v = v - ((v >> 1) & (T) ~(T)0 / 3); | ||
| 26 | v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3); | ||
| 27 | v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15; | ||
| 28 | return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; | ||
| 29 | } | ||
| 30 | static inline int LeastSignificantSetBit(u8 val) { | ||
| 31 | unsigned long index; | ||
| 32 | _BitScanForward(&index, val); | ||
| 33 | return (int)index; | ||
| 34 | } | ||
| 35 | static inline int LeastSignificantSetBit(u16 val) { | ||
| 36 | unsigned long index; | ||
| 37 | _BitScanForward(&index, val); | ||
| 38 | return (int)index; | ||
| 39 | } | ||
| 40 | static inline int LeastSignificantSetBit(u32 val) { | ||
| 41 | unsigned long index; | ||
| 42 | _BitScanForward(&index, val); | ||
| 43 | return (int)index; | ||
| 44 | } | ||
| 45 | static inline int LeastSignificantSetBit(u64 val) { | ||
| 46 | unsigned long index; | ||
| 47 | _BitScanForward64(&index, val); | ||
| 48 | return (int)index; | ||
| 49 | } | ||
| 50 | #else | ||
| 51 | static inline int CountSetBits(u8 val) { | ||
| 52 | return __builtin_popcount(val); | ||
| 53 | } | ||
| 54 | static inline int CountSetBits(u16 val) { | ||
| 55 | return __builtin_popcount(val); | ||
| 56 | } | ||
| 57 | static inline int CountSetBits(u32 val) { | ||
| 58 | return __builtin_popcount(val); | ||
| 59 | } | ||
| 60 | static inline int CountSetBits(u64 val) { | ||
| 61 | return __builtin_popcountll(val); | ||
| 62 | } | ||
| 63 | static inline int LeastSignificantSetBit(u8 val) { | ||
| 64 | return __builtin_ctz(val); | ||
| 65 | } | ||
| 66 | static inline int LeastSignificantSetBit(u16 val) { | ||
| 67 | return __builtin_ctz(val); | ||
| 68 | } | ||
| 69 | static inline int LeastSignificantSetBit(u32 val) { | ||
| 70 | return __builtin_ctz(val); | ||
| 71 | } | ||
| 72 | static inline int LeastSignificantSetBit(u64 val) { | ||
| 73 | return __builtin_ctzll(val); | ||
| 74 | } | ||
| 75 | #endif | ||
| 76 | |||
| 77 | // Similar to std::bitset, this is a class which encapsulates a bitset, i.e. | ||
| 78 | // using the set bits of an integer to represent a set of integers. Like that | ||
| 79 | // class, it acts like an array of bools: | ||
| 80 | // BitSet32 bs; | ||
| 81 | // bs[1] = true; | ||
| 82 | // but also like the underlying integer ([0] = least significant bit): | ||
| 83 | // BitSet32 bs2 = ...; | ||
| 84 | // bs = (bs ^ bs2) & BitSet32(0xffff); | ||
| 85 | // The following additional functionality is provided: | ||
| 86 | // - Construction using an initializer list. | ||
| 87 | // BitSet bs { 1, 2, 4, 8 }; | ||
| 88 | // - Efficiently iterating through the set bits: | ||
| 89 | // for (int i : bs) | ||
| 90 | // [i is the *index* of a set bit] | ||
| 91 | // (This uses the appropriate CPU instruction to find the next set bit in one | ||
| 92 | // operation.) | ||
| 93 | // - Counting set bits using .Count() - see comment on that method. | ||
| 94 | |||
| 95 | // TODO: use constexpr when MSVC gets out of the Dark Ages | ||
| 96 | |||
| 97 | template <typename IntTy> | ||
| 98 | class BitSet { | ||
| 99 | static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types"); | ||
| 100 | |||
| 101 | public: | ||
| 102 | // A reference to a particular bit, returned from operator[]. | ||
| 103 | class Ref { | ||
| 104 | public: | ||
| 105 | Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} | ||
| 106 | Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} | ||
| 107 | operator bool() const { | ||
| 108 | return (m_bs->m_val & m_mask) != 0; | ||
| 109 | } | ||
| 110 | bool operator=(bool set) { | ||
| 111 | m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); | ||
| 112 | return set; | ||
| 113 | } | ||
| 114 | |||
| 115 | private: | ||
| 116 | BitSet* m_bs; | ||
| 117 | IntTy m_mask; | ||
| 118 | }; | ||
| 119 | |||
| 120 | // A STL-like iterator is required to be able to use range-based for loops. | ||
| 121 | class Iterator { | ||
| 122 | public: | ||
| 123 | Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {} | ||
| 124 | Iterator(IntTy val) : m_val(val), m_bit(0) {} | ||
| 125 | Iterator& operator=(Iterator other) { | ||
| 126 | new (this) Iterator(other); | ||
| 127 | return *this; | ||
| 128 | } | ||
| 129 | int operator*() { | ||
| 130 | return m_bit + ComputeLsb(); | ||
| 131 | } | ||
| 132 | Iterator& operator++() { | ||
| 133 | int lsb = ComputeLsb(); | ||
| 134 | m_val >>= lsb + 1; | ||
| 135 | m_bit += lsb + 1; | ||
| 136 | m_has_lsb = false; | ||
| 137 | return *this; | ||
| 138 | } | ||
| 139 | Iterator operator++(int _) { | ||
| 140 | Iterator other(*this); | ||
| 141 | ++*this; | ||
| 142 | return other; | ||
| 143 | } | ||
| 144 | bool operator==(Iterator other) const { | ||
| 145 | return m_val == other.m_val; | ||
| 146 | } | ||
| 147 | bool operator!=(Iterator other) const { | ||
| 148 | return m_val != other.m_val; | ||
| 149 | } | ||
| 150 | |||
| 151 | private: | ||
| 152 | int ComputeLsb() { | ||
| 153 | if (!m_has_lsb) { | ||
| 154 | m_lsb = LeastSignificantSetBit(m_val); | ||
| 155 | m_has_lsb = true; | ||
| 156 | } | ||
| 157 | return m_lsb; | ||
| 158 | } | ||
| 159 | IntTy m_val; | ||
| 160 | int m_bit; | ||
| 161 | int m_lsb = -1; | ||
| 162 | bool m_has_lsb = false; | ||
| 163 | }; | ||
| 164 | |||
| 165 | BitSet() : m_val(0) {} | ||
| 166 | explicit BitSet(IntTy val) : m_val(val) {} | ||
| 167 | BitSet(std::initializer_list<int> init) { | ||
| 168 | m_val = 0; | ||
| 169 | for (int bit : init) | ||
| 170 | m_val |= (IntTy)1 << bit; | ||
| 171 | } | ||
| 172 | |||
| 173 | static BitSet AllTrue(std::size_t count) { | ||
| 174 | return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); | ||
| 175 | } | ||
| 176 | |||
| 177 | Ref operator[](std::size_t bit) { | ||
| 178 | return Ref(this, (IntTy)1 << bit); | ||
| 179 | } | ||
| 180 | const Ref operator[](std::size_t bit) const { | ||
| 181 | return (*const_cast<BitSet*>(this))[bit]; | ||
| 182 | } | ||
| 183 | bool operator==(BitSet other) const { | ||
| 184 | return m_val == other.m_val; | ||
| 185 | } | ||
| 186 | bool operator!=(BitSet other) const { | ||
| 187 | return m_val != other.m_val; | ||
| 188 | } | ||
| 189 | bool operator<(BitSet other) const { | ||
| 190 | return m_val < other.m_val; | ||
| 191 | } | ||
| 192 | bool operator>(BitSet other) const { | ||
| 193 | return m_val > other.m_val; | ||
| 194 | } | ||
| 195 | BitSet operator|(BitSet other) const { | ||
| 196 | return BitSet(m_val | other.m_val); | ||
| 197 | } | ||
| 198 | BitSet operator&(BitSet other) const { | ||
| 199 | return BitSet(m_val & other.m_val); | ||
| 200 | } | ||
| 201 | BitSet operator^(BitSet other) const { | ||
| 202 | return BitSet(m_val ^ other.m_val); | ||
| 203 | } | ||
| 204 | BitSet operator~() const { | ||
| 205 | return BitSet(~m_val); | ||
| 206 | } | ||
| 207 | BitSet& operator|=(BitSet other) { | ||
| 208 | return *this = *this | other; | ||
| 209 | } | ||
| 210 | BitSet& operator&=(BitSet other) { | ||
| 211 | return *this = *this & other; | ||
| 212 | } | ||
| 213 | BitSet& operator^=(BitSet other) { | ||
| 214 | return *this = *this ^ other; | ||
| 215 | } | ||
| 216 | operator u32() = delete; | ||
| 217 | operator bool() { | ||
| 218 | return m_val != 0; | ||
| 219 | } | ||
| 220 | |||
| 221 | // Warning: Even though on modern CPUs this is a single fast instruction, | ||
| 222 | // Dolphin's official builds do not currently assume POPCNT support on x86, | ||
| 223 | // so slower explicit bit twiddling is generated. Still should generally | ||
| 224 | // be faster than a loop. | ||
| 225 | unsigned int Count() const { | ||
| 226 | return CountSetBits(m_val); | ||
| 227 | } | ||
| 228 | |||
| 229 | Iterator begin() const { | ||
| 230 | return Iterator(m_val); | ||
| 231 | } | ||
| 232 | Iterator end() const { | ||
| 233 | return Iterator(0); | ||
| 234 | } | ||
| 235 | |||
| 236 | IntTy m_val; | ||
| 237 | }; | ||
| 238 | |||
| 239 | } // namespace Common | ||
| 240 | |||
| 241 | typedef Common::BitSet<u8> BitSet8; | ||
| 242 | typedef Common::BitSet<u16> BitSet16; | ||
| 243 | typedef Common::BitSet<u32> BitSet32; | ||
| 244 | typedef Common::BitSet<u64> BitSet64; | ||
diff --git a/src/common/math_util.h b/src/common/math_util.h index 343cdd902..94b4394c5 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -4,18 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 8 | #include <cstdlib> | 7 | #include <cstdlib> |
| 9 | #include <type_traits> | 8 | #include <type_traits> |
| 10 | 9 | ||
| 11 | namespace MathUtil { | 10 | namespace MathUtil { |
| 12 | 11 | ||
| 13 | static constexpr float PI = 3.14159265f; | 12 | constexpr float PI = 3.14159265f; |
| 14 | |||
| 15 | inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, | ||
| 16 | unsigned length1) { | ||
| 17 | return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1)); | ||
| 18 | } | ||
| 19 | 13 | ||
| 20 | template <class T> | 14 | template <class T> |
| 21 | struct Rectangle { | 15 | struct Rectangle { |
| @@ -24,16 +18,16 @@ struct Rectangle { | |||
| 24 | T right{}; | 18 | T right{}; |
| 25 | T bottom{}; | 19 | T bottom{}; |
| 26 | 20 | ||
| 27 | Rectangle() = default; | 21 | constexpr Rectangle() = default; |
| 28 | 22 | ||
| 29 | Rectangle(T left, T top, T right, T bottom) | 23 | constexpr Rectangle(T left, T top, T right, T bottom) |
| 30 | : left(left), top(top), right(right), bottom(bottom) {} | 24 | : left(left), top(top), right(right), bottom(bottom) {} |
| 31 | 25 | ||
| 32 | T GetWidth() const { | 26 | T GetWidth() const { |
| 33 | return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); | 27 | return std::abs(static_cast<std::make_signed_t<T>>(right - left)); |
| 34 | } | 28 | } |
| 35 | T GetHeight() const { | 29 | T GetHeight() const { |
| 36 | return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); | 30 | return std::abs(static_cast<std::make_signed_t<T>>(bottom - top)); |
| 37 | } | 31 | } |
| 38 | Rectangle<T> TranslateX(const T x) const { | 32 | Rectangle<T> TranslateX(const T x) const { |
| 39 | return Rectangle{left + x, top, right + x, bottom}; | 33 | return Rectangle{left + x, top, right + x, bottom}; |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 9e207118f..5144c0d9f 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -25,23 +25,6 @@ | |||
| 25 | 25 | ||
| 26 | namespace Common { | 26 | namespace Common { |
| 27 | 27 | ||
| 28 | int CurrentThreadId() { | ||
| 29 | #ifdef _MSC_VER | ||
| 30 | return GetCurrentThreadId(); | ||
| 31 | #elif defined __APPLE__ | ||
| 32 | return mach_thread_self(); | ||
| 33 | #else | ||
| 34 | return 0; | ||
| 35 | #endif | ||
| 36 | } | ||
| 37 | |||
| 38 | #ifdef _WIN32 | ||
| 39 | // Supporting functions | ||
| 40 | void SleepCurrentThread(int ms) { | ||
| 41 | Sleep(ms); | ||
| 42 | } | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #ifdef _MSC_VER | 28 | #ifdef _MSC_VER |
| 46 | 29 | ||
| 47 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { | 30 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { |
| @@ -62,7 +45,7 @@ void SwitchCurrentThread() { | |||
| 62 | 45 | ||
| 63 | // This is implemented much nicer in upcoming msvc++, see: | 46 | // This is implemented much nicer in upcoming msvc++, see: |
| 64 | // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx | 47 | // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx |
| 65 | void SetCurrentThreadName(const char* szThreadName) { | 48 | void SetCurrentThreadName(const char* name) { |
| 66 | static const DWORD MS_VC_EXCEPTION = 0x406D1388; | 49 | static const DWORD MS_VC_EXCEPTION = 0x406D1388; |
| 67 | 50 | ||
| 68 | #pragma pack(push, 8) | 51 | #pragma pack(push, 8) |
| @@ -75,7 +58,7 @@ void SetCurrentThreadName(const char* szThreadName) { | |||
| 75 | #pragma pack(pop) | 58 | #pragma pack(pop) |
| 76 | 59 | ||
| 77 | info.dwType = 0x1000; | 60 | info.dwType = 0x1000; |
| 78 | info.szName = szThreadName; | 61 | info.szName = name; |
| 79 | info.dwThreadID = -1; // dwThreadID; | 62 | info.dwThreadID = -1; // dwThreadID; |
| 80 | info.dwFlags = 0; | 63 | info.dwFlags = 0; |
| 81 | 64 | ||
| @@ -107,10 +90,6 @@ void SetCurrentThreadAffinity(u32 mask) { | |||
| 107 | } | 90 | } |
| 108 | 91 | ||
| 109 | #ifndef _WIN32 | 92 | #ifndef _WIN32 |
| 110 | void SleepCurrentThread(int ms) { | ||
| 111 | usleep(1000 * ms); | ||
| 112 | } | ||
| 113 | |||
| 114 | void SwitchCurrentThread() { | 93 | void SwitchCurrentThread() { |
| 115 | usleep(1000 * 1); | 94 | usleep(1000 * 1); |
| 116 | } | 95 | } |
| @@ -118,15 +97,15 @@ void SwitchCurrentThread() { | |||
| 118 | 97 | ||
| 119 | // MinGW with the POSIX threading model does not support pthread_setname_np | 98 | // MinGW with the POSIX threading model does not support pthread_setname_np |
| 120 | #if !defined(_WIN32) || defined(_MSC_VER) | 99 | #if !defined(_WIN32) || defined(_MSC_VER) |
| 121 | void SetCurrentThreadName(const char* szThreadName) { | 100 | void SetCurrentThreadName(const char* name) { |
| 122 | #ifdef __APPLE__ | 101 | #ifdef __APPLE__ |
| 123 | pthread_setname_np(szThreadName); | 102 | pthread_setname_np(name); |
| 124 | #elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) | 103 | #elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) |
| 125 | pthread_set_name_np(pthread_self(), szThreadName); | 104 | pthread_set_name_np(pthread_self(), name); |
| 126 | #elif defined(__NetBSD__) | 105 | #elif defined(__NetBSD__) |
| 127 | pthread_setname_np(pthread_self(), "%s", (void*)szThreadName); | 106 | pthread_setname_np(pthread_self(), "%s", (void*)name); |
| 128 | #else | 107 | #else |
| 129 | pthread_setname_np(pthread_self(), szThreadName); | 108 | pthread_setname_np(pthread_self(), name); |
| 130 | #endif | 109 | #endif |
| 131 | } | 110 | } |
| 132 | #endif | 111 | #endif |
diff --git a/src/common/thread.h b/src/common/thread.h index 6cbdb96a3..2cf74452d 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -13,15 +13,8 @@ | |||
| 13 | 13 | ||
| 14 | namespace Common { | 14 | namespace Common { |
| 15 | 15 | ||
| 16 | int CurrentThreadId(); | ||
| 17 | |||
| 18 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); | ||
| 19 | void SetCurrentThreadAffinity(u32 mask); | ||
| 20 | |||
| 21 | class Event { | 16 | class Event { |
| 22 | public: | 17 | public: |
| 23 | Event() : is_set(false) {} | ||
| 24 | |||
| 25 | void Set() { | 18 | void Set() { |
| 26 | std::lock_guard<std::mutex> lk(mutex); | 19 | std::lock_guard<std::mutex> lk(mutex); |
| 27 | if (!is_set) { | 20 | if (!is_set) { |
| @@ -53,14 +46,14 @@ public: | |||
| 53 | } | 46 | } |
| 54 | 47 | ||
| 55 | private: | 48 | private: |
| 56 | bool is_set; | 49 | bool is_set = false; |
| 57 | std::condition_variable condvar; | 50 | std::condition_variable condvar; |
| 58 | std::mutex mutex; | 51 | std::mutex mutex; |
| 59 | }; | 52 | }; |
| 60 | 53 | ||
| 61 | class Barrier { | 54 | class Barrier { |
| 62 | public: | 55 | public: |
| 63 | explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {} | 56 | explicit Barrier(std::size_t count_) : count(count_) {} |
| 64 | 57 | ||
| 65 | /// Blocks until all "count" threads have called Sync() | 58 | /// Blocks until all "count" threads have called Sync() |
| 66 | void Sync() { | 59 | void Sync() { |
| @@ -80,12 +73,13 @@ public: | |||
| 80 | private: | 73 | private: |
| 81 | std::condition_variable condvar; | 74 | std::condition_variable condvar; |
| 82 | std::mutex mutex; | 75 | std::mutex mutex; |
| 83 | const std::size_t count; | 76 | std::size_t count; |
| 84 | std::size_t waiting; | 77 | std::size_t waiting = 0; |
| 85 | std::size_t generation; // Incremented once each time the barrier is used | 78 | std::size_t generation = 0; // Incremented once each time the barrier is used |
| 86 | }; | 79 | }; |
| 87 | 80 | ||
| 88 | void SleepCurrentThread(int ms); | 81 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); |
| 82 | void SetCurrentThreadAffinity(u32 mask); | ||
| 89 | void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms | 83 | void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms |
| 90 | void SetCurrentThreadName(const char* name); | 84 | void SetCurrentThreadName(const char* name); |
| 91 | 85 | ||
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h deleted file mode 100644 index 636a5c0f9..000000000 --- a/src/common/x64/xbyak_abi.h +++ /dev/null | |||
| @@ -1,222 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <initializer_list> | ||
| 8 | #include <xbyak.h> | ||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/bit_set.h" | ||
| 11 | |||
| 12 | namespace Common::X64 { | ||
| 13 | |||
| 14 | inline int RegToIndex(const Xbyak::Reg& reg) { | ||
| 15 | using Kind = Xbyak::Reg::Kind; | ||
| 16 | ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0, | ||
| 17 | "RegSet only support GPRs and XMM registers."); | ||
| 18 | ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15."); | ||
| 19 | return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16); | ||
| 20 | } | ||
| 21 | |||
| 22 | inline Xbyak::Reg64 IndexToReg64(int reg_index) { | ||
| 23 | ASSERT(reg_index < 16); | ||
| 24 | return Xbyak::Reg64(reg_index); | ||
| 25 | } | ||
| 26 | |||
| 27 | inline Xbyak::Xmm IndexToXmm(int reg_index) { | ||
| 28 | ASSERT(reg_index >= 16 && reg_index < 32); | ||
| 29 | return Xbyak::Xmm(reg_index - 16); | ||
| 30 | } | ||
| 31 | |||
| 32 | inline Xbyak::Reg IndexToReg(int reg_index) { | ||
| 33 | if (reg_index < 16) { | ||
| 34 | return IndexToReg64(reg_index); | ||
| 35 | } else { | ||
| 36 | return IndexToXmm(reg_index); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | inline BitSet32 BuildRegSet(std::initializer_list<Xbyak::Reg> regs) { | ||
| 41 | BitSet32 bits; | ||
| 42 | for (const Xbyak::Reg& reg : regs) { | ||
| 43 | bits[RegToIndex(reg)] = true; | ||
| 44 | } | ||
| 45 | return bits; | ||
| 46 | } | ||
| 47 | |||
| 48 | const BitSet32 ABI_ALL_GPRS(0x0000FFFF); | ||
| 49 | const BitSet32 ABI_ALL_XMMS(0xFFFF0000); | ||
| 50 | |||
| 51 | #ifdef _WIN32 | ||
| 52 | |||
| 53 | // Microsoft x64 ABI | ||
| 54 | const Xbyak::Reg ABI_RETURN = Xbyak::util::rax; | ||
| 55 | const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx; | ||
| 56 | const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx; | ||
| 57 | const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8; | ||
| 58 | const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9; | ||
| 59 | |||
| 60 | const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({ | ||
| 61 | // GPRs | ||
| 62 | Xbyak::util::rcx, | ||
| 63 | Xbyak::util::rdx, | ||
| 64 | Xbyak::util::r8, | ||
| 65 | Xbyak::util::r9, | ||
| 66 | Xbyak::util::r10, | ||
| 67 | Xbyak::util::r11, | ||
| 68 | // XMMs | ||
| 69 | Xbyak::util::xmm0, | ||
| 70 | Xbyak::util::xmm1, | ||
| 71 | Xbyak::util::xmm2, | ||
| 72 | Xbyak::util::xmm3, | ||
| 73 | Xbyak::util::xmm4, | ||
| 74 | Xbyak::util::xmm5, | ||
| 75 | }); | ||
| 76 | |||
| 77 | const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ | ||
| 78 | // GPRs | ||
| 79 | Xbyak::util::rbx, | ||
| 80 | Xbyak::util::rsi, | ||
| 81 | Xbyak::util::rdi, | ||
| 82 | Xbyak::util::rbp, | ||
| 83 | Xbyak::util::r12, | ||
| 84 | Xbyak::util::r13, | ||
| 85 | Xbyak::util::r14, | ||
| 86 | Xbyak::util::r15, | ||
| 87 | // XMMs | ||
| 88 | Xbyak::util::xmm6, | ||
| 89 | Xbyak::util::xmm7, | ||
| 90 | Xbyak::util::xmm8, | ||
| 91 | Xbyak::util::xmm9, | ||
| 92 | Xbyak::util::xmm10, | ||
| 93 | Xbyak::util::xmm11, | ||
| 94 | Xbyak::util::xmm12, | ||
| 95 | Xbyak::util::xmm13, | ||
| 96 | Xbyak::util::xmm14, | ||
| 97 | Xbyak::util::xmm15, | ||
| 98 | }); | ||
| 99 | |||
| 100 | constexpr std::size_t ABI_SHADOW_SPACE = 0x20; | ||
| 101 | |||
| 102 | #else | ||
| 103 | |||
| 104 | // System V x86-64 ABI | ||
| 105 | const Xbyak::Reg ABI_RETURN = Xbyak::util::rax; | ||
| 106 | const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi; | ||
| 107 | const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi; | ||
| 108 | const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx; | ||
| 109 | const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx; | ||
| 110 | |||
| 111 | const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({ | ||
| 112 | // GPRs | ||
| 113 | Xbyak::util::rcx, | ||
| 114 | Xbyak::util::rdx, | ||
| 115 | Xbyak::util::rdi, | ||
| 116 | Xbyak::util::rsi, | ||
| 117 | Xbyak::util::r8, | ||
| 118 | Xbyak::util::r9, | ||
| 119 | Xbyak::util::r10, | ||
| 120 | Xbyak::util::r11, | ||
| 121 | // XMMs | ||
| 122 | Xbyak::util::xmm0, | ||
| 123 | Xbyak::util::xmm1, | ||
| 124 | Xbyak::util::xmm2, | ||
| 125 | Xbyak::util::xmm3, | ||
| 126 | Xbyak::util::xmm4, | ||
| 127 | Xbyak::util::xmm5, | ||
| 128 | Xbyak::util::xmm6, | ||
| 129 | Xbyak::util::xmm7, | ||
| 130 | Xbyak::util::xmm8, | ||
| 131 | Xbyak::util::xmm9, | ||
| 132 | Xbyak::util::xmm10, | ||
| 133 | Xbyak::util::xmm11, | ||
| 134 | Xbyak::util::xmm12, | ||
| 135 | Xbyak::util::xmm13, | ||
| 136 | Xbyak::util::xmm14, | ||
| 137 | Xbyak::util::xmm15, | ||
| 138 | }); | ||
| 139 | |||
| 140 | const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ | ||
| 141 | // GPRs | ||
| 142 | Xbyak::util::rbx, | ||
| 143 | Xbyak::util::rbp, | ||
| 144 | Xbyak::util::r12, | ||
| 145 | Xbyak::util::r13, | ||
| 146 | Xbyak::util::r14, | ||
| 147 | Xbyak::util::r15, | ||
| 148 | }); | ||
| 149 | |||
| 150 | constexpr std::size_t ABI_SHADOW_SPACE = 0; | ||
| 151 | |||
| 152 | #endif | ||
| 153 | |||
| 154 | inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment, | ||
| 155 | std::size_t needed_frame_size, s32* out_subtraction, | ||
| 156 | s32* out_xmm_offset) { | ||
| 157 | int count = (regs & ABI_ALL_GPRS).Count(); | ||
| 158 | rsp_alignment -= count * 8; | ||
| 159 | std::size_t subtraction = 0; | ||
| 160 | int xmm_count = (regs & ABI_ALL_XMMS).Count(); | ||
| 161 | if (xmm_count) { | ||
| 162 | // If we have any XMMs to save, we must align the stack here. | ||
| 163 | subtraction = rsp_alignment & 0xF; | ||
| 164 | } | ||
| 165 | subtraction += 0x10 * xmm_count; | ||
| 166 | std::size_t xmm_base_subtraction = subtraction; | ||
| 167 | subtraction += needed_frame_size; | ||
| 168 | subtraction += ABI_SHADOW_SPACE; | ||
| 169 | // Final alignment. | ||
| 170 | rsp_alignment -= subtraction; | ||
| 171 | subtraction += rsp_alignment & 0xF; | ||
| 172 | |||
| 173 | *out_subtraction = (s32)subtraction; | ||
| 174 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); | ||
| 175 | } | ||
| 176 | |||
| 177 | inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, | ||
| 178 | std::size_t rsp_alignment, | ||
| 179 | std::size_t needed_frame_size = 0) { | ||
| 180 | s32 subtraction, xmm_offset; | ||
| 181 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | ||
| 182 | |||
| 183 | for (int reg_index : (regs & ABI_ALL_GPRS)) { | ||
| 184 | code.push(IndexToReg64(reg_index)); | ||
| 185 | } | ||
| 186 | |||
| 187 | if (subtraction != 0) { | ||
| 188 | code.sub(code.rsp, subtraction); | ||
| 189 | } | ||
| 190 | |||
| 191 | for (int reg_index : (regs & ABI_ALL_XMMS)) { | ||
| 192 | code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(reg_index)); | ||
| 193 | xmm_offset += 0x10; | ||
| 194 | } | ||
| 195 | |||
| 196 | return ABI_SHADOW_SPACE; | ||
| 197 | } | ||
| 198 | |||
| 199 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, | ||
| 200 | std::size_t rsp_alignment, | ||
| 201 | std::size_t needed_frame_size = 0) { | ||
| 202 | s32 subtraction, xmm_offset; | ||
| 203 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | ||
| 204 | |||
| 205 | for (int reg_index : (regs & ABI_ALL_XMMS)) { | ||
| 206 | code.movaps(IndexToXmm(reg_index), code.xword[code.rsp + xmm_offset]); | ||
| 207 | xmm_offset += 0x10; | ||
| 208 | } | ||
| 209 | |||
| 210 | if (subtraction != 0) { | ||
| 211 | code.add(code.rsp, subtraction); | ||
| 212 | } | ||
| 213 | |||
| 214 | // GPRs need to be popped in reverse order | ||
| 215 | for (int reg_index = 15; reg_index >= 0; reg_index--) { | ||
| 216 | if (regs[reg_index]) { | ||
| 217 | code.pop(IndexToReg64(reg_index)); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | } // namespace Common::X64 | ||
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h deleted file mode 100644 index 5cc8a8c76..000000000 --- a/src/common/x64/xbyak_util.h +++ /dev/null | |||
| @@ -1,47 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <type_traits> | ||
| 8 | #include <xbyak.h> | ||
| 9 | #include "common/x64/xbyak_abi.h" | ||
| 10 | |||
| 11 | namespace Common::X64 { | ||
| 12 | |||
| 13 | // Constants for use with cmpps/cmpss | ||
| 14 | enum { | ||
| 15 | CMP_EQ = 0, | ||
| 16 | CMP_LT = 1, | ||
| 17 | CMP_LE = 2, | ||
| 18 | CMP_UNORD = 3, | ||
| 19 | CMP_NEQ = 4, | ||
| 20 | CMP_NLT = 5, | ||
| 21 | CMP_NLE = 6, | ||
| 22 | CMP_ORD = 7, | ||
| 23 | }; | ||
| 24 | |||
| 25 | inline bool IsWithin2G(uintptr_t ref, uintptr_t target) { | ||
| 26 | u64 distance = target - (ref + 5); | ||
| 27 | return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL); | ||
| 28 | } | ||
| 29 | |||
| 30 | inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) { | ||
| 31 | return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target); | ||
| 32 | } | ||
| 33 | |||
| 34 | template <typename T> | ||
| 35 | inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { | ||
| 36 | static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); | ||
| 37 | std::size_t addr = reinterpret_cast<std::size_t>(f); | ||
| 38 | if (IsWithin2G(code, addr)) { | ||
| 39 | code.call(f); | ||
| 40 | } else { | ||
| 41 | // ABI_RETURN is a safe temp register to use before a call | ||
| 42 | code.mov(ABI_RETURN, addr); | ||
| 43 | code.call(ABI_RETURN); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | } // namespace Common::X64 | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a355eaca6..e1f21a764 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -12,6 +12,8 @@ add_library(core STATIC | |||
| 12 | core_timing.h | 12 | core_timing.h |
| 13 | core_timing_util.cpp | 13 | core_timing_util.cpp |
| 14 | core_timing_util.h | 14 | core_timing_util.h |
| 15 | cpu_core_manager.cpp | ||
| 16 | cpu_core_manager.h | ||
| 15 | crypto/aes_util.cpp | 17 | crypto/aes_util.cpp |
| 16 | crypto/aes_util.h | 18 | crypto/aes_util.h |
| 17 | crypto/encryption_layer.cpp | 19 | crypto/encryption_layer.cpp |
| @@ -156,6 +158,8 @@ add_library(core STATIC | |||
| 156 | hle/service/am/applets/applets.h | 158 | hle/service/am/applets/applets.h |
| 157 | hle/service/am/applets/software_keyboard.cpp | 159 | hle/service/am/applets/software_keyboard.cpp |
| 158 | hle/service/am/applets/software_keyboard.h | 160 | hle/service/am/applets/software_keyboard.h |
| 161 | hle/service/am/applets/stub_applet.cpp | ||
| 162 | hle/service/am/applets/stub_applet.h | ||
| 159 | hle/service/am/idle.cpp | 163 | hle/service/am/idle.cpp |
| 160 | hle/service/am/idle.h | 164 | hle/service/am/idle.h |
| 161 | hle/service/am/omm.cpp | 165 | hle/service/am/omm.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 6c72fdf4a..795fabc65 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/core_cpu.h" | 15 | #include "core/core_cpu.h" |
| 16 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 17 | #include "core/cpu_core_manager.h" | ||
| 17 | #include "core/file_sys/mode.h" | 18 | #include "core/file_sys/mode.h" |
| 18 | #include "core/file_sys/vfs_concat.h" | 19 | #include "core/file_sys/vfs_concat.h" |
| 19 | #include "core/file_sys/vfs_real.h" | 20 | #include "core/file_sys/vfs_real.h" |
| @@ -28,7 +29,6 @@ | |||
| 28 | #include "core/hle/service/sm/sm.h" | 29 | #include "core/hle/service/sm/sm.h" |
| 29 | #include "core/loader/loader.h" | 30 | #include "core/loader/loader.h" |
| 30 | #include "core/perf_stats.h" | 31 | #include "core/perf_stats.h" |
| 31 | #include "core/settings.h" | ||
| 32 | #include "core/telemetry_session.h" | 32 | #include "core/telemetry_session.h" |
| 33 | #include "frontend/applets/software_keyboard.h" | 33 | #include "frontend/applets/software_keyboard.h" |
| 34 | #include "video_core/debug_utils/debug_utils.h" | 34 | #include "video_core/debug_utils/debug_utils.h" |
| @@ -71,64 +71,22 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 71 | 71 | ||
| 72 | return vfs->OpenFile(path, FileSys::Mode::Read); | 72 | return vfs->OpenFile(path, FileSys::Mode::Read); |
| 73 | } | 73 | } |
| 74 | |||
| 75 | /// Runs a CPU core while the system is powered on | ||
| 76 | void RunCpuCore(Cpu& cpu_state) { | ||
| 77 | while (Core::System::GetInstance().IsPoweredOn()) { | ||
| 78 | cpu_state.RunLoop(true); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } // Anonymous namespace | 74 | } // Anonymous namespace |
| 82 | 75 | ||
| 83 | struct System::Impl { | 76 | struct System::Impl { |
| 84 | Cpu& CurrentCpuCore() { | 77 | Cpu& CurrentCpuCore() { |
| 85 | if (Settings::values.use_multi_core) { | 78 | return cpu_core_manager.GetCurrentCore(); |
| 86 | const auto& search = thread_to_cpu.find(std::this_thread::get_id()); | ||
| 87 | ASSERT(search != thread_to_cpu.end()); | ||
| 88 | ASSERT(search->second); | ||
| 89 | return *search->second; | ||
| 90 | } | ||
| 91 | |||
| 92 | // Otherwise, use single-threaded mode active_core variable | ||
| 93 | return *cpu_cores[active_core]; | ||
| 94 | } | 79 | } |
| 95 | 80 | ||
| 96 | ResultStatus RunLoop(bool tight_loop) { | 81 | ResultStatus RunLoop(bool tight_loop) { |
| 97 | status = ResultStatus::Success; | 82 | status = ResultStatus::Success; |
| 98 | 83 | ||
| 99 | // Update thread_to_cpu in case Core 0 is run from a different host thread | 84 | cpu_core_manager.RunLoop(tight_loop); |
| 100 | thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get(); | ||
| 101 | |||
| 102 | if (GDBStub::IsServerEnabled()) { | ||
| 103 | GDBStub::HandlePacket(); | ||
| 104 | |||
| 105 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | ||
| 106 | // execute. Otherwise, get out of the loop function. | ||
| 107 | if (GDBStub::GetCpuHaltFlag()) { | ||
| 108 | if (GDBStub::GetCpuStepFlag()) { | ||
| 109 | tight_loop = false; | ||
| 110 | } else { | ||
| 111 | return ResultStatus::Success; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | ||
| 117 | cpu_cores[active_core]->RunLoop(tight_loop); | ||
| 118 | if (Settings::values.use_multi_core) { | ||
| 119 | // Cores 1-3 are run on other threads in this mode | ||
| 120 | break; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | if (GDBStub::IsServerEnabled()) { | ||
| 125 | GDBStub::SetCpuStepFlag(false); | ||
| 126 | } | ||
| 127 | 85 | ||
| 128 | return status; | 86 | return status; |
| 129 | } | 87 | } |
| 130 | 88 | ||
| 131 | ResultStatus Init(Frontend::EmuWindow& emu_window) { | 89 | ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { |
| 132 | LOG_DEBUG(HW_Memory, "initialized OK"); | 90 | LOG_DEBUG(HW_Memory, "initialized OK"); |
| 133 | 91 | ||
| 134 | CoreTiming::Init(); | 92 | CoreTiming::Init(); |
| @@ -145,12 +103,6 @@ struct System::Impl { | |||
| 145 | auto main_process = Kernel::Process::Create(kernel, "main"); | 103 | auto main_process = Kernel::Process::Create(kernel, "main"); |
| 146 | kernel.MakeCurrentProcess(main_process.get()); | 104 | kernel.MakeCurrentProcess(main_process.get()); |
| 147 | 105 | ||
| 148 | cpu_barrier = std::make_unique<CpuBarrier>(); | ||
| 149 | cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); | ||
| 150 | for (std::size_t index = 0; index < cpu_cores.size(); ++index) { | ||
| 151 | cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index); | ||
| 152 | } | ||
| 153 | |||
| 154 | telemetry_session = std::make_unique<Core::TelemetrySession>(); | 106 | telemetry_session = std::make_unique<Core::TelemetrySession>(); |
| 155 | service_manager = std::make_shared<Service::SM::ServiceManager>(); | 107 | service_manager = std::make_shared<Service::SM::ServiceManager>(); |
| 156 | 108 | ||
| @@ -164,17 +116,8 @@ struct System::Impl { | |||
| 164 | 116 | ||
| 165 | gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); | 117 | gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); |
| 166 | 118 | ||
| 167 | // Create threads for CPU cores 1-3, and build thread_to_cpu map | 119 | cpu_core_manager.Initialize(system); |
| 168 | // CPU core 0 is run on the main thread | 120 | is_powered_on = true; |
| 169 | thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get(); | ||
| 170 | if (Settings::values.use_multi_core) { | ||
| 171 | for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) { | ||
| 172 | cpu_core_threads[index] = | ||
| 173 | std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1])); | ||
| 174 | thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get(); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | LOG_DEBUG(Core, "Initialized OK"); | 121 | LOG_DEBUG(Core, "Initialized OK"); |
| 179 | 122 | ||
| 180 | // Reset counters and set time origin to current frame | 123 | // Reset counters and set time origin to current frame |
| @@ -184,7 +127,8 @@ struct System::Impl { | |||
| 184 | return ResultStatus::Success; | 127 | return ResultStatus::Success; |
| 185 | } | 128 | } |
| 186 | 129 | ||
| 187 | ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { | 130 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, |
| 131 | const std::string& filepath) { | ||
| 188 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); | 132 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); |
| 189 | 133 | ||
| 190 | if (!app_loader) { | 134 | if (!app_loader) { |
| @@ -201,7 +145,7 @@ struct System::Impl { | |||
| 201 | return ResultStatus::ErrorSystemMode; | 145 | return ResultStatus::ErrorSystemMode; |
| 202 | } | 146 | } |
| 203 | 147 | ||
| 204 | ResultStatus init_result{Init(emu_window)}; | 148 | ResultStatus init_result{Init(system, emu_window)}; |
| 205 | if (init_result != ResultStatus::Success) { | 149 | if (init_result != ResultStatus::Success) { |
| 206 | LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", | 150 | LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", |
| 207 | static_cast<int>(init_result)); | 151 | static_cast<int>(init_result)); |
| @@ -231,6 +175,8 @@ struct System::Impl { | |||
| 231 | Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", | 175 | Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", |
| 232 | perf_results.frametime * 1000.0); | 176 | perf_results.frametime * 1000.0); |
| 233 | 177 | ||
| 178 | is_powered_on = false; | ||
| 179 | |||
| 234 | // Shutdown emulation session | 180 | // Shutdown emulation session |
| 235 | renderer.reset(); | 181 | renderer.reset(); |
| 236 | GDBStub::Shutdown(); | 182 | GDBStub::Shutdown(); |
| @@ -240,19 +186,7 @@ struct System::Impl { | |||
| 240 | gpu_core.reset(); | 186 | gpu_core.reset(); |
| 241 | 187 | ||
| 242 | // Close all CPU/threading state | 188 | // Close all CPU/threading state |
| 243 | cpu_barrier->NotifyEnd(); | 189 | cpu_core_manager.Shutdown(); |
| 244 | if (Settings::values.use_multi_core) { | ||
| 245 | for (auto& thread : cpu_core_threads) { | ||
| 246 | thread->join(); | ||
| 247 | thread.reset(); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | thread_to_cpu.clear(); | ||
| 251 | for (auto& cpu_core : cpu_cores) { | ||
| 252 | cpu_core.reset(); | ||
| 253 | } | ||
| 254 | cpu_exclusive_monitor.reset(); | ||
| 255 | cpu_barrier.reset(); | ||
| 256 | 190 | ||
| 257 | // Shutdown kernel and core timing | 191 | // Shutdown kernel and core timing |
| 258 | kernel.Shutdown(); | 192 | kernel.Shutdown(); |
| @@ -289,11 +223,8 @@ struct System::Impl { | |||
| 289 | std::unique_ptr<VideoCore::RendererBase> renderer; | 223 | std::unique_ptr<VideoCore::RendererBase> renderer; |
| 290 | std::unique_ptr<Tegra::GPU> gpu_core; | 224 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 291 | std::shared_ptr<Tegra::DebugContext> debug_context; | 225 | std::shared_ptr<Tegra::DebugContext> debug_context; |
| 292 | std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor; | 226 | CpuCoreManager cpu_core_manager; |
| 293 | std::unique_ptr<CpuBarrier> cpu_barrier; | 227 | bool is_powered_on = false; |
| 294 | std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; | ||
| 295 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; | ||
| 296 | std::size_t active_core{}; ///< Active core, only used in single thread mode | ||
| 297 | 228 | ||
| 298 | /// Frontend applets | 229 | /// Frontend applets |
| 299 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; | 230 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; |
| @@ -307,9 +238,6 @@ struct System::Impl { | |||
| 307 | ResultStatus status = ResultStatus::Success; | 238 | ResultStatus status = ResultStatus::Success; |
| 308 | std::string status_details = ""; | 239 | std::string status_details = ""; |
| 309 | 240 | ||
| 310 | /// Map of guest threads to CPU cores | ||
| 311 | std::map<std::thread::id, Cpu*> thread_to_cpu; | ||
| 312 | |||
| 313 | Core::PerfStats perf_stats; | 241 | Core::PerfStats perf_stats; |
| 314 | Core::FrameLimiter frame_limiter; | 242 | Core::FrameLimiter frame_limiter; |
| 315 | }; | 243 | }; |
| @@ -334,17 +262,15 @@ System::ResultStatus System::SingleStep() { | |||
| 334 | } | 262 | } |
| 335 | 263 | ||
| 336 | void System::InvalidateCpuInstructionCaches() { | 264 | void System::InvalidateCpuInstructionCaches() { |
| 337 | for (auto& cpu : impl->cpu_cores) { | 265 | impl->cpu_core_manager.InvalidateAllInstructionCaches(); |
| 338 | cpu->ArmInterface().ClearInstructionCache(); | ||
| 339 | } | ||
| 340 | } | 266 | } |
| 341 | 267 | ||
| 342 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { | 268 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { |
| 343 | return impl->Load(emu_window, filepath); | 269 | return impl->Load(*this, emu_window, filepath); |
| 344 | } | 270 | } |
| 345 | 271 | ||
| 346 | bool System::IsPoweredOn() const { | 272 | bool System::IsPoweredOn() const { |
| 347 | return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); | 273 | return impl->is_powered_on; |
| 348 | } | 274 | } |
| 349 | 275 | ||
| 350 | void System::PrepareReschedule() { | 276 | void System::PrepareReschedule() { |
| @@ -408,21 +334,20 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const { | |||
| 408 | } | 334 | } |
| 409 | 335 | ||
| 410 | Cpu& System::CpuCore(std::size_t core_index) { | 336 | Cpu& System::CpuCore(std::size_t core_index) { |
| 411 | ASSERT(core_index < NUM_CPU_CORES); | 337 | return impl->cpu_core_manager.GetCore(core_index); |
| 412 | return *impl->cpu_cores[core_index]; | ||
| 413 | } | 338 | } |
| 414 | 339 | ||
| 415 | const Cpu& System::CpuCore(std::size_t core_index) const { | 340 | const Cpu& System::CpuCore(std::size_t core_index) const { |
| 416 | ASSERT(core_index < NUM_CPU_CORES); | 341 | ASSERT(core_index < NUM_CPU_CORES); |
| 417 | return *impl->cpu_cores[core_index]; | 342 | return impl->cpu_core_manager.GetCore(core_index); |
| 418 | } | 343 | } |
| 419 | 344 | ||
| 420 | ExclusiveMonitor& System::Monitor() { | 345 | ExclusiveMonitor& System::Monitor() { |
| 421 | return *impl->cpu_exclusive_monitor; | 346 | return impl->cpu_core_manager.GetExclusiveMonitor(); |
| 422 | } | 347 | } |
| 423 | 348 | ||
| 424 | const ExclusiveMonitor& System::Monitor() const { | 349 | const ExclusiveMonitor& System::Monitor() const { |
| 425 | return *impl->cpu_exclusive_monitor; | 350 | return impl->cpu_core_manager.GetExclusiveMonitor(); |
| 426 | } | 351 | } |
| 427 | 352 | ||
| 428 | Tegra::GPU& System::GPU() { | 353 | Tegra::GPU& System::GPU() { |
| @@ -506,7 +431,7 @@ const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() cons | |||
| 506 | } | 431 | } |
| 507 | 432 | ||
| 508 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 433 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { |
| 509 | return impl->Init(emu_window); | 434 | return impl->Init(*this, emu_window); |
| 510 | } | 435 | } |
| 511 | 436 | ||
| 512 | void System::Shutdown() { | 437 | void System::Shutdown() { |
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp new file mode 100644 index 000000000..769a6fefa --- /dev/null +++ b/src/core/cpu_core_manager.cpp | |||
| @@ -0,0 +1,142 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "core/arm/exclusive_monitor.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/core_cpu.h" | ||
| 9 | #include "core/cpu_core_manager.h" | ||
| 10 | #include "core/gdbstub/gdbstub.h" | ||
| 11 | #include "core/settings.h" | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | namespace { | ||
| 15 | void RunCpuCore(const System& system, Cpu& cpu_state) { | ||
| 16 | while (system.IsPoweredOn()) { | ||
| 17 | cpu_state.RunLoop(true); | ||
| 18 | } | ||
| 19 | } | ||
| 20 | } // Anonymous namespace | ||
| 21 | |||
| 22 | CpuCoreManager::CpuCoreManager() = default; | ||
| 23 | CpuCoreManager::~CpuCoreManager() = default; | ||
| 24 | |||
| 25 | void CpuCoreManager::Initialize(System& system) { | ||
| 26 | barrier = std::make_unique<CpuBarrier>(); | ||
| 27 | exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); | ||
| 28 | |||
| 29 | for (std::size_t index = 0; index < cores.size(); ++index) { | ||
| 30 | cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index); | ||
| 31 | } | ||
| 32 | |||
| 33 | // Create threads for CPU cores 1-3, and build thread_to_cpu map | ||
| 34 | // CPU core 0 is run on the main thread | ||
| 35 | thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); | ||
| 36 | if (!Settings::values.use_multi_core) { | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | |||
| 40 | for (std::size_t index = 0; index < core_threads.size(); ++index) { | ||
| 41 | core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system), | ||
| 42 | std::ref(*cores[index + 1])); | ||
| 43 | thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get(); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | void CpuCoreManager::Shutdown() { | ||
| 48 | barrier->NotifyEnd(); | ||
| 49 | if (Settings::values.use_multi_core) { | ||
| 50 | for (auto& thread : core_threads) { | ||
| 51 | thread->join(); | ||
| 52 | thread.reset(); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | thread_to_cpu.clear(); | ||
| 57 | for (auto& cpu_core : cores) { | ||
| 58 | cpu_core.reset(); | ||
| 59 | } | ||
| 60 | |||
| 61 | exclusive_monitor.reset(); | ||
| 62 | barrier.reset(); | ||
| 63 | } | ||
| 64 | |||
| 65 | Cpu& CpuCoreManager::GetCore(std::size_t index) { | ||
| 66 | return *cores.at(index); | ||
| 67 | } | ||
| 68 | |||
| 69 | const Cpu& CpuCoreManager::GetCore(std::size_t index) const { | ||
| 70 | return *cores.at(index); | ||
| 71 | } | ||
| 72 | |||
| 73 | ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() { | ||
| 74 | return *exclusive_monitor; | ||
| 75 | } | ||
| 76 | |||
| 77 | const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const { | ||
| 78 | return *exclusive_monitor; | ||
| 79 | } | ||
| 80 | |||
| 81 | Cpu& CpuCoreManager::GetCurrentCore() { | ||
| 82 | if (Settings::values.use_multi_core) { | ||
| 83 | const auto& search = thread_to_cpu.find(std::this_thread::get_id()); | ||
| 84 | ASSERT(search != thread_to_cpu.end()); | ||
| 85 | ASSERT(search->second); | ||
| 86 | return *search->second; | ||
| 87 | } | ||
| 88 | |||
| 89 | // Otherwise, use single-threaded mode active_core variable | ||
| 90 | return *cores[active_core]; | ||
| 91 | } | ||
| 92 | |||
| 93 | const Cpu& CpuCoreManager::GetCurrentCore() const { | ||
| 94 | if (Settings::values.use_multi_core) { | ||
| 95 | const auto& search = thread_to_cpu.find(std::this_thread::get_id()); | ||
| 96 | ASSERT(search != thread_to_cpu.end()); | ||
| 97 | ASSERT(search->second); | ||
| 98 | return *search->second; | ||
| 99 | } | ||
| 100 | |||
| 101 | // Otherwise, use single-threaded mode active_core variable | ||
| 102 | return *cores[active_core]; | ||
| 103 | } | ||
| 104 | |||
| 105 | void CpuCoreManager::RunLoop(bool tight_loop) { | ||
| 106 | // Update thread_to_cpu in case Core 0 is run from a different host thread | ||
| 107 | thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); | ||
| 108 | |||
| 109 | if (GDBStub::IsServerEnabled()) { | ||
| 110 | GDBStub::HandlePacket(); | ||
| 111 | |||
| 112 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | ||
| 113 | // execute. Otherwise, get out of the loop function. | ||
| 114 | if (GDBStub::GetCpuHaltFlag()) { | ||
| 115 | if (GDBStub::GetCpuStepFlag()) { | ||
| 116 | tight_loop = false; | ||
| 117 | } else { | ||
| 118 | return; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | ||
| 124 | cores[active_core]->RunLoop(tight_loop); | ||
| 125 | if (Settings::values.use_multi_core) { | ||
| 126 | // Cores 1-3 are run on other threads in this mode | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | if (GDBStub::IsServerEnabled()) { | ||
| 132 | GDBStub::SetCpuStepFlag(false); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | void CpuCoreManager::InvalidateAllInstructionCaches() { | ||
| 137 | for (auto& cpu : cores) { | ||
| 138 | cpu->ArmInterface().ClearInstructionCache(); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | } // namespace Core | ||
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h new file mode 100644 index 000000000..a4d70ec56 --- /dev/null +++ b/src/core/cpu_core_manager.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <map> | ||
| 9 | #include <memory> | ||
| 10 | #include <thread> | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | |||
| 14 | class Cpu; | ||
| 15 | class CpuBarrier; | ||
| 16 | class ExclusiveMonitor; | ||
| 17 | class System; | ||
| 18 | |||
| 19 | class CpuCoreManager { | ||
| 20 | public: | ||
| 21 | CpuCoreManager(); | ||
| 22 | CpuCoreManager(const CpuCoreManager&) = delete; | ||
| 23 | CpuCoreManager(CpuCoreManager&&) = delete; | ||
| 24 | |||
| 25 | ~CpuCoreManager(); | ||
| 26 | |||
| 27 | CpuCoreManager& operator=(const CpuCoreManager&) = delete; | ||
| 28 | CpuCoreManager& operator=(CpuCoreManager&&) = delete; | ||
| 29 | |||
| 30 | void Initialize(System& system); | ||
| 31 | void Shutdown(); | ||
| 32 | |||
| 33 | Cpu& GetCore(std::size_t index); | ||
| 34 | const Cpu& GetCore(std::size_t index) const; | ||
| 35 | |||
| 36 | Cpu& GetCurrentCore(); | ||
| 37 | const Cpu& GetCurrentCore() const; | ||
| 38 | |||
| 39 | ExclusiveMonitor& GetExclusiveMonitor(); | ||
| 40 | const ExclusiveMonitor& GetExclusiveMonitor() const; | ||
| 41 | |||
| 42 | void RunLoop(bool tight_loop); | ||
| 43 | |||
| 44 | void InvalidateAllInstructionCaches(); | ||
| 45 | |||
| 46 | private: | ||
| 47 | static constexpr std::size_t NUM_CPU_CORES = 4; | ||
| 48 | |||
| 49 | std::unique_ptr<ExclusiveMonitor> exclusive_monitor; | ||
| 50 | std::unique_ptr<CpuBarrier> barrier; | ||
| 51 | std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores; | ||
| 52 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads; | ||
| 53 | std::size_t active_core{}; ///< Active core, only used in single thread mode | ||
| 54 | |||
| 55 | /// Map of guest threads to CPU cores | ||
| 56 | std::map<std::thread::id, Cpu*> thread_to_cpu; | ||
| 57 | }; | ||
| 58 | |||
| 59 | } // namespace Core | ||
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 25f5914b6..a350496f7 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -32,7 +32,18 @@ enum class GamecardSize : u8 { | |||
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | struct GamecardInfo { | 34 | struct GamecardInfo { |
| 35 | std::array<u8, 0x70> data; | 35 | u64_le firmware_version; |
| 36 | u32_le access_control_flags; | ||
| 37 | u32_le read_wait_time1; | ||
| 38 | u32_le read_wait_time2; | ||
| 39 | u32_le write_wait_time1; | ||
| 40 | u32_le write_wait_time2; | ||
| 41 | u32_le firmware_mode; | ||
| 42 | u32_le cup_version; | ||
| 43 | std::array<u8, 4> reserved1; | ||
| 44 | u64_le update_partition_hash; | ||
| 45 | u64_le cup_id; | ||
| 46 | std::array<u8, 0x38> reserved2; | ||
| 36 | }; | 47 | }; |
| 37 | static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size."); | 48 | static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size."); |
| 38 | 49 | ||
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 8d062eb3e..e8df08724 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -26,6 +26,11 @@ namespace FileSys { | |||
| 26 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | 26 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; |
| 27 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 27 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; |
| 28 | 28 | ||
| 29 | constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{ | ||
| 30 | "main", "main.npdm", "rtld", "sdk", "subsdk0", "subsdk1", "subsdk2", | ||
| 31 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", | ||
| 32 | }; | ||
| 33 | |||
| 29 | struct NSOBuildHeader { | 34 | struct NSOBuildHeader { |
| 30 | u32_le magic; | 35 | u32_le magic; |
| 31 | INSERT_PADDING_BYTES(0x3C); | 36 | INSERT_PADDING_BYTES(0x3C); |
| @@ -57,6 +62,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 57 | if (exefs == nullptr) | 62 | if (exefs == nullptr) |
| 58 | return exefs; | 63 | return exefs; |
| 59 | 64 | ||
| 65 | if (Settings::values.dump_exefs) { | ||
| 66 | LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); | ||
| 67 | const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | ||
| 68 | if (dump_dir != nullptr) { | ||
| 69 | const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); | ||
| 70 | VfsRawCopyD(exefs, exefs_dir); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 60 | const auto installed = Service::FileSystem::GetUnionContents(); | 74 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 61 | 75 | ||
| 62 | // Game Updates | 76 | // Game Updates |
| @@ -70,6 +84,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 70 | exefs = update->GetExeFS(); | 84 | exefs = update->GetExeFS(); |
| 71 | } | 85 | } |
| 72 | 86 | ||
| 87 | // LayeredExeFS | ||
| 88 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 89 | if (load_dir != nullptr && load_dir->GetSize() > 0) { | ||
| 90 | auto patch_dirs = load_dir->GetSubdirectories(); | ||
| 91 | std::sort( | ||
| 92 | patch_dirs.begin(), patch_dirs.end(), | ||
| 93 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 94 | |||
| 95 | std::vector<VirtualDir> layers; | ||
| 96 | layers.reserve(patch_dirs.size() + 1); | ||
| 97 | for (const auto& subdir : patch_dirs) { | ||
| 98 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | ||
| 99 | if (exefs_dir != nullptr) | ||
| 100 | layers.push_back(std::move(exefs_dir)); | ||
| 101 | } | ||
| 102 | layers.push_back(exefs); | ||
| 103 | |||
| 104 | auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); | ||
| 105 | if (layered != nullptr) { | ||
| 106 | LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); | ||
| 107 | exefs = std::move(layered); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 73 | return exefs; | 111 | return exefs; |
| 74 | } | 112 | } |
| 75 | 113 | ||
| @@ -314,18 +352,25 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 314 | if (IsDirValidAndNonEmpty(exefs_dir)) { | 352 | if (IsDirValidAndNonEmpty(exefs_dir)) { |
| 315 | bool ips = false; | 353 | bool ips = false; |
| 316 | bool ipswitch = false; | 354 | bool ipswitch = false; |
| 355 | bool layeredfs = false; | ||
| 317 | 356 | ||
| 318 | for (const auto& file : exefs_dir->GetFiles()) { | 357 | for (const auto& file : exefs_dir->GetFiles()) { |
| 319 | if (file->GetExtension() == "ips") | 358 | if (file->GetExtension() == "ips") { |
| 320 | ips = true; | 359 | ips = true; |
| 321 | else if (file->GetExtension() == "pchtxt") | 360 | } else if (file->GetExtension() == "pchtxt") { |
| 322 | ipswitch = true; | 361 | ipswitch = true; |
| 362 | } else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(), | ||
| 363 | file->GetName()) != EXEFS_FILE_NAMES.end()) { | ||
| 364 | layeredfs = true; | ||
| 365 | } | ||
| 323 | } | 366 | } |
| 324 | 367 | ||
| 325 | if (ips) | 368 | if (ips) |
| 326 | AppendCommaIfNotEmpty(types, "IPS"); | 369 | AppendCommaIfNotEmpty(types, "IPS"); |
| 327 | if (ipswitch) | 370 | if (ipswitch) |
| 328 | AppendCommaIfNotEmpty(types, "IPSwitch"); | 371 | AppendCommaIfNotEmpty(types, "IPSwitch"); |
| 372 | if (layeredfs) | ||
| 373 | AppendCommaIfNotEmpty(types, "LayeredExeFS"); | ||
| 329 | } | 374 | } |
| 330 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) | 375 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) |
| 331 | AppendCommaIfNotEmpty(types, "LayeredFS"); | 376 | AppendCommaIfNotEmpty(types, "LayeredFS"); |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index bdcc889e0..687dea409 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -71,10 +71,6 @@ constexpr u32 PSTATE_REGISTER = 33; | |||
| 71 | constexpr u32 UC_ARM64_REG_Q0 = 34; | 71 | constexpr u32 UC_ARM64_REG_Q0 = 34; |
| 72 | constexpr u32 FPCR_REGISTER = 66; | 72 | constexpr u32 FPCR_REGISTER = 66; |
| 73 | 73 | ||
| 74 | // TODO/WiP - Used while working on support for FPU | ||
| 75 | constexpr u32 TODO_DUMMY_REG_997 = 997; | ||
| 76 | constexpr u32 TODO_DUMMY_REG_998 = 998; | ||
| 77 | |||
| 78 | // For sample XML files see the GDB source /gdb/features | 74 | // For sample XML files see the GDB source /gdb/features |
| 79 | // GDB also wants the l character at the start | 75 | // GDB also wants the l character at the start |
| 80 | // This XML defines what the registers are for this specific ARM device | 76 | // This XML defines what the registers are for this specific ARM device |
| @@ -260,6 +256,36 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) | |||
| 260 | } | 256 | } |
| 261 | } | 257 | } |
| 262 | 258 | ||
| 259 | static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) { | ||
| 260 | if (!thread) { | ||
| 261 | return u128{0}; | ||
| 262 | } | ||
| 263 | |||
| 264 | auto& thread_context = thread->GetContext(); | ||
| 265 | |||
| 266 | if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { | ||
| 267 | return thread_context.vector_registers[id - UC_ARM64_REG_Q0]; | ||
| 268 | } else if (id == FPCR_REGISTER) { | ||
| 269 | return u128{thread_context.fpcr, 0}; | ||
| 270 | } else { | ||
| 271 | return u128{0}; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) { | ||
| 276 | if (!thread) { | ||
| 277 | return; | ||
| 278 | } | ||
| 279 | |||
| 280 | auto& thread_context = thread->GetContext(); | ||
| 281 | |||
| 282 | if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { | ||
| 283 | thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val; | ||
| 284 | } else if (id == FPCR_REGISTER) { | ||
| 285 | thread_context.fpcr = val[0]; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 263 | /** | 289 | /** |
| 264 | * Turns hex string character into the equivalent byte. | 290 | * Turns hex string character into the equivalent byte. |
| 265 | * | 291 | * |
| @@ -409,6 +435,27 @@ static u64 GdbHexToLong(const u8* src) { | |||
| 409 | return output; | 435 | return output; |
| 410 | } | 436 | } |
| 411 | 437 | ||
| 438 | /** | ||
| 439 | * Convert a gdb-formatted hex string into a u128. | ||
| 440 | * | ||
| 441 | * @param src Pointer to hex string. | ||
| 442 | */ | ||
| 443 | static u128 GdbHexToU128(const u8* src) { | ||
| 444 | u128 output; | ||
| 445 | |||
| 446 | for (int i = 0; i < 16; i += 2) { | ||
| 447 | output[0] = (output[0] << 4) | HexCharToValue(src[15 - i - 1]); | ||
| 448 | output[0] = (output[0] << 4) | HexCharToValue(src[15 - i]); | ||
| 449 | } | ||
| 450 | |||
| 451 | for (int i = 0; i < 16; i += 2) { | ||
| 452 | output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i - 1]); | ||
| 453 | output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i]); | ||
| 454 | } | ||
| 455 | |||
| 456 | return output; | ||
| 457 | } | ||
| 458 | |||
| 412 | /// Read a byte from the gdb client. | 459 | /// Read a byte from the gdb client. |
| 413 | static u8 ReadByte() { | 460 | static u8 ReadByte() { |
| 414 | u8 c; | 461 | u8 c; |
| @@ -599,8 +646,7 @@ static void HandleQuery() { | |||
| 599 | for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { | 646 | for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { |
| 600 | const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList(); | 647 | const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList(); |
| 601 | for (const auto& thread : threads) { | 648 | for (const auto& thread : threads) { |
| 602 | val += fmt::format("{:x}", thread->GetThreadID()); | 649 | val += fmt::format("{:x},", thread->GetThreadID()); |
| 603 | val += ","; | ||
| 604 | } | 650 | } |
| 605 | } | 651 | } |
| 606 | val.pop_back(); | 652 | val.pop_back(); |
| @@ -791,11 +837,15 @@ static void ReadRegister() { | |||
| 791 | } else if (id == PSTATE_REGISTER) { | 837 | } else if (id == PSTATE_REGISTER) { |
| 792 | IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); | 838 | IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); |
| 793 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { | 839 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { |
| 794 | LongToGdbHex(reply, RegRead(id, current_thread)); | 840 | u128 r = FpuRead(id, current_thread); |
| 841 | LongToGdbHex(reply, r[0]); | ||
| 842 | LongToGdbHex(reply + 16, r[1]); | ||
| 795 | } else if (id == FPCR_REGISTER) { | 843 | } else if (id == FPCR_REGISTER) { |
| 796 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); | 844 | u128 r = FpuRead(id, current_thread); |
| 797 | } else { | 845 | IntToGdbHex(reply, static_cast<u32>(r[0])); |
| 798 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); | 846 | } else if (id == FPCR_REGISTER + 1) { |
| 847 | u128 r = FpuRead(id, current_thread); | ||
| 848 | IntToGdbHex(reply, static_cast<u32>(r[0] >> 32)); | ||
| 799 | } | 849 | } |
| 800 | 850 | ||
| 801 | SendReply(reinterpret_cast<char*>(reply)); | 851 | SendReply(reinterpret_cast<char*>(reply)); |
| @@ -822,13 +872,18 @@ static void ReadRegisters() { | |||
| 822 | 872 | ||
| 823 | bufptr += 8; | 873 | bufptr += 8; |
| 824 | 874 | ||
| 825 | for (u32 reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) { | 875 | u128 r; |
| 826 | LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); | 876 | |
| 877 | for (u32 reg = UC_ARM64_REG_Q0; reg < FPCR_REGISTER; reg++) { | ||
| 878 | r = FpuRead(reg, current_thread); | ||
| 879 | LongToGdbHex(bufptr + reg * 32, r[0]); | ||
| 880 | LongToGdbHex(bufptr + reg * 32 + 16, r[1]); | ||
| 827 | } | 881 | } |
| 828 | 882 | ||
| 829 | bufptr += 32 * 32; | 883 | bufptr += 32 * 32; |
| 830 | 884 | ||
| 831 | LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread)); | 885 | r = FpuRead(FPCR_REGISTER, current_thread); |
| 886 | IntToGdbHex(bufptr, static_cast<u32>(r[0])); | ||
| 832 | 887 | ||
| 833 | bufptr += 8; | 888 | bufptr += 8; |
| 834 | 889 | ||
| @@ -853,14 +908,12 @@ static void WriteRegister() { | |||
| 853 | } else if (id == PSTATE_REGISTER) { | 908 | } else if (id == PSTATE_REGISTER) { |
| 854 | RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); | 909 | RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); |
| 855 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { | 910 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { |
| 856 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | 911 | FpuWrite(id, GdbHexToU128(buffer_ptr), current_thread); |
| 857 | } else if (id == FPCR_REGISTER) { | 912 | } else if (id == FPCR_REGISTER) { |
| 858 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); | 913 | } else if (id == FPCR_REGISTER + 1) { |
| 859 | } else { | ||
| 860 | RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread); | ||
| 861 | } | 914 | } |
| 862 | 915 | ||
| 863 | // Update Unicorn context skipping scheduler, no running threads at this point | 916 | // Update ARM context, skipping scheduler - no running threads at this point |
| 864 | Core::System::GetInstance() | 917 | Core::System::GetInstance() |
| 865 | .ArmInterface(current_core) | 918 | .ArmInterface(current_core) |
| 866 | .LoadContext(current_thread->GetContext()); | 919 | .LoadContext(current_thread->GetContext()); |
| @@ -885,13 +938,13 @@ static void WriteRegisters() { | |||
| 885 | } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { | 938 | } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { |
| 886 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 939 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 887 | } else if (reg == FPCR_REGISTER) { | 940 | } else if (reg == FPCR_REGISTER) { |
| 888 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 941 | RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 889 | } else { | 942 | } else if (reg == FPCR_REGISTER + 1) { |
| 890 | UNIMPLEMENTED(); | 943 | RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 891 | } | 944 | } |
| 892 | } | 945 | } |
| 893 | 946 | ||
| 894 | // Update Unicorn context skipping scheduler, no running threads at this point | 947 | // Update ARM context, skipping scheduler - no running threads at this point |
| 895 | Core::System::GetInstance() | 948 | Core::System::GetInstance() |
| 896 | .ArmInterface(current_core) | 949 | .ArmInterface(current_core) |
| 897 | .LoadContext(current_thread->GetContext()); | 950 | .LoadContext(current_thread->GetContext()); |
| @@ -917,12 +970,6 @@ static void ReadMemory() { | |||
| 917 | SendReply("E01"); | 970 | SendReply("E01"); |
| 918 | } | 971 | } |
| 919 | 972 | ||
| 920 | const auto& vm_manager = Core::CurrentProcess()->VMManager(); | ||
| 921 | if (addr < vm_manager.GetCodeRegionBaseAddress() || | ||
| 922 | addr >= vm_manager.GetMapRegionEndAddress()) { | ||
| 923 | return SendReply("E00"); | ||
| 924 | } | ||
| 925 | |||
| 926 | if (!Memory::IsValidVirtualAddress(addr)) { | 973 | if (!Memory::IsValidVirtualAddress(addr)) { |
| 927 | return SendReply("E00"); | 974 | return SendReply("E00"); |
| 928 | } | 975 | } |
| @@ -967,7 +1014,7 @@ void Break(bool is_memory_break) { | |||
| 967 | static void Step() { | 1014 | static void Step() { |
| 968 | if (command_length > 1) { | 1015 | if (command_length > 1) { |
| 969 | RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); | 1016 | RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); |
| 970 | // Update Unicorn context skipping scheduler, no running threads at this point | 1017 | // Update ARM context, skipping scheduler - no running threads at this point |
| 971 | Core::System::GetInstance() | 1018 | Core::System::GetInstance() |
| 972 | .ArmInterface(current_core) | 1019 | .ArmInterface(current_core) |
| 973 | .LoadContext(current_thread->GetContext()); | 1020 | .LoadContext(current_thread->GetContext()); |
| @@ -1010,7 +1057,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { | |||
| 1010 | breakpoint.addr = addr; | 1057 | breakpoint.addr = addr; |
| 1011 | breakpoint.len = len; | 1058 | breakpoint.len = len; |
| 1012 | Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); | 1059 | Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); |
| 1013 | static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}}; | 1060 | static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4}; |
| 1014 | Memory::WriteBlock(addr, btrap.data(), btrap.size()); | 1061 | Memory::WriteBlock(addr, btrap.data(), btrap.size()); |
| 1015 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 1062 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); |
| 1016 | p.insert({addr, breakpoint}); | 1063 | p.insert({addr, breakpoint}); |
| @@ -1321,13 +1368,15 @@ void SetCpuStepFlag(bool is_step) { | |||
| 1321 | } | 1368 | } |
| 1322 | 1369 | ||
| 1323 | void SendTrap(Kernel::Thread* thread, int trap) { | 1370 | void SendTrap(Kernel::Thread* thread, int trap) { |
| 1324 | if (send_trap) { | 1371 | if (!send_trap) { |
| 1325 | if (!halt_loop || current_thread == thread) { | 1372 | return; |
| 1326 | current_thread = thread; | ||
| 1327 | SendSignal(thread, trap); | ||
| 1328 | } | ||
| 1329 | halt_loop = true; | ||
| 1330 | send_trap = false; | ||
| 1331 | } | 1373 | } |
| 1374 | |||
| 1375 | if (!halt_loop || current_thread == thread) { | ||
| 1376 | current_thread = thread; | ||
| 1377 | SendSignal(thread, trap); | ||
| 1378 | } | ||
| 1379 | halt_loop = true; | ||
| 1380 | send_trap = false; | ||
| 1332 | } | 1381 | } |
| 1333 | }; // namespace GDBStub | 1382 | }; // namespace GDBStub |
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 5ee5c05e3..1bf79b692 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp | |||
| @@ -12,12 +12,23 @@ | |||
| 12 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 13 | 13 | ||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | namespace { | ||
| 16 | constexpr u16 GetSlot(Handle handle) { | ||
| 17 | return handle >> 15; | ||
| 18 | } | ||
| 19 | |||
| 20 | constexpr u16 GetGeneration(Handle handle) { | ||
| 21 | return handle & 0x7FFF; | ||
| 22 | } | ||
| 23 | } // Anonymous namespace | ||
| 15 | 24 | ||
| 16 | HandleTable::HandleTable() { | 25 | HandleTable::HandleTable() { |
| 17 | next_generation = 1; | 26 | next_generation = 1; |
| 18 | Clear(); | 27 | Clear(); |
| 19 | } | 28 | } |
| 20 | 29 | ||
| 30 | HandleTable::~HandleTable() = default; | ||
| 31 | |||
| 21 | ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { | 32 | ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { |
| 22 | DEBUG_ASSERT(obj != nullptr); | 33 | DEBUG_ASSERT(obj != nullptr); |
| 23 | 34 | ||
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index 9e2f33e8a..e3f3e3fb8 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h | |||
| @@ -43,6 +43,7 @@ enum KernelHandle : Handle { | |||
| 43 | class HandleTable final : NonCopyable { | 43 | class HandleTable final : NonCopyable { |
| 44 | public: | 44 | public: |
| 45 | HandleTable(); | 45 | HandleTable(); |
| 46 | ~HandleTable(); | ||
| 46 | 47 | ||
| 47 | /** | 48 | /** |
| 48 | * Allocates a handle for the given object. | 49 | * Allocates a handle for the given object. |
| @@ -89,18 +90,8 @@ public: | |||
| 89 | void Clear(); | 90 | void Clear(); |
| 90 | 91 | ||
| 91 | private: | 92 | private: |
| 92 | /** | 93 | /// This is the maximum limit of handles allowed per process in Horizon |
| 93 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | 94 | static constexpr std::size_t MAX_COUNT = 1024; |
| 94 | * reduced by ExHeader values, but this is not emulated here. | ||
| 95 | */ | ||
| 96 | static const std::size_t MAX_COUNT = 4096; | ||
| 97 | |||
| 98 | static u16 GetSlot(Handle handle) { | ||
| 99 | return handle >> 15; | ||
| 100 | } | ||
| 101 | static u16 GetGeneration(Handle handle) { | ||
| 102 | return handle & 0x7FFF; | ||
| 103 | } | ||
| 104 | 95 | ||
| 105 | /// Stores the Object referenced by the handle or null if the slot is empty. | 96 | /// Stores the Object referenced by the handle or null if the slot is empty. |
| 106 | std::array<SharedPtr<Object>, MAX_COUNT> objects; | 97 | std::array<SharedPtr<Object>, MAX_COUNT> objects; |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 1412257a0..7ca538401 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <random> | ||
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 10 | #include "core/core.h" |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 230e395ff..ada845c7f 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <bitset> | 8 | #include <bitset> |
| 9 | #include <cstddef> | 9 | #include <cstddef> |
| 10 | #include <memory> | 10 | #include <memory> |
| 11 | #include <random> | ||
| 12 | #include <string> | 11 | #include <string> |
| 13 | #include <vector> | 12 | #include <vector> |
| 14 | #include <boost/container/static_vector.hpp> | 13 | #include <boost/container/static_vector.hpp> |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index a016a86b6..0494581f5 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -61,7 +61,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce | |||
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | SharedPtr<SharedMemory> SharedMemory::CreateForApplet( | 63 | SharedPtr<SharedMemory> SharedMemory::CreateForApplet( |
| 64 | KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size, | 64 | KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size, |
| 65 | MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { | 65 | MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { |
| 66 | SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); | 66 | SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); |
| 67 | 67 | ||
| @@ -78,10 +78,10 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( | |||
| 78 | return shared_memory; | 78 | return shared_memory; |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, | 81 | ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions, |
| 82 | MemoryPermission other_permissions) { | 82 | MemoryPermission other_permissions) { |
| 83 | const MemoryPermission own_other_permissions = | 83 | const MemoryPermission own_other_permissions = |
| 84 | target_process == owner_process ? this->permissions : this->other_permissions; | 84 | &target_process == owner_process ? this->permissions : this->other_permissions; |
| 85 | 85 | ||
| 86 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare | 86 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare |
| 87 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { | 87 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { |
| @@ -106,7 +106,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi | |||
| 106 | VAddr target_address = address; | 106 | VAddr target_address = address; |
| 107 | 107 | ||
| 108 | // Map the memory block into the target process | 108 | // Map the memory block into the target process |
| 109 | auto result = target_process->VMManager().MapMemoryBlock( | 109 | auto result = target_process.VMManager().MapMemoryBlock( |
| 110 | target_address, backing_block, backing_block_offset, size, MemoryState::Shared); | 110 | target_address, backing_block, backing_block_offset, size, MemoryState::Shared); |
| 111 | if (result.Failed()) { | 111 | if (result.Failed()) { |
| 112 | LOG_ERROR( | 112 | LOG_ERROR( |
| @@ -116,14 +116,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi | |||
| 116 | return result.Code(); | 116 | return result.Code(); |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | return target_process->VMManager().ReprotectRange(target_address, size, | 119 | return target_process.VMManager().ReprotectRange(target_address, size, |
| 120 | ConvertPermissions(permissions)); | 120 | ConvertPermissions(permissions)); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { | 123 | ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) { |
| 124 | // TODO(Subv): Verify what happens if the application tries to unmap an address that is not | 124 | // TODO(Subv): Verify what happens if the application tries to unmap an address that is not |
| 125 | // mapped to a SharedMemory. | 125 | // mapped to a SharedMemory. |
| 126 | return target_process->VMManager().UnmapRange(address, size); | 126 | return target_process.VMManager().UnmapRange(address, size); |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { | 129 | VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { |
| @@ -132,7 +132,11 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { | |||
| 132 | return static_cast<VMAPermission>(masked_permissions); | 132 | return static_cast<VMAPermission>(masked_permissions); |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | u8* SharedMemory::GetPointer(u32 offset) { | 135 | u8* SharedMemory::GetPointer(std::size_t offset) { |
| 136 | return backing_block->data() + backing_block_offset + offset; | ||
| 137 | } | ||
| 138 | |||
| 139 | const u8* SharedMemory::GetPointer(std::size_t offset) const { | ||
| 136 | return backing_block->data() + backing_block_offset + offset; | 140 | return backing_block->data() + backing_block_offset + offset; |
| 137 | } | 141 | } |
| 138 | 142 | ||
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 2c06bb7ce..0b48db699 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -64,7 +64,7 @@ public: | |||
| 64 | */ | 64 | */ |
| 65 | static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel, | 65 | static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel, |
| 66 | std::shared_ptr<std::vector<u8>> heap_block, | 66 | std::shared_ptr<std::vector<u8>> heap_block, |
| 67 | u32 offset, u32 size, | 67 | std::size_t offset, u64 size, |
| 68 | MemoryPermission permissions, | 68 | MemoryPermission permissions, |
| 69 | MemoryPermission other_permissions, | 69 | MemoryPermission other_permissions, |
| 70 | std::string name = "Unknown Applet"); | 70 | std::string name = "Unknown Applet"); |
| @@ -81,6 +81,11 @@ public: | |||
| 81 | return HANDLE_TYPE; | 81 | return HANDLE_TYPE; |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | /// Gets the size of the underlying memory block in bytes. | ||
| 85 | u64 GetSize() const { | ||
| 86 | return size; | ||
| 87 | } | ||
| 88 | |||
| 84 | /** | 89 | /** |
| 85 | * Converts the specified MemoryPermission into the equivalent VMAPermission. | 90 | * Converts the specified MemoryPermission into the equivalent VMAPermission. |
| 86 | * @param permission The MemoryPermission to convert. | 91 | * @param permission The MemoryPermission to convert. |
| @@ -94,44 +99,51 @@ public: | |||
| 94 | * @param permissions Memory block map permissions (specified by SVC field) | 99 | * @param permissions Memory block map permissions (specified by SVC field) |
| 95 | * @param other_permissions Memory block map other permissions (specified by SVC field) | 100 | * @param other_permissions Memory block map other permissions (specified by SVC field) |
| 96 | */ | 101 | */ |
| 97 | ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, | 102 | ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions, |
| 98 | MemoryPermission other_permissions); | 103 | MemoryPermission other_permissions); |
| 99 | 104 | ||
| 100 | /** | 105 | /** |
| 101 | * Unmaps a shared memory block from the specified address in system memory | 106 | * Unmaps a shared memory block from the specified address in system memory |
| 102 | * @param target_process Process from which to umap the memory block. | 107 | * @param target_process Process from which to unmap the memory block. |
| 103 | * @param address Address in system memory where the shared memory block is mapped | 108 | * @param address Address in system memory where the shared memory block is mapped |
| 104 | * @return Result code of the unmap operation | 109 | * @return Result code of the unmap operation |
| 105 | */ | 110 | */ |
| 106 | ResultCode Unmap(Process* target_process, VAddr address); | 111 | ResultCode Unmap(Process& target_process, VAddr address); |
| 107 | 112 | ||
| 108 | /** | 113 | /** |
| 109 | * Gets a pointer to the shared memory block | 114 | * Gets a pointer to the shared memory block |
| 110 | * @param offset Offset from the start of the shared memory block to get pointer | 115 | * @param offset Offset from the start of the shared memory block to get pointer |
| 111 | * @return Pointer to the shared memory block from the specified offset | 116 | * @return A pointer to the shared memory block from the specified offset |
| 112 | */ | 117 | */ |
| 113 | u8* GetPointer(u32 offset = 0); | 118 | u8* GetPointer(std::size_t offset = 0); |
| 119 | |||
| 120 | /** | ||
| 121 | * Gets a constant pointer to the shared memory block | ||
| 122 | * @param offset Offset from the start of the shared memory block to get pointer | ||
| 123 | * @return A constant pointer to the shared memory block from the specified offset | ||
| 124 | */ | ||
| 125 | const u8* GetPointer(std::size_t offset = 0) const; | ||
| 126 | |||
| 127 | private: | ||
| 128 | explicit SharedMemory(KernelCore& kernel); | ||
| 129 | ~SharedMemory() override; | ||
| 114 | 130 | ||
| 115 | /// Process that created this shared memory block. | ||
| 116 | SharedPtr<Process> owner_process; | ||
| 117 | /// Address of shared memory block in the owner process if specified. | ||
| 118 | VAddr base_address; | ||
| 119 | /// Backing memory for this shared memory block. | 131 | /// Backing memory for this shared memory block. |
| 120 | std::shared_ptr<std::vector<u8>> backing_block; | 132 | std::shared_ptr<std::vector<u8>> backing_block; |
| 121 | /// Offset into the backing block for this shared memory. | 133 | /// Offset into the backing block for this shared memory. |
| 122 | std::size_t backing_block_offset; | 134 | std::size_t backing_block_offset = 0; |
| 123 | /// Size of the memory block. Page-aligned. | 135 | /// Size of the memory block. Page-aligned. |
| 124 | u64 size; | 136 | u64 size = 0; |
| 125 | /// Permission restrictions applied to the process which created the block. | 137 | /// Permission restrictions applied to the process which created the block. |
| 126 | MemoryPermission permissions; | 138 | MemoryPermission permissions{}; |
| 127 | /// Permission restrictions applied to other processes mapping the block. | 139 | /// Permission restrictions applied to other processes mapping the block. |
| 128 | MemoryPermission other_permissions; | 140 | MemoryPermission other_permissions{}; |
| 141 | /// Process that created this shared memory block. | ||
| 142 | SharedPtr<Process> owner_process; | ||
| 143 | /// Address of shared memory block in the owner process if specified. | ||
| 144 | VAddr base_address = 0; | ||
| 129 | /// Name of shared memory object. | 145 | /// Name of shared memory object. |
| 130 | std::string name; | 146 | std::string name; |
| 131 | |||
| 132 | private: | ||
| 133 | explicit SharedMemory(KernelCore& kernel); | ||
| 134 | ~SharedMemory() override; | ||
| 135 | }; | 147 | }; |
| 136 | 148 | ||
| 137 | } // namespace Kernel | 149 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 51c367de7..b8b6b4d49 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -789,7 +789,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 789 | return ERR_INVALID_MEMORY_RANGE; | 789 | return ERR_INVALID_MEMORY_RANGE; |
| 790 | } | 790 | } |
| 791 | 791 | ||
| 792 | return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare); | 792 | return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); |
| 793 | } | 793 | } |
| 794 | 794 | ||
| 795 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { | 795 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { |
| @@ -819,7 +819,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 819 | return ERR_INVALID_MEMORY_RANGE; | 819 | return ERR_INVALID_MEMORY_RANGE; |
| 820 | } | 820 | } |
| 821 | 821 | ||
| 822 | return shared_memory->Unmap(current_process, addr); | 822 | return shared_memory->Unmap(*current_process, addr); |
| 823 | } | 823 | } |
| 824 | 824 | ||
| 825 | /// Query process memory | 825 | /// Query process memory |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index fd14af1e7..4f17b52f9 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -6,8 +6,6 @@ | |||
| 6 | #include <cinttypes> | 6 | #include <cinttypes> |
| 7 | #include <cstring> | 7 | #include <cstring> |
| 8 | #include <stack> | 8 | #include <stack> |
| 9 | #include "applets/applets.h" | ||
| 10 | #include "applets/software_keyboard.h" | ||
| 11 | #include "audio_core/audio_renderer.h" | 9 | #include "audio_core/audio_renderer.h" |
| 12 | #include "core/core.h" | 10 | #include "core/core.h" |
| 13 | #include "core/hle/ipc_helpers.h" | 11 | #include "core/hle/ipc_helpers.h" |
| @@ -18,6 +16,9 @@ | |||
| 18 | #include "core/hle/service/am/am.h" | 16 | #include "core/hle/service/am/am.h" |
| 19 | #include "core/hle/service/am/applet_ae.h" | 17 | #include "core/hle/service/am/applet_ae.h" |
| 20 | #include "core/hle/service/am/applet_oe.h" | 18 | #include "core/hle/service/am/applet_oe.h" |
| 19 | #include "core/hle/service/am/applets/applets.h" | ||
| 20 | #include "core/hle/service/am/applets/software_keyboard.h" | ||
| 21 | #include "core/hle/service/am/applets/stub_applet.h" | ||
| 21 | #include "core/hle/service/am/idle.h" | 22 | #include "core/hle/service/am/idle.h" |
| 22 | #include "core/hle/service/am/omm.h" | 23 | #include "core/hle/service/am/omm.h" |
| 23 | #include "core/hle/service/am/spsm.h" | 24 | #include "core/hle/service/am/spsm.h" |
| @@ -482,11 +483,15 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& | |||
| 482 | rb.Push(RESULT_SUCCESS); | 483 | rb.Push(RESULT_SUCCESS); |
| 483 | 484 | ||
| 484 | if (Settings::values.use_docked_mode) { | 485 | if (Settings::values.use_docked_mode) { |
| 485 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); | 486 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * |
| 486 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); | 487 | static_cast<u32>(Settings::values.resolution_factor)); |
| 488 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * | ||
| 489 | static_cast<u32>(Settings::values.resolution_factor)); | ||
| 487 | } else { | 490 | } else { |
| 488 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); | 491 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * |
| 489 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); | 492 | static_cast<u32>(Settings::values.resolution_factor)); |
| 493 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * | ||
| 494 | static_cast<u32>(Settings::values.resolution_factor)); | ||
| 490 | } | 495 | } |
| 491 | 496 | ||
| 492 | LOG_DEBUG(Service_AM, "called"); | 497 | LOG_DEBUG(Service_AM, "called"); |
| @@ -532,8 +537,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { | |||
| 532 | class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { | 537 | class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { |
| 533 | public: | 538 | public: |
| 534 | explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet) | 539 | explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet) |
| 535 | : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)), | 540 | : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) { |
| 536 | broker(std::make_shared<Applets::AppletDataBroker>()) { | ||
| 537 | // clang-format off | 541 | // clang-format off |
| 538 | static const FunctionInfo functions[] = { | 542 | static const FunctionInfo functions[] = { |
| 539 | {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, | 543 | {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, |
| @@ -562,7 +566,7 @@ public: | |||
| 562 | 566 | ||
| 563 | private: | 567 | private: |
| 564 | void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { | 568 | void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { |
| 565 | const auto event = broker->GetStateChangedEvent(); | 569 | const auto event = applet->GetBroker().GetStateChangedEvent(); |
| 566 | event->Signal(); | 570 | event->Signal(); |
| 567 | 571 | ||
| 568 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 572 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| @@ -590,7 +594,7 @@ private: | |||
| 590 | void Start(Kernel::HLERequestContext& ctx) { | 594 | void Start(Kernel::HLERequestContext& ctx) { |
| 591 | ASSERT(applet != nullptr); | 595 | ASSERT(applet != nullptr); |
| 592 | 596 | ||
| 593 | applet->Initialize(broker); | 597 | applet->Initialize(); |
| 594 | applet->Execute(); | 598 | applet->Execute(); |
| 595 | 599 | ||
| 596 | IPC::ResponseBuilder rb{ctx, 2}; | 600 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -601,7 +605,7 @@ private: | |||
| 601 | 605 | ||
| 602 | void PushInData(Kernel::HLERequestContext& ctx) { | 606 | void PushInData(Kernel::HLERequestContext& ctx) { |
| 603 | IPC::RequestParser rp{ctx}; | 607 | IPC::RequestParser rp{ctx}; |
| 604 | broker->PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); | 608 | applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); |
| 605 | 609 | ||
| 606 | IPC::ResponseBuilder rb{ctx, 2}; | 610 | IPC::ResponseBuilder rb{ctx, 2}; |
| 607 | rb.Push(RESULT_SUCCESS); | 611 | rb.Push(RESULT_SUCCESS); |
| @@ -612,7 +616,7 @@ private: | |||
| 612 | void PopOutData(Kernel::HLERequestContext& ctx) { | 616 | void PopOutData(Kernel::HLERequestContext& ctx) { |
| 613 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 617 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 614 | 618 | ||
| 615 | const auto storage = broker->PopNormalDataToGame(); | 619 | const auto storage = applet->GetBroker().PopNormalDataToGame(); |
| 616 | if (storage == nullptr) { | 620 | if (storage == nullptr) { |
| 617 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | 621 | rb.Push(ERR_NO_DATA_IN_CHANNEL); |
| 618 | return; | 622 | return; |
| @@ -626,7 +630,7 @@ private: | |||
| 626 | 630 | ||
| 627 | void PushInteractiveInData(Kernel::HLERequestContext& ctx) { | 631 | void PushInteractiveInData(Kernel::HLERequestContext& ctx) { |
| 628 | IPC::RequestParser rp{ctx}; | 632 | IPC::RequestParser rp{ctx}; |
| 629 | broker->PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); | 633 | applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); |
| 630 | 634 | ||
| 631 | ASSERT(applet->IsInitialized()); | 635 | ASSERT(applet->IsInitialized()); |
| 632 | applet->ExecuteInteractive(); | 636 | applet->ExecuteInteractive(); |
| @@ -641,7 +645,7 @@ private: | |||
| 641 | void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { | 645 | void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { |
| 642 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 646 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 643 | 647 | ||
| 644 | const auto storage = broker->PopInteractiveDataToGame(); | 648 | const auto storage = applet->GetBroker().PopInteractiveDataToGame(); |
| 645 | if (storage == nullptr) { | 649 | if (storage == nullptr) { |
| 646 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | 650 | rb.Push(ERR_NO_DATA_IN_CHANNEL); |
| 647 | return; | 651 | return; |
| @@ -656,7 +660,7 @@ private: | |||
| 656 | void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { | 660 | void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { |
| 657 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 661 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 658 | rb.Push(RESULT_SUCCESS); | 662 | rb.Push(RESULT_SUCCESS); |
| 659 | rb.PushCopyObjects(broker->GetNormalDataEvent()); | 663 | rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent()); |
| 660 | 664 | ||
| 661 | LOG_DEBUG(Service_AM, "called"); | 665 | LOG_DEBUG(Service_AM, "called"); |
| 662 | } | 666 | } |
| @@ -664,13 +668,12 @@ private: | |||
| 664 | void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { | 668 | void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { |
| 665 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 669 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 666 | rb.Push(RESULT_SUCCESS); | 670 | rb.Push(RESULT_SUCCESS); |
| 667 | rb.PushCopyObjects(broker->GetInteractiveDataEvent()); | 671 | rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); |
| 668 | 672 | ||
| 669 | LOG_DEBUG(Service_AM, "called"); | 673 | LOG_DEBUG(Service_AM, "called"); |
| 670 | } | 674 | } |
| 671 | 675 | ||
| 672 | std::shared_ptr<Applets::Applet> applet; | 676 | std::shared_ptr<Applets::Applet> applet; |
| 673 | std::shared_ptr<Applets::AppletDataBroker> broker; | ||
| 674 | }; | 677 | }; |
| 675 | 678 | ||
| 676 | void IStorage::Open(Kernel::HLERequestContext& ctx) { | 679 | void IStorage::Open(Kernel::HLERequestContext& ctx) { |
| @@ -763,8 +766,9 @@ static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { | |||
| 763 | case AppletId::SoftwareKeyboard: | 766 | case AppletId::SoftwareKeyboard: |
| 764 | return std::make_shared<Applets::SoftwareKeyboard>(); | 767 | return std::make_shared<Applets::SoftwareKeyboard>(); |
| 765 | default: | 768 | default: |
| 766 | UNREACHABLE_MSG("Unimplemented AppletId [{:08X}]!", static_cast<u32>(id)); | 769 | LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!", |
| 767 | return nullptr; | 770 | static_cast<u32>(id)); |
| 771 | return std::make_shared<Applets::StubApplet>(); | ||
| 768 | } | 772 | } |
| 769 | } | 773 | } |
| 770 | 774 | ||
| @@ -820,8 +824,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex | |||
| 820 | return; | 824 | return; |
| 821 | } | 825 | } |
| 822 | 826 | ||
| 823 | const auto mem_begin = shared_mem->backing_block->begin() + shared_mem->backing_block_offset; | 827 | const u8* mem_begin = shared_mem->GetPointer(); |
| 824 | const auto mem_end = mem_begin + shared_mem->size; | 828 | const u8* mem_end = mem_begin + shared_mem->GetSize(); |
| 825 | std::vector<u8> memory{mem_begin, mem_end}; | 829 | std::vector<u8> memory{mem_begin, mem_end}; |
| 826 | 830 | ||
| 827 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 831 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 8adb81823..becbadd06 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -98,10 +98,8 @@ Applet::Applet() = default; | |||
| 98 | 98 | ||
| 99 | Applet::~Applet() = default; | 99 | Applet::~Applet() = default; |
| 100 | 100 | ||
| 101 | void Applet::Initialize(std::shared_ptr<AppletDataBroker> broker_) { | 101 | void Applet::Initialize() { |
| 102 | broker = std::move(broker_); | 102 | const auto common = broker.PopNormalDataToApplet(); |
| 103 | |||
| 104 | const auto common = broker->PopNormalDataToApplet(); | ||
| 105 | ASSERT(common != nullptr); | 103 | ASSERT(common != nullptr); |
| 106 | 104 | ||
| 107 | const auto common_data = common->GetData(); | 105 | const auto common_data = common->GetData(); |
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 136445649..f65ea119c 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -4,14 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <functional> | ||
| 8 | #include <memory> | 7 | #include <memory> |
| 9 | #include <queue> | 8 | #include <queue> |
| 10 | #include "common/swap.h" | 9 | #include "common/swap.h" |
| 11 | #include "core/hle/kernel/event.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 12 | 11 | ||
| 13 | union ResultCode; | 12 | union ResultCode; |
| 14 | 13 | ||
| 14 | namespace Kernel { | ||
| 15 | class Event; | ||
| 16 | } | ||
| 17 | |||
| 15 | namespace Service::AM { | 18 | namespace Service::AM { |
| 16 | 19 | ||
| 17 | class IStorage; | 20 | class IStorage; |
| @@ -43,19 +46,26 @@ public: | |||
| 43 | 46 | ||
| 44 | private: | 47 | private: |
| 45 | // Queues are named from applet's perspective | 48 | // Queues are named from applet's perspective |
| 46 | std::queue<std::unique_ptr<IStorage>> | 49 | |
| 47 | in_channel; // PopNormalDataToApplet and PushNormalDataFromGame | 50 | // PopNormalDataToApplet and PushNormalDataFromGame |
| 48 | std::queue<std::unique_ptr<IStorage>> | 51 | std::queue<std::unique_ptr<IStorage>> in_channel; |
| 49 | out_channel; // PopNormalDataToGame and PushNormalDataFromApplet | 52 | |
| 50 | std::queue<std::unique_ptr<IStorage>> | 53 | // PopNormalDataToGame and PushNormalDataFromApplet |
| 51 | in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame | 54 | std::queue<std::unique_ptr<IStorage>> out_channel; |
| 52 | std::queue<std::unique_ptr<IStorage>> | 55 | |
| 53 | out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet | 56 | // PopInteractiveDataToApplet and PushInteractiveDataFromGame |
| 57 | std::queue<std::unique_ptr<IStorage>> in_interactive_channel; | ||
| 58 | |||
| 59 | // PopInteractiveDataToGame and PushInteractiveDataFromApplet | ||
| 60 | std::queue<std::unique_ptr<IStorage>> out_interactive_channel; | ||
| 54 | 61 | ||
| 55 | Kernel::SharedPtr<Kernel::Event> state_changed_event; | 62 | Kernel::SharedPtr<Kernel::Event> state_changed_event; |
| 56 | Kernel::SharedPtr<Kernel::Event> pop_out_data_event; // Signaled on PushNormalDataFromApplet | 63 | |
| 57 | Kernel::SharedPtr<Kernel::Event> | 64 | // Signaled on PushNormalDataFromApplet |
| 58 | pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet | 65 | Kernel::SharedPtr<Kernel::Event> pop_out_data_event; |
| 66 | |||
| 67 | // Signaled on PushInteractiveDataFromApplet | ||
| 68 | Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event; | ||
| 59 | }; | 69 | }; |
| 60 | 70 | ||
| 61 | class Applet { | 71 | class Applet { |
| @@ -63,7 +73,7 @@ public: | |||
| 63 | Applet(); | 73 | Applet(); |
| 64 | virtual ~Applet(); | 74 | virtual ~Applet(); |
| 65 | 75 | ||
| 66 | virtual void Initialize(std::shared_ptr<AppletDataBroker> broker); | 76 | virtual void Initialize(); |
| 67 | 77 | ||
| 68 | virtual bool TransactionComplete() const = 0; | 78 | virtual bool TransactionComplete() const = 0; |
| 69 | virtual ResultCode GetStatus() const = 0; | 79 | virtual ResultCode GetStatus() const = 0; |
| @@ -74,6 +84,14 @@ public: | |||
| 74 | return initialized; | 84 | return initialized; |
| 75 | } | 85 | } |
| 76 | 86 | ||
| 87 | AppletDataBroker& GetBroker() { | ||
| 88 | return broker; | ||
| 89 | } | ||
| 90 | |||
| 91 | const AppletDataBroker& GetBroker() const { | ||
| 92 | return broker; | ||
| 93 | } | ||
| 94 | |||
| 77 | protected: | 95 | protected: |
| 78 | struct CommonArguments { | 96 | struct CommonArguments { |
| 79 | u32_le arguments_version; | 97 | u32_le arguments_version; |
| @@ -85,8 +103,8 @@ protected: | |||
| 85 | }; | 103 | }; |
| 86 | static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); | 104 | static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); |
| 87 | 105 | ||
| 88 | CommonArguments common_args; | 106 | CommonArguments common_args{}; |
| 89 | std::shared_ptr<AppletDataBroker> broker; | 107 | AppletDataBroker broker; |
| 90 | bool initialized = false; | 108 | bool initialized = false; |
| 91 | }; | 109 | }; |
| 92 | 110 | ||
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index c4b76a515..981bdec51 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp | |||
| @@ -42,21 +42,21 @@ SoftwareKeyboard::SoftwareKeyboard() = default; | |||
| 42 | 42 | ||
| 43 | SoftwareKeyboard::~SoftwareKeyboard() = default; | 43 | SoftwareKeyboard::~SoftwareKeyboard() = default; |
| 44 | 44 | ||
| 45 | void SoftwareKeyboard::Initialize(std::shared_ptr<AppletDataBroker> broker_) { | 45 | void SoftwareKeyboard::Initialize() { |
| 46 | complete = false; | 46 | complete = false; |
| 47 | initial_text.clear(); | 47 | initial_text.clear(); |
| 48 | final_data.clear(); | 48 | final_data.clear(); |
| 49 | 49 | ||
| 50 | Applet::Initialize(std::move(broker_)); | 50 | Applet::Initialize(); |
| 51 | 51 | ||
| 52 | const auto keyboard_config_storage = broker->PopNormalDataToApplet(); | 52 | const auto keyboard_config_storage = broker.PopNormalDataToApplet(); |
| 53 | ASSERT(keyboard_config_storage != nullptr); | 53 | ASSERT(keyboard_config_storage != nullptr); |
| 54 | const auto& keyboard_config = keyboard_config_storage->GetData(); | 54 | const auto& keyboard_config = keyboard_config_storage->GetData(); |
| 55 | 55 | ||
| 56 | ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); | 56 | ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); |
| 57 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); | 57 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); |
| 58 | 58 | ||
| 59 | const auto work_buffer_storage = broker->PopNormalDataToApplet(); | 59 | const auto work_buffer_storage = broker.PopNormalDataToApplet(); |
| 60 | ASSERT(work_buffer_storage != nullptr); | 60 | ASSERT(work_buffer_storage != nullptr); |
| 61 | const auto& work_buffer = work_buffer_storage->GetData(); | 61 | const auto& work_buffer = work_buffer_storage->GetData(); |
| 62 | 62 | ||
| @@ -81,7 +81,7 @@ void SoftwareKeyboard::ExecuteInteractive() { | |||
| 81 | if (complete) | 81 | if (complete) |
| 82 | return; | 82 | return; |
| 83 | 83 | ||
| 84 | const auto storage = broker->PopInteractiveDataToApplet(); | 84 | const auto storage = broker.PopInteractiveDataToApplet(); |
| 85 | ASSERT(storage != nullptr); | 85 | ASSERT(storage != nullptr); |
| 86 | const auto data = storage->GetData(); | 86 | const auto data = storage->GetData(); |
| 87 | const auto status = static_cast<bool>(data[0]); | 87 | const auto status = static_cast<bool>(data[0]); |
| @@ -95,13 +95,13 @@ void SoftwareKeyboard::ExecuteInteractive() { | |||
| 95 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); | 95 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); |
| 96 | frontend.SendTextCheckDialog( | 96 | frontend.SendTextCheckDialog( |
| 97 | Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), | 97 | Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), |
| 98 | [this] { broker->SignalStateChanged(); }); | 98 | [this] { broker.SignalStateChanged(); }); |
| 99 | } | 99 | } |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | void SoftwareKeyboard::Execute() { | 102 | void SoftwareKeyboard::Execute() { |
| 103 | if (complete) { | 103 | if (complete) { |
| 104 | broker->PushNormalDataFromApplet(IStorage{final_data}); | 104 | broker.PushNormalDataFromApplet(IStorage{final_data}); |
| 105 | return; | 105 | return; |
| 106 | } | 106 | } |
| 107 | 107 | ||
| @@ -145,17 +145,17 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { | |||
| 145 | final_data = output_main; | 145 | final_data = output_main; |
| 146 | 146 | ||
| 147 | if (complete) { | 147 | if (complete) { |
| 148 | broker->PushNormalDataFromApplet(IStorage{output_main}); | 148 | broker.PushNormalDataFromApplet(IStorage{output_main}); |
| 149 | } else { | 149 | } else { |
| 150 | broker->PushInteractiveDataFromApplet(IStorage{output_sub}); | 150 | broker.PushInteractiveDataFromApplet(IStorage{output_sub}); |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | broker->SignalStateChanged(); | 153 | broker.SignalStateChanged(); |
| 154 | } else { | 154 | } else { |
| 155 | output_main[0] = 1; | 155 | output_main[0] = 1; |
| 156 | complete = true; | 156 | complete = true; |
| 157 | broker->PushNormalDataFromApplet(IStorage{output_main}); | 157 | broker.PushNormalDataFromApplet(IStorage{output_main}); |
| 158 | broker->SignalStateChanged(); | 158 | broker.SignalStateChanged(); |
| 159 | } | 159 | } |
| 160 | } | 160 | } |
| 161 | } // namespace Service::AM::Applets | 161 | } // namespace Service::AM::Applets |
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 16e1fff66..efd5753a1 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h | |||
| @@ -4,7 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 7 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 12 | #include "common/swap.h" | ||
| 8 | #include "core/hle/service/am/am.h" | 13 | #include "core/hle/service/am/am.h" |
| 9 | #include "core/hle/service/am/applets/applets.h" | 14 | #include "core/hle/service/am/applets/applets.h" |
| 10 | 15 | ||
| @@ -50,7 +55,7 @@ public: | |||
| 50 | SoftwareKeyboard(); | 55 | SoftwareKeyboard(); |
| 51 | ~SoftwareKeyboard() override; | 56 | ~SoftwareKeyboard() override; |
| 52 | 57 | ||
| 53 | void Initialize(std::shared_ptr<AppletDataBroker> broker) override; | 58 | void Initialize() override; |
| 54 | 59 | ||
| 55 | bool TransactionComplete() const override; | 60 | bool TransactionComplete() const override; |
| 56 | ResultCode GetStatus() const override; | 61 | ResultCode GetStatus() const override; |
diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/stub_applet.cpp new file mode 100644 index 000000000..ed166b87d --- /dev/null +++ b/src/core/hle/service/am/applets/stub_applet.cpp | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include "common/hex_util.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "core/hle/service/am/am.h" | ||
| 11 | #include "core/hle/service/am/applets/stub_applet.h" | ||
| 12 | |||
| 13 | namespace Service::AM::Applets { | ||
| 14 | |||
| 15 | static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) { | ||
| 16 | std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet(); | ||
| 17 | for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { | ||
| 18 | const auto data = storage->GetData(); | ||
| 19 | LOG_INFO(Service_AM, | ||
| 20 | "called (STUBBED), during {} recieved normal data with size={:08X}, data={}", | ||
| 21 | prefix, data.size(), Common::HexVectorToString(data)); | ||
| 22 | } | ||
| 23 | |||
| 24 | storage = broker.PopInteractiveDataToApplet(); | ||
| 25 | for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) { | ||
| 26 | const auto data = storage->GetData(); | ||
| 27 | LOG_INFO(Service_AM, | ||
| 28 | "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}", | ||
| 29 | prefix, data.size(), Common::HexVectorToString(data)); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | StubApplet::StubApplet() = default; | ||
| 34 | |||
| 35 | StubApplet::~StubApplet() = default; | ||
| 36 | |||
| 37 | void StubApplet::Initialize() { | ||
| 38 | LOG_WARNING(Service_AM, "called (STUBBED)"); | ||
| 39 | Applet::Initialize(); | ||
| 40 | LogCurrentStorage(broker, "Initialize"); | ||
| 41 | } | ||
| 42 | |||
| 43 | bool StubApplet::TransactionComplete() const { | ||
| 44 | LOG_WARNING(Service_AM, "called (STUBBED)"); | ||
| 45 | return true; | ||
| 46 | } | ||
| 47 | |||
| 48 | ResultCode StubApplet::GetStatus() const { | ||
| 49 | LOG_WARNING(Service_AM, "called (STUBBED)"); | ||
| 50 | return RESULT_SUCCESS; | ||
| 51 | } | ||
| 52 | |||
| 53 | void StubApplet::ExecuteInteractive() { | ||
| 54 | LOG_WARNING(Service_AM, "called (STUBBED)"); | ||
| 55 | LogCurrentStorage(broker, "ExecuteInteractive"); | ||
| 56 | |||
| 57 | broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | ||
| 58 | broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | ||
| 59 | broker.SignalStateChanged(); | ||
| 60 | } | ||
| 61 | |||
| 62 | void StubApplet::Execute() { | ||
| 63 | LOG_WARNING(Service_AM, "called (STUBBED)"); | ||
| 64 | LogCurrentStorage(broker, "Execute"); | ||
| 65 | |||
| 66 | broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | ||
| 67 | broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | ||
| 68 | broker.SignalStateChanged(); | ||
| 69 | } | ||
| 70 | } // namespace Service::AM::Applets | ||
diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h new file mode 100644 index 000000000..7d8dc968d --- /dev/null +++ b/src/core/hle/service/am/applets/stub_applet.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/am/applets/applets.h" | ||
| 8 | |||
| 9 | namespace Service::AM::Applets { | ||
| 10 | |||
| 11 | class StubApplet final : public Applet { | ||
| 12 | public: | ||
| 13 | StubApplet(); | ||
| 14 | ~StubApplet() override; | ||
| 15 | |||
| 16 | void Initialize() override; | ||
| 17 | |||
| 18 | bool TransactionComplete() const override; | ||
| 19 | ResultCode GetStatus() const override; | ||
| 20 | void ExecuteInteractive() override; | ||
| 21 | void Execute() override; | ||
| 22 | }; | ||
| 23 | |||
| 24 | } // namespace Service::AM::Applets | ||
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index ff1edefbb..23e1f1165 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -44,8 +44,10 @@ enum class AudioState : u32 { | |||
| 44 | 44 | ||
| 45 | class IAudioOut final : public ServiceFramework<IAudioOut> { | 45 | class IAudioOut final : public ServiceFramework<IAudioOut> { |
| 46 | public: | 46 | public: |
| 47 | IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) | 47 | IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, |
| 48 | : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) { | 48 | std::string&& unique_name) |
| 49 | : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params), | ||
| 50 | device_name(std::move(device_name)) { | ||
| 49 | 51 | ||
| 50 | static const FunctionInfo functions[] = { | 52 | static const FunctionInfo functions[] = { |
| 51 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, | 53 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, |
| @@ -69,7 +71,7 @@ public: | |||
| 69 | Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); | 71 | Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); |
| 70 | 72 | ||
| 71 | stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, | 73 | stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, |
| 72 | "IAudioOut", [=]() { buffer_event->Signal(); }); | 74 | std::move(unique_name), [=]() { buffer_event->Signal(); }); |
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | private: | 77 | private: |
| @@ -177,6 +179,7 @@ private: | |||
| 177 | 179 | ||
| 178 | AudioCore::AudioOut& audio_core; | 180 | AudioCore::AudioOut& audio_core; |
| 179 | AudioCore::StreamPtr stream; | 181 | AudioCore::StreamPtr stream; |
| 182 | std::string device_name; | ||
| 180 | 183 | ||
| 181 | AudoutParams audio_params{}; | 184 | AudoutParams audio_params{}; |
| 182 | 185 | ||
| @@ -199,7 +202,15 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { | |||
| 199 | void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { | 202 | void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { |
| 200 | LOG_DEBUG(Service_Audio, "called"); | 203 | LOG_DEBUG(Service_Audio, "called"); |
| 201 | 204 | ||
| 202 | ctx.WriteBuffer(DefaultDevice); | 205 | const auto device_name_data{ctx.ReadBuffer()}; |
| 206 | std::string device_name; | ||
| 207 | if (device_name_data[0] != '\0') { | ||
| 208 | device_name.assign(device_name_data.begin(), device_name_data.end()); | ||
| 209 | } else { | ||
| 210 | device_name.assign(DefaultDevice.begin(), DefaultDevice.end()); | ||
| 211 | } | ||
| 212 | ctx.WriteBuffer(device_name); | ||
| 213 | |||
| 203 | IPC::RequestParser rp{ctx}; | 214 | IPC::RequestParser rp{ctx}; |
| 204 | auto params{rp.PopRaw<AudoutParams>()}; | 215 | auto params{rp.PopRaw<AudoutParams>()}; |
| 205 | if (params.channel_count <= 2) { | 216 | if (params.channel_count <= 2) { |
| @@ -212,10 +223,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { | |||
| 212 | params.sample_rate = DefaultSampleRate; | 223 | params.sample_rate = DefaultSampleRate; |
| 213 | } | 224 | } |
| 214 | 225 | ||
| 215 | // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl | 226 | std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; |
| 216 | // will likely need to be updated as well. | 227 | auto audio_out_interface = std::make_shared<IAudioOut>( |
| 217 | ASSERT_MSG(!audio_out_interface, "Unimplemented"); | 228 | params, *audio_core, std::move(device_name), std::move(unique_name)); |
| 218 | audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core); | ||
| 219 | 229 | ||
| 220 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | 230 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; |
| 221 | rb.Push(RESULT_SUCCESS); | 231 | rb.Push(RESULT_SUCCESS); |
| @@ -224,6 +234,8 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { | |||
| 224 | rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); | 234 | rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); |
| 225 | rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); | 235 | rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); |
| 226 | rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); | 236 | rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); |
| 237 | |||
| 238 | audio_out_interfaces.push_back(std::move(audio_out_interface)); | ||
| 227 | } | 239 | } |
| 228 | 240 | ||
| 229 | AudOutU::AudOutU() : ServiceFramework("audout:u") { | 241 | AudOutU::AudOutU() : ServiceFramework("audout:u") { |
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index dcaf64708..aed4c43b2 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vector> | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | 9 | ||
| 9 | namespace AudioCore { | 10 | namespace AudioCore { |
| @@ -24,7 +25,7 @@ public: | |||
| 24 | ~AudOutU() override; | 25 | ~AudOutU() override; |
| 25 | 26 | ||
| 26 | private: | 27 | private: |
| 27 | std::shared_ptr<IAudioOut> audio_out_interface; | 28 | std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; |
| 28 | std::unique_ptr<AudioCore::AudioOut> audio_core; | 29 | std::unique_ptr<AudioCore::AudioOut> audio_core; |
| 29 | 30 | ||
| 30 | void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); | 31 | void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index e76c83aee..c22357d8c 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -71,8 +71,9 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { | |||
| 71 | 71 | ||
| 72 | void Controller_DebugPad::OnLoadInputDevices() { | 72 | void Controller_DebugPad::OnLoadInputDevices() { |
| 73 | std::transform(Settings::values.debug_pad_buttons.begin(), | 73 | std::transform(Settings::values.debug_pad_buttons.begin(), |
| 74 | Settings::values.debug_pad_buttons.end(), buttons.begin(), | 74 | Settings::values.debug_pad_buttons.begin() + |
| 75 | Input::CreateDevice<Input::ButtonDevice>); | 75 | Settings::NativeButton::NUM_BUTTONS_HID, |
| 76 | buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 76 | std::transform(Settings::values.debug_pad_analogs.begin(), | 77 | std::transform(Settings::values.debug_pad_analogs.begin(), |
| 77 | Settings::values.debug_pad_analogs.end(), analogs.begin(), | 78 | Settings::values.debug_pad_analogs.end(), analogs.begin(), |
| 78 | Input::CreateDevice<Input::AnalogDevice>); | 79 | Input::CreateDevice<Input::AnalogDevice>); |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index b43f1f054..7a9d0d0dd 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -16,35 +16,18 @@ | |||
| 16 | 16 | ||
| 17 | namespace Service::LDR { | 17 | namespace Service::LDR { |
| 18 | 18 | ||
| 19 | namespace ErrCodes { | 19 | constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51}; |
| 20 | enum { | 20 | constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52}; |
| 21 | InvalidMemoryState = 51, | 21 | constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53}; |
| 22 | InvalidNRO = 52, | 22 | constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54}; |
| 23 | InvalidNRR = 53, | 23 | constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55}; |
| 24 | MissingNRRHash = 54, | 24 | constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56}; |
| 25 | MaximumNRO = 55, | 25 | constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57}; |
| 26 | MaximumNRR = 56, | 26 | constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81}; |
| 27 | AlreadyLoaded = 57, | 27 | constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82}; |
| 28 | InvalidAlignment = 81, | 28 | constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84}; |
| 29 | InvalidSize = 82, | 29 | constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85}; |
| 30 | InvalidNROAddress = 84, | 30 | constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; |
| 31 | InvalidNRRAddress = 85, | ||
| 32 | NotInitialized = 87, | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState); | ||
| 37 | constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO); | ||
| 38 | constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR); | ||
| 39 | constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash); | ||
| 40 | constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO); | ||
| 41 | constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR); | ||
| 42 | constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded); | ||
| 43 | constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment); | ||
| 44 | constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize); | ||
| 45 | constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress); | ||
| 46 | constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress); | ||
| 47 | constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized); | ||
| 48 | 31 | ||
| 49 | constexpr u64 MAXIMUM_LOADED_RO = 0x40; | 32 | constexpr u64 MAXIMUM_LOADED_RO = 0x40; |
| 50 | 33 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 7a88ae029..792d26e52 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 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_timing.h" | ||
| 9 | #include "core/core_timing_util.h" | ||
| 8 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" | 10 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" |
| 9 | 11 | ||
| 10 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| @@ -33,6 +35,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec | |||
| 33 | return ZBCQueryTable(input, output); | 35 | return ZBCQueryTable(input, output); |
| 34 | case IoctlCommand::IocFlushL2: | 36 | case IoctlCommand::IocFlushL2: |
| 35 | return FlushL2(input, output); | 37 | return FlushL2(input, output); |
| 38 | case IoctlCommand::IocGetGpuTime: | ||
| 39 | return GetGpuTime(input, output); | ||
| 36 | } | 40 | } |
| 37 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | 41 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); |
| 38 | return 0; | 42 | return 0; |
| @@ -169,4 +173,13 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp | |||
| 169 | return 0; | 173 | return 0; |
| 170 | } | 174 | } |
| 171 | 175 | ||
| 176 | u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 177 | LOG_DEBUG(Service_NVDRV, "called"); | ||
| 178 | IoctlGetGpuTime params{}; | ||
| 179 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 180 | params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks()); | ||
| 181 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 182 | return 0; | ||
| 183 | } | ||
| 184 | |||
| 172 | } // namespace Service::Nvidia::Devices | 185 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 3bbf028ad..240435eea 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | |||
| @@ -156,6 +156,11 @@ private: | |||
| 156 | }; | 156 | }; |
| 157 | static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); | 157 | static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); |
| 158 | 158 | ||
| 159 | struct IoctlGetGpuTime { | ||
| 160 | u64_le gpu_time; | ||
| 161 | }; | ||
| 162 | static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); | ||
| 163 | |||
| 159 | u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); | 164 | u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); |
| 160 | u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); | 165 | u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); |
| 161 | u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); | 166 | u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); |
| @@ -164,6 +169,7 @@ private: | |||
| 164 | u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); | 169 | u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); |
| 165 | u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); | 170 | u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); |
| 166 | u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); | 171 | u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); |
| 172 | u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 167 | }; | 173 | }; |
| 168 | 174 | ||
| 169 | } // namespace Service::Nvidia::Devices | 175 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index d25fdb1fe..a72416084 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -510,7 +510,11 @@ private: | |||
| 510 | 510 | ||
| 511 | if (transaction == TransactionId::Connect) { | 511 | if (transaction == TransactionId::Connect) { |
| 512 | IGBPConnectRequestParcel request{ctx.ReadBuffer()}; | 512 | IGBPConnectRequestParcel request{ctx.ReadBuffer()}; |
| 513 | IGBPConnectResponseParcel response{1280, 720}; | 513 | IGBPConnectResponseParcel response{ |
| 514 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) * | ||
| 515 | Settings::values.resolution_factor), | ||
| 516 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * | ||
| 517 | Settings::values.resolution_factor)}; | ||
| 514 | ctx.WriteBuffer(response.Serialize()); | 518 | ctx.WriteBuffer(response.Serialize()); |
| 515 | } else if (transaction == TransactionId::SetPreallocatedBuffer) { | 519 | } else if (transaction == TransactionId::SetPreallocatedBuffer) { |
| 516 | IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; | 520 | IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; |
| @@ -692,11 +696,15 @@ private: | |||
| 692 | rb.Push(RESULT_SUCCESS); | 696 | rb.Push(RESULT_SUCCESS); |
| 693 | 697 | ||
| 694 | if (Settings::values.use_docked_mode) { | 698 | if (Settings::values.use_docked_mode) { |
| 695 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); | 699 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * |
| 696 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); | 700 | static_cast<u32>(Settings::values.resolution_factor)); |
| 701 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * | ||
| 702 | static_cast<u32>(Settings::values.resolution_factor)); | ||
| 697 | } else { | 703 | } else { |
| 698 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); | 704 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * |
| 699 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); | 705 | static_cast<u32>(Settings::values.resolution_factor)); |
| 706 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * | ||
| 707 | static_cast<u32>(Settings::values.resolution_factor)); | ||
| 700 | } | 708 | } |
| 701 | 709 | ||
| 702 | rb.PushRaw<float>(60.0f); | 710 | rb.PushRaw<float>(60.0f); |
| @@ -901,11 +909,15 @@ private: | |||
| 901 | rb.Push(RESULT_SUCCESS); | 909 | rb.Push(RESULT_SUCCESS); |
| 902 | 910 | ||
| 903 | if (Settings::values.use_docked_mode) { | 911 | if (Settings::values.use_docked_mode) { |
| 904 | rb.Push(static_cast<u64>(DisplayResolution::DockedWidth)); | 912 | rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) * |
| 905 | rb.Push(static_cast<u64>(DisplayResolution::DockedHeight)); | 913 | static_cast<u32>(Settings::values.resolution_factor)); |
| 914 | rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) * | ||
| 915 | static_cast<u32>(Settings::values.resolution_factor)); | ||
| 906 | } else { | 916 | } else { |
| 907 | rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); | 917 | rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) * |
| 908 | rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight)); | 918 | static_cast<u32>(Settings::values.resolution_factor)); |
| 919 | rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) * | ||
| 920 | static_cast<u32>(Settings::values.resolution_factor)); | ||
| 909 | } | 921 | } |
| 910 | } | 922 | } |
| 911 | 923 | ||
| @@ -922,6 +934,8 @@ private: | |||
| 922 | void ListDisplays(Kernel::HLERequestContext& ctx) { | 934 | void ListDisplays(Kernel::HLERequestContext& ctx) { |
| 923 | IPC::RequestParser rp{ctx}; | 935 | IPC::RequestParser rp{ctx}; |
| 924 | DisplayInfo display_info; | 936 | DisplayInfo display_info; |
| 937 | display_info.width *= static_cast<u64>(Settings::values.resolution_factor); | ||
| 938 | display_info.height *= static_cast<u64>(Settings::values.resolution_factor); | ||
| 925 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); | 939 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); |
| 926 | IPC::ResponseBuilder rb{ctx, 4}; | 940 | IPC::ResponseBuilder rb{ctx, 4}; |
| 927 | rb.Push(RESULT_SUCCESS); | 941 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/settings.h b/src/core/settings.h index e63134f80..a0c5fd447 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -403,6 +403,7 @@ struct Values { | |||
| 403 | bool use_gdbstub; | 403 | bool use_gdbstub; |
| 404 | u16 gdbstub_port; | 404 | u16 gdbstub_port; |
| 405 | std::string program_args; | 405 | std::string program_args; |
| 406 | bool dump_exefs; | ||
| 406 | bool dump_nso; | 407 | bool dump_nso; |
| 407 | 408 | ||
| 408 | // WebService | 409 | // WebService |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index a04e00ecb..2bc534be3 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -69,6 +69,15 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 69 | // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a | 69 | // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a |
| 70 | // register carrying a default value. Assume it's OpenGL's default (1). | 70 | // register carrying a default value. Assume it's OpenGL's default (1). |
| 71 | regs.point_size = 1.0f; | 71 | regs.point_size = 1.0f; |
| 72 | |||
| 73 | // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a | ||
| 74 | // default of enabled fixes rendering here. | ||
| 75 | for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) { | ||
| 76 | regs.color_mask[color_mask].R.Assign(1); | ||
| 77 | regs.color_mask[color_mask].G.Assign(1); | ||
| 78 | regs.color_mask[color_mask].B.Assign(1); | ||
| 79 | regs.color_mask[color_mask].A.Assign(1); | ||
| 80 | } | ||
| 72 | } | 81 | } |
| 73 | 82 | ||
| 74 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { | 83 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 9e480dc39..4f137e693 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -389,6 +389,13 @@ public: | |||
| 389 | ReverseSubtract = 3, | 389 | ReverseSubtract = 3, |
| 390 | Min = 4, | 390 | Min = 4, |
| 391 | Max = 5, | 391 | Max = 5, |
| 392 | |||
| 393 | // These values are used by Nouveau and some games. | ||
| 394 | AddGL = 0x8006, | ||
| 395 | SubtractGL = 0x8007, | ||
| 396 | ReverseSubtractGL = 0x8008, | ||
| 397 | MinGL = 0x800a, | ||
| 398 | MaxGL = 0x800b | ||
| 392 | }; | 399 | }; |
| 393 | 400 | ||
| 394 | enum class Factor : u32 { | 401 | enum class Factor : u32 { |
| @@ -624,7 +631,16 @@ public: | |||
| 624 | } | 631 | } |
| 625 | } zeta; | 632 | } zeta; |
| 626 | 633 | ||
| 627 | INSERT_PADDING_WORDS(0x5B); | 634 | INSERT_PADDING_WORDS(0x41); |
| 635 | |||
| 636 | union { | ||
| 637 | BitField<0, 4, u32> stencil; | ||
| 638 | BitField<4, 4, u32> unknown; | ||
| 639 | BitField<8, 4, u32> scissor; | ||
| 640 | BitField<12, 4, u32> viewport; | ||
| 641 | } clear_flags; | ||
| 642 | |||
| 643 | INSERT_PADDING_WORDS(0x19); | ||
| 628 | 644 | ||
| 629 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; | 645 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; |
| 630 | 646 | ||
| @@ -1127,6 +1143,7 @@ ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); | |||
| 1127 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); | 1143 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); |
| 1128 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); | 1144 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); |
| 1129 | ASSERT_REG_POSITION(zeta, 0x3F8); | 1145 | ASSERT_REG_POSITION(zeta, 0x3F8); |
| 1146 | ASSERT_REG_POSITION(clear_flags, 0x43E); | ||
| 1130 | ASSERT_REG_POSITION(vertex_attrib_format, 0x458); | 1147 | ASSERT_REG_POSITION(vertex_attrib_format, 0x458); |
| 1131 | ASSERT_REG_POSITION(rt_control, 0x487); | 1148 | ASSERT_REG_POSITION(rt_control, 0x487); |
| 1132 | ASSERT_REG_POSITION(zeta_width, 0x48a); | 1149 | ASSERT_REG_POSITION(zeta_width, 0x48a); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 83a6fd875..7e8449bc4 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -153,6 +153,7 @@ enum class PredCondition : u64 { | |||
| 153 | NotEqual = 5, | 153 | NotEqual = 5, |
| 154 | GreaterEqual = 6, | 154 | GreaterEqual = 6, |
| 155 | LessThanWithNan = 9, | 155 | LessThanWithNan = 9, |
| 156 | LessEqualWithNan = 11, | ||
| 156 | GreaterThanWithNan = 12, | 157 | GreaterThanWithNan = 12, |
| 157 | NotEqualWithNan = 13, | 158 | NotEqualWithNan = 13, |
| 158 | GreaterEqualWithNan = 14, | 159 | GreaterEqualWithNan = 14, |
| @@ -261,7 +262,7 @@ enum class FlowCondition : u64 { | |||
| 261 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? | 262 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? |
| 262 | }; | 263 | }; |
| 263 | 264 | ||
| 264 | enum class ControlCode : u64 { | 265 | enum class ConditionCode : u64 { |
| 265 | F = 0, | 266 | F = 0, |
| 266 | LT = 1, | 267 | LT = 1, |
| 267 | EQ = 2, | 268 | EQ = 2, |
| @@ -569,7 +570,6 @@ union Instruction { | |||
| 569 | BitField<39, 2, u64> tab5cb8_2; | 570 | BitField<39, 2, u64> tab5cb8_2; |
| 570 | BitField<41, 3, u64> tab5c68_1; | 571 | BitField<41, 3, u64> tab5c68_1; |
| 571 | BitField<44, 2, u64> tab5c68_0; | 572 | BitField<44, 2, u64> tab5c68_0; |
| 572 | BitField<47, 1, u64> cc; | ||
| 573 | BitField<48, 1, u64> negate_b; | 573 | BitField<48, 1, u64> negate_b; |
| 574 | } fmul; | 574 | } fmul; |
| 575 | 575 | ||
| @@ -831,7 +831,7 @@ union Instruction { | |||
| 831 | union { | 831 | union { |
| 832 | BitField<0, 3, u64> pred0; | 832 | BitField<0, 3, u64> pred0; |
| 833 | BitField<3, 3, u64> pred3; | 833 | BitField<3, 3, u64> pred3; |
| 834 | BitField<8, 5, ControlCode> cc; // flag in cc | 834 | BitField<8, 5, ConditionCode> cc; // flag in cc |
| 835 | BitField<39, 3, u64> pred39; | 835 | BitField<39, 3, u64> pred39; |
| 836 | BitField<42, 1, u64> neg_pred39; | 836 | BitField<42, 1, u64> neg_pred39; |
| 837 | BitField<45, 4, PredOperation> op; // op with pred39 | 837 | BitField<45, 4, PredOperation> op; // op with pred39 |
| @@ -1235,7 +1235,7 @@ union Instruction { | |||
| 1235 | BitField<60, 1, u64> is_b_gpr; | 1235 | BitField<60, 1, u64> is_b_gpr; |
| 1236 | BitField<59, 1, u64> is_c_gpr; | 1236 | BitField<59, 1, u64> is_c_gpr; |
| 1237 | BitField<20, 24, s64> smem_imm; | 1237 | BitField<20, 24, s64> smem_imm; |
| 1238 | BitField<0, 5, ControlCode> flow_control_code; | 1238 | BitField<0, 5, ConditionCode> flow_condition_code; |
| 1239 | 1239 | ||
| 1240 | Attribute attribute; | 1240 | Attribute attribute; |
| 1241 | Sampler sampler; | 1241 | Sampler sampler; |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 83c7e5b0b..51b3904f6 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -17,6 +17,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) { | |||
| 17 | switch (format) { | 17 | switch (format) { |
| 18 | case PixelFormat::ABGR8: | 18 | case PixelFormat::ABGR8: |
| 19 | return 4; | 19 | return 4; |
| 20 | default: | ||
| 21 | return 4; | ||
| 20 | } | 22 | } |
| 21 | 23 | ||
| 22 | UNREACHABLE(); | 24 | UNREACHABLE(); |
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 335a8d407..2b0dea5cd 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp | |||
| @@ -35,6 +35,7 @@ void MacroInterpreter::Reset() { | |||
| 35 | // The next parameter index starts at 1, because $r1 already has the value of the first | 35 | // The next parameter index starts at 1, because $r1 already has the value of the first |
| 36 | // parameter. | 36 | // parameter. |
| 37 | next_parameter_index = 1; | 37 | next_parameter_index = 1; |
| 38 | carry_flag = false; | ||
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | 41 | bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { |
| @@ -135,14 +136,28 @@ MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const { | |||
| 135 | return {macro_memory[offset + pc / sizeof(u32)]}; | 136 | return {macro_memory[offset + pc / sizeof(u32)]}; |
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { | 139 | u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) { |
| 139 | switch (operation) { | 140 | switch (operation) { |
| 140 | case ALUOperation::Add: | 141 | case ALUOperation::Add: { |
| 141 | return src_a + src_b; | 142 | const u64 result{static_cast<u64>(src_a) + src_b}; |
| 142 | // TODO(Subv): Implement AddWithCarry | 143 | carry_flag = result > 0xffffffff; |
| 143 | case ALUOperation::Subtract: | 144 | return static_cast<u32>(result); |
| 144 | return src_a - src_b; | 145 | } |
| 145 | // TODO(Subv): Implement SubtractWithBorrow | 146 | case ALUOperation::AddWithCarry: { |
| 147 | const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)}; | ||
| 148 | carry_flag = result > 0xffffffff; | ||
| 149 | return static_cast<u32>(result); | ||
| 150 | } | ||
| 151 | case ALUOperation::Subtract: { | ||
| 152 | const u64 result{static_cast<u64>(src_a) - src_b}; | ||
| 153 | carry_flag = result < 0x100000000; | ||
| 154 | return static_cast<u32>(result); | ||
| 155 | } | ||
| 156 | case ALUOperation::SubtractWithBorrow: { | ||
| 157 | const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)}; | ||
| 158 | carry_flag = result < 0x100000000; | ||
| 159 | return static_cast<u32>(result); | ||
| 160 | } | ||
| 146 | case ALUOperation::Xor: | 161 | case ALUOperation::Xor: |
| 147 | return src_a ^ src_b; | 162 | return src_a ^ src_b; |
| 148 | case ALUOperation::Or: | 163 | case ALUOperation::Or: |
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h index 62d1ce289..cde360288 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro_interpreter.h | |||
| @@ -117,7 +117,7 @@ private: | |||
| 117 | bool Step(u32 offset, bool is_delay_slot); | 117 | bool Step(u32 offset, bool is_delay_slot); |
| 118 | 118 | ||
| 119 | /// Calculates the result of an ALU operation. src_a OP src_b; | 119 | /// Calculates the result of an ALU operation. src_a OP src_b; |
| 120 | u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; | 120 | u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b); |
| 121 | 121 | ||
| 122 | /// Performs the result operation on the input result and stores it in the specified register | 122 | /// Performs the result operation on the input result and stores it in the specified register |
| 123 | /// (if necessary). | 123 | /// (if necessary). |
| @@ -165,5 +165,7 @@ private: | |||
| 165 | std::vector<u32> parameters; | 165 | std::vector<u32> parameters; |
| 166 | /// Index of the next parameter that will be fetched by the 'parm' instruction. | 166 | /// Index of the next parameter that will be fetched by the 'parm' instruction. |
| 167 | u32 next_parameter_index = 0; | 167 | u32 next_parameter_index = 0; |
| 168 | |||
| 169 | bool carry_flag{}; | ||
| 168 | }; | 170 | }; |
| 169 | } // namespace Tegra | 171 | } // namespace Tegra |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 8e5ca298f..630a58e49 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -537,6 +537,30 @@ void RasterizerOpenGL::Clear() { | |||
| 537 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); | 537 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); |
| 538 | use_stencil = true; | 538 | use_stencil = true; |
| 539 | clear_state.stencil.test_enabled = true; | 539 | clear_state.stencil.test_enabled = true; |
| 540 | if (regs.clear_flags.stencil) { | ||
| 541 | // Stencil affects the clear so fill it with the used masks | ||
| 542 | clear_state.stencil.front.test_func = GL_ALWAYS; | ||
| 543 | clear_state.stencil.front.test_mask = regs.stencil_front_func_mask; | ||
| 544 | clear_state.stencil.front.action_stencil_fail = GL_KEEP; | ||
| 545 | clear_state.stencil.front.action_depth_fail = GL_KEEP; | ||
| 546 | clear_state.stencil.front.action_depth_pass = GL_KEEP; | ||
| 547 | clear_state.stencil.front.write_mask = regs.stencil_front_mask; | ||
| 548 | if (regs.stencil_two_side_enable) { | ||
| 549 | clear_state.stencil.back.test_func = GL_ALWAYS; | ||
| 550 | clear_state.stencil.back.test_mask = regs.stencil_back_func_mask; | ||
| 551 | clear_state.stencil.back.action_stencil_fail = GL_KEEP; | ||
| 552 | clear_state.stencil.back.action_depth_fail = GL_KEEP; | ||
| 553 | clear_state.stencil.back.action_depth_pass = GL_KEEP; | ||
| 554 | clear_state.stencil.back.write_mask = regs.stencil_back_mask; | ||
| 555 | } else { | ||
| 556 | clear_state.stencil.back.test_func = GL_ALWAYS; | ||
| 557 | clear_state.stencil.back.test_mask = 0xFFFFFFFF; | ||
| 558 | clear_state.stencil.back.write_mask = 0xFFFFFFFF; | ||
| 559 | clear_state.stencil.back.action_stencil_fail = GL_KEEP; | ||
| 560 | clear_state.stencil.back.action_depth_fail = GL_KEEP; | ||
| 561 | clear_state.stencil.back.action_depth_pass = GL_KEEP; | ||
| 562 | } | ||
| 563 | } | ||
| 540 | } | 564 | } |
| 541 | 565 | ||
| 542 | if (!use_color && !use_depth && !use_stencil) { | 566 | if (!use_color && !use_depth && !use_stencil) { |
| @@ -548,6 +572,14 @@ void RasterizerOpenGL::Clear() { | |||
| 548 | 572 | ||
| 549 | ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false, | 573 | ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false, |
| 550 | regs.clear_buffers.RT.Value()); | 574 | regs.clear_buffers.RT.Value()); |
| 575 | if (regs.clear_flags.scissor) { | ||
| 576 | SyncScissorTest(clear_state); | ||
| 577 | } | ||
| 578 | |||
| 579 | if (regs.clear_flags.viewport) { | ||
| 580 | clear_state.EmulateViewportWithScissor(); | ||
| 581 | } | ||
| 582 | |||
| 551 | clear_state.Apply(); | 583 | clear_state.Apply(); |
| 552 | 584 | ||
| 553 | if (use_color) { | 585 | if (use_color) { |
| @@ -583,7 +615,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 583 | SyncLogicOpState(); | 615 | SyncLogicOpState(); |
| 584 | SyncCullMode(); | 616 | SyncCullMode(); |
| 585 | SyncPrimitiveRestart(); | 617 | SyncPrimitiveRestart(); |
| 586 | SyncScissorTest(); | 618 | SyncScissorTest(state); |
| 587 | // Alpha Testing is synced on shaders. | 619 | // Alpha Testing is synced on shaders. |
| 588 | SyncTransformFeedback(); | 620 | SyncTransformFeedback(); |
| 589 | SyncPointState(); | 621 | SyncPointState(); |
| @@ -810,7 +842,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr | |||
| 810 | } | 842 | } |
| 811 | const u32 bias = config.mip_lod_bias.Value(); | 843 | const u32 bias = config.mip_lod_bias.Value(); |
| 812 | // Sign extend the 13-bit value. | 844 | // Sign extend the 13-bit value. |
| 813 | const u32 mask = 1U << (13 - 1); | 845 | constexpr u32 mask = 1U << (13 - 1); |
| 814 | const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f; | 846 | const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f; |
| 815 | if (lod_bias != bias_lod) { | 847 | if (lod_bias != bias_lod) { |
| 816 | lod_bias = bias_lod; | 848 | lod_bias = bias_lod; |
| @@ -942,8 +974,8 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { | |||
| 942 | auto& viewport = current_state.viewports[i]; | 974 | auto& viewport = current_state.viewports[i]; |
| 943 | viewport.x = viewport_rect.left; | 975 | viewport.x = viewport_rect.left; |
| 944 | viewport.y = viewport_rect.bottom; | 976 | viewport.y = viewport_rect.bottom; |
| 945 | viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); | 977 | viewport.width = viewport_rect.GetWidth(); |
| 946 | viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); | 978 | viewport.height = viewport_rect.GetHeight(); |
| 947 | viewport.depth_range_far = regs.viewports[i].depth_range_far; | 979 | viewport.depth_range_far = regs.viewports[i].depth_range_far; |
| 948 | viewport.depth_range_near = regs.viewports[i].depth_range_near; | 980 | viewport.depth_range_near = regs.viewports[i].depth_range_near; |
| 949 | } | 981 | } |
| @@ -1115,11 +1147,11 @@ void RasterizerOpenGL::SyncLogicOpState() { | |||
| 1115 | state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); | 1147 | state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); |
| 1116 | } | 1148 | } |
| 1117 | 1149 | ||
| 1118 | void RasterizerOpenGL::SyncScissorTest() { | 1150 | void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) { |
| 1119 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1151 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 1120 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { | 1152 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { |
| 1121 | const auto& src = regs.scissor_test[i]; | 1153 | const auto& src = regs.scissor_test[i]; |
| 1122 | auto& dst = state.viewports[i].scissor; | 1154 | auto& dst = current_state.viewports[i].scissor; |
| 1123 | dst.enabled = (src.enable != 0); | 1155 | dst.enabled = (src.enable != 0); |
| 1124 | if (dst.enabled == 0) { | 1156 | if (dst.enabled == 0) { |
| 1125 | return; | 1157 | return; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index fd6c8c3c5..f4354289c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -91,19 +91,20 @@ private: | |||
| 91 | void SyncWithConfig(const Tegra::Texture::TSCEntry& info); | 91 | void SyncWithConfig(const Tegra::Texture::TSCEntry& info); |
| 92 | 92 | ||
| 93 | private: | 93 | private: |
| 94 | Tegra::Texture::TextureFilter mag_filter; | 94 | Tegra::Texture::TextureFilter mag_filter = Tegra::Texture::TextureFilter::Nearest; |
| 95 | Tegra::Texture::TextureFilter min_filter; | 95 | Tegra::Texture::TextureFilter min_filter = Tegra::Texture::TextureFilter::Nearest; |
| 96 | Tegra::Texture::TextureMipmapFilter mip_filter; | 96 | Tegra::Texture::TextureMipmapFilter mip_filter = Tegra::Texture::TextureMipmapFilter::None; |
| 97 | Tegra::Texture::WrapMode wrap_u; | 97 | Tegra::Texture::WrapMode wrap_u = Tegra::Texture::WrapMode::ClampToEdge; |
| 98 | Tegra::Texture::WrapMode wrap_v; | 98 | Tegra::Texture::WrapMode wrap_v = Tegra::Texture::WrapMode::ClampToEdge; |
| 99 | Tegra::Texture::WrapMode wrap_p; | 99 | Tegra::Texture::WrapMode wrap_p = Tegra::Texture::WrapMode::ClampToEdge; |
| 100 | bool uses_depth_compare; | 100 | bool uses_depth_compare = false; |
| 101 | Tegra::Texture::DepthCompareFunc depth_compare_func; | 101 | Tegra::Texture::DepthCompareFunc depth_compare_func = |
| 102 | GLvec4 border_color; | 102 | Tegra::Texture::DepthCompareFunc::Always; |
| 103 | float min_lod; | 103 | GLvec4 border_color = {}; |
| 104 | float max_lod; | 104 | float min_lod = 0.0f; |
| 105 | float lod_bias; | 105 | float max_lod = 16.0f; |
| 106 | float max_anisotropic; | 106 | float lod_bias = 0.0f; |
| 107 | float max_anisotropic = 1.0f; | ||
| 107 | }; | 108 | }; |
| 108 | 109 | ||
| 109 | /** | 110 | /** |
| @@ -171,7 +172,7 @@ private: | |||
| 171 | void SyncMultiSampleState(); | 172 | void SyncMultiSampleState(); |
| 172 | 173 | ||
| 173 | /// Syncs the scissor test state to match the guest state | 174 | /// Syncs the scissor test state to match the guest state |
| 174 | void SyncScissorTest(); | 175 | void SyncScissorTest(OpenGLState& current_state); |
| 175 | 176 | ||
| 176 | /// Syncs the transform feedback state to match the guest state | 177 | /// Syncs the transform feedback state to match the guest state |
| 177 | void SyncTransformFeedback(); | 178 | void SyncTransformFeedback(); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 83e6a4b50..4f434fc31 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -1275,6 +1275,31 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) { | |||
| 1275 | return surface; | 1275 | return surface; |
| 1276 | } | 1276 | } |
| 1277 | 1277 | ||
| 1278 | void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface, | ||
| 1279 | const Surface& dst_surface) { | ||
| 1280 | const auto& init_params{src_surface->GetSurfaceParams()}; | ||
| 1281 | const auto& dst_params{dst_surface->GetSurfaceParams()}; | ||
| 1282 | VAddr address = init_params.addr; | ||
| 1283 | const std::size_t layer_size = dst_params.LayerMemorySize(); | ||
| 1284 | for (u32 layer = 0; layer < dst_params.depth; layer++) { | ||
| 1285 | for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { | ||
| 1286 | const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap); | ||
| 1287 | const Surface& copy = TryGet(sub_address); | ||
| 1288 | if (!copy) | ||
| 1289 | continue; | ||
| 1290 | const auto& src_params{copy->GetSurfaceParams()}; | ||
| 1291 | const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))}; | ||
| 1292 | const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))}; | ||
| 1293 | |||
| 1294 | glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0, | ||
| 1295 | 0, 0, dst_surface->Texture().handle, | ||
| 1296 | SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width, | ||
| 1297 | height, 1); | ||
| 1298 | } | ||
| 1299 | address += layer_size; | ||
| 1300 | } | ||
| 1301 | } | ||
| 1302 | |||
| 1278 | void RasterizerCacheOpenGL::FermiCopySurface( | 1303 | void RasterizerCacheOpenGL::FermiCopySurface( |
| 1279 | const Tegra::Engines::Fermi2D::Regs::Surface& src_config, | 1304 | const Tegra::Engines::Fermi2D::Regs::Surface& src_config, |
| 1280 | const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) { | 1305 | const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) { |
| @@ -1340,11 +1365,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1340 | CopySurface(old_surface, new_surface, copy_pbo.handle); | 1365 | CopySurface(old_surface, new_surface, copy_pbo.handle); |
| 1341 | } | 1366 | } |
| 1342 | break; | 1367 | break; |
| 1343 | case SurfaceTarget::TextureCubemap: | ||
| 1344 | case SurfaceTarget::Texture3D: | 1368 | case SurfaceTarget::Texture3D: |
| 1369 | AccurateCopySurface(old_surface, new_surface); | ||
| 1370 | break; | ||
| 1371 | case SurfaceTarget::TextureCubemap: | ||
| 1345 | case SurfaceTarget::Texture2DArray: | 1372 | case SurfaceTarget::Texture2DArray: |
| 1346 | case SurfaceTarget::TextureCubeArray: | 1373 | case SurfaceTarget::TextureCubeArray: |
| 1347 | AccurateCopySurface(old_surface, new_surface); | 1374 | FastLayeredCopySurface(old_surface, new_surface); |
| 1348 | break; | 1375 | break; |
| 1349 | default: | 1376 | default: |
| 1350 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | 1377 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 494f6b903..9ac79c5a4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -350,6 +350,7 @@ private: | |||
| 350 | 350 | ||
| 351 | /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data | 351 | /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data |
| 352 | void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface); | 352 | void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface); |
| 353 | void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface); | ||
| 353 | 354 | ||
| 354 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have | 355 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have |
| 355 | /// previously been used. This is to prevent surfaces from being constantly created and | 356 | /// previously been used. This is to prevent surfaces from being constantly created and |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index a85a7c0c5..038b25c75 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -84,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) | |||
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | entries = program_result.second; | 86 | entries = program_result.second; |
| 87 | shader_length = entries.shader_length; | ||
| 87 | 88 | ||
| 88 | if (program_type != Maxwell::ShaderProgram::Geometry) { | 89 | if (program_type != Maxwell::ShaderProgram::Geometry) { |
| 89 | OGLShader shader; | 90 | OGLShader shader; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index ffbf21831..08f470de3 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -30,7 +30,7 @@ public: | |||
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | std::size_t GetSizeInBytes() const override { | 32 | std::size_t GetSizeInBytes() const override { |
| 33 | return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); | 33 | return shader_length; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | // We do not have to flush this cache as things in it are never modified by us. | 36 | // We do not have to flush this cache as things in it are never modified by us. |
| @@ -82,6 +82,7 @@ private: | |||
| 82 | u32 max_vertices, const std::string& debug_name); | 82 | u32 max_vertices, const std::string& debug_name); |
| 83 | 83 | ||
| 84 | VAddr addr; | 84 | VAddr addr; |
| 85 | std::size_t shader_length; | ||
| 85 | Maxwell::ShaderProgram program_type; | 86 | Maxwell::ShaderProgram program_type; |
| 86 | GLShader::ShaderSetup setup; | 87 | GLShader::ShaderSetup setup; |
| 87 | GLShader::ShaderEntries entries; | 88 | GLShader::ShaderEntries entries; |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 5fde22ad4..97b9028c5 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -34,6 +34,17 @@ constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); | |||
| 34 | constexpr u32 MAX_GEOMETRY_BUFFERS = 6; | 34 | constexpr u32 MAX_GEOMETRY_BUFFERS = 6; |
| 35 | constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested | 35 | constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested |
| 36 | 36 | ||
| 37 | static const char* INTERNAL_FLAG_NAMES[] = {"zero_flag", "sign_flag", "carry_flag", | ||
| 38 | "overflow_flag"}; | ||
| 39 | |||
| 40 | enum class InternalFlag : u64 { | ||
| 41 | ZeroFlag = 0, | ||
| 42 | SignFlag = 1, | ||
| 43 | CarryFlag = 2, | ||
| 44 | OverflowFlag = 3, | ||
| 45 | Amount | ||
| 46 | }; | ||
| 47 | |||
| 37 | class DecompileFail : public std::runtime_error { | 48 | class DecompileFail : public std::runtime_error { |
| 38 | public: | 49 | public: |
| 39 | using std::runtime_error::runtime_error; | 50 | using std::runtime_error::runtime_error; |
| @@ -49,8 +60,7 @@ static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { | |||
| 49 | case Tegra::Shader::OutputTopology::TriangleStrip: | 60 | case Tegra::Shader::OutputTopology::TriangleStrip: |
| 50 | return "triangle_strip"; | 61 | return "triangle_strip"; |
| 51 | default: | 62 | default: |
| 52 | LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology)); | 63 | UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology)); |
| 53 | UNREACHABLE(); | ||
| 54 | return "points"; | 64 | return "points"; |
| 55 | } | 65 | } |
| 56 | } | 66 | } |
| @@ -85,7 +95,8 @@ struct Subroutine { | |||
| 85 | class ControlFlowAnalyzer { | 95 | class ControlFlowAnalyzer { |
| 86 | public: | 96 | public: |
| 87 | ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix) | 97 | ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix) |
| 88 | : program_code(program_code) { | 98 | : program_code(program_code), shader_coverage_begin(main_offset), |
| 99 | shader_coverage_end(main_offset + 1) { | ||
| 89 | 100 | ||
| 90 | // Recursively finds all subroutines. | 101 | // Recursively finds all subroutines. |
| 91 | const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix); | 102 | const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix); |
| @@ -97,10 +108,16 @@ public: | |||
| 97 | return std::move(subroutines); | 108 | return std::move(subroutines); |
| 98 | } | 109 | } |
| 99 | 110 | ||
| 111 | std::size_t GetShaderLength() const { | ||
| 112 | return shader_coverage_end * sizeof(u64); | ||
| 113 | } | ||
| 114 | |||
| 100 | private: | 115 | private: |
| 101 | const ProgramCode& program_code; | 116 | const ProgramCode& program_code; |
| 102 | std::set<Subroutine> subroutines; | 117 | std::set<Subroutine> subroutines; |
| 103 | std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; | 118 | std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; |
| 119 | u32 shader_coverage_begin; | ||
| 120 | u32 shader_coverage_end; | ||
| 104 | 121 | ||
| 105 | /// Adds and analyzes a new subroutine if it is not added yet. | 122 | /// Adds and analyzes a new subroutine if it is not added yet. |
| 106 | const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) { | 123 | const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) { |
| @@ -142,6 +159,9 @@ private: | |||
| 142 | return exit_method; | 159 | return exit_method; |
| 143 | 160 | ||
| 144 | for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { | 161 | for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { |
| 162 | shader_coverage_begin = std::min(shader_coverage_begin, offset); | ||
| 163 | shader_coverage_end = std::max(shader_coverage_end, offset + 1); | ||
| 164 | |||
| 145 | const Instruction instr = {program_code[offset]}; | 165 | const Instruction instr = {program_code[offset]}; |
| 146 | if (const auto opcode = OpCode::Decode(instr)) { | 166 | if (const auto opcode = OpCode::Decode(instr)) { |
| 147 | switch (opcode->get().GetId()) { | 167 | switch (opcode->get().GetId()) { |
| @@ -167,8 +187,8 @@ private: | |||
| 167 | case OpCode::Id::SSY: | 187 | case OpCode::Id::SSY: |
| 168 | case OpCode::Id::PBK: { | 188 | case OpCode::Id::PBK: { |
| 169 | // The SSY and PBK use a similar encoding as the BRA instruction. | 189 | // The SSY and PBK use a similar encoding as the BRA instruction. |
| 170 | ASSERT_MSG(instr.bra.constant_buffer == 0, | 190 | UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, |
| 171 | "Constant buffer branching is not supported"); | 191 | "Constant buffer branching is not supported"); |
| 172 | const u32 target = offset + instr.bra.GetBranchTarget(); | 192 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 173 | labels.insert(target); | 193 | labels.insert(target); |
| 174 | // Continue scanning for an exit method. | 194 | // Continue scanning for an exit method. |
| @@ -258,14 +278,6 @@ private: | |||
| 258 | const std::string& suffix; | 278 | const std::string& suffix; |
| 259 | }; | 279 | }; |
| 260 | 280 | ||
| 261 | enum class InternalFlag : u64 { | ||
| 262 | ZeroFlag = 0, | ||
| 263 | CarryFlag = 1, | ||
| 264 | OverflowFlag = 2, | ||
| 265 | NaNFlag = 3, | ||
| 266 | Amount | ||
| 267 | }; | ||
| 268 | |||
| 269 | /** | 281 | /** |
| 270 | * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state | 282 | * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state |
| 271 | * of all registers (e.g. whether they are currently being used as Floats or Integers), and | 283 | * of all registers (e.g. whether they are currently being used as Floats or Integers), and |
| @@ -299,8 +311,7 @@ public: | |||
| 299 | // Default - do nothing | 311 | // Default - do nothing |
| 300 | return value; | 312 | return value; |
| 301 | default: | 313 | default: |
| 302 | LOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size)); | 314 | UNIMPLEMENTED_MSG("Unimplemented conversion size: {}", static_cast<u32>(size)); |
| 303 | UNREACHABLE(); | ||
| 304 | } | 315 | } |
| 305 | } | 316 | } |
| 306 | 317 | ||
| @@ -363,7 +374,7 @@ public: | |||
| 363 | u64 value_num_components, bool is_saturated = false, | 374 | u64 value_num_components, bool is_saturated = false, |
| 364 | u64 dest_elem = 0, Register::Size size = Register::Size::Word, | 375 | u64 dest_elem = 0, Register::Size size = Register::Size::Word, |
| 365 | bool sets_cc = false) { | 376 | bool sets_cc = false) { |
| 366 | ASSERT_MSG(!is_saturated, "Unimplemented"); | 377 | UNIMPLEMENTED_IF(is_saturated); |
| 367 | 378 | ||
| 368 | const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; | 379 | const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; |
| 369 | 380 | ||
| @@ -373,7 +384,7 @@ public: | |||
| 373 | if (sets_cc) { | 384 | if (sets_cc) { |
| 374 | const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; | 385 | const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; |
| 375 | SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); | 386 | SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); |
| 376 | LOG_WARNING(HW_GPU, "Control Codes Imcomplete."); | 387 | LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); |
| 377 | } | 388 | } |
| 378 | } | 389 | } |
| 379 | 390 | ||
| @@ -392,7 +403,7 @@ public: | |||
| 392 | Tegra::Shader::HalfMerge merge, u64 dest_num_components, | 403 | Tegra::Shader::HalfMerge merge, u64 dest_num_components, |
| 393 | u64 value_num_components, bool is_saturated = false, | 404 | u64 value_num_components, bool is_saturated = false, |
| 394 | u64 dest_elem = 0) { | 405 | u64 dest_elem = 0) { |
| 395 | ASSERT_MSG(!is_saturated, "Unimplemented"); | 406 | UNIMPLEMENTED_IF(is_saturated); |
| 396 | 407 | ||
| 397 | const std::string result = [&]() { | 408 | const std::string result = [&]() { |
| 398 | switch (merge) { | 409 | switch (merge) { |
| @@ -456,24 +467,25 @@ public: | |||
| 456 | shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");"); | 467 | shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");"); |
| 457 | } | 468 | } |
| 458 | 469 | ||
| 459 | std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { | 470 | std::string GetConditionCode(const Tegra::Shader::ConditionCode cc) const { |
| 460 | switch (cc) { | 471 | switch (cc) { |
| 461 | case Tegra::Shader::ControlCode::NEU: | 472 | case Tegra::Shader::ConditionCode::NEU: |
| 462 | return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; | 473 | return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; |
| 463 | default: | 474 | default: |
| 464 | LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc)); | 475 | UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); |
| 465 | UNREACHABLE(); | ||
| 466 | return "false"; | 476 | return "false"; |
| 467 | } | 477 | } |
| 468 | } | 478 | } |
| 469 | 479 | ||
| 470 | std::string GetInternalFlag(const InternalFlag ii) const { | 480 | std::string GetInternalFlag(const InternalFlag flag) const { |
| 471 | const u32 code = static_cast<u32>(ii); | 481 | const auto index = static_cast<u32>(flag); |
| 472 | return "internalFlag_" + std::to_string(code) + suffix; | 482 | ASSERT(index < static_cast<u32>(InternalFlag::Amount)); |
| 483 | |||
| 484 | return std::string(INTERNAL_FLAG_NAMES[index]) + '_' + suffix; | ||
| 473 | } | 485 | } |
| 474 | 486 | ||
| 475 | void SetInternalFlag(const InternalFlag ii, const std::string& value) const { | 487 | void SetInternalFlag(const InternalFlag flag, const std::string& value) const { |
| 476 | shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); | 488 | shader.AddLine(GetInternalFlag(flag) + " = " + value + ';'); |
| 477 | } | 489 | } |
| 478 | 490 | ||
| 479 | /** | 491 | /** |
| @@ -624,8 +636,8 @@ private: | |||
| 624 | 636 | ||
| 625 | /// Generates declarations for internal flags. | 637 | /// Generates declarations for internal flags. |
| 626 | void GenerateInternalFlags() { | 638 | void GenerateInternalFlags() { |
| 627 | for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { | 639 | for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { |
| 628 | const InternalFlag code = static_cast<InternalFlag>(ii); | 640 | const InternalFlag code = static_cast<InternalFlag>(flag); |
| 629 | declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); | 641 | declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); |
| 630 | } | 642 | } |
| 631 | declarations.AddNewLine(); | 643 | declarations.AddNewLine(); |
| @@ -761,8 +773,7 @@ private: | |||
| 761 | u64 dest_num_components, u64 value_num_components, u64 dest_elem, | 773 | u64 dest_num_components, u64 value_num_components, u64 dest_elem, |
| 762 | bool precise) { | 774 | bool precise) { |
| 763 | if (reg == Register::ZeroIndex) { | 775 | if (reg == Register::ZeroIndex) { |
| 764 | LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); | 776 | // Setting RZ is a nop in hardware. |
| 765 | UNREACHABLE(); | ||
| 766 | return; | 777 | return; |
| 767 | } | 778 | } |
| 768 | 779 | ||
| @@ -847,16 +858,13 @@ private: | |||
| 847 | if (declr_input_attribute.count(attribute) == 0) { | 858 | if (declr_input_attribute.count(attribute) == 0) { |
| 848 | declr_input_attribute[attribute] = input_mode; | 859 | declr_input_attribute[attribute] = input_mode; |
| 849 | } else { | 860 | } else { |
| 850 | if (declr_input_attribute[attribute] != input_mode) { | 861 | UNIMPLEMENTED_IF_MSG(declr_input_attribute[attribute] != input_mode, |
| 851 | LOG_CRITICAL(HW_GPU, "Same Input multiple input modes"); | 862 | "Multiple input modes for the same attribute"); |
| 852 | UNREACHABLE(); | ||
| 853 | } | ||
| 854 | } | 863 | } |
| 855 | return GeometryPass("input_attribute_" + std::to_string(index)); | 864 | return GeometryPass("input_attribute_" + std::to_string(index)); |
| 856 | } | 865 | } |
| 857 | 866 | ||
| 858 | LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); | 867 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); |
| 859 | UNREACHABLE(); | ||
| 860 | } | 868 | } |
| 861 | 869 | ||
| 862 | return "vec4(0, 0, 0, 0)"; | 870 | return "vec4(0, 0, 0, 0)"; |
| @@ -882,24 +890,20 @@ private: | |||
| 882 | break; | 890 | break; |
| 883 | } | 891 | } |
| 884 | default: { | 892 | default: { |
| 885 | LOG_CRITICAL(HW_GPU, "Unhandled Ipa InterpMode: {}", static_cast<u32>(interp_mode)); | 893 | UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode)); |
| 886 | UNREACHABLE(); | ||
| 887 | } | 894 | } |
| 888 | } | 895 | } |
| 889 | switch (sample_mode) { | 896 | switch (sample_mode) { |
| 890 | case Tegra::Shader::IpaSampleMode::Centroid: { | 897 | case Tegra::Shader::IpaSampleMode::Centroid: |
| 891 | // Note not implemented, it can be implemented with the "centroid " keyword in glsl; | 898 | // It can be implemented with the "centroid " keyword in glsl |
| 892 | LOG_CRITICAL(HW_GPU, "Ipa Sampler Mode: centroid, not implemented"); | 899 | UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid"); |
| 893 | UNREACHABLE(); | ||
| 894 | break; | 900 | break; |
| 895 | } | 901 | case Tegra::Shader::IpaSampleMode::Default: |
| 896 | case Tegra::Shader::IpaSampleMode::Default: { | ||
| 897 | // Default, n/a | 902 | // Default, n/a |
| 898 | break; | 903 | break; |
| 899 | } | ||
| 900 | default: { | 904 | default: { |
| 901 | LOG_CRITICAL(HW_GPU, "Unhandled Ipa SampleMode: {}", static_cast<u32>(sample_mode)); | 905 | UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode)); |
| 902 | UNREACHABLE(); | 906 | break; |
| 903 | } | 907 | } |
| 904 | } | 908 | } |
| 905 | return out; | 909 | return out; |
| @@ -920,8 +924,7 @@ private: | |||
| 920 | return "output_attribute_" + std::to_string(index); | 924 | return "output_attribute_" + std::to_string(index); |
| 921 | } | 925 | } |
| 922 | 926 | ||
| 923 | LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); | 927 | UNIMPLEMENTED_MSG("Unhandled output attribute={}", index); |
| 924 | UNREACHABLE(); | ||
| 925 | return {}; | 928 | return {}; |
| 926 | } | 929 | } |
| 927 | } | 930 | } |
| @@ -951,9 +954,10 @@ private: | |||
| 951 | class GLSLGenerator { | 954 | class GLSLGenerator { |
| 952 | public: | 955 | public: |
| 953 | GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, | 956 | GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, |
| 954 | u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) | 957 | u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix, |
| 958 | std::size_t shader_length) | ||
| 955 | : subroutines(subroutines), program_code(program_code), main_offset(main_offset), | 959 | : subroutines(subroutines), program_code(program_code), main_offset(main_offset), |
| 956 | stage(stage), suffix(suffix) { | 960 | stage(stage), suffix(suffix), shader_length(shader_length) { |
| 957 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); | 961 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); |
| 958 | local_memory_size = header.GetLocalMemorySize(); | 962 | local_memory_size = header.GetLocalMemorySize(); |
| 959 | regs.SetLocalMemory(local_memory_size); | 963 | regs.SetLocalMemory(local_memory_size); |
| @@ -966,7 +970,7 @@ public: | |||
| 966 | 970 | ||
| 967 | /// Returns entries in the shader that are useful for external functions | 971 | /// Returns entries in the shader that are useful for external functions |
| 968 | ShaderEntries GetEntries() const { | 972 | ShaderEntries GetEntries() const { |
| 969 | return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; | 973 | return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), shader_length}; |
| 970 | } | 974 | } |
| 971 | 975 | ||
| 972 | private: | 976 | private: |
| @@ -1071,19 +1075,26 @@ private: | |||
| 1071 | const std::string& op_a, const std::string& op_b) const { | 1075 | const std::string& op_a, const std::string& op_b) const { |
| 1072 | using Tegra::Shader::PredCondition; | 1076 | using Tegra::Shader::PredCondition; |
| 1073 | static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { | 1077 | static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { |
| 1074 | {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, | 1078 | {PredCondition::LessThan, "<"}, |
| 1075 | {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, | 1079 | {PredCondition::Equal, "=="}, |
| 1076 | {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, | 1080 | {PredCondition::LessEqual, "<="}, |
| 1077 | {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="}, | 1081 | {PredCondition::GreaterThan, ">"}, |
| 1078 | {PredCondition::GreaterThanWithNan, ">"}, {PredCondition::GreaterEqualWithNan, ">="}}; | 1082 | {PredCondition::NotEqual, "!="}, |
| 1083 | {PredCondition::GreaterEqual, ">="}, | ||
| 1084 | {PredCondition::LessThanWithNan, "<"}, | ||
| 1085 | {PredCondition::NotEqualWithNan, "!="}, | ||
| 1086 | {PredCondition::LessEqualWithNan, "<="}, | ||
| 1087 | {PredCondition::GreaterThanWithNan, ">"}, | ||
| 1088 | {PredCondition::GreaterEqualWithNan, ">="}}; | ||
| 1079 | 1089 | ||
| 1080 | const auto& comparison{PredicateComparisonStrings.find(condition)}; | 1090 | const auto& comparison{PredicateComparisonStrings.find(condition)}; |
| 1081 | ASSERT_MSG(comparison != PredicateComparisonStrings.end(), | 1091 | UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonStrings.end(), |
| 1082 | "Unknown predicate comparison operation"); | 1092 | "Unknown predicate comparison operation"); |
| 1083 | 1093 | ||
| 1084 | std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; | 1094 | std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; |
| 1085 | if (condition == PredCondition::LessThanWithNan || | 1095 | if (condition == PredCondition::LessThanWithNan || |
| 1086 | condition == PredCondition::NotEqualWithNan || | 1096 | condition == PredCondition::NotEqualWithNan || |
| 1097 | condition == PredCondition::LessEqualWithNan || | ||
| 1087 | condition == PredCondition::GreaterThanWithNan || | 1098 | condition == PredCondition::GreaterThanWithNan || |
| 1088 | condition == PredCondition::GreaterEqualWithNan) { | 1099 | condition == PredCondition::GreaterEqualWithNan) { |
| 1089 | predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; | 1100 | predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; |
| @@ -1107,7 +1118,7 @@ private: | |||
| 1107 | }; | 1118 | }; |
| 1108 | 1119 | ||
| 1109 | auto op = PredicateOperationStrings.find(operation); | 1120 | auto op = PredicateOperationStrings.find(operation); |
| 1110 | ASSERT_MSG(op != PredicateOperationStrings.end(), "Unknown predicate operation"); | 1121 | UNIMPLEMENTED_IF_MSG(op == PredicateOperationStrings.end(), "Unknown predicate operation"); |
| 1111 | return op->second; | 1122 | return op->second; |
| 1112 | } | 1123 | } |
| 1113 | 1124 | ||
| @@ -1205,8 +1216,7 @@ private: | |||
| 1205 | break; | 1216 | break; |
| 1206 | } | 1217 | } |
| 1207 | default: | 1218 | default: |
| 1208 | LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op)); | 1219 | UNIMPLEMENTED_MSG("Unimplemented logic operation={}", static_cast<u32>(logic_op)); |
| 1209 | UNREACHABLE(); | ||
| 1210 | } | 1220 | } |
| 1211 | 1221 | ||
| 1212 | if (dest != Tegra::Shader::Register::ZeroIndex) { | 1222 | if (dest != Tegra::Shader::Register::ZeroIndex) { |
| @@ -1224,9 +1234,8 @@ private: | |||
| 1224 | SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0"); | 1234 | SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0"); |
| 1225 | break; | 1235 | break; |
| 1226 | default: | 1236 | default: |
| 1227 | LOG_CRITICAL(HW_GPU, "Unimplemented predicate result mode: {}", | 1237 | UNIMPLEMENTED_MSG("Unimplemented predicate result mode: {}", |
| 1228 | static_cast<u32>(predicate_mode)); | 1238 | static_cast<u32>(predicate_mode)); |
| 1229 | UNREACHABLE(); | ||
| 1230 | } | 1239 | } |
| 1231 | } | 1240 | } |
| 1232 | 1241 | ||
| @@ -1257,14 +1266,7 @@ private: | |||
| 1257 | regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); | 1266 | regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); |
| 1258 | } | 1267 | } |
| 1259 | 1268 | ||
| 1260 | void WriteTexsInstruction(const Instruction& instr, const std::string& coord, | 1269 | void WriteTexsInstruction(const Instruction& instr, const std::string& texture) { |
| 1261 | const std::string& texture) { | ||
| 1262 | // Add an extra scope and declare the texture coords inside to prevent | ||
| 1263 | // overwriting them in case they are used as outputs of the texs instruction. | ||
| 1264 | shader.AddLine('{'); | ||
| 1265 | ++shader.scope; | ||
| 1266 | shader.AddLine(coord); | ||
| 1267 | |||
| 1268 | // TEXS has two destination registers and a swizzle. The first two elements in the swizzle | 1270 | // TEXS has two destination registers and a swizzle. The first two elements in the swizzle |
| 1269 | // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 | 1271 | // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 |
| 1270 | 1272 | ||
| @@ -1287,26 +1289,19 @@ private: | |||
| 1287 | 1289 | ||
| 1288 | ++written_components; | 1290 | ++written_components; |
| 1289 | } | 1291 | } |
| 1290 | |||
| 1291 | --shader.scope; | ||
| 1292 | shader.AddLine('}'); | ||
| 1293 | } | 1292 | } |
| 1294 | 1293 | ||
| 1295 | static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { | 1294 | static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { |
| 1296 | switch (texture_type) { | 1295 | switch (texture_type) { |
| 1297 | case Tegra::Shader::TextureType::Texture1D: { | 1296 | case Tegra::Shader::TextureType::Texture1D: |
| 1298 | return 1; | 1297 | return 1; |
| 1299 | } | 1298 | case Tegra::Shader::TextureType::Texture2D: |
| 1300 | case Tegra::Shader::TextureType::Texture2D: { | ||
| 1301 | return 2; | 1299 | return 2; |
| 1302 | } | ||
| 1303 | case Tegra::Shader::TextureType::Texture3D: | 1300 | case Tegra::Shader::TextureType::Texture3D: |
| 1304 | case Tegra::Shader::TextureType::TextureCube: { | 1301 | case Tegra::Shader::TextureType::TextureCube: |
| 1305 | return 3; | 1302 | return 3; |
| 1306 | } | ||
| 1307 | default: | 1303 | default: |
| 1308 | LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast<u32>(texture_type)); | 1304 | UNIMPLEMENTED_MSG("Unhandled texture type: {}", static_cast<u32>(texture_type)); |
| 1309 | UNREACHABLE(); | ||
| 1310 | return 0; | 1305 | return 0; |
| 1311 | } | 1306 | } |
| 1312 | } | 1307 | } |
| @@ -1342,7 +1337,7 @@ private: | |||
| 1342 | void EmitFragmentOutputsWrite() { | 1337 | void EmitFragmentOutputsWrite() { |
| 1343 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); | 1338 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); |
| 1344 | 1339 | ||
| 1345 | ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); | 1340 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Samplemask write is unimplemented"); |
| 1346 | 1341 | ||
| 1347 | shader.AddLine("if (alpha_test[0] != 0) {"); | 1342 | shader.AddLine("if (alpha_test[0] != 0) {"); |
| 1348 | ++shader.scope; | 1343 | ++shader.scope; |
| @@ -1408,7 +1403,7 @@ private: | |||
| 1408 | case Tegra::Shader::VideoType::Size32: | 1403 | case Tegra::Shader::VideoType::Size32: |
| 1409 | // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when | 1404 | // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when |
| 1410 | // this type is used (1 * 1 + 0 == 0x5b800000). Until a better | 1405 | // this type is used (1 * 1 + 0 == 0x5b800000). Until a better |
| 1411 | // explanation is found: assert. | 1406 | // explanation is found: abort. |
| 1412 | UNIMPLEMENTED(); | 1407 | UNIMPLEMENTED(); |
| 1413 | return zero; | 1408 | return zero; |
| 1414 | case Tegra::Shader::VideoType::Invalid: | 1409 | case Tegra::Shader::VideoType::Invalid: |
| @@ -1464,8 +1459,7 @@ private: | |||
| 1464 | 1459 | ||
| 1465 | // Decoding failure | 1460 | // Decoding failure |
| 1466 | if (!opcode) { | 1461 | if (!opcode) { |
| 1467 | LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); | 1462 | UNIMPLEMENTED_MSG("Unhandled instruction: {0:x}", instr.value); |
| 1468 | UNREACHABLE(); | ||
| 1469 | return offset + 1; | 1463 | return offset + 1; |
| 1470 | } | 1464 | } |
| 1471 | 1465 | ||
| @@ -1473,8 +1467,8 @@ private: | |||
| 1473 | fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value)); | 1467 | fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value)); |
| 1474 | 1468 | ||
| 1475 | using Tegra::Shader::Pred; | 1469 | using Tegra::Shader::Pred; |
| 1476 | ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, | 1470 | UNIMPLEMENTED_IF_MSG(instr.pred.full_pred == Pred::NeverExecute, |
| 1477 | "NeverExecute predicate not implemented"); | 1471 | "NeverExecute predicate not implemented"); |
| 1478 | 1472 | ||
| 1479 | // Some instructions (like SSY) don't have a predicate field, they are always | 1473 | // Some instructions (like SSY) don't have a predicate field, they are always |
| 1480 | // unconditionally executed. | 1474 | // unconditionally executed. |
| @@ -1517,37 +1511,36 @@ private: | |||
| 1517 | case OpCode::Id::FMUL_R: | 1511 | case OpCode::Id::FMUL_R: |
| 1518 | case OpCode::Id::FMUL_IMM: { | 1512 | case OpCode::Id::FMUL_IMM: { |
| 1519 | // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. | 1513 | // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. |
| 1520 | ASSERT_MSG(instr.fmul.tab5cb8_2 == 0, "FMUL tab5cb8_2({}) is not implemented", | 1514 | UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0, |
| 1521 | instr.fmul.tab5cb8_2.Value()); | 1515 | "FMUL tab5cb8_2({}) is not implemented", |
| 1522 | ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented", | 1516 | instr.fmul.tab5cb8_2.Value()); |
| 1523 | instr.fmul.tab5c68_1.Value()); | 1517 | UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0, |
| 1524 | ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented", | 1518 | "FMUL tab5cb8_1({}) is not implemented", |
| 1525 | instr.fmul.tab5c68_0 | 1519 | instr.fmul.tab5c68_1.Value()); |
| 1526 | .Value()); // SMO typical sends 1 here which seems to be the default | 1520 | UNIMPLEMENTED_IF_MSG( |
| 1527 | ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); | 1521 | instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", |
| 1522 | instr.fmul.tab5c68_0 | ||
| 1523 | .Value()); // SMO typical sends 1 here which seems to be the default | ||
| 1524 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1525 | "Condition codes generation in FMUL is not implemented"); | ||
| 1528 | 1526 | ||
| 1529 | op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); | 1527 | op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); |
| 1530 | 1528 | ||
| 1531 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, | 1529 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, |
| 1532 | instr.alu.saturate_d, 0, true); | 1530 | instr.alu.saturate_d, 0, true); |
| 1533 | if (instr.generates_cc) { | ||
| 1534 | LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code"); | ||
| 1535 | UNREACHABLE(); | ||
| 1536 | } | ||
| 1537 | break; | 1531 | break; |
| 1538 | } | 1532 | } |
| 1539 | case OpCode::Id::FADD_C: | 1533 | case OpCode::Id::FADD_C: |
| 1540 | case OpCode::Id::FADD_R: | 1534 | case OpCode::Id::FADD_R: |
| 1541 | case OpCode::Id::FADD_IMM: { | 1535 | case OpCode::Id::FADD_IMM: { |
| 1536 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1537 | "Condition codes generation in FADD is not implemented"); | ||
| 1538 | |||
| 1542 | op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); | 1539 | op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); |
| 1543 | op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); | 1540 | op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); |
| 1544 | 1541 | ||
| 1545 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, | 1542 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, |
| 1546 | instr.alu.saturate_d, 0, true); | 1543 | instr.alu.saturate_d, 0, true); |
| 1547 | if (instr.generates_cc) { | ||
| 1548 | LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code"); | ||
| 1549 | UNREACHABLE(); | ||
| 1550 | } | ||
| 1551 | break; | 1544 | break; |
| 1552 | } | 1545 | } |
| 1553 | case OpCode::Id::MUFU: { | 1546 | case OpCode::Id::MUFU: { |
| @@ -1582,15 +1575,17 @@ private: | |||
| 1582 | instr.alu.saturate_d, 0, true); | 1575 | instr.alu.saturate_d, 0, true); |
| 1583 | break; | 1576 | break; |
| 1584 | default: | 1577 | default: |
| 1585 | LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", | 1578 | UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}", |
| 1586 | static_cast<unsigned>(instr.sub_op.Value())); | 1579 | static_cast<unsigned>(instr.sub_op.Value())); |
| 1587 | UNREACHABLE(); | ||
| 1588 | } | 1580 | } |
| 1589 | break; | 1581 | break; |
| 1590 | } | 1582 | } |
| 1591 | case OpCode::Id::FMNMX_C: | 1583 | case OpCode::Id::FMNMX_C: |
| 1592 | case OpCode::Id::FMNMX_R: | 1584 | case OpCode::Id::FMNMX_R: |
| 1593 | case OpCode::Id::FMNMX_IMM: { | 1585 | case OpCode::Id::FMNMX_IMM: { |
| 1586 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1587 | "Condition codes generation in FMNMX is not implemented"); | ||
| 1588 | |||
| 1594 | op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); | 1589 | op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); |
| 1595 | op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); | 1590 | op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); |
| 1596 | 1591 | ||
| @@ -1601,10 +1596,6 @@ private: | |||
| 1601 | '(' + condition + ") ? min(" + parameters + ") : max(" + | 1596 | '(' + condition + ") ? min(" + parameters + ") : max(" + |
| 1602 | parameters + ')', | 1597 | parameters + ')', |
| 1603 | 1, 1, false, 0, true); | 1598 | 1, 1, false, 0, true); |
| 1604 | if (instr.generates_cc) { | ||
| 1605 | LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code"); | ||
| 1606 | UNREACHABLE(); | ||
| 1607 | } | ||
| 1608 | break; | 1599 | break; |
| 1609 | } | 1600 | } |
| 1610 | case OpCode::Id::RRO_C: | 1601 | case OpCode::Id::RRO_C: |
| @@ -1617,9 +1608,7 @@ private: | |||
| 1617 | break; | 1608 | break; |
| 1618 | } | 1609 | } |
| 1619 | default: { | 1610 | default: { |
| 1620 | LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", | 1611 | UNIMPLEMENTED_MSG("Unhandled arithmetic instruction: {}", opcode->get().GetName()); |
| 1621 | opcode->get().GetName()); | ||
| 1622 | UNREACHABLE(); | ||
| 1623 | } | 1612 | } |
| 1624 | } | 1613 | } |
| 1625 | break; | 1614 | break; |
| @@ -1631,17 +1620,19 @@ private: | |||
| 1631 | break; | 1620 | break; |
| 1632 | } | 1621 | } |
| 1633 | case OpCode::Id::FMUL32_IMM: { | 1622 | case OpCode::Id::FMUL32_IMM: { |
| 1623 | UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, | ||
| 1624 | "Condition codes generation in FMUL32 is not implemented"); | ||
| 1625 | |||
| 1634 | regs.SetRegisterToFloat(instr.gpr0, 0, | 1626 | regs.SetRegisterToFloat(instr.gpr0, 0, |
| 1635 | regs.GetRegisterAsFloat(instr.gpr8) + " * " + | 1627 | regs.GetRegisterAsFloat(instr.gpr8) + " * " + |
| 1636 | GetImmediate32(instr), | 1628 | GetImmediate32(instr), |
| 1637 | 1, 1, instr.fmul32.saturate, 0, true); | 1629 | 1, 1, instr.fmul32.saturate, 0, true); |
| 1638 | if (instr.op_32.generates_cc) { | ||
| 1639 | LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code"); | ||
| 1640 | UNREACHABLE(); | ||
| 1641 | } | ||
| 1642 | break; | 1630 | break; |
| 1643 | } | 1631 | } |
| 1644 | case OpCode::Id::FADD32I: { | 1632 | case OpCode::Id::FADD32I: { |
| 1633 | UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, | ||
| 1634 | "Condition codes generation in FADD32I is not implemented"); | ||
| 1635 | |||
| 1645 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 1636 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); |
| 1646 | std::string op_b = GetImmediate32(instr); | 1637 | std::string op_b = GetImmediate32(instr); |
| 1647 | 1638 | ||
| @@ -1662,23 +1653,22 @@ private: | |||
| 1662 | } | 1653 | } |
| 1663 | 1654 | ||
| 1664 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); | 1655 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); |
| 1665 | if (instr.op_32.generates_cc) { | ||
| 1666 | LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code"); | ||
| 1667 | UNREACHABLE(); | ||
| 1668 | } | ||
| 1669 | break; | 1656 | break; |
| 1670 | } | 1657 | } |
| 1671 | } | 1658 | } |
| 1672 | break; | 1659 | break; |
| 1673 | } | 1660 | } |
| 1674 | case OpCode::Type::Bfe: { | 1661 | case OpCode::Type::Bfe: { |
| 1675 | ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); | 1662 | UNIMPLEMENTED_IF(instr.bfe.negate_b); |
| 1676 | 1663 | ||
| 1677 | std::string op_a = instr.bfe.negate_a ? "-" : ""; | 1664 | std::string op_a = instr.bfe.negate_a ? "-" : ""; |
| 1678 | op_a += regs.GetRegisterAsInteger(instr.gpr8); | 1665 | op_a += regs.GetRegisterAsInteger(instr.gpr8); |
| 1679 | 1666 | ||
| 1680 | switch (opcode->get().GetId()) { | 1667 | switch (opcode->get().GetId()) { |
| 1681 | case OpCode::Id::BFE_IMM: { | 1668 | case OpCode::Id::BFE_IMM: { |
| 1669 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1670 | "Condition codes generation in BFE is not implemented"); | ||
| 1671 | |||
| 1682 | std::string inner_shift = | 1672 | std::string inner_shift = |
| 1683 | '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; | 1673 | '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; |
| 1684 | std::string outer_shift = | 1674 | std::string outer_shift = |
| @@ -1686,15 +1676,10 @@ private: | |||
| 1686 | std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; | 1676 | std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; |
| 1687 | 1677 | ||
| 1688 | regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); | 1678 | regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); |
| 1689 | if (instr.generates_cc) { | ||
| 1690 | LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code"); | ||
| 1691 | UNREACHABLE(); | ||
| 1692 | } | ||
| 1693 | break; | 1679 | break; |
| 1694 | } | 1680 | } |
| 1695 | default: { | 1681 | default: { |
| 1696 | LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName()); | 1682 | UNIMPLEMENTED_MSG("Unhandled BFE instruction: {}", opcode->get().GetName()); |
| 1697 | UNREACHABLE(); | ||
| 1698 | } | 1683 | } |
| 1699 | } | 1684 | } |
| 1700 | 1685 | ||
| @@ -1719,6 +1704,9 @@ private: | |||
| 1719 | case OpCode::Id::SHR_C: | 1704 | case OpCode::Id::SHR_C: |
| 1720 | case OpCode::Id::SHR_R: | 1705 | case OpCode::Id::SHR_R: |
| 1721 | case OpCode::Id::SHR_IMM: { | 1706 | case OpCode::Id::SHR_IMM: { |
| 1707 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1708 | "Condition codes generation in SHR is not implemented"); | ||
| 1709 | |||
| 1722 | if (!instr.shift.is_signed) { | 1710 | if (!instr.shift.is_signed) { |
| 1723 | // Logical shift right | 1711 | // Logical shift right |
| 1724 | op_a = "uint(" + op_a + ')'; | 1712 | op_a = "uint(" + op_a + ')'; |
| @@ -1727,24 +1715,17 @@ private: | |||
| 1727 | // Cast to int is superfluous for arithmetic shift, it's only for a logical shift | 1715 | // Cast to int is superfluous for arithmetic shift, it's only for a logical shift |
| 1728 | regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', | 1716 | regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', |
| 1729 | 1, 1); | 1717 | 1, 1); |
| 1730 | if (instr.generates_cc) { | ||
| 1731 | LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code"); | ||
| 1732 | UNREACHABLE(); | ||
| 1733 | } | ||
| 1734 | break; | 1718 | break; |
| 1735 | } | 1719 | } |
| 1736 | case OpCode::Id::SHL_C: | 1720 | case OpCode::Id::SHL_C: |
| 1737 | case OpCode::Id::SHL_R: | 1721 | case OpCode::Id::SHL_R: |
| 1738 | case OpCode::Id::SHL_IMM: | 1722 | case OpCode::Id::SHL_IMM: |
| 1723 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1724 | "Condition codes generation in SHL is not implemented"); | ||
| 1739 | regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); | 1725 | regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); |
| 1740 | if (instr.generates_cc) { | ||
| 1741 | LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code"); | ||
| 1742 | UNREACHABLE(); | ||
| 1743 | } | ||
| 1744 | break; | 1726 | break; |
| 1745 | default: { | 1727 | default: { |
| 1746 | LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); | 1728 | UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName()); |
| 1747 | UNREACHABLE(); | ||
| 1748 | } | 1729 | } |
| 1749 | } | 1730 | } |
| 1750 | break; | 1731 | break; |
| @@ -1755,17 +1736,19 @@ private: | |||
| 1755 | 1736 | ||
| 1756 | switch (opcode->get().GetId()) { | 1737 | switch (opcode->get().GetId()) { |
| 1757 | case OpCode::Id::IADD32I: | 1738 | case OpCode::Id::IADD32I: |
| 1739 | UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, | ||
| 1740 | "Condition codes generation in IADD32I is not implemented"); | ||
| 1741 | |||
| 1758 | if (instr.iadd32i.negate_a) | 1742 | if (instr.iadd32i.negate_a) |
| 1759 | op_a = "-(" + op_a + ')'; | 1743 | op_a = "-(" + op_a + ')'; |
| 1760 | 1744 | ||
| 1761 | regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, | 1745 | regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, |
| 1762 | instr.iadd32i.saturate != 0); | 1746 | instr.iadd32i.saturate != 0); |
| 1763 | if (instr.op_32.generates_cc) { | ||
| 1764 | LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code"); | ||
| 1765 | UNREACHABLE(); | ||
| 1766 | } | ||
| 1767 | break; | 1747 | break; |
| 1768 | case OpCode::Id::LOP32I: { | 1748 | case OpCode::Id::LOP32I: { |
| 1749 | UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, | ||
| 1750 | "Condition codes generation in LOP32I is not implemented"); | ||
| 1751 | |||
| 1769 | if (instr.alu.lop32i.invert_a) | 1752 | if (instr.alu.lop32i.invert_a) |
| 1770 | op_a = "~(" + op_a + ')'; | 1753 | op_a = "~(" + op_a + ')'; |
| 1771 | 1754 | ||
| @@ -1775,16 +1758,11 @@ private: | |||
| 1775 | WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, | 1758 | WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, |
| 1776 | Tegra::Shader::PredicateResultMode::None, | 1759 | Tegra::Shader::PredicateResultMode::None, |
| 1777 | Tegra::Shader::Pred::UnusedIndex); | 1760 | Tegra::Shader::Pred::UnusedIndex); |
| 1778 | if (instr.op_32.generates_cc) { | ||
| 1779 | LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code"); | ||
| 1780 | UNREACHABLE(); | ||
| 1781 | } | ||
| 1782 | break; | 1761 | break; |
| 1783 | } | 1762 | } |
| 1784 | default: { | 1763 | default: { |
| 1785 | LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", | 1764 | UNIMPLEMENTED_MSG("Unhandled ArithmeticIntegerImmediate instruction: {}", |
| 1786 | opcode->get().GetName()); | 1765 | opcode->get().GetName()); |
| 1787 | UNREACHABLE(); | ||
| 1788 | } | 1766 | } |
| 1789 | } | 1767 | } |
| 1790 | break; | 1768 | break; |
| @@ -1807,6 +1785,9 @@ private: | |||
| 1807 | case OpCode::Id::IADD_C: | 1785 | case OpCode::Id::IADD_C: |
| 1808 | case OpCode::Id::IADD_R: | 1786 | case OpCode::Id::IADD_R: |
| 1809 | case OpCode::Id::IADD_IMM: { | 1787 | case OpCode::Id::IADD_IMM: { |
| 1788 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1789 | "Condition codes generation in IADD is not implemented"); | ||
| 1790 | |||
| 1810 | if (instr.alu_integer.negate_a) | 1791 | if (instr.alu_integer.negate_a) |
| 1811 | op_a = "-(" + op_a + ')'; | 1792 | op_a = "-(" + op_a + ')'; |
| 1812 | 1793 | ||
| @@ -1815,15 +1796,14 @@ private: | |||
| 1815 | 1796 | ||
| 1816 | regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, | 1797 | regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, |
| 1817 | instr.alu.saturate_d); | 1798 | instr.alu.saturate_d); |
| 1818 | if (instr.generates_cc) { | ||
| 1819 | LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code"); | ||
| 1820 | UNREACHABLE(); | ||
| 1821 | } | ||
| 1822 | break; | 1799 | break; |
| 1823 | } | 1800 | } |
| 1824 | case OpCode::Id::IADD3_C: | 1801 | case OpCode::Id::IADD3_C: |
| 1825 | case OpCode::Id::IADD3_R: | 1802 | case OpCode::Id::IADD3_R: |
| 1826 | case OpCode::Id::IADD3_IMM: { | 1803 | case OpCode::Id::IADD3_IMM: { |
| 1804 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1805 | "Condition codes generation in IADD3 is not implemented"); | ||
| 1806 | |||
| 1827 | std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); | 1807 | std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); |
| 1828 | 1808 | ||
| 1829 | auto apply_height = [](auto height, auto& oprand) { | 1809 | auto apply_height = [](auto height, auto& oprand) { |
| @@ -1837,9 +1817,8 @@ private: | |||
| 1837 | oprand = "((" + oprand + ") >> 16)"; | 1817 | oprand = "((" + oprand + ") >> 16)"; |
| 1838 | break; | 1818 | break; |
| 1839 | default: | 1819 | default: |
| 1840 | LOG_CRITICAL(HW_GPU, "Unhandled IADD3 height: {}", | 1820 | UNIMPLEMENTED_MSG("Unhandled IADD3 height: {}", |
| 1841 | static_cast<u32>(height.Value())); | 1821 | static_cast<u32>(height.Value())); |
| 1842 | UNREACHABLE(); | ||
| 1843 | } | 1822 | } |
| 1844 | }; | 1823 | }; |
| 1845 | 1824 | ||
| @@ -1880,16 +1859,14 @@ private: | |||
| 1880 | } | 1859 | } |
| 1881 | 1860 | ||
| 1882 | regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); | 1861 | regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); |
| 1883 | |||
| 1884 | if (instr.generates_cc) { | ||
| 1885 | LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code"); | ||
| 1886 | UNREACHABLE(); | ||
| 1887 | } | ||
| 1888 | break; | 1862 | break; |
| 1889 | } | 1863 | } |
| 1890 | case OpCode::Id::ISCADD_C: | 1864 | case OpCode::Id::ISCADD_C: |
| 1891 | case OpCode::Id::ISCADD_R: | 1865 | case OpCode::Id::ISCADD_R: |
| 1892 | case OpCode::Id::ISCADD_IMM: { | 1866 | case OpCode::Id::ISCADD_IMM: { |
| 1867 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1868 | "Condition codes generation in ISCADD is not implemented"); | ||
| 1869 | |||
| 1893 | if (instr.alu_integer.negate_a) | 1870 | if (instr.alu_integer.negate_a) |
| 1894 | op_a = "-(" + op_a + ')'; | 1871 | op_a = "-(" + op_a + ')'; |
| 1895 | 1872 | ||
| @@ -1900,10 +1877,6 @@ private: | |||
| 1900 | 1877 | ||
| 1901 | regs.SetRegisterToInteger(instr.gpr0, true, 0, | 1878 | regs.SetRegisterToInteger(instr.gpr0, true, 0, |
| 1902 | "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); | 1879 | "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); |
| 1903 | if (instr.generates_cc) { | ||
| 1904 | LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code"); | ||
| 1905 | UNREACHABLE(); | ||
| 1906 | } | ||
| 1907 | break; | 1880 | break; |
| 1908 | } | 1881 | } |
| 1909 | case OpCode::Id::POPC_C: | 1882 | case OpCode::Id::POPC_C: |
| @@ -1927,6 +1900,9 @@ private: | |||
| 1927 | case OpCode::Id::LOP_C: | 1900 | case OpCode::Id::LOP_C: |
| 1928 | case OpCode::Id::LOP_R: | 1901 | case OpCode::Id::LOP_R: |
| 1929 | case OpCode::Id::LOP_IMM: { | 1902 | case OpCode::Id::LOP_IMM: { |
| 1903 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1904 | "Condition codes generation in LOP is not implemented"); | ||
| 1905 | |||
| 1930 | if (instr.alu.lop.invert_a) | 1906 | if (instr.alu.lop.invert_a) |
| 1931 | op_a = "~(" + op_a + ')'; | 1907 | op_a = "~(" + op_a + ')'; |
| 1932 | 1908 | ||
| @@ -1935,15 +1911,14 @@ private: | |||
| 1935 | 1911 | ||
| 1936 | WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, | 1912 | WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, |
| 1937 | instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); | 1913 | instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); |
| 1938 | if (instr.generates_cc) { | ||
| 1939 | LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code"); | ||
| 1940 | UNREACHABLE(); | ||
| 1941 | } | ||
| 1942 | break; | 1914 | break; |
| 1943 | } | 1915 | } |
| 1944 | case OpCode::Id::LOP3_C: | 1916 | case OpCode::Id::LOP3_C: |
| 1945 | case OpCode::Id::LOP3_R: | 1917 | case OpCode::Id::LOP3_R: |
| 1946 | case OpCode::Id::LOP3_IMM: { | 1918 | case OpCode::Id::LOP3_IMM: { |
| 1919 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 1920 | "Condition codes generation in LOP3 is not implemented"); | ||
| 1921 | |||
| 1947 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); | 1922 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); |
| 1948 | std::string lut; | 1923 | std::string lut; |
| 1949 | 1924 | ||
| @@ -1954,17 +1929,15 @@ private: | |||
| 1954 | } | 1929 | } |
| 1955 | 1930 | ||
| 1956 | WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); | 1931 | WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); |
| 1957 | if (instr.generates_cc) { | ||
| 1958 | LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code"); | ||
| 1959 | UNREACHABLE(); | ||
| 1960 | } | ||
| 1961 | break; | 1932 | break; |
| 1962 | } | 1933 | } |
| 1963 | case OpCode::Id::IMNMX_C: | 1934 | case OpCode::Id::IMNMX_C: |
| 1964 | case OpCode::Id::IMNMX_R: | 1935 | case OpCode::Id::IMNMX_R: |
| 1965 | case OpCode::Id::IMNMX_IMM: { | 1936 | case OpCode::Id::IMNMX_IMM: { |
| 1966 | ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, | 1937 | UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None); |
| 1967 | "Unimplemented"); | 1938 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, |
| 1939 | "Condition codes generation in IMNMX is not implemented"); | ||
| 1940 | |||
| 1968 | const std::string condition = | 1941 | const std::string condition = |
| 1969 | GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); | 1942 | GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); |
| 1970 | const std::string parameters = op_a + ',' + op_b; | 1943 | const std::string parameters = op_a + ',' + op_b; |
| @@ -1972,10 +1945,6 @@ private: | |||
| 1972 | '(' + condition + ") ? min(" + parameters + ") : max(" + | 1945 | '(' + condition + ") ? min(" + parameters + ") : max(" + |
| 1973 | parameters + ')', | 1946 | parameters + ')', |
| 1974 | 1, 1); | 1947 | 1, 1); |
| 1975 | if (instr.generates_cc) { | ||
| 1976 | LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code"); | ||
| 1977 | UNREACHABLE(); | ||
| 1978 | } | ||
| 1979 | break; | 1948 | break; |
| 1980 | } | 1949 | } |
| 1981 | case OpCode::Id::LEA_R2: | 1950 | case OpCode::Id::LEA_R2: |
| @@ -2030,24 +1999,19 @@ private: | |||
| 2030 | op_b = regs.GetRegisterAsInteger(instr.gpr8); | 1999 | op_b = regs.GetRegisterAsInteger(instr.gpr8); |
| 2031 | op_a = std::to_string(instr.lea.imm.entry_a); | 2000 | op_a = std::to_string(instr.lea.imm.entry_a); |
| 2032 | op_c = std::to_string(instr.lea.imm.entry_b); | 2001 | op_c = std::to_string(instr.lea.imm.entry_b); |
| 2033 | LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", | 2002 | UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName()); |
| 2034 | opcode->get().GetName()); | ||
| 2035 | UNREACHABLE(); | ||
| 2036 | } | ||
| 2037 | } | 2003 | } |
| 2038 | if (instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 2039 | LOG_ERROR(HW_GPU, "Unhandled LEA Predicate"); | ||
| 2040 | UNREACHABLE(); | ||
| 2041 | } | 2004 | } |
| 2005 | UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex), | ||
| 2006 | "Unhandled LEA Predicate"); | ||
| 2042 | const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; | 2007 | const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; |
| 2043 | regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); | 2008 | regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); |
| 2044 | 2009 | ||
| 2045 | break; | 2010 | break; |
| 2046 | } | 2011 | } |
| 2047 | default: { | 2012 | default: { |
| 2048 | LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", | 2013 | UNIMPLEMENTED_MSG("Unhandled ArithmeticInteger instruction: {}", |
| 2049 | opcode->get().GetName()); | 2014 | opcode->get().GetName()); |
| 2050 | UNREACHABLE(); | ||
| 2051 | } | 2015 | } |
| 2052 | } | 2016 | } |
| 2053 | 2017 | ||
| @@ -2056,7 +2020,7 @@ private: | |||
| 2056 | case OpCode::Type::ArithmeticHalf: { | 2020 | case OpCode::Type::ArithmeticHalf: { |
| 2057 | if (opcode->get().GetId() == OpCode::Id::HADD2_C || | 2021 | if (opcode->get().GetId() == OpCode::Id::HADD2_C || |
| 2058 | opcode->get().GetId() == OpCode::Id::HADD2_R) { | 2022 | opcode->get().GetId() == OpCode::Id::HADD2_R) { |
| 2059 | ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); | 2023 | UNIMPLEMENTED_IF(instr.alu_half.ftz != 0); |
| 2060 | } | 2024 | } |
| 2061 | const bool negate_a = | 2025 | const bool negate_a = |
| 2062 | opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; | 2026 | opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; |
| @@ -2094,9 +2058,8 @@ private: | |||
| 2094 | case OpCode::Id::HMUL2_R: | 2058 | case OpCode::Id::HMUL2_R: |
| 2095 | return '(' + op_a + " * " + op_b + ')'; | 2059 | return '(' + op_a + " * " + op_b + ')'; |
| 2096 | default: | 2060 | default: |
| 2097 | LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", | 2061 | UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", |
| 2098 | opcode->get().GetName()); | 2062 | opcode->get().GetName()); |
| 2099 | UNREACHABLE(); | ||
| 2100 | return std::string("0"); | 2063 | return std::string("0"); |
| 2101 | } | 2064 | } |
| 2102 | }(); | 2065 | }(); |
| @@ -2107,10 +2070,10 @@ private: | |||
| 2107 | } | 2070 | } |
| 2108 | case OpCode::Type::ArithmeticHalfImmediate: { | 2071 | case OpCode::Type::ArithmeticHalfImmediate: { |
| 2109 | if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { | 2072 | if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { |
| 2110 | ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); | 2073 | UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0); |
| 2111 | } else { | 2074 | } else { |
| 2112 | ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, | 2075 | UNIMPLEMENTED_IF(instr.alu_half_imm.precision != |
| 2113 | "Unimplemented"); | 2076 | Tegra::Shader::HalfPrecision::None); |
| 2114 | } | 2077 | } |
| 2115 | 2078 | ||
| 2116 | const std::string op_a = GetHalfFloat( | 2079 | const std::string op_a = GetHalfFloat( |
| @@ -2140,11 +2103,14 @@ private: | |||
| 2140 | std::string op_b = instr.ffma.negate_b ? "-" : ""; | 2103 | std::string op_b = instr.ffma.negate_b ? "-" : ""; |
| 2141 | std::string op_c = instr.ffma.negate_c ? "-" : ""; | 2104 | std::string op_c = instr.ffma.negate_c ? "-" : ""; |
| 2142 | 2105 | ||
| 2143 | ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented"); | 2106 | UNIMPLEMENTED_IF_MSG(instr.ffma.cc != 0, "FFMA cc not implemented"); |
| 2144 | ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented", | 2107 | UNIMPLEMENTED_IF_MSG( |
| 2145 | instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO | 2108 | instr.ffma.tab5980_0 != 1, "FFMA tab5980_0({}) not implemented", |
| 2146 | ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", | 2109 | instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO |
| 2147 | instr.ffma.tab5980_1.Value()); | 2110 | UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented", |
| 2111 | instr.ffma.tab5980_1.Value()); | ||
| 2112 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 2113 | "Condition codes generation in FFMA is not implemented"); | ||
| 2148 | 2114 | ||
| 2149 | switch (opcode->get().GetId()) { | 2115 | switch (opcode->get().GetId()) { |
| 2150 | case OpCode::Id::FFMA_CR: { | 2116 | case OpCode::Id::FFMA_CR: { |
| @@ -2170,27 +2136,19 @@ private: | |||
| 2170 | break; | 2136 | break; |
| 2171 | } | 2137 | } |
| 2172 | default: { | 2138 | default: { |
| 2173 | LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName()); | 2139 | UNIMPLEMENTED_MSG("Unhandled FFMA instruction: {}", opcode->get().GetName()); |
| 2174 | UNREACHABLE(); | ||
| 2175 | } | 2140 | } |
| 2176 | } | 2141 | } |
| 2177 | 2142 | ||
| 2178 | regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', | 2143 | regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', |
| 2179 | 1, 1, instr.alu.saturate_d, 0, true); | 2144 | 1, 1, instr.alu.saturate_d, 0, true); |
| 2180 | if (instr.generates_cc) { | ||
| 2181 | LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code"); | ||
| 2182 | UNREACHABLE(); | ||
| 2183 | } | ||
| 2184 | |||
| 2185 | break; | 2145 | break; |
| 2186 | } | 2146 | } |
| 2187 | case OpCode::Type::Hfma2: { | 2147 | case OpCode::Type::Hfma2: { |
| 2188 | if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) { | 2148 | if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) { |
| 2189 | ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, | 2149 | UNIMPLEMENTED_IF(instr.hfma2.rr.precision != Tegra::Shader::HalfPrecision::None); |
| 2190 | "Unimplemented"); | ||
| 2191 | } else { | 2150 | } else { |
| 2192 | ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, | 2151 | UNIMPLEMENTED_IF(instr.hfma2.precision != Tegra::Shader::HalfPrecision::None); |
| 2193 | "Unimplemented"); | ||
| 2194 | } | 2152 | } |
| 2195 | const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR | 2153 | const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR |
| 2196 | ? instr.hfma2.rr.saturate != 0 | 2154 | ? instr.hfma2.rr.saturate != 0 |
| @@ -2240,7 +2198,7 @@ private: | |||
| 2240 | case OpCode::Type::Conversion: { | 2198 | case OpCode::Type::Conversion: { |
| 2241 | switch (opcode->get().GetId()) { | 2199 | switch (opcode->get().GetId()) { |
| 2242 | case OpCode::Id::I2I_R: { | 2200 | case OpCode::Id::I2I_R: { |
| 2243 | ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); | 2201 | UNIMPLEMENTED_IF(instr.conversion.selector); |
| 2244 | 2202 | ||
| 2245 | std::string op_a = regs.GetRegisterAsInteger( | 2203 | std::string op_a = regs.GetRegisterAsInteger( |
| 2246 | instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); | 2204 | instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); |
| @@ -2260,8 +2218,10 @@ private: | |||
| 2260 | } | 2218 | } |
| 2261 | case OpCode::Id::I2F_R: | 2219 | case OpCode::Id::I2F_R: |
| 2262 | case OpCode::Id::I2F_C: { | 2220 | case OpCode::Id::I2F_C: { |
| 2263 | ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); | 2221 | UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); |
| 2264 | ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); | 2222 | UNIMPLEMENTED_IF(instr.conversion.selector); |
| 2223 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 2224 | "Condition codes generation in I2F is not implemented"); | ||
| 2265 | 2225 | ||
| 2266 | std::string op_a{}; | 2226 | std::string op_a{}; |
| 2267 | 2227 | ||
| @@ -2286,16 +2246,13 @@ private: | |||
| 2286 | } | 2246 | } |
| 2287 | 2247 | ||
| 2288 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); | 2248 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); |
| 2289 | |||
| 2290 | if (instr.generates_cc) { | ||
| 2291 | LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code"); | ||
| 2292 | UNREACHABLE(); | ||
| 2293 | } | ||
| 2294 | break; | 2249 | break; |
| 2295 | } | 2250 | } |
| 2296 | case OpCode::Id::F2F_R: { | 2251 | case OpCode::Id::F2F_R: { |
| 2297 | ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); | 2252 | UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); |
| 2298 | ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); | 2253 | UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); |
| 2254 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 2255 | "Condition codes generation in F2F is not implemented"); | ||
| 2299 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); | 2256 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); |
| 2300 | 2257 | ||
| 2301 | if (instr.conversion.abs_a) { | 2258 | if (instr.conversion.abs_a) { |
| @@ -2322,23 +2279,19 @@ private: | |||
| 2322 | op_a = "trunc(" + op_a + ')'; | 2279 | op_a = "trunc(" + op_a + ')'; |
| 2323 | break; | 2280 | break; |
| 2324 | default: | 2281 | default: |
| 2325 | LOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", | 2282 | UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", |
| 2326 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); | 2283 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); |
| 2327 | UNREACHABLE(); | ||
| 2328 | break; | 2284 | break; |
| 2329 | } | 2285 | } |
| 2330 | 2286 | ||
| 2331 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); | 2287 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); |
| 2332 | |||
| 2333 | if (instr.generates_cc) { | ||
| 2334 | LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code"); | ||
| 2335 | UNREACHABLE(); | ||
| 2336 | } | ||
| 2337 | break; | 2288 | break; |
| 2338 | } | 2289 | } |
| 2339 | case OpCode::Id::F2I_R: | 2290 | case OpCode::Id::F2I_R: |
| 2340 | case OpCode::Id::F2I_C: { | 2291 | case OpCode::Id::F2I_C: { |
| 2341 | ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); | 2292 | UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); |
| 2293 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 2294 | "Condition codes generation in F2I is not implemented"); | ||
| 2342 | std::string op_a{}; | 2295 | std::string op_a{}; |
| 2343 | 2296 | ||
| 2344 | if (instr.is_b_gpr) { | 2297 | if (instr.is_b_gpr) { |
| @@ -2369,9 +2322,8 @@ private: | |||
| 2369 | op_a = "trunc(" + op_a + ')'; | 2322 | op_a = "trunc(" + op_a + ')'; |
| 2370 | break; | 2323 | break; |
| 2371 | default: | 2324 | default: |
| 2372 | LOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", | 2325 | UNIMPLEMENTED_MSG("Unimplemented F2I rounding mode {}", |
| 2373 | static_cast<u32>(instr.conversion.f2i.rounding.Value())); | 2326 | static_cast<u32>(instr.conversion.f2i.rounding.Value())); |
| 2374 | UNREACHABLE(); | ||
| 2375 | break; | 2327 | break; |
| 2376 | } | 2328 | } |
| 2377 | 2329 | ||
| @@ -2383,16 +2335,10 @@ private: | |||
| 2383 | 2335 | ||
| 2384 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, | 2336 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, |
| 2385 | 1, false, 0, instr.conversion.dest_size); | 2337 | 1, false, 0, instr.conversion.dest_size); |
| 2386 | if (instr.generates_cc) { | ||
| 2387 | LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code"); | ||
| 2388 | UNREACHABLE(); | ||
| 2389 | } | ||
| 2390 | break; | 2338 | break; |
| 2391 | } | 2339 | } |
| 2392 | default: { | 2340 | default: { |
| 2393 | LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", | 2341 | UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName()); |
| 2394 | opcode->get().GetName()); | ||
| 2395 | UNREACHABLE(); | ||
| 2396 | } | 2342 | } |
| 2397 | } | 2343 | } |
| 2398 | break; | 2344 | break; |
| @@ -2401,10 +2347,10 @@ private: | |||
| 2401 | switch (opcode->get().GetId()) { | 2347 | switch (opcode->get().GetId()) { |
| 2402 | case OpCode::Id::LD_A: { | 2348 | case OpCode::Id::LD_A: { |
| 2403 | // Note: Shouldn't this be interp mode flat? As in no interpolation made. | 2349 | // Note: Shouldn't this be interp mode flat? As in no interpolation made. |
| 2404 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, | 2350 | UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, |
| 2405 | "Indirect attribute loads are not supported"); | 2351 | "Indirect attribute loads are not supported"); |
| 2406 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, | 2352 | UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, |
| 2407 | "Unaligned attribute loads are not supported"); | 2353 | "Unaligned attribute loads are not supported"); |
| 2408 | 2354 | ||
| 2409 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, | 2355 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, |
| 2410 | Tegra::Shader::IpaSampleMode::Default}; | 2356 | Tegra::Shader::IpaSampleMode::Default}; |
| @@ -2431,7 +2377,7 @@ private: | |||
| 2431 | break; | 2377 | break; |
| 2432 | } | 2378 | } |
| 2433 | case OpCode::Id::LD_C: { | 2379 | case OpCode::Id::LD_C: { |
| 2434 | ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); | 2380 | UNIMPLEMENTED_IF(instr.ld_c.unknown != 0); |
| 2435 | 2381 | ||
| 2436 | // Add an extra scope and declare the index register inside to prevent | 2382 | // Add an extra scope and declare the index register inside to prevent |
| 2437 | // overwriting it in case it is used as an output of the LD instruction. | 2383 | // overwriting it in case it is used as an output of the LD instruction. |
| @@ -2459,9 +2405,8 @@ private: | |||
| 2459 | break; | 2405 | break; |
| 2460 | } | 2406 | } |
| 2461 | default: | 2407 | default: |
| 2462 | LOG_CRITICAL(HW_GPU, "Unhandled type: {}", | 2408 | UNIMPLEMENTED_MSG("Unhandled type: {}", |
| 2463 | static_cast<unsigned>(instr.ld_c.type.Value())); | 2409 | static_cast<unsigned>(instr.ld_c.type.Value())); |
| 2464 | UNREACHABLE(); | ||
| 2465 | } | 2410 | } |
| 2466 | 2411 | ||
| 2467 | --shader.scope; | 2412 | --shader.scope; |
| @@ -2469,6 +2414,9 @@ private: | |||
| 2469 | break; | 2414 | break; |
| 2470 | } | 2415 | } |
| 2471 | case OpCode::Id::LD_L: { | 2416 | case OpCode::Id::LD_L: { |
| 2417 | UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}", | ||
| 2418 | static_cast<unsigned>(instr.ld_l.unknown.Value())); | ||
| 2419 | |||
| 2472 | // Add an extra scope and declare the index register inside to prevent | 2420 | // Add an extra scope and declare the index register inside to prevent |
| 2473 | // overwriting it in case it is used as an output of the LD instruction. | 2421 | // overwriting it in case it is used as an output of the LD instruction. |
| 2474 | shader.AddLine('{'); | 2422 | shader.AddLine('{'); |
| @@ -2481,20 +2429,13 @@ private: | |||
| 2481 | 2429 | ||
| 2482 | const std::string op_a = regs.GetLocalMemoryAsFloat("index"); | 2430 | const std::string op_a = regs.GetLocalMemoryAsFloat("index"); |
| 2483 | 2431 | ||
| 2484 | if (instr.ld_l.unknown != 1) { | ||
| 2485 | LOG_CRITICAL(HW_GPU, "LD_L Unhandled mode: {}", | ||
| 2486 | static_cast<unsigned>(instr.ld_l.unknown.Value())); | ||
| 2487 | UNREACHABLE(); | ||
| 2488 | } | ||
| 2489 | |||
| 2490 | switch (instr.ldst_sl.type.Value()) { | 2432 | switch (instr.ldst_sl.type.Value()) { |
| 2491 | case Tegra::Shader::StoreType::Bytes32: | 2433 | case Tegra::Shader::StoreType::Bytes32: |
| 2492 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); | 2434 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); |
| 2493 | break; | 2435 | break; |
| 2494 | default: | 2436 | default: |
| 2495 | LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}", | 2437 | UNIMPLEMENTED_MSG("LD_L Unhandled type: {}", |
| 2496 | static_cast<unsigned>(instr.ldst_sl.type.Value())); | 2438 | static_cast<unsigned>(instr.ldst_sl.type.Value())); |
| 2497 | UNREACHABLE(); | ||
| 2498 | } | 2439 | } |
| 2499 | 2440 | ||
| 2500 | --shader.scope; | 2441 | --shader.scope; |
| @@ -2502,10 +2443,10 @@ private: | |||
| 2502 | break; | 2443 | break; |
| 2503 | } | 2444 | } |
| 2504 | case OpCode::Id::ST_A: { | 2445 | case OpCode::Id::ST_A: { |
| 2505 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, | 2446 | UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, |
| 2506 | "Indirect attribute loads are not supported"); | 2447 | "Indirect attribute loads are not supported"); |
| 2507 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, | 2448 | UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, |
| 2508 | "Unaligned attribute loads are not supported"); | 2449 | "Unaligned attribute loads are not supported"); |
| 2509 | 2450 | ||
| 2510 | u64 next_element = instr.attribute.fmt20.element; | 2451 | u64 next_element = instr.attribute.fmt20.element; |
| 2511 | u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); | 2452 | u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); |
| @@ -2530,6 +2471,9 @@ private: | |||
| 2530 | break; | 2471 | break; |
| 2531 | } | 2472 | } |
| 2532 | case OpCode::Id::ST_L: { | 2473 | case OpCode::Id::ST_L: { |
| 2474 | UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}", | ||
| 2475 | static_cast<unsigned>(instr.st_l.unknown.Value())); | ||
| 2476 | |||
| 2533 | // Add an extra scope and declare the index register inside to prevent | 2477 | // Add an extra scope and declare the index register inside to prevent |
| 2534 | // overwriting it in case it is used as an output of the LD instruction. | 2478 | // overwriting it in case it is used as an output of the LD instruction. |
| 2535 | shader.AddLine('{'); | 2479 | shader.AddLine('{'); |
| @@ -2540,20 +2484,13 @@ private: | |||
| 2540 | 2484 | ||
| 2541 | shader.AddLine("uint index = (" + op + " / 4);"); | 2485 | shader.AddLine("uint index = (" + op + " / 4);"); |
| 2542 | 2486 | ||
| 2543 | if (instr.st_l.unknown != 0) { | ||
| 2544 | LOG_CRITICAL(HW_GPU, "ST_L Unhandled mode: {}", | ||
| 2545 | static_cast<unsigned>(instr.st_l.unknown.Value())); | ||
| 2546 | UNREACHABLE(); | ||
| 2547 | } | ||
| 2548 | |||
| 2549 | switch (instr.ldst_sl.type.Value()) { | 2487 | switch (instr.ldst_sl.type.Value()) { |
| 2550 | case Tegra::Shader::StoreType::Bytes32: | 2488 | case Tegra::Shader::StoreType::Bytes32: |
| 2551 | regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0)); | 2489 | regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0)); |
| 2552 | break; | 2490 | break; |
| 2553 | default: | 2491 | default: |
| 2554 | LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}", | 2492 | UNIMPLEMENTED_MSG("ST_L Unhandled type: {}", |
| 2555 | static_cast<unsigned>(instr.ldst_sl.type.Value())); | 2493 | static_cast<unsigned>(instr.ldst_sl.type.Value())); |
| 2556 | UNREACHABLE(); | ||
| 2557 | } | 2494 | } |
| 2558 | 2495 | ||
| 2559 | --shader.scope; | 2496 | --shader.scope; |
| @@ -2565,10 +2502,10 @@ private: | |||
| 2565 | std::string coord; | 2502 | std::string coord; |
| 2566 | const bool is_array = instr.tex.array != 0; | 2503 | const bool is_array = instr.tex.array != 0; |
| 2567 | 2504 | ||
| 2568 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2505 | UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2569 | "NODEP is not implemented"); | 2506 | "NODEP is not implemented"); |
| 2570 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | 2507 | UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), |
| 2571 | "AOFFI is not implemented"); | 2508 | "AOFFI is not implemented"); |
| 2572 | 2509 | ||
| 2573 | const bool depth_compare = | 2510 | const bool depth_compare = |
| 2574 | instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 2511 | instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); |
| @@ -2634,9 +2571,8 @@ private: | |||
| 2634 | break; | 2571 | break; |
| 2635 | } | 2572 | } |
| 2636 | default: | 2573 | default: |
| 2637 | LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", | 2574 | UNIMPLEMENTED_MSG("Unhandled coordinates number {}", |
| 2638 | static_cast<u32>(num_coordinates)); | 2575 | static_cast<u32>(num_coordinates)); |
| 2639 | UNREACHABLE(); | ||
| 2640 | 2576 | ||
| 2641 | // Fallback to interpreting as a 2D texture for now | 2577 | // Fallback to interpreting as a 2D texture for now |
| 2642 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2578 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| @@ -2646,7 +2582,6 @@ private: | |||
| 2646 | } | 2582 | } |
| 2647 | // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias | 2583 | // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias |
| 2648 | // or lod. | 2584 | // or lod. |
| 2649 | std::string op_c; | ||
| 2650 | 2585 | ||
| 2651 | const std::string sampler = | 2586 | const std::string sampler = |
| 2652 | GetSampler(instr.sampler, texture_type, is_array, depth_compare); | 2587 | GetSampler(instr.sampler, texture_type, is_array, depth_compare); |
| @@ -2669,34 +2604,41 @@ private: | |||
| 2669 | } | 2604 | } |
| 2670 | case Tegra::Shader::TextureProcessMode::LB: | 2605 | case Tegra::Shader::TextureProcessMode::LB: |
| 2671 | case Tegra::Shader::TextureProcessMode::LBA: { | 2606 | case Tegra::Shader::TextureProcessMode::LBA: { |
| 2672 | if (depth_compare) { | 2607 | const std::string bias = [&]() { |
| 2673 | if (is_array) | 2608 | if (depth_compare) { |
| 2674 | op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); | 2609 | if (is_array) |
| 2675 | else | 2610 | return regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); |
| 2676 | op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); | 2611 | else |
| 2677 | } else { | 2612 | return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); |
| 2678 | op_c = regs.GetRegisterAsFloat(instr.gpr20); | 2613 | } else { |
| 2679 | } | 2614 | return regs.GetRegisterAsFloat(instr.gpr20); |
| 2615 | } | ||
| 2616 | }(); | ||
| 2617 | shader.AddLine("float bias = " + bias + ';'); | ||
| 2618 | |||
| 2680 | // TODO: Figure if A suffix changes the equation at all. | 2619 | // TODO: Figure if A suffix changes the equation at all. |
| 2681 | texture = "texture(" + sampler + ", coords, " + op_c + ')'; | 2620 | texture = "texture(" + sampler + ", coords, bias)"; |
| 2682 | break; | 2621 | break; |
| 2683 | } | 2622 | } |
| 2684 | case Tegra::Shader::TextureProcessMode::LL: | 2623 | case Tegra::Shader::TextureProcessMode::LL: |
| 2685 | case Tegra::Shader::TextureProcessMode::LLA: { | 2624 | case Tegra::Shader::TextureProcessMode::LLA: { |
| 2686 | if (num_coordinates <= 2) { | 2625 | const std::string lod = [&]() { |
| 2687 | op_c = regs.GetRegisterAsFloat(instr.gpr20); | 2626 | if (num_coordinates <= 2) { |
| 2688 | } else { | 2627 | return regs.GetRegisterAsFloat(instr.gpr20); |
| 2689 | op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); | 2628 | } else { |
| 2690 | } | 2629 | return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); |
| 2630 | } | ||
| 2631 | }(); | ||
| 2632 | shader.AddLine("float lod = " + lod + ';'); | ||
| 2633 | |||
| 2691 | // TODO: Figure if A suffix changes the equation at all. | 2634 | // TODO: Figure if A suffix changes the equation at all. |
| 2692 | texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; | 2635 | texture = "textureLod(" + sampler + ", coords, lod)"; |
| 2693 | break; | 2636 | break; |
| 2694 | } | 2637 | } |
| 2695 | default: { | 2638 | default: { |
| 2696 | texture = "texture(" + sampler + ", coords)"; | 2639 | texture = "texture(" + sampler + ", coords)"; |
| 2697 | LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", | 2640 | UNIMPLEMENTED_MSG("Unhandled texture process mode {}", |
| 2698 | static_cast<u32>(instr.tex.GetTextureProcessMode())); | 2641 | static_cast<u32>(instr.tex.GetTextureProcessMode())); |
| 2699 | UNREACHABLE(); | ||
| 2700 | } | 2642 | } |
| 2701 | } | 2643 | } |
| 2702 | if (!depth_compare) { | 2644 | if (!depth_compare) { |
| @@ -2717,12 +2659,11 @@ private: | |||
| 2717 | break; | 2659 | break; |
| 2718 | } | 2660 | } |
| 2719 | case OpCode::Id::TEXS: { | 2661 | case OpCode::Id::TEXS: { |
| 2720 | std::string coord; | ||
| 2721 | Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; | 2662 | Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; |
| 2722 | bool is_array{instr.texs.IsArrayTexture()}; | 2663 | bool is_array{instr.texs.IsArrayTexture()}; |
| 2723 | 2664 | ||
| 2724 | ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2665 | UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2725 | "NODEP is not implemented"); | 2666 | "NODEP is not implemented"); |
| 2726 | 2667 | ||
| 2727 | const bool depth_compare = | 2668 | const bool depth_compare = |
| 2728 | instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 2669 | instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); |
| @@ -2730,17 +2671,21 @@ private: | |||
| 2730 | if (depth_compare) | 2671 | if (depth_compare) |
| 2731 | num_coordinates += 1; | 2672 | num_coordinates += 1; |
| 2732 | 2673 | ||
| 2674 | // Scope to avoid variable name overlaps. | ||
| 2675 | shader.AddLine('{'); | ||
| 2676 | ++shader.scope; | ||
| 2677 | |||
| 2733 | switch (num_coordinates) { | 2678 | switch (num_coordinates) { |
| 2734 | case 2: { | 2679 | case 2: { |
| 2735 | if (is_array) { | 2680 | if (is_array) { |
| 2736 | const std::string index = regs.GetRegisterAsInteger(instr.gpr8); | 2681 | const std::string index = regs.GetRegisterAsInteger(instr.gpr8); |
| 2737 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2682 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2738 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | 2683 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); |
| 2739 | coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; | 2684 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"); |
| 2740 | } else { | 2685 | } else { |
| 2741 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2686 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2742 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | 2687 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); |
| 2743 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2688 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); |
| 2744 | } | 2689 | } |
| 2745 | break; | 2690 | break; |
| 2746 | } | 2691 | } |
| @@ -2750,25 +2695,24 @@ private: | |||
| 2750 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2695 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2751 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); | 2696 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); |
| 2752 | const std::string z = regs.GetRegisterAsFloat(instr.gpr20); | 2697 | const std::string z = regs.GetRegisterAsFloat(instr.gpr20); |
| 2753 | coord = | 2698 | shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + |
| 2754 | "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");"; | 2699 | index + ");"); |
| 2755 | } else { | 2700 | } else { |
| 2756 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2701 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2757 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2702 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2758 | const std::string z = regs.GetRegisterAsFloat(instr.gpr20); | 2703 | const std::string z = regs.GetRegisterAsFloat(instr.gpr20); |
| 2759 | coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; | 2704 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); |
| 2760 | } | 2705 | } |
| 2761 | break; | 2706 | break; |
| 2762 | } | 2707 | } |
| 2763 | default: | 2708 | default: |
| 2764 | LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", | 2709 | UNIMPLEMENTED_MSG("Unhandled coordinates number {}", |
| 2765 | static_cast<u32>(num_coordinates)); | 2710 | static_cast<u32>(num_coordinates)); |
| 2766 | UNREACHABLE(); | ||
| 2767 | 2711 | ||
| 2768 | // Fallback to interpreting as a 2D texture for now | 2712 | // Fallback to interpreting as a 2D texture for now |
| 2769 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2713 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2770 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | 2714 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); |
| 2771 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2715 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); |
| 2772 | texture_type = Tegra::Shader::TextureType::Texture2D; | 2716 | texture_type = Tegra::Shader::TextureType::Texture2D; |
| 2773 | is_array = false; | 2717 | is_array = false; |
| 2774 | } | 2718 | } |
| @@ -2795,57 +2739,57 @@ private: | |||
| 2795 | } | 2739 | } |
| 2796 | default: { | 2740 | default: { |
| 2797 | texture = "texture(" + sampler + ", coords)"; | 2741 | texture = "texture(" + sampler + ", coords)"; |
| 2798 | LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", | 2742 | UNIMPLEMENTED_MSG("Unhandled texture process mode {}", |
| 2799 | static_cast<u32>(instr.texs.GetTextureProcessMode())); | 2743 | static_cast<u32>(instr.texs.GetTextureProcessMode())); |
| 2800 | UNREACHABLE(); | ||
| 2801 | } | 2744 | } |
| 2802 | } | 2745 | } |
| 2803 | if (!depth_compare) { | 2746 | if (!depth_compare) { |
| 2804 | WriteTexsInstruction(instr, coord, texture); | 2747 | WriteTexsInstruction(instr, texture); |
| 2805 | } else { | 2748 | } else { |
| 2806 | WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); | 2749 | WriteTexsInstruction(instr, "vec4(" + texture + ')'); |
| 2807 | } | 2750 | } |
| 2751 | |||
| 2752 | shader.AddLine('}'); | ||
| 2753 | --shader.scope; | ||
| 2808 | break; | 2754 | break; |
| 2809 | } | 2755 | } |
| 2810 | case OpCode::Id::TLDS: { | 2756 | case OpCode::Id::TLDS: { |
| 2811 | std::string coord; | ||
| 2812 | const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; | 2757 | const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; |
| 2813 | const bool is_array{instr.tlds.IsArrayTexture()}; | 2758 | const bool is_array{instr.tlds.IsArrayTexture()}; |
| 2814 | 2759 | ||
| 2815 | ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); | 2760 | ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); |
| 2816 | ASSERT(is_array == false); | 2761 | ASSERT(is_array == false); |
| 2817 | 2762 | ||
| 2818 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2763 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2819 | "NODEP is not implemented"); | 2764 | "NODEP is not implemented"); |
| 2820 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | 2765 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), |
| 2821 | "AOFFI is not implemented"); | 2766 | "AOFFI is not implemented"); |
| 2822 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), | 2767 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), |
| 2823 | "MZ is not implemented"); | 2768 | "MZ is not implemented"); |
| 2824 | 2769 | ||
| 2825 | u32 op_c_offset = 0; | 2770 | u32 extra_op_offset = 0; |
| 2771 | |||
| 2772 | // Scope to avoid variable name overlaps. | ||
| 2773 | shader.AddLine('{'); | ||
| 2774 | ++shader.scope; | ||
| 2826 | 2775 | ||
| 2827 | switch (texture_type) { | 2776 | switch (texture_type) { |
| 2828 | case Tegra::Shader::TextureType::Texture1D: { | 2777 | case Tegra::Shader::TextureType::Texture1D: { |
| 2829 | const std::string x = regs.GetRegisterAsInteger(instr.gpr8); | 2778 | const std::string x = regs.GetRegisterAsInteger(instr.gpr8); |
| 2830 | coord = "int coords = " + x + ';'; | 2779 | shader.AddLine("int coords = " + x + ';'); |
| 2831 | break; | 2780 | break; |
| 2832 | } | 2781 | } |
| 2833 | case Tegra::Shader::TextureType::Texture2D: { | 2782 | case Tegra::Shader::TextureType::Texture2D: { |
| 2834 | if (is_array) { | 2783 | UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture"); |
| 2835 | LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); | 2784 | |
| 2836 | UNREACHABLE(); | 2785 | const std::string x = regs.GetRegisterAsInteger(instr.gpr8); |
| 2837 | } else { | 2786 | const std::string y = regs.GetRegisterAsInteger(instr.gpr20); |
| 2838 | const std::string x = regs.GetRegisterAsInteger(instr.gpr8); | 2787 | shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); |
| 2839 | const std::string y = regs.GetRegisterAsInteger(instr.gpr20); | 2788 | extra_op_offset = 1; |
| 2840 | coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; | ||
| 2841 | op_c_offset = 1; | ||
| 2842 | } | ||
| 2843 | break; | 2789 | break; |
| 2844 | } | 2790 | } |
| 2845 | default: | 2791 | default: |
| 2846 | LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | 2792 | UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type)); |
| 2847 | static_cast<u32>(texture_type)); | ||
| 2848 | UNREACHABLE(); | ||
| 2849 | } | 2793 | } |
| 2850 | const std::string sampler = | 2794 | const std::string sampler = |
| 2851 | GetSampler(instr.sampler, texture_type, is_array, false); | 2795 | GetSampler(instr.sampler, texture_type, is_array, false); |
| @@ -2856,19 +2800,22 @@ private: | |||
| 2856 | break; | 2800 | break; |
| 2857 | } | 2801 | } |
| 2858 | case Tegra::Shader::TextureProcessMode::LL: { | 2802 | case Tegra::Shader::TextureProcessMode::LL: { |
| 2859 | const std::string op_c = | 2803 | shader.AddLine( |
| 2860 | regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset); | 2804 | "float lod = " + |
| 2861 | texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; | 2805 | regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';'); |
| 2806 | texture = "texelFetch(" + sampler + ", coords, lod)"; | ||
| 2862 | break; | 2807 | break; |
| 2863 | } | 2808 | } |
| 2864 | default: { | 2809 | default: { |
| 2865 | texture = "texelFetch(" + sampler + ", coords, 0)"; | 2810 | texture = "texelFetch(" + sampler + ", coords, 0)"; |
| 2866 | LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", | 2811 | UNIMPLEMENTED_MSG("Unhandled texture process mode {}", |
| 2867 | static_cast<u32>(instr.tlds.GetTextureProcessMode())); | 2812 | static_cast<u32>(instr.tlds.GetTextureProcessMode())); |
| 2868 | UNREACHABLE(); | ||
| 2869 | } | 2813 | } |
| 2870 | } | 2814 | } |
| 2871 | WriteTexsInstruction(instr, coord, texture); | 2815 | WriteTexsInstruction(instr, texture); |
| 2816 | |||
| 2817 | --shader.scope; | ||
| 2818 | shader.AddLine('}'); | ||
| 2872 | break; | 2819 | break; |
| 2873 | } | 2820 | } |
| 2874 | case OpCode::Id::TLD4: { | 2821 | case OpCode::Id::TLD4: { |
| @@ -2876,14 +2823,14 @@ private: | |||
| 2876 | ASSERT(instr.tld4.array == 0); | 2823 | ASSERT(instr.tld4.array == 0); |
| 2877 | std::string coord; | 2824 | std::string coord; |
| 2878 | 2825 | ||
| 2879 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2826 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2880 | "NODEP is not implemented"); | 2827 | "NODEP is not implemented"); |
| 2881 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | 2828 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), |
| 2882 | "AOFFI is not implemented"); | 2829 | "AOFFI is not implemented"); |
| 2883 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | 2830 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), |
| 2884 | "NDV is not implemented"); | 2831 | "NDV is not implemented"); |
| 2885 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), | 2832 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), |
| 2886 | "PTP is not implemented"); | 2833 | "PTP is not implemented"); |
| 2887 | const bool depth_compare = | 2834 | const bool depth_compare = |
| 2888 | instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 2835 | instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); |
| 2889 | auto texture_type = instr.tld4.texture_type.Value(); | 2836 | auto texture_type = instr.tld4.texture_type.Value(); |
| @@ -2891,37 +2838,37 @@ private: | |||
| 2891 | if (depth_compare) | 2838 | if (depth_compare) |
| 2892 | num_coordinates += 1; | 2839 | num_coordinates += 1; |
| 2893 | 2840 | ||
| 2841 | // Add an extra scope and declare the texture coords inside to prevent | ||
| 2842 | // overwriting them in case they are used as outputs of the texs instruction. | ||
| 2843 | shader.AddLine('{'); | ||
| 2844 | ++shader.scope; | ||
| 2845 | |||
| 2894 | switch (num_coordinates) { | 2846 | switch (num_coordinates) { |
| 2895 | case 2: { | 2847 | case 2: { |
| 2896 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2848 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2897 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2849 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2898 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2850 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); |
| 2899 | break; | 2851 | break; |
| 2900 | } | 2852 | } |
| 2901 | case 3: { | 2853 | case 3: { |
| 2902 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2854 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2903 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2855 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2904 | const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); | 2856 | const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); |
| 2905 | coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; | 2857 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); |
| 2906 | break; | 2858 | break; |
| 2907 | } | 2859 | } |
| 2908 | default: | 2860 | default: |
| 2909 | LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", | 2861 | UNIMPLEMENTED_MSG("Unhandled coordinates number {}", |
| 2910 | static_cast<u32>(num_coordinates)); | 2862 | static_cast<u32>(num_coordinates)); |
| 2911 | UNREACHABLE(); | ||
| 2912 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2863 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2913 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2864 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2914 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2865 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); |
| 2915 | texture_type = Tegra::Shader::TextureType::Texture2D; | 2866 | texture_type = Tegra::Shader::TextureType::Texture2D; |
| 2916 | } | 2867 | } |
| 2917 | 2868 | ||
| 2918 | const std::string sampler = | 2869 | const std::string sampler = |
| 2919 | GetSampler(instr.sampler, texture_type, false, depth_compare); | 2870 | GetSampler(instr.sampler, texture_type, false, depth_compare); |
| 2920 | // Add an extra scope and declare the texture coords inside to prevent | 2871 | |
| 2921 | // overwriting them in case they are used as outputs of the texs instruction. | ||
| 2922 | shader.AddLine("{"); | ||
| 2923 | ++shader.scope; | ||
| 2924 | shader.AddLine(coord); | ||
| 2925 | const std::string texture = "textureGather(" + sampler + ", coords, " + | 2872 | const std::string texture = "textureGather(" + sampler + ", coords, " + |
| 2926 | std::to_string(instr.tld4.component) + ')'; | 2873 | std::to_string(instr.tld4.component) + ')'; |
| 2927 | if (!depth_compare) { | 2874 | if (!depth_compare) { |
| @@ -2938,14 +2885,20 @@ private: | |||
| 2938 | regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); | 2885 | regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); |
| 2939 | } | 2886 | } |
| 2940 | --shader.scope; | 2887 | --shader.scope; |
| 2941 | shader.AddLine("}"); | 2888 | shader.AddLine('}'); |
| 2942 | break; | 2889 | break; |
| 2943 | } | 2890 | } |
| 2944 | case OpCode::Id::TLD4S: { | 2891 | case OpCode::Id::TLD4S: { |
| 2945 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2892 | UNIMPLEMENTED_IF_MSG( |
| 2946 | "NODEP is not implemented"); | 2893 | instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2947 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | 2894 | "NODEP is not implemented"); |
| 2948 | "AOFFI is not implemented"); | 2895 | UNIMPLEMENTED_IF_MSG( |
| 2896 | instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 2897 | "AOFFI is not implemented"); | ||
| 2898 | |||
| 2899 | // Scope to avoid variable name overlaps. | ||
| 2900 | shader.AddLine('{'); | ||
| 2901 | ++shader.scope; | ||
| 2949 | 2902 | ||
| 2950 | const bool depth_compare = | 2903 | const bool depth_compare = |
| 2951 | instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 2904 | instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); |
| @@ -2954,28 +2907,33 @@ private: | |||
| 2954 | // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. | 2907 | // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. |
| 2955 | const std::string sampler = GetSampler( | 2908 | const std::string sampler = GetSampler( |
| 2956 | instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); | 2909 | instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); |
| 2957 | std::string coord; | ||
| 2958 | if (!depth_compare) { | 2910 | if (!depth_compare) { |
| 2959 | coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; | 2911 | shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");"); |
| 2960 | } else { | 2912 | } else { |
| 2961 | // Note: TLD4S coordinate encoding works just like TEXS's | 2913 | // Note: TLD4S coordinate encoding works just like TEXS's |
| 2962 | const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2914 | shader.AddLine( |
| 2963 | coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; | 2915 | "float op_y = " + regs.GetRegisterAsFloat(instr.gpr8.Value() + 1) + ';'); |
| 2916 | shader.AddLine("vec3 coords = vec3(" + op_a + ", op_y, " + op_b + ");"); | ||
| 2964 | } | 2917 | } |
| 2965 | const std::string texture = "textureGather(" + sampler + ", coords, " + | 2918 | const std::string texture = "textureGather(" + sampler + ", coords, " + |
| 2966 | std::to_string(instr.tld4s.component) + ')'; | 2919 | std::to_string(instr.tld4s.component) + ')'; |
| 2967 | 2920 | ||
| 2968 | if (!depth_compare) { | 2921 | if (!depth_compare) { |
| 2969 | WriteTexsInstruction(instr, coord, texture); | 2922 | WriteTexsInstruction(instr, texture); |
| 2970 | } else { | 2923 | } else { |
| 2971 | WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); | 2924 | WriteTexsInstruction(instr, "vec4(" + texture + ')'); |
| 2972 | } | 2925 | } |
| 2926 | |||
| 2927 | --shader.scope; | ||
| 2928 | shader.AddLine('}'); | ||
| 2973 | break; | 2929 | break; |
| 2974 | } | 2930 | } |
| 2975 | case OpCode::Id::TXQ: { | 2931 | case OpCode::Id::TXQ: { |
| 2976 | ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2932 | UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2977 | "NODEP is not implemented"); | 2933 | "NODEP is not implemented"); |
| 2978 | 2934 | ||
| 2935 | ++shader.scope; | ||
| 2936 | shader.AddLine('{'); | ||
| 2979 | // TODO: the new commits on the texture refactor, change the way samplers work. | 2937 | // TODO: the new commits on the texture refactor, change the way samplers work. |
| 2980 | // Sadly, not all texture instructions specify the type of texture their sampler | 2938 | // Sadly, not all texture instructions specify the type of texture their sampler |
| 2981 | // uses. This must be fixed at a later instance. | 2939 | // uses. This must be fixed at a later instance. |
| @@ -2983,23 +2941,30 @@ private: | |||
| 2983 | GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); | 2941 | GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); |
| 2984 | switch (instr.txq.query_type) { | 2942 | switch (instr.txq.query_type) { |
| 2985 | case Tegra::Shader::TextureQueryType::Dimension: { | 2943 | case Tegra::Shader::TextureQueryType::Dimension: { |
| 2986 | const std::string texture = "textureQueryLevels(" + sampler + ')'; | 2944 | const std::string texture = "textureSize(" + sampler + ", " + |
| 2987 | regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1); | 2945 | regs.GetRegisterAsInteger(instr.gpr8) + ')'; |
| 2946 | const std::string mip_level = "textureQueryLevels(" + sampler + ')'; | ||
| 2947 | shader.AddLine("ivec2 sizes = " + texture + ';'); | ||
| 2948 | regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1); | ||
| 2949 | regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1); | ||
| 2950 | regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1); | ||
| 2951 | regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1); | ||
| 2988 | break; | 2952 | break; |
| 2989 | } | 2953 | } |
| 2990 | default: { | 2954 | default: { |
| 2991 | LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}", | 2955 | UNIMPLEMENTED_MSG("Unhandled texture query type: {}", |
| 2992 | static_cast<u32>(instr.txq.query_type.Value())); | 2956 | static_cast<u32>(instr.txq.query_type.Value())); |
| 2993 | UNREACHABLE(); | ||
| 2994 | } | 2957 | } |
| 2995 | } | 2958 | } |
| 2959 | --shader.scope; | ||
| 2960 | shader.AddLine('}'); | ||
| 2996 | break; | 2961 | break; |
| 2997 | } | 2962 | } |
| 2998 | case OpCode::Id::TMML: { | 2963 | case OpCode::Id::TMML: { |
| 2999 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2964 | UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 3000 | "NODEP is not implemented"); | 2965 | "NODEP is not implemented"); |
| 3001 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | 2966 | UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), |
| 3002 | "NDV is not implemented"); | 2967 | "NDV is not implemented"); |
| 3003 | 2968 | ||
| 3004 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2969 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 3005 | const bool is_array = instr.tmml.array != 0; | 2970 | const bool is_array = instr.tmml.array != 0; |
| @@ -3021,9 +2986,7 @@ private: | |||
| 3021 | break; | 2986 | break; |
| 3022 | } | 2987 | } |
| 3023 | default: | 2988 | default: |
| 3024 | LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | 2989 | UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type)); |
| 3025 | static_cast<u32>(texture_type)); | ||
| 3026 | UNREACHABLE(); | ||
| 3027 | 2990 | ||
| 3028 | // Fallback to interpreting as a 2D texture for now | 2991 | // Fallback to interpreting as a 2D texture for now |
| 3029 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2992 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| @@ -3046,8 +3009,7 @@ private: | |||
| 3046 | break; | 3009 | break; |
| 3047 | } | 3010 | } |
| 3048 | default: { | 3011 | default: { |
| 3049 | LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName()); | 3012 | UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName()); |
| 3050 | UNREACHABLE(); | ||
| 3051 | } | 3013 | } |
| 3052 | } | 3014 | } |
| 3053 | break; | 3015 | break; |
| @@ -3133,7 +3095,7 @@ private: | |||
| 3133 | break; | 3095 | break; |
| 3134 | } | 3096 | } |
| 3135 | case OpCode::Type::HalfSetPredicate: { | 3097 | case OpCode::Type::HalfSetPredicate: { |
| 3136 | ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented"); | 3098 | UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0); |
| 3137 | 3099 | ||
| 3138 | const std::string op_a = | 3100 | const std::string op_a = |
| 3139 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a, | 3101 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a, |
| @@ -3178,6 +3140,9 @@ private: | |||
| 3178 | break; | 3140 | break; |
| 3179 | } | 3141 | } |
| 3180 | case OpCode::Type::PredicateSetRegister: { | 3142 | case OpCode::Type::PredicateSetRegister: { |
| 3143 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 3144 | "Condition codes generation in PSET is not implemented"); | ||
| 3145 | |||
| 3181 | const std::string op_a = | 3146 | const std::string op_a = |
| 3182 | GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); | 3147 | GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); |
| 3183 | const std::string op_b = | 3148 | const std::string op_b = |
| @@ -3198,12 +3163,6 @@ private: | |||
| 3198 | const std::string value = '(' + result + ") ? 1.0 : 0.0"; | 3163 | const std::string value = '(' + result + ") ? 1.0 : 0.0"; |
| 3199 | regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); | 3164 | regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); |
| 3200 | } | 3165 | } |
| 3201 | |||
| 3202 | if (instr.generates_cc) { | ||
| 3203 | LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code"); | ||
| 3204 | UNREACHABLE(); | ||
| 3205 | } | ||
| 3206 | |||
| 3207 | break; | 3166 | break; |
| 3208 | } | 3167 | } |
| 3209 | case OpCode::Type::PredicateSetPredicate: { | 3168 | case OpCode::Type::PredicateSetPredicate: { |
| @@ -3241,21 +3200,19 @@ private: | |||
| 3241 | const std::string pred = | 3200 | const std::string pred = |
| 3242 | GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); | 3201 | GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); |
| 3243 | const std::string combiner = GetPredicateCombiner(instr.csetp.op); | 3202 | const std::string combiner = GetPredicateCombiner(instr.csetp.op); |
| 3244 | const std::string control_code = regs.GetControlCode(instr.csetp.cc); | 3203 | const std::string condition_code = regs.GetConditionCode(instr.csetp.cc); |
| 3245 | if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { | 3204 | if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { |
| 3246 | SetPredicate(instr.csetp.pred3, | 3205 | SetPredicate(instr.csetp.pred3, |
| 3247 | '(' + control_code + ") " + combiner + " (" + pred + ')'); | 3206 | '(' + condition_code + ") " + combiner + " (" + pred + ')'); |
| 3248 | } | 3207 | } |
| 3249 | if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { | 3208 | if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { |
| 3250 | SetPredicate(instr.csetp.pred0, | 3209 | SetPredicate(instr.csetp.pred0, |
| 3251 | "!(" + control_code + ") " + combiner + " (" + pred + ')'); | 3210 | "!(" + condition_code + ") " + combiner + " (" + pred + ')'); |
| 3252 | } | 3211 | } |
| 3253 | break; | 3212 | break; |
| 3254 | } | 3213 | } |
| 3255 | default: { | 3214 | default: { |
| 3256 | LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", | 3215 | UNIMPLEMENTED_MSG("Unhandled predicate instruction: {}", opcode->get().GetName()); |
| 3257 | opcode->get().GetName()); | ||
| 3258 | UNREACHABLE(); | ||
| 3259 | } | 3216 | } |
| 3260 | } | 3217 | } |
| 3261 | break; | 3218 | break; |
| @@ -3335,7 +3292,7 @@ private: | |||
| 3335 | break; | 3292 | break; |
| 3336 | } | 3293 | } |
| 3337 | case OpCode::Type::HalfSet: { | 3294 | case OpCode::Type::HalfSet: { |
| 3338 | ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented"); | 3295 | UNIMPLEMENTED_IF(instr.hset2.ftz != 0); |
| 3339 | 3296 | ||
| 3340 | const std::string op_a = | 3297 | const std::string op_a = |
| 3341 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a, | 3298 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a, |
| @@ -3379,15 +3336,17 @@ private: | |||
| 3379 | break; | 3336 | break; |
| 3380 | } | 3337 | } |
| 3381 | case OpCode::Type::Xmad: { | 3338 | case OpCode::Type::Xmad: { |
| 3382 | ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); | 3339 | UNIMPLEMENTED_IF(instr.xmad.sign_a); |
| 3383 | ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); | 3340 | UNIMPLEMENTED_IF(instr.xmad.sign_b); |
| 3341 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 3342 | "Condition codes generation in XMAD is not implemented"); | ||
| 3384 | 3343 | ||
| 3385 | std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; | 3344 | std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; |
| 3386 | std::string op_b; | 3345 | std::string op_b; |
| 3387 | std::string op_c; | 3346 | std::string op_c; |
| 3388 | 3347 | ||
| 3389 | // TODO(bunnei): Needs to be fixed once op_a or op_b is signed | 3348 | // TODO(bunnei): Needs to be fixed once op_a or op_b is signed |
| 3390 | ASSERT_MSG(instr.xmad.sign_a == instr.xmad.sign_b, "Unimplemented"); | 3349 | UNIMPLEMENTED_IF(instr.xmad.sign_a != instr.xmad.sign_b); |
| 3391 | const bool is_signed{instr.xmad.sign_a == 1}; | 3350 | const bool is_signed{instr.xmad.sign_a == 1}; |
| 3392 | 3351 | ||
| 3393 | bool is_merge{}; | 3352 | bool is_merge{}; |
| @@ -3420,8 +3379,7 @@ private: | |||
| 3420 | break; | 3379 | break; |
| 3421 | } | 3380 | } |
| 3422 | default: { | 3381 | default: { |
| 3423 | LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName()); | 3382 | UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); |
| 3424 | UNREACHABLE(); | ||
| 3425 | } | 3383 | } |
| 3426 | } | 3384 | } |
| 3427 | 3385 | ||
| @@ -3457,9 +3415,8 @@ private: | |||
| 3457 | op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; | 3415 | op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; |
| 3458 | break; | 3416 | break; |
| 3459 | default: { | 3417 | default: { |
| 3460 | LOG_CRITICAL(HW_GPU, "Unhandled XMAD mode: {}", | 3418 | UNIMPLEMENTED_MSG("Unhandled XMAD mode: {}", |
| 3461 | static_cast<u32>(instr.xmad.mode.Value())); | 3419 | static_cast<u32>(instr.xmad.mode.Value())); |
| 3462 | UNREACHABLE(); | ||
| 3463 | } | 3420 | } |
| 3464 | } | 3421 | } |
| 3465 | 3422 | ||
| @@ -3469,25 +3426,19 @@ private: | |||
| 3469 | } | 3426 | } |
| 3470 | 3427 | ||
| 3471 | regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); | 3428 | regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); |
| 3472 | if (instr.generates_cc) { | ||
| 3473 | LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code"); | ||
| 3474 | UNREACHABLE(); | ||
| 3475 | } | ||
| 3476 | break; | 3429 | break; |
| 3477 | } | 3430 | } |
| 3478 | default: { | 3431 | default: { |
| 3479 | switch (opcode->get().GetId()) { | 3432 | switch (opcode->get().GetId()) { |
| 3480 | case OpCode::Id::EXIT: { | 3433 | case OpCode::Id::EXIT: { |
| 3434 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; | ||
| 3435 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, | ||
| 3436 | "EXIT condition code used: {}", static_cast<u32>(cc)); | ||
| 3437 | |||
| 3481 | if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { | 3438 | if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { |
| 3482 | EmitFragmentOutputsWrite(); | 3439 | EmitFragmentOutputsWrite(); |
| 3483 | } | 3440 | } |
| 3484 | 3441 | ||
| 3485 | const Tegra::Shader::ControlCode cc = instr.flow_control_code; | ||
| 3486 | if (cc != Tegra::Shader::ControlCode::T) { | ||
| 3487 | LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc)); | ||
| 3488 | UNREACHABLE(); | ||
| 3489 | } | ||
| 3490 | |||
| 3491 | switch (instr.flow.cond) { | 3442 | switch (instr.flow.cond) { |
| 3492 | case Tegra::Shader::FlowCondition::Always: | 3443 | case Tegra::Shader::FlowCondition::Always: |
| 3493 | shader.AddLine("return true;"); | 3444 | shader.AddLine("return true;"); |
| @@ -3502,26 +3453,24 @@ private: | |||
| 3502 | case Tegra::Shader::FlowCondition::Fcsm_Tr: | 3453 | case Tegra::Shader::FlowCondition::Fcsm_Tr: |
| 3503 | // TODO(bunnei): What is this used for? If we assume this conditon is not | 3454 | // TODO(bunnei): What is this used for? If we assume this conditon is not |
| 3504 | // satisifed, dual vertex shaders in Farming Simulator make more sense | 3455 | // satisifed, dual vertex shaders in Farming Simulator make more sense |
| 3505 | LOG_CRITICAL(HW_GPU, "Skipping unknown FlowCondition::Fcsm_Tr"); | 3456 | UNIMPLEMENTED_MSG("Skipping unknown FlowCondition::Fcsm_Tr"); |
| 3506 | break; | 3457 | break; |
| 3507 | 3458 | ||
| 3508 | default: | 3459 | default: |
| 3509 | LOG_CRITICAL(HW_GPU, "Unhandled flow condition: {}", | 3460 | UNIMPLEMENTED_MSG("Unhandled flow condition: {}", |
| 3510 | static_cast<u32>(instr.flow.cond.Value())); | 3461 | static_cast<u32>(instr.flow.cond.Value())); |
| 3511 | UNREACHABLE(); | ||
| 3512 | } | 3462 | } |
| 3513 | break; | 3463 | break; |
| 3514 | } | 3464 | } |
| 3515 | case OpCode::Id::KIL: { | 3465 | case OpCode::Id::KIL: { |
| 3516 | ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); | 3466 | UNIMPLEMENTED_IF(instr.flow.cond != Tegra::Shader::FlowCondition::Always); |
| 3467 | |||
| 3468 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; | ||
| 3469 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, | ||
| 3470 | "KIL condition code used: {}", static_cast<u32>(cc)); | ||
| 3517 | 3471 | ||
| 3518 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain | 3472 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain |
| 3519 | // about unexecuted instructions that may follow this. | 3473 | // about unexecuted instructions that may follow this. |
| 3520 | const Tegra::Shader::ControlCode cc = instr.flow_control_code; | ||
| 3521 | if (cc != Tegra::Shader::ControlCode::T) { | ||
| 3522 | LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc)); | ||
| 3523 | UNREACHABLE(); | ||
| 3524 | } | ||
| 3525 | shader.AddLine("if (true) {"); | 3474 | shader.AddLine("if (true) {"); |
| 3526 | ++shader.scope; | 3475 | ++shader.scope; |
| 3527 | shader.AddLine("discard;"); | 3476 | shader.AddLine("discard;"); |
| @@ -3531,7 +3480,8 @@ private: | |||
| 3531 | break; | 3480 | break; |
| 3532 | } | 3481 | } |
| 3533 | case OpCode::Id::OUT_R: { | 3482 | case OpCode::Id::OUT_R: { |
| 3534 | ASSERT(instr.gpr20.Value() == Register::ZeroIndex); | 3483 | UNIMPLEMENTED_IF_MSG(instr.gpr20.Value() != Register::ZeroIndex, |
| 3484 | "Stream buffer is not supported"); | ||
| 3535 | ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, | 3485 | ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, |
| 3536 | "OUT is expected to be used in a geometry shader."); | 3486 | "OUT is expected to be used in a geometry shader."); |
| 3537 | 3487 | ||
| @@ -3558,18 +3508,17 @@ private: | |||
| 3558 | break; | 3508 | break; |
| 3559 | } | 3509 | } |
| 3560 | default: { | 3510 | default: { |
| 3561 | LOG_CRITICAL(HW_GPU, "Unhandled system move: {}", | 3511 | UNIMPLEMENTED_MSG("Unhandled system move: {}", |
| 3562 | static_cast<u32>(instr.sys20.Value())); | 3512 | static_cast<u32>(instr.sys20.Value())); |
| 3563 | UNREACHABLE(); | ||
| 3564 | } | 3513 | } |
| 3565 | } | 3514 | } |
| 3566 | break; | 3515 | break; |
| 3567 | } | 3516 | } |
| 3568 | case OpCode::Id::ISBERD: { | 3517 | case OpCode::Id::ISBERD: { |
| 3569 | ASSERT(instr.isberd.o == 0); | 3518 | UNIMPLEMENTED_IF(instr.isberd.o != 0); |
| 3570 | ASSERT(instr.isberd.skew == 0); | 3519 | UNIMPLEMENTED_IF(instr.isberd.skew != 0); |
| 3571 | ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None); | 3520 | UNIMPLEMENTED_IF(instr.isberd.shift != Tegra::Shader::IsberdShift::None); |
| 3572 | ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None); | 3521 | UNIMPLEMENTED_IF(instr.isberd.mode != Tegra::Shader::IsberdMode::None); |
| 3573 | ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, | 3522 | ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, |
| 3574 | "ISBERD is expected to be used in a geometry shader."); | 3523 | "ISBERD is expected to be used in a geometry shader."); |
| 3575 | LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); | 3524 | LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); |
| @@ -3577,13 +3526,13 @@ private: | |||
| 3577 | break; | 3526 | break; |
| 3578 | } | 3527 | } |
| 3579 | case OpCode::Id::BRA: { | 3528 | case OpCode::Id::BRA: { |
| 3580 | ASSERT_MSG(instr.bra.constant_buffer == 0, | 3529 | UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, |
| 3581 | "BRA with constant buffers are not implemented"); | 3530 | "BRA with constant buffers are not implemented"); |
| 3582 | const Tegra::Shader::ControlCode cc = instr.flow_control_code; | 3531 | |
| 3583 | if (cc != Tegra::Shader::ControlCode::T) { | 3532 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; |
| 3584 | LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc)); | 3533 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, |
| 3585 | UNREACHABLE(); | 3534 | "BRA condition code used: {}", static_cast<u32>(cc)); |
| 3586 | } | 3535 | |
| 3587 | const u32 target = offset + instr.bra.GetBranchTarget(); | 3536 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 3588 | shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); | 3537 | shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); |
| 3589 | break; | 3538 | break; |
| @@ -3606,7 +3555,8 @@ private: | |||
| 3606 | // The SSY opcode tells the GPU where to re-converge divergent execution paths, it | 3555 | // The SSY opcode tells the GPU where to re-converge divergent execution paths, it |
| 3607 | // sets the target of the jump that the SYNC instruction will make. The SSY opcode | 3556 | // sets the target of the jump that the SYNC instruction will make. The SSY opcode |
| 3608 | // has a similar structure to the BRA opcode. | 3557 | // has a similar structure to the BRA opcode. |
| 3609 | ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported"); | 3558 | UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, |
| 3559 | "Constant buffer flow is not supported"); | ||
| 3610 | 3560 | ||
| 3611 | const u32 target = offset + instr.bra.GetBranchTarget(); | 3561 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 3612 | EmitPushToFlowStack(target); | 3562 | EmitPushToFlowStack(target); |
| @@ -3616,29 +3566,28 @@ private: | |||
| 3616 | // PBK pushes to a stack the address where BRK will jump to. This shares stack with | 3566 | // PBK pushes to a stack the address where BRK will jump to. This shares stack with |
| 3617 | // SSY but using SYNC on a PBK address will kill the shader execution. We don't | 3567 | // SSY but using SYNC on a PBK address will kill the shader execution. We don't |
| 3618 | // emulate this because it's very unlikely a driver will emit such invalid shader. | 3568 | // emulate this because it's very unlikely a driver will emit such invalid shader. |
| 3619 | ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported"); | 3569 | UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, |
| 3570 | "Constant buffer PBK is not supported"); | ||
| 3620 | 3571 | ||
| 3621 | const u32 target = offset + instr.bra.GetBranchTarget(); | 3572 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 3622 | EmitPushToFlowStack(target); | 3573 | EmitPushToFlowStack(target); |
| 3623 | break; | 3574 | break; |
| 3624 | } | 3575 | } |
| 3625 | case OpCode::Id::SYNC: { | 3576 | case OpCode::Id::SYNC: { |
| 3577 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; | ||
| 3578 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, | ||
| 3579 | "SYNC condition code used: {}", static_cast<u32>(cc)); | ||
| 3580 | |||
| 3626 | // The SYNC opcode jumps to the address previously set by the SSY opcode | 3581 | // The SYNC opcode jumps to the address previously set by the SSY opcode |
| 3627 | const Tegra::Shader::ControlCode cc = instr.flow_control_code; | ||
| 3628 | if (cc != Tegra::Shader::ControlCode::T) { | ||
| 3629 | LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc)); | ||
| 3630 | UNREACHABLE(); | ||
| 3631 | } | ||
| 3632 | EmitPopFromFlowStack(); | 3582 | EmitPopFromFlowStack(); |
| 3633 | break; | 3583 | break; |
| 3634 | } | 3584 | } |
| 3635 | case OpCode::Id::BRK: { | 3585 | case OpCode::Id::BRK: { |
| 3636 | // The BRK opcode jumps to the address previously set by the PBK opcode | 3586 | // The BRK opcode jumps to the address previously set by the PBK opcode |
| 3637 | const Tegra::Shader::ControlCode cc = instr.flow_control_code; | 3587 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; |
| 3638 | if (cc != Tegra::Shader::ControlCode::T) { | 3588 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, |
| 3639 | LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc)); | 3589 | "BRK condition code used: {}", static_cast<u32>(cc)); |
| 3640 | UNREACHABLE(); | 3590 | |
| 3641 | } | ||
| 3642 | EmitPopFromFlowStack(); | 3591 | EmitPopFromFlowStack(); |
| 3643 | break; | 3592 | break; |
| 3644 | } | 3593 | } |
| @@ -3649,6 +3598,9 @@ private: | |||
| 3649 | break; | 3598 | break; |
| 3650 | } | 3599 | } |
| 3651 | case OpCode::Id::VMAD: { | 3600 | case OpCode::Id::VMAD: { |
| 3601 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | ||
| 3602 | "Condition codes generation in VMAD is not implemented"); | ||
| 3603 | |||
| 3652 | const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; | 3604 | const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; |
| 3653 | const std::string op_a = GetVideoOperandA(instr); | 3605 | const std::string op_a = GetVideoOperandA(instr); |
| 3654 | const std::string op_b = GetVideoOperandB(instr); | 3606 | const std::string op_b = GetVideoOperandB(instr); |
| @@ -3668,11 +3620,6 @@ private: | |||
| 3668 | regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, | 3620 | regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, |
| 3669 | instr.vmad.saturate == 1, 0, Register::Size::Word, | 3621 | instr.vmad.saturate == 1, 0, Register::Size::Word, |
| 3670 | instr.vmad.cc); | 3622 | instr.vmad.cc); |
| 3671 | if (instr.generates_cc) { | ||
| 3672 | LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code"); | ||
| 3673 | UNREACHABLE(); | ||
| 3674 | } | ||
| 3675 | |||
| 3676 | break; | 3623 | break; |
| 3677 | } | 3624 | } |
| 3678 | case OpCode::Id::VSETP: { | 3625 | case OpCode::Id::VSETP: { |
| @@ -3699,10 +3646,7 @@ private: | |||
| 3699 | } | 3646 | } |
| 3700 | break; | 3647 | break; |
| 3701 | } | 3648 | } |
| 3702 | default: { | 3649 | default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); } |
| 3703 | LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName()); | ||
| 3704 | UNREACHABLE(); | ||
| 3705 | } | ||
| 3706 | } | 3650 | } |
| 3707 | 3651 | ||
| 3708 | break; | 3652 | break; |
| @@ -3827,6 +3771,7 @@ private: | |||
| 3827 | Maxwell3D::Regs::ShaderStage stage; | 3771 | Maxwell3D::Regs::ShaderStage stage; |
| 3828 | const std::string& suffix; | 3772 | const std::string& suffix; |
| 3829 | u64 local_memory_size; | 3773 | u64 local_memory_size; |
| 3774 | std::size_t shader_length; | ||
| 3830 | 3775 | ||
| 3831 | ShaderWriter shader; | 3776 | ShaderWriter shader; |
| 3832 | ShaderWriter declarations; | 3777 | ShaderWriter declarations; |
| @@ -3845,9 +3790,10 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u | |||
| 3845 | Maxwell3D::Regs::ShaderStage stage, | 3790 | Maxwell3D::Regs::ShaderStage stage, |
| 3846 | const std::string& suffix) { | 3791 | const std::string& suffix) { |
| 3847 | try { | 3792 | try { |
| 3848 | const auto subroutines = | 3793 | ControlFlowAnalyzer analyzer(program_code, main_offset, suffix); |
| 3849 | ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); | 3794 | const auto subroutines = analyzer.GetSubroutines(); |
| 3850 | GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); | 3795 | GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix, |
| 3796 | analyzer.GetShaderLength()); | ||
| 3851 | return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; | 3797 | return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; |
| 3852 | } catch (const DecompileFail& exception) { | 3798 | } catch (const DecompileFail& exception) { |
| 3853 | LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); | 3799 | LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 520b9d4e3..b425d98ae 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -163,6 +163,7 @@ private: | |||
| 163 | struct ShaderEntries { | 163 | struct ShaderEntries { |
| 164 | std::vector<ConstBufferEntry> const_buffer_entries; | 164 | std::vector<ConstBufferEntry> const_buffer_entries; |
| 165 | std::vector<SamplerEntry> texture_samplers; | 165 | std::vector<SamplerEntry> texture_samplers; |
| 166 | std::size_t shader_length; | ||
| 166 | }; | 167 | }; |
| 167 | 168 | ||
| 168 | using ProgramResult = std::pair<std::string, ShaderEntries>; | 169 | using ProgramResult = std::pair<std::string, ShaderEntries>; |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 9f96b2745..934f4db78 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -233,6 +233,28 @@ void OpenGLState::ApplyStencilTest() const { | |||
| 233 | config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); | 233 | config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); |
| 234 | } | 234 | } |
| 235 | } | 235 | } |
| 236 | // Viewport does not affects glClearBuffer so emulate viewport using scissor test | ||
| 237 | void OpenGLState::EmulateViewportWithScissor() { | ||
| 238 | auto& current = viewports[0]; | ||
| 239 | if (current.scissor.enabled) { | ||
| 240 | const GLint left = std::max(current.x, current.scissor.x); | ||
| 241 | const GLint right = | ||
| 242 | std::max(current.x + current.width, current.scissor.x + current.scissor.width); | ||
| 243 | const GLint bottom = std::max(current.y, current.scissor.y); | ||
| 244 | const GLint top = | ||
| 245 | std::max(current.y + current.height, current.scissor.y + current.scissor.height); | ||
| 246 | current.scissor.x = std::max(left, 0); | ||
| 247 | current.scissor.y = std::max(bottom, 0); | ||
| 248 | current.scissor.width = std::max(right - left, 0); | ||
| 249 | current.scissor.height = std::max(top - bottom, 0); | ||
| 250 | } else { | ||
| 251 | current.scissor.enabled = true; | ||
| 252 | current.scissor.x = current.x; | ||
| 253 | current.scissor.y = current.y; | ||
| 254 | current.scissor.width = current.width; | ||
| 255 | current.scissor.height = current.height; | ||
| 256 | } | ||
| 257 | } | ||
| 236 | 258 | ||
| 237 | void OpenGLState::ApplyViewport() const { | 259 | void OpenGLState::ApplyViewport() const { |
| 238 | if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) { | 260 | if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) { |
| @@ -242,7 +264,9 @@ void OpenGLState::ApplyViewport() const { | |||
| 242 | const auto& updated = viewports[i]; | 264 | const auto& updated = viewports[i]; |
| 243 | if (updated.x != current.x || updated.y != current.y || | 265 | if (updated.x != current.x || updated.y != current.y || |
| 244 | updated.width != current.width || updated.height != current.height) { | 266 | updated.width != current.width || updated.height != current.height) { |
| 245 | glViewportIndexedf(i, updated.x, updated.y, updated.width, updated.height); | 267 | glViewportIndexedf( |
| 268 | i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y), | ||
| 269 | static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height)); | ||
| 246 | } | 270 | } |
| 247 | if (updated.depth_range_near != current.depth_range_near || | 271 | if (updated.depth_range_near != current.depth_range_near || |
| 248 | updated.depth_range_far != current.depth_range_far) { | 272 | updated.depth_range_far != current.depth_range_far) { |
| @@ -270,8 +294,7 @@ void OpenGLState::ApplyViewport() const { | |||
| 270 | const auto& updated = viewports[0]; | 294 | const auto& updated = viewports[0]; |
| 271 | if (updated.x != current.x || updated.y != current.y || updated.width != current.width || | 295 | if (updated.x != current.x || updated.y != current.y || updated.width != current.width || |
| 272 | updated.height != current.height) { | 296 | updated.height != current.height) { |
| 273 | glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y), | 297 | glViewport(updated.x, updated.y, updated.width, updated.height); |
| 274 | static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height)); | ||
| 275 | } | 298 | } |
| 276 | if (updated.depth_range_near != current.depth_range_near || | 299 | if (updated.depth_range_near != current.depth_range_near || |
| 277 | updated.depth_range_far != current.depth_range_far) { | 300 | updated.depth_range_far != current.depth_range_far) { |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index bdc743b0f..032fc43f0 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -156,10 +156,10 @@ public: | |||
| 156 | } draw; | 156 | } draw; |
| 157 | 157 | ||
| 158 | struct viewport { | 158 | struct viewport { |
| 159 | GLfloat x; | 159 | GLint x; |
| 160 | GLfloat y; | 160 | GLint y; |
| 161 | GLfloat width; | 161 | GLint width; |
| 162 | GLfloat height; | 162 | GLint height; |
| 163 | GLfloat depth_range_near; // GL_DEPTH_RANGE | 163 | GLfloat depth_range_near; // GL_DEPTH_RANGE |
| 164 | GLfloat depth_range_far; // GL_DEPTH_RANGE | 164 | GLfloat depth_range_far; // GL_DEPTH_RANGE |
| 165 | struct { | 165 | struct { |
| @@ -206,6 +206,7 @@ public: | |||
| 206 | OpenGLState& ResetBuffer(GLuint handle); | 206 | OpenGLState& ResetBuffer(GLuint handle); |
| 207 | OpenGLState& ResetVertexArray(GLuint handle); | 207 | OpenGLState& ResetVertexArray(GLuint handle); |
| 208 | OpenGLState& ResetFramebuffer(GLuint handle); | 208 | OpenGLState& ResetFramebuffer(GLuint handle); |
| 209 | void EmulateViewportWithScissor(); | ||
| 209 | 210 | ||
| 210 | private: | 211 | private: |
| 211 | static OpenGLState cur_state; | 212 | static OpenGLState cur_state; |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 065b3929c..a8833c06e 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -218,14 +218,19 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { | |||
| 218 | inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { | 218 | inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { |
| 219 | switch (equation) { | 219 | switch (equation) { |
| 220 | case Maxwell::Blend::Equation::Add: | 220 | case Maxwell::Blend::Equation::Add: |
| 221 | case Maxwell::Blend::Equation::AddGL: | ||
| 221 | return GL_FUNC_ADD; | 222 | return GL_FUNC_ADD; |
| 222 | case Maxwell::Blend::Equation::Subtract: | 223 | case Maxwell::Blend::Equation::Subtract: |
| 224 | case Maxwell::Blend::Equation::SubtractGL: | ||
| 223 | return GL_FUNC_SUBTRACT; | 225 | return GL_FUNC_SUBTRACT; |
| 224 | case Maxwell::Blend::Equation::ReverseSubtract: | 226 | case Maxwell::Blend::Equation::ReverseSubtract: |
| 227 | case Maxwell::Blend::Equation::ReverseSubtractGL: | ||
| 225 | return GL_FUNC_REVERSE_SUBTRACT; | 228 | return GL_FUNC_REVERSE_SUBTRACT; |
| 226 | case Maxwell::Blend::Equation::Min: | 229 | case Maxwell::Blend::Equation::Min: |
| 230 | case Maxwell::Blend::Equation::MinGL: | ||
| 227 | return GL_MIN; | 231 | return GL_MIN; |
| 228 | case Maxwell::Blend::Equation::Max: | 232 | case Maxwell::Blend::Equation::Max: |
| 233 | case Maxwell::Blend::Equation::MaxGL: | ||
| 229 | return GL_MAX; | 234 | return GL_MAX; |
| 230 | } | 235 | } |
| 231 | LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); | 236 | LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index aad0b07ca..1492e063a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -304,6 +304,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | |||
| 304 | gl_framebuffer_data.resize(texture.width * texture.height * 4); | 304 | gl_framebuffer_data.resize(texture.width * texture.height * 4); |
| 305 | break; | 305 | break; |
| 306 | default: | 306 | default: |
| 307 | internal_format = GL_RGBA; | ||
| 308 | texture.gl_format = GL_RGBA; | ||
| 309 | texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 310 | gl_framebuffer_data.resize(texture.width * texture.height * 4); | ||
| 311 | LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer pixel format: {}", | ||
| 312 | static_cast<u32>(framebuffer.pixel_format)); | ||
| 307 | UNREACHABLE(); | 313 | UNREACHABLE(); |
| 308 | } | 314 | } |
| 309 | 315 | ||
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index efefb1f99..8a26fdff1 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp | |||
| @@ -82,8 +82,8 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( | |||
| 82 | : QString::fromStdU16String(parameters.submit_text), | 82 | : QString::fromStdU16String(parameters.submit_text), |
| 83 | QDialogButtonBox::AcceptRole); | 83 | QDialogButtonBox::AcceptRole); |
| 84 | 84 | ||
| 85 | connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::Submit); | 85 | connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept); |
| 86 | connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::Reject); | 86 | connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject); |
| 87 | layout->addWidget(header_label); | 87 | layout->addWidget(header_label); |
| 88 | layout->addWidget(sub_label); | 88 | layout->addWidget(sub_label); |
| 89 | layout->addWidget(guide_label); | 89 | layout->addWidget(guide_label); |
| @@ -96,16 +96,16 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( | |||
| 96 | 96 | ||
| 97 | QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; | 97 | QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; |
| 98 | 98 | ||
| 99 | void QtSoftwareKeyboardDialog::Submit() { | 99 | void QtSoftwareKeyboardDialog::accept() { |
| 100 | ok = true; | 100 | ok = true; |
| 101 | text = line_edit->text().toStdU16String(); | 101 | text = line_edit->text().toStdU16String(); |
| 102 | accept(); | 102 | QDialog::accept(); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | void QtSoftwareKeyboardDialog::Reject() { | 105 | void QtSoftwareKeyboardDialog::reject() { |
| 106 | ok = false; | 106 | ok = false; |
| 107 | text.clear(); | 107 | text.clear(); |
| 108 | accept(); | 108 | QDialog::reject(); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | std::u16string QtSoftwareKeyboardDialog::GetText() const { | 111 | std::u16string QtSoftwareKeyboardDialog::GetText() const { |
| @@ -129,13 +129,13 @@ QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; | |||
| 129 | 129 | ||
| 130 | void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out, | 130 | void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out, |
| 131 | Core::Frontend::SoftwareKeyboardParameters parameters) const { | 131 | Core::Frontend::SoftwareKeyboardParameters parameters) const { |
| 132 | text_output = out; | 132 | text_output = std::move(out); |
| 133 | emit MainWindowGetText(parameters); | 133 | emit MainWindowGetText(parameters); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, | 136 | void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, |
| 137 | std::function<void()> finished_check) const { | 137 | std::function<void()> finished_check) const { |
| 138 | this->finished_check = finished_check; | 138 | this->finished_check = std::move(finished_check); |
| 139 | emit MainWindowTextCheckDialog(error_message); | 139 | emit MainWindowTextCheckDialog(error_message); |
| 140 | } | 140 | } |
| 141 | 141 | ||
diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 73f56714f..c63720ba4 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h | |||
| @@ -33,8 +33,8 @@ public: | |||
| 33 | Core::Frontend::SoftwareKeyboardParameters parameters); | 33 | Core::Frontend::SoftwareKeyboardParameters parameters); |
| 34 | ~QtSoftwareKeyboardDialog() override; | 34 | ~QtSoftwareKeyboardDialog() override; |
| 35 | 35 | ||
| 36 | void Submit(); | 36 | void accept() override; |
| 37 | void Reject(); | 37 | void reject() override; |
| 38 | 38 | ||
| 39 | std::u16string GetText() const; | 39 | std::u16string GetText() const; |
| 40 | bool GetStatus() const; | 40 | bool GetStatus() const; |
| @@ -70,11 +70,10 @@ signals: | |||
| 70 | void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; | 70 | void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; |
| 71 | void MainWindowTextCheckDialog(std::u16string error_message) const; | 71 | void MainWindowTextCheckDialog(std::u16string error_message) const; |
| 72 | 72 | ||
| 73 | public slots: | 73 | private: |
| 74 | void MainWindowFinishedText(std::optional<std::u16string> text); | 74 | void MainWindowFinishedText(std::optional<std::u16string> text); |
| 75 | void MainWindowFinishedCheckDialog(); | 75 | void MainWindowFinishedCheckDialog(); |
| 76 | 76 | ||
| 77 | private: | ||
| 78 | mutable std::function<void(std::optional<std::u16string>)> text_output; | 77 | mutable std::function<void(std::optional<std::u16string>)> text_output; |
| 79 | mutable std::function<void()> finished_check; | 78 | mutable std::function<void()> finished_check; |
| 80 | }; | 79 | }; |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index e24ed5f2b..83ebbd1fe 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -432,6 +432,7 @@ void Config::ReadValues() { | |||
| 432 | Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); | 432 | Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); |
| 433 | Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); | 433 | Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); |
| 434 | Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); | 434 | Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); |
| 435 | Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool(); | ||
| 435 | Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); | 436 | Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); |
| 436 | qt_config->endGroup(); | 437 | qt_config->endGroup(); |
| 437 | 438 | ||
| @@ -638,6 +639,7 @@ void Config::SaveValues() { | |||
| 638 | qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); | 639 | qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); |
| 639 | qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); | 640 | qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); |
| 640 | qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); | 641 | qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); |
| 642 | qt_config->setValue("dump_exefs", Settings::values.dump_exefs); | ||
| 641 | qt_config->setValue("dump_nso", Settings::values.dump_nso); | 643 | qt_config->setValue("dump_nso", Settings::values.dump_nso); |
| 642 | qt_config->endGroup(); | 644 | qt_config->endGroup(); |
| 643 | 645 | ||
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index fd5876b41..aa7de7b54 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() { | |||
| 34 | ui->toggle_console->setChecked(UISettings::values.show_console); | 34 | ui->toggle_console->setChecked(UISettings::values.show_console); |
| 35 | ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); | 35 | ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); |
| 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); |
| 37 | ui->dump_exefs->setChecked(Settings::values.dump_exefs); | ||
| 37 | ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); | 38 | ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| @@ -43,6 +44,7 @@ void ConfigureDebug::applyConfiguration() { | |||
| 43 | UISettings::values.show_console = ui->toggle_console->isChecked(); | 44 | UISettings::values.show_console = ui->toggle_console->isChecked(); |
| 44 | Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); | 45 | Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); |
| 45 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | 46 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); |
| 47 | Settings::values.dump_exefs = ui->dump_exefs->isChecked(); | ||
| 46 | Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); | 48 | Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); |
| 47 | Debugger::ToggleConsole(); | 49 | Debugger::ToggleConsole(); |
| 48 | Log::Filter filter; | 50 | Log::Filter filter; |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 9c5b702f8..758a92335 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -145,6 +145,16 @@ | |||
| 145 | </property> | 145 | </property> |
| 146 | </widget> | 146 | </widget> |
| 147 | </item> | 147 | </item> |
| 148 | <item> | ||
| 149 | <widget class="QCheckBox" name="dump_exefs"> | ||
| 150 | <property name="whatsThis"> | ||
| 151 | <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> | ||
| 152 | </property> | ||
| 153 | <property name="text"> | ||
| 154 | <string>Dump ExeFS</string> | ||
| 155 | </property> | ||
| 156 | </widget> | ||
| 157 | </item> | ||
| 148 | </layout> | 158 | </layout> |
| 149 | </widget> | 159 | </widget> |
| 150 | </item> | 160 | </item> |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 91fcad994..e278cdd05 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -23,31 +23,31 @@ | |||
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 25 | <item> | 25 | <item> |
| 26 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | 26 | <layout class="QHBoxLayout" name="horizontalLayout_2"> |
| 27 | <item> | 27 | <item> |
| 28 | <widget class="QCheckBox" name="toggle_frame_limit"> | 28 | <widget class="QCheckBox" name="toggle_frame_limit"> |
| 29 | <property name="text"> | 29 | <property name="text"> |
| 30 | <string>Limit Speed Percent</string> | 30 | <string>Limit Speed Percent</string> |
| 31 | </property> | 31 | </property> |
| 32 | </widget> | 32 | </widget> |
| 33 | </item> | 33 | </item> |
| 34 | <item> | 34 | <item> |
| 35 | <widget class="QSpinBox" name="frame_limit"> | 35 | <widget class="QSpinBox" name="frame_limit"> |
| 36 | <property name="suffix"> | 36 | <property name="suffix"> |
| 37 | <string>%</string> | 37 | <string>%</string> |
| 38 | </property> | 38 | </property> |
| 39 | <property name="minimum"> | 39 | <property name="minimum"> |
| 40 | <number>1</number> | 40 | <number>1</number> |
| 41 | </property> | 41 | </property> |
| 42 | <property name="maximum"> | 42 | <property name="maximum"> |
| 43 | <number>9999</number> | 43 | <number>9999</number> |
| 44 | </property> | 44 | </property> |
| 45 | <property name="value"> | 45 | <property name="value"> |
| 46 | <number>100</number> | 46 | <number>100</number> |
| 47 | </property> | 47 | </property> |
| 48 | </widget> | 48 | </widget> |
| 49 | </item> | 49 | </item> |
| 50 | </layout> | 50 | </layout> |
| 51 | </item> | 51 | </item> |
| 52 | <item> | 52 | <item> |
| 53 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> | 53 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> |
| @@ -61,7 +61,7 @@ | |||
| 61 | <item> | 61 | <item> |
| 62 | <widget class="QLabel" name="label"> | 62 | <widget class="QLabel" name="label"> |
| 63 | <property name="text"> | 63 | <property name="text"> |
| 64 | <string>Internal Resolution:(Currently does nothing.)</string> | 64 | <string>Internal Resolution</string> |
| 65 | </property> | 65 | </property> |
| 66 | </widget> | 66 | </widget> |
| 67 | </item> | 67 | </item> |
| @@ -96,27 +96,27 @@ | |||
| 96 | </item> | 96 | </item> |
| 97 | </layout> | 97 | </layout> |
| 98 | </item> | 98 | </item> |
| 99 | <item> | 99 | <item> |
| 100 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | 100 | <layout class="QHBoxLayout" name="horizontalLayout_6"> |
| 101 | <item> | 101 | <item> |
| 102 | <widget class="QLabel" name="bg_label"> | 102 | <widget class="QLabel" name="bg_label"> |
| 103 | <property name="text"> | 103 | <property name="text"> |
| 104 | <string>Background Color:</string> | 104 | <string>Background Color:</string> |
| 105 | </property> | 105 | </property> |
| 106 | </widget> | 106 | </widget> |
| 107 | </item> | 107 | </item> |
| 108 | <item> | 108 | <item> |
| 109 | <widget class="QPushButton" name="bg_button"> | 109 | <widget class="QPushButton" name="bg_button"> |
| 110 | <property name="maximumSize"> | 110 | <property name="maximumSize"> |
| 111 | <size> | 111 | <size> |
| 112 | <width>40</width> | 112 | <width>40</width> |
| 113 | <height>16777215</height> | 113 | <height>16777215</height> |
| 114 | </size> | 114 | </size> |
| 115 | </property> | 115 | </property> |
| 116 | </widget> | 116 | </widget> |
| 117 | </item> | 117 | </item> |
| 118 | </layout> | 118 | </layout> |
| 119 | </item> | 119 | </item> |
| 120 | </layout> | 120 | </layout> |
| 121 | </widget> | 121 | </widget> |
| 122 | </item> | 122 | </item> |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index c66353a65..097c1fbe3 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -366,6 +366,7 @@ void Config::ReadValues() { | |||
| 366 | Settings::values.gdbstub_port = | 366 | Settings::values.gdbstub_port = |
| 367 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); | 367 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); |
| 368 | Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); | 368 | Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); |
| 369 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); | ||
| 369 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); | 370 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); |
| 370 | 371 | ||
| 371 | // Web Service | 372 | // Web Service |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index ecf625e7b..d73669f36 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -206,6 +206,8 @@ log_filter = *:Trace | |||
| 206 | # Port for listening to GDB connections. | 206 | # Port for listening to GDB connections. |
| 207 | use_gdbstub=false | 207 | use_gdbstub=false |
| 208 | gdbstub_port=24689 | 208 | gdbstub_port=24689 |
| 209 | # Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them | ||
| 210 | dump_exefs=false | ||
| 209 | # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them | 211 | # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them |
| 210 | dump_nso=false | 212 | dump_nso=false |
| 211 | 213 | ||