2020-07-07 14:29:30 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2020-07-06 18:58:26 -04:00
/*=============================================================================
VirtualShadowMap . h :
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "VirtualShadowMapCacheManager.h"
2022-01-18 08:51:35 -05:00
# include "VirtualShadowMapClipmap.h"
2020-07-06 18:58:26 -04:00
# include "RendererModule.h"
# include "RenderGraphUtils.h"
# include "RHIGPUReadback.h"
2020-08-25 10:06:54 -04:00
# include "ScenePrivate.h"
2020-07-06 18:58:26 -04:00
# include "HAL/FileManager.h"
2020-08-25 10:06:54 -04:00
# include "PrimitiveSceneInfo.h"
2022-02-22 15:35:01 -05:00
# include "ShaderPrint.h"
2021-12-03 17:53:58 -05:00
# include "RendererOnScreenNotification.h"
2020-07-06 18:58:26 -04:00
static TAutoConsoleVariable < int32 > CVarAccumulateStats (
2021-02-18 13:44:36 -04:00
TEXT ( " r.Shadow.Virtual.AccumulateStats " ) ,
2020-07-06 18:58:26 -04:00
0 ,
TEXT ( " AccumulateStats " ) ,
ECVF_RenderThreadSafe
) ;
static TAutoConsoleVariable < int32 > CVarCacheVirtualSMs (
2021-02-18 13:44:36 -04:00
TEXT ( " r.Shadow.Virtual.Cache " ) ,
2021-03-15 22:06:28 -04:00
1 ,
2020-07-06 18:58:26 -04:00
TEXT ( " Turn on to enable caching " ) ,
ECVF_RenderThreadSafe
) ;
2021-08-24 06:42:43 -04:00
static TAutoConsoleVariable < int32 > CVarDrawInvalidatingBounds (
2022-02-22 13:52:40 -05:00
TEXT ( " r.Shadow.Virtual.Cache.DrawInvalidatingBounds " ) ,
2021-08-24 06:42:43 -04:00
0 ,
2021-12-03 16:08:27 -05:00
TEXT ( " Turn on debug render cache invalidating instance bounds, heat mapped by number of pages invalidated. \n " )
TEXT ( " 1 = Draw all bounds. \n " )
TEXT ( " 2 = Draw those invalidating static cached pages only \n " )
TEXT ( " 3 = Draw those invalidating dynamic cached pages only " ) ,
2021-08-24 06:42:43 -04:00
ECVF_RenderThreadSafe
) ;
2020-07-06 18:58:26 -04:00
2021-09-02 10:25:16 -04:00
static TAutoConsoleVariable < int32 > CVarCacheVsmUseHzb (
TEXT ( " r.Shadow.Virtual.Cache.InvalidateUseHZB " ) ,
1 ,
TEXT ( " Enables testing HZB for Virtual Shadow Map invalidations. " ) ,
ECVF_RenderThreadSafe ) ;
2022-02-02 02:18:54 -05:00
int32 GClipmapPanning = 1 ;
FAutoConsoleVariableRef CVarEnableClipmapPanning (
TEXT ( " r.Shadow.Virtual.Cache.ClipmapPanning " ) ,
GClipmapPanning ,
TEXT ( " Enable support for panning cached clipmap pages for directional lights. " ) ,
ECVF_RenderThreadSafe
) ;
2022-02-09 15:11:16 -05:00
static int32 GVSMCacheDeformableMeshesInvalidate = 1 ;
FAutoConsoleVariableRef CVarCacheInvalidateOftenMoving (
TEXT ( " r.Shadow.Virtual.Cache.DeformableMeshesInvalidate " ) ,
GVSMCacheDeformableMeshesInvalidate ,
TEXT ( " If enabled, Primitive Proxies that are marked as having deformable meshes (HasDeformableMesh() == true) causes invalidations regardless of whether their transforms are updated. " ) ,
ECVF_RenderThreadSafe ) ;
2020-12-16 17:57:13 -04:00
void FVirtualShadowMapCacheEntry : : UpdateClipmap (
int32 VirtualShadowMapId ,
const FMatrix & WorldToLight ,
FIntPoint PageSpaceLocation ,
2022-02-11 14:57:27 -05:00
double LevelRadius ,
double ViewCenterZ ,
2021-08-07 15:19:34 -04:00
// NOTE: ViewRadiusZ must be constant for a given clipmap level
2022-02-11 14:57:27 -05:00
double ViewRadiusZ )
2020-07-06 18:58:26 -04:00
{
2021-05-27 17:36:27 -04:00
bool bCacheValid = ( CurrentVirtualShadowMapId ! = INDEX_NONE ) ;
2020-07-06 18:58:26 -04:00
2021-05-27 17:36:27 -04:00
if ( bCacheValid & & WorldToLight ! = Clipmap . WorldToLight )
2020-07-06 18:58:26 -04:00
{
2021-05-27 17:36:27 -04:00
bCacheValid = false ;
//UE_LOG(LogRenderer, Display, TEXT("Invalidated clipmap level (VSM %d) due to light movement"), VirtualShadowMapId);
2020-07-06 18:58:26 -04:00
}
2022-02-02 02:18:54 -05:00
if ( bCacheValid & & GClipmapPanning = = 0 )
2021-05-27 17:36:27 -04:00
{
2021-08-07 15:19:34 -04:00
if ( PageSpaceLocation . X ! = PrevPageSpaceLocation . X | |
PageSpaceLocation . Y ! = PrevPageSpaceLocation . Y )
2021-05-27 17:36:27 -04:00
{
2021-08-07 15:19:34 -04:00
bCacheValid = false ;
2021-05-27 17:36:27 -04:00
//UE_LOG(LogRenderer, Display, TEXT("Invalidated clipmap level (VSM %d) with page space location %d,%d (Prev %d, %d)"),
// VirtualShadowMapId, PageSpaceLocation.X, PageSpaceLocation.Y, PrevPageSpaceLocation.X, PrevPageSpaceLocation.Y);
}
}
// Invalidate if the new Z radius strayed too close/outside the guardband of the cached shadow map
if ( bCacheValid )
{
2022-02-11 14:57:27 -05:00
double DeltaZ = FMath : : Abs ( ViewCenterZ - Clipmap . ViewCenterZ ) ;
if ( ( DeltaZ + LevelRadius ) > 0.9 * Clipmap . ViewRadiusZ )
2021-05-27 17:36:27 -04:00
{
bCacheValid = false ;
//UE_LOG(LogRenderer, Display, TEXT("Invalidated clipmap level (VSM %d) due to depth range movement"), VirtualShadowMapId);
}
}
2021-12-07 16:24:58 -05:00
bool bRadiusMatches = ( ViewRadiusZ = = Clipmap . ViewRadiusZ ) ;
if ( bCacheValid & & bRadiusMatches )
2021-05-27 17:36:27 -04:00
{
PrevVirtualShadowMapId = CurrentVirtualShadowMapId ;
}
else
{
2021-12-15 12:03:31 -05:00
if ( bCacheValid & & ! bRadiusMatches )
{
// These really should be exact by construction currently
UE_LOG ( LogRenderer , Warning , TEXT ( " Invalidated clipmap level (VSM %d) due to Z radius mismatch " ) , VirtualShadowMapId ) ;
}
2021-05-27 17:36:27 -04:00
// New cached level
PrevVirtualShadowMapId = INDEX_NONE ;
Clipmap . WorldToLight = WorldToLight ;
Clipmap . ViewCenterZ = ViewCenterZ ;
Clipmap . ViewRadiusZ = ViewRadiusZ ;
}
PrevPageSpaceLocation = CurrentPageSpaceLocation ;
2021-07-28 17:00:37 -04:00
bPrevRendered = bCurrentRendered ;
2021-05-27 17:36:27 -04:00
2020-07-06 18:58:26 -04:00
CurrentVirtualShadowMapId = VirtualShadowMapId ;
CurrentPageSpaceLocation = PageSpaceLocation ;
2021-07-28 17:00:37 -04:00
bCurrentRendered = false ;
2020-07-06 18:58:26 -04:00
}
2021-05-27 17:36:27 -04:00
void FVirtualShadowMapCacheEntry : : UpdateLocal ( int32 VirtualShadowMapId , const FWholeSceneProjectedShadowInitializer & InCacheValidKey )
2020-07-06 18:58:26 -04:00
{
// Swap previous frame data over.
2021-07-28 17:00:37 -04:00
PrevPageSpaceLocation = FIntPoint ( 0 , 0 ) ; // Not used for local lights
2020-07-06 18:58:26 -04:00
PrevVirtualShadowMapId = CurrentVirtualShadowMapId ;
2021-07-28 17:00:37 -04:00
bPrevRendered = bCurrentRendered ;
2020-07-06 18:58:26 -04:00
// Check cache validity based of shadow setup
2021-05-27 17:36:27 -04:00
if ( ! LocalCacheValidKey . IsCachedShadowValid ( InCacheValidKey ) )
2020-07-06 18:58:26 -04:00
{
2020-12-16 17:57:13 -04:00
PrevVirtualShadowMapId = INDEX_NONE ;
//UE_LOG(LogRenderer, Display, TEXT("Invalidated!"));
2020-07-06 18:58:26 -04:00
}
2021-05-27 17:36:27 -04:00
LocalCacheValidKey = InCacheValidKey ;
2020-07-06 18:58:26 -04:00
CurrentVirtualShadowMapId = VirtualShadowMapId ;
2021-07-28 17:00:37 -04:00
CurrentPageSpaceLocation = FIntPoint ( 0 , 0 ) ; // Not used for local lights
bCurrentRendered = false ;
2020-07-06 18:58:26 -04:00
}
2022-04-14 19:28:00 -04:00
static inline uint32 EncodeInstanceInvalidationPayload ( bool bInvalidateStaticPage , int32 ClipmapVirtualShadowMapId = INDEX_NONE )
{
uint32 Payload = 0 ;
if ( bInvalidateStaticPage )
{
Payload = Payload | 0x2 ;
}
if ( ClipmapVirtualShadowMapId ! = INDEX_NONE )
{
// Do a single clipmap level
Payload = Payload | 0x1 ;
Payload = Payload | ( ( ( uint32 ) ClipmapVirtualShadowMapId ) < < 2 ) ;
}
return Payload ;
}
2022-05-05 14:02:57 -04:00
FVirtualShadowMapArrayCacheManager : : FInvalidatingPrimitiveCollector : : FInvalidatingPrimitiveCollector ( FVirtualShadowMapArrayCacheManager * InVirtualShadowMapArrayCacheManager )
: AlreadyAddedPrimitives ( false , InVirtualShadowMapArrayCacheManager - > Scene - > Primitives . Num ( ) )
, Scene ( * InVirtualShadowMapArrayCacheManager - > Scene )
, GPUScene ( InVirtualShadowMapArrayCacheManager - > Scene - > GPUScene )
, Manager ( * InVirtualShadowMapArrayCacheManager )
{
bool bPossiblyCachedAsStatic = false ; // TODO
// Add and clear pending invalidations enqueued on the GPU Scene from dynamic primitives added since last invalidation
for ( const FGPUScene : : FInstanceRange & Range : GPUScene . DynamicPrimitiveInstancesToInvalidate )
{
LoadBalancer . Add ( Range . InstanceSceneDataOffset , Range . NumInstanceSceneDataEntries , EncodeInstanceInvalidationPayload ( bPossiblyCachedAsStatic ) ) ;
# if VSM_LOG_INVALIDATIONS
RangesStr . Appendf ( TEXT ( " [%6d, %6d), " ) , Range . InstanceSceneDataOffset , Range . InstanceSceneDataOffset + Range . NumInstanceSceneDataEntries ) ;
# endif
TotalInstanceCount + = Range . NumInstanceSceneDataEntries ;
}
GPUScene . DynamicPrimitiveInstancesToInvalidate . Reset ( ) ;
for ( auto & CacheEntry : Manager . PrevCacheEntries )
{
for ( const FVirtualShadowMapPerLightCacheEntry : : FInstanceRange & Range : CacheEntry . Value - > PrimitiveInstancesToInvalidate )
{
// Add item for each shadow map explicitly, inflates host data but improves load balancing,
// TODO: maybe add permutation so we can strip the loop completely.
for ( const auto & SmCacheEntry : CacheEntry . Value - > ShadowMapEntries )
{
if ( SmCacheEntry . IsValid ( ) )
{
// Lowest bit indicates whether to run the clipmap loop, add 1 to ID so != 0 <==> single level processing
LoadBalancer . Add ( Range . InstanceSceneDataOffset , Range . NumInstanceSceneDataEntries ,
EncodeInstanceInvalidationPayload ( bPossiblyCachedAsStatic , SmCacheEntry - > CurrentVirtualShadowMapId ) ) ;
}
}
# if VSM_LOG_INVALIDATIONS
RangesStr . Appendf ( TEXT ( " [%6d, %6d), " ) , Range . InstanceSceneDataOffset , Range . InstanceSceneDataOffset + Range . NumInstanceSceneDataEntries ) ;
# endif
TotalInstanceCount + = Range . NumInstanceSceneDataEntries ;
}
CacheEntry . Value - > PrimitiveInstancesToInvalidate . Reset ( ) ;
}
}
2022-01-18 08:51:35 -05:00
void FVirtualShadowMapArrayCacheManager : : FInvalidatingPrimitiveCollector : : Add ( const FPrimitiveSceneInfo * PrimitiveSceneInfo )
{
int32 PrimitiveID = PrimitiveSceneInfo - > GetIndex ( ) ;
if ( PrimitiveID > = 0
& & ! AlreadyAddedPrimitives [ PrimitiveID ]
& & PrimitiveSceneInfo - > GetInstanceSceneDataOffset ( ) ! = INDEX_NONE
// Don't process primitives that are still in the 'added' state because this means that they
// have not been uploaded to the GPU yet and may be pending from a previous call to update primitive scene infos.
& & ! EnumHasAnyFlags ( GPUScene . GetPrimitiveDirtyState ( PrimitiveID ) , EPrimitiveDirtyState : : Added ) )
{
AlreadyAddedPrimitives [ PrimitiveID ] = true ;
int32 PersistentPrimitiveIndex = PrimitiveSceneInfo - > GetPersistentIndex ( ) . Index ;
// Nanite meshes need special handling because they don't get culled on CPU, thus always process invalidations for those
const bool bIsNaniteMesh = Scene . PrimitiveFlagsCompact [ PrimitiveID ] . bIsNaniteMesh ;
2022-04-14 19:28:00 -04:00
const bool bPossiblyCachedAsStatic = ! PrimitiveSceneInfo - > Proxy - > IsMovable ( ) ;
const int32 NumInstanceSceneDataEntries = PrimitiveSceneInfo - > GetNumInstanceSceneDataEntries ( ) ;
// Add for non-directional lights, mark for skipping clipmaps as these are handled individually below
LoadBalancer . Add ( PrimitiveSceneInfo - > GetInstanceSceneDataOffset ( ) , NumInstanceSceneDataEntries , EncodeInstanceInvalidationPayload ( bPossiblyCachedAsStatic ) ) ;
2022-01-18 08:51:35 -05:00
// Process directional lights, where we explicitly filter out primitives that were not rendered (and mark this fact)
for ( auto & CacheEntry : Manager . PrevCacheEntries )
{
2022-02-09 15:11:16 -05:00
TBitArray < > & CachedPrimitives = CacheEntry . Value - > CachedPrimitives ;
if ( bIsNaniteMesh | | ( PersistentPrimitiveIndex < CachedPrimitives . Num ( ) & & CachedPrimitives [ PersistentPrimitiveIndex ] ) )
2022-01-18 08:51:35 -05:00
{
if ( ! bIsNaniteMesh )
{
// Clear the record as we're wiping it out.
2022-02-09 15:11:16 -05:00
CachedPrimitives [ PersistentPrimitiveIndex ] = false ;
2022-01-18 08:51:35 -05:00
}
// Add item for each shadow map explicitly, inflates host data but improves load balancing,
// TODO: maybe add permutation so we can strip the loop completely.
for ( const auto & SmCacheEntry : CacheEntry . Value - > ShadowMapEntries )
{
if ( SmCacheEntry . IsValid ( ) )
{
2022-04-14 19:28:00 -04:00
checkSlow ( SmCacheEntry - > CurrentVirtualShadowMapId ! = INDEX_NONE ) ;
LoadBalancer . Add ( PrimitiveSceneInfo - > GetInstanceSceneDataOffset ( ) , NumInstanceSceneDataEntries ,
EncodeInstanceInvalidationPayload ( bPossiblyCachedAsStatic , SmCacheEntry - > CurrentVirtualShadowMapId ) ) ;
2022-01-18 08:51:35 -05:00
}
}
}
}
# if VSM_LOG_INVALIDATIONS
RangesStr . Appendf ( TEXT ( " [%6d, %6d), " ) , PrimitiveSceneInfo - > GetInstanceSceneDataOffset ( ) , PrimitiveSceneInfo - > GetInstanceSceneDataOffset ( ) + NumInstanceSceneDataEntries ) ;
# endif
TotalInstanceCount + = NumInstanceSceneDataEntries ;
}
}
2021-12-03 16:08:27 -05:00
FVirtualShadowMapArrayCacheManager : : FVirtualShadowMapArrayCacheManager ( FScene * InScene )
: Scene ( InScene )
{
// Handle message with status sent back from GPU
StatusFeedbackSocket = GPUMessage : : RegisterHandler ( TEXT ( " Shadow.Virtual.StatusFeedback " ) , [ this ] ( GPUMessage : : FReader Message )
{
2022-05-24 13:17:13 -04:00
// Only process status messages that came from this specific cache manager
if ( Message . MessageId = = this - > StatusFeedbackSocket . GetMessageId ( ) )
2021-12-03 16:08:27 -05:00
{
2022-05-24 13:17:13 -04:00
// Get the frame that the message was sent.
uint32 FrameNumber = Message . Read < uint32 > ( 0 ) ;
// Goes negative on underflow
int32 NumPagesFree = Message . Read < int32 > ( 0 ) ;
2021-12-03 16:08:27 -05:00
2022-05-24 13:17:13 -04:00
if ( NumPagesFree < 0 )
2021-12-03 16:08:27 -05:00
{
2022-05-24 13:17:13 -04:00
static const auto * CVarResolutionLodBiasLocalPtr = IConsoleManager : : Get ( ) . FindTConsoleVariableDataFloat ( TEXT ( " r.Shadow.Virtual.ResolutionLodBiasLocal " ) ) ;
const float LodBiasLocal = CVarResolutionLodBiasLocalPtr - > GetValueOnRenderThread ( ) ;
static const auto * CVarResolutionLodBiasDirectionalPtr = IConsoleManager : : Get ( ) . FindTConsoleVariableDataFloat ( TEXT ( " r.Shadow.Virtual.ResolutionLodBiasDirectional " ) ) ;
const float LodBiasDirectional = CVarResolutionLodBiasDirectionalPtr - > GetValueOnRenderThread ( ) ;
static const auto * CVarMaxPhysicalPagesPtr = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.Shadow.Virtual.MaxPhysicalPages " ) ) ;
const int32 MaxPhysicalPages = CVarMaxPhysicalPagesPtr - > GetValueOnRenderThread ( ) ;
2021-12-03 16:08:27 -05:00
# if !UE_BUILD_SHIPPING
2022-05-24 13:17:13 -04:00
if ( ! bLoggedPageOverflow )
{
UE_LOG ( LogRenderer , Warning , TEXT ( " Virtual Shadow Map Page Pool overflow (%d page allocations were not served), this will produce visual artifacts (missing shadow), increase the page pool limit or reduce resolution bias to avoid. \n " )
TEXT ( " See r.Shadow.Virtual.MaxPhysicalPages (%d), r.Shadow.Virtual.ResolutionLodBiasLocal (%.2f), and r.Shadow.Virtual.ResolutionLodBiasDirectional (%.2f) " ) ,
- NumPagesFree ,
MaxPhysicalPages ,
LodBiasLocal ,
LodBiasDirectional ) ;
bLoggedPageOverflow = true ;
}
LastOverflowFrame = Scene - > GetFrameNumber ( ) ;
2021-12-03 16:08:27 -05:00
# endif
2022-05-24 13:17:13 -04:00
}
# if !UE_BUILD_SHIPPING
else
{
bLoggedPageOverflow = false ;
}
# endif
}
2021-12-03 16:08:27 -05:00
} ) ;
# if !UE_BUILD_SHIPPING
2021-12-03 17:53:58 -05:00
ScreenMessageDelegate = FRendererOnScreenNotification : : Get ( ) . AddLambda ( [ this ] ( TMultiMap < FCoreDelegates : : EOnScreenMessageSeverity , FText > & OutMessages )
2021-12-03 16:08:27 -05:00
{
// Show for ~5s after last overflow
int32 CurrentFrameNumber = Scene - > GetFrameNumber ( ) ;
if ( LastOverflowFrame > = 0 & & CurrentFrameNumber - LastOverflowFrame < 30 * 5 )
{
OutMessages . Add ( FCoreDelegates : : EOnScreenMessageSeverity : : Warning , FText : : FromString ( FString : : Printf ( TEXT ( " Virtual Shadow Map Page Pool overflow detected (%d frames ago) " ) , CurrentFrameNumber - LastOverflowFrame ) ) ) ;
}
} ) ;
# endif
}
FVirtualShadowMapArrayCacheManager : : ~ FVirtualShadowMapArrayCacheManager ( )
{
# if !UE_BUILD_SHIPPING
2021-12-03 17:53:58 -05:00
FRendererOnScreenNotification : : Get ( ) . Remove ( ScreenMessageDelegate ) ;
2021-12-03 16:08:27 -05:00
# endif
}
2020-07-06 18:58:26 -04:00
2022-04-07 18:36:13 -04:00
TRefCountPtr < IPooledRenderTarget > FVirtualShadowMapArrayCacheManager : : SetPhysicalPoolSize ( FRDGBuilder & GraphBuilder , FIntPoint RequestedSize , int RequestedArraySize )
2021-07-28 17:00:37 -04:00
{
2022-04-07 18:36:13 -04:00
if ( ! PhysicalPagePool | | PhysicalPagePool - > GetDesc ( ) . Extent ! = RequestedSize | | PhysicalPagePool - > GetDesc ( ) . ArraySize ! = RequestedArraySize )
2021-07-28 17:00:37 -04:00
{
2022-04-07 18:36:13 -04:00
FPooledRenderTargetDesc Desc2D = FPooledRenderTargetDesc : : Create2DArrayDesc (
2021-07-28 17:00:37 -04:00
RequestedSize ,
PF_R32_UINT ,
FClearValueBinding : : None ,
TexCreate_None ,
TexCreate_ShaderResource | TexCreate_UAV ,
2022-04-07 18:36:13 -04:00
false ,
RequestedArraySize
) ;
2021-07-28 17:00:37 -04:00
GRenderTargetPool . FindFreeElement ( GraphBuilder . RHICmdList , Desc2D , PhysicalPagePool , TEXT ( " Shadow.Virtual.PhysicalPagePool " ) ) ;
Invalidate ( ) ;
2021-07-28 21:25:41 -04:00
//UE_LOG(LogRenderer, Display, TEXT("Recreating Shadow.Virtual.PhysicalPagePool. This will also drop any cached pages."));
2021-07-28 17:00:37 -04:00
}
return PhysicalPagePool ;
}
void FVirtualShadowMapArrayCacheManager : : FreePhysicalPool ( )
{
if ( PhysicalPagePool )
{
PhysicalPagePool = nullptr ;
Invalidate ( ) ;
}
}
2022-03-23 15:54:41 -04:00
TRefCountPtr < IPooledRenderTarget > FVirtualShadowMapArrayCacheManager : : SetHZBPhysicalPoolSize ( FRDGBuilder & GraphBuilder , FIntPoint RequestedHZBSize , const EPixelFormat Format )
{
if ( ! HZBPhysicalPagePool | | HZBPhysicalPagePool - > GetDesc ( ) . Extent ! = RequestedHZBSize | | HZBPhysicalPagePool - > GetDesc ( ) . Format ! = Format )
{
2022-04-07 18:36:13 -04:00
// TODO: This may need to be an array as well
2022-03-23 15:54:41 -04:00
FPooledRenderTargetDesc Desc = FPooledRenderTargetDesc : : Create2DDesc (
RequestedHZBSize ,
Format ,
FClearValueBinding : : None ,
GFastVRamConfig . HZB ,
TexCreate_ShaderResource | TexCreate_UAV ,
false ,
FVirtualShadowMap : : NumHZBLevels ) ;
GRenderTargetPool . FindFreeElement ( GraphBuilder . RHICmdList , Desc , HZBPhysicalPagePool , TEXT ( " Shadow.Virtual.HZBPhysicalPagePool " ) ) ;
}
return HZBPhysicalPagePool ;
}
void FVirtualShadowMapArrayCacheManager : : FreeHZBPhysicalPool ( )
{
if ( HZBPhysicalPagePool )
{
HZBPhysicalPagePool = nullptr ;
Invalidate ( ) ;
}
}
2021-07-28 17:00:37 -04:00
void FVirtualShadowMapArrayCacheManager : : Invalidate ( )
{
// Clear the cache
PrevCacheEntries . Empty ( ) ;
CacheEntries . Reset ( ) ;
}
2022-01-18 08:51:35 -05:00
TSharedPtr < FVirtualShadowMapCacheEntry > FVirtualShadowMapPerLightCacheEntry : : FindCreateShadowMapEntry ( int32 Index )
{
check ( Index > = 0 ) ;
ShadowMapEntries . SetNum ( FMath : : Max ( Index + 1 , ShadowMapEntries . Num ( ) ) ) ;
TSharedPtr < FVirtualShadowMapCacheEntry > & EntryRef = ShadowMapEntries [ Index ] ;
if ( ! EntryRef . IsValid ( ) )
{
EntryRef = MakeShared < FVirtualShadowMapCacheEntry > ( ) ;
}
return EntryRef ;
}
TSharedPtr < FVirtualShadowMapPerLightCacheEntry > FVirtualShadowMapArrayCacheManager : : FindCreateLightCacheEntry ( int32 LightSceneId )
{
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) = = 0 )
{
return nullptr ;
}
if ( TSharedPtr < FVirtualShadowMapPerLightCacheEntry > * LightEntry = CacheEntries . Find ( LightSceneId ) )
{
return * LightEntry ;
}
// Add to current frame / active set.
TSharedPtr < FVirtualShadowMapPerLightCacheEntry > & NewLightEntry = CacheEntries . Add ( LightSceneId ) ;
// Copy data if available
if ( TSharedPtr < FVirtualShadowMapPerLightCacheEntry > * PrevNewLightEntry = PrevCacheEntries . Find ( LightSceneId ) )
{
NewLightEntry = * PrevNewLightEntry ;
}
else
{
2022-01-19 07:04:51 -05:00
NewLightEntry = MakeShared < FVirtualShadowMapPerLightCacheEntry > ( Scene - > GetMaxPersistentPrimitiveIndex ( ) ) ;
2022-01-18 08:51:35 -05:00
}
// return entry
return NewLightEntry ;
}
2021-07-28 17:00:37 -04:00
TSharedPtr < FVirtualShadowMapCacheEntry > FVirtualShadowMapArrayCacheManager : : FindCreateCacheEntry ( int32 LightSceneId , int32 Index )
2020-07-06 18:58:26 -04:00
{
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) = = 0 )
{
return nullptr ;
}
2022-01-18 08:51:35 -05:00
return FindCreateLightCacheEntry ( LightSceneId ) - > FindCreateShadowMapEntry ( Index ) ;
2020-07-06 18:58:26 -04:00
}
2022-02-09 15:11:16 -05:00
void FVirtualShadowMapPerLightCacheEntry : : OnPrimitiveRendered ( const FPrimitiveSceneInfo * PrimitiveSceneInfo )
{
// Mark as (potentially present in a cached page somehwere, so we'd need to invalidate if it is removed/moved)
CachedPrimitives [ PrimitiveSceneInfo - > GetPersistentIndex ( ) . Index ] = true ;
if ( GVSMCacheDeformableMeshesInvalidate ! = 0 )
{
// Deformable mesh primitives need to trigger invalidation (even if they did not move) or we get artifacts, for example skinned meshes that are animating but not currently moving.
if ( PrimitiveSceneInfo - > Proxy - > HasDeformableMesh ( ) )
{
PrimitiveInstancesToInvalidate . Add ( FInstanceRange { PrimitiveSceneInfo - > GetInstanceSceneDataOffset ( ) , PrimitiveSceneInfo - > GetNumInstanceSceneDataEntries ( ) } ) ;
}
}
}
2020-07-06 18:58:26 -04:00
class FVirtualSmCopyStatsCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER ( FVirtualSmCopyStatsCS ) ;
SHADER_USE_PARAMETER_STRUCT ( FVirtualSmCopyStatsCS , FGlobalShader )
public :
BEGIN_SHADER_PARAMETER_STRUCT ( FParameters , )
SHADER_PARAMETER_RDG_BUFFER_SRV ( StructuredBuffer < uint > , InStatsBuffer )
SHADER_PARAMETER_RDG_BUFFER_UAV ( RWBuffer < uint > , AccumulatedStatsBufferOut )
SHADER_PARAMETER ( uint32 , NumStats )
END_SHADER_PARAMETER_STRUCT ( )
static bool ShouldCompilePermutation ( const FGlobalShaderPermutationParameters & Parameters )
{
2022-01-27 14:34:07 -05:00
return IsFeatureLevelSupported ( Parameters . Platform , ERHIFeatureLevel : : SM5 ) & &
DoesPlatformSupportNanite ( Parameters . Platform ) ;
2020-07-06 18:58:26 -04:00
}
static void ModifyCompilationEnvironment ( const FGlobalShaderPermutationParameters & Parameters , FShaderCompilerEnvironment & OutEnvironment )
{
FGlobalShader : : ModifyCompilationEnvironment ( Parameters , OutEnvironment ) ;
OutEnvironment . SetDefine ( TEXT ( " MAX_STAT_FRAMES " ) , FVirtualShadowMapArrayCacheManager : : MaxStatFrames ) ;
}
} ;
2022-02-14 05:44:50 -05:00
IMPLEMENT_GLOBAL_SHADER ( FVirtualSmCopyStatsCS , " /Engine/Private/VirtualShadowMaps/VirtualShadowMapCopyStats.usf " , " CopyStatsCS " , SF_Compute ) ;
2020-07-06 18:58:26 -04:00
2021-07-28 17:00:37 -04:00
void FVirtualShadowMapArrayCacheManager : : ExtractFrameData (
FRDGBuilder & GraphBuilder ,
FVirtualShadowMapArray & VirtualShadowMapArray ,
2022-02-09 15:11:16 -05:00
const FSceneRenderer & SceneRenderer ,
2021-07-28 17:00:37 -04:00
bool bEnableCaching )
2020-07-06 18:58:26 -04:00
{
2022-04-04 18:25:43 -04:00
const bool bNewShadowData = VirtualShadowMapArray . IsAllocated ( ) ;
const bool bDropAll = ! bEnableCaching ;
const bool bDropPrevBuffers = bDropAll | | bNewShadowData ;
2021-03-23 21:23:57 -04:00
2022-04-04 18:25:43 -04:00
if ( bDropPrevBuffers )
{
PrevBuffers = FVirtualShadowMapArrayFrameData ( ) ;
PrevUniformParameters . NumShadowMaps = 0 ;
}
if ( bDropAll )
{
// We drop the physical page pool here as well to ensure that it disappears in the case where
// thumbnail rendering or similar creates multiple FSceneRenderers that never get deleted.
// Caching is disabled on these contexts intentionally to avoid these issues.
FreePhysicalPool ( ) ;
}
else if ( bNewShadowData )
2020-07-06 18:58:26 -04:00
{
2022-03-03 06:41:20 -05:00
bool bExtractHzbData = false ;
2021-03-25 18:19:54 -04:00
// HZB and associated page table are needed by next frame even when VSM physical page caching is disabled
if ( VirtualShadowMapArray . HZBPhysical )
{
2022-03-03 06:41:20 -05:00
bExtractHzbData = true ;
2021-03-25 18:19:54 -04:00
GraphBuilder . QueueTextureExtraction ( VirtualShadowMapArray . HZBPhysical , & PrevBuffers . HZBPhysical ) ;
PrevBuffers . HZBMetadata = VirtualShadowMapArray . HZBMetadata ;
}
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) ! = 0 )
{
2022-03-03 06:41:20 -05:00
bExtractHzbData = true ;
2022-04-04 18:25:43 -04:00
2021-03-25 18:19:54 -04:00
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . PhysicalPageMetaDataRDG , & PrevBuffers . PhysicalPageMetaData ) ;
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . DynamicCasterPageFlagsRDG , & PrevBuffers . DynamicCasterPageFlags ) ;
2022-02-02 02:18:54 -05:00
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . ProjectionDataRDG , & PrevBuffers . ProjectionData ) ;
2021-09-02 07:13:18 -04:00
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . InvalidatingInstancesRDG , & PrevBuffers . InvalidatingInstancesBuffer ) ;
PrevBuffers . NumInvalidatingInstanceSlots = VirtualShadowMapArray . NumInvalidatingInstanceSlots ;
2021-03-25 18:19:54 -04:00
// Move cache entries to previous frame, this implicitly removes any that were not used
PrevCacheEntries = CacheEntries ;
2022-04-04 18:25:43 -04:00
// Store but drop any temp references embedded in the uniform parameters this frame.
// We'll reestablish them when we reimport the extracted resources next frame
2021-03-25 18:19:54 -04:00
PrevUniformParameters = VirtualShadowMapArray . UniformParameters ;
2022-04-04 18:25:43 -04:00
PrevUniformParameters . ProjectionData = nullptr ;
PrevUniformParameters . PageTable = nullptr ;
PrevUniformParameters . PhysicalPagePool = nullptr ;
2021-02-22 14:47:36 -04:00
}
2021-02-25 05:03:27 -04:00
2022-03-03 06:41:20 -05:00
if ( bExtractHzbData )
2021-03-25 18:19:54 -04:00
{
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . PageTableRDG , & PrevBuffers . PageTable ) ;
2022-03-03 06:41:20 -05:00
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . PageRectBoundsRDG , & PrevBuffers . PageRectBounds ) ;
2022-03-15 10:05:21 -04:00
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . PageFlagsRDG , & PrevBuffers . PageFlags ) ;
2021-03-25 18:19:54 -04:00
}
2021-07-28 21:25:41 -04:00
2022-02-09 15:11:16 -05:00
// propagate current-frame primitive state to cache entry
UE5_MAIN: Multi-view-family scene renderer refactor, part 1. Major structural change to allow scene renderer to accept multiple view families, with otherwise negligible changes in internal behavior.
* Added "BeginRenderingViewFamilies" render interface call that accepts multiple view families. Original "BeginRenderingViewFamily" falls through to this.
* FSceneRenderer modified to include an array of view families, plus an active view family and the Views for that family.
* Swap ViewFamily to ActiveViewFamily.
* Swap Views array from TArray<FViewInfo> to TArrayView<FViewInfo>, including where the Views array is passed to functions.
* FSceneRenderer iterates over the view families, rendering each one at a time, as separate render graph executions.
* Some frame setup and cleanup logic outside the render graph runs once.
* Moved stateful FSceneRenderer members to FViewFamilyInfo, to preserve existing one-at-a-time view family rendering behavior.
* Display Cluster (Virtual Production) uses new API.
Next step will push everything into one render graph, which requires handling per-family external resources and cleaning up singletons (like FSceneTextures and FSceneTexturesConfig). Once that's done, we'll be in a position to further interleave rendering, properly handle once per frame work, and solve artifacts in various systems.
#jira none
#rnx
#rb zach.bethel
#preflight 625df821b21bb49791d377c9
[CL 19813996 by jason hoerner in ue5-main branch]
2022-04-19 14:45:26 -04:00
for ( const auto & LightInfo : SceneRenderer . ActiveViewFamily - > VisibleLightInfos )
2022-02-09 15:11:16 -05:00
{
for ( const TSharedPtr < FVirtualShadowMapClipmap > & Clipmap : LightInfo . VirtualShadowMapClipmaps )
{
// Push data to cache entry
Clipmap - > UpdateCachedFrameData ( ) ;
}
}
2021-07-28 21:25:41 -04:00
CacheEntries . Reset ( ) ;
2022-04-04 18:25:43 -04:00
ExtractStats ( GraphBuilder , VirtualShadowMapArray ) ;
2020-07-06 18:58:26 -04:00
}
else
{
2022-04-04 18:25:43 -04:00
// Do nothing; maintain the data that we had
// This allows us to work around some cases where the renderer gets called multiple times in a given frame
// - such as scene captures - but does no shadow-related work in all but one of them. We do not want to drop
// all the cached data in this case otherwise we effectively get no caching at all.
// Ideally in the long run we want the cache itself to be more robust against rendering multiple views. but
// for now this at least provides a work-around for some common cases where only one view is rendering VSMs.
2021-07-28 17:00:37 -04:00
}
}
void FVirtualShadowMapArrayCacheManager : : ExtractStats ( FRDGBuilder & GraphBuilder , FVirtualShadowMapArray & VirtualShadowMapArray )
{
2020-09-24 00:43:27 -04:00
FRDGBufferRef AccumulatedStatsBufferRDG = nullptr ;
2020-07-06 18:58:26 -04:00
// Note: stats accumulation thing is here because it needs to persist over frames.
2021-12-03 01:29:35 -05:00
if ( AccumulatedStatsBuffer . IsValid ( ) )
2020-09-24 00:43:27 -04:00
{
2021-02-18 13:44:36 -04:00
AccumulatedStatsBufferRDG = GraphBuilder . RegisterExternalBuffer ( AccumulatedStatsBuffer , TEXT ( " Shadow.Virtual.AccumulatedStatsBuffer " ) ) ;
2020-07-06 18:58:26 -04:00
}
if ( IsAccumulatingStats ( ) )
{
2021-12-03 01:29:35 -05:00
if ( ! AccumulatedStatsBuffer . IsValid ( ) )
{
FRDGBufferDesc Desc = FRDGBufferDesc : : CreateBufferDesc ( 4 , 1 + FVirtualShadowMapArray : : NumStats * MaxStatFrames ) ;
Desc . Usage = EBufferUsageFlags ( Desc . Usage | BUF_SourceCopy ) ;
AccumulatedStatsBufferRDG = GraphBuilder . CreateBuffer ( Desc , TEXT ( " Shadow.Virtual.AccumulatedStatsBuffer " ) ) ; // TODO: Can't be a structured buffer as EnqueueCopy is only defined for vertex buffers
AddClearUAVPass ( GraphBuilder , GraphBuilder . CreateUAV ( AccumulatedStatsBufferRDG , PF_R32_UINT ) , 0 ) ;
AccumulatedStatsBuffer = GraphBuilder . ConvertToExternalBuffer ( AccumulatedStatsBufferRDG ) ;
}
2020-07-06 18:58:26 -04:00
// Initialize/clear
if ( ! bAccumulatingStats )
{
AddClearUAVPass ( GraphBuilder , GraphBuilder . CreateUAV ( AccumulatedStatsBufferRDG , PF_R32_UINT ) , 0 ) ;
bAccumulatingStats = true ;
}
FVirtualSmCopyStatsCS : : FParameters * PassParameters = GraphBuilder . AllocParameters < FVirtualSmCopyStatsCS : : FParameters > ( ) ;
2020-11-09 15:47:39 -04:00
PassParameters - > InStatsBuffer = GraphBuilder . CreateSRV ( VirtualShadowMapArray . StatsBufferRDG , PF_R32_UINT ) ;
2020-07-06 18:58:26 -04:00
PassParameters - > AccumulatedStatsBufferOut = GraphBuilder . CreateUAV ( AccumulatedStatsBufferRDG , PF_R32_UINT ) ;
PassParameters - > NumStats = FVirtualShadowMapArray : : NumStats ;
auto ComputeShader = GetGlobalShaderMap ( GMaxRHIFeatureLevel ) - > GetShader < FVirtualSmCopyStatsCS > ( ) ;
FComputeShaderUtils : : AddPass (
GraphBuilder ,
RDG_EVENT_NAME ( " Copy Stats " ) ,
ComputeShader ,
PassParameters ,
FIntVector ( 1 , 1 , 1 )
) ;
}
else if ( bAccumulatingStats )
{
bAccumulatingStats = false ;
2020-09-24 00:43:27 -04:00
2021-12-03 01:29:35 -05:00
GPUBufferReadback = new FRHIGPUBufferReadback ( TEXT ( " Shadow.Virtual.AccumulatedStatsBufferReadback " ) ) ;
2020-09-24 00:43:27 -04:00
AddEnqueueCopyPass ( GraphBuilder , GPUBufferReadback , AccumulatedStatsBufferRDG , 0u ) ;
2020-07-06 18:58:26 -04:00
}
2021-12-03 01:29:35 -05:00
else if ( AccumulatedStatsBuffer . IsValid ( ) )
{
AccumulatedStatsBuffer . SafeRelease ( ) ;
}
2020-07-06 18:58:26 -04:00
if ( GPUBufferReadback & & GPUBufferReadback - > IsReady ( ) )
{
TArray < uint32 > Tmp ;
Tmp . AddDefaulted ( 1 + FVirtualShadowMapArray : : NumStats * MaxStatFrames ) ;
{
const uint32 * BufferPtr = ( const uint32 * ) GPUBufferReadback - > Lock ( ( 1 + FVirtualShadowMapArray : : NumStats * MaxStatFrames ) * sizeof ( uint32 ) ) ;
FPlatformMemory : : Memcpy ( Tmp . GetData ( ) , BufferPtr , Tmp . Num ( ) * Tmp . GetTypeSize ( ) ) ;
GPUBufferReadback - > Unlock ( ) ;
delete GPUBufferReadback ;
GPUBufferReadback = nullptr ;
}
2021-02-18 13:44:36 -04:00
FString FileName = TEXT ( " VirtualShadowMapCacheStats.csv " ) ; // FString::Printf(TEXT("%s.csv"), *FileNameToUse);
2020-07-06 18:58:26 -04:00
FArchive * FileToLogTo = IFileManager : : Get ( ) . CreateFileWriter ( * FileName , false ) ;
ensure ( FileToLogTo ) ;
if ( FileToLogTo )
{
2021-12-02 18:25:13 -05:00
static const FString StatNames [ ] =
2020-07-06 18:58:26 -04:00
{
TEXT ( " Allocated " ) ,
2021-12-02 23:08:02 -05:00
TEXT ( " StaticCached " ) ,
TEXT ( " StaticInvalidated " ) ,
TEXT ( " DynamicCached " ) ,
TEXT ( " DynamicInvalidated " ) ,
2020-07-06 18:58:26 -04:00
TEXT ( " NumSms " ) ,
2021-12-02 18:25:13 -05:00
TEXT ( " NonNaniteInstances " ) ,
TEXT ( " NonNaniteInstancesDrawn " ) ,
TEXT ( " NonNaniteInstancesHZBCulled " ) ,
TEXT ( " NonNaniteInstancesPageMaskCulled " ) ,
TEXT ( " NonNaniteInstancesEmptyRectCulled " ) ,
TEXT ( " NonNaniteInstancesFrustumCulled " ) ,
2020-07-06 18:58:26 -04:00
} ;
// Print header
FString StringToPrint ;
2021-12-02 18:25:13 -05:00
for ( int32 Index = 0 ; Index < FVirtualShadowMapArray : : NumStats ; + + Index )
2020-07-06 18:58:26 -04:00
{
if ( ! StringToPrint . IsEmpty ( ) )
{
StringToPrint + = TEXT ( " , " ) ;
}
2021-12-02 18:25:13 -05:00
if ( Index < int32 ( UE_ARRAY_COUNT ( StatNames ) ) )
{
StringToPrint . Append ( StatNames [ Index ] ) ;
}
else
{
StringToPrint . Appendf ( TEXT ( " Stat_%d " ) , Index ) ;
}
2020-07-06 18:58:26 -04:00
}
StringToPrint + = TEXT ( " \n " ) ;
FileToLogTo - > Serialize ( TCHAR_TO_ANSI ( * StringToPrint ) , StringToPrint . Len ( ) ) ;
uint32 Num = Tmp [ 0 ] ;
for ( uint32 Ind = 0 ; Ind < Num ; + + Ind )
{
StringToPrint . Empty ( ) ;
for ( uint32 StatInd = 0 ; StatInd < FVirtualShadowMapArray : : NumStats ; + + StatInd )
{
if ( ! StringToPrint . IsEmpty ( ) )
{
StringToPrint + = TEXT ( " , " ) ;
}
StringToPrint + = FString : : Printf ( TEXT ( " %d " ) , Tmp [ 1 + Ind * FVirtualShadowMapArray : : NumStats + StatInd ] ) ;
}
StringToPrint + = TEXT ( " \n " ) ;
FileToLogTo - > Serialize ( TCHAR_TO_ANSI ( * StringToPrint ) , StringToPrint . Len ( ) ) ;
}
FileToLogTo - > Close ( ) ;
}
}
}
2021-07-28 17:00:37 -04:00
2020-07-06 18:58:26 -04:00
bool FVirtualShadowMapArrayCacheManager : : IsValid ( )
{
return CVarCacheVirtualSMs . GetValueOnRenderThread ( ) ! = 0
2021-03-23 21:23:57 -04:00
& & PrevBuffers . PageTable
& & PrevBuffers . PageFlags
& & PrevBuffers . PhysicalPageMetaData
& & PrevBuffers . DynamicCasterPageFlags ;
2020-07-06 18:58:26 -04:00
}
bool FVirtualShadowMapArrayCacheManager : : IsAccumulatingStats ( )
{
return CVarAccumulateStats . GetValueOnRenderThread ( ) ! = 0 ;
}
2020-08-25 10:06:54 -04:00
2021-12-03 15:36:38 -05:00
void FVirtualShadowMapArrayCacheManager : : ProcessRemovedOrUpdatedPrimitives ( FRDGBuilder & GraphBuilder , const FGPUScene & GPUScene , FInvalidatingPrimitiveCollector & InvalidatingPrimitiveCollector )
2020-08-25 10:06:54 -04:00
{
2021-12-09 04:08:19 -05:00
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) ! = 0 & & PrevBuffers . DynamicCasterPageFlags . IsValid ( ) )
2020-08-25 10:06:54 -04:00
{
2021-12-03 15:36:38 -05:00
RDG_EVENT_SCOPE ( GraphBuilder , " Shadow.Virtual.ProcessRemovedOrUpdatedPrimitives " ) ;
2021-12-09 04:08:19 -05:00
ProcessGPUInstanceInvalidations ( GraphBuilder , GPUScene ) ;
if ( ! InvalidatingPrimitiveCollector . IsEmpty ( ) )
{
2021-12-03 15:36:38 -05:00
# if VSM_LOG_INVALIDATIONS
2021-12-09 04:08:19 -05:00
UE_LOG ( LogTemp , Warning , TEXT ( " ProcessRemovedOrUpdatedPrimitives: \n %s " ) , * InvalidatingPrimitiveCollector . RangesStr ) ;
2021-12-03 15:36:38 -05:00
# endif
2021-12-09 04:08:19 -05:00
ProcessInvalidations ( GraphBuilder , InvalidatingPrimitiveCollector . LoadBalancer , InvalidatingPrimitiveCollector . TotalInstanceCount , GPUScene ) ;
}
2020-08-25 10:06:54 -04:00
}
}
2022-01-18 08:51:35 -05:00
static void ResizeFlagArray ( TBitArray < > & BitArray , int32 NewMax )
{
if ( BitArray . Num ( ) > NewMax )
{
// Trim off excess items
BitArray . SetNumUninitialized ( NewMax ) ;
}
else if ( BitArray . Num ( ) < NewMax )
{
// Add false
BitArray . Add ( false , NewMax - BitArray . Num ( ) ) ;
}
}
void FVirtualShadowMapArrayCacheManager : : OnSceneChange ( )
{
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) ! = 0 )
{
for ( auto & CacheEntry : PrevCacheEntries )
{
2022-02-09 15:11:16 -05:00
ResizeFlagArray ( CacheEntry . Value - > CachedPrimitives , Scene - > GetMaxPersistentPrimitiveIndex ( ) ) ;
2022-01-18 08:51:35 -05:00
ResizeFlagArray ( CacheEntry . Value - > RenderedPrimitives , Scene - > GetMaxPersistentPrimitiveIndex ( ) ) ;
}
2022-01-19 07:04:51 -05:00
for ( auto & CacheEntry : CacheEntries )
{
2022-02-09 15:11:16 -05:00
ResizeFlagArray ( CacheEntry . Value - > CachedPrimitives , Scene - > GetMaxPersistentPrimitiveIndex ( ) ) ;
2022-01-19 07:04:51 -05:00
ResizeFlagArray ( CacheEntry . Value - > RenderedPrimitives , Scene - > GetMaxPersistentPrimitiveIndex ( ) ) ;
}
2022-01-18 08:51:35 -05:00
}
}
2020-08-25 10:06:54 -04:00
/**
* Compute shader to project and invalidate the rectangles of given instances .
*/
class FVirtualSmInvalidateInstancePagesCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER ( FVirtualSmInvalidateInstancePagesCS ) ;
SHADER_USE_PARAMETER_STRUCT ( FVirtualSmInvalidateInstancePagesCS , FGlobalShader )
2021-01-21 13:38:01 -04:00
2021-09-02 07:13:18 -04:00
enum EInputDataKind
{
EInputKind_GPUInstances ,
2021-12-03 10:22:46 -05:00
EInputKind_LoadBalancer ,
2021-09-02 07:13:18 -04:00
EInputKind_Num
} ;
2021-08-24 06:42:43 -04:00
class FDebugDim : SHADER_PERMUTATION_BOOL ( " ENABLE_DEBUG_MODE " ) ;
2021-09-02 07:13:18 -04:00
class FInputKindDim : SHADER_PERMUTATION_INT ( " INPUT_KIND " , EInputKind_Num ) ;
2021-09-02 10:25:16 -04:00
class FUseHzbDim : SHADER_PERMUTATION_BOOL ( " USE_HZB_OCCLUSION " ) ;
using FPermutationDomain = TShaderPermutationDomain < FUseHzbDim , FDebugDim , FInputKindDim > ;
2021-01-21 13:38:01 -04:00
2020-08-25 10:06:54 -04:00
public :
BEGIN_SHADER_PARAMETER_STRUCT ( FParameters , )
2021-03-08 23:14:54 -04:00
SHADER_PARAMETER_RDG_UNIFORM_BUFFER ( FVirtualShadowMapUniformParameters , VirtualShadowMap )
2022-02-22 15:35:01 -05:00
SHADER_PARAMETER_STRUCT_INCLUDE ( ShaderPrint : : FShaderParameters , ShaderPrintUniformBuffer )
2021-08-24 06:42:43 -04:00
SHADER_PARAMETER ( uint32 , bDrawBounds )
2021-12-02 23:08:02 -05:00
SHADER_PARAMETER_RDG_BUFFER_UAV ( RWStructuredBuffer < uint > , OutDynamicCasterPageFlags )
2020-08-25 10:06:54 -04:00
2022-05-03 12:08:20 -04:00
SHADER_PARAMETER_RDG_BUFFER_SRV ( StructuredBuffer < float4 > , GPUSceneInstanceSceneData )
SHADER_PARAMETER_RDG_BUFFER_SRV ( StructuredBuffer < float4 > , GPUSceneInstancePayloadData )
SHADER_PARAMETER_RDG_BUFFER_SRV ( StructuredBuffer < float4 > , GPUScenePrimitiveSceneData )
2020-08-25 10:06:54 -04:00
SHADER_PARAMETER ( uint32 , GPUSceneFrameNumber )
2021-06-14 13:43:26 -04:00
SHADER_PARAMETER ( uint32 , InstanceSceneDataSOAStride )
2021-12-03 15:36:38 -05:00
SHADER_PARAMETER ( uint32 , GPUSceneNumAllocatedInstances )
SHADER_PARAMETER ( uint32 , GPUSceneNumAllocatedPrimitives )
2021-09-02 07:13:18 -04:00
2022-03-03 06:41:20 -05:00
SHADER_PARAMETER_RDG_BUFFER_SRV ( StructuredBuffer < uint > , HZBPageTable )
SHADER_PARAMETER_RDG_BUFFER_SRV ( StructuredBuffer < uint4 > , HZBPageRectBounds )
2021-09-02 10:25:16 -04:00
SHADER_PARAMETER_RDG_TEXTURE ( Texture2D , HZBTexture )
SHADER_PARAMETER_SAMPLER ( SamplerState , HZBSampler )
2021-11-18 14:37:34 -05:00
SHADER_PARAMETER ( FVector2f , HZBSize )
2021-09-02 10:25:16 -04:00
2021-09-02 07:13:18 -04:00
SHADER_PARAMETER_RDG_BUFFER_SRV ( StructuredBuffer < uint > , InvalidatingInstances )
SHADER_PARAMETER ( uint32 , NumInvalidatingInstanceSlots )
RDG_BUFFER_ACCESS ( IndirectArgs , ERHIAccess : : IndirectArgs )
2021-12-03 10:22:46 -05:00
SHADER_PARAMETER_STRUCT_INCLUDE ( FGPUScene : : FInstanceGPULoadBalancer : : FShaderParameters , LoadBalancerParameters )
2020-08-25 10:06:54 -04:00
END_SHADER_PARAMETER_STRUCT ( )
2021-12-03 10:22:46 -05:00
static constexpr int Cs1dGroupSizeX = FVirtualShadowMapArrayCacheManager : : FInstanceGPULoadBalancer : : ThreadGroupSize ;
2020-08-25 10:06:54 -04:00
static bool ShouldCompilePermutation ( const FGlobalShaderPermutationParameters & Parameters )
{
2022-01-27 14:34:07 -05:00
return IsFeatureLevelSupported ( Parameters . Platform , ERHIFeatureLevel : : SM5 ) & &
DoesPlatformSupportNanite ( Parameters . Platform ) ;
2020-08-25 10:06:54 -04:00
}
static void ModifyCompilationEnvironment ( const FGlobalShaderPermutationParameters & Parameters , FShaderCompilerEnvironment & OutEnvironment )
{
FGlobalShader : : ModifyCompilationEnvironment ( Parameters , OutEnvironment ) ;
FVirtualShadowMapArray : : SetShaderDefines ( OutEnvironment ) ;
OutEnvironment . SetDefine ( TEXT ( " CS_1D_GROUP_SIZE_X " ) , Cs1dGroupSizeX ) ;
OutEnvironment . SetDefine ( TEXT ( " USE_GLOBAL_GPU_SCENE_DATA " ) , 1 ) ;
OutEnvironment . SetDefine ( TEXT ( " VF_SUPPORTS_PRIMITIVE_SCENE_DATA " ) , 1 ) ;
2021-09-02 07:13:18 -04:00
OutEnvironment . SetDefine ( TEXT ( " INPUT_KIND_GPU_INSTANCES " ) , EInputKind_GPUInstances ) ;
2021-12-03 10:22:46 -05:00
OutEnvironment . SetDefine ( TEXT ( " INPUT_KIND_LOAD_BALANCER " ) , EInputKind_LoadBalancer ) ;
FGPUScene : : FInstanceGPULoadBalancer : : SetShaderDefines ( OutEnvironment ) ;
2020-08-25 10:06:54 -04:00
}
} ;
2022-02-14 05:44:50 -05:00
IMPLEMENT_GLOBAL_SHADER ( FVirtualSmInvalidateInstancePagesCS , " /Engine/Private/VirtualShadowMaps/VirtualShadowMapCacheManagement.usf " , " VirtualSmInvalidateInstancePagesCS " , SF_Compute ) ;
2020-08-25 10:06:54 -04:00
2021-03-08 23:14:54 -04:00
TRDGUniformBufferRef < FVirtualShadowMapUniformParameters > FVirtualShadowMapArrayCacheManager : : GetPreviousUniformBuffer ( FRDGBuilder & GraphBuilder ) const
{
FVirtualShadowMapUniformParameters * VersionedParameters = GraphBuilder . AllocParameters < FVirtualShadowMapUniformParameters > ( ) ;
* VersionedParameters = PrevUniformParameters ;
return GraphBuilder . CreateUniformBuffer ( VersionedParameters ) ;
}
2021-12-02 18:25:13 -05:00
void FVirtualShadowMapArrayCacheManager : : SetHZBViewParams ( int32 HZBKey , Nanite : : FPackedViewParams & OutParams )
{
FVirtualShadowMapHZBMetadata * PrevHZBMeta = PrevBuffers . HZBMetadata . Find ( HZBKey ) ;
if ( PrevHZBMeta )
{
OutParams . PrevTargetLayerIndex = PrevHZBMeta - > TargetLayerIndex ;
OutParams . PrevViewMatrices = PrevHZBMeta - > ViewMatrices ;
Removed a massive number of Nanite rasterizer shader permutations across all platforms/shaderdbs, significantly improving iteration times for the editor and cooker, especially when these numbers get multiplied by the number of materials that utilize programmable features in addition to the default material "fixed function" path.
Reductions *per material*:
SM5
--
FHWRasterizeVS: 832 -> 21
FHWRasterizePS: 104 -> 39
SM6
--
FHWRasterizeVS: 320 -> 9
FHWRasterizeMS: 640 -> 9
FHWRasterizePS: 120 -> 30
Vulkan
--
FHWRasterizeVS: 320 -> 9
FHWRasterizePS: 40 -> 15
Other platforms redacted =)
-- Details
* CLUSTER_PER_PAGE has been fully removed (since we no longer ever run CLUSTER_PER_PAGE=0), which now makes it mutually inclusive with VIRTUAL_TEXTURE_TARGET
* HAS_RASTER_BIN has been replaced with a dynamic branch, since this is just a per cluster index offset based on a simple uniform buffer load
* ADD_CLUSTER_OFFSET has been replaced with a dynamic branch, since this is just a per cluster index offset based on a simple uniform buffer load
* HAS_PREV_DRAW_DATA has been replaced with a dynamic branch, since this is just a per cluster index offset based on a simple uniform buffer load
* NEAR_CLIP (only change to significantly affect codegen) has been turned into a dynamic branch based on FNaniteView - this lets us merge depth clip/clamp rasterizer calls in VSM together instead of relying on HAS_PREV_DRAW_DATA, and a future optimization can now be done to merge local and directional light full Nanite pipeline calls together.
* VISUALIZE permutation removed from VS/MS since it only loaded unform values that passed down per-vertex into fragment stage as nointerpolation parameters. Pixel shader now constructs this uint2 directly under the VISUALIZE permutation
* NANITE_MESH_SHADER_INTERP removed by default but still left in the code, since it is a work in progress potential optimization for DX12 mesh shaders
* Removed explicit Lumen and VSM usage of NANITE_RENDER_FLAG_HAVE_PREV_DRAW_DATA (now the dynamic branch path is only taken if CullRasterizeMultiPass implicitly breaks the rasterization into multiple calls due to NANITE_MAX_VIEWS_PER_CULL_RASTERIZE_PASS overflow)
Performance was tested on a 2080Ti in AncientGame, and the delta is effectively noise (tested cached and uncached VSM). Further testing on other platforms will occur, but important to get this change in for all the benefits and easy to tweak things later if needed.
#rb rune.stubbe
#fyi brian.karis, ola.olsson, andrew.lauritzen, jamie.hayes, daniel.wright, krzysztof.narkowicz
#preflight 622e684c7e2e35638c96a16a
#robomerge FNNC
[CL 19370372 by graham wihlidal in ue5-main branch]
2022-03-13 23:18:25 -04:00
OutParams . Flags | = NANITE_VIEW_FLAG_HZBTEST ;
2021-12-02 18:25:13 -05:00
}
}
2021-09-02 10:25:16 -04:00
static void SetupCommonParameters ( FRDGBuilder & GraphBuilder , FVirtualShadowMapArrayCacheManager * CacheManager , int32 TotalInstanceCount , const FGPUScene & GPUScene ,
FVirtualSmInvalidateInstancePagesCS : : FParameters & OutPassParameters ,
FVirtualSmInvalidateInstancePagesCS : : FPermutationDomain & OutPermutationVector )
2020-08-25 10:06:54 -04:00
{
2021-08-24 06:42:43 -04:00
2021-03-08 23:14:54 -04:00
auto RegExtCreateSrv = [ & GraphBuilder ] ( const TRefCountPtr < FRDGPooledBuffer > & Buffer , const TCHAR * Name ) - > FRDGBufferSRVRef
{
return GraphBuilder . CreateSRV ( GraphBuilder . RegisterExternalBuffer ( Buffer , Name ) ) ;
} ;
2021-08-24 06:42:43 -04:00
const bool bDrawBounds = CVarDrawInvalidatingBounds . GetValueOnRenderThread ( ) ! = 0 ;
if ( bDrawBounds )
{
2022-02-22 15:35:01 -05:00
ShaderPrint : : SetEnabled ( true ) ;
ShaderPrint : : RequestSpaceForLines ( TotalInstanceCount * 12 ) ;
2021-08-24 06:42:43 -04:00
}
// Note: this disables the whole debug permutation since the parameters must be bound.
2022-02-22 15:35:01 -05:00
const bool bUseDebugPermutation = bDrawBounds & & ShaderPrint : : IsDefaultViewEnabled ( ) ;
2021-08-24 06:42:43 -04:00
2022-01-18 13:05:54 -05:00
FVirtualShadowMapArrayFrameData & PrevBuffers = CacheManager - > PrevBuffers ;
2021-09-02 07:13:18 -04:00
2021-03-08 23:14:54 -04:00
// Update references in our last frame uniform buffer with reimported resources for this frame
2022-02-02 02:18:54 -05:00
CacheManager - > PrevUniformParameters . ProjectionData = RegExtCreateSrv ( PrevBuffers . ProjectionData , TEXT ( " Shadow.Virtual.PrevProjectionData " ) ) ;
2021-09-02 07:13:18 -04:00
CacheManager - > PrevUniformParameters . PageTable = RegExtCreateSrv ( PrevBuffers . PageTable , TEXT ( " Shadow.Virtual.PrevPageTable " ) ) ;
2022-01-18 13:05:54 -05:00
CacheManager - > PrevUniformParameters . PageFlags = RegExtCreateSrv ( PrevBuffers . PageFlags , TEXT ( " Shadow.Virtual.PrevPageFlags " ) ) ;
CacheManager - > PrevUniformParameters . PageRectBounds = RegExtCreateSrv ( PrevBuffers . PageRectBounds , TEXT ( " Shadow.Virtual.PrevPageRectBounds " ) ) ;
2021-03-08 23:14:54 -04:00
// Unused in this path
2022-04-07 18:36:13 -04:00
CacheManager - > PrevUniformParameters . PhysicalPagePool = GSystemTextures . GetZeroUIntArrayDummy ( GraphBuilder ) ;
2021-09-02 07:13:18 -04:00
OutPassParameters . VirtualShadowMap = CacheManager - > GetPreviousUniformBuffer ( GraphBuilder ) ;
2021-12-02 23:08:02 -05:00
FRDGBufferRef DynamicCasterPageFlagsRDG = GraphBuilder . RegisterExternalBuffer ( PrevBuffers . DynamicCasterPageFlags , TEXT ( " Shadow.Virtual.PrevDynamicCasterFlags " ) ) ;
OutPassParameters . OutDynamicCasterPageFlags = GraphBuilder . CreateUAV ( DynamicCasterPageFlagsRDG ) ;
2021-09-02 07:13:18 -04:00
2022-05-03 12:08:20 -04:00
OutPassParameters . GPUSceneInstanceSceneData = GraphBuilder . CreateSRV ( GraphBuilder . RegisterExternalBuffer ( GPUScene . InstanceSceneDataBuffer ) ) ;
OutPassParameters . GPUScenePrimitiveSceneData = GraphBuilder . CreateSRV ( GraphBuilder . RegisterExternalBuffer ( GPUScene . PrimitiveBuffer ) ) ;
OutPassParameters . GPUSceneInstancePayloadData = GraphBuilder . CreateSRV ( GraphBuilder . RegisterExternalBuffer ( GPUScene . InstancePayloadDataBuffer ) ) ;
2021-09-02 07:13:18 -04:00
OutPassParameters . GPUSceneFrameNumber = GPUScene . GetSceneFrameNumber ( ) ;
2021-12-03 15:36:38 -05:00
OutPassParameters . GPUSceneNumAllocatedInstances = GPUScene . GetNumInstances ( ) ;
OutPassParameters . GPUSceneNumAllocatedPrimitives = GPUScene . GetNumPrimitives ( ) ;
2021-09-02 07:13:18 -04:00
OutPassParameters . InstanceSceneDataSOAStride = GPUScene . InstanceSceneDataSOAStride ;
OutPassParameters . bDrawBounds = bDrawBounds ;
if ( bUseDebugPermutation )
{
2022-02-22 15:35:01 -05:00
ShaderPrint : : SetParameters ( GraphBuilder , OutPassParameters . ShaderPrintUniformBuffer ) ;
2021-09-02 07:13:18 -04:00
}
2021-09-02 10:25:16 -04:00
const bool bUseHZB = ( CVarCacheVsmUseHzb . GetValueOnRenderThread ( ) ! = 0 ) ;
const TRefCountPtr < IPooledRenderTarget > PrevHZBPhysical = bUseHZB ? PrevBuffers . HZBPhysical : nullptr ;
if ( PrevHZBPhysical )
{
// Same, since we are not producing a new frame just yet
2022-03-03 06:41:20 -05:00
OutPassParameters . HZBPageTable = CacheManager - > PrevUniformParameters . PageTable ;
OutPassParameters . HZBPageRectBounds = CacheManager - > PrevUniformParameters . PageRectBounds ;
2021-09-02 10:25:16 -04:00
OutPassParameters . HZBTexture = GraphBuilder . RegisterExternalTexture ( PrevHZBPhysical ) ;
OutPassParameters . HZBSize = PrevHZBPhysical - > GetDesc ( ) . Extent ;
OutPassParameters . HZBSampler = TStaticSamplerState < SF_Point , AM_Clamp , AM_Clamp , AM_Clamp > : : GetRHI ( ) ;
}
OutPermutationVector . Set < FVirtualSmInvalidateInstancePagesCS : : FDebugDim > ( bUseDebugPermutation ) ;
OutPermutationVector . Set < FVirtualSmInvalidateInstancePagesCS : : FUseHzbDim > ( PrevHZBPhysical ! = nullptr ) ;
2021-09-02 07:13:18 -04:00
}
2021-12-03 10:22:46 -05:00
void FVirtualShadowMapArrayCacheManager : : ProcessInvalidations ( FRDGBuilder & GraphBuilder , FInstanceGPULoadBalancer & Instances , int32 TotalInstanceCount , const FGPUScene & GPUScene )
2021-09-02 07:13:18 -04:00
{
2021-12-03 10:22:46 -05:00
if ( Instances . IsEmpty ( ) )
2021-09-02 07:13:18 -04:00
{
return ;
}
2021-03-08 23:14:54 -04:00
2021-12-03 10:22:46 -05:00
Instances . FinalizeBatches ( ) ;
2021-05-05 13:20:09 -04:00
2021-12-03 10:22:46 -05:00
RDG_EVENT_SCOPE ( GraphBuilder , " ProcessInvalidations [%d batches] " , Instances . GetBatches ( ) . Num ( ) ) ;
FVirtualSmInvalidateInstancePagesCS : : FParameters * PassParameters = GraphBuilder . AllocParameters < FVirtualSmInvalidateInstancePagesCS : : FParameters > ( ) ;
2021-08-24 06:42:43 -04:00
FVirtualSmInvalidateInstancePagesCS : : FPermutationDomain PermutationVector ;
2021-12-03 10:22:46 -05:00
SetupCommonParameters ( GraphBuilder , this , TotalInstanceCount , GPUScene , * PassParameters , PermutationVector ) ;
Instances . Upload ( GraphBuilder ) . GetShaderParameters ( GraphBuilder , PassParameters - > LoadBalancerParameters ) ;
2021-09-02 10:25:16 -04:00
2021-12-03 10:22:46 -05:00
PermutationVector . Set < FVirtualSmInvalidateInstancePagesCS : : FInputKindDim > ( FVirtualSmInvalidateInstancePagesCS : : EInputKind_LoadBalancer ) ;
2020-10-27 13:40:36 -04:00
2021-12-03 10:22:46 -05:00
auto ComputeShader = GetGlobalShaderMap ( GMaxRHIFeatureLevel ) - > GetShader < FVirtualSmInvalidateInstancePagesCS > ( PermutationVector ) ;
2020-08-25 10:06:54 -04:00
2021-12-03 10:22:46 -05:00
FComputeShaderUtils : : AddPass (
GraphBuilder ,
RDG_EVENT_NAME ( " VirtualSmInvalidateInstancePagesCS " ) ,
ComputeShader ,
PassParameters ,
Instances . GetWrappedCsGroupCount ( )
) ;
2021-01-21 13:38:01 -04:00
2020-08-25 10:06:54 -04:00
}
2021-09-02 07:13:18 -04:00
void FVirtualShadowMapArrayCacheManager : : ProcessGPUInstanceInvalidations ( FRDGBuilder & GraphBuilder , const FGPUScene & GPUScene )
{
// Dispatch CS indirectly to process instances that are marked to update from the GPU side.
2021-12-09 04:08:19 -05:00
if ( PrevBuffers . InvalidatingInstancesBuffer . IsValid ( ) )
2021-09-02 07:13:18 -04:00
{
2021-12-03 10:22:46 -05:00
RDG_EVENT_SCOPE ( GraphBuilder , " ProcessGPUInstanceInvalidations [GPU-Instances] " ) ;
2021-09-02 07:13:18 -04:00
FRDGBufferRef InvalidatingInstancesBufferRDG = GraphBuilder . RegisterExternalBuffer ( PrevBuffers . InvalidatingInstancesBuffer , TEXT ( " Shadow.Virtual.PrevInvalidatingInstancesBuffer " ) ) ;
FRDGBufferRef IndirectArgs = FComputeShaderUtils : : AddIndirectArgsSetupCsPass1D ( GraphBuilder , InvalidatingInstancesBufferRDG , TEXT ( " Shadow.Virtual.ProcessGPUInstanceInvalidationsIndirectArgs " ) , FVirtualSmInvalidateInstancePagesCS : : Cs1dGroupSizeX ) ;
2021-09-02 10:25:16 -04:00
FVirtualSmInvalidateInstancePagesCS : : FPermutationDomain PermutationVector ;
2021-09-02 07:13:18 -04:00
FVirtualSmInvalidateInstancePagesCS : : FParameters * PassParameters = GraphBuilder . AllocParameters < FVirtualSmInvalidateInstancePagesCS : : FParameters > ( ) ;
2021-09-02 10:25:16 -04:00
SetupCommonParameters ( GraphBuilder , this , 16 * 1024 , GPUScene , * PassParameters , PermutationVector ) ;
2021-09-02 07:13:18 -04:00
PassParameters - > IndirectArgs = IndirectArgs ;
PassParameters - > InvalidatingInstances = GraphBuilder . CreateSRV ( InvalidatingInstancesBufferRDG ) ;
PassParameters - > NumInvalidatingInstanceSlots = PrevBuffers . NumInvalidatingInstanceSlots ;
PermutationVector . Set < FVirtualSmInvalidateInstancePagesCS : : FInputKindDim > ( FVirtualSmInvalidateInstancePagesCS : : EInputKind_GPUInstances ) ;
auto ComputeShader = GetGlobalShaderMap ( GMaxRHIFeatureLevel ) - > GetShader < FVirtualSmInvalidateInstancePagesCS > ( PermutationVector ) ;
FComputeShaderUtils : : AddPass (
GraphBuilder ,
RDG_EVENT_NAME ( " VirtualSmInvalidateInstancePagesCS " ) ,
ComputeShader ,
PassParameters ,
IndirectArgs ,
0
) ;
2021-12-09 04:08:19 -05:00
// Drop the InvalidatingInstancesBuffer to make sure we don't redundantly process the associated invalidations if ProcessRemovedOrUpdatedPrimitives is called multiple times.
PrevBuffers . InvalidatingInstancesBuffer . SafeRelease ( ) ;
2021-09-02 07:13:18 -04:00
}
2022-05-24 13:17:13 -04:00
}