Bound GPU backbuffer layer retention

This commit is contained in:
gamer147
2026-07-30 11:49:14 -04:00
parent 37bf8d31c8
commit d2efec2cd6
5 changed files with 84 additions and 30 deletions

View File

@@ -3876,11 +3876,15 @@ The port now gives op `0xd5` a dedicated non-presenting deadline wait which keep
barrier held, while backbuffer `0x222` suspends that barrier and synchronously snapshots the exact callback
state before returning. Incremental range publications preserve the preceding backbuffer: the software
backend skips its black clear and the GPU backend appends the new draw layers over its retained stage.
An explicit backbuffer `0x20e` disables preservation for the following publication, and ordinary `0x20c`
continues to replace the retained stage as a full reconstruction. This removes the pre-animation
full-popup frame and supplies the preserved pixels needed by the `[0,59999)` close endpoint without
special-casing BUNKI handles. A one-frame black flicker is still visible at the end of close, however, so
some later publication or retained-stage reset remains to be reconciled.
An explicit backbuffer `0x20e` disables preservation for the following publication. Native
`gfx_render_frame@0x4820b0`, used by ordinary `0x20c`, likewise has no implicit clear: it draws all visible
objects over the selected target and leaves clearing to `0x20e`. The software backend can apply that rule
literally to its pixel buffer. Godot GPU nodes require a separate retention policy: partial ranges append
over the retained stage, nonempty full publications replace/reuse the existing Sprite2D pool, and empty
full publications leave the preceding stage untouched. Treating every full publication as an append caused
unbounded draw-node accumulation and rapid slowdown. This split removes the pre-animation full-popup frame
and prevents BUNKI's post-close empty `0x20c`—issued after releasing both the capture and stable menu
objects—from reconstructing an empty selected set over black, without stacking complete scenes.
Surface text also remains modeled metadata rather than pixels. Selected-target publication now projects
that metadata into the destination surface, and the final Godot labels inherit the retained object's
@@ -3892,8 +3896,10 @@ text trails even though preserving the underlying software pixels or GPU draw la
Focused timed-callback boundary coverage passes with the complete 532-test engine suite. The Godot build
is warning-free and the Himegari-targeted threaded frontend selftest passes. User testing confirmed the
premature opening frame and accumulated text trails are gone. The close-only black frame remains the visual
acceptance gap.
premature opening frame and accumulated text trails are gone. The close-only black frame was traced to the
port's incorrect implicit clear on an empty full `0x20c`. The first preservation correction over-retained
GPU draw nodes; the bounded full/partial/empty publication policy passes the frontend selftest. User
retesting accepted both the restored performance and close behavior on 2026-07-30.
### Formatted integers on text surfaces — opcode `0x205` (2026-07-21)

View File

@@ -896,13 +896,13 @@ The worker clips the paired source and destination rectangles against both surfa
- **evidence:** Ghidra /v2: op_0x20b_handler@0x422d50 converts x/y/width/height to an exclusive rectangle, clamps alpha to 255, repacks RGB to native ARGB, and calls the surface manager's rectangle-fill path at 0x4790e0. HISTORY.BIN clears each 600x30 name strip on temporary surface 0xc1 before draw-string.
### 0x20c `present-frame` (present-frame, argc 0)
- **summary:** Present the composited frame; label_1235a uses this on the read/message-skip branch to expose the completed foreground endpoint immediately.
- **summary:** Draw every visible retained object over the selected target without implicitly clearing it, then present the composited backbuffer; label_1235a uses this on the read/message-skip branch to expose the completed foreground endpoint immediately.
- **grounding:** source=investigation, confidence=high
- **depends on:** 0x223, 0x1c7, 0x1cc
- **depended on by:** 0x21, 0x22, 0x23, 0x24, 0x25, 0x20d, 0x223
- **evidence:** Ghidra: dispatch table FUN_00413860 param_1[0x26e9f]=gfx_op_0x20c_present_frame; 0x26e9f-0x26c93=0x20c. 2026-07-08.
- **evidence:** Ghidra /v2: dispatch table FUN_00413860 param_1[0x26e9f]=gfx_op_0x20c_present_frame; 0x26e9f-0x26c93=0x20c. The handler calls gfx_render_frame@0x4820b0, which begins a D3D scene, consumes queued surface commands, draws all visible retained objects, ends the scene, and presents the backbuffer without calling d3d_clear_render_target_black; opcode 0x20e is the separate clear. Refined 2026-07-30.
Native handler gfx_op_0x20c_present_frame -> gfx_render_frame @0x4820b0. This is an explicit retained-state publication boundary, not a continuously visible object-store mutation. The read/message-skip branch resets the animation service then presents; the port publishes and snaps pending 0x223 state here. Normal playback branches to 0x21c, which owns repeated render/wait/resume. Headless hosts remain non-blocking.
Native handler gfx_op_0x20c_present_frame -> gfx_render_frame @0x4820b0. This is an explicit retained-state publication boundary, not a continuously visible object-store mutation. The renderer does not clear its selected target: opcode 0x20e owns explicit black clearing, so full-frame publication preserves preceding target pixels beneath its redraw. The read/message-skip branch resets the animation service then presents; the port publishes and snaps pending 0x223 state here. Normal playback branches to 0x21c, which owns repeated render/wait/resume. Headless hosts remain non-blocking.
### 0x20d `select-render-target` (select-render-target, argc 1)
- **summary:** Select an offscreen surface slot as Direct3D render target 0, or restore the device backbuffer when the operand is at least 1000.