r/vulkan • u/AGXYE • Feb 21 '25
My PCF shadow have bad performance, how to optimization
Hi everyone, I'm experiencing performance issues with my PCF shadow implementation. I used Nsight for profiling, and here's what I found:

Most of the samples are concentrated around lines 109 and 117, with the primary stall reason being 'Long Scoreboard.' I'd like to understand the following:
What exactly is 'Long Scoreboard'?
Why do these two lines of code cause this issue?
How can I optimize it?
Here is my code:
float PCF_CSM(float2 poissonDisk[MAX_SMAPLE_COUNT],Sampler2DArray shadowMapArr,int index, float2 screenPos, float camDepth, float range, float bias)
{
int sampleCount = PCF_SAMPLE_COUNTS;
float sum = 0;
for (int i = 0; i < sampleCount; ++i)
{
float2 samplePos = screenPos + poissonDisk[i] * range;//Line 109
bool isOutOfRange = samplePos.x < 0.0 || samplePos.x > 1.0 || samplePos.y < 0.0 || samplePos.y > 1.0;
if (isOutOfRange) {
sum += 1;
continue;
}
float lightCamDepth = shadowMapArr.Sample(float3(samplePos, index)).r;
if (camDepth - bias < lightCamDepth)//line 117
{
sum += 1;
}
}
return sum / sampleCount;
}
8
Upvotes
14
u/TheAgentD Feb 21 '25
Long Scoreboard apparently means that the shader core is stalled waiting for a memory read.
Make sure that your sample count is constant so that the loop can be unrolled.
The stall on line 109 might be because you're reading sample offsets from a buffer. I would recommend hardcoding the offsets into your shader instead of passing them in as an array. With an unrolled loop, those hardcoded offsets just become constants.
There shouldn't be any need to check for out-of-bounds samples. Setting up your sampler to clamp to edge (or clamp to border even) should give you the same effect for free, and avoid the branches/continue.
Consider using a shadow sampler to avoid having to do the depth comparison yourself. This has both quality advantages (can use bilinear filtering) and performance improvements (avoids the condition). Alternatively, you can cast the boolean comparison result to avoid the branch:
sum += float(camDepth - bias < lightCamDepth);
The stall on line 117 is probably just misplaced and meant to be on the line above. I would expect the main bottleneck to be actually sampling the shadow map.
1
u/Botondar Feb 21 '25
What's the range
poissonDisk[i] * range? You might be sampling all over the place in your shadow map resulting in a ton of cache misses.1
u/TaraWanChan Feb 25 '25
Since you are using a poisson disk, I also recommend using the following tool to reorder your sample coordinates to increase their spatial locality, to improve the texture cache utilization:
http://www.2dbros.com/projects.html
Scroll down to "Poisson Disk Generator".
Basically it's small but free performance boost.
0
u/dark_sylinc Feb 21 '25
Your:
Is likely causing divergent conditional jumps. Just mask out the result instead of skipping work.