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
2022-08-30 02:21:44 -04:00
# define VSM_LOG_STATIC_CACHING 0
2022-11-02 12:29:07 -04:00
CSV_DECLARE_CATEGORY_EXTERN ( VSM ) ;
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 ) ;
2022-08-26 11:06:04 -04:00
int32 GForceInvalidateDirectionalVSM = 0 ;
static FAutoConsoleVariableRef CVarForceInvalidateDirectionalVSM (
TEXT ( " r.Shadow.Virtual.Cache.ForceInvalidateDirectional " ) ,
GForceInvalidateDirectionalVSM ,
2022-07-01 06:56:25 -04:00
TEXT ( " Forces the clipmap to always invalidate, useful to emulate a moving sun to avoid misrepresenting cache performance. " ) ,
ECVF_RenderThreadSafe ) ;
2022-02-09 15:11:16 -05:00
2022-08-30 02:21:44 -04:00
2020-12-16 17:57:13 -04:00
void FVirtualShadowMapCacheEntry : : UpdateClipmap (
int32 VirtualShadowMapId ,
const FMatrix & WorldToLight ,
2022-09-12 03:42:35 -04:00
FInt64Point PageSpaceLocation ,
FInt64Point ClipmapCornerOffset ,
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-07-21 07:46:36 -04:00
double ViewRadiusZ ,
const FVirtualShadowMapPerLightCacheEntry & PerLightEntry )
2020-07-06 18:58:26 -04:00
{
2022-08-26 11:06:04 -04:00
bool bCacheValid = ( CurrentVirtualShadowMapId ! = INDEX_NONE ) & & GForceInvalidateDirectionalVSM = = 0 ;
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);
}
}
2022-07-21 07:46:36 -04:00
// Not valid if it was never rendered
if ( PerLightEntry . PrevRenderedFrameNumber < 0 )
{
bCacheValid = false ;
}
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 ;
2020-07-06 18:58:26 -04:00
CurrentVirtualShadowMapId = VirtualShadowMapId ;
CurrentPageSpaceLocation = PageSpaceLocation ;
2022-09-12 03:42:35 -04:00
Clipmap . PrevClipmapCornerOffset = Clipmap . CurrentClipmapCornerOffset ;
Clipmap . CurrentClipmapCornerOffset = ClipmapCornerOffset ;
2020-07-06 18:58:26 -04:00
}
2022-07-21 07:46:36 -04:00
void FVirtualShadowMapCacheEntry : : UpdateLocal ( int32 VirtualShadowMapId , const FVirtualShadowMapPerLightCacheEntry & PerLightEntry )
2020-07-06 18:58:26 -04:00
{
// Swap previous frame data over.
2022-09-12 03:42:35 -04:00
PrevPageSpaceLocation = FInt64Point ( 0 , 0 ) ; // Not used for local lights
2020-07-06 18:58:26 -04:00
PrevVirtualShadowMapId = CurrentVirtualShadowMapId ;
2022-07-21 07:46:36 -04:00
// Not valid if it was never rendered
if ( PerLightEntry . PrevRenderedFrameNumber < 0 )
2020-07-06 18:58:26 -04:00
{
2020-12-16 17:57:13 -04:00
PrevVirtualShadowMapId = INDEX_NONE ;
2020-07-06 18:58:26 -04:00
}
2022-08-26 11:06:04 -04:00
// Invalidate on transition from distant to full
if ( ! PerLightEntry . bCurrentIsDistantLight & & PerLightEntry . bPrevIsDistantLight )
{
PrevVirtualShadowMapId = INDEX_NONE ;
}
2020-07-06 18:58:26 -04:00
CurrentVirtualShadowMapId = VirtualShadowMapId ;
2022-09-12 03:42:35 -04:00
CurrentPageSpaceLocation = FInt64Point ( 0 , 0 ) ; // Not used for local lights
2022-07-21 07:46:36 -04:00
}
void FVirtualShadowMapCacheEntry : : Invalidate ( )
{
PrevVirtualShadowMapId = INDEX_NONE ;
}
void FVirtualShadowMapPerLightCacheEntry : : UpdateClipmap ( )
{
PrevRenderedFrameNumber = FMath : : Max ( PrevRenderedFrameNumber , CurrentRenderedFrameNumber ) ;
CurrentRenderedFrameNumber = - 1 ;
}
void FVirtualShadowMapPerLightCacheEntry : : UpdateLocal ( const FProjectedShadowInitializer & InCacheKey , bool bIsDistantLight )
{
bPrevIsDistantLight = bCurrentIsDistantLight ;
PrevRenderedFrameNumber = FMath : : Max ( PrevRenderedFrameNumber , CurrentRenderedFrameNumber ) ;
PrevScheduledFrameNumber = FMath : : Max ( PrevScheduledFrameNumber , CurrenScheduledFrameNumber ) ;
// Check cache validity based of shadow setup
if ( ! LocalCacheKey . IsCachedShadowValid ( InCacheKey ) )
{
// If it is a distant light, we want to let the time-share perform the invalidation.
// TODO: track invalidation state somehow for later.
if ( ! bIsDistantLight )
{
PrevRenderedFrameNumber = - 1 ;
}
//UE_LOG(LogRenderer, Display, TEXT("Invalidated!"));
}
LocalCacheKey = InCacheKey ;
bCurrentIsDistantLight = bIsDistantLight ;
CurrentRenderedFrameNumber = - 1 ;
CurrenScheduledFrameNumber = - 1 ;
}
void FVirtualShadowMapPerLightCacheEntry : : Invalidate ( )
{
PrevRenderedFrameNumber = - 1 ;
for ( TSharedPtr < FVirtualShadowMapCacheEntry > & Entry : ShadowMapEntries )
{
Entry - > Invalidate ( ) ;
}
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 )
2022-08-30 02:21:44 -04:00
: Scene ( * InVirtualShadowMapArrayCacheManager - > Scene )
2022-05-05 14:02:57 -04:00
, GPUScene ( InVirtualShadowMapArrayCacheManager - > Scene - > GPUScene )
, Manager ( * InVirtualShadowMapArrayCacheManager )
2022-08-30 02:21:44 -04:00
, AlreadyAddedPrimitives ( false , InVirtualShadowMapArrayCacheManager - > Scene - > Primitives . Num ( ) )
2022-05-05 14:02:57 -04:00
{
// Add and clear pending invalidations enqueued on the GPU Scene from dynamic primitives added since last invalidation
for ( const FGPUScene : : FInstanceRange & Range : GPUScene . DynamicPrimitiveInstancesToInvalidate )
{
2022-08-30 02:21:44 -04:00
// Dynamic primitives are never cached as static; see FUploadDataSourceAdapterDynamicPrimitives::GetPrimitiveInfo
LoadBalancer . Add ( Range . InstanceSceneDataOffset , Range . NumInstanceSceneDataEntries , EncodeInstanceInvalidationPayload ( false ) ) ;
2022-05-05 14:02:57 -04:00
# 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 ( ) )
{
LoadBalancer . Add ( Range . InstanceSceneDataOffset , Range . NumInstanceSceneDataEntries ,
2022-08-30 02:21:44 -04:00
EncodeInstanceInvalidationPayload ( Range . bInvalidateStaticPage , SmCacheEntry - > CurrentVirtualShadowMapId ) ) ;
2022-05-05 14:02:57 -04:00
}
}
# if VSM_LOG_INVALIDATIONS
RangesStr . Appendf ( TEXT ( " [%6d, %6d), " ) , Range . InstanceSceneDataOffset , Range . InstanceSceneDataOffset + Range . NumInstanceSceneDataEntries ) ;
# endif
TotalInstanceCount + = Range . NumInstanceSceneDataEntries ;
}
CacheEntry . Value - > PrimitiveInstancesToInvalidate . Reset ( ) ;
}
2022-08-30 02:21:44 -04:00
// Process any GPU readback static invalidations
{
FVirtualShadowMapFeedback : : FReadbackInfo Readback = Manager . StaticGPUInvalidationsFeedback . GetLatestReadbackBuffer ( ) ;
if ( Readback . Size > 0 )
{
TBitArray < SceneRenderingAllocator > Primitives ;
{
const uint32 * Data = ( const uint32 * ) Readback . Buffer - > Lock ( Readback . Size ) ;
Primitives . AddRange ( Data , Readback . Size * 8 ) ;
Readback . Buffer - > Unlock ( ) ;
// TODO: Mark that we've done this buffer? Not really harmful to redo it
}
2022-09-16 12:44:19 -04:00
for ( TConstSetBitIterator < SceneRenderingAllocator > PrimitivesIt ( Primitives ) ; PrimitivesIt ; + + PrimitivesIt )
2022-08-30 02:21:44 -04:00
{
const FPersistentPrimitiveIndex PersistentPrimitiveIndex = FPersistentPrimitiveIndex { PrimitivesIt . GetIndex ( ) } ;
// NOTE: Have to be a bit careful as this primitive index came from a few frames ago... thus it may no longer
// be valid, or possibly even replaced by a new primitive.
// TODO: Dual iterator and avoid any primitives that have been removed recently (might resuse the slot)
const int32 PrimitiveIndex = Scene . GetPrimitiveIndex ( PersistentPrimitiveIndex ) ;
FPrimitiveSceneInfo * PrimitiveSceneInfo = Scene . GetPrimitiveSceneInfo ( PrimitiveIndex ) ;
if ( PrimitiveSceneInfo )
{
if ( Manager . WasRecentlyRemoved ( PersistentPrimitiveIndex ) )
{
// Do nothing for now, as this slot may have been reused after a previous removal
# if VSM_LOG_STATIC_CACHING
UE_LOG ( LogRenderer , Warning , TEXT ( " Ignoring GPU invalidation due to recent primitive removal: %u " ) , PersistentPrimitiveIndex . Index ) ;
# endif
}
else
{
# if VSM_LOG_STATIC_CACHING
UE_LOG ( LogRenderer , Warning , TEXT ( " Transitioning GPU invalidation to dynamic caching: %u " ) , PersistentPrimitiveIndex . Index ) ;
# endif
AddInvalidation ( PrimitiveSceneInfo , false ) ;
}
}
}
}
}
2022-05-05 14:02:57 -04:00
}
2022-08-30 02:21:44 -04:00
void FVirtualShadowMapArrayCacheManager : : FInvalidatingPrimitiveCollector : : AddInvalidation ( FPrimitiveSceneInfo * PrimitiveSceneInfo , bool bRemovedPrimitive )
2022-01-18 08:51:35 -05:00
{
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 ;
2022-08-30 02:21:44 -04:00
const FPersistentPrimitiveIndex PersistentPrimitiveIndex = PrimitiveSceneInfo - > GetPersistentIndex ( ) ;
2022-01-18 08:51:35 -05:00
// 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-08-30 02:21:44 -04:00
const bool bPreviouslyCachedAsStatic = PrimitiveSceneInfo - > ShouldCacheShadowAsStatic ( ) ;
if ( bRemovedPrimitive )
{
RemovedPrimitives . PadToNum ( PersistentPrimitiveIndex . Index + 1 , false ) ;
RemovedPrimitives [ PersistentPrimitiveIndex . Index ] = true ;
}
else
{
// Swap to dynamic caching if it was already static
if ( bPreviouslyCachedAsStatic )
{
PrimitiveSceneInfo - > SetCacheShadowAsStatic ( false ) ;
# if VSM_LOG_STATIC_CACHING
UE_LOG ( LogRenderer , Warning , TEXT ( " FVirtualShadowMapArrayCacheManager: '%s' switched to dynamic caching " ) , * PrimitiveSceneInfo - > GetFullnameForDebuggingOnly ( ) ) ;
# endif
}
}
2022-04-14 19:28:00 -04:00
const int32 NumInstanceSceneDataEntries = PrimitiveSceneInfo - > GetNumInstanceSceneDataEntries ( ) ;
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 ;
2022-08-30 02:21:44 -04:00
if ( bIsNaniteMesh | | ( PersistentPrimitiveIndex . Index < CachedPrimitives . Num ( ) & & CachedPrimitives [ PersistentPrimitiveIndex . Index ] ) )
2022-01-18 08:51:35 -05:00
{
if ( ! bIsNaniteMesh )
{
// Clear the record as we're wiping it out.
2022-08-30 02:21:44 -04:00
CachedPrimitives [ PersistentPrimitiveIndex . Index ] = 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 ,
2022-08-30 02:21:44 -04:00
EncodeInstanceInvalidationPayload ( bPreviouslyCachedAsStatic , 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 ;
}
}
2022-08-30 02:21:44 -04:00
FVirtualShadowMapFeedback : : FVirtualShadowMapFeedback ( )
{
for ( int32 i = 0 ; i < MaxBuffers ; + + i )
{
Buffers [ i ] . Buffer = new FRHIGPUBufferReadback ( TEXT ( " Shadow.Virtual.Readback " ) ) ;
Buffers [ i ] . Size = 0 ;
}
}
FVirtualShadowMapFeedback : : ~ FVirtualShadowMapFeedback ( )
{
for ( int32 i = 0 ; i < MaxBuffers ; + + i )
{
delete Buffers [ i ] . Buffer ;
Buffers [ i ] . Buffer = nullptr ;
Buffers [ i ] . Size = 0 ;
}
}
void FVirtualShadowMapFeedback : : SubmitFeedbackBuffer (
FRDGBuilder & GraphBuilder ,
FRDGBufferRef FeedbackBuffer )
{
// Source copy usage is required for readback
check ( ( FeedbackBuffer - > Desc . Usage & EBufferUsageFlags : : SourceCopy ) = = EBufferUsageFlags : : SourceCopy ) ;
if ( NumPending = = MaxBuffers )
{
# if VSM_LOG_STATIC_CACHING
UE_LOG ( LogRenderer , Warning , TEXT ( " FVirtualShadowMapFeedback ran out of feedback buffers! " ) ) ;
# endif
return ;
}
FRHIGPUBufferReadback * ReadbackBuffer = Buffers [ WriteIndex ] . Buffer ;
Buffers [ WriteIndex ] . Size = FeedbackBuffer - > Desc . GetSize ( ) ;
AddReadbackBufferPass ( GraphBuilder , RDG_EVENT_NAME ( " Readback " ) , FeedbackBuffer ,
[ ReadbackBuffer , FeedbackBuffer ] ( FRHICommandList & RHICmdList )
{
ReadbackBuffer - > EnqueueCopy ( RHICmdList , FeedbackBuffer - > GetRHI ( ) , 0u ) ;
} ) ;
WriteIndex = ( WriteIndex + 1 ) % MaxBuffers ;
NumPending = FMath : : Min ( NumPending + 1 , MaxBuffers ) ;
}
FVirtualShadowMapFeedback : : FReadbackInfo FVirtualShadowMapFeedback : : GetLatestReadbackBuffer ( )
{
int32 LatestBufferIndex = - 1 ;
// Find latest buffer that is ready
while ( NumPending > 0 )
{
uint32 Index = ( WriteIndex + MaxBuffers - NumPending ) % MaxBuffers ;
if ( Buffers [ Index ] . Buffer - > IsReady ( ) )
{
- - NumPending ;
LatestBufferIndex = Index ;
}
else
{
break ;
}
}
return LatestBufferIndex > = 0 ? Buffers [ LatestBufferIndex ] : FReadbackInfo ( ) ;
}
2022-01-18 08:51:35 -05:00
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-11-02 12:29:07 -04:00
// Goes negative on underflow
int32 NumPagesFree = Message . Read < int32 > ( 0 ) ;
CSV_CUSTOM_STAT ( VSM , FreePages , NumPagesFree , ECsvCustomStatOp : : Set ) ;
if ( NumPagesFree < 0 )
2021-12-03 16:08:27 -05:00
{
2022-11-02 12:29:07 -04:00
static const auto * CVarResolutionLodBiasLocalPtr = IConsoleManager : : Get ( ) . FindTConsoleVariableDataFloat ( TEXT ( " r.Shadow.Virtual.ResolutionLodBiasLocal " ) ) ;
const float LodBiasLocal = CVarResolutionLodBiasLocalPtr - > GetValueOnRenderThread ( ) ;
2021-12-03 16:08:27 -05:00
2022-11-02 12:29:07 -04:00
static const auto * CVarResolutionLodBiasDirectionalPtr = IConsoleManager : : Get ( ) . FindTConsoleVariableDataFloat ( TEXT ( " r.Shadow.Virtual.ResolutionLodBiasDirectional " ) ) ;
const float LodBiasDirectional = CVarResolutionLodBiasDirectionalPtr - > GetValueOnRenderThread ( ) ;
2022-05-24 13:17:13 -04:00
2022-11-02 12:29:07 -04:00
static const auto * CVarMaxPhysicalPagesPtr = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.Shadow.Virtual.MaxPhysicalPages " ) ) ;
const int32 MaxPhysicalPages = CVarMaxPhysicalPagesPtr - > GetValueOnRenderThread ( ) ;
2022-05-24 13:17:13 -04:00
2022-09-19 04:15:20 -04:00
static const auto * CVarMaxPhysicalPagesSceneCapturePtr = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.Shadow.Virtual.MaxPhysicalPagesSceneCapture " ) ) ;
const int32 MaxPhysicalPagesSceneCapture = CVarMaxPhysicalPagesSceneCapturePtr - > GetValueOnRenderThread ( ) ;
2021-12-03 16:08:27 -05:00
# if !UE_BUILD_SHIPPING
2022-11-02 12:29:07 -04:00
if ( ! bLoggedPageOverflow )
2022-05-24 13:17:13 -04:00
{
2022-11-02 12:29:07 -04:00
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.MaxPhysicalPagesSceneCapture (%d), r.Shadow.Virtual.ResolutionLodBiasLocal (%.2f), and r.Shadow.Virtual.ResolutionLodBiasDirectional (%.2f) " ) ,
- NumPagesFree ,
MaxPhysicalPages ,
MaxPhysicalPagesSceneCapture ,
LodBiasLocal ,
LodBiasDirectional ) ;
bLoggedPageOverflow = true ;
2022-05-24 13:17:13 -04:00
}
2022-11-02 12:29:07 -04:00
LastOverflowTime = float ( FGameTime : : GetTimeSinceAppStart ( ) . GetRealTimeSeconds ( ) ) ;
2022-05-24 13:17:13 -04:00
# endif
}
2022-11-02 12:29:07 -04:00
# if !UE_BUILD_SHIPPING
else
{
bLoggedPageOverflow = false ;
}
# endif
2021-12-03 16:08:27 -05:00
} ) ;
2022-10-28 11:39:21 -04:00
# if !UE_BUILD_SHIPPING
// Handle message with stats sent back from GPU whenever stats are enabled
StatsFeedbackSocket = GPUMessage : : RegisterHandler ( TEXT ( " Shadow.Virtual.StatsFeedback " ) , [ this ] ( GPUMessage : : FReader Message )
{
2022-11-02 12:29:07 -04:00
// Culling stats
int32 NaniteNumTris = Message . Read < int32 > ( 0 ) ;
int32 NanitePostCullNodeCount = Message . Read < int32 > ( 0 ) ;
int32 NonNanitePostCullInstanceCount = Message . Read < int32 > ( 0 ) ;
CSV_CUSTOM_STAT ( VSM , NaniteNumTris , NaniteNumTris , ECsvCustomStatOp : : Set ) ;
CSV_CUSTOM_STAT ( VSM , NanitePostCullNodeCount , NanitePostCullNodeCount , ECsvCustomStatOp : : Set ) ;
CSV_CUSTOM_STAT ( VSM , NonNanitePostCullInstanceCount , NonNanitePostCullInstanceCount , ECsvCustomStatOp : : Set ) ;
// Large page area items
2022-10-28 11:39:21 -04:00
LastLoggedPageOverlapAppTime . SetNumZeroed ( Scene - > GetMaxPersistentPrimitiveIndex ( ) ) ;
float RealTimeSeconds = float ( FGameTime : : GetTimeSinceAppStart ( ) . GetRealTimeSeconds ( ) ) ;
2022-11-02 12:29:07 -04:00
TConstArrayView < uint32 > PageAreaDiags = Message . ReadCount ( FVirtualShadowMapArray : : MaxPageAreaDiagnosticSlots * 2 ) ;
2022-10-28 11:39:21 -04:00
for ( int32 Index = 0 ; Index < PageAreaDiags . Num ( ) ; Index + = 2 )
{
uint32 Overlap = PageAreaDiags [ Index ] ;
uint32 PersistentPrimitiveId = PageAreaDiags [ Index + 1 ] ;
int32 PrimtiveIndex = Scene - > GetPrimitiveIndex ( FPersistentPrimitiveIndex { int32 ( PersistentPrimitiveId ) } ) ;
if ( Overlap > 0 & & PrimtiveIndex ! = INDEX_NONE )
{
if ( RealTimeSeconds - LastLoggedPageOverlapAppTime [ PersistentPrimitiveId ] > 5.0f )
{
LastLoggedPageOverlapAppTime [ PersistentPrimitiveId ] = RealTimeSeconds ;
2022-11-21 07:56:54 -05:00
UE_LOG ( LogRenderer , Warning , TEXT ( " Non-Nanite VSM page overlap performance Warning, %d, %s, %s " ) , Overlap , * Scene - > Primitives [ PrimtiveIndex ] - > GetOwnerActorNameOrLabelForDebuggingOnly ( ) , * Scene - > Primitives [ PrimtiveIndex ] - > GetFullnameForDebuggingOnly ( ) ) ;
2022-10-28 11:39:21 -04:00
}
LargePageAreaItems . Add ( PersistentPrimitiveId , FLargePageAreaItem { Overlap , RealTimeSeconds } ) ;
}
}
} ) ;
# 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
{
2022-10-28 11:39:21 -04:00
float RealTimeSeconds = float ( FGameTime : : GetTimeSinceAppStart ( ) . GetRealTimeSeconds ( ) ) ;
2021-12-03 16:08:27 -05:00
// Show for ~5s after last overflow
int32 CurrentFrameNumber = Scene - > GetFrameNumber ( ) ;
2022-10-28 11:39:21 -04:00
if ( LastOverflowTime > = 0.0f & & RealTimeSeconds - LastOverflowTime < 5.0f )
2021-12-03 16:08:27 -05:00
{
2022-10-28 11:39:21 -04:00
OutMessages . Add ( FCoreDelegates : : EOnScreenMessageSeverity : : Warning , FText : : FromString ( FString : : Printf ( TEXT ( " Virtual Shadow Map Page Pool overflow detected (%0.0f seconds ago) " ) , RealTimeSeconds - LastOverflowTime ) ) ) ;
2021-12-03 16:08:27 -05:00
}
2022-10-28 11:39:21 -04:00
for ( const auto & Item : LargePageAreaItems )
{
int32 PrimtiveIndex = Scene - > GetPrimitiveIndex ( FPersistentPrimitiveIndex { int32 ( Item . Key ) } ) ;
uint32 Overlap = Item . Value . PageArea ;
if ( PrimtiveIndex ! = INDEX_NONE & & RealTimeSeconds - Item . Value . LastTimeSeen < 2.5f )
{
OutMessages . Add ( FCoreDelegates : : EOnScreenMessageSeverity : : Warning , FText : : FromString ( FString : : Printf ( TEXT ( " Non-Nanite VSM page overlap performance Warning: Primitive '%s' overlapped %d Pages " ) , * Scene - > Primitives [ PrimtiveIndex ] - > GetOwnerActorNameOrLabelForDebuggingOnly ( ) , Overlap ) ) ) ;
}
}
TrimLoggingInfo ( ) ;
2021-12-03 16:08:27 -05:00
} ) ;
# 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 ;
}
2022-07-21 12:59:02 -04:00
TSharedPtr < FVirtualShadowMapPerLightCacheEntry > FVirtualShadowMapArrayCacheManager : : FindCreateLightCacheEntry ( int32 LightSceneId , uint32 ViewUniqueID )
2022-01-18 08:51:35 -05:00
{
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) = = 0 )
{
return nullptr ;
}
2022-07-21 12:59:02 -04:00
const uint64 CacheKey = ( uint64 ( ViewUniqueID ) < < 32U ) | uint64 ( LightSceneId ) ;
if ( TSharedPtr < FVirtualShadowMapPerLightCacheEntry > * LightEntry = CacheEntries . Find ( CacheKey ) )
2022-01-18 08:51:35 -05:00
{
return * LightEntry ;
}
// Add to current frame / active set.
2022-07-21 12:59:02 -04:00
TSharedPtr < FVirtualShadowMapPerLightCacheEntry > & NewLightEntry = CacheEntries . Add ( CacheKey ) ;
2022-01-18 08:51:35 -05:00
// Copy data if available
2022-07-21 12:59:02 -04:00
if ( TSharedPtr < FVirtualShadowMapPerLightCacheEntry > * PrevNewLightEntry = PrevCacheEntries . Find ( CacheKey ) )
2022-01-18 08:51:35 -05:00
{
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 ;
}
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 ( ) )
{
2022-08-30 02:21:44 -04:00
PrimitiveInstancesToInvalidate . Add ( FInstanceRange {
PrimitiveSceneInfo - > GetInstanceSceneDataOffset ( ) ,
PrimitiveSceneInfo - > GetNumInstanceSceneDataEntries ( ) ,
PrimitiveSceneInfo - > ShouldCacheShadowAsStatic ( )
} ) ;
2022-02-09 15:11:16 -05:00
}
}
}
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 ) & &
2022-10-04 13:52:45 -04:00
DoesPlatformSupportVirtualShadowMaps ( 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-10-28 11:39:21 -04:00
TrimLoggingInfo ( ) ;
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 ( ) ;
2022-08-26 11:06:04 -04:00
PrevUniformParameters . NumFullShadowMaps = 0 ;
PrevUniformParameters . NumSinglePageShadowMaps = 0 ;
PrevUniformParameters . NumShadowMapSlots = 0 ;
2022-04-04 18:25:43 -04:00
}
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 ) ;
2022-02-02 02:18:54 -05:00
GraphBuilder . QueueBufferExtraction ( VirtualShadowMapArray . ProjectionDataRDG , & PrevBuffers . ProjectionData ) ;
2021-09-02 07:13:18 -04:00
2022-09-12 18:06:57 -04:00
// Enqueue readback
StaticGPUInvalidationsFeedback . SubmitFeedbackBuffer ( GraphBuilder , VirtualShadowMapArray . StaticInvalidatingPrimitivesRDG ) ;
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
}
2022-07-21 07:46:36 -04:00
// Move cache entries to previous frame, this implicitly removes any that were not used
PrevCacheEntries = CacheEntries ;
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
2022-06-07 13:19:54 -04:00
for ( const auto & LightInfo : SceneRenderer . 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-07-21 07:46:36 -04:00
// Caching is disabled
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) = = 0 )
{
// Make sure we empty out any resources associated with caching
PrevCacheEntries . Reset ( ) ;
CacheEntries . Reset ( ) ;
}
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
}
2022-08-30 02:21:44 -04:00
// Every once in a while zero out our recently removed primitive flags. This lets us ignore slots that
// may have been reused since they were flagged from GPU invalidations.
+ + RecentlyRemovedFrameCounter ;
if ( RecentlyRemovedFrameCounter > = 3 )
{
RecentlyRemovedPrimitives [ RecentlyRemovedReadIndex ] . Reset ( ) ;
RecentlyRemovedReadIndex = 1 - RecentlyRemovedReadIndex ;
RecentlyRemovedFrameCounter = 0 ;
}
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 ;
2022-08-18 15:29:29 -04:00
auto ComputeShader = GetGlobalShaderMap ( Scene - > GetFeatureLevel ( ) ) - > GetShader < FVirtualSmCopyStatsCS > ( ) ;
2020-07-06 18:58:26 -04:00
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
2022-09-12 18:06:57 -04:00
& & PrevBuffers . PhysicalPageMetaData ;
2020-07-06 18:58:26 -04:00
}
bool FVirtualShadowMapArrayCacheManager : : IsAccumulatingStats ( )
{
return CVarAccumulateStats . GetValueOnRenderThread ( ) ! = 0 ;
}
2020-08-25 10:06:54 -04:00
2022-08-30 02:21:44 -04:00
static uint32 GetPrimFlagsBufferSizeInDwords ( int32 MaxPersistentPrimitiveIndex )
{
return FMath : : RoundUpToPowerOfTwo ( FMath : : DivideAndRoundUp ( MaxPersistentPrimitiveIndex , 32 ) ) ;
}
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
{
2022-08-30 02:21:44 -04:00
// Always incorporate any scene removals into the "recently removed" list
UpdateRecentlyRemoved ( InvalidatingPrimitiveCollector . GetRemovedPrimitives ( ) ) ;
2022-09-12 18:06:57 -04:00
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) ! = 0 & & PrevBuffers . PhysicalPageMetaData . 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
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-08-30 02:21:44 -04:00
static void ResizeFlagArray ( TBitArray < > & BitArray , int32 NewMax , bool DefaultValue = false )
2022-01-18 08:51:35 -05:00
{
if ( BitArray . Num ( ) > NewMax )
{
// Trim off excess items
BitArray . SetNumUninitialized ( NewMax ) ;
}
else if ( BitArray . Num ( ) < NewMax )
{
2022-08-30 02:21:44 -04:00
BitArray . Add ( DefaultValue , NewMax - BitArray . Num ( ) ) ;
2022-01-18 08:51:35 -05:00
}
}
void FVirtualShadowMapArrayCacheManager : : OnSceneChange ( )
{
if ( CVarCacheVirtualSMs . GetValueOnRenderThread ( ) ! = 0 )
{
2022-08-30 02:21:44 -04:00
const int32 MaxPersistentPrimitiveIndex = FMath : : Max ( 1 , Scene - > GetMaxPersistentPrimitiveIndex ( ) ) ;
2022-01-18 08:51:35 -05:00
for ( auto & CacheEntry : PrevCacheEntries )
{
2022-08-30 02:21:44 -04:00
ResizeFlagArray ( CacheEntry . Value - > CachedPrimitives , MaxPersistentPrimitiveIndex ) ;
ResizeFlagArray ( CacheEntry . Value - > RenderedPrimitives , MaxPersistentPrimitiveIndex ) ;
2022-01-18 08:51:35 -05:00
}
2022-01-19 07:04:51 -05:00
for ( auto & CacheEntry : CacheEntries )
{
2022-08-30 02:21:44 -04:00
ResizeFlagArray ( CacheEntry . Value - > CachedPrimitives , MaxPersistentPrimitiveIndex ) ;
ResizeFlagArray ( CacheEntry . Value - > RenderedPrimitives , MaxPersistentPrimitiveIndex ) ;
2022-01-19 07:04:51 -05:00
}
2022-01-18 08:51:35 -05:00
}
}
2022-07-21 07:46:36 -04:00
void FVirtualShadowMapArrayCacheManager : : OnLightRemoved ( int32 LightId )
{
CacheEntries . Remove ( LightId ) ;
PrevCacheEntries . Remove ( LightId ) ;
}
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-08-24 06:42:43 -04:00
class FDebugDim : SHADER_PERMUTATION_BOOL ( " ENABLE_DEBUG_MODE " ) ;
2021-09-02 10:25:16 -04:00
class FUseHzbDim : SHADER_PERMUTATION_BOOL ( " USE_HZB_OCCLUSION " ) ;
2022-09-12 18:06:57 -04:00
using FPermutationDomain = TShaderPermutationDomain < FUseHzbDim , FDebugDim > ;
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 )
2022-09-12 18:06:57 -04:00
SHADER_PARAMETER_RDG_BUFFER_UAV ( RWStructuredBuffer < FPhysicalPageMetaData > , PrevPhysicalPageMetaDataOut )
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
2022-08-30 02:21:44 -04:00
// GPU instances parameters
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 )
2022-08-30 02:21:44 -04:00
SHADER_PARAMETER_RDG_BUFFER_UAV ( RWStructuredBuffer < uint > , OutStaticInvalidatingPrimitives )
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 ) & &
2022-10-04 13:52:45 -04:00
DoesPlatformSupportVirtualShadowMaps ( 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-12-03 10:22:46 -05:00
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
}
}
2022-07-08 15:10:05 -04:00
# if WITH_MGPU
void FVirtualShadowMapArrayCacheManager : : UpdateGPUMask ( FRHIGPUMask GPUMask )
{
if ( LastGPUMask ! = GPUMask )
{
LastGPUMask = GPUMask ;
Invalidate ( ) ;
}
}
# endif // WITH_MGPU
2022-08-30 02:21:44 -04:00
static void SetupCommonParameters (
FRDGBuilder & GraphBuilder ,
FVirtualShadowMapArrayCacheManager * CacheManager ,
int32 TotalInstanceCount ,
const FGPUScene & GPUScene ,
2021-09-02 10:25:16 -04:00
FVirtualSmInvalidateInstancePagesCS : : FParameters & OutPassParameters ,
FVirtualSmInvalidateInstancePagesCS : : FPermutationDomain & OutPermutationVector )
2020-08-25 10:06:54 -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 ) ;
2022-09-12 18:06:57 -04:00
OutPassParameters . PrevPhysicalPageMetaDataOut = GraphBuilder . CreateUAV ( GraphBuilder . RegisterExternalBuffer ( PrevBuffers . PhysicalPageMetaData ) ) ;
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
2022-08-30 02:21:44 -04: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
2022-08-18 15:29:29 -04:00
auto ComputeShader = GetGlobalShaderMap ( Scene - > GetFeatureLevel ( ) ) - > 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
}
2022-10-28 11:39:21 -04:00
// Remove old info used to track logging.
void FVirtualShadowMapArrayCacheManager : : TrimLoggingInfo ( )
{
# if !UE_BUILD_SHIPPING
// Remove old items
float RealTimeSeconds = float ( FGameTime : : GetTimeSinceAppStart ( ) . GetRealTimeSeconds ( ) ) ;
LargePageAreaItems = LargePageAreaItems . FilterByPredicate ( [ RealTimeSeconds ] ( const TMap < uint32 , FLargePageAreaItem > : : ElementType & Element )
{
return RealTimeSeconds - Element . Value . LastTimeSeen < 5.0f ;
} ) ;
# endif
}