1015 lines
45 KiB
C++
1015 lines
45 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RendererPrivate.h"
|
|
#include "ScenePrivate.h"
|
|
#include "SceneUtils.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "ShaderParameterStruct.h"
|
|
#include "PixelShaderUtils.h"
|
|
#include "LumenSceneLighting.h"
|
|
#include "LumenSceneCardCapture.h"
|
|
#include "LumenRadiosity.h"
|
|
#include "ComponentRecreateRenderStateContext.h"
|
|
|
|
static TAutoConsoleVariable<int32> CVarLumenSurfaceCacheDilationMode(
|
|
TEXT("r.LumenScene.SurfaceCache.DilationMode"),
|
|
0,
|
|
TEXT("Whether to allow dilating card page data by one texel.\n")
|
|
TEXT("Better coverage for meshes even if they don't match their surface cache very well but data from dilation may not be accurate.\n")
|
|
TEXT("It is a choice between getting possibly incorrect lighting or no lighting.\n")
|
|
TEXT("0 - Disabled\n")
|
|
TEXT("1 - Only two-sided (foliage but currently only wired up for mostly two-sided Nanite skinned meshes)\n")
|
|
TEXT("2 - All"),
|
|
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* InVariable) { FGlobalComponentRecreateRenderStateContext Context; }),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
int32 GLumenSurfaceCacheCompress = 1;
|
|
FAutoConsoleVariableRef CVarLumenSurfaceCacheCompress(
|
|
TEXT("r.LumenScene.SurfaceCache.Compress"),
|
|
GLumenSurfaceCacheCompress,
|
|
TEXT("Whether to use run time compression for surface cache.\n")
|
|
TEXT("0 - Disabled\n")
|
|
TEXT("1 - Compress using UAV aliasing if supported\n")
|
|
TEXT("2 - Compress using CopyTexture (may be very slow on some RHIs)\n"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarLumenSurfaceCacheCullUndergroundTexels(
|
|
TEXT("r.LumenScene.SurfaceCache.CullUndergroundTexels"),
|
|
0,
|
|
TEXT("Whether to cull underground Lumen card texels. This relies on RVT to obtain landscape height. Enabling this will disable card sharing."),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarLumenSurfaceCacheCullUndergroundTexelsHeightBias(
|
|
TEXT("r.LumenScene.SurfaceCache.CullUndergroundTexels.HeightBias"),
|
|
30.0f,
|
|
TEXT("A bias added to texel world height before comparing against landscape height. Larger values push texels up and make them less likely culled."),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
bool LumenScene::CullUndergroundTexels()
|
|
{
|
|
return CVarLumenSurfaceCacheCullUndergroundTexels.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
ESurfaceCacheCompression GetSurfaceCacheCompression()
|
|
{
|
|
const bool bSupportsBCTextureCompression = (GPixelFormats[PF_BC5].Supported && GPixelFormats[PF_BC6H].Supported && GPixelFormats[PF_BC7].Supported);
|
|
|
|
if (GLumenSurfaceCacheCompress == 1)
|
|
{
|
|
if (GRHISupportsUAVFormatAliasing && bSupportsBCTextureCompression)
|
|
{
|
|
return ESurfaceCacheCompression::UAVAliasing;
|
|
}
|
|
else if (GRHISupportsLossyFramebufferCompression)
|
|
{
|
|
return ESurfaceCacheCompression::FramebufferCompression;
|
|
}
|
|
}
|
|
else if (GLumenSurfaceCacheCompress == 2 && bSupportsBCTextureCompression)
|
|
{
|
|
return ESurfaceCacheCompression::CopyTextureRegion;
|
|
}
|
|
|
|
return ESurfaceCacheCompression::Disabled;
|
|
}
|
|
|
|
enum class ELumenSurfaceCacheLayer : uint8
|
|
{
|
|
Depth,
|
|
Albedo,
|
|
Opacity,
|
|
Normal,
|
|
Emissive,
|
|
|
|
MAX
|
|
};
|
|
|
|
struct FLumenSurfaceLayerConfig
|
|
{
|
|
const TCHAR* Name;
|
|
EPixelFormat UncompressedFormat;
|
|
EPixelFormat CompressedFormat;
|
|
EPixelFormat CompressedUAVFormat;
|
|
FVector ClearValue;
|
|
};
|
|
|
|
const FLumenSurfaceLayerConfig& GetSurfaceLayerConfig(ELumenSurfaceCacheLayer Layer)
|
|
{
|
|
static FLumenSurfaceLayerConfig Configs[(uint32)ELumenSurfaceCacheLayer::MAX] =
|
|
{
|
|
{ TEXT("Depth"), PF_G16, PF_Unknown, PF_Unknown, FVector(1.0f, 0.0f, 0.0f) },
|
|
{ TEXT("Albedo"), PF_R8G8B8A8, PF_BC7, PF_R32G32B32A32_UINT, FVector(0.0f, 0.0f, 0.0f) },
|
|
{ TEXT("Opacity"), PF_G8, PF_Unknown, PF_Unknown, FVector(1.0f, 0.0f, 0.0f) }, // #lumen_todo: Fix BC4 compression and re-enable
|
|
{ TEXT("Normal"), PF_R8G8, PF_BC5, PF_R32G32B32A32_UINT, FVector(0.0f, 0.0f, 0.0f) },
|
|
{ TEXT("Emissive"), PF_FloatR11G11B10, PF_BC6H, PF_R32G32B32A32_UINT, FVector(0.0f, 0.0f, 0.0f) }
|
|
};
|
|
|
|
check((uint32)Layer < UE_ARRAY_COUNT(Configs));
|
|
|
|
return Configs[(uint32)Layer];
|
|
}
|
|
|
|
class FLumenCardCopyPS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FLumenCardCopyPS);
|
|
SHADER_USE_PARAMETER_STRUCT(FLumenCardCopyPS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenLandscapeHeightSamplingParameters, LandscapeHeightSamplingParameters)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float4>, CardUVRects)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, CardIndices)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint4>, RWAtlasBlock4)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint2>, RWAtlasBlock2)
|
|
SHADER_PARAMETER(FVector2f, OneOverSourceAtlasSize)
|
|
SHADER_PARAMETER(float, TexelCullingHeightBias)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceAlbedoAtlas)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceNormalAtlas)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceEmissiveAtlas)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceDepthAtlas)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FSurfaceCacheLayer : SHADER_PERMUTATION_ENUM_CLASS("SURFACE_LAYER", ELumenSurfaceCacheLayer);
|
|
class FCompress : SHADER_PERMUTATION_BOOL("COMPRESS");
|
|
class FCullUndergroundTexels : SHADER_PERMUTATION_BOOL("CULL_UNDERGROUND_TEXELS");
|
|
using FPermutationDomain = TShaderPermutationDomain<FSurfaceCacheLayer, FCompress, FCullUndergroundTexels>;
|
|
|
|
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
|
|
{
|
|
if (PermutationVector.Get<FSurfaceCacheLayer>() != ELumenSurfaceCacheLayer::Depth)
|
|
{
|
|
PermutationVector.Set<FCullUndergroundTexels>(false);
|
|
}
|
|
|
|
return PermutationVector;
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
const FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
if (RemapPermutation(PermutationVector) != PermutationVector)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return DoesPlatformSupportLumenGI(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
if (!PermutationVector.Get<FCompress>())
|
|
{
|
|
const FLumenSurfaceLayerConfig& LumenSurfaceLayerConfig = GetSurfaceLayerConfig(PermutationVector.Get<FSurfaceCacheLayer>());
|
|
OutEnvironment.SetRenderTargetOutputFormat(0, LumenSurfaceLayerConfig.UncompressedFormat);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FLumenCardCopyPS, "/Engine/Private/Lumen/SurfaceCache/LumenSurfaceCache.usf", "LumenCardCopyPS", SF_Pixel);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FLumenCardCopyParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FPixelShaderUtils::FRasterizeToRectsVS::FParameters, VS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenCardCopyPS::FParameters, PS)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FClearLumenRectsParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FPixelShaderUtils::FRasterizeToRectsVS::FParameters, VS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FClearLumenCardsPS::FParameters, PS)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FCopyCardCaptureLightingToAtlasParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FPixelShaderUtils::FRasterizeToRectsVS::FParameters, VS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FCopyCardCaptureLightingToAtlasPS::FParameters, PS)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FCopyTextureParameters, )
|
|
RDG_TEXTURE_ACCESS(InputTexture, ERHIAccess::CopySrc)
|
|
RDG_TEXTURE_ACCESS(OutputTexture, ERHIAccess::CopyDest)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
FRDGTextureRef CreateCardAtlas(FRDGBuilder& GraphBuilder, const FIntPoint PageAtlasSize, ESurfaceCacheCompression PhysicalAtlasCompression, ELumenSurfaceCacheLayer LayerId, const TCHAR* Name)
|
|
{
|
|
const FLumenSurfaceLayerConfig& Config = GetSurfaceLayerConfig(LayerId);
|
|
|
|
ETextureCreateFlags TexFlags = TexCreate_ShaderResource | TexCreate_NoFastClear;
|
|
EPixelFormat Format = Config.CompressedFormat;
|
|
|
|
if (PhysicalAtlasCompression == ESurfaceCacheCompression::Disabled || PhysicalAtlasCompression == ESurfaceCacheCompression::FramebufferCompression || Config.CompressedFormat == PF_Unknown)
|
|
{
|
|
// Without compression we can write directly into this surface
|
|
Format = Config.UncompressedFormat;
|
|
TexFlags |= TexCreate_RenderTargetable;
|
|
if (PhysicalAtlasCompression == ESurfaceCacheCompression::FramebufferCompression)
|
|
{
|
|
TexFlags |= TexCreate_LossyCompressionLowBitrate;
|
|
}
|
|
}
|
|
|
|
FRDGTextureDesc CreateInfo = FRDGTextureDesc::Create2D(
|
|
PageAtlasSize,
|
|
Format,
|
|
FClearValueBinding(FLinearColor(Config.ClearValue)),
|
|
TexFlags);
|
|
|
|
// With UAV aliasing we can directly write into a BC target by overriding UAV format
|
|
if (PhysicalAtlasCompression == ESurfaceCacheCompression::UAVAliasing && Config.CompressedFormat != PF_Unknown)
|
|
{
|
|
CreateInfo.Flags |= TexCreate_UAV;
|
|
CreateInfo.UAVFormat = Config.CompressedUAVFormat;
|
|
}
|
|
|
|
return GraphBuilder.CreateTexture(CreateInfo, Name);
|
|
}
|
|
|
|
void FLumenSceneData::AllocateCardAtlases(FRDGBuilder& GraphBuilder, FLumenSceneFrameTemporaries& FrameTemporaries, const FSceneViewFamily* ViewFamily)
|
|
{
|
|
const FIntPoint PageAtlasSize = GetPhysicalAtlasSize();
|
|
|
|
FrameTemporaries.AlbedoAtlas = CreateCardAtlas(GraphBuilder, PageAtlasSize, PhysicalAtlasCompression, ELumenSurfaceCacheLayer::Albedo, TEXT("Lumen.SceneAlbedo"));
|
|
FrameTemporaries.OpacityAtlas = CreateCardAtlas(GraphBuilder, PageAtlasSize, PhysicalAtlasCompression, ELumenSurfaceCacheLayer::Opacity, TEXT("Lumen.SceneOpacity"));
|
|
FrameTemporaries.DepthAtlas = CreateCardAtlas(GraphBuilder, PageAtlasSize, PhysicalAtlasCompression, ELumenSurfaceCacheLayer::Depth, TEXT("Lumen.SceneDepth"));
|
|
FrameTemporaries.NormalAtlas = CreateCardAtlas(GraphBuilder, PageAtlasSize, PhysicalAtlasCompression, ELumenSurfaceCacheLayer::Normal, TEXT("Lumen.SceneNormal"));
|
|
FrameTemporaries.EmissiveAtlas = CreateCardAtlas(GraphBuilder, PageAtlasSize, PhysicalAtlasCompression, ELumenSurfaceCacheLayer::Emissive, TEXT("Lumen.SceneEmissive"));
|
|
|
|
FrameTemporaries.DirectLightingAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
PageAtlasSize,
|
|
Lumen::GetDirectLightingAtlasFormat(),
|
|
FClearValueBinding::Black,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV
|
|
), TEXT("Lumen.SceneDirectLighting"));
|
|
|
|
FrameTemporaries.IndirectLightingAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
GetRadiosityAtlasSize(),
|
|
Lumen::GetIndirectLightingAtlasFormat(),
|
|
FClearValueBinding::Black,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV
|
|
), TEXT("Lumen.SceneIndirectLighting"));
|
|
|
|
FrameTemporaries.RadiosityNumFramesAccumulatedAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
GetRadiosityAtlasSize(),
|
|
Lumen::GetNumFramesAccumulatedAtlasFormat(),
|
|
FClearValueBinding::Black,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV
|
|
), TEXT("Lumen.SceneNumFramesAccumulatedAtlas"));
|
|
|
|
FrameTemporaries.FinalLightingAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
PageAtlasSize,
|
|
Lumen::GetLightingDataFormat(),
|
|
FClearValueBinding::Black,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV
|
|
), TEXT("Lumen.SceneFinalLighting"));
|
|
|
|
const FIntPoint PageAtlasSizeInTiles = PageAtlasSize / Lumen::CardTileSize;
|
|
FrameTemporaries.TileShadowDownsampleFactorAtlas = GraphBuilder.CreateBuffer(
|
|
FRDGBufferDesc::CreateBufferDesc(
|
|
sizeof(uint32),
|
|
PageAtlasSizeInTiles.X * PageAtlasSizeInTiles.Y * Lumen::CardTileShadowDownsampleFactorDwords
|
|
), TEXT("Lumen.TileShadowDownsampleFactorAtlas"));
|
|
|
|
if (LumenSceneDirectLighting::UseStochasticLighting(*ViewFamily))
|
|
{
|
|
FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
PageAtlasSize,
|
|
PF_FloatRGBA,
|
|
FClearValueBinding::Black,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV
|
|
), TEXT("Lumen.SceneDirectLighting.DiffuseLightingAndSecondMomentHistory"));
|
|
|
|
FrameTemporaries.NumFramesAccumulatedHistoryAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
PageAtlasSize,
|
|
PF_G8,
|
|
FClearValueBinding::Black,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV
|
|
), TEXT("Lumen.SceneDirectLighting.NumFramesAccumulatedHistory"));
|
|
}
|
|
}
|
|
|
|
class FGenerateDilationTileDataCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FGenerateDilationTileDataCS)
|
|
SHADER_USE_PARAMETER_STRUCT(FGenerateDilationTileDataCS, FGlobalShader)
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER(uint32, NumCardPages)
|
|
SHADER_PARAMETER(uint32, DilationTileDataOffset)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint4>, CardPageRectBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, DilationPageMaskBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWTileAllocator)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWDilationTileAllocator)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWPackedCardTileDataBuffer)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return DoesPlatformSupportLumenGI(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
|
|
}
|
|
|
|
static int32 GetGroupSize()
|
|
{
|
|
return 64;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FGenerateDilationTileDataCS, "/Engine/Private/Lumen/SurfaceCache/LumenSurfaceCache.usf", "GenerateDilationTileDataCS", SF_Compute);
|
|
|
|
class FCopyCapturedCardPageCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FCopyCapturedCardPageCS)
|
|
SHADER_USE_PARAMETER_STRUCT(FCopyCapturedCardPageCS, FGlobalShader)
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER(uint32, DilationTileDataOffset)
|
|
SHADER_PARAMETER(FVector2f, OneOverSourceAtlasSize)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceAlbedoAtlas)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceNormalAtlas)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceDepthAtlas)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint4>, CardPageRectBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, PackedCardTileDataBuffer)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<UNORM float4>, RWAlbedoAtlas)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<UNORM float4>, RWNormalAtlas)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float>, RWDepthAtlas)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FDilateOneTexel : SHADER_PERMUTATION_BOOL("DILATE_ONE_TEXEL");
|
|
using FPermutationDomain = TShaderPermutationDomain<FDilateOneTexel>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return DoesPlatformSupportLumenGI(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
|
|
}
|
|
|
|
static int32 GetGroupSize()
|
|
{
|
|
return 8;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FCopyCapturedCardPageCS, "/Engine/Private/Lumen/SurfaceCache/LumenSurfaceCache.usf", "CopyCapturedCardPageCS", SF_Compute);
|
|
|
|
void FDeferredShadingSceneRenderer::DilateCardPageOneTexel(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
int32 NumPagesNeedDilation,
|
|
FRDGBufferSRVRef CardCaptureRectBufferSRV,
|
|
TConstArrayView<FCardPageRenderData> CardPagesToRender,
|
|
FCardCaptureAtlas& CardCaptureAtlas)
|
|
{
|
|
const int32 DilationMode = CVarLumenSurfaceCacheDilationMode.GetValueOnRenderThread();
|
|
|
|
if ((NumPagesNeedDilation == 0 && DilationMode != 2) || DilationMode == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLM_SCOPE_BYTAG(Lumen);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "DilateCardPageOneTexel");
|
|
|
|
FRDGUploadData<uint32> DilationPageMaskData(GraphBuilder, FMath::DivideAndRoundUp(CardPagesToRender.Num(), 32));
|
|
FMemory::Memzero(DilationPageMaskData.GetData(), DilationPageMaskData.Num() * sizeof(uint32));
|
|
int32 CopyTileCount = 0;
|
|
int32 DilationTileCount = 0;
|
|
int32 LocalNumPagesNeedDilation = 0;
|
|
|
|
for (int32 PageIndex = 0; PageIndex < CardPagesToRender.Num(); ++PageIndex)
|
|
{
|
|
const FCardPageRenderData& CardPageRenderData = CardPagesToRender[PageIndex];
|
|
|
|
const FIntPoint PageSize = CardPageRenderData.CardCaptureAtlasRect.Size();
|
|
check(PageSize.X % Lumen::CardTileSize == 0 && PageSize.Y % Lumen::CardTileSize == 0);
|
|
const int32 TileCount = (PageSize.X / Lumen::CardTileSize) * (PageSize.Y / Lumen::CardTileSize);
|
|
|
|
if (CardPageRenderData.NeedsRender() && (CardPageRenderData.DilationMode == ELumenCardDilationMode::DilateOneTexel || DilationMode == 2))
|
|
{
|
|
DilationPageMaskData[PageIndex / 32] |= (1u << (PageIndex % 32u));
|
|
DilationTileCount += TileCount;
|
|
++LocalNumPagesNeedDilation;
|
|
}
|
|
else
|
|
{
|
|
CopyTileCount += TileCount;
|
|
}
|
|
}
|
|
|
|
check(LocalNumPagesNeedDilation == NumPagesNeedDilation || DilationMode == 2);
|
|
|
|
const int32 PageTileCount = CopyTileCount + DilationTileCount;
|
|
FRDGBufferRef DilationPageMaskBuffer = CreateUploadBuffer(GraphBuilder, TEXT("Lumen.DilateOneTexel.DilationPageMask"), DilationPageMaskData);
|
|
FRDGBufferRef TileDataBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), PageTileCount), TEXT("Lumen.DilateOneTexel.PackedTileData"));
|
|
|
|
{
|
|
FRDGBufferRef TileAllocatorBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Lumen.DilateOneTexel.TileAllocator"));
|
|
FRDGBufferRef DilationTileAllocatorBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Lumen.DilateOneTexel.DilationTileAllocator"));
|
|
|
|
FRDGUploadData<uint32> TileAllocatorInitData(GraphBuilder, 1);
|
|
TileAllocatorInitData[0] = 0;
|
|
GraphBuilder.QueueBufferUpload(TileAllocatorBuffer, TileAllocatorInitData.GetData(), TileAllocatorInitData.GetTotalSize(), ERDGInitialDataFlags::NoCopy);
|
|
GraphBuilder.QueueBufferUpload(DilationTileAllocatorBuffer, TileAllocatorInitData.GetData(), TileAllocatorInitData.GetTotalSize(), ERDGInitialDataFlags::NoCopy);
|
|
|
|
FRDGBufferUAVRef TileAllocatorUAV = GraphBuilder.CreateUAV(TileAllocatorBuffer, PF_R32_UINT);
|
|
FRDGBufferUAVRef DilationTileAllocatorUAV = GraphBuilder.CreateUAV(DilationTileAllocatorBuffer, PF_R32_UINT);
|
|
|
|
FGenerateDilationTileDataCS::FParameters* Parameters = GraphBuilder.AllocParameters<FGenerateDilationTileDataCS::FParameters>();
|
|
Parameters->NumCardPages = CardPagesToRender.Num();
|
|
Parameters->DilationTileDataOffset = CopyTileCount;
|
|
Parameters->CardPageRectBuffer = CardCaptureRectBufferSRV;
|
|
Parameters->DilationPageMaskBuffer = GraphBuilder.CreateSRV(DilationPageMaskBuffer, PF_R32_UINT);
|
|
Parameters->RWTileAllocator = TileAllocatorUAV;
|
|
Parameters->RWDilationTileAllocator = DilationTileAllocatorUAV;
|
|
Parameters->RWPackedCardTileDataBuffer = GraphBuilder.CreateUAV(TileDataBuffer, PF_R32_UINT);
|
|
|
|
auto ComputeShader = View.ShaderMap->GetShader<FGenerateDilationTileDataCS>();
|
|
const FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(CardPagesToRender.Num(), FGenerateDilationTileDataCS::GetGroupSize());
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("GenerateDilationTileData"),
|
|
ComputeShader,
|
|
Parameters,
|
|
GroupCount);
|
|
}
|
|
|
|
FRDGTextureDesc NewAtlasDesc = CardCaptureAtlas.Albedo->Desc;
|
|
NewAtlasDesc.Flags |= TexCreate_UAV;
|
|
FRDGTextureRef DilatedAlbedo = GraphBuilder.CreateTexture(NewAtlasDesc, CardCaptureAtlas.Albedo->Name);
|
|
FRDGTextureUAVRef DilatedAlbedoUAV = GraphBuilder.CreateUAV(DilatedAlbedo, ERDGUnorderedAccessViewFlags::SkipBarrier);
|
|
|
|
NewAtlasDesc = CardCaptureAtlas.Normal->Desc;
|
|
NewAtlasDesc.Flags |= TexCreate_UAV;
|
|
FRDGTextureRef DilatedNormal = GraphBuilder.CreateTexture(NewAtlasDesc, CardCaptureAtlas.Normal->Name);
|
|
FRDGTextureUAVRef DilatedNormalUAV = GraphBuilder.CreateUAV(DilatedNormal, ERDGUnorderedAccessViewFlags::SkipBarrier);
|
|
|
|
// Cannot create UAVs on PF_DepthStencil so use PF_R32_FLOAT instead. As of now, the stencil plane is not used later down the pipeline
|
|
NewAtlasDesc = FRDGTextureDesc::Create2D(CardCaptureAtlas.Size, PF_R32_FLOAT, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable | TexCreate_NoFastClear);
|
|
FRDGTextureRef DilatedDepth = GraphBuilder.CreateTexture(NewAtlasDesc, CardCaptureAtlas.DepthStencil->Name);
|
|
FRDGTextureUAVRef DilatedDepthUAV = GraphBuilder.CreateUAV(DilatedDepth, ERDGUnorderedAccessViewFlags::SkipBarrier);
|
|
|
|
auto CopyCapturedCardPages = [&](bool bDilateOneTexel)
|
|
{
|
|
const int32 GroupCount = bDilateOneTexel ? DilationTileCount : CopyTileCount;
|
|
|
|
if (GroupCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FCopyCapturedCardPageCS::FParameters* Parameters = GraphBuilder.AllocParameters<FCopyCapturedCardPageCS::FParameters>();
|
|
Parameters->View = View.ViewUniformBuffer;
|
|
Parameters->DilationTileDataOffset = CopyTileCount;
|
|
Parameters->OneOverSourceAtlasSize = FVector2f(1.f / CardCaptureAtlas.Size.X, 1.f / CardCaptureAtlas.Size.Y);
|
|
Parameters->SourceAlbedoAtlas = CardCaptureAtlas.Albedo;
|
|
Parameters->SourceNormalAtlas = CardCaptureAtlas.Normal;
|
|
Parameters->SourceDepthAtlas = CardCaptureAtlas.DepthStencil;
|
|
Parameters->CardPageRectBuffer = CardCaptureRectBufferSRV;
|
|
Parameters->PackedCardTileDataBuffer = GraphBuilder.CreateSRV(TileDataBuffer, PF_R32_UINT);
|
|
Parameters->RWAlbedoAtlas = DilatedAlbedoUAV;
|
|
Parameters->RWNormalAtlas = DilatedNormalUAV;
|
|
Parameters->RWDepthAtlas = DilatedDepthUAV;
|
|
|
|
FCopyCapturedCardPageCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FCopyCapturedCardPageCS::FDilateOneTexel>(bDilateOneTexel);
|
|
|
|
auto ComputeShader = View.ShaderMap->GetShader<FCopyCapturedCardPageCS>(PermutationVector);
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("CopyCapturedCardPages%s", bDilateOneTexel ? TEXT(" DilateOneTexel") : TEXT("")),
|
|
ComputeShader,
|
|
Parameters,
|
|
FIntVector(GroupCount, 1, 1));
|
|
};
|
|
|
|
CopyCapturedCardPages(false);
|
|
CopyCapturedCardPages(true);
|
|
|
|
CardCaptureAtlas.Albedo = DilatedAlbedo;
|
|
CardCaptureAtlas.Normal = DilatedNormal;
|
|
CardCaptureAtlas.DepthStencil = DilatedDepth;
|
|
}
|
|
|
|
// Copy captured cards into surface cache. Possibly with compression. Has three paths:
|
|
// - Compress from capture atlas to surface cache (for platforms supporting GRHISupportsUAVFormatAliasing or when compression is disabled)
|
|
// - Compress from capture atlas into a temporary atlas and copy results into surface cache
|
|
// - Straight copy into uncompressed atlas
|
|
void FDeferredShadingSceneRenderer::UpdateLumenSurfaceCacheAtlas(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const FLumenSceneFrameTemporaries& FrameTemporaries,
|
|
const TArray<FCardPageRenderData, SceneRenderingAllocator>& CardPagesToRender,
|
|
FRDGBufferSRVRef CardCaptureRectBufferSRV,
|
|
const FCardCaptureAtlas& CardCaptureAtlas,
|
|
const FResampledCardCaptureAtlas& ResampledCardCaptureAtlas)
|
|
{
|
|
LLM_SCOPE_BYTAG(Lumen);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "CopyCardsToSurfaceCache");
|
|
|
|
FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View);
|
|
|
|
// Create rect buffer
|
|
FRDGBufferRef SurfaceCacheRectBuffer;
|
|
{
|
|
FRDGUploadData<FUintVector4> SurfaceCacheRectArray(GraphBuilder, CardPagesToRender.Num());
|
|
for (int32 Index = 0; Index < CardPagesToRender.Num(); Index++)
|
|
{
|
|
const FCardPageRenderData& CardPageRenderData = CardPagesToRender[Index];
|
|
FUintVector4& Rect = SurfaceCacheRectArray[Index];
|
|
Rect.X = FMath::Max(CardPageRenderData.SurfaceCacheAtlasRect.Min.X, 0);
|
|
Rect.Y = FMath::Max(CardPageRenderData.SurfaceCacheAtlasRect.Min.Y, 0);
|
|
Rect.Z = FMath::Max(CardPageRenderData.SurfaceCacheAtlasRect.Max.X, 0);
|
|
Rect.W = FMath::Max(CardPageRenderData.SurfaceCacheAtlasRect.Max.Y, 0);
|
|
}
|
|
|
|
SurfaceCacheRectBuffer =
|
|
CreateUploadBuffer(GraphBuilder, TEXT("Lumen.SurfaceCacheRects"),
|
|
sizeof(FUintVector4), FMath::RoundUpToPowerOfTwo(CardPagesToRender.Num()),
|
|
SurfaceCacheRectArray);
|
|
}
|
|
FRDGBufferSRVRef SurfaceCacheRectBufferSRV = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(SurfaceCacheRectBuffer, PF_R32G32B32A32_UINT));
|
|
|
|
const FIntPoint PhysicalAtlasSize = LumenSceneData.GetPhysicalAtlasSize();
|
|
const ESurfaceCacheCompression PhysicalAtlasCompression = LumenSceneData.GetPhysicalAtlasCompression();
|
|
const FIntPoint CardCaptureAtlasSize = LumenSceneData.GetCardCaptureAtlasSize();
|
|
|
|
struct FPassConfig
|
|
{
|
|
FRDGTextureRef SurfaceCacheAtlas = nullptr;
|
|
ELumenSurfaceCacheLayer Layer = ELumenSurfaceCacheLayer::MAX;
|
|
};
|
|
|
|
FPassConfig PassConfigs[(uint32)ELumenSurfaceCacheLayer::MAX] =
|
|
{
|
|
{ FrameTemporaries.DepthAtlas, ELumenSurfaceCacheLayer::Depth },
|
|
{ FrameTemporaries.AlbedoAtlas, ELumenSurfaceCacheLayer::Albedo },
|
|
{ FrameTemporaries.OpacityAtlas, ELumenSurfaceCacheLayer::Opacity },
|
|
{ FrameTemporaries.NormalAtlas, ELumenSurfaceCacheLayer::Normal },
|
|
{ FrameTemporaries.EmissiveAtlas, ELumenSurfaceCacheLayer::Emissive },
|
|
};
|
|
|
|
for (FPassConfig& Pass : PassConfigs)
|
|
{
|
|
const FLumenSurfaceLayerConfig& LayerConfig = GetSurfaceLayerConfig(Pass.Layer);
|
|
|
|
if (PhysicalAtlasCompression == ESurfaceCacheCompression::UAVAliasing && LayerConfig.CompressedFormat != PF_Unknown)
|
|
{
|
|
// Compress to surface cache directly
|
|
{
|
|
const FIntPoint CompressedCardCaptureAtlasSize = FIntPoint::DivideAndRoundUp(CardCaptureAtlasSize, 4);
|
|
const FIntPoint CompressedPhysicalAtlasSize = FIntPoint::DivideAndRoundUp(PhysicalAtlasSize, 4);
|
|
|
|
FLumenCardCopyParameters* PassParameters = GraphBuilder.AllocParameters<FLumenCardCopyParameters>();
|
|
PassParameters->PS.View = View.ViewUniformBuffer;
|
|
PassParameters->PS.RWAtlasBlock4 = LayerConfig.CompressedUAVFormat == PF_R32G32B32A32_UINT ? GraphBuilder.CreateUAV(Pass.SurfaceCacheAtlas) : nullptr;
|
|
PassParameters->PS.RWAtlasBlock2 = LayerConfig.CompressedUAVFormat == PF_R32G32_UINT ? GraphBuilder.CreateUAV(Pass.SurfaceCacheAtlas) : nullptr;
|
|
PassParameters->PS.SourceAlbedoAtlas = CardCaptureAtlas.Albedo;
|
|
PassParameters->PS.SourceNormalAtlas = CardCaptureAtlas.Normal;
|
|
PassParameters->PS.SourceEmissiveAtlas = CardCaptureAtlas.Emissive;
|
|
PassParameters->PS.SourceDepthAtlas = CardCaptureAtlas.DepthStencil;
|
|
PassParameters->PS.OneOverSourceAtlasSize = FVector2f(1.0f, 1.0f) / FVector2f(CardCaptureAtlasSize);
|
|
|
|
FLumenCardCopyPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FLumenCardCopyPS::FSurfaceCacheLayer>(Pass.Layer);
|
|
PermutationVector.Set<FLumenCardCopyPS::FCompress>(true);
|
|
auto PixelShader = View.ShaderMap->GetShader<FLumenCardCopyPS>(PermutationVector);
|
|
|
|
const ERDGPassFlags AdditionalRenderPassFlags = (PassParameters->RenderTargets.GetActiveCount() == 0) ? ERDGPassFlags::SkipRenderPass : ERDGPassFlags::None;
|
|
|
|
FPixelShaderUtils::AddRasterizeToRectsPass<FLumenCardCopyPS>(GraphBuilder,
|
|
View.ShaderMap,
|
|
RDG_EVENT_NAME("CompressToSurfaceCache %s", LayerConfig.Name),
|
|
PixelShader,
|
|
PassParameters,
|
|
CompressedPhysicalAtlasSize,
|
|
SurfaceCacheRectBufferSRV,
|
|
CardPagesToRender.Num(),
|
|
/*BlendState*/ nullptr,
|
|
/*RasterizerState*/ nullptr,
|
|
/*DepthStencilState*/ nullptr,
|
|
/*StencilRef*/ 0,
|
|
/*TextureSize*/ CompressedCardCaptureAtlasSize,
|
|
/*RectUVBufferSRV*/ CardCaptureRectBufferSRV,
|
|
/*DownsampleFactor*/ 4,
|
|
AdditionalRenderPassFlags);
|
|
}
|
|
}
|
|
else if (PhysicalAtlasCompression == ESurfaceCacheCompression::CopyTextureRegion && LayerConfig.CompressedFormat != PF_Unknown)
|
|
{
|
|
// Compress through a temp surface
|
|
const FIntPoint TempAtlasSize = FIntPoint::DivideAndRoundUp(CardCaptureAtlasSize, 4);
|
|
|
|
// TempAtlas is required on platforms without UAV aliasing (GRHISupportsUAVFormatAliasing), where we can't directly compress into the final surface cache
|
|
FRDGTextureRef TempAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
TempAtlasSize,
|
|
LayerConfig.CompressedUAVFormat,
|
|
FClearValueBinding::None,
|
|
TexCreate_UAV | TexCreate_ShaderResource | TexCreate_NoFastClear),
|
|
TEXT("Lumen.TempCaptureAtlas"));
|
|
|
|
// Compress into temporary atlas
|
|
{
|
|
FLumenCardCopyParameters* PassParameters = GraphBuilder.AllocParameters<FLumenCardCopyParameters>();
|
|
|
|
PassParameters->PS.View = View.ViewUniformBuffer;
|
|
PassParameters->PS.RWAtlasBlock4 = LayerConfig.CompressedUAVFormat == PF_R32G32B32A32_UINT ? GraphBuilder.CreateUAV(TempAtlas) : nullptr;
|
|
PassParameters->PS.RWAtlasBlock2 = LayerConfig.CompressedUAVFormat == PF_R32G32_UINT ? GraphBuilder.CreateUAV(TempAtlas) : nullptr;
|
|
PassParameters->PS.SourceAlbedoAtlas = CardCaptureAtlas.Albedo;
|
|
PassParameters->PS.SourceNormalAtlas = CardCaptureAtlas.Normal;
|
|
PassParameters->PS.SourceEmissiveAtlas = CardCaptureAtlas.Emissive;
|
|
PassParameters->PS.SourceDepthAtlas = CardCaptureAtlas.DepthStencil;
|
|
PassParameters->PS.OneOverSourceAtlasSize = FVector2f(1.0f, 1.0f) / FVector2f(CardCaptureAtlasSize);
|
|
|
|
FLumenCardCopyPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FLumenCardCopyPS::FSurfaceCacheLayer>(Pass.Layer);
|
|
PermutationVector.Set<FLumenCardCopyPS::FCompress>(true);
|
|
auto PixelShader = View.ShaderMap->GetShader<FLumenCardCopyPS>(PermutationVector);
|
|
|
|
const ERDGPassFlags AdditionalRenderPassFlags = (PassParameters->RenderTargets.GetActiveCount() == 0) ? ERDGPassFlags::SkipRenderPass : ERDGPassFlags::None;
|
|
|
|
FPixelShaderUtils::AddRasterizeToRectsPass<FLumenCardCopyPS>(GraphBuilder,
|
|
View.ShaderMap,
|
|
RDG_EVENT_NAME("CompressToTemp %s", LayerConfig.Name),
|
|
PixelShader,
|
|
PassParameters,
|
|
TempAtlasSize,
|
|
CardCaptureRectBufferSRV,
|
|
CardPagesToRender.Num(),
|
|
/*BlendState*/ nullptr,
|
|
/*RasterizerState*/ nullptr,
|
|
/*DepthStencilState*/ nullptr,
|
|
/*StencilRef*/ 0,
|
|
/*TextureSize*/ TempAtlasSize,
|
|
/*RectUVBufferSRV*/ nullptr,
|
|
/*DownsampleFactor*/ 4,
|
|
AdditionalRenderPassFlags);
|
|
}
|
|
|
|
// Copy from temporary atlas to surface cache
|
|
{
|
|
FCopyTextureParameters* Parameters = GraphBuilder.AllocParameters<FCopyTextureParameters>();
|
|
Parameters->InputTexture = TempAtlas;
|
|
Parameters->OutputTexture = Pass.SurfaceCacheAtlas;
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("CopyTempToSurfaceCache %s", LayerConfig.Name),
|
|
Parameters,
|
|
ERDGPassFlags::Copy,
|
|
[&CardPagesToRender, InputTexture = TempAtlas, OutputTexture = Pass.SurfaceCacheAtlas](FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
for (int32 PageIndex = 0; PageIndex < CardPagesToRender.Num(); ++PageIndex)
|
|
{
|
|
const FCardPageRenderData& Page = CardPagesToRender[PageIndex];
|
|
|
|
FRHICopyTextureInfo CopyInfo;
|
|
CopyInfo.Size.X = Page.CardCaptureAtlasRect.Width() / 4;
|
|
CopyInfo.Size.Y = Page.CardCaptureAtlasRect.Height() / 4;
|
|
CopyInfo.Size.Z = 1;
|
|
CopyInfo.SourcePosition.X = Page.CardCaptureAtlasRect.Min.X / 4;
|
|
CopyInfo.SourcePosition.Y = Page.CardCaptureAtlasRect.Min.Y / 4;
|
|
CopyInfo.SourcePosition.Z = 0;
|
|
CopyInfo.DestPosition.X = Page.SurfaceCacheAtlasRect.Min.X;
|
|
CopyInfo.DestPosition.Y = Page.SurfaceCacheAtlasRect.Min.Y;
|
|
CopyInfo.DestPosition.Z = 0;
|
|
|
|
RHICmdList.CopyTexture(InputTexture->GetRHI(), OutputTexture->GetRHI(), CopyInfo);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Copy uncompressed to surface cache
|
|
{
|
|
FLumenCardCopyParameters* PassParameters = GraphBuilder.AllocParameters<FLumenCardCopyParameters>();
|
|
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(Pass.SurfaceCacheAtlas, ERenderTargetLoadAction::ELoad, 0);
|
|
PassParameters->PS.View = View.ViewUniformBuffer;
|
|
PassParameters->PS.SourceAlbedoAtlas = CardCaptureAtlas.Albedo;
|
|
PassParameters->PS.SourceNormalAtlas = CardCaptureAtlas.Normal;
|
|
PassParameters->PS.SourceEmissiveAtlas = CardCaptureAtlas.Emissive;
|
|
PassParameters->PS.SourceDepthAtlas = CardCaptureAtlas.DepthStencil;
|
|
PassParameters->PS.OneOverSourceAtlasSize = FVector2f(1.0f, 1.0f) / FVector2f(CardCaptureAtlasSize);
|
|
|
|
bool bCullUndergroundTexels = Pass.Layer == ELumenSurfaceCacheLayer::Depth && LumenScene::CullUndergroundTexels();
|
|
|
|
if (bCullUndergroundTexels)
|
|
{
|
|
bCullUndergroundTexels = Lumen::SetLandscapeHeightSamplingParameters(
|
|
FrameTemporaries.ViewOrigins[0].LumenSceneViewOrigin,
|
|
Scene,
|
|
PassParameters->PS.LandscapeHeightSamplingParameters);
|
|
|
|
if (bCullUndergroundTexels)
|
|
{
|
|
FRDGUploadData<FVector4f> CardUVRectsData(GraphBuilder, CardPagesToRender.Num());
|
|
FRDGUploadData<uint32> CardIndicesData(GraphBuilder, CardPagesToRender.Num());
|
|
|
|
for (int32 Index = 0; Index < CardPagesToRender.Num(); ++Index)
|
|
{
|
|
const FCardPageRenderData& CardPageRenderData = CardPagesToRender[Index];
|
|
|
|
check(CardPageRenderData.CardIndex >= 0);
|
|
CardUVRectsData[Index] = CardPageRenderData.CardUVRect;
|
|
CardIndicesData[Index] = CardPageRenderData.bHeightField ? 0xffffffff : CardPageRenderData.CardIndex;
|
|
}
|
|
|
|
FRDGBufferRef CardUVRectsBuffer = CreateUploadBuffer(GraphBuilder, TEXT("Lumen.CardUVRects"), CardUVRectsData);
|
|
FRDGBufferRef CardIndicesBuffer = CreateUploadBuffer(GraphBuilder, TEXT("Lumen.CardIndices"), CardIndicesData);
|
|
|
|
PassParameters->PS.LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
|
|
PassParameters->PS.CardUVRects = GraphBuilder.CreateSRV(CardUVRectsBuffer, PF_R32G32B32A32_UINT);
|
|
PassParameters->PS.CardIndices = GraphBuilder.CreateSRV(CardIndicesBuffer, PF_R32_UINT);
|
|
PassParameters->PS.TexelCullingHeightBias = CVarLumenSurfaceCacheCullUndergroundTexelsHeightBias.GetValueOnRenderThread();
|
|
}
|
|
}
|
|
|
|
FLumenCardCopyPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FLumenCardCopyPS::FSurfaceCacheLayer>(Pass.Layer);
|
|
PermutationVector.Set<FLumenCardCopyPS::FCompress>(false);
|
|
PermutationVector.Set<FLumenCardCopyPS::FCullUndergroundTexels>(bCullUndergroundTexels);
|
|
auto PixelShader = View.ShaderMap->GetShader<FLumenCardCopyPS>(PermutationVector);
|
|
|
|
FPixelShaderUtils::AddRasterizeToRectsPass<FLumenCardCopyPS>(GraphBuilder,
|
|
View.ShaderMap,
|
|
RDG_EVENT_NAME("CopyToSurfaceCache %s", LayerConfig.Name),
|
|
PixelShader,
|
|
PassParameters,
|
|
PhysicalAtlasSize,
|
|
SurfaceCacheRectBufferSRV,
|
|
CardPagesToRender.Num(),
|
|
/*BlendState*/ nullptr,
|
|
/*RasterizerState*/ nullptr,
|
|
/*DepthStencilState*/ nullptr,
|
|
/*StencilRef*/ 0,
|
|
/*TextureSize*/ CardCaptureAtlasSize,
|
|
/*RectUVBufferSRV*/ CardCaptureRectBufferSRV);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fill lighting for newly captured cards
|
|
{
|
|
// Downsampled radiosity atlas copy not implemented yet
|
|
check(LumenRadiosity::GetAtlasDownsampleFactor() == 1);
|
|
|
|
extern int32 GLumenSceneSurfaceCacheResampleLighting;
|
|
const bool bResample = GLumenSceneSurfaceCacheResampleLighting != 0 && ResampledCardCaptureAtlas.DirectLighting != nullptr;
|
|
const bool bRadiosityEnabled = LumenRadiosity::IsEnabled(ViewFamily);
|
|
|
|
FCopyCardCaptureLightingToAtlasParameters* PassParameters = GraphBuilder.AllocParameters<FCopyCardCaptureLightingToAtlasParameters>();
|
|
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(FrameTemporaries.DirectLightingAtlas, ERenderTargetLoadAction::ELoad, 0);
|
|
PassParameters->RenderTargets[1] = FRenderTargetBinding(FrameTemporaries.FinalLightingAtlas, ERenderTargetLoadAction::ELoad, 0);
|
|
PassParameters->RenderTargets[2] = FRenderTargetBinding(FrameTemporaries.IndirectLightingAtlas, ERenderTargetLoadAction::ELoad, 0);
|
|
PassParameters->RenderTargets[3] = FRenderTargetBinding(FrameTemporaries.RadiosityNumFramesAccumulatedAtlas, ERenderTargetLoadAction::ELoad, 0);
|
|
|
|
PassParameters->PS.View = View.ViewUniformBuffer;
|
|
PassParameters->PS.DiffuseColorBoost = 1.0f / FMath::Max(View.FinalPostProcessSettings.LumenDiffuseColorBoost, 1.0f);
|
|
const FIntPoint CardCaptureAtlasSizeInTiles = CardCaptureAtlasSize / Lumen::CardTileSize;
|
|
PassParameters->PS.CardCaptureAtlasSizeInTiles = FUintVector2(CardCaptureAtlasSizeInTiles.X, CardCaptureAtlasSizeInTiles.Y);
|
|
PassParameters->PS.OutputAtlasWidthInTiles = PhysicalAtlasSize.X / Lumen::CardTileSize;
|
|
PassParameters->PS.AlbedoCardCaptureAtlas = CardCaptureAtlas.Albedo;
|
|
PassParameters->PS.EmissiveCardCaptureAtlas = CardCaptureAtlas.Emissive;
|
|
PassParameters->PS.DirectLightingCardCaptureAtlas = ResampledCardCaptureAtlas.DirectLighting;
|
|
PassParameters->PS.RadiosityCardCaptureAtlas = ResampledCardCaptureAtlas.IndirectLighting;
|
|
PassParameters->PS.RadiosityNumFramesAccumulatedCardCaptureAtlas = ResampledCardCaptureAtlas.NumFramesAccumulated;
|
|
PassParameters->PS.TileShadowDownsampleFactorAtlasForResampling = bResample ? GraphBuilder.CreateSRV(ResampledCardCaptureAtlas.TileShadowDownsampleFactor, PF_R32G32B32A32_UINT) : nullptr;
|
|
PassParameters->PS.RWTileShadowDownsampleFactorAtlas = GraphBuilder.CreateUAV(FrameTemporaries.TileShadowDownsampleFactorAtlas, PF_R32G32B32A32_UINT);
|
|
|
|
FCopyCardCaptureLightingToAtlasPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FCopyCardCaptureLightingToAtlasPS::FIndirectLighting>(bRadiosityEnabled);
|
|
PermutationVector.Set<FCopyCardCaptureLightingToAtlasPS::FResample>(bResample);
|
|
auto PixelShader = View.ShaderMap->GetShader<FCopyCardCaptureLightingToAtlasPS>(PermutationVector);
|
|
|
|
FPixelShaderUtils::AddRasterizeToRectsPass<FCopyCardCaptureLightingToAtlasPS>(GraphBuilder,
|
|
View.ShaderMap,
|
|
RDG_EVENT_NAME("CopyCardCaptureLightingToAtlas"),
|
|
PixelShader,
|
|
PassParameters,
|
|
LumenSceneData.GetPhysicalAtlasSize(),
|
|
SurfaceCacheRectBufferSRV,
|
|
CardPagesToRender.Num(),
|
|
/*BlendState*/ nullptr,
|
|
/*RasterizerState*/ nullptr,
|
|
/*DepthStencilState*/ nullptr,
|
|
/*StencilRef*/ 0,
|
|
/*TextureSize*/ CardCaptureAtlasSize,
|
|
/*RectUVBufferSRV*/ CardCaptureRectBufferSRV,
|
|
/*DownsampleFactor*/ 1);
|
|
}
|
|
}
|
|
|
|
class FClearCompressedAtlasCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FClearCompressedAtlasCS)
|
|
SHADER_USE_PARAMETER_STRUCT(FClearCompressedAtlasCS, FGlobalShader)
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint4>, RWAtlasBlock4)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<uint2>, RWAtlasBlock2)
|
|
SHADER_PARAMETER(FVector3f, ClearValue)
|
|
SHADER_PARAMETER(FIntPoint, OutputAtlasSize)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FSurfaceCacheLayer : SHADER_PERMUTATION_ENUM_CLASS("SURFACE_LAYER", ELumenSurfaceCacheLayer);
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<FSurfaceCacheLayer>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return DoesPlatformSupportLumenGI(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
|
|
}
|
|
|
|
static int32 GetGroupSize()
|
|
{
|
|
return 8;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FClearCompressedAtlasCS, "/Engine/Private/Lumen/SurfaceCache/LumenSurfaceCache.usf", "ClearCompressedAtlasCS", SF_Compute);
|
|
|
|
// Clear entire Lumen surface cache to debug default values
|
|
// Surface cache can be compressed
|
|
void FDeferredShadingSceneRenderer::ClearLumenSurfaceCacheAtlas(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FLumenSceneFrameTemporaries& FrameTemporaries,
|
|
const FGlobalShaderMap* GlobalShaderMap)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "ClearLumenSurfaceCache");
|
|
|
|
FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(Views[0]);
|
|
|
|
struct FPassConfig
|
|
{
|
|
FRDGTextureRef SurfaceCacheAtlas = nullptr;
|
|
ELumenSurfaceCacheLayer Layer = ELumenSurfaceCacheLayer::MAX;
|
|
};
|
|
|
|
FPassConfig PassConfigs[(uint32)ELumenSurfaceCacheLayer::MAX] =
|
|
{
|
|
{ FrameTemporaries.DepthAtlas, ELumenSurfaceCacheLayer::Depth },
|
|
{ FrameTemporaries.AlbedoAtlas, ELumenSurfaceCacheLayer::Albedo },
|
|
{ FrameTemporaries.OpacityAtlas, ELumenSurfaceCacheLayer::Opacity },
|
|
{ FrameTemporaries.NormalAtlas, ELumenSurfaceCacheLayer::Normal },
|
|
{ FrameTemporaries.EmissiveAtlas, ELumenSurfaceCacheLayer::Emissive },
|
|
};
|
|
|
|
const FIntPoint PhysicalAtlasSize = LumenSceneData.GetPhysicalAtlasSize();
|
|
const ESurfaceCacheCompression PhysicalAtlasCompression = LumenSceneData.GetPhysicalAtlasCompression();
|
|
|
|
for (FPassConfig& Pass : PassConfigs)
|
|
{
|
|
const FLumenSurfaceLayerConfig& LayerConfig = GetSurfaceLayerConfig(Pass.Layer);
|
|
|
|
if (PhysicalAtlasCompression == ESurfaceCacheCompression::UAVAliasing && LayerConfig.CompressedFormat != PF_Unknown)
|
|
{
|
|
// Clear compressed surface cache directly
|
|
{
|
|
const FRDGTextureUAVDesc CompressedSurfaceUAVDesc(Pass.SurfaceCacheAtlas, 0, LayerConfig.CompressedUAVFormat);
|
|
|
|
FClearCompressedAtlasCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FClearCompressedAtlasCS::FParameters>();
|
|
PassParameters->RWAtlasBlock4 = LayerConfig.CompressedUAVFormat == PF_R32G32B32A32_UINT ? GraphBuilder.CreateUAV(CompressedSurfaceUAVDesc) : nullptr;
|
|
PassParameters->RWAtlasBlock2 = LayerConfig.CompressedUAVFormat == PF_R32G32_UINT ? GraphBuilder.CreateUAV(CompressedSurfaceUAVDesc) : nullptr;
|
|
PassParameters->ClearValue = (FVector3f)LayerConfig.ClearValue;
|
|
PassParameters->OutputAtlasSize = PhysicalAtlasSize;
|
|
|
|
FClearCompressedAtlasCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FClearCompressedAtlasCS::FSurfaceCacheLayer>(Pass.Layer);
|
|
auto ComputeShader = GlobalShaderMap->GetShader<FClearCompressedAtlasCS>(PermutationVector);
|
|
|
|
FIntPoint GroupSize(FIntPoint::DivideAndRoundUp(PhysicalAtlasSize, FClearCompressedAtlasCS::GetGroupSize()));
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("ClearCompressedAtlas %s", LayerConfig.Name),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FIntVector(GroupSize.X, GroupSize.Y, 1));
|
|
}
|
|
}
|
|
else if (PhysicalAtlasCompression == ESurfaceCacheCompression::CopyTextureRegion && LayerConfig.CompressedFormat != PF_Unknown)
|
|
{
|
|
// Temporary atlas is required on platforms without UAV aliasing (GRHISupportsUAVFormatAliasing), where we can't directly compress into the final surface cache
|
|
const FIntPoint TempAtlasSize = FIntPoint::DivideAndRoundUp(LumenSceneData.GetCardCaptureAtlasSize(), 4);
|
|
|
|
const EPixelFormat TempFormat = LayerConfig.CompressedUAVFormat;
|
|
|
|
FRDGTextureRef TempAtlas = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create2D(
|
|
TempAtlasSize,
|
|
LayerConfig.CompressedUAVFormat,
|
|
FClearValueBinding::None,
|
|
TexCreate_UAV | TexCreate_ShaderResource | TexCreate_NoFastClear),
|
|
TEXT("Lumen.TempCaptureAtlas"));
|
|
|
|
// Clear temporary atlas
|
|
{
|
|
FClearCompressedAtlasCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FClearCompressedAtlasCS::FParameters>();
|
|
PassParameters->RWAtlasBlock4 = LayerConfig.CompressedUAVFormat == PF_R32G32B32A32_UINT ? GraphBuilder.CreateUAV(TempAtlas) : nullptr;
|
|
PassParameters->RWAtlasBlock2 = LayerConfig.CompressedUAVFormat == PF_R32G32_UINT ? GraphBuilder.CreateUAV(TempAtlas) : nullptr;
|
|
PassParameters->ClearValue = (FVector3f)LayerConfig.ClearValue;
|
|
PassParameters->OutputAtlasSize = TempAtlasSize;
|
|
|
|
FClearCompressedAtlasCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FClearCompressedAtlasCS::FSurfaceCacheLayer>(Pass.Layer);
|
|
auto ComputeShader = GlobalShaderMap->GetShader<FClearCompressedAtlasCS>(PermutationVector);
|
|
|
|
FIntPoint GroupSize(FIntPoint::DivideAndRoundUp(TempAtlasSize, FClearCompressedAtlasCS::GetGroupSize()));
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("ClearCompressedAtlas %s", LayerConfig.Name),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FIntVector(GroupSize.X, GroupSize.Y, 1));
|
|
}
|
|
|
|
// Copy from temporary atlas into surface cache
|
|
{
|
|
FCopyTextureParameters* Parameters = GraphBuilder.AllocParameters<FCopyTextureParameters>();
|
|
Parameters->InputTexture = TempAtlas;
|
|
Parameters->OutputTexture = Pass.SurfaceCacheAtlas;
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("CopyToSurfaceCache %s", LayerConfig.Name),
|
|
Parameters,
|
|
ERDGPassFlags::Copy,
|
|
[InputTexture = TempAtlas, PhysicalAtlasSize, TempAtlasSize, OutputTexture = Pass.SurfaceCacheAtlas](FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
const int32 NumTilesX = FMath::DivideAndRoundDown(PhysicalAtlasSize.X / 4, TempAtlasSize.X);
|
|
const int32 NumTilesY = FMath::DivideAndRoundDown(PhysicalAtlasSize.Y / 4, TempAtlasSize.Y);
|
|
|
|
for (int32 TileY = 0; TileY < NumTilesY; ++TileY)
|
|
{
|
|
for (int32 TileX = 0; TileX < NumTilesX; ++TileX)
|
|
{
|
|
FRHICopyTextureInfo CopyInfo;
|
|
CopyInfo.Size.X = TempAtlasSize.X;
|
|
CopyInfo.Size.Y = TempAtlasSize.Y;
|
|
CopyInfo.Size.Z = 1;
|
|
CopyInfo.SourcePosition.X = 0;
|
|
CopyInfo.SourcePosition.Y = 0;
|
|
CopyInfo.SourcePosition.Z = 0;
|
|
CopyInfo.DestPosition.X = TileX * TempAtlasSize.X * 4;
|
|
CopyInfo.DestPosition.Y = TileY * TempAtlasSize.Y * 4;
|
|
CopyInfo.DestPosition.Z = 0;
|
|
|
|
RHICmdList.CopyTexture(InputTexture->GetRHI(), OutputTexture->GetRHI(), CopyInfo);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Simple clear of an uncompressed surface cache
|
|
AddClearRenderTargetPass(GraphBuilder, Pass.SurfaceCacheAtlas, FLinearColor(LayerConfig.ClearValue));
|
|
}
|
|
}
|
|
|
|
AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.DirectLightingAtlas);
|
|
AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.IndirectLightingAtlas);
|
|
AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.RadiosityNumFramesAccumulatedAtlas);
|
|
AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.FinalLightingAtlas);
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(FrameTemporaries.TileShadowDownsampleFactorAtlas, PF_R32_UINT), 0);
|
|
} |