phase(N.5) Task 7: dispatcher SSBO + indirect buffer infrastructure
Adds DrawElementsIndirectCommand struct (20-byte layout for glMultiDrawElementsIndirect). Replaces _instanceVbo field on WbDrawDispatcher with three buffers: _instanceSsbo (mat4[]), _batchSsbo (BatchData[]), _indirectBuffer (DEIC[]). Adds BindlessSupport constructor parameter — non-null required since the dispatcher is only constructed when WB foundation is on (which implies bindless is present per Task 6 capability detection). Existing Draw() method substitutes _instanceVbo -> _instanceSsbo for compile. Behavior is temporarily wrong (SSBO bound as ArrayBuffer for per-vertex attribs); Tasks 9-10 fully rewrite the draw loop and the per-frame uploads to use BindBufferBase + glMultiDrawElementsIndirect. GameWindow construction site updated to add _bindlessSupport guard and pass it as the new last argument to the constructor. Dispatcher is only constructed when bindless is guaranteed present. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
12170f9d78
commit
86c471d2d1
3 changed files with 63 additions and 8 deletions
|
|
@ -1524,10 +1524,11 @@ public sealed class GameWindow : IDisposable
|
|||
_staticMesh = new InstancedMeshRenderer(_gl, _meshShader, _textureCache, _wbMeshAdapter);
|
||||
|
||||
if (AcDream.App.Rendering.Wb.WbFoundationFlag.IsEnabled
|
||||
&& _wbMeshAdapter is not null && _wbEntitySpawnAdapter is not null)
|
||||
&& _wbMeshAdapter is not null && _wbEntitySpawnAdapter is not null
|
||||
&& _bindlessSupport is not null)
|
||||
{
|
||||
_wbDrawDispatcher = new AcDream.App.Rendering.Wb.WbDrawDispatcher(
|
||||
_gl, _meshShader, _textureCache, _wbMeshAdapter, _wbEntitySpawnAdapter);
|
||||
_gl, _meshShader, _textureCache, _wbMeshAdapter, _wbEntitySpawnAdapter, _bindlessSupport);
|
||||
}
|
||||
|
||||
// Phase G.1 sky renderer — its own shader (sky.vert / sky.frag)
|
||||
|
|
|
|||
17
src/AcDream.App/Rendering/Wb/DrawElementsIndirectCommand.cs
Normal file
17
src/AcDream.App/Rendering/Wb/DrawElementsIndirectCommand.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AcDream.App.Rendering.Wb;
|
||||
|
||||
/// <summary>
|
||||
/// Layout matches what <c>glMultiDrawElementsIndirect</c> expects.
|
||||
/// Total size 20 bytes; arrays are typically uploaded with stride = sizeof(this).
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct DrawElementsIndirectCommand
|
||||
{
|
||||
public uint Count; // index count for this draw
|
||||
public uint InstanceCount; // number of instances
|
||||
public uint FirstIndex; // offset into IBO, in indices
|
||||
public int BaseVertex; // vertex offset into VBO
|
||||
public uint BaseInstance; // first instance ID (offsets per-instance attribs / SSBO read)
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using AcDream.Core.Meshing;
|
||||
using AcDream.Core.Terrain;
|
||||
using AcDream.Core.World;
|
||||
|
|
@ -61,7 +62,32 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
private readonly WbMeshAdapter _meshAdapter;
|
||||
private readonly EntitySpawnAdapter _entitySpawnAdapter;
|
||||
|
||||
private readonly uint _instanceVbo;
|
||||
private readonly BindlessSupport _bindless;
|
||||
|
||||
// SSBO buffer ids
|
||||
private uint _instanceSsbo;
|
||||
private uint _batchSsbo;
|
||||
private uint _indirectBuffer;
|
||||
|
||||
// Per-frame scratch arrays — Tasks 9-10 fully wire these.
|
||||
private float[] _instanceData = new float[256 * 16]; // mat4 floats per instance
|
||||
private BatchData[] _batchData = new BatchData[256];
|
||||
private DrawElementsIndirectCommand[] _indirectCommands = new DrawElementsIndirectCommand[256];
|
||||
|
||||
#pragma warning disable CS0169 // Tasks 9-10 wire these counters
|
||||
private int _opaqueDrawCount;
|
||||
private int _transparentDrawCount;
|
||||
private int _transparentByteOffset;
|
||||
#pragma warning restore CS0169
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
private struct BatchData
|
||||
{
|
||||
public ulong TextureHandle; // bindless handle (uvec2 in GLSL)
|
||||
public uint TextureLayer;
|
||||
public uint Flags;
|
||||
}
|
||||
|
||||
private readonly HashSet<uint> _patchedVaos = new();
|
||||
|
||||
// Per-frame scratch — reused across frames to avoid per-frame allocation.
|
||||
|
|
@ -89,7 +115,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
Shader shader,
|
||||
TextureCache textures,
|
||||
WbMeshAdapter meshAdapter,
|
||||
EntitySpawnAdapter entitySpawnAdapter)
|
||||
EntitySpawnAdapter entitySpawnAdapter,
|
||||
BindlessSupport bindless)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gl);
|
||||
ArgumentNullException.ThrowIfNull(shader);
|
||||
|
|
@ -103,7 +130,10 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
_meshAdapter = meshAdapter;
|
||||
_entitySpawnAdapter = entitySpawnAdapter;
|
||||
|
||||
_instanceVbo = _gl.GenBuffer();
|
||||
_bindless = bindless ?? throw new ArgumentNullException(nameof(bindless));
|
||||
_instanceSsbo = _gl.GenBuffer();
|
||||
_batchSsbo = _gl.GenBuffer();
|
||||
_indirectBuffer = _gl.GenBuffer();
|
||||
}
|
||||
|
||||
public static Matrix4x4 ComposePartWorldMatrix(
|
||||
|
|
@ -291,7 +321,10 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
_opaqueDraws.Sort(static (a, b) => a.SortDistance.CompareTo(b.SortDistance));
|
||||
|
||||
// ── Phase 3: one upload of all matrices ─────────────────────────────
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _instanceVbo);
|
||||
// NOTE: _instanceSsbo is temporarily bound as ArrayBuffer for compile
|
||||
// compatibility. Tasks 9-10 rewrite this to BindBufferBase(SSBO) +
|
||||
// glMultiDrawElementsIndirect.
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _instanceSsbo);
|
||||
fixed (float* p = _instanceBuffer)
|
||||
_gl.BufferData(BufferTargetARB.ArrayBuffer,
|
||||
(nuint)(totalInstances * 16 * sizeof(float)), p, BufferUsageARB.DynamicDraw);
|
||||
|
|
@ -472,7 +505,9 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
if (!_patchedVaos.Add(vao)) return;
|
||||
|
||||
_gl.BindVertexArray(vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _instanceVbo);
|
||||
// NOTE: temporarily binding _instanceSsbo as ArrayBuffer for compile
|
||||
// compatibility. Tasks 9-10 replace with BindBufferBase(SSBO).
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _instanceSsbo);
|
||||
for (uint row = 0; row < 4; row++)
|
||||
{
|
||||
uint loc = 3 + row;
|
||||
|
|
@ -494,7 +529,9 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
_gl.DeleteBuffer(_instanceVbo);
|
||||
_gl.DeleteBuffer(_instanceSsbo);
|
||||
_gl.DeleteBuffer(_batchSsbo);
|
||||
_gl.DeleteBuffer(_indirectBuffer);
|
||||
}
|
||||
|
||||
private readonly record struct GroupKey(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue