diff options
53 files changed, 548 insertions, 412 deletions
| diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 50d2a1ed3..8619a3f03 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<Stream>( -        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 df9607ac7..b07588287 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<Buffer::Tag> 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 00c026511..9a0939883 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<Kernel::WritableEvent> buffer_event)      : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),        effects(params.effect_count) {      audio_out = std::make_unique<AudioCore::AudioOut>(); -    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 7826881bf..201ec7a3c 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<Kernel::WritableEvent> buffer_event);      ~AudioRenderer(); diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 8ab5649df..d89ff30b7 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 caa775544..05071243b 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<void()>; -    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<BufferPtr> queued_buffers;     ///< Buffers queued to be played in the stream      std::queue<BufferPtr> 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 f28951f8a..9b7ca4030 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<u64>(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<ARM_Dynarmic_Callbacks>(*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<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing}, +      core_index{core_index}, core_timing{core_timing},        exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(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 512bf8ce9..6cc458296 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 c36c15c02..a542a098b 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 75761950b..dbd6955ea 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 4d9d21ee4..8aa0932c5 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::seconds>(              std::chrono::system_clock::now().time_since_epoch()); @@ -120,7 +120,7 @@ struct System::Impl {          telemetry_session = std::make_unique<Core::TelemetrySession>();          service_manager = std::make_shared<Service::SM::ServiceManager>(); -        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 511a5ad3a..d720013f7 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 452366250..54aa21a3a 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<ARM_Dynarmic>(exclusive_monitor, core_index); +        arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);  #else          arm_interface = std::make_unique<ARM_Unicorn>();          LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");  #endif      } else { -        arm_interface = std::make_unique<ARM_Unicorn>(); +        arm_interface = std::make_unique<ARM_Unicorn>(core_timing);      }      scheduler = std::make_unique<Kernel::Scheduler>(*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 1d2bdc6cd..e2204c6b0 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> arm_interface;      CpuBarrier& cpu_barrier;      std::unique_ptr<Kernel::Scheduler> scheduler; +    Timing::CoreTiming& core_timing;      std::atomic<bool> reschedule_pending = false;      std::size_t core_index; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 4ea00c277..a0dd5db24 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -8,94 +8,42 @@  #include <mutex>  #include <string>  #include <tuple> -#include <unordered_map> -#include <vector> +  #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; - -struct EventType { -    TimedCallback callback; -    const std::string* name; -}; +constexpr int MAX_SLICE_LENGTH = 20000; -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 -static bool operator>(const Event& left, const Event& right) { -    return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); -} - -static bool operator<(const Event& left, const Event& right) { -    return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); -} - -// 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<std::string, EventType> 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> 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<Event> ts_queue; - -// the queue for unscheduling the events from other threads threadsafe -static Common::MPSCQueue<std::pair<const EventType*, u64>> 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) { -    // 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()); +    // 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); +    } -    auto info = event_types.emplace(name, EventType{callback, nullptr}); -    EventType* event_type = &info.first->second; -    event_type->name = &info.first->first; -    return event_type; -} +    friend bool operator<(const Event& left, const Event& right) { +        return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); +    } +}; -void UnregisterAllEvents() { -    ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); -    event_types.clear(); -} +CoreTiming::CoreTiming() = default; +CoreTiming::~CoreTiming() = default; -void Init() { +void CoreTiming::Initialize() {      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 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. @@ -107,50 +55,51 @@ void Init() {      ev_lost = RegisterEvent("_lost_event", empty_timed_callback);  } -void Shutdown() { +void CoreTiming::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<u64>(global_timer); -    if (!is_global_timer_sane) { -        ticks += slice_length - downcount; -    } -    return ticks; -} - -void AddTicks(u64 ticks) { -    downcount -= static_cast<int>(ticks); -} +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(), +               "CoreTiming Event \"{}\" is already registered. Events should only be registered " +               "during Init to avoid breaking save states.", +               name.c_str()); -u64 GetIdleTicks() { -    return static_cast<u64>(idled_cycles); +    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 ClearPendingEvents() { -    event_queue.clear(); +void CoreTiming::UnregisterAllEvents() { +    ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); +    event_types.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<u64>(global_timer); +    if (!is_global_timer_sane) { +        ticks += slice_length - downcount; +    } +    return ticks; +} + +u64 CoreTiming::GetIdleTicks() const { +    return static_cast<u64>(idled_cycles); +} + +void CoreTiming::AddTicks(u64 ticks) { +    downcount -= static_cast<int>(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<s64>(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<int>(cycles); -        downcount = static_cast<int>(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<int>(cycles); +    downcount = static_cast<int>(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<const EventType*, u64> 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 093989d4c..59163bae1 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -4,92 +4,153 @@  #pragma once -/** - * This is a system to schedule events into the emulated machine's future. Time is measured - * in main CPU clock cycles. - * - * To schedule an event, you first have to register its type. This is where you pass in the - * callback. You then schedule events using the type id you get back. - * - * The int cyclesLate that the callbacks get is how many cycles late it was. - * So to schedule a new event on a regular basis: - * inside callback: - *   ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") - */ -  #include <chrono>  #include <functional>  #include <string> +#include <unordered_map> +#include <vector>  #include "common/common_types.h" +#include "common/threadsafe_queue.h"  namespace Core::Timing { -struct EventType; - +/// A callback that may be scheduled for a particular core timing event.  using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; -/** - * 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(); - -/** - * 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); - -/** - * 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); +/// 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 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); - -/// 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); - -/** 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. + * This is a system to schedule events into the emulated machine's future. Time is measured + * in main CPU clock cycles. + * + * To schedule an event, you first have to register its type. This is where you pass in the + * callback. You then schedule events using the type id you get back. + * + * The int cyclesLate that the callbacks get is how many cycles late it was. + * So to schedule a new event on a regular basis: + * inside callback: + *   ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")   */ -void Advance(); -void MoveEvents(); - -/// Pretend that the main CPU has executed enough cycles to reach the next event. -void Idle(); - -/// Clear all pending events. This should ONLY be done on exit. -void ClearPendingEvents(); - -void ForceExceptionCheck(s64 cycles); - -std::chrono::microseconds GetGlobalTimeUs(); - -int GetDowncount(); +class CoreTiming { +public: +    CoreTiming(); +    ~CoreTiming(); + +    CoreTiming(const CoreTiming&) = delete; +    CoreTiming(CoreTiming&&) = delete; + +    CoreTiming& operator=(const CoreTiming&) = delete; +    CoreTiming& operator=(CoreTiming&&) = delete; + +    /// 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(); + +    /// 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. +    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); + +    /// 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); + +    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); + +    void ForceExceptionCheck(s64 cycles); + +    /// 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; + +    u64 GetIdleTicks() const; + +    void AddTicks(u64 ticks); + +    /// 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(); + +    /// Pretend that the main CPU has executed enough cycles to reach the next event. +    void Idle(); + +    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> 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<std::string, EventType> 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<Event> ts_queue; + +    // The queue for unscheduling the events from other threads threadsafe +    Common::MPSCQueue<std::pair<const EventType*, u64>> 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 769a6fefa..2ddb3610d 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<Cpu>(*exclusive_monitor, *barrier, index); +        cores[index] = +            std::make_unique<Cpu>(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 3721ae8fe..dd749eed4 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<u32> 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 7406f107e..154bced42 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -12,8 +12,9 @@ template <typename T>  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 9e2517e1b..44f30d070 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 5f040f79f..c5d399bab 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 7881c2b90..6661e2130 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<s32> GetNextProcessorId(u64 mask) { @@ -190,6 +192,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name          return ResultCode(-1);      } +    auto& system = Core::System::GetInstance();      SharedPtr<Thread> thread(new Thread(kernel));      thread->thread_id = kernel.CreateNewThreadID(); @@ -198,7 +201,7 @@ ResultVal<SharedPtr<Thread>> 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<SharedPtr<Thread>> 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 dc6a6b188..6831c0735 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 76cc48254..7e0cc64a8 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<AudioCore::AudioRenderer>(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<AudioCore::AudioRenderer>(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 f0e092b1b..5e5097a03 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 b264c9503..c5c2e032a 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 68b734248..929035034 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 6d21f1a7d..a179252e3 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 1056ffbcd..f305fe90f 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 798f30436..92d7bfb52 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 f52775456..73cd2c7bb 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 4985037be..11ab096d9 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 70b654d07..9d46eecbe 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 ffdd1c593..e7fc7a619 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<s32>(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 106cf58c8..18c7a94e6 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 cca4dca1d..946948f5e 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 4a21c643e..24469f03e 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 a7c8acc72..1a8445a43 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 94cd0eba9..012b6e0dd 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 eff03d14e..1a9da9576 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 ff836989f..2864e6617 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 79c320d04..8a6de83a2 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<Controller_Stubbed>(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<IActiveVibrationDeviceList> { diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index b427d4068..2c4625c99 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<u64>(Core::Timing::GetTicks()); +    rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks());      rb.PushRaw<u32>(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 88d80ba06..45812d238 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 <cstring>  #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<u8>& input, std::vector<u8>& 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 ce1b59860..3babc3f7c 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<u64>(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<Nvidia::Module> instance) { diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 6d8bcbd30..437aa592d 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 d25b80ab0..117f87a45 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::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) { +void Init(std::shared_ptr<SM::ServiceManager>& 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<NVFlinger::NVFlinger>(); +    auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());      SM::ServiceManager::InstallInterfaces(sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 029533628..830790269 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::ServiceManager>& sm, FileSys::VfsFilesystem& vfs); +void Init(std::shared_ptr<SM::ServiceManager>& 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 efebd1b24..aa115935d 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -5,6 +5,7 @@  #include <chrono>  #include <ctime>  #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 77607a755..340d6a272 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()); - -    AdvanceAndCheck(3, 400); -    AdvanceAndCheck(1, 300); -    AdvanceAndCheck(2, 200); -    AdvanceAndCheck(0, 200); -    AdvanceAndCheck(4, MAX_SLICE_LENGTH); +    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(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()); - -    AdvanceAndCheck(3, 400); -    AdvanceAndCheck(1, 300); -    AdvanceAndCheck(2, 200); -    AdvanceAndCheck(0, 200); -    AdvanceAndCheck(4, MAX_SLICE_LENGTH); +    core_timing.ForceExceptionCheck(1200); +    REQUIRE(100 == core_timing.GetDowncount()); + +    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<Core::Timing::EventType*>(userdata), -                                    userdata); +        core_timing.ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(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<u64>(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<u64>(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 19b6b14b2..86ede5faa 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 3d00c308b..b86265dfe 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 cca2ed708..d40666ac6 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 | 
