From 4498a1a97445891dc5474e86d09d13e8f8f3ca5d Mon Sep 17 00:00:00 2001 From: gamer147 Date: Sun, 2 Aug 2026 21:37:53 -0400 Subject: [PATCH] Split GfxState surface operations --- docs/PROJECT-STRUCTURE.md | 6 + docs/remake-architecture-and-roadmap.md | 10 +- engine/Age.Engine/Model/GfxState.Surfaces.cs | 123 +++++++++++++++++++ engine/Age.Engine/Model/GfxState.cs | 116 +---------------- 4 files changed, 138 insertions(+), 117 deletions(-) create mode 100644 engine/Age.Engine/Model/GfxState.Surfaces.cs diff --git a/docs/PROJECT-STRUCTURE.md b/docs/PROJECT-STRUCTURE.md index 477cf84..9cbf164 100644 --- a/docs/PROJECT-STRUCTURE.md +++ b/docs/PROJECT-STRUCTURE.md @@ -160,6 +160,12 @@ transitions, diagnostic snapshots, frame/completion publication, and mask teardo control, and blocking BGM fades; presentation/input retains the message-skip, reset, and frame-pulse consumers of that state through the sealed partial class. +`engine/Age.Engine/Model/GfxState.cs` retains cross-domain retained-graphics coordination. +`engine/Age.Engine/Model/GfxState.Contracts.cs` owns its public render, transition, diagnostic, persistence, +animation, numeric-glyph, and handle-range contracts. `engine/Age.Engine/Model/GfxState.Surfaces.cs` owns surface +resource/color-key state, created/reloadable classification, movie stop-time metadata, render-target/tile +configuration, and surface lifecycle operations. + 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 have no repository output tree and write their automatic maps below `user://diagnostics/page-maps` instead. diff --git a/docs/remake-architecture-and-roadmap.md b/docs/remake-architecture-and-roadmap.md index c81a826..f888131 100644 --- a/docs/remake-architecture-and-roadmap.md +++ b/docs/remake-architecture-and-roadmap.md @@ -601,6 +601,12 @@ do not mix mechanical moves with semantic changes. `GfxState` remains the same sealed runtime type, and every moved declaration is textually unchanged; runtime validation remains green. + The second bounded `GfxState` split converted the sealed runtime type to a sealed partial class and moved + surface resource/color-key state, created/reloadable classification, movie stop-time metadata, + render-target/tile configuration, and surface lifecycle operations into + `engine/Age.Engine/Model/GfxState.Surfaces.cs`. Reset, persistence, retained-object sampling, and transition + teardown retain direct access through the partial class; 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. @@ -972,7 +978,7 @@ layer's rendering diverges from ADV; save layout. ## 8. Immediate next step 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` and `GodotAdvHost` domains -isolated and the `GfxState` public contract layer separated, move `GfxState` surface ownership next, preserving -public types, commands, and generated output. +isolated and the first two `GfxState` domains separated, move retained-object ownership next, preserving 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/Model/GfxState.Surfaces.cs b/engine/Age.Engine/Model/GfxState.Surfaces.cs new file mode 100644 index 0000000..aa0f960 --- /dev/null +++ b/engine/Age.Engine/Model/GfxState.Surfaces.cs @@ -0,0 +1,123 @@ +namespace Age.Engine.Model; + +public sealed partial class GfxState +{ + /// The D3D render target selected by op 0x20d. -1 denotes the main backbuffer. + public int CurrentRenderTargetSlot { get; private set; } = -1; + /// Legacy mode-1 surface tile edge selected by op 0x248. The portable backend retains + /// the native process-global value for parity but stores every surface as one contiguous image. + public int TiledSurfaceEdgeLength { get; private set; } + + // ---- surfaces (image buffers per slot): ctx+0x52bd4[slot], from create/set-texture ---- + private readonly Dictionary _surfaces = new(); + // Created surfaces have real pixels but no asset resource id. Keep their class separate from both + // loaded textures and truly surfaceless objects because native mode-0 consumes packed alpha differently. + private readonly HashSet _createdSurfaces = new(); + // Native surface record +0x08. Ordinary create/load/release workers leave this bit unchanged; + // the numbered-save restore loop consults it to decide which asset-backed surfaces to reopen. + private readonly HashSet _reloadableSurfaces = new(); + // A separate entry models the native CMovieToTexture object attached to a surface. A null value means + // the movie object exists but its host decoder supplied no usable IMediaPosition stop time. + private readonly Dictionary _movieStopTimesMs = new(); + + public void SetTiledSurfaceEdgeLength(long edgeLength) + { + // Native stores the complete operand in one signed dword. It does not invalidate or rebuild + // already-created mode-1 surfaces, nor does it mark the retained compositor dirty. + lock (_lock) TiledSurfaceEdgeLength = unchecked((int)edgeLength); + } + + public void SetSurface(int slot, long resId, long colorKey) + { + lock (_lock) + { + _surfaces[slot] = (resId, colorKey); + _createdSurfaces.Remove(slot); + _movieStopTimesMs.Remove(slot); + MarkRetainedMutation(); + } + } + + /// Set the native surface-record +0x08 reload policy. This is separate from loading a + /// texture because AGE's ordinary create/load/release workers preserve the existing bit. + public void SetSurfaceReloadOnRestore(int slot, bool reload) + { + lock (_lock) + { + if (reload) _reloadableSurfaces.Add(slot); + else _reloadableSurfaces.Remove(slot); + } + } + + /// Opcode 0x259 script-entry lifecycle: clear record +0x08 for every surface. + /// Native also clears the adjacent unknown +0x0c field, which the port does not otherwise model. + public void ClearSurfaceReloadPolicies() + { + lock (_lock) _reloadableSurfaces.Clear(); + } + + /// Op 0x236 handoff: retain the initialized movie graph's IMediaPosition stop time. Null + /// deliberately distinguishes a movie surface with unavailable metadata from an empty movie slot. + public void SetMovieStopTime(int slot, long? stopTimeMs) + { + lock (_lock) _movieStopTimesMs[slot] = stopTimeMs; + } + + /// Op 0x23f query. False means no movie object occupies the slot; true with a null value + /// means the movie exists but its stop-time query failed or returned unusable metadata. + public bool TryGetMovieStopTime(int slot, out long? stopTimeMs) + { + lock (_lock) return _movieStopTimesMs.TryGetValue(slot, out stopTimeMs); + } + + /// Op 0x20d: select a surface as the D3D render target; values at or above 1000 restore the + /// device backbuffer in the native engine. + public void SelectRenderTarget(long slot) + { + lock (_lock) CurrentRenderTargetSlot = slot is >= 0 and < 1000 ? (int)slot : -1; + } + + /// Op 0x23d: release the transient surface range while retaining system-owned low slots. + public void ReleaseSurfaceRange(int firstSlot, int count) + { + lock (_lock) + { + int end = checked(firstSlot + count); + for (int slot = firstSlot; slot < end; slot++) + { + _surfaces.Remove(slot); + _createdSurfaces.Remove(slot); + _movieStopTimesMs.Remove(slot); + _surfaceTransitions.Remove(slot); + _movieMaskTransitions.Remove(slot); + } + if (CurrentRenderTargetSlot >= firstSlot && CurrentRenderTargetSlot < end) + CurrentRenderTargetSlot = -1; + MarkRetainedMutation(); + } + } + + public void CreateSurface(int slot) + { + lock (_lock) + { + _surfaces[slot] = (0, -1); // create-texture: real mutable pixels, no asset id or color key + _createdSurfaces.Add(slot); + _movieStopTimesMs.Remove(slot); + MarkRetainedMutation(); + } + } + + public void ClearSurface(int slot) + { + lock (_lock) + { + _surfaces.Remove(slot); + _createdSurfaces.Remove(slot); + _movieStopTimesMs.Remove(slot); + _surfaceTransitions.Remove(slot); + _movieMaskTransitions.Remove(slot); + MarkRetainedMutation(); + } + } +} diff --git a/engine/Age.Engine/Model/GfxState.cs b/engine/Age.Engine/Model/GfxState.cs index c7a132d..21b5ba8 100644 --- a/engine/Age.Engine/Model/GfxState.cs +++ b/engine/Age.Engine/Model/GfxState.cs @@ -10,7 +10,7 @@ namespace Age.Engine.Model; /// slot (returned by 0x215) and three 3-vectors: V18 (set 0x217 / get 0x218, anchor), V24 (set 0x219 / /// get 0x21a, position), V16c (set 0x1ff). The native DirectDraw workers are NOT modelled — only the data /// the query ops read back, which is all the bytecode geometry math needs. -public sealed class GfxState +public sealed partial class GfxState { private sealed class SurfaceTransition { @@ -115,11 +115,6 @@ public sealed class GfxState public long CurrentObject { get; private set; } /// EngineCtx+0x14e08, selected by op 0x80 and used by op 0x1d9 when its slot is zero. public int DefaultObjectSlot { get; private set; } - /// The D3D render target selected by op 0x20d. -1 denotes the main backbuffer. - public int CurrentRenderTargetSlot { get; private set; } = -1; - /// Legacy mode-1 surface tile edge selected by op 0x248. The portable backend retains - /// the native process-global value for parity but stores every surface as one contiguous image. - public int TiledSurfaceEdgeLength { get; private set; } // ---- Separate global animation service clock (op 0x238; ctx+0x51b7c total / +0x51b78 elapsed). // Retained for its opcode family; 0x21e scale and 0x220 translation use frame-time directly instead. ---- @@ -165,12 +160,6 @@ public sealed class GfxState lock (_lock) DefaultObjectSlot = slot; } - public void SetTiledSurfaceEdgeLength(long edgeLength) - { - // Native stores the complete operand in one signed dword. It does not invalidate or rebuild - // already-created mode-1 surfaces, nor does it mark the retained compositor dirty. - lock (_lock) TiledSurfaceEdgeLength = unchecked((int)edgeLength); - } public void SetObjectAnchor(long handle, (long X, long Y, long Z) anchor) { @@ -426,88 +415,8 @@ public sealed class GfxState private readonly object _lock = new(); - // ---- surfaces (image buffers per slot): ctx+0x52bd4[slot], from create/set-texture ---- - private readonly Dictionary _surfaces = new(); - // Created surfaces have real pixels but no asset resource id. Keep their class separate from both - // loaded textures and truly surfaceless objects because native mode-0 consumes packed alpha differently. - private readonly HashSet _createdSurfaces = new(); - // Native surface record +0x08. Ordinary create/load/release workers leave this bit unchanged; - // the numbered-save restore loop consults it to decide which asset-backed surfaces to reopen. - private readonly HashSet _reloadableSurfaces = new(); - // A separate entry models the native CMovieToTexture object attached to a surface. A null value means - // the movie object exists but its host decoder supplied no usable IMediaPosition stop time. - private readonly Dictionary _movieStopTimesMs = new(); private readonly Dictionary _surfaceTransitions = new(); private readonly Dictionary _movieMaskTransitions = new(); - public void SetSurface(int slot, long resId, long colorKey) - { - lock (_lock) - { - _surfaces[slot] = (resId, colorKey); - _createdSurfaces.Remove(slot); - _movieStopTimesMs.Remove(slot); - MarkRetainedMutation(); - } - } - - /// Set the native surface-record +0x08 reload policy. This is separate from loading a - /// texture because AGE's ordinary create/load/release workers preserve the existing bit. - public void SetSurfaceReloadOnRestore(int slot, bool reload) - { - lock (_lock) - { - if (reload) _reloadableSurfaces.Add(slot); - else _reloadableSurfaces.Remove(slot); - } - } - - /// Opcode 0x259 script-entry lifecycle: clear record +0x08 for every surface. - /// Native also clears the adjacent unknown +0x0c field, which the port does not otherwise model. - public void ClearSurfaceReloadPolicies() - { - lock (_lock) _reloadableSurfaces.Clear(); - } - - /// Op 0x236 handoff: retain the initialized movie graph's IMediaPosition stop time. Null - /// deliberately distinguishes a movie surface with unavailable metadata from an empty movie slot. - public void SetMovieStopTime(int slot, long? stopTimeMs) - { - lock (_lock) _movieStopTimesMs[slot] = stopTimeMs; - } - - /// Op 0x23f query. False means no movie object occupies the slot; true with a null value - /// means the movie exists but its stop-time query failed or returned unusable metadata. - public bool TryGetMovieStopTime(int slot, out long? stopTimeMs) - { - lock (_lock) return _movieStopTimesMs.TryGetValue(slot, out stopTimeMs); - } - - /// Op 0x20d: select a surface as the D3D render target; values at or above 1000 restore the - /// device backbuffer in the native engine. - public void SelectRenderTarget(long slot) - { - lock (_lock) CurrentRenderTargetSlot = slot is >= 0 and < 1000 ? (int)slot : -1; - } - - /// Op 0x23d: release the transient surface range while retaining system-owned low slots. - public void ReleaseSurfaceRange(int firstSlot, int count) - { - lock (_lock) - { - int end = checked(firstSlot + count); - for (int slot = firstSlot; slot < end; slot++) - { - _surfaces.Remove(slot); - _createdSurfaces.Remove(slot); - _movieStopTimesMs.Remove(slot); - _surfaceTransitions.Remove(slot); - _movieMaskTransitions.Remove(slot); - } - if (CurrentRenderTargetSlot >= firstSlot && CurrentRenderTargetSlot < end) - CurrentRenderTargetSlot = -1; - MarkRetainedMutation(); - } - } /// Ops 0x202/0x203: record a packed 0xAARRGGBB color/alpha modulation on the object and mark it /// HasColor so the compositor applies alpha+tint (vs the opaque default). @@ -603,29 +512,6 @@ public sealed class GfxState o.ColorAnim = true; } } - public void CreateSurface(int slot) - { - lock (_lock) - { - _surfaces[slot] = (0, -1); // create-texture: real mutable pixels, no asset id or color key - _createdSurfaces.Add(slot); - _movieStopTimesMs.Remove(slot); - MarkRetainedMutation(); - } - } - - public void ClearSurface(int slot) - { - lock (_lock) - { - _surfaces.Remove(slot); - _createdSurfaces.Remove(slot); - _movieStopTimesMs.Remove(slot); - _surfaceTransitions.Remove(slot); - _movieMaskTransitions.Remove(slot); - MarkRetainedMutation(); - } - } /// Op 0x223: queue a type-0 timed alpha transition into a target surface slot. public void QueueSurfaceAlphaTransition(long commandKey, int targetSlot,