From bd983414f643b734a1f8bebe3183723733344f72 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Thu, 14 Feb 2019 12:42:58 -0500 Subject: [PATCH] core_timing: Convert core timing into a class Gets rid of the largest set of mutable global state within the core. This also paves a way for eliminating usages of GetInstance() on the System class as a follow-up. Note that no behavioral changes have been made, and this simply extracts the functionality into a class. This also has the benefit of making dependencies on the core timing functionality explicit within the relevant interfaces. --- src/audio_core/audio_out.cpp | 5 +- src/audio_core/audio_out.h | 8 +- src/audio_core/audio_renderer.cpp | 7 +- src/audio_core/audio_renderer.h | 6 +- src/audio_core/stream.cpp | 11 +- src/audio_core/stream.h | 8 +- src/core/arm/dynarmic/arm_dynarmic.cpp | 12 +- src/core/arm/dynarmic/arm_dynarmic.h | 8 +- src/core/arm/unicorn/arm_unicorn.cpp | 6 +- src/core/arm/unicorn/arm_unicorn.h | 8 +- src/core/core.cpp | 19 +- src/core/core.h | 10 + src/core/core_cpu.cpp | 15 +- src/core/core_cpu.h | 8 +- src/core/core_timing.cpp | 203 ++++++++--------- src/core/core_timing.h | 177 ++++++++++----- src/core/cpu_core_manager.cpp | 3 +- src/core/hle/kernel/kernel.cpp | 12 +- src/core/hle/kernel/kernel.h | 9 +- src/core/hle/kernel/scheduler.cpp | 2 +- src/core/hle/kernel/svc.cpp | 10 +- src/core/hle/kernel/thread.cpp | 19 +- src/core/hle/service/audio/audout_u.cpp | 10 +- src/core/hle/service/audio/audren_u.cpp | 9 +- .../service/hid/controllers/controller_base.h | 7 +- .../hle/service/hid/controllers/debug_pad.cpp | 5 +- .../hle/service/hid/controllers/debug_pad.h | 2 +- .../hle/service/hid/controllers/gesture.cpp | 5 +- .../hle/service/hid/controllers/gesture.h | 2 +- .../hle/service/hid/controllers/keyboard.cpp | 5 +- .../hle/service/hid/controllers/keyboard.h | 2 +- .../hle/service/hid/controllers/mouse.cpp | 5 +- src/core/hle/service/hid/controllers/mouse.h | 2 +- src/core/hle/service/hid/controllers/npad.cpp | 5 +- src/core/hle/service/hid/controllers/npad.h | 2 +- .../hle/service/hid/controllers/stubbed.cpp | 5 +- .../hle/service/hid/controllers/stubbed.h | 2 +- .../service/hid/controllers/touchscreen.cpp | 7 +- .../hle/service/hid/controllers/touchscreen.h | 2 +- src/core/hle/service/hid/controllers/xpad.cpp | 5 +- src/core/hle/service/hid/controllers/xpad.h | 2 +- src/core/hle/service/hid/hid.cpp | 18 +- src/core/hle/service/hid/irs.cpp | 2 +- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 3 +- src/core/hle/service/nvflinger/nvflinger.cpp | 10 +- src/core/hle/service/nvflinger/nvflinger.h | 8 +- src/core/hle/service/service.cpp | 5 +- src/core/hle/service/service.h | 15 +- src/core/hle/service/time/time.cpp | 7 +- src/tests/core/core_timing.cpp | 211 +++++++++--------- src/video_core/engines/maxwell_3d.cpp | 2 +- src/video_core/gpu.cpp | 3 +- .../renderer_opengl/renderer_opengl.cpp | 2 +- 53 files changed, 536 insertions(+), 400 deletions(-) diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 50d2a1ed35..8619a3f030 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp @@ -26,14 +26,15 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) { return {}; } -StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, +StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, + u32 num_channels, std::string&& name, Stream::ReleaseCallback&& release_callback) { if (!sink) { sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id); } return std::make_shared( - sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback), + core_timing, sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback), sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); } diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h index df9607ac74..b07588287c 100644 --- a/src/audio_core/audio_out.h +++ b/src/audio_core/audio_out.h @@ -13,6 +13,10 @@ #include "audio_core/stream.h" #include "common/common_types.h" +namespace Core::Timing { +class CoreTiming; +} + namespace AudioCore { /** @@ -21,8 +25,8 @@ namespace AudioCore { class AudioOut { public: /// Opens a new audio stream - StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, - Stream::ReleaseCallback&& release_callback); + StreamPtr OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, u32 num_channels, + std::string&& name, Stream::ReleaseCallback&& release_callback); /// Returns a vector of recently released buffers specified by tag for the specified stream std::vector GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count); diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 00c0265110..9a0939883c 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -8,6 +8,7 @@ #include "audio_core/codec.h" #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/kernel/writable_event.h" #include "core/memory.h" @@ -71,14 +72,14 @@ private: EffectOutStatus out_status{}; EffectInStatus info{}; }; -AudioRenderer::AudioRenderer(AudioRendererParameter params, +AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params, Kernel::SharedPtr buffer_event) : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), effects(params.effect_count) { audio_out = std::make_unique(); - stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", - [=]() { buffer_event->Signal(); }); + stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, + "AudioRenderer", [=]() { buffer_event->Signal(); }); audio_out->StartStream(stream); QueueMixedBuffer(0); diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 7826881bf5..201ec7a3ca 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -14,6 +14,10 @@ #include "common/swap.h" #include "core/hle/kernel/object.h" +namespace Core::Timing { +class CoreTiming; +} + namespace Kernel { class WritableEvent; } @@ -208,7 +212,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size class AudioRenderer { public: - AudioRenderer(AudioRendererParameter params, + AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params, Kernel::SharedPtr buffer_event); ~AudioRenderer(); diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 8ab5649df4..d89ff30b72 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -32,12 +32,12 @@ u32 Stream::GetNumChannels() const { return {}; } -Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, - SinkStream& sink_stream, std::string&& name_) +Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format, + ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_) : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, - sink_stream{sink_stream}, name{std::move(name_)} { + sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { - release_event = Core::Timing::RegisterEvent( + release_event = core_timing.RegisterEvent( name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); } @@ -99,8 +99,7 @@ void Stream::PlayNextBuffer() { sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); - Core::Timing::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, - {}); + core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); } void Stream::ReleaseActiveBuffer() { diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index caa775544d..05071243b1 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -14,8 +14,9 @@ #include "common/common_types.h" namespace Core::Timing { +class CoreTiming; struct EventType; -} +} // namespace Core::Timing namespace AudioCore { @@ -42,8 +43,8 @@ public: /// Callback function type, used to change guest state on a buffer being released using ReleaseCallback = std::function; - Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, - SinkStream& sink_stream, std::string&& name_); + Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format, + ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_); /// Plays the audio stream void Play(); @@ -100,6 +101,7 @@ private: std::queue queued_buffers; ///< Buffers queued to be played in the stream std::queue released_buffers; ///< Buffers recently released from the stream SinkStream& sink_stream; ///< Output sink for the stream + Core::Timing::CoreTiming& core_timing; ///< Core timing instance. std::string name; ///< Name of the stream, must be unique }; diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index f28951f8a2..9b7ca40308 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -112,14 +112,14 @@ public: // Always execute at least one tick. amortized_ticks = std::max(amortized_ticks, 1); - Timing::AddTicks(amortized_ticks); + parent.core_timing.AddTicks(amortized_ticks); num_interpreted_instructions = 0; } u64 GetTicksRemaining() override { - return std::max(Timing::GetDowncount(), 0); + return std::max(parent.core_timing.GetDowncount(), 0); } u64 GetCNTPCT() override { - return Timing::GetTicks(); + return parent.core_timing.GetTicks(); } ARM_Dynarmic& parent; @@ -172,8 +172,10 @@ void ARM_Dynarmic::Step() { cb->InterpreterFallback(jit->GetPC(), 1); } -ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : cb(std::make_unique(*this)), core_index{core_index}, +ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : cb(std::make_unique(*this)), inner_unicorn{core_timing}, + core_index{core_index}, core_timing{core_timing}, exclusive_monitor{dynamic_cast(exclusive_monitor)} { ThreadContext ctx{}; inner_unicorn.SaveContext(ctx); diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 512bf8ce97..6cc458296e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -16,6 +16,10 @@ namespace Memory { struct PageTable; } +namespace Core::Timing { +class CoreTiming; +} + namespace Core { class ARM_Dynarmic_Callbacks; @@ -23,7 +27,8 @@ class DynarmicExclusiveMonitor; class ARM_Dynarmic final : public ARM_Interface { public: - ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index); + ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index); ~ARM_Dynarmic(); void MapBackingMemory(VAddr address, std::size_t size, u8* memory, @@ -62,6 +67,7 @@ private: ARM_Unicorn inner_unicorn; std::size_t core_index; + Timing::CoreTiming& core_timing; DynarmicExclusiveMonitor& exclusive_monitor; Memory::PageTable* current_page_table = nullptr; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index c36c15c024..a542a098b8 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -72,7 +72,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return {}; } -ARM_Unicorn::ARM_Unicorn() { +ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} { CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); auto fpv = 3 << 20; @@ -177,7 +177,7 @@ void ARM_Unicorn::Run() { if (GDBStub::IsServerEnabled()) { ExecuteInstructions(std::max(4000000, 0)); } else { - ExecuteInstructions(std::max(Timing::GetDowncount(), 0)); + ExecuteInstructions(std::max(core_timing.GetDowncount(), 0)); } } @@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); void ARM_Unicorn::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit_Unicorn); CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); - Timing::AddTicks(num_instructions); + core_timing.AddTicks(num_instructions); if (GDBStub::IsServerEnabled()) { if (last_bkpt_hit) { uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 75761950b5..dbd6955ea0 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -9,12 +9,17 @@ #include "core/arm/arm_interface.h" #include "core/gdbstub/gdbstub.h" +namespace Core::Timing { +class CoreTiming; +} + namespace Core { class ARM_Unicorn final : public ARM_Interface { public: - ARM_Unicorn(); + explicit ARM_Unicorn(Timing::CoreTiming& core_timing); ~ARM_Unicorn(); + void MapBackingMemory(VAddr address, std::size_t size, u8* memory, Kernel::VMAPermission perms) override; void UnmapMemory(VAddr address, std::size_t size) override; @@ -43,6 +48,7 @@ public: private: uc_engine* uc{}; + Timing::CoreTiming& core_timing; GDBStub::BreakpointAddress last_bkpt{}; bool last_bkpt_hit; }; diff --git a/src/core/core.cpp b/src/core/core.cpp index 4d9d21ee42..8aa0932c51 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -94,8 +94,8 @@ struct System::Impl { ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { LOG_DEBUG(HW_Memory, "initialized OK"); - Timing::Init(); - kernel.Initialize(); + core_timing.Initialize(); + kernel.Initialize(core_timing); const auto current_time = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()); @@ -120,7 +120,7 @@ struct System::Impl { telemetry_session = std::make_unique(); service_manager = std::make_shared(); - Service::Init(service_manager, *virtual_filesystem); + Service::Init(service_manager, system, *virtual_filesystem); GDBStub::Init(); renderer = VideoCore::CreateRenderer(emu_window, system); @@ -205,7 +205,7 @@ struct System::Impl { // Shutdown kernel and core timing kernel.Shutdown(); - Timing::Shutdown(); + core_timing.Shutdown(); // Close app loader app_loader.reset(); @@ -232,9 +232,10 @@ struct System::Impl { } PerfStatsResults GetAndResetPerfStats() { - return perf_stats.GetAndResetStats(Timing::GetGlobalTimeUs()); + return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs()); } + Timing::CoreTiming core_timing; Kernel::KernelCore kernel; /// RealVfsFilesystem instance FileSys::VirtualFilesystem virtual_filesystem; @@ -396,6 +397,14 @@ const Kernel::KernelCore& System::Kernel() const { return impl->kernel; } +Timing::CoreTiming& System::CoreTiming() { + return impl->core_timing; +} + +const Timing::CoreTiming& System::CoreTiming() const { + return impl->core_timing; +} + Core::PerfStats& System::GetPerfStats() { return impl->perf_stats; } diff --git a/src/core/core.h b/src/core/core.h index 511a5ad3ae..d720013f73 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -47,6 +47,10 @@ namespace VideoCore { class RendererBase; } // namespace VideoCore +namespace Core::Timing { +class CoreTiming; +} + namespace Core { class ARM_Interface; @@ -205,6 +209,12 @@ public: /// Provides a constant pointer to the current process. const Kernel::Process* CurrentProcess() const; + /// Provides a reference to the core timing instance. + Timing::CoreTiming& CoreTiming(); + + /// Provides a constant reference to the core timing instance. + const Timing::CoreTiming& CoreTiming() const; + /// Provides a reference to the kernel instance. Kernel::KernelCore& Kernel(); diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 452366250a..54aa21a3ae 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -49,17 +49,18 @@ bool CpuBarrier::Rendezvous() { return false; } -Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index) - : cpu_barrier{cpu_barrier}, core_index{core_index} { +Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, + CpuBarrier& cpu_barrier, std::size_t core_index) + : cpu_barrier{cpu_barrier}, core_timing{core_timing}, core_index{core_index} { if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 - arm_interface = std::make_unique(exclusive_monitor, core_index); + arm_interface = std::make_unique(core_timing, exclusive_monitor, core_index); #else arm_interface = std::make_unique(); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif } else { - arm_interface = std::make_unique(); + arm_interface = std::make_unique(core_timing); } scheduler = std::make_unique(*arm_interface); @@ -93,14 +94,14 @@ void Cpu::RunLoop(bool tight_loop) { if (IsMainCore()) { // TODO(Subv): Only let CoreTiming idle if all 4 cores are idling. - Timing::Idle(); - Timing::Advance(); + core_timing.Idle(); + core_timing.Advance(); } PrepareReschedule(); } else { if (IsMainCore()) { - Timing::Advance(); + core_timing.Advance(); } if (tight_loop) { diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index 1d2bdc6cd3..e2204c6b01 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h @@ -15,6 +15,10 @@ namespace Kernel { class Scheduler; } +namespace Core::Timing { +class CoreTiming; +} + namespace Core { class ARM_Interface; @@ -41,7 +45,8 @@ private: class Cpu { public: - Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index); + Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, + CpuBarrier& cpu_barrier, std::size_t core_index); ~Cpu(); void RunLoop(bool tight_loop = true); @@ -82,6 +87,7 @@ private: std::unique_ptr arm_interface; CpuBarrier& cpu_barrier; std::unique_ptr scheduler; + Timing::CoreTiming& core_timing; std::atomic reschedule_pending = false; std::size_t core_index; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 4ea00c277b..a0dd5db242 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -8,69 +8,60 @@ #include #include #include -#include -#include + #include "common/assert.h" #include "common/thread.h" -#include "common/threadsafe_queue.h" #include "core/core_timing_util.h" namespace Core::Timing { -static s64 global_timer; -static int slice_length; -static int downcount; +constexpr int MAX_SLICE_LENGTH = 20000; -struct EventType { - TimedCallback callback; - const std::string* name; -}; - -struct Event { +struct CoreTiming::Event { s64 time; u64 fifo_order; u64 userdata; const EventType* type; + + // Sort by time, unless the times are the same, in which case sort by + // the order added to the queue + friend bool operator>(const Event& left, const Event& right) { + return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); + } + + friend bool operator<(const Event& left, const Event& right) { + return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); + } }; -// Sort by time, unless the times are the same, in which case sort by the order added to the queue -static bool operator>(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); +CoreTiming::CoreTiming() = default; +CoreTiming::~CoreTiming() = default; + +void CoreTiming::Initialize() { + downcount = MAX_SLICE_LENGTH; + slice_length = MAX_SLICE_LENGTH; + global_timer = 0; + idled_cycles = 0; + + // 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; + + event_fifo_id = 0; + + const auto empty_timed_callback = [](u64, s64) {}; + ev_lost = RegisterEvent("_lost_event", empty_timed_callback); } -static bool operator<(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); +void CoreTiming::Shutdown() { + MoveEvents(); + ClearPendingEvents(); + UnregisterAllEvents(); } -// unordered_map stores each element separately as a linked list node so pointers to elements -// remain stable regardless of rehashes/resizing. -static std::unordered_map event_types; - -// The queue is a min-heap using std::make_heap/push_heap/pop_heap. -// We don't use std::priority_queue because we need to be able to serialize, unserialize and -// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated -// by the standard adaptor class. -static std::vector event_queue; -static u64 event_fifo_id; -// the queue for storing the events from other threads threadsafe until they will be added -// to the event_queue by the emu thread -static Common::MPSCQueue ts_queue; - -// the queue for unscheduling the events from other threads threadsafe -static Common::MPSCQueue> unschedule_queue; - -constexpr int MAX_SLICE_LENGTH = 20000; - -static s64 idled_cycles; - -// Are we in a function that has been called from Advance() -// If events are sheduled from a function that gets called from Advance(), -// don't change slice_length and downcount. -static bool is_global_timer_sane; - -static EventType* ev_lost = nullptr; - -EventType* RegisterEvent(const std::string& name, TimedCallback callback) { +EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { // check for existing type with same name. // we want event type names to remain unique so that we can use them for serialization. ASSERT_MSG(event_types.find(name) == event_types.end(), @@ -84,73 +75,31 @@ EventType* RegisterEvent(const std::string& name, TimedCallback callback) { return event_type; } -void UnregisterAllEvents() { +void CoreTiming::UnregisterAllEvents() { ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); event_types.clear(); } -void Init() { - downcount = MAX_SLICE_LENGTH; - slice_length = MAX_SLICE_LENGTH; - global_timer = 0; - idled_cycles = 0; - - // The time between CoreTiming being intialized 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; - - event_fifo_id = 0; - - const auto empty_timed_callback = [](u64, s64) {}; - ev_lost = RegisterEvent("_lost_event", empty_timed_callback); -} - -void Shutdown() { - MoveEvents(); - ClearPendingEvents(); - UnregisterAllEvents(); -} - -// This should only be called from the CPU thread. If you are calling -// it from any other thread, you are doing something evil -u64 GetTicks() { - u64 ticks = static_cast(global_timer); - if (!is_global_timer_sane) { - ticks += slice_length - downcount; - } - return ticks; -} - -void AddTicks(u64 ticks) { - downcount -= static_cast(ticks); -} - -u64 GetIdleTicks() { - return static_cast(idled_cycles); -} - -void ClearPendingEvents() { - event_queue.clear(); -} - -void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { +void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { ASSERT(event_type != nullptr); - s64 timeout = GetTicks() + cycles_into_future; + const s64 timeout = GetTicks() + cycles_into_future; + // If this event needs to be scheduled before the next advance(), force one early - if (!is_global_timer_sane) + if (!is_global_timer_sane) { ForceExceptionCheck(cycles_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<>()); } -void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { +void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, + u64 userdata) { ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type}); } -void UnscheduleEvent(const EventType* event_type, u64 userdata) { - auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { +void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type == event_type && e.userdata == userdata; }); @@ -161,13 +110,33 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) { } } -void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { +void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { unschedule_queue.Push(std::make_pair(event_type, userdata)); } -void RemoveEvent(const EventType* event_type) { - auto itr = std::remove_if(event_queue.begin(), event_queue.end(), - [&](const Event& e) { return e.type == event_type; }); +u64 CoreTiming::GetTicks() const { + u64 ticks = static_cast(global_timer); + if (!is_global_timer_sane) { + ticks += slice_length - downcount; + } + return ticks; +} + +u64 CoreTiming::GetIdleTicks() const { + return static_cast(idled_cycles); +} + +void CoreTiming::AddTicks(u64 ticks) { + downcount -= static_cast(ticks); +} + +void CoreTiming::ClearPendingEvents() { + event_queue.clear(); +} + +void CoreTiming::RemoveEvent(const EventType* event_type) { + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), + [&](const Event& e) { return e.type == event_type; }); // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { @@ -176,22 +145,24 @@ void RemoveEvent(const EventType* event_type) { } } -void RemoveNormalAndThreadsafeEvent(const EventType* event_type) { +void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) { MoveEvents(); RemoveEvent(event_type); } -void ForceExceptionCheck(s64 cycles) { +void CoreTiming::ForceExceptionCheck(s64 cycles) { cycles = std::max(0, cycles); - if (downcount > cycles) { - // 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 - slice_length -= downcount - static_cast(cycles); - downcount = static_cast(cycles); + if (downcount <= 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 + slice_length -= downcount - static_cast(cycles); + downcount = static_cast(cycles); } -void MoveEvents() { +void CoreTiming::MoveEvents() { for (Event ev; ts_queue.Pop(ev);) { ev.fifo_order = event_fifo_id++; event_queue.emplace_back(std::move(ev)); @@ -199,13 +170,13 @@ void MoveEvents() { } } -void Advance() { +void CoreTiming::Advance() { MoveEvents(); for (std::pair ev; unschedule_queue.Pop(ev);) { UnscheduleEvent(ev.first, ev.second); } - int cycles_executed = slice_length - downcount; + const int cycles_executed = slice_length - downcount; global_timer += cycles_executed; slice_length = MAX_SLICE_LENGTH; @@ -229,16 +200,16 @@ void Advance() { downcount = slice_length; } -void Idle() { +void CoreTiming::Idle() { idled_cycles += downcount; downcount = 0; } -std::chrono::microseconds GetGlobalTimeUs() { +std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE}; } -int GetDowncount() { +int CoreTiming::GetDowncount() const { return downcount; } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 093989d4c3..59163bae1c 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -4,6 +4,27 @@ #pragma once +#include +#include +#include +#include +#include +#include "common/common_types.h" +#include "common/threadsafe_queue.h" + +namespace Core::Timing { + +/// A callback that may be scheduled for a particular core timing event. +using TimedCallback = std::function; + +/// Contains the characteristics of a particular event. +struct EventType { + /// The event's callback function. + TimedCallback callback; + /// A pointer to the name of the event. + const std::string* name; +}; + /** * This is a system to schedule events into the emulated machine's future. Time is measured * in main CPU clock cycles. @@ -16,80 +37,120 @@ * inside callback: * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") */ +class CoreTiming { +public: + CoreTiming(); + ~CoreTiming(); -#include -#include -#include -#include "common/common_types.h" + CoreTiming(const CoreTiming&) = delete; + CoreTiming(CoreTiming&&) = delete; -namespace Core::Timing { + CoreTiming& operator=(const CoreTiming&) = delete; + CoreTiming& operator=(CoreTiming&&) = delete; -struct EventType; + /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is + /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. + void Initialize(); -using TimedCallback = std::function; + /// Tears down all timing related functionality. + void Shutdown(); -/** - * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is - * required to end slice -1 and start slice 0 before the first cycle of code is executed. - */ -void Init(); -void Shutdown(); + /// Registers a core timing event with the given name and callback. + /// + /// @param name The name of the core timing event to register. + /// @param callback The callback to execute for the event. + /// + /// @returns An EventType instance representing the registered event. + /// + /// @pre The name of the event being registered must be unique among all + /// registered events. + /// + EventType* RegisterEvent(const std::string& name, TimedCallback callback); -/** - * This should only be called from the emu thread, if you are calling it any other thread, you are - * doing something evil - */ -u64 GetTicks(); -u64 GetIdleTicks(); -void AddTicks(u64 ticks); + /// Unregisters all registered events thus far. + void UnregisterAllEvents(); -/** - * Returns the event_type identifier. if name is not unique, it will assert. - */ -EventType* RegisterEvent(const std::string& name, TimedCallback callback); -void UnregisterAllEvents(); + /// After the first Advance, the slice lengths and the downcount will be reduced whenever an + /// event is scheduled earlier than the current values. + /// + /// Scheduling from a callback will not update the downcount until the Advance() completes. + void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); -/** - * After the first Advance, the slice lengths and the downcount will be reduced whenever an event - * is scheduled earlier than the current values. - * Scheduling from a callback will not update the downcount until the Advance() completes. - */ -void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); + /// This is to be called when outside of hle threads, such as the graphics thread, wants to + /// schedule things to be executed on the main thread. + /// + /// @note This doesn't change slice_length and thus events scheduled by this might be + /// called with a delay of up to MAX_SLICE_LENGTH + void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, + u64 userdata = 0); -/** - * This is to be called when outside of hle threads, such as the graphics thread, wants to - * schedule things to be executed on the main thread. - * Not that this doesn't change slice_length and thus events scheduled by this might be called - * with a delay of up to MAX_SLICE_LENGTH - */ -void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); + void UnscheduleEvent(const EventType* event_type, u64 userdata); + void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata); -void UnscheduleEvent(const EventType* event_type, u64 userdata); -void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata); + /// We only permit one event of each type in the queue at a time. + void RemoveEvent(const EventType* event_type); + void RemoveNormalAndThreadsafeEvent(const EventType* event_type); -/// We only permit one event of each type in the queue at a time. -void RemoveEvent(const EventType* event_type); -void RemoveNormalAndThreadsafeEvent(const EventType* event_type); + void ForceExceptionCheck(s64 cycles); -/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends - * the previous timing slice and begins the next one, you must Advance from the previous - * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an - * Advance() is required to initialize the slice length before the first cycle of emulated - * instructions is executed. - */ -void Advance(); -void MoveEvents(); + /// This should only be called from the emu thread, if you are calling it any other thread, + /// you are doing something evil + u64 GetTicks() const; -/// Pretend that the main CPU has executed enough cycles to reach the next event. -void Idle(); + u64 GetIdleTicks() const; -/// Clear all pending events. This should ONLY be done on exit. -void ClearPendingEvents(); + void AddTicks(u64 ticks); -void ForceExceptionCheck(s64 cycles); + /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends + /// the previous timing slice and begins the next one, you must Advance from the previous + /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an + /// Advance() is required to initialize the slice length before the first cycle of emulated + /// instructions is executed. + void Advance(); -std::chrono::microseconds GetGlobalTimeUs(); + /// Pretend that the main CPU has executed enough cycles to reach the next event. + void Idle(); -int GetDowncount(); + std::chrono::microseconds GetGlobalTimeUs() const; + + int GetDowncount() const; + +private: + struct Event; + + /// Clear all pending events. This should ONLY be done on exit. + void ClearPendingEvents(); + void MoveEvents(); + + s64 global_timer = 0; + s64 idled_cycles = 0; + int slice_length = 0; + int downcount = 0; + + // Are we in a function that has been called from Advance() + // If events are scheduled from a function that gets called from Advance(), + // don't change slice_length and downcount. + bool is_global_timer_sane = false; + + // The queue is a min-heap using std::make_heap/push_heap/pop_heap. + // We don't use std::priority_queue because we need to be able to serialize, unserialize and + // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't + // accomodated by the standard adaptor class. + std::vector event_queue; + u64 event_fifo_id = 0; + + // Stores each element separately as a linked list node so pointers to elements + // remain stable regardless of rehashes/resizing. + std::unordered_map event_types; + + // The queue for storing the events from other threads threadsafe until they will be added + // to the event_queue by the emu thread + Common::MPSCQueue ts_queue; + + // The queue for unscheduling the events from other threads threadsafe + Common::MPSCQueue> unschedule_queue; + + EventType* ev_lost = nullptr; +}; } // namespace Core::Timing diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp index 769a6fefaf..2ddb3610d2 100644 --- a/src/core/cpu_core_manager.cpp +++ b/src/core/cpu_core_manager.cpp @@ -27,7 +27,8 @@ void CpuCoreManager::Initialize(System& system) { exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); for (std::size_t index = 0; index < cores.size(); ++index) { - cores[index] = std::make_unique(*exclusive_monitor, *barrier, index); + cores[index] = + std::make_unique(system.CoreTiming(), *exclusive_monitor, *barrier, index); } // Create threads for CPU cores 1-3, and build thread_to_cpu map diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 3721ae8fe6..dd749eed48 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -86,11 +86,11 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_ } struct KernelCore::Impl { - void Initialize(KernelCore& kernel) { + void Initialize(KernelCore& kernel, Core::Timing::CoreTiming& core_timing) { Shutdown(); InitializeSystemResourceLimit(kernel); - InitializeThreads(); + InitializeThreads(core_timing); } void Shutdown() { @@ -122,9 +122,9 @@ struct KernelCore::Impl { ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); } - void InitializeThreads() { + void InitializeThreads(Core::Timing::CoreTiming& core_timing) { thread_wakeup_event_type = - Core::Timing::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); + core_timing.RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); } std::atomic next_object_id{0}; @@ -152,8 +152,8 @@ KernelCore::~KernelCore() { Shutdown(); } -void KernelCore::Initialize() { - impl->Initialize(*this); +void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) { + impl->Initialize(*this, core_timing); } void KernelCore::Shutdown() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 7406f107e4..154bced42b 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -12,8 +12,9 @@ template class ResultVal; namespace Core::Timing { +class CoreTiming; struct EventType; -} +} // namespace Core::Timing namespace Kernel { @@ -39,7 +40,11 @@ public: KernelCore& operator=(KernelCore&&) = delete; /// Resets the kernel to a clean slate for use. - void Initialize(); + /// + /// @param core_timing CoreTiming instance used to create any necessary + /// kernel-specific callback events. + /// + void Initialize(Core::Timing::CoreTiming& core_timing); /// Clears all resources in use by the kernel instance. void Shutdown(); diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 9e2517e1b6..44f30d0704 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -111,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) { void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { const u64 prev_switch_ticks = last_context_switch_time; - const u64 most_recent_switch_ticks = Core::Timing::GetTicks(); + const u64 most_recent_switch_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; if (thread != nullptr) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5f040f79fd..c5d399babf 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -918,6 +918,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) } const auto& system = Core::System::GetInstance(); + const auto& core_timing = system.CoreTiming(); const auto& scheduler = system.CurrentScheduler(); const auto* const current_thread = scheduler.GetCurrentThread(); const bool same_thread = current_thread == thread; @@ -927,9 +928,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); - out_ticks = thread_ticks + (Core::Timing::GetTicks() - prev_ctx_ticks); + out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { - out_ticks = Core::Timing::GetTicks() - prev_ctx_ticks; + out_ticks = core_timing.GetTicks() - prev_ctx_ticks; } *result = out_ticks; @@ -1546,10 +1547,11 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to static u64 GetSystemTick() { LOG_TRACE(Kernel_SVC, "called"); - const u64 result{Core::Timing::GetTicks()}; + auto& core_timing = Core::System::GetInstance().CoreTiming(); + const u64 result{core_timing.GetTicks()}; // Advance time to defeat dumb games that busy-wait for the frame to end. - Core::Timing::AddTicks(400); + core_timing.AddTicks(400); return result; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 7881c2b908..6661e21306 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -43,7 +43,8 @@ Thread::~Thread() = default; void Thread::Stop() { // Cancel any outstanding wakeup events for this thread - Core::Timing::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle); + Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), + callback_handle); kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); callback_handle = 0; @@ -85,13 +86,14 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { // This function might be called from any thread so we have to be cautious and use the // thread-safe version of ScheduleEvent. - Core::Timing::ScheduleEventThreadsafe(Core::Timing::nsToCycles(nanoseconds), - kernel.ThreadWakeupCallbackEventType(), callback_handle); + Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe( + Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(), + callback_handle); } void Thread::CancelWakeupTimer() { - Core::Timing::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), - callback_handle); + Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe( + kernel.ThreadWakeupCallbackEventType(), callback_handle); } static std::optional GetNextProcessorId(u64 mask) { @@ -190,6 +192,7 @@ ResultVal> Thread::Create(KernelCore& kernel, std::string name return ResultCode(-1); } + auto& system = Core::System::GetInstance(); SharedPtr thread(new Thread(kernel)); thread->thread_id = kernel.CreateNewThreadID(); @@ -198,7 +201,7 @@ ResultVal> Thread::Create(KernelCore& kernel, std::string name thread->stack_top = stack_top; thread->tpidr_el0 = 0; thread->nominal_priority = thread->current_priority = priority; - thread->last_running_ticks = Core::Timing::GetTicks(); + thread->last_running_ticks = system.CoreTiming().GetTicks(); thread->processor_id = processor_id; thread->ideal_core = processor_id; thread->affinity_mask = 1ULL << processor_id; @@ -209,7 +212,7 @@ ResultVal> Thread::Create(KernelCore& kernel, std::string name thread->name = std::move(name); thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); thread->owner_process = &owner_process; - thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id); + thread->scheduler = &system.Scheduler(processor_id); thread->scheduler->AddThread(thread, priority); thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); @@ -258,7 +261,7 @@ void Thread::SetStatus(ThreadStatus new_status) { } if (status == ThreadStatus::Running) { - last_running_ticks = Core::Timing::GetTicks(); + last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); } status = new_status; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index dc6a6b1885..6831c07355 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -68,12 +68,12 @@ public: RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released - auto& kernel = Core::System::GetInstance().Kernel(); - buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, - "IAudioOutBufferReleased"); + auto& system = Core::System::GetInstance(); + buffer_event = Kernel::WritableEvent::CreateEventPair( + system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); - stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, - std::move(unique_name), + stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, + audio_params.channel_count, std::move(unique_name), [=]() { buffer_event.writable->Signal(); }); } diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 76cc48254a..7e0cc64a81 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -42,10 +42,11 @@ public: // clang-format on RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); - system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, - "IAudioRenderer:SystemEvent"); - renderer = std::make_unique(audren_params, system_event.writable); + auto& system = Core::System::GetInstance(); + system_event = Kernel::WritableEvent::CreateEventPair( + system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); + renderer = std::make_unique(system.CoreTiming(), audren_params, + system_event.writable); } private: diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index f0e092b1be..5e5097a039 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -7,6 +7,10 @@ #include "common/common_types.h" #include "common/swap.h" +namespace Core::Timing { +class CoreTiming; +} + namespace Service::HID { class ControllerBase { public: @@ -20,7 +24,8 @@ public: virtual void OnRelease() = 0; // When the controller is requesting an update for the shared memory - virtual void OnUpdate(u8* data, std::size_t size) = 0; + virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) = 0; // Called when input devices should be loaded virtual void OnLoadInputDevices() = 0; diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index b264c95036..c5c2e032af 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -21,8 +21,9 @@ void Controller_DebugPad::OnInit() {} void Controller_DebugPad::OnRelease() {} -void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = Core::Timing::GetTicks(); +void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 68b734248e..9290350342 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -26,7 +26,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 6d21f1a7dd..a179252e34 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -17,8 +17,9 @@ void Controller_Gesture::OnInit() {} void Controller_Gesture::OnRelease() {} -void Controller_Gesture::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = Core::Timing::GetTicks(); +void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 1056ffbcdf..f305fe90f2 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -22,7 +22,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 798f304365..92d7bfb52c 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -19,8 +19,9 @@ void Controller_Keyboard::OnInit() {} void Controller_Keyboard::OnRelease() {} -void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = Core::Timing::GetTicks(); +void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index f527754561..73cd2c7bba 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -25,7 +25,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 4985037be5..11ab096d9b 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -17,8 +17,9 @@ Controller_Mouse::~Controller_Mouse() = default; void Controller_Mouse::OnInit() {} void Controller_Mouse::OnRelease() {} -void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = Core::Timing::GetTicks(); +void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 70b654d074..9d46eecbe3 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -24,7 +24,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index ffdd1c5937..e7fc7a6197 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -288,7 +288,8 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { rstick_entry.y = static_cast(stick_r_y_f * HID_JOYSTICK_MAX); } -void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { +void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t data_len) { if (!IsControllerActivated()) return; for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { @@ -308,7 +309,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { const auto& last_entry = main_controller->npad[main_controller->common.last_entry_index]; - main_controller->common.timestamp = Core::Timing::GetTicks(); + main_controller->common.timestamp = core_timing.GetTicks(); main_controller->common.last_entry_index = (main_controller->common.last_entry_index + 1) % 17; diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 106cf58c81..18c7a94e6a 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -30,7 +30,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index cca4dca1d1..946948f5ec 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -16,13 +16,14 @@ void Controller_Stubbed::OnInit() {} void Controller_Stubbed::OnRelease() {} -void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) { +void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { if (!smart_update) { return; } CommonHeader header{}; - header.timestamp = Core::Timing::GetTicks(); + header.timestamp = core_timing.GetTicks(); header.total_entry_count = 17; header.entry_count = 0; header.last_entry_index = 0; diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 4a21c643e4..24469f03e4 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -20,7 +20,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index a7c8acc727..1a8445a430 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -20,8 +20,9 @@ void Controller_Touchscreen::OnInit() {} void Controller_Touchscreen::OnRelease() {} -void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = Core::Timing::GetTicks(); +void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { @@ -48,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; - const u64 tick = Core::Timing::GetTicks(); + const u64 tick = core_timing.GetTicks(); touch_entry.delta_time = tick - last_touch; last_touch = tick; touch_entry.finger = Settings::values.touchscreen.finger; diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 94cd0eba9a..012b6e0dd8 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -24,7 +24,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index eff03d14ea..1a9da95767 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -17,9 +17,10 @@ void Controller_XPad::OnInit() {} void Controller_XPad::OnRelease() {} -void Controller_XPad::OnUpdate(u8* data, std::size_t size) { +void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { for (auto& xpad_entry : shared_memory.shared_memory_entries) { - xpad_entry.header.timestamp = Core::Timing::GetTicks(); + xpad_entry.header.timestamp = core_timing.GetTicks(); xpad_entry.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index ff836989f2..2864e6617e 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -22,7 +22,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 79c320d041..8a6de83a2e 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -73,13 +73,15 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { GetController(HidController::Unknown3).SetCommonHeaderOffset(0x5000); // Register update callbacks - pad_update_event = Core::Timing::RegisterEvent( - "HID::UpdatePadCallback", - [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); }); + auto& core_timing = Core::System::GetInstance().CoreTiming(); + pad_update_event = + core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) { + UpdateControllers(userdata, cycles_late); + }); // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) - Core::Timing::ScheduleEvent(pad_update_ticks, pad_update_event); + core_timing.ScheduleEvent(pad_update_ticks, pad_update_event); ReloadInputDevices(); } @@ -93,7 +95,7 @@ void IAppletResource::DeactivateController(HidController controller) { } IAppletResource ::~IAppletResource() { - Core::Timing::UnscheduleEvent(pad_update_event, 0); + Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0); } void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { @@ -105,15 +107,17 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { } void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) { + auto& core_timing = Core::System::GetInstance().CoreTiming(); + const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); for (const auto& controller : controllers) { if (should_reload) { controller->OnLoadInputDevices(); } - controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE); + controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); } - Core::Timing::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); + core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); } class IActiveVibrationDeviceList final : public ServiceFramework { diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index b427d40682..2c4625c991 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 5}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(Core::Timing::GetTicks()); + rb.PushRaw(Core::System::GetInstance().CoreTiming().GetTicks()); rb.PushRaw(0); } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 88d80ba069..45812d2389 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -5,6 +5,7 @@ #include #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" @@ -184,7 +185,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector& input, std::vector& o IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); - params.gpu_time = Core::Timing::cyclesToNs(Core::Timing::GetTicks()); + params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index ce1b598606..3babc3f7cf 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -27,19 +27,19 @@ namespace Service::NVFlinger { constexpr std::size_t SCREEN_REFRESH_RATE = 60; constexpr u64 frame_ticks = static_cast(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); -NVFlinger::NVFlinger() { +NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { // Schedule the screen composition events composition_event = - Core::Timing::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { + core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { Compose(); - Core::Timing::ScheduleEvent(frame_ticks - cycles_late, composition_event); + this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); }); - Core::Timing::ScheduleEvent(frame_ticks, composition_event); + core_timing.ScheduleEvent(frame_ticks, composition_event); } NVFlinger::~NVFlinger() { - Core::Timing::UnscheduleEvent(composition_event, 0); + core_timing.UnscheduleEvent(composition_event, 0); } void NVFlinger::SetNVDrvInstance(std::shared_ptr instance) { diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 6d8bcbd30e..437aa592dd 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -15,8 +15,9 @@ #include "core/hle/kernel/object.h" namespace Core::Timing { +class CoreTiming; struct EventType; -} +} // namespace Core::Timing namespace Kernel { class ReadableEvent; @@ -52,7 +53,7 @@ struct Display { class NVFlinger final { public: - NVFlinger(); + explicit NVFlinger(Core::Timing::CoreTiming& core_timing); ~NVFlinger(); /// Sets the NVDrv module instance to use to send buffers to the GPU. @@ -117,6 +118,9 @@ private: /// Event that handles screen composition. Core::Timing::EventType* composition_event; + + /// Core timing instance for registering/unregistering the composition event. + Core::Timing::CoreTiming& core_timing; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index d25b80ab09..117f87a457 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -194,10 +194,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co // Module interface /// Initialize ServiceManager -void Init(std::shared_ptr& sm, FileSys::VfsFilesystem& vfs) { +void Init(std::shared_ptr& sm, Core::System& system, + FileSys::VfsFilesystem& vfs) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. - auto nv_flinger = std::make_shared(); + auto nv_flinger = std::make_shared(system.CoreTiming()); SM::ServiceManager::InstallInterfaces(sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 0295336287..830790269a 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -14,6 +14,14 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service +namespace Core { +class System; +} + +namespace FileSys { +class VfsFilesystem; +} + namespace Kernel { class ClientPort; class ServerPort; @@ -21,10 +29,6 @@ class ServerSession; class HLERequestContext; } // namespace Kernel -namespace FileSys { -class VfsFilesystem; -} - namespace Service { namespace SM { @@ -178,7 +182,8 @@ private: }; /// Initialize ServiceManager -void Init(std::shared_ptr& sm, FileSys::VfsFilesystem& vfs); +void Init(std::shared_ptr& sm, Core::System& system, + FileSys::VfsFilesystem& vfs); /// Shutdown ServiceManager void Shutdown(); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index efebd1b241..aa115935d8 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -5,6 +5,7 @@ #include #include #include "common/logging/log.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/ipc_helpers.h" @@ -106,8 +107,9 @@ private: void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); + const auto& core_timing = Core::System::GetInstance().CoreTiming(); const SteadyClockTimePoint steady_clock_time_point{ - Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000}; + Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000}; IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; rb.Push(RESULT_SUCCESS); rb.PushRaw(steady_clock_time_point); @@ -281,8 +283,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { return; } + const auto& core_timing = Core::System::GetInstance().CoreTiming(); const SteadyClockTimePoint steady_clock_time_point{ - Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000, {}}; + Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}}; CalendarTime calendar_time{}; calendar_time.year = tm->tm_year + 1900; diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 77607a7558..340d6a272f 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -28,100 +28,103 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) { REQUIRE(lateness == cycles_late); } -class ScopeInit final { -public: +struct ScopeInit final { ScopeInit() { - Core::Timing::Init(); + core_timing.Initialize(); } ~ScopeInit() { - Core::Timing::Shutdown(); + core_timing.Shutdown(); } + + Core::Timing::CoreTiming core_timing; }; -static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0, - int cpu_downcount = 0) { +static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, int downcount, + int expected_lateness = 0, int cpu_downcount = 0) { callbacks_ran_flags = 0; expected_callback = CB_IDS[idx]; lateness = expected_lateness; // Pretend we executed X cycles of instructions. - Core::Timing::AddTicks(Core::Timing::GetDowncount() - cpu_downcount); - Core::Timing::Advance(); + core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount); + core_timing.Advance(); REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); - REQUIRE(downcount == Core::Timing::GetDowncount()); + REQUIRE(downcount == core_timing.GetDowncount()); } TEST_CASE("CoreTiming[BasicOrder]", "[core]") { ScopeInit guard; + auto& core_timing = guard.core_timing; - Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); - Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); - Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>); - Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>); - Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>); + Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); + Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>); + Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>); + Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>); // Enter slice 0 - Core::Timing::Advance(); + core_timing.Advance(); // D -> B -> C -> A -> E - Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]); - REQUIRE(1000 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEvent(500, cb_b, CB_IDS[1]); - REQUIRE(500 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEvent(800, cb_c, CB_IDS[2]); - REQUIRE(500 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEvent(100, cb_d, CB_IDS[3]); - REQUIRE(100 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEvent(1200, cb_e, CB_IDS[4]); - REQUIRE(100 == Core::Timing::GetDowncount()); + core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); + REQUIRE(1000 == core_timing.GetDowncount()); + core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]); + REQUIRE(500 == core_timing.GetDowncount()); + core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]); + REQUIRE(500 == core_timing.GetDowncount()); + core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]); + REQUIRE(100 == core_timing.GetDowncount()); + core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); + REQUIRE(100 == core_timing.GetDowncount()); - AdvanceAndCheck(3, 400); - AdvanceAndCheck(1, 300); - AdvanceAndCheck(2, 200); - AdvanceAndCheck(0, 200); - AdvanceAndCheck(4, MAX_SLICE_LENGTH); + AdvanceAndCheck(core_timing, 3, 400); + AdvanceAndCheck(core_timing, 1, 300); + AdvanceAndCheck(core_timing, 2, 200); + AdvanceAndCheck(core_timing, 0, 200); + AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH); } TEST_CASE("CoreTiming[Threadsave]", "[core]") { ScopeInit guard; + auto& core_timing = guard.core_timing; - Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); - Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); - Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>); - Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>); - Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>); + Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); + Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>); + Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>); + Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>); // Enter slice 0 - Core::Timing::Advance(); + core_timing.Advance(); // D -> B -> C -> A -> E - Core::Timing::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); + core_timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); // Manually force since ScheduleEventThreadsafe doesn't call it - Core::Timing::ForceExceptionCheck(1000); - REQUIRE(1000 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); + core_timing.ForceExceptionCheck(1000); + REQUIRE(1000 == core_timing.GetDowncount()); + core_timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); // Manually force since ScheduleEventThreadsafe doesn't call it - Core::Timing::ForceExceptionCheck(500); - REQUIRE(500 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); + core_timing.ForceExceptionCheck(500); + REQUIRE(500 == core_timing.GetDowncount()); + core_timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); // Manually force since ScheduleEventThreadsafe doesn't call it - Core::Timing::ForceExceptionCheck(800); - REQUIRE(500 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); + core_timing.ForceExceptionCheck(800); + REQUIRE(500 == core_timing.GetDowncount()); + core_timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); // Manually force since ScheduleEventThreadsafe doesn't call it - Core::Timing::ForceExceptionCheck(100); - REQUIRE(100 == Core::Timing::GetDowncount()); - Core::Timing::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); + core_timing.ForceExceptionCheck(100); + REQUIRE(100 == core_timing.GetDowncount()); + core_timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); // Manually force since ScheduleEventThreadsafe doesn't call it - Core::Timing::ForceExceptionCheck(1200); - REQUIRE(100 == Core::Timing::GetDowncount()); + core_timing.ForceExceptionCheck(1200); + REQUIRE(100 == core_timing.GetDowncount()); - AdvanceAndCheck(3, 400); - AdvanceAndCheck(1, 300); - AdvanceAndCheck(2, 200); - AdvanceAndCheck(0, 200); - AdvanceAndCheck(4, MAX_SLICE_LENGTH); + AdvanceAndCheck(core_timing, 3, 400); + AdvanceAndCheck(core_timing, 1, 300); + AdvanceAndCheck(core_timing, 2, 200); + AdvanceAndCheck(core_timing, 0, 200); + AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH); } namespace SharedSlotTest { @@ -142,59 +145,62 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") { using namespace SharedSlotTest; ScopeInit guard; + auto& core_timing = guard.core_timing; - Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", FifoCallback<0>); - Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", FifoCallback<1>); - Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", FifoCallback<2>); - Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", FifoCallback<3>); - Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", FifoCallback<4>); + Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", FifoCallback<0>); + Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", FifoCallback<1>); + Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", FifoCallback<2>); + Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", FifoCallback<3>); + Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", FifoCallback<4>); - Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]); - Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]); - Core::Timing::ScheduleEvent(1000, cb_c, CB_IDS[2]); - Core::Timing::ScheduleEvent(1000, cb_d, CB_IDS[3]); - Core::Timing::ScheduleEvent(1000, cb_e, CB_IDS[4]); + core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); + core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); + core_timing.ScheduleEvent(1000, cb_c, CB_IDS[2]); + core_timing.ScheduleEvent(1000, cb_d, CB_IDS[3]); + core_timing.ScheduleEvent(1000, cb_e, CB_IDS[4]); // Enter slice 0 - Core::Timing::Advance(); - REQUIRE(1000 == Core::Timing::GetDowncount()); + core_timing.Advance(); + REQUIRE(1000 == core_timing.GetDowncount()); callbacks_ran_flags = 0; counter = 0; lateness = 0; - Core::Timing::AddTicks(Core::Timing::GetDowncount()); - Core::Timing::Advance(); - REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount()); + core_timing.AddTicks(core_timing.GetDowncount()); + core_timing.Advance(); + REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount()); REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong()); } TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { ScopeInit guard; + auto& core_timing = guard.core_timing; - Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); - Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); + Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); // Enter slice 0 - Core::Timing::Advance(); + core_timing.Advance(); - Core::Timing::ScheduleEvent(100, cb_a, CB_IDS[0]); - Core::Timing::ScheduleEvent(200, cb_b, CB_IDS[1]); + core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]); + core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]); - AdvanceAndCheck(0, 90, 10, -10); // (100 - 10) - AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50); + AdvanceAndCheck(core_timing, 0, 90, 10, -10); // (100 - 10) + AdvanceAndCheck(core_timing, 1, MAX_SLICE_LENGTH, 50, -50); } namespace ChainSchedulingTest { static int reschedules = 0; -static void RescheduleCallback(u64 userdata, s64 cycles_late) { +static void RescheduleCallback(Core::Timing::CoreTiming& core_timing, u64 userdata, + s64 cycles_late) { --reschedules; REQUIRE(reschedules >= 0); REQUIRE(lateness == cycles_late); if (reschedules > 0) { - Core::Timing::ScheduleEvent(1000, reinterpret_cast(userdata), - userdata); + core_timing.ScheduleEvent(1000, reinterpret_cast(userdata), + userdata); } } } // namespace ChainSchedulingTest @@ -203,36 +209,39 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { using namespace ChainSchedulingTest; ScopeInit guard; + auto& core_timing = guard.core_timing; - Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); - Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); - Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>); - Core::Timing::EventType* cb_rs = - Core::Timing::RegisterEvent("callbackReschedule", RescheduleCallback); + Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); + Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); + Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>); + Core::Timing::EventType* cb_rs = core_timing.RegisterEvent( + "callbackReschedule", [&core_timing](u64 userdata, s64 cycles_late) { + RescheduleCallback(core_timing, userdata, cycles_late); + }); // Enter slice 0 - Core::Timing::Advance(); + core_timing.Advance(); - Core::Timing::ScheduleEvent(800, cb_a, CB_IDS[0]); - Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]); - Core::Timing::ScheduleEvent(2200, cb_c, CB_IDS[2]); - Core::Timing::ScheduleEvent(1000, cb_rs, reinterpret_cast(cb_rs)); - REQUIRE(800 == Core::Timing::GetDowncount()); + core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]); + core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); + core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]); + core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast(cb_rs)); + REQUIRE(800 == core_timing.GetDowncount()); reschedules = 3; - AdvanceAndCheck(0, 200); // cb_a - AdvanceAndCheck(1, 1000); // cb_b, cb_rs + AdvanceAndCheck(core_timing, 0, 200); // cb_a + AdvanceAndCheck(core_timing, 1, 1000); // cb_b, cb_rs REQUIRE(2 == reschedules); - Core::Timing::AddTicks(Core::Timing::GetDowncount()); - Core::Timing::Advance(); // cb_rs + core_timing.AddTicks(core_timing.GetDowncount()); + core_timing.Advance(); // cb_rs REQUIRE(1 == reschedules); - REQUIRE(200 == Core::Timing::GetDowncount()); + REQUIRE(200 == core_timing.GetDowncount()); - AdvanceAndCheck(2, 800); // cb_c + AdvanceAndCheck(core_timing, 2, 800); // cb_c - Core::Timing::AddTicks(Core::Timing::GetDowncount()); - Core::Timing::Advance(); // cb_rs + core_timing.AddTicks(core_timing.GetDowncount()); + core_timing.Advance(); // cb_rs REQUIRE(0 == reschedules); - REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount()); + REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount()); } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 19b6b14b22..86ede5faa6 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -317,7 +317,7 @@ void Maxwell3D::ProcessQueryGet() { LongQueryResult query_result{}; query_result.value = result; // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming - query_result.timestamp = Core::Timing::GetTicks(); + query_result.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); Memory::WriteBlock(*address, &query_result, sizeof(query_result)); } dirty_flags.OnMemoryWrite(); diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 3d00c308bf..b86265dfee 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/assert.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/memory.h" #include "video_core/engines/fermi_2d.h" @@ -283,7 +284,7 @@ void GPU::ProcessSemaphoreTriggerMethod() { block.sequence = regs.semaphore_sequence; // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of // CoreTiming - block.timestamp = Core::Timing::GetTicks(); + block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); Memory::WriteBlock(*address, &block, sizeof(block)); } else { const auto address = diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index cca2ed7081..d40666ac66 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -137,7 +137,7 @@ void RendererOpenGL::SwapBuffers( render_window.PollEvents(); - system.FrameLimiter().DoFrameLimiting(Core::Timing::GetGlobalTimeUs()); + system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); // Restore the rasterizer state