diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 6a5f53a57b..4ca98f8ea7 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -37,7 +37,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { - release_event = core_timing.RegisterEvent( + release_event = Core::Timing::CreateEvent( name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); }); } diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 8106cea433..1708a4d989 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -98,18 +98,19 @@ private: /// Gets the number of core cycles when the specified buffer will be released s64 GetBufferReleaseCycles(const Buffer& buffer) const; - u32 sample_rate; ///< Sample rate of the stream - Format format; ///< Format of the stream - float game_volume = 1.0f; ///< The volume the game currently has set - ReleaseCallback release_callback; ///< Buffer release callback for the stream - State state{State::Stopped}; ///< Playback state of the stream - Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream - BufferPtr active_buffer; ///< Actively playing buffer in the stream - 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 + u32 sample_rate; ///< Sample rate of the stream + Format format; ///< Format of the stream + float game_volume = 1.0f; ///< The volume the game currently has set + ReleaseCallback release_callback; ///< Buffer release callback for the stream + State state{State::Stopped}; ///< Playback state of the stream + std::shared_ptr + release_event; ///< Core timing release event for the stream + BufferPtr active_buffer; ///< Actively playing buffer in the stream + 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 }; using StreamPtr = std::shared_ptr; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 0e95706856..aa09fa4538 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -17,11 +17,15 @@ 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 fifo_order; u64 userdata; - const EventType* type; + std::weak_ptr type; // Sort by time, unless the times are the same, in which case sort by // the order added to the queue @@ -54,36 +58,15 @@ void CoreTiming::Initialize() { event_fifo_id = 0; const auto empty_timed_callback = [](u64, s64) {}; - ev_lost = RegisterEvent("_lost_event", empty_timed_callback); + ev_lost = CreateEvent("_lost_event", empty_timed_callback); } void CoreTiming::Shutdown() { ClearPendingEvents(); - UnregisterAllEvents(); } -EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { - std::lock_guard guard{inner_mutex}; - // 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(), - "CoreTiming Event \"{}\" is already registered. Events should only be registered " - "during Init to avoid breaking save states.", - name.c_str()); - - auto info = event_types.emplace(name, EventType{callback, nullptr}); - EventType* event_type = &info.first->second; - event_type->name = &info.first->first; - return event_type; -} - -void CoreTiming::UnregisterAllEvents() { - ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); - event_types.clear(); -} - -void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { - ASSERT(event_type != nullptr); +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; @@ -93,13 +76,15 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty } event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } -void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { +void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { std::lock_guard guard{inner_mutex}; + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { - return e.type == event_type && e.userdata == userdata; + return e.type.lock().get() == event_type.get() && e.userdata == userdata; }); // Removing random items breaks the invariant so we have to re-establish it. @@ -130,10 +115,12 @@ void CoreTiming::ClearPendingEvents() { event_queue.clear(); } -void CoreTiming::RemoveEvent(const EventType* event_type) { +void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { std::lock_guard guard{inner_mutex}; - const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), - [&](const Event& e) { return e.type == event_type; }); + + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { + return e.type.lock().get() == event_type.get(); + }); // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { @@ -181,7 +168,11 @@ void CoreTiming::Advance() { std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); inner_mutex.unlock(); - evt.type->callback(evt.userdata, global_timer - evt.time); + + if (auto event_type{evt.type.lock()}) { + event_type->callback(evt.userdata, global_timer - evt.time); + } + inner_mutex.lock(); } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 3bb88c810e..d50f4eb8a7 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -6,11 +6,12 @@ #include #include +#include #include #include #include -#include #include + #include "common/common_types.h" #include "common/threadsafe_queue.h" @@ -21,10 +22,13 @@ using TimedCallback = std::function; /// Contains the characteristics of a particular event. struct EventType { + EventType(TimedCallback&& callback, std::string&& name) + : callback{std::move(callback)}, name{std::move(name)} {} + /// The event's callback function. TimedCallback callback; /// A pointer to the name of the event. - const std::string* name; + const std::string name; }; /** @@ -57,31 +61,17 @@ public: /// Tears down all timing related functionality. 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); - - /// Unregisters all registered events thus far. Note: not thread unsafe - 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); + void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr& event_type, + u64 userdata = 0); - void UnscheduleEvent(const EventType* event_type, u64 userdata); + void UnscheduleEvent(const std::shared_ptr& 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 RemoveEvent(const std::shared_ptr& event_type); void ForceExceptionCheck(s64 cycles); @@ -148,13 +138,18 @@ private: 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; - - EventType* ev_lost = nullptr; + std::shared_ptr ev_lost; std::mutex inner_mutex; }; +/// Creates a core timing event with the given name and callback. +/// +/// @param name The name of the core timing event to create. +/// @param callback The callback to execute for the event. +/// +/// @returns An EventType instance representing the created event. +/// +std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback); + } // namespace Core::Timing diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index c2115db2de..c629d9fa10 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp @@ -11,13 +11,12 @@ namespace Core::Hardware { InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { - gpu_interrupt_event = - system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) { - auto nvdrv = system.ServiceManager().GetService("nvdrv"); - const u32 syncpt = static_cast(message >> 32); - const u32 value = static_cast(message); - nvdrv->SignalGPUInterruptSyncpt(syncpt, value); - }); + gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) { + auto nvdrv = system.ServiceManager().GetService("nvdrv"); + const u32 syncpt = static_cast(message >> 32); + const u32 value = static_cast(message); + nvdrv->SignalGPUInterruptSyncpt(syncpt, value); + }); } InterruptManager::~InterruptManager() = default; diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h index 494db883ad..5fa306ae0a 100644 --- a/src/core/hardware_interrupt_manager.h +++ b/src/core/hardware_interrupt_manager.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "common/common_types.h" namespace Core { @@ -25,7 +27,7 @@ public: private: Core::System& system; - Core::Timing::EventType* gpu_interrupt_event{}; + std::shared_ptr gpu_interrupt_event; }; } // namespace Core::Hardware diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 63ad079505..a9851113a9 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -139,12 +139,12 @@ struct KernelCore::Impl { void InitializeThreads() { thread_wakeup_event_type = - system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); + Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); } void InitializePreemption() { - preemption_event = system.CoreTiming().RegisterEvent( - "PreemptionCallback", [this](u64 userdata, s64 cycles_late) { + preemption_event = + Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) { global_scheduler.PreemptThreads(); s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); system.CoreTiming().ScheduleEvent(time_interval, preemption_event); @@ -166,8 +166,9 @@ struct KernelCore::Impl { std::shared_ptr system_resource_limit; - Core::Timing::EventType* thread_wakeup_event_type = nullptr; - Core::Timing::EventType* preemption_event = nullptr; + std::shared_ptr thread_wakeup_event_type; + std::shared_ptr preemption_event; + // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, // allowing us to simply use a pool index or similar. Kernel::HandleTable thread_wakeup_callback_handle_table; @@ -269,7 +270,7 @@ u64 KernelCore::CreateNewUserProcessID() { return impl->next_user_process_id++; } -Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const { +const std::shared_ptr& KernelCore::ThreadWakeupCallbackEventType() const { return impl->thread_wakeup_event_type; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c74b9078fe..babb531c64 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -113,7 +113,7 @@ private: u64 CreateNewThreadID(); /// Retrieves the event type used for thread wakeup callbacks. - Core::Timing::EventType* ThreadWakeupCallbackEventType() const; + const std::shared_ptr& ThreadWakeupCallbackEventType() const; /// Provides a reference to the thread wakeup callback handle table. Kernel::HandleTable& ThreadWakeupCallbackHandleTable(); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 8ef029e0f1..89bf8b815b 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -77,15 +77,14 @@ IAppletResource::IAppletResource(Core::System& system) GetController(HidController::Unknown3).SetCommonHeaderOffset(0x5000); // Register update callbacks - auto& core_timing = system.CoreTiming(); pad_update_event = - core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { + Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { UpdateControllers(userdata, cycles_late); }); // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) - core_timing.ScheduleEvent(pad_update_ticks, pad_update_event); + system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event); ReloadInputDevices(); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 923762fff8..ad20f147ce 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -69,7 +69,7 @@ private: std::shared_ptr shared_mem; - Core::Timing::EventType* pad_update_event; + std::shared_ptr pad_update_event; Core::System& system; std::array, static_cast(HidController::MaxControllers)> diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index cd07ab3622..52623cf897 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -37,8 +37,8 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { displays.emplace_back(4, "Null", system); // Schedule the screen composition events - composition_event = system.CoreTiming().RegisterEvent( - "ScreenComposition", [this](u64 userdata, s64 cycles_late) { + composition_event = + Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { Compose(); const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 9cc41f2e6f..e3cc14bdca 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -103,7 +103,7 @@ private: u32 swap_interval = 1; /// Event that handles screen composition. - Core::Timing::EventType* composition_event; + std::shared_ptr composition_event; Core::System& system; }; diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 10821d4527..b73cc9fbd9 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -186,7 +186,7 @@ CheatEngine::~CheatEngine() { } void CheatEngine::Initialize() { - event = core_timing.RegisterEvent( + event = Core::Timing::CreateEvent( "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index 0f012e9b5c..e3db90dacc 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "common/common_types.h" #include "core/memory/dmnt_cheat_types.h" @@ -78,7 +79,7 @@ private: std::vector cheats; std::atomic_bool is_pending_reload{false}; - Core::Timing::EventType* event{}; + std::shared_ptr event; Core::Timing::CoreTiming& core_timing; Core::System& system; }; diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 17f050068a..19b531ecb0 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -54,7 +54,7 @@ void MemoryWriteWidth(u32 width, VAddr addr, u64 value) { } // Anonymous namespace Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) { - event = core_timing.RegisterEvent( + event = Core::Timing::CreateEvent( "MemoryFreezer::FrameCallback", [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index b58de5472c..90b1a885c1 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -75,7 +76,7 @@ private: mutable std::mutex entries_mutex; std::vector entries; - Core::Timing::EventType* event; + std::shared_ptr event; Core::Timing::CoreTiming& core_timing; }; diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 3443bf05e9..1e39408011 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include + #include "common/file_util.h" #include "core/core.h" #include "core/core_timing.h" @@ -65,11 +67,16 @@ 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>); + std::shared_ptr cb_a = + Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); + std::shared_ptr cb_b = + Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); + std::shared_ptr cb_c = + Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>); + std::shared_ptr cb_d = + Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>); + std::shared_ptr cb_e = + Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>); // Enter slice 0 core_timing.ResetRun(); @@ -99,8 +106,8 @@ TEST_CASE("CoreTiming[FairSharing]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; - Core::Timing::EventType* empty_callback = - core_timing.RegisterEvent("empty_callback", EmptyCallback); + std::shared_ptr empty_callback = + Core::Timing::CreateEvent("empty_callback", EmptyCallback); callbacks_done = 0; u64 MAX_CALLBACKS = 10; @@ -133,8 +140,10 @@ 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>); + std::shared_ptr cb_a = + Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); + std::shared_ptr cb_b = + Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); // Enter slice 0 core_timing.ResetRun(); @@ -145,60 +154,3 @@ TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) AdvanceAndCheck(core_timing, 1, 1, 50, -50); } - -namespace ChainSchedulingTest { -static int reschedules = 0; - -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); - } -} -} // namespace ChainSchedulingTest - -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", [&core_timing](u64 userdata, s64 cycles_late) { - RescheduleCallback(core_timing, userdata, cycles_late); - }); - - // Enter slice 0 - core_timing.ResetRun(); - - 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(core_timing, 0, 0); // cb_a - AdvanceAndCheck(core_timing, 1, 1); // cb_b, cb_rs - REQUIRE(2 == reschedules); - - core_timing.AddTicks(core_timing.GetDowncount()); - core_timing.Advance(); // cb_rs - core_timing.SwitchContext(3); - REQUIRE(1 == reschedules); - REQUIRE(200 == core_timing.GetDowncount()); - - AdvanceAndCheck(core_timing, 2, 3); // cb_c - - core_timing.AddTicks(core_timing.GetDowncount()); - core_timing.Advance(); // cb_rs - REQUIRE(0 == reschedules); -}