summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/memory_hook.cpp (renamed from src/core/memory_hook.cpp)6
-rw-r--r--src/common/memory_hook.h (renamed from src/core/memory_hook.h)4
-rw-r--r--src/common/page_table.cpp29
-rw-r--r--src/common/page_table.h80
-rw-r--r--src/common/uint128.cpp41
-rw-r--r--src/common/uint128.h14
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core_timing_util.cpp6
-rw-r--r--src/core/core_timing_util.h3
-rw-r--r--src/core/hle/ipc_helpers.h48
-rw-r--r--src/core/hle/kernel/client_port.cpp9
-rw-r--r--src/core/hle/kernel/process.cpp13
-rw-r--r--src/core/hle/kernel/process.h6
-rw-r--r--src/core/hle/kernel/scheduler.cpp8
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_port.h35
-rw-r--r--src/core/hle/kernel/svc.cpp32
-rw-r--r--src/core/hle/kernel/thread.cpp19
-rw-r--r--src/core/hle/kernel/thread.h13
-rw-r--r--src/core/hle/kernel/vm_manager.cpp6
-rw-r--r--src/core/hle/kernel/vm_manager.h9
-rw-r--r--src/core/hle/result.h18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp3
-rw-r--r--src/core/hle/service/service.cpp3
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/vi/vi.cpp38
-rw-r--r--src/core/hle/service/vi/vi.h40
-rw-r--r--src/core/hle/service/vi/vi_m.cpp12
-rw-r--r--src/core/hle/service/vi/vi_m.h19
-rw-r--r--src/core/hle/service/vi/vi_s.cpp12
-rw-r--r--src/core/hle/service/vi/vi_s.h19
-rw-r--r--src/core/hle/service/vi/vi_u.cpp12
-rw-r--r--src/core/hle/service/vi/vi_u.h19
-rw-r--r--src/core/memory.cpp218
-rw-r--r--src/core/memory.h80
-rw-r--r--src/core/memory_setup.h19
-rw-r--r--src/input_common/CMakeLists.txt15
-rw-r--r--src/input_common/main.cpp23
-rw-r--r--src/input_common/main.h2
-rw-r--r--src/input_common/sdl/sdl.cpp636
-rw-r--r--src/input_common/sdl/sdl.h53
-rw-r--r--src/input_common/sdl/sdl_impl.cpp669
-rw-r--r--src/input_common/sdl/sdl_impl.h64
-rw-r--r--src/tests/core/arm/arm_test_common.cpp3
-rw-r--r--src/tests/core/arm/arm_test_common.h8
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/dma_pusher.cpp7
-rw-r--r--src/video_core/engines/kepler_memory.cpp15
-rw-r--r--src/video_core/engines/maxwell_3d.cpp42
-rw-r--r--src/video_core/engines/maxwell_dma.cpp27
-rw-r--r--src/video_core/gpu.cpp13
-rw-r--r--src/video_core/gpu.h11
-rw-r--r--src/video_core/gpu_asynch.cpp6
-rw-r--r--src/video_core/gpu_asynch.h6
-rw-r--r--src/video_core/gpu_synch.cpp6
-rw-r--r--src/video_core/gpu_synch.h6
-rw-r--r--src/video_core/gpu_thread.cpp136
-rw-r--r--src/video_core/gpu_thread.h132
-rw-r--r--src/video_core/memory_manager.cpp55
-rw-r--r--src/video_core/memory_manager.h17
-rw-r--r--src/video_core/morton.cpp324
-rw-r--r--src/video_core/morton.h6
-rw-r--r--src/video_core/rasterizer_cache.h70
-rw-r--r--src/video_core/rasterizer_interface.h8
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp26
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h31
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp43
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.h21
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp75
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp69
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h19
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp62
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h19
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp27
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h31
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.cpp81
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.h56
-rw-r--r--src/video_core/textures/decoders.cpp32
-rw-r--r--src/video_core/textures/decoders.h13
-rw-r--r--src/video_core/textures/texture.h55
-rw-r--r--src/yuzu/bootmanager.cpp1
-rw-r--r--src/yuzu/configuration/config.cpp448
-rw-r--r--src/yuzu/configuration/config.h5
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp13
91 files changed, 2499 insertions, 1938 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 3d30f0e3e..43ae8a9e7 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -92,10 +92,14 @@ add_library(common STATIC
92 logging/text_formatter.cpp 92 logging/text_formatter.cpp
93 logging/text_formatter.h 93 logging/text_formatter.h
94 math_util.h 94 math_util.h
95 memory_hook.cpp
96 memory_hook.h
95 microprofile.cpp 97 microprofile.cpp
96 microprofile.h 98 microprofile.h
97 microprofileui.h 99 microprofileui.h
98 misc.cpp 100 misc.cpp
101 page_table.cpp
102 page_table.h
99 param_package.cpp 103 param_package.cpp
100 param_package.h 104 param_package.h
101 quaternion.h 105 quaternion.h
@@ -114,6 +118,8 @@ add_library(common STATIC
114 threadsafe_queue.h 118 threadsafe_queue.h
115 timer.cpp 119 timer.cpp
116 timer.h 120 timer.h
121 uint128.cpp
122 uint128.h
117 vector_math.h 123 vector_math.h
118 web_result.h 124 web_result.h
119) 125)
diff --git a/src/core/memory_hook.cpp b/src/common/memory_hook.cpp
index c61c6c1fb..3986986d6 100644
--- a/src/core/memory_hook.cpp
+++ b/src/common/memory_hook.cpp
@@ -2,10 +2,10 @@
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 "core/memory_hook.h" 5#include "common/memory_hook.h"
6 6
7namespace Memory { 7namespace Common {
8 8
9MemoryHook::~MemoryHook() = default; 9MemoryHook::~MemoryHook() = default;
10 10
11} // namespace Memory 11} // namespace Common
diff --git a/src/core/memory_hook.h b/src/common/memory_hook.h
index 940777107..adaa4c2c5 100644
--- a/src/core/memory_hook.h
+++ b/src/common/memory_hook.h
@@ -9,7 +9,7 @@
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace Memory { 12namespace Common {
13 13
14/** 14/**
15 * Memory hooks have two purposes: 15 * Memory hooks have two purposes:
@@ -44,4 +44,4 @@ public:
44}; 44};
45 45
46using MemoryHookPointer = std::shared_ptr<MemoryHook>; 46using MemoryHookPointer = std::shared_ptr<MemoryHook>;
47} // namespace Memory 47} // namespace Common
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
new file mode 100644
index 000000000..8eba1c3f1
--- /dev/null
+++ b/src/common/page_table.cpp
@@ -0,0 +1,29 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/page_table.h"
6
7namespace Common {
8
9PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {}
10
11PageTable::~PageTable() = default;
12
13void PageTable::Resize(std::size_t address_space_width_in_bits) {
14 const std::size_t num_page_table_entries = 1ULL
15 << (address_space_width_in_bits - page_size_in_bits);
16
17 pointers.resize(num_page_table_entries);
18 attributes.resize(num_page_table_entries);
19
20 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
21 // vector size is subsequently decreased (via resize), the vector might not automatically
22 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
23 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
24
25 pointers.shrink_to_fit();
26 attributes.shrink_to_fit();
27}
28
29} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
new file mode 100644
index 000000000..8339f2890
--- /dev/null
+++ b/src/common/page_table.h
@@ -0,0 +1,80 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include <boost/icl/interval_map.hpp>
9#include "common/common_types.h"
10#include "common/memory_hook.h"
11
12namespace Common {
13
14enum class PageType : u8 {
15 /// Page is unmapped and should cause an access error.
16 Unmapped,
17 /// Page is mapped to regular memory. This is the only type you can get pointers to.
18 Memory,
19 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
20 /// invalidation
21 RasterizerCachedMemory,
22 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
23 Special,
24};
25
26struct SpecialRegion {
27 enum class Type {
28 DebugHook,
29 IODevice,
30 } type;
31
32 MemoryHookPointer handler;
33
34 bool operator<(const SpecialRegion& other) const {
35 return std::tie(type, handler) < std::tie(other.type, other.handler);
36 }
37
38 bool operator==(const SpecialRegion& other) const {
39 return std::tie(type, handler) == std::tie(other.type, other.handler);
40 }
41};
42
43/**
44 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
45 * mimics the way a real CPU page table works.
46 */
47struct PageTable {
48 explicit PageTable(std::size_t page_size_in_bits);
49 ~PageTable();
50
51 /**
52 * Resizes the page table to be able to accomodate enough pages within
53 * a given address space.
54 *
55 * @param address_space_width_in_bits The address size width in bits.
56 */
57 void Resize(std::size_t address_space_width_in_bits);
58
59 /**
60 * Vector of memory pointers backing each page. An entry can only be non-null if the
61 * corresponding entry in the `attributes` vector is of type `Memory`.
62 */
63 std::vector<u8*> pointers;
64
65 /**
66 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
67 * of type `Special`.
68 */
69 boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
70
71 /**
72 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
73 * the corresponding entry in `pointers` MUST be set to null.
74 */
75 std::vector<PageType> attributes;
76
77 const std::size_t page_size_in_bits{};
78};
79
80} // namespace Common
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
new file mode 100644
index 000000000..2238a52c5
--- /dev/null
+++ b/src/common/uint128.cpp
@@ -0,0 +1,41 @@
1#ifdef _MSC_VER
2#include <intrin.h>
3
4#pragma intrinsic(_umul128)
5#endif
6#include <cstring>
7#include "common/uint128.h"
8
9namespace Common {
10
11u128 Multiply64Into128(u64 a, u64 b) {
12 u128 result;
13#ifdef _MSC_VER
14 result[0] = _umul128(a, b, &result[1]);
15#else
16 unsigned __int128 tmp = a;
17 tmp *= b;
18 std::memcpy(&result, &tmp, sizeof(u128));
19#endif
20 return result;
21}
22
23std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
24 u64 remainder = dividend[0] % divisor;
25 u64 accum = dividend[0] / divisor;
26 if (dividend[1] == 0)
27 return {accum, remainder};
28 // We ignore dividend[1] / divisor as that overflows
29 const u64 first_segment = (dividend[1] % divisor) << 32;
30 accum += (first_segment / divisor) << 32;
31 const u64 second_segment = (first_segment % divisor) << 32;
32 accum += (second_segment / divisor);
33 remainder += second_segment % divisor;
34 if (remainder >= divisor) {
35 accum++;
36 remainder -= divisor;
37 }
38 return {accum, remainder};
39}
40
41} // namespace Common
diff --git a/src/common/uint128.h b/src/common/uint128.h
new file mode 100644
index 000000000..52e6b46eb
--- /dev/null
+++ b/src/common/uint128.h
@@ -0,0 +1,14 @@
1
2#include <utility>
3#include "common/common_types.h"
4
5namespace Common {
6
7// This function multiplies 2 u64 values and produces a u128 value;
8u128 Multiply64Into128(u64 a, u64 b);
9
10// This function divides a u128 by a u32 value and produces two u64 values:
11// the result of division and the remainder
12std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
13
14} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8ccb2d5f0..aee8bc27d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -437,8 +437,6 @@ add_library(core STATIC
437 loader/xci.h 437 loader/xci.h
438 memory.cpp 438 memory.cpp
439 memory.h 439 memory.h
440 memory_hook.cpp
441 memory_hook.h
442 memory_setup.h 440 memory_setup.h
443 perf_stats.cpp 441 perf_stats.cpp
444 perf_stats.h 442 perf_stats.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 9b7ca4030..4fdc12f11 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -12,6 +12,7 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/core_cpu.h" 13#include "core/core_cpu.h"
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/core_timing_util.h"
15#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
16#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/svc.h" 18#include "core/hle/kernel/svc.h"
@@ -119,7 +120,7 @@ public:
119 return std::max(parent.core_timing.GetDowncount(), 0); 120 return std::max(parent.core_timing.GetDowncount(), 0);
120 } 121 }
121 u64 GetCNTPCT() override { 122 u64 GetCNTPCT() override {
122 return parent.core_timing.GetTicks(); 123 return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
123 } 124 }
124 125
125 ARM_Dynarmic& parent; 126 ARM_Dynarmic& parent;
@@ -151,7 +152,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
151 config.tpidr_el0 = &cb->tpidr_el0; 152 config.tpidr_el0 = &cb->tpidr_el0;
152 config.dczid_el0 = 4; 153 config.dczid_el0 = 4;
153 config.ctr_el0 = 0x8444c004; 154 config.ctr_el0 = 0x8444c004;
154 config.cntfrq_el0 = 19200000; // Value from fusee. 155 config.cntfrq_el0 = Timing::CNTFREQ;
155 156
156 // Unpredictable instructions 157 // Unpredictable instructions
157 config.define_unpredictable_behaviour = true; 158 config.define_unpredictable_behaviour = true;
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 6cc458296..aada1e862 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,7 +12,7 @@
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
14 14
15namespace Memory { 15namespace Common {
16struct PageTable; 16struct PageTable;
17} 17}
18 18
@@ -70,7 +70,7 @@ private:
70 Timing::CoreTiming& core_timing; 70 Timing::CoreTiming& core_timing;
71 DynarmicExclusiveMonitor& exclusive_monitor; 71 DynarmicExclusiveMonitor& exclusive_monitor;
72 72
73 Memory::PageTable* current_page_table = nullptr; 73 Common::PageTable* current_page_table = nullptr;
74}; 74};
75 75
76class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 76class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index 88ff70233..7942f30d6 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -7,6 +7,7 @@
7#include <cinttypes> 7#include <cinttypes>
8#include <limits> 8#include <limits>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/uint128.h"
10 11
11namespace Core::Timing { 12namespace Core::Timing {
12 13
@@ -60,4 +61,9 @@ s64 nsToCycles(u64 ns) {
60 return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000; 61 return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
61} 62}
62 63
64u64 CpuCyclesToClockCycles(u64 ticks) {
65 const u128 temporal = Common::Multiply64Into128(ticks, CNTFREQ);
66 return Common::Divide128On32(temporal, static_cast<u32>(BASE_CLOCK_RATE)).first;
67}
68
63} // namespace Core::Timing 69} // namespace Core::Timing
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 513cfac1b..679aa3123 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -11,6 +11,7 @@ namespace Core::Timing {
11// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz 11// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
12// The exact value used is of course unverified. 12// The exact value used is of course unverified.
13constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked 13constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
14constexpr u64 CNTFREQ = 19200000; // Value from fusee.
14 15
15inline s64 msToCycles(int ms) { 16inline s64 msToCycles(int ms) {
16 // since ms is int there is no way to overflow 17 // since ms is int there is no way to overflow
@@ -61,4 +62,6 @@ inline u64 cyclesToMs(s64 cycles) {
61 return cycles * 1000 / BASE_CLOCK_RATE; 62 return cycles * 1000 / BASE_CLOCK_RATE;
62} 63}
63 64
65u64 CpuCyclesToClockCycles(u64 ticks);
66
64} // namespace Core::Timing 67} // namespace Core::Timing
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 079283830..68406eb63 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -19,9 +19,12 @@
19#include "core/hle/kernel/hle_ipc.h" 19#include "core/hle/kernel/hle_ipc.h"
20#include "core/hle/kernel/object.h" 20#include "core/hle/kernel/object.h"
21#include "core/hle/kernel/server_session.h" 21#include "core/hle/kernel/server_session.h"
22#include "core/hle/result.h"
22 23
23namespace IPC { 24namespace IPC {
24 25
26constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
27
25class RequestHelperBase { 28class RequestHelperBase {
26protected: 29protected:
27 Kernel::HLERequestContext* context = nullptr; 30 Kernel::HLERequestContext* context = nullptr;
@@ -272,6 +275,20 @@ inline void ResponseBuilder::Push(u64 value) {
272} 275}
273 276
274template <> 277template <>
278inline void ResponseBuilder::Push(float value) {
279 u32 integral;
280 std::memcpy(&integral, &value, sizeof(u32));
281 Push(integral);
282}
283
284template <>
285inline void ResponseBuilder::Push(double value) {
286 u64 integral;
287 std::memcpy(&integral, &value, sizeof(u64));
288 Push(integral);
289}
290
291template <>
275inline void ResponseBuilder::Push(bool value) { 292inline void ResponseBuilder::Push(bool value) {
276 Push(static_cast<u8>(value)); 293 Push(static_cast<u8>(value));
277} 294}
@@ -362,6 +379,11 @@ inline u32 RequestParser::Pop() {
362 return cmdbuf[index++]; 379 return cmdbuf[index++];
363} 380}
364 381
382template <>
383inline s32 RequestParser::Pop() {
384 return static_cast<s32>(Pop<u32>());
385}
386
365template <typename T> 387template <typename T>
366void RequestParser::PopRaw(T& value) { 388void RequestParser::PopRaw(T& value) {
367 std::memcpy(&value, cmdbuf + index, sizeof(T)); 389 std::memcpy(&value, cmdbuf + index, sizeof(T));
@@ -393,11 +415,37 @@ inline u64 RequestParser::Pop() {
393} 415}
394 416
395template <> 417template <>
418inline s8 RequestParser::Pop() {
419 return static_cast<s8>(Pop<u8>());
420}
421
422template <>
423inline s16 RequestParser::Pop() {
424 return static_cast<s16>(Pop<u16>());
425}
426
427template <>
396inline s64 RequestParser::Pop() { 428inline s64 RequestParser::Pop() {
397 return static_cast<s64>(Pop<u64>()); 429 return static_cast<s64>(Pop<u64>());
398} 430}
399 431
400template <> 432template <>
433inline float RequestParser::Pop() {
434 const u32 value = Pop<u32>();
435 float real;
436 std::memcpy(&real, &value, sizeof(real));
437 return real;
438}
439
440template <>
441inline double RequestParser::Pop() {
442 const u64 value = Pop<u64>();
443 float real;
444 std::memcpy(&real, &value, sizeof(real));
445 return real;
446}
447
448template <>
401inline bool RequestParser::Pop() { 449inline bool RequestParser::Pop() {
402 return Pop<u8>() != 0; 450 return Pop<u8>() != 0;
403} 451}
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index d4c91d529..aa432658e 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -33,10 +33,11 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
33 // Create a new session pair, let the created sessions inherit the parent port's HLE handler. 33 // Create a new session pair, let the created sessions inherit the parent port's HLE handler.
34 auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this); 34 auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
35 35
36 if (server_port->hle_handler) 36 if (server_port->HasHLEHandler()) {
37 server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); 37 server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
38 else 38 } else {
39 server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions)); 39 server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions));
40 }
40 41
41 // Wake the threads waiting on the ServerPort 42 // Wake the threads waiting on the ServerPort
42 server_port->WakeupAllWaitingThreads(); 43 server_port->WakeupAllWaitingThreads();
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 7e8ba978c..65c51003d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -31,7 +31,7 @@ namespace {
31 */ 31 */
32void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { 32void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
33 // Setup page table so we can write to memory 33 // Setup page table so we can write to memory
34 SetCurrentPageTable(&owner_process.VMManager().page_table); 34 Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
35 35
36 // Initialize new "main" thread 36 // Initialize new "main" thread
37 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 37 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
@@ -133,7 +133,7 @@ void Process::PrepareForTermination() {
133 if (thread->GetOwnerProcess() != this) 133 if (thread->GetOwnerProcess() != this)
134 continue; 134 continue;
135 135
136 if (thread == GetCurrentThread()) 136 if (thread == system.CurrentScheduler().GetCurrentThread())
137 continue; 137 continue;
138 138
139 // TODO(Subv): When are the other running/ready threads terminated? 139 // TODO(Subv): When are the other running/ready threads terminated?
@@ -145,7 +145,6 @@ void Process::PrepareForTermination() {
145 } 145 }
146 }; 146 };
147 147
148 const auto& system = Core::System::GetInstance();
149 stop_threads(system.Scheduler(0).GetThreadList()); 148 stop_threads(system.Scheduler(0).GetThreadList());
150 stop_threads(system.Scheduler(1).GetThreadList()); 149 stop_threads(system.Scheduler(1).GetThreadList());
151 stop_threads(system.Scheduler(2).GetThreadList()); 150 stop_threads(system.Scheduler(2).GetThreadList());
@@ -228,13 +227,11 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
228 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 227 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
229 228
230 // Clear instruction cache in CPU JIT 229 // Clear instruction cache in CPU JIT
231 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); 230 system.InvalidateCpuInstructionCaches();
232 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
233 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
234 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
235} 231}
236 232
237Process::Process(Core::System& system) : WaitObject{system.Kernel()}, address_arbiter{system} {} 233Process::Process(Core::System& system)
234 : WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {}
238Process::~Process() = default; 235Process::~Process() = default;
239 236
240void Process::Acquire(Thread* thread) { 237void Process::Acquire(Thread* thread) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 2a132c894..47ffd4ad3 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -266,7 +266,7 @@ public:
266 void FreeTLSSlot(VAddr tls_address); 266 void FreeTLSSlot(VAddr tls_address);
267 267
268private: 268private:
269 explicit Process(Core::System& kernel); 269 explicit Process(Core::System& system);
270 ~Process() override; 270 ~Process() override;
271 271
272 /// Checks if the specified thread should wait until this process is available. 272 /// Checks if the specified thread should wait until this process is available.
@@ -330,6 +330,10 @@ private:
330 /// Random values for svcGetInfo RandomEntropy 330 /// Random values for svcGetInfo RandomEntropy
331 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; 331 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
332 332
333 /// System context
334 Core::System& system;
335
336 /// Name of this process
333 std::string name; 337 std::string name;
334}; 338};
335 339
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 5fccfd9f4..cc189cc64 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -96,7 +96,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
96 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 96 auto* const thread_owner_process = current_thread->GetOwnerProcess();
97 if (previous_process != thread_owner_process) { 97 if (previous_process != thread_owner_process) {
98 system.Kernel().MakeCurrentProcess(thread_owner_process); 98 system.Kernel().MakeCurrentProcess(thread_owner_process);
99 SetCurrentPageTable(&thread_owner_process->VMManager().page_table); 99 Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
100 } 100 }
101 101
102 cpu_core.LoadContext(new_thread->GetContext()); 102 cpu_core.LoadContext(new_thread->GetContext());
@@ -199,8 +199,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
199 ASSERT(thread->GetPriority() < THREADPRIO_COUNT); 199 ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
200 200
201 // Yield this thread -- sleep for zero time and force reschedule to different thread 201 // Yield this thread -- sleep for zero time and force reschedule to different thread
202 WaitCurrentThread_Sleep(); 202 GetCurrentThread()->Sleep(0);
203 GetCurrentThread()->WakeAfterDelay(0);
204} 203}
205 204
206void Scheduler::YieldWithLoadBalancing(Thread* thread) { 205void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -215,8 +214,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
215 ASSERT(priority < THREADPRIO_COUNT); 214 ASSERT(priority < THREADPRIO_COUNT);
216 215
217 // Sleep for zero time to be able to force reschedule to different thread 216 // Sleep for zero time to be able to force reschedule to different thread
218 WaitCurrentThread_Sleep(); 217 GetCurrentThread()->Sleep(0);
219 GetCurrentThread()->WakeAfterDelay(0);
220 218
221 Thread* suggested_thread = nullptr; 219 Thread* suggested_thread = nullptr;
222 220
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index d6ceeb2da..0e1515c89 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -26,6 +26,10 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
26 return MakeResult(std::move(session)); 26 return MakeResult(std::move(session));
27} 27}
28 28
29void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) {
30 pending_sessions.push_back(std::move(pending_session));
31}
32
29bool ServerPort::ShouldWait(Thread* thread) const { 33bool ServerPort::ShouldWait(Thread* thread) const {
30 // If there are no pending sessions, we wait until a new one is added. 34 // If there are no pending sessions, we wait until a new one is added.
31 return pending_sessions.empty(); 35 return pending_sessions.empty();
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index e52f8245f..9bc667cf2 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -22,6 +22,8 @@ class SessionRequestHandler;
22 22
23class ServerPort final : public WaitObject { 23class ServerPort final : public WaitObject {
24public: 24public:
25 using HLEHandler = std::shared_ptr<SessionRequestHandler>;
26
25 /** 27 /**
26 * Creates a pair of ServerPort and an associated ClientPort. 28 * Creates a pair of ServerPort and an associated ClientPort.
27 * 29 *
@@ -51,22 +53,27 @@ public:
51 */ 53 */
52 ResultVal<SharedPtr<ServerSession>> Accept(); 54 ResultVal<SharedPtr<ServerSession>> Accept();
53 55
56 /// Whether or not this server port has an HLE handler available.
57 bool HasHLEHandler() const {
58 return hle_handler != nullptr;
59 }
60
61 /// Gets the HLE handler for this port.
62 HLEHandler GetHLEHandler() const {
63 return hle_handler;
64 }
65
54 /** 66 /**
55 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port 67 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
56 * will inherit a reference to this handler. 68 * will inherit a reference to this handler.
57 */ 69 */
58 void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { 70 void SetHleHandler(HLEHandler hle_handler_) {
59 hle_handler = std::move(hle_handler_); 71 hle_handler = std::move(hle_handler_);
60 } 72 }
61 73
62 std::string name; ///< Name of port (optional) 74 /// Appends a ServerSession to the collection of ServerSessions
63 75 /// waiting to be accepted by this port.
64 /// ServerSessions waiting to be accepted by the port 76 void AppendPendingSession(SharedPtr<ServerSession> pending_session);
65 std::vector<SharedPtr<ServerSession>> pending_sessions;
66
67 /// This session's HLE request handler template (optional)
68 /// ServerSessions created from this port inherit a reference to this handler.
69 std::shared_ptr<SessionRequestHandler> hle_handler;
70 77
71 bool ShouldWait(Thread* thread) const override; 78 bool ShouldWait(Thread* thread) const override;
72 void Acquire(Thread* thread) override; 79 void Acquire(Thread* thread) override;
@@ -74,6 +81,16 @@ public:
74private: 81private:
75 explicit ServerPort(KernelCore& kernel); 82 explicit ServerPort(KernelCore& kernel);
76 ~ServerPort() override; 83 ~ServerPort() override;
84
85 /// ServerSessions waiting to be accepted by the port
86 std::vector<SharedPtr<ServerSession>> pending_sessions;
87
88 /// This session's HLE request handler template (optional)
89 /// ServerSessions created from this port inherit a reference to this handler.
90 HLEHandler hle_handler;
91
92 /// Name of the port (optional)
93 std::string name;
77}; 94};
78 95
79} // namespace Kernel 96} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 77d0e3d96..047fa0c19 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1284,10 +1284,14 @@ static ResultCode StartThread(Handle thread_handle) {
1284 1284
1285/// Called when a thread exits 1285/// Called when a thread exits
1286static void ExitThread() { 1286static void ExitThread() {
1287 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); 1287 auto& system = Core::System::GetInstance();
1288 1288
1289 ExitCurrentThread(); 1289 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1290 Core::System::GetInstance().PrepareReschedule(); 1290
1291 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
1292 current_thread->Stop();
1293 system.CurrentScheduler().RemoveThread(current_thread);
1294 system.PrepareReschedule();
1291} 1295}
1292 1296
1293/// Sleep the current thread 1297/// Sleep the current thread
@@ -1300,32 +1304,32 @@ static void SleepThread(s64 nanoseconds) {
1300 YieldAndWaitForLoadBalancing = -2, 1304 YieldAndWaitForLoadBalancing = -2,
1301 }; 1305 };
1302 1306
1307 auto& system = Core::System::GetInstance();
1308 auto& scheduler = system.CurrentScheduler();
1309 auto* const current_thread = scheduler.GetCurrentThread();
1310
1303 if (nanoseconds <= 0) { 1311 if (nanoseconds <= 0) {
1304 auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
1305 switch (static_cast<SleepType>(nanoseconds)) { 1312 switch (static_cast<SleepType>(nanoseconds)) {
1306 case SleepType::YieldWithoutLoadBalancing: 1313 case SleepType::YieldWithoutLoadBalancing:
1307 scheduler.YieldWithoutLoadBalancing(GetCurrentThread()); 1314 scheduler.YieldWithoutLoadBalancing(current_thread);
1308 break; 1315 break;
1309 case SleepType::YieldWithLoadBalancing: 1316 case SleepType::YieldWithLoadBalancing:
1310 scheduler.YieldWithLoadBalancing(GetCurrentThread()); 1317 scheduler.YieldWithLoadBalancing(current_thread);
1311 break; 1318 break;
1312 case SleepType::YieldAndWaitForLoadBalancing: 1319 case SleepType::YieldAndWaitForLoadBalancing:
1313 scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); 1320 scheduler.YieldAndWaitForLoadBalancing(current_thread);
1314 break; 1321 break;
1315 default: 1322 default:
1316 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); 1323 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1317 } 1324 }
1318 } else { 1325 } else {
1319 // Sleep current thread and check for next thread to schedule 1326 current_thread->Sleep(nanoseconds);
1320 WaitCurrentThread_Sleep();
1321
1322 // Create an event to wake the thread up after the specified nanosecond delay has passed
1323 GetCurrentThread()->WakeAfterDelay(nanoseconds);
1324 } 1327 }
1325 1328
1326 // Reschedule all CPU cores 1329 // Reschedule all CPU cores
1327 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) 1330 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
1328 Core::System::GetInstance().CpuCore(i).PrepareReschedule(); 1331 system.CpuCore(i).PrepareReschedule();
1332 }
1329} 1333}
1330 1334
1331/// Wait process wide key atomic 1335/// Wait process wide key atomic
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 202997d20..d9ffebc3f 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -66,17 +66,6 @@ void Thread::Stop() {
66 owner_process->FreeTLSSlot(tls_address); 66 owner_process->FreeTLSSlot(tls_address);
67} 67}
68 68
69void WaitCurrentThread_Sleep() {
70 Thread* thread = GetCurrentThread();
71 thread->SetStatus(ThreadStatus::WaitSleep);
72}
73
74void ExitCurrentThread() {
75 Thread* thread = GetCurrentThread();
76 thread->Stop();
77 Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
78}
79
80void Thread::WakeAfterDelay(s64 nanoseconds) { 69void Thread::WakeAfterDelay(s64 nanoseconds) {
81 // Don't schedule a wakeup if the thread wants to wait forever 70 // Don't schedule a wakeup if the thread wants to wait forever
82 if (nanoseconds == -1) 71 if (nanoseconds == -1)
@@ -405,6 +394,14 @@ void Thread::SetActivity(ThreadActivity value) {
405 } 394 }
406} 395}
407 396
397void Thread::Sleep(s64 nanoseconds) {
398 // Sleep current thread and check for next thread to schedule
399 SetStatus(ThreadStatus::WaitSleep);
400
401 // Create an event to wake the thread up after the specified nanosecond delay has passed
402 WakeAfterDelay(nanoseconds);
403}
404
408//////////////////////////////////////////////////////////////////////////////////////////////////// 405////////////////////////////////////////////////////////////////////////////////////////////////////
409 406
410/** 407/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 96d9982d8..faad5f391 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -383,6 +383,9 @@ public:
383 383
384 void SetActivity(ThreadActivity value); 384 void SetActivity(ThreadActivity value);
385 385
386 /// Sleeps this thread for the given amount of nanoseconds.
387 void Sleep(s64 nanoseconds);
388
386private: 389private:
387 explicit Thread(KernelCore& kernel); 390 explicit Thread(KernelCore& kernel);
388 ~Thread() override; 391 ~Thread() override;
@@ -466,14 +469,4 @@ private:
466 */ 469 */
467Thread* GetCurrentThread(); 470Thread* GetCurrentThread();
468 471
469/**
470 * Waits the current thread on a sleep
471 */
472void WaitCurrentThread_Sleep();
473
474/**
475 * Stops the current thread and removes it from the thread_list
476 */
477void ExitCurrentThread();
478
479} // namespace Kernel 472} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 05c59af34..3def3e52c 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -7,13 +7,13 @@
7#include <utility> 7#include <utility>
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/memory_hook.h"
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/file_sys/program_metadata.h" 13#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/errors.h" 14#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/vm_manager.h" 15#include "core/hle/kernel/vm_manager.h"
15#include "core/memory.h" 16#include "core/memory.h"
16#include "core/memory_hook.h"
17#include "core/memory_setup.h" 17#include "core/memory_setup.h"
18 18
19namespace Kernel { 19namespace Kernel {
@@ -177,7 +177,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
177 177
178ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, 178ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
179 MemoryState state, 179 MemoryState state,
180 Memory::MemoryHookPointer mmio_handler) { 180 Common::MemoryHookPointer mmio_handler) {
181 // This is the appropriately sized VMA that will turn into our allocation. 181 // This is the appropriately sized VMA that will turn into our allocation.
182 CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); 182 CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
183 VirtualMemoryArea& final_vma = vma_handle->second; 183 VirtualMemoryArea& final_vma = vma_handle->second;
@@ -624,7 +624,7 @@ void VMManager::ClearPageTable() {
624 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); 624 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
625 page_table.special_regions.clear(); 625 page_table.special_regions.clear();
626 std::fill(page_table.attributes.begin(), page_table.attributes.end(), 626 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
627 Memory::PageType::Unmapped); 627 Common::PageType::Unmapped);
628} 628}
629 629
630VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask, 630VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 88e0b3c02..b96980f8f 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,9 +9,10 @@
9#include <tuple> 9#include <tuple>
10#include <vector> 10#include <vector>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/memory_hook.h"
13#include "common/page_table.h"
12#include "core/hle/result.h" 14#include "core/hle/result.h"
13#include "core/memory.h" 15#include "core/memory.h"
14#include "core/memory_hook.h"
15 16
16namespace FileSys { 17namespace FileSys {
17enum class ProgramAddressSpaceType : u8; 18enum class ProgramAddressSpaceType : u8;
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
290 // Settings for type = MMIO 291 // Settings for type = MMIO
291 /// Physical address of the register area this VMA maps to. 292 /// Physical address of the register area this VMA maps to.
292 PAddr paddr = 0; 293 PAddr paddr = 0;
293 Memory::MemoryHookPointer mmio_handler = nullptr; 294 Common::MemoryHookPointer mmio_handler = nullptr;
294 295
295 /// Tests if this area can be merged to the right with `next`. 296 /// Tests if this area can be merged to the right with `next`.
296 bool CanBeMergedWith(const VirtualMemoryArea& next) const; 297 bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -368,7 +369,7 @@ public:
368 * @param mmio_handler The handler that will implement read and write for this MMIO region. 369 * @param mmio_handler The handler that will implement read and write for this MMIO region.
369 */ 370 */
370 ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, 371 ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
371 Memory::MemoryHookPointer mmio_handler); 372 Common::MemoryHookPointer mmio_handler);
372 373
373 /// Unmaps a range of addresses, splitting VMAs as necessary. 374 /// Unmaps a range of addresses, splitting VMAs as necessary.
374 ResultCode UnmapRange(VAddr target, u64 size); 375 ResultCode UnmapRange(VAddr target, u64 size);
@@ -509,7 +510,7 @@ public:
509 510
510 /// Each VMManager has its own page table, which is set as the main one when the owning process 511 /// Each VMManager has its own page table, which is set as the main one when the owning process
511 /// is scheduled. 512 /// is scheduled.
512 Memory::PageTable page_table; 513 Common::PageTable page_table{Memory::PAGE_BITS};
513 514
514private: 515private:
515 using VMAIter = VMAMap::iterator; 516 using VMAIter = VMAMap::iterator;
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 1ed144481..ab84f5ddc 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -13,14 +13,6 @@
13// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes 13// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
14 14
15/** 15/**
16 * Detailed description of the error. Code 0 always means success.
17 */
18enum class ErrorDescription : u32 {
19 Success = 0,
20 RemoteProcessDead = 301,
21};
22
23/**
24 * Identifies the module which caused the error. Error codes can be propagated through a call 16 * Identifies the module which caused the error. Error codes can be propagated through a call
25 * chain, meaning that this doesn't always correspond to the module where the API call made is 17 * chain, meaning that this doesn't always correspond to the module where the API call made is
26 * contained. 18 * contained.
@@ -120,7 +112,7 @@ enum class ErrorModule : u32 {
120 ShopN = 811, 112 ShopN = 811,
121}; 113};
122 114
123/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields. 115/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
124union ResultCode { 116union ResultCode {
125 u32 raw; 117 u32 raw;
126 118
@@ -133,17 +125,9 @@ union ResultCode {
133 125
134 constexpr explicit ResultCode(u32 raw) : raw(raw) {} 126 constexpr explicit ResultCode(u32 raw) : raw(raw) {}
135 127
136 constexpr ResultCode(ErrorModule module, ErrorDescription description)
137 : ResultCode(module, static_cast<u32>(description)) {}
138
139 constexpr ResultCode(ErrorModule module_, u32 description_) 128 constexpr ResultCode(ErrorModule module_, u32 description_)
140 : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} 129 : raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
141 130
142 constexpr ResultCode& operator=(const ResultCode& o) {
143 raw = o.raw;
144 return *this;
145 }
146
147 constexpr bool IsSuccess() const { 131 constexpr bool IsSuccess() const {
148 return raw == 0; 132 return raw == 0;
149 } 133 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index a34b9e753..b031ebc66 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -10,6 +10,7 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 11#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
12#include "core/hle/service/nvdrv/devices/nvmap.h" 12#include "core/hle/service/nvdrv/devices/nvmap.h"
13#include "core/memory.h"
13#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.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"
@@ -178,7 +179,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
178 auto& gpu = system_instance.GPU(); 179 auto& gpu = system_instance.GPU();
179 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); 180 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
180 ASSERT(cpu_addr); 181 ASSERT(cpu_addr);
181 gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size); 182 gpu.FlushAndInvalidateRegion(ToCacheAddr(Memory::GetPointer(*cpu_addr)), itr->second.size);
182 183
183 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 184 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
184 185
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 576fd6407..00806b0ed 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,7 +11,6 @@
11#include "core/hle/ipc.h" 11#include "core/hle/ipc.h"
12#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/client_port.h" 13#include "core/hle/kernel/client_port.h"
14#include "core/hle/kernel/handle_table.h"
15#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/server_port.h" 16#include "core/hle/kernel/server_port.h"
@@ -168,7 +167,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
168 case IPC::CommandType::Close: { 167 case IPC::CommandType::Close: {
169 IPC::ResponseBuilder rb{context, 2}; 168 IPC::ResponseBuilder rb{context, 2};
170 rb.Push(RESULT_SUCCESS); 169 rb.Push(RESULT_SUCCESS);
171 return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead); 170 return IPC::ERR_REMOTE_PROCESS_DEAD;
172 } 171 }
173 case IPC::CommandType::ControlWithContext: 172 case IPC::CommandType::ControlWithContext:
174 case IPC::CommandType::Control: { 173 case IPC::CommandType::Control: {
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index bef25433e..b9d6381b4 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -67,7 +67,7 @@ public:
67 if (port == nullptr) { 67 if (port == nullptr) {
68 return nullptr; 68 return nullptr;
69 } 69 }
70 return std::static_pointer_cast<T>(port->hle_handler); 70 return std::static_pointer_cast<T>(port->GetHLEHandler());
71 } 71 }
72 72
73 void InvokeControlRequest(Kernel::HLERequestContext& context); 73 void InvokeControlRequest(Kernel::HLERequestContext& context);
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index a975767bb..566cd6006 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -24,6 +24,7 @@
24#include "core/hle/service/nvdrv/nvdrv.h" 24#include "core/hle/service/nvdrv/nvdrv.h"
25#include "core/hle/service/nvflinger/buffer_queue.h" 25#include "core/hle/service/nvflinger/buffer_queue.h"
26#include "core/hle/service/nvflinger/nvflinger.h" 26#include "core/hle/service/nvflinger/nvflinger.h"
27#include "core/hle/service/service.h"
27#include "core/hle/service/vi/vi.h" 28#include "core/hle/service/vi/vi.h"
28#include "core/hle/service/vi/vi_m.h" 29#include "core/hle/service/vi/vi_m.h"
29#include "core/hle/service/vi/vi_s.h" 30#include "core/hle/service/vi/vi_s.h"
@@ -33,6 +34,7 @@
33namespace Service::VI { 34namespace Service::VI {
34 35
35constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1}; 36constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
37constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
36constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6}; 38constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
37constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7}; 39constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
38 40
@@ -1203,26 +1205,40 @@ IApplicationDisplayService::IApplicationDisplayService(
1203 RegisterHandlers(functions); 1205 RegisterHandlers(functions);
1204} 1206}
1205 1207
1206Module::Interface::Interface(std::shared_ptr<Module> module, const char* name, 1208static bool IsValidServiceAccess(Permission permission, Policy policy) {
1207 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 1209 if (permission == Permission::User) {
1208 : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} 1210 return policy == Policy::User;
1211 }
1212
1213 if (permission == Permission::System || permission == Permission::Manager) {
1214 return policy == Policy::User || policy == Policy::Compositor;
1215 }
1209 1216
1210Module::Interface::~Interface() = default; 1217 return false;
1218}
1211 1219
1212void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { 1220void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
1213 LOG_WARNING(Service_VI, "(STUBBED) called"); 1221 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
1222 Permission permission) {
1223 IPC::RequestParser rp{ctx};
1224 const auto policy = rp.PopEnum<Policy>();
1225
1226 if (!IsValidServiceAccess(permission, policy)) {
1227 IPC::ResponseBuilder rb{ctx, 2};
1228 rb.Push(ERR_PERMISSION_DENIED);
1229 return;
1230 }
1214 1231
1215 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1232 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1216 rb.Push(RESULT_SUCCESS); 1233 rb.Push(RESULT_SUCCESS);
1217 rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); 1234 rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger));
1218} 1235}
1219 1236
1220void InstallInterfaces(SM::ServiceManager& service_manager, 1237void InstallInterfaces(SM::ServiceManager& service_manager,
1221 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) { 1238 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
1222 auto module = std::make_shared<Module>(); 1239 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
1223 std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager); 1240 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
1224 std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager); 1241 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
1225 std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
1226} 1242}
1227 1243
1228} // namespace Service::VI 1244} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index e3963502a..6b66f8b81 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -4,12 +4,21 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/service.h" 7#include <memory>
8#include "common/common_types.h"
9
10namespace Kernel {
11class HLERequestContext;
12}
8 13
9namespace Service::NVFlinger { 14namespace Service::NVFlinger {
10class NVFlinger; 15class NVFlinger;
11} 16}
12 17
18namespace Service::SM {
19class ServiceManager;
20}
21
13namespace Service::VI { 22namespace Service::VI {
14 23
15enum class DisplayResolution : u32 { 24enum class DisplayResolution : u32 {
@@ -19,22 +28,25 @@ enum class DisplayResolution : u32 {
19 UndockedHeight = 720, 28 UndockedHeight = 720,
20}; 29};
21 30
22class Module final { 31/// Permission level for a particular VI service instance
23public: 32enum class Permission {
24 class Interface : public ServiceFramework<Interface> { 33 User,
25 public: 34 System,
26 explicit Interface(std::shared_ptr<Module> module, const char* name, 35 Manager,
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 36};
28 ~Interface() override;
29
30 void GetDisplayService(Kernel::HLERequestContext& ctx);
31 37
32 protected: 38/// A policy type that may be requested via GetDisplayService and
33 std::shared_ptr<Module> module; 39/// GetDisplayServiceWithProxyNameExchange
34 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 40enum class Policy {
35 }; 41 User,
42 Compositor,
36}; 43};
37 44
45namespace detail {
46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
47 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission);
48} // namespace detail
49
38/// Registers all VI services with the specified service manager. 50/// Registers all VI services with the specified service manager.
39void InstallInterfaces(SM::ServiceManager& service_manager, 51void InstallInterfaces(SM::ServiceManager& service_manager,
40 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 52 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 207c06b16..06070087f 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -2,12 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/service/vi/vi.h"
5#include "core/hle/service/vi/vi_m.h" 7#include "core/hle/service/vi/vi_m.h"
6 8
7namespace Service::VI { 9namespace Service::VI {
8 10
9VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
10 : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) { 12 : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 14 {2, &VI_M::GetDisplayService, "GetDisplayService"},
13 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
17 19
18VI_M::~VI_M() = default; 20VI_M::~VI_M() = default;
19 21
22void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called");
24
25 detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::Manager);
26}
27
20} // namespace Service::VI 28} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 487d58d50..290e06689 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -4,14 +4,27 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/vi/vi.h" 7#include "core/hle/service/service.h"
8
9namespace Kernel {
10class HLERequestContext;
11}
12
13namespace Service::NVFlinger {
14class NVFlinger;
15}
8 16
9namespace Service::VI { 17namespace Service::VI {
10 18
11class VI_M final : public Module::Interface { 19class VI_M final : public ServiceFramework<VI_M> {
12public: 20public:
13 explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_M() override; 22 ~VI_M() override;
23
24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
15}; 28};
16 29
17} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 920e6a1f6..57c596cc4 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -2,12 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/service/vi/vi.h"
5#include "core/hle/service/vi/vi_s.h" 7#include "core/hle/service/vi/vi_s.h"
6 8
7namespace Service::VI { 9namespace Service::VI {
8 10
9VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
10 : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) { 12 : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 14 {1, &VI_S::GetDisplayService, "GetDisplayService"},
13 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
17 19
18VI_S::~VI_S() = default; 20VI_S::~VI_S() = default;
19 21
22void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called");
24
25 detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::System);
26}
27
20} // namespace Service::VI 28} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index bbc31148f..47804dc0b 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -4,14 +4,27 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/vi/vi.h" 7#include "core/hle/service/service.h"
8
9namespace Kernel {
10class HLERequestContext;
11}
12
13namespace Service::NVFlinger {
14class NVFlinger;
15}
8 16
9namespace Service::VI { 17namespace Service::VI {
10 18
11class VI_S final : public Module::Interface { 19class VI_S final : public ServiceFramework<VI_S> {
12public: 20public:
13 explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_S() override; 22 ~VI_S() override;
23
24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
15}; 28};
16 29
17} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index d81e410d6..9d5ceb608 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -2,12 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/service/vi/vi.h"
5#include "core/hle/service/vi/vi_u.h" 7#include "core/hle/service/vi/vi_u.h"
6 8
7namespace Service::VI { 9namespace Service::VI {
8 10
9VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
10 : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) { 12 : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 14 {0, &VI_U::GetDisplayService, "GetDisplayService"},
13 }; 15 };
@@ -16,4 +18,10 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
16 18
17VI_U::~VI_U() = default; 19VI_U::~VI_U() = default;
18 20
21void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
22 LOG_DEBUG(Service_VI, "called");
23
24 detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::User);
25}
26
19} // namespace Service::VI 27} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index b92f28c92..19bdb73b0 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -4,14 +4,27 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/vi/vi.h" 7#include "core/hle/service/service.h"
8
9namespace Kernel {
10class HLERequestContext;
11}
12
13namespace Service::NVFlinger {
14class NVFlinger;
15}
8 16
9namespace Service::VI { 17namespace Service::VI {
10 18
11class VI_U final : public Module::Interface { 19class VI_U final : public ServiceFramework<VI_U> {
12public: 20public:
13 explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_U() override; 22 ~VI_U() override;
23
24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
15}; 28};
16 29
17} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 6591c45d2..365ac82b4 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/page_table.h"
13#include "common/swap.h" 14#include "common/swap.h"
14#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
15#include "core/core.h" 16#include "core/core.h"
@@ -18,13 +19,14 @@
18#include "core/hle/lock.h" 19#include "core/hle/lock.h"
19#include "core/memory.h" 20#include "core/memory.h"
20#include "core/memory_setup.h" 21#include "core/memory_setup.h"
22#include "video_core/gpu.h"
21#include "video_core/renderer_base.h" 23#include "video_core/renderer_base.h"
22 24
23namespace Memory { 25namespace Memory {
24 26
25static PageTable* current_page_table = nullptr; 27static Common::PageTable* current_page_table = nullptr;
26 28
27void SetCurrentPageTable(PageTable* page_table) { 29void SetCurrentPageTable(Common::PageTable* page_table) {
28 current_page_table = page_table; 30 current_page_table = page_table;
29 31
30 auto& system = Core::System::GetInstance(); 32 auto& system = Core::System::GetInstance();
@@ -36,39 +38,20 @@ void SetCurrentPageTable(PageTable* page_table) {
36 } 38 }
37} 39}
38 40
39PageTable* GetCurrentPageTable() { 41Common::PageTable* GetCurrentPageTable() {
40 return current_page_table; 42 return current_page_table;
41} 43}
42 44
43PageTable::PageTable() = default; 45static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
44 46 Common::PageType type) {
45PageTable::PageTable(std::size_t address_space_width_in_bits) {
46 Resize(address_space_width_in_bits);
47}
48
49PageTable::~PageTable() = default;
50
51void PageTable::Resize(std::size_t address_space_width_in_bits) {
52 const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
53
54 pointers.resize(num_page_table_entries);
55 attributes.resize(num_page_table_entries);
56
57 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
58 // vector size is subsequently decreased (via resize), the vector might not automatically
59 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
60 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
61
62 pointers.shrink_to_fit();
63 attributes.shrink_to_fit();
64}
65
66static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
67 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, 47 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
68 (base + size) * PAGE_SIZE); 48 (base + size) * PAGE_SIZE);
69 49
70 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, 50 // During boot, current_page_table might not be set yet, in which case we need not flush
71 FlushMode::FlushAndInvalidate); 51 if (current_page_table) {
52 Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
53 size * PAGE_SIZE);
54 }
72 55
73 VAddr end = base + size; 56 VAddr end = base + size;
74 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", 57 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
@@ -88,41 +71,47 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
88 } 71 }
89} 72}
90 73
91void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) { 74void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
92 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 75 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
93 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 76 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
94 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); 77 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
95} 78}
96 79
97void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) { 80void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
81 Common::MemoryHookPointer mmio_handler) {
98 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 82 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
99 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 83 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
100 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); 84 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
101 85
102 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 86 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
103 SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)}; 87 Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
104 page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region})); 88 page_table.special_regions.add(
89 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
105} 90}
106 91
107void UnmapRegion(PageTable& page_table, VAddr base, u64 size) { 92void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
108 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 93 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
109 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 94 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
110 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); 95 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
111 96
112 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 97 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
113 page_table.special_regions.erase(interval); 98 page_table.special_regions.erase(interval);
114} 99}
115 100
116void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) { 101void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
102 Common::MemoryHookPointer hook) {
117 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 103 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
118 SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)}; 104 Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
119 page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region})); 105 page_table.special_regions.add(
106 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
120} 107}
121 108
122void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) { 109void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
110 Common::MemoryHookPointer hook) {
123 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 111 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
124 SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)}; 112 Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
125 page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region})); 113 page_table.special_regions.subtract(
114 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
126} 115}
127 116
128/** 117/**
@@ -171,19 +160,19 @@ T Read(const VAddr vaddr) {
171 return value; 160 return value;
172 } 161 }
173 162
174 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 163 Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
175 switch (type) { 164 switch (type) {
176 case PageType::Unmapped: 165 case Common::PageType::Unmapped:
177 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 166 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
178 return 0; 167 return 0;
179 case PageType::Memory: 168 case Common::PageType::Memory:
180 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 169 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
181 break; 170 break;
182 case PageType::RasterizerCachedMemory: { 171 case Common::PageType::RasterizerCachedMemory: {
183 RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Flush); 172 auto host_ptr{GetPointerFromVMA(vaddr)};
184 173 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
185 T value; 174 T value;
186 std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T)); 175 std::memcpy(&value, host_ptr, sizeof(T));
187 return value; 176 return value;
188 } 177 }
189 default: 178 default:
@@ -201,18 +190,19 @@ void Write(const VAddr vaddr, const T data) {
201 return; 190 return;
202 } 191 }
203 192
204 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 193 Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
205 switch (type) { 194 switch (type) {
206 case PageType::Unmapped: 195 case Common::PageType::Unmapped:
207 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 196 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
208 static_cast<u32>(data), vaddr); 197 static_cast<u32>(data), vaddr);
209 return; 198 return;
210 case PageType::Memory: 199 case Common::PageType::Memory:
211 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 200 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
212 break; 201 break;
213 case PageType::RasterizerCachedMemory: { 202 case Common::PageType::RasterizerCachedMemory: {
214 RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate); 203 auto host_ptr{GetPointerFromVMA(vaddr)};
215 std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T)); 204 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
205 std::memcpy(host_ptr, &data, sizeof(T));
216 break; 206 break;
217 } 207 }
218 default: 208 default:
@@ -227,10 +217,10 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
227 if (page_pointer) 217 if (page_pointer)
228 return true; 218 return true;
229 219
230 if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) 220 if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
231 return true; 221 return true;
232 222
233 if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special) 223 if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
234 return false; 224 return false;
235 225
236 return false; 226 return false;
@@ -250,7 +240,8 @@ u8* GetPointer(const VAddr vaddr) {
250 return page_pointer + (vaddr & PAGE_MASK); 240 return page_pointer + (vaddr & PAGE_MASK);
251 } 241 }
252 242
253 if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) { 243 if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
244 Common::PageType::RasterizerCachedMemory) {
254 return GetPointerFromVMA(vaddr); 245 return GetPointerFromVMA(vaddr);
255 } 246 }
256 247
@@ -284,20 +275,20 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
284 275
285 u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; 276 u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
286 for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { 277 for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
287 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; 278 Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
288 279
289 if (cached) { 280 if (cached) {
290 // Switch page type to cached if now cached 281 // Switch page type to cached if now cached
291 switch (page_type) { 282 switch (page_type) {
292 case PageType::Unmapped: 283 case Common::PageType::Unmapped:
293 // It is not necessary for a process to have this region mapped into its address 284 // It is not necessary for a process to have this region mapped into its address
294 // space, for example, a system module need not have a VRAM mapping. 285 // space, for example, a system module need not have a VRAM mapping.
295 break; 286 break;
296 case PageType::Memory: 287 case Common::PageType::Memory:
297 page_type = PageType::RasterizerCachedMemory; 288 page_type = Common::PageType::RasterizerCachedMemory;
298 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; 289 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
299 break; 290 break;
300 case PageType::RasterizerCachedMemory: 291 case Common::PageType::RasterizerCachedMemory:
301 // There can be more than one GPU region mapped per CPU region, so it's common that 292 // There can be more than one GPU region mapped per CPU region, so it's common that
302 // this area is already marked as cached. 293 // this area is already marked as cached.
303 break; 294 break;
@@ -307,23 +298,23 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
307 } else { 298 } else {
308 // Switch page type to uncached if now uncached 299 // Switch page type to uncached if now uncached
309 switch (page_type) { 300 switch (page_type) {
310 case PageType::Unmapped: 301 case Common::PageType::Unmapped:
311 // It is not necessary for a process to have this region mapped into its address 302 // It is not necessary for a process to have this region mapped into its address
312 // space, for example, a system module need not have a VRAM mapping. 303 // space, for example, a system module need not have a VRAM mapping.
313 break; 304 break;
314 case PageType::Memory: 305 case Common::PageType::Memory:
315 // There can be more than one GPU region mapped per CPU region, so it's common that 306 // There can be more than one GPU region mapped per CPU region, so it's common that
316 // this area is already unmarked as cached. 307 // this area is already unmarked as cached.
317 break; 308 break;
318 case PageType::RasterizerCachedMemory: { 309 case Common::PageType::RasterizerCachedMemory: {
319 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); 310 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
320 if (pointer == nullptr) { 311 if (pointer == nullptr) {
321 // It's possible that this function has been called while updating the pagetable 312 // It's possible that this function has been called while updating the pagetable
322 // after unmapping a VMA. In that case the underlying VMA will no longer exist, 313 // after unmapping a VMA. In that case the underlying VMA will no longer exist,
323 // and we should just leave the pagetable entry blank. 314 // and we should just leave the pagetable entry blank.
324 page_type = PageType::Unmapped; 315 page_type = Common::PageType::Unmapped;
325 } else { 316 } else {
326 page_type = PageType::Memory; 317 page_type = Common::PageType::Memory;
327 current_page_table->pointers[vaddr >> PAGE_BITS] = pointer; 318 current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
328 } 319 }
329 break; 320 break;
@@ -335,47 +326,6 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
335 } 326 }
336} 327}
337 328
338void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
339 auto& system_instance = Core::System::GetInstance();
340
341 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
342 // null here
343 if (!system_instance.IsPoweredOn()) {
344 return;
345 }
346
347 const VAddr end = start + size;
348
349 const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
350 if (start >= region_end || end <= region_start) {
351 // No overlap with region
352 return;
353 }
354
355 const VAddr overlap_start = std::max(start, region_start);
356 const VAddr overlap_end = std::min(end, region_end);
357 const VAddr overlap_size = overlap_end - overlap_start;
358
359 auto& gpu = system_instance.GPU();
360 switch (mode) {
361 case FlushMode::Flush:
362 gpu.FlushRegion(overlap_start, overlap_size);
363 break;
364 case FlushMode::Invalidate:
365 gpu.InvalidateRegion(overlap_start, overlap_size);
366 break;
367 case FlushMode::FlushAndInvalidate:
368 gpu.FlushAndInvalidateRegion(overlap_start, overlap_size);
369 break;
370 }
371 };
372
373 const auto& vm_manager = Core::CurrentProcess()->VMManager();
374
375 CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
376 CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
377}
378
379u8 Read8(const VAddr addr) { 329u8 Read8(const VAddr addr) {
380 return Read<u8>(addr); 330 return Read<u8>(addr);
381} 331}
@@ -406,24 +356,24 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
406 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 356 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
407 357
408 switch (page_table.attributes[page_index]) { 358 switch (page_table.attributes[page_index]) {
409 case PageType::Unmapped: { 359 case Common::PageType::Unmapped: {
410 LOG_ERROR(HW_Memory, 360 LOG_ERROR(HW_Memory,
411 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 361 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
412 current_vaddr, src_addr, size); 362 current_vaddr, src_addr, size);
413 std::memset(dest_buffer, 0, copy_amount); 363 std::memset(dest_buffer, 0, copy_amount);
414 break; 364 break;
415 } 365 }
416 case PageType::Memory: { 366 case Common::PageType::Memory: {
417 DEBUG_ASSERT(page_table.pointers[page_index]); 367 DEBUG_ASSERT(page_table.pointers[page_index]);
418 368
419 const u8* src_ptr = page_table.pointers[page_index] + page_offset; 369 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
420 std::memcpy(dest_buffer, src_ptr, copy_amount); 370 std::memcpy(dest_buffer, src_ptr, copy_amount);
421 break; 371 break;
422 } 372 }
423 case PageType::RasterizerCachedMemory: { 373 case Common::PageType::RasterizerCachedMemory: {
424 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 374 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
425 FlushMode::Flush); 375 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
426 std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount); 376 std::memcpy(dest_buffer, host_ptr, copy_amount);
427 break; 377 break;
428 } 378 }
429 default: 379 default:
@@ -470,23 +420,23 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
470 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 420 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
471 421
472 switch (page_table.attributes[page_index]) { 422 switch (page_table.attributes[page_index]) {
473 case PageType::Unmapped: { 423 case Common::PageType::Unmapped: {
474 LOG_ERROR(HW_Memory, 424 LOG_ERROR(HW_Memory,
475 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 425 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
476 current_vaddr, dest_addr, size); 426 current_vaddr, dest_addr, size);
477 break; 427 break;
478 } 428 }
479 case PageType::Memory: { 429 case Common::PageType::Memory: {
480 DEBUG_ASSERT(page_table.pointers[page_index]); 430 DEBUG_ASSERT(page_table.pointers[page_index]);
481 431
482 u8* dest_ptr = page_table.pointers[page_index] + page_offset; 432 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
483 std::memcpy(dest_ptr, src_buffer, copy_amount); 433 std::memcpy(dest_ptr, src_buffer, copy_amount);
484 break; 434 break;
485 } 435 }
486 case PageType::RasterizerCachedMemory: { 436 case Common::PageType::RasterizerCachedMemory: {
487 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 437 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
488 FlushMode::Invalidate); 438 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
489 std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount); 439 std::memcpy(host_ptr, src_buffer, copy_amount);
490 break; 440 break;
491 } 441 }
492 default: 442 default:
@@ -516,23 +466,23 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
516 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 466 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
517 467
518 switch (page_table.attributes[page_index]) { 468 switch (page_table.attributes[page_index]) {
519 case PageType::Unmapped: { 469 case Common::PageType::Unmapped: {
520 LOG_ERROR(HW_Memory, 470 LOG_ERROR(HW_Memory,
521 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 471 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
522 current_vaddr, dest_addr, size); 472 current_vaddr, dest_addr, size);
523 break; 473 break;
524 } 474 }
525 case PageType::Memory: { 475 case Common::PageType::Memory: {
526 DEBUG_ASSERT(page_table.pointers[page_index]); 476 DEBUG_ASSERT(page_table.pointers[page_index]);
527 477
528 u8* dest_ptr = page_table.pointers[page_index] + page_offset; 478 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
529 std::memset(dest_ptr, 0, copy_amount); 479 std::memset(dest_ptr, 0, copy_amount);
530 break; 480 break;
531 } 481 }
532 case PageType::RasterizerCachedMemory: { 482 case Common::PageType::RasterizerCachedMemory: {
533 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 483 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
534 FlushMode::Invalidate); 484 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
535 std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount); 485 std::memset(host_ptr, 0, copy_amount);
536 break; 486 break;
537 } 487 }
538 default: 488 default:
@@ -558,23 +508,23 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
558 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 508 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
559 509
560 switch (page_table.attributes[page_index]) { 510 switch (page_table.attributes[page_index]) {
561 case PageType::Unmapped: { 511 case Common::PageType::Unmapped: {
562 LOG_ERROR(HW_Memory, 512 LOG_ERROR(HW_Memory,
563 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 513 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
564 current_vaddr, src_addr, size); 514 current_vaddr, src_addr, size);
565 ZeroBlock(process, dest_addr, copy_amount); 515 ZeroBlock(process, dest_addr, copy_amount);
566 break; 516 break;
567 } 517 }
568 case PageType::Memory: { 518 case Common::PageType::Memory: {
569 DEBUG_ASSERT(page_table.pointers[page_index]); 519 DEBUG_ASSERT(page_table.pointers[page_index]);
570 const u8* src_ptr = page_table.pointers[page_index] + page_offset; 520 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
571 WriteBlock(process, dest_addr, src_ptr, copy_amount); 521 WriteBlock(process, dest_addr, src_ptr, copy_amount);
572 break; 522 break;
573 } 523 }
574 case PageType::RasterizerCachedMemory: { 524 case Common::PageType::RasterizerCachedMemory: {
575 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 525 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
576 FlushMode::Flush); 526 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
577 WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount); 527 WriteBlock(process, dest_addr, host_ptr, copy_amount);
578 break; 528 break;
579 } 529 }
580 default: 530 default:
diff --git a/src/core/memory.h b/src/core/memory.h
index 1acf5ce8c..3f60d868c 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -10,7 +10,10 @@
10#include <vector> 10#include <vector>
11#include <boost/icl/interval_map.hpp> 11#include <boost/icl/interval_map.hpp>
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/memory_hook.h" 13
14namespace Common {
15struct PageTable;
16}
14 17
15namespace Kernel { 18namespace Kernel {
16class Process; 19class Process;
@@ -26,71 +29,6 @@ constexpr std::size_t PAGE_BITS = 12;
26constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; 29constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
27constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 30constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
28 31
29enum class PageType : u8 {
30 /// Page is unmapped and should cause an access error.
31 Unmapped,
32 /// Page is mapped to regular memory. This is the only type you can get pointers to.
33 Memory,
34 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
35 /// invalidation
36 RasterizerCachedMemory,
37 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
38 Special,
39};
40
41struct SpecialRegion {
42 enum class Type {
43 DebugHook,
44 IODevice,
45 } type;
46
47 MemoryHookPointer handler;
48
49 bool operator<(const SpecialRegion& other) const {
50 return std::tie(type, handler) < std::tie(other.type, other.handler);
51 }
52
53 bool operator==(const SpecialRegion& other) const {
54 return std::tie(type, handler) == std::tie(other.type, other.handler);
55 }
56};
57
58/**
59 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
60 * mimics the way a real CPU page table works.
61 */
62struct PageTable {
63 explicit PageTable();
64 explicit PageTable(std::size_t address_space_width_in_bits);
65 ~PageTable();
66
67 /**
68 * Resizes the page table to be able to accomodate enough pages within
69 * a given address space.
70 *
71 * @param address_space_width_in_bits The address size width in bits.
72 */
73 void Resize(std::size_t address_space_width_in_bits);
74
75 /**
76 * Vector of memory pointers backing each page. An entry can only be non-null if the
77 * corresponding entry in the `attributes` vector is of type `Memory`.
78 */
79 std::vector<u8*> pointers;
80
81 /**
82 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
83 * of type `Special`.
84 */
85 boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
86
87 /**
88 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
89 * the corresponding entry in `pointers` MUST be set to null.
90 */
91 std::vector<PageType> attributes;
92};
93
94/// Virtual user-space memory regions 32/// Virtual user-space memory regions
95enum : VAddr { 33enum : VAddr {
96 /// Read-only page containing kernel and system configuration values. 34 /// Read-only page containing kernel and system configuration values.
@@ -116,8 +54,8 @@ enum : VAddr {
116}; 54};
117 55
118/// Currently active page table 56/// Currently active page table
119void SetCurrentPageTable(PageTable* page_table); 57void SetCurrentPageTable(Common::PageTable* page_table);
120PageTable* GetCurrentPageTable(); 58Common::PageTable* GetCurrentPageTable();
121 59
122/// Determines if the given VAddr is valid for the specified process. 60/// Determines if the given VAddr is valid for the specified process.
123bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); 61bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
@@ -161,10 +99,4 @@ enum class FlushMode {
161 */ 99 */
162void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached); 100void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
163 101
164/**
165 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
166 * address region.
167 */
168void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
169
170} // namespace Memory 102} // namespace Memory
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 9a1a4f4be..5225ee8e2 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -5,7 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/memory_hook.h" 8#include "common/memory_hook.h"
9
10namespace Common {
11struct PageTable;
12}
9 13
10namespace Memory { 14namespace Memory {
11 15
@@ -17,7 +21,7 @@ namespace Memory {
17 * @param size The amount of bytes to map. Must be page-aligned. 21 * @param size The amount of bytes to map. Must be page-aligned.
18 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. 22 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
19 */ 23 */
20void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target); 24void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
21 25
22/** 26/**
23 * Maps a region of the emulated process address space as a IO region. 27 * Maps a region of the emulated process address space as a IO region.
@@ -26,11 +30,14 @@ void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
26 * @param size The amount of bytes to map. Must be page-aligned. 30 * @param size The amount of bytes to map. Must be page-aligned.
27 * @param mmio_handler The handler that backs the mapping. 31 * @param mmio_handler The handler that backs the mapping.
28 */ 32 */
29void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler); 33void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
34 Common::MemoryHookPointer mmio_handler);
30 35
31void UnmapRegion(PageTable& page_table, VAddr base, u64 size); 36void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
32 37
33void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook); 38void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
34void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook); 39 Common::MemoryHookPointer hook);
40void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
41 Common::MemoryHookPointer hook);
35 42
36} // namespace Memory 43} // namespace Memory
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 1c7db28c0..5b4e032bd 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,15 +7,18 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 10 sdl/sdl.cpp
11 $<$<BOOL:${SDL2_FOUND}>:sdl/sdl.cpp sdl/sdl.h> 11 sdl/sdl.h
12) 12)
13 13
14create_target_directory_groups(input_common)
15
16target_link_libraries(input_common PUBLIC core PRIVATE common)
17
18if(SDL2_FOUND) 14if(SDL2_FOUND)
15 target_sources(input_common PRIVATE
16 sdl/sdl_impl.cpp
17 sdl/sdl_impl.h
18 )
19 target_link_libraries(input_common PRIVATE SDL2) 19 target_link_libraries(input_common PRIVATE SDL2)
20 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 20 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
21endif() 21endif()
22
23create_target_directory_groups(input_common)
24target_link_libraries(input_common PUBLIC core PRIVATE common)
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 37f572853..8e66c1b15 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -17,10 +17,7 @@ namespace InputCommon {
17 17
18static std::shared_ptr<Keyboard> keyboard; 18static std::shared_ptr<Keyboard> keyboard;
19static std::shared_ptr<MotionEmu> motion_emu; 19static std::shared_ptr<MotionEmu> motion_emu;
20 20static std::unique_ptr<SDL::State> sdl;
21#ifdef HAVE_SDL2
22static std::thread poll_thread;
23#endif
24 21
25void Init() { 22void Init() {
26 keyboard = std::make_shared<Keyboard>(); 23 keyboard = std::make_shared<Keyboard>();
@@ -30,15 +27,7 @@ void Init() {
30 motion_emu = std::make_shared<MotionEmu>(); 27 motion_emu = std::make_shared<MotionEmu>();
31 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu); 28 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
32 29
33#ifdef HAVE_SDL2 30 sdl = SDL::Init();
34 SDL::Init();
35#endif
36}
37
38void StartJoystickEventHandler() {
39#ifdef HAVE_SDL2
40 poll_thread = std::thread(SDL::PollLoop);
41#endif
42} 31}
43 32
44void Shutdown() { 33void Shutdown() {
@@ -47,11 +36,7 @@ void Shutdown() {
47 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); 36 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
48 Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); 37 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
49 motion_emu.reset(); 38 motion_emu.reset();
50 39 sdl.reset();
51#ifdef HAVE_SDL2
52 SDL::Shutdown();
53 poll_thread.join();
54#endif
55} 40}
56 41
57Keyboard* GetKeyboard() { 42Keyboard* GetKeyboard() {
@@ -88,7 +73,7 @@ namespace Polling {
88 73
89std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) { 74std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
90#ifdef HAVE_SDL2 75#ifdef HAVE_SDL2
91 return SDL::Polling::GetPollers(type); 76 return sdl->GetPollers(type);
92#else 77#else
93 return {}; 78 return {};
94#endif 79#endif
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 9eb13106e..77a0ce90b 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -20,8 +20,6 @@ void Init();
20/// Deregisters all built-in input device factories and shuts them down. 20/// Deregisters all built-in input device factories and shuts them down.
21void Shutdown(); 21void Shutdown();
22 22
23void StartJoystickEventHandler();
24
25class Keyboard; 23class Keyboard;
26 24
27/// Gets the keyboard button device factory. 25/// Gets the keyboard button device factory.
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index faf3c1fa3..644db3448 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -1,631 +1,19 @@
1// Copyright 2017 Citra Emulator Project 1// Copyright 2018 Citra Emulator Project
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 <algorithm>
6#include <atomic>
7#include <cmath>
8#include <functional>
9#include <iterator>
10#include <mutex>
11#include <string>
12#include <thread>
13#include <tuple>
14#include <unordered_map>
15#include <utility>
16#include <vector>
17#include <SDL.h>
18#include "common/assert.h"
19#include "common/logging/log.h"
20#include "common/math_util.h"
21#include "common/param_package.h"
22#include "common/threadsafe_queue.h"
23#include "input_common/main.h"
24#include "input_common/sdl/sdl.h" 5#include "input_common/sdl/sdl.h"
6#ifdef HAVE_SDL2
7#include "input_common/sdl/sdl_impl.h"
8#endif
25 9
26namespace InputCommon { 10namespace InputCommon::SDL {
27 11
28namespace SDL { 12std::unique_ptr<State> Init() {
29 13#ifdef HAVE_SDL2
30class SDLJoystick; 14 return std::make_unique<SDLState>();
31class SDLButtonFactory; 15#else
32class SDLAnalogFactory; 16 return std::make_unique<NullState>();
33 17#endif
34/// Map of GUID of a list of corresponding virtual Joysticks
35static std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
36static std::mutex joystick_map_mutex;
37
38static std::shared_ptr<SDLButtonFactory> button_factory;
39static std::shared_ptr<SDLAnalogFactory> analog_factory;
40
41/// Used by the Pollers during config
42static std::atomic<bool> polling;
43static Common::SPSCQueue<SDL_Event> event_queue;
44
45static std::atomic<bool> initialized = false;
46
47static std::string GetGUID(SDL_Joystick* joystick) {
48 SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
49 char guid_str[33];
50 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
51 return guid_str;
52}
53
54class SDLJoystick {
55public:
56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
57 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
59
60 void SetButton(int button, bool value) {
61 std::lock_guard<std::mutex> lock(mutex);
62 state.buttons[button] = value;
63 }
64
65 bool GetButton(int button) const {
66 std::lock_guard<std::mutex> lock(mutex);
67 return state.buttons.at(button);
68 }
69
70 void SetAxis(int axis, Sint16 value) {
71 std::lock_guard<std::mutex> lock(mutex);
72 state.axes[axis] = value;
73 }
74
75 float GetAxis(int axis) const {
76 std::lock_guard<std::mutex> lock(mutex);
77 return state.axes.at(axis) / 32767.0f;
78 }
79
80 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
81 float x = GetAxis(axis_x);
82 float y = GetAxis(axis_y);
83 y = -y; // 3DS uses an y-axis inverse from SDL
84
85 // Make sure the coordinates are in the unit circle,
86 // otherwise normalize it.
87 float r = x * x + y * y;
88 if (r > 1.0f) {
89 r = std::sqrt(r);
90 x /= r;
91 y /= r;
92 }
93
94 return std::make_tuple(x, y);
95 }
96
97 void SetHat(int hat, Uint8 direction) {
98 std::lock_guard<std::mutex> lock(mutex);
99 state.hats[hat] = direction;
100 }
101
102 bool GetHatDirection(int hat, Uint8 direction) const {
103 std::lock_guard<std::mutex> lock(mutex);
104 return (state.hats.at(hat) & direction) != 0;
105 }
106 /**
107 * The guid of the joystick
108 */
109 const std::string& GetGUID() const {
110 return guid;
111 }
112
113 /**
114 * The number of joystick from the same type that were connected before this joystick
115 */
116 int GetPort() const {
117 return port;
118 }
119
120 SDL_Joystick* GetSDLJoystick() const {
121 return sdl_joystick.get();
122 }
123
124 void SetSDLJoystick(SDL_Joystick* joystick,
125 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
126 sdl_joystick =
127 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
128 }
129
130private:
131 struct State {
132 std::unordered_map<int, bool> buttons;
133 std::unordered_map<int, Sint16> axes;
134 std::unordered_map<int, Uint8> hats;
135 } state;
136 std::string guid;
137 int port;
138 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
139 mutable std::mutex mutex;
140};
141
142/**
143 * Get the nth joystick with the corresponding GUID
144 */
145static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port) {
146 std::lock_guard<std::mutex> lock(joystick_map_mutex);
147 const auto it = joystick_map.find(guid);
148 if (it != joystick_map.end()) {
149 while (it->second.size() <= port) {
150 auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
151 [](SDL_Joystick*) {});
152 it->second.emplace_back(std::move(joystick));
153 }
154 return it->second[port];
155 }
156 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
157 return joystick_map[guid].emplace_back(std::move(joystick));
158}
159
160/**
161 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
162 * it to a SDLJoystick with the same guid and that port
163 */
164static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
165 std::lock_guard<std::mutex> lock(joystick_map_mutex);
166 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
167 const std::string guid = GetGUID(sdl_joystick);
168 auto map_it = joystick_map.find(guid);
169 if (map_it != joystick_map.end()) {
170 auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
171 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
172 return sdl_joystick == joystick->GetSDLJoystick();
173 });
174 if (vec_it != map_it->second.end()) {
175 // This is the common case: There is already an existing SDL_Joystick maped to a
176 // SDLJoystick. return the SDLJoystick
177 return *vec_it;
178 }
179 // Search for a SDLJoystick without a mapped SDL_Joystick...
180 auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
181 [](const std::shared_ptr<SDLJoystick>& joystick) {
182 return !joystick->GetSDLJoystick();
183 });
184 if (nullptr_it != map_it->second.end()) {
185 // ... and map it
186 (*nullptr_it)->SetSDLJoystick(sdl_joystick);
187 return *nullptr_it;
188 }
189 // There is no SDLJoystick without a mapped SDL_Joystick
190 // Create a new SDLJoystick
191 auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
192 return map_it->second.emplace_back(std::move(joystick));
193 }
194 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
195 return joystick_map[guid].emplace_back(std::move(joystick));
196}
197
198void InitJoystick(int joystick_index) {
199 std::lock_guard<std::mutex> lock(joystick_map_mutex);
200 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
201 if (!sdl_joystick) {
202 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
203 return;
204 }
205 std::string guid = GetGUID(sdl_joystick);
206 if (joystick_map.find(guid) == joystick_map.end()) {
207 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
208 joystick_map[guid].emplace_back(std::move(joystick));
209 return;
210 }
211 auto& joystick_guid_list = joystick_map[guid];
212 const auto it = std::find_if(
213 joystick_guid_list.begin(), joystick_guid_list.end(),
214 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
215 if (it != joystick_guid_list.end()) {
216 (*it)->SetSDLJoystick(sdl_joystick);
217 return;
218 }
219 auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
220 joystick_guid_list.emplace_back(std::move(joystick));
221}
222
223void CloseJoystick(SDL_Joystick* sdl_joystick) {
224 std::lock_guard<std::mutex> lock(joystick_map_mutex);
225 std::string guid = GetGUID(sdl_joystick);
226 // This call to guid is save since the joystick is guranteed to be in that map
227 auto& joystick_guid_list = joystick_map[guid];
228 const auto joystick_it =
229 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
230 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
231 return joystick->GetSDLJoystick() == sdl_joystick;
232 });
233 (*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
234}
235
236void HandleGameControllerEvent(const SDL_Event& event) {
237 switch (event.type) {
238 case SDL_JOYBUTTONUP: {
239 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
240 if (joystick) {
241 joystick->SetButton(event.jbutton.button, false);
242 }
243 break;
244 }
245 case SDL_JOYBUTTONDOWN: {
246 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
247 if (joystick) {
248 joystick->SetButton(event.jbutton.button, true);
249 }
250 break;
251 }
252 case SDL_JOYHATMOTION: {
253 auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
254 if (joystick) {
255 joystick->SetHat(event.jhat.hat, event.jhat.value);
256 }
257 break;
258 }
259 case SDL_JOYAXISMOTION: {
260 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
261 if (joystick) {
262 joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
263 }
264 break;
265 }
266 case SDL_JOYDEVICEREMOVED:
267 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
268 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
269 break;
270 case SDL_JOYDEVICEADDED:
271 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
272 InitJoystick(event.jdevice.which);
273 break;
274 }
275}
276
277void CloseSDLJoysticks() {
278 std::lock_guard<std::mutex> lock(joystick_map_mutex);
279 joystick_map.clear();
280}
281
282void PollLoop() {
283 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
284 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
285 return;
286 }
287
288 SDL_Event event;
289 while (initialized) {
290 // Wait for 10 ms or until an event happens
291 if (SDL_WaitEventTimeout(&event, 10)) {
292 // Don't handle the event if we are configuring
293 if (polling) {
294 event_queue.Push(event);
295 } else {
296 HandleGameControllerEvent(event);
297 }
298 }
299 }
300 CloseSDLJoysticks();
301 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
302}
303
304class SDLButton final : public Input::ButtonDevice {
305public:
306 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
307 : joystick(std::move(joystick_)), button(button_) {}
308
309 bool GetStatus() const override {
310 return joystick->GetButton(button);
311 }
312
313private:
314 std::shared_ptr<SDLJoystick> joystick;
315 int button;
316};
317
318class SDLDirectionButton final : public Input::ButtonDevice {
319public:
320 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
321 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
322
323 bool GetStatus() const override {
324 return joystick->GetHatDirection(hat, direction);
325 }
326
327private:
328 std::shared_ptr<SDLJoystick> joystick;
329 int hat;
330 Uint8 direction;
331};
332
333class SDLAxisButton final : public Input::ButtonDevice {
334public:
335 explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
336 bool trigger_if_greater_)
337 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
338 trigger_if_greater(trigger_if_greater_) {}
339
340 bool GetStatus() const override {
341 float axis_value = joystick->GetAxis(axis);
342 if (trigger_if_greater)
343 return axis_value > threshold;
344 return axis_value < threshold;
345 }
346
347private:
348 std::shared_ptr<SDLJoystick> joystick;
349 int axis;
350 float threshold;
351 bool trigger_if_greater;
352};
353
354class SDLAnalog final : public Input::AnalogDevice {
355public:
356 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_)
357 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_) {}
358
359 std::tuple<float, float> GetStatus() const override {
360 return joystick->GetAnalog(axis_x, axis_y);
361 }
362
363private:
364 std::shared_ptr<SDLJoystick> joystick;
365 int axis_x;
366 int axis_y;
367};
368
369/// A button device factory that creates button devices from SDL joystick
370class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
371public:
372 /**
373 * Creates a button device from a joystick button
374 * @param params contains parameters for creating the device:
375 * - "guid": the guid of the joystick to bind
376 * - "port": the nth joystick of the same type to bind
377 * - "button"(optional): the index of the button to bind
378 * - "hat"(optional): the index of the hat to bind as direction buttons
379 * - "axis"(optional): the index of the axis to bind
380 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
381 * "down", "left" or "right"
382 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
383 * triggered if the axis value crosses
384 * - "direction"(only used for axis): "+" means the button is triggered when the axis
385 * value is greater than the threshold; "-" means the button is triggered when the axis
386 * value is smaller than the threshold
387 */
388 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
389 const std::string guid = params.Get("guid", "0");
390 const int port = params.Get("port", 0);
391
392 auto joystick = GetSDLJoystickByGUID(guid, port);
393
394 if (params.Has("hat")) {
395 const int hat = params.Get("hat", 0);
396 const std::string direction_name = params.Get("direction", "");
397 Uint8 direction;
398 if (direction_name == "up") {
399 direction = SDL_HAT_UP;
400 } else if (direction_name == "down") {
401 direction = SDL_HAT_DOWN;
402 } else if (direction_name == "left") {
403 direction = SDL_HAT_LEFT;
404 } else if (direction_name == "right") {
405 direction = SDL_HAT_RIGHT;
406 } else {
407 direction = 0;
408 }
409 // This is necessary so accessing GetHat with hat won't crash
410 joystick->SetHat(hat, SDL_HAT_CENTERED);
411 return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
412 }
413
414 if (params.Has("axis")) {
415 const int axis = params.Get("axis", 0);
416 const float threshold = params.Get("threshold", 0.5f);
417 const std::string direction_name = params.Get("direction", "");
418 bool trigger_if_greater;
419 if (direction_name == "+") {
420 trigger_if_greater = true;
421 } else if (direction_name == "-") {
422 trigger_if_greater = false;
423 } else {
424 trigger_if_greater = true;
425 LOG_ERROR(Input, "Unknown direction '{}'", direction_name);
426 }
427 // This is necessary so accessing GetAxis with axis won't crash
428 joystick->SetAxis(axis, 0);
429 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
430 }
431
432 const int button = params.Get("button", 0);
433 // This is necessary so accessing GetButton with button won't crash
434 joystick->SetButton(button, false);
435 return std::make_unique<SDLButton>(joystick, button);
436 }
437};
438
439/// An analog device factory that creates analog devices from SDL joystick
440class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
441public:
442 /**
443 * Creates analog device from joystick axes
444 * @param params contains parameters for creating the device:
445 * - "guid": the guid of the joystick to bind
446 * - "port": the nth joystick of the same type
447 * - "axis_x": the index of the axis to be bind as x-axis
448 * - "axis_y": the index of the axis to be bind as y-axis
449 */
450 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
451 const std::string guid = params.Get("guid", "0");
452 const int port = params.Get("port", 0);
453 const int axis_x = params.Get("axis_x", 0);
454 const int axis_y = params.Get("axis_y", 1);
455
456 auto joystick = GetSDLJoystickByGUID(guid, port);
457
458 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
459 joystick->SetAxis(axis_x, 0);
460 joystick->SetAxis(axis_y, 0);
461 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y);
462 }
463};
464
465void Init() {
466 using namespace Input;
467 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
468 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
469 polling = false;
470 initialized = true;
471}
472
473void Shutdown() {
474 if (initialized) {
475 using namespace Input;
476 UnregisterFactory<ButtonDevice>("sdl");
477 UnregisterFactory<AnalogDevice>("sdl");
478 initialized = false;
479 }
480}
481
482Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
483 Common::ParamPackage params({{"engine", "sdl"}});
484 switch (event.type) {
485 case SDL_JOYAXISMOTION: {
486 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
487 params.Set("port", joystick->GetPort());
488 params.Set("guid", joystick->GetGUID());
489 params.Set("axis", event.jaxis.axis);
490 if (event.jaxis.value > 0) {
491 params.Set("direction", "+");
492 params.Set("threshold", "0.5");
493 } else {
494 params.Set("direction", "-");
495 params.Set("threshold", "-0.5");
496 }
497 break;
498 }
499 case SDL_JOYBUTTONUP: {
500 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
501 params.Set("port", joystick->GetPort());
502 params.Set("guid", joystick->GetGUID());
503 params.Set("button", event.jbutton.button);
504 break;
505 }
506 case SDL_JOYHATMOTION: {
507 auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
508 params.Set("port", joystick->GetPort());
509 params.Set("guid", joystick->GetGUID());
510 params.Set("hat", event.jhat.hat);
511 switch (event.jhat.value) {
512 case SDL_HAT_UP:
513 params.Set("direction", "up");
514 break;
515 case SDL_HAT_DOWN:
516 params.Set("direction", "down");
517 break;
518 case SDL_HAT_LEFT:
519 params.Set("direction", "left");
520 break;
521 case SDL_HAT_RIGHT:
522 params.Set("direction", "right");
523 break;
524 default:
525 return {};
526 }
527 break;
528 }
529 }
530 return params;
531}
532
533namespace Polling {
534
535class SDLPoller : public InputCommon::Polling::DevicePoller {
536public:
537 void Start() override {
538 event_queue.Clear();
539 polling = true;
540 }
541
542 void Stop() override {
543 polling = false;
544 }
545};
546
547class SDLButtonPoller final : public SDLPoller {
548public:
549 Common::ParamPackage GetNextInput() override {
550 SDL_Event event;
551 while (event_queue.Pop(event)) {
552 switch (event.type) {
553 case SDL_JOYAXISMOTION:
554 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
555 break;
556 }
557 case SDL_JOYBUTTONUP:
558 case SDL_JOYHATMOTION:
559 return SDLEventToButtonParamPackage(event);
560 }
561 }
562 return {};
563 }
564};
565
566class SDLAnalogPoller final : public SDLPoller {
567public:
568 void Start() override {
569 SDLPoller::Start();
570
571 // Reset stored axes
572 analog_xaxis = -1;
573 analog_yaxis = -1;
574 analog_axes_joystick = -1;
575 }
576
577 Common::ParamPackage GetNextInput() override {
578 SDL_Event event;
579 while (event_queue.Pop(event)) {
580 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
581 continue;
582 }
583 // An analog device needs two axes, so we need to store the axis for later and wait for
584 // a second SDL event. The axes also must be from the same joystick.
585 int axis = event.jaxis.axis;
586 if (analog_xaxis == -1) {
587 analog_xaxis = axis;
588 analog_axes_joystick = event.jaxis.which;
589 } else if (analog_yaxis == -1 && analog_xaxis != axis &&
590 analog_axes_joystick == event.jaxis.which) {
591 analog_yaxis = axis;
592 }
593 }
594 Common::ParamPackage params;
595 if (analog_xaxis != -1 && analog_yaxis != -1) {
596 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
597 params.Set("engine", "sdl");
598 params.Set("port", joystick->GetPort());
599 params.Set("guid", joystick->GetGUID());
600 params.Set("axis_x", analog_xaxis);
601 params.Set("axis_y", analog_yaxis);
602 analog_xaxis = -1;
603 analog_yaxis = -1;
604 analog_axes_joystick = -1;
605 return params;
606 }
607 return params;
608 }
609
610private:
611 int analog_xaxis = -1;
612 int analog_yaxis = -1;
613 SDL_JoystickID analog_axes_joystick = -1;
614};
615
616std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
617 InputCommon::Polling::DeviceType type) {
618 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
619 switch (type) {
620 case InputCommon::Polling::DeviceType::Analog:
621 pollers.push_back(std::make_unique<SDLAnalogPoller>());
622 break;
623 case InputCommon::Polling::DeviceType::Button:
624 pollers.push_back(std::make_unique<SDLButtonPoller>());
625 break;
626 }
627 return pollers;
628} 18}
629} // namespace Polling 19} // namespace InputCommon::SDL
630} // namespace SDL
631} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 0206860d3..02a8d2e2c 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -1,4 +1,4 @@
1// Copyright 2017 Citra Emulator Project 1// Copyright 2018 Citra Emulator Project
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
@@ -7,45 +7,36 @@
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "core/frontend/input.h" 9#include "core/frontend/input.h"
10#include "input_common/main.h"
10 11
11union SDL_Event; 12union SDL_Event;
13
12namespace Common { 14namespace Common {
13class ParamPackage; 15class ParamPackage;
14} 16} // namespace Common
15namespace InputCommon { 17
16namespace Polling { 18namespace InputCommon::Polling {
17class DevicePoller; 19class DevicePoller;
18enum class DeviceType; 20enum class DeviceType;
19} // namespace Polling 21} // namespace InputCommon::Polling
20} // namespace InputCommon
21
22namespace InputCommon {
23namespace SDL {
24
25/// Initializes and registers SDL device factories
26void Init();
27
28/// Unresisters SDL device factories and shut them down.
29void Shutdown();
30
31/// Needs to be called before SDL_QuitSubSystem.
32void CloseSDLJoysticks();
33 22
34/// Handle SDL_Events for joysticks from SDL_PollEvent 23namespace InputCommon::SDL {
35void HandleGameControllerEvent(const SDL_Event& event);
36 24
37/// A Loop that calls HandleGameControllerEvent until Shutdown is called 25class State {
38void PollLoop(); 26public:
27 /// Unresisters SDL device factories and shut them down.
28 virtual ~State() = default;
39 29
40/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 30 virtual std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
41Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); 31 InputCommon::Polling::DeviceType type) = 0;
32};
42 33
43namespace Polling { 34class NullState : public State {
35public:
36 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
37 InputCommon::Polling::DeviceType type) override {}
38};
44 39
45/// Get all DevicePoller that use the SDL backend for a specific device type 40std::unique_ptr<State> Init();
46std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
47 InputCommon::Polling::DeviceType type);
48 41
49} // namespace Polling 42} // namespace InputCommon::SDL
50} // namespace SDL
51} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
new file mode 100644
index 000000000..934339d3b
--- /dev/null
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -0,0 +1,669 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <atomic>
7#include <cmath>
8#include <functional>
9#include <iterator>
10#include <mutex>
11#include <string>
12#include <thread>
13#include <tuple>
14#include <unordered_map>
15#include <utility>
16#include <vector>
17#include <SDL.h>
18#include "common/assert.h"
19#include "common/logging/log.h"
20#include "common/math_util.h"
21#include "common/param_package.h"
22#include "common/threadsafe_queue.h"
23#include "core/frontend/input.h"
24#include "input_common/sdl/sdl_impl.h"
25
26namespace InputCommon {
27
28namespace SDL {
29
30static std::string GetGUID(SDL_Joystick* joystick) {
31 SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
32 char guid_str[33];
33 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
34 return guid_str;
35}
36
37/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
38static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
39
40static int SDLEventWatcher(void* userdata, SDL_Event* event) {
41 SDLState* sdl_state = reinterpret_cast<SDLState*>(userdata);
42 // Don't handle the event if we are configuring
43 if (sdl_state->polling) {
44 sdl_state->event_queue.Push(*event);
45 } else {
46 sdl_state->HandleGameControllerEvent(*event);
47 }
48 return 0;
49}
50
51class SDLJoystick {
52public:
53 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
54 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
55 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
56
57 void SetButton(int button, bool value) {
58 std::lock_guard<std::mutex> lock(mutex);
59 state.buttons[button] = value;
60 }
61
62 bool GetButton(int button) const {
63 std::lock_guard<std::mutex> lock(mutex);
64 return state.buttons.at(button);
65 }
66
67 void SetAxis(int axis, Sint16 value) {
68 std::lock_guard<std::mutex> lock(mutex);
69 state.axes[axis] = value;
70 }
71
72 float GetAxis(int axis) const {
73 std::lock_guard<std::mutex> lock(mutex);
74 return state.axes.at(axis) / 32767.0f;
75 }
76
77 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
78 float x = GetAxis(axis_x);
79 float y = GetAxis(axis_y);
80 y = -y; // 3DS uses an y-axis inverse from SDL
81
82 // Make sure the coordinates are in the unit circle,
83 // otherwise normalize it.
84 float r = x * x + y * y;
85 if (r > 1.0f) {
86 r = std::sqrt(r);
87 x /= r;
88 y /= r;
89 }
90
91 return std::make_tuple(x, y);
92 }
93
94 void SetHat(int hat, Uint8 direction) {
95 std::lock_guard<std::mutex> lock(mutex);
96 state.hats[hat] = direction;
97 }
98
99 bool GetHatDirection(int hat, Uint8 direction) const {
100 std::lock_guard<std::mutex> lock(mutex);
101 return (state.hats.at(hat) & direction) != 0;
102 }
103 /**
104 * The guid of the joystick
105 */
106 const std::string& GetGUID() const {
107 return guid;
108 }
109
110 /**
111 * The number of joystick from the same type that were connected before this joystick
112 */
113 int GetPort() const {
114 return port;
115 }
116
117 SDL_Joystick* GetSDLJoystick() const {
118 return sdl_joystick.get();
119 }
120
121 void SetSDLJoystick(SDL_Joystick* joystick,
122 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
123 sdl_joystick =
124 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
125 }
126
127private:
128 struct State {
129 std::unordered_map<int, bool> buttons;
130 std::unordered_map<int, Sint16> axes;
131 std::unordered_map<int, Uint8> hats;
132 } state;
133 std::string guid;
134 int port;
135 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
136 mutable std::mutex mutex;
137};
138
139/**
140 * Get the nth joystick with the corresponding GUID
141 */
142std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
143 std::lock_guard<std::mutex> lock(joystick_map_mutex);
144 const auto it = joystick_map.find(guid);
145 if (it != joystick_map.end()) {
146 while (it->second.size() <= port) {
147 auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
148 [](SDL_Joystick*) {});
149 it->second.emplace_back(std::move(joystick));
150 }
151 return it->second[port];
152 }
153 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
154 return joystick_map[guid].emplace_back(std::move(joystick));
155}
156
157/**
158 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
159 * it to a SDLJoystick with the same guid and that port
160 */
161std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
162 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
163 const std::string guid = GetGUID(sdl_joystick);
164 std::lock_guard<std::mutex> lock(joystick_map_mutex);
165 auto map_it = joystick_map.find(guid);
166 if (map_it != joystick_map.end()) {
167 auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
168 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
169 return sdl_joystick == joystick->GetSDLJoystick();
170 });
171 if (vec_it != map_it->second.end()) {
172 // This is the common case: There is already an existing SDL_Joystick maped to a
173 // SDLJoystick. return the SDLJoystick
174 return *vec_it;
175 }
176 // Search for a SDLJoystick without a mapped SDL_Joystick...
177 auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
178 [](const std::shared_ptr<SDLJoystick>& joystick) {
179 return !joystick->GetSDLJoystick();
180 });
181 if (nullptr_it != map_it->second.end()) {
182 // ... and map it
183 (*nullptr_it)->SetSDLJoystick(sdl_joystick);
184 return *nullptr_it;
185 }
186 // There is no SDLJoystick without a mapped SDL_Joystick
187 // Create a new SDLJoystick
188 auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
189 return map_it->second.emplace_back(std::move(joystick));
190 }
191 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
192 return joystick_map[guid].emplace_back(std::move(joystick));
193}
194
195void SDLState::InitJoystick(int joystick_index) {
196 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
197 if (!sdl_joystick) {
198 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
199 return;
200 }
201 std::string guid = GetGUID(sdl_joystick);
202 std::lock_guard<std::mutex> lock(joystick_map_mutex);
203 if (joystick_map.find(guid) == joystick_map.end()) {
204 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
205 joystick_map[guid].emplace_back(std::move(joystick));
206 return;
207 }
208 auto& joystick_guid_list = joystick_map[guid];
209 const auto it = std::find_if(
210 joystick_guid_list.begin(), joystick_guid_list.end(),
211 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
212 if (it != joystick_guid_list.end()) {
213 (*it)->SetSDLJoystick(sdl_joystick);
214 return;
215 }
216 auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
217 joystick_guid_list.emplace_back(std::move(joystick));
218}
219
220void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
221 std::string guid = GetGUID(sdl_joystick);
222 std::shared_ptr<SDLJoystick> joystick;
223 {
224 std::lock_guard<std::mutex> lock(joystick_map_mutex);
225 // This call to guid is safe since the joystick is guaranteed to be in the map
226 auto& joystick_guid_list = joystick_map[guid];
227 const auto joystick_it =
228 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
229 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
230 return joystick->GetSDLJoystick() == sdl_joystick;
231 });
232 joystick = *joystick_it;
233 }
234 // Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback
235 // which locks the mutex again
236 joystick->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
237}
238
239void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
240 switch (event.type) {
241 case SDL_JOYBUTTONUP: {
242 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
243 joystick->SetButton(event.jbutton.button, false);
244 }
245 break;
246 }
247 case SDL_JOYBUTTONDOWN: {
248 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
249 joystick->SetButton(event.jbutton.button, true);
250 }
251 break;
252 }
253 case SDL_JOYHATMOTION: {
254 if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
255 joystick->SetHat(event.jhat.hat, event.jhat.value);
256 }
257 break;
258 }
259 case SDL_JOYAXISMOTION: {
260 if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
261 joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
262 }
263 break;
264 }
265 case SDL_JOYDEVICEREMOVED:
266 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
267 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
268 break;
269 case SDL_JOYDEVICEADDED:
270 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
271 InitJoystick(event.jdevice.which);
272 break;
273 }
274}
275
276void SDLState::CloseJoysticks() {
277 std::lock_guard<std::mutex> lock(joystick_map_mutex);
278 joystick_map.clear();
279}
280
281class SDLButton final : public Input::ButtonDevice {
282public:
283 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
284 : joystick(std::move(joystick_)), button(button_) {}
285
286 bool GetStatus() const override {
287 return joystick->GetButton(button);
288 }
289
290private:
291 std::shared_ptr<SDLJoystick> joystick;
292 int button;
293};
294
295class SDLDirectionButton final : public Input::ButtonDevice {
296public:
297 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
298 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
299
300 bool GetStatus() const override {
301 return joystick->GetHatDirection(hat, direction);
302 }
303
304private:
305 std::shared_ptr<SDLJoystick> joystick;
306 int hat;
307 Uint8 direction;
308};
309
310class SDLAxisButton final : public Input::ButtonDevice {
311public:
312 explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
313 bool trigger_if_greater_)
314 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
315 trigger_if_greater(trigger_if_greater_) {}
316
317 bool GetStatus() const override {
318 float axis_value = joystick->GetAxis(axis);
319 if (trigger_if_greater)
320 return axis_value > threshold;
321 return axis_value < threshold;
322 }
323
324private:
325 std::shared_ptr<SDLJoystick> joystick;
326 int axis;
327 float threshold;
328 bool trigger_if_greater;
329};
330
331class SDLAnalog final : public Input::AnalogDevice {
332public:
333 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_)
334 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {}
335
336 std::tuple<float, float> GetStatus() const override {
337 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y);
338 const float r = std::sqrt((x * x) + (y * y));
339 if (r > deadzone) {
340 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
341 y / r * (r - deadzone) / (1 - deadzone));
342 }
343 return std::make_tuple<float, float>(0.0f, 0.0f);
344 }
345
346private:
347 std::shared_ptr<SDLJoystick> joystick;
348 const int axis_x;
349 const int axis_y;
350 const float deadzone;
351};
352
353/// A button device factory that creates button devices from SDL joystick
354class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
355public:
356 explicit SDLButtonFactory(SDLState& state_) : state(state_) {}
357
358 /**
359 * Creates a button device from a joystick button
360 * @param params contains parameters for creating the device:
361 * - "guid": the guid of the joystick to bind
362 * - "port": the nth joystick of the same type to bind
363 * - "button"(optional): the index of the button to bind
364 * - "hat"(optional): the index of the hat to bind as direction buttons
365 * - "axis"(optional): the index of the axis to bind
366 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
367 * "down", "left" or "right"
368 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
369 * triggered if the axis value crosses
370 * - "direction"(only used for axis): "+" means the button is triggered when the axis
371 * value is greater than the threshold; "-" means the button is triggered when the axis
372 * value is smaller than the threshold
373 */
374 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
375 const std::string guid = params.Get("guid", "0");
376 const int port = params.Get("port", 0);
377
378 auto joystick = state.GetSDLJoystickByGUID(guid, port);
379
380 if (params.Has("hat")) {
381 const int hat = params.Get("hat", 0);
382 const std::string direction_name = params.Get("direction", "");
383 Uint8 direction;
384 if (direction_name == "up") {
385 direction = SDL_HAT_UP;
386 } else if (direction_name == "down") {
387 direction = SDL_HAT_DOWN;
388 } else if (direction_name == "left") {
389 direction = SDL_HAT_LEFT;
390 } else if (direction_name == "right") {
391 direction = SDL_HAT_RIGHT;
392 } else {
393 direction = 0;
394 }
395 // This is necessary so accessing GetHat with hat won't crash
396 joystick->SetHat(hat, SDL_HAT_CENTERED);
397 return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
398 }
399
400 if (params.Has("axis")) {
401 const int axis = params.Get("axis", 0);
402 const float threshold = params.Get("threshold", 0.5f);
403 const std::string direction_name = params.Get("direction", "");
404 bool trigger_if_greater;
405 if (direction_name == "+") {
406 trigger_if_greater = true;
407 } else if (direction_name == "-") {
408 trigger_if_greater = false;
409 } else {
410 trigger_if_greater = true;
411 LOG_ERROR(Input, "Unknown direction {}", direction_name);
412 }
413 // This is necessary so accessing GetAxis with axis won't crash
414 joystick->SetAxis(axis, 0);
415 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
416 }
417
418 const int button = params.Get("button", 0);
419 // This is necessary so accessing GetButton with button won't crash
420 joystick->SetButton(button, false);
421 return std::make_unique<SDLButton>(joystick, button);
422 }
423
424private:
425 SDLState& state;
426};
427
428/// An analog device factory that creates analog devices from SDL joystick
429class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
430public:
431 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
432 /**
433 * Creates analog device from joystick axes
434 * @param params contains parameters for creating the device:
435 * - "guid": the guid of the joystick to bind
436 * - "port": the nth joystick of the same type
437 * - "axis_x": the index of the axis to be bind as x-axis
438 * - "axis_y": the index of the axis to be bind as y-axis
439 */
440 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
441 const std::string guid = params.Get("guid", "0");
442 const int port = params.Get("port", 0);
443 const int axis_x = params.Get("axis_x", 0);
444 const int axis_y = params.Get("axis_y", 1);
445 float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
446
447 auto joystick = state.GetSDLJoystickByGUID(guid, port);
448
449 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
450 joystick->SetAxis(axis_x, 0);
451 joystick->SetAxis(axis_y, 0);
452 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone);
453 }
454
455private:
456 SDLState& state;
457};
458
459SDLState::SDLState() {
460 using namespace Input;
461 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
462 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
463
464 // If the frontend is going to manage the event loop, then we dont start one here
465 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
466 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
467 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
468 return;
469 }
470 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
471 LOG_ERROR(Input, "Failed to set Hint for background events", SDL_GetError());
472 }
473
474 SDL_AddEventWatch(&SDLEventWatcher, this);
475
476 initialized = true;
477 if (start_thread) {
478 poll_thread = std::thread([&] {
479 using namespace std::chrono_literals;
480 SDL_Event event;
481 while (initialized) {
482 SDL_PumpEvents();
483 std::this_thread::sleep_for(std::chrono::duration(10ms));
484 }
485 });
486 }
487 // Because the events for joystick connection happens before we have our event watcher added, we
488 // can just open all the joysticks right here
489 for (int i = 0; i < SDL_NumJoysticks(); ++i) {
490 InitJoystick(i);
491 }
492}
493
494SDLState::~SDLState() {
495 using namespace Input;
496 UnregisterFactory<ButtonDevice>("sdl");
497 UnregisterFactory<AnalogDevice>("sdl");
498
499 CloseJoysticks();
500 SDL_DelEventWatch(&SDLEventWatcher, this);
501
502 initialized = false;
503 if (start_thread) {
504 poll_thread.join();
505 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
506 }
507}
508
509Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
510 Common::ParamPackage params({{"engine", "sdl"}});
511
512 switch (event.type) {
513 case SDL_JOYAXISMOTION: {
514 auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
515 params.Set("port", joystick->GetPort());
516 params.Set("guid", joystick->GetGUID());
517 params.Set("axis", event.jaxis.axis);
518 if (event.jaxis.value > 0) {
519 params.Set("direction", "+");
520 params.Set("threshold", "0.5");
521 } else {
522 params.Set("direction", "-");
523 params.Set("threshold", "-0.5");
524 }
525 break;
526 }
527 case SDL_JOYBUTTONUP: {
528 auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
529 params.Set("port", joystick->GetPort());
530 params.Set("guid", joystick->GetGUID());
531 params.Set("button", event.jbutton.button);
532 break;
533 }
534 case SDL_JOYHATMOTION: {
535 auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
536 params.Set("port", joystick->GetPort());
537 params.Set("guid", joystick->GetGUID());
538 params.Set("hat", event.jhat.hat);
539 switch (event.jhat.value) {
540 case SDL_HAT_UP:
541 params.Set("direction", "up");
542 break;
543 case SDL_HAT_DOWN:
544 params.Set("direction", "down");
545 break;
546 case SDL_HAT_LEFT:
547 params.Set("direction", "left");
548 break;
549 case SDL_HAT_RIGHT:
550 params.Set("direction", "right");
551 break;
552 default:
553 return {};
554 }
555 break;
556 }
557 }
558 return params;
559}
560
561namespace Polling {
562
563class SDLPoller : public InputCommon::Polling::DevicePoller {
564public:
565 explicit SDLPoller(SDLState& state_) : state(state_) {}
566
567 void Start() override {
568 state.event_queue.Clear();
569 state.polling = true;
570 }
571
572 void Stop() override {
573 state.polling = false;
574 }
575
576protected:
577 SDLState& state;
578};
579
580class SDLButtonPoller final : public SDLPoller {
581public:
582 explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
583
584 Common::ParamPackage GetNextInput() override {
585 SDL_Event event;
586 while (state.event_queue.Pop(event)) {
587 switch (event.type) {
588 case SDL_JOYAXISMOTION:
589 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
590 break;
591 }
592 case SDL_JOYBUTTONUP:
593 case SDL_JOYHATMOTION:
594 return SDLEventToButtonParamPackage(state, event);
595 }
596 }
597 return {};
598 }
599};
600
601class SDLAnalogPoller final : public SDLPoller {
602public:
603 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {}
604
605 void Start() override {
606 SDLPoller::Start();
607
608 // Reset stored axes
609 analog_xaxis = -1;
610 analog_yaxis = -1;
611 analog_axes_joystick = -1;
612 }
613
614 Common::ParamPackage GetNextInput() override {
615 SDL_Event event;
616 while (state.event_queue.Pop(event)) {
617 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
618 continue;
619 }
620 // An analog device needs two axes, so we need to store the axis for later and wait for
621 // a second SDL event. The axes also must be from the same joystick.
622 int axis = event.jaxis.axis;
623 if (analog_xaxis == -1) {
624 analog_xaxis = axis;
625 analog_axes_joystick = event.jaxis.which;
626 } else if (analog_yaxis == -1 && analog_xaxis != axis &&
627 analog_axes_joystick == event.jaxis.which) {
628 analog_yaxis = axis;
629 }
630 }
631 Common::ParamPackage params;
632 if (analog_xaxis != -1 && analog_yaxis != -1) {
633 auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
634 params.Set("engine", "sdl");
635 params.Set("port", joystick->GetPort());
636 params.Set("guid", joystick->GetGUID());
637 params.Set("axis_x", analog_xaxis);
638 params.Set("axis_y", analog_yaxis);
639 analog_xaxis = -1;
640 analog_yaxis = -1;
641 analog_axes_joystick = -1;
642 return params;
643 }
644 return params;
645 }
646
647private:
648 int analog_xaxis = -1;
649 int analog_yaxis = -1;
650 SDL_JoystickID analog_axes_joystick = -1;
651};
652} // namespace Polling
653
654std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers(
655 InputCommon::Polling::DeviceType type) {
656 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
657 switch (type) {
658 case InputCommon::Polling::DeviceType::Analog:
659 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
660 break;
661 case InputCommon::Polling::DeviceType::Button:
662 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
663 break;
664 return pollers;
665 }
666}
667
668} // namespace SDL
669} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
new file mode 100644
index 000000000..fec82fbe6
--- /dev/null
+++ b/src/input_common/sdl/sdl_impl.h
@@ -0,0 +1,64 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <memory>
9#include <thread>
10#include "common/threadsafe_queue.h"
11#include "input_common/sdl/sdl.h"
12
13union SDL_Event;
14using SDL_Joystick = struct _SDL_Joystick;
15using SDL_JoystickID = s32;
16
17namespace InputCommon::SDL {
18
19class SDLJoystick;
20class SDLButtonFactory;
21class SDLAnalogFactory;
22
23class SDLState : public State {
24public:
25 /// Initializes and registers SDL device factories
26 SDLState();
27
28 /// Unresisters SDL device factories and shut them down.
29 ~SDLState() override;
30
31 /// Handle SDL_Events for joysticks from SDL_PollEvent
32 void HandleGameControllerEvent(const SDL_Event& event);
33
34 std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
35 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
36
37 /// Get all DevicePoller that use the SDL backend for a specific device type
38 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
39 InputCommon::Polling::DeviceType type) override;
40
41 /// Used by the Pollers during config
42 std::atomic<bool> polling = false;
43 Common::SPSCQueue<SDL_Event> event_queue;
44
45private:
46 void InitJoystick(int joystick_index);
47 void CloseJoystick(SDL_Joystick* sdl_joystick);
48
49 /// Needs to be called before SDL_QuitSubSystem.
50 void CloseJoysticks();
51
52 /// Map of GUID of a list of corresponding virtual Joysticks
53 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
54 std::mutex joystick_map_mutex;
55
56 std::shared_ptr<SDLButtonFactory> button_factory;
57 std::shared_ptr<SDLAnalogFactory> analog_factory;
58
59 bool start_thread = false;
60 std::atomic<bool> initialized = false;
61
62 std::thread poll_thread;
63};
64} // namespace InputCommon::SDL
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 6fe56833d..3e1a735c3 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6 6
7#include "common/page_table.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
9#include "core/memory.h" 10#include "core/memory.h"
@@ -22,7 +23,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 23 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
23 page_table->special_regions.clear(); 24 page_table->special_regions.clear();
24 std::fill(page_table->attributes.begin(), page_table->attributes.end(), 25 std::fill(page_table->attributes.begin(), page_table->attributes.end(),
25 Memory::PageType::Unmapped); 26 Common::PageType::Unmapped);
26 27
27 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); 28 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
28 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); 29 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index 0b7539601..d145dbfcc 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -9,10 +9,10 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/memory_hook.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/memory_hook.h"
14 14
15namespace Memory { 15namespace Common {
16struct PageTable; 16struct PageTable;
17} 17}
18 18
@@ -58,7 +58,7 @@ public:
58 58
59private: 59private:
60 friend struct TestMemory; 60 friend struct TestMemory;
61 struct TestMemory final : Memory::MemoryHook { 61 struct TestMemory final : Common::MemoryHook {
62 explicit TestMemory(TestEnvironment* env_) : env(env_) {} 62 explicit TestMemory(TestEnvironment* env_) : env(env_) {}
63 TestEnvironment* env; 63 TestEnvironment* env;
64 64
@@ -86,7 +86,7 @@ private:
86 bool mutable_memory; 86 bool mutable_memory;
87 std::shared_ptr<TestMemory> test_memory; 87 std::shared_ptr<TestMemory> test_memory;
88 std::vector<WriteRecord> write_records; 88 std::vector<WriteRecord> write_records;
89 Memory::PageTable* page_table = nullptr; 89 Common::PageTable* page_table = nullptr;
90 Kernel::KernelCore kernel; 90 Kernel::KernelCore kernel;
91}; 91};
92 92
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 0c3038c52..14b76680f 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -123,6 +123,8 @@ if (ENABLE_VULKAN)
123 renderer_vulkan/vk_memory_manager.h 123 renderer_vulkan/vk_memory_manager.h
124 renderer_vulkan/vk_resource_manager.cpp 124 renderer_vulkan/vk_resource_manager.cpp
125 renderer_vulkan/vk_resource_manager.h 125 renderer_vulkan/vk_resource_manager.h
126 renderer_vulkan/vk_sampler_cache.cpp
127 renderer_vulkan/vk_sampler_cache.h
126 renderer_vulkan/vk_scheduler.cpp 128 renderer_vulkan/vk_scheduler.cpp
127 renderer_vulkan/vk_scheduler.h 129 renderer_vulkan/vk_scheduler.h
128 renderer_vulkan/vk_stream_buffer.cpp 130 renderer_vulkan/vk_stream_buffer.cpp
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index bff1a37ff..8b1bea1ae 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -55,12 +55,9 @@ bool DmaPusher::Step() {
55 } 55 }
56 56
57 // Push buffer non-empty, read a word 57 // Push buffer non-empty, read a word
58 const auto address = gpu.MemoryManager().GpuToCpuAddress(dma_get);
59 ASSERT_MSG(address, "Invalid GPU address");
60
61 command_headers.resize(command_list_header.size); 58 command_headers.resize(command_list_header.size);
62 59 gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(),
63 Memory::ReadBlock(*address, command_headers.data(), command_list_header.size * sizeof(u32)); 60 command_list_header.size * sizeof(u32));
64 61
65 for (const CommandHeader& command_header : command_headers) { 62 for (const CommandHeader& command_header : command_headers) {
66 63
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index aae2a4019..0931b9626 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -9,6 +9,7 @@
9#include "video_core/engines/kepler_memory.h" 9#include "video_core/engines/kepler_memory.h"
10#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
11#include "video_core/rasterizer_interface.h" 11#include "video_core/rasterizer_interface.h"
12#include "video_core/renderer_base.h"
12 13
13namespace Tegra::Engines { 14namespace Tegra::Engines {
14 15
@@ -40,17 +41,13 @@ void KeplerMemory::ProcessData(u32 data) {
40 ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported"); 41 ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported");
41 ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0); 42 ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0);
42 43
43 const GPUVAddr address = regs.dest.Address();
44 const auto dest_address =
45 memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
46 ASSERT_MSG(dest_address, "Invalid GPU address");
47
48 // We have to invalidate the destination region to evict any outdated surfaces from the cache. 44 // We have to invalidate the destination region to evict any outdated surfaces from the cache.
49 // We do this before actually writing the new data because the destination address might contain 45 // We do this before actually writing the new data because the destination address might
50 // a dirty surface that will have to be written back to memory. 46 // contain a dirty surface that will have to be written back to memory.
51 Core::System::GetInstance().GPU().InvalidateRegion(*dest_address, sizeof(u32)); 47 const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)};
48 rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32));
49 memory_manager.Write32(address, data);
52 50
53 Memory::Write32(*dest_address, data);
54 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); 51 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
55 52
56 state.write_offset++; 53 state.write_offset++;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 144e7fa82..c5d5be4ef 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -270,11 +270,9 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
270} 270}
271 271
272void Maxwell3D::ProcessQueryGet() { 272void Maxwell3D::ProcessQueryGet() {
273 GPUVAddr sequence_address = regs.query.QueryAddress(); 273 const GPUVAddr sequence_address{regs.query.QueryAddress()};
274 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application 274 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application
275 // VAddr before writing. 275 // VAddr before writing.
276 const auto address = memory_manager.GpuToCpuAddress(sequence_address);
277 ASSERT_MSG(address, "Invalid GPU address");
278 276
279 // TODO(Subv): Support the other query units. 277 // TODO(Subv): Support the other query units.
280 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 278 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
@@ -309,7 +307,7 @@ void Maxwell3D::ProcessQueryGet() {
309 // Write the current query sequence to the sequence address. 307 // Write the current query sequence to the sequence address.
310 // TODO(Subv): Find out what happens if you use a long query type but mark it as a short 308 // TODO(Subv): Find out what happens if you use a long query type but mark it as a short
311 // query. 309 // query.
312 Memory::Write32(*address, sequence); 310 memory_manager.Write32(sequence_address, sequence);
313 } else { 311 } else {
314 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast 312 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
315 // GPU, this command may actually take a while to complete in real hardware due to GPU 313 // GPU, this command may actually take a while to complete in real hardware due to GPU
@@ -318,7 +316,7 @@ void Maxwell3D::ProcessQueryGet() {
318 query_result.value = result; 316 query_result.value = result;
319 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming 317 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
320 query_result.timestamp = system.CoreTiming().GetTicks(); 318 query_result.timestamp = system.CoreTiming().GetTicks();
321 Memory::WriteBlock(*address, &query_result, sizeof(query_result)); 319 memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result));
322 } 320 }
323 dirty_flags.OnMemoryWrite(); 321 dirty_flags.OnMemoryWrite();
324 break; 322 break;
@@ -393,10 +391,12 @@ void Maxwell3D::ProcessCBData(u32 value) {
393 // Don't allow writing past the end of the buffer. 391 // Don't allow writing past the end of the buffer.
394 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size); 392 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
395 393
396 const auto address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); 394 const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
397 ASSERT_MSG(address, "Invalid GPU address"); 395
396 u8* ptr{memory_manager.GetPointer(address)};
397 rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32));
398 memory_manager.Write32(address, value);
398 399
399 Memory::Write32(*address, value);
400 dirty_flags.OnMemoryWrite(); 400 dirty_flags.OnMemoryWrite();
401 401
402 // Increment the current buffer position. 402 // Increment the current buffer position.
@@ -404,14 +404,10 @@ void Maxwell3D::ProcessCBData(u32 value) {
404} 404}
405 405
406Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { 406Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
407 const GPUVAddr tic_base_address = regs.tic.TICAddress(); 407 const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)};
408
409 const GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
410 const auto tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
411 ASSERT_MSG(tic_address_cpu, "Invalid GPU address");
412 408
413 Texture::TICEntry tic_entry; 409 Texture::TICEntry tic_entry;
414 Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); 410 memory_manager.ReadBlock(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
415 411
416 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || 412 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear ||
417 tic_entry.header_version == Texture::TICHeaderVersion::Pitch, 413 tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
@@ -429,14 +425,10 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
429} 425}
430 426
431Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { 427Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
432 const GPUVAddr tsc_base_address = regs.tsc.TSCAddress(); 428 const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)};
433
434 const GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
435 const auto tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
436 ASSERT_MSG(tsc_address_cpu, "Invalid GPU address");
437 429
438 Texture::TSCEntry tsc_entry; 430 Texture::TSCEntry tsc_entry;
439 Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry)); 431 memory_manager.ReadBlock(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
440 return tsc_entry; 432 return tsc_entry;
441} 433}
442 434
@@ -455,10 +447,7 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
455 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; 447 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
456 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { 448 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
457 449
458 const auto address = memory_manager.GpuToCpuAddress(current_texture); 450 const Texture::TextureHandle tex_handle{memory_manager.Read32(current_texture)};
459 ASSERT_MSG(address, "Invalid GPU address");
460
461 const Texture::TextureHandle tex_handle{Memory::Read32(*address)};
462 451
463 Texture::FullTextureInfo tex_info{}; 452 Texture::FullTextureInfo tex_info{};
464 // TODO(Subv): Use the shader to determine which textures are actually accessed. 453 // TODO(Subv): Use the shader to determine which textures are actually accessed.
@@ -493,10 +482,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
493 482
494 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); 483 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
495 484
496 const auto tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); 485 const Texture::TextureHandle tex_handle{memory_manager.Read32(tex_info_address)};
497 ASSERT_MSG(tex_address_cpu, "Invalid GPU address");
498
499 const Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
500 486
501 Texture::FullTextureInfo tex_info{}; 487 Texture::FullTextureInfo tex_info{};
502 tex_info.index = static_cast<u32>(offset); 488 tex_info.index = static_cast<u32>(offset);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 9dfea5999..a0ded4c25 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -9,6 +9,7 @@
9#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/engines/maxwell_dma.h" 10#include "video_core/engines/maxwell_dma.h"
11#include "video_core/rasterizer_interface.h" 11#include "video_core/rasterizer_interface.h"
12#include "video_core/renderer_base.h"
12#include "video_core/textures/decoders.h" 13#include "video_core/textures/decoders.h"
13 14
14namespace Tegra::Engines { 15namespace Tegra::Engines {
@@ -42,11 +43,6 @@ void MaxwellDMA::HandleCopy() {
42 const GPUVAddr source = regs.src_address.Address(); 43 const GPUVAddr source = regs.src_address.Address();
43 const GPUVAddr dest = regs.dst_address.Address(); 44 const GPUVAddr dest = regs.dst_address.Address();
44 45
45 const auto source_cpu = memory_manager.GpuToCpuAddress(source);
46 const auto dest_cpu = memory_manager.GpuToCpuAddress(dest);
47 ASSERT_MSG(source_cpu, "Invalid source GPU address");
48 ASSERT_MSG(dest_cpu, "Invalid destination GPU address");
49
50 // TODO(Subv): Perform more research and implement all features of this engine. 46 // TODO(Subv): Perform more research and implement all features of this engine.
51 ASSERT(regs.exec.enable_swizzle == 0); 47 ASSERT(regs.exec.enable_swizzle == 0);
52 ASSERT(regs.exec.query_mode == Regs::QueryMode::None); 48 ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
@@ -69,7 +65,7 @@ void MaxwellDMA::HandleCopy() {
69 // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count, 65 // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
70 // y_count). 66 // y_count).
71 if (!regs.exec.enable_2d) { 67 if (!regs.exec.enable_2d) {
72 Memory::CopyBlock(*dest_cpu, *source_cpu, regs.x_count); 68 memory_manager.CopyBlock(dest, source, regs.x_count);
73 return; 69 return;
74 } 70 }
75 71
@@ -78,9 +74,9 @@ void MaxwellDMA::HandleCopy() {
78 // rectangle. There is no need to manually flush/invalidate the regions because 74 // rectangle. There is no need to manually flush/invalidate the regions because
79 // CopyBlock does that for us. 75 // CopyBlock does that for us.
80 for (u32 line = 0; line < regs.y_count; ++line) { 76 for (u32 line = 0; line < regs.y_count; ++line) {
81 const VAddr source_line = *source_cpu + line * regs.src_pitch; 77 const GPUVAddr source_line = source + line * regs.src_pitch;
82 const VAddr dest_line = *dest_cpu + line * regs.dst_pitch; 78 const GPUVAddr dest_line = dest + line * regs.dst_pitch;
83 Memory::CopyBlock(dest_line, source_line, regs.x_count); 79 memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
84 } 80 }
85 return; 81 return;
86 } 82 }
@@ -89,15 +85,18 @@ void MaxwellDMA::HandleCopy() {
89 85
90 const std::size_t copy_size = regs.x_count * regs.y_count; 86 const std::size_t copy_size = regs.x_count * regs.y_count;
91 87
88 auto source_ptr{memory_manager.GetPointer(source)};
89 auto dst_ptr{memory_manager.GetPointer(dest)};
90
92 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { 91 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
93 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 92 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
94 // copying. 93 // copying.
95 Core::System::GetInstance().GPU().FlushRegion(*source_cpu, src_size); 94 rasterizer.FlushRegion(ToCacheAddr(source_ptr), src_size);
96 95
97 // We have to invalidate the destination region to evict any outdated surfaces from the 96 // We have to invalidate the destination region to evict any outdated surfaces from the
98 // cache. We do this before actually writing the new data because the destination address 97 // cache. We do this before actually writing the new data because the destination address
99 // might contain a dirty surface that will have to be written back to memory. 98 // might contain a dirty surface that will have to be written back to memory.
100 Core::System::GetInstance().GPU().InvalidateRegion(*dest_cpu, dst_size); 99 rasterizer.InvalidateRegion(ToCacheAddr(dst_ptr), dst_size);
101 }; 100 };
102 101
103 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 102 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
@@ -110,8 +109,8 @@ void MaxwellDMA::HandleCopy() {
110 copy_size * src_bytes_per_pixel); 109 copy_size * src_bytes_per_pixel);
111 110
112 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, 111 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
113 regs.src_params.size_x, src_bytes_per_pixel, *source_cpu, 112 regs.src_params.size_x, src_bytes_per_pixel, source_ptr, dst_ptr,
114 *dest_cpu, regs.src_params.BlockHeight(), regs.src_params.pos_x, 113 regs.src_params.BlockHeight(), regs.src_params.pos_x,
115 regs.src_params.pos_y); 114 regs.src_params.pos_y);
116 } else { 115 } else {
117 ASSERT(regs.dst_params.size_z == 1); 116 ASSERT(regs.dst_params.size_z == 1);
@@ -124,7 +123,7 @@ void MaxwellDMA::HandleCopy() {
124 123
125 // If the input is linear and the output is tiled, swizzle the input and copy it over. 124 // If the input is linear and the output is tiled, swizzle the input and copy it over.
126 Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, 125 Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
127 src_bpp, *dest_cpu, *source_cpu, regs.dst_params.BlockHeight()); 126 src_bpp, dst_ptr, source_ptr, regs.dst_params.BlockHeight());
128 } 127 }
129} 128}
130 129
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 08abf8ac9..66c690494 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -274,7 +274,6 @@ void GPU::ProcessSemaphoreTriggerMethod() {
274 const auto op = 274 const auto op =
275 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask); 275 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
276 if (op == GpuSemaphoreOperation::WriteLong) { 276 if (op == GpuSemaphoreOperation::WriteLong) {
277 auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
278 struct Block { 277 struct Block {
279 u32 sequence; 278 u32 sequence;
280 u32 zeros = 0; 279 u32 zeros = 0;
@@ -286,11 +285,9 @@ void GPU::ProcessSemaphoreTriggerMethod() {
286 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of 285 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
287 // CoreTiming 286 // CoreTiming
288 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); 287 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
289 Memory::WriteBlock(*address, &block, sizeof(block)); 288 memory_manager->WriteBlock(regs.smaphore_address.SmaphoreAddress(), &block, sizeof(block));
290 } else { 289 } else {
291 const auto address = 290 const u32 word{memory_manager->Read32(regs.smaphore_address.SmaphoreAddress())};
292 memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
293 const u32 word = Memory::Read32(*address);
294 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || 291 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
295 (op == GpuSemaphoreOperation::AcquireGequal && 292 (op == GpuSemaphoreOperation::AcquireGequal &&
296 static_cast<s32>(word - regs.semaphore_sequence) > 0) || 293 static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
@@ -317,13 +314,11 @@ void GPU::ProcessSemaphoreTriggerMethod() {
317} 314}
318 315
319void GPU::ProcessSemaphoreRelease() { 316void GPU::ProcessSemaphoreRelease() {
320 const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress()); 317 memory_manager->Write32(regs.smaphore_address.SmaphoreAddress(), regs.semaphore_release);
321 Memory::Write32(*address, regs.semaphore_release);
322} 318}
323 319
324void GPU::ProcessSemaphoreAcquire() { 320void GPU::ProcessSemaphoreAcquire() {
325 const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress()); 321 const u32 word = memory_manager->Read32(regs.smaphore_address.SmaphoreAddress());
326 const u32 word = Memory::Read32(*address);
327 const auto value = regs.semaphore_acquire; 322 const auto value = regs.semaphore_acquire;
328 if (word != value) { 323 if (word != value) {
329 regs.acquire_active = true; 324 regs.acquire_active = true;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 56a203275..a14b95c30 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -11,6 +11,11 @@
11#include "video_core/dma_pusher.h" 11#include "video_core/dma_pusher.h"
12#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
13 13
14using CacheAddr = std::uintptr_t;
15inline CacheAddr ToCacheAddr(const void* host_ptr) {
16 return reinterpret_cast<CacheAddr>(host_ptr);
17}
18
14namespace Core { 19namespace Core {
15class System; 20class System;
16} 21}
@@ -209,13 +214,13 @@ public:
209 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0; 214 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
210 215
211 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 216 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
212 virtual void FlushRegion(VAddr addr, u64 size) = 0; 217 virtual void FlushRegion(CacheAddr addr, u64 size) = 0;
213 218
214 /// Notify rasterizer that any caches of the specified region should be invalidated 219 /// Notify rasterizer that any caches of the specified region should be invalidated
215 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 220 virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0;
216 221
217 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 222 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
218 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 223 virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
219 224
220private: 225private:
221 void ProcessBindMethod(const MethodCall& method_call); 226 void ProcessBindMethod(const MethodCall& method_call);
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index ad0a747e3..8b355cf7b 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -22,15 +22,15 @@ void GPUAsynch::SwapBuffers(
22 gpu_thread.SwapBuffers(std::move(framebuffer)); 22 gpu_thread.SwapBuffers(std::move(framebuffer));
23} 23}
24 24
25void GPUAsynch::FlushRegion(VAddr addr, u64 size) { 25void GPUAsynch::FlushRegion(CacheAddr addr, u64 size) {
26 gpu_thread.FlushRegion(addr, size); 26 gpu_thread.FlushRegion(addr, size);
27} 27}
28 28
29void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) { 29void GPUAsynch::InvalidateRegion(CacheAddr addr, u64 size) {
30 gpu_thread.InvalidateRegion(addr, size); 30 gpu_thread.InvalidateRegion(addr, size);
31} 31}
32 32
33void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { 33void GPUAsynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
34 gpu_thread.FlushAndInvalidateRegion(addr, size); 34 gpu_thread.FlushAndInvalidateRegion(addr, size);
35} 35}
36 36
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index e6a807aba..1dcc61a6c 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -26,9 +26,9 @@ public:
26 void PushGPUEntries(Tegra::CommandList&& entries) override; 26 void PushGPUEntries(Tegra::CommandList&& entries) override;
27 void SwapBuffers( 27 void SwapBuffers(
28 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 28 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
29 void FlushRegion(VAddr addr, u64 size) override; 29 void FlushRegion(CacheAddr addr, u64 size) override;
30 void InvalidateRegion(VAddr addr, u64 size) override; 30 void InvalidateRegion(CacheAddr addr, u64 size) override;
31 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 31 void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
32 32
33private: 33private:
34 GPUThread::ThreadManager gpu_thread; 34 GPUThread::ThreadManager gpu_thread;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 4c00b96c7..2cfc900ed 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -22,15 +22,15 @@ void GPUSynch::SwapBuffers(
22 renderer.SwapBuffers(std::move(framebuffer)); 22 renderer.SwapBuffers(std::move(framebuffer));
23} 23}
24 24
25void GPUSynch::FlushRegion(VAddr addr, u64 size) { 25void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
26 renderer.Rasterizer().FlushRegion(addr, size); 26 renderer.Rasterizer().FlushRegion(addr, size);
27} 27}
28 28
29void GPUSynch::InvalidateRegion(VAddr addr, u64 size) { 29void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) {
30 renderer.Rasterizer().InvalidateRegion(addr, size); 30 renderer.Rasterizer().InvalidateRegion(addr, size);
31} 31}
32 32
33void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { 33void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
34 renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); 34 renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
35} 35}
36 36
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 7d5a241ff..766b5631c 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -21,9 +21,9 @@ public:
21 void PushGPUEntries(Tegra::CommandList&& entries) override; 21 void PushGPUEntries(Tegra::CommandList&& entries) override;
22 void SwapBuffers( 22 void SwapBuffers(
23 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 23 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
24 void FlushRegion(VAddr addr, u64 size) override; 24 void FlushRegion(CacheAddr addr, u64 size) override;
25 void InvalidateRegion(VAddr addr, u64 size) override; 25 void InvalidateRegion(CacheAddr addr, u64 size) override;
26 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 26 void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
27}; 27};
28 28
29} // namespace VideoCommon 29} // namespace VideoCommon
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index c5bdd2a17..086b2f625 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -5,7 +5,6 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/frontend/scope_acquire_window_context.h" 7#include "core/frontend/scope_acquire_window_context.h"
8#include "core/settings.h"
9#include "video_core/dma_pusher.h" 8#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h" 9#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h" 10#include "video_core/gpu_thread.h"
@@ -13,38 +12,13 @@
13 12
14namespace VideoCommon::GPUThread { 13namespace VideoCommon::GPUThread {
15 14
16/// Executes a single GPU thread command
17static void ExecuteCommand(CommandData* command, VideoCore::RendererBase& renderer,
18 Tegra::DmaPusher& dma_pusher) {
19 if (const auto submit_list = std::get_if<SubmitListCommand>(command)) {
20 dma_pusher.Push(std::move(submit_list->entries));
21 dma_pusher.DispatchCalls();
22 } else if (const auto data = std::get_if<SwapBuffersCommand>(command)) {
23 renderer.SwapBuffers(data->framebuffer);
24 } else if (const auto data = std::get_if<FlushRegionCommand>(command)) {
25 renderer.Rasterizer().FlushRegion(data->addr, data->size);
26 } else if (const auto data = std::get_if<InvalidateRegionCommand>(command)) {
27 renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
28 } else if (const auto data = std::get_if<FlushAndInvalidateRegionCommand>(command)) {
29 renderer.Rasterizer().FlushAndInvalidateRegion(data->addr, data->size);
30 } else {
31 UNREACHABLE();
32 }
33}
34
35/// Runs the GPU thread 15/// Runs the GPU thread
36static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, 16static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
37 SynchState& state) { 17 SynchState& state) {
38
39 MicroProfileOnThreadCreate("GpuThread"); 18 MicroProfileOnThreadCreate("GpuThread");
40 19
41 auto WaitForWakeup = [&]() {
42 std::unique_lock<std::mutex> lock{state.signal_mutex};
43 state.signal_condition.wait(lock, [&] { return !state.is_idle || !state.is_running; });
44 };
45
46 // Wait for first GPU command before acquiring the window context 20 // Wait for first GPU command before acquiring the window context
47 WaitForWakeup(); 21 state.WaitForCommands();
48 22
49 // If emulation was stopped during disk shader loading, abort before trying to acquire context 23 // If emulation was stopped during disk shader loading, abort before trying to acquire context
50 if (!state.is_running) { 24 if (!state.is_running) {
@@ -53,100 +27,72 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
53 27
54 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; 28 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()};
55 29
30 CommandDataContainer next;
56 while (state.is_running) { 31 while (state.is_running) {
57 if (!state.is_running) { 32 state.WaitForCommands();
58 return; 33 while (!state.queue.Empty()) {
59 } 34 state.queue.Pop(next);
60 35 if (const auto submit_list = std::get_if<SubmitListCommand>(&next.data)) {
61 { 36 dma_pusher.Push(std::move(submit_list->entries));
62 // Thread has been woken up, so make the previous write queue the next read queue 37 dma_pusher.DispatchCalls();
63 std::lock_guard<std::mutex> lock{state.signal_mutex}; 38 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
64 std::swap(state.push_queue, state.pop_queue); 39 state.DecrementFramesCounter();
65 } 40 renderer.SwapBuffers(std::move(data->framebuffer));
66 41 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
67 // Execute all of the GPU commands 42 renderer.Rasterizer().FlushRegion(data->addr, data->size);
68 while (!state.pop_queue->empty()) { 43 } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) {
69 ExecuteCommand(&state.pop_queue->front(), renderer, dma_pusher); 44 renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
70 state.pop_queue->pop(); 45 } else if (const auto data = std::get_if<EndProcessingCommand>(&next.data)) {
46 return;
47 } else {
48 UNREACHABLE();
49 }
71 } 50 }
72
73 state.UpdateIdleState();
74
75 // Signal that the GPU thread has finished processing commands
76 if (state.is_idle) {
77 state.idle_condition.notify_one();
78 }
79
80 // Wait for CPU thread to send more GPU commands
81 WaitForWakeup();
82 } 51 }
83} 52}
84 53
85ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) 54ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher)
86 : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer), 55 : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer),
87 std::ref(dma_pusher), std::ref(state)}, 56 std::ref(dma_pusher), std::ref(state)} {}
88 thread_id{thread.get_id()} {}
89 57
90ThreadManager::~ThreadManager() { 58ThreadManager::~ThreadManager() {
91 { 59 // Notify GPU thread that a shutdown is pending
92 // Notify GPU thread that a shutdown is pending 60 PushCommand(EndProcessingCommand());
93 std::lock_guard<std::mutex> lock{state.signal_mutex};
94 state.is_running = false;
95 }
96
97 state.signal_condition.notify_one();
98 thread.join(); 61 thread.join();
99} 62}
100 63
101void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 64void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
102 if (entries.empty()) { 65 PushCommand(SubmitListCommand(std::move(entries)));
103 return;
104 }
105
106 PushCommand(SubmitListCommand(std::move(entries)), false, false);
107} 66}
108 67
109void ThreadManager::SwapBuffers( 68void ThreadManager::SwapBuffers(
110 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { 69 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
111 PushCommand(SwapBuffersCommand(std::move(framebuffer)), true, false); 70 state.IncrementFramesCounter();
71 PushCommand(SwapBuffersCommand(std::move(framebuffer)));
72 state.WaitForFrames();
112} 73}
113 74
114void ThreadManager::FlushRegion(VAddr addr, u64 size) { 75void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
115 // Block the CPU when using accurate emulation 76 PushCommand(FlushRegionCommand(addr, size));
116 PushCommand(FlushRegionCommand(addr, size), Settings::values.use_accurate_gpu_emulation, false);
117} 77}
118 78
119void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { 79void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) {
120 PushCommand(InvalidateRegionCommand(addr, size), true, true); 80 if (state.queue.Empty()) {
81 // It's quicker to invalidate a single region on the CPU if the queue is already empty
82 renderer.Rasterizer().InvalidateRegion(addr, size);
83 } else {
84 PushCommand(InvalidateRegionCommand(addr, size));
85 }
121} 86}
122 87
123void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { 88void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
89 // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
124 InvalidateRegion(addr, size); 90 InvalidateRegion(addr, size);
125} 91}
126 92
127void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) { 93void ThreadManager::PushCommand(CommandData&& command_data) {
128 { 94 state.queue.Push(CommandDataContainer(std::move(command_data)));
129 std::lock_guard<std::mutex> lock{state.signal_mutex}; 95 state.SignalCommands();
130
131 if ((allow_on_cpu && state.is_idle) || IsGpuThread()) {
132 // Execute the command synchronously on the current thread
133 ExecuteCommand(&command_data, renderer, dma_pusher);
134 return;
135 }
136
137 // Push the command to the GPU thread
138 state.UpdateIdleState();
139 state.push_queue->emplace(command_data);
140 }
141
142 // Signal the GPU thread that commands are pending
143 state.signal_condition.notify_one();
144
145 if (wait_for_idle) {
146 // Wait for the GPU to be idle (all commands to be executed)
147 std::unique_lock<std::mutex> lock{state.idle_mutex};
148 state.idle_condition.wait(lock, [this] { return static_cast<bool>(state.is_idle); });
149 }
150} 96}
151 97
152} // namespace VideoCommon::GPUThread 98} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index edb148b14..8cd7db1c6 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -13,6 +13,9 @@
13#include <thread> 13#include <thread>
14#include <variant> 14#include <variant>
15 15
16#include "common/threadsafe_queue.h"
17#include "video_core/gpu.h"
18
16namespace Tegra { 19namespace Tegra {
17struct FramebufferConfig; 20struct FramebufferConfig;
18class DmaPusher; 21class DmaPusher;
@@ -24,6 +27,9 @@ class RendererBase;
24 27
25namespace VideoCommon::GPUThread { 28namespace VideoCommon::GPUThread {
26 29
30/// Command to signal to the GPU thread that processing has ended
31struct EndProcessingCommand final {};
32
27/// Command to signal to the GPU thread that a command list is ready for processing 33/// Command to signal to the GPU thread that a command list is ready for processing
28struct SubmitListCommand final { 34struct SubmitListCommand final {
29 explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {} 35 explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {}
@@ -36,59 +42,110 @@ struct SwapBuffersCommand final {
36 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer) 42 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
37 : framebuffer{std::move(framebuffer)} {} 43 : framebuffer{std::move(framebuffer)} {}
38 44
39 std::optional<const Tegra::FramebufferConfig> framebuffer; 45 std::optional<Tegra::FramebufferConfig> framebuffer;
40}; 46};
41 47
42/// Command to signal to the GPU thread to flush a region 48/// Command to signal to the GPU thread to flush a region
43struct FlushRegionCommand final { 49struct FlushRegionCommand final {
44 explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} 50 explicit constexpr FlushRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {}
45 51
46 const VAddr addr; 52 CacheAddr addr;
47 const u64 size; 53 u64 size;
48}; 54};
49 55
50/// Command to signal to the GPU thread to invalidate a region 56/// Command to signal to the GPU thread to invalidate a region
51struct InvalidateRegionCommand final { 57struct InvalidateRegionCommand final {
52 explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} 58 explicit constexpr InvalidateRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {}
53 59
54 const VAddr addr; 60 CacheAddr addr;
55 const u64 size; 61 u64 size;
56}; 62};
57 63
58/// Command to signal to the GPU thread to flush and invalidate a region 64/// Command to signal to the GPU thread to flush and invalidate a region
59struct FlushAndInvalidateRegionCommand final { 65struct FlushAndInvalidateRegionCommand final {
60 explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size) 66 explicit constexpr FlushAndInvalidateRegionCommand(CacheAddr addr, u64 size)
61 : addr{addr}, size{size} {} 67 : addr{addr}, size{size} {}
62 68
63 const VAddr addr; 69 CacheAddr addr;
64 const u64 size; 70 u64 size;
65}; 71};
66 72
67using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 73using CommandData =
68 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>; 74 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
75 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>;
76
77struct CommandDataContainer {
78 CommandDataContainer() = default;
79
80 CommandDataContainer(CommandData&& data) : data{std::move(data)} {}
81
82 CommandDataContainer& operator=(const CommandDataContainer& t) {
83 data = std::move(t.data);
84 return *this;
85 }
86
87 CommandData data;
88};
69 89
70/// Struct used to synchronize the GPU thread 90/// Struct used to synchronize the GPU thread
71struct SynchState final { 91struct SynchState final {
72 std::atomic<bool> is_running{true}; 92 std::atomic_bool is_running{true};
73 std::atomic<bool> is_idle{true}; 93 std::atomic_int queued_frame_count{};
74 std::condition_variable signal_condition; 94 std::mutex frames_mutex;
75 std::mutex signal_mutex; 95 std::mutex commands_mutex;
76 std::condition_variable idle_condition; 96 std::condition_variable commands_condition;
77 std::mutex idle_mutex; 97 std::condition_variable frames_condition;
78 98
79 // We use two queues for sending commands to the GPU thread, one for writing (push_queue) to and 99 void IncrementFramesCounter() {
80 // one for reading from (pop_queue). These are swapped whenever the current pop_queue becomes 100 std::lock_guard<std::mutex> lock{frames_mutex};
81 // empty. This allows for efficient thread-safe access, as it does not require any copies. 101 ++queued_frame_count;
82 102 }
83 using CommandQueue = std::queue<CommandData>; 103
84 std::array<CommandQueue, 2> command_queues; 104 void DecrementFramesCounter() {
85 CommandQueue* push_queue{&command_queues[0]}; 105 {
86 CommandQueue* pop_queue{&command_queues[1]}; 106 std::lock_guard<std::mutex> lock{frames_mutex};
87 107 --queued_frame_count;
88 void UpdateIdleState() { 108
89 std::lock_guard<std::mutex> lock{idle_mutex}; 109 if (queued_frame_count) {
90 is_idle = command_queues[0].empty() && command_queues[1].empty(); 110 return;
111 }
112 }
113 frames_condition.notify_one();
91 } 114 }
115
116 void WaitForFrames() {
117 {
118 std::lock_guard<std::mutex> lock{frames_mutex};
119 if (!queued_frame_count) {
120 return;
121 }
122 }
123
124 // Wait for the GPU to be idle (all commands to be executed)
125 {
126 std::unique_lock<std::mutex> lock{frames_mutex};
127 frames_condition.wait(lock, [this] { return !queued_frame_count; });
128 }
129 }
130
131 void SignalCommands() {
132 {
133 std::unique_lock<std::mutex> lock{commands_mutex};
134 if (queue.Empty()) {
135 return;
136 }
137 }
138
139 commands_condition.notify_one();
140 }
141
142 void WaitForCommands() {
143 std::unique_lock<std::mutex> lock{commands_mutex};
144 commands_condition.wait(lock, [this] { return !queue.Empty(); });
145 }
146
147 using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
148 CommandQueue queue;
92}; 149};
93 150
94/// Class used to manage the GPU thread 151/// Class used to manage the GPU thread
@@ -105,22 +162,17 @@ public:
105 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer); 162 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
106 163
107 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 164 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
108 void FlushRegion(VAddr addr, u64 size); 165 void FlushRegion(CacheAddr addr, u64 size);
109 166
110 /// Notify rasterizer that any caches of the specified region should be invalidated 167 /// Notify rasterizer that any caches of the specified region should be invalidated
111 void InvalidateRegion(VAddr addr, u64 size); 168 void InvalidateRegion(CacheAddr addr, u64 size);
112 169
113 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 170 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
114 void FlushAndInvalidateRegion(VAddr addr, u64 size); 171 void FlushAndInvalidateRegion(CacheAddr addr, u64 size);
115 172
116private: 173private:
117 /// Pushes a command to be executed by the GPU thread 174 /// Pushes a command to be executed by the GPU thread
118 void PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu); 175 void PushCommand(CommandData&& command_data);
119
120 /// Returns true if this is called by the GPU thread
121 bool IsGpuThread() const {
122 return std::this_thread::get_id() == thread_id;
123 }
124 176
125private: 177private:
126 SynchState state; 178 SynchState state;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 54abe5298..8e8f36f28 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -5,6 +5,7 @@
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/memory.h"
8#include "video_core/memory_manager.h" 9#include "video_core/memory_manager.h"
9 10
10namespace Tegra { 11namespace Tegra {
@@ -162,15 +163,51 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
162 return base_addr + (gpu_addr & PAGE_MASK); 163 return base_addr + (gpu_addr & PAGE_MASK);
163} 164}
164 165
165std::vector<GPUVAddr> MemoryManager::CpuToGpuAddress(VAddr cpu_addr) const { 166u8 MemoryManager::Read8(GPUVAddr addr) {
166 std::vector<GPUVAddr> results; 167 return Memory::Read8(*GpuToCpuAddress(addr));
167 for (const auto& region : mapped_regions) { 168}
168 if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) { 169
169 const u64 offset{cpu_addr - region.cpu_addr}; 170u16 MemoryManager::Read16(GPUVAddr addr) {
170 results.push_back(region.gpu_addr + offset); 171 return Memory::Read16(*GpuToCpuAddress(addr));
171 } 172}
172 } 173
173 return results; 174u32 MemoryManager::Read32(GPUVAddr addr) {
175 return Memory::Read32(*GpuToCpuAddress(addr));
176}
177
178u64 MemoryManager::Read64(GPUVAddr addr) {
179 return Memory::Read64(*GpuToCpuAddress(addr));
180}
181
182void MemoryManager::Write8(GPUVAddr addr, u8 data) {
183 Memory::Write8(*GpuToCpuAddress(addr), data);
184}
185
186void MemoryManager::Write16(GPUVAddr addr, u16 data) {
187 Memory::Write16(*GpuToCpuAddress(addr), data);
188}
189
190void MemoryManager::Write32(GPUVAddr addr, u32 data) {
191 Memory::Write32(*GpuToCpuAddress(addr), data);
192}
193
194void MemoryManager::Write64(GPUVAddr addr, u64 data) {
195 Memory::Write64(*GpuToCpuAddress(addr), data);
196}
197
198u8* MemoryManager::GetPointer(GPUVAddr addr) {
199 return Memory::GetPointer(*GpuToCpuAddress(addr));
200}
201
202void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) {
203 std::memcpy(dest_buffer, GetPointer(src_addr), size);
204}
205void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) {
206 std::memcpy(GetPointer(dest_addr), src_buffer, size);
207}
208
209void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) {
210 std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size);
174} 211}
175 212
176VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) { 213VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) {
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index fb03497ca..425e2f31c 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -27,12 +27,27 @@ public:
27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const; 28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
31 30
32 static constexpr u64 PAGE_BITS = 16; 31 static constexpr u64 PAGE_BITS = 16;
33 static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; 32 static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 33 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
35 34
35 u8 Read8(GPUVAddr addr);
36 u16 Read16(GPUVAddr addr);
37 u32 Read32(GPUVAddr addr);
38 u64 Read64(GPUVAddr addr);
39
40 void Write8(GPUVAddr addr, u8 data);
41 void Write16(GPUVAddr addr, u16 data);
42 void Write32(GPUVAddr addr, u32 data);
43 void Write64(GPUVAddr addr, u64 data);
44
45 u8* GetPointer(GPUVAddr vaddr);
46
47 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size);
48 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
49 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
50
36private: 51private:
37 enum class PageStatus : u64 { 52 enum class PageStatus : u64 {
38 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 53 Unmapped = 0xFFFFFFFFFFFFFFFFULL,
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index b68f4fb13..3e91cbc83 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -6,7 +6,6 @@
6#include <cstring> 6#include <cstring>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/memory.h"
10#include "video_core/morton.h" 9#include "video_core/morton.h"
11#include "video_core/surface.h" 10#include "video_core/surface.h"
12#include "video_core/textures/decoders.h" 11#include "video_core/textures/decoders.h"
@@ -16,12 +15,12 @@ namespace VideoCore {
16using Surface::GetBytesPerPixel; 15using Surface::GetBytesPerPixel;
17using Surface::PixelFormat; 16using Surface::PixelFormat;
18 17
19using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, std::size_t, VAddr); 18using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, u8*);
20using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>; 19using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>;
21 20
22template <bool morton_to_linear, PixelFormat format> 21template <bool morton_to_linear, PixelFormat format>
23static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, 22static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth,
24 u32 tile_width_spacing, u8* buffer, std::size_t buffer_size, VAddr addr) { 23 u32 tile_width_spacing, u8* buffer, u8* addr) {
25 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format); 24 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
26 25
27 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual 26 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
@@ -34,150 +33,146 @@ static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth
34 stride, height, depth, block_height, block_depth, 33 stride, height, depth, block_height, block_depth,
35 tile_width_spacing); 34 tile_width_spacing);
36 } else { 35 } else {
37 Tegra::Texture::CopySwizzledData( 36 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
38 (stride + tile_size_x - 1) / tile_size_x, (height + tile_size_y - 1) / tile_size_y, 37 (height + tile_size_y - 1) / tile_size_y, depth,
39 depth, bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), buffer, false, 38 bytes_per_pixel, bytes_per_pixel, addr, buffer, false,
40 block_height, block_depth, tile_width_spacing); 39 block_height, block_depth, tile_width_spacing);
41 } 40 }
42} 41}
43 42
44static constexpr ConversionArray morton_to_linear_fns = { 43static constexpr ConversionArray morton_to_linear_fns = {
45 // clang-format off 44 MortonCopy<true, PixelFormat::ABGR8U>,
46 MortonCopy<true, PixelFormat::ABGR8U>, 45 MortonCopy<true, PixelFormat::ABGR8S>,
47 MortonCopy<true, PixelFormat::ABGR8S>, 46 MortonCopy<true, PixelFormat::ABGR8UI>,
48 MortonCopy<true, PixelFormat::ABGR8UI>, 47 MortonCopy<true, PixelFormat::B5G6R5U>,
49 MortonCopy<true, PixelFormat::B5G6R5U>, 48 MortonCopy<true, PixelFormat::A2B10G10R10U>,
50 MortonCopy<true, PixelFormat::A2B10G10R10U>, 49 MortonCopy<true, PixelFormat::A1B5G5R5U>,
51 MortonCopy<true, PixelFormat::A1B5G5R5U>, 50 MortonCopy<true, PixelFormat::R8U>,
52 MortonCopy<true, PixelFormat::R8U>, 51 MortonCopy<true, PixelFormat::R8UI>,
53 MortonCopy<true, PixelFormat::R8UI>, 52 MortonCopy<true, PixelFormat::RGBA16F>,
54 MortonCopy<true, PixelFormat::RGBA16F>, 53 MortonCopy<true, PixelFormat::RGBA16U>,
55 MortonCopy<true, PixelFormat::RGBA16U>, 54 MortonCopy<true, PixelFormat::RGBA16UI>,
56 MortonCopy<true, PixelFormat::RGBA16UI>, 55 MortonCopy<true, PixelFormat::R11FG11FB10F>,
57 MortonCopy<true, PixelFormat::R11FG11FB10F>, 56 MortonCopy<true, PixelFormat::RGBA32UI>,
58 MortonCopy<true, PixelFormat::RGBA32UI>, 57 MortonCopy<true, PixelFormat::DXT1>,
59 MortonCopy<true, PixelFormat::DXT1>, 58 MortonCopy<true, PixelFormat::DXT23>,
60 MortonCopy<true, PixelFormat::DXT23>, 59 MortonCopy<true, PixelFormat::DXT45>,
61 MortonCopy<true, PixelFormat::DXT45>, 60 MortonCopy<true, PixelFormat::DXN1>,
62 MortonCopy<true, PixelFormat::DXN1>, 61 MortonCopy<true, PixelFormat::DXN2UNORM>,
63 MortonCopy<true, PixelFormat::DXN2UNORM>, 62 MortonCopy<true, PixelFormat::DXN2SNORM>,
64 MortonCopy<true, PixelFormat::DXN2SNORM>, 63 MortonCopy<true, PixelFormat::BC7U>,
65 MortonCopy<true, PixelFormat::BC7U>, 64 MortonCopy<true, PixelFormat::BC6H_UF16>,
66 MortonCopy<true, PixelFormat::BC6H_UF16>, 65 MortonCopy<true, PixelFormat::BC6H_SF16>,
67 MortonCopy<true, PixelFormat::BC6H_SF16>, 66 MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
68 MortonCopy<true, PixelFormat::ASTC_2D_4X4>, 67 MortonCopy<true, PixelFormat::BGRA8>,
69 MortonCopy<true, PixelFormat::BGRA8>, 68 MortonCopy<true, PixelFormat::RGBA32F>,
70 MortonCopy<true, PixelFormat::RGBA32F>, 69 MortonCopy<true, PixelFormat::RG32F>,
71 MortonCopy<true, PixelFormat::RG32F>, 70 MortonCopy<true, PixelFormat::R32F>,
72 MortonCopy<true, PixelFormat::R32F>, 71 MortonCopy<true, PixelFormat::R16F>,
73 MortonCopy<true, PixelFormat::R16F>, 72 MortonCopy<true, PixelFormat::R16U>,
74 MortonCopy<true, PixelFormat::R16U>, 73 MortonCopy<true, PixelFormat::R16S>,
75 MortonCopy<true, PixelFormat::R16S>, 74 MortonCopy<true, PixelFormat::R16UI>,
76 MortonCopy<true, PixelFormat::R16UI>, 75 MortonCopy<true, PixelFormat::R16I>,
77 MortonCopy<true, PixelFormat::R16I>, 76 MortonCopy<true, PixelFormat::RG16>,
78 MortonCopy<true, PixelFormat::RG16>, 77 MortonCopy<true, PixelFormat::RG16F>,
79 MortonCopy<true, PixelFormat::RG16F>, 78 MortonCopy<true, PixelFormat::RG16UI>,
80 MortonCopy<true, PixelFormat::RG16UI>, 79 MortonCopy<true, PixelFormat::RG16I>,
81 MortonCopy<true, PixelFormat::RG16I>, 80 MortonCopy<true, PixelFormat::RG16S>,
82 MortonCopy<true, PixelFormat::RG16S>, 81 MortonCopy<true, PixelFormat::RGB32F>,
83 MortonCopy<true, PixelFormat::RGB32F>, 82 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
84 MortonCopy<true, PixelFormat::RGBA8_SRGB>, 83 MortonCopy<true, PixelFormat::RG8U>,
85 MortonCopy<true, PixelFormat::RG8U>, 84 MortonCopy<true, PixelFormat::RG8S>,
86 MortonCopy<true, PixelFormat::RG8S>, 85 MortonCopy<true, PixelFormat::RG32UI>,
87 MortonCopy<true, PixelFormat::RG32UI>, 86 MortonCopy<true, PixelFormat::R32UI>,
88 MortonCopy<true, PixelFormat::R32UI>, 87 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
89 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 88 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
90 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 89 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
91 MortonCopy<true, PixelFormat::ASTC_2D_5X4>, 90 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
92 MortonCopy<true, PixelFormat::BGRA8_SRGB>, 91 MortonCopy<true, PixelFormat::DXT1_SRGB>,
93 MortonCopy<true, PixelFormat::DXT1_SRGB>, 92 MortonCopy<true, PixelFormat::DXT23_SRGB>,
94 MortonCopy<true, PixelFormat::DXT23_SRGB>, 93 MortonCopy<true, PixelFormat::DXT45_SRGB>,
95 MortonCopy<true, PixelFormat::DXT45_SRGB>, 94 MortonCopy<true, PixelFormat::BC7U_SRGB>,
96 MortonCopy<true, PixelFormat::BC7U_SRGB>, 95 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
97 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>, 96 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
98 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, 97 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
99 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, 98 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
100 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, 99 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
101 MortonCopy<true, PixelFormat::ASTC_2D_5X5>, 100 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
102 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>, 101 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
103 MortonCopy<true, PixelFormat::ASTC_2D_10X8>, 102 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
104 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>, 103 MortonCopy<true, PixelFormat::Z32F>,
105 MortonCopy<true, PixelFormat::Z32F>, 104 MortonCopy<true, PixelFormat::Z16>,
106 MortonCopy<true, PixelFormat::Z16>, 105 MortonCopy<true, PixelFormat::Z24S8>,
107 MortonCopy<true, PixelFormat::Z24S8>, 106 MortonCopy<true, PixelFormat::S8Z24>,
108 MortonCopy<true, PixelFormat::S8Z24>, 107 MortonCopy<true, PixelFormat::Z32FS8>,
109 MortonCopy<true, PixelFormat::Z32FS8>,
110 // clang-format on
111}; 108};
112 109
113static constexpr ConversionArray linear_to_morton_fns = { 110static constexpr ConversionArray linear_to_morton_fns = {
114 // clang-format off 111 MortonCopy<false, PixelFormat::ABGR8U>,
115 MortonCopy<false, PixelFormat::ABGR8U>, 112 MortonCopy<false, PixelFormat::ABGR8S>,
116 MortonCopy<false, PixelFormat::ABGR8S>, 113 MortonCopy<false, PixelFormat::ABGR8UI>,
117 MortonCopy<false, PixelFormat::ABGR8UI>, 114 MortonCopy<false, PixelFormat::B5G6R5U>,
118 MortonCopy<false, PixelFormat::B5G6R5U>, 115 MortonCopy<false, PixelFormat::A2B10G10R10U>,
119 MortonCopy<false, PixelFormat::A2B10G10R10U>, 116 MortonCopy<false, PixelFormat::A1B5G5R5U>,
120 MortonCopy<false, PixelFormat::A1B5G5R5U>, 117 MortonCopy<false, PixelFormat::R8U>,
121 MortonCopy<false, PixelFormat::R8U>, 118 MortonCopy<false, PixelFormat::R8UI>,
122 MortonCopy<false, PixelFormat::R8UI>, 119 MortonCopy<false, PixelFormat::RGBA16F>,
123 MortonCopy<false, PixelFormat::RGBA16F>, 120 MortonCopy<false, PixelFormat::RGBA16U>,
124 MortonCopy<false, PixelFormat::RGBA16U>, 121 MortonCopy<false, PixelFormat::RGBA16UI>,
125 MortonCopy<false, PixelFormat::RGBA16UI>, 122 MortonCopy<false, PixelFormat::R11FG11FB10F>,
126 MortonCopy<false, PixelFormat::R11FG11FB10F>, 123 MortonCopy<false, PixelFormat::RGBA32UI>,
127 MortonCopy<false, PixelFormat::RGBA32UI>, 124 MortonCopy<false, PixelFormat::DXT1>,
128 MortonCopy<false, PixelFormat::DXT1>, 125 MortonCopy<false, PixelFormat::DXT23>,
129 MortonCopy<false, PixelFormat::DXT23>, 126 MortonCopy<false, PixelFormat::DXT45>,
130 MortonCopy<false, PixelFormat::DXT45>, 127 MortonCopy<false, PixelFormat::DXN1>,
131 MortonCopy<false, PixelFormat::DXN1>, 128 MortonCopy<false, PixelFormat::DXN2UNORM>,
132 MortonCopy<false, PixelFormat::DXN2UNORM>, 129 MortonCopy<false, PixelFormat::DXN2SNORM>,
133 MortonCopy<false, PixelFormat::DXN2SNORM>, 130 MortonCopy<false, PixelFormat::BC7U>,
134 MortonCopy<false, PixelFormat::BC7U>, 131 MortonCopy<false, PixelFormat::BC6H_UF16>,
135 MortonCopy<false, PixelFormat::BC6H_UF16>, 132 MortonCopy<false, PixelFormat::BC6H_SF16>,
136 MortonCopy<false, PixelFormat::BC6H_SF16>, 133 // TODO(Subv): Swizzling ASTC formats are not supported
137 // TODO(Subv): Swizzling ASTC formats are not supported 134 nullptr,
138 nullptr, 135 MortonCopy<false, PixelFormat::BGRA8>,
139 MortonCopy<false, PixelFormat::BGRA8>, 136 MortonCopy<false, PixelFormat::RGBA32F>,
140 MortonCopy<false, PixelFormat::RGBA32F>, 137 MortonCopy<false, PixelFormat::RG32F>,
141 MortonCopy<false, PixelFormat::RG32F>, 138 MortonCopy<false, PixelFormat::R32F>,
142 MortonCopy<false, PixelFormat::R32F>, 139 MortonCopy<false, PixelFormat::R16F>,
143 MortonCopy<false, PixelFormat::R16F>, 140 MortonCopy<false, PixelFormat::R16U>,
144 MortonCopy<false, PixelFormat::R16U>, 141 MortonCopy<false, PixelFormat::R16S>,
145 MortonCopy<false, PixelFormat::R16S>, 142 MortonCopy<false, PixelFormat::R16UI>,
146 MortonCopy<false, PixelFormat::R16UI>, 143 MortonCopy<false, PixelFormat::R16I>,
147 MortonCopy<false, PixelFormat::R16I>, 144 MortonCopy<false, PixelFormat::RG16>,
148 MortonCopy<false, PixelFormat::RG16>, 145 MortonCopy<false, PixelFormat::RG16F>,
149 MortonCopy<false, PixelFormat::RG16F>, 146 MortonCopy<false, PixelFormat::RG16UI>,
150 MortonCopy<false, PixelFormat::RG16UI>, 147 MortonCopy<false, PixelFormat::RG16I>,
151 MortonCopy<false, PixelFormat::RG16I>, 148 MortonCopy<false, PixelFormat::RG16S>,
152 MortonCopy<false, PixelFormat::RG16S>, 149 MortonCopy<false, PixelFormat::RGB32F>,
153 MortonCopy<false, PixelFormat::RGB32F>, 150 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
154 MortonCopy<false, PixelFormat::RGBA8_SRGB>, 151 MortonCopy<false, PixelFormat::RG8U>,
155 MortonCopy<false, PixelFormat::RG8U>, 152 MortonCopy<false, PixelFormat::RG8S>,
156 MortonCopy<false, PixelFormat::RG8S>, 153 MortonCopy<false, PixelFormat::RG32UI>,
157 MortonCopy<false, PixelFormat::RG32UI>, 154 MortonCopy<false, PixelFormat::R32UI>,
158 MortonCopy<false, PixelFormat::R32UI>, 155 nullptr,
159 nullptr, 156 nullptr,
160 nullptr, 157 nullptr,
161 nullptr, 158 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
162 MortonCopy<false, PixelFormat::BGRA8_SRGB>, 159 MortonCopy<false, PixelFormat::DXT1_SRGB>,
163 MortonCopy<false, PixelFormat::DXT1_SRGB>, 160 MortonCopy<false, PixelFormat::DXT23_SRGB>,
164 MortonCopy<false, PixelFormat::DXT23_SRGB>, 161 MortonCopy<false, PixelFormat::DXT45_SRGB>,
165 MortonCopy<false, PixelFormat::DXT45_SRGB>, 162 MortonCopy<false, PixelFormat::BC7U_SRGB>,
166 MortonCopy<false, PixelFormat::BC7U_SRGB>, 163 nullptr,
167 nullptr, 164 nullptr,
168 nullptr, 165 nullptr,
169 nullptr, 166 nullptr,
170 nullptr, 167 nullptr,
171 nullptr, 168 nullptr,
172 nullptr, 169 nullptr,
173 nullptr, 170 nullptr,
174 nullptr, 171 MortonCopy<false, PixelFormat::Z32F>,
175 MortonCopy<false, PixelFormat::Z32F>, 172 MortonCopy<false, PixelFormat::Z16>,
176 MortonCopy<false, PixelFormat::Z16>, 173 MortonCopy<false, PixelFormat::Z24S8>,
177 MortonCopy<false, PixelFormat::Z24S8>, 174 MortonCopy<false, PixelFormat::S8Z24>,
178 MortonCopy<false, PixelFormat::S8Z24>, 175 MortonCopy<false, PixelFormat::Z32FS8>,
179 MortonCopy<false, PixelFormat::Z32FS8>,
180 // clang-format on
181}; 176};
182 177
183static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) { 178static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) {
@@ -191,45 +186,6 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor
191 return morton_to_linear_fns[static_cast<std::size_t>(format)]; 186 return morton_to_linear_fns[static_cast<std::size_t>(format)];
192} 187}
193 188
194/// 8x8 Z-Order coordinate from 2D coordinates
195static u32 MortonInterleave(u32 x, u32 y) {
196 static const u32 xlut[] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15};
197 static const u32 ylut[] = {0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a};
198 return xlut[x % 8] + ylut[y % 8];
199}
200
201/// Calculates the offset of the position of the pixel in Morton order
202static u32 GetMortonOffset(u32 x, u32 y, u32 bytes_per_pixel) {
203 // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each
204 // of which is composed of four 2x2 subtiles each of which is composed of four texels.
205 // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g.
206 // texels are laid out in a 2x2 subtile like this:
207 // 2 3
208 // 0 1
209 //
210 // The full 8x8 tile has the texels arranged like this:
211 //
212 // 42 43 46 47 58 59 62 63
213 // 40 41 44 45 56 57 60 61
214 // 34 35 38 39 50 51 54 55
215 // 32 33 36 37 48 49 52 53
216 // 10 11 14 15 26 27 30 31
217 // 08 09 12 13 24 25 28 29
218 // 02 03 06 07 18 19 22 23
219 // 00 01 04 05 16 17 20 21
220 //
221 // This pattern is what's called Z-order curve, or Morton order.
222
223 const unsigned int block_height = 8;
224 const unsigned int coarse_x = x & ~7;
225
226 u32 i = MortonInterleave(x, y);
227
228 const unsigned int offset = coarse_x * block_height;
229
230 return (i + offset) * bytes_per_pixel;
231}
232
233static u32 MortonInterleave128(u32 x, u32 y) { 189static u32 MortonInterleave128(u32 x, u32 y) {
234 // 128x128 Z-Order coordinate from 2D coordinates 190 // 128x128 Z-Order coordinate from 2D coordinates
235 static constexpr u32 xlut[] = { 191 static constexpr u32 xlut[] = {
@@ -325,14 +281,14 @@ static u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
325 281
326void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride, 282void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride,
327 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing, 283 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
328 u8* buffer, std::size_t buffer_size, VAddr addr) { 284 u8* buffer, u8* addr) {
329
330 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth, 285 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth,
331 tile_width_spacing, buffer, buffer_size, addr); 286 tile_width_spacing, buffer, addr);
332} 287}
333 288
334void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 linear_bytes_per_pixel, 289void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
335 u8* morton_data, u8* linear_data, bool morton_to_linear) { 290 u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data) {
291 const bool morton_to_linear = mode == MortonSwizzleMode::MortonToLinear;
336 u8* data_ptrs[2]; 292 u8* data_ptrs[2];
337 for (u32 y = 0; y < height; ++y) { 293 for (u32 y = 0; y < height; ++y) {
338 for (u32 x = 0; x < width; ++x) { 294 for (u32 x = 0; x < width; ++x) {
diff --git a/src/video_core/morton.h b/src/video_core/morton.h
index 065f59ce3..ee5b45555 100644
--- a/src/video_core/morton.h
+++ b/src/video_core/morton.h
@@ -13,9 +13,9 @@ enum class MortonSwizzleMode { MortonToLinear, LinearToMorton };
13 13
14void MortonSwizzle(MortonSwizzleMode mode, VideoCore::Surface::PixelFormat format, u32 stride, 14void MortonSwizzle(MortonSwizzleMode mode, VideoCore::Surface::PixelFormat format, u32 stride,
15 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing, 15 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
16 u8* buffer, std::size_t buffer_size, VAddr addr); 16 u8* buffer, u8* addr);
17 17
18void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 linear_bytes_per_pixel, 18void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
19 u8* morton_data, u8* linear_data, bool morton_to_linear); 19 u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data);
20 20
21} // namespace VideoCore 21} // namespace VideoCore
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index a7bcf26fb..ecd9986a0 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <mutex>
7#include <set> 8#include <set>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -12,14 +13,26 @@
12 13
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "core/settings.h" 15#include "core/settings.h"
16#include "video_core/gpu.h"
15#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
16 18
17class RasterizerCacheObject { 19class RasterizerCacheObject {
18public: 20public:
21 explicit RasterizerCacheObject(const u8* host_ptr)
22 : host_ptr{host_ptr}, cache_addr{ToCacheAddr(host_ptr)} {}
23
19 virtual ~RasterizerCacheObject(); 24 virtual ~RasterizerCacheObject();
20 25
26 CacheAddr GetCacheAddr() const {
27 return cache_addr;
28 }
29
30 const u8* GetHostPtr() const {
31 return host_ptr;
32 }
33
21 /// Gets the address of the shader in guest memory, required for cache management 34 /// Gets the address of the shader in guest memory, required for cache management
22 virtual VAddr GetAddr() const = 0; 35 virtual VAddr GetCpuAddr() const = 0;
23 36
24 /// Gets the size of the shader in guest memory, required for cache management 37 /// Gets the size of the shader in guest memory, required for cache management
25 virtual std::size_t GetSizeInBytes() const = 0; 38 virtual std::size_t GetSizeInBytes() const = 0;
@@ -58,6 +71,8 @@ private:
58 bool is_registered{}; ///< Whether the object is currently registered with the cache 71 bool is_registered{}; ///< Whether the object is currently registered with the cache
59 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory) 72 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
60 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing 73 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
74 CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space
75 const u8* host_ptr{}; ///< Pointer to the memory backing this cached region
61}; 76};
62 77
63template <class T> 78template <class T>
@@ -68,7 +83,9 @@ public:
68 explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} 83 explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {}
69 84
70 /// Write any cached resources overlapping the specified region back to memory 85 /// Write any cached resources overlapping the specified region back to memory
71 void FlushRegion(Tegra::GPUVAddr addr, size_t size) { 86 void FlushRegion(CacheAddr addr, std::size_t size) {
87 std::lock_guard<std::recursive_mutex> lock{mutex};
88
72 const auto& objects{GetSortedObjectsFromRegion(addr, size)}; 89 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
73 for (auto& object : objects) { 90 for (auto& object : objects) {
74 FlushObject(object); 91 FlushObject(object);
@@ -76,7 +93,9 @@ public:
76 } 93 }
77 94
78 /// Mark the specified region as being invalidated 95 /// Mark the specified region as being invalidated
79 void InvalidateRegion(VAddr addr, u64 size) { 96 void InvalidateRegion(CacheAddr addr, u64 size) {
97 std::lock_guard<std::recursive_mutex> lock{mutex};
98
80 const auto& objects{GetSortedObjectsFromRegion(addr, size)}; 99 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
81 for (auto& object : objects) { 100 for (auto& object : objects) {
82 if (!object->IsRegistered()) { 101 if (!object->IsRegistered()) {
@@ -89,48 +108,60 @@ public:
89 108
90 /// Invalidates everything in the cache 109 /// Invalidates everything in the cache
91 void InvalidateAll() { 110 void InvalidateAll() {
111 std::lock_guard<std::recursive_mutex> lock{mutex};
112
92 while (interval_cache.begin() != interval_cache.end()) { 113 while (interval_cache.begin() != interval_cache.end()) {
93 Unregister(*interval_cache.begin()->second.begin()); 114 Unregister(*interval_cache.begin()->second.begin());
94 } 115 }
95 } 116 }
96 117
97protected: 118protected:
98 /// Tries to get an object from the cache with the specified address 119 /// Tries to get an object from the cache with the specified cache address
99 T TryGet(VAddr addr) const { 120 T TryGet(CacheAddr addr) const {
100 const auto iter = map_cache.find(addr); 121 const auto iter = map_cache.find(addr);
101 if (iter != map_cache.end()) 122 if (iter != map_cache.end())
102 return iter->second; 123 return iter->second;
103 return nullptr; 124 return nullptr;
104 } 125 }
105 126
127 T TryGet(const void* addr) const {
128 const auto iter = map_cache.find(ToCacheAddr(addr));
129 if (iter != map_cache.end())
130 return iter->second;
131 return nullptr;
132 }
133
106 /// Register an object into the cache 134 /// Register an object into the cache
107 void Register(const T& object) { 135 void Register(const T& object) {
136 std::lock_guard<std::recursive_mutex> lock{mutex};
137
108 object->SetIsRegistered(true); 138 object->SetIsRegistered(true);
109 interval_cache.add({GetInterval(object), ObjectSet{object}}); 139 interval_cache.add({GetInterval(object), ObjectSet{object}});
110 map_cache.insert({object->GetAddr(), object}); 140 map_cache.insert({object->GetCacheAddr(), object});
111 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 141 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), 1);
112 } 142 }
113 143
114 /// Unregisters an object from the cache 144 /// Unregisters an object from the cache
115 void Unregister(const T& object) { 145 void Unregister(const T& object) {
116 object->SetIsRegistered(false); 146 std::lock_guard<std::recursive_mutex> lock{mutex};
117 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
118 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
119 if (Settings::values.use_accurate_gpu_emulation) {
120 FlushObject(object);
121 }
122 147
148 object->SetIsRegistered(false);
149 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
123 interval_cache.subtract({GetInterval(object), ObjectSet{object}}); 150 interval_cache.subtract({GetInterval(object), ObjectSet{object}});
124 map_cache.erase(object->GetAddr()); 151 map_cache.erase(object->GetCacheAddr());
125 } 152 }
126 153
127 /// Returns a ticks counter used for tracking when cached objects were last modified 154 /// Returns a ticks counter used for tracking when cached objects were last modified
128 u64 GetModifiedTicks() { 155 u64 GetModifiedTicks() {
156 std::lock_guard<std::recursive_mutex> lock{mutex};
157
129 return ++modified_ticks; 158 return ++modified_ticks;
130 } 159 }
131 160
132 /// Flushes the specified object, updating appropriate cache state as needed 161 /// Flushes the specified object, updating appropriate cache state as needed
133 void FlushObject(const T& object) { 162 void FlushObject(const T& object) {
163 std::lock_guard<std::recursive_mutex> lock{mutex};
164
134 if (!object->IsDirty()) { 165 if (!object->IsDirty()) {
135 return; 166 return;
136 } 167 }
@@ -140,7 +171,7 @@ protected:
140 171
141private: 172private:
142 /// Returns a list of cached objects from the specified memory region, ordered by access time 173 /// Returns a list of cached objects from the specified memory region, ordered by access time
143 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) { 174 std::vector<T> GetSortedObjectsFromRegion(CacheAddr addr, u64 size) {
144 if (size == 0) { 175 if (size == 0) {
145 return {}; 176 return {};
146 } 177 }
@@ -164,17 +195,18 @@ private:
164 } 195 }
165 196
166 using ObjectSet = std::set<T>; 197 using ObjectSet = std::set<T>;
167 using ObjectCache = std::unordered_map<VAddr, T>; 198 using ObjectCache = std::unordered_map<CacheAddr, T>;
168 using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>; 199 using IntervalCache = boost::icl::interval_map<CacheAddr, ObjectSet>;
169 using ObjectInterval = typename IntervalCache::interval_type; 200 using ObjectInterval = typename IntervalCache::interval_type;
170 201
171 static auto GetInterval(const T& object) { 202 static auto GetInterval(const T& object) {
172 return ObjectInterval::right_open(object->GetAddr(), 203 return ObjectInterval::right_open(object->GetCacheAddr(),
173 object->GetAddr() + object->GetSizeInBytes()); 204 object->GetCacheAddr() + object->GetSizeInBytes());
174 } 205 }
175 206
176 ObjectCache map_cache; 207 ObjectCache map_cache;
177 IntervalCache interval_cache; ///< Cache of objects 208 IntervalCache interval_cache; ///< Cache of objects
178 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing 209 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
179 VideoCore::RasterizerInterface& rasterizer; 210 VideoCore::RasterizerInterface& rasterizer;
211 std::recursive_mutex mutex;
180}; 212};
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 6a1dc9cf6..76e292e87 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -35,14 +35,14 @@ public:
35 virtual void FlushAll() = 0; 35 virtual void FlushAll() = 0;
36 36
37 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 37 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
38 virtual void FlushRegion(VAddr addr, u64 size) = 0; 38 virtual void FlushRegion(CacheAddr addr, u64 size) = 0;
39 39
40 /// Notify rasterizer that any caches of the specified region should be invalidated 40 /// Notify rasterizer that any caches of the specified region should be invalidated
41 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 41 virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0;
42 42
43 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 43 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
44 /// and invalidated 44 /// and invalidated
45 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 45 virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
46 46
47 /// Attempt to use a faster method to perform a surface copy 47 /// Attempt to use a faster method to perform a surface copy
48 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 48 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
@@ -63,7 +63,7 @@ public:
63 } 63 }
64 64
65 /// Increase/decrease the number of object in pages touching the specified region 65 /// Increase/decrease the number of object in pages touching the specified region
66 virtual void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {} 66 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
67 67
68 /// Initialize disk cached resources for the game being emulated 68 /// Initialize disk cached resources for the game being emulated
69 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, 69 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index b3062e5ba..5048ed6ce 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -13,24 +13,28 @@
13 13
14namespace OpenGL { 14namespace OpenGL {
15 15
16CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr offset,
17 std::size_t alignment, u8* host_ptr)
18 : cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{
19 host_ptr} {}
20
16OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size) 21OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
17 : RasterizerCache{rasterizer}, stream_buffer(size, true) {} 22 : RasterizerCache{rasterizer}, stream_buffer(size, true) {}
18 23
19GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, 24GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
20 std::size_t alignment, bool cache) { 25 std::size_t alignment, bool cache) {
21 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 26 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
22 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
23 ASSERT_MSG(cpu_addr, "Invalid GPU address");
24 27
25 // Cache management is a big overhead, so only cache entries with a given size. 28 // Cache management is a big overhead, so only cache entries with a given size.
26 // TODO: Figure out which size is the best for given games. 29 // TODO: Figure out which size is the best for given games.
27 cache &= size >= 2048; 30 cache &= size >= 2048;
28 31
32 const auto& host_ptr{memory_manager.GetPointer(gpu_addr)};
29 if (cache) { 33 if (cache) {
30 auto entry = TryGet(*cpu_addr); 34 auto entry = TryGet(host_ptr);
31 if (entry) { 35 if (entry) {
32 if (entry->size >= size && entry->alignment == alignment) { 36 if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
33 return entry->offset; 37 return entry->GetOffset();
34 } 38 }
35 Unregister(entry); 39 Unregister(entry);
36 } 40 }
@@ -39,17 +43,17 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size
39 AlignBuffer(alignment); 43 AlignBuffer(alignment);
40 const GLintptr uploaded_offset = buffer_offset; 44 const GLintptr uploaded_offset = buffer_offset;
41 45
42 Memory::ReadBlock(*cpu_addr, buffer_ptr, size); 46 if (!host_ptr) {
47 return uploaded_offset;
48 }
43 49
50 std::memcpy(buffer_ptr, host_ptr, size);
44 buffer_ptr += size; 51 buffer_ptr += size;
45 buffer_offset += size; 52 buffer_offset += size;
46 53
47 if (cache) { 54 if (cache) {
48 auto entry = std::make_shared<CachedBufferEntry>(); 55 auto entry = std::make_shared<CachedBufferEntry>(
49 entry->offset = uploaded_offset; 56 *memory_manager.GpuToCpuAddress(gpu_addr), size, uploaded_offset, alignment, host_ptr);
50 entry->size = size;
51 entry->alignment = alignment;
52 entry->addr = *cpu_addr;
53 Register(entry); 57 Register(entry);
54 } 58 }
55 59
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index c11acfb79..1de1f84ae 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -17,22 +17,39 @@ namespace OpenGL {
17 17
18class RasterizerOpenGL; 18class RasterizerOpenGL;
19 19
20struct CachedBufferEntry final : public RasterizerCacheObject { 20class CachedBufferEntry final : public RasterizerCacheObject {
21 VAddr GetAddr() const override { 21public:
22 return addr; 22 explicit CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr offset,
23 std::size_t alignment, u8* host_ptr);
24
25 VAddr GetCpuAddr() const override {
26 return cpu_addr;
23 } 27 }
24 28
25 std::size_t GetSizeInBytes() const override { 29 std::size_t GetSizeInBytes() const override {
26 return size; 30 return size;
27 } 31 }
28 32
33 std::size_t GetSize() const {
34 return size;
35 }
36
37 GLintptr GetOffset() const {
38 return offset;
39 }
40
41 std::size_t GetAlignment() const {
42 return alignment;
43 }
44
29 // We do not have to flush this cache as things in it are never modified by us. 45 // We do not have to flush this cache as things in it are never modified by us.
30 void Flush() override {} 46 void Flush() override {}
31 47
32 VAddr addr; 48private:
33 std::size_t size; 49 VAddr cpu_addr{};
34 GLintptr offset; 50 std::size_t size{};
35 std::size_t alignment; 51 GLintptr offset{};
52 std::size_t alignment{};
36}; 53};
37 54
38class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { 55class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index c7f32feaa..c8dbcacbd 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -7,7 +7,6 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/memory.h"
11#include "video_core/renderer_opengl/gl_global_cache.h" 10#include "video_core/renderer_opengl/gl_global_cache.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h" 11#include "video_core/renderer_opengl/gl_rasterizer.h"
13#include "video_core/renderer_opengl/gl_shader_decompiler.h" 12#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -15,12 +14,13 @@
15 14
16namespace OpenGL { 15namespace OpenGL {
17 16
18CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{size} { 17CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr)
18 : cpu_addr{cpu_addr}, size{size}, RasterizerCacheObject{host_ptr} {
19 buffer.Create(); 19 buffer.Create();
20 // Bind and unbind the buffer so it gets allocated by the driver 20 // Bind and unbind the buffer so it gets allocated by the driver
21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
23 LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory"); 23 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory");
24} 24}
25 25
26void CachedGlobalRegion::Reload(u32 size_) { 26void CachedGlobalRegion::Reload(u32 size_) {
@@ -35,10 +35,10 @@ void CachedGlobalRegion::Reload(u32 size_) {
35 35
36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer 36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer
37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, Memory::GetPointer(addr), GL_DYNAMIC_DRAW); 38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW);
39} 39}
40 40
41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const { 41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const {
42 const auto search{reserve.find(addr)}; 42 const auto search{reserve.find(addr)};
43 if (search == reserve.end()) { 43 if (search == reserve.end()) {
44 return {}; 44 return {};
@@ -46,19 +46,22 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32
46 return search->second; 46 return search->second;
47} 47}
48 48
49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size) { 49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size,
50 GlobalRegion region{TryGetReservedGlobalRegion(addr, size)}; 50 u8* host_ptr) {
51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)};
51 if (!region) { 52 if (!region) {
52 // No reserved surface available, create a new one and reserve it 53 // No reserved surface available, create a new one and reserve it
53 region = std::make_shared<CachedGlobalRegion>(addr, size); 54 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
55 const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr);
56 region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr);
54 ReserveGlobalRegion(region); 57 ReserveGlobalRegion(region);
55 } 58 }
56 region->Reload(size); 59 region->Reload(size);
57 return region; 60 return region;
58} 61}
59 62
60void GlobalRegionCacheOpenGL::ReserveGlobalRegion(const GlobalRegion& region) { 63void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
61 reserve[region->GetAddr()] = region; 64 reserve.insert_or_assign(region->GetCacheAddr(), std::move(region));
62} 65}
63 66
64GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) 67GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
@@ -69,22 +72,20 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
69 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) { 72 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
70 73
71 auto& gpu{Core::System::GetInstance().GPU()}; 74 auto& gpu{Core::System::GetInstance().GPU()};
72 const auto cbufs = gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]; 75 auto& memory_manager{gpu.MemoryManager()};
73 const auto cbuf_addr = gpu.MemoryManager().GpuToCpuAddress( 76 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]};
74 cbufs.const_buffers[global_region.GetCbufIndex()].address + global_region.GetCbufOffset()); 77 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address +
75 ASSERT(cbuf_addr); 78 global_region.GetCbufOffset()};
76 79 const auto actual_addr{memory_manager.Read64(addr)};
77 const auto actual_addr_gpu = Memory::Read64(*cbuf_addr); 80 const auto size{memory_manager.Read32(addr + 8)};
78 const auto size = Memory::Read32(*cbuf_addr + 8);
79 const auto actual_addr = gpu.MemoryManager().GpuToCpuAddress(actual_addr_gpu);
80 ASSERT(actual_addr);
81 81
82 // Look up global region in the cache based on address 82 // Look up global region in the cache based on address
83 GlobalRegion region = TryGet(*actual_addr); 83 const auto& host_ptr{memory_manager.GetPointer(actual_addr)};
84 GlobalRegion region{TryGet(host_ptr)};
84 85
85 if (!region) { 86 if (!region) {
86 // No global region found - create a new one 87 // No global region found - create a new one
87 region = GetUncachedGlobalRegion(*actual_addr, size); 88 region = GetUncachedGlobalRegion(actual_addr, size, host_ptr);
88 Register(region); 89 Register(region);
89 } 90 }
90 91
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h
index 37830bb7c..a840491f7 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.h
+++ b/src/video_core/renderer_opengl/gl_global_cache.h
@@ -27,15 +27,13 @@ using GlobalRegion = std::shared_ptr<CachedGlobalRegion>;
27 27
28class CachedGlobalRegion final : public RasterizerCacheObject { 28class CachedGlobalRegion final : public RasterizerCacheObject {
29public: 29public:
30 explicit CachedGlobalRegion(VAddr addr, u32 size); 30 explicit CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr);
31 31
32 /// Gets the address of the shader in guest memory, required for cache management 32 VAddr GetCpuAddr() const override {
33 VAddr GetAddr() const { 33 return cpu_addr;
34 return addr;
35 } 34 }
36 35
37 /// Gets the size of the shader in guest memory, required for cache management 36 std::size_t GetSizeInBytes() const override {
38 std::size_t GetSizeInBytes() const {
39 return size; 37 return size;
40 } 38 }
41 39
@@ -53,9 +51,8 @@ public:
53 } 51 }
54 52
55private: 53private:
56 VAddr addr{}; 54 VAddr cpu_addr{};
57 u32 size{}; 55 u32 size{};
58
59 OGLBuffer buffer; 56 OGLBuffer buffer;
60}; 57};
61 58
@@ -68,11 +65,11 @@ public:
68 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage); 65 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage);
69 66
70private: 67private:
71 GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const; 68 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const;
72 GlobalRegion GetUncachedGlobalRegion(VAddr addr, u32 size); 69 GlobalRegion GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size, u8* host_ptr);
73 void ReserveGlobalRegion(const GlobalRegion& region); 70 void ReserveGlobalRegion(GlobalRegion region);
74 71
75 std::unordered_map<VAddr, GlobalRegion> reserve; 72 std::unordered_map<CacheAddr, GlobalRegion> reserve;
76}; 73};
77 74
78} // namespace OpenGL 75} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
index 77d5cedd2..75d816795 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
@@ -46,10 +46,7 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
46 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); 46 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
47 47
48 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 48 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
49 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; 49 const u8* source{memory_manager.GetPointer(gpu_addr)};
50 ASSERT_MSG(cpu_addr, "Invalid GPU address");
51
52 const u8* source{Memory::GetPointer(*cpu_addr)};
53 50
54 for (u32 primitive = 0; primitive < count / 4; ++primitive) { 51 for (u32 primitive = 0; primitive < count / 4; ++primitive) {
55 for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) { 52 for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) {
@@ -64,4 +61,4 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
64 return index_offset; 61 return index_offset;
65} 62}
66 63
67} // namespace OpenGL \ No newline at end of file 64} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 824863561..198c54872 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -102,8 +102,9 @@ struct FramebufferCacheKey {
102 102
103RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system, 103RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system,
104 ScreenInfo& info) 104 ScreenInfo& info)
105 : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, emu_window{window}, 105 : res_cache{*this}, shader_cache{*this, system}, global_cache{*this},
106 screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { 106 emu_window{window}, system{system}, screen_info{info},
107 buffer_cache(*this, STREAM_BUFFER_SIZE) {
107 // Create sampler objects 108 // Create sampler objects
108 for (std::size_t i = 0; i < texture_samplers.size(); ++i) { 109 for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
109 texture_samplers[i].Create(); 110 texture_samplers[i].Create();
@@ -138,7 +139,7 @@ void RasterizerOpenGL::CheckExtensions() {
138} 139}
139 140
140GLuint RasterizerOpenGL::SetupVertexFormat() { 141GLuint RasterizerOpenGL::SetupVertexFormat() {
141 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 142 auto& gpu = system.GPU().Maxwell3D();
142 const auto& regs = gpu.regs; 143 const auto& regs = gpu.regs;
143 144
144 if (!gpu.dirty_flags.vertex_attrib_format) { 145 if (!gpu.dirty_flags.vertex_attrib_format) {
@@ -207,7 +208,7 @@ GLuint RasterizerOpenGL::SetupVertexFormat() {
207} 208}
208 209
209void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) { 210void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
210 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 211 auto& gpu = system.GPU().Maxwell3D();
211 const auto& regs = gpu.regs; 212 const auto& regs = gpu.regs;
212 213
213 if (gpu.dirty_flags.vertex_array.none()) 214 if (gpu.dirty_flags.vertex_array.none())
@@ -248,7 +249,7 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
248} 249}
249 250
250DrawParameters RasterizerOpenGL::SetupDraw() { 251DrawParameters RasterizerOpenGL::SetupDraw() {
251 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 252 const auto& gpu = system.GPU().Maxwell3D();
252 const auto& regs = gpu.regs; 253 const auto& regs = gpu.regs;
253 const bool is_indexed = accelerate_draw == AccelDraw::Indexed; 254 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
254 255
@@ -297,7 +298,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
297 298
298void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { 299void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
299 MICROPROFILE_SCOPE(OpenGL_Shader); 300 MICROPROFILE_SCOPE(OpenGL_Shader);
300 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 301 auto& gpu = system.GPU().Maxwell3D();
301 302
302 BaseBindings base_bindings; 303 BaseBindings base_bindings;
303 std::array<bool, Maxwell::NumClipDistances> clip_distances{}; 304 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
@@ -413,7 +414,7 @@ void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey,
413} 414}
414 415
415std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 416std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
416 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 417 const auto& regs = system.GPU().Maxwell3D().regs;
417 418
418 std::size_t size = 0; 419 std::size_t size = 0;
419 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 420 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -431,7 +432,7 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
431} 432}
432 433
433std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const { 434std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
434 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 435 const auto& regs = system.GPU().Maxwell3D().regs;
435 436
436 return static_cast<std::size_t>(regs.index_array.count) * 437 return static_cast<std::size_t>(regs.index_array.count) *
437 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); 438 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
@@ -448,7 +449,7 @@ static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
448 return boost::make_iterator_range(map.equal_range(interval)); 449 return boost::make_iterator_range(map.equal_range(interval));
449} 450}
450 451
451void RasterizerOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) { 452void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
452 const u64 page_start{addr >> Memory::PAGE_BITS}; 453 const u64 page_start{addr >> Memory::PAGE_BITS};
453 const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS}; 454 const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS};
454 455
@@ -487,7 +488,7 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
487 OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents, 488 OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents,
488 std::optional<std::size_t> single_color_target) { 489 std::optional<std::size_t> single_color_target) {
489 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 490 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
490 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 491 auto& gpu = system.GPU().Maxwell3D();
491 const auto& regs = gpu.regs; 492 const auto& regs = gpu.regs;
492 493
493 const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents, 494 const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
@@ -581,7 +582,7 @@ void RasterizerOpenGL::Clear() {
581 const auto prev_state{state}; 582 const auto prev_state{state};
582 SCOPE_EXIT({ prev_state.Apply(); }); 583 SCOPE_EXIT({ prev_state.Apply(); });
583 584
584 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 585 const auto& regs = system.GPU().Maxwell3D().regs;
585 bool use_color{}; 586 bool use_color{};
586 bool use_depth{}; 587 bool use_depth{};
587 bool use_stencil{}; 588 bool use_stencil{};
@@ -672,7 +673,7 @@ void RasterizerOpenGL::DrawArrays() {
672 return; 673 return;
673 674
674 MICROPROFILE_SCOPE(OpenGL_Drawing); 675 MICROPROFILE_SCOPE(OpenGL_Drawing);
675 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 676 auto& gpu = system.GPU().Maxwell3D();
676 const auto& regs = gpu.regs; 677 const auto& regs = gpu.regs;
677 678
678 ConfigureFramebuffers(state); 679 ConfigureFramebuffers(state);
@@ -746,20 +747,26 @@ void RasterizerOpenGL::DrawArrays() {
746 747
747void RasterizerOpenGL::FlushAll() {} 748void RasterizerOpenGL::FlushAll() {}
748 749
749void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { 750void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) {
750 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 751 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
752 if (!addr || !size) {
753 return;
754 }
751 res_cache.FlushRegion(addr, size); 755 res_cache.FlushRegion(addr, size);
752} 756}
753 757
754void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 758void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
755 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 759 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
760 if (!addr || !size) {
761 return;
762 }
756 res_cache.InvalidateRegion(addr, size); 763 res_cache.InvalidateRegion(addr, size);
757 shader_cache.InvalidateRegion(addr, size); 764 shader_cache.InvalidateRegion(addr, size);
758 global_cache.InvalidateRegion(addr, size); 765 global_cache.InvalidateRegion(addr, size);
759 buffer_cache.InvalidateRegion(addr, size); 766 buffer_cache.InvalidateRegion(addr, size);
760} 767}
761 768
762void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 769void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
763 FlushRegion(addr, size); 770 FlushRegion(addr, size);
764 InvalidateRegion(addr, size); 771 InvalidateRegion(addr, size);
765} 772}
@@ -781,7 +788,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
781 788
782 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 789 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
783 790
784 const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)}; 791 const auto& surface{res_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))};
785 if (!surface) { 792 if (!surface) {
786 return {}; 793 return {};
787 } 794 }
@@ -892,7 +899,7 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
892 const Shader& shader, GLuint program_handle, 899 const Shader& shader, GLuint program_handle,
893 BaseBindings base_bindings) { 900 BaseBindings base_bindings) {
894 MICROPROFILE_SCOPE(OpenGL_UBO); 901 MICROPROFILE_SCOPE(OpenGL_UBO);
895 const auto& gpu = Core::System::GetInstance().GPU(); 902 const auto& gpu = system.GPU();
896 const auto& maxwell3d = gpu.Maxwell3D(); 903 const auto& maxwell3d = gpu.Maxwell3D();
897 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; 904 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
898 const auto& entries = shader->GetShaderEntries().const_buffers; 905 const auto& entries = shader->GetShaderEntries().const_buffers;
@@ -971,7 +978,7 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade
971void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader, 978void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
972 GLuint program_handle, BaseBindings base_bindings) { 979 GLuint program_handle, BaseBindings base_bindings) {
973 MICROPROFILE_SCOPE(OpenGL_Texture); 980 MICROPROFILE_SCOPE(OpenGL_Texture);
974 const auto& gpu = Core::System::GetInstance().GPU(); 981 const auto& gpu = system.GPU();
975 const auto& maxwell3d = gpu.Maxwell3D(); 982 const auto& maxwell3d = gpu.Maxwell3D();
976 const auto& entries = shader->GetShaderEntries().samplers; 983 const auto& entries = shader->GetShaderEntries().samplers;
977 984
@@ -998,7 +1005,7 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s
998} 1005}
999 1006
1000void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { 1007void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
1001 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1008 const auto& regs = system.GPU().Maxwell3D().regs;
1002 const bool geometry_shaders_enabled = 1009 const bool geometry_shaders_enabled =
1003 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); 1010 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
1004 const std::size_t viewport_count = 1011 const std::size_t viewport_count =
@@ -1021,7 +1028,7 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
1021void RasterizerOpenGL::SyncClipEnabled( 1028void RasterizerOpenGL::SyncClipEnabled(
1022 const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) { 1029 const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) {
1023 1030
1024 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1031 const auto& regs = system.GPU().Maxwell3D().regs;
1025 const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{ 1032 const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{
1026 regs.clip_distance_enabled.c0 != 0, regs.clip_distance_enabled.c1 != 0, 1033 regs.clip_distance_enabled.c0 != 0, regs.clip_distance_enabled.c1 != 0,
1027 regs.clip_distance_enabled.c2 != 0, regs.clip_distance_enabled.c3 != 0, 1034 regs.clip_distance_enabled.c2 != 0, regs.clip_distance_enabled.c3 != 0,
@@ -1038,7 +1045,7 @@ void RasterizerOpenGL::SyncClipCoef() {
1038} 1045}
1039 1046
1040void RasterizerOpenGL::SyncCullMode() { 1047void RasterizerOpenGL::SyncCullMode() {
1041 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1048 const auto& regs = system.GPU().Maxwell3D().regs;
1042 1049
1043 state.cull.enabled = regs.cull.enabled != 0; 1050 state.cull.enabled = regs.cull.enabled != 0;
1044 1051
@@ -1062,14 +1069,14 @@ void RasterizerOpenGL::SyncCullMode() {
1062} 1069}
1063 1070
1064void RasterizerOpenGL::SyncPrimitiveRestart() { 1071void RasterizerOpenGL::SyncPrimitiveRestart() {
1065 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1072 const auto& regs = system.GPU().Maxwell3D().regs;
1066 1073
1067 state.primitive_restart.enabled = regs.primitive_restart.enabled; 1074 state.primitive_restart.enabled = regs.primitive_restart.enabled;
1068 state.primitive_restart.index = regs.primitive_restart.index; 1075 state.primitive_restart.index = regs.primitive_restart.index;
1069} 1076}
1070 1077
1071void RasterizerOpenGL::SyncDepthTestState() { 1078void RasterizerOpenGL::SyncDepthTestState() {
1072 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1079 const auto& regs = system.GPU().Maxwell3D().regs;
1073 1080
1074 state.depth.test_enabled = regs.depth_test_enable != 0; 1081 state.depth.test_enabled = regs.depth_test_enable != 0;
1075 state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE; 1082 state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
@@ -1081,7 +1088,7 @@ void RasterizerOpenGL::SyncDepthTestState() {
1081} 1088}
1082 1089
1083void RasterizerOpenGL::SyncStencilTestState() { 1090void RasterizerOpenGL::SyncStencilTestState() {
1084 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1091 const auto& regs = system.GPU().Maxwell3D().regs;
1085 state.stencil.test_enabled = regs.stencil_enable != 0; 1092 state.stencil.test_enabled = regs.stencil_enable != 0;
1086 1093
1087 if (!regs.stencil_enable) { 1094 if (!regs.stencil_enable) {
@@ -1115,7 +1122,7 @@ void RasterizerOpenGL::SyncStencilTestState() {
1115} 1122}
1116 1123
1117void RasterizerOpenGL::SyncColorMask() { 1124void RasterizerOpenGL::SyncColorMask() {
1118 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1125 const auto& regs = system.GPU().Maxwell3D().regs;
1119 const std::size_t count = 1126 const std::size_t count =
1120 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1; 1127 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
1121 for (std::size_t i = 0; i < count; i++) { 1128 for (std::size_t i = 0; i < count; i++) {
@@ -1129,18 +1136,18 @@ void RasterizerOpenGL::SyncColorMask() {
1129} 1136}
1130 1137
1131void RasterizerOpenGL::SyncMultiSampleState() { 1138void RasterizerOpenGL::SyncMultiSampleState() {
1132 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1139 const auto& regs = system.GPU().Maxwell3D().regs;
1133 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0; 1140 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
1134 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0; 1141 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
1135} 1142}
1136 1143
1137void RasterizerOpenGL::SyncFragmentColorClampState() { 1144void RasterizerOpenGL::SyncFragmentColorClampState() {
1138 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1145 const auto& regs = system.GPU().Maxwell3D().regs;
1139 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0; 1146 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
1140} 1147}
1141 1148
1142void RasterizerOpenGL::SyncBlendState() { 1149void RasterizerOpenGL::SyncBlendState() {
1143 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1150 const auto& regs = system.GPU().Maxwell3D().regs;
1144 1151
1145 state.blend_color.red = regs.blend_color.r; 1152 state.blend_color.red = regs.blend_color.r;
1146 state.blend_color.green = regs.blend_color.g; 1153 state.blend_color.green = regs.blend_color.g;
@@ -1182,7 +1189,7 @@ void RasterizerOpenGL::SyncBlendState() {
1182} 1189}
1183 1190
1184void RasterizerOpenGL::SyncLogicOpState() { 1191void RasterizerOpenGL::SyncLogicOpState() {
1185 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1192 const auto& regs = system.GPU().Maxwell3D().regs;
1186 1193
1187 state.logic_op.enabled = regs.logic_op.enable != 0; 1194 state.logic_op.enabled = regs.logic_op.enable != 0;
1188 1195
@@ -1196,7 +1203,7 @@ void RasterizerOpenGL::SyncLogicOpState() {
1196} 1203}
1197 1204
1198void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) { 1205void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
1199 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1206 const auto& regs = system.GPU().Maxwell3D().regs;
1200 const bool geometry_shaders_enabled = 1207 const bool geometry_shaders_enabled =
1201 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); 1208 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
1202 const std::size_t viewport_count = 1209 const std::size_t viewport_count =
@@ -1218,17 +1225,17 @@ void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
1218} 1225}
1219 1226
1220void RasterizerOpenGL::SyncTransformFeedback() { 1227void RasterizerOpenGL::SyncTransformFeedback() {
1221 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1228 const auto& regs = system.GPU().Maxwell3D().regs;
1222 UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented"); 1229 UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented");
1223} 1230}
1224 1231
1225void RasterizerOpenGL::SyncPointState() { 1232void RasterizerOpenGL::SyncPointState() {
1226 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1233 const auto& regs = system.GPU().Maxwell3D().regs;
1227 state.point.size = regs.point_size; 1234 state.point.size = regs.point_size;
1228} 1235}
1229 1236
1230void RasterizerOpenGL::SyncPolygonOffset() { 1237void RasterizerOpenGL::SyncPolygonOffset() {
1231 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1238 const auto& regs = system.GPU().Maxwell3D().regs;
1232 state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0; 1239 state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
1233 state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0; 1240 state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0;
1234 state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0; 1241 state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0;
@@ -1238,7 +1245,7 @@ void RasterizerOpenGL::SyncPolygonOffset() {
1238} 1245}
1239 1246
1240void RasterizerOpenGL::CheckAlphaTests() { 1247void RasterizerOpenGL::CheckAlphaTests() {
1241 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1248 const auto& regs = system.GPU().Maxwell3D().regs;
1242 UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1, 1249 UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1,
1243 "Alpha Testing is enabled with more than one rendertarget"); 1250 "Alpha Testing is enabled with more than one rendertarget");
1244} 1251}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 7e63f8008..30f3e8acb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -57,9 +57,9 @@ public:
57 void DrawArrays() override; 57 void DrawArrays() override;
58 void Clear() override; 58 void Clear() override;
59 void FlushAll() override; 59 void FlushAll() override;
60 void FlushRegion(VAddr addr, u64 size) override; 60 void FlushRegion(CacheAddr addr, u64 size) override;
61 void InvalidateRegion(VAddr addr, u64 size) override; 61 void InvalidateRegion(CacheAddr addr, u64 size) override;
62 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 62 void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
63 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 63 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
64 const Tegra::Engines::Fermi2D::Regs::Surface& dst, 64 const Tegra::Engines::Fermi2D::Regs::Surface& dst,
65 const Common::Rectangle<u32>& src_rect, 65 const Common::Rectangle<u32>& src_rect,
@@ -67,7 +67,7 @@ public:
67 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 67 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
68 u32 pixel_stride) override; 68 u32 pixel_stride) override;
69 bool AccelerateDrawBatch(bool is_indexed) override; 69 bool AccelerateDrawBatch(bool is_indexed) override;
70 void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override; 70 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
71 void LoadDiskResources(const std::atomic_bool& stop_loading, 71 void LoadDiskResources(const std::atomic_bool& stop_loading,
72 const VideoCore::DiskResourceLoadCallback& callback) override; 72 const VideoCore::DiskResourceLoadCallback& callback) override;
73 73
@@ -215,6 +215,7 @@ private:
215 GlobalRegionCacheOpenGL global_cache; 215 GlobalRegionCacheOpenGL global_cache;
216 216
217 Core::Frontend::EmuWindow& emu_window; 217 Core::Frontend::EmuWindow& emu_window;
218 Core::System& system;
218 219
219 ScreenInfo& screen_info; 220 ScreenInfo& screen_info;
220 221
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index e9eb6e921..57329cd61 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -57,10 +57,9 @@ static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) {
57 57
58void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { 58void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
59 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 59 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
60 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
61 60
62 addr = cpu_addr ? *cpu_addr : 0;
63 gpu_addr = gpu_addr_; 61 gpu_addr = gpu_addr_;
62 host_ptr = memory_manager.GetPointer(gpu_addr_);
64 size_in_bytes = SizeInBytesRaw(); 63 size_in_bytes = SizeInBytesRaw();
65 64
66 if (IsPixelFormatASTC(pixel_format)) { 65 if (IsPixelFormatASTC(pixel_format)) {
@@ -446,7 +445,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
446 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 445 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
447 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 446 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
448 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing, 447 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing,
449 gl_buffer.data() + offset_gl, gl_size, params.addr + offset); 448 gl_buffer.data() + offset_gl, params.host_ptr + offset);
450 offset += layer_size; 449 offset += layer_size;
451 offset_gl += gl_size; 450 offset_gl += gl_size;
452 } 451 }
@@ -455,7 +454,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
455 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 454 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
456 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 455 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
457 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing, 456 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing,
458 gl_buffer.data(), gl_buffer.size(), params.addr + offset); 457 gl_buffer.data(), params.host_ptr + offset);
459 } 458 }
460} 459}
461 460
@@ -513,9 +512,9 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
513 "reinterpretation but the texture is tiled."); 512 "reinterpretation but the texture is tiled.");
514 } 513 }
515 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; 514 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
516 515 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
517 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, 516 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
518 Memory::GetPointer(dst_params.addr + src_params.size_in_bytes)); 517 memory_manager.GetPointer(dst_params.gpu_addr + src_params.size_in_bytes));
519 } 518 }
520 519
521 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 520 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -563,8 +562,8 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
563} 562}
564 563
565CachedSurface::CachedSurface(const SurfaceParams& params) 564CachedSurface::CachedSurface(const SurfaceParams& params)
566 : params(params), gl_target(SurfaceTargetToGL(params.target)), 565 : params{params}, gl_target{SurfaceTargetToGL(params.target)},
567 cached_size_in_bytes(params.size_in_bytes) { 566 cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} {
568 texture.Create(gl_target); 567 texture.Create(gl_target);
569 568
570 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0) 569 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0)
@@ -603,7 +602,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
603 602
604 ApplyTextureDefaults(texture.handle, params.max_mip_level); 603 ApplyTextureDefaults(texture.handle, params.max_mip_level);
605 604
606 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString()); 605 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString());
607 606
608 // Clamp size to mapped GPU memory region 607 // Clamp size to mapped GPU memory region
609 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000 608 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
@@ -616,6 +615,8 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
616 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size); 615 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
617 cached_size_in_bytes = max_size; 616 cached_size_in_bytes = max_size;
618 } 617 }
618
619 cpu_addr = *memory_manager.GpuToCpuAddress(params.gpu_addr);
619} 620}
620 621
621MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); 622MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
@@ -633,10 +634,9 @@ void CachedSurface::LoadGLBuffer() {
633 const u32 bpp = params.GetFormatBpp() / 8; 634 const u32 bpp = params.GetFormatBpp() / 8;
634 const u32 copy_size = params.width * bpp; 635 const u32 copy_size = params.width * bpp;
635 if (params.pitch == copy_size) { 636 if (params.pitch == copy_size) {
636 std::memcpy(gl_buffer[0].data(), Memory::GetPointer(params.addr), 637 std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl);
637 params.size_in_bytes_gl);
638 } else { 638 } else {
639 const u8* start = Memory::GetPointer(params.addr); 639 const u8* start{params.host_ptr};
640 u8* write_to = gl_buffer[0].data(); 640 u8* write_to = gl_buffer[0].data();
641 for (u32 h = params.height; h > 0; h--) { 641 for (u32 h = params.height; h > 0; h--) {
642 std::memcpy(write_to, start, copy_size); 642 std::memcpy(write_to, start, copy_size);
@@ -680,8 +680,6 @@ void CachedSurface::FlushGLBuffer() {
680 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 680 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
681 Tegra::Texture::ConvertFromHostToGuest(gl_buffer[0].data(), params.pixel_format, params.width, 681 Tegra::Texture::ConvertFromHostToGuest(gl_buffer[0].data(), params.pixel_format, params.width,
682 params.height, params.depth, true, true); 682 params.height, params.depth, true, true);
683 const u8* const texture_src_data = Memory::GetPointer(params.addr);
684 ASSERT(texture_src_data);
685 if (params.is_tiled) { 683 if (params.is_tiled) {
686 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 684 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
687 params.block_width, static_cast<u32>(params.target)); 685 params.block_width, static_cast<u32>(params.target));
@@ -691,9 +689,9 @@ void CachedSurface::FlushGLBuffer() {
691 const u32 bpp = params.GetFormatBpp() / 8; 689 const u32 bpp = params.GetFormatBpp() / 8;
692 const u32 copy_size = params.width * bpp; 690 const u32 copy_size = params.width * bpp;
693 if (params.pitch == copy_size) { 691 if (params.pitch == copy_size) {
694 std::memcpy(Memory::GetPointer(params.addr), gl_buffer[0].data(), GetSizeInBytes()); 692 std::memcpy(params.host_ptr, gl_buffer[0].data(), GetSizeInBytes());
695 } else { 693 } else {
696 u8* start = Memory::GetPointer(params.addr); 694 u8* start{params.host_ptr};
697 const u8* read_to = gl_buffer[0].data(); 695 const u8* read_to = gl_buffer[0].data();
698 for (u32 h = params.height; h > 0; h--) { 696 for (u32 h = params.height; h > 0; h--) {
699 std::memcpy(start, read_to, copy_size); 697 std::memcpy(start, read_to, copy_size);
@@ -927,12 +925,12 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
927} 925}
928 926
929Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 927Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
930 if (params.addr == 0 || params.height * params.width == 0) { 928 if (params.gpu_addr == 0 || params.height * params.width == 0) {
931 return {}; 929 return {};
932 } 930 }
933 931
934 // Look up surface in the cache based on address 932 // Look up surface in the cache based on address
935 Surface surface{TryGet(params.addr)}; 933 Surface surface{TryGet(params.host_ptr)};
936 if (surface) { 934 if (surface) {
937 if (surface->GetSurfaceParams().IsCompatibleSurface(params)) { 935 if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
938 // Use the cached surface as-is unless it's not synced with memory 936 // Use the cached surface as-is unless it's not synced with memory
@@ -981,14 +979,16 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
981 const Surface& dst_surface) { 979 const Surface& dst_surface) {
982 const auto& init_params{src_surface->GetSurfaceParams()}; 980 const auto& init_params{src_surface->GetSurfaceParams()};
983 const auto& dst_params{dst_surface->GetSurfaceParams()}; 981 const auto& dst_params{dst_surface->GetSurfaceParams()};
984 VAddr address = init_params.addr; 982 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
985 const std::size_t layer_size = dst_params.LayerMemorySize(); 983 Tegra::GPUVAddr address{init_params.gpu_addr};
984 const std::size_t layer_size{dst_params.LayerMemorySize()};
986 for (u32 layer = 0; layer < dst_params.depth; layer++) { 985 for (u32 layer = 0; layer < dst_params.depth; layer++) {
987 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { 986 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
988 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap); 987 const Tegra::GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)};
989 const Surface& copy = TryGet(sub_address); 988 const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))};
990 if (!copy) 989 if (!copy) {
991 continue; 990 continue;
991 }
992 const auto& src_params{copy->GetSurfaceParams()}; 992 const auto& src_params{copy->GetSurfaceParams()};
993 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))}; 993 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
994 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))}; 994 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
@@ -1163,7 +1163,8 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1163 const auto& dst_params{dst_surface->GetSurfaceParams()}; 1163 const auto& dst_params{dst_surface->GetSurfaceParams()};
1164 1164
1165 // Flush enough memory for both the source and destination surface 1165 // Flush enough memory for both the source and destination surface
1166 FlushRegion(src_params.addr, std::max(src_params.MemorySize(), dst_params.MemorySize())); 1166 FlushRegion(ToCacheAddr(src_params.host_ptr),
1167 std::max(src_params.MemorySize(), dst_params.MemorySize()));
1167 1168
1168 LoadSurface(dst_surface); 1169 LoadSurface(dst_surface);
1169} 1170}
@@ -1215,8 +1216,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1215 return new_surface; 1216 return new_surface;
1216} 1217}
1217 1218
1218Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { 1219Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(const u8* host_ptr) const {
1219 return TryGet(addr); 1220 return TryGet(host_ptr);
1220} 1221}
1221 1222
1222void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) { 1223void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) {
@@ -1243,9 +1244,10 @@ static std::optional<u32> TryFindBestMipMap(std::size_t memory, const SurfacePar
1243 return {}; 1244 return {};
1244} 1245}
1245 1246
1246static std::optional<u32> TryFindBestLayer(VAddr addr, const SurfaceParams params, u32 mipmap) { 1247static std::optional<u32> TryFindBestLayer(Tegra::GPUVAddr addr, const SurfaceParams params,
1247 const std::size_t size = params.LayerMemorySize(); 1248 u32 mipmap) {
1248 VAddr start = params.addr + params.GetMipmapLevelOffset(mipmap); 1249 const std::size_t size{params.LayerMemorySize()};
1250 Tegra::GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)};
1249 for (u32 i = 0; i < params.depth; i++) { 1251 for (u32 i = 0; i < params.depth; i++) {
1250 if (start == addr) { 1252 if (start == addr) {
1251 return {i}; 1253 return {i};
@@ -1267,7 +1269,7 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1267 src_params.height == dst_params.MipHeight(*level) && 1269 src_params.height == dst_params.MipHeight(*level) &&
1268 src_params.block_height >= dst_params.MipBlockHeight(*level)) { 1270 src_params.block_height >= dst_params.MipBlockHeight(*level)) {
1269 const std::optional<u32> slot = 1271 const std::optional<u32> slot =
1270 TryFindBestLayer(render_surface->GetAddr(), dst_params, *level); 1272 TryFindBestLayer(render_surface->GetSurfaceParams().gpu_addr, dst_params, *level);
1271 if (slot.has_value()) { 1273 if (slot.has_value()) {
1272 glCopyImageSubData(render_surface->Texture().handle, 1274 glCopyImageSubData(render_surface->Texture().handle,
1273 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0, 1275 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0,
@@ -1283,8 +1285,8 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1283} 1285}
1284 1286
1285static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) { 1287static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) {
1286 const VAddr bound1 = blitted_surface->GetAddr() + blitted_surface->GetMemorySize(); 1288 const VAddr bound1 = blitted_surface->GetCpuAddr() + blitted_surface->GetMemorySize();
1287 const VAddr bound2 = render_surface->GetAddr() + render_surface->GetMemorySize(); 1289 const VAddr bound2 = render_surface->GetCpuAddr() + render_surface->GetMemorySize();
1288 if (bound2 > bound1) 1290 if (bound2 > bound1)
1289 return true; 1291 return true;
1290 const auto& dst_params = blitted_surface->GetSurfaceParams(); 1292 const auto& dst_params = blitted_surface->GetSurfaceParams();
@@ -1327,7 +1329,8 @@ void RasterizerCacheOpenGL::SignalPreDrawCall() {
1327void RasterizerCacheOpenGL::SignalPostDrawCall() { 1329void RasterizerCacheOpenGL::SignalPostDrawCall() {
1328 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) { 1330 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) {
1329 if (current_color_buffers[i] != nullptr) { 1331 if (current_color_buffers[i] != nullptr) {
1330 Surface intersect = CollideOnReinterpretedSurface(current_color_buffers[i]->GetAddr()); 1332 Surface intersect =
1333 CollideOnReinterpretedSurface(current_color_buffers[i]->GetCacheAddr());
1331 if (intersect != nullptr) { 1334 if (intersect != nullptr) {
1332 PartialReinterpretSurface(current_color_buffers[i], intersect); 1335 PartialReinterpretSurface(current_color_buffers[i], intersect);
1333 texception = true; 1336 texception = true;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 9cf6f50be..9366f47f2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -296,7 +296,7 @@ struct SurfaceParams {
296 bool is_array; 296 bool is_array;
297 bool srgb_conversion; 297 bool srgb_conversion;
298 // Parameters used for caching 298 // Parameters used for caching
299 VAddr addr; 299 u8* host_ptr;
300 Tegra::GPUVAddr gpu_addr; 300 Tegra::GPUVAddr gpu_addr;
301 std::size_t size_in_bytes; 301 std::size_t size_in_bytes;
302 std::size_t size_in_bytes_gl; 302 std::size_t size_in_bytes_gl;
@@ -345,10 +345,10 @@ class RasterizerOpenGL;
345 345
346class CachedSurface final : public RasterizerCacheObject { 346class CachedSurface final : public RasterizerCacheObject {
347public: 347public:
348 CachedSurface(const SurfaceParams& params); 348 explicit CachedSurface(const SurfaceParams& params);
349 349
350 VAddr GetAddr() const override { 350 VAddr GetCpuAddr() const override {
351 return params.addr; 351 return cpu_addr;
352 } 352 }
353 353
354 std::size_t GetSizeInBytes() const override { 354 std::size_t GetSizeInBytes() const override {
@@ -432,6 +432,7 @@ private:
432 std::size_t memory_size; 432 std::size_t memory_size;
433 bool reinterpreted = false; 433 bool reinterpreted = false;
434 bool must_reload = false; 434 bool must_reload = false;
435 VAddr cpu_addr{};
435}; 436};
436 437
437class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 438class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -449,7 +450,7 @@ public:
449 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 450 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
450 451
451 /// Tries to find a framebuffer using on the provided CPU address 452 /// Tries to find a framebuffer using on the provided CPU address
452 Surface TryFindFramebufferSurface(VAddr addr) const; 453 Surface TryFindFramebufferSurface(const u8* host_ptr) const;
453 454
454 /// Copies the contents of one surface to another 455 /// Copies the contents of one surface to another
455 void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config, 456 void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
@@ -506,12 +507,12 @@ private:
506 std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers; 507 std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers;
507 Surface last_depth_buffer; 508 Surface last_depth_buffer;
508 509
509 using SurfaceIntervalCache = boost::icl::interval_map<VAddr, Surface>; 510 using SurfaceIntervalCache = boost::icl::interval_map<CacheAddr, Surface>;
510 using SurfaceInterval = typename SurfaceIntervalCache::interval_type; 511 using SurfaceInterval = typename SurfaceIntervalCache::interval_type;
511 512
512 static auto GetReinterpretInterval(const Surface& object) { 513 static auto GetReinterpretInterval(const Surface& object) {
513 return SurfaceInterval::right_open(object->GetAddr() + 1, 514 return SurfaceInterval::right_open(object->GetCacheAddr() + 1,
514 object->GetAddr() + object->GetMemorySize() - 1); 515 object->GetCacheAddr() + object->GetMemorySize() - 1);
515 } 516 }
516 517
517 // Reinterpreted surfaces are very fragil as the game may keep rendering into them. 518 // Reinterpreted surfaces are very fragil as the game may keep rendering into them.
@@ -523,7 +524,7 @@ private:
523 reinterpret_surface->MarkReinterpreted(); 524 reinterpret_surface->MarkReinterpreted();
524 } 525 }
525 526
526 Surface CollideOnReinterpretedSurface(VAddr addr) const { 527 Surface CollideOnReinterpretedSurface(CacheAddr addr) const {
527 const SurfaceInterval interval{addr}; 528 const SurfaceInterval interval{addr};
528 for (auto& pair : 529 for (auto& pair :
529 boost::make_iterator_range(reinterpreted_surfaces.equal_range(interval))) { 530 boost::make_iterator_range(reinterpreted_surfaces.equal_range(interval))) {
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 4883e4f62..1ed740877 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -32,19 +32,16 @@ struct UnspecializedShader {
32namespace { 32namespace {
33 33
34/// Gets the address for the specified shader stage program 34/// Gets the address for the specified shader stage program
35VAddr GetShaderAddress(Maxwell::ShaderProgram program) { 35Tegra::GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
36 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 36 const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
37 const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)]; 37 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
38 const auto address = gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + 38 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
39 shader_config.offset);
40 ASSERT_MSG(address, "Invalid GPU address");
41 return *address;
42} 39}
43 40
44/// Gets the shader program code from memory for the specified address 41/// Gets the shader program code from memory for the specified address
45ProgramCode GetShaderCode(VAddr addr) { 42ProgramCode GetShaderCode(const u8* host_ptr) {
46 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); 43 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
47 Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64)); 44 std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64));
48 return program_code; 45 return program_code;
49} 46}
50 47
@@ -214,12 +211,13 @@ std::set<GLenum> GetSupportedFormats() {
214 211
215} // namespace 212} // namespace
216 213
217CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 214CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
218 ShaderDiskCacheOpenGL& disk_cache, 215 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
219 const PrecompiledPrograms& precompiled_programs, 216 const PrecompiledPrograms& precompiled_programs,
220 ProgramCode&& program_code, ProgramCode&& program_code_b) 217 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
221 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 218 : host_ptr{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
222 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 219 program_type{program_type}, disk_cache{disk_cache},
220 precompiled_programs{precompiled_programs}, RasterizerCacheObject{host_ptr} {
223 221
224 const std::size_t code_size = CalculateProgramSize(program_code); 222 const std::size_t code_size = CalculateProgramSize(program_code);
225 const std::size_t code_size_b = 223 const std::size_t code_size_b =
@@ -243,12 +241,13 @@ CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderPro
243 disk_cache.SaveRaw(raw); 241 disk_cache.SaveRaw(raw);
244} 242}
245 243
246CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 244CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
247 ShaderDiskCacheOpenGL& disk_cache, 245 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
248 const PrecompiledPrograms& precompiled_programs, 246 const PrecompiledPrograms& precompiled_programs,
249 GLShader::ProgramResult result) 247 GLShader::ProgramResult result, u8* host_ptr)
250 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 248 : cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, program_type{program_type},
251 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 249 disk_cache{disk_cache}, precompiled_programs{precompiled_programs}, RasterizerCacheObject{
250 host_ptr} {
252 251
253 code = std::move(result.first); 252 code = std::move(result.first);
254 entries = result.second; 253 entries = result.second;
@@ -271,7 +270,7 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive
271 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 270 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
272 } 271 }
273 272
274 LabelGLObject(GL_PROGRAM, program->handle, addr); 273 LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
275 } 274 }
276 275
277 handle = program->handle; 276 handle = program->handle;
@@ -323,7 +322,7 @@ GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBind
323 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 322 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
324 } 323 }
325 324
326 LabelGLObject(GL_PROGRAM, target_program->handle, addr, debug_name); 325 LabelGLObject(GL_PROGRAM, target_program->handle, cpu_addr, debug_name);
327 326
328 return target_program->handle; 327 return target_program->handle;
329}; 328};
@@ -486,29 +485,32 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
486 return last_shaders[static_cast<u32>(program)]; 485 return last_shaders[static_cast<u32>(program)];
487 } 486 }
488 487
489 const VAddr program_addr{GetShaderAddress(program)}; 488 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
489 const Tegra::GPUVAddr program_addr{GetShaderAddress(program)};
490 490
491 // Look up shader in the cache based on address 491 // Look up shader in the cache based on address
492 Shader shader{TryGet(program_addr)}; 492 const auto& host_ptr{memory_manager.GetPointer(program_addr)};
493 Shader shader{TryGet(host_ptr)};
493 494
494 if (!shader) { 495 if (!shader) {
495 // No shader found - create a new one 496 // No shader found - create a new one
496 ProgramCode program_code = GetShaderCode(program_addr); 497 ProgramCode program_code{GetShaderCode(host_ptr)};
497 ProgramCode program_code_b; 498 ProgramCode program_code_b;
498 if (program == Maxwell::ShaderProgram::VertexA) { 499 if (program == Maxwell::ShaderProgram::VertexA) {
499 program_code_b = GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB)); 500 program_code_b = GetShaderCode(
501 memory_manager.GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB)));
500 } 502 }
501 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); 503 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
502 504 const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
503 const auto found = precompiled_shaders.find(unique_identifier); 505 const auto found = precompiled_shaders.find(unique_identifier);
504 if (found != precompiled_shaders.end()) { 506 if (found != precompiled_shaders.end()) {
505 shader = 507 shader =
506 std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache, 508 std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
507 precompiled_programs, found->second); 509 precompiled_programs, found->second, host_ptr);
508 } else { 510 } else {
509 shader = std::make_shared<CachedShader>( 511 shader = std::make_shared<CachedShader>(
510 program_addr, unique_identifier, program, disk_cache, precompiled_programs, 512 cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
511 std::move(program_code), std::move(program_code_b)); 513 std::move(program_code), std::move(program_code_b), host_ptr);
512 } 514 }
513 Register(shader); 515 Register(shader);
514 } 516 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 97eed192f..fd1c85115 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -39,18 +39,18 @@ using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>;
39 39
40class CachedShader final : public RasterizerCacheObject { 40class CachedShader final : public RasterizerCacheObject {
41public: 41public:
42 explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 42 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
43 ShaderDiskCacheOpenGL& disk_cache, 43 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
44 const PrecompiledPrograms& precompiled_programs, 44 const PrecompiledPrograms& precompiled_programs,
45 ProgramCode&& program_code, ProgramCode&& program_code_b); 45 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr);
46 46
47 explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 47 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
48 ShaderDiskCacheOpenGL& disk_cache, 48 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
49 const PrecompiledPrograms& precompiled_programs, 49 const PrecompiledPrograms& precompiled_programs,
50 GLShader::ProgramResult result); 50 GLShader::ProgramResult result, u8* host_ptr);
51 51
52 VAddr GetAddr() const override { 52 VAddr GetCpuAddr() const override {
53 return addr; 53 return cpu_addr;
54 } 54 }
55 55
56 std::size_t GetSizeInBytes() const override { 56 std::size_t GetSizeInBytes() const override {
@@ -91,7 +91,8 @@ private:
91 91
92 ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const; 92 ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const;
93 93
94 VAddr addr{}; 94 u8* host_ptr{};
95 VAddr cpu_addr{};
95 u64 unique_identifier{}; 96 u64 unique_identifier{};
96 Maxwell::ShaderProgram program_type{}; 97 Maxwell::ShaderProgram program_type{};
97 ShaderDiskCacheOpenGL& disk_cache; 98 ShaderDiskCacheOpenGL& disk_cache;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 8b510b6ae..5e3d862c6 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -164,12 +164,13 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
164 // Reset the screen info's display texture to its own permanent texture 164 // Reset the screen info's display texture to its own permanent texture
165 screen_info.display_texture = screen_info.texture.resource.handle; 165 screen_info.display_texture = screen_info.texture.resource.handle;
166 166
167 Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes, 167 rasterizer->FlushRegion(ToCacheAddr(Memory::GetPointer(framebuffer_addr)), size_in_bytes);
168 Memory::FlushMode::Flush);
169 168
170 VideoCore::MortonCopyPixels128(framebuffer.width, framebuffer.height, bytes_per_pixel, 4, 169 constexpr u32 linear_bpp = 4;
171 Memory::GetPointer(framebuffer_addr), 170 VideoCore::MortonCopyPixels128(VideoCore::MortonSwizzleMode::MortonToLinear,
172 gl_framebuffer_data.data(), true); 171 framebuffer.width, framebuffer.height, bytes_per_pixel,
172 linear_bpp, Memory::GetPointer(framebuffer_addr),
173 gl_framebuffer_data.data());
173 174
174 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); 175 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
175 176
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 4a33a6c84..95eab3fec 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -17,6 +17,11 @@
17 17
18namespace Vulkan { 18namespace Vulkan {
19 19
20CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offset,
21 std::size_t alignment, u8* host_ptr)
22 : cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{
23 host_ptr} {}
24
20VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, 25VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
21 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, 26 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
22 VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size) 27 VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size)
@@ -37,16 +42,18 @@ VKBufferCache::~VKBufferCache() = default;
37u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment, 42u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment,
38 bool cache) { 43 bool cache) {
39 const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)}; 44 const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)};
40 ASSERT(cpu_addr); 45 ASSERT_MSG(cpu_addr, "Invalid GPU address");
41 46
42 // Cache management is a big overhead, so only cache entries with a given size. 47 // Cache management is a big overhead, so only cache entries with a given size.
43 // TODO: Figure out which size is the best for given games. 48 // TODO: Figure out which size is the best for given games.
44 cache &= size >= 2048; 49 cache &= size >= 2048;
45 50
51 const auto& host_ptr{Memory::GetPointer(*cpu_addr)};
46 if (cache) { 52 if (cache) {
47 if (auto entry = TryGet(*cpu_addr); entry) { 53 auto entry = TryGet(host_ptr);
48 if (entry->size >= size && entry->alignment == alignment) { 54 if (entry) {
49 return entry->offset; 55 if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
56 return entry->GetOffset();
50 } 57 }
51 Unregister(entry); 58 Unregister(entry);
52 } 59 }
@@ -55,17 +62,17 @@ u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64
55 AlignBuffer(alignment); 62 AlignBuffer(alignment);
56 const u64 uploaded_offset = buffer_offset; 63 const u64 uploaded_offset = buffer_offset;
57 64
58 Memory::ReadBlock(*cpu_addr, buffer_ptr, size); 65 if (!host_ptr) {
66 return uploaded_offset;
67 }
59 68
69 std::memcpy(buffer_ptr, host_ptr, size);
60 buffer_ptr += size; 70 buffer_ptr += size;
61 buffer_offset += size; 71 buffer_offset += size;
62 72
63 if (cache) { 73 if (cache) {
64 auto entry = std::make_shared<CachedBufferEntry>(); 74 auto entry = std::make_shared<CachedBufferEntry>(*cpu_addr, size, uploaded_offset,
65 entry->offset = uploaded_offset; 75 alignment, host_ptr);
66 entry->size = size;
67 entry->alignment = alignment;
68 entry->addr = *cpu_addr;
69 Register(entry); 76 Register(entry);
70 } 77 }
71 78
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index d8e916f31..8b415744b 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -24,22 +24,39 @@ class VKFence;
24class VKMemoryManager; 24class VKMemoryManager;
25class VKStreamBuffer; 25class VKStreamBuffer;
26 26
27struct CachedBufferEntry final : public RasterizerCacheObject { 27class CachedBufferEntry final : public RasterizerCacheObject {
28 VAddr GetAddr() const override { 28public:
29 return addr; 29 explicit CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offset, std::size_t alignment,
30 u8* host_ptr);
31
32 VAddr GetCpuAddr() const override {
33 return cpu_addr;
30 } 34 }
31 35
32 std::size_t GetSizeInBytes() const override { 36 std::size_t GetSizeInBytes() const override {
33 return size; 37 return size;
34 } 38 }
35 39
40 std::size_t GetSize() const {
41 return size;
42 }
43
44 u64 GetOffset() const {
45 return offset;
46 }
47
48 std::size_t GetAlignment() const {
49 return alignment;
50 }
51
36 // We do not have to flush this cache as things in it are never modified by us. 52 // We do not have to flush this cache as things in it are never modified by us.
37 void Flush() override {} 53 void Flush() override {}
38 54
39 VAddr addr; 55private:
40 std::size_t size; 56 VAddr cpu_addr{};
41 u64 offset; 57 std::size_t size{};
42 std::size_t alignment; 58 u64 offset{};
59 std::size_t alignment{};
43}; 60};
44 61
45class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { 62class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
new file mode 100644
index 000000000..ed3178f09
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
@@ -0,0 +1,81 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <optional>
7#include <unordered_map>
8
9#include "common/assert.h"
10#include "common/cityhash.h"
11#include "video_core/renderer_vulkan/declarations.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/vk_sampler_cache.h"
14#include "video_core/textures/texture.h"
15
16namespace Vulkan {
17
18static std::optional<vk::BorderColor> TryConvertBorderColor(std::array<float, 4> color) {
19 // TODO(Rodrigo): Manage integer border colors
20 if (color == std::array<float, 4>{0, 0, 0, 0}) {
21 return vk::BorderColor::eFloatTransparentBlack;
22 } else if (color == std::array<float, 4>{0, 0, 0, 1}) {
23 return vk::BorderColor::eFloatOpaqueBlack;
24 } else if (color == std::array<float, 4>{1, 1, 1, 1}) {
25 return vk::BorderColor::eFloatOpaqueWhite;
26 } else {
27 return {};
28 }
29}
30
31std::size_t SamplerCacheKey::Hash() const {
32 static_assert(sizeof(raw) % sizeof(u64) == 0);
33 return static_cast<std::size_t>(
34 Common::CityHash64(reinterpret_cast<const char*>(raw.data()), sizeof(raw) / sizeof(u64)));
35}
36
37bool SamplerCacheKey::operator==(const SamplerCacheKey& rhs) const {
38 return raw == rhs.raw;
39}
40
41VKSamplerCache::VKSamplerCache(const VKDevice& device) : device{device} {}
42
43VKSamplerCache::~VKSamplerCache() = default;
44
45vk::Sampler VKSamplerCache::GetSampler(const Tegra::Texture::TSCEntry& tsc) {
46 const auto [entry, is_cache_miss] = cache.try_emplace(SamplerCacheKey{tsc});
47 auto& sampler = entry->second;
48 if (is_cache_miss) {
49 sampler = CreateSampler(tsc);
50 }
51 return *sampler;
52}
53
54UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) {
55 const float max_anisotropy = tsc.GetMaxAnisotropy();
56 const bool has_anisotropy = max_anisotropy > 1.0f;
57
58 const auto border_color = tsc.GetBorderColor();
59 const auto vk_border_color = TryConvertBorderColor(border_color);
60 UNIMPLEMENTED_IF_MSG(!vk_border_color, "Unimplemented border color {} {} {} {}",
61 border_color[0], border_color[1], border_color[2], border_color[3]);
62
63 constexpr bool unnormalized_coords = false;
64
65 const vk::SamplerCreateInfo sampler_ci(
66 {}, MaxwellToVK::Sampler::Filter(tsc.mag_filter),
67 MaxwellToVK::Sampler::Filter(tsc.min_filter),
68 MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
69 MaxwellToVK::Sampler::WrapMode(tsc.wrap_u), MaxwellToVK::Sampler::WrapMode(tsc.wrap_v),
70 MaxwellToVK::Sampler::WrapMode(tsc.wrap_p), tsc.GetLodBias(), has_anisotropy,
71 max_anisotropy, tsc.depth_compare_enabled,
72 MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), tsc.GetMinLod(),
73 tsc.GetMaxLod(), vk_border_color.value_or(vk::BorderColor::eFloatTransparentBlack),
74 unnormalized_coords);
75
76 const auto& dld = device.GetDispatchLoader();
77 const auto dev = device.GetLogical();
78 return dev.createSamplerUnique(sampler_ci, nullptr, dld);
79}
80
81} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.h b/src/video_core/renderer_vulkan/vk_sampler_cache.h
new file mode 100644
index 000000000..c6394dc87
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_sampler_cache.h
@@ -0,0 +1,56 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <unordered_map>
8
9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/declarations.h"
11#include "video_core/textures/texture.h"
12
13namespace Vulkan {
14
15class VKDevice;
16
17struct SamplerCacheKey final : public Tegra::Texture::TSCEntry {
18 std::size_t Hash() const;
19
20 bool operator==(const SamplerCacheKey& rhs) const;
21
22 bool operator!=(const SamplerCacheKey& rhs) const {
23 return !operator==(rhs);
24 }
25};
26
27} // namespace Vulkan
28
29namespace std {
30
31template <>
32struct hash<Vulkan::SamplerCacheKey> {
33 std::size_t operator()(const Vulkan::SamplerCacheKey& k) const noexcept {
34 return k.Hash();
35 }
36};
37
38} // namespace std
39
40namespace Vulkan {
41
42class VKSamplerCache {
43public:
44 explicit VKSamplerCache(const VKDevice& device);
45 ~VKSamplerCache();
46
47 vk::Sampler GetSampler(const Tegra::Texture::TSCEntry& tsc);
48
49private:
50 UniqueSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc);
51
52 const VKDevice& device;
53 std::unordered_map<SamplerCacheKey, UniqueSampler> cache;
54};
55
56} // namespace Vulkan
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index cad7340f5..995d0e068 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -6,7 +6,6 @@
6#include <cstring> 6#include <cstring>
7#include "common/alignment.h" 7#include "common/alignment.h"
8#include "common/assert.h" 8#include "common/assert.h"
9#include "core/memory.h"
10#include "video_core/gpu.h" 9#include "video_core/gpu.h"
11#include "video_core/textures/decoders.h" 10#include "video_core/textures/decoders.h"
12#include "video_core/textures/texture.h" 11#include "video_core/textures/texture.h"
@@ -230,18 +229,18 @@ u32 BytesPerPixel(TextureFormat format) {
230 } 229 }
231} 230}
232 231
233void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y, 232void UnswizzleTexture(u8* const unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
234 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, 233 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
235 u32 block_depth, u32 width_spacing) { 234 u32 block_depth, u32 width_spacing) {
236 CopySwizzledData((width + tile_size_x - 1) / tile_size_x, 235 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
237 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel, 236 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
238 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true, 237 bytes_per_pixel, address, unswizzled_data, true, block_height, block_depth,
239 block_height, block_depth, width_spacing); 238 width_spacing);
240} 239}
241 240
242std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 241std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y, u32 bytes_per_pixel,
243 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 242 u32 width, u32 height, u32 depth, u32 block_height,
244 u32 block_height, u32 block_depth, u32 width_spacing) { 243 u32 block_depth, u32 width_spacing) {
245 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 244 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
246 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel, 245 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
247 width, height, depth, block_height, block_depth, width_spacing); 246 width, height, depth, block_height, block_depth, width_spacing);
@@ -249,8 +248,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y
249} 248}
250 249
251void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 250void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
252 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 251 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height) {
253 u32 block_height) {
254 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) / 252 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
255 gob_size_x}; 253 gob_size_x};
256 for (u32 line = 0; line < subrect_height; ++line) { 254 for (u32 line = 0; line < subrect_height; ++line) {
@@ -262,17 +260,17 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
262 const u32 gob_address = 260 const u32 gob_address =
263 gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height; 261 gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
264 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x]; 262 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
265 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; 263 u8* source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
266 const VAddr dest_addr = swizzled_data + swizzled_offset; 264 u8* dest_addr = swizzled_data + swizzled_offset;
267 265
268 Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel); 266 std::memcpy(dest_addr, source_line, bytes_per_pixel);
269 } 267 }
270 } 268 }
271} 269}
272 270
273void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 271void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
274 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 272 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
275 u32 block_height, u32 offset_x, u32 offset_y) { 273 u32 offset_x, u32 offset_y) {
276 for (u32 line = 0; line < subrect_height; ++line) { 274 for (u32 line = 0; line < subrect_height; ++line) {
277 const u32 y2 = line + offset_y; 275 const u32 y2 = line + offset_y;
278 const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height + 276 const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
@@ -282,10 +280,10 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
282 const u32 x2 = (x + offset_x) * bytes_per_pixel; 280 const u32 x2 = (x + offset_x) * bytes_per_pixel;
283 const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height; 281 const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
284 const u32 swizzled_offset = gob_address + table[x2 % gob_size_x]; 282 const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
285 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; 283 u8* dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
286 const VAddr source_addr = swizzled_data + swizzled_offset; 284 u8* source_addr = swizzled_data + swizzled_offset;
287 285
288 Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel); 286 std::memcpy(dest_line, source_addr, bytes_per_pixel);
289 } 287 }
290 } 288 }
291} 289}
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 65df86890..e078fa274 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -17,14 +17,14 @@ inline std::size_t GetGOBSize() {
17} 17}
18 18
19/// Unswizzles a swizzled texture without changing its format. 19/// Unswizzles a swizzled texture without changing its format.
20void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y, 20void UnswizzleTexture(u8* unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
21 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 21 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
22 u32 block_height = TICEntry::DefaultBlockHeight, 22 u32 block_height = TICEntry::DefaultBlockHeight,
23 u32 block_depth = TICEntry::DefaultBlockHeight, u32 width_spacing = 0); 23 u32 block_depth = TICEntry::DefaultBlockHeight, u32 width_spacing = 0);
24 24
25/// Unswizzles a swizzled texture without changing its format. 25/// Unswizzles a swizzled texture without changing its format.
26std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 26std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y, u32 bytes_per_pixel,
27 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 27 u32 width, u32 height, u32 depth,
28 u32 block_height = TICEntry::DefaultBlockHeight, 28 u32 block_height = TICEntry::DefaultBlockHeight,
29 u32 block_depth = TICEntry::DefaultBlockHeight, 29 u32 block_depth = TICEntry::DefaultBlockHeight,
30 u32 width_spacing = 0); 30 u32 width_spacing = 0);
@@ -44,12 +44,11 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height
44 44
45/// Copies an untiled subrectangle into a tiled surface. 45/// Copies an untiled subrectangle into a tiled surface.
46void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 46void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
47 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 47 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height);
48 u32 block_height);
49 48
50/// Copies a tiled subrectangle into a linear surface. 49/// Copies a tiled subrectangle into a linear surface.
51void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 50void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
52 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 51 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
53 u32 block_height, u32 offset_x, u32 offset_y); 52 u32 offset_x, u32 offset_y);
54 53
55} // namespace Tegra::Texture 54} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 8c278c0e2..93ecc6e31 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -283,31 +283,36 @@ enum class TextureMipmapFilter : u32 {
283 283
284struct TSCEntry { 284struct TSCEntry {
285 union { 285 union {
286 BitField<0, 3, WrapMode> wrap_u; 286 struct {
287 BitField<3, 3, WrapMode> wrap_v; 287 union {
288 BitField<6, 3, WrapMode> wrap_p; 288 BitField<0, 3, WrapMode> wrap_u;
289 BitField<9, 1, u32> depth_compare_enabled; 289 BitField<3, 3, WrapMode> wrap_v;
290 BitField<10, 3, DepthCompareFunc> depth_compare_func; 290 BitField<6, 3, WrapMode> wrap_p;
291 BitField<13, 1, u32> srgb_conversion; 291 BitField<9, 1, u32> depth_compare_enabled;
292 BitField<20, 3, u32> max_anisotropy; 292 BitField<10, 3, DepthCompareFunc> depth_compare_func;
293 BitField<13, 1, u32> srgb_conversion;
294 BitField<20, 3, u32> max_anisotropy;
295 };
296 union {
297 BitField<0, 2, TextureFilter> mag_filter;
298 BitField<4, 2, TextureFilter> min_filter;
299 BitField<6, 2, TextureMipmapFilter> mipmap_filter;
300 BitField<9, 1, u32> cubemap_interface_filtering;
301 BitField<12, 13, u32> mip_lod_bias;
302 };
303 union {
304 BitField<0, 12, u32> min_lod_clamp;
305 BitField<12, 12, u32> max_lod_clamp;
306 BitField<24, 8, u32> srgb_border_color_r;
307 };
308 union {
309 BitField<12, 8, u32> srgb_border_color_g;
310 BitField<20, 8, u32> srgb_border_color_b;
311 };
312 std::array<f32, 4> border_color;
313 };
314 std::array<u8, 0x20> raw;
293 }; 315 };
294 union {
295 BitField<0, 2, TextureFilter> mag_filter;
296 BitField<4, 2, TextureFilter> min_filter;
297 BitField<6, 2, TextureMipmapFilter> mipmap_filter;
298 BitField<9, 1, u32> cubemap_interface_filtering;
299 BitField<12, 13, u32> mip_lod_bias;
300 };
301 union {
302 BitField<0, 12, u32> min_lod_clamp;
303 BitField<12, 12, u32> max_lod_clamp;
304 BitField<24, 8, u32> srgb_border_color_r;
305 };
306 union {
307 BitField<12, 8, u32> srgb_border_color_g;
308 BitField<20, 8, u32> srgb_border_color_b;
309 };
310 std::array<f32, 4> border_color;
311 316
312 float GetMaxAnisotropy() const { 317 float GetMaxAnisotropy() const {
313 return static_cast<float>(1U << max_anisotropy); 318 return static_cast<float>(1U << max_anisotropy);
@@ -324,7 +329,7 @@ struct TSCEntry {
324 float GetLodBias() const { 329 float GetLodBias() const {
325 // Sign extend the 13-bit value. 330 // Sign extend the 13-bit value.
326 constexpr u32 mask = 1U << (13 - 1); 331 constexpr u32 mask = 1U << (13 - 1);
327 return static_cast<float>((mip_lod_bias ^ mask) - mask) / 256.0f; 332 return static_cast<s32>((mip_lod_bias ^ mask) - mask) / 256.0f;
328 } 333 }
329 334
330 std::array<float, 4> GetBorderColor() const { 335 std::array<float, 4> GetBorderColor() const {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 3b070bfbb..d2c97b1f8 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -123,7 +123,6 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
123 setAttribute(Qt::WA_AcceptTouchEvents); 123 setAttribute(Qt::WA_AcceptTouchEvents);
124 124
125 InputCommon::Init(); 125 InputCommon::Init();
126 InputCommon::StartJoystickEventHandler();
127 connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent), 126 connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent),
128 &GMainWindow::OnLoadComplete); 127 &GMainWindow::OnLoadComplete);
129} 128}
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 74dc6bb28..4650f96a3 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -209,7 +209,7 @@ void Config::ReadPlayerValues() {
209 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 209 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
210 auto& player = Settings::values.players[p]; 210 auto& player = Settings::values.players[p];
211 211
212 player.connected = qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); 212 player.connected = ReadSetting(QString("player_%1_connected").arg(p), false).toBool();
213 213
214 player.type = static_cast<Settings::ControllerType>( 214 player.type = static_cast<Settings::ControllerType>(
215 qt_config 215 qt_config
@@ -269,7 +269,7 @@ void Config::ReadPlayerValues() {
269} 269}
270 270
271void Config::ReadDebugValues() { 271void Config::ReadDebugValues() {
272 Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool(); 272 Settings::values.debug_pad_enabled = ReadSetting("debug_pad_enabled", false).toBool();
273 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 273 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
274 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 274 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
275 Settings::values.debug_pad_buttons[i] = 275 Settings::values.debug_pad_buttons[i] =
@@ -298,7 +298,7 @@ void Config::ReadDebugValues() {
298} 298}
299 299
300void Config::ReadKeyboardValues() { 300void Config::ReadKeyboardValues() {
301 Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool(); 301 Settings::values.keyboard_enabled = ReadSetting("keyboard_enabled", false).toBool();
302 302
303 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), 303 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
304 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); 304 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
@@ -311,7 +311,7 @@ void Config::ReadKeyboardValues() {
311} 311}
312 312
313void Config::ReadMouseValues() { 313void Config::ReadMouseValues() {
314 Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool(); 314 Settings::values.mouse_enabled = ReadSetting("mouse_enabled", false).toBool();
315 315
316 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 316 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
317 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); 317 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
@@ -327,16 +327,14 @@ void Config::ReadMouseValues() {
327} 327}
328 328
329void Config::ReadTouchscreenValues() { 329void Config::ReadTouchscreenValues() {
330 Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool(); 330 Settings::values.touchscreen.enabled = ReadSetting("touchscreen_enabled", true).toBool();
331 Settings::values.touchscreen.device = 331 Settings::values.touchscreen.device =
332 qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString(); 332 ReadSetting("touchscreen_device", "engine:emu_window").toString().toStdString();
333 333
334 Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt(); 334 Settings::values.touchscreen.finger = ReadSetting("touchscreen_finger", 0).toUInt();
335 Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt(); 335 Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt();
336 Settings::values.touchscreen.diameter_x = 336 Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt();
337 qt_config->value("touchscreen_diameter_x", 15).toUInt(); 337 Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt();
338 Settings::values.touchscreen.diameter_y =
339 qt_config->value("touchscreen_diameter_y", 15).toUInt();
340 qt_config->endGroup(); 338 qt_config->endGroup();
341} 339}
342 340
@@ -357,42 +355,41 @@ void Config::ReadValues() {
357 ReadTouchscreenValues(); 355 ReadTouchscreenValues();
358 356
359 Settings::values.motion_device = 357 Settings::values.motion_device =
360 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") 358 ReadSetting("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
361 .toString() 359 .toString()
362 .toStdString(); 360 .toStdString();
363 361
364 qt_config->beginGroup("Core"); 362 qt_config->beginGroup("Core");
365 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 363 Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
366 Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool(); 364 Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
367 qt_config->endGroup(); 365 qt_config->endGroup();
368 366
369 qt_config->beginGroup("Renderer"); 367 qt_config->beginGroup("Renderer");
370 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 368 Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat();
371 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); 369 Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool();
372 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); 370 Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt();
373 Settings::values.use_disk_shader_cache = 371 Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool();
374 qt_config->value("use_disk_shader_cache", false).toBool();
375 Settings::values.use_accurate_gpu_emulation = 372 Settings::values.use_accurate_gpu_emulation =
376 qt_config->value("use_accurate_gpu_emulation", false).toBool(); 373 ReadSetting("use_accurate_gpu_emulation", false).toBool();
377 Settings::values.use_asynchronous_gpu_emulation = 374 Settings::values.use_asynchronous_gpu_emulation =
378 qt_config->value("use_asynchronous_gpu_emulation", false).toBool(); 375 ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
379 376
380 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 377 Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat();
381 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 378 Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat();
382 Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat(); 379 Settings::values.bg_blue = ReadSetting("bg_blue", 0.0).toFloat();
383 qt_config->endGroup(); 380 qt_config->endGroup();
384 381
385 qt_config->beginGroup("Audio"); 382 qt_config->beginGroup("Audio");
386 Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); 383 Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString();
387 Settings::values.enable_audio_stretching = 384 Settings::values.enable_audio_stretching =
388 qt_config->value("enable_audio_stretching", true).toBool(); 385 ReadSetting("enable_audio_stretching", true).toBool();
389 Settings::values.audio_device_id = 386 Settings::values.audio_device_id =
390 qt_config->value("output_device", "auto").toString().toStdString(); 387 ReadSetting("output_device", "auto").toString().toStdString();
391 Settings::values.volume = qt_config->value("volume", 1).toFloat(); 388 Settings::values.volume = ReadSetting("volume", 1).toFloat();
392 qt_config->endGroup(); 389 qt_config->endGroup();
393 390
394 qt_config->beginGroup("Data Storage"); 391 qt_config->beginGroup("Data Storage");
395 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); 392 Settings::values.use_virtual_sd = ReadSetting("use_virtual_sd", true).toBool();
396 FileUtil::GetUserPath( 393 FileUtil::GetUserPath(
397 FileUtil::UserPath::NANDDir, 394 FileUtil::UserPath::NANDDir,
398 qt_config 395 qt_config
@@ -410,30 +407,30 @@ void Config::ReadValues() {
410 qt_config->endGroup(); 407 qt_config->endGroup();
411 408
412 qt_config->beginGroup("Core"); 409 qt_config->beginGroup("Core");
413 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 410 Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
414 Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool(); 411 Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
415 qt_config->endGroup(); 412 qt_config->endGroup();
416 413
417 qt_config->beginGroup("System"); 414 qt_config->beginGroup("System");
418 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 415 Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool();
419 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); 416 Settings::values.enable_nfc = ReadSetting("enable_nfc", true).toBool();
420 417
421 Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0, 418 Settings::values.current_user =
422 Service::Account::MAX_USERS - 1); 419 std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1);
423 420
424 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 421 Settings::values.language_index = ReadSetting("language_index", 1).toInt();
425 422
426 const auto rng_seed_enabled = qt_config->value("rng_seed_enabled", false).toBool(); 423 const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool();
427 if (rng_seed_enabled) { 424 if (rng_seed_enabled) {
428 Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong(); 425 Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong();
429 } else { 426 } else {
430 Settings::values.rng_seed = std::nullopt; 427 Settings::values.rng_seed = std::nullopt;
431 } 428 }
432 429
433 const auto custom_rtc_enabled = qt_config->value("custom_rtc_enabled", false).toBool(); 430 const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool();
434 if (custom_rtc_enabled) { 431 if (custom_rtc_enabled) {
435 Settings::values.custom_rtc = 432 Settings::values.custom_rtc =
436 std::chrono::seconds(qt_config->value("custom_rtc", 0).toULongLong()); 433 std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong());
437 } else { 434 } else {
438 Settings::values.custom_rtc = std::nullopt; 435 Settings::values.custom_rtc = std::nullopt;
439 } 436 }
@@ -441,35 +438,35 @@ void Config::ReadValues() {
441 qt_config->endGroup(); 438 qt_config->endGroup();
442 439
443 qt_config->beginGroup("Miscellaneous"); 440 qt_config->beginGroup("Miscellaneous");
444 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); 441 Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString();
445 Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool(); 442 Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool();
446 qt_config->endGroup(); 443 qt_config->endGroup();
447 444
448 qt_config->beginGroup("Debugging"); 445 qt_config->beginGroup("Debugging");
449 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 446 Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool();
450 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 447 Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt();
451 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); 448 Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString();
452 Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool(); 449 Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool();
453 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); 450 Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool();
454 qt_config->endGroup(); 451 qt_config->endGroup();
455 452
456 qt_config->beginGroup("WebService"); 453 qt_config->beginGroup("WebService");
457 Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool(); 454 Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool();
458 Settings::values.web_api_url = 455 Settings::values.web_api_url =
459 qt_config->value("web_api_url", "https://api.yuzu-emu.org").toString().toStdString(); 456 ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString();
460 Settings::values.yuzu_username = qt_config->value("yuzu_username").toString().toStdString(); 457 Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString();
461 Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); 458 Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString();
462 qt_config->endGroup(); 459 qt_config->endGroup();
463 460
464 const auto size = qt_config->beginReadArray("DisabledAddOns"); 461 const auto size = qt_config->beginReadArray("DisabledAddOns");
465 for (int i = 0; i < size; ++i) { 462 for (int i = 0; i < size; ++i) {
466 qt_config->setArrayIndex(i); 463 qt_config->setArrayIndex(i);
467 const auto title_id = qt_config->value("title_id", 0).toULongLong(); 464 const auto title_id = ReadSetting("title_id", 0).toULongLong();
468 std::vector<std::string> out; 465 std::vector<std::string> out;
469 const auto d_size = qt_config->beginReadArray("disabled"); 466 const auto d_size = qt_config->beginReadArray("disabled");
470 for (int j = 0; j < d_size; ++j) { 467 for (int j = 0; j < d_size; ++j) {
471 qt_config->setArrayIndex(j); 468 qt_config->setArrayIndex(j);
472 out.push_back(qt_config->value("d", "").toString().toStdString()); 469 out.push_back(ReadSetting("d", "").toString().toStdString());
473 } 470 }
474 qt_config->endArray(); 471 qt_config->endArray();
475 Settings::values.disabled_addons.insert_or_assign(title_id, out); 472 Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -477,41 +474,38 @@ void Config::ReadValues() {
477 qt_config->endArray(); 474 qt_config->endArray();
478 475
479 qt_config->beginGroup("UI"); 476 qt_config->beginGroup("UI");
480 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 477 UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString();
481 UISettings::values.enable_discord_presence = 478 UISettings::values.enable_discord_presence =
482 qt_config->value("enable_discord_presence", true).toBool(); 479 ReadSetting("enable_discord_presence", true).toBool();
483 UISettings::values.screenshot_resolution_factor = 480 UISettings::values.screenshot_resolution_factor =
484 static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt()); 481 static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt());
485 UISettings::values.select_user_on_boot = 482 UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool();
486 qt_config->value("select_user_on_boot", false).toBool();
487 483
488 qt_config->beginGroup("UIGameList"); 484 qt_config->beginGroup("UIGameList");
489 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 485 UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool();
490 UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool(); 486 UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool();
491 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); 487 UISettings::values.icon_size = ReadSetting("icon_size", 64).toUInt();
492 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt(); 488 UISettings::values.row_1_text_id = ReadSetting("row_1_text_id", 3).toUInt();
493 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt(); 489 UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt();
494 qt_config->endGroup(); 490 qt_config->endGroup();
495 491
496 qt_config->beginGroup("UILayout"); 492 qt_config->beginGroup("UILayout");
497 UISettings::values.geometry = qt_config->value("geometry").toByteArray(); 493 UISettings::values.geometry = ReadSetting("geometry").toByteArray();
498 UISettings::values.state = qt_config->value("state").toByteArray(); 494 UISettings::values.state = ReadSetting("state").toByteArray();
499 UISettings::values.renderwindow_geometry = 495 UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray();
500 qt_config->value("geometryRenderWindow").toByteArray(); 496 UISettings::values.gamelist_header_state = ReadSetting("gameListHeaderState").toByteArray();
501 UISettings::values.gamelist_header_state =
502 qt_config->value("gameListHeaderState").toByteArray();
503 UISettings::values.microprofile_geometry = 497 UISettings::values.microprofile_geometry =
504 qt_config->value("microProfileDialogGeometry").toByteArray(); 498 ReadSetting("microProfileDialogGeometry").toByteArray();
505 UISettings::values.microprofile_visible = 499 UISettings::values.microprofile_visible =
506 qt_config->value("microProfileDialogVisible", false).toBool(); 500 ReadSetting("microProfileDialogVisible", false).toBool();
507 qt_config->endGroup(); 501 qt_config->endGroup();
508 502
509 qt_config->beginGroup("Paths"); 503 qt_config->beginGroup("Paths");
510 UISettings::values.roms_path = qt_config->value("romsPath").toString(); 504 UISettings::values.roms_path = ReadSetting("romsPath").toString();
511 UISettings::values.symbols_path = qt_config->value("symbolsPath").toString(); 505 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
512 UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString(); 506 UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString();
513 UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool(); 507 UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool();
514 UISettings::values.recent_files = qt_config->value("recentFiles").toStringList(); 508 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
515 qt_config->endGroup(); 509 qt_config->endGroup();
516 510
517 qt_config->beginGroup("Shortcuts"); 511 qt_config->beginGroup("Shortcuts");
@@ -524,8 +518,8 @@ void Config::ReadValues() {
524 qt_config->beginGroup(hotkey); 518 qt_config->beginGroup(hotkey);
525 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( 519 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
526 group + "/" + hotkey, 520 group + "/" + hotkey,
527 UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), 521 UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(),
528 qt_config->value("Context").toInt()))); 522 ReadSetting("Context").toInt())));
529 qt_config->endGroup(); 523 qt_config->endGroup();
530 } 524 }
531 525
@@ -533,16 +527,16 @@ void Config::ReadValues() {
533 } 527 }
534 qt_config->endGroup(); 528 qt_config->endGroup();
535 529
536 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); 530 UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool();
537 UISettings::values.fullscreen = qt_config->value("fullscreen", false).toBool(); 531 UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool();
538 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); 532 UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool();
539 UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool(); 533 UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool();
540 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); 534 UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool();
541 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); 535 UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool();
542 UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); 536 UISettings::values.first_start = ReadSetting("firstStart", true).toBool();
543 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); 537 UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt();
544 UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); 538 UISettings::values.show_console = ReadSetting("showConsole", false).toBool();
545 UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt(); 539 UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt();
546 540
547 ApplyDefaultProfileIfInputInvalid(); 541 ApplyDefaultProfileIfInputInvalid();
548 542
@@ -553,62 +547,79 @@ void Config::SavePlayerValues() {
553 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 547 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
554 const auto& player = Settings::values.players[p]; 548 const auto& player = Settings::values.players[p];
555 549
556 qt_config->setValue(QString("player_%1_connected").arg(p), player.connected); 550 WriteSetting(QString("player_%1_connected").arg(p), player.connected, false);
557 qt_config->setValue(QString("player_%1_type").arg(p), static_cast<u8>(player.type)); 551 WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type),
552 static_cast<u8>(Settings::ControllerType::DualJoycon));
558 553
559 qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left); 554 WriteSetting(QString("player_%1_body_color_left").arg(p), player.body_color_left,
560 qt_config->setValue(QString("player_%1_body_color_right").arg(p), player.body_color_right); 555 Settings::JOYCON_BODY_NEON_BLUE);
561 qt_config->setValue(QString("player_%1_button_color_left").arg(p), 556 WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right,
562 player.button_color_left); 557 Settings::JOYCON_BODY_NEON_RED);
563 qt_config->setValue(QString("player_%1_button_color_right").arg(p), 558 WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left,
564 player.button_color_right); 559 Settings::JOYCON_BUTTONS_NEON_BLUE);
560 WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right,
561 Settings::JOYCON_BUTTONS_NEON_RED);
565 562
566 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 563 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
567 qt_config->setValue(QString("player_%1_").arg(p) + 564 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
568 QString::fromStdString(Settings::NativeButton::mapping[i]), 565 WriteSetting(QString("player_%1_").arg(p) +
569 QString::fromStdString(player.buttons[i])); 566 QString::fromStdString(Settings::NativeButton::mapping[i]),
567 QString::fromStdString(player.buttons[i]),
568 QString::fromStdString(default_param));
570 } 569 }
571 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 570 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
572 qt_config->setValue(QString("player_%1_").arg(p) + 571 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
573 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 572 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
574 QString::fromStdString(player.analogs[i])); 573 default_analogs[i][3], default_analogs[i][4], 0.5f);
574 WriteSetting(QString("player_%1_").arg(p) +
575 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
576 QString::fromStdString(player.analogs[i]),
577 QString::fromStdString(default_param));
575 } 578 }
576 } 579 }
577} 580}
578 581
579void Config::SaveDebugValues() { 582void Config::SaveDebugValues() {
580 qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled); 583 WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false);
581 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 584 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
582 qt_config->setValue(QString("debug_pad_") + 585 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
583 QString::fromStdString(Settings::NativeButton::mapping[i]), 586 WriteSetting(QString("debug_pad_") +
584 QString::fromStdString(Settings::values.debug_pad_buttons[i])); 587 QString::fromStdString(Settings::NativeButton::mapping[i]),
588 QString::fromStdString(Settings::values.debug_pad_buttons[i]),
589 QString::fromStdString(default_param));
585 } 590 }
586 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 591 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
587 qt_config->setValue(QString("debug_pad_") + 592 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
588 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 593 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
589 QString::fromStdString(Settings::values.debug_pad_analogs[i])); 594 default_analogs[i][3], default_analogs[i][4], 0.5f);
595 WriteSetting(QString("debug_pad_") +
596 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
597 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
598 QString::fromStdString(default_param));
590 } 599 }
591} 600}
592 601
593void Config::SaveMouseValues() { 602void Config::SaveMouseValues() {
594 qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled); 603 WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false);
595 604
596 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 605 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
597 qt_config->setValue(QString("mouse_") + 606 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
598 QString::fromStdString(Settings::NativeMouseButton::mapping[i]), 607 WriteSetting(QString("mouse_") +
599 QString::fromStdString(Settings::values.mouse_buttons[i])); 608 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
609 QString::fromStdString(Settings::values.mouse_buttons[i]),
610 QString::fromStdString(default_param));
600 } 611 }
601} 612}
602 613
603void Config::SaveTouchscreenValues() { 614void Config::SaveTouchscreenValues() {
604 qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled); 615 WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true);
605 qt_config->setValue("touchscreen_device", 616 WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device),
606 QString::fromStdString(Settings::values.touchscreen.device)); 617 "engine:emu_window");
607 618
608 qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger); 619 WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0);
609 qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle); 620 WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0);
610 qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x); 621 WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15);
611 qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y); 622 WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15);
612} 623}
613 624
614void Config::SaveValues() { 625void Config::SaveValues() {
@@ -619,91 +630,96 @@ void Config::SaveValues() {
619 SaveMouseValues(); 630 SaveMouseValues();
620 SaveTouchscreenValues(); 631 SaveTouchscreenValues();
621 632
622 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); 633 WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device),
623 qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled); 634 "engine:motion_emu,update_period:100,sensitivity:0.01");
635 WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false);
624 636
625 qt_config->endGroup(); 637 qt_config->endGroup();
626 638
627 qt_config->beginGroup("Core"); 639 qt_config->beginGroup("Core");
628 qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit); 640 WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true);
629 qt_config->setValue("use_multi_core", Settings::values.use_multi_core); 641 WriteSetting("use_multi_core", Settings::values.use_multi_core, false);
630 qt_config->endGroup(); 642 qt_config->endGroup();
631 643
632 qt_config->beginGroup("Renderer"); 644 qt_config->beginGroup("Renderer");
633 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 645 WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0);
634 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 646 WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true);
635 qt_config->setValue("frame_limit", Settings::values.frame_limit); 647 WriteSetting("frame_limit", Settings::values.frame_limit, 100);
636 qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache); 648 WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true);
637 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); 649 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
638 qt_config->setValue("use_asynchronous_gpu_emulation", 650 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
639 Settings::values.use_asynchronous_gpu_emulation); 651 false);
640 652
641 // Cast to double because Qt's written float values are not human-readable 653 // Cast to double because Qt's written float values are not human-readable
642 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 654 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
643 qt_config->setValue("bg_green", (double)Settings::values.bg_green); 655 WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0);
644 qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); 656 WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0);
645 qt_config->endGroup(); 657 qt_config->endGroup();
646 658
647 qt_config->beginGroup("Audio"); 659 qt_config->beginGroup("Audio");
648 qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); 660 WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto");
649 qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); 661 WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
650 qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); 662 WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
651 qt_config->setValue("volume", Settings::values.volume); 663 WriteSetting("volume", Settings::values.volume, 1.0f);
652 qt_config->endGroup(); 664 qt_config->endGroup();
653 665
654 qt_config->beginGroup("Data Storage"); 666 qt_config->beginGroup("Data Storage");
655 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); 667 WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true);
656 qt_config->setValue("nand_directory", 668 WriteSetting("nand_directory",
657 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 669 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
658 qt_config->setValue("sdmc_directory", 670 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
659 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 671 WriteSetting("sdmc_directory",
672 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
673 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
660 qt_config->endGroup(); 674 qt_config->endGroup();
661 675
662 qt_config->beginGroup("System"); 676 qt_config->beginGroup("System");
663 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 677 WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false);
664 qt_config->setValue("enable_nfc", Settings::values.enable_nfc); 678 WriteSetting("enable_nfc", Settings::values.enable_nfc, true);
665 qt_config->setValue("current_user", Settings::values.current_user); 679 WriteSetting("current_user", Settings::values.current_user, 0);
666 qt_config->setValue("language_index", Settings::values.language_index); 680 WriteSetting("language_index", Settings::values.language_index, 1);
667 681
668 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); 682 WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false);
669 qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0)); 683 WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0);
670 684
671 qt_config->setValue("custom_rtc_enabled", Settings::values.custom_rtc.has_value()); 685 WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false);
672 qt_config->setValue("custom_rtc", 686 WriteSetting("custom_rtc",
673 QVariant::fromValue<long long>( 687 QVariant::fromValue<long long>(
674 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count())); 688 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
689 0);
675 690
676 qt_config->endGroup(); 691 qt_config->endGroup();
677 692
678 qt_config->beginGroup("Miscellaneous"); 693 qt_config->beginGroup("Miscellaneous");
679 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); 694 WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info");
680 qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys); 695 WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false);
681 qt_config->endGroup(); 696 qt_config->endGroup();
682 697
683 qt_config->beginGroup("Debugging"); 698 qt_config->beginGroup("Debugging");
684 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 699 WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false);
685 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 700 WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689);
686 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); 701 WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), "");
687 qt_config->setValue("dump_exefs", Settings::values.dump_exefs); 702 WriteSetting("dump_exefs", Settings::values.dump_exefs, false);
688 qt_config->setValue("dump_nso", Settings::values.dump_nso); 703 WriteSetting("dump_nso", Settings::values.dump_nso, false);
689 qt_config->endGroup(); 704 qt_config->endGroup();
690 705
691 qt_config->beginGroup("WebService"); 706 qt_config->beginGroup("WebService");
692 qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); 707 WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true);
693 qt_config->setValue("web_api_url", QString::fromStdString(Settings::values.web_api_url)); 708 WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url),
694 qt_config->setValue("yuzu_username", QString::fromStdString(Settings::values.yuzu_username)); 709 "https://api.yuzu-emu.org");
695 qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); 710 WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username));
711 WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
696 qt_config->endGroup(); 712 qt_config->endGroup();
697 713
698 qt_config->beginWriteArray("DisabledAddOns"); 714 qt_config->beginWriteArray("DisabledAddOns");
699 int i = 0; 715 int i = 0;
700 for (const auto& elem : Settings::values.disabled_addons) { 716 for (const auto& elem : Settings::values.disabled_addons) {
701 qt_config->setArrayIndex(i); 717 qt_config->setArrayIndex(i);
702 qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first)); 718 WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0);
703 qt_config->beginWriteArray("disabled"); 719 qt_config->beginWriteArray("disabled");
704 for (std::size_t j = 0; j < elem.second.size(); ++j) { 720 for (std::size_t j = 0; j < elem.second.size(); ++j) {
705 qt_config->setArrayIndex(static_cast<int>(j)); 721 qt_config->setArrayIndex(static_cast<int>(j));
706 qt_config->setValue("d", QString::fromStdString(elem.second[j])); 722 WriteSetting("d", QString::fromStdString(elem.second[j]), "");
707 } 723 }
708 qt_config->endArray(); 724 qt_config->endArray();
709 ++i; 725 ++i;
@@ -711,60 +727,86 @@ void Config::SaveValues() {
711 qt_config->endArray(); 727 qt_config->endArray();
712 728
713 qt_config->beginGroup("UI"); 729 qt_config->beginGroup("UI");
714 qt_config->setValue("theme", UISettings::values.theme); 730 WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second);
715 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); 731 WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true);
716 qt_config->setValue("screenshot_resolution_factor", 732 WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor,
717 UISettings::values.screenshot_resolution_factor); 733 0);
718 qt_config->setValue("select_user_on_boot", UISettings::values.select_user_on_boot); 734 WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false);
719 735
720 qt_config->beginGroup("UIGameList"); 736 qt_config->beginGroup("UIGameList");
721 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 737 WriteSetting("show_unknown", UISettings::values.show_unknown, true);
722 qt_config->setValue("show_add_ons", UISettings::values.show_add_ons); 738 WriteSetting("show_add_ons", UISettings::values.show_add_ons, true);
723 qt_config->setValue("icon_size", UISettings::values.icon_size); 739 WriteSetting("icon_size", UISettings::values.icon_size, 64);
724 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); 740 WriteSetting("row_1_text_id", UISettings::values.row_1_text_id, 3);
725 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id); 741 WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2);
726 qt_config->endGroup(); 742 qt_config->endGroup();
727 743
728 qt_config->beginGroup("UILayout"); 744 qt_config->beginGroup("UILayout");
729 qt_config->setValue("geometry", UISettings::values.geometry); 745 WriteSetting("geometry", UISettings::values.geometry);
730 qt_config->setValue("state", UISettings::values.state); 746 WriteSetting("state", UISettings::values.state);
731 qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry); 747 WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry);
732 qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state); 748 WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state);
733 qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry); 749 WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
734 qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible); 750 WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false);
735 qt_config->endGroup(); 751 qt_config->endGroup();
736 752
737 qt_config->beginGroup("Paths"); 753 qt_config->beginGroup("Paths");
738 qt_config->setValue("romsPath", UISettings::values.roms_path); 754 WriteSetting("romsPath", UISettings::values.roms_path);
739 qt_config->setValue("symbolsPath", UISettings::values.symbols_path); 755 WriteSetting("symbolsPath", UISettings::values.symbols_path);
740 qt_config->setValue("screenshotPath", UISettings::values.screenshot_path); 756 WriteSetting("screenshotPath", UISettings::values.screenshot_path);
741 qt_config->setValue("gameListRootDir", UISettings::values.gamedir); 757 WriteSetting("gameListRootDir", UISettings::values.gamedir, ".");
742 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); 758 WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false);
743 qt_config->setValue("recentFiles", UISettings::values.recent_files); 759 WriteSetting("recentFiles", UISettings::values.recent_files);
744 qt_config->endGroup(); 760 qt_config->endGroup();
745 761
746 qt_config->beginGroup("Shortcuts"); 762 qt_config->beginGroup("Shortcuts");
747 for (auto shortcut : UISettings::values.shortcuts) { 763 for (auto shortcut : UISettings::values.shortcuts) {
748 qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first); 764 WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first);
749 qt_config->setValue(shortcut.first + "/Context", shortcut.second.second); 765 WriteSetting(shortcut.first + "/Context", shortcut.second.second);
750 } 766 }
751 qt_config->endGroup(); 767 qt_config->endGroup();
752 768
753 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode); 769 WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true);
754 qt_config->setValue("fullscreen", UISettings::values.fullscreen); 770 WriteSetting("fullscreen", UISettings::values.fullscreen, false);
755 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar); 771 WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true);
756 qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar); 772 WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true);
757 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); 773 WriteSetting("showStatusBar", UISettings::values.show_status_bar, true);
758 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); 774 WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true);
759 qt_config->setValue("firstStart", UISettings::values.first_start); 775 WriteSetting("firstStart", UISettings::values.first_start, true);
760 qt_config->setValue("calloutFlags", UISettings::values.callout_flags); 776 WriteSetting("calloutFlags", UISettings::values.callout_flags, 0);
761 qt_config->setValue("showConsole", UISettings::values.show_console); 777 WriteSetting("showConsole", UISettings::values.show_console, false);
762 qt_config->setValue("profileIndex", UISettings::values.profile_index); 778 WriteSetting("profileIndex", UISettings::values.profile_index, 0);
763 qt_config->endGroup(); 779 qt_config->endGroup();
764} 780}
765 781
782QVariant Config::ReadSetting(const QString& name) const {
783 return qt_config->value(name);
784}
785
786QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
787 QVariant result;
788 if (qt_config->value(name + "/default", false).toBool()) {
789 result = default_value;
790 } else {
791 result = qt_config->value(name, default_value);
792 }
793 return result;
794}
795
796void Config::WriteSetting(const QString& name, const QVariant& value) {
797 qt_config->setValue(name, value);
798}
799
800void Config::WriteSetting(const QString& name, const QVariant& value,
801 const QVariant& default_value) {
802 qt_config->setValue(name + "/default", value == default_value);
803 qt_config->setValue(name, value);
804}
805
766void Config::Reload() { 806void Config::Reload() {
767 ReadValues(); 807 ReadValues();
808 // To apply default value changes
809 SaveValues();
768 Settings::Apply(); 810 Settings::Apply();
769} 811}
770 812
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index e73ad19bb..f4185db18 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -42,6 +42,11 @@ private:
42 void SaveMouseValues(); 42 void SaveMouseValues();
43 void SaveTouchscreenValues(); 43 void SaveTouchscreenValues();
44 44
45 QVariant ReadSetting(const QString& name) const;
46 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
47 void WriteSetting(const QString& name, const QVariant& value);
48 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
49
45 std::unique_ptr<QSettings> qt_config; 50 std::unique_ptr<QSettings> qt_config;
46 std::string qt_config_loc; 51 std::string qt_config_loc;
47}; 52};
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 71683da8e..29f01dfb2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -383,13 +383,12 @@ void GraphicsSurfaceWidget::OnUpdate() {
383 // TODO: Implement a good way to visualize alpha components! 383 // TODO: Implement a good way to visualize alpha components!
384 384
385 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); 385 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
386 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
387 386
388 // 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.
389 // 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.
390 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
391 *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, 390 gpu.MemoryManager().GetPointer(surface_address), 1, 1,
392 surface_height, 1U); 391 Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U);
393 392
394 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
395 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 7df8eff53..de7a26e14 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -135,16 +135,16 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
135} 135}
136 136
137EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { 137EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
138 InputCommon::Init();
139
140 SDL_SetMainReady();
141
142 // Initialize the window 138 // Initialize the window
143 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 139 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
144 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 140 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
145 exit(1); 141 exit(1);
146 } 142 }
147 143
144 InputCommon::Init();
145
146 SDL_SetMainReady();
147
148 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 148 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
149 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 149 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
150 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 150 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
@@ -201,11 +201,9 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
201} 201}
202 202
203EmuWindow_SDL2::~EmuWindow_SDL2() { 203EmuWindow_SDL2::~EmuWindow_SDL2() {
204 InputCommon::SDL::CloseSDLJoysticks(); 204 InputCommon::Shutdown();
205 SDL_GL_DeleteContext(gl_context); 205 SDL_GL_DeleteContext(gl_context);
206 SDL_Quit(); 206 SDL_Quit();
207
208 InputCommon::Shutdown();
209} 207}
210 208
211void EmuWindow_SDL2::SwapBuffers() { 209void EmuWindow_SDL2::SwapBuffers() {
@@ -262,7 +260,6 @@ void EmuWindow_SDL2::PollEvents() {
262 is_open = false; 260 is_open = false;
263 break; 261 break;
264 default: 262 default:
265 InputCommon::SDL::HandleGameControllerEvent(event);
266 break; 263 break;
267 } 264 }
268 } 265 }