Home

Compute Shader Particle System

During my apprenticeship I wrote a GPU-based particle system which uses compute shaders to update the location, rotation, and velocity of particles.

CPU-based particle systems can be expensive when updating large numbers of particles, so we can use the huge parallelisation potential of the GPU to update vastly more particles with much less performance impact.

In this project I worked with hlsl files to manage the initialisation, emission, and update of particles. This involved understanding how to compile and bind shaders in DX11, as well as the syntax and structure of hlsl files.

I used a Structured Buffer for the particles, and a free list to determine at what point in a buffer a new particle can be assigned. I used RenderDoc a lot during this process to debug why my particles weren't being allocated and destroyed as I intended.

Here's an example of an HLSL function which indexes into a buffer of particle structs in a thread-safe way. It uses a CAS (compare and swap) loop to check that no other threads have taken ownership of a particular index before returning it for modification.


uint GetIndexInterlocked(int modifyAmt)
{
	uint currentValue = g_particleControlBuffer[0].freeCount.x;
	uint originalValue;
	InterlockedCompareExchange(g_particleControlBuffer[0].freeCount.x, currentValue, currentValue + modifyAmt, originalValue);
 
	while (currentValue != originalValue)
	{
		if (currentValue < 0 || currentValue > g_maxParticles.x)
			return -1;
 
		currentValue = originalValue;
		InterlockedCompareExchange(g_particleControlBuffer[0].freeCount.x, currentValue, currentValue + modifyAmt, originalValue);
	}
 
	return g_freelist[currentValue];
}
    

In summary, this project highlights a few different things: