Advertisement

GPU Particle System with Indirect Drawing

Started by April 25, 2019 03:32 PM
4 comments, last by Oniikuma 5 years, 9 months ago

Hi,

I am fairly inexperienced in DX11 and I am trying to implement a particle system that is handled purely on the GPU. To keep things simple, the particle system will have a fixed number of particles that, once dead, will be respawned at the emitter location. As such, I only have a single compute shader that updates each particle.

Based on the method outlined in 'Practical Rendering and Computation in Direct3D 11', an AppendStructuredBuffer<Particle> and a ConsumeStructuredBuffer<Particle> are bound to the compute shader, so that last frame's particles can be consumed, updated and appended, ready for the next frame.

As I want to minimise the data flowing back to the CPU, I want to make a call to DrawIndexedInstancedIndirect(), feeding the particle data directly from the compute shader to the vertex shader. In the vertex shader, the particle will be expanded to a quad billboard (I am trying to avoid the geometry shader for performance) and sent to the pixel shader for rasterization. As a side-effect, the primitive topology will be set to TRIANGLELIST.  I am using DrawIndexedInstancedIndirect() rather than DrawInstancedIndirect() as the number of particles and, by extension, the index buffer will remain fixed.

However, I am unclear on how to implement indirect draw calls and have the following questions:

- How can I pass the updated particle data from the compute shader to the vertex shader without sending the data back to the CPU? As I understand, the vertex shader cannot read from the AppendStructuredBuffer that was written to by the compute shader.

- How do I generate vertices without a vertex buffer bound to the input assembler? I know that vertices are identified with the semantic SV_VertexID, but I have no input layout bound to the input assembler.

Any help would be greatly appreciated, as I am not very confident with indirect drawing in DX11 and I cannot find any beginner-friendly resources to point me in the right direction.

16 hours ago, Oniikuma said:

- How can I pass the updated particle data from the compute shader to the vertex shader without sending the data back to the CPU? As I understand, the vertex shader cannot read from the AppendStructuredBuffer that was written to by the compute shader.

Just create a SRV for the buffer, you can read it back through that. Being an append/consume buffer isn't the property of the backing buffer itself, if you look at it, you specify the append flag during the UAV creation. That means anything that makes that buffer an append buffer, is handled/stuffed in by the UAV, without that it's just a generic data buffer that behaves the way it's own creation flags dictate.

16 hours ago, Oniikuma said:

- How do I generate vertices without a vertex buffer bound to the input assembler? I know that vertices are identified with the semantic SV_VertexID, but I have no input layout bound to the input assembler.

The vertex shader can "emit" vertices, it doesn't have to read them from a vertex buffer, and if you don't read from a VB, you don't need an input layout either, you just submit a draw call with the amount of vertices you want to draw. Basically in your case you take the vertex id input that you have available, and look up the particle data through the SRV you created in the previous step, then using that data you fill out the fields of the vertex. Actual example for point sprites: https://www.slideshare.net/DevCentralAMD/vertex-shader-tricks-bill-bilodeau Slides 15-19

Advertisement

Thanks for the link, @LandonJerre, it’s just what I was looking for! I made a few modifications to my particle system to exploit the fixed number of active particles. I use a single RWStructuredBuffer<Particle> to update each particle’s state each frame and an SRV to the particle buffer for the vertex shader to use once updated.

As I have a fixed index buffer and particle count, can I use DrawIndexed() (as it says in the slides) instead of DrawIndexedIndirect(), or does this require copying vertex data back to the CPU?

With a fixed size particle buffer, you won't need any indirect draw calls, because you already know the vertex count for the call, without the GPU doing anything. Indirect draw calls enter the picture when your particle buffer only has a maximum size, but it can hold any amount of particles less than that. In that case, the simulation+emission passes determine the actual vertex count to be drawn, so you have to parameterize your draw call on the GPU, to avoid copying and inspecting the result particle buffer, and thats what indirect draw/dispatch calls are for. (Basically instead of passing the parameters to a draw/dispatch call from the CPU as a normal function call, you write those values into a small GPU buffer from a shader, and pass that buffer to the appropriate indirect call. The only practical difference between normal and indirect calls with D3D11 is the place where the "parameters" come from.)

Thanks a lot for your advice! It's helped a great deal and I have everything working now.

 

This topic is closed to new replies.

Advertisement