diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index cf4e159806..72da1904e1 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -39,6 +39,8 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten auto current_context = context.Acquire(); CommandDataContainer next; + // When a pending channel last moved or a command arrived (see the polling below). + auto last_progress = std::chrono::steady_clock::now(); namespace Profile = VideoCommon::Nx2Profile; while (!stop_token.stop_requested()) { Profile::Report(); @@ -49,8 +51,16 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten scheduler.ResumePending(system.GPU()); } if (!state.queue.TryPop(next)) { + // The CPU usually releases the semaphore soon after: poll for half a + // millisecond before sleeping, or every wait also waits out a host sleep + // (title 1 waits ~25 times a second). Profile::Scope scope{Profile::Zone::PendingSleep}; - std::this_thread::sleep_for(std::chrono::milliseconds{1}); + if (std::chrono::steady_clock::now() - last_progress < + std::chrono::microseconds{500}) { + std::this_thread::yield(); + } else { + std::this_thread::sleep_for(std::chrono::milliseconds{1}); + } continue; } } else { @@ -60,6 +70,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten if (stop_token.stop_requested()) { break; } + last_progress = std::chrono::steady_clock::now(); if (auto* submit_list = std::get_if(&next.data)) { Profile::Scope scope{Profile::Zone::Push}; scheduler.Push(system.GPU(), submit_list->channel, std::move(submit_list->entries)); diff --git a/src/video_core/nx2_profile.h b/src/video_core/nx2_profile.h index 051403c982..a162ce16a3 100644 --- a/src/video_core/nx2_profile.h +++ b/src/video_core/nx2_profile.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "common/logging.h" +#include "common/cpu_features.h" // EDEN_NX2_GPU_PROFILE: time where the GPU thread spends its time. Each zone counts its own // (exclusive) time, nested zones excluded; every 10 s the thread logs each zone's share. @@ -61,10 +62,10 @@ inline bool Enabled() { return enabled; } +// Nanoseconds; from the host's own counter where the wall clock reads it directly (the zones +// run ~150,000 times a second, and QueryPerformanceCounter cost ~5% of the GPU thread). inline u64 Now() { - return static_cast(std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count()); + return static_cast(Common::g_wall_clock.GetTimeNS().count()); } struct ThreadData {