summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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.cpp26
-rw-r--r--src/core/core.h14
-rw-r--r--src/core/core_cpu.cpp6
-rw-r--r--src/core/crypto/key_manager.cpp3
-rw-r--r--src/core/file_sys/patch_manager.cpp22
-rw-r--r--src/core/file_sys/patch_manager.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp275
-rw-r--r--src/core/file_sys/registered_cache.h156
-rw-r--r--src/core/file_sys/romfs_factory.cpp2
-rw-r--r--src/core/file_sys/submission_package.cpp13
-rw-r--r--src/core/file_sys/submission_package.h11
-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.cpp2
-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/core/hle/service/am/applets/web_browser.cpp2
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp4
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp11
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp15
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp16
-rw-r--r--src/core/loader/nso.cpp6
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/CMakeLists.txt9
-rw-r--r--src/video_core/dma_pusher.cpp1
-rw-r--r--src/video_core/engines/shader_bytecode.h5
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp1
-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.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-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/shader/decode/xmad.cpp39
-rw-r--r--src/video_core/texture_cache.cpp386
-rw-r--r--src/video_core/texture_cache.h586
-rw-r--r--src/video_core/textures/convert.cpp1
-rw-r--r--src/video_core/textures/convert.h5
-rw-r--r--src/video_core/textures/texture.h2
-rw-r--r--src/yuzu/CMakeLists.txt8
-rw-r--r--src/yuzu/applets/profile_select.cpp1
-rw-r--r--src/yuzu/applets/profile_select.h2
-rw-r--r--src/yuzu/bootmanager.cpp208
-rw-r--r--src/yuzu/bootmanager.h29
-rw-r--r--src/yuzu/configuration/config.cpp69
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configure.ui19
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp16
-rw-r--r--src/yuzu/configuration/configure_dialog.h3
-rw-r--r--src/yuzu/configuration/configure_general.cpp8
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui24
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp3
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp121
-rw-r--r--src/yuzu/configuration/configure_hotkeys.h48
-rw-r--r--src/yuzu/configuration/configure_hotkeys.ui42
-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.cpp14
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/game_list_worker.cpp125
-rw-r--r--src/yuzu/game_list_worker.h16
-rw-r--r--src/yuzu/hotkeys.cpp73
-rw-r--r--src/yuzu/hotkeys.h42
-rw-r--r--src/yuzu/hotkeys.ui46
-rw-r--r--src/yuzu/loading_screen.cpp7
-rw-r--r--src/yuzu/main.cpp121
-rw-r--r--src/yuzu/main.h14
-rw-r--r--src/yuzu/ui_settings.cpp1
-rw-r--r--src/yuzu/ui_settings.h11
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp37
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.h24
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp37
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
101 files changed, 4283 insertions, 1774 deletions
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index baf1f1c9e..1176a72b1 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -20,7 +20,7 @@ struct ScopeExitHelper {
20 20
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.cpp b/src/core/core.cpp
index 4fe77c25b..bc9e887b6 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -17,6 +17,7 @@
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18#include "core/cpu_core_manager.h" 18#include "core/cpu_core_manager.h"
19#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
20#include "core/file_sys/registered_cache.h"
20#include "core/file_sys/vfs_concat.h" 21#include "core/file_sys/vfs_concat.h"
21#include "core/file_sys/vfs_real.h" 22#include "core/file_sys/vfs_real.h"
22#include "core/gdbstub/gdbstub.h" 23#include "core/gdbstub/gdbstub.h"
@@ -108,6 +109,8 @@ struct System::Impl {
108 // Create a default fs if one doesn't already exist. 109 // Create a default fs if one doesn't already exist.
109 if (virtual_filesystem == nullptr) 110 if (virtual_filesystem == nullptr)
110 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 111 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
112 if (content_provider == nullptr)
113 content_provider = std::make_unique<FileSys::ContentProviderUnion>();
111 114
112 /// Create default implementations of applets if one is not provided. 115 /// Create default implementations of applets if one is not provided.
113 if (profile_selector == nullptr) 116 if (profile_selector == nullptr)
@@ -249,6 +252,8 @@ struct System::Impl {
249 Kernel::KernelCore kernel; 252 Kernel::KernelCore kernel;
250 /// RealVfsFilesystem instance 253 /// RealVfsFilesystem instance
251 FileSys::VirtualFilesystem virtual_filesystem; 254 FileSys::VirtualFilesystem virtual_filesystem;
255 /// ContentProviderUnion instance
256 std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
252 /// AppLoader used to load the current executing application 257 /// AppLoader used to load the current executing application
253 std::unique_ptr<Loader::AppLoader> app_loader; 258 std::unique_ptr<Loader::AppLoader> app_loader;
254 std::unique_ptr<VideoCore::RendererBase> renderer; 259 std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -488,6 +493,27 @@ const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
488 return *impl->software_keyboard; 493 return *impl->software_keyboard;
489} 494}
490 495
496void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
497 impl->content_provider = std::move(provider);
498}
499
500FileSys::ContentProvider& System::GetContentProvider() {
501 return *impl->content_provider;
502}
503
504const FileSys::ContentProvider& System::GetContentProvider() const {
505 return *impl->content_provider;
506}
507
508void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
509 FileSys::ContentProvider* provider) {
510 impl->content_provider->SetSlot(slot, provider);
511}
512
513void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
514 impl->content_provider->ClearSlot(slot);
515}
516
491void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) { 517void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
492 impl->web_browser = std::move(applet); 518 impl->web_browser = std::move(applet);
493} 519}
diff --git a/src/core/core.h b/src/core/core.h
index 4d83b93cc..82b2e087e 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -21,6 +21,9 @@ class WebBrowserApplet;
21 21
22namespace FileSys { 22namespace FileSys {
23class CheatList; 23class CheatList;
24class ContentProvider;
25class ContentProviderUnion;
26enum class ContentProviderUnionSlot;
24class VfsFilesystem; 27class VfsFilesystem;
25} // namespace FileSys 28} // namespace FileSys
26 29
@@ -270,6 +273,17 @@ public:
270 Frontend::WebBrowserApplet& GetWebBrowser(); 273 Frontend::WebBrowserApplet& GetWebBrowser();
271 const Frontend::WebBrowserApplet& GetWebBrowser() const; 274 const Frontend::WebBrowserApplet& GetWebBrowser() const;
272 275
276 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
277
278 FileSys::ContentProvider& GetContentProvider();
279
280 const FileSys::ContentProvider& GetContentProvider() const;
281
282 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
283 FileSys::ContentProvider* provider);
284
285 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
286
273private: 287private:
274 System(); 288 System();
275 289
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index e75741db0..ba63c3e61 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { 55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
56 if (Settings::values.use_cpu_jit) { 56 if (Settings::values.use_cpu_jit) {
57#ifdef ARCHITECTURE_x86_64 57#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index); 58 arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
59#else 59#else
60 arm_interface = std::make_unique<ARM_Unicorn>(); 60 arm_interface = std::make_unique<ARM_Unicorn>(system);
61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
62#endif 62#endif
63 } else { 63 } else {
64 arm_interface = std::make_unique<ARM_Unicorn>(core_timing); 64 arm_interface = std::make_unique<ARM_Unicorn>(system);
65 } 65 }
66 66
67 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); 67 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index dfac9a4b3..dc006e2bb 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -22,6 +22,7 @@
22#include "common/file_util.h" 22#include "common/file_util.h"
23#include "common/hex_util.h" 23#include "common/hex_util.h"
24#include "common/logging/log.h" 24#include "common/logging/log.h"
25#include "core/core.h"
25#include "core/crypto/aes_util.h" 26#include "core/crypto/aes_util.h"
26#include "core/crypto/key_manager.h" 27#include "core/crypto/key_manager.h"
27#include "core/crypto/partition_data_manager.h" 28#include "core/crypto/partition_data_manager.h"
@@ -794,7 +795,7 @@ void KeyManager::DeriveBase() {
794 795
795void KeyManager::DeriveETicket(PartitionDataManager& data) { 796void KeyManager::DeriveETicket(PartitionDataManager& data) {
796 // ETicket keys 797 // ETicket keys
797 const auto es = Service::FileSystem::GetUnionContents().GetEntry( 798 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
798 0x0100000000000033, FileSys::ContentRecordType::Program); 799 0x0100000000000033, FileSys::ContentRecordType::Program);
799 800
800 if (es == nullptr) 801 if (es == nullptr)
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index e11217708..78dbadee3 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -10,6 +10,7 @@
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/hex_util.h" 11#include "common/hex_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "core/core.h"
13#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
14#include "core/file_sys/control_metadata.h" 15#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/ips_layer.h" 16#include "core/file_sys/ips_layer.h"
@@ -69,7 +70,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
69 } 70 }
70 } 71 }
71 72
72 const auto installed = Service::FileSystem::GetUnionContents(); 73 const auto& installed = Core::System::GetInstance().GetContentProvider();
73 74
74 const auto& disabled = Settings::values.disabled_addons[title_id]; 75 const auto& disabled = Settings::values.disabled_addons[title_id];
75 const auto update_disabled = 76 const auto update_disabled =
@@ -155,7 +156,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
155 return out; 156 return out;
156} 157}
157 158
158std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { 159std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::string& name) const {
159 if (nso.size() < sizeof(Loader::NSOHeader)) { 160 if (nso.size() < sizeof(Loader::NSOHeader)) {
160 return nso; 161 return nso;
161 } 162 }
@@ -171,18 +172,19 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
171 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 172 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
172 173
173 if (Settings::values.dump_nso) { 174 if (Settings::values.dump_nso) {
174 LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id); 175 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
176 title_id);
175 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); 177 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
176 if (dump_dir != nullptr) { 178 if (dump_dir != nullptr) {
177 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); 179 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
178 const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id)); 180 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
179 181
180 file->Resize(nso.size()); 182 file->Resize(nso.size());
181 file->WriteBytes(nso); 183 file->WriteBytes(nso);
182 } 184 }
183 } 185 }
184 186
185 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); 187 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
186 188
187 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 189 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
188 auto patch_dirs = load_dir->GetSubdirectories(); 190 auto patch_dirs = load_dir->GetSubdirectories();
@@ -345,7 +347,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
345 if (romfs == nullptr) 347 if (romfs == nullptr)
346 return romfs; 348 return romfs;
347 349
348 const auto installed = Service::FileSystem::GetUnionContents(); 350 const auto& installed = Core::System::GetInstance().GetContentProvider();
349 351
350 // Game Updates 352 // Game Updates
351 const auto update_tid = GetUpdateTitleID(title_id); 353 const auto update_tid = GetUpdateTitleID(title_id);
@@ -392,7 +394,7 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
392std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( 394std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
393 VirtualFile update_raw) const { 395 VirtualFile update_raw) const {
394 std::map<std::string, std::string, std::less<>> out; 396 std::map<std::string, std::string, std::less<>> out;
395 const auto installed = Service::FileSystem::GetUnionContents(); 397 const auto& installed = Core::System::GetInstance().GetContentProvider();
396 const auto& disabled = Settings::values.disabled_addons[title_id]; 398 const auto& disabled = Settings::values.disabled_addons[title_id];
397 399
398 // Game Updates 400 // Game Updates
@@ -466,10 +468,10 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
466 468
467 // DLC 469 // DLC
468 const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); 470 const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
469 std::vector<RegisteredCacheEntry> dlc_match; 471 std::vector<ContentProviderEntry> dlc_match;
470 dlc_match.reserve(dlc_entries.size()); 472 dlc_match.reserve(dlc_entries.size());
471 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), 473 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
472 [this, &installed](const RegisteredCacheEntry& entry) { 474 [this, &installed](const ContentProviderEntry& entry) {
473 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && 475 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
474 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; 476 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
475 }); 477 });
@@ -492,7 +494,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
492} 494}
493 495
494std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 496std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
495 const auto installed{Service::FileSystem::GetUnionContents()}; 497 const auto& installed = Core::System::GetInstance().GetContentProvider();
496 498
497 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); 499 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
498 if (base_control_nca == nullptr) 500 if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index de2672c76..769f8c6f0 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -44,7 +44,7 @@ public:
44 // Currently tracked NSO patches: 44 // Currently tracked NSO patches:
45 // - IPS 45 // - IPS
46 // - IPSwitch 46 // - IPSwitch
47 std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; 47 std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const;
48 48
49 // Checks to see if PatchNSO() will have any effect given the NSO's build ID. 49 // Checks to see if PatchNSO() will have any effect given the NSO's build ID.
50 // Used to prevent expensive copies in NSO loader. 50 // Used to prevent expensive copies in NSO loader.
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 1c6bacace..3946ff871 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -23,19 +23,19 @@ namespace FileSys {
23// The size of blocks to use when vfs raw copying into nand. 23// The size of blocks to use when vfs raw copying into nand.
24constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000; 24constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
25 25
26std::string RegisteredCacheEntry::DebugInfo() const { 26std::string ContentProviderEntry::DebugInfo() const {
27 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); 27 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
28} 28}
29 29
30bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 30bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); 31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
32} 32}
33 33
34bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 34bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); 35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36} 36}
37 37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 38bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
39 return !operator==(lhs, rhs); 39 return !operator==(lhs, rhs);
40} 40}
41 41
@@ -84,7 +84,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
84 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); 84 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
85} 85}
86 86
87static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { 87ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
88 switch (type) { 88 switch (type) {
89 case NCAContentType::Program: 89 case NCAContentType::Program:
90 // TODO(DarkLordZach): Differentiate between Program and Patch 90 // TODO(DarkLordZach): Differentiate between Program and Patch
@@ -104,6 +104,28 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
104 } 104 }
105} 105}
106 106
107ContentProvider::~ContentProvider() = default;
108
109bool ContentProvider::HasEntry(ContentProviderEntry entry) const {
110 return HasEntry(entry.title_id, entry.type);
111}
112
113VirtualFile ContentProvider::GetEntryUnparsed(ContentProviderEntry entry) const {
114 return GetEntryUnparsed(entry.title_id, entry.type);
115}
116
117VirtualFile ContentProvider::GetEntryRaw(ContentProviderEntry entry) const {
118 return GetEntryRaw(entry.title_id, entry.type);
119}
120
121std::unique_ptr<NCA> ContentProvider::GetEntry(ContentProviderEntry entry) const {
122 return GetEntry(entry.title_id, entry.type);
123}
124
125std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
126 return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
127}
128
107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 129VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
108 std::string_view path) const { 130 std::string_view path) const {
109 const auto file = dir->GetFileRelative(path); 131 const auto file = dir->GetFileRelative(path);
@@ -161,8 +183,8 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
161 return file; 183 return file;
162} 184}
163 185
164static std::optional<NcaID> CheckMapForContentRecord( 186static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
165 const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { 187 ContentRecordType type) {
166 if (map.find(title_id) == map.end()) 188 if (map.find(title_id) == map.end())
167 return {}; 189 return {};
168 190
@@ -268,7 +290,7 @@ void RegisteredCache::Refresh() {
268 AccumulateYuzuMeta(); 290 AccumulateYuzuMeta();
269} 291}
270 292
271RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function) 293RegisteredCache::RegisteredCache(VirtualDir dir_, ContentProviderParsingFunction parsing_function)
272 : dir(std::move(dir_)), parser(std::move(parsing_function)) { 294 : dir(std::move(dir_)), parser(std::move(parsing_function)) {
273 Refresh(); 295 Refresh();
274} 296}
@@ -279,19 +301,11 @@ bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
279 return GetEntryRaw(title_id, type) != nullptr; 301 return GetEntryRaw(title_id, type) != nullptr;
280} 302}
281 303
282bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
283 return GetEntryRaw(entry) != nullptr;
284}
285
286VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 304VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
287 const auto id = GetNcaIDFromMetadata(title_id, type); 305 const auto id = GetNcaIDFromMetadata(title_id, type);
288 return id ? GetFileAtID(*id) : nullptr; 306 return id ? GetFileAtID(*id) : nullptr;
289} 307}
290 308
291VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
292 return GetEntryUnparsed(entry.title_id, entry.type);
293}
294
295std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { 309std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
296 const auto meta_iter = meta.find(title_id); 310 const auto meta_iter = meta.find(title_id);
297 if (meta_iter != meta.end()) 311 if (meta_iter != meta.end())
@@ -309,10 +323,6 @@ VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) c
309 return id ? parser(GetFileAtID(*id), *id) : nullptr; 323 return id ? parser(GetFileAtID(*id), *id) : nullptr;
310} 324}
311 325
312VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
313 return GetEntryRaw(entry.title_id, entry.type);
314}
315
316std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 326std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
317 const auto raw = GetEntryRaw(title_id, type); 327 const auto raw = GetEntryRaw(title_id, type);
318 if (raw == nullptr) 328 if (raw == nullptr)
@@ -320,10 +330,6 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
320 return std::make_unique<NCA>(raw, nullptr, 0, keys); 330 return std::make_unique<NCA>(raw, nullptr, 0, keys);
321} 331}
322 332
323std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
324 return GetEntry(entry.title_id, entry.type);
325}
326
327template <typename T> 333template <typename T>
328void RegisteredCache::IterateAllMetadata( 334void RegisteredCache::IterateAllMetadata(
329 std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc, 335 std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc,
@@ -348,25 +354,14 @@ void RegisteredCache::IterateAllMetadata(
348 } 354 }
349} 355}
350 356
351std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const { 357std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
352 std::vector<RegisteredCacheEntry> out;
353 IterateAllMetadata<RegisteredCacheEntry>(
354 out,
355 [](const CNMT& c, const ContentRecord& r) {
356 return RegisteredCacheEntry{c.GetTitleID(), r.type};
357 },
358 [](const CNMT& c, const ContentRecord& r) { return true; });
359 return out;
360}
361
362std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
363 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, 358 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
364 std::optional<u64> title_id) const { 359 std::optional<u64> title_id) const {
365 std::vector<RegisteredCacheEntry> out; 360 std::vector<ContentProviderEntry> out;
366 IterateAllMetadata<RegisteredCacheEntry>( 361 IterateAllMetadata<ContentProviderEntry>(
367 out, 362 out,
368 [](const CNMT& c, const ContentRecord& r) { 363 [](const CNMT& c, const ContentRecord& r) {
369 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 364 return ContentProviderEntry{c.GetTitleID(), r.type};
370 }, 365 },
371 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 366 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
372 if (title_type && *title_type != c.GetType()) 367 if (title_type && *title_type != c.GetType())
@@ -521,37 +516,56 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
521 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
522} 517}
523 518
524RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches) 519ContentProviderUnion::~ContentProviderUnion() = default;
525 : caches(std::move(caches)) {}
526 520
527void RegisteredCacheUnion::Refresh() { 521void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) {
528 for (const auto& c : caches) 522 providers[slot] = provider;
529 c->Refresh();
530} 523}
531 524
532bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const { 525void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) {
533 return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) { 526 providers[slot] = nullptr;
534 return cache->HasEntry(title_id, type);
535 });
536} 527}
537 528
538bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { 529void ContentProviderUnion::Refresh() {
539 return HasEntry(entry.title_id, entry.type); 530 for (auto& provider : providers) {
531 if (provider.second == nullptr)
532 continue;
533
534 provider.second->Refresh();
535 }
540} 536}
541 537
542std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { 538bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const {
543 for (const auto& c : caches) { 539 for (const auto& provider : providers) {
544 const auto res = c->GetEntryVersion(title_id); 540 if (provider.second == nullptr)
545 if (res) 541 continue;
542
543 if (provider.second->HasEntry(title_id, type))
544 return true;
545 }
546
547 return false;
548}
549
550std::optional<u32> ContentProviderUnion::GetEntryVersion(u64 title_id) const {
551 for (const auto& provider : providers) {
552 if (provider.second == nullptr)
553 continue;
554
555 const auto res = provider.second->GetEntryVersion(title_id);
556 if (res != std::nullopt)
546 return res; 557 return res;
547 } 558 }
548 559
549 return {}; 560 return std::nullopt;
550} 561}
551 562
552VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 563VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
553 for (const auto& c : caches) { 564 for (const auto& provider : providers) {
554 const auto res = c->GetEntryUnparsed(title_id, type); 565 if (provider.second == nullptr)
566 continue;
567
568 const auto res = provider.second->GetEntryUnparsed(title_id, type);
555 if (res != nullptr) 569 if (res != nullptr)
556 return res; 570 return res;
557 } 571 }
@@ -559,13 +573,12 @@ VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordTy
559 return nullptr; 573 return nullptr;
560} 574}
561 575
562VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const { 576VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
563 return GetEntryUnparsed(entry.title_id, entry.type); 577 for (const auto& provider : providers) {
564} 578 if (provider.second == nullptr)
579 continue;
565 580
566VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { 581 const auto res = provider.second->GetEntryRaw(title_id, type);
567 for (const auto& c : caches) {
568 const auto res = c->GetEntryRaw(title_id, type);
569 if (res != nullptr) 582 if (res != nullptr)
570 return res; 583 return res;
571 } 584 }
@@ -573,30 +586,56 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType ty
573 return nullptr; 586 return nullptr;
574} 587}
575 588
576VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const { 589std::unique_ptr<NCA> ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const {
577 return GetEntryRaw(entry.title_id, entry.type); 590 for (const auto& provider : providers) {
578} 591 if (provider.second == nullptr)
592 continue;
579 593
580std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 594 auto res = provider.second->GetEntry(title_id, type);
581 const auto raw = GetEntryRaw(title_id, type); 595 if (res != nullptr)
582 if (raw == nullptr) 596 return res;
583 return nullptr; 597 }
584 return std::make_unique<NCA>(raw); 598
599 return nullptr;
585} 600}
586 601
587std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 602std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
588 return GetEntry(entry.title_id, entry.type); 603 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
604 std::optional<u64> title_id) const {
605 std::vector<ContentProviderEntry> out;
606
607 for (const auto& provider : providers) {
608 if (provider.second == nullptr)
609 continue;
610
611 const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
612 std::copy(vec.begin(), vec.end(), std::back_inserter(out));
613 }
614
615 std::sort(out.begin(), out.end());
616 out.erase(std::unique(out.begin(), out.end()), out.end());
617 return out;
589} 618}
590 619
591std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { 620std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>>
592 std::vector<RegisteredCacheEntry> out; 621ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnionSlot> origin,
593 for (const auto& c : caches) { 622 std::optional<TitleType> title_type,
594 c->IterateAllMetadata<RegisteredCacheEntry>( 623 std::optional<ContentRecordType> record_type,
595 out, 624 std::optional<u64> title_id) const {
596 [](const CNMT& c, const ContentRecord& r) { 625 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> out;
597 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 626
598 }, 627 for (const auto& provider : providers) {
599 [](const CNMT& c, const ContentRecord& r) { return true; }); 628 if (provider.second == nullptr)
629 continue;
630
631 if (origin.has_value() && *origin != provider.first)
632 continue;
633
634 const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
635 std::transform(vec.begin(), vec.end(), std::back_inserter(out),
636 [&provider](const ContentProviderEntry& entry) {
637 return std::make_pair(provider.first, entry);
638 });
600 } 639 }
601 640
602 std::sort(out.begin(), out.end()); 641 std::sort(out.begin(), out.end());
@@ -604,25 +643,61 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
604 return out; 643 return out;
605} 644}
606 645
607std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( 646ManualContentProvider::~ManualContentProvider() = default;
647
648void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
649 u64 title_id, VirtualFile file) {
650 entries.insert_or_assign({title_type, content_type, title_id}, file);
651}
652
653void ManualContentProvider::ClearAllEntries() {
654 entries.clear();
655}
656
657void ManualContentProvider::Refresh() {}
658
659bool ManualContentProvider::HasEntry(u64 title_id, ContentRecordType type) const {
660 return GetEntryRaw(title_id, type) != nullptr;
661}
662
663std::optional<u32> ManualContentProvider::GetEntryVersion(u64 title_id) const {
664 return std::nullopt;
665}
666
667VirtualFile ManualContentProvider::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
668 return GetEntryRaw(title_id, type);
669}
670
671VirtualFile ManualContentProvider::GetEntryRaw(u64 title_id, ContentRecordType type) const {
672 const auto iter =
673 std::find_if(entries.begin(), entries.end(), [title_id, type](const auto& entry) {
674 const auto [title_type, content_type, e_title_id] = entry.first;
675 return content_type == type && e_title_id == title_id;
676 });
677 if (iter == entries.end())
678 return nullptr;
679 return iter->second;
680}
681
682std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecordType type) const {
683 const auto res = GetEntryRaw(title_id, type);
684 if (res == nullptr)
685 return nullptr;
686 return std::make_unique<NCA>(res, nullptr, 0, keys);
687}
688
689std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
608 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, 690 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
609 std::optional<u64> title_id) const { 691 std::optional<u64> title_id) const {
610 std::vector<RegisteredCacheEntry> out; 692 std::vector<ContentProviderEntry> out;
611 for (const auto& c : caches) { 693
612 c->IterateAllMetadata<RegisteredCacheEntry>( 694 for (const auto& entry : entries) {
613 out, 695 const auto [e_title_type, e_content_type, e_title_id] = entry.first;
614 [](const CNMT& c, const ContentRecord& r) { 696 if ((title_type == std::nullopt || e_title_type == *title_type) &&
615 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 697 (record_type == std::nullopt || e_content_type == *record_type) &&
616 }, 698 (title_id == std::nullopt || e_title_id == *title_id)) {
617 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 699 out.emplace_back(ContentProviderEntry{e_title_id, e_content_type});
618 if (title_type && *title_type != c.GetType()) 700 }
619 return false;
620 if (record_type && *record_type != r.type)
621 return false;
622 if (title_id && *title_id != c.GetTitleID())
623 return false;
624 return true;
625 });
626 } 701 }
627 702
628 std::sort(out.begin(), out.end()); 703 std::sort(out.begin(), out.end());
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 3b77af4e0..ec9052653 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -21,12 +21,13 @@ class NSP;
21class XCI; 21class XCI;
22 22
23enum class ContentRecordType : u8; 23enum class ContentRecordType : u8;
24enum class NCAContentType : u8;
24enum class TitleType : u8; 25enum class TitleType : u8;
25 26
26struct ContentRecord; 27struct ContentRecord;
27 28
28using NcaID = std::array<u8, 0x10>; 29using NcaID = std::array<u8, 0x10>;
29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 30using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
30using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>; 31using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
31 32
32enum class InstallResult { 33enum class InstallResult {
@@ -36,7 +37,7 @@ enum class InstallResult {
36 ErrorMetaFailed, 37 ErrorMetaFailed,
37}; 38};
38 39
39struct RegisteredCacheEntry { 40struct ContentProviderEntry {
40 u64 title_id; 41 u64 title_id;
41 ContentRecordType type; 42 ContentRecordType type;
42 43
@@ -47,12 +48,46 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
47 return base_title_id | 0x800; 48 return base_title_id | 0x800;
48} 49}
49 50
51ContentRecordType GetCRTypeFromNCAType(NCAContentType type);
52
50// boost flat_map requires operator< for O(log(n)) lookups. 53// boost flat_map requires operator< for O(log(n)) lookups.
51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 54bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
52 55
53// std unique requires operator== to identify duplicates. 56// std unique requires operator== to identify duplicates.
54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 57bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
55bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 58bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
59
60class ContentProvider {
61public:
62 virtual ~ContentProvider();
63
64 virtual void Refresh() = 0;
65
66 virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
67 virtual bool HasEntry(ContentProviderEntry entry) const;
68
69 virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
70
71 virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
72 virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
73
74 virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
75 virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
76
77 virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
78 virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
79
80 virtual std::vector<ContentProviderEntry> ListEntries() const;
81
82 // If a parameter is not std::nullopt, it will be filtered for from all entries.
83 virtual std::vector<ContentProviderEntry> ListEntriesFilter(
84 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
85 std::optional<u64> title_id = {}) const = 0;
86
87protected:
88 // A single instance of KeyManager to be used by GetEntry()
89 Core::Crypto::KeyManager keys;
90};
56 91
57/* 92/*
58 * A class that catalogues NCAs in the registered directory structure. 93 * A class that catalogues NCAs in the registered directory structure.
@@ -67,39 +102,32 @@ bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
67 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient 102 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
68 * when 4GB splitting can be ignored.) 103 * when 4GB splitting can be ignored.)
69 */ 104 */
70class RegisteredCache { 105class RegisteredCache : public ContentProvider {
71 friend class RegisteredCacheUnion;
72
73public: 106public:
74 // Parsing function defines the conversion from raw file to NCA. If there are other steps 107 // Parsing function defines the conversion from raw file to NCA. If there are other steps
75 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 108 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
76 // parsing function. 109 // parsing function.
77 explicit RegisteredCache(VirtualDir dir, 110 explicit RegisteredCache(VirtualDir dir,
78 RegisteredCacheParsingFunction parsing_function = 111 ContentProviderParsingFunction parsing_function =
79 [](const VirtualFile& file, const NcaID& id) { return file; }); 112 [](const VirtualFile& file, const NcaID& id) { return file; });
80 ~RegisteredCache(); 113 ~RegisteredCache() override;
81 114
82 void Refresh(); 115 void Refresh() override;
83 116
84 bool HasEntry(u64 title_id, ContentRecordType type) const; 117 bool HasEntry(u64 title_id, ContentRecordType type) const override;
85 bool HasEntry(RegisteredCacheEntry entry) const;
86 118
87 std::optional<u32> GetEntryVersion(u64 title_id) const; 119 std::optional<u32> GetEntryVersion(u64 title_id) const override;
88 120
89 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 121 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
90 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
91 122
92 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 123 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
93 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
94 124
95 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 125 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
96 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
97 126
98 std::vector<RegisteredCacheEntry> ListEntries() const;
99 // If a parameter is not std::nullopt, it will be filtered for from all entries. 127 // If a parameter is not std::nullopt, it will be filtered for from all entries.
100 std::vector<RegisteredCacheEntry> ListEntriesFilter( 128 std::vector<ContentProviderEntry> ListEntriesFilter(
101 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 129 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
102 std::optional<u64> title_id = {}) const; 130 std::optional<u64> title_id = {}) const override;
103 131
104 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 132 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
105 // there is a meta NCA and all of them are accessible. 133 // there is a meta NCA and all of them are accessible.
@@ -131,46 +159,70 @@ private:
131 bool RawInstallYuzuMeta(const CNMT& cnmt); 159 bool RawInstallYuzuMeta(const CNMT& cnmt);
132 160
133 VirtualDir dir; 161 VirtualDir dir;
134 RegisteredCacheParsingFunction parser; 162 ContentProviderParsingFunction parser;
135 Core::Crypto::KeyManager keys;
136 163
137 // maps tid -> NcaID of meta 164 // maps tid -> NcaID of meta
138 boost::container::flat_map<u64, NcaID> meta_id; 165 std::map<u64, NcaID> meta_id;
139 // maps tid -> meta 166 // maps tid -> meta
140 boost::container::flat_map<u64, CNMT> meta; 167 std::map<u64, CNMT> meta;
141 // maps tid -> meta for CNMT in yuzu_meta 168 // maps tid -> meta for CNMT in yuzu_meta
142 boost::container::flat_map<u64, CNMT> yuzu_meta; 169 std::map<u64, CNMT> yuzu_meta;
143}; 170};
144 171
145// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 172enum class ContentProviderUnionSlot {
146class RegisteredCacheUnion { 173 SysNAND, ///< System NAND
147public: 174 UserNAND, ///< User NAND
148 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches); 175 SDMC, ///< SD Card
149 176 FrontendManual, ///< Frontend-defined game list or similar
150 void Refresh(); 177};
151
152 bool HasEntry(u64 title_id, ContentRecordType type) const;
153 bool HasEntry(RegisteredCacheEntry entry) const;
154
155 std::optional<u32> GetEntryVersion(u64 title_id) const;
156
157 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
159
160 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
161 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
162
163 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
164 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
165 178
166 std::vector<RegisteredCacheEntry> ListEntries() const; 179// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
167 // If a parameter is not std::nullopt, it will be filtered for from all entries. 180class ContentProviderUnion : public ContentProvider {
168 std::vector<RegisteredCacheEntry> ListEntriesFilter( 181public:
182 ~ContentProviderUnion() override;
183
184 void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider);
185 void ClearSlot(ContentProviderUnionSlot slot);
186
187 void Refresh() override;
188 bool HasEntry(u64 title_id, ContentRecordType type) const override;
189 std::optional<u32> GetEntryVersion(u64 title_id) const override;
190 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
191 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
192 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
193 std::vector<ContentProviderEntry> ListEntriesFilter(
194 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
195 std::optional<u64> title_id) const override;
196
197 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin(
198 std::optional<ContentProviderUnionSlot> origin = {},
169 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 199 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
170 std::optional<u64> title_id = {}) const; 200 std::optional<u64> title_id = {}) const;
171 201
172private: 202private:
173 std::vector<RegisteredCache*> caches; 203 std::map<ContentProviderUnionSlot, ContentProvider*> providers;
204};
205
206class ManualContentProvider : public ContentProvider {
207public:
208 ~ManualContentProvider() override;
209
210 void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id,
211 VirtualFile file);
212 void ClearAllEntries();
213
214 void Refresh() override;
215 bool HasEntry(u64 title_id, ContentRecordType type) const override;
216 std::optional<u32> GetEntryVersion(u64 title_id) const override;
217 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
218 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
219 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
220 std::vector<ContentProviderEntry> ListEntriesFilter(
221 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
222 std::optional<u64> title_id) const override;
223
224private:
225 std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries;
174}; 226};
175 227
176} // namespace FileSys 228} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 6ad1e4f86..b2ccb2926 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
48 48
49 switch (storage) { 49 switch (storage) {
50 case StorageId::None: 50 case StorageId::None:
51 res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type); 51 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
52 break; 52 break;
53 case StorageId::NandSystem: 53 case StorageId::NandSystem:
54 res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); 54 res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index e1a4210db..c69caae0f 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -143,11 +143,12 @@ std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
143 return out; 143 return out;
144} 144}
145 145
146std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const { 146std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>>
147NSP::GetNCAs() const {
147 return ncas; 148 return ncas;
148} 149}
149 150
150std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const { 151std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const {
151 if (extracted) 152 if (extracted)
152 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 153 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
153 154
@@ -155,14 +156,14 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
155 if (title_id_iter == ncas.end()) 156 if (title_id_iter == ncas.end())
156 return nullptr; 157 return nullptr;
157 158
158 const auto type_iter = title_id_iter->second.find(type); 159 const auto type_iter = title_id_iter->second.find({title_type, type});
159 if (type_iter == title_id_iter->second.end()) 160 if (type_iter == title_id_iter->second.end())
160 return nullptr; 161 return nullptr;
161 162
162 return type_iter->second; 163 return type_iter->second;
163} 164}
164 165
165VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const { 166VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
166 if (extracted) 167 if (extracted)
167 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 168 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
168 const auto nca = GetNCA(title_id, type); 169 const auto nca = GetNCA(title_id, type);
@@ -240,7 +241,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
240 const CNMT cnmt(inner_file); 241 const CNMT cnmt(inner_file);
241 auto& ncas_title = ncas[cnmt.GetTitleID()]; 242 auto& ncas_title = ncas[cnmt.GetTitleID()];
242 243
243 ncas_title[ContentRecordType::Meta] = nca; 244 ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
244 for (const auto& rec : cnmt.GetContentRecords()) { 245 for (const auto& rec : cnmt.GetContentRecords()) {
245 const auto id_string = Common::HexArrayToString(rec.nca_id, false); 246 const auto id_string = Common::HexArrayToString(rec.nca_id, false);
246 const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); 247 const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -258,7 +259,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
258 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 259 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
259 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 260 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
260 (cnmt.GetTitleID() & 0x800) != 0)) { 261 (cnmt.GetTitleID() & 0x800) != 0)) {
261 ncas_title[rec.type] = std::move(next_nca); 262 ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca);
262 } 263 }
263 } 264 }
264 265
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 9a28ed5bb..ee9b6ce17 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -42,9 +42,12 @@ public:
42 // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML) 42 // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
43 std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const; 43 std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
44 std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const; 44 std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
45 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const; 45 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> GetNCAs()
46 std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const; 46 const;
47 VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const; 47 std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type,
48 TitleType title_type = TitleType::Application) const;
49 VirtualFile GetNCAFile(u64 title_id, ContentRecordType type,
50 TitleType title_type = TitleType::Application) const;
48 std::vector<Core::Crypto::Key128> GetTitlekey() const; 51 std::vector<Core::Crypto::Key128> GetTitlekey() const;
49 52
50 std::vector<VirtualFile> GetFiles() const override; 53 std::vector<VirtualFile> GetFiles() const override;
@@ -67,7 +70,7 @@ private:
67 70
68 std::shared_ptr<PartitionFilesystem> pfs; 71 std::shared_ptr<PartitionFilesystem> pfs;
69 // Map title id -> {map type -> NCA} 72 // Map title id -> {map type -> NCA}
70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; 73 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
71 std::vector<VirtualFile> ticket_files; 74 std::vector<VirtualFile> ticket_files;
72 75
73 Core::Crypto::KeyManager keys; 76 Core::Crypto::KeyManager keys;
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index d0bcb4660..70a522556 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -13,6 +13,23 @@
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.cpp b/src/core/hle/kernel/process.cpp
index 26c6b95ab..4e94048da 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -106,6 +106,8 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
106 is_64bit_process = metadata.Is64BitProgram(); 106 is_64bit_process = metadata.Is64BitProgram();
107 107
108 vm_manager.Reset(metadata.GetAddressSpaceType()); 108 vm_manager.Reset(metadata.GetAddressSpaceType());
109 // Ensure that the potentially resized page table is seen by CPU backends.
110 Memory::SetCurrentPageTable(&vm_manager.page_table);
109 111
110 const auto& caps = metadata.GetKernelCapabilities(); 112 const auto& caps = metadata.GetKernelCapabilities();
111 const auto capability_init_result = 113 const auto capability_init_result =
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index f060f2a3b..dda52f4c0 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -85,7 +85,7 @@ public:
85 return name; 85 return name;
86 } 86 }
87 87
88 static const HandleType HANDLE_TYPE = HandleType::Process; 88 static constexpr HandleType HANDLE_TYPE = HandleType::Process;
89 HandleType GetHandleType() const override { 89 HandleType GetHandleType() const override {
90 return HANDLE_TYPE; 90 return HANDLE_TYPE;
91 } 91 }
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h
index 2eb9dcbb7..84215f572 100644
--- a/src/core/hle/kernel/readable_event.h
+++ b/src/core/hle/kernel/readable_event.h
@@ -31,7 +31,7 @@ public:
31 return reset_type; 31 return reset_type;
32 } 32 }
33 33
34 static const HandleType HANDLE_TYPE = HandleType::ReadableEvent; 34 static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
35 HandleType GetHandleType() const override { 35 HandleType GetHandleType() const override {
36 return HANDLE_TYPE; 36 return HANDLE_TYPE;
37 } 37 }
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 70e09858a..2613a6bb5 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -41,7 +41,7 @@ public:
41 return GetTypeName(); 41 return GetTypeName();
42 } 42 }
43 43
44 static const HandleType HANDLE_TYPE = HandleType::ResourceLimit; 44 static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
45 HandleType GetHandleType() const override { 45 HandleType GetHandleType() const override {
46 return HANDLE_TYPE; 46 return HANDLE_TYPE;
47 } 47 }
diff --git a/src/core/hle/kernel/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/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9b0aa7f5f..7e17df98a 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -86,7 +86,7 @@ static FileSys::VirtualFile GetManualRomFS() {
86 if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) 86 if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
87 return out; 87 return out;
88 88
89 const auto& installed{FileSystem::GetUnionContents()}; 89 const auto& installed{Core::System::GetInstance().GetContentProvider()};
90 const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(), 90 const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
91 FileSys::ContentRecordType::Manual); 91 FileSys::ContentRecordType::Manual);
92 92
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index b506bc3dd..2d768d9fc 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -33,11 +33,11 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
33 33
34static std::vector<u64> AccumulateAOCTitleIDs() { 34static std::vector<u64> AccumulateAOCTitleIDs() {
35 std::vector<u64> add_on_content; 35 std::vector<u64> add_on_content;
36 const auto rcu = FileSystem::GetUnionContents(); 36 const auto& rcu = Core::System::GetInstance().GetContentProvider();
37 const auto list = 37 const auto list =
38 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 38 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
39 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), 39 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
40 [](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; }); 40 [](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
41 add_on_content.erase( 41 add_on_content.erase(
42 std::remove_if( 42 std::remove_if(
43 add_on_content.begin(), add_on_content.end(), 43 add_on_content.begin(), add_on_content.end(),
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 4c2b371c3..1ebfeb4bf 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -391,11 +391,6 @@ void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
391 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); 391 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
392} 392}
393 393
394FileSys::RegisteredCacheUnion GetUnionContents() {
395 return FileSys::RegisteredCacheUnion{
396 {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
397}
398
399FileSys::RegisteredCache* GetSystemNANDContents() { 394FileSys::RegisteredCache* GetSystemNANDContents() {
400 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 395 LOG_TRACE(Service_FS, "Opening System NAND Contents");
401 396
@@ -460,6 +455,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
460 if (bis_factory == nullptr) { 455 if (bis_factory == nullptr) {
461 bis_factory = 456 bis_factory =
462 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); 457 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
458 Core::System::GetInstance().RegisterContentProvider(
459 FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents());
460 Core::System::GetInstance().RegisterContentProvider(
461 FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents());
463 } 462 }
464 463
465 if (save_data_factory == nullptr) { 464 if (save_data_factory == nullptr) {
@@ -468,6 +467,8 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
468 467
469 if (sdmc_factory == nullptr) { 468 if (sdmc_factory == nullptr) {
470 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 469 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
470 Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
471 sdmc_factory->GetSDMCContents());
471 } 472 }
472} 473}
473 474
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 7cfc0d902..6481f237c 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -54,8 +54,6 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
54void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, 54void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
55 FileSys::SaveDataSize new_value); 55 FileSys::SaveDataSize new_value);
56 56
57FileSys::RegisteredCacheUnion GetUnionContents();
58
59FileSys::RegisteredCache* GetSystemNANDContents(); 57FileSys::RegisteredCache* GetSystemNANDContents();
60FileSys::RegisteredCache* GetUserNANDContents(); 58FileSys::RegisteredCache* GetUserNANDContents();
61FileSys::RegisteredCache* GetSDMCContents(); 59FileSys::RegisteredCache* GetSDMCContents();
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 657baddb8..0249b6992 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -115,11 +115,12 @@ private:
115 115
116 void Read(Kernel::HLERequestContext& ctx) { 116 void Read(Kernel::HLERequestContext& ctx) {
117 IPC::RequestParser rp{ctx}; 117 IPC::RequestParser rp{ctx};
118 const u64 unk = rp.Pop<u64>(); 118 const u64 option = rp.Pop<u64>();
119 const s64 offset = rp.Pop<s64>(); 119 const s64 offset = rp.Pop<s64>();
120 const s64 length = rp.Pop<s64>(); 120 const s64 length = rp.Pop<s64>();
121 121
122 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); 122 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
123 length);
123 124
124 // Error checking 125 // Error checking
125 if (length < 0) { 126 if (length < 0) {
@@ -148,11 +149,12 @@ private:
148 149
149 void Write(Kernel::HLERequestContext& ctx) { 150 void Write(Kernel::HLERequestContext& ctx) {
150 IPC::RequestParser rp{ctx}; 151 IPC::RequestParser rp{ctx};
151 const u64 unk = rp.Pop<u64>(); 152 const u64 option = rp.Pop<u64>();
152 const s64 offset = rp.Pop<s64>(); 153 const s64 offset = rp.Pop<s64>();
153 const s64 length = rp.Pop<s64>(); 154 const s64 length = rp.Pop<s64>();
154 155
155 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); 156 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
157 length);
156 158
157 // Error checking 159 // Error checking
158 if (length < 0) { 160 if (length < 0) {
@@ -250,10 +252,7 @@ private:
250 u64 next_entry_index = 0; 252 u64 next_entry_index = 0;
251 253
252 void Read(Kernel::HLERequestContext& ctx) { 254 void Read(Kernel::HLERequestContext& ctx) {
253 IPC::RequestParser rp{ctx}; 255 LOG_DEBUG(Service_FS, "called.");
254 const u64 unk = rp.Pop<u64>();
255
256 LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk);
257 256
258 // Calculate how many entries we can fit in the output buffer 257 // Calculate how many entries we can fit in the output buffer
259 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); 258 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index c7f5bbf28..3c5c53e24 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -21,12 +21,13 @@
21#include "core/hle/service/vi/display/vi_display.h" 21#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h" 22#include "core/hle/service/vi/layer/vi_layer.h"
23#include "core/perf_stats.h" 23#include "core/perf_stats.h"
24#include "core/settings.h"
24#include "video_core/renderer_base.h" 25#include "video_core/renderer_base.h"
25 26
26namespace Service::NVFlinger { 27namespace Service::NVFlinger {
27 28
28constexpr std::size_t SCREEN_REFRESH_RATE = 60; 29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); 30constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
30 31
31NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { 32NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
32 displays.emplace_back(0, "Default"); 33 displays.emplace_back(0, "Default");
@@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
36 displays.emplace_back(4, "Null"); 37 displays.emplace_back(4, "Null");
37 38
38 // Schedule the screen composition events 39 // Schedule the screen composition events
39 composition_event = 40 const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
40 core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { 41
42 composition_event = core_timing.RegisterEvent(
43 "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) {
41 Compose(); 44 Compose();
42 this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); 45 this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event);
43 }); 46 });
44 47
45 core_timing.ScheduleEvent(frame_ticks, composition_event); 48 core_timing.ScheduleEvent(ticks, composition_event);
46} 49}
47 50
48NVFlinger::~NVFlinger() { 51NVFlinger::~NVFlinger() {
@@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
62 const auto itr = 65 const auto itr =
63 std::find_if(displays.begin(), displays.end(), 66 std::find_if(displays.begin(), displays.end(),
64 [&](const VI::Display& display) { return display.GetName() == name; }); 67 [&](const VI::Display& display) { return display.GetName() == name; });
68
65 if (itr == displays.end()) { 69 if (itr == displays.end()) {
66 return {}; 70 return {};
67 } 71 }
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index ffe2eea8a..d7c47c197 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -21,6 +21,8 @@
21#include "core/memory.h" 21#include "core/memory.h"
22#include "core/settings.h" 22#include "core/settings.h"
23 23
24#pragma optimize("", off)
25
24namespace Loader { 26namespace Loader {
25namespace { 27namespace {
26struct MODHeader { 28struct MODHeader {
@@ -136,13 +138,13 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
136 138
137 // Apply patches if necessary 139 // Apply patches if necessary
138 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { 140 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
139 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); 141 std::vector<u8> pi_header;
140 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), 142 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
141 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); 143 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
142 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(), 144 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
143 program_image.end()); 145 program_image.end());
144 146
145 pi_header = pm->PatchNSO(pi_header); 147 pi_header = pm->PatchNSO(pi_header, file.GetName());
146 148
147 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin()); 149 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
148 } 150 }
diff --git a/src/core/settings.h b/src/core/settings.h
index d543eb32f..b84390745 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -393,6 +393,7 @@ struct Values {
393 bool use_disk_shader_cache; 393 bool use_disk_shader_cache;
394 bool use_accurate_gpu_emulation; 394 bool use_accurate_gpu_emulation;
395 bool use_asynchronous_gpu_emulation; 395 bool use_asynchronous_gpu_emulation;
396 bool force_30fps_mode;
396 397
397 float bg_red; 398 float bg_red;
398 float bg_green; 399 float bg_green;
diff --git a/src/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/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 8b1bea1ae..046d047cb 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -8,6 +8,7 @@
8#include "video_core/dma_pusher.h" 8#include "video_core/dma_pusher.h"
9#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/gpu.h" 10#include "video_core/gpu.h"
11#include "video_core/memory_manager.h"
11 12
12namespace Tegra { 13namespace Tegra {
13 14
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 7f613370b..2e1e96c81 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -1238,13 +1238,16 @@ union Instruction {
1238 1238
1239 union { 1239 union {
1240 BitField<20, 16, u64> imm20_16; 1240 BitField<20, 16, u64> imm20_16;
1241 BitField<35, 1, u64> high_b_rr; // used on RR
1241 BitField<36, 1, u64> product_shift_left; 1242 BitField<36, 1, u64> product_shift_left;
1242 BitField<37, 1, u64> merge_37; 1243 BitField<37, 1, u64> merge_37;
1243 BitField<48, 1, u64> sign_a; 1244 BitField<48, 1, u64> sign_a;
1244 BitField<49, 1, u64> sign_b; 1245 BitField<49, 1, u64> sign_b;
1246 BitField<50, 2, XmadMode> mode_cbf; // used by CR, RC
1245 BitField<50, 3, XmadMode> mode; 1247 BitField<50, 3, XmadMode> mode;
1246 BitField<52, 1, u64> high_b; 1248 BitField<52, 1, u64> high_b;
1247 BitField<53, 1, u64> high_a; 1249 BitField<53, 1, u64> high_a;
1250 BitField<55, 1, u64> product_shift_left_second; // used on CR
1248 BitField<56, 1, u64> merge_56; 1251 BitField<56, 1, u64> merge_56;
1249 } xmad; 1252 } xmad;
1250 1253
@@ -1662,7 +1665,7 @@ private:
1662 INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), 1665 INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"),
1663 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), 1666 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
1664 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), 1667 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
1665 INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"), 1668 INST("0011100-01000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"),
1666 INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"), 1669 INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"),
1667 INST("0000001---------", Id::LOP3_C, Type::ArithmeticInteger, "LOP3_C"), 1670 INST("0000001---------", Id::LOP3_C, Type::ArithmeticInteger, "LOP3_C"),
1668 INST("0101101111100---", Id::LOP3_R, Type::ArithmeticInteger, "LOP3_R"), 1671 INST("0101101111100---", Id::LOP3_R, Type::ArithmeticInteger, "LOP3_R"),
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 7989ec11b..25652e794 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "video_core/memory_manager.h"
10#include "video_core/renderer_opengl/gl_buffer_cache.h" 11#include "video_core/renderer_opengl/gl_buffer_cache.h"
11#include "video_core/renderer_opengl/gl_rasterizer.h" 12#include "video_core/renderer_opengl/gl_rasterizer.h"
12 13
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index 5842d6213..8d9ee81f1 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "video_core/memory_manager.h"
9#include "video_core/renderer_opengl/gl_global_cache.h" 10#include "video_core/renderer_opengl/gl_global_cache.h"
10#include "video_core/renderer_opengl/gl_rasterizer.h" 11#include "video_core/renderer_opengl/gl_rasterizer.h"
11#include "video_core/renderer_opengl/gl_shader_decompiler.h" 12#include "video_core/renderer_opengl/gl_shader_decompiler.h"
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 e2ec72b4e..f2ffc4710 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
16#include "core/settings.h" 16#include "core/settings.h"
17#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/memory_manager.h"
18#include "video_core/morton.h" 19#include "video_core/morton.h"
19#include "video_core/renderer_opengl/gl_rasterizer.h" 20#include "video_core/renderer_opengl/gl_rasterizer.h"
20#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 21#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
@@ -280,6 +281,10 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
280 params.component_type = ComponentTypeFromRenderTarget(config.format); 281 params.component_type = ComponentTypeFromRenderTarget(config.format);
281 params.type = GetFormatType(params.pixel_format); 282 params.type = GetFormatType(params.pixel_format);
282 params.width = config.width; 283 params.width = config.width;
284 if (!params.is_tiled) {
285 const u32 bpp = params.GetFormatBpp() / 8;
286 params.pitch = config.width * bpp;
287 }
283 params.height = config.height; 288 params.height = config.height;
284 params.unaligned_height = config.height; 289 params.unaligned_height = config.height;
285 params.target = SurfaceTarget::Texture2D; 290 params.target = SurfaceTarget::Texture2D;
@@ -676,8 +681,8 @@ void CachedSurface::FlushGLBuffer() {
676 gl_buffer[0].resize(GetSizeInBytes()); 681 gl_buffer[0].resize(GetSizeInBytes());
677 682
678 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 683 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
679 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 684 const u32 align = std::clamp(params.RowAlign(0), 1U, 8U);
680 ASSERT(params.width * GetBytesPerPixel(params.pixel_format) % 4 == 0); 685 glPixelStorei(GL_PACK_ALIGNMENT, align);
681 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); 686 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
682 ASSERT(!tuple.compressed); 687 ASSERT(!tuple.compressed);
683 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 688 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -722,8 +727,8 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
722 727
723 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 728 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
724 729
725 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 730 const u32 align = std::clamp(params.RowAlign(mip_map), 1U, 8U);
726 ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0); 731 glPixelStorei(GL_UNPACK_ALIGNMENT, align);
727 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); 732 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map)));
728 733
729 const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); 734 const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
@@ -1189,10 +1194,16 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1189 return new_surface; 1194 return new_surface;
1190 } 1195 }
1191 1196
1197 const bool old_compressed =
1198 GetFormatTuple(old_params.pixel_format, old_params.component_type).compressed;
1199 const bool new_compressed =
1200 GetFormatTuple(new_params.pixel_format, new_params.component_type).compressed;
1201 const bool compatible_formats =
1202 GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format) &&
1203 !(old_compressed || new_compressed);
1192 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1204 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1193 if (old_params.target == new_params.target && old_params.type == new_params.type && 1205 if (old_params.target == new_params.target && old_params.depth == new_params.depth &&
1194 old_params.depth == new_params.depth && old_params.depth == 1 && 1206 old_params.depth == 1 && compatible_formats) {
1195 GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) {
1196 FastCopySurface(old_surface, new_surface); 1207 FastCopySurface(old_surface, new_surface);
1197 return new_surface; 1208 return new_surface;
1198 } 1209 }
@@ -1207,7 +1218,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1207 case SurfaceTarget::TextureCubemap: 1218 case SurfaceTarget::TextureCubemap:
1208 case SurfaceTarget::Texture2DArray: 1219 case SurfaceTarget::Texture2DArray:
1209 case SurfaceTarget::TextureCubeArray: 1220 case SurfaceTarget::TextureCubeArray:
1210 if (old_params.pixel_format == new_params.pixel_format) 1221 if (compatible_formats)
1211 FastLayeredCopySurface(old_surface, new_surface); 1222 FastLayeredCopySurface(old_surface, new_surface);
1212 else { 1223 else {
1213 AccurateCopySurface(old_surface, new_surface); 1224 AccurateCopySurface(old_surface, new_surface);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index ad4fd3ad2..db280dbb3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -11,6 +11,7 @@
11#include <vector> 11#include <vector>
12 12
13#include "common/alignment.h" 13#include "common/alignment.h"
14#include "common/bit_util.h"
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "common/hash.h" 16#include "common/hash.h"
16#include "common/math_util.h" 17#include "common/math_util.h"
@@ -205,6 +206,13 @@ struct SurfaceParams {
205 return bd; 206 return bd;
206 } 207 }
207 208
209 u32 RowAlign(u32 mip_level) const {
210 const u32 m_width = MipWidth(mip_level);
211 const u32 bytes_per_pixel = GetBytesPerPixel(pixel_format);
212 const u32 l2 = Common::CountTrailingZeroes32(m_width * bytes_per_pixel);
213 return (1U << l2);
214 }
215
208 /// Creates SurfaceParams from a texture configuration 216 /// Creates SurfaceParams from a texture configuration
209 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, 217 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config,
210 const GLShader::SamplerEntry& entry); 218 const GLShader::SamplerEntry& entry);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index ab381932c..99f67494c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -7,6 +7,7 @@
7#include "common/hash.h" 7#include "common/hash.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/memory_manager.h"
10#include "video_core/renderer_opengl/gl_rasterizer.h" 11#include "video_core/renderer_opengl/gl_rasterizer.h"
11#include "video_core/renderer_opengl/gl_shader_cache.h" 12#include "video_core/renderer_opengl/gl_shader_cache.h"
12#include "video_core/renderer_opengl/gl_shader_decompiler.h" 13#include "video_core/renderer_opengl/gl_shader_decompiler.h"
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/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp
index c34843307..db15c0718 100644
--- a/src/video_core/shader/decode/xmad.cpp
+++ b/src/video_core/shader/decode/xmad.cpp
@@ -29,39 +29,55 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
29 const bool is_signed_b = instr.xmad.sign_b == 1; 29 const bool is_signed_b = instr.xmad.sign_b == 1;
30 const bool is_signed_c = is_signed_a; 30 const bool is_signed_c = is_signed_a;
31 31
32 auto [is_merge, op_b, op_c] = [&]() -> std::tuple<bool, Node, Node> { 32 auto [is_merge, is_psl, is_high_b, mode, op_b,
33 op_c] = [&]() -> std::tuple<bool, bool, bool, Tegra::Shader::XmadMode, Node, Node> {
33 switch (opcode->get().GetId()) { 34 switch (opcode->get().GetId()) {
34 case OpCode::Id::XMAD_CR: 35 case OpCode::Id::XMAD_CR:
35 return {instr.xmad.merge_56, 36 return {instr.xmad.merge_56,
37 instr.xmad.product_shift_left_second,
38 instr.xmad.high_b,
39 instr.xmad.mode_cbf,
36 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), 40 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()),
37 GetRegister(instr.gpr39)}; 41 GetRegister(instr.gpr39)};
38 case OpCode::Id::XMAD_RR: 42 case OpCode::Id::XMAD_RR:
39 return {instr.xmad.merge_37, GetRegister(instr.gpr20), GetRegister(instr.gpr39)}; 43 return {instr.xmad.merge_37, instr.xmad.product_shift_left, instr.xmad.high_b_rr,
44 instr.xmad.mode, GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
40 case OpCode::Id::XMAD_RC: 45 case OpCode::Id::XMAD_RC:
41 return {false, GetRegister(instr.gpr39), 46 return {false,
47 false,
48 instr.xmad.high_b,
49 instr.xmad.mode_cbf,
50 GetRegister(instr.gpr39),
42 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; 51 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
43 case OpCode::Id::XMAD_IMM: 52 case OpCode::Id::XMAD_IMM:
44 return {instr.xmad.merge_37, Immediate(static_cast<u32>(instr.xmad.imm20_16)), 53 return {instr.xmad.merge_37,
54 instr.xmad.product_shift_left,
55 false,
56 instr.xmad.mode,
57 Immediate(static_cast<u32>(instr.xmad.imm20_16)),
45 GetRegister(instr.gpr39)}; 58 GetRegister(instr.gpr39)};
46 } 59 }
47 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); 60 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
48 return {false, Immediate(0), Immediate(0)}; 61 return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)};
49 }(); 62 }();
50 63
51 op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); 64 op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16);
52 65
53 const Node original_b = op_b; 66 const Node original_b = op_b;
54 op_b = BitfieldExtract(op_b, instr.xmad.high_b ? 16 : 0, 16); 67 op_b = BitfieldExtract(op_b, is_high_b ? 16 : 0, 16);
55 68
56 // TODO(Rodrigo): Use an appropiate sign for this operation 69 // TODO(Rodrigo): Use an appropiate sign for this operation
57 Node product = Operation(OperationCode::IMul, NO_PRECISE, op_a, op_b); 70 Node product = Operation(OperationCode::IMul, NO_PRECISE, op_a, op_b);
58 if (instr.xmad.product_shift_left) { 71 if (is_psl) {
59 product = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, product, Immediate(16)); 72 product = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, product, Immediate(16));
60 } 73 }
74 SetTemporal(bb, 0, product);
75 product = GetTemporal(0);
61 76
62 const Node original_c = op_c; 77 const Node original_c = op_c;
78 const Tegra::Shader::XmadMode set_mode = mode; // Workaround to clang compile error
63 op_c = [&]() { 79 op_c = [&]() {
64 switch (instr.xmad.mode) { 80 switch (set_mode) {
65 case Tegra::Shader::XmadMode::None: 81 case Tegra::Shader::XmadMode::None:
66 return original_c; 82 return original_c;
67 case Tegra::Shader::XmadMode::CLo: 83 case Tegra::Shader::XmadMode::CLo:
@@ -80,8 +96,13 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
80 } 96 }
81 }(); 97 }();
82 98
99 SetTemporal(bb, 1, op_c);
100 op_c = GetTemporal(1);
101
83 // TODO(Rodrigo): Use an appropiate sign for this operation 102 // TODO(Rodrigo): Use an appropiate sign for this operation
84 Node sum = Operation(OperationCode::IAdd, product, op_c); 103 Node sum = Operation(OperationCode::IAdd, product, op_c);
104 SetTemporal(bb, 2, sum);
105 sum = GetTemporal(2);
85 if (is_merge) { 106 if (is_merge) {
86 const Node a = BitfieldExtract(sum, 0, 16); 107 const Node a = BitfieldExtract(sum, 0, 16);
87 const Node b = 108 const Node b =
@@ -95,4 +116,4 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
95 return pc; 116 return pc;
96} 117}
97 118
98} // namespace VideoCommon::Shader \ No newline at end of file 119} // namespace VideoCommon::Shader
diff --git a/src/video_core/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/video_core/textures/convert.cpp b/src/video_core/textures/convert.cpp
index 5e439f036..82050bd51 100644
--- a/src/video_core/textures/convert.cpp
+++ b/src/video_core/textures/convert.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "video_core/surface.h"
13#include "video_core/textures/astc.h" 14#include "video_core/textures/astc.h"
14#include "video_core/textures/convert.h" 15#include "video_core/textures/convert.h"
15 16
diff --git a/src/video_core/textures/convert.h b/src/video_core/textures/convert.h
index 07cd8b5da..12542e71c 100644
--- a/src/video_core/textures/convert.h
+++ b/src/video_core/textures/convert.h
@@ -5,7 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/surface.h" 8
9namespace VideoCore::Surface {
10enum class PixelFormat;
11}
9 12
10namespace Tegra::Texture { 13namespace Tegra::Texture {
11 14
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 93ecc6e31..bea0d5bc2 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -7,9 +7,7 @@
7#include <array> 7#include <array>
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "video_core/memory_manager.h"
13 11
14namespace Tegra::Texture { 12namespace Tegra::Texture {
15 13
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 4cab599b4..2eb86d6e5 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -31,6 +31,8 @@ add_executable(yuzu
31 configuration/configure_general.h 31 configuration/configure_general.h
32 configuration/configure_graphics.cpp 32 configuration/configure_graphics.cpp
33 configuration/configure_graphics.h 33 configuration/configure_graphics.h
34 configuration/configure_hotkeys.cpp
35 configuration/configure_hotkeys.h
34 configuration/configure_input.cpp 36 configuration/configure_input.cpp
35 configuration/configure_input.h 37 configuration/configure_input.h
36 configuration/configure_input_player.cpp 38 configuration/configure_input_player.cpp
@@ -54,8 +56,6 @@ add_executable(yuzu
54 debugger/graphics/graphics_breakpoints.cpp 56 debugger/graphics/graphics_breakpoints.cpp
55 debugger/graphics/graphics_breakpoints.h 57 debugger/graphics/graphics_breakpoints.h
56 debugger/graphics/graphics_breakpoints_p.h 58 debugger/graphics/graphics_breakpoints_p.h
57 debugger/graphics/graphics_surface.cpp
58 debugger/graphics/graphics_surface.h
59 debugger/console.cpp 59 debugger/console.cpp
60 debugger/console.h 60 debugger/console.h
61 debugger/profiler.cpp 61 debugger/profiler.cpp
@@ -78,6 +78,8 @@ add_executable(yuzu
78 ui_settings.h 78 ui_settings.h
79 util/limitable_input_dialog.cpp 79 util/limitable_input_dialog.cpp
80 util/limitable_input_dialog.h 80 util/limitable_input_dialog.h
81 util/sequence_dialog/sequence_dialog.cpp
82 util/sequence_dialog/sequence_dialog.h
81 util/spinbox.cpp 83 util/spinbox.cpp
82 util/spinbox.h 84 util/spinbox.h
83 util/util.cpp 85 util/util.cpp
@@ -95,6 +97,7 @@ set(UIS
95 configuration/configure_gamelist.ui 97 configuration/configure_gamelist.ui
96 configuration/configure_general.ui 98 configuration/configure_general.ui
97 configuration/configure_graphics.ui 99 configuration/configure_graphics.ui
100 configuration/configure_hotkeys.ui
98 configuration/configure_input.ui 101 configuration/configure_input.ui
99 configuration/configure_input_player.ui 102 configuration/configure_input_player.ui
100 configuration/configure_input_simple.ui 103 configuration/configure_input_simple.ui
@@ -105,7 +108,6 @@ set(UIS
105 configuration/configure_touchscreen_advanced.ui 108 configuration/configure_touchscreen_advanced.ui
106 configuration/configure_web.ui 109 configuration/configure_web.ui
107 compatdb.ui 110 compatdb.ui
108 hotkeys.ui
109 loading_screen.ui 111 loading_screen.ui
110 main.ui 112 main.ui
111) 113)
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index f95f7fe3c..743b24d76 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -4,6 +4,7 @@
4 4
5#include <mutex> 5#include <mutex>
6#include <QDialogButtonBox> 6#include <QDialogButtonBox>
7#include <QHeaderView>
7#include <QLabel> 8#include <QLabel>
8#include <QLineEdit> 9#include <QLineEdit>
9#include <QScrollArea> 10#include <QScrollArea>
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h
index 868573324..1c2922e54 100644
--- a/src/yuzu/applets/profile_select.h
+++ b/src/yuzu/applets/profile_select.h
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8#include <QDialog> 8#include <QDialog>
9#include <QList> 9#include <QList>
10#include <QTreeView>
10#include "core/frontend/applets/profile_select.h" 11#include "core/frontend/applets/profile_select.h"
11 12
12class GMainWindow; 13class GMainWindow;
@@ -16,7 +17,6 @@ class QLabel;
16class QScrollArea; 17class QScrollArea;
17class QStandardItem; 18class QStandardItem;
18class QStandardItemModel; 19class QStandardItemModel;
19class QTreeView;
20class QVBoxLayout; 20class QVBoxLayout;
21 21
22class QtProfileSelectionDialog final : public QDialog { 22class QtProfileSelectionDialog final : public QDialog {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 7438fbc0a..c29f2d2dc 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -1,6 +1,13 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#include <QApplication> 5#include <QApplication>
2#include <QHBoxLayout> 6#include <QHBoxLayout>
3#include <QKeyEvent> 7#include <QKeyEvent>
8#include <QOffscreenSurface>
9#include <QOpenGLWindow>
10#include <QPainter>
4#include <QScreen> 11#include <QScreen>
5#include <QWindow> 12#include <QWindow>
6#include <fmt/format.h> 13#include <fmt/format.h>
@@ -82,13 +89,36 @@ void EmuThread::run() {
82 render_window->moveContext(); 89 render_window->moveContext();
83} 90}
84 91
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 dead9f807..8725a78dc 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <QKeySequence>
5#include <QSettings> 7#include <QSettings>
6#include "common/file_util.h" 8#include "common/file_util.h"
7#include "configure_input_simple.h" 9#include "configure_input_simple.h"
@@ -9,7 +11,6 @@
9#include "core/hle/service/hid/controllers/npad.h" 11#include "core/hle/service/hid/controllers/npad.h"
10#include "input_common/main.h" 12#include "input_common/main.h"
11#include "yuzu/configuration/config.h" 13#include "yuzu/configuration/config.h"
12#include "yuzu/ui_settings.h"
13 14
14Config::Config() { 15Config::Config() {
15 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 16 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -17,7 +18,6 @@ Config::Config() {
17 FileUtil::CreateFullPath(qt_config_loc); 18 FileUtil::CreateFullPath(qt_config_loc);
18 qt_config = 19 qt_config =
19 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 20 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
20
21 Reload(); 21 Reload();
22} 22}
23 23
@@ -205,6 +205,27 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
205 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight, 205 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
206}; 206};
207 207
208// This shouldn't have anything except static initializers (no functions). So
209// QKeySequnce(...).toString() is NOT ALLOWED HERE.
210// This must be in alphabetical order according to action name as it must have the same order as
211// UISetting::values.shortcuts, which is alphabetically ordered.
212const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{
213 {{"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}},
214 {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}},
215 {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}},
216 {"Exit yuzu", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}},
217 {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}},
218 {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}},
219 {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}},
220 {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}},
221 {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}},
222 {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}},
223 {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}},
224 {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}},
225 {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}},
226 {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}},
227 {"Change Docked Mode", "Main Window", {"F10", Qt::ApplicationShortcut}}}};
228
208void Config::ReadPlayerValues() { 229void Config::ReadPlayerValues() {
209 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 230 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
210 auto& player = Settings::values.players[p]; 231 auto& player = Settings::values.players[p];
@@ -373,6 +394,7 @@ void Config::ReadValues() {
373 ReadSetting("use_accurate_gpu_emulation", false).toBool(); 394 ReadSetting("use_accurate_gpu_emulation", false).toBool();
374 Settings::values.use_asynchronous_gpu_emulation = 395 Settings::values.use_asynchronous_gpu_emulation =
375 ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); 396 ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
397 Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool();
376 398
377 Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); 399 Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat();
378 Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); 400 Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat();
@@ -502,26 +524,21 @@ void Config::ReadValues() {
502 qt_config->beginGroup("Paths"); 524 qt_config->beginGroup("Paths");
503 UISettings::values.roms_path = ReadSetting("romsPath").toString(); 525 UISettings::values.roms_path = ReadSetting("romsPath").toString();
504 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); 526 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
505 UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString(); 527 UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString();
506 UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool(); 528 UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool();
507 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); 529 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
508 qt_config->endGroup(); 530 qt_config->endGroup();
509 531
510 qt_config->beginGroup("Shortcuts"); 532 qt_config->beginGroup("Shortcuts");
511 QStringList groups = qt_config->childGroups(); 533 for (auto [name, group, shortcut] : default_hotkeys) {
512 for (auto group : groups) { 534 auto [keyseq, context] = shortcut;
513 qt_config->beginGroup(group); 535 qt_config->beginGroup(group);
514 536 qt_config->beginGroup(name);
515 QStringList hotkeys = qt_config->childGroups(); 537 UISettings::values.shortcuts.push_back(
516 for (auto hotkey : hotkeys) { 538 {name,
517 qt_config->beginGroup(hotkey); 539 group,
518 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( 540 {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}});
519 group + "/" + hotkey, 541 qt_config->endGroup();
520 UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(),
521 ReadSetting("Context").toInt())));
522 qt_config->endGroup();
523 }
524
525 qt_config->endGroup(); 542 qt_config->endGroup();
526 } 543 }
527 qt_config->endGroup(); 544 qt_config->endGroup();
@@ -648,6 +665,7 @@ void Config::SaveValues() {
648 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); 665 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
649 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, 666 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
650 false); 667 false);
668 WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false);
651 669
652 // Cast to double because Qt's written float values are not human-readable 670 // Cast to double because Qt's written float values are not human-readable
653 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); 671 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
@@ -752,15 +770,22 @@ void Config::SaveValues() {
752 WriteSetting("romsPath", UISettings::values.roms_path); 770 WriteSetting("romsPath", UISettings::values.roms_path);
753 WriteSetting("symbolsPath", UISettings::values.symbols_path); 771 WriteSetting("symbolsPath", UISettings::values.symbols_path);
754 WriteSetting("screenshotPath", UISettings::values.screenshot_path); 772 WriteSetting("screenshotPath", UISettings::values.screenshot_path);
755 WriteSetting("gameListRootDir", UISettings::values.gamedir, "."); 773 WriteSetting("gameListRootDir", UISettings::values.game_directory_path, ".");
756 WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false); 774 WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false);
757 WriteSetting("recentFiles", UISettings::values.recent_files); 775 WriteSetting("recentFiles", UISettings::values.recent_files);
758 qt_config->endGroup(); 776 qt_config->endGroup();
759 777
760 qt_config->beginGroup("Shortcuts"); 778 qt_config->beginGroup("Shortcuts");
761 for (auto shortcut : UISettings::values.shortcuts) { 779 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
762 WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first); 780 // However, their ordering must also be the same.
763 WriteSetting(shortcut.first + "/Context", shortcut.second.second); 781 for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
782 auto [name, group, shortcut] = UISettings::values.shortcuts[i];
783 qt_config->beginGroup(group);
784 qt_config->beginGroup(name);
785 WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first);
786 WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second);
787 qt_config->endGroup();
788 qt_config->endGroup();
764 } 789 }
765 qt_config->endGroup(); 790 qt_config->endGroup();
766 791
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index f4185db18..221d2364c 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -9,6 +9,7 @@
9#include <string> 9#include <string>
10#include <QVariant> 10#include <QVariant>
11#include "core/settings.h" 11#include "core/settings.h"
12#include "yuzu/ui_settings.h"
12 13
13class QSettings; 14class QSettings;
14 15
@@ -47,6 +48,8 @@ private:
47 void WriteSetting(const QString& name, const QVariant& value); 48 void WriteSetting(const QString& name, const QVariant& value);
48 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); 49 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
49 50
51 static const std::array<UISettings::Shortcut, 15> default_hotkeys;
52
50 std::unique_ptr<QSettings> qt_config; 53 std::unique_ptr<QSettings> qt_config;
51 std::string qt_config_loc; 54 std::string qt_config_loc;
52}; 55};
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 3f03f0b77..267717bc9 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -7,9 +7,15 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>382</width> 9 <width>382</width>
10 <height>241</height> 10 <height>650</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="minimumSize">
14 <size>
15 <width>0</width>
16 <height>650</height>
17 </size>
18 </property>
13 <property name="windowTitle"> 19 <property name="windowTitle">
14 <string>yuzu Configuration</string> 20 <string>yuzu Configuration</string>
15 </property> 21 </property>
@@ -62,6 +68,11 @@
62 <string>Input</string> 68 <string>Input</string>
63 </attribute> 69 </attribute>
64 </widget> 70 </widget>
71 <widget class="ConfigureHotkeys" name="hotkeysTab">
72 <attribute name="title">
73 <string>Hotkeys</string>
74 </attribute>
75 </widget>
65 <widget class="ConfigureGraphics" name="graphicsTab"> 76 <widget class="ConfigureGraphics" name="graphicsTab">
66 <attribute name="title"> 77 <attribute name="title">
67 <string>Graphics</string> 78 <string>Graphics</string>
@@ -150,6 +161,12 @@
150 <header>configuration/configure_input_simple.h</header> 161 <header>configuration/configure_input_simple.h</header>
151 <container>1</container> 162 <container>1</container>
152 </customwidget> 163 </customwidget>
164 <customwidget>
165 <class>ConfigureHotkeys</class>
166 <extends>QWidget</extends>
167 <header>configuration/configure_hotkeys.h</header>
168 <container>1</container>
169 </customwidget>
153 </customwidgets> 170 </customwidgets>
154 <resources/> 171 <resources/>
155 <connections> 172 <connections>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 777050405..51bd1f121 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,20 +8,22 @@
8#include "ui_configure.h" 8#include "ui_configure.h"
9#include "yuzu/configuration/config.h" 9#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/configure_dialog.h" 10#include "yuzu/configuration/configure_dialog.h"
11#include "yuzu/configuration/configure_input_player.h"
11#include "yuzu/hotkeys.h" 12#include "yuzu/hotkeys.h"
12 13
13ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry) 14ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
14 : QDialog(parent), ui(new Ui::ConfigureDialog) { 15 : QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) {
15 ui->setupUi(this); 16 ui->setupUi(this);
16 ui->generalTab->PopulateHotkeyList(registry); 17 ui->hotkeysTab->Populate(registry);
17 this->setConfiguration(); 18 this->setConfiguration();
18 this->PopulateSelectionList(); 19 this->PopulateSelectionList();
19 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, 20 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
20 &ConfigureDialog::UpdateVisibleTabs); 21 &ConfigureDialog::UpdateVisibleTabs);
21
22 adjustSize(); 22 adjustSize();
23
24 ui->selectorList->setCurrentRow(0); 23 ui->selectorList->setCurrentRow(0);
24
25 // Synchronise lists upon initialisation
26 ui->hotkeysTab->EmitHotkeysChanged();
25} 27}
26 28
27ConfigureDialog::~ConfigureDialog() = default; 29ConfigureDialog::~ConfigureDialog() = default;
@@ -34,6 +36,7 @@ void ConfigureDialog::applyConfiguration() {
34 ui->systemTab->applyConfiguration(); 36 ui->systemTab->applyConfiguration();
35 ui->profileManagerTab->applyConfiguration(); 37 ui->profileManagerTab->applyConfiguration();
36 ui->inputTab->applyConfiguration(); 38 ui->inputTab->applyConfiguration();
39 ui->hotkeysTab->applyConfiguration(registry);
37 ui->graphicsTab->applyConfiguration(); 40 ui->graphicsTab->applyConfiguration();
38 ui->audioTab->applyConfiguration(); 41 ui->audioTab->applyConfiguration();
39 ui->debugTab->applyConfiguration(); 42 ui->debugTab->applyConfiguration();
@@ -47,7 +50,7 @@ void ConfigureDialog::PopulateSelectionList() {
47 {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}}, 50 {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}},
48 {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}}, 51 {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}},
49 {tr("Graphics"), {tr("Graphics")}}, 52 {tr("Graphics"), {tr("Graphics")}},
50 {tr("Controls"), {tr("Input")}}}}; 53 {tr("Controls"), {tr("Input"), tr("Hotkeys")}}}};
51 54
52 for (const auto& entry : items) { 55 for (const auto& entry : items) {
53 auto* const item = new QListWidgetItem(entry.first); 56 auto* const item = new QListWidgetItem(entry.first);
@@ -66,6 +69,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
66 {tr("System"), ui->systemTab}, 69 {tr("System"), ui->systemTab},
67 {tr("Profiles"), ui->profileManagerTab}, 70 {tr("Profiles"), ui->profileManagerTab},
68 {tr("Input"), ui->inputTab}, 71 {tr("Input"), ui->inputTab},
72 {tr("Hotkeys"), ui->hotkeysTab},
69 {tr("Graphics"), ui->graphicsTab}, 73 {tr("Graphics"), ui->graphicsTab},
70 {tr("Audio"), ui->audioTab}, 74 {tr("Audio"), ui->audioTab},
71 {tr("Debug"), ui->debugTab}, 75 {tr("Debug"), ui->debugTab},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 243d9fa09..2363ba584 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -17,7 +17,7 @@ class ConfigureDialog : public QDialog {
17 Q_OBJECT 17 Q_OBJECT
18 18
19public: 19public:
20 explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry); 20 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
21 ~ConfigureDialog() override; 21 ~ConfigureDialog() override;
22 22
23 void applyConfiguration(); 23 void applyConfiguration();
@@ -28,4 +28,5 @@ private:
28 void PopulateSelectionList(); 28 void PopulateSelectionList();
29 29
30 std::unique_ptr<Ui::ConfigureDialog> ui; 30 std::unique_ptr<Ui::ConfigureDialog> ui;
31 HotkeyRegistry& registry;
31}; 32};
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 389fcf667..e48f4f5a3 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -28,19 +28,15 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
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));
35 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 35 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
36} 36}
37 37
38void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
39 ui->widget->Populate(registry);
40}
41
42void ConfigureGeneral::applyConfiguration() { 38void ConfigureGeneral::applyConfiguration() {
43 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 39 UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked();
44 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 40 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
45 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 41 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
46 UISettings::values.theme = 42 UISettings::values.theme =
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 59738af40..df41d995b 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -20,7 +20,6 @@ public:
20 explicit ConfigureGeneral(QWidget* parent = nullptr); 20 explicit ConfigureGeneral(QWidget* parent = nullptr);
21 ~ConfigureGeneral() override; 21 ~ConfigureGeneral() override;
22 22
23 void PopulateHotkeyList(const HotkeyRegistry& registry);
24 void applyConfiguration(); 23 void applyConfiguration();
25 24
26private: 25private:
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 01d1c0b8e..1a5721fe7 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -98,22 +98,6 @@
98 </widget> 98 </widget>
99 </item> 99 </item>
100 <item> 100 <item>
101 <widget class="QGroupBox" name="HotKeysGroupBox">
102 <property name="title">
103 <string>Hotkeys</string>
104 </property>
105 <layout class="QHBoxLayout" name="HotKeysHorizontalLayout">
106 <item>
107 <layout class="QVBoxLayout" name="HotKeysVerticalLayout">
108 <item>
109 <widget class="GHotkeysDialog" name="widget" native="true"/>
110 </item>
111 </layout>
112 </item>
113 </layout>
114 </widget>
115 </item>
116 <item>
117 <spacer name="verticalSpacer"> 101 <spacer name="verticalSpacer">
118 <property name="orientation"> 102 <property name="orientation">
119 <enum>Qt::Vertical</enum> 103 <enum>Qt::Vertical</enum>
@@ -130,14 +114,6 @@
130 </item> 114 </item>
131 </layout> 115 </layout>
132 </widget> 116 </widget>
133 <customwidgets>
134 <customwidget>
135 <class>GHotkeysDialog</class>
136 <extends>QWidget</extends>
137 <header>hotkeys.h</header>
138 <container>1</container>
139 </customwidget>
140 </customwidgets>
141 <resources/> 117 <resources/>
142 <connections/> 118 <connections/>
143</ui> 119</ui>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index dd1d67488..0a9883d37 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -77,6 +77,8 @@ void ConfigureGraphics::setConfiguration() {
77 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); 77 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
78 ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 78 ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
79 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); 79 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
80 ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
81 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
80 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 82 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
81 Settings::values.bg_blue)); 83 Settings::values.bg_blue));
82} 84}
@@ -90,6 +92,7 @@ void ConfigureGraphics::applyConfiguration() {
90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); 92 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.use_asynchronous_gpu_emulation = 93 Settings::values.use_asynchronous_gpu_emulation =
92 ui->use_asynchronous_gpu_emulation->isChecked(); 94 ui->use_asynchronous_gpu_emulation->isChecked();
95 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
93 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 96 Settings::values.bg_red = static_cast<float>(bg_color.redF());
94 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 97 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
95 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 98 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index c6767e0ca..15ab18ecd 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -71,6 +71,13 @@
71 </widget> 71 </widget>
72 </item> 72 </item>
73 <item> 73 <item>
74 <widget class="QCheckBox" name="force_30fps_mode">
75 <property name="text">
76 <string>Force 30 FPS mode</string>
77 </property>
78 </widget>
79 </item>
80 <item>
74 <layout class="QHBoxLayout" name="horizontalLayout"> 81 <layout class="QHBoxLayout" name="horizontalLayout">
75 <item> 82 <item>
76 <widget class="QLabel" name="label"> 83 <widget class="QLabel" name="label">
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
new file mode 100644
index 000000000..bfb562535
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -0,0 +1,121 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QMessageBox>
6#include <QStandardItemModel>
7#include "core/settings.h"
8#include "ui_configure_hotkeys.h"
9#include "yuzu/configuration/configure_hotkeys.h"
10#include "yuzu/hotkeys.h"
11#include "yuzu/util/sequence_dialog/sequence_dialog.h"
12
13ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
14 : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) {
15 ui->setupUi(this);
16 setFocusPolicy(Qt::ClickFocus);
17
18 model = new QStandardItemModel(this);
19 model->setColumnCount(3);
20 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")});
21
22 connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure);
23 ui->hotkey_list->setModel(model);
24
25 // TODO(Kloen): Make context configurable as well (hiding the column for now)
26 ui->hotkey_list->hideColumn(2);
27
28 ui->hotkey_list->setColumnWidth(0, 200);
29 ui->hotkey_list->resizeColumnToContents(1);
30}
31
32ConfigureHotkeys::~ConfigureHotkeys() = default;
33
34void ConfigureHotkeys::EmitHotkeysChanged() {
35 emit HotkeysChanged(GetUsedKeyList());
36}
37
38QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const {
39 QList<QKeySequence> list;
40 for (int r = 0; r < model->rowCount(); r++) {
41 const QStandardItem* parent = model->item(r, 0);
42 for (int r2 = 0; r2 < parent->rowCount(); r2++) {
43 const QStandardItem* keyseq = parent->child(r2, 1);
44 list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText);
45 }
46 }
47 return list;
48}
49
50void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
51 for (const auto& group : registry.hotkey_groups) {
52 auto* parent_item = new QStandardItem(group.first);
53 parent_item->setEditable(false);
54 for (const auto& hotkey : group.second) {
55 auto* action = new QStandardItem(hotkey.first);
56 auto* keyseq =
57 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
58 action->setEditable(false);
59 keyseq->setEditable(false);
60 parent_item->appendRow({action, keyseq});
61 }
62 model->appendRow(parent_item);
63 }
64
65 ui->hotkey_list->expandAll();
66}
67
68void ConfigureHotkeys::Configure(QModelIndex index) {
69 if (index.parent() == QModelIndex())
70 return;
71
72 index = index.sibling(index.row(), 1);
73 auto* model = ui->hotkey_list->model();
74 auto previous_key = model->data(index);
75
76 auto* hotkey_dialog = new SequenceDialog;
77 int return_code = hotkey_dialog->exec();
78
79 auto key_sequence = hotkey_dialog->GetSequence();
80
81 if (return_code == QDialog::Rejected || key_sequence.isEmpty())
82 return;
83
84 if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) {
85 QMessageBox::critical(this, tr("Error in inputted key"),
86 tr("You're using a key that's already bound."));
87 } else {
88 model->setData(index, key_sequence.toString(QKeySequence::NativeText));
89 EmitHotkeysChanged();
90 }
91}
92
93bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) {
94 return GetUsedKeyList().contains(key_sequence);
95}
96
97void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) {
98 for (int key_id = 0; key_id < model->rowCount(); key_id++) {
99 const QStandardItem* parent = model->item(key_id, 0);
100 for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
101 const QStandardItem* action = parent->child(key_column_id, 0);
102 const QStandardItem* keyseq = parent->child(key_column_id, 1);
103 for (auto& [group, sub_actions] : registry.hotkey_groups) {
104 if (group != parent->text())
105 continue;
106 for (auto& [action_name, hotkey] : sub_actions) {
107 if (action_name != action->text())
108 continue;
109 hotkey.keyseq = QKeySequence(keyseq->text());
110 }
111 }
112 }
113 }
114
115 registry.SaveHotkeys();
116 Settings::Apply();
117}
118
119void ConfigureHotkeys::retranslateUi() {
120 ui->retranslateUi(this);
121}
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h
new file mode 100644
index 000000000..cd203aad6
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.h
@@ -0,0 +1,48 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9#include "core/settings.h"
10
11namespace Ui {
12class ConfigureHotkeys;
13}
14
15class HotkeyRegistry;
16class QStandardItemModel;
17
18class ConfigureHotkeys : public QWidget {
19 Q_OBJECT
20
21public:
22 explicit ConfigureHotkeys(QWidget* parent = nullptr);
23 ~ConfigureHotkeys() override;
24
25 void applyConfiguration(HotkeyRegistry& registry);
26 void retranslateUi();
27
28 void EmitHotkeysChanged();
29
30 /**
31 * Populates the hotkey list widget using data from the provided registry.
32 * Called everytime the Configure dialog is opened.
33 * @param registry The HotkeyRegistry whose data is used to populate the list.
34 */
35 void Populate(const HotkeyRegistry& registry);
36
37signals:
38 void HotkeysChanged(QList<QKeySequence> new_key_list);
39
40private:
41 void Configure(QModelIndex index);
42 bool IsUsedKey(QKeySequence key_sequence);
43 QList<QKeySequence> GetUsedKeyList() const;
44
45 std::unique_ptr<Ui::ConfigureHotkeys> ui;
46
47 QStandardItemModel* model;
48};
diff --git a/src/yuzu/configuration/configure_hotkeys.ui b/src/yuzu/configuration/configure_hotkeys.ui
new file mode 100644
index 000000000..0d0b70f38
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.ui
@@ -0,0 +1,42 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureHotkeys</class>
4 <widget class="QWidget" name="ConfigureHotkeys">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>363</width>
10 <height>388</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Hotkey Settings</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_2">
19 <item>
20 <widget class="QLabel" name="label_2">
21 <property name="text">
22 <string>Double-click on a binding to change it.</string>
23 </property>
24 </widget>
25 </item>
26 <item>
27 <widget class="QTreeView" name="hotkey_list">
28 <property name="editTriggers">
29 <set>QAbstractItemView::NoEditTriggers</set>
30 </property>
31 <property name="sortingEnabled">
32 <bool>false</bool>
33 </property>
34 </widget>
35 </item>
36 </layout>
37 </item>
38 </layout>
39 </widget>
40 <resources/>
41 <connections/>
42</ui> \ No newline at end of file
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
deleted file mode 100644
index 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 4422a572b..b0ca766ec 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -18,6 +18,7 @@
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "common/logging/log.h" 19#include "common/logging/log.h"
20#include "core/file_sys/patch_manager.h" 20#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/registered_cache.h"
21#include "yuzu/compatibility_list.h" 22#include "yuzu/compatibility_list.h"
22#include "yuzu/game_list.h" 23#include "yuzu/game_list.h"
23#include "yuzu/game_list_p.h" 24#include "yuzu/game_list_p.h"
@@ -193,8 +194,9 @@ void GameList::onFilterCloseClicked() {
193 main_window->filterBarSetChecked(false); 194 main_window->filterBarSetChecked(false);
194} 195}
195 196
196GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) 197GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider,
197 : QWidget{parent}, vfs(std::move(vfs)) { 198 GMainWindow* parent)
199 : QWidget{parent}, vfs(std::move(vfs)), provider(provider) {
198 watcher = new QFileSystemWatcher(this); 200 watcher = new QFileSystemWatcher(this);
199 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); 201 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
200 202
@@ -432,7 +434,8 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
432 434
433 emit ShouldCancelWorker(); 435 emit ShouldCancelWorker();
434 436
435 GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list); 437 GameListWorker* worker =
438 new GameListWorker(vfs, provider, dir_path, deep_scan, compatibility_list);
436 439
437 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); 440 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
438 connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, 441 connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
@@ -464,9 +467,10 @@ void GameList::LoadInterfaceLayout() {
464const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; 467const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
465 468
466void GameList::RefreshGameDirectory() { 469void GameList::RefreshGameDirectory() {
467 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { 470 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
468 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.");
469 search_field->clear(); 472 search_field->clear();
470 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 473 PopulateAsync(UISettings::values.game_directory_path,
474 UISettings::values.game_directory_deepscan);
471 } 475 }
472} 476}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 8ea5cbaaa..56007eef8 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -26,8 +26,9 @@ class GameListSearchField;
26class GMainWindow; 26class GMainWindow;
27 27
28namespace FileSys { 28namespace FileSys {
29class ManualContentProvider;
29class VfsFilesystem; 30class VfsFilesystem;
30} 31} // namespace FileSys
31 32
32enum class GameListOpenTarget { 33enum class GameListOpenTarget {
33 SaveData, 34 SaveData,
@@ -47,7 +48,8 @@ public:
47 COLUMN_COUNT, // Number of columns 48 COLUMN_COUNT, // Number of columns
48 }; 49 };
49 50
50 explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); 51 explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs,
52 FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr);
51 ~GameList() override; 53 ~GameList() override;
52 54
53 void clearFilter(); 55 void clearFilter();
@@ -86,6 +88,7 @@ private:
86 void RefreshGameDirectory(); 88 void RefreshGameDirectory();
87 89
88 std::shared_ptr<FileSys::VfsFilesystem> vfs; 90 std::shared_ptr<FileSys::VfsFilesystem> vfs;
91 FileSys::ManualContentProvider* provider;
89 GameListSearchField* search_field; 92 GameListSearchField* search_field;
90 GMainWindow* main_window = nullptr; 93 GMainWindow* main_window = nullptr;
91 QVBoxLayout* layout = nullptr; 94 QVBoxLayout* layout = nullptr;
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index b37710f59..8687e7c5a 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -12,12 +12,15 @@
12 12
13#include "common/common_paths.h" 13#include "common/common_paths.h"
14#include "common/file_util.h" 14#include "common/file_util.h"
15#include "core/core.h"
16#include "core/file_sys/card_image.h"
15#include "core/file_sys/content_archive.h" 17#include "core/file_sys/content_archive.h"
16#include "core/file_sys/control_metadata.h" 18#include "core/file_sys/control_metadata.h"
17#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
18#include "core/file_sys/nca_metadata.h" 20#include "core/file_sys/nca_metadata.h"
19#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/registered_cache.h" 22#include "core/file_sys/registered_cache.h"
23#include "core/file_sys/submission_package.h"
21#include "core/hle/service/filesystem/filesystem.h" 24#include "core/hle/service/filesystem/filesystem.h"
22#include "core/loader/loader.h" 25#include "core/loader/loader.h"
23#include "yuzu/compatibility_list.h" 26#include "yuzu/compatibility_list.h"
@@ -119,20 +122,25 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
119} 122}
120} // Anonymous namespace 123} // Anonymous namespace
121 124
122GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, 125GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs,
123 const CompatibilityList& compatibility_list) 126 FileSys::ManualContentProvider* provider, QString dir_path,
124 : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), 127 bool deep_scan, const CompatibilityList& compatibility_list)
128 : vfs(std::move(vfs)), provider(provider), dir_path(std::move(dir_path)), deep_scan(deep_scan),
125 compatibility_list(compatibility_list) {} 129 compatibility_list(compatibility_list) {}
126 130
127GameListWorker::~GameListWorker() = default; 131GameListWorker::~GameListWorker() = default;
128 132
129void GameListWorker::AddInstalledTitlesToGameList() { 133void GameListWorker::AddTitlesToGameList() {
130 const auto cache = Service::FileSystem::GetUnionContents(); 134 const auto& cache = dynamic_cast<FileSys::ContentProviderUnion&>(
131 const auto installed_games = cache.ListEntriesFilter(FileSys::TitleType::Application, 135 Core::System::GetInstance().GetContentProvider());
132 FileSys::ContentRecordType::Program); 136 const auto installed_games = cache.ListEntriesFilterOrigin(
137 std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
133 138
134 for (const auto& game : installed_games) { 139 for (const auto& [slot, game] : installed_games) {
135 const auto file = cache.GetEntryUnparsed(game); 140 if (slot == FileSys::ContentProviderUnionSlot::FrontendManual)
141 continue;
142
143 const auto file = cache.GetEntryUnparsed(game.title_id, game.type);
136 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 144 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
137 if (!loader) 145 if (!loader)
138 continue; 146 continue;
@@ -150,45 +158,13 @@ void GameListWorker::AddInstalledTitlesToGameList() {
150 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, 158 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
151 compatibility_list, patch)); 159 compatibility_list, patch));
152 } 160 }
153
154 const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application,
155 FileSys::ContentRecordType::Control);
156
157 for (const auto& entry : control_data) {
158 auto nca = cache.GetEntry(entry);
159 if (nca != nullptr) {
160 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
161 }
162 }
163} 161}
164 162
165void GameListWorker::FillControlMap(const std::string& dir_path) { 163void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path,
166 const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, 164 unsigned int recursion) {
167 const std::string& virtual_name) -> bool { 165 const auto callback = [this, target, recursion](u64* num_entries_out,
168 if (stop_processing) { 166 const std::string& directory,
169 // Breaks the callback loop 167 const std::string& virtual_name) -> bool {
170 return false;
171 }
172
173 const std::string physical_name = directory + DIR_SEP + virtual_name;
174 const QFileInfo file_info(QString::fromStdString(physical_name));
175 if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) {
176 auto nca =
177 std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
178 if (nca->GetType() == FileSys::NCAContentType::Control) {
179 const u64 title_id = nca->GetTitleId();
180 nca_control_map.insert_or_assign(title_id, std::move(nca));
181 }
182 }
183 return true;
184 };
185
186 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
187}
188
189void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
190 const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
191 const std::string& virtual_name) -> bool {
192 if (stop_processing) { 168 if (stop_processing) {
193 // Breaks the callback loop. 169 // Breaks the callback loop.
194 return false; 170 return false;
@@ -198,7 +174,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
198 const bool is_dir = FileUtil::IsDirectory(physical_name); 174 const bool is_dir = FileUtil::IsDirectory(physical_name);
199 if (!is_dir && 175 if (!is_dir &&
200 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 176 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
201 auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 177 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
178 auto loader = Loader::GetLoader(file);
202 if (!loader) { 179 if (!loader) {
203 return true; 180 return true;
204 } 181 }
@@ -209,31 +186,42 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
209 return true; 186 return true;
210 } 187 }
211 188
212 std::vector<u8> icon;
213 const auto res1 = loader->ReadIcon(icon);
214
215 u64 program_id = 0; 189 u64 program_id = 0;
216 const auto res2 = loader->ReadProgramId(program_id); 190 const auto res2 = loader->ReadProgramId(program_id);
217 191
218 std::string name = " "; 192 if (target == ScanTarget::FillManualContentProvider) {
219 const auto res3 = loader->ReadTitle(name); 193 if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
194 provider->AddEntry(FileSys::TitleType::Application,
195 FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
196 program_id, file);
197 } else if (res2 == Loader::ResultStatus::Success &&
198 (file_type == Loader::FileType::XCI ||
199 file_type == Loader::FileType::NSP)) {
200 const auto nsp = file_type == Loader::FileType::NSP
201 ? std::make_shared<FileSys::NSP>(file)
202 : FileSys::XCI{file}.GetSecurePartitionNSP();
203 for (const auto& title : nsp->GetNCAs()) {
204 for (const auto& entry : title.second) {
205 provider->AddEntry(entry.first.first, entry.first.second, title.first,
206 entry.second->GetBaseFile());
207 }
208 }
209 }
210 } else {
211 std::vector<u8> icon;
212 const auto res1 = loader->ReadIcon(icon);
220 213
221 const FileSys::PatchManager patch{program_id}; 214 std::string name = " ";
215 const auto res3 = loader->ReadTitle(name);
222 216
223 if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && 217 const FileSys::PatchManager patch{program_id};
224 res2 == Loader::ResultStatus::Success) {
225 // Use from metadata pool.
226 if (nca_control_map.find(program_id) != nca_control_map.end()) {
227 const auto& nca = nca_control_map[program_id];
228 GetMetadataFromControlNCA(patch, *nca, icon, name);
229 }
230 }
231 218
232 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, 219 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
233 compatibility_list, patch)); 220 compatibility_list, patch));
221 }
234 } else if (is_dir && recursion > 0) { 222 } else if (is_dir && recursion > 0) {
235 watch_list.append(QString::fromStdString(physical_name)); 223 watch_list.append(QString::fromStdString(physical_name));
236 AddFstEntriesToGameList(physical_name, recursion - 1); 224 ScanFileSystem(target, physical_name, recursion - 1);
237 } 225 }
238 226
239 return true; 227 return true;
@@ -245,10 +233,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
245void GameListWorker::run() { 233void GameListWorker::run() {
246 stop_processing = false; 234 stop_processing = false;
247 watch_list.append(dir_path); 235 watch_list.append(dir_path);
248 FillControlMap(dir_path.toStdString()); 236 provider->ClearAllEntries();
249 AddInstalledTitlesToGameList(); 237 ScanFileSystem(ScanTarget::FillManualContentProvider, dir_path.toStdString(),
250 AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); 238 deep_scan ? 256 : 0);
251 nca_control_map.clear(); 239 AddTitlesToGameList();
240 ScanFileSystem(ScanTarget::PopulateGameList, dir_path.toStdString(), deep_scan ? 256 : 0);
252 emit Finished(watch_list); 241 emit Finished(watch_list);
253} 242}
254 243
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 0e42d0bde..7c3074af9 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -33,7 +33,8 @@ class GameListWorker : public QObject, public QRunnable {
33 Q_OBJECT 33 Q_OBJECT
34 34
35public: 35public:
36 GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, 36 GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs,
37 FileSys::ManualContentProvider* provider, QString dir_path, bool deep_scan,
37 const CompatibilityList& compatibility_list); 38 const CompatibilityList& compatibility_list);
38 ~GameListWorker() override; 39 ~GameListWorker() override;
39 40
@@ -58,12 +59,17 @@ signals:
58 void Finished(QStringList watch_list); 59 void Finished(QStringList watch_list);
59 60
60private: 61private:
61 void AddInstalledTitlesToGameList(); 62 void AddTitlesToGameList();
62 void FillControlMap(const std::string& dir_path); 63
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); 64 enum class ScanTarget {
65 FillManualContentProvider,
66 PopulateGameList,
67 };
68
69 void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion = 0);
64 70
65 std::shared_ptr<FileSys::VfsFilesystem> vfs; 71 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map; 72 FileSys::ManualContentProvider* provider;
67 QStringList watch_list; 73 QStringList watch_list;
68 QString dir_path; 74 QString dir_path;
69 bool deep_scan; 75 bool deep_scan;
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index dce399774..4582e7f21 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map>
6#include <QKeySequence> 5#include <QKeySequence>
7#include <QShortcut> 6#include <QShortcut>
8#include <QTreeWidgetItem> 7#include <QTreeWidgetItem>
@@ -13,47 +12,32 @@
13HotkeyRegistry::HotkeyRegistry() = default; 12HotkeyRegistry::HotkeyRegistry() = default;
14HotkeyRegistry::~HotkeyRegistry() = default; 13HotkeyRegistry::~HotkeyRegistry() = default;
15 14
16void HotkeyRegistry::LoadHotkeys() {
17 // Make sure NOT to use a reference here because it would become invalid once we call
18 // beginGroup()
19 for (auto shortcut : UISettings::values.shortcuts) {
20 const QStringList cat = shortcut.first.split('/');
21 Q_ASSERT(cat.size() >= 2);
22
23 // RegisterHotkey assigns default keybindings, so use old values as default parameters
24 Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
25 if (!shortcut.second.first.isEmpty()) {
26 hk.keyseq = QKeySequence::fromString(shortcut.second.first);
27 hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
28 }
29 if (hk.shortcut)
30 hk.shortcut->setKey(hk.keyseq);
31 }
32}
33
34void HotkeyRegistry::SaveHotkeys() { 15void HotkeyRegistry::SaveHotkeys() {
35 UISettings::values.shortcuts.clear(); 16 UISettings::values.shortcuts.clear();
36 for (const auto& group : hotkey_groups) { 17 for (const auto& group : hotkey_groups) {
37 for (const auto& hotkey : group.second) { 18 for (const auto& hotkey : group.second) {
38 UISettings::values.shortcuts.emplace_back( 19 UISettings::values.shortcuts.push_back(
39 UISettings::Shortcut(group.first + '/' + hotkey.first, 20 {hotkey.first, group.first,
40 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), 21 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
41 hotkey.second.context))); 22 hotkey.second.context)});
42 } 23 }
43 } 24 }
44} 25}
45 26
46void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action, 27void HotkeyRegistry::LoadHotkeys() {
47 const QKeySequence& default_keyseq, 28 // Make sure NOT to use a reference here because it would become invalid once we call
48 Qt::ShortcutContext default_context) { 29 // beginGroup()
49 auto& hotkey_group = hotkey_groups[group]; 30 for (auto shortcut : UISettings::values.shortcuts) {
50 if (hotkey_group.find(action) != hotkey_group.end()) { 31 Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
51 return; 32 if (!shortcut.shortcut.first.isEmpty()) {
33 hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText);
34 hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second);
35 }
36 if (hk.shortcut) {
37 hk.shortcut->disconnect();
38 hk.shortcut->setKey(hk.keyseq);
39 }
52 } 40 }
53
54 auto& hotkey_action = hotkey_groups[group][action];
55 hotkey_action.keyseq = default_keyseq;
56 hotkey_action.context = default_context;
57} 41}
58 42
59QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { 43QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
@@ -65,24 +49,11 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
65 return hk.shortcut; 49 return hk.shortcut;
66} 50}
67 51
68GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { 52QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) {
69 ui.setupUi(this); 53 return hotkey_groups[group][action].keyseq;
70} 54}
71 55
72void GHotkeysDialog::Populate(const HotkeyRegistry& registry) { 56Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group,
73 for (const auto& group : registry.hotkey_groups) { 57 const QString& action) {
74 QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); 58 return hotkey_groups[group][action].context;
75 for (const auto& hotkey : group.second) {
76 QStringList columns;
77 columns << hotkey.first << hotkey.second.keyseq.toString();
78 QTreeWidgetItem* item = new QTreeWidgetItem(columns);
79 toplevel_item->addChild(item);
80 }
81 ui.treeWidget->addTopLevelItem(toplevel_item);
82 }
83 // TODO: Make context configurable as well (hiding the column for now)
84 ui.treeWidget->setColumnCount(2);
85
86 ui.treeWidget->resizeColumnToContents(0);
87 ui.treeWidget->resizeColumnToContents(1);
88} 59}
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index f38e6c002..4f526dc7e 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <map> 7#include <map>
8#include "ui_hotkeys.h"
9 8
10class QDialog; 9class QDialog;
11class QKeySequence; 10class QKeySequence;
@@ -14,7 +13,7 @@ class QShortcut;
14 13
15class HotkeyRegistry final { 14class HotkeyRegistry final {
16public: 15public:
17 friend class GHotkeysDialog; 16 friend class ConfigureHotkeys;
18 17
19 explicit HotkeyRegistry(); 18 explicit HotkeyRegistry();
20 ~HotkeyRegistry(); 19 ~HotkeyRegistry();
@@ -49,22 +48,27 @@ public:
49 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); 48 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
50 49
51 /** 50 /**
52 * Register a hotkey. 51 * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut.
53 * 52 *
54 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") 53 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
55 * @param action Name of the action (e.g. "Start Emulation", "Load Image") 54 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
56 * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the 55 */
57 * settings file before 56 QKeySequence GetKeySequence(const QString& group, const QString& action);
58 * @param default_context Default context to assign if the hotkey wasn't present in the settings 57
59 * file before 58 /**
60 * @warning Both the group and action strings will be displayed in the hotkey settings dialog 59 * Returns a Qt::ShortcutContext object who can be connected to other
60 * QAction::setShortcutContext.
61 *
62 * @param group General group this shortcut context belongs to (e.g. "Main Window",
63 * "Debugger").
64 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
61 */ 65 */
62 void RegisterHotkey(const QString& group, const QString& action, 66 Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action);
63 const QKeySequence& default_keyseq = {},
64 Qt::ShortcutContext default_context = Qt::WindowShortcut);
65 67
66private: 68private:
67 struct Hotkey { 69 struct Hotkey {
70 Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
71
68 QKeySequence keyseq; 72 QKeySequence keyseq;
69 QShortcut* shortcut = nullptr; 73 QShortcut* shortcut = nullptr;
70 Qt::ShortcutContext context = Qt::WindowShortcut; 74 Qt::ShortcutContext context = Qt::WindowShortcut;
@@ -75,15 +79,3 @@ private:
75 79
76 HotkeyGroupMap hotkey_groups; 80 HotkeyGroupMap hotkey_groups;
77}; 81};
78
79class GHotkeysDialog : public QWidget {
80 Q_OBJECT
81
82public:
83 explicit GHotkeysDialog(QWidget* parent = nullptr);
84
85 void Populate(const HotkeyRegistry& registry);
86
87private:
88 Ui::hotkeys ui;
89};
diff --git a/src/yuzu/hotkeys.ui b/src/yuzu/hotkeys.ui
deleted file mode 100644
index 050fe064e..000000000
--- a/src/yuzu/hotkeys.ui
+++ /dev/null
@@ -1,46 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>hotkeys</class>
4 <widget class="QWidget" name="hotkeys">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>363</width>
10 <height>388</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Hotkey Settings</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QTreeWidget" name="treeWidget">
19 <property name="selectionBehavior">
20 <enum>QAbstractItemView::SelectItems</enum>
21 </property>
22 <property name="headerHidden">
23 <bool>false</bool>
24 </property>
25 <column>
26 <property name="text">
27 <string>Action</string>
28 </property>
29 </column>
30 <column>
31 <property name="text">
32 <string>Hotkey</string>
33 </property>
34 </column>
35 <column>
36 <property name="text">
37 <string>Context</string>
38 </property>
39 </column>
40 </widget>
41 </item>
42 </layout>
43 </widget>
44 <resources/>
45 <connections/>
46</ui>
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index 86f6d0165..4e2d988cd 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -192,7 +192,12 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
192 } 192 }
193 193
194 // update labels and progress bar 194 // update labels and progress bar
195 ui->stage->setText(stage_translations[stage].arg(value).arg(total)); 195 if (stage == VideoCore::LoadCallbackStage::Decompile ||
196 stage == VideoCore::LoadCallbackStage::Build) {
197 ui->stage->setText(stage_translations[stage].arg(value).arg(total));
198 } else {
199 ui->stage->setText(stage_translations[stage]);
200 }
196 ui->value->setText(estimate); 201 ui->value->setText(estimate);
197 ui->progress_bar->setValue(static_cast<int>(value)); 202 ui->progress_bar->setValue(static_cast<int>(value));
198 previous_time = now; 203 previous_time = now;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2b9db69a3..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"
@@ -171,7 +170,8 @@ static void InitializeLogging() {
171 170
172GMainWindow::GMainWindow() 171GMainWindow::GMainWindow()
173 : config(new Config()), emu_thread(nullptr), 172 : config(new Config()), emu_thread(nullptr),
174 vfs(std::make_shared<FileSys::RealVfsFilesystem>()) { 173 vfs(std::make_shared<FileSys::RealVfsFilesystem>()),
174 provider(std::make_unique<FileSys::ManualContentProvider>()) {
175 InitializeLogging(); 175 InitializeLogging();
176 176
177 debug_context = Tegra::DebugContext::Construct(); 177 debug_context = Tegra::DebugContext::Construct();
@@ -203,13 +203,18 @@ GMainWindow::GMainWindow()
203 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); 203 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
204 show(); 204 show();
205 205
206 Core::System::GetInstance().SetContentProvider(
207 std::make_unique<FileSys::ContentProviderUnion>());
208 Core::System::GetInstance().RegisterContentProvider(
209 FileSys::ContentProviderUnionSlot::FrontendManual, provider.get());
210 Service::FileSystem::CreateFactories(*vfs);
211
206 // Gen keys if necessary 212 // Gen keys if necessary
207 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 213 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
208 214
209 // Necessary to load titles from nand in gamelist.
210 Service::FileSystem::CreateFactories(*vfs);
211 game_list->LoadCompatibilityList(); 215 game_list->LoadCompatibilityList();
212 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);
213 218
214 // Show one-time "callout" messages to the user 219 // Show one-time "callout" messages to the user
215 ShowTelemetryCallout(); 220 ShowTelemetryCallout();
@@ -419,7 +424,7 @@ void GMainWindow::InitializeWidgets() {
419 render_window = new GRenderWindow(this, emu_thread.get()); 424 render_window = new GRenderWindow(this, emu_thread.get());
420 render_window->hide(); 425 render_window->hide();
421 426
422 game_list = new GameList(vfs, this); 427 game_list = new GameList(vfs, provider.get(), this);
423 ui.horizontalLayout->addWidget(game_list); 428 ui.horizontalLayout->addWidget(game_list);
424 429
425 loading_screen = new LoadingScreen(this); 430 loading_screen = new LoadingScreen(this);
@@ -478,11 +483,6 @@ void GMainWindow::InitializeDebugWidgets() {
478 graphicsBreakpointsWidget->hide(); 483 graphicsBreakpointsWidget->hide();
479 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); 484 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
480 485
481 graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this);
482 addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget);
483 graphicsSurfaceWidget->hide();
484 debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction());
485
486 waitTreeWidget = new WaitTreeWidget(this); 486 waitTreeWidget = new WaitTreeWidget(this);
487 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); 487 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
488 waitTreeWidget->hide(); 488 waitTreeWidget->hide();
@@ -514,33 +514,34 @@ void GMainWindow::InitializeRecentFileMenuActions() {
514} 514}
515 515
516void GMainWindow::InitializeHotkeys() { 516void GMainWindow::InitializeHotkeys() {
517 hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
518 hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
519 hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
520 hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5));
521 hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
522 hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
523 Qt::ApplicationShortcut);
524 hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
525 Qt::ApplicationShortcut);
526 hotkey_registry.RegisterHotkey("Main Window", "Increase Speed Limit", QKeySequence("+"),
527 Qt::ApplicationShortcut);
528 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
529 Qt::ApplicationShortcut);
530 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
531 Qt::ApplicationShortcut);
532 hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
533 QKeySequence(QKeySequence::Print));
534 hotkey_registry.RegisterHotkey("Main Window", "Change Docked Mode", QKeySequence(Qt::Key_F10));
535
536 hotkey_registry.LoadHotkeys(); 517 hotkey_registry.LoadHotkeys();
537 518
519 ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File"));
520 ui.action_Load_File->setShortcutContext(
521 hotkey_registry.GetShortcutContext("Main Window", "Load File"));
522
523 ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit yuzu"));
524 ui.action_Exit->setShortcutContext(
525 hotkey_registry.GetShortcutContext("Main Window", "Exit yuzu"));
526
527 ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation"));
528 ui.action_Stop->setShortcutContext(
529 hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation"));
530
531 ui.action_Show_Filter_Bar->setShortcut(
532 hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar"));
533 ui.action_Show_Filter_Bar->setShortcutContext(
534 hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar"));
535
536 ui.action_Show_Status_Bar->setShortcut(
537 hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar"));
538 ui.action_Show_Status_Bar->setShortcutContext(
539 hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar"));
540
538 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 541 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
539 this, &GMainWindow::OnMenuLoadFile); 542 this, &GMainWindow::OnMenuLoadFile);
540 connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this), 543 connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this),
541 &QShortcut::activated, this, &GMainWindow::OnStartGame); 544 &QShortcut::activated, this, [&] {
542 connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
543 this, [&] {
544 if (emulation_running) { 545 if (emulation_running) {
545 if (emu_thread->IsRunning()) { 546 if (emu_thread->IsRunning()) {
546 OnPauseGame(); 547 OnPauseGame();
@@ -549,8 +550,8 @@ void GMainWindow::InitializeHotkeys() {
549 } 550 }
550 } 551 }
551 }); 552 });
552 connect(hotkey_registry.GetHotkey("Main Window", "Restart", this), &QShortcut::activated, this, 553 connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this),
553 [this] { 554 &QShortcut::activated, this, [this] {
554 if (!Core::System::GetInstance().IsPoweredOn()) 555 if (!Core::System::GetInstance().IsPoweredOn())
555 return; 556 return;
556 BootGame(QString(game_path)); 557 BootGame(QString(game_path));
@@ -697,7 +698,6 @@ void GMainWindow::ConnectMenuEvents() {
697 &GMainWindow::ToggleWindowMode); 698 &GMainWindow::ToggleWindowMode);
698 connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this, 699 connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this,
699 &GMainWindow::OnDisplayTitleBars); 700 &GMainWindow::OnDisplayTitleBars);
700 ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F"));
701 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); 701 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
702 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 702 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
703 703
@@ -1179,7 +1179,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1179 return; 1179 return;
1180 } 1180 }
1181 1181
1182 const auto installed = Service::FileSystem::GetUnionContents(); 1182 const auto& installed = Core::System::GetInstance().GetContentProvider();
1183 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); 1183 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
1184 1184
1185 if (!romfs_title_id) { 1185 if (!romfs_title_id) {
@@ -1279,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1279 1279
1280 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);
1281 if (reload) { 1281 if (reload) {
1282 game_list->PopulateAsync(UISettings::values.gamedir, 1282 game_list->PopulateAsync(UISettings::values.game_directory_path,
1283 UISettings::values.gamedir_deepscan); 1283 UISettings::values.game_directory_deepscan);
1284 } 1284 }
1285 1285
1286 config->Save(); 1286 config->Save();
@@ -1368,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() {
1368 const auto success = [this]() { 1368 const auto success = [this]() {
1369 QMessageBox::information(this, tr("Successfully Installed"), 1369 QMessageBox::information(this, tr("Successfully Installed"),
1370 tr("The file was successfully installed.")); 1370 tr("The file was successfully installed."));
1371 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);
1372 }; 1373 };
1373 1374
1374 const auto failed = [this]() { 1375 const auto failed = [this]() {
@@ -1495,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() {
1495void GMainWindow::OnMenuSelectGameListRoot() { 1496void GMainWindow::OnMenuSelectGameListRoot() {
1496 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 1497 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
1497 if (!dir_path.isEmpty()) { 1498 if (!dir_path.isEmpty()) {
1498 UISettings::values.gamedir = dir_path; 1499 UISettings::values.game_directory_path = dir_path;
1499 game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); 1500 game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan);
1500 } 1501 }
1501} 1502}
1502 1503
@@ -1518,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1518 : FileUtil::UserPath::NANDDir, 1519 : FileUtil::UserPath::NANDDir,
1519 dir_path.toStdString()); 1520 dir_path.toStdString());
1520 Service::FileSystem::CreateFactories(*vfs); 1521 Service::FileSystem::CreateFactories(*vfs);
1521 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);
1522 } 1524 }
1523} 1525}
1524 1526
@@ -1662,6 +1664,7 @@ void GMainWindow::OnConfigure() {
1662 auto result = configureDialog.exec(); 1664 auto result = configureDialog.exec();
1663 if (result == QDialog::Accepted) { 1665 if (result == QDialog::Accepted) {
1664 configureDialog.applyConfiguration(); 1666 configureDialog.applyConfiguration();
1667 InitializeHotkeys();
1665 if (UISettings::values.theme != old_theme) 1668 if (UISettings::values.theme != old_theme)
1666 UpdateUITheme(); 1669 UpdateUITheme();
1667 if (UISettings::values.enable_discord_presence != old_discord_presence) 1670 if (UISettings::values.enable_discord_presence != old_discord_presence)
@@ -1669,8 +1672,8 @@ void GMainWindow::OnConfigure() {
1669 1672
1670 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);
1671 if (reload) { 1674 if (reload) {
1672 game_list->PopulateAsync(UISettings::values.gamedir, 1675 game_list->PopulateAsync(UISettings::values.game_directory_path,
1673 UISettings::values.gamedir_deepscan); 1676 UISettings::values.game_directory_deepscan);
1674 } 1677 }
1675 1678
1676 config->Save(); 1679 config->Save();
@@ -1920,18 +1923,19 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1920 Service::FileSystem::CreateFactories(*vfs); 1923 Service::FileSystem::CreateFactories(*vfs);
1921 1924
1922 if (behavior == ReinitializeKeyBehavior::Warning) { 1925 if (behavior == ReinitializeKeyBehavior::Warning) {
1923 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);
1924 } 1928 }
1925} 1929}
1926 1930
1927std::optional<u64> GMainWindow::SelectRomFSDumpTarget( 1931std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed,
1928 const FileSys::RegisteredCacheUnion& installed, u64 program_id) { 1932 u64 program_id) {
1929 const auto dlc_entries = 1933 const auto dlc_entries =
1930 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 1934 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1931 std::vector<FileSys::RegisteredCacheEntry> dlc_match; 1935 std::vector<FileSys::ContentProviderEntry> dlc_match;
1932 dlc_match.reserve(dlc_entries.size()); 1936 dlc_match.reserve(dlc_entries.size());
1933 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), 1937 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1934 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) { 1938 [&program_id, &installed](const FileSys::ContentProviderEntry& entry) {
1935 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id && 1939 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1936 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; 1940 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1937 }); 1941 });
@@ -2027,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
2027 event->acceptProposedAction(); 2031 event->acceptProposedAction();
2028} 2032}
2029 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
2030bool GMainWindow::ConfirmChangeGame() { 2046bool GMainWindow::ConfirmChangeGame() {
2031 if (emu_thread == nullptr) 2047 if (emu_thread == nullptr)
2032 return true; 2048 return true;
@@ -2094,7 +2110,8 @@ int main(int argc, char* argv[]) {
2094 QCoreApplication::setOrganizationName("yuzu team"); 2110 QCoreApplication::setOrganizationName("yuzu team");
2095 QCoreApplication::setApplicationName("yuzu"); 2111 QCoreApplication::setApplicationName("yuzu");
2096 2112
2097 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);
2098 QApplication app(argc, argv); 2115 QApplication app(argc, argv);
2099 2116
2100 // 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 7f3aa998e..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;
@@ -37,7 +36,8 @@ struct SoftwareKeyboardParameters;
37} // namespace Core::Frontend 36} // namespace Core::Frontend
38 37
39namespace FileSys { 38namespace FileSys {
40class RegisteredCacheUnion; 39class ContentProvider;
40class ManualContentProvider;
41class VfsFilesystem; 41class VfsFilesystem;
42} // namespace FileSys 42} // namespace FileSys
43 43
@@ -120,7 +120,6 @@ private:
120 void InitializeWidgets(); 120 void InitializeWidgets();
121 void InitializeDebugWidgets(); 121 void InitializeDebugWidgets();
122 void InitializeRecentFileMenuActions(); 122 void InitializeRecentFileMenuActions();
123 void InitializeHotkeys();
124 123
125 void SetDefaultUIGeometry(); 124 void SetDefaultUIGeometry();
126 void RestoreUIState(); 125 void RestoreUIState();
@@ -196,6 +195,7 @@ private slots:
196 void OnAbout(); 195 void OnAbout();
197 void OnToggleFilterBar(); 196 void OnToggleFilterBar();
198 void OnDisplayTitleBars(bool); 197 void OnDisplayTitleBars(bool);
198 void InitializeHotkeys();
199 void ToggleFullscreen(); 199 void ToggleFullscreen();
200 void ShowFullscreen(); 200 void ShowFullscreen();
201 void HideFullscreen(); 201 void HideFullscreen();
@@ -205,7 +205,7 @@ private slots:
205 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 205 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
206 206
207private: 207private:
208 std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id); 208 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
209 void UpdateStatusBar(); 209 void UpdateStatusBar();
210 210
211 Ui::MainWindow ui; 211 Ui::MainWindow ui;
@@ -233,12 +233,12 @@ private:
233 233
234 // FS 234 // FS
235 std::shared_ptr<FileSys::VfsFilesystem> vfs; 235 std::shared_ptr<FileSys::VfsFilesystem> vfs;
236 std::unique_ptr<FileSys::ManualContentProvider> provider;
236 237
237 // Debugger panes 238 // Debugger panes
238 ProfilerWidget* profilerWidget; 239 ProfilerWidget* profilerWidget;
239 MicroProfileDialog* microProfileDialog; 240 MicroProfileDialog* microProfileDialog;
240 GraphicsBreakPointsWidget* graphicsBreakpointsWidget; 241 GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
241 GraphicsSurfaceWidget* graphicsSurfaceWidget;
242 WaitTreeWidget* waitTreeWidget; 242 WaitTreeWidget* waitTreeWidget;
243 243
244 QAction* actions_recent_files[max_recent_files_item]; 244 QAction* actions_recent_files[max_recent_files_item];
@@ -252,4 +252,8 @@ protected:
252 void dropEvent(QDropEvent* event) override; 252 void dropEvent(QDropEvent* event) override;
253 void dragEnterEvent(QDragEnterEvent* event) override; 253 void dragEnterEvent(QDragEnterEvent* event) override;
254 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;
255}; 259};
diff --git a/src/yuzu/ui_settings.cpp b/src/yuzu/ui_settings.cpp
index a314493fc..4bdc302e0 100644
--- a/src/yuzu/ui_settings.cpp
+++ b/src/yuzu/ui_settings.cpp
@@ -12,5 +12,4 @@ const Themes themes{{
12}}; 12}};
13 13
14Values values = {}; 14Values values = {};
15
16} // namespace UISettings 15} // namespace UISettings
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 82aaeedb0..dbd318e20 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -15,7 +15,12 @@
15namespace UISettings { 15namespace UISettings {
16 16
17using ContextualShortcut = std::pair<QString, int>; 17using ContextualShortcut = std::pair<QString, int>;
18using Shortcut = std::pair<QString, ContextualShortcut>; 18
19struct Shortcut {
20 QString name;
21 QString group;
22 ContextualShortcut shortcut;
23};
19 24
20using Themes = std::array<std::pair<const char*, const char*>, 2>; 25using Themes = std::array<std::pair<const char*, const char*>, 2>;
21extern const Themes themes; 26extern const Themes themes;
@@ -50,8 +55,8 @@ struct Values {
50 QString roms_path; 55 QString roms_path;
51 QString symbols_path; 56 QString symbols_path;
52 QString screenshot_path; 57 QString screenshot_path;
53 QString gamedir; 58 QString game_directory_path;
54 bool gamedir_deepscan; 59 bool game_directory_deepscan;
55 QStringList recent_files; 60 QStringList recent_files;
56 61
57 QString theme; 62 QString theme;
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
new file mode 100644
index 000000000..d3edf6ec3
--- /dev/null
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
@@ -0,0 +1,37 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QDialogButtonBox>
6#include <QKeySequenceEdit>
7#include <QVBoxLayout>
8#include "yuzu/util/sequence_dialog/sequence_dialog.h"
9
10SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
11 setWindowTitle(tr("Enter a hotkey"));
12 auto* layout = new QVBoxLayout(this);
13 key_sequence = new QKeySequenceEdit;
14 layout->addWidget(key_sequence);
15 auto* buttons =
16 new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
17 buttons->setCenterButtons(true);
18 layout->addWidget(buttons);
19 connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
20 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
21 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
22}
23
24SequenceDialog::~SequenceDialog() = default;
25
26QKeySequence SequenceDialog::GetSequence() const {
27 // Only the first key is returned. The other 3, if present, are ignored.
28 return QKeySequence(key_sequence->keySequence()[0]);
29}
30
31bool SequenceDialog::focusNextPrevChild(bool next) {
32 return false;
33}
34
35void SequenceDialog::closeEvent(QCloseEvent*) {
36 reject();
37}
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.h b/src/yuzu/util/sequence_dialog/sequence_dialog.h
new file mode 100644
index 000000000..969c77740
--- /dev/null
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.h
@@ -0,0 +1,24 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDialog>
8
9class QKeySequenceEdit;
10
11class SequenceDialog : public QDialog {
12 Q_OBJECT
13
14public:
15 explicit SequenceDialog(QWidget* parent = nullptr);
16 ~SequenceDialog() override;
17
18 QKeySequence GetSequence() const;
19 void closeEvent(QCloseEvent*) override;
20
21private:
22 QKeySequenceEdit* key_sequence;
23 bool focusNextPrevChild(bool next) override;
24};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index de7a26e14..68a176032 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -19,6 +19,37 @@
19#include "input_common/sdl/sdl.h" 19#include "input_common/sdl/sdl.h"
20#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 20#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
21 21
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
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 245f25847..7ea4a1b18 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -33,6 +33,7 @@
33#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 33#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
34 34
35#include <getopt.h> 35#include <getopt.h>
36#include "core/file_sys/registered_cache.h"
36#ifndef _MSC_VER 37#ifndef _MSC_VER
37#include <unistd.h> 38#include <unistd.h>
38#endif 39#endif
@@ -178,6 +179,7 @@ int main(int argc, char** argv) {
178 } 179 }
179 180
180 Core::System& system{Core::System::GetInstance()}; 181 Core::System& system{Core::System::GetInstance()};
182 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
181 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 183 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
182 Service::FileSystem::CreateFactories(*system.GetFilesystem()); 184 Service::FileSystem::CreateFactories(*system.GetFilesystem());
183 185