diff options
Diffstat (limited to 'src')
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() { | |||
| 438 | template <> | 438 | template <> |
| 439 | inline double RequestParser::Pop() { | 439 | inline 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 | ||
| 35 | enum class ResetType { | 35 | enum 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 | ||
| 40 | class Object : NonCopyable { | 40 | class 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 { | |||
| 21 | void ReadableEvent::Acquire(Thread* thread) { | 21 | void 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 | ||
| 28 | void ReadableEvent::Signal() { | 29 | void 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 | ||
| 1258 | ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | 1258 | static 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 |
| 1343 | static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, | 1343 | static 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 |
| 1404 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { | 1404 | static 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 |
| 1427 | static void ExitThread(Core::System& system) { | 1427 | static 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 |
| 1437 | static void SleepThread(Core::System& system, s64 nanoseconds) { | 1437 | static 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 | ||
| 1882 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, | 1882 | static 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 | ||
| 2186 | ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, | 2193 | static 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 | ||
| 32 | enum ThreadProcessorId : s32 { | 32 | enum 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 | ||
| 443 | AppletMessageQueue::AppletMessageQueue() { | 443 | AppletMessageQueue::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 | ||
| 451 | AppletMessageQueue::~AppletMessageQueue() = default; | 451 | AppletMessageQueue::~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 { | |||
| 26 | AppletDataBroker::AppletDataBroker() { | 26 | AppletDataBroker::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 | ||
| 36 | AppletDataBroker::~AppletDataBroker() = default; | 36 | AppletDataBroker::~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 | ||
| 266 | static 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 | |||
| 265 | void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | 272 | void 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 | ||
| 325 | void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { | 566 | void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { |
| @@ -357,10 +598,15 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { | |||
| 357 | } | 598 | } |
| 358 | 599 | ||
| 359 | bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { | 600 | bool 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 | ||
| 41 | private: | 41 | private: |
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) { | |||
| 170 | void Controller_NPad::OnInit() { | 170 | void 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); | |||
| 26 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 26 | Module::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 | ||
| 75 | private: | 75 | private: |
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 | ||
| 17 | BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { | 17 | BufferQueue::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 | ||
| 13 | namespace Service::Set { | 12 | namespace Service::Set { |
| 14 | 13 | namespace { | |
| 15 | constexpr std::array<LanguageCode, 17> available_language_codes = {{ | 14 | constexpr 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 | ||
| 35 | constexpr std::size_t pre4_0_0_max_entries = 0xF; | 34 | constexpr std::size_t pre4_0_0_max_entries = 15; |
| 36 | constexpr std::size_t post4_0_0_max_entries = 0x40; | 35 | constexpr std::size_t post4_0_0_max_entries = 17; |
| 37 | 36 | ||
| 38 | constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; | 37 | constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; |
| 39 | 38 | ||
| 40 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { | 39 | void 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 | ||
| 44 | template <std::size_t size> | 45 | void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) { |
| 45 | static 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 | ||
| 51 | static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) { | 55 | LanguageCode 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 | ||
| 61 | void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { | 59 | void 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 | ||
| 72 | void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { | 65 | void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { |
| @@ -87,12 +80,7 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { | |||
| 87 | void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) { | 80 | void 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 | ||
| 98 | void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { | 86 | void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { |
| @@ -102,9 +90,9 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { | |||
| 102 | } | 90 | } |
| 103 | 91 | ||
| 104 | void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { | 92 | void 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 | ||
| 110 | void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { | 98 | void 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 | ||
| 18 | Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { | 18 | Display::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 @@ | |||
| 10 | namespace Tegra::Engines::Upload { | 12 | namespace Tegra::Engines::Upload { |
| 11 | 13 | ||
| 12 | State::State(MemoryManager& memory_manager, Registers& regs) | 14 | State::State(MemoryManager& memory_manager, Registers& regs) |
| 13 | : memory_manager(memory_manager), regs(regs) {} | 15 | : regs{regs}, memory_manager{memory_manager} {} |
| 16 | |||
| 17 | State::~State() = default; | ||
| 14 | 18 | ||
| 15 | void State::ProcessExec(const bool is_linear) { | 19 | void 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 | ||
| 13 | namespace Tegra { | 11 | namespace Tegra { |
| @@ -57,10 +55,10 @@ struct Registers { | |||
| 57 | class State { | 55 | class State { |
| 58 | public: | 56 | public: |
| 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 | ||
| 65 | private: | 63 | private: |
| 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 | ||
| 506 | enum class PhysicalAttributeDirection : u64 { | ||
| 507 | Input = 0, | ||
| 508 | Output = 1, | ||
| 509 | }; | ||
| 510 | |||
| 502 | union Instruction { | 511 | union 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 | ||
| 22 | Device::Device() { | 22 | Device::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 | ||
| 29 | Device::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 | |||
| 27 | bool Device::TestVariableAoffi() { | 36 | bool Device::TestVariableAoffi() { |
| 28 | const GLchar* AOFFI_TEST = R"(#version 430 core | 37 | const GLchar* AOFFI_TEST = R"(#version 430 core |
| 29 | uniform sampler2D tex; | 38 | uniform 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 | ||
| 9 | namespace OpenGL { | 10 | namespace OpenGL { |
| 10 | 11 | ||
| 11 | class Device { | 12 | class Device { |
| 12 | public: | 13 | public: |
| 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 | ||
| 1136 | void RasterizerOpenGL::SyncPointState() { | 1136 | void 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 | ||
| 1141 | void RasterizerOpenGL::SyncPolygonOffset() { | 1143 | void 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 | ||
| 134 | constexpr bool IsGenericAttribute(Attribute::Index index) { | ||
| 135 | return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31; | ||
| 136 | } | ||
| 137 | |||
| 138 | constexpr Attribute::Index ToGenericAttribute(u32 value) { | ||
| 139 | return static_cast<Attribute::Index>(value + static_cast<u32>(Attribute::Index::Attribute_0)); | ||
| 140 | } | ||
| 141 | |||
| 142 | u32 GetGenericAttributeIndex(Attribute::Index index) { | ||
| 143 | ASSERT(IsGenericAttribute(index)); | ||
| 144 | return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0); | ||
| 145 | } | ||
| 146 | |||
| 137 | class GLSLDecompiler final { | 147 | class GLSLDecompiler final { |
| 138 | public: | 148 | public: |
| 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 | ||
| 1654 | std::string GetCommonDeclarations() { | 1748 | std::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 | ||
| 1675 | ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, | 1770 | ProgramResult 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 | ||
| 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) | 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} |
| 108 | : system{system}, precompiled_cache_virtual_file_offset{0} {} | 108 | |
| 109 | ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default; | ||
| 109 | 110 | ||
| 110 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> | 111 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> |
| 111 | ShaderDiskCacheOpenGL::LoadTransferable() { | 112 | ShaderDiskCacheOpenGL::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 | ||
| 71 | template <> | 71 | template <> |
| 72 | struct hash<OpenGL::BaseBindings> { | 72 | struct 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 | ||
| 78 | template <> | 78 | template <> |
| 79 | struct hash<OpenGL::ShaderDiskCacheUsage> { | 79 | struct 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 { | |||
| 162 | class ShaderDiskCacheOpenGL { | 162 | class ShaderDiskCacheOpenGL { |
| 163 | public: | 163 | public: |
| 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 | ||
| 82 | ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { | 82 | ProgramResult 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 | ||
| 113 | ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { | 113 | ProgramResult 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; | |||
| 21 | using Tegra::Shader::PredOperation; | 21 | using Tegra::Shader::PredOperation; |
| 22 | using Tegra::Shader::Register; | 22 | using Tegra::Shader::Register; |
| 23 | 23 | ||
| 24 | ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset) | ||
| 25 | : program_code{program_code}, main_offset{main_offset} { | ||
| 26 | Decode(); | ||
| 27 | } | ||
| 28 | |||
| 29 | ShaderIR::~ShaderIR() = default; | ||
| 30 | |||
| 24 | Node ShaderIR::StoreNode(NodeData&& node_data) { | 31 | Node 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 | ||
| 35 | Node ShaderIR::Comment(const std::string& text) { | 42 | Node ShaderIR::Comment(std::string text) { |
| 36 | return StoreNode(CommentNode(text)); | 43 | return StoreNode(CommentNode(std::move(text))); |
| 37 | } | 44 | } |
| 38 | 45 | ||
| 39 | Node ShaderIR::Immediate(u32 value) { | 46 | Node 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 | ||
| 92 | Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, | 99 | Node 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)); | 104 | Node 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 | ||
| 101 | Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) { | 109 | Node 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 | ||
| 331 | inline constexpr MetaArithmetic PRECISE = {true}; | 331 | constexpr MetaArithmetic PRECISE = {true}; |
| 332 | inline constexpr MetaArithmetic NO_PRECISE = {false}; | 332 | constexpr MetaArithmetic NO_PRECISE = {false}; |
| 333 | 333 | ||
| 334 | using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>; | 334 | using 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 |
| 337 | class OperationNode final { | 337 | class OperationNode final { |
| 338 | public: | 338 | public: |
| 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) |
| 466 | class AbufNode final { | 457 | class AbufNode final { |
| 467 | public: | 458 | public: |
| 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 | |||
| 492 | private: | 488 | private: |
| 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 | ||
| 568 | class ShaderIR final { | 564 | class ShaderIR final { |
| 569 | public: | 565 | public: |
| 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 | ||
| 35 | Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) { | 37 | Node 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 | ||
| 68 | std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) { | 70 | std::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 | ||
| 82 | std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, | 84 | std::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 | ||
| 55 | void QtErrorDisplay::MainWindowFinishedError() { | 55 | void 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: | |||
| 188 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 188 | GRenderWindow::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 | ||
| 292 | void Config::ReadDebugValues() { | 301 | void 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 | ||
| 321 | void Config::ReadKeyboardValues() { | 338 | void 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 | ||
| 334 | void Config::ReadMouseValues() { | 352 | void 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 | ||
| 362 | void Config::ApplyDefaultProfileIfInputInvalid() { | 383 | void Config::ApplyDefaultProfileIfInputInvalid() { |
| @@ -366,8 +387,25 @@ void Config::ApplyDefaultProfileIfInputInvalid() { | |||
| 366 | } | 387 | } |
| 367 | } | 388 | } |
| 368 | 389 | ||
| 369 | void Config::ReadValues() { | 390 | void 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 | |||
| 407 | void 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"); | 425 | void 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 | |||
| 434 | void 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"); | 456 | void 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"); | 469 | void 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"); | 489 | void 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 | |||
| 501 | void 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 | |||
| 515 | void 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 | |||
| 541 | void 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 | |||
| 560 | void 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(); | 590 | void 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 | ||
| 628 | void 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 | |||
| 640 | void 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 | |||
| 657 | void 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 | |||
| 674 | void 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 | |||
| 564 | void Config::SavePlayerValues() { | 688 | void 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() { | |||
| 600 | void Config::SaveDebugValues() { | 725 | void 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 | ||
| 620 | void Config::SaveMouseValues() { | 745 | void 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 | ||
| 632 | void Config::SaveTouchscreenValues() { | 758 | void 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 | ||
| 643 | void Config::SaveValues() { | 771 | void 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 | |||
| 785 | void 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 | |||
| 799 | void 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"); | 815 | void 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"); | 824 | void 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"); | 838 | void 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 | |||
| 851 | void 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"); | 871 | void 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 | |||
| 881 | void 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 | |||
| 896 | void 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 | |||
| 921 | void 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 | |||
| 941 | void 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 | |||
| 961 | void 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 | |||
| 991 | void 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 | |||
| 1003 | void 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 | |||
| 1018 | void 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 | ||
| 812 | QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { | 1036 | QVariant 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 | ||
| 826 | void Config::WriteSetting(const QString& name, const QVariant& value, | 1050 | void 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 | ||
| 36 | ConfigureAudio::~ConfigureAudio() = default; | 36 | ConfigureAudio::~ConfigureAudio() = default; |
| @@ -94,7 +94,7 @@ void ConfigureAudio::applyConfiguration() { | |||
| 94 | 94 | ||
| 95 | void ConfigureAudio::updateAudioDevices(int sink_index) { | 95 | void 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 | ||
| 101 | void ConfigureGameList::InitializeIconSizeComboBox() { | 101 | void 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 | ||
| 107 | void ConfigureGameList::InitializeRowComboBoxes() { | 107 | void 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, | |||
| 65 | static QString ButtonToText(const Common::ParamPackage& param) { | 65 | static 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 | ||
| 88 | static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { | 101 | static 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 | ||
| 109 | ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) | 133 | ConfigureInputPlayer::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 | ||
| 357 | void ConfigureInputPlayer::loadConfiguration() { | 385 | void 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 | ||
| 411 | void ConfigureInputPlayer::ClearAll() { | 440 | void 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) { | |||
| 34 | static QString ButtonToText(const Common::ParamPackage& param) { | 34 | static 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 | ||
| 57 | ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) | 69 | ConfigureMouseAdvanced::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 | ||
| 139 | void ConfigureMouseAdvanced::ClearAll() { | 152 | void 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 | ||
| 90 | void ConfigurePerGameGeneral::loadConfiguration() { | 90 | void 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() { | |||
| 78 | void ConfigureWeb::OnLoginChanged() { | 78 | void 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 | ||
| 49 | MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) { | 49 | MicroProfileDialog::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 | |||
| 91 | WaitTreeMutexInfo::~WaitTreeMutexInfo() = default; | 91 | WaitTreeMutexInfo::~WaitTreeMutexInfo() = default; |
| 92 | 92 | ||
| 93 | QString WaitTreeMutexInfo::GetText() const { | 93 | QString 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 | ||
| 97 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { | 97 | std::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 | ||
| 175 | QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) { | 178 | QString 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 | ||
| 257 | QColor WaitTreeThread::GetColor() const { | 260 | QColor WaitTreeThread::GetColor() const { |
| @@ -424,7 +427,7 @@ void WaitTreeModel::InitItems() { | |||
| 424 | } | 427 | } |
| 425 | 428 | ||
| 426 | WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) { | 429 | WaitTreeWidget::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 | ||
| 95 | void GameListSearchField::clear() { | 94 | void GameListSearchField::clear() { |
| 96 | edit_filter->setText(""); | 95 | edit_filter->clear(); |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | void GameListSearchField::setFocus() { | 98 | void GameListSearchField::setFocus() { |
| @@ -103,25 +102,26 @@ void GameListSearchField::setFocus() { | |||
| 103 | } | 102 | } |
| 104 | 103 | ||
| 105 | GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { | 104 | GameListSearchField::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 | */ |
| 142 | static bool ContainsAllWords(const QString& haystack, const QString& userinput) { | 142 | static 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 |
| 151 | void GameList::onTextChanged(const QString& newText) { | 151 | void 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 | ||
| 362 | void GameList::LoadCompatibilityList() { | 362 | void 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 | ||
| 467 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; | 469 | const QStringList GameList::supported_file_extensions = { |
| 470 | QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), | ||
| 471 | QStringLiteral("xci"), QStringLiteral("nsp"), | ||
| 472 | }; | ||
| 468 | 473 | ||
| 469 | void GameList::RefreshGameDirectory() { | 474 | void 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 | ||
| 78 | private slots: | 78 | private slots: |
| 79 | void onTextChanged(const QString& newText); | 79 | void onTextChanged(const QString& new_text); |
| 80 | void onFilterCloseClicked(); | 80 | void onFilterCloseClicked(); |
| 81 | 81 | ||
| 82 | private: | 82 | private: |
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 | */ |
| 32 | static QPixmap GetDefaultIcon(u32 size) { | 30 | static 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 | */ |
| 51 | class GameListItemPath : public GameListItem { | 49 | class GameListItemPath : public GameListItem { |
| 52 | public: | 50 | public: |
| @@ -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 | ||
| 47 | bool IsExtractedNCAMain(const std::string& file_name) { | 47 | bool 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 | ||
| 51 | QString FormatGameName(const std::string& physical_name) { | 51 | QString 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 | ||
| 33 | constexpr const char PROGRESSBAR_STYLE_PREPARE[] = R"( | 33 | constexpr char PROGRESSBAR_STYLE_PREPARE[] = R"( |
| 34 | QProgressBar {} | 34 | QProgressBar {} |
| 35 | QProgressBar::chunk {})"; | 35 | QProgressBar::chunk {})"; |
| 36 | 36 | ||
| 37 | constexpr const char PROGRESSBAR_STYLE_DECOMPILE[] = R"( | 37 | constexpr char PROGRESSBAR_STYLE_DECOMPILE[] = R"( |
| 38 | QProgressBar { | 38 | QProgressBar { |
| 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 | ||
| 49 | constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( | 49 | constexpr char PROGRESSBAR_STYLE_BUILD[] = R"( |
| 50 | QProgressBar { | 50 | QProgressBar { |
| 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 | ||
| 61 | constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( | 61 | constexpr char PROGRESSBAR_STYLE_COMPLETE[] = R"( |
| 62 | QProgressBar { | 62 | QProgressBar { |
| 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() { | |||
| 149 | void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, | 149 | void 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 | |||
| 37 | CSpinBox::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 | |||
| 47 | void 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 | |||
| 57 | void CSpinBox::SetRange(qint64 min, qint64 max) { | ||
| 58 | min_value = min; | ||
| 59 | max_value = max; | ||
| 60 | |||
| 61 | SetValue(value); | ||
| 62 | UpdateText(); | ||
| 63 | } | ||
| 64 | |||
| 65 | void 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 | |||
| 91 | QAbstractSpinBox::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 | |||
| 103 | void CSpinBox::SetBase(int base) { | ||
| 104 | this->base = base; | ||
| 105 | |||
| 106 | UpdateText(); | ||
| 107 | } | ||
| 108 | |||
| 109 | void CSpinBox::SetNumDigits(int num_digits) { | ||
| 110 | this->num_digits = num_digits; | ||
| 111 | |||
| 112 | UpdateText(); | ||
| 113 | } | ||
| 114 | |||
| 115 | void CSpinBox::SetPrefix(const QString& prefix) { | ||
| 116 | this->prefix = prefix; | ||
| 117 | |||
| 118 | UpdateText(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void CSpinBox::SetSuffix(const QString& suffix) { | ||
| 122 | this->suffix = suffix; | ||
| 123 | |||
| 124 | UpdateText(); | ||
| 125 | } | ||
| 126 | |||
| 127 | static 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 | |||
| 154 | void 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 | |||
| 187 | QString 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 | |||
| 193 | qint64 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 | |||
| 200 | bool CSpinBox::HasSign() const { | ||
| 201 | return base == 10 && min_value < 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | void 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 | |||
| 212 | QValidator::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 | |||
| 36 | class QVariant; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * A custom spin box widget with enhanced functionality over Qt's QSpinBox | ||
| 40 | */ | ||
| 41 | class CSpinBox : public QAbstractSpinBox { | ||
| 42 | Q_OBJECT | ||
| 43 | |||
| 44 | public: | ||
| 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 | |||
| 63 | signals: | ||
| 64 | void ValueChanged(qint64 val); | ||
| 65 | |||
| 66 | private slots: | ||
| 67 | void OnEditingFinished(); | ||
| 68 | |||
| 69 | private: | ||
| 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 | ||
| 10 | QFont GetMonospaceFont() { | 10 | QFont 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 | ||
| 18 | QString ReadableByteSize(qulonglong size) { | 18 | QString 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 | ||
| 29 | QPixmap CreateCirclePixmapFromColor(const QColor& color) { | 31 | QPixmap CreateCirclePixmapFromColor(const QColor& color) { |