summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/web_result.h1
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp43
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/core_cpu.cpp14
-rw-r--r--src/core/core_cpu.h17
-rw-r--r--src/core/crypto/key_manager.cpp39
-rw-r--r--src/core/crypto/key_manager.h2
-rw-r--r--src/core/crypto/partition_data_manager.cpp80
-rw-r--r--src/core/crypto/partition_data_manager.h18
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h8
-rw-r--r--src/core/file_sys/content_archive.cpp2
-rw-r--r--src/core/file_sys/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp12
-rw-r--r--src/core/file_sys/registered_cache.cpp14
-rw-r--r--src/core/file_sys/registered_cache.h12
-rw-r--r--src/core/file_sys/savedata_factory.cpp13
-rw-r--r--src/core/file_sys/sdmc_factory.cpp8
-rw-r--r--src/core/file_sys/sdmc_factory.h4
-rw-r--r--src/core/gdbstub/gdbstub.cpp6
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp10
-rw-r--r--src/core/hle/kernel/process.h21
-rw-r--r--src/core/hle/kernel/svc.cpp54
-rw-r--r--src/core/hle/kernel/svc.h4
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/kernel/thread.cpp16
-rw-r--r--src/core/hle/kernel/vm_manager.cpp35
-rw-r--r--src/core/hle/kernel/vm_manager.h12
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp29
-rw-r--r--src/core/hle/service/filesystem/filesystem.h12
-rw-r--r--src/core/hle/service/ns/pl_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/hle/service/vi/vi.cpp50
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp23
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp4
-rw-r--r--src/video_core/engines/fermi_2d.cpp14
-rw-r--r--src/video_core/engines/maxwell_dma.cpp10
-rw-r--r--src/video_core/engines/maxwell_dma.h4
-rw-r--r--src/video_core/engines/shader_bytecode.h2
-rw-r--r--src/video_core/memory_manager.cpp10
-rw-r--r--src/video_core/memory_manager.h1
-rw-r--r--src/video_core/rasterizer_cache.h127
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp31
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp363
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h101
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp77
-rw-r--r--src/video_core/textures/decoders.cpp193
-rw-r--r--src/video_core/textures/decoders.h15
-rw-r--r--src/video_core/textures/texture.h1
-rw-r--r--src/web_service/telemetry_json.cpp90
-rw-r--r--src/web_service/telemetry_json.h24
-rw-r--r--src/web_service/verify_login.cpp1
-rw-r--r--src/web_service/web_backend.cpp235
-rw-r--r--src/web_service/web_backend.h58
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.ui4
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp5
-rw-r--r--src/yuzu/debugger/wait_tree.cpp9
-rw-r--r--src/yuzu/game_list_worker.cpp19
-rw-r--r--src/yuzu/game_list_worker.h2
-rw-r--r--src/yuzu/main.cpp44
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h4
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
81 files changed, 1379 insertions, 796 deletions
diff --git a/src/common/web_result.h b/src/common/web_result.h
index 969926674..8bfa2141d 100644
--- a/src/common/web_result.h
+++ b/src/common/web_result.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h"
8 9
9namespace Common { 10namespace Common {
10struct WebResult { 11struct WebResult {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4fddaafd1..78986deb5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -400,8 +400,8 @@ create_target_directory_groups(core)
400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) 401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
402if (ENABLE_WEB_SERVICE) 402if (ENABLE_WEB_SERVICE)
403 add_definitions(-DENABLE_WEB_SERVICE) 403 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
404 target_link_libraries(core PUBLIC json-headers web_service) 404 target_link_libraries(core PRIVATE web_service)
405endif() 405endif()
406 406
407if (ARCHITECTURE_x86_64) 407if (ARCHITECTURE_x86_64)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 0762321a9..4d2491870 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -144,7 +144,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
144 144
145 // Multi-process state 145 // Multi-process state
146 config.processor_id = core_index; 146 config.processor_id = core_index;
147 config.global_monitor = &exclusive_monitor->monitor; 147 config.global_monitor = &exclusive_monitor.monitor;
148 148
149 // System registers 149 // System registers
150 config.tpidrro_el0 = &cb->tpidrro_el0; 150 config.tpidrro_el0 = &cb->tpidrro_el0;
@@ -171,10 +171,9 @@ void ARM_Dynarmic::Step() {
171 cb->InterpreterFallback(jit->GetPC(), 1); 171 cb->InterpreterFallback(jit->GetPC(), 1);
172} 172}
173 173
174ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 174ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
175 std::size_t core_index)
176 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, 175 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
177 exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { 176 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
178 ThreadContext ctx{}; 177 ThreadContext ctx{};
179 inner_unicorn.SaveContext(ctx); 178 inner_unicorn.SaveContext(ctx);
180 PageTableChanged(); 179 PageTableChanged();
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 4ee92ee27..512bf8ce9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -23,7 +23,7 @@ class DynarmicExclusiveMonitor;
23 23
24class ARM_Dynarmic final : public ARM_Interface { 24class ARM_Dynarmic final : public ARM_Interface {
25public: 25public:
26 ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index); 26 ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
27 ~ARM_Dynarmic(); 27 ~ARM_Dynarmic();
28 28
29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,7 +62,7 @@ private:
62 ARM_Unicorn inner_unicorn; 62 ARM_Unicorn inner_unicorn;
63 63
64 std::size_t core_index; 64 std::size_t core_index;
65 std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; 65 DynarmicExclusiveMonitor& exclusive_monitor;
66 66
67 Memory::PageTable* current_page_table = nullptr; 67 Memory::PageTable* current_page_table = nullptr;
68}; 68};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e2fb9e038..3c57a62ec 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -71,9 +71,9 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
71} 71}
72 72
73/// Runs a CPU core while the system is powered on 73/// Runs a CPU core while the system is powered on
74void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { 74void RunCpuCore(Cpu& cpu_state) {
75 while (Core::System::GetInstance().IsPoweredOn()) { 75 while (Core::System::GetInstance().IsPoweredOn()) {
76 cpu_state->RunLoop(true); 76 cpu_state.RunLoop(true);
77 } 77 }
78} 78}
79} // Anonymous namespace 79} // Anonymous namespace
@@ -95,7 +95,7 @@ struct System::Impl {
95 status = ResultStatus::Success; 95 status = ResultStatus::Success;
96 96
97 // Update thread_to_cpu in case Core 0 is run from a different host thread 97 // Update thread_to_cpu in case Core 0 is run from a different host thread
98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
99 99
100 if (GDBStub::IsServerEnabled()) { 100 if (GDBStub::IsServerEnabled()) {
101 GDBStub::HandlePacket(); 101 GDBStub::HandlePacket();
@@ -139,16 +139,16 @@ struct System::Impl {
139 auto main_process = Kernel::Process::Create(kernel, "main"); 139 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get()); 140 kernel.MakeCurrentProcess(main_process.get());
141 141
142 cpu_barrier = std::make_shared<CpuBarrier>(); 142 cpu_barrier = std::make_unique<CpuBarrier>();
143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) { 144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
145 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); 145 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
146 } 146 }
147 147
148 telemetry_session = std::make_unique<Core::TelemetrySession>(); 148 telemetry_session = std::make_unique<Core::TelemetrySession>();
149 service_manager = std::make_shared<Service::SM::ServiceManager>(); 149 service_manager = std::make_shared<Service::SM::ServiceManager>();
150 150
151 Service::Init(service_manager, virtual_filesystem); 151 Service::Init(service_manager, *virtual_filesystem);
152 GDBStub::Init(); 152 GDBStub::Init();
153 153
154 renderer = VideoCore::CreateRenderer(emu_window); 154 renderer = VideoCore::CreateRenderer(emu_window);
@@ -160,12 +160,12 @@ struct System::Impl {
160 160
161 // Create threads for CPU cores 1-3, and build thread_to_cpu map 161 // Create threads for CPU cores 1-3, and build thread_to_cpu map
162 // CPU core 0 is run on the main thread 162 // CPU core 0 is run on the main thread
163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
164 if (Settings::values.use_multi_core) { 164 if (Settings::values.use_multi_core) {
165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) { 165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
166 cpu_core_threads[index] = 166 cpu_core_threads[index] =
167 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); 167 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; 168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
169 } 169 }
170 } 170 }
171 171
@@ -245,6 +245,7 @@ struct System::Impl {
245 for (auto& cpu_core : cpu_cores) { 245 for (auto& cpu_core : cpu_cores) {
246 cpu_core.reset(); 246 cpu_core.reset();
247 } 247 }
248 cpu_exclusive_monitor.reset();
248 cpu_barrier.reset(); 249 cpu_barrier.reset();
249 250
250 // Shutdown kernel and core timing 251 // Shutdown kernel and core timing
@@ -282,9 +283,9 @@ struct System::Impl {
282 std::unique_ptr<VideoCore::RendererBase> renderer; 283 std::unique_ptr<VideoCore::RendererBase> renderer;
283 std::unique_ptr<Tegra::GPU> gpu_core; 284 std::unique_ptr<Tegra::GPU> gpu_core;
284 std::shared_ptr<Tegra::DebugContext> debug_context; 285 std::shared_ptr<Tegra::DebugContext> debug_context;
285 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 286 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
286 std::shared_ptr<CpuBarrier> cpu_barrier; 287 std::unique_ptr<CpuBarrier> cpu_barrier;
287 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 288 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
288 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 289 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
289 std::size_t active_core{}; ///< Active core, only used in single thread mode 290 std::size_t active_core{}; ///< Active core, only used in single thread mode
290 291
@@ -298,7 +299,7 @@ struct System::Impl {
298 std::string status_details = ""; 299 std::string status_details = "";
299 300
300 /// Map of guest threads to CPU cores 301 /// Map of guest threads to CPU cores
301 std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu; 302 std::map<std::thread::id, Cpu*> thread_to_cpu;
302 303
303 Core::PerfStats perf_stats; 304 Core::PerfStats perf_stats;
304 Core::FrameLimiter frame_limiter; 305 Core::FrameLimiter frame_limiter;
@@ -354,12 +355,15 @@ std::size_t System::CurrentCoreIndex() {
354} 355}
355 356
356Kernel::Scheduler& System::CurrentScheduler() { 357Kernel::Scheduler& System::CurrentScheduler() {
357 return *CurrentCpuCore().Scheduler(); 358 return CurrentCpuCore().Scheduler();
358} 359}
359 360
360const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { 361Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
361 ASSERT(core_index < NUM_CPU_CORES); 362 return CpuCore(core_index).Scheduler();
362 return impl->cpu_cores[core_index]->Scheduler(); 363}
364
365const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
366 return CpuCore(core_index).Scheduler();
363} 367}
364 368
365Kernel::Process* System::CurrentProcess() { 369Kernel::Process* System::CurrentProcess() {
@@ -380,6 +384,11 @@ Cpu& System::CpuCore(std::size_t core_index) {
380 return *impl->cpu_cores[core_index]; 384 return *impl->cpu_cores[core_index];
381} 385}
382 386
387const Cpu& System::CpuCore(std::size_t core_index) const {
388 ASSERT(core_index < NUM_CPU_CORES);
389 return *impl->cpu_cores[core_index];
390}
391
383ExclusiveMonitor& System::Monitor() { 392ExclusiveMonitor& System::Monitor() {
384 return *impl->cpu_exclusive_monitor; 393 return *impl->cpu_exclusive_monitor;
385} 394}
diff --git a/src/core/core.h b/src/core/core.h
index ea4d53914..173be45f8 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -156,6 +156,9 @@ public:
156 /// Gets a CPU interface to the CPU core with the specified index 156 /// Gets a CPU interface to the CPU core with the specified index
157 Cpu& CpuCore(std::size_t core_index); 157 Cpu& CpuCore(std::size_t core_index);
158 158
159 /// Gets a CPU interface to the CPU core with the specified index
160 const Cpu& CpuCore(std::size_t core_index) const;
161
159 /// Gets the exclusive monitor 162 /// Gets the exclusive monitor
160 ExclusiveMonitor& Monitor(); 163 ExclusiveMonitor& Monitor();
161 164
@@ -172,7 +175,10 @@ public:
172 const VideoCore::RendererBase& Renderer() const; 175 const VideoCore::RendererBase& Renderer() const;
173 176
174 /// Gets the scheduler for the CPU core with the specified index 177 /// Gets the scheduler for the CPU core with the specified index
175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); 178 Kernel::Scheduler& Scheduler(std::size_t core_index);
179
180 /// Gets the scheduler for the CPU core with the specified index
181 const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
176 182
177 /// Provides a pointer to the current process 183 /// Provides a pointer to the current process
178 Kernel::Process* CurrentProcess(); 184 Kernel::Process* CurrentProcess();
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 265f8ed9c..fffda8a99 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -49,10 +49,8 @@ bool CpuBarrier::Rendezvous() {
49 return false; 49 return false;
50} 50}
51 51
52Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 52Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
53 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index) 53 : cpu_barrier{cpu_barrier}, core_index{core_index} {
54 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
55
56 if (Settings::values.use_cpu_jit) { 54 if (Settings::values.use_cpu_jit) {
57#ifdef ARCHITECTURE_x86_64 55#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); 56 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
@@ -64,15 +62,15 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
64 arm_interface = std::make_unique<ARM_Unicorn>(); 62 arm_interface = std::make_unique<ARM_Unicorn>();
65 } 63 }
66 64
67 scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface); 65 scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
68} 66}
69 67
70Cpu::~Cpu() = default; 68Cpu::~Cpu() = default;
71 69
72std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { 70std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
73 if (Settings::values.use_cpu_jit) { 71 if (Settings::values.use_cpu_jit) {
74#ifdef ARCHITECTURE_x86_64 72#ifdef ARCHITECTURE_x86_64
75 return std::make_shared<DynarmicExclusiveMonitor>(num_cores); 73 return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
76#else 74#else
77 return nullptr; // TODO(merry): Passthrough exclusive monitor 75 return nullptr; // TODO(merry): Passthrough exclusive monitor
78#endif 76#endif
@@ -83,7 +81,7 @@ std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_core
83 81
84void Cpu::RunLoop(bool tight_loop) { 82void Cpu::RunLoop(bool tight_loop) {
85 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step 83 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
86 if (!cpu_barrier->Rendezvous()) { 84 if (!cpu_barrier.Rendezvous()) {
87 // If rendezvous failed, session has been killed 85 // If rendezvous failed, session has been killed
88 return; 86 return;
89 } 87 }
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index ee7e04abc..1d2bdc6cd 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -41,8 +41,7 @@ private:
41 41
42class Cpu { 42class Cpu {
43public: 43public:
44 Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 44 Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
45 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
46 ~Cpu(); 45 ~Cpu();
47 46
48 void RunLoop(bool tight_loop = true); 47 void RunLoop(bool tight_loop = true);
@@ -59,8 +58,12 @@ public:
59 return *arm_interface; 58 return *arm_interface;
60 } 59 }
61 60
62 const std::shared_ptr<Kernel::Scheduler>& Scheduler() const { 61 Kernel::Scheduler& Scheduler() {
63 return scheduler; 62 return *scheduler;
63 }
64
65 const Kernel::Scheduler& Scheduler() const {
66 return *scheduler;
64 } 67 }
65 68
66 bool IsMainCore() const { 69 bool IsMainCore() const {
@@ -71,14 +74,14 @@ public:
71 return core_index; 74 return core_index;
72 } 75 }
73 76
74 static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); 77 static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
75 78
76private: 79private:
77 void Reschedule(); 80 void Reschedule();
78 81
79 std::unique_ptr<ARM_Interface> arm_interface; 82 std::unique_ptr<ARM_Interface> arm_interface;
80 std::shared_ptr<CpuBarrier> cpu_barrier; 83 CpuBarrier& cpu_barrier;
81 std::shared_ptr<Kernel::Scheduler> scheduler; 84 std::unique_ptr<Kernel::Scheduler> scheduler;
82 85
83 std::atomic<bool> reschedule_pending = false; 86 std::atomic<bool> reschedule_pending = false;
84 std::size_t core_index; 87 std::size_t core_index;
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index a59a7e1f5..fd0786068 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -98,7 +98,7 @@ std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
98 return keyblob; 98 return keyblob;
99} 99}
100 100
101void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) { 101void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
102 const auto kek_generation_source = 102 const auto kek_generation_source =
103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)); 103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
104 const auto key_generation_source = 104 const auto key_generation_source =
@@ -147,31 +147,38 @@ boost::optional<Key128> DeriveSDSeed() {
147 "rb+"); 147 "rb+");
148 if (!save_43.IsOpen()) 148 if (!save_43.IsOpen())
149 return boost::none; 149 return boost::none;
150
150 const FileUtil::IOFile sd_private( 151 const FileUtil::IOFile sd_private(
151 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
152 if (!sd_private.IsOpen()) 153 if (!sd_private.IsOpen())
153 return boost::none; 154 return boost::none;
154 155
155 sd_private.Seek(0, SEEK_SET);
156 std::array<u8, 0x10> private_seed{}; 156 std::array<u8, 0x10> private_seed{};
157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10) 157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
158 return boost::none; 158 return boost::none;
159 }
159 160
160 std::array<u8, 0x10> buffer{}; 161 std::array<u8, 0x10> buffer{};
161 std::size_t offset = 0; 162 std::size_t offset = 0;
162 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 163 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
163 save_43.Seek(offset, SEEK_SET); 164 if (!save_43.Seek(offset, SEEK_SET)) {
165 return boost::none;
166 }
167
164 save_43.ReadBytes(buffer.data(), buffer.size()); 168 save_43.ReadBytes(buffer.data(), buffer.size());
165 if (buffer == private_seed) 169 if (buffer == private_seed) {
166 break; 170 break;
171 }
167 } 172 }
168 173
169 if (offset + 0x10 >= save_43.GetSize()) 174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
170 return boost::none; 175 return boost::none;
176 }
171 177
172 Key128 seed{}; 178 Key128 seed{};
173 save_43.Seek(offset + 0x10, SEEK_SET); 179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
174 save_43.ReadBytes(seed.data(), seed.size()); 180 return boost::none;
181 }
175 return seed; 182 return seed;
176} 183}
177 184
@@ -234,7 +241,9 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
234 return {}; 241 return {};
235 242
236 std::vector<u8> buffer(ticket_save.GetSize()); 243 std::vector<u8> buffer(ticket_save.GetSize());
237 ticket_save.ReadBytes(buffer.data(), buffer.size()); 244 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
245 return {};
246 }
238 247
239 std::vector<TicketRaw> out; 248 std::vector<TicketRaw> out;
240 u32 magic{}; 249 u32 magic{};
@@ -261,6 +270,9 @@ static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
261 270
262template <size_t target_size, size_t in_size> 271template <size_t target_size, size_t in_size>
263static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) { 272static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
273 // Avoids truncation overflow within the loop below.
274 static_assert(target_size <= 0xFF);
275
264 std::array<u8, in_size + 4> seed_exp{}; 276 std::array<u8, in_size + 4> seed_exp{};
265 std::memcpy(seed_exp.data(), seed.data(), in_size); 277 std::memcpy(seed_exp.data(), seed.data(), in_size);
266 278
@@ -268,7 +280,7 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
268 size_t i = 0; 280 size_t i = 0;
269 while (out.size() < target_size) { 281 while (out.size() < target_size) {
270 out.resize(out.size() + 0x20); 282 out.resize(out.size() + 0x20);
271 seed_exp[in_size + 3] = i; 283 seed_exp[in_size + 3] = static_cast<u8>(i);
272 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0); 284 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
273 ++i; 285 ++i;
274 } 286 }
@@ -299,10 +311,11 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
299 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); 311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
300 if (cert_authority == 0) 312 if (cert_authority == 0)
301 return boost::none; 313 return boost::none;
302 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) 314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
303 LOG_INFO(Crypto, 315 LOG_INFO(Crypto,
304 "Attempting to parse ticket with non-standard certificate authority {:08X}.", 316 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
305 cert_authority); 317 cert_authority);
318 }
306 319
307 Key128 rights_id; 320 Key128 rights_id;
308 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); 321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
@@ -871,9 +884,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
871 "/system/save/80000000000000e2", 884 "/system/save/80000000000000e2",
872 "rb+"); 885 "rb+");
873 886
887 const auto blob2 = GetTicketblob(save2);
874 auto res = GetTicketblob(save1); 888 auto res = GetTicketblob(save1);
875 const auto res2 = GetTicketblob(save2); 889 res.insert(res.end(), blob2.begin(), blob2.end());
876 std::copy(res2.begin(), res2.end(), std::back_inserter(res));
877 890
878 for (const auto& raw : res) { 891 for (const auto& raw : res) {
879 const auto pair = ParseTicket(raw, rsa_key); 892 const auto pair = ParseTicket(raw, rsa_key);
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index a41abbdfc..cccb3c0ae 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -175,7 +175,7 @@ private:
175 void WriteKeyToFile(KeyCategory category, std::string_view keyname, 175 void WriteKeyToFile(KeyCategory category, std::string_view keyname,
176 const std::array<u8, Size>& key); 176 const std::array<u8, Size>& key);
177 177
178 void DeriveGeneralPurposeKeys(u8 crypto_revision); 178 void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
179 179
180 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 180 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
181 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 181 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index d1c04e98d..25cee1f3a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -11,7 +11,6 @@
11#include <array> 11#include <array>
12#include <cctype> 12#include <cctype>
13#include <cstring> 13#include <cstring>
14#include <boost/optional/optional.hpp>
15#include <mbedtls/sha256.h> 14#include <mbedtls/sha256.h>
16#include "common/assert.h" 15#include "common/assert.h"
17#include "common/common_funcs.h" 16#include "common/common_funcs.h"
@@ -19,7 +18,7 @@
19#include "common/hex_util.h" 18#include "common/hex_util.h"
20#include "common/logging/log.h" 19#include "common/logging/log.h"
21#include "common/string_util.h" 20#include "common/string_util.h"
22#include "core/crypto/ctr_encryption_layer.h" 21#include "common/swap.h"
23#include "core/crypto/key_manager.h" 22#include "core/crypto/key_manager.h"
24#include "core/crypto/partition_data_manager.h" 23#include "core/crypto/partition_data_manager.h"
25#include "core/crypto/xts_encryption_layer.h" 24#include "core/crypto/xts_encryption_layer.h"
@@ -302,10 +301,10 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
302 return nullptr; 301 return nullptr;
303} 302}
304 303
305PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) 304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
306 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), 305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
307 fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), 306 fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
308 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), 307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
309 package2({ 308 package2({
310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"), 309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
311 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"), 310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
@@ -314,13 +313,14 @@ PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir)
314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"), 313 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
315 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"), 314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
316 }), 315 }),
316 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")), 317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")), 318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{} 319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
320 : secure_monitor->ReadAllBytes()), 320 : secure_monitor->ReadAllBytes()),
321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{} 321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
322 : package1_decrypted->ReadAllBytes()), 322 : package1_decrypted->ReadAllBytes()) {
323 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {} 323}
324 324
325PartitionDataManager::~PartitionDataManager() = default; 325PartitionDataManager::~PartitionDataManager() = default;
326 326
@@ -332,18 +332,19 @@ FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
332 return boot0; 332 return boot0;
333} 333}
334 334
335std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const { 335PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
336 if (HasBoot0() && index < 32) 336 std::size_t index) const {
337 if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
337 return GetEncryptedKeyblobs()[index]; 338 return GetEncryptedKeyblobs()[index];
338 return {}; 339 return {};
339} 340}
340 341
341std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const { 342PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
342 if (!HasBoot0()) 343 if (!HasBoot0())
343 return {}; 344 return {};
344 345
345 std::array<std::array<u8, 176>, 32> out{}; 346 EncryptedKeyBlobs out{};
346 for (size_t i = 0; i < 0x20; ++i) 347 for (size_t i = 0; i < out.size(); ++i)
347 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200); 348 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
348 return out; 349 return out;
349} 350}
@@ -389,7 +390,7 @@ std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
389 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]); 390 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
390} 391}
391 392
392std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const { 393std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
393 if (keyblob_source_hashes[revision] == SHA256Hash{}) { 394 if (keyblob_source_hashes[revision] == SHA256Hash{}) {
394 LOG_WARNING(Crypto, 395 LOG_WARNING(Crypto,
395 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...", 396 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
@@ -446,7 +447,7 @@ bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
446 return false; 447 return false;
447} 448}
448 449
449void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys, 450void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
450 Package2Type type) { 451 Package2Type type) {
451 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>( 452 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
452 package2[static_cast<size_t>(type)], 453 package2[static_cast<size_t>(type)],
@@ -456,43 +457,38 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
456 if (file->ReadObject(&header) != sizeof(Package2Header)) 457 if (file->ReadObject(&header) != sizeof(Package2Header))
457 return; 458 return;
458 459
459 u8 revision = 0xFF; 460 std::size_t revision = 0xFF;
460 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) { 461 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
461 for (size_t i = 0; i < package2_keys.size(); ++i) { 462 for (std::size_t i = 0; i < package2_keys.size(); ++i) {
462 if (AttemptDecrypt(package2_keys[i], header)) 463 if (AttemptDecrypt(package2_keys[i], header)) {
463 revision = i; 464 revision = i;
465 }
464 } 466 }
465 } 467 }
466 468
467 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) 469 if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
468 return; 470 return;
469 471
470 const std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end());
471
472 const auto a = std::make_shared<FileSys::OffsetVfsFile>( 472 const auto a = std::make_shared<FileSys::OffsetVfsFile>(
473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header)); 473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
474 474
475 auto c = a->ReadAllBytes(); 475 auto c = a->ReadAllBytes();
476 476
477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); 477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
478 cipher.SetIV(s1_iv); 478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); 479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480 480
481 // package2_decrypted[static_cast<size_t>(type)] = s1;
482
483 INIHeader ini; 481 INIHeader ini;
484 std::memcpy(&ini, c.data(), sizeof(INIHeader)); 482 std::memcpy(&ini, c.data(), sizeof(INIHeader));
485 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) 483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
486 return; 484 return;
487 485
488 std::map<u64, KIPHeader> kips{};
489 u64 offset = sizeof(INIHeader); 486 u64 offset = sizeof(INIHeader);
490 for (size_t i = 0; i < ini.process_count; ++i) { 487 for (size_t i = 0; i < ini.process_count; ++i) {
491 KIPHeader kip; 488 KIPHeader kip;
492 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader)); 489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
493 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1')) 490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
494 return; 491 return;
495 kips.emplace(offset, kip);
496 492
497 const auto name = 493 const auto name =
498 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size()); 494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
@@ -503,33 +499,29 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
503 continue; 499 continue;
504 } 500 }
505 501
506 std::vector<u8> text(kip.sections[0].size_compressed); 502 const u64 initial_offset = sizeof(KIPHeader) + offset;
507 std::vector<u8> rodata(kip.sections[1].size_compressed); 503 const auto text_begin = c.cbegin() + initial_offset;
508 std::vector<u8> data(kip.sections[2].size_compressed); 504 const auto text_end = text_begin + kip.sections[0].size_compressed;
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
509 506
510 u64 offset_sec = sizeof(KIPHeader) + offset; 507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
511 std::memcpy(text.data(), c.data() + offset_sec, text.size()); 508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
512 offset_sec += text.size();
513 std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size());
514 offset_sec += rodata.size();
515 std::memcpy(data.data(), c.data() + offset_sec, data.size());
516 509
517 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + 510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
518 kip.sections[1].size_compressed + kip.sections[2].size_compressed; 511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
519 512
520 text = DecompressBLZ(text); 513 std::vector<u8> out;
521 rodata = DecompressBLZ(rodata); 514 out.reserve(text.size() + rodata.size() + data.size());
522 data = DecompressBLZ(data); 515 out.insert(out.end(), text.begin(), text.end());
516 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end());
523 518
524 std::vector<u8> out(text.size() + rodata.size() + data.size()); 519 offset += sizeof(KIPHeader) + out.size();
525 std::memcpy(out.data(), text.data(), text.size());
526 std::memcpy(out.data() + text.size(), rodata.data(), rodata.size());
527 std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size());
528 520
529 if (name == "FS") 521 if (name == "FS")
530 package2_fs[static_cast<size_t>(type)] = out; 522 package2_fs[static_cast<size_t>(type)] = std::move(out);
531 else if (name == "spl") 523 else if (name == "spl")
532 package2_spl[static_cast<size_t>(type)] = out; 524 package2_spl[static_cast<size_t>(type)] = std::move(out);
533 } 525 }
534} 526}
535 527
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 45c7fecfa..0ad007c72 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -5,9 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <vector>
8#include "common/common_funcs.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/file_sys/vfs_types.h" 9#include "core/file_sys/vfs_types.h"
12 10
13namespace Core::Crypto { 11namespace Core::Crypto {
@@ -24,15 +22,20 @@ enum class Package2Type {
24class PartitionDataManager { 22class PartitionDataManager {
25public: 23public:
26 static const u8 MAX_KEYBLOB_SOURCE_HASH; 24 static const u8 MAX_KEYBLOB_SOURCE_HASH;
25 static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32;
26 static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0;
27 27
28 explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir); 28 using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>;
29 using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
30
31 explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
29 ~PartitionDataManager(); 32 ~PartitionDataManager();
30 33
31 // BOOT0 34 // BOOT0
32 bool HasBoot0() const; 35 bool HasBoot0() const;
33 FileSys::VirtualFile GetBoot0Raw() const; 36 FileSys::VirtualFile GetBoot0Raw() const;
34 std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const; 37 EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
35 std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() const; 38 EncryptedKeyBlobs GetEncryptedKeyblobs() const;
36 std::vector<u8> GetSecureMonitor() const; 39 std::vector<u8> GetSecureMonitor() const;
37 std::array<u8, 0x10> GetPackage2KeySource() const; 40 std::array<u8, 0x10> GetPackage2KeySource() const;
38 std::array<u8, 0x10> GetAESKekGenerationSource() const; 41 std::array<u8, 0x10> GetAESKekGenerationSource() const;
@@ -43,7 +46,7 @@ public:
43 std::vector<u8> GetPackage1Decrypted() const; 46 std::vector<u8> GetPackage1Decrypted() const;
44 std::array<u8, 0x10> GetMasterKeySource() const; 47 std::array<u8, 0x10> GetMasterKeySource() const;
45 std::array<u8, 0x10> GetKeyblobMACKeySource() const; 48 std::array<u8, 0x10> GetKeyblobMACKeySource() const;
46 std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const; 49 std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
47 50
48 // Fuses 51 // Fuses
49 bool HasFuses() const; 52 bool HasFuses() const;
@@ -57,7 +60,8 @@ public:
57 // Package2 60 // Package2
58 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const; 61 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
59 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const; 62 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
60 void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type); 63 void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
64 Package2Type type);
61 const std::vector<u8>& GetPackage2FSDecompressed( 65 const std::vector<u8>& GetPackage2FSDecompressed(
62 Package2Type type = Package2Type::NormalMain) const; 66 Package2Type type = Package2Type::NormalMain) const;
63 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource( 67 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 6102ef476..76a2b7e86 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -10,19 +10,19 @@ namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 sysnand_cache(std::make_shared<RegisteredCache>( 13 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_shared<RegisteredCache>( 15 usrnand_cache(std::make_unique<RegisteredCache>(
16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
17 17
18BISFactory::~BISFactory() = default; 18BISFactory::~BISFactory() = default;
19 19
20std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { 20RegisteredCache* BISFactory::GetSystemNANDContents() const {
21 return sysnand_cache; 21 return sysnand_cache.get();
22} 22}
23 23
24std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const { 24RegisteredCache* BISFactory::GetUserNANDContents() const {
25 return usrnand_cache; 25 return usrnand_cache.get();
26} 26}
27 27
28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { 28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index c352e0925..364d309bd 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -20,8 +20,8 @@ public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 27
@@ -29,8 +29,8 @@ private:
29 VirtualDir nand_root; 29 VirtualDir nand_root;
30 VirtualDir load_root; 30 VirtualDir load_root;
31 31
32 std::shared_ptr<RegisteredCache> sysnand_cache; 32 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::shared_ptr<RegisteredCache> usrnand_cache; 33 std::unique_ptr<RegisteredCache> usrnand_cache;
34}; 34};
35 35
36} // namespace FileSys 36} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index aa1b3c17d..6dcec7816 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -133,7 +133,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
133 static_cast<u8>(type)); 133 static_cast<u8>(type));
134 u128 out_128{}; 134 u128 out_128{};
135 memcpy(out_128.data(), out.data(), 16); 135 memcpy(out_128.data(), out.data(), 16);
136 LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", 136 LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
137 master_key_id, header.key_index, out_128[1], out_128[0]); 137 master_key_id, header.key_index, out_128[1], out_128[0]);
138 138
139 return out; 139 return out;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
17}; 17};
18 18
19std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
21 application_name.size());
21} 22}
22 23
23std::string LanguageEntry::GetDeveloperName() const { 24std::string LanguageEntry::GetDeveloperName() const {
24 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); 25 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
26 developer_name.size());
25} 27}
26 28
27NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 29NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
56 return raw->title_id; 58 return raw->title_id;
57} 59}
58 60
61u64 NACP::GetDLCBaseTitleId() const {
62 return raw->dlc_base_title_id;
63}
64
59std::string NACP::GetVersionString() const { 65std::string NACP::GetVersionString() const {
60 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size());
61} 68}
62} // namespace FileSys 69} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
79 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const;
82 std::string GetVersionString() const; 83 std::string GetVersionString() const;
83 84
84private: 85private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 019caebe9..0117cb0bf 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -214,8 +214,14 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
214 214
215VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, 215VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
216 VirtualFile update_raw) const { 216 VirtualFile update_raw) const {
217 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, 217 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
218 static_cast<u8>(type)); 218 title_id, static_cast<u8>(type))
219 .c_str();
220
221 if (type == ContentRecordType::Program)
222 LOG_INFO(Loader, log_string);
223 else
224 LOG_DEBUG(Loader, log_string);
219 225
220 if (romfs == nullptr) 226 if (romfs == nullptr)
221 return romfs; 227 return romfs;
@@ -346,7 +352,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
346} 352}
347 353
348std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 354std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
349 const auto& installed{Service::FileSystem::GetUnionContents()}; 355 const auto installed{Service::FileSystem::GetUnionContents()};
350 356
351 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); 357 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
352 if (base_control_nca == nullptr) 358 if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e9b040689..1febb398e 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -308,14 +308,14 @@ VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
308 return GetEntryRaw(entry.title_id, entry.type); 308 return GetEntryRaw(entry.title_id, entry.type);
309} 309}
310 310
311std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 311std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
312 const auto raw = GetEntryRaw(title_id, type); 312 const auto raw = GetEntryRaw(title_id, type);
313 if (raw == nullptr) 313 if (raw == nullptr)
314 return nullptr; 314 return nullptr;
315 return std::make_shared<NCA>(raw); 315 return std::make_unique<NCA>(raw);
316} 316}
317 317
318std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 318std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
319 return GetEntry(entry.title_id, entry.type); 319 return GetEntry(entry.title_id, entry.type);
320} 320}
321 321
@@ -516,7 +516,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
516 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
517} 517}
518 518
519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) 519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
520 : caches(std::move(caches)) {} 520 : caches(std::move(caches)) {}
521 521
522void RegisteredCacheUnion::Refresh() { 522void RegisteredCacheUnion::Refresh() {
@@ -572,14 +572,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
572 return GetEntryRaw(entry.title_id, entry.type); 572 return GetEntryRaw(entry.title_id, entry.type);
573} 573}
574 574
575std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 575std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
576 const auto raw = GetEntryRaw(title_id, type); 576 const auto raw = GetEntryRaw(title_id, type);
577 if (raw == nullptr) 577 if (raw == nullptr)
578 return nullptr; 578 return nullptr;
579 return std::make_shared<NCA>(raw); 579 return std::make_unique<NCA>(raw);
580} 580}
581 581
582std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 582std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
583 return GetEntry(entry.title_id, entry.type); 583 return GetEntry(entry.title_id, entry.type);
584} 584}
585 585
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index c0cd59fc5..5ddacba47 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,8 +88,8 @@ public:
88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
90 90
91 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 91 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
92 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 92 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
93 93
94 std::vector<RegisteredCacheEntry> ListEntries() const; 94 std::vector<RegisteredCacheEntry> ListEntries() const;
95 // If a parameter is not boost::none, it will be filtered for from all entries. 95 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -142,7 +142,7 @@ private:
142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
143class RegisteredCacheUnion { 143class RegisteredCacheUnion {
144public: 144public:
145 explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); 145 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
146 146
147 void Refresh(); 147 void Refresh();
148 148
@@ -157,8 +157,8 @@ public:
157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
159 159
160 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 160 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
161 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 161 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162 162
163 std::vector<RegisteredCacheEntry> ListEntries() const; 163 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries. 164 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -168,7 +168,7 @@ public:
168 boost::optional<u64> title_id = boost::none) const; 168 boost::optional<u64> title_id = boost::none) const;
169 169
170private: 170private:
171 std::vector<std::shared_ptr<RegisteredCache>> caches; 171 std::vector<RegisteredCache*> caches;
172}; 172};
173 173
174} // namespace FileSys 174} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 47f2ab9e0..ef1aaebbb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
51 meta.title_id); 51 meta.title_id);
52 } 52 }
53 53
54 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
55 LOG_WARNING(Service_FS,
56 "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
57 "non-zero ({:016X}{:016X})",
58 meta.user_id[1], meta.user_id[0]);
59 }
60
54 std::string save_directory = 61 std::string save_directory =
55 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 62 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
56 63
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
92 case SaveDataSpaceId::NandUser: 99 case SaveDataSpaceId::NandUser:
93 out = "/user/"; 100 out = "/user/";
94 break; 101 break;
102 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/";
104 break;
95 default: 105 default:
96 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
97 } 107 }
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
100 case SaveDataType::SystemSaveData: 110 case SaveDataType::SystemSaveData:
101 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); 111 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
102 case SaveDataType::SaveData: 112 case SaveDataType::SaveData:
113 case SaveDataType::DeviceSaveData:
103 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 114 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
104 title_id); 115 title_id);
105 case SaveDataType::TemporaryStorage: 116 case SaveDataType::TemporaryStorage:
106 return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 117 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
107 title_id); 118 title_id);
108 default: 119 default:
109 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 120 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d66a9c9a4..bd3a57058 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -10,10 +10,10 @@
10namespace FileSys { 10namespace FileSys {
11 11
12SDMCFactory::SDMCFactory(VirtualDir dir_) 12SDMCFactory::SDMCFactory(VirtualDir dir_)
13 : dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>( 13 : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
15 [](const VirtualFile& file, const NcaID& id) { 15 [](const VirtualFile& file, const NcaID& id) {
16 return std::make_shared<NAX>(file, id)->GetDecrypted(); 16 return NAX{file, id}.GetDecrypted();
17 })) {} 17 })) {}
18 18
19SDMCFactory::~SDMCFactory() = default; 19SDMCFactory::~SDMCFactory() = default;
@@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
22 return MakeResult<VirtualDir>(dir); 22 return MakeResult<VirtualDir>(dir);
23} 23}
24 24
25std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const { 25RegisteredCache* SDMCFactory::GetSDMCContents() const {
26 return contents; 26 return contents.get();
27} 27}
28 28
29} // namespace FileSys 29} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index ea12149de..42794ba5b 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,12 +19,12 @@ public:
19 ~SDMCFactory(); 19 ~SDMCFactory();
20 20
21 ResultVal<VirtualDir> Open(); 21 ResultVal<VirtualDir> Open();
22 std::shared_ptr<RegisteredCache> GetSDMCContents() const; 22 RegisteredCache* GetSDMCContents() const;
23 23
24private: 24private:
25 VirtualDir dir; 25 VirtualDir dir;
26 26
27 std::shared_ptr<RegisteredCache> contents; 27 std::unique_ptr<RegisteredCache> contents;
28}; 28};
29 29
30} // namespace FileSys 30} // namespace FileSys
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index e961ef121..bdcc889e0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -207,7 +207,7 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
207 207
208static Kernel::Thread* FindThreadById(int id) { 208static Kernel::Thread* FindThreadById(int id) {
209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
210 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 210 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
211 for (auto& thread : threads) { 211 for (auto& thread : threads) {
212 if (thread->GetThreadID() == static_cast<u32>(id)) { 212 if (thread->GetThreadID() == static_cast<u32>(id)) {
213 current_core = core; 213 current_core = core;
@@ -597,7 +597,7 @@ static void HandleQuery() {
597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { 597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
598 std::string val = "m"; 598 std::string val = "m";
599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
600 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 600 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
601 for (const auto& thread : threads) { 601 for (const auto& thread : threads) {
602 val += fmt::format("{:x}", thread->GetThreadID()); 602 val += fmt::format("{:x}", thread->GetThreadID());
603 val += ","; 603 val += ",";
@@ -612,7 +612,7 @@ static void HandleQuery() {
612 buffer += "l<?xml version=\"1.0\"?>"; 612 buffer += "l<?xml version=\"1.0\"?>";
613 buffer += "<threads>"; 613 buffer += "<threads>";
614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
615 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 615 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
616 for (const auto& thread : threads) { 616 for (const auto& thread : threads) {
617 buffer += 617 buffer +=
618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", 618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index ebf193930..57157beb4 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -39,7 +39,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
39 std::vector<SharedPtr<Thread>>& waiting_threads, 39 std::vector<SharedPtr<Thread>>& waiting_threads,
40 VAddr arb_addr) { 40 VAddr arb_addr) {
41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
42 const auto& thread_list = scheduler->GetThreadList(); 42 const auto& thread_list = scheduler.GetThreadList();
43 43
44 for (const auto& thread : thread_list) { 44 for (const auto& thread : thread_list) {
45 if (thread->GetArbiterWaitAddress() == arb_addr) 45 if (thread->GetArbiterWaitAddress() == arb_addr)
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c80b2c507..073dd5a7d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -153,11 +153,11 @@ void Process::PrepareForTermination() {
153 } 153 }
154 }; 154 };
155 155
156 auto& system = Core::System::GetInstance(); 156 const auto& system = Core::System::GetInstance();
157 stop_threads(system.Scheduler(0)->GetThreadList()); 157 stop_threads(system.Scheduler(0).GetThreadList());
158 stop_threads(system.Scheduler(1)->GetThreadList()); 158 stop_threads(system.Scheduler(1).GetThreadList());
159 stop_threads(system.Scheduler(2)->GetThreadList()); 159 stop_threads(system.Scheduler(2).GetThreadList());
160 stop_threads(system.Scheduler(3)->GetThreadList()); 160 stop_threads(system.Scheduler(3).GetThreadList());
161} 161}
162 162
163/** 163/**
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 73ec01e11..f2816943a 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -24,6 +24,7 @@ class ProgramMetadata;
24namespace Kernel { 24namespace Kernel {
25 25
26class KernelCore; 26class KernelCore;
27class ResourceLimit;
27 28
28struct AddressMapping { 29struct AddressMapping {
29 // Address and size must be page-aligned 30 // Address and size must be page-aligned
@@ -57,9 +58,23 @@ union ProcessFlags {
57 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). 58 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
58}; 59};
59 60
60enum class ProcessStatus { Created, Running, Exited }; 61/**
61 62 * Indicates the status of a Process instance.
62class ResourceLimit; 63 *
64 * @note These match the values as used by kernel,
65 * so new entries should only be added if RE
66 * shows that a new value has been introduced.
67 */
68enum class ProcessStatus {
69 Created,
70 CreatedWithDebuggerAttached,
71 Running,
72 WaitingForDebuggerToAttach,
73 DebuggerAttached,
74 Exiting,
75 Exited,
76 DebugBreak,
77};
63 78
64struct CodeSet final { 79struct CodeSet final {
65 struct Segment { 80 struct Segment {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e406df829..d08b84bde 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -389,6 +389,12 @@ static void Break(u32 reason, u64 info1, u64 info2) {
389 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 389 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
390 reason, info1, info2); 390 reason, info1, info2);
391 ASSERT(false); 391 ASSERT(false);
392
393 Core::CurrentProcess()->PrepareForTermination();
394
395 // Kill the current thread
396 GetCurrentThread()->Stop();
397 Core::System::GetInstance().PrepareReschedule();
392 } 398 }
393} 399}
394 400
@@ -442,25 +448,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
442 case GetInfoType::RandomEntropy: 448 case GetInfoType::RandomEntropy:
443 *result = 0; 449 *result = 0;
444 break; 450 break;
445 case GetInfoType::AddressSpaceBaseAddr: 451 case GetInfoType::ASLRRegionBaseAddr:
446 *result = vm_manager.GetCodeRegionBaseAddress(); 452 *result = vm_manager.GetASLRRegionBaseAddress();
447 break; 453 break;
448 case GetInfoType::AddressSpaceSize: { 454 case GetInfoType::ASLRRegionSize:
449 const u64 width = vm_manager.GetAddressSpaceWidth(); 455 *result = vm_manager.GetASLRRegionSize();
450
451 switch (width) {
452 case 32:
453 *result = 0xFFE00000;
454 break;
455 case 36:
456 *result = 0xFF8000000;
457 break;
458 case 39:
459 *result = 0x7FF8000000;
460 break;
461 }
462 break; 456 break;
463 }
464 case GetInfoType::NewMapRegionBaseAddr: 457 case GetInfoType::NewMapRegionBaseAddr:
465 *result = vm_manager.GetNewMapRegionBaseAddress(); 458 *result = vm_manager.GetNewMapRegionBaseAddress();
466 break; 459 break;
@@ -803,7 +796,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
803 std::vector<SharedPtr<Thread>>& waiting_threads, 796 std::vector<SharedPtr<Thread>>& waiting_threads,
804 VAddr condvar_addr) { 797 VAddr condvar_addr) {
805 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 798 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
806 const auto& thread_list = scheduler->GetThreadList(); 799 const auto& thread_list = scheduler.GetThreadList();
807 800
808 for (const auto& thread : thread_list) { 801 for (const auto& thread : thread_list) {
809 if (thread->GetCondVarWaitAddress() == condvar_addr) 802 if (thread->GetCondVarWaitAddress() == condvar_addr)
@@ -1092,6 +1085,29 @@ static ResultCode ClearEvent(Handle handle) {
1092 return RESULT_SUCCESS; 1085 return RESULT_SUCCESS;
1093} 1086}
1094 1087
1088static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1089 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1090
1091 // This function currently only allows retrieving a process' status.
1092 enum class InfoType {
1093 Status,
1094 };
1095
1096 const auto& kernel = Core::System::GetInstance().Kernel();
1097 const auto process = kernel.HandleTable().Get<Process>(process_handle);
1098 if (!process) {
1099 return ERR_INVALID_HANDLE;
1100 }
1101
1102 const auto info_type = static_cast<InfoType>(type);
1103 if (info_type != InfoType::Status) {
1104 return ERR_INVALID_ENUM_VALUE;
1105 }
1106
1107 *out = static_cast<u64>(process->GetStatus());
1108 return RESULT_SUCCESS;
1109}
1110
1095namespace { 1111namespace {
1096struct FunctionDef { 1112struct FunctionDef {
1097 using Func = void(); 1113 using Func = void();
@@ -1227,7 +1243,7 @@ static const FunctionDef SVC_Table[] = {
1227 {0x79, nullptr, "CreateProcess"}, 1243 {0x79, nullptr, "CreateProcess"},
1228 {0x7A, nullptr, "StartProcess"}, 1244 {0x7A, nullptr, "StartProcess"},
1229 {0x7B, nullptr, "TerminateProcess"}, 1245 {0x7B, nullptr, "TerminateProcess"},
1230 {0x7C, nullptr, "GetProcessInfo"}, 1246 {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
1231 {0x7D, nullptr, "CreateResourceLimit"}, 1247 {0x7D, nullptr, "CreateResourceLimit"},
1232 {0x7E, nullptr, "SetResourceLimitLimitValue"}, 1248 {0x7E, nullptr, "SetResourceLimitLimitValue"},
1233 {0x7F, nullptr, "CallSecureMonitor"}, 1249 {0x7F, nullptr, "CallSecureMonitor"},
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 70148c4fe..554a5e328 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -41,8 +41,8 @@ enum class GetInfoType : u64 {
41 RandomEntropy = 11, 41 RandomEntropy = 11,
42 PerformanceCounter = 0xF0000002, 42 PerformanceCounter = 0xF0000002,
43 // 2.0.0+ 43 // 2.0.0+
44 AddressSpaceBaseAddr = 12, 44 ASLRRegionBaseAddr = 12,
45 AddressSpaceSize = 13, 45 ASLRRegionSize = 13,
46 NewMapRegionBaseAddr = 14, 46 NewMapRegionBaseAddr = 14,
47 NewMapRegionSize = 15, 47 NewMapRegionSize = 15,
48 // 3.0.0+ 48 // 3.0.0+
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index cbb80c3c4..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -77,6 +77,14 @@ void SvcWrap() {
77 FuncReturn(retval); 77 FuncReturn(retval);
78} 78}
79 79
80template <ResultCode func(u64*, u32, u32)>
81void SvcWrap() {
82 u64 param_1 = 0;
83 u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
84 Core::CurrentArmInterface().SetReg(1, param_1);
85 FuncReturn(retval);
86}
87
80template <ResultCode func(u32, u64)> 88template <ResultCode func(u32, u64)>
81void SvcWrap() { 89void SvcWrap() {
82 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); 90 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 352ce1725..35ec98c1a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -97,7 +97,7 @@ void Thread::CancelWakeupTimer() {
97static boost::optional<s32> GetNextProcessorId(u64 mask) { 97static boost::optional<s32> GetNextProcessorId(u64 mask) {
98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { 98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
99 if (mask & (1ULL << index)) { 99 if (mask & (1ULL << index)) {
100 if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) { 100 if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
101 // Core is enabled and not running any threads, use this one 101 // Core is enabled and not running any threads, use this one
102 return index; 102 return index;
103 } 103 }
@@ -147,14 +147,14 @@ void Thread::ResumeFromWait() {
147 new_processor_id = processor_id; 147 new_processor_id = processor_id;
148 } 148 }
149 if (ideal_core != -1 && 149 if (ideal_core != -1 &&
150 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 150 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
151 new_processor_id = ideal_core; 151 new_processor_id = ideal_core;
152 } 152 }
153 153
154 ASSERT(*new_processor_id < 4); 154 ASSERT(*new_processor_id < 4);
155 155
156 // Add thread to new core's scheduler 156 // Add thread to new core's scheduler
157 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 157 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
158 158
159 if (*new_processor_id != processor_id) { 159 if (*new_processor_id != processor_id) {
160 // Remove thread from previous core's scheduler 160 // Remove thread from previous core's scheduler
@@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {
169 next_scheduler->ScheduleThread(this, current_priority); 169 next_scheduler->ScheduleThread(this, current_priority);
170 170
171 // Change thread's scheduler 171 // Change thread's scheduler
172 scheduler = next_scheduler.get(); 172 scheduler = next_scheduler;
173 173
174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
175} 175}
@@ -230,7 +230,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
230 thread->name = std::move(name); 230 thread->name = std::move(name);
231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
232 thread->owner_process = &owner_process; 232 thread->owner_process = &owner_process;
233 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); 233 thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
234 thread->scheduler->AddThread(thread, priority); 234 thread->scheduler->AddThread(thread, priority);
235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
236 236
@@ -375,14 +375,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {
375 new_processor_id = processor_id; 375 new_processor_id = processor_id;
376 } 376 }
377 if (ideal_core != -1 && 377 if (ideal_core != -1 &&
378 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
379 new_processor_id = ideal_core; 379 new_processor_id = ideal_core;
380 } 380 }
381 381
382 ASSERT(*new_processor_id < 4); 382 ASSERT(*new_processor_id < 4);
383 383
384 // Add thread to new core's scheduler 384 // Add thread to new core's scheduler
385 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
386 386
387 if (*new_processor_id != processor_id) { 387 if (*new_processor_id != processor_id) {
388 // Remove thread from previous core's scheduler 388 // Remove thread from previous core's scheduler
@@ -397,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
397 next_scheduler->ScheduleThread(this, current_priority); 397 next_scheduler->ScheduleThread(this, current_priority);
398 398
399 // Change thread's scheduler 399 // Change thread's scheduler
400 scheduler = next_scheduler.get(); 400 scheduler = next_scheduler;
401 401
402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
403} 403}
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..1e28ccbda 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -393,30 +393,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
393 393
394 switch (type) { 394 switch (type) {
395 case FileSys::ProgramAddressSpaceType::Is32Bit: 395 case FileSys::ProgramAddressSpaceType::Is32Bit:
396 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
396 address_space_width = 32; 397 address_space_width = 32;
397 code_region_base = 0x200000; 398 code_region_base = 0x200000;
398 code_region_end = code_region_base + 0x3FE00000; 399 code_region_end = code_region_base + 0x3FE00000;
399 map_region_size = 0x40000000; 400 aslr_region_base = 0x200000;
400 heap_region_size = 0x40000000; 401 aslr_region_end = aslr_region_base + 0xFFE00000;
402 if (type == FileSys::ProgramAddressSpaceType::Is32Bit) {
403 map_region_size = 0x40000000;
404 heap_region_size = 0x40000000;
405 } else {
406 map_region_size = 0;
407 heap_region_size = 0x80000000;
408 }
401 break; 409 break;
402 case FileSys::ProgramAddressSpaceType::Is36Bit: 410 case FileSys::ProgramAddressSpaceType::Is36Bit:
403 address_space_width = 36; 411 address_space_width = 36;
404 code_region_base = 0x8000000; 412 code_region_base = 0x8000000;
405 code_region_end = code_region_base + 0x78000000; 413 code_region_end = code_region_base + 0x78000000;
414 aslr_region_base = 0x8000000;
415 aslr_region_end = aslr_region_base + 0xFF8000000;
406 map_region_size = 0x180000000; 416 map_region_size = 0x180000000;
407 heap_region_size = 0x180000000; 417 heap_region_size = 0x180000000;
408 break; 418 break;
409 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
410 address_space_width = 32;
411 code_region_base = 0x200000;
412 code_region_end = code_region_base + 0x3FE00000;
413 map_region_size = 0;
414 heap_region_size = 0x80000000;
415 break;
416 case FileSys::ProgramAddressSpaceType::Is39Bit: 419 case FileSys::ProgramAddressSpaceType::Is39Bit:
417 address_space_width = 39; 420 address_space_width = 39;
418 code_region_base = 0x8000000; 421 code_region_base = 0x8000000;
419 code_region_end = code_region_base + 0x80000000; 422 code_region_end = code_region_base + 0x80000000;
423 aslr_region_base = 0x8000000;
424 aslr_region_end = aslr_region_base + 0x7FF8000000;
420 map_region_size = 0x1000000000; 425 map_region_size = 0x1000000000;
421 heap_region_size = 0x180000000; 426 heap_region_size = 0x180000000;
422 new_map_region_size = 0x80000000; 427 new_map_region_size = 0x80000000;
@@ -490,6 +495,18 @@ u64 VMManager::GetAddressSpaceWidth() const {
490 return address_space_width; 495 return address_space_width;
491} 496}
492 497
498VAddr VMManager::GetASLRRegionBaseAddress() const {
499 return aslr_region_base;
500}
501
502VAddr VMManager::GetASLRRegionEndAddress() const {
503 return aslr_region_end;
504}
505
506u64 VMManager::GetASLRRegionSize() const {
507 return aslr_region_end - aslr_region_base;
508}
509
493VAddr VMManager::GetCodeRegionBaseAddress() const { 510VAddr VMManager::GetCodeRegionBaseAddress() const {
494 return code_region_base; 511 return code_region_base;
495} 512}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 015559a64..4accde6b3 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -205,6 +205,15 @@ public:
205 /// Gets the address space width in bits. 205 /// Gets the address space width in bits.
206 u64 GetAddressSpaceWidth() const; 206 u64 GetAddressSpaceWidth() const;
207 207
208 /// Gets the base address of the ASLR region.
209 VAddr GetASLRRegionBaseAddress() const;
210
211 /// Gets the end address of the ASLR region.
212 VAddr GetASLRRegionEndAddress() const;
213
214 /// Gets the size of the ASLR region
215 u64 GetASLRRegionSize() const;
216
208 /// Gets the base address of the code region. 217 /// Gets the base address of the code region.
209 VAddr GetCodeRegionBaseAddress() const; 218 VAddr GetCodeRegionBaseAddress() const;
210 219
@@ -306,6 +315,9 @@ private:
306 VAddr address_space_base = 0; 315 VAddr address_space_base = 0;
307 VAddr address_space_end = 0; 316 VAddr address_space_end = 0;
308 317
318 VAddr aslr_region_base = 0;
319 VAddr aslr_region_end = 0;
320
309 VAddr code_region_base = 0; 321 VAddr code_region_base = 0;
310 VAddr code_region_end = 0; 322 VAddr code_region_end = 0;
311 323
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0ecfb5af1..518161bf7 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,8 +7,10 @@
7#include <vector> 7#include <vector>
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
10#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/partition_filesystem.h" 12#include "core/file_sys/partition_filesystem.h"
13#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
13#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
@@ -19,7 +21,7 @@
19namespace Service::AOC { 21namespace Service::AOC {
20 22
21constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 23constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
22constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; 24constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
23 25
24static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 26static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
25 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 27 return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
@@ -97,14 +99,24 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
97 99
98 ctx.WriteBuffer(out); 100 ctx.WriteBuffer(out);
99 101
100 IPC::ResponseBuilder rb{ctx, 2}; 102 IPC::ResponseBuilder rb{ctx, 3};
101 rb.Push(RESULT_SUCCESS); 103 rb.Push(RESULT_SUCCESS);
104 rb.Push(count);
102} 105}
103 106
104void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { 107void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
105 IPC::ResponseBuilder rb{ctx, 4}; 108 IPC::ResponseBuilder rb{ctx, 4};
106 rb.Push(RESULT_SUCCESS); 109 rb.Push(RESULT_SUCCESS);
107 rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); 110 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
111 FileSys::PatchManager pm{title_id};
112
113 const auto res = pm.GetControlMetadata();
114 if (res.first == nullptr) {
115 rb.Push(title_id + DLC_BASE_TO_AOC_ID);
116 return;
117 }
118
119 rb.Push(res.first->GetDLCBaseTitleId());
108} 120}
109 121
110void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { 122void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 439e62d27..e32a7c48e 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -319,13 +319,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
319 return sdmc_factory->Open(); 319 return sdmc_factory->Open();
320} 320}
321 321
322std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { 322std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
323 return std::make_shared<FileSys::RegisteredCacheUnion>( 323 return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
324 std::vector<std::shared_ptr<FileSys::RegisteredCache>>{ 324 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
325 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
326} 325}
327 326
328std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { 327FileSys::RegisteredCache* GetSystemNANDContents() {
329 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 328 LOG_TRACE(Service_FS, "Opening System NAND Contents");
330 329
331 if (bis_factory == nullptr) 330 if (bis_factory == nullptr)
@@ -334,7 +333,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
334 return bis_factory->GetSystemNANDContents(); 333 return bis_factory->GetSystemNANDContents();
335} 334}
336 335
337std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() { 336FileSys::RegisteredCache* GetUserNANDContents() {
338 LOG_TRACE(Service_FS, "Opening User NAND Contents"); 337 LOG_TRACE(Service_FS, "Opening User NAND Contents");
339 338
340 if (bis_factory == nullptr) 339 if (bis_factory == nullptr)
@@ -343,7 +342,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {
343 return bis_factory->GetUserNANDContents(); 342 return bis_factory->GetUserNANDContents();
344} 343}
345 344
346std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() { 345FileSys::RegisteredCache* GetSDMCContents() {
347 LOG_TRACE(Service_FS, "Opening SDMC Contents"); 346 LOG_TRACE(Service_FS, "Opening SDMC Contents");
348 347
349 if (sdmc_factory == nullptr) 348 if (sdmc_factory == nullptr)
@@ -361,19 +360,19 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
361 return bis_factory->GetModificationLoadRoot(title_id); 360 return bis_factory->GetModificationLoadRoot(title_id);
362} 361}
363 362
364void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { 363void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
365 if (overwrite) { 364 if (overwrite) {
366 bis_factory = nullptr; 365 bis_factory = nullptr;
367 save_data_factory = nullptr; 366 save_data_factory = nullptr;
368 sdmc_factory = nullptr; 367 sdmc_factory = nullptr;
369 } 368 }
370 369
371 auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 370 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
372 FileSys::Mode::ReadWrite); 371 FileSys::Mode::ReadWrite);
373 auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 372 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
374 FileSys::Mode::ReadWrite); 373 FileSys::Mode::ReadWrite);
375 auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 374 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
376 FileSys::Mode::ReadWrite); 375 FileSys::Mode::ReadWrite);
377 376
378 if (bis_factory == nullptr) 377 if (bis_factory == nullptr)
379 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); 378 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
@@ -383,7 +382,7 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
383 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 382 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
384} 383}
385 384
386void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { 385void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
387 romfs_factory = nullptr; 386 romfs_factory = nullptr;
388 CreateFactories(vfs, false); 387 CreateFactories(vfs, false);
389 std::make_shared<FSP_LDR>()->InstallAsService(service_manager); 388 std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 53b01bb01..6ca5c5636 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -47,19 +47,19 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
47 FileSys::SaveDataDescriptor save_struct); 47 FileSys::SaveDataDescriptor save_struct);
48ResultVal<FileSys::VirtualDir> OpenSDMC(); 48ResultVal<FileSys::VirtualDir> OpenSDMC();
49 49
50std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 50std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
51 51
52std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); 52FileSys::RegisteredCache* GetSystemNANDContents();
53std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); 53FileSys::RegisteredCache* GetUserNANDContents();
54std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); 54FileSys::RegisteredCache* GetSDMCContents();
55 55
56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
57 57
58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
59// above is called. 59// above is called.
60void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); 60void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
61 61
62void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs); 62void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs);
63 63
64// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of 64// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
65// pointers and booleans. This makes using a VfsDirectory with switch services much easier and 65// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 4b2f758a8..44accecb7 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -161,7 +161,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
161 }; 161 };
162 RegisterHandlers(functions); 162 RegisterHandlers(functions);
163 // Attempt to load shared font data from disk 163 // Attempt to load shared font data from disk
164 const auto nand = FileSystem::GetSystemNANDContents(); 164 const auto* nand = FileSystem::GetSystemNANDContents();
165 std::size_t offset = 0; 165 std::size_t offset = 0;
166 // Rebuild shared fonts from data ncas 166 // Rebuild shared fonts from data ncas
167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 62f049660..a225cb4cb 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -197,7 +197,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
197// Module interface 197// Module interface
198 198
199/// Initialize ServiceManager 199/// Initialize ServiceManager
200void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { 200void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {
201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
202 // here and pass it into the respective InstallInterfaces functions. 202 // here and pass it into the respective InstallInterfaces functions.
203 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); 203 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -220,7 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
220 EUPLD::InstallInterfaces(*sm); 220 EUPLD::InstallInterfaces(*sm);
221 Fatal::InstallInterfaces(*sm); 221 Fatal::InstallInterfaces(*sm);
222 FGM::InstallInterfaces(*sm); 222 FGM::InstallInterfaces(*sm);
223 FileSystem::InstallInterfaces(*sm, rfs); 223 FileSystem::InstallInterfaces(*sm, vfs);
224 Friend::InstallInterfaces(*sm); 224 Friend::InstallInterfaces(*sm);
225 GRC::InstallInterfaces(*sm); 225 GRC::InstallInterfaces(*sm);
226 HID::InstallInterfaces(*sm); 226 HID::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2fc57a82e..98483ecf1 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -180,8 +180,7 @@ private:
180}; 180};
181 181
182/// Initialize ServiceManager 182/// Initialize ServiceManager
183void Init(std::shared_ptr<SM::ServiceManager>& sm, 183void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);
184 const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
185 184
186/// Shutdown ServiceManager 185/// Shutdown ServiceManager
187void Shutdown(); 186void Shutdown();
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index bbc02abcc..184537daa 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -968,6 +968,54 @@ private:
968 rb.PushCopyObjects(vsync_event); 968 rb.PushCopyObjects(vsync_event);
969 } 969 }
970 970
971 enum class ConvertedScaleMode : u64 {
972 None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no
973 // scaling/default
974 Freeze = 1,
975 ScaleToWindow = 2,
976 Crop = 3,
977 NoCrop = 4,
978 };
979
980 // This struct is different, currently it's 1:1 but this might change in the future.
981 enum class NintendoScaleMode : u32 {
982 None = 0,
983 Freeze = 1,
984 ScaleToWindow = 2,
985 Crop = 3,
986 NoCrop = 4,
987 };
988
989 void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
990 IPC::RequestParser rp{ctx};
991 auto mode = rp.PopEnum<NintendoScaleMode>();
992 LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode));
993
994 IPC::ResponseBuilder rb{ctx, 4};
995 rb.Push(RESULT_SUCCESS);
996 switch (mode) {
997 case NintendoScaleMode::None:
998 rb.PushEnum(ConvertedScaleMode::None);
999 break;
1000 case NintendoScaleMode::Freeze:
1001 rb.PushEnum(ConvertedScaleMode::Freeze);
1002 break;
1003 case NintendoScaleMode::ScaleToWindow:
1004 rb.PushEnum(ConvertedScaleMode::ScaleToWindow);
1005 break;
1006 case NintendoScaleMode::Crop:
1007 rb.PushEnum(ConvertedScaleMode::Crop);
1008 break;
1009 case NintendoScaleMode::NoCrop:
1010 rb.PushEnum(ConvertedScaleMode::NoCrop);
1011 break;
1012 default:
1013 UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode));
1014 rb.PushEnum(ConvertedScaleMode::None);
1015 break;
1016 }
1017 }
1018
971 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 1019 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
972}; 1020};
973 1021
@@ -991,7 +1039,7 @@ IApplicationDisplayService::IApplicationDisplayService(
991 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, 1039 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
992 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, 1040 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
993 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, 1041 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
994 {2102, nullptr, "ConvertScalingMode"}, 1042 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
995 {2450, nullptr, "GetIndirectLayerImageMap"}, 1043 {2450, nullptr, "GetIndirectLayerImageMap"},
996 {2451, nullptr, "GetIndirectLayerImageCropMap"}, 1044 {2451, nullptr, "GetIndirectLayerImageCropMap"},
997 {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, 1045 {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"},
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 951fd8257..8518dddcb 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -139,14 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
141 const FileSys::VirtualFile module_file = dir->GetFile(module); 141 const FileSys::VirtualFile module_file = dir->GetFile(module);
142 if (module_file != nullptr) { 142 if (module_file == nullptr) {
143 const VAddr load_addr = next_load_addr; 143 continue;
144 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr,
145 std::strcmp(module, "rtld") == 0, pm);
146 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
147 // Register module with GDBStub
148 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
149 } 144 }
145
146 const VAddr load_addr = next_load_addr;
147 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
148 const auto tentative_next_load_addr =
149 AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
150 if (!tentative_next_load_addr) {
151 return ResultStatus::ErrorLoadingNSO;
152 }
153
154 next_load_addr = *tentative_next_load_addr;
155 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
156 // Register module with GDBStub
157 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
150 } 158 }
151 159
152 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); 160 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 91659ec17..9cd0b0ccd 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 93 return "unknown";
94} 94}
95 95
96constexpr std::array<const char*, 59> RESULT_MESSAGES{ 96constexpr std::array<const char*, 60> RESULT_MESSAGES{
97 "The operation completed successfully.", 97 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 98 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 99 "The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
128 "The RomFS could not be found.", 128 "The RomFS could not be found.",
129 "The ELF file has incorrect size as determined by the header.", 129 "The ELF file has incorrect size as determined by the header.",
130 "There was a general error loading the NRO into emulated memory.", 130 "There was a general error loading the NRO into emulated memory.",
131 "There was a general error loading the NSO into emulated memory.",
131 "There is no icon available.", 132 "There is no icon available.",
132 "There is no control data available.", 133 "There is no control data available.",
133 "The NAX file has a bad header.", 134 "The NAX file has a bad header.",
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 0e0333db5..e562b3a04 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -90,6 +90,7 @@ enum class ResultStatus : u16 {
90 ErrorNoRomFS, 90 ErrorNoRomFS,
91 ErrorIncorrectELFFileSize, 91 ErrorIncorrectELFFileSize,
92 ErrorLoadingNRO, 92 ErrorLoadingNRO,
93 ErrorLoadingNSO,
93 ErrorNoIcon, 94 ErrorNoIcon,
94 ErrorNoControl, 95 ErrorNoControl,
95 ErrorBadNAXHeader, 96 ErrorBadNAXHeader,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 576fe692a..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
128} 128}
129 129
130bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { 130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
131 // Read NSO header 131 // Read NSO header
132 NroHeader nro_header{}; 132 NroHeader nro_header{};
133 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { 133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
134 return {}; 134 return {};
135 } 135 }
136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -138,7 +138,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
138 } 138 }
139 139
140 // Build program image 140 // Build program image
141 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); 141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
142 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 142 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
143 return {}; 143 return {};
144 } 144 }
@@ -182,7 +182,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
183 183
184 // Register module with GDBStub 184 // Register module with GDBStub
185 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 185 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
186 186
187 return true; 187 return true;
188} 188}
@@ -195,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
195 // Load NRO 195 // Load NRO
196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
197 197
198 if (!LoadNro(file, base_address)) { 198 if (!LoadNro(*file, base_address)) {
199 return ResultStatus::ErrorLoadingNRO; 199 return ResultStatus::ErrorLoadingNRO;
200 } 200 }
201 201
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04b46119a..50ee5a78a 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -41,7 +41,7 @@ public:
41 bool IsRomFSUpdatable() const override; 41 bool IsRomFSUpdatable() const override;
42 42
43private: 43private:
44 bool LoadNro(FileSys::VirtualFile file, VAddr load_base); 44 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 45
46 std::vector<u8> icon_data; 46 std::vector<u8> icon_data;
47 std::unique_ptr<FileSys::NACP> nacp; 47 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index ba57db9bf..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -93,17 +93,14 @@ static constexpr u32 PageAlignSize(u32 size) {
93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
94} 94}
95 95
96VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
97 bool should_pass_arguments, 97 bool should_pass_arguments,
98 boost::optional<FileSys::PatchManager> pm) { 98 std::optional<FileSys::PatchManager> pm) {
99 if (file == nullptr) 99 if (file.GetSize() < sizeof(NsoHeader))
100 return {};
101
102 if (file->GetSize() < sizeof(NsoHeader))
103 return {}; 100 return {};
104 101
105 NsoHeader nso_header{}; 102 NsoHeader nso_header{};
106 if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) 103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
107 return {}; 104 return {};
108 105
109 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
@@ -114,7 +111,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
114 std::vector<u8> program_image; 111 std::vector<u8> program_image;
115 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 112 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
116 std::vector<u8> data = 113 std::vector<u8> data =
117 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 114 file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
118 if (nso_header.IsSegmentCompressed(i)) { 115 if (nso_header.IsSegmentCompressed(i)) {
119 data = DecompressSegment(data, nso_header.segments[i]); 116 data = DecompressSegment(data, nso_header.segments[i]);
120 } 117 }
@@ -157,7 +154,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
157 program_image.resize(image_size); 154 program_image.resize(image_size);
158 155
159 // Apply patches if necessary 156 // Apply patches if necessary
160 if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && pm->HasNSOPatch(nso_header.build_id)) {
161 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
162 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
163 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
@@ -172,7 +169,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
172 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 169 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
173 170
174 // Register module with GDBStub 171 // Register module with GDBStub
175 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 172 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
176 173
177 return load_base + image_size; 174 return load_base + image_size;
178} 175}
@@ -184,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
184 181
185 // Load module 182 // Load module
186 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 183 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
187 LoadModule(file, base_address, true); 184 if (!LoadModule(*file, base_address, true)) {
185 return ResultStatus::ErrorLoadingNSO;
186 }
188 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 187 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
189 188
190 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 189 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 70ab3b718..433306139 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/file_sys/patch_manager.h" 9#include "core/file_sys/patch_manager.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
@@ -36,8 +37,9 @@ public:
36 return IdentifyType(file); 37 return IdentifyType(file);
37 } 38 }
38 39
39 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, 40 static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
40 boost::optional<FileSys::PatchManager> pm = boost::none); 41 bool should_pass_arguments,
42 std::optional<FileSys::PatchManager> pm = {});
41 43
42 ResultStatus Load(Kernel::Process& process) override; 44 ResultStatus Load(Kernel::Process& process) override;
43}; 45};
diff --git a/src/core/settings.h b/src/core/settings.h
index 83b9a04c8..8f2da01c8 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -136,7 +136,7 @@ struct Values {
136 float resolution_factor; 136 float resolution_factor;
137 bool use_frame_limit; 137 bool use_frame_limit;
138 u16 frame_limit; 138 u16 frame_limit;
139 bool use_accurate_framebuffers; 139 bool use_accurate_gpu_emulation;
140 140
141 float bg_red; 141 float bg_red;
142 float bg_green; 142 float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 7b04792b5..0de13edd3 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -163,8 +163,8 @@ TelemetrySession::TelemetrySession() {
163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", 163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
164 Settings::values.use_frame_limit); 164 Settings::values.use_frame_limit);
165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); 165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", 166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
167 Settings::values.use_accurate_framebuffers); 167 Settings::values.use_accurate_gpu_emulation);
168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
169 Settings::values.use_docked_mode); 169 Settings::values.use_docked_mode);
170} 170}
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 912e785b9..597b279b9 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -62,14 +62,16 @@ void Fermi2D::HandleSurfaceCopy() {
62 u8* dst_buffer = Memory::GetPointer(dest_cpu); 62 u8* dst_buffer = Memory::GetPointer(dest_cpu);
63 if (!regs.src.linear && regs.dst.linear) { 63 if (!regs.src.linear && regs.dst.linear) {
64 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 64 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
65 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 65 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
66 dst_bytes_per_pixel, src_buffer, dst_buffer, true, 66 src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
67 regs.src.BlockHeight()); 67 dst_buffer, true, regs.src.BlockHeight(),
68 regs.src.BlockDepth());
68 } else { 69 } else {
69 // If the input is linear and the output is tiled, swizzle the input and copy it over. 70 // If the input is linear and the output is tiled, swizzle the input and copy it over.
70 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 71 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
71 dst_bytes_per_pixel, dst_buffer, src_buffer, false, 72 src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
72 regs.dst.BlockHeight()); 73 src_buffer, false, regs.dst.BlockHeight(),
74 regs.dst.BlockDepth());
73 } 75 }
74 } 76 }
75} 77}
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index aa7481b8c..bf2a21bb6 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -68,12 +68,14 @@ void MaxwellDMA::HandleCopy() {
68 68
69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
70 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 70 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer, 71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y,
72 dst_buffer, true, regs.src_params.BlockHeight()); 72 regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true,
73 regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
73 } else { 74 } else {
74 // If the input is linear and the output is tiled, swizzle the input and copy it over. 75 // If the input is linear and the output is tiled, swizzle the input and copy it over.
75 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer, 76 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y,
76 src_buffer, false, regs.dst_params.BlockHeight()); 77 regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false,
78 regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
77 } 79 }
78} 80}
79 81
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 311ccb616..df19e02e2 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -43,6 +43,10 @@ public:
43 u32 BlockHeight() const { 43 u32 BlockHeight() const {
44 return 1 << block_height; 44 return 1 << block_height;
45 } 45 }
46
47 u32 BlockDepth() const {
48 return 1 << block_depth;
49 }
46 }; 50 };
47 51
48 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); 52 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 9a59b65b3..f356f9a03 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -267,7 +267,7 @@ enum class ControlCode : u64 {
267 GTU = 12, 267 GTU = 12,
268 NEU = 13, 268 NEU = 13,
269 GEU = 14, 269 GEU = 14,
270 // 270 T = 15,
271 OFF = 16, 271 OFF = 16,
272 LO = 17, 272 LO = 17,
273 SFF = 18, 273 SFF = 18,
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index ca923d17d..022d4ab74 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -87,6 +87,16 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
87 return gpu_addr; 87 return gpu_addr;
88} 88}
89 89
90GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const {
91 for (const auto& region : mapped_regions) {
92 const GPUVAddr region_end{region.gpu_addr + region.size};
93 if (region_start >= region.gpu_addr && region_start < region_end) {
94 return region_end;
95 }
96 }
97 return {};
98}
99
90boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { 100boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
91 GPUVAddr gpu_addr = 0; 101 GPUVAddr gpu_addr = 0;
92 u64 free_space = 0; 102 u64 free_space = 0;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 86765e72a..caf80093f 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -26,6 +26,7 @@ public:
26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
29 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
29 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 30 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; 31 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
31 32
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 083b283b0..0a3b3951e 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -11,32 +11,77 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/settings.h"
14#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
16 17
18class RasterizerCacheObject {
19public:
20 /// Gets the address of the shader in guest memory, required for cache management
21 virtual VAddr GetAddr() const = 0;
22
23 /// Gets the size of the shader in guest memory, required for cache management
24 virtual std::size_t GetSizeInBytes() const = 0;
25
26 /// Wriets any cached resources back to memory
27 virtual void Flush() = 0;
28
29 /// Sets whether the cached object should be considered registered
30 void SetIsRegistered(bool registered) {
31 is_registered = registered;
32 }
33
34 /// Returns true if the cached object is registered
35 bool IsRegistered() const {
36 return is_registered;
37 }
38
39 /// Returns true if the cached object is dirty
40 bool IsDirty() const {
41 return is_dirty;
42 }
43
44 /// Returns ticks from when this cached object was last modified
45 u64 GetLastModifiedTicks() const {
46 return last_modified_ticks;
47 }
48
49 /// Marks an object as recently modified, used to specify whether it is clean or dirty
50 template <class T>
51 void MarkAsModified(bool dirty, T& cache) {
52 is_dirty = dirty;
53 last_modified_ticks = cache.GetModifiedTicks();
54 }
55
56private:
57 bool is_registered{}; ///< Whether the object is currently registered with the cache
58 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
59 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
60};
61
17template <class T> 62template <class T>
18class RasterizerCache : NonCopyable { 63class RasterizerCache : NonCopyable {
64 friend class RasterizerCacheObject;
65
19public: 66public:
67 /// Write any cached resources overlapping the specified region back to memory
68 void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
69 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
70 for (auto& object : objects) {
71 FlushObject(object);
72 }
73 }
74
20 /// Mark the specified region as being invalidated 75 /// Mark the specified region as being invalidated
21 void InvalidateRegion(VAddr addr, u64 size) { 76 void InvalidateRegion(VAddr addr, u64 size) {
22 if (size == 0) 77 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
23 return; 78 for (auto& object : objects) {
24 79 if (!object->IsRegistered()) {
25 const ObjectInterval interval{addr, addr + size}; 80 // Skip duplicates
26 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 81 continue;
27 for (auto& cached_object : pair.second) {
28 if (!cached_object)
29 continue;
30
31 remove_objects.emplace(cached_object);
32 } 82 }
83 Unregister(object);
33 } 84 }
34
35 for (auto& remove_object : remove_objects) {
36 Unregister(remove_object);
37 }
38
39 remove_objects.clear();
40 } 85 }
41 86
42 /// Invalidates everything in the cache 87 /// Invalidates everything in the cache
@@ -62,6 +107,7 @@ protected:
62 107
63 /// Register an object into the cache 108 /// Register an object into the cache
64 void Register(const T& object) { 109 void Register(const T& object) {
110 object->SetIsRegistered(true);
65 object_cache.add({GetInterval(object), ObjectSet{object}}); 111 object_cache.add({GetInterval(object), ObjectSet{object}});
66 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 112 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
67 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 113 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
@@ -69,12 +115,57 @@ protected:
69 115
70 /// Unregisters an object from the cache 116 /// Unregisters an object from the cache
71 void Unregister(const T& object) { 117 void Unregister(const T& object) {
118 object->SetIsRegistered(false);
72 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 119 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
73 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); 120 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
121
122 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
123 if (Settings::values.use_accurate_gpu_emulation) {
124 FlushObject(object);
125 }
126
74 object_cache.subtract({GetInterval(object), ObjectSet{object}}); 127 object_cache.subtract({GetInterval(object), ObjectSet{object}});
75 } 128 }
76 129
130 /// Returns a ticks counter used for tracking when cached objects were last modified
131 u64 GetModifiedTicks() {
132 return ++modified_ticks;
133 }
134
77private: 135private:
136 /// Returns a list of cached objects from the specified memory region, ordered by access time
137 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
138 if (size == 0) {
139 return {};
140 }
141
142 std::vector<T> objects;
143 const ObjectInterval interval{addr, addr + size};
144 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
145 for (auto& cached_object : pair.second) {
146 if (!cached_object) {
147 continue;
148 }
149 objects.push_back(cached_object);
150 }
151 }
152
153 std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
154 return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
155 });
156
157 return objects;
158 }
159
160 /// Flushes the specified object, updating appropriate cache state as needed
161 void FlushObject(const T& object) {
162 if (!object->IsDirty()) {
163 return;
164 }
165 object->Flush();
166 object->MarkAsModified(false, *this);
167 }
168
78 using ObjectSet = std::set<T>; 169 using ObjectSet = std::set<T>;
79 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; 170 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
80 using ObjectInterval = typename ObjectCache::interval_type; 171 using ObjectInterval = typename ObjectCache::interval_type;
@@ -84,6 +175,6 @@ private:
84 object->GetAddr() + object->GetSizeInBytes()); 175 object->GetAddr() + object->GetSizeInBytes());
85 } 176 }
86 177
87 ObjectCache object_cache; 178 ObjectCache object_cache; ///< Cache of objects
88 ObjectSet remove_objects; 179 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
89}; 180};
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 965976334..be29dc8be 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -15,15 +15,18 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18struct CachedBufferEntry final { 18struct CachedBufferEntry final : public RasterizerCacheObject {
19 VAddr GetAddr() const { 19 VAddr GetAddr() const override {
20 return addr; 20 return addr;
21 } 21 }
22 22
23 std::size_t GetSizeInBytes() const { 23 std::size_t GetSizeInBytes() const override {
24 return size; 24 return size;
25 } 25 }
26 26
27 // We do not have to flush this cache as things in it are never modified by us.
28 void Flush() override {}
29
27 VAddr addr; 30 VAddr addr;
28 std::size_t size; 31 std::size_t size;
29 GLintptr offset; 32 GLintptr offset;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84582c777..468253033 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -286,7 +286,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
287 287
288 // Bind the buffer 288 // Bind the buffer
289 glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); 289 glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
290 offset, static_cast<GLsizeiptr>(sizeof(ubo)));
290 291
291 Shader shader{shader_cache.GetStageProgram(program)}; 292 Shader shader{shader_cache.GetStageProgram(program)};
292 293
@@ -423,6 +424,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
423 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer 424 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
424 Surface color_surface = 425 Surface color_surface =
425 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents); 426 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents);
427
428 if (color_surface) {
429 // Assume that a surface will be written to if it is used as a framebuffer, even if
430 // the shader doesn't actually write to it.
431 color_surface->MarkAsModified(true, res_cache);
432 }
433
426 glFramebufferTexture2D( 434 glFramebufferTexture2D(
427 GL_DRAW_FRAMEBUFFER, 435 GL_DRAW_FRAMEBUFFER,
428 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, 436 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D,
@@ -433,6 +441,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
433 std::array<GLenum, Maxwell::NumRenderTargets> buffers; 441 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
434 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 442 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
435 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); 443 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
444
445 if (color_surface) {
446 // Assume that a surface will be written to if it is used as a framebuffer, even
447 // if the shader doesn't actually write to it.
448 color_surface->MarkAsModified(true, res_cache);
449 }
450
436 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 451 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
437 glFramebufferTexture2D( 452 glFramebufferTexture2D(
438 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), 453 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
@@ -452,6 +467,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
452 } 467 }
453 468
454 if (depth_surface) { 469 if (depth_surface) {
470 // Assume that a surface will be written to if it is used as a framebuffer, even if
471 // the shader doesn't actually write to it.
472 depth_surface->MarkAsModified(true, res_cache);
473
455 if (regs.stencil_enable) { 474 if (regs.stencil_enable) {
456 // Attach both depth and stencil 475 // Attach both depth and stencil
457 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 476 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
@@ -616,7 +635,14 @@ void RasterizerOpenGL::DrawArrays() {
616 635
617void RasterizerOpenGL::FlushAll() {} 636void RasterizerOpenGL::FlushAll() {}
618 637
619void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {} 638void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
639 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
640
641 if (Settings::values.use_accurate_gpu_emulation) {
642 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
643 res_cache.FlushRegion(addr, size);
644 }
645}
620 646
621void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 647void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
622 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 648 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -626,6 +652,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
626} 652}
627 653
628void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 654void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
655 FlushRegion(addr, size);
629 InvalidateRegion(addr, size); 656 InvalidateRegion(addr, size);
630} 657}
631 658
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 65a220c41..1cb77aaf2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -34,16 +34,53 @@ struct FormatTuple {
34 bool compressed; 34 bool compressed;
35}; 35};
36 36
37static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { 37static bool IsPixelFormatASTC(PixelFormat format) {
38 auto& gpu{Core::System::GetInstance().GPU()}; 38 switch (format) {
39 const auto cpu_addr{gpu.MemoryManager().GpuToCpuAddress(gpu_addr)}; 39 case PixelFormat::ASTC_2D_4X4:
40 return cpu_addr ? *cpu_addr : 0; 40 case PixelFormat::ASTC_2D_5X4:
41 case PixelFormat::ASTC_2D_8X8:
42 case PixelFormat::ASTC_2D_8X5:
43 return true;
44 default:
45 return false;
46 }
47}
48
49static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
50 switch (format) {
51 case PixelFormat::ASTC_2D_4X4:
52 return {4, 4};
53 case PixelFormat::ASTC_2D_5X4:
54 return {5, 4};
55 case PixelFormat::ASTC_2D_8X8:
56 return {8, 8};
57 case PixelFormat::ASTC_2D_8X5:
58 return {8, 5};
59 default:
60 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
61 UNREACHABLE();
62 }
63}
64
65void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
66 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
67 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
68
69 addr = cpu_addr ? *cpu_addr : 0;
70 gpu_addr = gpu_addr_;
71 size_in_bytes = SizeInBytesRaw();
72
73 if (IsPixelFormatASTC(pixel_format)) {
74 // ASTC is uncompressed in software, in emulated as RGBA8
75 size_in_bytes_gl = width * height * depth * 4;
76 } else {
77 size_in_bytes_gl = SizeInBytesGL();
78 }
41} 79}
42 80
43/*static*/ SurfaceParams SurfaceParams::CreateForTexture( 81/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
44 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { 82 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
45 SurfaceParams params{}; 83 SurfaceParams params{};
46 params.addr = TryGetCpuAddr(config.tic.Address());
47 params.is_tiled = config.tic.IsTiled(); 84 params.is_tiled = config.tic.IsTiled();
48 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, 85 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
49 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 86 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
@@ -87,18 +124,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
87 break; 124 break;
88 } 125 }
89 126
90 params.size_in_bytes_total = params.SizeInBytesTotal();
91 params.size_in_bytes_2d = params.SizeInBytes2D();
92 params.max_mip_level = config.tic.max_mip_level + 1; 127 params.max_mip_level = config.tic.max_mip_level + 1;
93 params.rt = {}; 128 params.rt = {};
94 129
130 params.InitCacheParameters(config.tic.Address());
131
95 return params; 132 return params;
96} 133}
97 134
98/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { 135/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {
99 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 136 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
100 SurfaceParams params{}; 137 SurfaceParams params{};
101 params.addr = TryGetCpuAddr(config.Address()); 138
102 params.is_tiled = 139 params.is_tiled =
103 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 140 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
104 params.block_width = 1 << config.memory_layout.block_width; 141 params.block_width = 1 << config.memory_layout.block_width;
@@ -112,8 +149,6 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
112 params.unaligned_height = config.height; 149 params.unaligned_height = config.height;
113 params.target = SurfaceTarget::Texture2D; 150 params.target = SurfaceTarget::Texture2D;
114 params.depth = 1; 151 params.depth = 1;
115 params.size_in_bytes_total = params.SizeInBytesTotal();
116 params.size_in_bytes_2d = params.SizeInBytes2D();
117 params.max_mip_level = 0; 152 params.max_mip_level = 0;
118 153
119 // Render target specific parameters, not used for caching 154 // Render target specific parameters, not used for caching
@@ -122,6 +157,8 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
122 params.rt.layer_stride = config.layer_stride; 157 params.rt.layer_stride = config.layer_stride;
123 params.rt.base_layer = config.base_layer; 158 params.rt.base_layer = config.base_layer;
124 159
160 params.InitCacheParameters(config.Address());
161
125 return params; 162 return params;
126} 163}
127 164
@@ -130,7 +167,7 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
130 u32 block_width, u32 block_height, u32 block_depth, 167 u32 block_width, u32 block_height, u32 block_depth,
131 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { 168 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
132 SurfaceParams params{}; 169 SurfaceParams params{};
133 params.addr = TryGetCpuAddr(zeta_address); 170
134 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 171 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
135 params.block_width = 1 << std::min(block_width, 5U); 172 params.block_width = 1 << std::min(block_width, 5U);
136 params.block_height = 1 << std::min(block_height, 5U); 173 params.block_height = 1 << std::min(block_height, 5U);
@@ -143,18 +180,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
143 params.unaligned_height = zeta_height; 180 params.unaligned_height = zeta_height;
144 params.target = SurfaceTarget::Texture2D; 181 params.target = SurfaceTarget::Texture2D;
145 params.depth = 1; 182 params.depth = 1;
146 params.size_in_bytes_total = params.SizeInBytesTotal();
147 params.size_in_bytes_2d = params.SizeInBytes2D();
148 params.max_mip_level = 0; 183 params.max_mip_level = 0;
149 params.rt = {}; 184 params.rt = {};
150 185
186 params.InitCacheParameters(zeta_address);
187
151 return params; 188 return params;
152} 189}
153 190
154/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface( 191/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
155 const Tegra::Engines::Fermi2D::Regs::Surface& config) { 192 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
156 SurfaceParams params{}; 193 SurfaceParams params{};
157 params.addr = TryGetCpuAddr(config.Address()); 194
158 params.is_tiled = !config.linear; 195 params.is_tiled = !config.linear;
159 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, 196 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
160 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, 197 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
@@ -167,11 +204,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
167 params.unaligned_height = config.height; 204 params.unaligned_height = config.height;
168 params.target = SurfaceTarget::Texture2D; 205 params.target = SurfaceTarget::Texture2D;
169 params.depth = 1; 206 params.depth = 1;
170 params.size_in_bytes_total = params.SizeInBytesTotal();
171 params.size_in_bytes_2d = params.SizeInBytes2D();
172 params.max_mip_level = 0; 207 params.max_mip_level = 0;
173 params.rt = {}; 208 params.rt = {};
174 209
210 params.InitCacheParameters(config.Address());
211
175 return params; 212 return params;
176} 213}
177 214
@@ -231,6 +268,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
231 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 268 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
232 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 269 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
233 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 270 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
271 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
272 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
234 273
235 // Depth formats 274 // Depth formats
236 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 275 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -274,28 +313,6 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
274 return format; 313 return format;
275} 314}
276 315
277static bool IsPixelFormatASTC(PixelFormat format) {
278 switch (format) {
279 case PixelFormat::ASTC_2D_4X4:
280 case PixelFormat::ASTC_2D_8X8:
281 return true;
282 default:
283 return false;
284 }
285}
286
287static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
288 switch (format) {
289 case PixelFormat::ASTC_2D_4X4:
290 return {4, 4};
291 case PixelFormat::ASTC_2D_8X8:
292 return {8, 8};
293 default:
294 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
295 UNREACHABLE();
296 }
297}
298
299MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { 316MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
300 u32 actual_height{unaligned_height}; 317 u32 actual_height{unaligned_height};
301 if (IsPixelFormatASTC(pixel_format)) { 318 if (IsPixelFormatASTC(pixel_format)) {
@@ -323,29 +340,27 @@ static bool IsFormatBCn(PixelFormat format) {
323} 340}
324 341
325template <bool morton_to_gl, PixelFormat format> 342template <bool morton_to_gl, PixelFormat format>
326void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, 343void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
327 VAddr addr) { 344 std::size_t gl_buffer_size, VAddr addr) {
328 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 345 constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format);
329 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 346
347 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
348 // pixel values.
349 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
330 350
331 if (morton_to_gl) { 351 if (morton_to_gl) {
332 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
333 // pixel values.
334 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
335 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 352 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
336 addr, tile_size, bytes_per_pixel, stride, height, block_height); 353 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
337 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 354 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
338 memcpy(gl_buffer, data.data(), size_to_copy); 355 memcpy(gl_buffer, data.data(), size_to_copy);
339 } else { 356 } else {
340 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 357 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth,
341 // check the configuration for this and perform more generic un/swizzle 358 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
342 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 359 gl_buffer, false, block_height, block_depth);
343 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
344 Memory::GetPointer(addr), gl_buffer, morton_to_gl);
345 } 360 }
346} 361}
347 362
348static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 363static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
349 SurfaceParams::MaxPixelFormat> 364 SurfaceParams::MaxPixelFormat>
350 morton_to_gl_fns = { 365 morton_to_gl_fns = {
351 // clang-format off 366 // clang-format off
@@ -395,6 +410,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
395 MortonCopy<true, PixelFormat::RG32UI>, 410 MortonCopy<true, PixelFormat::RG32UI>,
396 MortonCopy<true, PixelFormat::R32UI>, 411 MortonCopy<true, PixelFormat::R32UI>,
397 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 412 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
413 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
414 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
398 MortonCopy<true, PixelFormat::Z32F>, 415 MortonCopy<true, PixelFormat::Z32F>,
399 MortonCopy<true, PixelFormat::Z16>, 416 MortonCopy<true, PixelFormat::Z16>,
400 MortonCopy<true, PixelFormat::Z24S8>, 417 MortonCopy<true, PixelFormat::Z24S8>,
@@ -403,7 +420,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
403 // clang-format on 420 // clang-format on
404}; 421};
405 422
406static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 423static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
407 SurfaceParams::MaxPixelFormat> 424 SurfaceParams::MaxPixelFormat>
408 gl_to_morton_fns = { 425 gl_to_morton_fns = {
409 // clang-format off 426 // clang-format off
@@ -420,17 +437,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
420 MortonCopy<false, PixelFormat::RGBA16UI>, 437 MortonCopy<false, PixelFormat::RGBA16UI>,
421 MortonCopy<false, PixelFormat::R11FG11FB10F>, 438 MortonCopy<false, PixelFormat::R11FG11FB10F>,
422 MortonCopy<false, PixelFormat::RGBA32UI>, 439 MortonCopy<false, PixelFormat::RGBA32UI>,
423 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4 440 MortonCopy<false, PixelFormat::DXT1>,
424 // formats are not supported 441 MortonCopy<false, PixelFormat::DXT23>,
425 nullptr, 442 MortonCopy<false, PixelFormat::DXT45>,
426 nullptr, 443 MortonCopy<false, PixelFormat::DXN1>,
427 nullptr, 444 MortonCopy<false, PixelFormat::DXN2UNORM>,
428 nullptr, 445 MortonCopy<false, PixelFormat::DXN2SNORM>,
429 nullptr, 446 MortonCopy<false, PixelFormat::BC7U>,
430 nullptr, 447 MortonCopy<false, PixelFormat::BC6H_UF16>,
431 nullptr, 448 MortonCopy<false, PixelFormat::BC6H_SF16>,
432 nullptr, 449 // TODO(Subv): Swizzling ASTC formats are not supported
433 nullptr,
434 nullptr, 450 nullptr,
435 MortonCopy<false, PixelFormat::G8R8U>, 451 MortonCopy<false, PixelFormat::G8R8U>,
436 MortonCopy<false, PixelFormat::G8R8S>, 452 MortonCopy<false, PixelFormat::G8R8S>,
@@ -455,6 +471,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
455 MortonCopy<false, PixelFormat::RG32UI>, 471 MortonCopy<false, PixelFormat::RG32UI>,
456 MortonCopy<false, PixelFormat::R32UI>, 472 MortonCopy<false, PixelFormat::R32UI>,
457 nullptr, 473 nullptr,
474 nullptr,
475 nullptr,
458 MortonCopy<false, PixelFormat::Z32F>, 476 MortonCopy<false, PixelFormat::Z32F>,
459 MortonCopy<false, PixelFormat::Z16>, 477 MortonCopy<false, PixelFormat::Z16>,
460 MortonCopy<false, PixelFormat::Z24S8>, 478 MortonCopy<false, PixelFormat::Z24S8>,
@@ -614,22 +632,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
614 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 632 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
615 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 633 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
616 634
617 std::size_t buffer_size = 635 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
618 std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total);
619 636
620 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 637 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
621 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 638 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
622 if (source_format.compressed) { 639 if (source_format.compressed) {
623 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 640 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
624 static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr); 641 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
625 } else { 642 } else {
626 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format, 643 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format,
627 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total), 644 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes),
628 nullptr); 645 nullptr);
629 } 646 }
630 // If the new texture is bigger than the previous one, we need to fill in the rest with data 647 // If the new texture is bigger than the previous one, we need to fill in the rest with data
631 // from the CPU. 648 // from the CPU.
632 if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) { 649 if (src_params.size_in_bytes < dst_params.size_in_bytes) {
633 // Upload the rest of the memory. 650 // Upload the rest of the memory.
634 if (dst_params.is_tiled) { 651 if (dst_params.is_tiled) {
635 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest 652 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
@@ -639,12 +656,12 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
639 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 656 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
640 "reinterpretation but the texture is tiled."); 657 "reinterpretation but the texture is tiled.");
641 } 658 }
642 std::size_t remaining_size = 659 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
643 dst_params.size_in_bytes_total - src_params.size_in_bytes_total;
644 std::vector<u8> data(remaining_size); 660 std::vector<u8> data(remaining_size);
645 Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(), 661 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
646 data.size()); 662 data.size());
647 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size, 663
664 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
648 data.data()); 665 data.data());
649 } 666 }
650 667
@@ -690,7 +707,8 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
690} 707}
691 708
692CachedSurface::CachedSurface(const SurfaceParams& params) 709CachedSurface::CachedSurface(const SurfaceParams& params)
693 : params(params), gl_target(SurfaceTargetToGL(params.target)) { 710 : params(params), gl_target(SurfaceTargetToGL(params.target)),
711 cached_size_in_bytes(params.size_in_bytes) {
694 texture.Create(); 712 texture.Create();
695 const auto& rect{params.GetRect()}; 713 const auto& rect{params.GetRect()};
696 714
@@ -740,9 +758,21 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
740 758
741 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, 759 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
742 SurfaceParams::SurfaceTargetName(params.target)); 760 SurfaceParams::SurfaceTargetName(params.target));
761
762 // Clamp size to mapped GPU memory region
763 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
764 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
765 // check is necessary to prevent flushing from overwriting unmapped memory.
766
767 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
768 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
769 if (cached_size_in_bytes > max_size) {
770 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
771 cached_size_in_bytes = max_size;
772 }
743} 773}
744 774
745static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 775static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
746 union S8Z24 { 776 union S8Z24 {
747 BitField<0, 24, u32> z24; 777 BitField<0, 24, u32> z24;
748 BitField<24, 8, u32> s8; 778 BitField<24, 8, u32> s8;
@@ -755,22 +785,29 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
755 }; 785 };
756 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); 786 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
757 787
758 S8Z24 input_pixel{}; 788 S8Z24 s8z24_pixel{};
759 Z24S8 output_pixel{}; 789 Z24S8 z24s8_pixel{};
760 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; 790 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)};
761 for (std::size_t y = 0; y < height; ++y) { 791 for (std::size_t y = 0; y < height; ++y) {
762 for (std::size_t x = 0; x < width; ++x) { 792 for (std::size_t x = 0; x < width; ++x) {
763 const std::size_t offset{bpp * (y * width + x)}; 793 const std::size_t offset{bpp * (y * width + x)};
764 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 794 if (reverse) {
765 output_pixel.s8.Assign(input_pixel.s8); 795 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
766 output_pixel.z24.Assign(input_pixel.z24); 796 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
767 std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); 797 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
798 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
799 } else {
800 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
801 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
802 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
803 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
804 }
768 } 805 }
769 } 806 }
770} 807}
771 808
772static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 809static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
773 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; 810 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)};
774 for (std::size_t y = 0; y < height; ++y) { 811 for (std::size_t y = 0; y < height; ++y) {
775 for (std::size_t x = 0; x < width; ++x) { 812 for (std::size_t x = 0; x < width; ++x) {
776 const std::size_t offset{bpp * (y * width + x)}; 813 const std::size_t offset{bpp * (y * width + x)};
@@ -790,7 +827,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
790 u32 width, u32 height) { 827 u32 width, u32 height) {
791 switch (pixel_format) { 828 switch (pixel_format) {
792 case PixelFormat::ASTC_2D_4X4: 829 case PixelFormat::ASTC_2D_4X4:
793 case PixelFormat::ASTC_2D_8X8: { 830 case PixelFormat::ASTC_2D_8X8:
831 case PixelFormat::ASTC_2D_8X5:
832 case PixelFormat::ASTC_2D_5X4: {
794 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 833 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
795 u32 block_width{}; 834 u32 block_width{};
796 u32 block_height{}; 835 u32 block_height{};
@@ -800,7 +839,7 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
800 } 839 }
801 case PixelFormat::S8Z24: 840 case PixelFormat::S8Z24:
802 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. 841 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
803 ConvertS8Z24ToZ24S8(data, width, height); 842 ConvertS8Z24ToZ24S8(data, width, height, false);
804 break; 843 break;
805 844
806 case PixelFormat::G8R8U: 845 case PixelFormat::G8R8U:
@@ -811,54 +850,54 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
811 } 850 }
812} 851}
813 852
853/**
854 * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
855 * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
856 * with typical desktop GPUs.
857 */
858static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
859 u32 width, u32 height) {
860 switch (pixel_format) {
861 case PixelFormat::G8R8U:
862 case PixelFormat::G8R8S:
863 case PixelFormat::ASTC_2D_4X4:
864 case PixelFormat::ASTC_2D_8X8: {
865 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
866 static_cast<u32>(pixel_format));
867 UNREACHABLE();
868 break;
869 }
870 case PixelFormat::S8Z24:
871 // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
872 ConvertS8Z24ToZ24S8(data, width, height, true);
873 break;
874 }
875}
876
814MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 877MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
815void CachedSurface::LoadGLBuffer() { 878void CachedSurface::LoadGLBuffer() {
816 ASSERT(params.type != SurfaceType::Fill);
817
818 const u8* const texture_src_data = Memory::GetPointer(params.addr);
819
820 ASSERT(texture_src_data);
821
822 const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
823 const u32 copy_size = params.width * params.height * bytes_per_pixel;
824 const std::size_t total_size = copy_size * params.depth;
825
826 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 879 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
827 880
881 gl_buffer.resize(params.size_in_bytes_gl);
828 if (params.is_tiled) { 882 if (params.is_tiled) {
829 gl_buffer.resize(total_size); 883 u32 depth = params.depth;
884 u32 block_depth = params.block_depth;
830 885
831 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 886 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
832 params.block_width, static_cast<u32>(params.target)); 887 params.block_width, static_cast<u32>(params.target));
833 ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}",
834 params.block_depth, static_cast<u32>(params.target));
835 888
836 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do 889 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
837 // this for 3D textures, etc. 890 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
838 switch (params.target) { 891 depth = 1U;
839 case SurfaceParams::SurfaceTarget::Texture2D: 892 block_depth = 1U;
840 // Pass impl. to the fallback code below
841 break;
842 case SurfaceParams::SurfaceTarget::Texture2DArray:
843 case SurfaceParams::SurfaceTarget::TextureCubemap:
844 for (std::size_t index = 0; index < params.depth; ++index) {
845 const std::size_t offset{index * copy_size};
846 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
847 params.width, params.block_height, params.height, gl_buffer.data() + offset,
848 copy_size, params.addr + offset);
849 }
850 break;
851 default:
852 LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
853 static_cast<u32>(params.target));
854 UNREACHABLE();
855 } 893 }
856 894
857 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( 895 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
858 params.width, params.block_height, params.height, gl_buffer.data(), copy_size, 896 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
859 params.addr); 897 gl_buffer.size(), params.addr);
860 } else { 898 } else {
861 const u8* const texture_src_data_end{texture_src_data + total_size}; 899 const auto texture_src_data{Memory::GetPointer(params.addr)};
900 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
862 gl_buffer.assign(texture_src_data, texture_src_data_end); 901 gl_buffer.assign(texture_src_data, texture_src_data_end);
863 } 902 }
864 903
@@ -867,7 +906,44 @@ void CachedSurface::LoadGLBuffer() {
867 906
868MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 907MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
869void CachedSurface::FlushGLBuffer() { 908void CachedSurface::FlushGLBuffer() {
870 ASSERT_MSG(false, "Unimplemented"); 909 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
910
911 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
912
913 // OpenGL temporary buffer needs to be big enough to store raw texture size
914 gl_buffer.resize(GetSizeInBytes());
915
916 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
917 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
918 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
919 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
920 ASSERT(!tuple.compressed);
921 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
922 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(),
923 gl_buffer.data());
924 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
925 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
926 params.height);
927 ASSERT(params.type != SurfaceType::Fill);
928 const u8* const texture_src_data = Memory::GetPointer(params.addr);
929 ASSERT(texture_src_data);
930 if (params.is_tiled) {
931 u32 depth = params.depth;
932 u32 block_depth = params.block_depth;
933
934 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
935 params.block_width, static_cast<u32>(params.target));
936
937 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
938 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
939 depth = 1U;
940 }
941 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
942 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
943 gl_buffer.size(), GetAddr());
944 } else {
945 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
946 }
871} 947}
872 948
873MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 949MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -877,9 +953,6 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
877 953
878 MICROPROFILE_SCOPE(OpenGL_TextureUL); 954 MICROPROFILE_SCOPE(OpenGL_TextureUL);
879 955
880 ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height *
881 GetGLBytesPerPixel(params.pixel_format) * params.depth);
882
883 const auto& rect{params.GetRect()}; 956 const auto& rect{params.GetRect()};
884 957
885 // Load data from memory to the surface 958 // Load data from memory to the surface
@@ -888,7 +961,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
888 std::size_t buffer_offset = 961 std::size_t buffer_offset =
889 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + 962 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +
890 static_cast<std::size_t>(x0)) * 963 static_cast<std::size_t>(x0)) *
891 GetGLBytesPerPixel(params.pixel_format); 964 SurfaceParams::GetBytesPerPixel(params.pixel_format);
892 965
893 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 966 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
894 const GLuint target_tex = texture.handle; 967 const GLuint target_tex = texture.handle;
@@ -904,7 +977,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
904 cur_state.Apply(); 977 cur_state.Apply();
905 978
906 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 979 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
907 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); 980 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
908 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 981 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width));
909 982
910 glActiveTexture(GL_TEXTURE0); 983 glActiveTexture(GL_TEXTURE0);
@@ -914,7 +987,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
914 glCompressedTexImage2D( 987 glCompressedTexImage2D(
915 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 988 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
916 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, 989 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
917 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 990 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
918 break; 991 break;
919 case SurfaceParams::SurfaceTarget::Texture3D: 992 case SurfaceParams::SurfaceTarget::Texture3D:
920 case SurfaceParams::SurfaceTarget::Texture2DArray: 993 case SurfaceParams::SurfaceTarget::Texture2DArray:
@@ -922,16 +995,16 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
922 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 995 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
923 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 996 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
924 static_cast<GLsizei>(params.depth), 0, 997 static_cast<GLsizei>(params.depth), 0,
925 static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]); 998 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
926 break; 999 break;
927 case SurfaceParams::SurfaceTarget::TextureCubemap: 1000 case SurfaceParams::SurfaceTarget::TextureCubemap:
928 for (std::size_t face = 0; face < params.depth; ++face) { 1001 for (std::size_t face = 0; face < params.depth; ++face) {
929 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 1002 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
930 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1003 0, tuple.internal_format, static_cast<GLsizei>(params.width),
931 static_cast<GLsizei>(params.height), 0, 1004 static_cast<GLsizei>(params.height), 0,
932 static_cast<GLsizei>(params.size_in_bytes_2d), 1005 static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()),
933 &gl_buffer[buffer_offset]); 1006 &gl_buffer[buffer_offset]);
934 buffer_offset += params.size_in_bytes_2d; 1007 buffer_offset += params.SizeInBytesCubeFace();
935 } 1008 }
936 break; 1009 break;
937 default: 1010 default:
@@ -941,7 +1014,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
941 glCompressedTexImage2D( 1014 glCompressedTexImage2D(
942 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1015 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
943 static_cast<GLsizei>(params.height), 0, 1016 static_cast<GLsizei>(params.height), 0,
944 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 1017 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
945 } 1018 }
946 } else { 1019 } else {
947 1020
@@ -970,7 +1043,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
970 y0, static_cast<GLsizei>(rect.GetWidth()), 1043 y0, static_cast<GLsizei>(rect.GetWidth()),
971 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1044 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
972 &gl_buffer[buffer_offset]); 1045 &gl_buffer[buffer_offset]);
973 buffer_offset += params.size_in_bytes_2d; 1046 buffer_offset += params.SizeInBytesCubeFace();
974 } 1047 }
975 break; 1048 break;
976 default: 1049 default:
@@ -1032,10 +1105,7 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre
1032void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { 1105void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
1033 surface->LoadGLBuffer(); 1106 surface->LoadGLBuffer();
1034 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); 1107 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
1035} 1108 surface->MarkAsModified(false, *this);
1036
1037void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
1038 surface->FlushGLBuffer();
1039} 1109}
1040 1110
1041Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 1111Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
@@ -1104,6 +1174,14 @@ void RasterizerCacheOpenGL::FermiCopySurface(
1104 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false)); 1174 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
1105} 1175}
1106 1176
1177void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1178 const Surface& dst_surface) {
1179 const auto& src_params{src_surface->GetSurfaceParams()};
1180 const auto& dst_params{dst_surface->GetSurfaceParams()};
1181 FlushRegion(src_params.addr, dst_params.size_in_bytes);
1182 LoadSurface(dst_surface);
1183}
1184
1107Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, 1185Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1108 const SurfaceParams& new_params) { 1186 const SurfaceParams& new_params) {
1109 // Verify surface is compatible for blitting 1187 // Verify surface is compatible for blitting
@@ -1112,6 +1190,12 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1112 // Get a new surface with the new parameters, and blit the previous surface to it 1190 // Get a new surface with the new parameters, and blit the previous surface to it
1113 Surface new_surface{GetUncachedSurface(new_params)}; 1191 Surface new_surface{GetUncachedSurface(new_params)};
1114 1192
1193 // With use_accurate_gpu_emulation enabled, do an accurate surface copy
1194 if (Settings::values.use_accurate_gpu_emulation) {
1195 AccurateCopySurface(old_surface, new_surface);
1196 return new_surface;
1197 }
1198
1115 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1199 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1116 if (old_params.target == new_params.target && old_params.type == new_params.type && 1200 if (old_params.target == new_params.target && old_params.type == new_params.type &&
1117 old_params.depth == new_params.depth && old_params.depth == 1 && 1201 old_params.depth == new_params.depth && old_params.depth == 1 &&
@@ -1123,11 +1207,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1123 1207
1124 // If the format is the same, just do a framebuffer blit. This is significantly faster than 1208 // If the format is the same, just do a framebuffer blit. This is significantly faster than
1125 // using PBOs. The is also likely less accurate, as textures will be converted rather than 1209 // using PBOs. The is also likely less accurate, as textures will be converted rather than
1126 // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate 1210 // reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate
1127 // surface copy, where pixels are reinterpreted as a new format (without conversion). This 1211 // surface copy, where pixels are reinterpreted as a new format (without conversion). This
1128 // code path uses OpenGL PBOs and is quite slow. 1212 // code path uses OpenGL PBOs and is quite slow.
1129 const bool is_blit{old_params.pixel_format == new_params.pixel_format || 1213 const bool is_blit{old_params.pixel_format == new_params.pixel_format};
1130 !Settings::values.use_accurate_framebuffers};
1131 1214
1132 switch (new_params.target) { 1215 switch (new_params.target) {
1133 case SurfaceParams::SurfaceTarget::Texture2D: 1216 case SurfaceParams::SurfaceTarget::Texture2D:
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 66d98ad4e..7c1cb72d0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -18,6 +18,7 @@
18#include "video_core/rasterizer_cache.h" 18#include "video_core/rasterizer_cache.h"
19#include "video_core/renderer_opengl/gl_resource_manager.h" 19#include "video_core/renderer_opengl/gl_resource_manager.h"
20#include "video_core/renderer_opengl/gl_shader_gen.h" 20#include "video_core/renderer_opengl/gl_shader_gen.h"
21#include "video_core/textures/decoders.h"
21#include "video_core/textures/texture.h" 22#include "video_core/textures/texture.h"
22 23
23namespace OpenGL { 24namespace OpenGL {
@@ -74,19 +75,21 @@ struct SurfaceParams {
74 RG32UI = 43, 75 RG32UI = 43,
75 R32UI = 44, 76 R32UI = 44,
76 ASTC_2D_8X8 = 45, 77 ASTC_2D_8X8 = 45,
78 ASTC_2D_8X5 = 46,
79 ASTC_2D_5X4 = 47,
77 80
78 MaxColorFormat, 81 MaxColorFormat,
79 82
80 // Depth formats 83 // Depth formats
81 Z32F = 46, 84 Z32F = 48,
82 Z16 = 47, 85 Z16 = 49,
83 86
84 MaxDepthFormat, 87 MaxDepthFormat,
85 88
86 // DepthStencil formats 89 // DepthStencil formats
87 Z24S8 = 48, 90 Z24S8 = 50,
88 S8Z24 = 49, 91 S8Z24 = 51,
89 Z32FS8 = 50, 92 Z32FS8 = 52,
90 93
91 MaxDepthStencilFormat, 94 MaxDepthStencilFormat,
92 95
@@ -220,6 +223,8 @@ struct SurfaceParams {
220 1, // RG32UI 223 1, // RG32UI
221 1, // R32UI 224 1, // R32UI
222 4, // ASTC_2D_8X8 225 4, // ASTC_2D_8X8
226 4, // ASTC_2D_8X5
227 4, // ASTC_2D_5X4
223 1, // Z32F 228 1, // Z32F
224 1, // Z16 229 1, // Z16
225 1, // Z24S8 230 1, // Z24S8
@@ -282,6 +287,8 @@ struct SurfaceParams {
282 64, // RG32UI 287 64, // RG32UI
283 32, // R32UI 288 32, // R32UI
284 16, // ASTC_2D_8X8 289 16, // ASTC_2D_8X8
290 32, // ASTC_2D_8X5
291 32, // ASTC_2D_5X4
285 32, // Z32F 292 32, // Z32F
286 16, // Z16 293 16, // Z16
287 32, // Z24S8 294 32, // Z24S8
@@ -553,8 +560,12 @@ struct SurfaceParams {
553 return PixelFormat::BC6H_SF16; 560 return PixelFormat::BC6H_SF16;
554 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 561 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
555 return PixelFormat::ASTC_2D_4X4; 562 return PixelFormat::ASTC_2D_4X4;
563 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
564 return PixelFormat::ASTC_2D_5X4;
556 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 565 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
557 return PixelFormat::ASTC_2D_8X8; 566 return PixelFormat::ASTC_2D_8X8;
567 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
568 return PixelFormat::ASTC_2D_8X5;
558 case Tegra::Texture::TextureFormat::R16_G16: 569 case Tegra::Texture::TextureFormat::R16_G16:
559 switch (component_type) { 570 switch (component_type) {
560 case Tegra::Texture::ComponentType::FLOAT: 571 case Tegra::Texture::ComponentType::FLOAT:
@@ -691,21 +702,42 @@ struct SurfaceParams {
691 return SurfaceType::Invalid; 702 return SurfaceType::Invalid;
692 } 703 }
693 704
705 /// Returns the sizer in bytes of the specified pixel format
706 static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
707 if (pixel_format == SurfaceParams::PixelFormat::Invalid) {
708 return 0;
709 }
710 return GetFormatBpp(pixel_format) / CHAR_BIT;
711 }
712
694 /// Returns the rectangle corresponding to this surface 713 /// Returns the rectangle corresponding to this surface
695 MathUtil::Rectangle<u32> GetRect() const; 714 MathUtil::Rectangle<u32> GetRect() const;
696 715
697 /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression 716 /// Returns the total size of this surface in bytes, adjusted for compression
698 std::size_t SizeInBytes2D() const { 717 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {
699 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 718 const u32 compression_factor{GetCompressionFactor(pixel_format)};
700 ASSERT(width % compression_factor == 0); 719 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
701 ASSERT(height % compression_factor == 0); 720 const size_t uncompressed_size{
702 return (width / compression_factor) * (height / compression_factor) * 721 Tegra::Texture::CalculateSize((ignore_tiled ? false : is_tiled), bytes_per_pixel, width,
703 GetFormatBpp(pixel_format) / CHAR_BIT; 722 height, depth, block_height, block_depth)};
723
724 // Divide by compression_factor^2, as height and width are factored by this
725 return uncompressed_size / (compression_factor * compression_factor);
704 } 726 }
705 727
706 /// Returns the total size of this surface in bytes, adjusted for compression 728 /// Returns the size of this surface as an OpenGL texture in bytes
707 std::size_t SizeInBytesTotal() const { 729 std::size_t SizeInBytesGL() const {
708 return SizeInBytes2D() * depth; 730 return SizeInBytesRaw(true);
731 }
732
733 /// Returns the size of this surface as a cube face in bytes
734 std::size_t SizeInBytesCubeFace() const {
735 return size_in_bytes / 6;
736 }
737
738 /// Returns the size of this surface as an OpenGL cube face in bytes
739 std::size_t SizeInBytesCubeFaceGL() const {
740 return size_in_bytes_gl / 6;
709 } 741 }
710 742
711 /// Creates SurfaceParams from a texture configuration 743 /// Creates SurfaceParams from a texture configuration
@@ -732,7 +764,9 @@ struct SurfaceParams {
732 other.depth); 764 other.depth);
733 } 765 }
734 766
735 VAddr addr; 767 /// Initializes parameters for caching, should be called after everything has been initialized
768 void InitCacheParameters(Tegra::GPUVAddr gpu_addr);
769
736 bool is_tiled; 770 bool is_tiled;
737 u32 block_width; 771 u32 block_width;
738 u32 block_height; 772 u32 block_height;
@@ -744,11 +778,15 @@ struct SurfaceParams {
744 u32 height; 778 u32 height;
745 u32 depth; 779 u32 depth;
746 u32 unaligned_height; 780 u32 unaligned_height;
747 std::size_t size_in_bytes_total;
748 std::size_t size_in_bytes_2d;
749 SurfaceTarget target; 781 SurfaceTarget target;
750 u32 max_mip_level; 782 u32 max_mip_level;
751 783
784 // Parameters used for caching
785 VAddr addr;
786 Tegra::GPUVAddr gpu_addr;
787 std::size_t size_in_bytes;
788 std::size_t size_in_bytes_gl;
789
752 // Render target specific parameters, not used in caching 790 // Render target specific parameters, not used in caching
753 struct { 791 struct {
754 u32 index; 792 u32 index;
@@ -765,7 +803,8 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
765 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) { 803 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
766 SurfaceReserveKey res; 804 SurfaceReserveKey res;
767 res.state = params; 805 res.state = params;
768 res.state.rt = {}; // Ignore rt config in caching 806 res.state.gpu_addr = {}; // Ignore GPU vaddr in caching
807 res.state.rt = {}; // Ignore rt config in caching
769 return res; 808 return res;
770 } 809 }
771}; 810};
@@ -780,16 +819,20 @@ struct hash<SurfaceReserveKey> {
780 819
781namespace OpenGL { 820namespace OpenGL {
782 821
783class CachedSurface final { 822class CachedSurface final : public RasterizerCacheObject {
784public: 823public:
785 CachedSurface(const SurfaceParams& params); 824 CachedSurface(const SurfaceParams& params);
786 825
787 VAddr GetAddr() const { 826 VAddr GetAddr() const override {
788 return params.addr; 827 return params.addr;
789 } 828 }
790 829
791 std::size_t GetSizeInBytes() const { 830 std::size_t GetSizeInBytes() const override {
792 return params.size_in_bytes_total; 831 return cached_size_in_bytes;
832 }
833
834 void Flush() override {
835 FlushGLBuffer();
793 } 836 }
794 837
795 const OGLTexture& Texture() const { 838 const OGLTexture& Texture() const {
@@ -800,13 +843,6 @@ public:
800 return gl_target; 843 return gl_target;
801 } 844 }
802 845
803 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
804 if (format == SurfaceParams::PixelFormat::Invalid)
805 return 0;
806
807 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
808 }
809
810 const SurfaceParams& GetSurfaceParams() const { 846 const SurfaceParams& GetSurfaceParams() const {
811 return params; 847 return params;
812 } 848 }
@@ -823,6 +859,7 @@ private:
823 std::vector<u8> gl_buffer; 859 std::vector<u8> gl_buffer;
824 SurfaceParams params; 860 SurfaceParams params;
825 GLenum gl_target; 861 GLenum gl_target;
862 std::size_t cached_size_in_bytes;
826}; 863};
827 864
828class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 865class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -839,9 +876,6 @@ public:
839 /// Get the color surface based on the framebuffer configuration and the specified render target 876 /// Get the color surface based on the framebuffer configuration and the specified render target
840 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 877 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
841 878
842 /// Flushes the surface to Switch memory
843 void FlushSurface(const Surface& surface);
844
845 /// Tries to find a framebuffer using on the provided CPU address 879 /// Tries to find a framebuffer using on the provided CPU address
846 Surface TryFindFramebufferSurface(VAddr addr) const; 880 Surface TryFindFramebufferSurface(VAddr addr) const;
847 881
@@ -865,6 +899,9 @@ private:
865 /// Tries to get a reserved surface for the specified parameters 899 /// Tries to get a reserved surface for the specified parameters
866 Surface TryGetReservedSurface(const SurfaceParams& params); 900 Surface TryGetReservedSurface(const SurfaceParams& params);
867 901
902 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
903 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
904
868 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 905 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
869 /// previously been used. This is to prevent surfaces from being constantly created and 906 /// previously been used. This is to prevent surfaces from being constantly created and
870 /// destroyed when used with different surface parameters. 907 /// destroyed when used with different surface parameters.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 7bb287f56..a210f1731 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -19,20 +19,21 @@ class CachedShader;
19using Shader = std::shared_ptr<CachedShader>; 19using Shader = std::shared_ptr<CachedShader>;
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 21
22class CachedShader final { 22class CachedShader final : public RasterizerCacheObject {
23public: 23public:
24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); 24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type);
25 25
26 /// Gets the address of the shader in guest memory, required for cache management 26 VAddr GetAddr() const override {
27 VAddr GetAddr() const {
28 return addr; 27 return addr;
29 } 28 }
30 29
31 /// Gets the size of the shader in guest memory, required for cache management 30 std::size_t GetSizeInBytes() const override {
32 std::size_t GetSizeInBytes() const {
33 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 31 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
34 } 32 }
35 33
34 // We do not have to flush this cache as things in it are never modified by us.
35 void Flush() override {}
36
36 /// Gets the shader entries for the shader 37 /// Gets the shader entries for the shader
37 const GLShader::ShaderEntries& GetShaderEntries() const { 38 const GLShader::ShaderEntries& GetShaderEntries() const {
38 return entries; 39 return entries;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 6220323a0..55c33c3a9 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1436,7 +1436,6 @@ private:
1436 1436
1437 break; 1437 break;
1438 } 1438 }
1439
1440 case OpCode::Type::Shift: { 1439 case OpCode::Type::Shift: {
1441 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1440 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1442 std::string op_b; 1441 std::string op_b;
@@ -1478,7 +1477,6 @@ private:
1478 } 1477 }
1479 break; 1478 break;
1480 } 1479 }
1481
1482 case OpCode::Type::ArithmeticIntegerImmediate: { 1480 case OpCode::Type::ArithmeticIntegerImmediate: {
1483 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1481 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1484 std::string op_b = std::to_string(instr.alu.imm20_32.Value()); 1482 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
@@ -2038,9 +2036,9 @@ private:
2038 break; 2036 break;
2039 } 2037 }
2040 case OpCode::Id::TEX: { 2038 case OpCode::Id::TEX: {
2041 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
2042 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; 2039 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
2043 std::string coord; 2040 std::string coord;
2041 const bool is_array = instr.tex.array != 0;
2044 2042
2045 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2043 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2046 "NODEP is not implemented"); 2044 "NODEP is not implemented");
@@ -2055,21 +2053,59 @@ private:
2055 2053
2056 switch (num_coordinates) { 2054 switch (num_coordinates) {
2057 case 1: { 2055 case 1: {
2058 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2056 if (is_array) {
2059 coord = "float coords = " + x + ';'; 2057 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2058 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2059 coord = "vec2 coords = vec2(" + x + ", " + index + ");";
2060 } else {
2061 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2062 coord = "float coords = " + x + ';';
2063 }
2060 break; 2064 break;
2061 } 2065 }
2062 case 2: { 2066 case 2: {
2063 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2067 if (is_array) {
2064 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2068 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2065 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2069 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2070 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2071 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
2072 } else {
2073 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2074 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2075 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2076 }
2066 break; 2077 break;
2067 } 2078 }
2068 case 3: { 2079 case 3: {
2069 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2080 if (depth_compare) {
2070 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2081 if (is_array) {
2071 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2082 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2072 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2083 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2084 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2085 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2086 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2087 ");";
2088 } else {
2089 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2090 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2091 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2092 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2093 }
2094 } else {
2095 if (is_array) {
2096 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2097 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2098 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2099 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3);
2100 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2101 ");";
2102 } else {
2103 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2104 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2105 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2106 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2107 }
2108 }
2073 break; 2109 break;
2074 } 2110 }
2075 default: 2111 default:
@@ -2088,7 +2124,7 @@ private:
2088 std::string op_c; 2124 std::string op_c;
2089 2125
2090 const std::string sampler = 2126 const std::string sampler =
2091 GetSampler(instr.sampler, texture_type, false, depth_compare); 2127 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
2092 // Add an extra scope and declare the texture coords inside to prevent 2128 // Add an extra scope and declare the texture coords inside to prevent
2093 // overwriting them in case they are used as outputs of the texs instruction. 2129 // overwriting them in case they are used as outputs of the texs instruction.
2094 2130
@@ -2108,10 +2144,13 @@ private:
2108 } 2144 }
2109 case Tegra::Shader::TextureProcessMode::LB: 2145 case Tegra::Shader::TextureProcessMode::LB:
2110 case Tegra::Shader::TextureProcessMode::LBA: { 2146 case Tegra::Shader::TextureProcessMode::LBA: {
2111 if (num_coordinates <= 2) { 2147 if (depth_compare) {
2112 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2148 if (is_array)
2149 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2150 else
2151 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2113 } else { 2152 } else {
2114 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2153 op_c = regs.GetRegisterAsFloat(instr.gpr20);
2115 } 2154 }
2116 // TODO: Figure if A suffix changes the equation at all. 2155 // TODO: Figure if A suffix changes the equation at all.
2117 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2156 texture = "texture(" + sampler + ", coords, " + op_c + ')';
@@ -2630,14 +2669,14 @@ private:
2630 const std::string pred = 2669 const std::string pred =
2631 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 2670 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2632 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 2671 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2633 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 2672 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2634 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 2673 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2635 SetPredicate(instr.csetp.pred3, 2674 SetPredicate(instr.csetp.pred3,
2636 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 2675 '(' + control_code + ") " + combiner + " (" + pred + ')');
2637 } 2676 }
2638 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2677 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2639 SetPredicate(instr.csetp.pred0, 2678 SetPredicate(instr.csetp.pred0,
2640 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 2679 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2641 } 2680 }
2642 break; 2681 break;
2643 } 2682 }
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 0d2456b56..18ab723f7 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -40,72 +40,146 @@ struct alignas(64) SwizzleTable {
40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
42 42
43static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 43/**
44 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
45 u32 block_height) { 45 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1.
47 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
52 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
46 std::array<u8*, 2> data_ptrs; 53 std::array<u8*, 2> data_ptrs;
47 const std::size_t stride = width * bytes_per_pixel; 54 u32 z_address = tile_offset;
48 const std::size_t gobs_in_x = 64; 55 const u32 gob_size_x = 64;
49 const std::size_t gobs_in_y = 8; 56 const u32 gob_size_y = 8;
50 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 57 const u32 gob_size_z = 1;
51 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 58 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
52 for (std::size_t y = 0; y < height; ++y) { 59 for (u32 z = z_start; z < z_end; z++) {
53 const std::size_t gob_y_address = 60 u32 y_address = z_address;
54 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 61 u32 pixel_base = layer_z * z + y_start * stride_x;
55 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 62 for (u32 y = y_start; y < y_end; y++) {
56 const auto& table = legacy_swizzle_table[y % gobs_in_y]; 63 const auto& table = legacy_swizzle_table[y % gob_size_y];
57 for (std::size_t x = 0; x < width; ++x) { 64 for (u32 x = x_start; x < x_end; x++) {
58 const std::size_t gob_address = 65 const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
59 gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; 66 const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
60 const std::size_t x2 = x * bytes_per_pixel; 67 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
61 const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; 68 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
62 const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; 69 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
63 70 }
64 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 71 pixel_base += stride_x;
65 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 72 if ((y + 1) % gob_size_y == 0)
66 73 y_address += gob_size;
67 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
68 } 74 }
75 z_address += xy_block_size;
69 } 76 }
70} 77}
71 78
72static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 79/**
73 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 80 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
74 u32 block_height) { 81 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1.
83 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
88 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
75 std::array<u8*, 2> data_ptrs; 89 std::array<u8*, 2> data_ptrs;
76 const std::size_t stride{width * bytes_per_pixel}; 90 u32 z_address = tile_offset;
77 const std::size_t gobs_in_x = 64; 91 const u32 x_startb = x_start * bytes_per_pixel;
78 const std::size_t gobs_in_y = 8; 92 const u32 x_endb = x_end * bytes_per_pixel;
79 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 93 const u32 copy_size = 16;
80 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 94 const u32 gob_size_x = 64;
81 const std::size_t copy_size{16}; 95 const u32 gob_size_y = 8;
82 for (std::size_t y = 0; y < height; ++y) { 96 const u32 gob_size_z = 1;
83 const std::size_t initial_gob = 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
84 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 98 for (u32 z = z_start; z < z_end; z++) {
85 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 99 u32 y_address = z_address;
86 const std::size_t pixel_base{y * width * out_bytes_per_pixel}; 100 u32 pixel_base = layer_z * z + y_start * stride_x;
87 const auto& table = fast_swizzle_table[y % gobs_in_y]; 101 for (u32 y = y_start; y < y_end; y++) {
88 for (std::size_t xb = 0; xb < stride; xb += copy_size) { 102 const auto& table = fast_swizzle_table[y % gob_size_y];
89 const std::size_t gob_address{initial_gob + 103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
90 (xb / gobs_in_x) * gobs_size * block_height}; 104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
91 const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; 105 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
92 const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 106 const u32 pixel_index{out_x + pixel_base};
93 const std::size_t pixel_index{out_x + pixel_base}; 107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
94 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
95 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
96 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); 110 }
111 pixel_base += stride_x;
112 if ((y + 1) % gob_size_y == 0)
113 y_address += gob_size;
114 }
115 z_address += xy_block_size;
116 }
117}
118
119/**
120 * This function unswizzles or swizzles a texture by mapping Linear to BlockLinear Textue.
121 * The body of this function takes care of splitting the swizzled texture into blocks,
122 * and managing the extents of it. Once all the parameters of a single block are obtained,
123 * the function calls 'ProcessBlock' to process that particular Block.
124 *
125 * Documentation for the memory layout and decoding can be found at:
126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
127 */
128template <bool fast>
129void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
130 const u32 height, const u32 depth, const u32 bytes_per_pixel,
131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x;
135 const u32 gob_x_bytes = 64;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
137 const u32 gob_elements_y = 8;
138 const u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z;
146 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
147 const u32 xy_block_size = gob_size * block_height;
148 const u32 block_size = xy_block_size * block_depth;
149 u32 tile_offset = 0;
150 for (u32 zb = 0; zb < blocks_on_z; zb++) {
151 const u32 z_start = zb * block_z_elements;
152 const u32 z_end = std::min(depth, z_start + block_z_elements);
153 for (u32 yb = 0; yb < blocks_on_y; yb++) {
154 const u32 y_start = yb * block_y_elements;
155 const u32 y_end = std::min(height, y_start + block_y_elements);
156 for (u32 xb = 0; xb < blocks_on_x; xb++) {
157 const u32 x_start = xb * block_x_elements;
158 const u32 x_end = std::min(width, x_start + block_x_elements);
159 if (fast) {
160 FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
161 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
162 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
163 } else {
164 PreciseProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
165 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
166 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
167 }
168 tile_offset += block_size;
169 }
97 } 170 }
98 } 171 }
99} 172}
100 173
101void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 174void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
102 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { 175 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
176 bool unswizzle, u32 block_height, u32 block_depth) {
103 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 177 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
104 FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 178 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
105 unswizzled_data, unswizzle, block_height); 179 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
106 } else { 180 } else {
107 LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 181 SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
108 unswizzled_data, unswizzle, block_height); 182 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
109 } 183 }
110} 184}
111 185
@@ -126,7 +200,9 @@ u32 BytesPerPixel(TextureFormat format) {
126 case TextureFormat::R32_G32_B32: 200 case TextureFormat::R32_G32_B32:
127 return 12; 201 return 12;
128 case TextureFormat::ASTC_2D_4X4: 202 case TextureFormat::ASTC_2D_4X4:
203 case TextureFormat::ASTC_2D_5X4:
129 case TextureFormat::ASTC_2D_8X8: 204 case TextureFormat::ASTC_2D_8X8:
205 case TextureFormat::ASTC_2D_8X5:
130 case TextureFormat::A8R8G8B8: 206 case TextureFormat::A8R8G8B8:
131 case TextureFormat::A2B10G10R10: 207 case TextureFormat::A2B10G10R10:
132 case TextureFormat::BF10GF11RF11: 208 case TextureFormat::BF10GF11RF11:
@@ -153,10 +229,11 @@ u32 BytesPerPixel(TextureFormat format) {
153} 229}
154 230
155std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 231std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
156 u32 height, u32 block_height) { 232 u32 height, u32 depth, u32 block_height, u32 block_depth) {
157 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
158 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, 234 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel,
159 Memory::GetPointer(address), unswizzled_data.data(), true, block_height); 235 Memory::GetPointer(address), unswizzled_data.data(), true, block_height,
236 block_depth);
160 return unswizzled_data; 237 return unswizzled_data;
161} 238}
162 239
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 234d250af..aaf316947 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -14,17 +14,14 @@ namespace Tegra::Texture {
14 * Unswizzles a swizzled texture without changing its format. 14 * Unswizzles a swizzled texture without changing its format.
15 */ 15 */
16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
17 u32 height, u32 block_height = TICEntry::DefaultBlockHeight); 17 u32 height, u32 depth,
18 18 u32 block_height = TICEntry::DefaultBlockHeight,
19/** 19 u32 block_depth = TICEntry::DefaultBlockHeight);
20 * Unswizzles a swizzled depth texture without changing its format.
21 */
22std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
23 u32 block_height = TICEntry::DefaultBlockHeight);
24 20
25/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary. 21/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary.
26void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 22void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
27 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height); 23 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
24 bool unswizzle, u32 block_height, u32 block_depth);
28 25
29/** 26/**
30 * Decodes an unswizzled texture into a A8R8G8B8 texture. 27 * Decodes an unswizzled texture into a A8R8G8B8 texture.
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 58d17abcb..5947bd2b9 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -141,6 +141,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
141 141
142struct TICEntry { 142struct TICEntry {
143 static constexpr u32 DefaultBlockHeight = 16; 143 static constexpr u32 DefaultBlockHeight = 16;
144 static constexpr u32 DefaultBlockDepth = 1;
144 145
145 union { 146 union {
146 u32 raw; 147 u32 raw;
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 033ea1ea4..0a8f2bd9e 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -2,96 +2,114 @@
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 <thread> 5#include <json.hpp>
6#include "common/assert.h"
7#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 8#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 9#include "web_service/web_backend.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
13TelemetryJson::TelemetryJson(const std::string& host, const std::string& username, 13struct TelemetryJson::Impl {
14 const std::string& token) 14 Impl(std::string host, std::string username, std::string token)
15 : host(std::move(host)), username(std::move(username)), token(std::move(token)) {} 15 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
16TelemetryJson::~TelemetryJson() = default;
17 16
18template <class T> 17 nlohmann::json& TopSection() {
19void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { 18 return sections[static_cast<u8>(Telemetry::FieldType::None)];
20 sections[static_cast<u8>(type)][name] = value; 19 }
21}
22 20
23void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { 21 const nlohmann::json& TopSection() const {
24 TopSection()[name] = sections[static_cast<unsigned>(type)]; 22 return sections[static_cast<u8>(Telemetry::FieldType::None)];
25} 23 }
24
25 template <class T>
26 void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
27 sections[static_cast<u8>(type)][name] = value;
28 }
29
30 void SerializeSection(Telemetry::FieldType type, const std::string& name) {
31 TopSection()[name] = sections[static_cast<unsigned>(type)];
32 }
33
34 nlohmann::json output;
35 std::array<nlohmann::json, 7> sections;
36 std::string host;
37 std::string username;
38 std::string token;
39};
40
41TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
42 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
43TelemetryJson::~TelemetryJson() = default;
26 44
27void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { 45void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
28 Serialize(field.GetType(), field.GetName(), field.GetValue()); 46 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
29} 47}
30 48
31void TelemetryJson::Visit(const Telemetry::Field<double>& field) { 49void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
32 Serialize(field.GetType(), field.GetName(), field.GetValue()); 50 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
33} 51}
34 52
35void TelemetryJson::Visit(const Telemetry::Field<float>& field) { 53void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
36 Serialize(field.GetType(), field.GetName(), field.GetValue()); 54 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
37} 55}
38 56
39void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { 57void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
40 Serialize(field.GetType(), field.GetName(), field.GetValue()); 58 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
41} 59}
42 60
43void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { 61void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
44 Serialize(field.GetType(), field.GetName(), field.GetValue()); 62 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
45} 63}
46 64
47void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { 65void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
48 Serialize(field.GetType(), field.GetName(), field.GetValue()); 66 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
49} 67}
50 68
51void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { 69void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
52 Serialize(field.GetType(), field.GetName(), field.GetValue()); 70 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
53} 71}
54 72
55void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { 73void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
56 Serialize(field.GetType(), field.GetName(), field.GetValue()); 74 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
57} 75}
58 76
59void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { 77void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
60 Serialize(field.GetType(), field.GetName(), field.GetValue()); 78 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
61} 79}
62 80
63void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { 81void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
64 Serialize(field.GetType(), field.GetName(), field.GetValue()); 82 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
65} 83}
66 84
67void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { 85void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
68 Serialize(field.GetType(), field.GetName(), field.GetValue()); 86 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
69} 87}
70 88
71void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { 89void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
72 Serialize(field.GetType(), field.GetName(), field.GetValue()); 90 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
73} 91}
74 92
75void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { 93void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
76 Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); 94 impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
77} 95}
78 96
79void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { 97void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
80 Serialize(field.GetType(), field.GetName(), field.GetValue().count()); 98 impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
81} 99}
82 100
83void TelemetryJson::Complete() { 101void TelemetryJson::Complete() {
84 SerializeSection(Telemetry::FieldType::App, "App"); 102 impl->SerializeSection(Telemetry::FieldType::App, "App");
85 SerializeSection(Telemetry::FieldType::Session, "Session"); 103 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
86 SerializeSection(Telemetry::FieldType::Performance, "Performance"); 104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
87 SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); 105 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
88 SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 106 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
89 SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 107 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
90 108
91 auto content = TopSection().dump(); 109 auto content = impl->TopSection().dump();
92 // Send the telemetry async but don't handle the errors since they were written to the log 110 // Send the telemetry async but don't handle the errors since they were written to the log
93 Common::DetachedTasks::AddTask( 111 Common::DetachedTasks::AddTask(
94 [host{this->host}, username{this->username}, token{this->token}, content]() { 112 [host{impl->host}, username{impl->username}, token{impl->token}, content]() {
95 Client{host, username, token}.PostJson("/telemetry", content, true); 113 Client{host, username, token}.PostJson("/telemetry", content, true);
96 }); 114 });
97} 115}
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 0fe6f9a3e..93371414a 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <chrono>
8#include <string> 8#include <string>
9#include <json.hpp>
10#include "common/telemetry.h" 9#include "common/telemetry.h"
11#include "common/web_result.h"
12 10
13namespace WebService { 11namespace WebService {
14 12
@@ -18,8 +16,8 @@ namespace WebService {
18 */ 16 */
19class TelemetryJson : public Telemetry::VisitorInterface { 17class TelemetryJson : public Telemetry::VisitorInterface {
20public: 18public:
21 TelemetryJson(const std::string& host, const std::string& username, const std::string& token); 19 TelemetryJson(std::string host, std::string username, std::string token);
22 ~TelemetryJson(); 20 ~TelemetryJson() override;
23 21
24 void Visit(const Telemetry::Field<bool>& field) override; 22 void Visit(const Telemetry::Field<bool>& field) override;
25 void Visit(const Telemetry::Field<double>& field) override; 23 void Visit(const Telemetry::Field<double>& field) override;
@@ -39,20 +37,8 @@ public:
39 void Complete() override; 37 void Complete() override;
40 38
41private: 39private:
42 nlohmann::json& TopSection() { 40 struct Impl;
43 return sections[static_cast<u8>(Telemetry::FieldType::None)]; 41 std::unique_ptr<Impl> impl;
44 }
45
46 template <class T>
47 void Serialize(Telemetry::FieldType type, const std::string& name, T value);
48
49 void SerializeSection(Telemetry::FieldType type, const std::string& name);
50
51 nlohmann::json output;
52 std::array<nlohmann::json, 7> sections;
53 std::string host;
54 std::string username;
55 std::string token;
56}; 42};
57 43
58} // namespace WebService 44} // namespace WebService
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index 124aa3863..ca4b43b93 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <json.hpp> 5#include <json.hpp>
6#include "common/web_result.h"
6#include "web_service/verify_login.h" 7#include "web_service/verify_login.h"
7#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
8 9
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 787b0fbcb..b7737b615 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -3,9 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdlib> 5#include <cstdlib>
6#include <mutex>
6#include <string> 7#include <string>
7#include <thread>
8#include <LUrlParser.h> 8#include <LUrlParser.h>
9#include <httplib.h>
10#include "common/common_types.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/web_result.h" 12#include "common/web_result.h"
11#include "core/settings.h" 13#include "core/settings.h"
@@ -20,99 +22,132 @@ constexpr u32 HTTPS_PORT = 443;
20 22
21constexpr u32 TIMEOUT_SECONDS = 30; 23constexpr u32 TIMEOUT_SECONDS = 30;
22 24
23Client::JWTCache Client::jwt_cache{}; 25struct Client::Impl {
26 Impl(std::string host, std::string username, std::string token)
27 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
28 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
29 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
30 jwt = jwt_cache.jwt;
31 }
32 }
33
34 /// A generic function handles POST, GET and DELETE request together
35 Common::WebResult GenericJson(const std::string& method, const std::string& path,
36 const std::string& data, bool allow_anonymous) {
37 if (jwt.empty()) {
38 UpdateJWT();
39 }
40
41 if (jwt.empty() && !allow_anonymous) {
42 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
43 return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
44 "Credentials needed"};
45 }
46
47 auto result = GenericJson(method, path, data, jwt);
48 if (result.result_string == "401") {
49 // Try again with new JWT
50 UpdateJWT();
51 result = GenericJson(method, path, data, jwt);
52 }
24 53
25Client::Client(const std::string& host, const std::string& username, const std::string& token) 54 return result;
26 : host(host), username(username), token(token) {
27 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
28 if (username == jwt_cache.username && token == jwt_cache.token) {
29 jwt = jwt_cache.jwt;
30 } 55 }
31}
32 56
33Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 57 /**
34 const std::string& data, const std::string& jwt, 58 * A generic function with explicit authentication method specified
35 const std::string& username, const std::string& token) { 59 * JWT is used if the jwt parameter is not empty
36 if (cli == nullptr) { 60 * username + token is used if jwt is empty but username and token are not empty
37 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 61 * anonymous if all of jwt, username and token are empty
38 int port; 62 */
39 if (parsedUrl.m_Scheme == "http") { 63 Common::WebResult GenericJson(const std::string& method, const std::string& path,
40 if (!parsedUrl.GetPort(&port)) { 64 const std::string& data, const std::string& jwt = "",
41 port = HTTP_PORT; 65 const std::string& username = "", const std::string& token = "") {
42 } 66 if (cli == nullptr) {
43 cli = 67 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
44 std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS); 68 int port;
45 } else if (parsedUrl.m_Scheme == "https") { 69 if (parsedUrl.m_Scheme == "http") {
46 if (!parsedUrl.GetPort(&port)) { 70 if (!parsedUrl.GetPort(&port)) {
47 port = HTTPS_PORT; 71 port = HTTP_PORT;
72 }
73 cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port,
74 TIMEOUT_SECONDS);
75 } else if (parsedUrl.m_Scheme == "https") {
76 if (!parsedUrl.GetPort(&port)) {
77 port = HTTPS_PORT;
78 }
79 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
80 TIMEOUT_SECONDS);
81 } else {
82 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
83 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
48 } 84 }
49 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
50 TIMEOUT_SECONDS);
51 } else {
52 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
53 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
54 } 85 }
55 } 86 if (cli == nullptr) {
56 if (cli == nullptr) { 87 LOG_ERROR(WebService, "Invalid URL {}", host + path);
57 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
58 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; 89 }
59 }
60 90
61 httplib::Headers params; 91 httplib::Headers params;
62 if (!jwt.empty()) { 92 if (!jwt.empty()) {
63 params = { 93 params = {
64 {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, 94 {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
65 }; 95 };
66 } else if (!username.empty()) { 96 } else if (!username.empty()) {
67 params = { 97 params = {
68 {std::string("x-username"), username}, 98 {std::string("x-username"), username},
69 {std::string("x-token"), token}, 99 {std::string("x-token"), token},
100 };
101 }
102
103 params.emplace(std::string("api-version"),
104 std::string(API_VERSION.begin(), API_VERSION.end()));
105 if (method != "GET") {
106 params.emplace(std::string("Content-Type"), std::string("application/json"));
70 }; 107 };
71 }
72 108
73 params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end())); 109 httplib::Request request;
74 if (method != "GET") { 110 request.method = method;
75 params.emplace(std::string("Content-Type"), std::string("application/json")); 111 request.path = path;
76 }; 112 request.headers = params;
113 request.body = data;
77 114
78 httplib::Request request; 115 httplib::Response response;
79 request.method = method;
80 request.path = path;
81 request.headers = params;
82 request.body = data;
83 116
84 httplib::Response response; 117 if (!cli->send(request, response)) {
118 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
119 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"};
120 }
85 121
86 if (!cli->send(request, response)) { 122 if (response.status >= 400) {
87 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 123 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
88 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; 124 response.status);
89 } 125 return Common::WebResult{Common::WebResult::Code::HttpError,
126 std::to_string(response.status)};
127 }
90 128
91 if (response.status >= 400) { 129 auto content_type = response.headers.find("content-type");
92 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
93 response.status);
94 return Common::WebResult{Common::WebResult::Code::HttpError,
95 std::to_string(response.status)};
96 }
97 130
98 auto content_type = response.headers.find("content-type"); 131 if (content_type == response.headers.end()) {
132 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
133 return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
134 }
99 135
100 if (content_type == response.headers.end()) { 136 if (content_type->second.find("application/json") == std::string::npos &&
101 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 137 content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
102 return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
139 content_type->second);
140 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
141 }
142 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
103 } 143 }
104 144
105 if (content_type->second.find("application/json") == std::string::npos && 145 // Retrieve a new JWT from given username and token
106 content_type->second.find("text/html; charset=utf-8") == std::string::npos) { 146 void UpdateJWT() {
107 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 147 if (username.empty() || token.empty()) {
108 content_type->second); 148 return;
109 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; 149 }
110 }
111 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
112}
113 150
114void Client::UpdateJWT() {
115 if (!username.empty() && !token.empty()) {
116 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token); 151 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
117 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != Common::WebResult::Code::Success) {
118 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
@@ -123,27 +158,39 @@ void Client::UpdateJWT() {
123 jwt_cache.jwt = jwt = result.returned_data; 158 jwt_cache.jwt = jwt = result.returned_data;
124 } 159 }
125 } 160 }
126}
127 161
128Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 162 std::string host;
129 const std::string& data, bool allow_anonymous) { 163 std::string username;
130 if (jwt.empty()) { 164 std::string token;
131 UpdateJWT(); 165 std::string jwt;
132 } 166 std::unique_ptr<httplib::Client> cli;
167
168 struct JWTCache {
169 std::mutex mutex;
170 std::string username;
171 std::string token;
172 std::string jwt;
173 };
174 static inline JWTCache jwt_cache;
175};
133 176
134 if (jwt.empty() && !allow_anonymous) { 177Client::Client(std::string host, std::string username, std::string token)
135 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 178 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
136 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"};
137 }
138 179
139 auto result = GenericJson(method, path, data, jwt); 180Client::~Client() = default;
140 if (result.result_string == "401") { 181
141 // Try again with new JWT 182Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
142 UpdateJWT(); 183 bool allow_anonymous) {
143 result = GenericJson(method, path, data, jwt); 184 return impl->GenericJson("POST", path, data, allow_anonymous);
144 } 185}
186
187Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
188 return impl->GenericJson("GET", path, "", allow_anonymous);
189}
145 190
146 return result; 191Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
192 bool allow_anonymous) {
193 return impl->GenericJson("DELETE", path, data, allow_anonymous);
147} 194}
148 195
149} // namespace WebService 196} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index d75fbcc15..c637e09df 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -4,23 +4,19 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <memory>
8#include <mutex>
9#include <string> 8#include <string>
10#include <tuple>
11#include <httplib.h>
12#include "common/common_types.h"
13#include "common/web_result.h"
14 9
15namespace httplib { 10namespace Common {
16class Client; 11struct WebResult;
17} 12}
18 13
19namespace WebService { 14namespace WebService {
20 15
21class Client { 16class Client {
22public: 17public:
23 Client(const std::string& host, const std::string& username, const std::string& token); 18 Client(std::string host, std::string username, std::string token);
19 ~Client();
24 20
25 /** 21 /**
26 * Posts JSON to the specified path. 22 * Posts JSON to the specified path.
@@ -30,9 +26,7 @@ public:
30 * @return the result of the request. 26 * @return the result of the request.
31 */ 27 */
32 Common::WebResult PostJson(const std::string& path, const std::string& data, 28 Common::WebResult PostJson(const std::string& path, const std::string& data,
33 bool allow_anonymous) { 29 bool allow_anonymous);
34 return GenericJson("POST", path, data, allow_anonymous);
35 }
36 30
37 /** 31 /**
38 * Gets JSON from the specified path. 32 * Gets JSON from the specified path.
@@ -40,9 +34,7 @@ public:
40 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 34 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
41 * @return the result of the request. 35 * @return the result of the request.
42 */ 36 */
43 Common::WebResult GetJson(const std::string& path, bool allow_anonymous) { 37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
44 return GenericJson("GET", path, "", allow_anonymous);
45 }
46 38
47 /** 39 /**
48 * Deletes JSON to the specified path. 40 * Deletes JSON to the specified path.
@@ -52,41 +44,11 @@ public:
52 * @return the result of the request. 44 * @return the result of the request.
53 */ 45 */
54 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 46 Common::WebResult DeleteJson(const std::string& path, const std::string& data,
55 bool allow_anonymous) { 47 bool allow_anonymous);
56 return GenericJson("DELETE", path, data, allow_anonymous);
57 }
58 48
59private: 49private:
60 /// A generic function handles POST, GET and DELETE request together 50 struct Impl;
61 Common::WebResult GenericJson(const std::string& method, const std::string& path, 51 std::unique_ptr<Impl> impl;
62 const std::string& data, bool allow_anonymous);
63
64 /**
65 * A generic function with explicit authentication method specified
66 * JWT is used if the jwt parameter is not empty
67 * username + token is used if jwt is empty but username and token are not empty
68 * anonymous if all of jwt, username and token are empty
69 */
70 Common::WebResult GenericJson(const std::string& method, const std::string& path,
71 const std::string& data, const std::string& jwt = "",
72 const std::string& username = "", const std::string& token = "");
73
74 // Retrieve a new JWT from given username and token
75 void UpdateJWT();
76
77 std::string host;
78 std::string username;
79 std::string token;
80 std::string jwt;
81 std::unique_ptr<httplib::Client> cli;
82
83 struct JWTCache {
84 std::mutex mutex;
85 std::string username;
86 std::string token;
87 std::string jwt;
88 };
89 static JWTCache jwt_cache;
90}; 52};
91 53
92} // namespace WebService 54} // namespace WebService
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7fec15991..71c6ebb41 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -85,8 +85,8 @@ void Config::ReadValues() {
85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); 86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); 87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt();
88 Settings::values.use_accurate_framebuffers = 88 Settings::values.use_accurate_gpu_emulation =
89 qt_config->value("use_accurate_framebuffers", false).toBool(); 89 qt_config->value("use_accurate_gpu_emulation", false).toBool();
90 90
91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -233,7 +233,7 @@ void Config::SaveValues() {
233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);
235 qt_config->setValue("frame_limit", Settings::values.frame_limit); 235 qt_config->setValue("frame_limit", Settings::values.frame_limit);
236 qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers); 236 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
237 237
238 // Cast to double because Qt's written float values are not human-readable 238 // Cast to double because Qt's written float values are not human-readable
239 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 239 qt_config->setValue("bg_red", (double)Settings::values.bg_red);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index cd1549462..8290b4384 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -75,7 +75,7 @@ void ConfigureGraphics::setConfiguration() {
75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
77 ui->frame_limit->setValue(Settings::values.frame_limit); 77 ui->frame_limit->setValue(Settings::values.frame_limit);
78 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); 78 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
80 Settings::values.bg_blue); 80 Settings::values.bg_blue);
81 ui->bg_button->setStyleSheet( 81 ui->bg_button->setStyleSheet(
@@ -87,7 +87,7 @@ void ConfigureGraphics::applyConfiguration() {
87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
89 Settings::values.frame_limit = ui->frame_limit->value(); 89 Settings::values.frame_limit = ui->frame_limit->value();
90 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); 90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 91 Settings::values.bg_red = static_cast<float>(bg_color.redF());
92 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 92 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 93 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 8fc00af1b..91fcad994 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -50,9 +50,9 @@
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_accurate_framebuffers"> 53 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
54 <property name="text"> 54 <property name="text">
55 <string>Use accurate framebuffers (slow)</string> 55 <string>Use accurate GPU emulation (slow)</string>
56 </property> 56 </property>
57 </widget> 57 </widget>
58 </item> 58 </item>
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index cbcd5dd5f..44d423da2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,8 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 389 auto unswizzled_data =
390 *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height); 390 Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format),
391 surface_width, surface_height, 1U);
391 392
392 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
393 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 4a09da685..7403e9ccd 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -66,10 +66,11 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
66 } 66 }
67 }; 67 };
68 68
69 add_threads(Core::System::GetInstance().Scheduler(0)->GetThreadList()); 69 const auto& system = Core::System::GetInstance();
70 add_threads(Core::System::GetInstance().Scheduler(1)->GetThreadList()); 70 add_threads(system.Scheduler(0).GetThreadList());
71 add_threads(Core::System::GetInstance().Scheduler(2)->GetThreadList()); 71 add_threads(system.Scheduler(1).GetThreadList());
72 add_threads(Core::System::GetInstance().Scheduler(3)->GetThreadList()); 72 add_threads(system.Scheduler(2).GetThreadList());
73 add_threads(system.Scheduler(3).GetThreadList());
73 74
74 return item_list; 75 return item_list;
75} 76}
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 8f99a1c78..3881aba5f 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -96,7 +96,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
96 FileSys::ContentRecordType::Program); 96 FileSys::ContentRecordType::Program);
97 97
98 for (const auto& game : installed_games) { 98 for (const auto& game : installed_games) {
99 const auto& file = cache->GetEntryUnparsed(game); 99 const auto file = cache->GetEntryUnparsed(game);
100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
101 if (!loader) 101 if (!loader)
102 continue; 102 continue;
@@ -107,7 +107,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
107 loader->ReadProgramId(program_id); 107 loader->ReadProgramId(program_id);
108 108
109 const FileSys::PatchManager patch{program_id}; 109 const FileSys::PatchManager patch{program_id};
110 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); 110 const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
111 if (control != nullptr) 111 if (control != nullptr)
112 GetMetadataFromControlNCA(patch, *control, icon, name); 112 GetMetadataFromControlNCA(patch, *control, icon, name);
113 113
@@ -135,9 +135,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {
135 FileSys::ContentRecordType::Control); 135 FileSys::ContentRecordType::Control);
136 136
137 for (const auto& entry : control_data) { 137 for (const auto& entry : control_data) {
138 const auto nca = cache->GetEntry(entry); 138 auto nca = cache->GetEntry(entry);
139 if (nca != nullptr) 139 if (nca != nullptr) {
140 nca_control_map.insert_or_assign(entry.title_id, nca); 140 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
141 }
141 } 142 }
142} 143}
143 144
@@ -153,9 +154,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
153 QFileInfo file_info(physical_name.c_str()); 154 QFileInfo file_info(physical_name.c_str());
154 if (!is_dir && file_info.suffix().toStdString() == "nca") { 155 if (!is_dir && file_info.suffix().toStdString() == "nca") {
155 auto nca = 156 auto nca =
156 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 157 std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
157 if (nca->GetType() == FileSys::NCAContentType::Control) 158 if (nca->GetType() == FileSys::NCAContentType::Control) {
158 nca_control_map.insert_or_assign(nca->GetTitleId(), nca); 159 const u64 title_id = nca->GetTitleId();
160 nca_control_map.insert_or_assign(title_id, std::move(nca));
161 }
159 } 162 }
160 return true; 163 return true;
161 }; 164 };
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 09d20c42f..0e42d0bde 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -63,7 +63,7 @@ private:
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); 63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
64 64
65 std::shared_ptr<FileSys::VfsFilesystem> vfs; 65 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; 66 std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;
67 QStringList watch_list; 67 QStringList watch_list;
68 QString dir_path; 68 QString dir_path;
69 bool deep_scan; 69 bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fc186dc2d..bef9df00d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -176,7 +176,7 @@ GMainWindow::GMainWindow()
176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
177 177
178 // Necessary to load titles from nand in gamelist. 178 // Necessary to load titles from nand in gamelist.
179 Service::FileSystem::CreateFactories(vfs); 179 Service::FileSystem::CreateFactories(*vfs);
180 game_list->LoadCompatibilityList(); 180 game_list->LoadCompatibilityList();
181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
182 182
@@ -908,22 +908,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
908} 908}
909 909
910void GMainWindow::OnMenuLoadFile() { 910void GMainWindow::OnMenuLoadFile() {
911 QString extensions; 911 const QString extensions =
912 for (const auto& piece : game_list->supported_file_extensions) 912 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
913 extensions += "*." + piece + " "; 913 const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
914 "%1 is an identifier for the Switch executable file extensions.")
915 .arg(extensions);
916 const QString filename = QFileDialog::getOpenFileName(
917 this, tr("Load File"), UISettings::values.roms_path, file_filter);
914 918
915 extensions += "main "; 919 if (filename.isEmpty()) {
916 920 return;
917 QString file_filter = tr("Switch Executable") + " (" + extensions + ")";
918 file_filter += ";;" + tr("All Files (*.*)");
919
920 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
921 UISettings::values.roms_path, file_filter);
922 if (!filename.isEmpty()) {
923 UISettings::values.roms_path = QFileInfo(filename).path();
924
925 BootGame(filename);
926 } 921 }
922
923 UISettings::values.roms_path = QFileInfo(filename).path();
924 BootGame(filename);
927} 925}
928 926
929void GMainWindow::OnMenuLoadFolder() { 927void GMainWindow::OnMenuLoadFolder() {
@@ -1139,7 +1137,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1139 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1137 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1140 : FileUtil::UserPath::NANDDir, 1138 : FileUtil::UserPath::NANDDir,
1141 dir_path.toStdString()); 1139 dir_path.toStdString());
1142 Service::FileSystem::CreateFactories(vfs); 1140 Service::FileSystem::CreateFactories(*vfs);
1143 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1141 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1144 } 1142 }
1145} 1143}
@@ -1410,7 +1408,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1410 1408
1411 const auto function = [this, &keys, &pdm] { 1409 const auto function = [this, &keys, &pdm] {
1412 keys.PopulateFromPartitionData(pdm); 1410 keys.PopulateFromPartitionData(pdm);
1413 Service::FileSystem::CreateFactories(vfs); 1411 Service::FileSystem::CreateFactories(*vfs);
1414 keys.DeriveETicket(pdm); 1412 keys.DeriveETicket(pdm);
1415 }; 1413 };
1416 1414
@@ -1430,8 +1428,12 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1430 QMessageBox::warning( 1428 QMessageBox::warning(
1431 this, tr("Warning Missing Derivation Components"), 1429 this, tr("Warning Missing Derivation Components"),
1432 tr("The following are missing from your configuration that may hinder key " 1430 tr("The following are missing from your configuration that may hinder key "
1433 "derivation. It will be attempted but may not complete.\n\n") + 1431 "derivation. It will be attempted but may not complete.<br><br>") +
1434 errors); 1432 errors +
1433 tr("<br><br>You can get all of these and dump all of your games easily by "
1434 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
1435 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1436 "to obtain all of your keys."));
1435 } 1437 }
1436 1438
1437 QProgressDialog prog; 1439 QProgressDialog prog;
@@ -1450,7 +1452,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1450 prog.close(); 1452 prog.close();
1451 } 1453 }
1452 1454
1453 Service::FileSystem::CreateFactories(vfs); 1455 Service::FileSystem::CreateFactories(*vfs);
1454 1456
1455 if (behavior == ReinitializeKeyBehavior::Warning) { 1457 if (behavior == ReinitializeKeyBehavior::Warning) {
1456 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1458 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
@@ -1565,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {
1565 emit UpdateThemedIcons(); 1567 emit UpdateThemedIcons();
1566} 1568}
1567 1569
1568void GMainWindow::SetDiscordEnabled(bool state) { 1570void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
1569#ifdef USE_DISCORD_PRESENCE 1571#ifdef USE_DISCORD_PRESENCE
1570 if (state) { 1572 if (state) {
1571 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); 1573 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 2470f4640..5e42e48b2 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -99,8 +99,8 @@ void Config::ReadValues() {
99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
100 Settings::values.frame_limit = 100 Settings::values.frame_limit =
101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
102 Settings::values.use_accurate_framebuffers = 102 Settings::values.use_accurate_gpu_emulation =
103 sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false); 103 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
104 104
105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); 105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 762396e3b..a97b75f7b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -110,9 +110,9 @@ use_frame_limit =
110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) 110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
111frame_limit = 111frame_limit =
112 112
113# Whether to use accurate framebuffers 113# Whether to use accurate GPU emulation
114# 0 (default): Off (fast), 1 : On (slow) 114# 0 (default): Off (fast), 1 : On (slow)
115use_accurate_framebuffers = 115use_accurate_gpu_emulation =
116 116
117# The clear color for the renderer. What shows up on the sides of the bottom screen. 117# The clear color for the renderer. What shows up on the sides of the bottom screen.
118# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 118# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 27aba95f6..c8b93b85b 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -175,7 +175,7 @@ int main(int argc, char** argv) {
175 175
176 Core::System& system{Core::System::GetInstance()}; 176 Core::System& system{Core::System::GetInstance()};
177 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 177 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
178 Service::FileSystem::CreateFactories(system.GetFilesystem()); 178 Service::FileSystem::CreateFactories(*system.GetFilesystem());
179 179
180 SCOPE_EXIT({ system.Shutdown(); }); 180 SCOPE_EXIT({ system.Shutdown(); });
181 181