Files
UnrealEngine/Engine/Source/Runtime/HeadMountedDisplay/Private/LateUpdateManager.cpp
2025-05-18 13:04:45 +08:00

115 lines
4.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LateUpdateManager.h"
#include "PrimitiveSceneProxy.h"
#include "Components/PrimitiveComponent.h"
#include "PrimitiveSceneInfo.h"
#include "HeadMountedDisplayTypes.h"
#include "SceneInterface.h"
static TAutoConsoleVariable<bool> CVarXRLateUpdateMangerDisable(
TEXT("xr.LateUpdateManager.Disable"),
false,
TEXT("Disable the LateUpdateManager preventing child components from receiving late updates.\n"),
ECVF_Default);
void FLateUpdateManager::Setup(const FTransform& ParentToWorld, USceneComponent* Component, bool bSkipLateUpdate)
{
check(IsInGameThread());
bSkipLateUpdate = bSkipLateUpdate || CVarXRLateUpdateMangerDisable.GetValueOnGameThread();
PipelinedUpdateStatesGame.Primitives.Reset();
PipelinedUpdateStatesGame.ParentToWorld = ParentToWorld;
GatherLateUpdatePrimitives(Component);
PipelinedUpdateStatesGame.bSkip = bSkipLateUpdate;
ENQUEUE_RENDER_COMMAND(UpdateLateUpdateStatesRendering)(
[UpdateStatesGame = PipelinedUpdateStatesGame, this](FRHICommandListImmediate& RHICmdList) mutable
{
PipelinedUpdateStatesRendering = MoveTemp(UpdateStatesGame);
});
}
void FLateUpdateManager::Apply_RenderThread(FSceneInterface* Scene, const FTransform& OldRelativeTransform, const FTransform& NewRelativeTransform)
{
FRHICommandListBase& RHICmdList = FRHICommandListImmediate::Get();
if (!PipelinedUpdateStatesRendering.Primitives.Num() || PipelinedUpdateStatesRendering.bSkip)
{
return;
}
const FTransform OldCameraTransform = OldRelativeTransform * PipelinedUpdateStatesRendering.ParentToWorld;
const FTransform NewCameraTransform = NewRelativeTransform * PipelinedUpdateStatesRendering.ParentToWorld;
const FMatrix LateUpdateTransform = (OldCameraTransform.Inverse() * NewCameraTransform).ToMatrixWithScale();
bool bIndicesHaveChanged = false;
// Apply delta to the cached scene proxies
// Also check whether any primitive indices have changed, in case the scene has been modified in the meantime.
for (auto& PrimitivePair : PipelinedUpdateStatesRendering.Primitives)
{
FPrimitiveSceneInfo* RetrievedSceneInfo = Scene->GetPrimitiveSceneInfo(PrimitivePair.Value);
FPrimitiveSceneInfo* CachedSceneInfo = PrimitivePair.Key;
// If the retrieved scene info is different than our cached scene info then the scene has changed in the meantime
// and we need to search through the entire scene to make sure it still exists.
if (CachedSceneInfo != RetrievedSceneInfo)
{
bIndicesHaveChanged = true;
break; // No need to continue here, as we are going to brute force the scene primitives below anyway.
}
else if (CachedSceneInfo->Proxy)
{
CachedSceneInfo->Proxy->ApplyLateUpdateTransform(RHICmdList, LateUpdateTransform);
PrimitivePair.Value = -1; // Set the cached index to -1 to indicate that this primitive was already processed
}
}
// Indices have changed, so we need to scan the entire scene for primitives that might still exist
if (bIndicesHaveChanged)
{
int32 Index = 0;
FPrimitiveSceneInfo* RetrievedSceneInfo;
RetrievedSceneInfo = Scene->GetPrimitiveSceneInfo(Index++);
while(RetrievedSceneInfo)
{
if (RetrievedSceneInfo->Proxy && PipelinedUpdateStatesRendering.Primitives.Contains(RetrievedSceneInfo) && PipelinedUpdateStatesRendering.Primitives[RetrievedSceneInfo] >= 0)
{
RetrievedSceneInfo->Proxy->ApplyLateUpdateTransform(RHICmdList, LateUpdateTransform);
}
RetrievedSceneInfo = Scene->GetPrimitiveSceneInfo(Index++);
}
}
}
void FLateUpdateManager::CacheSceneInfo(USceneComponent* Component)
{
ensureMsgf(!Component->IsUsingAbsoluteLocation() && !Component->IsUsingAbsoluteRotation(), TEXT("SceneComponents that use absolute location or rotation are not supported by the LateUpdateManager"));
// If a scene proxy is present, cache it
UPrimitiveComponent* PrimitiveComponent = dynamic_cast<UPrimitiveComponent*>(Component);
if (PrimitiveComponent && PrimitiveComponent->SceneProxy)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveComponent->SceneProxy->GetPrimitiveSceneInfo();
if (PrimitiveSceneInfo && PrimitiveSceneInfo->IsIndexValid())
{
PipelinedUpdateStatesGame.Primitives.Emplace(PrimitiveSceneInfo, PrimitiveSceneInfo->GetIndex());
}
}
}
void FLateUpdateManager::GatherLateUpdatePrimitives(USceneComponent* ParentComponent)
{
CacheSceneInfo(ParentComponent);
TArray<USceneComponent*> Components;
ParentComponent->GetChildrenComponents(true, Components);
for(USceneComponent* Component : Components)
{
if (Component != nullptr)
{
CacheSceneInfo(Component);
}
}
}