Skip to content

fix(webgl): stale GPU quads on rtt toggle and generic reorder (surgical guard) - #871

Closed
wouterlucas wants to merge 2 commits into
mainfrom
fix/rtt-stale-slot-guard
Closed

fix(webgl): stale GPU quads on rtt toggle and generic reorder (surgical guard)#871
wouterlucas wants to merge 2 commits into
mainfrom
fix/rtt-stale-slot-guard

Conversation

@wouterlucas

Copy link
Copy Markdown
Contributor

Fixes the reported “all blue images” on dirty repaints (PR #861 surgical path).

Root cause

  • CoreNode.set rtt() changed which buffer (quadBuffer vs rttQuadBuffer in WebGlRenderer.addQuad()) a node writes to without rebuilding the flat render list. Next frame chose SURGICAL (dirty/len < 0.4) and only bufferSubData’d dirty quads — outer quads stayed stale on the GPU (blue 0x0000ffff / white placeholder).
  • Same class via any reorder that bypasses Stage.requestRenderListUpdate()WebGlRenderer.invalidateQuadBuffer() (direct stage.renderListNodes splice).

Fix

  • src/core/CoreNode.ts:2727 set rtt now calls stage.requestRenderListUpdate()invalidateQuadBuffer() (quadBufferIndex=-1, needsFull=true) so the next frame does a single bufferData with re-assigned contiguous 0,20,40… slots. Mirrors clipping/clipRadius/zIndex.
  • src/core/renderers/webgl/WebGlRenderer.ts:505 addQuad() generic guard: quadBufferIndex != -1 && quadBufferIndex != curBufferIdx && !isQuadDirty → isQuadDirty=true (no !isRTT gate, just index vs index). Heals any bypass via surgical re-upload; 2/3 dirty then falls back to FULL via 0.4 threshold.

Test

  • src/core/renderers/webgl/WebGlRenderer.dirtyQuadBuffer.test.ts:507rtt toggle invalidates → FULL (faked stage.txManager + gpuBuffer mirror).
  • examples/tests/dirty-stale-slot.ts — minimal 480×160 a red b green c blue, b green→light-green→orange→green (4 snapshots initial/correct-reorder-full/surgical-color/recover).

Refs: #861

…al guard)

rtt toggle changed which buffer (main vs rttQuadBuffer) a node writes to
without rebuilding the flat render list, so the surgical bufferSubData
path left stale quads on the GPU (blue/white flash, PR #861).

- CoreNode.set rtt now calls stage.requestRenderListUpdate() ->
  WebGlRenderer.invalidateQuadBuffer() so the next frame does a FULL
  bufferData with re-assigned contiguous slots.
- WebGlRenderer.addQuad() adds a generic guard: if
  quadBufferIndex != -1 && quadBufferIndex != curBufferIdx && !isQuadDirty
  force isQuadDirty=true (no !isRTT gate, just index vs index). This heals
  any reorder that bypassed invalidation via surgical re-upload.
- Add minimal repro examples/tests/dirty-stale-slot.ts (3 rects,
  green->light-green->orange) and unit test rtt toggle -> FULL.

Fixes the reported 'all blue images' on dirty repaints.

Refs: #861
Captured via docker (RUNTIME_ENV=ci) for the 4 automation steps:
initial, correct-reorder-full, surgical-color, recover.

Outer rects stay red/blue across all frames (no stale quads); the
middle rect cycles green -> light-green -> orange -> green.
@wouterlucas wouterlucas closed this Sep 4, 2026
@wouterlucas
wouterlucas deleted the fix/rtt-stale-slot-guard branch September 4, 2026 20:53
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