Files
UnrealEngineUWP/Engine/Shaders/Private/VirtualShadowMaps/VirtualShadowMapCacheManagement.usf
Ola Olsson abc495b9ec Change VSM cache manager over to use SceneUB.
- also clean up dangling RDG resource references in GPUScene.

#rb wouter.dek
#preflight 64648598c34c2e1212651169

[CL 25503619 by Ola Olsson in ue5-main branch]
2023-05-17 03:58:05 -04:00

228 lines
8.4 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "../SceneData.ush"
#include "VirtualShadowMapProjectionCommon.ush"
#include "VirtualShadowMapPageOverlap.ush"
#include "VirtualShadowMapPageCacheCommon.ush"
#include "../ScreenSpaceDenoise/SSDDefinitions.ush" // For LIGHT_TYPE's
//#include "NaniteDataDecode.ush"
#include "../Nanite/NaniteHZBCull.ush"
#if ENABLE_DEBUG_MODE
#include "../ShaderPrint.ush"
#include "../ColorMap.ush"
#endif
#include "../InstanceCulling/InstanceCullingLoadBalancer.ush"
RWStructuredBuffer<FPhysicalPageMetaData> PhysicalPageMetaDataOut;
#if ENABLE_DEBUG_MODE
uint bDrawBounds;
#endif
struct FInstanceInvalidationPayload
{
int VirtualShadowMapId;
bool bInvalidateStaticPage;
};
FInstanceInvalidationPayload DecodeInstanceInvalidationPayload(uint Payload)
{
FInstanceInvalidationPayload Result;
Result.bInvalidateStaticPage = (Payload & 0x2) != 0;
if (Payload & 0x1)
{
// Single shadow map
Result.VirtualShadowMapId = Payload >> 2;
}
else
{
// All lights
Result.VirtualShadowMapId = INDEX_NONE;
}
return Result;
}
/**
* Each thread loops over a range on instances loaded from a buffer. The instance bounds are projected to all cached virtual shadow map address space
* and any overlapped pages are marked as invalid.
*/
[numthreads(CS_1D_GROUP_SIZE_X, 1, 1)]
void VirtualSmInvalidateInstancePagesCS(
uint DispatchIndex : SV_DispatchThreadID,
uint3 GroupId : SV_GroupID,
uint GroupThreadIndex : SV_GroupIndex)
{
// Loop only over the non-single-page VSMs
uint FirstVirtualShadowMapId = VSM_MAX_SINGLE_PAGE_SHADOW_MAPS;
uint EndVirtualShadowMapId = VSM_MAX_SINGLE_PAGE_SHADOW_MAPS + VirtualShadowMap.NumFullShadowMaps;
bool bForceInvalidateStaticPage = false;
FInstanceWorkSetup WorkSetup = InstanceCullingLoadBalancer_Setup(GroupId, GroupThreadIndex, 0U);
if (!WorkSetup.bValid)
{
return;
}
FInstanceInvalidationPayload Payload = DecodeInstanceInvalidationPayload(WorkSetup.Item.Payload);
bForceInvalidateStaticPage = Payload.bInvalidateStaticPage;
if (Payload.VirtualShadowMapId != INDEX_NONE)
{
// Do a single shadowmap
FirstVirtualShadowMapId = Payload.VirtualShadowMapId;
EndVirtualShadowMapId = FirstVirtualShadowMapId + 1U;
}
uint InstanceId = WorkSetup.Item.InstanceDataOffset + uint(WorkSetup.LocalItemIndex);
{
checkSlow(InstanceId >= 0 && InstanceId < Scene.GPUScene.InstanceDataSOAStride);
checkSlow(InstanceId >= 0 && InstanceId < Scene.GPUScene.NumInstances);
FInstanceSceneData InstanceSceneData = GetInstanceSceneData(InstanceId, Scene.GPUScene.InstanceDataSOAStride);
if (!InstanceSceneData.ValidInstance)
{
return;
}
const bool bIsNanite = InstanceSceneData.NaniteRuntimeResourceID != 0xFFFFFFFFu;
bool bCastShadows = InstanceSceneData.ValidInstance
&& (GetPrimitiveData(InstanceSceneData.PrimitiveId).Flags & PRIMITIVE_SCENE_DATA_FLAG_CAST_SHADOWS) != 0U;
#if ENABLE_DEBUG_MODE
uint PageInvalidationCount = 0U;
#endif
// TODO: test the flag on the instance instead once it is updated correctly InstanceSceneData.CastShadows
if (bCastShadows)
{
for (uint VirtualShadowMapId = FirstVirtualShadowMapId; VirtualShadowMapId < EndVirtualShadowMapId; ++VirtualShadowMapId)
{
// 1. Load cached projection data
FVirtualShadowMapProjectionShaderData ProjectionData = GetVirtualShadowMapProjectionData(VirtualShadowMapId);
const bool bDirectionalLight = (ProjectionData.LightType == LIGHT_TYPE_DIRECTIONAL);
// Stop invalidations if the light is 'distant'
if (ProjectionData.bCurrentDistantLight)
{
continue;
}
// NOTE: This is the *shadow view*'s translated world, not primary view
float4x4 LocalToTranslatedWorld = LWCMultiplyTranslation(InstanceSceneData.LocalToWorld, ProjectionData.PreViewTranslation);
// distance cull invalidations for local lights
if (!bDirectionalLight)
{
float InstanceRadius = length(InstanceSceneData.LocalBoundsExtent * InstanceSceneData.NonUniformScale.xyz);
float3 TranslatedWorldCenter = mul(float4(InstanceSceneData.LocalBoundsCenter, 1.0f), LocalToTranslatedWorld).xyz;
if (length2(TranslatedWorldCenter) > Square(ProjectionData.LightRadius + InstanceRadius))
{
continue;
}
}
// Go back to clip space
float4x4 UVToClip;
UVToClip[0] = float4(2, 0, 0, 0);
UVToClip[1] = float4(0, -2, 0, 0);
UVToClip[2] = float4(0, 0, 1, 0);
UVToClip[3] = float4(-1, 1, 0, 1);
const bool bIsOrtho = bDirectionalLight;
const bool bNearClip = !bDirectionalLight;
FFrustumCullData Cull = BoxCullFrustum(
InstanceSceneData.LocalBoundsCenter,
InstanceSceneData.LocalBoundsExtent,
LocalToTranslatedWorld,
mul(ProjectionData.TranslatedWorldToShadowUVMatrix, UVToClip),
ProjectionData.ShadowViewToClipMatrix, bIsOrtho, bNearClip, false);
float PixelEstRadius = CalcClipSpaceRadiusEstimate(bIsOrtho, InstanceSceneData, LocalToTranslatedWorld, ProjectionData.ShadowViewToClipMatrix) * float(VSM_VIRTUAL_MAX_RESOLUTION_XY);
if (Cull.bIsVisible)
{
bool bShouldCacheAsStatic = ShouldCacheInstanceAsStatic(InstanceSceneData, ProjectionData.bUnCached);
bool bInvalidateStaticPage = bForceInvalidateStaticPage || bShouldCacheAsStatic;
uint InvalidationFlags = (bInvalidateStaticPage ? VSM_STATIC_UNCACHED_FLAG : VSM_DYNAMIC_UNCACHED_FLAG) << VSM_PHYSICAL_PAGE_INVALIDATION_FLAGS_SHIFT;
// 2. figure out overlap and all that
// case #1 mip-map VSM - loop all mip levels, case #2 clipmap, just one 'mip level'
int NumMipLevels = (ProjectionData.ClipmapLevelCountRemaining <= 0) ? VSM_MAX_MIP_LEVELS : 1;
{
for (int MipLevel = 0; MipLevel < NumMipLevels; ++MipLevel)
{
int ViewDim = int(uint(VSM_VIRTUAL_MAX_RESOLUTION_XY) >> MipLevel);
FScreenRect Rect = GetScreenRect(int4(0, 0, ViewDim, ViewDim), Cull, 4);
// Add a small epsilon to the HZB depth test
// This is to handle the rare case where an object that is fully parallel to the
// light's near plane might self-occlude the HZB test due to minor precision differences
// in the computation. While rare, this can come up with things like point lights and
// axis aligned boxes.
Rect.Depth += 1e-8f;
uint4 RectPages = GetPageRect(Rect, ProjectionData.VirtualShadowMapId, MipLevel);
uint FlagMask = VSM_ALLOCATED_FLAG | GetDetailGeometryFlagMaskForCulling(bShouldCacheAsStatic, bIsNanite, PixelEstRadius);
PixelEstRadius *= 0.5f;
// Use Hierarchical mip test to speed up (allows skipping invalidating areas that don't have any flags anyway)
if (OverlapsAnyValidPage(ProjectionData.VirtualShadowMapId, MipLevel, RectPages, FlagMask))
{
#if USE_HZB_OCCLUSION
FPageTestScreenRect HZBTestRect = SetupPageHZBRect(Rect, ProjectionData.VirtualShadowMapId, MipLevel);
#endif // USE_HZB_OCCLUSION
// 3. do invalidation
FVirtualSMLevelOffset PageTableLevelOffset = CalcPageTableLevelOffset(ProjectionData.VirtualShadowMapId, MipLevel);
for (uint y = RectPages.y; y <= RectPages.w; y++)
{
for (uint x = RectPages.x; x <= RectPages.z; x++)
{
uint PageFlagOffset = CalcPageOffset(PageTableLevelOffset, MipLevel, uint2(x, y));
uint PageFlag = VirtualShadowMap.PageFlags[PageFlagOffset];
if ((PageFlag & FlagMask) == FlagMask)
{
#if USE_HZB_OCCLUSION
if (!IsPageVisibleHZB(uint2(x, y), PageFlagOffset, HZBTestRect))
{
continue;
}
#endif // USE_HZB_OCCLUSION
// Accumulate static/dynamic invalidation flags
// TODO: Wave version
uint2 PhysicalAddress = ShadowGetPhysicalPage(PageFlagOffset).PhysicalAddress;
uint PhysicalPageIndex = VSMPhysicalPageAddressToIndex(PhysicalAddress);
InterlockedOr(PhysicalPageMetaDataOut[PhysicalPageIndex].Flags, InvalidationFlags);
#if ENABLE_DEBUG_MODE
++PageInvalidationCount;
#endif
}
}
}
}
}
}
}
}
}
#if ENABLE_DEBUG_MODE
if (bDrawBounds && PageInvalidationCount > 0U)
{
float3 Color = float3(0.3f, 0.3f, 0.3f) + ColorMapTurbo(min(1.0f, float(PageInvalidationCount) / 100.0f)) * 0.7f;
AddOBBWS(InstanceSceneData.LocalBoundsCenter - InstanceSceneData.LocalBoundsExtent, InstanceSceneData.LocalBoundsCenter + InstanceSceneData.LocalBoundsExtent, float4(Color, 1.0f), LWCHackToFloat(InstanceSceneData.LocalToWorld));
}
#endif
}
}