Skip to content

Commit e748f7f

Browse files
committed
Use multi draw indirect in renderer
1 parent 19e2f31 commit e748f7f

File tree

4 files changed

+83
-7
lines changed

4 files changed

+83
-7
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace Nanoforge.Render.Misc;
4+
5+
[StructLayout(LayoutKind.Sequential)]
6+
public struct VkDrawIndexedIndirectCommand
7+
{
8+
public uint IndexCount;
9+
public uint InstanceCount;
10+
public uint FirstIndex;
11+
public int VertexOffset;
12+
public uint FirstInstance;
13+
14+
public VkDrawIndexedIndirectCommand(uint indexCount, uint instanceCount, uint firstIndex, int vertexOffset, uint firstInstance)
15+
{
16+
IndexCount = indexCount;
17+
InstanceCount = instanceCount;
18+
FirstIndex = firstIndex;
19+
VertexOffset = vertexOffset;
20+
FirstInstance = firstInstance;
21+
}
22+
}
23+
24+
// struct VkDrawIndexedIndirectCommand {
25+
// uint32_t indexCount;
26+
// uint32_t instanceCount;
27+
// uint32_t firstIndex;
28+
// int32_t vertexOffset;
29+
// uint32_t firstInstance;
30+
// };

Nanoforge/Render/RenderContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ public RenderContext()
202202
PhysicalDeviceFeatures deviceFeatures = new()
203203
{
204204
SamplerAnisotropy = true,
205+
MultiDrawIndirect = true,
205206
};
206207

207208
DeviceCreateInfo deviceCreateInfo = new()

Nanoforge/Render/Renderer.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public unsafe class Renderer
4646
private VkBuffer[] _materialInfoGPUBuffer;
4747
private GpuFrameDataWriter _gpuFrameDataWriter = new(MaxObjectsPerScene, MaxMaterialInstances);
4848

49+
public const int MaxCommands = 150_000;
50+
private VkDrawIndexedIndirectCommand[] _cpuDrawCommandsBuffer;
51+
private VkBuffer[] _drawCommandBuffer;
52+
4953
public static readonly Format RenderTextureImageFormat = Format.B8G8R8A8Srgb;
5054
public static Format DepthTextureFormat { get; private set; }
5155

@@ -109,6 +113,10 @@ public unsafe Renderer(uint viewportWidth, uint viewportHeight)
109113
{
110114
throw new Exception("SizeOf<Pixlit4UvNmapVertex>() != 36. Required for shaders to work.");
111115
}
116+
if (Unsafe.SizeOf<VkDrawIndexedIndirectCommand>() != 20)
117+
{
118+
throw new Exception("SizeOf<VkDrawIndexedIndirectCommand>() != 20. Required for shaders to work.");
119+
}
112120

113121
Context = new RenderContext();
114122
//Load global textures like 1x1 white texture & setup default texture sampler
@@ -118,6 +126,7 @@ public unsafe Renderer(uint viewportWidth, uint viewportHeight)
118126
CreateUniformBuffers();
119127
CreatePerObjectConstantBuffers();
120128
CreateMaterialInfoBuffers();
129+
CreateDrawCommandBuffers();
121130
MaterialHelper.Init(Context, _renderPass, _perFrameUniformBuffers!, _perObjectConstantsGPUBuffer, _materialInfoGPUBuffer);
122131
CreateSyncObjects();
123132
}
@@ -331,6 +340,22 @@ private void CreateMaterialInfoBuffers()
331340
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit);
332341
}
333342
}
343+
344+
[MemberNotNull(nameof(_drawCommandBuffer))]
345+
[MemberNotNull(nameof(_cpuDrawCommandsBuffer))]
346+
private void CreateDrawCommandBuffers()
347+
{
348+
_cpuDrawCommandsBuffer = new VkDrawIndexedIndirectCommand[MaxCommands];
349+
350+
int bufferSize = MaxCommands * Unsafe.SizeOf<VkDrawIndexedIndirectCommand>();
351+
_drawCommandBuffer = new VkBuffer[MaxFramesInFlight];
352+
for (int i = 0; i < MaxFramesInFlight; i++)
353+
{
354+
_drawCommandBuffer[i] = new VkBuffer(Context, (ulong)bufferSize,
355+
BufferUsageFlags.TransferDstBit | BufferUsageFlags.StorageBufferBit | BufferUsageFlags.IndirectBufferBit,
356+
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit);
357+
}
358+
}
334359

335360
private void UpdatePerObjectConstantBuffers(uint currentImage)
336361
{
@@ -442,15 +467,33 @@ private void RecordCommands(Scene scene, uint index)
442467

443468
MaterialPipeline unifiedPipeline = MaterialHelper.GetMaterialPipeline("UnifiedMaterial") ?? throw new NullReferenceException("Failed to get unified material pipeline!");
444469
unifiedPipeline.Bind(commandBuffer, (int)index);
470+
int totalNumCommands = 0;
445471
foreach (IGrouping<Mesh, RenderCommand> meshGrouping in _commands.GroupBy(command => command.Mesh))
446472
{
447473
Mesh mesh = meshGrouping.Key;
448474
mesh.Bind(Context, commandBuffer);
449475

476+
int batchFirstCommand = totalNumCommands;
477+
int batchNumCommands = 0;
450478
foreach (RenderCommand command in meshGrouping)
451479
{
452-
Context.Vk.CmdDrawIndexed(commandBuffer, command.IndexCount, 1, command.StartIndex, 0, command.ObjectIndex);
480+
if (totalNumCommands >= MaxCommands)
481+
throw new Exception($"Exceeded max draw command count of {MaxCommands}. Please increase Renderer.MaxCommands and recompile Nanoforge to fix this.");
482+
483+
_cpuDrawCommandsBuffer[totalNumCommands++] = new VkDrawIndexedIndirectCommand(command.IndexCount, 1, command.StartIndex, 0, command.ObjectIndex);
484+
batchNumCommands++;
453485
}
486+
487+
ulong batchOffset = (ulong)batchFirstCommand * (ulong)Unsafe.SizeOf<VkDrawIndexedIndirectCommand>();
488+
fixed (VkDrawIndexedIndirectCommand* commandsPtr = _cpuDrawCommandsBuffer)
489+
{
490+
var commandsSpan = new Span<VkDrawIndexedIndirectCommand>(commandsPtr + batchFirstCommand, batchNumCommands);
491+
_drawCommandBuffer[index].SetData(commandsSpan, batchOffset);
492+
}
493+
494+
uint drawCount = (uint)batchNumCommands;
495+
uint drawCommandStride = (uint)Unsafe.SizeOf<VkDrawIndexedIndirectCommand>();
496+
Context.Vk.CmdDrawIndexedIndirect(commandBuffer, _drawCommandBuffer[index].VkHandle, batchOffset, drawCount, drawCommandStride);
454497
}
455498

456499
scene.PrimitiveRenderer.RenderPrimitives(Context, commandBuffer, index);

Nanoforge/Render/Resources/VkBuffer.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,25 +64,25 @@ private void Init()
6464
Vk.BindBufferMemory(Device, VkHandle, Memory, 0);
6565
}
6666

67-
public void SetData<T>(ref T data) where T : unmanaged
67+
public void SetData<T>(ref T data, ulong offset = 0) where T : unmanaged
6868
{
6969
fixed (void* ptr = &data)
7070
{
7171
int sizeInBytes = Unsafe.SizeOf<T>();
72-
SetData(new Span<byte>(ptr, sizeInBytes));
72+
SetData(new Span<byte>(ptr, sizeInBytes), offset);
7373
}
7474
}
7575

76-
public void SetData<T>(Span<T> data) where T : unmanaged
76+
public void SetData<T>(Span<T> data, ulong offset = 0) where T : unmanaged
7777
{
7878
fixed (void* ptr = data)
7979
{
8080
int sizeInBytes = Unsafe.SizeOf<T>() * data.Length;
81-
SetData(new Span<byte>(ptr, sizeInBytes));
81+
SetData(new Span<byte>(ptr, sizeInBytes), offset);
8282
}
8383
}
8484

85-
public void SetData(Span<byte> data)
85+
public void SetData(Span<byte> data, ulong offset = 0)
8686
{
8787
if ((ulong)data.Length > Size)
8888
{
@@ -99,7 +99,9 @@ public void SetData(Span<byte> data)
9999

100100
void* ptr = null;
101101
MapMemory(ref ptr);
102-
data.CopyTo(new Span<byte>(ptr, data.Length));
102+
byte* offsetPtr = (byte*)ptr;
103+
offsetPtr += offset;
104+
data.CopyTo(new Span<byte>(offsetPtr, data.Length));
103105
UnmapMemory();
104106
}
105107

0 commit comments

Comments
 (0)