Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/LightSceneInfo.cpp
andrew davidson 3542cab338 FMatrix explicit cast fixes - Renderer
#rb ben.ingram, zak.middleton
#preflight 61f285e71e5d78c38307cda4

#ROBOMERGE-AUTHOR: andrew.davidson
#ROBOMERGE-SOURCE: CL 18752245 in //UE5/Release-5.0/... via CL 18752267 via CL 18752335
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v903-18687472)

[CL 18752338 by andrew davidson in ue5-main branch]
2022-01-27 07:20:20 -05:00

474 lines
18 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LightSceneInfo.cpp: Light scene info implementation.
=============================================================================*/
#include "LightSceneInfo.h"
#include "Components/LightComponent.h"
#include "SceneCore.h"
#include "ScenePrivate.h"
#include "DistanceFieldLightingShared.h"
#include "Misc/LargeWorldRenderPosition.h"
int32 GWholeSceneShadowUnbuiltInteractionThreshold = 500;
static FAutoConsoleVariableRef CVarWholeSceneShadowUnbuiltInteractionThreshold(
TEXT("r.Shadow.WholeSceneShadowUnbuiltInteractionThreshold"),
GWholeSceneShadowUnbuiltInteractionThreshold,
TEXT("How many unbuilt light-primitive interactions there can be for a light before the light switches to whole scene shadows"),
ECVF_RenderThreadSafe
);
static int32 GRecordInteractionShadowPrimitives = 1;
FAutoConsoleVariableRef CVarRecordInteractionShadowPrimitives(
TEXT("r.Shadow.RecordInteractionShadowPrimitives"),
GRecordInteractionShadowPrimitives,
TEXT(""),
ECVF_RenderThreadSafe);
void FLightSceneInfoCompact::Init(FLightSceneInfo* InLightSceneInfo)
{
LightSceneInfo = InLightSceneInfo;
FSphere BoundingSphere = InLightSceneInfo->Proxy->GetBoundingSphere();
BoundingSphere.W = BoundingSphere.W > 0.0f ? BoundingSphere.W : FLT_MAX;
FMemory::Memcpy(&BoundingSphereVector,&BoundingSphere,sizeof(BoundingSphereVector));
Color = InLightSceneInfo->Proxy->GetColor();
LightType = InLightSceneInfo->Proxy->GetLightType();
bCastDynamicShadow = InLightSceneInfo->Proxy->CastsDynamicShadow();
bCastStaticShadow = InLightSceneInfo->Proxy->CastsStaticShadow();
bStaticLighting = InLightSceneInfo->Proxy->HasStaticLighting();
bAffectReflection = InLightSceneInfo->Proxy->AffectReflection();
bAffectGlobalIllumination = InLightSceneInfo->Proxy->AffectGlobalIllumination();
CastRaytracedShadow = InLightSceneInfo->Proxy->CastsRaytracedShadow();
}
FLightSceneInfo::FLightSceneInfo(FLightSceneProxy* InProxy, bool InbVisible)
: bRecordInteractionShadowPrimitives(!!GRecordInteractionShadowPrimitives && InProxy->GetLightType() != ELightComponentType::LightType_Directional)
, DynamicInteractionOftenMovingPrimitiveList(NULL)
, DynamicInteractionStaticPrimitiveList(NULL)
, Proxy(InProxy)
, Id(INDEX_NONE)
, DynamicShadowMapChannel(-1)
, bPrecomputedLightingIsValid(InProxy->GetLightComponent()->IsPrecomputedLightingValid())
, bVisible(InbVisible)
, bEnableLightShaftBloom(InProxy->GetLightComponent()->bEnableLightShaftBloom)
, BloomScale(InProxy->GetLightComponent()->BloomScale)
, BloomThreshold(InProxy->GetLightComponent()->BloomThreshold)
, BloomMaxBrightness(InProxy->GetLightComponent()->BloomMaxBrightness)
, BloomTint(InProxy->GetLightComponent()->BloomTint)
, NumUnbuiltInteractions(0)
, bCreatePerObjectShadowsForDynamicObjects(Proxy->ShouldCreatePerObjectShadowsForDynamicObjects())
, Scene(InProxy->GetLightComponent()->GetScene()->GetRenderScene())
{
// Only visible lights can be added in game
check(bVisible || GIsEditor);
BeginInitResource(this);
}
FLightSceneInfo::~FLightSceneInfo()
{
ReleaseResource();
}
void FLightSceneInfo::AddToScene()
{
const FLightSceneInfoCompact& LightSceneInfoCompact = Scene->Lights[Id];
bool bIsValidLightTypeMobile = false;
if (Scene->GetShadingPath() == EShadingPath::Mobile && Proxy->IsMovable())
{
static const auto MobileEnableMovableSpotLightsVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableMovableSpotLights"));
const uint8 LightType = Proxy->GetLightType();
bIsValidLightTypeMobile = LightType == LightType_Rect || LightType == LightType_Point
|| (LightType == LightType_Spot && MobileEnableMovableSpotLightsVar->GetValueOnRenderThread());
}
// Only need to create light interactions for lights that can cast a shadow,
// As deferred shading doesn't need to know anything about the primitives that a light affects
if (Proxy->CastsDynamicShadow()
|| Proxy->CastsStaticShadow()
// Lights that should be baked need to check for interactions to track unbuilt state correctly
|| Proxy->HasStaticLighting()
// Mobile path supports dynamic point/spot lights in the base pass using forward rendering, so we need to know the primitives
|| bIsValidLightTypeMobile)
{
// Directional lights have no finite extent and cannot meaningfully be in the LocalShadowCastingLightOctree
if (LightSceneInfoCompact.LightType == LightType_Directional)
{
//
Scene->DirectionalShadowCastingLightIDs.Add(Id);
// All primitives may interact with a directional light
FMemMark MemStackMark(FMemStack::Get());
for (FPrimitiveSceneInfo *PrimitiveSceneInfo : Scene->Primitives)
{
CreateLightPrimitiveInteraction(LightSceneInfoCompact, PrimitiveSceneInfo);
}
}
else
{
// Add the light to the scene's light octree.
Scene->LocalShadowCastingLightOctree.AddElement(LightSceneInfoCompact);
// Find primitives that the light affects in the primitive octree.
FMemMark MemStackMark(FMemStack::Get());
Scene->PrimitiveOctree.FindElementsWithBoundsTest(GetBoundingBox(), [&LightSceneInfoCompact, this](const FPrimitiveSceneInfoCompact& PrimitiveSceneInfoCompact)
{
CreateLightPrimitiveInteraction(LightSceneInfoCompact, PrimitiveSceneInfoCompact);
});
if (bIsValidLightTypeMobile)
{
Proxy->MobileMovablePointLightUniformBuffer = TUniformBufferRef<FMobileMovablePointLightUniformShaderParameters>::CreateUniformBufferImmediate(GetDummyMovablePointLightUniformShaderParameters(), UniformBuffer_MultiFrame);
Proxy->bMobileMovablePointLightUniformBufferNeedsUpdate = true;
}
}
}
}
/**
* If the light affects the primitive, create an interaction, and process children
*
* @param LightSceneInfoCompact Compact representation of the light
* @param PrimitiveSceneInfoCompact Compact representation of the primitive
*/
void FLightSceneInfo::CreateLightPrimitiveInteraction(const FLightSceneInfoCompact& LightSceneInfoCompact, const FPrimitiveSceneInfoCompact& PrimitiveSceneInfoCompact)
{
if( !Scene->IsPrimitiveBeingRemoved(PrimitiveSceneInfoCompact.PrimitiveSceneInfo) && LightSceneInfoCompact.AffectsPrimitive(FBoxSphereBounds(PrimitiveSceneInfoCompact.Bounds), PrimitiveSceneInfoCompact.Proxy))
{
// create light interaction and add to light/primitive lists
FLightPrimitiveInteraction::Create(this,PrimitiveSceneInfoCompact.PrimitiveSceneInfo);
}
}
void FLightSceneInfo::RemoveFromScene()
{
if (OctreeId.IsValidId())
{
// Remove the light from the octree.
Scene->LocalShadowCastingLightOctree.RemoveElement(OctreeId);
}
else
{
Scene->DirectionalShadowCastingLightIDs.RemoveSwap(Id);
}
Scene->CachedShadowMaps.Remove(Id);
// Detach the light from the primitives it affects.
Detach();
}
void FLightSceneInfo::Detach()
{
check(IsInRenderingThread());
InteractionShadowPrimitives.Empty();
// implicit linked list. The destruction will update this "head" pointer to the next item in the list.
while(DynamicInteractionOftenMovingPrimitiveList)
{
FLightPrimitiveInteraction::Destroy(DynamicInteractionOftenMovingPrimitiveList);
}
while(DynamicInteractionStaticPrimitiveList)
{
FLightPrimitiveInteraction::Destroy(DynamicInteractionStaticPrimitiveList);
}
}
bool FLightSceneInfo::ShouldRenderLight(const FViewInfo& View, bool bOffscreen) const
{
// Only render the light if it is in the view frustum
bool bLocalVisible = bVisible && (bOffscreen ? View.VisibleLightInfos[Id].bInDrawRange : View.VisibleLightInfos[Id].bInViewFrustum);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
ELightComponentType Type = (ELightComponentType)Proxy->GetLightType();
switch(Type)
{
case LightType_Directional:
if(!View.Family->EngineShowFlags.DirectionalLights)
{
bLocalVisible = false;
}
break;
case LightType_Point:
if(!View.Family->EngineShowFlags.PointLights)
{
bLocalVisible = false;
}
break;
case LightType_Spot:
if(!View.Family->EngineShowFlags.SpotLights)
{
bLocalVisible = false;
}
break;
case LightType_Rect:
if(!View.Family->EngineShowFlags.RectLights)
{
bLocalVisible = false;
}
break;
}
#endif
return bLocalVisible
// Only render lights with static shadowing for reflection captures, since they are only captured at edit time
&& (!View.bStaticSceneOnly || Proxy->HasStaticShadowing())
// Only render lights in the default channel, or if there are any primitives outside the default channel
&& (Proxy->GetLightingChannelMask() & GetDefaultLightingChannelMask() || View.bUsesLightingChannels);
}
bool FLightSceneInfo::IsPrecomputedLightingValid() const
{
return (bPrecomputedLightingIsValid && NumUnbuiltInteractions < GWholeSceneShadowUnbuiltInteractionThreshold) || !Proxy->HasStaticShadowing();
}
const TArray<FLightPrimitiveInteraction*>* FLightSceneInfo::GetInteractionShadowPrimitives() const
{
return bRecordInteractionShadowPrimitives ? &InteractionShadowPrimitives : nullptr;
}
FLightPrimitiveInteraction* FLightSceneInfo::GetDynamicInteractionOftenMovingPrimitiveList() const
{
return DynamicInteractionOftenMovingPrimitiveList;
}
FLightPrimitiveInteraction* FLightSceneInfo::GetDynamicInteractionStaticPrimitiveList() const
{
return DynamicInteractionStaticPrimitiveList;
}
void FLightSceneInfo::ConditionalUpdateMobileMovablePointLightUniformBuffer(const FSceneRenderer* SceneRenderer)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FLightSceneProxy_UpdateMobileMovablePointLightUniformBuffer);
static auto* MobileNumDynamicPointLightsCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MobileNumDynamicPointLights"));
const int32 MobileNumDynamicPointLights = MobileNumDynamicPointLightsCVar->GetValueOnRenderThread();
static auto* MobileEnableMovableSpotlightsCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableMovableSpotlights"));
const int32 MobileEnableMovableSpotlights = MobileEnableMovableSpotlightsCVar->GetValueOnRenderThread();
static auto* EnableMovableSpotlightsShadowCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableMovableSpotlightsShadow"));
const int32 EnableMovableSpotlightsShadow = EnableMovableSpotlightsShadowCVar->GetValueOnRenderThread();
checkSlow(MobileNumDynamicPointLights > 0 && SceneRenderer);
FVector4f LightPositionAndInvRadius;
FVector4f LightColorAndFalloffExponent;
FVector4f SpotLightDirectionAndSpecularScale;
FVector4f SpotLightAnglesAndSoftTransitionScaleAndLightShadowType;
FVector4f SpotLightShadowSharpenAndShadowFadeFraction;
FVector4f SpotLightShadowmapMinMax;
FVector4f LightTilePosition;
FMatrix44f SpotLightWorldToShadowMatrix;
bool bShouldBeRender = false;
bool bShouldCastShadow = false;
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
bShouldBeRender = ShouldRenderLight(SceneRenderer->Views[ViewIndex]);
if (bShouldBeRender)
{
break;
}
}
if (bShouldBeRender)
{
const uint8 LightType = Proxy->GetLightType();
const bool bIsValidLightType =
LightType == LightType_Point
|| LightType == LightType_Rect
|| (LightType == LightType_Spot && MobileEnableMovableSpotlights);
checkSlow(bIsValidLightType && Proxy->IsMovable());
FLightRenderParameters LightParameters;
Proxy->GetLightShaderParameters(LightParameters);
float LightFadeFactor = 0.0f;
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
LightFadeFactor = FMath::Max(LightFadeFactor, GetLightFadeFactor(SceneRenderer->Views[ViewIndex], Proxy));
}
LightParameters.Color *= LightFadeFactor;
if (Proxy->IsInverseSquared())
{
LightParameters.FalloffExponent = 0;
}
uint32 LightShadowType = LightType == LightType_Spot ? 2 : 1;
bShouldCastShadow = SceneRenderer->ViewFamily.EngineShowFlags.DynamicShadows
&& !IsSimpleForwardShadingEnabled(SceneRenderer->ShaderPlatform)
&& GetShadowQuality() > 0
&& EnableMovableSpotlightsShadow != 0
&& LightType == LightType_Spot
&& SceneRenderer->VisibleLightInfos[Id].AllProjectedShadows.Num() > 0
&& SceneRenderer->VisibleLightInfos[Id].AllProjectedShadows.Last()->bAllocated;
LightShadowType |= bShouldCastShadow ? 4 : 0;
float SoftTransitionScale = 0.0f;
float ShadowFadeFraction = 0.0f;
if (bShouldCastShadow)
{
FProjectedShadowInfo* ProjectedShadowInfo = SceneRenderer->VisibleLightInfos[Id].AllProjectedShadows.Last();
checkSlow(ProjectedShadowInfo && ProjectedShadowInfo->CacheMode != SDCM_StaticPrimitivesOnly);
const float TransitionSize = ProjectedShadowInfo->ComputeTransitionSize();
checkSlow(TransitionSize > 0.0f);
SoftTransitionScale = 1.0f / TransitionSize;
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
ShadowFadeFraction = FMath::Max(ShadowFadeFraction, ProjectedShadowInfo->FadeAlphas[ViewIndex]);
}
SpotLightShadowSharpenAndShadowFadeFraction = FVector4f(Proxy->GetShadowSharpen() * 7.0f + 1.0f, ShadowFadeFraction, ProjectedShadowInfo->GetShaderReceiverDepthBias(), 0.0f);
const FMatrix WorldToShadowMatrix = ProjectedShadowInfo->GetWorldToShadowMatrix(SpotLightShadowmapMinMax);
SpotLightWorldToShadowMatrix = FMatrix44f(FTranslationMatrix(LightParameters.WorldPosition) * WorldToShadowMatrix);
}
const FLargeWorldRenderPosition AbsoluteWorldPosition(LightParameters.WorldPosition);
LightPositionAndInvRadius = FVector4f(AbsoluteWorldPosition.GetOffset(), LightParameters.InvRadius);
LightTilePosition = FVector4f(AbsoluteWorldPosition.GetTile(), 0);
LightColorAndFalloffExponent = FVector4f(LightParameters.Color, LightParameters.FalloffExponent);
SpotLightDirectionAndSpecularScale = FVector4f(LightParameters.Direction.X, LightParameters.Direction.Y, LightParameters.Direction.Z, Proxy->GetSpecularScale());
SpotLightAnglesAndSoftTransitionScaleAndLightShadowType = FVector4f(LightParameters.SpotAngles.X, LightParameters.SpotAngles.Y, SoftTransitionScale, LightShadowType);
}
if (bShouldBeRender != Proxy->bMobileMovablePointLightShouldBeRender ||
bShouldCastShadow != Proxy->bMobileMovablePointLightShouldCastShadow ||
SpotLightShadowmapMinMax != Proxy->MobileMovablePointLightShadowmapMinMax)
{
Proxy->bMobileMovablePointLightUniformBufferNeedsUpdate = true;
Proxy->bMobileMovablePointLightShouldBeRender = bShouldBeRender;
Proxy->bMobileMovablePointLightShouldCastShadow = bShouldCastShadow;
Proxy->MobileMovablePointLightShadowmapMinMax = SpotLightShadowmapMinMax;
}
if (Proxy->bMobileMovablePointLightUniformBufferNeedsUpdate)
{
const FMobileMovablePointLightUniformShaderParameters MobileMovablePointLightUniformShaderParameters =
GetMovablePointLightUniformShaderParameters(
LightPositionAndInvRadius,
LightTilePosition,
LightColorAndFalloffExponent,
SpotLightDirectionAndSpecularScale,
SpotLightAnglesAndSoftTransitionScaleAndLightShadowType,
SpotLightShadowSharpenAndShadowFadeFraction,
SpotLightShadowmapMinMax,
SpotLightWorldToShadowMatrix
);
Proxy->MobileMovablePointLightUniformBuffer.UpdateUniformBufferImmediate(MobileMovablePointLightUniformShaderParameters);
Proxy->bMobileMovablePointLightUniformBufferNeedsUpdate = false;
}
}
bool FLightSceneInfo::ShouldRecordShadowSubjectsForMobile() const
{
if (Proxy == nullptr)
{
return false;
}
// record shadow casters if CSM culling is enabled for the light's mobility type and the culling mode requires the list of casters.
static auto* MyCVarMobileEnableStaticAndCSMShadowReceivers = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableStaticAndCSMShadowReceivers"));
const bool bCombinedStaticAndCSMEnabled = MyCVarMobileEnableStaticAndCSMShadowReceivers->GetValueOnAnyThread() != 0;
static auto* CVarMobileEnableMovableLightCSMShaderCulling = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableMovableLightCSMShaderCulling"));
const bool bMobileEnableMovableLightCSMShaderCulling = CVarMobileEnableMovableLightCSMShaderCulling->GetValueOnAnyThread() == 1;
static auto* CVarMobileCSMShaderCullingMethod = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.Shadow.CSMShaderCullingMethod"));
const uint32 MobileCSMCullingMode = CVarMobileCSMShaderCullingMethod->GetValueOnAnyThread() & 0xF;
const FLightSceneProxy* LightSceneProxy = Proxy;
bool bRenderMovableDirectionalLightCSM = LightSceneProxy->IsMovable() && ShouldRenderViewIndependentWholeSceneShadows();
bool bLightHasCombinedStaticAndCSMEnabled = bCombinedStaticAndCSMEnabled && LightSceneProxy->UseCSMForDynamicObjects();
bool bMovableLightUsingCSM = bMobileEnableMovableLightCSMShaderCulling && bRenderMovableDirectionalLightCSM;
bool bShouldRecordShadowSubjectsForMobile = (MobileCSMCullingMode == 2 || MobileCSMCullingMode == 3) && (bLightHasCombinedStaticAndCSMEnabled || bMovableLightUsingCSM);
return bShouldRecordShadowSubjectsForMobile;
}
/** Determines whether two bounding spheres intersect. */
FORCEINLINE bool AreSpheresNotIntersecting(
const VectorRegister& A_XYZ,
const VectorRegister& A_Radius,
const VectorRegister& B_XYZ,
const VectorRegister& B_Radius
)
{
const VectorRegister DeltaVector = VectorSubtract(A_XYZ,B_XYZ);
const VectorRegister DistanceSquared = VectorDot3(DeltaVector,DeltaVector);
const VectorRegister MaxDistance = VectorAdd(A_Radius,B_Radius);
const VectorRegister MaxDistanceSquared = VectorMultiply(MaxDistance,MaxDistance);
return !!VectorAnyGreaterThan(DistanceSquared,MaxDistanceSquared);
}
/**
* Tests whether this light affects the given primitive. This checks both the primitive and light settings for light relevance
* and also calls AffectsBounds.
*
* @param CompactPrimitiveSceneInfo - The primitive to test.
* @return True if the light affects the primitive.
*/
bool FLightSceneInfoCompact::AffectsPrimitive(const FBoxSphereBounds& PrimitiveBounds, const FPrimitiveSceneProxy* PrimitiveSceneProxy) const
{
// Check if the light's bounds intersect the primitive's bounds.
// Directional lights reach everywhere (the hacky world max radius does not work for large worlds)
if(LightType != LightType_Directional && AreSpheresNotIntersecting(
BoundingSphereVector,
VectorReplicate(BoundingSphereVector,3),
VectorLoadFloat3(&PrimitiveBounds.Origin),
VectorLoadFloat1(&PrimitiveBounds.SphereRadius)
))
{
return false;
}
// Cull based on information in the full scene infos.
if(!LightSceneInfo->Proxy->AffectsBounds(PrimitiveBounds))
{
return false;
}
if (LightSceneInfo->Proxy->CastsShadowsFromCinematicObjectsOnly() && !PrimitiveSceneProxy->CastsCinematicShadow())
{
return false;
}
if (!(LightSceneInfo->Proxy->GetLightingChannelMask() & PrimitiveSceneProxy->GetLightingChannelMask()))
{
return false;
}
return true;
}