Implement more detailed diagnostics for OpenXR stages

This commit is contained in:
iChris4 committed 2026-09-20 17:24:16 +02:00
1 parent c4c7078afc
commit 787dbcf7d2
8 files changed
+212 -34

No files matched your search

+3 -1
View File
@@ -316,7 +316,9 @@ public:
submission_success_ = false;
submission_unsafe_ = false;
}
if (!aurora_d3d12_set_stereo_targets(frame.xr_frame.serial, targets.data(), target_count)) {
if (!diagnostics::Measure(diagnostics::Stage::SetTargets, [&] {
return aurora_d3d12_set_stereo_targets(frame.xr_frame.serial, targets.data(), target_count);
})) {
{
std::lock_guard lock(submission_mutex_);
awaiting_token_ = 0;
+58 -8
View File
@@ -14,6 +14,10 @@
namespace mkw::vr::diagnostics {
namespace {
constexpr std::array<const char*, static_cast<size_t>(Stage::Count)> kStageNames{
"poll-events", "begin-call", "locate-views", "input-sync", "sync-actions",
"publish", "withdraw", "submission-wait", "cancel", "set-targets"};
constexpr double kNanosecondsPerMillisecond = 1'000'000.0;
double Milliseconds(int64_t nanoseconds) noexcept {
@@ -123,6 +127,7 @@ void FrameDiagnostics::Reset(int64_t now_ns) {
Sink sink = std::move(sink_);
*this = FrameDiagnostics(std::move(sink));
window_start_ns_ = now_ns;
session_start_ns_ = now_ns;
session_info_requested_ = true;
}
@@ -136,6 +141,7 @@ void FrameDiagnostics::OnWaitFrame(int64_t now_ns, int64_t wait_ns, int64_t disp
window_start_ns_ = now_ns;
}
++cycles_;
++cycle_sequence_;
wait_frame_ms_.Add(Milliseconds(wait_ns));
if (display_period > 0) {
display_period_ = display_period;
@@ -280,14 +286,31 @@ void FrameDiagnostics::OnPacketPublished(int64_t now_ns) {
published_ns_ = now_ns;
}
void FrameDiagnostics::OnPacketCanceled(int64_t consumed_ns) {
void FrameDiagnostics::OnStage(Stage stage, int64_t ns, int64_t now_ns) {
if (ns < 0 || static_cast<size_t>(stage) >= stages_.size()) return;
auto& sample = stages_[static_cast<size_t>(stage)];
sample.samples.Add(Milliseconds(ns));
if (ns > sample.worst_ns) {
sample.worst_ns = ns;
sample.worst_at_ns = now_ns;
sample.cycle = cycle_sequence_;
}
}
void FrameDiagnostics::OnPacketCanceled(int64_t now_ns, int64_t consumed_ns) {
if (published_ns_ != 0 && now_ns >= published_ns_) {
cancel_age_ms_.Add(Milliseconds(now_ns - published_ns_));
if (consumed_ns >= published_ns_ && consumed_ns <= now_ns) {
cancel_pickup_ms_.Add(Milliseconds(consumed_ns - published_ns_));
cancel_after_pickup_ms_.Add(Milliseconds(now_ns - consumed_ns));
}
}
if (consumed_ns == 0 || consumed_ns < published_ns_) {
++packet_unused_;
Event("stereo packet withdrawn: no game frame picked it up within 50 ms");
Event("stereo packet withdrawn: no game frame picked it up before cancellation");
} else {
++packet_rejected_;
Event("stereo packet canceled: Aurora picked it up but rendered that frame without it "
"(content tag or transform check)");
Event("stereo packet canceled: Aurora picked it up but the bridge had not encoded it before cancellation");
}
published_ns_ = 0;
}
@@ -355,8 +378,13 @@ void FrameDiagnostics::ClearWindow(int64_t now_ns) {
keepalive_ = packet_unused_ = packet_rejected_ = submit_failed_ = interp_skip_ = 0;
immersive_ = screen_ = not_rendered_ = no_orientation_ = no_position_ = 0;
events_ = suppressed_ = 0;
for (auto& stage : stages_) {
stage.samples.Clear();
stage.worst_ns = -1;
}
for (Samples* samples : {&wait_frame_ms_, &open_ms_, &margin_ms_, &end_gap_ms_, &end_call_ms_,
&acquire_ms_, &release_ms_, &game_wait_ms_, &encode_ms_}) {
&acquire_ms_, &release_ms_, &game_wait_ms_, &encode_ms_,
&cancel_age_ms_, &cancel_pickup_ms_, &cancel_after_pickup_ms_}) {
samples->Clear();
}
}
@@ -365,7 +393,7 @@ void FrameDiagnostics::EmitSummary(int64_t now_ns) {
std::string text;
AppendFormat(text, "%.2fs", Milliseconds(now_ns - window_start_ns_) / 1000.0);
if (display_period_ > 0) {
AppendFormat(text, " %.1fHz", 1.0e9 / static_cast<double>(display_period_));
AppendFormat(text, " predicted-rate=%.1fHz", 1.0e9 / static_cast<double>(display_period_));
}
AppendFormat(text, " cycles=%u skipped-slots=%u late=%u", cycles_, skipped_slots_, late_);
AppendFormat(text, " | layers new=%u repeat=%u empty=%u discarded=%u layer-rejected=%u", layers_new_,
@@ -384,8 +412,26 @@ void FrameDiagnostics::EmitSummary(int64_t now_ns) {
keepalive_, packet_unused_, packet_rejected_, submit_failed_, interp_skip_);
AppendFormat(text, " | frames immersive=%u screen=%u not-rendered=%u no-orientation=%u no-position=%u",
immersive_, screen_, not_rendered_, no_orientation_, no_position_);
AppendFormat(text, " | suppressed=%u", suppressed_);
AppendFormat(text, " | suppressed=%u cycle=%llu t=%.3fs", suppressed_,
static_cast<unsigned long long>(cycle_sequence_),
static_cast<double>(now_ns - session_start_ns_) / 1.0e9);
Info(text);
// Skipped-slot event spam must not suppress evidence of a blocking call.
std::string stages = "stages ms";
for (size_t i = 0; i < stages_.size(); ++i) {
auto& sample = stages_[i];
AppendStat(stages, kStageNames[i], sample.samples.values, false);
if (sample.worst_ns >= 0) {
AppendFormat(stages, "@cycle=%llu,t=%.3fs",
static_cast<unsigned long long>(sample.cycle),
static_cast<double>(sample.worst_at_ns - session_start_ns_) / 1.0e9);
}
}
AppendStat(stages, "cancel-age", cancel_age_ms_.values, false);
AppendStat(stages, "cancel-pickup", cancel_pickup_ms_.values, false);
AppendStat(stages, "cancel-after-pickup", cancel_after_pickup_ms_.values, false);
if (!cancel_age_ms_.values.empty() || std::any_of(stages_.begin(), stages_.end(),
[](const auto& stage) { return stage.worst_ns >= 0; })) Info(stages);
}
namespace {
@@ -473,6 +519,10 @@ void OnWaitFrame(int64_t wait_ns, int64_t display_time, int64_t display_period)
collector.OnWaitFrame(NowNs(), wait_ns, display_time, display_period, ConvertDisplayTime(display_time));
}
void OnStage(Stage stage, int64_t ns) {
Collector().OnStage(stage, ns, NowNs());
}
void OnBeginFrame() {
Collector().OnBeginFrame(NowNs());
}
@@ -524,7 +574,7 @@ void OnPacketPublished() {
}
void OnPacketCanceled() {
Collector().OnPacketCanceled(g_packet_consumed_ns.load(std::memory_order_relaxed));
Collector().OnPacketCanceled(NowNs(), g_packet_consumed_ns.load(std::memory_order_relaxed));
}
void OnKeepaliveRepeat() {
+4 -1
View File
@@ -10,6 +10,7 @@
#endif
#include "vr/openxr_input.h"
#include "vr/openxr_diagnostics.h"
#include <SDL3/SDL_gamepad.h>
#include <SDL3/SDL_joystick.h>
@@ -518,7 +519,9 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
XrActionsSyncInfo sync{XR_TYPE_ACTIONS_SYNC_INFO};
sync.countActiveActionSets = 1;
sync.activeActionSets = &active;
const XrResult result = xrSyncActions(m_runtime->Session(), &sync);
const XrResult result = diagnostics::Measure(diagnostics::Stage::SyncActions, [&] {
return xrSyncActions(m_runtime->Session(), &sync);
});
m_runtime->ObserveResult(result);
if (XR_FAILED(result)) {
if (!m_logged_sync_failure) {
+26 -10
View File
@@ -736,7 +736,9 @@ private:
gx_registered = RegisterGxThread();
}
#endif
const OpenXREventStatus events = runtime_->PollEvents();
const OpenXREventStatus events = diagnostics::Measure(diagnostics::Stage::PollEvents, [&] {
return runtime_->PollEvents();
});
const bool session_active = runtime_->IsSessionRunning();
MkwVRPolicySetSessionActive(session_active);
const uint64_t session_run_serial = runtime_->SessionRunSerial();
@@ -859,6 +861,7 @@ private:
ServiceRecenterRequest();
UpdateVirtualScreenPose(frame);
if (input_ != nullptr) {
const diagnostics::ScopedStage input_timer(diagnostics::Stage::InputSync);
// After the screen is placed, so the pointer aims at this
// frame's screen rather than the previous one's.
input_->Sync(frame.xr_frame.predicted_display_time, PointerScreen(frame, policy, immersive),
@@ -876,7 +879,9 @@ private:
if (aurora_get_stereo_frame_interpolation() &&
!interpolation_pacing_.ShouldRender(frame.xr_frame.predicted_display_time, interpolation_target)) {
diagnostics::OnInterpolationSkip();
if (!backend_->TryCancelPendingFrame(frame) || !backend_->FinishFrame(frame, false)) {
if (!diagnostics::Measure(diagnostics::Stage::Cancel, [&] {
return backend_->TryCancelPendingFrame(frame);
}) || !backend_->FinishFrame(frame, false)) {
SetError(backend_->LastError());
fatal = true;
}
@@ -884,6 +889,7 @@ private:
}
{
const diagnostics::ScopedStage publish_timer(diagnostics::Stage::Publish);
std::lock_guard lock(published_mutex_);
// First person renders at life-size scale, third person at the
// configured diorama scale. Head translation and IPD are the
@@ -904,14 +910,18 @@ private:
submission == OpenXRSubmissionStatus::Timeout) {
// Fresh rendering wakes us immediately. A 50 ms keep-alive
// protects stalls without issuing eager repeats during GPU work.
submission = backend_->WaitForSubmission(frame, 50);
submission = diagnostics::Measure(diagnostics::Stage::SubmissionWait, [&] {
return backend_->WaitForSubmission(frame, 50);
});
if (submission == OpenXRSubmissionStatus::Timeout) {
// A pause, minimized window, or guest stall may leave no GX
// frame to consume this packet. Withdraw it, then cancel the
// matching bridge target only if Encode has not taken ownership.
if (std::chrono::steady_clock::now() >= cancel_after) {
WithdrawPublishedFrame();
canceled_before_encode = backend_->TryCancelPendingFrame(frame);
diagnostics::Measure(diagnostics::Stage::Withdraw, [&] { WithdrawPublishedFrame(); });
canceled_before_encode = diagnostics::Measure(diagnostics::Stage::Cancel, [&] {
return backend_->TryCancelPendingFrame(frame);
});
if (canceled_before_encode) {
diagnostics::OnPacketCanceled();
break;
@@ -925,7 +935,7 @@ private:
}
}
}
WithdrawPublishedFrame();
diagnostics::Measure(diagnostics::Stage::Withdraw, [&] { WithdrawPublishedFrame(); });
if (stop_.load(std::memory_order_acquire)) {
// Aurora has been drained by Shutdown(); backend shutdown below
// cancels its pending target, then either safely releases the
@@ -1035,6 +1045,7 @@ private:
ServiceRecenterRequest();
UpdateVirtualScreenPose(packet);
if (input_ != nullptr) {
const diagnostics::ScopedStage input_timer(diagnostics::Stage::InputSync);
input_->Sync(packet.xr_frame.predicted_display_time, PointerScreen(packet, policy, immersive),
SettingsPanelScreen(packet, policy, immersive));
}
@@ -1043,6 +1054,7 @@ private:
return KeepAlive();
}
{
const diagnostics::ScopedStage publish_timer(diagnostics::Stage::Publish);
std::lock_guard lock(published_mutex_);
BuildPublishedFrame(packet, immersive, policy.EffectiveUnitsPerMeter(), policy.content_tag);
diagnostics::OnPacketPublished();
@@ -1056,11 +1068,15 @@ private:
bool canceled_before_encode = false;
const auto cancel_after = std::chrono::steady_clock::now() + std::chrono::milliseconds(50);
while (!stop_.load(std::memory_order_acquire) && submission == OpenXRSubmissionStatus::Timeout) {
submission = backend_->WaitForSubmission(packet, 50);
submission = diagnostics::Measure(diagnostics::Stage::SubmissionWait, [&] {
return backend_->WaitForSubmission(packet, 50);
});
if (submission == OpenXRSubmissionStatus::Timeout) {
if (std::chrono::steady_clock::now() >= cancel_after) {
WithdrawPublishedFrame();
canceled_before_encode = backend_->TryCancelPendingPacket(packet);
diagnostics::Measure(diagnostics::Stage::Withdraw, [&] { WithdrawPublishedFrame(); });
canceled_before_encode = diagnostics::Measure(diagnostics::Stage::Cancel, [&] {
return backend_->TryCancelPendingPacket(packet);
});
if (canceled_before_encode) {
diagnostics::OnPacketCanceled();
break;
@@ -1072,7 +1088,7 @@ private:
}
}
}
WithdrawPublishedFrame();
diagnostics::Measure(diagnostics::Stage::Withdraw, [&] { WithdrawPublishedFrame(); });
if (stop_.load(std::memory_order_acquire) || canceled_before_encode ||
submission == OpenXRSubmissionStatus::ShuttingDown) {
return true;
+8 -4
View File
@@ -723,7 +723,9 @@ bool OpenXRRuntime::BeginFrame(const OpenXRFrame& frame) {
}
XrFrameBeginInfo begin_info{XR_TYPE_FRAME_BEGIN_INFO};
const XrResult result = xrBeginFrame(m_session, &begin_info);
const XrResult result = diagnostics::Measure(diagnostics::Stage::BeginCall, [&] {
return xrBeginFrame(m_session, &begin_info);
});
if (XR_FAILED(result)) {
m_frame_phase = FramePhase::Idle;
m_active_frame_serial = 0;
@@ -769,9 +771,11 @@ bool OpenXRRuntime::LocateViewsForFrame(OpenXRFrame& frame) {
locate_info.space = m_app_space;
XrViewState view_state{XR_TYPE_VIEW_STATE};
uint32_t view_count = 0;
if (!Check(xrLocateViews(m_session, &locate_info, &view_state,
kOpenXREyeCount, &view_count, frame.views.data()),
"xrLocateViews")) {
const XrResult locate_result = diagnostics::Measure(diagnostics::Stage::LocateViews, [&] {
return xrLocateViews(m_session, &locate_info, &view_state,
kOpenXREyeCount, &view_count, frame.views.data());
});
if (!Check(locate_result, "xrLocateViews")) {
return false;
}
if (view_count != kOpenXREyeCount) {