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);
|
_staticMesh = new InstancedMeshRenderer(_gl, _meshShader, _textureCache, _wbMeshAdapter);
|
||||||
|
|
||||||
if (AcDream.App.Rendering.Wb.WbFoundationFlag.IsEnabled
|
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(
|
_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)
|
// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using AcDream.Core.Meshing;
|
using AcDream.Core.Meshing;
|
||||||
using AcDream.Core.Terrain;
|
using AcDream.Core.Terrain;
|
||||||
using AcDream.Core.World;
|
using AcDream.Core.World;
|
||||||
|
|
@ -61,7 +62,32 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
private readonly WbMeshAdapter _meshAdapter;
|
private readonly WbMeshAdapter _meshAdapter;
|
||||||
private readonly EntitySpawnAdapter _entitySpawnAdapter;
|
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();
|
private readonly HashSet<uint> _patchedVaos = new();
|
||||||
|
|
||||||
// Per-frame scratch — reused across frames to avoid per-frame allocation.
|
// Per-frame scratch — reused across frames to avoid per-frame allocation.
|
||||||
|
|
@ -89,7 +115,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
Shader shader,
|
Shader shader,
|
||||||
TextureCache textures,
|
TextureCache textures,
|
||||||
WbMeshAdapter meshAdapter,
|
WbMeshAdapter meshAdapter,
|
||||||
EntitySpawnAdapter entitySpawnAdapter)
|
EntitySpawnAdapter entitySpawnAdapter,
|
||||||
|
BindlessSupport bindless)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(gl);
|
ArgumentNullException.ThrowIfNull(gl);
|
||||||
ArgumentNullException.ThrowIfNull(shader);
|
ArgumentNullException.ThrowIfNull(shader);
|
||||||
|
|
@ -103,7 +130,10 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
_meshAdapter = meshAdapter;
|
_meshAdapter = meshAdapter;
|
||||||
_entitySpawnAdapter = entitySpawnAdapter;
|
_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(
|
public static Matrix4x4 ComposePartWorldMatrix(
|
||||||
|
|
@ -291,7 +321,10 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
_opaqueDraws.Sort(static (a, b) => a.SortDistance.CompareTo(b.SortDistance));
|
_opaqueDraws.Sort(static (a, b) => a.SortDistance.CompareTo(b.SortDistance));
|
||||||
|
|
||||||
// ── Phase 3: one upload of all matrices ─────────────────────────────
|
// ── 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)
|
fixed (float* p = _instanceBuffer)
|
||||||
_gl.BufferData(BufferTargetARB.ArrayBuffer,
|
_gl.BufferData(BufferTargetARB.ArrayBuffer,
|
||||||
(nuint)(totalInstances * 16 * sizeof(float)), p, BufferUsageARB.DynamicDraw);
|
(nuint)(totalInstances * 16 * sizeof(float)), p, BufferUsageARB.DynamicDraw);
|
||||||
|
|
@ -472,7 +505,9 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
if (!_patchedVaos.Add(vao)) return;
|
if (!_patchedVaos.Add(vao)) return;
|
||||||
|
|
||||||
_gl.BindVertexArray(vao);
|
_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++)
|
for (uint row = 0; row < 4; row++)
|
||||||
{
|
{
|
||||||
uint loc = 3 + row;
|
uint loc = 3 + row;
|
||||||
|
|
@ -494,7 +529,9 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
_gl.DeleteBuffer(_instanceVbo);
|
_gl.DeleteBuffer(_instanceSsbo);
|
||||||
|
_gl.DeleteBuffer(_batchSsbo);
|
||||||
|
_gl.DeleteBuffer(_indirectBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly record struct GroupKey(
|
private readonly record struct GroupKey(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue