phase(N.5b) Task 1: TerrainAtlas bindless extension
Add optional BindlessSupport ctor parameter + GetBindlessHandles() method that returns (terrainHandle, alphaHandle) ulongs with both textures made resident. Two-phase Dispose mirroring TextureCache (MakeNonResident before DeleteTexture per ARB_bindless_texture spec). Existing callers pass `Build(gl, dats)` unchanged; bindless = null default keeps them working until T6/T8 wires the renderer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
79367d4c15
commit
db0f010544
1 changed files with 46 additions and 3 deletions
|
|
@ -53,14 +53,45 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
|||
/// <summary>RCode for each RoadMap, parallel to <see cref="RoadAlphaLayers"/>.</summary>
|
||||
public IReadOnlyList<uint> RoadAlphaRCodes { get; }
|
||||
|
||||
private readonly Wb.BindlessSupport? _bindless;
|
||||
|
||||
// Cached bindless handles. Generated lazily on first GetBindlessHandles() call;
|
||||
// reused for the lifetime of the atlas.
|
||||
private ulong _terrainHandle;
|
||||
private ulong _alphaHandle;
|
||||
private bool _handlesGenerated;
|
||||
|
||||
/// <summary>
|
||||
/// Get 64-bit bindless handles for the terrain + alpha texture arrays.
|
||||
/// Throws <see cref="InvalidOperationException"/> if the atlas was constructed
|
||||
/// without a <see cref="Wb.BindlessSupport"/> instance. Handles are generated
|
||||
/// lazily on first call and cached for the atlas's lifetime; both textures
|
||||
/// are made resident.
|
||||
/// </summary>
|
||||
public (ulong terrain, ulong alpha) GetBindlessHandles()
|
||||
{
|
||||
if (_bindless is null)
|
||||
throw new InvalidOperationException(
|
||||
"TerrainAtlas was constructed without BindlessSupport; cannot return bindless handles.");
|
||||
if (!_handlesGenerated)
|
||||
{
|
||||
_terrainHandle = _bindless.GetResidentHandle(GlTexture);
|
||||
_alphaHandle = _bindless.GetResidentHandle(GlAlphaTexture);
|
||||
_handlesGenerated = true;
|
||||
}
|
||||
return (_terrainHandle, _alphaHandle);
|
||||
}
|
||||
|
||||
private TerrainAtlas(
|
||||
GL gl,
|
||||
Wb.BindlessSupport? bindless,
|
||||
uint glTexture, IReadOnlyDictionary<uint, uint> map, int layerCount,
|
||||
uint glAlphaTexture, int alphaLayerCount,
|
||||
IReadOnlyList<byte> cornerLayers, IReadOnlyList<byte> sideLayers, IReadOnlyList<byte> roadLayers,
|
||||
IReadOnlyList<uint> cornerTCodes, IReadOnlyList<uint> sideTCodes, IReadOnlyList<uint> roadRCodes)
|
||||
{
|
||||
_gl = gl;
|
||||
_bindless = bindless;
|
||||
GlTexture = glTexture;
|
||||
TerrainTypeToLayer = map;
|
||||
LayerCount = layerCount;
|
||||
|
|
@ -79,7 +110,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
|||
/// for the mapping from TerrainTextureType to SurfaceTexture id, decoding each
|
||||
/// to RGBA8, and uploading as layers in a single GL_TEXTURE_2D_ARRAY.
|
||||
/// </summary>
|
||||
public static TerrainAtlas Build(GL gl, DatCollection dats)
|
||||
public static TerrainAtlas Build(GL gl, DatCollection dats, Wb.BindlessSupport? bindless = null)
|
||||
{
|
||||
var region = dats.Get<Region>(0x13000000u)
|
||||
?? throw new InvalidOperationException("Region dat id 0x13000000 missing");
|
||||
|
|
@ -89,7 +120,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
|||
if (terrainDesc is null || terrainDesc.Count == 0)
|
||||
{
|
||||
Console.WriteLine("WARN: TerrainDesc missing, using single white fallback layer");
|
||||
return BuildFallback(gl);
|
||||
return BuildFallback(gl, bindless);
|
||||
}
|
||||
|
||||
// ---- Terrain atlas (unchanged Phase 2b logic) ----
|
||||
|
|
@ -167,6 +198,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
|||
|
||||
return new TerrainAtlas(
|
||||
gl,
|
||||
bindless,
|
||||
tex, map, layerCount,
|
||||
alphaBuild.gl, alphaBuild.layerCount,
|
||||
alphaBuild.corner, alphaBuild.side, alphaBuild.road,
|
||||
|
|
@ -350,7 +382,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
|||
return dst;
|
||||
}
|
||||
|
||||
private static TerrainAtlas BuildFallback(GL gl)
|
||||
private static TerrainAtlas BuildFallback(GL gl, Wb.BindlessSupport? bindless = null)
|
||||
{
|
||||
uint tex = gl.GenTexture();
|
||||
gl.BindTexture(TextureTarget.Texture2DArray, tex);
|
||||
|
|
@ -372,6 +404,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
|||
|
||||
return new TerrainAtlas(
|
||||
gl,
|
||||
bindless,
|
||||
tex, new Dictionary<uint, uint> { [0] = 0u }, 1,
|
||||
alphaTex, 1,
|
||||
Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>(),
|
||||
|
|
@ -380,6 +413,16 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
// Phase 1: release bindless residency BEFORE deleting textures.
|
||||
// ARB_bindless_texture requires this ordering; interleaving is UB.
|
||||
if (_handlesGenerated && _bindless is not null)
|
||||
{
|
||||
_bindless.MakeNonResident(_terrainHandle);
|
||||
_bindless.MakeNonResident(_alphaHandle);
|
||||
_handlesGenerated = false;
|
||||
}
|
||||
|
||||
// Phase 2: delete the underlying GL textures.
|
||||
_gl.DeleteTexture(GlTexture);
|
||||
_gl.DeleteTexture(GlAlphaTexture);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue