Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapCacheManager.h
andrew lauritzen 70a2837739 Move static separate cache to second texture array slice rather than "below" in UV space:
- Avoid gotchas with max texture size when static separate enabled
- Simplify addressing logic in a number of places
- Avoid allocating extra HZB that we never use

Details:
- Support rendering/sampling to 2D depth texture array in Nanite and virtual shadow map pass
- Remove some unnecessary HZB-related cvars
- Remove unused permutations from VSM HW raster

#preflight 624f4e5611261bc7b2171208
#rb jamie.hayes

#ROBOMERGE-AUTHOR: andrew.lauritzen
#ROBOMERGE-SOURCE: CL 19679616 via CL 19679656 via CL 19679706
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v938-19570697)

[CL 19680680 by andrew lauritzen in ue5-main branch]
2022-04-07 18:36:13 -04:00

242 lines
8.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VirtualShadowMapArray.h:
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "VirtualShadowMapArray.h"
#include "SceneManagement.h"
#include "InstanceCulling/InstanceCullingLoadBalancer.h"
#include "GPUScene.h"
#include "GPUMessaging.h"
class FRHIGPUBufferReadback;
class FGPUScene;
#define VSM_LOG_INVALIDATIONS 0
class FVirtualShadowMapCacheEntry
{
public:
// True if the cache has been (re)populated, set to false on init and set to true once the cache update process has happened.
// Also set to false whenever key data was not valid and all cached data is invalidated.
bool IsValid() { return PrevVirtualShadowMapId != INDEX_NONE && bPrevRendered; }
void UpdateLocal(int32 VirtualShadowMapId,
const FWholeSceneProjectedShadowInitializer &InCacheValidKey);
void UpdateClipmap(int32 VirtualShadowMapId,
const FMatrix &WorldToLight,
FIntPoint PageSpaceLocation,
double LevelRadius,
double ViewCenterZ,
double ViewRadiusZ);
void MarkRendered() { bCurrentRendered = true; }
// Previous frame data
FIntPoint PrevPageSpaceLocation = FIntPoint(0, 0);
int32 PrevVirtualShadowMapId = INDEX_NONE;
bool bPrevRendered = false;
// Current frame data
FIntPoint CurrentPageSpaceLocation = FIntPoint(0, 0);
int32 CurrentVirtualShadowMapId = INDEX_NONE;
bool bCurrentRendered = false;
// TODO: Potentially refactor this to decouple the cache key details
FWholeSceneProjectedShadowInitializer LocalCacheValidKey;
struct FClipmapInfo
{
FMatrix WorldToLight;
double ViewCenterZ;
double ViewRadiusZ;
};
FClipmapInfo Clipmap;
};
class FVirtualShadowMapPerLightCacheEntry
{
public:
FVirtualShadowMapPerLightCacheEntry(int32 MaxPersistentScenePrimitiveIndex)
: RenderedPrimitives(false, MaxPersistentScenePrimitiveIndex)
, CachedPrimitives(false, MaxPersistentScenePrimitiveIndex)
{
}
TSharedPtr<FVirtualShadowMapCacheEntry> FindCreateShadowMapEntry(int32 Index);
void OnPrimitiveRendered(const FPrimitiveSceneInfo* PrimitiveSceneInfo);
// Primitives that have been rendered (not culled) the previous frame, when a primitive transitions from being culled to not it must be rendered into the VSM
// Key culling reasons are small size or distance cutoff.
TBitArray<> RenderedPrimitives;
// Primitives that have been rendered (not culled) _some_ previous frame, tracked so we can invalidate when they move/are removed (and not otherwise).
TBitArray<> CachedPrimitives;
// One entry represents the cached state of a given shadow map in the set of either a clipmap(N), one cube map(6) or a regular VSM (1)
TArray< TSharedPtr<FVirtualShadowMapCacheEntry> > ShadowMapEntries;
// TODO: refactor this to not ne stored in the cache entry when we move (some) invalidaitons to the end of frame rather than in the scene primitive updates.
struct FInstanceRange
{
int32 InstanceSceneDataOffset;
int32 NumInstanceSceneDataEntries;
};
TArray<FInstanceRange> PrimitiveInstancesToInvalidate;
};
// Persistent buffers that we ping pong frame by frame
struct FVirtualShadowMapArrayFrameData
{
TRefCountPtr<FRDGPooledBuffer> PageTable;
TRefCountPtr<FRDGPooledBuffer> PageFlags;
TRefCountPtr<FRDGPooledBuffer> ProjectionData;
TRefCountPtr<FRDGPooledBuffer> PageRectBounds;
TRefCountPtr<FRDGPooledBuffer> DynamicCasterPageFlags;
TRefCountPtr<FRDGPooledBuffer> PhysicalPageMetaData;
TRefCountPtr<IPooledRenderTarget> HZBPhysical;
TMap<int32, FVirtualShadowMapHZBMetadata> HZBMetadata;
TRefCountPtr<FRDGPooledBuffer> InvalidatingInstancesBuffer;
int32 NumInvalidatingInstanceSlots = 0;
};
class FVirtualShadowMapArrayCacheManager
{
public:
FVirtualShadowMapArrayCacheManager(FScene *InScene);
~FVirtualShadowMapArrayCacheManager();
// Enough for er lots...
static constexpr uint32 MaxStatFrames = 512*1024U;
// Called by VirtualShadowMapArray to potentially resize the physical pool
// If the requested size is not already the size, all cache data is dropped and the pool is resized.
TRefCountPtr<IPooledRenderTarget> SetPhysicalPoolSize(FRDGBuilder& GraphBuilder, FIntPoint RequestedSize, int RequestedArraySize);
void FreePhysicalPool();
// Called by VirtualShadowMapArray to potentially resize the HZB physical pool
TRefCountPtr<IPooledRenderTarget> SetHZBPhysicalPoolSize(FRDGBuilder& GraphBuilder, FIntPoint RequestedSize, const EPixelFormat Format);
void FreeHZBPhysicalPool();
// Invalidate the cache for all shadows, causing any pages to be rerendered
void Invalidate();
/**
* Call at end of frame to extract resouces from the virtual SM array to preserve to next frame.
* If bCachingEnabled is false, all previous frame data is dropped and cache (and HZB!) data will not be available for the next frame.
*/
void ExtractFrameData(FRDGBuilder& GraphBuilder,
FVirtualShadowMapArray &VirtualShadowMapArray,
const FSceneRenderer& SceneRenderer,
bool bEnableCaching);
/**
* Finds an existing cache entry and moves to the active set or creates a fresh one.
*/
TSharedPtr<FVirtualShadowMapPerLightCacheEntry> FindCreateLightCacheEntry(int32 LightSceneId);
/**
* Finds an existing cache entry and moves to the active set or creates a fresh one.
*/
TSharedPtr<FVirtualShadowMapCacheEntry> FindCreateCacheEntry(int32 LightSceneId, int32 Index = 0);
/*
* Returns true if cached data is available.
*/
bool IsValid();
bool IsAccumulatingStats();
using FInstanceGPULoadBalancer = TInstanceCullingLoadBalancer<SceneRenderingAllocator>;
/**
* Helper to collect primitives that need invalidation, filters out redundant adds and also those that are not yet known to the GPU
*/
class FInvalidatingPrimitiveCollector
{
public:
FInvalidatingPrimitiveCollector(FVirtualShadowMapArrayCacheManager* InVirtualShadowMapArrayCacheManager);
/**
* Add a primitive to invalidate the instances for, the function filters redundant primitive adds, and thus expects valid IDs (so can't be called for primitives that have not yet been added)
* and unchanging IDs (so can't be used over a span that include any scene mutation).
*/
void Add(const FPrimitiveSceneInfo* PrimitiveSceneInfo);
bool IsEmpty() const { return LoadBalancer.IsEmpty(); }
TBitArray<SceneRenderingAllocator> AlreadyAddedPrimitives;
FInstanceGPULoadBalancer LoadBalancer;
int32 TotalInstanceCount = 0;
#if VSM_LOG_INVALIDATIONS
FString RangesStr;
#endif
const FScene& Scene;
FGPUScene& GPUScene;
FVirtualShadowMapArrayCacheManager& Manager;
};
/**
* This must to be executed before the instances are actually removed / updated, otherwise the wrong position will be used.
* In particular, it must be processed before the Scene primitive IDs are updated/compacted as part of the removal.
* Invalidate pages that are touched by (the instances of) the removed primitives.
*/
void ProcessRemovedOrUpdatedPrimitives(FRDGBuilder& GraphBuilder, const FGPUScene& GPUScene, FInvalidatingPrimitiveCollector& InvalidatingPrimitiveCollector);
/**
* Allow the cache manager to track scene changes, in particular track resizing of primitive tracking data.
*/
void OnSceneChange();
TRDGUniformBufferRef<FVirtualShadowMapUniformParameters> GetPreviousUniformBuffer(FRDGBuilder& GraphBuilder) const;
FVirtualShadowMapArrayFrameData PrevBuffers;
FVirtualShadowMapUniformParameters PrevUniformParameters;
void SetHZBViewParams(int32 HZBKey, Nanite::FPackedViewParams& OutParams);
GPUMessage::FSocket StatusFeedbackSocket;
private:
void ProcessInvalidations(FRDGBuilder& GraphBuilder, FInstanceGPULoadBalancer& Instances, int32 TotalInstanceCount, const FGPUScene& GPUScene);
void ProcessGPUInstanceInvalidations(FRDGBuilder& GraphBuilder, const FGPUScene& GPUScene);
void ExtractStats(FRDGBuilder& GraphBuilder, FVirtualShadowMapArray &VirtualShadowMapArray);
// The actual physical texture data is stored here rather than in VirtualShadowMapArray (which is recreated each frame)
// This allows us to (optionally) persist cached pages between frames. Regardless of whether caching is enabled,
// we store the physical pool here.
TRefCountPtr<IPooledRenderTarget> PhysicalPagePool;
TRefCountPtr<IPooledRenderTarget> HZBPhysicalPagePool;
// Index the Cache entries by the light ID
TMap< int32, TSharedPtr<FVirtualShadowMapPerLightCacheEntry> > CacheEntries;
TMap< int32, TSharedPtr<FVirtualShadowMapPerLightCacheEntry> > PrevCacheEntries;
// Stores stats over frames when activated.
TRefCountPtr<FRDGPooledBuffer> AccumulatedStatsBuffer;
bool bAccumulatingStats = false;
FRHIGPUBufferReadback* GPUBufferReadback = nullptr;
#if !UE_BUILD_SHIPPING
FDelegateHandle ScreenMessageDelegate;
int32 LastOverflowFrame = -1;
bool bLoggedPageOverflow = false;
#endif
FScene* Scene;
};