Implement native 0x202 color interpolation
This commit is contained in:
@@ -756,14 +756,15 @@ Ghidra functions renamed + plate-commented, saved).
|
||||
RGB888; `<0` = none; else texels whose `(R,G,B)` equal the key become transparent when the surface image
|
||||
is loaded/cached.
|
||||
- **`0x202` (`gfx_op_0x202_worker_set_color_anim` `0x47ea00`)**: sets an **animated** color/alpha target
|
||||
`obj+0x64 = packedARGB`, the color-anim active bit, resets progress `obj+0x34=0`. Animates over the global
|
||||
clock (`0x238`).
|
||||
`obj+0x64 = packedARGB`, the color-anim active bit, and resets shared start `obj+0x34=0`. Operands 2/3
|
||||
are delay/duration at `obj+0x38/+0x4c`; sampling uses frame clock `ctx+0xb550`, not op `0x238`.
|
||||
- **`0x203` (`gfx_op_0x203_worker_set_color` `0x47e9b0`)**: sets a **static** color/alpha `obj+0x60`, no anim
|
||||
bit. Immediate per-object modulation.
|
||||
- **Blit** (`gfx_object_blit_d3d9` `0x4774c0`): selects a **blend mode** (`local_2c`: 0 opaque, 1 alpha
|
||||
`SRCALPHA/INVSRCALPHA`, 2/3 additive/special for glow/flash) and passes a modulation color/alpha to the
|
||||
device draw. Slice A ports the **alpha** path (fades); additive (glow) is deferred (its `local_2c` source
|
||||
field is not yet pinned).
|
||||
field is `obj+0x30`, the value written by op `0x203`. Mode 1 uses packed ARGB alpha as opacity and RGB
|
||||
as multiplicative D3D modulation. Modes 2/3 remain separately scoped beyond the completed mode-1 path.
|
||||
|
||||
**Interpolation RE pass (2026-07-08, stalled → both deferrals confirmed).** Attempted to pin how a `0x202`
|
||||
fade animates so smooth ramping could join slice A. Findings (Ghidra `gfx_object_anim_interpolate`
|
||||
@@ -778,6 +779,20 @@ one-shot fade's exact source→target→easing is still unresolved and would tak
|
||||
stays deferred**; slice A ships the static end-state (which reaches the correct final alpha/tint and fixes
|
||||
the stuck-opaque bug), with interpolation as a scoped follow-up.
|
||||
|
||||
**Resolution (2026-07-10 — supersedes the deferral above).** The missing consumer was the bit-1 branch in
|
||||
`gfx_object_apply_transform_channels` (`0x472f00`), before its matrix channels. It seeds shared start
|
||||
`obj+0x34` from `ctx+0xb550`; holds current packed ARGB `obj+0x60` through delay `+0x38`; then performs an
|
||||
integer, bytewise linear interpolation to target `+0x64` for duration `+0x4c`. At natural completion—or
|
||||
when `ctx+0xb55c == 1` requests forced completion—the target commits to current, delay/duration clear,
|
||||
target becomes `0xffffffff`, and the one-shot active bit clears when no color/matrix/src-rect sibling remains.
|
||||
The object-local override bit at `+0x2d0` suppresses the global force. Negative alpha/RGB target operands
|
||||
independently preserve their bytes from current `+0x60`.
|
||||
|
||||
The port now carries current and target separately and samples them from the unified `FrameClock`; an op
|
||||
`0x203` static write after `0x202` therefore becomes the ramp's current value rather than overwriting its
|
||||
target. Mode 0 retains the established CG/tint/fill behavior; mode 1 now uses native alpha opacity plus RGB
|
||||
modulation. `draw-string 0x204`/`0x7a` remains a separate dependency.
|
||||
|
||||
### SC0000 anim/transform/spritesheet cluster — op→field map (2026-07-08)
|
||||
|
||||
Reversed for the animation cluster slice (spec `docs/superpowers/specs/2026-07-08-sc0000-anim-transform-cluster-design.md`).
|
||||
|
||||
@@ -132,14 +132,14 @@ SC0000 label_1235a reaches this when the OR of 0x1c7 message-skip and 0x1cc read
|
||||
- **evidence:** Ghidra handler 0x4227b0 (dispatch ctx[0x26c93+0x1ff]); FUN_0047e800(op1,(float)op2,(float)op3,(float)op4).
|
||||
|
||||
### 0x202 `gfx-blit-color` (gfx-blit-color, argc 5)
|
||||
- **summary:** 0x202 (handle)(x)(y)(alpha)(color) — gfx cmd-type 0xb. Handler gfx_op_0x202_blit_color @0x4228d0: worker gfx_op_0x202_worker_set_color_anim @0x47ea00 sets an ANIMATED color/alpha target (obj+0x64) + anim bit; packs ARGB from alpha(op4, ≥0x100→0xff, <0→FUN_0047f3e0) and color(op5, <0→FUN_0047f3e0). C# VM (2026-07-08 blend slice): routes through GfxState.SetObjectColor → the compositor applies STATIC alpha/tint (BlendKind.Alpha); smooth color-anim interpolation deferred. See docs/engine-re.md §Blend & transparency.
|
||||
- **summary:** 0x202 (handle)(delay_ms)(duration_ms)(alpha)(color) — arm the one-shot packed-ARGB channel. Worker gfx_op_0x202_worker_set_color_anim @0x47ea00 resets shared start obj+0x34, writes delay +0x38, duration +0x4c, and target +0x64. gfx_object_apply_transform_channels @0x472f00 linearly interpolates each byte from current +0x60 on frame clock ctx+0xb550, commits the target, clears timing, writes target -1, and clears the one-shot active bit when all sibling channels finish. Negative alpha/RGB independently preserve current bytes. Implemented in GfxState with synchronized current/target timeline evidence; draw-string 0x204/0x7a remains separate.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra handler 0x4228d0 (dispatch ctx[0x26c93+0x202]); packs (alpha<<24|rgb) from operands 4/5, FUN_0047ea00(op1,op2,op3,packed).
|
||||
- **evidence:** Ghidra handler 0x4228d0 packs operands 4/5 and calls worker 0x47ea00(handle,delay,duration,packed). Consumer 0x472f00: shared start +0x34; color delay/duration +0x38/+0x4c; current/target +0x60/+0x64; frame clock ctx+0xb550; bytewise integer LERP; natural or ctx+0xb55c forced completion. /v2 annotated and saved 2026-07-10.
|
||||
|
||||
### 0x203 `gfx-draw-color` (gfx-draw-color, argc 4)
|
||||
- **summary:** 0x203 (handle)(mode)(alpha)(color) — gfx cmd-type 9. Worker stores mode at obj+0x30 and STATIC packed color at obj+0x60. Negative alpha/RGB preserve current static bytes through FUN_0047f3e0. Mode 2 is the 0x223 transition-source alpha/identity path (0xffffffff = opaque identity, not solid white); mode 0 retains the established tint-strength behavior.
|
||||
- **summary:** 0x203 (handle)(mode)(alpha)(color) — gfx cmd-type 9. Worker stores the D3D blend selector at obj+0x30 and STATIC packed color at obj+0x60. Negative alpha/RGB preserve current static bytes. Mode 0 retains established opaque CG/tint/fill behavior; mode 1 is SRCALPHA/INVSRCALPHA with ARGB alpha opacity and multiplicative RGB modulation; mode 2 is the 0x223 transition-source identity path.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra handler 0x4229a0 (dispatch ctx[0x26c93+0x203]); negative operands read current obj+0x60 via FUN_0047f3e0, then gfx_op_0x203_worker_set_color stores op2 at obj+0x30 and packed ARGB at obj+0x60. SC0000 transition setup uses mode 2 with -1/-1 before 0x21d/0x223; synchronized window pixels prove treating 0xffffffff as generic white tint is wrong.
|
||||
- **evidence:** Ghidra handler 0x4229a0; negative operands read current obj+0x60, then worker 0x47e9b0 stores op2 at obj+0x30 and ARGB at +0x60. gfx_object_composite call-site 0x47f78f passes +0x30/+0x60 directly to gfx_object_blit_d3d9; mode 1 sets D3DRS SRCALPHA/INVSRCALPHA and the packed color is the device draw modulation. Mode 2 transition setup and synchronized pixels prove 0xffffffff is identity, not solid white.
|
||||
|
||||
### 0x208 `get-texture-size` (get-texture-size, argc 3)
|
||||
- **summary:** 0x208 (slot)(out_w)(out_h) — writes the loaded texture's width/height into two output globals; keystone for bytecode-computed sprite/bg geometry (SC0000 label_12649)
|
||||
|
||||
@@ -939,3 +939,44 @@ apart from the pre-existing nullable warning and threaded `SELFTEST OK`; all six
|
||||
opcode/ctx lint, 481-script decode validation, RECOVER, and Windows CR-aware `git diff --check` clean.
|
||||
The plain check reports only the generated reference's CRLF on its newly added row. SC0000 coverage is
|
||||
**85/129 handled (65.9%)**, 44 GAP ops / 598 GAP instructions. No commit was made.
|
||||
|
||||
### A2b — op 0x202 one-shot color/presentation ✅ DONE (2026-07-10)
|
||||
|
||||
**Native contract.** `gfx_op_0x202_worker_set_color_anim@0x47ea00` writes target packed ARGB at
|
||||
`obj+0x64`, delay/duration at `+0x38/+0x4c`, and resets the one-shot family's shared start `+0x34`.
|
||||
The missing consumer is the bit-1 path in `gfx_object_apply_transform_channels@0x472f00`: it seeds start
|
||||
from frame time `ctx+0xb550`, performs an integer bytewise current-`+0x60` to target-`+0x64` LERP, then
|
||||
commits target, clears timing, writes target `-1`, and clears the active bit once all sibling one-shot
|
||||
channels finish. This is ordinary presentation-clock coupling, not op `0x238`'s separate service clock.
|
||||
`ctx+0xb55c == 1` forces completion unless the object-local `+0x2d0` override bit is set. `/v2` comments
|
||||
were corrected and saved.
|
||||
|
||||
**Port and endpoint diagnosis.** `GfxState` now retains current/target/delay/duration separately, shares
|
||||
the start timestamp with scale/rotation/translation, and exposes synchronized `color=current->target` plus
|
||||
progress in the compositor/timeline log. This fixes the initial black-to-EV049AA cut: windowed frames sample
|
||||
the full-screen `0xcf08` black owner through 11 monotonically decreasing states from `1.00` to `0.00` instead
|
||||
of installing the transparent endpoint immediately. The ADV textbox backing `0xd2f0` is a real 800×227 black
|
||||
fill at `(0,373)`; repeated show/hide passages now produce intermediate samples (for example
|
||||
`0.00, 0.26, 0.54, 0.82, 1.00` and the reverse), synchronized to the executing `0x202` sites around
|
||||
`0x12283..0x12342`.
|
||||
|
||||
The remaining full-white runs are not a stuck color clock or d2f0 endpoint: pixel windows place them after
|
||||
d2f0 is transparent, while object timelines show `AE*` handles first becoming visible in mode 0 with default
|
||||
`0xffffffff` before their later mode-1 initializer. Native call-site disassembly proves op `0x203`'s
|
||||
`obj+0x30` is passed directly as the D3D blend selector and mode 1 uses `SRCALPHA/INVSRCALPHA`; the port now
|
||||
uses ARGB alpha as opacity and RGB as multiplicative modulation for that mode. The long white intervals did
|
||||
not disappear, which localizes the residual to when intermediate retained state is presented/batched, not to
|
||||
0x202 interpolation. Altering that scheduler boundary without a matching native present trace would exceed
|
||||
this bounded slice. `draw-string 0x204`/`0x7a` was not pulled in.
|
||||
|
||||
Windowed evidence: 340 PNGs, 107 distinct pixel states; black-to-first-CG and d2f0 ramps agree with synchronized
|
||||
object progress. Focused tests cover exact integer midpoint math, delay, natural completion field cleanup,
|
||||
negative sentinel resolution, the 0x202-then-0x203 current/target pattern, shared color/matrix clock start,
|
||||
mode-1 opacity/modulation, and the software rasterizer.
|
||||
|
||||
**Validation:** engine **108/108**; full sweep unchanged at **284 exit / 13 STEP-LIMIT**; Godot build clean
|
||||
apart from the pre-existing nullable warning and threaded `SELFTEST OK`; all seven Python test scripts,
|
||||
opcode/ctx lint, 481-script decode validation, and RECOVER clean. SC0000 coverage remains **85/129 handled
|
||||
(65.9%)**, 44 GAP ops / 598 GAP instructions. The differential oracle retains its prior branch-state
|
||||
divergence after `0x12031`; it agrees through the executed `0x202`/`0x203` sequence and does not implicate
|
||||
this slice. Windows CR-aware whitespace validation is clean.
|
||||
|
||||
@@ -138,7 +138,9 @@ texture ops (no GPU context) — run windowed for real scenes. User args (after
|
||||
- `--speed <f>` — scale the unified runtime clock (VM cadence, sleeps, and retained animation) without auto-advancing input waits. Values 0.05–8 are accepted; `--speed 0.25` is useful for transform inspection, while 1.0 is normal playback.
|
||||
- `--gfx-log <file>` — **compositor + op diagnostic** (the tool that root-caused the grey background). Logs, per rendered frame, only the objects whose draw outcome **CHANGED** (drawn↔skip↔gone, resId, resolved file, `slot`, `src`/`dst`, `op`acity, `tintStr`ength) — quiet until something actually changes, so the exact frame a layer drops out (and why) stands out. Also traces every `set-texture`/`create-texture` **slot assignment** (via `GodotAdvHost.TraceOps`). Works live or with `--shot-sequence`. Use it before theorising about layering/blend/geometry: it showed the grey BG = the slot-selecting globals resolving to 0 → every texture collapsing into slot 0 (see engine-re.md §"Grey-background root cause"). e.g. `godot --path godot -- --boot --gfx-log out/gfx.log` then click to the bad page.
|
||||
Matrix-channel outcomes also include `base`, `anchor`, projected `dst`, sampled `scale`/`trans`, and
|
||||
one-shot-plus-cyclic `rot`ation angles. Parent directories are created automatically.
|
||||
one-shot-plus-cyclic `rot`ation angles. Active op-`0x202` outcomes include packed `color=current->target`
|
||||
and `colorProgress`, synchronized with the same frame/clock in `--timeline-log`. Parent directories are
|
||||
created automatically.
|
||||
- `--transition-click-ms <n>` — diagnostic-only input injector: after a foreground transition has been active for `n` virtual milliseconds, send one click through the real input lifecycle. The click completes/consumes the transition and does not advance a stable page. Use with `--timeline-log`, `--gfx-log`, and windowed `--shot-sequence`; omit for normal play.
|
||||
- `--timeline-log <jsonl>` — diagnostic-only synchronized event stream for a real Godot run. Records every
|
||||
executed script byte offset/opcode, virtual time/frame, VM state changes (`running`, `sleep`, `input-wait`,
|
||||
|
||||
Reference in New Issue
Block a user