From 7ee76003adbc24bdc651710bcab9860efe3b8c22 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 24 Feb 2020 22:04:12 -0400 Subject: General: Recover Prometheus project from harddrive failure This commit: Implements CPU Interrupts, Replaces Cycle Timing for Host Timing, Reworks the Kernel's Scheduler, Introduce Idle State and Suspended State, Recreates the bootmanager, Initializes Multicore system. --- src/core/core_timing.cpp | 208 ++++++++++++++++++++++------------------------- 1 file changed, 96 insertions(+), 112 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 46d4178c43..a3ce69790a 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -1,5 +1,5 @@ -// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project -// Licensed under GPLv2+ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "core/core_timing.h" @@ -10,20 +10,16 @@ #include #include "common/assert.h" -#include "common/thread.h" #include "core/core_timing_util.h" -#include "core/hardware_properties.h" namespace Core::Timing { -constexpr int MAX_SLICE_LENGTH = 10000; - std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback) { return std::make_shared(std::move(callback), std::move(name)); } struct CoreTiming::Event { - s64 time; + u64 time; u64 fifo_order; u64 userdata; std::weak_ptr type; @@ -39,51 +35,74 @@ struct CoreTiming::Event { } }; -CoreTiming::CoreTiming() = default; -CoreTiming::~CoreTiming() = default; +CoreTiming::CoreTiming() { + clock = + Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); +} -void CoreTiming::Initialize() { - downcounts.fill(MAX_SLICE_LENGTH); - time_slice.fill(MAX_SLICE_LENGTH); - slice_length = MAX_SLICE_LENGTH; - global_timer = 0; - idled_cycles = 0; - current_context = 0; +CoreTiming::~CoreTiming() = default; - // The time between CoreTiming being initialized and the first call to Advance() is considered - // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before - // executing the first cycle of each slice to prepare the slice length and downcount for - // that slice. - is_global_timer_sane = true; +void CoreTiming::ThreadEntry(CoreTiming& instance) { + std::string name = "yuzu:HostTiming"; + Common::SetCurrentThreadName(name.c_str()); + instance.on_thread_init(); + instance.ThreadLoop(); +} +void CoreTiming::Initialize(std::function&& on_thread_init_) { + on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; - const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); + timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); } void CoreTiming::Shutdown() { + paused = true; + shutting_down = true; + event.Set(); + timer_thread->join(); ClearPendingEvents(); + timer_thread.reset(); + has_started = false; } -void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr& event_type, - u64 userdata) { - std::lock_guard guard{inner_mutex}; - const s64 timeout = GetTicks() + cycles_into_future; +void CoreTiming::Pause(bool is_paused) { + paused = is_paused; +} - // If this event needs to be scheduled before the next advance(), force one early - if (!is_global_timer_sane) { - ForceExceptionCheck(cycles_into_future); +void CoreTiming::SyncPause(bool is_paused) { + if (is_paused == paused && paused_set == paused) { + return; } + Pause(is_paused); + event.Set(); + while (paused_set != is_paused) + ; +} + +bool CoreTiming::IsRunning() const { + return !paused_set; +} + +bool CoreTiming::HasPendingEvents() const { + return !(wait_set && event_queue.empty()); +} + +void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, + u64 userdata) { + basic_lock.lock(); + const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + basic_lock.unlock(); + event.Set(); } void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { - std::lock_guard guard{inner_mutex}; - + basic_lock.lock(); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get() && e.userdata == userdata; }); @@ -93,23 +112,23 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } + basic_lock.unlock(); } -u64 CoreTiming::GetTicks() const { - u64 ticks = static_cast(global_timer); - if (!is_global_timer_sane) { - ticks += accumulated_ticks; - } - return ticks; +void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) { + ticks_count[core_index] += ticks; +} + +void CoreTiming::ResetTicks(std::size_t core_index) { + ticks_count[core_index] = 0; } -u64 CoreTiming::GetIdleTicks() const { - return static_cast(idled_cycles); +u64 CoreTiming::GetCPUTicks() const { + return clock->GetCPUCycles(); } -void CoreTiming::AddTicks(u64 ticks) { - accumulated_ticks += ticks; - downcounts[current_context] -= static_cast(ticks); +u64 CoreTiming::GetClockTicks() const { + return clock->GetClockCycles(); } void CoreTiming::ClearPendingEvents() { @@ -117,7 +136,7 @@ void CoreTiming::ClearPendingEvents() { } void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { - std::lock_guard guard{inner_mutex}; + basic_lock.lock(); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get(); @@ -128,99 +147,64 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } + basic_lock.unlock(); } -void CoreTiming::ForceExceptionCheck(s64 cycles) { - cycles = std::max(0, cycles); - if (downcounts[current_context] <= cycles) { - return; - } - - // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int - // here. Account for cycles already executed by adjusting the g.slice_length - downcounts[current_context] = static_cast(cycles); -} - -std::optional CoreTiming::NextAvailableCore(const s64 needed_ticks) const { - const u64 original_context = current_context; - u64 next_context = (original_context + 1) % num_cpu_cores; - while (next_context != original_context) { - if (time_slice[next_context] >= needed_ticks) { - return {next_context}; - } else if (time_slice[next_context] >= 0) { - return std::nullopt; - } - next_context = (next_context + 1) % num_cpu_cores; - } - return std::nullopt; -} - -void CoreTiming::Advance() { - std::unique_lock guard(inner_mutex); - - const u64 cycles_executed = accumulated_ticks; - time_slice[current_context] = std::max(0, time_slice[current_context] - accumulated_ticks); - global_timer += cycles_executed; - - is_global_timer_sane = true; +std::optional CoreTiming::Advance() { + advance_lock.lock(); + basic_lock.lock(); + global_timer = GetGlobalTimeNs().count(); while (!event_queue.empty() && event_queue.front().time <= global_timer) { Event evt = std::move(event_queue.front()); std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); - inner_mutex.unlock(); + basic_lock.unlock(); if (auto event_type{evt.type.lock()}) { event_type->callback(evt.userdata, global_timer - evt.time); } - inner_mutex.lock(); + basic_lock.lock(); } - is_global_timer_sane = false; - - // Still events left (scheduled in the future) if (!event_queue.empty()) { - const s64 needed_ticks = - std::min(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); - const auto next_core = NextAvailableCore(needed_ticks); - if (next_core) { - downcounts[*next_core] = needed_ticks; - } + const u64 next_time = event_queue.front().time - global_timer; + basic_lock.unlock(); + advance_lock.unlock(); + return next_time; + } else { + basic_lock.unlock(); + advance_lock.unlock(); + return std::nullopt; } - - accumulated_ticks = 0; - - downcounts[current_context] = time_slice[current_context]; } -void CoreTiming::ResetRun() { - downcounts.fill(MAX_SLICE_LENGTH); - time_slice.fill(MAX_SLICE_LENGTH); - current_context = 0; - // Still events left (scheduled in the future) - if (!event_queue.empty()) { - const s64 needed_ticks = - std::min(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); - downcounts[current_context] = needed_ticks; +void CoreTiming::ThreadLoop() { + has_started = true; + while (!shutting_down) { + while (!paused) { + paused_set = false; + const auto next_time = Advance(); + if (next_time) { + std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); + event.WaitFor(next_time_ns); + } else { + wait_set = true; + event.Wait(); + } + wait_set = false; + } + paused_set = true; } - - is_global_timer_sane = false; - accumulated_ticks = 0; } -void CoreTiming::Idle() { - accumulated_ticks += downcounts[current_context]; - idled_cycles += downcounts[current_context]; - downcounts[current_context] = 0; +std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { + return clock->GetTimeNS(); } std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; -} - -s64 CoreTiming::GetDowncount() const { - return downcounts[current_context]; + return clock->GetTimeUS(); } } // namespace Core::Timing -- cgit v1.2.3 From 9846f5822d937ba6ea752daecd5815155deeb7e1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 11:12:46 -0400 Subject: General: Setup yuzu threads' microprofile, naming and registry. --- src/core/core_timing.cpp | 5 +++-- src/core/cpu_manager.cpp | 5 ++++- src/video_core/gpu_thread.cpp | 6 +++++- src/yuzu/bootmanager.cpp | 4 +++- src/yuzu/main.cpp | 2 ++ src/yuzu_cmd/yuzu.cpp | 6 ++++-- src/yuzu_tester/yuzu.cpp | 6 ++++-- 7 files changed, 25 insertions(+), 9 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a3ce69790a..cc32a853b9 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -2,14 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/core_timing.h" - #include #include #include #include #include "common/assert.h" +#include "common/microprofile.h" +#include "core/core_timing.h" #include "core/core_timing_util.h" namespace Core::Timing { @@ -44,6 +44,7 @@ CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { std::string name = "yuzu:HostTiming"; + MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); instance.on_thread_init(); instance.ThreadLoop(); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index ff2fe8eadc..9b93371318 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/fiber.h" +#include "common/microprofile.h" #include "common/thread.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -36,6 +37,7 @@ void CpuManager::Shutdown() { Pause(false); for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { core_data[core].host_thread->join(); + core_data[core].host_thread.reset(); } } @@ -80,7 +82,7 @@ void CpuManager::RunGuestThread() { auto& physical_core = kernel.CurrentPhysicalCore(); if (!physical_core.IsInterrupted()) { physical_core.Idle(); - //physical_core.Run(); + // physical_core.Run(); } auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); @@ -159,6 +161,7 @@ void CpuManager::RunThread(std::size_t core) { /// Initialization system.RegisterCoreThread(core); std::string name = "yuzu:CoreHostThread_" + std::to_string(core); + MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); auto& data = core_data[core]; data.enter_barrier = std::make_unique(); diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index c3bb4fe062..323185bfcd 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/microprofile.h" +#include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" #include "core/settings.h" @@ -18,7 +19,10 @@ namespace VideoCommon::GPUThread { static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, SynchState& state) { - MicroProfileOnThreadCreate("GpuThread"); + std::string name = "yuzu:GPU"; + MicroProfileOnThreadCreate(name.c_str()); + Common::SetCurrentThreadName(name.c_str()); + system.RegisterHostThread(); // Wait for first GPU command before acquiring the window context while (state.queue.Empty()) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 9ceb6c8d73..468dde782c 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -44,7 +44,9 @@ EmuThread::EmuThread() = default; EmuThread::~EmuThread() = default; void EmuThread::run() { - MicroProfileOnThreadCreate("EmuThread"); + std::string name = "yuzu:EmuControlThread"; + MicroProfileOnThreadCreate(name.c_str()); + Common::SetCurrentThreadName(name.c_str()); // Main process has been loaded. Make the context current to this thread and begin GPU and CPU // execution. diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ba69139e5b..de0c7fe8cc 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -925,6 +925,8 @@ bool GMainWindow::LoadROM(const QString& filename) { nullptr, // E-Commerce }); + system.RegisterHostThread(); + const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; const auto drd_callout = diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 38ffdfbd33..e6c6a839de 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -237,8 +238,9 @@ int main(int argc, char** argv) { std::thread render_thread([&emu_window] { emu_window->Present(); }); system.Run(); - while (emu_window->IsOpen()) - ; + while (emu_window->IsOpen()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } system.Pause(); render_thread.join(); diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index d62686dd27..083667baf1 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -256,8 +257,9 @@ int main(int argc, char** argv) { system.Renderer().Rasterizer().LoadDiskResources(); system.Run(); - while (!finished) - ; + while (!finished) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } system.Pause(); detached_tasks.WaitForAllTasks(); -- cgit v1.2.3 From fb1d75a78885c2231cea1816150bd06027c12644 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 12:28:55 -0400 Subject: HostTiming: Pause the hardware clock on pause. --- src/common/wall_clock.cpp | 4 ++++ src/common/wall_clock.h | 2 ++ src/common/x64/native_clock.cpp | 7 +++++++ src/common/x64/native_clock.h | 2 ++ src/core/core.cpp | 2 +- src/core/core_timing.cpp | 6 ++++++ src/core/core_timing.h | 1 + 7 files changed, 23 insertions(+), 1 deletion(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index d4d35f4e7a..a46db6bbfb 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -53,6 +53,10 @@ public: return Common::Divide128On32(temporary, 1000000000).first; } + void Pause(bool is_paused) override { + // Do nothing in this clock type. + } + private: base_time_point start_time; }; diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index ed284cf502..367d721348 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -28,6 +28,8 @@ public: /// Returns current wall time in emulated cpu cycles virtual u64 GetCPUCycles() = 0; + virtual void Pause(bool is_paused) = 0; + /// Tells if the wall clock, uses the host CPU's hardware clock bool IsNative() const { return is_native; diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 26d4d0ba68..926f92ff86 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -65,6 +65,13 @@ u64 NativeClock::GetRTSC() { return accumulated_ticks; } +void NativeClock::Pause(bool is_paused) { + if (!is_paused) { + _mm_mfence(); + last_measure = __rdtsc(); + } +} + std::chrono::nanoseconds NativeClock::GetTimeNS() { const u64 rtsc_value = GetRTSC(); return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index b58cf9f5a4..3851f8fc22 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -26,6 +26,8 @@ public: u64 GetCPUCycles() override; + void Pause(bool is_paused) override; + private: u64 GetRTSC(); diff --git a/src/core/core.cpp b/src/core/core.cpp index e8936b09d5..1d6179a80e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -137,8 +137,8 @@ struct System::Impl { ResultStatus Pause() { status = ResultStatus::Success; - kernel.Suspend(true); core_timing.SyncPause(true); + kernel.Suspend(true); cpu_manager.Pause(true); return status; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index cc32a853b9..5a7abcfca9 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -77,6 +77,9 @@ void CoreTiming::SyncPause(bool is_paused) { return; } Pause(is_paused); + if (!is_paused) { + pause_event.Set(); + } event.Set(); while (paused_set != is_paused) ; @@ -197,6 +200,9 @@ void CoreTiming::ThreadLoop() { wait_set = false; } paused_set = true; + clock->Pause(true); + pause_event.Wait(); + clock->Pause(false); } } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 707c8ef0c2..c70b605c89 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -136,6 +136,7 @@ private: std::shared_ptr ev_lost; Common::Event event{}; + Common::Event pause_event{}; Common::SpinLock basic_lock{}; Common::SpinLock advance_lock{}; std::unique_ptr timer_thread; -- cgit v1.2.3 From 47c01e68da4bc6fd28f6b705e2226078e8026647 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 3 Mar 2020 15:50:38 -0400 Subject: Kernel: Corrections to Scheduling. --- src/core/core_timing.cpp | 11 +++++++---- src/core/core_timing.h | 2 +- src/core/hle/kernel/scheduler.cpp | 26 +++++++++++++------------- src/core/hle/kernel/scheduler.h | 2 ++ src/core/hle/kernel/svc.cpp | 1 - 5 files changed, 23 insertions(+), 19 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 5a7abcfca9..c91ae99759 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -154,7 +154,7 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { basic_lock.unlock(); } -std::optional CoreTiming::Advance() { +std::optional CoreTiming::Advance() { advance_lock.lock(); basic_lock.lock(); global_timer = GetGlobalTimeNs().count(); @@ -170,10 +170,11 @@ std::optional CoreTiming::Advance() { } basic_lock.lock(); + global_timer = GetGlobalTimeNs().count(); } if (!event_queue.empty()) { - const u64 next_time = event_queue.front().time - global_timer; + const s64 next_time = event_queue.front().time - global_timer; basic_lock.unlock(); advance_lock.unlock(); return next_time; @@ -191,8 +192,10 @@ void CoreTiming::ThreadLoop() { paused_set = false; const auto next_time = Advance(); if (next_time) { - std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); - event.WaitFor(next_time_ns); + if (*next_time > 0) { + std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); + event.WaitFor(next_time_ns); + } } else { wait_set = true; event.Wait(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index c70b605c89..032eb08aad 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -110,7 +110,7 @@ public: std::chrono::nanoseconds GetGlobalTimeNs() const; /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. - std::optional Advance(); + std::optional Advance(); private: struct Event; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index d67d3c5cdd..da77967dd8 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -47,13 +47,13 @@ u32 GlobalScheduler::SelectThreads() { ASSERT(is_locked); const auto update_thread = [](Thread* thread, Scheduler& sched) { sched.guard.lock(); - if (thread != sched.selected_thread.get()) { + if (thread != sched.selected_thread_set.get()) { if (thread == nullptr) { ++sched.idle_selection_count; } - sched.selected_thread = SharedFrom(thread); + sched.selected_thread_set = SharedFrom(thread); } - const bool reschedule_pending = sched.selected_thread != sched.current_thread; + const bool reschedule_pending = sched.selected_thread_set != sched.current_thread; sched.is_context_switch_pending = reschedule_pending; std::atomic_thread_fence(std::memory_order_seq_cst); sched.guard.unlock(); @@ -118,6 +118,8 @@ u32 GlobalScheduler::SelectThreads() { suggested); top_threads[candidate_core] = next; break; + } else { + suggested = nullptr; } } } @@ -590,7 +592,7 @@ void Scheduler::OnThreadStart() { } void Scheduler::SwitchContextStep2() { - Thread* previous_thread = current_thread.get(); + Thread* previous_thread = current_thread_prev.get(); Thread* new_thread = selected_thread.get(); // Load context of new thread @@ -606,8 +608,6 @@ void Scheduler::SwitchContextStep2() { "Thread must be ready to become running."); // Cancel any outstanding wakeup events for this thread - current_thread = SharedFrom(new_thread); - new_thread->SetStatus(ThreadStatus::Running); new_thread->SetIsRunning(true); auto* const thread_owner_process = current_thread->GetOwnerProcess(); @@ -622,21 +622,21 @@ void Scheduler::SwitchContextStep2() { cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); cpu_core.ClearExclusiveState(); } - } else { - current_thread = nullptr; - // Note: We do not reset the current process and current page table when idling because - // technically we haven't changed processes, our threads are just paused. } - guard.unlock(); + + TryDoContextSwitch(); } void Scheduler::SwitchContext() { - Thread* previous_thread = current_thread.get(); + current_thread_prev = current_thread; + selected_thread = selected_thread_set; + Thread* previous_thread = current_thread_prev.get(); Thread* new_thread = selected_thread.get(); + current_thread = selected_thread; is_context_switch_pending = false; + guard.unlock(); if (new_thread == previous_thread) { - guard.unlock(); return; } diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index f26a554f50..f73ca777e4 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -249,6 +249,8 @@ private: std::shared_ptr current_thread = nullptr; std::shared_ptr selected_thread = nullptr; + std::shared_ptr current_thread_prev = nullptr; + std::shared_ptr selected_thread_set = nullptr; std::shared_ptr idle_thread = nullptr; Core::System& system; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9f46a17589..5e9dd43bf0 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -316,7 +316,6 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle, /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Core::System& system, Handle handle) { - std::lock_guard lock{HLE::g_hle_lock}; const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); std::shared_ptr session = handle_table.Get(handle); if (!session) { -- cgit v1.2.3 From cb974be3ce3de687f6c4f160ed5183f0de0958f5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 11 Mar 2020 20:44:53 -0400 Subject: General: Fix Stop function --- src/core/core_timing.cpp | 1 + src/core/hle/kernel/kernel.cpp | 15 +++++++++++++++ src/core/hle/kernel/thread.cpp | 8 +++++--- src/yuzu/bootmanager.cpp | 6 ++++-- 4 files changed, 25 insertions(+), 5 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index c91ae99759..3438f79cee 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -61,6 +61,7 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { void CoreTiming::Shutdown() { paused = true; shutting_down = true; + pause_event.Set(); event.Set(); timer_thread->join(); ClearPendingEvents(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 2a1b917527..24da4367e4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -119,6 +119,7 @@ struct KernelCore::Impl { void Initialize(KernelCore& kernel) { Shutdown(); + RegisterHostThread(); InitializePhysicalCores(); InitializeSystemResourceLimit(kernel); @@ -135,6 +136,19 @@ struct KernelCore::Impl { next_user_process_id = Process::ProcessIDMin; next_thread_id = 1; + for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + if (suspend_threads[i]) { + suspend_threads[i].reset(); + } + } + + for (std::size_t i = 0; i < cores.size(); i++) { + cores[i].Shutdown(); + } + cores.clear(); + + registered_core_threads.reset(); + process_list.clear(); current_process = nullptr; @@ -154,6 +168,7 @@ struct KernelCore::Impl { cores.clear(); exclusive_monitor.reset(); + host_thread_ids.clear(); } void InitializePhysicalCores() { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1c32552b12..6f8e7a070a 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -56,10 +56,12 @@ void Thread::Stop() { Signal(); kernel.GlobalHandleTable().Close(global_handle); - owner_process->UnregisterThread(this); + if (owner_process) { + owner_process->UnregisterThread(this); - // Mark the TLS slot in the thread's page as free. - owner_process->FreeTLSRegion(tls_address); + // Mark the TLS slot in the thread's page as free. + owner_process->FreeTLSRegion(tls_address); + } } global_handle = 0; } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 468dde782c..6fad01d50b 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -89,8 +89,10 @@ void EmuThread::run() { } running_guard = false; - was_active = true; - emit DebugModeEntered(); + if (!stop_run) { + was_active = true; + emit DebugModeEntered(); + } } else if (exec_step) { UNIMPLEMENTED(); } else { -- cgit v1.2.3 From 287ea2d3b4324110eec1a972ebe59fa702c082e9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 19 Mar 2020 13:09:32 -0400 Subject: SingleCore: Move Host Timing from a sepparate thread to main cpu thread. --- src/core/core.cpp | 2 ++ src/core/core_timing.cpp | 20 +++++++++++++------- src/core/core_timing.h | 7 +++++++ src/core/cpu_manager.cpp | 17 +++++++++++++++-- src/core/cpu_manager.h | 1 + src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/kernel/thread.h | 9 +++++++++ 7 files changed, 48 insertions(+), 10 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/core.cpp b/src/core/core.cpp index 0f0eb885ad..2ca9c0be54 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -158,6 +158,8 @@ struct System::Impl { kernel.SetMulticore(is_multicore); cpu_manager.SetMulticore(is_multicore); cpu_manager.SetAsyncGpu(is_async_gpu); + core_timing.SetMulticore(is_multicore); + cpu_manager.SetRenderWindow(emu_window); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); kernel.Initialize(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 3438f79cee..189d4aa34d 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -55,7 +55,9 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { event_fifo_id = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); - timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); + if (is_multicore) { + timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); + } } void CoreTiming::Shutdown() { @@ -63,7 +65,9 @@ void CoreTiming::Shutdown() { shutting_down = true; pause_event.Set(); event.Set(); - timer_thread->join(); + if (timer_thread) { + timer_thread->join(); + } ClearPendingEvents(); timer_thread.reset(); has_started = false; @@ -78,12 +82,14 @@ void CoreTiming::SyncPause(bool is_paused) { return; } Pause(is_paused); - if (!is_paused) { - pause_event.Set(); + if (timer_thread) { + if (!is_paused) { + pause_event.Set(); + } + event.Set(); + while (paused_set != is_paused) + ; } - event.Set(); - while (paused_set != is_paused) - ; } bool CoreTiming::IsRunning() const { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 032eb08aad..03f9a5c764 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -67,6 +67,11 @@ public: /// Tears down all timing related functionality. void Shutdown(); + /// Sets if emulation is multicore or single core, must be set before Initialize + void SetMulticore(bool is_multicore) { + this->is_multicore = is_multicore; + } + /// Pauses/Unpauses the execution of the timer thread. void Pause(bool is_paused); @@ -147,6 +152,8 @@ private: std::atomic has_started{}; std::function on_thread_init{}; + bool is_multicore{}; + std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; }; diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index d7bd162bc5..2aea95a257 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -242,8 +242,11 @@ void CpuManager::SingleCoreRunGuestLoop() { break; } } - physical_core.ClearExclusive(); system.ExitDynarmicProfile(); + thread->SetPhantomMode(true); + system.CoreTiming().Advance(); + thread->SetPhantomMode(false); + physical_core.ClearExclusive(); PreemptSingleCore(); auto& scheduler = kernel.Scheduler(current_core); scheduler.TryDoContextSwitch(); @@ -255,6 +258,7 @@ void CpuManager::SingleCoreRunIdleThread() { while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); PreemptSingleCore(); + idle_count++; auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); } @@ -280,15 +284,24 @@ void CpuManager::SingleCoreRunSuspendThread() { void CpuManager::PreemptSingleCore() { preemption_count = 0; std::size_t old_core = current_core; - current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); auto& scheduler = system.Kernel().Scheduler(old_core); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + if (idle_count >= 4) { + current_thread->SetPhantomMode(true); + system.CoreTiming().Advance(); + current_thread->SetPhantomMode(false); + } + current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); scheduler.Unload(); auto& next_scheduler = system.Kernel().Scheduler(current_core); Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); /// May have changed scheduler auto& current_scheduler = system.Kernel().Scheduler(current_core); current_scheduler.Reload(); + auto* currrent_thread2 = current_scheduler.GetCurrentThread(); + if (!currrent_thread2->IsIdleThread()) { + idle_count = 0; + } } void CpuManager::SingleCorePause(bool paused) { diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 37cef2b122..e6b8612f0e 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -104,6 +104,7 @@ private: bool is_multicore{}; std::atomic current_core{}; std::size_t preemption_count{}; + std::size_t idle_count{}; static constexpr std::size_t max_cycle_runs = 5; Core::Frontend::EmuWindow* render_window; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d2f5f9bf23..a19cd7a1ff 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -303,7 +303,7 @@ struct KernelCore::Impl { } const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); const Kernel::Thread* current = sched.GetCurrentThread(); - if (current != nullptr) { + if (current != nullptr && !current->IsPhantomMode()) { result.guest_handle = current->GetGlobalHandle(); } else { result.guest_handle = InvalidHandle; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f42d7bd136..f998890c4b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -597,6 +597,14 @@ public: is_continuous_on_svc = is_continuous; } + bool IsPhantomMode() const { + return is_phantom_mode; + } + + void SetPhantomMode(bool phantom) { + is_phantom_mode = phantom; + } + private: friend class GlobalScheduler; friend class Scheduler; @@ -699,6 +707,7 @@ private: bool is_continuous_on_svc = false; bool will_be_terminated = false; + bool is_phantom_mode = false; bool was_running = false; -- cgit v1.2.3 From f0f2fc4243add00274c68e6dafa509fd68701e11 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 28 Mar 2020 15:23:28 -0400 Subject: SingleCore: Use Cycle Timing instead of Host Timing. --- src/core/arm/arm_interface.h | 6 +++-- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 37 +++++++++++++++++++--------- src/core/arm/dynarmic/arm_dynarmic_32.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 39 +++++++++++++++++++---------- src/core/arm/dynarmic/arm_dynarmic_64.h | 2 +- src/core/arm/unicorn/arm_unicorn.cpp | 6 ++--- src/core/arm/unicorn/arm_unicorn.h | 4 +-- src/core/core_timing.cpp | 41 +++++++++++++++++++++++++------ src/core/core_timing.h | 14 ++++++++--- src/core/core_timing_util.cpp | 29 +++++++++++++++------- src/core/core_timing_util.h | 15 +++-------- src/core/cpu_manager.cpp | 18 +++++++------- src/core/cpu_manager.h | 2 +- src/core/hle/kernel/svc.cpp | 5 ++++ src/core/hle/kernel/thread.cpp | 12 ++++++--- 15 files changed, 152 insertions(+), 80 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index e5c4843364..fbdce4134d 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -26,8 +26,9 @@ using CPUInterrupts = std::arrayticks -= ticks; + if (parent.uses_wall_clock) { + return; + } + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a + // rough approximation of the amount of executed ticks in the system, it may be thrown off + // if not all cores are doing a similar amount of work. Instead of doing this, we should + // device a way so that timing is consistent across all cores without increasing the ticks 4 + // times. + u64 amortized_ticks = + (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; + // Always execute at least one tick. + amortized_ticks = std::max(amortized_ticks, 1); + + parent.system.CoreTiming().AddTicks(amortized_ticks); + num_interpreted_instructions = 0; } u64 GetTicksRemaining() override { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return std::max(ticks, 0); + if (parent.uses_wall_clock) { + if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { + return std::max(1000U, 0); + } + return 0ULL; } - return 0ULL; - } - - void ResetTicks() { - ticks = 1000LL; + return std::max(parent.system.CoreTiming().GetDowncount(), 0LL); } ARM_Dynarmic_32& parent; std::size_t num_interpreted_instructions{}; - s64 ticks{}; }; std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, @@ -103,7 +115,6 @@ std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& } void ARM_Dynarmic_32::Run() { - cb->ResetTicks(); jit->Run(); } @@ -112,8 +123,10 @@ void ARM_Dynarmic_32::Step() { } ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, - ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : ARM_Interface{system, interrupt_handlers}, cb(std::make_unique(*this)), + bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, + cb(std::make_unique(*this)), cp15(std::make_shared(*this)), core_index{core_index}, exclusive_monitor{dynamic_cast(exclusive_monitor)} {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index bea4933c81..8afd15c8bf 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -29,7 +29,7 @@ class System; class ARM_Dynarmic_32 final : public ARM_Interface { public: - ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, + ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_32() override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 03b3313cf1..a518733b62 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -124,29 +124,41 @@ public: } void AddTicks(u64 ticks) override { - this->ticks -= ticks; + if (parent.uses_wall_clock) { + return; + } + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a + // rough approximation of the amount of executed ticks in the system, it may be thrown off + // if not all cores are doing a similar amount of work. Instead of doing this, we should + // device a way so that timing is consistent across all cores without increasing the ticks 4 + // times. + u64 amortized_ticks = + (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; + // Always execute at least one tick. + amortized_ticks = std::max(amortized_ticks, 1); + + parent.system.CoreTiming().AddTicks(amortized_ticks); + num_interpreted_instructions = 0; } u64 GetTicksRemaining() override { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return std::max(ticks, 0); + if (parent.uses_wall_clock) { + if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { + return std::max(1000U, 0); + } + return 0ULL; } - return 0ULL; + return std::max(parent.system.CoreTiming().GetDowncount(), 0LL); } u64 GetCNTPCT() override { return parent.system.CoreTiming().GetClockTicks(); } - void ResetTicks() { - ticks = 1000LL; - } - ARM_Dynarmic_64& parent; std::size_t num_interpreted_instructions = 0; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; - s64 ticks{}; }; std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, @@ -185,13 +197,12 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& } // CNTPCT uses wall clock. - config.wall_clock_cntpct = true; + config.wall_clock_cntpct = uses_wall_clock; return std::make_shared(config); } void ARM_Dynarmic_64::Run() { - cb->ResetTicks(); jit->Run(); } @@ -200,9 +211,11 @@ void ARM_Dynarmic_64::Step() { } ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, - ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : ARM_Interface{system, interrupt_handler}, + bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : ARM_Interface{system, interrupt_handler, uses_wall_clock}, cb(std::make_unique(*this)), inner_unicorn{system, interrupt_handler, + uses_wall_clock, ARM_Unicorn::Arch::AArch64, core_index}, core_index{core_index}, exclusive_monitor{ diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index c26b47249c..31ec165210 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -28,7 +28,7 @@ class System; class ARM_Dynarmic_64 final : public ARM_Interface { public: - ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, + ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_64() override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 099229c8d4..1cb71942b9 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -63,9 +63,9 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return false; } -ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture, - std::size_t core_index) - : ARM_Interface{system, interrupt_handler}, core_index{core_index} { +ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, + bool uses_wall_clock, Arch architecture, std::size_t core_index) + : ARM_Interface{system, interrupt_handler, uses_wall_clock}, core_index{core_index} { const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index f09b24a850..a01751e651 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -20,8 +20,8 @@ public: AArch64, // 64-bit ARM }; - explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture, - std::size_t core_index); + explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, + bool uses_wall_clock, Arch architecture, std::size_t core_index); ~ARM_Unicorn() override; void SetPC(u64 pc) override; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 189d4aa34d..12e9e60a4a 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -14,6 +14,8 @@ namespace Core::Timing { +constexpr u64 MAX_SLICE_LENGTH = 4000; + std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback) { return std::make_shared(std::move(callback), std::move(name)); } @@ -53,6 +55,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { void CoreTiming::Initialize(std::function&& on_thread_init_) { on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; + ticks = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { @@ -126,20 +129,36 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u basic_lock.unlock(); } -void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) { - ticks_count[core_index] += ticks; +void CoreTiming::AddTicks(u64 ticks) { + this->ticks += ticks; + downcount -= ticks; } -void CoreTiming::ResetTicks(std::size_t core_index) { - ticks_count[core_index] = 0; +void CoreTiming::Idle() { + if (!event_queue.empty()) { + u64 next_event_time = event_queue.front().time; + ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; + return; + } + ticks += 1000U; +} + +void CoreTiming::ResetTicks() { + downcount = MAX_SLICE_LENGTH; } u64 CoreTiming::GetCPUTicks() const { - return clock->GetCPUCycles(); + if (is_multicore) { + return clock->GetCPUCycles(); + } + return ticks; } u64 CoreTiming::GetClockTicks() const { - return clock->GetClockCycles(); + if (is_multicore) { + return clock->GetClockCycles(); + } + return CpuCyclesToClockCycles(ticks); } void CoreTiming::ClearPendingEvents() { @@ -217,11 +236,17 @@ void CoreTiming::ThreadLoop() { } std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { - return clock->GetTimeNS(); + if (is_multicore) { + return clock->GetTimeNS(); + } + return CyclesToNs(ticks); } std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - return clock->GetTimeUS(); + if (is_multicore) { + return clock->GetTimeUS(); + } + return CyclesToUs(ticks); } } // namespace Core::Timing diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 03f9a5c764..ed5de9b97a 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -98,9 +98,15 @@ public: /// We only permit one event of each type in the queue at a time. void RemoveEvent(const std::shared_ptr& event_type); - void AddTicks(std::size_t core_index, u64 ticks); + void AddTicks(u64 ticks); - void ResetTicks(std::size_t core_index); + void ResetTicks(); + + void Idle(); + + s64 GetDowncount() const { + return downcount; + } /// Returns current time in emulated CPU cycles u64 GetCPUTicks() const; @@ -154,7 +160,9 @@ private: bool is_multicore{}; - std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; + /// Cycle timing + u64 ticks{}; + s64 downcount{}; }; /// Creates a core timing event with the given name and callback. diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index be34b26fe4..aefc636638 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -38,15 +38,8 @@ s64 usToCycles(std::chrono::microseconds us) { } s64 nsToCycles(std::chrono::nanoseconds ns) { - if (static_cast(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) { - LOG_ERROR(Core_Timing, "Integer overflow, use max value"); - return std::numeric_limits::max(); - } - if (static_cast(ns.count()) > MAX_VALUE_TO_MULTIPLY) { - LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return Hardware::BASE_CLOCK_RATE * (ns.count() / 1000000000); - } - return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; + const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE); + return Common::Divide128On32(temporal, static_cast(1000000000)).first; } u64 msToClockCycles(std::chrono::milliseconds ns) { @@ -69,4 +62,22 @@ u64 CpuCyclesToClockCycles(u64 ticks) { return Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; } +std::chrono::milliseconds CyclesToMs(s64 cycles) { + const u128 temporal = Common::Multiply64Into128(cycles, 1000); + u64 ms = Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; + return std::chrono::milliseconds(ms); +} + +std::chrono::nanoseconds CyclesToNs(s64 cycles) { + const u128 temporal = Common::Multiply64Into128(cycles, 1000000000); + u64 ns = Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; + return std::chrono::nanoseconds(ns); +} + +std::chrono::microseconds CyclesToUs(s64 cycles) { + const u128 temporal = Common::Multiply64Into128(cycles, 1000000); + u64 us = Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; + return std::chrono::microseconds(us); +} + } // namespace Core::Timing diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index b3c58447d5..2ed979e14c 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -16,18 +16,9 @@ s64 nsToCycles(std::chrono::nanoseconds ns); u64 msToClockCycles(std::chrono::milliseconds ns); u64 usToClockCycles(std::chrono::microseconds ns); u64 nsToClockCycles(std::chrono::nanoseconds ns); - -inline std::chrono::milliseconds CyclesToMs(s64 cycles) { - return std::chrono::milliseconds(cycles * 1000 / Hardware::BASE_CLOCK_RATE); -} - -inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { - return std::chrono::nanoseconds(cycles * 1000000000 / Hardware::BASE_CLOCK_RATE); -} - -inline std::chrono::microseconds CyclesToUs(s64 cycles) { - return std::chrono::microseconds(cycles * 1000000 / Hardware::BASE_CLOCK_RATE); -} +std::chrono::milliseconds CyclesToMs(s64 cycles); +std::chrono::nanoseconds CyclesToNs(s64 cycles); +std::chrono::microseconds CyclesToUs(s64 cycles); u64 CpuCyclesToClockCycles(u64 ticks); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 2e9dc9dc3d..6044050607 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -232,13 +232,10 @@ void CpuManager::SingleCoreRunGuestLoop() { auto* physical_core = &kernel.CurrentPhysicalCore(); auto& arm_interface = thread->ArmInterface(); system.EnterDynarmicProfile(); - while (!physical_core->IsInterrupted()) { + if (!physical_core->IsInterrupted()) { + system.CoreTiming().ResetTicks(); arm_interface.Run(); physical_core = &kernel.CurrentPhysicalCore(); - preemption_count++; - if (preemption_count % max_cycle_runs == 0) { - break; - } } system.ExitDynarmicProfile(); thread->SetPhantomMode(true); @@ -255,7 +252,7 @@ void CpuManager::SingleCoreRunIdleThread() { auto& kernel = system.Kernel(); while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); - PreemptSingleCore(); + PreemptSingleCore(false); idle_count++; auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); @@ -279,12 +276,15 @@ void CpuManager::SingleCoreRunSuspendThread() { } } -void CpuManager::PreemptSingleCore() { - preemption_count = 0; +void CpuManager::PreemptSingleCore(bool from_running_enviroment) { std::size_t old_core = current_core; auto& scheduler = system.Kernel().Scheduler(old_core); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); - if (idle_count >= 4) { + if (idle_count >= 4 || from_running_enviroment) { + if (!from_running_enviroment) { + system.CoreTiming().Idle(); + idle_count = 0; + } current_thread->SetPhantomMode(true); system.CoreTiming().Advance(); current_thread->SetPhantomMode(false); diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index e6b8612f0e..ae55d6427e 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -55,7 +55,7 @@ public: std::function GetSuspendThreadStartFunc(); void* GetStartFuncParamater(); - void PreemptSingleCore(); + void PreemptSingleCore(bool from_running_enviroment = true); std::size_t CurrentCore() const { return current_core.load(); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5999722119..c47fa91671 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1534,6 +1534,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { if (is_redundant && !system.Kernel().IsMulticore()) { system.Kernel().ExitSVCProfile(); + system.CoreTiming().AddTicks(1000U); system.GetCpuManager().PreemptSingleCore(); system.Kernel().EnterSVCProfile(); } @@ -1762,6 +1763,10 @@ static u64 GetSystemTick(Core::System& system) { // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) const u64 result{system.CoreTiming().GetClockTicks()}; + if (!system.Kernel().IsMulticore()) { + core_timing.AddTicks(400U); + } + return result; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 65fedfc9b9..d88039a162 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -246,19 +246,23 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy #ifdef ARCHITECTURE_x86_64 if (owner_process && !owner_process->Is64BitProcess()) { thread->arm_interface = std::make_unique( - system, kernel.Interrupts(), kernel.GetExclusiveMonitor(), processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), + processor_id); } else { thread->arm_interface = std::make_unique( - system, kernel.Interrupts(), kernel.GetExclusiveMonitor(), processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), + processor_id); } #else if (owner_process && !owner_process->Is64BitProcess()) { thread->arm_interface = std::make_shared( - system, kernel.Interrupts(), ARM_Unicorn::Arch::AArch32, processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32, + processor_id); } else { thread->arm_interface = std::make_shared( - system, kernel.Interrupts(), ARM_Unicorn::Arch::AArch64, processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64, + processor_id); } LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif -- cgit v1.2.3 From afb6d31c02e7a421a568007cb6c534fe69b9ee4e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 2 Apr 2020 13:27:08 -0400 Subject: CoreTiming/CycleTimer: Correct Idling. --- src/core/core_timing.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 12e9e60a4a..b02119494e 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -136,8 +136,11 @@ void CoreTiming::AddTicks(u64 ticks) { void CoreTiming::Idle() { if (!event_queue.empty()) { - u64 next_event_time = event_queue.front().time; - ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; + const u64 next_event_time = event_queue.front().time; + const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; + if (next_ticks > ticks) { + ticks = next_ticks; + } return; } ticks += 1000U; -- cgit v1.2.3 From fb07639438539dcb161b06bf125933249f5cefc6 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 5 Apr 2020 09:48:53 -0400 Subject: General: Tune the priority of main emulation threads so they have higher priority than less important helper threads. --- src/common/thread.cpp | 46 +++++++++++++++++++++++++ src/common/thread.h | 9 +++++ src/core/core_timing.cpp | 1 + src/core/cpu_manager.cpp | 1 + src/video_core/gpu_thread.cpp | 1 + src/video_core/renderer_vulkan/vk_scheduler.cpp | 2 ++ 6 files changed, 60 insertions(+) (limited to 'src/core/core_timing.cpp') diff --git a/src/common/thread.cpp b/src/common/thread.cpp index c9684aed93..33c8437f52 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -25,6 +25,52 @@ namespace Common { +#ifdef _WIN32 + +void SetCurrentThreadPriority(ThreadPriority new_priority) { + auto handle = GetCurrentThread(); + int windows_priority = 0; + switch (new_priority) { + case ThreadPriority::Low: + windows_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case ThreadPriority::Normal: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::High: + windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case ThreadPriority::VeryHigh: + windows_priority = THREAD_PRIORITY_HIGHEST; + break; + default: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + } + SetThreadPriority(handle, windows_priority); +} + +#else + +void SetCurrentThreadPriority(ThreadPriority new_priority) { + pthread_t this_thread = pthread_self(); + + s32 max_prio = sched_get_priority_max(SCHED_OTHER); + s32 min_prio = sched_get_priority_min(SCHED_OTHER); + u32 level = static_cast(new_priority) + 1; + + struct sched_param params; + if (max_prio > min_prio) { + params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4; + } else { + params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; + } + + pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); +} + +#endif + #ifdef _MSC_VER // Sets the debugger-visible name of the current thread. diff --git a/src/common/thread.h b/src/common/thread.h index 127cc7e233..52b3594131 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -86,6 +86,15 @@ private: std::size_t generation = 0; // Incremented once each time the barrier is used }; +enum class ThreadPriority : u32 { + Low = 0, + Normal = 1, + High = 2, + VeryHigh = 3, +}; + +void SetCurrentThreadPriority(ThreadPriority new_priority); + void SetCurrentThreadName(const char* name); } // namespace Common diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b02119494e..032b29e339 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -48,6 +48,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { std::string name = "yuzu:HostTiming"; MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); instance.on_thread_init(); instance.ThreadLoop(); } diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 63c5788520..32afcf3ae9 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -337,6 +337,7 @@ void CpuManager::RunThread(std::size_t core) { } MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); auto& data = core_data[core]; data.enter_barrier = std::make_unique(); data.exit_barrier = std::make_unique(); diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 323185bfcd..738c6f0c1f 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -22,6 +22,7 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, std::string name = "yuzu:GPU"; MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); system.RegisterHostThread(); // Wait for first GPU command before acquiring the window context diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 82ec9180e5..56524e6f30 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -9,6 +9,7 @@ #include #include "common/microprofile.h" +#include "common/thread.h" #include "video_core/renderer_vulkan/vk_device.h" #include "video_core/renderer_vulkan/vk_query_cache.h" #include "video_core/renderer_vulkan/vk_resource_manager.h" @@ -133,6 +134,7 @@ void VKScheduler::BindGraphicsPipeline(VkPipeline pipeline) { } void VKScheduler::WorkerThread() { + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); std::unique_lock lock{mutex}; do { cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; }); -- cgit v1.2.3 From a4e7d0b1068cfa1f9afb0a09dbb4cd56b03767ef Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 23 Apr 2020 12:58:41 -0400 Subject: CoreTiming: Correct rebase bugs and other miscellaneous things. --- src/core/core_timing.cpp | 2 ++ src/tests/core/core_timing.cpp | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 032b29e339..1aa89a1ccd 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -56,6 +56,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { void CoreTiming::Initialize(std::function&& on_thread_init_) { on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; + shutting_down = false; ticks = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); @@ -79,6 +80,7 @@ void CoreTiming::Shutdown() { void CoreTiming::Pause(bool is_paused) { paused = is_paused; + pause_event.Set(); } void CoreTiming::SyncPause(bool is_paused) { diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 21a5840fb5..5aa339731d 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -16,10 +16,10 @@ namespace { // Numbers are chosen randomly to make sure the correct one is given. -static constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; -static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals -static constexpr std::array calls_order{{2, 0, 1, 4, 3}}; -static std::array delays{}; +constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; +constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals +constexpr std::array calls_order{{2, 0, 1, 4, 3}}; +std::array delays{}; std::bitset callbacks_ran_flags; u64 expected_callback = 0; @@ -49,6 +49,8 @@ struct ScopeInit final { Core::Timing::CoreTiming core_timing; }; +} // Anonymous namespace + TEST_CASE("CoreTiming[BasicOrder]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; -- cgit v1.2.3 From 94a51e6b212e989be5ffb2a38dfcdc3140640cfe Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 27 Jun 2020 18:20:06 -0400 Subject: Core/Common: Address Feedback. --- src/common/fiber.cpp | 10 +++------- src/common/spin_lock.cpp | 6 +++--- src/common/spin_lock.h | 5 +++++ src/common/x64/native_clock.cpp | 4 ++-- src/core/arm/arm_interface.h | 2 +- src/core/arm/cpu_interrupt_handler.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 5 +++-- src/core/arm/dynarmic/arm_dynarmic_32.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 5 +++-- src/core/arm/dynarmic/arm_dynarmic_64.h | 2 +- src/core/arm/unicorn/arm_unicorn.cpp | 2 +- src/core/arm/unicorn/arm_unicorn.h | 2 +- src/core/core.cpp | 2 +- src/core/core.h | 4 ++-- src/core/core_timing.cpp | 28 ++++++++++++---------------- src/core/hle/kernel/kernel.cpp | 14 +++++--------- src/core/hle/kernel/physical_core.cpp | 4 ++++ src/core/hle/kernel/physical_core.h | 7 ++----- src/core/hle/kernel/scheduler.cpp | 4 ++-- src/core/hle/kernel/scheduler.h | 4 ++++ src/tests/common/fibers.cpp | 2 +- 21 files changed, 58 insertions(+), 58 deletions(-) (limited to 'src/core/core_timing.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f97ad433b4..1c1d09ccb8 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -54,9 +54,7 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); } -Fiber::Fiber() { - impl = std::make_unique(); -} +Fiber::Fiber() : impl{std::make_unique()} {} Fiber::~Fiber() { if (released) { @@ -116,8 +114,8 @@ std::shared_ptr Fiber::ThreadToFiber() { struct Fiber::FiberImpl { alignas(64) std::array stack; - u8* stack_limit; alignas(64) std::array rewind_stack; + u8* stack_limit; u8* rewind_stack_limit; boost::context::detail::fcontext_t context; boost::context::detail::fcontext_t rewind_context; @@ -168,9 +166,7 @@ void Fiber::SetRewindPoint(std::function&& rewind_func, void* start rewind_parameter = start_parameter; } -Fiber::Fiber() { - impl = std::make_unique(); -} +Fiber::Fiber() : impl{std::make_unique()} {} Fiber::~Fiber() { if (released) { diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index c7b46aac6b..c1524220f6 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -20,7 +20,7 @@ namespace { -void thread_pause() { +void ThreadPause() { #if __x86_64__ _mm_pause(); #elif __aarch64__ && _MSC_VER @@ -30,13 +30,13 @@ void thread_pause() { #endif } -} // namespace +} // Anonymous namespace namespace Common { void SpinLock::lock() { while (lck.test_and_set(std::memory_order_acquire)) { - thread_pause(); + ThreadPause(); } } diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index 70282a961f..1df5528c47 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h @@ -8,6 +8,11 @@ namespace Common { +/** + * SpinLock class + * a lock similar to mutex that forces a thread to spin wait instead calling the + * supervisor. Should be used on short sequences of code. + */ class SpinLock { public: void lock(); diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index f1bc60fd20..424b39b1f1 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #ifdef _MSC_VER @@ -52,7 +53,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequenc } u64 NativeClock::GetRTSC() { - rtsc_serialize.lock(); + std::scoped_lock scope{rtsc_serialize}; _mm_mfence(); const u64 current_measure = __rdtsc(); u64 diff = current_measure - last_measure; @@ -61,7 +62,6 @@ u64 NativeClock::GetRTSC() { last_measure = current_measure; } accumulated_ticks += diff; - rtsc_serialize.unlock(); /// The clock cannot be more precise than the guest timer, remove the lower bits return accumulated_ticks & inaccuracy_mask; } diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 0c1d6ac391..1f24051e4a 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -148,7 +148,7 @@ public: */ virtual void SetTPIDR_EL0(u64 value) = 0; - virtual void ChangeProcessorId(std::size_t new_core_id) = 0; + virtual void ChangeProcessorID(std::size_t new_core_id) = 0; virtual void SaveContext(ThreadContext32& ctx) = 0; virtual void SaveContext(ThreadContext64& ctx) = 0; diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h index 91c31a2710..3d062d326b 100644 --- a/src/core/arm/cpu_interrupt_handler.h +++ b/src/core/arm/cpu_interrupt_handler.h @@ -23,7 +23,7 @@ public: CPUInterruptHandler(CPUInterruptHandler&&) = default; CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; - constexpr bool IsInterrupted() const { + bool IsInterrupted() const { return is_interrupted; } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index cfda120988..0d4ab95b72 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -107,7 +107,7 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return 1000U; + return minimum_run_cycles; } return 0U; } @@ -116,6 +116,7 @@ public: ARM_Dynarmic_32& parent; std::size_t num_interpreted_instructions{}; + static constexpr u64 minimum_run_cycles = 1000U; }; std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, @@ -214,7 +215,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { cp15->uprw = static_cast(value); } -void ARM_Dynarmic_32::ChangeProcessorId(std::size_t new_core_id) { +void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { jit->ChangeProcessorID(new_core_id); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index d9c0bfede2..2bab31b920 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -47,7 +47,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorId(std::size_t new_core_id) override; + void ChangeProcessorID(std::size_t new_core_id) override; void SaveContext(ThreadContext32& ctx) override; void SaveContext(ThreadContext64& ctx) override {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 35a99e28aa..7909810346 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -144,7 +144,7 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return 1000U; + return minimum_run_cycles; } return 0U; } @@ -159,6 +159,7 @@ public: std::size_t num_interpreted_instructions = 0; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; + static constexpr u64 minimum_run_cycles = 1000U; }; std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, @@ -271,7 +272,7 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { cb->tpidr_el0 = value; } -void ARM_Dynarmic_64::ChangeProcessorId(std::size_t new_core_id) { +void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { jit->ChangeProcessorID(new_core_id); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index c74fcbcea5..403c559615 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -45,7 +45,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorId(std::size_t new_core_id) override; + void ChangeProcessorID(std::size_t new_core_id) override; void SaveContext(ThreadContext32& ctx) override {} void SaveContext(ThreadContext64& ctx) override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 35e8f42e86..1df3f3ed1e 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -159,7 +159,7 @@ void ARM_Unicorn::SetTPIDR_EL0(u64 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); } -void ARM_Unicorn::ChangeProcessorId(std::size_t new_core_id) { +void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) { core_index = new_core_id; } diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 8ace8b86fa..810aff311e 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -36,7 +36,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorId(std::size_t new_core_id) override; + void ChangeProcessorID(std::size_t new_core_id) override; void PrepareReschedule() override; void ClearExclusiveState() override; void ExecuteInstructions(std::size_t num_instructions); diff --git a/src/core/core.cpp b/src/core/core.cpp index 8256ec0fc5..1a243c515a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -443,7 +443,7 @@ bool System::IsPoweredOn() const { } void System::PrepareReschedule() { - // impl->CurrentPhysicalCore().Stop(); + // Deprecated, does nothing, kept for backward compatibility. } void System::PrepareReschedule(const u32 core_index) { diff --git a/src/core/core.h b/src/core/core.h index 133ecb8e1c..5c6cfbffe1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -138,13 +138,13 @@ public: /** * Run the OS and Application - * This function will start emulation and run the competent devices + * This function will start emulation and run the relevant devices */ ResultStatus Run(); /** * Pause the OS and Application - * This function will pause emulation and stop the competent devices + * This function will pause emulation and stop the relevant devices */ ResultStatus Pause(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 1aa89a1ccd..5c83c41a40 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -45,9 +45,9 @@ CoreTiming::CoreTiming() { CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { - std::string name = "yuzu:HostTiming"; - MicroProfileOnThreadCreate(name.c_str()); - Common::SetCurrentThreadName(name.c_str()); + constexpr char name[] = "yuzu:HostTiming"; + MicroProfileOnThreadCreate(name); + Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); instance.on_thread_init(); instance.ThreadLoop(); @@ -108,18 +108,19 @@ bool CoreTiming::HasPendingEvents() const { void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, u64 userdata) { - basic_lock.lock(); - const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); + { + std::scoped_lock scope{basic_lock}; + const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); - event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); + event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - basic_lock.unlock(); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } event.Set(); } void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { - basic_lock.lock(); + std::scoped_lock scope{basic_lock}; const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get() && e.userdata == userdata; }); @@ -129,7 +130,6 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } - basic_lock.unlock(); } void CoreTiming::AddTicks(u64 ticks) { @@ -187,8 +187,8 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { } std::optional CoreTiming::Advance() { - advance_lock.lock(); - basic_lock.lock(); + std::scoped_lock advance_scope{advance_lock}; + std::scoped_lock basic_scope{basic_lock}; global_timer = GetGlobalTimeNs().count(); while (!event_queue.empty() && event_queue.front().time <= global_timer) { @@ -207,12 +207,8 @@ std::optional CoreTiming::Advance() { if (!event_queue.empty()) { const s64 next_time = event_queue.front().time - global_timer; - basic_lock.unlock(); - advance_lock.unlock(); return next_time; } else { - basic_lock.unlock(); - advance_lock.unlock(); return std::nullopt; } } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index dbb75416d7..1f2af7a1b5 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -472,16 +472,12 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { } void KernelCore::InvalidateAllInstructionCaches() { - if (!IsMulticore()) { - auto& threads = GlobalScheduler().GetThreadList(); - for (auto& thread : threads) { - if (!thread->IsHLEThread()) { - auto& arm_interface = thread->ArmInterface(); - arm_interface.ClearInstructionCache(); - } + auto& threads = GlobalScheduler().GetThreadList(); + for (auto& thread : threads) { + if (!thread->IsHLEThread()) { + auto& arm_interface = thread->ArmInterface(); + arm_interface.ClearInstructionCache(); } - } else { - UNIMPLEMENTED_MSG("Cache Invalidation unimplemented for multicore"); } } diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index c82c60a16d..c6bbdb080e 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -37,6 +37,10 @@ void PhysicalCore::Shutdown() { scheduler.Shutdown(); } +bool PhysicalCore::IsInterrupted() const { + return interrupt_handler.IsInterrupted(); +} + void PhysicalCore::Interrupt() { guard->lock(); interrupt_handler.SetInterrupt(true); diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 85f6dec055..d7a7a951ca 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -7,8 +7,6 @@ #include #include -#include "core/arm/cpu_interrupt_handler.h" - namespace Common { class SpinLock; } @@ -19,6 +17,7 @@ class Scheduler; namespace Core { class ARM_Interface; +class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -45,9 +44,7 @@ public: void ClearInterrupt(); /// Check if this core is interrupted - bool IsInterrupted() const { - return interrupt_handler.IsInterrupted(); - } + bool IsInterrupted() const; // Shutdown this physical core. void Shutdown(); diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 61b8a396a8..2b12c0dbf4 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -658,7 +658,7 @@ void Scheduler::Reload() { cpu_core.LoadContext(thread->GetContext64()); cpu_core.SetTlsAddress(thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); - cpu_core.ChangeProcessorId(this->core_id); + cpu_core.ChangeProcessorID(this->core_id); cpu_core.ClearExclusiveState(); } } @@ -691,7 +691,7 @@ void Scheduler::SwitchContextStep2() { cpu_core.LoadContext(new_thread->GetContext64()); cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); - cpu_core.ChangeProcessorId(this->core_id); + cpu_core.ChangeProcessorID(this->core_id); cpu_core.ClearExclusiveState(); } } diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 3481071603..b3b4b51699 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -240,6 +240,10 @@ public: return switch_fiber; } + const std::shared_ptr& ControlContext() const { + return switch_fiber; + } + private: friend class GlobalScheduler; diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 12536b6d88..4fd92428f8 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -68,7 +68,7 @@ static void ThreadStart1(u32 id, TestControl1& test_control) { * doing all the work required. */ TEST_CASE("Fibers::Setup", "[common]") { - constexpr u32 num_threads = 7; + constexpr std::size_t num_threads = 7; TestControl1 test_control{}; test_control.thread_fibers.resize(num_threads); test_control.work_fibers.resize(num_threads); -- cgit v1.2.3