서론
Unreal Engine 5.1 릴리즈 버전에서 Planar reflection과 VSM 사용 시, 프레임이 많이 하락하는 현상을 보인다.
원인은 planar reflection 렌더러 / 메인 렌더러에서 불리는 D3D12RHI::CreateD3D12Texture 콜

VSMCachemanager::SetPhysicalPoolSize 는 한번 세팅되면 그만불려야하는 함수인데,
SceneCapture에서 쓰는 VSM cache, 메인 렌더러에서 쓰는 VSM cache가 공유되고있어서
'if (!PhysicalPagePool || PhysicalPagePool->GetDesc().Extent != RequestedSize || PhysicalPagePool->GetDesc().ArraySize != RequestedArraySize)' 조건에 의해 매번 PooledRenderTarget이 만들어지고, cache invalidate가 발생하고있다.
TRefCountPtr<IPooledRenderTarget> FVirtualShadowMapArrayCacheManager::SetPhysicalPoolSize(FRDGBuilder& GraphBuilder, FIntPoint RequestedSize, int RequestedArraySize)
{
if (!PhysicalPagePool || PhysicalPagePool->GetDesc().Extent != RequestedSize || PhysicalPagePool->GetDesc().ArraySize != RequestedArraySize)
{
FPooledRenderTargetDesc Desc2D = FPooledRenderTargetDesc::Create2DArrayDesc(
RequestedSize,
PF_R32_UINT,
FClearValueBinding::None,
TexCreate_None,
TexCreate_ShaderResource | TexCreate_UAV,
false,
RequestedArraySize
);
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc2D, PhysicalPagePool, TEXT("Shadow.Virtual.PhysicalPagePool"));
Invalidate();
//UE_LOG(LogRenderer, Display, TEXT("Recreating Shadow.Virtual.PhysicalPagePool. This will also drop any cached pages."));
}
return PhysicalPagePool;
}
해결 방법
- 각 렌더러가 각각의 VSMManager를 가진다.
-> Unreal 5.2 에서 해결됨
5.2 릴리즈 노트 및 수정 커밋 (seperate VSM cache 사용)

https://github.com/EpicGames/UnrealEngine/commit/a4262b8a07e9e063c12586a6721057e0a5d6addd
해당 커밋은 UE5.1에도 이미 적용되어있었음
코드 분석
Engine/Source/Runtime/Renderer/Private/SceneCaptureRendering.cpp
FScene::UpdateSceneCaptureContents
if(UseVirtualShadowMaps(SceneRenderer->ShaderPlatform, FeatureLevel))
{
// A separate VSM cache is added to the SceneCapture views.
// This is needed to prevent the cache being invalidated on every frame.
// In a future iteration, it may be more efficient to share this cache with the main viewfamily.
if (ViewStateInterface)
{
ViewStateInterface->AddVirtualShadowMapCache(this);
}
else
{
GEngine->AddOnScreenDebugMessage((uint64)this, 10.0f, FColor::Yellow, TEXT("SceneCapture has no viewstate. This may affect Virtual Shadow Map caching performance. Consider enabling bAlwaysPersistRenderingState or bCaptureEveryFrame."));
}
}
else if (ViewStateInterface)
{
ViewStateInterface->RemoveVirtualShadowMapCache(this);
}
해당코드는 씬캡쳐 FSceneViewState에 VSMCacheManager를 독립적으로 추가하는 코드이다.
PlanarReflectionRendering.cpp 의 FScene::UpdatePlanarReflectionContents
반면 Planar Reflection에서는 독립적인 VSMCacheManager를 추가하는 코드가 없다. 따라서 같은 VSMCacheManager 및 PhysicalPagePool (렌더타겟포함)을 사용하는 것으로 확인되었다.
main renderer 의 PhysicalPagePool pointer
PhysicalPagePool {Reference=0x0000023cbf4d6a00 {RenderTargetPool=0x00007ff6c815c280 {MV.exe!TGlobalResource<FRenderTargetPool> GRenderTargetPool} {...} ...} } TRefCountPtr<IPooledRenderTarget>
planar reflection renderer의 PhysicalPagePool pointer
PhysicalPagePool {Reference=0x0000023cbf4d6a00 {RenderTargetPool=0x00007ff6c815c280 {MV.exe!TGlobalResource<FRenderTargetPool> GRenderTargetPool} {...} ...} } TRefCountPtr<IPooledRenderTarget>
(같음)
따라서 FScene::UpdatePlanarReflectionContents 함수에 View고유의 VSMCacheManager를 추가하는 코드를 추가하였음.
FScene::UpdatePlanarReflectionContents (..)
{
...
FSceneViewStateInterface* ViewStateInterface = CaptureComponent->GetViewState(0);
if (UseVirtualShadowMaps(SceneRenderer->ShaderPlatform, FeatureLevel))
{
// A separate VSM cache is added to the SceneCapture views.
// This is needed to prevent the cache being invalidated on every frame.
// In a future iteration, it may be more efficient to share this cache with the main viewfamily.
if (ViewStateInterface)
{
ViewStateInterface->AddVirtualShadowMapCache(this);
}
else
{
GEngine->AddOnScreenDebugMessage((uint64)this, 10.0f, FColor::Yellow, TEXT("SceneCapture has no viewstate. This may affect Virtual Shadow Map caching performance. Consider enabling bAlwaysPersistRenderingState or bCaptureEveryFrame."));
}
}
else if (ViewStateInterface)
{
ViewStateInterface->RemoveVirtualShadowMapCache(this);
}
...
}
변경 후
불필요한 D3D12RHI::CreateD3D12Texture 콜이 사라지고, FPS 46 -> FPS 110 으로 평균프레임 향상
'언리얼 엔진' 카테고리의 다른 글
| [Unreal Engine 5] Virtual Shadow Map (1) | 2025.07.29 |
|---|---|
| [Unreal Engine 5] Nanite Basepass 분석 (5) | 2025.07.25 |
| Expert's guide to unreal engine performance (0) | 2023.09.01 |
| [언리얼 엔진] Referencing Assets 쿠킹 테스트 (0) | 2023.03.24 |
| [언리얼 엔진] 블루프린트 String Path로 로드 시 주의할 점 (2) | 2023.03.20 |