Skip to content

Hot Path Vulkan Frame Scoped Texture Trust

github-actions[bot] edited this page Sep 14, 2026 · 3 revisions

Vulkan Frame-Scoped Texture Trust

Lifecycle: Performance - Unproven · Type: Approximate shortcut
State: Source-backed; runtime value is not established.

Status: Applicable approximation candidate; not implemented, not benchmarked, and unsuitable as an unlabeled default.

Mechanism class: Assumption-based shortcut / deferred validation.

Reviewed source: 322986f4c5502b3fa887a9605ac8cd88e9b2c579

Backend: Vulkan.

Summary

Current Vulkan texture validation can test and clear guest-memory dirty pages, propagate possibly_dirty state through active texture-cache entries, inspect surface overlap, hash content, and upload changed data. The proposed shortcut validates an admitted RAM-backed texture once at a defined guest-frame boundary, then bypasses repeated validation for later uses in the same frame.

This is not equivalent behavior when the guest rewrites texture or palette memory after the first validation and samples it again before the next boundary. The proposal is useful only as an explicit compatibility/performance approximation with a measured stale-content window.

What would disappear

On an admitted same-frame hit, xemu would bypass:

  • dirty-page test-and-clear work;
  • propagation through active cache entries;
  • surface-overlap searches;
  • content hashing triggered by possible dirtiness;
  • upload/rebind work that the skipped validation might otherwise request.

The shortcut must not clear dirty evidence merely because it chose not to inspect it.

Current flow

flowchart TD
    A[Texture stage used] --> B[Resolve source, palette and current binding]
    B --> C{Binding/state already potentially reusable?}
    C -->|No| R[Run normal create/update path]
    C -->|Yes| D[Test and clear texture/palette dirty pages]
    D --> E[Propagate possibly-dirty state to overlapping cached entries]
    E --> F{Surface overlap, source change or content change?}
    F -->|Yes| R
    F -->|No| G[Retain validated binding]
    R --> H[Hash/upload/copy/bind as required]
    G --> I[Draw]
    H --> I
Loading

check_texture_possibly_dirty() is not a pure predicate: it consumes dirty-memory evidence and can mark multiple cache entries. That side effect is part of the reference semantics and makes careless A/B instrumentation invalid.

Candidate flow

flowchart TD
    A[Texture stage used] --> B{Narrow texture class admitted?}
    B -->|No| R[Run maintained reference validation]
    B -->|Yes| C{Binding version and trusted frame match?}
    C -->|No| R
    C -->|Yes| D{Policy permits frame trust?}
    D -->|No| R
    D -->|Yes| E[Retain existing binding without memory validation]
    R --> F{Reference validation succeeds?}
    F -->|Yes| G[Publish trusted frame and binding version]
    F -->|No| H[Preserve retry/dirty state]
    E --> I[Draw with possibly stale contents]
    G --> I
Loading

Initial admission should be deliberately narrow

A first experiment should admit only textures that are:

  • RAM-backed rather than surface-sourced;
  • non-paletted;
  • represented by an existing complete binding;
  • using the same effective texture state and DMA address;
  • not known possibly_dirty at the first validation;
  • not overlapping a current or retained render surface;
  • not crossing a snapshot restore, renderer reset, or resource recreation;
  • not shared through another binding whose writes require independent invalidation.

Palette textures, surface aliases, dynamic DMA remaps, and same-address resource reuse should begin on the reference route. Expanding the admitted subset is a later experiment.

Frame boundary is part of the approximation

A “guest frame” must be defined precisely. A display-write or pg->frame_time transition may be a useful serial, but neither automatically proves that the title treats all texture writes within that interval as deferred to the next frame.

The required contract is not merely “refresh eventually.” It must state:

  • which event expires trust;
  • whether dirty evidence remains pending;
  • the maximum intended stale interval;
  • what happens when no normal frame boundary occurs;
  • how pause, snapshot, reset, and skipped presentation affect the serial.

Decisive stale-content fixture

1. Upload red texture bytes.
2. Draw the texture and complete reference validation.
3. Overwrite the same guest bytes with blue without rebinding.
4. Draw again before the trust boundary expires.
5. Advance the defined boundary.
6. Draw again.

The exact route establishes when blue becomes visible. The approximate route is expected to expose its difference at step 4 and must recover at the documented boundary. If blue never appears, dirty evidence or trust invalidation has been lost.

Repeat the fixture with:

  • palette-only writes;
  • a second binding sharing the same memory;
  • texture DMA descriptor remapping;
  • GPU-written surface content;
  • same-address resource destruction/recreation;
  • snapshot restore without advancing the serial.

Emulation boundary

The physical NV2A can observe memory changes according to its own fetch/cache behavior. Current xemu uses guest-memory dirty tracking, hashes, host images, and explicit surface aliases to approximate that behavior.

This shortcut does not claim improved hardware accuracy. It deliberately trades same-frame texture freshness for less host validation work.

Why it may help

The opportunity is:

repeated uses after first validation
× cost of dirty-page checks, cache propagation, overlap scans and hashes
− trust/version bookkeeping

It is worth continuing only if counters show frequent repeated validation of unchanged admitted bindings and meaningful renderer CPU time in the removed work.

Seen in xemu

The expensive operations and side effects are source-confirmed. No current benchmark proves that repeated same-frame validation is a material bottleneck or that affected titles tolerate delayed visibility.

The existing Clean Texture Stages shortcut is exact under its guards and should not be confused with this proposal. Clean-stage skipping still consults the relevant current validation state; frame trust intentionally stops consulting memory after the first admitted validation.

External precedent

Frame-scoped resource validity is common in engines that own all resource mutations. That precedent is weaker for an emulator because guest software can modify memory through paths the host renderer does not directly own. Therefore this must remain an approximation unless all same-frame mutation sources become tracked.

Counters

texture_validation_requests
frame_trust_opportunities
frame_trust_hits
frame_trust_reference_calls
frame_trust_reject_surface
frame_trust_reject_palette
frame_trust_reject_dma_change
frame_trust_reject_possible_dirty
memory_dirty_tests
cache_entries_visited
bytes_hashed
upload_bytes
stale_oracle_differences
frames_until_refresh
renderer_cpu_ns

Risks

  • Persistent stale content because dirty evidence was consumed or lost.
  • A frame serial that does not match the title’s resource lifetime.
  • Palette or alias writes escaping the binding version.
  • DMA remap without texture-state write.
  • Snapshot restore preserving stale trust stamps.
  • GPU-produced contents changing without a RAM dirty event.
  • Local CPU savings offset by later upload bursts or additional rendering errors.
  • A compatibility option being mislabeled as an exact optimization.

Qualification

  1. Implement the stale-content fixture and recovery oracle first.
  2. Keep reference and candidate fixtures independently seeded; reference validation mutates dirty state.
  3. Add per-reason admission counters without per-draw text logging.
  4. Run texture, palette, alias, DMA-remap, reset, restore, eviction, and teardown tests.
  5. Run Vulkan validation and full XISO correctness coverage.
  6. Use texture-heavy representative titles and compare renderer CPU, frame p95/p99, uploads, hashes, and visual differences.
  7. Define a title or compatibility scope before any product exposure.
  8. Reject the shortcut if the stale interval is not bounded and recoverable.

Disposition

Applicable only as an explicitly approximate, opt-in research shortcut. It must never be described as equivalent merely because most tested frames appear unchanged.

Related pages

Full technical reference

Primary reference: This subject page is the design and research reference for Frame-Scoped Texture Trust. Pull requests are implementation/test history only.
Lifecycle: Performance - Unproven · Mechanism: Explicit stale-within-frame approximation
Reviewed source: 322986f4c5502b3fa887a9605ac8cd88e9b2c579 · Acceptance class: intended exact or behavior-preserving for its admitted scope

1. Scope

Validate an admitted RAM-backed texture on first use in a guest frame, then bypass further dirty-page, overlap, and content checks until the defined next validation boundary.

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.

2. Source seams

File / function Role
hw/xbox/nv2a/pgraph/vk/texture.c check_texture_possibly_dirty() and binding validation

3. Current execution path

flowchart TD
    N0["Texture use"]
    N1["Dirty-page test-and-clear"]
    N0 --> N1
    N2["Propagate possibly-dirty state"]
    N1 --> N2
    N3["Surface-overlap/content checks"]
    N2 --> N3
    N4["Hash/upload/rebind if needed"]
    N3 --> N4
    N5["Repeat on later use"]
    N4 --> N5
Loading

4. Candidate execution path

flowchart TD
    N0["First admitted use in frame: full reference validation"]
    N1["Record frame serial and binding version"]
    N0 --> N1
    N2["Later same-frame use: trust retained binding"]
    N1 --> N2
    N3["Revalidate at next frame or invalidation"]
    N2 --> N3
Loading

5. Why it may help

Many textures are unchanged between uses, but current validation protects same-frame guest writes and aliasing. The proposed route intentionally permits a bounded stale interval and is therefore an approximation. It is useful as a cost-removal experiment and perhaps a compatibility mode, not as an exact optimization unless stronger write tracking is added.

6. Work removed

  • Repeated dirty-page checks
  • Repeated surface-overlap searches
  • Repeated content validation/hashing on trusted later uses

7. Work that remains mandatory

  • First-use validation
  • Texture binding and sampling
  • Next-boundary refresh
  • Immediate invalidation for tracked destructive events
  • Reference route for palette/surface/DMA complexity

8. Implementation sketch

if (policy == TEXTURE_TRUST_FRAME && binding->admitted &&
    binding->validation_frame == pg->guest_frame_serial &&
    binding->state_version == current_state_version) {
    stats.frame_trust_hits++;
    return retain_existing_binding(binding);
}

bool ok = validate_and_bind_reference(pg, stage);
if (ok && binding_is_admitted(binding)) {
    binding->validation_frame = pg->guest_frame_serial;
    binding->state_version = current_state_version;
}

The sketch defines the intended boundary; it is not a complete patch and does not replace source review, ownership design, or focused tests.

9. Correctness contract

  • The allowed stale interval must be explicit and measurable.
  • Skipped validation must not clear dirty evidence.
  • Snapshot/reset/recreation and frame-serial wrap must invalidate stamps.
  • Palette, surface-backed, remapped DMA, and shared-memory cases need separate admission or fallback.

10. State, ownership, invalidation, and fallback

Trust stamps belong to the renderer binding. They are invalidated on frame advance, binding/state version change, restore/reset, resource recreation, and any tracked write/alias event chosen by the policy.

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.

11. Advantages

  • Can remove substantial repeated coherency work
  • Very clear same-binary reference route
  • Useful for quantifying validation cost
  • Bounded approximation is easier to characterize than indefinite staleness

12. Costs and failure modes

  • Same-frame writes can produce stale pixels
  • Dirty test-and-clear ordering can be corrupted
  • Multiple bindings sharing memory complicate recovery
  • May hide real invalidation bugs

13. Evidence counters

Counters must be cheap, subsystem-owned, and snapshotted outside the hot loop. Record at least:

  • texture_validation_requests
  • first_frame_validations
  • frame_trust_hits
  • dirty_pages_tested
  • surface_overlap_queries
  • hash_bytes
  • uploads
  • stale_frames_detected

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.

14. Qualification matrix

  • Red draw, overwrite same bytes blue, draw again same frame, advance frame, draw
  • Palette-only changes
  • Shared-memory bindings
  • DMA remap
  • GPU-written content
  • Same-address resource reuse
  • Snapshot restore without frame advance
  • Long frame and serial wrap tests

Correctness runs may use validation layers, detailed comparisons, and fault injection. Performance acceptance must use optimized Release builds with matching symbols and minimal instrumentation.

15. Acceptance rule

Accept only as an explicitly approximate mode when cost reduction is material and stale duration/recovery match a documented contract.

16. Rejection rule

Reject as exact/default when same-frame changes are common, refresh can be missed, dirty evidence is lost, or the path produces persistent corruption.

17. Related architecture and history

Vulkan-Renderer · Performance-Unproven · Hot-Path-Vulkan-Clean-Texture-Stages · Using-Hot-Path-Shortcuts

PR pages may be linked as supporting evidence, but they are not substitutes for this reference.

Clone this wiki locally