diff options
Diffstat (limited to 'src')
29 files changed, 401 insertions, 238 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index aab3e979a..f80ab92e4 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo | |||
| 38 | sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { | 38 | sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { |
| 39 | 39 | ||
| 40 | release_event = Core::Timing::CreateEvent( | 40 | release_event = Core::Timing::CreateEvent( |
| 41 | name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); }); | 41 | name, [this](u64, std::chrono::nanoseconds ns_late) { ReleaseActiveBuffer(ns_late); }); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | void Stream::Play() { | 44 | void Stream::Play() { |
| @@ -59,11 +59,9 @@ Stream::State Stream::GetState() const { | |||
| 59 | return state; | 59 | return state; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const { | 62 | std::chrono::nanoseconds Stream::GetBufferReleaseNS(const Buffer& buffer) const { |
| 63 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; | 63 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; |
| 64 | const auto ns = | 64 | return std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate); |
| 65 | std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate); | ||
| 66 | return ns.count(); | ||
| 67 | } | 65 | } |
| 68 | 66 | ||
| 69 | static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { | 67 | static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { |
| @@ -80,7 +78,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { | |||
| 80 | } | 78 | } |
| 81 | } | 79 | } |
| 82 | 80 | ||
| 83 | void Stream::PlayNextBuffer(s64 cycles_late) { | 81 | void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) { |
| 84 | if (!IsPlaying()) { | 82 | if (!IsPlaying()) { |
| 85 | // Ensure we are in playing state before playing the next buffer | 83 | // Ensure we are in playing state before playing the next buffer |
| 86 | sink_stream.Flush(); | 84 | sink_stream.Flush(); |
| @@ -105,17 +103,18 @@ void Stream::PlayNextBuffer(s64 cycles_late) { | |||
| 105 | 103 | ||
| 106 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); | 104 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); |
| 107 | 105 | ||
| 108 | core_timing.ScheduleEvent( | 106 | const auto time_stretch_delta = Settings::values.enable_audio_stretching.GetValue() |
| 109 | GetBufferReleaseNS(*active_buffer) - | 107 | ? std::chrono::nanoseconds::zero() |
| 110 | (Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late), | 108 | : ns_late; |
| 111 | release_event, {}); | 109 | const auto future_time = GetBufferReleaseNS(*active_buffer) - time_stretch_delta; |
| 110 | core_timing.ScheduleEvent(future_time, release_event, {}); | ||
| 112 | } | 111 | } |
| 113 | 112 | ||
| 114 | void Stream::ReleaseActiveBuffer(s64 cycles_late) { | 113 | void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) { |
| 115 | ASSERT(active_buffer); | 114 | ASSERT(active_buffer); |
| 116 | released_buffers.push(std::move(active_buffer)); | 115 | released_buffers.push(std::move(active_buffer)); |
| 117 | release_callback(); | 116 | release_callback(); |
| 118 | PlayNextBuffer(cycles_late); | 117 | PlayNextBuffer(ns_late); |
| 119 | } | 118 | } |
| 120 | 119 | ||
| 121 | bool Stream::QueueBuffer(BufferPtr&& buffer) { | 120 | bool Stream::QueueBuffer(BufferPtr&& buffer) { |
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 524376257..6437b8591 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <chrono> | ||
| 7 | #include <functional> | 8 | #include <functional> |
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <string> | 10 | #include <string> |
| @@ -90,16 +91,13 @@ public: | |||
| 90 | 91 | ||
| 91 | private: | 92 | private: |
| 92 | /// Plays the next queued buffer in the audio stream, starting playback if necessary | 93 | /// Plays the next queued buffer in the audio stream, starting playback if necessary |
| 93 | void PlayNextBuffer(s64 cycles_late = 0); | 94 | void PlayNextBuffer(std::chrono::nanoseconds ns_late = {}); |
| 94 | 95 | ||
| 95 | /// Releases the actively playing buffer, signalling that it has been completed | 96 | /// Releases the actively playing buffer, signalling that it has been completed |
| 96 | void ReleaseActiveBuffer(s64 cycles_late = 0); | 97 | void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {}); |
| 97 | 98 | ||
| 98 | /// Gets the number of core cycles when the specified buffer will be released | 99 | /// Gets the number of core cycles when the specified buffer will be released |
| 99 | s64 GetBufferReleaseNS(const Buffer& buffer) const; | 100 | std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const; |
| 100 | |||
| 101 | /// Gets the number of core cycles when the specified buffer will be released | ||
| 102 | s64 GetBufferReleaseNSHostTiming(const Buffer& buffer) const; | ||
| 103 | 101 | ||
| 104 | u32 sample_rate; ///< Sample rate of the stream | 102 | u32 sample_rate; ///< Sample rate of the stream |
| 105 | Format format; ///< Format of the stream | 103 | Format format; ///< Format of the stream |
diff --git a/src/common/alignment.h b/src/common/alignment.h index b37044bb6..ef4d6f896 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | #pragma once | 3 | #pragma once |
| 4 | 4 | ||
| 5 | #include <cstddef> | 5 | #include <cstddef> |
| 6 | #include <memory> | 6 | #include <new> |
| 7 | #include <type_traits> | 7 | #include <type_traits> |
| 8 | 8 | ||
| 9 | namespace Common { | 9 | namespace Common { |
| @@ -54,66 +54,28 @@ public: | |||
| 54 | using size_type = std::size_t; | 54 | using size_type = std::size_t; |
| 55 | using difference_type = std::ptrdiff_t; | 55 | using difference_type = std::ptrdiff_t; |
| 56 | 56 | ||
| 57 | using pointer = T*; | ||
| 58 | using const_pointer = const T*; | ||
| 59 | |||
| 60 | using reference = T&; | ||
| 61 | using const_reference = const T&; | ||
| 62 | |||
| 63 | using propagate_on_container_copy_assignment = std::true_type; | 57 | using propagate_on_container_copy_assignment = std::true_type; |
| 64 | using propagate_on_container_move_assignment = std::true_type; | 58 | using propagate_on_container_move_assignment = std::true_type; |
| 65 | using propagate_on_container_swap = std::true_type; | 59 | using propagate_on_container_swap = std::true_type; |
| 66 | using is_always_equal = std::true_type; | 60 | using is_always_equal = std::true_type; |
| 67 | 61 | ||
| 68 | public: | ||
| 69 | constexpr AlignmentAllocator() noexcept = default; | 62 | constexpr AlignmentAllocator() noexcept = default; |
| 70 | 63 | ||
| 71 | template <typename T2> | 64 | template <typename T2> |
| 72 | constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} | 65 | constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} |
| 73 | 66 | ||
| 74 | pointer address(reference r) noexcept { | 67 | T* allocate(size_type n) { |
| 75 | return std::addressof(r); | 68 | return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align})); |
| 76 | } | ||
| 77 | |||
| 78 | const_pointer address(const_reference r) const noexcept { | ||
| 79 | return std::addressof(r); | ||
| 80 | } | ||
| 81 | |||
| 82 | pointer allocate(size_type n) { | ||
| 83 | return static_cast<pointer>(::operator new (n, std::align_val_t{Align})); | ||
| 84 | } | ||
| 85 | |||
| 86 | void deallocate(pointer p, size_type) { | ||
| 87 | ::operator delete (p, std::align_val_t{Align}); | ||
| 88 | } | 69 | } |
| 89 | 70 | ||
| 90 | void construct(pointer p, const value_type& wert) { | 71 | void deallocate(T* p, size_type n) { |
| 91 | new (p) value_type(wert); | 72 | ::operator delete (p, n * sizeof(T), std::align_val_t{Align}); |
| 92 | } | ||
| 93 | |||
| 94 | void destroy(pointer p) { | ||
| 95 | p->~value_type(); | ||
| 96 | } | ||
| 97 | |||
| 98 | size_type max_size() const noexcept { | ||
| 99 | return size_type(-1) / sizeof(value_type); | ||
| 100 | } | 73 | } |
| 101 | 74 | ||
| 102 | template <typename T2> | 75 | template <typename T2> |
| 103 | struct rebind { | 76 | struct rebind { |
| 104 | using other = AlignmentAllocator<T2, Align>; | 77 | using other = AlignmentAllocator<T2, Align>; |
| 105 | }; | 78 | }; |
| 106 | |||
| 107 | bool operator!=(const AlignmentAllocator<T, Align>& other) const noexcept { | ||
| 108 | return !(*this == other); | ||
| 109 | } | ||
| 110 | |||
| 111 | // Returns true if and only if storage allocated from *this | ||
| 112 | // can be deallocated from other, and vice versa. | ||
| 113 | // Always returns true for stateless allocators. | ||
| 114 | bool operator==(const AlignmentAllocator<T, Align>& other) const noexcept { | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | }; | 79 | }; |
| 118 | 80 | ||
| 119 | } // namespace Common | 81 | } // namespace Common |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a63e60461..b5feb3f24 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -53,12 +53,12 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { | |||
| 53 | instance.ThreadLoop(); | 53 | instance.ThreadLoop(); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) { | 56 | void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { |
| 57 | on_thread_init = std::move(on_thread_init_); | 57 | on_thread_init = std::move(on_thread_init_); |
| 58 | event_fifo_id = 0; | 58 | event_fifo_id = 0; |
| 59 | shutting_down = false; | 59 | shutting_down = false; |
| 60 | ticks = 0; | 60 | ticks = 0; |
| 61 | const auto empty_timed_callback = [](u64, s64) {}; | 61 | const auto empty_timed_callback = [](u64, std::chrono::nanoseconds) {}; |
| 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); | 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); |
| 63 | if (is_multicore) { | 63 | if (is_multicore) { |
| 64 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); | 64 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); |
| @@ -106,11 +106,11 @@ bool CoreTiming::HasPendingEvents() const { | |||
| 106 | return !(wait_set && event_queue.empty()); | 106 | return !(wait_set && event_queue.empty()); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | 109 | void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 110 | u64 userdata) { | 110 | const std::shared_ptr<EventType>& event_type, u64 userdata) { |
| 111 | { | 111 | { |
| 112 | std::scoped_lock scope{basic_lock}; | 112 | std::scoped_lock scope{basic_lock}; |
| 113 | const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future); | 113 | const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); |
| 114 | 114 | ||
| 115 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | 115 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); |
| 116 | 116 | ||
| @@ -195,8 +195,9 @@ std::optional<s64> CoreTiming::Advance() { | |||
| 195 | event_queue.pop_back(); | 195 | event_queue.pop_back(); |
| 196 | basic_lock.unlock(); | 196 | basic_lock.unlock(); |
| 197 | 197 | ||
| 198 | if (auto event_type{evt.type.lock()}) { | 198 | if (const auto event_type{evt.type.lock()}) { |
| 199 | event_type->callback(evt.userdata, global_timer - evt.time); | 199 | event_type->callback( |
| 200 | evt.userdata, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); | ||
| 200 | } | 201 | } |
| 201 | 202 | ||
| 202 | basic_lock.lock(); | 203 | basic_lock.lock(); |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 72faaab64..120c74e46 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -17,14 +17,12 @@ | |||
| 17 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "common/spin_lock.h" | 18 | #include "common/spin_lock.h" |
| 19 | #include "common/thread.h" | 19 | #include "common/thread.h" |
| 20 | #include "common/threadsafe_queue.h" | ||
| 21 | #include "common/wall_clock.h" | 20 | #include "common/wall_clock.h" |
| 22 | #include "core/hardware_properties.h" | ||
| 23 | 21 | ||
| 24 | namespace Core::Timing { | 22 | namespace Core::Timing { |
| 25 | 23 | ||
| 26 | /// A callback that may be scheduled for a particular core timing event. | 24 | /// A callback that may be scheduled for a particular core timing event. |
| 27 | using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>; | 25 | using TimedCallback = std::function<void(u64 userdata, std::chrono::nanoseconds ns_late)>; |
| 28 | 26 | ||
| 29 | /// Contains the characteristics of a particular event. | 27 | /// Contains the characteristics of a particular event. |
| 30 | struct EventType { | 28 | struct EventType { |
| @@ -42,12 +40,12 @@ struct EventType { | |||
| 42 | * in main CPU clock cycles. | 40 | * in main CPU clock cycles. |
| 43 | * | 41 | * |
| 44 | * To schedule an event, you first have to register its type. This is where you pass in the | 42 | * To schedule an event, you first have to register its type. This is where you pass in the |
| 45 | * callback. You then schedule events using the type id you get back. | 43 | * callback. You then schedule events using the type ID you get back. |
| 46 | * | 44 | * |
| 47 | * The int cyclesLate that the callbacks get is how many cycles late it was. | 45 | * The s64 ns_late that the callbacks get is how many ns late it was. |
| 48 | * So to schedule a new event on a regular basis: | 46 | * So to schedule a new event on a regular basis: |
| 49 | * inside callback: | 47 | * inside callback: |
| 50 | * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") | 48 | * ScheduleEvent(period_in_ns - ns_late, callback, "whatever") |
| 51 | */ | 49 | */ |
| 52 | class CoreTiming { | 50 | class CoreTiming { |
| 53 | public: | 51 | public: |
| @@ -62,7 +60,7 @@ public: | |||
| 62 | 60 | ||
| 63 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is | 61 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is |
| 64 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. | 62 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. |
| 65 | void Initialize(std::function<void(void)>&& on_thread_init_); | 63 | void Initialize(std::function<void()>&& on_thread_init_); |
| 66 | 64 | ||
| 67 | /// Tears down all timing related functionality. | 65 | /// Tears down all timing related functionality. |
| 68 | void Shutdown(); | 66 | void Shutdown(); |
| @@ -95,8 +93,8 @@ public: | |||
| 95 | bool HasPendingEvents() const; | 93 | bool HasPendingEvents() const; |
| 96 | 94 | ||
| 97 | /// Schedules an event in core timing | 95 | /// Schedules an event in core timing |
| 98 | void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | 96 | void ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 99 | u64 userdata = 0); | 97 | const std::shared_ptr<EventType>& event_type, u64 userdata = 0); |
| 100 | 98 | ||
| 101 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); | 99 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); |
| 102 | 100 | ||
| @@ -141,8 +139,6 @@ private: | |||
| 141 | 139 | ||
| 142 | u64 global_timer = 0; | 140 | u64 global_timer = 0; |
| 143 | 141 | ||
| 144 | std::chrono::nanoseconds start_point; | ||
| 145 | |||
| 146 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 142 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. |
| 147 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 143 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and |
| 148 | // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't | 144 | // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't |
| @@ -161,7 +157,7 @@ private: | |||
| 161 | std::atomic<bool> wait_set{}; | 157 | std::atomic<bool> wait_set{}; |
| 162 | std::atomic<bool> shutting_down{}; | 158 | std::atomic<bool> shutting_down{}; |
| 163 | std::atomic<bool> has_started{}; | 159 | std::atomic<bool> has_started{}; |
| 164 | std::function<void(void)> on_thread_init{}; | 160 | std::function<void()> on_thread_init{}; |
| 165 | 161 | ||
| 166 | bool is_multicore{}; | 162 | bool is_multicore{}; |
| 167 | 163 | ||
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index c629d9fa1..efc1030c1 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp | |||
| @@ -11,19 +11,20 @@ | |||
| 11 | namespace Core::Hardware { | 11 | namespace Core::Hardware { |
| 12 | 12 | ||
| 13 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | 13 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { |
| 14 | gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) { | 14 | gpu_interrupt_event = |
| 15 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | 15 | Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, std::chrono::nanoseconds) { |
| 16 | const u32 syncpt = static_cast<u32>(message >> 32); | 16 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); |
| 17 | const u32 value = static_cast<u32>(message); | 17 | const u32 syncpt = static_cast<u32>(message >> 32); |
| 18 | nvdrv->SignalGPUInterruptSyncpt(syncpt, value); | 18 | const u32 value = static_cast<u32>(message); |
| 19 | }); | 19 | nvdrv->SignalGPUInterruptSyncpt(syncpt, value); |
| 20 | }); | ||
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | InterruptManager::~InterruptManager() = default; | 23 | InterruptManager::~InterruptManager() = default; |
| 23 | 24 | ||
| 24 | void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { | 25 | void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { |
| 25 | const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; | 26 | const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; |
| 26 | system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg); | 27 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | } // namespace Core::Hardware | 30 | } // namespace Core::Hardware |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e1c7a0f3b..8dd4a2637 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -145,16 +145,18 @@ struct KernelCore::Impl { | |||
| 145 | 145 | ||
| 146 | void InitializePreemption(KernelCore& kernel) { | 146 | void InitializePreemption(KernelCore& kernel) { |
| 147 | preemption_event = Core::Timing::CreateEvent( | 147 | preemption_event = Core::Timing::CreateEvent( |
| 148 | "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { | 148 | "PreemptionCallback", [this, &kernel](u64, std::chrono::nanoseconds) { |
| 149 | { | 149 | { |
| 150 | SchedulerLock lock(kernel); | 150 | SchedulerLock lock(kernel); |
| 151 | global_scheduler.PreemptThreads(); | 151 | global_scheduler.PreemptThreads(); |
| 152 | } | 152 | } |
| 153 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | 153 | const auto time_interval = std::chrono::nanoseconds{ |
| 154 | Core::Timing::msToCycles(std::chrono::milliseconds(10))}; | ||
| 154 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 155 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 155 | }); | 156 | }); |
| 156 | 157 | ||
| 157 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | 158 | const auto time_interval = |
| 159 | std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))}; | ||
| 158 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 160 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 159 | } | 161 | } |
| 160 | 162 | ||
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 7b23a6889..af22f4c33 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern | |||
| 34 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; | 34 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; |
| 35 | 35 | ||
| 36 | session->request_event = Core::Timing::CreateEvent( | 36 | session->request_event = Core::Timing::CreateEvent( |
| 37 | name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); }); | 37 | name, [session](u64, std::chrono::nanoseconds) { session->CompleteSyncRequest(); }); |
| 38 | session->name = std::move(name); | 38 | session->name = std::move(name); |
| 39 | session->parent = std::move(parent); | 39 | session->parent = std::move(parent); |
| 40 | 40 | ||
| @@ -184,8 +184,8 @@ ResultCode ServerSession::CompleteSyncRequest() { | |||
| 184 | 184 | ||
| 185 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | 185 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, |
| 186 | Core::Memory::Memory& memory) { | 186 | Core::Memory::Memory& memory) { |
| 187 | ResultCode result = QueueSyncRequest(std::move(thread), memory); | 187 | const ResultCode result = QueueSyncRequest(std::move(thread), memory); |
| 188 | const u64 delay = kernel.IsMulticore() ? 0U : 20000U; | 188 | const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000}; |
| 189 | Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); | 189 | Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); |
| 190 | return result; | 190 | return result; |
| 191 | } | 191 | } |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 941305e8e..88b01b751 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -16,7 +16,7 @@ namespace Kernel { | |||
| 16 | 16 | ||
| 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { | 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { |
| 18 | time_manager_event_type = Core::Timing::CreateEvent( | 18 | time_manager_event_type = Core::Timing::CreateEvent( |
| 19 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | 19 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, std::chrono::nanoseconds) { |
| 20 | SchedulerLock lock(system.Kernel()); | 20 | SchedulerLock lock(system.Kernel()); |
| 21 | Handle proper_handle = static_cast<Handle>(thread_handle); | 21 | Handle proper_handle = static_cast<Handle>(thread_handle); |
| 22 | if (cancelled_events[proper_handle]) { | 22 | if (cancelled_events[proper_handle]) { |
| @@ -34,7 +34,8 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 | |||
| 34 | ASSERT(timetask); | 34 | ASSERT(timetask); |
| 35 | ASSERT(timetask->GetStatus() != ThreadStatus::Ready); | 35 | ASSERT(timetask->GetStatus() != ThreadStatus::Ready); |
| 36 | ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); | 36 | ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); |
| 37 | system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle); | 37 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, |
| 38 | time_manager_event_type, event_handle); | ||
| 38 | } else { | 39 | } else { |
| 39 | event_handle = InvalidHandle; | 40 | event_handle = InvalidHandle; |
| 40 | } | 41 | } |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index cadc03805..c66124998 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -55,6 +55,10 @@ std::string VfsDirectoryServiceWrapper::GetName() const { | |||
| 55 | ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { | 55 | ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { |
| 56 | std::string path(FileUtil::SanitizePath(path_)); | 56 | std::string path(FileUtil::SanitizePath(path_)); |
| 57 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | 57 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); |
| 58 | // dir can be nullptr if path contains subdirectories, create those prior to creating the file. | ||
| 59 | if (dir == nullptr) { | ||
| 60 | dir = backing->CreateSubdirectory(FileUtil::GetParentPath(path)); | ||
| 61 | } | ||
| 58 | auto file = dir->CreateFile(FileUtil::GetFilename(path)); | 62 | auto file = dir->CreateFile(FileUtil::GetFilename(path)); |
| 59 | if (file == nullptr) { | 63 | if (file == nullptr) { |
| 60 | // TODO(DarkLordZach): Find a better error code for this | 64 | // TODO(DarkLordZach): Find a better error code for this |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e9020e0dc..680290cbd 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -39,9 +39,10 @@ namespace Service::HID { | |||
| 39 | 39 | ||
| 40 | // Updating period for each HID device. | 40 | // Updating period for each HID device. |
| 41 | // TODO(ogniK): Find actual polling rate of hid | 41 | // TODO(ogniK): Find actual polling rate of hid |
| 42 | constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66); | 42 | constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66}; |
| 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100); | 43 | [[maybe_unused]] constexpr auto accelerometer_update_ns = |
| 44 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100); | 44 | std::chrono::nanoseconds{1000000000 / 100}; |
| 45 | [[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100}; | ||
| 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 46 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 46 | 47 | ||
| 47 | IAppletResource::IAppletResource(Core::System& system) | 48 | IAppletResource::IAppletResource(Core::System& system) |
| @@ -75,14 +76,14 @@ IAppletResource::IAppletResource(Core::System& system) | |||
| 75 | GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); | 76 | GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); |
| 76 | 77 | ||
| 77 | // Register update callbacks | 78 | // Register update callbacks |
| 78 | pad_update_event = | 79 | pad_update_event = Core::Timing::CreateEvent( |
| 79 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) { | 80 | "HID::UpdatePadCallback", [this](u64 userdata, std::chrono::nanoseconds ns_late) { |
| 80 | UpdateControllers(userdata, ns_late); | 81 | UpdateControllers(userdata, ns_late); |
| 81 | }); | 82 | }); |
| 82 | 83 | ||
| 83 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) | 84 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) |
| 84 | 85 | ||
| 85 | system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event); | 86 | system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); |
| 86 | 87 | ||
| 87 | ReloadInputDevices(); | 88 | ReloadInputDevices(); |
| 88 | } | 89 | } |
| @@ -107,7 +108,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 107 | rb.PushCopyObjects(shared_mem); | 108 | rb.PushCopyObjects(shared_mem); |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { | 111 | void IAppletResource::UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late) { |
| 111 | auto& core_timing = system.CoreTiming(); | 112 | auto& core_timing = system.CoreTiming(); |
| 112 | 113 | ||
| 113 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | 114 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); |
| @@ -118,7 +119,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { | |||
| 118 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); | 119 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event); | 122 | core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); |
| 122 | } | 123 | } |
| 123 | 124 | ||
| 124 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 125 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 6fb048360..c6f0a2584 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -4,10 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/hid/controllers/controller_base.h" | 7 | #include <chrono> |
| 8 | #include "core/hle/service/service.h" | ||
| 9 | 8 | ||
| 10 | #include "controllers/controller_base.h" | 9 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 11 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 12 | 11 | ||
| 13 | namespace Core::Timing { | 12 | namespace Core::Timing { |
| @@ -65,7 +64,7 @@ private: | |||
| 65 | } | 64 | } |
| 66 | 65 | ||
| 67 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); | 66 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); |
| 68 | void UpdateControllers(u64 userdata, s64 cycles_late); | 67 | void UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late); |
| 69 | 68 | ||
| 70 | std::shared_ptr<Kernel::SharedMemory> shared_mem; | 69 | std::shared_ptr<Kernel::SharedMemory> shared_mem; |
| 71 | 70 | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 2f44d3779..789856118 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -28,8 +28,7 @@ | |||
| 28 | 28 | ||
| 29 | namespace Service::NVFlinger { | 29 | namespace Service::NVFlinger { |
| 30 | 30 | ||
| 31 | constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60); | 31 | constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; |
| 32 | constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30); | ||
| 33 | 32 | ||
| 34 | void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { | 33 | void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { |
| 35 | nv_flinger.SplitVSync(); | 34 | nv_flinger.SplitVSync(); |
| @@ -67,20 +66,24 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { | |||
| 67 | guard = std::make_shared<std::mutex>(); | 66 | guard = std::make_shared<std::mutex>(); |
| 68 | 67 | ||
| 69 | // Schedule the screen composition events | 68 | // Schedule the screen composition events |
| 70 | composition_event = | 69 | composition_event = Core::Timing::CreateEvent( |
| 71 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { | 70 | "ScreenComposition", [this](u64, std::chrono::nanoseconds ns_late) { |
| 72 | Lock(); | 71 | Lock(); |
| 73 | Compose(); | 72 | Compose(); |
| 74 | const auto ticks = GetNextTicks(); | 73 | |
| 75 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late), | 74 | const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; |
| 76 | composition_event); | 75 | const auto ticks_delta = ticks - ns_late; |
| 76 | const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); | ||
| 77 | |||
| 78 | this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); | ||
| 77 | }); | 79 | }); |
| 80 | |||
| 78 | if (system.IsMulticore()) { | 81 | if (system.IsMulticore()) { |
| 79 | is_running = true; | 82 | is_running = true; |
| 80 | wait_event = std::make_unique<Common::Event>(); | 83 | wait_event = std::make_unique<Common::Event>(); |
| 81 | vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); | 84 | vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); |
| 82 | } else { | 85 | } else { |
| 83 | system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); | 86 | system.CoreTiming().ScheduleEvent(frame_ns, composition_event); |
| 84 | } | 87 | } |
| 85 | } | 88 | } |
| 86 | 89 | ||
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 53d27859b..ced41b1fe 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace Core::Memory { | 21 | namespace Core::Memory { |
| 22 | 22 | ||
| 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12); | 23 | constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; |
| 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 25 | 25 | ||
| 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) | 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) |
| @@ -188,10 +188,12 @@ CheatEngine::~CheatEngine() { | |||
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | void CheatEngine::Initialize() { | 190 | void CheatEngine::Initialize() { |
| 191 | event = Core::Timing::CreateEvent( | 191 | event = Core::Timing::CreateEvent("CheatEngine::FrameCallback::" + |
| 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | 192 | Common::HexToString(metadata.main_nso_build_id), |
| 193 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); | 193 | [this](u64 userdata, std::chrono::nanoseconds ns_late) { |
| 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | 194 | FrameCallback(userdata, ns_late); |
| 195 | }); | ||
| 196 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); | ||
| 195 | 197 | ||
| 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | 198 | metadata.process_id = system.CurrentProcess()->GetProcessID(); |
| 197 | metadata.title_id = system.CurrentProcess()->GetTitleID(); | 199 | metadata.title_id = system.CurrentProcess()->GetTitleID(); |
| @@ -217,7 +219,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | |||
| 217 | 219 | ||
| 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | 220 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); |
| 219 | 221 | ||
| 220 | void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { | 222 | void CheatEngine::FrameCallback(u64, std::chrono::nanoseconds ns_late) { |
| 221 | if (is_pending_reload.exchange(false)) { | 223 | if (is_pending_reload.exchange(false)) { |
| 222 | vm.LoadProgram(cheats); | 224 | vm.LoadProgram(cheats); |
| 223 | } | 225 | } |
| @@ -230,7 +232,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { | |||
| 230 | 232 | ||
| 231 | vm.Execute(metadata); | 233 | vm.Execute(metadata); |
| 232 | 234 | ||
| 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event); | 235 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event); |
| 234 | } | 236 | } |
| 235 | 237 | ||
| 236 | } // namespace Core::Memory | 238 | } // namespace Core::Memory |
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index 2649423f8..d4068cf84 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <chrono> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| @@ -71,7 +72,7 @@ public: | |||
| 71 | void Reload(std::vector<CheatEntry> cheats); | 72 | void Reload(std::vector<CheatEntry> cheats); |
| 72 | 73 | ||
| 73 | private: | 74 | private: |
| 74 | void FrameCallback(u64 userdata, s64 cycles_late); | 75 | void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); |
| 75 | 76 | ||
| 76 | DmntCheatVm vm; | 77 | DmntCheatVm vm; |
| 77 | CheatProcessMetadata metadata; | 78 | CheatProcessMetadata metadata; |
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 8b0c50d11..27b894b51 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | namespace Tools { | 14 | namespace Tools { |
| 15 | namespace { | 15 | namespace { |
| 16 | 16 | ||
| 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60); | 17 | constexpr auto memory_freezer_ns = std::chrono::nanoseconds{1000000000 / 60}; |
| 18 | 18 | ||
| 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { | 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { |
| 20 | switch (width) { | 20 | switch (width) { |
| @@ -55,10 +55,11 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v | |||
| 55 | 55 | ||
| 56 | Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) | 56 | Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) |
| 57 | : core_timing{core_timing_}, memory{memory_} { | 57 | : core_timing{core_timing_}, memory{memory_} { |
| 58 | event = Core::Timing::CreateEvent( | 58 | event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback", |
| 59 | "MemoryFreezer::FrameCallback", | 59 | [this](u64 userdata, std::chrono::nanoseconds ns_late) { |
| 60 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); | 60 | FrameCallback(userdata, ns_late); |
| 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | 61 | }); |
| 62 | core_timing.ScheduleEvent(memory_freezer_ns, event); | ||
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | Freezer::~Freezer() { | 65 | Freezer::~Freezer() { |
| @@ -68,7 +69,7 @@ Freezer::~Freezer() { | |||
| 68 | void Freezer::SetActive(bool active) { | 69 | void Freezer::SetActive(bool active) { |
| 69 | if (!this->active.exchange(active)) { | 70 | if (!this->active.exchange(active)) { |
| 70 | FillEntryReads(); | 71 | FillEntryReads(); |
| 71 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | 72 | core_timing.ScheduleEvent(memory_freezer_ns, event); |
| 72 | LOG_DEBUG(Common_Memory, "Memory freezer activated!"); | 73 | LOG_DEBUG(Common_Memory, "Memory freezer activated!"); |
| 73 | } else { | 74 | } else { |
| 74 | LOG_DEBUG(Common_Memory, "Memory freezer deactivated!"); | 75 | LOG_DEBUG(Common_Memory, "Memory freezer deactivated!"); |
| @@ -158,7 +159,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const { | |||
| 158 | return entries; | 159 | return entries; |
| 159 | } | 160 | } |
| 160 | 161 | ||
| 161 | void Freezer::FrameCallback(u64 userdata, s64 ns_late) { | 162 | void Freezer::FrameCallback(u64, std::chrono::nanoseconds ns_late) { |
| 162 | if (!IsActive()) { | 163 | if (!IsActive()) { |
| 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); | 164 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); |
| 164 | return; | 165 | return; |
| @@ -173,7 +174,7 @@ void Freezer::FrameCallback(u64 userdata, s64 ns_late) { | |||
| 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); | 174 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); |
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event); | 177 | core_timing.ScheduleEvent(memory_freezer_ns - ns_late, event); |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | void Freezer::FillEntryReads() { | 180 | void Freezer::FillEntryReads() { |
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 62fc6aa6c..8438783d5 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <chrono> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <mutex> | 10 | #include <mutex> |
| 10 | #include <optional> | 11 | #include <optional> |
| @@ -72,7 +73,7 @@ public: | |||
| 72 | std::vector<Entry> GetEntries() const; | 73 | std::vector<Entry> GetEntries() const; |
| 73 | 74 | ||
| 74 | private: | 75 | private: |
| 75 | void FrameCallback(u64 userdata, s64 cycles_late); | 76 | void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); |
| 76 | void FillEntryReads(); | 77 | void FillEntryReads(); |
| 77 | 78 | ||
| 78 | std::atomic_bool active{false}; | 79 | std::atomic_bool active{false}; |
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index e66db1940..244463a47 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <bitset> | 8 | #include <bitset> |
| 9 | #include <chrono> | ||
| 9 | #include <cstdlib> | 10 | #include <cstdlib> |
| 10 | #include <memory> | 11 | #include <memory> |
| 11 | #include <string> | 12 | #include <string> |
| @@ -17,7 +18,6 @@ | |||
| 17 | namespace { | 18 | namespace { |
| 18 | // Numbers are chosen randomly to make sure the correct one is given. | 19 | // Numbers are chosen randomly to make sure the correct one is given. |
| 19 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; | 20 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; |
| 20 | constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals | ||
| 21 | constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}}; | 21 | constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}}; |
| 22 | std::array<s64, 5> delays{}; | 22 | std::array<s64, 5> delays{}; |
| 23 | 23 | ||
| @@ -25,12 +25,12 @@ std::bitset<CB_IDS.size()> callbacks_ran_flags; | |||
| 25 | u64 expected_callback = 0; | 25 | u64 expected_callback = 0; |
| 26 | 26 | ||
| 27 | template <unsigned int IDX> | 27 | template <unsigned int IDX> |
| 28 | void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { | 28 | void HostCallbackTemplate(u64 userdata, std::chrono::nanoseconds ns_late) { |
| 29 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); | 29 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); |
| 30 | callbacks_ran_flags.set(IDX); | 30 | callbacks_ran_flags.set(IDX); |
| 31 | REQUIRE(CB_IDS[IDX] == userdata); | 31 | REQUIRE(CB_IDS[IDX] == userdata); |
| 32 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); | 32 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); |
| 33 | delays[IDX] = nanoseconds_late; | 33 | delays[IDX] = ns_late.count(); |
| 34 | ++expected_callback; | 34 | ++expected_callback; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| @@ -77,10 +77,12 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | |||
| 77 | 77 | ||
| 78 | core_timing.SyncPause(true); | 78 | core_timing.SyncPause(true); |
| 79 | 79 | ||
| 80 | u64 one_micro = 1000U; | 80 | const u64 one_micro = 1000U; |
| 81 | for (std::size_t i = 0; i < events.size(); i++) { | 81 | for (std::size_t i = 0; i < events.size(); i++) { |
| 82 | u64 order = calls_order[i]; | 82 | const u64 order = calls_order[i]; |
| 83 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | 83 | const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)}; |
| 84 | |||
| 85 | core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); | ||
| 84 | } | 86 | } |
| 85 | /// test pause | 87 | /// test pause |
| 86 | REQUIRE(callbacks_ran_flags.none()); | 88 | REQUIRE(callbacks_ran_flags.none()); |
| @@ -116,13 +118,16 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") { | |||
| 116 | 118 | ||
| 117 | expected_callback = 0; | 119 | expected_callback = 0; |
| 118 | 120 | ||
| 119 | u64 start = core_timing.GetGlobalTimeNs().count(); | 121 | const u64 start = core_timing.GetGlobalTimeNs().count(); |
| 120 | u64 one_micro = 1000U; | 122 | const u64 one_micro = 1000U; |
| 123 | |||
| 121 | for (std::size_t i = 0; i < events.size(); i++) { | 124 | for (std::size_t i = 0; i < events.size(); i++) { |
| 122 | u64 order = calls_order[i]; | 125 | const u64 order = calls_order[i]; |
| 123 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | 126 | const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)}; |
| 127 | core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); | ||
| 124 | } | 128 | } |
| 125 | u64 end = core_timing.GetGlobalTimeNs().count(); | 129 | |
| 130 | const u64 end = core_timing.GetGlobalTimeNs().count(); | ||
| 126 | const double scheduling_time = static_cast<double>(end - start); | 131 | const double scheduling_time = static_cast<double>(end - start); |
| 127 | const double timer_time = static_cast<double>(TestTimerSpeed(core_timing)); | 132 | const double timer_time = static_cast<double>(TestTimerSpeed(core_timing)); |
| 128 | 133 | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index a862b2610..656096c9f 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -133,11 +133,44 @@ file(GLOB COMPAT_LIST | |||
| 133 | file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) | 133 | file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) |
| 134 | file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) | 134 | file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) |
| 135 | 135 | ||
| 136 | if (ENABLE_QT_TRANSLATION) | ||
| 137 | set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") | ||
| 138 | option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) | ||
| 139 | |||
| 140 | # Update source TS file if enabled | ||
| 141 | if (GENERATE_QT_TRANSLATION) | ||
| 142 | get_target_property(SRCS yuzu SOURCES) | ||
| 143 | qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts) | ||
| 144 | add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts) | ||
| 145 | endif() | ||
| 146 | |||
| 147 | # Find all TS files except en.ts | ||
| 148 | file(GLOB_RECURSE LANGUAGES_TS ${YUZU_QT_LANGUAGES}/*.ts) | ||
| 149 | list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) | ||
| 150 | |||
| 151 | # Compile TS files to QM files | ||
| 152 | qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) | ||
| 153 | |||
| 154 | # Build a QRC file from the QM file list | ||
| 155 | set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) | ||
| 156 | file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n") | ||
| 157 | foreach (QM ${LANGUAGES_QM}) | ||
| 158 | get_filename_component(QM_FILE ${QM} NAME) | ||
| 159 | file(APPEND ${LANGUAGES_QRC} "<file>${QM_FILE}</file>\n") | ||
| 160 | endforeach (QM) | ||
| 161 | file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") | ||
| 162 | |||
| 163 | # Add the QRC file to package in all QM files | ||
| 164 | qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) | ||
| 165 | else() | ||
| 166 | set(LANGUAGES) | ||
| 167 | endif() | ||
| 136 | 168 | ||
| 137 | target_sources(yuzu | 169 | target_sources(yuzu |
| 138 | PRIVATE | 170 | PRIVATE |
| 139 | ${COMPAT_LIST} | 171 | ${COMPAT_LIST} |
| 140 | ${ICONS} | 172 | ${ICONS} |
| 173 | ${LANGUAGES} | ||
| 141 | ${THEMES} | 174 | ${THEMES} |
| 142 | ) | 175 | ) |
| 143 | 176 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 805bb954b..59a193edd 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -611,6 +611,7 @@ void Config::ReadPathValues() { | |||
| 611 | } | 611 | } |
| 612 | } | 612 | } |
| 613 | UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); | 613 | UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); |
| 614 | UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); | ||
| 614 | 615 | ||
| 615 | qt_config->endGroup(); | 616 | qt_config->endGroup(); |
| 616 | } | 617 | } |
| @@ -1095,6 +1096,7 @@ void Config::SavePathValues() { | |||
| 1095 | } | 1096 | } |
| 1096 | qt_config->endArray(); | 1097 | qt_config->endArray(); |
| 1097 | WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); | 1098 | WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); |
| 1099 | WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); | ||
| 1098 | 1100 | ||
| 1099 | qt_config->endGroup(); | 1101 | qt_config->endGroup(); |
| 1100 | } | 1102 | } |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index a5afb354f..4e30dc51e 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -23,6 +23,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) | |||
| 23 | SetConfiguration(); | 23 | SetConfiguration(); |
| 24 | PopulateSelectionList(); | 24 | PopulateSelectionList(); |
| 25 | 25 | ||
| 26 | connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); | ||
| 26 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, | 27 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, |
| 27 | &ConfigureDialog::UpdateVisibleTabs); | 28 | &ConfigureDialog::UpdateVisibleTabs); |
| 28 | 29 | ||
| @@ -98,6 +99,14 @@ void ConfigureDialog::PopulateSelectionList() { | |||
| 98 | } | 99 | } |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 102 | void ConfigureDialog::OnLanguageChanged(const QString& locale) { | ||
| 103 | emit LanguageChanged(locale); | ||
| 104 | // first apply the configuration, and then restore the display | ||
| 105 | ApplyConfiguration(); | ||
| 106 | RetranslateUI(); | ||
| 107 | SetConfiguration(); | ||
| 108 | } | ||
| 109 | |||
| 101 | void ConfigureDialog::UpdateVisibleTabs() { | 110 | void ConfigureDialog::UpdateVisibleTabs() { |
| 102 | const auto items = ui->selectorList->selectedItems(); | 111 | const auto items = ui->selectorList->selectedItems(); |
| 103 | if (items.isEmpty()) { | 112 | if (items.isEmpty()) { |
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 2d3bfc2da..4289bc225 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h | |||
| @@ -22,6 +22,12 @@ public: | |||
| 22 | 22 | ||
| 23 | void ApplyConfiguration(); | 23 | void ApplyConfiguration(); |
| 24 | 24 | ||
| 25 | private slots: | ||
| 26 | void OnLanguageChanged(const QString& locale); | ||
| 27 | |||
| 28 | signals: | ||
| 29 | void LanguageChanged(const QString& locale); | ||
| 30 | |||
| 25 | private: | 31 | private: |
| 26 | void changeEvent(QEvent* event) override; | 32 | void changeEvent(QEvent* event) override; |
| 27 | 33 | ||
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 94424ee44..24b6c5b72 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <utility> | 6 | #include <utility> |
| 7 | 7 | ||
| 8 | #include <QDirIterator> | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| 10 | #include "ui_configure_ui.h" | 11 | #include "ui_configure_ui.h" |
| @@ -29,6 +30,8 @@ constexpr std::array row_text_names{ | |||
| 29 | ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) { | 30 | ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) { |
| 30 | ui->setupUi(this); | 31 | ui->setupUi(this); |
| 31 | 32 | ||
| 33 | InitializeLanguageComboBox(); | ||
| 34 | |||
| 32 | for (const auto& theme : UISettings::themes) { | 35 | for (const auto& theme : UISettings::themes) { |
| 33 | ui->theme_combobox->addItem(QString::fromUtf8(theme.first), | 36 | ui->theme_combobox->addItem(QString::fromUtf8(theme.first), |
| 34 | QString::fromUtf8(theme.second)); | 37 | QString::fromUtf8(theme.second)); |
| @@ -72,6 +75,8 @@ void ConfigureUi::RequestGameListUpdate() { | |||
| 72 | 75 | ||
| 73 | void ConfigureUi::SetConfiguration() { | 76 | void ConfigureUi::SetConfiguration() { |
| 74 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 77 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 78 | ui->language_combobox->setCurrentIndex( | ||
| 79 | ui->language_combobox->findData(UISettings::values.language)); | ||
| 75 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); | 80 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); |
| 76 | ui->icon_size_combobox->setCurrentIndex( | 81 | ui->icon_size_combobox->setCurrentIndex( |
| 77 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); | 82 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); |
| @@ -100,6 +105,25 @@ void ConfigureUi::RetranslateUI() { | |||
| 100 | } | 105 | } |
| 101 | } | 106 | } |
| 102 | 107 | ||
| 108 | void ConfigureUi::InitializeLanguageComboBox() { | ||
| 109 | ui->language_combobox->addItem(tr("<System>"), QString{}); | ||
| 110 | ui->language_combobox->addItem(tr("English"), QStringLiteral("en")); | ||
| 111 | QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags); | ||
| 112 | while (it.hasNext()) { | ||
| 113 | QString locale = it.next(); | ||
| 114 | locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); | ||
| 115 | locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1); | ||
| 116 | const QString lang = QLocale::languageToString(QLocale(locale).language()); | ||
| 117 | ui->language_combobox->addItem(lang, locale); | ||
| 118 | } | ||
| 119 | |||
| 120 | // Unlike other configuration changes, interface language changes need to be reflected on the | ||
| 121 | // interface immediately. This is done by passing a signal to the main window, and then | ||
| 122 | // retranslating when passing back. | ||
| 123 | connect(ui->language_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | ||
| 124 | &ConfigureUi::OnLanguageChanged); | ||
| 125 | } | ||
| 126 | |||
| 103 | void ConfigureUi::InitializeIconSizeComboBox() { | 127 | void ConfigureUi::InitializeIconSizeComboBox() { |
| 104 | for (const auto& size : default_icon_sizes) { | 128 | for (const auto& size : default_icon_sizes) { |
| 105 | ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); | 129 | ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); |
| @@ -147,3 +171,10 @@ void ConfigureUi::UpdateSecondRowComboBox(bool init) { | |||
| 147 | ui->row_2_text_combobox->removeItem( | 171 | ui->row_2_text_combobox->removeItem( |
| 148 | ui->row_2_text_combobox->findData(ui->row_1_text_combobox->currentData())); | 172 | ui->row_2_text_combobox->findData(ui->row_1_text_combobox->currentData())); |
| 149 | } | 173 | } |
| 174 | |||
| 175 | void ConfigureUi::OnLanguageChanged(int index) { | ||
| 176 | if (index == -1) | ||
| 177 | return; | ||
| 178 | |||
| 179 | emit LanguageChanged(ui->language_combobox->itemData(index).toString()); | ||
| 180 | } | ||
diff --git a/src/yuzu/configuration/configure_ui.h b/src/yuzu/configuration/configure_ui.h index d471afe99..c30bcf6ff 100644 --- a/src/yuzu/configuration/configure_ui.h +++ b/src/yuzu/configuration/configure_ui.h | |||
| @@ -20,6 +20,12 @@ public: | |||
| 20 | 20 | ||
| 21 | void ApplyConfiguration(); | 21 | void ApplyConfiguration(); |
| 22 | 22 | ||
| 23 | private slots: | ||
| 24 | void OnLanguageChanged(int index); | ||
| 25 | |||
| 26 | signals: | ||
| 27 | void LanguageChanged(const QString& locale); | ||
| 28 | |||
| 23 | private: | 29 | private: |
| 24 | void RequestGameListUpdate(); | 30 | void RequestGameListUpdate(); |
| 25 | 31 | ||
| @@ -28,6 +34,7 @@ private: | |||
| 28 | void changeEvent(QEvent*) override; | 34 | void changeEvent(QEvent*) override; |
| 29 | void RetranslateUI(); | 35 | void RetranslateUI(); |
| 30 | 36 | ||
| 37 | void InitializeLanguageComboBox(); | ||
| 31 | void InitializeIconSizeComboBox(); | 38 | void InitializeIconSizeComboBox(); |
| 32 | void InitializeRowComboBoxes(); | 39 | void InitializeRowComboBoxes(); |
| 33 | 40 | ||
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index bd5c5d3c2..0b81747d7 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui | |||
| @@ -13,112 +13,132 @@ | |||
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| 14 | <string>Form</string> | 14 | <string>Form</string> |
| 15 | </property> | 15 | </property> |
| 16 | <layout class="QHBoxLayout" name="HorizontalLayout"> | 16 | <layout class="QVBoxLayout" name="verticalLayout"> |
| 17 | <item> | 17 | <item> |
| 18 | <layout class="QVBoxLayout" name="VerticalLayout"> | 18 | <widget class="QGroupBox" name="general_groupBox"> |
| 19 | <item> | 19 | <property name="title"> |
| 20 | <widget class="QGroupBox" name="GeneralGroupBox"> | 20 | <string>General</string> |
| 21 | <property name="title"> | 21 | </property> |
| 22 | <string>General</string> | 22 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 23 | </property> | 23 | <item> |
| 24 | <layout class="QHBoxLayout" name="horizontalLayout"> | 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 25 | <item> | 25 | <item> |
| 26 | <layout class="QVBoxLayout" name="verticalLayout"> | 26 | <widget class="QLabel" name="label_change_language_info"> |
| 27 | <property name="text"> | ||
| 28 | <string>Note: Changing language will apply your configuration.</string> | ||
| 29 | </property> | ||
| 30 | <property name="wordWrap"> | ||
| 31 | <bool>true</bool> | ||
| 32 | </property> | ||
| 33 | </widget> | ||
| 34 | </item> | ||
| 35 | <item> | ||
| 36 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 37 | <item> | ||
| 38 | <widget class="QLabel" name="language_label"> | ||
| 39 | <property name="text"> | ||
| 40 | <string>Interface language:</string> | ||
| 41 | </property> | ||
| 42 | </widget> | ||
| 43 | </item> | ||
| 44 | <item> | ||
| 45 | <widget class="QComboBox" name="language_combobox"/> | ||
| 46 | </item> | ||
| 47 | </layout> | ||
| 48 | </item> | ||
| 49 | <item> | ||
| 50 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 51 | <item> | ||
| 52 | <widget class="QLabel" name="theme_label"> | ||
| 53 | <property name="text"> | ||
| 54 | <string>Theme:</string> | ||
| 55 | </property> | ||
| 56 | </widget> | ||
| 57 | </item> | ||
| 27 | <item> | 58 | <item> |
| 28 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | 59 | <widget class="QComboBox" name="theme_combobox"/> |
| 29 | <item> | ||
| 30 | <widget class="QLabel" name="theme_label"> | ||
| 31 | <property name="text"> | ||
| 32 | <string>Theme:</string> | ||
| 33 | </property> | ||
| 34 | </widget> | ||
| 35 | </item> | ||
| 36 | <item> | ||
| 37 | <widget class="QComboBox" name="theme_combobox"/> | ||
| 38 | </item> | ||
| 39 | </layout> | ||
| 40 | </item> | 60 | </item> |
| 41 | </layout> | 61 | </layout> |
| 42 | </item> | 62 | </item> |
| 43 | </layout> | 63 | </layout> |
| 44 | </widget> | 64 | </item> |
| 45 | </item> | 65 | </layout> |
| 46 | <item> | 66 | </widget> |
| 47 | <widget class="QGroupBox" name="GameListGroupBox"> | 67 | </item> |
| 48 | <property name="title"> | 68 | <item> |
| 49 | <string>Game List</string> | 69 | <widget class="QGroupBox" name="GameListGroupBox"> |
| 50 | </property> | 70 | <property name="title"> |
| 51 | <layout class="QHBoxLayout" name="GameListHorizontalLayout"> | 71 | <string>Game List</string> |
| 72 | </property> | ||
| 73 | <layout class="QHBoxLayout" name="GameListHorizontalLayout"> | ||
| 74 | <item> | ||
| 75 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | ||
| 52 | <item> | 76 | <item> |
| 53 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | 77 | <widget class="QCheckBox" name="show_add_ons"> |
| 78 | <property name="text"> | ||
| 79 | <string>Show Add-Ons Column</string> | ||
| 80 | </property> | ||
| 81 | </widget> | ||
| 82 | </item> | ||
| 83 | <item> | ||
| 84 | <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> | ||
| 54 | <item> | 85 | <item> |
| 55 | <widget class="QCheckBox" name="show_add_ons"> | 86 | <widget class="QLabel" name="icon_size_label"> |
| 56 | <property name="text"> | 87 | <property name="text"> |
| 57 | <string>Show Add-Ons Column</string> | 88 | <string>Icon Size:</string> |
| 58 | </property> | 89 | </property> |
| 59 | </widget> | 90 | </widget> |
| 60 | </item> | 91 | </item> |
| 61 | <item> | 92 | <item> |
| 62 | <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> | 93 | <widget class="QComboBox" name="icon_size_combobox"/> |
| 63 | <item> | ||
| 64 | <widget class="QLabel" name="icon_size_label"> | ||
| 65 | <property name="text"> | ||
| 66 | <string>Icon Size:</string> | ||
| 67 | </property> | ||
| 68 | </widget> | ||
| 69 | </item> | ||
| 70 | <item> | ||
| 71 | <widget class="QComboBox" name="icon_size_combobox"/> | ||
| 72 | </item> | ||
| 73 | </layout> | ||
| 74 | </item> | 94 | </item> |
| 95 | </layout> | ||
| 96 | </item> | ||
| 97 | <item> | ||
| 98 | <layout class="QHBoxLayout" name="row_1_qhbox_layout"> | ||
| 75 | <item> | 99 | <item> |
| 76 | <layout class="QHBoxLayout" name="row_1_qhbox_layout"> | 100 | <widget class="QLabel" name="row_1_label"> |
| 77 | <item> | 101 | <property name="text"> |
| 78 | <widget class="QLabel" name="row_1_label"> | 102 | <string>Row 1 Text:</string> |
| 79 | <property name="text"> | 103 | </property> |
| 80 | <string>Row 1 Text:</string> | 104 | </widget> |
| 81 | </property> | ||
| 82 | </widget> | ||
| 83 | </item> | ||
| 84 | <item> | ||
| 85 | <widget class="QComboBox" name="row_1_text_combobox"/> | ||
| 86 | </item> | ||
| 87 | </layout> | ||
| 88 | </item> | 105 | </item> |
| 89 | <item> | 106 | <item> |
| 90 | <layout class="QHBoxLayout" name="row_2_qhbox_layout"> | 107 | <widget class="QComboBox" name="row_1_text_combobox"/> |
| 91 | <item> | 108 | </item> |
| 92 | <widget class="QLabel" name="row_2_label"> | 109 | </layout> |
| 93 | <property name="text"> | 110 | </item> |
| 94 | <string>Row 2 Text:</string> | 111 | <item> |
| 95 | </property> | 112 | <layout class="QHBoxLayout" name="row_2_qhbox_layout"> |
| 96 | </widget> | 113 | <item> |
| 97 | </item> | 114 | <widget class="QLabel" name="row_2_label"> |
| 98 | <item> | 115 | <property name="text"> |
| 99 | <widget class="QComboBox" name="row_2_text_combobox"/> | 116 | <string>Row 2 Text:</string> |
| 100 | </item> | 117 | </property> |
| 101 | </layout> | 118 | </widget> |
| 119 | </item> | ||
| 120 | <item> | ||
| 121 | <widget class="QComboBox" name="row_2_text_combobox"/> | ||
| 102 | </item> | 122 | </item> |
| 103 | </layout> | 123 | </layout> |
| 104 | </item> | 124 | </item> |
| 105 | </layout> | 125 | </layout> |
| 106 | </widget> | 126 | </item> |
| 107 | </item> | 127 | </layout> |
| 108 | <item> | 128 | </widget> |
| 109 | <spacer name="verticalSpacer"> | 129 | </item> |
| 110 | <property name="orientation"> | 130 | <item> |
| 111 | <enum>Qt::Vertical</enum> | 131 | <spacer name="verticalSpacer"> |
| 112 | </property> | 132 | <property name="orientation"> |
| 113 | <property name="sizeHint" stdset="0"> | 133 | <enum>Qt::Vertical</enum> |
| 114 | <size> | 134 | </property> |
| 115 | <width>20</width> | 135 | <property name="sizeHint" stdset="0"> |
| 116 | <height>40</height> | 136 | <size> |
| 117 | </size> | 137 | <width>20</width> |
| 118 | </property> | 138 | <height>40</height> |
| 119 | </spacer> | 139 | </size> |
| 120 | </item> | 140 | </property> |
| 121 | </layout> | 141 | </spacer> |
| 122 | </item> | 142 | </item> |
| 123 | </layout> | 143 | </layout> |
| 124 | </widget> | 144 | </widget> |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 9bb0a0109..f391a41a9 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -2,9 +2,11 @@ | |||
| 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 <array> | ||
| 5 | #include <fmt/format.h> | 6 | #include <fmt/format.h> |
| 6 | 7 | ||
| 7 | #include "yuzu/debugger/wait_tree.h" | 8 | #include "yuzu/debugger/wait_tree.h" |
| 9 | #include "yuzu/uisettings.h" | ||
| 8 | #include "yuzu/util/util.h" | 10 | #include "yuzu/util/util.h" |
| 9 | 11 | ||
| 10 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| @@ -19,11 +21,37 @@ | |||
| 19 | #include "core/hle/kernel/thread.h" | 21 | #include "core/hle/kernel/thread.h" |
| 20 | #include "core/memory.h" | 22 | #include "core/memory.h" |
| 21 | 23 | ||
| 24 | namespace { | ||
| 25 | |||
| 26 | constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{ | ||
| 27 | {Qt::GlobalColor::darkGreen, Qt::GlobalColor::green}, | ||
| 28 | {Qt::GlobalColor::darkGreen, Qt::GlobalColor::green}, | ||
| 29 | {Qt::GlobalColor::darkBlue, Qt::GlobalColor::cyan}, | ||
| 30 | {Qt::GlobalColor::lightGray, Qt::GlobalColor::lightGray}, | ||
| 31 | {Qt::GlobalColor::lightGray, Qt::GlobalColor::lightGray}, | ||
| 32 | {Qt::GlobalColor::darkRed, Qt::GlobalColor::red}, | ||
| 33 | {Qt::GlobalColor::darkYellow, Qt::GlobalColor::yellow}, | ||
| 34 | {Qt::GlobalColor::red, Qt::GlobalColor::red}, | ||
| 35 | {Qt::GlobalColor::darkCyan, Qt::GlobalColor::cyan}, | ||
| 36 | {Qt::GlobalColor::gray, Qt::GlobalColor::gray}, | ||
| 37 | }}; | ||
| 38 | |||
| 39 | bool IsDarkTheme() { | ||
| 40 | const auto& theme = UISettings::values.theme; | ||
| 41 | return theme == QStringLiteral("qdarkstyle") || theme == QStringLiteral("colorful_dark"); | ||
| 42 | } | ||
| 43 | |||
| 44 | } // namespace | ||
| 45 | |||
| 22 | WaitTreeItem::WaitTreeItem() = default; | 46 | WaitTreeItem::WaitTreeItem() = default; |
| 23 | WaitTreeItem::~WaitTreeItem() = default; | 47 | WaitTreeItem::~WaitTreeItem() = default; |
| 24 | 48 | ||
| 25 | QColor WaitTreeItem::GetColor() const { | 49 | QColor WaitTreeItem::GetColor() const { |
| 26 | return QColor(Qt::GlobalColor::black); | 50 | if (IsDarkTheme()) { |
| 51 | return QColor(Qt::GlobalColor::white); | ||
| 52 | } else { | ||
| 53 | return QColor(Qt::GlobalColor::black); | ||
| 54 | } | ||
| 27 | } | 55 | } |
| 28 | 56 | ||
| 29 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeItem::GetChildren() const { | 57 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeItem::GetChildren() const { |
| @@ -263,36 +291,38 @@ QString WaitTreeThread::GetText() const { | |||
| 263 | } | 291 | } |
| 264 | 292 | ||
| 265 | QColor WaitTreeThread::GetColor() const { | 293 | QColor WaitTreeThread::GetColor() const { |
| 294 | const std::size_t color_index = IsDarkTheme() ? 1 : 0; | ||
| 295 | |||
| 266 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 296 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 267 | switch (thread.GetStatus()) { | 297 | switch (thread.GetStatus()) { |
| 268 | case Kernel::ThreadStatus::Running: | 298 | case Kernel::ThreadStatus::Running: |
| 269 | return QColor(Qt::GlobalColor::darkGreen); | 299 | return QColor(WaitTreeColors[0][color_index]); |
| 270 | case Kernel::ThreadStatus::Ready: | 300 | case Kernel::ThreadStatus::Ready: |
| 271 | if (!thread.IsPaused()) { | 301 | if (!thread.IsPaused()) { |
| 272 | if (thread.WasRunning()) { | 302 | if (thread.WasRunning()) { |
| 273 | return QColor(Qt::GlobalColor::darkGreen); | 303 | return QColor(WaitTreeColors[1][color_index]); |
| 274 | } else { | 304 | } else { |
| 275 | return QColor(Qt::GlobalColor::darkBlue); | 305 | return QColor(WaitTreeColors[2][color_index]); |
| 276 | } | 306 | } |
| 277 | } else { | 307 | } else { |
| 278 | return QColor(Qt::GlobalColor::lightGray); | 308 | return QColor(WaitTreeColors[3][color_index]); |
| 279 | } | 309 | } |
| 280 | case Kernel::ThreadStatus::Paused: | 310 | case Kernel::ThreadStatus::Paused: |
| 281 | return QColor(Qt::GlobalColor::lightGray); | 311 | return QColor(WaitTreeColors[4][color_index]); |
| 282 | case Kernel::ThreadStatus::WaitHLEEvent: | 312 | case Kernel::ThreadStatus::WaitHLEEvent: |
| 283 | case Kernel::ThreadStatus::WaitIPC: | 313 | case Kernel::ThreadStatus::WaitIPC: |
| 284 | return QColor(Qt::GlobalColor::darkRed); | 314 | return QColor(WaitTreeColors[5][color_index]); |
| 285 | case Kernel::ThreadStatus::WaitSleep: | 315 | case Kernel::ThreadStatus::WaitSleep: |
| 286 | return QColor(Qt::GlobalColor::darkYellow); | 316 | return QColor(WaitTreeColors[6][color_index]); |
| 287 | case Kernel::ThreadStatus::WaitSynch: | 317 | case Kernel::ThreadStatus::WaitSynch: |
| 288 | case Kernel::ThreadStatus::WaitMutex: | 318 | case Kernel::ThreadStatus::WaitMutex: |
| 289 | case Kernel::ThreadStatus::WaitCondVar: | 319 | case Kernel::ThreadStatus::WaitCondVar: |
| 290 | case Kernel::ThreadStatus::WaitArb: | 320 | case Kernel::ThreadStatus::WaitArb: |
| 291 | return QColor(Qt::GlobalColor::red); | 321 | return QColor(WaitTreeColors[7][color_index]); |
| 292 | case Kernel::ThreadStatus::Dormant: | 322 | case Kernel::ThreadStatus::Dormant: |
| 293 | return QColor(Qt::GlobalColor::darkCyan); | 323 | return QColor(WaitTreeColors[8][color_index]); |
| 294 | case Kernel::ThreadStatus::Dead: | 324 | case Kernel::ThreadStatus::Dead: |
| 295 | return QColor(Qt::GlobalColor::gray); | 325 | return QColor(WaitTreeColors[9][color_index]); |
| 296 | default: | 326 | default: |
| 297 | return WaitTreeItem::GetColor(); | 327 | return WaitTreeItem::GetColor(); |
| 298 | } | 328 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6909d65d0..31a635176 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -191,6 +191,8 @@ GMainWindow::GMainWindow() | |||
| 191 | provider(std::make_unique<FileSys::ManualContentProvider>()) { | 191 | provider(std::make_unique<FileSys::ManualContentProvider>()) { |
| 192 | InitializeLogging(); | 192 | InitializeLogging(); |
| 193 | 193 | ||
| 194 | LoadTranslation(); | ||
| 195 | |||
| 194 | setAcceptDrops(true); | 196 | setAcceptDrops(true); |
| 195 | ui.setupUi(this); | 197 | ui.setupUi(this); |
| 196 | statusBar()->hide(); | 198 | statusBar()->hide(); |
| @@ -2048,6 +2050,9 @@ void GMainWindow::OnConfigure() { | |||
| 2048 | const bool old_discord_presence = UISettings::values.enable_discord_presence; | 2050 | const bool old_discord_presence = UISettings::values.enable_discord_presence; |
| 2049 | 2051 | ||
| 2050 | ConfigureDialog configure_dialog(this, hotkey_registry); | 2052 | ConfigureDialog configure_dialog(this, hotkey_registry); |
| 2053 | connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, | ||
| 2054 | &GMainWindow::OnLanguageChanged); | ||
| 2055 | |||
| 2051 | const auto result = configure_dialog.exec(); | 2056 | const auto result = configure_dialog.exec(); |
| 2052 | if (result != QDialog::Accepted) { | 2057 | if (result != QDialog::Accepted) { |
| 2053 | return; | 2058 | return; |
| @@ -2620,6 +2625,43 @@ void GMainWindow::UpdateUITheme() { | |||
| 2620 | QIcon::setThemeSearchPaths(theme_paths); | 2625 | QIcon::setThemeSearchPaths(theme_paths); |
| 2621 | } | 2626 | } |
| 2622 | 2627 | ||
| 2628 | void GMainWindow::LoadTranslation() { | ||
| 2629 | // If the selected language is English, no need to install any translation | ||
| 2630 | if (UISettings::values.language == QStringLiteral("en")) { | ||
| 2631 | return; | ||
| 2632 | } | ||
| 2633 | |||
| 2634 | bool loaded; | ||
| 2635 | |||
| 2636 | if (UISettings::values.language.isEmpty()) { | ||
| 2637 | // If the selected language is empty, use system locale | ||
| 2638 | loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); | ||
| 2639 | } else { | ||
| 2640 | // Otherwise load from the specified file | ||
| 2641 | loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | if (loaded) { | ||
| 2645 | qApp->installTranslator(&translator); | ||
| 2646 | } else { | ||
| 2647 | UISettings::values.language = QStringLiteral("en"); | ||
| 2648 | } | ||
| 2649 | } | ||
| 2650 | |||
| 2651 | void GMainWindow::OnLanguageChanged(const QString& locale) { | ||
| 2652 | if (UISettings::values.language != QStringLiteral("en")) { | ||
| 2653 | qApp->removeTranslator(&translator); | ||
| 2654 | } | ||
| 2655 | |||
| 2656 | UISettings::values.language = locale; | ||
| 2657 | LoadTranslation(); | ||
| 2658 | ui.retranslateUi(this); | ||
| 2659 | UpdateWindowTitle(); | ||
| 2660 | |||
| 2661 | if (emulation_running) | ||
| 2662 | ui.action_Start->setText(tr("Continue")); | ||
| 2663 | } | ||
| 2664 | |||
| 2623 | void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | 2665 | void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { |
| 2624 | #ifdef USE_DISCORD_PRESENCE | 2666 | #ifdef USE_DISCORD_PRESENCE |
| 2625 | if (state) { | 2667 | if (state) { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 59d9073ae..db573d606 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | #include <QMainWindow> | 11 | #include <QMainWindow> |
| 12 | #include <QTimer> | 12 | #include <QTimer> |
| 13 | #include <QTranslator> | ||
| 13 | 14 | ||
| 14 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 15 | #include "core/core.h" | 16 | #include "core/core.h" |
| @@ -225,6 +226,7 @@ private slots: | |||
| 225 | void OnCaptureScreenshot(); | 226 | void OnCaptureScreenshot(); |
| 226 | void OnCoreError(Core::System::ResultStatus, std::string); | 227 | void OnCoreError(Core::System::ResultStatus, std::string); |
| 227 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 228 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 229 | void OnLanguageChanged(const QString& locale); | ||
| 228 | 230 | ||
| 229 | private: | 231 | private: |
| 230 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 232 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| @@ -237,6 +239,7 @@ private: | |||
| 237 | void HideMouseCursor(); | 239 | void HideMouseCursor(); |
| 238 | void ShowMouseCursor(); | 240 | void ShowMouseCursor(); |
| 239 | void OpenURL(const QUrl& url); | 241 | void OpenURL(const QUrl& url); |
| 242 | void LoadTranslation(); | ||
| 240 | 243 | ||
| 241 | Ui::MainWindow ui; | 244 | Ui::MainWindow ui; |
| 242 | 245 | ||
| @@ -285,6 +288,8 @@ private: | |||
| 285 | 288 | ||
| 286 | HotkeyRegistry hotkey_registry; | 289 | HotkeyRegistry hotkey_registry; |
| 287 | 290 | ||
| 291 | QTranslator translator; | ||
| 292 | |||
| 288 | // Install progress dialog | 293 | // Install progress dialog |
| 289 | QProgressDialog* install_progress; | 294 | QProgressDialog* install_progress; |
| 290 | 295 | ||
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 830932d45..6cc65736d 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -75,6 +75,7 @@ struct Values { | |||
| 75 | bool game_dir_deprecated_deepscan; | 75 | bool game_dir_deprecated_deepscan; |
| 76 | QVector<UISettings::GameDir> game_dirs; | 76 | QVector<UISettings::GameDir> game_dirs; |
| 77 | QStringList recent_files; | 77 | QStringList recent_files; |
| 78 | QString language; | ||
| 78 | 79 | ||
| 79 | QString theme; | 80 | QString theme; |
| 80 | 81 | ||