// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include #include #include #include #include "common/common_types.h" #include "common/thread.h" #include "common/wall_clock.h" namespace Core::Timing { /// A callback that may be scheduled for a particular core timing event. using TimedCallback = std::function( s64 time, std::chrono::nanoseconds ns_late)>; /// Contains the characteristics of a particular event. struct EventType { explicit EventType(TimedCallback&& callback_, std::string&& name_) : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {} /// The event's callback function. TimedCallback callback; /// A pointer to the name of the event. const std::string name; /// A monotonic sequence number, incremented when this event is /// changed externally. size_t sequence_number; }; enum class UnscheduleEventType { Wait, NoWait, }; class CoreTiming { public: CoreTiming(); ~CoreTiming(); CoreTiming(const CoreTiming&) = delete; CoreTiming(CoreTiming&&) = delete; CoreTiming& operator=(const CoreTiming&) = delete; CoreTiming& operator=(CoreTiming&&) = delete; void Initialize(std::function&& on_thread_init_); void ClearPendingEvents(); void SetMulticore(bool is_multicore_) { is_multicore = is_multicore_; } void Pause(bool is_paused); void SyncPause(bool is_paused); bool IsRunning() const; bool HasStarted() const { return has_started; } bool HasPendingEvents() const; void ScheduleEvent(std::chrono::nanoseconds ns_into_future, const std::shared_ptr& event_type, bool absolute_time = false); void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, std::chrono::nanoseconds resched_time, const std::shared_ptr& event_type, bool absolute_time = false); void UnscheduleEvent(const std::shared_ptr& event_type, UnscheduleEventType type = UnscheduleEventType::Wait); void AddTicks(u64 ticks_to_add); void ResetTicks(); void Idle(); s64 GetDowncount() const { return downcount.load(std::memory_order_relaxed); } u64 GetClockTicks() const; u64 GetGPUTicks() const; std::chrono::microseconds GetGlobalTimeUs() const; std::chrono::nanoseconds GetGlobalTimeNs() const; std::optional Advance(); private: struct Event { s64 time; u64 fifo_order; std::shared_ptr type; bool operator>(const Event& other) const { return std::tie(time, fifo_order) > std::tie(other.time, other.fifo_order); } }; static void ThreadEntry(CoreTiming& instance); void ThreadLoop(); void Reset(); std::unique_ptr clock; std::atomic global_timer{0}; std::vector event_queue; std::atomic event_fifo_id{0}; Common::Event event{}; Common::Event pause_event{}; mutable std::mutex basic_lock; std::mutex advance_lock; std::unique_ptr timer_thread; std::atomic paused{}; std::atomic paused_set{}; std::atomic wait_set{}; std::atomic shutting_down{}; std::atomic has_started{}; std::function on_thread_init{}; bool is_multicore{}; std::atomic pause_end_time{}; std::atomic cpu_ticks{}; std::atomic downcount{}; }; std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback); } // namespace Core::Timing