Skip to content

Round-2 stability: profiler honesty, GI backend logging, crash-triage kit, test repairs#75

Merged
proggeramlug merged 1 commit into
mainfrom
fix/round2-stability
Jul 8, 2026
Merged

Round-2 stability: profiler honesty, GI backend logging, crash-triage kit, test repairs#75
proggeramlug merged 1 commit into
mainfrom
fix/round2-stability

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Phase-0 of the round-2 audit plan (full report: shooter docs/audit-round2.md). No rendering behavior changes — observability, measurement correctness, and CI health.

Profiler correctness (audit F9)

Round-1's numbers were all taken through two silent distortions this PR fixes/documents:

  • Stale rolling averages: a pass that stops running kept showing its frozen average forever (the audit's feature-toggle ladder read nonsense until this was understood). Entries now expire from readouts after one 120-frame window and evict after four.
  • Fresh sessions: re-enabling the profiler clears rolling stats + histogram instead of blending pre-disable numbers.
  • One-shot warning when the 32-pair GPU timestamp budget is exhausted (was silent truncation).
  • The post_fx CPU bracket's begin() had been lost — end() was a no-op while comments claimed the phase covered the composite tail.
  • frame_end docs now state the truth: the GPU readback blocks (map_async + poll(Wait)) every frame, so wall fps is pessimistic while profiling (~3 fps at the shooter's combat load).
  • Unit tests for expiry + fresh-session semantics.

Boot observability (audit F4)

The GI stack silently falls back HW→SDF→Hi-Z; nothing reported which tier ran, and it cost the audit a debugger-day to learn the dev box never runs HW ray query. Now:

bloom: adapter 'AMD Radeon 760M Graphics' (Dx12), ray_query=false, timestamps=true
bloom: ssgi trace backend = hiz-screen
bloom: ssgi trace backend = sdf-clipmap   <- promotion after first clipmap bake

Crash-triage kit (audit F1 / EN-020)

Three scripted audit runs hit the same c0000005 (main.exe+0xe8e5, read 8 bytes below a page boundary); after a relink it stopped reproducing — layout-sensitive heap-read overrun. Rather than guess: line-tables now ship in the staticlib (debug = "line-tables-only", negligible cost), perry compile --debug-symbols yields a PDB, WER LocalDumps are armed on the dev box, and docs/crash-triage-windows.md is the runbook. EN-020 tracks it; the next occurrence is a five-minute symbolized diagnosis.

Test repairs (pre-existing round-1 breakage)

  • cargo test has not compiled since Tier 2 added MaterialDrawCommand.model — the translucent-sort fixture was never updated.
  • Two golden references still encoded pre-round-1 shading (lit_primitives_3d, many_point_lights_clustered_scene) and failed beyond tolerance; regenerated via BLOOM_UPDATE_GOLDEN=1. The other goldens pass unchanged and are not touched.

Suite is green: 99 lib tests + 7 golden (5 profiler tests including the 2 new ones).

Summary by CodeRabbit

  • Bug Fixes

    • Improved profiler behavior so outdated stats drop off, sessions reset cleanly when re-enabled, and GPU budget exhaustion is reported once.
    • Fixed post-processing profiling coverage and made SSGI backend reporting less noisy by only logging changes.
    • Added clearer Windows startup diagnostics and better debug info support for crash analysis.
  • Tests

    • Strengthened renderer tests to ensure material sort commands include a defined transform.
  • Documentation

    • Added Windows crash-triage and ticket notes to help with native crash investigation.

…nd-2 audit)

Profiler correctness (audit F9 — these skewed every round-1 measurement):
- Rolling stats expire: a pass that stops running (feature toggled off)
  drops out of snapshot/summary/overlay after one window instead of
  showing a frozen average forever; entries evict after 4 windows.
- Re-enabling the profiler starts a fresh session (clears rolling stats
  + frame histogram) instead of blending pre-disable numbers in.
- One-shot warning when the 32-pair GPU timestamp budget is exhausted
  (was a silent truncation of later passes).
- Restore the orphaned `post_fx` CPU bracket — begin() was lost at some
  point, leaving end() a no-op while a comment claimed the phase covered
  the composite tail's cost.
- frame_end doc no longer claims the readback is non-blocking: it
  map_asyncs + poll(Wait)s every frame, serialising CPU<->GPU, so wall
  fps is pessimistic while profiling (measured ~3 fps at combat load).
- Unit tests for the expiry + fresh-session semantics.

Boot observability (audit F4 — the silent HW->SW GI fallback cost a
debugger day):
- windows: log adapter name/backend + ray_query/timestamp feature flags
  at device creation.
- One-shot "bloom: ssgi trace backend = hw-ray-query|sdf-clipmap|
  hiz-screen" when the trace tier is chosen (re-logs on promotion).

Crash-triage kit (audit F1, EN-020):
- native/windows release profile carries line-tables-only debug info so
  `perry compile --debug-symbols` yields a usable PDB.
- docs/crash-triage-windows.md: the standing WER LocalDumps + symbols +
  llvm-symbolizer workflow, and everything known about the layout-
  sensitive heap-read AV (3x reproduced pre-relink, gone after relink).
- docs/tickets.md: EN-020 filed with repro history and ruled-out causes.

Test repairs (pre-existing breakage from the round-1 merge):
- material_system_tests: MaterialDrawCommand fixture gained the `model`
  field Tier 2 added — cargo test has not compiled since.
- Regenerate the two golden references (lit_primitives_3d,
  many_point_lights_clustered_scene) that still encoded pre-round-1
  shading; the other goldens pass unchanged and stay untouched.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds rolling-stats eviction and session-reset logic plus a one-shot GPU budget warning to the profiler, introduces SSGI backend-change logging tied to a new Renderer field, fixes an unbalanced post_fx profiler bracket, enables Windows release line-table debug symbols with adapter diagnostic logging, and adds crash-triage documentation and a ticket entry.

Changes

Profiler rolling-stats eviction and reset

Layer / File(s) Summary
RollingStats recency tracking and eviction
native/shared/src/profiler.rs
Adds last_frame to RollingStats, updates frame_end_cpu to record it and evict stale entries, and filters summary, avg_frame_cpu_us, avg_frame_gpu_us, and snapshot by recency; updates frame_end doc comment.
Session reset and GPU budget warning Adds budget_warned field, resets rolling/histogram state on re-enable in set_enabled, and emits a one-time eprintln! when the GPU timestamp pair budget (MAX_GPU_PAIRS) is exhausted in reserve_gpu_pair.
Eviction/reset tests Adds tests verifying stale labels drop from snapshots and eventually evict, and that re-enabling the profiler clears history and snapshot output.

SSGI backend logging and renderer state

Layer / File(s) Summary
Renderer field and initialization
native/shared/src/renderer/mod.rs
Adds ssgi_backend_logged: Option<&'static str> to Renderer and initializes it to None in new_impl.
Backend-change logging
native/shared/src/renderer/ssgi_pass.rs
record_ssgi_passes computes the selected trace backend and logs it via eprintln! only when it differs from the previously logged value.
post_fx bracket fix and test model field
native/shared/src/renderer/mod.rs, native/shared/src/renderer/material_system_tests.rs
Adds profiler.begin("post_fx") to balance the existing profiler.end("post_fx"); sets an identity model matrix on MaterialDrawCommand in a test helper.

Windows crash triage: symbols, adapter logging, and docs

Layer / File(s) Summary
Release debug symbols and adapter diagnostics
native/windows/Cargo.toml, native/windows/src/lib.rs
Sets debug = "line-tables-only" in the release profile and logs adapter name, backend, and ray-query/timestamp feature availability after adapter selection.
Crash-triage docs and ticket
docs/crash-triage-windows.md, docs/tickets.md
Adds a Windows crash-triage guide covering the AV signature, WER LocalDumps setup, symbolization steps, and a repro harness; adds ticket EN-020 documenting the heap read overrun.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Frame as Frame Loop
  participant Profiler
  participant RollingStats

  Frame->>Profiler: frame_end_cpu(frame_count)
  Profiler->>RollingStats: record entry.last_frame per label
  Profiler->>RollingStats: retain labels within 4*ROLLING_FRAMES
  Frame->>Profiler: snapshot()/summary()/avg_frame_cpu_us()
  Profiler->>RollingStats: filter by fc - last_frame <= ROLLING_FRAMES
  RollingStats-->>Profiler: recent stats only
  Profiler-->>Frame: filtered readouts
Loading

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately captures the main stability, observability, crash-triage, and test-fix work.
Description check ✅ Passed It covers the required summary, test results, and reviewer notes content, though not in the exact template format.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/round2-stability

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@native/shared/src/profiler.rs`:
- Around line 161-174: The fresh-session reset in set_enabled should also clear
the current in-flight profiler state, not just the rolling aggregates. Update
Profiler::set_enabled so that when transitioning from disabled to enabled it
resets frame, open_cpu, next_query, pending_gpu, and budget_warned alongside the
existing rolling/history fields, ensuring any old samples or warning suppression
do not carry into the first frame of a new session.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e33ac7c9-a6ec-48c6-9f4a-bf01b14c7419

📥 Commits

Reviewing files that changed from the base of the PR and between f8614c6 and d9976b9.

⛔ Files ignored due to path filters (2)
  • native/shared/tests/golden/lit_primitives_3d.png is excluded by !**/*.png
  • native/shared/tests/golden/many_point_lights_clustered_scene.png is excluded by !**/*.png
📒 Files selected for processing (8)
  • docs/crash-triage-windows.md
  • docs/tickets.md
  • native/shared/src/profiler.rs
  • native/shared/src/renderer/material_system_tests.rs
  • native/shared/src/renderer/mod.rs
  • native/shared/src/renderer/ssgi_pass.rs
  • native/windows/Cargo.toml
  • native/windows/src/lib.rs

Comment on lines +161 to +174
pub fn set_enabled(&mut self, on: bool) {
if on && !self.enabled {
// Fresh measuring session: without this, stats captured before
// a disable (potentially under a different feature set) show
// until the rolling window refills and skew the first seconds
// of every new session.
self.rolling.clear();
self.frame_total_cpu_us = [0.0; ROLLING_FRAMES];
self.frame_total_gpu_us = [0.0; ROLLING_FRAMES];
self.histogram_idx = 0;
self.histogram_filled = 0;
}
self.enabled = on;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear current-frame profiler state when starting a fresh session.

set_enabled(true) resets rolling history, but not frame, open_cpu, next_query, pending_gpu, or budget_warned. If an external caller toggles off→on before frame_end runs, old in-flight samples can be folded into the first “fresh” frame, and the GPU budget warning stays suppressed from the previous session.

Proposed fix
     pub fn set_enabled(&mut self, on: bool) {
         if on && !self.enabled {
             // Fresh measuring session: without this, stats captured before
             // a disable (potentially under a different feature set) show
             // until the rolling window refills and skew the first seconds
             // of every new session.
+            self.open_cpu.clear();
+            self.frame.clear();
             self.rolling.clear();
+            self.next_query = 0;
+            self.pending_gpu.clear();
+            self.budget_warned = false;
             self.frame_total_cpu_us = [0.0; ROLLING_FRAMES];
             self.frame_total_gpu_us = [0.0; ROLLING_FRAMES];
             self.histogram_idx = 0;
             self.histogram_filled = 0;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn set_enabled(&mut self, on: bool) {
if on && !self.enabled {
// Fresh measuring session: without this, stats captured before
// a disable (potentially under a different feature set) show
// until the rolling window refills and skew the first seconds
// of every new session.
self.rolling.clear();
self.frame_total_cpu_us = [0.0; ROLLING_FRAMES];
self.frame_total_gpu_us = [0.0; ROLLING_FRAMES];
self.histogram_idx = 0;
self.histogram_filled = 0;
}
self.enabled = on;
}
pub fn set_enabled(&mut self, on: bool) {
if on && !self.enabled {
// Fresh measuring session: without this, stats captured before
// a disable (potentially under a different feature set) show
// until the rolling window refills and skew the first seconds
// of every new session.
self.open_cpu.clear();
self.frame.clear();
self.rolling.clear();
self.next_query = 0;
self.pending_gpu.clear();
self.budget_warned = false;
self.frame_total_cpu_us = [0.0; ROLLING_FRAMES];
self.frame_total_gpu_us = [0.0; ROLLING_FRAMES];
self.histogram_idx = 0;
self.histogram_filled = 0;
}
self.enabled = on;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@native/shared/src/profiler.rs` around lines 161 - 174, The fresh-session
reset in set_enabled should also clear the current in-flight profiler state, not
just the rolling aggregates. Update Profiler::set_enabled so that when
transitioning from disabled to enabled it resets frame, open_cpu, next_query,
pending_gpu, and budget_warned alongside the existing rolling/history fields,
ensuring any old samples or warning suppression do not carry into the first
frame of a new session.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant