Files
UnrealEngine/Engine/Shaders/Private/VirtualShadowMaps/VirtualShadowMapPageCacheCommon.ush
2025-05-18 13:04:45 +08:00

107 lines
4.4 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VirtualShadowMapPageCacheCommon.ush:
=============================================================================*/
#pragma once
#include "../Nanite/NaniteDataDecode.ush"
#include "VirtualShadowMapProjectionStructs.ush"
bool ShouldMaterialInvalidateShadowCache(FPrimitiveSceneData PrimitiveData, bool bEnableWPO)
{
// Determine if this instance will cause page invalidations.
bool bInvalidate = bEnableWPO;
// Ignore WPO disable distance for this if any of the materials have bAlwaysEvaluateWorldPositionOffset = true.
bInvalidate |= ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_HAS_ALWAYS_EVALUATE_WPO_MATERIALS) != 0u);
// Unless, of course, the primitive opts to disable shadow cache invalidations.
bInvalidate &= ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_DISABLE_MATERIAL_INVALIDATIONS) == 0u);
return bInvalidate;
}
// NOTE: This logic must be constant, or else we could miss invalidations if it swaps suddenly.
// Any changes to the underlying data (i.e. evaluate WPO flag) need to generate a GPUScene update and
// associated VSM instance invalidation.
bool VirtualShadowMapIsWPOAllowed(FPrimitiveSceneData PrimitiveData, FVirtualShadowMapHandle VirtualShadowMapHandle)
{
// If always evaluate WPO is enabled, that takes priority
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_HAS_ALWAYS_EVALUATE_WPO_MATERIALS) != 0u)
{
return true;
}
bool bEvaluateWPO = (PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_EVALUATE_WORLD_POSITION_OFFSET) != 0u;
bool bWPODisableDistance = (PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_WPO_DISABLE_DISTANCE) != 0u;
if (bEvaluateWPO && bWPODisableDistance && VirtualShadowMapHandle.IsValid())
{
FVirtualShadowMapProjectionShaderData ProjectionData = GetVirtualShadowMapProjectionData(VirtualShadowMapHandle);
// NOTE: ProjectionData set to a safe value for this test even if this is not a clipmap
return ProjectionData.ClipmapLevelWPODistanceDisabledThresholdSquared <= PrimitiveData.InstanceWPODisableDistanceSquared;
}
return bEvaluateWPO;
}
bool VirtualShadowMapIsWPOAllowed(FPrimitiveSceneData PrimitiveData, int VirtualShadowMapId)
{
return VirtualShadowMapIsWPOAllowed(PrimitiveData, FVirtualShadowMapHandle::MakeFromId(VirtualShadowMapId));
}
bool GetCachePrimitiveAsDynamic(uint PersistentPrimitiveIndex)
{
// Dynamic primitives are not tracked, so we treat them as dynamic since they can't be cached by definition.
if (PersistentPrimitiveIndex >= GetSceneData().MaxPersistentPrimitiveIndex)
{
return true;
}
uint PrimitiveIdWordOffset = PersistentPrimitiveIndex / 32U;
uint PrimitiveIdWordMask = 1U << (PersistentPrimitiveIndex % 32U);
return (VirtualShadowMap.CachePrimitiveAsDynamic[PrimitiveIdWordOffset] & PrimitiveIdWordMask) != 0;
}
bool GetCacheInstanceAsDynamic(uint InstanceId, int SceneRendererPrimaryViewId)
{
uint WordOffset = InstanceId / 32U;
uint WordMask = 1U << (InstanceId % 32U);
uint ViewWordOffset = Scene.VSMCache.InstanceStateViewWordStride * SceneRendererPrimaryViewId * 2;
return (Scene.VSMCache.CacheInstanceAsDynamic[ViewWordOffset + WordOffset] & WordMask) != 0;
}
/**
* Returns true if, for the currect view & frame, the instance transitioned from being static to dynamic or vice versa.
*/
bool GetCacheTransitioned(uint InstanceId, int SceneRendererPrimaryViewId)
{
uint WordOffset = InstanceId / 32U;
uint WordMask = 1U << (InstanceId % 32U);
// Note: transitioned bits are stored after the other ones.
uint ViewWordOffset = Scene.VSMCache.InstanceStateViewWordStride * SceneRendererPrimaryViewId * 2 + Scene.VSMCache.InstanceStateViewWordStride;
return (Scene.VSMCache.CacheInstanceAsDynamic[ViewWordOffset + WordOffset] & WordMask) != 0;
}
bool ShouldCacheInstanceAsStatic(uint InstanceId, bool bViewUncached, bool bAllowWPO, int SceneRendererPrimaryViewId)
{
if (bViewUncached || bAllowWPO)
{
return false;
}
FInstanceSceneData InstanceData = GetInstanceSceneDataUnchecked(InstanceId);
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
// TODO: Move this also to the per-instance bit vector?
// Whole primitive decision takes precentence
if (GetCachePrimitiveAsDynamic(PrimitiveData.PersistentPrimitiveIndex))
{
return false;
}
if (GetCacheInstanceAsDynamic(InstanceId, SceneRendererPrimaryViewId))
{
return false;
}
return true;
}