diff options
Diffstat (limited to 'src')
48 files changed, 3302 insertions, 1258 deletions
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h index baf1f1c9e..1176a72b1 100644 --- a/src/common/scope_exit.h +++ b/src/common/scope_exit.h | |||
| @@ -20,7 +20,7 @@ struct ScopeExitHelper { | |||
| 20 | 20 | ||
| 21 | template <typename Func> | 21 | template <typename Func> |
| 22 | ScopeExitHelper<Func> ScopeExit(Func&& func) { | 22 | ScopeExitHelper<Func> ScopeExit(Func&& func) { |
| 23 | return ScopeExitHelper<Func>(std::move(func)); | 23 | return ScopeExitHelper<Func>(std::forward<Func>(func)); |
| 24 | } | 24 | } |
| 25 | } // namespace detail | 25 | } // namespace detail |
| 26 | 26 | ||
diff --git a/src/common/swap.h b/src/common/swap.h index b3eab1324..71932c2bb 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -21,11 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | #if defined(_MSC_VER) | 22 | #if defined(_MSC_VER) |
| 23 | #include <cstdlib> | 23 | #include <cstdlib> |
| 24 | #elif defined(__linux__) | ||
| 25 | #include <byteswap.h> | ||
| 26 | #elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \ | ||
| 27 | defined(__NetBSD__) || defined(__OpenBSD__) | ||
| 28 | #include <sys/endian.h> | ||
| 29 | #endif | 24 | #endif |
| 30 | #include <cstring> | 25 | #include <cstring> |
| 31 | #include "common/common_types.h" | 26 | #include "common/common_types.h" |
| @@ -62,86 +57,49 @@ | |||
| 62 | namespace Common { | 57 | namespace Common { |
| 63 | 58 | ||
| 64 | #ifdef _MSC_VER | 59 | #ifdef _MSC_VER |
| 65 | inline u16 swap16(u16 _data) { | 60 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 66 | return _byteswap_ushort(_data); | 61 | return _byteswap_ushort(data); |
| 67 | } | 62 | } |
| 68 | inline u32 swap32(u32 _data) { | 63 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 69 | return _byteswap_ulong(_data); | 64 | return _byteswap_ulong(data); |
| 70 | } | 65 | } |
| 71 | inline u64 swap64(u64 _data) { | 66 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 72 | return _byteswap_uint64(_data); | 67 | return _byteswap_uint64(data); |
| 73 | } | 68 | } |
| 74 | #elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6) | 69 | #elif defined(__clang__) || defined(__GNUC__) |
| 75 | inline u16 swap16(u16 _data) { | 70 | #if defined(__Bitrig__) || defined(__OpenBSD__) |
| 76 | u32 data = _data; | ||
| 77 | __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); | ||
| 78 | return (u16)data; | ||
| 79 | } | ||
| 80 | inline u32 swap32(u32 _data) { | ||
| 81 | __asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data)); | ||
| 82 | return _data; | ||
| 83 | } | ||
| 84 | inline u64 swap64(u64 _data) { | ||
| 85 | return ((u64)swap32(_data) << 32) | swap32(_data >> 32); | ||
| 86 | } | ||
| 87 | #elif __linux__ | ||
| 88 | inline u16 swap16(u16 _data) { | ||
| 89 | return bswap_16(_data); | ||
| 90 | } | ||
| 91 | inline u32 swap32(u32 _data) { | ||
| 92 | return bswap_32(_data); | ||
| 93 | } | ||
| 94 | inline u64 swap64(u64 _data) { | ||
| 95 | return bswap_64(_data); | ||
| 96 | } | ||
| 97 | #elif __APPLE__ | ||
| 98 | inline __attribute__((always_inline)) u16 swap16(u16 _data) { | ||
| 99 | return (_data >> 8) | (_data << 8); | ||
| 100 | } | ||
| 101 | inline __attribute__((always_inline)) u32 swap32(u32 _data) { | ||
| 102 | return __builtin_bswap32(_data); | ||
| 103 | } | ||
| 104 | inline __attribute__((always_inline)) u64 swap64(u64 _data) { | ||
| 105 | return __builtin_bswap64(_data); | ||
| 106 | } | ||
| 107 | #elif defined(__Bitrig__) || defined(__OpenBSD__) | ||
| 108 | // redefine swap16, swap32, swap64 as inline functions | 71 | // redefine swap16, swap32, swap64 as inline functions |
| 109 | #undef swap16 | 72 | #undef swap16 |
| 110 | #undef swap32 | 73 | #undef swap32 |
| 111 | #undef swap64 | 74 | #undef swap64 |
| 112 | inline u16 swap16(u16 _data) { | 75 | #endif |
| 113 | return __swap16(_data); | 76 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 114 | } | 77 | return __builtin_bswap16(data); |
| 115 | inline u32 swap32(u32 _data) { | ||
| 116 | return __swap32(_data); | ||
| 117 | } | ||
| 118 | inline u64 swap64(u64 _data) { | ||
| 119 | return __swap64(_data); | ||
| 120 | } | ||
| 121 | #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) | ||
| 122 | inline u16 swap16(u16 _data) { | ||
| 123 | return bswap16(_data); | ||
| 124 | } | 78 | } |
| 125 | inline u32 swap32(u32 _data) { | 79 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 126 | return bswap32(_data); | 80 | return __builtin_bswap32(data); |
| 127 | } | 81 | } |
| 128 | inline u64 swap64(u64 _data) { | 82 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 129 | return bswap64(_data); | 83 | return __builtin_bswap64(data); |
| 130 | } | 84 | } |
| 131 | #else | 85 | #else |
| 132 | // Slow generic implementation. | 86 | // Generic implementation. |
| 133 | inline u16 swap16(u16 data) { | 87 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 134 | return (data >> 8) | (data << 8); | 88 | return (data >> 8) | (data << 8); |
| 135 | } | 89 | } |
| 136 | inline u32 swap32(u32 data) { | 90 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 137 | return (swap16(data) << 16) | swap16(data >> 16); | 91 | return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) | |
| 92 | ((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24); | ||
| 138 | } | 93 | } |
| 139 | inline u64 swap64(u64 data) { | 94 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 140 | return ((u64)swap32(data) << 32) | swap32(data >> 32); | 95 | return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) | |
| 96 | ((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) | | ||
| 97 | ((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) | | ||
| 98 | ((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56); | ||
| 141 | } | 99 | } |
| 142 | #endif | 100 | #endif |
| 143 | 101 | ||
| 144 | inline float swapf(float f) { | 102 | [[nodiscard]] inline float swapf(float f) noexcept { |
| 145 | static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); | 103 | static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); |
| 146 | 104 | ||
| 147 | u32 value; | 105 | u32 value; |
| @@ -153,7 +111,7 @@ inline float swapf(float f) { | |||
| 153 | return f; | 111 | return f; |
| 154 | } | 112 | } |
| 155 | 113 | ||
| 156 | inline double swapd(double f) { | 114 | [[nodiscard]] inline double swapd(double f) noexcept { |
| 157 | static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); | 115 | static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); |
| 158 | 116 | ||
| 159 | u64 value; | 117 | u64 value; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 49145911b..dc96e35d5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 15 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| 16 | #include "core/gdbstub/gdbstub.h" | 16 | #include "core/gdbstub/gdbstub.h" |
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 17 | #include "core/hle/kernel/process.h" | 18 | #include "core/hle/kernel/process.h" |
| 18 | #include "core/hle/kernel/svc.h" | 19 | #include "core/hle/kernel/svc.h" |
| 19 | #include "core/hle/kernel/vm_manager.h" | 20 | #include "core/hle/kernel/vm_manager.h" |
| @@ -99,7 +100,7 @@ public: | |||
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | void CallSVC(u32 swi) override { | 102 | void CallSVC(u32 swi) override { |
| 102 | Kernel::CallSVC(swi); | 103 | Kernel::CallSVC(parent.system, swi); |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | void AddTicks(u64 ticks) override { | 106 | void AddTicks(u64 ticks) override { |
| @@ -112,14 +113,14 @@ public: | |||
| 112 | // Always execute at least one tick. | 113 | // Always execute at least one tick. |
| 113 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 114 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 114 | 115 | ||
| 115 | parent.core_timing.AddTicks(amortized_ticks); | 116 | parent.system.CoreTiming().AddTicks(amortized_ticks); |
| 116 | num_interpreted_instructions = 0; | 117 | num_interpreted_instructions = 0; |
| 117 | } | 118 | } |
| 118 | u64 GetTicksRemaining() override { | 119 | u64 GetTicksRemaining() override { |
| 119 | return std::max(parent.core_timing.GetDowncount(), 0); | 120 | return std::max(parent.system.CoreTiming().GetDowncount(), 0); |
| 120 | } | 121 | } |
| 121 | u64 GetCNTPCT() override { | 122 | u64 GetCNTPCT() override { |
| 122 | return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks()); | 123 | return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); |
| 123 | } | 124 | } |
| 124 | 125 | ||
| 125 | ARM_Dynarmic& parent; | 126 | ARM_Dynarmic& parent; |
| @@ -129,7 +130,7 @@ public: | |||
| 129 | }; | 130 | }; |
| 130 | 131 | ||
| 131 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { | 132 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { |
| 132 | auto* current_process = Core::CurrentProcess(); | 133 | auto* current_process = system.Kernel().CurrentProcess(); |
| 133 | auto** const page_table = current_process->VMManager().page_table.pointers.data(); | 134 | auto** const page_table = current_process->VMManager().page_table.pointers.data(); |
| 134 | 135 | ||
| 135 | Dynarmic::A64::UserConfig config; | 136 | Dynarmic::A64::UserConfig config; |
| @@ -171,10 +172,10 @@ void ARM_Dynarmic::Step() { | |||
| 171 | cb->InterpreterFallback(jit->GetPC(), 1); | 172 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 172 | } | 173 | } |
| 173 | 174 | ||
| 174 | ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, | 175 | ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, |
| 175 | std::size_t core_index) | 176 | std::size_t core_index) |
| 176 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing}, | 177 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, |
| 177 | core_index{core_index}, core_timing{core_timing}, | 178 | core_index{core_index}, system{system}, |
| 178 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { | 179 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { |
| 179 | ThreadContext ctx{}; | 180 | ThreadContext ctx{}; |
| 180 | inner_unicorn.SaveContext(ctx); | 181 | inner_unicorn.SaveContext(ctx); |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index d867c2a50..c1db254e8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h | |||
| @@ -12,19 +12,15 @@ | |||
| 12 | #include "core/arm/exclusive_monitor.h" | 12 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | 13 | #include "core/arm/unicorn/arm_unicorn.h" |
| 14 | 14 | ||
| 15 | namespace Core::Timing { | ||
| 16 | class CoreTiming; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Core { | 15 | namespace Core { |
| 20 | 16 | ||
| 21 | class ARM_Dynarmic_Callbacks; | 17 | class ARM_Dynarmic_Callbacks; |
| 22 | class DynarmicExclusiveMonitor; | 18 | class DynarmicExclusiveMonitor; |
| 19 | class System; | ||
| 23 | 20 | ||
| 24 | class ARM_Dynarmic final : public ARM_Interface { | 21 | class ARM_Dynarmic final : public ARM_Interface { |
| 25 | public: | 22 | public: |
| 26 | ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, | 23 | ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); |
| 27 | std::size_t core_index); | ||
| 28 | ~ARM_Dynarmic() override; | 24 | ~ARM_Dynarmic() override; |
| 29 | 25 | ||
| 30 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, | 26 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| @@ -63,7 +59,7 @@ private: | |||
| 63 | ARM_Unicorn inner_unicorn; | 59 | ARM_Unicorn inner_unicorn; |
| 64 | 60 | ||
| 65 | std::size_t core_index; | 61 | std::size_t core_index; |
| 66 | Timing::CoreTiming& core_timing; | 62 | System& system; |
| 67 | DynarmicExclusiveMonitor& exclusive_monitor; | 63 | DynarmicExclusiveMonitor& exclusive_monitor; |
| 68 | }; | 64 | }; |
| 69 | 65 | ||
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 27309280c..4e07fe8b5 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 12 | #include "core/hle/kernel/svc.h" | 12 | #include "core/hle/kernel/svc.h" |
| 13 | #include "core/memory.h" | ||
| 14 | 13 | ||
| 15 | namespace Core { | 14 | namespace Core { |
| 16 | 15 | ||
| @@ -49,20 +48,6 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_ | |||
| 49 | } | 48 | } |
| 50 | } | 49 | } |
| 51 | 50 | ||
| 52 | static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) { | ||
| 53 | u32 esr{}; | ||
| 54 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); | ||
| 55 | |||
| 56 | auto ec = esr >> 26; | ||
| 57 | auto iss = esr & 0xFFFFFF; | ||
| 58 | |||
| 59 | switch (ec) { | ||
| 60 | case 0x15: // SVC | ||
| 61 | Kernel::CallSVC(iss); | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, | 51 | static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, |
| 67 | void* user_data) { | 52 | void* user_data) { |
| 68 | ARM_Interface::ThreadContext ctx{}; | 53 | ARM_Interface::ThreadContext ctx{}; |
| @@ -72,7 +57,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si | |||
| 72 | return {}; | 57 | return {}; |
| 73 | } | 58 | } |
| 74 | 59 | ||
| 75 | ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} { | 60 | ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { |
| 76 | CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); | 61 | CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); |
| 77 | 62 | ||
| 78 | auto fpv = 3 << 20; | 63 | auto fpv = 3 << 20; |
| @@ -177,7 +162,7 @@ void ARM_Unicorn::Run() { | |||
| 177 | if (GDBStub::IsServerEnabled()) { | 162 | if (GDBStub::IsServerEnabled()) { |
| 178 | ExecuteInstructions(std::max(4000000, 0)); | 163 | ExecuteInstructions(std::max(4000000, 0)); |
| 179 | } else { | 164 | } else { |
| 180 | ExecuteInstructions(std::max(core_timing.GetDowncount(), 0)); | 165 | ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0)); |
| 181 | } | 166 | } |
| 182 | } | 167 | } |
| 183 | 168 | ||
| @@ -190,7 +175,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); | |||
| 190 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { | 175 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { |
| 191 | MICROPROFILE_SCOPE(ARM_Jit_Unicorn); | 176 | MICROPROFILE_SCOPE(ARM_Jit_Unicorn); |
| 192 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); | 177 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); |
| 193 | core_timing.AddTicks(num_instructions); | 178 | system.CoreTiming().AddTicks(num_instructions); |
| 194 | if (GDBStub::IsServerEnabled()) { | 179 | if (GDBStub::IsServerEnabled()) { |
| 195 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { | 180 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { |
| 196 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 181 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
| @@ -273,4 +258,20 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) { | |||
| 273 | last_bkpt_hit = true; | 258 | last_bkpt_hit = true; |
| 274 | } | 259 | } |
| 275 | 260 | ||
| 261 | void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) { | ||
| 262 | u32 esr{}; | ||
| 263 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); | ||
| 264 | |||
| 265 | const auto ec = esr >> 26; | ||
| 266 | const auto iss = esr & 0xFFFFFF; | ||
| 267 | |||
| 268 | auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data); | ||
| 269 | |||
| 270 | switch (ec) { | ||
| 271 | case 0x15: // SVC | ||
| 272 | Kernel::CallSVC(arm_instance->system, iss); | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 276 | } // namespace Core | 277 | } // namespace Core |
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 1e44f0736..209fc16ad 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h | |||
| @@ -9,15 +9,13 @@ | |||
| 9 | #include "core/arm/arm_interface.h" | 9 | #include "core/arm/arm_interface.h" |
| 10 | #include "core/gdbstub/gdbstub.h" | 10 | #include "core/gdbstub/gdbstub.h" |
| 11 | 11 | ||
| 12 | namespace Core::Timing { | ||
| 13 | class CoreTiming; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Core { | 12 | namespace Core { |
| 17 | 13 | ||
| 14 | class System; | ||
| 15 | |||
| 18 | class ARM_Unicorn final : public ARM_Interface { | 16 | class ARM_Unicorn final : public ARM_Interface { |
| 19 | public: | 17 | public: |
| 20 | explicit ARM_Unicorn(Timing::CoreTiming& core_timing); | 18 | explicit ARM_Unicorn(System& system); |
| 21 | ~ARM_Unicorn() override; | 19 | ~ARM_Unicorn() override; |
| 22 | 20 | ||
| 23 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, | 21 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| @@ -47,8 +45,10 @@ public: | |||
| 47 | void RecordBreak(GDBStub::BreakpointAddress bkpt); | 45 | void RecordBreak(GDBStub::BreakpointAddress bkpt); |
| 48 | 46 | ||
| 49 | private: | 47 | private: |
| 48 | static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data); | ||
| 49 | |||
| 50 | uc_engine* uc{}; | 50 | uc_engine* uc{}; |
| 51 | Timing::CoreTiming& core_timing; | 51 | System& system; |
| 52 | GDBStub::BreakpointAddress last_bkpt{}; | 52 | GDBStub::BreakpointAddress last_bkpt{}; |
| 53 | bool last_bkpt_hit = false; | 53 | bool last_bkpt_hit = false; |
| 54 | }; | 54 | }; |
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index e75741db0..ba63c3e61 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp | |||
| @@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba | |||
| 55 | : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { | 55 | : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { |
| 56 | if (Settings::values.use_cpu_jit) { | 56 | if (Settings::values.use_cpu_jit) { |
| 57 | #ifdef ARCHITECTURE_x86_64 | 57 | #ifdef ARCHITECTURE_x86_64 |
| 58 | arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index); | 58 | arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); |
| 59 | #else | 59 | #else |
| 60 | arm_interface = std::make_unique<ARM_Unicorn>(); | 60 | arm_interface = std::make_unique<ARM_Unicorn>(system); |
| 61 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | 61 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); |
| 62 | #endif | 62 | #endif |
| 63 | } else { | 63 | } else { |
| 64 | arm_interface = std::make_unique<ARM_Unicorn>(core_timing); | 64 | arm_interface = std::make_unique<ARM_Unicorn>(system); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); | 67 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index d0bcb4660..70a522556 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -13,6 +13,23 @@ | |||
| 13 | namespace Core::Frontend { | 13 | namespace Core::Frontend { |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Represents a graphics context that can be used for background computation or drawing. If the | ||
| 17 | * graphics backend doesn't require the context, then the implementation of these methods can be | ||
| 18 | * stubs | ||
| 19 | */ | ||
| 20 | class GraphicsContext { | ||
| 21 | public: | ||
| 22 | /// Makes the graphics context current for the caller thread | ||
| 23 | virtual void MakeCurrent() = 0; | ||
| 24 | |||
| 25 | /// Releases (dunno if this is the "right" word) the context from the caller thread | ||
| 26 | virtual void DoneCurrent() = 0; | ||
| 27 | |||
| 28 | /// Swap buffers to display the next frame | ||
| 29 | virtual void SwapBuffers() = 0; | ||
| 30 | }; | ||
| 31 | |||
| 32 | /** | ||
| 16 | * Abstraction class used to provide an interface between emulation code and the frontend | 33 | * Abstraction class used to provide an interface between emulation code and the frontend |
| 17 | * (e.g. SDL, QGLWidget, GLFW, etc...). | 34 | * (e.g. SDL, QGLWidget, GLFW, etc...). |
| 18 | * | 35 | * |
| @@ -30,7 +47,7 @@ namespace Core::Frontend { | |||
| 30 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | 47 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please |
| 31 | * re-read the upper points again and think about it if you don't see this. | 48 | * re-read the upper points again and think about it if you don't see this. |
| 32 | */ | 49 | */ |
| 33 | class EmuWindow { | 50 | class EmuWindow : public GraphicsContext { |
| 34 | public: | 51 | public: |
| 35 | /// Data structure to store emuwindow configuration | 52 | /// Data structure to store emuwindow configuration |
| 36 | struct WindowConfig { | 53 | struct WindowConfig { |
| @@ -40,17 +57,21 @@ public: | |||
| 40 | std::pair<unsigned, unsigned> min_client_area_size; | 57 | std::pair<unsigned, unsigned> min_client_area_size; |
| 41 | }; | 58 | }; |
| 42 | 59 | ||
| 43 | /// Swap buffers to display the next frame | ||
| 44 | virtual void SwapBuffers() = 0; | ||
| 45 | |||
| 46 | /// Polls window events | 60 | /// Polls window events |
| 47 | virtual void PollEvents() = 0; | 61 | virtual void PollEvents() = 0; |
| 48 | 62 | ||
| 49 | /// Makes the graphics context current for the caller thread | 63 | /** |
| 50 | virtual void MakeCurrent() = 0; | 64 | * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This |
| 51 | 65 | * context can be used from other threads for background graphics computation. If the frontend | |
| 52 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread | 66 | * is using a graphics backend that doesn't need anything specific to run on a different thread, |
| 53 | virtual void DoneCurrent() = 0; | 67 | * then it can use a stubbed implemenation for GraphicsContext. |
| 68 | * | ||
| 69 | * If the return value is null, then the core should assume that the frontend cannot provide a | ||
| 70 | * Shared Context | ||
| 71 | */ | ||
| 72 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { | ||
| 73 | return nullptr; | ||
| 74 | } | ||
| 54 | 75 | ||
| 55 | /** | 76 | /** |
| 56 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 77 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 6cd607206..4921ad4f0 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | return name; | 25 | return name; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | static const HandleType HANDLE_TYPE = HandleType::ClientPort; | 28 | static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort; |
| 29 | HandleType GetHandleType() const override { | 29 | HandleType GetHandleType() const override { |
| 30 | return HANDLE_TYPE; | 30 | return HANDLE_TYPE; |
| 31 | } | 31 | } |
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index b1f39aad7..09cdff588 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h | |||
| @@ -29,7 +29,7 @@ public: | |||
| 29 | return name; | 29 | return name; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | static const HandleType HANDLE_TYPE = HandleType::ClientSession; | 32 | static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession; |
| 33 | HandleType GetHandleType() const override { | 33 | HandleType GetHandleType() const override { |
| 34 | return HANDLE_TYPE; | 34 | return HANDLE_TYPE; |
| 35 | } | 35 | } |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index f060f2a3b..dda52f4c0 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -85,7 +85,7 @@ public: | |||
| 85 | return name; | 85 | return name; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | static const HandleType HANDLE_TYPE = HandleType::Process; | 88 | static constexpr HandleType HANDLE_TYPE = HandleType::Process; |
| 89 | HandleType GetHandleType() const override { | 89 | HandleType GetHandleType() const override { |
| 90 | return HANDLE_TYPE; | 90 | return HANDLE_TYPE; |
| 91 | } | 91 | } |
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 2eb9dcbb7..84215f572 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | return reset_type; | 31 | return reset_type; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | static const HandleType HANDLE_TYPE = HandleType::ReadableEvent; | 34 | static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent; |
| 35 | HandleType GetHandleType() const override { | 35 | HandleType GetHandleType() const override { |
| 36 | return HANDLE_TYPE; | 36 | return HANDLE_TYPE; |
| 37 | } | 37 | } |
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 70e09858a..2613a6bb5 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h | |||
| @@ -41,7 +41,7 @@ public: | |||
| 41 | return GetTypeName(); | 41 | return GetTypeName(); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | static const HandleType HANDLE_TYPE = HandleType::ResourceLimit; | 44 | static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit; |
| 45 | HandleType GetHandleType() const override { | 45 | HandleType GetHandleType() const override { |
| 46 | return HANDLE_TYPE; | 46 | return HANDLE_TYPE; |
| 47 | } | 47 | } |
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index fef573b71..dc88a1ebd 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -43,7 +43,7 @@ public: | |||
| 43 | return name; | 43 | return name; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | static const HandleType HANDLE_TYPE = HandleType::ServerPort; | 46 | static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort; |
| 47 | HandleType GetHandleType() const override { | 47 | HandleType GetHandleType() const override { |
| 48 | return HANDLE_TYPE; | 48 | return HANDLE_TYPE; |
| 49 | } | 49 | } |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index a6b2cf06a..696a82cd9 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -28,11 +28,9 @@ ServerSession::~ServerSession() { | |||
| 28 | // the emulated application. | 28 | // the emulated application. |
| 29 | 29 | ||
| 30 | // Decrease the port's connection count. | 30 | // Decrease the port's connection count. |
| 31 | if (parent->port) | 31 | if (parent->port) { |
| 32 | parent->port->ConnectionClosed(); | 32 | parent->port->ConnectionClosed(); |
| 33 | 33 | } | |
| 34 | // TODO(Subv): Wake up all the ClientSession's waiting threads and set | ||
| 35 | // the SendSyncRequest result to 0xC920181A. | ||
| 36 | 34 | ||
| 37 | parent->server = nullptr; | 35 | parent->server = nullptr; |
| 38 | } | 36 | } |
| @@ -74,9 +72,6 @@ void ServerSession::ClientDisconnected() { | |||
| 74 | handler->ClientDisconnected(this); | 72 | handler->ClientDisconnected(this); |
| 75 | } | 73 | } |
| 76 | 74 | ||
| 77 | // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set | ||
| 78 | // their WaitSynchronization result to 0xC920181A. | ||
| 79 | |||
| 80 | // Clean up the list of client threads with pending requests, they are unneeded now that the | 75 | // Clean up the list of client threads with pending requests, they are unneeded now that the |
| 81 | // client endpoint is closed. | 76 | // client endpoint is closed. |
| 82 | pending_requesting_threads.clear(); | 77 | pending_requesting_threads.clear(); |
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 09b835ff8..738df30f8 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -46,7 +46,7 @@ public: | |||
| 46 | return name; | 46 | return name; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | static const HandleType HANDLE_TYPE = HandleType::ServerSession; | 49 | static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession; |
| 50 | HandleType GetHandleType() const override { | 50 | HandleType GetHandleType() const override { |
| 51 | return HANDLE_TYPE; | 51 | return HANDLE_TYPE; |
| 52 | } | 52 | } |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 37e18c443..c2b6155e1 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -76,7 +76,7 @@ public: | |||
| 76 | return name; | 76 | return name; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | static const HandleType HANDLE_TYPE = HandleType::SharedMemory; | 79 | static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory; |
| 80 | HandleType GetHandleType() const override { | 80 | HandleType GetHandleType() const override { |
| 81 | return HANDLE_TYPE; | 81 | return HANDLE_TYPE; |
| 82 | } | 82 | } |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2fd07ab34..e5d4d6b55 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -131,16 +131,15 @@ enum class ResourceLimitValueType { | |||
| 131 | LimitValue, | 131 | LimitValue, |
| 132 | }; | 132 | }; |
| 133 | 133 | ||
| 134 | ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type, | 134 | ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, |
| 135 | ResourceLimitValueType value_type) { | 135 | u32 resource_type, ResourceLimitValueType value_type) { |
| 136 | const auto type = static_cast<ResourceType>(resource_type); | 136 | const auto type = static_cast<ResourceType>(resource_type); |
| 137 | if (!IsValidResourceType(type)) { | 137 | if (!IsValidResourceType(type)) { |
| 138 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); | 138 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); |
| 139 | return ERR_INVALID_ENUM_VALUE; | 139 | return ERR_INVALID_ENUM_VALUE; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | const auto& kernel = Core::System::GetInstance().Kernel(); | 142 | const auto* const current_process = system.Kernel().CurrentProcess(); |
| 143 | const auto* const current_process = kernel.CurrentProcess(); | ||
| 144 | ASSERT(current_process != nullptr); | 143 | ASSERT(current_process != nullptr); |
| 145 | 144 | ||
| 146 | const auto resource_limit_object = | 145 | const auto resource_limit_object = |
| @@ -160,7 +159,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_ty | |||
| 160 | } // Anonymous namespace | 159 | } // Anonymous namespace |
| 161 | 160 | ||
| 162 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | 161 | /// Set the process heap to a given Size. It can both extend and shrink the heap. |
| 163 | static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | 162 | static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { |
| 164 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); | 163 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); |
| 165 | 164 | ||
| 166 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. | 165 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. |
| @@ -175,7 +174,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | |||
| 175 | return ERR_INVALID_SIZE; | 174 | return ERR_INVALID_SIZE; |
| 176 | } | 175 | } |
| 177 | 176 | ||
| 178 | auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager(); | 177 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 179 | const auto alloc_result = vm_manager.SetHeapSize(heap_size); | 178 | const auto alloc_result = vm_manager.SetHeapSize(heap_size); |
| 180 | if (alloc_result.Failed()) { | 179 | if (alloc_result.Failed()) { |
| 181 | return alloc_result.Code(); | 180 | return alloc_result.Code(); |
| @@ -185,7 +184,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | |||
| 185 | return RESULT_SUCCESS; | 184 | return RESULT_SUCCESS; |
| 186 | } | 185 | } |
| 187 | 186 | ||
| 188 | static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | 187 | static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) { |
| 189 | LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); | 188 | LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); |
| 190 | 189 | ||
| 191 | if (!Common::Is4KBAligned(addr)) { | 190 | if (!Common::Is4KBAligned(addr)) { |
| @@ -217,7 +216,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | |||
| 217 | return ERR_INVALID_MEMORY_PERMISSIONS; | 216 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 218 | } | 217 | } |
| 219 | 218 | ||
| 220 | auto* const current_process = Core::CurrentProcess(); | 219 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 221 | auto& vm_manager = current_process->VMManager(); | 220 | auto& vm_manager = current_process->VMManager(); |
| 222 | 221 | ||
| 223 | if (!vm_manager.IsWithinAddressSpace(addr, size)) { | 222 | if (!vm_manager.IsWithinAddressSpace(addr, size)) { |
| @@ -242,7 +241,8 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | |||
| 242 | return vm_manager.ReprotectRange(addr, size, converted_permissions); | 241 | return vm_manager.ReprotectRange(addr, size, converted_permissions); |
| 243 | } | 242 | } |
| 244 | 243 | ||
| 245 | static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) { | 244 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, |
| 245 | u32 attribute) { | ||
| 246 | LOG_DEBUG(Kernel_SVC, | 246 | LOG_DEBUG(Kernel_SVC, |
| 247 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | 247 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, |
| 248 | size, mask, attribute); | 248 | size, mask, attribute); |
| @@ -280,7 +280,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr | |||
| 280 | return ERR_INVALID_COMBINATION; | 280 | return ERR_INVALID_COMBINATION; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 283 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 284 | if (!vm_manager.IsWithinAddressSpace(address, size)) { | 284 | if (!vm_manager.IsWithinAddressSpace(address, size)) { |
| 285 | LOG_ERROR(Kernel_SVC, | 285 | LOG_ERROR(Kernel_SVC, |
| 286 | "Given address (0x{:016X}) is outside the bounds of the address space.", address); | 286 | "Given address (0x{:016X}) is outside the bounds of the address space.", address); |
| @@ -291,11 +291,11 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr | |||
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | /// Maps a memory range into a different range. | 293 | /// Maps a memory range into a different range. |
| 294 | static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 294 | static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 295 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 295 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 296 | src_addr, size); | 296 | src_addr, size); |
| 297 | 297 | ||
| 298 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 298 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 299 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); | 299 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); |
| 300 | 300 | ||
| 301 | if (result.IsError()) { | 301 | if (result.IsError()) { |
| @@ -306,11 +306,11 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | /// Unmaps a region that was previously mapped with svcMapMemory | 308 | /// Unmaps a region that was previously mapped with svcMapMemory |
| 309 | static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 309 | static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 310 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 310 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 311 | src_addr, size); | 311 | src_addr, size); |
| 312 | 312 | ||
| 313 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 313 | auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); |
| 314 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); | 314 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); |
| 315 | 315 | ||
| 316 | if (result.IsError()) { | 316 | if (result.IsError()) { |
| @@ -321,7 +321,8 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | /// Connect to an OS service given the port name, returns the handle to the port to out | 323 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 324 | static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { | 324 | static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, |
| 325 | VAddr port_name_address) { | ||
| 325 | if (!Memory::IsValidVirtualAddress(port_name_address)) { | 326 | if (!Memory::IsValidVirtualAddress(port_name_address)) { |
| 326 | LOG_ERROR(Kernel_SVC, | 327 | LOG_ERROR(Kernel_SVC, |
| 327 | "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", | 328 | "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", |
| @@ -340,8 +341,8 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address | |||
| 340 | 341 | ||
| 341 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); | 342 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); |
| 342 | 343 | ||
| 343 | auto& kernel = Core::System::GetInstance().Kernel(); | 344 | auto& kernel = system.Kernel(); |
| 344 | auto it = kernel.FindNamedPort(port_name); | 345 | const auto it = kernel.FindNamedPort(port_name); |
| 345 | if (!kernel.IsValidNamedPort(it)) { | 346 | if (!kernel.IsValidNamedPort(it)) { |
| 346 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); | 347 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); |
| 347 | return ERR_NOT_FOUND; | 348 | return ERR_NOT_FOUND; |
| @@ -353,14 +354,14 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address | |||
| 353 | CASCADE_RESULT(client_session, client_port->Connect()); | 354 | CASCADE_RESULT(client_session, client_port->Connect()); |
| 354 | 355 | ||
| 355 | // Return the client session | 356 | // Return the client session |
| 356 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 357 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 357 | CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); | 358 | CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); |
| 358 | return RESULT_SUCCESS; | 359 | return RESULT_SUCCESS; |
| 359 | } | 360 | } |
| 360 | 361 | ||
| 361 | /// Makes a blocking IPC call to an OS service. | 362 | /// Makes a blocking IPC call to an OS service. |
| 362 | static ResultCode SendSyncRequest(Handle handle) { | 363 | static ResultCode SendSyncRequest(Core::System& system, Handle handle) { |
| 363 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 364 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 364 | SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); | 365 | SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); |
| 365 | if (!session) { | 366 | if (!session) { |
| 366 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); | 367 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); |
| @@ -369,18 +370,18 @@ static ResultCode SendSyncRequest(Handle handle) { | |||
| 369 | 370 | ||
| 370 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | 371 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); |
| 371 | 372 | ||
| 372 | Core::System::GetInstance().PrepareReschedule(); | 373 | system.PrepareReschedule(); |
| 373 | 374 | ||
| 374 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server | 375 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server |
| 375 | // responds and cause a reschedule. | 376 | // responds and cause a reschedule. |
| 376 | return session->SendSyncRequest(GetCurrentThread()); | 377 | return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread()); |
| 377 | } | 378 | } |
| 378 | 379 | ||
| 379 | /// Get the ID for the specified thread. | 380 | /// Get the ID for the specified thread. |
| 380 | static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { | 381 | static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) { |
| 381 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 382 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 382 | 383 | ||
| 383 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 384 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 384 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 385 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 385 | if (!thread) { | 386 | if (!thread) { |
| 386 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); | 387 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); |
| @@ -392,10 +393,10 @@ static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { | |||
| 392 | } | 393 | } |
| 393 | 394 | ||
| 394 | /// Gets the ID of the specified process or a specified thread's owning process. | 395 | /// Gets the ID of the specified process or a specified thread's owning process. |
| 395 | static ResultCode GetProcessId(u64* process_id, Handle handle) { | 396 | static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) { |
| 396 | LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); | 397 | LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); |
| 397 | 398 | ||
| 398 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 399 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 399 | const SharedPtr<Process> process = handle_table.Get<Process>(handle); | 400 | const SharedPtr<Process> process = handle_table.Get<Process>(handle); |
| 400 | if (process) { | 401 | if (process) { |
| 401 | *process_id = process->GetProcessID(); | 402 | *process_id = process->GetProcessID(); |
| @@ -437,8 +438,8 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr | |||
| 437 | }; | 438 | }; |
| 438 | 439 | ||
| 439 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 440 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 440 | static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, | 441 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, |
| 441 | s64 nano_seconds) { | 442 | u64 handle_count, s64 nano_seconds) { |
| 442 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", | 443 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", |
| 443 | handles_address, handle_count, nano_seconds); | 444 | handles_address, handle_count, nano_seconds); |
| 444 | 445 | ||
| @@ -457,11 +458,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 457 | return ERR_OUT_OF_RANGE; | 458 | return ERR_OUT_OF_RANGE; |
| 458 | } | 459 | } |
| 459 | 460 | ||
| 460 | auto* const thread = GetCurrentThread(); | 461 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); |
| 461 | 462 | ||
| 462 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; | 463 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; |
| 463 | Thread::ThreadWaitObjects objects(handle_count); | 464 | Thread::ThreadWaitObjects objects(handle_count); |
| 464 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 465 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 465 | 466 | ||
| 466 | for (u64 i = 0; i < handle_count; ++i) { | 467 | for (u64 i = 0; i < handle_count; ++i) { |
| 467 | const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); | 468 | const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); |
| @@ -507,16 +508,16 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 507 | thread->WakeAfterDelay(nano_seconds); | 508 | thread->WakeAfterDelay(nano_seconds); |
| 508 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); | 509 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); |
| 509 | 510 | ||
| 510 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 511 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 511 | 512 | ||
| 512 | return RESULT_TIMEOUT; | 513 | return RESULT_TIMEOUT; |
| 513 | } | 514 | } |
| 514 | 515 | ||
| 515 | /// Resumes a thread waiting on WaitSynchronization | 516 | /// Resumes a thread waiting on WaitSynchronization |
| 516 | static ResultCode CancelSynchronization(Handle thread_handle) { | 517 | static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) { |
| 517 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); | 518 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); |
| 518 | 519 | ||
| 519 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 520 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 520 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 521 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 521 | if (!thread) { | 522 | if (!thread) { |
| 522 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 523 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -531,8 +532,8 @@ static ResultCode CancelSynchronization(Handle thread_handle) { | |||
| 531 | } | 532 | } |
| 532 | 533 | ||
| 533 | /// Attempts to locks a mutex, creating it if it does not already exist | 534 | /// Attempts to locks a mutex, creating it if it does not already exist |
| 534 | static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | 535 | static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, |
| 535 | Handle requesting_thread_handle) { | 536 | VAddr mutex_addr, Handle requesting_thread_handle) { |
| 536 | LOG_TRACE(Kernel_SVC, | 537 | LOG_TRACE(Kernel_SVC, |
| 537 | "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " | 538 | "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " |
| 538 | "requesting_current_thread_handle=0x{:08X}", | 539 | "requesting_current_thread_handle=0x{:08X}", |
| @@ -549,13 +550,13 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 549 | return ERR_INVALID_ADDRESS; | 550 | return ERR_INVALID_ADDRESS; |
| 550 | } | 551 | } |
| 551 | 552 | ||
| 552 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 553 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 553 | return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, | 554 | return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, |
| 554 | requesting_thread_handle); | 555 | requesting_thread_handle); |
| 555 | } | 556 | } |
| 556 | 557 | ||
| 557 | /// Unlock a mutex | 558 | /// Unlock a mutex |
| 558 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | 559 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { |
| 559 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | 560 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); |
| 560 | 561 | ||
| 561 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { | 562 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { |
| @@ -569,7 +570,7 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | |||
| 569 | return ERR_INVALID_ADDRESS; | 570 | return ERR_INVALID_ADDRESS; |
| 570 | } | 571 | } |
| 571 | 572 | ||
| 572 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 573 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 573 | return current_process->GetMutex().Release(mutex_addr); | 574 | return current_process->GetMutex().Release(mutex_addr); |
| 574 | } | 575 | } |
| 575 | 576 | ||
| @@ -592,7 +593,7 @@ struct BreakReason { | |||
| 592 | }; | 593 | }; |
| 593 | 594 | ||
| 594 | /// Break program execution | 595 | /// Break program execution |
| 595 | static void Break(u32 reason, u64 info1, u64 info2) { | 596 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { |
| 596 | BreakReason break_reason{reason}; | 597 | BreakReason break_reason{reason}; |
| 597 | bool has_dumped_buffer{}; | 598 | bool has_dumped_buffer{}; |
| 598 | 599 | ||
| @@ -670,22 +671,24 @@ static void Break(u32 reason, u64 info1, u64 info2) { | |||
| 670 | Debug_Emulated, | 671 | Debug_Emulated, |
| 671 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 672 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| 672 | reason, info1, info2); | 673 | reason, info1, info2); |
| 674 | |||
| 673 | handle_debug_buffer(info1, info2); | 675 | handle_debug_buffer(info1, info2); |
| 674 | Core::System::GetInstance() | 676 | |
| 675 | .ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID())) | 677 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 676 | .LogBacktrace(); | 678 | const auto thread_processor_id = current_thread->GetProcessorID(); |
| 679 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); | ||
| 677 | ASSERT(false); | 680 | ASSERT(false); |
| 678 | 681 | ||
| 679 | Core::CurrentProcess()->PrepareForTermination(); | 682 | system.Kernel().CurrentProcess()->PrepareForTermination(); |
| 680 | 683 | ||
| 681 | // Kill the current thread | 684 | // Kill the current thread |
| 682 | GetCurrentThread()->Stop(); | 685 | current_thread->Stop(); |
| 683 | Core::System::GetInstance().PrepareReschedule(); | 686 | system.PrepareReschedule(); |
| 684 | } | 687 | } |
| 685 | } | 688 | } |
| 686 | 689 | ||
| 687 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 690 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 688 | static void OutputDebugString(VAddr address, u64 len) { | 691 | static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { |
| 689 | if (len == 0) { | 692 | if (len == 0) { |
| 690 | return; | 693 | return; |
| 691 | } | 694 | } |
| @@ -696,7 +699,8 @@ static void OutputDebugString(VAddr address, u64 len) { | |||
| 696 | } | 699 | } |
| 697 | 700 | ||
| 698 | /// Gets system/memory information for the current process | 701 | /// Gets system/memory information for the current process |
| 699 | static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { | 702 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, |
| 703 | u64 info_sub_id) { | ||
| 700 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | 704 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, |
| 701 | info_sub_id, handle); | 705 | info_sub_id, handle); |
| 702 | 706 | ||
| @@ -754,7 +758,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 754 | return ERR_INVALID_ENUM_VALUE; | 758 | return ERR_INVALID_ENUM_VALUE; |
| 755 | } | 759 | } |
| 756 | 760 | ||
| 757 | const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable(); | 761 | const auto& current_process_handle_table = |
| 762 | system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 758 | const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle)); | 763 | const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle)); |
| 759 | if (!process) { | 764 | if (!process) { |
| 760 | return ERR_INVALID_HANDLE; | 765 | return ERR_INVALID_HANDLE; |
| @@ -844,7 +849,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 844 | return ERR_INVALID_COMBINATION; | 849 | return ERR_INVALID_COMBINATION; |
| 845 | } | 850 | } |
| 846 | 851 | ||
| 847 | Process* const current_process = Core::CurrentProcess(); | 852 | Process* const current_process = system.Kernel().CurrentProcess(); |
| 848 | HandleTable& handle_table = current_process->GetHandleTable(); | 853 | HandleTable& handle_table = current_process->GetHandleTable(); |
| 849 | const auto resource_limit = current_process->GetResourceLimit(); | 854 | const auto resource_limit = current_process->GetResourceLimit(); |
| 850 | if (!resource_limit) { | 855 | if (!resource_limit) { |
| @@ -875,7 +880,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 875 | return ERR_INVALID_COMBINATION; | 880 | return ERR_INVALID_COMBINATION; |
| 876 | } | 881 | } |
| 877 | 882 | ||
| 878 | *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id); | 883 | *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); |
| 879 | return RESULT_SUCCESS; | 884 | return RESULT_SUCCESS; |
| 880 | 885 | ||
| 881 | case GetInfoType::PrivilegedProcessId: | 886 | case GetInfoType::PrivilegedProcessId: |
| @@ -892,15 +897,14 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 892 | return ERR_INVALID_COMBINATION; | 897 | return ERR_INVALID_COMBINATION; |
| 893 | } | 898 | } |
| 894 | 899 | ||
| 895 | const auto thread = | 900 | const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>( |
| 896 | Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); | 901 | static_cast<Handle>(handle)); |
| 897 | if (!thread) { | 902 | if (!thread) { |
| 898 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", | 903 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", |
| 899 | static_cast<Handle>(handle)); | 904 | static_cast<Handle>(handle)); |
| 900 | return ERR_INVALID_HANDLE; | 905 | return ERR_INVALID_HANDLE; |
| 901 | } | 906 | } |
| 902 | 907 | ||
| 903 | const auto& system = Core::System::GetInstance(); | ||
| 904 | const auto& core_timing = system.CoreTiming(); | 908 | const auto& core_timing = system.CoreTiming(); |
| 905 | const auto& scheduler = system.CurrentScheduler(); | 909 | const auto& scheduler = system.CurrentScheduler(); |
| 906 | const auto* const current_thread = scheduler.GetCurrentThread(); | 910 | const auto* const current_thread = scheduler.GetCurrentThread(); |
| @@ -927,13 +931,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 927 | } | 931 | } |
| 928 | 932 | ||
| 929 | /// Sets the thread activity | 933 | /// Sets the thread activity |
| 930 | static ResultCode SetThreadActivity(Handle handle, u32 activity) { | 934 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { |
| 931 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); | 935 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); |
| 932 | if (activity > static_cast<u32>(ThreadActivity::Paused)) { | 936 | if (activity > static_cast<u32>(ThreadActivity::Paused)) { |
| 933 | return ERR_INVALID_ENUM_VALUE; | 937 | return ERR_INVALID_ENUM_VALUE; |
| 934 | } | 938 | } |
| 935 | 939 | ||
| 936 | const auto* current_process = Core::CurrentProcess(); | 940 | const auto* current_process = system.Kernel().CurrentProcess(); |
| 937 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | 941 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 938 | if (!thread) { | 942 | if (!thread) { |
| 939 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 943 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| @@ -950,7 +954,7 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) { | |||
| 950 | return ERR_INVALID_HANDLE; | 954 | return ERR_INVALID_HANDLE; |
| 951 | } | 955 | } |
| 952 | 956 | ||
| 953 | if (thread == GetCurrentThread()) { | 957 | if (thread == system.CurrentScheduler().GetCurrentThread()) { |
| 954 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); | 958 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); |
| 955 | return ERR_BUSY; | 959 | return ERR_BUSY; |
| 956 | } | 960 | } |
| @@ -960,10 +964,10 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) { | |||
| 960 | } | 964 | } |
| 961 | 965 | ||
| 962 | /// Gets the thread context | 966 | /// Gets the thread context |
| 963 | static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | 967 | static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) { |
| 964 | LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); | 968 | LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); |
| 965 | 969 | ||
| 966 | const auto* current_process = Core::CurrentProcess(); | 970 | const auto* current_process = system.Kernel().CurrentProcess(); |
| 967 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | 971 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 968 | if (!thread) { | 972 | if (!thread) { |
| 969 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 973 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| @@ -980,7 +984,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 980 | return ERR_INVALID_HANDLE; | 984 | return ERR_INVALID_HANDLE; |
| 981 | } | 985 | } |
| 982 | 986 | ||
| 983 | if (thread == GetCurrentThread()) { | 987 | if (thread == system.CurrentScheduler().GetCurrentThread()) { |
| 984 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); | 988 | LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); |
| 985 | return ERR_BUSY; | 989 | return ERR_BUSY; |
| 986 | } | 990 | } |
| @@ -1001,10 +1005,10 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 1001 | } | 1005 | } |
| 1002 | 1006 | ||
| 1003 | /// Gets the priority for the specified thread | 1007 | /// Gets the priority for the specified thread |
| 1004 | static ResultCode GetThreadPriority(u32* priority, Handle handle) { | 1008 | static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { |
| 1005 | LOG_TRACE(Kernel_SVC, "called"); | 1009 | LOG_TRACE(Kernel_SVC, "called"); |
| 1006 | 1010 | ||
| 1007 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1011 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1008 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); | 1012 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); |
| 1009 | if (!thread) { | 1013 | if (!thread) { |
| 1010 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 1014 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| @@ -1016,7 +1020,7 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) { | |||
| 1016 | } | 1020 | } |
| 1017 | 1021 | ||
| 1018 | /// Sets the priority for the specified thread | 1022 | /// Sets the priority for the specified thread |
| 1019 | static ResultCode SetThreadPriority(Handle handle, u32 priority) { | 1023 | static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { |
| 1020 | LOG_TRACE(Kernel_SVC, "called"); | 1024 | LOG_TRACE(Kernel_SVC, "called"); |
| 1021 | 1025 | ||
| 1022 | if (priority > THREADPRIO_LOWEST) { | 1026 | if (priority > THREADPRIO_LOWEST) { |
| @@ -1027,7 +1031,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 1027 | return ERR_INVALID_THREAD_PRIORITY; | 1031 | return ERR_INVALID_THREAD_PRIORITY; |
| 1028 | } | 1032 | } |
| 1029 | 1033 | ||
| 1030 | const auto* const current_process = Core::CurrentProcess(); | 1034 | const auto* const current_process = system.Kernel().CurrentProcess(); |
| 1031 | 1035 | ||
| 1032 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | 1036 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 1033 | if (!thread) { | 1037 | if (!thread) { |
| @@ -1037,18 +1041,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 1037 | 1041 | ||
| 1038 | thread->SetPriority(priority); | 1042 | thread->SetPriority(priority); |
| 1039 | 1043 | ||
| 1040 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1044 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1041 | return RESULT_SUCCESS; | 1045 | return RESULT_SUCCESS; |
| 1042 | } | 1046 | } |
| 1043 | 1047 | ||
| 1044 | /// Get which CPU core is executing the current thread | 1048 | /// Get which CPU core is executing the current thread |
| 1045 | static u32 GetCurrentProcessorNumber() { | 1049 | static u32 GetCurrentProcessorNumber(Core::System& system) { |
| 1046 | LOG_TRACE(Kernel_SVC, "called"); | 1050 | LOG_TRACE(Kernel_SVC, "called"); |
| 1047 | return GetCurrentThread()->GetProcessorID(); | 1051 | return system.CurrentScheduler().GetCurrentThread()->GetProcessorID(); |
| 1048 | } | 1052 | } |
| 1049 | 1053 | ||
| 1050 | static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, | 1054 | static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, |
| 1051 | u32 permissions) { | 1055 | u64 size, u32 permissions) { |
| 1052 | LOG_TRACE(Kernel_SVC, | 1056 | LOG_TRACE(Kernel_SVC, |
| 1053 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | 1057 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", |
| 1054 | shared_memory_handle, addr, size, permissions); | 1058 | shared_memory_handle, addr, size, permissions); |
| @@ -1082,7 +1086,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 1082 | return ERR_INVALID_MEMORY_PERMISSIONS; | 1086 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 1083 | } | 1087 | } |
| 1084 | 1088 | ||
| 1085 | auto* const current_process = Core::CurrentProcess(); | 1089 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1086 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); | 1090 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); |
| 1087 | if (!shared_memory) { | 1091 | if (!shared_memory) { |
| 1088 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", | 1092 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", |
| @@ -1100,7 +1104,8 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 1100 | return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); | 1104 | return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); |
| 1101 | } | 1105 | } |
| 1102 | 1106 | ||
| 1103 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { | 1107 | static ResultCode UnmapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, |
| 1108 | u64 size) { | ||
| 1104 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", | 1109 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", |
| 1105 | shared_memory_handle, addr, size); | 1110 | shared_memory_handle, addr, size); |
| 1106 | 1111 | ||
| @@ -1125,7 +1130,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 1125 | return ERR_INVALID_ADDRESS_STATE; | 1130 | return ERR_INVALID_ADDRESS_STATE; |
| 1126 | } | 1131 | } |
| 1127 | 1132 | ||
| 1128 | auto* const current_process = Core::CurrentProcess(); | 1133 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1129 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); | 1134 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); |
| 1130 | if (!shared_memory) { | 1135 | if (!shared_memory) { |
| 1131 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", | 1136 | LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", |
| @@ -1143,10 +1148,11 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 1143 | return shared_memory->Unmap(*current_process, addr, size); | 1148 | return shared_memory->Unmap(*current_process, addr, size); |
| 1144 | } | 1149 | } |
| 1145 | 1150 | ||
| 1146 | static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, | 1151 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1147 | Handle process_handle, VAddr address) { | 1152 | VAddr page_info_address, Handle process_handle, |
| 1153 | VAddr address) { | ||
| 1148 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); | 1154 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); |
| 1149 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1155 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1150 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); | 1156 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); |
| 1151 | if (!process) { | 1157 | if (!process) { |
| 1152 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | 1158 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", |
| @@ -1172,20 +1178,20 @@ static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_ | |||
| 1172 | return RESULT_SUCCESS; | 1178 | return RESULT_SUCCESS; |
| 1173 | } | 1179 | } |
| 1174 | 1180 | ||
| 1175 | static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address, | 1181 | static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, |
| 1176 | VAddr query_address) { | 1182 | VAddr page_info_address, VAddr query_address) { |
| 1177 | LOG_TRACE(Kernel_SVC, | 1183 | LOG_TRACE(Kernel_SVC, |
| 1178 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " | 1184 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " |
| 1179 | "query_address=0x{:016X}", | 1185 | "query_address=0x{:016X}", |
| 1180 | memory_info_address, page_info_address, query_address); | 1186 | memory_info_address, page_info_address, query_address); |
| 1181 | 1187 | ||
| 1182 | return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess, | 1188 | return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, |
| 1183 | query_address); | 1189 | query_address); |
| 1184 | } | 1190 | } |
| 1185 | 1191 | ||
| 1186 | /// Exits the current process | 1192 | /// Exits the current process |
| 1187 | static void ExitProcess() { | 1193 | static void ExitProcess(Core::System& system) { |
| 1188 | auto* current_process = Core::CurrentProcess(); | 1194 | auto* current_process = system.Kernel().CurrentProcess(); |
| 1189 | 1195 | ||
| 1190 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); | 1196 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); |
| 1191 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, | 1197 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, |
| @@ -1194,20 +1200,20 @@ static void ExitProcess() { | |||
| 1194 | current_process->PrepareForTermination(); | 1200 | current_process->PrepareForTermination(); |
| 1195 | 1201 | ||
| 1196 | // Kill the current thread | 1202 | // Kill the current thread |
| 1197 | GetCurrentThread()->Stop(); | 1203 | system.CurrentScheduler().GetCurrentThread()->Stop(); |
| 1198 | 1204 | ||
| 1199 | Core::System::GetInstance().PrepareReschedule(); | 1205 | system.PrepareReschedule(); |
| 1200 | } | 1206 | } |
| 1201 | 1207 | ||
| 1202 | /// Creates a new thread | 1208 | /// Creates a new thread |
| 1203 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, | 1209 | static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, |
| 1204 | u32 priority, s32 processor_id) { | 1210 | VAddr stack_top, u32 priority, s32 processor_id) { |
| 1205 | LOG_TRACE(Kernel_SVC, | 1211 | LOG_TRACE(Kernel_SVC, |
| 1206 | "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " | 1212 | "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " |
| 1207 | "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", | 1213 | "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", |
| 1208 | entry_point, arg, stack_top, priority, processor_id, *out_handle); | 1214 | entry_point, arg, stack_top, priority, processor_id, *out_handle); |
| 1209 | 1215 | ||
| 1210 | auto* const current_process = Core::CurrentProcess(); | 1216 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1211 | 1217 | ||
| 1212 | if (processor_id == THREADPROCESSORID_IDEAL) { | 1218 | if (processor_id == THREADPROCESSORID_IDEAL) { |
| 1213 | // Set the target CPU to the one specified by the process. | 1219 | // Set the target CPU to the one specified by the process. |
| @@ -1239,7 +1245,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 1239 | } | 1245 | } |
| 1240 | 1246 | ||
| 1241 | const std::string name = fmt::format("thread-{:X}", entry_point); | 1247 | const std::string name = fmt::format("thread-{:X}", entry_point); |
| 1242 | auto& kernel = Core::System::GetInstance().Kernel(); | 1248 | auto& kernel = system.Kernel(); |
| 1243 | CASCADE_RESULT(SharedPtr<Thread> thread, | 1249 | CASCADE_RESULT(SharedPtr<Thread> thread, |
| 1244 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, | 1250 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, |
| 1245 | *current_process)); | 1251 | *current_process)); |
| @@ -1253,16 +1259,16 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 1253 | thread->SetGuestHandle(*new_guest_handle); | 1259 | thread->SetGuestHandle(*new_guest_handle); |
| 1254 | *out_handle = *new_guest_handle; | 1260 | *out_handle = *new_guest_handle; |
| 1255 | 1261 | ||
| 1256 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1262 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1257 | 1263 | ||
| 1258 | return RESULT_SUCCESS; | 1264 | return RESULT_SUCCESS; |
| 1259 | } | 1265 | } |
| 1260 | 1266 | ||
| 1261 | /// Starts the thread for the provided handle | 1267 | /// Starts the thread for the provided handle |
| 1262 | static ResultCode StartThread(Handle thread_handle) { | 1268 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { |
| 1263 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 1269 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 1264 | 1270 | ||
| 1265 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1271 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1266 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1272 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1267 | if (!thread) { | 1273 | if (!thread) { |
| 1268 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 1274 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -1275,16 +1281,14 @@ static ResultCode StartThread(Handle thread_handle) { | |||
| 1275 | thread->ResumeFromWait(); | 1281 | thread->ResumeFromWait(); |
| 1276 | 1282 | ||
| 1277 | if (thread->GetStatus() == ThreadStatus::Ready) { | 1283 | if (thread->GetStatus() == ThreadStatus::Ready) { |
| 1278 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1284 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1279 | } | 1285 | } |
| 1280 | 1286 | ||
| 1281 | return RESULT_SUCCESS; | 1287 | return RESULT_SUCCESS; |
| 1282 | } | 1288 | } |
| 1283 | 1289 | ||
| 1284 | /// Called when a thread exits | 1290 | /// Called when a thread exits |
| 1285 | static void ExitThread() { | 1291 | static void ExitThread(Core::System& system) { |
| 1286 | auto& system = Core::System::GetInstance(); | ||
| 1287 | |||
| 1288 | LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); | 1292 | LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); |
| 1289 | 1293 | ||
| 1290 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); | 1294 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| @@ -1294,7 +1298,7 @@ static void ExitThread() { | |||
| 1294 | } | 1298 | } |
| 1295 | 1299 | ||
| 1296 | /// Sleep the current thread | 1300 | /// Sleep the current thread |
| 1297 | static void SleepThread(s64 nanoseconds) { | 1301 | static void SleepThread(Core::System& system, s64 nanoseconds) { |
| 1298 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); | 1302 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); |
| 1299 | 1303 | ||
| 1300 | enum class SleepType : s64 { | 1304 | enum class SleepType : s64 { |
| @@ -1303,7 +1307,6 @@ static void SleepThread(s64 nanoseconds) { | |||
| 1303 | YieldAndWaitForLoadBalancing = -2, | 1307 | YieldAndWaitForLoadBalancing = -2, |
| 1304 | }; | 1308 | }; |
| 1305 | 1309 | ||
| 1306 | auto& system = Core::System::GetInstance(); | ||
| 1307 | auto& scheduler = system.CurrentScheduler(); | 1310 | auto& scheduler = system.CurrentScheduler(); |
| 1308 | auto* const current_thread = scheduler.GetCurrentThread(); | 1311 | auto* const current_thread = scheduler.GetCurrentThread(); |
| 1309 | 1312 | ||
| @@ -1332,8 +1335,9 @@ static void SleepThread(s64 nanoseconds) { | |||
| 1332 | } | 1335 | } |
| 1333 | 1336 | ||
| 1334 | /// Wait process wide key atomic | 1337 | /// Wait process wide key atomic |
| 1335 | static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, | 1338 | static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, |
| 1336 | Handle thread_handle, s64 nano_seconds) { | 1339 | VAddr condition_variable_addr, Handle thread_handle, |
| 1340 | s64 nano_seconds) { | ||
| 1337 | LOG_TRACE( | 1341 | LOG_TRACE( |
| 1338 | Kernel_SVC, | 1342 | Kernel_SVC, |
| 1339 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", | 1343 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", |
| @@ -1353,7 +1357,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 1353 | return ERR_INVALID_ADDRESS; | 1357 | return ERR_INVALID_ADDRESS; |
| 1354 | } | 1358 | } |
| 1355 | 1359 | ||
| 1356 | auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 1360 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1357 | const auto& handle_table = current_process->GetHandleTable(); | 1361 | const auto& handle_table = current_process->GetHandleTable(); |
| 1358 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1362 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1359 | ASSERT(thread); | 1363 | ASSERT(thread); |
| @@ -1363,7 +1367,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 1363 | return release_result; | 1367 | return release_result; |
| 1364 | } | 1368 | } |
| 1365 | 1369 | ||
| 1366 | SharedPtr<Thread> current_thread = GetCurrentThread(); | 1370 | SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 1367 | current_thread->SetCondVarWaitAddress(condition_variable_addr); | 1371 | current_thread->SetCondVarWaitAddress(condition_variable_addr); |
| 1368 | current_thread->SetMutexWaitAddress(mutex_addr); | 1372 | current_thread->SetMutexWaitAddress(mutex_addr); |
| 1369 | current_thread->SetWaitHandle(thread_handle); | 1373 | current_thread->SetWaitHandle(thread_handle); |
| @@ -1374,19 +1378,20 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 1374 | 1378 | ||
| 1375 | // Note: Deliberately don't attempt to inherit the lock owner's priority. | 1379 | // Note: Deliberately don't attempt to inherit the lock owner's priority. |
| 1376 | 1380 | ||
| 1377 | Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); | 1381 | system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); |
| 1378 | return RESULT_SUCCESS; | 1382 | return RESULT_SUCCESS; |
| 1379 | } | 1383 | } |
| 1380 | 1384 | ||
| 1381 | /// Signal process wide key | 1385 | /// Signal process wide key |
| 1382 | static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { | 1386 | static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, |
| 1387 | s32 target) { | ||
| 1383 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", | 1388 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", |
| 1384 | condition_variable_addr, target); | 1389 | condition_variable_addr, target); |
| 1385 | 1390 | ||
| 1386 | const auto RetrieveWaitingThreads = [](std::size_t core_index, | 1391 | const auto RetrieveWaitingThreads = [&system](std::size_t core_index, |
| 1387 | std::vector<SharedPtr<Thread>>& waiting_threads, | 1392 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 1388 | VAddr condvar_addr) { | 1393 | VAddr condvar_addr) { |
| 1389 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 1394 | const auto& scheduler = system.Scheduler(core_index); |
| 1390 | const auto& thread_list = scheduler.GetThreadList(); | 1395 | const auto& thread_list = scheduler.GetThreadList(); |
| 1391 | 1396 | ||
| 1392 | for (const auto& thread : thread_list) { | 1397 | for (const auto& thread : thread_list) { |
| @@ -1425,9 +1430,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1425 | // liberate Cond Var Thread. | 1430 | // liberate Cond Var Thread. |
| 1426 | thread->SetCondVarWaitAddress(0); | 1431 | thread->SetCondVarWaitAddress(0); |
| 1427 | 1432 | ||
| 1428 | std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); | 1433 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 1429 | 1434 | auto& monitor = system.Monitor(); | |
| 1430 | auto& monitor = Core::System::GetInstance().Monitor(); | ||
| 1431 | 1435 | ||
| 1432 | // Atomically read the value of the mutex. | 1436 | // Atomically read the value of the mutex. |
| 1433 | u32 mutex_val = 0; | 1437 | u32 mutex_val = 0; |
| @@ -1456,7 +1460,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1456 | thread->SetLockOwner(nullptr); | 1460 | thread->SetLockOwner(nullptr); |
| 1457 | thread->SetMutexWaitAddress(0); | 1461 | thread->SetMutexWaitAddress(0); |
| 1458 | thread->SetWaitHandle(0); | 1462 | thread->SetWaitHandle(0); |
| 1459 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); | 1463 | system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 1460 | } else { | 1464 | } else { |
| 1461 | // Atomically signal that the mutex now has a waiting thread. | 1465 | // Atomically signal that the mutex now has a waiting thread. |
| 1462 | do { | 1466 | do { |
| @@ -1472,7 +1476,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1472 | 1476 | ||
| 1473 | // The mutex is already owned by some other thread, make this thread wait on it. | 1477 | // The mutex is already owned by some other thread, make this thread wait on it. |
| 1474 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 1478 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 1475 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1479 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1476 | auto owner = handle_table.Get<Thread>(owner_handle); | 1480 | auto owner = handle_table.Get<Thread>(owner_handle); |
| 1477 | ASSERT(owner); | 1481 | ASSERT(owner); |
| 1478 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); | 1482 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); |
| @@ -1487,14 +1491,17 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 1487 | } | 1491 | } |
| 1488 | 1492 | ||
| 1489 | // Wait for an address (via Address Arbiter) | 1493 | // Wait for an address (via Address Arbiter) |
| 1490 | static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { | 1494 | static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, |
| 1495 | s64 timeout) { | ||
| 1491 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", | 1496 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", |
| 1492 | address, type, value, timeout); | 1497 | address, type, value, timeout); |
| 1498 | |||
| 1493 | // If the passed address is a kernel virtual address, return invalid memory state. | 1499 | // If the passed address is a kernel virtual address, return invalid memory state. |
| 1494 | if (Memory::IsKernelVirtualAddress(address)) { | 1500 | if (Memory::IsKernelVirtualAddress(address)) { |
| 1495 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | 1501 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); |
| 1496 | return ERR_INVALID_ADDRESS_STATE; | 1502 | return ERR_INVALID_ADDRESS_STATE; |
| 1497 | } | 1503 | } |
| 1504 | |||
| 1498 | // If the address is not properly aligned to 4 bytes, return invalid address. | 1505 | // If the address is not properly aligned to 4 bytes, return invalid address. |
| 1499 | if (!Common::IsWordAligned(address)) { | 1506 | if (!Common::IsWordAligned(address)) { |
| 1500 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); | 1507 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); |
| @@ -1502,20 +1509,22 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout | |||
| 1502 | } | 1509 | } |
| 1503 | 1510 | ||
| 1504 | const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); | 1511 | const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); |
| 1505 | auto& address_arbiter = | 1512 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); |
| 1506 | Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); | ||
| 1507 | return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); | 1513 | return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); |
| 1508 | } | 1514 | } |
| 1509 | 1515 | ||
| 1510 | // Signals to an address (via Address Arbiter) | 1516 | // Signals to an address (via Address Arbiter) |
| 1511 | static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) { | 1517 | static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, |
| 1518 | s32 num_to_wake) { | ||
| 1512 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", | 1519 | LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", |
| 1513 | address, type, value, num_to_wake); | 1520 | address, type, value, num_to_wake); |
| 1521 | |||
| 1514 | // If the passed address is a kernel virtual address, return invalid memory state. | 1522 | // If the passed address is a kernel virtual address, return invalid memory state. |
| 1515 | if (Memory::IsKernelVirtualAddress(address)) { | 1523 | if (Memory::IsKernelVirtualAddress(address)) { |
| 1516 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | 1524 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); |
| 1517 | return ERR_INVALID_ADDRESS_STATE; | 1525 | return ERR_INVALID_ADDRESS_STATE; |
| 1518 | } | 1526 | } |
| 1527 | |||
| 1519 | // If the address is not properly aligned to 4 bytes, return invalid address. | 1528 | // If the address is not properly aligned to 4 bytes, return invalid address. |
| 1520 | if (!Common::IsWordAligned(address)) { | 1529 | if (!Common::IsWordAligned(address)) { |
| 1521 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); | 1530 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); |
| @@ -1523,16 +1532,15 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to | |||
| 1523 | } | 1532 | } |
| 1524 | 1533 | ||
| 1525 | const auto signal_type = static_cast<AddressArbiter::SignalType>(type); | 1534 | const auto signal_type = static_cast<AddressArbiter::SignalType>(type); |
| 1526 | auto& address_arbiter = | 1535 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); |
| 1527 | Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); | ||
| 1528 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); | 1536 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); |
| 1529 | } | 1537 | } |
| 1530 | 1538 | ||
| 1531 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 1539 | /// This returns the total CPU ticks elapsed since the CPU was powered-on |
| 1532 | static u64 GetSystemTick() { | 1540 | static u64 GetSystemTick(Core::System& system) { |
| 1533 | LOG_TRACE(Kernel_SVC, "called"); | 1541 | LOG_TRACE(Kernel_SVC, "called"); |
| 1534 | 1542 | ||
| 1535 | auto& core_timing = Core::System::GetInstance().CoreTiming(); | 1543 | auto& core_timing = system.CoreTiming(); |
| 1536 | const u64 result{core_timing.GetTicks()}; | 1544 | const u64 result{core_timing.GetTicks()}; |
| 1537 | 1545 | ||
| 1538 | // Advance time to defeat dumb games that busy-wait for the frame to end. | 1546 | // Advance time to defeat dumb games that busy-wait for the frame to end. |
| @@ -1542,18 +1550,18 @@ static u64 GetSystemTick() { | |||
| 1542 | } | 1550 | } |
| 1543 | 1551 | ||
| 1544 | /// Close a handle | 1552 | /// Close a handle |
| 1545 | static ResultCode CloseHandle(Handle handle) { | 1553 | static ResultCode CloseHandle(Core::System& system, Handle handle) { |
| 1546 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | 1554 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); |
| 1547 | 1555 | ||
| 1548 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1556 | auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1549 | return handle_table.Close(handle); | 1557 | return handle_table.Close(handle); |
| 1550 | } | 1558 | } |
| 1551 | 1559 | ||
| 1552 | /// Clears the signaled state of an event or process. | 1560 | /// Clears the signaled state of an event or process. |
| 1553 | static ResultCode ResetSignal(Handle handle) { | 1561 | static ResultCode ResetSignal(Core::System& system, Handle handle) { |
| 1554 | LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); | 1562 | LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); |
| 1555 | 1563 | ||
| 1556 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1564 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1557 | 1565 | ||
| 1558 | auto event = handle_table.Get<ReadableEvent>(handle); | 1566 | auto event = handle_table.Get<ReadableEvent>(handle); |
| 1559 | if (event) { | 1567 | if (event) { |
| @@ -1570,7 +1578,8 @@ static ResultCode ResetSignal(Handle handle) { | |||
| 1570 | } | 1578 | } |
| 1571 | 1579 | ||
| 1572 | /// Creates a TransferMemory object | 1580 | /// Creates a TransferMemory object |
| 1573 | static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { | 1581 | static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, |
| 1582 | u32 permissions) { | ||
| 1574 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, | 1583 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, |
| 1575 | permissions); | 1584 | permissions); |
| 1576 | 1585 | ||
| @@ -1598,7 +1607,7 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 | |||
| 1598 | return ERR_INVALID_MEMORY_PERMISSIONS; | 1607 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 1599 | } | 1608 | } |
| 1600 | 1609 | ||
| 1601 | auto& kernel = Core::System::GetInstance().Kernel(); | 1610 | auto& kernel = system.Kernel(); |
| 1602 | auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); | 1611 | auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); |
| 1603 | 1612 | ||
| 1604 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | 1613 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| @@ -1611,7 +1620,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 | |||
| 1611 | return RESULT_SUCCESS; | 1620 | return RESULT_SUCCESS; |
| 1612 | } | 1621 | } |
| 1613 | 1622 | ||
| 1614 | static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) { | 1623 | static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size, |
| 1624 | u32 permission_raw) { | ||
| 1615 | LOG_DEBUG(Kernel_SVC, | 1625 | LOG_DEBUG(Kernel_SVC, |
| 1616 | "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", | 1626 | "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", |
| 1617 | handle, address, size, permission_raw); | 1627 | handle, address, size, permission_raw); |
| @@ -1645,7 +1655,7 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 | |||
| 1645 | return ERR_INVALID_STATE; | 1655 | return ERR_INVALID_STATE; |
| 1646 | } | 1656 | } |
| 1647 | 1657 | ||
| 1648 | const auto& kernel = Core::System::GetInstance().Kernel(); | 1658 | const auto& kernel = system.Kernel(); |
| 1649 | const auto* const current_process = kernel.CurrentProcess(); | 1659 | const auto* const current_process = kernel.CurrentProcess(); |
| 1650 | const auto& handle_table = current_process->GetHandleTable(); | 1660 | const auto& handle_table = current_process->GetHandleTable(); |
| 1651 | 1661 | ||
| @@ -1667,7 +1677,8 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 | |||
| 1667 | return transfer_memory->MapMemory(address, size, permissions); | 1677 | return transfer_memory->MapMemory(address, size, permissions); |
| 1668 | } | 1678 | } |
| 1669 | 1679 | ||
| 1670 | static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { | 1680 | static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address, |
| 1681 | u64 size) { | ||
| 1671 | LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, | 1682 | LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, |
| 1672 | address, size); | 1683 | address, size); |
| 1673 | 1684 | ||
| @@ -1692,7 +1703,7 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { | |||
| 1692 | return ERR_INVALID_ADDRESS_STATE; | 1703 | return ERR_INVALID_ADDRESS_STATE; |
| 1693 | } | 1704 | } |
| 1694 | 1705 | ||
| 1695 | const auto& kernel = Core::System::GetInstance().Kernel(); | 1706 | const auto& kernel = system.Kernel(); |
| 1696 | const auto* const current_process = kernel.CurrentProcess(); | 1707 | const auto* const current_process = kernel.CurrentProcess(); |
| 1697 | const auto& handle_table = current_process->GetHandleTable(); | 1708 | const auto& handle_table = current_process->GetHandleTable(); |
| 1698 | 1709 | ||
| @@ -1714,10 +1725,11 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { | |||
| 1714 | return transfer_memory->UnmapMemory(address, size); | 1725 | return transfer_memory->UnmapMemory(address, size); |
| 1715 | } | 1726 | } |
| 1716 | 1727 | ||
| 1717 | static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { | 1728 | static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, |
| 1729 | u64* mask) { | ||
| 1718 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | 1730 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); |
| 1719 | 1731 | ||
| 1720 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1732 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1721 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1733 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1722 | if (!thread) { | 1734 | if (!thread) { |
| 1723 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 1735 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -1731,11 +1743,12 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) | |||
| 1731 | return RESULT_SUCCESS; | 1743 | return RESULT_SUCCESS; |
| 1732 | } | 1744 | } |
| 1733 | 1745 | ||
| 1734 | static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | 1746 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, |
| 1747 | u64 mask) { | ||
| 1735 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, | 1748 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, |
| 1736 | mask, core); | 1749 | mask, core); |
| 1737 | 1750 | ||
| 1738 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1751 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1739 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1752 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1740 | if (!thread) { | 1753 | if (!thread) { |
| 1741 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 1754 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| @@ -1780,8 +1793,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | |||
| 1780 | return RESULT_SUCCESS; | 1793 | return RESULT_SUCCESS; |
| 1781 | } | 1794 | } |
| 1782 | 1795 | ||
| 1783 | static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, | 1796 | static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size, |
| 1784 | u32 remote_permissions) { | 1797 | u32 local_permissions, u32 remote_permissions) { |
| 1785 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, | 1798 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, |
| 1786 | local_permissions, remote_permissions); | 1799 | local_permissions, remote_permissions); |
| 1787 | if (size == 0) { | 1800 | if (size == 0) { |
| @@ -1817,7 +1830,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1817 | return ERR_INVALID_MEMORY_PERMISSIONS; | 1830 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 1818 | } | 1831 | } |
| 1819 | 1832 | ||
| 1820 | auto& kernel = Core::System::GetInstance().Kernel(); | 1833 | auto& kernel = system.Kernel(); |
| 1821 | auto process = kernel.CurrentProcess(); | 1834 | auto process = kernel.CurrentProcess(); |
| 1822 | auto& handle_table = process->GetHandleTable(); | 1835 | auto& handle_table = process->GetHandleTable(); |
| 1823 | auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); | 1836 | auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); |
| @@ -1826,10 +1839,10 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1826 | return RESULT_SUCCESS; | 1839 | return RESULT_SUCCESS; |
| 1827 | } | 1840 | } |
| 1828 | 1841 | ||
| 1829 | static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { | 1842 | static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { |
| 1830 | LOG_DEBUG(Kernel_SVC, "called"); | 1843 | LOG_DEBUG(Kernel_SVC, "called"); |
| 1831 | 1844 | ||
| 1832 | auto& kernel = Core::System::GetInstance().Kernel(); | 1845 | auto& kernel = system.Kernel(); |
| 1833 | const auto [readable_event, writable_event] = | 1846 | const auto [readable_event, writable_event] = |
| 1834 | WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); | 1847 | WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); |
| 1835 | 1848 | ||
| @@ -1854,10 +1867,10 @@ static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { | |||
| 1854 | return RESULT_SUCCESS; | 1867 | return RESULT_SUCCESS; |
| 1855 | } | 1868 | } |
| 1856 | 1869 | ||
| 1857 | static ResultCode ClearEvent(Handle handle) { | 1870 | static ResultCode ClearEvent(Core::System& system, Handle handle) { |
| 1858 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); | 1871 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); |
| 1859 | 1872 | ||
| 1860 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1873 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1861 | 1874 | ||
| 1862 | auto writable_event = handle_table.Get<WritableEvent>(handle); | 1875 | auto writable_event = handle_table.Get<WritableEvent>(handle); |
| 1863 | if (writable_event) { | 1876 | if (writable_event) { |
| @@ -1875,10 +1888,10 @@ static ResultCode ClearEvent(Handle handle) { | |||
| 1875 | return ERR_INVALID_HANDLE; | 1888 | return ERR_INVALID_HANDLE; |
| 1876 | } | 1889 | } |
| 1877 | 1890 | ||
| 1878 | static ResultCode SignalEvent(Handle handle) { | 1891 | static ResultCode SignalEvent(Core::System& system, Handle handle) { |
| 1879 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); | 1892 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); |
| 1880 | 1893 | ||
| 1881 | HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1894 | HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1882 | auto writable_event = handle_table.Get<WritableEvent>(handle); | 1895 | auto writable_event = handle_table.Get<WritableEvent>(handle); |
| 1883 | 1896 | ||
| 1884 | if (!writable_event) { | 1897 | if (!writable_event) { |
| @@ -1890,7 +1903,7 @@ static ResultCode SignalEvent(Handle handle) { | |||
| 1890 | return RESULT_SUCCESS; | 1903 | return RESULT_SUCCESS; |
| 1891 | } | 1904 | } |
| 1892 | 1905 | ||
| 1893 | static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | 1906 | static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { |
| 1894 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); | 1907 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); |
| 1895 | 1908 | ||
| 1896 | // This function currently only allows retrieving a process' status. | 1909 | // This function currently only allows retrieving a process' status. |
| @@ -1898,7 +1911,7 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | |||
| 1898 | Status, | 1911 | Status, |
| 1899 | }; | 1912 | }; |
| 1900 | 1913 | ||
| 1901 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1914 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1902 | const auto process = handle_table.Get<Process>(process_handle); | 1915 | const auto process = handle_table.Get<Process>(process_handle); |
| 1903 | if (!process) { | 1916 | if (!process) { |
| 1904 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | 1917 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", |
| @@ -1916,10 +1929,10 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | |||
| 1916 | return RESULT_SUCCESS; | 1929 | return RESULT_SUCCESS; |
| 1917 | } | 1930 | } |
| 1918 | 1931 | ||
| 1919 | static ResultCode CreateResourceLimit(Handle* out_handle) { | 1932 | static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { |
| 1920 | LOG_DEBUG(Kernel_SVC, "called"); | 1933 | LOG_DEBUG(Kernel_SVC, "called"); |
| 1921 | 1934 | ||
| 1922 | auto& kernel = Core::System::GetInstance().Kernel(); | 1935 | auto& kernel = system.Kernel(); |
| 1923 | auto resource_limit = ResourceLimit::Create(kernel); | 1936 | auto resource_limit = ResourceLimit::Create(kernel); |
| 1924 | 1937 | ||
| 1925 | auto* const current_process = kernel.CurrentProcess(); | 1938 | auto* const current_process = kernel.CurrentProcess(); |
| @@ -1934,11 +1947,11 @@ static ResultCode CreateResourceLimit(Handle* out_handle) { | |||
| 1934 | return RESULT_SUCCESS; | 1947 | return RESULT_SUCCESS; |
| 1935 | } | 1948 | } |
| 1936 | 1949 | ||
| 1937 | static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit, | 1950 | static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_value, |
| 1938 | u32 resource_type) { | 1951 | Handle resource_limit, u32 resource_type) { |
| 1939 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); | 1952 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); |
| 1940 | 1953 | ||
| 1941 | const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type, | 1954 | const auto limit_value = RetrieveResourceLimitValue(system, resource_limit, resource_type, |
| 1942 | ResourceLimitValueType::LimitValue); | 1955 | ResourceLimitValueType::LimitValue); |
| 1943 | if (limit_value.Failed()) { | 1956 | if (limit_value.Failed()) { |
| 1944 | return limit_value.Code(); | 1957 | return limit_value.Code(); |
| @@ -1948,11 +1961,11 @@ static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_lim | |||
| 1948 | return RESULT_SUCCESS; | 1961 | return RESULT_SUCCESS; |
| 1949 | } | 1962 | } |
| 1950 | 1963 | ||
| 1951 | static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit, | 1964 | static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_value, |
| 1952 | u32 resource_type) { | 1965 | Handle resource_limit, u32 resource_type) { |
| 1953 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); | 1966 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); |
| 1954 | 1967 | ||
| 1955 | const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type, | 1968 | const auto current_value = RetrieveResourceLimitValue(system, resource_limit, resource_type, |
| 1956 | ResourceLimitValueType::CurrentValue); | 1969 | ResourceLimitValueType::CurrentValue); |
| 1957 | if (current_value.Failed()) { | 1970 | if (current_value.Failed()) { |
| 1958 | return current_value.Code(); | 1971 | return current_value.Code(); |
| @@ -1962,7 +1975,8 @@ static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_l | |||
| 1962 | return RESULT_SUCCESS; | 1975 | return RESULT_SUCCESS; |
| 1963 | } | 1976 | } |
| 1964 | 1977 | ||
| 1965 | static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) { | 1978 | static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit, |
| 1979 | u32 resource_type, u64 value) { | ||
| 1966 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, | 1980 | LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, |
| 1967 | resource_type, value); | 1981 | resource_type, value); |
| 1968 | 1982 | ||
| @@ -1972,8 +1986,7 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource | |||
| 1972 | return ERR_INVALID_ENUM_VALUE; | 1986 | return ERR_INVALID_ENUM_VALUE; |
| 1973 | } | 1987 | } |
| 1974 | 1988 | ||
| 1975 | auto& kernel = Core::System::GetInstance().Kernel(); | 1989 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1976 | auto* const current_process = kernel.CurrentProcess(); | ||
| 1977 | ASSERT(current_process != nullptr); | 1990 | ASSERT(current_process != nullptr); |
| 1978 | 1991 | ||
| 1979 | auto resource_limit_object = | 1992 | auto resource_limit_object = |
| @@ -1997,8 +2010,8 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource | |||
| 1997 | return RESULT_SUCCESS; | 2010 | return RESULT_SUCCESS; |
| 1998 | } | 2011 | } |
| 1999 | 2012 | ||
| 2000 | static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, | 2013 | static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, |
| 2001 | u32 out_process_ids_size) { | 2014 | VAddr out_process_ids, u32 out_process_ids_size) { |
| 2002 | LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", | 2015 | LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", |
| 2003 | out_process_ids, out_process_ids_size); | 2016 | out_process_ids, out_process_ids_size); |
| 2004 | 2017 | ||
| @@ -2010,7 +2023,7 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, | |||
| 2010 | return ERR_OUT_OF_RANGE; | 2023 | return ERR_OUT_OF_RANGE; |
| 2011 | } | 2024 | } |
| 2012 | 2025 | ||
| 2013 | const auto& kernel = Core::System::GetInstance().Kernel(); | 2026 | const auto& kernel = system.Kernel(); |
| 2014 | const auto& vm_manager = kernel.CurrentProcess()->VMManager(); | 2027 | const auto& vm_manager = kernel.CurrentProcess()->VMManager(); |
| 2015 | const auto total_copy_size = out_process_ids_size * sizeof(u64); | 2028 | const auto total_copy_size = out_process_ids_size * sizeof(u64); |
| 2016 | 2029 | ||
| @@ -2034,8 +2047,8 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, | |||
| 2034 | return RESULT_SUCCESS; | 2047 | return RESULT_SUCCESS; |
| 2035 | } | 2048 | } |
| 2036 | 2049 | ||
| 2037 | ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size, | 2050 | ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, |
| 2038 | Handle debug_handle) { | 2051 | u32 out_thread_ids_size, Handle debug_handle) { |
| 2039 | // TODO: Handle this case when debug events are supported. | 2052 | // TODO: Handle this case when debug events are supported. |
| 2040 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); | 2053 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); |
| 2041 | 2054 | ||
| @@ -2049,7 +2062,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr | |||
| 2049 | return ERR_OUT_OF_RANGE; | 2062 | return ERR_OUT_OF_RANGE; |
| 2050 | } | 2063 | } |
| 2051 | 2064 | ||
| 2052 | const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); | 2065 | const auto* const current_process = system.Kernel().CurrentProcess(); |
| 2053 | const auto& vm_manager = current_process->VMManager(); | 2066 | const auto& vm_manager = current_process->VMManager(); |
| 2054 | const auto total_copy_size = out_thread_ids_size * sizeof(u64); | 2067 | const auto total_copy_size = out_thread_ids_size * sizeof(u64); |
| 2055 | 2068 | ||
| @@ -2076,7 +2089,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr | |||
| 2076 | 2089 | ||
| 2077 | namespace { | 2090 | namespace { |
| 2078 | struct FunctionDef { | 2091 | struct FunctionDef { |
| 2079 | using Func = void(); | 2092 | using Func = void(Core::System&); |
| 2080 | 2093 | ||
| 2081 | u32 id; | 2094 | u32 id; |
| 2082 | Func* func; | 2095 | Func* func; |
| @@ -2225,7 +2238,7 @@ static const FunctionDef* GetSVCInfo(u32 func_num) { | |||
| 2225 | 2238 | ||
| 2226 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | 2239 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); |
| 2227 | 2240 | ||
| 2228 | void CallSVC(u32 immediate) { | 2241 | void CallSVC(Core::System& system, u32 immediate) { |
| 2229 | MICROPROFILE_SCOPE(Kernel_SVC); | 2242 | MICROPROFILE_SCOPE(Kernel_SVC); |
| 2230 | 2243 | ||
| 2231 | // Lock the global kernel mutex when we enter the kernel HLE. | 2244 | // Lock the global kernel mutex when we enter the kernel HLE. |
| @@ -2234,7 +2247,7 @@ void CallSVC(u32 immediate) { | |||
| 2234 | const FunctionDef* info = GetSVCInfo(immediate); | 2247 | const FunctionDef* info = GetSVCInfo(immediate); |
| 2235 | if (info) { | 2248 | if (info) { |
| 2236 | if (info->func) { | 2249 | if (info->func) { |
| 2237 | info->func(); | 2250 | info->func(system); |
| 2238 | } else { | 2251 | } else { |
| 2239 | LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); | 2252 | LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); |
| 2240 | } | 2253 | } |
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index c37ae0f98..c5539ac1c 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h | |||
| @@ -6,8 +6,12 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Kernel { | 13 | namespace Kernel { |
| 10 | 14 | ||
| 11 | void CallSVC(u32 immediate); | 15 | void CallSVC(Core::System& system, u32 immediate); |
| 12 | 16 | ||
| 13 | } // namespace Kernel | 17 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index b3733680f..b3690b5f3 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -11,278 +11,312 @@ | |||
| 11 | 11 | ||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| 14 | static inline u64 Param(int n) { | 14 | static inline u64 Param(const Core::System& system, int n) { |
| 15 | return Core::CurrentArmInterface().GetReg(n); | 15 | return system.CurrentArmInterface().GetReg(n); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | * HLE a function return from the current ARM userland process | 19 | * HLE a function return from the current ARM userland process |
| 20 | * @param res Result to return | 20 | * @param system System context |
| 21 | * @param result Result to return | ||
| 21 | */ | 22 | */ |
| 22 | static inline void FuncReturn(u64 res) { | 23 | static inline void FuncReturn(Core::System& system, u64 result) { |
| 23 | Core::CurrentArmInterface().SetReg(0, res); | 24 | system.CurrentArmInterface().SetReg(0, result); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 27 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 27 | // Function wrappers that return type ResultCode | 28 | // Function wrappers that return type ResultCode |
| 28 | 29 | ||
| 29 | template <ResultCode func(u64)> | 30 | template <ResultCode func(Core::System&, u64)> |
| 30 | void SvcWrap() { | 31 | void SvcWrap(Core::System& system) { |
| 31 | FuncReturn(func(Param(0)).raw); | 32 | FuncReturn(system, func(system, Param(system, 0)).raw); |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | template <ResultCode func(u32)> | 35 | template <ResultCode func(Core::System&, u32)> |
| 35 | void SvcWrap() { | 36 | void SvcWrap(Core::System& system) { |
| 36 | FuncReturn(func(static_cast<u32>(Param(0))).raw); | 37 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); |
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | template <ResultCode func(u32, u32)> | 40 | template <ResultCode func(Core::System&, u32, u32)> |
| 40 | void SvcWrap() { | 41 | void SvcWrap(Core::System& system) { |
| 41 | FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw); | 42 | FuncReturn( |
| 43 | system, | ||
| 44 | func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); | ||
| 42 | } | 45 | } |
| 43 | 46 | ||
| 44 | template <ResultCode func(u32*)> | 47 | template <ResultCode func(Core::System&, u32*)> |
| 45 | void SvcWrap() { | 48 | void SvcWrap(Core::System& system) { |
| 46 | u32 param = 0; | 49 | u32 param = 0; |
| 47 | const u32 retval = func(¶m).raw; | 50 | const u32 retval = func(system, ¶m).raw; |
| 48 | Core::CurrentArmInterface().SetReg(1, param); | 51 | system.CurrentArmInterface().SetReg(1, param); |
| 49 | FuncReturn(retval); | 52 | FuncReturn(system, retval); |
| 50 | } | 53 | } |
| 51 | 54 | ||
| 52 | template <ResultCode func(u32*, u32)> | 55 | template <ResultCode func(Core::System&, u32*, u32)> |
| 53 | void SvcWrap() { | 56 | void SvcWrap(Core::System& system) { |
| 54 | u32 param_1 = 0; | 57 | u32 param_1 = 0; |
| 55 | u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw; | 58 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; |
| 56 | Core::CurrentArmInterface().SetReg(1, param_1); | 59 | system.CurrentArmInterface().SetReg(1, param_1); |
| 57 | FuncReturn(retval); | 60 | FuncReturn(system, retval); |
| 58 | } | 61 | } |
| 59 | 62 | ||
| 60 | template <ResultCode func(u32*, u32*)> | 63 | template <ResultCode func(Core::System&, u32*, u32*)> |
| 61 | void SvcWrap() { | 64 | void SvcWrap(Core::System& system) { |
| 62 | u32 param_1 = 0; | 65 | u32 param_1 = 0; |
| 63 | u32 param_2 = 0; | 66 | u32 param_2 = 0; |
| 64 | const u32 retval = func(¶m_1, ¶m_2).raw; | 67 | const u32 retval = func(system, ¶m_1, ¶m_2).raw; |
| 65 | 68 | ||
| 66 | auto& arm_interface = Core::CurrentArmInterface(); | 69 | auto& arm_interface = system.CurrentArmInterface(); |
| 67 | arm_interface.SetReg(1, param_1); | 70 | arm_interface.SetReg(1, param_1); |
| 68 | arm_interface.SetReg(2, param_2); | 71 | arm_interface.SetReg(2, param_2); |
| 69 | 72 | ||
| 70 | FuncReturn(retval); | 73 | FuncReturn(system, retval); |
| 71 | } | 74 | } |
| 72 | 75 | ||
| 73 | template <ResultCode func(u32*, u64)> | 76 | template <ResultCode func(Core::System&, u32*, u64)> |
| 74 | void SvcWrap() { | 77 | void SvcWrap(Core::System& system) { |
| 75 | u32 param_1 = 0; | 78 | u32 param_1 = 0; |
| 76 | const u32 retval = func(¶m_1, Param(1)).raw; | 79 | const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; |
| 77 | Core::CurrentArmInterface().SetReg(1, param_1); | 80 | system.CurrentArmInterface().SetReg(1, param_1); |
| 78 | FuncReturn(retval); | 81 | FuncReturn(system, retval); |
| 79 | } | 82 | } |
| 80 | 83 | ||
| 81 | template <ResultCode func(u32*, u64, u32)> | 84 | template <ResultCode func(Core::System&, u32*, u64, u32)> |
| 82 | void SvcWrap() { | 85 | void SvcWrap(Core::System& system) { |
| 83 | u32 param_1 = 0; | 86 | u32 param_1 = 0; |
| 84 | const u32 retval = func(¶m_1, Param(1), static_cast<u32>(Param(2))).raw; | 87 | const u32 retval = |
| 85 | Core::CurrentArmInterface().SetReg(1, param_1); | 88 | func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw; |
| 86 | FuncReturn(retval); | 89 | |
| 90 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 91 | FuncReturn(system, retval); | ||
| 87 | } | 92 | } |
| 88 | 93 | ||
| 89 | template <ResultCode func(u64*, u32)> | 94 | template <ResultCode func(Core::System&, u64*, u32)> |
| 90 | void SvcWrap() { | 95 | void SvcWrap(Core::System& system) { |
| 91 | u64 param_1 = 0; | 96 | u64 param_1 = 0; |
| 92 | const u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw; | 97 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; |
| 93 | Core::CurrentArmInterface().SetReg(1, param_1); | 98 | |
| 94 | FuncReturn(retval); | 99 | system.CurrentArmInterface().SetReg(1, param_1); |
| 100 | FuncReturn(system, retval); | ||
| 95 | } | 101 | } |
| 96 | 102 | ||
| 97 | template <ResultCode func(u64, s32)> | 103 | template <ResultCode func(Core::System&, u64, s32)> |
| 98 | void SvcWrap() { | 104 | void SvcWrap(Core::System& system) { |
| 99 | FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw); | 105 | FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw); |
| 100 | } | 106 | } |
| 101 | 107 | ||
| 102 | template <ResultCode func(u64, u32)> | 108 | template <ResultCode func(Core::System&, u64, u32)> |
| 103 | void SvcWrap() { | 109 | void SvcWrap(Core::System& system) { |
| 104 | FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw); | 110 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw); |
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | template <ResultCode func(u64*, u64)> | 113 | template <ResultCode func(Core::System&, u64*, u64)> |
| 108 | void SvcWrap() { | 114 | void SvcWrap(Core::System& system) { |
| 109 | u64 param_1 = 0; | 115 | u64 param_1 = 0; |
| 110 | u32 retval = func(¶m_1, Param(1)).raw; | 116 | const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; |
| 111 | Core::CurrentArmInterface().SetReg(1, param_1); | 117 | |
| 112 | FuncReturn(retval); | 118 | system.CurrentArmInterface().SetReg(1, param_1); |
| 119 | FuncReturn(system, retval); | ||
| 113 | } | 120 | } |
| 114 | 121 | ||
| 115 | template <ResultCode func(u64*, u32, u32)> | 122 | template <ResultCode func(Core::System&, u64*, u32, u32)> |
| 116 | void SvcWrap() { | 123 | void SvcWrap(Core::System& system) { |
| 117 | u64 param_1 = 0; | 124 | u64 param_1 = 0; |
| 118 | u32 retval = func(¶m_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw; | 125 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)), |
| 119 | Core::CurrentArmInterface().SetReg(1, param_1); | 126 | static_cast<u32>(Param(system, 2))) |
| 120 | FuncReturn(retval); | 127 | .raw; |
| 128 | |||
| 129 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 130 | FuncReturn(system, retval); | ||
| 121 | } | 131 | } |
| 122 | 132 | ||
| 123 | template <ResultCode func(u32, u64)> | 133 | template <ResultCode func(Core::System&, u32, u64)> |
| 124 | void SvcWrap() { | 134 | void SvcWrap(Core::System& system) { |
| 125 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); | 135 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw); |
| 126 | } | 136 | } |
| 127 | 137 | ||
| 128 | template <ResultCode func(u32, u32, u64)> | 138 | template <ResultCode func(Core::System&, u32, u32, u64)> |
| 129 | void SvcWrap() { | 139 | void SvcWrap(Core::System& system) { |
| 130 | FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw); | 140 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), |
| 141 | static_cast<u32>(Param(system, 1)), Param(system, 2)) | ||
| 142 | .raw); | ||
| 131 | } | 143 | } |
| 132 | 144 | ||
| 133 | template <ResultCode func(u32, u32*, u64*)> | 145 | template <ResultCode func(Core::System&, u32, u32*, u64*)> |
| 134 | void SvcWrap() { | 146 | void SvcWrap(Core::System& system) { |
| 135 | u32 param_1 = 0; | 147 | u32 param_1 = 0; |
| 136 | u64 param_2 = 0; | 148 | u64 param_2 = 0; |
| 137 | ResultCode retval = func(static_cast<u32>(Param(2)), ¶m_1, ¶m_2); | 149 | const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); |
| 138 | Core::CurrentArmInterface().SetReg(1, param_1); | ||
| 139 | Core::CurrentArmInterface().SetReg(2, param_2); | ||
| 140 | FuncReturn(retval.raw); | ||
| 141 | } | ||
| 142 | 150 | ||
| 143 | template <ResultCode func(u64, u64, u32, u32)> | 151 | system.CurrentArmInterface().SetReg(1, param_1); |
| 144 | void SvcWrap() { | 152 | system.CurrentArmInterface().SetReg(2, param_2); |
| 145 | FuncReturn( | 153 | FuncReturn(system, retval.raw); |
| 146 | func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw); | ||
| 147 | } | 154 | } |
| 148 | 155 | ||
| 149 | template <ResultCode func(u64, u64, u32, u64)> | 156 | template <ResultCode func(Core::System&, u64, u64, u32, u32)> |
| 150 | void SvcWrap() { | 157 | void SvcWrap(Core::System& system) { |
| 151 | FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw); | 158 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), |
| 159 | static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) | ||
| 160 | .raw); | ||
| 152 | } | 161 | } |
| 153 | 162 | ||
| 154 | template <ResultCode func(u32, u64, u32)> | 163 | template <ResultCode func(Core::System&, u64, u64, u32, u64)> |
| 155 | void SvcWrap() { | 164 | void SvcWrap(Core::System& system) { |
| 156 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw); | 165 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), |
| 166 | static_cast<u32>(Param(system, 2)), Param(system, 3)) | ||
| 167 | .raw); | ||
| 157 | } | 168 | } |
| 158 | 169 | ||
| 159 | template <ResultCode func(u64, u64, u64)> | 170 | template <ResultCode func(Core::System&, u32, u64, u32)> |
| 160 | void SvcWrap() { | 171 | void SvcWrap(Core::System& system) { |
| 161 | FuncReturn(func(Param(0), Param(1), Param(2)).raw); | 172 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), |
| 173 | static_cast<u32>(Param(system, 2))) | ||
| 174 | .raw); | ||
| 162 | } | 175 | } |
| 163 | 176 | ||
| 164 | template <ResultCode func(u64, u64, u32)> | 177 | template <ResultCode func(Core::System&, u64, u64, u64)> |
| 165 | void SvcWrap() { | 178 | void SvcWrap(Core::System& system) { |
| 166 | FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw); | 179 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw); |
| 167 | } | 180 | } |
| 168 | 181 | ||
| 169 | template <ResultCode func(u32, u64, u64, u32)> | 182 | template <ResultCode func(Core::System&, u64, u64, u32)> |
| 170 | void SvcWrap() { | 183 | void SvcWrap(Core::System& system) { |
| 171 | FuncReturn( | 184 | FuncReturn( |
| 172 | func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw); | 185 | system, |
| 186 | func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw); | ||
| 187 | } | ||
| 188 | |||
| 189 | template <ResultCode func(Core::System&, u32, u64, u64, u32)> | ||
| 190 | void SvcWrap(Core::System& system) { | ||
| 191 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), | ||
| 192 | Param(system, 2), static_cast<u32>(Param(system, 3))) | ||
| 193 | .raw); | ||
| 173 | } | 194 | } |
| 174 | 195 | ||
| 175 | template <ResultCode func(u32, u64, u64)> | 196 | template <ResultCode func(Core::System&, u32, u64, u64)> |
| 176 | void SvcWrap() { | 197 | void SvcWrap(Core::System& system) { |
| 177 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw); | 198 | FuncReturn( |
| 199 | system, | ||
| 200 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); | ||
| 178 | } | 201 | } |
| 179 | 202 | ||
| 180 | template <ResultCode func(u32*, u64, u64, s64)> | 203 | template <ResultCode func(Core::System&, u32*, u64, u64, s64)> |
| 181 | void SvcWrap() { | 204 | void SvcWrap(Core::System& system) { |
| 182 | u32 param_1 = 0; | 205 | u32 param_1 = 0; |
| 183 | ResultCode retval = | 206 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), |
| 184 | func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))); | 207 | static_cast<s64>(Param(system, 3))) |
| 185 | Core::CurrentArmInterface().SetReg(1, param_1); | 208 | .raw; |
| 186 | FuncReturn(retval.raw); | 209 | |
| 210 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 211 | FuncReturn(system, retval); | ||
| 187 | } | 212 | } |
| 188 | 213 | ||
| 189 | template <ResultCode func(u64, u64, u32, s64)> | 214 | template <ResultCode func(Core::System&, u64, u64, u32, s64)> |
| 190 | void SvcWrap() { | 215 | void SvcWrap(Core::System& system) { |
| 191 | FuncReturn( | 216 | FuncReturn(system, func(system, Param(system, 0), Param(system, 1), |
| 192 | func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw); | 217 | static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) |
| 218 | .raw); | ||
| 193 | } | 219 | } |
| 194 | 220 | ||
| 195 | template <ResultCode func(u64*, u64, u64, u64)> | 221 | template <ResultCode func(Core::System&, u64*, u64, u64, u64)> |
| 196 | void SvcWrap() { | 222 | void SvcWrap(Core::System& system) { |
| 197 | u64 param_1 = 0; | 223 | u64 param_1 = 0; |
| 198 | u32 retval = func(¶m_1, Param(1), Param(2), Param(3)).raw; | 224 | const u32 retval = |
| 199 | Core::CurrentArmInterface().SetReg(1, param_1); | 225 | func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw; |
| 200 | FuncReturn(retval); | 226 | |
| 227 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 228 | FuncReturn(system, retval); | ||
| 201 | } | 229 | } |
| 202 | 230 | ||
| 203 | template <ResultCode func(u32*, u64, u64, u64, u32, s32)> | 231 | template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> |
| 204 | void SvcWrap() { | 232 | void SvcWrap(Core::System& system) { |
| 205 | u32 param_1 = 0; | 233 | u32 param_1 = 0; |
| 206 | u32 retval = func(¶m_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)), | 234 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3), |
| 207 | static_cast<s32>(Param(5))) | 235 | static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5))) |
| 208 | .raw; | 236 | .raw; |
| 209 | Core::CurrentArmInterface().SetReg(1, param_1); | 237 | |
| 210 | FuncReturn(retval); | 238 | system.CurrentArmInterface().SetReg(1, param_1); |
| 239 | FuncReturn(system, retval); | ||
| 211 | } | 240 | } |
| 212 | 241 | ||
| 213 | template <ResultCode func(u32*, u64, u64, u32)> | 242 | template <ResultCode func(Core::System&, u32*, u64, u64, u32)> |
| 214 | void SvcWrap() { | 243 | void SvcWrap(Core::System& system) { |
| 215 | u32 param_1 = 0; | 244 | u32 param_1 = 0; |
| 216 | u32 retval = func(¶m_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw; | 245 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), |
| 217 | Core::CurrentArmInterface().SetReg(1, param_1); | 246 | static_cast<u32>(Param(system, 3))) |
| 218 | FuncReturn(retval); | 247 | .raw; |
| 248 | |||
| 249 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 250 | FuncReturn(system, retval); | ||
| 219 | } | 251 | } |
| 220 | 252 | ||
| 221 | template <ResultCode func(Handle*, u64, u32, u32)> | 253 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> |
| 222 | void SvcWrap() { | 254 | void SvcWrap(Core::System& system) { |
| 223 | u32 param_1 = 0; | 255 | u32 param_1 = 0; |
| 224 | u32 retval = | 256 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), |
| 225 | func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw; | 257 | static_cast<u32>(Param(system, 3))) |
| 226 | Core::CurrentArmInterface().SetReg(1, param_1); | 258 | .raw; |
| 227 | FuncReturn(retval); | 259 | |
| 260 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 261 | FuncReturn(system, retval); | ||
| 228 | } | 262 | } |
| 229 | 263 | ||
| 230 | template <ResultCode func(u64, u32, s32, s64)> | 264 | template <ResultCode func(Core::System&, u64, u32, s32, s64)> |
| 231 | void SvcWrap() { | 265 | void SvcWrap(Core::System& system) { |
| 232 | FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), | 266 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), |
| 233 | static_cast<s64>(Param(3))) | 267 | static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) |
| 234 | .raw); | 268 | .raw); |
| 235 | } | 269 | } |
| 236 | 270 | ||
| 237 | template <ResultCode func(u64, u32, s32, s32)> | 271 | template <ResultCode func(Core::System&, u64, u32, s32, s32)> |
| 238 | void SvcWrap() { | 272 | void SvcWrap(Core::System& system) { |
| 239 | FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), | 273 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), |
| 240 | static_cast<s32>(Param(3))) | 274 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) |
| 241 | .raw); | 275 | .raw); |
| 242 | } | 276 | } |
| 243 | 277 | ||
| 244 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 278 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 245 | // Function wrappers that return type u32 | 279 | // Function wrappers that return type u32 |
| 246 | 280 | ||
| 247 | template <u32 func()> | 281 | template <u32 func(Core::System&)> |
| 248 | void SvcWrap() { | 282 | void SvcWrap(Core::System& system) { |
| 249 | FuncReturn(func()); | 283 | FuncReturn(system, func(system)); |
| 250 | } | 284 | } |
| 251 | 285 | ||
| 252 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 286 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 253 | // Function wrappers that return type u64 | 287 | // Function wrappers that return type u64 |
| 254 | 288 | ||
| 255 | template <u64 func()> | 289 | template <u64 func(Core::System&)> |
| 256 | void SvcWrap() { | 290 | void SvcWrap(Core::System& system) { |
| 257 | FuncReturn(func()); | 291 | FuncReturn(system, func(system)); |
| 258 | } | 292 | } |
| 259 | 293 | ||
| 260 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 294 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 261 | /// Function wrappers that return type void | 295 | /// Function wrappers that return type void |
| 262 | 296 | ||
| 263 | template <void func()> | 297 | template <void func(Core::System&)> |
| 264 | void SvcWrap() { | 298 | void SvcWrap(Core::System& system) { |
| 265 | func(); | 299 | func(system); |
| 266 | } | 300 | } |
| 267 | 301 | ||
| 268 | template <void func(s64)> | 302 | template <void func(Core::System&, s64)> |
| 269 | void SvcWrap() { | 303 | void SvcWrap(Core::System& system) { |
| 270 | func(static_cast<s64>(Param(0))); | 304 | func(system, static_cast<s64>(Param(system, 0))); |
| 271 | } | 305 | } |
| 272 | 306 | ||
| 273 | template <void func(u64, u64 len)> | 307 | template <void func(Core::System&, u64, u64)> |
| 274 | void SvcWrap() { | 308 | void SvcWrap(Core::System& system) { |
| 275 | func(Param(0), Param(1)); | 309 | func(system, Param(system, 0), Param(system, 1)); |
| 276 | } | 310 | } |
| 277 | 311 | ||
| 278 | template <void func(u64, u64, u64)> | 312 | template <void func(Core::System&, u64, u64, u64)> |
| 279 | void SvcWrap() { | 313 | void SvcWrap(Core::System& system) { |
| 280 | func(Param(0), Param(1), Param(2)); | 314 | func(system, Param(system, 0), Param(system, 1), Param(system, 2)); |
| 281 | } | 315 | } |
| 282 | 316 | ||
| 283 | template <void func(u32, u64, u64)> | 317 | template <void func(Core::System&, u32, u64, u64)> |
| 284 | void SvcWrap() { | 318 | void SvcWrap(Core::System& system) { |
| 285 | func(static_cast<u32>(Param(0)), Param(1), Param(2)); | 319 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); |
| 286 | } | 320 | } |
| 287 | 321 | ||
| 288 | } // namespace Kernel | 322 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 73e5d1bb4..83c83e45a 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -106,7 +106,7 @@ public: | |||
| 106 | return "Thread"; | 106 | return "Thread"; |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | static const HandleType HANDLE_TYPE = HandleType::Thread; | 109 | static constexpr HandleType HANDLE_TYPE = HandleType::Thread; |
| 110 | HandleType GetHandleType() const override { | 110 | HandleType GetHandleType() const override { |
| 111 | return HANDLE_TYPE; | 111 | return HANDLE_TYPE; |
| 112 | } | 112 | } |
diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h index c9068dd3d..d00c92a6b 100644 --- a/src/core/hle/kernel/writable_event.h +++ b/src/core/hle/kernel/writable_event.h | |||
| @@ -37,7 +37,7 @@ public: | |||
| 37 | return name; | 37 | return name; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | static const HandleType HANDLE_TYPE = HandleType::WritableEvent; | 40 | static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent; |
| 41 | HandleType GetHandleType() const override { | 41 | HandleType GetHandleType() const override { |
| 42 | return HANDLE_TYPE; | 42 | return HANDLE_TYPE; |
| 43 | } | 43 | } |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 242a0d1cd..114bed20d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -106,6 +106,8 @@ add_library(video_core STATIC | |||
| 106 | textures/decoders.cpp | 106 | textures/decoders.cpp |
| 107 | textures/decoders.h | 107 | textures/decoders.h |
| 108 | textures/texture.h | 108 | textures/texture.h |
| 109 | texture_cache.cpp | ||
| 110 | texture_cache.h | ||
| 109 | video_core.cpp | 111 | video_core.cpp |
| 110 | video_core.h | 112 | video_core.h |
| 111 | ) | 113 | ) |
| @@ -127,12 +129,14 @@ if (ENABLE_VULKAN) | |||
| 127 | renderer_vulkan/vk_sampler_cache.h | 129 | renderer_vulkan/vk_sampler_cache.h |
| 128 | renderer_vulkan/vk_scheduler.cpp | 130 | renderer_vulkan/vk_scheduler.cpp |
| 129 | renderer_vulkan/vk_scheduler.h | 131 | renderer_vulkan/vk_scheduler.h |
| 132 | renderer_vulkan/vk_shader_decompiler.cpp | ||
| 133 | renderer_vulkan/vk_shader_decompiler.h | ||
| 130 | renderer_vulkan/vk_stream_buffer.cpp | 134 | renderer_vulkan/vk_stream_buffer.cpp |
| 131 | renderer_vulkan/vk_stream_buffer.h | 135 | renderer_vulkan/vk_stream_buffer.h |
| 132 | renderer_vulkan/vk_swapchain.cpp | 136 | renderer_vulkan/vk_swapchain.cpp |
| 133 | renderer_vulkan/vk_swapchain.h) | 137 | renderer_vulkan/vk_swapchain.h) |
| 134 | 138 | ||
| 135 | target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) | 139 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) |
| 136 | target_compile_definitions(video_core PRIVATE HAS_VULKAN) | 140 | target_compile_definitions(video_core PRIVATE HAS_VULKAN) |
| 137 | endif() | 141 | endif() |
| 138 | 142 | ||
| @@ -140,3 +144,6 @@ create_target_directory_groups(video_core) | |||
| 140 | 144 | ||
| 141 | target_link_libraries(video_core PUBLIC common core) | 145 | target_link_libraries(video_core PUBLIC common core) |
| 142 | target_link_libraries(video_core PRIVATE glad) | 146 | target_link_libraries(video_core PRIVATE glad) |
| 147 | if (ENABLE_VULKAN) | ||
| 148 | target_link_libraries(video_core PRIVATE sirit) | ||
| 149 | endif() | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7ff1e6737..d250d5cbb 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -299,6 +299,10 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 299 | BaseBindings base_bindings; | 299 | BaseBindings base_bindings; |
| 300 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | 300 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; |
| 301 | 301 | ||
| 302 | // Prepare packed bindings | ||
| 303 | bind_ubo_pushbuffer.Setup(base_bindings.cbuf); | ||
| 304 | bind_ssbo_pushbuffer.Setup(base_bindings.gmem); | ||
| 305 | |||
| 302 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | 306 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
| 303 | const auto& shader_config = gpu.regs.shader_config[index]; | 307 | const auto& shader_config = gpu.regs.shader_config[index]; |
| 304 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; | 308 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; |
| @@ -321,8 +325,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 321 | &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); | 325 | &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); |
| 322 | 326 | ||
| 323 | // Bind the emulation info buffer | 327 | // Bind the emulation info buffer |
| 324 | glBindBufferRange(GL_UNIFORM_BUFFER, base_bindings.cbuf, buffer_cache.GetHandle(), offset, | 328 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, |
| 325 | static_cast<GLsizeiptr>(sizeof(ubo))); | 329 | static_cast<GLsizeiptr>(sizeof(ubo))); |
| 326 | 330 | ||
| 327 | Shader shader{shader_cache.GetStageProgram(program)}; | 331 | Shader shader{shader_cache.GetStageProgram(program)}; |
| 328 | const auto [program_handle, next_bindings] = | 332 | const auto [program_handle, next_bindings] = |
| @@ -366,6 +370,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 366 | base_bindings = next_bindings; | 370 | base_bindings = next_bindings; |
| 367 | } | 371 | } |
| 368 | 372 | ||
| 373 | bind_ubo_pushbuffer.Bind(); | ||
| 374 | bind_ssbo_pushbuffer.Bind(); | ||
| 375 | |||
| 369 | SyncClipEnabled(clip_distances); | 376 | SyncClipEnabled(clip_distances); |
| 370 | 377 | ||
| 371 | gpu.dirty_flags.shaders = false; | 378 | gpu.dirty_flags.shaders = false; |
| @@ -900,23 +907,14 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader | |||
| 900 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; | 907 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; |
| 901 | const auto& entries = shader->GetShaderEntries().const_buffers; | 908 | const auto& entries = shader->GetShaderEntries().const_buffers; |
| 902 | 909 | ||
| 903 | constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers; | ||
| 904 | std::array<GLuint, max_binds> bind_buffers; | ||
| 905 | std::array<GLintptr, max_binds> bind_offsets; | ||
| 906 | std::array<GLsizeiptr, max_binds> bind_sizes; | ||
| 907 | |||
| 908 | ASSERT_MSG(entries.size() <= max_binds, "Exceeded expected number of binding points."); | ||
| 909 | |||
| 910 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage | 910 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage |
| 911 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | 911 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { |
| 912 | const auto& used_buffer = entries[bindpoint]; | 912 | const auto& used_buffer = entries[bindpoint]; |
| 913 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; | 913 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; |
| 914 | 914 | ||
| 915 | if (!buffer.enabled) { | 915 | if (!buffer.enabled) { |
| 916 | // With disabled buffers set values as zero to unbind them | 916 | // Set values to zero to unbind buffers |
| 917 | bind_buffers[bindpoint] = 0; | 917 | bind_ubo_pushbuffer.Push(0, 0, 0); |
| 918 | bind_offsets[bindpoint] = 0; | ||
| 919 | bind_sizes[bindpoint] = 0; | ||
| 920 | continue; | 918 | continue; |
| 921 | } | 919 | } |
| 922 | 920 | ||
| @@ -944,30 +942,19 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader | |||
| 944 | const GLintptr const_buffer_offset = buffer_cache.UploadMemory( | 942 | const GLintptr const_buffer_offset = buffer_cache.UploadMemory( |
| 945 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); | 943 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); |
| 946 | 944 | ||
| 947 | // Prepare values for multibind | 945 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size); |
| 948 | bind_buffers[bindpoint] = buffer_cache.GetHandle(); | ||
| 949 | bind_offsets[bindpoint] = const_buffer_offset; | ||
| 950 | bind_sizes[bindpoint] = size; | ||
| 951 | } | 946 | } |
| 952 | |||
| 953 | // The first binding is reserved for emulation values | ||
| 954 | const GLuint ubo_base_binding = base_bindings.cbuf + 1; | ||
| 955 | glBindBuffersRange(GL_UNIFORM_BUFFER, ubo_base_binding, static_cast<GLsizei>(entries.size()), | ||
| 956 | bind_buffers.data(), bind_offsets.data(), bind_sizes.data()); | ||
| 957 | } | 947 | } |
| 958 | 948 | ||
| 959 | void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, | 949 | void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, |
| 960 | const Shader& shader, GLenum primitive_mode, | 950 | const Shader& shader, GLenum primitive_mode, |
| 961 | BaseBindings base_bindings) { | 951 | BaseBindings base_bindings) { |
| 962 | // TODO(Rodrigo): Use ARB_multi_bind here | ||
| 963 | const auto& entries = shader->GetShaderEntries().global_memory_entries; | 952 | const auto& entries = shader->GetShaderEntries().global_memory_entries; |
| 964 | 953 | for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | |
| 965 | for (u32 bindpoint = 0; bindpoint < static_cast<u32>(entries.size()); ++bindpoint) { | 954 | const auto& entry{entries[bindpoint]}; |
| 966 | const auto& entry = entries[bindpoint]; | 955 | const auto& region{global_cache.GetGlobalRegion(entry, stage)}; |
| 967 | const u32 current_bindpoint = base_bindings.gmem + bindpoint; | 956 | bind_ssbo_pushbuffer.Push(region->GetBufferHandle(), 0, |
| 968 | const auto& region = global_cache.GetGlobalRegion(entry, stage); | 957 | static_cast<GLsizeiptr>(region->GetSizeInBytes())); |
| 969 | |||
| 970 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, current_bindpoint, region->GetBufferHandle()); | ||
| 971 | } | 958 | } |
| 972 | } | 959 | } |
| 973 | 960 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 54fbf48aa..e4c64ae71 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 28 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 29 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 29 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 30 | #include "video_core/renderer_opengl/gl_state.h" | 30 | #include "video_core/renderer_opengl/gl_state.h" |
| 31 | #include "video_core/renderer_opengl/utils.h" | ||
| 31 | 32 | ||
| 32 | namespace Core { | 33 | namespace Core { |
| 33 | class System; | 34 | class System; |
| @@ -229,6 +230,9 @@ private: | |||
| 229 | PrimitiveAssembler primitive_assembler{buffer_cache}; | 230 | PrimitiveAssembler primitive_assembler{buffer_cache}; |
| 230 | GLint uniform_buffer_alignment; | 231 | GLint uniform_buffer_alignment; |
| 231 | 232 | ||
| 233 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; | ||
| 234 | BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; | ||
| 235 | |||
| 232 | std::size_t CalculateVertexArraysSize() const; | 236 | std::size_t CalculateVertexArraysSize() const; |
| 233 | 237 | ||
| 234 | std::size_t CalculateIndexBufferSize() const; | 238 | std::size_t CalculateIndexBufferSize() const; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index aa6da1944..55b6d8591 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -266,6 +266,10 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, | |||
| 266 | params.component_type = ComponentTypeFromRenderTarget(config.format); | 266 | params.component_type = ComponentTypeFromRenderTarget(config.format); |
| 267 | params.type = GetFormatType(params.pixel_format); | 267 | params.type = GetFormatType(params.pixel_format); |
| 268 | params.width = config.width; | 268 | params.width = config.width; |
| 269 | if (!params.is_tiled) { | ||
| 270 | const u32 bpp = params.GetFormatBpp() / 8; | ||
| 271 | params.pitch = config.width * bpp; | ||
| 272 | } | ||
| 269 | params.height = config.height; | 273 | params.height = config.height; |
| 270 | params.unaligned_height = config.height; | 274 | params.unaligned_height = config.height; |
| 271 | params.target = SurfaceTarget::Texture2D; | 275 | params.target = SurfaceTarget::Texture2D; |
| @@ -1175,10 +1179,16 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1175 | return new_surface; | 1179 | return new_surface; |
| 1176 | } | 1180 | } |
| 1177 | 1181 | ||
| 1182 | const bool old_compressed = | ||
| 1183 | GetFormatTuple(old_params.pixel_format, old_params.component_type).compressed; | ||
| 1184 | const bool new_compressed = | ||
| 1185 | GetFormatTuple(new_params.pixel_format, new_params.component_type).compressed; | ||
| 1186 | const bool compatible_formats = | ||
| 1187 | GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format) && | ||
| 1188 | !(old_compressed || new_compressed); | ||
| 1178 | // For compatible surfaces, we can just do fast glCopyImageSubData based copy | 1189 | // For compatible surfaces, we can just do fast glCopyImageSubData based copy |
| 1179 | if (old_params.target == new_params.target && old_params.type == new_params.type && | 1190 | if (old_params.target == new_params.target && old_params.depth == new_params.depth && |
| 1180 | old_params.depth == new_params.depth && old_params.depth == 1 && | 1191 | old_params.depth == 1 && compatible_formats) { |
| 1181 | GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) { | ||
| 1182 | FastCopySurface(old_surface, new_surface); | 1192 | FastCopySurface(old_surface, new_surface); |
| 1183 | return new_surface; | 1193 | return new_surface; |
| 1184 | } | 1194 | } |
| @@ -1193,7 +1203,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1193 | case SurfaceTarget::TextureCubemap: | 1203 | case SurfaceTarget::TextureCubemap: |
| 1194 | case SurfaceTarget::Texture2DArray: | 1204 | case SurfaceTarget::Texture2DArray: |
| 1195 | case SurfaceTarget::TextureCubeArray: | 1205 | case SurfaceTarget::TextureCubeArray: |
| 1196 | if (old_params.pixel_format == new_params.pixel_format) | 1206 | if (compatible_formats) |
| 1197 | FastLayeredCopySurface(old_surface, new_surface); | 1207 | FastLayeredCopySurface(old_surface, new_surface); |
| 1198 | else { | 1208 | else { |
| 1199 | AccurateCopySurface(old_surface, new_surface); | 1209 | AccurateCopySurface(old_surface, new_surface); |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 3ea08ef7b..28e490b3c 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -552,8 +552,7 @@ private: | |||
| 552 | } else if (std::holds_alternative<OperationNode>(*offset)) { | 552 | } else if (std::holds_alternative<OperationNode>(*offset)) { |
| 553 | // Indirect access | 553 | // Indirect access |
| 554 | const std::string final_offset = code.GenerateTemporary(); | 554 | const std::string final_offset = code.GenerateTemporary(); |
| 555 | code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " + | 555 | code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);"); |
| 556 | std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';'); | ||
| 557 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), | 556 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), |
| 558 | final_offset, final_offset); | 557 | final_offset, final_offset); |
| 559 | 558 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index eaf3e03a0..05ab01dcb 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -2,12 +2,44 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "video_core/engines/maxwell_3d.h" | ||
| 5 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 7 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 6 | 8 | ||
| 7 | namespace OpenGL::GLShader { | 9 | namespace OpenGL::GLShader { |
| 8 | 10 | ||
| 9 | using Tegra::Engines::Maxwell3D; | 11 | using Tegra::Engines::Maxwell3D; |
| 10 | 12 | ||
| 13 | ProgramManager::ProgramManager() { | ||
| 14 | pipeline.Create(); | ||
| 15 | } | ||
| 16 | |||
| 17 | ProgramManager::~ProgramManager() = default; | ||
| 18 | |||
| 19 | void ProgramManager::ApplyTo(OpenGLState& state) { | ||
| 20 | UpdatePipeline(); | ||
| 21 | state.draw.shader_program = 0; | ||
| 22 | state.draw.program_pipeline = pipeline.handle; | ||
| 23 | } | ||
| 24 | |||
| 25 | void ProgramManager::UpdatePipeline() { | ||
| 26 | // Avoid updating the pipeline when values have no changed | ||
| 27 | if (old_state == current_state) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | |||
| 31 | // Workaround for AMD bug | ||
| 32 | constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | | ||
| 33 | GL_FRAGMENT_SHADER_BIT}; | ||
| 34 | glUseProgramStages(pipeline.handle, all_used_stages, 0); | ||
| 35 | |||
| 36 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader); | ||
| 37 | glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader); | ||
| 38 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader); | ||
| 39 | |||
| 40 | old_state = current_state; | ||
| 41 | } | ||
| 42 | |||
| 11 | void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { | 43 | void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { |
| 12 | const auto& regs = maxwell.regs; | 44 | const auto& regs = maxwell.regs; |
| 13 | const auto& state = maxwell.state; | 45 | const auto& state = maxwell.state; |
| @@ -16,7 +48,7 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shade | |||
| 16 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; | 48 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; |
| 17 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; | 49 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; |
| 18 | 50 | ||
| 19 | u32 func = static_cast<u32>(regs.alpha_test_func); | 51 | auto func{static_cast<u32>(regs.alpha_test_func)}; |
| 20 | // Normalize the gl variants of opCompare to be the same as the normal variants | 52 | // Normalize the gl variants of opCompare to be the same as the normal variants |
| 21 | const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never); | 53 | const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never); |
| 22 | if (func >= op_gl_variant_base) { | 54 | if (func >= op_gl_variant_base) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 37dcfefdb..cec18a832 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 7 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 8 | 10 | ||
| 9 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 11 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| @@ -38,55 +40,48 @@ static_assert(sizeof(MaxwellUniformData) < 16384, | |||
| 38 | 40 | ||
| 39 | class ProgramManager { | 41 | class ProgramManager { |
| 40 | public: | 42 | public: |
| 41 | ProgramManager() { | 43 | explicit ProgramManager(); |
| 42 | pipeline.Create(); | 44 | ~ProgramManager(); |
| 43 | } | 45 | |
| 46 | void ApplyTo(OpenGLState& state); | ||
| 44 | 47 | ||
| 45 | void UseProgrammableVertexShader(GLuint program) { | 48 | void UseProgrammableVertexShader(GLuint program) { |
| 46 | vs = program; | 49 | current_state.vertex_shader = program; |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 49 | void UseProgrammableGeometryShader(GLuint program) { | 52 | void UseProgrammableGeometryShader(GLuint program) { |
| 50 | gs = program; | 53 | current_state.geometry_shader = program; |
| 51 | } | 54 | } |
| 52 | 55 | ||
| 53 | void UseProgrammableFragmentShader(GLuint program) { | 56 | void UseProgrammableFragmentShader(GLuint program) { |
| 54 | fs = program; | 57 | current_state.fragment_shader = program; |
| 55 | } | 58 | } |
| 56 | 59 | ||
| 57 | void UseTrivialGeometryShader() { | 60 | void UseTrivialGeometryShader() { |
| 58 | gs = 0; | 61 | current_state.geometry_shader = 0; |
| 59 | } | ||
| 60 | |||
| 61 | void ApplyTo(OpenGLState& state) { | ||
| 62 | UpdatePipeline(); | ||
| 63 | state.draw.shader_program = 0; | ||
| 64 | state.draw.program_pipeline = pipeline.handle; | ||
| 65 | } | 62 | } |
| 66 | 63 | ||
| 67 | private: | 64 | private: |
| 68 | void UpdatePipeline() { | 65 | struct PipelineState { |
| 69 | // Avoid updating the pipeline when values have no changed | 66 | bool operator==(const PipelineState& rhs) const { |
| 70 | if (old_vs == vs && old_fs == fs && old_gs == gs) | 67 | return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader && |
| 71 | return; | 68 | geometry_shader == rhs.geometry_shader; |
| 72 | // Workaround for AMD bug | 69 | } |
| 73 | glUseProgramStages(pipeline.handle, | 70 | |
| 74 | GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, | 71 | bool operator!=(const PipelineState& rhs) const { |
| 75 | 0); | 72 | return !operator==(rhs); |
| 76 | 73 | } | |
| 77 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vs); | 74 | |
| 78 | glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, gs); | 75 | GLuint vertex_shader{}; |
| 79 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); | 76 | GLuint fragment_shader{}; |
| 80 | 77 | GLuint geometry_shader{}; | |
| 81 | // Update the old values | 78 | }; |
| 82 | old_vs = vs; | 79 | |
| 83 | old_fs = fs; | 80 | void UpdatePipeline(); |
| 84 | old_gs = gs; | ||
| 85 | } | ||
| 86 | 81 | ||
| 87 | OGLPipeline pipeline; | 82 | OGLPipeline pipeline; |
| 88 | GLuint vs{}, fs{}, gs{}; | 83 | PipelineState current_state; |
| 89 | GLuint old_vs{}, old_fs{}, old_gs{}; | 84 | PipelineState old_state; |
| 90 | }; | 85 | }; |
| 91 | 86 | ||
| 92 | } // namespace OpenGL::GLShader | 87 | } // namespace OpenGL::GLShader |
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp index d84634cb3..84a987371 100644 --- a/src/video_core/renderer_opengl/utils.cpp +++ b/src/video_core/renderer_opengl/utils.cpp | |||
| @@ -5,11 +5,39 @@ | |||
| 5 | #include <string> | 5 | #include <string> |
| 6 | #include <fmt/format.h> | 6 | #include <fmt/format.h> |
| 7 | #include <glad/glad.h> | 7 | #include <glad/glad.h> |
| 8 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "video_core/renderer_opengl/utils.h" | 10 | #include "video_core/renderer_opengl/utils.h" |
| 10 | 11 | ||
| 11 | namespace OpenGL { | 12 | namespace OpenGL { |
| 12 | 13 | ||
| 14 | BindBuffersRangePushBuffer::BindBuffersRangePushBuffer(GLenum target) : target{target} {} | ||
| 15 | |||
| 16 | BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default; | ||
| 17 | |||
| 18 | void BindBuffersRangePushBuffer::Setup(GLuint first_) { | ||
| 19 | first = first_; | ||
| 20 | buffers.clear(); | ||
| 21 | offsets.clear(); | ||
| 22 | sizes.clear(); | ||
| 23 | } | ||
| 24 | |||
| 25 | void BindBuffersRangePushBuffer::Push(GLuint buffer, GLintptr offset, GLsizeiptr size) { | ||
| 26 | buffers.push_back(buffer); | ||
| 27 | offsets.push_back(offset); | ||
| 28 | sizes.push_back(size); | ||
| 29 | } | ||
| 30 | |||
| 31 | void BindBuffersRangePushBuffer::Bind() const { | ||
| 32 | const std::size_t count{buffers.size()}; | ||
| 33 | DEBUG_ASSERT(count == offsets.size() && count == sizes.size()); | ||
| 34 | if (count == 0) { | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | glBindBuffersRange(target, first, static_cast<GLsizei>(count), buffers.data(), offsets.data(), | ||
| 38 | sizes.data()); | ||
| 39 | } | ||
| 40 | |||
| 13 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { | 41 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { |
| 14 | if (!GLAD_GL_KHR_debug) { | 42 | if (!GLAD_GL_KHR_debug) { |
| 15 | return; // We don't need to throw an error as this is just for debugging | 43 | return; // We don't need to throw an error as this is just for debugging |
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h index 1fcb6fc11..aef45c9dc 100644 --- a/src/video_core/renderer_opengl/utils.h +++ b/src/video_core/renderer_opengl/utils.h | |||
| @@ -5,11 +5,31 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <vector> | ||
| 8 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| 11 | namespace OpenGL { | 12 | namespace OpenGL { |
| 12 | 13 | ||
| 14 | class BindBuffersRangePushBuffer { | ||
| 15 | public: | ||
| 16 | BindBuffersRangePushBuffer(GLenum target); | ||
| 17 | ~BindBuffersRangePushBuffer(); | ||
| 18 | |||
| 19 | void Setup(GLuint first_); | ||
| 20 | |||
| 21 | void Push(GLuint buffer, GLintptr offset, GLsizeiptr size); | ||
| 22 | |||
| 23 | void Bind() const; | ||
| 24 | |||
| 25 | private: | ||
| 26 | GLenum target; | ||
| 27 | GLuint first; | ||
| 28 | std::vector<GLuint> buffers; | ||
| 29 | std::vector<GLintptr> offsets; | ||
| 30 | std::vector<GLsizeiptr> sizes; | ||
| 31 | }; | ||
| 32 | |||
| 13 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); | 33 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); |
| 14 | 34 | ||
| 15 | } // namespace OpenGL \ No newline at end of file | 35 | } // namespace OpenGL \ No newline at end of file |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp new file mode 100644 index 000000000..e0a6f5e87 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -0,0 +1,1379 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <functional> | ||
| 6 | #include <map> | ||
| 7 | #include <set> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include <sirit/sirit.h> | ||
| 12 | |||
| 13 | #include "common/alignment.h" | ||
| 14 | #include "common/assert.h" | ||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "common/logging/log.h" | ||
| 17 | #include "video_core/engines/maxwell_3d.h" | ||
| 18 | #include "video_core/engines/shader_bytecode.h" | ||
| 19 | #include "video_core/engines/shader_header.h" | ||
| 20 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | ||
| 21 | #include "video_core/shader/shader_ir.h" | ||
| 22 | |||
| 23 | namespace Vulkan::VKShader { | ||
| 24 | |||
| 25 | using Sirit::Id; | ||
| 26 | using Tegra::Shader::Attribute; | ||
| 27 | using Tegra::Shader::AttributeUse; | ||
| 28 | using Tegra::Shader::Register; | ||
| 29 | using namespace VideoCommon::Shader; | ||
| 30 | |||
| 31 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 32 | using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage; | ||
| 33 | using Operation = const OperationNode&; | ||
| 34 | |||
| 35 | // TODO(Rodrigo): Use rasterizer's value | ||
| 36 | constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 0x1000; | ||
| 37 | constexpr u32 STAGE_BINDING_STRIDE = 0x100; | ||
| 38 | |||
| 39 | enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat }; | ||
| 40 | |||
| 41 | struct SamplerImage { | ||
| 42 | Id image_type; | ||
| 43 | Id sampled_image_type; | ||
| 44 | Id sampler; | ||
| 45 | }; | ||
| 46 | |||
| 47 | namespace { | ||
| 48 | |||
| 49 | spv::Dim GetSamplerDim(const Sampler& sampler) { | ||
| 50 | switch (sampler.GetType()) { | ||
| 51 | case Tegra::Shader::TextureType::Texture1D: | ||
| 52 | return spv::Dim::Dim1D; | ||
| 53 | case Tegra::Shader::TextureType::Texture2D: | ||
| 54 | return spv::Dim::Dim2D; | ||
| 55 | case Tegra::Shader::TextureType::Texture3D: | ||
| 56 | return spv::Dim::Dim3D; | ||
| 57 | case Tegra::Shader::TextureType::TextureCube: | ||
| 58 | return spv::Dim::Cube; | ||
| 59 | default: | ||
| 60 | UNIMPLEMENTED_MSG("Unimplemented sampler type={}", static_cast<u32>(sampler.GetType())); | ||
| 61 | return spv::Dim::Dim2D; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Returns true if an attribute index is one of the 32 generic attributes | ||
| 66 | constexpr bool IsGenericAttribute(Attribute::Index attribute) { | ||
| 67 | return attribute >= Attribute::Index::Attribute_0 && | ||
| 68 | attribute <= Attribute::Index::Attribute_31; | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Returns the location of a generic attribute | ||
| 72 | constexpr u32 GetGenericAttributeLocation(Attribute::Index attribute) { | ||
| 73 | ASSERT(IsGenericAttribute(attribute)); | ||
| 74 | return static_cast<u32>(attribute) - static_cast<u32>(Attribute::Index::Attribute_0); | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Returns true if an object has to be treated as precise | ||
| 78 | bool IsPrecise(Operation operand) { | ||
| 79 | const auto& meta = operand.GetMeta(); | ||
| 80 | |||
| 81 | if (std::holds_alternative<MetaArithmetic>(meta)) { | ||
| 82 | return std::get<MetaArithmetic>(meta).precise; | ||
| 83 | } | ||
| 84 | if (std::holds_alternative<MetaHalfArithmetic>(meta)) { | ||
| 85 | return std::get<MetaHalfArithmetic>(meta).precise; | ||
| 86 | } | ||
| 87 | return false; | ||
| 88 | } | ||
| 89 | |||
| 90 | } // namespace | ||
| 91 | |||
| 92 | class SPIRVDecompiler : public Sirit::Module { | ||
| 93 | public: | ||
| 94 | explicit SPIRVDecompiler(const ShaderIR& ir, ShaderStage stage) | ||
| 95 | : Module(0x00010300), ir{ir}, stage{stage}, header{ir.GetHeader()} { | ||
| 96 | AddCapability(spv::Capability::Shader); | ||
| 97 | AddExtension("SPV_KHR_storage_buffer_storage_class"); | ||
| 98 | AddExtension("SPV_KHR_variable_pointers"); | ||
| 99 | } | ||
| 100 | |||
| 101 | void Decompile() { | ||
| 102 | AllocateBindings(); | ||
| 103 | AllocateLabels(); | ||
| 104 | |||
| 105 | DeclareVertex(); | ||
| 106 | DeclareGeometry(); | ||
| 107 | DeclareFragment(); | ||
| 108 | DeclareRegisters(); | ||
| 109 | DeclarePredicates(); | ||
| 110 | DeclareLocalMemory(); | ||
| 111 | DeclareInternalFlags(); | ||
| 112 | DeclareInputAttributes(); | ||
| 113 | DeclareOutputAttributes(); | ||
| 114 | DeclareConstantBuffers(); | ||
| 115 | DeclareGlobalBuffers(); | ||
| 116 | DeclareSamplers(); | ||
| 117 | |||
| 118 | execute_function = | ||
| 119 | Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); | ||
| 120 | Emit(OpLabel()); | ||
| 121 | |||
| 122 | const u32 first_address = ir.GetBasicBlocks().begin()->first; | ||
| 123 | const Id loop_label = OpLabel("loop"); | ||
| 124 | const Id merge_label = OpLabel("merge"); | ||
| 125 | const Id dummy_label = OpLabel(); | ||
| 126 | const Id jump_label = OpLabel(); | ||
| 127 | continue_label = OpLabel("continue"); | ||
| 128 | |||
| 129 | std::vector<Sirit::Literal> literals; | ||
| 130 | std::vector<Id> branch_labels; | ||
| 131 | for (const auto& pair : labels) { | ||
| 132 | const auto [literal, label] = pair; | ||
| 133 | literals.push_back(literal); | ||
| 134 | branch_labels.push_back(label); | ||
| 135 | } | ||
| 136 | |||
| 137 | // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely | ||
| 138 | // that shaders will use 20 nested SSYs and PBKs. | ||
| 139 | constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 140 | const Id flow_stack_type = TypeArray(t_uint, Constant(t_uint, FLOW_STACK_SIZE)); | ||
| 141 | jmp_to = Emit(OpVariable(TypePointer(spv::StorageClass::Function, t_uint), | ||
| 142 | spv::StorageClass::Function, Constant(t_uint, first_address))); | ||
| 143 | flow_stack = Emit(OpVariable(TypePointer(spv::StorageClass::Function, flow_stack_type), | ||
| 144 | spv::StorageClass::Function, ConstantNull(flow_stack_type))); | ||
| 145 | flow_stack_top = | ||
| 146 | Emit(OpVariable(t_func_uint, spv::StorageClass::Function, Constant(t_uint, 0))); | ||
| 147 | |||
| 148 | Name(jmp_to, "jmp_to"); | ||
| 149 | Name(flow_stack, "flow_stack"); | ||
| 150 | Name(flow_stack_top, "flow_stack_top"); | ||
| 151 | |||
| 152 | Emit(OpBranch(loop_label)); | ||
| 153 | Emit(loop_label); | ||
| 154 | Emit(OpLoopMerge(merge_label, continue_label, spv::LoopControlMask::Unroll)); | ||
| 155 | Emit(OpBranch(dummy_label)); | ||
| 156 | |||
| 157 | Emit(dummy_label); | ||
| 158 | const Id default_branch = OpLabel(); | ||
| 159 | const Id jmp_to_load = Emit(OpLoad(t_uint, jmp_to)); | ||
| 160 | Emit(OpSelectionMerge(jump_label, spv::SelectionControlMask::MaskNone)); | ||
| 161 | Emit(OpSwitch(jmp_to_load, default_branch, literals, branch_labels)); | ||
| 162 | |||
| 163 | Emit(default_branch); | ||
| 164 | Emit(OpReturn()); | ||
| 165 | |||
| 166 | for (const auto& pair : ir.GetBasicBlocks()) { | ||
| 167 | const auto& [address, bb] = pair; | ||
| 168 | Emit(labels.at(address)); | ||
| 169 | |||
| 170 | VisitBasicBlock(bb); | ||
| 171 | |||
| 172 | const auto next_it = labels.lower_bound(address + 1); | ||
| 173 | const Id next_label = next_it != labels.end() ? next_it->second : default_branch; | ||
| 174 | Emit(OpBranch(next_label)); | ||
| 175 | } | ||
| 176 | |||
| 177 | Emit(jump_label); | ||
| 178 | Emit(OpBranch(continue_label)); | ||
| 179 | Emit(continue_label); | ||
| 180 | Emit(OpBranch(loop_label)); | ||
| 181 | Emit(merge_label); | ||
| 182 | Emit(OpReturn()); | ||
| 183 | Emit(OpFunctionEnd()); | ||
| 184 | } | ||
| 185 | |||
| 186 | ShaderEntries GetShaderEntries() const { | ||
| 187 | ShaderEntries entries; | ||
| 188 | entries.const_buffers_base_binding = const_buffers_base_binding; | ||
| 189 | entries.global_buffers_base_binding = global_buffers_base_binding; | ||
| 190 | entries.samplers_base_binding = samplers_base_binding; | ||
| 191 | for (const auto& cbuf : ir.GetConstantBuffers()) { | ||
| 192 | entries.const_buffers.emplace_back(cbuf.second, cbuf.first); | ||
| 193 | } | ||
| 194 | for (const auto& gmem : ir.GetGlobalMemoryBases()) { | ||
| 195 | entries.global_buffers.emplace_back(gmem.cbuf_index, gmem.cbuf_offset); | ||
| 196 | } | ||
| 197 | for (const auto& sampler : ir.GetSamplers()) { | ||
| 198 | entries.samplers.emplace_back(sampler); | ||
| 199 | } | ||
| 200 | for (const auto& attr : ir.GetInputAttributes()) { | ||
| 201 | entries.attributes.insert(GetGenericAttributeLocation(attr.first)); | ||
| 202 | } | ||
| 203 | entries.clip_distances = ir.GetClipDistances(); | ||
| 204 | entries.shader_length = ir.GetLength(); | ||
| 205 | entries.entry_function = execute_function; | ||
| 206 | entries.interfaces = interfaces; | ||
| 207 | return entries; | ||
| 208 | } | ||
| 209 | |||
| 210 | private: | ||
| 211 | using OperationDecompilerFn = Id (SPIRVDecompiler::*)(Operation); | ||
| 212 | using OperationDecompilersArray = | ||
| 213 | std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>; | ||
| 214 | |||
| 215 | static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount); | ||
| 216 | static constexpr u32 CBUF_STRIDE = 16; | ||
| 217 | |||
| 218 | void AllocateBindings() { | ||
| 219 | const u32 binding_base = static_cast<u32>(stage) * STAGE_BINDING_STRIDE; | ||
| 220 | u32 binding_iterator = binding_base; | ||
| 221 | |||
| 222 | const auto Allocate = [&binding_iterator](std::size_t count) { | ||
| 223 | const u32 current_binding = binding_iterator; | ||
| 224 | binding_iterator += static_cast<u32>(count); | ||
| 225 | return current_binding; | ||
| 226 | }; | ||
| 227 | const_buffers_base_binding = Allocate(ir.GetConstantBuffers().size()); | ||
| 228 | global_buffers_base_binding = Allocate(ir.GetGlobalMemoryBases().size()); | ||
| 229 | samplers_base_binding = Allocate(ir.GetSamplers().size()); | ||
| 230 | |||
| 231 | ASSERT_MSG(binding_iterator - binding_base < STAGE_BINDING_STRIDE, | ||
| 232 | "Stage binding stride is too small"); | ||
| 233 | } | ||
| 234 | |||
| 235 | void AllocateLabels() { | ||
| 236 | for (const auto& pair : ir.GetBasicBlocks()) { | ||
| 237 | const u32 address = pair.first; | ||
| 238 | labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address))); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | void DeclareVertex() { | ||
| 243 | if (stage != ShaderStage::Vertex) | ||
| 244 | return; | ||
| 245 | |||
| 246 | DeclareVertexRedeclarations(); | ||
| 247 | } | ||
| 248 | |||
| 249 | void DeclareGeometry() { | ||
| 250 | if (stage != ShaderStage::Geometry) | ||
| 251 | return; | ||
| 252 | |||
| 253 | UNIMPLEMENTED(); | ||
| 254 | } | ||
| 255 | |||
| 256 | void DeclareFragment() { | ||
| 257 | if (stage != ShaderStage::Fragment) | ||
| 258 | return; | ||
| 259 | |||
| 260 | for (u32 rt = 0; rt < static_cast<u32>(frag_colors.size()); ++rt) { | ||
| 261 | if (!IsRenderTargetUsed(rt)) { | ||
| 262 | continue; | ||
| 263 | } | ||
| 264 | |||
| 265 | const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output)); | ||
| 266 | Name(id, fmt::format("frag_color{}", rt)); | ||
| 267 | Decorate(id, spv::Decoration::Location, rt); | ||
| 268 | |||
| 269 | frag_colors[rt] = id; | ||
| 270 | interfaces.push_back(id); | ||
| 271 | } | ||
| 272 | |||
| 273 | if (header.ps.omap.depth) { | ||
| 274 | frag_depth = AddGlobalVariable(OpVariable(t_out_float, spv::StorageClass::Output)); | ||
| 275 | Name(frag_depth, "frag_depth"); | ||
| 276 | Decorate(frag_depth, spv::Decoration::BuiltIn, | ||
| 277 | static_cast<u32>(spv::BuiltIn::FragDepth)); | ||
| 278 | |||
| 279 | interfaces.push_back(frag_depth); | ||
| 280 | } | ||
| 281 | |||
| 282 | frag_coord = DeclareBuiltIn(spv::BuiltIn::FragCoord, spv::StorageClass::Input, t_in_float4, | ||
| 283 | "frag_coord"); | ||
| 284 | front_facing = DeclareBuiltIn(spv::BuiltIn::FrontFacing, spv::StorageClass::Input, | ||
| 285 | t_in_bool, "front_facing"); | ||
| 286 | } | ||
| 287 | |||
| 288 | void DeclareRegisters() { | ||
| 289 | for (const u32 gpr : ir.GetRegisters()) { | ||
| 290 | const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero); | ||
| 291 | Name(id, fmt::format("gpr_{}", gpr)); | ||
| 292 | registers.emplace(gpr, AddGlobalVariable(id)); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | void DeclarePredicates() { | ||
| 297 | for (const auto pred : ir.GetPredicates()) { | ||
| 298 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 299 | Name(id, fmt::format("pred_{}", static_cast<u32>(pred))); | ||
| 300 | predicates.emplace(pred, AddGlobalVariable(id)); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | void DeclareLocalMemory() { | ||
| 305 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { | ||
| 306 | const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4); | ||
| 307 | const Id type_array = TypeArray(t_float, Constant(t_uint, element_count)); | ||
| 308 | const Id type_pointer = TypePointer(spv::StorageClass::Private, type_array); | ||
| 309 | Name(type_pointer, "LocalMemory"); | ||
| 310 | |||
| 311 | local_memory = | ||
| 312 | OpVariable(type_pointer, spv::StorageClass::Private, ConstantNull(type_array)); | ||
| 313 | AddGlobalVariable(Name(local_memory, "local_memory")); | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | void DeclareInternalFlags() { | ||
| 318 | constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry", | ||
| 319 | "overflow"}; | ||
| 320 | for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) { | ||
| 321 | const auto flag_code = static_cast<InternalFlag>(flag); | ||
| 322 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 323 | internal_flags[flag] = AddGlobalVariable(Name(id, names[flag])); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | void DeclareInputAttributes() { | ||
| 328 | for (const auto element : ir.GetInputAttributes()) { | ||
| 329 | const Attribute::Index index = element.first; | ||
| 330 | if (!IsGenericAttribute(index)) { | ||
| 331 | continue; | ||
| 332 | } | ||
| 333 | |||
| 334 | UNIMPLEMENTED_IF(stage == ShaderStage::Geometry); | ||
| 335 | |||
| 336 | const u32 location = GetGenericAttributeLocation(index); | ||
| 337 | const Id id = OpVariable(t_in_float4, spv::StorageClass::Input); | ||
| 338 | Name(AddGlobalVariable(id), fmt::format("in_attr{}", location)); | ||
| 339 | input_attributes.emplace(index, id); | ||
| 340 | interfaces.push_back(id); | ||
| 341 | |||
| 342 | Decorate(id, spv::Decoration::Location, location); | ||
| 343 | |||
| 344 | if (stage != ShaderStage::Fragment) { | ||
| 345 | continue; | ||
| 346 | } | ||
| 347 | switch (header.ps.GetAttributeUse(location)) { | ||
| 348 | case AttributeUse::Constant: | ||
| 349 | Decorate(id, spv::Decoration::Flat); | ||
| 350 | break; | ||
| 351 | case AttributeUse::ScreenLinear: | ||
| 352 | Decorate(id, spv::Decoration::NoPerspective); | ||
| 353 | break; | ||
| 354 | case AttributeUse::Perspective: | ||
| 355 | // Default | ||
| 356 | break; | ||
| 357 | default: | ||
| 358 | UNREACHABLE_MSG("Unused attribute being fetched"); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | void DeclareOutputAttributes() { | ||
| 364 | for (const auto index : ir.GetOutputAttributes()) { | ||
| 365 | if (!IsGenericAttribute(index)) { | ||
| 366 | continue; | ||
| 367 | } | ||
| 368 | const auto location = GetGenericAttributeLocation(index); | ||
| 369 | const Id id = OpVariable(t_out_float4, spv::StorageClass::Output); | ||
| 370 | Name(AddGlobalVariable(id), fmt::format("out_attr{}", location)); | ||
| 371 | output_attributes.emplace(index, id); | ||
| 372 | interfaces.push_back(id); | ||
| 373 | |||
| 374 | Decorate(id, spv::Decoration::Location, location); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | void DeclareConstantBuffers() { | ||
| 379 | u32 binding = const_buffers_base_binding; | ||
| 380 | for (const auto& entry : ir.GetConstantBuffers()) { | ||
| 381 | const auto [index, size] = entry; | ||
| 382 | const Id id = OpVariable(t_cbuf_ubo, spv::StorageClass::Uniform); | ||
| 383 | AddGlobalVariable(Name(id, fmt::format("cbuf_{}", index))); | ||
| 384 | |||
| 385 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 386 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 387 | constant_buffers.emplace(index, id); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | void DeclareGlobalBuffers() { | ||
| 392 | u32 binding = global_buffers_base_binding; | ||
| 393 | for (const auto& entry : ir.GetGlobalMemoryBases()) { | ||
| 394 | const Id id = OpVariable(t_gmem_ssbo, spv::StorageClass::StorageBuffer); | ||
| 395 | AddGlobalVariable( | ||
| 396 | Name(id, fmt::format("gmem_{}_{}", entry.cbuf_index, entry.cbuf_offset))); | ||
| 397 | |||
| 398 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 399 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 400 | global_buffers.emplace(entry, id); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | void DeclareSamplers() { | ||
| 405 | u32 binding = samplers_base_binding; | ||
| 406 | for (const auto& sampler : ir.GetSamplers()) { | ||
| 407 | const auto dim = GetSamplerDim(sampler); | ||
| 408 | const int depth = sampler.IsShadow() ? 1 : 0; | ||
| 409 | const int arrayed = sampler.IsArray() ? 1 : 0; | ||
| 410 | // TODO(Rodrigo): Sampled 1 indicates that the image will be used with a sampler. When | ||
| 411 | // SULD and SUST instructions are implemented, replace this value. | ||
| 412 | const int sampled = 1; | ||
| 413 | const Id image_type = | ||
| 414 | TypeImage(t_float, dim, depth, arrayed, false, sampled, spv::ImageFormat::Unknown); | ||
| 415 | const Id sampled_image_type = TypeSampledImage(image_type); | ||
| 416 | const Id pointer_type = | ||
| 417 | TypePointer(spv::StorageClass::UniformConstant, sampled_image_type); | ||
| 418 | const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant); | ||
| 419 | AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex()))); | ||
| 420 | |||
| 421 | sampler_images.insert( | ||
| 422 | {static_cast<u32>(sampler.GetIndex()), {image_type, sampled_image_type, id}}); | ||
| 423 | |||
| 424 | Decorate(id, spv::Decoration::Binding, binding++); | ||
| 425 | Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | void DeclareVertexRedeclarations() { | ||
| 430 | vertex_index = DeclareBuiltIn(spv::BuiltIn::VertexIndex, spv::StorageClass::Input, | ||
| 431 | t_in_uint, "vertex_index"); | ||
| 432 | instance_index = DeclareBuiltIn(spv::BuiltIn::InstanceIndex, spv::StorageClass::Input, | ||
| 433 | t_in_uint, "instance_index"); | ||
| 434 | |||
| 435 | bool is_point_size_declared = false; | ||
| 436 | bool is_clip_distances_declared = false; | ||
| 437 | for (const auto index : ir.GetOutputAttributes()) { | ||
| 438 | if (index == Attribute::Index::PointSize) { | ||
| 439 | is_point_size_declared = true; | ||
| 440 | } else if (index == Attribute::Index::ClipDistances0123 || | ||
| 441 | index == Attribute::Index::ClipDistances4567) { | ||
| 442 | is_clip_distances_declared = true; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | std::vector<Id> members; | ||
| 447 | members.push_back(t_float4); | ||
| 448 | if (is_point_size_declared) { | ||
| 449 | members.push_back(t_float); | ||
| 450 | } | ||
| 451 | if (is_clip_distances_declared) { | ||
| 452 | members.push_back(TypeArray(t_float, Constant(t_uint, 8))); | ||
| 453 | } | ||
| 454 | |||
| 455 | const Id gl_per_vertex_struct = Name(TypeStruct(members), "PerVertex"); | ||
| 456 | Decorate(gl_per_vertex_struct, spv::Decoration::Block); | ||
| 457 | |||
| 458 | u32 declaration_index = 0; | ||
| 459 | const auto MemberDecorateBuiltIn = [&](spv::BuiltIn builtin, std::string name, | ||
| 460 | bool condition) { | ||
| 461 | if (!condition) | ||
| 462 | return u32{}; | ||
| 463 | MemberName(gl_per_vertex_struct, declaration_index, name); | ||
| 464 | MemberDecorate(gl_per_vertex_struct, declaration_index, spv::Decoration::BuiltIn, | ||
| 465 | static_cast<u32>(builtin)); | ||
| 466 | return declaration_index++; | ||
| 467 | }; | ||
| 468 | |||
| 469 | position_index = MemberDecorateBuiltIn(spv::BuiltIn::Position, "position", true); | ||
| 470 | point_size_index = | ||
| 471 | MemberDecorateBuiltIn(spv::BuiltIn::PointSize, "point_size", is_point_size_declared); | ||
| 472 | clip_distances_index = MemberDecorateBuiltIn(spv::BuiltIn::ClipDistance, "clip_distances", | ||
| 473 | is_clip_distances_declared); | ||
| 474 | |||
| 475 | const Id type_pointer = TypePointer(spv::StorageClass::Output, gl_per_vertex_struct); | ||
| 476 | per_vertex = OpVariable(type_pointer, spv::StorageClass::Output); | ||
| 477 | AddGlobalVariable(Name(per_vertex, "per_vertex")); | ||
| 478 | interfaces.push_back(per_vertex); | ||
| 479 | } | ||
| 480 | |||
| 481 | void VisitBasicBlock(const NodeBlock& bb) { | ||
| 482 | for (const Node node : bb) { | ||
| 483 | static_cast<void>(Visit(node)); | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | Id Visit(Node node) { | ||
| 488 | if (const auto operation = std::get_if<OperationNode>(node)) { | ||
| 489 | const auto operation_index = static_cast<std::size_t>(operation->GetCode()); | ||
| 490 | const auto decompiler = operation_decompilers[operation_index]; | ||
| 491 | if (decompiler == nullptr) { | ||
| 492 | UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); | ||
| 493 | } | ||
| 494 | return (this->*decompiler)(*operation); | ||
| 495 | |||
| 496 | } else if (const auto gpr = std::get_if<GprNode>(node)) { | ||
| 497 | const u32 index = gpr->GetIndex(); | ||
| 498 | if (index == Register::ZeroIndex) { | ||
| 499 | return Constant(t_float, 0.0f); | ||
| 500 | } | ||
| 501 | return Emit(OpLoad(t_float, registers.at(index))); | ||
| 502 | |||
| 503 | } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { | ||
| 504 | return BitcastTo<Type::Float>(Constant(t_uint, immediate->GetValue())); | ||
| 505 | |||
| 506 | } else if (const auto predicate = std::get_if<PredicateNode>(node)) { | ||
| 507 | const auto value = [&]() -> Id { | ||
| 508 | switch (const auto index = predicate->GetIndex(); index) { | ||
| 509 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 510 | return v_true; | ||
| 511 | case Tegra::Shader::Pred::NeverExecute: | ||
| 512 | return v_false; | ||
| 513 | default: | ||
| 514 | return Emit(OpLoad(t_bool, predicates.at(index))); | ||
| 515 | } | ||
| 516 | }(); | ||
| 517 | if (predicate->IsNegated()) { | ||
| 518 | return Emit(OpLogicalNot(t_bool, value)); | ||
| 519 | } | ||
| 520 | return value; | ||
| 521 | |||
| 522 | } else if (const auto abuf = std::get_if<AbufNode>(node)) { | ||
| 523 | const auto attribute = abuf->GetIndex(); | ||
| 524 | const auto element = abuf->GetElement(); | ||
| 525 | |||
| 526 | switch (attribute) { | ||
| 527 | case Attribute::Index::Position: | ||
| 528 | if (stage != ShaderStage::Fragment) { | ||
| 529 | UNIMPLEMENTED(); | ||
| 530 | break; | ||
| 531 | } else { | ||
| 532 | if (element == 3) { | ||
| 533 | return Constant(t_float, 1.0f); | ||
| 534 | } | ||
| 535 | return Emit(OpLoad(t_float, AccessElement(t_in_float, frag_coord, element))); | ||
| 536 | } | ||
| 537 | case Attribute::Index::TessCoordInstanceIDVertexID: | ||
| 538 | // TODO(Subv): Find out what the values are for the first two elements when inside a | ||
| 539 | // vertex shader, and what's the value of the fourth element when inside a Tess Eval | ||
| 540 | // shader. | ||
| 541 | ASSERT(stage == ShaderStage::Vertex); | ||
| 542 | switch (element) { | ||
| 543 | case 2: | ||
| 544 | return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, instance_index))); | ||
| 545 | case 3: | ||
| 546 | return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, vertex_index))); | ||
| 547 | } | ||
| 548 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); | ||
| 549 | return Constant(t_float, 0); | ||
| 550 | case Attribute::Index::FrontFacing: | ||
| 551 | // TODO(Subv): Find out what the values are for the other elements. | ||
| 552 | ASSERT(stage == ShaderStage::Fragment); | ||
| 553 | if (element == 3) { | ||
| 554 | const Id is_front_facing = Emit(OpLoad(t_bool, front_facing)); | ||
| 555 | const Id true_value = | ||
| 556 | BitcastTo<Type::Float>(Constant(t_int, static_cast<s32>(-1))); | ||
| 557 | const Id false_value = BitcastTo<Type::Float>(Constant(t_int, 0)); | ||
| 558 | return Emit(OpSelect(t_float, is_front_facing, true_value, false_value)); | ||
| 559 | } | ||
| 560 | UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); | ||
| 561 | return Constant(t_float, 0.0f); | ||
| 562 | default: | ||
| 563 | if (IsGenericAttribute(attribute)) { | ||
| 564 | const Id pointer = | ||
| 565 | AccessElement(t_in_float, input_attributes.at(attribute), element); | ||
| 566 | return Emit(OpLoad(t_float, pointer)); | ||
| 567 | } | ||
| 568 | break; | ||
| 569 | } | ||
| 570 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); | ||
| 571 | |||
| 572 | } else if (const auto cbuf = std::get_if<CbufNode>(node)) { | ||
| 573 | const Node offset = cbuf->GetOffset(); | ||
| 574 | const Id buffer_id = constant_buffers.at(cbuf->GetIndex()); | ||
| 575 | |||
| 576 | Id buffer_index{}; | ||
| 577 | Id buffer_element{}; | ||
| 578 | |||
| 579 | if (const auto immediate = std::get_if<ImmediateNode>(offset)) { | ||
| 580 | // Direct access | ||
| 581 | const u32 offset_imm = immediate->GetValue(); | ||
| 582 | ASSERT(offset_imm % 4 == 0); | ||
| 583 | buffer_index = Constant(t_uint, offset_imm / 16); | ||
| 584 | buffer_element = Constant(t_uint, (offset_imm / 4) % 4); | ||
| 585 | |||
| 586 | } else if (std::holds_alternative<OperationNode>(*offset)) { | ||
| 587 | // Indirect access | ||
| 588 | // TODO(Rodrigo): Use a uniform buffer stride of 4 and drop this slow math (which | ||
| 589 | // emits sub-optimal code on GLSL from my testing). | ||
| 590 | const Id offset_id = BitcastTo<Type::Uint>(Visit(offset)); | ||
| 591 | const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4))); | ||
| 592 | const Id final_offset = Emit( | ||
| 593 | OpUMod(t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1))); | ||
| 594 | buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4))); | ||
| 595 | buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4))); | ||
| 596 | |||
| 597 | } else { | ||
| 598 | UNREACHABLE_MSG("Unmanaged offset node type"); | ||
| 599 | } | ||
| 600 | |||
| 601 | const Id pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0), | ||
| 602 | buffer_index, buffer_element)); | ||
| 603 | return Emit(OpLoad(t_float, pointer)); | ||
| 604 | |||
| 605 | } else if (const auto gmem = std::get_if<GmemNode>(node)) { | ||
| 606 | const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); | ||
| 607 | const Id real = BitcastTo<Type::Uint>(Visit(gmem->GetRealAddress())); | ||
| 608 | const Id base = BitcastTo<Type::Uint>(Visit(gmem->GetBaseAddress())); | ||
| 609 | |||
| 610 | Id offset = Emit(OpISub(t_uint, real, base)); | ||
| 611 | offset = Emit(OpUDiv(t_uint, offset, Constant(t_uint, 4u))); | ||
| 612 | return Emit(OpLoad(t_float, Emit(OpAccessChain(t_gmem_float, gmem_buffer, | ||
| 613 | Constant(t_uint, 0u), offset)))); | ||
| 614 | |||
| 615 | } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { | ||
| 616 | // It's invalid to call conditional on nested nodes, use an operation instead | ||
| 617 | const Id true_label = OpLabel(); | ||
| 618 | const Id skip_label = OpLabel(); | ||
| 619 | Emit(OpBranchConditional(Visit(conditional->GetCondition()), true_label, skip_label)); | ||
| 620 | Emit(true_label); | ||
| 621 | |||
| 622 | VisitBasicBlock(conditional->GetCode()); | ||
| 623 | |||
| 624 | Emit(OpBranch(skip_label)); | ||
| 625 | Emit(skip_label); | ||
| 626 | return {}; | ||
| 627 | |||
| 628 | } else if (const auto comment = std::get_if<CommentNode>(node)) { | ||
| 629 | Name(Emit(OpUndef(t_void)), comment->GetText()); | ||
| 630 | return {}; | ||
| 631 | } | ||
| 632 | |||
| 633 | UNREACHABLE(); | ||
| 634 | return {}; | ||
| 635 | } | ||
| 636 | |||
| 637 | template <Id (Module::*func)(Id, Id), Type result_type, Type type_a = result_type> | ||
| 638 | Id Unary(Operation operation) { | ||
| 639 | const Id type_def = GetTypeDefinition(result_type); | ||
| 640 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 641 | |||
| 642 | const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a))); | ||
| 643 | if (IsPrecise(operation)) { | ||
| 644 | Decorate(value, spv::Decoration::NoContraction); | ||
| 645 | } | ||
| 646 | return value; | ||
| 647 | } | ||
| 648 | |||
| 649 | template <Id (Module::*func)(Id, Id, Id), Type result_type, Type type_a = result_type, | ||
| 650 | Type type_b = type_a> | ||
| 651 | Id Binary(Operation operation) { | ||
| 652 | const Id type_def = GetTypeDefinition(result_type); | ||
| 653 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 654 | const Id op_b = VisitOperand<type_b>(operation, 1); | ||
| 655 | |||
| 656 | const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b))); | ||
| 657 | if (IsPrecise(operation)) { | ||
| 658 | Decorate(value, spv::Decoration::NoContraction); | ||
| 659 | } | ||
| 660 | return value; | ||
| 661 | } | ||
| 662 | |||
| 663 | template <Id (Module::*func)(Id, Id, Id, Id), Type result_type, Type type_a = result_type, | ||
| 664 | Type type_b = type_a, Type type_c = type_b> | ||
| 665 | Id Ternary(Operation operation) { | ||
| 666 | const Id type_def = GetTypeDefinition(result_type); | ||
| 667 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 668 | const Id op_b = VisitOperand<type_b>(operation, 1); | ||
| 669 | const Id op_c = VisitOperand<type_c>(operation, 2); | ||
| 670 | |||
| 671 | const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c))); | ||
| 672 | if (IsPrecise(operation)) { | ||
| 673 | Decorate(value, spv::Decoration::NoContraction); | ||
| 674 | } | ||
| 675 | return value; | ||
| 676 | } | ||
| 677 | |||
| 678 | template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, Type type_a = result_type, | ||
| 679 | Type type_b = type_a, Type type_c = type_b, Type type_d = type_c> | ||
| 680 | Id Quaternary(Operation operation) { | ||
| 681 | const Id type_def = GetTypeDefinition(result_type); | ||
| 682 | const Id op_a = VisitOperand<type_a>(operation, 0); | ||
| 683 | const Id op_b = VisitOperand<type_b>(operation, 1); | ||
| 684 | const Id op_c = VisitOperand<type_c>(operation, 2); | ||
| 685 | const Id op_d = VisitOperand<type_d>(operation, 3); | ||
| 686 | |||
| 687 | const Id value = | ||
| 688 | BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c, op_d))); | ||
| 689 | if (IsPrecise(operation)) { | ||
| 690 | Decorate(value, spv::Decoration::NoContraction); | ||
| 691 | } | ||
| 692 | return value; | ||
| 693 | } | ||
| 694 | |||
| 695 | Id Assign(Operation operation) { | ||
| 696 | const Node dest = operation[0]; | ||
| 697 | const Node src = operation[1]; | ||
| 698 | |||
| 699 | Id target{}; | ||
| 700 | if (const auto gpr = std::get_if<GprNode>(dest)) { | ||
| 701 | if (gpr->GetIndex() == Register::ZeroIndex) { | ||
| 702 | // Writing to Register::ZeroIndex is a no op | ||
| 703 | return {}; | ||
| 704 | } | ||
| 705 | target = registers.at(gpr->GetIndex()); | ||
| 706 | |||
| 707 | } else if (const auto abuf = std::get_if<AbufNode>(dest)) { | ||
| 708 | target = [&]() -> Id { | ||
| 709 | switch (const auto attribute = abuf->GetIndex(); attribute) { | ||
| 710 | case Attribute::Index::Position: | ||
| 711 | return AccessElement(t_out_float, per_vertex, position_index, | ||
| 712 | abuf->GetElement()); | ||
| 713 | case Attribute::Index::PointSize: | ||
| 714 | return AccessElement(t_out_float, per_vertex, point_size_index); | ||
| 715 | case Attribute::Index::ClipDistances0123: | ||
| 716 | return AccessElement(t_out_float, per_vertex, clip_distances_index, | ||
| 717 | abuf->GetElement()); | ||
| 718 | case Attribute::Index::ClipDistances4567: | ||
| 719 | return AccessElement(t_out_float, per_vertex, clip_distances_index, | ||
| 720 | abuf->GetElement() + 4); | ||
| 721 | default: | ||
| 722 | if (IsGenericAttribute(attribute)) { | ||
| 723 | return AccessElement(t_out_float, output_attributes.at(attribute), | ||
| 724 | abuf->GetElement()); | ||
| 725 | } | ||
| 726 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", | ||
| 727 | static_cast<u32>(attribute)); | ||
| 728 | return {}; | ||
| 729 | } | ||
| 730 | }(); | ||
| 731 | |||
| 732 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { | ||
| 733 | Id address = BitcastTo<Type::Uint>(Visit(lmem->GetAddress())); | ||
| 734 | address = Emit(OpUDiv(t_uint, address, Constant(t_uint, 4))); | ||
| 735 | target = Emit(OpAccessChain(t_prv_float, local_memory, {address})); | ||
| 736 | } | ||
| 737 | |||
| 738 | Emit(OpStore(target, Visit(src))); | ||
| 739 | return {}; | ||
| 740 | } | ||
| 741 | |||
| 742 | Id HNegate(Operation operation) { | ||
| 743 | UNIMPLEMENTED(); | ||
| 744 | return {}; | ||
| 745 | } | ||
| 746 | |||
| 747 | Id HMergeF32(Operation operation) { | ||
| 748 | UNIMPLEMENTED(); | ||
| 749 | return {}; | ||
| 750 | } | ||
| 751 | |||
| 752 | Id HMergeH0(Operation operation) { | ||
| 753 | UNIMPLEMENTED(); | ||
| 754 | return {}; | ||
| 755 | } | ||
| 756 | |||
| 757 | Id HMergeH1(Operation operation) { | ||
| 758 | UNIMPLEMENTED(); | ||
| 759 | return {}; | ||
| 760 | } | ||
| 761 | |||
| 762 | Id HPack2(Operation operation) { | ||
| 763 | UNIMPLEMENTED(); | ||
| 764 | return {}; | ||
| 765 | } | ||
| 766 | |||
| 767 | Id LogicalAssign(Operation operation) { | ||
| 768 | const Node dest = operation[0]; | ||
| 769 | const Node src = operation[1]; | ||
| 770 | |||
| 771 | Id target{}; | ||
| 772 | if (const auto pred = std::get_if<PredicateNode>(dest)) { | ||
| 773 | ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment"); | ||
| 774 | |||
| 775 | const auto index = pred->GetIndex(); | ||
| 776 | switch (index) { | ||
| 777 | case Tegra::Shader::Pred::NeverExecute: | ||
| 778 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 779 | // Writing to these predicates is a no-op | ||
| 780 | return {}; | ||
| 781 | } | ||
| 782 | target = predicates.at(index); | ||
| 783 | |||
| 784 | } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) { | ||
| 785 | target = internal_flags.at(static_cast<u32>(flag->GetFlag())); | ||
| 786 | } | ||
| 787 | |||
| 788 | Emit(OpStore(target, Visit(src))); | ||
| 789 | return {}; | ||
| 790 | } | ||
| 791 | |||
| 792 | Id LogicalPick2(Operation operation) { | ||
| 793 | UNIMPLEMENTED(); | ||
| 794 | return {}; | ||
| 795 | } | ||
| 796 | |||
| 797 | Id LogicalAll2(Operation operation) { | ||
| 798 | UNIMPLEMENTED(); | ||
| 799 | return {}; | ||
| 800 | } | ||
| 801 | |||
| 802 | Id LogicalAny2(Operation operation) { | ||
| 803 | UNIMPLEMENTED(); | ||
| 804 | return {}; | ||
| 805 | } | ||
| 806 | |||
| 807 | Id GetTextureSampler(Operation operation) { | ||
| 808 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 809 | const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex())); | ||
| 810 | return Emit(OpLoad(entry.sampled_image_type, entry.sampler)); | ||
| 811 | } | ||
| 812 | |||
| 813 | Id GetTextureImage(Operation operation) { | ||
| 814 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 815 | const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex())); | ||
| 816 | return Emit(OpImage(entry.image_type, GetTextureSampler(operation))); | ||
| 817 | } | ||
| 818 | |||
| 819 | Id GetTextureCoordinates(Operation operation) { | ||
| 820 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 821 | std::vector<Id> coords; | ||
| 822 | for (std::size_t i = 0; i < operation.GetOperandsCount(); ++i) { | ||
| 823 | coords.push_back(Visit(operation[i])); | ||
| 824 | } | ||
| 825 | if (meta->sampler.IsArray()) { | ||
| 826 | const Id array_integer = BitcastTo<Type::Int>(Visit(meta->array)); | ||
| 827 | coords.push_back(Emit(OpConvertSToF(t_float, array_integer))); | ||
| 828 | } | ||
| 829 | if (meta->sampler.IsShadow()) { | ||
| 830 | coords.push_back(Visit(meta->depth_compare)); | ||
| 831 | } | ||
| 832 | |||
| 833 | const std::array<Id, 4> t_float_lut = {nullptr, t_float2, t_float3, t_float4}; | ||
| 834 | return coords.size() == 1 | ||
| 835 | ? coords[0] | ||
| 836 | : Emit(OpCompositeConstruct(t_float_lut.at(coords.size() - 1), coords)); | ||
| 837 | } | ||
| 838 | |||
| 839 | Id GetTextureElement(Operation operation, Id sample_value) { | ||
| 840 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 841 | ASSERT(meta); | ||
| 842 | return Emit(OpCompositeExtract(t_float, sample_value, meta->element)); | ||
| 843 | } | ||
| 844 | |||
| 845 | Id Texture(Operation operation) { | ||
| 846 | const Id texture = Emit(OpImageSampleImplicitLod(t_float4, GetTextureSampler(operation), | ||
| 847 | GetTextureCoordinates(operation))); | ||
| 848 | return GetTextureElement(operation, texture); | ||
| 849 | } | ||
| 850 | |||
| 851 | Id TextureLod(Operation operation) { | ||
| 852 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 853 | const Id texture = Emit(OpImageSampleExplicitLod( | ||
| 854 | t_float4, GetTextureSampler(operation), GetTextureCoordinates(operation), | ||
| 855 | spv::ImageOperandsMask::Lod, Visit(meta->lod))); | ||
| 856 | return GetTextureElement(operation, texture); | ||
| 857 | } | ||
| 858 | |||
| 859 | Id TextureGather(Operation operation) { | ||
| 860 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 861 | const auto coords = GetTextureCoordinates(operation); | ||
| 862 | |||
| 863 | Id texture; | ||
| 864 | if (meta->sampler.IsShadow()) { | ||
| 865 | texture = Emit(OpImageDrefGather(t_float4, GetTextureSampler(operation), coords, | ||
| 866 | Visit(meta->component))); | ||
| 867 | } else { | ||
| 868 | u32 component_value = 0; | ||
| 869 | if (meta->component) { | ||
| 870 | const auto component = std::get_if<ImmediateNode>(meta->component); | ||
| 871 | ASSERT_MSG(component, "Component is not an immediate value"); | ||
| 872 | component_value = component->GetValue(); | ||
| 873 | } | ||
| 874 | texture = Emit(OpImageGather(t_float4, GetTextureSampler(operation), coords, | ||
| 875 | Constant(t_uint, component_value))); | ||
| 876 | } | ||
| 877 | |||
| 878 | return GetTextureElement(operation, texture); | ||
| 879 | } | ||
| 880 | |||
| 881 | Id TextureQueryDimensions(Operation operation) { | ||
| 882 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | ||
| 883 | const auto image_id = GetTextureImage(operation); | ||
| 884 | AddCapability(spv::Capability::ImageQuery); | ||
| 885 | |||
| 886 | if (meta->element == 3) { | ||
| 887 | return BitcastTo<Type::Float>(Emit(OpImageQueryLevels(t_int, image_id))); | ||
| 888 | } | ||
| 889 | |||
| 890 | const Id lod = VisitOperand<Type::Uint>(operation, 0); | ||
| 891 | const std::size_t coords_count = [&]() { | ||
| 892 | switch (const auto type = meta->sampler.GetType(); type) { | ||
| 893 | case Tegra::Shader::TextureType::Texture1D: | ||
| 894 | return 1; | ||
| 895 | case Tegra::Shader::TextureType::Texture2D: | ||
| 896 | case Tegra::Shader::TextureType::TextureCube: | ||
| 897 | return 2; | ||
| 898 | case Tegra::Shader::TextureType::Texture3D: | ||
| 899 | return 3; | ||
| 900 | default: | ||
| 901 | UNREACHABLE_MSG("Invalid texture type={}", static_cast<u32>(type)); | ||
| 902 | return 2; | ||
| 903 | } | ||
| 904 | }(); | ||
| 905 | |||
| 906 | if (meta->element >= coords_count) { | ||
| 907 | return Constant(t_float, 0.0f); | ||
| 908 | } | ||
| 909 | |||
| 910 | const std::array<Id, 3> types = {t_int, t_int2, t_int3}; | ||
| 911 | const Id sizes = Emit(OpImageQuerySizeLod(types.at(coords_count - 1), image_id, lod)); | ||
| 912 | const Id size = Emit(OpCompositeExtract(t_int, sizes, meta->element)); | ||
| 913 | return BitcastTo<Type::Float>(size); | ||
| 914 | } | ||
| 915 | |||
| 916 | Id TextureQueryLod(Operation operation) { | ||
| 917 | UNIMPLEMENTED(); | ||
| 918 | return {}; | ||
| 919 | } | ||
| 920 | |||
| 921 | Id TexelFetch(Operation operation) { | ||
| 922 | UNIMPLEMENTED(); | ||
| 923 | return {}; | ||
| 924 | } | ||
| 925 | |||
| 926 | Id Branch(Operation operation) { | ||
| 927 | const auto target = std::get_if<ImmediateNode>(operation[0]); | ||
| 928 | UNIMPLEMENTED_IF(!target); | ||
| 929 | |||
| 930 | Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); | ||
| 931 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | ||
| 932 | return {}; | ||
| 933 | } | ||
| 934 | |||
| 935 | Id PushFlowStack(Operation operation) { | ||
| 936 | const auto target = std::get_if<ImmediateNode>(operation[0]); | ||
| 937 | ASSERT(target); | ||
| 938 | |||
| 939 | const Id current = Emit(OpLoad(t_uint, flow_stack_top)); | ||
| 940 | const Id next = Emit(OpIAdd(t_uint, current, Constant(t_uint, 1))); | ||
| 941 | const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, current)); | ||
| 942 | |||
| 943 | Emit(OpStore(access, Constant(t_uint, target->GetValue()))); | ||
| 944 | Emit(OpStore(flow_stack_top, next)); | ||
| 945 | return {}; | ||
| 946 | } | ||
| 947 | |||
| 948 | Id PopFlowStack(Operation operation) { | ||
| 949 | const Id current = Emit(OpLoad(t_uint, flow_stack_top)); | ||
| 950 | const Id previous = Emit(OpISub(t_uint, current, Constant(t_uint, 1))); | ||
| 951 | const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, previous)); | ||
| 952 | const Id target = Emit(OpLoad(t_uint, access)); | ||
| 953 | |||
| 954 | Emit(OpStore(flow_stack_top, previous)); | ||
| 955 | Emit(OpStore(jmp_to, target)); | ||
| 956 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | ||
| 957 | return {}; | ||
| 958 | } | ||
| 959 | |||
| 960 | Id Exit(Operation operation) { | ||
| 961 | switch (stage) { | ||
| 962 | case ShaderStage::Vertex: { | ||
| 963 | // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't | ||
| 964 | // seem to be working on Nvidia's drivers and Intel (mesa and blob) doesn't support it. | ||
| 965 | const Id position = AccessElement(t_float4, per_vertex, position_index); | ||
| 966 | Id depth = Emit(OpLoad(t_float, AccessElement(t_out_float, position, 2))); | ||
| 967 | depth = Emit(OpFAdd(t_float, depth, Constant(t_float, 1.0f))); | ||
| 968 | depth = Emit(OpFMul(t_float, depth, Constant(t_float, 0.5f))); | ||
| 969 | Emit(OpStore(AccessElement(t_out_float, position, 2), depth)); | ||
| 970 | break; | ||
| 971 | } | ||
| 972 | case ShaderStage::Fragment: { | ||
| 973 | const auto SafeGetRegister = [&](u32 reg) { | ||
| 974 | // TODO(Rodrigo): Replace with contains once C++20 releases | ||
| 975 | if (const auto it = registers.find(reg); it != registers.end()) { | ||
| 976 | return Emit(OpLoad(t_float, it->second)); | ||
| 977 | } | ||
| 978 | return Constant(t_float, 0.0f); | ||
| 979 | }; | ||
| 980 | |||
| 981 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, | ||
| 982 | "Sample mask write is unimplemented"); | ||
| 983 | |||
| 984 | // TODO(Rodrigo): Alpha testing | ||
| 985 | |||
| 986 | // Write the color outputs using the data in the shader registers, disabled | ||
| 987 | // rendertargets/components are skipped in the register assignment. | ||
| 988 | u32 current_reg = 0; | ||
| 989 | for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { | ||
| 990 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | ||
| 991 | for (u32 component = 0; component < 4; ++component) { | ||
| 992 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { | ||
| 993 | Emit(OpStore(AccessElement(t_out_float, frag_colors.at(rt), component), | ||
| 994 | SafeGetRegister(current_reg))); | ||
| 995 | ++current_reg; | ||
| 996 | } | ||
| 997 | } | ||
| 998 | } | ||
| 999 | if (header.ps.omap.depth) { | ||
| 1000 | // The depth output is always 2 registers after the last color output, and | ||
| 1001 | // current_reg already contains one past the last color register. | ||
| 1002 | Emit(OpStore(frag_depth, SafeGetRegister(current_reg + 1))); | ||
| 1003 | } | ||
| 1004 | break; | ||
| 1005 | } | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | BranchingOp([&]() { Emit(OpReturn()); }); | ||
| 1009 | return {}; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | Id Discard(Operation operation) { | ||
| 1013 | BranchingOp([&]() { Emit(OpKill()); }); | ||
| 1014 | return {}; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | Id EmitVertex(Operation operation) { | ||
| 1018 | UNIMPLEMENTED(); | ||
| 1019 | return {}; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | Id EndPrimitive(Operation operation) { | ||
| 1023 | UNIMPLEMENTED(); | ||
| 1024 | return {}; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | Id YNegate(Operation operation) { | ||
| 1028 | UNIMPLEMENTED(); | ||
| 1029 | return {}; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type, | ||
| 1033 | const std::string& name) { | ||
| 1034 | const Id id = OpVariable(type, storage); | ||
| 1035 | Decorate(id, spv::Decoration::BuiltIn, static_cast<u32>(builtin)); | ||
| 1036 | AddGlobalVariable(Name(id, name)); | ||
| 1037 | interfaces.push_back(id); | ||
| 1038 | return id; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | bool IsRenderTargetUsed(u32 rt) const { | ||
| 1042 | for (u32 component = 0; component < 4; ++component) { | ||
| 1043 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { | ||
| 1044 | return true; | ||
| 1045 | } | ||
| 1046 | } | ||
| 1047 | return false; | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | template <typename... Args> | ||
| 1051 | Id AccessElement(Id pointer_type, Id composite, Args... elements_) { | ||
| 1052 | std::vector<Id> members; | ||
| 1053 | auto elements = {elements_...}; | ||
| 1054 | for (const auto element : elements) { | ||
| 1055 | members.push_back(Constant(t_uint, element)); | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | return Emit(OpAccessChain(pointer_type, composite, members)); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | template <Type type> | ||
| 1062 | Id VisitOperand(Operation operation, std::size_t operand_index) { | ||
| 1063 | const Id value = Visit(operation[operand_index]); | ||
| 1064 | |||
| 1065 | switch (type) { | ||
| 1066 | case Type::Bool: | ||
| 1067 | case Type::Bool2: | ||
| 1068 | case Type::Float: | ||
| 1069 | return value; | ||
| 1070 | case Type::Int: | ||
| 1071 | return Emit(OpBitcast(t_int, value)); | ||
| 1072 | case Type::Uint: | ||
| 1073 | return Emit(OpBitcast(t_uint, value)); | ||
| 1074 | case Type::HalfFloat: | ||
| 1075 | UNIMPLEMENTED(); | ||
| 1076 | } | ||
| 1077 | UNREACHABLE(); | ||
| 1078 | return value; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | template <Type type> | ||
| 1082 | Id BitcastFrom(Id value) { | ||
| 1083 | switch (type) { | ||
| 1084 | case Type::Bool: | ||
| 1085 | case Type::Bool2: | ||
| 1086 | case Type::Float: | ||
| 1087 | return value; | ||
| 1088 | case Type::Int: | ||
| 1089 | case Type::Uint: | ||
| 1090 | return Emit(OpBitcast(t_float, value)); | ||
| 1091 | case Type::HalfFloat: | ||
| 1092 | UNIMPLEMENTED(); | ||
| 1093 | } | ||
| 1094 | UNREACHABLE(); | ||
| 1095 | return value; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | template <Type type> | ||
| 1099 | Id BitcastTo(Id value) { | ||
| 1100 | switch (type) { | ||
| 1101 | case Type::Bool: | ||
| 1102 | case Type::Bool2: | ||
| 1103 | UNREACHABLE(); | ||
| 1104 | case Type::Float: | ||
| 1105 | return Emit(OpBitcast(t_float, value)); | ||
| 1106 | case Type::Int: | ||
| 1107 | return Emit(OpBitcast(t_int, value)); | ||
| 1108 | case Type::Uint: | ||
| 1109 | return Emit(OpBitcast(t_uint, value)); | ||
| 1110 | case Type::HalfFloat: | ||
| 1111 | UNIMPLEMENTED(); | ||
| 1112 | } | ||
| 1113 | UNREACHABLE(); | ||
| 1114 | return value; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | Id GetTypeDefinition(Type type) { | ||
| 1118 | switch (type) { | ||
| 1119 | case Type::Bool: | ||
| 1120 | return t_bool; | ||
| 1121 | case Type::Bool2: | ||
| 1122 | return t_bool2; | ||
| 1123 | case Type::Float: | ||
| 1124 | return t_float; | ||
| 1125 | case Type::Int: | ||
| 1126 | return t_int; | ||
| 1127 | case Type::Uint: | ||
| 1128 | return t_uint; | ||
| 1129 | case Type::HalfFloat: | ||
| 1130 | UNIMPLEMENTED(); | ||
| 1131 | } | ||
| 1132 | UNREACHABLE(); | ||
| 1133 | return {}; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | void BranchingOp(std::function<void()> call) { | ||
| 1137 | const Id true_label = OpLabel(); | ||
| 1138 | const Id skip_label = OpLabel(); | ||
| 1139 | Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten)); | ||
| 1140 | Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0)); | ||
| 1141 | Emit(true_label); | ||
| 1142 | call(); | ||
| 1143 | |||
| 1144 | Emit(skip_label); | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | static constexpr OperationDecompilersArray operation_decompilers = { | ||
| 1148 | &SPIRVDecompiler::Assign, | ||
| 1149 | |||
| 1150 | &SPIRVDecompiler::Ternary<&Module::OpSelect, Type::Float, Type::Bool, Type::Float, | ||
| 1151 | Type::Float>, | ||
| 1152 | |||
| 1153 | &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::Float>, | ||
| 1154 | &SPIRVDecompiler::Binary<&Module::OpFMul, Type::Float>, | ||
| 1155 | &SPIRVDecompiler::Binary<&Module::OpFDiv, Type::Float>, | ||
| 1156 | &SPIRVDecompiler::Ternary<&Module::OpFma, Type::Float>, | ||
| 1157 | &SPIRVDecompiler::Unary<&Module::OpFNegate, Type::Float>, | ||
| 1158 | &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::Float>, | ||
| 1159 | &SPIRVDecompiler::Ternary<&Module::OpFClamp, Type::Float>, | ||
| 1160 | &SPIRVDecompiler::Binary<&Module::OpFMin, Type::Float>, | ||
| 1161 | &SPIRVDecompiler::Binary<&Module::OpFMax, Type::Float>, | ||
| 1162 | &SPIRVDecompiler::Unary<&Module::OpCos, Type::Float>, | ||
| 1163 | &SPIRVDecompiler::Unary<&Module::OpSin, Type::Float>, | ||
| 1164 | &SPIRVDecompiler::Unary<&Module::OpExp2, Type::Float>, | ||
| 1165 | &SPIRVDecompiler::Unary<&Module::OpLog2, Type::Float>, | ||
| 1166 | &SPIRVDecompiler::Unary<&Module::OpInverseSqrt, Type::Float>, | ||
| 1167 | &SPIRVDecompiler::Unary<&Module::OpSqrt, Type::Float>, | ||
| 1168 | &SPIRVDecompiler::Unary<&Module::OpRoundEven, Type::Float>, | ||
| 1169 | &SPIRVDecompiler::Unary<&Module::OpFloor, Type::Float>, | ||
| 1170 | &SPIRVDecompiler::Unary<&Module::OpCeil, Type::Float>, | ||
| 1171 | &SPIRVDecompiler::Unary<&Module::OpTrunc, Type::Float>, | ||
| 1172 | &SPIRVDecompiler::Unary<&Module::OpConvertSToF, Type::Float, Type::Int>, | ||
| 1173 | &SPIRVDecompiler::Unary<&Module::OpConvertUToF, Type::Float, Type::Uint>, | ||
| 1174 | |||
| 1175 | &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Int>, | ||
| 1176 | &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Int>, | ||
| 1177 | &SPIRVDecompiler::Binary<&Module::OpSDiv, Type::Int>, | ||
| 1178 | &SPIRVDecompiler::Unary<&Module::OpSNegate, Type::Int>, | ||
| 1179 | &SPIRVDecompiler::Unary<&Module::OpSAbs, Type::Int>, | ||
| 1180 | &SPIRVDecompiler::Binary<&Module::OpSMin, Type::Int>, | ||
| 1181 | &SPIRVDecompiler::Binary<&Module::OpSMax, Type::Int>, | ||
| 1182 | |||
| 1183 | &SPIRVDecompiler::Unary<&Module::OpConvertFToS, Type::Int, Type::Float>, | ||
| 1184 | &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Int, Type::Uint>, | ||
| 1185 | &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Int, Type::Int, Type::Uint>, | ||
| 1186 | &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Int, Type::Int, Type::Uint>, | ||
| 1187 | &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Int, Type::Int, Type::Uint>, | ||
| 1188 | &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Int>, | ||
| 1189 | &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Int>, | ||
| 1190 | &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Int>, | ||
| 1191 | &SPIRVDecompiler::Unary<&Module::OpNot, Type::Int>, | ||
| 1192 | &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Int>, | ||
| 1193 | &SPIRVDecompiler::Ternary<&Module::OpBitFieldSExtract, Type::Int>, | ||
| 1194 | &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Int>, | ||
| 1195 | |||
| 1196 | &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Uint>, | ||
| 1197 | &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Uint>, | ||
| 1198 | &SPIRVDecompiler::Binary<&Module::OpUDiv, Type::Uint>, | ||
| 1199 | &SPIRVDecompiler::Binary<&Module::OpUMin, Type::Uint>, | ||
| 1200 | &SPIRVDecompiler::Binary<&Module::OpUMax, Type::Uint>, | ||
| 1201 | &SPIRVDecompiler::Unary<&Module::OpConvertFToU, Type::Uint, Type::Float>, | ||
| 1202 | &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Uint, Type::Int>, | ||
| 1203 | &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Uint>, | ||
| 1204 | &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Uint>, | ||
| 1205 | &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Uint>, | ||
| 1206 | &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Uint>, | ||
| 1207 | &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Uint>, | ||
| 1208 | &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Uint>, | ||
| 1209 | &SPIRVDecompiler::Unary<&Module::OpNot, Type::Uint>, | ||
| 1210 | &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Uint>, | ||
| 1211 | &SPIRVDecompiler::Ternary<&Module::OpBitFieldUExtract, Type::Uint>, | ||
| 1212 | &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Uint>, | ||
| 1213 | |||
| 1214 | &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::HalfFloat>, | ||
| 1215 | &SPIRVDecompiler::Binary<&Module::OpFMul, Type::HalfFloat>, | ||
| 1216 | &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>, | ||
| 1217 | &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>, | ||
| 1218 | &SPIRVDecompiler::HNegate, | ||
| 1219 | &SPIRVDecompiler::HMergeF32, | ||
| 1220 | &SPIRVDecompiler::HMergeH0, | ||
| 1221 | &SPIRVDecompiler::HMergeH1, | ||
| 1222 | &SPIRVDecompiler::HPack2, | ||
| 1223 | |||
| 1224 | &SPIRVDecompiler::LogicalAssign, | ||
| 1225 | &SPIRVDecompiler::Binary<&Module::OpLogicalAnd, Type::Bool>, | ||
| 1226 | &SPIRVDecompiler::Binary<&Module::OpLogicalOr, Type::Bool>, | ||
| 1227 | &SPIRVDecompiler::Binary<&Module::OpLogicalNotEqual, Type::Bool>, | ||
| 1228 | &SPIRVDecompiler::Unary<&Module::OpLogicalNot, Type::Bool>, | ||
| 1229 | &SPIRVDecompiler::LogicalPick2, | ||
| 1230 | &SPIRVDecompiler::LogicalAll2, | ||
| 1231 | &SPIRVDecompiler::LogicalAny2, | ||
| 1232 | |||
| 1233 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::Float>, | ||
| 1234 | &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::Float>, | ||
| 1235 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::Float>, | ||
| 1236 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>, | ||
| 1237 | &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>, | ||
| 1238 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>, | ||
| 1239 | &SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool>, | ||
| 1240 | |||
| 1241 | &SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>, | ||
| 1242 | &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>, | ||
| 1243 | &SPIRVDecompiler::Binary<&Module::OpSLessThanEqual, Type::Bool, Type::Int>, | ||
| 1244 | &SPIRVDecompiler::Binary<&Module::OpSGreaterThan, Type::Bool, Type::Int>, | ||
| 1245 | &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Int>, | ||
| 1246 | &SPIRVDecompiler::Binary<&Module::OpSGreaterThanEqual, Type::Bool, Type::Int>, | ||
| 1247 | |||
| 1248 | &SPIRVDecompiler::Binary<&Module::OpULessThan, Type::Bool, Type::Uint>, | ||
| 1249 | &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Uint>, | ||
| 1250 | &SPIRVDecompiler::Binary<&Module::OpULessThanEqual, Type::Bool, Type::Uint>, | ||
| 1251 | &SPIRVDecompiler::Binary<&Module::OpUGreaterThan, Type::Bool, Type::Uint>, | ||
| 1252 | &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Uint>, | ||
| 1253 | &SPIRVDecompiler::Binary<&Module::OpUGreaterThanEqual, Type::Bool, Type::Uint>, | ||
| 1254 | |||
| 1255 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>, | ||
| 1256 | &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>, | ||
| 1257 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>, | ||
| 1258 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>, | ||
| 1259 | &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>, | ||
| 1260 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>, | ||
| 1261 | |||
| 1262 | &SPIRVDecompiler::Texture, | ||
| 1263 | &SPIRVDecompiler::TextureLod, | ||
| 1264 | &SPIRVDecompiler::TextureGather, | ||
| 1265 | &SPIRVDecompiler::TextureQueryDimensions, | ||
| 1266 | &SPIRVDecompiler::TextureQueryLod, | ||
| 1267 | &SPIRVDecompiler::TexelFetch, | ||
| 1268 | |||
| 1269 | &SPIRVDecompiler::Branch, | ||
| 1270 | &SPIRVDecompiler::PushFlowStack, | ||
| 1271 | &SPIRVDecompiler::PopFlowStack, | ||
| 1272 | &SPIRVDecompiler::Exit, | ||
| 1273 | &SPIRVDecompiler::Discard, | ||
| 1274 | |||
| 1275 | &SPIRVDecompiler::EmitVertex, | ||
| 1276 | &SPIRVDecompiler::EndPrimitive, | ||
| 1277 | |||
| 1278 | &SPIRVDecompiler::YNegate, | ||
| 1279 | }; | ||
| 1280 | |||
| 1281 | const ShaderIR& ir; | ||
| 1282 | const ShaderStage stage; | ||
| 1283 | const Tegra::Shader::Header header; | ||
| 1284 | |||
| 1285 | const Id t_void = Name(TypeVoid(), "void"); | ||
| 1286 | |||
| 1287 | const Id t_bool = Name(TypeBool(), "bool"); | ||
| 1288 | const Id t_bool2 = Name(TypeVector(t_bool, 2), "bool2"); | ||
| 1289 | |||
| 1290 | const Id t_int = Name(TypeInt(32, true), "int"); | ||
| 1291 | const Id t_int2 = Name(TypeVector(t_int, 2), "int2"); | ||
| 1292 | const Id t_int3 = Name(TypeVector(t_int, 3), "int3"); | ||
| 1293 | const Id t_int4 = Name(TypeVector(t_int, 4), "int4"); | ||
| 1294 | |||
| 1295 | const Id t_uint = Name(TypeInt(32, false), "uint"); | ||
| 1296 | const Id t_uint2 = Name(TypeVector(t_uint, 2), "uint2"); | ||
| 1297 | const Id t_uint3 = Name(TypeVector(t_uint, 3), "uint3"); | ||
| 1298 | const Id t_uint4 = Name(TypeVector(t_uint, 4), "uint4"); | ||
| 1299 | |||
| 1300 | const Id t_float = Name(TypeFloat(32), "float"); | ||
| 1301 | const Id t_float2 = Name(TypeVector(t_float, 2), "float2"); | ||
| 1302 | const Id t_float3 = Name(TypeVector(t_float, 3), "float3"); | ||
| 1303 | const Id t_float4 = Name(TypeVector(t_float, 4), "float4"); | ||
| 1304 | |||
| 1305 | const Id t_prv_bool = Name(TypePointer(spv::StorageClass::Private, t_bool), "prv_bool"); | ||
| 1306 | const Id t_prv_float = Name(TypePointer(spv::StorageClass::Private, t_float), "prv_float"); | ||
| 1307 | |||
| 1308 | const Id t_func_uint = Name(TypePointer(spv::StorageClass::Function, t_uint), "func_uint"); | ||
| 1309 | |||
| 1310 | const Id t_in_bool = Name(TypePointer(spv::StorageClass::Input, t_bool), "in_bool"); | ||
| 1311 | const Id t_in_uint = Name(TypePointer(spv::StorageClass::Input, t_uint), "in_uint"); | ||
| 1312 | const Id t_in_float = Name(TypePointer(spv::StorageClass::Input, t_float), "in_float"); | ||
| 1313 | const Id t_in_float4 = Name(TypePointer(spv::StorageClass::Input, t_float4), "in_float4"); | ||
| 1314 | |||
| 1315 | const Id t_out_float = Name(TypePointer(spv::StorageClass::Output, t_float), "out_float"); | ||
| 1316 | const Id t_out_float4 = Name(TypePointer(spv::StorageClass::Output, t_float4), "out_float4"); | ||
| 1317 | |||
| 1318 | const Id t_cbuf_float = TypePointer(spv::StorageClass::Uniform, t_float); | ||
| 1319 | const Id t_cbuf_array = | ||
| 1320 | Decorate(Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufArray"), | ||
| 1321 | spv::Decoration::ArrayStride, CBUF_STRIDE); | ||
| 1322 | const Id t_cbuf_struct = MemberDecorate( | ||
| 1323 | Decorate(TypeStruct(t_cbuf_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); | ||
| 1324 | const Id t_cbuf_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_struct); | ||
| 1325 | |||
| 1326 | const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float); | ||
| 1327 | const Id t_gmem_array = | ||
| 1328 | Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4u), "GmemArray"); | ||
| 1329 | const Id t_gmem_struct = MemberDecorate( | ||
| 1330 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); | ||
| 1331 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); | ||
| 1332 | |||
| 1333 | const Id v_float_zero = Constant(t_float, 0.0f); | ||
| 1334 | const Id v_true = ConstantTrue(t_bool); | ||
| 1335 | const Id v_false = ConstantFalse(t_bool); | ||
| 1336 | |||
| 1337 | Id per_vertex{}; | ||
| 1338 | std::map<u32, Id> registers; | ||
| 1339 | std::map<Tegra::Shader::Pred, Id> predicates; | ||
| 1340 | Id local_memory{}; | ||
| 1341 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; | ||
| 1342 | std::map<Attribute::Index, Id> input_attributes; | ||
| 1343 | std::map<Attribute::Index, Id> output_attributes; | ||
| 1344 | std::map<u32, Id> constant_buffers; | ||
| 1345 | std::map<GlobalMemoryBase, Id> global_buffers; | ||
| 1346 | std::map<u32, SamplerImage> sampler_images; | ||
| 1347 | |||
| 1348 | Id instance_index{}; | ||
| 1349 | Id vertex_index{}; | ||
| 1350 | std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; | ||
| 1351 | Id frag_depth{}; | ||
| 1352 | Id frag_coord{}; | ||
| 1353 | Id front_facing{}; | ||
| 1354 | |||
| 1355 | u32 position_index{}; | ||
| 1356 | u32 point_size_index{}; | ||
| 1357 | u32 clip_distances_index{}; | ||
| 1358 | |||
| 1359 | std::vector<Id> interfaces; | ||
| 1360 | |||
| 1361 | u32 const_buffers_base_binding{}; | ||
| 1362 | u32 global_buffers_base_binding{}; | ||
| 1363 | u32 samplers_base_binding{}; | ||
| 1364 | |||
| 1365 | Id execute_function{}; | ||
| 1366 | Id jmp_to{}; | ||
| 1367 | Id flow_stack_top{}; | ||
| 1368 | Id flow_stack{}; | ||
| 1369 | Id continue_label{}; | ||
| 1370 | std::map<u32, Id> labels; | ||
| 1371 | }; | ||
| 1372 | |||
| 1373 | DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage) { | ||
| 1374 | auto decompiler = std::make_unique<SPIRVDecompiler>(ir, stage); | ||
| 1375 | decompiler->Decompile(); | ||
| 1376 | return {std::move(decompiler), decompiler->GetShaderEntries()}; | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | } // namespace Vulkan::VKShader | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h new file mode 100644 index 000000000..329d8fa38 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <memory> | ||
| 9 | #include <set> | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include <sirit/sirit.h> | ||
| 14 | |||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "video_core/engines/maxwell_3d.h" | ||
| 17 | #include "video_core/shader/shader_ir.h" | ||
| 18 | |||
| 19 | namespace VideoCommon::Shader { | ||
| 20 | class ShaderIR; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Vulkan::VKShader { | ||
| 24 | |||
| 25 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 26 | |||
| 27 | using SamplerEntry = VideoCommon::Shader::Sampler; | ||
| 28 | |||
| 29 | constexpr u32 DESCRIPTOR_SET = 0; | ||
| 30 | |||
| 31 | class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer { | ||
| 32 | public: | ||
| 33 | explicit constexpr ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, u32 index) | ||
| 34 | : VideoCommon::Shader::ConstBuffer{entry}, index{index} {} | ||
| 35 | |||
| 36 | constexpr u32 GetIndex() const { | ||
| 37 | return index; | ||
| 38 | } | ||
| 39 | |||
| 40 | private: | ||
| 41 | u32 index{}; | ||
| 42 | }; | ||
| 43 | |||
| 44 | class GlobalBufferEntry { | ||
| 45 | public: | ||
| 46 | explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset) | ||
| 47 | : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} | ||
| 48 | |||
| 49 | u32 GetCbufIndex() const { | ||
| 50 | return cbuf_index; | ||
| 51 | } | ||
| 52 | |||
| 53 | u32 GetCbufOffset() const { | ||
| 54 | return cbuf_offset; | ||
| 55 | } | ||
| 56 | |||
| 57 | private: | ||
| 58 | u32 cbuf_index{}; | ||
| 59 | u32 cbuf_offset{}; | ||
| 60 | }; | ||
| 61 | |||
| 62 | struct ShaderEntries { | ||
| 63 | u32 const_buffers_base_binding{}; | ||
| 64 | u32 global_buffers_base_binding{}; | ||
| 65 | u32 samplers_base_binding{}; | ||
| 66 | std::vector<ConstBufferEntry> const_buffers; | ||
| 67 | std::vector<GlobalBufferEntry> global_buffers; | ||
| 68 | std::vector<SamplerEntry> samplers; | ||
| 69 | std::set<u32> attributes; | ||
| 70 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | ||
| 71 | std::size_t shader_length{}; | ||
| 72 | Sirit::Id entry_function{}; | ||
| 73 | std::vector<Sirit::Id> interfaces; | ||
| 74 | }; | ||
| 75 | |||
| 76 | using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>; | ||
| 77 | |||
| 78 | DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage); | ||
| 79 | |||
| 80 | } // namespace Vulkan::VKShader | ||
diff --git a/src/video_core/texture_cache.cpp b/src/video_core/texture_cache.cpp new file mode 100644 index 000000000..e96eba7cc --- /dev/null +++ b/src/video_core/texture_cache.cpp | |||
| @@ -0,0 +1,386 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/alignment.h" | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/cityhash.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "video_core/surface.h" | ||
| 11 | #include "video_core/texture_cache.h" | ||
| 12 | #include "video_core/textures/decoders.h" | ||
| 13 | #include "video_core/textures/texture.h" | ||
| 14 | |||
| 15 | namespace VideoCommon { | ||
| 16 | |||
| 17 | using VideoCore::Surface::SurfaceTarget; | ||
| 18 | |||
| 19 | using VideoCore::Surface::ComponentTypeFromDepthFormat; | ||
| 20 | using VideoCore::Surface::ComponentTypeFromRenderTarget; | ||
| 21 | using VideoCore::Surface::ComponentTypeFromTexture; | ||
| 22 | using VideoCore::Surface::PixelFormatFromDepthFormat; | ||
| 23 | using VideoCore::Surface::PixelFormatFromRenderTargetFormat; | ||
| 24 | using VideoCore::Surface::PixelFormatFromTextureFormat; | ||
| 25 | using VideoCore::Surface::SurfaceTargetFromTextureType; | ||
| 26 | |||
| 27 | constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) { | ||
| 28 | return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile); | ||
| 29 | } | ||
| 30 | |||
| 31 | SurfaceParams SurfaceParams::CreateForTexture(Core::System& system, | ||
| 32 | const Tegra::Texture::FullTextureInfo& config) { | ||
| 33 | SurfaceParams params; | ||
| 34 | params.is_tiled = config.tic.IsTiled(); | ||
| 35 | params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, | ||
| 36 | params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, | ||
| 37 | params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, | ||
| 38 | params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1; | ||
| 39 | params.pixel_format = | ||
| 40 | PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), false); | ||
| 41 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); | ||
| 42 | params.type = GetFormatType(params.pixel_format); | ||
| 43 | params.target = SurfaceTargetFromTextureType(config.tic.texture_type); | ||
| 44 | params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); | ||
| 45 | params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); | ||
| 46 | params.depth = config.tic.Depth(); | ||
| 47 | if (params.target == SurfaceTarget::TextureCubemap || | ||
| 48 | params.target == SurfaceTarget::TextureCubeArray) { | ||
| 49 | params.depth *= 6; | ||
| 50 | } | ||
| 51 | params.pitch = params.is_tiled ? 0 : config.tic.Pitch(); | ||
| 52 | params.unaligned_height = config.tic.Height(); | ||
| 53 | params.num_levels = config.tic.max_mip_level + 1; | ||
| 54 | |||
| 55 | params.CalculateCachedValues(); | ||
| 56 | return params; | ||
| 57 | } | ||
| 58 | |||
| 59 | SurfaceParams SurfaceParams::CreateForDepthBuffer( | ||
| 60 | Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | ||
| 61 | u32 block_width, u32 block_height, u32 block_depth, | ||
| 62 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { | ||
| 63 | SurfaceParams params; | ||
| 64 | params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; | ||
| 65 | params.block_width = 1 << std::min(block_width, 5U); | ||
| 66 | params.block_height = 1 << std::min(block_height, 5U); | ||
| 67 | params.block_depth = 1 << std::min(block_depth, 5U); | ||
| 68 | params.tile_width_spacing = 1; | ||
| 69 | params.pixel_format = PixelFormatFromDepthFormat(format); | ||
| 70 | params.component_type = ComponentTypeFromDepthFormat(format); | ||
| 71 | params.type = GetFormatType(params.pixel_format); | ||
| 72 | params.width = zeta_width; | ||
| 73 | params.height = zeta_height; | ||
| 74 | params.unaligned_height = zeta_height; | ||
| 75 | params.target = SurfaceTarget::Texture2D; | ||
| 76 | params.depth = 1; | ||
| 77 | params.num_levels = 1; | ||
| 78 | |||
| 79 | params.CalculateCachedValues(); | ||
| 80 | return params; | ||
| 81 | } | ||
| 82 | |||
| 83 | SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) { | ||
| 84 | const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; | ||
| 85 | SurfaceParams params; | ||
| 86 | params.is_tiled = | ||
| 87 | config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; | ||
| 88 | params.block_width = 1 << config.memory_layout.block_width; | ||
| 89 | params.block_height = 1 << config.memory_layout.block_height; | ||
| 90 | params.block_depth = 1 << config.memory_layout.block_depth; | ||
| 91 | params.tile_width_spacing = 1; | ||
| 92 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); | ||
| 93 | params.component_type = ComponentTypeFromRenderTarget(config.format); | ||
| 94 | params.type = GetFormatType(params.pixel_format); | ||
| 95 | if (params.is_tiled) { | ||
| 96 | params.width = config.width; | ||
| 97 | } else { | ||
| 98 | const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT; | ||
| 99 | params.pitch = config.width; | ||
| 100 | params.width = params.pitch / bpp; | ||
| 101 | } | ||
| 102 | params.height = config.height; | ||
| 103 | params.depth = 1; | ||
| 104 | params.unaligned_height = config.height; | ||
| 105 | params.target = SurfaceTarget::Texture2D; | ||
| 106 | params.num_levels = 1; | ||
| 107 | |||
| 108 | params.CalculateCachedValues(); | ||
| 109 | return params; | ||
| 110 | } | ||
| 111 | |||
| 112 | SurfaceParams SurfaceParams::CreateForFermiCopySurface( | ||
| 113 | const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||
| 114 | SurfaceParams params{}; | ||
| 115 | params.is_tiled = !config.linear; | ||
| 116 | params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, | ||
| 117 | params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, | ||
| 118 | params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, | ||
| 119 | params.tile_width_spacing = 1; | ||
| 120 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); | ||
| 121 | params.component_type = ComponentTypeFromRenderTarget(config.format); | ||
| 122 | params.type = GetFormatType(params.pixel_format); | ||
| 123 | params.width = config.width; | ||
| 124 | params.height = config.height; | ||
| 125 | params.unaligned_height = config.height; | ||
| 126 | // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters | ||
| 127 | params.target = SurfaceTarget::Texture2D; | ||
| 128 | params.depth = 1; | ||
| 129 | params.num_levels = 1; | ||
| 130 | |||
| 131 | params.CalculateCachedValues(); | ||
| 132 | return params; | ||
| 133 | } | ||
| 134 | |||
| 135 | u32 SurfaceParams::GetMipWidth(u32 level) const { | ||
| 136 | return std::max(1U, width >> level); | ||
| 137 | } | ||
| 138 | |||
| 139 | u32 SurfaceParams::GetMipHeight(u32 level) const { | ||
| 140 | return std::max(1U, height >> level); | ||
| 141 | } | ||
| 142 | |||
| 143 | u32 SurfaceParams::GetMipDepth(u32 level) const { | ||
| 144 | return IsLayered() ? depth : std::max(1U, depth >> level); | ||
| 145 | } | ||
| 146 | |||
| 147 | bool SurfaceParams::IsLayered() const { | ||
| 148 | switch (target) { | ||
| 149 | case SurfaceTarget::Texture1DArray: | ||
| 150 | case SurfaceTarget::Texture2DArray: | ||
| 151 | case SurfaceTarget::TextureCubeArray: | ||
| 152 | case SurfaceTarget::TextureCubemap: | ||
| 153 | return true; | ||
| 154 | default: | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | u32 SurfaceParams::GetMipBlockHeight(u32 level) const { | ||
| 160 | // Auto block resizing algorithm from: | ||
| 161 | // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c | ||
| 162 | if (level == 0) { | ||
| 163 | return block_height; | ||
| 164 | } | ||
| 165 | const u32 height{GetMipHeight(level)}; | ||
| 166 | const u32 default_block_height{GetDefaultBlockHeight(pixel_format)}; | ||
| 167 | const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height}; | ||
| 168 | u32 block_height = 16; | ||
| 169 | while (block_height > 1 && blocks_in_y <= block_height * 4) { | ||
| 170 | block_height >>= 1; | ||
| 171 | } | ||
| 172 | return block_height; | ||
| 173 | } | ||
| 174 | |||
| 175 | u32 SurfaceParams::GetMipBlockDepth(u32 level) const { | ||
| 176 | if (level == 0) | ||
| 177 | return block_depth; | ||
| 178 | if (target != SurfaceTarget::Texture3D) | ||
| 179 | return 1; | ||
| 180 | |||
| 181 | const u32 depth{GetMipDepth(level)}; | ||
| 182 | u32 block_depth = 32; | ||
| 183 | while (block_depth > 1 && depth * 2 <= block_depth) { | ||
| 184 | block_depth >>= 1; | ||
| 185 | } | ||
| 186 | if (block_depth == 32 && GetMipBlockHeight(level) >= 4) { | ||
| 187 | return 16; | ||
| 188 | } | ||
| 189 | return block_depth; | ||
| 190 | } | ||
| 191 | |||
| 192 | std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const { | ||
| 193 | std::size_t offset = 0; | ||
| 194 | for (u32 i = 0; i < level; i++) { | ||
| 195 | offset += GetInnerMipmapMemorySize(i, false, IsLayered(), false); | ||
| 196 | } | ||
| 197 | return offset; | ||
| 198 | } | ||
| 199 | |||
| 200 | std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const { | ||
| 201 | std::size_t offset = 0; | ||
| 202 | for (u32 i = 0; i < level; i++) { | ||
| 203 | offset += GetInnerMipmapMemorySize(i, true, false, false); | ||
| 204 | } | ||
| 205 | return offset; | ||
| 206 | } | ||
| 207 | |||
| 208 | std::size_t SurfaceParams::GetGuestLayerSize() const { | ||
| 209 | return GetInnerMemorySize(false, true, false); | ||
| 210 | } | ||
| 211 | |||
| 212 | std::size_t SurfaceParams::GetHostLayerSize(u32 level) const { | ||
| 213 | return GetInnerMipmapMemorySize(level, true, IsLayered(), false); | ||
| 214 | } | ||
| 215 | |||
| 216 | bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const { | ||
| 217 | if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) != | ||
| 218 | std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format, | ||
| 219 | view_params.component_type, view_params.type)) { | ||
| 220 | return false; | ||
| 221 | } | ||
| 222 | |||
| 223 | const SurfaceTarget view_target{view_params.target}; | ||
| 224 | if (view_target == target) { | ||
| 225 | return true; | ||
| 226 | } | ||
| 227 | |||
| 228 | switch (target) { | ||
| 229 | case SurfaceTarget::Texture1D: | ||
| 230 | case SurfaceTarget::Texture2D: | ||
| 231 | case SurfaceTarget::Texture3D: | ||
| 232 | return false; | ||
| 233 | case SurfaceTarget::Texture1DArray: | ||
| 234 | return view_target == SurfaceTarget::Texture1D; | ||
| 235 | case SurfaceTarget::Texture2DArray: | ||
| 236 | return view_target == SurfaceTarget::Texture2D; | ||
| 237 | case SurfaceTarget::TextureCubemap: | ||
| 238 | return view_target == SurfaceTarget::Texture2D || | ||
| 239 | view_target == SurfaceTarget::Texture2DArray; | ||
| 240 | case SurfaceTarget::TextureCubeArray: | ||
| 241 | return view_target == SurfaceTarget::Texture2D || | ||
| 242 | view_target == SurfaceTarget::Texture2DArray || | ||
| 243 | view_target == SurfaceTarget::TextureCubemap; | ||
| 244 | default: | ||
| 245 | UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target)); | ||
| 246 | return false; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | bool SurfaceParams::IsPixelFormatZeta() const { | ||
| 251 | return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat && | ||
| 252 | pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat; | ||
| 253 | } | ||
| 254 | |||
| 255 | void SurfaceParams::CalculateCachedValues() { | ||
| 256 | guest_size_in_bytes = GetInnerMemorySize(false, false, false); | ||
| 257 | |||
| 258 | // ASTC is uncompressed in software, in emulated as RGBA8 | ||
| 259 | if (IsPixelFormatASTC(pixel_format)) { | ||
| 260 | host_size_in_bytes = width * height * depth * 4; | ||
| 261 | } else { | ||
| 262 | host_size_in_bytes = GetInnerMemorySize(true, false, false); | ||
| 263 | } | ||
| 264 | |||
| 265 | switch (target) { | ||
| 266 | case SurfaceTarget::Texture1D: | ||
| 267 | case SurfaceTarget::Texture2D: | ||
| 268 | case SurfaceTarget::Texture3D: | ||
| 269 | num_layers = 1; | ||
| 270 | break; | ||
| 271 | case SurfaceTarget::Texture1DArray: | ||
| 272 | case SurfaceTarget::Texture2DArray: | ||
| 273 | case SurfaceTarget::TextureCubemap: | ||
| 274 | case SurfaceTarget::TextureCubeArray: | ||
| 275 | num_layers = depth; | ||
| 276 | break; | ||
| 277 | default: | ||
| 278 | UNREACHABLE(); | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only, | ||
| 283 | bool uncompressed) const { | ||
| 284 | const bool tiled{as_host_size ? false : is_tiled}; | ||
| 285 | const u32 tile_x{GetDefaultBlockWidth(pixel_format)}; | ||
| 286 | const u32 tile_y{GetDefaultBlockHeight(pixel_format)}; | ||
| 287 | const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), tile_x)}; | ||
| 288 | const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), tile_y)}; | ||
| 289 | const u32 depth{layer_only ? 1U : GetMipDepth(level)}; | ||
| 290 | return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(pixel_format), width, height, | ||
| 291 | depth, GetMipBlockHeight(level), GetMipBlockDepth(level)); | ||
| 292 | } | ||
| 293 | |||
| 294 | std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only, | ||
| 295 | bool uncompressed) const { | ||
| 296 | std::size_t size = 0; | ||
| 297 | for (u32 level = 0; level < num_levels; ++level) { | ||
| 298 | size += GetInnerMipmapMemorySize(level, as_host_size, layer_only, uncompressed); | ||
| 299 | } | ||
| 300 | if (!as_host_size && is_tiled) { | ||
| 301 | size = Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth); | ||
| 302 | } | ||
| 303 | return size; | ||
| 304 | } | ||
| 305 | |||
| 306 | std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const { | ||
| 307 | std::map<u64, std::pair<u32, u32>> view_offset_map; | ||
| 308 | switch (target) { | ||
| 309 | case SurfaceTarget::Texture1D: | ||
| 310 | case SurfaceTarget::Texture2D: | ||
| 311 | case SurfaceTarget::Texture3D: { | ||
| 312 | constexpr u32 layer = 0; | ||
| 313 | for (u32 level = 0; level < num_levels; ++level) { | ||
| 314 | const std::size_t offset{GetGuestMipmapLevelOffset(level)}; | ||
| 315 | view_offset_map.insert({offset, {layer, level}}); | ||
| 316 | } | ||
| 317 | break; | ||
| 318 | } | ||
| 319 | case SurfaceTarget::Texture1DArray: | ||
| 320 | case SurfaceTarget::Texture2DArray: | ||
| 321 | case SurfaceTarget::TextureCubemap: | ||
| 322 | case SurfaceTarget::TextureCubeArray: { | ||
| 323 | const std::size_t layer_size{GetGuestLayerSize()}; | ||
| 324 | for (u32 level = 0; level < num_levels; ++level) { | ||
| 325 | const std::size_t level_offset{GetGuestMipmapLevelOffset(level)}; | ||
| 326 | for (u32 layer = 0; layer < num_layers; ++layer) { | ||
| 327 | const auto layer_offset{static_cast<std::size_t>(layer_size * layer)}; | ||
| 328 | const std::size_t offset{level_offset + layer_offset}; | ||
| 329 | view_offset_map.insert({offset, {layer, level}}); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | default: | ||
| 335 | UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target)); | ||
| 336 | } | ||
| 337 | return view_offset_map; | ||
| 338 | } | ||
| 339 | |||
| 340 | bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const { | ||
| 341 | return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) && | ||
| 342 | IsInBounds(view_params, layer, level); | ||
| 343 | } | ||
| 344 | |||
| 345 | bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const { | ||
| 346 | return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level); | ||
| 347 | } | ||
| 348 | |||
| 349 | bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const { | ||
| 350 | if (view_params.target != SurfaceTarget::Texture3D) { | ||
| 351 | return true; | ||
| 352 | } | ||
| 353 | return view_params.depth == GetMipDepth(level); | ||
| 354 | } | ||
| 355 | |||
| 356 | bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const { | ||
| 357 | return layer + view_params.num_layers <= num_layers && | ||
| 358 | level + view_params.num_levels <= num_levels; | ||
| 359 | } | ||
| 360 | |||
| 361 | std::size_t HasheableSurfaceParams::Hash() const { | ||
| 362 | return static_cast<std::size_t>( | ||
| 363 | Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); | ||
| 364 | } | ||
| 365 | |||
| 366 | bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const { | ||
| 367 | return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width, | ||
| 368 | height, depth, pitch, unaligned_height, num_levels, pixel_format, | ||
| 369 | component_type, type, target) == | ||
| 370 | std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth, | ||
| 371 | rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch, | ||
| 372 | rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type, | ||
| 373 | rhs.type, rhs.target); | ||
| 374 | } | ||
| 375 | |||
| 376 | std::size_t ViewKey::Hash() const { | ||
| 377 | return static_cast<std::size_t>( | ||
| 378 | Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); | ||
| 379 | } | ||
| 380 | |||
| 381 | bool ViewKey::operator==(const ViewKey& rhs) const { | ||
| 382 | return std::tie(base_layer, num_layers, base_level, num_levels) == | ||
| 383 | std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels); | ||
| 384 | } | ||
| 385 | |||
| 386 | } // namespace VideoCommon | ||
diff --git a/src/video_core/texture_cache.h b/src/video_core/texture_cache.h new file mode 100644 index 000000000..041551691 --- /dev/null +++ b/src/video_core/texture_cache.h | |||
| @@ -0,0 +1,586 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <list> | ||
| 8 | #include <memory> | ||
| 9 | #include <set> | ||
| 10 | #include <tuple> | ||
| 11 | #include <type_traits> | ||
| 12 | #include <unordered_map> | ||
| 13 | |||
| 14 | #include <boost/icl/interval_map.hpp> | ||
| 15 | #include <boost/range/iterator_range.hpp> | ||
| 16 | |||
| 17 | #include "common/assert.h" | ||
| 18 | #include "common/common_types.h" | ||
| 19 | #include "core/memory.h" | ||
| 20 | #include "video_core/engines/fermi_2d.h" | ||
| 21 | #include "video_core/engines/maxwell_3d.h" | ||
| 22 | #include "video_core/gpu.h" | ||
| 23 | #include "video_core/rasterizer_interface.h" | ||
| 24 | #include "video_core/surface.h" | ||
| 25 | |||
| 26 | namespace Core { | ||
| 27 | class System; | ||
| 28 | } | ||
| 29 | |||
| 30 | namespace Tegra::Texture { | ||
| 31 | struct FullTextureInfo; | ||
| 32 | } | ||
| 33 | |||
| 34 | namespace VideoCore { | ||
| 35 | class RasterizerInterface; | ||
| 36 | } | ||
| 37 | |||
| 38 | namespace VideoCommon { | ||
| 39 | |||
| 40 | class HasheableSurfaceParams { | ||
| 41 | public: | ||
| 42 | std::size_t Hash() const; | ||
| 43 | |||
| 44 | bool operator==(const HasheableSurfaceParams& rhs) const; | ||
| 45 | |||
| 46 | protected: | ||
| 47 | // Avoid creation outside of a managed environment. | ||
| 48 | HasheableSurfaceParams() = default; | ||
| 49 | |||
| 50 | bool is_tiled; | ||
| 51 | u32 block_width; | ||
| 52 | u32 block_height; | ||
| 53 | u32 block_depth; | ||
| 54 | u32 tile_width_spacing; | ||
| 55 | u32 width; | ||
| 56 | u32 height; | ||
| 57 | u32 depth; | ||
| 58 | u32 pitch; | ||
| 59 | u32 unaligned_height; | ||
| 60 | u32 num_levels; | ||
| 61 | VideoCore::Surface::PixelFormat pixel_format; | ||
| 62 | VideoCore::Surface::ComponentType component_type; | ||
| 63 | VideoCore::Surface::SurfaceType type; | ||
| 64 | VideoCore::Surface::SurfaceTarget target; | ||
| 65 | }; | ||
| 66 | |||
| 67 | class SurfaceParams final : public HasheableSurfaceParams { | ||
| 68 | public: | ||
| 69 | /// Creates SurfaceCachedParams from a texture configuration. | ||
| 70 | static SurfaceParams CreateForTexture(Core::System& system, | ||
| 71 | const Tegra::Texture::FullTextureInfo& config); | ||
| 72 | |||
| 73 | /// Creates SurfaceCachedParams for a depth buffer configuration. | ||
| 74 | static SurfaceParams CreateForDepthBuffer( | ||
| 75 | Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | ||
| 76 | u32 block_width, u32 block_height, u32 block_depth, | ||
| 77 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); | ||
| 78 | |||
| 79 | /// Creates SurfaceCachedParams from a framebuffer configuration. | ||
| 80 | static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); | ||
| 81 | |||
| 82 | /// Creates SurfaceCachedParams from a Fermi2D surface configuration. | ||
| 83 | static SurfaceParams CreateForFermiCopySurface( | ||
| 84 | const Tegra::Engines::Fermi2D::Regs::Surface& config); | ||
| 85 | |||
| 86 | bool IsTiled() const { | ||
| 87 | return is_tiled; | ||
| 88 | } | ||
| 89 | |||
| 90 | u32 GetBlockWidth() const { | ||
| 91 | return block_width; | ||
| 92 | } | ||
| 93 | |||
| 94 | u32 GetTileWidthSpacing() const { | ||
| 95 | return tile_width_spacing; | ||
| 96 | } | ||
| 97 | |||
| 98 | u32 GetWidth() const { | ||
| 99 | return width; | ||
| 100 | } | ||
| 101 | |||
| 102 | u32 GetHeight() const { | ||
| 103 | return height; | ||
| 104 | } | ||
| 105 | |||
| 106 | u32 GetDepth() const { | ||
| 107 | return depth; | ||
| 108 | } | ||
| 109 | |||
| 110 | u32 GetPitch() const { | ||
| 111 | return pitch; | ||
| 112 | } | ||
| 113 | |||
| 114 | u32 GetNumLevels() const { | ||
| 115 | return num_levels; | ||
| 116 | } | ||
| 117 | |||
| 118 | VideoCore::Surface::PixelFormat GetPixelFormat() const { | ||
| 119 | return pixel_format; | ||
| 120 | } | ||
| 121 | |||
| 122 | VideoCore::Surface::ComponentType GetComponentType() const { | ||
| 123 | return component_type; | ||
| 124 | } | ||
| 125 | |||
| 126 | VideoCore::Surface::SurfaceTarget GetTarget() const { | ||
| 127 | return target; | ||
| 128 | } | ||
| 129 | |||
| 130 | VideoCore::Surface::SurfaceType GetType() const { | ||
| 131 | return type; | ||
| 132 | } | ||
| 133 | |||
| 134 | std::size_t GetGuestSizeInBytes() const { | ||
| 135 | return guest_size_in_bytes; | ||
| 136 | } | ||
| 137 | |||
| 138 | std::size_t GetHostSizeInBytes() const { | ||
| 139 | return host_size_in_bytes; | ||
| 140 | } | ||
| 141 | |||
| 142 | u32 GetNumLayers() const { | ||
| 143 | return num_layers; | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Returns the width of a given mipmap level. | ||
| 147 | u32 GetMipWidth(u32 level) const; | ||
| 148 | |||
| 149 | /// Returns the height of a given mipmap level. | ||
| 150 | u32 GetMipHeight(u32 level) const; | ||
| 151 | |||
| 152 | /// Returns the depth of a given mipmap level. | ||
| 153 | u32 GetMipDepth(u32 level) const; | ||
| 154 | |||
| 155 | /// Returns true if these parameters are from a layered surface. | ||
| 156 | bool IsLayered() const; | ||
| 157 | |||
| 158 | /// Returns the block height of a given mipmap level. | ||
| 159 | u32 GetMipBlockHeight(u32 level) const; | ||
| 160 | |||
| 161 | /// Returns the block depth of a given mipmap level. | ||
| 162 | u32 GetMipBlockDepth(u32 level) const; | ||
| 163 | |||
| 164 | /// Returns the offset in bytes in guest memory of a given mipmap level. | ||
| 165 | std::size_t GetGuestMipmapLevelOffset(u32 level) const; | ||
| 166 | |||
| 167 | /// Returns the offset in bytes in host memory (linear) of a given mipmap level. | ||
| 168 | std::size_t GetHostMipmapLevelOffset(u32 level) const; | ||
| 169 | |||
| 170 | /// Returns the size of a layer in bytes in guest memory. | ||
| 171 | std::size_t GetGuestLayerSize() const; | ||
| 172 | |||
| 173 | /// Returns the size of a layer in bytes in host memory for a given mipmap level. | ||
| 174 | std::size_t GetHostLayerSize(u32 level) const; | ||
| 175 | |||
| 176 | /// Returns true if another surface can be familiar with this. This is a loosely defined term | ||
| 177 | /// that reflects the possibility of these two surface parameters potentially being part of a | ||
| 178 | /// bigger superset. | ||
| 179 | bool IsFamiliar(const SurfaceParams& view_params) const; | ||
| 180 | |||
| 181 | /// Returns true if the pixel format is a depth and/or stencil format. | ||
| 182 | bool IsPixelFormatZeta() const; | ||
| 183 | |||
| 184 | /// Creates a map that redirects an address difference to a layer and mipmap level. | ||
| 185 | std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const; | ||
| 186 | |||
| 187 | /// Returns true if the passed surface view parameters is equal or a valid subset of this. | ||
| 188 | bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||
| 189 | |||
| 190 | private: | ||
| 191 | /// Calculates values that can be deduced from HasheableSurfaceParams. | ||
| 192 | void CalculateCachedValues(); | ||
| 193 | |||
| 194 | /// Returns the size of a given mipmap level. | ||
| 195 | std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only, | ||
| 196 | bool uncompressed) const; | ||
| 197 | |||
| 198 | /// Returns the size of all mipmap levels and aligns as needed. | ||
| 199 | std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const; | ||
| 200 | |||
| 201 | /// Returns true if the passed view width and height match the size of this params in a given | ||
| 202 | /// mipmap level. | ||
| 203 | bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const; | ||
| 204 | |||
| 205 | /// Returns true if the passed view depth match the size of this params in a given mipmap level. | ||
| 206 | bool IsDepthValid(const SurfaceParams& view_params, u32 level) const; | ||
| 207 | |||
| 208 | /// Returns true if the passed view layers and mipmap levels are in bounds. | ||
| 209 | bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||
| 210 | |||
| 211 | std::size_t guest_size_in_bytes; | ||
| 212 | std::size_t host_size_in_bytes; | ||
| 213 | u32 num_layers; | ||
| 214 | }; | ||
| 215 | |||
| 216 | struct ViewKey { | ||
| 217 | std::size_t Hash() const; | ||
| 218 | |||
| 219 | bool operator==(const ViewKey& rhs) const; | ||
| 220 | |||
| 221 | u32 base_layer{}; | ||
| 222 | u32 num_layers{}; | ||
| 223 | u32 base_level{}; | ||
| 224 | u32 num_levels{}; | ||
| 225 | }; | ||
| 226 | |||
| 227 | } // namespace VideoCommon | ||
| 228 | |||
| 229 | namespace std { | ||
| 230 | |||
| 231 | template <> | ||
| 232 | struct hash<VideoCommon::SurfaceParams> { | ||
| 233 | std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { | ||
| 234 | return k.Hash(); | ||
| 235 | } | ||
| 236 | }; | ||
| 237 | |||
| 238 | template <> | ||
| 239 | struct hash<VideoCommon::ViewKey> { | ||
| 240 | std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept { | ||
| 241 | return k.Hash(); | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | |||
| 245 | } // namespace std | ||
| 246 | |||
| 247 | namespace VideoCommon { | ||
| 248 | |||
| 249 | template <typename TView, typename TExecutionContext> | ||
| 250 | class SurfaceBase { | ||
| 251 | static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||
| 252 | |||
| 253 | public: | ||
| 254 | virtual void LoadBuffer() = 0; | ||
| 255 | |||
| 256 | virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0; | ||
| 257 | |||
| 258 | virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0; | ||
| 259 | |||
| 260 | TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) { | ||
| 261 | if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) { | ||
| 262 | // It can't be a view if it's in a prior address. | ||
| 263 | return {}; | ||
| 264 | } | ||
| 265 | |||
| 266 | const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)}; | ||
| 267 | const auto it{view_offset_map.find(relative_offset)}; | ||
| 268 | if (it == view_offset_map.end()) { | ||
| 269 | // Couldn't find an aligned view. | ||
| 270 | return {}; | ||
| 271 | } | ||
| 272 | const auto [layer, level] = it->second; | ||
| 273 | |||
| 274 | if (!params.IsViewValid(view_params, layer, level)) { | ||
| 275 | return {}; | ||
| 276 | } | ||
| 277 | |||
| 278 | return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels()); | ||
| 279 | } | ||
| 280 | |||
| 281 | VAddr GetCpuAddr() const { | ||
| 282 | ASSERT(is_registered); | ||
| 283 | return cpu_addr; | ||
| 284 | } | ||
| 285 | |||
| 286 | u8* GetHostPtr() const { | ||
| 287 | ASSERT(is_registered); | ||
| 288 | return host_ptr; | ||
| 289 | } | ||
| 290 | |||
| 291 | CacheAddr GetCacheAddr() const { | ||
| 292 | ASSERT(is_registered); | ||
| 293 | return cache_addr; | ||
| 294 | } | ||
| 295 | |||
| 296 | std::size_t GetSizeInBytes() const { | ||
| 297 | return params.GetGuestSizeInBytes(); | ||
| 298 | } | ||
| 299 | |||
| 300 | void MarkAsModified(bool is_modified_) { | ||
| 301 | is_modified = is_modified_; | ||
| 302 | } | ||
| 303 | |||
| 304 | const SurfaceParams& GetSurfaceParams() const { | ||
| 305 | return params; | ||
| 306 | } | ||
| 307 | |||
| 308 | TView* GetView(VAddr view_addr, const SurfaceParams& view_params) { | ||
| 309 | TView* view{TryGetView(view_addr, view_params)}; | ||
| 310 | ASSERT(view != nullptr); | ||
| 311 | return view; | ||
| 312 | } | ||
| 313 | |||
| 314 | void Register(VAddr cpu_addr_, u8* host_ptr_) { | ||
| 315 | ASSERT(!is_registered); | ||
| 316 | is_registered = true; | ||
| 317 | cpu_addr = cpu_addr_; | ||
| 318 | host_ptr = host_ptr_; | ||
| 319 | cache_addr = ToCacheAddr(host_ptr_); | ||
| 320 | } | ||
| 321 | |||
| 322 | void Register(VAddr cpu_addr_) { | ||
| 323 | Register(cpu_addr_, Memory::GetPointer(cpu_addr_)); | ||
| 324 | } | ||
| 325 | |||
| 326 | void Unregister() { | ||
| 327 | ASSERT(is_registered); | ||
| 328 | is_registered = false; | ||
| 329 | } | ||
| 330 | |||
| 331 | bool IsRegistered() const { | ||
| 332 | return is_registered; | ||
| 333 | } | ||
| 334 | |||
| 335 | protected: | ||
| 336 | explicit SurfaceBase(const SurfaceParams& params) | ||
| 337 | : params{params}, view_offset_map{params.CreateViewOffsetMap()} {} | ||
| 338 | |||
| 339 | ~SurfaceBase() = default; | ||
| 340 | |||
| 341 | virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0; | ||
| 342 | |||
| 343 | bool IsModified() const { | ||
| 344 | return is_modified; | ||
| 345 | } | ||
| 346 | |||
| 347 | const SurfaceParams params; | ||
| 348 | |||
| 349 | private: | ||
| 350 | TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) { | ||
| 351 | const ViewKey key{base_layer, num_layers, base_level, num_levels}; | ||
| 352 | const auto [entry, is_cache_miss] = views.try_emplace(key); | ||
| 353 | auto& view{entry->second}; | ||
| 354 | if (is_cache_miss) { | ||
| 355 | view = CreateView(key); | ||
| 356 | } | ||
| 357 | return view.get(); | ||
| 358 | } | ||
| 359 | |||
| 360 | const std::map<u64, std::pair<u32, u32>> view_offset_map; | ||
| 361 | |||
| 362 | VAddr cpu_addr{}; | ||
| 363 | u8* host_ptr{}; | ||
| 364 | CacheAddr cache_addr{}; | ||
| 365 | bool is_modified{}; | ||
| 366 | bool is_registered{}; | ||
| 367 | std::unordered_map<ViewKey, std::unique_ptr<TView>> views; | ||
| 368 | }; | ||
| 369 | |||
| 370 | template <typename TSurface, typename TView, typename TExecutionContext> | ||
| 371 | class TextureCache { | ||
| 372 | static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||
| 373 | using ResultType = std::tuple<TView*, TExecutionContext>; | ||
| 374 | using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>; | ||
| 375 | using IntervalType = typename IntervalMap::interval_type; | ||
| 376 | |||
| 377 | public: | ||
| 378 | void InvalidateRegion(CacheAddr addr, std::size_t size) { | ||
| 379 | for (TSurface* surface : GetSurfacesInRegion(addr, size)) { | ||
| 380 | if (!surface->IsRegistered()) { | ||
| 381 | // Skip duplicates | ||
| 382 | continue; | ||
| 383 | } | ||
| 384 | Unregister(surface); | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | ResultType GetTextureSurface(TExecutionContext exctx, | ||
| 389 | const Tegra::Texture::FullTextureInfo& config) { | ||
| 390 | auto& memory_manager{system.GPU().MemoryManager()}; | ||
| 391 | const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())}; | ||
| 392 | if (!cpu_addr) { | ||
| 393 | return {{}, exctx}; | ||
| 394 | } | ||
| 395 | const auto params{SurfaceParams::CreateForTexture(system, config)}; | ||
| 396 | return GetSurfaceView(exctx, *cpu_addr, params, true); | ||
| 397 | } | ||
| 398 | |||
| 399 | ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) { | ||
| 400 | const auto& regs{system.GPU().Maxwell3D().regs}; | ||
| 401 | if (!regs.zeta.Address() || !regs.zeta_enable) { | ||
| 402 | return {{}, exctx}; | ||
| 403 | } | ||
| 404 | |||
| 405 | auto& memory_manager{system.GPU().MemoryManager()}; | ||
| 406 | const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())}; | ||
| 407 | if (!cpu_addr) { | ||
| 408 | return {{}, exctx}; | ||
| 409 | } | ||
| 410 | |||
| 411 | const auto depth_params{SurfaceParams::CreateForDepthBuffer( | ||
| 412 | system, regs.zeta_width, regs.zeta_height, regs.zeta.format, | ||
| 413 | regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, | ||
| 414 | regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; | ||
| 415 | return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents); | ||
| 416 | } | ||
| 417 | |||
| 418 | ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index, | ||
| 419 | bool preserve_contents) { | ||
| 420 | ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); | ||
| 421 | |||
| 422 | const auto& regs{system.GPU().Maxwell3D().regs}; | ||
| 423 | if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || | ||
| 424 | regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { | ||
| 425 | return {{}, exctx}; | ||
| 426 | } | ||
| 427 | |||
| 428 | auto& memory_manager{system.GPU().MemoryManager()}; | ||
| 429 | const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; | ||
| 430 | const auto cpu_addr{memory_manager.GpuToCpuAddress( | ||
| 431 | config.Address() + config.base_layer * config.layer_stride * sizeof(u32))}; | ||
| 432 | if (!cpu_addr) { | ||
| 433 | return {{}, exctx}; | ||
| 434 | } | ||
| 435 | |||
| 436 | return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index), | ||
| 437 | preserve_contents); | ||
| 438 | } | ||
| 439 | |||
| 440 | ResultType GetFermiSurface(TExecutionContext exctx, | ||
| 441 | const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||
| 442 | const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())}; | ||
| 443 | ASSERT(cpu_addr); | ||
| 444 | return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config), | ||
| 445 | true); | ||
| 446 | } | ||
| 447 | |||
| 448 | TSurface* TryFindFramebufferSurface(const u8* host_ptr) const { | ||
| 449 | const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; | ||
| 450 | return it != registered_surfaces.end() ? *it->second.begin() : nullptr; | ||
| 451 | } | ||
| 452 | |||
| 453 | protected: | ||
| 454 | TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | ||
| 455 | : system{system}, rasterizer{rasterizer} {} | ||
| 456 | |||
| 457 | ~TextureCache() = default; | ||
| 458 | |||
| 459 | virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr, | ||
| 460 | const SurfaceParams& params, bool preserve_contents, | ||
| 461 | const std::vector<TSurface*>& overlaps) = 0; | ||
| 462 | |||
| 463 | virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0; | ||
| 464 | |||
| 465 | void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) { | ||
| 466 | surface->Register(cpu_addr, host_ptr); | ||
| 467 | registered_surfaces.add({GetSurfaceInterval(surface), {surface}}); | ||
| 468 | rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1); | ||
| 469 | } | ||
| 470 | |||
| 471 | void Unregister(TSurface* surface) { | ||
| 472 | registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); | ||
| 473 | rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); | ||
| 474 | surface->Unregister(); | ||
| 475 | } | ||
| 476 | |||
| 477 | TSurface* GetUncachedSurface(const SurfaceParams& params) { | ||
| 478 | if (TSurface* surface = TryGetReservedSurface(params); surface) | ||
| 479 | return surface; | ||
| 480 | // No reserved surface available, create a new one and reserve it | ||
| 481 | auto new_surface{CreateSurface(params)}; | ||
| 482 | TSurface* surface{new_surface.get()}; | ||
| 483 | ReserveSurface(params, std::move(new_surface)); | ||
| 484 | return surface; | ||
| 485 | } | ||
| 486 | |||
| 487 | Core::System& system; | ||
| 488 | |||
| 489 | private: | ||
| 490 | ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params, | ||
| 491 | bool preserve_contents) { | ||
| 492 | const auto host_ptr{Memory::GetPointer(cpu_addr)}; | ||
| 493 | const auto cache_addr{ToCacheAddr(host_ptr)}; | ||
| 494 | const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())}; | ||
| 495 | if (overlaps.empty()) { | ||
| 496 | return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents); | ||
| 497 | } | ||
| 498 | |||
| 499 | if (overlaps.size() == 1) { | ||
| 500 | if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view) | ||
| 501 | return {view, exctx}; | ||
| 502 | } | ||
| 503 | |||
| 504 | TView* fast_view; | ||
| 505 | std::tie(fast_view, exctx) = | ||
| 506 | TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps); | ||
| 507 | |||
| 508 | for (TSurface* surface : overlaps) { | ||
| 509 | if (!fast_view) { | ||
| 510 | // Flush even when we don't care about the contents, to preserve memory not written | ||
| 511 | // by the new surface. | ||
| 512 | exctx = surface->FlushBuffer(exctx); | ||
| 513 | } | ||
| 514 | Unregister(surface); | ||
| 515 | } | ||
| 516 | |||
| 517 | if (fast_view) { | ||
| 518 | return {fast_view, exctx}; | ||
| 519 | } | ||
| 520 | |||
| 521 | return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents); | ||
| 522 | } | ||
| 523 | |||
| 524 | ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr, | ||
| 525 | const SurfaceParams& params, bool preserve_contents) { | ||
| 526 | TSurface* new_surface{GetUncachedSurface(params)}; | ||
| 527 | Register(new_surface, cpu_addr, host_ptr); | ||
| 528 | if (preserve_contents) { | ||
| 529 | exctx = LoadSurface(exctx, new_surface); | ||
| 530 | } | ||
| 531 | return {new_surface->GetView(cpu_addr, params), exctx}; | ||
| 532 | } | ||
| 533 | |||
| 534 | TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) { | ||
| 535 | surface->LoadBuffer(); | ||
| 536 | exctx = surface->UploadTexture(exctx); | ||
| 537 | surface->MarkAsModified(false); | ||
| 538 | return exctx; | ||
| 539 | } | ||
| 540 | |||
| 541 | std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const { | ||
| 542 | if (size == 0) { | ||
| 543 | return {}; | ||
| 544 | } | ||
| 545 | const IntervalType interval{cache_addr, cache_addr + size}; | ||
| 546 | |||
| 547 | std::vector<TSurface*> surfaces; | ||
| 548 | for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { | ||
| 549 | surfaces.push_back(*pair.second.begin()); | ||
| 550 | } | ||
| 551 | return surfaces; | ||
| 552 | } | ||
| 553 | |||
| 554 | void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) { | ||
| 555 | surface_reserve[params].push_back(std::move(surface)); | ||
| 556 | } | ||
| 557 | |||
| 558 | TSurface* TryGetReservedSurface(const SurfaceParams& params) { | ||
| 559 | auto search{surface_reserve.find(params)}; | ||
| 560 | if (search == surface_reserve.end()) { | ||
| 561 | return {}; | ||
| 562 | } | ||
| 563 | for (auto& surface : search->second) { | ||
| 564 | if (!surface->IsRegistered()) { | ||
| 565 | return surface.get(); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | return {}; | ||
| 569 | } | ||
| 570 | |||
| 571 | IntervalType GetSurfaceInterval(TSurface* surface) const { | ||
| 572 | return IntervalType::right_open(surface->GetCacheAddr(), | ||
| 573 | surface->GetCacheAddr() + surface->GetSizeInBytes()); | ||
| 574 | } | ||
| 575 | |||
| 576 | VideoCore::RasterizerInterface& rasterizer; | ||
| 577 | |||
| 578 | IntervalMap registered_surfaces; | ||
| 579 | |||
| 580 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have | ||
| 581 | /// previously been used. This is to prevent surfaces from being constantly created and | ||
| 582 | /// destroyed when used with different surface parameters. | ||
| 583 | std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve; | ||
| 584 | }; | ||
| 585 | |||
| 586 | } // namespace VideoCommon | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 732a1bf89..2eb86d6e5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -56,8 +56,6 @@ add_executable(yuzu | |||
| 56 | debugger/graphics/graphics_breakpoints.cpp | 56 | debugger/graphics/graphics_breakpoints.cpp |
| 57 | debugger/graphics/graphics_breakpoints.h | 57 | debugger/graphics/graphics_breakpoints.h |
| 58 | debugger/graphics/graphics_breakpoints_p.h | 58 | debugger/graphics/graphics_breakpoints_p.h |
| 59 | debugger/graphics/graphics_surface.cpp | ||
| 60 | debugger/graphics/graphics_surface.h | ||
| 61 | debugger/console.cpp | 59 | debugger/console.cpp |
| 62 | debugger/console.h | 60 | debugger/console.h |
| 63 | debugger/profiler.cpp | 61 | debugger/profiler.cpp |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7438fbc0a..c29f2d2dc 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -1,6 +1,13 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #include <QApplication> | 5 | #include <QApplication> |
| 2 | #include <QHBoxLayout> | 6 | #include <QHBoxLayout> |
| 3 | #include <QKeyEvent> | 7 | #include <QKeyEvent> |
| 8 | #include <QOffscreenSurface> | ||
| 9 | #include <QOpenGLWindow> | ||
| 10 | #include <QPainter> | ||
| 4 | #include <QScreen> | 11 | #include <QScreen> |
| 5 | #include <QWindow> | 12 | #include <QWindow> |
| 6 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| @@ -82,13 +89,36 @@ void EmuThread::run() { | |||
| 82 | render_window->moveContext(); | 89 | render_window->moveContext(); |
| 83 | } | 90 | } |
| 84 | 91 | ||
| 92 | class GGLContext : public Core::Frontend::GraphicsContext { | ||
| 93 | public: | ||
| 94 | explicit GGLContext(QOpenGLContext* shared_context) : surface() { | ||
| 95 | context = std::make_unique<QOpenGLContext>(shared_context); | ||
| 96 | surface.setFormat(shared_context->format()); | ||
| 97 | surface.create(); | ||
| 98 | } | ||
| 99 | |||
| 100 | void MakeCurrent() override { | ||
| 101 | context->makeCurrent(&surface); | ||
| 102 | } | ||
| 103 | |||
| 104 | void DoneCurrent() override { | ||
| 105 | context->doneCurrent(); | ||
| 106 | } | ||
| 107 | |||
| 108 | void SwapBuffers() override {} | ||
| 109 | |||
| 110 | private: | ||
| 111 | std::unique_ptr<QOpenGLContext> context; | ||
| 112 | QOffscreenSurface surface; | ||
| 113 | }; | ||
| 114 | |||
| 85 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | 115 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL |
| 86 | // context. | 116 | // context. |
| 87 | // The corresponding functionality is handled in EmuThread instead | 117 | // The corresponding functionality is handled in EmuThread instead |
| 88 | class GGLWidgetInternal : public QGLWidget { | 118 | class GGLWidgetInternal : public QOpenGLWindow { |
| 89 | public: | 119 | public: |
| 90 | GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) | 120 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) |
| 91 | : QGLWidget(fmt, parent), parent(parent) {} | 121 | : QOpenGLWindow(shared_context), parent(parent) {} |
| 92 | 122 | ||
| 93 | void paintEvent(QPaintEvent* ev) override { | 123 | void paintEvent(QPaintEvent* ev) override { |
| 94 | if (do_painting) { | 124 | if (do_painting) { |
| @@ -101,9 +131,51 @@ public: | |||
| 101 | parent->OnFramebufferSizeChanged(); | 131 | parent->OnFramebufferSizeChanged(); |
| 102 | } | 132 | } |
| 103 | 133 | ||
| 134 | void keyPressEvent(QKeyEvent* event) override { | ||
| 135 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 136 | } | ||
| 137 | |||
| 138 | void keyReleaseEvent(QKeyEvent* event) override { | ||
| 139 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 140 | } | ||
| 141 | |||
| 142 | void mousePressEvent(QMouseEvent* event) override { | ||
| 143 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 144 | return; // touch input is handled in TouchBeginEvent | ||
| 145 | |||
| 146 | const auto pos{event->pos()}; | ||
| 147 | if (event->button() == Qt::LeftButton) { | ||
| 148 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 149 | parent->TouchPressed(x, y); | ||
| 150 | } else if (event->button() == Qt::RightButton) { | ||
| 151 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | void mouseMoveEvent(QMouseEvent* event) override { | ||
| 156 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 157 | return; // touch input is handled in TouchUpdateEvent | ||
| 158 | |||
| 159 | const auto pos{event->pos()}; | ||
| 160 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 161 | parent->TouchMoved(x, y); | ||
| 162 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 163 | } | ||
| 164 | |||
| 165 | void mouseReleaseEvent(QMouseEvent* event) override { | ||
| 166 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 167 | return; // touch input is handled in TouchEndEvent | ||
| 168 | |||
| 169 | if (event->button() == Qt::LeftButton) | ||
| 170 | parent->TouchReleased(); | ||
| 171 | else if (event->button() == Qt::RightButton) | ||
| 172 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 173 | } | ||
| 174 | |||
| 104 | void DisablePainting() { | 175 | void DisablePainting() { |
| 105 | do_painting = false; | 176 | do_painting = false; |
| 106 | } | 177 | } |
| 178 | |||
| 107 | void EnablePainting() { | 179 | void EnablePainting() { |
| 108 | do_painting = true; | 180 | do_painting = true; |
| 109 | } | 181 | } |
| @@ -114,7 +186,7 @@ private: | |||
| 114 | }; | 186 | }; |
| 115 | 187 | ||
| 116 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 188 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 117 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { | 189 | : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) { |
| 118 | 190 | ||
| 119 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 191 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 120 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | 192 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); |
| @@ -137,19 +209,19 @@ void GRenderWindow::moveContext() { | |||
| 137 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) | 209 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) |
| 138 | ? emu_thread | 210 | ? emu_thread |
| 139 | : qApp->thread(); | 211 | : qApp->thread(); |
| 140 | child->context()->moveToThread(thread); | 212 | context->moveToThread(thread); |
| 141 | } | 213 | } |
| 142 | 214 | ||
| 143 | void GRenderWindow::SwapBuffers() { | 215 | void GRenderWindow::SwapBuffers() { |
| 144 | // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, | 216 | // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`, |
| 145 | // since we never call `doneCurrent` in this thread. | 217 | // since we never call `doneCurrent` in this thread. |
| 146 | // However: | 218 | // However: |
| 147 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called | 219 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called |
| 148 | // since the last time `swapBuffers` was executed; | 220 | // since the last time `swapBuffers` was executed; |
| 149 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. | 221 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. |
| 150 | child->makeCurrent(); | 222 | context->makeCurrent(child); |
| 151 | 223 | ||
| 152 | child->swapBuffers(); | 224 | context->swapBuffers(child); |
| 153 | if (!first_frame) { | 225 | if (!first_frame) { |
| 154 | emit FirstFrameDisplayed(); | 226 | emit FirstFrameDisplayed(); |
| 155 | first_frame = true; | 227 | first_frame = true; |
| @@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() { | |||
| 157 | } | 229 | } |
| 158 | 230 | ||
| 159 | void GRenderWindow::MakeCurrent() { | 231 | void GRenderWindow::MakeCurrent() { |
| 160 | child->makeCurrent(); | 232 | context->makeCurrent(child); |
| 161 | } | 233 | } |
| 162 | 234 | ||
| 163 | void GRenderWindow::DoneCurrent() { | 235 | void GRenderWindow::DoneCurrent() { |
| 164 | child->doneCurrent(); | 236 | context->doneCurrent(); |
| 165 | } | 237 | } |
| 166 | 238 | ||
| 167 | void GRenderWindow::PollEvents() {} | 239 | void GRenderWindow::PollEvents() {} |
| @@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {} | |||
| 174 | void GRenderWindow::OnFramebufferSizeChanged() { | 246 | void GRenderWindow::OnFramebufferSizeChanged() { |
| 175 | // Screen changes potentially incur a change in screen DPI, hence we should update the | 247 | // Screen changes potentially incur a change in screen DPI, hence we should update the |
| 176 | // framebuffer size | 248 | // framebuffer size |
| 177 | qreal pixelRatio = windowPixelRatio(); | 249 | qreal pixelRatio = GetWindowPixelRatio(); |
| 178 | unsigned width = child->QPaintDevice::width() * pixelRatio; | 250 | unsigned width = child->QPaintDevice::width() * pixelRatio; |
| 179 | unsigned height = child->QPaintDevice::height() * pixelRatio; | 251 | unsigned height = child->QPaintDevice::height() * pixelRatio; |
| 180 | UpdateCurrentFramebufferLayout(width, height); | 252 | UpdateCurrentFramebufferLayout(width, height); |
| 181 | } | 253 | } |
| 182 | 254 | ||
| 255 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | ||
| 256 | if (child) { | ||
| 257 | child->keyPressEvent(event); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { | ||
| 262 | if (child) { | ||
| 263 | child->keyReleaseEvent(event); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 183 | void GRenderWindow::BackupGeometry() { | 267 | void GRenderWindow::BackupGeometry() { |
| 184 | geometry = ((QGLWidget*)this)->saveGeometry(); | 268 | geometry = ((QWidget*)this)->saveGeometry(); |
| 185 | } | 269 | } |
| 186 | 270 | ||
| 187 | void GRenderWindow::RestoreGeometry() { | 271 | void GRenderWindow::RestoreGeometry() { |
| @@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 199 | // If we are a top-level widget, store the current geometry | 283 | // If we are a top-level widget, store the current geometry |
| 200 | // otherwise, store the last backup | 284 | // otherwise, store the last backup |
| 201 | if (parent() == nullptr) | 285 | if (parent() == nullptr) |
| 202 | return ((QGLWidget*)this)->saveGeometry(); | 286 | return ((QWidget*)this)->saveGeometry(); |
| 203 | else | 287 | else |
| 204 | return geometry; | 288 | return geometry; |
| 205 | } | 289 | } |
| 206 | 290 | ||
| 207 | qreal GRenderWindow::windowPixelRatio() const { | 291 | qreal GRenderWindow::GetWindowPixelRatio() const { |
| 208 | // windowHandle() might not be accessible until the window is displayed to screen. | 292 | // windowHandle() might not be accessible until the window is displayed to screen. |
| 209 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | 293 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; |
| 210 | } | 294 | } |
| 211 | 295 | ||
| 212 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { | 296 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { |
| 213 | const qreal pixel_ratio = windowPixelRatio(); | 297 | const qreal pixel_ratio = GetWindowPixelRatio(); |
| 214 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 298 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 215 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 299 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| 216 | } | 300 | } |
| @@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 220 | QWidget::closeEvent(event); | 304 | QWidget::closeEvent(event); |
| 221 | } | 305 | } |
| 222 | 306 | ||
| 223 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||
| 224 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 225 | } | ||
| 226 | |||
| 227 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 228 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 229 | } | ||
| 230 | |||
| 231 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||
| 232 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 233 | return; // touch input is handled in TouchBeginEvent | ||
| 234 | |||
| 235 | auto pos = event->pos(); | ||
| 236 | if (event->button() == Qt::LeftButton) { | ||
| 237 | const auto [x, y] = ScaleTouch(pos); | ||
| 238 | this->TouchPressed(x, y); | ||
| 239 | } else if (event->button() == Qt::RightButton) { | ||
| 240 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||
| 245 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 246 | return; // touch input is handled in TouchUpdateEvent | ||
| 247 | |||
| 248 | auto pos = event->pos(); | ||
| 249 | const auto [x, y] = ScaleTouch(pos); | ||
| 250 | this->TouchMoved(x, y); | ||
| 251 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 252 | } | ||
| 253 | |||
| 254 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||
| 255 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 256 | return; // touch input is handled in TouchEndEvent | ||
| 257 | |||
| 258 | if (event->button() == Qt::LeftButton) | ||
| 259 | this->TouchReleased(); | ||
| 260 | else if (event->button() == Qt::RightButton) | ||
| 261 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 262 | } | ||
| 263 | |||
| 264 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 307 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| 265 | // TouchBegin always has exactly one touch point, so take the .first() | 308 | // TouchBegin always has exactly one touch point, so take the .first() |
| 266 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | 309 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); |
| @@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { | |||
| 313 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | 356 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); |
| 314 | } | 357 | } |
| 315 | 358 | ||
| 359 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | ||
| 360 | return std::make_unique<GGLContext>(shared_context.get()); | ||
| 361 | } | ||
| 362 | |||
| 316 | void GRenderWindow::InitRenderTarget() { | 363 | void GRenderWindow::InitRenderTarget() { |
| 317 | if (child) { | 364 | shared_context.reset(); |
| 318 | delete child; | 365 | context.reset(); |
| 319 | } | ||
| 320 | 366 | ||
| 321 | if (layout()) { | 367 | delete child; |
| 322 | delete layout(); | 368 | child = nullptr; |
| 323 | } | 369 | |
| 370 | delete container; | ||
| 371 | container = nullptr; | ||
| 372 | |||
| 373 | delete layout(); | ||
| 324 | 374 | ||
| 325 | first_frame = false; | 375 | first_frame = false; |
| 326 | 376 | ||
| 327 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 377 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 328 | // WA_DontShowOnScreen, WA_DeleteOnClose | 378 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 329 | QGLFormat fmt; | 379 | QSurfaceFormat fmt; |
| 330 | fmt.setVersion(4, 3); | 380 | fmt.setVersion(4, 3); |
| 331 | fmt.setProfile(QGLFormat::CoreProfile); | 381 | fmt.setProfile(QSurfaceFormat::CoreProfile); |
| 382 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 383 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 384 | shared_context = std::make_unique<QOpenGLContext>(); | ||
| 385 | shared_context->setFormat(fmt); | ||
| 386 | shared_context->create(); | ||
| 387 | context = std::make_unique<QOpenGLContext>(); | ||
| 388 | context->setShareContext(shared_context.get()); | ||
| 389 | context->setFormat(fmt); | ||
| 390 | context->create(); | ||
| 332 | fmt.setSwapInterval(false); | 391 | fmt.setSwapInterval(false); |
| 333 | 392 | ||
| 334 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X | 393 | child = new GGLWidgetInternal(this, shared_context.get()); |
| 335 | fmt.setOption(QGL::NoDeprecatedFunctions); | 394 | container = QWidget::createWindowContainer(child, this); |
| 336 | 395 | ||
| 337 | child = new GGLWidgetInternal(fmt, this); | ||
| 338 | QBoxLayout* layout = new QHBoxLayout(this); | 396 | QBoxLayout* layout = new QHBoxLayout(this); |
| 339 | 397 | layout->addWidget(container); | |
| 340 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 341 | layout->addWidget(child); | ||
| 342 | layout->setMargin(0); | 398 | layout->setMargin(0); |
| 343 | setLayout(layout); | 399 | setLayout(layout); |
| 344 | 400 | ||
| 401 | // Reset minimum size to avoid unwanted resizes when this function is called for a second time. | ||
| 402 | setMinimumSize(1, 1); | ||
| 403 | |||
| 404 | // Show causes the window to actually be created and the OpenGL context as well, but we don't | ||
| 405 | // want the widget to be shown yet, so immediately hide it. | ||
| 406 | show(); | ||
| 407 | hide(); | ||
| 408 | |||
| 409 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 410 | child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 411 | container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 412 | |||
| 345 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 413 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 346 | 414 | ||
| 347 | OnFramebufferSizeChanged(); | 415 | OnFramebufferSizeChanged(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3183621bc..9608b959f 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <mutex> | 9 | #include <mutex> |
| 10 | #include <QGLWidget> | ||
| 11 | #include <QImage> | 10 | #include <QImage> |
| 12 | #include <QThread> | 11 | #include <QThread> |
| 12 | #include <QWidget> | ||
| 13 | #include "common/thread.h" | 13 | #include "common/thread.h" |
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/frontend/emu_window.h" | 15 | #include "core/frontend/emu_window.h" |
| @@ -21,6 +21,8 @@ class QTouchEvent; | |||
| 21 | class GGLWidgetInternal; | 21 | class GGLWidgetInternal; |
| 22 | class GMainWindow; | 22 | class GMainWindow; |
| 23 | class GRenderWindow; | 23 | class GRenderWindow; |
| 24 | class QSurface; | ||
| 25 | class QOpenGLContext; | ||
| 24 | 26 | ||
| 25 | namespace VideoCore { | 27 | namespace VideoCore { |
| 26 | enum class LoadCallbackStage; | 28 | enum class LoadCallbackStage; |
| @@ -121,25 +123,21 @@ public: | |||
| 121 | void MakeCurrent() override; | 123 | void MakeCurrent() override; |
| 122 | void DoneCurrent() override; | 124 | void DoneCurrent() override; |
| 123 | void PollEvents() override; | 125 | void PollEvents() override; |
| 126 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 127 | |||
| 128 | void ForwardKeyPressEvent(QKeyEvent* event); | ||
| 129 | void ForwardKeyReleaseEvent(QKeyEvent* event); | ||
| 124 | 130 | ||
| 125 | void BackupGeometry(); | 131 | void BackupGeometry(); |
| 126 | void RestoreGeometry(); | 132 | void RestoreGeometry(); |
| 127 | void restoreGeometry(const QByteArray& geometry); // overridden | 133 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 128 | QByteArray saveGeometry(); // overridden | 134 | QByteArray saveGeometry(); // overridden |
| 129 | 135 | ||
| 130 | qreal windowPixelRatio() const; | 136 | qreal GetWindowPixelRatio() const; |
| 137 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 131 | 138 | ||
| 132 | void closeEvent(QCloseEvent* event) override; | 139 | void closeEvent(QCloseEvent* event) override; |
| 133 | |||
| 134 | void keyPressEvent(QKeyEvent* event) override; | ||
| 135 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 136 | |||
| 137 | void mousePressEvent(QMouseEvent* event) override; | ||
| 138 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 139 | void mouseReleaseEvent(QMouseEvent* event) override; | ||
| 140 | |||
| 141 | bool event(QEvent* event) override; | 140 | bool event(QEvent* event) override; |
| 142 | |||
| 143 | void focusOutEvent(QFocusEvent* event) override; | 141 | void focusOutEvent(QFocusEvent* event) override; |
| 144 | 142 | ||
| 145 | void OnClientAreaResized(unsigned width, unsigned height); | 143 | void OnClientAreaResized(unsigned width, unsigned height); |
| @@ -161,7 +159,6 @@ signals: | |||
| 161 | void FirstFrameDisplayed(); | 159 | void FirstFrameDisplayed(); |
| 162 | 160 | ||
| 163 | private: | 161 | private: |
| 164 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 165 | void TouchBeginEvent(const QTouchEvent* event); | 162 | void TouchBeginEvent(const QTouchEvent* event); |
| 166 | void TouchUpdateEvent(const QTouchEvent* event); | 163 | void TouchUpdateEvent(const QTouchEvent* event); |
| 167 | void TouchEndEvent(); | 164 | void TouchEndEvent(); |
| @@ -169,11 +166,17 @@ private: | |||
| 169 | void OnMinimalClientAreaChangeRequest( | 166 | void OnMinimalClientAreaChangeRequest( |
| 170 | const std::pair<unsigned, unsigned>& minimal_size) override; | 167 | const std::pair<unsigned, unsigned>& minimal_size) override; |
| 171 | 168 | ||
| 172 | GGLWidgetInternal* child; | 169 | QWidget* container = nullptr; |
| 170 | GGLWidgetInternal* child = nullptr; | ||
| 173 | 171 | ||
| 174 | QByteArray geometry; | 172 | QByteArray geometry; |
| 175 | 173 | ||
| 176 | EmuThread* emu_thread; | 174 | EmuThread* emu_thread; |
| 175 | // Context that backs the GGLWidgetInternal (and will be used by core to render) | ||
| 176 | std::unique_ptr<QOpenGLContext> context; | ||
| 177 | // Context that will be shared between all newly created contexts. This should never be made | ||
| 178 | // current | ||
| 179 | std::unique_ptr<QOpenGLContext> shared_context; | ||
| 177 | 180 | ||
| 178 | /// Temporary storage of the screenshot taken | 181 | /// Temporary storage of the screenshot taken |
| 179 | QImage screenshot_image; | 182 | QImage screenshot_image; |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 802db3945..ca60bc0c9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -523,8 +523,8 @@ void Config::ReadValues() { | |||
| 523 | qt_config->beginGroup("Paths"); | 523 | qt_config->beginGroup("Paths"); |
| 524 | UISettings::values.roms_path = ReadSetting("romsPath").toString(); | 524 | UISettings::values.roms_path = ReadSetting("romsPath").toString(); |
| 525 | UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); | 525 | UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); |
| 526 | UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString(); | 526 | UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString(); |
| 527 | UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool(); | 527 | UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool(); |
| 528 | UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); | 528 | UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); |
| 529 | qt_config->endGroup(); | 529 | qt_config->endGroup(); |
| 530 | 530 | ||
| @@ -768,8 +768,8 @@ void Config::SaveValues() { | |||
| 768 | WriteSetting("romsPath", UISettings::values.roms_path); | 768 | WriteSetting("romsPath", UISettings::values.roms_path); |
| 769 | WriteSetting("symbolsPath", UISettings::values.symbols_path); | 769 | WriteSetting("symbolsPath", UISettings::values.symbols_path); |
| 770 | WriteSetting("screenshotPath", UISettings::values.screenshot_path); | 770 | WriteSetting("screenshotPath", UISettings::values.screenshot_path); |
| 771 | WriteSetting("gameListRootDir", UISettings::values.gamedir, "."); | 771 | WriteSetting("gameListRootDir", UISettings::values.game_directory_path, "."); |
| 772 | WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false); | 772 | WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false); |
| 773 | WriteSetting("recentFiles", UISettings::values.recent_files); | 773 | WriteSetting("recentFiles", UISettings::values.recent_files); |
| 774 | qt_config->endGroup(); | 774 | qt_config->endGroup(); |
| 775 | 775 | ||
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index eeb038afb..e48f4f5a3 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -28,7 +28,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 28 | ConfigureGeneral::~ConfigureGeneral() = default; | 28 | ConfigureGeneral::~ConfigureGeneral() = default; |
| 29 | 29 | ||
| 30 | void ConfigureGeneral::setConfiguration() { | 30 | void ConfigureGeneral::setConfiguration() { |
| 31 | ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); | 31 | ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan); |
| 32 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 32 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 33 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 33 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 34 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 34 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| @@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() { | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | void ConfigureGeneral::applyConfiguration() { | 38 | void ConfigureGeneral::applyConfiguration() { |
| 39 | UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); | 39 | UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked(); |
| 40 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | 40 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); |
| 41 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 41 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 42 | UISettings::values.theme = | 42 | UISettings::values.theme = |
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp deleted file mode 100644 index f2d14becf..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ /dev/null | |||
| @@ -1,516 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QBoxLayout> | ||
| 6 | #include <QComboBox> | ||
| 7 | #include <QDebug> | ||
| 8 | #include <QFileDialog> | ||
| 9 | #include <QLabel> | ||
| 10 | #include <QMessageBox> | ||
| 11 | #include <QMouseEvent> | ||
| 12 | #include <QPushButton> | ||
| 13 | #include <QScrollArea> | ||
| 14 | #include <QSpinBox> | ||
| 15 | #include "common/vector_math.h" | ||
| 16 | #include "core/core.h" | ||
| 17 | #include "core/memory.h" | ||
| 18 | #include "video_core/engines/maxwell_3d.h" | ||
| 19 | #include "video_core/gpu.h" | ||
| 20 | #include "video_core/textures/decoders.h" | ||
| 21 | #include "video_core/textures/texture.h" | ||
| 22 | #include "yuzu/debugger/graphics/graphics_surface.h" | ||
| 23 | #include "yuzu/util/spinbox.h" | ||
| 24 | |||
| 25 | static Tegra::Texture::TextureFormat ConvertToTextureFormat( | ||
| 26 | Tegra::RenderTargetFormat render_target_format) { | ||
| 27 | switch (render_target_format) { | ||
| 28 | case Tegra::RenderTargetFormat::RGBA8_UNORM: | ||
| 29 | return Tegra::Texture::TextureFormat::A8R8G8B8; | ||
| 30 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | ||
| 31 | return Tegra::Texture::TextureFormat::A2B10G10R10; | ||
| 32 | default: | ||
| 33 | UNIMPLEMENTED_MSG("Unimplemented RT format"); | ||
| 34 | return Tegra::Texture::TextureFormat::A8R8G8B8; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) | ||
| 39 | : QLabel(parent), surface_widget(surface_widget_) {} | ||
| 40 | |||
| 41 | SurfacePicture::~SurfacePicture() = default; | ||
| 42 | |||
| 43 | void SurfacePicture::mousePressEvent(QMouseEvent* event) { | ||
| 44 | // Only do something while the left mouse button is held down | ||
| 45 | if (!(event->buttons() & Qt::LeftButton)) | ||
| 46 | return; | ||
| 47 | |||
| 48 | if (pixmap() == nullptr) | ||
| 49 | return; | ||
| 50 | |||
| 51 | if (surface_widget) | ||
| 52 | surface_widget->Pick(event->x() * pixmap()->width() / width(), | ||
| 53 | event->y() * pixmap()->height() / height()); | ||
| 54 | } | ||
| 55 | |||
| 56 | void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { | ||
| 57 | // We also want to handle the event if the user moves the mouse while holding down the LMB | ||
| 58 | mousePressEvent(event); | ||
| 59 | } | ||
| 60 | |||
| 61 | GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||
| 62 | QWidget* parent) | ||
| 63 | : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent), | ||
| 64 | surface_source(Source::RenderTarget0) { | ||
| 65 | setObjectName("MaxwellSurface"); | ||
| 66 | |||
| 67 | surface_source_list = new QComboBox; | ||
| 68 | surface_source_list->addItem(tr("Render Target 0")); | ||
| 69 | surface_source_list->addItem(tr("Render Target 1")); | ||
| 70 | surface_source_list->addItem(tr("Render Target 2")); | ||
| 71 | surface_source_list->addItem(tr("Render Target 3")); | ||
| 72 | surface_source_list->addItem(tr("Render Target 4")); | ||
| 73 | surface_source_list->addItem(tr("Render Target 5")); | ||
| 74 | surface_source_list->addItem(tr("Render Target 6")); | ||
| 75 | surface_source_list->addItem(tr("Render Target 7")); | ||
| 76 | surface_source_list->addItem(tr("Z Buffer")); | ||
| 77 | surface_source_list->addItem(tr("Custom")); | ||
| 78 | surface_source_list->setCurrentIndex(static_cast<int>(surface_source)); | ||
| 79 | |||
| 80 | surface_address_control = new CSpinBox; | ||
| 81 | surface_address_control->SetBase(16); | ||
| 82 | surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF); | ||
| 83 | surface_address_control->SetPrefix("0x"); | ||
| 84 | |||
| 85 | unsigned max_dimension = 16384; // TODO: Find actual maximum | ||
| 86 | |||
| 87 | surface_width_control = new QSpinBox; | ||
| 88 | surface_width_control->setRange(0, max_dimension); | ||
| 89 | |||
| 90 | surface_height_control = new QSpinBox; | ||
| 91 | surface_height_control->setRange(0, max_dimension); | ||
| 92 | |||
| 93 | surface_picker_x_control = new QSpinBox; | ||
| 94 | surface_picker_x_control->setRange(0, max_dimension - 1); | ||
| 95 | |||
| 96 | surface_picker_y_control = new QSpinBox; | ||
| 97 | surface_picker_y_control->setRange(0, max_dimension - 1); | ||
| 98 | |||
| 99 | // clang-format off | ||
| 100 | // Color formats sorted by Maxwell texture format index | ||
| 101 | const QStringList surface_formats{ | ||
| 102 | tr("None"), | ||
| 103 | QStringLiteral("R32_G32_B32_A32"), | ||
| 104 | QStringLiteral("R32_G32_B32"), | ||
| 105 | QStringLiteral("R16_G16_B16_A16"), | ||
| 106 | QStringLiteral("R32_G32"), | ||
| 107 | QStringLiteral("R32_B24G8"), | ||
| 108 | QStringLiteral("ETC2_RGB"), | ||
| 109 | QStringLiteral("X8B8G8R8"), | ||
| 110 | QStringLiteral("A8R8G8B8"), | ||
| 111 | QStringLiteral("A2B10G10R10"), | ||
| 112 | QStringLiteral("ETC2_RGB_PTA"), | ||
| 113 | QStringLiteral("ETC2_RGBA"), | ||
| 114 | QStringLiteral("R16_G16"), | ||
| 115 | QStringLiteral("G8R24"), | ||
| 116 | QStringLiteral("G24R8"), | ||
| 117 | QStringLiteral("R32"), | ||
| 118 | QStringLiteral("BC6H_SF16"), | ||
| 119 | QStringLiteral("BC6H_UF16"), | ||
| 120 | QStringLiteral("A4B4G4R4"), | ||
| 121 | QStringLiteral("A5B5G5R1"), | ||
| 122 | QStringLiteral("A1B5G5R5"), | ||
| 123 | QStringLiteral("B5G6R5"), | ||
| 124 | QStringLiteral("B6G5R5"), | ||
| 125 | QStringLiteral("BC7U"), | ||
| 126 | QStringLiteral("G8R8"), | ||
| 127 | QStringLiteral("EAC"), | ||
| 128 | QStringLiteral("EACX2"), | ||
| 129 | QStringLiteral("R16"), | ||
| 130 | QStringLiteral("Y8_VIDEO"), | ||
| 131 | QStringLiteral("R8"), | ||
| 132 | QStringLiteral("G4R4"), | ||
| 133 | QStringLiteral("R1"), | ||
| 134 | QStringLiteral("E5B9G9R9_SHAREDEXP"), | ||
| 135 | QStringLiteral("BF10GF11RF11"), | ||
| 136 | QStringLiteral("G8B8G8R8"), | ||
| 137 | QStringLiteral("B8G8R8G8"), | ||
| 138 | QStringLiteral("DXT1"), | ||
| 139 | QStringLiteral("DXT23"), | ||
| 140 | QStringLiteral("DXT45"), | ||
| 141 | QStringLiteral("DXN1"), | ||
| 142 | QStringLiteral("DXN2"), | ||
| 143 | QStringLiteral("Z24S8"), | ||
| 144 | QStringLiteral("X8Z24"), | ||
| 145 | QStringLiteral("S8Z24"), | ||
| 146 | QStringLiteral("X4V4Z24__COV4R4V"), | ||
| 147 | QStringLiteral("X4V4Z24__COV8R8V"), | ||
| 148 | QStringLiteral("V8Z24__COV4R12V"), | ||
| 149 | QStringLiteral("ZF32"), | ||
| 150 | QStringLiteral("ZF32_X24S8"), | ||
| 151 | QStringLiteral("X8Z24_X20V4S8__COV4R4V"), | ||
| 152 | QStringLiteral("X8Z24_X20V4S8__COV8R8V"), | ||
| 153 | QStringLiteral("ZF32_X20V4X8__COV4R4V"), | ||
| 154 | QStringLiteral("ZF32_X20V4X8__COV8R8V"), | ||
| 155 | QStringLiteral("ZF32_X20V4S8__COV4R4V"), | ||
| 156 | QStringLiteral("ZF32_X20V4S8__COV8R8V"), | ||
| 157 | QStringLiteral("X8Z24_X16V8S8__COV4R12V"), | ||
| 158 | QStringLiteral("ZF32_X16V8X8__COV4R12V"), | ||
| 159 | QStringLiteral("ZF32_X16V8S8__COV4R12V"), | ||
| 160 | QStringLiteral("Z16"), | ||
| 161 | QStringLiteral("V8Z24__COV8R24V"), | ||
| 162 | QStringLiteral("X8Z24_X16V8S8__COV8R24V"), | ||
| 163 | QStringLiteral("ZF32_X16V8X8__COV8R24V"), | ||
| 164 | QStringLiteral("ZF32_X16V8S8__COV8R24V"), | ||
| 165 | QStringLiteral("ASTC_2D_4X4"), | ||
| 166 | QStringLiteral("ASTC_2D_5X5"), | ||
| 167 | QStringLiteral("ASTC_2D_6X6"), | ||
| 168 | QStringLiteral("ASTC_2D_8X8"), | ||
| 169 | QStringLiteral("ASTC_2D_10X10"), | ||
| 170 | QStringLiteral("ASTC_2D_12X12"), | ||
| 171 | QStringLiteral("ASTC_2D_5X4"), | ||
| 172 | QStringLiteral("ASTC_2D_6X5"), | ||
| 173 | QStringLiteral("ASTC_2D_8X6"), | ||
| 174 | QStringLiteral("ASTC_2D_10X8"), | ||
| 175 | QStringLiteral("ASTC_2D_12X10"), | ||
| 176 | QStringLiteral("ASTC_2D_8X5"), | ||
| 177 | QStringLiteral("ASTC_2D_10X5"), | ||
| 178 | QStringLiteral("ASTC_2D_10X6"), | ||
| 179 | }; | ||
| 180 | // clang-format on | ||
| 181 | |||
| 182 | surface_format_control = new QComboBox; | ||
| 183 | surface_format_control->addItems(surface_formats); | ||
| 184 | |||
| 185 | surface_info_label = new QLabel(); | ||
| 186 | surface_info_label->setWordWrap(true); | ||
| 187 | |||
| 188 | surface_picture_label = new SurfacePicture(0, this); | ||
| 189 | surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | ||
| 190 | surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); | ||
| 191 | surface_picture_label->setScaledContents(false); | ||
| 192 | |||
| 193 | auto scroll_area = new QScrollArea(); | ||
| 194 | scroll_area->setBackgroundRole(QPalette::Dark); | ||
| 195 | scroll_area->setWidgetResizable(false); | ||
| 196 | scroll_area->setWidget(surface_picture_label); | ||
| 197 | |||
| 198 | save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); | ||
| 199 | |||
| 200 | // Connections | ||
| 201 | connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); | ||
| 202 | connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||
| 203 | &GraphicsSurfaceWidget::OnSurfaceSourceChanged); | ||
| 204 | connect(surface_address_control, &CSpinBox::ValueChanged, this, | ||
| 205 | &GraphicsSurfaceWidget::OnSurfaceAddressChanged); | ||
| 206 | connect(surface_width_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 207 | &GraphicsSurfaceWidget::OnSurfaceWidthChanged); | ||
| 208 | connect(surface_height_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 209 | &GraphicsSurfaceWidget::OnSurfaceHeightChanged); | ||
| 210 | connect(surface_format_control, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||
| 211 | &GraphicsSurfaceWidget::OnSurfaceFormatChanged); | ||
| 212 | connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 213 | &GraphicsSurfaceWidget::OnSurfacePickerXChanged); | ||
| 214 | connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 215 | &GraphicsSurfaceWidget::OnSurfacePickerYChanged); | ||
| 216 | connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); | ||
| 217 | |||
| 218 | auto main_widget = new QWidget; | ||
| 219 | auto main_layout = new QVBoxLayout; | ||
| 220 | { | ||
| 221 | auto sub_layout = new QHBoxLayout; | ||
| 222 | sub_layout->addWidget(new QLabel(tr("Source:"))); | ||
| 223 | sub_layout->addWidget(surface_source_list); | ||
| 224 | main_layout->addLayout(sub_layout); | ||
| 225 | } | ||
| 226 | { | ||
| 227 | auto sub_layout = new QHBoxLayout; | ||
| 228 | sub_layout->addWidget(new QLabel(tr("GPU Address:"))); | ||
| 229 | sub_layout->addWidget(surface_address_control); | ||
| 230 | main_layout->addLayout(sub_layout); | ||
| 231 | } | ||
| 232 | { | ||
| 233 | auto sub_layout = new QHBoxLayout; | ||
| 234 | sub_layout->addWidget(new QLabel(tr("Width:"))); | ||
| 235 | sub_layout->addWidget(surface_width_control); | ||
| 236 | main_layout->addLayout(sub_layout); | ||
| 237 | } | ||
| 238 | { | ||
| 239 | auto sub_layout = new QHBoxLayout; | ||
| 240 | sub_layout->addWidget(new QLabel(tr("Height:"))); | ||
| 241 | sub_layout->addWidget(surface_height_control); | ||
| 242 | main_layout->addLayout(sub_layout); | ||
| 243 | } | ||
| 244 | { | ||
| 245 | auto sub_layout = new QHBoxLayout; | ||
| 246 | sub_layout->addWidget(new QLabel(tr("Format:"))); | ||
| 247 | sub_layout->addWidget(surface_format_control); | ||
| 248 | main_layout->addLayout(sub_layout); | ||
| 249 | } | ||
| 250 | main_layout->addWidget(scroll_area); | ||
| 251 | |||
| 252 | auto info_layout = new QHBoxLayout; | ||
| 253 | { | ||
| 254 | auto xy_layout = new QVBoxLayout; | ||
| 255 | { | ||
| 256 | { | ||
| 257 | auto sub_layout = new QHBoxLayout; | ||
| 258 | sub_layout->addWidget(new QLabel(tr("X:"))); | ||
| 259 | sub_layout->addWidget(surface_picker_x_control); | ||
| 260 | xy_layout->addLayout(sub_layout); | ||
| 261 | } | ||
| 262 | { | ||
| 263 | auto sub_layout = new QHBoxLayout; | ||
| 264 | sub_layout->addWidget(new QLabel(tr("Y:"))); | ||
| 265 | sub_layout->addWidget(surface_picker_y_control); | ||
| 266 | xy_layout->addLayout(sub_layout); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | info_layout->addLayout(xy_layout); | ||
| 270 | surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); | ||
| 271 | info_layout->addWidget(surface_info_label); | ||
| 272 | } | ||
| 273 | main_layout->addLayout(info_layout); | ||
| 274 | |||
| 275 | main_layout->addWidget(save_surface); | ||
| 276 | main_widget->setLayout(main_layout); | ||
| 277 | setWidget(main_widget); | ||
| 278 | |||
| 279 | // Load current data - TODO: Make sure this works when emulation is not running | ||
| 280 | if (debug_context && debug_context->at_breakpoint) { | ||
| 281 | emit Update(); | ||
| 282 | widget()->setEnabled(debug_context->at_breakpoint); | ||
| 283 | } else { | ||
| 284 | widget()->setEnabled(false); | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { | ||
| 289 | emit Update(); | ||
| 290 | widget()->setEnabled(true); | ||
| 291 | } | ||
| 292 | |||
| 293 | void GraphicsSurfaceWidget::OnResumed() { | ||
| 294 | widget()->setEnabled(false); | ||
| 295 | } | ||
| 296 | |||
| 297 | void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { | ||
| 298 | surface_source = static_cast<Source>(new_value); | ||
| 299 | emit Update(); | ||
| 300 | } | ||
| 301 | |||
| 302 | void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { | ||
| 303 | if (surface_address != new_value) { | ||
| 304 | surface_address = static_cast<GPUVAddr>(new_value); | ||
| 305 | |||
| 306 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 307 | emit Update(); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) { | ||
| 312 | if (surface_width != static_cast<unsigned>(new_value)) { | ||
| 313 | surface_width = static_cast<unsigned>(new_value); | ||
| 314 | |||
| 315 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 316 | emit Update(); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) { | ||
| 321 | if (surface_height != static_cast<unsigned>(new_value)) { | ||
| 322 | surface_height = static_cast<unsigned>(new_value); | ||
| 323 | |||
| 324 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 325 | emit Update(); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) { | ||
| 330 | if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) { | ||
| 331 | surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value); | ||
| 332 | |||
| 333 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 334 | emit Update(); | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) { | ||
| 339 | if (surface_picker_x != new_value) { | ||
| 340 | surface_picker_x = new_value; | ||
| 341 | Pick(surface_picker_x, surface_picker_y); | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) { | ||
| 346 | if (surface_picker_y != new_value) { | ||
| 347 | surface_picker_y = new_value; | ||
| 348 | Pick(surface_picker_x, surface_picker_y); | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | void GraphicsSurfaceWidget::Pick(int x, int y) { | ||
| 353 | surface_picker_x_control->setValue(x); | ||
| 354 | surface_picker_y_control->setValue(y); | ||
| 355 | |||
| 356 | if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 || | ||
| 357 | y >= static_cast<int>(surface_height)) { | ||
| 358 | surface_info_label->setText(tr("Pixel out of bounds")); | ||
| 359 | surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||
| 360 | return; | ||
| 361 | } | ||
| 362 | |||
| 363 | surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>")); | ||
| 364 | surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||
| 365 | } | ||
| 366 | |||
| 367 | void GraphicsSurfaceWidget::OnUpdate() { | ||
| 368 | auto& gpu = Core::System::GetInstance().GPU(); | ||
| 369 | |||
| 370 | QPixmap pixmap; | ||
| 371 | |||
| 372 | switch (surface_source) { | ||
| 373 | case Source::RenderTarget0: | ||
| 374 | case Source::RenderTarget1: | ||
| 375 | case Source::RenderTarget2: | ||
| 376 | case Source::RenderTarget3: | ||
| 377 | case Source::RenderTarget4: | ||
| 378 | case Source::RenderTarget5: | ||
| 379 | case Source::RenderTarget6: | ||
| 380 | case Source::RenderTarget7: { | ||
| 381 | // TODO: Store a reference to the registers in the debug context instead of accessing them | ||
| 382 | // directly... | ||
| 383 | |||
| 384 | const auto& registers = gpu.Maxwell3D().regs; | ||
| 385 | const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - | ||
| 386 | static_cast<std::size_t>(Source::RenderTarget0)]; | ||
| 387 | |||
| 388 | surface_address = rt.Address(); | ||
| 389 | surface_width = rt.width; | ||
| 390 | surface_height = rt.height; | ||
| 391 | if (rt.format != Tegra::RenderTargetFormat::NONE) { | ||
| 392 | surface_format = ConvertToTextureFormat(rt.format); | ||
| 393 | } | ||
| 394 | |||
| 395 | break; | ||
| 396 | } | ||
| 397 | |||
| 398 | case Source::Custom: { | ||
| 399 | // Keep user-specified values | ||
| 400 | break; | ||
| 401 | } | ||
| 402 | |||
| 403 | default: | ||
| 404 | qDebug() << "Unknown surface source " << static_cast<int>(surface_source); | ||
| 405 | break; | ||
| 406 | } | ||
| 407 | |||
| 408 | surface_address_control->SetValue(surface_address); | ||
| 409 | surface_width_control->setValue(surface_width); | ||
| 410 | surface_height_control->setValue(surface_height); | ||
| 411 | surface_format_control->setCurrentIndex(static_cast<int>(surface_format)); | ||
| 412 | |||
| 413 | if (surface_address == 0) { | ||
| 414 | surface_picture_label->hide(); | ||
| 415 | surface_info_label->setText(tr("(invalid surface address)")); | ||
| 416 | surface_info_label->setAlignment(Qt::AlignCenter); | ||
| 417 | surface_picker_x_control->setEnabled(false); | ||
| 418 | surface_picker_y_control->setEnabled(false); | ||
| 419 | save_surface->setEnabled(false); | ||
| 420 | return; | ||
| 421 | } | ||
| 422 | |||
| 423 | // TODO: Implement a good way to visualize alpha components! | ||
| 424 | |||
| 425 | QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); | ||
| 426 | |||
| 427 | // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. | ||
| 428 | // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. | ||
| 429 | auto unswizzled_data = Tegra::Texture::UnswizzleTexture( | ||
| 430 | gpu.MemoryManager().GetPointer(surface_address), 1, 1, | ||
| 431 | Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U); | ||
| 432 | |||
| 433 | auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, | ||
| 434 | surface_width, surface_height); | ||
| 435 | |||
| 436 | surface_picture_label->show(); | ||
| 437 | |||
| 438 | for (unsigned int y = 0; y < surface_height; ++y) { | ||
| 439 | for (unsigned int x = 0; x < surface_width; ++x) { | ||
| 440 | Common::Vec4<u8> color; | ||
| 441 | color[0] = texture_data[x + y * surface_width + 0]; | ||
| 442 | color[1] = texture_data[x + y * surface_width + 1]; | ||
| 443 | color[2] = texture_data[x + y * surface_width + 2]; | ||
| 444 | color[3] = texture_data[x + y * surface_width + 3]; | ||
| 445 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | pixmap = QPixmap::fromImage(decoded_image); | ||
| 450 | surface_picture_label->setPixmap(pixmap); | ||
| 451 | surface_picture_label->resize(pixmap.size()); | ||
| 452 | |||
| 453 | // Update the info with pixel data | ||
| 454 | surface_picker_x_control->setEnabled(true); | ||
| 455 | surface_picker_y_control->setEnabled(true); | ||
| 456 | Pick(surface_picker_x, surface_picker_y); | ||
| 457 | |||
| 458 | // Enable saving the converted pixmap to file | ||
| 459 | save_surface->setEnabled(true); | ||
| 460 | } | ||
| 461 | |||
| 462 | void GraphicsSurfaceWidget::SaveSurface() { | ||
| 463 | const QString png_filter = tr("Portable Network Graphic (*.png)"); | ||
| 464 | const QString bin_filter = tr("Binary data (*.bin)"); | ||
| 465 | |||
| 466 | QString selected_filter; | ||
| 467 | const QString filename = QFileDialog::getSaveFileName( | ||
| 468 | this, tr("Save Surface"), | ||
| 469 | QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)), | ||
| 470 | QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter); | ||
| 471 | |||
| 472 | if (filename.isEmpty()) { | ||
| 473 | // If the user canceled the dialog, don't save anything. | ||
| 474 | return; | ||
| 475 | } | ||
| 476 | |||
| 477 | if (selected_filter == png_filter) { | ||
| 478 | const QPixmap* const pixmap = surface_picture_label->pixmap(); | ||
| 479 | ASSERT_MSG(pixmap != nullptr, "No pixmap set"); | ||
| 480 | |||
| 481 | QFile file{filename}; | ||
| 482 | if (!file.open(QIODevice::WriteOnly)) { | ||
| 483 | QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); | ||
| 484 | return; | ||
| 485 | } | ||
| 486 | |||
| 487 | if (!pixmap->save(&file, "PNG")) { | ||
| 488 | QMessageBox::warning(this, tr("Error"), | ||
| 489 | tr("Failed to save surface data to file '%1'").arg(filename)); | ||
| 490 | } | ||
| 491 | } else if (selected_filter == bin_filter) { | ||
| 492 | auto& gpu = Core::System::GetInstance().GPU(); | ||
| 493 | const std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); | ||
| 494 | |||
| 495 | const u8* const buffer = Memory::GetPointer(*address); | ||
| 496 | ASSERT_MSG(buffer != nullptr, "Memory not accessible"); | ||
| 497 | |||
| 498 | QFile file{filename}; | ||
| 499 | if (!file.open(QIODevice::WriteOnly)) { | ||
| 500 | QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); | ||
| 501 | return; | ||
| 502 | } | ||
| 503 | |||
| 504 | const int size = | ||
| 505 | surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); | ||
| 506 | const QByteArray data(reinterpret_cast<const char*>(buffer), size); | ||
| 507 | if (file.write(data) != data.size()) { | ||
| 508 | QMessageBox::warning( | ||
| 509 | this, tr("Error"), | ||
| 510 | tr("Failed to completely write surface data to file. The saved data will " | ||
| 511 | "likely be corrupt.")); | ||
| 512 | } | ||
| 513 | } else { | ||
| 514 | UNREACHABLE_MSG("Unhandled filter selected"); | ||
| 515 | } | ||
| 516 | } | ||
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h deleted file mode 100644 index 89445b18f..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.h +++ /dev/null | |||
| @@ -1,96 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QLabel> | ||
| 8 | #include <QPushButton> | ||
| 9 | #include "video_core/memory_manager.h" | ||
| 10 | #include "video_core/textures/texture.h" | ||
| 11 | #include "yuzu/debugger/graphics/graphics_breakpoint_observer.h" | ||
| 12 | |||
| 13 | class QComboBox; | ||
| 14 | class QSpinBox; | ||
| 15 | class CSpinBox; | ||
| 16 | |||
| 17 | class GraphicsSurfaceWidget; | ||
| 18 | |||
| 19 | class SurfacePicture : public QLabel { | ||
| 20 | Q_OBJECT | ||
| 21 | |||
| 22 | public: | ||
| 23 | explicit SurfacePicture(QWidget* parent = nullptr, | ||
| 24 | GraphicsSurfaceWidget* surface_widget = nullptr); | ||
| 25 | ~SurfacePicture() override; | ||
| 26 | |||
| 27 | protected slots: | ||
| 28 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 29 | void mousePressEvent(QMouseEvent* event) override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | GraphicsSurfaceWidget* surface_widget; | ||
| 33 | }; | ||
| 34 | |||
| 35 | class GraphicsSurfaceWidget : public BreakPointObserverDock { | ||
| 36 | Q_OBJECT | ||
| 37 | |||
| 38 | using Event = Tegra::DebugContext::Event; | ||
| 39 | |||
| 40 | enum class Source { | ||
| 41 | RenderTarget0 = 0, | ||
| 42 | RenderTarget1 = 1, | ||
| 43 | RenderTarget2 = 2, | ||
| 44 | RenderTarget3 = 3, | ||
| 45 | RenderTarget4 = 4, | ||
| 46 | RenderTarget5 = 5, | ||
| 47 | RenderTarget6 = 6, | ||
| 48 | RenderTarget7 = 7, | ||
| 49 | ZBuffer = 8, | ||
| 50 | Custom = 9, | ||
| 51 | }; | ||
| 52 | |||
| 53 | public: | ||
| 54 | explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||
| 55 | QWidget* parent = nullptr); | ||
| 56 | void Pick(int x, int y); | ||
| 57 | |||
| 58 | public slots: | ||
| 59 | void OnSurfaceSourceChanged(int new_value); | ||
| 60 | void OnSurfaceAddressChanged(qint64 new_value); | ||
| 61 | void OnSurfaceWidthChanged(int new_value); | ||
| 62 | void OnSurfaceHeightChanged(int new_value); | ||
| 63 | void OnSurfaceFormatChanged(int new_value); | ||
| 64 | void OnSurfacePickerXChanged(int new_value); | ||
| 65 | void OnSurfacePickerYChanged(int new_value); | ||
| 66 | void OnUpdate(); | ||
| 67 | |||
| 68 | signals: | ||
| 69 | void Update(); | ||
| 70 | |||
| 71 | private: | ||
| 72 | void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; | ||
| 73 | void OnResumed() override; | ||
| 74 | |||
| 75 | void SaveSurface(); | ||
| 76 | |||
| 77 | QComboBox* surface_source_list; | ||
| 78 | CSpinBox* surface_address_control; | ||
| 79 | QSpinBox* surface_width_control; | ||
| 80 | QSpinBox* surface_height_control; | ||
| 81 | QComboBox* surface_format_control; | ||
| 82 | |||
| 83 | SurfacePicture* surface_picture_label; | ||
| 84 | QSpinBox* surface_picker_x_control; | ||
| 85 | QSpinBox* surface_picker_y_control; | ||
| 86 | QLabel* surface_info_label; | ||
| 87 | QPushButton* save_surface; | ||
| 88 | |||
| 89 | Source surface_source; | ||
| 90 | GPUVAddr surface_address; | ||
| 91 | unsigned surface_width; | ||
| 92 | unsigned surface_height; | ||
| 93 | Tegra::Texture::TextureFormat surface_format; | ||
| 94 | int surface_picker_x = 0; | ||
| 95 | int surface_picker_y = 0; | ||
| 96 | }; | ||
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 4b67656ac..b0ca766ec 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -467,9 +467,10 @@ void GameList::LoadInterfaceLayout() { | |||
| 467 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; | 467 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; |
| 468 | 468 | ||
| 469 | void GameList::RefreshGameDirectory() { | 469 | void GameList::RefreshGameDirectory() { |
| 470 | if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { | 470 | if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { |
| 471 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | 471 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); |
| 472 | search_field->clear(); | 472 | search_field->clear(); |
| 473 | PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 473 | PopulateAsync(UISettings::values.game_directory_path, |
| 474 | UISettings::values.game_directory_deepscan); | ||
| 474 | } | 475 | } |
| 475 | } | 476 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d5a328d92..bdee44b04 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -90,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 90 | #include "yuzu/configuration/configure_dialog.h" | 90 | #include "yuzu/configuration/configure_dialog.h" |
| 91 | #include "yuzu/debugger/console.h" | 91 | #include "yuzu/debugger/console.h" |
| 92 | #include "yuzu/debugger/graphics/graphics_breakpoints.h" | 92 | #include "yuzu/debugger/graphics/graphics_breakpoints.h" |
| 93 | #include "yuzu/debugger/graphics/graphics_surface.h" | ||
| 94 | #include "yuzu/debugger/profiler.h" | 93 | #include "yuzu/debugger/profiler.h" |
| 95 | #include "yuzu/debugger/wait_tree.h" | 94 | #include "yuzu/debugger/wait_tree.h" |
| 96 | #include "yuzu/discord.h" | 95 | #include "yuzu/discord.h" |
| @@ -214,7 +213,8 @@ GMainWindow::GMainWindow() | |||
| 214 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); | 213 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); |
| 215 | 214 | ||
| 216 | game_list->LoadCompatibilityList(); | 215 | game_list->LoadCompatibilityList(); |
| 217 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 216 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 217 | UISettings::values.game_directory_deepscan); | ||
| 218 | 218 | ||
| 219 | // Show one-time "callout" messages to the user | 219 | // Show one-time "callout" messages to the user |
| 220 | ShowTelemetryCallout(); | 220 | ShowTelemetryCallout(); |
| @@ -483,11 +483,6 @@ void GMainWindow::InitializeDebugWidgets() { | |||
| 483 | graphicsBreakpointsWidget->hide(); | 483 | graphicsBreakpointsWidget->hide(); |
| 484 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | 484 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); |
| 485 | 485 | ||
| 486 | graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this); | ||
| 487 | addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget); | ||
| 488 | graphicsSurfaceWidget->hide(); | ||
| 489 | debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction()); | ||
| 490 | |||
| 491 | waitTreeWidget = new WaitTreeWidget(this); | 486 | waitTreeWidget = new WaitTreeWidget(this); |
| 492 | addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); | 487 | addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); |
| 493 | waitTreeWidget->hide(); | 488 | waitTreeWidget->hide(); |
| @@ -1284,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | |||
| 1284 | 1279 | ||
| 1285 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | 1280 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); |
| 1286 | if (reload) { | 1281 | if (reload) { |
| 1287 | game_list->PopulateAsync(UISettings::values.gamedir, | 1282 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1288 | UISettings::values.gamedir_deepscan); | 1283 | UISettings::values.game_directory_deepscan); |
| 1289 | } | 1284 | } |
| 1290 | 1285 | ||
| 1291 | config->Save(); | 1286 | config->Save(); |
| @@ -1373,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1373 | const auto success = [this]() { | 1368 | const auto success = [this]() { |
| 1374 | QMessageBox::information(this, tr("Successfully Installed"), | 1369 | QMessageBox::information(this, tr("Successfully Installed"), |
| 1375 | tr("The file was successfully installed.")); | 1370 | tr("The file was successfully installed.")); |
| 1376 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1371 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1372 | UISettings::values.game_directory_deepscan); | ||
| 1377 | }; | 1373 | }; |
| 1378 | 1374 | ||
| 1379 | const auto failed = [this]() { | 1375 | const auto failed = [this]() { |
| @@ -1500,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1500 | void GMainWindow::OnMenuSelectGameListRoot() { | 1496 | void GMainWindow::OnMenuSelectGameListRoot() { |
| 1501 | QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); | 1497 | QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); |
| 1502 | if (!dir_path.isEmpty()) { | 1498 | if (!dir_path.isEmpty()) { |
| 1503 | UISettings::values.gamedir = dir_path; | 1499 | UISettings::values.game_directory_path = dir_path; |
| 1504 | game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); | 1500 | game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan); |
| 1505 | } | 1501 | } |
| 1506 | } | 1502 | } |
| 1507 | 1503 | ||
| @@ -1523,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) | |||
| 1523 | : FileUtil::UserPath::NANDDir, | 1519 | : FileUtil::UserPath::NANDDir, |
| 1524 | dir_path.toStdString()); | 1520 | dir_path.toStdString()); |
| 1525 | Service::FileSystem::CreateFactories(*vfs); | 1521 | Service::FileSystem::CreateFactories(*vfs); |
| 1526 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1522 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1523 | UISettings::values.game_directory_deepscan); | ||
| 1527 | } | 1524 | } |
| 1528 | } | 1525 | } |
| 1529 | 1526 | ||
| @@ -1675,8 +1672,8 @@ void GMainWindow::OnConfigure() { | |||
| 1675 | 1672 | ||
| 1676 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | 1673 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); |
| 1677 | if (reload) { | 1674 | if (reload) { |
| 1678 | game_list->PopulateAsync(UISettings::values.gamedir, | 1675 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1679 | UISettings::values.gamedir_deepscan); | 1676 | UISettings::values.game_directory_deepscan); |
| 1680 | } | 1677 | } |
| 1681 | 1678 | ||
| 1682 | config->Save(); | 1679 | config->Save(); |
| @@ -1926,7 +1923,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 1926 | Service::FileSystem::CreateFactories(*vfs); | 1923 | Service::FileSystem::CreateFactories(*vfs); |
| 1927 | 1924 | ||
| 1928 | if (behavior == ReinitializeKeyBehavior::Warning) { | 1925 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 1929 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1926 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1927 | UISettings::values.game_directory_deepscan); | ||
| 1930 | } | 1928 | } |
| 1931 | } | 1929 | } |
| 1932 | 1930 | ||
| @@ -2033,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | |||
| 2033 | event->acceptProposedAction(); | 2031 | event->acceptProposedAction(); |
| 2034 | } | 2032 | } |
| 2035 | 2033 | ||
| 2034 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2035 | if (render_window) { | ||
| 2036 | render_window->ForwardKeyPressEvent(event); | ||
| 2037 | } | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2041 | if (render_window) { | ||
| 2042 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 2036 | bool GMainWindow::ConfirmChangeGame() { | 2046 | bool GMainWindow::ConfirmChangeGame() { |
| 2037 | if (emu_thread == nullptr) | 2047 | if (emu_thread == nullptr) |
| 2038 | return true; | 2048 | return true; |
| @@ -2100,7 +2110,8 @@ int main(int argc, char* argv[]) { | |||
| 2100 | QCoreApplication::setOrganizationName("yuzu team"); | 2110 | QCoreApplication::setOrganizationName("yuzu team"); |
| 2101 | QCoreApplication::setApplicationName("yuzu"); | 2111 | QCoreApplication::setApplicationName("yuzu"); |
| 2102 | 2112 | ||
| 2103 | QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2113 | // Enables the core to make the qt created contexts current on std::threads |
| 2114 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | ||
| 2104 | QApplication app(argc, argv); | 2115 | QApplication app(argc, argv); |
| 2105 | 2116 | ||
| 2106 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2117 | // Qt changes the locale and causes issues in float conversion using std::to_string() when |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index c727e942c..ce5045819 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -23,7 +23,6 @@ class EmuThread; | |||
| 23 | class GameList; | 23 | class GameList; |
| 24 | class GImageInfo; | 24 | class GImageInfo; |
| 25 | class GraphicsBreakPointsWidget; | 25 | class GraphicsBreakPointsWidget; |
| 26 | class GraphicsSurfaceWidget; | ||
| 27 | class GRenderWindow; | 26 | class GRenderWindow; |
| 28 | class LoadingScreen; | 27 | class LoadingScreen; |
| 29 | class MicroProfileDialog; | 28 | class MicroProfileDialog; |
| @@ -240,7 +239,6 @@ private: | |||
| 240 | ProfilerWidget* profilerWidget; | 239 | ProfilerWidget* profilerWidget; |
| 241 | MicroProfileDialog* microProfileDialog; | 240 | MicroProfileDialog* microProfileDialog; |
| 242 | GraphicsBreakPointsWidget* graphicsBreakpointsWidget; | 241 | GraphicsBreakPointsWidget* graphicsBreakpointsWidget; |
| 243 | GraphicsSurfaceWidget* graphicsSurfaceWidget; | ||
| 244 | WaitTreeWidget* waitTreeWidget; | 242 | WaitTreeWidget* waitTreeWidget; |
| 245 | 243 | ||
| 246 | QAction* actions_recent_files[max_recent_files_item]; | 244 | QAction* actions_recent_files[max_recent_files_item]; |
| @@ -254,4 +252,8 @@ protected: | |||
| 254 | void dropEvent(QDropEvent* event) override; | 252 | void dropEvent(QDropEvent* event) override; |
| 255 | void dragEnterEvent(QDragEnterEvent* event) override; | 253 | void dragEnterEvent(QDragEnterEvent* event) override; |
| 256 | void dragMoveEvent(QDragMoveEvent* event) override; | 254 | void dragMoveEvent(QDragMoveEvent* event) override; |
| 255 | |||
| 256 | // Overrides used to forward signals to the render window when the focus moves out. | ||
| 257 | void keyPressEvent(QKeyEvent* event) override; | ||
| 258 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 257 | }; | 259 | }; |
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 45e705b61..dbd318e20 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h | |||
| @@ -55,8 +55,8 @@ struct Values { | |||
| 55 | QString roms_path; | 55 | QString roms_path; |
| 56 | QString symbols_path; | 56 | QString symbols_path; |
| 57 | QString screenshot_path; | 57 | QString screenshot_path; |
| 58 | QString gamedir; | 58 | QString game_directory_path; |
| 59 | bool gamedir_deepscan; | 59 | bool game_directory_deepscan; |
| 60 | QStringList recent_files; | 60 | QStringList recent_files; |
| 61 | 61 | ||
| 62 | QString theme; | 62 | QString theme; |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index de7a26e14..68a176032 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -19,6 +19,37 @@ | |||
| 19 | #include "input_common/sdl/sdl.h" | 19 | #include "input_common/sdl/sdl.h" |
| 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 21 | 21 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 23 | public: | ||
| 24 | explicit SDLGLContext() { | ||
| 25 | // create a hidden window to make the shared context against | ||
| 26 | window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 27 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 28 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 29 | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); | ||
| 30 | context = SDL_GL_CreateContext(window); | ||
| 31 | } | ||
| 32 | |||
| 33 | ~SDLGLContext() { | ||
| 34 | SDL_GL_DeleteContext(context); | ||
| 35 | SDL_DestroyWindow(window); | ||
| 36 | } | ||
| 37 | |||
| 38 | void MakeCurrent() override { | ||
| 39 | SDL_GL_MakeCurrent(window, context); | ||
| 40 | } | ||
| 41 | |||
| 42 | void DoneCurrent() override { | ||
| 43 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 44 | } | ||
| 45 | |||
| 46 | void SwapBuffers() override {} | ||
| 47 | |||
| 48 | private: | ||
| 49 | SDL_Window* window; | ||
| 50 | SDL_GLContext context; | ||
| 51 | }; | ||
| 52 | |||
| 22 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 53 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| 23 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | 54 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); |
| 24 | InputCommon::GetMotionEmu()->Tilt(x, y); | 55 | InputCommon::GetMotionEmu()->Tilt(x, y); |
| @@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 153 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | 184 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); |
| 154 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 185 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
| 155 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | 186 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |
| 187 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | ||
| 156 | 188 | ||
| 157 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | 189 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, |
| 158 | Common::g_scm_branch, Common::g_scm_desc); | 190 | Common::g_scm_branch, Common::g_scm_desc); |
| @@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 171 | if (fullscreen) { | 203 | if (fullscreen) { |
| 172 | Fullscreen(); | 204 | Fullscreen(); |
| 173 | } | 205 | } |
| 174 | |||
| 175 | gl_context = SDL_GL_CreateContext(render_window); | 206 | gl_context = SDL_GL_CreateContext(render_window); |
| 176 | 207 | ||
| 177 | if (gl_context == nullptr) { | 208 | if (gl_context == nullptr) { |
| @@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( | |||
| 278 | 309 | ||
| 279 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); | 310 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); |
| 280 | } | 311 | } |
| 312 | |||
| 313 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { | ||
| 314 | return std::make_unique<SDLGLContext>(); | ||
| 315 | } | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index b0d4116cc..17e98227f 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -27,6 +27,8 @@ public: | |||
| 27 | /// Releases the GL context from the caller thread | 27 | /// Releases the GL context from the caller thread |
| 28 | void DoneCurrent() override; | 28 | void DoneCurrent() override; |
| 29 | 29 | ||
| 30 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 31 | |||
| 30 | /// Whether the window is still open, and a close request hasn't yet been sent | 32 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 31 | bool IsOpen() const; | 33 | bool IsOpen() const; |
| 32 | 34 | ||