feat(a2b): screen-backbuffer blit compositor + real GetTextureSize

Godot host: source images kept per slot with dims read from the BMP header on
the VM thread (synchronous, so the bytecode's geometry math sees real sizes);
draw-texture blits src rect -> dst into one 800x600 canvas in execution order,
shown by a single TextureRect. Selftest byte-parity preserved.

Slot 0 = primary/screen surface: seed its dims to 800x600 (the boot-time
create-texture the single-scene harness skips) and record create-texture(w,h),
so the first CG's anchor-preserve math stays an identity instead of corrupting
the persistent base globals. Fixes the grey-first-CG opening. Verified: SC0000
pages 1/3 composite the opening event CG at (0,0) with dialogue over it.

Also adds a --shot <png> [--shot-page N] dev capture to Main (page-gated on a
VM-thread counter) for headless visual verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-06 23:02:58 -04:00
parent de5eb6562e
commit 78199e9b8c
3 changed files with 90 additions and 42 deletions

View File

@@ -114,15 +114,17 @@ sealed class GfxTraceHost : IHost
private readonly ResourceMap _res;
private readonly string _scene;
private readonly Dictionary<int, string?> _slotBmp = new(); // slot -> resolved BMP path (or null)
// slot -> dims. Slot 0 is the primary/screen surface (800x600), normally created at engine boot which
// the single-scene harness skips; seed it so the first CG's anchor math stays correct (not 0x0).
private readonly Dictionary<int, (int W, int H)> _slotDims = new() { { 0, (800, 600) } };
public List<string> Events { get; } = new();
public GfxTraceHost(ResourceMap res, string scene) { _res = res; _scene = scene; }
public (int Width, int Height) GetTextureSize(int slot)
{
_slotBmp.TryGetValue(slot, out var bmp);
var (w, h) = BmpHeader.ReadDims(bmp);
Events.Add($"get-tex-size slot={slot} -> {w}x{h}");
return (w, h);
var d = _slotDims.TryGetValue(slot, out var v) ? v : (0, 0);
Events.Add($"get-tex-size slot={slot} -> {d.Item1}x{d.Item2}");
return d;
}
public void SetTexture(long resId, int slot)
@@ -130,6 +132,7 @@ sealed class GfxTraceHost : IHost
var e = _res.Resolve(_scene, resId);
var bmp = e != null ? ResourceMap.TexturePath(e) : null;
_slotBmp[slot] = bmp;
_slotDims[slot] = BmpHeader.ReadDims(bmp);
Events.Add($"set-texture slot={slot} res=0x{resId:x} -> {(e?.Name ?? "<unresolved>")}"
+ (bmp == null ? " [NO BMP]" : ""));
}
@@ -141,7 +144,11 @@ sealed class GfxTraceHost : IHost
+ $"file={(bmp != null ? System.IO.Path.GetFileName(bmp) : "<none>")}");
}
public void CreateTexture(int slot, int width, int height) => Events.Add($"create-texture slot={slot} {width}x{height}");
public void CreateTexture(int slot, int width, int height)
{
_slotDims[slot] = (width, height);
Events.Add($"create-texture slot={slot} {width}x{height}");
}
public void ShowText(int offset, string text) { }
public void CallScript(long id) { }
public void OnStub(int opcode) { }