feat(D.2b): Step-0 chrome sprites confirmed + direct-RenderSurface upload path
Step-0 prove-out result: retail UI chrome sprites are RenderSurface objects (0x06xxxxxx) that must be decoded DIRECTLY, not via the Surface->SurfaceTexture chain GetOrUpload uses for world materials (which produced 1x1 magenta/garbage). Added TextureCache.GetOrUploadRenderSurface(id, out w, out h) — Portal/HighRes TryGet<RenderSurface> -> DecodeRenderSurface(palette:null) -> upload, separately cached. This is the path UI chrome + (later) dat fonts use. Confirmed the universal floating-window bevel is an 8-piece border + center fill: center 0x06004CC2 (48x48) edges 0x060074BF/C1 (10x5 horiz) 0x060074C0/C2 (5x10 vert) corners 0x060074C3..C6 (5x5) Recorded in RetailChromeSprites.cs (edge/corner->position mapping is a best guess pending the LayoutDesc 0x21000040 parse; visually confirmed at panel render). The memory-note ids were right; only the decode path was wrong. Temporary prove-out harness (added to GameWindow.OnRender) removed. proveout*.log gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
66888d2c8e
commit
8e91805206
3 changed files with 92 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -30,6 +30,7 @@ references/*
|
||||||
/.superpowers/
|
/.superpowers/
|
||||||
launch.log
|
launch.log
|
||||||
launch-*.log
|
launch-*.log
|
||||||
|
proveout*.log
|
||||||
launch.utf8.log
|
launch.utf8.log
|
||||||
n4-verify*.log
|
n4-verify*.log
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab
|
||||||
private readonly Dictionary<(uint surfaceId, uint origTexOverride, ulong paletteHash), uint> _handlesByPalette = new();
|
private readonly Dictionary<(uint surfaceId, uint origTexOverride, ulong paletteHash), uint> _handlesByPalette = new();
|
||||||
private uint _magentaHandle;
|
private uint _magentaHandle;
|
||||||
|
|
||||||
|
// Direct-RenderSurface caches for UI sprites: 0x06xxxxxx RenderSurface ids
|
||||||
|
// decoded directly (Portal/HighRes → DecodeRenderSurface), bypassing the
|
||||||
|
// Surface→SurfaceTexture chain that GetOrUpload uses for world materials.
|
||||||
|
private readonly Dictionary<uint, uint> _handlesByRenderSurfaceId = new();
|
||||||
|
private readonly Dictionary<uint, (int w, int h)> _rsSizeById = new();
|
||||||
|
|
||||||
private readonly Wb.BindlessSupport? _bindless;
|
private readonly Wb.BindlessSupport? _bindless;
|
||||||
|
|
||||||
// Bindless / Texture2DArray parallel caches. Keys mirror the legacy three
|
// Bindless / Texture2DArray parallel caches. Keys mirror the legacy three
|
||||||
|
|
@ -103,6 +109,43 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Upload a UI sprite by its RenderSurface DataId (0x06xxxxxx), decoded
|
||||||
|
/// DIRECTLY (Portal/HighRes → DecodeRenderSurface) rather than through the
|
||||||
|
/// Surface→SurfaceTexture chain that <see cref="GetOrUpload(uint)"/> uses
|
||||||
|
/// for world-geometry materials. This is the correct path for retail UI
|
||||||
|
/// chrome + font glyph sheets, which reference RenderSurface directly.
|
||||||
|
/// Palette is null for now (a paletted INDEX16/P8 UI sprite would return
|
||||||
|
/// Magenta — wire a UI palette when one is actually encountered). Returns a
|
||||||
|
/// 1x1 magenta handle on miss.
|
||||||
|
/// </summary>
|
||||||
|
public uint GetOrUploadRenderSurface(uint renderSurfaceId, out int width, out int height)
|
||||||
|
{
|
||||||
|
if (_handlesByRenderSurfaceId.TryGetValue(renderSurfaceId, out var existing)
|
||||||
|
&& _rsSizeById.TryGetValue(renderSurfaceId, out var sz))
|
||||||
|
{
|
||||||
|
width = sz.w; height = sz.h;
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecodedTexture decoded;
|
||||||
|
if (_dats.Portal.TryGet<RenderSurface>(renderSurfaceId, out var rs)
|
||||||
|
|| _dats.HighRes.TryGet<RenderSurface>(renderSurfaceId, out rs))
|
||||||
|
{
|
||||||
|
decoded = SurfaceDecoder.DecodeRenderSurface(rs, palette: null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decoded = DecodedTexture.Magenta;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint h = UploadRgba8(decoded);
|
||||||
|
_handlesByRenderSurfaceId[renderSurfaceId] = h;
|
||||||
|
_rsSizeById[renderSurfaceId] = (decoded.Width, decoded.Height);
|
||||||
|
width = decoded.Width; height = decoded.Height;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Alpha-channel histogram for one decoded texture. Used to diagnose
|
/// Alpha-channel histogram for one decoded texture. Used to diagnose
|
||||||
/// "why are clouds not transparent" — if cloud textures come out with
|
/// "why are clouds not transparent" — if cloud textures come out with
|
||||||
|
|
|
||||||
48
src/AcDream.App/UI/RetailChromeSprites.cs
Normal file
48
src/AcDream.App/UI/RetailChromeSprites.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
namespace AcDream.App.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retail window-chrome RenderSurface DataIds, CONFIRMED via the D.2b Step-0
|
||||||
|
/// prove-out (2026-06-14). These are RenderSurface objects (0x06xxxxxx) decoded
|
||||||
|
/// DIRECTLY (<see cref="Rendering.TextureCache.GetOrUploadRenderSurface"/>), NOT
|
||||||
|
/// through the Surface→SurfaceTexture chain.
|
||||||
|
///
|
||||||
|
/// <para>
|
||||||
|
/// The universal floating-window bevel is an <b>8-piece border</b> (4 corners
|
||||||
|
/// 5×5 + 4 edges) drawn around a tiled center fill — it is NOT a single
|
||||||
|
/// 9-slice texture. Decoded sizes are in the comments (from the prove-out).
|
||||||
|
/// </para>
|
||||||
|
///
|
||||||
|
/// <para>
|
||||||
|
/// The edge/corner → position mapping below is a reasonable guess pending the
|
||||||
|
/// LayoutDesc 0x21000040 parse (sub-project 3) and is confirmed visually in the
|
||||||
|
/// first vitals-panel render. If a corner's bevel highlight looks wrong, swap
|
||||||
|
/// the four corner constants; if top/bottom or left/right look inverted, swap
|
||||||
|
/// those edge pairs.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public static class RetailChromeSprites
|
||||||
|
{
|
||||||
|
/// <summary>Tiled interior fill — the shared panel background (48×48).</summary>
|
||||||
|
public const uint CenterFill = 0x06004CC2;
|
||||||
|
|
||||||
|
/// <summary>Horizontal top edge (10×5, tiled across the top span).</summary>
|
||||||
|
public const uint TopEdge = 0x060074BF;
|
||||||
|
/// <summary>Horizontal bottom edge (10×5).</summary>
|
||||||
|
public const uint BottomEdge = 0x060074C1;
|
||||||
|
/// <summary>Vertical left edge (5×10).</summary>
|
||||||
|
public const uint LeftEdge = 0x060074C0;
|
||||||
|
/// <summary>Vertical right edge (5×10).</summary>
|
||||||
|
public const uint RightEdge = 0x060074C2;
|
||||||
|
|
||||||
|
/// <summary>Top-left corner (5×5).</summary>
|
||||||
|
public const uint CornerTL = 0x060074C3;
|
||||||
|
/// <summary>Top-right corner (5×5).</summary>
|
||||||
|
public const uint CornerTR = 0x060074C4;
|
||||||
|
/// <summary>Bottom-left corner (5×5).</summary>
|
||||||
|
public const uint CornerBL = 0x060074C5;
|
||||||
|
/// <summary>Bottom-right corner (5×5).</summary>
|
||||||
|
public const uint CornerBR = 0x060074C6;
|
||||||
|
|
||||||
|
/// <summary>Border thickness in pixels = the corner/edge sprite size (5px).</summary>
|
||||||
|
public const int Border = 5;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue