Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/ScreenSpaceReflectionTiles.cpp
2025-05-18 13:04:45 +08:00

220 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ScreenSpaceReflectionTiles.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "SceneRendering.h"
static TAutoConsoleVariable<int32> CVarSSRTiledComposite(
TEXT("r.SSR.TiledComposite"), 0,
TEXT("Enable tiled optimization of the screen space reflection."),
ECVF_RenderThreadSafe | ECVF_Scalability);
static TAutoConsoleVariable<float> CVarSSRTiledCompositeOverrideMaxRoughness(
TEXT("r.SSR.TiledComposite.OverrideMaxRoughness"), -1.0f,
TEXT("Ignore pixels with roughness larger than this value.")
TEXT("<0: use derived value from ScreenSpaceReflectionMaxRoughness of FinalPostProcessSettings."),
ECVF_RenderThreadSafe | ECVF_Scalability);
static TAutoConsoleVariable<float> CVarSSRTiledCompositeMinSpecular(
TEXT("r.SSR.TiledComposite.MinSpecular"), 0.0f,
TEXT("Ignore pixels with very small specular contribution in case max roughness cannot filter them out"),
ECVF_RenderThreadSafe | ECVF_Scalability);
static TAutoConsoleVariable<int32> CVarSSRTiledCompositeTwoSidedFoliage(
TEXT("r.SSR.TiledComposite.TwoSidedFoliage"), 0,
TEXT("0: diable SSR for foliage if tiling is enabled."),
ECVF_RenderThreadSafe | ECVF_Scalability);
static TAutoConsoleVariable<bool> CVarSSRTiledCompositeVisualize(
TEXT("r.SSR.TiledComposite.Visualize"), 0,
TEXT("1: Visualize the tiling region."),
ECVF_RenderThreadSafe | ECVF_Scalability);
static float GetScreenSpaceReflectionMaxRoughnessScale(const FViewInfo& View)
{
float MaxRoughness = CVarSSRTiledCompositeOverrideMaxRoughness.GetValueOnRenderThread();
if (MaxRoughness < 0)
{
MaxRoughness = FMath::Clamp(View.FinalPostProcessSettings.ScreenSpaceReflectionMaxRoughness, 0.01f, 1.0f);
}
return MaxRoughness;
}
bool UseSSRIndirectDraw(EShaderPlatform ShaderPlatform)
{
return IsFeatureLevelSupported(ShaderPlatform, ERHIFeatureLevel::SM5)
// todo: check if this is true for screenspacereflection: Vulkan gives error with SSRTileCatergorisationMarkCS usage of atomic, and Metal does not play nice, either.
&& !IsVulkanMobilePlatform(ShaderPlatform);
//&& FDataDrivenShaderPlatformInfo::GetSupportsWaterIndirectDraw(ShaderPlatform);
}
bool ShouldVisualizeTiledScreenSpaceReflection()
{
return CVarSSRTiledCompositeVisualize.GetValueOnRenderThread() != 0;
}
class FSSRTileCategorisationMarkCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSSRTileCategorisationMarkCS);
SHADER_USE_PARAMETER_STRUCT(FSSRTileCategorisationMarkCS, FGlobalShader)
class FUsePrepassStencil : SHADER_PERMUTATION_BOOL("USE_SSR_PRE_PASS_STENCIL");
using FPermutationDomain = TShaderPermutationDomain<FUsePrepassStencil>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate)
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SSRDepthStencilTexture)
SHADER_PARAMETER(FIntPoint, TiledViewRes)
SHADER_PARAMETER(float, MaxRoughness)
SHADER_PARAMETER(float, MinSpecular)
SHADER_PARAMETER(int32, bEnableTwoSidedFoliage)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, TileMaskBufferOut)
END_SHADER_PARAMETER_STRUCT()
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
return PermutationVector;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return UseSSRIndirectDraw(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("TILE_CATERGORISATION_SHADER"), 1);
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FSSRTileCategorisationMarkCS, "/Engine/Private/DefaultSSRTiles.usf", "SSRTileCategorisationMarkCS", SF_Compute);
class FSSRTileClassificationBuildListsCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSSRTileClassificationBuildListsCS);
SHADER_USE_PARAMETER_STRUCT(FSSRTileClassificationBuildListsCS, FGlobalShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER(uint32, VertexCountPerInstanceIndirect)
SHADER_PARAMETER(FIntPoint, TiledViewRes)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, DrawIndirectDataUAV)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, DispatchIndirectDataUAV)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, SSRTileListDataUAV)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, TileMaskBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return UseSSRIndirectDraw(Parameters.Platform);
}
static int32 GetGroupSize()
{
return SSR_TILE_SIZE_XY;
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("TILE_CATERGORISATION_SHADER"), 1);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
}
};
IMPLEMENT_GLOBAL_SHADER(FSSRTileClassificationBuildListsCS, "/Engine/Private/DefaultSSRTiles.usf", "SSRTileClassificationBuildListsCS", SF_Compute);
bool IsDefaultSSRTileEnabled(const FViewInfo& View)
{
return UseSSRIndirectDraw(View.GetShaderPlatform()) && CVarSSRTiledComposite.GetValueOnRenderThread();
}
/**
* Build lists of 8x8 tiles used by SSR pixels
* Mark and build list steps are separated in order to build a more coherent list (z-ordered over a larger region), which is important for the performance of future passes like ray traced Lumen reflections
*/
FScreenSpaceReflectionTileClassification ClassifySSRTiles(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSceneTextures& SceneTextures, const FRDGTextureRef& DepthPrepassTexture)
{
FScreenSpaceReflectionTileClassification Result;
const bool bRunTiled = UseSSRIndirectDraw(View.GetShaderPlatform()) && CVarSSRTiledComposite.GetValueOnRenderThread();
if (bRunTiled)
{
FIntPoint ViewRes(View.ViewRect.Width(), View.ViewRect.Height());
Result.TiledViewRes = FIntPoint::DivideAndRoundUp(ViewRes, SSR_TILE_SIZE_XY);
Result.TiledReflection.DrawIndirectParametersBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDrawIndirectParameters>(), TEXT("SSR.IndirectDrawParameters"));
Result.TiledReflection.DispatchIndirectParametersBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDispatchIndirectParameters>(1), TEXT("SSR.IndirectDispatchParameters"));
FRDGBufferRef TileListDataBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Result.TiledViewRes.X * Result.TiledViewRes.Y), TEXT("SSR.TileListDataBuffer"));
Result.TiledReflection.TileListDataBufferSRV = GraphBuilder.CreateSRV(TileListDataBuffer, PF_R32_UINT);
FRDGBufferUAVRef DrawIndirectParametersBufferUAV = GraphBuilder.CreateUAV(Result.TiledReflection.DrawIndirectParametersBuffer);
FRDGBufferUAVRef DispatchIndirectParametersBufferUAV = GraphBuilder.CreateUAV(Result.TiledReflection.DispatchIndirectParametersBuffer);
// Allocate buffer with 1 bit / tile
Result.TileMaskBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), FMath::DivideAndRoundUp(Result.TiledViewRes.X * Result.TiledViewRes.Y, 32)), TEXT("SSR.TileMaskBuffer"));
FRDGBufferUAVRef TileMaskBufferUAV = GraphBuilder.CreateUAV(Result.TileMaskBuffer);
AddClearUAVPass(GraphBuilder, TileMaskBufferUAV, 0);
// Clear DrawIndirectParametersBuffer
AddClearUAVPass(GraphBuilder, DrawIndirectParametersBufferUAV, 0);
AddClearUAVPass(GraphBuilder, DispatchIndirectParametersBufferUAV, 0);
// Mark used tiles based on SHADING_MODEL_ID, roughness
{
typedef FSSRTileCategorisationMarkCS SHADER;
SHADER::FPermutationDomain PermutationVector;
PermutationVector.Set<SHADER::FUsePrepassStencil>(DepthPrepassTexture != nullptr);
TShaderMapRef<SHADER> ComputeShader(View.ShaderMap, PermutationVector);
SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters<SHADER::FParameters>();
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
PassParameters->View = View.GetShaderParameters();
PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View);
PassParameters->TiledViewRes = Result.TiledViewRes;
PassParameters->SSRDepthStencilTexture = DepthPrepassTexture ? GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(DepthPrepassTexture, PF_X24_G8)) : nullptr;
PassParameters->TileMaskBufferOut = TileMaskBufferUAV;
PassParameters->MaxRoughness = GetScreenSpaceReflectionMaxRoughnessScale(View);
PassParameters->MinSpecular = FMath::Clamp(CVarSSRTiledCompositeMinSpecular.GetValueOnRenderThread(), -0.001f, 1.001f);
PassParameters->bEnableTwoSidedFoliage = CVarSSRTiledCompositeTwoSidedFoliage.GetValueOnRenderThread() != 0;
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSR::TileCategorisationMarkTiles"),
ComputeShader,
PassParameters,
FIntVector(Result.TiledViewRes.X, Result.TiledViewRes.Y, 1)
);
}
// Build compacted and coherent z-order tile lists from bit-marked tiles
{
typedef FSSRTileClassificationBuildListsCS SHADER;
TShaderMapRef<SHADER> ComputeShader(View.ShaderMap);
SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters<SHADER::FParameters>();
PassParameters->View = View.GetShaderParameters();
PassParameters->TiledViewRes = Result.TiledViewRes;
PassParameters->VertexCountPerInstanceIndirect = GRHISupportsRectTopology ? 3 : 6;
PassParameters->DrawIndirectDataUAV = DrawIndirectParametersBufferUAV;
PassParameters->DispatchIndirectDataUAV = DispatchIndirectParametersBufferUAV;
PassParameters->SSRTileListDataUAV = GraphBuilder.CreateUAV(TileListDataBuffer, PF_R32_UINT);
PassParameters->TileMaskBuffer = GraphBuilder.CreateSRV(Result.TileMaskBuffer);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSR::TileCategorisationBuildList"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(Result.TiledViewRes, SHADER::GetGroupSize())
);
}
}
return Result;
}