We have found an issue where disabling Distance Field AO for the project was breaking the collisions for Niagara particles when used in tandem with Hardware Lumen. After some investigation we have found the reason.
DistanceFieldAmbientOcclusion.cpp implements this method (line 756):
bool ShouldPrepareGlobalDistanceField(const FSceneRenderer* Renderer)Which performs this check on line 777:
if (SupportsDistanceFieldAO(Renderer->FeatureLevel, Renderer->ShaderPlatform) && (bShouldPrepareForMaterialsOrNiagara || ShouldPrepareForDistanceFieldAO(Scene, ViewFamily, Renderer->AnyViewHasGIMethodSupportingDFAO())))Which always resolves to false in the cases where we don’t need the Global DF scene for Lumen and have DFAO disabled. However, we have noticed that SupportsDistanceFieldAO() gets called in ShouldPrepareForDistanceFieldAO() later anyways, so the first check is not needed if we want to keep Global DF collisions in scenes with Niagara systems that require the Global Distance Field without breaking any other logic.
We have modified the check locally by removing the SupportsDistanceFieldAO() and restored the Niagara functionality:
if (bShouldPrepareForMaterialsOrNiagara || ShouldPrepareForDistanceFieldAO(Scene, ViewFamily, Renderer->AnyViewHasGIMethodSupportingDFAO())))Is this a safe change to make?
Steps to Reproduce
Tested on clean projects on 5.7 preview and 5.6.1, launcher versions.
Create a new project that uses hardware Lumen by default;
Create a new level using the open world template;
Create a new Niagara system using the Fountain emitter preset. Switch it to GPU mode and add GPU Distance Field collisions to the Particle Update step;
Add the system to the level. The particles will collide with the landscape;
Disable Distance Field AO (r.DistanceFieldAO 0). The particles will stop colliding with the landscape.
Sorry for the late response. You’re correct with your investigation. The current code does conflate the use of DistanceFieldAO and the use of the GlobalDistanceField. In general I’m hesitant to have large global features (like whether we are generating a global distance field) being enabled because we end up having a material or an effect that might depend on it; it feels like too much of a trap where a someone drops in the wrong asset into a level and all of a sudden we’re paying a huge cost and it can be annoying to track down. That being said, it’s equally annoying to not be able to enable these kinds of features (global distance field) without enabling all kinds of other stuff that you don’t want (distance field AO).
I think that what I’ll do is introduce a new cvar that will allow projects to define whether an instance of a material/effect is sufficient to enable GDF…basically the same code as what you have but gated by another cvar (which we’ll default to off to preserve behavior).
you’re not wrong…some random explosion happening every 10 seconds and the GDF popping in/out of existence does sound like a bit of a nightmare. It does feel like a second cvar would be valuable…will check out opinions on the team..
Looking at the code a bit more, there already is r.AOGlobalDistanceField.DetailedNecessityCheck which can be set to 0, which would (along with a new cvar to make the ‘necessity check’ sufficient to enable GDF) solve things…
I absolutely agree with your opinion on big rendering features being potentially enabled due to content in a scene. I also like your suggestion to introduce an additional CVar.
But now I have another question - wouldn’t linking global df scene availability to content presence in the scene introduce volatility into rendering in general? e.g. spawning an effect that requires the distance field for collisions when the scene is not built yet or removing everything from the scene that was keeping the distance fields in place? Perhaps it would be better to completely decouple content from DF availability and provide a global CVar that controls if the global DF scene should be built?