Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp
Martin Mittring 4d20764bac fixed pause motionblur of static meshes (per object motionblur)
[CL 2527253 by Martin Mittring in Main branch]
2015-04-27 14:32:32 -04:00

2800 lines
95 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Scene.cpp: Scene manager implementation.
=============================================================================*/
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "ShaderCompiler.h"
#include "StaticMeshResources.h"
#include "ParameterCollection.h"
#include "DistanceFieldSurfaceCacheLighting.h"
#include "EngineModule.h"
#include "PrecomputedLightVolume.h"
#include "FXSystem.h"
#include "DistanceFieldLightingShared.h"
#include "SpeedTreeWind.h"
#include "HeightfieldLighting.h"
// Enable this define to do slow checks for components being added to the wrong
// world's scene, when using PIE. This can happen if a PIE component is reattached
// while GWorld is the editor world, for example.
#define CHECK_FOR_PIE_PRIMITIVE_ATTACH_SCENE_MISMATCH 0
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FDistanceCullFadeUniformShaderParameters,TEXT("PrimitiveFade"));
/** Global primitive uniform buffer resource containing faded in */
TGlobalResource< FGlobalDistanceCullFadeUniformBuffer > GDistanceCullFadedInUniformBuffer;
SIZE_T FStaticMeshDrawListBase::TotalBytesUsed = 0;
static FThreadSafeCounter FSceneViewState_UniqueID;
/**
* 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. */
TUniformBuffer<FSpeedTreeUniformParameters> UniformBuffer;
int32 ReferenceCount;
};
/** Default constructor. */
FSceneViewState::FSceneViewState()
: OcclusionQueryPool(RQT_Occlusion)
{
UniqueID = FSceneViewState_UniqueID.Increment();
OcclusionFrameCounter = 0;
LastRenderTime = -FLT_MAX;
LastRenderTimeDelta = 0.0f;
MotionBlurTimeScale = 1.0f;
PrevViewMatrixForOcclusionQuery.SetIdentity();
PrevViewOriginForOcclusionQuery = FVector::ZeroVector;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
bIsFreezing = false;
bIsFrozen = 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;
TemporalAASampleCount = 1;
AOTileIntersectionResources = NULL;
bBokehDOFHistory = true;
bBokehDOFHistory2 = true;
LightPropagationVolume = NULL;
HeightfieldLightingAtlas = NULL;
for (int32 CascadeIndex = 0; CascadeIndex < ARRAY_COUNT(TranslucencyLightingCacheAllocations); CascadeIndex++)
{
TranslucencyLightingCacheAllocations[CascadeIndex] = NULL;
}
#if BUFFERED_OCCLUSION_QUERIES
NumBufferedFrames = FOcclusionQueryHelpers::GetNumBufferedFrames();
ShadowOcclusionQueryMaps.Empty(NumBufferedFrames);
ShadowOcclusionQueryMaps.AddZeroed(NumBufferedFrames);
#endif
}
void DestroyRenderResource(FRenderResource* RenderResource)
{
if (RenderResource)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
DestroySceneViewStateRenderResource,
FRenderResource*, RenderResourceRT, RenderResource,
{
RenderResourceRT->ReleaseResource();
delete RenderResourceRT;
}
);
}
}
FSceneViewState::~FSceneViewState()
{
CachedVisibilityChunk = NULL;
for (int32 CascadeIndex = 0; CascadeIndex < ARRAY_COUNT(TranslucencyLightingCacheAllocations); CascadeIndex++)
{
delete TranslucencyLightingCacheAllocations[CascadeIndex];
}
DestroyRenderResource(HeightfieldLightingAtlas);
DestroyRenderResource(AOTileIntersectionResources);
AOTileIntersectionResources = NULL;
DestroyLightPropagationVolume();
}
FDistanceFieldSceneData::FDistanceFieldSceneData(EShaderPlatform ShaderPlatform)
: NumObjectsInBuffer(0)
, ObjectBuffers(NULL)
, SurfelBuffers(NULL)
, InstancedSurfelBuffers(NULL)
, AtlasGeneration(0)
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GenerateMeshDistanceFields"));
bTrackPrimitives = (DoesPlatformSupportDistanceFieldAO(ShaderPlatform) || DoesPlatformSupportDistanceFieldShadowing(ShaderPlatform)) && CVar->GetValueOnGameThread() != 0;
}
FDistanceFieldSceneData::~FDistanceFieldSceneData()
{
delete ObjectBuffers;
}
void FDistanceFieldSceneData::AddPrimitive(FPrimitiveSceneInfo* InPrimitive)
{
const FPrimitiveSceneProxy* Proxy = InPrimitive->Proxy;
if (bTrackPrimitives
&& Proxy->CastsDynamicShadow()
&& Proxy->AffectsDistanceFieldLighting())
{
if (Proxy->SupportsHeightfieldRepresentation())
{
HeightfieldPrimitives.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 (bTrackPrimitives
&& Proxy->CastsDynamicShadow()
&& Proxy->AffectsDistanceFieldLighting()
&& 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)
{
const FPrimitiveSceneProxy* Proxy = InPrimitive->Proxy;
if (bTrackPrimitives && Proxy->AffectsDistanceFieldLighting())
{
if (Proxy->SupportsDistanceFieldRepresentation())
{
PendingAddOperations.Remove(InPrimitive);
PendingUpdateOperations.Remove(InPrimitive);
if (InPrimitive->DistanceFieldInstanceIndices.Num() > 0)
{
PendingRemoveOperations.Add(FPrimitiveRemoveInfo(InPrimitive));
}
InPrimitive->DistanceFieldInstanceIndices.Empty();
}
if (Proxy->SupportsHeightfieldRepresentation())
{
HeightfieldPrimitives.Remove(InPrimitive);
}
}
}
void FDistanceFieldSceneData::Release()
{
if (ObjectBuffers)
{
ObjectBuffers->Release();
}
}
void FDistanceFieldSceneData::VerifyIntegrity()
{
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);
}
}
/**
* 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 FScene::SetClearMotionBlurInfoGameThread()
{
check(IsInGameThread());
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
ShouldClearMBInfoCommand,
FScene*,Scene,this,
{
Scene->MotionBlurInfoData.SetClearMotionBlurInfo();
});
}
void FScene::UpdateParameterCollections(const TArray<FMaterialParameterCollectionInstanceResource*>& InParameterCollections)
{
// Empy the scene's map so any unused uniform buffers will be released
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
ClearParameterCollectionsCommand,
FScene*,Scene,this,
{
Scene->ParameterCollections.Empty();
});
// Add each existing parameter collection id and its uniform buffer
for (int32 CollectionIndex = 0; CollectionIndex < InParameterCollections.Num(); CollectionIndex++)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
AddParameterCollectionCommand,
FScene*,Scene,this,
FMaterialParameterCollectionInstanceResource*,InstanceResource,InParameterCollections[CollectionIndex],
{
Scene->ParameterCollections.Add(InstanceResource->GetId(), InstanceResource->GetUniformBuffer());
});
}
}
SIZE_T FScene::GetSizeBytes() const
{
return sizeof(*this)
+ Primitives.GetAllocatedSize()
+ Lights.GetAllocatedSize()
+ StaticMeshes.GetAllocatedSize()
+ ExponentialFogs.GetAllocatedSize()
+ WindSources.GetAllocatedSize()
+ SpeedTreeVertexFactoryMap.GetAllocatedSize()
+ SpeedTreeWindComputationMap.GetAllocatedSize()
+ LightOctree.GetSizeBytes()
+ PrimitiveOctree.GetSizeBytes();
}
void FScene::CheckPrimitiveArrays()
{
check(Primitives.Num() == PrimitiveBounds.Num());
check(Primitives.Num() == PrimitiveVisibilityIds.Num());
check(Primitives.Num() == PrimitiveOcclusionFlags.Num());
check(Primitives.Num() == PrimitiveComponentIds.Num());
check(Primitives.Num() == PrimitiveOcclusionBounds.Num());
}
void FScene::AddPrimitiveSceneInfo_RenderThread(FRHICommandListImmediate& RHICmdList, FPrimitiveSceneInfo* PrimitiveSceneInfo)
{
SCOPE_CYCLE_COUNTER(STAT_AddScenePrimitiveRenderThreadTime);
CheckPrimitiveArrays();
int32 PrimitiveIndex = Primitives.Add(PrimitiveSceneInfo);
PrimitiveSceneInfo->PackedIndex = PrimitiveIndex;
PrimitiveBounds.AddUninitialized();
PrimitiveVisibilityIds.AddUninitialized();
PrimitiveOcclusionFlags.AddUninitialized();
PrimitiveComponentIds.AddUninitialized();
PrimitiveOcclusionBounds.AddUninitialized();
CheckPrimitiveArrays();
// Add the primitive to its shadow parent's linked list of children.
// Note: must happen before AddToScene because AddToScene depends on LightingAttachmentRoot
PrimitiveSceneInfo->LinkAttachmentGroup();
// Set lod Parent information if valid
PrimitiveSceneInfo->LinkLODParentComponent();
// Add the primitive to the scene.
PrimitiveSceneInfo->AddToScene(RHICmdList, true);
DistanceFieldSceneData.AddPrimitive(PrimitiveSceneInfo);
// LOD Parent, if this is LOD parent, we should update Proxy Scene Info
// LOD parent gets removed WHEN no children is accessing
// LOD parent can be recreated as scene updates
// I update if the parent component ID is still valid
// @Todo : really remove it if you know this is being destroyed - should happen from game thread as streaming in/out
SceneLODHierarchy.UpdateNodeSceneInfo(PrimitiveSceneInfo->PrimitiveComponentId, PrimitiveSceneInfo);
}
/**
* 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)
{
#if CHECK_FOR_PIE_PRIMITIVE_ATTACH_SCENE_MISMATCH
checkf(Component->GetOuter() == GetTransientPackage() ||
(FPackageName::GetLongPackageAssetName(Component->GetOutermost()->GetName()).StartsWith(PLAYWORLD_PACKAGE_PREFIX) ==
FPackageName::GetLongPackageAssetName(World->GetOutermost()->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()
);
#endif
}
FScene::FScene(UWorld* InWorld, bool bInRequiresHitProxies, bool bInIsEditorScene, bool bCreateFXSystem, ERHIFeatureLevel::Type InFeatureLevel)
: World(InWorld)
, FXSystem(NULL)
, bStaticDrawListsMobileHDR(false)
, bStaticDrawListsMobileHDR32bpp(false)
, StaticDrawListsEarlyZPassMode(0)
, bScenesPrimitivesNeedStaticMeshElementUpdate(false)
, SkyLight(NULL)
, SimpleDirectionalLight(NULL)
, SunLight(NULL)
, ReflectionSceneData(InFeatureLevel)
, IndirectLightingCache(InFeatureLevel)
, SurfaceCacheResources(NULL)
, DistanceFieldSceneData(GShaderPlatformForFeatureLevel[InFeatureLevel])
, PreshadowCacheLayout(0, 0, 0, 0, false, false)
, AtmosphericFog(NULL)
, PrecomputedVisibilityHandler(NULL)
, LightOctree(FVector::ZeroVector,HALF_WORLD_MAX)
, PrimitiveOctree(FVector::ZeroVector,HALF_WORLD_MAX)
, bRequiresHitProxies(bInRequiresHitProxies)
, bIsEditorScene(bInIsEditorScene)
, NumUncachedStaticLightingInteractions(0)
, UpperDynamicSkylightColor(FLinearColor::Black)
, LowerDynamicSkylightColor(FLinearColor::Black)
, SceneLODHierarchy(this)
, NumVisibleLights_GameThread(0)
, NumEnabledSkylights_GameThread(0)
{
check(World);
World->Scene = this;
FeatureLevel = World->FeatureLevel;
static auto* MobileHDRCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MobileHDR"));
static auto* MobileHDR32bppCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MobileHDR32bpp"));
bStaticDrawListsMobileHDR = MobileHDRCvar->GetValueOnAnyThread() == 1;
bStaticDrawListsMobileHDR32bpp = bStaticDrawListsMobileHDR && (GSupportsRenderTargetFormat_PF_FloatRGBA == false || MobileHDR32bppCvar->GetValueOnAnyThread() == 1);
static auto* EarlyZPassCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.EarlyZPass"));
StaticDrawListsEarlyZPassMode = EarlyZPassCvar->GetValueOnAnyThread();
if (World->FXSystem)
{
FFXSystemInterface::Destroy(World->FXSystem);
}
if (bCreateFXSystem)
{
World->CreateFXSystem();
}
else
{
World->FXSystem = NULL;
SetFXSystem(NULL);
}
World->UpdateParameterCollectionInstances(false);
}
FScene::~FScene()
{
#if 0 // if you have component that has invalid scene, try this code to see this is reason.
for (FObjectIterator 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
ReflectionSceneData.CubemapArray.ReleaseResource();
IndirectLightingCache.ReleaseResource();
DistanceFieldSceneData.Release();
if (SurfaceCacheResources)
{
SurfaceCacheResources->ReleaseResource();
delete SurfaceCacheResources;
SurfaceCacheResources = NULL;
}
if (AtmosphericFog)
{
delete AtmosphericFog;
AtmosphericFog = NULL;
}
}
void FScene::AddPrimitive(UPrimitiveComponent* Primitive)
{
SCOPE_CYCLE_COUNTER(STAT_AddScenePrimitiveGT);
checkf(!Primitive->HasAnyFlags(RF_Unreachable), TEXT("%s"), *Primitive->GetFullName());
// Save the world transform for next time the primitive is added to the scene
float DeltaTime = GetWorld()->GetTimeSeconds() - Primitive->LastSubmitTime;
if ( DeltaTime < -0.0001f || Primitive->LastSubmitTime < 0.0001f )
{
// Time was reset?
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
}
else if ( DeltaTime > 0.0001f )
{
// First call for the new frame?
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
}
// 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;
}
// Cache the primitive's initial transform.
FMatrix RenderMatrix = Primitive->GetRenderMatrix();
FVector OwnerPosition(0);
AActor* Owner = Primitive->GetOwner();
if (Owner)
{
OwnerPosition = Owner->GetActorLocation();
}
struct FCreateRenderThreadParameters
{
FPrimitiveSceneProxy* PrimitiveSceneProxy;
FMatrix RenderMatrix;
FBoxSphereBounds WorldBounds;
FVector OwnerPosition;
FBoxSphereBounds LocalBounds;
};
FCreateRenderThreadParameters Params =
{
PrimitiveSceneProxy,
RenderMatrix,
Primitive->Bounds,
OwnerPosition,
Primitive->CalcBounds(FTransform::Identity)
};
// Create any RenderThreadResources required.
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FCreateRenderThreadResourcesCommand,
FCreateRenderThreadParameters, Params, Params,
{
FPrimitiveSceneProxy* SceneProxy = Params.PrimitiveSceneProxy;
FScopeCycleCounter Context(SceneProxy->GetStatId());
SceneProxy->SetTransform(Params.RenderMatrix, Params.WorldBounds, Params.LocalBounds, Params.OwnerPosition);
// Create any RenderThreadResources required.
SceneProxy->CreateRenderThreadResources();
});
// Create the primitive scene info.
FPrimitiveSceneInfo* PrimitiveSceneInfo = new FPrimitiveSceneInfo(Primitive,this);
PrimitiveSceneProxy->PrimitiveSceneInfo = PrimitiveSceneInfo;
INC_DWORD_STAT_BY( STAT_GameToRendererMallocTotal, PrimitiveSceneProxy->GetMemoryFootprint() + PrimitiveSceneInfo->GetMemoryFootprint() );
// Verify the primitive is valid (this will compile away to a nop without CHECK_FOR_PIE_PRIMITIVE_ATTACH_SCENE_MISMATCH)
VerifyProperPIEScene(Primitive, World);
// Increment the attachment counter, the primitive is about to be attached to the scene.
Primitive->AttachmentCounter.Increment();
// Send a command to the rendering thread to add the primitive to the scene.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddPrimitiveCommand,
FScene*,Scene,this,
FPrimitiveSceneInfo*,PrimitiveSceneInfo,PrimitiveSceneInfo,
{
FScopeCycleCounter Context(PrimitiveSceneInfo->Proxy->GetStatId());
Scene->AddPrimitiveSceneInfo_RenderThread(RHICmdList, PrimitiveSceneInfo);
});
}
void FScene::UpdatePrimitiveTransform_RenderThread(FRHICommandListImmediate& RHICmdList, FPrimitiveSceneProxy* PrimitiveSceneProxy, const FBoxSphereBounds& WorldBounds, const FBoxSphereBounds& LocalBounds, const FMatrix& LocalToWorld, const FVector& OwnerPosition)
{
SCOPE_CYCLE_COUNTER(STAT_UpdatePrimitiveTransformRenderThreadTime);
const bool bUpdateStaticDrawLists = !PrimitiveSceneProxy->StaticElementsAlwaysUseProxyPrimitiveUniformBuffer();
// Remove the primitive from the scene at its old location
// (note that the octree update relies on the bounds not being modified yet).
PrimitiveSceneProxy->GetPrimitiveSceneInfo()->RemoveFromScene(bUpdateStaticDrawLists);
// Update the primitive motion blur information.
// hack
FScene* Scene = (FScene*)&PrimitiveSceneProxy->GetScene();
Scene->MotionBlurInfoData.UpdatePrimitiveMotionBlur(PrimitiveSceneProxy->GetPrimitiveSceneInfo());
// Update the primitive transform.
PrimitiveSceneProxy->SetTransform(LocalToWorld, WorldBounds, LocalBounds, OwnerPosition);
DistanceFieldSceneData.UpdatePrimitive(PrimitiveSceneProxy->GetPrimitiveSceneInfo());
// If the primitive has static mesh elements, it should have returned true from ShouldRecreateProxyOnUpdateTransform!
check(!(bUpdateStaticDrawLists && PrimitiveSceneProxy->GetPrimitiveSceneInfo()->StaticMeshes.Num()));
// Re-add the primitive to the scene with the new transform.
PrimitiveSceneProxy->GetPrimitiveSceneInfo()->AddToScene(RHICmdList, bUpdateStaticDrawLists);
}
void FScene::UpdatePrimitiveTransform(UPrimitiveComponent* Primitive)
{
SCOPE_CYCLE_COUNTER(STAT_UpdatePrimitiveTransformGT);
// Save the world transform for next time the primitive is added to the scene
float DeltaTime = GetWorld()->GetTimeSeconds() - Primitive->LastSubmitTime;
if ( DeltaTime < -0.0001f || Primitive->LastSubmitTime < 0.0001f )
{
// Time was reset?
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
}
else if ( DeltaTime > 0.0001f )
{
// First call for the new frame?
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
}
AActor* Owner = Primitive->GetOwner();
// If the root component of an actor is being moved, update all the actor position of the other components sharing that actor
if (Owner && Owner->GetRootComponent() == Primitive)
{
TInlineComponentArray<UPrimitiveComponent*> Components;
Owner->GetComponents(Components);
for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++)
{
UPrimitiveComponent* PrimitiveComponent = Components[ComponentIndex];
// Only update components that are already attached
if (PrimitiveComponent
&& PrimitiveComponent->SceneProxy
&& PrimitiveComponent != Primitive
// Don't bother if it is going to have its transform updated anyway
&& !PrimitiveComponent->IsRenderTransformDirty()
&& !PrimitiveComponent->IsRenderStateDirty())
{
PrimitiveComponent->SceneProxy->UpdateActorPosition(Owner->GetActorLocation());
}
}
}
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 OwnerPosition(0);
AActor* Actor = Primitive->GetOwner();
if (Actor != NULL)
{
OwnerPosition = Actor->GetActorLocation();
}
struct FPrimitiveUpdateParams
{
FScene* Scene;
FPrimitiveSceneProxy* PrimitiveSceneProxy;
FBoxSphereBounds WorldBounds;
FBoxSphereBounds LocalBounds;
FMatrix LocalToWorld;
FVector OwnerPosition;
};
FPrimitiveUpdateParams UpdateParams;
UpdateParams.Scene = this;
UpdateParams.PrimitiveSceneProxy = Primitive->SceneProxy;
UpdateParams.WorldBounds = Primitive->Bounds;
UpdateParams.LocalToWorld = Primitive->GetRenderMatrix();
UpdateParams.OwnerPosition = OwnerPosition;
UpdateParams.LocalBounds = Primitive->CalcBounds(FTransform::Identity);
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
UpdateTransformCommand,
FPrimitiveUpdateParams,UpdateParams,UpdateParams,
{
FScopeCycleCounter Context(UpdateParams.PrimitiveSceneProxy->GetStatId());
UpdateParams.Scene->UpdatePrimitiveTransform_RenderThread(RHICmdList, UpdateParams.PrimitiveSceneProxy, UpdateParams.WorldBounds, UpdateParams.LocalBounds, UpdateParams.LocalToWorld, UpdateParams.OwnerPosition);
});
}
}
else
{
// If the primitive doesn't have a scene info object yet, it must be added from scratch.
AddPrimitive(Primitive);
}
}
void FScene::UpdatePrimitiveLightingAttachmentRoot(UPrimitiveComponent* Primitive)
{
const UPrimitiveComponent* NewLightingAttachmentRoot = Cast<UPrimitiveComponent>(Primitive->GetAttachmentRoot());
if (NewLightingAttachmentRoot == Primitive)
{
NewLightingAttachmentRoot = NULL;
}
FPrimitiveComponentId NewComponentId = NewLightingAttachmentRoot ? NewLightingAttachmentRoot->ComponentId : FPrimitiveComponentId();
if (Primitive->SceneProxy)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
UpdatePrimitiveAttachment,
FPrimitiveSceneProxy*,Proxy,Primitive->SceneProxy,
FPrimitiveComponentId,NewComponentId,NewComponentId,
{
FPrimitiveSceneInfo* PrimitiveInfo = Proxy->GetPrimitiveSceneInfo();
PrimitiveInfo->UnlinkAttachmentGroup();
PrimitiveInfo->LightingAttachmentRoot = NewComponentId;
PrimitiveInfo->LinkAttachmentGroup();
});
}
}
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();
UPrimitiveComponent* CurrentPrimitive = Cast<UPrimitiveComponent>(Current);
check(Current);
if (CurrentPrimitive
&& CurrentPrimitive->GetWorld()
&& CurrentPrimitive->GetWorld()->Scene
&& CurrentPrimitive->GetWorld()->Scene == this
&& CurrentPrimitive->ShouldComponentAddToScene())
{
UpdatePrimitiveLightingAttachmentRoot(CurrentPrimitive);
}
for (int32 ChildIndex = 0; ChildIndex < Current->AttachChildren.Num(); ChildIndex++)
{
USceneComponent* ChildComponent = Current->AttachChildren[ChildIndex];
if (ChildComponent)
{
ProcessStack.Push(ChildComponent);
}
}
}
}
void FScene::RemovePrimitiveSceneInfo_RenderThread(FPrimitiveSceneInfo* PrimitiveSceneInfo)
{
SCOPE_CYCLE_COUNTER(STAT_RemoveScenePrimitiveTime);
// clear it up, parent is getting removed
SceneLODHierarchy.UpdateNodeSceneInfo(PrimitiveSceneInfo->PrimitiveComponentId, nullptr);
CheckPrimitiveArrays();
int32 PrimitiveIndex = PrimitiveSceneInfo->PackedIndex;
Primitives.RemoveAtSwap(PrimitiveIndex);
PrimitiveBounds.RemoveAtSwap(PrimitiveIndex);
PrimitiveVisibilityIds.RemoveAtSwap(PrimitiveIndex);
PrimitiveOcclusionFlags.RemoveAtSwap(PrimitiveIndex);
PrimitiveComponentIds.RemoveAtSwap(PrimitiveIndex);
PrimitiveOcclusionBounds.RemoveAtSwap(PrimitiveIndex);
if (Primitives.IsValidIndex(PrimitiveIndex))
{
FPrimitiveSceneInfo* OtherPrimitive = Primitives[PrimitiveIndex];
OtherPrimitive->PackedIndex = PrimitiveIndex;
}
CheckPrimitiveArrays();
// Update the primitive's motion blur information.
MotionBlurInfoData.RemovePrimitiveMotionBlur(PrimitiveSceneInfo);
// Unlink the primitive from its shadow parent.
PrimitiveSceneInfo->UnlinkAttachmentGroup();
// Unlink the LOD parent info if valid
PrimitiveSceneInfo->UnlinkLODParentComponent();
// Remove the primitive from the scene.
PrimitiveSceneInfo->RemoveFromScene(true);
DistanceFieldSceneData.RemovePrimitive(PrimitiveSceneInfo);
// free the primitive scene proxy.
delete PrimitiveSceneInfo->Proxy;
}
void FScene::RemovePrimitive( UPrimitiveComponent* Primitive )
{
SCOPE_CYCLE_COUNTER(STAT_RemoveScenePrimitiveGT);
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.
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
FRemovePrimitiveCommand,
FScene*,Scene,this,
FPrimitiveSceneInfo*,PrimitiveSceneInfo,PrimitiveSceneProxy->GetPrimitiveSceneInfo(),
FThreadSafeCounter*,AttachmentCounter,&Primitive->AttachmentCounter,
{
FScopeCycleCounter Context(PrimitiveSceneInfo->Proxy->GetStatId());
Scene->RemovePrimitiveSceneInfo_RenderThread(PrimitiveSceneInfo);
AttachmentCounter->Decrement();
});
// 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.
BeginCleanup(PrimitiveSceneInfo);
}
}
void FScene::ReleasePrimitive( UPrimitiveComponent* PrimitiveComponent )
{
// Send a command to the rendering thread to clean up any state dependent on this primitive
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FReleasePrimitiveCommand,
FScene*,Scene,this,
FPrimitiveComponentId,PrimitiveComponentId,PrimitiveComponent->ComponentId,
{
// Free the space in the indirect lighting cache
Scene->IndirectLightingCache.ReleasePrimitive(PrimitiveComponentId);
});
}
void FScene::AddLightSceneInfo_RenderThread(FLightSceneInfo* LightSceneInfo)
{
SCOPE_CYCLE_COUNTER(STAT_AddSceneLightTime);
check(LightSceneInfo->bVisible);
// Add the light to the light list.
LightSceneInfo->Id = Lights.Add(FLightSceneInfoCompact(LightSceneInfo));
const FLightSceneInfoCompact& LightSceneInfoCompact = Lights[LightSceneInfo->Id];
if (!SimpleDirectionalLight &&
LightSceneInfo->Proxy->GetLightType() == LightType_Directional &&
// Only use a stationary or movable light
!LightSceneInfo->Proxy->HasStaticLighting())
{
SimpleDirectionalLight = LightSceneInfo;
// if we are forward rendered and this light is a dynamic shadowcast then we need to update the static draw lists to pick a new lightingpolicy
bScenesPrimitivesNeedStaticMeshElementUpdate = bScenesPrimitivesNeedStaticMeshElementUpdate || (!ShouldUseDeferredRenderer() && !SimpleDirectionalLight->Proxy->HasStaticShadowing());
}
if (LightSceneInfo->Proxy->IsUsedAsAtmosphereSunLight() &&
(!SunLight || LightSceneInfo->Proxy->GetColor().ComputeLuminance() > SunLight->Proxy->GetColor().ComputeLuminance()) ) // choose brightest sun light...
{
SunLight = LightSceneInfo;
}
// Add the light to the scene.
LightSceneInfo->AddToScene();
}
void FScene::AddLight(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->ComponentToWorld.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.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddLightCommand,
FScene*,Scene,this,
FLightSceneInfo*,LightSceneInfo,Proxy->LightSceneInfo,
{
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->ComponentToWorld.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.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddLightCommand,
FScene*,Scene,this,
FLightSceneInfo*,LightSceneInfo,Proxy->LightSceneInfo,
{
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
LightSceneInfo->Id = Scene->InvisibleLights.Add(FLightSceneInfoCompact(LightSceneInfo));
});
}
}
void FScene::SetSkyLight(FSkyLightSceneProxy* LightProxy)
{
check(LightProxy);
NumEnabledSkylights_GameThread++;
// Send a command to the rendering thread to add the light to the scene.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FSetSkyLightCommand,
FScene*,Scene,this,
FSkyLightSceneProxy*,LightProxy,LightProxy,
{
check(!Scene->SkyLightStack.Contains(LightProxy));
Scene->SkyLightStack.Push(LightProxy);
const bool bHadSkylight = Scene->SkyLight != NULL;
// Use the most recently enabled skylight
Scene->SkyLight = LightProxy;
if (!bHadSkylight)
{
// 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;
}
});
}
void FScene::DisableSkyLight(FSkyLightSceneProxy* LightProxy)
{
check(LightProxy);
NumEnabledSkylights_GameThread--;
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FDisableSkyLightCommand,
FScene*,Scene,this,
FSkyLightSceneProxy*,LightProxy,LightProxy,
{
const bool bHadSkylight = Scene->SkyLight != NULL;
Scene->SkyLightStack.RemoveSingle(LightProxy);
if (Scene->SkyLightStack.Num() > 0)
{
// Use the most recently enabled skylight
Scene->SkyLight = Scene->SkyLightStack.Last();
}
else
{
Scene->SkyLight = NULL;
}
// Update the scene if we switched skylight enabled states
if ((Scene->SkyLight != NULL) != bHadSkylight)
{
Scene->bScenesPrimitivesNeedStaticMeshElementUpdate = true;
}
});
}
void FScene::AddOrRemoveDecal_RenderThread(FDeferredDecalProxy* Proxy, bool bAdd)
{
if(bAdd)
{
Decals.Add(Proxy);
}
else
{
// can be optimized
for(TSparseArray<FDeferredDecalProxy*>::TIterator It(Decals); It; ++It)
{
FDeferredDecalProxy* CurrentProxy = *It;
if (CurrentProxy == Proxy)
{
It.RemoveCurrent();
delete CurrentProxy;
break;
}
}
}
}
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.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddDecalCommand,
FScene*,Scene,this,
FDeferredDecalProxy*,Proxy,Component->SceneProxy,
{
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.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FRemoveDecalCommand,
FScene*,Scene,this,
FDeferredDecalProxy*,Proxy,Component->SceneProxy,
{
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.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
UpdateTransformCommand,
FDeferredDecalProxy*,DecalSceneProxy,Decal->SceneProxy,
FTransform,ComponentToWorld,Decal->GetComponentToWorld(),
{
// Update the primitive's transform.
DecalSceneProxy->SetTransform(ComponentToWorld);
});
}
}
void FScene::AddReflectionCapture(UReflectionCaptureComponent* Component)
{
if (!Component->SceneProxy)
{
Component->SceneProxy = Component->CreateSceneProxy();
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddCaptureCommand,
FScene*,Scene,this,
FReflectionCaptureProxy*,Proxy,Component->SceneProxy,
{
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
const int32 PackedIndex = Scene->ReflectionSceneData.RegisteredReflectionCaptures.Add(Proxy);
Proxy->PackedIndex = PackedIndex;
Scene->ReflectionSceneData.RegisteredReflectionCapturePositions.Add(Proxy->Position);
checkSlow(Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num() == Scene->ReflectionSceneData.RegisteredReflectionCapturePositions.Num());
});
}
}
void FScene::RemoveReflectionCapture(UReflectionCaptureComponent* Component)
{
if (Component->SceneProxy)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FRemoveCaptureCommand,
FScene*,Scene,this,
FReflectionCaptureProxy*,Proxy,Component->SceneProxy,
{
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
int32 CaptureIndex = Proxy->PackedIndex;
Scene->ReflectionSceneData.RegisteredReflectionCaptures.RemoveAtSwap(CaptureIndex);
Scene->ReflectionSceneData.RegisteredReflectionCapturePositions.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.RegisteredReflectionCapturePositions.Num());
});
// Disassociate the primitive's scene proxy.
Component->SceneProxy = NULL;
}
}
void FScene::UpdateReflectionCaptureTransform(UReflectionCaptureComponent* Component)
{
if (Component->SceneProxy)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
UpdateTransformCommand,
FReflectionCaptureProxy*,Proxy,Component->SceneProxy,
FMatrix,Transform,Component->ComponentToWorld.ToMatrixWithScale(),
FScene*,Scene,this,
{
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
Proxy->SetTransform(Transform);
});
}
}
void FScene::ReleaseReflectionCubemap(UReflectionCaptureComponent* CaptureComponent)
{
for (TSparseArray<UReflectionCaptureComponent*>::TIterator It(ReflectionSceneData.AllocatedReflectionCapturesGameThread); It; ++It)
{
UReflectionCaptureComponent* CurrentCapture = *It;
if (CurrentCapture == CaptureComponent)
{
It.RemoveCurrent();
break;
}
}
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
RemoveCaptureCommand,
UReflectionCaptureComponent*,Component,CaptureComponent,
FScene*,Scene,this,
{
Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Remove(Component);
});
}
const FReflectionCaptureProxy* FScene::FindClosestReflectionCapture(FVector Position) const
{
checkSlow(IsInParallelRenderingThread());
int32 ClosestCaptureIndex = INDEX_NONE;
float ClosestDistanceSquared = FLT_MAX;
// Linear search through the scene's reflection captures
// ReflectionSceneData.RegisteredReflectionCapturePositions has been packed densely to make this coherent in memory
for (int32 CaptureIndex = 0; CaptureIndex < ReflectionSceneData.RegisteredReflectionCapturePositions.Num(); CaptureIndex++)
{
const float DistanceSquared = (ReflectionSceneData.RegisteredReflectionCapturePositions[CaptureIndex] - Position).SizeSquared();
if (DistanceSquared < ClosestDistanceSquared)
{
ClosestDistanceSquared = DistanceSquared;
ClosestCaptureIndex = CaptureIndex;
}
}
return ClosestCaptureIndex != INDEX_NONE ? ReflectionSceneData.RegisteredReflectionCaptures[ClosestCaptureIndex] : NULL;
}
void FScene::GetCaptureParameters(const FReflectionCaptureProxy* ReflectionProxy, FTextureRHIParamRef& ReflectionCubemapArray, int32& ArrayIndex) const
{
ERHIFeatureLevel::Type LocalFeatureLevel = GetFeatureLevel();
if (LocalFeatureLevel >= ERHIFeatureLevel::SM5)
{
const FCaptureComponentSceneState* FoundState = ReflectionSceneData.AllocatedReflectionCaptureState.Find(ReflectionProxy->Component);
if (FoundState)
{
ReflectionCubemapArray = ReflectionSceneData.CubemapArray.GetRenderTarget().ShaderResourceTexture;
ArrayIndex = FoundState->CaptureIndex;
}
}
else if (ReflectionProxy->SM4FullHDRCubemap)
{
ReflectionCubemapArray = ReflectionProxy->SM4FullHDRCubemap->TextureRHI;
ArrayIndex = 0;
}
}
void FScene::AddPrecomputedLightVolume(const FPrecomputedLightVolume* Volume)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
AddVolumeCommand,
const FPrecomputedLightVolume*,Volume,Volume,
FScene*,Scene,this,
{
Scene->PrecomputedLightVolumes.Add(Volume);
Scene->IndirectLightingCache.SetLightingCacheDirty();
});
}
void FScene::RemovePrecomputedLightVolume(const FPrecomputedLightVolume* Volume)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
RemoveVolumeCommand,
const FPrecomputedLightVolume*,Volume,Volume,
FScene*,Scene,this,
{
Scene->PrecomputedLightVolumes.Remove(Volume);
Scene->IndirectLightingCache.SetLightingCacheDirty();
});
}
struct FUpdateLightTransformParameters
{
FMatrix LightToWorld;
FVector4 Position;
};
void FScene::UpdateLightTransform_RenderThread(FLightSceneInfo* LightSceneInfo, const FUpdateLightTransformParameters& Parameters)
{
SCOPE_CYCLE_COUNTER(STAT_UpdateSceneLightTime);
if( LightSceneInfo && LightSceneInfo->bVisible )
{
// Don't remove directional lights when their transform changes as nothing in RemoveFromScene() depends on their transform
if (!(LightSceneInfo->Proxy->GetLightType() == LightType_Directional))
{
// Remove the light from the scene.
LightSceneInfo->RemoveFromScene();
}
// Update the light's transform and position.
LightSceneInfo->Proxy->SetTransform(Parameters.LightToWorld,Parameters.Position);
// Also update the LightSceneInfoCompact
if( LightSceneInfo->Id != INDEX_NONE )
{
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 (!(LightSceneInfo->Proxy->GetLightType() == LightType_Directional))
{
// 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->ComponentToWorld.ToMatrixNoScale();
Parameters.Position = Light->GetLightPosition();
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
UpdateLightTransform,
FScene*,Scene,this,
FLightSceneInfo*,LightSceneInfo,Light->SceneProxy->GetLightSceneInfo(),
FUpdateLightTransformParameters,Parameters,Parameters,
{
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;
};
FUpdateLightColorParameters NewParameters;
NewParameters.NewColor = FLinearColor(Light->LightColor) * Light->ComputeLightBrightness();
NewParameters.NewIndirectLightingScale = Light->IndirectLightingIntensity;
if( Light->bUseTemperature )
{
NewParameters.NewColor *= FLinearColor::MakeFromColorTemperature(Light->Temperature);
}
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
UpdateLightColorAndBrightness,
FLightSceneInfo*,LightSceneInfo,Light->SceneProxy->GetLightSceneInfo(),
FScene*,Scene,this,
FUpdateLightColorParameters,Parameters,NewParameters,
{
if( LightSceneInfo && LightSceneInfo->bVisible )
{
LightSceneInfo->Proxy->SetColor(Parameters.NewColor);
LightSceneInfo->Proxy->IndirectLightingScale = Parameters.NewIndirectLightingScale;
// Also update the LightSceneInfoCompact
if( LightSceneInfo->Id != INDEX_NONE )
{
Scene->Lights[ LightSceneInfo->Id ].Color = Parameters.NewColor;
}
}
});
}
}
/** Updates the scene's dynamic skylight. */
void FScene::UpdateDynamicSkyLight(const FLinearColor& UpperColor, const FLinearColor& LowerColor)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
UpdateDynamicSkyLight,
FScene*,Scene,this,
FLinearColor,UpperColor,UpperColor,
FLinearColor,LowerColor,LowerColor,
{
Scene->UpperDynamicSkylightColor = UpperColor;
Scene->LowerDynamicSkylightColor = LowerColor;
});
}
void FScene::RemoveLightSceneInfo_RenderThread(FLightSceneInfo* LightSceneInfo)
{
SCOPE_CYCLE_COUNTER(STAT_RemoveSceneLightTime);
if (LightSceneInfo->bVisible)
{
if (LightSceneInfo == SimpleDirectionalLight)
{
// if we are forward rendered and this light is a dynamic shadowcast then we need to update the static draw lists to pick a new lightingpolicy
bScenesPrimitivesNeedStaticMeshElementUpdate = bScenesPrimitivesNeedStaticMeshElementUpdate || (!ShouldUseDeferredRenderer() && !SimpleDirectionalLight->Proxy->HasStaticShadowing());
SimpleDirectionalLight = NULL;
}
if (LightSceneInfo == SunLight)
{
SunLight = NULL;
// Search for new sun light...
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator It(Lights); It; ++It)
{
const FLightSceneInfoCompact& LightInfo = *It;
if (LightInfo.LightSceneInfo != LightSceneInfo
&& LightInfo.LightSceneInfo->Proxy->bUsedAsAtmosphereSunLight
&& (!SunLight || SunLight->Proxy->GetColor().ComputeLuminance() < LightInfo.LightSceneInfo->Proxy->GetColor().ComputeLuminance()) )
{
SunLight = LightInfo.LightSceneInfo;
}
}
}
// Remove the light from the scene.
LightSceneInfo->RemoveFromScene();
// Remove the light from the lights list.
Lights.RemoveAt(LightSceneInfo->Id);
}
else
{
InvisibleLights.RemoveAt(LightSceneInfo->Id);
}
// 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.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FRemoveLightCommand,
FScene*,Scene,this,
FLightSceneInfo*,LightSceneInfo,LightSceneInfo,
{
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
Scene->RemoveLightSceneInfo_RenderThread(LightSceneInfo);
});
}
}
void FScene::AddExponentialHeightFog(UExponentialHeightFogComponent* FogComponent)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddFogCommand,
FScene*,Scene,this,
FExponentialHeightFogSceneInfo,HeightFogSceneInfo,FExponentialHeightFogSceneInfo(FogComponent),
{
// Create a FExponentialHeightFogSceneInfo for the component in the scene's fog array.
new(Scene->ExponentialFogs) FExponentialHeightFogSceneInfo(HeightFogSceneInfo);
});
}
void FScene::RemoveExponentialHeightFog(UExponentialHeightFogComponent* FogComponent)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FRemoveFogCommand,
FScene*,Scene,this,
UExponentialHeightFogComponent*,FogComponent,FogComponent,
{
// 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);
break;
}
}
});
}
void FScene::AddAtmosphericFog(UAtmosphericFogComponent* FogComponent)
{
check(FogComponent);
FAtmosphericFogSceneInfo* FogSceneInfo = new FAtmosphericFogSceneInfo(FogComponent, this);
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddAtmosphericFogCommand,
FScene*,Scene,this,
FAtmosphericFogSceneInfo*,FogSceneInfo,FogSceneInfo,
{
if (Scene->AtmosphericFog && Scene->AtmosphericFog->Component != FogSceneInfo->Component)
{
delete Scene->AtmosphericFog;
Scene->AtmosphericFog = NULL;
}
if (Scene->AtmosphericFog == NULL)
{
Scene->AtmosphericFog = FogSceneInfo;
}
else
{
delete FogSceneInfo;
}
});
}
void FScene::RemoveAtmosphericFog(UAtmosphericFogComponent* FogComponent)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FRemoveAtmosphericFogCommand,
FScene*,Scene,this,
UAtmosphericFogComponent*,FogComponent,FogComponent,
{
// Remove the given component's FExponentialHeightFogSceneInfo from the scene's fog array.
if (Scene->AtmosphericFog && Scene->AtmosphericFog->Component == FogComponent)
{
delete Scene->AtmosphericFog;
Scene->AtmosphericFog = NULL;
}
});
}
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;
}
FWindSourceSceneProxy* SceneProxy = WindComponent->CreateSceneProxy();
WindComponent->SceneProxy = SceneProxy;
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddWindSourceCommand,
FScene*,Scene,this,
FWindSourceSceneProxy*,SceneProxy,SceneProxy,
{
Scene->WindSources.Add(SceneProxy);
});
}
void FScene::RemoveWindSource(UWindDirectionalSourceComponent* WindComponent)
{
FWindSourceSceneProxy* SceneProxy = WindComponent->SceneProxy;
WindComponent->SceneProxy = NULL;
if(SceneProxy)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FRemoveWindSourceCommand,
FScene*,Scene,this,
FWindSourceSceneProxy*,SceneProxy,SceneProxy,
{
Scene->WindSources.Remove(SceneProxy);
delete SceneProxy;
});
}
}
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
{
FWindSourceSceneProxy::FWindData AccumWindData;
AccumWindData.PrepareForAccumulate();
int32 NumActiveWindSources = 0;
FVector4 AccumulatedDirectionAndSpeed(0,0,0,0);
float TotalWeight = 0.0f;
for (int32 i = 0; i < WindSources.Num(); i++)
{
FVector4 CurrentDirectionAndSpeed;
float Weight;
const FWindSourceSceneProxy* CurrentSource = WindSources[i];
FWindSourceSceneProxy::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::GetDirectionalWindParameters(FVector& OutDirection, float& OutSpeed, float& OutMinGustAmt, float& OutMaxGustAmt) const
{
FWindSourceSceneProxy::FWindData AccumWindData;
AccumWindData.PrepareForAccumulate();
int32 NumActiveWindSources = 0;
FVector4 AccumulatedDirectionAndSpeed(0,0,0,0);
float TotalWeight = 0.0f;
for (int32 i = 0; i < WindSources.Num(); i++)
{
FVector4 CurrentDirectionAndSpeed;
float Weight;
const FWindSourceSceneProxy* CurrentSource = WindSources[i];
FWindSourceSceneProxy::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->RenderData.IsValid())
{
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
FAddSpeedTreeWindCommand,
FScene*,Scene,this,
const UStaticMesh*,StaticMesh,StaticMesh,
FVertexFactory*,VertexFactory,VertexFactory,
{
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( ));
WindComputation->UniformBuffer.InitResource();
Scene->SpeedTreeWindComputationMap.Add(StaticMesh, WindComputation);
}
});
}
}
void FScene::RemoveSpeedTreeWind(class FVertexFactory* VertexFactory, const class UStaticMesh* StaticMesh)
{
if (StaticMesh != NULL && StaticMesh->SpeedTreeWind.IsValid() && StaticMesh->RenderData.IsValid())
{
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
FRemoveSpeedTreeWindCommand,
FScene*,Scene,this,
const UStaticMesh*, StaticMesh, StaticMesh,
FVertexFactory*,VertexFactory,VertexFactory,
{
Scene->RemoveSpeedTreeWind_RenderThread(VertexFactory, StaticMesh);
});
}
}
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);
WindComputation->UniformBuffer.ReleaseResource();
delete WindComputation;
}
}
}
void FScene::UpdateSpeedTreeWind(double CurrentTime)
{
#define SET_SPEEDTREE_TABLE_FLOAT4V(name, offset) \
UniformParameters.name = *(FVector4*)(WindShaderValues + FSpeedTreeWind::offset); \
UniformParameters.Prev##name = *(FVector4*)(WindShaderValues + FSpeedTreeWind::offset + FSpeedTreeWind::NUM_SHADER_VALUES);
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FUpdateSpeedTreeWindCommand,
FScene*,Scene,this,
double,CurrentTime,CurrentTime,
{
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->RenderData )
{
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( ));
// make sure the vertex factories are registered (sometimes goes wrong during a reimport)
for (int32 LODIndex = 0; LODIndex < StaticMesh->RenderData->LODResources.Num(); ++LODIndex)
{
Scene->SpeedTreeVertexFactoryMap.Add(&StaticMesh->RenderData->LODResources[LODIndex].VertexFactory, StaticMesh);
}
}
// 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.SetContents(UniformParameters);
}
});
#undef SET_SPEEDTREE_TABLE_FLOAT4V
}
FUniformBufferRHIParamRef FScene::GetSpeedTreeUniformBuffer(const FVertexFactory* VertexFactory)
{
if (VertexFactory != NULL)
{
const UStaticMesh** StaticMesh = SpeedTreeVertexFactoryMap.Find(VertexFactory);
if (StaticMesh != NULL)
{
FSpeedTreeWindComputation** WindComputation = SpeedTreeWindComputationMap.Find(*StaticMesh);
if (WindComputation != NULL)
{
return (*WindComputation)->UniformBuffer.GetUniformBufferRHI();
}
}
}
return FUniformBufferRHIParamRef();
}
/**
* 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.
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
FGetRelevantLightsCommand,
const FScene*,Scene,this,
UPrimitiveComponent*,Primitive,Primitive,
TArray<const ULightComponent*>*,RelevantLights,RelevantLights,
{
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)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
UpdatePrecomputedVisibility,
FScene*,Scene,this,
const FPrecomputedVisibilityHandler*,PrecomputedVisibilityHandler,NewPrecomputedVisibilityHandler,
{
Scene->PrecomputedVisibilityHandler = PrecomputedVisibilityHandler;
});
}
void FScene::SetShaderMapsOnMaterialResources_RenderThread(FRHICommandListImmediate& RHICmdList, const FMaterialsToUpdateMap& MaterialsToUpdate)
{
SCOPE_CYCLE_COUNTER(STAT_Scene_SetShaderMapsOnMaterialResources_RT);
TArray<const FMaterial*> MaterialArray;
for (FMaterialsToUpdateMap::TConstIterator It(MaterialsToUpdate); It; ++It)
{
FMaterial* Material = It.Key();
FMaterialShaderMap* ShaderMap = It.Value();
Material->SetRenderingThreadShaderMap(ShaderMap);
check(!ShaderMap || ShaderMap->IsValidForRendering());
MaterialArray.Add(Material);
}
const auto SceneFeatureLevel = GetFeatureLevel();
bool bFoundAnyInitializedMaterials = false;
// Iterate through all loaded material render proxies and recache their uniform expressions if needed
// This search does not scale well, but is only used when uploading async shader compile results
for (TSet<FMaterialRenderProxy*>::TConstIterator It(FMaterialRenderProxy::GetMaterialRenderProxyMap()); It; ++It)
{
FMaterialRenderProxy* MaterialProxy = *It;
FMaterial* Material = MaterialProxy->GetMaterialNoFallback(SceneFeatureLevel);
if (Material && MaterialsToUpdate.Contains(Material))
{
// Materials used as async fallbacks can't be updated through this mechanism and should have been updated synchronously earlier
check(!Material->RequiresSynchronousCompilation());
MaterialProxy->CacheUniformExpressions();
bFoundAnyInitializedMaterials = true;
const FMaterial& MaterialForRendering = *MaterialProxy->GetMaterial(SceneFeatureLevel);
check(MaterialForRendering.GetRenderingThreadShaderMap());
check(!MaterialProxy->UniformExpressionCache[SceneFeatureLevel].bUpToDate
|| MaterialProxy->UniformExpressionCache[SceneFeatureLevel].CachedUniformExpressionShaderMap == MaterialForRendering.GetRenderingThreadShaderMap());
check(MaterialForRendering.GetRenderingThreadShaderMap()->IsValidForRendering());
}
}
// Update static draw lists, which cache shader references from materials, but the shader map has now changed
if (bFoundAnyInitializedMaterials)
{
UpdateStaticDrawListsForMaterials_RenderThread(RHICmdList, MaterialArray);
}
}
void FScene::SetShaderMapsOnMaterialResources(const TMap<FMaterial*, class FMaterialShaderMap*>& MaterialsToUpdate)
{
for (TMap<FMaterial*, FMaterialShaderMap*>::TConstIterator It(MaterialsToUpdate); It; ++It)
{
FMaterial* Material = It.Key();
check(!Material->RequiresSynchronousCompilation());
}
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FSetShaderMapOnMaterialResources,
FScene*,Scene,this,
FMaterialsToUpdateMap,MaterialsToUpdate,MaterialsToUpdate,
{
Scene->SetShaderMapsOnMaterialResources_RenderThread(RHICmdList, MaterialsToUpdate);
});
}
void FScene::UpdateStaticDrawListsForMaterials_RenderThread(FRHICommandListImmediate& RHICmdList, const TArray<const FMaterial*>& Materials)
{
SCOPE_CYCLE_COUNTER(STAT_Scene_UpdateStaticDrawListsForMaterials_RT);
// Warning: if any static draw lists are missed here, there will be a crash when trying to render with shaders that have been deleted!
TArray<FPrimitiveSceneInfo*> PrimitivesToUpdate;
auto SceneFeatureLevel = GetFeatureLevel();
if (ShouldUseDeferredRenderer())
{
for (int32 DrawType = 0; DrawType < EBasePass_MAX; DrawType++)
{
BasePassNoLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassSimpleDynamicLightingDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassCachedVolumeIndirectLightingDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassCachedPointIndirectLightingDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassHighQualityLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassDistanceFieldShadowMapLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassLowQualityLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassSelfShadowedTranslucencyDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
}
}
else
{
for (int32 DrawType = 0; DrawType < EBasePass_MAX; DrawType++)
{
BasePassForForwardShadingNoLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingLowQualityLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingMovableDirectionalLightDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingMovableDirectionalLightCSMDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingMovableDirectionalLightLightmapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
BasePassForForwardShadingMovableDirectionalLightCSMLightmapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
}
}
PositionOnlyDepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
DepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
MaskedDepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
HitProxyDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
HitProxyDrawList_OpaqueOnly.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
VelocityDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
WholeSceneShadowDepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
WholeSceneReflectiveShadowMapDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
for (int32 PrimitiveIndex = 0; PrimitiveIndex < PrimitivesToUpdate.Num(); PrimitiveIndex++)
{
FPrimitiveSceneInfo* Primitive = PrimitivesToUpdate[PrimitiveIndex];
Primitive->RemoveStaticMeshes();
Primitive->AddStaticMeshes(RHICmdList);
}
}
void FScene::UpdateStaticDrawListsForMaterials(const TArray<const FMaterial*>& Materials)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FUpdateDrawLists,
FScene*,Scene,this,
TArray<const FMaterial*>,Materials,Materials,
{
Scene->UpdateStaticDrawListsForMaterials_RenderThread(RHICmdList, Materials);
});
}
/**
* @return true if hit proxies should be rendered in this scene.
*/
bool FScene::RequiresHitProxies() const
{
return (GIsEditor && bRequiresHitProxies);
}
void 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 ( !ensureMsg(!ActorComponent->IsRegistered() || ActorComponent->GetScene() != this,
*FString::Printf(TEXT("Component Name: %s World Name: %s Component Mesh: %s"),
*ActorComponent->GetFullName(),
*GetWorld()->GetFullName(),
Cast<UStaticMeshComponent>(ActorComponent) ? *CastChecked<UStaticMeshComponent>(ActorComponent)->StaticMesh->GetFullName() : TEXT("Not a static mesh"))) )
{
bTriggeredOnce = true;
break;
}
}
}
#endif
GetRendererModule().RemoveScene(this);
// Send a command to the rendering thread to release the scene.
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FReleaseCommand,
FScene*,Scene,this,
{
delete Scene;
});
}
void FScene::ConditionalMarkStaticMeshElementsForUpdate()
{
static auto* EarlyZPassCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.EarlyZPass"));
bool bMobileHDR = IsMobileHDR();
bool bMobileHDR32bpp = IsMobileHDR32bpp();
int32 DesiredStaticDrawListsEarlyZPassMode = EarlyZPassCvar->GetValueOnRenderThread();
if (bScenesPrimitivesNeedStaticMeshElementUpdate
|| bStaticDrawListsMobileHDR != bMobileHDR
|| bStaticDrawListsMobileHDR32bpp != bMobileHDR32bpp
|| StaticDrawListsEarlyZPassMode != DesiredStaticDrawListsEarlyZPassMode)
{
// 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;
bStaticDrawListsMobileHDR = bMobileHDR;
bStaticDrawListsMobileHDR32bpp = bMobileHDR32bpp;
StaticDrawListsEarlyZPassMode = DesiredStaticDrawListsEarlyZPassMode;
}
}
void FScene::DumpUnbuiltLightIteractions( FOutputDevice& Ar ) const
{
FlushRenderingCommands();
TArray<FString> LightsWithUnbuiltInteractions;
TArray<FString> PrimitivesWithUnbuiltInteractions;
// if want to print out all of the lights
for( TSparseArray<FLightSceneInfoCompact>::TConstIterator It(Lights); It; ++It )
{
const FLightSceneInfoCompact& LightCompactInfo = *It;
FLightSceneInfo* LightSceneInfo = LightCompactInfo.LightSceneInfo;
bool bLightHasUnbuiltInteractions = false;
for(FLightPrimitiveInteraction* Interaction = LightSceneInfo->DynamicPrimitiveList;
Interaction;
Interaction = Interaction->GetNextPrimitive())
{
if (Interaction->IsUncachedStaticLighting())
{
bLightHasUnbuiltInteractions = true;
PrimitivesWithUnbuiltInteractions.AddUnique(Interaction->GetPrimitiveSceneInfo()->ComponentForDebuggingOnly->GetFullName());
}
}
if (bLightHasUnbuiltInteractions)
{
LightsWithUnbuiltInteractions.AddUnique(LightSceneInfo->Proxy->GetComponentName().ToString());
}
}
Ar.Logf( TEXT( "DumpUnbuiltLightIteractions" ) );
Ar.Logf( TEXT( "Lights with unbuilt interactions: %d" ), LightsWithUnbuiltInteractions.Num() );
for (int Index = 0; Index < LightsWithUnbuiltInteractions.Num(); Index++)
{
Ar.Logf(*(FString(TEXT(" Light ")) + LightsWithUnbuiltInteractions[Index]));
}
Ar.Logf( TEXT( "" ) );
Ar.Logf( TEXT( "Primitives with unbuilt interactions: %d" ), PrimitivesWithUnbuiltInteractions.Num() );
for (int Index = 0; Index < PrimitivesWithUnbuiltInteractions.Num(); Index++)
{
Ar.Logf(*(FString(TEXT(" Primitive ")) + PrimitivesWithUnbuiltInteractions[Index]));
}
}
/**
* Logs the provided draw list stats.
*/
static void LogDrawListStats(FDrawListStats Stats, const TCHAR* DrawListName)
{
if (Stats.NumDrawingPolicies == 0 || Stats.NumMeshes == 0)
{
UE_LOG(LogRenderer,Log,TEXT("%s: empty"), DrawListName);
}
else
{
UE_LOG(LogRenderer,Log,
TEXT("%s: %d policies %d meshes\n")
TEXT(" - %d median meshes/policy\n")
TEXT(" - %f mean meshes/policy\n")
TEXT(" - %d max meshes/policy\n")
TEXT(" - %d policies with one mesh"),
DrawListName,
Stats.NumDrawingPolicies,
Stats.NumMeshes,
Stats.MedianMeshesPerDrawingPolicy,
(float)Stats.NumMeshes / (float)Stats.NumDrawingPolicies,
Stats.MaxMeshesPerDrawingPolicy,
Stats.NumSingleMeshDrawingPolicies
);
}
}
void FScene::DumpStaticMeshDrawListStats() const
{
UE_LOG(LogRenderer,Log,TEXT("Static mesh draw lists for %s:"),
World ? *World->GetFullName() : TEXT("[no world]")
);
#define DUMP_DRAW_LIST(Name) LogDrawListStats(Name.GetStats(), TEXT(#Name))
DUMP_DRAW_LIST(PositionOnlyDepthDrawList);
DUMP_DRAW_LIST(DepthDrawList);
DUMP_DRAW_LIST(MaskedDepthDrawList);
DUMP_DRAW_LIST(BasePassNoLightMapDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassNoLightMapDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassSimpleDynamicLightingDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassSimpleDynamicLightingDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassCachedVolumeIndirectLightingDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassCachedVolumeIndirectLightingDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassCachedPointIndirectLightingDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassCachedPointIndirectLightingDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassHighQualityLightMapDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassHighQualityLightMapDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassDistanceFieldShadowMapLightMapDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassDistanceFieldShadowMapLightMapDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassLowQualityLightMapDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassLowQualityLightMapDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassSelfShadowedTranslucencyDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassSelfShadowedTranslucencyDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingNoLightMapDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingNoLightMapDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingLowQualityLightMapDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingLowQualityLightMapDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightCSMDrawList[EBasePass_Default]);
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightCSMDrawList[EBasePass_Masked]);
DUMP_DRAW_LIST(HitProxyDrawList);
DUMP_DRAW_LIST(HitProxyDrawList_OpaqueOnly);
DUMP_DRAW_LIST(VelocityDrawList);
DUMP_DRAW_LIST(WholeSceneShadowDepthDrawList);
#undef DUMP_DRAW_LIST
}
/**
* Dumps stats for all scenes to the log.
*/
static void DumpDrawListStats()
{
for (TObjectIterator<UWorld> It; It; ++It)
{
UWorld* World = *It;
if (World && World->Scene)
{
World->Scene->DumpStaticMeshDrawListStats();
}
}
}
static FAutoConsoleCommand GDumpDrawListStatsCmd(
TEXT("r.DumpDrawListStats"),
TEXT("Dumps static mesh draw list statistics for all scenes associated with ")
TEXT("world objects."),
FConsoleCommandDelegate::CreateStatic(&DumpDrawListStats)
);
/**
* 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
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FApplyWorldOffset,
FScene*,Scene,this,
FVector,InOffset,InOffset,
{
Scene->ApplyWorldOffset_RenderThread(InOffset);
});
}
// StaticMeshDrawList elements shifting
template<typename T>
static void StaticMeshDrawListApplyWorldOffset(T& InList, FVector InOffset)
{
InList.ApplyWorldOffset(InOffset);
}
// StaticMeshDrawList elements shifting: specialization for an arrays
template<typename T, int32 N>
static void StaticMeshDrawListApplyWorldOffset(T(&InList)[N], FVector InOffset)
{
for (int32 i = 0; i < N; i++)
{
InList[i].ApplyWorldOffset(InOffset);
}
}
void FScene::ApplyWorldOffset_RenderThread(FVector InOffset)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_SceneApplyWorldOffset);
// Primitives
for (auto It = Primitives.CreateIterator(); It; ++It)
{
(*It)->ApplyWorldOffset(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();
// Primitives octree
PrimitiveOctree.ApplyOffset(InOffset);
// Primitive bounds
for (auto It = PrimitiveBounds.CreateIterator(); It; ++It)
{
(*It).Origin+= InOffset;
}
// Primitive occlusion bounds
for (auto It = PrimitiveOcclusionBounds.CreateIterator(); It; ++It)
{
(*It).Origin+= InOffset;
}
// Lights
VectorRegister OffsetReg = VectorLoadFloat3_W0(&InOffset);
for (auto It = Lights.CreateIterator(); It; ++It)
{
(*It).BoundingSphereVector = VectorAdd((*It).BoundingSphereVector, OffsetReg);
(*It).LightSceneInfo->Proxy->ApplyWorldOffset(InOffset);
}
// Lights octree
LightOctree.ApplyOffset(InOffset);
// 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 = (*It)->BoxTransform.Inverse().ConcatTranslation(InOffset);
(*It)->SetTransform(NewTransform);
}
// StaticMeshDrawLists
StaticMeshDrawListApplyWorldOffset(PositionOnlyDepthDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(DepthDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(MaskedDepthDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassNoLightMapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassCachedVolumeIndirectLightingDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassCachedPointIndirectLightingDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassSimpleDynamicLightingDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassHighQualityLightMapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassDistanceFieldShadowMapLightMapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassLowQualityLightMapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassSelfShadowedTranslucencyDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(HitProxyDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(HitProxyDrawList_OpaqueOnly, InOffset);
StaticMeshDrawListApplyWorldOffset(VelocityDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(WholeSceneShadowDepthDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingNoLightMapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingLowQualityLightMapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightCSMDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightLightmapDrawList, InOffset);
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightCSMLightmapDrawList, InOffset);
// Motion blur
MotionBlurInfoData.ApplyOffset(InOffset);
}
void FScene::OnLevelAddedToWorld(FName LevelAddedName)
{
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FLevelAddedToWorld,
class FScene*, Scene, this,
FName, LevelName, LevelAddedName,
{
Scene->OnLevelAddedToWorld_RenderThread(LevelName);
});
}
void FScene::OnLevelAddedToWorld_RenderThread(FName InLevelName)
{
// Mark level primitives
for (auto It = Primitives.CreateIterator(); It; ++It)
{
FPrimitiveSceneProxy* Proxy = (*It)->Proxy;
if (Proxy->LevelName == InLevelName)
{
Proxy->bIsComponentLevelVisible = true;
if (Proxy->NeedsLevelAddedToWorldNotification())
{
Proxy->OnLevelAddedToWorld();
}
}
}
}
/**
* Dummy NULL scene interface used by dedicated servers.
*/
class FNULLSceneInterface : public FSceneInterface
{
public:
FNULLSceneInterface(UWorld* InWorld, bool bCreateFXSystem )
: 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 {}
/** Updates the transform of a primitive which has already been added to the scene. */
virtual void UpdatePrimitiveTransform(UPrimitiveComponent* Primitive) override {}
virtual void UpdatePrimitiveAttachment(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 void AddDecal(UDecalComponent*) override {}
virtual void RemoveDecal(UDecalComponent*) override {}
virtual void UpdateDecalTransform(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 void AddAtmosphericFog(class UAtmosphericFogComponent* FogComponent) override {}
virtual void RemoveAtmosphericFog(class UAtmosphericFogComponent* FogComponent) override {}
virtual FAtmosphericFogSceneInfo* GetAtmosphericFogSceneInfo() override { return NULL; }
virtual void AddWindSource(class UWindDirectionalSourceComponent* WindComponent) override {}
virtual void RemoveWindSource(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 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(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 FUniformBufferRHIParamRef GetSpeedTreeUniformBuffer(const FVertexFactory* VertexFactory) override { return FUniformBufferRHIParamRef(); }
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; }
private:
UWorld* World;
class FFXSystemInterface* FXSystem;
};
FSceneInterface* FRendererModule::AllocateScene(UWorld* World, bool bInRequiresHitProxies, bool bCreateFXSystem, ERHIFeatureLevel::Type InFeatureLevel)
{
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->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::UpdateStaticDrawListsForMaterials(const TArray<const FMaterial*>& Materials)
{
for (TSet<FSceneInterface*>::TConstIterator SceneIt(AllocatedScenes); SceneIt; ++SceneIt)
{
(*SceneIt)->UpdateStaticDrawListsForMaterials(Materials);
}
}
FSceneViewStateInterface* FRendererModule::AllocateViewState()
{
return new FSceneViewState();
}
/** Maps the no light-map case to the appropriate base pass draw list. */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy<FNoLightMapPolicy> >& FScene::GetBasePassDrawList<FNoLightMapPolicy>(EBasePassDrawListType DrawType)
{
return BasePassNoLightMapDrawList[DrawType];
}
/** Maps the directional light-map texture case to the appropriate base pass draw list. */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy< TLightMapPolicy<HQ_LIGHTMAP> > >& FScene::GetBasePassDrawList< TLightMapPolicy<HQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
{
return BasePassHighQualityLightMapDrawList[DrawType];
}
/** */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy< TDistanceFieldShadowsAndLightMapPolicy<HQ_LIGHTMAP> > >& FScene::GetBasePassDrawList< TDistanceFieldShadowsAndLightMapPolicy<HQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
{
return BasePassDistanceFieldShadowMapLightMapDrawList[DrawType];
}
/** Maps the simple light-map texture case to the appropriate base pass draw list. */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy< TLightMapPolicy<LQ_LIGHTMAP> > >& FScene::GetBasePassDrawList< TLightMapPolicy<LQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
{
return BasePassLowQualityLightMapDrawList[DrawType];
}
/** */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy<FSelfShadowedTranslucencyPolicy> >& FScene::GetBasePassDrawList<FSelfShadowedTranslucencyPolicy>(EBasePassDrawListType DrawType)
{
return BasePassSelfShadowedTranslucencyDrawList[DrawType];
}
/** */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy<FSelfShadowedCachedPointIndirectLightingPolicy> >& FScene::GetBasePassDrawList<FSelfShadowedCachedPointIndirectLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[DrawType];
}
/** */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy<FCachedVolumeIndirectLightingPolicy> >& FScene::GetBasePassDrawList<FCachedVolumeIndirectLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassCachedVolumeIndirectLightingDrawList[DrawType];
}
/** */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy<FCachedPointIndirectLightingPolicy> >& FScene::GetBasePassDrawList<FCachedPointIndirectLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassCachedPointIndirectLightingDrawList[DrawType];
}
/** */
template<>
TStaticMeshDrawList<TBasePassDrawingPolicy<FSimpleDynamicLightingPolicy> >& FScene::GetBasePassDrawList<FSimpleDynamicLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassSimpleDynamicLightingDrawList[DrawType];
}
/** Maps the no light-map case to the appropriate base pass draw list. */
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FNoLightMapPolicy> >& FScene::GetForwardShadingBasePassDrawList<FNoLightMapPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingNoLightMapDrawList[DrawType];
}
/** Maps the simple light-map texture case to the appropriate base pass draw list. */
template<>
TStaticMeshDrawList< TBasePassForForwardShadingDrawingPolicy< TLightMapPolicy<LQ_LIGHTMAP> > >& FScene::GetForwardShadingBasePassDrawList< TLightMapPolicy<LQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingLowQualityLightMapDrawList[DrawType];
}
template<>
TStaticMeshDrawList< TBasePassForForwardShadingDrawingPolicy< TDistanceFieldShadowsAndLightMapPolicy<LQ_LIGHTMAP> > >& FScene::GetForwardShadingBasePassDrawList< TDistanceFieldShadowsAndLightMapPolicy<LQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[DrawType];
}
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FSimpleDirectionalLightAndSHIndirectPolicy> >& FScene::GetForwardShadingBasePassDrawList<FSimpleDirectionalLightAndSHIndirectPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[DrawType];
}
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FSimpleDirectionalLightAndSHDirectionalIndirectPolicy> >& FScene::GetForwardShadingBasePassDrawList<FSimpleDirectionalLightAndSHDirectionalIndirectPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[DrawType];
}
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FSimpleDirectionalLightAndSHDirectionalCSMIndirectPolicy> >& FScene::GetForwardShadingBasePassDrawList<FSimpleDirectionalLightAndSHDirectionalCSMIndirectPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[DrawType];
}
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingMovableDirectionalLightDrawList[DrawType];
}
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightCSMLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightCSMLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingMovableDirectionalLightCSMDrawList[DrawType];
}
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightWithLightmapLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightWithLightmapLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingMovableDirectionalLightLightmapDrawList[DrawType];
}
template<>
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightCSMWithLightmapLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightCSMWithLightmapLightingPolicy>(EBasePassDrawListType DrawType)
{
return BasePassForForwardShadingMovableDirectionalLightCSMLightmapDrawList[DrawType];
}
/*-----------------------------------------------------------------------------
MotionBlurInfoData
-----------------------------------------------------------------------------*/
FMotionBlurInfoData::FMotionBlurInfoData()
: bShouldClearMotionBlurInfo(false)
, bWorldIsPaused(false)
{
}
void FMotionBlurInfoData::UpdatePrimitiveMotionBlur(FPrimitiveSceneInfo* PrimitiveSceneInfo)
{
check(PrimitiveSceneInfo && IsInRenderingThread());
const FPrimitiveSceneProxy* Proxy = PrimitiveSceneInfo->Proxy;
FPrimitiveComponentId ComponentId = PrimitiveSceneInfo->PrimitiveComponentId;
if (Proxy != NULL && ComponentId.IsValid() && Proxy->IsMovable())
{
FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(ComponentId);
if(MotionBlurInfo)
{
if(!MotionBlurInfo->GetPrimitiveSceneInfo())
{
MotionBlurInfo->SetPrimitiveSceneInfo(PrimitiveSceneInfo);
}
}
else
{
// add to the end
MotionBlurInfo = &MotionBlurInfos.Add(ComponentId, FMotionBlurInfo(ComponentId, PrimitiveSceneInfo));
}
//request that this primitive scene info caches its transform at the end of the frame
MotionBlurInfo->SetKeepAndUpdateThisFrame();
}
}
void FMotionBlurInfoData::RemovePrimitiveMotionBlur(FPrimitiveSceneInfo* PrimitiveSceneInfo)
{
check(PrimitiveSceneInfo && IsInRenderingThread());
const FPrimitiveSceneProxy* Proxy = PrimitiveSceneInfo->Proxy;
if (Proxy != NULL && PrimitiveSceneInfo->PrimitiveComponentId.IsValid() && Proxy->IsMovable())
{
FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(PrimitiveSceneInfo->PrimitiveComponentId);
if(MotionBlurInfo)
{
// in case someone called SetKeepAndUpdateThisFrame() before
MotionBlurInfo->SetKeepAndUpdateThisFrame(false);
MotionBlurInfo->SetPrimitiveSceneInfo(0);
}
}
}
void FMotionBlurInfo::UpdateMotionBlurInfo()
{
if(MBPrimitiveSceneInfo && MBPrimitiveSceneInfo->Proxy)
{
// only if the proxy is still there
CurrentLocalToWorld = MBPrimitiveSceneInfo->Proxy->GetLocalToWorld();
}
bKeepAndUpdateThisFrame = false;
}
// Doxygen has trouble parsing these functions because the header declaring them is in Engine, not Renderer
#if !UE_BUILD_DOCS
void FMotionBlurInfoData::StartFrame(bool bInWorldIsPaused)
{
bWorldIsPaused = bInWorldIsPaused;
if(!bWorldIsPaused)
{
for (TMap<FPrimitiveComponentId, FMotionBlurInfo>::TIterator It(MotionBlurInfos); It; ++It)
{
FMotionBlurInfo& MotionBlurInfo = It.Value();
MotionBlurInfo.OnStartFrame();
}
}
}
void FMotionBlurInfoData::UpdateMotionBlurCache(FScene* InScene)
{
check(InScene && IsInRenderingThread());
if(bWorldIsPaused)
{
return;
}
if (InScene->GetFeatureLevel() >= ERHIFeatureLevel::SM4)
{
if(bShouldClearMotionBlurInfo)
{
// Clear the motion blur information for this frame.
MotionBlurInfos.Empty();
bShouldClearMotionBlurInfo = false;
}
else
{
for (TMap<FPrimitiveComponentId, FMotionBlurInfo>::TIterator It(MotionBlurInfos); It; ++It)
{
FMotionBlurInfo& MotionBlurInfo = It.Value();
if (MotionBlurInfo.GetKeepAndUpdateThisFrame())
{
MotionBlurInfo.UpdateMotionBlurInfo();
}
else
{
It.RemoveCurrent();
}
}
}
}
}
void FMotionBlurInfoData::SetClearMotionBlurInfo()
{
bShouldClearMotionBlurInfo = true;
}
void FMotionBlurInfoData::ApplyOffset(FVector InOffset)
{
for (auto It = MotionBlurInfos.CreateIterator(); It; ++It)
{
It.Value().ApplyOffset(InOffset);
}
}
FString FMotionBlurInfoData::GetDebugString() const
{
return FString::Printf(TEXT("Num=%d Clear=%d"), MotionBlurInfos.Num(), bShouldClearMotionBlurInfo);
}
const FMotionBlurInfo* FMotionBlurInfoData::FindMBInfoIndex(FPrimitiveComponentId ComponentId) const
{
return MotionBlurInfos.Find(ComponentId);
}
FMotionBlurInfo* FMotionBlurInfoData::FindMBInfoIndex(FPrimitiveComponentId ComponentId)
{
return MotionBlurInfos.Find(ComponentId);
}
bool FMotionBlurInfoData::GetPrimitiveMotionBlurInfo(const FPrimitiveSceneInfo* PrimitiveSceneInfo, FMatrix& OutPreviousLocalToWorld)
{
check(IsInParallelRenderingThread());
if (PrimitiveSceneInfo && PrimitiveSceneInfo->PrimitiveComponentId.IsValid())
{
FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(PrimitiveSceneInfo->PrimitiveComponentId);
if(MotionBlurInfo)
{
OutPreviousLocalToWorld = MotionBlurInfo->GetPreviousLocalToWorld();
return true;
}
}
return false;
}
bool FMotionBlurInfoData::GetPrimitiveMotionBlurInfo(const FPrimitiveSceneInfo* PrimitiveSceneInfo, FMatrix& OutPreviousLocalToWorld) const
{
check(IsInParallelRenderingThread());
if (PrimitiveSceneInfo && PrimitiveSceneInfo->PrimitiveComponentId.IsValid())
{
const FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(PrimitiveSceneInfo->PrimitiveComponentId);
if (MotionBlurInfo)
{
OutPreviousLocalToWorld = MotionBlurInfo->GetPreviousLocalToWorld();
return true;
}
}
return false;
}
#endif