You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rnx #rb jamie.hayes #preflight 628739252c34da686abc3088 #jira UE-151665 [CL 20291919 by Ola Olsson in ue5-main branch]
2856 lines
133 KiB
C++
2856 lines
133 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
/*=============================================================================
|
|
VirtualShadowMap.h:
|
|
=============================================================================*/
|
|
#include "VirtualShadowMapArray.h"
|
|
#include "VirtualShadowMapVisualizationData.h"
|
|
#include "VirtualShadowMapDefinitions.h"
|
|
#include "../BasePassRendering.h"
|
|
#include "../ScreenPass.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 "GPUMessaging.h"
|
|
#include "InstanceCulling/InstanceCullingMergedContext.h"
|
|
|
|
#define DEBUG_ALLOW_STATIC_SEPARATE_WITHOUT_CACHING 0
|
|
|
|
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;
|
|
uint32 VirtualPageOffset;
|
|
};
|
|
|
|
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_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
TAutoConsoleVariable<int32> CVarCacheStaticSeparate(
|
|
TEXT("r.Shadow.Virtual.Cache.StaticSeparate"),
|
|
1,
|
|
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> 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_Scalability | 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_Scalability | 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_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
TAutoConsoleVariable<int32> CVarCoarsePagesIncludeNonNanite(
|
|
TEXT("r.Shadow.Virtual.NonNanite.IncludeInCoarsePages"),
|
|
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_Scalability | 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_Scalability | 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.Visualize.DumpLightNames"),
|
|
TEXT("Dump light names with virtual shadow maps (for developer use in non-shiping builds)"),
|
|
FConsoleCommandDelegate::CreateStatic(DumpVSMLightNames)
|
|
);
|
|
|
|
FString GVirtualShadowMapVisualizeLightName;
|
|
FAutoConsoleVariableRef CVarVisualizeLightName(
|
|
TEXT("r.Shadow.Virtual.Visualize.LightName"),
|
|
GVirtualShadowMapVisualizeLightName,
|
|
TEXT("Sets the name of a specific light to visualize (for developer use in non-shiping builds)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarVisualizeLayout(
|
|
TEXT("r.Shadow.Virtual.Visualize.Layout"),
|
|
0,
|
|
TEXT("Overlay layout when virtual shadow map visualization is enabled:\n")
|
|
TEXT(" 0: Full screen\n")
|
|
TEXT(" 1: Thumbnail\n")
|
|
TEXT(" 2: Split screen"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
TAutoConsoleVariable<int32> CVarDebugSkipMergePhysical(
|
|
TEXT("r.Shadow.Virtual.DebugSkipMergePhysical"),
|
|
0,
|
|
TEXT(""),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
TAutoConsoleVariable<int32> CVarDebugSkipDynamicPageInvalidation(
|
|
TEXT("r.Shadow.Virtual.Cache.DebugSkipDynamicPageInvalidation"),
|
|
0,
|
|
TEXT("Skip invalidation of cached pages when geometry moves for debugging purposes. This will create obvious visual artifacts when disabled.\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
#endif // !UE_BUILD_SHIPPING
|
|
|
|
static TAutoConsoleVariable<float> CVarMaxMaterialPositionInvalidationRange(
|
|
TEXT("r.Shadow.Virtual.Cache.MaxMaterialPositionInvalidationRange"),
|
|
-1.0f,
|
|
TEXT("Beyond this distance in world units, material position effects (e.g., WPO or PDO) cease to cause VSM invalidations.\n")
|
|
TEXT(" This can be used to tune performance by reducing re-draw overhead, but causes some artifacts.\n")
|
|
TEXT(" < 0 <=> infinite (default)"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarShadowsVirtualUseHZB(
|
|
TEXT("r.Shadow.Virtual.UseHZB"),
|
|
2,
|
|
TEXT("Enables HZB for (Nanite) Virtual Shadow Maps - Non-Nanite unfortunately has a separate flag with different semantics: r.Shadow.Virtual.NonNanite.UseHZB.\n")
|
|
TEXT(" 0 - No HZB occlusion culling\n")
|
|
TEXT(" 1 - Approximate Single-pass HZB occlusion culling (using previous frame HZB)\n")
|
|
TEXT(" 2 - Two-pass occlusion culling (default)."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarShadowsVirtualForceFullHZBUpdate(
|
|
TEXT("r.Shadow.Virtual.ForceFullHZBUpdate"),
|
|
0,
|
|
TEXT("Forces full HZB update every frame rather than just dirty pages.\n"),
|
|
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();
|
|
}
|
|
|
|
|
|
template <typename ShaderType>
|
|
static bool SetStatsArgsAndPermutation(FRDGBuilder& GraphBuilder, FRDGBufferRef StatsBufferRDG, typename ShaderType::FParameters *OutPassParameters, typename ShaderType::FPermutationDomain& OutPermutationVector)
|
|
{
|
|
bool bGenerateStats = StatsBufferRDG != nullptr;
|
|
if (bGenerateStats)
|
|
{
|
|
OutPassParameters->OutStatsBuffer = GraphBuilder.CreateUAV(StatsBufferRDG);
|
|
}
|
|
;
|
|
OutPermutationVector.template Set<typename ShaderType::FGenerateStatsDim>(bGenerateStats);
|
|
|
|
return bGenerateStats;
|
|
}
|
|
|
|
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)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV( StructuredBuffer< uint >, PrevProjectionData)
|
|
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")));
|
|
CacheDataParameters.PrevProjectionData = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.ProjectionData, TEXT("Shadow.Virtual.PrevProjectionData")));
|
|
}
|
|
|
|
static FRDGBufferRef CreateProjectionDataBuffer(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TCHAR* Name,
|
|
const TArray<FVirtualShadowMapProjectionShaderData, SceneRenderingAllocator>& InitialData)
|
|
{
|
|
uint64 DataSize = InitialData.Num() * InitialData.GetTypeSize();
|
|
|
|
FRDGBufferDesc Desc;
|
|
Desc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::ByteAddressBuffer | EBufferUsageFlags::StructuredBuffer;
|
|
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;
|
|
|
|
bCullBackfacingPixels = CVarCullBackfacingPixels.GetValueOnRenderThread() != 0;
|
|
bUseHzbOcclusion = CVarShadowsVirtualUseHZB.GetValueOnRenderThread() != 0;
|
|
bUseTwoPassHzbOcclusion = CVarShadowsVirtualUseHZB.GetValueOnRenderThread() == 2;
|
|
|
|
UniformParameters.NumShadowMaps = 0;
|
|
UniformParameters.NumDirectionalLights = 0;
|
|
UniformParameters.MaxPhysicalPages = 0;
|
|
UniformParameters.StaticCachedArrayIndex = 0;
|
|
// NOTE: Most uniform values don't matter when VSM is disabled
|
|
|
|
// Reference dummy data in the UB initially
|
|
const uint32 DummyPageTableElement = 0xFFFFFFFF;
|
|
UniformParameters.PageTable = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(DummyPageTableElement), DummyPageTableElement));
|
|
UniformParameters.ProjectionData = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FVirtualShadowMapProjectionShaderData)));
|
|
UniformParameters.PageFlags = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(uint32)));
|
|
UniformParameters.PageRectBounds = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FIntVector4)));
|
|
|
|
if (bEnabled)
|
|
{
|
|
// 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
|
|
// NOTE: This assumes GetMax2DTextureDimension() is a power of two on supported platforms
|
|
const uint32 PhysicalPagesX = FMath::DivideAndRoundDown(GetMax2DTextureDimension(), FVirtualShadowMap::PageSize);
|
|
check(FMath::IsPowerOfTwo(PhysicalPagesX));
|
|
uint32 PhysicalPagesY = FMath::DivideAndRoundUp((uint32)FMath::Max(1, CVarMaxPhysicalPages.GetValueOnRenderThread()), PhysicalPagesX);
|
|
|
|
UniformParameters.MaxPhysicalPages = PhysicalPagesX * PhysicalPagesY;
|
|
|
|
if (CVarCacheStaticSeparate.GetValueOnRenderThread() != 0)
|
|
{
|
|
#if !DEBUG_ALLOW_STATIC_SEPARATE_WITHOUT_CACHING
|
|
if (CacheManager->IsValid())
|
|
#endif
|
|
{
|
|
// Enable separate static caching in the second texture array element
|
|
UniformParameters.StaticCachedArrayIndex = 1;
|
|
}
|
|
}
|
|
|
|
uint32 PhysicalX = PhysicalPagesX * FVirtualShadowMap::PageSize;
|
|
uint32 PhysicalY = PhysicalPagesY * FVirtualShadowMap::PageSize;
|
|
|
|
// TODO: Some sort of better fallback with warning?
|
|
// All supported platforms support at least 16384 texture dimensions which translates to 16384 max pages with default 128x128 page size
|
|
check(PhysicalX <= GetMax2DTextureDimension());
|
|
check(PhysicalY <= GetMax2DTextureDimension());
|
|
|
|
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);
|
|
|
|
// 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
|
|
const int PoolArraySize = ShouldCacheStaticSeparately() ? 2 : 1;
|
|
TRefCountPtr<IPooledRenderTarget> PhysicalPagePool = CacheManager->SetPhysicalPoolSize(GraphBuilder, GetPhysicalPoolSize(), PoolArraySize);
|
|
PhysicalPagePoolRDG = GraphBuilder.RegisterExternalTexture(PhysicalPagePool);
|
|
UniformParameters.PhysicalPagePool = PhysicalPagePoolRDG;
|
|
}
|
|
else
|
|
{
|
|
CacheManager->FreePhysicalPool();
|
|
UniformParameters.PhysicalPagePool = GSystemTextures.GetZeroUIntArrayDummy(GraphBuilder);
|
|
}
|
|
|
|
if (bEnabled && bUseHzbOcclusion)
|
|
{
|
|
TRefCountPtr<IPooledRenderTarget> HzbPhysicalPagePool = CacheManager->SetHZBPhysicalPoolSize(GraphBuilder, GetHZBPhysicalPoolSize(), PF_R32_FLOAT);
|
|
HZBPhysical = GraphBuilder.RegisterExternalTexture(HzbPhysicalPagePool);
|
|
}
|
|
else
|
|
{
|
|
CacheManager->FreeHZBPhysicalPool();
|
|
HZBPhysical = 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::GetHZBPhysicalPoolSize() const
|
|
{
|
|
check(bInitialized);
|
|
FIntPoint PhysicalPoolSize = GetPhysicalPoolSize();
|
|
FIntPoint HZBSize(FMath::Max(FPlatformMath::RoundUpToPowerOfTwo(PhysicalPoolSize.X) >> 1, 1u),
|
|
FMath::Max(FPlatformMath::RoundUpToPowerOfTwo(PhysicalPoolSize.Y) >> 1, 1u));
|
|
return HZBSize;
|
|
}
|
|
|
|
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 + NANITE_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_RDG_UNIFORM_BUFFER(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)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FStrataGlobalUniformParameters, Strata)
|
|
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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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>, OutPageFlags)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FIntVector4>, OutPageRectBounds)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
};
|
|
IMPLEMENT_GLOBAL_SHADER(FGenerateHierarchicalPageFlagsCS, "/Engine/Private/VirtualShadowMaps/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.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);
|
|
|
|
FSelectPagesToMergeCS::FPermutationDomain PermutationVector;
|
|
SetStatsArgsAndPermutation<FSelectPagesToMergeCS>(GraphBuilder, StatsBufferRDG, PassParameters, PermutationVector);
|
|
|
|
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 PoolSize = GetPhysicalPoolSize();
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("MergeStaticPhysicalPages"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FIntVector(
|
|
FMath::DivideAndRoundUp(PoolSize.X, 16),
|
|
FMath::DivideAndRoundUp(PoolSize.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>, OutPageRectBounds)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
};
|
|
IMPLEMENT_GLOBAL_SHADER(FInitPageRectBoundsCS, "/Engine/Private/VirtualShadowMaps/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.usf", "FeedbackStatusCS", SF_Compute);
|
|
|
|
void FVirtualShadowMapVisualizeLightSearch::CheckLight(const FLightSceneProxy* CheckProxy, int CheckVirtualShadowMapId)
|
|
{
|
|
#if !UE_BUILD_SHIPPING
|
|
FString CheckLightName = CheckProxy->GetOwnerNameOrLabel();
|
|
if (GDumpVSMLightNames)
|
|
{
|
|
UE_LOG(LogRenderer, Display, TEXT("%s"), *CheckLightName);
|
|
}
|
|
|
|
const ULightComponent* Component = CheckProxy->GetLightComponent();
|
|
check(Component);
|
|
|
|
// Fill out new sort key and compare to our best found so far
|
|
SortKey CheckKey;
|
|
CheckKey.Packed = 0;
|
|
CheckKey.Fields.bExactNameMatch = (CheckLightName == GVirtualShadowMapVisualizeLightName);
|
|
CheckKey.Fields.bPartialNameMatch = CheckKey.Fields.bExactNameMatch || CheckLightName.Contains(GVirtualShadowMapVisualizeLightName);
|
|
CheckKey.Fields.bSelected = Component->IsSelected();
|
|
CheckKey.Fields.bOwnerSelected = Component->IsOwnerSelected();
|
|
CheckKey.Fields.bDirectionalLight = CheckProxy->GetLightType() == LightType_Directional;
|
|
CheckKey.Fields.bExists = 1;
|
|
|
|
if (CheckKey.Packed > FoundKey.Packed) //-V547
|
|
{
|
|
FoundKey = CheckKey;
|
|
FoundProxy = CheckProxy;
|
|
FoundVirtualShadowMapId = CheckVirtualShadowMapId;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static FRDGTextureRef CreateDebugVisualizationTexture(FRDGBuilder& GraphBuilder, FIntPoint Extent)
|
|
{
|
|
const FLinearColor ClearColor(0.0f, 0.0f, 0.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;
|
|
}
|
|
|
|
static uint32 GetShadowMapsToAllocate(uint32 NumShadowMaps)
|
|
{
|
|
// Round up to powers of two to be friendlier to the buffer pool
|
|
return FMath::RoundUpToPowerOfTwo(FMath::Max(64U, NumShadowMaps));
|
|
}
|
|
|
|
void FVirtualShadowMapArray::BuildPageAllocations(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
const TArrayView<FViewInfo>& Views,
|
|
const FEngineShowFlags& EngineShowFlags,
|
|
const FSortedLightSetSceneInfo& SortedLightsInfo,
|
|
const TArray<FVisibleLightInfo, SceneRenderingAllocator>& VisibleLightInfos,
|
|
const TArray<Nanite::FRasterResults, TInlineAllocator<2>>& NaniteRasterResults,
|
|
FScene& Scene)
|
|
{
|
|
check(IsEnabled());
|
|
|
|
if (ShadowMaps.Num() == 0 || Views.Num() == 0)
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "FVirtualShadowMapArray::BuildPageAllocation");
|
|
|
|
bool bDebugOutputEnabled = false;
|
|
VisualizeLight.Reset();
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
if (GDumpVSMLightNames)
|
|
{
|
|
bDebugOutputEnabled = true;
|
|
UE_LOG(LogRenderer, Display, TEXT("Lights with Virtual Shadow Maps:"));
|
|
}
|
|
|
|
// Setup debug visualization/output if enabled
|
|
{
|
|
FVirtualShadowMapVisualizationData& VisualizationData = GetVirtualShadowMapVisualizationData();
|
|
|
|
for (const FViewInfo& View : Views)
|
|
{
|
|
const FName& VisualizationMode = View.CurrentVirtualShadowMapVisualizationMode;
|
|
// for stereo views that aren't multi-view, don't account for the left
|
|
FIntPoint Extent = View.ViewRect.Max - View.ViewRect.Min;
|
|
if (VisualizationData.Update(VisualizationMode))
|
|
{
|
|
// TODO - automatically enable the show flag when set from command line?
|
|
//EngineShowFlags.SetVisualizeVirtualShadowMap(true);
|
|
}
|
|
|
|
if (VisualizationData.IsActive() && EngineShowFlags.VisualizeVirtualShadowMap)
|
|
{
|
|
bDebugOutputEnabled = true;
|
|
DebugVisualizationOutput.Add(CreateDebugVisualizationTexture(GraphBuilder, Extent));
|
|
}
|
|
}
|
|
}
|
|
#endif //!UE_BUILD_SHIPPING
|
|
|
|
// Store shadow map projection data for each virtual shadow map
|
|
TArray<FVirtualShadowMapProjectionShaderData, SceneRenderingAllocator> ProjectionData;
|
|
ProjectionData.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)
|
|
{
|
|
ProjectionData[ClipmapID + ClipmapLevel] = Clipmap->GetProjectionShaderData(ClipmapLevel);
|
|
}
|
|
|
|
if (bDebugOutputEnabled)
|
|
{
|
|
VisualizeLight.CheckLight(Clipmap->GetLightSceneInfo().Proxy, ClipmapID);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
const FViewMatrices ViewMatrices = ProjectedShadowInfo->GetShadowDepthRenderingViewMatrices( i, true );
|
|
const FLargeWorldRenderPosition PreViewTranslation(ProjectedShadowInfo->PreShadowTranslation);
|
|
|
|
FVirtualShadowMapProjectionShaderData& Data = ProjectionData[ ID ];
|
|
Data.TranslatedWorldToShadowViewMatrix = FMatrix44f(ViewMatrices.GetTranslatedViewMatrix()); // LWC_TODO: Precision loss?
|
|
Data.ShadowViewToClipMatrix = FMatrix44f(ViewMatrices.GetProjectionMatrix());
|
|
Data.TranslatedWorldToShadowUVMatrix = FMatrix44f(CalcTranslatedWorldToShadowUVMatrix( ViewMatrices.GetTranslatedViewMatrix(), ViewMatrices.GetProjectionMatrix() ));
|
|
Data.TranslatedWorldToShadowUVNormalMatrix = FMatrix44f(CalcTranslatedWorldToShadowUVNormalMatrix( ViewMatrices.GetTranslatedViewMatrix(), ViewMatrices.GetProjectionMatrix() ));
|
|
Data.PreViewTranslationLWCTile = PreViewTranslation.GetTile();
|
|
Data.PreViewTranslationLWCOffset = PreViewTranslation.GetOffset();
|
|
Data.LightType = ProjectedShadowInfo->GetLightSceneInfo().Proxy->GetLightType();
|
|
Data.LightSourceRadius = ProjectedShadowInfo->GetLightSceneInfo().Proxy->GetSourceRadius();
|
|
}
|
|
|
|
if (bDebugOutputEnabled)
|
|
{
|
|
VisualizeLight.CheckLight(ProjectedShadowInfo->GetLightSceneInfo().Proxy, ProjectedShadowInfo->VirtualShadowMaps[0]->ID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UniformParameters.NumShadowMaps = ShadowMaps.Num();
|
|
UniformParameters.NumDirectionalLights = DirectionalLightIds.Num();
|
|
|
|
ProjectionDataRDG = CreateProjectionDataBuffer(GraphBuilder, TEXT("Shadow.Virtual.ProjectionData"), ProjectionData);
|
|
|
|
UniformParameters.ProjectionData = GraphBuilder.CreateSRV(ProjectionDataRDG);
|
|
|
|
if (CVarShowStats.GetValueOnRenderThread() || CacheManager->IsAccumulatingStats())
|
|
{
|
|
StatsBufferRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumStats), TEXT("Shadow.Virtual.StatsBuffer"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(StatsBufferRDG), 0);
|
|
}
|
|
|
|
// We potentially over-allocate these to avoid too many different allocation sizes each frame
|
|
const uint32 NumShadowMapsToAllocate = GetShadowMapsToAllocate(ShadowMaps.Num());
|
|
const uint32 NumPageFlagsToAllocate = NumShadowMapsToAllocate * FVirtualShadowMap::PageTableSize;
|
|
|
|
// Create and clear the requested page flags
|
|
FRDGBufferRef PageRequestFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumPageFlagsToAllocate), TEXT("Shadow.Virtual.PageRequestFlags"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(PageRequestFlagsRDG), 0);
|
|
DynamicCasterPageFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumPageFlagsToAllocate), TEXT("Shadow.Virtual.DynamicCasterPageFlags"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(DynamicCasterPageFlagsRDG), 0);
|
|
|
|
DirtyPageFlagsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumPageFlagsToAllocate), TEXT("Shadow.Virtual.DirtyPageFlags"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(DirtyPageFlagsRDG), 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);
|
|
|
|
const uint32 NumPageRects = ShadowMaps.Num() * FVirtualShadowMap::MaxMipLevels;
|
|
const uint32 NumPageRectsToAllocate = NumShadowMapsToAllocate * FVirtualShadowMap::MaxMipLevels;
|
|
PageRectBoundsRDG = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(FIntVector4), NumPageRectsToAllocate), TEXT("Shadow.Virtual.PageRectBounds"));
|
|
{
|
|
FInitPageRectBoundsCS::FParameters* PassParameters = GraphBuilder.AllocParameters< FInitPageRectBoundsCS::FParameters >();
|
|
PassParameters->VirtualShadowMap = GetUniformBuffer(GraphBuilder);
|
|
PassParameters->OutPageRectBounds = 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)
|
|
{
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", 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 = [&](const EVirtualShadowMapProjectionInputType InputType)
|
|
{
|
|
FGeneratePageFlagsFromPixelsCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FGeneratePageFlagsFromPixelsCS::FInputType>(InputType == EVirtualShadowMapProjectionInputType::HairStrands ? 1u : 0u);
|
|
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.ForwardLightUniformBuffer;
|
|
PassParameters->DirectionalLightIds = GraphBuilder.CreateSRV(DirectionalLightIdsRDG);
|
|
PassParameters->ResolutionLodBiasLocal = CVarResolutionLodBiasLocal.GetValueOnRenderThread();
|
|
PassParameters->PageDilationBorderSizeLocal = CVarPageDilationBorderSizeLocal.GetValueOnRenderThread();
|
|
PassParameters->PageDilationBorderSizeDirectional = CVarPageDilationBorderSizeDirectional.GetValueOnRenderThread();
|
|
PassParameters->bCullBackfacingPixels = ShouldCullBackfacingPixels() ? 1 : 0;
|
|
PassParameters->Strata = Strata::BindStrataGlobalUniformParameters(View);
|
|
|
|
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 (InputType == EVirtualShadowMapProjectionInputType::HairStrands && 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,NumShadowMaps=%d)", ToString(InputType), ShadowMaps.Num()),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FIntVector(GridSize.X, GridSize.Y, 1));
|
|
}
|
|
};
|
|
|
|
GeneratePageFlags(EVirtualShadowMapProjectionInputType::GBuffer);
|
|
if (HairStrands::HasViewHairStrandsData(View))
|
|
{
|
|
GeneratePageFlags(EVirtualShadowMapProjectionInputType::HairStrands);
|
|
}
|
|
}
|
|
// 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), NumPageFlagsToAllocate), 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), NumPageFlagsToAllocate), TEXT("Shadow.Virtual.PageFlags"));
|
|
|
|
// 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);
|
|
}
|
|
|
|
FCreateCachedPageMappingsCS::FPermutationDomain PermutationVector;
|
|
SetStatsArgsAndPermutation<FCreateCachedPageMappingsCS>(GraphBuilder, StatsBufferRDG, PassParameters, PermutationVector);
|
|
PermutationVector.Set<FCreateCachedPageMappingsCS::FHasCacheDataDim>(bCacheEnabled);
|
|
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);
|
|
|
|
FAllocateNewPageMappingsCS::FPermutationDomain PermutationVector;
|
|
SetStatsArgsAndPermutation<FAllocateNewPageMappingsCS>(GraphBuilder, StatsBufferRDG, PassParameters, PermutationVector);
|
|
|
|
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->OutPageFlags = GraphBuilder.CreateUAV(PageFlagsRDG);
|
|
PassParameters->OutPageRectBounds = 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 PoolSize = GetPhysicalPoolSize();
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("InitializePhysicalPages"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FIntVector(
|
|
FMath::DivideAndRoundUp(PoolSize.X, 16),
|
|
FMath::DivideAndRoundUp(PoolSize.Y, 16),
|
|
1)
|
|
);
|
|
}
|
|
}
|
|
|
|
UniformParameters.PageTable = GraphBuilder.CreateSRV(PageTableRDG);
|
|
UniformParameters.PageFlags = GraphBuilder.CreateSRV(PageFlagsRDG);
|
|
UniformParameters.PageRectBounds = GraphBuilder.CreateSRV(PageRectBoundsRDG);
|
|
|
|
// 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)
|
|
|
|
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, VisualizeModeId)
|
|
SHADER_PARAMETER(int32, VirtualShadowMapId)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, OutVisualize)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
};
|
|
IMPLEMENT_GLOBAL_SHADER(FDebugVisualizeVirtualSmCS, "/Engine/Private/VirtualShadowMaps/VirtualShadowMapDebug.usf", "DebugVisualizeVirtualSmCS", SF_Compute);
|
|
|
|
|
|
void FVirtualShadowMapArray::RenderDebugInfo(FRDGBuilder& GraphBuilder, TArrayView<FViewInfo> Views)
|
|
{
|
|
check(IsEnabled());
|
|
|
|
if (DebugVisualizationOutput.IsEmpty() || !VisualizeLight.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FVirtualShadowMapVisualizationData& VisualizationData = GetVirtualShadowMapVisualizationData();
|
|
if (VisualizationData.GetActiveModeID() != VIRTUAL_SHADOW_MAP_VISUALIZE_CLIPMAP_VIRTUAL_SPACE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int32 BorderWidth = 2;
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
|
|
FIntPoint DebugTargetExtent = DebugVisualizationOutput[ViewIndex]->Desc.Extent;
|
|
|
|
FDebugVisualizeVirtualSmCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDebugVisualizeVirtualSmCS::FParameters>();
|
|
PassParameters->ProjectionParameters = GetSamplingParameters(GraphBuilder);
|
|
|
|
PassParameters->DebugTargetWidth = DebugTargetExtent.X;
|
|
PassParameters->DebugTargetHeight = DebugTargetExtent.Y;
|
|
PassParameters->BorderWidth = BorderWidth;
|
|
PassParameters->VisualizeModeId = VisualizationData.GetActiveModeID();
|
|
PassParameters->VirtualShadowMapId = VisualizeLight.GetVirtualShadowMapId();
|
|
|
|
PassParameters->OutVisualize = GraphBuilder.CreateUAV(DebugVisualizationOutput[ViewIndex]);
|
|
|
|
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FDebugVisualizeVirtualSmCS>();
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("DebugVisualizeVirtualShadowMap"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FComputeShaderUtils::GetGroupCount(DebugTargetExtent, FVirtualPageManagementShader::DefaultCSGroupXY)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
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/VirtualShadowMapPrintStats.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)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
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.TranslatedWorldToSubpixelClip = Nanite::FPackedView::CalcTranslatedWorldToSubpixelClip(MipView.TranslatedWorldToClip, FIntRect(ViewMin.X, ViewMin.Y, ViewMin.X + ViewSize.X, ViewMin.Y + ViewSize.Y));
|
|
}
|
|
|
|
MipView.HZBTestViewRect = MipView.ViewRect; // Assumed to always be the same for VSM
|
|
|
|
float 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 = NANITE_VIEW_FLAG_HZBTEST | NANITE_VIEW_FLAG_NEAR_CLIP;
|
|
MipView.StreamingPriorityCategory_AndFlags = (ViewFlags << NANITE_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/VirtualShadowMapPrintStats.usf", "PrintClipmapStats", SF_Compute);
|
|
|
|
|
|
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
|
|
{
|
|
FVector3f CullingViewOriginOffset;
|
|
uint32 FirstPrimaryView;
|
|
FVector3f CullingViewOriginTile;
|
|
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) &&
|
|
DoesPlatformSupportNanite(Parameters.Platform);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
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 >, HZBPageTable)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, HZBPageFlags)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint4 >, HZBPageRectBounds)
|
|
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_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
|
|
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, GPUSceneInstanceSceneData)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, GPUSceneInstancePayloadData)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, GPUScenePrimitiveSceneData)
|
|
SHADER_PARAMETER(uint32, InstanceSceneDataSOAStride)
|
|
SHADER_PARAMETER(uint32, GPUSceneFrameNumber)
|
|
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, PrimitiveRevealedMask)
|
|
SHADER_PARAMETER(uint32, PrimitiveRevealedNum)
|
|
|
|
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(float, MaxMaterialPositionInvalidationRange)
|
|
SHADER_PARAMETER(FVector3f, CullingViewOriginOffset)
|
|
SHADER_PARAMETER(FVector3f, CullingViewOriginTile)
|
|
|
|
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/VirtualShadowMapBuildPerPageDrawCommands.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) &&
|
|
DoesPlatformSupportNanite(Parameters.Platform);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
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/VirtualShadowMapBuildPerPageDrawCommands.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) &&
|
|
DoesPlatformSupportNanite(Parameters.Platform);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
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/VirtualShadowMapBuildPerPageDrawCommands.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,
|
|
FRDGBufferRef PrimitiveRevealedMaskRdg,
|
|
int32 PrimitiveRevealedNum)
|
|
{
|
|
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
|
|
FRDGBufferDesc IndirectArgsDesc = FRDGBufferDesc::CreateIndirectDesc(FInstanceCullingContext::IndirectArgsNumWords * IndirectArgs.Num());
|
|
IndirectArgsDesc.Usage |= BUF_MultiGPUGraphIgnore;
|
|
|
|
CullingResult.DrawIndirectArgsRDG = GraphBuilder.CreateBuffer(IndirectArgsDesc, 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>();
|
|
|
|
PassParameters->VirtualShadowMap = VirtualShadowMapArray->GetUniformBuffer(GraphBuilder);
|
|
|
|
const FGPUSceneResourceParameters GPUSceneParameters = GPUScene.GetShaderParameters();
|
|
|
|
PassParameters->GPUSceneInstanceSceneData = GPUSceneParameters.GPUSceneInstanceSceneData;
|
|
PassParameters->GPUSceneInstancePayloadData = GPUSceneParameters.GPUSceneInstancePayloadData;
|
|
PassParameters->GPUScenePrimitiveSceneData = GPUSceneParameters.GPUScenePrimitiveSceneData;
|
|
PassParameters->GPUSceneFrameNumber = GPUSceneParameters.GPUSceneFrameNumber;
|
|
PassParameters->InstanceSceneDataSOAStride = GPUSceneParameters.InstanceDataSOAStride;
|
|
|
|
// Make sure there is enough space in the buffer for all the primitive IDs that might be used to index.
|
|
check(PrimitiveRevealedMaskRdg->Desc.NumElements * 32u >= uint32(PrimitiveRevealedNum));
|
|
PassParameters->PrimitiveRevealedMask = GraphBuilder.CreateSRV(PrimitiveRevealedMaskRdg);
|
|
PassParameters->PrimitiveRevealedNum = uint32(PrimitiveRevealedNum);
|
|
|
|
PassParameters->DynamicInstanceIdOffset = BatchInfos[0].DynamicInstanceIdOffset;
|
|
PassParameters->DynamicInstanceIdMax = BatchInfos[0].DynamicInstanceIdMax;
|
|
|
|
PassParameters->MaxMaterialPositionInvalidationRange = CVarMaxMaterialPositionInvalidationRange.GetValueOnRenderThread();
|
|
|
|
auto GPUData = LoadBalancer->Upload(GraphBuilder);
|
|
GPUData.GetShaderParameters(GraphBuilder, PassParameters->LoadBalancerParameters);
|
|
|
|
PassParameters->FirstPrimaryView = VSMCullingBatchInfos[0].FirstPrimaryView;
|
|
PassParameters->NumPrimaryViews = VSMCullingBatchInfos[0].NumPrimaryViews;
|
|
PassParameters->CullingViewOriginOffset = VSMCullingBatchInfos[0].CullingViewOriginOffset;
|
|
PassParameters->CullingViewOriginTile = VSMCullingBatchInfos[0].CullingViewOriginTile;
|
|
|
|
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.ResolveRect = FResolveRect(ViewRect);
|
|
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, TArrayView<FViewInfo> Views)
|
|
{
|
|
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;
|
|
FRDGBufferSRVRef PrevPageFlagsRDGSRV = CacheManager->IsValid() ? GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.PageFlags, TEXT("Shadow.Virtual.PrevPageFlags"))) : nullptr;
|
|
FRDGBufferSRVRef PrevPageRectBoundsRDGSRV = CacheManager->IsValid() ? GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(CacheManager->PrevBuffers.PageRectBounds, TEXT("Shadow.Virtual.PrevPageRectBounds"))) : 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());
|
|
|
|
/**
|
|
* Use the 'dependent view' i.e., the view used to set up a view dependent CSM/VSM(clipmap) OR select the view closest to the local light.
|
|
* This last is important to get some kind of reasonable behavior for split screen.
|
|
*/
|
|
auto GetCullingViewOrigin = [&Views](const FProjectedShadowInfo* ProjectedShadowInfo) -> FLargeWorldRenderPosition
|
|
{
|
|
if (ProjectedShadowInfo->DependentView != nullptr)
|
|
{
|
|
return FLargeWorldRenderPosition(ProjectedShadowInfo->DependentView->ShadowViewMatrices.GetViewOrigin());
|
|
}
|
|
|
|
// VSM supports only whole scene shadows, so those without a "DependentView" are local lights
|
|
// For local lights the origin is the (inverse of) pre-shadow translation.
|
|
check(ProjectedShadowInfo->bWholeSceneShadow);
|
|
|
|
FVector MinOrigin = Views[0].ShadowViewMatrices.GetViewOrigin();
|
|
double MinDistanceSq = (MinOrigin + ProjectedShadowInfo->PreShadowTranslation).SquaredLength();
|
|
for (int Index = 1; Index < Views.Num(); ++Index)
|
|
{
|
|
FVector TestOrigin = Views[Index].ShadowViewMatrices.GetViewOrigin();
|
|
double TestDistanceSq = (TestOrigin + ProjectedShadowInfo->PreShadowTranslation).SquaredLength();
|
|
if (TestDistanceSq < MinDistanceSq)
|
|
{
|
|
MinOrigin = TestOrigin;
|
|
MinDistanceSq = TestDistanceSq;
|
|
}
|
|
|
|
}
|
|
return FLargeWorldRenderPosition(MinOrigin);
|
|
};
|
|
|
|
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 FLargeWorldRenderPosition CullingViewOrigin = GetCullingViewOrigin(ProjectedShadowInfo);
|
|
VSMCullingBatchInfo.CullingViewOriginOffset = CullingViewOrigin.GetOffset();
|
|
VSMCullingBatchInfo.CullingViewOriginTile = CullingViewOrigin.GetTile();
|
|
}
|
|
|
|
const TSharedPtr<FVirtualShadowMapClipmap> Clipmap = ProjectedShadowInfo->VirtualShadowMapClipmap;
|
|
if (Clipmap)
|
|
{
|
|
VSMCullingBatchInfo.NumPrimaryViews = AddRenderViews(Clipmap, 1.0f, HZBTexture != nullptr, false, ProjectedShadowInfo->ShouldClampToNearPlane(), 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, ProjectedShadowInfo->ShouldClampToNearPlane(), 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
|
|
const FSceneTextures* SceneTextures = &GetViewFamily(Views).GetSceneTextures();
|
|
auto CreateShadowDepthPassUniformBuffer = [this, &VirtualShadowViewsRDG, &GraphBuilder, SceneTextures](bool bClampToNearPlane)
|
|
{
|
|
FShadowDepthPassUniformParameters* ShadowDepthPassParameters = GraphBuilder.AllocParameters<FShadowDepthPassUniformParameters>();
|
|
check(PhysicalPagePoolRDG != nullptr);
|
|
// TODO: These are not used for this case anyway
|
|
ShadowDepthPassParameters->ProjectionMatrix = FMatrix44f::Identity;
|
|
ShadowDepthPassParameters->ViewMatrix = FMatrix44f::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->OutDepthBufferArray = GraphBuilder.CreateUAV(PhysicalPagePoolRDG, ERDGUnorderedAccessViewFlags::SkipBarrier);
|
|
SetupSceneTextureUniformParameters(GraphBuilder, SceneTextures, 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.HZBPageTable = HZBMode == 2 ? GraphBuilder.CreateSRV(PageTableRDG) : PrevPageTableRDGSRV;
|
|
HZBShaderParameters.HZBPageFlags = HZBMode == 2 ? GraphBuilder.CreateSRV(PageFlagsRDG) : PrevPageFlagsRDGSRV;
|
|
HZBShaderParameters.HZBPageRectBounds = HZBMode == 2 ? GraphBuilder.CreateSRV(PageRectBoundsRDG) : PrevPageRectBoundsRDGSRV;
|
|
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,
|
|
GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, 4),
|
|
0
|
|
);
|
|
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 for 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();
|
|
|
|
FRDGBufferRef PrimitiveRevealedMaskRdg = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, 4);
|
|
int32 PrimitiveRevealedNum = 0;
|
|
|
|
|
|
if (!Clipmap->GetRevealedPrimitivesMask().IsEmpty())
|
|
{
|
|
PrimitiveRevealedMaskRdg = CreateStructuredBuffer(GraphBuilder, TEXT("Shadow.Virtual.RevealedPrimitivesMask"), Clipmap->GetRevealedPrimitivesMask());
|
|
PrimitiveRevealedNum = Clipmap->GetNumRevealedPrimitives();
|
|
}
|
|
|
|
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,
|
|
PrimitiveRevealedMaskRdg,
|
|
PrimitiveRevealedNum
|
|
);
|
|
|
|
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)
|
|
|
|
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>, OutPagesForHZBIndirectArgsBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutPhysicalPagesForHZB)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, DirtyPageFlags)
|
|
SHADER_PARAMETER(uint32, bFirstBuildThisFrame)
|
|
SHADER_PARAMETER(uint32, bForceFullHZBUpdate)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer< uint >, OutStatsBuffer)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
};
|
|
IMPLEMENT_GLOBAL_SHADER(FSelectPagesForHZBCS, "/Engine/Private/VirtualShadowMaps/VirtualShadowMapPageManagement.usf", "SelectPagesForHZBCS", SF_Compute);
|
|
|
|
|
|
class FVirtualSmBuildHZBPerPageCS : public FVirtualPageManagementShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FVirtualSmBuildHZBPerPageCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FVirtualSmBuildHZBPerPageCS, FVirtualPageManagementShader)
|
|
|
|
static constexpr uint32 TotalHZBLevels = FVirtualShadowMap::NumHZBLevels;
|
|
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(Texture2DArray<uint>, PhysicalPagePool)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(RWTexture2D<float>, FurthestHZBOutput, [HZBLevelsBase])
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
};
|
|
IMPLEMENT_GLOBAL_SHADER(FVirtualSmBuildHZBPerPageCS, "/Engine/Private/VirtualShadowMaps/VirtualShadowMapPageManagement.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/VirtualShadowMapPageManagement.usf", "BuildHZBPerPageTopCS", SF_Compute);
|
|
|
|
void FVirtualShadowMapArray::UpdateHZB(FRDGBuilder& GraphBuilder)
|
|
{
|
|
const FIntRect ViewRect(0, 0, GetPhysicalPoolSize().X, GetPhysicalPoolSize().Y);
|
|
|
|
// 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);
|
|
PassParameters->DirtyPageFlags = GraphBuilder.CreateSRV(DirtyPageFlagsRDG);
|
|
PassParameters->bFirstBuildThisFrame = !bHZBBuiltThisFrame;
|
|
PassParameters->bForceFullHZBUpdate = CVarShadowsVirtualForceFullHZBUpdate.GetValueOnRenderThread();
|
|
FSelectPagesForHZBCS::FPermutationDomain PermutationVector;
|
|
SetStatsArgsAndPermutation<FSelectPagesForHZBCS>(GraphBuilder, StatsBufferRDG, PassParameters, PermutationVector);
|
|
auto ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FSelectPagesForHZBCS>(PermutationVector);
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("SelectPagesForHZB"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FIntVector(FMath::DivideAndRoundUp(UniformParameters.MaxPhysicalPages, FSelectPagesForHZBCS::DefaultCSGroupX), 1, 1)
|
|
);
|
|
}
|
|
|
|
// Clear the dirty flags (for subsequent render passes).
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(DirtyPageFlagsRDG), 0);
|
|
|
|
bHZBBuiltThisFrame = true;
|
|
|
|
{
|
|
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(HZBPhysical, 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(HZBPhysical, StartDestMip + DestMip));
|
|
}
|
|
FIntPoint SrcSize = FIntPoint::DivideAndRoundUp(FIntPoint(HZBPhysical->Desc.GetSize().X, HZBPhysical->Desc.GetSize().Y), 1 << int32(StartDestMip - 1));
|
|
PassParameters->InvHzbInputSize = FVector2f(1.0f / SrcSize.X, 1.0f / SrcSize.Y);;
|
|
PassParameters->ParentTextureMip = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(HZBPhysical, 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 * sizeof(uint32)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
uint32 FVirtualShadowMapArray::AddRenderViews(const TSharedPtr<FVirtualShadowMapClipmap>& Clipmap, float LODScaleFactor, bool bSetHZBParams, bool bUpdateHZBMetaData, bool bClampToNearPlane, 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
|
|
BaseParams.Flags = bClampToNearPlane ? 0u : NANITE_VIEW_FLAG_NEAR_CLIP;
|
|
|
|
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;
|
|
|
|
// 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, bool bClampToNearPlane, 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;
|
|
BaseParams.Flags = bClampToNearPlane ? 0u : NANITE_VIEW_FLAG_NEAR_CLIP;
|
|
|
|
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);
|
|
}
|
|
|
|
void FVirtualShadowMapArray::AddVisualizePass(FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, FScreenPassTexture Output)
|
|
{
|
|
#if !UE_BUILD_SHIPPING
|
|
if (!IsAllocated() || DebugVisualizationOutput.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FVirtualShadowMapVisualizationData& VisualizationData = GetVirtualShadowMapVisualizationData();
|
|
if (VisualizationData.IsActive() && VisualizeLight.IsValid())
|
|
{
|
|
FCopyRectPS::FParameters* Parameters = GraphBuilder.AllocParameters<FCopyRectPS::FParameters>();
|
|
Parameters->InputTexture = DebugVisualizationOutput[ViewIndex];
|
|
Parameters->InputSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Parameters->RenderTargets[0] = FRenderTargetBinding(Output.Texture, ERenderTargetLoadAction::ENoAction);
|
|
|
|
TShaderMapRef<FCopyRectPS> PixelShader(View.ShaderMap);
|
|
|
|
FScreenPassTextureViewport InputViewport(DebugVisualizationOutput[ViewIndex]->Desc.Extent);
|
|
FScreenPassTextureViewport OutputViewport(Output);
|
|
|
|
// See CVarVisualizeLayout documentation
|
|
const int32 VisualizeLayout = CVarVisualizeLayout.GetValueOnRenderThread();
|
|
if (VisualizeLayout == 1) // Thumbnail
|
|
{
|
|
const int32 TileWidth = View.UnscaledViewRect.Width() / 3;
|
|
const int32 TileHeight = View.UnscaledViewRect.Height() / 3;
|
|
|
|
OutputViewport.Rect.Max = OutputViewport.Rect.Min + FIntPoint(TileWidth, TileHeight);
|
|
}
|
|
else if (VisualizeLayout == 2) // Split screen
|
|
{
|
|
InputViewport.Rect.Max.X = InputViewport.Rect.Min.X + (InputViewport.Rect.Width() / 2);
|
|
OutputViewport.Rect.Max.X = OutputViewport.Rect.Min.X + (OutputViewport.Rect.Width() / 2);
|
|
}
|
|
|
|
// Use separate input and output viewports w/ bilinear sampling to properly support dynamic resolution scaling
|
|
AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("DrawTexture"), View, OutputViewport, InputViewport, PixelShader, Parameters, EScreenPassDrawFlags::None);
|
|
|
|
// Visualization light name
|
|
{
|
|
FScreenPassRenderTarget OutputTarget(Output.Texture, View.UnscaledViewRect, ERenderTargetLoadAction::ELoad);
|
|
|
|
AddDrawCanvasPass(GraphBuilder, RDG_EVENT_NAME("Labels"), View, OutputTarget,
|
|
[&VisualizeLight=VisualizeLight, &OutputViewport=OutputViewport](FCanvas& Canvas)
|
|
{
|
|
const FLinearColor LabelColor(1, 1, 0);
|
|
Canvas.DrawShadowedString(
|
|
OutputViewport.Rect.Min.X + 8,
|
|
OutputViewport.Rect.Max.Y - 19,
|
|
*VisualizeLight.GetLightName(),
|
|
GetStatsFont(),
|
|
LabelColor);
|
|
});
|
|
}
|
|
}
|
|
#endif
|
|
}
|