Repository navigation
Hot Path Vulkan Uniform Update Plans
Lifecycle: Performance - Unproven · Type: Prepared-state optimization
State: Source-backed; runtime value is not established.
Status: Source-backed narrow follow-up candidate; not implemented as described and not benchmarked.
Reviewed source:
322986f4c5502b3fa887a9605ac8cd88e9b2c579Scope: Uniform dirty-row discovery and repeated interpretation of fixed shader-layout metadata.
Current main already contains substantial shader/uniform avoidance: stage-specific source epochs, dirty rows, layout-change tracking, complete-binding reuse and the optional unchanged-shader fast path. This candidate does not claim xemu rebuilds all uniforms or shaders every draw.
The remaining opportunity is narrower: avoid scanning clean dirty-flag arrays and prepare a compact copy plan when a shader layout becomes known.
Current Vulkan shader code includes:
- a helper that scans dirty-flag arrays to determine whether a stage needs work;
- row-update logic that scans rows again during a partial update;
- generic uniform metadata traversal/copy dispatch;
- stage/layout state that often remains stable across many draws.
The existing Skip Unchanged Shader Work can bypass broader shader/pipeline preparation only when its complete guard set passes. Uniform updates still need a precise and efficient route when dynamic rows actually change or when the broad shortcut is ineligible.
Potentially avoided work includes:
- scanning all vertex/light/context dirty booleans to discover that none are set;
- rescanning every row when only a few changed;
- revisiting inactive uniform metadata;
- recomputing source/destination offsets for a stable layout;
- element-by-element compare/copy where one contiguous compatible region is valid.
The candidate targets CPU preparation and staging writes, not shader compilation or graphics-pipeline creation.
flowchart TD
A[Prepare draw uniforms] --> B[Scan dirty arrays for any changed row]
B --> C{Stage update required?}
C -->|No| D[Reuse current stage data]
C -->|Yes| E[Walk rows and dirty flags again]
E --> F[Interpret active uniform metadata]
F --> G[Generic compare/copy into staging allocation]
G --> H[Clear dirty state and publish offsets]
flowchart TD
A[Uniform writer marks row] --> B[Set row bit and stage summary bit]
C[Prepare draw uniforms] --> D{Stage summary empty?}
D -->|Yes| E[Skip row scan]
D -->|No| F[Iterate only set bits]
F --> G[Execute PreparedUniformCopyPlan entries]
G --> H[Use specialized contiguous or strided copy operation]
H --> I[Clear processed bits and summary exactly]
A safe design keeps:
- a bitmap for each row family/stage;
- a cheap
any_dirtysummary derived and maintained by writers/clearers; - explicit full-update state for layout/source changes;
- existing source epochs and layout-change flags.
The summary is not authoritative on its own. It accelerates the empty case; the bitmap identifies exact rows.
All writer and clearing paths must update both consistently, including:
- guest register/control writes;
- full uniform rebuild;
- rows with no active destination;
- shader/layout change;
- reset and snapshot restore;
- error/retry paths.
When ShaderUniformLayout becomes stable, prepare entries containing only active destinations:
source family / base
source row or field index
source stride
staging destination offset
copy size / element count
destination padding/stride rule
specialized copy kind
The plan must be invalidated when layout, source representation, push-constant/UBO policy or any offset/stride contract changes.
A copy plan does not make source values immutable. It prepares where and how to copy live values.
Some active locations may form contiguous compatible regions. Those can use one compare/copy operation. Padded arrays, transposed data, component conversion or strided layouts retain dedicated routes.
Do not combine this work with claims about uniform_index() string lookup unless profiling proves that lookup occurs in the hot path. The source audit supports dirty scans and layout interpretation; it does not establish a per-draw string-search bottleneck.
General renderer precedent: engines track constant/uniform updates with dirty bitsets and precomputed binding/copy layouts, visiting only changed ranges while retaining a full rebuild path after layout changes.
Internal xemu precedent: current stage dirty flags, source epochs and unchanged-binding shortcuts already use dependency summaries. This candidate extends that model to row discovery and copy execution rather than inventing a new global cache.
| Area | Role |
|---|---|
vk/shaders.c::get_uniform_stage_update_needs() |
Determines stage update requirements |
| Row update helpers | Current per-row dirty processing |
| Uniform metadata/copy helpers | Interpret layout and move values |
ShaderUniformLayout |
Stable layout information suitable for prepared plan |
| PGRAPH/Vulkan render thread | Owns dirty consumption and staging publication |
| Guest state writers | Must maintain dirty bitmap/summary contract |
uniform_prepare_calls
uniform_stage_summary_empty_hits
uniform_dirty_rows_marked
uniform_dirty_rows_visited
uniform_rows_scanned
uniform_copy_plan_builds
uniform_copy_plan_executes
uniform_copy_entries_executed
uniform_bytes_compared
uniform_bytes_copied
full_uniform_updates
partial_uniform_updates
uniform_prepare_cpu_ns
Expected useful signature:
stable shader/layout workload
-> plan builds remain low
-> many prepare calls find empty summary
-> partial updates visit far fewer rows than total rows
-> staging bytes and output remain equivalent
- A writer changes a row without setting the bitmap/summary.
- Clearing one row incorrectly clears the stage summary while other bits remain.
- Full-update/layout-change path leaves stale partial bits.
- Snapshot restore reconstructs values but not matching dirty/plan state.
- Prepared offsets outlive a layout or staging-policy change.
- Contiguous specialization crosses padding/stride boundaries.
- Added summary/bitmap writes cost more than scans in highly dynamic workloads.
- Broader unchanged-shader fast path and this plan disagree about what remains dynamic.
- Add pure bitmap/summary state-machine tests.
- Instrument current row scans before changing representation.
- Split plan construction from plan execution with reference output comparison.
- Exercise no-change, one-row, sparse-row, all-row and full-layout-change cases.
- Exercise absent locations, push-constant/UBO policy changes and ubershader controls.
- Exercise reset, shader switch, command-buffer rollover, snapshot save/restore and teardown.
- Compare staging bytes, descriptor state and framebuffer hashes.
- Profile Release builds with counters disabled for final timing.
- Keep dirty-summary and copy-plan changes independently bisectable.
Applicable as a focused refinement, not a replacement for existing shader fast paths. Add counters first because current main is already partially optimized and the remaining scan cost may be small.
Primary reference: This subject page is the design and research reference for Uniform Update Plans. Pull requests are implementation/test history only.
Lifecycle: Performance - Unproven · Mechanism: Dirty-summary and prepared-copy optimization
Reviewed source:322986f4c5502b3fa887a9605ac8cd88e9b2c579· Acceptance class: intended exact or behavior-preserving for its admitted scope
Maintain compact dirty summaries and a shader-layout-specific copy plan so clean arrays are not rescanned and inactive uniform metadata is not reinterpreted on every update.
This entry remains Unproven: the source path and opportunity are concrete, but no accepted xemu runtime result has established that the candidate improves representative workloads. The page defines what must be bypassed, what remains mandatory, and how the hypothesis can be rejected.
| File / function | Role |
|---|---|
hw/xbox/nv2a/pgraph/vk/shaders.c |
get_uniform_stage_update_needs(), update_uniform_rows(), apply_uniform_updates() |
hw/xbox/nv2a/pgraph/vk/glsl.h |
uniform layout metadata |
flowchart TD
N0["Check several dirty bool arrays"]
N1["Decide stage update"]
N0 --> N1
N2["Scan rows again"]
N1 --> N2
N3["Walk generic uniform metadata"]
N2 --> N3
N4["Compare/copy active values"]
N3 --> N4
N5["Clear dirty state"]
N4 --> N5
flowchart TD
N0["Writers maintain dirty bitmap and nonzero summary"]
N1["No dirty words: skip row scan"]
N0 --> N1
N2["Visit only set bits"]
N1 --> N2
N3["Execute prepared active-copy plan"]
N2 --> N3
N4["Clear bits and summary exactly"]
N3 --> N4
Current main already has stage epochs and partial dirty tracking; the remaining target is narrower. The candidate should not claim that all uniforms are rebuilt every draw. It removes repeated scans and layout interpretation when a known layout receives sparse updates.
- Scanning clean dirty arrays
- Scanning clean rows during partial update
- Repeated traversal of inactive uniform entries
- Repeated offset/stride interpretation
- Authoritative dirty marking
- Value comparisons/copies
- Full-update route
- Texture/polygon/inline inputs
- Descriptor/upload work after data preparation
if (dirty_summary == 0 && !layout_changed && !source_changed) {
stats.uniform_stage_skips++;
return;
}
for_each_set_bit(word, dirty_bitmap) {
copy_uniform_row(plan->row[word]);
clear_bit(word, dirty_bitmap);
}
dirty_summary = recompute_or_clear_summary(dirty_bitmap);The sketch defines the intended boundary; it is not a complete patch and does not replace source review, ownership design, or focused tests.
- Every writer and full-clear/full-update path maintains the bitmap/summary consistently.
- The prepared plan is keyed by the exact active shader uniform layout.
- Padding/strided arrays use correct copy operations; contiguous memcpy is only admitted where layout proves it.
- Absent locations and reset/restore state are handled.
Dirty metadata is renderer/state-owned. Copy plans belong to shader module/layout lifetime and are invalidated with module/layout replacement. Snapshot/reset can rebuild summaries from canonical dirty arrays or force a full update.
The maintained reference path remains the fallback whenever admission, capability, lifetime, or validation is uncertain. A fast result with an unproven dependency is not acceptable as an exact optimization.
- Reduces common clean/sparse-update CPU work
- Builds on existing layout objects
- Does not change shader semantics
- Counters can attribute scans and copies
- Missed dirty writer creates stale uniforms
- Bitmap maintenance can add writer cost
- Copy-plan complexity for padding/strides
- Benefit may be small if full updates dominate
Counters must be cheap, subsystem-owned, and snapshotted outside the hot loop. Record at least:
- stage_update_checks
- dirty_array_elements_scanned
- dirty_words_nonzero
- rows_copied
- full_updates
- plan_entries_executed
- uniform_prepare_cpu_us
- descriptor updates
A local reduction is not enough. Evidence must connect route reachability and removed work to fixed guest progress, CPU/GPU time where applicable, and p95/p99 behavior.
- Mutate every uniform source independently
- Sparse and dense dirty patterns
- Full update and absent locations
- Layout/module switch
- Padded/strided arrays
- Reset/snapshot
- Compare allocation bytes and output
- Release A/B on constant-heavy scenes
Correctness runs may use validation layers, detailed comparisons, and fault injection. Performance acceptance must use optimized Release builds with matching symbols and minimal instrumentation.
Promote when mutation coverage proves summary correctness and sparse/clean workloads show meaningful preparation savings.
Reject or simplify if writers are too numerous to maintain safely, full updates dominate, or plan execution does not beat current scans.
Vulkan-Shader-Binding-and-Compilation · Performance-Unproven · Vulkan-Dynamic-Uniform-Descriptors · Hot-Path-Vulkan-Unchanged-Shader-Work
PR pages may be linked as supporting evidence, but they are not substitutes for this reference.
DSP tooling guide and navigation updated November 2, 2026 for experimental PR #384, source 8f51d237. Capture/replay usage and limits.
The earlier wiki-wide review was published November 1, 2026. Emulator source: b7140848. Reviewed wiki content: 1d2b671. Current status · Release availability · Publication process.
- Home
- Current status
- Stable versus main
- Implemented mechanisms
- Controls and defaults
- Measured results and limits
- Complete change register
- System
- CPU and vCPU
- Memory and DMA
- Graphics / NV2A
- APU and audio
- Timing and timers
- Platform and I/O
- Hardware observations
- Fidelity boundaries
- Methodology
- PGR2 routes
- Debugging lag / hot paths
- Tools and commands
- DSP capture, replay and fuzzing
- HLE readiness and public XISO proof
- Concurrent DSP shadow and OpenAL proof
- DSP HLE operation inventory
- DSP HLE fix examples and testing
- Development
- Upstream shortlist
- Wiki maintenance
- Research / retired work
- Optimization catalog
- Hot-path shortcuts
- Researched experiments
- Unproven proposal directory
- PR lessons
- September26 snapshot
Repository · PRs · Issues