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>
|
/// <summary>RCode for each RoadMap, parallel to <see cref="RoadAlphaLayers"/>.</summary>
|
||||||
public IReadOnlyList<uint> RoadAlphaRCodes { get; }
|
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(
|
private TerrainAtlas(
|
||||||
GL gl,
|
GL gl,
|
||||||
|
Wb.BindlessSupport? bindless,
|
||||||
uint glTexture, IReadOnlyDictionary<uint, uint> map, int layerCount,
|
uint glTexture, IReadOnlyDictionary<uint, uint> map, int layerCount,
|
||||||
uint glAlphaTexture, int alphaLayerCount,
|
uint glAlphaTexture, int alphaLayerCount,
|
||||||
IReadOnlyList<byte> cornerLayers, IReadOnlyList<byte> sideLayers, IReadOnlyList<byte> roadLayers,
|
IReadOnlyList<byte> cornerLayers, IReadOnlyList<byte> sideLayers, IReadOnlyList<byte> roadLayers,
|
||||||
IReadOnlyList<uint> cornerTCodes, IReadOnlyList<uint> sideTCodes, IReadOnlyList<uint> roadRCodes)
|
IReadOnlyList<uint> cornerTCodes, IReadOnlyList<uint> sideTCodes, IReadOnlyList<uint> roadRCodes)
|
||||||
{
|
{
|
||||||
_gl = gl;
|
_gl = gl;
|
||||||
|
_bindless = bindless;
|
||||||
GlTexture = glTexture;
|
GlTexture = glTexture;
|
||||||
TerrainTypeToLayer = map;
|
TerrainTypeToLayer = map;
|
||||||
LayerCount = layerCount;
|
LayerCount = layerCount;
|
||||||
|
|
@ -79,7 +110,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
||||||
/// for the mapping from TerrainTextureType to SurfaceTexture id, decoding each
|
/// for the mapping from TerrainTextureType to SurfaceTexture id, decoding each
|
||||||
/// to RGBA8, and uploading as layers in a single GL_TEXTURE_2D_ARRAY.
|
/// to RGBA8, and uploading as layers in a single GL_TEXTURE_2D_ARRAY.
|
||||||
/// </summary>
|
/// </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)
|
var region = dats.Get<Region>(0x13000000u)
|
||||||
?? throw new InvalidOperationException("Region dat id 0x13000000 missing");
|
?? 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)
|
if (terrainDesc is null || terrainDesc.Count == 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine("WARN: TerrainDesc missing, using single white fallback layer");
|
Console.WriteLine("WARN: TerrainDesc missing, using single white fallback layer");
|
||||||
return BuildFallback(gl);
|
return BuildFallback(gl, bindless);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Terrain atlas (unchanged Phase 2b logic) ----
|
// ---- Terrain atlas (unchanged Phase 2b logic) ----
|
||||||
|
|
@ -167,6 +198,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
||||||
|
|
||||||
return new TerrainAtlas(
|
return new TerrainAtlas(
|
||||||
gl,
|
gl,
|
||||||
|
bindless,
|
||||||
tex, map, layerCount,
|
tex, map, layerCount,
|
||||||
alphaBuild.gl, alphaBuild.layerCount,
|
alphaBuild.gl, alphaBuild.layerCount,
|
||||||
alphaBuild.corner, alphaBuild.side, alphaBuild.road,
|
alphaBuild.corner, alphaBuild.side, alphaBuild.road,
|
||||||
|
|
@ -350,7 +382,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TerrainAtlas BuildFallback(GL gl)
|
private static TerrainAtlas BuildFallback(GL gl, Wb.BindlessSupport? bindless = null)
|
||||||
{
|
{
|
||||||
uint tex = gl.GenTexture();
|
uint tex = gl.GenTexture();
|
||||||
gl.BindTexture(TextureTarget.Texture2DArray, tex);
|
gl.BindTexture(TextureTarget.Texture2DArray, tex);
|
||||||
|
|
@ -372,6 +404,7 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
||||||
|
|
||||||
return new TerrainAtlas(
|
return new TerrainAtlas(
|
||||||
gl,
|
gl,
|
||||||
|
bindless,
|
||||||
tex, new Dictionary<uint, uint> { [0] = 0u }, 1,
|
tex, new Dictionary<uint, uint> { [0] = 0u }, 1,
|
||||||
alphaTex, 1,
|
alphaTex, 1,
|
||||||
Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>(),
|
Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>(),
|
||||||
|
|
@ -380,6 +413,16 @@ public sealed unsafe class TerrainAtlas : IDisposable
|
||||||
|
|
||||||
public void Dispose()
|
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(GlTexture);
|
||||||
_gl.DeleteTexture(GlAlphaTexture);
|
_gl.DeleteTexture(GlAlphaTexture);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue