2022-05-26 15:26:34 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Scene . cpp : Scene manager implementation .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "CoreMinimal.h"
# include "HAL/ThreadSafeCounter.h"
# include "HAL/PlatformFileManager.h"
# include "Stats/Stats.h"
# include "HAL/IConsoleManager.h"
# include "Misc/App.h"
# include "UObject/UObjectIterator.h"
# include "Misc/PackageName.h"
# include "EngineDefines.h"
# include "EngineGlobals.h"
# include "Components/ActorComponent.h"
# include "RHI.h"
# include "RenderingThread.h"
# include "RenderResource.h"
# include "UniformBuffer.h"
# include "SceneTypes.h"
# include "SceneInterface.h"
# include "Components/PrimitiveComponent.h"
# include "PhysicsField/PhysicsFieldComponent.h"
# include "MaterialShared.h"
# include "SceneManagement.h"
# include "PrecomputedLightVolume.h"
# include "PrecomputedVolumetricLightmap.h"
# include "Components/LightComponent.h"
# include "GameFramework/WorldSettings.h"
# include "Components/DecalComponent.h"
# include "Components/ReflectionCaptureComponent.h"
# include "Components/RuntimeVirtualTextureComponent.h"
# include "Components/InstancedStaticMeshComponent.h"
# include "ScenePrivateBase.h"
# include "SceneCore.h"
# include "Rendering/MotionVectorSimulation.h"
# include "PrimitiveSceneInfo.h"
# include "LightSceneInfo.h"
# include "LightMapRendering.h"
# include "SkyAtmosphereRendering.h"
# include "BasePassRendering.h"
# include "MobileBasePassRendering.h"
# include "ScenePrivate.h"
# include "RendererModule.h"
# include "StaticMeshResources.h"
# include "ParameterCollection.h"
# include "DistanceFieldAmbientOcclusion.h"
# include "EngineModule.h"
# include "FXSystem.h"
# include "DistanceFieldLightingShared.h"
# include "SpeedTreeWind.h"
# include "Components/WindDirectionalSourceComponent.h"
# include "Lumen/LumenSceneRendering.h"
# include "PlanarReflectionSceneProxy.h"
# include "Engine/StaticMesh.h"
# include "GPUSkinCache.h"
# include "ComputeSystemInterface.h"
# include "DynamicShadowMapChannelBindingHelper.h"
# include "GPUScene.h"
# include "HAL/LowLevelMemTracker.h"
# include "VT/RuntimeVirtualTextureSceneProxy.h"
# include "HairStrandsInterface.h"
# include "VelocityRendering.h"
# include "RectLightSceneProxy.h"
# include "RectLightTextureManager.h"
# if RHI_RAYTRACING
# include "RayTracingDynamicGeometryCollection.h"
# include "RayTracingSkinnedGeometry.h"
# include "RayTracing/RayTracingScene.h"
# endif
# include "RHIGPUReadback.h"
# include "ShaderPrint.h"
# include "VirtualShadowMaps/VirtualShadowMapCacheManager.h"
# if WITH_EDITOR
# include "Rendering/StaticLightingSystemInterface.h"
# endif
# define VALIDATE_PRIMITIVE_PACKED_INDEX 0
/** Affects BasePassPixelShader.usf so must relaunch editor to recompile shaders. */
static TAutoConsoleVariable < int32 > CVarEarlyZPassOnlyMaterialMasking (
TEXT ( " r.EarlyZPassOnlyMaterialMasking " ) ,
0 ,
TEXT ( " Whether to compute materials' mask opacity only in early Z pass. Changing this setting requires restarting the editor. \n " )
TEXT ( " Note: Needs r.EarlyZPass == 2 && r.EarlyZPassMovable == 1 " ) ,
ECVF_RenderThreadSafe | ECVF_ReadOnly
) ;
/** Affects MobileBasePassPixelShader.usf so must relaunch editor to recompile shaders. */
static TAutoConsoleVariable < int32 > CVarMobileEarlyZPassOnlyMaterialMasking (
TEXT ( " r.Mobile.EarlyZPassOnlyMaterialMasking " ) ,
0 ,
TEXT ( " Whether to compute materials' mask opacity only in early Z pass for Mobile platform. Changing this setting requires restarting the editor. \n " )
TEXT ( " <=0: off \n " )
TEXT ( " 1: on \n " ) ,
ECVF_RenderThreadSafe | ECVF_ReadOnly
) ;
TAutoConsoleVariable < int32 > CVarEarlyZPass (
TEXT ( " r.EarlyZPass " ) ,
3 ,
TEXT ( " Whether to use a depth only pass to initialize Z culling for the base pass. Cannot be changed at runtime. \n " )
TEXT ( " Note: also look at r.EarlyZPassMovable \n " )
TEXT ( " 0: off \n " )
TEXT ( " 1: good occluders only: not masked, and large on screen \n " )
TEXT ( " 2: all opaque (including masked) \n " )
TEXT ( " x: use built in heuristic (default is 3) " ) ,
ECVF_Scalability ) ;
static TAutoConsoleVariable < int32 > CVarMobileEarlyZPass (
TEXT ( " r.Mobile.EarlyZPass " ) ,
0 ,
TEXT ( " Whether to use a depth only pass to initialize Z culling for the mobile base pass. \n " )
TEXT ( " 0: off \n " )
TEXT ( " 1: all opaque \n " ) ,
ECVF_Scalability
) ;
static TAutoConsoleVariable < int32 > CVarBasePassWriteDepthEvenWithFullPrepass (
TEXT ( " r.BasePassWriteDepthEvenWithFullPrepass " ) ,
0 ,
TEXT ( " 0 to allow a readonly base pass, which skips an MSAA depth resolve, and allows masked materials to get EarlyZ (writing to depth while doing clip() disables EarlyZ) (default) \n " )
TEXT ( " 1 to force depth writes in the base pass. Useful for debugging when the prepass and base pass don't match what they render. " ) ) ;
DECLARE_CYCLE_STAT ( TEXT ( " DeferredShadingSceneRenderer MotionBlurStartFrame " ) , STAT_FDeferredShadingSceneRenderer_MotionBlurStartFrame , STATGROUP_SceneRendering ) ;
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT ( FDistanceCullFadeUniformShaderParameters , " PrimitiveFade " ) ;
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT ( FDitherUniformShaderParameters , " PrimitiveDither " ) ;
/** Global primitive uniform buffer resource containing distance cull faded in */
TGlobalResource < FGlobalDistanceCullFadeUniformBuffer > GDistanceCullFadedInUniformBuffer ;
/** Global primitive uniform buffer resource containing dither faded in */
TGlobalResource < FGlobalDitherUniformBuffer > GDitherFadedInUniformBuffer ;
static FThreadSafeCounter FSceneViewState_UniqueID ;
# define ENABLE_LOG_PRIMITIVE_INSTANCE_ID_STATS_TO_CSV 0
# if ENABLE_LOG_PRIMITIVE_INSTANCE_ID_STATS_TO_CSV
int32 GDumpPrimitiveAllocatorStatsToCSV = 0 ;
FAutoConsoleVariableRef CVarDumpPrimitiveStatsToCSV (
TEXT ( " r.DumpPrimitiveStatsToCSV " ) ,
GDumpPrimitiveAllocatorStatsToCSV ,
TEXT ( " Dump primitive and instance stats to CSV \n " )
TEXT ( " 0 - stop recording, dump to csv and clear array. \n " )
TEXT ( " 1 - start recording into array \n " )
TEXT ( " 2 - dump to csv and continue recording without clearing array \n " ) ,
ECVF_RenderThreadSafe
) ;
static constexpr int32 StatStride = 8 ;
TArray < int32 > GPrimitiveAllocatorStats ;
void DumpPrimitiveAllocatorStats ( )
{
if ( ! GPrimitiveAllocatorStats . IsEmpty ( ) )
{
FString FileName = FPaths : : ProjectLogDir ( ) / TEXT ( " PrimitiveStats- " ) + FDateTime : : Now ( ) . ToString ( ) + TEXT ( " .csv " ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " Dumping primitive allocator stats trace to: '%s' " ) , * FileName ) ;
FArchive * FileToLogTo = IFileManager : : Get ( ) . CreateFileWriter ( * FileName , false ) ;
ensure ( FileToLogTo ) ;
if ( FileToLogTo )
{
static const FString StatNames [ StatStride ] =
{
TEXT ( " NumPrimitives " ) ,
TEXT ( " MaxPersistent " ) ,
TEXT ( " NumPersistentAllocated " ) ,
TEXT ( " PersistentFreeListSizeBC " ) ,
TEXT ( " PersistentPendingListSizeBC " ) ,
TEXT ( " PersistentFreeListSize " ) ,
TEXT ( " MaxInstances " ) ,
TEXT ( " NumInstancesAllocated " ) ,
} ;
// Print header
FString StringToPrint ;
for ( int32 Index = 0 ; Index < StatStride ; + + Index )
{
if ( ! StringToPrint . IsEmpty ( ) )
{
StringToPrint + = TEXT ( " , " ) ;
}
if ( Index < int32 ( UE_ARRAY_COUNT ( StatNames ) ) )
{
StringToPrint . Append ( StatNames [ Index ] ) ;
}
else
{
StringToPrint . Appendf ( TEXT ( " Stat_%d " ) , Index ) ;
}
}
StringToPrint + = TEXT ( " \n " ) ;
FileToLogTo - > Serialize ( TCHAR_TO_ANSI ( * StringToPrint ) , StringToPrint . Len ( ) ) ;
int32 Num = GPrimitiveAllocatorStats . Num ( ) / StatStride ;
for ( int32 Ind = 0 ; Ind < Num ; + + Ind )
{
StringToPrint . Empty ( ) ;
for ( int32 StatInd = 0 ; StatInd < StatStride ; + + StatInd )
{
if ( ! StringToPrint . IsEmpty ( ) )
{
StringToPrint . Append ( TEXT ( " , " ) ) ;
}
StringToPrint . Appendf ( TEXT ( " %d " ) , GPrimitiveAllocatorStats [ Ind * StatStride + StatInd ] ) ;
}
StringToPrint + = TEXT ( " \n " ) ;
FileToLogTo - > Serialize ( TCHAR_TO_ANSI ( * StringToPrint ) , StringToPrint . Len ( ) ) ;
}
FileToLogTo - > Close ( ) ;
}
}
}
void UpdatePrimitiveAllocatorStats ( int32 NumPrimitives , int32 MaxPersistent , int32 NumPersistentAllocated , int32 PersistemFreeListSize , int32 PersistemFreeListSizeBC , int32 PersistentPendingListSizeBC , int32 MaxInstances , int32 NumInstancesAllocated )
{
// Dump and clear when turning off the cvar
if ( GDumpPrimitiveAllocatorStatsToCSV = = 0 & & ! GPrimitiveAllocatorStats . IsEmpty ( ) )
{
DumpPrimitiveAllocatorStats ( ) ;
GPrimitiveAllocatorStats . Empty ( ) ;
}
// Dump snapshot (and don't clear or stop) when cvar is set to 2
if ( GDumpPrimitiveAllocatorStatsToCSV = = 2 & & ! GPrimitiveAllocatorStats . IsEmpty ( ) )
{
DumpPrimitiveAllocatorStats ( ) ;
GDumpPrimitiveAllocatorStatsToCSV = 1 ;
}
if ( GDumpPrimitiveAllocatorStatsToCSV ! = 0 )
{
GPrimitiveAllocatorStats . Add ( NumPrimitives ) ;
GPrimitiveAllocatorStats . Add ( MaxPersistent ) ;
GPrimitiveAllocatorStats . Add ( NumPersistentAllocated ) ;
GPrimitiveAllocatorStats . Add ( PersistemFreeListSizeBC ) ;
GPrimitiveAllocatorStats . Add ( PersistentPendingListSizeBC ) ;
GPrimitiveAllocatorStats . Add ( PersistemFreeListSize ) ;
GPrimitiveAllocatorStats . Add ( MaxInstances ) ;
GPrimitiveAllocatorStats . Add ( NumInstancesAllocated ) ;
}
}
# endif // ENABLE_LOG_PRIMITIVE_INSTANCE_ID_STATS_TO_CSV
/**
* Holds the info to update SpeedTree wind per unique tree object in the scene , instead of per instance
*/
struct FSpeedTreeWindComputation
{
explicit FSpeedTreeWindComputation ( ) :
ReferenceCount ( 1 )
{
}
/** SpeedTree wind object */
FSpeedTreeWind Wind ;
/** Uniform buffer shared between trees of the same type. */
TUniformBufferRef < FSpeedTreeUniformParameters > UniformBuffer ;
int32 ReferenceCount ;
} ;
FPersistentSkyAtmosphereData : : FPersistentSkyAtmosphereData ( )
: bInitialised ( false )
, CameraAerialPerspectiveVolumeIndex ( 0 )
{
}
void FPersistentSkyAtmosphereData : : InitialiseOrNextFrame ( ERHIFeatureLevel : : Type FeatureLevel , FPooledRenderTargetDesc & AerialPerspectiveDesc , FRHICommandListImmediate & RHICmdList )
{
if ( ! bInitialised )
{
CameraAerialPerspectiveVolumeCount = FeatureLevel = = ERHIFeatureLevel : : ES3_1 ? 2 : 1 ;
for ( int i = 0 ; i < CameraAerialPerspectiveVolumeCount ; + + i )
{
GRenderTargetPool . FindFreeElement ( RHICmdList , AerialPerspectiveDesc , CameraAerialPerspectiveVolumes [ i ] ,
i = = 0 ? TEXT ( " SkyAtmosphere.CameraAPVolume0 " ) : TEXT ( " SkyAtmosphere.CameraAPVolume1 " ) ) ;
}
bInitialised = true ;
}
CameraAerialPerspectiveVolumeIndex = ( CameraAerialPerspectiveVolumeIndex + 1 ) % CameraAerialPerspectiveVolumeCount ;
}
TRefCountPtr < IPooledRenderTarget > FPersistentSkyAtmosphereData : : GetCurrentCameraAerialPerspectiveVolume ( )
{
check ( CameraAerialPerspectiveVolumes [ CameraAerialPerspectiveVolumeIndex ] . IsValid ( ) ) ;
return CameraAerialPerspectiveVolumes [ CameraAerialPerspectiveVolumeIndex ] ;
}
/** Default constructor. */
FSceneViewState : : FSceneViewState ( ERHIFeatureLevel : : Type FeatureLevel )
: OcclusionQueryPool ( RHICreateRenderQueryPool ( RQT_Occlusion ) )
{
// Set FeatureLevel to a valid value, so we get Init/ReleaseDynamicRHI calls on FeatureLevel changes
SetFeatureLevel ( FeatureLevel ) ;
UniqueID = FSceneViewState_UniqueID . Increment ( ) ;
Scene = nullptr ;
OcclusionFrameCounter = 0 ;
LastRenderTime = - FLT_MAX ;
LastRenderTimeDelta = 0.0f ;
MotionBlurTimeScale = 1.0f ;
MotionBlurTargetDeltaTime = 1.0f / 60.0f ; // Start with a reasonable default of 60hz.
PrevViewMatrixForOcclusionQuery . SetIdentity ( ) ;
PrevViewOriginForOcclusionQuery = FVector : : ZeroVector ;
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
bIsFreezing = false ;
bIsFrozen = false ;
bIsFrozenViewMatricesCached = false ;
# endif
// Register this object as a resource, so it will receive device reset notifications.
if ( IsInGameThread ( ) )
{
BeginInitResource ( this ) ;
}
else
{
InitResource ( ) ;
}
CachedVisibilityChunk = NULL ;
CachedVisibilityHandlerId = INDEX_NONE ;
CachedVisibilityBucketIndex = INDEX_NONE ;
CachedVisibilityChunkIndex = INDEX_NONE ;
MIDUsedCount = 0 ;
TemporalAASampleIndex = 0 ;
FrameIndex = 0 ;
DistanceFieldTemporalSampleIndex = 0 ;
bDOFHistory = true ;
bDOFHistory2 = true ;
// Sets the mipbias to invalid large number.
MaterialTextureCachedMipBias = BIG_NUMBER ;
SequencerState = ESS_None ;
bIsStereoView = false ;
bRoundRobinOcclusionEnabled = false ;
for ( int32 CascadeIndex = 0 ; CascadeIndex < UE_ARRAY_COUNT ( TranslucencyLightingCacheAllocations ) ; CascadeIndex + + )
{
TranslucencyLightingCacheAllocations [ CascadeIndex ] = NULL ;
}
bInitializedGlobalDistanceFieldOrigins = false ;
GlobalDistanceFieldUpdateIndex = 0 ;
GlobalDistanceFieldCameraVelocityOffset = FVector : : ZeroVector ;
ShadowOcclusionQueryMaps . Empty ( FOcclusionQueryHelpers : : MaxBufferedOcclusionFrames ) ;
ShadowOcclusionQueryMaps . AddZeroed ( FOcclusionQueryHelpers : : MaxBufferedOcclusionFrames ) ;
LastAutoDownsampleChangeTime = 0 ;
SmoothedHalfResTranslucencyGPUDuration = 0 ;
SmoothedFullResTranslucencyGPUDuration = 0 ;
bShouldAutoDownsampleTranslucency = false ;
PreExposure = 1.f ;
bUpdateLastExposure = false ;
# if RHI_RAYTRACING
GatherPointsBuffer = nullptr ;
GatherPointsResolution = FIntVector ( 0 , 0 , 0 ) ;
# endif
ViewVirtualShadowMapCache = nullptr ;
}
void DestroyRenderResource ( FRenderResource * RenderResource )
{
if ( RenderResource )
{
ENQUEUE_RENDER_COMMAND ( DestroySceneViewStateRenderResource ) (
[ RenderResource ] ( FRHICommandList & )
{
RenderResource - > ReleaseResource ( ) ;
delete RenderResource ;
} ) ;
}
}
void DestroyRWBuffer ( FRWBuffer * RWBuffer )
{
ENQUEUE_RENDER_COMMAND ( DestroyRWBuffer ) (
[ RWBuffer ] ( FRHICommandList & )
{
delete RWBuffer ;
} ) ;
}
FSceneViewState : : ~ FSceneViewState ( )
{
CachedVisibilityChunk = NULL ;
ShadowOcclusionQueryMaps . Reset ( ) ;
for ( int32 CascadeIndex = 0 ; CascadeIndex < UE_ARRAY_COUNT ( TranslucencyLightingCacheAllocations ) ; CascadeIndex + + )
{
delete TranslucencyLightingCacheAllocations [ CascadeIndex ] ;
}
HairStrandsViewStateData . Release ( ) ;
ShaderPrintStateData . Release ( ) ;
if ( ViewVirtualShadowMapCache )
{
delete ViewVirtualShadowMapCache ;
ViewVirtualShadowMapCache = nullptr ;
}
if ( Scene )
{
Scene - > RemoveViewState ( this ) ;
}
}
FSceneViewStateInterface * FScene : : AllocateViewState ( )
{
FSceneViewState * Result = new FSceneViewState ( FeatureLevel ) ;
Result - > Scene = this ;
ViewStates . Add ( Result ) ;
return Result ;
}
void FScene : : RemoveViewState ( FSceneViewStateInterface * ViewState )
{
for ( int32 ViewStateIndex = 0 ; ViewStateIndex < ViewStates . Num ( ) ; ViewStateIndex + + )
{
if ( ViewStates [ ViewStateIndex ] = = ViewState )
{
ViewStates . RemoveAt ( ViewStateIndex ) ;
break ;
}
}
}
# if WITH_EDITOR
FPixelInspectorData : : FPixelInspectorData ( )
{
for ( int32 i = 0 ; i < 2 ; + + i )
{
RenderTargetBufferFinalColor [ i ] = nullptr ;
RenderTargetBufferDepth [ i ] = nullptr ;
RenderTargetBufferSceneColor [ i ] = nullptr ;
RenderTargetBufferHDR [ i ] = nullptr ;
RenderTargetBufferA [ i ] = nullptr ;
RenderTargetBufferBCDEF [ i ] = nullptr ;
}
}
void FPixelInspectorData : : InitializeBuffers ( FRenderTarget * BufferFinalColor , FRenderTarget * BufferSceneColor , FRenderTarget * BufferDepth , FRenderTarget * BufferHDR , FRenderTarget * BufferA , FRenderTarget * BufferBCDEF , int32 BufferIndex )
{
RenderTargetBufferFinalColor [ BufferIndex ] = BufferFinalColor ;
RenderTargetBufferDepth [ BufferIndex ] = BufferDepth ;
RenderTargetBufferSceneColor [ BufferIndex ] = BufferSceneColor ;
RenderTargetBufferHDR [ BufferIndex ] = BufferHDR ;
RenderTargetBufferA [ BufferIndex ] = BufferA ;
RenderTargetBufferBCDEF [ BufferIndex ] = BufferBCDEF ;
check ( RenderTargetBufferBCDEF [ BufferIndex ] ! = nullptr ) ;
FIntPoint BufferSize = RenderTargetBufferBCDEF [ BufferIndex ] - > GetSizeXY ( ) ;
check ( BufferSize . X = = 4 & & BufferSize . Y = = 1 ) ;
if ( RenderTargetBufferA [ BufferIndex ] ! = nullptr )
{
BufferSize = RenderTargetBufferA [ BufferIndex ] - > GetSizeXY ( ) ;
check ( BufferSize . X = = 1 & & BufferSize . Y = = 1 ) ;
}
if ( RenderTargetBufferFinalColor [ BufferIndex ] ! = nullptr )
{
BufferSize = RenderTargetBufferFinalColor [ BufferIndex ] - > GetSizeXY ( ) ;
//The Final color grab an area and can change depending on the setup
//It should at least contain 1 pixel but can be 3x3 or more
check ( BufferSize . X > 0 & & BufferSize . Y > 0 ) ;
}
if ( RenderTargetBufferDepth [ BufferIndex ] ! = nullptr )
{
BufferSize = RenderTargetBufferDepth [ BufferIndex ] - > GetSizeXY ( ) ;
check ( BufferSize . X = = 1 & & BufferSize . Y = = 1 ) ;
}
if ( RenderTargetBufferSceneColor [ BufferIndex ] ! = nullptr )
{
BufferSize = RenderTargetBufferSceneColor [ BufferIndex ] - > GetSizeXY ( ) ;
check ( BufferSize . X = = 1 & & BufferSize . Y = = 1 ) ;
}
if ( RenderTargetBufferHDR [ BufferIndex ] ! = nullptr )
{
BufferSize = RenderTargetBufferHDR [ BufferIndex ] - > GetSizeXY ( ) ;
check ( BufferSize . X = = 1 & & BufferSize . Y = = 1 ) ;
}
}
bool FPixelInspectorData : : AddPixelInspectorRequest ( FPixelInspectorRequest * PixelInspectorRequest )
{
if ( PixelInspectorRequest = = nullptr )
return false ;
FVector2f ViewportUV = PixelInspectorRequest - > SourceViewportUV ;
if ( Requests . Contains ( ViewportUV ) )
return false ;
//Remove the oldest request since the new request use the buffer
if ( Requests . Num ( ) > 1 )
{
FVector2f FirstKey ( - 1 , - 1 ) ;
for ( auto kvp : Requests )
{
FirstKey = kvp . Key ;
break ;
}
if ( Requests . Contains ( FirstKey ) )
{
Requests . Remove ( FirstKey ) ;
}
}
Requests . Add ( ViewportUV , PixelInspectorRequest ) ;
return true ;
}
# endif //WITH_EDITOR
FDistanceFieldSceneData : : FDistanceFieldSceneData ( EShaderPlatform ShaderPlatform )
: NumObjectsInBuffer ( 0 )
, NumHeightFieldObjectsInBuffer ( 0 )
, IndirectionAtlasLayout ( 8 , 8 , 8 , 512 , 512 , 512 , false , true , false )
, HeightFieldAtlasGeneration ( 0 )
, HFVisibilityAtlasGenerattion ( 0 )
{
ObjectBuffers = nullptr ;
HeightFieldObjectBuffers = nullptr ;
HeightFieldObjectBuffers = nullptr ;
bTrackAllPrimitives = ShouldAllPrimitivesHaveDistanceField ( ShaderPlatform ) ;
bCanUse16BitObjectIndices = RHISupportsBufferLoadTypeConversion ( ShaderPlatform ) ;
StreamingRequestReadbackBuffers . AddZeroed ( MaxStreamingReadbackBuffers ) ;
}
FDistanceFieldSceneData : : ~ FDistanceFieldSceneData ( )
{
delete ObjectBuffers ;
}
bool IncludePrimitiveInDistanceFieldSceneData ( bool bTrackAllPrimitives , const FPrimitiveSceneProxy * Proxy )
{
return PrimitiveNeedsDistanceFieldSceneData (
bTrackAllPrimitives ,
Proxy - > CastsDynamicIndirectShadow ( ) ,
Proxy - > AffectsDistanceFieldLighting ( ) ,
Proxy - > IsDrawnInGame ( ) ,
Proxy - > CastsHiddenShadow ( ) ,
Proxy - > CastsDynamicShadow ( ) ,
Proxy - > AffectsDynamicIndirectLighting ( ) ) ;
}
void FDistanceFieldSceneData : : AddPrimitive ( FPrimitiveSceneInfo * InPrimitive )
{
FPrimitiveSceneProxy * Proxy = InPrimitive - > Proxy ;
if ( IncludePrimitiveInDistanceFieldSceneData ( bTrackAllPrimitives , Proxy ) )
{
if ( Proxy - > SupportsHeightfieldRepresentation ( ) )
{
UTexture2D * HeightAndNormal ;
UTexture2D * DiffuseColor ;
UTexture2D * Visibility ;
FHeightfieldComponentDescription Desc ( FMatrix : : Identity ) ;
Proxy - > GetHeightfieldRepresentation ( HeightAndNormal , DiffuseColor , Visibility , Desc ) ;
GHeightFieldTextureAtlas . AddAllocation ( HeightAndNormal ) ;
if ( Visibility )
{
check ( Desc . VisibilityChannel > = 0 & & Desc . VisibilityChannel < 4 ) ;
GHFVisibilityTextureAtlas . AddAllocation ( Visibility , Desc . VisibilityChannel ) ;
}
checkSlow ( ! PendingHeightFieldAddOps . Contains ( InPrimitive ) ) ;
checkSlow ( ! PendingHeightFieldUpdateOps . Contains ( InPrimitive ) ) ;
PendingHeightFieldAddOps . Add ( InPrimitive ) ;
}
if ( Proxy - > SupportsDistanceFieldRepresentation ( ) )
{
checkSlow ( ! PendingAddOperations . Contains ( InPrimitive ) ) ;
checkSlow ( ! PendingUpdateOperations . Contains ( InPrimitive ) ) ;
PendingAddOperations . Add ( InPrimitive ) ;
}
}
}
void FDistanceFieldSceneData : : UpdatePrimitive ( FPrimitiveSceneInfo * InPrimitive )
{
const FPrimitiveSceneProxy * Proxy = InPrimitive - > Proxy ;
if ( IncludePrimitiveInDistanceFieldSceneData ( bTrackAllPrimitives , Proxy )
& & Proxy - > SupportsDistanceFieldRepresentation ( )
& & ! PendingAddOperations . Contains ( InPrimitive )
// This is needed to prevent infinite buildup when DF features are off such that the pending operations don't get consumed
& & ! PendingUpdateOperations . Contains ( InPrimitive )
// This can happen when the primitive fails to allocate from the SDF atlas
& & InPrimitive - > DistanceFieldInstanceIndices . Num ( ) > 0 )
{
PendingUpdateOperations . Add ( InPrimitive ) ;
}
}
void FDistanceFieldSceneData : : RemovePrimitive ( FPrimitiveSceneInfo * InPrimitive )
{
FPrimitiveSceneProxy * Proxy = InPrimitive - > Proxy ;
if ( IncludePrimitiveInDistanceFieldSceneData ( bTrackAllPrimitives , Proxy ) )
{
if ( Proxy - > SupportsDistanceFieldRepresentation ( ) )
{
PendingAddOperations . Remove ( InPrimitive ) ;
PendingUpdateOperations . Remove ( InPrimitive ) ;
PendingThrottledOperations . Remove ( InPrimitive ) ;
if ( InPrimitive - > DistanceFieldInstanceIndices . Num ( ) > 0 )
{
PendingRemoveOperations . Add ( FPrimitiveRemoveInfo ( InPrimitive ) ) ;
}
InPrimitive - > DistanceFieldInstanceIndices . Empty ( ) ;
}
if ( Proxy - > SupportsHeightfieldRepresentation ( ) )
{
UTexture2D * HeightAndNormal ;
UTexture2D * DiffuseColor ;
UTexture2D * Visibility ;
FHeightfieldComponentDescription Desc ( FMatrix : : Identity ) ;
Proxy - > GetHeightfieldRepresentation ( HeightAndNormal , DiffuseColor , Visibility , Desc ) ;
GHeightFieldTextureAtlas . RemoveAllocation ( HeightAndNormal ) ;
if ( Visibility )
{
GHFVisibilityTextureAtlas . RemoveAllocation ( Visibility ) ;
}
PendingHeightFieldAddOps . Remove ( InPrimitive ) ;
PendingHeightFieldUpdateOps . Remove ( InPrimitive ) ;
if ( InPrimitive - > DistanceFieldInstanceIndices . Num ( ) > 0 )
{
PendingHeightFieldRemoveOps . Add ( FHeightFieldPrimitiveRemoveInfo ( InPrimitive ) ) ;
}
InPrimitive - > DistanceFieldInstanceIndices . Empty ( ) ;
}
}
checkf ( ! PendingAddOperations . Contains ( InPrimitive ) , TEXT ( " Primitive is being removed from the scene, but didn't remove from Distance Field Scene properly - a crash will occur when processing PendingAddOperations. This can happen if the proxy's properties have changed without recreating its render state. " ) ) ;
checkf ( ! PendingUpdateOperations . Contains ( InPrimitive ) , TEXT ( " Primitive is being removed from the scene, but didn't remove from Distance Field Scene properly - a crash will occur when processing PendingUpdateOperations. This can happen if the proxy's properties have changed without recreating its render state. " ) ) ;
checkf ( ! PendingThrottledOperations . Contains ( InPrimitive ) , TEXT ( " Primitive is being removed from the scene, but didn't remove from Distance Field Scene properly - a crash will occur when processing PendingThrottledOperations. This can happen if the proxy's properties have changed without recreating its render state. " ) ) ;
checkf ( ! PendingHeightFieldAddOps . Contains ( InPrimitive ) , TEXT ( " Primitive is being removed from the scene, but didn't remove from Distance Field Scene properly - a crash will occur when processing PendingHeightFieldAddOps. This can happen if the proxy's properties have changed without recreating its render state. " ) ) ;
checkf ( ! PendingHeightFieldUpdateOps . Contains ( InPrimitive ) , TEXT ( " Primitive is being removed from the scene, but didn't remove from Distance Field Scene properly - a crash will occur when processing PendingHeightFieldUpdateOps. This can happen if the proxy's properties have changed without recreating its render state. " ) ) ;
}
void FDistanceFieldSceneData : : Release ( )
{
if ( ObjectBuffers ! = nullptr )
{
ObjectBuffers - > Release ( ) ;
}
for ( int32 BufferIndex = 0 ; BufferIndex < StreamingRequestReadbackBuffers . Num ( ) ; + + BufferIndex )
{
if ( StreamingRequestReadbackBuffers [ BufferIndex ] )
{
delete StreamingRequestReadbackBuffers [ BufferIndex ] ;
StreamingRequestReadbackBuffers [ BufferIndex ] = nullptr ;
}
}
}
void FDistanceFieldSceneData : : VerifyIntegrity ( )
{
# if DO_CHECK
check ( NumObjectsInBuffer = = PrimitiveInstanceMapping . Num ( ) ) ;
for ( int32 PrimitiveInstanceIndex = 0 ; PrimitiveInstanceIndex < PrimitiveInstanceMapping . Num ( ) ; PrimitiveInstanceIndex + + )
{
const FPrimitiveAndInstance & PrimitiveAndInstance = PrimitiveInstanceMapping [ PrimitiveInstanceIndex ] ;
check ( PrimitiveAndInstance . Primitive & & PrimitiveAndInstance . Primitive - > DistanceFieldInstanceIndices . Num ( ) > 0 ) ;
check ( PrimitiveAndInstance . Primitive - > DistanceFieldInstanceIndices . IsValidIndex ( PrimitiveAndInstance . InstanceIndex ) ) ;
const int32 InstanceIndex = PrimitiveAndInstance . Primitive - > DistanceFieldInstanceIndices [ PrimitiveAndInstance . InstanceIndex ] ;
check ( InstanceIndex = = PrimitiveInstanceIndex | | InstanceIndex = = - 1 ) ;
}
# endif
}
void FScene : : UpdateSceneSettings ( AWorldSettings * WorldSettings )
{
FScene * Scene = this ;
float InDefaultMaxDistanceFieldOcclusionDistance = WorldSettings - > DefaultMaxDistanceFieldOcclusionDistance ;
float InGlobalDistanceFieldViewDistance = WorldSettings - > GlobalDistanceFieldViewDistance ;
float InDynamicIndirectShadowsSelfShadowingIntensity = FMath : : Clamp ( WorldSettings - > DynamicIndirectShadowsSelfShadowingIntensity , 0.0f , 1.0f ) ;
ENQUEUE_RENDER_COMMAND ( UpdateSceneSettings ) (
[ Scene , InDefaultMaxDistanceFieldOcclusionDistance , InGlobalDistanceFieldViewDistance , InDynamicIndirectShadowsSelfShadowingIntensity ] ( FRHICommandList & RHICmdList )
{
Scene - > DefaultMaxDistanceFieldOcclusionDistance = InDefaultMaxDistanceFieldOcclusionDistance ;
Scene - > GlobalDistanceFieldViewDistance = InGlobalDistanceFieldViewDistance ;
Scene - > DynamicIndirectShadowsSelfShadowingIntensity = InDynamicIndirectShadowsSelfShadowingIntensity ;
} ) ;
}
/**
* Sets the FX system associated with the scene .
*/
void FScene : : SetFXSystem ( class FFXSystemInterface * InFXSystem )
{
FXSystem = InFXSystem ;
}
/**
* Get the FX system associated with the scene .
*/
FFXSystemInterface * FScene : : GetFXSystem ( )
{
return FXSystem ;
}
void FSceneViewState : : AddVirtualShadowMapCache ( )
{
// Can only allocate a virtual shadow map cache if we have a scene pointer available
if ( Scene )
{
// Don't allocate if one already exists
if ( ViewVirtualShadowMapCache = = nullptr )
{
FVirtualShadowMapArrayCacheManager * ViewCache = new FVirtualShadowMapArrayCacheManager ( Scene ) ;
// Need to add reference to virtual shadow map cache in render thread
ENQUEUE_RENDER_COMMAND ( LinkVirtualShadowMapCache ) (
[ this , ViewCache ] ( FRHICommandListImmediate & RHICmdList )
{
this - > ViewVirtualShadowMapCache = ViewCache ;
} ) ;
} //-V773
}
}
FVirtualShadowMapArrayCacheManager * FSceneViewState : : GetVirtualShadowMapCache ( ) const
{
return ViewVirtualShadowMapCache ;
}
FVirtualShadowMapArrayCacheManager * FScene : : GetVirtualShadowMapCache ( FSceneView & View ) const
{
FVirtualShadowMapArrayCacheManager * Result = DefaultVirtualShadowMapCache ;
if ( View . State )
{
FVirtualShadowMapArrayCacheManager * ViewCache = View . State - > GetVirtualShadowMapCache ( ) ;
if ( ViewCache )
{
Result = ViewCache ;
}
}
return Result ;
}
void FScene : : GetAllVirtualShadowMapCacheManagers ( TArray < FVirtualShadowMapArrayCacheManager * , SceneRenderingAllocator > & OutCacheManagers ) const
{
OutCacheManagers . Empty ( ) ;
if ( DefaultVirtualShadowMapCache )
{
OutCacheManagers . Add ( DefaultVirtualShadowMapCache ) ;
}
for ( const FSceneViewState * ViewState : ViewStates )
{
if ( ViewState - > ViewVirtualShadowMapCache )
{
OutCacheManagers . Add ( ViewState - > ViewVirtualShadowMapCache ) ;
}
}
}
void FScene : : UpdateParameterCollections ( const TArray < FMaterialParameterCollectionInstanceResource * > & InParameterCollections )
{
ENQUEUE_RENDER_COMMAND ( UpdateParameterCollectionsCommand ) (
[ this , InParameterCollections ] ( FRHICommandList & )
{
// Empty the scene's map so any unused uniform buffers will be released
ParameterCollections . Empty ( ) ;
// Add each existing parameter collection id and its uniform buffer
for ( int32 CollectionIndex = 0 ; CollectionIndex < InParameterCollections . Num ( ) ; CollectionIndex + + )
{
FMaterialParameterCollectionInstanceResource * InstanceResource = InParameterCollections [ CollectionIndex ] ;
ParameterCollections . Add ( InstanceResource - > GetId ( ) , InstanceResource - > GetUniformBuffer ( ) ) ;
}
} ) ;
}
bool FScene : : RequestGPUSceneUpdate ( FPrimitiveSceneInfo & PrimitiveSceneInfo , EPrimitiveDirtyState PrimitiveDirtyState )
{
return PrimitiveSceneInfo . RequestGPUSceneUpdate ( PrimitiveDirtyState ) ;
}
SIZE_T FScene : : GetSizeBytes ( ) const
{
return sizeof ( * this )
+ Primitives . GetAllocatedSize ( )
+ Lights . GetAllocatedSize ( )
+ StaticMeshes . GetAllocatedSize ( )
+ ExponentialFogs . GetAllocatedSize ( )
+ WindSources . GetAllocatedSize ( )
+ SpeedTreeVertexFactoryMap . GetAllocatedSize ( )
+ SpeedTreeWindComputationMap . GetAllocatedSize ( )
+ LocalShadowCastingLightOctree . GetSizeBytes ( )
+ PrimitiveOctree . GetSizeBytes ( ) ;
}
void FScene : : OnWorldCleanup ( )
{
UniformBuffers . Clear ( ) ;
}
void FScene : : CheckPrimitiveArrays ( int MaxTypeOffsetIndex )
{
check ( Primitives . Num ( ) = = PrimitiveTransforms . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveSceneProxies . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveBounds . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveFlagsCompact . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveVisibilityIds . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveOctreeIndex . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveOcclusionFlags . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveComponentIds . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveVirtualTextureFlags . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveVirtualTextureLod . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveOcclusionBounds . Num ( ) ) ;
# if WITH_EDITOR
check ( Primitives . Num ( ) = = PrimitivesSelected . Num ( ) ) ;
# endif
# if RHI_RAYTRACING
check ( Primitives . Num ( ) = = PrimitiveRayTracingFlags . Num ( ) ) ;
check ( Primitives . Num ( ) = = PrimitiveRayTracingGroupIds . Num ( ) ) ;
# endif
check ( Primitives . Num ( ) = = PrimitivesNeedingStaticMeshUpdate . Num ( ) ) ;
# if UE_BUILD_DEBUG
MaxTypeOffsetIndex = MaxTypeOffsetIndex = = - 1 ? TypeOffsetTable . Num ( ) : MaxTypeOffsetIndex ;
for ( int i = 0 ; i < MaxTypeOffsetIndex ; i + + )
{
for ( int j = i + 1 ; j < MaxTypeOffsetIndex ; j + + )
{
check ( TypeOffsetTable [ i ] . PrimitiveSceneProxyType ! = TypeOffsetTable [ j ] . PrimitiveSceneProxyType ) ;
check ( TypeOffsetTable [ i ] . Offset < = TypeOffsetTable [ j ] . Offset ) ;
}
}
uint32 NextOffset = 0 ;
for ( int i = 0 ; i < MaxTypeOffsetIndex ; i + + )
{
const FTypeOffsetTableEntry & Entry = TypeOffsetTable [ i ] ;
for ( uint32 Index = NextOffset ; Index < Entry . Offset ; Index + + )
{
checkSlow ( Primitives [ Index ] - > Proxy = = PrimitiveSceneProxies [ Index ] ) ;
SIZE_T TypeHash = PrimitiveSceneProxies [ Index ] - > GetTypeHash ( ) ;
checkfSlow ( TypeHash = = Entry . PrimitiveSceneProxyType , TEXT ( " TypeHash: %i not matching, expected: %i " ) , TypeHash , Entry . PrimitiveSceneProxyType ) ;
}
NextOffset = Entry . Offset ;
}
# endif
}
static TAutoConsoleVariable < int32 > CVarDoLazyStaticMeshUpdate (
TEXT ( " r.DoLazyStaticMeshUpdate " ) ,
0 ,
TEXT ( " If true, then do not add meshes to the static mesh draw lists until they are visible. Experiemental option. " ) ) ;
static void DoLazyStaticMeshUpdateCVarSinkFunction ( )
{
if ( ! GIsRunning | | GIsEditor | | ! FApp : : CanEverRender ( ) )
{
return ;
}
static bool CachedDoLazyStaticMeshUpdate = ! ! CVarDoLazyStaticMeshUpdate . GetValueOnGameThread ( ) ;
bool DoLazyStaticMeshUpdate = ! ! CVarDoLazyStaticMeshUpdate . GetValueOnGameThread ( ) ;
if ( DoLazyStaticMeshUpdate ! = CachedDoLazyStaticMeshUpdate )
{
CachedDoLazyStaticMeshUpdate = DoLazyStaticMeshUpdate ;
for ( TObjectIterator < UWorld > It ; It ; + + It )
{
UWorld * World = * It ;
if ( World & & World - > Scene )
{
FScene * Scene = ( FScene * ) ( World - > Scene ) ;
ENQUEUE_RENDER_COMMAND ( UpdateDoLazyStaticMeshUpdate ) (
[ Scene ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > UpdateDoLazyStaticMeshUpdate ( RHICmdList ) ;
} ) ;
}
}
}
}
static FAutoConsoleVariableSink CVarDoLazyStaticMeshUpdateSink ( FConsoleCommandDelegate : : CreateStatic ( & DoLazyStaticMeshUpdateCVarSinkFunction ) ) ;
static void UpdateEarlyZPassModeCVarSinkFunction ( )
{
static auto * CVarAntiAliasingMethod = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.AntiAliasingMethod " ) ) ;
static int32 CachedAntiAliasingMethod = CVarAntiAliasingMethod - > GetValueOnGameThread ( ) ;
static int32 CachedEarlyZPass = CVarEarlyZPass . GetValueOnGameThread ( ) ;
static int32 CachedBasePassWriteDepthEvenWithFullPrepass = CVarBasePassWriteDepthEvenWithFullPrepass . GetValueOnGameThread ( ) ;
static int32 CachedMobileEarlyZPass = CVarMobileEarlyZPass . GetValueOnGameThread ( ) ;
const int32 AntiAliasingMethod = CVarAntiAliasingMethod - > GetValueOnGameThread ( ) ;
const int32 EarlyZPass = CVarEarlyZPass . GetValueOnGameThread ( ) ;
const int32 BasePassWriteDepthEvenWithFullPrepass = CVarBasePassWriteDepthEvenWithFullPrepass . GetValueOnGameThread ( ) ;
const int32 MobileEarlyZPass = CVarMobileEarlyZPass . GetValueOnGameThread ( ) ;
// Switching between MSAA and another AA in forward shading mode requires EarlyZPassMode to update.
if ( AntiAliasingMethod ! = CachedAntiAliasingMethod
| | EarlyZPass ! = CachedEarlyZPass
| | BasePassWriteDepthEvenWithFullPrepass ! = CachedBasePassWriteDepthEvenWithFullPrepass
| | MobileEarlyZPass ! = CachedMobileEarlyZPass )
{
for ( TObjectIterator < UWorld > It ; It ; + + It )
{
UWorld * World = * It ;
if ( World & & World - > Scene )
{
FScene * Scene = ( FScene * ) ( World - > Scene ) ;
Scene - > UpdateEarlyZPassMode ( ) ;
}
}
CachedAntiAliasingMethod = AntiAliasingMethod ;
CachedEarlyZPass = EarlyZPass ;
CachedBasePassWriteDepthEvenWithFullPrepass = BasePassWriteDepthEvenWithFullPrepass ;
CachedMobileEarlyZPass = MobileEarlyZPass ;
}
}
static FAutoConsoleVariableSink CVarUpdateEarlyZPassModeSink ( FConsoleCommandDelegate : : CreateStatic ( & UpdateEarlyZPassModeCVarSinkFunction ) ) ;
void FScene : : UpdateDoLazyStaticMeshUpdate ( FRHICommandListImmediate & CmdList )
{
bool DoLazyStaticMeshUpdate = CVarDoLazyStaticMeshUpdate . GetValueOnRenderThread ( ) & & ! GIsEditor & & FApp : : CanEverRender ( ) ;
FPrimitiveSceneInfo : : UpdateStaticMeshes ( CmdList , this , Primitives , EUpdateStaticMeshFlags : : AllCommands , ! DoLazyStaticMeshUpdate ) ;
}
void FScene : : DumpMeshDrawCommandMemoryStats ( )
{
SIZE_T TotalCachedMeshDrawCommands = 0 ;
SIZE_T TotalStaticMeshCommandInfos = 0 ;
struct FPassStats
{
SIZE_T CachedMeshDrawCommandBytes = 0 ;
SIZE_T PSOBytes = 0 ;
SIZE_T ShaderBindingInlineBytes = 0 ;
SIZE_T ShaderBindingHeapBytes = 0 ;
SIZE_T VertexStreamsInlineBytes = 0 ;
SIZE_T DebugDataBytes = 0 ;
SIZE_T DrawCommandParameterBytes = 0 ;
uint32 NumCommands = 0 ;
} ;
FPassStats AllPassStats [ EMeshPass : : Num ] ;
TArray < bool > StateBucketAccounted [ EMeshPass : : Num ] ;
for ( int32 PassIndex = 0 ; PassIndex < EMeshPass : : Num ; PassIndex + + )
{
StateBucketAccounted [ PassIndex ] . Empty ( CachedMeshDrawCommandStateBuckets [ PassIndex ] . GetMaxIndex ( ) ) ;
StateBucketAccounted [ PassIndex ] . AddZeroed ( CachedMeshDrawCommandStateBuckets [ PassIndex ] . GetMaxIndex ( ) ) ;
}
for ( int32 i = 0 ; i < Primitives . Num ( ) ; i + + )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = Primitives [ i ] ;
TotalStaticMeshCommandInfos + = PrimitiveSceneInfo - > StaticMeshCommandInfos . GetAllocatedSize ( ) ;
for ( int32 CommandIndex = 0 ; CommandIndex < PrimitiveSceneInfo - > StaticMeshCommandInfos . Num ( ) ; + + CommandIndex )
{
const FCachedMeshDrawCommandInfo & CachedCommand = PrimitiveSceneInfo - > StaticMeshCommandInfos [ CommandIndex ] ;
int PassIndex = CachedCommand . MeshPass ;
const FMeshDrawCommand * MeshDrawCommandPtr = nullptr ;
if ( CachedCommand . StateBucketId ! = INDEX_NONE )
{
if ( ! StateBucketAccounted [ PassIndex ] [ CachedCommand . StateBucketId ] )
{
StateBucketAccounted [ PassIndex ] [ CachedCommand . StateBucketId ] = true ;
MeshDrawCommandPtr = & CachedMeshDrawCommandStateBuckets [ PassIndex ] . GetByElementId ( CachedCommand . StateBucketId ) . Key ;
}
}
else if ( CachedCommand . CommandIndex > = 0 )
{
FCachedPassMeshDrawList & PassDrawList = CachedDrawLists [ CachedCommand . MeshPass ] ;
MeshDrawCommandPtr = & PassDrawList . MeshDrawCommands [ CachedCommand . CommandIndex ] ;
}
if ( MeshDrawCommandPtr )
{
const FMeshDrawCommand & MeshDrawCommand = * MeshDrawCommandPtr ;
FPassStats & PassStats = AllPassStats [ CachedCommand . MeshPass ] ;
SIZE_T CommandBytes = sizeof ( MeshDrawCommand ) + MeshDrawCommand . GetAllocatedSize ( ) ;
PassStats . CachedMeshDrawCommandBytes + = CommandBytes ;
TotalCachedMeshDrawCommands + = MeshDrawCommand . GetAllocatedSize ( ) ;
PassStats . PSOBytes + = sizeof ( MeshDrawCommand . CachedPipelineId ) ;
PassStats . ShaderBindingInlineBytes + = sizeof ( MeshDrawCommand . ShaderBindings ) ;
PassStats . ShaderBindingHeapBytes + = MeshDrawCommand . ShaderBindings . GetAllocatedSize ( ) ;
PassStats . VertexStreamsInlineBytes + = sizeof ( MeshDrawCommand . VertexStreams ) ;
PassStats . DebugDataBytes + = MeshDrawCommand . GetDebugDataSize ( ) ;
PassStats . DrawCommandParameterBytes + = sizeof ( MeshDrawCommand . IndexBuffer ) + sizeof ( MeshDrawCommand . FirstIndex ) + sizeof ( MeshDrawCommand . NumPrimitives ) + sizeof ( MeshDrawCommand . NumInstances ) + sizeof ( MeshDrawCommand . VertexParams ) ; //-V568
PassStats . NumCommands + + ;
}
}
}
for ( int32 PassIndex = 0 ; PassIndex < EMeshPass : : Num ; PassIndex + + )
{
TotalCachedMeshDrawCommands + = CachedMeshDrawCommandStateBuckets [ PassIndex ] . GetAllocatedSize ( ) ;
}
for ( int32 i = 0 ; i < EMeshPass : : Num ; i + + )
{
TotalCachedMeshDrawCommands + = CachedDrawLists [ i ] . MeshDrawCommands . GetAllocatedSize ( ) ;
}
for ( int32 i = 0 ; i < EMeshPass : : Num ; i + + )
{
const FPassStats & PassStats = AllPassStats [ i ] ;
if ( PassStats . NumCommands > 0 )
{
UE_LOG ( LogRenderer , Log , TEXT ( " %s: %.1fKb for %u CachedMeshDrawCommands " ) , GetMeshPassName ( ( EMeshPass : : Type ) i ) , PassStats . CachedMeshDrawCommandBytes / 1024.0f , PassStats . NumCommands ) ;
if ( PassStats . CachedMeshDrawCommandBytes > 1024 & & i < = EMeshPass : : BasePass )
{
UE_LOG ( LogRenderer , Log , TEXT ( " avg %.1f bytes PSO " ) , PassStats . PSOBytes / ( float ) PassStats . NumCommands ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " avg %.1f bytes ShaderBindingInline " ) , PassStats . ShaderBindingInlineBytes / ( float ) PassStats . NumCommands ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " avg %.1f bytes ShaderBindingHeap " ) , PassStats . ShaderBindingHeapBytes / ( float ) PassStats . NumCommands ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " avg %.1f bytes VertexStreamsInline " ) , PassStats . VertexStreamsInlineBytes / ( float ) PassStats . NumCommands ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " avg %.1f bytes DebugData " ) , PassStats . DebugDataBytes / ( float ) PassStats . NumCommands ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " avg %.1f bytes DrawCommandParameters " ) , PassStats . DrawCommandParameterBytes / ( float ) PassStats . NumCommands ) ;
const SIZE_T Other = PassStats . CachedMeshDrawCommandBytes -
( PassStats . PSOBytes +
PassStats . ShaderBindingInlineBytes +
PassStats . ShaderBindingHeapBytes +
PassStats . VertexStreamsInlineBytes +
PassStats . DebugDataBytes +
PassStats . DrawCommandParameterBytes ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " avg %.1f bytes Other " ) , Other / ( float ) PassStats . NumCommands ) ;
}
}
}
UE_LOG ( LogRenderer , Log , TEXT ( " sizeof(FMeshDrawCommand) %u " ) , sizeof ( FMeshDrawCommand ) ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " Total cached MeshDrawCommands %.3fMb " ) , TotalCachedMeshDrawCommands / 1024.0f / 1024.0f ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " Primitive StaticMeshCommandInfos %.1fKb " ) , TotalStaticMeshCommandInfos / 1024.0f ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " GPUScene CPU structures %.1fKb " ) , GPUScene . PrimitivesToUpdate . GetAllocatedSize ( ) / 1024.0f ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " PSO persistent Id table %.1fKb %d elements " ) , FGraphicsMinimalPipelineStateId : : GetPersistentIdTableSize ( ) / 1024.0f , FGraphicsMinimalPipelineStateId : : GetPersistentIdNum ( ) ) ;
UE_LOG ( LogRenderer , Log , TEXT ( " PSO one frame Id %.1fKb " ) , FGraphicsMinimalPipelineStateId : : GetLocalPipelineIdTableSize ( ) / 1024.0f ) ;
}
template < typename T >
static void TArraySwapElements ( TArray < T > & Array , int i1 , int i2 )
{
T tmp = Array [ i1 ] ;
Array [ i1 ] = Array [ i2 ] ;
Array [ i2 ] = tmp ;
}
static void TBitArraySwapElements ( TBitArray < > & Array , int32 i1 , int32 i2 )
{
FBitReference BitRef1 = Array [ i1 ] ;
FBitReference BitRef2 = Array [ i2 ] ;
bool Bit1 = BitRef1 ;
bool Bit2 = BitRef2 ;
BitRef1 = Bit2 ;
BitRef2 = Bit1 ;
}
void FScene : : AddPrimitiveSceneInfo_RenderThread ( FPrimitiveSceneInfo * PrimitiveSceneInfo , const TOptional < FTransform > & PreviousTransform )
{
check ( IsInRenderingThread ( ) ) ;
check ( PrimitiveSceneInfo - > PackedIndex = = INDEX_NONE ) ;
check ( AddedPrimitiveSceneInfos . Find ( PrimitiveSceneInfo ) = = nullptr ) ;
AddedPrimitiveSceneInfos . FindOrAdd ( PrimitiveSceneInfo ) ;
if ( PreviousTransform . IsSet ( ) )
{
OverridenPreviousTransforms . Update ( PrimitiveSceneInfo , PreviousTransform . GetValue ( ) . ToMatrixWithScale ( ) ) ;
}
}
/**
* Verifies that a component is added to the proper scene
*
* @ param Component Component to verify
* @ param World World who ' s scene the primitive is being attached to
*/
FORCEINLINE static void VerifyProperPIEScene ( UPrimitiveComponent * Component , UWorld * World )
{
checkfSlow ( Component - > GetOuter ( ) = = GetTransientPackage ( ) | |
( FPackageName : : GetLongPackageAssetName ( Component - > GetOutermostObject ( ) - > GetPackage ( ) - > GetName ( ) ) . StartsWith ( PLAYWORLD_PACKAGE_PREFIX ) = =
FPackageName : : GetLongPackageAssetName ( World - > GetPackage ( ) - > GetName ( ) ) . StartsWith ( PLAYWORLD_PACKAGE_PREFIX ) ) ,
TEXT ( " The component %s was added to the wrong world's scene (due to PIE). The callstack should tell you why " ) ,
* Component - > GetFullName ( )
) ;
}
void FPersistentUniformBuffers : : Clear ( )
{
ViewUniformBuffer . SafeRelease ( ) ;
for ( auto & UniformBuffer : MobileDirectionalLightUniformBuffers )
{
UniformBuffer . SafeRelease ( ) ;
}
MobileSkyReflectionUniformBuffer . SafeRelease ( ) ;
Initialize ( ) ;
}
void FPersistentUniformBuffers : : Initialize ( )
{
FViewUniformShaderParameters ViewUniformBufferParameters ;
ViewUniformBuffer = TUniformBufferRef < FViewUniformShaderParameters > : : CreateUniformBufferImmediate ( ViewUniformBufferParameters , UniformBuffer_MultiFrame , EUniformBufferValidation : : None ) ;
FNaniteUniformParameters NaniteUniformBufferParameters ;
NaniteUniformBuffer = TUniformBufferRef < FNaniteUniformParameters > : : CreateUniformBufferImmediate ( NaniteUniformBufferParameters , UniformBuffer_MultiFrame , EUniformBufferValidation : : None ) ;
LumenCardCaptureViewUniformBuffer = TUniformBufferRef < FViewUniformShaderParameters > : : CreateUniformBufferImmediate ( ViewUniformBufferParameters , UniformBuffer_MultiFrame , EUniformBufferValidation : : None ) ;
FMobileDirectionalLightShaderParameters MobileDirectionalLightShaderParameters = { } ;
for ( int32 Index = 0 ; Index < UE_ARRAY_COUNT ( MobileDirectionalLightUniformBuffers ) ; + + Index )
{
// UniformBuffer_SingleFrame here is an optimization as this buffer gets uploaded everyframe
MobileDirectionalLightUniformBuffers [ Index ] = TUniformBufferRef < FMobileDirectionalLightShaderParameters > : : CreateUniformBufferImmediate ( MobileDirectionalLightShaderParameters , UniformBuffer_SingleFrame , EUniformBufferValidation : : None ) ;
}
const FMobileReflectionCaptureShaderParameters * DefaultMobileSkyReflectionParameters = ( const FMobileReflectionCaptureShaderParameters * ) GDefaultMobileReflectionCaptureUniformBuffer . GetContents ( ) ;
MobileSkyReflectionUniformBuffer = TUniformBufferRef < FMobileReflectionCaptureShaderParameters > : : CreateUniformBufferImmediate ( * DefaultMobileSkyReflectionParameters , UniformBuffer_MultiFrame , EUniformBufferValidation : : None ) ;
}
TSet < IPersistentViewUniformBufferExtension * > PersistentViewUniformBufferExtensions ;
void FRendererModule : : RegisterPersistentViewUniformBufferExtension ( IPersistentViewUniformBufferExtension * Extension )
{
PersistentViewUniformBufferExtensions . Add ( Extension ) ;
}
class FAsyncCreateLightPrimitiveInteractionsTask : public FNonAbandonableTask
{
TSet < FPrimitiveSceneInfo * > PendingPrimitives ;
const FScene * Scene ;
public :
FAsyncCreateLightPrimitiveInteractionsTask ( )
: Scene ( nullptr )
{ }
void Init ( const FScene * InScene )
{
Scene = InScene ;
}
void AddPrimitive ( FPrimitiveSceneInfo * PrimInfo )
{
PendingPrimitives . Add ( PrimInfo ) ;
}
bool HasPendingPrimitives ( ) const
{
return PendingPrimitives . Num ( ) > 0 ;
}
void DoWork ( )
{
FOptionalTaskTagScope Scope ( ETaskTag : : EParallelRenderingThread ) ;
for ( TSet < FPrimitiveSceneInfo * > : : TConstIterator It ( PendingPrimitives ) ; It ; + + It )
{
FPrimitiveSceneInfo * PrimInfo = * It ;
FPrimitiveSceneProxy * Proxy = PrimInfo - > Proxy ;
if ( Proxy - > GetLightingChannelMask ( ) ! = 0 )
{
FMemMark MemStackMark ( FMemStack : : Get ( ) ) ;
const FBoxSphereBounds & Bounds = Proxy - > GetBounds ( ) ;
const FPrimitiveSceneInfoCompact PrimitiveSceneInfoCompact ( PrimInfo ) ;
// Find local lights that affect the primitive in the light octree.
Scene - > LocalShadowCastingLightOctree . FindElementsWithBoundsTest ( Bounds . GetBox ( ) , [ & PrimitiveSceneInfoCompact ] ( const FLightSceneInfoCompact & LightSceneInfoCompact )
{
LightSceneInfoCompact . LightSceneInfo - > CreateLightPrimitiveInteraction ( LightSceneInfoCompact , PrimitiveSceneInfoCompact ) ;
} ) ;
// Also loop through non-local (directional) shadow-casting lights
for ( int32 LightID : Scene - > DirectionalShadowCastingLightIDs )
{
const FLightSceneInfoCompact & LightSceneInfoCompact = Scene - > Lights [ LightID ] ;
LightSceneInfoCompact . LightSceneInfo - > CreateLightPrimitiveInteraction ( LightSceneInfoCompact , PrimitiveSceneInfoCompact ) ;
}
}
}
PendingPrimitives . Reset ( ) ;
Scene = nullptr ;
}
FORCEINLINE TStatId GetStatId ( ) const
{
RETURN_QUICK_DECLARE_CYCLE_STAT ( FAsyncCreateLightPrimitiveInteractionsTask , STATGROUP_ThreadPoolAsyncTasks ) ;
}
} ;
FScene : : FScene ( UWorld * InWorld , bool bInRequiresHitProxies , bool bInIsEditorScene , bool bCreateFXSystem , ERHIFeatureLevel : : Type InFeatureLevel )
: FSceneInterface ( InFeatureLevel )
, World ( InWorld )
, FXSystem ( nullptr )
, bScenesPrimitivesNeedStaticMeshElementUpdate ( false )
, PathTracingInvalidationCounter ( 0 )
# if RHI_RAYTRACING
, CachedRayTracingMeshCommandsMode ( ERayTracingMeshCommandsMode : : RAY_TRACING )
# endif
, SkyLight ( NULL )
, ConvolvedSkyRenderTargetReadyIndex ( - 1 )
, RealTimeSlicedReflectionCaptureFirstFrameState ( ERealTimeSlicedReflectionCaptureFirstFrameState : : INIT )
, RealTimeSlicedReflectionCaptureState ( - 1 )
, RealTimeSlicedReflectionCaptureStateStep ( 0 )
, bRealTimeSlicedReflectionCaptureStateStepDone ( true )
, RealTimeSlicedReflectionCaptureFrameNumber ( 0 )
, PathTracingSkylightColor ( 0 , 0 , 0 , 0 )
, SimpleDirectionalLight ( NULL )
, ReflectionSceneData ( InFeatureLevel )
, IndirectLightingCache ( InFeatureLevel )
, VolumetricLightmapSceneData ( this )
, DistanceFieldSceneData ( GShaderPlatformForFeatureLevel [ InFeatureLevel ] )
, LumenSceneData ( nullptr )
, PreshadowCacheLayout ( 0 , 0 , 0 , 0 , false )
, SkyAtmosphere ( NULL )
, VolumetricCloud ( NULL )
, PrecomputedVisibilityHandler ( NULL )
, LocalShadowCastingLightOctree ( FVector : : ZeroVector , HALF_WORLD_MAX )
, PrimitiveOctree ( FVector : : ZeroVector , HALF_WORLD_MAX )
, bRequiresHitProxies ( bInRequiresHitProxies )
, bIsEditorScene ( bInIsEditorScene )
, NumUncachedStaticLightingInteractions ( 0 )
, NumUnbuiltReflectionCaptures ( 0 )
, NumMobileStaticAndCSMLights_RenderThread ( 0 )
, NumMobileMovableDirectionalLights_RenderThread ( 0 )
, GPUSkinCache ( nullptr )
, SceneLODHierarchy ( this )
, RuntimeVirtualTexturePrimitiveHideMaskEditor ( 0 )
, RuntimeVirtualTexturePrimitiveHideMaskGame ( 0 )
, DefaultMaxDistanceFieldOcclusionDistance ( InWorld - > GetWorldSettings ( ) - > DefaultMaxDistanceFieldOcclusionDistance )
, GlobalDistanceFieldViewDistance ( InWorld - > GetWorldSettings ( ) - > GlobalDistanceFieldViewDistance )
, DynamicIndirectShadowsSelfShadowingIntensity ( FMath : : Clamp ( InWorld - > GetWorldSettings ( ) - > DynamicIndirectShadowsSelfShadowingIntensity , 0.0f , 1.0f ) )
, ReadOnlyCVARCache ( FReadOnlyCVARCache : : Get ( ) )
# if RHI_RAYTRACING
, RayTracingDynamicGeometryCollection ( nullptr )
, RayTracingSkinnedGeometryUpdateQueue ( nullptr )
# endif
, NumVisibleLights_GameThread ( 0 )
, NumEnabledSkylights_GameThread ( 0 )
, SceneFrameNumber ( 0 )
, bForceNoPrecomputedLighting ( InWorld - > GetWorldSettings ( ) - > bForceNoPrecomputedLighting )
{
FMemory : : Memzero ( MobileDirectionalLights ) ;
FMemory : : Memzero ( AtmosphereLights ) ;
check ( World ) ;
World - > Scene = this ;
FeatureLevel = World - > FeatureLevel ;
GPUScene . SetEnabled ( FeatureLevel ) ;
if ( World - > FXSystem )
{
FFXSystemInterface : : Destroy ( World - > FXSystem ) ;
}
if ( bCreateFXSystem )
{
World - > CreateFXSystem ( ) ;
}
else
{
World - > FXSystem = NULL ;
SetFXSystem ( NULL ) ;
}
if ( IsGPUSkinCacheAvailable ( GetFeatureLevelShaderPlatform ( InFeatureLevel ) ) )
{
const bool bRequiresMemoryLimit = ! bInIsEditorScene ;
GPUSkinCache = new FGPUSkinCache ( InFeatureLevel , bRequiresMemoryLimit , World ) ;
}
ComputeSystemInterface : : CreateWorkers ( this , ComputeTaskWorkers ) ;
# if RHI_RAYTRACING
if ( IsRayTracingEnabled ( ) )
{
RayTracingDynamicGeometryCollection = new FRayTracingDynamicGeometryCollection ( ) ;
RayTracingSkinnedGeometryUpdateQueue = new FRayTracingSkinnedGeometryUpdateQueue ( ) ;
}
# endif
World - > UpdateParameterCollectionInstances ( false , false ) ;
FPersistentUniformBuffers * PersistentUniformBuffers = & UniformBuffers ;
ENQUEUE_RENDER_COMMAND ( InitializeUniformBuffers ) (
[ PersistentUniformBuffers ] ( FRHICommandListImmediate & RHICmdList )
{
PersistentUniformBuffers - > Initialize ( ) ;
} ) ;
UpdateEarlyZPassMode ( ) ;
LumenSceneData = new FLumenSceneData ( GShaderPlatformForFeatureLevel [ InFeatureLevel ] , InWorld - > WorldType ) ;
// We use a default Virtual Shadow Map cache, if one hasn't been allocated for a specific view. GPU resources for
// a cache aren't allocated until the cache is actually used, so this shouldn't waste any GPU memory when not in use.
DefaultVirtualShadowMapCache = new FVirtualShadowMapArrayCacheManager ( this ) ;
}
FScene : : ~ FScene ( )
{
#if 0 // if you have component that has invalid scene, try this code to see this is reason.
for ( FThreadSafeObjectIterator Iter ( UActorComponent : : StaticClass ( ) ) ; Iter ; + + Iter )
{
UActorComponent * ActorComp = CastChecked < UActorComponent > ( * Iter ) ;
if ( ActorComp - > GetScene ( ) = = this )
{
UE_LOG ( LogRenderer , Log , TEXT ( " %s's scene is going to get invalidated " ) , * ActorComp - > GetName ( ) ) ;
}
}
# endif
checkf ( RemovedPrimitiveSceneInfos . Num ( ) = = 0 , TEXT ( " All pending primitive removal operations are expected to be flushed when the scene is destroyed. Remaining operations are likely to cause a memory leak. " ) ) ;
checkf ( AddedPrimitiveSceneInfos . Num ( ) = = 0 , TEXT ( " All pending primitive addition operations are expected to be flushed when the scene is destroyed. Remaining operations are likely to cause a memory leak. " ) ) ;
checkf ( Primitives . Num ( ) = = 0 , TEXT ( " All primitives are expected to be removed before the scene is destroyed. Remaining primitives are likely to cause a memory leak. " ) ) ;
checkf ( ViewStates . Num ( ) = = 0 , TEXT ( " All scene view states are expected to be removed before the scene is destroyed. " ) ) ;
// Delete default cache
if ( DefaultVirtualShadowMapCache )
{
delete DefaultVirtualShadowMapCache ;
DefaultVirtualShadowMapCache = nullptr ;
}
if ( LumenSceneData )
{
delete LumenSceneData ;
LumenSceneData = nullptr ;
}
ReflectionSceneData . CubemapArray . ReleaseResource ( ) ;
IndirectLightingCache . ReleaseResource ( ) ;
DistanceFieldSceneData . Release ( ) ;
delete GPUSkinCache ;
GPUSkinCache = nullptr ;
ComputeSystemInterface : : DestroyWorkers ( this , ComputeTaskWorkers ) ;
# if RHI_RAYTRACING
delete RayTracingDynamicGeometryCollection ;
RayTracingDynamicGeometryCollection = nullptr ;
delete RayTracingSkinnedGeometryUpdateQueue ;
RayTracingSkinnedGeometryUpdateQueue = nullptr ;
# endif // RHI_RAYTRACING
checkf ( RemovedPrimitiveSceneInfos . Num ( ) = = 0 , TEXT ( " Leaking %d FPrimitiveSceneInfo instances. " ) , RemovedPrimitiveSceneInfos . Num ( ) ) ; // Ensure UpdateAllPrimitiveSceneInfos() is called before destruction.
}
void FScene : : AddPrimitive ( UPrimitiveComponent * Primitive )
{
SCOPE_CYCLE_COUNTER ( STAT_AddScenePrimitiveGT ) ;
SCOPED_NAMED_EVENT ( FScene_AddPrimitive , FColor : : Green ) ;
checkf ( ! Primitive - > IsUnreachable ( ) , TEXT ( " %s " ) , * Primitive - > GetFullName ( ) ) ;
const float WorldTime = GetWorld ( ) - > GetTimeSeconds ( ) ;
// Save the world transform for next time the primitive is added to the scene
float DeltaTime = WorldTime - Primitive - > LastSubmitTime ;
if ( DeltaTime < - 0.0001f | | Primitive - > LastSubmitTime < 0.0001f )
{
// Time was reset?
Primitive - > LastSubmitTime = WorldTime ;
}
else if ( DeltaTime > 0.0001f )
{
// First call for the new frame?
Primitive - > LastSubmitTime = WorldTime ;
}
checkf ( ! Primitive - > SceneProxy , TEXT ( " Primitive has already been added to the scene! " ) ) ;
// Create the primitive's scene proxy.
FPrimitiveSceneProxy * PrimitiveSceneProxy = Primitive - > CreateSceneProxy ( ) ;
Primitive - > SceneProxy = PrimitiveSceneProxy ;
if ( ! PrimitiveSceneProxy )
{
// Primitives which don't have a proxy are irrelevant to the scene manager.
return ;
}
// Create the primitive scene info.
FPrimitiveSceneInfo * PrimitiveSceneInfo = new FPrimitiveSceneInfo ( Primitive , this ) ;
PrimitiveSceneProxy - > PrimitiveSceneInfo = PrimitiveSceneInfo ;
// Cache the primitives initial transform.
FMatrix RenderMatrix = Primitive - > GetRenderMatrix ( ) ;
FVector AttachmentRootPosition ( 0 ) ;
AActor * AttachmentRoot = Primitive - > GetAttachmentRootActor ( ) ;
if ( AttachmentRoot )
{
AttachmentRootPosition = AttachmentRoot - > GetActorLocation ( ) ;
}
struct FCreateRenderThreadParameters
{
FPrimitiveSceneProxy * PrimitiveSceneProxy ;
FMatrix RenderMatrix ;
FBoxSphereBounds WorldBounds ;
FVector AttachmentRootPosition ;
FBoxSphereBounds LocalBounds ;
} ;
FCreateRenderThreadParameters Params =
{
PrimitiveSceneProxy ,
RenderMatrix ,
Primitive - > Bounds ,
AttachmentRootPosition ,
Primitive - > GetLocalBounds ( )
} ;
// Help track down primitive with bad bounds way before the it gets to the Renderer
ensureMsgf ( ! Primitive - > Bounds . ContainsNaN ( ) ,
TEXT ( " Nans found on Bounds for Primitive %s: Origin %s, BoxExtent %s, SphereRadius %f " ) , * Primitive - > GetName ( ) , * Primitive - > Bounds . Origin . ToString ( ) , * Primitive - > Bounds . BoxExtent . ToString ( ) , Primitive - > Bounds . SphereRadius ) ;
INC_DWORD_STAT_BY ( STAT_GameToRendererMallocTotal , PrimitiveSceneProxy - > GetMemoryFootprint ( ) + PrimitiveSceneInfo - > GetMemoryFootprint ( ) ) ;
// Verify the primitive is valid
VerifyProperPIEScene ( Primitive , World ) ;
// Increment the attachment counter, the primitive is about to be attached to the scene.
Primitive - > AttachmentCounter . Increment ( ) ;
// Create any RenderThreadResources required and send a command to the rendering thread to add the primitive to the scene.
FScene * Scene = this ;
// If this primitive has a simulated previous transform, ensure that the velocity data for the scene representation is correct
TOptional < FTransform > PreviousTransform = FMotionVectorSimulation : : Get ( ) . GetPreviousTransform ( Primitive ) ;
ENQUEUE_RENDER_COMMAND ( AddPrimitiveCommand ) (
[ Params = MoveTemp ( Params ) , Scene , PrimitiveSceneInfo , PreviousTransform = MoveTemp ( PreviousTransform ) ] ( FRHICommandListImmediate & RHICmdList )
{
FPrimitiveSceneProxy * SceneProxy = Params . PrimitiveSceneProxy ;
FScopeCycleCounter Context ( SceneProxy - > GetStatId ( ) ) ;
SceneProxy - > SetTransform ( Params . RenderMatrix , Params . WorldBounds , Params . LocalBounds , Params . AttachmentRootPosition ) ;
// Create any RenderThreadResources required.
SceneProxy - > CreateRenderThreadResources ( ) ;
Scene - > AddPrimitiveSceneInfo_RenderThread ( PrimitiveSceneInfo , PreviousTransform ) ;
} ) ;
}
static int32 GWarningOnRedundantTransformUpdate = 0 ;
static FAutoConsoleVariableRef CVarWarningOnRedundantTransformUpdate (
TEXT ( " r.WarningOnRedundantTransformUpdate " ) ,
GWarningOnRedundantTransformUpdate ,
TEXT ( " Produce a warning when UpdatePrimitiveTransform is called redundantly. " ) ,
ECVF_Default
) ;
static int32 GSkipRedundantTransformUpdate = 1 ;
static FAutoConsoleVariableRef CVarSkipRedundantTransformUpdate (
TEXT ( " r.SkipRedundantTransformUpdate " ) ,
GSkipRedundantTransformUpdate ,
TEXT ( " Skip updates UpdatePrimitiveTransform is called redundantly, if the proxy allows it. " ) ,
ECVF_Default
) ;
void FScene : : UpdatePrimitiveTransform_RenderThread ( FPrimitiveSceneProxy * PrimitiveSceneProxy , const FBoxSphereBounds & WorldBounds , const FBoxSphereBounds & LocalBounds , const FMatrix & LocalToWorld , const FVector & AttachmentRootPosition , const TOptional < FTransform > & PreviousTransform )
{
check ( IsInRenderingThread ( ) ) ;
# if VALIDATE_PRIMITIVE_PACKED_INDEX
if ( AddedPrimitiveSceneInfos . Find ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) ! = nullptr )
{
check ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex = = INDEX_NONE ) ;
}
else
{
check ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex ! = INDEX_NONE ) ;
}
check ( RemovedPrimitiveSceneInfos . Find ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) = = nullptr ) ;
# endif
UpdatedTransforms . Update ( PrimitiveSceneProxy , { WorldBounds , LocalBounds , LocalToWorld , AttachmentRootPosition } ) ;
if ( PreviousTransform . IsSet ( ) )
{
OverridenPreviousTransforms . Update ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) , PreviousTransform . GetValue ( ) . ToMatrixWithScale ( ) ) ;
}
}
void FScene : : UpdatePrimitiveOcclusionBoundsSlack_RenderThread ( const FPrimitiveSceneProxy * PrimitiveSceneProxy , float NewSlack )
{
check ( IsInRenderingThread ( ) ) ;
# if VALIDATE_PRIMITIVE_PACKED_INDEX
if ( AddedPrimitiveSceneInfos . Find ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) ! = nullptr )
{
check ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex = = INDEX_NONE ) ;
}
else
{
check ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex ! = INDEX_NONE ) ;
}
check ( RemovedPrimitiveSceneInfos . Find ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) = = nullptr ) ;
# endif
UpdatedOcclusionBoundsSlacks . Update ( PrimitiveSceneProxy , NewSlack ) ;
}
void FScene : : UpdatePrimitiveTransform ( UPrimitiveComponent * Primitive )
{
SCOPE_CYCLE_COUNTER ( STAT_UpdatePrimitiveTransformGT ) ;
SCOPED_NAMED_EVENT ( FScene_UpdatePrimitiveTransform , FColor : : Yellow ) ;
// Save the world transform for next time the primitive is added to the scene
const float WorldTime = GetWorld ( ) - > GetTimeSeconds ( ) ;
float DeltaTime = WorldTime - Primitive - > LastSubmitTime ;
if ( DeltaTime < - 0.0001f | | Primitive - > LastSubmitTime < 0.0001f )
{
// Time was reset?
Primitive - > LastSubmitTime = WorldTime ;
}
else if ( DeltaTime > 0.0001f )
{
// First call for the new frame?
Primitive - > LastSubmitTime = WorldTime ;
}
if ( Primitive - > SceneProxy )
{
// Check if the primitive needs to recreate its proxy for the transform update.
if ( Primitive - > ShouldRecreateProxyOnUpdateTransform ( ) )
{
// Re-add the primitive from scratch to recreate the primitive's proxy.
RemovePrimitive ( Primitive ) ;
AddPrimitive ( Primitive ) ;
}
else
{
FVector AttachmentRootPosition ( 0 ) ;
AActor * Actor = Primitive - > GetAttachmentRootActor ( ) ;
if ( Actor ! = nullptr )
{
AttachmentRootPosition = Actor - > GetActorLocation ( ) ;
}
struct FPrimitiveUpdateParams
{
FScene * Scene ;
FPrimitiveSceneProxy * PrimitiveSceneProxy ;
FBoxSphereBounds WorldBounds ;
FBoxSphereBounds LocalBounds ;
FMatrix LocalToWorld ;
TOptional < FTransform > PreviousTransform ;
FVector AttachmentRootPosition ;
} ;
FPrimitiveUpdateParams UpdateParams ;
UpdateParams . Scene = this ;
UpdateParams . PrimitiveSceneProxy = Primitive - > SceneProxy ;
UpdateParams . WorldBounds = Primitive - > Bounds ;
UpdateParams . LocalToWorld = Primitive - > GetRenderMatrix ( ) ;
UpdateParams . AttachmentRootPosition = AttachmentRootPosition ;
UpdateParams . LocalBounds = Primitive - > GetLocalBounds ( ) ;
UpdateParams . PreviousTransform = FMotionVectorSimulation : : Get ( ) . GetPreviousTransform ( Primitive ) ;
// Help track down primitive with bad bounds way before the it gets to the renderer.
ensureMsgf ( ! UpdateParams . WorldBounds . BoxExtent . ContainsNaN ( ) & & ! UpdateParams . WorldBounds . Origin . ContainsNaN ( ) & & ! FMath : : IsNaN ( UpdateParams . WorldBounds . SphereRadius ) & & FMath : : IsFinite ( UpdateParams . WorldBounds . SphereRadius ) ,
TEXT ( " NaNs found on Bounds for Primitive %s: Owner: %s, Resource: %s, Level: %s, Origin: %s, BoxExtent: %s, SphereRadius: %f " ) ,
* Primitive - > GetName ( ) ,
* Primitive - > SceneProxy - > GetOwnerName ( ) . ToString ( ) ,
* Primitive - > SceneProxy - > GetResourceName ( ) . ToString ( ) ,
* Primitive - > SceneProxy - > GetLevelName ( ) . ToString ( ) ,
* UpdateParams . WorldBounds . Origin . ToString ( ) ,
* UpdateParams . WorldBounds . BoxExtent . ToString ( ) ,
UpdateParams . WorldBounds . SphereRadius
) ;
bool bPerformUpdate = true ;
const bool bAllowSkip = GSkipRedundantTransformUpdate & & Primitive - > SceneProxy - > CanSkipRedundantTransformUpdates ( ) ;
if ( bAllowSkip | | GWarningOnRedundantTransformUpdate )
{
if ( Primitive - > SceneProxy - > WouldSetTransformBeRedundant_AnyThread (
UpdateParams . LocalToWorld ,
UpdateParams . WorldBounds ,
UpdateParams . LocalBounds ,
UpdateParams . AttachmentRootPosition ) )
{
if ( bAllowSkip )
{
// Do not perform the transform update!
bPerformUpdate = false ;
}
else
{
// Not skipping, and warnings are enabled.
UE_LOG ( LogRenderer , Warning ,
TEXT ( " Redundant UpdatePrimitiveTransform for Primitive %s: Owner: %s, Resource: %s, Level: %s " ) ,
* Primitive - > GetName ( ) ,
* Primitive - > SceneProxy - > GetOwnerName ( ) . ToString ( ) ,
* Primitive - > SceneProxy - > GetResourceName ( ) . ToString ( ) ,
* Primitive - > SceneProxy - > GetLevelName ( ) . ToString ( )
) ;
}
}
}
if ( bPerformUpdate )
{
ENQUEUE_RENDER_COMMAND ( UpdateTransformCommand ) (
[ UpdateParams ] ( FRHICommandListImmediate & RHICmdList )
{
FScopeCycleCounter Context ( UpdateParams . PrimitiveSceneProxy - > GetStatId ( ) ) ;
UpdateParams . Scene - > UpdatePrimitiveTransform_RenderThread (
UpdateParams . PrimitiveSceneProxy ,
UpdateParams . WorldBounds ,
UpdateParams . LocalBounds ,
UpdateParams . LocalToWorld ,
UpdateParams . AttachmentRootPosition ,
UpdateParams . PreviousTransform
) ;
}
) ;
}
}
}
else
{
// If the primitive doesn't have a scene info object yet, it must be added from scratch.
AddPrimitive ( Primitive ) ;
}
}
void FScene : : UpdatePrimitiveOcclusionBoundsSlack ( UPrimitiveComponent * Primitive , float NewSlack )
{
if ( Primitive - > SceneProxy )
{
const FPrimitiveSceneProxy * SceneProxy = Primitive - > SceneProxy ;
ENQUEUE_RENDER_COMMAND ( UpdateOcclusionBoundsSlackCmd ) (
[ this , SceneProxy , NewSlack ] ( FRHICommandListImmediate & )
{
UpdatePrimitiveOcclusionBoundsSlack_RenderThread ( SceneProxy , NewSlack ) ;
} ) ;
}
}
void FScene : : UpdatePrimitiveInstances ( UInstancedStaticMeshComponent * Primitive )
{
SCOPE_CYCLE_COUNTER ( STAT_UpdatePrimitiveInstanceGT ) ;
SCOPED_NAMED_EVENT ( FScene_UpdatePrimitiveInstance , FColor : : Yellow ) ;
if ( Primitive - > SceneProxy )
{
FUpdateInstanceCommand UpdateParams ;
UpdateParams . PrimitiveSceneProxy = Primitive - > SceneProxy ;
UpdateParams . WorldBounds = Primitive - > Bounds ;
UpdateParams . LocalBounds = Cast < UPrimitiveComponent > ( Primitive ) - > GetLocalBounds ( ) ;
// #todo (jnadro) This code should not be dependent on static mesh bounds.
UStaticMeshComponent * StaticMeshComponent = Cast < UStaticMeshComponent > ( Primitive ) ;
UpdateParams . StaticMeshBounds = StaticMeshComponent ? StaticMeshComponent - > GetStaticMesh ( ) - > GetBounds ( ) : FBoxSphereBounds ( ) ;
UpdateParams . CmdBuffer = Primitive - > InstanceUpdateCmdBuffer ; // Copy
ENQUEUE_RENDER_COMMAND ( UpdateInstanceCommand ) (
[ this , UpdateParams = MoveTemp ( UpdateParams ) ] ( FRHICommandListImmediate & RHICmdList )
{
# if VALIDATE_PRIMITIVE_PACKED_INDEX
if ( AddedPrimitiveSceneInfos . Find ( UpdateParams . PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) ! = nullptr )
{
check ( UpdateParams . PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex = = INDEX_NONE ) ;
}
else
{
check ( UpdateParams . PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex ! = INDEX_NONE ) ;
}
check ( RemovedPrimitiveSceneInfos . Find ( UpdateParams . PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) = = nullptr ) ;
# endif
FScopeCycleCounter Context ( UpdateParams . PrimitiveSceneProxy - > GetStatId ( ) ) ;
UpdatedInstances . Update ( UpdateParams . PrimitiveSceneProxy , UpdateParams ) ;
}
) ;
}
else
{
// If the primitive doesn't have a scene info object yet, it must be added from scratch.
AddPrimitive ( Primitive ) ;
}
}
void FScene : : UpdatePrimitiveSelectedState_RenderThread ( const FPrimitiveSceneInfo * PrimitiveSceneInfo , bool bIsSelected )
{
check ( IsInRenderingThread ( ) ) ;
# if WITH_EDITOR
if ( PrimitiveSceneInfo )
{
if ( PrimitiveSceneInfo - > GetIndex ( ) ! = INDEX_NONE )
{
PrimitivesSelected [ PrimitiveSceneInfo - > GetIndex ( ) ] = bIsSelected ;
}
}
# endif // WITH_EDITOR
}
void FScene : : UpdatePrimitiveLightingAttachmentRoot ( UPrimitiveComponent * Primitive )
{
const UPrimitiveComponent * NewLightingAttachmentRoot = Primitive - > GetLightingAttachmentRoot ( ) ;
if ( NewLightingAttachmentRoot = = Primitive )
{
NewLightingAttachmentRoot = NULL ;
}
FPrimitiveComponentId NewComponentId = NewLightingAttachmentRoot ? NewLightingAttachmentRoot - > ComponentId : FPrimitiveComponentId ( ) ;
if ( Primitive - > SceneProxy )
{
FPrimitiveSceneProxy * Proxy = Primitive - > SceneProxy ;
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( UpdatePrimitiveAttachment ) (
[ Scene , Proxy , NewComponentId ] ( FRHICommandList & )
{
FPrimitiveSceneInfo * PrimitiveInfo = Proxy - > GetPrimitiveSceneInfo ( ) ;
Scene - > UpdatedAttachmentRoots . Update ( PrimitiveInfo , NewComponentId ) ;
} ) ;
}
}
void FScene : : UpdatePrimitiveAttachment ( UPrimitiveComponent * Primitive )
{
TArray < USceneComponent * , TInlineAllocator < 1 > > ProcessStack ;
ProcessStack . Push ( Primitive ) ;
// Walk down the tree updating, because the scene's attachment data structures must be updated if the root of the attachment tree changes
while ( ProcessStack . Num ( ) > 0 )
{
USceneComponent * Current = ProcessStack . Pop ( /*bAllowShrinking=*/ false ) ;
if ( Current )
{
UPrimitiveComponent * CurrentPrimitive = Cast < UPrimitiveComponent > ( Current ) ;
if ( CurrentPrimitive
& & CurrentPrimitive - > GetWorld ( )
& & CurrentPrimitive - > GetWorld ( ) - > Scene = = this
& & CurrentPrimitive - > ShouldComponentAddToScene ( ) )
{
UpdatePrimitiveLightingAttachmentRoot ( CurrentPrimitive ) ;
}
ProcessStack . Append ( Current - > GetAttachChildren ( ) ) ;
}
}
}
void FScene : : UpdateCustomPrimitiveData ( UPrimitiveComponent * Primitive )
{
SCOPE_CYCLE_COUNTER ( STAT_UpdateCustomPrimitiveDataGT ) ;
SCOPED_NAMED_EVENT ( FScene_UpdateCustomPrimitiveData , FColor : : Yellow ) ;
// This path updates the primitive data directly in the GPUScene.
if ( Primitive - > SceneProxy )
{
struct FUpdateParams
{
FScene * Scene ;
FPrimitiveSceneProxy * PrimitiveSceneProxy ;
FCustomPrimitiveData CustomPrimitiveData ;
} ;
FUpdateParams UpdateParams ;
UpdateParams . Scene = this ;
UpdateParams . PrimitiveSceneProxy = Primitive - > SceneProxy ;
UpdateParams . CustomPrimitiveData = Primitive - > GetCustomPrimitiveData ( ) ;
ENQUEUE_RENDER_COMMAND ( UpdateCustomPrimitiveDataCommand ) (
[ UpdateParams ] ( FRHICommandListImmediate & RHICmdList )
{
UpdateParams . Scene - > UpdatedCustomPrimitiveParams . Update ( UpdateParams . PrimitiveSceneProxy , UpdateParams . CustomPrimitiveData ) ;
} ) ;
}
}
void FScene : : UpdatePrimitiveDistanceFieldSceneData_GameThread ( UPrimitiveComponent * Primitive )
{
check ( IsInGameThread ( ) ) ;
if ( Primitive - > SceneProxy )
{
Primitive - > LastSubmitTime = GetWorld ( ) - > GetTimeSeconds ( ) ;
ENQUEUE_RENDER_COMMAND ( UpdatePrimDFSceneDataCmd ) (
[ this , PrimitiveSceneProxy = Primitive - > SceneProxy ] ( FRHICommandList & )
{
if ( PrimitiveSceneProxy & & PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) )
{
FPrimitiveSceneInfo * Info = PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ;
this - > DistanceFieldSceneDataUpdates . FindOrAdd ( Info ) ;
}
} ) ;
}
}
FPrimitiveSceneInfo * FScene : : GetPrimitiveSceneInfo ( int32 PrimitiveIndex )
{
if ( Primitives . IsValidIndex ( PrimitiveIndex ) )
{
return Primitives [ PrimitiveIndex ] ;
}
return NULL ;
}
void FScene : : RemovePrimitiveSceneInfo_RenderThread ( FPrimitiveSceneInfo * PrimitiveSceneInfo )
{
check ( IsInRenderingThread ( ) ) ;
if ( AddedPrimitiveSceneInfos . Remove ( PrimitiveSceneInfo ) )
{
check ( PrimitiveSceneInfo - > PackedIndex = = INDEX_NONE ) ;
UpdatedTransforms . Remove ( PrimitiveSceneInfo - > Proxy ) ;
UpdatedCustomPrimitiveParams . Remove ( PrimitiveSceneInfo - > Proxy ) ;
OverridenPreviousTransforms . Remove ( PrimitiveSceneInfo ) ;
UpdatedOcclusionBoundsSlacks . Remove ( PrimitiveSceneInfo - > Proxy ) ;
DistanceFieldSceneDataUpdates . Remove ( PrimitiveSceneInfo ) ;
UpdatedAttachmentRoots . Remove ( PrimitiveSceneInfo ) ;
{
SCOPED_NAMED_EVENT ( FScene_DeletePrimitiveSceneInfo , FColor : : Red ) ;
// Delete the PrimitiveSceneInfo on the game thread after the rendering thread has processed its removal.
// This must be done on the game thread because the hit proxy references (and possibly other members) need to be freed on the game thread.
struct DeferDeleteHitProxies : FDeferredCleanupInterface
{
DeferDeleteHitProxies ( TArray < TRefCountPtr < HHitProxy > > & & InHitProxies ) : HitProxies ( MoveTemp ( InHitProxies ) ) { }
TArray < TRefCountPtr < HHitProxy > > HitProxies ;
} ;
BeginCleanup ( new DeferDeleteHitProxies ( MoveTemp ( PrimitiveSceneInfo - > HitProxies ) ) ) ;
delete PrimitiveSceneInfo - > Proxy ;
delete PrimitiveSceneInfo ;
}
}
else
{
check ( PrimitiveSceneInfo - > PackedIndex ! = INDEX_NONE ) ;
check ( RemovedPrimitiveSceneInfos . Find ( PrimitiveSceneInfo ) = = nullptr ) ;
RemovedPrimitiveSceneInfos . FindOrAdd ( PrimitiveSceneInfo ) ;
}
}
void FScene : : RemovePrimitive ( UPrimitiveComponent * Primitive )
{
SCOPE_CYCLE_COUNTER ( STAT_RemoveScenePrimitiveGT ) ;
SCOPED_NAMED_EVENT ( FScene_RemovePrimitive , FColor : : Yellow ) ;
FPrimitiveSceneProxy * PrimitiveSceneProxy = Primitive - > SceneProxy ;
if ( PrimitiveSceneProxy )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ;
// Disassociate the primitive's scene proxy.
Primitive - > SceneProxy = NULL ;
// Send a command to the rendering thread to remove the primitive from the scene.
FScene * Scene = this ;
FThreadSafeCounter * AttachmentCounter = & Primitive - > AttachmentCounter ;
ENQUEUE_RENDER_COMMAND ( FRemovePrimitiveCommand ) (
[ Scene , PrimitiveSceneInfo , AttachmentCounter ] ( FRHICommandList & )
{
PrimitiveSceneInfo - > Proxy - > DestroyRenderThreadResources ( ) ;
Scene - > RemovePrimitiveSceneInfo_RenderThread ( PrimitiveSceneInfo ) ;
AttachmentCounter - > Decrement ( ) ;
} ) ;
}
}
void FScene : : ReleasePrimitive ( UPrimitiveComponent * PrimitiveComponent )
{
// Send a command to the rendering thread to clean up any state dependent on this primitive
FScene * Scene = this ;
FPrimitiveComponentId PrimitiveComponentId = PrimitiveComponent - > ComponentId ;
ENQUEUE_RENDER_COMMAND ( FReleasePrimitiveCommand ) (
[ Scene , PrimitiveComponentId ] ( FRHICommandList & )
{
// Free the space in the indirect lighting cache
Scene - > IndirectLightingCache . ReleasePrimitive ( PrimitiveComponentId ) ;
} ) ;
}
void FScene : : AssignAvailableShadowMapChannelForLight ( FLightSceneInfo * LightSceneInfo )
{
FDynamicShadowMapChannelBindingHelper Helper ;
check ( LightSceneInfo & & LightSceneInfo - > Proxy ) ;
// For lights with static shadowing, only check for lights intersecting the preview channel if any.
if ( LightSceneInfo - > Proxy - > HasStaticShadowing ( ) )
{
Helper . DisableAllOtherChannels ( LightSceneInfo - > GetDynamicShadowMapChannel ( ) ) ;
// If this static shadowing light does not need a (preview) channel, skip it.
if ( ! Helper . HasAnyChannelEnabled ( ) )
{
return ;
}
}
else if ( LightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Directional & & ! IsMobilePlatform ( GetShaderPlatform ( ) ) )
{
// The implementation of forward lighting in ShadowProjectionPixelShader.usf does not support binding the directional light to channel 3.
// This is related to the USE_FADE_PLANE feature that encodes the CSM blend factor the alpha channel.
Helper . DisableChannel ( 3 ) ;
}
Helper . UpdateAvailableChannels ( Lights , LightSceneInfo ) ;
const int32 NewChannelIndex = Helper . GetBestAvailableChannel ( ) ;
if ( NewChannelIndex ! = INDEX_NONE )
{
// Unbind the channels previously allocated to lower priority lights.
for ( FLightSceneInfo * OtherLight : Helper . GetLights ( NewChannelIndex ) )
{
OtherLight - > SetDynamicShadowMapChannel ( INDEX_NONE ) ;
}
LightSceneInfo - > SetDynamicShadowMapChannel ( NewChannelIndex ) ;
// Try to assign new channels to lights that were just unbound.
// Sort the lights so that they only get inserted once (prevents recursion).
Helper . SortLightByPriority ( NewChannelIndex ) ;
for ( FLightSceneInfo * OtherLight : Helper . GetLights ( NewChannelIndex ) )
{
AssignAvailableShadowMapChannelForLight ( OtherLight ) ;
}
}
else
{
LightSceneInfo - > SetDynamicShadowMapChannel ( INDEX_NONE ) ;
OverflowingDynamicShadowedLights . AddUnique ( LightSceneInfo - > Proxy - > GetOwnerNameOrLabel ( ) ) ;
}
}
void FScene : : AddLightSceneInfo_RenderThread ( FLightSceneInfo * LightSceneInfo )
{
SCOPE_CYCLE_COUNTER ( STAT_AddSceneLightTime ) ;
SCOPED_NAMED_EVENT ( FScene_AddLightSceneInfo_RenderThread , FColor : : Green ) ;
check ( LightSceneInfo - > bVisible ) ;
// Add the light to the light list.
LightSceneInfo - > Id = Lights . Add ( FLightSceneInfoCompact ( LightSceneInfo ) ) ;
const FLightSceneInfoCompact & LightSceneInfoCompact = Lights [ LightSceneInfo - > Id ] ;
const ELightComponentType LightType = ( ELightComponentType ) LightSceneInfo - > Proxy - > GetLightType ( ) ;
const bool bDirectionalLight = LightType = = LightType_Directional ;
if ( bDirectionalLight )
{
DirectionalLights . Add ( LightSceneInfo ) ;
}
if ( bDirectionalLight & &
// Only use a stationary or movable light
! ( LightSceneInfo - > Proxy - > HasStaticLighting ( )
// if it is a Static DirectionalLight and the light has not been built, add it to MobileDirectionalLights for mobile preview.
& & LightSceneInfo - > IsPrecomputedLightingValid ( ) )
)
{
// Set SimpleDirectionalLight
if ( ! SimpleDirectionalLight )
{
SimpleDirectionalLight = LightSceneInfo ;
}
if ( GetShadingPath ( ) = = EShadingPath : : Mobile )
{
const bool bUseCSMForDynamicObjects = LightSceneInfo - > Proxy - > UseCSMForDynamicObjects ( ) ;
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// these are tracked for disabled shader permutation warnings
if ( LightSceneInfo - > Proxy - > IsMovable ( ) )
{
NumMobileMovableDirectionalLights_RenderThread + + ;
}
if ( bUseCSMForDynamicObjects )
{
NumMobileStaticAndCSMLights_RenderThread + + ;
}
# endif
// Set MobileDirectionalLights entry
int32 FirstLightingChannel = GetFirstLightingChannelFromMask ( LightSceneInfo - > Proxy - > GetLightingChannelMask ( ) ) ;
if ( FirstLightingChannel > = 0 & & MobileDirectionalLights [ FirstLightingChannel ] = = nullptr )
{
MobileDirectionalLights [ FirstLightingChannel ] = LightSceneInfo ;
// if this light is a dynamic shadowcast then we need to update the static draw lists to pick a new lighting policy:
if ( ! LightSceneInfo - > Proxy - > HasStaticShadowing ( ) | | bUseCSMForDynamicObjects )
{
bScenesPrimitivesNeedStaticMeshElementUpdate = true ;
}
}
}
}
// Register rect. light source texture
if ( LightType = = LightType_Rect )
{
FRectLightSceneProxy * RectProxy = ( FRectLightSceneProxy * ) LightSceneInfo - > Proxy ;
RectProxy - > AtlasSlotIndex = RectLightAtlas : : AddRectLightTexture ( RectProxy - > SourceTexture ) ;
}
const EShaderPlatform ShaderPlatform = GetShaderPlatform ( ) ;
const bool bAssignShadowMapChannel = IsForwardShadingEnabled ( ShaderPlatform ) | | ( IsMobilePlatform ( ShaderPlatform ) & & MobileUsesShadowMaskTexture ( ShaderPlatform ) ) ;
if ( bAssignShadowMapChannel & & ( LightSceneInfo - > Proxy - > CastsDynamicShadow ( ) | | LightSceneInfo - > Proxy - > GetLightFunctionMaterial ( ) ) )
{
AssignAvailableShadowMapChannelForLight ( LightSceneInfo ) ;
}
ProcessAtmosphereLightAddition_RenderThread ( LightSceneInfo ) ;
InvalidatePathTracedOutput ( ) ;
// Add the light to the scene.
LightSceneInfo - > AddToScene ( ) ;
}
void FScene : : AddLight ( ULightComponent * Light )
{
LLM_SCOPE ( ELLMTag : : SceneRender ) ;
// Create the light's scene proxy.
FLightSceneProxy * Proxy = Light - > CreateSceneProxy ( ) ;
if ( Proxy )
{
// Associate the proxy with the light.
Light - > SceneProxy = Proxy ;
// Update the light's transform and position.
Proxy - > SetTransform ( Light - > GetComponentTransform ( ) . ToMatrixNoScale ( ) , Light - > GetLightPosition ( ) ) ;
// Create the light scene info.
Proxy - > LightSceneInfo = new FLightSceneInfo ( Proxy , true ) ;
INC_DWORD_STAT ( STAT_SceneLights ) ;
// Adding a new light
+ + NumVisibleLights_GameThread ;
// Send a command to the rendering thread to add the light to the scene.
FScene * Scene = this ;
FLightSceneInfo * LightSceneInfo = Proxy - > LightSceneInfo ;
ENQUEUE_RENDER_COMMAND ( FAddLightCommand ) (
[ Scene , LightSceneInfo ] ( FRHICommandListImmediate & RHICmdList )
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( Scene_AddLight ) ;
FScopeCycleCounter Context ( LightSceneInfo - > Proxy - > GetStatId ( ) ) ;
Scene - > AddLightSceneInfo_RenderThread ( LightSceneInfo ) ;
} ) ;
}
}
void FScene : : AddInvisibleLight ( ULightComponent * Light )
{
// Create the light's scene proxy.
FLightSceneProxy * Proxy = Light - > CreateSceneProxy ( ) ;
if ( Proxy )
{
// Associate the proxy with the light.
Light - > SceneProxy = Proxy ;
// Update the light's transform and position.
Proxy - > SetTransform ( Light - > GetComponentTransform ( ) . ToMatrixNoScale ( ) , Light - > GetLightPosition ( ) ) ;
// Create the light scene info.
Proxy - > LightSceneInfo = new FLightSceneInfo ( Proxy , false ) ;
INC_DWORD_STAT ( STAT_SceneLights ) ;
// Send a command to the rendering thread to add the light to the scene.
FScene * Scene = this ;
FLightSceneInfo * LightSceneInfo = Proxy - > LightSceneInfo ;
ENQUEUE_RENDER_COMMAND ( FAddLightCommand ) (
[ Scene , LightSceneInfo ] ( FRHICommandListImmediate & RHICmdList )
{
FScopeCycleCounter Context ( LightSceneInfo - > Proxy - > GetStatId ( ) ) ;
LightSceneInfo - > Id = Scene - > InvisibleLights . Add ( FLightSceneInfoCompact ( LightSceneInfo ) ) ;
} ) ;
}
}
void FScene : : SetSkyLight ( FSkyLightSceneProxy * LightProxy )
{
check ( LightProxy ) ;
NumEnabledSkylights_GameThread + + ;
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FSetSkyLightCommand )
( [ Scene , LightProxy ] ( FRHICommandListImmediate & RHICmdList )
{
check ( ! Scene - > SkyLightStack . Contains ( LightProxy ) ) ;
Scene - > SkyLightStack . Push ( LightProxy ) ;
const bool bOriginalHadSkylight = Scene - > ShouldRenderSkylightInBasePass ( BLEND_Opaque ) ;
// Use the most recently enabled skylight
Scene - > SkyLight = LightProxy ;
const bool bNewHasSkylight = Scene - > ShouldRenderSkylightInBasePass ( BLEND_Opaque ) ;
if ( bOriginalHadSkylight ! = bNewHasSkylight )
{
// Mark the scene as needing static draw lists to be recreated if needed
// The base pass chooses shaders based on whether there's a skylight in the scene, and that is cached in static draw lists
Scene - > bScenesPrimitivesNeedStaticMeshElementUpdate = true ;
}
Scene - > InvalidatePathTracedOutput ( ) ;
} ) ;
}
void FScene : : DisableSkyLight ( FSkyLightSceneProxy * LightProxy )
{
check ( LightProxy ) ;
NumEnabledSkylights_GameThread - - ;
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FDisableSkyLightCommand )
( [ Scene , LightProxy ] ( FRHICommandListImmediate & RHICmdList )
{
const bool bOriginalHadSkylight = Scene - > ShouldRenderSkylightInBasePass ( BLEND_Opaque ) ;
Scene - > SkyLightStack . RemoveSingle ( LightProxy ) ;
if ( Scene - > SkyLightStack . Num ( ) > 0 )
{
// Use the most recently enabled skylight
Scene - > SkyLight = Scene - > SkyLightStack . Last ( ) ;
}
else
{
Scene - > SkyLight = NULL ;
}
const bool bNewHasSkylight = Scene - > ShouldRenderSkylightInBasePass ( BLEND_Opaque ) ;
// Update the scene if we switched skylight enabled states
if ( bOriginalHadSkylight ! = bNewHasSkylight )
{
Scene - > bScenesPrimitivesNeedStaticMeshElementUpdate = true ;
}
Scene - > InvalidatePathTracedOutput ( ) ;
} ) ;
}
bool FScene : : HasSkyLightRequiringLightingBuild ( ) const
{
return SkyLight ! = nullptr & & ! SkyLight - > IsMovable ( ) ;
}
bool FScene : : HasAtmosphereLightRequiringLightingBuild ( ) const
{
bool AnySunLightNotMovable = false ;
for ( uint8 Index = 0 ; Index < NUM_ATMOSPHERE_LIGHTS ; + + Index )
{
AnySunLightNotMovable | = AtmosphereLights [ Index ] ! = nullptr & & ! AtmosphereLights [ Index ] - > Proxy - > IsMovable ( ) ;
}
return AnySunLightNotMovable ;
}
void FScene : : AddOrRemoveDecal_RenderThread ( FDeferredDecalProxy * Proxy , bool bAdd )
{
if ( bAdd )
{
Decals . Add ( Proxy ) ;
InvalidatePathTracedOutput ( ) ;
}
else
{
// can be optimized
for ( TSparseArray < FDeferredDecalProxy * > : : TIterator It ( Decals ) ; It ; + + It )
{
FDeferredDecalProxy * CurrentProxy = * It ;
if ( CurrentProxy = = Proxy )
{
InvalidatePathTracedOutput ( ) ;
It . RemoveCurrent ( ) ;
delete CurrentProxy ;
break ;
}
}
}
}
void FScene : : SetPhysicsField ( FPhysicsFieldSceneProxy * PhysicsFieldSceneProxy )
{
check ( PhysicsFieldSceneProxy ) ;
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FSetPhysicsFieldCommand ) (
[ Scene , PhysicsFieldSceneProxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > PhysicsField = PhysicsFieldSceneProxy ;
} ) ;
}
void FScene : : ShowPhysicsField ( )
{
// Set the shader print/debug values from game thread if
// physics field visualisation has been enabled
if ( PhysicsField & & PhysicsField - > FieldResource & & PhysicsField - > FieldResource - > FieldInfos . bShowFields )
{
if ( ! ShaderPrint : : IsEnabled ( ) )
{
ShaderPrint : : SetEnabled ( true ) ;
ShaderPrint : : SetFontSize ( 8 ) ;
}
ShaderPrint : : RequestSpaceForLines ( 128000 ) ;
}
}
void FScene : : ResetPhysicsField ( )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FResetPhysicsFieldCommand ) (
[ Scene ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > PhysicsField = nullptr ;
} ) ;
}
void FScene : : UpdatePhysicsField ( FRDGBuilder & GraphBuilder , FViewInfo & View )
{
if ( PhysicsField )
{
PhysicsField - > FieldResource - > FieldInfos . ViewOrigin = View . ViewMatrices . GetViewOrigin ( ) ;
if ( View . Family )
{
PhysicsField - > FieldResource - > FieldInfos . bShowFields = View . Family - > EngineShowFlags . PhysicsField ;
}
}
}
void FScene : : AddDecal ( UDecalComponent * Component )
{
if ( ! Component - > SceneProxy )
{
// Create the decals's scene proxy.
Component - > SceneProxy = Component - > CreateSceneProxy ( ) ;
INC_DWORD_STAT ( STAT_SceneDecals ) ;
// Send a command to the rendering thread to add the light to the scene.
FScene * Scene = this ;
FDeferredDecalProxy * Proxy = Component - > SceneProxy ;
ENQUEUE_RENDER_COMMAND ( FAddDecalCommand ) (
[ Scene , Proxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > AddOrRemoveDecal_RenderThread ( Proxy , true ) ;
} ) ;
}
}
void FScene : : RemoveDecal ( UDecalComponent * Component )
{
if ( Component - > SceneProxy )
{
DEC_DWORD_STAT ( STAT_SceneDecals ) ;
// Send a command to the rendering thread to remove the light from the scene.
FScene * Scene = this ;
FDeferredDecalProxy * Proxy = Component - > SceneProxy ;
ENQUEUE_RENDER_COMMAND ( FRemoveDecalCommand ) (
[ Scene , Proxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > AddOrRemoveDecal_RenderThread ( Proxy , false ) ;
} ) ;
// Disassociate the primitive's scene proxy.
Component - > SceneProxy = NULL ;
}
}
void FScene : : UpdateDecalTransform ( UDecalComponent * Decal )
{
if ( Decal - > SceneProxy )
{
//Send command to the rendering thread to update the decal's transform.
FScene * Scene = this ;
FDeferredDecalProxy * DecalSceneProxy = Decal - > SceneProxy ;
FTransform ComponentToWorldIncludingDecalSize = Decal - > GetTransformIncludingDecalSize ( ) ;
FBoxSphereBounds Bounds = Decal - > CalcBounds ( Decal - > GetComponentTransform ( ) ) ;
ENQUEUE_RENDER_COMMAND ( UpdateTransformCommand ) (
[ DecalSceneProxy , ComponentToWorldIncludingDecalSize , Bounds , Scene ] ( FRHICommandListImmediate & RHICmdList )
{
// Invalidate the path tracer only if the decal was sufficiently moved
if ( ! ComponentToWorldIncludingDecalSize . Equals ( DecalSceneProxy - > ComponentTrans , SMALL_NUMBER ) )
{
Scene - > InvalidatePathTracedOutput ( ) ;
}
// Update the primitive's transform.
DecalSceneProxy - > SetTransformIncludingDecalSize ( ComponentToWorldIncludingDecalSize , Bounds ) ;
} ) ;
}
}
void FScene : : UpdateDecalFadeOutTime ( UDecalComponent * Decal )
{
FDeferredDecalProxy * Proxy = Decal - > SceneProxy ;
if ( Proxy )
{
float CurrentTime = GetWorld ( ) - > GetTimeSeconds ( ) ;
float DecalFadeStartDelay = Decal - > FadeStartDelay ;
float DecalFadeDuration = Decal - > FadeDuration ;
ENQUEUE_RENDER_COMMAND ( FUpdateDecalFadeInTimeCommand ) (
[ Proxy , CurrentTime , DecalFadeStartDelay , DecalFadeDuration ] ( FRHICommandListImmediate & RHICmdList )
{
if ( DecalFadeDuration > 0.0f )
{
Proxy - > InvFadeDuration = 1.0f / DecalFadeDuration ;
Proxy - > FadeStartDelayNormalized = ( CurrentTime + DecalFadeStartDelay + DecalFadeDuration ) * Proxy - > InvFadeDuration ;
}
else
{
Proxy - > InvFadeDuration = - 1.0f ;
Proxy - > FadeStartDelayNormalized = 1.0f ;
}
} ) ;
}
}
void FScene : : UpdateDecalFadeInTime ( UDecalComponent * Decal )
{
FDeferredDecalProxy * Proxy = Decal - > SceneProxy ;
if ( Proxy )
{
float CurrentTime = GetWorld ( ) - > GetTimeSeconds ( ) ;
float DecalFadeStartDelay = Decal - > FadeInStartDelay ;
float DecalFadeDuration = Decal - > FadeInDuration ;
ENQUEUE_RENDER_COMMAND ( FUpdateDecalFadeInTimeCommand ) (
[ Proxy , CurrentTime , DecalFadeStartDelay , DecalFadeDuration ] ( FRHICommandListImmediate & RHICmdList )
{
if ( DecalFadeDuration > 0.0f )
{
Proxy - > InvFadeInDuration = 1.0f / DecalFadeDuration ;
Proxy - > FadeInStartDelayNormalized = ( CurrentTime + DecalFadeStartDelay ) * - Proxy - > InvFadeInDuration ;
}
else
{
Proxy - > InvFadeInDuration = 1.0f ;
Proxy - > FadeInStartDelayNormalized = 0.0f ;
}
} ) ;
}
}
void FScene : : AddHairStrands ( FHairStrandsInstance * Proxy )
{
if ( Proxy )
{
check ( IsInRenderingThread ( ) ) ;
const int32 PackedIndex = HairStrandsSceneData . RegisteredProxies . Add ( Proxy ) ;
Proxy - > RegisteredIndex = PackedIndex ;
}
}
void FScene : : RemoveHairStrands ( FHairStrandsInstance * Proxy )
{
if ( Proxy )
{
check ( IsInRenderingThread ( ) ) ;
int32 ProxyIndex = Proxy - > RegisteredIndex ;
if ( HairStrandsSceneData . RegisteredProxies . IsValidIndex ( ProxyIndex ) )
{
HairStrandsSceneData . RegisteredProxies . RemoveAtSwap ( ProxyIndex ) ;
}
Proxy - > RegisteredIndex = - 1 ;
if ( HairStrandsSceneData . RegisteredProxies . IsValidIndex ( ProxyIndex ) )
{
FHairStrandsInstance * Other = HairStrandsSceneData . RegisteredProxies [ ProxyIndex ] ;
Other - > RegisteredIndex = ProxyIndex ;
}
}
}
void FScene : : GetRectLightAtlasSlot ( const FRectLightSceneProxy * Proxy , FLightRenderParameters * Out )
{
if ( Proxy )
{
check ( IsInRenderingThread ( ) ) ;
const RectLightAtlas : : FAtlasSlotDesc Slot = RectLightAtlas : : GetRectLightAtlasSlot ( Proxy - > AtlasSlotIndex ) ;
Out - > RectLightAtlasUVOffset = Slot . UVOffset ;
Out - > RectLightAtlasUVScale = Slot . UVScale ;
Out - > RectLightAtlasMaxLevel = Slot . MaxMipLevel ;
}
}
void FScene : : AddReflectionCapture ( UReflectionCaptureComponent * Component )
{
if ( ! Component - > SceneProxy )
{
Component - > SceneProxy = Component - > CreateSceneProxy ( ) ;
FScene * Scene = this ;
FReflectionCaptureProxy * Proxy = Component - > SceneProxy ;
const FVector Position = Component - > GetComponentLocation ( ) ;
ENQUEUE_RENDER_COMMAND ( FAddCaptureCommand )
( [ Scene , Proxy , Position ] ( FRHICommandListImmediate & RHICmdList )
{
if ( Proxy - > bUsingPreviewCaptureData )
{
FPlatformAtomics : : InterlockedIncrement ( & Scene - > NumUnbuiltReflectionCaptures ) ;
}
Scene - > ReflectionSceneData . bRegisteredReflectionCapturesHasChanged = true ;
const int32 PackedIndex = Scene - > ReflectionSceneData . RegisteredReflectionCaptures . Add ( Proxy ) ;
Proxy - > PackedIndex = PackedIndex ;
Scene - > ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius . Add ( FSphere ( Position , Proxy - > InfluenceRadius ) ) ;
if ( Scene - > GetFeatureLevel ( ) < = ERHIFeatureLevel : : ES3_1 )
{
Proxy - > UpdateMobileUniformBuffer ( ) ;
}
checkSlow ( Scene - > ReflectionSceneData . RegisteredReflectionCaptures . Num ( ) = = Scene - > ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius . Num ( ) ) ;
} ) ;
}
}
void FScene : : RemoveReflectionCapture ( UReflectionCaptureComponent * Component )
{
if ( Component - > SceneProxy )
{
FScene * Scene = this ;
FReflectionCaptureProxy * Proxy = Component - > SceneProxy ;
ENQUEUE_RENDER_COMMAND ( FRemoveCaptureCommand )
( [ Scene , Proxy ] ( FRHICommandListImmediate & RHICmdList )
{
if ( Proxy - > bUsingPreviewCaptureData )
{
FPlatformAtomics : : InterlockedDecrement ( & Scene - > NumUnbuiltReflectionCaptures ) ;
}
Scene - > ReflectionSceneData . bRegisteredReflectionCapturesHasChanged = true ;
// Need to clear out all reflection captures on removal to avoid dangling pointers.
for ( int32 PrimitiveIndex = 0 ; PrimitiveIndex < Scene - > Primitives . Num ( ) ; + + PrimitiveIndex )
{
Scene - > Primitives [ PrimitiveIndex ] - > RemoveCachedReflectionCaptures ( ) ;
}
int32 CaptureIndex = Proxy - > PackedIndex ;
Scene - > ReflectionSceneData . RegisteredReflectionCaptures . RemoveAtSwap ( CaptureIndex ) ;
Scene - > ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius . RemoveAtSwap ( CaptureIndex ) ;
if ( Scene - > ReflectionSceneData . RegisteredReflectionCaptures . IsValidIndex ( CaptureIndex ) )
{
FReflectionCaptureProxy * OtherCapture = Scene - > ReflectionSceneData . RegisteredReflectionCaptures [ CaptureIndex ] ;
OtherCapture - > PackedIndex = CaptureIndex ;
}
delete Proxy ;
checkSlow ( Scene - > ReflectionSceneData . RegisteredReflectionCaptures . Num ( ) = = Scene - > ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius . Num ( ) ) ;
} ) ;
// Disassociate the primitive's scene proxy.
Component - > SceneProxy = NULL ;
}
}
void FScene : : UpdateReflectionCaptureTransform ( UReflectionCaptureComponent * Component )
{
if ( Component - > SceneProxy )
{
const FReflectionCaptureMapBuildData * MapBuildData = Component - > GetMapBuildData ( ) ;
bool bUsingPreviewCaptureData = MapBuildData = = NULL ;
FScene * Scene = this ;
FReflectionCaptureProxy * Proxy = Component - > SceneProxy ;
FMatrix Transform = Component - > GetComponentTransform ( ) . ToMatrixWithScale ( ) ;
ENQUEUE_RENDER_COMMAND ( FUpdateTransformCommand )
( [ Scene , Proxy , Transform , bUsingPreviewCaptureData ] ( FRHICommandListImmediate & RHICmdList )
{
if ( Proxy - > bUsingPreviewCaptureData )
{
FPlatformAtomics : : InterlockedDecrement ( & Scene - > NumUnbuiltReflectionCaptures ) ;
}
Proxy - > bUsingPreviewCaptureData = bUsingPreviewCaptureData ;
if ( Proxy - > bUsingPreviewCaptureData )
{
FPlatformAtomics : : InterlockedIncrement ( & Scene - > NumUnbuiltReflectionCaptures ) ;
}
Scene - > ReflectionSceneData . bRegisteredReflectionCapturesHasChanged = true ;
Proxy - > SetTransform ( Transform ) ;
if ( Scene - > GetFeatureLevel ( ) < = ERHIFeatureLevel : : ES3_1 )
{
Proxy - > UpdateMobileUniformBuffer ( ) ;
}
} ) ;
}
}
void FScene : : ReleaseReflectionCubemap ( UReflectionCaptureComponent * CaptureComponent )
{
bool bRemoved = false ;
for ( TSparseArray < UReflectionCaptureComponent * > : : TIterator It ( ReflectionSceneData . AllocatedReflectionCapturesGameThread ) ; It ; + + It )
{
UReflectionCaptureComponent * CurrentCapture = * It ;
if ( CurrentCapture = = CaptureComponent )
{
It . RemoveCurrent ( ) ;
bRemoved = true ;
break ;
}
}
if ( bRemoved )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( RemoveCaptureCommand ) (
[ CaptureComponent , Scene ] ( FRHICommandListImmediate & RHICmdList )
{
int32 IndexToFree = - 1 ;
const FCaptureComponentSceneState * ComponentStatePtr = Scene - > ReflectionSceneData . AllocatedReflectionCaptureState . Find ( CaptureComponent ) ;
if ( ComponentStatePtr )
{
// We track removed captures so we can remap them when reallocating the cubemap array
check ( ComponentStatePtr - > CubemapIndex ! = - 1 ) ;
IndexToFree = ComponentStatePtr - > CubemapIndex ;
}
const bool bDidRemove = Scene - > ReflectionSceneData . AllocatedReflectionCaptureState . Remove ( CaptureComponent ) ;
if ( bDidRemove & & ( IndexToFree ! = - 1 ) )
{
Scene - > ReflectionSceneData . CubemapArraySlotsUsed [ IndexToFree ] = false ;
}
} ) ;
}
}
const FReflectionCaptureProxy * FScene : : FindClosestReflectionCapture ( FVector Position ) const
{
checkSlow ( IsInParallelRenderingThread ( ) ) ;
float ClosestDistanceSquared = FLT_MAX ;
int32 ClosestInfluencingCaptureIndex = INDEX_NONE ;
// Linear search through the scene's reflection captures
// ReflectionSceneData.RegisteredReflectionCapturePositionAndRadius has been packed densely to make this coherent in memory
for ( int32 CaptureIndex = 0 ; CaptureIndex < ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius . Num ( ) ; CaptureIndex + + )
{
const FSphere & ReflectionCapturePositionAndRadius = ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius [ CaptureIndex ] ;
const float DistanceSquared = ( ReflectionCapturePositionAndRadius . Center - Position ) . SizeSquared ( ) ;
// If the Position is inside the InfluenceRadius of a ReflectionCapture
if ( DistanceSquared < = FMath : : Square ( ReflectionCapturePositionAndRadius . W ) )
{
// Choose the closest ReflectionCapture or record the first one found.
if ( ClosestInfluencingCaptureIndex = = INDEX_NONE | | DistanceSquared < ClosestDistanceSquared )
{
ClosestDistanceSquared = DistanceSquared ;
ClosestInfluencingCaptureIndex = CaptureIndex ;
}
}
}
return ClosestInfluencingCaptureIndex ! = INDEX_NONE ? ReflectionSceneData . RegisteredReflectionCaptures [ ClosestInfluencingCaptureIndex ] : NULL ;
}
const FPlanarReflectionSceneProxy * FScene : : FindClosestPlanarReflection ( const FBoxSphereBounds & Bounds ) const
{
checkSlow ( IsInParallelRenderingThread ( ) ) ;
const FPlanarReflectionSceneProxy * ClosestPlanarReflection = NULL ;
float ClosestDistance = FLT_MAX ;
FBox PrimitiveBoundingBox ( Bounds . Origin - Bounds . BoxExtent , Bounds . Origin + Bounds . BoxExtent ) ;
// Linear search through the scene's planar reflections
for ( int32 CaptureIndex = 0 ; CaptureIndex < PlanarReflections . Num ( ) ; CaptureIndex + + )
{
FPlanarReflectionSceneProxy * CurrentPlanarReflection = PlanarReflections [ CaptureIndex ] ;
const FBox ReflectionBounds = CurrentPlanarReflection - > WorldBounds ;
if ( PrimitiveBoundingBox . Intersect ( ReflectionBounds ) )
{
const float Distance = FMath : : Abs ( CurrentPlanarReflection - > ReflectionPlane . PlaneDot ( Bounds . Origin ) ) ;
if ( Distance < ClosestDistance )
{
ClosestDistance = Distance ;
ClosestPlanarReflection = CurrentPlanarReflection ;
}
}
}
return ClosestPlanarReflection ;
}
const FPlanarReflectionSceneProxy * FScene : : GetForwardPassGlobalPlanarReflection ( ) const
{
// For the forward pass just pick first planar reflection.
if ( PlanarReflections . Num ( ) > 0 )
{
return PlanarReflections [ 0 ] ;
}
return nullptr ;
}
void FScene : : FindClosestReflectionCaptures ( FVector Position , const FReflectionCaptureProxy * ( & SortedByDistanceOUT ) [ FPrimitiveSceneInfo : : MaxCachedReflectionCaptureProxies ] ) const
{
checkSlow ( IsInParallelRenderingThread ( ) ) ;
static const int32 ArraySize = FPrimitiveSceneInfo : : MaxCachedReflectionCaptureProxies ;
struct FReflectionCaptureDistIndex
{
int32 CaptureIndex ;
float CaptureDistance ;
const FReflectionCaptureProxy * CaptureProxy ;
} ;
// Find the nearest n captures to this primitive.
const int32 NumRegisteredReflectionCaptures = ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius . Num ( ) ;
const int32 PopulateCaptureCount = FMath : : Min ( ArraySize , NumRegisteredReflectionCaptures ) ;
TArray < FReflectionCaptureDistIndex , TFixedAllocator < ArraySize > > ClosestCaptureIndices ;
ClosestCaptureIndices . AddUninitialized ( PopulateCaptureCount ) ;
for ( int32 CaptureIndex = 0 ; CaptureIndex < PopulateCaptureCount ; CaptureIndex + + )
{
ClosestCaptureIndices [ CaptureIndex ] . CaptureIndex = CaptureIndex ;
ClosestCaptureIndices [ CaptureIndex ] . CaptureDistance = ( ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius [ CaptureIndex ] . Center - Position ) . SizeSquared ( ) ;
}
for ( int32 CaptureIndex = PopulateCaptureCount ; CaptureIndex < NumRegisteredReflectionCaptures ; CaptureIndex + + )
{
const float DistanceSquared = ( ReflectionSceneData . RegisteredReflectionCapturePositionAndRadius [ CaptureIndex ] . Center - Position ) . SizeSquared ( ) ;
for ( int32 i = 0 ; i < ArraySize ; i + + )
{
if ( DistanceSquared < ClosestCaptureIndices [ i ] . CaptureDistance )
{
ClosestCaptureIndices [ i ] . CaptureDistance = DistanceSquared ;
ClosestCaptureIndices [ i ] . CaptureIndex = CaptureIndex ;
break ;
}
}
}
for ( int32 CaptureIndex = 0 ; CaptureIndex < PopulateCaptureCount ; CaptureIndex + + )
{
FReflectionCaptureProxy * CaptureProxy = ReflectionSceneData . RegisteredReflectionCaptures [ ClosestCaptureIndices [ CaptureIndex ] . CaptureIndex ] ;
ClosestCaptureIndices [ CaptureIndex ] . CaptureProxy = CaptureProxy ;
}
// Sort by influence radius.
ClosestCaptureIndices . Sort ( [ ] ( const FReflectionCaptureDistIndex & A , const FReflectionCaptureDistIndex & B )
{
if ( A . CaptureProxy - > InfluenceRadius ! = B . CaptureProxy - > InfluenceRadius )
{
return ( A . CaptureProxy - > InfluenceRadius < B . CaptureProxy - > InfluenceRadius ) ;
}
return A . CaptureProxy - > Guid < B . CaptureProxy - > Guid ;
} ) ;
FMemory : : Memzero ( SortedByDistanceOUT ) ;
for ( int32 CaptureIndex = 0 ; CaptureIndex < PopulateCaptureCount ; CaptureIndex + + )
{
SortedByDistanceOUT [ CaptureIndex ] = ClosestCaptureIndices [ CaptureIndex ] . CaptureProxy ;
}
}
int64 FScene : : GetCachedWholeSceneShadowMapsSize ( ) const
{
int64 CachedShadowmapMemory = 0 ;
for ( TMap < int32 , TArray < FCachedShadowMapData > > : : TConstIterator CachedShadowMapIt ( CachedShadowMaps ) ; CachedShadowMapIt ; + + CachedShadowMapIt )
{
const TArray < FCachedShadowMapData > & ShadowMapDatas = CachedShadowMapIt . Value ( ) ;
for ( const auto & ShadowMapData : ShadowMapDatas )
{
if ( ShadowMapData . ShadowMap . IsValid ( ) )
{
CachedShadowmapMemory + = ShadowMapData . ShadowMap . ComputeMemorySize ( ) ;
}
}
}
return CachedShadowmapMemory ;
}
void FScene : : AddPrecomputedLightVolume ( const FPrecomputedLightVolume * Volume )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( AddVolumeCommand )
( [ Scene , Volume ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > PrecomputedLightVolumes . Add ( Volume ) ;
Scene - > IndirectLightingCache . SetLightingCacheDirty ( Scene , Volume ) ;
} ) ;
}
void FScene : : RemovePrecomputedLightVolume ( const FPrecomputedLightVolume * Volume )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( RemoveVolumeCommand )
( [ Scene , Volume ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > PrecomputedLightVolumes . Remove ( Volume ) ;
Scene - > IndirectLightingCache . SetLightingCacheDirty ( Scene , Volume ) ;
} ) ;
}
void FVolumetricLightmapSceneData : : AddLevelVolume ( const FPrecomputedVolumetricLightmap * InVolume , EShadingPath ShadingPath , bool bIsPersistentLevel )
{
LevelVolumetricLightmaps . Add ( InVolume ) ;
if ( bIsPersistentLevel )
{
PersistentLevelVolumetricLightmap = InVolume ;
}
InVolume - > Data - > AddToSceneData ( & GlobalVolumetricLightmapData ) ;
// Invalidate CPU lightmap lookup cache
CPUInterpolationCache . Empty ( ) ;
}
void FVolumetricLightmapSceneData : : RemoveLevelVolume ( const FPrecomputedVolumetricLightmap * InVolume )
{
LevelVolumetricLightmaps . Remove ( InVolume ) ;
InVolume - > Data - > RemoveFromSceneData ( & GlobalVolumetricLightmapData , PersistentLevelVolumetricLightmap ? PersistentLevelVolumetricLightmap - > Data - > BrickDataBaseOffsetInAtlas : 0 ) ;
if ( PersistentLevelVolumetricLightmap = = InVolume )
{
PersistentLevelVolumetricLightmap = nullptr ;
}
// Invalidate CPU lightmap lookup cache
CPUInterpolationCache . Empty ( ) ;
}
const FPrecomputedVolumetricLightmap * FVolumetricLightmapSceneData : : GetLevelVolumetricLightmap ( ) const
{
# if WITH_EDITOR
if ( FStaticLightingSystemInterface : : GetPrecomputedVolumetricLightmap ( Scene - > GetWorld ( ) ) )
{
return FStaticLightingSystemInterface : : GetPrecomputedVolumetricLightmap ( Scene - > GetWorld ( ) ) ;
}
# endif
return & GlobalVolumetricLightmap ;
}
bool FVolumetricLightmapSceneData : : HasData ( ) const
{
# if WITH_EDITOR
if ( FStaticLightingSystemInterface : : GetPrecomputedVolumetricLightmap ( Scene - > GetWorld ( ) ) )
{
return true ;
}
# endif
if ( LevelVolumetricLightmaps . Num ( ) > 0 )
{
if ( Scene - > GetFeatureLevel ( ) > = ERHIFeatureLevel : : SM5 )
{
return GlobalVolumetricLightmapData . IndirectionTexture . Texture . IsValid ( ) ;
}
else
{
return GlobalVolumetricLightmapData . IndirectionTexture . Data . Num ( ) > 0 ;
}
}
return false ;
}
bool FScene : : HasPrecomputedVolumetricLightmap_RenderThread ( ) const
{
# if WITH_EDITOR
if ( FStaticLightingSystemInterface : : GetPrecomputedVolumetricLightmap ( GetWorld ( ) ) )
{
return true ;
}
# endif
return VolumetricLightmapSceneData . HasData ( ) ;
}
void FScene : : AddPrecomputedVolumetricLightmap ( const FPrecomputedVolumetricLightmap * Volume , bool bIsPersistentLevel )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( AddVolumeCommand )
( [ Scene , Volume , bIsPersistentLevel ] ( FRHICommandListImmediate & RHICmdList )
{
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if ( Volume & & Scene - > GetShadingPath ( ) = = EShadingPath : : Mobile )
{
const FPrecomputedVolumetricLightmapData * VolumeData = Volume - > Data ;
if ( VolumeData & & VolumeData - > BrickData . LQLightDirection . Data . Num ( ) = = 0 )
{
FPlatformAtomics : : InterlockedIncrement ( & Scene - > NumUncachedStaticLightingInteractions ) ;
}
}
# endif
Scene - > VolumetricLightmapSceneData . AddLevelVolume ( Volume , Scene - > GetShadingPath ( ) , bIsPersistentLevel ) ;
} ) ;
}
void FScene : : RemovePrecomputedVolumetricLightmap ( const FPrecomputedVolumetricLightmap * Volume )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( RemoveVolumeCommand )
( [ Scene , Volume ] ( FRHICommandListImmediate & RHICmdList )
{
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if ( Volume & & Scene - > GetShadingPath ( ) = = EShadingPath : : Mobile )
{
const FPrecomputedVolumetricLightmapData * VolumeData = Volume - > Data ;
if ( VolumeData & & VolumeData - > BrickData . LQLightDirection . Data . Num ( ) = = 0 )
{
FPlatformAtomics : : InterlockedDecrement ( & Scene - > NumUncachedStaticLightingInteractions ) ;
}
}
# endif
Scene - > VolumetricLightmapSceneData . RemoveLevelVolume ( Volume ) ;
} ) ;
}
void FScene : : AddRuntimeVirtualTexture ( class URuntimeVirtualTextureComponent * Component )
{
if ( Component - > SceneProxy = = nullptr )
{
Component - > SceneProxy = new FRuntimeVirtualTextureSceneProxy ( Component ) ;
FScene * Scene = this ;
FRuntimeVirtualTextureSceneProxy * SceneProxy = Component - > SceneProxy ;
ENQUEUE_RENDER_COMMAND ( AddRuntimeVirtualTextureCommand ) (
[ Scene , SceneProxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > AddRuntimeVirtualTexture_RenderThread ( SceneProxy ) ;
Scene - > UpdateRuntimeVirtualTextureForAllPrimitives_RenderThread ( ) ;
} ) ;
}
else
{
// This is a component update.
// Store the new FRuntimeVirtualTextureSceneProxy at the same index as the old (to avoid needing to update any associated primitives).
// Defer old proxy deletion to the render thread.
FRuntimeVirtualTextureSceneProxy * SceneProxyToReplace = Component - > SceneProxy ;
Component - > SceneProxy = new FRuntimeVirtualTextureSceneProxy ( Component ) ;
FScene * Scene = this ;
FRuntimeVirtualTextureSceneProxy * SceneProxy = Component - > SceneProxy ;
ENQUEUE_RENDER_COMMAND ( AddRuntimeVirtualTextureCommand ) (
[ Scene , SceneProxy , SceneProxyToReplace ] ( FRHICommandListImmediate & RHICmdList )
{
const bool bUpdatePrimitives = SceneProxy - > VirtualTexture ! = SceneProxyToReplace - > VirtualTexture ;
Scene - > UpdateRuntimeVirtualTexture_RenderThread ( SceneProxy , SceneProxyToReplace ) ;
if ( bUpdatePrimitives )
{
Scene - > UpdateRuntimeVirtualTextureForAllPrimitives_RenderThread ( ) ;
}
} ) ;
}
}
void FScene : : RemoveRuntimeVirtualTexture ( class URuntimeVirtualTextureComponent * Component )
{
FRuntimeVirtualTextureSceneProxy * SceneProxy = Component - > SceneProxy ;
if ( SceneProxy ! = nullptr )
{
// Release now but defer any deletion to the render thread
Component - > SceneProxy - > Release ( ) ;
Component - > SceneProxy = nullptr ;
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( RemoveRuntimeVirtualTextureCommand ) (
[ Scene , SceneProxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > RemoveRuntimeVirtualTexture_RenderThread ( SceneProxy ) ;
Scene - > UpdateRuntimeVirtualTextureForAllPrimitives_RenderThread ( ) ;
} ) ;
}
}
void FScene : : AddRuntimeVirtualTexture_RenderThread ( FRuntimeVirtualTextureSceneProxy * SceneProxy )
{
SceneProxy - > SceneIndex = RuntimeVirtualTextures . Add ( SceneProxy ) ;
const uint8 HideFlagBit = 1 < < SceneProxy - > SceneIndex ;
RuntimeVirtualTexturePrimitiveHideMaskEditor & = ~ HideFlagBit ;
RuntimeVirtualTexturePrimitiveHideMaskEditor | = ( SceneProxy - > bHidePrimitivesInEditor ? HideFlagBit : 0 ) ;
RuntimeVirtualTexturePrimitiveHideMaskGame & = ~ HideFlagBit ;
RuntimeVirtualTexturePrimitiveHideMaskGame | = ( SceneProxy - > bHidePrimitivesInGame ? HideFlagBit : 0 ) ;
}
void FScene : : UpdateRuntimeVirtualTexture_RenderThread ( FRuntimeVirtualTextureSceneProxy * SceneProxy , FRuntimeVirtualTextureSceneProxy * SceneProxyToReplace )
{
const uint8 HideFlagBit = 1 < < SceneProxy - > SceneIndex ;
RuntimeVirtualTexturePrimitiveHideMaskEditor & = ~ HideFlagBit ;
RuntimeVirtualTexturePrimitiveHideMaskEditor | = ( SceneProxy - > bHidePrimitivesInEditor ? HideFlagBit : 0 ) ;
RuntimeVirtualTexturePrimitiveHideMaskGame & = ~ HideFlagBit ;
RuntimeVirtualTexturePrimitiveHideMaskGame | = ( SceneProxy - > bHidePrimitivesInGame ? HideFlagBit : 0 ) ;
for ( TSparseArray < FRuntimeVirtualTextureSceneProxy * > : : TIterator It ( RuntimeVirtualTextures ) ; It ; + + It )
{
if ( * It = = SceneProxyToReplace )
{
SceneProxy - > SceneIndex = It . GetIndex ( ) ;
* It = SceneProxy ;
delete SceneProxyToReplace ;
return ;
}
}
// If we get here then we didn't find the object to replace!
check ( false ) ;
}
void FScene : : RemoveRuntimeVirtualTexture_RenderThread ( FRuntimeVirtualTextureSceneProxy * SceneProxy )
{
const uint8 HideFlagBit = 1 < < SceneProxy - > SceneIndex ;
RuntimeVirtualTexturePrimitiveHideMaskEditor & = ~ HideFlagBit ;
RuntimeVirtualTexturePrimitiveHideMaskGame & = ~ HideFlagBit ;
RuntimeVirtualTextures . RemoveAt ( SceneProxy - > SceneIndex ) ;
delete SceneProxy ;
}
void FScene : : UpdateRuntimeVirtualTextureForAllPrimitives_RenderThread ( )
{
for ( int32 Index = 0 ; Index < Primitives . Num ( ) ; + + Index )
{
if ( PrimitiveVirtualTextureFlags [ Index ] . bRenderToVirtualTexture )
{
Primitives [ Index ] - > UpdateRuntimeVirtualTextureFlags ( ) ;
PrimitiveVirtualTextureFlags [ Index ] = Primitives [ Index ] - > GetRuntimeVirtualTextureFlags ( ) ;
}
}
}
uint32 FScene : : GetRuntimeVirtualTextureSceneIndex ( uint32 ProducerId )
{
checkSlow ( IsInRenderingThread ( ) ) ;
for ( FRuntimeVirtualTextureSceneProxy const * Proxy : RuntimeVirtualTextures )
{
if ( Proxy - > ProducerId = = ProducerId )
{
return Proxy - > SceneIndex ;
}
}
// Should not get here
check ( false ) ;
return 0 ;
}
void FScene : : GetRuntimeVirtualTextureHidePrimitiveMask ( uint8 & bHideMaskEditor , uint8 & bHideMaskGame ) const
{
bHideMaskEditor = RuntimeVirtualTexturePrimitiveHideMaskEditor ;
bHideMaskGame = RuntimeVirtualTexturePrimitiveHideMaskGame ;
}
void FScene : : InvalidateRuntimeVirtualTexture ( class URuntimeVirtualTextureComponent * Component , FBoxSphereBounds const & WorldBounds )
{
if ( Component - > SceneProxy ! = nullptr )
{
FRuntimeVirtualTextureSceneProxy * SceneProxy = Component - > SceneProxy ;
ENQUEUE_RENDER_COMMAND ( RuntimeVirtualTextureComponent_SetDirty ) (
[ SceneProxy , WorldBounds ] ( FRHICommandList & RHICmdList )
{
SceneProxy - > Dirty ( WorldBounds ) ;
} ) ;
}
}
void FScene : : InvalidatePathTracedOutput ( )
{
// NOTE: this is an atomic, so this function is ok to call from any thread
+ + PathTracingInvalidationCounter ;
}
void FScene : : FlushDirtyRuntimeVirtualTextures ( )
{
checkSlow ( IsInRenderingThread ( ) ) ;
for ( TSparseArray < FRuntimeVirtualTextureSceneProxy * > : : TIterator It ( RuntimeVirtualTextures ) ; It ; + + It )
{
( * It ) - > FlushDirtyPages ( ) ;
}
}
bool FScene : : GetPreviousLocalToWorld ( const FPrimitiveSceneInfo * PrimitiveSceneInfo , FMatrix & OutPreviousLocalToWorld ) const
{
return VelocityData . GetComponentPreviousLocalToWorld ( PrimitiveSceneInfo - > PrimitiveComponentId , OutPreviousLocalToWorld ) ;
}
void FSceneVelocityData : : StartFrame ( FScene * Scene )
{
InternalFrameIndex + + ;
const bool bTrimOld = InternalFrameIndex % 100 = = 0 ;
for ( TMap < FPrimitiveComponentId , FComponentVelocityData > : : TIterator It ( ComponentData ) ; It ; + + It )
{
FComponentVelocityData & VelocityData = It . Value ( ) ;
VelocityData . PreviousLocalToWorld = VelocityData . LocalToWorld ;
VelocityData . bPreviousLocalToWorldValid = true ;
if ( ( InternalFrameIndex - VelocityData . LastFrameUpdated = = 1 ) & & VelocityData . PrimitiveSceneInfo )
{
// Recreate PrimitiveUniformBuffer on the frame after the primitive moved, since it contains PreviousLocalToWorld
VelocityData . PrimitiveSceneInfo - > SetNeedsUniformBufferUpdate ( true ) ;
}
if ( bTrimOld & & ( InternalFrameIndex - VelocityData . LastFrameUsed ) > 10 )
{
if ( VelocityData . PrimitiveSceneInfo )
{
Scene - > GPUScene . AddPrimitiveToUpdate ( VelocityData . PrimitiveSceneInfo - > GetIndex ( ) , EPrimitiveDirtyState : : ChangedOther ) ;
}
It . RemoveCurrent ( ) ;
}
}
}
void FScene : : GetPrimitiveUniformShaderParameters_RenderThread ( const FPrimitiveSceneInfo * PrimitiveSceneInfo , bool & bHasPrecomputedVolumetricLightmap , FMatrix & PreviousLocalToWorld , int32 & SingleCaptureIndex , bool & bOutputVelocity ) const
{
bHasPrecomputedVolumetricLightmap = VolumetricLightmapSceneData . HasData ( ) ;
bOutputVelocity = VelocityData . GetComponentPreviousLocalToWorld ( PrimitiveSceneInfo - > PrimitiveComponentId , PreviousLocalToWorld ) ;
if ( ! bOutputVelocity )
{
PreviousLocalToWorld = PrimitiveSceneInfo - > Proxy - > GetLocalToWorld ( ) ;
}
// Get index if proxy exists, otherwise fall back to index 0 which will contain the default black cubemap
SingleCaptureIndex = PrimitiveSceneInfo - > CachedReflectionCaptureProxy ? PrimitiveSceneInfo - > CachedReflectionCaptureProxy - > SortedCaptureIndex : 0 ;
}
struct FUpdateLightTransformParameters
{
FMatrix LightToWorld ;
FVector4 Position ;
} ;
void FScene : : UpdateLightTransform_RenderThread ( FLightSceneInfo * LightSceneInfo , const FUpdateLightTransformParameters & Parameters )
{
SCOPE_CYCLE_COUNTER ( STAT_UpdateSceneLightTime ) ;
SCOPED_NAMED_EVENT ( FScene_UpdateLightTransform_RenderThread , FColor : : Yellow ) ;
if ( LightSceneInfo & & LightSceneInfo - > bVisible )
{
// Don't remove directional lights when their transform changes as nothing in RemoveFromScene() depends on their transform
bool bRemove = ! ( LightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Directional ) ;
bool bHasId = LightSceneInfo - > Id ! = INDEX_NONE ;
if ( bRemove )
{
// Remove the light from the scene.
LightSceneInfo - > RemoveFromScene ( ) ;
}
// Invalidate the path tracer if the transform actually changed
// NOTE: Position is derived from the Matrix, so there is no need to check it separately
if ( ! Parameters . LightToWorld . Equals ( LightSceneInfo - > Proxy - > LightToWorld , SMALL_NUMBER ) )
{
InvalidatePathTracedOutput ( ) ;
}
// Update the light's transform and position.
LightSceneInfo - > Proxy - > SetTransform ( Parameters . LightToWorld , Parameters . Position ) ;
// Also update the LightSceneInfoCompact
if ( bHasId )
{
LightSceneInfo - > Scene - > Lights [ LightSceneInfo - > Id ] . Init ( LightSceneInfo ) ;
// Don't re-add directional lights when their transform changes as nothing in AddToScene() depends on their transform
if ( bRemove )
{
// Add the light to the scene at its new location.
LightSceneInfo - > AddToScene ( ) ;
}
}
}
}
void FScene : : UpdateLightTransform ( ULightComponent * Light )
{
if ( Light - > SceneProxy )
{
FUpdateLightTransformParameters Parameters ;
Parameters . LightToWorld = Light - > GetComponentTransform ( ) . ToMatrixNoScale ( ) ;
Parameters . Position = Light - > GetLightPosition ( ) ;
FScene * Scene = this ;
FLightSceneInfo * LightSceneInfo = Light - > SceneProxy - > GetLightSceneInfo ( ) ;
ENQUEUE_RENDER_COMMAND ( UpdateLightTransform ) (
[ Scene , LightSceneInfo , Parameters ] ( FRHICommandListImmediate & RHICmdList )
{
FScopeCycleCounter Context ( LightSceneInfo - > Proxy - > GetStatId ( ) ) ;
Scene - > UpdateLightTransform_RenderThread ( LightSceneInfo , Parameters ) ;
} ) ;
}
}
/**
* Updates the color and brightness of a light which has already been added to the scene .
*
* @ param Light - light component to update
*/
void FScene : : UpdateLightColorAndBrightness ( ULightComponent * Light )
{
if ( Light - > SceneProxy )
{
struct FUpdateLightColorParameters
{
FLinearColor NewColor ;
float NewIndirectLightingScale ;
float NewVolumetricScatteringIntensity ;
} ;
FUpdateLightColorParameters NewParameters ;
NewParameters . NewColor = FLinearColor ( Light - > LightColor ) * Light - > ComputeLightBrightness ( ) ;
NewParameters . NewIndirectLightingScale = Light - > IndirectLightingIntensity ;
NewParameters . NewVolumetricScatteringIntensity = Light - > VolumetricScatteringIntensity ;
if ( Light - > bUseTemperature )
{
NewParameters . NewColor * = FLinearColor : : MakeFromColorTemperature ( Light - > Temperature ) ;
}
FScene * Scene = this ;
FLightSceneInfo * LightSceneInfo = Light - > SceneProxy - > GetLightSceneInfo ( ) ;
ENQUEUE_RENDER_COMMAND ( UpdateLightColorAndBrightness ) (
[ LightSceneInfo , Scene , NewParameters ] ( FRHICommandListImmediate & RHICmdList )
{
if ( LightSceneInfo & & LightSceneInfo - > bVisible )
{
// Mobile renderer:
// a light with no color/intensity can cause the light to be ignored when rendering.
// thus, lights that change state in this way must update the draw lists.
Scene - > bScenesPrimitivesNeedStaticMeshElementUpdate =
Scene - > bScenesPrimitivesNeedStaticMeshElementUpdate | |
( Scene - > GetShadingPath ( ) = = EShadingPath : : Mobile
& & NewParameters . NewColor . IsAlmostBlack ( ) ! = LightSceneInfo - > Proxy - > GetColor ( ) . IsAlmostBlack ( ) ) ;
// Path Tracing: something about the light has changed, restart path traced accumulation
Scene - > InvalidatePathTracedOutput ( ) ;
LightSceneInfo - > Proxy - > SetColor ( NewParameters . NewColor ) ;
LightSceneInfo - > Proxy - > IndirectLightingScale = NewParameters . NewIndirectLightingScale ;
LightSceneInfo - > Proxy - > VolumetricScatteringIntensity = NewParameters . NewVolumetricScatteringIntensity ;
// Also update the LightSceneInfoCompact
if ( LightSceneInfo - > Id ! = INDEX_NONE )
{
Scene - > Lights [ LightSceneInfo - > Id ] . Color = NewParameters . NewColor ;
}
}
} ) ;
}
}
void FScene : : RemoveLightSceneInfo_RenderThread ( FLightSceneInfo * LightSceneInfo )
{
SCOPE_CYCLE_COUNTER ( STAT_RemoveSceneLightTime ) ;
SCOPED_NAMED_EVENT ( FScene_RemoveLightSceneInfo_RenderThread , FColor : : Red ) ;
if ( LightSceneInfo - > bVisible )
{
const bool bDirectionalLight = LightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Directional ;
if ( bDirectionalLight )
{
DirectionalLights . Remove ( LightSceneInfo ) ;
}
// check SimpleDirectionalLight
if ( LightSceneInfo = = SimpleDirectionalLight )
{
SimpleDirectionalLight = nullptr ;
}
if ( GetShadingPath ( ) = = EShadingPath : : Mobile )
{
const bool bUseCSMForDynamicObjects = LightSceneInfo - > Proxy - > UseCSMForDynamicObjects ( ) ;
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// Tracked for disabled shader permutation warnings.
// Condition must match that in AddLightSceneInfo_RenderThread
if ( LightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Directional & & ! LightSceneInfo - > Proxy - > HasStaticLighting ( ) )
{
if ( LightSceneInfo - > Proxy - > IsMovable ( ) )
{
NumMobileMovableDirectionalLights_RenderThread - - ;
}
if ( bUseCSMForDynamicObjects )
{
NumMobileStaticAndCSMLights_RenderThread - - ;
}
}
# endif
// check MobileDirectionalLights
for ( int32 LightChannelIdx = 0 ; LightChannelIdx < UE_ARRAY_COUNT ( MobileDirectionalLights ) ; LightChannelIdx + + )
{
if ( LightSceneInfo = = MobileDirectionalLights [ LightChannelIdx ] )
{
MobileDirectionalLights [ LightChannelIdx ] = nullptr ;
// find another light that could be the new MobileDirectionalLight for this channel
for ( const FLightSceneInfoCompact & OtherLight : Lights )
{
if ( OtherLight . LightSceneInfo ! = LightSceneInfo & &
OtherLight . LightType = = LightType_Directional & &
! OtherLight . bStaticLighting & &
GetFirstLightingChannelFromMask ( OtherLight . LightSceneInfo - > Proxy - > GetLightingChannelMask ( ) ) = = LightChannelIdx )
{
MobileDirectionalLights [ LightChannelIdx ] = OtherLight . LightSceneInfo ;
break ;
}
}
// if this light is a dynamic shadowcast then we need to update the static draw lists to pick a new lightingpolicy
if ( ! LightSceneInfo - > Proxy - > HasStaticShadowing ( ) | | bUseCSMForDynamicObjects )
{
bScenesPrimitivesNeedStaticMeshElementUpdate = true ;
}
break ;
}
}
}
ProcessAtmosphereLightRemoval_RenderThread ( LightSceneInfo ) ;
// Remove the light from the scene.
LightSceneInfo - > RemoveFromScene ( ) ;
// Remove the light from the lights list.
Lights . RemoveAt ( LightSceneInfo - > Id ) ;
if ( ! LightSceneInfo - > Proxy - > HasStaticShadowing ( )
& & LightSceneInfo - > Proxy - > CastsDynamicShadow ( )
& & LightSceneInfo - > GetDynamicShadowMapChannel ( ) = = - 1 )
{
OverflowingDynamicShadowedLights . Remove ( LightSceneInfo - > Proxy - > GetOwnerNameOrLabel ( ) ) ;
}
InvalidatePathTracedOutput ( ) ;
}
else
{
InvisibleLights . RemoveAt ( LightSceneInfo - > Id ) ;
}
if ( LightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Rect )
{
const FRectLightSceneProxy * RectProxy = ( const FRectLightSceneProxy * ) LightSceneInfo - > Proxy ;
RectLightAtlas : : RemoveRectLightTexture ( RectProxy - > AtlasSlotIndex ) ;
}
// Free the light scene info and proxy.
delete LightSceneInfo - > Proxy ;
delete LightSceneInfo ;
}
void FScene : : RemoveLight ( ULightComponent * Light )
{
if ( Light - > SceneProxy )
{
FLightSceneInfo * LightSceneInfo = Light - > SceneProxy - > GetLightSceneInfo ( ) ;
DEC_DWORD_STAT ( STAT_SceneLights ) ;
// Removing one visible light
- - NumVisibleLights_GameThread ;
// Disassociate the primitive's render info.
Light - > SceneProxy = NULL ;
// Send a command to the rendering thread to remove the light from the scene.
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FRemoveLightCommand ) (
[ Scene , LightSceneInfo ] ( FRHICommandListImmediate & RHICmdList )
{
FScopeCycleCounter Context ( LightSceneInfo - > Proxy - > GetStatId ( ) ) ;
Scene - > RemoveLightSceneInfo_RenderThread ( LightSceneInfo ) ;
} ) ;
}
}
void FScene : : AddExponentialHeightFog ( UExponentialHeightFogComponent * FogComponent )
{
FScene * Scene = this ;
FExponentialHeightFogSceneInfo HeightFogSceneInfo = FExponentialHeightFogSceneInfo ( FogComponent ) ;
ENQUEUE_RENDER_COMMAND ( FAddFogCommand ) (
[ Scene , HeightFogSceneInfo ] ( FRHICommandListImmediate & RHICmdList )
{
// Create a FExponentialHeightFogSceneInfo for the component in the scene's fog array.
new ( Scene - > ExponentialFogs ) FExponentialHeightFogSceneInfo ( HeightFogSceneInfo ) ;
Scene - > InvalidatePathTracedOutput ( ) ;
} ) ;
}
void FScene : : RemoveExponentialHeightFog ( UExponentialHeightFogComponent * FogComponent )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FRemoveFogCommand ) (
[ Scene , FogComponent ] ( FRHICommandListImmediate & RHICmdList )
{
// Remove the given component's FExponentialHeightFogSceneInfo from the scene's fog array.
for ( int32 FogIndex = 0 ; FogIndex < Scene - > ExponentialFogs . Num ( ) ; FogIndex + + )
{
if ( Scene - > ExponentialFogs [ FogIndex ] . Component = = FogComponent )
{
Scene - > ExponentialFogs . RemoveAt ( FogIndex ) ;
Scene - > InvalidatePathTracedOutput ( ) ;
break ;
}
}
} ) ;
}
bool FScene : : HasAnyExponentialHeightFog ( ) const
{
return this - > ExponentialFogs . Num ( ) > 0 ;
}
void FScene : : AddWindSource ( UWindDirectionalSourceComponent * WindComponent )
{
// if this wind component is not activated (or Auto Active is set to false), then don't add to WindSources
if ( ! WindComponent - > IsActive ( ) )
{
return ;
}
ensure ( IsInGameThread ( ) ) ;
WindComponents_GameThread . Add ( WindComponent ) ;
FWindSourceSceneProxy * SceneProxy = WindComponent - > CreateSceneProxy ( ) ;
WindComponent - > SceneProxy = SceneProxy ;
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FAddWindSourceCommand ) (
[ Scene , SceneProxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > WindSources . Add ( SceneProxy ) ;
} ) ;
}
void FScene : : RemoveWindSource ( UWindDirectionalSourceComponent * WindComponent )
{
ensure ( IsInGameThread ( ) ) ;
WindComponents_GameThread . Remove ( WindComponent ) ;
FWindSourceSceneProxy * SceneProxy = WindComponent - > SceneProxy ;
WindComponent - > SceneProxy = NULL ;
if ( SceneProxy )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FRemoveWindSourceCommand ) (
[ Scene , SceneProxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > WindSources . Remove ( SceneProxy ) ;
delete SceneProxy ;
} ) ;
}
}
void FScene : : UpdateWindSource ( UWindDirectionalSourceComponent * WindComponent )
{
// Recreate the scene proxy without touching WindComponents_GameThread
// so that this function is kept thread safe when iterating in parallel
// over components (unlike AddWindSource and RemoveWindSource)
FWindSourceSceneProxy * const OldSceneProxy = WindComponent - > SceneProxy ;
if ( OldSceneProxy )
{
WindComponent - > SceneProxy = nullptr ;
ENQUEUE_RENDER_COMMAND ( FRemoveWindSourceCommand ) (
[ Scene = this , OldSceneProxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > WindSources . Remove ( OldSceneProxy ) ;
delete OldSceneProxy ;
} ) ;
}
if ( WindComponent - > IsActive ( ) )
{
FWindSourceSceneProxy * const NewSceneProxy = WindComponent - > CreateSceneProxy ( ) ;
WindComponent - > SceneProxy = NewSceneProxy ;
ENQUEUE_RENDER_COMMAND ( FAddWindSourceCommand ) (
[ Scene = this , NewSceneProxy ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > WindSources . Add ( NewSceneProxy ) ;
} ) ;
}
}
const TArray < FWindSourceSceneProxy * > & FScene : : GetWindSources_RenderThread ( ) const
{
checkSlow ( IsInRenderingThread ( ) ) ;
return WindSources ;
}
void FScene : : GetWindParameters ( const FVector & Position , FVector & OutDirection , float & OutSpeed , float & OutMinGustAmt , float & OutMaxGustAmt ) const
{
FWindData AccumWindData ;
AccumWindData . PrepareForAccumulate ( ) ;
int32 NumActiveWindSources = 0 ;
FVector4f AccumulatedDirectionAndSpeed ( 0 , 0 , 0 , 0 ) ;
float TotalWeight = 0.0f ;
for ( int32 i = 0 ; i < WindSources . Num ( ) ; i + + )
{
FVector4f CurrentDirectionAndSpeed ;
float Weight ;
const FWindSourceSceneProxy * CurrentSource = WindSources [ i ] ;
FWindData CurrentSourceData ;
if ( CurrentSource - > GetWindParameters ( Position , CurrentSourceData , Weight ) )
{
AccumWindData . AddWeighted ( CurrentSourceData , Weight ) ;
TotalWeight + = Weight ;
NumActiveWindSources + + ;
}
}
AccumWindData . NormalizeByTotalWeight ( TotalWeight ) ;
if ( NumActiveWindSources = = 0 )
{
AccumWindData . Direction = FVector ( 1.0f , 0.0f , 0.0f ) ;
}
OutDirection = AccumWindData . Direction ;
OutSpeed = AccumWindData . Speed ;
OutMinGustAmt = AccumWindData . MinGustAmt ;
OutMaxGustAmt = AccumWindData . MaxGustAmt ;
}
void FScene : : GetWindParameters_GameThread ( const FVector & Position , FVector & OutDirection , float & OutSpeed , float & OutMinGustAmt , float & OutMaxGustAmt ) const
{
FWindData AccumWindData ;
AccumWindData . PrepareForAccumulate ( ) ;
const int32 NumSources = WindComponents_GameThread . Num ( ) ;
int32 NumActiveSources = 0 ;
float TotalWeight = 0.0f ;
// read the wind component array, this is safe for the game thread
for ( UWindDirectionalSourceComponent * Component : WindComponents_GameThread )
{
float Weight = 0.0f ;
FWindData CurrentComponentData ;
if ( Component & & Component - > GetWindParameters ( Position , CurrentComponentData , Weight ) )
{
AccumWindData . AddWeighted ( CurrentComponentData , Weight ) ;
TotalWeight + = Weight ;
+ + NumActiveSources ;
}
}
AccumWindData . NormalizeByTotalWeight ( TotalWeight ) ;
if ( NumActiveSources = = 0 )
{
AccumWindData . Direction = FVector ( 1.0f , 0.0f , 0.0f ) ;
}
OutDirection = AccumWindData . Direction ;
OutSpeed = AccumWindData . Speed ;
OutMinGustAmt = AccumWindData . MinGustAmt ;
OutMaxGustAmt = AccumWindData . MaxGustAmt ;
}
void FScene : : GetDirectionalWindParameters ( FVector & OutDirection , float & OutSpeed , float & OutMinGustAmt , float & OutMaxGustAmt ) const
{
FWindData AccumWindData ;
AccumWindData . PrepareForAccumulate ( ) ;
int32 NumActiveWindSources = 0 ;
FVector4f AccumulatedDirectionAndSpeed ( 0 , 0 , 0 , 0 ) ;
float TotalWeight = 0.0f ;
for ( int32 i = 0 ; i < WindSources . Num ( ) ; i + + )
{
FVector4f CurrentDirectionAndSpeed ;
float Weight ;
const FWindSourceSceneProxy * CurrentSource = WindSources [ i ] ;
FWindData CurrentSourceData ;
if ( CurrentSource - > GetDirectionalWindParameters ( CurrentSourceData , Weight ) )
{
AccumWindData . AddWeighted ( CurrentSourceData , Weight ) ;
TotalWeight + = Weight ;
NumActiveWindSources + + ;
}
}
AccumWindData . NormalizeByTotalWeight ( TotalWeight ) ;
if ( NumActiveWindSources = = 0 )
{
AccumWindData . Direction = FVector ( 1.0f , 0.0f , 0.0f ) ;
}
OutDirection = AccumWindData . Direction ;
OutSpeed = AccumWindData . Speed ;
OutMinGustAmt = AccumWindData . MinGustAmt ;
OutMaxGustAmt = AccumWindData . MaxGustAmt ;
}
void FScene : : AddSpeedTreeWind ( FVertexFactory * VertexFactory , const UStaticMesh * StaticMesh )
{
if ( StaticMesh ! = NULL & & StaticMesh - > SpeedTreeWind . IsValid ( ) & & StaticMesh - > GetRenderData ( ) )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FAddSpeedTreeWindCommand ) (
[ Scene , StaticMesh , VertexFactory ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > SpeedTreeVertexFactoryMap . Add ( VertexFactory , StaticMesh ) ;
if ( Scene - > SpeedTreeWindComputationMap . Contains ( StaticMesh ) )
{
( * ( Scene - > SpeedTreeWindComputationMap . Find ( StaticMesh ) ) ) - > ReferenceCount + + ;
}
else
{
FSpeedTreeWindComputation * WindComputation = new FSpeedTreeWindComputation ;
WindComputation - > Wind = * ( StaticMesh - > SpeedTreeWind . Get ( ) ) ;
FSpeedTreeUniformParameters UniformParameters ;
FPlatformMemory : : Memzero ( & UniformParameters , sizeof ( UniformParameters ) ) ;
WindComputation - > UniformBuffer = TUniformBufferRef < FSpeedTreeUniformParameters > : : CreateUniformBufferImmediate ( UniformParameters , UniformBuffer_MultiFrame ) ;
Scene - > SpeedTreeWindComputationMap . Add ( StaticMesh , WindComputation ) ;
}
} ) ;
}
}
void FScene : : RemoveSpeedTreeWind_RenderThread ( class FVertexFactory * VertexFactory , const class UStaticMesh * StaticMesh )
{
FSpeedTreeWindComputation * * WindComputationRef = SpeedTreeWindComputationMap . Find ( StaticMesh ) ;
if ( WindComputationRef ! = NULL )
{
FSpeedTreeWindComputation * WindComputation = * WindComputationRef ;
WindComputation - > ReferenceCount - - ;
if ( WindComputation - > ReferenceCount < 1 )
{
for ( auto Iter = SpeedTreeVertexFactoryMap . CreateIterator ( ) ; Iter ; + + Iter )
{
if ( Iter . Value ( ) = = StaticMesh )
{
Iter . RemoveCurrent ( ) ;
}
}
SpeedTreeWindComputationMap . Remove ( StaticMesh ) ;
delete WindComputation ;
}
}
}
void FScene : : UpdateSpeedTreeWind ( double CurrentTime )
{
# define SET_SPEEDTREE_TABLE_FLOAT4V(name, offset) \
UniformParameters . name = * ( FVector4f * ) ( WindShaderValues + FSpeedTreeWind : : offset ) ; \
UniformParameters . Prev # # name = * ( FVector4f * ) ( WindShaderValues + FSpeedTreeWind : : offset + FSpeedTreeWind : : NUM_SHADER_VALUES ) ;
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FUpdateSpeedTreeWindCommand ) (
[ Scene , CurrentTime ] ( FRHICommandListImmediate & RHICmdList )
{
FVector WindDirection ;
float WindSpeed ;
float WindMinGustAmt ;
float WindMaxGustAmt ;
Scene - > GetDirectionalWindParameters ( WindDirection , WindSpeed , WindMinGustAmt , WindMaxGustAmt ) ;
for ( TMap < const UStaticMesh * , FSpeedTreeWindComputation * > : : TIterator It ( Scene - > SpeedTreeWindComputationMap ) ; It ; + + It )
{
const UStaticMesh * StaticMesh = It . Key ( ) ;
FSpeedTreeWindComputation * WindComputation = It . Value ( ) ;
if ( ! ( StaticMesh - > GetRenderData ( ) & & StaticMesh - > SpeedTreeWind . IsValid ( ) ) )
{
It . RemoveCurrent ( ) ;
continue ;
}
if ( GIsEditor & & StaticMesh - > SpeedTreeWind - > NeedsReload ( ) )
{
// reload the wind since it may have changed or been scaled differently during reimport
StaticMesh - > SpeedTreeWind - > SetNeedsReload ( false ) ;
WindComputation - > Wind = * ( StaticMesh - > SpeedTreeWind . Get ( ) ) ;
}
// advance the wind object
WindComputation - > Wind . SetDirection ( WindDirection ) ;
WindComputation - > Wind . SetStrength ( WindSpeed ) ;
WindComputation - > Wind . SetGustMin ( WindMinGustAmt ) ;
WindComputation - > Wind . SetGustMax ( WindMaxGustAmt ) ;
WindComputation - > Wind . Advance ( true , CurrentTime ) ;
// copy data into uniform buffer
const float * WindShaderValues = WindComputation - > Wind . GetShaderTable ( ) ;
FSpeedTreeUniformParameters UniformParameters ;
UniformParameters . WindAnimation . Set ( CurrentTime , 0.0f , 0.0f , 0.0f ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindVector , SH_WIND_DIR_X ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindGlobal , SH_GLOBAL_TIME ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindBranch , SH_BRANCH_1_TIME ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindBranchTwitch , SH_BRANCH_1_TWITCH ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindBranchWhip , SH_BRANCH_1_WHIP ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindBranchAnchor , SH_WIND_ANCHOR_X ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindBranchAdherences , SH_GLOBAL_DIRECTION_ADHERENCE ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindTurbulences , SH_BRANCH_1_TURBULENCE ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindLeaf1Ripple , SH_LEAF_1_RIPPLE_TIME ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindLeaf1Tumble , SH_LEAF_1_TUMBLE_TIME ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindLeaf1Twitch , SH_LEAF_1_TWITCH_THROW ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindLeaf2Ripple , SH_LEAF_2_RIPPLE_TIME ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindLeaf2Tumble , SH_LEAF_2_TUMBLE_TIME ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindLeaf2Twitch , SH_LEAF_2_TWITCH_THROW ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindFrondRipple , SH_FROND_RIPPLE_TIME ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindRollingBranch , SH_ROLLING_BRANCH_FIELD_MIN ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindRollingLeafAndDirection , SH_ROLLING_LEAF_RIPPLE_MIN ) ;
SET_SPEEDTREE_TABLE_FLOAT4V ( WindRollingNoise , SH_ROLLING_NOISE_PERIOD ) ;
WindComputation - > UniformBuffer . UpdateUniformBufferImmediate ( UniformParameters ) ;
}
} ) ;
# undef SET_SPEEDTREE_TABLE_FLOAT4V
}
FRHIUniformBuffer * FScene : : GetSpeedTreeUniformBuffer ( const FVertexFactory * VertexFactory ) const
{
if ( VertexFactory ! = NULL )
{
const UStaticMesh * const * StaticMesh = SpeedTreeVertexFactoryMap . Find ( VertexFactory ) ;
if ( StaticMesh ! = NULL )
{
const FSpeedTreeWindComputation * const * WindComputation = SpeedTreeWindComputationMap . Find ( * StaticMesh ) ;
if ( WindComputation ! = NULL )
{
return ( * WindComputation ) - > UniformBuffer ;
}
}
}
return nullptr ;
}
/**
* Retrieves the lights interacting with the passed in primitive and adds them to the out array .
*
* Render thread version of function .
*
* @ param Primitive Primitive to retrieve interacting lights for
* @ param RelevantLights [ out ] Array of lights interacting with primitive
*/
void FScene : : GetRelevantLights_RenderThread ( UPrimitiveComponent * Primitive , TArray < const ULightComponent * > * RelevantLights ) const
{
check ( Primitive ) ;
check ( RelevantLights ) ;
if ( Primitive - > SceneProxy )
{
for ( const FLightPrimitiveInteraction * Interaction = Primitive - > SceneProxy - > GetPrimitiveSceneInfo ( ) - > LightList ; Interaction ; Interaction = Interaction - > GetNextLight ( ) )
{
RelevantLights - > Add ( Interaction - > GetLight ( ) - > Proxy - > GetLightComponent ( ) ) ;
}
}
}
/**
* Retrieves the lights interacting with the passed in primitive and adds them to the out array .
*
* @ param Primitive Primitive to retrieve interacting lights for
* @ param RelevantLights [ out ] Array of lights interacting with primitive
*/
void FScene : : GetRelevantLights ( UPrimitiveComponent * Primitive , TArray < const ULightComponent * > * RelevantLights ) const
{
if ( Primitive & & RelevantLights )
{
// Add interacting lights to the array.
const FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FGetRelevantLightsCommand ) (
[ Scene , Primitive , RelevantLights ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > GetRelevantLights_RenderThread ( Primitive , RelevantLights ) ;
} ) ;
// We need to block the main thread as the rendering thread needs to finish modifying the array before we can continue.
FlushRenderingCommands ( ) ;
}
}
/** Sets the precomputed visibility handler for the scene, or NULL to clear the current one. */
void FScene : : SetPrecomputedVisibility ( const FPrecomputedVisibilityHandler * NewPrecomputedVisibilityHandler )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( UpdatePrecomputedVisibility ) (
[ Scene , NewPrecomputedVisibilityHandler ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > PrecomputedVisibilityHandler = NewPrecomputedVisibilityHandler ;
} ) ;
}
void FScene : : UpdateStaticDrawLists_RenderThread ( FRHICommandListImmediate & RHICmdList )
{
SCOPE_CYCLE_COUNTER ( STAT_Scene_UpdateStaticDrawLists_RT ) ;
SCOPED_NAMED_EVENT ( FScene_UpdateStaticDrawLists_RenderThread , FColor : : Yellow ) ;
const int32 NumPrimitives = Primitives . Num ( ) ;
for ( int32 PrimitiveIndex = 0 ; PrimitiveIndex < NumPrimitives ; + + PrimitiveIndex )
{
FPrimitiveSceneInfo * Primitive = Primitives [ PrimitiveIndex ] ;
Primitive - > RemoveStaticMeshes ( ) ;
}
FPrimitiveSceneInfo : : AddStaticMeshes ( RHICmdList , this , Primitives ) ;
}
void FScene : : UpdateStaticDrawLists ( )
{
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FUpdateDrawLists ) (
[ Scene ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > UpdateStaticDrawLists_RenderThread ( RHICmdList ) ;
} ) ;
}
void FScene : : UpdateCachedRenderStates ( FPrimitiveSceneProxy * SceneProxy )
{
check ( IsInRenderingThread ( ) ) ;
if ( SceneProxy - > GetPrimitiveSceneInfo ( ) )
{
SceneProxy - > GetPrimitiveSceneInfo ( ) - > BeginDeferredUpdateStaticMeshes ( ) ;
}
}
# if RHI_RAYTRACING
void FScene : : UpdateCachedRayTracingState ( FPrimitiveSceneProxy * SceneProxy )
{
check ( IsInRenderingThread ( ) ) ;
if ( SceneProxy - > GetPrimitiveSceneInfo ( ) )
{
SceneProxy - > GetPrimitiveSceneInfo ( ) - > bCachedRaytracingDataDirty = true ;
// Clear the recounted pointer as well since we don't need it anymore
SceneProxy - > GetPrimitiveSceneInfo ( ) - > CachedRayTracingInstance . GeometryRHI = nullptr ;
}
}
# endif // RHI_RAYTRACING
/**
* @ return true if hit proxies should be rendered in this scene .
*/
bool FScene : : RequiresHitProxies ( ) const
{
return ( GIsEditor & & bRequiresHitProxies ) ;
}
void FScene : : Release ( )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FScene : : Release ) ;
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// Verify that no components reference this scene being destroyed
static bool bTriggeredOnce = false ;
if ( ! bTriggeredOnce )
{
for ( auto * ActorComponent : TObjectRange < UActorComponent > ( ) )
{
if ( ! ensureMsgf ( ! ActorComponent - > IsRegistered ( ) | | ActorComponent - > GetScene ( ) ! = this ,
TEXT ( " Component Name: %s World Name: %s Component Asset: %s " ) ,
* ActorComponent - > GetFullName ( ) ,
* GetWorld ( ) - > GetFullName ( ) ,
* ActorComponent - > AdditionalStatObject ( ) - > GetPathName ( ) ) )
{
bTriggeredOnce = true ;
break ;
}
}
}
# endif
GetRendererModule ( ) . RemoveScene ( this ) ;
// Send a command to the rendering thread to release the scene.
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FReleaseCommand ) (
[ Scene ] ( FRHICommandListImmediate & RHICmdList )
{
// Flush any remaining batched primitive update commands before deleting the scene.
Scene - > UpdateAllPrimitiveSceneInfos ( RHICmdList ) ;
delete Scene ;
} ) ;
}
bool ShouldForceFullDepthPass ( EShaderPlatform ShaderPlatform )
{
const bool bNaniteEnabled = UseNanite ( ShaderPlatform ) ;
const bool bDBufferAllowed = IsUsingDBuffers ( ShaderPlatform ) ;
static const auto StencilLODDitherCVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.StencilForLODDither " ) ) ;
const bool bStencilLODDither = StencilLODDitherCVar - > GetValueOnAnyThread ( ) ! = 0 ;
static const auto AOComputeCVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.AmbientOcclusion.Compute " ) ) ;
const bool bAOCompute = AOComputeCVar - > GetValueOnAnyThread ( ) > 0 ;
const bool bEarlyZMaterialMasking = MaskedInEarlyPass ( ShaderPlatform ) ;
// Note: ShouldForceFullDepthPass affects which static draw lists meshes go into, so nothing it depends on can change at runtime, unless you do a FGlobalComponentRecreateRenderStateContext to propagate the cvar change
return bNaniteEnabled | | bAOCompute | | bDBufferAllowed | | bStencilLODDither | | bEarlyZMaterialMasking | | IsForwardShadingEnabled ( ShaderPlatform ) | | IsUsingSelectiveBasePassOutputs ( ShaderPlatform ) ;
}
void FScene : : UpdateEarlyZPassMode ( )
{
checkSlow ( IsInGameThread ( ) ) ;
DefaultBasePassDepthStencilAccess = FExclusiveDepthStencil : : DepthWrite_StencilWrite ;
EarlyZPassMode = DDM_NonMaskedOnly ;
bEarlyZPassMovable = false ;
if ( GetShadingPath ( GetFeatureLevel ( ) ) = = EShadingPath : : Deferred )
{
// developer override, good for profiling, can be useful as project setting
{
const int32 CVarValue = CVarEarlyZPass . GetValueOnAnyThread ( ) ;
switch ( CVarValue )
{
case 0 : EarlyZPassMode = DDM_None ; break ;
case 1 : EarlyZPassMode = DDM_NonMaskedOnly ; break ;
case 2 : EarlyZPassMode = DDM_AllOccluders ; break ;
case 3 : break ; // Note: 3 indicates "default behavior" and does not specify an override
}
}
const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform ( FeatureLevel ) ;
if ( ShouldForceFullDepthPass ( ShaderPlatform ) )
{
// DBuffer decals and stencil LOD dithering force a full prepass
const bool bDepthPassCanOutputVelocity = FVelocityRendering : : DepthPassCanOutputVelocity ( FeatureLevel ) ;
EarlyZPassMode = bDepthPassCanOutputVelocity ? DDM_AllOpaqueNoVelocity : DDM_AllOpaque ;
bEarlyZPassMovable = bDepthPassCanOutputVelocity ? false : true ;
}
if ( ( EarlyZPassMode = = DDM_AllOpaque | | EarlyZPassMode = = DDM_AllOpaqueNoVelocity )
& & CVarBasePassWriteDepthEvenWithFullPrepass . GetValueOnAnyThread ( ) = = 0 )
{
DefaultBasePassDepthStencilAccess = FExclusiveDepthStencil : : DepthRead_StencilWrite ;
}
}
else if ( GetShadingPath ( GetFeatureLevel ( ) ) = = EShadingPath : : Mobile )
{
EarlyZPassMode = DDM_None ;
const EShaderPlatform ShaderPlatform = GetShaderPlatform ( ) ;
static const bool bMaskedInEarlyPass = MaskedInEarlyPass ( ShaderPlatform ) ;
if ( bMaskedInEarlyPass )
{
EarlyZPassMode = DDM_MaskedOnly ;
}
const bool bIsMobileAmbientOcclusionEnabled = IsMobileAmbientOcclusionEnabled ( ShaderPlatform ) ;
const bool bMobileUsesShadowMaskTexture = MobileUsesShadowMaskTexture ( ShaderPlatform ) ;
if ( bIsMobileAmbientOcclusionEnabled | | bMobileUsesShadowMaskTexture )
{
EarlyZPassMode = DDM_AllOpaque ;
}
bool bMobileForceFullDepthPrepass = CVarMobileEarlyZPass . GetValueOnAnyThread ( ) = = 1 ;
if ( bMobileForceFullDepthPrepass )
{
EarlyZPassMode = DDM_AllOpaque ;
}
}
}
void FScene : : ConditionalMarkStaticMeshElementsForUpdate ( )
{
if ( bScenesPrimitivesNeedStaticMeshElementUpdate
| | CachedDefaultBasePassDepthStencilAccess ! = DefaultBasePassDepthStencilAccess )
{
// Mark all primitives as needing an update
// Note: Only visible primitives will actually update their static mesh elements
for ( int32 PrimitiveIndex = 0 ; PrimitiveIndex < Primitives . Num ( ) ; PrimitiveIndex + + )
{
Primitives [ PrimitiveIndex ] - > BeginDeferredUpdateStaticMeshes ( ) ;
}
bScenesPrimitivesNeedStaticMeshElementUpdate = false ;
CachedDefaultBasePassDepthStencilAccess = DefaultBasePassDepthStencilAccess ;
}
}
void FScene : : DumpUnbuiltLightInteractions ( FOutputDevice & Ar ) const
{
FlushRenderingCommands ( ) ;
TSet < FString > LightsWithUnbuiltInteractions ;
TSet < FString > PrimitivesWithUnbuiltInteractions ;
// if want to print out all of the lights
for ( auto It = Lights . CreateConstIterator ( ) ; It ; + + It )
{
const FLightSceneInfoCompact & LightCompactInfo = * It ;
FLightSceneInfo * LightSceneInfo = LightCompactInfo . LightSceneInfo ;
bool bLightHasUnbuiltInteractions = false ;
for ( FLightPrimitiveInteraction * Interaction = LightSceneInfo - > GetDynamicInteractionOftenMovingPrimitiveList ( ) ;
Interaction ;
Interaction = Interaction - > GetNextPrimitive ( ) )
{
if ( Interaction - > IsUncachedStaticLighting ( ) )
{
bLightHasUnbuiltInteractions = true ;
PrimitivesWithUnbuiltInteractions . Add ( Interaction - > GetPrimitiveSceneInfo ( ) - > ComponentForDebuggingOnly - > GetFullName ( ) ) ;
}
}
for ( FLightPrimitiveInteraction * Interaction = LightSceneInfo - > GetDynamicInteractionStaticPrimitiveList ( ) ;
Interaction ;
Interaction = Interaction - > GetNextPrimitive ( ) )
{
if ( Interaction - > IsUncachedStaticLighting ( ) )
{
bLightHasUnbuiltInteractions = true ;
PrimitivesWithUnbuiltInteractions . Add ( Interaction - > GetPrimitiveSceneInfo ( ) - > ComponentForDebuggingOnly - > GetFullName ( ) ) ;
}
}
if ( bLightHasUnbuiltInteractions )
{
LightsWithUnbuiltInteractions . Add ( LightSceneInfo - > Proxy - > GetOwnerNameOrLabel ( ) ) ;
}
}
Ar . Logf ( TEXT ( " DumpUnbuiltLightIteractions " ) ) ;
Ar . Logf ( TEXT ( " Lights with unbuilt interactions: %d " ) , LightsWithUnbuiltInteractions . Num ( ) ) ;
for ( auto & LightName : LightsWithUnbuiltInteractions )
{
Ar . Logf ( TEXT ( " Light %s " ) , * LightName ) ;
}
Ar . Logf ( TEXT ( " " ) ) ;
Ar . Logf ( TEXT ( " Primitives with unbuilt interactions: %d " ) , PrimitivesWithUnbuiltInteractions . Num ( ) ) ;
for ( auto & PrimitiveName : PrimitivesWithUnbuiltInteractions )
{
Ar . Logf ( TEXT ( " Primitive %s " ) , * PrimitiveName ) ;
}
}
/**
* Exports the scene .
*
* @ param Ar The Archive used for exporting .
* */
void FScene : : Export ( FArchive & Ar ) const
{
}
void FScene : : ApplyWorldOffset ( FVector InOffset )
{
// Send a command to the rendering thread to shift scene data
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FApplyWorldOffset ) (
[ Scene , InOffset ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > UpdateAllPrimitiveSceneInfos ( RHICmdList ) ;
Scene - > ApplyWorldOffset_RenderThread ( InOffset ) ;
} ) ;
}
void FScene : : ApplyWorldOffset_RenderThread ( const FVector & InOffset )
{
QUICK_SCOPE_CYCLE_COUNTER ( STAT_SceneApplyWorldOffset ) ;
SCOPED_NAMED_EVENT ( FScene_ApplyWorldOffset_RenderThread , FColor : : Yellow ) ;
GPUScene . bUpdateAllPrimitives = true ;
// Primitives
checkf ( AddedPrimitiveSceneInfos . Num ( ) = = 0 , TEXT ( " All primitives found in AddedPrimitiveSceneInfos must have been added to the scene before the world offset is applied " ) ) ;
for ( int32 Idx = 0 ; Idx < Primitives . Num ( ) ; + + Idx )
{
Primitives [ Idx ] - > ApplyWorldOffset ( InOffset ) ;
}
// Primitive transforms
for ( int32 Idx = 0 ; Idx < PrimitiveTransforms . Num ( ) ; + + Idx )
{
PrimitiveTransforms [ Idx ] . SetOrigin ( PrimitiveTransforms [ Idx ] . GetOrigin ( ) + InOffset ) ;
}
// Primitive bounds
for ( int32 Idx = 0 ; Idx < PrimitiveBounds . Num ( ) ; + + Idx )
{
PrimitiveBounds [ Idx ] . BoxSphereBounds . Origin + = InOffset ;
}
# if RHI_RAYTRACING
for ( auto & BoundsPair : PrimitiveRayTracingGroups )
{
BoundsPair . Value . Bounds . Origin + = InOffset ;
}
# endif
// Primitive occlusion bounds
for ( int32 Idx = 0 ; Idx < PrimitiveOcclusionBounds . Num ( ) ; + + Idx )
{
PrimitiveOcclusionBounds [ Idx ] . Origin + = InOffset ;
}
// Precomputed light volumes
for ( const FPrecomputedLightVolume * It : PrecomputedLightVolumes )
{
const_cast < FPrecomputedLightVolume * > ( It ) - > ApplyWorldOffset ( InOffset ) ;
}
// Precomputed visibility
if ( PrecomputedVisibilityHandler )
{
const_cast < FPrecomputedVisibilityHandler * > ( PrecomputedVisibilityHandler ) - > ApplyWorldOffset ( InOffset ) ;
}
// Invalidate indirect lighting cache
IndirectLightingCache . SetLightingCacheDirty ( this , NULL ) ;
// Primitives octree
PrimitiveOctree . ApplyOffset ( InOffset , /*bGlobalOctee*/ true ) ;
// Lights
VectorRegister OffsetReg = VectorLoadFloat3_W0 ( & InOffset ) ;
for ( auto It = Lights . CreateIterator ( ) ; It ; + + It )
{
( * It ) . BoundingSphereVector = VectorAdd ( ( * It ) . BoundingSphereVector , OffsetReg ) ;
( * It ) . LightSceneInfo - > Proxy - > ApplyWorldOffset ( InOffset ) ;
}
LocalShadowCastingLightOctree . ApplyOffset ( InOffset , /*bGlobalOctee*/ true ) ;
// Cached preshadows
for ( auto It = CachedPreshadows . CreateIterator ( ) ; It ; + + It )
{
( * It ) - > PreShadowTranslation - = InOffset ;
( * It ) - > ShadowBounds . Center + = InOffset ;
}
// Decals
for ( auto It = Decals . CreateIterator ( ) ; It ; + + It )
{
( * It ) - > ComponentTrans . AddToTranslation ( InOffset ) ;
}
// Wind sources
for ( auto It = WindSources . CreateIterator ( ) ; It ; + + It )
{
( * It ) - > ApplyWorldOffset ( InOffset ) ;
}
// Reflection captures
for ( auto It = ReflectionSceneData . RegisteredReflectionCaptures . CreateIterator ( ) ; It ; + + It )
{
FMatrix NewTransform = FMatrix ( ( * It ) - > BoxTransform . Inverse ( ) . ConcatTranslation ( ( FVector3f ) InOffset ) ) ;
( * It ) - > SetTransform ( NewTransform ) ;
}
// Planar reflections
for ( auto It = PlanarReflections . CreateIterator ( ) ; It ; + + It )
{
( * It ) - > ApplyWorldOffset ( InOffset ) ;
}
// Exponential Fog
for ( FExponentialHeightFogSceneInfo & FogInfo : ExponentialFogs )
{
for ( FExponentialHeightFogSceneInfo : : FExponentialHeightFogSceneData & FogData : FogInfo . FogData )
{
FogData . Height + = InOffset . Z ;
}
}
// SkyAtmospheres
for ( FSkyAtmosphereSceneProxy * SkyAtmosphereProxy : SkyAtmosphereStack )
{
SkyAtmosphereProxy - > ApplyWorldOffset ( ( FVector3f ) InOffset ) ;
}
VelocityData . ApplyOffset ( InOffset ) ;
}
void FScene : : OnLevelAddedToWorld ( FName LevelAddedName , UWorld * InWorld , bool bIsLightingScenario )
{
if ( bIsLightingScenario )
{
InWorld - > PropagateLightingScenarioChange ( ) ;
}
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FLevelAddedToWorld ) (
[ Scene , LevelAddedName ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > UpdateAllPrimitiveSceneInfos ( RHICmdList ) ;
Scene - > OnLevelAddedToWorld_RenderThread ( LevelAddedName ) ;
} ) ;
}
void FScene : : OnLevelAddedToWorld_RenderThread ( FName InLevelName )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FScene : : OnLevelAddedToWorld_RenderThread ) ;
// Mark level primitives
TArray < FPrimitiveSceneInfo * > PrimitivesToAdd ;
if ( const TArray < FPrimitiveSceneInfo * > * LevelPrimitives = PrimitivesNeedingLevelUpdateNotification . Find ( InLevelName ) )
{
for ( FPrimitiveSceneInfo * Primitive : * LevelPrimitives )
{
// If the primitive proxy returns true, it needs it's static meshes added to the scene
if ( Primitive - > Proxy - > OnLevelAddedToWorld_RenderThread ( ) )
{
// Remove static meshes and cached commands for any primitives that need to be added
Primitive - > RemoveStaticMeshes ( ) ;
PrimitivesToAdd . Add ( Primitive ) ;
}
// Invalidate primitive proxy entry in GPU Scene.
// This is necessary for Nanite::FSceneProxy to be uploaded to GPU scene (see GetPrimitiveID in GPUScene.cpp)
if ( Primitive - > Proxy - > IsNaniteMesh ( ) )
{
Primitive - > RequestGPUSceneUpdate ( ) ;
}
}
}
FPrimitiveSceneInfo : : AddStaticMeshes ( FRHICommandListExecutor : : GetImmediateCommandList ( ) , this , PrimitivesToAdd ) ;
}
void FScene : : OnLevelRemovedFromWorld ( FName LevelRemovedName , UWorld * InWorld , bool bIsLightingScenario )
{
if ( bIsLightingScenario )
{
InWorld - > PropagateLightingScenarioChange ( ) ;
}
FScene * Scene = this ;
ENQUEUE_RENDER_COMMAND ( FLevelRemovedFromWorld ) (
[ Scene , LevelRemovedName ] ( FRHICommandListImmediate & RHICmdList )
{
Scene - > UpdateAllPrimitiveSceneInfos ( RHICmdList ) ;
Scene - > OnLevelRemovedFromWorld_RenderThread ( LevelRemovedName ) ;
} ) ;
}
void FScene : : OnLevelRemovedFromWorld_RenderThread ( FName InLevelName )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FScene : : OnLevelRemovedFromWorld_RenderThread ) ;
if ( TArray < FPrimitiveSceneInfo * > * LevelPrimitives = PrimitivesNeedingLevelUpdateNotification . Find ( InLevelName ) )
{
for ( FPrimitiveSceneInfo * Primitive : * LevelPrimitives )
{
Primitive - > Proxy - > OnLevelRemovedFromWorld_RenderThread ( ) ;
// Invalidate primitive proxy entry in GPU Scene.
// This is necessary for Nanite::FSceneProxy to be uploaded to GPU scene (see GetPrimitiveID in GPUScene.cpp)
if ( Primitive - > Proxy - > IsNaniteMesh ( ) )
{
Primitive - > RequestGPUSceneUpdate ( ) ;
}
}
}
}
void FScene : : ProcessAtmosphereLightAddition_RenderThread ( FLightSceneInfo * LightSceneInfo )
{
if ( LightSceneInfo - > Proxy - > IsUsedAsAtmosphereSunLight ( ) )
{
const uint8 Index = LightSceneInfo - > Proxy - > GetAtmosphereSunLightIndex ( ) ;
if ( ! AtmosphereLights [ Index ] | | // Set it if null
LightSceneInfo - > Proxy - > GetColor ( ) . GetLuminance ( ) > AtmosphereLights [ Index ] - > Proxy - > GetColor ( ) . GetLuminance ( ) ) // Or choose the brightest sun light
{
AtmosphereLights [ Index ] = LightSceneInfo ;
}
}
}
void FScene : : ProcessAtmosphereLightRemoval_RenderThread ( FLightSceneInfo * LightSceneInfo )
{
// When a light has its intensity or index changed, it will be removed first, then re-added. So we only need to check the index of the removed light.
const uint8 Index = LightSceneInfo - > Proxy - > GetAtmosphereSunLightIndex ( ) ;
if ( AtmosphereLights [ Index ] = = LightSceneInfo )
{
AtmosphereLights [ Index ] = nullptr ;
float SelectedLightLuminance = 0.0f ;
for ( auto It = Lights . CreateConstIterator ( ) ; It ; + + It )
{
const FLightSceneInfoCompact & LightInfo = * It ;
float LightLuminance = LightInfo . LightSceneInfo - > Proxy - > GetColor ( ) . GetLuminance ( ) ;
if ( LightInfo . LightSceneInfo ! = LightSceneInfo
& & LightInfo . LightSceneInfo - > Proxy - > IsUsedAsAtmosphereSunLight ( ) & & LightInfo . LightSceneInfo - > Proxy - > GetAtmosphereSunLightIndex ( ) = = Index
& & ( ! AtmosphereLights [ Index ] | | SelectedLightLuminance < LightLuminance ) )
{
AtmosphereLights [ Index ] = LightInfo . LightSceneInfo ;
SelectedLightLuminance = LightLuminance ;
}
}
}
}
# if WITH_EDITOR
bool FScene : : InitializePixelInspector ( FRenderTarget * BufferFinalColor , FRenderTarget * BufferSceneColor , FRenderTarget * BufferDepth , FRenderTarget * BufferHDR , FRenderTarget * BufferA , FRenderTarget * BufferBCDEF , int32 BufferIndex )
{
//Initialize the buffers
PixelInspectorData . InitializeBuffers ( BufferFinalColor , BufferSceneColor , BufferDepth , BufferHDR , BufferA , BufferBCDEF , BufferIndex ) ;
//return true when the interface is implemented
return true ;
}
bool FScene : : AddPixelInspectorRequest ( FPixelInspectorRequest * PixelInspectorRequest )
{
return PixelInspectorData . AddPixelInspectorRequest ( PixelInspectorRequest ) ;
}
# endif //WITH_EDITOR
struct FPrimitiveArraySortKey
{
inline bool operator ( ) ( const FPrimitiveSceneInfo & A , const FPrimitiveSceneInfo & B ) const
{
uint32 AHash = A . Proxy - > GetTypeHash ( ) ;
uint32 BHash = B . Proxy - > GetTypeHash ( ) ;
if ( AHash = = BHash )
{
return A . RegistrationSerialNumber < B . RegistrationSerialNumber ;
}
else
{
return AHash < BHash ;
}
}
} ;
static bool ShouldPrimitiveOutputVelocity ( const FPrimitiveSceneProxy * Proxy , const FStaticShaderPlatform ShaderPlatform )
{
bool bShouldPrimitiveOutputVelocity = Proxy - > HasDynamicTransform ( ) ;
bool bPlatformSupportsVelocityRendering = PlatformSupportsVelocityRendering ( ShaderPlatform ) ;
return bPlatformSupportsVelocityRendering & & bShouldPrimitiveOutputVelocity ;
}
void FScene : : UpdatePrimitiveVelocityState_RenderThread ( FPrimitiveSceneInfo * PrimitiveSceneInfo , bool bIsBeingMoved )
{
if ( bIsBeingMoved )
{
if ( ShouldPrimitiveOutputVelocity ( PrimitiveSceneInfo - > Proxy , GetShaderPlatform ( ) ) )
{
PrimitiveSceneInfo - > bRegisteredWithVelocityData = true ;
// We must register the initial LocalToWorld with the velocity state.
int32 PrimitiveIndex = PrimitiveSceneInfo - > PackedIndex ;
VelocityData . UpdateTransform ( PrimitiveSceneInfo , PrimitiveTransforms [ PrimitiveIndex ] , PrimitiveTransforms [ PrimitiveIndex ] ) ;
}
}
else if ( PrimitiveSceneInfo - > bRegisteredWithVelocityData )
{
PrimitiveSceneInfo - > bRegisteredWithVelocityData = false ;
VelocityData . RemoveFromScene ( PrimitiveSceneInfo - > PrimitiveComponentId , true ) ;
}
}
# if RHI_RAYTRACING
void FScene : : UpdateRayTracingGroupBounds_AddPrimitives ( const Experimental : : TRobinHoodHashSet < FPrimitiveSceneInfo * > & PrimitiveSceneInfos )
{
for ( FPrimitiveSceneInfo * const PrimitiveSceneInfo : PrimitiveSceneInfos )
{
const int32 GroupId = PrimitiveSceneInfo - > Proxy - > GetRayTracingGroupId ( ) ;
if ( GroupId ! = - 1 )
{
bool bInMap = false ;
static const FRayTracingCullingGroup DefaultGroup ;
FRayTracingCullingGroup * const Group = PrimitiveRayTracingGroups . FindOrAdd ( GroupId , DefaultGroup , bInMap ) ;
if ( bInMap )
{
Group - > Bounds = Group - > Bounds + PrimitiveSceneInfo - > Proxy - > GetBounds ( ) ;
Group - > MinDrawDistance = FMath : : Max ( Group - > MinDrawDistance , PrimitiveSceneInfo - > Proxy - > GetMinDrawDistance ( ) ) ;
}
else
{
Group - > Bounds = PrimitiveSceneInfo - > Proxy - > GetBounds ( ) ;
Group - > MinDrawDistance = PrimitiveSceneInfo - > Proxy - > GetMinDrawDistance ( ) ;
}
Group - > Primitives . Add ( PrimitiveSceneInfo ) ;
}
}
}
static void UpdateRayTracingGroupBounds ( Experimental : : TRobinHoodHashSet < FScene : : FRayTracingCullingGroup * > & GroupsToUpdate )
{
for ( FScene : : FRayTracingCullingGroup * const Group : GroupsToUpdate )
{
bool bFirstBounds = false ;
for ( FPrimitiveSceneInfo * const Primitive : Group - > Primitives )
{
if ( ! bFirstBounds )
{
Group - > Bounds = Primitive - > Proxy - > GetBounds ( ) ;
bFirstBounds = true ;
}
else
{
Group - > Bounds = Group - > Bounds + Primitive - > Proxy - > GetBounds ( ) ;
}
}
}
}
void FScene : : UpdateRayTracingGroupBounds_RemovePrimitives ( const Experimental : : TRobinHoodHashSet < FPrimitiveSceneInfo * > & PrimitiveSceneInfos )
{
Experimental : : TRobinHoodHashSet < FRayTracingCullingGroup * > GroupsToUpdate ;
for ( FPrimitiveSceneInfo * const PrimitiveSceneInfo : PrimitiveSceneInfos )
{
const int32 RayTracingGroupId = PrimitiveSceneInfo - > Proxy - > GetRayTracingGroupId ( ) ;
const Experimental : : FHashElementId GroupId = ( RayTracingGroupId ! = - 1 ) ? PrimitiveRayTracingGroups . FindId ( RayTracingGroupId ) : Experimental : : FHashElementId ( ) ;
if ( GroupId . IsValid ( ) )
{
FRayTracingCullingGroup & Group = PrimitiveRayTracingGroups . GetByElementId ( GroupId ) . Value ;
Group . Primitives . RemoveSingleSwap ( PrimitiveSceneInfo ) ;
if ( Group . Primitives . Num ( ) = = 0 )
{
PrimitiveRayTracingGroups . RemoveByElementId ( GroupId ) ;
}
else
{
GroupsToUpdate . FindOrAdd ( & Group ) ;
}
}
}
UpdateRayTracingGroupBounds ( GroupsToUpdate ) ;
}
template < typename ValueType >
inline void FScene : : UpdateRayTracingGroupBounds_UpdatePrimitives ( const Experimental : : TRobinHoodHashMap < FPrimitiveSceneProxy * , ValueType > & InUpdatedTransforms )
{
Experimental : : TRobinHoodHashSet < FRayTracingCullingGroup * > GroupsToUpdate ;
for ( const auto & Transform : InUpdatedTransforms )
{
FPrimitiveSceneProxy * const PrimitiveSceneProxy = Transform . Key ;
const int32 RayTracingGroupId = PrimitiveSceneProxy - > GetRayTracingGroupId ( ) ;
const Experimental : : FHashElementId GroupId = ( RayTracingGroupId ! = - 1 ) ? PrimitiveRayTracingGroups . FindId ( RayTracingGroupId ) : Experimental : : FHashElementId ( ) ;
if ( GroupId . IsValid ( ) )
{
FRayTracingCullingGroup & Group = PrimitiveRayTracingGroups . GetByElementId ( GroupId ) . Value ;
GroupsToUpdate . FindOrAdd ( & Group ) ;
}
}
UpdateRayTracingGroupBounds ( GroupsToUpdate ) ;
}
# endif
static inline bool IsPrimitiveRelevantToPathTracing ( FPrimitiveSceneInfo * PrimitiveSceneInfo )
{
# if RHI_RAYTRACING
// returns true if the primitive is likely to impact the path traced image
return PrimitiveSceneInfo - > bIsRayTracingRelevant & &
PrimitiveSceneInfo - > bIsVisibleInRayTracing & &
PrimitiveSceneInfo - > bDrawInGame & &
PrimitiveSceneInfo - > bShouldRenderInMainPass ;
# else
return false ;
# endif
}
void FScene : : UpdateAllPrimitiveSceneInfos ( FRDGBuilder & GraphBuilder , bool bAsyncCreateLPIs )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( Scene : : UpdateAllPrimitiveSceneInfos ) ;
SCOPED_NAMED_EVENT ( FScene_UpdateAllPrimitiveSceneInfos , FColor : : Orange ) ;
SCOPE_CYCLE_COUNTER ( STAT_UpdateScenePrimitiveRenderThreadTime ) ;
check ( IsInRenderingThread ( ) ) ;
FSceneRenderer : : WaitForCleanUpTasks ( GraphBuilder . RHICmdList ) ;
RDG_EVENT_SCOPE ( GraphBuilder , " UpdateAllPrimitiveSceneInfos " ) ;
# if RHI_RAYTRACING
UpdateRayTracingGroupBounds_RemovePrimitives ( RemovedPrimitiveSceneInfos ) ;
UpdateRayTracingGroupBounds_AddPrimitives ( AddedPrimitiveSceneInfos ) ;
# endif
TArray < FPrimitiveSceneInfo * > RemovedLocalPrimitiveSceneInfos ;
RemovedLocalPrimitiveSceneInfos . Reserve ( RemovedPrimitiveSceneInfos . Num ( ) ) ;
for ( FPrimitiveSceneInfo * SceneInfo : RemovedPrimitiveSceneInfos )
{
RemovedLocalPrimitiveSceneInfos . Add ( SceneInfo ) ;
}
// NOTE: We clear this early because IsPrimitiveBeingRemoved gets called from the CreateLightPrimitiveInteraction (to make sure that old primitives are not accessed)
// we cannot safely kick off the AsyncCreateLightPrimitiveInteractionsTask before the RemovedPrimitiveSceneInfos has been cleared.
RemovedPrimitiveSceneInfos . Empty ( ) ;
RemovedLocalPrimitiveSceneInfos . Sort ( FPrimitiveArraySortKey ( ) ) ;
TArray < FVirtualShadowMapArrayCacheManager * , SceneRenderingAllocator > VirtualShadowCacheManagers ;
GetAllVirtualShadowMapCacheManagers ( VirtualShadowCacheManagers ) ;
for ( FVirtualShadowMapArrayCacheManager * CacheManager : VirtualShadowCacheManagers )
{
FVirtualShadowMapArrayCacheManager : : FInvalidatingPrimitiveCollector InvalidatingPrimitiveCollector ( CacheManager ) ;
// All removed primitives must invalidate their footprints in the VSM before leaving
for ( const FPrimitiveSceneInfo * PrimitiveSceneInfo : RemovedLocalPrimitiveSceneInfos )
{
InvalidatingPrimitiveCollector . Add ( PrimitiveSceneInfo ) ;
}
// All updated instances must also before moving or re-allocating (TODO: filter out only those actually updated)
for ( const auto & Instance : UpdatedInstances )
{
InvalidatingPrimitiveCollector . Add ( Instance . Key - > GetPrimitiveSceneInfo ( ) ) ;
}
// As must all primitive updates,
for ( const auto & Transform : UpdatedTransforms )
{
InvalidatingPrimitiveCollector . Add ( Transform . Key - > GetPrimitiveSceneInfo ( ) ) ;
}
CacheManager - > ProcessRemovedOrUpdatedPrimitives ( GraphBuilder , GPUScene , InvalidatingPrimitiveCollector ) ;
}
TArray < FPrimitiveSceneInfo * > AddedLocalPrimitiveSceneInfos ;
AddedLocalPrimitiveSceneInfos . Reserve ( AddedPrimitiveSceneInfos . Num ( ) ) ;
for ( FPrimitiveSceneInfo * SceneInfo : AddedPrimitiveSceneInfos )
{
AddedLocalPrimitiveSceneInfos . Add ( SceneInfo ) ;
}
AddedLocalPrimitiveSceneInfos . Sort ( FPrimitiveArraySortKey ( ) ) ;
TSet < FPrimitiveSceneInfo * > DeletedSceneInfos ;
DeletedSceneInfos . Reserve ( RemovedLocalPrimitiveSceneInfos . Num ( ) ) ;
TArray < int32 > RemovedPrimitiveIndices ;
RemovedPrimitiveIndices . SetNumUninitialized ( RemovedLocalPrimitiveSceneInfos . Num ( ) ) ;
GPUScene . ResizeDirtyState ( Primitives . Num ( ) ) ;
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( RemovePrimitiveSceneInfos ) ;
SCOPED_NAMED_EVENT ( FScene_RemovePrimitiveSceneInfos , FColor : : Red ) ;
SCOPE_CYCLE_COUNTER ( STAT_RemoveScenePrimitiveTime ) ;
GPUScene . BeginDeferAllocatorMerges ( ) ;
for ( FPrimitiveSceneInfo * PrimitiveSceneInfo : RemovedLocalPrimitiveSceneInfos )
{
// clear it up, parent is getting removed
SceneLODHierarchy . UpdateNodeSceneInfo ( PrimitiveSceneInfo - > PrimitiveComponentId , nullptr ) ;
}
while ( RemovedLocalPrimitiveSceneInfos . Num ( ) )
{
int32 StartIndex = RemovedLocalPrimitiveSceneInfos . Num ( ) - 1 ;
SIZE_T InsertProxyHash = RemovedLocalPrimitiveSceneInfos [ StartIndex ] - > Proxy - > GetTypeHash ( ) ;
while ( StartIndex > 0 & & RemovedLocalPrimitiveSceneInfos [ StartIndex - 1 ] - > Proxy - > GetTypeHash ( ) = = InsertProxyHash )
{
StartIndex - - ;
}
int32 BroadIndex = - 1 ;
//broad phase search for a matching type
for ( BroadIndex = TypeOffsetTable . Num ( ) - 1 ; BroadIndex > = 0 ; BroadIndex - - )
{
// example how the prefix sum of the tails could look like
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,2,1,1,1,7,4,8]
// TypeOffsetTable[3,8,12,15,16,17,18]
if ( TypeOffsetTable [ BroadIndex ] . PrimitiveSceneProxyType = = InsertProxyHash )
{
const int32 InsertionOffset = TypeOffsetTable [ BroadIndex ] . Offset ;
const int32 PrevOffset = BroadIndex > 0 ? TypeOffsetTable [ BroadIndex - 1 ] . Offset : 0 ;
for ( int32 CheckIndex = StartIndex ; CheckIndex < RemovedLocalPrimitiveSceneInfos . Num ( ) ; CheckIndex + + )
{
int32 PrimitiveIndex = RemovedLocalPrimitiveSceneInfos [ CheckIndex ] - > PackedIndex ;
checkfSlow ( PrimitiveIndex > = PrevOffset & & PrimitiveIndex < InsertionOffset , TEXT ( " PrimitiveIndex %d not in Bucket Range [%d, %d] " ) , PrimitiveIndex , PrevOffset , InsertionOffset ) ;
}
break ;
}
}
{
SCOPED_NAMED_EVENT ( FScene_SwapPrimitiveSceneInfos , FColor : : Turquoise ) ;
for ( int32 CheckIndex = StartIndex ; CheckIndex < RemovedLocalPrimitiveSceneInfos . Num ( ) ; CheckIndex + + )
{
int32 SourceIndex = RemovedLocalPrimitiveSceneInfos [ CheckIndex ] - > PackedIndex ;
for ( int32 TypeIndex = BroadIndex ; TypeIndex < TypeOffsetTable . Num ( ) ; TypeIndex + + )
{
FTypeOffsetTableEntry & NextEntry = TypeOffsetTable [ TypeIndex ] ;
int DestIndex = - - NextEntry . Offset ; //decrement and prepare swap
// example swap chain of removing X
// PrimitiveSceneProxies[0,0,0,6,X,6,6,6,2,2,2,2,1,1,1,7,4,8]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,X,2,2,2,1,1,1,7,4,8]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,X,1,1,1,7,4,8]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,1,1,1,X,7,4,8]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,1,1,1,7,X,4,8]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,1,1,1,7,4,X,8]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,1,1,1,7,4,8,X]
if ( DestIndex ! = SourceIndex )
{
checkfSlow ( DestIndex > SourceIndex , TEXT ( " Corrupted Prefix Sum [%d, %d] " ) , DestIndex , SourceIndex ) ;
Primitives [ DestIndex ] - > PackedIndex = SourceIndex ;
Primitives [ SourceIndex ] - > PackedIndex = DestIndex ;
TArraySwapElements ( Primitives , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveTransforms , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveSceneProxies , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveBounds , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveFlagsCompact , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveVisibilityIds , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveOctreeIndex , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveOcclusionFlags , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveComponentIds , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveVirtualTextureFlags , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveVirtualTextureLod , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveOcclusionBounds , DestIndex , SourceIndex ) ;
# if WITH_EDITOR
TBitArraySwapElements ( PrimitivesSelected , DestIndex , SourceIndex ) ;
# endif
# if RHI_RAYTRACING
TArraySwapElements ( PrimitiveRayTracingFlags , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveRayTracingGroupIds , DestIndex , SourceIndex ) ;
# endif
TBitArraySwapElements ( PrimitivesNeedingStaticMeshUpdate , DestIndex , SourceIndex ) ;
for ( TMap < int32 , TArray < FCachedShadowMapData > > : : TIterator CachedShadowMapIt ( CachedShadowMaps ) ; CachedShadowMapIt ; + + CachedShadowMapIt )
{
TArray < FCachedShadowMapData > & ShadowMapDatas = CachedShadowMapIt . Value ( ) ;
for ( auto & ShadowMapData : ShadowMapDatas )
{
if ( ShadowMapData . StaticShadowSubjectMap . Num ( ) > 0 )
{
TBitArraySwapElements ( ShadowMapData . StaticShadowSubjectMap , DestIndex , SourceIndex ) ;
}
}
}
GPUScene . RecordPrimitiveIdSwap ( DestIndex , SourceIndex ) ;
# if RHI_RAYTRACING
// Update cached PrimitiveIndex after an index swap
Primitives [ SourceIndex ] - > CachedRayTracingInstance . DefaultUserData = SourceIndex ;
Primitives [ DestIndex ] - > CachedRayTracingInstance . DefaultUserData = DestIndex ;
# endif
SourceIndex = DestIndex ;
}
}
}
}
const int32 PreviousOffset = BroadIndex > 0 ? TypeOffsetTable [ BroadIndex - 1 ] . Offset : 0 ;
const int32 CurrentOffset = TypeOffsetTable [ BroadIndex ] . Offset ;
checkfSlow ( PreviousOffset < = CurrentOffset , TEXT ( " Corrupted Bucket [%d, %d] " ) , PreviousOffset , CurrentOffset ) ;
if ( CurrentOffset - PreviousOffset = = 0 )
{
// remove empty OffsetTable entries e.g.
// TypeOffsetTable[3,8,12,15,15,17,18]
// TypeOffsetTable[3,8,12,15,17,18]
TypeOffsetTable . RemoveAt ( BroadIndex ) ;
}
checkfSlow ( ( TypeOffsetTable . Num ( ) = = 0 & & Primitives . Num ( ) = = ( RemovedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ) ) | | TypeOffsetTable [ TypeOffsetTable . Num ( ) - 1 ] . Offset = = Primitives . Num ( ) - ( RemovedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ) , TEXT ( " Corrupted Tail Offset [%d, %d] " ) , TypeOffsetTable [ TypeOffsetTable . Num ( ) - 1 ] . Offset , Primitives . Num ( ) - ( RemovedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ) ) ;
for ( int32 RemoveIndex = StartIndex ; RemoveIndex < RemovedLocalPrimitiveSceneInfos . Num ( ) ; RemoveIndex + + )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = RemovedLocalPrimitiveSceneInfos [ RemoveIndex ] ;
checkf ( RemovedLocalPrimitiveSceneInfos [ RemoveIndex ] - > PackedIndex > = Primitives . Num ( ) - RemovedLocalPrimitiveSceneInfos . Num ( ) , TEXT ( " Removed item should be at the end " ) ) ;
// Store the previous index for use later, and set the PackedIndex member to invalid.
// FPrimitiveOctreeSemantics::SetOctreeNodeIndex will attempt to remove the node index from the
// PrimitiveOctreeIndex. Since the elements have already been swapped, this will cause an invalid change to PrimitiveOctreeIndex.
// Setting the packed index to INDEX_NONE prevents this from happening, but we also need to keep track of the old
// index for use below.
RemovedPrimitiveIndices [ RemoveIndex ] = RemovedLocalPrimitiveSceneInfos [ RemoveIndex ] - > PackedIndex ;
PrimitiveSceneInfo - > PackedIndex = INDEX_NONE ;
}
//Remove all items from the location of StartIndex to the end of the arrays.
int RemoveCount = RemovedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ;
int SourceIndex = Primitives . Num ( ) - RemoveCount ;
Primitives . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveTransforms . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveSceneProxies . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveBounds . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveFlagsCompact . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveVisibilityIds . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveOctreeIndex . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveOcclusionFlags . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveComponentIds . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveVirtualTextureFlags . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveVirtualTextureLod . RemoveAt ( SourceIndex , RemoveCount , false ) ;
PrimitiveOcclusionBounds . RemoveAt ( SourceIndex , RemoveCount , false ) ;
# if WITH_EDITOR
PrimitivesSelected . RemoveAt ( SourceIndex , RemoveCount ) ;
# endif
# if RHI_RAYTRACING
PrimitiveRayTracingFlags . RemoveAt ( SourceIndex , RemoveCount ) ;
PrimitiveRayTracingGroupIds . RemoveAt ( SourceIndex , RemoveCount ) ;
# endif
PrimitivesNeedingStaticMeshUpdate . RemoveAt ( SourceIndex , RemoveCount ) ;
CheckPrimitiveArrays ( ) ;
for ( int32 RemoveIndex = StartIndex ; RemoveIndex < RemovedLocalPrimitiveSceneInfos . Num ( ) ; RemoveIndex + + )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = RemovedLocalPrimitiveSceneInfos [ RemoveIndex ] ;
FScopeCycleCounter Context ( PrimitiveSceneInfo - > Proxy - > GetStatId ( ) ) ;
// The removed items PrimitiveIndex has already been invalidated, but a backup is kept in RemovedPrimitiveIndices
int32 PrimitiveIndex = RemovedPrimitiveIndices [ RemoveIndex ] ;
if ( PrimitiveSceneInfo - > bRegisteredWithVelocityData )
{
// Remove primitive's motion blur information.
VelocityData . RemoveFromScene ( PrimitiveSceneInfo - > PrimitiveComponentId , false ) ;
}
// Unlink the primitive from its shadow parent.
PrimitiveSceneInfo - > UnlinkAttachmentGroup ( ) ;
// Unlink the LOD parent info if valid
PrimitiveSceneInfo - > UnlinkLODParentComponent ( ) ;
// Flush virtual textures touched by primitive
PrimitiveSceneInfo - > FlushRuntimeVirtualTexture ( ) ;
// Remove the primitive from the scene.
PrimitiveSceneInfo - > RemoveFromScene ( true ) ;
PrimitiveSceneInfo - > FreeGPUSceneInstances ( ) ;
// Update the primitive that was swapped to this index
GPUScene . AddPrimitiveToUpdate ( PrimitiveIndex , EPrimitiveDirtyState : : Removed ) ;
DistanceFieldSceneData . RemovePrimitive ( PrimitiveSceneInfo ) ;
LumenSceneData - > RemovePrimitive ( PrimitiveSceneInfo , PrimitiveIndex ) ;
DeletedSceneInfos . Add ( PrimitiveSceneInfo ) ;
for ( const FLightSceneInfo * LightSceneInfo : DirectionalLights )
{
TArray < FCachedShadowMapData > * CachedShadowMapDatas = GetCachedShadowMapDatas ( LightSceneInfo - > Id ) ;
if ( CachedShadowMapDatas )
{
for ( auto & CachedShadowMapData : * CachedShadowMapDatas )
{
if ( CachedShadowMapData . StaticShadowSubjectMap [ PrimitiveIndex ] = = true )
{
CachedShadowMapData . InvalidateCachedShadow ( ) ;
}
}
}
}
PersistentPrimitiveIdAllocator . Free ( PrimitiveSceneInfo - > PersistentIndex . Index ) ;
}
for ( TMap < int32 , TArray < FCachedShadowMapData > > : : TIterator CachedShadowMapIt ( CachedShadowMaps ) ; CachedShadowMapIt ; + + CachedShadowMapIt )
{
TArray < FCachedShadowMapData > & ShadowMapDatas = CachedShadowMapIt . Value ( ) ;
for ( auto & ShadowMapData : ShadowMapDatas )
{
if ( ShadowMapData . StaticShadowSubjectMap . Num ( ) > 0 )
{
ShadowMapData . StaticShadowSubjectMap . RemoveAt ( SourceIndex , RemoveCount ) ;
check ( Primitives . Num ( ) = = ShadowMapData . StaticShadowSubjectMap . Num ( ) ) ;
}
}
}
RemovedLocalPrimitiveSceneInfos . RemoveAt ( StartIndex , RemovedLocalPrimitiveSceneInfos . Num ( ) - StartIndex , false ) ;
}
GPUScene . EndDeferAllocatorMerges ( ) ;
}
bool bNeedPathTracedInvalidation = false ;
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( AddPrimitiveSceneInfos ) ;
SCOPED_NAMED_EVENT ( FScene_AddPrimitiveSceneInfos , FColor : : Green ) ;
SCOPE_CYCLE_COUNTER ( STAT_AddScenePrimitiveRenderThreadTime ) ;
# if ENABLE_LOG_PRIMITIVE_INSTANCE_ID_STATS_TO_CSV
int32 PersistentPrimitiveFreeListSizeBeforeConsolidate = PersistentPrimitiveIdAllocator . GetNumFreeSpans ( ) ;
int32 PersistentPrimitivePendingFreeListSizeBeforeConsolidate = PersistentPrimitiveIdAllocator . GetNumPendingFreeSpans ( ) ;
# endif
PersistentPrimitiveIdAllocator . Consolidate ( ) ;
if ( AddedLocalPrimitiveSceneInfos . Num ( ) )
{
Primitives . Reserve ( Primitives . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveTransforms . Reserve ( PrimitiveTransforms . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveSceneProxies . Reserve ( PrimitiveSceneProxies . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveBounds . Reserve ( PrimitiveBounds . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveFlagsCompact . Reserve ( PrimitiveFlagsCompact . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveVisibilityIds . Reserve ( PrimitiveVisibilityIds . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveOcclusionFlags . Reserve ( PrimitiveOcclusionFlags . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveComponentIds . Reserve ( PrimitiveComponentIds . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveVirtualTextureFlags . Reserve ( PrimitiveVirtualTextureFlags . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveVirtualTextureLod . Reserve ( PrimitiveVirtualTextureLod . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveOcclusionBounds . Reserve ( PrimitiveOcclusionBounds . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
# if WITH_EDITOR
PrimitivesSelected . Reserve ( PrimitivesSelected . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
# endif
# if RHI_RAYTRACING
PrimitiveRayTracingFlags . Reserve ( PrimitiveRayTracingFlags . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
PrimitiveRayTracingGroupIds . Reserve ( PrimitiveRayTracingGroupIds . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
# endif
PrimitivesNeedingStaticMeshUpdate . Reserve ( PrimitivesNeedingStaticMeshUpdate . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
for ( TMap < int32 , TArray < FCachedShadowMapData > > : : TIterator CachedShadowMapIt ( CachedShadowMaps ) ; CachedShadowMapIt ; + + CachedShadowMapIt )
{
TArray < FCachedShadowMapData > & ShadowMapDatas = CachedShadowMapIt . Value ( ) ;
for ( auto & ShadowMapData : ShadowMapDatas )
{
if ( ShadowMapData . StaticShadowSubjectMap . Num ( ) > 0 )
{
ShadowMapData . StaticShadowSubjectMap . Reserve ( ShadowMapData . StaticShadowSubjectMap . Num ( ) + AddedLocalPrimitiveSceneInfos . Num ( ) ) ;
}
}
}
}
while ( AddedLocalPrimitiveSceneInfos . Num ( ) )
{
int32 StartIndex = AddedLocalPrimitiveSceneInfos . Num ( ) - 1 ;
SIZE_T InsertProxyHash = AddedLocalPrimitiveSceneInfos [ StartIndex ] - > Proxy - > GetTypeHash ( ) ;
while ( StartIndex > 0 & & AddedLocalPrimitiveSceneInfos [ StartIndex - 1 ] - > Proxy - > GetTypeHash ( ) = = InsertProxyHash )
{
StartIndex - - ;
}
for ( int32 AddIndex = StartIndex ; AddIndex < AddedLocalPrimitiveSceneInfos . Num ( ) ; AddIndex + + )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = AddedLocalPrimitiveSceneInfos [ AddIndex ] ;
Primitives . Add ( PrimitiveSceneInfo ) ;
const FMatrix LocalToWorld = PrimitiveSceneInfo - > Proxy - > GetLocalToWorld ( ) ;
PrimitiveTransforms . Add ( LocalToWorld ) ;
PrimitiveSceneProxies . Add ( PrimitiveSceneInfo - > Proxy ) ;
PrimitiveBounds . AddUninitialized ( ) ;
PrimitiveFlagsCompact . AddUninitialized ( ) ;
PrimitiveVisibilityIds . AddUninitialized ( ) ;
PrimitiveOctreeIndex . Add ( 0 ) ;
PrimitiveOcclusionFlags . AddUninitialized ( ) ;
PrimitiveComponentIds . AddUninitialized ( ) ;
PrimitiveVirtualTextureFlags . AddUninitialized ( ) ;
PrimitiveVirtualTextureLod . AddUninitialized ( ) ;
PrimitiveOcclusionBounds . AddUninitialized ( ) ;
# if WITH_EDITOR
PrimitivesSelected . Add ( PrimitiveSceneInfo - > Proxy - > IsSelected ( ) ) ;
# endif
# if RHI_RAYTRACING
PrimitiveRayTracingFlags . AddZeroed ( ) ;
PrimitiveRayTracingGroupIds . Add ( Experimental : : FHashElementId ( ) ) ;
# endif
PrimitivesNeedingStaticMeshUpdate . Add ( false ) ;
for ( TMap < int32 , TArray < FCachedShadowMapData > > : : TIterator CachedShadowMapIt ( CachedShadowMaps ) ; CachedShadowMapIt ; + + CachedShadowMapIt )
{
TArray < FCachedShadowMapData > & ShadowMapDatas = CachedShadowMapIt . Value ( ) ;
for ( auto & ShadowMapData : ShadowMapDatas )
{
if ( ShadowMapData . StaticShadowSubjectMap . Num ( ) > 0 )
{
ShadowMapData . StaticShadowSubjectMap . Add ( false ) ;
}
}
}
const int32 SourceIndex = PrimitiveSceneProxies . Num ( ) - 1 ;
PrimitiveSceneInfo - > PackedIndex = SourceIndex ;
checkSlow ( PrimitiveSceneInfo - > PersistentIndex . Index = = INDEX_NONE ) ;
PrimitiveSceneInfo - > PersistentIndex = FPersistentPrimitiveIndex { PersistentPrimitiveIdAllocator . Allocate ( ) } ;
GPUScene . AddPrimitiveToUpdate ( SourceIndex , EPrimitiveDirtyState : : AddedMask ) ;
}
bool EntryFound = false ;
int32 BroadIndex = - 1 ;
//broad phase search for a matching type
for ( BroadIndex = TypeOffsetTable . Num ( ) - 1 ; BroadIndex > = 0 ; BroadIndex - - )
{
// example how the prefix sum of the tails could look like
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,2,1,1,1,7,4,8]
// TypeOffsetTable[3,8,12,15,16,17,18]
if ( TypeOffsetTable [ BroadIndex ] . PrimitiveSceneProxyType = = InsertProxyHash )
{
EntryFound = true ;
break ;
}
}
//new type encountered
if ( EntryFound = = false )
{
BroadIndex = TypeOffsetTable . Num ( ) ;
if ( BroadIndex )
{
FTypeOffsetTableEntry Entry = TypeOffsetTable [ BroadIndex - 1 ] ;
//adding to the end of the list and offset of the tail (will will be incremented once during the while loop)
TypeOffsetTable . Push ( FTypeOffsetTableEntry ( InsertProxyHash , Entry . Offset ) ) ;
}
else
{
//starting with an empty list and offset zero (will will be incremented once during the while loop)
TypeOffsetTable . Push ( FTypeOffsetTableEntry ( InsertProxyHash , 0 ) ) ;
}
}
{
SCOPED_NAMED_EVENT ( FScene_SwapPrimitiveSceneInfos , FColor : : Turquoise ) ;
for ( int32 AddIndex = StartIndex ; AddIndex < AddedLocalPrimitiveSceneInfos . Num ( ) ; AddIndex + + )
{
int32 SourceIndex = AddedLocalPrimitiveSceneInfos [ AddIndex ] - > PackedIndex ;
for ( int32 TypeIndex = BroadIndex ; TypeIndex < TypeOffsetTable . Num ( ) ; TypeIndex + + )
{
FTypeOffsetTableEntry & NextEntry = TypeOffsetTable [ TypeIndex ] ;
int32 DestIndex = NextEntry . Offset + + ; //prepare swap and increment
// example swap chain of inserting a type of 6 at the end
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,2,2,2,2,1,1,1,7,4,8,6]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,6,2,2,2,1,1,1,7,4,8,2]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,6,2,2,2,2,1,1,7,4,8,1]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,6,2,2,2,2,1,1,1,4,8,7]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,6,2,2,2,2,1,1,1,7,8,4]
// PrimitiveSceneProxies[0,0,0,6,6,6,6,6,6,2,2,2,2,1,1,1,7,4,8]
if ( DestIndex ! = SourceIndex )
{
checkfSlow ( SourceIndex > DestIndex , TEXT ( " Corrupted Prefix Sum [%d, %d] " ) , SourceIndex , DestIndex ) ;
Primitives [ DestIndex ] - > PackedIndex = SourceIndex ;
Primitives [ SourceIndex ] - > PackedIndex = DestIndex ;
TArraySwapElements ( Primitives , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveTransforms , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveSceneProxies , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveBounds , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveFlagsCompact , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveVisibilityIds , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveOctreeIndex , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveOcclusionFlags , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveComponentIds , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveVirtualTextureFlags , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveVirtualTextureLod , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveOcclusionBounds , DestIndex , SourceIndex ) ;
# if WITH_EDITOR
TBitArraySwapElements ( PrimitivesSelected , DestIndex , SourceIndex ) ;
# endif
# if RHI_RAYTRACING
TArraySwapElements ( PrimitiveRayTracingFlags , DestIndex , SourceIndex ) ;
TArraySwapElements ( PrimitiveRayTracingGroupIds , DestIndex , SourceIndex ) ;
# endif
TBitArraySwapElements ( PrimitivesNeedingStaticMeshUpdate , DestIndex , SourceIndex ) ;
for ( TMap < int32 , TArray < FCachedShadowMapData > > : : TIterator CachedShadowMapIt ( CachedShadowMaps ) ; CachedShadowMapIt ; + + CachedShadowMapIt )
{
TArray < FCachedShadowMapData > & ShadowMapDatas = CachedShadowMapIt . Value ( ) ;
for ( auto & ShadowMapData : ShadowMapDatas )
{
if ( ShadowMapData . StaticShadowSubjectMap . Num ( ) > 0 )
{
TBitArraySwapElements ( ShadowMapData . StaticShadowSubjectMap , DestIndex , SourceIndex ) ;
}
}
}
GPUScene . RecordPrimitiveIdSwap ( DestIndex , SourceIndex ) ;
# if RHI_RAYTRACING
// Update cached PrimitiveIndex after an index swap
Primitives [ SourceIndex ] - > CachedRayTracingInstance . DefaultUserData = SourceIndex ;
Primitives [ DestIndex ] - > CachedRayTracingInstance . DefaultUserData = DestIndex ;
# endif
}
}
}
}
CheckPrimitiveArrays ( ) ;
# if ENABLE_LOG_PRIMITIVE_INSTANCE_ID_STATS_TO_CSV
UpdatePrimitiveAllocatorStats ( Primitives . Num ( ) , PersistentPrimitiveIdAllocator . GetMaxSize ( ) , PersistentPrimitiveIdAllocator . GetSparselyAllocatedSize ( ) , PersistentPrimitiveIdAllocator . GetNumFreeSpans ( ) , PersistentPrimitiveFreeListSizeBeforeConsolidate , PersistentPrimitivePendingFreeListSizeBeforeConsolidate , GPUScene . GetNumInstances ( ) , GPUScene . GetInstanceSceneDataAllocator ( ) . GetSparselyAllocatedSize ( ) ) ;
# endif
for ( int32 AddIndex = StartIndex ; AddIndex < AddedLocalPrimitiveSceneInfos . Num ( ) ; AddIndex + + )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = AddedLocalPrimitiveSceneInfos [ AddIndex ] ;
FScopeCycleCounter Context ( PrimitiveSceneInfo - > Proxy - > GetStatId ( ) ) ;
int32 PrimitiveIndex = PrimitiveSceneInfo - > PackedIndex ;
// Add the primitive to its shadow parent's linked list of children.
// Note: must happen before AddToScene because AddToScene depends on LightingAttachmentRoot
PrimitiveSceneInfo - > LinkAttachmentGroup ( ) ;
}
FPrimitiveSceneInfo : : AllocateGPUSceneInstances ( this , TArrayView < FPrimitiveSceneInfo * > ( & AddedLocalPrimitiveSceneInfos [ StartIndex ] , AddedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ) ) ;
{
SCOPED_NAMED_EVENT ( FScene_AddPrimitiveSceneInfoToScene , FColor : : Turquoise ) ;
if ( GIsEditor )
{
FPrimitiveSceneInfo : : AddToScene ( GraphBuilder . RHICmdList , this , TArrayView < FPrimitiveSceneInfo * > ( & AddedLocalPrimitiveSceneInfos [ StartIndex ] , AddedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ) , true ) ;
}
else
{
const bool bAddToDrawLists = ! ( CVarDoLazyStaticMeshUpdate . GetValueOnRenderThread ( ) ) ;
if ( bAddToDrawLists )
{
FPrimitiveSceneInfo : : AddToScene ( GraphBuilder . RHICmdList , this , TArrayView < FPrimitiveSceneInfo * > ( & AddedLocalPrimitiveSceneInfos [ StartIndex ] , AddedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ) , true , true , bAsyncCreateLPIs ) ;
}
else
{
FPrimitiveSceneInfo : : AddToScene ( GraphBuilder . RHICmdList , this , TArrayView < FPrimitiveSceneInfo * > ( & AddedLocalPrimitiveSceneInfos [ StartIndex ] , AddedLocalPrimitiveSceneInfos . Num ( ) - StartIndex ) , true , false , bAsyncCreateLPIs ) ;
for ( int AddIndex = StartIndex ; AddIndex < AddedLocalPrimitiveSceneInfos . Num ( ) ; AddIndex + + )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = AddedLocalPrimitiveSceneInfos [ AddIndex ] ;
PrimitiveSceneInfo - > BeginDeferredUpdateStaticMeshes ( ) ;
}
}
}
}
for ( int AddIndex = StartIndex ; AddIndex < AddedLocalPrimitiveSceneInfos . Num ( ) ; AddIndex + + )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = AddedLocalPrimitiveSceneInfos [ AddIndex ] ;
int32 PrimitiveIndex = PrimitiveSceneInfo - > PackedIndex ;
if ( ShouldPrimitiveOutputVelocity ( PrimitiveSceneInfo - > Proxy , GetShaderPlatform ( ) ) )
{
PrimitiveSceneInfo - > bRegisteredWithVelocityData = true ;
// We must register the initial LocalToWorld with the velocity state.
// In the case of a moving component with MarkRenderStateDirty() called every frame, UpdateTransform will never happen.
VelocityData . UpdateTransform ( PrimitiveSceneInfo , PrimitiveTransforms [ PrimitiveIndex ] , PrimitiveTransforms [ PrimitiveIndex ] ) ;
}
DistanceFieldSceneData . AddPrimitive ( PrimitiveSceneInfo ) ;
LumenSceneData - > AddPrimitive ( PrimitiveSceneInfo ) ;
// Flush virtual textures touched by primitive
PrimitiveSceneInfo - > FlushRuntimeVirtualTexture ( ) ;
// Set LOD parent information if valid
PrimitiveSceneInfo - > LinkLODParentComponent ( ) ;
// Update scene LOD tree
SceneLODHierarchy . UpdateNodeSceneInfo ( PrimitiveSceneInfo - > PrimitiveComponentId , PrimitiveSceneInfo ) ;
bNeedPathTracedInvalidation = bNeedPathTracedInvalidation | | IsPrimitiveRelevantToPathTracing ( PrimitiveSceneInfo ) ;
}
AddedLocalPrimitiveSceneInfos . RemoveAt ( StartIndex , AddedLocalPrimitiveSceneInfos . Num ( ) - StartIndex , false ) ;
}
}
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( UpdatePrimitiveTransform ) ;
SCOPED_NAMED_EVENT ( FScene_AddPrimitiveSceneInfos , FColor : : Yellow ) ;
SCOPE_CYCLE_COUNTER ( STAT_UpdatePrimitiveTransformRenderThreadTime ) ;
TArray < FPrimitiveSceneInfo * > UpdatedSceneInfosWithStaticDrawListUpdate ;
TArray < FPrimitiveSceneInfo * > UpdatedSceneInfosWithoutStaticDrawListUpdate ;
UpdatedSceneInfosWithStaticDrawListUpdate . Reserve ( UpdatedTransforms . Num ( ) ) ;
UpdatedSceneInfosWithoutStaticDrawListUpdate . Reserve ( UpdatedTransforms . Num ( ) ) ;
for ( const auto & Transform : UpdatedTransforms )
{
FPrimitiveSceneProxy * PrimitiveSceneProxy = Transform . Key ;
if ( DeletedSceneInfos . Contains ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) )
{
continue ;
}
check ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex ! = INDEX_NONE ) ;
const FBoxSphereBounds & WorldBounds = Transform . Value . WorldBounds ;
const FBoxSphereBounds & LocalBounds = Transform . Value . LocalBounds ;
const FMatrix & LocalToWorld = Transform . Value . LocalToWorld ;
const FVector & AttachmentRootPosition = Transform . Value . AttachmentRootPosition ;
FScopeCycleCounter Context ( PrimitiveSceneProxy - > GetStatId ( ) ) ;
FPrimitiveSceneInfo * PrimitiveSceneInfo = PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ;
const bool bUpdateStaticDrawLists = ! PrimitiveSceneProxy - > StaticElementsAlwaysUseProxyPrimitiveUniformBuffer ( ) ;
if ( bUpdateStaticDrawLists )
{
UpdatedSceneInfosWithStaticDrawListUpdate . Push ( PrimitiveSceneInfo ) ;
}
else
{
UpdatedSceneInfosWithoutStaticDrawListUpdate . Push ( PrimitiveSceneInfo ) ;
}
PrimitiveSceneInfo - > FlushRuntimeVirtualTexture ( ) ;
// Remove the primitive from the scene at its old location
// (note that the octree update relies on the bounds not being modified yet).
PrimitiveSceneInfo - > RemoveFromScene ( bUpdateStaticDrawLists ) ;
if ( ShouldPrimitiveOutputVelocity ( PrimitiveSceneInfo - > Proxy , GetShaderPlatform ( ) ) )
{
PrimitiveSceneInfo - > bRegisteredWithVelocityData = true ;
VelocityData . UpdateTransform ( PrimitiveSceneInfo , LocalToWorld , PrimitiveSceneProxy - > GetLocalToWorld ( ) ) ;
}
bNeedPathTracedInvalidation = bNeedPathTracedInvalidation | | ( IsPrimitiveRelevantToPathTracing ( PrimitiveSceneInfo ) & &
! PrimitiveTransforms [ PrimitiveSceneInfo - > PackedIndex ] . Equals ( LocalToWorld , SMALL_NUMBER ) ) ;
// Update the primitive transform.
PrimitiveSceneProxy - > SetTransform ( LocalToWorld , WorldBounds , LocalBounds , AttachmentRootPosition ) ;
PrimitiveTransforms [ PrimitiveSceneInfo - > PackedIndex ] = LocalToWorld ;
if ( ! RHISupportsVolumeTextures ( GetFeatureLevel ( ) )
& & ( PrimitiveSceneProxy - > IsMovable ( ) | | PrimitiveSceneProxy - > NeedsUnbuiltPreviewLighting ( ) | | PrimitiveSceneProxy - > GetLightmapType ( ) = = ELightmapType : : ForceVolumetric ) )
{
PrimitiveSceneInfo - > MarkIndirectLightingCacheBufferDirty ( ) ;
}
GPUScene . AddPrimitiveToUpdate ( PrimitiveSceneInfo - > PackedIndex , EPrimitiveDirtyState : : ChangedTransform ) ;
DistanceFieldSceneData . UpdatePrimitive ( PrimitiveSceneInfo ) ;
LumenSceneData - > UpdatePrimitive ( PrimitiveSceneInfo ) ;
# if RHI_RAYTRACING
PrimitiveSceneInfo - > UpdateCachedRayTracingInstanceWorldBounds ( LocalToWorld ) ;
# endif
// If the primitive has static mesh elements, it should have returned true from ShouldRecreateProxyOnUpdateTransform!
check ( ! ( bUpdateStaticDrawLists & & PrimitiveSceneInfo - > StaticMeshes . Num ( ) ) ) ;
}
# if RHI_RAYTRACING
{
UpdateRayTracingGroupBounds_UpdatePrimitives ( UpdatedTransforms ) ;
}
# endif
// Re-add the primitive to the scene with the new transform.
if ( UpdatedSceneInfosWithStaticDrawListUpdate . Num ( ) > 0 )
{
FPrimitiveSceneInfo : : AddToScene ( GraphBuilder . RHICmdList , this , UpdatedSceneInfosWithStaticDrawListUpdate , true , true , bAsyncCreateLPIs ) ;
}
if ( UpdatedSceneInfosWithoutStaticDrawListUpdate . Num ( ) > 0 )
{
FPrimitiveSceneInfo : : AddToScene ( GraphBuilder . RHICmdList , this , UpdatedSceneInfosWithoutStaticDrawListUpdate , false , true , bAsyncCreateLPIs ) ;
for ( FPrimitiveSceneInfo * PrimitiveSceneInfo : UpdatedSceneInfosWithoutStaticDrawListUpdate )
{
PrimitiveSceneInfo - > FlushRuntimeVirtualTexture ( ) ;
}
}
for ( const auto & Transform : OverridenPreviousTransforms )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = Transform . Key ;
VelocityData . OverridePreviousTransform ( PrimitiveSceneInfo - > PrimitiveComponentId , Transform . Value ) ;
}
}
// handle scene changes
for ( FVirtualShadowMapArrayCacheManager * CacheManager : VirtualShadowCacheManagers )
{
CacheManager - > OnSceneChange ( ) ;
}
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( UpdatePrimitiveInstances ) ;
SCOPED_NAMED_EVENT ( FScene_UpdatePrimitiveInstances , FColor : : Emerald ) ;
SCOPE_CYCLE_COUNTER ( STAT_UpdatePrimitiveInstanceRenderThreadTime ) ;
TArray < FPrimitiveSceneInfo * > UpdatedSceneInfosWithStaticDrawListUpdate ;
UpdatedSceneInfosWithStaticDrawListUpdate . Reserve ( UpdatedInstances . Num ( ) ) ;
TArray < FPrimitiveSceneInfo * > UpdatedSceneInfosWithoutStaticDrawListUpdate ;
UpdatedSceneInfosWithoutStaticDrawListUpdate . Reserve ( UpdatedInstances . Num ( ) ) ;
# if RHI_RAYTRACING
TArray < FPrimitiveSceneInfo * > RayTracingPrimitivesToUpdate ;
RayTracingPrimitivesToUpdate . Reserve ( UpdatedInstances . Num ( ) ) ;
# endif
for ( const auto & UpdateInstance : UpdatedInstances )
{
FPrimitiveSceneProxy * PrimitiveSceneProxy = UpdateInstance . Key ;
if ( DeletedSceneInfos . Contains ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) )
{
continue ;
}
check ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > PackedIndex ! = INDEX_NONE ) ;
FScopeCycleCounter Context ( PrimitiveSceneProxy - > GetStatId ( ) ) ;
FPrimitiveSceneInfo * PrimitiveSceneInfo = PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ;
PrimitiveSceneInfo - > FlushRuntimeVirtualTexture ( ) ;
const bool bUpdateStaticDrawLists = ! PrimitiveSceneProxy - > StaticElementsAlwaysUseProxyPrimitiveUniformBuffer ( ) ;
// If we recorded no adds or removes the instance count has stayed the same. Therefore the cached mesh draw commands do not
// need to be updated. In situations where the instance count changes the mesh draw command stores the instance count which
// would need to be updated.
const bool bInstanceCountChanged = ( UpdateInstance . Value . CmdBuffer . NumAdds > 0 ) | | ( UpdateInstance . Value . CmdBuffer . NumRemoves > 0 ) ;
if ( bUpdateStaticDrawLists | | bInstanceCountChanged )
{
UpdatedSceneInfosWithStaticDrawListUpdate . Push ( PrimitiveSceneInfo ) ;
PrimitiveSceneInfo - > RemoveFromScene ( true ) ;
}
else
{
# if RHI_RAYTRACING
RayTracingPrimitivesToUpdate . Add ( PrimitiveSceneInfo ) ;
# endif
UpdatedSceneInfosWithoutStaticDrawListUpdate . Push ( PrimitiveSceneInfo ) ;
PrimitiveSceneInfo - > RemoveFromScene ( false ) ;
}
// Update the Proxy's data.
PrimitiveSceneProxy - > UpdateInstances_RenderThread ( UpdateInstance . Value . CmdBuffer , UpdateInstance . Value . WorldBounds , UpdateInstance . Value . LocalBounds , UpdateInstance . Value . StaticMeshBounds ) ;
if ( ! RHISupportsVolumeTextures ( GetFeatureLevel ( ) )
& & ( PrimitiveSceneProxy - > IsMovable ( ) | | PrimitiveSceneProxy - > NeedsUnbuiltPreviewLighting ( ) | | PrimitiveSceneProxy - > GetLightmapType ( ) = = ELightmapType : : ForceVolumetric ) )
{
PrimitiveSceneInfo - > MarkIndirectLightingCacheBufferDirty ( ) ;
}
if ( UpdateInstance . Value . CmdBuffer . NumAdds > 0 | | UpdateInstance . Value . CmdBuffer . NumRemoves > 0 )
{
PrimitiveSceneInfo - > FreeGPUSceneInstances ( ) ;
FPrimitiveSceneInfo : : AllocateGPUSceneInstances ( this , MakeArrayView ( & PrimitiveSceneInfo , 1 ) ) ;
DistanceFieldSceneData . RemovePrimitive ( PrimitiveSceneInfo ) ;
DistanceFieldSceneData . AddPrimitive ( PrimitiveSceneInfo ) ;
LumenSceneData - > RemovePrimitive ( PrimitiveSceneInfo , PrimitiveSceneInfo - > GetIndex ( ) ) ;
LumenSceneData - > AddPrimitive ( PrimitiveSceneInfo ) ;
}
else
{
GPUScene . AddPrimitiveToUpdate ( PrimitiveSceneInfo - > PackedIndex , EPrimitiveDirtyState : : ChangedAll ) ;
DistanceFieldSceneData . UpdatePrimitive ( PrimitiveSceneInfo ) ;
LumenSceneData - > UpdatePrimitive ( PrimitiveSceneInfo ) ;
}
bNeedPathTracedInvalidation = bNeedPathTracedInvalidation | | IsPrimitiveRelevantToPathTracing ( PrimitiveSceneInfo ) ;
}
# if RHI_RAYTRACING
{
UpdateRayTracingGroupBounds_UpdatePrimitives ( UpdatedInstances ) ;
}
# endif
// Re-add the primitive to the scene with the new transform.
if ( UpdatedSceneInfosWithStaticDrawListUpdate . Num ( ) > 0 )
{
FPrimitiveSceneInfo : : AddToScene ( GraphBuilder . RHICmdList , this , UpdatedSceneInfosWithStaticDrawListUpdate , true , true , bAsyncCreateLPIs ) ;
}
if ( UpdatedSceneInfosWithoutStaticDrawListUpdate . Num ( ) > 0 )
{
FPrimitiveSceneInfo : : AddToScene ( GraphBuilder . RHICmdList , this , UpdatedSceneInfosWithoutStaticDrawListUpdate , false , true , bAsyncCreateLPIs ) ;
# if RHI_RAYTRACING
FPrimitiveSceneInfo : : UpdateCachedRayTracingInstances ( this , RayTracingPrimitivesToUpdate ) ;
# endif
}
}
for ( const auto & Attachments : UpdatedAttachmentRoots )
{
FPrimitiveSceneInfo * PrimitiveSceneInfo = Attachments . Key ;
if ( DeletedSceneInfos . Contains ( PrimitiveSceneInfo ) )
{
continue ;
}
PrimitiveSceneInfo - > UnlinkAttachmentGroup ( ) ;
PrimitiveSceneInfo - > LightingAttachmentRoot = Attachments . Value ;
PrimitiveSceneInfo - > LinkAttachmentGroup ( ) ;
}
for ( const auto & CustomParams : UpdatedCustomPrimitiveParams )
{
FPrimitiveSceneProxy * PrimitiveSceneProxy = CustomParams . Key ;
if ( DeletedSceneInfos . Contains ( PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) ) )
{
continue ;
}
FScopeCycleCounter Context ( PrimitiveSceneProxy - > GetStatId ( ) ) ;
PrimitiveSceneProxy - > CustomPrimitiveData = CustomParams . Value ;
// Make sure the uniform buffer is updated before rendering
PrimitiveSceneProxy - > GetPrimitiveSceneInfo ( ) - > SetNeedsUniformBufferUpdate ( true ) ;
}
for ( FPrimitiveSceneInfo * PrimitiveSceneInfo : DistanceFieldSceneDataUpdates )
{
if ( DeletedSceneInfos . Contains ( PrimitiveSceneInfo ) )
{
continue ;
}
DistanceFieldSceneData . UpdatePrimitive ( PrimitiveSceneInfo ) ;
}
for ( const auto & OccSlackDelta : UpdatedOcclusionBoundsSlacks )
{
const FPrimitiveSceneProxy * SceneProxy = OccSlackDelta . Key ;
const FPrimitiveSceneInfo * SceneInfo = SceneProxy - > GetPrimitiveSceneInfo ( ) ;
if ( DeletedSceneInfos . Contains ( SceneInfo ) )
{
continue ;
}
FBoxSphereBounds NewOccBounds ;
if ( SceneProxy - > HasCustomOcclusionBounds ( ) )
{
NewOccBounds = SceneProxy - > GetCustomOcclusionBounds ( ) ;
}
else
{
NewOccBounds = SceneProxy - > GetBounds ( ) ;
}
PrimitiveOcclusionBounds [ SceneInfo - > PackedIndex ] = NewOccBounds . ExpandBy ( OCCLUSION_SLOP + OccSlackDelta . Value ) ;
}
{
SCOPED_NAMED_EVENT ( FScene_DeletePrimitiveSceneInfo , FColor : : Red ) ;
for ( FPrimitiveSceneInfo * PrimitiveSceneInfo : DeletedSceneInfos )
{
bNeedPathTracedInvalidation = bNeedPathTracedInvalidation | | IsPrimitiveRelevantToPathTracing ( PrimitiveSceneInfo ) ;
// It is possible that the HitProxies list isn't empty if PrimitiveSceneInfo was Added/Removed in same frame
// Delete the PrimitiveSceneInfo on the game thread after the rendering thread has processed its removal.
// This must be done on the game thread because the hit proxy references (and possibly other members) need to be freed on the game thread.
struct DeferDeleteHitProxies : FDeferredCleanupInterface
{
DeferDeleteHitProxies ( TArray < TRefCountPtr < HHitProxy > > & & InHitProxies ) : HitProxies ( MoveTemp ( InHitProxies ) ) { }
TArray < TRefCountPtr < HHitProxy > > HitProxies ;
} ;
BeginCleanup ( new DeferDeleteHitProxies ( MoveTemp ( PrimitiveSceneInfo - > HitProxies ) ) ) ;
// free the primitive scene proxy.
delete PrimitiveSceneInfo - > Proxy ;
delete PrimitiveSceneInfo ;
}
}
if ( bNeedPathTracedInvalidation )
{
InvalidatePathTracedOutput ( ) ;
}
UpdatedAttachmentRoots . Empty ( ) ;
UpdatedTransforms . Empty ( ) ;
UpdatedInstances . Empty ( ) ;
UpdatedCustomPrimitiveParams . Empty ( ) ;
OverridenPreviousTransforms . Empty ( ) ;
UpdatedOcclusionBoundsSlacks . Empty ( ) ;
DistanceFieldSceneDataUpdates . Empty ( ) ;
AddedPrimitiveSceneInfos . Empty ( ) ;
}
void FScene : : CreateLightPrimitiveInteractionsForPrimitive ( FPrimitiveSceneInfo * PrimitiveInfo , bool bAsyncCreateLPIs )
{
FPrimitiveSceneProxy * Proxy = PrimitiveInfo - > Proxy ;
if ( Proxy - > GetLightingChannelMask ( ) ! = 0 )
{
FMemMark MemStackMark ( FMemStack : : Get ( ) ) ;
const FBoxSphereBounds & Bounds = Proxy - > GetBounds ( ) ;
const FPrimitiveSceneInfoCompact PrimitiveSceneInfoCompact ( PrimitiveInfo ) ;
// Find local lights that affect the primitive in the light octree.
LocalShadowCastingLightOctree . FindElementsWithBoundsTest ( Bounds . GetBox ( ) , [ & PrimitiveSceneInfoCompact ] ( const FLightSceneInfoCompact & LightSceneInfoCompact )
{
LightSceneInfoCompact . LightSceneInfo - > CreateLightPrimitiveInteraction ( LightSceneInfoCompact , PrimitiveSceneInfoCompact ) ;
} ) ;
// Also loop through non-local (directional) shadow-casting lights
for ( int32 LightID : DirectionalShadowCastingLightIDs )
{
const FLightSceneInfoCompact & LightSceneInfoCompact = Lights [ LightID ] ;
LightSceneInfoCompact . LightSceneInfo - > CreateLightPrimitiveInteraction ( LightSceneInfoCompact , PrimitiveSceneInfoCompact ) ;
}
}
}
bool FScene : : IsPrimitiveBeingRemoved ( FPrimitiveSceneInfo * PrimitiveSceneInfo ) const
{
check ( IsInParallelRenderingThread ( ) | | IsInRenderingThread ( ) ) ;
return RemovedPrimitiveSceneInfos . Find ( PrimitiveSceneInfo ) ! = nullptr ;
}
/**
* Dummy NULL scene interface used by dedicated servers .
*/
class FNULLSceneInterface : public FSceneInterface
{
public :
FNULLSceneInterface ( UWorld * InWorld , bool bCreateFXSystem )
: FSceneInterface ( GMaxRHIFeatureLevel )
, World ( InWorld )
, FXSystem ( NULL )
{
World - > Scene = this ;
if ( bCreateFXSystem )
{
World - > CreateFXSystem ( ) ;
}
else
{
World - > FXSystem = NULL ;
SetFXSystem ( NULL ) ;
}
}
virtual void AddPrimitive ( UPrimitiveComponent * Primitive ) override { }
virtual void RemovePrimitive ( UPrimitiveComponent * Primitive ) override { }
virtual void ReleasePrimitive ( UPrimitiveComponent * Primitive ) override { }
virtual void UpdateAllPrimitiveSceneInfos ( FRDGBuilder & GraphBuilder , bool bAsyncCreateLPIs = false ) override { }
virtual FPrimitiveSceneInfo * GetPrimitiveSceneInfo ( int32 PrimiteIndex ) override { return NULL ; }
/** Updates the transform of a primitive which has already been added to the scene. */
virtual void UpdatePrimitiveTransform ( UPrimitiveComponent * Primitive ) override { }
virtual void UpdatePrimitiveInstances ( UInstancedStaticMeshComponent * Primitive ) override { }
virtual void UpdatePrimitiveOcclusionBoundsSlack ( UPrimitiveComponent * Primitive , float NewSlack ) override { }
virtual void UpdatePrimitiveAttachment ( UPrimitiveComponent * Primitive ) override { }
virtual void UpdateCustomPrimitiveData ( UPrimitiveComponent * Primitive ) override { }
virtual void AddLight ( ULightComponent * Light ) override { }
virtual void RemoveLight ( ULightComponent * Light ) override { }
virtual void AddInvisibleLight ( ULightComponent * Light ) override { }
virtual void SetSkyLight ( FSkyLightSceneProxy * Light ) override { }
virtual void DisableSkyLight ( FSkyLightSceneProxy * Light ) override { }
virtual bool HasSkyLightRequiringLightingBuild ( ) const { return false ; }
virtual bool HasAtmosphereLightRequiringLightingBuild ( ) const { return false ; }
virtual void AddDecal ( UDecalComponent * ) override { }
virtual void RemoveDecal ( UDecalComponent * ) override { }
virtual void UpdateDecalTransform ( UDecalComponent * Decal ) override { }
virtual void UpdateDecalFadeOutTime ( UDecalComponent * Decal ) override { } ;
virtual void UpdateDecalFadeInTime ( UDecalComponent * Decal ) override { } ;
/** Updates the transform of a light which has already been added to the scene. */
virtual void UpdateLightTransform ( ULightComponent * Light ) override { }
virtual void UpdateLightColorAndBrightness ( ULightComponent * Light ) override { }
virtual void AddExponentialHeightFog ( class UExponentialHeightFogComponent * FogComponent ) override { }
virtual void RemoveExponentialHeightFog ( class UExponentialHeightFogComponent * FogComponent ) override { }
virtual bool HasAnyExponentialHeightFog ( ) const override { return false ; }
virtual void AddSkyAtmosphere ( FSkyAtmosphereSceneProxy * SkyAtmosphereSceneProxy , bool bStaticLightingBuilt ) override { }
virtual void RemoveSkyAtmosphere ( FSkyAtmosphereSceneProxy * SkyAtmosphereSceneProxy ) override { }
virtual FSkyAtmosphereRenderSceneInfo * GetSkyAtmosphereSceneInfo ( ) override { return NULL ; }
virtual const FSkyAtmosphereRenderSceneInfo * GetSkyAtmosphereSceneInfo ( ) const override { return NULL ; }
virtual void AddHairStrands ( FHairStrandsInstance * Proxy ) override { }
virtual void RemoveHairStrands ( FHairStrandsInstance * Proxy ) override { }
virtual void GetRectLightAtlasSlot ( const FRectLightSceneProxy * Proxy , FLightRenderParameters * Out ) override { }
virtual void SetPhysicsField ( FPhysicsFieldSceneProxy * PhysicsFieldSceneProxy ) override { }
virtual void ResetPhysicsField ( ) override { }
virtual void ShowPhysicsField ( ) override { }
virtual void UpdatePhysicsField ( FRDGBuilder & GraphBuilder , FViewInfo & View ) override { }
virtual void AddVolumetricCloud ( FVolumetricCloudSceneProxy * VolumetricCloudSceneProxy ) override { }
virtual void RemoveVolumetricCloud ( FVolumetricCloudSceneProxy * VolumetricCloudSceneProxy ) override { }
virtual FVolumetricCloudRenderSceneInfo * GetVolumetricCloudSceneInfo ( ) override { return NULL ; }
virtual const FVolumetricCloudRenderSceneInfo * GetVolumetricCloudSceneInfo ( ) const override { return NULL ; }
virtual void AddWindSource ( class UWindDirectionalSourceComponent * WindComponent ) override { }
virtual void RemoveWindSource ( class UWindDirectionalSourceComponent * WindComponent ) override { }
virtual void UpdateWindSource ( class UWindDirectionalSourceComponent * WindComponent ) override { }
virtual const TArray < class FWindSourceSceneProxy * > & GetWindSources_RenderThread ( ) const override
{
static TArray < class FWindSourceSceneProxy * > NullWindSources ;
return NullWindSources ;
}
virtual void GetWindParameters ( const FVector & Position , FVector & OutDirection , float & OutSpeed , float & OutMinGustAmt , float & OutMaxGustAmt ) const override { OutDirection = FVector ( 1.0f , 0.0f , 0.0f ) ; OutSpeed = 0.0f ; OutMinGustAmt = 0.0f ; OutMaxGustAmt = 0.0f ; }
virtual void GetWindParameters_GameThread ( const FVector & Position , FVector & OutDirection , float & OutSpeed , float & OutMinGustAmt , float & OutMaxGustAmt ) const override { OutDirection = FVector ( 1.0f , 0.0f , 0.0f ) ; OutSpeed = 0.0f ; OutMinGustAmt = 0.0f ; OutMaxGustAmt = 0.0f ; }
virtual void GetDirectionalWindParameters ( FVector & OutDirection , float & OutSpeed , float & OutMinGustAmt , float & OutMaxGustAmt ) const override { OutDirection = FVector ( 1.0f , 0.0f , 0.0f ) ; OutSpeed = 0.0f ; OutMinGustAmt = 0.0f ; OutMaxGustAmt = 0.0f ; }
virtual void AddSpeedTreeWind ( class FVertexFactory * VertexFactory , const class UStaticMesh * StaticMesh ) override { }
virtual void RemoveSpeedTreeWind_RenderThread ( class FVertexFactory * VertexFactory , const class UStaticMesh * StaticMesh ) override { }
virtual void UpdateSpeedTreeWind ( double CurrentTime ) override { }
virtual FRHIUniformBuffer * GetSpeedTreeUniformBuffer ( const FVertexFactory * VertexFactory ) const override { return nullptr ; }
virtual void Release ( ) override { }
/**
* Retrieves the lights interacting with the passed in primitive and adds them to the out array .
*
* @ param Primitive Primitive to retrieve interacting lights for
* @ param RelevantLights [ out ] Array of lights interacting with primitive
*/
virtual void GetRelevantLights ( UPrimitiveComponent * Primitive , TArray < const ULightComponent * > * RelevantLights ) const override { }
/**
* @ return true if hit proxies should be rendered in this scene .
*/
virtual bool RequiresHitProxies ( ) const override
{
return false ;
}
// Accessors.
virtual class UWorld * GetWorld ( ) const override
{
return World ;
}
/**
* Return the scene to be used for rendering
*/
virtual class FScene * GetRenderScene ( ) override
{
return NULL ;
}
/**
* Sets the FX system associated with the scene .
*/
virtual void SetFXSystem ( class FFXSystemInterface * InFXSystem ) override
{
FXSystem = InFXSystem ;
}
/**
* Get the FX system associated with the scene .
*/
virtual class FFXSystemInterface * GetFXSystem ( ) override
{
return FXSystem ;
}
virtual bool HasAnyLights ( ) const override { return false ; }
protected :
virtual FSceneViewStateInterface * AllocateViewState ( ) override
{
return new FSceneViewState ( FeatureLevel ) ;
}
private :
UWorld * World ;
class FFXSystemInterface * FXSystem ;
} ;
FSceneInterface * FRendererModule : : AllocateScene ( UWorld * World , bool bInRequiresHitProxies , bool bCreateFXSystem , ERHIFeatureLevel : : Type InFeatureLevel )
{
LLM_SCOPE ( ELLMTag : : SceneRender ) ;
check ( IsInGameThread ( ) ) ;
// Create a full fledged scene if we have something to render.
if ( GIsClient & & FApp : : CanEverRender ( ) & & ! GUsingNullRHI )
{
FScene * NewScene = new FScene ( World , bInRequiresHitProxies , GIsEditor & & ( ! World | | ! World - > IsGameWorld ( ) ) , bCreateFXSystem , InFeatureLevel ) ;
AllocatedScenes . Add ( NewScene ) ;
return NewScene ;
}
// And fall back to a dummy/ NULL implementation for commandlets and dedicated server.
else
{
return new FNULLSceneInterface ( World , bCreateFXSystem ) ;
}
}
void FRendererModule : : RemoveScene ( FSceneInterface * Scene )
{
check ( IsInGameThread ( ) ) ;
AllocatedScenes . Remove ( Scene ) ;
}
void FRendererModule : : UpdateStaticDrawLists ( )
{
// Update all static meshes in order to recache cached mesh draw commands.
check ( IsInGameThread ( ) ) ; // AllocatedScenes is managed by the game thread
for ( FSceneInterface * Scene : AllocatedScenes )
{
Scene - > UpdateStaticDrawLists ( ) ;
}
}
void UpdateStaticMeshesForMaterials ( const TArray < const FMaterial * > & MaterialResourcesToUpdate )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( UpdateStaticMeshesForMaterials ) ;
TArray < UMaterialInterface * > UsedMaterials ;
TSet < UMaterialInterface * > UsedMaterialsDependencies ;
TMap < FScene * , TArray < FPrimitiveSceneInfo * > > UsedPrimitives ;
for ( TObjectIterator < UPrimitiveComponent > PrimitiveIt ; PrimitiveIt ; + + PrimitiveIt )
{
UPrimitiveComponent * PrimitiveComponent = * PrimitiveIt ;
if ( PrimitiveComponent - > IsRenderStateCreated ( ) & & PrimitiveComponent - > SceneProxy )
{
UsedMaterialsDependencies . Reset ( ) ;
UsedMaterials . Reset ( ) ;
// Note: relying on GetUsedMaterials to be accurate, or else we won't propagate to the right primitives and the renderer will crash later
// FPrimitiveSceneProxy::VerifyUsedMaterial is used to make sure that all materials used for rendering are reported in GetUsedMaterials
PrimitiveComponent - > GetUsedMaterials ( UsedMaterials ) ;
for ( UMaterialInterface * UsedMaterial : UsedMaterials )
{
if ( UsedMaterial )
{
UsedMaterial - > GetDependencies ( UsedMaterialsDependencies ) ;
}
}
if ( UsedMaterialsDependencies . Num ( ) > 0 )
{
for ( const FMaterial * MaterialResourceToUpdate : MaterialResourcesToUpdate )
{
UMaterialInterface * UpdatedMaterialInterface = MaterialResourceToUpdate - > GetMaterialInterface ( ) ;
if ( UpdatedMaterialInterface )
{
if ( UsedMaterialsDependencies . Contains ( UpdatedMaterialInterface ) )
{
FPrimitiveSceneProxy * SceneProxy = PrimitiveComponent - > SceneProxy ;
FPrimitiveSceneInfo * SceneInfo = SceneProxy - > GetPrimitiveSceneInfo ( ) ;
FScene * Scene = SceneInfo - > Scene ;
TArray < FPrimitiveSceneInfo * > & SceneInfos = UsedPrimitives . FindOrAdd ( Scene ) ;
SceneInfos . Add ( SceneInfo ) ;
break ;
}
}
}
}
}
}
ENQUEUE_RENDER_COMMAND ( FUpdateStaticMeshesForMaterials ) (
[ UsedPrimitives = MoveTemp ( UsedPrimitives ) ] ( FRHICommandListImmediate & RHICmdList ) mutable
{
// Defer the caching until the next render tick, to make sure that all render components queued
// for re-creation are processed. Otherwise, we may end up caching mesh commands from stale data.
for ( auto & SceneInfos : UsedPrimitives )
{
SceneInfos . Key - > UpdateAllPrimitiveSceneInfos ( RHICmdList ) ;
}
for ( auto & SceneInfos : UsedPrimitives )
{
TArray < FPrimitiveSceneInfo * > & SceneInfoArray = SceneInfos . Value ;
FPrimitiveSceneInfo : : UpdateStaticMeshes ( RHICmdList , SceneInfos . Key , SceneInfoArray , EUpdateStaticMeshFlags : : AllCommands , false ) ;
}
} ) ;
}
void FRendererModule : : UpdateStaticDrawListsForMaterials ( const TArray < const FMaterial * > & Materials )
{
// Update static meshes for a given set of materials in order to recache cached mesh draw commands.
UpdateStaticMeshesForMaterials ( Materials ) ;
}
FSceneViewStateInterface * FRendererModule : : AllocateViewState ( ERHIFeatureLevel : : Type FeatureLevel )
{
return new FSceneViewState ( FeatureLevel ) ;
}
void FRendererModule : : InvalidatePathTracedOutput ( )
{
// AllocatedScenes is managed by the game thread
// #jira UE-130700:
// Because material updates call this function and could happen in parallel, we also allow the parallel game thread here.
// We assume that no changes will be made to AllocatedScene during this time, otherwise locking would need to
// be introduced (which could have performance implications).
check ( IsInGameThread ( ) | | IsInParallelGameThread ( ) ) ;
for ( FSceneInterface * Scene : AllocatedScenes )
{
Scene - > InvalidatePathTracedOutput ( ) ;
}
}