aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 00:27:35 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 00:27:35 -0400
commit934eeb0e6d82458048c94361b0d7b2db1b89f041 (patch)
treeac13dba71747bb4f406bb823722c8fd931e6ff0d /src
parent863d3afa90f2dfde8e715b45f28fc75c1b472739 (diff)
nx2: poll pending channels briefly before sleeping; profile zones on the TSC
- A channel waiting on a semaphore the CPU releases slept 1 ms per poll (~25 waits a second in title 1); poll for 0.5 ms first. - The GPU profile zones read the wall clock (the TSC) instead of QueryPerformanceCounter, which cost ~5% of the GPU thread when on.
Diffstat (limited to 'src')
-rw-r--r--src/video_core/gpu_thread.cpp13
-rw-r--r--src/video_core/nx2_profile.h7
2 files changed, 16 insertions, 4 deletions
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<SubmitListCommand>(&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<u64>(std::chrono::duration_cast<std::chrono::nanoseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count());
+ return static_cast<u64>(Common::g_wall_clock.GetTimeNS().count());
}
struct ThreadData {