I have a tiled forward render pipeline (also called forward+). It assigns a list of lights for every 16*16 block of pixels (tiles) on the screen. Lights are accumulated additively so their order doesn't matter inside the list.
The problem comes when I add decals to the light list. Decals need to be evaluated before the lights (because they can modify surface properties), and even their order among themselves matter because their texture is alpha blended on top of each other.
Because the lights and decals are culled in parallel, and added to the list by keeping a shared pointer to the end of the list which is incremented atomically when a light is added, the order of them can not be guaranteed. This is how I add a light to the tile's light list from a thread:
// I have a [numthreads(16,16,1)] thread group
for (uint i = groupIndex; i < g_nNumLights; i += 16 * 16)
{
Light light = GlobalLightArray[i];
if(IsLightVisible(light))
{
uint index;
InterlockedAdd(LightCount, 1, index); // LightCount is a groupshared uint (the whole threadgroup can see it)
LightArray[index] = i; // A light is always added to the back of the per tile light array
}
}
I thought I should try to sort the light list after the culling and the creation of the list has ended, but I have no experience with highly parallel sorting algorithms. I however came across a nice presentation: Holy smoke! Faster Particle Rendering using Direct Compute by Gareth Thomas . From the 8th slide it has a brief explanation of the bitonic sort algorithm with pseudocode and nice pictures. I get the idea, but I have no idea how to implement it or if it could be even done efficiently for my case because my light list is variable in size for each tile. Maybe it could be done in the same dispatch when the culling occurs, on this array (which hold a variable amount of light indices <= 1024):
groupshared uint LightArray[1024]
I have also seen a DirectX11 example of the algorithm of this kind of sorting, but on a different kind of dataset and across multiple dispatches and other post-processing transposing which is not clear to me at all.
Or maybe there is a completely different approach that I should take which doesn't involve sorting? Maybe when adding lights to the back of the array, the order of addition could be enforced somehow?
I have also learnt that the new Doom game also renders its decals this way.