summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/ipc_helpers.h18
-rw-r--r--src/core/hle/kernel/client_port.cpp9
-rw-r--r--src/core/hle/kernel/process.cpp11
-rw-r--r--src/core/hle/kernel/process.h6
-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/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.cpp13
-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/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/engines/kepler_memory.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp5
-rw-r--r--src/video_core/engines/maxwell_dma.cpp7
-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/morton.cpp315
-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.cpp24
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h31
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp20
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.h17
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp69
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp40
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp43
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h19
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp8
-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/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_cmd/emu_window/emu_window_sdl2.cpp13
61 files changed, 1974 insertions, 1513 deletions
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 079283830..a1e4be070 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;
@@ -362,6 +365,11 @@ inline u32 RequestParser::Pop() {
362 return cmdbuf[index++]; 365 return cmdbuf[index++];
363} 366}
364 367
368template <>
369inline s32 RequestParser::Pop() {
370 return static_cast<s32>(Pop<u32>());
371}
372
365template <typename T> 373template <typename T>
366void RequestParser::PopRaw(T& value) { 374void RequestParser::PopRaw(T& value) {
367 std::memcpy(&value, cmdbuf + index, sizeof(T)); 375 std::memcpy(&value, cmdbuf + index, sizeof(T));
@@ -393,6 +401,16 @@ inline u64 RequestParser::Pop() {
393} 401}
394 402
395template <> 403template <>
404inline s8 RequestParser::Pop() {
405 return static_cast<s8>(Pop<u8>());
406}
407
408template <>
409inline s16 RequestParser::Pop() {
410 return static_cast<s16>(Pop<u16>());
411}
412
413template <>
396inline s64 RequestParser::Pop() { 414inline s64 RequestParser::Pop() {
397 return static_cast<s64>(Pop<u64>()); 415 return static_cast<s64>(Pop<u64>());
398} 416}
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..49fced7b1 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -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/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/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..4fde53033 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -67,8 +67,11 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
67 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, 67 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
68 (base + size) * PAGE_SIZE); 68 (base + size) * PAGE_SIZE);
69 69
70 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, 70 // During boot, current_page_table might not be set yet, in which case we need not flush
71 FlushMode::FlushAndInvalidate); 71 if (current_page_table) {
72 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
73 FlushMode::FlushAndInvalidate);
74 }
72 75
73 VAddr end = base + size; 76 VAddr end = base + size;
74 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", 77 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
@@ -359,13 +362,13 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
359 auto& gpu = system_instance.GPU(); 362 auto& gpu = system_instance.GPU();
360 switch (mode) { 363 switch (mode) {
361 case FlushMode::Flush: 364 case FlushMode::Flush:
362 gpu.FlushRegion(overlap_start, overlap_size); 365 gpu.FlushRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
363 break; 366 break;
364 case FlushMode::Invalidate: 367 case FlushMode::Invalidate:
365 gpu.InvalidateRegion(overlap_start, overlap_size); 368 gpu.InvalidateRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
366 break; 369 break;
367 case FlushMode::FlushAndInvalidate: 370 case FlushMode::FlushAndInvalidate:
368 gpu.FlushAndInvalidateRegion(overlap_start, overlap_size); 371 gpu.FlushAndInvalidateRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
369 break; 372 break;
370 } 373 }
371 }; 374 };
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/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/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index aae2a4019..daefa43a6 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
@@ -48,7 +49,8 @@ void KeplerMemory::ProcessData(u32 data) {
48 // We have to invalidate the destination region to evict any outdated surfaces from the cache. 49 // 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 50 // We do this before actually writing the new data because the destination address might contain
50 // a dirty surface that will have to be written back to memory. 51 // a dirty surface that will have to be written back to memory.
51 Core::System::GetInstance().GPU().InvalidateRegion(*dest_address, sizeof(u32)); 52 system.Renderer().Rasterizer().InvalidateRegion(ToCacheAddr(Memory::GetPointer(*dest_address)),
53 sizeof(u32));
52 54
53 Memory::Write32(*dest_address, data); 55 Memory::Write32(*dest_address, data);
54 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); 56 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 144e7fa82..49979694e 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -396,7 +396,10 @@ void Maxwell3D::ProcessCBData(u32 value) {
396 const auto address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); 396 const auto address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
397 ASSERT_MSG(address, "Invalid GPU address"); 397 ASSERT_MSG(address, "Invalid GPU address");
398 398
399 Memory::Write32(*address, value); 399 u8* ptr{Memory::GetPointer(*address)};
400 rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32));
401 std::memcpy(ptr, &value, sizeof(u32));
402
400 dirty_flags.OnMemoryWrite(); 403 dirty_flags.OnMemoryWrite();
401 404
402 // Increment the current buffer position. 405 // Increment the current buffer position.
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 9dfea5999..415a6319a 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 {
@@ -92,12 +93,14 @@ void MaxwellDMA::HandleCopy() {
92 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { 93 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
93 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 94 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
94 // copying. 95 // copying.
95 Core::System::GetInstance().GPU().FlushRegion(*source_cpu, src_size); 96 Core::System::GetInstance().Renderer().Rasterizer().FlushRegion(
97 ToCacheAddr(Memory::GetPointer(*source_cpu)), src_size);
96 98
97 // We have to invalidate the destination region to evict any outdated surfaces from the 99 // 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 100 // 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. 101 // might contain a dirty surface that will have to be written back to memory.
100 Core::System::GetInstance().GPU().InvalidateRegion(*dest_cpu, dst_size); 102 Core::System::GetInstance().Renderer().Rasterizer().InvalidateRegion(
103 ToCacheAddr(Memory::GetPointer(*dest_cpu)), dst_size);
101 }; 104 };
102 105
103 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 106 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
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/morton.cpp b/src/video_core/morton.cpp
index b68f4fb13..9692ce143 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -16,12 +16,12 @@ namespace VideoCore {
16using Surface::GetBytesPerPixel; 16using Surface::GetBytesPerPixel;
17using Surface::PixelFormat; 17using Surface::PixelFormat;
18 18
19using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, std::size_t, VAddr); 19using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, VAddr);
20using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>; 20using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>;
21 21
22template <bool morton_to_linear, PixelFormat format> 22template <bool morton_to_linear, PixelFormat format>
23static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, 23static 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) { 24 u32 tile_width_spacing, u8* buffer, VAddr addr) {
25 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format); 25 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
26 26
27 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual 27 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
@@ -42,142 +42,138 @@ static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth
42} 42}
43 43
44static constexpr ConversionArray morton_to_linear_fns = { 44static constexpr ConversionArray morton_to_linear_fns = {
45 // clang-format off 45 MortonCopy<true, PixelFormat::ABGR8U>,
46 MortonCopy<true, PixelFormat::ABGR8U>, 46 MortonCopy<true, PixelFormat::ABGR8S>,
47 MortonCopy<true, PixelFormat::ABGR8S>, 47 MortonCopy<true, PixelFormat::ABGR8UI>,
48 MortonCopy<true, PixelFormat::ABGR8UI>, 48 MortonCopy<true, PixelFormat::B5G6R5U>,
49 MortonCopy<true, PixelFormat::B5G6R5U>, 49 MortonCopy<true, PixelFormat::A2B10G10R10U>,
50 MortonCopy<true, PixelFormat::A2B10G10R10U>, 50 MortonCopy<true, PixelFormat::A1B5G5R5U>,
51 MortonCopy<true, PixelFormat::A1B5G5R5U>, 51 MortonCopy<true, PixelFormat::R8U>,
52 MortonCopy<true, PixelFormat::R8U>, 52 MortonCopy<true, PixelFormat::R8UI>,
53 MortonCopy<true, PixelFormat::R8UI>, 53 MortonCopy<true, PixelFormat::RGBA16F>,
54 MortonCopy<true, PixelFormat::RGBA16F>, 54 MortonCopy<true, PixelFormat::RGBA16U>,
55 MortonCopy<true, PixelFormat::RGBA16U>, 55 MortonCopy<true, PixelFormat::RGBA16UI>,
56 MortonCopy<true, PixelFormat::RGBA16UI>, 56 MortonCopy<true, PixelFormat::R11FG11FB10F>,
57 MortonCopy<true, PixelFormat::R11FG11FB10F>, 57 MortonCopy<true, PixelFormat::RGBA32UI>,
58 MortonCopy<true, PixelFormat::RGBA32UI>, 58 MortonCopy<true, PixelFormat::DXT1>,
59 MortonCopy<true, PixelFormat::DXT1>, 59 MortonCopy<true, PixelFormat::DXT23>,
60 MortonCopy<true, PixelFormat::DXT23>, 60 MortonCopy<true, PixelFormat::DXT45>,
61 MortonCopy<true, PixelFormat::DXT45>, 61 MortonCopy<true, PixelFormat::DXN1>,
62 MortonCopy<true, PixelFormat::DXN1>, 62 MortonCopy<true, PixelFormat::DXN2UNORM>,
63 MortonCopy<true, PixelFormat::DXN2UNORM>, 63 MortonCopy<true, PixelFormat::DXN2SNORM>,
64 MortonCopy<true, PixelFormat::DXN2SNORM>, 64 MortonCopy<true, PixelFormat::BC7U>,
65 MortonCopy<true, PixelFormat::BC7U>, 65 MortonCopy<true, PixelFormat::BC6H_UF16>,
66 MortonCopy<true, PixelFormat::BC6H_UF16>, 66 MortonCopy<true, PixelFormat::BC6H_SF16>,
67 MortonCopy<true, PixelFormat::BC6H_SF16>, 67 MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
68 MortonCopy<true, PixelFormat::ASTC_2D_4X4>, 68 MortonCopy<true, PixelFormat::BGRA8>,
69 MortonCopy<true, PixelFormat::BGRA8>, 69 MortonCopy<true, PixelFormat::RGBA32F>,
70 MortonCopy<true, PixelFormat::RGBA32F>, 70 MortonCopy<true, PixelFormat::RG32F>,
71 MortonCopy<true, PixelFormat::RG32F>, 71 MortonCopy<true, PixelFormat::R32F>,
72 MortonCopy<true, PixelFormat::R32F>, 72 MortonCopy<true, PixelFormat::R16F>,
73 MortonCopy<true, PixelFormat::R16F>, 73 MortonCopy<true, PixelFormat::R16U>,
74 MortonCopy<true, PixelFormat::R16U>, 74 MortonCopy<true, PixelFormat::R16S>,
75 MortonCopy<true, PixelFormat::R16S>, 75 MortonCopy<true, PixelFormat::R16UI>,
76 MortonCopy<true, PixelFormat::R16UI>, 76 MortonCopy<true, PixelFormat::R16I>,
77 MortonCopy<true, PixelFormat::R16I>, 77 MortonCopy<true, PixelFormat::RG16>,
78 MortonCopy<true, PixelFormat::RG16>, 78 MortonCopy<true, PixelFormat::RG16F>,
79 MortonCopy<true, PixelFormat::RG16F>, 79 MortonCopy<true, PixelFormat::RG16UI>,
80 MortonCopy<true, PixelFormat::RG16UI>, 80 MortonCopy<true, PixelFormat::RG16I>,
81 MortonCopy<true, PixelFormat::RG16I>, 81 MortonCopy<true, PixelFormat::RG16S>,
82 MortonCopy<true, PixelFormat::RG16S>, 82 MortonCopy<true, PixelFormat::RGB32F>,
83 MortonCopy<true, PixelFormat::RGB32F>, 83 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
84 MortonCopy<true, PixelFormat::RGBA8_SRGB>, 84 MortonCopy<true, PixelFormat::RG8U>,
85 MortonCopy<true, PixelFormat::RG8U>, 85 MortonCopy<true, PixelFormat::RG8S>,
86 MortonCopy<true, PixelFormat::RG8S>, 86 MortonCopy<true, PixelFormat::RG32UI>,
87 MortonCopy<true, PixelFormat::RG32UI>, 87 MortonCopy<true, PixelFormat::R32UI>,
88 MortonCopy<true, PixelFormat::R32UI>, 88 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
89 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 89 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
90 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 90 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
91 MortonCopy<true, PixelFormat::ASTC_2D_5X4>, 91 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
92 MortonCopy<true, PixelFormat::BGRA8_SRGB>, 92 MortonCopy<true, PixelFormat::DXT1_SRGB>,
93 MortonCopy<true, PixelFormat::DXT1_SRGB>, 93 MortonCopy<true, PixelFormat::DXT23_SRGB>,
94 MortonCopy<true, PixelFormat::DXT23_SRGB>, 94 MortonCopy<true, PixelFormat::DXT45_SRGB>,
95 MortonCopy<true, PixelFormat::DXT45_SRGB>, 95 MortonCopy<true, PixelFormat::BC7U_SRGB>,
96 MortonCopy<true, PixelFormat::BC7U_SRGB>, 96 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
97 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>, 97 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
98 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, 98 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
99 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, 99 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
100 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, 100 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
101 MortonCopy<true, PixelFormat::ASTC_2D_5X5>, 101 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
102 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>, 102 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
103 MortonCopy<true, PixelFormat::ASTC_2D_10X8>, 103 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
104 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>, 104 MortonCopy<true, PixelFormat::Z32F>,
105 MortonCopy<true, PixelFormat::Z32F>, 105 MortonCopy<true, PixelFormat::Z16>,
106 MortonCopy<true, PixelFormat::Z16>, 106 MortonCopy<true, PixelFormat::Z24S8>,
107 MortonCopy<true, PixelFormat::Z24S8>, 107 MortonCopy<true, PixelFormat::S8Z24>,
108 MortonCopy<true, PixelFormat::S8Z24>, 108 MortonCopy<true, PixelFormat::Z32FS8>,
109 MortonCopy<true, PixelFormat::Z32FS8>,
110 // clang-format on
111}; 109};
112 110
113static constexpr ConversionArray linear_to_morton_fns = { 111static constexpr ConversionArray linear_to_morton_fns = {
114 // clang-format off 112 MortonCopy<false, PixelFormat::ABGR8U>,
115 MortonCopy<false, PixelFormat::ABGR8U>, 113 MortonCopy<false, PixelFormat::ABGR8S>,
116 MortonCopy<false, PixelFormat::ABGR8S>, 114 MortonCopy<false, PixelFormat::ABGR8UI>,
117 MortonCopy<false, PixelFormat::ABGR8UI>, 115 MortonCopy<false, PixelFormat::B5G6R5U>,
118 MortonCopy<false, PixelFormat::B5G6R5U>, 116 MortonCopy<false, PixelFormat::A2B10G10R10U>,
119 MortonCopy<false, PixelFormat::A2B10G10R10U>, 117 MortonCopy<false, PixelFormat::A1B5G5R5U>,
120 MortonCopy<false, PixelFormat::A1B5G5R5U>, 118 MortonCopy<false, PixelFormat::R8U>,
121 MortonCopy<false, PixelFormat::R8U>, 119 MortonCopy<false, PixelFormat::R8UI>,
122 MortonCopy<false, PixelFormat::R8UI>, 120 MortonCopy<false, PixelFormat::RGBA16F>,
123 MortonCopy<false, PixelFormat::RGBA16F>, 121 MortonCopy<false, PixelFormat::RGBA16U>,
124 MortonCopy<false, PixelFormat::RGBA16U>, 122 MortonCopy<false, PixelFormat::RGBA16UI>,
125 MortonCopy<false, PixelFormat::RGBA16UI>, 123 MortonCopy<false, PixelFormat::R11FG11FB10F>,
126 MortonCopy<false, PixelFormat::R11FG11FB10F>, 124 MortonCopy<false, PixelFormat::RGBA32UI>,
127 MortonCopy<false, PixelFormat::RGBA32UI>, 125 MortonCopy<false, PixelFormat::DXT1>,
128 MortonCopy<false, PixelFormat::DXT1>, 126 MortonCopy<false, PixelFormat::DXT23>,
129 MortonCopy<false, PixelFormat::DXT23>, 127 MortonCopy<false, PixelFormat::DXT45>,
130 MortonCopy<false, PixelFormat::DXT45>, 128 MortonCopy<false, PixelFormat::DXN1>,
131 MortonCopy<false, PixelFormat::DXN1>, 129 MortonCopy<false, PixelFormat::DXN2UNORM>,
132 MortonCopy<false, PixelFormat::DXN2UNORM>, 130 MortonCopy<false, PixelFormat::DXN2SNORM>,
133 MortonCopy<false, PixelFormat::DXN2SNORM>, 131 MortonCopy<false, PixelFormat::BC7U>,
134 MortonCopy<false, PixelFormat::BC7U>, 132 MortonCopy<false, PixelFormat::BC6H_UF16>,
135 MortonCopy<false, PixelFormat::BC6H_UF16>, 133 MortonCopy<false, PixelFormat::BC6H_SF16>,
136 MortonCopy<false, PixelFormat::BC6H_SF16>, 134 // TODO(Subv): Swizzling ASTC formats are not supported
137 // TODO(Subv): Swizzling ASTC formats are not supported 135 nullptr,
138 nullptr, 136 MortonCopy<false, PixelFormat::BGRA8>,
139 MortonCopy<false, PixelFormat::BGRA8>, 137 MortonCopy<false, PixelFormat::RGBA32F>,
140 MortonCopy<false, PixelFormat::RGBA32F>, 138 MortonCopy<false, PixelFormat::RG32F>,
141 MortonCopy<false, PixelFormat::RG32F>, 139 MortonCopy<false, PixelFormat::R32F>,
142 MortonCopy<false, PixelFormat::R32F>, 140 MortonCopy<false, PixelFormat::R16F>,
143 MortonCopy<false, PixelFormat::R16F>, 141 MortonCopy<false, PixelFormat::R16U>,
144 MortonCopy<false, PixelFormat::R16U>, 142 MortonCopy<false, PixelFormat::R16S>,
145 MortonCopy<false, PixelFormat::R16S>, 143 MortonCopy<false, PixelFormat::R16UI>,
146 MortonCopy<false, PixelFormat::R16UI>, 144 MortonCopy<false, PixelFormat::R16I>,
147 MortonCopy<false, PixelFormat::R16I>, 145 MortonCopy<false, PixelFormat::RG16>,
148 MortonCopy<false, PixelFormat::RG16>, 146 MortonCopy<false, PixelFormat::RG16F>,
149 MortonCopy<false, PixelFormat::RG16F>, 147 MortonCopy<false, PixelFormat::RG16UI>,
150 MortonCopy<false, PixelFormat::RG16UI>, 148 MortonCopy<false, PixelFormat::RG16I>,
151 MortonCopy<false, PixelFormat::RG16I>, 149 MortonCopy<false, PixelFormat::RG16S>,
152 MortonCopy<false, PixelFormat::RG16S>, 150 MortonCopy<false, PixelFormat::RGB32F>,
153 MortonCopy<false, PixelFormat::RGB32F>, 151 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
154 MortonCopy<false, PixelFormat::RGBA8_SRGB>, 152 MortonCopy<false, PixelFormat::RG8U>,
155 MortonCopy<false, PixelFormat::RG8U>, 153 MortonCopy<false, PixelFormat::RG8S>,
156 MortonCopy<false, PixelFormat::RG8S>, 154 MortonCopy<false, PixelFormat::RG32UI>,
157 MortonCopy<false, PixelFormat::RG32UI>, 155 MortonCopy<false, PixelFormat::R32UI>,
158 MortonCopy<false, PixelFormat::R32UI>, 156 nullptr,
159 nullptr, 157 nullptr,
160 nullptr, 158 nullptr,
161 nullptr, 159 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
162 MortonCopy<false, PixelFormat::BGRA8_SRGB>, 160 MortonCopy<false, PixelFormat::DXT1_SRGB>,
163 MortonCopy<false, PixelFormat::DXT1_SRGB>, 161 MortonCopy<false, PixelFormat::DXT23_SRGB>,
164 MortonCopy<false, PixelFormat::DXT23_SRGB>, 162 MortonCopy<false, PixelFormat::DXT45_SRGB>,
165 MortonCopy<false, PixelFormat::DXT45_SRGB>, 163 MortonCopy<false, PixelFormat::BC7U_SRGB>,
166 MortonCopy<false, PixelFormat::BC7U_SRGB>, 164 nullptr,
167 nullptr, 165 nullptr,
168 nullptr, 166 nullptr,
169 nullptr, 167 nullptr,
170 nullptr, 168 nullptr,
171 nullptr, 169 nullptr,
172 nullptr, 170 nullptr,
173 nullptr, 171 nullptr,
174 nullptr, 172 MortonCopy<false, PixelFormat::Z32F>,
175 MortonCopy<false, PixelFormat::Z32F>, 173 MortonCopy<false, PixelFormat::Z16>,
176 MortonCopy<false, PixelFormat::Z16>, 174 MortonCopy<false, PixelFormat::Z24S8>,
177 MortonCopy<false, PixelFormat::Z24S8>, 175 MortonCopy<false, PixelFormat::S8Z24>,
178 MortonCopy<false, PixelFormat::S8Z24>, 176 MortonCopy<false, PixelFormat::Z32FS8>,
179 MortonCopy<false, PixelFormat::Z32FS8>,
180 // clang-format on
181}; 177};
182 178
183static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) { 179static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) {
@@ -191,45 +187,6 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor
191 return morton_to_linear_fns[static_cast<std::size_t>(format)]; 187 return morton_to_linear_fns[static_cast<std::size_t>(format)];
192} 188}
193 189
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) { 190static u32 MortonInterleave128(u32 x, u32 y) {
234 // 128x128 Z-Order coordinate from 2D coordinates 191 // 128x128 Z-Order coordinate from 2D coordinates
235 static constexpr u32 xlut[] = { 192 static constexpr u32 xlut[] = {
@@ -325,14 +282,14 @@ static u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
325 282
326void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride, 283void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride,
327 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing, 284 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
328 u8* buffer, std::size_t buffer_size, VAddr addr) { 285 u8* buffer, VAddr addr) {
329
330 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth, 286 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth,
331 tile_width_spacing, buffer, buffer_size, addr); 287 tile_width_spacing, buffer, addr);
332} 288}
333 289
334void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 linear_bytes_per_pixel, 290void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
335 u8* morton_data, u8* linear_data, bool morton_to_linear) { 291 u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data) {
292 const bool morton_to_linear = mode == MortonSwizzleMode::MortonToLinear;
336 u8* data_ptrs[2]; 293 u8* data_ptrs[2];
337 for (u32 y = 0; y < height; ++y) { 294 for (u32 y = 0; y < height; ++y) {
338 for (u32 x = 0; x < width; ++x) { 295 for (u32 x = 0; x < width; ++x) {
diff --git a/src/video_core/morton.h b/src/video_core/morton.h
index 065f59ce3..b565204b5 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, VAddr 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..a4eea61a6 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -13,6 +13,11 @@
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
@@ -26,11 +31,12 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size
26 // TODO: Figure out which size is the best for given games. 31 // TODO: Figure out which size is the best for given games.
27 cache &= size >= 2048; 32 cache &= size >= 2048;
28 33
34 const auto& host_ptr{Memory::GetPointer(*cpu_addr)};
29 if (cache) { 35 if (cache) {
30 auto entry = TryGet(*cpu_addr); 36 auto entry = TryGet(host_ptr);
31 if (entry) { 37 if (entry) {
32 if (entry->size >= size && entry->alignment == alignment) { 38 if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
33 return entry->offset; 39 return entry->GetOffset();
34 } 40 }
35 Unregister(entry); 41 Unregister(entry);
36 } 42 }
@@ -39,17 +45,17 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size
39 AlignBuffer(alignment); 45 AlignBuffer(alignment);
40 const GLintptr uploaded_offset = buffer_offset; 46 const GLintptr uploaded_offset = buffer_offset;
41 47
42 Memory::ReadBlock(*cpu_addr, buffer_ptr, size); 48 if (!host_ptr) {
49 return uploaded_offset;
50 }
43 51
52 std::memcpy(buffer_ptr, host_ptr, size);
44 buffer_ptr += size; 53 buffer_ptr += size;
45 buffer_offset += size; 54 buffer_offset += size;
46 55
47 if (cache) { 56 if (cache) {
48 auto entry = std::make_shared<CachedBufferEntry>(); 57 auto entry = std::make_shared<CachedBufferEntry>(*cpu_addr, size, uploaded_offset,
49 entry->offset = uploaded_offset; 58 alignment, host_ptr);
50 entry->size = size;
51 entry->alignment = alignment;
52 entry->addr = *cpu_addr;
53 Register(entry); 59 Register(entry);
54 } 60 }
55 61
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..a2c509c24 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -15,12 +15,13 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{size} { 18CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr)
19 : cpu_addr{cpu_addr}, size{size}, RasterizerCacheObject{host_ptr} {
19 buffer.Create(); 20 buffer.Create();
20 // Bind and unbind the buffer so it gets allocated by the driver 21 // Bind and unbind the buffer so it gets allocated by the driver
21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 23 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
23 LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory"); 24 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory");
24} 25}
25 26
26void CachedGlobalRegion::Reload(u32 size_) { 27void CachedGlobalRegion::Reload(u32 size_) {
@@ -35,7 +36,7 @@ void CachedGlobalRegion::Reload(u32 size_) {
35 36
36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer 37 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer
37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 38 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, Memory::GetPointer(addr), GL_DYNAMIC_DRAW); 39 glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW);
39} 40}
40 41
41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const { 42GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const {
@@ -46,19 +47,19 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32
46 return search->second; 47 return search->second;
47} 48}
48 49
49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size) { 50GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size, u8* host_ptr) {
50 GlobalRegion region{TryGetReservedGlobalRegion(addr, size)}; 51 GlobalRegion region{TryGetReservedGlobalRegion(addr, 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 region = std::make_shared<CachedGlobalRegion>(addr, size, host_ptr);
54 ReserveGlobalRegion(region); 55 ReserveGlobalRegion(region);
55 } 56 }
56 region->Reload(size); 57 region->Reload(size);
57 return region; 58 return region;
58} 59}
59 60
60void GlobalRegionCacheOpenGL::ReserveGlobalRegion(const GlobalRegion& region) { 61void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
61 reserve[region->GetAddr()] = region; 62 reserve.insert_or_assign(region->GetCpuAddr(), std::move(region));
62} 63}
63 64
64GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) 65GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
@@ -80,11 +81,12 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
80 ASSERT(actual_addr); 81 ASSERT(actual_addr);
81 82
82 // Look up global region in the cache based on address 83 // Look up global region in the cache based on address
83 GlobalRegion region = TryGet(*actual_addr); 84 const auto& host_ptr{Memory::GetPointer(*actual_addr)};
85 GlobalRegion region{TryGet(host_ptr)};
84 86
85 if (!region) { 87 if (!region) {
86 // No global region found - create a new one 88 // No global region found - create a new one
87 region = GetUncachedGlobalRegion(*actual_addr, size); 89 region = GetUncachedGlobalRegion(*actual_addr, size, host_ptr);
88 Register(region); 90 Register(region);
89 } 91 }
90 92
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h
index 37830bb7c..e497a0619 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
@@ -69,8 +66,8 @@ public:
69 66
70private: 67private:
71 GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const; 68 GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const;
72 GlobalRegion GetUncachedGlobalRegion(VAddr addr, u32 size); 69 GlobalRegion GetUncachedGlobalRegion(VAddr 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<VAddr, GlobalRegion> reserve;
76}; 73};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 824863561..bb6de5477 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,12 +747,12 @@ 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);
751 res_cache.FlushRegion(addr, size); 752 res_cache.FlushRegion(addr, size);
752} 753}
753 754
754void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 755void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
755 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 756 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
756 res_cache.InvalidateRegion(addr, size); 757 res_cache.InvalidateRegion(addr, size);
757 shader_cache.InvalidateRegion(addr, size); 758 shader_cache.InvalidateRegion(addr, size);
@@ -759,7 +760,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
759 buffer_cache.InvalidateRegion(addr, size); 760 buffer_cache.InvalidateRegion(addr, size);
760} 761}
761 762
762void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 763void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
763 FlushRegion(addr, size); 764 FlushRegion(addr, size);
764 InvalidateRegion(addr, size); 765 InvalidateRegion(addr, size);
765} 766}
@@ -781,7 +782,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
781 782
782 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 783 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
783 784
784 const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)}; 785 const auto& surface{res_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))};
785 if (!surface) { 786 if (!surface) {
786 return {}; 787 return {};
787 } 788 }
@@ -892,7 +893,7 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
892 const Shader& shader, GLuint program_handle, 893 const Shader& shader, GLuint program_handle,
893 BaseBindings base_bindings) { 894 BaseBindings base_bindings) {
894 MICROPROFILE_SCOPE(OpenGL_UBO); 895 MICROPROFILE_SCOPE(OpenGL_UBO);
895 const auto& gpu = Core::System::GetInstance().GPU(); 896 const auto& gpu = system.GPU();
896 const auto& maxwell3d = gpu.Maxwell3D(); 897 const auto& maxwell3d = gpu.Maxwell3D();
897 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; 898 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
898 const auto& entries = shader->GetShaderEntries().const_buffers; 899 const auto& entries = shader->GetShaderEntries().const_buffers;
@@ -971,7 +972,7 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade
971void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader, 972void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
972 GLuint program_handle, BaseBindings base_bindings) { 973 GLuint program_handle, BaseBindings base_bindings) {
973 MICROPROFILE_SCOPE(OpenGL_Texture); 974 MICROPROFILE_SCOPE(OpenGL_Texture);
974 const auto& gpu = Core::System::GetInstance().GPU(); 975 const auto& gpu = system.GPU();
975 const auto& maxwell3d = gpu.Maxwell3D(); 976 const auto& maxwell3d = gpu.Maxwell3D();
976 const auto& entries = shader->GetShaderEntries().samplers; 977 const auto& entries = shader->GetShaderEntries().samplers;
977 978
@@ -998,7 +999,7 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s
998} 999}
999 1000
1000void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { 1001void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
1001 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1002 const auto& regs = system.GPU().Maxwell3D().regs;
1002 const bool geometry_shaders_enabled = 1003 const bool geometry_shaders_enabled =
1003 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); 1004 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
1004 const std::size_t viewport_count = 1005 const std::size_t viewport_count =
@@ -1021,7 +1022,7 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
1021void RasterizerOpenGL::SyncClipEnabled( 1022void RasterizerOpenGL::SyncClipEnabled(
1022 const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) { 1023 const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) {
1023 1024
1024 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1025 const auto& regs = system.GPU().Maxwell3D().regs;
1025 const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{ 1026 const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{
1026 regs.clip_distance_enabled.c0 != 0, regs.clip_distance_enabled.c1 != 0, 1027 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, 1028 regs.clip_distance_enabled.c2 != 0, regs.clip_distance_enabled.c3 != 0,
@@ -1038,7 +1039,7 @@ void RasterizerOpenGL::SyncClipCoef() {
1038} 1039}
1039 1040
1040void RasterizerOpenGL::SyncCullMode() { 1041void RasterizerOpenGL::SyncCullMode() {
1041 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1042 const auto& regs = system.GPU().Maxwell3D().regs;
1042 1043
1043 state.cull.enabled = regs.cull.enabled != 0; 1044 state.cull.enabled = regs.cull.enabled != 0;
1044 1045
@@ -1062,14 +1063,14 @@ void RasterizerOpenGL::SyncCullMode() {
1062} 1063}
1063 1064
1064void RasterizerOpenGL::SyncPrimitiveRestart() { 1065void RasterizerOpenGL::SyncPrimitiveRestart() {
1065 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1066 const auto& regs = system.GPU().Maxwell3D().regs;
1066 1067
1067 state.primitive_restart.enabled = regs.primitive_restart.enabled; 1068 state.primitive_restart.enabled = regs.primitive_restart.enabled;
1068 state.primitive_restart.index = regs.primitive_restart.index; 1069 state.primitive_restart.index = regs.primitive_restart.index;
1069} 1070}
1070 1071
1071void RasterizerOpenGL::SyncDepthTestState() { 1072void RasterizerOpenGL::SyncDepthTestState() {
1072 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1073 const auto& regs = system.GPU().Maxwell3D().regs;
1073 1074
1074 state.depth.test_enabled = regs.depth_test_enable != 0; 1075 state.depth.test_enabled = regs.depth_test_enable != 0;
1075 state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE; 1076 state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
@@ -1081,7 +1082,7 @@ void RasterizerOpenGL::SyncDepthTestState() {
1081} 1082}
1082 1083
1083void RasterizerOpenGL::SyncStencilTestState() { 1084void RasterizerOpenGL::SyncStencilTestState() {
1084 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1085 const auto& regs = system.GPU().Maxwell3D().regs;
1085 state.stencil.test_enabled = regs.stencil_enable != 0; 1086 state.stencil.test_enabled = regs.stencil_enable != 0;
1086 1087
1087 if (!regs.stencil_enable) { 1088 if (!regs.stencil_enable) {
@@ -1115,7 +1116,7 @@ void RasterizerOpenGL::SyncStencilTestState() {
1115} 1116}
1116 1117
1117void RasterizerOpenGL::SyncColorMask() { 1118void RasterizerOpenGL::SyncColorMask() {
1118 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1119 const auto& regs = system.GPU().Maxwell3D().regs;
1119 const std::size_t count = 1120 const std::size_t count =
1120 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1; 1121 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
1121 for (std::size_t i = 0; i < count; i++) { 1122 for (std::size_t i = 0; i < count; i++) {
@@ -1129,18 +1130,18 @@ void RasterizerOpenGL::SyncColorMask() {
1129} 1130}
1130 1131
1131void RasterizerOpenGL::SyncMultiSampleState() { 1132void RasterizerOpenGL::SyncMultiSampleState() {
1132 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1133 const auto& regs = system.GPU().Maxwell3D().regs;
1133 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0; 1134 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; 1135 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
1135} 1136}
1136 1137
1137void RasterizerOpenGL::SyncFragmentColorClampState() { 1138void RasterizerOpenGL::SyncFragmentColorClampState() {
1138 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1139 const auto& regs = system.GPU().Maxwell3D().regs;
1139 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0; 1140 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
1140} 1141}
1141 1142
1142void RasterizerOpenGL::SyncBlendState() { 1143void RasterizerOpenGL::SyncBlendState() {
1143 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1144 const auto& regs = system.GPU().Maxwell3D().regs;
1144 1145
1145 state.blend_color.red = regs.blend_color.r; 1146 state.blend_color.red = regs.blend_color.r;
1146 state.blend_color.green = regs.blend_color.g; 1147 state.blend_color.green = regs.blend_color.g;
@@ -1182,7 +1183,7 @@ void RasterizerOpenGL::SyncBlendState() {
1182} 1183}
1183 1184
1184void RasterizerOpenGL::SyncLogicOpState() { 1185void RasterizerOpenGL::SyncLogicOpState() {
1185 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1186 const auto& regs = system.GPU().Maxwell3D().regs;
1186 1187
1187 state.logic_op.enabled = regs.logic_op.enable != 0; 1188 state.logic_op.enabled = regs.logic_op.enable != 0;
1188 1189
@@ -1196,7 +1197,7 @@ void RasterizerOpenGL::SyncLogicOpState() {
1196} 1197}
1197 1198
1198void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) { 1199void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
1199 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1200 const auto& regs = system.GPU().Maxwell3D().regs;
1200 const bool geometry_shaders_enabled = 1201 const bool geometry_shaders_enabled =
1201 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); 1202 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
1202 const std::size_t viewport_count = 1203 const std::size_t viewport_count =
@@ -1218,17 +1219,17 @@ void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
1218} 1219}
1219 1220
1220void RasterizerOpenGL::SyncTransformFeedback() { 1221void RasterizerOpenGL::SyncTransformFeedback() {
1221 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1222 const auto& regs = system.GPU().Maxwell3D().regs;
1222 UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented"); 1223 UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented");
1223} 1224}
1224 1225
1225void RasterizerOpenGL::SyncPointState() { 1226void RasterizerOpenGL::SyncPointState() {
1226 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1227 const auto& regs = system.GPU().Maxwell3D().regs;
1227 state.point.size = regs.point_size; 1228 state.point.size = regs.point_size;
1228} 1229}
1229 1230
1230void RasterizerOpenGL::SyncPolygonOffset() { 1231void RasterizerOpenGL::SyncPolygonOffset() {
1231 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1232 const auto& regs = system.GPU().Maxwell3D().regs;
1232 state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0; 1233 state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
1233 state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0; 1234 state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0;
1234 state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0; 1235 state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0;
@@ -1238,7 +1239,7 @@ void RasterizerOpenGL::SyncPolygonOffset() {
1238} 1239}
1239 1240
1240void RasterizerOpenGL::CheckAlphaTests() { 1241void RasterizerOpenGL::CheckAlphaTests() {
1241 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1242 const auto& regs = system.GPU().Maxwell3D().regs;
1242 UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1, 1243 UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1,
1243 "Alpha Testing is enabled with more than one rendertarget"); 1244 "Alpha Testing is enabled with more than one rendertarget");
1244} 1245}
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..451de00e8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -61,6 +61,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
61 61
62 addr = cpu_addr ? *cpu_addr : 0; 62 addr = cpu_addr ? *cpu_addr : 0;
63 gpu_addr = gpu_addr_; 63 gpu_addr = gpu_addr_;
64 host_ptr = Memory::GetPointer(addr);
64 size_in_bytes = SizeInBytesRaw(); 65 size_in_bytes = SizeInBytesRaw();
65 66
66 if (IsPixelFormatASTC(pixel_format)) { 67 if (IsPixelFormatASTC(pixel_format)) {
@@ -446,7 +447,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
446 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 447 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
447 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 448 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
448 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing, 449 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing,
449 gl_buffer.data() + offset_gl, gl_size, params.addr + offset); 450 gl_buffer.data() + offset_gl, params.addr + offset);
450 offset += layer_size; 451 offset += layer_size;
451 offset_gl += gl_size; 452 offset_gl += gl_size;
452 } 453 }
@@ -455,7 +456,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
455 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 456 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
456 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 457 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
457 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing, 458 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing,
458 gl_buffer.data(), gl_buffer.size(), params.addr + offset); 459 gl_buffer.data(), params.addr + offset);
459 } 460 }
460} 461}
461 462
@@ -563,8 +564,8 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
563} 564}
564 565
565CachedSurface::CachedSurface(const SurfaceParams& params) 566CachedSurface::CachedSurface(const SurfaceParams& params)
566 : params(params), gl_target(SurfaceTargetToGL(params.target)), 567 : params{params}, gl_target{SurfaceTargetToGL(params.target)},
567 cached_size_in_bytes(params.size_in_bytes) { 568 cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} {
568 texture.Create(gl_target); 569 texture.Create(gl_target);
569 570
570 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0) 571 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0)
@@ -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);
@@ -932,7 +930,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
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
@@ -986,7 +984,7 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
986 for (u32 layer = 0; layer < dst_params.depth; layer++) { 984 for (u32 layer = 0; layer < dst_params.depth; layer++) {
987 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { 985 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
988 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap); 986 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap);
989 const Surface& copy = TryGet(sub_address); 987 const Surface& copy = TryGet(Memory::GetPointer(sub_address));
990 if (!copy) 988 if (!copy)
991 continue; 989 continue;
992 const auto& src_params{copy->GetSurfaceParams()}; 990 const auto& src_params{copy->GetSurfaceParams()};
@@ -1163,7 +1161,8 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1163 const auto& dst_params{dst_surface->GetSurfaceParams()}; 1161 const auto& dst_params{dst_surface->GetSurfaceParams()};
1164 1162
1165 // Flush enough memory for both the source and destination surface 1163 // Flush enough memory for both the source and destination surface
1166 FlushRegion(src_params.addr, std::max(src_params.MemorySize(), dst_params.MemorySize())); 1164 FlushRegion(ToCacheAddr(src_params.host_ptr),
1165 std::max(src_params.MemorySize(), dst_params.MemorySize()));
1167 1166
1168 LoadSurface(dst_surface); 1167 LoadSurface(dst_surface);
1169} 1168}
@@ -1215,8 +1214,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1215 return new_surface; 1214 return new_surface;
1216} 1215}
1217 1216
1218Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { 1217Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(const u8* host_ptr) const {
1219 return TryGet(addr); 1218 return TryGet(host_ptr);
1220} 1219}
1221 1220
1222void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) { 1221void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) {
@@ -1267,7 +1266,7 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1267 src_params.height == dst_params.MipHeight(*level) && 1266 src_params.height == dst_params.MipHeight(*level) &&
1268 src_params.block_height >= dst_params.MipBlockHeight(*level)) { 1267 src_params.block_height >= dst_params.MipBlockHeight(*level)) {
1269 const std::optional<u32> slot = 1268 const std::optional<u32> slot =
1270 TryFindBestLayer(render_surface->GetAddr(), dst_params, *level); 1269 TryFindBestLayer(render_surface->GetCpuAddr(), dst_params, *level);
1271 if (slot.has_value()) { 1270 if (slot.has_value()) {
1272 glCopyImageSubData(render_surface->Texture().handle, 1271 glCopyImageSubData(render_surface->Texture().handle,
1273 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0, 1272 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0,
@@ -1283,8 +1282,8 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1283} 1282}
1284 1283
1285static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) { 1284static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) {
1286 const VAddr bound1 = blitted_surface->GetAddr() + blitted_surface->GetMemorySize(); 1285 const VAddr bound1 = blitted_surface->GetCpuAddr() + blitted_surface->GetMemorySize();
1287 const VAddr bound2 = render_surface->GetAddr() + render_surface->GetMemorySize(); 1286 const VAddr bound2 = render_surface->GetCpuAddr() + render_surface->GetMemorySize();
1288 if (bound2 > bound1) 1287 if (bound2 > bound1)
1289 return true; 1288 return true;
1290 const auto& dst_params = blitted_surface->GetSurfaceParams(); 1289 const auto& dst_params = blitted_surface->GetSurfaceParams();
@@ -1327,7 +1326,8 @@ void RasterizerCacheOpenGL::SignalPreDrawCall() {
1327void RasterizerCacheOpenGL::SignalPostDrawCall() { 1326void RasterizerCacheOpenGL::SignalPostDrawCall() {
1328 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) { 1327 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) {
1329 if (current_color_buffers[i] != nullptr) { 1328 if (current_color_buffers[i] != nullptr) {
1330 Surface intersect = CollideOnReinterpretedSurface(current_color_buffers[i]->GetAddr()); 1329 Surface intersect =
1330 CollideOnReinterpretedSurface(current_color_buffers[i]->GetCacheAddr());
1331 if (intersect != nullptr) { 1331 if (intersect != nullptr) {
1332 PartialReinterpretSurface(current_color_buffers[i], intersect); 1332 PartialReinterpretSurface(current_color_buffers[i], intersect);
1333 texception = true; 1333 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..b3afad139 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -297,6 +297,7 @@ struct SurfaceParams {
297 bool srgb_conversion; 297 bool srgb_conversion;
298 // Parameters used for caching 298 // Parameters used for caching
299 VAddr addr; 299 VAddr addr;
300 u8* host_ptr;
300 Tegra::GPUVAddr gpu_addr; 301 Tegra::GPUVAddr gpu_addr;
301 std::size_t size_in_bytes; 302 std::size_t size_in_bytes;
302 std::size_t size_in_bytes_gl; 303 std::size_t size_in_bytes_gl;
@@ -345,9 +346,9 @@ class RasterizerOpenGL;
345 346
346class CachedSurface final : public RasterizerCacheObject { 347class CachedSurface final : public RasterizerCacheObject {
347public: 348public:
348 CachedSurface(const SurfaceParams& params); 349 explicit CachedSurface(const SurfaceParams& params);
349 350
350 VAddr GetAddr() const override { 351 VAddr GetCpuAddr() const override {
351 return params.addr; 352 return params.addr;
352 } 353 }
353 354
@@ -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..60a04e146 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -42,9 +42,9 @@ VAddr GetShaderAddress(Maxwell::ShaderProgram program) {
42} 42}
43 43
44/// Gets the shader program code from memory for the specified address 44/// Gets the shader program code from memory for the specified address
45ProgramCode GetShaderCode(VAddr addr) { 45ProgramCode GetShaderCode(const u8* host_ptr) {
46 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); 46 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
47 Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64)); 47 std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64));
48 return program_code; 48 return program_code;
49} 49}
50 50
@@ -214,12 +214,13 @@ std::set<GLenum> GetSupportedFormats() {
214 214
215} // namespace 215} // namespace
216 216
217CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 217CachedShader::CachedShader(VAddr guest_addr, u64 unique_identifier,
218 ShaderDiskCacheOpenGL& disk_cache, 218 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
219 const PrecompiledPrograms& precompiled_programs, 219 const PrecompiledPrograms& precompiled_programs,
220 ProgramCode&& program_code, ProgramCode&& program_code_b) 220 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
221 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 221 : host_ptr{host_ptr}, guest_addr{guest_addr}, unique_identifier{unique_identifier},
222 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 222 program_type{program_type}, disk_cache{disk_cache},
223 precompiled_programs{precompiled_programs}, RasterizerCacheObject{host_ptr} {
223 224
224 const std::size_t code_size = CalculateProgramSize(program_code); 225 const std::size_t code_size = CalculateProgramSize(program_code);
225 const std::size_t code_size_b = 226 const std::size_t code_size_b =
@@ -243,12 +244,13 @@ CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderPro
243 disk_cache.SaveRaw(raw); 244 disk_cache.SaveRaw(raw);
244} 245}
245 246
246CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 247CachedShader::CachedShader(VAddr guest_addr, u64 unique_identifier,
247 ShaderDiskCacheOpenGL& disk_cache, 248 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
248 const PrecompiledPrograms& precompiled_programs, 249 const PrecompiledPrograms& precompiled_programs,
249 GLShader::ProgramResult result) 250 GLShader::ProgramResult result, u8* host_ptr)
250 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 251 : guest_addr{guest_addr}, unique_identifier{unique_identifier}, program_type{program_type},
251 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 252 disk_cache{disk_cache}, precompiled_programs{precompiled_programs}, RasterizerCacheObject{
253 host_ptr} {
252 254
253 code = std::move(result.first); 255 code = std::move(result.first);
254 entries = result.second; 256 entries = result.second;
@@ -271,7 +273,7 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive
271 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 273 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
272 } 274 }
273 275
274 LabelGLObject(GL_PROGRAM, program->handle, addr); 276 LabelGLObject(GL_PROGRAM, program->handle, guest_addr);
275 } 277 }
276 278
277 handle = program->handle; 279 handle = program->handle;
@@ -323,7 +325,7 @@ GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBind
323 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 325 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
324 } 326 }
325 327
326 LabelGLObject(GL_PROGRAM, target_program->handle, addr, debug_name); 328 LabelGLObject(GL_PROGRAM, target_program->handle, guest_addr, debug_name);
327 329
328 return target_program->handle; 330 return target_program->handle;
329}; 331};
@@ -489,14 +491,17 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
489 const VAddr program_addr{GetShaderAddress(program)}; 491 const VAddr program_addr{GetShaderAddress(program)};
490 492
491 // Look up shader in the cache based on address 493 // Look up shader in the cache based on address
492 Shader shader{TryGet(program_addr)}; 494 const auto& host_ptr{Memory::GetPointer(program_addr)};
495 Shader shader{TryGet(host_ptr)};
493 496
494 if (!shader) { 497 if (!shader) {
495 // No shader found - create a new one 498 // No shader found - create a new one
496 ProgramCode program_code = GetShaderCode(program_addr); 499 const auto& host_ptr{Memory::GetPointer(program_addr)};
500 ProgramCode program_code{GetShaderCode(host_ptr)};
497 ProgramCode program_code_b; 501 ProgramCode program_code_b;
498 if (program == Maxwell::ShaderProgram::VertexA) { 502 if (program == Maxwell::ShaderProgram::VertexA) {
499 program_code_b = GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB)); 503 program_code_b = GetShaderCode(
504 Memory::GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB)));
500 } 505 }
501 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); 506 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
502 507
@@ -504,11 +509,11 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
504 if (found != precompiled_shaders.end()) { 509 if (found != precompiled_shaders.end()) {
505 shader = 510 shader =
506 std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache, 511 std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache,
507 precompiled_programs, found->second); 512 precompiled_programs, found->second, host_ptr);
508 } else { 513 } else {
509 shader = std::make_shared<CachedShader>( 514 shader = std::make_shared<CachedShader>(
510 program_addr, unique_identifier, program, disk_cache, precompiled_programs, 515 program_addr, unique_identifier, program, disk_cache, precompiled_programs,
511 std::move(program_code), std::move(program_code_b)); 516 std::move(program_code), std::move(program_code_b), host_ptr);
512 } 517 }
513 Register(shader); 518 Register(shader);
514 } 519 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 97eed192f..81fe716b4 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 guest_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 guest_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 guest_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 guest_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..b97576309 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -167,9 +167,11 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
167 Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes, 167 Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes,
168 Memory::FlushMode::Flush); 168 Memory::FlushMode::Flush);
169 169
170 VideoCore::MortonCopyPixels128(framebuffer.width, framebuffer.height, bytes_per_pixel, 4, 170 constexpr u32 linear_bpp = 4;
171 Memory::GetPointer(framebuffer_addr), 171 VideoCore::MortonCopyPixels128(VideoCore::MortonSwizzleMode::MortonToLinear,
172 gl_framebuffer_data.data(), true); 172 framebuffer.width, framebuffer.height, bytes_per_pixel,
173 linear_bpp, Memory::GetPointer(framebuffer_addr),
174 gl_framebuffer_data.data());
173 175
174 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); 176 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
175 177
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/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_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 }