Performance fix: timing instrumentation #743

Merged
SakulFlee merged 4 commits from perf/timing-instrumentation into main 2026-08-01 04:03:46 +02:00
Owner

Instead of spamming the console, we collect and print an average result every second. This can later be expanded with an in-game/engine UI.

Instead of spamming the console, we collect and print an average result every second. This can later be expanded with an in-game/engine UI.
The pre-allocated light buffer (MAX_LIGHTS=256) caused the PBR shader to
loop 256 times per fragment via arrayLength(&light_store), running the
full BRDF+shadow path 256 times instead of ~6 actual lights. This made
the model render pass ~3x slower (6.8ms→22ms in RenderDoc).

Changes:
- Add binding 13 (light_count uniform) to world bind group layout
- Compute active_light_count from LightDescriptorEcs dense.len() each frame
- Write to a static 16-byte UNIFORM|COPY_DST buffer (LIGHT_COUNT_BUF)
- Shader: loop bound changed from arrayLength(&light_store) to light_count
- Shader: add zero-intensity early continue guard (light.color.w == 0.0)
- Also moved global_model_dirty check before realize_models (Fix B1) to
  catch newly-imported models (e.g. DamagedHelmet) whose ModelDirty flag
  is cleared by realize_models before the check runs
On frame 1 (bootstrap), ensure_cubes(2) was called mid-loop when the
second point light was processed, reallocating the cube depth texture
from 1 to 2 cubes. This dropped the first point light's freshly-rendered
cube-0 depth data into the old (dropped) texture, leaving cube 0 as
uninitialized garbage in the new texture. The shader's textureSampleCompare
for that cube returned shadowed (0) for all fragments, making the Room 4
point light contribute nothing — dark room, no shadows.

Changes:
- Pre-compute needed_cubes (count of enabled point lights) and
  needed_layers (spot/directional cascade sum) BEFORE the render loop
- Call ensure_cubes/ensure_layers up-front to final capacity
- Track grew = cubes_grew || layers_grew; when true, force_all_dirty
  makes all lights re-render this frame so no previously-stored depth
  is silently lost after a texture growth realloc
- Remove redundant mid-loop ensure_cubes and post-loop ensure_layers
Fix round-robin starvation: reserve floor budget for proactive shadow refresh
Some checks failed
ci/woodpecker/pr/linting Pipeline failed
ci/woodpecker/pr/test unknown status
99208c9a99
The dirty-first pass consumed the entire per-frame budget when lights
were constantly dirty (e.g. the temporary LightAnimator marking Room-1
spot dirty every frame), starving the proactive round-robin refresh.
This meant clean shadow maps were never re-rendered, preventing newly
imported models from ever appearing in shadow maps.

Changes:
- Reserve max(1, budget/2) for round-robin when clean casters exist
  (0 reserve when no clean casters — all budget goes to dirty)
- Dirty lights get up to budget - reserve slots (still priority)
- Round-robin uses the remainder, ensuring proactive refresh runs
  even when some lights are always dirty
- TimingAccumulator prints avg section times every ~1s at INFO level
- GPU timestamps via TIMESTAMP_QUERY_INSIDE_ENCODERS (double-buffered, non-blocking)
- Captures: shadow pass, skybox, main pass boundaries
- Feature gated on adapter support (graceful fallback if unsupported)
- wgpu requires MAP_READ buffers to have ONLY MAP_READ | COPY_DST — no
  other flags (including QUERY_RESOLVE). Split into:
  resolve buffer: QUERY_RESOLVE | COPY_SRC
  staging buffers: COPY_DST | MAP_READ (double-buffered)
  copy_buffer_to_buffer bridges them each frame (24 bytes, negligible)
- Replace device.poll(wait_indefinitely) with submission-index-based wait:
  waits only on PREVIOUS frame's tiny resolve, preserves CPU/GPU overlap
- Fix GPU timestamp math: store durations (shadow/skybox+models/total) in ms
Downgrade stagger log from debug! to trace! (hidden at Debug filter)
Some checks failed
ci/woodpecker/pr/linting Pipeline failed
ci/woodpecker/pr/test unknown status
a30f3a8d23
Merge branch 'main' into perf/timing-instrumentation
Some checks failed
ci/woodpecker/pr/linting Pipeline failed
ci/woodpecker/pr/test unknown status
ecde4c5ae5
style: auto-format code
Some checks failed
ci/woodpecker/pr/linting Pipeline failed
ci/woodpecker/pr/test unknown status
5ffe7d0889
fix: auto-fix clippy warnings
Some checks failed
ci/woodpecker/pr/linting Pipeline failed
ci/woodpecker/pr/test unknown status
751f18f47a
style: auto-format code
All checks were successful
ci/woodpecker/pr/linting Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ba91ae103c
SakulFlee force-pushed perf/timing-instrumentation from ba91ae103c
All checks were successful
ci/woodpecker/pr/linting Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
to 92867212cc
Some checks failed
ci/woodpecker/pr/linting Pipeline failed
ci/woodpecker/pr/test unknown status
2026-08-01 03:32:20 +02:00
Compare
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
SakulFlee/Orbital!743
No description provided.