summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/ipc_helpers.h2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp2
-rw-r--r--src/core/hle/kernel/object.h4
-rw-r--r--src/core/hle/kernel/readable_event.cpp3
-rw-r--r--src/core/hle/kernel/svc.cpp89
-rw-r--r--src/core/hle/kernel/thread.h21
-rw-r--r--src/core/hle/service/am/am.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.cpp6
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp352
-rw-r--r--src/core/hle/service/audio/audren_u.h2
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp4
-rw-r--r--src/core/hle/service/btm/btm.cpp8
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp6
-rw-r--r--src/core/hle/service/nifm/nifm.cpp4
-rw-r--r--src/core/hle/service/nim/nim.cpp2
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp2
-rw-r--r--src/core/hle/service/set/set.cpp56
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp2
-rw-r--r--src/video_core/dma_pusher.cpp7
-rw-r--r--src/video_core/engines/engine_upload.cpp6
-rw-r--r--src/video_core/engines/engine_upload.h8
-rw-r--r--src/video_core/engines/maxwell_3d.cpp38
-rw-r--r--src/video_core/engines/maxwell_3d.h3
-rw-r--r--src/video_core/engines/shader_bytecode.h22
-rw-r--r--src/video_core/gpu_thread.cpp2
-rw-r--r--src/video_core/gpu_thread.h8
-rw-r--r--src/video_core/macro_interpreter.cpp6
-rw-r--r--src/video_core/rasterizer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_device.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp663
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp54
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h28
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp14
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp7
-rw-r--r--src/video_core/shader/decode/arithmetic.cpp3
-rw-r--r--src/video_core/shader/decode/arithmetic_half.cpp1
-rw-r--r--src/video_core/shader/decode/arithmetic_half_immediate.cpp3
-rw-r--r--src/video_core/shader/decode/arithmetic_immediate.cpp2
-rw-r--r--src/video_core/shader/decode/arithmetic_integer_immediate.cpp2
-rw-r--r--src/video_core/shader/decode/bfe.cpp2
-rw-r--r--src/video_core/shader/decode/bfi.cpp2
-rw-r--r--src/video_core/shader/decode/ffma.cpp2
-rw-r--r--src/video_core/shader/decode/float_set.cpp2
-rw-r--r--src/video_core/shader/decode/float_set_predicate.cpp2
-rw-r--r--src/video_core/shader/decode/half_set.cpp3
-rw-r--r--src/video_core/shader/decode/half_set_predicate.cpp2
-rw-r--r--src/video_core/shader/decode/integer_set.cpp3
-rw-r--r--src/video_core/shader/decode/integer_set_predicate.cpp2
-rw-r--r--src/video_core/shader/decode/memory.cpp28
-rw-r--r--src/video_core/shader/decode/other.cpp14
-rw-r--r--src/video_core/shader/decode/predicate_set_predicate.cpp2
-rw-r--r--src/video_core/shader/decode/predicate_set_register.cpp2
-rw-r--r--src/video_core/shader/decode/register_set_predicate.cpp2
-rw-r--r--src/video_core/shader/decode/shift.cpp2
-rw-r--r--src/video_core/shader/decode/video.cpp2
-rw-r--r--src/video_core/shader/shader_ir.cpp24
-rw-r--r--src/video_core/shader/shader_ir.h88
-rw-r--r--src/video_core/shader/track.cpp12
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/applets/error.cpp2
-rw-r--r--src/yuzu/applets/profile_select.cpp4
-rw-r--r--src/yuzu/bootmanager.cpp7
-rw-r--r--src/yuzu/configuration/config.cpp854
-rw-r--r--src/yuzu/configuration/config.h34
-rw-r--r--src/yuzu/configuration/configure_audio.cpp14
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp8
-rw-r--r--src/yuzu/configuration/configure_general.cpp3
-rw-r--r--src/yuzu/configuration/configure_input.cpp13
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp153
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp75
-rw-r--r--src/yuzu/configuration/configure_per_general.cpp14
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp5
-rw-r--r--src/yuzu/configuration/configure_web.cpp24
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp2
-rw-r--r--src/yuzu/debugger/profiler.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.cpp39
-rw-r--r--src/yuzu/game_list.cpp103
-rw-r--r--src/yuzu/game_list.h2
-rw-r--r--src/yuzu/game_list_p.h25
-rw-r--r--src/yuzu/game_list_worker.cpp4
-rw-r--r--src/yuzu/loading_screen.cpp18
-rw-r--r--src/yuzu/util/spinbox.cpp278
-rw-r--r--src/yuzu/util/spinbox.h86
-rw-r--r--src/yuzu/util/util.cpp18
92 files changed, 1953 insertions, 1530 deletions
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index ac0e1d796..5bb139483 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -438,7 +438,7 @@ inline float RequestParser::Pop() {
438template <> 438template <>
439inline double RequestParser::Pop() { 439inline double RequestParser::Pop() {
440 const u64 value = Pop<u64>(); 440 const u64 value = Pop<u64>();
441 float real; 441 double real;
442 std::memcpy(&real, &value, sizeof(real)); 442 std::memcpy(&real, &value, sizeof(real));
443 return real; 443 return real;
444} 444}
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index dd4eb0978..f3da525d6 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -58,7 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
58 auto& kernel = Core::System::GetInstance().Kernel(); 58 auto& kernel = Core::System::GetInstance().Kernel();
59 if (!writable_event) { 59 if (!writable_event) {
60 // Create event if not provided 60 // Create event if not provided
61 const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 61 const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
62 "HLE Pause Event: " + reason); 62 "HLE Pause Event: " + reason);
63 writable_event = pair.writable; 63 writable_event = pair.writable;
64 } 64 }
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 332876c27..2821176a7 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -33,8 +33,8 @@ enum class HandleType : u32 {
33}; 33};
34 34
35enum class ResetType { 35enum class ResetType {
36 OneShot, ///< Reset automatically on object acquisition 36 Automatic, ///< Reset automatically on object acquisition
37 Sticky, ///< Never reset automatically 37 Manual, ///< Never reset automatically
38}; 38};
39 39
40class Object : NonCopyable { 40class Object : NonCopyable {
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index c2b798a4e..06463cd26 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -21,8 +21,9 @@ bool ReadableEvent::ShouldWait(const Thread* thread) const {
21void ReadableEvent::Acquire(Thread* thread) { 21void ReadableEvent::Acquire(Thread* thread) {
22 ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); 22 ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
23 23
24 if (reset_type == ResetType::OneShot) 24 if (reset_type == ResetType::Automatic) {
25 signaled = false; 25 signaled = false;
26 }
26} 27}
27 28
28void ReadableEvent::Signal() { 29void ReadableEvent::Signal() {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2dcf174c5..5a5851f66 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1255,8 +1255,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
1255 return vm_manager.MapCodeMemory(dst_address, src_address, size); 1255 return vm_manager.MapCodeMemory(dst_address, src_address, size);
1256} 1256}
1257 1257
1258ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, 1258static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
1259 u64 src_address, u64 size) { 1259 u64 dst_address, u64 src_address, u64 size) {
1260 LOG_DEBUG(Kernel_SVC, 1260 LOG_DEBUG(Kernel_SVC,
1261 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " 1261 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1262 "size=0x{:016X}", 1262 "size=0x{:016X}",
@@ -1342,7 +1342,7 @@ static void ExitProcess(Core::System& system) {
1342/// Creates a new thread 1342/// Creates a new thread
1343static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, 1343static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1344 VAddr stack_top, u32 priority, s32 processor_id) { 1344 VAddr stack_top, u32 priority, s32 processor_id) {
1345 LOG_TRACE(Kernel_SVC, 1345 LOG_DEBUG(Kernel_SVC,
1346 "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " 1346 "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
1347 "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", 1347 "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
1348 entry_point, arg, stack_top, priority, processor_id, *out_handle); 1348 entry_point, arg, stack_top, priority, processor_id, *out_handle);
@@ -1402,7 +1402,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
1402 1402
1403/// Starts the thread for the provided handle 1403/// Starts the thread for the provided handle
1404static ResultCode StartThread(Core::System& system, Handle thread_handle) { 1404static ResultCode StartThread(Core::System& system, Handle thread_handle) {
1405 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 1405 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
1406 1406
1407 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1407 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1408 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1408 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
@@ -1425,7 +1425,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
1425 1425
1426/// Called when a thread exits 1426/// Called when a thread exits
1427static void ExitThread(Core::System& system) { 1427static void ExitThread(Core::System& system) {
1428 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); 1428 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1429 1429
1430 auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); 1430 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
1431 current_thread->Stop(); 1431 current_thread->Stop();
@@ -1435,7 +1435,7 @@ static void ExitThread(Core::System& system) {
1435 1435
1436/// Sleep the current thread 1436/// Sleep the current thread
1437static void SleepThread(Core::System& system, s64 nanoseconds) { 1437static void SleepThread(Core::System& system, s64 nanoseconds) {
1438 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); 1438 LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds);
1439 1439
1440 enum class SleepType : s64 { 1440 enum class SleepType : s64 {
1441 YieldWithoutLoadBalancing = 0, 1441 YieldWithoutLoadBalancing = 0,
@@ -1880,52 +1880,59 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
1880} 1880}
1881 1881
1882static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, 1882static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
1883 u64 mask) { 1883 u64 affinity_mask) {
1884 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, 1884 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
1885 mask, core); 1885 thread_handle, core, affinity_mask);
1886 1886
1887 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1887 const auto* const current_process = system.Kernel().CurrentProcess();
1888 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1889 if (!thread) {
1890 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
1891 thread_handle);
1892 return ERR_INVALID_HANDLE;
1893 }
1894 1888
1895 if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) { 1889 if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
1896 const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore(); 1890 const u8 ideal_cpu_core = current_process->GetIdealCore();
1897 1891
1898 ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL)); 1892 ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
1899 1893
1900 // Set the target CPU to the ideal core specified by the process. 1894 // Set the target CPU to the ideal core specified by the process.
1901 core = ideal_cpu_core; 1895 core = ideal_cpu_core;
1902 mask = 1ULL << core; 1896 affinity_mask = 1ULL << core;
1903 } 1897 } else {
1904 1898 const u64 core_mask = current_process->GetCoreMask();
1905 if (mask == 0) { 1899
1906 LOG_ERROR(Kernel_SVC, "Mask is 0"); 1900 if ((core_mask | affinity_mask) != core_mask) {
1907 return ERR_INVALID_COMBINATION; 1901 LOG_ERROR(
1908 } 1902 Kernel_SVC,
1903 "Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
1904 core_mask, affinity_mask);
1905 return ERR_INVALID_PROCESSOR_ID;
1906 }
1909 1907
1910 /// This value is used to only change the affinity mask without changing the current ideal core. 1908 if (affinity_mask == 0) {
1911 static constexpr u32 OnlyChangeMask = static_cast<u32>(-3); 1909 LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
1910 return ERR_INVALID_COMBINATION;
1911 }
1912 1912
1913 if (core == OnlyChangeMask) { 1913 if (core < Core::NUM_CPU_CORES) {
1914 core = thread->GetIdealCore(); 1914 if ((affinity_mask & (1ULL << core)) == 0) {
1915 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { 1915 LOG_ERROR(Kernel_SVC,
1916 LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core); 1916 "Core is not enabled for the current mask, core={}, mask={:016X}", core,
1917 return ERR_INVALID_PROCESSOR_ID; 1917 affinity_mask);
1918 return ERR_INVALID_COMBINATION;
1919 }
1920 } else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
1921 core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
1922 LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
1923 return ERR_INVALID_PROCESSOR_ID;
1924 }
1918 } 1925 }
1919 1926
1920 // Error out if the input core isn't enabled in the input mask. 1927 const auto& handle_table = current_process->GetHandleTable();
1921 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { 1928 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1922 LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}", 1929 if (!thread) {
1923 core, mask); 1930 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
1924 return ERR_INVALID_COMBINATION; 1931 thread_handle);
1932 return ERR_INVALID_HANDLE;
1925 } 1933 }
1926 1934
1927 thread->ChangeCore(core, mask); 1935 thread->ChangeCore(core, affinity_mask);
1928
1929 return RESULT_SUCCESS; 1936 return RESULT_SUCCESS;
1930} 1937}
1931 1938
@@ -1980,7 +1987,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle
1980 1987
1981 auto& kernel = system.Kernel(); 1988 auto& kernel = system.Kernel();
1982 const auto [readable_event, writable_event] = 1989 const auto [readable_event, writable_event] =
1983 WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); 1990 WritableEvent::CreateEventPair(kernel, ResetType::Manual, "CreateEvent");
1984 1991
1985 HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable(); 1992 HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
1986 1993
@@ -2183,8 +2190,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
2183 return RESULT_SUCCESS; 2190 return RESULT_SUCCESS;
2184} 2191}
2185 2192
2186ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, 2193static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
2187 u32 out_thread_ids_size, Handle debug_handle) { 2194 u32 out_thread_ids_size, Handle debug_handle) {
2188 // TODO: Handle this case when debug events are supported. 2195 // TODO: Handle this case when debug events are supported.
2189 UNIMPLEMENTED_IF(debug_handle != InvalidHandle); 2196 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
2190 2197
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index f07332f02..b4b9cda7c 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -30,12 +30,21 @@ enum ThreadPriority : u32 {
30}; 30};
31 31
32enum ThreadProcessorId : s32 { 32enum ThreadProcessorId : s32 {
33 THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process. 33 /// Indicates that no particular processor core is preferred.
34 THREADPROCESSORID_0 = 0, ///< Run thread on core 0 34 THREADPROCESSORID_DONT_CARE = -1,
35 THREADPROCESSORID_1 = 1, ///< Run thread on core 1 35
36 THREADPROCESSORID_2 = 2, ///< Run thread on core 2 36 /// Run thread on the ideal core specified by the process.
37 THREADPROCESSORID_3 = 3, ///< Run thread on core 3 37 THREADPROCESSORID_IDEAL = -2,
38 THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this 38
39 /// Indicates that the preferred processor ID shouldn't be updated in
40 /// a core mask setting operation.
41 THREADPROCESSORID_DONT_UPDATE = -3,
42
43 THREADPROCESSORID_0 = 0, ///< Run thread on core 0
44 THREADPROCESSORID_1 = 1, ///< Run thread on core 1
45 THREADPROCESSORID_2 = 2, ///< Run thread on core 2
46 THREADPROCESSORID_3 = 3, ///< Run thread on core 3
47 THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
39 48
40 /// Allowed CPU mask 49 /// Allowed CPU mask
41 THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) | 50 THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 26a665bfd..1a32a109f 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -276,7 +276,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
276 RegisterHandlers(functions); 276 RegisterHandlers(functions);
277 277
278 auto& kernel = Core::System::GetInstance().Kernel(); 278 auto& kernel = Core::System::GetInstance().Kernel();
279 launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, 279 launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
280 "ISelfController:LaunchableEvent"); 280 "ISelfController:LaunchableEvent");
281} 281}
282 282
@@ -442,10 +442,10 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
442 442
443AppletMessageQueue::AppletMessageQueue() { 443AppletMessageQueue::AppletMessageQueue() {
444 auto& kernel = Core::System::GetInstance().Kernel(); 444 auto& kernel = Core::System::GetInstance().Kernel();
445 on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, 445 on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
446 "AMMessageQueue:OnMessageRecieved"); 446 "AMMessageQueue:OnMessageRecieved");
447 on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( 447 on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
448 kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged"); 448 kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged");
449} 449}
450 450
451AppletMessageQueue::~AppletMessageQueue() = default; 451AppletMessageQueue::~AppletMessageQueue() = default;
@@ -835,6 +835,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
835 835
836 IPC::ResponseBuilder rb{ctx, 2}; 836 IPC::ResponseBuilder rb{ctx, 2};
837 rb.Push(ERR_SIZE_OUT_OF_BOUNDS); 837 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
838 return;
838 } 839 }
839 840
840 std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); 841 std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
@@ -857,6 +858,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
857 858
858 IPC::ResponseBuilder rb{ctx, 2}; 859 IPC::ResponseBuilder rb{ctx, 2};
859 rb.Push(ERR_SIZE_OUT_OF_BOUNDS); 860 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
861 return;
860 } 862 }
861 863
862 ctx.WriteBuffer(backing.buffer.data() + offset, size); 864 ctx.WriteBuffer(backing.buffer.data() + offset, size);
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 7f70b10df..e812c66e9 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -26,11 +26,11 @@ namespace Service::AM::Applets {
26AppletDataBroker::AppletDataBroker() { 26AppletDataBroker::AppletDataBroker() {
27 auto& kernel = Core::System::GetInstance().Kernel(); 27 auto& kernel = Core::System::GetInstance().Kernel();
28 state_changed_event = Kernel::WritableEvent::CreateEventPair( 28 state_changed_event = Kernel::WritableEvent::CreateEventPair(
29 kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent"); 29 kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
30 pop_out_data_event = Kernel::WritableEvent::CreateEventPair( 30 pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
31 kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent"); 31 kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
32 pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair( 32 pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
33 kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); 33 kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
34} 34}
35 35
36AppletDataBroker::~AppletDataBroker() = default; 36AppletDataBroker::~AppletDataBroker() = default;
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 51d8c26b4..bd4e38461 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -68,7 +68,7 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
68 RegisterHandlers(functions); 68 RegisterHandlers(functions);
69 69
70 auto& kernel = Core::System::GetInstance().Kernel(); 70 auto& kernel = Core::System::GetInstance().Kernel();
71 aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, 71 aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
72 "GetAddOnContentListChanged:Event"); 72 "GetAddOnContentListChanged:Event");
73} 73}
74 74
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 12875fb42..6ba41b20a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -67,7 +67,7 @@ public:
67 // This is the event handle used to check if the audio buffer was released 67 // This is the event handle used to check if the audio buffer was released
68 auto& system = Core::System::GetInstance(); 68 auto& system = Core::System::GetInstance();
69 buffer_event = Kernel::WritableEvent::CreateEventPair( 69 buffer_event = Kernel::WritableEvent::CreateEventPair(
70 system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); 70 system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
71 71
72 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, 72 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
73 audio_params.channel_count, std::move(unique_name), 73 audio_params.channel_count, std::move(unique_name),
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 1dde6edb7..75db0c2dc 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -8,6 +8,7 @@
8 8
9#include "audio_core/audio_renderer.h" 9#include "audio_core/audio_renderer.h"
10#include "common/alignment.h" 10#include "common/alignment.h"
11#include "common/bit_util.h"
11#include "common/common_funcs.h" 12#include "common/common_funcs.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/string_util.h" 14#include "common/string_util.h"
@@ -46,7 +47,7 @@ public:
46 47
47 auto& system = Core::System::GetInstance(); 48 auto& system = Core::System::GetInstance();
48 system_event = Kernel::WritableEvent::CreateEventPair( 49 system_event = Kernel::WritableEvent::CreateEventPair(
49 system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); 50 system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
50 renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params, 51 renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
51 system_event.writable); 52 system_event.writable);
52 } 53 }
@@ -178,7 +179,7 @@ public:
178 RegisterHandlers(functions); 179 RegisterHandlers(functions);
179 180
180 auto& kernel = Core::System::GetInstance().Kernel(); 181 auto& kernel = Core::System::GetInstance().Kernel();
181 buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 182 buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
182 "IAudioOutBufferReleasedEvent"); 183 "IAudioOutBufferReleasedEvent");
183 } 184 }
184 185
@@ -262,64 +263,304 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
262 OpenAudioRendererImpl(ctx); 263 OpenAudioRendererImpl(ctx);
263} 264}
264 265
266static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) {
267 // +1 represents the final mix.
268 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
269 1;
270}
271
265void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { 272void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
266 IPC::RequestParser rp{ctx};
267 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
268 LOG_DEBUG(Service_Audio, "called"); 273 LOG_DEBUG(Service_Audio, "called");
269 274
270 u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40); 275 // Several calculations below align the sizes being calculated
271 buffer_sz += params.submix_count * 1024; 276 // onto a 64 byte boundary.
272 buffer_sz += 0x940 * (params.submix_count + 1); 277 static constexpr u64 buffer_alignment_size = 64;
273 buffer_sz += 0x3F0 * params.voice_count; 278
274 buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10); 279 // Some calculations that calculate portions of the buffer
275 buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); 280 // that will contain information, on the other hand, align
276 buffer_sz += Common::AlignUp( 281 // the result of some of their calcularions on a 16 byte boundary.
277 (0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) * 282 static constexpr u64 info_field_alignment_size = 16;
278 (params.mix_buffer_count + 6), 283
279 0x40); 284 // Maximum detail entries that may exist at one time for performance
280 285 // frame statistics.
281 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 286 static constexpr u64 max_perf_detail_entries = 100;
282 const u32 count = params.submix_count + 1; 287
283 u64 node_count = Common::AlignUp(count, 0x40); 288 // Size of the data structure representing the bulk of the voice-related state.
284 const u64 node_state_buffer_sz = 289 static constexpr u64 voice_state_size = 0x100;
285 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8); 290
286 u64 edge_matrix_buffer_sz = 0; 291 // Size of the upsampler manager data structure
287 node_count = Common::AlignUp(count * count, 0x40); 292 constexpr u64 upsampler_manager_size = 0x48;
288 if (node_count >> 31 != 0) { 293
289 edge_matrix_buffer_sz = (node_count | 7) / 8; 294 // Calculates the part of the size that relates to mix buffers.
290 } else { 295 const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) {
291 edge_matrix_buffer_sz = node_count / 8; 296 // As of 8.0.0 this is the maximum on voice channels.
297 constexpr u64 max_voice_channels = 6;
298
299 // The service expects the sample_count member of the parameters to either be
300 // a value of 160 or 240, so the maximum sample count is assumed in order
301 // to adequately handle all values at runtime.
302 constexpr u64 default_max_sample_count = 240;
303
304 const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
305
306 u64 size = 0;
307 size += total_mix_buffers * (sizeof(s32) * params.sample_count);
308 size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
309 size += u64{params.submix_count} + params.sink_count;
310 size = Common::AlignUp(size, buffer_alignment_size);
311 size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
312 size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
313 return size;
314 };
315
316 // Calculates the portion of the size related to the mix data (and the sorting thereof).
317 const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) {
318 // The size of the mixing info data structure.
319 constexpr u64 mix_info_size = 0x940;
320
321 // Consists of total submixes with the final mix included.
322 const u64 total_mix_count = u64{params.submix_count} + 1;
323
324 // The total number of effects that may be available to the audio renderer at any time.
325 constexpr u64 max_effects = 256;
326
327 // Calculates the part of the size related to the audio node state.
328 // This will only be used if the audio revision supports the splitter.
329 const auto calculate_node_state_size = [](std::size_t num_nodes) {
330 // Internally within a nodestate, it appears to use a data structure
331 // similar to a std::bitset<64> twice.
332 constexpr u64 bit_size = Common::BitSize<u64>();
333 constexpr u64 num_bitsets = 2;
334
335 // Node state instances have three states internally for performing
336 // depth-first searches of nodes. Initialized, Found, and Done Sorting.
337 constexpr u64 num_states = 3;
338
339 u64 size = 0;
340 size += (num_nodes * num_nodes) * sizeof(s32);
341 size += num_states * (num_nodes * sizeof(s32));
342 size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
343 return size;
344 };
345
346 // Calculates the part of the size related to the adjacency (aka edge) matrix.
347 const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
348 return (num_nodes * num_nodes) * sizeof(s32);
349 };
350
351 u64 size = 0;
352 size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
353 size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
354 size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
355 info_field_alignment_size);
356
357 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
358 size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
359 calculate_edge_matrix_size(total_mix_count),
360 info_field_alignment_size);
292 } 361 }
293 buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
294 }
295 362
296 buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50; 363 return size;
297 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 364 };
298 buffer_sz += 0xE0 * params.num_splitter_send_channels;
299 buffer_sz += 0x20 * params.splitter_count;
300 buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10);
301 }
302 buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
303 u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
304 ((params.voice_count * 256) | 0x40);
305
306 if (params.performance_frame_count >= 1) {
307 output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
308 16 * params.voice_count + 16) +
309 0x658) *
310 (params.performance_frame_count + 1) +
311 0xc0,
312 0x40) +
313 output_sz;
314 }
315 output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
316 365
317 IPC::ResponseBuilder rb{ctx, 4}; 366 // Calculates the part of the size related to voice channel info.
367 const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) {
368 constexpr u64 voice_info_size = 0x220;
369 constexpr u64 voice_resource_size = 0xD0;
370
371 u64 size = 0;
372 size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
373 size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
374 size +=
375 Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
376 size += Common::AlignUp(voice_state_size * params.voice_count, info_field_alignment_size);
377 return size;
378 };
379
380 // Calculates the part of the size related to memory pools.
381 const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) {
382 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
383 const u64 memory_pool_info_size = 0x20;
384 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
385 };
386
387 // Calculates the part of the size related to the splitter context.
388 const auto calculate_splitter_context_size =
389 [this](const AudioCore::AudioRendererParameter& params) -> u64 {
390 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
391 return 0;
392 }
393
394 constexpr u64 splitter_info_size = 0x20;
395 constexpr u64 splitter_destination_data_size = 0xE0;
396
397 u64 size = 0;
398 size += params.num_splitter_send_channels;
399 size +=
400 Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
401 size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
402 info_field_alignment_size);
403
404 return size;
405 };
406
407 // Calculates the part of the size related to the upsampler info.
408 const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) {
409 constexpr u64 upsampler_info_size = 0x280;
410 // Yes, using the buffer size over info alignment size is intentional here.
411 return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count),
412 buffer_alignment_size);
413 };
414
415 // Calculates the part of the size related to effect info.
416 const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) {
417 constexpr u64 effect_info_size = 0x2B0;
418 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
419 };
420
421 // Calculates the part of the size related to audio sink info.
422 const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) {
423 const u64 sink_info_size = 0x170;
424 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
425 };
426
427 // Calculates the part of the size related to voice state info.
428 const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) {
429 const u64 voice_state_size = 0x100;
430 const u64 additional_size = buffer_alignment_size - 1;
431 return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
432 info_field_alignment_size);
433 };
434
435 // Calculates the part of the size related to performance statistics.
436 const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) {
437 // Extra size value appended to the end of the calculation.
438 constexpr u64 appended = 128;
439
440 // Whether or not we assume the newer version of performance metrics data structures.
441 const bool is_v2 =
442 IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
443
444 // Data structure sizes
445 constexpr u64 perf_statistics_size = 0x0C;
446 const u64 header_size = is_v2 ? 0x30 : 0x18;
447 const u64 entry_size = is_v2 ? 0x18 : 0x10;
448 const u64 detail_size = is_v2 ? 0x18 : 0x10;
449
450 const u64 entry_count = CalculateNumPerformanceEntries(params);
451 const u64 size_per_frame =
452 header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
453
454 u64 size = 0;
455 size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
456 buffer_alignment_size);
457 size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
458 size += appended;
459 return size;
460 };
461
462 // Calculates the part of the size that relates to the audio command buffer.
463 const auto calculate_command_buffer_size =
464 [this](const AudioCore::AudioRendererParameter& params) {
465 constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
466
467 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
468 constexpr u64 command_buffer_size = 0x18000;
469
470 return command_buffer_size + alignment;
471 }
472
473 // When the variadic command buffer is supported, this means
474 // the command generator for the audio renderer can issue commands
475 // that are (as one would expect), variable in size. So what we need to do
476 // is determine the maximum possible size for a few command data structures
477 // then multiply them by the amount of present commands indicated by the given
478 // respective audio parameters.
479
480 constexpr u64 max_biquad_filters = 2;
481 constexpr u64 max_mix_buffers = 24;
482
483 constexpr u64 biquad_filter_command_size = 0x2C;
484
485 constexpr u64 depop_mix_command_size = 0x24;
486 constexpr u64 depop_setup_command_size = 0x50;
487
488 constexpr u64 effect_command_max_size = 0x540;
489
490 constexpr u64 mix_command_size = 0x1C;
491 constexpr u64 mix_ramp_command_size = 0x24;
492 constexpr u64 mix_ramp_grouped_command_size = 0x13C;
493
494 constexpr u64 perf_command_size = 0x28;
495
496 constexpr u64 sink_command_size = 0x130;
497
498 constexpr u64 submix_command_max_size =
499 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
500
501 constexpr u64 volume_command_size = 0x1C;
502 constexpr u64 volume_ramp_command_size = 0x20;
503
504 constexpr u64 voice_biquad_filter_command_size =
505 biquad_filter_command_size * max_biquad_filters;
506 constexpr u64 voice_data_command_size = 0x9C;
507 const u64 voice_command_max_size =
508 (params.splitter_count * depop_setup_command_size) +
509 (voice_data_command_size + voice_biquad_filter_command_size +
510 volume_ramp_command_size + mix_ramp_grouped_command_size);
511
512 // Now calculate the individual elements that comprise the size and add them together.
513 const u64 effect_commands_size = params.effect_count * effect_command_max_size;
514
515 const u64 final_mix_commands_size =
516 depop_mix_command_size + volume_command_size * max_mix_buffers;
318 517
518 const u64 perf_commands_size =
519 perf_command_size *
520 (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
521
522 const u64 sink_commands_size = params.sink_count * sink_command_size;
523
524 const u64 splitter_commands_size =
525 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
526
527 const u64 submix_commands_size = params.submix_count * submix_command_max_size;
528
529 const u64 voice_commands_size = params.voice_count * voice_command_max_size;
530
531 return effect_commands_size + final_mix_commands_size + perf_commands_size +
532 sink_commands_size + splitter_commands_size + submix_commands_size +
533 voice_commands_size + alignment;
534 };
535
536 IPC::RequestParser rp{ctx};
537 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
538
539 u64 size = 0;
540 size += calculate_mix_buffer_sizes(params);
541 size += calculate_mix_info_size(params);
542 size += calculate_voice_info_size(params);
543 size += upsampler_manager_size;
544 size += calculate_memory_pools_size(params);
545 size += calculate_splitter_context_size(params);
546
547 size = Common::AlignUp(size, buffer_alignment_size);
548
549 size += calculate_upsampler_info_size(params);
550 size += calculate_effect_info_size(params);
551 size += calculate_sink_info_size(params);
552 size += calculate_voice_state_size(params);
553 size += calculate_perf_size(params);
554 size += calculate_command_buffer_size(params);
555
556 // finally, 4KB page align the size, and we're done.
557 size = Common::AlignUp(size, 4096);
558
559 IPC::ResponseBuilder rb{ctx, 4};
319 rb.Push(RESULT_SUCCESS); 560 rb.Push(RESULT_SUCCESS);
320 rb.Push<u64>(output_sz); 561 rb.Push<u64>(size);
321 562
322 LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz); 563 LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
323} 564}
324 565
325void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { 566void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
@@ -357,10 +598,15 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
357} 598}
358 599
359bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { 600bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
360 u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap 601 // Byte swap
602 const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
603
361 switch (feature) { 604 switch (feature) {
362 case AudioFeatures::Splitter: 605 case AudioFeatures::Splitter:
363 return version_num >= 2u; 606 return version_num >= 2U;
607 case AudioFeatures::PerformanceMetricsVersion2:
608 case AudioFeatures::VariadicCommandBuffer:
609 return version_num >= 5U;
364 default: 610 default:
365 return false; 611 return false;
366 } 612 }
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index e55d25973..1d3c8df61 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -28,6 +28,8 @@ private:
28 28
29 enum class AudioFeatures : u32 { 29 enum class AudioFeatures : u32 {
30 Splitter, 30 Splitter,
31 PerformanceMetricsVersion2,
32 VariadicCommandBuffer,
31 }; 33 };
32 34
33 bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const; 35 bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 974ff8e1a..3c7ca2c44 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -34,8 +34,8 @@ public:
34 RegisterHandlers(functions); 34 RegisterHandlers(functions);
35 35
36 auto& kernel = Core::System::GetInstance().Kernel(); 36 auto& kernel = Core::System::GetInstance().Kernel();
37 register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 37 register_event = Kernel::WritableEvent::CreateEventPair(
38 "BT:RegisterEvent"); 38 kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
39 } 39 }
40 40
41private: 41private:
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 4f15c3f19..b439ee7ec 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -57,13 +57,13 @@ public:
57 RegisterHandlers(functions); 57 RegisterHandlers(functions);
58 58
59 auto& kernel = Core::System::GetInstance().Kernel(); 59 auto& kernel = Core::System::GetInstance().Kernel();
60 scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 60 scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
61 "IBtmUserCore:ScanEvent"); 61 "IBtmUserCore:ScanEvent");
62 connection_event = Kernel::WritableEvent::CreateEventPair( 62 connection_event = Kernel::WritableEvent::CreateEventPair(
63 kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent"); 63 kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent");
64 service_discovery = Kernel::WritableEvent::CreateEventPair( 64 service_discovery = Kernel::WritableEvent::CreateEventPair(
65 kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery"); 65 kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery");
66 config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 66 config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
67 "IBtmUserCore:ConfigEvent"); 67 "IBtmUserCore:ConfigEvent");
68 } 68 }
69 69
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e7fc7a619..fdd6d79a2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -170,7 +170,7 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
170void Controller_NPad::OnInit() { 170void Controller_NPad::OnInit() {
171 auto& kernel = Core::System::GetInstance().Kernel(); 171 auto& kernel = Core::System::GetInstance().Kernel();
172 styleset_changed_event = Kernel::WritableEvent::CreateEventPair( 172 styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
173 kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); 173 kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged");
174 174
175 if (!IsControllerActivated()) { 175 if (!IsControllerActivated()) {
176 return; 176 return;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index c6babdd4d..a5cb06f8a 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -26,7 +26,7 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
26Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 26Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
27 : ServiceFramework(name), module(std::move(module)) { 27 : ServiceFramework(name), module(std::move(module)) {
28 auto& kernel = Core::System::GetInstance().Kernel(); 28 auto& kernel = Core::System::GetInstance().Kernel();
29 nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 29 nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
30 "IUser:NFCTagDetected"); 30 "IUser:NFCTagDetected");
31} 31}
32 32
@@ -67,9 +67,9 @@ public:
67 67
68 auto& kernel = Core::System::GetInstance().Kernel(); 68 auto& kernel = Core::System::GetInstance().Kernel();
69 deactivate_event = Kernel::WritableEvent::CreateEventPair( 69 deactivate_event = Kernel::WritableEvent::CreateEventPair(
70 kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); 70 kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
71 availability_change_event = Kernel::WritableEvent::CreateEventPair( 71 availability_change_event = Kernel::WritableEvent::CreateEventPair(
72 kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent"); 72 kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent");
73 } 73 }
74 74
75private: 75private:
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index f92571008..76b12b482 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -62,9 +62,9 @@ public:
62 RegisterHandlers(functions); 62 RegisterHandlers(functions);
63 63
64 auto& kernel = Core::System::GetInstance().Kernel(); 64 auto& kernel = Core::System::GetInstance().Kernel();
65 event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 65 event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
66 "IRequest:Event1"); 66 "IRequest:Event1");
67 event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 67 event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
68 "IRequest:Event2"); 68 "IRequest:Event2");
69 } 69 }
70 70
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 0dabcd23b..f319a3ca1 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -141,7 +141,7 @@ public:
141 141
142 auto& kernel = Core::System::GetInstance().Kernel(); 142 auto& kernel = Core::System::GetInstance().Kernel();
143 finished_event = Kernel::WritableEvent::CreateEventPair( 143 finished_event = Kernel::WritableEvent::CreateEventPair(
144 kernel, Kernel::ResetType::OneShot, 144 kernel, Kernel::ResetType::Automatic,
145 "IEnsureNetworkClockAvailabilityService:FinishEvent"); 145 "IEnsureNetworkClockAvailabilityService:FinishEvent");
146 } 146 }
147 147
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 3b9ab4b14..b60fc748b 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -129,7 +129,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
129 RegisterHandlers(functions); 129 RegisterHandlers(functions);
130 130
131 auto& kernel = Core::System::GetInstance().Kernel(); 131 auto& kernel = Core::System::GetInstance().Kernel();
132 query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, 132 query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
133 "NVDRV::query_event"); 133 "NVDRV::query_event");
134} 134}
135 135
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 4d150fc71..5731e815f 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -16,7 +16,7 @@ namespace Service::NVFlinger {
16 16
17BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { 17BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
18 auto& kernel = Core::System::GetInstance().Kernel(); 18 auto& kernel = Core::System::GetInstance().Kernel();
19 buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, 19 buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
20 "BufferQueue NativeHandle"); 20 "BufferQueue NativeHandle");
21} 21}
22 22
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 4ecb6bcef..298d85011 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -2,16 +2,15 @@
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>
5#include <chrono> 6#include <chrono>
6#include "common/logging/log.h" 7#include "common/logging/log.h"
7#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/client_port.h"
9#include "core/hle/kernel/client_session.h"
10#include "core/hle/service/set/set.h" 9#include "core/hle/service/set/set.h"
11#include "core/settings.h" 10#include "core/settings.h"
12 11
13namespace Service::Set { 12namespace Service::Set {
14 13namespace {
15constexpr std::array<LanguageCode, 17> available_language_codes = {{ 14constexpr std::array<LanguageCode, 17> available_language_codes = {{
16 LanguageCode::JA, 15 LanguageCode::JA,
17 LanguageCode::EN_US, 16 LanguageCode::EN_US,
@@ -32,41 +31,35 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
32 LanguageCode::ZH_HANT, 31 LanguageCode::ZH_HANT,
33}}; 32}};
34 33
35constexpr std::size_t pre4_0_0_max_entries = 0xF; 34constexpr std::size_t pre4_0_0_max_entries = 15;
36constexpr std::size_t post4_0_0_max_entries = 0x40; 35constexpr std::size_t post4_0_0_max_entries = 17;
37 36
38constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; 37constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
39 38
40LanguageCode GetLanguageCodeFromIndex(std::size_t index) { 39void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) {
41 return available_language_codes.at(index); 40 IPC::ResponseBuilder rb{ctx, 3};
41 rb.Push(RESULT_SUCCESS);
42 rb.Push(static_cast<u32>(num_language_codes));
42} 43}
43 44
44template <std::size_t size> 45void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
45static std::array<LanguageCode, size> MakeLanguageCodeSubset() { 46 const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
46 std::array<LanguageCode, size> arr; 47 const std::size_t copy_amount = std::min(requested_amount, max_size);
47 std::copy_n(available_language_codes.begin(), size, arr.begin()); 48 const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
48 return arr; 49
50 ctx.WriteBuffer(available_language_codes.data(), copy_size);
51 PushResponseLanguageCode(ctx, copy_amount);
49} 52}
53} // Anonymous namespace
50 54
51static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) { 55LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
52 IPC::ResponseBuilder rb{ctx, 3}; 56 return available_language_codes.at(index);
53 rb.Push(RESULT_SUCCESS);
54 if (available_language_codes.size() > max_size) {
55 rb.Push(static_cast<u32>(max_size));
56 } else {
57 rb.Push(static_cast<u32>(available_language_codes.size()));
58 }
59} 57}
60 58
61void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { 59void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
62 LOG_DEBUG(Service_SET, "called"); 60 LOG_DEBUG(Service_SET, "called");
63 61
64 if (available_language_codes.size() > pre4_0_0_max_entries) { 62 GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
65 ctx.WriteBuffer(MakeLanguageCodeSubset<pre4_0_0_max_entries>());
66 } else {
67 ctx.WriteBuffer(available_language_codes);
68 }
69 PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
70} 63}
71 64
72void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { 65void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -87,12 +80,7 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
87void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) { 80void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
88 LOG_DEBUG(Service_SET, "called"); 81 LOG_DEBUG(Service_SET, "called");
89 82
90 if (available_language_codes.size() > post4_0_0_max_entries) { 83 GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
91 ctx.WriteBuffer(MakeLanguageCodeSubset<post4_0_0_max_entries>());
92 } else {
93 ctx.WriteBuffer(available_language_codes);
94 }
95 PushResponseLanguageCode(ctx, post4_0_0_max_entries);
96} 84}
97 85
98void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { 86void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
@@ -102,9 +90,9 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
102} 90}
103 91
104void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { 92void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
105 PushResponseLanguageCode(ctx, post4_0_0_max_entries);
106
107 LOG_DEBUG(Service_SET, "called"); 93 LOG_DEBUG(Service_SET, "called");
94
95 PushResponseLanguageCode(ctx, post4_0_0_max_entries);
108} 96}
109 97
110void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { 98void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 01d80311b..a8d088305 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -17,7 +17,7 @@ namespace Service::VI {
17 17
18Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { 18Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} {
19 auto& kernel = Core::System::GetInstance().Kernel(); 19 auto& kernel = Core::System::GetInstance().Kernel();
20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, 20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
21 fmt::format("Display VSync Event {}", id)); 21 fmt::format("Display VSync Event {}", id));
22} 22}
23 23
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 036e66f05..3175579cc 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -40,6 +40,13 @@ bool DmaPusher::Step() {
40 } 40 }
41 41
42 const CommandList& command_list{dma_pushbuffer.front()}; 42 const CommandList& command_list{dma_pushbuffer.front()};
43 ASSERT_OR_EXECUTE(!command_list.empty(), {
44 // Somehow the command_list is empty, in order to avoid a crash
45 // We ignore it and assume its size is 0.
46 dma_pushbuffer.pop();
47 dma_pushbuffer_subindex = 0;
48 return true;
49 });
43 const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]}; 50 const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
44 GPUVAddr dma_get = command_list_header.addr; 51 GPUVAddr dma_get = command_list_header.addr;
45 GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32); 52 GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index f8aa4ff55..082a40cd9 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "video_core/engines/engine_upload.h" 8#include "video_core/engines/engine_upload.h"
7#include "video_core/memory_manager.h" 9#include "video_core/memory_manager.h"
@@ -10,7 +12,9 @@
10namespace Tegra::Engines::Upload { 12namespace Tegra::Engines::Upload {
11 13
12State::State(MemoryManager& memory_manager, Registers& regs) 14State::State(MemoryManager& memory_manager, Registers& regs)
13 : memory_manager(memory_manager), regs(regs) {} 15 : regs{regs}, memory_manager{memory_manager} {}
16
17State::~State() = default;
14 18
15void State::ProcessExec(const bool is_linear) { 19void State::ProcessExec(const bool is_linear) {
16 write_offset = 0; 20 write_offset = 0;
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h
index 9c6e0d21c..ef4f5839a 100644
--- a/src/video_core/engines/engine_upload.h
+++ b/src/video_core/engines/engine_upload.h
@@ -4,10 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstddef>
8#include <vector> 7#include <vector>
9#include "common/bit_field.h" 8#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h" 9#include "common/common_types.h"
12 10
13namespace Tegra { 11namespace Tegra {
@@ -57,10 +55,10 @@ struct Registers {
57class State { 55class State {
58public: 56public:
59 State(MemoryManager& memory_manager, Registers& regs); 57 State(MemoryManager& memory_manager, Registers& regs);
60 ~State() = default; 58 ~State();
61 59
62 void ProcessExec(const bool is_linear); 60 void ProcessExec(bool is_linear);
63 void ProcessData(const u32 data, const bool is_last_call); 61 void ProcessData(u32 data, bool is_last_call);
64 62
65private: 63private:
66 u32 write_offset = 0; 64 u32 write_offset = 0;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index d7b586db9..39968d403 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -34,9 +34,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
34 34
35 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is 35 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
36 // needed for ARMS. 36 // needed for ARMS.
37 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { 37 for (auto& viewport : regs.viewports) {
38 regs.viewports[viewport].depth_range_near = 0.0f; 38 viewport.depth_range_near = 0.0f;
39 regs.viewports[viewport].depth_range_far = 1.0f; 39 viewport.depth_range_far = 1.0f;
40 } 40 }
41 41
42 // Doom and Bomberman seems to use the uninitialized registers and just enable blend 42 // Doom and Bomberman seems to use the uninitialized registers and just enable blend
@@ -47,13 +47,13 @@ void Maxwell3D::InitializeRegisterDefaults() {
47 regs.blend.equation_a = Regs::Blend::Equation::Add; 47 regs.blend.equation_a = Regs::Blend::Equation::Add;
48 regs.blend.factor_source_a = Regs::Blend::Factor::One; 48 regs.blend.factor_source_a = Regs::Blend::Factor::One;
49 regs.blend.factor_dest_a = Regs::Blend::Factor::Zero; 49 regs.blend.factor_dest_a = Regs::Blend::Factor::Zero;
50 for (std::size_t blend_index = 0; blend_index < Regs::NumRenderTargets; blend_index++) { 50 for (auto& blend : regs.independent_blend) {
51 regs.independent_blend[blend_index].equation_rgb = Regs::Blend::Equation::Add; 51 blend.equation_rgb = Regs::Blend::Equation::Add;
52 regs.independent_blend[blend_index].factor_source_rgb = Regs::Blend::Factor::One; 52 blend.factor_source_rgb = Regs::Blend::Factor::One;
53 regs.independent_blend[blend_index].factor_dest_rgb = Regs::Blend::Factor::Zero; 53 blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
54 regs.independent_blend[blend_index].equation_a = Regs::Blend::Equation::Add; 54 blend.equation_a = Regs::Blend::Equation::Add;
55 regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One; 55 blend.factor_source_a = Regs::Blend::Factor::One;
56 regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero; 56 blend.factor_dest_a = Regs::Blend::Factor::Zero;
57 } 57 }
58 regs.stencil_front_op_fail = Regs::StencilOp::Keep; 58 regs.stencil_front_op_fail = Regs::StencilOp::Keep;
59 regs.stencil_front_op_zfail = Regs::StencilOp::Keep; 59 regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
@@ -75,11 +75,11 @@ void Maxwell3D::InitializeRegisterDefaults() {
75 75
76 // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a 76 // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
77 // default of enabled fixes rendering here. 77 // default of enabled fixes rendering here.
78 for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) { 78 for (auto& color_mask : regs.color_mask) {
79 regs.color_mask[color_mask].R.Assign(1); 79 color_mask.R.Assign(1);
80 regs.color_mask[color_mask].G.Assign(1); 80 color_mask.G.Assign(1);
81 regs.color_mask[color_mask].B.Assign(1); 81 color_mask.B.Assign(1);
82 regs.color_mask[color_mask].A.Assign(1); 82 color_mask.A.Assign(1);
83 } 83 }
84 84
85 // Commercial games seem to assume this value is enabled and nouveau sets this value manually. 85 // Commercial games seem to assume this value is enabled and nouveau sets this value manually.
@@ -178,13 +178,13 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
178 178
179 // Vertex buffer 179 // Vertex buffer
180 if (method >= MAXWELL3D_REG_INDEX(vertex_array) && 180 if (method >= MAXWELL3D_REG_INDEX(vertex_array) &&
181 method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) { 181 method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * Regs::NumVertexArrays) {
182 dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2); 182 dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2);
183 } else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) && 183 } else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) &&
184 method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) { 184 method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * Regs::NumVertexArrays) {
185 dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1); 185 dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1);
186 } else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) && 186 } else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) &&
187 method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) { 187 method < MAXWELL3D_REG_INDEX(instanced_arrays) + Regs::NumVertexArrays) {
188 dirty_flags.vertex_array.set(method - MAXWELL3D_REG_INDEX(instanced_arrays)); 188 dirty_flags.vertex_array.set(method - MAXWELL3D_REG_INDEX(instanced_arrays));
189 } 189 }
190 } 190 }
@@ -442,7 +442,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
442 const auto a_type = tic_entry.a_type.Value(); 442 const auto a_type = tic_entry.a_type.Value();
443 443
444 // TODO(Subv): Different data types for separate components are not supported 444 // TODO(Subv): Different data types for separate components are not supported
445 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); 445 DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
446 446
447 return tic_entry; 447 return tic_entry;
448} 448}
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4883b582a..f342c78e6 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <type_traits>
9#include <unordered_map> 10#include <unordered_map>
10#include <vector> 11#include <vector>
11 12
@@ -58,6 +59,7 @@ public:
58 static constexpr std::size_t NumCBData = 16; 59 static constexpr std::size_t NumCBData = 16;
59 static constexpr std::size_t NumVertexArrays = 32; 60 static constexpr std::size_t NumVertexArrays = 32;
60 static constexpr std::size_t NumVertexAttributes = 32; 61 static constexpr std::size_t NumVertexAttributes = 32;
62 static constexpr std::size_t NumVaryings = 31;
61 static constexpr std::size_t NumTextureSamplers = 32; 63 static constexpr std::size_t NumTextureSamplers = 32;
62 static constexpr std::size_t NumClipDistances = 8; 64 static constexpr std::size_t NumClipDistances = 8;
63 static constexpr std::size_t MaxShaderProgram = 6; 65 static constexpr std::size_t MaxShaderProgram = 6;
@@ -1107,6 +1109,7 @@ public:
1107 } regs{}; 1109 } regs{};
1108 1110
1109 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); 1111 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
1112 static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
1110 1113
1111 struct State { 1114 struct State {
1112 struct ConstBufferInfo { 1115 struct ConstBufferInfo {
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e5b4eadea..7bbc556da 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -98,6 +98,10 @@ union Attribute {
98 BitField<22, 2, u64> element; 98 BitField<22, 2, u64> element;
99 BitField<24, 6, Index> index; 99 BitField<24, 6, Index> index;
100 BitField<47, 3, AttributeSize> size; 100 BitField<47, 3, AttributeSize> size;
101
102 bool IsPhysical() const {
103 return element == 0 && static_cast<u64>(index.Value()) == 0;
104 }
101 } fmt20; 105 } fmt20;
102 106
103 union { 107 union {
@@ -499,6 +503,11 @@ enum class SystemVariable : u64 {
499 CircularQueueEntryAddressHigh = 0x63, 503 CircularQueueEntryAddressHigh = 0x63,
500}; 504};
501 505
506enum class PhysicalAttributeDirection : u64 {
507 Input = 0,
508 Output = 1,
509};
510
502union Instruction { 511union Instruction {
503 Instruction& operator=(const Instruction& instr) { 512 Instruction& operator=(const Instruction& instr) {
504 value = instr.value; 513 value = instr.value;
@@ -587,6 +596,7 @@ union Instruction {
587 } alu; 596 } alu;
588 597
589 union { 598 union {
599 BitField<38, 1, u64> idx;
590 BitField<51, 1, u64> saturate; 600 BitField<51, 1, u64> saturate;
591 BitField<52, 2, IpaSampleMode> sample_mode; 601 BitField<52, 2, IpaSampleMode> sample_mode;
592 BitField<54, 2, IpaInterpMode> interp_mode; 602 BitField<54, 2, IpaInterpMode> interp_mode;
@@ -812,6 +822,12 @@ union Instruction {
812 } stg; 822 } stg;
813 823
814 union { 824 union {
825 BitField<32, 1, PhysicalAttributeDirection> direction;
826 BitField<47, 3, AttributeSize> size;
827 BitField<20, 11, u64> address;
828 } al2p;
829
830 union {
815 BitField<0, 3, u64> pred0; 831 BitField<0, 3, u64> pred0;
816 BitField<3, 3, u64> pred3; 832 BitField<3, 3, u64> pred3;
817 BitField<7, 1, u64> abs_a; 833 BitField<7, 1, u64> abs_a;
@@ -1374,8 +1390,9 @@ public:
1374 ST_A, 1390 ST_A,
1375 ST_L, 1391 ST_L,
1376 ST_S, 1392 ST_S,
1377 LDG, // Load from global memory 1393 LDG, // Load from global memory
1378 STG, // Store in global memory 1394 STG, // Store in global memory
1395 AL2P, // Transforms attribute memory into physical memory
1379 TEX, 1396 TEX,
1380 TEX_B, // Texture Load Bindless 1397 TEX_B, // Texture Load Bindless
1381 TXQ, // Texture Query 1398 TXQ, // Texture Query
@@ -1646,6 +1663,7 @@ private:
1646 INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), 1663 INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
1647 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), 1664 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
1648 INST("1110111011011---", Id::STG, Type::Memory, "STG"), 1665 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
1666 INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
1649 INST("110000----111---", Id::TEX, Type::Texture, "TEX"), 1667 INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
1650 INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"), 1668 INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
1651 INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"), 1669 INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 03856013f..1e2ff46b0 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -118,7 +118,7 @@ void SynchState::WaitForSynchronization(u64 fence) {
118 // Wait for the GPU to be idle (all commands to be executed) 118 // Wait for the GPU to be idle (all commands to be executed)
119 { 119 {
120 MICROPROFILE_SCOPE(GPU_wait); 120 MICROPROFILE_SCOPE(GPU_wait);
121 std::unique_lock<std::mutex> lock{synchronization_mutex}; 121 std::unique_lock lock{synchronization_mutex};
122 synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; }); 122 synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; });
123 } 123 }
124} 124}
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index cc14527c7..05a168a72 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -81,12 +81,6 @@ struct CommandDataContainer {
81 CommandDataContainer(CommandData&& data, u64 next_fence) 81 CommandDataContainer(CommandData&& data, u64 next_fence)
82 : data{std::move(data)}, fence{next_fence} {} 82 : data{std::move(data)}, fence{next_fence} {}
83 83
84 CommandDataContainer& operator=(const CommandDataContainer& t) {
85 data = std::move(t.data);
86 fence = t.fence;
87 return *this;
88 }
89
90 CommandData data; 84 CommandData data;
91 u64 fence{}; 85 u64 fence{};
92}; 86};
@@ -109,7 +103,7 @@ struct SynchState final {
109 103
110 void TrySynchronize() { 104 void TrySynchronize() {
111 if (IsSynchronized()) { 105 if (IsSynchronized()) {
112 std::lock_guard<std::mutex> lock{synchronization_mutex}; 106 std::lock_guard lock{synchronization_mutex};
113 synchronization_condition.notify_one(); 107 synchronization_condition.notify_one();
114 } 108 }
115 } 109 }
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 524d9ea5a..c766ed692 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -118,10 +118,12 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
118 static_cast<u32>(opcode.operation.Value())); 118 static_cast<u32>(opcode.operation.Value()));
119 } 119 }
120 120
121 // An instruction with the Exit flag will not actually
122 // cause an exit if it's executed inside a delay slot.
123 // TODO(Blinkhawk): Reversed to always exit. The behavior explained above requires further
124 // testing on the MME code.
121 if (opcode.is_exit) { 125 if (opcode.is_exit) {
122 // Exit has a delay slot, execute the next instruction 126 // Exit has a delay slot, execute the next instruction
123 // Note: Executing an exit during a branch delay slot will cause the instruction at the
124 // branch target to be executed before exiting.
125 Step(offset, true); 127 Step(offset, true);
126 return false; 128 return false;
127 } 129 }
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index f820f3ed9..0c4ea1494 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -144,8 +144,9 @@ protected:
144 144
145 object->SetIsRegistered(false); 145 object->SetIsRegistered(false);
146 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1); 146 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
147 const CacheAddr addr = object->GetCacheAddr();
147 interval_cache.subtract({GetInterval(object), ObjectSet{object}}); 148 interval_cache.subtract({GetInterval(object), ObjectSet{object}});
148 map_cache.erase(object->GetCacheAddr()); 149 map_cache.erase(addr);
149 } 150 }
150 151
151 /// Returns a ticks counter used for tracking when cached objects were last modified 152 /// Returns a ticks counter used for tracking when cached objects were last modified
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index b6d9e0ddb..38497678a 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -21,9 +21,18 @@ T GetInteger(GLenum pname) {
21 21
22Device::Device() { 22Device::Device() {
23 uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); 23 uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
24 max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
25 max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
24 has_variable_aoffi = TestVariableAoffi(); 26 has_variable_aoffi = TestVariableAoffi();
25} 27}
26 28
29Device::Device(std::nullptr_t) {
30 uniform_buffer_alignment = 0;
31 max_vertex_attributes = 16;
32 max_varyings = 15;
33 has_variable_aoffi = true;
34}
35
27bool Device::TestVariableAoffi() { 36bool Device::TestVariableAoffi() {
28 const GLchar* AOFFI_TEST = R"(#version 430 core 37 const GLchar* AOFFI_TEST = R"(#version 430 core
29uniform sampler2D tex; 38uniform sampler2D tex;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 78ff5ee58..de8490682 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -5,17 +5,27 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include "common/common_types.h"
8 9
9namespace OpenGL { 10namespace OpenGL {
10 11
11class Device { 12class Device {
12public: 13public:
13 Device(); 14 explicit Device();
15 explicit Device(std::nullptr_t);
14 16
15 std::size_t GetUniformBufferAlignment() const { 17 std::size_t GetUniformBufferAlignment() const {
16 return uniform_buffer_alignment; 18 return uniform_buffer_alignment;
17 } 19 }
18 20
21 u32 GetMaxVertexAttributes() const {
22 return max_vertex_attributes;
23 }
24
25 u32 GetMaxVaryings() const {
26 return max_varyings;
27 }
28
19 bool HasVariableAoffi() const { 29 bool HasVariableAoffi() const {
20 return has_variable_aoffi; 30 return has_variable_aoffi;
21 } 31 }
@@ -24,6 +34,8 @@ private:
24 static bool TestVariableAoffi(); 34 static bool TestVariableAoffi();
25 35
26 std::size_t uniform_buffer_alignment{}; 36 std::size_t uniform_buffer_alignment{};
37 u32 max_vertex_attributes{};
38 u32 max_varyings{};
27 bool has_variable_aoffi{}; 39 bool has_variable_aoffi{};
28}; 40};
29 41
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3cc945235..dbd8049f5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -261,8 +261,8 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
261 // MakeQuadArray always generates u32 indexes 261 // MakeQuadArray always generates u32 indexes
262 params.index_format = GL_UNSIGNED_INT; 262 params.index_format = GL_UNSIGNED_INT;
263 params.count = (regs.vertex_buffer.count / 4) * 6; 263 params.count = (regs.vertex_buffer.count / 4) * 6;
264 params.index_buffer_offset = 264 params.index_buffer_offset = primitive_assembler.MakeQuadArray(
265 primitive_assembler.MakeQuadArray(regs.vertex_buffer.first, params.count); 265 regs.vertex_buffer.first, regs.vertex_buffer.count);
266 } 266 }
267 return params; 267 return params;
268 } 268 }
@@ -1135,7 +1135,9 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1135 1135
1136void RasterizerOpenGL::SyncPointState() { 1136void RasterizerOpenGL::SyncPointState() {
1137 const auto& regs = system.GPU().Maxwell3D().regs; 1137 const auto& regs = system.GPU().Maxwell3D().regs;
1138 state.point.size = regs.point_size; 1138 // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
1139 // in OpenGL).
1140 state.point.size = std::max(1.0f, regs.point_size);
1139} 1141}
1140 1142
1141void RasterizerOpenGL::SyncPolygonOffset() { 1143void RasterizerOpenGL::SyncPolygonOffset() {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 1a62795e1..6d4658c8b 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -57,15 +57,14 @@ public:
57 shader_source += text; 57 shader_source += text;
58 } 58 }
59 59
60 void AddLine(std::string_view text) { 60 // Forwards all arguments directly to libfmt.
61 AddExpression(text); 61 // Note that all formatting requirements for fmt must be
62 AddNewLine(); 62 // obeyed when using this function. (e.g. {{ must be used
63 } 63 // printing the character '{' is desirable. Ditto for }} and '}',
64 64 // etc).
65 void AddLine(char character) { 65 template <typename... Args>
66 DEBUG_ASSERT(scope >= 0); 66 void AddLine(std::string_view text, Args&&... args) {
67 AppendIndentation(); 67 AddExpression(fmt::format(text, std::forward<Args>(args)...));
68 shader_source += character;
69 AddNewLine(); 68 AddNewLine();
70 } 69 }
71 70
@@ -75,9 +74,7 @@ public:
75 } 74 }
76 75
77 std::string GenerateTemporary() { 76 std::string GenerateTemporary() {
78 std::string temporary = "tmp"; 77 return fmt::format("tmp{}", temporary_index++);
79 temporary += std::to_string(temporary_index++);
80 return temporary;
81 } 78 }
82 79
83 std::string GetResult() { 80 std::string GetResult() {
@@ -134,6 +131,19 @@ bool IsPrecise(Node node) {
134 return false; 131 return false;
135} 132}
136 133
134constexpr bool IsGenericAttribute(Attribute::Index index) {
135 return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
136}
137
138constexpr Attribute::Index ToGenericAttribute(u32 value) {
139 return static_cast<Attribute::Index>(value + static_cast<u32>(Attribute::Index::Attribute_0));
140}
141
142u32 GetGenericAttributeIndex(Attribute::Index index) {
143 ASSERT(IsGenericAttribute(index));
144 return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
145}
146
137class GLSLDecompiler final { 147class GLSLDecompiler final {
138public: 148public:
139 explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage, 149 explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage,
@@ -152,42 +162,43 @@ public:
152 DeclareConstantBuffers(); 162 DeclareConstantBuffers();
153 DeclareGlobalMemory(); 163 DeclareGlobalMemory();
154 DeclareSamplers(); 164 DeclareSamplers();
165 DeclarePhysicalAttributeReader();
155 166
156 code.AddLine("void execute_" + suffix + "() {"); 167 code.AddLine("void execute_{}() {{", suffix);
157 ++code.scope; 168 ++code.scope;
158 169
159 // VM's program counter 170 // VM's program counter
160 const auto first_address = ir.GetBasicBlocks().begin()->first; 171 const auto first_address = ir.GetBasicBlocks().begin()->first;
161 code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;"); 172 code.AddLine("uint jmp_to = {}u;", first_address);
162 173
163 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems 174 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
164 // unlikely that shaders will use 20 nested SSYs and PBKs. 175 // unlikely that shaders will use 20 nested SSYs and PBKs.
165 constexpr u32 FLOW_STACK_SIZE = 20; 176 constexpr u32 FLOW_STACK_SIZE = 20;
166 code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE)); 177 code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE);
167 code.AddLine("uint flow_stack_top = 0u;"); 178 code.AddLine("uint flow_stack_top = 0u;");
168 179
169 code.AddLine("while (true) {"); 180 code.AddLine("while (true) {{");
170 ++code.scope; 181 ++code.scope;
171 182
172 code.AddLine("switch (jmp_to) {"); 183 code.AddLine("switch (jmp_to) {{");
173 184
174 for (const auto& pair : ir.GetBasicBlocks()) { 185 for (const auto& pair : ir.GetBasicBlocks()) {
175 const auto [address, bb] = pair; 186 const auto [address, bb] = pair;
176 code.AddLine(fmt::format("case 0x{:x}u: {{", address)); 187 code.AddLine("case 0x{:x}u: {{", address);
177 ++code.scope; 188 ++code.scope;
178 189
179 VisitBlock(bb); 190 VisitBlock(bb);
180 191
181 --code.scope; 192 --code.scope;
182 code.AddLine('}'); 193 code.AddLine("}}");
183 } 194 }
184 195
185 code.AddLine("default: return;"); 196 code.AddLine("default: return;");
186 code.AddLine('}'); 197 code.AddLine("}}");
187 198
188 for (std::size_t i = 0; i < 2; ++i) { 199 for (std::size_t i = 0; i < 2; ++i) {
189 --code.scope; 200 --code.scope;
190 code.AddLine('}'); 201 code.AddLine("}}");
191 } 202 }
192 } 203 }
193 204
@@ -227,12 +238,13 @@ private:
227 } 238 }
228 239
229 void DeclareGeometry() { 240 void DeclareGeometry() {
230 if (stage != ShaderStage::Geometry) 241 if (stage != ShaderStage::Geometry) {
231 return; 242 return;
243 }
232 244
233 const auto topology = GetTopologyName(header.common3.output_topology); 245 const auto topology = GetTopologyName(header.common3.output_topology);
234 const auto max_vertices = std::to_string(header.common4.max_output_vertices); 246 const auto max_vertices = header.common4.max_output_vertices.Value();
235 code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;"); 247 code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
236 code.AddNewLine(); 248 code.AddNewLine();
237 249
238 DeclareVertexRedeclarations(); 250 DeclareVertexRedeclarations();
@@ -241,7 +253,7 @@ private:
241 void DeclareVertexRedeclarations() { 253 void DeclareVertexRedeclarations() {
242 bool clip_distances_declared = false; 254 bool clip_distances_declared = false;
243 255
244 code.AddLine("out gl_PerVertex {"); 256 code.AddLine("out gl_PerVertex {{");
245 ++code.scope; 257 ++code.scope;
246 258
247 code.AddLine("vec4 gl_Position;"); 259 code.AddLine("vec4 gl_Position;");
@@ -257,122 +269,143 @@ private:
257 } 269 }
258 270
259 --code.scope; 271 --code.scope;
260 code.AddLine("};"); 272 code.AddLine("}};");
261 code.AddNewLine(); 273 code.AddNewLine();
262 } 274 }
263 275
264 void DeclareRegisters() { 276 void DeclareRegisters() {
265 const auto& registers = ir.GetRegisters(); 277 const auto& registers = ir.GetRegisters();
266 for (const u32 gpr : registers) { 278 for (const u32 gpr : registers) {
267 code.AddLine("float " + GetRegister(gpr) + " = 0;"); 279 code.AddLine("float {} = 0;", GetRegister(gpr));
268 } 280 }
269 if (!registers.empty()) 281 if (!registers.empty()) {
270 code.AddNewLine(); 282 code.AddNewLine();
283 }
271 } 284 }
272 285
273 void DeclarePredicates() { 286 void DeclarePredicates() {
274 const auto& predicates = ir.GetPredicates(); 287 const auto& predicates = ir.GetPredicates();
275 for (const auto pred : predicates) { 288 for (const auto pred : predicates) {
276 code.AddLine("bool " + GetPredicate(pred) + " = false;"); 289 code.AddLine("bool {} = false;", GetPredicate(pred));
277 } 290 }
278 if (!predicates.empty()) 291 if (!predicates.empty()) {
279 code.AddNewLine(); 292 code.AddNewLine();
293 }
280 } 294 }
281 295
282 void DeclareLocalMemory() { 296 void DeclareLocalMemory() {
283 if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { 297 if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) {
284 const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; 298 const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
285 code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];"); 299 code.AddLine("float {}[{}];", GetLocalMemory(), element_count);
286 code.AddNewLine(); 300 code.AddNewLine();
287 } 301 }
288 } 302 }
289 303
290 void DeclareInternalFlags() { 304 void DeclareInternalFlags() {
291 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { 305 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
292 const InternalFlag flag_code = static_cast<InternalFlag>(flag); 306 const auto flag_code = static_cast<InternalFlag>(flag);
293 code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;"); 307 code.AddLine("bool {} = false;", GetInternalFlag(flag_code));
294 } 308 }
295 code.AddNewLine(); 309 code.AddNewLine();
296 } 310 }
297 311
298 std::string GetInputFlags(AttributeUse attribute) { 312 std::string GetInputFlags(AttributeUse attribute) {
299 std::string out;
300
301 switch (attribute) { 313 switch (attribute) {
302 case AttributeUse::Constant:
303 out += "flat ";
304 break;
305 case AttributeUse::ScreenLinear:
306 out += "noperspective ";
307 break;
308 case AttributeUse::Perspective: 314 case AttributeUse::Perspective:
309 // Default, Smooth 315 // Default, Smooth
310 break; 316 return {};
317 case AttributeUse::Constant:
318 return "flat ";
319 case AttributeUse::ScreenLinear:
320 return "noperspective ";
311 default: 321 default:
312 LOG_CRITICAL(HW_GPU, "Unused attribute being fetched"); 322 case AttributeUse::Unused:
313 UNREACHABLE(); 323 UNREACHABLE_MSG("Unused attribute being fetched");
324 return {};
325 UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
326 return {};
314 } 327 }
315 return out;
316 } 328 }
317 329
318 void DeclareInputAttributes() { 330 void DeclareInputAttributes() {
319 const auto& attributes = ir.GetInputAttributes(); 331 if (ir.HasPhysicalAttributes()) {
320 for (const auto element : attributes) { 332 const u32 num_inputs{GetNumPhysicalInputAttributes()};
321 const Attribute::Index index = element.first; 333 for (u32 i = 0; i < num_inputs; ++i) {
322 if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { 334 DeclareInputAttribute(ToGenericAttribute(i), true);
323 // Skip when it's not a generic attribute
324 continue;
325 } 335 }
336 code.AddNewLine();
337 return;
338 }
326 339
327 // TODO(bunnei): Use proper number of elements for these 340 const auto& attributes = ir.GetInputAttributes();
328 u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0); 341 for (const auto index : attributes) {
329 if (stage != ShaderStage::Vertex) { 342 if (IsGenericAttribute(index)) {
330 // If inputs are varyings, add an offset 343 DeclareInputAttribute(index, false);
331 idx += GENERIC_VARYING_START_LOCATION;
332 } 344 }
345 }
346 if (!attributes.empty()) {
347 code.AddNewLine();
348 }
349 }
333 350
334 std::string attr = GetInputAttribute(index); 351 void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
335 if (stage == ShaderStage::Geometry) { 352 const u32 generic_index{GetGenericAttributeIndex(index)};
336 attr = "gs_" + attr + "[]"; 353
337 } 354 std::string name{GetInputAttribute(index)};
338 std::string suffix; 355 if (stage == ShaderStage::Geometry) {
339 if (stage == ShaderStage::Fragment) { 356 name = "gs_" + name + "[]";
340 const auto input_mode = 357 }
341 header.ps.GetAttributeUse(idx - GENERIC_VARYING_START_LOCATION); 358
342 suffix = GetInputFlags(input_mode); 359 std::string suffix;
360 if (stage == ShaderStage::Fragment) {
361 const auto input_mode{header.ps.GetAttributeUse(generic_index)};
362 if (skip_unused && input_mode == AttributeUse::Unused) {
363 return;
343 } 364 }
344 code.AddLine("layout (location = " + std::to_string(idx) + ") " + suffix + "in vec4 " + 365 suffix = GetInputFlags(input_mode);
345 attr + ';');
346 } 366 }
347 if (!attributes.empty()) 367
348 code.AddNewLine(); 368 u32 location = generic_index;
369 if (stage != ShaderStage::Vertex) {
370 // If inputs are varyings, add an offset
371 location += GENERIC_VARYING_START_LOCATION;
372 }
373
374 code.AddLine("layout (location = {}) {} in vec4 {};", location, suffix, name);
349 } 375 }
350 376
351 void DeclareOutputAttributes() { 377 void DeclareOutputAttributes() {
378 if (ir.HasPhysicalAttributes() && stage != ShaderStage::Fragment) {
379 for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
380 DeclareOutputAttribute(ToGenericAttribute(i));
381 }
382 code.AddNewLine();
383 return;
384 }
385
352 const auto& attributes = ir.GetOutputAttributes(); 386 const auto& attributes = ir.GetOutputAttributes();
353 for (const auto index : attributes) { 387 for (const auto index : attributes) {
354 if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { 388 if (IsGenericAttribute(index)) {
355 // Skip when it's not a generic attribute 389 DeclareOutputAttribute(index);
356 continue;
357 } 390 }
358 // TODO(bunnei): Use proper number of elements for these 391 }
359 const auto idx = static_cast<u32>(index) - 392 if (!attributes.empty()) {
360 static_cast<u32>(Attribute::Index::Attribute_0) +
361 GENERIC_VARYING_START_LOCATION;
362 code.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
363 GetOutputAttribute(index) + ';');
364 }
365 if (!attributes.empty())
366 code.AddNewLine(); 393 code.AddNewLine();
394 }
395 }
396
397 void DeclareOutputAttribute(Attribute::Index index) {
398 const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION};
399 code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
367 } 400 }
368 401
369 void DeclareConstantBuffers() { 402 void DeclareConstantBuffers() {
370 for (const auto& entry : ir.GetConstantBuffers()) { 403 for (const auto& entry : ir.GetConstantBuffers()) {
371 const auto [index, size] = entry; 404 const auto [index, size] = entry;
372 code.AddLine("layout (std140, binding = CBUF_BINDING_" + std::to_string(index) + 405 code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index,
373 ") uniform " + GetConstBufferBlock(index) + " {"); 406 GetConstBufferBlock(index));
374 code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];"); 407 code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index));
375 code.AddLine("};"); 408 code.AddLine("}};");
376 code.AddNewLine(); 409 code.AddNewLine();
377 } 410 }
378 } 411 }
@@ -384,17 +417,16 @@ private:
384 // Since we don't know how the shader will use the shader, hint the driver to disable as 417 // Since we don't know how the shader will use the shader, hint the driver to disable as
385 // much optimizations as possible 418 // much optimizations as possible
386 std::string qualifier = "coherent volatile"; 419 std::string qualifier = "coherent volatile";
387 if (usage.is_read && !usage.is_written) 420 if (usage.is_read && !usage.is_written) {
388 qualifier += " readonly"; 421 qualifier += " readonly";
389 else if (usage.is_written && !usage.is_read) 422 } else if (usage.is_written && !usage.is_read) {
390 qualifier += " writeonly"; 423 qualifier += " writeonly";
424 }
391 425
392 const std::string binding = 426 code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{",
393 fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset); 427 base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base));
394 code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " + 428 code.AddLine(" float {}[];", GetGlobalMemory(base));
395 GetGlobalMemoryBlock(base) + " {"); 429 code.AddLine("}};");
396 code.AddLine(" float " + GetGlobalMemory(base) + "[];");
397 code.AddLine("};");
398 code.AddNewLine(); 430 code.AddNewLine();
399 } 431 }
400 } 432 }
@@ -402,7 +434,7 @@ private:
402 void DeclareSamplers() { 434 void DeclareSamplers() {
403 const auto& samplers = ir.GetSamplers(); 435 const auto& samplers = ir.GetSamplers();
404 for (const auto& sampler : samplers) { 436 for (const auto& sampler : samplers) {
405 std::string sampler_type = [&]() { 437 std::string sampler_type = [&sampler] {
406 switch (sampler.GetType()) { 438 switch (sampler.GetType()) {
407 case Tegra::Shader::TextureType::Texture1D: 439 case Tegra::Shader::TextureType::Texture1D:
408 return "sampler1D"; 440 return "sampler1D";
@@ -417,16 +449,52 @@ private:
417 return "sampler2D"; 449 return "sampler2D";
418 } 450 }
419 }(); 451 }();
420 if (sampler.IsArray()) 452 if (sampler.IsArray()) {
421 sampler_type += "Array"; 453 sampler_type += "Array";
422 if (sampler.IsShadow()) 454 }
455 if (sampler.IsShadow()) {
423 sampler_type += "Shadow"; 456 sampler_type += "Shadow";
457 }
424 458
425 code.AddLine("layout (binding = SAMPLER_BINDING_" + std::to_string(sampler.GetIndex()) + 459 code.AddLine("layout (binding = SAMPLER_BINDING_{}) uniform {} {};", sampler.GetIndex(),
426 ") uniform " + sampler_type + ' ' + GetSampler(sampler) + ';'); 460 sampler_type, GetSampler(sampler));
427 } 461 }
428 if (!samplers.empty()) 462 if (!samplers.empty()) {
429 code.AddNewLine(); 463 code.AddNewLine();
464 }
465 }
466
467 void DeclarePhysicalAttributeReader() {
468 if (!ir.HasPhysicalAttributes()) {
469 return;
470 }
471 code.AddLine("float readPhysicalAttribute(uint physical_address) {{");
472 ++code.scope;
473 code.AddLine("switch (physical_address) {{");
474
475 // Just declare generic attributes for now.
476 const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())};
477 for (u32 index = 0; index < num_attributes; ++index) {
478 const auto attribute{ToGenericAttribute(index)};
479 for (u32 element = 0; element < 4; ++element) {
480 constexpr u32 generic_base{0x80};
481 constexpr u32 generic_stride{16};
482 constexpr u32 element_stride{4};
483 const u32 address{generic_base + index * generic_stride + element * element_stride};
484
485 const bool declared{stage != ShaderStage::Fragment ||
486 header.ps.GetAttributeUse(index) != AttributeUse::Unused};
487 const std::string value{declared ? ReadAttribute(attribute, element) : "0"};
488 code.AddLine("case 0x{:x}: return {};", address, value);
489 }
490 }
491
492 code.AddLine("default: return 0;");
493
494 code.AddLine("}}");
495 --code.scope;
496 code.AddLine("}}");
497 code.AddNewLine();
430 } 498 }
431 499
432 void VisitBlock(const NodeBlock& bb) { 500 void VisitBlock(const NodeBlock& bb) {
@@ -450,23 +518,26 @@ private:
450 return {}; 518 return {};
451 } 519 }
452 return (this->*decompiler)(*operation); 520 return (this->*decompiler)(*operation);
521 }
453 522
454 } else if (const auto gpr = std::get_if<GprNode>(node)) { 523 if (const auto gpr = std::get_if<GprNode>(node)) {
455 const u32 index = gpr->GetIndex(); 524 const u32 index = gpr->GetIndex();
456 if (index == Register::ZeroIndex) { 525 if (index == Register::ZeroIndex) {
457 return "0"; 526 return "0";
458 } 527 }
459 return GetRegister(index); 528 return GetRegister(index);
529 }
460 530
461 } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { 531 if (const auto immediate = std::get_if<ImmediateNode>(node)) {
462 const u32 value = immediate->GetValue(); 532 const u32 value = immediate->GetValue();
463 if (value < 10) { 533 if (value < 10) {
464 // For eyecandy avoid using hex numbers on single digits 534 // For eyecandy avoid using hex numbers on single digits
465 return fmt::format("utof({}u)", immediate->GetValue()); 535 return fmt::format("utof({}u)", immediate->GetValue());
466 } 536 }
467 return fmt::format("utof(0x{:x}u)", immediate->GetValue()); 537 return fmt::format("utof(0x{:x}u)", immediate->GetValue());
538 }
468 539
469 } else if (const auto predicate = std::get_if<PredicateNode>(node)) { 540 if (const auto predicate = std::get_if<PredicateNode>(node)) {
470 const auto value = [&]() -> std::string { 541 const auto value = [&]() -> std::string {
471 switch (const auto index = predicate->GetIndex(); index) { 542 switch (const auto index = predicate->GetIndex(); index) {
472 case Tegra::Shader::Pred::UnusedIndex: 543 case Tegra::Shader::Pred::UnusedIndex:
@@ -478,77 +549,22 @@ private:
478 } 549 }
479 }(); 550 }();
480 if (predicate->IsNegated()) { 551 if (predicate->IsNegated()) {
481 return "!(" + value + ')'; 552 return fmt::format("!({})", value);
482 } 553 }
483 return value; 554 return value;
555 }
484 556
485 } else if (const auto abuf = std::get_if<AbufNode>(node)) { 557 if (const auto abuf = std::get_if<AbufNode>(node)) {
486 const auto attribute = abuf->GetIndex(); 558 UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
487 const auto element = abuf->GetElement(); 559 "Physical attributes in geometry shaders are not implemented");
488 560 if (abuf->IsPhysicalBuffer()) {
489 const auto GeometryPass = [&](const std::string& name) { 561 return fmt::format("readPhysicalAttribute(ftou({}))",
490 if (stage == ShaderStage::Geometry && abuf->GetBuffer()) { 562 Visit(abuf->GetPhysicalAddress()));
491 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
492 // set an 0x80000000 index for those and the shader fails to build. Find out why
493 // this happens and what's its intent.
494 return "gs_" + name + "[ftou(" + Visit(abuf->GetBuffer()) +
495 ") % MAX_VERTEX_INPUT]";
496 }
497 return name;
498 };
499
500 switch (attribute) {
501 case Attribute::Index::Position:
502 if (stage != ShaderStage::Fragment) {
503 return GeometryPass("position") + GetSwizzle(element);
504 } else {
505 return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
506 }
507 case Attribute::Index::PointCoord:
508 switch (element) {
509 case 0:
510 return "gl_PointCoord.x";
511 case 1:
512 return "gl_PointCoord.y";
513 case 2:
514 case 3:
515 return "0";
516 }
517 UNREACHABLE();
518 return "0";
519 case Attribute::Index::TessCoordInstanceIDVertexID:
520 // TODO(Subv): Find out what the values are for the first two elements when inside a
521 // vertex shader, and what's the value of the fourth element when inside a Tess Eval
522 // shader.
523 ASSERT(stage == ShaderStage::Vertex);
524 switch (element) {
525 case 2:
526 // Config pack's first value is instance_id.
527 return "uintBitsToFloat(config_pack[0])";
528 case 3:
529 return "uintBitsToFloat(gl_VertexID)";
530 }
531 UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
532 return "0";
533 case Attribute::Index::FrontFacing:
534 // TODO(Subv): Find out what the values are for the other elements.
535 ASSERT(stage == ShaderStage::Fragment);
536 switch (element) {
537 case 3:
538 return "itof(gl_FrontFacing ? -1 : 0)";
539 }
540 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
541 return "0";
542 default:
543 if (attribute >= Attribute::Index::Attribute_0 &&
544 attribute <= Attribute::Index::Attribute_31) {
545 return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
546 }
547 break;
548 } 563 }
549 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); 564 return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
565 }
550 566
551 } else if (const auto cbuf = std::get_if<CbufNode>(node)) { 567 if (const auto cbuf = std::get_if<CbufNode>(node)) {
552 const Node offset = cbuf->GetOffset(); 568 const Node offset = cbuf->GetOffset();
553 if (const auto immediate = std::get_if<ImmediateNode>(offset)) { 569 if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
554 // Direct access 570 // Direct access
@@ -556,48 +572,117 @@ private:
556 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); 572 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
557 return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), 573 return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
558 offset_imm / (4 * 4), (offset_imm / 4) % 4); 574 offset_imm / (4 * 4), (offset_imm / 4) % 4);
575 }
559 576
560 } else if (std::holds_alternative<OperationNode>(*offset)) { 577 if (std::holds_alternative<OperationNode>(*offset)) {
561 // Indirect access 578 // Indirect access
562 const std::string final_offset = code.GenerateTemporary(); 579 const std::string final_offset = code.GenerateTemporary();
563 code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);"); 580 code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset));
564 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), 581 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
565 final_offset, final_offset); 582 final_offset, final_offset);
566
567 } else {
568 UNREACHABLE_MSG("Unmanaged offset node type");
569 } 583 }
570 584
571 } else if (const auto gmem = std::get_if<GmemNode>(node)) { 585 UNREACHABLE_MSG("Unmanaged offset node type");
586 }
587
588 if (const auto gmem = std::get_if<GmemNode>(node)) {
572 const std::string real = Visit(gmem->GetRealAddress()); 589 const std::string real = Visit(gmem->GetRealAddress());
573 const std::string base = Visit(gmem->GetBaseAddress()); 590 const std::string base = Visit(gmem->GetBaseAddress());
574 const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; 591 const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
575 return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); 592 return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
593 }
576 594
577 } else if (const auto lmem = std::get_if<LmemNode>(node)) { 595 if (const auto lmem = std::get_if<LmemNode>(node)) {
578 return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); 596 return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
597 }
579 598
580 } else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { 599 if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
581 return GetInternalFlag(internal_flag->GetFlag()); 600 return GetInternalFlag(internal_flag->GetFlag());
601 }
582 602
583 } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { 603 if (const auto conditional = std::get_if<ConditionalNode>(node)) {
584 // It's invalid to call conditional on nested nodes, use an operation instead 604 // It's invalid to call conditional on nested nodes, use an operation instead
585 code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {"); 605 code.AddLine("if ({}) {{", Visit(conditional->GetCondition()));
586 ++code.scope; 606 ++code.scope;
587 607
588 VisitBlock(conditional->GetCode()); 608 VisitBlock(conditional->GetCode());
589 609
590 --code.scope; 610 --code.scope;
591 code.AddLine('}'); 611 code.AddLine("}}");
592 return {}; 612 return {};
613 }
593 614
594 } else if (const auto comment = std::get_if<CommentNode>(node)) { 615 if (const auto comment = std::get_if<CommentNode>(node)) {
595 return "// " + comment->GetText(); 616 return "// " + comment->GetText();
596 } 617 }
618
597 UNREACHABLE(); 619 UNREACHABLE();
598 return {}; 620 return {};
599 } 621 }
600 622
623 std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
624 const auto GeometryPass = [&](std::string_view name) {
625 if (stage == ShaderStage::Geometry && buffer) {
626 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
627 // set an 0x80000000 index for those and the shader fails to build. Find out why
628 // this happens and what's its intent.
629 return fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer));
630 }
631 return std::string(name);
632 };
633
634 switch (attribute) {
635 case Attribute::Index::Position:
636 if (stage != ShaderStage::Fragment) {
637 return GeometryPass("position") + GetSwizzle(element);
638 } else {
639 return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
640 }
641 case Attribute::Index::PointCoord:
642 switch (element) {
643 case 0:
644 return "gl_PointCoord.x";
645 case 1:
646 return "gl_PointCoord.y";
647 case 2:
648 case 3:
649 return "0";
650 }
651 UNREACHABLE();
652 return "0";
653 case Attribute::Index::TessCoordInstanceIDVertexID:
654 // TODO(Subv): Find out what the values are for the first two elements when inside a
655 // vertex shader, and what's the value of the fourth element when inside a Tess Eval
656 // shader.
657 ASSERT(stage == ShaderStage::Vertex);
658 switch (element) {
659 case 2:
660 // Config pack's first value is instance_id.
661 return "uintBitsToFloat(config_pack[0])";
662 case 3:
663 return "uintBitsToFloat(gl_VertexID)";
664 }
665 UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
666 return "0";
667 case Attribute::Index::FrontFacing:
668 // TODO(Subv): Find out what the values are for the other elements.
669 ASSERT(stage == ShaderStage::Fragment);
670 switch (element) {
671 case 3:
672 return "itof(gl_FrontFacing ? -1 : 0)";
673 }
674 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
675 return "0";
676 default:
677 if (IsGenericAttribute(attribute)) {
678 return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
679 }
680 break;
681 }
682 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
683 return "0";
684 }
685
601 std::string ApplyPrecise(Operation operation, const std::string& value) { 686 std::string ApplyPrecise(Operation operation, const std::string& value) {
602 if (!IsPrecise(operation)) { 687 if (!IsPrecise(operation)) {
603 return value; 688 return value;
@@ -606,7 +691,7 @@ private:
606 const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; 691 const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
607 692
608 const std::string temporary = code.GenerateTemporary(); 693 const std::string temporary = code.GenerateTemporary();
609 code.AddLine(precise + "float " + temporary + " = " + value + ';'); 694 code.AddLine("{}float {} = {};", precise, temporary, value);
610 return temporary; 695 return temporary;
611 } 696 }
612 697
@@ -620,7 +705,7 @@ private:
620 } 705 }
621 706
622 const std::string temporary = code.GenerateTemporary(); 707 const std::string temporary = code.GenerateTemporary();
623 code.AddLine("float " + temporary + " = " + Visit(operand) + ';'); 708 code.AddLine("float {} = {};", temporary, Visit(operand));
624 return temporary; 709 return temporary;
625 } 710 }
626 711
@@ -635,31 +720,32 @@ private:
635 case Type::Float: 720 case Type::Float:
636 return value; 721 return value;
637 case Type::Int: 722 case Type::Int:
638 return "ftoi(" + value + ')'; 723 return fmt::format("ftoi({})", value);
639 case Type::Uint: 724 case Type::Uint:
640 return "ftou(" + value + ')'; 725 return fmt::format("ftou({})", value);
641 case Type::HalfFloat: 726 case Type::HalfFloat:
642 return "toHalf2(" + value + ')'; 727 return fmt::format("toHalf2({})", value);
643 } 728 }
644 UNREACHABLE(); 729 UNREACHABLE();
645 return value; 730 return value;
646 } 731 }
647 732
648 std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) { 733 std::string BitwiseCastResult(const std::string& value, Type type,
734 bool needs_parenthesis = false) {
649 switch (type) { 735 switch (type) {
650 case Type::Bool: 736 case Type::Bool:
651 case Type::Bool2: 737 case Type::Bool2:
652 case Type::Float: 738 case Type::Float:
653 if (needs_parenthesis) { 739 if (needs_parenthesis) {
654 return '(' + value + ')'; 740 return fmt::format("({})", value);
655 } 741 }
656 return value; 742 return value;
657 case Type::Int: 743 case Type::Int:
658 return "itof(" + value + ')'; 744 return fmt::format("itof({})", value);
659 case Type::Uint: 745 case Type::Uint:
660 return "utof(" + value + ')'; 746 return fmt::format("utof({})", value);
661 case Type::HalfFloat: 747 case Type::HalfFloat:
662 return "fromHalf2(" + value + ')'; 748 return fmt::format("fromHalf2({})", value);
663 } 749 }
664 UNREACHABLE(); 750 UNREACHABLE();
665 return value; 751 return value;
@@ -667,27 +753,27 @@ private:
667 753
668 std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, 754 std::string GenerateUnary(Operation operation, const std::string& func, Type result_type,
669 Type type_a, bool needs_parenthesis = true) { 755 Type type_a, bool needs_parenthesis = true) {
670 return ApplyPrecise(operation, 756 const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a));
671 BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')', 757
672 result_type, needs_parenthesis)); 758 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis));
673 } 759 }
674 760
675 std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, 761 std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type,
676 Type type_a, Type type_b) { 762 Type type_a, Type type_b) {
677 const std::string op_a = VisitOperand(operation, 0, type_a); 763 const std::string op_a = VisitOperand(operation, 0, type_a);
678 const std::string op_b = VisitOperand(operation, 1, type_b); 764 const std::string op_b = VisitOperand(operation, 1, type_b);
765 const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b);
679 766
680 return ApplyPrecise( 767 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
681 operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type));
682 } 768 }
683 769
684 std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, 770 std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type,
685 Type type_a, Type type_b) { 771 Type type_a, Type type_b) {
686 const std::string op_a = VisitOperand(operation, 0, type_a); 772 const std::string op_a = VisitOperand(operation, 0, type_a);
687 const std::string op_b = VisitOperand(operation, 1, type_b); 773 const std::string op_b = VisitOperand(operation, 1, type_b);
774 const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b);
688 775
689 return ApplyPrecise(operation, 776 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
690 BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type));
691 } 777 }
692 778
693 std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, 779 std::string GenerateTernary(Operation operation, const std::string& func, Type result_type,
@@ -695,10 +781,9 @@ private:
695 const std::string op_a = VisitOperand(operation, 0, type_a); 781 const std::string op_a = VisitOperand(operation, 0, type_a);
696 const std::string op_b = VisitOperand(operation, 1, type_b); 782 const std::string op_b = VisitOperand(operation, 1, type_b);
697 const std::string op_c = VisitOperand(operation, 2, type_c); 783 const std::string op_c = VisitOperand(operation, 2, type_c);
784 const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c);
698 785
699 return ApplyPrecise( 786 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
700 operation,
701 BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type));
702 } 787 }
703 788
704 std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, 789 std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type,
@@ -707,10 +792,9 @@ private:
707 const std::string op_b = VisitOperand(operation, 1, type_b); 792 const std::string op_b = VisitOperand(operation, 1, type_b);
708 const std::string op_c = VisitOperand(operation, 2, type_c); 793 const std::string op_c = VisitOperand(operation, 2, type_c);
709 const std::string op_d = VisitOperand(operation, 3, type_d); 794 const std::string op_d = VisitOperand(operation, 3, type_d);
795 const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d);
710 796
711 return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + 797 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
712 op_c + ", " + op_d + ')',
713 result_type));
714 } 798 }
715 799
716 std::string GenerateTexture(Operation operation, const std::string& function_suffix, 800 std::string GenerateTexture(Operation operation, const std::string& function_suffix,
@@ -773,7 +857,7 @@ private:
773 // required to be constant) 857 // required to be constant)
774 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 858 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
775 } else { 859 } else {
776 expr += "ftoi(" + Visit(operand) + ')'; 860 expr += fmt::format("ftoi({})", Visit(operand));
777 } 861 }
778 break; 862 break;
779 case Type::Float: 863 case Type::Float:
@@ -806,7 +890,7 @@ private:
806 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 890 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
807 } else if (device.HasVariableAoffi()) { 891 } else if (device.HasVariableAoffi()) {
808 // Avoid using variable AOFFI on unsupported devices. 892 // Avoid using variable AOFFI on unsupported devices.
809 expr += "ftoi(" + Visit(operand) + ')'; 893 expr += fmt::format("ftoi({})", Visit(operand));
810 } else { 894 } else {
811 // Insert 0 on devices not supporting variable AOFFI. 895 // Insert 0 on devices not supporting variable AOFFI.
812 expr += '0'; 896 expr += '0';
@@ -831,8 +915,9 @@ private:
831 return {}; 915 return {};
832 } 916 }
833 target = GetRegister(gpr->GetIndex()); 917 target = GetRegister(gpr->GetIndex());
834
835 } else if (const auto abuf = std::get_if<AbufNode>(dest)) { 918 } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
919 UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
920
836 target = [&]() -> std::string { 921 target = [&]() -> std::string {
837 switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) { 922 switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
838 case Attribute::Index::Position: 923 case Attribute::Index::Position:
@@ -840,12 +925,11 @@ private:
840 case Attribute::Index::PointSize: 925 case Attribute::Index::PointSize:
841 return "gl_PointSize"; 926 return "gl_PointSize";
842 case Attribute::Index::ClipDistances0123: 927 case Attribute::Index::ClipDistances0123:
843 return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']'; 928 return fmt::format("gl_ClipDistance[{}]", abuf->GetElement());
844 case Attribute::Index::ClipDistances4567: 929 case Attribute::Index::ClipDistances4567:
845 return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']'; 930 return fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4);
846 default: 931 default:
847 if (attribute >= Attribute::Index::Attribute_0 && 932 if (IsGenericAttribute(attribute)) {
848 attribute <= Attribute::Index::Attribute_31) {
849 return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); 933 return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
850 } 934 }
851 UNIMPLEMENTED_MSG("Unhandled output attribute: {}", 935 UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
@@ -853,21 +937,18 @@ private:
853 return "0"; 937 return "0";
854 } 938 }
855 }(); 939 }();
856
857 } else if (const auto lmem = std::get_if<LmemNode>(dest)) { 940 } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
858 target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; 941 target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
859
860 } else if (const auto gmem = std::get_if<GmemNode>(dest)) { 942 } else if (const auto gmem = std::get_if<GmemNode>(dest)) {
861 const std::string real = Visit(gmem->GetRealAddress()); 943 const std::string real = Visit(gmem->GetRealAddress());
862 const std::string base = Visit(gmem->GetBaseAddress()); 944 const std::string base = Visit(gmem->GetBaseAddress());
863 const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; 945 const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
864 target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); 946 target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
865
866 } else { 947 } else {
867 UNREACHABLE_MSG("Assign called without a proper target"); 948 UNREACHABLE_MSG("Assign called without a proper target");
868 } 949 }
869 950
870 code.AddLine(target + " = " + Visit(src) + ';'); 951 code.AddLine("{} = {};", target, Visit(src));
871 return {}; 952 return {};
872 } 953 }
873 954
@@ -920,8 +1001,9 @@ private:
920 const std::string condition = Visit(operation[0]); 1001 const std::string condition = Visit(operation[0]);
921 const std::string true_case = Visit(operation[1]); 1002 const std::string true_case = Visit(operation[1]);
922 const std::string false_case = Visit(operation[2]); 1003 const std::string false_case = Visit(operation[2]);
923 return ApplyPrecise(operation, 1004 const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case);
924 '(' + condition + " ? " + true_case + " : " + false_case + ')'); 1005
1006 return ApplyPrecise(operation, op_str);
925 } 1007 }
926 1008
927 std::string FCos(Operation operation) { 1009 std::string FCos(Operation operation) {
@@ -985,9 +1067,9 @@ private:
985 std::string ILogicalShiftRight(Operation operation) { 1067 std::string ILogicalShiftRight(Operation operation) {
986 const std::string op_a = VisitOperand(operation, 0, Type::Uint); 1068 const std::string op_a = VisitOperand(operation, 0, Type::Uint);
987 const std::string op_b = VisitOperand(operation, 1, Type::Uint); 1069 const std::string op_b = VisitOperand(operation, 1, Type::Uint);
1070 const std::string op_str = fmt::format("int({} >> {})", op_a, op_b);
988 1071
989 return ApplyPrecise(operation, 1072 return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int));
990 BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int));
991 } 1073 }
992 1074
993 std::string IArithmeticShiftRight(Operation operation) { 1075 std::string IArithmeticShiftRight(Operation operation) {
@@ -1043,11 +1125,12 @@ private:
1043 } 1125 }
1044 1126
1045 std::string HNegate(Operation operation) { 1127 std::string HNegate(Operation operation) {
1046 const auto GetNegate = [&](std::size_t index) -> std::string { 1128 const auto GetNegate = [&](std::size_t index) {
1047 return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; 1129 return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1";
1048 }; 1130 };
1049 const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" + 1131 const std::string value =
1050 GetNegate(1) + ", " + GetNegate(2) + "))"; 1132 fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat),
1133 GetNegate(1), GetNegate(2));
1051 return BitwiseCastResult(value, Type::HalfFloat); 1134 return BitwiseCastResult(value, Type::HalfFloat);
1052 } 1135 }
1053 1136
@@ -1055,7 +1138,8 @@ private:
1055 const std::string value = VisitOperand(operation, 0, Type::HalfFloat); 1138 const std::string value = VisitOperand(operation, 0, Type::HalfFloat);
1056 const std::string min = VisitOperand(operation, 1, Type::Float); 1139 const std::string min = VisitOperand(operation, 1, Type::Float);
1057 const std::string max = VisitOperand(operation, 2, Type::Float); 1140 const std::string max = VisitOperand(operation, 2, Type::Float);
1058 const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))"; 1141 const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max);
1142
1059 return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); 1143 return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat));
1060 } 1144 }
1061 1145
@@ -1066,34 +1150,35 @@ private:
1066 case Tegra::Shader::HalfType::H0_H1: 1150 case Tegra::Shader::HalfType::H0_H1:
1067 return operand; 1151 return operand;
1068 case Tegra::Shader::HalfType::F32: 1152 case Tegra::Shader::HalfType::F32:
1069 return "vec2(fromHalf2(" + operand + "))"; 1153 return fmt::format("vec2(fromHalf2({}))", operand);
1070 case Tegra::Shader::HalfType::H0_H0: 1154 case Tegra::Shader::HalfType::H0_H0:
1071 return "vec2(" + operand + "[0])"; 1155 return fmt::format("vec2({}[0])", operand);
1072 case Tegra::Shader::HalfType::H1_H1: 1156 case Tegra::Shader::HalfType::H1_H1:
1073 return "vec2(" + operand + "[1])"; 1157 return fmt::format("vec2({}[1])", operand);
1074 } 1158 }
1075 UNREACHABLE(); 1159 UNREACHABLE();
1076 return "0"; 1160 return "0";
1077 }(); 1161 }();
1078 return "fromHalf2(" + value + ')'; 1162 return fmt::format("fromHalf2({})", value);
1079 } 1163 }
1080 1164
1081 std::string HMergeF32(Operation operation) { 1165 std::string HMergeF32(Operation operation) {
1082 return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; 1166 return fmt::format("float(toHalf2({})[0])", Visit(operation[0]));
1083 } 1167 }
1084 1168
1085 std::string HMergeH0(Operation operation) { 1169 std::string HMergeH0(Operation operation) {
1086 return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" + 1170 return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]),
1087 Visit(operation[0]) + ")[1]))"; 1171 Visit(operation[0]));
1088 } 1172 }
1089 1173
1090 std::string HMergeH1(Operation operation) { 1174 std::string HMergeH1(Operation operation) {
1091 return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" + 1175 return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]),
1092 Visit(operation[1]) + ")[1]))"; 1176 Visit(operation[1]));
1093 } 1177 }
1094 1178
1095 std::string HPack2(Operation operation) { 1179 std::string HPack2(Operation operation) {
1096 return "utof(packHalf2x16(vec2(" + Visit(operation[0]) + ", " + Visit(operation[1]) + ")))"; 1180 return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]),
1181 Visit(operation[1]));
1097 } 1182 }
1098 1183
1099 template <Type type> 1184 template <Type type>
@@ -1151,7 +1236,7 @@ private:
1151 target = GetInternalFlag(flag->GetFlag()); 1236 target = GetInternalFlag(flag->GetFlag());
1152 } 1237 }
1153 1238
1154 code.AddLine(target + " = " + Visit(src) + ';'); 1239 code.AddLine("{} = {};", target, Visit(src));
1155 return {}; 1240 return {};
1156 } 1241 }
1157 1242
@@ -1173,7 +1258,7 @@ private:
1173 1258
1174 std::string LogicalPick2(Operation operation) { 1259 std::string LogicalPick2(Operation operation) {
1175 const std::string pair = VisitOperand(operation, 0, Type::Bool2); 1260 const std::string pair = VisitOperand(operation, 0, Type::Bool2);
1176 return pair + '[' + VisitOperand(operation, 1, Type::Uint) + ']'; 1261 return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint));
1177 } 1262 }
1178 1263
1179 std::string LogicalAll2(Operation operation) { 1264 std::string LogicalAll2(Operation operation) {
@@ -1185,15 +1270,15 @@ private:
1185 } 1270 }
1186 1271
1187 template <bool with_nan> 1272 template <bool with_nan>
1188 std::string GenerateHalfComparison(Operation operation, std::string compare_op) { 1273 std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) {
1189 std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, 1274 const std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2,
1190 Type::HalfFloat, Type::HalfFloat)}; 1275 Type::HalfFloat, Type::HalfFloat)};
1191 if constexpr (!with_nan) { 1276 if constexpr (!with_nan) {
1192 return comparison; 1277 return comparison;
1193 } 1278 }
1194 return "halfFloatNanComparison(" + comparison + ", " + 1279 return fmt::format("halfFloatNanComparison({}, {}, {})", comparison,
1195 VisitOperand(operation, 0, Type::HalfFloat) + ", " + 1280 VisitOperand(operation, 0, Type::HalfFloat),
1196 VisitOperand(operation, 1, Type::HalfFloat) + ')'; 1281 VisitOperand(operation, 1, Type::HalfFloat));
1197 } 1282 }
1198 1283
1199 template <bool with_nan> 1284 template <bool with_nan>
@@ -1270,12 +1355,12 @@ private:
1270 switch (meta->element) { 1355 switch (meta->element) {
1271 case 0: 1356 case 0:
1272 case 1: 1357 case 1:
1273 return "itof(int(textureSize(" + sampler + ", " + lod + ')' + 1358 return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod,
1274 GetSwizzle(meta->element) + "))"; 1359 GetSwizzle(meta->element));
1275 case 2: 1360 case 2:
1276 return "0"; 1361 return "0";
1277 case 3: 1362 case 3:
1278 return "itof(textureQueryLevels(" + sampler + "))"; 1363 return fmt::format("itof(textureQueryLevels({}))", sampler);
1279 } 1364 }
1280 UNREACHABLE(); 1365 UNREACHABLE();
1281 return "0"; 1366 return "0";
@@ -1286,8 +1371,9 @@ private:
1286 ASSERT(meta); 1371 ASSERT(meta);
1287 1372
1288 if (meta->element < 2) { 1373 if (meta->element < 2) {
1289 return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" + 1374 return fmt::format("itof(int(({} * vec2(256)){}))",
1290 GetSwizzle(meta->element) + "))"; 1375 GenerateTexture(operation, "QueryLod", {}),
1376 GetSwizzle(meta->element));
1291 } 1377 }
1292 return "0"; 1378 return "0";
1293 } 1379 }
@@ -1326,7 +1412,7 @@ private:
1326 const auto target = std::get_if<ImmediateNode>(operation[0]); 1412 const auto target = std::get_if<ImmediateNode>(operation[0]);
1327 UNIMPLEMENTED_IF(!target); 1413 UNIMPLEMENTED_IF(!target);
1328 1414
1329 code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target->GetValue())); 1415 code.AddLine("jmp_to = 0x{:x}u;", target->GetValue());
1330 code.AddLine("break;"); 1416 code.AddLine("break;");
1331 return {}; 1417 return {};
1332 } 1418 }
@@ -1335,7 +1421,7 @@ private:
1335 const auto target = std::get_if<ImmediateNode>(operation[0]); 1421 const auto target = std::get_if<ImmediateNode>(operation[0]);
1336 UNIMPLEMENTED_IF(!target); 1422 UNIMPLEMENTED_IF(!target);
1337 1423
1338 code.AddLine(fmt::format("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue())); 1424 code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue());
1339 return {}; 1425 return {};
1340 } 1426 }
1341 1427
@@ -1361,7 +1447,7 @@ private:
1361 1447
1362 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); 1448 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
1363 1449
1364 code.AddLine("if (alpha_test[0] != 0) {"); 1450 code.AddLine("if (alpha_test[0] != 0) {{");
1365 ++code.scope; 1451 ++code.scope;
1366 // We start on the register containing the alpha value in the first RT. 1452 // We start on the register containing the alpha value in the first RT.
1367 u32 current_reg = 3; 1453 u32 current_reg = 3;
@@ -1372,13 +1458,12 @@ private:
1372 header.ps.IsColorComponentOutputEnabled(render_target, 1) || 1458 header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
1373 header.ps.IsColorComponentOutputEnabled(render_target, 2) || 1459 header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
1374 header.ps.IsColorComponentOutputEnabled(render_target, 3)) { 1460 header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
1375 code.AddLine( 1461 code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg));
1376 fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg)));
1377 current_reg += 4; 1462 current_reg += 4;
1378 } 1463 }
1379 } 1464 }
1380 --code.scope; 1465 --code.scope;
1381 code.AddLine('}'); 1466 code.AddLine("}}");
1382 1467
1383 // Write the color outputs using the data in the shader registers, disabled 1468 // Write the color outputs using the data in the shader registers, disabled
1384 // rendertargets/components are skipped in the register assignment. 1469 // rendertargets/components are skipped in the register assignment.
@@ -1387,8 +1472,8 @@ private:
1387 // TODO(Subv): Figure out how dual-source blending is configured in the Switch. 1472 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
1388 for (u32 component = 0; component < 4; ++component) { 1473 for (u32 component = 0; component < 4; ++component) {
1389 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { 1474 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
1390 code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, 1475 code.AddLine("FragColor{}[{}] = {};", render_target, component,
1391 SafeGetRegister(current_reg))); 1476 SafeGetRegister(current_reg));
1392 ++current_reg; 1477 ++current_reg;
1393 } 1478 }
1394 } 1479 }
@@ -1397,7 +1482,7 @@ private:
1397 if (header.ps.omap.depth) { 1482 if (header.ps.omap.depth) {
1398 // The depth output is always 2 registers after the last color output, and current_reg 1483 // The depth output is always 2 registers after the last color output, and current_reg
1399 // already contains one past the last color register. 1484 // already contains one past the last color register.
1400 code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';'); 1485 code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1));
1401 } 1486 }
1402 1487
1403 code.AddLine("return;"); 1488 code.AddLine("return;");
@@ -1407,11 +1492,11 @@ private:
1407 std::string Discard(Operation operation) { 1492 std::string Discard(Operation operation) {
1408 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 1493 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
1409 // about unexecuted instructions that may follow this. 1494 // about unexecuted instructions that may follow this.
1410 code.AddLine("if (true) {"); 1495 code.AddLine("if (true) {{");
1411 ++code.scope; 1496 ++code.scope;
1412 code.AddLine("discard;"); 1497 code.AddLine("discard;");
1413 --code.scope; 1498 --code.scope;
1414 code.AddLine("}"); 1499 code.AddLine("}}");
1415 return {}; 1500 return {};
1416 } 1501 }
1417 1502
@@ -1591,15 +1676,11 @@ private:
1591 } 1676 }
1592 1677
1593 std::string GetInputAttribute(Attribute::Index attribute) const { 1678 std::string GetInputAttribute(Attribute::Index attribute) const {
1594 const auto index{static_cast<u32>(attribute) - 1679 return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr");
1595 static_cast<u32>(Attribute::Index::Attribute_0)};
1596 return GetDeclarationWithSuffix(index, "input_attr");
1597 } 1680 }
1598 1681
1599 std::string GetOutputAttribute(Attribute::Index attribute) const { 1682 std::string GetOutputAttribute(Attribute::Index attribute) const {
1600 const auto index{static_cast<u32>(attribute) - 1683 return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr");
1601 static_cast<u32>(Attribute::Index::Attribute_0)};
1602 return GetDeclarationWithSuffix(index, "output_attr");
1603 } 1684 }
1604 1685
1605 std::string GetConstBuffer(u32 index) const { 1686 std::string GetConstBuffer(u32 index) const {
@@ -1629,7 +1710,7 @@ private:
1629 const auto index = static_cast<u32>(flag); 1710 const auto index = static_cast<u32>(flag);
1630 ASSERT(index < static_cast<u32>(InternalFlag::Amount)); 1711 ASSERT(index < static_cast<u32>(InternalFlag::Amount));
1631 1712
1632 return std::string(InternalFlagNames[index]) + '_' + suffix; 1713 return fmt::format("{}_{}", InternalFlagNames[index], suffix);
1633 } 1714 }
1634 1715
1635 std::string GetSampler(const Sampler& sampler) const { 1716 std::string GetSampler(const Sampler& sampler) const {
@@ -1637,7 +1718,20 @@ private:
1637 } 1718 }
1638 1719
1639 std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { 1720 std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const {
1640 return name + '_' + std::to_string(index) + '_' + suffix; 1721 return fmt::format("{}_{}_{}", name, index, suffix);
1722 }
1723
1724 u32 GetNumPhysicalInputAttributes() const {
1725 return stage == ShaderStage::Vertex ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
1726 }
1727
1728 u32 GetNumPhysicalAttributes() const {
1729 return std::min<u32>(device.GetMaxVertexAttributes(), Maxwell::NumVertexAttributes);
1730 }
1731
1732 u32 GetNumPhysicalVaryings() const {
1733 return std::min<u32>(device.GetMaxVaryings() - GENERIC_VARYING_START_LOCATION,
1734 Maxwell::NumVaryings);
1641 } 1735 }
1642 1736
1643 const Device& device; 1737 const Device& device;
@@ -1652,24 +1746,25 @@ private:
1652} // Anonymous namespace 1746} // Anonymous namespace
1653 1747
1654std::string GetCommonDeclarations() { 1748std::string GetCommonDeclarations() {
1655 const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); 1749 return fmt::format(
1656 return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + 1750 "#define MAX_CONSTBUFFER_ELEMENTS {}\n"
1657 "#define ftoi floatBitsToInt\n" 1751 "#define ftoi floatBitsToInt\n"
1658 "#define ftou floatBitsToUint\n" 1752 "#define ftou floatBitsToUint\n"
1659 "#define itof intBitsToFloat\n" 1753 "#define itof intBitsToFloat\n"
1660 "#define utof uintBitsToFloat\n\n" 1754 "#define utof uintBitsToFloat\n\n"
1661 "float fromHalf2(vec2 pair) {\n" 1755 "float fromHalf2(vec2 pair) {{\n"
1662 " return utof(packHalf2x16(pair));\n" 1756 " return utof(packHalf2x16(pair));\n"
1663 "}\n\n" 1757 "}}\n\n"
1664 "vec2 toHalf2(float value) {\n" 1758 "vec2 toHalf2(float value) {{\n"
1665 " return unpackHalf2x16(ftou(value));\n" 1759 " return unpackHalf2x16(ftou(value));\n"
1666 "}\n\n" 1760 "}}\n\n"
1667 "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n" 1761 "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n"
1668 " bvec2 is_nan1 = isnan(pair1);\n" 1762 " bvec2 is_nan1 = isnan(pair1);\n"
1669 " bvec2 is_nan2 = isnan(pair2);\n" 1763 " bvec2 is_nan2 = isnan(pair2);\n"
1670 " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " 1764 " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
1671 "is_nan2.y);\n" 1765 "is_nan2.y);\n"
1672 "}\n"; 1766 "}}\n",
1767 MAX_CONSTBUFFER_ELEMENTS);
1673} 1768}
1674 1769
1675ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, 1770ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage,
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 254c0d499..fba9c594a 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -104,8 +104,9 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
104 return true; 104 return true;
105} 105}
106 106
107ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) 107ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
108 : system{system}, precompiled_cache_virtual_file_offset{0} {} 108
109ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
109 110
110std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> 111std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
111ShaderDiskCacheOpenGL::LoadTransferable() { 112ShaderDiskCacheOpenGL::LoadTransferable() {
@@ -243,7 +244,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
243 return {}; 244 return {};
244 } 245 }
245 246
246 const auto entry = LoadDecompiledEntry(); 247 auto entry = LoadDecompiledEntry();
247 if (!entry) { 248 if (!entry) {
248 return {}; 249 return {};
249 } 250 }
@@ -287,13 +288,13 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
287 return {}; 288 return {};
288 } 289 }
289 290
290 std::vector<u8> code(code_size); 291 std::string code(code_size, '\0');
291 if (!LoadArrayFromPrecompiled(code.data(), code.size())) { 292 if (!LoadArrayFromPrecompiled(code.data(), code.size())) {
292 return {}; 293 return {};
293 } 294 }
294 295
295 ShaderDiskCacheDecompiled entry; 296 ShaderDiskCacheDecompiled entry;
296 entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size); 297 entry.code = std::move(code);
297 298
298 u32 const_buffers_count{}; 299 u32 const_buffers_count{};
299 if (!LoadObjectFromPrecompiled(const_buffers_count)) { 300 if (!LoadObjectFromPrecompiled(const_buffers_count)) {
@@ -303,12 +304,12 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
303 for (u32 i = 0; i < const_buffers_count; ++i) { 304 for (u32 i = 0; i < const_buffers_count; ++i) {
304 u32 max_offset{}; 305 u32 max_offset{};
305 u32 index{}; 306 u32 index{};
306 u8 is_indirect{}; 307 bool is_indirect{};
307 if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) || 308 if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) ||
308 !LoadObjectFromPrecompiled(is_indirect)) { 309 !LoadObjectFromPrecompiled(is_indirect)) {
309 return {}; 310 return {};
310 } 311 }
311 entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); 312 entry.entries.const_buffers.emplace_back(max_offset, is_indirect, index);
312 } 313 }
313 314
314 u32 samplers_count{}; 315 u32 samplers_count{};
@@ -320,18 +321,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
320 u64 offset{}; 321 u64 offset{};
321 u64 index{}; 322 u64 index{};
322 u32 type{}; 323 u32 type{};
323 u8 is_array{}; 324 bool is_array{};
324 u8 is_shadow{}; 325 bool is_shadow{};
325 u8 is_bindless{}; 326 bool is_bindless{};
326 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || 327 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
327 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) || 328 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) ||
328 !LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) { 329 !LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) {
329 return {}; 330 return {};
330 } 331 }
331 entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset), 332 entry.entries.samplers.emplace_back(
332 static_cast<std::size_t>(index), 333 static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
333 static_cast<Tegra::Shader::TextureType>(type), 334 static_cast<Tegra::Shader::TextureType>(type), is_array, is_shadow, is_bindless);
334 is_array != 0, is_shadow != 0, is_bindless != 0);
335 } 335 }
336 336
337 u32 global_memory_count{}; 337 u32 global_memory_count{};
@@ -342,21 +342,20 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
342 for (u32 i = 0; i < global_memory_count; ++i) { 342 for (u32 i = 0; i < global_memory_count; ++i) {
343 u32 cbuf_index{}; 343 u32 cbuf_index{};
344 u32 cbuf_offset{}; 344 u32 cbuf_offset{};
345 u8 is_read{}; 345 bool is_read{};
346 u8 is_written{}; 346 bool is_written{};
347 if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) || 347 if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) ||
348 !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) { 348 !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) {
349 return {}; 349 return {};
350 } 350 }
351 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0, 351 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read,
352 is_written != 0); 352 is_written);
353 } 353 }
354 354
355 for (auto& clip_distance : entry.entries.clip_distances) { 355 for (auto& clip_distance : entry.entries.clip_distances) {
356 u8 clip_distance_raw{}; 356 if (!LoadObjectFromPrecompiled(clip_distance)) {
357 if (!LoadObjectFromPrecompiled(clip_distance_raw))
358 return {}; 357 return {};
359 clip_distance = clip_distance_raw != 0; 358 }
360 } 359 }
361 360
362 u64 shader_length{}; 361 u64 shader_length{};
@@ -384,7 +383,7 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
384 for (const auto& cbuf : entries.const_buffers) { 383 for (const auto& cbuf : entries.const_buffers) {
385 if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) || 384 if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) ||
386 !SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) || 385 !SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) ||
387 !SaveObjectToPrecompiled(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0))) { 386 !SaveObjectToPrecompiled(cbuf.IsIndirect())) {
388 return false; 387 return false;
389 } 388 }
390 } 389 }
@@ -396,9 +395,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
396 if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) || 395 if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) ||
397 !SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) || 396 !SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) ||
398 !SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) || 397 !SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) ||
399 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsArray() ? 1 : 0)) || 398 !SaveObjectToPrecompiled(sampler.IsArray()) ||
400 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) || 399 !SaveObjectToPrecompiled(sampler.IsShadow()) ||
401 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsBindless() ? 1 : 0))) { 400 !SaveObjectToPrecompiled(sampler.IsBindless())) {
402 return false; 401 return false;
403 } 402 }
404 } 403 }
@@ -409,14 +408,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
409 for (const auto& gmem : entries.global_memory_entries) { 408 for (const auto& gmem : entries.global_memory_entries) {
410 if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) || 409 if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) ||
411 !SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) || 410 !SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) ||
412 !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsRead() ? 1 : 0)) || 411 !SaveObjectToPrecompiled(gmem.IsRead()) || !SaveObjectToPrecompiled(gmem.IsWritten())) {
413 !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsWritten() ? 1 : 0))) {
414 return false; 412 return false;
415 } 413 }
416 } 414 }
417 415
418 for (const bool clip_distance : entries.clip_distances) { 416 for (const bool clip_distance : entries.clip_distances) {
419 if (!SaveObjectToPrecompiled(static_cast<u8>(clip_distance ? 1 : 0))) { 417 if (!SaveObjectToPrecompiled(clip_distance)) {
420 return false; 418 return false;
421 } 419 }
422 } 420 }
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index 0142b2e3b..2da0a4a23 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -70,14 +70,14 @@ namespace std {
70 70
71template <> 71template <>
72struct hash<OpenGL::BaseBindings> { 72struct hash<OpenGL::BaseBindings> {
73 std::size_t operator()(const OpenGL::BaseBindings& bindings) const { 73 std::size_t operator()(const OpenGL::BaseBindings& bindings) const noexcept {
74 return bindings.cbuf | bindings.gmem << 8 | bindings.sampler << 16; 74 return bindings.cbuf | bindings.gmem << 8 | bindings.sampler << 16;
75 } 75 }
76}; 76};
77 77
78template <> 78template <>
79struct hash<OpenGL::ShaderDiskCacheUsage> { 79struct hash<OpenGL::ShaderDiskCacheUsage> {
80 std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const { 80 std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept {
81 return static_cast<std::size_t>(usage.unique_identifier) ^ 81 return static_cast<std::size_t>(usage.unique_identifier) ^
82 std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16; 82 std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16;
83 } 83 }
@@ -162,6 +162,7 @@ struct ShaderDiskCacheDump {
162class ShaderDiskCacheOpenGL { 162class ShaderDiskCacheOpenGL {
163public: 163public:
164 explicit ShaderDiskCacheOpenGL(Core::System& system); 164 explicit ShaderDiskCacheOpenGL(Core::System& system);
165 ~ShaderDiskCacheOpenGL();
165 166
166 /// Loads transferable cache. If file has a old version or on failure, it deletes the file. 167 /// Loads transferable cache. If file has a old version or on failure, it deletes the file.
167 std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> 168 std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
@@ -259,20 +260,35 @@ private:
259 return SaveArrayToPrecompiled(&object, 1); 260 return SaveArrayToPrecompiled(&object, 1);
260 } 261 }
261 262
263 bool SaveObjectToPrecompiled(bool object) {
264 const auto value = static_cast<u8>(object);
265 return SaveArrayToPrecompiled(&value, 1);
266 }
267
262 template <typename T> 268 template <typename T>
263 bool LoadObjectFromPrecompiled(T& object) { 269 bool LoadObjectFromPrecompiled(T& object) {
264 return LoadArrayFromPrecompiled(&object, 1); 270 return LoadArrayFromPrecompiled(&object, 1);
265 } 271 }
266 272
267 // Copre system 273 bool LoadObjectFromPrecompiled(bool& object) {
274 u8 value;
275 const bool read_ok = LoadArrayFromPrecompiled(&value, 1);
276 if (!read_ok) {
277 return false;
278 }
279
280 object = value != 0;
281 return true;
282 }
283
284 // Core system
268 Core::System& system; 285 Core::System& system;
269 // Stored transferable shaders 286 // Stored transferable shaders
270 std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; 287 std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
271 // Stores whole precompiled cache which will be read from or saved to the precompiled chache 288 // Stores whole precompiled cache which will be read from/saved to the precompiled cache file
272 // file
273 FileSys::VectorVfsFile precompiled_cache_virtual_file; 289 FileSys::VectorVfsFile precompiled_cache_virtual_file;
274 // Stores the current offset of the precompiled cache file for IO purposes 290 // Stores the current offset of the precompiled cache file for IO purposes
275 std::size_t precompiled_cache_virtual_file_offset; 291 std::size_t precompiled_cache_virtual_file_offset = 0;
276 292
277 // The cache has been loaded at boot 293 // The cache has been loaded at boot
278 bool tried_to_load{}; 294 bool tried_to_load{};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 6abf948f8..7ab0b4553 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -33,14 +33,14 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
33}; 33};
34 34
35)"; 35)";
36 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); 36 const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
37 ProgramResult program = 37 ProgramResult program =
38 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); 38 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex");
39 39
40 out += program.first; 40 out += program.first;
41 41
42 if (setup.IsDualProgram()) { 42 if (setup.IsDualProgram()) {
43 ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET); 43 const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET);
44 ProgramResult program_b = 44 ProgramResult program_b =
45 Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); 45 Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b");
46 46
@@ -76,7 +76,7 @@ void main() {
76 } 76 }
77})"; 77})";
78 78
79 return {out, program.second}; 79 return {std::move(out), std::move(program.second)};
80} 80}
81 81
82ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { 82ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) {
@@ -97,7 +97,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
97}; 97};
98 98
99)"; 99)";
100 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); 100 const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
101 ProgramResult program = 101 ProgramResult program =
102 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); 102 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry");
103 out += program.first; 103 out += program.first;
@@ -107,7 +107,7 @@ void main() {
107 execute_geometry(); 107 execute_geometry();
108};)"; 108};)";
109 109
110 return {out, program.second}; 110 return {std::move(out), std::move(program.second)};
111} 111}
112 112
113ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { 113ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) {
@@ -160,7 +160,7 @@ bool AlphaFunc(in float value) {
160} 160}
161 161
162)"; 162)";
163 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); 163 const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
164 ProgramResult program = 164 ProgramResult program =
165 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); 165 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment");
166 166
@@ -172,7 +172,7 @@ void main() {
172} 172}
173 173
174)"; 174)";
175 return {out, program.second}; 175 return {std::move(out), std::move(program.second)};
176} 176}
177 177
178} // namespace OpenGL::GLShader 178} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 95b773135..ed7b5cff0 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -126,6 +126,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
126 return GL_TRIANGLES; 126 return GL_TRIANGLES;
127 case Maxwell::PrimitiveTopology::TriangleStrip: 127 case Maxwell::PrimitiveTopology::TriangleStrip:
128 return GL_TRIANGLE_STRIP; 128 return GL_TRIANGLE_STRIP;
129 case Maxwell::PrimitiveTopology::TriangleFan:
130 return GL_TRIANGLE_FAN;
129 default: 131 default:
130 LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); 132 LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology));
131 UNREACHABLE(); 133 UNREACHABLE();
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index a11000f6b..b61a6d170 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -194,8 +194,8 @@ public:
194 for (const auto& sampler : ir.GetSamplers()) { 194 for (const auto& sampler : ir.GetSamplers()) {
195 entries.samplers.emplace_back(sampler); 195 entries.samplers.emplace_back(sampler);
196 } 196 }
197 for (const auto& attr : ir.GetInputAttributes()) { 197 for (const auto& attribute : ir.GetInputAttributes()) {
198 entries.attributes.insert(GetGenericAttributeLocation(attr.first)); 198 entries.attributes.insert(GetGenericAttributeLocation(attribute));
199 } 199 }
200 entries.clip_distances = ir.GetClipDistances(); 200 entries.clip_distances = ir.GetClipDistances();
201 entries.shader_length = ir.GetLength(); 201 entries.shader_length = ir.GetLength();
@@ -321,8 +321,7 @@ private:
321 } 321 }
322 322
323 void DeclareInputAttributes() { 323 void DeclareInputAttributes() {
324 for (const auto element : ir.GetInputAttributes()) { 324 for (const auto index : ir.GetInputAttributes()) {
325 const Attribute::Index index = element.first;
326 if (!IsGenericAttribute(index)) { 325 if (!IsGenericAttribute(index)) {
327 continue; 326 continue;
328 } 327 }
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp
index 3190e2d7c..b4859bc1e 100644
--- a/src/video_core/shader/decode/arithmetic.cpp
+++ b/src/video_core/shader/decode/arithmetic.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/logging/log.h"
7#include "video_core/engines/shader_bytecode.h" 8#include "video_core/engines/shader_bytecode.h"
8#include "video_core/shader/shader_ir.h" 9#include "video_core/shader/shader_ir.h"
9 10
@@ -152,4 +153,4 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
152 return pc; 153 return pc;
153} 154}
154 155
155} // namespace VideoCommon::Shader \ No newline at end of file 156} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp
index 2098c1170..3a29c4a46 100644
--- a/src/video_core/shader/decode/arithmetic_half.cpp
+++ b/src/video_core/shader/decode/arithmetic_half.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/logging/log.h"
7#include "video_core/engines/shader_bytecode.h" 8#include "video_core/engines/shader_bytecode.h"
8#include "video_core/shader/shader_ir.h" 9#include "video_core/shader/shader_ir.h"
9 10
diff --git a/src/video_core/shader/decode/arithmetic_half_immediate.cpp b/src/video_core/shader/decode/arithmetic_half_immediate.cpp
index fbcd35b18..5341e460f 100644
--- a/src/video_core/shader/decode/arithmetic_half_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_half_immediate.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/logging/log.h"
7#include "video_core/engines/shader_bytecode.h" 8#include "video_core/engines/shader_bytecode.h"
8#include "video_core/shader/shader_ir.h" 9#include "video_core/shader/shader_ir.h"
9 10
@@ -47,4 +48,4 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) {
47 return pc; 48 return pc;
48} 49}
49 50
50} // namespace VideoCommon::Shader \ No newline at end of file 51} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/arithmetic_immediate.cpp b/src/video_core/shader/decode/arithmetic_immediate.cpp
index 0d139c0d2..3095f2fd4 100644
--- a/src/video_core/shader/decode/arithmetic_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_immediate.cpp
@@ -49,4 +49,4 @@ u32 ShaderIR::DecodeArithmeticImmediate(NodeBlock& bb, u32 pc) {
49 return pc; 49 return pc;
50} 50}
51 51
52} // namespace VideoCommon::Shader \ No newline at end of file 52} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
index 3ed5ccc5a..679ac0d4e 100644
--- a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
@@ -93,4 +93,4 @@ void ShaderIR::WriteLogicOperation(NodeBlock& bb, Register dest, LogicOperation
93 } 93 }
94} 94}
95 95
96} // namespace VideoCommon::Shader \ No newline at end of file 96} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/bfe.cpp b/src/video_core/shader/decode/bfe.cpp
index 6a95dc928..1ae192c6a 100644
--- a/src/video_core/shader/decode/bfe.cpp
+++ b/src/video_core/shader/decode/bfe.cpp
@@ -46,4 +46,4 @@ u32 ShaderIR::DecodeBfe(NodeBlock& bb, u32 pc) {
46 return pc; 46 return pc;
47} 47}
48 48
49} // namespace VideoCommon::Shader \ No newline at end of file 49} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/bfi.cpp b/src/video_core/shader/decode/bfi.cpp
index 601d66f1f..0b12a0d08 100644
--- a/src/video_core/shader/decode/bfi.cpp
+++ b/src/video_core/shader/decode/bfi.cpp
@@ -38,4 +38,4 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) {
38 return pc; 38 return pc;
39} 39}
40 40
41} // namespace VideoCommon::Shader \ No newline at end of file 41} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/ffma.cpp b/src/video_core/shader/decode/ffma.cpp
index 0559cc8de..a1d04c6e5 100644
--- a/src/video_core/shader/decode/ffma.cpp
+++ b/src/video_core/shader/decode/ffma.cpp
@@ -56,4 +56,4 @@ u32 ShaderIR::DecodeFfma(NodeBlock& bb, u32 pc) {
56 return pc; 56 return pc;
57} 57}
58 58
59} // namespace VideoCommon::Shader \ No newline at end of file 59} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/float_set.cpp b/src/video_core/shader/decode/float_set.cpp
index 1bd6755dd..cc522f1de 100644
--- a/src/video_core/shader/decode/float_set.cpp
+++ b/src/video_core/shader/decode/float_set.cpp
@@ -55,4 +55,4 @@ u32 ShaderIR::DecodeFloatSet(NodeBlock& bb, u32 pc) {
55 return pc; 55 return pc;
56} 56}
57 57
58} // namespace VideoCommon::Shader \ No newline at end of file 58} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/float_set_predicate.cpp b/src/video_core/shader/decode/float_set_predicate.cpp
index 9285b8d05..9d2322a1d 100644
--- a/src/video_core/shader/decode/float_set_predicate.cpp
+++ b/src/video_core/shader/decode/float_set_predicate.cpp
@@ -53,4 +53,4 @@ u32 ShaderIR::DecodeFloatSetPredicate(NodeBlock& bb, u32 pc) {
53 return pc; 53 return pc;
54} 54}
55 55
56} // namespace VideoCommon::Shader \ No newline at end of file 56} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp
index 1dd94bf9d..755f2ec44 100644
--- a/src/video_core/shader/decode/half_set.cpp
+++ b/src/video_core/shader/decode/half_set.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h"
9#include "video_core/engines/shader_bytecode.h" 10#include "video_core/engines/shader_bytecode.h"
10#include "video_core/shader/shader_ir.h" 11#include "video_core/shader/shader_ir.h"
11 12
@@ -64,4 +65,4 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
64 return pc; 65 return pc;
65} 66}
66 67
67} // namespace VideoCommon::Shader \ No newline at end of file 68} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp
index 6e59eb650..fba44d714 100644
--- a/src/video_core/shader/decode/half_set_predicate.cpp
+++ b/src/video_core/shader/decode/half_set_predicate.cpp
@@ -59,4 +59,4 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
59 return pc; 59 return pc;
60} 60}
61 61
62} // namespace VideoCommon::Shader \ No newline at end of file 62} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/integer_set.cpp b/src/video_core/shader/decode/integer_set.cpp
index a3bf17eba..a4cdaf74d 100644
--- a/src/video_core/shader/decode/integer_set.cpp
+++ b/src/video_core/shader/decode/integer_set.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h"
6#include "common/common_types.h" 5#include "common/common_types.h"
7#include "video_core/engines/shader_bytecode.h" 6#include "video_core/engines/shader_bytecode.h"
8#include "video_core/shader/shader_ir.h" 7#include "video_core/shader/shader_ir.h"
@@ -47,4 +46,4 @@ u32 ShaderIR::DecodeIntegerSet(NodeBlock& bb, u32 pc) {
47 return pc; 46 return pc;
48} 47}
49 48
50} // namespace VideoCommon::Shader \ No newline at end of file 49} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/integer_set_predicate.cpp b/src/video_core/shader/decode/integer_set_predicate.cpp
index aad836d24..a6a1fb632 100644
--- a/src/video_core/shader/decode/integer_set_predicate.cpp
+++ b/src/video_core/shader/decode/integer_set_predicate.cpp
@@ -50,4 +50,4 @@ u32 ShaderIR::DecodeIntegerSetPredicate(NodeBlock& bb, u32 pc) {
50 return pc; 50 return pc;
51} 51}
52 52
53} // namespace VideoCommon::Shader \ No newline at end of file 53} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index ea1092db1..165c2b41b 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -47,17 +47,20 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
47 "Indirect attribute loads are not supported"); 47 "Indirect attribute loads are not supported");
48 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, 48 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
49 "Unaligned attribute loads are not supported"); 49 "Unaligned attribute loads are not supported");
50 UNIMPLEMENTED_IF_MSG(instr.attribute.fmt20.IsPhysical() &&
51 instr.attribute.fmt20.size != Tegra::Shader::AttributeSize::Word,
52 "Non-32 bits PHYS reads are not implemented");
50 53
51 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Pass, 54 const Node buffer{GetRegister(instr.gpr39)};
52 Tegra::Shader::IpaSampleMode::Default};
53 55
54 u64 next_element = instr.attribute.fmt20.element; 56 u64 next_element = instr.attribute.fmt20.element;
55 auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); 57 auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
56 58
57 const auto LoadNextElement = [&](u32 reg_offset) { 59 const auto LoadNextElement = [&](u32 reg_offset) {
58 const Node buffer = GetRegister(instr.gpr39); 60 const Node attribute{instr.attribute.fmt20.IsPhysical()
59 const Node attribute = GetInputAttribute(static_cast<Attribute::Index>(next_index), 61 ? GetPhysicalInputAttribute(instr.gpr8, buffer)
60 next_element, input_mode, buffer); 62 : GetInputAttribute(static_cast<Attribute::Index>(next_index),
63 next_element, buffer)};
61 64
62 SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute); 65 SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
63 66
@@ -239,6 +242,21 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
239 } 242 }
240 break; 243 break;
241 } 244 }
245 case OpCode::Id::AL2P: {
246 // Ignore al2p.direction since we don't care about it.
247
248 // Calculate emulation fake physical address.
249 const Node fixed_address{Immediate(static_cast<u32>(instr.al2p.address))};
250 const Node reg{GetRegister(instr.gpr8)};
251 const Node fake_address{Operation(OperationCode::IAdd, NO_PRECISE, reg, fixed_address)};
252
253 // Set the fake address to target register.
254 SetRegister(bb, instr.gpr0, fake_address);
255
256 // Signal the shader IR to declare all possible attributes and varyings
257 uses_physical_attributes = true;
258 break;
259 }
242 default: 260 default:
243 UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName()); 261 UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
244 } 262 }
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index d750a2936..ca7af72e1 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/logging/log.h"
7#include "video_core/engines/shader_bytecode.h" 8#include "video_core/engines/shader_bytecode.h"
8#include "video_core/shader/shader_ir.h" 9#include "video_core/shader/shader_ir.h"
9 10
@@ -130,15 +131,18 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
130 break; 131 break;
131 } 132 }
132 case OpCode::Id::IPA: { 133 case OpCode::Id::IPA: {
133 const auto& attribute = instr.attribute.fmt28; 134 const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff;
135
136 const auto attribute = instr.attribute.fmt28;
134 const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), 137 const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(),
135 instr.ipa.sample_mode.Value()}; 138 instr.ipa.sample_mode.Value()};
136 139
137 const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode); 140 Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8)
138 Node value = attr; 141 : GetInputAttribute(attribute.index, attribute.element);
139 const Tegra::Shader::Attribute::Index index = attribute.index.Value(); 142 const Tegra::Shader::Attribute::Index index = attribute.index.Value();
140 if (index >= Tegra::Shader::Attribute::Index::Attribute_0 && 143 const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
141 index <= Tegra::Shader::Attribute::Index::Attribute_31) { 144 index <= Tegra::Shader::Attribute::Index::Attribute_31;
145 if (is_generic || is_physical) {
142 // TODO(Blinkhawk): There are cases where a perspective attribute use PASS. 146 // TODO(Blinkhawk): There are cases where a perspective attribute use PASS.
143 // In theory by setting them as perspective, OpenGL does the perspective correction. 147 // In theory by setting them as perspective, OpenGL does the perspective correction.
144 // A way must figured to reverse the last step of it. 148 // A way must figured to reverse the last step of it.
diff --git a/src/video_core/shader/decode/predicate_set_predicate.cpp b/src/video_core/shader/decode/predicate_set_predicate.cpp
index 83c61680e..71844c42b 100644
--- a/src/video_core/shader/decode/predicate_set_predicate.cpp
+++ b/src/video_core/shader/decode/predicate_set_predicate.cpp
@@ -64,4 +64,4 @@ u32 ShaderIR::DecodePredicateSetPredicate(NodeBlock& bb, u32 pc) {
64 return pc; 64 return pc;
65} 65}
66 66
67} // namespace VideoCommon::Shader \ No newline at end of file 67} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/predicate_set_register.cpp b/src/video_core/shader/decode/predicate_set_register.cpp
index d0495995d..387491bd3 100644
--- a/src/video_core/shader/decode/predicate_set_register.cpp
+++ b/src/video_core/shader/decode/predicate_set_register.cpp
@@ -43,4 +43,4 @@ u32 ShaderIR::DecodePredicateSetRegister(NodeBlock& bb, u32 pc) {
43 return pc; 43 return pc;
44} 44}
45 45
46} // namespace VideoCommon::Shader \ No newline at end of file 46} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/register_set_predicate.cpp b/src/video_core/shader/decode/register_set_predicate.cpp
index f070e8912..f8659e48e 100644
--- a/src/video_core/shader/decode/register_set_predicate.cpp
+++ b/src/video_core/shader/decode/register_set_predicate.cpp
@@ -48,4 +48,4 @@ u32 ShaderIR::DecodeRegisterSetPredicate(NodeBlock& bb, u32 pc) {
48 return pc; 48 return pc;
49} 49}
50 50
51} // namespace VideoCommon::Shader \ No newline at end of file 51} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp
index 951e85f44..44ae87ece 100644
--- a/src/video_core/shader/decode/shift.cpp
+++ b/src/video_core/shader/decode/shift.cpp
@@ -52,4 +52,4 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
52 return pc; 52 return pc;
53} 53}
54 54
55} // namespace VideoCommon::Shader \ No newline at end of file 55} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/video.cpp b/src/video_core/shader/decode/video.cpp
index 956c01d9b..cb9ab72b1 100644
--- a/src/video_core/shader/decode/video.cpp
+++ b/src/video_core/shader/decode/video.cpp
@@ -108,4 +108,4 @@ Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed,
108 } 108 }
109} 109}
110 110
111} // namespace VideoCommon::Shader \ No newline at end of file 111} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index e4eb0dfd9..8a6ee5cf5 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -21,6 +21,13 @@ using Tegra::Shader::PredCondition;
21using Tegra::Shader::PredOperation; 21using Tegra::Shader::PredOperation;
22using Tegra::Shader::Register; 22using Tegra::Shader::Register;
23 23
24ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset)
25 : program_code{program_code}, main_offset{main_offset} {
26 Decode();
27}
28
29ShaderIR::~ShaderIR() = default;
30
24Node ShaderIR::StoreNode(NodeData&& node_data) { 31Node ShaderIR::StoreNode(NodeData&& node_data) {
25 auto store = std::make_unique<NodeData>(node_data); 32 auto store = std::make_unique<NodeData>(node_data);
26 const Node node = store.get(); 33 const Node node = store.get();
@@ -32,8 +39,8 @@ Node ShaderIR::Conditional(Node condition, std::vector<Node>&& code) {
32 return StoreNode(ConditionalNode(condition, std::move(code))); 39 return StoreNode(ConditionalNode(condition, std::move(code)));
33} 40}
34 41
35Node ShaderIR::Comment(const std::string& text) { 42Node ShaderIR::Comment(std::string text) {
36 return StoreNode(CommentNode(text)); 43 return StoreNode(CommentNode(std::move(text)));
37} 44}
38 45
39Node ShaderIR::Immediate(u32 value) { 46Node ShaderIR::Immediate(u32 value) {
@@ -89,13 +96,14 @@ Node ShaderIR::GetPredicate(bool immediate) {
89 return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute)); 96 return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute));
90} 97}
91 98
92Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, 99Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
93 const Tegra::Shader::IpaMode& input_mode, Node buffer) { 100 used_input_attributes.emplace(index);
94 const auto [entry, is_new] = 101 return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
95 used_input_attributes.emplace(std::make_pair(index, std::set<Tegra::Shader::IpaMode>{})); 102}
96 entry->second.insert(input_mode);
97 103
98 return StoreNode(AbufNode(index, static_cast<u32>(element), input_mode, buffer)); 104Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) {
105 uses_physical_attributes = true;
106 return StoreNode(AbufNode(GetRegister(physical_address), buffer));
99} 107}
100 108
101Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) { 109Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 65f1e1de9..34d183ec7 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -328,40 +328,31 @@ struct MetaTexture {
328 u32 element{}; 328 u32 element{};
329}; 329};
330 330
331inline constexpr MetaArithmetic PRECISE = {true}; 331constexpr MetaArithmetic PRECISE = {true};
332inline constexpr MetaArithmetic NO_PRECISE = {false}; 332constexpr MetaArithmetic NO_PRECISE = {false};
333 333
334using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>; 334using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>;
335 335
336/// Holds any kind of operation that can be done in the IR 336/// Holds any kind of operation that can be done in the IR
337class OperationNode final { 337class OperationNode final {
338public: 338public:
339 template <typename... T> 339 explicit OperationNode(OperationCode code) : code{code} {}
340 explicit constexpr OperationNode(OperationCode code) : code{code}, meta{} {}
341 340
342 template <typename... T> 341 explicit OperationNode(OperationCode code, Meta&& meta) : code{code}, meta{std::move(meta)} {}
343 explicit constexpr OperationNode(OperationCode code, Meta&& meta)
344 : code{code}, meta{std::move(meta)} {}
345 342
346 template <typename... T> 343 template <typename... T>
347 explicit constexpr OperationNode(OperationCode code, const T*... operands) 344 explicit OperationNode(OperationCode code, const T*... operands)
348 : OperationNode(code, {}, operands...) {} 345 : OperationNode(code, {}, operands...) {}
349 346
350 template <typename... T> 347 template <typename... T>
351 explicit constexpr OperationNode(OperationCode code, Meta&& meta, const T*... operands_) 348 explicit OperationNode(OperationCode code, Meta&& meta, const T*... operands_)
352 : code{code}, meta{std::move(meta)} { 349 : code{code}, meta{std::move(meta)}, operands{operands_...} {}
353
354 auto operands_list = {operands_...};
355 for (auto& operand : operands_list) {
356 operands.push_back(operand);
357 }
358 }
359 350
360 explicit OperationNode(OperationCode code, Meta&& meta, std::vector<Node>&& operands) 351 explicit OperationNode(OperationCode code, Meta&& meta, std::vector<Node>&& operands)
361 : code{code}, meta{meta}, operands{std::move(operands)} {} 352 : code{code}, meta{meta}, operands{std::move(operands)} {}
362 353
363 explicit OperationNode(OperationCode code, std::vector<Node>&& operands) 354 explicit OperationNode(OperationCode code, std::vector<Node>&& operands)
364 : code{code}, meta{}, operands{std::move(operands)} {} 355 : code{code}, operands{std::move(operands)} {}
365 356
366 OperationCode GetCode() const { 357 OperationCode GetCode() const {
367 return code; 358 return code;
@@ -465,17 +456,14 @@ private:
465/// Attribute buffer memory (known as attributes or varyings in GLSL terms) 456/// Attribute buffer memory (known as attributes or varyings in GLSL terms)
466class AbufNode final { 457class AbufNode final {
467public: 458public:
468 explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, 459 // Initialize for standard attributes (index is explicit).
469 const Tegra::Shader::IpaMode& input_mode, Node buffer = {})
470 : input_mode{input_mode}, buffer{buffer}, index{index}, element{element} {}
471
472 explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, 460 explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
473 Node buffer = {}) 461 Node buffer = {})
474 : input_mode{}, buffer{buffer}, index{index}, element{element} {} 462 : buffer{buffer}, index{index}, element{element} {}
475 463
476 Tegra::Shader::IpaMode GetInputMode() const { 464 // Initialize for physical attributes (index is a variable value).
477 return input_mode; 465 explicit constexpr AbufNode(Node physical_address, Node buffer = {})
478 } 466 : physical_address{physical_address}, buffer{buffer} {}
479 467
480 Tegra::Shader::Attribute::Index GetIndex() const { 468 Tegra::Shader::Attribute::Index GetIndex() const {
481 return index; 469 return index;
@@ -489,11 +477,19 @@ public:
489 return buffer; 477 return buffer;
490 } 478 }
491 479
480 bool IsPhysicalBuffer() const {
481 return physical_address != nullptr;
482 }
483
484 Node GetPhysicalAddress() const {
485 return physical_address;
486 }
487
492private: 488private:
493 const Tegra::Shader::IpaMode input_mode; 489 Node physical_address{};
494 const Node buffer; 490 Node buffer{};
495 const Tegra::Shader::Attribute::Index index; 491 Tegra::Shader::Attribute::Index index{};
496 const u32 element; 492 u32 element{};
497}; 493};
498 494
499/// Constant buffer node, usually mapped to uniform buffers in GLSL 495/// Constant buffer node, usually mapped to uniform buffers in GLSL
@@ -567,11 +563,8 @@ private:
567 563
568class ShaderIR final { 564class ShaderIR final {
569public: 565public:
570 explicit ShaderIR(const ProgramCode& program_code, u32 main_offset) 566 explicit ShaderIR(const ProgramCode& program_code, u32 main_offset);
571 : program_code{program_code}, main_offset{main_offset} { 567 ~ShaderIR();
572
573 Decode();
574 }
575 568
576 const std::map<u32, NodeBlock>& GetBasicBlocks() const { 569 const std::map<u32, NodeBlock>& GetBasicBlocks() const {
577 return basic_blocks; 570 return basic_blocks;
@@ -585,8 +578,7 @@ public:
585 return used_predicates; 578 return used_predicates;
586 } 579 }
587 580
588 const std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>& 581 const std::set<Tegra::Shader::Attribute::Index>& GetInputAttributes() const {
589 GetInputAttributes() const {
590 return used_input_attributes; 582 return used_input_attributes;
591 } 583 }
592 584
@@ -615,6 +607,10 @@ public:
615 return static_cast<std::size_t>(coverage_end * sizeof(u64)); 607 return static_cast<std::size_t>(coverage_end * sizeof(u64));
616 } 608 }
617 609
610 bool HasPhysicalAttributes() const {
611 return uses_physical_attributes;
612 }
613
618 const Tegra::Shader::Header& GetHeader() const { 614 const Tegra::Shader::Header& GetHeader() const {
619 return header; 615 return header;
620 } 616 }
@@ -667,7 +663,7 @@ private:
667 /// Creates a conditional node 663 /// Creates a conditional node
668 Node Conditional(Node condition, std::vector<Node>&& code); 664 Node Conditional(Node condition, std::vector<Node>&& code);
669 /// Creates a commentary 665 /// Creates a commentary
670 Node Comment(const std::string& text); 666 Node Comment(std::string text);
671 /// Creates an u32 immediate 667 /// Creates an u32 immediate
672 Node Immediate(u32 value); 668 Node Immediate(u32 value);
673 /// Creates a s32 immediate 669 /// Creates a s32 immediate
@@ -696,8 +692,9 @@ private:
696 /// Generates a predicate node for an immediate true or false value 692 /// Generates a predicate node for an immediate true or false value
697 Node GetPredicate(bool immediate); 693 Node GetPredicate(bool immediate);
698 /// Generates a node representing an input attribute. Keeps track of used attributes. 694 /// Generates a node representing an input attribute. Keeps track of used attributes.
699 Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, 695 Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer = {});
700 const Tegra::Shader::IpaMode& input_mode, Node buffer = {}); 696 /// Generates a node representing a physical input attribute.
697 Node GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer = {});
701 /// Generates a node representing an output attribute. Keeps track of used attributes. 698 /// Generates a node representing an output attribute. Keeps track of used attributes.
702 Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); 699 Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
703 /// Generates a node representing an internal flag 700 /// Generates a node representing an internal flag
@@ -814,11 +811,12 @@ private:
814 void WriteLop3Instruction(NodeBlock& bb, Tegra::Shader::Register dest, Node op_a, Node op_b, 811 void WriteLop3Instruction(NodeBlock& bb, Tegra::Shader::Register dest, Node op_a, Node op_b,
815 Node op_c, Node imm_lut, bool sets_cc); 812 Node op_c, Node imm_lut, bool sets_cc);
816 813
817 Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor); 814 Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const;
818 815
819 std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor); 816 std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const;
820 817
821 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor); 818 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code,
819 s64 cursor) const;
822 820
823 std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb, 821 std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb,
824 Node addr_register, 822 Node addr_register,
@@ -835,12 +833,10 @@ private:
835 return StoreNode(OperationNode(code, std::move(meta), operands...)); 833 return StoreNode(OperationNode(code, std::move(meta), operands...));
836 } 834 }
837 835
838 template <typename... T>
839 Node Operation(OperationCode code, std::vector<Node>&& operands) { 836 Node Operation(OperationCode code, std::vector<Node>&& operands) {
840 return StoreNode(OperationNode(code, std::move(operands))); 837 return StoreNode(OperationNode(code, std::move(operands)));
841 } 838 }
842 839
843 template <typename... T>
844 Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) { 840 Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) {
845 return StoreNode(OperationNode(code, std::move(meta), std::move(operands))); 841 return StoreNode(OperationNode(code, std::move(meta), std::move(operands)));
846 } 842 }
@@ -872,13 +868,13 @@ private:
872 868
873 std::set<u32> used_registers; 869 std::set<u32> used_registers;
874 std::set<Tegra::Shader::Pred> used_predicates; 870 std::set<Tegra::Shader::Pred> used_predicates;
875 std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>> 871 std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
876 used_input_attributes;
877 std::set<Tegra::Shader::Attribute::Index> used_output_attributes; 872 std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
878 std::map<u32, ConstBuffer> used_cbufs; 873 std::map<u32, ConstBuffer> used_cbufs;
879 std::set<Sampler> used_samplers; 874 std::set<Sampler> used_samplers;
880 std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; 875 std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
881 std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; 876 std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
877 bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
882 878
883 Tegra::Shader::Header header; 879 Tegra::Shader::Header header;
884}; 880};
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index 4505667ff..19ede1eb9 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -17,22 +17,24 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
17 for (; cursor >= 0; --cursor) { 17 for (; cursor >= 0; --cursor) {
18 const Node node = code.at(cursor); 18 const Node node = code.at(cursor);
19 if (const auto operation = std::get_if<OperationNode>(node)) { 19 if (const auto operation = std::get_if<OperationNode>(node)) {
20 if (operation->GetCode() == operation_code) 20 if (operation->GetCode() == operation_code) {
21 return {node, cursor}; 21 return {node, cursor};
22 }
22 } 23 }
23 if (const auto conditional = std::get_if<ConditionalNode>(node)) { 24 if (const auto conditional = std::get_if<ConditionalNode>(node)) {
24 const auto& conditional_code = conditional->GetCode(); 25 const auto& conditional_code = conditional->GetCode();
25 const auto [found, internal_cursor] = FindOperation( 26 const auto [found, internal_cursor] = FindOperation(
26 conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code); 27 conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code);
27 if (found) 28 if (found) {
28 return {found, cursor}; 29 return {found, cursor};
30 }
29 } 31 }
30 } 32 }
31 return {}; 33 return {};
32} 34}
33} // namespace 35} // namespace
34 36
35Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) { 37Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const {
36 if (const auto cbuf = std::get_if<CbufNode>(tracked)) { 38 if (const auto cbuf = std::get_if<CbufNode>(tracked)) {
37 // Cbuf found, but it has to be immediate 39 // Cbuf found, but it has to be immediate
38 return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr; 40 return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr;
@@ -65,7 +67,7 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) {
65 return nullptr; 67 return nullptr;
66} 68}
67 69
68std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) { 70std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const {
69 // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register 71 // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register
70 // that it uses as operand 72 // that it uses as operand
71 const auto [found, found_cursor] = 73 const auto [found, found_cursor] =
@@ -80,7 +82,7 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code,
80} 82}
81 83
82std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, 84std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
83 s64 cursor) { 85 s64 cursor) const {
84 for (; cursor >= 0; --cursor) { 86 for (; cursor >= 0; --cursor) {
85 const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign); 87 const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign);
86 if (!found_node) { 88 if (!found_node) {
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 5138bd9a3..7e883991a 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -82,8 +82,6 @@ add_executable(yuzu
82 util/limitable_input_dialog.h 82 util/limitable_input_dialog.h
83 util/sequence_dialog/sequence_dialog.cpp 83 util/sequence_dialog/sequence_dialog.cpp
84 util/sequence_dialog/sequence_dialog.h 84 util/sequence_dialog/sequence_dialog.h
85 util/spinbox.cpp
86 util/spinbox.h
87 util/util.cpp 85 util/util.cpp
88 util/util.h 86 util/util.h
89 compatdb.cpp 87 compatdb.cpp
diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp
index 1fb2fe277..106dde9e2 100644
--- a/src/yuzu/applets/error.cpp
+++ b/src/yuzu/applets/error.cpp
@@ -54,6 +54,6 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
54 54
55void QtErrorDisplay::MainWindowFinishedError() { 55void QtErrorDisplay::MainWindowFinishedError() {
56 // Acquire the HLE mutex 56 // Acquire the HLE mutex
57 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 57 std::lock_guard lock{HLE::g_hle_lock};
58 callback(); 58 callback();
59} 59}
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index 743b24d76..7fbc9deeb 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -84,10 +84,10 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
84 tree_view->setContextMenuPolicy(Qt::NoContextMenu); 84 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
85 85
86 item_model->insertColumns(0, 1); 86 item_model->insertColumns(0, 1);
87 item_model->setHeaderData(0, Qt::Horizontal, "Users"); 87 item_model->setHeaderData(0, Qt::Horizontal, tr("Users"));
88 88
89 // We must register all custom types with the Qt Automoc system so that we are able to use it 89 // We must register all custom types with the Qt Automoc system so that we are able to use it
90 // with signals/slots. In this case, QList falls under the umbrells of custom types. 90 // with signals/slots. In this case, QList falls under the umbrella of custom types.
91 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); 91 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
92 92
93 layout->setContentsMargins(0, 0, 0, 0); 93 layout->setContentsMargins(0, 0, 0, 0);
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 5c98636c5..c2783d684 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -188,7 +188,9 @@ private:
188GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 188GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
189 : QWidget(parent), emu_thread(emu_thread) { 189 : QWidget(parent), emu_thread(emu_thread) {
190 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 190 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
191 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 191 .arg(QString::fromUtf8(Common::g_build_name),
192 QString::fromUtf8(Common::g_scm_branch),
193 QString::fromUtf8(Common::g_scm_desc)));
192 setAttribute(Qt::WA_AcceptTouchEvents); 194 setAttribute(Qt::WA_AcceptTouchEvents);
193 195
194 InputCommon::Init(); 196 InputCommon::Init();
@@ -217,7 +219,7 @@ void GRenderWindow::SwapBuffers() {
217 // However: 219 // However:
218 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called 220 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
219 // since the last time `swapBuffers` was executed; 221 // since the last time `swapBuffers` was executed;
220 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. 222 // - On macOS, if `makeCurrent` isn't called explicitly, resizing the buffer breaks.
221 context->makeCurrent(child); 223 context->makeCurrent(child);
222 224
223 context->swapBuffers(child); 225 context->swapBuffers(child);
@@ -379,6 +381,7 @@ void GRenderWindow::InitRenderTarget() {
379 fmt.setVersion(4, 3); 381 fmt.setVersion(4, 3);
380 if (Settings::values.use_compatibility_profile) { 382 if (Settings::values.use_compatibility_profile) {
381 fmt.setProfile(QSurfaceFormat::CompatibilityProfile); 383 fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
384 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
382 } else { 385 } else {
383 fmt.setProfile(QSurfaceFormat::CoreProfile); 386 fmt.setProfile(QSurfaceFormat::CoreProfile);
384 } 387 }
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 6c6f047d8..d28826c67 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -230,55 +230,64 @@ void Config::ReadPlayerValues() {
230 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 230 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
231 auto& player = Settings::values.players[p]; 231 auto& player = Settings::values.players[p];
232 232
233 player.connected = ReadSetting(QString("player_%1_connected").arg(p), false).toBool(); 233 player.connected =
234 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
234 235
235 player.type = static_cast<Settings::ControllerType>( 236 player.type = static_cast<Settings::ControllerType>(
236 qt_config 237 qt_config
237 ->value(QString("player_%1_type").arg(p), 238 ->value(QStringLiteral("player_%1_type").arg(p),
238 static_cast<u8>(Settings::ControllerType::DualJoycon)) 239 static_cast<u8>(Settings::ControllerType::DualJoycon))
239 .toUInt()); 240 .toUInt());
240 241
241 player.body_color_left = qt_config 242 player.body_color_left = qt_config
242 ->value(QString("player_%1_body_color_left").arg(p), 243 ->value(QStringLiteral("player_%1_body_color_left").arg(p),
243 Settings::JOYCON_BODY_NEON_BLUE) 244 Settings::JOYCON_BODY_NEON_BLUE)
244 .toUInt(); 245 .toUInt();
245 player.body_color_right = qt_config 246 player.body_color_right = qt_config
246 ->value(QString("player_%1_body_color_right").arg(p), 247 ->value(QStringLiteral("player_%1_body_color_right").arg(p),
247 Settings::JOYCON_BODY_NEON_RED) 248 Settings::JOYCON_BODY_NEON_RED)
248 .toUInt(); 249 .toUInt();
249 player.button_color_left = qt_config 250 player.button_color_left = qt_config
250 ->value(QString("player_%1_button_color_left").arg(p), 251 ->value(QStringLiteral("player_%1_button_color_left").arg(p),
251 Settings::JOYCON_BUTTONS_NEON_BLUE) 252 Settings::JOYCON_BUTTONS_NEON_BLUE)
252 .toUInt(); 253 .toUInt();
253 player.button_color_right = qt_config 254 player.button_color_right =
254 ->value(QString("player_%1_button_color_right").arg(p), 255 qt_config
255 Settings::JOYCON_BUTTONS_NEON_RED) 256 ->value(QStringLiteral("player_%1_button_color_right").arg(p),
256 .toUInt(); 257 Settings::JOYCON_BUTTONS_NEON_RED)
258 .toUInt();
257 259
258 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 260 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
259 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 261 const std::string default_param =
260 player.buttons[i] = 262 InputCommon::GenerateKeyboardParam(default_buttons[i]);
261 qt_config 263 auto& player_buttons = player.buttons[i];
262 ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i], 264
263 QString::fromStdString(default_param)) 265 player_buttons = qt_config
264 .toString() 266 ->value(QStringLiteral("player_%1_").arg(p) +
265 .toStdString(); 267 QString::fromUtf8(Settings::NativeButton::mapping[i]),
266 if (player.buttons[i].empty()) 268 QString::fromStdString(default_param))
267 player.buttons[i] = default_param; 269 .toString()
270 .toStdString();
271 if (player_buttons.empty()) {
272 player_buttons = default_param;
273 }
268 } 274 }
269 275
270 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 276 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
271 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 277 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
272 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 278 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
273 default_analogs[i][3], default_analogs[i][4], 0.5f); 279 default_analogs[i][3], default_analogs[i][4], 0.5f);
274 player.analogs[i] = 280 auto& player_analogs = player.analogs[i];
275 qt_config 281
276 ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i], 282 player_analogs = qt_config
277 QString::fromStdString(default_param)) 283 ->value(QStringLiteral("player_%1_").arg(p) +
278 .toString() 284 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
279 .toStdString(); 285 QString::fromStdString(default_param))
280 if (player.analogs[i].empty()) 286 .toString()
281 player.analogs[i] = default_param; 287 .toStdString();
288 if (player_analogs.empty()) {
289 player_analogs = default_param;
290 }
282 } 291 }
283 } 292 }
284 293
@@ -290,36 +299,45 @@ void Config::ReadPlayerValues() {
290} 299}
291 300
292void Config::ReadDebugValues() { 301void Config::ReadDebugValues() {
293 Settings::values.debug_pad_enabled = ReadSetting("debug_pad_enabled", false).toBool(); 302 Settings::values.debug_pad_enabled =
303 ReadSetting(QStringLiteral("debug_pad_enabled"), false).toBool();
304
294 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 305 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
295 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 306 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
296 Settings::values.debug_pad_buttons[i] = 307 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
297 qt_config 308
298 ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i], 309 debug_pad_buttons = qt_config
299 QString::fromStdString(default_param)) 310 ->value(QStringLiteral("debug_pad_") +
300 .toString() 311 QString::fromUtf8(Settings::NativeButton::mapping[i]),
301 .toStdString(); 312 QString::fromStdString(default_param))
302 if (Settings::values.debug_pad_buttons[i].empty()) 313 .toString()
303 Settings::values.debug_pad_buttons[i] = default_param; 314 .toStdString();
315 if (debug_pad_buttons.empty()) {
316 debug_pad_buttons = default_param;
317 }
304 } 318 }
305 319
306 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 320 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
307 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 321 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
308 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 322 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
309 default_analogs[i][3], default_analogs[i][4], 0.5f); 323 default_analogs[i][3], default_analogs[i][4], 0.5f);
310 Settings::values.debug_pad_analogs[i] = 324 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
311 qt_config 325
312 ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i], 326 debug_pad_analogs = qt_config
313 QString::fromStdString(default_param)) 327 ->value(QStringLiteral("debug_pad_") +
314 .toString() 328 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
315 .toStdString(); 329 QString::fromStdString(default_param))
316 if (Settings::values.debug_pad_analogs[i].empty()) 330 .toString()
317 Settings::values.debug_pad_analogs[i] = default_param; 331 .toStdString();
332 if (debug_pad_analogs.empty()) {
333 debug_pad_analogs = default_param;
334 }
318 } 335 }
319} 336}
320 337
321void Config::ReadKeyboardValues() { 338void Config::ReadKeyboardValues() {
322 Settings::values.keyboard_enabled = ReadSetting("keyboard_enabled", false).toBool(); 339 Settings::values.keyboard_enabled =
340 ReadSetting(QStringLiteral("keyboard_enabled"), false).toBool();
323 341
324 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), 342 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
325 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); 343 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
@@ -332,18 +350,22 @@ void Config::ReadKeyboardValues() {
332} 350}
333 351
334void Config::ReadMouseValues() { 352void Config::ReadMouseValues() {
335 Settings::values.mouse_enabled = ReadSetting("mouse_enabled", false).toBool(); 353 Settings::values.mouse_enabled = ReadSetting(QStringLiteral("mouse_enabled"), false).toBool();
336 354
337 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 355 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
338 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); 356 const std::string default_param =
339 Settings::values.mouse_buttons[i] = 357 InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
340 qt_config 358 auto& mouse_buttons = Settings::values.mouse_buttons[i];
341 ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i], 359
342 QString::fromStdString(default_param)) 360 mouse_buttons = qt_config
343 .toString() 361 ->value(QStringLiteral("mouse_") +
344 .toStdString(); 362 QString::fromUtf8(Settings::NativeMouseButton::mapping[i]),
345 if (Settings::values.mouse_buttons[i].empty()) 363 QString::fromStdString(default_param))
346 Settings::values.mouse_buttons[i] = default_param; 364 .toString()
365 .toStdString();
366 if (mouse_buttons.empty()) {
367 mouse_buttons = default_param;
368 }
347 } 369 }
348} 370}
349 371
@@ -356,7 +378,6 @@ void Config::ReadTouchscreenValues() {
356 Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt(); 378 Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt();
357 Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt(); 379 Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt();
358 Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt(); 380 Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt();
359 qt_config->endGroup();
360} 381}
361 382
362void Config::ApplyDefaultProfileIfInputInvalid() { 383void Config::ApplyDefaultProfileIfInputInvalid() {
@@ -366,8 +387,25 @@ void Config::ApplyDefaultProfileIfInputInvalid() {
366 } 387 }
367} 388}
368 389
369void Config::ReadValues() { 390void Config::ReadAudioValues() {
370 qt_config->beginGroup("Controls"); 391 qt_config->beginGroup(QStringLiteral("Audio"));
392
393 Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
394 .toString()
395 .toStdString();
396 Settings::values.enable_audio_stretching =
397 ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool();
398 Settings::values.audio_device_id =
399 ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
400 .toString()
401 .toStdString();
402 Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat();
403
404 qt_config->endGroup();
405}
406
407void Config::ReadControlValues() {
408 qt_config->beginGroup(QStringLiteral("Controls"));
371 409
372 ReadPlayerValues(); 410 ReadPlayerValues();
373 ReadDebugValues(); 411 ReadDebugValues();
@@ -376,162 +414,133 @@ void Config::ReadValues() {
376 ReadTouchscreenValues(); 414 ReadTouchscreenValues();
377 415
378 Settings::values.motion_device = 416 Settings::values.motion_device =
379 ReadSetting("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") 417 ReadSetting(QStringLiteral("motion_device"),
418 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
380 .toString() 419 .toString()
381 .toStdString(); 420 .toStdString();
382 421
383 qt_config->beginGroup("Core");
384 Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
385 Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
386 qt_config->endGroup(); 422 qt_config->endGroup();
423}
387 424
388 qt_config->beginGroup("Renderer"); 425void Config::ReadCoreValues() {
389 Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat(); 426 qt_config->beginGroup(QStringLiteral("Core"));
390 Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool();
391 Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt();
392 Settings::values.use_compatibility_profile =
393 ReadSetting("use_compatibility_profile", true).toBool();
394 Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool();
395 Settings::values.use_accurate_gpu_emulation =
396 ReadSetting("use_accurate_gpu_emulation", false).toBool();
397 Settings::values.use_asynchronous_gpu_emulation =
398 ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
399 Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool();
400 427
401 Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); 428 Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool();
402 Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); 429 Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool();
403 Settings::values.bg_blue = ReadSetting("bg_blue", 0.0).toFloat();
404 qt_config->endGroup();
405 430
406 qt_config->beginGroup("Audio");
407 Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString();
408 Settings::values.enable_audio_stretching =
409 ReadSetting("enable_audio_stretching", true).toBool();
410 Settings::values.audio_device_id =
411 ReadSetting("output_device", "auto").toString().toStdString();
412 Settings::values.volume = ReadSetting("volume", 1).toFloat();
413 qt_config->endGroup(); 431 qt_config->endGroup();
432}
433
434void Config::ReadDataStorageValues() {
435 qt_config->beginGroup(QStringLiteral("Data Storage"));
414 436
415 qt_config->beginGroup("Data Storage"); 437 Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
416 Settings::values.use_virtual_sd = ReadSetting("use_virtual_sd", true).toBool();
417 FileUtil::GetUserPath( 438 FileUtil::GetUserPath(
418 FileUtil::UserPath::NANDDir, 439 FileUtil::UserPath::NANDDir,
419 qt_config 440 qt_config
420 ->value("nand_directory", 441 ->value(QStringLiteral("nand_directory"),
421 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))) 442 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)))
422 .toString() 443 .toString()
423 .toStdString()); 444 .toStdString());
424 FileUtil::GetUserPath( 445 FileUtil::GetUserPath(
425 FileUtil::UserPath::SDMCDir, 446 FileUtil::UserPath::SDMCDir,
426 qt_config 447 qt_config
427 ->value("sdmc_directory", 448 ->value(QStringLiteral("sdmc_directory"),
428 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) 449 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
429 .toString() 450 .toString()
430 .toStdString()); 451 .toStdString());
431 qt_config->endGroup();
432 452
433 qt_config->beginGroup("Core");
434 Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
435 Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
436 qt_config->endGroup(); 453 qt_config->endGroup();
454}
437 455
438 qt_config->beginGroup("System"); 456void Config::ReadDebuggingValues() {
439 Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool(); 457 qt_config->beginGroup(QStringLiteral("Debugging"));
440
441 Settings::values.current_user =
442 std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1);
443
444 Settings::values.language_index = ReadSetting("language_index", 1).toInt();
445
446 const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool();
447 if (rng_seed_enabled) {
448 Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong();
449 } else {
450 Settings::values.rng_seed = std::nullopt;
451 }
452
453 const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool();
454 if (custom_rtc_enabled) {
455 Settings::values.custom_rtc =
456 std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong());
457 } else {
458 Settings::values.custom_rtc = std::nullopt;
459 }
460
461 qt_config->endGroup();
462 458
463 qt_config->beginGroup("Miscellaneous"); 459 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
464 Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString(); 460 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
465 Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool(); 461 Settings::values.program_args =
466 qt_config->endGroup(); 462 ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString();
463 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
464 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
467 465
468 qt_config->beginGroup("Debugging");
469 Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool();
470 Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt();
471 Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString();
472 Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool();
473 Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool();
474 qt_config->endGroup(); 466 qt_config->endGroup();
467}
475 468
476 qt_config->beginGroup("WebService"); 469void Config::ReadDisabledAddOnValues() {
477 Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool(); 470 const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns"));
478 Settings::values.web_api_url =
479 ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString();
480 Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString();
481 Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString();
482 qt_config->endGroup();
483 471
484 const auto size = qt_config->beginReadArray("DisabledAddOns");
485 for (int i = 0; i < size; ++i) { 472 for (int i = 0; i < size; ++i) {
486 qt_config->setArrayIndex(i); 473 qt_config->setArrayIndex(i);
487 const auto title_id = ReadSetting("title_id", 0).toULongLong(); 474 const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong();
488 std::vector<std::string> out; 475 std::vector<std::string> out;
489 const auto d_size = qt_config->beginReadArray("disabled"); 476 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
490 for (int j = 0; j < d_size; ++j) { 477 for (int j = 0; j < d_size; ++j) {
491 qt_config->setArrayIndex(j); 478 qt_config->setArrayIndex(j);
492 out.push_back(ReadSetting("d", "").toString().toStdString()); 479 out.push_back(
480 ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString());
493 } 481 }
494 qt_config->endArray(); 482 qt_config->endArray();
495 Settings::values.disabled_addons.insert_or_assign(title_id, out); 483 Settings::values.disabled_addons.insert_or_assign(title_id, out);
496 } 484 }
485
497 qt_config->endArray(); 486 qt_config->endArray();
487}
498 488
499 qt_config->beginGroup("UI"); 489void Config::ReadMiscellaneousValues() {
500 UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString(); 490 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
501 UISettings::values.enable_discord_presence = 491
502 ReadSetting("enable_discord_presence", true).toBool(); 492 Settings::values.log_filter =
503 UISettings::values.screenshot_resolution_factor = 493 ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info"))
504 static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt()); 494 .toString()
505 UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool(); 495 .toStdString();
496 Settings::values.use_dev_keys = ReadSetting(QStringLiteral("use_dev_keys"), false).toBool();
506 497
507 qt_config->beginGroup("UIGameList");
508 UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool();
509 UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool();
510 UISettings::values.icon_size = ReadSetting("icon_size", 64).toUInt();
511 UISettings::values.row_1_text_id = ReadSetting("row_1_text_id", 3).toUInt();
512 UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt();
513 qt_config->endGroup(); 498 qt_config->endGroup();
499}
500
501void Config::ReadPathValues() {
502 qt_config->beginGroup(QStringLiteral("Paths"));
503
504 UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
505 UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
506 UISettings::values.game_directory_path =
507 ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();
508 UISettings::values.game_directory_deepscan =
509 ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool();
510 UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
514 511
515 qt_config->beginGroup("UILayout");
516 UISettings::values.geometry = ReadSetting("geometry").toByteArray();
517 UISettings::values.state = ReadSetting("state").toByteArray();
518 UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray();
519 UISettings::values.gamelist_header_state = ReadSetting("gameListHeaderState").toByteArray();
520 UISettings::values.microprofile_geometry =
521 ReadSetting("microProfileDialogGeometry").toByteArray();
522 UISettings::values.microprofile_visible =
523 ReadSetting("microProfileDialogVisible", false).toBool();
524 qt_config->endGroup(); 512 qt_config->endGroup();
513}
514
515void Config::ReadRendererValues() {
516 qt_config->beginGroup(QStringLiteral("Renderer"));
517
518 Settings::values.resolution_factor =
519 ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat();
520 Settings::values.use_frame_limit =
521 ReadSetting(QStringLiteral("use_frame_limit"), true).toBool();
522 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
523 Settings::values.use_compatibility_profile =
524 ReadSetting(QStringLiteral("use_compatibility_profile"), true).toBool();
525 Settings::values.use_disk_shader_cache =
526 ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
527 Settings::values.use_accurate_gpu_emulation =
528 ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool();
529 Settings::values.use_asynchronous_gpu_emulation =
530 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
531 Settings::values.force_30fps_mode =
532 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
533
534 Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
535 Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
536 Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
525 537
526 qt_config->beginGroup("Paths");
527 UISettings::values.roms_path = ReadSetting("romsPath").toString();
528 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
529 UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString();
530 UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool();
531 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
532 qt_config->endGroup(); 538 qt_config->endGroup();
539}
540
541void Config::ReadShortcutValues() {
542 qt_config->beginGroup(QStringLiteral("Shortcuts"));
533 543
534 qt_config->beginGroup("Shortcuts");
535 for (auto [name, group, shortcut] : default_hotkeys) { 544 for (auto [name, group, shortcut] : default_hotkeys) {
536 auto [keyseq, context] = shortcut; 545 auto [keyseq, context] = shortcut;
537 qt_config->beginGroup(group); 546 qt_config->beginGroup(group);
@@ -539,57 +548,173 @@ void Config::ReadValues() {
539 UISettings::values.shortcuts.push_back( 548 UISettings::values.shortcuts.push_back(
540 {name, 549 {name,
541 group, 550 group,
542 {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}}); 551 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(),
552 ReadSetting(QStringLiteral("Context"), context).toInt()}});
543 qt_config->endGroup(); 553 qt_config->endGroup();
544 qt_config->endGroup(); 554 qt_config->endGroup();
545 } 555 }
556
557 qt_config->endGroup();
558}
559
560void Config::ReadSystemValues() {
561 qt_config->beginGroup(QStringLiteral("System"));
562
563 Settings::values.use_docked_mode =
564 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
565
566 Settings::values.current_user = std::clamp<int>(
567 ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1);
568
569 Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt();
570
571 const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool();
572 if (rng_seed_enabled) {
573 Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong();
574 } else {
575 Settings::values.rng_seed = std::nullopt;
576 }
577
578 const auto custom_rtc_enabled =
579 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
580 if (custom_rtc_enabled) {
581 Settings::values.custom_rtc =
582 std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong());
583 } else {
584 Settings::values.custom_rtc = std::nullopt;
585 }
586
546 qt_config->endGroup(); 587 qt_config->endGroup();
588}
547 589
548 UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool(); 590void Config::ReadUIValues() {
549 UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool(); 591 qt_config->beginGroup(QStringLiteral("UI"));
550 UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool(); 592
551 UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool(); 593 UISettings::values.theme =
552 UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool(); 594 ReadSetting(QStringLiteral("theme"), UISettings::themes[0].second).toString();
553 UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool(); 595 UISettings::values.enable_discord_presence =
554 UISettings::values.first_start = ReadSetting("firstStart", true).toBool(); 596 ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool();
555 UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt(); 597 UISettings::values.screenshot_resolution_factor =
556 UISettings::values.show_console = ReadSetting("showConsole", false).toBool(); 598 static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt());
557 UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt(); 599 UISettings::values.select_user_on_boot =
600 ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool();
601
602 ReadUIGamelistValues();
603 ReadUILayoutValues();
604 ReadPathValues();
605 ReadShortcutValues();
606
607 UISettings::values.single_window_mode =
608 ReadSetting(QStringLiteral("singleWindowMode"), true).toBool();
609 UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool();
610 UISettings::values.display_titlebar =
611 ReadSetting(QStringLiteral("displayTitleBars"), true).toBool();
612 UISettings::values.show_filter_bar =
613 ReadSetting(QStringLiteral("showFilterBar"), true).toBool();
614 UISettings::values.show_status_bar =
615 ReadSetting(QStringLiteral("showStatusBar"), true).toBool();
616 UISettings::values.confirm_before_closing =
617 ReadSetting(QStringLiteral("confirmClose"), true).toBool();
618 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
619 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
620 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
621 UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
558 622
559 ApplyDefaultProfileIfInputInvalid(); 623 ApplyDefaultProfileIfInputInvalid();
560 624
561 qt_config->endGroup(); 625 qt_config->endGroup();
562} 626}
563 627
628void Config::ReadUIGamelistValues() {
629 qt_config->beginGroup("UIGameList");
630
631 UISettings::values.show_unknown = ReadSetting(QStringLiteral("show_unknown"), true).toBool();
632 UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool();
633 UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt();
634 UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt();
635 UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt();
636
637 qt_config->endGroup();
638}
639
640void Config::ReadUILayoutValues() {
641 qt_config->beginGroup(QStringLiteral("UILayout"));
642
643 UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray();
644 UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray();
645 UISettings::values.renderwindow_geometry =
646 ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray();
647 UISettings::values.gamelist_header_state =
648 ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
649 UISettings::values.microprofile_geometry =
650 ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
651 UISettings::values.microprofile_visible =
652 ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool();
653
654 qt_config->endGroup();
655}
656
657void Config::ReadWebServiceValues() {
658 qt_config->beginGroup(QStringLiteral("WebService"));
659
660 Settings::values.enable_telemetry =
661 ReadSetting(QStringLiteral("enable_telemetry"), true).toBool();
662 Settings::values.web_api_url =
663 ReadSetting(QStringLiteral("web_api_url"), QStringLiteral("https://api.yuzu-emu.org"))
664 .toString()
665 .toStdString();
666 Settings::values.yuzu_username =
667 ReadSetting(QStringLiteral("yuzu_username")).toString().toStdString();
668 Settings::values.yuzu_token =
669 ReadSetting(QStringLiteral("yuzu_token")).toString().toStdString();
670
671 qt_config->endGroup();
672}
673
674void Config::ReadValues() {
675 ReadControlValues();
676 ReadCoreValues();
677 ReadRendererValues();
678 ReadAudioValues();
679 ReadDataStorageValues();
680 ReadSystemValues();
681 ReadMiscellaneousValues();
682 ReadDebugValues();
683 ReadWebServiceValues();
684 ReadDisabledAddOnValues();
685 ReadUIValues();
686}
687
564void Config::SavePlayerValues() { 688void Config::SavePlayerValues() {
565 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 689 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
566 const auto& player = Settings::values.players[p]; 690 const auto& player = Settings::values.players[p];
567 691
568 WriteSetting(QString("player_%1_connected").arg(p), player.connected, false); 692 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
569 WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type), 693 WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type),
570 static_cast<u8>(Settings::ControllerType::DualJoycon)); 694 static_cast<u8>(Settings::ControllerType::DualJoycon));
571 695
572 WriteSetting(QString("player_%1_body_color_left").arg(p), player.body_color_left, 696 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
573 Settings::JOYCON_BODY_NEON_BLUE); 697 Settings::JOYCON_BODY_NEON_BLUE);
574 WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right, 698 WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right,
575 Settings::JOYCON_BODY_NEON_RED); 699 Settings::JOYCON_BODY_NEON_RED);
576 WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left, 700 WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left,
577 Settings::JOYCON_BUTTONS_NEON_BLUE); 701 Settings::JOYCON_BUTTONS_NEON_BLUE);
578 WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right, 702 WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p),
579 Settings::JOYCON_BUTTONS_NEON_RED); 703 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
580 704
581 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 705 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
582 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 706 const std::string default_param =
583 WriteSetting(QString("player_%1_").arg(p) + 707 InputCommon::GenerateKeyboardParam(default_buttons[i]);
708 WriteSetting(QStringLiteral("player_%1_").arg(p) +
584 QString::fromStdString(Settings::NativeButton::mapping[i]), 709 QString::fromStdString(Settings::NativeButton::mapping[i]),
585 QString::fromStdString(player.buttons[i]), 710 QString::fromStdString(player.buttons[i]),
586 QString::fromStdString(default_param)); 711 QString::fromStdString(default_param));
587 } 712 }
588 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 713 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
589 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 714 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
590 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 715 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
591 default_analogs[i][3], default_analogs[i][4], 0.5f); 716 default_analogs[i][3], default_analogs[i][4], 0.5f);
592 WriteSetting(QString("player_%1_").arg(p) + 717 WriteSetting(QStringLiteral("player_%1_").arg(p) +
593 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 718 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
594 QString::fromStdString(player.analogs[i]), 719 QString::fromStdString(player.analogs[i]),
595 QString::fromStdString(default_param)); 720 QString::fromStdString(default_param));
@@ -600,17 +725,17 @@ void Config::SavePlayerValues() {
600void Config::SaveDebugValues() { 725void Config::SaveDebugValues() {
601 WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false); 726 WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false);
602 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 727 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
603 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 728 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
604 WriteSetting(QString("debug_pad_") + 729 WriteSetting(QStringLiteral("debug_pad_") +
605 QString::fromStdString(Settings::NativeButton::mapping[i]), 730 QString::fromStdString(Settings::NativeButton::mapping[i]),
606 QString::fromStdString(Settings::values.debug_pad_buttons[i]), 731 QString::fromStdString(Settings::values.debug_pad_buttons[i]),
607 QString::fromStdString(default_param)); 732 QString::fromStdString(default_param));
608 } 733 }
609 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 734 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
610 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 735 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
611 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 736 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
612 default_analogs[i][3], default_analogs[i][4], 0.5f); 737 default_analogs[i][3], default_analogs[i][4], 0.5f);
613 WriteSetting(QString("debug_pad_") + 738 WriteSetting(QStringLiteral("debug_pad_") +
614 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 739 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
615 QString::fromStdString(Settings::values.debug_pad_analogs[i]), 740 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
616 QString::fromStdString(default_param)); 741 QString::fromStdString(default_param));
@@ -618,11 +743,12 @@ void Config::SaveDebugValues() {
618} 743}
619 744
620void Config::SaveMouseValues() { 745void Config::SaveMouseValues() {
621 WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false); 746 WriteSetting(QStringLiteral("mouse_enabled"), Settings::values.mouse_enabled, false);
622 747
623 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 748 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
624 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); 749 const std::string default_param =
625 WriteSetting(QString("mouse_") + 750 InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
751 WriteSetting(QStringLiteral("mouse_") +
626 QString::fromStdString(Settings::NativeMouseButton::mapping[i]), 752 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
627 QString::fromStdString(Settings::values.mouse_buttons[i]), 753 QString::fromStdString(Settings::values.mouse_buttons[i]),
628 QString::fromStdString(default_param)); 754 QString::fromStdString(default_param));
@@ -630,178 +756,276 @@ void Config::SaveMouseValues() {
630} 756}
631 757
632void Config::SaveTouchscreenValues() { 758void Config::SaveTouchscreenValues() {
633 WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true); 759 const auto& touchscreen = Settings::values.touchscreen;
634 WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device), 760
635 "engine:emu_window"); 761 WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true);
762 WriteSetting(QStringLiteral("touchscreen_device"), QString::fromStdString(touchscreen.device),
763 QStringLiteral("engine:emu_window"));
636 764
637 WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0); 765 WriteSetting(QStringLiteral("touchscreen_finger"), touchscreen.finger, 0);
638 WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0); 766 WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0);
639 WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15); 767 WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15);
640 WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15); 768 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
641} 769}
642 770
643void Config::SaveValues() { 771void Config::SaveValues() {
644 qt_config->beginGroup("Controls"); 772 SaveControlValues();
773 SaveCoreValues();
774 SaveRendererValues();
775 SaveAudioValues();
776 SaveDataStorageValues();
777 SaveSystemValues();
778 SaveMiscellaneousValues();
779 SaveDebuggingValues();
780 SaveWebServiceValues();
781 SaveDisabledAddOnValues();
782 SaveUIValues();
783}
784
785void Config::SaveAudioValues() {
786 qt_config->beginGroup(QStringLiteral("Audio"));
787
788 WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id),
789 QStringLiteral("auto"));
790 WriteSetting(QStringLiteral("enable_audio_stretching"),
791 Settings::values.enable_audio_stretching, true);
792 WriteSetting(QStringLiteral("output_device"),
793 QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto"));
794 WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f);
795
796 qt_config->endGroup();
797}
798
799void Config::SaveControlValues() {
800 qt_config->beginGroup(QStringLiteral("Controls"));
645 801
646 SavePlayerValues(); 802 SavePlayerValues();
647 SaveDebugValues(); 803 SaveDebugValues();
648 SaveMouseValues(); 804 SaveMouseValues();
649 SaveTouchscreenValues(); 805 SaveTouchscreenValues();
650 806
651 WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device), 807 WriteSetting(QStringLiteral("motion_device"),
652 "engine:motion_emu,update_period:100,sensitivity:0.01"); 808 QString::fromStdString(Settings::values.motion_device),
653 WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false); 809 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
810 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
654 811
655 qt_config->endGroup(); 812 qt_config->endGroup();
813}
656 814
657 qt_config->beginGroup("Core"); 815void Config::SaveCoreValues() {
658 WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true); 816 qt_config->beginGroup(QStringLiteral("Core"));
659 WriteSetting("use_multi_core", Settings::values.use_multi_core, false);
660 qt_config->endGroup();
661 817
662 qt_config->beginGroup("Renderer"); 818 WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true);
663 WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0); 819 WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
664 WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true);
665 WriteSetting("frame_limit", Settings::values.frame_limit, 100);
666 WriteSetting("use_compatibility_profile", Settings::values.use_compatibility_profile, true);
667 WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true);
668 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
669 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
670 false);
671 WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false);
672 820
673 // Cast to double because Qt's written float values are not human-readable
674 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
675 WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0);
676 WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0);
677 qt_config->endGroup(); 821 qt_config->endGroup();
822}
678 823
679 qt_config->beginGroup("Audio"); 824void Config::SaveDataStorageValues() {
680 WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto"); 825 qt_config->beginGroup(QStringLiteral("Data Storage"));
681 WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
682 WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
683 WriteSetting("volume", Settings::values.volume, 1.0f);
684 qt_config->endGroup();
685 826
686 qt_config->beginGroup("Data Storage"); 827 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
687 WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true); 828 WriteSetting(QStringLiteral("nand_directory"),
688 WriteSetting("nand_directory",
689 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), 829 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
690 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 830 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
691 WriteSetting("sdmc_directory", 831 WriteSetting(QStringLiteral("sdmc_directory"),
692 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), 832 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
693 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 833 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
694 qt_config->endGroup();
695
696 qt_config->beginGroup("System");
697 WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false);
698 WriteSetting("current_user", Settings::values.current_user, 0);
699 WriteSetting("language_index", Settings::values.language_index, 1);
700
701 WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false);
702 WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0);
703
704 WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false);
705 WriteSetting("custom_rtc",
706 QVariant::fromValue<long long>(
707 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
708 0);
709 834
710 qt_config->endGroup(); 835 qt_config->endGroup();
836}
711 837
712 qt_config->beginGroup("Miscellaneous"); 838void Config::SaveDebuggingValues() {
713 WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info"); 839 qt_config->beginGroup(QStringLiteral("Debugging"));
714 WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false);
715 qt_config->endGroup();
716 840
717 qt_config->beginGroup("Debugging"); 841 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
718 WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false); 842 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
719 WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689); 843 WriteSetting(QStringLiteral("program_args"),
720 WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), ""); 844 QString::fromStdString(Settings::values.program_args), QStringLiteral(""));
721 WriteSetting("dump_exefs", Settings::values.dump_exefs, false); 845 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
722 WriteSetting("dump_nso", Settings::values.dump_nso, false); 846 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
723 qt_config->endGroup();
724 847
725 qt_config->beginGroup("WebService");
726 WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true);
727 WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url),
728 "https://api.yuzu-emu.org");
729 WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username));
730 WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
731 qt_config->endGroup(); 848 qt_config->endGroup();
849}
850
851void Config::SaveDisabledAddOnValues() {
852 qt_config->beginWriteArray(QStringLiteral("DisabledAddOns"));
732 853
733 qt_config->beginWriteArray("DisabledAddOns");
734 int i = 0; 854 int i = 0;
735 for (const auto& elem : Settings::values.disabled_addons) { 855 for (const auto& elem : Settings::values.disabled_addons) {
736 qt_config->setArrayIndex(i); 856 qt_config->setArrayIndex(i);
737 WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0); 857 WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0);
738 qt_config->beginWriteArray("disabled"); 858 qt_config->beginWriteArray(QStringLiteral("disabled"));
739 for (std::size_t j = 0; j < elem.second.size(); ++j) { 859 for (std::size_t j = 0; j < elem.second.size(); ++j) {
740 qt_config->setArrayIndex(static_cast<int>(j)); 860 qt_config->setArrayIndex(static_cast<int>(j));
741 WriteSetting("d", QString::fromStdString(elem.second[j]), ""); 861 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]),
862 QStringLiteral(""));
742 } 863 }
743 qt_config->endArray(); 864 qt_config->endArray();
744 ++i; 865 ++i;
745 } 866 }
867
746 qt_config->endArray(); 868 qt_config->endArray();
869}
747 870
748 qt_config->beginGroup("UI"); 871void Config::SaveMiscellaneousValues() {
749 WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second); 872 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
750 WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true); 873
751 WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor, 874 WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter),
752 0); 875 QStringLiteral("*:Info"));
753 WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false); 876 WriteSetting(QStringLiteral("use_dev_keys"), Settings::values.use_dev_keys, false);
754 877
755 qt_config->beginGroup("UIGameList");
756 WriteSetting("show_unknown", UISettings::values.show_unknown, true);
757 WriteSetting("show_add_ons", UISettings::values.show_add_ons, true);
758 WriteSetting("icon_size", UISettings::values.icon_size, 64);
759 WriteSetting("row_1_text_id", UISettings::values.row_1_text_id, 3);
760 WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2);
761 qt_config->endGroup(); 878 qt_config->endGroup();
879}
880
881void Config::SavePathValues() {
882 qt_config->beginGroup(QStringLiteral("Paths"));
883
884 WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);
885 WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path);
886 WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path);
887 WriteSetting(QStringLiteral("gameListRootDir"), UISettings::values.game_directory_path,
888 QStringLiteral("."));
889 WriteSetting(QStringLiteral("gameListDeepScan"), UISettings::values.game_directory_deepscan,
890 false);
891 WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
762 892
763 qt_config->beginGroup("UILayout");
764 WriteSetting("geometry", UISettings::values.geometry);
765 WriteSetting("state", UISettings::values.state);
766 WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry);
767 WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state);
768 WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
769 WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false);
770 qt_config->endGroup(); 893 qt_config->endGroup();
894}
895
896void Config::SaveRendererValues() {
897 qt_config->beginGroup(QStringLiteral("Renderer"));
898
899 WriteSetting(QStringLiteral("resolution_factor"),
900 static_cast<double>(Settings::values.resolution_factor), 1.0);
901 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
902 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
903 WriteSetting(QStringLiteral("use_compatibility_profile"),
904 Settings::values.use_compatibility_profile, true);
905 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
906 true);
907 WriteSetting(QStringLiteral("use_accurate_gpu_emulation"),
908 Settings::values.use_accurate_gpu_emulation, false);
909 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
910 Settings::values.use_asynchronous_gpu_emulation, false);
911 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
912
913 // Cast to double because Qt's written float values are not human-readable
914 WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0);
915 WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0);
916 WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0);
771 917
772 qt_config->beginGroup("Paths");
773 WriteSetting("romsPath", UISettings::values.roms_path);
774 WriteSetting("symbolsPath", UISettings::values.symbols_path);
775 WriteSetting("screenshotPath", UISettings::values.screenshot_path);
776 WriteSetting("gameListRootDir", UISettings::values.game_directory_path, ".");
777 WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false);
778 WriteSetting("recentFiles", UISettings::values.recent_files);
779 qt_config->endGroup(); 918 qt_config->endGroup();
919}
920
921void Config::SaveShortcutValues() {
922 qt_config->beginGroup(QStringLiteral("Shortcuts"));
780 923
781 qt_config->beginGroup("Shortcuts");
782 // Lengths of UISettings::values.shortcuts & default_hotkeys are same. 924 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
783 // However, their ordering must also be the same. 925 // However, their ordering must also be the same.
784 for (std::size_t i = 0; i < default_hotkeys.size(); i++) { 926 for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
785 auto [name, group, shortcut] = UISettings::values.shortcuts[i]; 927 const auto [name, group, shortcut] = UISettings::values.shortcuts[i];
928 const auto& default_hotkey = default_hotkeys[i].shortcut;
929
786 qt_config->beginGroup(group); 930 qt_config->beginGroup(group);
787 qt_config->beginGroup(name); 931 qt_config->beginGroup(name);
788 WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first); 932 WriteSetting(QStringLiteral("KeySeq"), shortcut.first, default_hotkey.first);
789 WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second); 933 WriteSetting(QStringLiteral("Context"), shortcut.second, default_hotkey.second);
790 qt_config->endGroup(); 934 qt_config->endGroup();
791 qt_config->endGroup(); 935 qt_config->endGroup();
792 } 936 }
937
938 qt_config->endGroup();
939}
940
941void Config::SaveSystemValues() {
942 qt_config->beginGroup(QStringLiteral("System"));
943
944 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
945 WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
946 WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1);
947
948 WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false);
949 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0);
950
951 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
952 false);
953 WriteSetting(QStringLiteral("custom_rtc"),
954 QVariant::fromValue<long long>(
955 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
956 0);
957
958 qt_config->endGroup();
959}
960
961void Config::SaveUIValues() {
962 qt_config->beginGroup(QStringLiteral("UI"));
963
964 WriteSetting(QStringLiteral("theme"), UISettings::values.theme, UISettings::themes[0].second);
965 WriteSetting(QStringLiteral("enable_discord_presence"),
966 UISettings::values.enable_discord_presence, true);
967 WriteSetting(QStringLiteral("screenshot_resolution_factor"),
968 UISettings::values.screenshot_resolution_factor, 0);
969 WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot,
970 false);
971
972 SaveUIGamelistValues();
973 SaveUILayoutValues();
974 SavePathValues();
975 SaveShortcutValues();
976
977 WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true);
978 WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false);
979 WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true);
980 WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true);
981 WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true);
982 WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true);
983 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
984 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
985 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
986 WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
987
988 qt_config->endGroup();
989}
990
991void Config::SaveUIGamelistValues() {
992 qt_config->beginGroup(QStringLiteral("UIGameList"));
993
994 WriteSetting(QStringLiteral("show_unknown"), UISettings::values.show_unknown, true);
995 WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true);
996 WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64);
997 WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3);
998 WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2);
999
1000 qt_config->endGroup();
1001}
1002
1003void Config::SaveUILayoutValues() {
1004 qt_config->beginGroup(QStringLiteral("UILayout"));
1005
1006 WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry);
1007 WriteSetting(QStringLiteral("state"), UISettings::values.state);
1008 WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry);
1009 WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
1010 WriteSetting(QStringLiteral("microProfileDialogGeometry"),
1011 UISettings::values.microprofile_geometry);
1012 WriteSetting(QStringLiteral("microProfileDialogVisible"),
1013 UISettings::values.microprofile_visible, false);
1014
793 qt_config->endGroup(); 1015 qt_config->endGroup();
1016}
1017
1018void Config::SaveWebServiceValues() {
1019 qt_config->beginGroup(QStringLiteral("WebService"));
1020
1021 WriteSetting(QStringLiteral("enable_telemetry"), Settings::values.enable_telemetry, true);
1022 WriteSetting(QStringLiteral("web_api_url"),
1023 QString::fromStdString(Settings::values.web_api_url),
1024 QStringLiteral("https://api.yuzu-emu.org"));
1025 WriteSetting(QStringLiteral("yuzu_username"),
1026 QString::fromStdString(Settings::values.yuzu_username));
1027 WriteSetting(QStringLiteral("yuzu_token"), QString::fromStdString(Settings::values.yuzu_token));
794 1028
795 WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true);
796 WriteSetting("fullscreen", UISettings::values.fullscreen, false);
797 WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true);
798 WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true);
799 WriteSetting("showStatusBar", UISettings::values.show_status_bar, true);
800 WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true);
801 WriteSetting("firstStart", UISettings::values.first_start, true);
802 WriteSetting("calloutFlags", UISettings::values.callout_flags, 0);
803 WriteSetting("showConsole", UISettings::values.show_console, false);
804 WriteSetting("profileIndex", UISettings::values.profile_index, 0);
805 qt_config->endGroup(); 1029 qt_config->endGroup();
806} 1030}
807 1031
@@ -811,7 +1035,7 @@ QVariant Config::ReadSetting(const QString& name) const {
811 1035
812QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { 1036QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
813 QVariant result; 1037 QVariant result;
814 if (qt_config->value(name + "/default", false).toBool()) { 1038 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
815 result = default_value; 1039 result = default_value;
816 } else { 1040 } else {
817 result = qt_config->value(name, default_value); 1041 result = qt_config->value(name, default_value);
@@ -825,7 +1049,7 @@ void Config::WriteSetting(const QString& name, const QVariant& value) {
825 1049
826void Config::WriteSetting(const QString& name, const QVariant& value, 1050void Config::WriteSetting(const QString& name, const QVariant& value,
827 const QVariant& default_value) { 1051 const QVariant& default_value) {
828 qt_config->setValue(name + "/default", value == default_value); 1052 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
829 qt_config->setValue(name, value); 1053 qt_config->setValue(name, value);
830} 1054}
831 1055
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 221d2364c..b62a480ee 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -37,12 +37,46 @@ private:
37 void ReadTouchscreenValues(); 37 void ReadTouchscreenValues();
38 void ApplyDefaultProfileIfInputInvalid(); 38 void ApplyDefaultProfileIfInputInvalid();
39 39
40 // Read functions bases off the respective config section names.
41 void ReadAudioValues();
42 void ReadControlValues();
43 void ReadCoreValues();
44 void ReadDataStorageValues();
45 void ReadDebuggingValues();
46 void ReadDisabledAddOnValues();
47 void ReadMiscellaneousValues();
48 void ReadPathValues();
49 void ReadRendererValues();
50 void ReadShortcutValues();
51 void ReadSystemValues();
52 void ReadUIValues();
53 void ReadUIGamelistValues();
54 void ReadUILayoutValues();
55 void ReadWebServiceValues();
56
40 void SaveValues(); 57 void SaveValues();
41 void SavePlayerValues(); 58 void SavePlayerValues();
42 void SaveDebugValues(); 59 void SaveDebugValues();
43 void SaveMouseValues(); 60 void SaveMouseValues();
44 void SaveTouchscreenValues(); 61 void SaveTouchscreenValues();
45 62
63 // Save functions based off the respective config section names.
64 void SaveAudioValues();
65 void SaveControlValues();
66 void SaveCoreValues();
67 void SaveDataStorageValues();
68 void SaveDebuggingValues();
69 void SaveDisabledAddOnValues();
70 void SaveMiscellaneousValues();
71 void SavePathValues();
72 void SaveRendererValues();
73 void SaveShortcutValues();
74 void SaveSystemValues();
75 void SaveUIValues();
76 void SaveUIGamelistValues();
77 void SaveUILayoutValues();
78 void SaveWebServiceValues();
79
46 QVariant ReadSetting(const QString& name) const; 80 QVariant ReadSetting(const QString& name) const;
47 QVariant ReadSetting(const QString& name, const QVariant& default_value) const; 81 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
48 void WriteSetting(const QString& name, const QVariant& value); 82 void WriteSetting(const QString& name, const QVariant& value);
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 5d9ccc6e8..b0f9b814d 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -16,21 +16,21 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
16 ui->setupUi(this); 16 ui->setupUi(this);
17 17
18 ui->output_sink_combo_box->clear(); 18 ui->output_sink_combo_box->clear();
19 ui->output_sink_combo_box->addItem("auto"); 19 ui->output_sink_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
20 for (const char* id : AudioCore::GetSinkIDs()) { 20 for (const char* id : AudioCore::GetSinkIDs()) {
21 ui->output_sink_combo_box->addItem(id); 21 ui->output_sink_combo_box->addItem(QString::fromUtf8(id));
22 } 22 }
23 23
24 connect(ui->volume_slider, &QSlider::valueChanged, this, 24 connect(ui->volume_slider, &QSlider::valueChanged, this,
25 &ConfigureAudio::setVolumeIndicatorText); 25 &ConfigureAudio::setVolumeIndicatorText);
26 26
27 this->setConfiguration(); 27 this->setConfiguration();
28 connect(ui->output_sink_combo_box, 28 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
29 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
30 &ConfigureAudio::updateAudioDevices); 29 &ConfigureAudio::updateAudioDevices);
31 30
32 ui->output_sink_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 31 const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
33 ui->audio_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 32 ui->output_sink_combo_box->setEnabled(!is_powered_on);
33 ui->audio_device_combo_box->setEnabled(!is_powered_on);
34} 34}
35 35
36ConfigureAudio::~ConfigureAudio() = default; 36ConfigureAudio::~ConfigureAudio() = default;
@@ -94,7 +94,7 @@ void ConfigureAudio::applyConfiguration() {
94 94
95void ConfigureAudio::updateAudioDevices(int sink_index) { 95void ConfigureAudio::updateAudioDevices(int sink_index) {
96 ui->audio_device_combo_box->clear(); 96 ui->audio_device_combo_box->clear();
97 ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); 97 ui->audio_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
98 98
99 const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); 99 const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
100 for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) { 100 for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) {
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
index ae8cac243..6f0d75605 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -100,13 +100,15 @@ void ConfigureGameList::RetranslateUI() {
100 100
101void ConfigureGameList::InitializeIconSizeComboBox() { 101void ConfigureGameList::InitializeIconSizeComboBox() {
102 for (const auto& size : default_icon_sizes) { 102 for (const auto& size : default_icon_sizes) {
103 ui->icon_size_combobox->addItem(size.second, size.first); 103 ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first);
104 } 104 }
105} 105}
106 106
107void ConfigureGameList::InitializeRowComboBoxes() { 107void ConfigureGameList::InitializeRowComboBoxes() {
108 for (std::size_t i = 0; i < row_text_names.size(); ++i) { 108 for (std::size_t i = 0; i < row_text_names.size(); ++i) {
109 ui->row_1_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); 109 const QString row_text_name = QString::fromUtf8(row_text_names[i]);
110 ui->row_2_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); 110
111 ui->row_1_text_combobox->addItem(row_text_name, QVariant::fromValue(i));
112 ui->row_2_text_combobox->addItem(row_text_name, QVariant::fromValue(i));
111 } 113 }
112} 114}
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index e48f4f5a3..dd25dc6e1 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -14,7 +14,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
14 ui->setupUi(this); 14 ui->setupUi(this);
15 15
16 for (const auto& theme : UISettings::themes) { 16 for (const auto& theme : UISettings::themes) {
17 ui->theme_combobox->addItem(theme.first, theme.second); 17 ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
18 QString::fromUtf8(theme.second));
18 } 19 }
19 20
20 this->setConfiguration(); 21 this->setConfiguration();
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index f39d57998..87e459714 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -75,8 +75,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
75 }; 75 };
76 76
77 for (auto* controller_box : players_controller) { 77 for (auto* controller_box : players_controller) {
78 controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon", 78 controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
79 "Single Left Joycon"}); 79 tr("Single Right Joycon"), tr("Single Left Joycon")});
80 } 80 }
81 81
82 this->loadConfiguration(); 82 this->loadConfiguration();
@@ -85,9 +85,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
85 connect(ui->restore_defaults_button, &QPushButton::pressed, this, 85 connect(ui->restore_defaults_button, &QPushButton::pressed, this,
86 &ConfigureInput::restoreDefaults); 86 &ConfigureInput::restoreDefaults);
87 87
88 for (auto* enabled : players_controller) 88 for (auto* enabled : players_controller) {
89 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 89 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
90 &ConfigureInput::updateUIEnabled); 90 &ConfigureInput::updateUIEnabled);
91 }
91 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); 92 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
92 connect(ui->handheld_connected, &QCheckBox::stateChanged, this, 93 connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
93 &ConfigureInput::updateUIEnabled); 94 &ConfigureInput::updateUIEnabled);
@@ -147,10 +148,12 @@ void ConfigureInput::updateUIEnabled() {
147 bool hit_disabled = false; 148 bool hit_disabled = false;
148 for (auto* player : players_controller) { 149 for (auto* player : players_controller) {
149 player->setDisabled(hit_disabled); 150 player->setDisabled(hit_disabled);
150 if (hit_disabled) 151 if (hit_disabled) {
151 player->setCurrentIndex(0); 152 player->setCurrentIndex(0);
152 if (!hit_disabled && player->currentIndex() == 0) 153 }
154 if (!hit_disabled && player->currentIndex() == 0) {
153 hit_disabled = true; 155 hit_disabled = true;
156 }
154 } 157 }
155 158
156 for (std::size_t i = 0; i < players_controller.size(); ++i) { 159 for (std::size_t i = 0; i < players_controller.size(); ++i) {
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index c5a245ebe..95b0a656a 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -45,7 +45,7 @@ static QString GetKeyName(int key_code) {
45 case Qt::Key_Alt: 45 case Qt::Key_Alt:
46 return QObject::tr("Alt"); 46 return QObject::tr("Alt");
47 case Qt::Key_Meta: 47 case Qt::Key_Meta:
48 return ""; 48 return {};
49 default: 49 default:
50 return QKeySequence(key_code).toString(); 50 return QKeySequence(key_code).toString();
51 } 51 }
@@ -65,46 +65,70 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
65static QString ButtonToText(const Common::ParamPackage& param) { 65static QString ButtonToText(const Common::ParamPackage& param) {
66 if (!param.Has("engine")) { 66 if (!param.Has("engine")) {
67 return QObject::tr("[not set]"); 67 return QObject::tr("[not set]");
68 } else if (param.Get("engine", "") == "keyboard") { 68 }
69
70 if (param.Get("engine", "") == "keyboard") {
69 return GetKeyName(param.Get("code", 0)); 71 return GetKeyName(param.Get("code", 0));
70 } else if (param.Get("engine", "") == "sdl") { 72 }
73
74 if (param.Get("engine", "") == "sdl") {
71 if (param.Has("hat")) { 75 if (param.Has("hat")) {
72 return QString(QObject::tr("Hat %1 %2")) 76 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
73 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); 77 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
78
79 return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
74 } 80 }
81
75 if (param.Has("axis")) { 82 if (param.Has("axis")) {
76 return QString(QObject::tr("Axis %1%2")) 83 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
77 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); 84 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
85
86 return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
78 } 87 }
88
79 if (param.Has("button")) { 89 if (param.Has("button")) {
80 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); 90 const QString button_str = QString::fromStdString(param.Get("button", ""));
91
92 return QObject::tr("Button %1").arg(button_str);
81 } 93 }
82 return QString(); 94
83 } else { 95 return {};
84 return QObject::tr("[unknown]");
85 } 96 }
86}; 97
98 return QObject::tr("[unknown]");
99}
87 100
88static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { 101static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
89 if (!param.Has("engine")) { 102 if (!param.Has("engine")) {
90 return QObject::tr("[not set]"); 103 return QObject::tr("[not set]");
91 } else if (param.Get("engine", "") == "analog_from_button") { 104 }
105
106 if (param.Get("engine", "") == "analog_from_button") {
92 return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); 107 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
93 } else if (param.Get("engine", "") == "sdl") { 108 }
109
110 if (param.Get("engine", "") == "sdl") {
94 if (dir == "modifier") { 111 if (dir == "modifier") {
95 return QString(QObject::tr("[unused]")); 112 return QObject::tr("[unused]");
96 } 113 }
97 114
98 if (dir == "left" || dir == "right") { 115 if (dir == "left" || dir == "right") {
99 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); 116 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
100 } else if (dir == "up" || dir == "down") { 117
101 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str()); 118 return QObject::tr("Axis %1").arg(axis_x_str);
102 } 119 }
103 return QString(); 120
104 } else { 121 if (dir == "up" || dir == "down") {
105 return QObject::tr("[unknown]"); 122 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
123
124 return QObject::tr("Axis %1").arg(axis_y_str);
125 }
126
127 return {};
106 } 128 }
107}; 129
130 return QObject::tr("[unknown]");
131}
108 132
109ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) 133ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug)
110 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 134 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
@@ -214,38 +238,42 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
214 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; 238 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
215 239
216 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 240 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
217 if (!button_map[button_id]) 241 auto* const button = button_map[button_id];
242 if (button == nullptr) {
218 continue; 243 continue;
219 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); 244 }
220 connect(button_map[button_id], &QPushButton::released, [=]() { 245
246 button->setContextMenuPolicy(Qt::CustomContextMenu);
247 connect(button, &QPushButton::released, [=] {
221 handleClick( 248 handleClick(
222 button_map[button_id], 249 button_map[button_id],
223 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, 250 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
224 InputCommon::Polling::DeviceType::Button); 251 InputCommon::Polling::DeviceType::Button);
225 }); 252 });
226 connect(button_map[button_id], &QPushButton::customContextMenuRequested, 253 connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
227 [=](const QPoint& menu_location) { 254 QMenu context_menu;
228 QMenu context_menu; 255 context_menu.addAction(tr("Clear"), [&] {
229 context_menu.addAction(tr("Clear"), [&] { 256 buttons_param[button_id].Clear();
230 buttons_param[button_id].Clear(); 257 button_map[button_id]->setText(tr("[not set]"));
231 button_map[button_id]->setText(tr("[not set]")); 258 });
232 }); 259 context_menu.addAction(tr("Restore Default"), [&] {
233 context_menu.addAction(tr("Restore Default"), [&] { 260 buttons_param[button_id] = Common::ParamPackage{
234 buttons_param[button_id] = Common::ParamPackage{ 261 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
235 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 262 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
236 button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); 263 });
237 }); 264 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
238 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); 265 });
239 });
240 } 266 }
241 267
242 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 268 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
243 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 269 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
244 if (!analog_map_buttons[analog_id][sub_button_id]) 270 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
271 if (analog_button == nullptr) {
245 continue; 272 continue;
246 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy( 273 }
247 Qt::CustomContextMenu); 274
248 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() { 275 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
276 connect(analog_button, &QPushButton::released, [=]() {
249 handleClick(analog_map_buttons[analog_id][sub_button_id], 277 handleClick(analog_map_buttons[analog_id][sub_button_id],
250 [=](const Common::ParamPackage& params) { 278 [=](const Common::ParamPackage& params) {
251 SetAnalogButton(params, analogs_param[analog_id], 279 SetAnalogButton(params, analogs_param[analog_id],
@@ -253,8 +281,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
253 }, 281 },
254 InputCommon::Polling::DeviceType::Button); 282 InputCommon::Polling::DeviceType::Button);
255 }); 283 });
256 connect(analog_map_buttons[analog_id][sub_button_id], 284 connect(analog_button, &QPushButton::customContextMenuRequested,
257 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { 285 [=](const QPoint& menu_location) {
258 QMenu context_menu; 286 QMenu context_menu;
259 context_menu.addAction(tr("Clear"), [&] { 287 context_menu.addAction(tr("Clear"), [&] {
260 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); 288 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
@@ -272,7 +300,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
272 menu_location)); 300 menu_location));
273 }); 301 });
274 } 302 }
275 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { 303 connect(analog_map_stick[analog_id], &QPushButton::released, [=] {
276 QMessageBox::information(this, tr("Information"), 304 QMessageBox::information(this, tr("Information"),
277 tr("After pressing OK, first move your joystick horizontally, " 305 tr("After pressing OK, first move your joystick horizontally, "
278 "and then vertically.")); 306 "and then vertically."));
@@ -351,7 +379,7 @@ void ConfigureInputPlayer::OnControllerButtonClick(int i) {
351 return; 379 return;
352 controller_colors[i] = new_bg_color; 380 controller_colors[i] = new_bg_color;
353 controller_color_buttons[i]->setStyleSheet( 381 controller_color_buttons[i]->setStyleSheet(
354 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); 382 QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
355} 383}
356 384
357void ConfigureInputPlayer::loadConfiguration() { 385void ConfigureInputPlayer::loadConfiguration() {
@@ -388,7 +416,8 @@ void ConfigureInputPlayer::loadConfiguration() {
388 416
389 for (std::size_t i = 0; i < colors.size(); ++i) { 417 for (std::size_t i = 0; i < colors.size(); ++i) {
390 controller_color_buttons[i]->setStyleSheet( 418 controller_color_buttons[i]->setStyleSheet(
391 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); 419 QStringLiteral("QPushButton { background-color: %1 }")
420 .arg(controller_colors[i].name()));
392 } 421 }
393} 422}
394 423
@@ -410,14 +439,22 @@ void ConfigureInputPlayer::restoreDefaults() {
410 439
411void ConfigureInputPlayer::ClearAll() { 440void ConfigureInputPlayer::ClearAll() {
412 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 441 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
413 if (button_map[button_id] && button_map[button_id]->isEnabled()) 442 const auto* const button = button_map[button_id];
414 buttons_param[button_id].Clear(); 443 if (button == nullptr || !button->isEnabled()) {
444 continue;
445 }
446
447 buttons_param[button_id].Clear();
415 } 448 }
449
416 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 450 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
417 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 451 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
418 if (analog_map_buttons[analog_id][sub_button_id] && 452 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
419 analog_map_buttons[analog_id][sub_button_id]->isEnabled()) 453 if (analog_button == nullptr || !analog_button->isEnabled()) {
420 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); 454 continue;
455 }
456
457 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
421 } 458 }
422 } 459 }
423 460
@@ -431,10 +468,14 @@ void ConfigureInputPlayer::updateButtonLabels() {
431 468
432 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 469 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
433 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 470 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
434 if (analog_map_buttons[analog_id][sub_button_id]) { 471 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
435 analog_map_buttons[analog_id][sub_button_id]->setText( 472
436 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 473 if (analog_button == nullptr) {
474 continue;
437 } 475 }
476
477 analog_button->setText(
478 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
438 } 479 }
439 analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); 480 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
440 } 481 }
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index ef857035e..a14bb1475 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -25,7 +25,7 @@ static QString GetKeyName(int key_code) {
25 case Qt::Key_Alt: 25 case Qt::Key_Alt:
26 return QObject::tr("Alt"); 26 return QObject::tr("Alt");
27 case Qt::Key_Meta: 27 case Qt::Key_Meta:
28 return ""; 28 return {};
29 default: 29 default:
30 return QKeySequence(key_code).toString(); 30 return QKeySequence(key_code).toString();
31 } 31 }
@@ -34,24 +34,36 @@ static QString GetKeyName(int key_code) {
34static QString ButtonToText(const Common::ParamPackage& param) { 34static QString ButtonToText(const Common::ParamPackage& param) {
35 if (!param.Has("engine")) { 35 if (!param.Has("engine")) {
36 return QObject::tr("[not set]"); 36 return QObject::tr("[not set]");
37 } else if (param.Get("engine", "") == "keyboard") { 37 }
38
39 if (param.Get("engine", "") == "keyboard") {
38 return GetKeyName(param.Get("code", 0)); 40 return GetKeyName(param.Get("code", 0));
39 } else if (param.Get("engine", "") == "sdl") { 41 }
42
43 if (param.Get("engine", "") == "sdl") {
40 if (param.Has("hat")) { 44 if (param.Has("hat")) {
41 return QString(QObject::tr("Hat %1 %2")) 45 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
42 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); 46 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
47
48 return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
43 } 49 }
50
44 if (param.Has("axis")) { 51 if (param.Has("axis")) {
45 return QString(QObject::tr("Axis %1%2")) 52 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
46 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); 53 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
54
55 return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
47 } 56 }
57
48 if (param.Has("button")) { 58 if (param.Has("button")) {
49 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); 59 const QString button_str = QString::fromStdString(param.Get("button", ""));
60
61 return QObject::tr("Button %1").arg(button_str);
50 } 62 }
51 return QString(); 63 return {};
52 } else {
53 return QObject::tr("[unknown]");
54 } 64 }
65
66 return QObject::tr("[unknown]");
55} 67}
56 68
57ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) 69ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
@@ -65,30 +77,31 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
65 }; 77 };
66 78
67 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { 79 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
68 if (!button_map[button_id]) 80 auto* const button = button_map[button_id];
81 if (button == nullptr) {
69 continue; 82 continue;
70 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); 83 }
71 connect(button_map[button_id], &QPushButton::released, [=]() { 84
85 button->setContextMenuPolicy(Qt::CustomContextMenu);
86 connect(button, &QPushButton::released, [=] {
72 handleClick( 87 handleClick(
73 button_map[button_id], 88 button_map[button_id],
74 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, 89 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
75 InputCommon::Polling::DeviceType::Button); 90 InputCommon::Polling::DeviceType::Button);
76 }); 91 });
77 connect(button_map[button_id], &QPushButton::customContextMenuRequested, 92 connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
78 [=](const QPoint& menu_location) { 93 QMenu context_menu;
79 QMenu context_menu; 94 context_menu.addAction(tr("Clear"), [&] {
80 context_menu.addAction(tr("Clear"), [&] { 95 buttons_param[button_id].Clear();
81 buttons_param[button_id].Clear(); 96 button_map[button_id]->setText(tr("[not set]"));
82 button_map[button_id]->setText(tr("[not set]")); 97 });
83 }); 98 context_menu.addAction(tr("Restore Default"), [&] {
84 context_menu.addAction(tr("Restore Default"), [&] { 99 buttons_param[button_id] = Common::ParamPackage{
85 buttons_param[button_id] = 100 InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
86 Common::ParamPackage{InputCommon::GenerateKeyboardParam( 101 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
87 Config::default_mouse_buttons[button_id])}; 102 });
88 button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); 103 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
89 }); 104 });
90 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
91 });
92 } 105 }
93 106
94 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); 107 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
@@ -138,8 +151,10 @@ void ConfigureMouseAdvanced::restoreDefaults() {
138 151
139void ConfigureMouseAdvanced::ClearAll() { 152void ConfigureMouseAdvanced::ClearAll() {
140 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 153 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
141 if (button_map[i] && button_map[i]->isEnabled()) 154 const auto* const button = button_map[i];
155 if (button != nullptr && button->isEnabled()) {
142 buttons_param[i].Clear(); 156 buttons_param[i].Clear();
157 }
143 } 158 }
144 159
145 updateButtonLabels(); 160 updateButtonLabels();
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp
index 022b94609..2bdfc8e5a 100644
--- a/src/yuzu/configuration/configure_per_general.cpp
+++ b/src/yuzu/configuration/configure_per_general.cpp
@@ -88,15 +88,15 @@ void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) {
88} 88}
89 89
90void ConfigurePerGameGeneral::loadConfiguration() { 90void ConfigurePerGameGeneral::loadConfiguration() {
91 if (file == nullptr) 91 if (file == nullptr) {
92 return; 92 return;
93 }
93 94
94 const auto loader = Loader::GetLoader(file); 95 ui->display_title_id->setText(QString::fromStdString(fmt::format("{:016X}", title_id)));
95
96 ui->display_title_id->setText(fmt::format("{:016X}", title_id).c_str());
97 96
98 FileSys::PatchManager pm{title_id}; 97 FileSys::PatchManager pm{title_id};
99 const auto control = pm.GetControlMetadata(); 98 const auto control = pm.GetControlMetadata();
99 const auto loader = Loader::GetLoader(file);
100 100
101 if (control.first != nullptr) { 101 if (control.first != nullptr) {
102 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); 102 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
@@ -142,8 +142,10 @@ void ConfigurePerGameGeneral::loadConfiguration() {
142 const auto& disabled = Settings::values.disabled_addons[title_id]; 142 const auto& disabled = Settings::values.disabled_addons[title_id];
143 143
144 for (const auto& patch : pm.GetPatchVersionNames(update_raw)) { 144 for (const auto& patch : pm.GetPatchVersionNames(update_raw)) {
145 QStandardItem* first_item = new QStandardItem; 145 const auto name =
146 const auto name = QString::fromStdString(patch.first).replace("[D] ", ""); 146 QString::fromStdString(patch.first).replace(QStringLiteral("[D] "), QString{});
147
148 auto* const first_item = new QStandardItem;
147 first_item->setText(name); 149 first_item->setText(name);
148 first_item->setCheckable(true); 150 first_item->setCheckable(true);
149 151
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index 41663e39a..002a51780 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -98,7 +98,7 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent)
98 tree_view->setContextMenuPolicy(Qt::NoContextMenu); 98 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
99 99
100 item_model->insertColumns(0, 1); 100 item_model->insertColumns(0, 1);
101 item_model->setHeaderData(0, Qt::Horizontal, "Users"); 101 item_model->setHeaderData(0, Qt::Horizontal, tr("Users"));
102 102
103 // We must register all custom types with the Qt Automoc system so that we are able to use it 103 // We must register all custom types with the Qt Automoc system so that we are able to use it
104 // with signals/slots. In this case, QList falls under the umbrells of custom types. 104 // with signals/slots. In this case, QList falls under the umbrells of custom types.
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 10645a2b3..ff18ace40 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -66,8 +66,9 @@ void ConfigureSystem::setConfiguration() {
66 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); 66 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
67 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); 67 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value());
68 68
69 const auto rng_seed = 69 const auto rng_seed = QStringLiteral("%1")
70 QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper(); 70 .arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'})
71 .toUpper();
71 ui->rng_seed_edit->setText(rng_seed); 72 ui->rng_seed_edit->setText(rng_seed);
72 73
73 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); 74 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value());
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
index 18566d028..9dc34412d 100644
--- a/src/yuzu/configuration/configure_web.cpp
+++ b/src/yuzu/configuration/configure_web.cpp
@@ -78,12 +78,16 @@ void ConfigureWeb::RefreshTelemetryID() {
78void ConfigureWeb::OnLoginChanged() { 78void ConfigureWeb::OnLoginChanged() {
79 if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) { 79 if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) {
80 user_verified = true; 80 user_verified = true;
81 ui->label_username_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); 81
82 ui->label_token_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); 82 const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
83 ui->label_username_verified->setPixmap(pixmap);
84 ui->label_token_verified->setPixmap(pixmap);
83 } else { 85 } else {
84 user_verified = false; 86 user_verified = false;
85 ui->label_username_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); 87
86 ui->label_token_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); 88 const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
89 ui->label_username_verified->setPixmap(pixmap);
90 ui->label_token_verified->setPixmap(pixmap);
87 } 91 }
88} 92}
89 93
@@ -101,11 +105,15 @@ void ConfigureWeb::OnLoginVerified() {
101 ui->button_verify_login->setText(tr("Verify")); 105 ui->button_verify_login->setText(tr("Verify"));
102 if (verify_watcher.result()) { 106 if (verify_watcher.result()) {
103 user_verified = true; 107 user_verified = true;
104 ui->label_username_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); 108
105 ui->label_token_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); 109 const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
110 ui->label_username_verified->setPixmap(pixmap);
111 ui->label_token_verified->setPixmap(pixmap);
106 } else { 112 } else {
107 ui->label_username_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); 113 const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
108 ui->label_token_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); 114 ui->label_username_verified->setPixmap(pixmap);
115 ui->label_token_verified->setPixmap(pixmap);
116
109 QMessageBox::critical( 117 QMessageBox::critical(
110 this, tr("Verification failed"), 118 this, tr("Verification failed"),
111 tr("Verification failed. Check that you have entered your username and token " 119 tr("Verification failed. Check that you have entered your username and token "
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
index 67ed0ba6d..1c80082a4 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
@@ -135,7 +135,7 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
135 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) 135 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
136 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( 136 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
137 debug_context) { 137 debug_context) {
138 setObjectName("TegraBreakPointsWidget"); 138 setObjectName(QStringLiteral("TegraBreakPointsWidget"));
139 139
140 status_text = new QLabel(tr("Emulation running")); 140 status_text = new QLabel(tr("Emulation running"));
141 resume_button = new QPushButton(tr("Resume")); 141 resume_button = new QPushButton(tr("Resume"));
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index 86e03e46d..f594ef076 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -47,7 +47,7 @@ private:
47#endif 47#endif
48 48
49MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) { 49MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
50 setObjectName("MicroProfile"); 50 setObjectName(QStringLiteral("MicroProfile"));
51 setWindowTitle(tr("MicroProfile")); 51 setWindowTitle(tr("MicroProfile"));
52 resize(1000, 600); 52 resize(1000, 600);
53 // Remove the "?" button from the titlebar and enable the maximize button 53 // Remove the "?" button from the titlebar and enable the maximize button
@@ -191,7 +191,7 @@ void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 tex
191 for (u32 i = 0; i < text_length; ++i) { 191 for (u32 i = 0; i < text_length; ++i) {
192 // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice 192 // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice
193 // vertical alignment of text for a wide range of tested fonts. 193 // vertical alignment of text for a wide range of tested fonts.
194 mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QChar(text[i])); 194 mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QString{QLatin1Char{text[i]}});
195 x += MICROPROFILE_TEXT_WIDTH + 1; 195 x += MICROPROFILE_TEXT_WIDTH + 1;
196 } 196 }
197} 197}
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 85b095688..cd8180f8b 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -91,19 +91,19 @@ WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTa
91WaitTreeMutexInfo::~WaitTreeMutexInfo() = default; 91WaitTreeMutexInfo::~WaitTreeMutexInfo() = default;
92 92
93QString WaitTreeMutexInfo::GetText() const { 93QString WaitTreeMutexInfo::GetText() const {
94 return tr("waiting for mutex 0x%1").arg(mutex_address, 16, 16, QLatin1Char('0')); 94 return tr("waiting for mutex 0x%1").arg(mutex_address, 16, 16, QLatin1Char{'0'});
95} 95}
96 96
97std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { 97std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const {
98 std::vector<std::unique_ptr<WaitTreeItem>> list; 98 const bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0;
99
100 bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0;
101 99
100 std::vector<std::unique_ptr<WaitTreeItem>> list;
102 list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); 101 list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters)));
103 list.push_back(std::make_unique<WaitTreeText>( 102 list.push_back(std::make_unique<WaitTreeText>(
104 tr("owner handle: 0x%1").arg(owner_handle, 8, 16, QLatin1Char('0')))); 103 tr("owner handle: 0x%1").arg(owner_handle, 8, 16, QLatin1Char{'0'})));
105 if (owner != nullptr) 104 if (owner != nullptr) {
106 list.push_back(std::make_unique<WaitTreeThread>(*owner)); 105 list.push_back(std::make_unique<WaitTreeThread>(*owner));
106 }
107 return list; 107 return list;
108} 108}
109 109
@@ -121,11 +121,14 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
121 u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; 121 u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister];
122 122
123 while (base_pointer != 0) { 123 while (base_pointer != 0) {
124 u64 lr = Memory::Read64(base_pointer + sizeof(u64)); 124 const u64 lr = Memory::Read64(base_pointer + sizeof(u64));
125 if (lr == 0) 125 if (lr == 0) {
126 break; 126 break;
127 list.push_back( 127 }
128 std::make_unique<WaitTreeText>(tr("0x%1").arg(lr - sizeof(u32), 16, 16, QChar('0')))); 128
129 list.push_back(std::make_unique<WaitTreeText>(
130 tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'})));
131
129 base_pointer = Memory::Read64(base_pointer); 132 base_pointer = Memory::Read64(base_pointer);
130 } 133 }
131 134
@@ -174,10 +177,10 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() con
174 177
175QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) { 178QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) {
176 switch (reset_type) { 179 switch (reset_type) {
177 case Kernel::ResetType::OneShot: 180 case Kernel::ResetType::Automatic:
178 return tr("one shot"); 181 return tr("automatic reset");
179 case Kernel::ResetType::Sticky: 182 case Kernel::ResetType::Manual:
180 return tr("sticky"); 183 return tr("manual reset");
181 } 184 }
182 UNREACHABLE(); 185 UNREACHABLE();
183 return {}; 186 return {};
@@ -249,9 +252,9 @@ QString WaitTreeThread::GetText() const {
249 252
250 const auto& context = thread.GetContext(); 253 const auto& context = thread.GetContext();
251 const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") 254 const QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
252 .arg(context.pc, 8, 16, QLatin1Char('0')) 255 .arg(context.pc, 8, 16, QLatin1Char{'0'})
253 .arg(context.cpu_registers[30], 8, 16, QLatin1Char('0')); 256 .arg(context.cpu_registers[30], 8, 16, QLatin1Char{'0'});
254 return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") "; 257 return QStringLiteral("%1%2 (%3) ").arg(WaitTreeWaitObject::GetText(), pc_info, status);
255} 258}
256 259
257QColor WaitTreeThread::GetColor() const { 260QColor WaitTreeThread::GetColor() const {
@@ -424,7 +427,7 @@ void WaitTreeModel::InitItems() {
424} 427}
425 428
426WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) { 429WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) {
427 setObjectName("WaitTreeWidget"); 430 setObjectName(QStringLiteral("WaitTreeWidget"));
428 view = new QTreeView(this); 431 view = new QTreeView(this);
429 view->setHeaderHidden(true); 432 view->setHeaderHidden(true);
430 setWidget(view); 433 setWidget(view);
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index b0ca766ec..83d675773 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -14,7 +14,6 @@
14#include <QMenu> 14#include <QMenu>
15#include <QThreadPool> 15#include <QThreadPool>
16#include <fmt/format.h> 16#include <fmt/format.h>
17#include "common/common_paths.h"
18#include "common/common_types.h" 17#include "common/common_types.h"
19#include "common/logging/log.h" 18#include "common/logging/log.h"
20#include "core/file_sys/patch_manager.h" 19#include "core/file_sys/patch_manager.h"
@@ -48,7 +47,7 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
48 return QObject::eventFilter(obj, event); 47 return QObject::eventFilter(obj, event);
49 } else { 48 } else {
50 gamelist->search_field->edit_filter->clear(); 49 gamelist->search_field->edit_filter->clear();
51 edit_filter_text = ""; 50 edit_filter_text.clear();
52 } 51 }
53 break; 52 break;
54 } 53 }
@@ -71,9 +70,9 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
71 } 70 }
72 if (resultCount == 1) { 71 if (resultCount == 1) {
73 // To avoid loading error dialog loops while confirming them using enter 72 // To avoid loading error dialog loops while confirming them using enter
74 // Also users usually want to run a diffrent game after closing one 73 // Also users usually want to run a different game after closing one
75 gamelist->search_field->edit_filter->setText(""); 74 gamelist->search_field->edit_filter->clear();
76 edit_filter_text = ""; 75 edit_filter_text.clear();
77 emit gamelist->GameChosen(file_path); 76 emit gamelist->GameChosen(file_path);
78 } else { 77 } else {
79 return QObject::eventFilter(obj, event); 78 return QObject::eventFilter(obj, event);
@@ -93,7 +92,7 @@ void GameListSearchField::setFilterResult(int visible, int total) {
93} 92}
94 93
95void GameListSearchField::clear() { 94void GameListSearchField::clear() {
96 edit_filter->setText(""); 95 edit_filter->clear();
97} 96}
98 97
99void GameListSearchField::setFocus() { 98void GameListSearchField::setFocus() {
@@ -103,25 +102,26 @@ void GameListSearchField::setFocus() {
103} 102}
104 103
105GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { 104GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
106 KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent); 105 auto* const key_release_eater = new KeyReleaseEater(parent);
107 layout_filter = new QHBoxLayout; 106 layout_filter = new QHBoxLayout;
108 layout_filter->setMargin(8); 107 layout_filter->setMargin(8);
109 label_filter = new QLabel; 108 label_filter = new QLabel;
110 label_filter->setText(tr("Filter:")); 109 label_filter->setText(tr("Filter:"));
111 edit_filter = new QLineEdit; 110 edit_filter = new QLineEdit;
112 edit_filter->setText(""); 111 edit_filter->clear();
113 edit_filter->setPlaceholderText(tr("Enter pattern to filter")); 112 edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
114 edit_filter->installEventFilter(keyReleaseEater); 113 edit_filter->installEventFilter(key_release_eater);
115 edit_filter->setClearButtonEnabled(true); 114 edit_filter->setClearButtonEnabled(true);
116 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged); 115 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged);
117 label_filter_result = new QLabel; 116 label_filter_result = new QLabel;
118 button_filter_close = new QToolButton(this); 117 button_filter_close = new QToolButton(this);
119 button_filter_close->setText("X"); 118 button_filter_close->setText(QStringLiteral("X"));
120 button_filter_close->setCursor(Qt::ArrowCursor); 119 button_filter_close->setCursor(Qt::ArrowCursor);
121 button_filter_close->setStyleSheet("QToolButton{ border: none; padding: 0px; color: " 120 button_filter_close->setStyleSheet(
122 "#000000; font-weight: bold; background: #F0F0F0; }" 121 QStringLiteral("QToolButton{ border: none; padding: 0px; color: "
123 "QToolButton:hover{ border: none; padding: 0px; color: " 122 "#000000; font-weight: bold; background: #F0F0F0; }"
124 "#EEEEEE; font-weight: bold; background: #E81123}"); 123 "QToolButton:hover{ border: none; padding: 0px; color: "
124 "#EEEEEE; font-weight: bold; background: #E81123}"));
125 connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked); 125 connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked);
126 layout_filter->setSpacing(10); 126 layout_filter->setSpacing(10);
127 layout_filter->addWidget(label_filter); 127 layout_filter->addWidget(label_filter);
@@ -141,36 +141,34 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
141 */ 141 */
142static bool ContainsAllWords(const QString& haystack, const QString& userinput) { 142static bool ContainsAllWords(const QString& haystack, const QString& userinput) {
143 const QStringList userinput_split = 143 const QStringList userinput_split =
144 userinput.split(' ', QString::SplitBehavior::SkipEmptyParts); 144 userinput.split(QLatin1Char{' '}, QString::SplitBehavior::SkipEmptyParts);
145 145
146 return std::all_of(userinput_split.begin(), userinput_split.end(), 146 return std::all_of(userinput_split.begin(), userinput_split.end(),
147 [&haystack](const QString& s) { return haystack.contains(s); }); 147 [&haystack](const QString& s) { return haystack.contains(s); });
148} 148}
149 149
150// Event in order to filter the gamelist after editing the searchfield 150// Event in order to filter the gamelist after editing the searchfield
151void GameList::onTextChanged(const QString& newText) { 151void GameList::onTextChanged(const QString& new_text) {
152 int rowCount = tree_view->model()->rowCount(); 152 const int row_count = tree_view->model()->rowCount();
153 QString edit_filter_text = newText.toLower(); 153 const QString edit_filter_text = new_text.toLower();
154 154 const QModelIndex root_index = item_model->invisibleRootItem()->index();
155 QModelIndex root_index = item_model->invisibleRootItem()->index();
156 155
157 // If the searchfield is empty every item is visible 156 // If the searchfield is empty every item is visible
158 // Otherwise the filter gets applied 157 // Otherwise the filter gets applied
159 if (edit_filter_text.isEmpty()) { 158 if (edit_filter_text.isEmpty()) {
160 for (int i = 0; i < rowCount; ++i) { 159 for (int i = 0; i < row_count; ++i) {
161 tree_view->setRowHidden(i, root_index, false); 160 tree_view->setRowHidden(i, root_index, false);
162 } 161 }
163 search_field->setFilterResult(rowCount, rowCount); 162 search_field->setFilterResult(row_count, row_count);
164 } else { 163 } else {
165 int result_count = 0; 164 int result_count = 0;
166 for (int i = 0; i < rowCount; ++i) { 165 for (int i = 0; i < row_count; ++i) {
167 const QStandardItem* child_file = item_model->item(i, 0); 166 const QStandardItem* child_file = item_model->item(i, 0);
168 const QString file_path = 167 const QString file_path =
169 child_file->data(GameListItemPath::FullPathRole).toString().toLower(); 168 child_file->data(GameListItemPath::FullPathRole).toString().toLower();
170 QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
171 const QString file_title = 169 const QString file_title =
172 child_file->data(GameListItemPath::TitleRole).toString().toLower(); 170 child_file->data(GameListItemPath::TitleRole).toString().toLower();
173 const QString file_programmid = 171 const QString file_program_id =
174 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); 172 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
175 173
176 // Only items which filename in combination with its title contains all words 174 // Only items which filename in combination with its title contains all words
@@ -178,14 +176,16 @@ void GameList::onTextChanged(const QString& newText) {
178 // The search is case insensitive because of toLower() 176 // The search is case insensitive because of toLower()
179 // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent 177 // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent
180 // multiple conversions of edit_filter_text for each game in the gamelist 178 // multiple conversions of edit_filter_text for each game in the gamelist
181 if (ContainsAllWords(file_name.append(' ').append(file_title), edit_filter_text) || 179 const QString file_name = file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) +
182 (file_programmid.count() == 16 && edit_filter_text.contains(file_programmid))) { 180 QLatin1Char{' '} + file_title;
181 if (ContainsAllWords(file_name, edit_filter_text) ||
182 (file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) {
183 tree_view->setRowHidden(i, root_index, false); 183 tree_view->setRowHidden(i, root_index, false);
184 ++result_count; 184 ++result_count;
185 } else { 185 } else {
186 tree_view->setRowHidden(i, root_index, true); 186 tree_view->setRowHidden(i, root_index, true);
187 } 187 }
188 search_field->setFilterResult(result_count, rowCount); 188 search_field->setFilterResult(result_count, row_count);
189 } 189 }
190 } 190 }
191} 191}
@@ -216,7 +216,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
216 tree_view->setEditTriggers(QHeaderView::NoEditTriggers); 216 tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
217 tree_view->setUniformRowHeights(true); 217 tree_view->setUniformRowHeights(true);
218 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 218 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
219 tree_view->setStyleSheet("QTreeView{ border: none; }"); 219 tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }"));
220 220
221 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1); 221 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
222 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); 222 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
@@ -282,9 +282,9 @@ void GameList::ValidateEntry(const QModelIndex& item) {
282 const QFileInfo file_info{file_path}; 282 const QFileInfo file_info{file_path};
283 if (file_info.isDir()) { 283 if (file_info.isDir()) {
284 const QDir dir{file_path}; 284 const QDir dir{file_path};
285 const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); 285 const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files);
286 if (matching_main.size() == 1) { 286 if (matching_main.size() == 1) {
287 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); 287 emit GameChosen(dir.path() + QDir::separator() + matching_main[0]);
288 } 288 }
289 return; 289 return;
290 } 290 }
@@ -360,7 +360,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
360} 360}
361 361
362void GameList::LoadCompatibilityList() { 362void GameList::LoadCompatibilityList() {
363 QFile compat_list{":compatibility_list/compatibility_list.json"}; 363 QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")};
364 364
365 if (!compat_list.open(QFile::ReadOnly | QFile::Text)) { 365 if (!compat_list.open(QFile::ReadOnly | QFile::Text)) {
366 LOG_ERROR(Frontend, "Unable to open game compatibility list"); 366 LOG_ERROR(Frontend, "Unable to open game compatibility list");
@@ -378,25 +378,27 @@ void GameList::LoadCompatibilityList() {
378 return; 378 return;
379 } 379 }
380 380
381 const QString string_content = content; 381 const QJsonDocument json = QJsonDocument::fromJson(content);
382 QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); 382 const QJsonArray arr = json.array();
383 QJsonArray arr = json.array();
384 383
385 for (const QJsonValueRef value : arr) { 384 for (const QJsonValue value : arr) {
386 QJsonObject game = value.toObject(); 385 const QJsonObject game = value.toObject();
386 const QString compatibility_key = QStringLiteral("compatibility");
387 387
388 if (game.contains("compatibility") && game["compatibility"].isDouble()) { 388 if (!game.contains(compatibility_key) || !game[compatibility_key].isDouble()) {
389 int compatibility = game["compatibility"].toInt(); 389 continue;
390 QString directory = game["directory"].toString(); 390 }
391 QJsonArray ids = game["releases"].toArray();
392 391
393 for (const QJsonValueRef id_ref : ids) { 392 const int compatibility = game[compatibility_key].toInt();
394 QJsonObject id_object = id_ref.toObject(); 393 const QString directory = game[QStringLiteral("directory")].toString();
395 QString id = id_object["id"].toString(); 394 const QJsonArray ids = game[QStringLiteral("releases")].toArray();
396 compatibility_list.emplace( 395
397 id.toUpper().toStdString(), 396 for (const QJsonValue id_ref : ids) {
398 std::make_pair(QString::number(compatibility), directory)); 397 const QJsonObject id_object = id_ref.toObject();
399 } 398 const QString id = id_object[QStringLiteral("id")].toString();
399
400 compatibility_list.emplace(id.toUpper().toStdString(),
401 std::make_pair(QString::number(compatibility), directory));
400 } 402 }
401 } 403 }
402} 404}
@@ -464,7 +466,10 @@ void GameList::LoadInterfaceLayout() {
464 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); 466 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
465} 467}
466 468
467const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; 469const QStringList GameList::supported_file_extensions = {
470 QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
471 QStringLiteral("xci"), QStringLiteral("nsp"),
472};
468 473
469void GameList::RefreshGameDirectory() { 474void GameList::RefreshGameDirectory() {
470 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { 475 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 56007eef8..f8f8bd6c5 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -76,7 +76,7 @@ signals:
76 void OpenPerGameGeneralRequested(const std::string& file); 76 void OpenPerGameGeneralRequested(const std::string& file);
77 77
78private slots: 78private slots:
79 void onTextChanged(const QString& newText); 79 void onTextChanged(const QString& new_text);
80 void onFilterCloseClicked(); 80 void onFilterCloseClicked();
81 81
82private: 82private:
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 2cf5c58a0..0b458ef48 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <array> 7#include <array>
9#include <map> 8#include <map>
10#include <string> 9#include <string>
11#include <unordered_map>
12#include <utility> 10#include <utility>
13 11
14#include <QCoreApplication> 12#include <QCoreApplication>
@@ -25,8 +23,8 @@
25#include "yuzu/util/util.h" 23#include "yuzu/util/util.h"
26 24
27/** 25/**
28 * Gets the default icon (for games without valid SMDH) 26 * Gets the default icon (for games without valid title metadata)
29 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) 27 * @param size The desired width and height of the default icon.
30 * @return QPixmap default icon 28 * @return QPixmap default icon
31 */ 29 */
32static QPixmap GetDefaultIcon(u32 size) { 30static QPixmap GetDefaultIcon(u32 size) {
@@ -46,7 +44,7 @@ public:
46 * A specialization of GameListItem for path values. 44 * A specialization of GameListItem for path values.
47 * This class ensures that for every full path value it holds, a correct string representation 45 * This class ensures that for every full path value it holds, a correct string representation
48 * of just the filename (with no extension) will be displayed to the user. 46 * of just the filename (with no extension) will be displayed to the user.
49 * If this class receives valid SMDH data, it will also display game icons and titles. 47 * If this class receives valid title metadata, it will also display game icons and titles.
50 */ 48 */
51class GameListItemPath : public GameListItem { 49class GameListItemPath : public GameListItem {
52public: 50public:
@@ -95,7 +93,7 @@ public:
95 if (row2.isEmpty()) 93 if (row2.isEmpty())
96 return row1; 94 return row1;
97 95
98 return QString(row1 + "\n " + row2); 96 return QString(row1 + QStringLiteral("\n ") + row2);
99 } 97 }
100 98
101 return GameListItem::data(role); 99 return GameListItem::data(role);
@@ -115,13 +113,14 @@ public:
115 }; 113 };
116 // clang-format off 114 // clang-format off
117 static const std::map<QString, CompatStatus> status_data = { 115 static const std::map<QString, CompatStatus> status_data = {
118 {"0", {"#5c93ed", QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, 116 {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}},
119 {"1", {"#47d35c", QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, 117 {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}},
120 {"2", {"#94b242", QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, 118 {QStringLiteral("2"), {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}},
121 {"3", {"#f2d624", QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, 119 {QStringLiteral("3"), {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}},
122 {"4", {"#FF0000", QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, 120 {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}},
123 {"5", {"#828282", QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, 121 {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},
124 {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; 122 {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}},
123 };
125 // clang-format on 124 // clang-format on
126 125
127 auto iterator = status_data.find(compatibility); 126 auto iterator = status_data.find(compatibility);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 8687e7c5a..82d2826ba 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -45,7 +45,7 @@ bool HasSupportedFileExtension(const std::string& file_name) {
45} 45}
46 46
47bool IsExtractedNCAMain(const std::string& file_name) { 47bool IsExtractedNCAMain(const std::string& file_name) {
48 return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; 48 return QFileInfo(QString::fromStdString(file_name)).fileName() == QStringLiteral("main");
49} 49}
50 50
51QString FormatGameName(const std::string& physical_name) { 51QString FormatGameName(const std::string& physical_name) {
@@ -97,7 +97,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
97 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 97 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
98 98
99 // The game list uses this as compatibility number for untested games 99 // The game list uses this as compatibility number for untested games
100 QString compatibility{"99"}; 100 QString compatibility{QStringLiteral("99")};
101 if (it != compatibility_list.end()) { 101 if (it != compatibility_list.end()) {
102 compatibility = it->second.first; 102 compatibility = it->second.first;
103 } 103 }
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index 4e2d988cd..4f2bfab48 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -30,11 +30,11 @@
30#include <QMovie> 30#include <QMovie>
31#endif 31#endif
32 32
33constexpr const char PROGRESSBAR_STYLE_PREPARE[] = R"( 33constexpr char PROGRESSBAR_STYLE_PREPARE[] = R"(
34QProgressBar {} 34QProgressBar {}
35QProgressBar::chunk {})"; 35QProgressBar::chunk {})";
36 36
37constexpr const char PROGRESSBAR_STYLE_DECOMPILE[] = R"( 37constexpr char PROGRESSBAR_STYLE_DECOMPILE[] = R"(
38QProgressBar { 38QProgressBar {
39 background-color: black; 39 background-color: black;
40 border: 2px solid white; 40 border: 2px solid white;
@@ -46,7 +46,7 @@ QProgressBar::chunk {
46 width: 1px; 46 width: 1px;
47})"; 47})";
48 48
49constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( 49constexpr char PROGRESSBAR_STYLE_BUILD[] = R"(
50QProgressBar { 50QProgressBar {
51 background-color: black; 51 background-color: black;
52 border: 2px solid white; 52 border: 2px solid white;
@@ -58,7 +58,7 @@ QProgressBar::chunk {
58 width: 1px; 58 width: 1px;
59})"; 59})";
60 60
61constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( 61constexpr char PROGRESSBAR_STYLE_COMPLETE[] = R"(
62QProgressBar { 62QProgressBar {
63 background-color: #0ab9e6; 63 background-color: #0ab9e6;
64 border: 2px solid white; 64 border: 2px solid white;
@@ -149,10 +149,10 @@ void LoadingScreen::OnLoadComplete() {
149void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, 149void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value,
150 std::size_t total) { 150 std::size_t total) {
151 using namespace std::chrono; 151 using namespace std::chrono;
152 auto now = high_resolution_clock::now(); 152 const auto now = high_resolution_clock::now();
153 // reset the timer if the stage changes 153 // reset the timer if the stage changes
154 if (stage != previous_stage) { 154 if (stage != previous_stage) {
155 ui->progress_bar->setStyleSheet(progressbar_style[stage]); 155 ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage]));
156 // Hide the progress bar during the prepare stage 156 // Hide the progress bar during the prepare stage
157 if (stage == VideoCore::LoadCallbackStage::Prepare) { 157 if (stage == VideoCore::LoadCallbackStage::Prepare) {
158 ui->progress_bar->hide(); 158 ui->progress_bar->hide();
@@ -178,16 +178,16 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
178 slow_shader_first_value = value; 178 slow_shader_first_value = value;
179 } 179 }
180 // only calculate an estimate time after a second has passed since stage change 180 // only calculate an estimate time after a second has passed since stage change
181 auto diff = duration_cast<milliseconds>(now - slow_shader_start); 181 const auto diff = duration_cast<milliseconds>(now - slow_shader_start);
182 if (diff > seconds{1}) { 182 if (diff > seconds{1}) {
183 auto eta_mseconds = 183 const auto eta_mseconds =
184 static_cast<long>(static_cast<double>(total - slow_shader_first_value) / 184 static_cast<long>(static_cast<double>(total - slow_shader_first_value) /
185 (value - slow_shader_first_value) * diff.count()); 185 (value - slow_shader_first_value) * diff.count());
186 estimate = 186 estimate =
187 tr("Estimated Time %1") 187 tr("Estimated Time %1")
188 .arg(QTime(0, 0, 0, 0) 188 .arg(QTime(0, 0, 0, 0)
189 .addMSecs(std::max<long>(eta_mseconds - diff.count() + 1000, 1000)) 189 .addMSecs(std::max<long>(eta_mseconds - diff.count() + 1000, 1000))
190 .toString("mm:ss")); 190 .toString(QStringLiteral("mm:ss")));
191 } 191 }
192 } 192 }
193 193
diff --git a/src/yuzu/util/spinbox.cpp b/src/yuzu/util/spinbox.cpp
deleted file mode 100644
index 14ef1e884..000000000
--- a/src/yuzu/util/spinbox.cpp
+++ /dev/null
@@ -1,278 +0,0 @@
1// Licensed under GPLv2 or any later version
2// Refer to the license.txt file included.
3
4// Copyright 2014 Tony Wasserka
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above copyright
13// notice, this list of conditions and the following disclaimer in the
14// documentation and/or other materials provided with the distribution.
15// * Neither the name of the owner nor the names of its contributors may
16// be used to endorse or promote products derived from this software
17// without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#include <cstdlib>
32#include <QLineEdit>
33#include <QRegExpValidator>
34#include "common/assert.h"
35#include "yuzu/util/spinbox.h"
36
37CSpinBox::CSpinBox(QWidget* parent)
38 : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) {
39 // TODO: Might be nice to not immediately call the slot.
40 // Think of an address that is being replaced by a different one, in which case a lot
41 // invalid intermediate addresses would be read from during editing.
42 connect(lineEdit(), &QLineEdit::textEdited, this, &CSpinBox::OnEditingFinished);
43
44 UpdateText();
45}
46
47void CSpinBox::SetValue(qint64 val) {
48 auto old_value = value;
49 value = std::max(std::min(val, max_value), min_value);
50
51 if (old_value != value) {
52 UpdateText();
53 emit ValueChanged(value);
54 }
55}
56
57void CSpinBox::SetRange(qint64 min, qint64 max) {
58 min_value = min;
59 max_value = max;
60
61 SetValue(value);
62 UpdateText();
63}
64
65void CSpinBox::stepBy(int steps) {
66 auto new_value = value;
67 // Scale number of steps by the currently selected digit
68 // TODO: Move this code elsewhere and enable it.
69 // TODO: Support for num_digits==0, too
70 // TODO: Support base!=16, too
71 // TODO: Make the cursor not jump back to the end of the line...
72 /*if (base == 16 && num_digits > 0) {
73 int digit = num_digits - (lineEdit()->cursorPosition() - prefix.length()) - 1;
74 digit = std::max(0, std::min(digit, num_digits - 1));
75 steps <<= digit * 4;
76 }*/
77
78 // Increment "new_value" by "steps", and perform annoying overflow checks, too.
79 if (steps < 0 && new_value + steps > new_value) {
80 new_value = std::numeric_limits<qint64>::min();
81 } else if (steps > 0 && new_value + steps < new_value) {
82 new_value = std::numeric_limits<qint64>::max();
83 } else {
84 new_value += steps;
85 }
86
87 SetValue(new_value);
88 UpdateText();
89}
90
91QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const {
92 StepEnabled ret = StepNone;
93
94 if (value > min_value)
95 ret |= StepDownEnabled;
96
97 if (value < max_value)
98 ret |= StepUpEnabled;
99
100 return ret;
101}
102
103void CSpinBox::SetBase(int base) {
104 this->base = base;
105
106 UpdateText();
107}
108
109void CSpinBox::SetNumDigits(int num_digits) {
110 this->num_digits = num_digits;
111
112 UpdateText();
113}
114
115void CSpinBox::SetPrefix(const QString& prefix) {
116 this->prefix = prefix;
117
118 UpdateText();
119}
120
121void CSpinBox::SetSuffix(const QString& suffix) {
122 this->suffix = suffix;
123
124 UpdateText();
125}
126
127static QString StringToInputMask(const QString& input) {
128 QString mask = input;
129
130 // ... replace any special characters by their escaped counterparts ...
131 mask.replace("\\", "\\\\");
132 mask.replace("A", "\\A");
133 mask.replace("a", "\\a");
134 mask.replace("N", "\\N");
135 mask.replace("n", "\\n");
136 mask.replace("X", "\\X");
137 mask.replace("x", "\\x");
138 mask.replace("9", "\\9");
139 mask.replace("0", "\\0");
140 mask.replace("D", "\\D");
141 mask.replace("d", "\\d");
142 mask.replace("#", "\\#");
143 mask.replace("H", "\\H");
144 mask.replace("h", "\\h");
145 mask.replace("B", "\\B");
146 mask.replace("b", "\\b");
147 mask.replace(">", "\\>");
148 mask.replace("<", "\\<");
149 mask.replace("!", "\\!");
150
151 return mask;
152}
153
154void CSpinBox::UpdateText() {
155 // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
156 // input mask.
157 QString mask;
158 if (num_digits != 0) {
159 mask += StringToInputMask(prefix);
160
161 // For base 10 and negative range, demand a single sign character
162 if (HasSign())
163 mask += "X"; // identified as "-" or "+" in the validator
164
165 // Uppercase digits greater than 9.
166 mask += ">";
167
168 // Match num_digits digits
169 // Digits irrelevant to the chosen number base are filtered in the validator
170 mask += QString("H").repeated(std::max(num_digits, 1));
171
172 // Switch off case conversion
173 mask += "!";
174
175 mask += StringToInputMask(suffix);
176 }
177 lineEdit()->setInputMask(mask);
178
179 // Set new text without changing the cursor position. This will cause the cursor to briefly
180 // appear at the end of the line and then to jump back to its original position. That's
181 // a bit ugly, but better than having setText() move the cursor permanently all the time.
182 int cursor_position = lineEdit()->cursorPosition();
183 lineEdit()->setText(TextFromValue());
184 lineEdit()->setCursorPosition(cursor_position);
185}
186
187QString CSpinBox::TextFromValue() {
188 return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") +
189 QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() +
190 suffix;
191}
192
193qint64 CSpinBox::ValueFromText() {
194 unsigned strpos = prefix.length();
195
196 QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
197 return num_string.toLongLong(nullptr, base);
198}
199
200bool CSpinBox::HasSign() const {
201 return base == 10 && min_value < 0;
202}
203
204void CSpinBox::OnEditingFinished() {
205 // Only update for valid input
206 QString input = lineEdit()->text();
207 int pos = 0;
208 if (QValidator::Acceptable == validate(input, pos))
209 SetValue(ValueFromText());
210}
211
212QValidator::State CSpinBox::validate(QString& input, int& pos) const {
213 if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
214 return QValidator::Invalid;
215
216 int strpos = prefix.length();
217
218 // Empty "numbers" allowed as intermediate values
219 if (strpos >= input.length() - HasSign() - suffix.length())
220 return QValidator::Intermediate;
221
222 DEBUG_ASSERT(base <= 10 || base == 16);
223 QString regexp;
224
225 // Demand sign character for negative ranges
226 if (HasSign())
227 regexp += "[+\\-]";
228
229 // Match digits corresponding to the chosen number base.
230 regexp += QString("[0-%1").arg(std::min(base, 9));
231 if (base == 16) {
232 regexp += "a-fA-F";
233 }
234 regexp += "]";
235
236 // Specify number of digits
237 if (num_digits > 0) {
238 regexp += QString("{%1}").arg(num_digits);
239 } else {
240 regexp += "+";
241 }
242
243 // Match string
244 QRegExp num_regexp(regexp);
245 int num_pos = strpos;
246 QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length());
247
248 if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0)
249 return QValidator::Invalid;
250
251 sub_input = sub_input.left(num_regexp.matchedLength());
252 bool ok;
253 qint64 val = sub_input.toLongLong(&ok, base);
254
255 if (!ok)
256 return QValidator::Invalid;
257
258 // Outside boundaries => don't accept
259 if (val < min_value || val > max_value)
260 return QValidator::Invalid;
261
262 // Make sure we are actually at the end of this string...
263 strpos += num_regexp.matchedLength();
264
265 if (!suffix.isEmpty() && input.mid(strpos) != suffix) {
266 return QValidator::Invalid;
267 } else {
268 strpos += suffix.length();
269 }
270
271 if (strpos != input.length())
272 return QValidator::Invalid;
273
274 // At this point we can say for sure that the input is fine. Let's fix it up a bit though
275 input.replace(num_pos, sub_input.length(), sub_input.toUpper());
276
277 return QValidator::Acceptable;
278}
diff --git a/src/yuzu/util/spinbox.h b/src/yuzu/util/spinbox.h
deleted file mode 100644
index 2fa1db3a4..000000000
--- a/src/yuzu/util/spinbox.h
+++ /dev/null
@@ -1,86 +0,0 @@
1// Licensed under GPLv2 or any later version
2// Refer to the license.txt file included.
3
4// Copyright 2014 Tony Wasserka
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above copyright
13// notice, this list of conditions and the following disclaimer in the
14// documentation and/or other materials provided with the distribution.
15// * Neither the name of the owner nor the names of its contributors may
16// be used to endorse or promote products derived from this software
17// without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#pragma once
32
33#include <QAbstractSpinBox>
34#include <QtGlobal>
35
36class QVariant;
37
38/**
39 * A custom spin box widget with enhanced functionality over Qt's QSpinBox
40 */
41class CSpinBox : public QAbstractSpinBox {
42 Q_OBJECT
43
44public:
45 explicit CSpinBox(QWidget* parent = nullptr);
46
47 void stepBy(int steps) override;
48 StepEnabled stepEnabled() const override;
49
50 void SetValue(qint64 val);
51
52 void SetRange(qint64 min, qint64 max);
53
54 void SetBase(int base);
55
56 void SetPrefix(const QString& prefix);
57 void SetSuffix(const QString& suffix);
58
59 void SetNumDigits(int num_digits);
60
61 QValidator::State validate(QString& input, int& pos) const override;
62
63signals:
64 void ValueChanged(qint64 val);
65
66private slots:
67 void OnEditingFinished();
68
69private:
70 void UpdateText();
71
72 bool HasSign() const;
73
74 QString TextFromValue();
75 qint64 ValueFromText();
76
77 qint64 min_value, max_value;
78
79 qint64 value;
80
81 QString prefix, suffix;
82
83 int base;
84
85 int num_digits;
86};
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index 62c080aff..ef31bc2d2 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -8,7 +8,7 @@
8#include "yuzu/util/util.h" 8#include "yuzu/util/util.h"
9 9
10QFont GetMonospaceFont() { 10QFont GetMonospaceFont() {
11 QFont font("monospace"); 11 QFont font(QStringLiteral("monospace"));
12 // Automatic fallback to a monospace font on on platforms without a font called "monospace" 12 // Automatic fallback to a monospace font on on platforms without a font called "monospace"
13 font.setStyleHint(QFont::Monospace); 13 font.setStyleHint(QFont::Monospace);
14 font.setFixedPitch(true); 14 font.setFixedPitch(true);
@@ -16,14 +16,16 @@ QFont GetMonospaceFont() {
16} 16}
17 17
18QString ReadableByteSize(qulonglong size) { 18QString ReadableByteSize(qulonglong size) {
19 static const std::array<const char*, 6> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; 19 static constexpr std::array units{"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
20 if (size == 0) 20 if (size == 0) {
21 return "0"; 21 return QStringLiteral("0");
22 int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), 22 }
23 static_cast<int>(units.size())); 23
24 return QString("%L1 %2") 24 const int digit_groups = std::min(static_cast<int>(std::log10(size) / std::log10(1024)),
25 static_cast<int>(units.size()));
26 return QStringLiteral("%L1 %2")
25 .arg(size / std::pow(1024, digit_groups), 0, 'f', 1) 27 .arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
26 .arg(units[digit_groups]); 28 .arg(QString::fromUtf8(units[digit_groups]));
27} 29}
28 30
29QPixmap CreateCirclePixmapFromColor(const QColor& color) { 31QPixmap CreateCirclePixmapFromColor(const QColor& color) {