Extract VM movie opcode handler
This commit is contained in:
@@ -175,7 +175,9 @@ accounting, and visible retained-scene snapshots.
|
|||||||
`engine/Age.Engine/Vm/VirtualMachine.cs` retains VM lifecycle, cross-domain state, and the proven top-level
|
`engine/Age.Engine/Vm/VirtualMachine.cs` retains VM lifecycle, cross-domain state, and the proven top-level
|
||||||
opcode dispatcher. Its partial-class companion `engine/Age.Engine/Vm/VirtualMachine.Audio.cs` owns VM audio
|
opcode dispatcher. Its partial-class companion `engine/Age.Engine/Vm/VirtualMachine.Audio.cs` owns VM audio
|
||||||
state, BGM restart semantics, and the BGM/voice/SFX/mixer opcode handler; `Step` retains the audio labels and
|
state, BGM restart semantics, and the BGM/voice/SFX/mixer opcode handler; `Step` retains the audio labels and
|
||||||
routes that family into the handler.
|
routes that family into the handler. `engine/Age.Engine/Vm/VirtualMachine.Movie.cs` owns modal/asynchronous/
|
||||||
|
positioned movie playback, movie surface metadata/activity queries, and movie-mask transition dispatch; `Step`
|
||||||
|
likewise retains and routes the movie labels.
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -630,6 +630,12 @@ do not mix mechanical moves with semantic changes.
|
|||||||
routes only that family through `StepAudio`; public VM behavior and case-body logic remain unchanged. Runtime
|
routes only that family through `StepAudio`; public VM behavior and case-body logic remain unchanged. Runtime
|
||||||
validation remains green.
|
validation remains green.
|
||||||
|
|
||||||
|
The second bounded `VirtualMachine.Step` extraction moved modal, asynchronous, and positioned movie playback,
|
||||||
|
movie surface stop-time/activity queries, and movie-mask transition case bodies into
|
||||||
|
`engine/Age.Engine/Vm/VirtualMachine.Movie.cs`. The top-level dispatcher retains all movie labels at their
|
||||||
|
existing positions and routes them through `StepMovie`; the original instruction remains available for the
|
||||||
|
two bytecode-offset compatibility paths. 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.
|
||||||
@@ -1001,8 +1007,8 @@ layer's rendering diverges from ADV; save layout.
|
|||||||
## 8. Immediate next step
|
## 8. Immediate next step
|
||||||
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 first `VirtualMachine.Step` family routed through a domain handler, extract the movie
|
domains isolated and the first two `VirtualMachine.Step` families routed through domain handlers, extract the
|
||||||
opcode family next without replacing the proven dispatcher or changing public types, commands, and generated
|
surface/texture opcode family next without replacing the proven dispatcher or changing public types, commands,
|
||||||
output.
|
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.
|
||||||
|
|||||||
100
engine/Age.Engine/Vm/VirtualMachine.Movie.cs
Normal file
100
engine/Age.Engine/Vm/VirtualMachine.Movie.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
using Age.Engine.Hosting;
|
||||||
|
using Age.Engine.Model;
|
||||||
|
|
||||||
|
namespace Age.Engine.Vm;
|
||||||
|
|
||||||
|
public sealed partial class VirtualMachine
|
||||||
|
{
|
||||||
|
private int StepMovie(string label, Instruction ins, int pc)
|
||||||
|
{
|
||||||
|
var a = ins.Args;
|
||||||
|
switch (label)
|
||||||
|
{
|
||||||
|
case "play-modal-movie-to-surface": // 0x20f (packed resource)(surface)(movie flags)
|
||||||
|
{
|
||||||
|
long resourceId = Read(a[0]);
|
||||||
|
int surfaceSlot = (int)Read(a[1]);
|
||||||
|
// Modal and non-modal paths share packed resolution and retained-surface composition.
|
||||||
|
// The distinct host entry point owns only 0x20f's blocking lifecycle. Native attaches its
|
||||||
|
// renderer to the existing mutable target; it does not reload that surface with resource
|
||||||
|
// id/movie bytes or apply color key 0. Keep the created surface's no-key state so MPEG
|
||||||
|
// black remains opaque.
|
||||||
|
_host.PlayModalMovieToSurface(resourceId, surfaceSlot, Read(a[2]));
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
case "u004221A0": // pre-reference compatibility
|
||||||
|
case "play-movie-to-surface": // 0x236 (resource)(surface)(movie flags)(start delay ms)
|
||||||
|
{
|
||||||
|
long resourceId = Read(a[0]);
|
||||||
|
int surfaceSlot = (int)Read(a[1]);
|
||||||
|
// Native SC0000's warm-engine trace evaluates this existing site as surface 0. The bounded
|
||||||
|
// single-scene bootstrap assigns its logical layer slot 5, which the immediately following
|
||||||
|
// 0x34/0x35 static loads reuse and would therefore evict the movie before presentation.
|
||||||
|
// Reproduce the native site assignment without changing the general surface allocator.
|
||||||
|
if (ins.Offset == 0x13c8 && _cur.Script.Name.StartsWith("SC0000", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& surfaceSlot != 0)
|
||||||
|
{
|
||||||
|
int logicalLayer = (int)Globals.GetValueOrDefault(0x62450);
|
||||||
|
long movieHandle = Globals.GetValueOrDefault(0x62455 + logicalLayer);
|
||||||
|
Gfx.RemapObjectSurface(movieHandle, surfaceSlot, 0);
|
||||||
|
surfaceSlot = 0;
|
||||||
|
}
|
||||||
|
// Graph construction is synchronous. Keep the existing created surface blank during that
|
||||||
|
// boundary; publishing the movie resource first would let a concurrent compositor mistake
|
||||||
|
// the MPEG payload's .AGF name for a still image before the host registers/decodes it.
|
||||||
|
long? stopTimeMs = _host.PlayMovieToSurface(resourceId, surfaceSlot, Read(a[2]), Read(a[3]));
|
||||||
|
// Native replaces the pixels of the already-created surface without changing its resource
|
||||||
|
// identity or color key. The host's playback-to-surface binding resolves the live frame.
|
||||||
|
// Native never encounters a missing system decoder for shipped assets. If a host backend
|
||||||
|
// cannot initialize one, model the valid movie as completing immediately: BTL feeds this
|
||||||
|
// value into its effect timeline, where zero is a safe duration and -1 is not meaningful.
|
||||||
|
Gfx.SetMovieStopTime(surfaceSlot, stopTimeMs ?? 0);
|
||||||
|
return pc + 1; // native cmd size 9 resumes at the next instruction; playback is asynchronous
|
||||||
|
}
|
||||||
|
case "u00422B80": // pre-reference compatibility
|
||||||
|
case "play-movie-to-surface-at-position": // 0x241 (+ initial position ms)
|
||||||
|
{
|
||||||
|
long resourceId = Read(a[0]);
|
||||||
|
int surfaceSlot = unchecked((int)Read(a[1]));
|
||||||
|
long? stopTimeMs = _host.PlayMovieToSurfaceAtPosition(
|
||||||
|
resourceId, surfaceSlot, Read(a[2]), Read(a[3]), Read(a[4]));
|
||||||
|
// As with 0x236, seeking changes decoder position but not the graph's stop metadata.
|
||||||
|
Gfx.SetMovieStopTime(surfaceSlot, stopTimeMs ?? 0);
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
// ---- gfx command-buffer ops (VM-internal GfxState; docs/engine-re.md op-contract table) ----
|
||||||
|
case "u00422930": // pre-reference compatibility
|
||||||
|
case "query-surface-stop-time-ms": // 0x23f (out_stop_time_ms)(surface_slot)
|
||||||
|
{
|
||||||
|
int surfaceSlot = (int)Read(a[1]);
|
||||||
|
if (!Gfx.TryGetMovieStopTime(surfaceSlot, out long? stopTimeMs))
|
||||||
|
{
|
||||||
|
Write(a[0], -1); // native null CMovieToTexture slot
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
if (!stopTimeMs.HasValue)
|
||||||
|
{
|
||||||
|
_host.ReportWarning(
|
||||||
|
$"movie stop-time unavailable {_cur.Script.Name}@0x{ins.Offset:x} " +
|
||||||
|
$"surface={surfaceSlot}; returning -1");
|
||||||
|
Write(a[0], -1);
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
Write(a[0], stopTimeMs.Value);
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
case "query-movie-surface-active": // 0x23a (out)(surface slot)
|
||||||
|
Write(a[0], _host.IsMovieSurfaceActive((int)Read(a[1])) ? 1 : 0); return pc + 1;
|
||||||
|
case "play-movie-mask-transition": // 0x24d: captured retained range + movie green-channel mask
|
||||||
|
_host.PlayMovieMaskTransition(Gfx, new MovieMaskTransitionRequest(
|
||||||
|
Read(a[0]), unchecked((int)Read(a[1])),
|
||||||
|
Read(a[2]), unchecked((int)Read(a[3])),
|
||||||
|
unchecked((int)Read(a[4])), unchecked((int)Read(a[5])),
|
||||||
|
unchecked((int)Read(a[6])), unchecked((int)Read(a[7])),
|
||||||
|
Read(a[8]), Read(a[9]), Read(a[10]), Read(a[11])));
|
||||||
|
return pc + 1;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Non-movie opcode routed to movie handler: {label}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2567,59 +2567,12 @@ public sealed partial class VirtualMachine
|
|||||||
case "get-initial-root-run": // 0x130 (out)
|
case "get-initial-root-run": // 0x130 (out)
|
||||||
Write(a[0], _initialRootRun ? 1 : 0);
|
Write(a[0], _initialRootRun ? 1 : 0);
|
||||||
return pc + 1;
|
return pc + 1;
|
||||||
case "play-modal-movie-to-surface": // 0x20f (packed resource)(surface)(movie flags)
|
case "play-modal-movie-to-surface":
|
||||||
{
|
case "u004221A0":
|
||||||
long resourceId = Read(a[0]);
|
case "play-movie-to-surface":
|
||||||
int surfaceSlot = (int)Read(a[1]);
|
case "u00422B80":
|
||||||
// Modal and non-modal paths share packed resolution and retained-surface composition.
|
case "play-movie-to-surface-at-position":
|
||||||
// The distinct host entry point owns only 0x20f's blocking lifecycle. Native attaches its
|
return StepMovie(label, ins, pc);
|
||||||
// renderer to the existing mutable target; it does not reload that surface with resource
|
|
||||||
// id/movie bytes or apply color key 0. Keep the created surface's no-key state so MPEG
|
|
||||||
// black remains opaque.
|
|
||||||
_host.PlayModalMovieToSurface(resourceId, surfaceSlot, Read(a[2]));
|
|
||||||
return pc + 1;
|
|
||||||
}
|
|
||||||
case "u004221A0": // pre-reference compatibility
|
|
||||||
case "play-movie-to-surface": // 0x236 (resource)(surface)(movie flags)(start delay ms)
|
|
||||||
{
|
|
||||||
long resourceId = Read(a[0]);
|
|
||||||
int surfaceSlot = (int)Read(a[1]);
|
|
||||||
// Native SC0000's warm-engine trace evaluates this existing site as surface 0. The bounded
|
|
||||||
// single-scene bootstrap assigns its logical layer slot 5, which the immediately following
|
|
||||||
// 0x34/0x35 static loads reuse and would therefore evict the movie before presentation.
|
|
||||||
// Reproduce the native site assignment without changing the general surface allocator.
|
|
||||||
if (ins.Offset == 0x13c8 && _cur.Script.Name.StartsWith("SC0000", StringComparison.OrdinalIgnoreCase)
|
|
||||||
&& surfaceSlot != 0)
|
|
||||||
{
|
|
||||||
int logicalLayer = (int)Globals.GetValueOrDefault(0x62450);
|
|
||||||
long movieHandle = Globals.GetValueOrDefault(0x62455 + logicalLayer);
|
|
||||||
Gfx.RemapObjectSurface(movieHandle, surfaceSlot, 0);
|
|
||||||
surfaceSlot = 0;
|
|
||||||
}
|
|
||||||
// Graph construction is synchronous. Keep the existing created surface blank during that
|
|
||||||
// boundary; publishing the movie resource first would let a concurrent compositor mistake
|
|
||||||
// the MPEG payload's .AGF name for a still image before the host registers/decodes it.
|
|
||||||
long? stopTimeMs = _host.PlayMovieToSurface(resourceId, surfaceSlot, Read(a[2]), Read(a[3]));
|
|
||||||
// Native replaces the pixels of the already-created surface without changing its resource
|
|
||||||
// identity or color key. The host's playback-to-surface binding resolves the live frame.
|
|
||||||
// Native never encounters a missing system decoder for shipped assets. If a host backend
|
|
||||||
// cannot initialize one, model the valid movie as completing immediately: BTL feeds this
|
|
||||||
// value into its effect timeline, where zero is a safe duration and -1 is not meaningful.
|
|
||||||
Gfx.SetMovieStopTime(surfaceSlot, stopTimeMs ?? 0);
|
|
||||||
return pc + 1; // native cmd size 9 resumes at the next instruction; playback is asynchronous
|
|
||||||
}
|
|
||||||
case "u00422B80": // pre-reference compatibility
|
|
||||||
case "play-movie-to-surface-at-position": // 0x241 (+ initial position ms)
|
|
||||||
{
|
|
||||||
long resourceId = Read(a[0]);
|
|
||||||
int surfaceSlot = unchecked((int)Read(a[1]));
|
|
||||||
long? stopTimeMs = _host.PlayMovieToSurfaceAtPosition(
|
|
||||||
resourceId, surfaceSlot, Read(a[2]), Read(a[3]), Read(a[4]));
|
|
||||||
// As with 0x236, seeking changes decoder position but not the graph's stop metadata.
|
|
||||||
Gfx.SetMovieStopTime(surfaceSlot, stopTimeMs ?? 0);
|
|
||||||
return pc + 1;
|
|
||||||
}
|
|
||||||
// ---- gfx command-buffer ops (VM-internal GfxState; docs/engine-re.md op-contract table) ----
|
|
||||||
case "query-gfx-object?": // 0x215 (out)(handle) -> slot | -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)
|
if (_diagSetTexture) // reuse the flag: show what the slot query returns (grey-BG slot dig)
|
||||||
{
|
{
|
||||||
@@ -2687,28 +2640,10 @@ public sealed partial class VirtualMachine
|
|||||||
else Write(a[0], 1); // native missing-object path leaves output operands untouched
|
else Write(a[0], 1); // native missing-object path leaves output operands untouched
|
||||||
return pc + 1;
|
return pc + 1;
|
||||||
}
|
}
|
||||||
case "u00422930": // pre-reference compatibility
|
case "u00422930":
|
||||||
case "query-surface-stop-time-ms": // 0x23f (out_stop_time_ms)(surface_slot)
|
case "query-surface-stop-time-ms":
|
||||||
{
|
case "query-movie-surface-active":
|
||||||
int surfaceSlot = (int)Read(a[1]);
|
return StepMovie(label, ins, pc);
|
||||||
if (!Gfx.TryGetMovieStopTime(surfaceSlot, out long? stopTimeMs))
|
|
||||||
{
|
|
||||||
Write(a[0], -1); // native null CMovieToTexture slot
|
|
||||||
return pc + 1;
|
|
||||||
}
|
|
||||||
if (!stopTimeMs.HasValue)
|
|
||||||
{
|
|
||||||
_host.ReportWarning(
|
|
||||||
$"movie stop-time unavailable {_cur.Script.Name}@0x{ins.Offset:x} " +
|
|
||||||
$"surface={surfaceSlot}; returning -1");
|
|
||||||
Write(a[0], -1);
|
|
||||||
return pc + 1;
|
|
||||||
}
|
|
||||||
Write(a[0], stopTimeMs.Value);
|
|
||||||
return pc + 1;
|
|
||||||
}
|
|
||||||
case "query-movie-surface-active": // 0x23a (out)(surface slot)
|
|
||||||
Write(a[0], _host.IsMovieSurfaceActive((int)Read(a[1])) ? 1 : 0); return pc + 1;
|
|
||||||
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": // 0x1ff: set current translation matrix
|
||||||
@@ -2772,14 +2707,8 @@ public sealed partial class VirtualMachine
|
|||||||
Gfx.ResetAnimClock(); return pc + 1;
|
Gfx.ResetAnimClock(); return pc + 1;
|
||||||
case "set-gfx-animation-service-flags": // 0x24e: bit 1 suppresses op 0x243
|
case "set-gfx-animation-service-flags": // 0x24e: bit 1 suppresses op 0x243
|
||||||
Gfx.SetAnimationServiceFlags(Read(a[0])); return pc + 1;
|
Gfx.SetAnimationServiceFlags(Read(a[0])); return pc + 1;
|
||||||
case "play-movie-mask-transition": // 0x24d: captured retained range + movie green-channel mask
|
case "play-movie-mask-transition":
|
||||||
_host.PlayMovieMaskTransition(Gfx, new MovieMaskTransitionRequest(
|
return StepMovie(label, ins, pc);
|
||||||
Read(a[0]), unchecked((int)Read(a[1])),
|
|
||||||
Read(a[2]), unchecked((int)Read(a[3])),
|
|
||||||
unchecked((int)Read(a[4])), unchecked((int)Read(a[5])),
|
|
||||||
unchecked((int)Read(a[6])), unchecked((int)Read(a[7])),
|
|
||||||
Read(a[8]), Read(a[9]), Read(a[10]), Read(a[11])));
|
|
||||||
return pc + 1;
|
|
||||||
case "queue-surface-alpha-transition": // 0x223: target surface crossfade over two object ranges
|
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]),
|
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;
|
Read(a[4]), (int)Read(a[5]), Read(a[6]), Read(a[7])); return pc + 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user