Extract VM retained object handler

This commit is contained in:
gamer147
2026-08-02 23:50:45 -04:00
parent 11f37672a0
commit 0941be549c
4 changed files with 121 additions and 62 deletions

View File

@@ -180,7 +180,9 @@ positioned movie playback, movie surface metadata/activity queries, and movie-ma
likewise retains and routes the movie labels. `engine/Age.Engine/Vm/VirtualMachine.Surface.cs` owns surface likewise retains and routes the movie labels. `engine/Age.Engine/Vm/VirtualMachine.Surface.cs` owns surface
allocation/loading, texture binding and sizing, mutable surface fill/copy, render-target control, and transient allocation/loading, texture binding and sizing, mutable surface fill/copy, render-target control, and transient
surface release; its labels remain at their existing dispatcher positions around the retained numeric-glyph and surface release; its labels remain at their existing dispatcher positions around the retained numeric-glyph and
object cases. object cases. `engine/Age.Engine/Vm/VirtualMachine.RetainedObjects.cs` owns retained-object registry queries,
default-slot and geometry mutation, direct and range transforms, clone, and erase dispatch; animation, surface,
ADV-binding, and presentation labels remain in their respective dispatcher groups.
The disposable `build/page-map-<SCENE>.jsonl` files are produced by editor/development Godot runs and map The disposable `build/page-map-<SCENE>.jsonl` files are produced by editor/development Godot runs and map
runtime ADV page ordinals to their authoritative script offsets for `tools/locate_page.py`. Packaged exports runtime ADV page ordinals to their authoritative script offsets for `tools/locate_page.py`. Packaged exports

View File

@@ -642,6 +642,13 @@ do not mix mechanical moves with semantic changes.
existing positions, including the separate routing groups around numeric-glyph and retained-object cases; existing positions, including the separate routing groups around numeric-glyph and retained-object cases;
public VM behavior and case-body logic remain unchanged. Runtime validation remains green. public VM behavior and case-body logic remain unchanged. Runtime validation remains green.
The fourth bounded `VirtualMachine.Step` extraction moved retained-object registry queries, default-slot and
geometry mutation, direct and embedded-range transforms, clone, and erase dispatch into
`engine/Age.Engine/Vm/VirtualMachine.RetainedObjects.cs`. The top-level dispatcher retains all labels at their
existing positions and routes the separated groups through `StepRetainedObject`; timed/cyclic animation,
color, surface release, ADV binding, and presentation behavior remain outside this handler. Runtime validation
remains green.
**Gate:** no externally visible behavior or command changes; generated artifacts are byte-identical where **Gate:** no externally visible behavior or command changes; generated artifacts are byte-identical where
deterministic, and the corresponding engine, Python, Godot, and corpus validations remain green after deterministic, and the corresponding engine, Python, Godot, and corpus validations remain green after
each domain move. each domain move.
@@ -1014,7 +1021,8 @@ layer's rendering diverges from ADV; save layout.
Continue step 2 of the **codebase consolidation** maintenance slice: behavior-neutral physical splits backed Continue step 2 of the **codebase consolidation** maintenance slice: behavior-neutral physical splits backed
by the tracked launcher and layered validation driver. With the planned `Main`, `GodotAdvHost`, and `GfxState` by the tracked launcher and layered validation driver. With the planned `Main`, `GodotAdvHost`, and `GfxState`
domains isolated and the audio, movie, and surface/texture `VirtualMachine.Step` families routed through domain domains isolated and the audio, movie, and surface/texture `VirtualMachine.Step` families routed through domain
handlers, extract the retained-object query/mutation and transform opcode family next without replacing the handlers, with retained-object query/mutation and transform dispatch now isolated as well, extract the retained
proven dispatcher or changing public types, commands, and generated output. animation/color/spritesheet opcode family next without replacing the proven dispatcher or changing public types,
commands, and generated output.
Concrete playthrough blockers may still preempt this bounded maintenance work; the consolidation effort does Concrete playthrough blockers may still preempt this bounded maintenance work; the consolidation effort does
not replace Phase B gameplay validation or the open cross-platform gates. not replace Phase B gameplay validation or the open cross-platform gates.

View File

@@ -0,0 +1,90 @@
using Age.Engine.Model;
namespace Age.Engine.Vm;
public sealed partial class VirtualMachine
{
private int StepRetainedObject(string label, IReadOnlyList<Operand> a, int pc)
{
switch (label)
{
case "clear-retained-gfx-objects": // 0x1f6: erase object records, but preserve surfaces
Gfx.ClearRetainedObjects(); return pc + 1;
case "query-gfx-object?": // 0x215 (out)(handle) -> slot | -1
if (_diagSetTexture) // reuse the flag: show what the slot query returns (grey-BG slot dig)
{
long h = Read(a[1]);
System.Console.Error.WriteLine($"[query] handle=0x{h:x} handleOp=(type={a[1].Type} val=0x{a[1].Value:x}) " +
$"-> QuerySlot={Gfx.QuerySlot(h)} objectPresent={Gfx.TryGet(h) != null}");
}
Write(a[0], Gfx.QuerySlot(Read(a[1]))); return pc + 1;
case "query-gfx-field?": // 0x216 (out)(idx)
Write(a[0], Gfx.QueryField(Read(a[1]))); return pc + 1;
case "get-gfx-geom3?": // 0x218 (handle)(outA)(outB)(outC) <- V18
{
var v = Gfx.TryGet(Read(a[0]))?.V18 ?? default;
Write(a[1], v.X); Write(a[2], v.Y); Write(a[3], v.Z); return pc + 1;
}
case "get-gfx-geom3-b?": // 0x21a (handle)(outA)(outB)(outC) <- V24
{
var v = Gfx.TryGet(Read(a[0]))?.V24 ?? default;
Write(a[1], v.X); Write(a[2], v.Y); Write(a[3], v.Z); return pc + 1;
}
case "set-gfx-geom3": // 0x217 (handle)(a)(b)(c) -> V18
Gfx.SetObjectAnchor(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1;
case "set-gfx-geom3-b": // 0x219 (handle)(a)(b)(c) -> V24
Gfx.SetObjectPosition(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1;
case "u0041AF00": // 0x80: default object slot substituted by native op 0x1d9
case "set-default-gfx-object-slot":
Gfx.SetDefaultObjectSlot((int)Read(a[0])); return pc + 1;
// ---- SC0000 anim/transform/spritesheet cluster (docs/engine-re.md §"SC0000 anim ... cluster") ----
case "u00421DD0": // 0x22f set-position: (handle)(op2)(x)(y)(z) -> base position (direct set)
Gfx.SetObjectPosition(Read(a[0]), (Read(a[2]), Read(a[3]), Read(a[4]))); return pc + 1;
case "u004219E0": // pre-reference compatibility
case "set-gfx-range-transform": // 0x229 (first)(count)(anchor x/y/z)
Gfx.SetRangeTransform(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4])));
return pc + 1;
case "u00421A90": // pre-reference compatibility
case "set-gfx-range-scale-current": // 0x22a (sx%)(sy%)(sz%)
Gfx.SetRangeScaleCurrent((Read(a[0]), Read(a[1]), Read(a[2]))); return pc + 1;
case "u00421BD0": // pre-reference compatibility
case "set-gfx-range-translation-current": // 0x22c (tx)(ty)(tz)
Gfx.SetRangeTranslationCurrent((Read(a[0]), Read(a[1]), Read(a[2]))); return pc + 1;
case "u00421C60": // pre-reference compatibility
case "set-gfx-range-scale-target": // 0x22d (delay)(duration)(sx%)(sy%)(sz%)
Gfx.SetRangeScaleChannel(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4])));
return pc + 1;
case "u00421940": // 0x228: (succ)(handle)(outX)(outY)(outZ) <- target translation matrix
{
if (Gfx.TryQueryTranslationTarget(Read(a[1]), out var v))
{
Write(a[2], (long)v.X); Write(a[3], (long)v.Y); Write(a[4], (long)v.Z);
Write(a[0], 0);
}
else Write(a[0], 1); // native missing-object path leaves output operands untouched
return pc + 1;
}
case "set-gfx-geom3-c": // 0x1ff: set current translation matrix
Gfx.SetCurrentTranslation(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1;
case "u00420620": // upstream ABI label
case "gfx-set-scale-current": // 0x1fd (handle)(sx%)(sy%)(sz%) -> current scale matrix
Gfx.SetCurrentScale(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1;
case "set-current-rotation-axis-angle": // 0x1fe (handle)(axis x/y/z)(angle degrees)
Gfx.SetCurrentRotation(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3])), Read(a[4]));
return pc + 1;
case "gfx-elem-erase": // 0x1f7 (handle)(count) — erase retained-object range
{
long first = Read(a[0]), count = Read(a[1]);
Gfx.EraseRange(first, count);
foreach (int layoutSlot in TextHistory.LayoutsCoveredByTextObjectErase(first, count))
_host.ClearRenderedAdvTextLayout(layoutSlot);
return pc + 1;
}
case "clone-gfx-object": // 0x21d (source handle)(destination handle)
Gfx.CloneObject(Read(a[0]), Read(a[1])); return pc + 1;
default:
throw new InvalidOperationException($"Non-retained-object opcode routed to retained-object handler: {label}");
}
}
}

View File

@@ -2490,8 +2490,8 @@ public sealed partial class VirtualMachine
case "u00420D50": case "u00420D50":
case "copy-surface-rect": case "copy-surface-rect":
return StepSurface(label, a, pc); return StepSurface(label, a, pc);
case "clear-retained-gfx-objects": // 0x1f6: erase object records, but preserve surfaces case "clear-retained-gfx-objects":
Gfx.ClearRetainedObjects(); return pc + 1; return StepRetainedObject(label, a, pc);
case "select-render-target": case "select-render-target":
case "clear-render-target": case "clear-render-target":
case "release-transient-surfaces": case "release-transient-surfaces":
@@ -2528,51 +2528,27 @@ public sealed partial class VirtualMachine
case "u00422B80": case "u00422B80":
case "play-movie-to-surface-at-position": case "play-movie-to-surface-at-position":
return StepMovie(label, ins, pc); return StepMovie(label, ins, pc);
case "query-gfx-object?": // 0x215 (out)(handle) -> slot | -1 case "query-gfx-object?":
if (_diagSetTexture) // reuse the flag: show what the slot query returns (grey-BG slot dig) case "query-gfx-field?":
{ case "get-gfx-geom3?":
long h = Read(a[1]); case "get-gfx-geom3-b?":
System.Console.Error.WriteLine($"[query] handle=0x{h:x} handleOp=(type={a[1].Type} val=0x{a[1].Value:x}) " + case "set-gfx-geom3":
$"-> QuerySlot={Gfx.QuerySlot(h)} objectPresent={Gfx.TryGet(h) != null}"); case "set-gfx-geom3-b":
}
Write(a[0], Gfx.QuerySlot(Read(a[1]))); return pc + 1;
case "query-gfx-field?": // 0x216 (out)(idx)
Write(a[0], Gfx.QueryField(Read(a[1]))); return pc + 1;
case "get-gfx-geom3?": // 0x218 (handle)(outA)(outB)(outC) <- V18
{
var v = Gfx.TryGet(Read(a[0]))?.V18 ?? default;
Write(a[1], v.X); Write(a[2], v.Y); Write(a[3], v.Z); return pc + 1;
}
case "get-gfx-geom3-b?": // 0x21a (handle)(outA)(outB)(outC) <- V24
{
var v = Gfx.TryGet(Read(a[0]))?.V24 ?? default;
Write(a[1], v.X); Write(a[2], v.Y); Write(a[3], v.Z); return pc + 1;
}
case "set-gfx-geom3": // 0x217 (handle)(a)(b)(c) -> V18
Gfx.SetObjectAnchor(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1;
case "set-gfx-geom3-b": // 0x219 (handle)(a)(b)(c) -> V24
Gfx.SetObjectPosition(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1;
case "u0041AF00": // 0x80: default object slot substituted by native op 0x1d9 case "u0041AF00": // 0x80: default object slot substituted by native op 0x1d9
case "set-default-gfx-object-slot": case "set-default-gfx-object-slot":
Gfx.SetDefaultObjectSlot((int)Read(a[0])); return pc + 1; return StepRetainedObject(label, a, pc);
// ---- SC0000 anim/transform/spritesheet cluster (docs/engine-re.md §"SC0000 anim ... cluster") ---- // ---- SC0000 anim/transform/spritesheet cluster (docs/engine-re.md §"SC0000 anim ... cluster") ----
case "u00421DD0": // 0x22f set-position: (handle)(op2)(x)(y)(z) -> base position (direct set) case "u00421DD0": // 0x22f set-position: (handle)(op2)(x)(y)(z) -> base position (direct set)
Gfx.SetObjectPosition(Read(a[0]), (Read(a[2]), Read(a[3]), Read(a[4]))); return pc + 1;
case "u004219E0": // pre-reference compatibility case "u004219E0": // pre-reference compatibility
case "set-gfx-range-transform": // 0x229 (first)(count)(anchor x/y/z) case "set-gfx-range-transform": // 0x229 (first)(count)(anchor x/y/z)
Gfx.SetRangeTransform(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4])));
return pc + 1;
case "u00421A90": // pre-reference compatibility case "u00421A90": // pre-reference compatibility
case "set-gfx-range-scale-current": // 0x22a (sx%)(sy%)(sz%) case "set-gfx-range-scale-current": // 0x22a (sx%)(sy%)(sz%)
Gfx.SetRangeScaleCurrent((Read(a[0]), Read(a[1]), Read(a[2]))); return pc + 1;
case "u00421BD0": // pre-reference compatibility case "u00421BD0": // pre-reference compatibility
case "set-gfx-range-translation-current": // 0x22c (tx)(ty)(tz) case "set-gfx-range-translation-current": // 0x22c (tx)(ty)(tz)
Gfx.SetRangeTranslationCurrent((Read(a[0]), Read(a[1]), Read(a[2]))); return pc + 1;
case "u00421C60": // pre-reference compatibility case "u00421C60": // pre-reference compatibility
case "set-gfx-range-scale-target": // 0x22d (delay)(duration)(sx%)(sy%)(sz%) case "set-gfx-range-scale-target": // 0x22d (delay)(duration)(sx%)(sy%)(sz%)
Gfx.SetRangeScaleChannel(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4]))); return StepRetainedObject(label, a, pc);
return pc + 1;
case "u004223C0": // 0x239 spritesheet cell: (handle)(delay)(duration)(frame count)(columns)(cell) case "u004223C0": // 0x239 spritesheet cell: (handle)(delay)(duration)(frame count)(columns)(cell)
Gfx.SetSrcRect(Read(a[0]), Read(a[3]), Read(a[4]), Read(a[5]), 0); return pc + 1; Gfx.SetSrcRect(Read(a[0]), Read(a[3]), Read(a[4]), Read(a[5]), 0); return pc + 1;
case "reset-gfx-cyclic-animations": // 0x230: stop all five retained looping channels case "reset-gfx-cyclic-animations": // 0x230: stop all five retained looping channels
@@ -2586,29 +2562,18 @@ public sealed partial class VirtualMachine
Gfx.SetScaleCycle(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4]))); Gfx.SetScaleCycle(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4])));
return pc + 1; return pc + 1;
case "u00421940": // 0x228: (succ)(handle)(outX)(outY)(outZ) <- target translation matrix case "u00421940": // 0x228: (succ)(handle)(outX)(outY)(outZ) <- target translation matrix
{ return StepRetainedObject(label, a, pc);
if (Gfx.TryQueryTranslationTarget(Read(a[1]), out var v))
{
Write(a[2], (long)v.X); Write(a[3], (long)v.Y); Write(a[4], (long)v.Z);
Write(a[0], 0);
}
else Write(a[0], 1); // native missing-object path leaves output operands untouched
return pc + 1;
}
case "u00422930": case "u00422930":
case "query-surface-stop-time-ms": case "query-surface-stop-time-ms":
case "query-movie-surface-active": case "query-movie-surface-active":
return StepMovie(label, ins, pc); return StepMovie(label, ins, pc);
case "sample-frame-time": // 0x23c: previous <- current; current <- monotonic time case "sample-frame-time": // 0x23c: previous <- current; current <- monotonic time
Gfx.SampleFrameTime(_host.InputClockMilliseconds); return pc + 1; Gfx.SampleFrameTime(_host.InputClockMilliseconds); return pc + 1;
case "set-gfx-geom3-c": // 0x1ff: set current translation matrix case "set-gfx-geom3-c":
Gfx.SetCurrentTranslation(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1;
case "u00420620": // upstream ABI label case "u00420620": // upstream ABI label
case "gfx-set-scale-current": // 0x1fd (handle)(sx%)(sy%)(sz%) -> current scale matrix case "gfx-set-scale-current": // 0x1fd (handle)(sx%)(sy%)(sz%) -> current scale matrix
Gfx.SetCurrentScale(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3]))); return pc + 1; case "set-current-rotation-axis-angle":
case "set-current-rotation-axis-angle": // 0x1fe (handle)(axis x/y/z)(angle degrees) return StepRetainedObject(label, a, pc);
Gfx.SetCurrentRotation(Read(a[0]), (Read(a[1]), Read(a[2]), Read(a[3])), Read(a[4]));
return pc + 1;
case "set-adv-wait-indicator-handle": // 0x212 (layout)(retained handle) case "set-adv-wait-indicator-handle": // 0x212 (layout)(retained handle)
{ {
int requestedSlot = (int)Read(a[0]); int requestedSlot = (int)Read(a[0]);
@@ -2625,18 +2590,12 @@ public sealed partial class VirtualMachine
TextHistory.SetTextObjectRange((int)Read(a[0]), Read(a[1]), Read(a[2])); TextHistory.SetTextObjectRange((int)Read(a[0]), Read(a[1]), Read(a[2]));
return pc + 1; return pc + 1;
} }
case "gfx-elem-erase": // 0x1f7 (handle)(count) — erase retained-object range case "gfx-elem-erase":
{ return StepRetainedObject(label, a, pc);
long first = Read(a[0]), count = Read(a[1]);
Gfx.EraseRange(first, count);
foreach (int layoutSlot in TextHistory.LayoutsCoveredByTextObjectErase(first, count))
_host.ClearRenderedAdvTextLayout(layoutSlot);
return pc + 1;
}
case "gfx-elem-release": // 0x1fa (surface slot) case "gfx-elem-release": // 0x1fa (surface slot)
_host.ReleaseSurface((int)Read(a[0])); Gfx.ClearSurface((int)Read(a[0])); return pc + 1; _host.ReleaseSurface((int)Read(a[0])); Gfx.ClearSurface((int)Read(a[0])); return pc + 1;
case "clone-gfx-object": // 0x21d (source handle)(destination handle) case "clone-gfx-object":
Gfx.CloneObject(Read(a[0]), Read(a[1])); return pc + 1; return StepRetainedObject(label, a, pc);
case "gfx-blit-color": // 0x202 (handle)(delay)(duration)(alpha)(color) — one-shot color case "gfx-blit-color": // 0x202 (handle)(delay)(duration)(alpha)(color) — one-shot color
Gfx.SetAnimatedObjectColorResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3]), Read(a[4])); Gfx.SetAnimatedObjectColorResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3]), Read(a[4]));
return pc + 1; return pc + 1;