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:
@@ -8,7 +8,10 @@ public sealed class GodotAdvHost : IHost
|
||||
private readonly Main _main;
|
||||
private readonly ResourceMap _res;
|
||||
private readonly string _scene; // e.g. "SC0000" — for section_base
|
||||
private readonly Dictionary<int, string?> _slotBmp = new(); // slot -> pre-converted BMP path
|
||||
private readonly Dictionary<int, string?> _slotBmp = new(); // slot -> pre-converted BMP path
|
||||
// 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) } };
|
||||
private readonly SemaphoreSlim _gate = new(0, 1);
|
||||
public volatile bool IsWaiting;
|
||||
public readonly List<(int Offset, string Text)> Captured = new();
|
||||
@@ -24,8 +27,11 @@ public sealed class GodotAdvHost : IHost
|
||||
_main.CallDeferred("AppendLine", text);
|
||||
}
|
||||
|
||||
public volatile int Pages; // VM-thread page counter (incremented before IsWaiting so shot-gating can't race)
|
||||
|
||||
public void WaitForInput()
|
||||
{
|
||||
Pages++;
|
||||
_main.CallDeferred("PageBreak");
|
||||
IsWaiting = true;
|
||||
_gate.Wait();
|
||||
@@ -40,23 +46,27 @@ public sealed class GodotAdvHost : IHost
|
||||
public void OnStub(int opcode) { }
|
||||
|
||||
// ---- texture ops (run on the VM thread; marshal Godot node work to the main thread) ----
|
||||
public void CreateTexture(int slot, int width, int height) => _slotBmp[slot] = null;
|
||||
public void CreateTexture(int slot, int width, int height) { _slotBmp[slot] = null; _slotDims[slot] = (width, height); }
|
||||
|
||||
public void SetTexture(long resourceId, int slot)
|
||||
{
|
||||
var asset = _res.Resolve(_scene, resourceId);
|
||||
_slotBmp[slot] = asset != null ? ResourceMap.TexturePath(asset) : null;
|
||||
var bmp = asset != null ? ResourceMap.TexturePath(asset) : null;
|
||||
_slotBmp[slot] = bmp;
|
||||
_slotDims[slot] = BmpHeader.ReadDims(bmp); // synchronous: dims from the header, no Godot Image
|
||||
}
|
||||
|
||||
// Dims are read from the BMP header on the VM thread so the bytecode's geometry math (which calls this
|
||||
// synchronously right after set-texture) sees the real size. Pixels are blitted later on the main thread.
|
||||
public (int Width, int Height) GetTextureSize(int slot)
|
||||
=> _slotDims.TryGetValue(slot, out var d) ? (d.W, d.H) : (0, 0);
|
||||
|
||||
public void DrawTexture(int slot, int srcX, int srcY, int width, int height, int dstX, int dstY)
|
||||
{
|
||||
if (_slotBmp.TryGetValue(slot, out var bmp) && bmp != null)
|
||||
_main.CallDeferred("DrawSlot", slot, bmp, dstX, dstY, width, height);
|
||||
_main.CallDeferred("BlitSlot", bmp, srcX, srcY, width, height, dstX, dstY);
|
||||
}
|
||||
|
||||
// Temporary stub — replaced by the real BMP-header-backed impl in Task 3 (blit compositor).
|
||||
public (int Width, int Height) GetTextureSize(int slot) => (0, 0);
|
||||
|
||||
// ---- audio ops (OGG plays natively in Godot) ----
|
||||
// BGM: addressed by direct name (BGM{id:D3}.OGG), NOT the manifest. Voice: via the per-scene manifest.
|
||||
public void PlayBgm(long id)
|
||||
|
||||
Reference in New Issue
Block a user