summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--externals/CMakeLists.txt5
m---------externals/sirit0
-rw-r--r--src/common/scope_exit.h2
-rw-r--r--src/common/swap.h96
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp17
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h10
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp37
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h12
-rw-r--r--src/core/core_cpu.cpp6
-rw-r--r--src/core/frontend/emu_window.h39
-rw-r--r--src/core/hle/kernel/client_port.h2
-rw-r--r--src/core/hle/kernel/client_session.h2
-rw-r--r--src/core/hle/kernel/process.h2
-rw-r--r--src/core/hle/kernel/readable_event.h2
-rw-r--r--src/core/hle/kernel/resource_limit.h2
-rw-r--r--src/core/hle/kernel/server_port.h2
-rw-r--r--src/core/hle/kernel/server_session.cpp9
-rw-r--r--src/core/hle/kernel/server_session.h2
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/svc.cpp337
-rw-r--r--src/core/hle/kernel/svc.h6
-rw-r--r--src/core/hle/kernel/svc_wrap.h352
-rw-r--r--src/core/hle/kernel/thread.h2
-rw-r--r--src/core/hle/kernel/writable_event.h2
-rw-r--r--src/video_core/CMakeLists.txt9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp47
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp34
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h61
-rw-r--r--src/video_core/renderer_opengl/utils.cpp28
-rw-r--r--src/video_core/renderer_opengl/utils.h20
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp1379
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.h80
-rw-r--r--src/video_core/texture_cache.cpp386
-rw-r--r--src/video_core/texture_cache.h586
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/bootmanager.cpp208
-rw-r--r--src/yuzu/bootmanager.h29
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/configure_general.cpp4
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp516
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h96
-rw-r--r--src/yuzu/game_list.cpp5
-rw-r--r--src/yuzu/main.cpp45
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/ui_settings.h4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp37
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h2
51 files changed, 3310 insertions, 1258 deletions
diff --git a/.gitmodules b/.gitmodules
index 26b4e5272..3a49c4874 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -43,3 +43,6 @@
43[submodule "externals/zstd"] 43[submodule "externals/zstd"]
44 path = externals/zstd 44 path = externals/zstd
45 url = https://github.com/facebook/zstd 45 url = https://github.com/facebook/zstd
46[submodule "sirit"]
47 path = externals/sirit
48 url = https://github.com/ReinUsesLisp/sirit
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index aa3319eb1..3f8b6cda8 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -72,6 +72,11 @@ if (USE_DISCORD_PRESENCE)
72 target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) 72 target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
73endif() 73endif()
74 74
75# Sirit
76if (ENABLE_VULKAN)
77 add_subdirectory(sirit)
78endif()
79
75if (ENABLE_WEB_SERVICE) 80if (ENABLE_WEB_SERVICE)
76 # LibreSSL 81 # LibreSSL
77 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") 82 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
diff --git a/externals/sirit b/externals/sirit
new file mode 160000
Subproject f7c4b07a7e14edb1dcd93bc9879c823423705c2
diff --git a/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
21template <typename Func> 21template <typename Func>
22ScopeExitHelper<Func> ScopeExit(Func&& func) { 22ScopeExitHelper<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 @@
62namespace Common { 57namespace Common {
63 58
64#ifdef _MSC_VER 59#ifdef _MSC_VER
65inline u16 swap16(u16 _data) { 60[[nodiscard]] inline u16 swap16(u16 data) noexcept {
66 return _byteswap_ushort(_data); 61 return _byteswap_ushort(data);
67} 62}
68inline u32 swap32(u32 _data) { 63[[nodiscard]] inline u32 swap32(u32 data) noexcept {
69 return _byteswap_ulong(_data); 64 return _byteswap_ulong(data);
70} 65}
71inline 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__)
75inline 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}
80inline u32 swap32(u32 _data) {
81 __asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data));
82 return _data;
83}
84inline u64 swap64(u64 _data) {
85 return ((u64)swap32(_data) << 32) | swap32(_data >> 32);
86}
87#elif __linux__
88inline u16 swap16(u16 _data) {
89 return bswap_16(_data);
90}
91inline u32 swap32(u32 _data) {
92 return bswap_32(_data);
93}
94inline u64 swap64(u64 _data) {
95 return bswap_64(_data);
96}
97#elif __APPLE__
98inline __attribute__((always_inline)) u16 swap16(u16 _data) {
99 return (_data >> 8) | (_data << 8);
100}
101inline __attribute__((always_inline)) u32 swap32(u32 _data) {
102 return __builtin_bswap32(_data);
103}
104inline __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
112inline u16 swap16(u16 _data) { 75#endif
113 return __swap16(_data); 76[[nodiscard]] inline u16 swap16(u16 data) noexcept {
114} 77 return __builtin_bswap16(data);
115inline u32 swap32(u32 _data) {
116 return __swap32(_data);
117}
118inline u64 swap64(u64 _data) {
119 return __swap64(_data);
120}
121#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
122inline u16 swap16(u16 _data) {
123 return bswap16(_data);
124} 78}
125inline u32 swap32(u32 _data) { 79[[nodiscard]] inline u32 swap32(u32 data) noexcept {
126 return bswap32(_data); 80 return __builtin_bswap32(data);
127} 81}
128inline 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.
133inline 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}
136inline 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}
139inline 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
144inline 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
156inline 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
131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 132std::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
174ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, 175ARM_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
15namespace Core::Timing {
16class CoreTiming;
17}
18
19namespace Core { 15namespace Core {
20 16
21class ARM_Dynarmic_Callbacks; 17class ARM_Dynarmic_Callbacks;
22class DynarmicExclusiveMonitor; 18class DynarmicExclusiveMonitor;
19class System;
23 20
24class ARM_Dynarmic final : public ARM_Interface { 21class ARM_Dynarmic final : public ARM_Interface {
25public: 22public:
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
15namespace Core { 14namespace 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
52static 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
66static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, 51static 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
75ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} { 60ARM_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));
190void ARM_Unicorn::ExecuteInstructions(int num_instructions) { 175void 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
261void 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
12namespace Core::Timing {
13class CoreTiming;
14}
15
16namespace Core { 12namespace Core {
17 13
14class System;
15
18class ARM_Unicorn final : public ARM_Interface { 16class ARM_Unicorn final : public ARM_Interface {
19public: 17public:
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
49private: 47private:
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 @@
13namespace Core::Frontend { 13namespace 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 */
20class GraphicsContext {
21public:
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 */
33class EmuWindow { 50class EmuWindow : public GraphicsContext {
34public: 51public:
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
134ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type, 134ResultVal<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.
163static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { 162static 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
188static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { 187static 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
245static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) { 244static 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.
294static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 294static 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
309static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 309static 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
324static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { 324static 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.
362static ResultCode SendSyncRequest(Handle handle) { 363static 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.
380static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { 381static 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.
395static ResultCode GetProcessId(u64* process_id, Handle handle) { 396static 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
440static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, 441static 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
516static ResultCode CancelSynchronization(Handle thread_handle) { 517static 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
534static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, 535static 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
558static ResultCode ArbitrateUnlock(VAddr mutex_addr) { 559static 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
595static void Break(u32 reason, u64 info1, u64 info2) { 596static 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
688static void OutputDebugString(VAddr address, u64 len) { 691static 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
699static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { 702static 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
930static ResultCode SetThreadActivity(Handle handle, u32 activity) { 934static 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
963static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { 967static 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
1004static ResultCode GetThreadPriority(u32* priority, Handle handle) { 1008static 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
1019static ResultCode SetThreadPriority(Handle handle, u32 priority) { 1023static 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
1045static u32 GetCurrentProcessorNumber() { 1049static 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
1050static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, 1054static 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
1103static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 1107static 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
1146static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, 1151static 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
1175static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address, 1181static 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
1187static void ExitProcess() { 1193static 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
1203static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 1209static 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
1262static ResultCode StartThread(Handle thread_handle) { 1268static 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
1285static void ExitThread() { 1291static 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
1297static void SleepThread(s64 nanoseconds) { 1301static 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
1335static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, 1338static 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
1382static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { 1386static 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)
1490static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { 1494static 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)
1511static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) { 1517static 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
1532static u64 GetSystemTick() { 1540static 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
1545static ResultCode CloseHandle(Handle handle) { 1553static 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.
1553static ResultCode ResetSignal(Handle handle) { 1561static 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
1573static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { 1581static 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
1614static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) { 1623static 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
1670static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { 1680static 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
1717static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1728static 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
1734static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { 1746static 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
1783static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, 1796static 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
1829static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { 1842static 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
1857static ResultCode ClearEvent(Handle handle) { 1870static 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
1878static ResultCode SignalEvent(Handle handle) { 1891static 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
1893static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { 1906static 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
1919static ResultCode CreateResourceLimit(Handle* out_handle) { 1932static 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
1937static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit, 1950static 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
1951static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit, 1964static 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
1965static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) { 1978static 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
2000static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, 2013static 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
2037ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size, 2050ResultCode 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
2077namespace { 2090namespace {
2078struct FunctionDef { 2091struct 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
2226MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); 2239MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
2227 2240
2228void CallSVC(u32 immediate) { 2241void 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
9namespace Core {
10class System;
11}
12
9namespace Kernel { 13namespace Kernel {
10 14
11void CallSVC(u32 immediate); 15void 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
12namespace Kernel { 12namespace Kernel {
13 13
14static inline u64 Param(int n) { 14static 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 */
22static inline void FuncReturn(u64 res) { 23static 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
29template <ResultCode func(u64)> 30template <ResultCode func(Core::System&, u64)>
30void SvcWrap() { 31void SvcWrap(Core::System& system) {
31 FuncReturn(func(Param(0)).raw); 32 FuncReturn(system, func(system, Param(system, 0)).raw);
32} 33}
33 34
34template <ResultCode func(u32)> 35template <ResultCode func(Core::System&, u32)>
35void SvcWrap() { 36void 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
39template <ResultCode func(u32, u32)> 40template <ResultCode func(Core::System&, u32, u32)>
40void SvcWrap() { 41void 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
44template <ResultCode func(u32*)> 47template <ResultCode func(Core::System&, u32*)>
45void SvcWrap() { 48void SvcWrap(Core::System& system) {
46 u32 param = 0; 49 u32 param = 0;
47 const u32 retval = func(&param).raw; 50 const u32 retval = func(system, &param).raw;
48 Core::CurrentArmInterface().SetReg(1, param); 51 system.CurrentArmInterface().SetReg(1, param);
49 FuncReturn(retval); 52 FuncReturn(system, retval);
50} 53}
51 54
52template <ResultCode func(u32*, u32)> 55template <ResultCode func(Core::System&, u32*, u32)>
53void SvcWrap() { 56void SvcWrap(Core::System& system) {
54 u32 param_1 = 0; 57 u32 param_1 = 0;
55 u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw; 58 const u32 retval = func(system, &param_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
60template <ResultCode func(u32*, u32*)> 63template <ResultCode func(Core::System&, u32*, u32*)>
61void SvcWrap() { 64void 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(&param_1, &param_2).raw; 67 const u32 retval = func(system, &param_1, &param_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
73template <ResultCode func(u32*, u64)> 76template <ResultCode func(Core::System&, u32*, u64)>
74void SvcWrap() { 77void SvcWrap(Core::System& system) {
75 u32 param_1 = 0; 78 u32 param_1 = 0;
76 const u32 retval = func(&param_1, Param(1)).raw; 79 const u32 retval = func(system, &param_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
81template <ResultCode func(u32*, u64, u32)> 84template <ResultCode func(Core::System&, u32*, u64, u32)>
82void SvcWrap() { 85void SvcWrap(Core::System& system) {
83 u32 param_1 = 0; 86 u32 param_1 = 0;
84 const u32 retval = func(&param_1, Param(1), static_cast<u32>(Param(2))).raw; 87 const u32 retval =
85 Core::CurrentArmInterface().SetReg(1, param_1); 88 func(system, &param_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
89template <ResultCode func(u64*, u32)> 94template <ResultCode func(Core::System&, u64*, u32)>
90void SvcWrap() { 95void SvcWrap(Core::System& system) {
91 u64 param_1 = 0; 96 u64 param_1 = 0;
92 const u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw; 97 const u32 retval = func(system, &param_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
97template <ResultCode func(u64, s32)> 103template <ResultCode func(Core::System&, u64, s32)>
98void SvcWrap() { 104void 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
102template <ResultCode func(u64, u32)> 108template <ResultCode func(Core::System&, u64, u32)>
103void SvcWrap() { 109void 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
107template <ResultCode func(u64*, u64)> 113template <ResultCode func(Core::System&, u64*, u64)>
108void SvcWrap() { 114void SvcWrap(Core::System& system) {
109 u64 param_1 = 0; 115 u64 param_1 = 0;
110 u32 retval = func(&param_1, Param(1)).raw; 116 const u32 retval = func(system, &param_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
115template <ResultCode func(u64*, u32, u32)> 122template <ResultCode func(Core::System&, u64*, u32, u32)>
116void SvcWrap() { 123void SvcWrap(Core::System& system) {
117 u64 param_1 = 0; 124 u64 param_1 = 0;
118 u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw; 125 const u32 retval = func(system, &param_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
123template <ResultCode func(u32, u64)> 133template <ResultCode func(Core::System&, u32, u64)>
124void SvcWrap() { 134void 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
128template <ResultCode func(u32, u32, u64)> 138template <ResultCode func(Core::System&, u32, u32, u64)>
129void SvcWrap() { 139void 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
133template <ResultCode func(u32, u32*, u64*)> 145template <ResultCode func(Core::System&, u32, u32*, u64*)>
134void SvcWrap() { 146void 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)), &param_1, &param_2); 149 const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
138 Core::CurrentArmInterface().SetReg(1, param_1);
139 Core::CurrentArmInterface().SetReg(2, param_2);
140 FuncReturn(retval.raw);
141}
142 150
143template <ResultCode func(u64, u64, u32, u32)> 151 system.CurrentArmInterface().SetReg(1, param_1);
144void 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
149template <ResultCode func(u64, u64, u32, u64)> 156template <ResultCode func(Core::System&, u64, u64, u32, u32)>
150void SvcWrap() { 157void 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
154template <ResultCode func(u32, u64, u32)> 163template <ResultCode func(Core::System&, u64, u64, u32, u64)>
155void SvcWrap() { 164void 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
159template <ResultCode func(u64, u64, u64)> 170template <ResultCode func(Core::System&, u32, u64, u32)>
160void SvcWrap() { 171void 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
164template <ResultCode func(u64, u64, u32)> 177template <ResultCode func(Core::System&, u64, u64, u64)>
165void SvcWrap() { 178void 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
169template <ResultCode func(u32, u64, u64, u32)> 182template <ResultCode func(Core::System&, u64, u64, u32)>
170void SvcWrap() { 183void 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
189template <ResultCode func(Core::System&, u32, u64, u64, u32)>
190void 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
175template <ResultCode func(u32, u64, u64)> 196template <ResultCode func(Core::System&, u32, u64, u64)>
176void SvcWrap() { 197void 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
180template <ResultCode func(u32*, u64, u64, s64)> 203template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
181void SvcWrap() { 204void SvcWrap(Core::System& system) {
182 u32 param_1 = 0; 205 u32 param_1 = 0;
183 ResultCode retval = 206 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
184 func(&param_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
189template <ResultCode func(u64, u64, u32, s64)> 214template <ResultCode func(Core::System&, u64, u64, u32, s64)>
190void SvcWrap() { 215void 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
195template <ResultCode func(u64*, u64, u64, u64)> 221template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
196void SvcWrap() { 222void SvcWrap(Core::System& system) {
197 u64 param_1 = 0; 223 u64 param_1 = 0;
198 u32 retval = func(&param_1, Param(1), Param(2), Param(3)).raw; 224 const u32 retval =
199 Core::CurrentArmInterface().SetReg(1, param_1); 225 func(system, &param_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
203template <ResultCode func(u32*, u64, u64, u64, u32, s32)> 231template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
204void SvcWrap() { 232void SvcWrap(Core::System& system) {
205 u32 param_1 = 0; 233 u32 param_1 = 0;
206 u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)), 234 const u32 retval = func(system, &param_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
213template <ResultCode func(u32*, u64, u64, u32)> 242template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
214void SvcWrap() { 243void SvcWrap(Core::System& system) {
215 u32 param_1 = 0; 244 u32 param_1 = 0;
216 u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw; 245 const u32 retval = func(system, &param_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
221template <ResultCode func(Handle*, u64, u32, u32)> 253template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
222void SvcWrap() { 254void SvcWrap(Core::System& system) {
223 u32 param_1 = 0; 255 u32 param_1 = 0;
224 u32 retval = 256 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
225 func(&param_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
230template <ResultCode func(u64, u32, s32, s64)> 264template <ResultCode func(Core::System&, u64, u32, s32, s64)>
231void SvcWrap() { 265void 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
237template <ResultCode func(u64, u32, s32, s32)> 271template <ResultCode func(Core::System&, u64, u32, s32, s32)>
238void SvcWrap() { 272void 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
247template <u32 func()> 281template <u32 func(Core::System&)>
248void SvcWrap() { 282void 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
255template <u64 func()> 289template <u64 func(Core::System&)>
256void SvcWrap() { 290void 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
263template <void func()> 297template <void func(Core::System&)>
264void SvcWrap() { 298void SvcWrap(Core::System& system) {
265 func(); 299 func(system);
266} 300}
267 301
268template <void func(s64)> 302template <void func(Core::System&, s64)>
269void SvcWrap() { 303void SvcWrap(Core::System& system) {
270 func(static_cast<s64>(Param(0))); 304 func(system, static_cast<s64>(Param(system, 0)));
271} 305}
272 306
273template <void func(u64, u64 len)> 307template <void func(Core::System&, u64, u64)>
274void SvcWrap() { 308void SvcWrap(Core::System& system) {
275 func(Param(0), Param(1)); 309 func(system, Param(system, 0), Param(system, 1));
276} 310}
277 311
278template <void func(u64, u64, u64)> 312template <void func(Core::System&, u64, u64, u64)>
279void SvcWrap() { 313void 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
283template <void func(u32, u64, u64)> 317template <void func(Core::System&, u32, u64, u64)>
284void SvcWrap() { 318void 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)
137endif() 141endif()
138 142
@@ -140,3 +144,6 @@ create_target_directory_groups(video_core)
140 144
141target_link_libraries(video_core PUBLIC common core) 145target_link_libraries(video_core PUBLIC common core)
142target_link_libraries(video_core PRIVATE glad) 146target_link_libraries(video_core PRIVATE glad)
147if (ENABLE_VULKAN)
148 target_link_libraries(video_core PRIVATE sirit)
149endif()
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
959void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, 949void 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
32namespace Core { 33namespace Core {
33class System; 34class 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
7namespace OpenGL::GLShader { 9namespace OpenGL::GLShader {
8 10
9using Tegra::Engines::Maxwell3D; 11using Tegra::Engines::Maxwell3D;
10 12
13ProgramManager::ProgramManager() {
14 pipeline.Create();
15}
16
17ProgramManager::~ProgramManager() = default;
18
19void ProgramManager::ApplyTo(OpenGLState& state) {
20 UpdatePipeline();
21 state.draw.shader_program = 0;
22 state.draw.program_pipeline = pipeline.handle;
23}
24
25void 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
11void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { 43void 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
39class ProgramManager { 41class ProgramManager {
40public: 42public:
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
67private: 64private:
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
11namespace OpenGL { 12namespace OpenGL {
12 13
14BindBuffersRangePushBuffer::BindBuffersRangePushBuffer(GLenum target) : target{target} {}
15
16BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default;
17
18void BindBuffersRangePushBuffer::Setup(GLuint first_) {
19 first = first_;
20 buffers.clear();
21 offsets.clear();
22 sizes.clear();
23}
24
25void 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
31void 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
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { 41void 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
11namespace OpenGL { 12namespace OpenGL {
12 13
14class BindBuffersRangePushBuffer {
15public:
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
25private:
26 GLenum target;
27 GLuint first;
28 std::vector<GLuint> buffers;
29 std::vector<GLintptr> offsets;
30 std::vector<GLsizeiptr> sizes;
31};
32
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); 33void 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
23namespace Vulkan::VKShader {
24
25using Sirit::Id;
26using Tegra::Shader::Attribute;
27using Tegra::Shader::AttributeUse;
28using Tegra::Shader::Register;
29using namespace VideoCommon::Shader;
30
31using Maxwell = Tegra::Engines::Maxwell3D::Regs;
32using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage;
33using Operation = const OperationNode&;
34
35// TODO(Rodrigo): Use rasterizer's value
36constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 0x1000;
37constexpr u32 STAGE_BINDING_STRIDE = 0x100;
38
39enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
40
41struct SamplerImage {
42 Id image_type;
43 Id sampled_image_type;
44 Id sampler;
45};
46
47namespace {
48
49spv::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
66constexpr 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
72constexpr 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
78bool 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
92class SPIRVDecompiler : public Sirit::Module {
93public:
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
210private:
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
1373DecompilerResult 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
19namespace VideoCommon::Shader {
20class ShaderIR;
21}
22
23namespace Vulkan::VKShader {
24
25using Maxwell = Tegra::Engines::Maxwell3D::Regs;
26
27using SamplerEntry = VideoCommon::Shader::Sampler;
28
29constexpr u32 DESCRIPTOR_SET = 0;
30
31class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer {
32public:
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
40private:
41 u32 index{};
42};
43
44class GlobalBufferEntry {
45public:
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
57private:
58 u32 cbuf_index{};
59 u32 cbuf_offset{};
60};
61
62struct 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
76using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>;
77
78DecompilerResult 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
15namespace VideoCommon {
16
17using VideoCore::Surface::SurfaceTarget;
18
19using VideoCore::Surface::ComponentTypeFromDepthFormat;
20using VideoCore::Surface::ComponentTypeFromRenderTarget;
21using VideoCore::Surface::ComponentTypeFromTexture;
22using VideoCore::Surface::PixelFormatFromDepthFormat;
23using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
24using VideoCore::Surface::PixelFormatFromTextureFormat;
25using VideoCore::Surface::SurfaceTargetFromTextureType;
26
27constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
28 return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
29}
30
31SurfaceParams 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
59SurfaceParams 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
83SurfaceParams 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
112SurfaceParams 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
135u32 SurfaceParams::GetMipWidth(u32 level) const {
136 return std::max(1U, width >> level);
137}
138
139u32 SurfaceParams::GetMipHeight(u32 level) const {
140 return std::max(1U, height >> level);
141}
142
143u32 SurfaceParams::GetMipDepth(u32 level) const {
144 return IsLayered() ? depth : std::max(1U, depth >> level);
145}
146
147bool 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
159u32 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
175u32 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
192std::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
200std::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
208std::size_t SurfaceParams::GetGuestLayerSize() const {
209 return GetInnerMemorySize(false, true, false);
210}
211
212std::size_t SurfaceParams::GetHostLayerSize(u32 level) const {
213 return GetInnerMipmapMemorySize(level, true, IsLayered(), false);
214}
215
216bool 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
250bool SurfaceParams::IsPixelFormatZeta() const {
251 return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
252 pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
253}
254
255void 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
282std::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
294std::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
306std::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
340bool 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
345bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
346 return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
347}
348
349bool 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
356bool 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
361std::size_t HasheableSurfaceParams::Hash() const {
362 return static_cast<std::size_t>(
363 Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
364}
365
366bool 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
376std::size_t ViewKey::Hash() const {
377 return static_cast<std::size_t>(
378 Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
379}
380
381bool 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
26namespace Core {
27class System;
28}
29
30namespace Tegra::Texture {
31struct FullTextureInfo;
32}
33
34namespace VideoCore {
35class RasterizerInterface;
36}
37
38namespace VideoCommon {
39
40class HasheableSurfaceParams {
41public:
42 std::size_t Hash() const;
43
44 bool operator==(const HasheableSurfaceParams& rhs) const;
45
46protected:
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
67class SurfaceParams final : public HasheableSurfaceParams {
68public:
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
190private:
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
216struct 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
229namespace std {
230
231template <>
232struct hash<VideoCommon::SurfaceParams> {
233 std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
234 return k.Hash();
235 }
236};
237
238template <>
239struct 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
247namespace VideoCommon {
248
249template <typename TView, typename TExecutionContext>
250class SurfaceBase {
251 static_assert(std::is_trivially_copyable_v<TExecutionContext>);
252
253public:
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
335protected:
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
349private:
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
370template <typename TSurface, typename TView, typename TExecutionContext>
371class 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
377public:
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
453protected:
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
489private:
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
92class GGLContext : public Core::Frontend::GraphicsContext {
93public:
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
110private:
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
88class GGLWidgetInternal : public QGLWidget { 118class GGLWidgetInternal : public QOpenGLWindow {
89public: 119public:
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
116GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 188GRenderWindow::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
143void GRenderWindow::SwapBuffers() { 215void 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
159void GRenderWindow::MakeCurrent() { 231void GRenderWindow::MakeCurrent() {
160 child->makeCurrent(); 232 context->makeCurrent(child);
161} 233}
162 234
163void GRenderWindow::DoneCurrent() { 235void GRenderWindow::DoneCurrent() {
164 child->doneCurrent(); 236 context->doneCurrent();
165} 237}
166 238
167void GRenderWindow::PollEvents() {} 239void GRenderWindow::PollEvents() {}
@@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {}
174void GRenderWindow::OnFramebufferSizeChanged() { 246void 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
255void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
256 if (child) {
257 child->keyPressEvent(event);
258 }
259}
260
261void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
262 if (child) {
263 child->keyReleaseEvent(event);
264 }
265}
266
183void GRenderWindow::BackupGeometry() { 267void GRenderWindow::BackupGeometry() {
184 geometry = ((QGLWidget*)this)->saveGeometry(); 268 geometry = ((QWidget*)this)->saveGeometry();
185} 269}
186 270
187void GRenderWindow::RestoreGeometry() { 271void 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
207qreal GRenderWindow::windowPixelRatio() const { 291qreal 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
212std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { 296std::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
223void GRenderWindow::keyPressEvent(QKeyEvent* event) {
224 InputCommon::GetKeyboard()->PressKey(event->key());
225}
226
227void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
228 InputCommon::GetKeyboard()->ReleaseKey(event->key());
229}
230
231void 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
244void 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
254void 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
264void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 307void 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
359std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
360 return std::make_unique<GGLContext>(shared_context.get());
361}
362
316void GRenderWindow::InitRenderTarget() { 363void 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;
21class GGLWidgetInternal; 21class GGLWidgetInternal;
22class GMainWindow; 22class GMainWindow;
23class GRenderWindow; 23class GRenderWindow;
24class QSurface;
25class QOpenGLContext;
24 26
25namespace VideoCore { 27namespace VideoCore {
26enum class LoadCallbackStage; 28enum 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
163private: 161private:
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)
28ConfigureGeneral::~ConfigureGeneral() = default; 28ConfigureGeneral::~ConfigureGeneral() = default;
29 29
30void ConfigureGeneral::setConfiguration() { 30void 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
38void ConfigureGeneral::applyConfiguration() { 38void 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
25static 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
38SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
39 : QLabel(parent), surface_widget(surface_widget_) {}
40
41SurfacePicture::~SurfacePicture() = default;
42
43void 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
56void 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
61GraphicsSurfaceWidget::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
288void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
289 emit Update();
290 widget()->setEnabled(true);
291}
292
293void GraphicsSurfaceWidget::OnResumed() {
294 widget()->setEnabled(false);
295}
296
297void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
298 surface_source = static_cast<Source>(new_value);
299 emit Update();
300}
301
302void 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
311void 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
320void 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
329void 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
338void 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
345void 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
352void 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
367void 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
462void 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
13class QComboBox;
14class QSpinBox;
15class CSpinBox;
16
17class GraphicsSurfaceWidget;
18
19class SurfacePicture : public QLabel {
20 Q_OBJECT
21
22public:
23 explicit SurfacePicture(QWidget* parent = nullptr,
24 GraphicsSurfaceWidget* surface_widget = nullptr);
25 ~SurfacePicture() override;
26
27protected slots:
28 void mouseMoveEvent(QMouseEvent* event) override;
29 void mousePressEvent(QMouseEvent* event) override;
30
31private:
32 GraphicsSurfaceWidget* surface_widget;
33};
34
35class 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
53public:
54 explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
55 QWidget* parent = nullptr);
56 void Pick(int x, int y);
57
58public 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
68signals:
69 void Update();
70
71private:
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() {
467const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; 467const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
468 468
469void GameList::RefreshGameDirectory() { 469void 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() {
1500void GMainWindow::OnMenuSelectGameListRoot() { 1496void 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
2034void GMainWindow::keyPressEvent(QKeyEvent* event) {
2035 if (render_window) {
2036 render_window->ForwardKeyPressEvent(event);
2037 }
2038}
2039
2040void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
2041 if (render_window) {
2042 render_window->ForwardKeyReleaseEvent(event);
2043 }
2044}
2045
2036bool GMainWindow::ConfirmChangeGame() { 2046bool 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;
23class GameList; 23class GameList;
24class GImageInfo; 24class GImageInfo;
25class GraphicsBreakPointsWidget; 25class GraphicsBreakPointsWidget;
26class GraphicsSurfaceWidget;
27class GRenderWindow; 26class GRenderWindow;
28class LoadingScreen; 27class LoadingScreen;
29class MicroProfileDialog; 28class 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
22class SDLGLContext : public Core::Frontend::GraphicsContext {
23public:
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
48private:
49 SDL_Window* window;
50 SDL_GLContext context;
51};
52
22void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 53void 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
313std::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