phase(N.5) Task 8: InstanceGroup + GroupKey carry bindless handle + layer

Replaces uint TextureHandle (32-bit GL name) with ulong
BindlessTextureHandle (64-bit) in InstanceGroup + GroupKey + ResolveTexture
return type. Adds TextureLayer (always 0 for per-instance composites,
becomes meaningful when WB atlas is adopted in N.6).

ClassifyBatches now calls TextureCache.GetOrUpload*Bindless variants —
these return Texture2DArray-backed bindless handles (Task 3 work).

DrawGroup body throws NotImplementedException — Task 10 rewrites the
whole Draw() method to use glMultiDrawElementsIndirect, which makes
DrawGroup obsolete. CPU-only tests don't invoke DrawGroup so the build
+ test gates stay green; visual launch fails until Task 10 (intentional).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-08 20:32:38 +02:00
parent 1b6995d2df
commit 424d7b9015

View file

@ -398,21 +398,10 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
private void DrawGroup(InstanceGroup grp)
{
_gl.ActiveTexture(TextureUnit.Texture0);
_gl.BindTexture(TextureTarget.Texture2D, grp.TextureHandle);
_gl.BindBuffer(BufferTargetARB.ElementArrayBuffer, grp.Ibo);
// BaseInstance offsets the per-instance attribute fetches into our
// shared instance VBO so each group reads its own slice. Requires
// GL_ARB_base_instance (GL 4.2+); WB requires 4.3 so this is available.
_gl.DrawElementsInstancedBaseVertexBaseInstance(
PrimitiveType.Triangles,
(uint)grp.IndexCount,
DrawElementsType.UnsignedShort,
(void*)(grp.FirstIndex * sizeof(ushort)),
(uint)grp.InstanceCount,
grp.BaseVertex,
(uint)grp.FirstInstance);
throw new NotImplementedException(
"DrawGroup is being removed in Task 10 — the dispatcher rewrites Draw() " +
"to use glMultiDrawElementsIndirect instead of per-group draws. " +
"If this throws at runtime, Task 10 hasn't landed yet.");
}
private void MaybeFlushDiag()
@ -452,12 +441,16 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
: TranslucencyKind.Opaque;
}
uint texHandle = ResolveTexture(entity, meshRef, batch, palHash);
ulong texHandle = ResolveTexture(entity, meshRef, batch, palHash);
if (texHandle == 0) continue;
// TextureLayer is always 0 for per-instance composites; non-zero when
// WB atlas is adopted in N.6+ and batches reference a shared atlas layer.
uint texLayer = 0;
var key = new GroupKey(
batch.IBO, batch.FirstIndex, (int)batch.BaseVertex,
batch.IndexCount, texHandle, translucency);
batch.IndexCount, texHandle, texLayer, translucency);
if (!_groups.TryGetValue(key, out var grp))
{
@ -467,7 +460,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
FirstIndex = batch.FirstIndex,
BaseVertex = (int)batch.BaseVertex,
IndexCount = batch.IndexCount,
TextureHandle = texHandle,
BindlessTextureHandle = texHandle,
TextureLayer = texLayer,
Translucency = translucency,
};
_groups[key] = grp;
@ -476,10 +470,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
}
}
private uint ResolveTexture(WorldEntity entity, MeshRef meshRef, ObjectRenderBatch batch, ulong palHash)
private ulong ResolveTexture(WorldEntity entity, MeshRef meshRef, ObjectRenderBatch batch, ulong palHash)
{
// WB stores the surface id on batch.Key.SurfaceId (TextureKey struct);
// batch.SurfaceId is unset (zero) for batches built by ObjectMeshManager.
uint surfaceId = batch.Key.SurfaceId;
if (surfaceId == 0 || surfaceId == 0xFFFFFFFF) return 0;
@ -490,19 +482,16 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
if (entity.PaletteOverride is not null)
{
// perf #4: pass the entity-precomputed palette hash so TextureCache
// can skip its internal HashPaletteOverride for repeat lookups
// within the same character.
return _textures.GetOrUploadWithPaletteOverride(
return _textures.GetOrUploadWithPaletteOverrideBindless(
surfaceId, origTexOverride, entity.PaletteOverride, palHash);
}
else if (hasOrigTexOverride)
{
return _textures.GetOrUploadWithOrigTextureOverride(surfaceId, overrideOrigTex);
return _textures.GetOrUploadWithOrigTextureOverrideBindless(surfaceId, overrideOrigTex);
}
else
{
return _textures.GetOrUpload(surfaceId);
return _textures.GetOrUploadBindless(surfaceId);
}
}
@ -545,7 +534,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
uint FirstIndex,
int BaseVertex,
int IndexCount,
uint TextureHandle,
ulong BindlessTextureHandle,
uint TextureLayer,
TranslucencyKind Translucency);
private sealed class InstanceGroup
@ -554,7 +544,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
public uint FirstIndex;
public int BaseVertex;
public int IndexCount;
public uint TextureHandle;
public ulong BindlessTextureHandle; // 64-bit (was uint TextureHandle in N.4)
public uint TextureLayer; // 0 for per-instance composites; non-zero when WB atlas is adopted in N.6+
public TranslucencyKind Translucency;
public int FirstInstance; // offset into the shared instance VBO (in instances, not bytes)
public int InstanceCount;