// 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 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& 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(*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 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(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 > 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(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::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::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::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& 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::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* 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* 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*,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 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::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& MaterialsToUpdate) { for (TMap::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& 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 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& Materials) { ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER( FUpdateDrawLists, FScene*,Scene,this, TArray,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()) { if ( !ensureMsg(!ActorComponent->IsRegistered() || ActorComponent->GetScene() != this, *FString::Printf(TEXT("Component Name: %s World Name: %s Component Mesh: %s"), *ActorComponent->GetFullName(), *GetWorld()->GetFullName(), Cast(ActorComponent) ? *CastChecked(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 LightsWithUnbuiltInteractions; TArray PrimitivesWithUnbuiltInteractions; // if want to print out all of the lights for( TSparseArray::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 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 static void StaticMeshDrawListApplyWorldOffset(T& InList, FVector InOffset) { InList.ApplyWorldOffset(InOffset); } // StaticMeshDrawList elements shifting: specialization for an arrays template 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(It)->ApplyWorldOffset(InOffset); } // Precomputed visibility if (PrecomputedVisibilityHandler) { const_cast(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& GetWindSources_RenderThread() const override { static TArray 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* 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& Materials) { for (TSet::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 >& FScene::GetBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassNoLightMapDrawList[DrawType]; } /** Maps the directional light-map texture case to the appropriate base pass draw list. */ template<> TStaticMeshDrawList > >& FScene::GetBasePassDrawList< TLightMapPolicy >(EBasePassDrawListType DrawType) { return BasePassHighQualityLightMapDrawList[DrawType]; } /** */ template<> TStaticMeshDrawList > >& FScene::GetBasePassDrawList< TDistanceFieldShadowsAndLightMapPolicy >(EBasePassDrawListType DrawType) { return BasePassDistanceFieldShadowMapLightMapDrawList[DrawType]; } /** Maps the simple light-map texture case to the appropriate base pass draw list. */ template<> TStaticMeshDrawList > >& FScene::GetBasePassDrawList< TLightMapPolicy >(EBasePassDrawListType DrawType) { return BasePassLowQualityLightMapDrawList[DrawType]; } /** */ template<> TStaticMeshDrawList >& FScene::GetBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassSelfShadowedTranslucencyDrawList[DrawType]; } /** */ template<> TStaticMeshDrawList >& FScene::GetBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[DrawType]; } /** */ template<> TStaticMeshDrawList >& FScene::GetBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassCachedVolumeIndirectLightingDrawList[DrawType]; } /** */ template<> TStaticMeshDrawList >& FScene::GetBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassCachedPointIndirectLightingDrawList[DrawType]; } /** */ template<> TStaticMeshDrawList >& FScene::GetBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassSimpleDynamicLightingDrawList[DrawType]; } /** Maps the no light-map case to the appropriate base pass draw list. */ template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassForForwardShadingNoLightMapDrawList[DrawType]; } /** Maps the simple light-map texture case to the appropriate base pass draw list. */ template<> TStaticMeshDrawList< TBasePassForForwardShadingDrawingPolicy< TLightMapPolicy > >& FScene::GetForwardShadingBasePassDrawList< TLightMapPolicy >(EBasePassDrawListType DrawType) { return BasePassForForwardShadingLowQualityLightMapDrawList[DrawType]; } template<> TStaticMeshDrawList< TBasePassForForwardShadingDrawingPolicy< TDistanceFieldShadowsAndLightMapPolicy > >& FScene::GetForwardShadingBasePassDrawList< TDistanceFieldShadowsAndLightMapPolicy >(EBasePassDrawListType DrawType) { return BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[DrawType]; } template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[DrawType]; } template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[DrawType]; } template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[DrawType]; } template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassForForwardShadingMovableDirectionalLightDrawList[DrawType]; } template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassForForwardShadingMovableDirectionalLightCSMDrawList[DrawType]; } template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(EBasePassDrawListType DrawType) { return BasePassForForwardShadingMovableDirectionalLightLightmapDrawList[DrawType]; } template<> TStaticMeshDrawList >& FScene::GetForwardShadingBasePassDrawList(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::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::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