Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapArray.h
jason hoerner b19bb6be2f 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

350 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VirtualShadowMapArray.h:
=============================================================================*/
#pragma once
#include "../Nanite/Nanite.h"
#include "../MeshDrawCommands.h"
#include "SceneTypes.h"
struct FMinimalSceneTextures;
struct FSortedLightSetSceneInfo;
class FViewInfo;
class FProjectedShadowInfo;
class FVisibleLightInfo;
class FVirtualShadowMapCacheEntry;
class FVirtualShadowMapArrayCacheManager;
struct FSortedLightSetSceneInfo;
class FVirtualShadowMapClipmap;
struct FScreenPassTexture;
// TODO: does this exist?
constexpr uint32 ILog2Const(uint32 n)
{
return (n > 1) ? 1 + ILog2Const(n / 2) : 0;
}
// See CalcLevelOffsets in PageAccessCommon.ush for some details on this logic
constexpr uint32 CalcVirtualShadowMapLevelOffsets(uint32 Level, uint32 Log2Level0DimPagesXY)
{
uint32 NumBits = Level << 1;
uint32 StartBit = (2U * Log2Level0DimPagesXY + 2U) - NumBits;
uint32 Mask = ((1U << NumBits) - 1U) << StartBit;
return 0x55555555U & Mask;
}
class FVirtualShadowMap
{
public:
// PageSize * Level0DimPagesXY defines the virtual address space, e.g., 128x128 = 16k
// 128x128 = 16k
static constexpr uint32 PageSize = 128U;
static constexpr uint32 Level0DimPagesXY = 128U;
static constexpr uint32 PageSizeMask = PageSize - 1U;
static constexpr uint32 Log2PageSize = ILog2Const(PageSize);
static constexpr uint32 Log2Level0DimPagesXY = ILog2Const(Level0DimPagesXY);
static constexpr uint32 MaxMipLevels = Log2Level0DimPagesXY + 1U;
static constexpr uint32 PageTableSize = CalcVirtualShadowMapLevelOffsets(MaxMipLevels, Log2Level0DimPagesXY);
static constexpr uint32 VirtualMaxResolutionXY = Level0DimPagesXY * PageSize;
static constexpr uint32 PhysicalPageAddressBits = 16U;
static constexpr uint32 MaxPhysicalTextureDimPages = 1U << PhysicalPageAddressBits;
static constexpr uint32 MaxPhysicalTextureDimTexels = MaxPhysicalTextureDimPages * PageSize;
static constexpr uint32 NumHZBLevels = Log2PageSize;
static constexpr uint32 RasterWindowPages = 4u;
static_assert(MaxMipLevels <= 8, ">8 mips requires more PageFlags bits. See VSM_PAGE_FLAGS_BITS_PER_HMIP in PageAccessCommon.ush");
FVirtualShadowMap(uint32 InID) : ID(InID)
{
}
int32 ID = INDEX_NONE;
TSharedPtr<FVirtualShadowMapCacheEntry> VirtualShadowMapCacheEntry;
};
// Useful data for both the page mapping shader and the projection shader
// as well as cached shadow maps
struct FVirtualShadowMapProjectionShaderData
{
/**
* Transform from shadow-pre-translated world space to shadow view space, example use: (WorldSpacePos + ShadowPreViewTranslation) * TranslatedWorldToShadowViewMatrix
* TODO: Why don't we call it a rotation and store in a 3x3? Does it ever have translation in?
*/
FMatrix44f TranslatedWorldToShadowViewMatrix;
FMatrix44f ShadowViewToClipMatrix;
FMatrix44f TranslatedWorldToShadowUVMatrix;
FMatrix44f TranslatedWorldToShadowUVNormalMatrix;
FVector3f PreViewTranslationLWCTile;
uint32 LightType = ELightComponentType::LightType_Directional;
FVector3f PreViewTranslationLWCOffset;
float LightSourceRadius; // This should live in shared light structure...
// TODO: There are more local lights than directional
// We should move the directional-specific stuff out to its own structure.
FVector3f NegativeClipmapWorldOriginLWCOffset; // Shares the LWCTile with PreViewTranslation
float ClipmapResolutionLodBias = 0.0f;
FIntPoint ClipmapCornerOffset = FIntPoint(0, 0);
int32 ClipmapIndex = 0; // 0 .. ClipmapLevelCount-1
int32 ClipmapLevel = 0; // "Absolute" level, can be negative
int32 ClipmapLevelCount = 0;
// Seems the FMatrix forces 16-byte alignment
float Padding[3];
};
static_assert((sizeof(FVirtualShadowMapProjectionShaderData) % 16) == 0, "FVirtualShadowMapProjectionShaderData size should be a multiple of 16-bytes for alignment.");
struct FVirtualShadowMapHZBMetadata
{
FViewMatrices ViewMatrices;
FIntRect ViewRect;
uint32 TargetLayerIndex = INDEX_NONE;
};
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FVirtualShadowMapUniformParameters, )
SHADER_PARAMETER(uint32, NumShadowMaps)
SHADER_PARAMETER(uint32, NumDirectionalLights)
SHADER_PARAMETER(uint32, MaxPhysicalPages)
// Set to 0 if separate static caching is disabled
SHADER_PARAMETER(uint32, StaticCachedArrayIndex)
// use to map linear index to x,y page coord
SHADER_PARAMETER(uint32, PhysicalPageRowMask)
SHADER_PARAMETER(uint32, PhysicalPageRowShift)
SHADER_PARAMETER(uint32, PackedShadowMaskMaxLightCount)
SHADER_PARAMETER(FVector4f, RecPhysicalPoolSize)
SHADER_PARAMETER(FIntPoint, PhysicalPoolSize)
SHADER_PARAMETER(FIntPoint, PhysicalPoolSizePages)
SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, ProjectionData)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, PageTable)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, PageFlags)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint4>, PageRectBounds)
SHADER_PARAMETER_RDG_TEXTURE(Texture2DArray<uint>, PhysicalPagePool)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FVirtualShadowMapSamplingParameters, )
// NOTE: These parameters must only be uniform buffers/references! Loose parameters do not get bound
// in some of the forward passes that use this structure.
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
END_SHADER_PARAMETER_STRUCT()
FMatrix CalcTranslatedWorldToShadowUVMatrix(const FMatrix& TranslatedWorldToShadowView, const FMatrix& ViewToClip);
FMatrix CalcTranslatedWorldToShadowUVNormalMatrix(const FMatrix& TranslatedWorldToShadowView, const FMatrix& ViewToClip);
struct FVirtualShadowMapVisualizeLightSearch
{
public:
FVirtualShadowMapVisualizeLightSearch()
{
Reset();
}
void Reset()
{
FoundKey.Packed = 0;
FoundProxy = nullptr;
FoundVirtualShadowMapId = INDEX_NONE;
}
void CheckLight(const FLightSceneProxy* CheckProxy, int CheckVirtualShadowMapId);
bool IsValid() const { return FoundProxy != nullptr; }
int GetVirtualShadowMapId() const { return FoundVirtualShadowMapId; }
const FLightSceneProxy* GetProxy() const { return FoundProxy; }
const FString GetLightName() const { return FoundProxy->GetOwnerNameOrLabel(); }
private:
union SortKey
{
struct
{
// NOTE: Lowest to highest priority
uint32 bExists : 1; // Catch-all
uint32 bDirectionalLight : 1;
uint32 bOwnerSelected : 1; // In editor
uint32 bSelected : 1; // In editor
uint32 bPartialNameMatch : 1;
uint32 bExactNameMatch : 1;
} Fields;
uint32 Packed;
};
SortKey FoundKey;
const FLightSceneProxy* FoundProxy = nullptr;
int FoundVirtualShadowMapId = INDEX_NONE;
};
class FVirtualShadowMapArray
{
public:
FVirtualShadowMapArray();
~FVirtualShadowMapArray();
void Initialize(FRDGBuilder& GraphBuilder, FVirtualShadowMapArrayCacheManager* InCacheManager, bool bInEnabled);
// Returns true if virtual shadow maps are enabled
bool IsEnabled() const
{
return bEnabled;
}
FVirtualShadowMap *Allocate()
{
check(IsEnabled());
FVirtualShadowMap *SM = new(FMemStack::Get()) FVirtualShadowMap(ShadowMaps.Num());
ShadowMaps.Add(SM);
return SM;
}
// Raw size of the physical pool, including both static and dynamic pages (if enabled)
FIntPoint GetPhysicalPoolSize() const;
// Size of HZB (level 0)
FIntPoint GetHZBPhysicalPoolSize() const;
// Maximum number of physical pages to allocate. This value is NOT doubled when static caching is
// enabled as we always allocate both as pairs (offset in the page pool).
uint32 GetMaxPhysicalPages() const { return UniformParameters.MaxPhysicalPages; }
// Total physical page count that includes separate static pages
uint32 GetTotalAllocatedPhysicalPages() const;
EPixelFormat GetPackedShadowMaskFormat() const;
static void SetShaderDefines(FShaderCompilerEnvironment& OutEnvironment);
void MergeStaticPhysicalPages(FRDGBuilder& GraphBuilder);
void BuildPageAllocations(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const TArrayView<FViewInfo> &Views,
const FEngineShowFlags& EngineShowFlags,
const FSortedLightSetSceneInfo& SortedLights,
const TArray<FVisibleLightInfo, SceneRenderingAllocator> &VisibleLightInfos,
const TArray<Nanite::FRasterResults, TInlineAllocator<2>> &NaniteRasterResults,
FScene& Scene);
bool IsAllocated() const
{
return PhysicalPagePoolRDG != nullptr && PageTableRDG != nullptr;
}
bool ShouldCacheStaticSeparately() const
{
return UniformParameters.StaticCachedArrayIndex > 0;
}
void CreateMipViews( TArray<Nanite::FPackedView, SceneRenderingAllocator>& Views ) const;
/**
* Draw Non-Nanite geometry into the VSMs.
*/
void RenderVirtualShadowMapsNonNanite(FRDGBuilder& GraphBuilder, const TArray<FProjectedShadowInfo*, SceneRenderingAllocator>& VirtualSmMeshCommandPasses, FScene& Scene, TArrayView<FViewInfo> Views);
void RenderDebugInfo(FRDGBuilder& GraphBuilder, TArrayView<FViewInfo> Views);
void PrintStats(FRDGBuilder& GraphBuilder, const FViewInfo& View);
TRDGUniformBufferRef<FVirtualShadowMapUniformParameters> GetUniformBuffer(FRDGBuilder& GraphBuilder) const;
// Get shader parameters necessary to sample virtual shadow maps
// It is safe to bind this buffer even if VSMs are disabled, but the sampling should be branched around in the shader.
// This data becomes valid after the shadow depths pass if VSMs are enabled
FVirtualShadowMapSamplingParameters GetSamplingParameters(FRDGBuilder& GraphBuilder) const;
bool HasAnyShadowData() const { return PhysicalPagePoolRDG != nullptr; }
bool ShouldCullBackfacingPixels() const { return bCullBackfacingPixels; }
void UpdateHZB(FRDGBuilder& GraphBuilder);
// Add render views, and mark shadow maps as rendered for a given clipmap or set of VSMs, returns the number of primary views added.
uint32 AddRenderViews(const TSharedPtr<FVirtualShadowMapClipmap>& Clipmap, float LODScaleFactor, bool bSetHzbParams, bool bUpdateHZBMetaData, bool bClampToNearPlane, TArray<Nanite::FPackedView, SceneRenderingAllocator>& OutVirtualShadowViews);
uint32 AddRenderViews(const FProjectedShadowInfo* ProjectedShadowInfo, float LODScaleFactor, bool bSetHzbParams, bool bUpdateHZBMetaData, bool bClampToNearPlane, TArray<Nanite::FPackedView, SceneRenderingAllocator>& OutVirtualShadowViews);
// Add visualization composite pass, if enabled
void AddVisualizePass(FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, FScreenPassTexture Output);
//
bool UseHzbOcclusion() const { return bUseHzbOcclusion; }
bool UseTwoPassHzbOcclusion() const { return bUseTwoPassHzbOcclusion; }
// We keep a reference to the cache manager that was used to initialize this frame as it owns some of the buffers
FVirtualShadowMapArrayCacheManager* CacheManager = nullptr;
TArray<FVirtualShadowMap*, SceneRenderingAllocator> ShadowMaps;
FVirtualShadowMapUniformParameters UniformParameters;
// Physical page pool shadow data
// NOTE: The underlying texture is owned by FVirtualShadowMapCacheManager.
// We just import and maintain a copy of the RDG reference for this frame here.
FRDGTextureRef PhysicalPagePoolRDG = nullptr;
// Buffer that serves as the page table for all virtual shadow maps
FRDGBufferRef PageTableRDG = nullptr;
// Buffer that stores flags (uints) marking each page that needs to be rendered and cache status, for all virtual shadow maps.
// Flag values defined in PageAccessCommon.ush
FRDGBufferRef PageFlagsRDG = nullptr;
// Allocation info for each page.
FRDGBufferRef CachedPageInfosRDG = nullptr;
FRDGBufferRef PhysicalPageMetaDataRDG = nullptr;
// TODO: make transient - Buffer that stores flags marking each page that received dynamic geo.
FRDGBufferRef DynamicCasterPageFlagsRDG = nullptr;
// Buffer that stores flags marking each instance that needs to be invalidated the subsequent frame (handled by the cache manager).
// This covers things like WPO or GPU-side updates, and any other case where we determine an instance needs to invalidate its footprint.
// Buffer of uints, organized as as follows: InvalidatingInstancesRDG[0] == count, InvalidatingInstancesRDG[1+MaxInstanceCount:1+MaxInstanceCount+MaxInstanceCount/32] == flags,
// InvalidatingInstancesRDG[1:MaxInstanceCount] == growing compact array of instances that need invaldation
FRDGBufferRef InvalidatingInstancesRDG = nullptr;
int32 NumInvalidatingInstanceSlots = 0;
// uint4 buffer with one rect for each mip level in all SMs, calculated to bound committed pages
// Used to clip the rect size of clusters during culling.
FRDGBufferRef PageRectBoundsRDG = nullptr;
FRDGBufferRef AllocatedPageRectBoundsRDG = nullptr;
FRDGBufferRef ProjectionDataRDG = nullptr;
FRDGBufferRef DirtyPageFlagsRDG = nullptr;
bool bHZBBuiltThisFrame = false;
FRDGTextureRef HZBPhysical = nullptr;
TMap<int32, FVirtualShadowMapHZBMetadata> HZBMetadata;
// See Engine\Shaders\Private\VirtualShadowMaps\VirtualShadowMapStats.ush for definitions of the different stat indexes
static constexpr uint32 NumStats = 16;
FRDGBufferRef StatsBufferRDG = nullptr;
// Debug visualization
TArray<FRDGTextureRef> DebugVisualizationOutput;
FVirtualShadowMapVisualizeLightSearch VisualizeLight;
private:
//
bool bUseHzbOcclusion = true;
bool bUseTwoPassHzbOcclusion = true;
bool bInitialized = false;
// Are virtual shadow maps enabled? We store this at the start of the frame to centralize the logic.
bool bEnabled = false;
// Is backface culling of pixels enabled? We store this here to keep it consistent between projection and generation
bool bCullBackfacingPixels = false;
};