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.
This commit is contained in:
Takoroka 2026-10-29 00:27:35 -04:00
commit ee9e787fc8
2 changed files with 16 additions and 4 deletions

View file

@ -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));

View file

@ -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 {