Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapArray.cpp
ola olsson e856b953f3 Implement batching of instance culling for non-nanite VSM, all local lights are processed as one dispatch, also:
- Factor out the batching setup & merging from the deferred instance culling
- Refactor the instance culling load balancer to be able to share types between different allocators
- Make batching logic use scene rendering allocator
- Rename RenderVirtualShadowMapsHw to RenderVirtualShadowMapsNonNanite (and similar names)
- Refactor view setup logic for Nanite & Non into common functions

#rb andrew.lauritzen

#ROBOMERGE-AUTHOR: ola.olsson
#ROBOMERGE-SOURCE: CL 18293379 via CL 18373760 via CL 18373855
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v895-18170469)

[CL 18373879 by ola olsson in ue5-release-engine-test branch]
2021-12-03 16:38:33 -05:00

2758 lines
126 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VirtualShadowMap.h:
=============================================================================*/
#include "VirtualShadowMapArray.h"
#include "../BasePassRendering.h"
#include "Components/LightComponent.h"
#include "RendererModule.h"
#include "Rendering/NaniteResources.h"
#include "ShaderPrint.h"
#include "ShaderPrintParameters.h"
#include "VirtualShadowMapCacheManager.h"
#include "VirtualShadowMapClipmap.h"
#include "ComponentRecreateRenderStateContext.h"
#include "HairStrands/HairStrandsData.h"
#include "SceneTextureReductions.h"
#include "ShaderDebug.h"
#include "GPUMessaging.h"
#include "InstanceCulling/InstanceCullingMergedContext.h"
IMPLEMENT_STATIC_UNIFORM_BUFFER_SLOT(VirtualShadowMapUbSlot);
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FVirtualShadowMapUniformParameters, "VirtualShadowMap", VirtualShadowMapUbSlot);
struct FShadowMapCacheData
{
int32 PrevVirtualShadowMapId = INDEX_NONE;
};
struct FPhysicalPageMetaData
{
uint32 Flags;
uint32 Age;
};
int32 GEnableVirtualShadowMaps = 0;
FAutoConsoleVariableRef CVarEnableVirtualShadowMaps(
TEXT("r.Shadow.Virtual.Enable"),
GEnableVirtualShadowMaps,
TEXT("Enable Virtual Shadow Maps."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* InVariable)
{
// Needed because the depth state changes with method (so cached draw commands must be re-created) see SetStateForShadowDepth
FGlobalComponentRecreateRenderStateContext Context;
}),
ECVF_Scalability | ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarMaxPhysicalPages(
TEXT("r.Shadow.Virtual.MaxPhysicalPages"),
2048,
TEXT("Maximum number of physical pages in the pool."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarCacheStaticSeparate(
TEXT("r.Shadow.Virtual.Cache.StaticSeparate"),
0,
TEXT("When enabled, caches static objects in separate pages from dynamic objects.\n")
TEXT("This can improve performance in largely static scenes, but doubles the memory cost of the physical page pool."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarDebugVisualizeVirtualSms(
TEXT("r.Shadow.Virtual.DebugVisualize"),
0,
TEXT("Set Debug Visualization method for virtual shadow maps, default is off (0).\n")
TEXT(" To display the result also use the command 'vis VirtSmDebug'"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarShowStats(
TEXT("r.Shadow.Virtual.ShowStats"),
0,
TEXT("ShowStats, also toggle shaderprint one!"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarResolutionLodBiasLocal(
TEXT("r.Shadow.Virtual.ResolutionLodBiasLocal"),
0.0f,
TEXT("Bias applied to LOD calculations for local lights. -1.0 doubles resolution, 1.0 halves it and so on."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarPageDilationBorderSizeDirectional(
TEXT("r.Shadow.Virtual.PageDilationBorderSizeDirectional"),
0.05f,
TEXT("If a screen pixel falls within this fraction of a page border for directional lights, the adacent page will also be mapped.")
TEXT("Higher values can reduce page misses at screen edges or disocclusions, but increase total page counts."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarPageDilationBorderSizeLocal(
TEXT("r.Shadow.Virtual.PageDilationBorderSizeLocal"),
0.05f,
TEXT("If a screen pixel falls within this fraction of a page border for local lights, the adacent page will also be mapped.")
TEXT("Higher values can reduce page misses at screen edges or disocclusions, but increase total page counts."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarMarkPixelPages(
TEXT("r.Shadow.Virtual.MarkPixelPages"),
1,
TEXT("Marks pages in virtual shadow maps based on depth buffer pixels. Ability to disable is primarily for profiling and debugging."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarMarkCoarsePagesDirectional(
TEXT("r.Shadow.Virtual.MarkCoarsePagesDirectional"),
1,
TEXT("Marks coarse pages in directional light virtual shadow maps so that low resolution data is available everywhere.")
TEXT("Ability to disable is primarily for profiling and debugging."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarMarkCoarsePagesLocal(
TEXT("r.Shadow.Virtual.MarkCoarsePagesLocal"),
1,
TEXT("Marks coarse pages in local light virtual shadow maps so that low resolution data is available everywhere.")
TEXT("Ability to disable is primarily for profiling and debugging."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarCoarsePagesIncludeNonNanite(
TEXT("r.Shadow.Virtual.CoarsePagesIncludeNonNanite"),
1,
TEXT("Include non-nanite geometry in coarse pages.")
TEXT("Rendering non-nanite geometry into large coarse pages can be expensive; disabling this can be a significant performance win."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarShowClipmapStats(
TEXT("r.Shadow.Virtual.ShowClipmapStats"),
-1,
TEXT("Set to the number of clipmap you want to show stats for (-1 == off)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarCullBackfacingPixels(
TEXT("r.Shadow.Virtual.CullBackfacingPixels"),
1,
TEXT("When enabled does not generate shadow data for pixels that are backfacing to the light."),
ECVF_RenderThreadSafe
);
int32 GEnableNonNaniteVSM = 1;
FAutoConsoleVariableRef CVarEnableNonNaniteVSM(
TEXT("r.Shadow.Virtual.NonNaniteVSM"),
GEnableNonNaniteVSM,
TEXT("Enable support for non-nanite Virtual Shadow Maps.")
TEXT("Read-only and to be set in a config file (requires restart)."),
ECVF_RenderThreadSafe | ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarNonNaniteVsmUseHzb(
TEXT("r.Shadow.Virtual.NonNanite.UseHZB"),
2,
TEXT("Cull Non-Nanite instances using HZB. If set to 2, attempt to use Nanite-HZB from the current frame."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarInitializePhysicalUsingIndirect(
TEXT("r.Shadow.Virtual.InitPhysicalUsingIndirect"),
1,
TEXT("."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarMergePhysicalUsingIndirect(
TEXT("r.Shadow.Virtual.MergePhysicalUsingIndirect"),
1,
TEXT("."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarVirtualShadowOnePassProjectionMaxLights(
TEXT("r.Shadow.Virtual.OnePassProjection.MaxLightsPerPixel"),
16,
TEXT("Maximum lights per pixel that get full filtering when using one pass projection and clustered shading.")
TEXT("Generally set to 8 (32bpp), 16 (64bpp) or 32 (128bpp). Lower values require less transient VRAM during the lighting pass."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarDoNonNaniteBatching(
TEXT("r.Shadow.Virtual.NonNanite.Batch"),
1,
TEXT("."),
ECVF_RenderThreadSafe
);
#if !UE_BUILD_SHIPPING
bool GDumpVSMLightNames = false;
void DumpVSMLightNames()
{
ENQUEUE_RENDER_COMMAND(DumpVSMLightNames)(
[](FRHICommandList& RHICmdList)
{
GDumpVSMLightNames = true;
});
}
FAutoConsoleCommand CmdDumpVSMLightNames(
TEXT("r.Shadow.Virtual.DumpLightNames"),
TEXT("Dump light names with Virtual Shadow Maps (for developer only, only for non shiping build)"),
FConsoleCommandDelegate::CreateStatic(DumpVSMLightNames)
);
FString GVirtualShadowMapDebugLight;
FAutoConsoleVariableRef CVarDebugLight(
TEXT("r.Shadow.Virtual.DebugLight"),
GVirtualShadowMapDebugLight,
TEXT("Sets the name of a specific light to debug."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarVirtualShadowMapDebugProjection(
TEXT("r.Shadow.Virtual.DebugProjection"),
0,
TEXT("Projection pass debug output visualization for use with 'vis Shadow.Virtual.DebugProjection'."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarDebugSkipMergePhysical(
TEXT("r.Shadow.Virtual.DebugSkipMergePhysical"),
0,
TEXT(""),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarDebugSkipDynamicPageInvalidation(
TEXT("r.Shadow.Virtual.DebugSkipDynamicPageInvalidation"),
0,
TEXT("Invalidate cached pages when geometry moves.\n")
TEXT("This should be left enabled except for targeted profiling, as disabling it will produce artifacts with moving geometry."),
ECVF_RenderThreadSafe
);
#endif // !UE_BUILD_SHIPPING
TAutoConsoleVariable<int32> CVarVSMBuildHZBPerPage(
TEXT("r.Shadow.Virtual.BuildHZBPerPage"),
1,
TEXT("."),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarVSMHZB32Bit(
TEXT("r.Shadow.Virtual.HZB32Bit"),
1,
TEXT("Use a full 32-bit HZB buffer. This uses more memory but can offer more precise culling in cases with lots of overlapping detailed geometry."),
ECVF_RenderThreadSafe
);
FMatrix CalcTranslatedWorldToShadowUVMatrix(
const FMatrix& TranslatedWorldToShadowView,
const FMatrix& ViewToClip)
{
FMatrix TranslatedWorldToShadowClip = TranslatedWorldToShadowView * ViewToClip;
FMatrix ScaleAndBiasToSmUV = FScaleMatrix(FVector(0.5f, -0.5f, 1.0f)) * FTranslationMatrix(FVector(0.5f, 0.5f, 0.0f));
FMatrix TranslatedWorldToShadowUv = TranslatedWorldToShadowClip * ScaleAndBiasToSmUV;
return TranslatedWorldToShadowUv;
}
FMatrix CalcTranslatedWorldToShadowUVNormalMatrix(
const FMatrix& TranslatedWorldToShadowView,
const FMatrix& ViewToClip)
{
return CalcTranslatedWorldToShadowUVMatrix(TranslatedWorldToShadowView, ViewToClip).GetTransposed().Inverse();
}
FVirtualShadowMapArray::FVirtualShadowMapArray()
{
}
BEGIN_SHADER_PARAMETER_STRUCT(FCacheDataParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< FShadowMapCacheData >, ShadowMapCacheData )
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< uint >, PrevPageFlags )
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< uint >, PrevPageTable )
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< FPhysicalPageMetaData >, PrevPhysicalPageMetaData)
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< uint >, PrevDynamicCasterPageFlags)
END_SHADER_PARAMETER_STRUCT()
static void SetCacheDataShaderParameters(FRDGBuilder& GraphBuilder, const TArray<FVirtualShadowMap*, SceneRenderingAllocator> &ShadowMaps, FVirtualShadowMapArrayCacheManager* CacheManager, FCacheDataParameters &CacheDataParameters)
{
TArray<FShadowMapCacheData, SceneRenderingAllocator> ShadowMapCacheData;
ShadowMapCacheData.AddDefaulted(ShadowMaps.Num());
for (int32 SmIndex = 0; SmIndex < ShadowMaps.Num(); ++SmIndex)
{
TSharedPtr<FVirtualShadowMapCacheEntry> VirtualShadowMapCacheEntry = ShadowMaps[SmIndex]->VirtualShadowMapCacheEntry;
if (VirtualShadowMapCacheEntry != nullptr && VirtualShadowMapCacheEntry->IsValid())
{
ShadowMapCacheData[SmIndex].PrevVirtualShadowMapId = VirtualShadowMapCacheEntry->PrevVirtualShadowMapId;
}
else
{
ShadowMapCacheData[SmIndex].PrevVirtualShadowMapId = INDEX_NONE;
}
}
CacheDataParameters.ShadowMapCacheData = GraphBuilder.CreateSRV(CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.ShadowMapCacheData"), ShadowMapCacheData));
CacheDataParameters.PrevPageFlags = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.PageFlags, TEXT("Shadow.Virtual.PrevPageFlags")));
CacheDataParameters.PrevPageTable = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.PageTable, TEXT("Shadow.Virtual.PrevPageTable")));
CacheDataParameters.PrevPhysicalPageMetaData = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.PhysicalPageMetaData, TEXT("Shadow.Virtual.PrevPhysicalPageMetaData")));
CacheDataParameters.PrevDynamicCasterPageFlags = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.DynamicCasterPageFlags, TEXT("Shadow.Virtual.PrevDynamicCasterPageFlags")));
}
static FRDGBufferRef CreateProjectionDataBuffer(
FRDGBuilder& GraphBuilder,
const TCHAR* Name,
const TArray<FVirtualShadowMapProjectionShaderData, SceneRenderingAllocator>& InitialData)
{
uint64 DataSize = InitialData.Num() * InitialData.GetTypeSize();
FRDGBufferDesc Desc;
Desc.UnderlyingType = FRDGBufferDesc::EUnderlyingType::StructuredBuffer;
Desc.Usage = (EBufferUsageFlags)(BUF_UnorderedAccess | BUF_ShaderResource | BUF_ByteAddressBuffer);
Desc.BytesPerElement = 4;
Desc.NumElements = DataSize / 4;
FRDGBufferRef Buffer = GraphBuilder.CreateBuffer(Desc, Name);
GraphBuilder.QueueBufferUpload(Buffer, InitialData.GetData(), DataSize);
return Buffer;
}
void FVirtualShadowMapArray::Initialize(FRDGBuilder& GraphBuilder, FVirtualShadowMapArrayCacheManager* InCacheManager, bool bInEnabled)
{
bInitialized = true;
bEnabled = bInEnabled;
CacheManager = InCacheManager;
check(!bEnabled || CacheManager);
bCullBackfacingPixels = CVarCullBackfacingPixels.GetValueOnRenderThread() != 0;
UniformParameters.NumShadowMaps = 0;
UniformParameters.NumDirectionalLights = 0;
uint32 HPageFlagOffset = 0;
for (uint32 Level = 0; Level < FVirtualShadowMap::MaxMipLevels - 1; ++Level)
{
GET_SCALAR_ARRAY_ELEMENT(UniformParameters.HPageFlagLevelOffsets, Level) = HPageFlagOffset;
HPageFlagOffset += FVirtualShadowMap::PageTableSize - CalcVirtualShadowMapLevelOffsets(Level + 1, FVirtualShadowMap::Log2Level0DimPagesXY);
}
// The last mip level is 1x1 and thus does not have any H levels possible.
GET_SCALAR_ARRAY_ELEMENT(UniformParameters.HPageFlagLevelOffsets, (FVirtualShadowMap::MaxMipLevels - 1)) = 0;
UniformParameters.HPageTableSize = HPageFlagOffset;
// Fixed physical page pool width, we adjust the height to accomodate the requested maximum
// NOTE: Row size in pages has to be POT since we use mask & shift in place of integer ops
const uint32 PhysicalPagesX = FMath::DivideAndRoundUp(8192U, FVirtualShadowMap::PageSize);
check(FMath::IsPowerOfTwo(PhysicalPagesX));
uint32 PhysicalPagesY = FMath::DivideAndRoundUp((uint32)FMath::Max(1, CVarMaxPhysicalPages.GetValueOnRenderThread()), PhysicalPagesX);
UniformParameters.MaxPhysicalPages = PhysicalPagesX * PhysicalPagesY;
if (CacheManager->IsValid() && CVarCacheStaticSeparate.GetValueOnRenderThread() != 0)
{
// Store the static pages below the dynamic/merged pages
UniformParameters.StaticCachedPixelOffsetY = PhysicalPagesY * FVirtualShadowMap::PageSize;
// Offset to static pages in linear page index
UniformParameters.StaticPageIndexOffset = PhysicalPagesY * PhysicalPagesX;
PhysicalPagesY *= 2;
}
else
{
UniformParameters.StaticCachedPixelOffsetY = 0;
UniformParameters.StaticPageIndexOffset = 0;
}
uint32 PhysicalX = PhysicalPagesX * FVirtualShadowMap::PageSize;
uint32 PhysicalY = PhysicalPagesY * FVirtualShadowMap::PageSize;
UniformParameters.PhysicalPageRowMask = (PhysicalPagesX - 1);
UniformParameters.PhysicalPageRowShift = FMath::FloorLog2( PhysicalPagesX );
UniformParameters.RecPhysicalPoolSize = FVector4f( 1.0f / PhysicalX, 1.0f / PhysicalY, 1.0f, 1.0f );
UniformParameters.PhysicalPoolSize = FIntPoint( PhysicalX, PhysicalY );
UniformParameters.PhysicalPoolSizePages = FIntPoint( PhysicalPagesX, PhysicalPagesY );
// TODO: Parameterize this in a useful way; potentially modify it automatically
// when there are fewer lights in the scene and/or clustered shading settings differ.
UniformParameters.PackedShadowMaskMaxLightCount = FMath::Min(CVarVirtualShadowOnePassProjectionMaxLights.GetValueOnRenderThread(), 32);
// Reference dummy data in the UB initially
const uint32 DummyPageElement = 0xFFFFFFFF;
UniformParameters.PageTable = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(DummyPageElement), DummyPageElement));
UniformParameters.ProjectionData = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FVirtualShadowMapProjectionShaderData)));
if (bEnabled)
{
// If enabled, ensure we have a properly-sized physical page pool
// We can do this here since the pool is independent of the number of shadow maps
TRefCountPtr<IPooledRenderTarget> PhysicalPagePool = CacheManager->SetPhysicalPoolSize(GraphBuilder, GetPhysicalPoolSize());
PhysicalPagePoolRDG = GraphBuilder.RegisterExternalTexture(PhysicalPagePool);
UniformParameters.PhysicalPagePool = PhysicalPagePoolRDG;
}
else
{
CacheManager->FreePhysicalPool();
UniformParameters.PhysicalPagePool = GSystemTextures.GetZeroUIntDummy(GraphBuilder);
}
}
FVirtualShadowMapArray::~FVirtualShadowMapArray()
{
for (FVirtualShadowMap *SM : ShadowMaps)
{
SM->~FVirtualShadowMap();
}
}
EPixelFormat FVirtualShadowMapArray::GetPackedShadowMaskFormat() const
{
// TODO: Check if we're after any point that determines the format later too (light setup)
check(bInitialized);
// NOTE: Currently 4bpp/light
if (UniformParameters.PackedShadowMaskMaxLightCount <= 8)
{
return PF_R32_UINT;
}
else if (UniformParameters.PackedShadowMaskMaxLightCount <= 16)
{
return PF_R32G32_UINT;
}
else
{
check(UniformParameters.PackedShadowMaskMaxLightCount <= 32);
return PF_R32G32B32A32_UINT;
}
}
FIntPoint FVirtualShadowMapArray::GetPhysicalPoolSize() const
{
check(bInitialized);
return FIntPoint(UniformParameters.PhysicalPoolSize.X, UniformParameters.PhysicalPoolSize.Y);
}
FIntPoint FVirtualShadowMapArray::GetDynamicPhysicalPoolSize() const
{
check(bInitialized);
return FIntPoint(
UniformParameters.PhysicalPoolSize.X,
ShouldCacheStaticSeparately() ? UniformParameters.StaticCachedPixelOffsetY : UniformParameters.PhysicalPoolSize.Y);
}
uint32 FVirtualShadowMapArray::GetTotalAllocatedPhysicalPages() const
{
check(bInitialized);
return ShouldCacheStaticSeparately() ? (2U * UniformParameters.MaxPhysicalPages) : UniformParameters.MaxPhysicalPages;
}
TRDGUniformBufferRef<FVirtualShadowMapUniformParameters> FVirtualShadowMapArray::GetUniformBuffer(FRDGBuilder& GraphBuilder) const
{
// NOTE: Need to allocate new parameter space since the UB changes over the frame as dummy references are replaced
// TODO: Should we be caching this once all the relevant updates to parameters have been made in a frame?
FVirtualShadowMapUniformParameters* VersionedParameters = GraphBuilder.AllocParameters<FVirtualShadowMapUniformParameters>();
*VersionedParameters = UniformParameters;
return GraphBuilder.CreateUniformBuffer(VersionedParameters);
}
void FVirtualShadowMapArray::SetShaderDefines(FShaderCompilerEnvironment& OutEnvironment)
{
static_assert(FVirtualShadowMap::Log2Level0DimPagesXY * 2U + MAX_VIEWS_PER_CULL_RASTERIZE_PASS_BITS <= 32U, "Page indirection plus view index must fit into 32-bits for page-routing storage!");
OutEnvironment.SetDefine(TEXT("ENABLE_NON_NANITE_VSM"), GEnableNonNaniteVSM);
OutEnvironment.SetDefine(TEXT("VSM_PAGE_SIZE"), FVirtualShadowMap::PageSize);
OutEnvironment.SetDefine(TEXT("VSM_PAGE_SIZE_MASK"), FVirtualShadowMap::PageSizeMask);
OutEnvironment.SetDefine(TEXT("VSM_LOG2_PAGE_SIZE"), FVirtualShadowMap::Log2PageSize);
OutEnvironment.SetDefine(TEXT("VSM_LEVEL0_DIM_PAGES_XY"), FVirtualShadowMap::Level0DimPagesXY);
OutEnvironment.SetDefine(TEXT("VSM_LOG2_LEVEL0_DIM_PAGES_XY"), FVirtualShadowMap::Log2Level0DimPagesXY);
OutEnvironment.SetDefine(TEXT("VSM_MAX_MIP_LEVELS"), FVirtualShadowMap::MaxMipLevels);
OutEnvironment.SetDefine(TEXT("VSM_VIRTUAL_MAX_RESOLUTION_XY"), FVirtualShadowMap::VirtualMaxResolutionXY);
OutEnvironment.SetDefine(TEXT("VSM_RASTER_WINDOW_PAGES"), FVirtualShadowMap::RasterWindowPages);
OutEnvironment.SetDefine(TEXT("VSM_PAGE_TABLE_SIZE"), FVirtualShadowMap::PageTableSize);
OutEnvironment.SetDefine(TEXT("VSM_NUM_STATS"), NumStats);
OutEnvironment.SetDefine(TEXT("INDEX_NONE"), INDEX_NONE);
}
FVirtualShadowMapSamplingParameters FVirtualShadowMapArray::GetSamplingParameters(FRDGBuilder& GraphBuilder) const
{
// Sanity check: either VSMs are disabled and it's expected to be relying on dummy data, or we should have valid data
// If this fires, it is likely because the caller is trying to sample VSMs before they have been rendered by the ShadowDepths pass
// This should not crash, but it is not an intended production path as it will not return valid shadow data.
// TODO: Disabled warning until SkyAtmosphereLUT is moved after ShadowDepths
//ensureMsgf(!IsEnabled() || IsAllocated(),
// TEXT("Attempt to use Virtual Shadow Maps before they have been rendered by ShadowDepths."));
FVirtualShadowMapSamplingParameters Parameters;
Parameters.VirtualShadowMap = GetUniformBuffer(GraphBuilder);
return Parameters;
}
class FVirtualPageManagementShader : public FGlobalShader
{
public:
// Kernel launch group sizes
static constexpr uint32 DefaultCSGroupXY = 8;
static constexpr uint32 DefaultCSGroupX = 256;
static constexpr uint32 GeneratePageFlagsGroupXYZ = 4;
static constexpr uint32 BuildExplicitBoundsGroupXY = 16;
FVirtualPageManagementShader()
{
}
FVirtualPageManagementShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) &&
DoesPlatformSupportNanite(Parameters.Platform);
}
/**
* Can be overridden by FVertexFactory subclasses to modify their compile environment just before compilation occurs.
*/
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
OutEnvironment.SetDefine(TEXT("VSM_DEFAULT_CS_GROUP_X"), DefaultCSGroupX);
OutEnvironment.SetDefine(TEXT("VSM_DEFAULT_CS_GROUP_XY"), DefaultCSGroupXY);
OutEnvironment.SetDefine(TEXT("VSM_GENERATE_PAGE_FLAGS_CS_GROUP_XYZ"), GeneratePageFlagsGroupXYZ);
OutEnvironment.SetDefine(TEXT("VSM_BUILD_EXPLICIT_BOUNDS_CS_XY"), BuildExplicitBoundsGroupXY);
FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment);
OutEnvironment.SetDefine(TEXT("VF_SUPPORTS_PRIMITIVE_SCENE_DATA"), 1);
}
};
class FGeneratePageFlagsFromPixelsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FGeneratePageFlagsFromPixelsCS);
SHADER_USE_PARAMETER_STRUCT(FGeneratePageFlagsFromPixelsCS, FVirtualPageManagementShader)
class FInputType : SHADER_PERMUTATION_INT("PERMUTATION_INPUT_TYPE", 3);
using FPermutationDomain = TShaderPermutationDomain<FInputType>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTexturesStruct)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_REF(FForwardLightData, ForwardLightData)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint2>, VisBuffer64)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, OutPageRequestFlags)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, DirectionalLightIds)
RDG_BUFFER_ACCESS(IndirectBufferArgs, ERHIAccess::IndirectArgs)
SHADER_PARAMETER(uint32, InputType)
SHADER_PARAMETER(uint32, NumDirectionalLightSmInds)
SHADER_PARAMETER(uint32, bPostBasePass)
SHADER_PARAMETER(float, ResolutionLodBiasLocal)
SHADER_PARAMETER(float, PageDilationBorderSizeDirectional)
SHADER_PARAMETER(float, PageDilationBorderSizeLocal)
SHADER_PARAMETER(uint32, bCullBackfacingPixels)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FGeneratePageFlagsFromPixelsCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "GeneratePageFlagsFromPixels", SF_Compute);
class FMarkCoarsePagesCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FMarkCoarsePagesCS);
SHADER_USE_PARAMETER_STRUCT(FMarkCoarsePagesCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, OutPageRequestFlags)
SHADER_PARAMETER(uint32, bMarkCoarsePagesLocal)
SHADER_PARAMETER(uint32, bIncludeNonNaniteGeometry)
SHADER_PARAMETER(uint32, ClipmapIndexMask)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FMarkCoarsePagesCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "MarkCoarsePages", SF_Compute);
class FGenerateHierarchicalPageFlagsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FGenerateHierarchicalPageFlagsCS);
SHADER_USE_PARAMETER_STRUCT(FGenerateHierarchicalPageFlagsCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, OutHPageFlags)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, PageFlags)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FIntVector4>, PageRectBoundsOut)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FGenerateHierarchicalPageFlagsCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "GenerateHierarchicalPageFlags", SF_Compute);
class FInitPhysicalPageMetaData : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FInitPhysicalPageMetaData);
SHADER_USE_PARAMETER_STRUCT(FInitPhysicalPageMetaData, FVirtualPageManagementShader )
BEGIN_SHADER_PARAMETER_STRUCT( FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< FPhysicalPageMetaData >, OutPhysicalPageMetaData)
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutFreePhysicalPages )
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FInitPhysicalPageMetaData, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "InitPhysicalPageMetaData", SF_Compute );
class FCreateCachedPageMappingsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FCreateCachedPageMappingsCS);
SHADER_USE_PARAMETER_STRUCT(FCreateCachedPageMappingsCS, FVirtualPageManagementShader)
class FHasCacheDataDim : SHADER_PERMUTATION_BOOL("HAS_CACHE_DATA");
class FGenerateStatsDim : SHADER_PERMUTATION_BOOL("VSM_GENERATE_STATS");
using FPermutationDomain = TShaderPermutationDomain<FHasCacheDataDim, FGenerateStatsDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER( FVirtualShadowMapUniformParameters, VirtualShadowMap )
SHADER_PARAMETER_STRUCT_INCLUDE( FCacheDataParameters, CacheDataParameters )
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< uint >, PageRequestFlags )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutPageFlags)
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutPageTable )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< FPhysicalPageMetaData >, OutPhysicalPageMetaData )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutStatsBuffer )
SHADER_PARAMETER( int32, bDynamicPageInvalidation )
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FCreateCachedPageMappingsCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "CreateCachedPageMappings", SF_Compute);
class FPackFreePagesCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FPackFreePagesCS);
SHADER_USE_PARAMETER_STRUCT(FPackFreePagesCS, FVirtualPageManagementShader )
BEGIN_SHADER_PARAMETER_STRUCT( FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< FPhysicalPageMetaData >, PhysicalPageMetaData)
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutFreePhysicalPages )
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FPackFreePagesCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "PackFreePages", SF_Compute );
class FAllocateNewPageMappingsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FAllocateNewPageMappingsCS);
SHADER_USE_PARAMETER_STRUCT(FAllocateNewPageMappingsCS, FVirtualPageManagementShader)
class FGenerateStatsDim : SHADER_PERMUTATION_BOOL("VSM_GENERATE_STATS");
using FPermutationDomain = TShaderPermutationDomain<FGenerateStatsDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER( FVirtualShadowMapUniformParameters, VirtualShadowMap )
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< uint >, PageRequestFlags )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutFreePhysicalPages )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutPageFlags)
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutPageTable )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< FPhysicalPageMetaData >, OutPhysicalPageMetaData )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutStatsBuffer )
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FAllocateNewPageMappingsCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "AllocateNewPageMappings", SF_Compute);
class FPropagateMappedMipsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FPropagateMappedMipsCS);
SHADER_USE_PARAMETER_STRUCT(FPropagateMappedMipsCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER( FVirtualShadowMapUniformParameters, VirtualShadowMap )
SHADER_PARAMETER_RDG_BUFFER_UAV( RWStructuredBuffer< uint >, OutPageTable )
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FPropagateMappedMipsCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "PropagateMappedMips", SF_Compute);
class FInitializePhysicalPagesCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FInitializePhysicalPagesCS);
SHADER_USE_PARAMETER_STRUCT(FInitializePhysicalPagesCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, PhysicalPageMetaData)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint>, OutPhysicalPagePool)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FInitializePhysicalPagesCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "InitializePhysicalPages", SF_Compute);
class FSelectPagesToInitializeCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FSelectPagesToInitializeCS);
SHADER_USE_PARAMETER_STRUCT(FSelectPagesToInitializeCS, FVirtualPageManagementShader)
class FGenerateStatsDim : SHADER_PERMUTATION_BOOL("VSM_GENERATE_STATS");
using FPermutationDomain = TShaderPermutationDomain<FGenerateStatsDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FPhysicalPageMetaData>, PhysicalPageMetaData)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, OutInitializePagesIndirectArgsBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutPhysicalPagesToInitialize)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutStatsBuffer)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FSelectPagesToInitializeCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "SelectPagesToInitializeCS", SF_Compute);
class FInitializePhysicalPagesIndirectCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FInitializePhysicalPagesIndirectCS);
SHADER_USE_PARAMETER_STRUCT(FInitializePhysicalPagesIndirectCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, PhysicalPagesToInitialize)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint>, OutPhysicalPagePool)
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FInitializePhysicalPagesIndirectCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "InitializePhysicalPagesIndirectCS", SF_Compute);
class FClearIndirectDispatchArgs1DCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FClearIndirectDispatchArgs1DCS);
SHADER_USE_PARAMETER_STRUCT(FClearIndirectDispatchArgs1DCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(uint32, NumIndirectArgs)
SHADER_PARAMETER(uint32, IndirectArgStride)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, OutIndirectArgsBuffer)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FClearIndirectDispatchArgs1DCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "ClearIndirectDispatchArgs1DCS", SF_Compute);
static void AddClearIndirectDispatchArgs1DPass(FRDGBuilder& GraphBuilder, FRDGBufferRef IndirectArgsRDG, uint32 NumIndirectArgs = 1U, uint32 IndirectArgStride = 4U)
{
FClearIndirectDispatchArgs1DCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FClearIndirectDispatchArgs1DCS::FParameters>();
PassParameters->NumIndirectArgs = NumIndirectArgs;
PassParameters->IndirectArgStride = IndirectArgStride;
PassParameters->OutIndirectArgsBuffer = GraphBuilder.CreateUAV(IndirectArgsRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FClearIndirectDispatchArgs1DCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("ClearIndirectDispatchArgs"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(NumIndirectArgs, 64)
);
}
class FMergeStaticPhysicalPagesCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FMergeStaticPhysicalPagesCS);
SHADER_USE_PARAMETER_STRUCT(FMergeStaticPhysicalPagesCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, PhysicalPageMetaData)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint>, OutPhysicalPagePool)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FMergeStaticPhysicalPagesCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "MergeStaticPhysicalPages", SF_Compute);
class FSelectPagesToMergeCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FSelectPagesToMergeCS);
SHADER_USE_PARAMETER_STRUCT(FSelectPagesToMergeCS, FVirtualPageManagementShader)
class FGenerateStatsDim : SHADER_PERMUTATION_BOOL("VSM_GENERATE_STATS");
using FPermutationDomain = TShaderPermutationDomain<FGenerateStatsDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FPhysicalPageMetaData>, PhysicalPageMetaData)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, OutMergePagesIndirectArgsBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutPhysicalPagesToMerge)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutStatsBuffer)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FSelectPagesToMergeCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "SelectPagesToMergeCS", SF_Compute);
class FMergeStaticPhysicalPagesIndirectCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FMergeStaticPhysicalPagesIndirectCS);
SHADER_USE_PARAMETER_STRUCT(FMergeStaticPhysicalPagesIndirectCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, PhysicalPagesToMerge)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint>, OutPhysicalPagePool)
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FMergeStaticPhysicalPagesIndirectCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "MergeStaticPhysicalPagesIndirectCS", SF_Compute);
void FVirtualShadowMapArray::MergeStaticPhysicalPages(FRDGBuilder& GraphBuilder)
{
check(IsEnabled());
if (ShadowMaps.Num() == 0 || !ShouldCacheStaticSeparately())
{
return;
}
#if !UE_BUILD_SHIPPING
if (CVarDebugSkipMergePhysical.GetValueOnRenderThread() != 0)
{
return;
}
#endif
RDG_EVENT_SCOPE(GraphBuilder, "FVirtualShadowMapArray::MergeStaticPhysicalPages");
if (CVarMergePhysicalUsingIndirect.GetValueOnRenderThread() != 0)
{
FRDGBufferRef MergePagesIndirectArgsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(3), TEXT("Shadow.Virtual.MergePagesIndirectArgs"));
// Note: We use GetTotalAllocatedPhysicalPages() to size the buffer as the selection shader emits both static/dynamic pages separately when enabled.
FRDGBufferRef PhysicalPagesToMergeRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(int32), GetTotalAllocatedPhysicalPages() + 1), TEXT("Shadow.Virtual.PhysicalPagesToMerge"));
// 1. Initialize the indirect args buffer
AddClearIndirectDispatchArgs1DPass(GraphBuilder, MergePagesIndirectArgsRDG);
// 2. Filter the relevant physical pages and set up the indirect args
{
FSelectPagesToMergeCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSelectPagesToMergeCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PhysicalPageMetaData = GraphBuilder.CreateSRV(PhysicalPageMetaDataRDG);
PassParameters->OutMergePagesIndirectArgsBuffer = GraphBuilder.CreateUAV(MergePagesIndirectArgsRDG);
PassParameters->OutPhysicalPagesToMerge = GraphBuilder.CreateUAV(PhysicalPagesToMergeRDG);
bool bGenerateStats = StatsBufferRDG != nullptr;
if (bGenerateStats)
{
PassParameters->OutStatsBuffer = GraphBuilder.CreateUAV(StatsBufferRDG);
}
FSelectPagesToMergeCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSelectPagesToMergeCS::FGenerateStatsDim>(bGenerateStats);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FSelectPagesToMergeCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SelectPagesToMerge"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(GetMaxPhysicalPages(), FSelectPagesToMergeCS::DefaultCSGroupX), 1, 1)
);
}
// 3. Indirect dispatch to clear the selected pages
{
FMergeStaticPhysicalPagesIndirectCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FMergeStaticPhysicalPagesIndirectCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->OutPhysicalPagePool = GraphBuilder.CreateUAV(PhysicalPagePoolRDG);
PassParameters->IndirectArgs = MergePagesIndirectArgsRDG;
PassParameters->PhysicalPagesToMerge = GraphBuilder.CreateSRV(PhysicalPagesToMergeRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FMergeStaticPhysicalPagesIndirectCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("MergeStaticPhysicalPagesIndirect"),
ComputeShader,
PassParameters,
PassParameters->IndirectArgs,
0
);
}
}
else
{
FMergeStaticPhysicalPagesCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FMergeStaticPhysicalPagesCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PhysicalPageMetaData = GraphBuilder.CreateSRV(PhysicalPageMetaDataRDG);
PassParameters->OutPhysicalPagePool = GraphBuilder.CreateUAV(PhysicalPagePoolRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FMergeStaticPhysicalPagesCS>();
// Shader contains logic to deal with static cached pages if enabled
// We only need to launch one per page, even if there are multiple cached pages per page
FIntPoint DynamicPoolSize = GetDynamicPhysicalPoolSize();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("MergeStaticPhysicalPages"),
ComputeShader,
PassParameters,
FIntVector(
FMath::DivideAndRoundUp(DynamicPoolSize.X, 16),
FMath::DivideAndRoundUp(DynamicPoolSize.Y, 16),
1)
);
}
}
/**
* Helper to get hold of / check for associated virtual shadow map
*/
FORCEINLINE FProjectedShadowInfo* GetVirtualShadowMapInfo(const FVisibleLightInfo &LightInfo)
{
for (FProjectedShadowInfo *ProjectedShadowInfo : LightInfo.AllProjectedShadows)
{
if (ProjectedShadowInfo->HasVirtualShadowMap())
{
return ProjectedShadowInfo;
}
}
return nullptr;
}
class FInitPageRectBoundsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FInitPageRectBoundsCS);
SHADER_USE_PARAMETER_STRUCT(FInitPageRectBoundsCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FIntVector4>, PageRectBoundsOut)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FInitPageRectBoundsCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "InitPageRectBounds", SF_Compute);
class FVirtualSmFeedbackStatusCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FVirtualSmFeedbackStatusCS);
SHADER_USE_PARAMETER_STRUCT(FVirtualSmFeedbackStatusCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, FreePhysicalPages)
SHADER_PARAMETER_STRUCT_INCLUDE(GPUMessage::FParameters, GPUMessageParams)
SHADER_PARAMETER(uint32, StatusMessageId)
END_SHADER_PARAMETER_STRUCT()
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FVirtualPageManagementShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FVirtualSmFeedbackStatusCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "FeedbackStatusCS", SF_Compute);
static FString GetLightNameForDebug(const FLightSceneProxy* Proxy)
{
// TODO: ActorLabel is editor-only... need a better solution that goes right into Proxy->GetComponentName()
#if WITH_EDITOR
const ULightComponent* Component = Proxy->GetLightComponent();
const AActor* Owner = Component->GetOwner();
// TODO: Sometimes Owner is null... not sure why but best we can do for now
return Owner ? Owner->GetActorLabel() : Proxy->GetComponentName().ToString();
#else
return Proxy->GetComponentName().ToString();
#endif
}
#if !UE_BUILD_SHIPPING
struct FDebugLightSearch
{
public:
void CheckDebugLight(const FLightSceneProxy* Proxy, int CheckVirtualShadowMapId)
{
if (bFoundExactMatch)
{
return;
}
FString LightName = GetLightNameForDebug(Proxy);
if (GDumpVSMLightNames)
{
UE_LOG(LogRenderer, Display, TEXT("%s"), *LightName);
}
bFoundExactMatch = (LightName == GVirtualShadowMapDebugLight);
bool bPartialMatch = LightName.Contains(GVirtualShadowMapDebugLight);
bool bDirectionalLight = Proxy->GetLightType() == LightType_Directional;
// Priority: exact match, partial match, directional light, anything
if (bFoundExactMatch ||
VirtualShadowMapId == INDEX_NONE ||
(!bFoundPartialMatch && (bPartialMatch || (!bFoundDirectionalLight && bDirectionalLight))))
{
bFoundDirectionalLight = bDirectionalLight;
bFoundPartialMatch = bPartialMatch;
VirtualShadowMapId = CheckVirtualShadowMapId;
}
}
bool bFoundDirectionalLight = false;
bool bFoundPartialMatch = false;
bool bFoundExactMatch = false;
int VirtualShadowMapId = INDEX_NONE;
};
static FRDGTextureRef CreateDebugOutputTexture(FRDGBuilder& GraphBuilder, FIntPoint Extent)
{
const FLinearColor ClearColor(1.0f, 0.0f, 1.0f, 0.0f);
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
Extent,
PF_R8G8B8A8,
FClearValueBinding(ClearColor),
TexCreate_ShaderResource | TexCreate_UAV);
FRDGTextureRef Texture = GraphBuilder.CreateTexture(Desc, TEXT("Shadow.Virtual.DebugProjection"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Texture), ClearColor);
return Texture;
}
#endif // !UE_BUILD_SHIPPING
void FVirtualShadowMapArray::BuildPageAllocations(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const TArray<FViewInfo>& Views,
const FSortedLightSetSceneInfo& SortedLightsInfo,
const TArray<FVisibleLightInfo, SceneRenderingAllocator>& VisibleLightInfos,
const TArray<Nanite::FRasterResults, TInlineAllocator<2>>& NaniteRasterResults,
FScene& Scene)
{
check(IsEnabled());
RDG_EVENT_SCOPE(GraphBuilder, "FVirtualShadowMapArray::BuildPageAllocation");
#if !UE_BUILD_SHIPPING
bool bDebugOutputEnabled = false;
if (GDumpVSMLightNames)
{
bDebugOutputEnabled = true;
UE_LOG(LogRenderer, Display, TEXT("Lights with Virtual Shadow Maps:"));
}
// Setup debug output
DebugOutputType = CVarVirtualShadowMapDebugProjection.GetValueOnRenderThread();
FDebugLightSearch DebugLightSearch;
if (DebugOutputType > 0)
{
bDebugOutputEnabled = true;
DebugVisualizationProjectionOutput = CreateDebugOutputTexture(GraphBuilder, SceneTextures.Config.Extent);
}
#endif //!UE_BUILD_SHIPPING
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator> &SortedLights = SortedLightsInfo.SortedLights;
if (ShadowMaps.Num())
{
// Store shadow map projection data for each virtual shadow map
TArray<FVirtualShadowMapProjectionShaderData, SceneRenderingAllocator> ShadowMapProjectionData;
ShadowMapProjectionData.AddDefaulted(ShadowMaps.Num());
// Gather directional light virtual shadow maps
TArray<int32, SceneRenderingAllocator> DirectionalLightIds;
for (const FVisibleLightInfo& VisibleLightInfo : VisibleLightInfos)
{
for (const TSharedPtr<FVirtualShadowMapClipmap>& Clipmap : VisibleLightInfo.VirtualShadowMapClipmaps)
{
// NOTE: Shader assumes all levels from a given clipmap are contiguous
int32 ClipmapID = Clipmap->GetVirtualShadowMap()->ID;
DirectionalLightIds.Add(ClipmapID);
for (int32 ClipmapLevel = 0; ClipmapLevel < Clipmap->GetLevelCount(); ++ClipmapLevel)
{
ShadowMapProjectionData[ClipmapID + ClipmapLevel] = Clipmap->GetProjectionShaderData(ClipmapLevel);
}
#if !UE_BUILD_SHIPPING
if (bDebugOutputEnabled)
{
DebugLightSearch.CheckDebugLight(Clipmap->GetLightSceneInfo().Proxy, ClipmapID);
}
#endif // !UE_BUILD_SHIPPING
}
for (FProjectedShadowInfo* ProjectedShadowInfo : VisibleLightInfo.AllProjectedShadows)
{
if (ProjectedShadowInfo->HasVirtualShadowMap())
{
check(ProjectedShadowInfo->CascadeSettings.ShadowSplitIndex == INDEX_NONE); // We use clipmaps for virtual shadow maps, not cascades
// NOTE: Virtual shadow maps are never atlased, but verify our assumptions
{
const FVector4f ClipToShadowUV = ProjectedShadowInfo->GetClipToShadowBufferUvScaleBias();
check(ProjectedShadowInfo->BorderSize == 0);
check(ProjectedShadowInfo->X == 0);
check(ProjectedShadowInfo->Y == 0);
const FIntRect ShadowViewRect = ProjectedShadowInfo->GetInnerViewRect();
check(ShadowViewRect.Min.X == 0);
check(ShadowViewRect.Min.Y == 0);
check(ShadowViewRect.Max.X == FVirtualShadowMap::VirtualMaxResolutionXY);
check(ShadowViewRect.Max.Y == FVirtualShadowMap::VirtualMaxResolutionXY);
}
int32 NumMaps = ProjectedShadowInfo->bOnePassPointLightShadow ? 6 : 1;
for( int32 i = 0; i < NumMaps; i++ )
{
int32 ID = ProjectedShadowInfo->VirtualShadowMaps[i]->ID;
FViewMatrices ViewMatrices = ProjectedShadowInfo->GetShadowDepthRenderingViewMatrices( i, true );
FVirtualShadowMapProjectionShaderData& Data = ShadowMapProjectionData[ ID ];
Data.TranslatedWorldToShadowViewMatrix = ViewMatrices.GetTranslatedViewMatrix();
Data.ShadowViewToClipMatrix = ViewMatrices.GetProjectionMatrix();
Data.TranslatedWorldToShadowUVMatrix = CalcTranslatedWorldToShadowUVMatrix( ViewMatrices.GetTranslatedViewMatrix(), ViewMatrices.GetProjectionMatrix() );
Data.TranslatedWorldToShadowUVNormalMatrix = CalcTranslatedWorldToShadowUVNormalMatrix( ViewMatrices.GetTranslatedViewMatrix(), ViewMatrices.GetProjectionMatrix() );
Data.ShadowPreViewTranslation = FVector(ProjectedShadowInfo->PreShadowTranslation);
Data.LightType = ProjectedShadowInfo->GetLightSceneInfo().Proxy->GetLightType();
Data.LightSourceRadius = ProjectedShadowInfo->GetLightSceneInfo().Proxy->GetSourceRadius();
}
#if !UE_BUILD_SHIPPING
if (bDebugOutputEnabled)
{
DebugLightSearch.CheckDebugLight(ProjectedShadowInfo->GetLightSceneInfo().Proxy, ProjectedShadowInfo->VirtualShadowMaps[0]->ID);
}
#endif // !UE_BUILD_SHIPPING
}
}
}
#if !UE_BUILD_SHIPPING
DebugVirtualShadowMapId = DebugLightSearch.VirtualShadowMapId;
#endif
UniformParameters.NumShadowMaps = ShadowMaps.Num();
UniformParameters.NumDirectionalLights = DirectionalLightIds.Num();
ShadowMapProjectionDataRDG = CreateProjectionDataBuffer(GraphBuilder, TEXT("Shadow.Virtual.ProjectionData"), ShadowMapProjectionData);
UniformParameters.ProjectionData = GraphBuilder.CreateSRV(ShadowMapProjectionDataRDG);
if (CVarShowStats.GetValueOnRenderThread() || CacheManager->IsAccumulatingStats())
{
StatsBufferRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumStats), TEXT("Shadow.Virtual.StatsBuffer"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(StatsBufferRDG), 0);
}
// Create and clear the requested page flags
const uint32 NumPageFlags = ShadowMaps.Num() * FVirtualShadowMap::PageTableSize;
FRDGBufferRef PageRequestFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumPageFlags), TEXT("Shadow.Virtual.PageRequestFlags"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(PageRequestFlagsRDG), 0);
// TODO: Remove/move to next frame and make temporary OR replace with direct page table manipulation?
DynamicCasterPageFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumPageFlags), TEXT("Shadow.Virtual.DynamicCasterPageFlags"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(DynamicCasterPageFlagsRDG), 0);
// Record the number of instances the buffer has capactiy for, should anything change (it shouldn't!)
NumInvalidatingInstanceSlots = Scene.GPUScene.GetNumInstances();
// Allocate space for counter, worst case ID storage, and flags.
int32 InstanceInvalidationBufferSize = 1 + NumInvalidatingInstanceSlots + FMath::DivideAndRoundUp(NumInvalidatingInstanceSlots, 32);
InvalidatingInstancesRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), InstanceInvalidationBufferSize), TEXT("Shadow.Virtual.InvalidatingInstances"));
// Clear to zero, technically only need to clear first Scene.GPUScene.GetNumInstances() + 1 uints
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(InvalidatingInstancesRDG), 0);
// Total storage for Hierarchical page tables for all virtual shadow maps
const uint32 NumHPageFlags = ShadowMaps.Num() * UniformParameters.HPageTableSize;
HPageFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumHPageFlags), TEXT("Shadow.Virtual.HPageFlags"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HPageFlagsRDG), 0);
const uint32 NumPageRects = UniformParameters.NumShadowMaps * FVirtualShadowMap::MaxMipLevels;
PageRectBoundsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(FIntVector4), NumPageRects), TEXT("Shadow.Virtual.PageRectBounds"));
{
FInitPageRectBoundsCS::FParameters* PassParameters = GraphBuilder.AllocParameters< FInitPageRectBoundsCS::FParameters >();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PageRectBoundsOut = GraphBuilder.CreateUAV(PageRectBoundsRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FInitPageRectBoundsCS>();
ClearUnusedGraphResources(ComputeShader, PassParameters);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("InitPageRectBounds"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(NumPageRects, FInitPageRectBoundsCS::DefaultCSGroupX), 1, 1)
);
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
const FViewInfo &View = Views[ViewIndex];
// This view contained no local lights (that were stored in the light grid), and no directional lights, so nothing to do.
if (View.ForwardLightingResources->LocalLightVisibleLightInfosIndex.Num() + DirectionalLightIds.Num() == 0)
{
continue;
}
FRDGBufferRef DirectionalLightIdsRDG = CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.DirectionalLightIds"), DirectionalLightIds);
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder);
FRDGBufferRef ScreenSpaceGridBoundsRDG = nullptr;
{
// It's safe to overlap these passes that all write to page request flags
FRDGBufferUAVRef PageRequestFlagsUAV = GraphBuilder.CreateUAV(PageRequestFlagsRDG, ERDGUnorderedAccessViewFlags::SkipBarrier);
// Mark pages based on projected depth buffer pixels
if (CVarMarkPixelPages.GetValueOnRenderThread() != 0)
{
auto GeneratePageFlags = [&](bool bHairPass)
{
const uint32 InputType = bHairPass ? 1U : 0U; // HairStrands or GBuffer
FGeneratePageFlagsFromPixelsCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FGeneratePageFlagsFromPixelsCS::FInputType>(InputType);
FGeneratePageFlagsFromPixelsCS::FParameters* PassParameters = GraphBuilder.AllocParameters< FGeneratePageFlagsFromPixelsCS::FParameters >();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->SceneTexturesStruct = SceneTextures.UniformBuffer;
PassParameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View);
PassParameters->View = View.ViewUniformBuffer;
PassParameters->OutPageRequestFlags = PageRequestFlagsUAV;
PassParameters->ForwardLightData = View.ForwardLightingResources->ForwardLightDataUniformBuffer;
PassParameters->DirectionalLightIds = GraphBuilder.CreateSRV(DirectionalLightIdsRDG);
PassParameters->ResolutionLodBiasLocal = CVarResolutionLodBiasLocal.GetValueOnRenderThread();
PassParameters->PageDilationBorderSizeLocal = CVarPageDilationBorderSizeLocal.GetValueOnRenderThread();
PassParameters->PageDilationBorderSizeDirectional = CVarPageDilationBorderSizeDirectional.GetValueOnRenderThread();
PassParameters->bCullBackfacingPixels = ShouldCullBackfacingPixels() ? 1 : 0;
auto ComputeShader = View.ShaderMap->GetShader<FGeneratePageFlagsFromPixelsCS>(PermutationVector);
static_assert((FVirtualPageManagementShader::DefaultCSGroupXY % 2) == 0, "GeneratePageFlagsFromPixels requires even-sized CS groups for quad swizzling.");
const FIntPoint GridSize = FIntPoint::DivideAndRoundUp(View.ViewRect.Size(), FVirtualPageManagementShader::DefaultCSGroupXY);
if (bHairPass && View.HairStrandsViewData.VisibilityData.TileData.IsValid())
{
PassParameters->IndirectBufferArgs = View.HairStrandsViewData.VisibilityData.TileData.TileIndirectDispatchBuffer;
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("GeneratePageFlagsFromPixels(HairStrands,Tile)"),
ComputeShader,
PassParameters,
View.HairStrandsViewData.VisibilityData.TileData.TileIndirectDispatchBuffer,
View.HairStrandsViewData.VisibilityData.TileData.GetIndirectDispatchArgOffset(FHairStrandsTiles::ETileType::HairAll));
}
else
{
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("GeneratePageFlagsFromPixels(%s)", bHairPass ? TEXT("HairStrands") : TEXT("GBuffer")),
ComputeShader,
PassParameters,
FIntVector(GridSize.X, GridSize.Y, 1));
}
};
GeneratePageFlags(false);
if (HairStrands::HasViewHairStrandsData(View))
{
GeneratePageFlags(true);
}
}
// Mark coarse pages
bool bMarkCoarsePagesDirectional = CVarMarkCoarsePagesDirectional.GetValueOnRenderThread() != 0;
bool bMarkCoarsePagesLocal = CVarMarkCoarsePagesLocal.GetValueOnRenderThread() != 0;
if (bMarkCoarsePagesDirectional || bMarkCoarsePagesLocal)
{
FMarkCoarsePagesCS::FParameters* PassParameters = GraphBuilder.AllocParameters< FMarkCoarsePagesCS::FParameters >();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->OutPageRequestFlags = PageRequestFlagsUAV;
PassParameters->bMarkCoarsePagesLocal = bMarkCoarsePagesLocal ? 1 : 0;
PassParameters->ClipmapIndexMask = bMarkCoarsePagesDirectional ? FVirtualShadowMapClipmap::GetCoarsePageClipmapIndexMask() : 0;
PassParameters->bIncludeNonNaniteGeometry = CVarCoarsePagesIncludeNonNanite.GetValueOnRenderThread();
auto ComputeShader = View.ShaderMap->GetShader<FMarkCoarsePagesCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("MarkCoarsePages"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(uint32(ShadowMaps.Num()), FMarkCoarsePagesCS::DefaultCSGroupX), 1, 1)
);
}
}
}
PageTableRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumPageFlags), TEXT("Shadow.Virtual.PageTable"));
// Note: these are passed to the rendering and are not identical to the PageRequest flags coming in from GeneratePageFlagsFromPixels
PageFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumPageFlags), TEXT("Shadow.Virtual.PageFlags"));
FRDGBufferRef HInvalidPageFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumHPageFlags), TEXT("Shadow.Virtual.HInvalidPageFlags"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HInvalidPageFlagsRDG), 0);
// One additional element as the last element is used as an atomic counter
FRDGBufferRef FreePhysicalPagesRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(int32), GetMaxPhysicalPages() + 1), TEXT("Shadow.Virtual.FreePhysicalPages"));
// Enough space for all physical pages that might be allocated
PhysicalPageMetaDataRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(FPhysicalPageMetaData), GetMaxPhysicalPages()), TEXT("Shadow.Virtual.PhysicalPageMetaData"));
AllocatedPageRectBoundsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(FIntVector4), NumPageRects), TEXT("Shadow.Virtual.AllocatedPageRectBounds"));
{
FInitPhysicalPageMetaData::FParameters* PassParameters = GraphBuilder.AllocParameters<FInitPhysicalPageMetaData::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->OutPhysicalPageMetaData = GraphBuilder.CreateUAV(PhysicalPageMetaDataRDG);
PassParameters->OutFreePhysicalPages = GraphBuilder.CreateUAV(FreePhysicalPagesRDG);
auto ComputeShader = Views[0].ShaderMap->GetShader<FInitPhysicalPageMetaData>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("InitPhysicalPageMetaData"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(GetMaxPhysicalPages(), FInitPhysicalPageMetaData::DefaultCSGroupX), 1, 1)
);
}
// Start by marking any physical pages that we are going to keep due to caching
// NOTE: We run this pass even with no caching since we still need to initialize the metadata
{
FCreateCachedPageMappingsCS::FParameters* PassParameters = GraphBuilder.AllocParameters< FCreateCachedPageMappingsCS::FParameters >();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PageRequestFlags = GraphBuilder.CreateSRV(PageRequestFlagsRDG);
PassParameters->OutPageTable = GraphBuilder.CreateUAV(PageTableRDG);
PassParameters->OutPhysicalPageMetaData = GraphBuilder.CreateUAV(PhysicalPageMetaDataRDG);
PassParameters->OutPageFlags = GraphBuilder.CreateUAV(PageFlagsRDG);
PassParameters->bDynamicPageInvalidation = 1;
#if !UE_BUILD_SHIPPING
PassParameters->bDynamicPageInvalidation = CVarDebugSkipDynamicPageInvalidation.GetValueOnRenderThread() == 0 ? 1 : 0;
#endif
bool bCacheEnabled = CacheManager->IsValid();
if (bCacheEnabled)
{
SetCacheDataShaderParameters(GraphBuilder, ShadowMaps, CacheManager, PassParameters->CacheDataParameters);
}
bool bGenerateStats = StatsBufferRDG != nullptr;
if (bGenerateStats)
{
PassParameters->OutStatsBuffer = GraphBuilder.CreateUAV(StatsBufferRDG);
}
FCreateCachedPageMappingsCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCreateCachedPageMappingsCS::FHasCacheDataDim>(bCacheEnabled);
PermutationVector.Set<FCreateCachedPageMappingsCS::FGenerateStatsDim>(bGenerateStats);
auto ComputeShader = Views[0].ShaderMap->GetShader<FCreateCachedPageMappingsCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("CreateCachedPageMappings"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(FVirtualShadowMap::PageTableSize, FCreateCachedPageMappingsCS::DefaultCSGroupX), ShadowMaps.Num(), 1)
);
}
// After we've marked any cached pages, collect all the remaining free pages into a list
// NOTE: We could optimize this more in the case where there's no caching of course; TBD priority
{
FPackFreePagesCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FPackFreePagesCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PhysicalPageMetaData = GraphBuilder.CreateSRV(PhysicalPageMetaDataRDG);
PassParameters->OutFreePhysicalPages = GraphBuilder.CreateUAV(FreePhysicalPagesRDG);
auto ComputeShader = Views[0].ShaderMap->GetShader<FPackFreePagesCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("PackFreePages"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(GetMaxPhysicalPages(), FPackFreePagesCS::DefaultCSGroupX), 1, 1)
);
}
// Allocate any new physical pages that were not cached from the free list
{
FAllocateNewPageMappingsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FAllocateNewPageMappingsCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PageRequestFlags = GraphBuilder.CreateSRV(PageRequestFlagsRDG);
PassParameters->OutPageTable = GraphBuilder.CreateUAV(PageTableRDG);
PassParameters->OutPageFlags = GraphBuilder.CreateUAV(PageFlagsRDG);
PassParameters->OutFreePhysicalPages = GraphBuilder.CreateUAV(FreePhysicalPagesRDG);
PassParameters->OutPhysicalPageMetaData = GraphBuilder.CreateUAV(PhysicalPageMetaDataRDG);
bool bGenerateStats = StatsBufferRDG != nullptr;
if (bGenerateStats)
{
PassParameters->OutStatsBuffer = GraphBuilder.CreateUAV(StatsBufferRDG);
}
FAllocateNewPageMappingsCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FAllocateNewPageMappingsCS::FGenerateStatsDim>(bGenerateStats);
auto ComputeShader = Views[0].ShaderMap->GetShader<FAllocateNewPageMappingsCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("AllocateNewPageMappings"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(FVirtualShadowMap::PageTableSize, FAllocateNewPageMappingsCS::DefaultCSGroupX), ShadowMaps.Num(), 1)
);
}
{
// Run pass building hierarchical page flags to make culling acceptable performance.
FGenerateHierarchicalPageFlagsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FGenerateHierarchicalPageFlagsCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->OutHPageFlags = GraphBuilder.CreateUAV(HPageFlagsRDG);
PassParameters->PageFlags = GraphBuilder.CreateSRV(PageFlagsRDG);
PassParameters->PageRectBoundsOut = GraphBuilder.CreateUAV(PageRectBoundsRDG);
auto ComputeShader = Views[0].ShaderMap->GetShader<FGenerateHierarchicalPageFlagsCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("GenerateHierarchicalPageFlags"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(FVirtualShadowMap::PageTableSize, FGenerateHierarchicalPageFlagsCS::DefaultCSGroupX), ShadowMaps.Num(), 1)
);
}
// NOTE: We could skip this (in shader) for shadow maps that only have 1 mip (ex. clipmaps)
{
// Propagate mapped mips down the hierarchy to allow O(1) lookup of coarser mapped pages
FPropagateMappedMipsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FPropagateMappedMipsCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->OutPageTable = GraphBuilder.CreateUAV(PageTableRDG);
auto ComputeShader = Views[0].ShaderMap->GetShader<FPropagateMappedMipsCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("PropagateMappedMips"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(FMath::Square(FVirtualShadowMap::Level0DimPagesXY), FPropagateMappedMipsCS::DefaultCSGroupX), ShadowMaps.Num(), 1)
);
}
// Initialize the physical page pool
check(PhysicalPagePoolRDG != nullptr);
{
RDG_EVENT_SCOPE( GraphBuilder, "InitializePhysicalPages" );
if (CVarInitializePhysicalUsingIndirect.GetValueOnRenderThread() != 0)
{
FRDGBufferRef InitializePagesIndirectArgsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(3), TEXT("Shadow.Virtual.InitializePagesIndirectArgs"));
// Note: We use GetTotalAllocatedPhysicalPages() to size the buffer as the selection shader emits both static/dynamic pages separately when enabled.
FRDGBufferRef PhysicalPagesToInitializeRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(int32), GetTotalAllocatedPhysicalPages() + 1), TEXT("Shadow.Virtual.PhysicalPagesToInitialize"));
// 1. Initialize the indirect args buffer
AddClearIndirectDispatchArgs1DPass(GraphBuilder, InitializePagesIndirectArgsRDG);
// 2. Filter the relevant physical pages and set up the indirect args
{
FSelectPagesToInitializeCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSelectPagesToInitializeCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PhysicalPageMetaData = GraphBuilder.CreateSRV(PhysicalPageMetaDataRDG);
PassParameters->OutInitializePagesIndirectArgsBuffer = GraphBuilder.CreateUAV(InitializePagesIndirectArgsRDG);
PassParameters->OutPhysicalPagesToInitialize = GraphBuilder.CreateUAV(PhysicalPagesToInitializeRDG);
bool bGenerateStats = StatsBufferRDG != nullptr;
if (bGenerateStats)
{
PassParameters->OutStatsBuffer = GraphBuilder.CreateUAV(StatsBufferRDG);
}
FSelectPagesToInitializeCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSelectPagesToInitializeCS::FGenerateStatsDim>(bGenerateStats);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FSelectPagesToInitializeCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SelectPagesToInitialize"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(GetMaxPhysicalPages(), FSelectPagesToInitializeCS::DefaultCSGroupX), 1, 1)
);
}
// 3. Indirect dispatch to clear the selected pages
{
FInitializePhysicalPagesIndirectCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FInitializePhysicalPagesIndirectCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->OutPhysicalPagePool = GraphBuilder.CreateUAV(PhysicalPagePoolRDG);
PassParameters->IndirectArgs = InitializePagesIndirectArgsRDG;
PassParameters->PhysicalPagesToInitialize = GraphBuilder.CreateSRV(PhysicalPagesToInitializeRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FInitializePhysicalPagesIndirectCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("InitializePhysicalMemoryIndirect"),
ComputeShader,
PassParameters,
PassParameters->IndirectArgs,
0
);
}
}
else
{
FInitializePhysicalPagesCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FInitializePhysicalPagesCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PhysicalPageMetaData = GraphBuilder.CreateSRV(PhysicalPageMetaDataRDG);
PassParameters->OutPhysicalPagePool = GraphBuilder.CreateUAV(PhysicalPagePoolRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FInitializePhysicalPagesCS>();
// Shader contains logic to deal with static cached pages if enabled
// We only need to launch one per page, even if there are multiple cached pages per page
FIntPoint DynamicPoolSize = GetDynamicPhysicalPoolSize();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("InitializePhysicalPages"),
ComputeShader,
PassParameters,
FIntVector(
FMath::DivideAndRoundUp(DynamicPoolSize.X, 16),
FMath::DivideAndRoundUp(DynamicPoolSize.Y, 16),
1)
);
}
}
UniformParameters.PageTable = GraphBuilder.CreateSRV(PageTableRDG);
// Add pass to pipe back important stats
{
FVirtualSmFeedbackStatusCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVirtualSmFeedbackStatusCS::FParameters>();
PassParameters->FreePhysicalPages = GraphBuilder.CreateSRV(FreePhysicalPagesRDG);
PassParameters->GPUMessageParams = GPUMessage::GetShaderParameters(GraphBuilder);
PassParameters->StatusMessageId = CacheManager->StatusFeedbackSocket.GetMessageId().GetIndex();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FVirtualSmFeedbackStatusCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Feedback Status"),
ComputeShader,
PassParameters,
FIntVector(1, 1, 1)
);
}
}
#if !UE_BUILD_SHIPPING
// Only dump one frame of light data
GDumpVSMLightNames = false;
#endif
}
class FDebugVisualizeVirtualSmCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FDebugVisualizeVirtualSmCS);
SHADER_USE_PARAMETER_STRUCT(FDebugVisualizeVirtualSmCS, FVirtualPageManagementShader)
class FHasCacheDataDim : SHADER_PERMUTATION_BOOL("HAS_CACHE_DATA");
using FPermutationDomain = TShaderPermutationDomain<FHasCacheDataDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, ProjectionParameters)
SHADER_PARAMETER(uint32, DebugTargetWidth)
SHADER_PARAMETER(uint32, DebugTargetHeight)
SHADER_PARAMETER(uint32, BorderWidth)
SHADER_PARAMETER(uint32, ZoomScaleFactor)
SHADER_PARAMETER(uint32, DebugMethod)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, PageFlags)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, HPageFlags)
SHADER_PARAMETER_RDG_TEXTURE( Texture2D< float >, HZBPhysical )
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, HZBPageTable )
SHADER_PARAMETER_STRUCT_INCLUDE(FCacheDataParameters, CacheDataParameters)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, DebugOutput)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FDebugVisualizeVirtualSmCS, "/Engine/Private/VirtualShadowMaps/Debug.usf", "DebugVisualizeVirtualSmCS", SF_Compute);
void FVirtualShadowMapArray::RenderDebugInfo(FRDGBuilder& GraphBuilder)
{
check(IsEnabled());
int32 DebugMethod = CVarDebugVisualizeVirtualSms.GetValueOnRenderThread();
if (ShadowMaps.Num() && DebugMethod > 0)
{
int32 ZoomScaleFactor = 1;
int32 BorderWidth = 2;
// Make debug target wide enough to show a mip-chain
int32 DebugTargetWidth = ZoomScaleFactor * (FVirtualShadowMap::Level0DimPagesXY * 2 + BorderWidth * (FVirtualShadowMap::MaxMipLevels));
// Enough rows for all the shadow maps to show
int32 DebugTargetHeight = ZoomScaleFactor * (FVirtualShadowMap::Level0DimPagesXY + BorderWidth * 2) * ShadowMaps.Num();
if( DebugMethod > 5 )
{
DebugTargetWidth = 2048;
DebugTargetHeight = 2048;
}
FRDGTextureDesc DebugOutputDesc = FRDGTextureDesc::Create2D(
FIntPoint(DebugTargetWidth, DebugTargetHeight),
PF_A32B32G32R32F,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
FRDGTextureRef DebugOutput = GraphBuilder.CreateTexture(
DebugOutputDesc,
TEXT("Shadow.Virtual.Debug"));
FDebugVisualizeVirtualSmCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDebugVisualizeVirtualSmCS::FParameters>();
PassParameters->ProjectionParameters = GetSamplingParameters(GraphBuilder);
PassParameters->PageFlags = GraphBuilder.CreateSRV( PageFlagsRDG );
PassParameters->HPageFlags = GraphBuilder.CreateSRV( HPageFlagsRDG );
// TODO: unclear if it's preferable to debug the HZB we generated "this frame" here, or the previous frame (that was used for culling)?
// We'll stick with the previous frame logic that was there, but it's cleaner to just reference the current frame one
TRefCountPtr<IPooledRenderTarget> PrevHZBPhysical = CacheManager->PrevBuffers.HZBPhysical;
TRefCountPtr<FRDGPooledBuffer> PrevPageTable = CacheManager->PrevBuffers.PageTable;
PassParameters->HZBPhysical = RegisterExternalTextureWithFallback( GraphBuilder, PrevHZBPhysical, GSystemTextures.BlackDummy );
PassParameters->HZBPageTable = GraphBuilder.CreateSRV( PrevPageTable ? GraphBuilder.RegisterExternalBuffer( PrevPageTable ) : PageTableRDG );
PassParameters->DebugTargetWidth = DebugTargetWidth;
PassParameters->DebugTargetHeight = DebugTargetHeight;
PassParameters->BorderWidth = BorderWidth;
PassParameters->ZoomScaleFactor = ZoomScaleFactor;
PassParameters->DebugMethod = DebugMethod;
bool bCacheDataAvailable = CacheManager->IsValid();
if (bCacheDataAvailable)
{
SetCacheDataShaderParameters(GraphBuilder, ShadowMaps, CacheManager, PassParameters->CacheDataParameters);
}
PassParameters->DebugOutput = GraphBuilder.CreateUAV(DebugOutput);
FDebugVisualizeVirtualSmCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDebugVisualizeVirtualSmCS::FHasCacheDataDim>(bCacheDataAvailable);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FDebugVisualizeVirtualSmCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DebugVisualizeVirtualSmCS"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(FIntPoint(DebugTargetWidth, DebugTargetHeight), FVirtualPageManagementShader::DefaultCSGroupXY)
);
DebugVisualizationOutput = GraphBuilder.ConvertToExternalTexture(DebugOutput);
}
}
class FVirtualSmPrintStatsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FVirtualSmPrintStatsCS);
SHADER_USE_PARAMETER_STRUCT(FVirtualSmPrintStatsCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_STRUCT_INCLUDE( ShaderPrint::FShaderParameters, ShaderPrintStruct )
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, InStatsBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FIntVector4>, AllocatedPageRectBounds)
SHADER_PARAMETER(int, ShowStatsValue)
END_SHADER_PARAMETER_STRUCT()
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FVirtualPageManagementShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
// Disable optimizations as shader print causes long compile times
OutEnvironment.CompilerFlags.Add(CFLAG_SkipOptimizations);
}
};
IMPLEMENT_GLOBAL_SHADER(FVirtualSmPrintStatsCS, "/Engine/Private/VirtualShadowMaps/PrintStats.usf", "PrintStats", SF_Compute);
void FVirtualShadowMapArray::PrintStats(FRDGBuilder& GraphBuilder, const FViewInfo& View)
{
check(IsEnabled());
LLM_SCOPE_BYTAG(Nanite);
// Print stats
int ShowStatsValue = CVarShowStats.GetValueOnRenderThread();
if (ShowStatsValue != 0 && StatsBufferRDG)
{
{
FVirtualSmPrintStatsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVirtualSmPrintStatsCS::FParameters>();
ShaderPrint::SetParameters(GraphBuilder, View, PassParameters->ShaderPrintStruct);
PassParameters->InStatsBuffer = GraphBuilder.CreateSRV(StatsBufferRDG);
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->ShowStatsValue = ShowStatsValue;
auto ComputeShader = View.ShaderMap->GetShader<FVirtualSmPrintStatsCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Print Stats"),
ComputeShader,
PassParameters,
FIntVector(1, 1, 1)
);
}
}
}
extern int32 GNaniteClusterPerPage;
void FVirtualShadowMapArray::CreateMipViews( TArray<Nanite::FPackedView, SceneRenderingAllocator>& Views ) const
{
// strategy:
// 1. Use the cull pass to generate copies of every node for every view needed.
// [2. Fabricate a HZB array?]
ensure(Views.Num() <= ShadowMaps.Num());
const int32 NumPrimaryViews = Views.Num();
// 1. create derivative views for each of the Mip levels,
Views.AddDefaulted( NumPrimaryViews * ( FVirtualShadowMap::MaxMipLevels - 1) );
int32 MaxMips = 0;
for (int32 ViewIndex = 0; ViewIndex < NumPrimaryViews; ++ViewIndex)
{
const Nanite::FPackedView& PrimaryView = Views[ViewIndex];
ensure( PrimaryView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.X >= 0 && PrimaryView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.X < ShadowMaps.Num() );
ensure( PrimaryView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.Y == 0 );
ensure( PrimaryView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.Z > 0 && PrimaryView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.Z <= FVirtualShadowMap::MaxMipLevels );
const int32 NumMips = PrimaryView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.Z;
MaxMips = FMath::Max(MaxMips, NumMips);
for (int32 MipLevel = 0; MipLevel < NumMips; ++MipLevel)
{
Nanite::FPackedView& MipView = Views[ MipLevel * NumPrimaryViews + ViewIndex ]; // Primary (Non-Mip views) first followed by derived mip views.
if( MipLevel > 0 )
{
MipView = PrimaryView;
// Slightly messy, but extract any scale factor that was applied to the LOD scale for re-application below
MipView.UpdateLODScales();
float LODScaleFactor = PrimaryView.LODScales.X / MipView.LODScales.X;
MipView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.Y = MipLevel;
MipView.TargetLayerIdX_AndMipLevelY_AndNumMipLevelsZ.Z = NumMips; //FVirtualShadowMap::MaxMipLevels;
// Size of view, for the virtual SMs these are assumed to not be offset.
FIntPoint ViewSize = FIntPoint::DivideAndRoundUp( FIntPoint( PrimaryView.ViewSizeAndInvSize.X + 0.5f, PrimaryView.ViewSizeAndInvSize.Y + 0.5f ), 1U << MipLevel );
FIntPoint ViewMin = FIntPoint(MipView.ViewRect.X, MipView.ViewRect.Y) / (1U << MipLevel);
MipView.ViewSizeAndInvSize = FVector4f(ViewSize.X, ViewSize.Y, 1.0f / float(ViewSize.X), 1.0f / float(ViewSize.Y));
MipView.ViewRect = FIntVector4(ViewMin.X, ViewMin.Y, ViewMin.X + ViewSize.X, ViewMin.Y + ViewSize.Y);
MipView.UpdateLODScales();
MipView.LODScales.X *= LODScaleFactor;
}
MipView.HZBTestViewRect = MipView.ViewRect; // Assumed to always be the same for VSM
float RcpExtXY = 1.0f / FVirtualShadowMap::VirtualMaxResolutionXY;
if( GNaniteClusterPerPage )
RcpExtXY = 1.0f / ( FVirtualShadowMap::PageSize * FVirtualShadowMap::RasterWindowPages );
// Transform clip from virtual address space to viewport.
MipView.ClipSpaceScaleOffset = FVector4f(
MipView.ViewSizeAndInvSize.X * RcpExtXY,
MipView.ViewSizeAndInvSize.Y * RcpExtXY,
(MipView.ViewSizeAndInvSize.X + 2.0f * MipView.ViewRect.X) * RcpExtXY - 1.0f,
-(MipView.ViewSizeAndInvSize.Y + 2.0f * MipView.ViewRect.Y) * RcpExtXY + 1.0f);
uint32 StreamingPriorityCategory = 0;
uint32 ViewFlags = VIEW_FLAG_HZBTEST;
MipView.StreamingPriorityCategory_AndFlags = (ViewFlags << NUM_STREAMING_PRIORITY_CATEGORY_BITS) | StreamingPriorityCategory;
}
}
// Remove unused mip views
check(Views.IsEmpty() || MaxMips > 0);
Views.SetNum(MaxMips * NumPrimaryViews, false);
}
class FVirtualSmPrintClipmapStatsCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FVirtualSmPrintClipmapStatsCS);
SHADER_USE_PARAMETER_STRUCT(FVirtualSmPrintClipmapStatsCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
//SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintStruct)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FIntVector4 >, PageRectBounds)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FIntVector4 >, AllocatedPageRectBounds)
SHADER_PARAMETER(uint32, ShadowMapIdRangeStart)
SHADER_PARAMETER(uint32, ShadowMapIdRangeEnd)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FVirtualSmPrintClipmapStatsCS, "/Engine/Private/VirtualShadowMaps/PrintStats.usf", "PrintClipmapStats", SF_Compute);
void FVirtualShadowMapArray::GetPageTableParameters(FRDGBuilder& GraphBuilder, FVirtualShadowMapPageTableParameters& OutParameters)
{
OutParameters.VirtualShadowMap = GetUniformBuffer(GraphBuilder);
OutParameters.PageFlags = GraphBuilder.CreateSRV(PageFlagsRDG);
OutParameters.HPageFlags = GraphBuilder.CreateSRV(HPageFlagsRDG);
OutParameters.PageRectBounds = GraphBuilder.CreateSRV(PageRectBoundsRDG);
OutParameters.PageTable = GraphBuilder.CreateSRV(PageTableRDG);
}
BEGIN_SHADER_PARAMETER_STRUCT(FVirtualShadowDepthPassParameters,)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FShadowDepthPassUniformParameters, ShadowDepthPass)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FPackedView >, InViews)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
struct FVSMCullingBatchInfo
{
uint32 FirstPrimaryView;
uint32 NumPrimaryViews;
};
struct FVisibleInstanceCmd
{
uint32 PackedPageInfo;
uint32 InstanceId;
uint32 DrawCommandId;
};
class FCullPerPageDrawCommandsCs : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCullPerPageDrawCommandsCs);
SHADER_USE_PARAMETER_STRUCT(FCullPerPageDrawCommandsCs, FGlobalShader)
class FNearClipDim : SHADER_PERMUTATION_BOOL( "NEAR_CLIP" );
class FUseHzbDim : SHADER_PERMUTATION_BOOL("USE_HZB_OCCLUSION");
class FGenerateStatsDim : SHADER_PERMUTATION_BOOL("VSM_GENERATE_STATS");
class FBatchedDim : SHADER_PERMUTATION_BOOL("ENABLE_BATCH_MODE");
using FPermutationDomain = TShaderPermutationDomain< FUseHzbDim, FNearClipDim, FBatchedDim, FGenerateStatsDim >;
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
/**
*/
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
FInstanceProcessingGPULoadBalancer::SetShaderDefines(OutEnvironment);
OutEnvironment.SetDefine(TEXT("NANITE_MULTI_VIEW"), 1);
OutEnvironment.SetDefine(TEXT("INDIRECT_ARGS_NUM_WORDS"), FInstanceCullingContext::IndirectArgsNumWords);
OutEnvironment.SetDefine(TEXT("VF_SUPPORTS_PRIMITIVE_SCENE_DATA"), 1);
OutEnvironment.SetDefine(TEXT("USE_GLOBAL_GPU_SCENE_DATA"), 1);
}
BEGIN_SHADER_PARAMETER_STRUCT(FHZBShaderParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, ShadowHZBPageTable)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HZBTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, HZBSampler)
SHADER_PARAMETER(FVector2f, HZBSize)
SHADER_PARAMETER(uint32, HZBMode)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapPageTableParameters, PageTableParams)
SHADER_PARAMETER_SRV(StructuredBuffer<float4>, GPUSceneInstanceSceneData)
SHADER_PARAMETER_SRV(StructuredBuffer<float4>, GPUSceneInstancePayloadData)
SHADER_PARAMETER_SRV(StructuredBuffer<float4>, GPUScenePrimitiveSceneData)
SHADER_PARAMETER(uint32, InstanceSceneDataSOAStride)
SHADER_PARAMETER(uint32, GPUSceneFrameNumber)
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceProcessingGPULoadBalancer::FShaderParameters, LoadBalancerParameters)
SHADER_PARAMETER(int32, FirstPrimaryView)
SHADER_PARAMETER(int32, NumPrimaryViews)
SHADER_PARAMETER(uint32, TotalPrimaryViews)
SHADER_PARAMETER(uint32, VisibleInstancesBufferNum)
SHADER_PARAMETER(int32, DynamicInstanceIdOffset)
SHADER_PARAMETER(int32, DynamicInstanceIdMax)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FPackedView >, InViews)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FInstanceCullingContext::FDrawCommandDesc >, DrawCommandDescs)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FContextBatchInfo >, BatchInfos)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FVSMCullingBatchInfo >, VSMCullingBatchInfos)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint32 >, BatchInds)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FVisibleInstanceCmd>, VisibleInstancesOut)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, DrawIndirectArgsBufferOut)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, VisibleInstanceCountBufferOut)
SHADER_PARAMETER_STRUCT_INCLUDE(FHZBShaderParameters, HZBShaderParameters)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, OutInvalidatingInstances)
SHADER_PARAMETER(uint32, NumInvalidatingInstanceSlots)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutStatsBuffer)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FCullPerPageDrawCommandsCs, "/Engine/Private/VirtualShadowMaps/BuildPerPageDrawCommands.usf", "CullPerPageDrawCommandsCs", SF_Compute);
class FAllocateCommandInstanceOutputSpaceCs : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FAllocateCommandInstanceOutputSpaceCs);
SHADER_USE_PARAMETER_STRUCT(FAllocateCommandInstanceOutputSpaceCs, FGlobalShader)
public:
static constexpr int32 NumThreadsPerGroup = 64;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
/**
*/
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
FInstanceProcessingGPULoadBalancer::SetShaderDefines(OutEnvironment);
OutEnvironment.SetDefine(TEXT("NUM_THREADS_PER_GROUP"), NumThreadsPerGroup);
OutEnvironment.SetDefine(TEXT("NANITE_MULTI_VIEW"), 1);
OutEnvironment.SetDefine(TEXT("INDIRECT_ARGS_NUM_WORDS"), FInstanceCullingContext::IndirectArgsNumWords);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(uint32, NumIndirectArgs)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, DrawIndirectArgsBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, InstanceIdOffsetBufferOut)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, OutputOffsetBufferOut)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, TmpInstanceIdOffsetBufferOut)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FAllocateCommandInstanceOutputSpaceCs, "/Engine/Private/VirtualShadowMaps/BuildPerPageDrawCommands.usf", "AllocateCommandInstanceOutputSpaceCs", SF_Compute);
class FOutputCommandInstanceListsCs : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FOutputCommandInstanceListsCs);
SHADER_USE_PARAMETER_STRUCT(FOutputCommandInstanceListsCs, FGlobalShader)
public:
static constexpr int32 NumThreadsPerGroup = 64;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
/**
*/
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
FInstanceProcessingGPULoadBalancer::SetShaderDefines(OutEnvironment);
OutEnvironment.SetDefine(TEXT("NUM_THREADS_PER_GROUP"), NumThreadsPerGroup);
OutEnvironment.SetDefine(TEXT("NANITE_MULTI_VIEW"), 1);
OutEnvironment.SetDefine(TEXT("INDIRECT_ARGS_NUM_WORDS"), FInstanceCullingContext::IndirectArgsNumWords);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FVisibleInstanceCmd >, VisibleInstances)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, InstanceIdsBufferOut)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, PageInfoBufferOut)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, TmpInstanceIdOffsetBufferOut)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, VisibleInstanceCountBuffer)
// Needed reference for make RDG happy somehow
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FOutputCommandInstanceListsCs, "/Engine/Private/VirtualShadowMaps/BuildPerPageDrawCommands.usf", "OutputCommandInstanceListsCs", SF_Compute);
struct FCullingResult
{
FRDGBufferRef DrawIndirectArgsRDG;
FRDGBufferRef InstanceIdOffsetBufferRDG;
FRDGBufferRef InstanceIdsBuffer;
FRDGBufferRef PageInfoBuffer;
uint32 MaxNumInstancesPerPass;
};
template <typename InstanceCullingLoadBalancerType>
static FCullingResult AddCullingPasses(FRDGBuilder& GraphBuilder,
const TConstArrayView<FRHIDrawIndexedIndirectParameters> &IndirectArgs,
const TConstArrayView<FInstanceCullingContext::FDrawCommandDesc>& DrawCommandDescs,
const TConstArrayView<uint32>& InstanceIdOffsets,
InstanceCullingLoadBalancerType *LoadBalancer,
const TConstArrayView<FInstanceCullingMergedContext::FContextBatchInfo> BatchInfos,
const TConstArrayView<FVSMCullingBatchInfo> VSMCullingBatchInfos,
const TConstArrayView<uint32> BatchInds,
bool bUseNearClip,
uint32 TotalInstances,
uint32 TotalPrimaryViews,
FRDGBufferRef VirtualShadowViewsRDG,
const FCullPerPageDrawCommandsCs::FHZBShaderParameters &HZBShaderParameters,
FVirtualShadowMapArray *VirtualShadowMapArray,
FGPUScene& GPUScene)
{
int32 NumIndirectArgs = IndirectArgs.Num();
FRDGBufferRef TmpInstanceIdOffsetBufferRDG = CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.TmpInstanceIdOffsetBuffer"), sizeof(uint32), NumIndirectArgs, nullptr, 0);
// TODO: This is both not right, and also over conservative when running with the atomic path
FCullingResult CullingResult;
CullingResult.MaxNumInstancesPerPass = TotalInstances * 64u;
FRDGBufferRef VisibleInstancesRdg = CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.VisibleInstances"), sizeof(FVisibleInstanceCmd), CullingResult.MaxNumInstancesPerPass, nullptr, 0);
FRDGBufferRef VisibleInstanceWriteOffsetRDG = CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.VisibleInstanceWriteOffset"), sizeof(uint32), 1, nullptr, 0);
FRDGBufferRef OutputOffsetBufferRDG = CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.OutputOffsetBuffer"), sizeof(uint32), 1, nullptr, 0);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VisibleInstanceWriteOffsetRDG), 0);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(OutputOffsetBufferRDG), 0);
// Create buffer for indirect args and upload draw arg data, also clears the instance to zero
CullingResult.DrawIndirectArgsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(FInstanceCullingContext::IndirectArgsNumWords * IndirectArgs.Num()), TEXT("Shadow.Virtual.DrawIndirectArgsBuffer"));
GraphBuilder.QueueBufferUpload(CullingResult.DrawIndirectArgsRDG, IndirectArgs.GetData(), IndirectArgs.GetTypeSize() * IndirectArgs.Num());
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
// Note: we redundantly clear the instance counts here as there is some issue with replays on certain consoles.
FInstanceCullingContext::AddClearIndirectArgInstanceCountPass(GraphBuilder, ShaderMap, CullingResult.DrawIndirectArgsRDG);
// not using structured buffer as we have to get at it as a vertex buffer
CullingResult.InstanceIdOffsetBufferRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), InstanceIdOffsets.Num()), TEXT("Shadow.Virtual.InstanceIdOffsetBuffer"));
{
FCullPerPageDrawCommandsCs::FParameters* PassParameters = GraphBuilder.AllocParameters<FCullPerPageDrawCommandsCs::FParameters>();
VirtualShadowMapArray->GetPageTableParameters(GraphBuilder, PassParameters->PageTableParams);
PassParameters->GPUSceneInstanceSceneData = GPUScene.InstanceSceneDataBuffer.SRV;
PassParameters->GPUSceneInstancePayloadData = GPUScene.InstancePayloadDataBuffer.SRV;
PassParameters->GPUScenePrimitiveSceneData = GPUScene.PrimitiveBuffer.SRV;
PassParameters->GPUSceneFrameNumber = GPUScene.GetSceneFrameNumber();
PassParameters->InstanceSceneDataSOAStride = GPUScene.InstanceSceneDataSOAStride;
PassParameters->DynamicInstanceIdOffset = BatchInfos[0].DynamicInstanceIdOffset;
PassParameters->DynamicInstanceIdMax = BatchInfos[0].DynamicInstanceIdMax;
auto GPUData = LoadBalancer->Upload(GraphBuilder);
GPUData.GetShaderParameters(GraphBuilder, PassParameters->LoadBalancerParameters);
PassParameters->FirstPrimaryView = VSMCullingBatchInfos[0].FirstPrimaryView;
PassParameters->NumPrimaryViews = VSMCullingBatchInfos[0].NumPrimaryViews;
PassParameters->TotalPrimaryViews = TotalPrimaryViews;
PassParameters->VisibleInstancesBufferNum = CullingResult.MaxNumInstancesPerPass;
PassParameters->InViews = GraphBuilder.CreateSRV(VirtualShadowViewsRDG);
PassParameters->DrawCommandDescs = GraphBuilder.CreateSRV(CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.DrawCommandDescs"), DrawCommandDescs));
const bool bUseBatchMode = !BatchInds.IsEmpty();
if (bUseBatchMode)
{
PassParameters->BatchInfos = GraphBuilder.CreateSRV(CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.BatchInfos"), BatchInfos));
PassParameters->VSMCullingBatchInfos = GraphBuilder.CreateSRV(CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.VSMCullingBatchInfos"), VSMCullingBatchInfos));
PassParameters->BatchInds = GraphBuilder.CreateSRV(CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.BatchInds"), BatchInds));
}
PassParameters->DrawIndirectArgsBufferOut = GraphBuilder.CreateUAV(CullingResult.DrawIndirectArgsRDG, PF_R32_UINT);
PassParameters->VisibleInstancesOut = GraphBuilder.CreateUAV(VisibleInstancesRdg);
PassParameters->VisibleInstanceCountBufferOut = GraphBuilder.CreateUAV(VisibleInstanceWriteOffsetRDG);
PassParameters->OutInvalidatingInstances = GraphBuilder.CreateUAV(VirtualShadowMapArray->InvalidatingInstancesRDG);
PassParameters->NumInvalidatingInstanceSlots = VirtualShadowMapArray->NumInvalidatingInstanceSlots;
PassParameters->HZBShaderParameters = HZBShaderParameters;
bool bGenerateStats = VirtualShadowMapArray->StatsBufferRDG != nullptr;
if (bGenerateStats)
{
PassParameters->OutStatsBuffer = GraphBuilder.CreateUAV(VirtualShadowMapArray->StatsBufferRDG);
}
FCullPerPageDrawCommandsCs::FPermutationDomain PermutationVector;
PermutationVector.Set< FCullPerPageDrawCommandsCs::FNearClipDim >(bUseNearClip);
PermutationVector.Set< FCullPerPageDrawCommandsCs::FBatchedDim >(bUseBatchMode);
PermutationVector.Set< FCullPerPageDrawCommandsCs::FUseHzbDim >(HZBShaderParameters.HZBTexture != nullptr);
PermutationVector.Set< FCullPerPageDrawCommandsCs::FGenerateStatsDim >(bGenerateStats);
auto ComputeShader = ShaderMap->GetShader<FCullPerPageDrawCommandsCs>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("CullPerPageDrawCommands"),
ComputeShader,
PassParameters,
LoadBalancer->GetWrappedCsGroupCount()
);
}
// 2.2.Allocate space for the final instance ID output and so on.
if (true)
{
FAllocateCommandInstanceOutputSpaceCs::FParameters* PassParameters = GraphBuilder.AllocParameters<FAllocateCommandInstanceOutputSpaceCs::FParameters>();
FRDGBufferRef InstanceIdOutOffsetBufferRDG = CreateStructuredBuffer(GraphBuilder, TEXT("InstanceCulling.OutputOffsetBufferOut"), sizeof(uint32), 1, nullptr, 0);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(InstanceIdOutOffsetBufferRDG), 0);
PassParameters->NumIndirectArgs = NumIndirectArgs;
PassParameters->InstanceIdOffsetBufferOut = GraphBuilder.CreateUAV(CullingResult.InstanceIdOffsetBufferRDG, PF_R32_UINT);;
PassParameters->OutputOffsetBufferOut = GraphBuilder.CreateUAV(InstanceIdOutOffsetBufferRDG);
PassParameters->TmpInstanceIdOffsetBufferOut = GraphBuilder.CreateUAV(TmpInstanceIdOffsetBufferRDG);
PassParameters->DrawIndirectArgsBuffer = GraphBuilder.CreateSRV(CullingResult.DrawIndirectArgsRDG, PF_R32_UINT);
auto ComputeShader = ShaderMap->GetShader<FAllocateCommandInstanceOutputSpaceCs>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("AllocateCommandInstanceOutputSpaceCs"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(NumIndirectArgs, FAllocateCommandInstanceOutputSpaceCs::NumThreadsPerGroup)
);
}
// 2.3. Perform final pass to re-shuffle the instance ID's to their final resting places
CullingResult.InstanceIdsBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), CullingResult.MaxNumInstancesPerPass), TEXT("Shadow.Virtual.InstanceIdsBuffer"));
CullingResult.PageInfoBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), CullingResult.MaxNumInstancesPerPass), TEXT("Shadow.Virtual.PageInfoBuffer"));
FRDGBufferRef OutputPassIndirectArgs = FComputeShaderUtils::AddIndirectArgsSetupCsPass1D(GraphBuilder, VisibleInstanceWriteOffsetRDG, TEXT("Shadow.Virtual.IndirectArgs"), FOutputCommandInstanceListsCs::NumThreadsPerGroup);
if (true)
{
FOutputCommandInstanceListsCs::FParameters* PassParameters = GraphBuilder.AllocParameters<FOutputCommandInstanceListsCs::FParameters>();
PassParameters->VisibleInstances = GraphBuilder.CreateSRV(VisibleInstancesRdg);
PassParameters->PageInfoBufferOut = GraphBuilder.CreateUAV(CullingResult.PageInfoBuffer);
PassParameters->InstanceIdsBufferOut = GraphBuilder.CreateUAV(CullingResult.InstanceIdsBuffer);
PassParameters->TmpInstanceIdOffsetBufferOut = GraphBuilder.CreateUAV(TmpInstanceIdOffsetBufferRDG);
PassParameters->VisibleInstanceCountBuffer = GraphBuilder.CreateSRV(VisibleInstanceWriteOffsetRDG);
PassParameters->IndirectArgs = OutputPassIndirectArgs;
auto ComputeShader = ShaderMap->GetShader<FOutputCommandInstanceListsCs>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("OutputCommandInstanceListsCs"),
ComputeShader,
PassParameters,
OutputPassIndirectArgs,
0
);
}
return CullingResult;
}
static void AddRasterPass(
FRDGBuilder& GraphBuilder,
FRDGEventName&& PassName,
const FViewInfo * ShadowDepthView,
const TRDGUniformBufferRef<FShadowDepthPassUniformParameters> &ShadowDepthPassUniformBuffer,
FVirtualShadowMapArray* VirtualShadowMapArray,
FRDGBufferRef VirtualShadowViewsRDG,
const FCullingResult &CullingResult,
FParallelMeshDrawCommandPass& MeshCommandPass,
FVirtualShadowDepthPassParameters* PassParameters,
TRDGUniformBufferRef<FInstanceCullingGlobalUniforms> InstanceCullingUniformBuffer)
{
PassParameters->View = ShadowDepthView->ViewUniformBuffer;
PassParameters->ShadowDepthPass = ShadowDepthPassUniformBuffer;
PassParameters->VirtualShadowMap = VirtualShadowMapArray->GetUniformBuffer(GraphBuilder);
PassParameters->InViews = GraphBuilder.CreateSRV(VirtualShadowViewsRDG);
PassParameters->InstanceCullingDrawParams.DrawIndirectArgsBuffer = CullingResult.DrawIndirectArgsRDG;
PassParameters->InstanceCullingDrawParams.InstanceIdOffsetBuffer = CullingResult.InstanceIdOffsetBufferRDG;
PassParameters->InstanceCullingDrawParams.InstanceCulling = InstanceCullingUniformBuffer;
FIntRect ViewRect;
ViewRect.Max = FVirtualShadowMap::VirtualMaxResolutionXY;
GraphBuilder.AddPass(
MoveTemp(PassName),
PassParameters,
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
[&MeshCommandPass, PassParameters, ViewRect](FRHICommandList& RHICmdList)
{
FRHIRenderPassInfo RPInfo;
RPInfo.ResolveParameters.DestRect.X1 = ViewRect.Min.X;
RPInfo.ResolveParameters.DestRect.Y1 = ViewRect.Min.Y;
RPInfo.ResolveParameters.DestRect.X2 = ViewRect.Max.X;
RPInfo.ResolveParameters.DestRect.Y2 = ViewRect.Max.Y;
RHICmdList.BeginRenderPass(RPInfo, TEXT("RasterizeVirtualShadowMaps(Non-Nanite)"));
RHICmdList.SetViewport(ViewRect.Min.X, ViewRect.Min.Y, 0.0f, FMath::Min(ViewRect.Max.X, 32767), FMath::Min(ViewRect.Max.Y, 32767), 1.0f);
MeshCommandPass.DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
RHICmdList.EndRenderPass();
});
}
void FVirtualShadowMapArray::RenderVirtualShadowMapsNonNanite(FRDGBuilder& GraphBuilder, const TArray<FProjectedShadowInfo *, SceneRenderingAllocator>& VirtualSmMeshCommandPasses, FScene& Scene)
{
if (VirtualSmMeshCommandPasses.Num() == 0)
{
return;
}
RDG_EVENT_SCOPE(GraphBuilder, "RenderVirtualShadowMaps(Non-Nanite)");
FGPUScene& GPUScene = Scene.GPUScene;
FRDGBufferSRVRef PrevPageTableRDGSRV = CacheManager->IsValid() ? GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.PageTable, TEXT("Shadow.Virtual.PrevPageTable"))) : nullptr;
int32 HZBMode = CVarNonNaniteVsmUseHzb.GetValueOnRenderThread();
auto InitHZB = [&]()->FRDGTextureRef
{
if (HZBMode == 1 && CacheManager->IsValid())
{
return GraphBuilder.RegisterExternalTexture(CacheManager->PrevBuffers.HZBPhysical);
}
if (HZBMode == 2 && HZBPhysical != nullptr)
{
return HZBPhysical;
}
return nullptr;
};
const FRDGTextureRef HZBTexture = InitHZB();
TArray<FVSMCullingBatchInfo, SceneRenderingAllocator> UnBatchedVSMCullingBatchInfo;
TArray<FProjectedShadowInfo*, SceneRenderingAllocator> BatchedVirtualSmMeshCommandPasses;
TArray<FProjectedShadowInfo*, SceneRenderingAllocator> UnBatchedVirtualSmMeshCommandPasses;
UnBatchedVSMCullingBatchInfo.Reserve(VirtualSmMeshCommandPasses.Num());
BatchedVirtualSmMeshCommandPasses.Reserve(VirtualSmMeshCommandPasses.Num());
UnBatchedVirtualSmMeshCommandPasses.Reserve(VirtualSmMeshCommandPasses.Num());
TArray<Nanite::FPackedView, SceneRenderingAllocator> VirtualShadowViews;
TArray<FVSMCullingBatchInfo, SceneRenderingAllocator> VSMCullingBatchInfos;
VSMCullingBatchInfos.Reserve(VirtualSmMeshCommandPasses.Num());
TArray<FVirtualShadowDepthPassParameters*, SceneRenderingAllocator> BatchedPassParameters;
BatchedPassParameters.Reserve(VirtualSmMeshCommandPasses.Num());
FInstanceCullingMergedContext InstanceCullingMergedContext(GMaxRHIFeatureLevel);
// We don't use the registered culling views (this redundancy should probably be addressed at some point), set the number to disable index range checking
InstanceCullingMergedContext.NumCullingViews = -1;
for (int32 Index = 0; Index < VirtualSmMeshCommandPasses.Num(); ++Index)
{
FProjectedShadowInfo* ProjectedShadowInfo = VirtualSmMeshCommandPasses[Index];
ProjectedShadowInfo->BeginRenderView(GraphBuilder, &Scene);
FVSMCullingBatchInfo VSMCullingBatchInfo;
VSMCullingBatchInfo.FirstPrimaryView = uint32(VirtualShadowViews.Num());
VSMCullingBatchInfo.NumPrimaryViews = 0U;
const TSharedPtr<FVirtualShadowMapClipmap> Clipmap = ProjectedShadowInfo->VirtualShadowMapClipmap;
if (Clipmap)
{
VSMCullingBatchInfo.NumPrimaryViews = AddRenderViews(Clipmap, 1.0f, HZBTexture != nullptr, false, VirtualShadowViews);
UnBatchedVSMCullingBatchInfo.Add(VSMCullingBatchInfo);
UnBatchedVirtualSmMeshCommandPasses.Add(ProjectedShadowInfo);
}
else if (ProjectedShadowInfo->HasVirtualShadowMap())
{
FParallelMeshDrawCommandPass& MeshCommandPass = ProjectedShadowInfo->GetShadowDepthPass();
MeshCommandPass.WaitForSetupTask();
FInstanceCullingContext* InstanceCullingContext = MeshCommandPass.GetInstanceCullingContext();
if (InstanceCullingContext->HasCullingCommands())
{
VSMCullingBatchInfo.NumPrimaryViews = AddRenderViews(ProjectedShadowInfo, 1.0f, HZBTexture != nullptr, false, VirtualShadowViews);
if (CVarDoNonNaniteBatching.GetValueOnRenderThread())
{
FViewInfo* ShadowDepthView = ProjectedShadowInfo->ShadowDepthView;
uint32 DynamicInstanceIdOffset = ShadowDepthView->DynamicPrimitiveCollector.GetInstanceSceneDataOffset();
uint32 DynamicInstanceIdMax = DynamicInstanceIdOffset + ShadowDepthView->DynamicPrimitiveCollector.NumInstances();
VSMCullingBatchInfos.Add(VSMCullingBatchInfo);
// Note: we have to allocate these up front as the context merging machinery writes the offsets directly to the &PassParameters->InstanceCullingDrawParams,
// this is a side-effect from sharing the code with the deferred culling. Should probably be refactored.
FVirtualShadowDepthPassParameters* PassParameters = GraphBuilder.AllocParameters<FVirtualShadowDepthPassParameters>();
InstanceCullingMergedContext.AddBatch(GraphBuilder, InstanceCullingContext, DynamicInstanceIdOffset, ShadowDepthView->DynamicPrimitiveCollector.NumInstances(), &PassParameters->InstanceCullingDrawParams);
BatchedVirtualSmMeshCommandPasses.Add(ProjectedShadowInfo);
BatchedPassParameters.Add(PassParameters);
}
else
{
UnBatchedVSMCullingBatchInfo.Add(VSMCullingBatchInfo);
UnBatchedVirtualSmMeshCommandPasses.Add(ProjectedShadowInfo);
}
}
}
}
uint32 TotalPrimaryViews = uint32(VirtualShadowViews.Num());
CreateMipViews(VirtualShadowViews);
FRDGBufferRef VirtualShadowViewsRDG = CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.VirtualShadowViews"), VirtualShadowViews);
// Helper function to create raster pass UB - only really need two of these ever
auto CreateShadowDepthPassUniformBuffer = [this, &VirtualShadowViewsRDG, &GraphBuilder](bool bClampToNearPlane)
{
FShadowDepthPassUniformParameters* ShadowDepthPassParameters = GraphBuilder.AllocParameters<FShadowDepthPassUniformParameters>();
check(PhysicalPagePoolRDG != nullptr);
// TODO: These are not used for this case anyway
ShadowDepthPassParameters->ProjectionMatrix = FMatrix::Identity;
ShadowDepthPassParameters->ViewMatrix = FMatrix::Identity;
ShadowDepthPassParameters->ShadowParams = FVector4f(0.0f, 0.0f, 0.0f, 1.0f);
ShadowDepthPassParameters->bRenderToVirtualShadowMap = true;
ShadowDepthPassParameters->VirtualSmPageTable = GraphBuilder.CreateSRV(PageTableRDG);
ShadowDepthPassParameters->PackedNaniteViews = GraphBuilder.CreateSRV(VirtualShadowViewsRDG);
ShadowDepthPassParameters->PageRectBounds = GraphBuilder.CreateSRV(PageRectBoundsRDG);
ShadowDepthPassParameters->OutDepthBuffer = GraphBuilder.CreateUAV(PhysicalPagePoolRDG, ERDGUnorderedAccessViewFlags::SkipBarrier);
SetupSceneTextureUniformParameters(GraphBuilder, GMaxRHIFeatureLevel, ESceneTextureSetupMode::None, ShadowDepthPassParameters->SceneTextures);
ShadowDepthPassParameters->bClampToNearPlane = bClampToNearPlane;
return GraphBuilder.CreateUniformBuffer(ShadowDepthPassParameters);
};
FCullPerPageDrawCommandsCs::FHZBShaderParameters HZBShaderParameters;
if (HZBTexture)
{
// Mode 2 uses the current frame HZB & page table.
HZBShaderParameters.ShadowHZBPageTable = HZBMode == 2 ? GraphBuilder.CreateSRV(PageTableRDG) : PrevPageTableRDGSRV;
HZBShaderParameters.HZBTexture = HZBTexture;
HZBShaderParameters.HZBSize = HZBTexture->Desc.Extent;
HZBShaderParameters.HZBSampler = TStaticSamplerState< SF_Point, AM_Clamp, AM_Clamp, AM_Clamp >::GetRHI();
HZBShaderParameters.HZBMode = HZBMode;
}
// Process batched passes
if (!InstanceCullingMergedContext.Batches.IsEmpty())
{
RDG_EVENT_SCOPE(GraphBuilder, "Batched");
InstanceCullingMergedContext.MergeBatches();
GraphBuilder.BeginEventScope(RDG_EVENT_NAME("CullingPasses"));
FCullingResult CullingResult = AddCullingPasses(
GraphBuilder,
InstanceCullingMergedContext.IndirectArgs,
InstanceCullingMergedContext.DrawCommandDescs,
InstanceCullingMergedContext.InstanceIdOffsets,
&InstanceCullingMergedContext.LoadBalancers[uint32(EBatchProcessingMode::Generic)],
InstanceCullingMergedContext.BatchInfos,
VSMCullingBatchInfos,
InstanceCullingMergedContext.BatchInds[uint32(EBatchProcessingMode::Generic)],
true,
InstanceCullingMergedContext.TotalInstances,
TotalPrimaryViews,
VirtualShadowViewsRDG,
HZBShaderParameters,
this,
GPUScene
);
GraphBuilder.EndEventScope();
TRDGUniformBufferRef<FShadowDepthPassUniformParameters> ShadowDepthPassUniformBuffer = CreateShadowDepthPassUniformBuffer(false);
FInstanceCullingGlobalUniforms* InstanceCullingGlobalUniforms = GraphBuilder.AllocParameters<FInstanceCullingGlobalUniforms>();
InstanceCullingGlobalUniforms->InstanceIdsBuffer = GraphBuilder.CreateSRV(CullingResult.InstanceIdsBuffer);
InstanceCullingGlobalUniforms->PageInfoBuffer = GraphBuilder.CreateSRV(CullingResult.PageInfoBuffer);
InstanceCullingGlobalUniforms->BufferCapacity = CullingResult.MaxNumInstancesPerPass;
TRDGUniformBufferRef<FInstanceCullingGlobalUniforms> InstanceCullingUniformBuffer = GraphBuilder.CreateUniformBuffer(InstanceCullingGlobalUniforms);
{
RDG_EVENT_SCOPE(GraphBuilder, "RasterPasses");
for (int Index = 0; Index < BatchedVirtualSmMeshCommandPasses.Num(); ++Index)
{
FProjectedShadowInfo* ProjectedShadowInfo = BatchedVirtualSmMeshCommandPasses[Index];
FParallelMeshDrawCommandPass& MeshCommandPass = ProjectedShadowInfo->GetShadowDepthPass();
FViewInfo* ShadowDepthView = ProjectedShadowInfo->ShadowDepthView;
// Local lights are assumed to not use the clamp to near-plane (this is used for some per-object SMs but these should never be used fotr VSM).
check(!ProjectedShadowInfo->ShouldClampToNearPlane());
FString LightNameWithLevel;
FSceneRenderer::GetLightNameForDrawEvent(ProjectedShadowInfo->GetLightSceneInfo().Proxy, LightNameWithLevel);
AddRasterPass(GraphBuilder, RDG_EVENT_NAME("Rasterize[%s]", *LightNameWithLevel), ShadowDepthView, ShadowDepthPassUniformBuffer, this, VirtualShadowViewsRDG, CullingResult, MeshCommandPass, BatchedPassParameters[Index], InstanceCullingUniformBuffer);
}
}
}
// Loop over the un batched mesh command passes needed, these are all the clipmaps (but we may change the criteria)
for (int Index = 0; Index < UnBatchedVirtualSmMeshCommandPasses.Num(); ++Index)
{
const auto VSMCullingBatchInfo = UnBatchedVSMCullingBatchInfo[Index];
FProjectedShadowInfo* ProjectedShadowInfo = UnBatchedVirtualSmMeshCommandPasses[Index];
FInstanceCullingMergedContext::FContextBatchInfo CullingBatchInfo = FInstanceCullingMergedContext::FContextBatchInfo{ 0 };
FParallelMeshDrawCommandPass& MeshCommandPass = ProjectedShadowInfo->GetShadowDepthPass();
const TSharedPtr<FVirtualShadowMapClipmap> Clipmap = ProjectedShadowInfo->VirtualShadowMapClipmap;
FViewInfo* ShadowDepthView = ProjectedShadowInfo->ShadowDepthView;
MeshCommandPass.WaitForSetupTask();
FInstanceCullingContext* InstanceCullingContext = MeshCommandPass.GetInstanceCullingContext();
if (InstanceCullingContext->HasCullingCommands())
{
FString LightNameWithLevel;
FSceneRenderer::GetLightNameForDrawEvent(ProjectedShadowInfo->GetLightSceneInfo().Proxy, LightNameWithLevel);
RDG_EVENT_SCOPE(GraphBuilder, "%s", *LightNameWithLevel);
CullingBatchInfo.DynamicInstanceIdOffset = ShadowDepthView->DynamicPrimitiveCollector.GetInstanceSceneDataOffset();
CullingBatchInfo.DynamicInstanceIdMax = CullingBatchInfo.DynamicInstanceIdOffset + ShadowDepthView->DynamicPrimitiveCollector.NumInstances();
FCullingResult CullingResult = AddCullingPasses(
GraphBuilder,
InstanceCullingContext->IndirectArgs,
InstanceCullingContext->DrawCommandDescs,
InstanceCullingContext->InstanceIdOffsets,
InstanceCullingContext->LoadBalancers[uint32(EBatchProcessingMode::Generic)],
MakeArrayView(&CullingBatchInfo, 1),
MakeArrayView(&VSMCullingBatchInfo, 1),
MakeArrayView<const uint32>(nullptr, 0),
!Clipmap.IsValid(),
InstanceCullingContext->TotalInstances,
TotalPrimaryViews,
VirtualShadowViewsRDG,
HZBShaderParameters,
this,
GPUScene
);
TRDGUniformBufferRef<FShadowDepthPassUniformParameters> ShadowDepthPassUniformBuffer = CreateShadowDepthPassUniformBuffer(ProjectedShadowInfo->ShouldClampToNearPlane());
FInstanceCullingGlobalUniforms* InstanceCullingGlobalUniforms = GraphBuilder.AllocParameters<FInstanceCullingGlobalUniforms>();
InstanceCullingGlobalUniforms->InstanceIdsBuffer = GraphBuilder.CreateSRV(CullingResult.InstanceIdsBuffer);
InstanceCullingGlobalUniforms->PageInfoBuffer = GraphBuilder.CreateSRV(CullingResult.PageInfoBuffer);
InstanceCullingGlobalUniforms->BufferCapacity = CullingResult.MaxNumInstancesPerPass;
TRDGUniformBufferRef<FInstanceCullingGlobalUniforms> InstanceCullingUniformBuffer = GraphBuilder.CreateUniformBuffer(InstanceCullingGlobalUniforms);
FVirtualShadowDepthPassParameters* DepthPassParams = GraphBuilder.AllocParameters<FVirtualShadowDepthPassParameters>();
DepthPassParams->InstanceCullingDrawParams.IndirectArgsByteOffset = 0;
DepthPassParams->InstanceCullingDrawParams.InstanceDataByteOffset = 0;
AddRasterPass(GraphBuilder, RDG_EVENT_NAME("Rasterize"), ShadowDepthView, ShadowDepthPassUniformBuffer, this, VirtualShadowViewsRDG, CullingResult, MeshCommandPass, DepthPassParams, InstanceCullingUniformBuffer);
}
//
if (Index == CVarShowClipmapStats.GetValueOnRenderThread())
{
// The 'main' view the shadow was created with respect to
const FViewInfo* ViewUsedToCreateShadow = ProjectedShadowInfo->DependentView;
const FViewInfo& View = *ViewUsedToCreateShadow;
FVirtualSmPrintClipmapStatsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVirtualSmPrintClipmapStatsCS::FParameters>();
ShaderPrint::SetParameters(GraphBuilder, View, PassParameters->ShaderPrintStruct);
//PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->ShadowMapIdRangeStart = Clipmap->GetVirtualShadowMap(0)->ID;
// Note: assumes range!
PassParameters->ShadowMapIdRangeEnd = Clipmap->GetVirtualShadowMap(0)->ID + Clipmap->GetLevelCount();
PassParameters->PageRectBounds = GraphBuilder.CreateSRV(PageRectBoundsRDG);
PassParameters->AllocatedPageRectBounds = GraphBuilder.CreateSRV(AllocatedPageRectBoundsRDG);
auto ComputeShader = View.ShaderMap->GetShader<FVirtualSmPrintClipmapStatsCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("PrintClipmapStats"),
ComputeShader,
PassParameters,
FIntVector(1, 1, 1)
);
}
}
}
class FSelectPagesForHZBCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FSelectPagesForHZBCS);
SHADER_USE_PARAMETER_STRUCT(FSelectPagesForHZBCS, FVirtualPageManagementShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FPhysicalPageMetaData>, PhysicalPageMetaData)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, OutPagesForHZBIndirectArgsBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutPhysicalPagesForHZB)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FSelectPagesForHZBCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "SelectPagesForHZBCS", SF_Compute);
class FVirtualSmBuildHZBPerPageCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FVirtualSmBuildHZBPerPageCS);
SHADER_USE_PARAMETER_STRUCT(FVirtualSmBuildHZBPerPageCS, FVirtualPageManagementShader)
static constexpr uint32 TotalHZBLevels = FVirtualShadowMap::Log2PageSize;
static constexpr uint32 HZBLevelsBase = TotalHZBLevels - 2U;
static_assert(HZBLevelsBase == 5U, "The shader is expecting 5 levels, if the page size is changed, this needs to be massaged");
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, PhysicalPagesForHZB)
SHADER_PARAMETER_SAMPLER(SamplerState, PhysicalPagePoolSampler)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint>, PhysicalPagePool)
SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(RWTexture2D<float>, FurthestHZBOutput, [HZBLevelsBase])
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FVirtualSmBuildHZBPerPageCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "BuildHZBPerPageCS", SF_Compute);
class FVirtualSmBBuildHZBPerPageTopCS : public FVirtualPageManagementShader
{
DECLARE_GLOBAL_SHADER(FVirtualSmBBuildHZBPerPageTopCS);
SHADER_USE_PARAMETER_STRUCT(FVirtualSmBBuildHZBPerPageTopCS, FVirtualPageManagementShader)
// We need one level less as HZB starts at half-size (not really sure if we really need 1x1 and 2x2 sized levels).
static constexpr uint32 HZBLevelsTop = 2;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, PhysicalPagesForHZB)
SHADER_PARAMETER_SAMPLER(SamplerState, ParentTextureMipSampler)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, ParentTextureMip)
SHADER_PARAMETER(FVector2f, InvHzbInputSize)
SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(RWTexture2D<float>, FurthestHZBOutput, [HZBLevelsTop])
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FVirtualSmBBuildHZBPerPageTopCS, "/Engine/Private/VirtualShadowMaps/PageManagement.usf", "BuildHZBPerPageTopCS", SF_Compute);
FRDGTextureRef FVirtualShadowMapArray::BuildHZBFurthest(FRDGBuilder& GraphBuilder)
{
const FIntRect ViewRect(0, 0, GetPhysicalPoolSize().X, GetPhysicalPoolSize().Y);
const EPixelFormat Format = CVarVSMHZB32Bit.GetValueOnRenderThread() ? PF_R32_FLOAT : PF_R16F;
FRDGTextureRef OutFurthestHZBTexture = nullptr;
if (CVarVSMBuildHZBPerPage.GetValueOnRenderThread())
{
// 1. Gather up all physical pages that are allocated
FRDGBufferRef PagesForHZBIndirectArgsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(2U * 4U), TEXT("Shadow.Virtual.PagesForHZBIndirectArgs"));
// NOTE: Total allocated pages since the shader outputs separate entries for static/dynamic pages
FRDGBufferRef PhysicalPagesForHZBRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(int32), GetTotalAllocatedPhysicalPages() + 1), TEXT("Shadow.Virtual.PhysicalPagesForHZB"));
// 1. Clear the indirect args buffer (note 2x args)
AddClearIndirectDispatchArgs1DPass(GraphBuilder, PagesForHZBIndirectArgsRDG, 2U);
// 2. Filter the relevant physical pages and set up the indirect args
{
FSelectPagesForHZBCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSelectPagesForHZBCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
PassParameters->PhysicalPageMetaData = GraphBuilder.CreateSRV(PhysicalPageMetaDataRDG);
PassParameters->OutPagesForHZBIndirectArgsBuffer = GraphBuilder.CreateUAV(PagesForHZBIndirectArgsRDG);
PassParameters->OutPhysicalPagesForHZB = GraphBuilder.CreateUAV(PhysicalPagesForHZBRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FSelectPagesForHZBCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SelectPagesForHZB"),
ComputeShader,
PassParameters,
FIntVector(FMath::DivideAndRoundUp(UniformParameters.MaxPhysicalPages, FSelectPagesForHZBCS::DefaultCSGroupX), 1, 1)
);
}
//FIntPoint HZBSize(GetPhysicalPoolSize().X / 2, GetPhysicalPoolSize().Y / 2);
FIntPoint PhysicalPoolSize = GetPhysicalPoolSize();
FIntPoint HZBSize;
//HZBSize = FIntPoint(PhysicalPoolSize.X / 2, PhysicalPoolSize.Y / 2);
// NOTE: IsVisibleHZB currently assumes HZB is pow2 size, so force that. See HZBCull.ush.
HZBSize.X = FMath::Max( FPlatformMath::RoundUpToPowerOfTwo( PhysicalPoolSize.X ) >> 1, 1u );
HZBSize.Y = FMath::Max( FPlatformMath::RoundUpToPowerOfTwo( PhysicalPoolSize.Y ) >> 1, 1u );
FRDGTextureDesc HZBDesc = FRDGTextureDesc::Create2D(HZBSize, Format, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV, FVirtualSmBuildHZBPerPageCS::TotalHZBLevels);
HZBDesc.Flags |= GFastVRamConfig.HZB;
/** Closest and furthest HZB are intentionally in separate render target, because majority of the case you only one or the other.
* Keeping them separate avoid doubling the size in cache for this cases, to avoid performance regression.
*/
OutFurthestHZBTexture = GraphBuilder.CreateTexture(HZBDesc, TEXT("Shadow.Virtual.PreviousOccluderHZB"));
{
FVirtualSmBuildHZBPerPageCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVirtualSmBuildHZBPerPageCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
for (int32 DestMip = 0; DestMip < FVirtualSmBuildHZBPerPageCS::HZBLevelsBase; DestMip++)
{
PassParameters->FurthestHZBOutput[DestMip] = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(OutFurthestHZBTexture, DestMip));
}
PassParameters->PhysicalPagePool = PhysicalPagePoolRDG;
PassParameters->PhysicalPagePoolSampler = TStaticSamplerState<SF_Point>::GetRHI();
PassParameters->IndirectArgs = PagesForHZBIndirectArgsRDG;
PassParameters->PhysicalPagesForHZB = GraphBuilder.CreateSRV(PhysicalPagesForHZBRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FVirtualSmBuildHZBPerPageCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("BuildHZBPerPage"),
ComputeShader,
PassParameters,
PassParameters->IndirectArgs,
0
);
}
{
FVirtualSmBBuildHZBPerPageTopCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVirtualSmBBuildHZBPerPageTopCS::FParameters>();
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
uint32 StartDestMip = FVirtualSmBuildHZBPerPageCS::HZBLevelsBase;
for (int32 DestMip = 0; DestMip < FVirtualSmBBuildHZBPerPageTopCS::HZBLevelsTop; DestMip++)
{
PassParameters->FurthestHZBOutput[DestMip] = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(OutFurthestHZBTexture, StartDestMip + DestMip));
}
FIntPoint SrcSize = FIntPoint::DivideAndRoundUp(HZBSize, 1 << int32(StartDestMip - 1));
PassParameters->InvHzbInputSize = FVector2f(1.0f / SrcSize.X, 1.0f / SrcSize.Y);;
PassParameters->ParentTextureMip = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(OutFurthestHZBTexture, StartDestMip - 1));
PassParameters->ParentTextureMipSampler = TStaticSamplerState<SF_Point>::GetRHI();
PassParameters->IndirectArgs = PagesForHZBIndirectArgsRDG;
PassParameters->PhysicalPagesForHZB = GraphBuilder.CreateSRV(PhysicalPagesForHZBRDG);
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FVirtualSmBBuildHZBPerPageTopCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("BuildHZBPerPageTop"),
ComputeShader,
PassParameters,
PassParameters->IndirectArgs,
// NOTE: offset 4 to get second set of args in the buffer.
4U
);
}
}
else
{
::BuildHZBFurthest(
GraphBuilder,
FRDGSystemTextures::Get(GraphBuilder).Black,
PhysicalPagePoolRDG,
ViewRect,
GMaxRHIFeatureLevel,
GMaxRHIShaderPlatform,
TEXT("Shadow.Virtual.PreviousOccluderHZB"),
/* OutFurthestHZBTexture = */ &OutFurthestHZBTexture,
Format);
}
return OutFurthestHZBTexture;
}
uint32 FVirtualShadowMapArray::AddRenderViews(const TSharedPtr<FVirtualShadowMapClipmap>& Clipmap, float LODScaleFactor, bool bSetHzbParams, bool bUpdateHZBMetaData, TArray<Nanite::FPackedView, SceneRenderingAllocator> &OutVirtualShadowViews)
{
// TODO: Decide if this sort of logic belongs here or in Nanite (as with the mip level view expansion logic)
// We're eventually going to want to snap/quantize these rectangles/positions somewhat so probably don't want it
// entirely within Nanite, but likely makes sense to have some sort of "multi-viewport" notion in Nanite that can
// handle both this and mips.
// NOTE: There's still the additional VSM view logic that runs on top of this in Nanite too (see CullRasterize variant)
Nanite::FPackedViewParams BaseParams;
BaseParams.ViewRect = FIntRect(0, 0, FVirtualShadowMap::VirtualMaxResolutionXY, FVirtualShadowMap::VirtualMaxResolutionXY);
BaseParams.HZBTestViewRect = BaseParams.ViewRect;
BaseParams.RasterContextSize = GetPhysicalPoolSize();
BaseParams.LODScaleFactor = LODScaleFactor;
BaseParams.PrevTargetLayerIndex = INDEX_NONE;
BaseParams.TargetMipLevel = 0;
BaseParams.TargetMipCount = 1; // No mips for clipmaps
for (int32 ClipmapLevelIndex = 0; ClipmapLevelIndex < Clipmap->GetLevelCount(); ++ClipmapLevelIndex)
{
FVirtualShadowMap* VirtualShadowMap = Clipmap->GetVirtualShadowMap(ClipmapLevelIndex);
Nanite::FPackedViewParams Params = BaseParams;
Params.TargetLayerIndex = VirtualShadowMap->ID;
Params.ViewMatrices = Clipmap->GetViewMatrices(ClipmapLevelIndex);
Params.PrevTargetLayerIndex = INDEX_NONE;
Params.PrevViewMatrices = Params.ViewMatrices;
Params.Flags = 0;
// TODO: Clean this up - could be stored in a single structure for the whole clipmap
int32 HZBKey = Clipmap->GetHZBKey(ClipmapLevelIndex);
if (bSetHzbParams)
{
CacheManager->SetHZBViewParams(HZBKey, Params);
}
// If we're going to generate a new HZB this frame, save the associated metadata
if (bUpdateHZBMetaData)
{
FVirtualShadowMapHZBMetadata& HZBMeta = HZBMetadata.FindOrAdd(HZBKey);
HZBMeta.TargetLayerIndex = Params.TargetLayerIndex;
HZBMeta.ViewMatrices = Params.ViewMatrices;
HZBMeta.ViewRect = Params.ViewRect;
}
Nanite::FPackedView View = Nanite::CreatePackedView(Params);
OutVirtualShadowViews.Add(View);
// Mark that we rendered to this VSM for caching purposes
if (VirtualShadowMap->VirtualShadowMapCacheEntry)
{
VirtualShadowMap->VirtualShadowMapCacheEntry->MarkRendered();
}
}
return uint32(Clipmap->GetLevelCount());
}
uint32 FVirtualShadowMapArray::AddRenderViews(const FProjectedShadowInfo* ProjectedShadowInfo, float LODScaleFactor, bool bSetHzbParams, bool bUpdateHZBMetaData, TArray<Nanite::FPackedView, SceneRenderingAllocator>& OutVirtualShadowViews)
{
Nanite::FPackedViewParams BaseParams;
BaseParams.ViewRect = ProjectedShadowInfo->GetOuterViewRect();
BaseParams.HZBTestViewRect = BaseParams.ViewRect;
BaseParams.RasterContextSize = GetPhysicalPoolSize();
BaseParams.LODScaleFactor = LODScaleFactor;
BaseParams.PrevTargetLayerIndex = INDEX_NONE;
BaseParams.TargetMipLevel = 0;
BaseParams.TargetMipCount = FVirtualShadowMap::MaxMipLevels;
int32 NumMaps = ProjectedShadowInfo->bOnePassPointLightShadow ? 6 : 1;
for (int32 Index = 0; Index < NumMaps; ++Index)
{
FVirtualShadowMap* VirtualShadowMap = ProjectedShadowInfo->VirtualShadowMaps[Index];
Nanite::FPackedViewParams Params = BaseParams;
Params.TargetLayerIndex = VirtualShadowMap->ID;
Params.ViewMatrices = ProjectedShadowInfo->GetShadowDepthRenderingViewMatrices(Index, true);
int32 HZBKey = ProjectedShadowInfo->GetLightSceneInfo().Id + (Index << 24);
if (bSetHzbParams)
{
CacheManager->SetHZBViewParams(HZBKey, Params);
}
// If we're going to generate a new HZB this frame, save the associated metadata
if (bUpdateHZBMetaData)
{
FVirtualShadowMapHZBMetadata& HZBMeta = HZBMetadata.FindOrAdd(HZBKey);
HZBMeta.TargetLayerIndex = Params.TargetLayerIndex;
HZBMeta.ViewMatrices = Params.ViewMatrices;
HZBMeta.ViewRect = Params.ViewRect;
}
OutVirtualShadowViews.Add(Nanite::CreatePackedView(Params));
if (VirtualShadowMap->VirtualShadowMapCacheEntry)
{
VirtualShadowMap->VirtualShadowMapCacheEntry->MarkRendered();
}
}
return uint32(NumMaps);
}