diff --git a/docs/PROJECT-STRUCTURE.md b/docs/PROJECT-STRUCTURE.md index 1e3c8e2..507aea5 100644 --- a/docs/PROJECT-STRUCTURE.md +++ b/docs/PROJECT-STRUCTURE.md @@ -185,6 +185,8 @@ default-slot and geometry mutation, direct and range transforms, clone, and eras ADV-binding, and presentation labels remain in their respective dispatcher groups. `engine/Age.Engine/Vm/VirtualMachine.Animation.cs` owns retained spritesheet and color channels, timed and cyclic transforms, per-object animation control, frame-time sampling, and the shared animation-clock opcode handler. +`engine/Age.Engine/Vm/VirtualMachine.Presentation.cs` owns queued surface-alpha transitions, frame and object-range +publication, skip-aware blocking fades/crossfades, foreground-transition waits, and graphics command-queue clear. The disposable `build/page-map-.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 diff --git a/docs/remake-architecture-and-roadmap.md b/docs/remake-architecture-and-roadmap.md index 2dfbcf8..18f8783 100644 --- a/docs/remake-architecture-and-roadmap.md +++ b/docs/remake-architecture-and-roadmap.md @@ -655,6 +655,12 @@ do not mix mechanical moves with semantic changes. existing positions and routes the separated groups through `StepAnimation`; presentation transitions and movie masking remain outside the guarded handler. Runtime validation remains green. + The sixth bounded `VirtualMachine.Step` extraction moved queued surface-alpha transitions, frame and object- + range publication, skip-aware blocking fades/crossfades, foreground-transition waits, and graphics command- + queue clear into `engine/Age.Engine/Vm/VirtualMachine.Presentation.cs`. The top-level dispatcher retains all + labels and aliases at their existing positions and routes them through guarded `StepPresentation`; movie-mask + transition dispatch remains with the movie handler. Runtime validation remains green. + **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 each domain move. @@ -1027,7 +1033,8 @@ layer's rendering diverges from ADV; save layout. 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` domains isolated and the audio, movie, surface/texture, retained-object, and animation `VirtualMachine.Step` -families routed through domain handlers, extract retained presentation/transition dispatch next without replacing -the proven dispatcher or changing public types, commands, and generated output. +families routed through domain handlers, with retained presentation/transition dispatch now isolated as well, +extract the ADV text-layout/rendering 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 not replace Phase B gameplay validation or the open cross-platform gates. diff --git a/engine/Age.Engine/Vm/VirtualMachine.Presentation.cs b/engine/Age.Engine/Vm/VirtualMachine.Presentation.cs new file mode 100644 index 0000000..74192e7 --- /dev/null +++ b/engine/Age.Engine/Vm/VirtualMachine.Presentation.cs @@ -0,0 +1,46 @@ +using Age.Engine.Hosting; +using Age.Engine.Model; + +namespace Age.Engine.Vm; + +public sealed partial class VirtualMachine +{ + private int StepPresentation(string label, IReadOnlyList a, int pc) + { + switch (label) + { + case "queue-surface-alpha-transition": // 0x223: target surface crossfade over two object ranges + Gfx.QueueSurfaceAlphaTransition(Read(a[0]), (int)Read(a[1]), Read(a[2]), (int)Read(a[3]), + Read(a[4]), (int)Read(a[5]), Read(a[6]), Read(a[7])); return pc + 1; + case "present-frame": // 0x20c: read/message-skip path snaps a queued transition to its endpoint + _host.PresentFrame(Gfx); return pc + 1; + case "fade-surface-in-from-black": // 0x21: blocking black -> captured full-frame surface + case "u00418860": + _host.FadeSurfaceWithBlack( + Gfx, (int)Read(a[0]), Read(a[1]), SurfaceBlackFadeDirection.FromBlack, + _messageSkipServiceActive || _host.IsMessageSkipActive); + return pc + 1; + case "fade-surface-out-to-black": // 0x22: blocking captured full-frame surface -> black + case "u00418920": + _host.FadeSurfaceWithBlack( + Gfx, (int)Read(a[0]), Read(a[1]), SurfaceBlackFadeDirection.ToBlack, + _messageSkipServiceActive || _host.IsMessageSkipActive); + return pc + 1; + case "crossfade-surfaces": // 0x25: legacy full-frame surface alpha transition + case "u00418B40": + _host.CrossfadeSurfaces( + Gfx, (int)Read(a[0]), (int)Read(a[1]), Read(a[2]), + _messageSkipServiceActive || _host.IsMessageSkipActive); + return pc + 1; + case "mark-frame-yield": // 0x21c: normal foreground-transition scheduler/resume boundary + _host.WaitForForegroundTransition(Gfx); return pc + 1; + case "clear-gfx-command-queue": // 0x224: retained compositor does not use this native queue + return pc + 1; + case "present-gfx-object-range": // 0x222: publish pending retained changes in the selected range + case "u004216C0": + _host.PresentObjectRange(Gfx, Read(a[0]), Read(a[1])); return pc + 1; + default: + throw new InvalidOperationException($"Non-presentation opcode routed to presentation handler: {label}"); + } + } +} diff --git a/engine/Age.Engine/Vm/VirtualMachine.cs b/engine/Age.Engine/Vm/VirtualMachine.cs index 9dafeb0..d669fba 100644 --- a/engine/Age.Engine/Vm/VirtualMachine.cs +++ b/engine/Age.Engine/Vm/VirtualMachine.cs @@ -2606,35 +2606,18 @@ public sealed partial class VirtualMachine case "play-movie-mask-transition": return StepMovie(label, ins, pc); case "queue-surface-alpha-transition": // 0x223: target surface crossfade over two object ranges - Gfx.QueueSurfaceAlphaTransition(Read(a[0]), (int)Read(a[1]), Read(a[2]), (int)Read(a[3]), - Read(a[4]), (int)Read(a[5]), Read(a[6]), Read(a[7])); return pc + 1; case "present-frame": // 0x20c: read/message-skip path snaps a queued transition to its endpoint - _host.PresentFrame(Gfx); return pc + 1; case "fade-surface-in-from-black": // 0x21: blocking black -> captured full-frame surface case "u00418860": - _host.FadeSurfaceWithBlack( - Gfx, (int)Read(a[0]), Read(a[1]), SurfaceBlackFadeDirection.FromBlack, - _messageSkipServiceActive || _host.IsMessageSkipActive); - return pc + 1; case "fade-surface-out-to-black": // 0x22: blocking captured full-frame surface -> black case "u00418920": - _host.FadeSurfaceWithBlack( - Gfx, (int)Read(a[0]), Read(a[1]), SurfaceBlackFadeDirection.ToBlack, - _messageSkipServiceActive || _host.IsMessageSkipActive); - return pc + 1; case "crossfade-surfaces": // 0x25: legacy full-frame surface alpha transition case "u00418B40": - _host.CrossfadeSurfaces( - Gfx, (int)Read(a[0]), (int)Read(a[1]), Read(a[2]), - _messageSkipServiceActive || _host.IsMessageSkipActive); - return pc + 1; case "mark-frame-yield": // 0x21c: normal foreground-transition scheduler/resume boundary - _host.WaitForForegroundTransition(Gfx); return pc + 1; case "clear-gfx-command-queue": // 0x224: retained compositor does not use this native queue - return pc + 1; case "present-gfx-object-range": // 0x222: publish pending retained changes in the selected range case "u004216C0": - _host.PresentObjectRange(Gfx, Read(a[0]), Read(a[1])); return pc + 1; + return StepPresentation(label, a, pc); default: // Stub is per-instruction frequency (the VM handles ~30 ops; the rest hit here, e.g. // 0x258/0x259 stmt markers appear en masse), so gate it with Step — else --trace floods.