Help me understand VirtualShadowMapProjectionMaskBits

I have a small-ish scene that is lit by a set of shadow casting lights. I have one pass projection with clustered deferred shading turned on, and I’m seeing a pretty significant amount of time being spent in VirtualShadowMapProjectionMaskBits. Can you guys explain what this pass is doing and what levels I have to make runtime/quality tradeoffs with it?

[Attachment Removed]

Hi Nick,

It would be beneficial to obtain more details about your setup before we can provide recommendations. Are you running your project on a mobile platform, since you mentioned that you are using the clustered deferred shading path? Are you using Shadow Map Raytracing (SMRT)? Do you have any performance capture data on hand that you could share with me? An Unreal Insights trace or a CSV profiler capture would help greatly in this case. Off the top of my head, if you are running into performance issues using a set of local lights, you might want to check that your local lights overlap as little as possible, since that will otherwise increase your per-pixel cost of evaluating the shadow map projection. You can use the engine show flags to visualize the light radius and the overall cost of the light grid culling shaders, providing a quick check (showflag.visualizelightculling and showflag.lightradius) to identify any regions on-screen that contribute to performance dips. I hope that helps you get started for now.

[Attachment Removed]

This is a scene with a lot of overlapping lights

Ok, that makes sense. If your cutscene has lots of overlapping lights, then you will need to be mindful of how many of these lights will cast shadows, because there is a direct pixel-to-lights cost associated with the VSM projection pass, even if you have one-pass projection turned on.

For starters, consider tightening the attenuation radius of your light sources, utilizing light channels, and carefully evaluating the placement of each light to ensure optimal coverage without compromising shadow performance. If you have shadows on small/fill lights, consider turning shadowing off entirely or fading them out via our existing shadow fade logic when the coverage of those lights becomes too low to be meaningful. The show flags that I pointed out in my earlier message should help you identify overlaps of light sources, which can reduce the per-pixel cost of the projection pass.

Next, adjust the per-light resolution LOD bias and clipmap settings (for directional lights) to control the VSM page footprint and sampling cost. Larger biases reduce resolution, so tune your r.Shadow.Virtual.* CVars for higher resolution biases to keep VSM page counts reasonable.

Check that you have r.Shadow.Virtual.OnePassProjection.SkipScreenShadowMask is enabled, so the renderer doesn’t also build per-light screen shadow masks. One-pass projection is designed to replace those passes for local lights, but it’s good to double-check that you are not doing extra work.

The problem with clustered deferred is that it causes visual differences in the form of light leaking through objects.

I would like to point out that the clustered deferred shading pass is likely to be removed in the future, as we do not actively maintain it at the moment. The one-pass projection pass also works without the clustered deferred shading pass, so you should give OPP a try with the deprecated shading pass.

Those are some initial pointers I can give you to get started, but if you need more details, please don’t hesitate to let me know.

[Attachment Removed]

This is a scene with a lot of overlapping lights, yes - it’s kind of a cutscene that you can walk around in. We’re not on mobile but I found another thread were it sounded like one pass projection required clustered deferred to actually be helpful, and this matches our profiling as well. The problem with clustered deferred is that it causes visual differences in the form of light leaking through objects. We’re not using any raytracing.

I can give you a profile, but it’s basically a huge block of VirtualShadowMapProjectionMaskBits followed by the render of each light into the scene

[Attachment Removed]