FDeferredShandingRenderer:Render()
FSortedLightSetSceneInfo& SortedLightSet = *GraphBuilder.AllocObject<FSortedLightSetSceneInfo>();
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, SortLights);
RDG_GPU_STAT_SCOPE(GraphBuilder, SortLights);
GatherLightsAndComputeLightGrid(GraphBuilder, bComputeLightGrid, SortedLightSet);
}
void FDeferredShadingSceneRenderer::GatherLightsAndComputeLightGrid(FRDGBuilder& GraphBuilder, bool bNeedLightGrid, FSortedLightSetSceneInfo& SortedLightSet)
{
bool bShadowedLightsInClustered = ShouldUseClusteredDeferredShading()
&& CVarVirtualShadowOnePassProjection.GetValueOnRenderThread()
&& VirtualShadowMapArray.IsEnabled();
const bool bUseLumenDirectLighting = ShouldRenderLumenDirectLighting(Scene, Views[0]);
GatherAndSortLights(SortedLightSet, bShadowedLightsInClustered, bUseLumenDirectLighting);
if (!bNeedLightGrid)
{
TRDGUniformBufferRef<FForwardLightData> ForwardLightUniformBuffer = CreateDummyForwardLightUniformBuffer(GraphBuilder);
for (auto& View : Views)
{
View.ForwardLightingResources.SetUniformBuffer(ForwardLightUniformBuffer);
}
return;
}
bool bAnyViewUsesForwardLighting = false;
bool bAnyViewUsesLumen = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
bAnyViewUsesForwardLighting |= View.bTranslucentSurfaceLighting || ShouldRenderVolumetricFog() || View.bHasSingleLayerWaterMaterial || VolumetricCloudWantsToSampleLocalLights(Scene, ViewFamily.EngineShowFlags) || ShouldVisualizeLightGrid();
bAnyViewUsesLumen |= GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen || GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen;
}
const bool bCullLightsToGrid = GLightCullingQuality
&& (IsForwardShadingEnabled(ShaderPlatform) || bAnyViewUsesForwardLighting || IsRayTracingEnabled() || ShouldUseClusteredDeferredShading() ||
bAnyViewUsesLumen || ViewFamily.EngineShowFlags.VisualizeMeshDistanceFields || VirtualShadowMapArray.IsEnabled());
// Store this flag if lights are injected in the grids, check with 'AreLightsInLightGrid()'
bAreLightsInLightGrid = bCullLightsToGrid;
ComputeLightGrid(GraphBuilder, bCullLightsToGrid, SortedLightSet);
}
bool ShouldRenderLumenDirectLighting(const FScene* Scene, const FSceneView& View)
{
return ShouldRenderLumenDiffuseGI(Scene, View)
&& GLumenGatherCvars.DirectLighting
&& !GLumenIrradianceFieldGather;
}
bool ShouldRenderLumenDiffuseGI(const FScene* Scene, const FSceneView& View, bool bSkipTracingDataCheck, bool bSkipProjectCheck)
{
return Lumen::IsLumenFeatureAllowedForView(Scene, View, bSkipTracingDataCheck, bSkipProjectCheck)
&& View.FinalPostProcessSettings.DynamicGlobalIlluminationMethod == EDynamicGlobalIlluminationMethod::Lumen
&& CVarLumenGlobalIllumination.GetValueOnAnyThread()
&& View.Family->EngineShowFlags.GlobalIllumination
&& View.Family->EngineShowFlags.LumenGlobalIllumination
&& (bSkipTracingDataCheck || Lumen::UseHardwareRayTracedScreenProbeGather(*View.Family) || Lumen::IsSoftwareRayTracingSupported());
}
static TAutoConsoleVariable<int> CVarLumenGlobalIllumination(
TEXT("r.Lumen.DiffuseIndirect.Allow"),
1,
TEXT("Whether to allow Lumen Global Illumination. Lumen GI is enabled in the project settings, this cvar can only disable it."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
// Default value is 0
FAutoConsoleVariableRef CVarLumenScreenProbeGatherDirectLighting(
TEXT("r.Lumen.ScreenProbeGather.DirectLighting"),
GLumenGatherCvars.DirectLighting,
TEXT("Whether to render all local lights through Lumen's Final Gather, when enabled. This gives very cheap but low quality direct lighting."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
void FSceneRenderer::GatherAndSortLights(FSortedLightSetSceneInfo& OutSortedLights, bool bShadowedLightsInClustered, bool bUseLumenDirectLighting)
{
if (AllowSimpleLights())
{
GatherSimpleLights(ViewFamily, Views, OutSortedLights.SimpleLights);
}
FSimpleLightArray& SimpleLights = OutSortedLights.SimpleLights;
TArray<FSortedLightSceneInfo, SceneRenderingAllocator>& SortedLights = OutSortedLights.SortedLights;
// NOTE: we allocate space also for simple lights such that they can be referenced in the same sorted range
SortedLights.Empty(Scene->Lights.Num() + SimpleLights.InstanceData.Num());
bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows && GetShadowQuality() > 0;
#if ENABLE_DEBUG_DISCARD_PROP
int32 Total = Scene->Lights.Num() + SimpleLights.InstanceData.Num();
int32 NumToKeep = int32(float(Total) * (1.0f - GDebugLightDiscardProp));
const float DebugDiscardStride = float(NumToKeep) / float(Total);
float DebugDiscardCounter = 0.0f;
#endif // ENABLE_DEBUG_DISCARD_PROP
// Build a list of visible lights.
for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
const FLightSceneInfo* const LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
#if ENABLE_DEBUG_DISCARD_PROP
{
int32 PrevCounter = int32(DebugDiscardCounter);
DebugDiscardCounter += DebugDiscardStride;
if (PrevCounter >= int32(DebugDiscardCounter))
{
continue;
}
}
#endif // ENABLE_DEBUG_DISCARD_PROP
if (LightSceneInfo->ShouldRenderLightViewIndependent()
// Reflection override skips direct specular because it tends to be blindingly bright with a perfectly smooth surface
&& !ViewFamily.EngineShowFlags.ReflectionOverride)
{
// Check if the light is visible in any of the views.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (LightSceneInfo->ShouldRenderLight(Views[ViewIndex]))
{
FSortedLightSceneInfo* SortedLightInfo = new(SortedLights) FSortedLightSceneInfo(LightSceneInfo);
// Check for shadows and light functions.
SortedLightInfo->SortKey.Fields.LightType = LightSceneInfoCompact.LightType;
SortedLightInfo->SortKey.Fields.bTextureProfile = ViewFamily.EngineShowFlags.TexturedLightProfiles && LightSceneInfo->Proxy->GetIESTextureResource();
SortedLightInfo->SortKey.Fields.bShadowed = bDynamicShadows && CheckForProjectedShadows(LightSceneInfo);
SortedLightInfo->SortKey.Fields.bLightFunction = ViewFamily.EngineShowFlags.LightFunctions && CheckForLightFunction(LightSceneInfo);
SortedLightInfo->SortKey.Fields.bUsesLightingChannels = Views[ViewIndex].bUsesLightingChannels && LightSceneInfo->Proxy->GetLightingChannelMask() != GetDefaultLightingChannelMask();
const bool bSupportedByLumenDirectLighting = bUseLumenDirectLighting &&
LightSceneInfoCompact.LightType != LightType_Directional;
// Todo - have to match shadow setup
/*&&
!SortedLightInfo->SortKey.Fields.bTextureProfile &&
!SortedLightInfo->SortKey.Fields.bLightFunction &&
!SortedLightInfo->SortKey.Fields.bUsesLightingChannels;*/
// These are not simple lights.
SortedLightInfo->SortKey.Fields.bIsNotSimpleLight = 1;
// tiled and clustered deferred lighting only supported for certain lights that don't use any additional features
// And also that are not directional (mostly because it does'nt make so much sense to insert them into every grid cell in the universe)
// In the forward case one directional light gets put into its own variables, and in the deferred case it gets a full-screen pass.
// Usually it'll have shadows and stuff anyway.
// Rect lights are not supported as the performance impact is significant even if not used, for now, left for trad. deferred.
const bool bClusteredDeferredSupported =
!SortedLightInfo->SortKey.Fields.bTextureProfile &&
(!SortedLightInfo->SortKey.Fields.bShadowed || bShadowedLightsInClustered) &&
!SortedLightInfo->SortKey.Fields.bLightFunction &&
!SortedLightInfo->SortKey.Fields.bUsesLightingChannels
&& LightSceneInfoCompact.LightType != LightType_Directional
&& LightSceneInfoCompact.LightType != LightType_Rect
&& !bSupportedByLumenDirectLighting;
// One pass projection is supported for lights with only virtual shadow maps
// TODO: Exclude lights that also have non-virtual shadow maps
bool bHasVirtualShadowMap = VisibleLightInfos[LightSceneInfo->Id].GetVirtualShadowMapId(&Views[ViewIndex]) != INDEX_NONE;
SortedLightInfo->SortKey.Fields.bDoesNotWriteIntoPackedShadowMask = LightSceneInfoCompact.LightType == LightType_Directional || !bHasVirtualShadowMap;
SortedLightInfo->SortKey.Fields.bClusteredDeferredNotSupported = !bClusteredDeferredSupported;
SortedLightInfo->SortKey.Fields.bHandledByLumen = bSupportedByLumenDirectLighting;
break;
}
}
}
}
// Add the simple lights also
for (int32 SimpleLightIndex = 0; SimpleLightIndex < SimpleLights.InstanceData.Num(); SimpleLightIndex++)
{
#if ENABLE_DEBUG_DISCARD_PROP
{
int32 PrevCounter = int32(DebugDiscardCounter);
DebugDiscardCounter += DebugDiscardStride;
if (PrevCounter >= int32(DebugDiscardCounter))
{
continue;
}
}
#endif // ENABLE_DEBUG_DISCARD_PROP
FSortedLightSceneInfo* SortedLightInfo = new(SortedLights) FSortedLightSceneInfo(SimpleLightIndex);
SortedLightInfo->SortKey.Fields.LightType = LightType_Point;
SortedLightInfo->SortKey.Fields.bTextureProfile = 0;
SortedLightInfo->SortKey.Fields.bShadowed = 0;
SortedLightInfo->SortKey.Fields.bLightFunction = 0;
SortedLightInfo->SortKey.Fields.bUsesLightingChannels = 0;
// These are simple lights.
SortedLightInfo->SortKey.Fields.bIsNotSimpleLight = 0;
// Simple lights are ok to use with tiled and clustered deferred lighting
SortedLightInfo->SortKey.Fields.bClusteredDeferredNotSupported = 0;
SortedLightInfo->SortKey.Fields.bHandledByLumen = 0;
}
// Sort non-shadowed, non-light function lights first to avoid render target switches.
struct FCompareFSortedLightSceneInfo
{
FORCEINLINE bool operator()( const FSortedLightSceneInfo& A, const FSortedLightSceneInfo& B ) const
{
return A.SortKey.Packed < B.SortKey.Packed;
}
};
SortedLights.Sort( FCompareFSortedLightSceneInfo() );
// Scan and find ranges.
OutSortedLights.SimpleLightsEnd = SortedLights.Num();
OutSortedLights.ClusteredSupportedEnd = SortedLights.Num();
OutSortedLights.UnbatchedLightStart = SortedLights.Num();
OutSortedLights.LumenLightStart = SortedLights.Num();
// Iterate over all lights to be rendered and build ranges for tiled deferred and unshadowed lights
for (int32 LightIndex = 0; LightIndex < SortedLights.Num(); LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const bool bDrawShadows = SortedLightInfo.SortKey.Fields.bShadowed;
const bool bDrawLightFunction = SortedLightInfo.SortKey.Fields.bLightFunction;
const bool bTextureLightProfile = SortedLightInfo.SortKey.Fields.bTextureProfile;
const bool bLightingChannels = SortedLightInfo.SortKey.Fields.bUsesLightingChannels;
if (SortedLightInfo.SortKey.Fields.bHandledByLumen && OutSortedLights.LumenLightStart == SortedLights.Num())
{
// Mark the first index that needs to be rendered
OutSortedLights.LumenLightStart = LightIndex;
}
if (SortedLightInfo.SortKey.Fields.bIsNotSimpleLight && OutSortedLights.SimpleLightsEnd == SortedLights.Num())
{
// Mark the first index to not be simple
OutSortedLights.SimpleLightsEnd = LightIndex;
}
if (SortedLightInfo.SortKey.Fields.bClusteredDeferredNotSupported && OutSortedLights.ClusteredSupportedEnd == SortedLights.Num())
{
// Mark the first index to not support clustered deferred
OutSortedLights.ClusteredSupportedEnd = LightIndex;
}
if ((bDrawShadows || bDrawLightFunction || bLightingChannels)
&& SortedLightInfo.SortKey.Fields.bClusteredDeferredNotSupported
&& !SortedLightInfo.SortKey.Fields.bHandledByLumen
&& OutSortedLights.UnbatchedLightStart == SortedLights.Num())
{
OutSortedLights.UnbatchedLightStart = LightIndex;
}
}
// Make sure no obvious things went wrong!
check(OutSortedLights.ClusteredSupportedEnd >= OutSortedLights.SimpleLightsEnd);
check(OutSortedLights.UnbatchedLightStart >= OutSortedLights.ClusteredSupportedEnd);
if (OutSortedLights.UnbatchedLightStart > OutSortedLights.LumenLightStart)
{
OutSortedLights.UnbatchedLightStart = OutSortedLights.LumenLightStart;
}
}