From 048b8aef5856117214839a75e7decb8b7cb1f91e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:28:35 -0700 Subject: [PATCH] videoout: Make present thread realtime on macOS. (#990) --- src/common/thread.cpp | 43 ++++++++++++++++++++++++++ src/common/thread.h | 3 ++ src/core/libraries/videoout/driver.cpp | 6 ++-- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/common/thread.cpp b/src/common/thread.cpp index f08b36faae..d1b2254727 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -9,6 +9,7 @@ #include "common/thread.h" #ifdef __APPLE__ #include +#include #include #elif defined(_WIN32) #include @@ -31,6 +32,48 @@ namespace Common { +#ifdef __APPLE__ + +void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) { + // CPU time to grant. + const std::chrono::nanoseconds computation_ns = period_ns / 2; + + // Determine the timebase for converting time to ticks. + struct mach_timebase_info timebase {}; + mach_timebase_info(&timebase); + const auto ticks_per_ns = + static_cast(timebase.denom) / static_cast(timebase.numer); + + const auto period_ticks = + static_cast(static_cast(period_ns.count()) * ticks_per_ns); + const auto computation_ticks = + static_cast(static_cast(computation_ns.count()) * ticks_per_ns); + + thread_time_constraint_policy policy = { + .period = period_ticks, + .computation = computation_ticks, + // Should not matter since preemptible is false, but needs to be >= computation regardless. + .constraint = computation_ticks, + .preemptible = false, + }; + + int ret = thread_policy_set( + pthread_mach_thread_np(pthread_self()), THREAD_TIME_CONSTRAINT_POLICY, + reinterpret_cast(&policy), THREAD_TIME_CONSTRAINT_POLICY_COUNT); + if (ret != KERN_SUCCESS) { + LOG_ERROR(Common, "Could not set thread to real-time with period {} ns: {}", + period_ns.count(), ret); + } +} + +#else + +void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) { + // Not implemented +} + +#endif + #ifdef _WIN32 void SetCurrentThreadPriority(ThreadPriority new_priority) { diff --git a/src/common/thread.h b/src/common/thread.h index 39acc1db55..3ee60c72fc 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/types.h" namespace Common { @@ -16,6 +17,8 @@ enum class ThreadPriority : u32 { Critical = 4, }; +void SetCurrentThreadRealtime(std::chrono::nanoseconds period_ns); + void SetCurrentThreadPriority(ThreadPriority new_priority); void SetCurrentThreadName(const char* name); diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 8fcdd118be..f04fb505db 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -261,8 +261,11 @@ void VideoOutDriver::SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_ } void VideoOutDriver::PresentThread(std::stop_token token) { - static constexpr std::chrono::milliseconds VblankPeriod{16}; + static constexpr std::chrono::nanoseconds VblankPeriod{16666667}; + const auto vblank_period = VblankPeriod / Config::vblankDiv(); + Common::SetCurrentThreadName("PresentThread"); + Common::SetCurrentThreadRealtime(vblank_period); const auto receive_request = [this] -> Request { std::scoped_lock lk{mutex}; @@ -274,7 +277,6 @@ void VideoOutDriver::PresentThread(std::stop_token token) { return {}; }; - auto vblank_period = VblankPeriod / Config::vblankDiv(); auto delay = std::chrono::microseconds{0}; while (!token.stop_requested()) { // Sleep for most of the vblank duration.