Scalability Culled Niagara Systems still perform Occlusion Queries?

What would be the best way to prevent paying the cost of occlusion queries on particle systems that we intend to be distance culled by scalability settings?

We are seeing an issue where Niagara Systems are issuing occlusion queries, even though they are distance culled and can no longer visually contribute.

The same systems, however, do not issue Occlusion Queries if they are instead culled by Max Draw Distance on the Component.

This manifests as a performance issue for us for certain types of ambient particle systems in a large world, where we expect scalability settings to reduce the burden on lower end hardware.

The attached project shows the error case. We would hope that systems culled by either scalability or draw distance would both not issue occlusion queries. Is there anything we can do to achieve that?

[Image Removed]

Thank you!

-Rick

Steps to Reproduce

  1. Open the attached project, `ScalabilityOcclusion`
  2. Open `Lvl_FirstPerson`
  3. Start Play In Editor
  4. open debug `stat InitViews`
  5. -- Note that there are 0 Occlusion Queries
  6. run debug command `ce EnableVFX`
  7. move around the level until the VFX are showing
  8. -- Note that there are ~3000 Occlusion Queries at max
  9. back away from the vfx and continue to look at them, until they are distance culled
  10. -- Note that there are still ~1500 Occlusion Queries

Uploading example project in a way that is easier to observe for other Epic Pro Support community members

Hi apologies for the delay.

Currently yes this is an issue. I’m hoping to address this properly in the near future but for a quick limited fix you could trigger the component to destroy it’s render state inside UNiagaraComponent::OnSystemComplete() if bIsCulledByScalability==true.

Other users have done the following

	if (bIsCulledByScalability == false && bDuringUpdateContextReset == false)
	{
		.....
	}
	else if (bIsCulledByScalability && !Asset->GetScalabilitySettings().VisibilityCulling.bCullWhenNotRendered)
	{
		if (IsRenderStateCreated())
		{
			DestroyRenderState_Concurrent();
		}
	}

Though as a full solution I would like to have both the rendering based visibility culling and the distance culling etc work together fully. i.e. if you’re culled by any other means then don’t submit occlusion queries but if you’re in range etc then rely on occlusion for additional culling.

Hope that helps.

Something I hope to get to in the 5.8 time frame but it’s nothing I can commit to 100% as there are always bigger fish to fry..

Thanks for the insights Simon! I’ll give this a try locally and see how it works out!

Cheers!

-Rick

So far, this fix is working great. Thank you so much Simon!

You’ve mentioned intent to eventually implement a more wholistic solution. Is there an estimated timeline for that update to arrive, so we know roughly which update cycle we should be looking to eventually unwind this change in?

Cheers!

-Rick

Totally understand that. Thanks for the insights, I’ll put a comment next to the edit in our codebase so we know that we expect our edit to eventually deprecate.

Thank you so much Simon, the change is working great for us.

Cheers!

-Rick