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

1046 lines
49 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "WaterInfoTextureRendering.h"
#include "UnrealClient.h"
#include "RenderGraph.h"
#include "DeferredShadingRenderer.h"
#include "SceneUtils.h"
#include "ScenePrivate.h"
#include "MeshPassProcessor.inl"
#include "PixelShaderUtils.h"
#include "BasePassRendering.h"
#include "MobileBasePassRendering.h"
#include "RenderCaptureInterface.h"
DECLARE_GPU_DRAWCALL_STAT(WaterInfoTexture);
static TAutoConsoleVariable<float> CVarWaterInfoUndergroundDilationDepthOffset(
TEXT("r.Water.WaterInfo.UndergroundDilationDepthOffset"),
64.f,
TEXT("The minimum distance below the ground when we allow dilation to write on top of water"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarWaterInfoDilationOverwriteMinimumDistance(
TEXT("r.Water.WaterInfo.DilationOverwriteMinimumDistance"),
128.f,
TEXT("The minimum distance below the ground when we allow dilation to write on top of water"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRenderCaptureNextWaterInfoDraws(
TEXT("r.Water.WaterInfo.RenderCaptureNextWaterInfoDraws"),
0,
TEXT("Enable capturing of the water info texture for the next N draws"),
ECVF_RenderThreadSafe);
BEGIN_SHADER_PARAMETER_STRUCT(FWaterInfoTexturePassParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FWaterInfoTextureDepthPassParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
class FWaterInfoTextureMergePS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FWaterInfoTextureMergePS);
SHADER_USE_PARAMETER_STRUCT(FWaterInfoTextureMergePS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float>, GroundDepthTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, GroundDepthTextureSampler)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float4>, WaterBodyTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, WaterBodyTextureSampler)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float>, WaterBodyDepthTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, WaterBodyDepthTextureSampler)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float>, DilatedWaterBodyDepthTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, DilatedWaterBodyDepthTextureSampler)
SHADER_PARAMETER(FVector2f, WaterHeightExtents)
SHADER_PARAMETER(float, GroundZMin)
SHADER_PARAMETER(float, CaptureZ)
SHADER_PARAMETER(float, UndergroundDilationDepthOffset)
SHADER_PARAMETER(float, DilationOverwriteMinimumDistance)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
class FEnable128BitRT : SHADER_PERMUTATION_BOOL("ENABLE_128_BIT");
using FPermutationDomain = TShaderPermutationDomain<FEnable128BitRT>;
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
const FPermutationDomain PermutationVector(Parameters.PermutationId);
OutEnvironment.SetRenderTargetOutputFormat(0, PermutationVector.Get<FEnable128BitRT>() ? PF_A32B32G32R32F : PF_FloatRGBA);
}
};
IMPLEMENT_GLOBAL_SHADER(FWaterInfoTextureMergePS, "/Engine/Private/WaterInfoTextureMerge.usf", "Main", SF_Pixel);
class FWaterInfoTextureBlurPS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FWaterInfoTextureBlurPS);
SHADER_USE_PARAMETER_STRUCT(FWaterInfoTextureBlurPS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, WaterInfoTexture)
SHADER_PARAMETER(float, WaterZMin)
SHADER_PARAMETER(float, WaterZMax)
SHADER_PARAMETER(float, GroundZMin)
SHADER_PARAMETER(float, CaptureZ)
SHADER_PARAMETER(int, BlurRadius)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
class FEnable128BitRT : SHADER_PERMUTATION_BOOL("ENABLE_128_BIT");
using FPermutationDomain = TShaderPermutationDomain<FEnable128BitRT>;
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
const FPermutationDomain PermutationVector(Parameters.PermutationId);
OutEnvironment.SetRenderTargetOutputFormat(0, PermutationVector.Get<FEnable128BitRT>() ? PF_A32B32G32R32F : PF_FloatRGBA);
}
};
IMPLEMENT_GLOBAL_SHADER(FWaterInfoTextureBlurPS, "/Engine/Private/WaterInfoTextureBlur.usf", "Main", SF_Pixel);
/**
* MeshPassProcessor for the "color" pass required for generating the water info texture. The associated pass draws water body meshes with an unlit material
* in order to write water surface depth, river velocity and possibly other data too.
*/
class FWaterInfoTexturePassMeshProcessor : public FSceneRenderingAllocatorObject<FWaterInfoTexturePassMeshProcessor>, public FMeshPassProcessor
{
public:
FWaterInfoTexturePassMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext, bool bIsMobile);
virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final;
virtual void CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers) override final;
private:
bool TryAddMeshBatch(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material);
FMeshPassProcessorRenderState PassDrawRenderState;
TArray<const TCHAR*, TInlineAllocator<1>> MaterialAllowList;
bool bIsMobile;
};
FWaterInfoTexturePassMeshProcessor::FWaterInfoTexturePassMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext, bool bIsMobile)
: FMeshPassProcessor(EMeshPass::WaterInfoTexturePass, Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext),
bIsMobile(bIsMobile)
{
PassDrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
PassDrawRenderState.SetDepthStencilAccess(FExclusiveDepthStencil::DepthWrite_StencilNop);
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
// HACK: This whole path for rendering the water info texture is a temporary solution, so in order to avoid supporting generic material setups, we use an allow list to restrict
// the set of supported materials to known working (and required) materials.
MaterialAllowList.Add(TEXT("DrawWaterInfo"));
}
void FWaterInfoTexturePassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
{
if (!MeshBatch.bUseForMaterial)
{
return;
}
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
while (MaterialRenderProxy)
{
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
if (Material && MaterialAllowList.Contains(Material->GetAssetName()))
{
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material))
{
break;
}
}
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
}
}
void FWaterInfoTexturePassMeshProcessor::CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers)
{
// Try to reduce the number of materials considered for this mesh pass:
// Materials for drawing the water info texture are supposed to be unlit (they write velocity and possibly other data into Emissive).
// They also need to be applied to meshes (MD_Surface) and we can safely exclude sky materials which also use the unlit shading model.
if (Material.GetShadingModels().IsUnlit() && Material.GetMaterialDomain() == MD_Surface && !Material.IsSky() && MaterialAllowList.Contains(Material.GetAssetName()))
{
// Determine the mesh's material and blend mode.
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(PreCacheParams);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
// Setup the render target info
FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo;
RenderTargetsInfo.NumSamples = 1;
AddRenderTargetInfo(PF_FloatRGBA, TexCreate_RenderTargetable | TexCreate_ShaderResource, RenderTargetsInfo);
SetupDepthStencilInfo(PF_DepthStencil, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthWrite_StencilNop, RenderTargetsInfo);
if (!bIsMobile)
{
// Get the shaders if possible for given vertex factory
TMeshProcessorShaders<
TBasePassVertexShaderPolicyParamType<FUniformLightMapPolicy>,
TBasePassPixelShaderPolicyParamType<FUniformLightMapPolicy>> PassShaders;
if (!GetBasePassShaders<FUniformLightMapPolicy>(
Material,
VertexFactoryData.VertexFactoryType,
FUniformLightMapPolicy(LMP_NO_LIGHTMAP),
FeatureLevel,
false, // bRenderSkylight
false, // 128bit
false, // bIsDebug
GBL_Default, // Currently only Nanite supports non-default layout
&PassShaders.VertexShader,
&PassShaders.PixelShader
))
{
return;
}
AddGraphicsPipelineStateInitializer(
VertexFactoryData,
Material,
PassDrawRenderState,
RenderTargetsInfo,
PassShaders,
MeshFillMode,
MeshCullMode,
(EPrimitiveType)PreCacheParams.PrimitiveType,
EMeshPassFeatures::Default,
true /*bRequired*/,
PSOInitializers);
}
else
{
TMeshProcessorShaders<
TMobileBasePassVSPolicyParamType<FUniformLightMapPolicy>,
TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>> PassShaders;
FMaterialShaderTypes ShaderTypes;
ShaderTypes.AddShaderType<TMobileBasePassVS<TUniformLightMapPolicy<LMP_NO_LIGHTMAP>>>();
ShaderTypes.AddShaderType<TMobileBasePassPS<TUniformLightMapPolicy<LMP_NO_LIGHTMAP>, LOCAL_LIGHTS_DISABLED>>();
FMaterialShaders Shaders;
if (!Material.TryGetShaders(ShaderTypes, VertexFactoryData.VertexFactoryType, Shaders))
{
return;
}
Shaders.TryGetVertexShader(PassShaders.VertexShader);
Shaders.TryGetPixelShader(PassShaders.PixelShader);
AddGraphicsPipelineStateInitializer(
VertexFactoryData,
Material,
PassDrawRenderState,
RenderTargetsInfo,
PassShaders,
MeshFillMode,
MeshCullMode,
(EPrimitiveType)PreCacheParams.PrimitiveType,
EMeshPassFeatures::Default,
true /*bRequired*/,
PSOInitializers);
}
}
}
bool FWaterInfoTexturePassMeshProcessor::TryAddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy& MaterialRenderProxy, const FMaterial& Material)
{
// Try to reduce the number of materials considered for this mesh pass:
// Materials for drawing the water info texture are supposed to be unlit (they write velocity and possibly other data into Emissive).
// They also need to be applied to meshes (MD_Surface) and we can safely exclude sky materials which also use the unlit shading model.
if (Material.GetShadingModels().IsUnlit() && Material.GetMaterialDomain() == MD_Surface && !Material.IsSky())
{
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
if (!bIsMobile)
{
TMeshProcessorShaders<
TBasePassVertexShaderPolicyParamType<FUniformLightMapPolicy>,
TBasePassPixelShaderPolicyParamType<FUniformLightMapPolicy>> PassShaders;
if (!GetBasePassShaders<FUniformLightMapPolicy>(
Material,
VertexFactory->GetType(),
FUniformLightMapPolicy(LMP_NO_LIGHTMAP),
FeatureLevel,
false, // bRenderSkylight
false, // 128bit
false, // bIsDebug
GBL_Default, // Currently only Nanite uses non-default layout
&PassShaders.VertexShader,
&PassShaders.PixelShader
))
{
return false;
}
FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(PassShaders.VertexShader, PassShaders.PixelShader);
TBasePassShaderElementData<FUniformLightMapPolicy> ShaderElementData(MeshBatch.LCI);
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, true);
BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
Material,
PassDrawRenderState,
PassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
EMeshPassFeatures::Default,
ShaderElementData);
}
else
{
TMeshProcessorShaders<
TMobileBasePassVSPolicyParamType<FUniformLightMapPolicy>,
TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>> PassShaders;
FMaterialShaderTypes ShaderTypes;
ShaderTypes.AddShaderType<TMobileBasePassVS<TUniformLightMapPolicy<LMP_NO_LIGHTMAP>>>();
ShaderTypes.AddShaderType<TMobileBasePassPS<TUniformLightMapPolicy<LMP_NO_LIGHTMAP>, LOCAL_LIGHTS_DISABLED>>();
FMaterialShaders Shaders;
if (!Material.TryGetShaders(ShaderTypes, VertexFactory->GetType(), Shaders))
{
return false;
}
Shaders.TryGetVertexShader(PassShaders.VertexShader);
Shaders.TryGetPixelShader(PassShaders.PixelShader);
FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(PassShaders.VertexShader, PassShaders.PixelShader);
TMobileBasePassShaderElementData<FUniformLightMapPolicy> ShaderElementData(MeshBatch.LCI, false);
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
Material,
PassDrawRenderState,
PassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
EMeshPassFeatures::Default,
ShaderElementData);
}
return true;
}
return false;
}
FMeshPassProcessor* CreateWaterInfoTexturePassMeshProcessor(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
return new FWaterInfoTexturePassMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext, false);
}
FMeshPassProcessor* CreateWaterInfoTexturePassMeshProcessorMobile(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
return new FWaterInfoTexturePassMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext, true);
}
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(WaterInfoTexturePass, CreateWaterInfoTexturePassMeshProcessor, EShadingPath::Deferred, EMeshPass::WaterInfoTexturePass, EMeshPassFlags::CachedMeshCommands);
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(MobileWaterInfoTexturePass, CreateWaterInfoTexturePassMeshProcessorMobile, EShadingPath::Mobile, EMeshPass::WaterInfoTexturePass, EMeshPassFlags::CachedMeshCommands);
/**
* MeshPassProcessor for the two depth-only passes involved in generating the water info texture. The two passes can use the same processor. The first (terrain) pass renders terrain
* with a fixed grid vertex factory and possibly other static meshes too. The second pass renders dilated water body meshes. All these meshes should only be rendered into this view/texture,
which is why MeshBatches need to have the bUseForWaterInfoTextureDepth bool set.
*/
class FWaterInfoTextureDepthPassMeshProcessor : public FSceneRenderingAllocatorObject<FWaterInfoTextureDepthPassMeshProcessor>, public FMeshPassProcessor
{
public:
FWaterInfoTextureDepthPassMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext);
virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final;
virtual void CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers) override final;
private:
bool TryAddMeshBatch(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material);
template<bool bPositionOnly>
bool Process(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode);
template<bool bPositionOnly>
void CollectPSOInitializersInternal(
const FSceneTexturesConfig& SceneTexturesConfig,
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode,
const FPSOPrecacheParams& PreCacheParams,
TArray<FPSOPrecacheData>& PSOInitializers);
FMeshPassProcessorRenderState PassDrawRenderState;
};
FWaterInfoTextureDepthPassMeshProcessor::FWaterInfoTextureDepthPassMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
: FMeshPassProcessor(EMeshPass::WaterInfoTextureDepthPass, Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext)
{
PassDrawRenderState.SetBlendState(TStaticBlendState<CW_NONE>::GetRHI());
PassDrawRenderState.SetDepthStencilAccess(FExclusiveDepthStencil::DepthWrite_StencilNop);
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
}
void FWaterInfoTextureDepthPassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
{
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
while (MaterialRenderProxy)
{
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
if (Material)
{
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material))
{
break;
}
}
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
}
}
bool FWaterInfoTextureDepthPassMeshProcessor::TryAddMeshBatch(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material)
{
if (MeshBatch.bUseForWaterInfoTextureDepth && Material.GetMaterialDomain() == MD_Surface && !IsTranslucentBlendMode(Material))
{
// Determine the mesh's material and blend mode.
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
const bool bVFTypeSupportsNullPixelShader = MeshBatch.VertexFactory->SupportsNullPixelShader();
const bool bEvaluateWPO = Material.MaterialModifiesMeshPosition_RenderThread() && (!ShouldOptimizedWPOAffectNonNaniteShaderSelection() || PrimitiveSceneProxy->EvaluateWorldPositionOffset());
if (IsOpaqueBlendMode(Material)
&& MeshBatch.VertexFactory->SupportsPositionOnlyStream()
&& !bEvaluateWPO
&& Material.WritesEveryPixel(false, bVFTypeSupportsNullPixelShader))
{
const FMaterialRenderProxy& DefaultProxy = *UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
const FMaterial& DefaultMaterial = *DefaultProxy.GetMaterialNoFallback(FeatureLevel);
return Process<true>(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, DefaultProxy, DefaultMaterial, MeshFillMode, MeshCullMode);
}
else
{
const bool bMaterialMasked = !Material.WritesEveryPixel(false, bVFTypeSupportsNullPixelShader);
const FMaterialRenderProxy* EffectiveMaterialRenderProxy = &MaterialRenderProxy;
const FMaterial* EffectiveMaterial = &Material;
if (!bMaterialMasked && !bEvaluateWPO)
{
// Override with the default material for opaque materials that are not two sided
EffectiveMaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
EffectiveMaterial = EffectiveMaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
check(EffectiveMaterial);
}
return Process<false>(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, MeshCullMode);
}
}
return true;
}
template<bool bPositionOnly>
bool FWaterInfoTextureDepthPassMeshProcessor::Process(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode)
{
TMeshProcessorShaders<TDepthOnlyVS<bPositionOnly>, FDepthOnlyPS> DepthPassShaders;
FShaderPipelineRef ShaderPipeline;
if (!GetDepthPassShaders<bPositionOnly>(
MaterialResource,
MeshBatch.VertexFactory->GetType(),
FeatureLevel,
MaterialResource.MaterialUsesPixelDepthOffset_GameThread(),
DepthPassShaders.VertexShader,
DepthPassShaders.PixelShader,
ShaderPipeline))
{
return false;
}
FMeshMaterialShaderElementData ShaderElementData;
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, true);
const bool bIsMasked = IsMaskedBlendMode(MaterialResource);
FMeshDrawCommandSortKey SortKey = CalculateDepthPassMeshStaticSortKey(bIsMasked, DepthPassShaders.VertexShader.GetShader(), DepthPassShaders.PixelShader.GetShader());
BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
MaterialResource,
PassDrawRenderState,
DepthPassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
bPositionOnly ? EMeshPassFeatures::PositionOnly : EMeshPassFeatures::Default,
ShaderElementData);
return true;
}
void FWaterInfoTextureDepthPassMeshProcessor::CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers)
{
// We need to support all materials that could possibly be rendered in a depth-only pass. Unfortunately there doesn't seem to be a way to filter for bUseForWaterInfoTextureDepth at this point.
if (Material.GetMaterialDomain() == MD_Surface && !IsTranslucentBlendMode(Material))
{
// Determine the mesh's material and blend mode.
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(PreCacheParams);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
const bool bSupportPositionOnlyStream = VertexFactoryData.VertexFactoryType->SupportsPositionOnly();
const bool bVFTypeSupportsNullPixelShader = VertexFactoryData.VertexFactoryType->SupportsNullPixelShader();
if (IsOpaqueBlendMode(Material)
&& bSupportPositionOnlyStream
&& !Material.MaterialModifiesMeshPosition_GameThread()
&& Material.WritesEveryPixel(false, bVFTypeSupportsNullPixelShader))
{
EMaterialQualityLevel::Type ActiveQualityLevel = GetCachedScalabilityCVars().MaterialQualityLevel;
const FMaterial* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface)->GetMaterialResource(FeatureLevel, ActiveQualityLevel);
check(DefaultMaterial);
CollectPSOInitializersInternal<true>(SceneTexturesConfig, VertexFactoryData, *DefaultMaterial, MeshFillMode, MeshCullMode, PreCacheParams, PSOInitializers);
}
else
{
const bool bMaterialMasked = !Material.WritesEveryPixel(false, bVFTypeSupportsNullPixelShader);
const FMaterial* EffectiveMaterial = &Material;
if (!bMaterialMasked && !Material.MaterialModifiesMeshPosition_GameThread())
{
// Override with the default material for opaque materials that are not two sided
EMaterialQualityLevel::Type ActiveQualityLevel = GetCachedScalabilityCVars().MaterialQualityLevel;
EffectiveMaterial = UMaterial::GetDefaultMaterial(MD_Surface)->GetMaterialResource(FeatureLevel, ActiveQualityLevel);
check(EffectiveMaterial);
}
CollectPSOInitializersInternal<false>(SceneTexturesConfig, VertexFactoryData, *EffectiveMaterial, MeshFillMode, MeshCullMode, PreCacheParams, PSOInitializers);
}
}
}
template<bool bPositionOnly>
void FWaterInfoTextureDepthPassMeshProcessor::CollectPSOInitializersInternal(
const FSceneTexturesConfig& SceneTexturesConfig,
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode,
const FPSOPrecacheParams& PreCacheParams,
TArray<FPSOPrecacheData>& PSOInitializers)
{
TMeshProcessorShaders<TDepthOnlyVS<bPositionOnly>, FDepthOnlyPS> DepthPassShaders;
FShaderPipelineRef ShaderPipeline;
if (!GetDepthPassShaders<bPositionOnly>(
MaterialResource,
VertexFactoryData.VertexFactoryType,
FeatureLevel,
MaterialResource.MaterialUsesPixelDepthOffset_GameThread(),
DepthPassShaders.VertexShader,
DepthPassShaders.PixelShader,
ShaderPipeline))
{
return;
}
FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo;
RenderTargetsInfo.NumSamples = 1;
SetupDepthStencilInfo(PF_DepthStencil, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource, ERenderTargetLoadAction::EClear,
ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthWrite_StencilNop, RenderTargetsInfo);
AddGraphicsPipelineStateInitializer(
VertexFactoryData,
MaterialResource,
PassDrawRenderState,
RenderTargetsInfo,
DepthPassShaders,
MeshFillMode,
MeshCullMode,
(EPrimitiveType)PreCacheParams.PrimitiveType,
bPositionOnly ? EMeshPassFeatures::PositionOnly : EMeshPassFeatures::Default,
true /*bRequired*/,
PSOInitializers);
}
FMeshPassProcessor* CreateWaterInfoTextureDepthPassMeshProcessor(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
return new FWaterInfoTextureDepthPassMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext);
}
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(WaterInfoTextureDepthPass, CreateWaterInfoTextureDepthPassMeshProcessor, EShadingPath::Deferred, EMeshPass::WaterInfoTextureDepthPass, EMeshPassFlags::CachedMeshCommands);
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(MobileWaterInfoTextureDepthPass, CreateWaterInfoTextureDepthPassMeshProcessor, EShadingPath::Mobile, EMeshPass::WaterInfoTextureDepthPass, EMeshPassFlags::CachedMeshCommands);
enum class EWaterInfoTextureMeshPass : int32
{
// Depth-only pass for actors which are considered the terrain for water rendering
TerrainDepth,
// Depth-only pass for water body dilated sections which get composited back into the water info texture at a lower priority than regular water body data
DilatedWaterBodyDepth,
// Depth and color pass for non-dilated water body meshes, rendering out water surface depth, river velocity/flow and possibly other data in the future.
WaterBody,
Num
};
struct FWaterInfoTextureDraws
{
EMeshPass::Type MeshPass = EMeshPass::WaterInfoTextureDepthPass;
const TSet<FPrimitiveComponentId>* ComponentIDsToDraw = nullptr;
uint32 PrimitiveInstanceIndex = 0;
FMeshCommandOneFrameArray VisibleMeshCommands;
TArray<uint32, SceneRenderingAllocator> InstanceRuns;
FInstanceCullingContext* InstanceCullingContext = nullptr;
FInstanceCullingResult InstanceCullingResult;
FWaterInfoTextureDraws() = default;
FWaterInfoTextureDraws(EMeshPass::Type InMeshPass, const TSet<FPrimitiveComponentId>* InComponentIDsToDraw, uint32 InPrimitiveInstanceIndex)
: MeshPass(InMeshPass), ComponentIDsToDraw(InComponentIDsToDraw), PrimitiveInstanceIndex(InPrimitiveInstanceIndex)
{}
};
static void AddWaterInfoTextureDraws(const FScene* Scene, const TArrayView<FWaterInfoTextureDraws>& PassDrawLists)
{
const int32 NumDrawLists = PassDrawLists.Num();
TArray<uint32, TInlineAllocator<3>> MaxVisibleMeshDrawCommands;
MaxVisibleMeshDrawCommands.SetNum(NumDrawLists);
// Compute upper bounds for mesh draw commands
for (const FPrimitiveSceneInfo* PrimitiveSceneInfo : Scene->Primitives)
{
if (PrimitiveSceneInfo && !PrimitiveSceneInfo->Proxy->IsNaniteMesh())
{
for (int32 DrawListIdx = 0; DrawListIdx < NumDrawLists; ++DrawListIdx)
{
if (PassDrawLists[DrawListIdx].ComponentIDsToDraw->Contains(PrimitiveSceneInfo->PrimitiveComponentId))
{
MaxVisibleMeshDrawCommands[DrawListIdx] += PrimitiveSceneInfo->StaticMeshRelevances.Num();
}
}
}
}
for (int32 DrawListIdx = 0; DrawListIdx < NumDrawLists; ++DrawListIdx)
{
PassDrawLists[DrawListIdx].InstanceRuns.Reserve(2 * MaxVisibleMeshDrawCommands[DrawListIdx]);
}
// Collect mesh draw commands
for (const FPrimitiveSceneInfo* PrimitiveSceneInfo : Scene->Primitives)
{
if (PrimitiveSceneInfo && !PrimitiveSceneInfo->Proxy->IsNaniteMesh())
{
for (int32 DrawListIdx = 0; DrawListIdx < NumDrawLists; ++DrawListIdx)
{
const EWaterInfoTextureMeshPass PassType = static_cast<EWaterInfoTextureMeshPass>(DrawListIdx);
FWaterInfoTextureDraws& Draws = PassDrawLists[DrawListIdx];
if (Draws.ComponentIDsToDraw->Contains(PrimitiveSceneInfo->PrimitiveComponentId))
{
FMeshDrawCommandPrimitiveIdInfo IdInfo(PrimitiveSceneInfo->GetMDCIdInfo());
for (int32 MeshIndex = 0; MeshIndex < PrimitiveSceneInfo->StaticMeshRelevances.Num(); MeshIndex++)
{
const FStaticMeshBatchRelevance& StaticMeshRelevance = PrimitiveSceneInfo->StaticMeshRelevances[MeshIndex];
const FStaticMeshBatch& StaticMesh = PrimitiveSceneInfo->StaticMeshes[MeshIndex];
const bool bUseInDepthPass = (PassType == EWaterInfoTextureMeshPass::TerrainDepth || PassType == EWaterInfoTextureMeshPass::DilatedWaterBodyDepth) && StaticMeshRelevance.bUseForWaterInfoTextureDepth;
const bool bUseInColorPass = (PassType == EWaterInfoTextureMeshPass::WaterBody) && StaticMeshRelevance.bUseForMaterial;
if ((bUseInDepthPass || bUseInColorPass) && StaticMeshRelevance.GetLODIndex() == 0)
{
const int32 StaticMeshCommandInfoIndex = StaticMeshRelevance.GetStaticMeshCommandInfoIndex(Draws.MeshPass);
if (StaticMeshCommandInfoIndex >= 0)
{
const FCachedMeshDrawCommandInfo& CachedMeshDrawCommand = PrimitiveSceneInfo->StaticMeshCommandInfos[StaticMeshCommandInfoIndex];
const FCachedPassMeshDrawList& SceneDrawList = Scene->CachedDrawLists[Draws.MeshPass];
const FMeshDrawCommand* MeshDrawCommand = nullptr;
if (CachedMeshDrawCommand.StateBucketId >= 0)
{
MeshDrawCommand = &Scene->CachedMeshDrawCommandStateBuckets[Draws.MeshPass].GetByElementId(CachedMeshDrawCommand.StateBucketId).Key;
}
else
{
MeshDrawCommand = &SceneDrawList.MeshDrawCommands[CachedMeshDrawCommand.CommandIndex];
}
const uint32* InstanceRunArray = nullptr;
uint32 NumInstanceRuns = 0;
if (MeshDrawCommand->NumInstances > 1 && Draws.PrimitiveInstanceIndex >= 0)
{
// Render only a single specified instance, by specifying an inclusive [x;x] range
ensure(Draws.InstanceRuns.Num() + 2 <= Draws.InstanceRuns.Max());
InstanceRunArray = Draws.InstanceRuns.GetData() + Draws.InstanceRuns.Num();
NumInstanceRuns = 1;
Draws.InstanceRuns.Add(Draws.PrimitiveInstanceIndex);
Draws.InstanceRuns.Add(Draws.PrimitiveInstanceIndex);
}
FVisibleMeshDrawCommand NewVisibleMeshDrawCommand;
NewVisibleMeshDrawCommand.Setup(
MeshDrawCommand,
IdInfo,
CachedMeshDrawCommand.StateBucketId,
CachedMeshDrawCommand.MeshFillMode,
CachedMeshDrawCommand.MeshCullMode,
CachedMeshDrawCommand.Flags,
CachedMeshDrawCommand.SortKey,
CachedMeshDrawCommand.CullingPayload,
EMeshDrawCommandCullingPayloadFlags::NoScreenSizeCull,
InstanceRunArray,
NumInstanceRuns);
Draws.VisibleMeshCommands.Add(NewVisibleMeshDrawCommand);
}
}
}
}
}
}
}
}
void RenderWaterInfoTexture(
FRDGBuilder& GraphBuilder,
FSceneRenderer& SceneRenderer,
const FScene* Scene
)
{
bool bAnyWaterInfoTextureUpdates = false;
for (const FViewInfo& View : SceneRenderer.Views)
{
bAnyWaterInfoTextureUpdates = bAnyWaterInfoTextureUpdates || !View.WaterInfoTextureRenderingParams.IsEmpty();
}
if (!bAnyWaterInfoTextureUpdates)
{
return;
}
TRACE_CPUPROFILER_EVENT_SCOPE(WaterInfo::RenderWaterInfoTexture);
RDG_EVENT_SCOPE_STAT(GraphBuilder, WaterInfoTexture, "WaterInfoTexture");
RDG_GPU_STAT_SCOPE(GraphBuilder, WaterInfoTexture);
int32 RenderCaptureNextWaterInfoDraws = CVarRenderCaptureNextWaterInfoDraws.GetValueOnRenderThread();
RenderCaptureInterface::FScopedCapture RenderCapture((RenderCaptureNextWaterInfoDraws != 0), GraphBuilder, TEXT("RenderWaterInfo"));
if (RenderCaptureNextWaterInfoDraws != 0)
{
RenderCaptureNextWaterInfoDraws = FMath::Max(0, RenderCaptureNextWaterInfoDraws - 1);
CVarRenderCaptureNextWaterInfoDraws->SetWithCurrentPriority(RenderCaptureNextWaterInfoDraws);
}
// Keep track of all the result textures so we can batch call UseExternalAccessMode() on them.
TArray<FRDGViewableResource*, TInlineAllocator<4>> OutputTextures;
// Check all views for WaterInfoTexture updates that we might need to execute.
for (const FViewInfo& View : SceneRenderer.Views)
{
for (const FViewInfo::FWaterInfoTextureRenderingParams& RenderingParams : View.WaterInfoTextureRenderingParams)
{
const FIntPoint TextureSize = RenderingParams.RenderTarget->GetSizeXY();
const FIntRect Viewport(0, 0, TextureSize.X, TextureSize.Y);
FViewInfo& WaterView = *View.CreateSnapshot();
{
WaterView.ViewState = nullptr;
WaterView.DynamicPrimitiveCollector = FGPUScenePrimitiveCollector(&SceneRenderer.GetGPUSceneDynamicContext());
WaterView.StereoPass = EStereoscopicPass::eSSP_FULL;
WaterView.DrawDynamicFlags = EDrawDynamicFlags::ForceLowestLOD;
WaterView.MaterialTextureMipBias = 0;
WaterView.PreExposure = 1.0f;
WaterView.ViewRect = Viewport;
WaterView.CachedViewUniformShaderParameters = MakeUnique<FViewUniformShaderParameters>();
FBox VolumeBounds[TVC_MAX];
WaterView.SetupUniformBufferParameters(VolumeBounds, TVC_MAX, *WaterView.CachedViewUniformShaderParameters);
WaterView.UpdateProjectionMatrix(RenderingParams.ProjectionMatrix);
FViewMatrices::FMinimalInitializer Initializer;
Initializer.ViewRotationMatrix = RenderingParams.ViewRotationMatrix;
Initializer.ViewOrigin = RenderingParams.ViewLocation;
Initializer.ProjectionMatrix = RenderingParams.ProjectionMatrix;
Initializer.ConstrainedViewRect = Viewport;
WaterView.ViewMatrices = FViewMatrices(Initializer);
WaterView.SetupCommonViewUniformBufferParameters(
*WaterView.CachedViewUniformShaderParameters,
TextureSize,
1,
Initializer.ConstrainedViewRect,
WaterView.ViewMatrices,
WaterView.ViewMatrices);
WaterView.CreateViewUniformBuffers(*WaterView.CachedViewUniformShaderParameters);
}
const FTextureRHIRef OutputRenderTarget = RenderingParams.RenderTarget->GetRenderTargetTexture();
FRDGTexture* OutputTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(OutputRenderTarget, TEXT("WaterInfo.OutputTexture")));
GraphBuilder.UseInternalAccessMode(OutputTexture); // Make sure the texture is actually managed by RDG as a call to RegisterExternalTexture does not guarantee this if the texture was already registered and then had UseExternalAccessMode() called on it.
FRDGTextureDesc TerrainDepthBufferDesc = FRDGTextureDesc::Create2D(TextureSize, PF_DepthStencil, FClearValueBinding::DepthFar, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource);
FRDGTexture* TerrainDepthBuffer = GraphBuilder.CreateTexture(TerrainDepthBufferDesc, TEXT("WaterInfo.TerrainDepthTexture"));
FRDGTextureDesc WaterInfoTextureDesc = FRDGTextureDesc::Create2D(TextureSize, PF_FloatRGBA, FClearValueBinding::Black, TexCreate_RenderTargetable | TexCreate_ShaderResource);
FRDGTexture* WaterInfoColorTexture = GraphBuilder.CreateTexture(WaterInfoTextureDesc, TEXT("WaterInfo.ColorTexture"));
FRDGTextureDesc WaterInfoDepthBufferDesc = FRDGTextureDesc::Create2D(TextureSize, PF_DepthStencil, FClearValueBinding::DepthFar, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource);
FRDGTexture* WaterInfoDepthBuffer = GraphBuilder.CreateTexture(WaterInfoDepthBufferDesc, TEXT("WaterInfo.DepthTexture"));
FRDGTextureDesc DilatedDepthBufferDesc = FRDGTextureDesc::Create2D(TextureSize, PF_DepthStencil, FClearValueBinding::DepthFar, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource);
FRDGTexture* DilatedDepthBuffer = GraphBuilder.CreateTexture(DilatedDepthBufferDesc, TEXT("WaterInfo.DilatedDepthTexture"));
FRDGTextureDesc MergedTextureDesc = FRDGTextureDesc::Create2D(TextureSize, OutputTexture->Desc.Format, FClearValueBinding::Black, TexCreate_RenderTargetable | TexCreate_ShaderResource);
FRDGTexture* MergedTexture = GraphBuilder.CreateTexture(MergedTextureDesc, TEXT("WaterInfo.MergedTexture"));
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(WaterView.GetFeatureLevel());
TStaticArray<FWaterInfoTextureDraws, static_cast<int32>(EWaterInfoTextureMeshPass::Num)> PassDraws;
PassDraws[static_cast<int32>(EWaterInfoTextureMeshPass::TerrainDepth)] = FWaterInfoTextureDraws(EMeshPass::WaterInfoTextureDepthPass, &RenderingParams.TerrainComponentIds, 0);
PassDraws[static_cast<int32>(EWaterInfoTextureMeshPass::DilatedWaterBodyDepth)] = FWaterInfoTextureDraws(EMeshPass::WaterInfoTextureDepthPass, &RenderingParams.DilatedWaterBodyComponentIds, 0);
PassDraws[static_cast<int32>(EWaterInfoTextureMeshPass::WaterBody)] = FWaterInfoTextureDraws(EMeshPass::WaterInfoTexturePass, &RenderingParams.WaterBodyComponentIds, 0);
AddWaterInfoTextureDraws(Scene, PassDraws);
// Build rendering commands or prepare primitive ID buffer for the mesh draw commands
for (int32 PassIdx = 0; PassIdx < 3; ++PassIdx)
{
if (Scene->GPUScene.IsEnabled())
{
int32 MaxInstances = 0;
int32 VisibleMeshDrawCommandsNum = 0;
int32 NewPassVisibleMeshDrawCommandsNum = 0;
PassDraws[PassIdx].InstanceCullingContext = GraphBuilder.AllocObject<FInstanceCullingContext>(TEXT("WaterInfoTexture"), WaterView.GetShaderPlatform(), nullptr, TArrayView<const int32>(&WaterView.SceneRendererPrimaryViewId, 1), nullptr);
PassDraws[PassIdx].InstanceCullingContext->SetupDrawCommands(PassDraws[PassIdx].VisibleMeshCommands, false, Scene, MaxInstances, VisibleMeshDrawCommandsNum, NewPassVisibleMeshDrawCommandsNum);
// Not supposed to do any compaction here.
ensure(VisibleMeshDrawCommandsNum == PassDraws[PassIdx].VisibleMeshCommands.Num());
PassDraws[PassIdx].InstanceCullingContext->BuildRenderingCommands(GraphBuilder, Scene->GPUScene, WaterView.DynamicPrimitiveCollector.GetInstanceSceneDataOffset(), WaterView.DynamicPrimitiveCollector.NumInstances(), PassDraws[PassIdx].InstanceCullingResult);
}
PassDraws[PassIdx].InstanceCullingResult.Parameters.Scene = SceneRenderer.GetSceneUniforms().GetBuffer(GraphBuilder);
}
// Execute the three mesh passes involved in generating the water info texture: terrain depth-only, dilated water body depth-only and finally regular water body depth + river velocity
for (uint32 PassIndex = 0; PassIndex < static_cast<int32>(EWaterInfoTextureMeshPass::Num); ++PassIndex)
{
const EWaterInfoTextureMeshPass PassType = static_cast<EWaterInfoTextureMeshPass>(PassIndex);
// Terrain depth and dilated water body depth share the same setup, they just render a different set of meshes into different depth buffers
if (PassType == EWaterInfoTextureMeshPass::TerrainDepth || PassType == EWaterInfoTextureMeshPass::DilatedWaterBodyDepth)
{
const bool bIsTerrainDepthPass = PassType == EWaterInfoTextureMeshPass::TerrainDepth;
FWaterInfoTextureDepthPassParameters* PassParameters = GraphBuilder.AllocParameters<FWaterInfoTextureDepthPassParameters>();
PassParameters->View = WaterView.GetShaderParameters();
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(bIsTerrainDepthPass ? TerrainDepthBuffer : DilatedDepthBuffer, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthWrite_StencilNop);
PassDraws[PassIndex].InstanceCullingResult.GetDrawParameters(PassParameters->InstanceCullingDrawParams);
GraphBuilder.AddPass(
bIsTerrainDepthPass ? RDG_EVENT_NAME("WaterInfoTexture(Terrain)") : RDG_EVENT_NAME("WaterInfoTexture(DilatedWaterBodies)"),
PassParameters,
ERDGPassFlags::Raster,
[MeshDrawCmds = MoveTemp(PassDraws[PassIndex].VisibleMeshCommands), Scene = Scene, Viewport,
PassParameters, InstanceCullingContext = PassDraws[PassIndex].InstanceCullingContext](FRHICommandList& RHICmdList)
{
QUICK_SCOPE_CYCLE_COUNTER(MeshPass);
RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f);
FGraphicsMinimalPipelineStateSet PipelineStateSet;
if (Scene->GPUScene.IsEnabled())
{
InstanceCullingContext->SubmitDrawCommands(MeshDrawCmds, PipelineStateSet, GetMeshDrawCommandOverrideArgs(PassParameters->InstanceCullingDrawParams), 0, MeshDrawCmds.Num(), 1, RHICmdList);
}
else
{
FMeshDrawCommandSceneArgs SceneArgs;
SubmitMeshDrawCommandsRange(MeshDrawCmds, PipelineStateSet, SceneArgs, FInstanceCullingContext::GetInstanceIdBufferStride(Scene->GetShaderPlatform()), false, 0, MeshDrawCmds.Num(), 1, RHICmdList);
}
});
}
else if (PassType == EWaterInfoTextureMeshPass::WaterBody)
{
FWaterInfoTexturePassParameters* PassParameters = GraphBuilder.AllocParameters<FWaterInfoTexturePassParameters>();
PassParameters->View = WaterView.GetShaderParameters();
PassParameters->RenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::EClear, MakeArrayView(&WaterInfoColorTexture, 1));
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(WaterInfoDepthBuffer, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthWrite_StencilNop);
PassDraws[PassIndex].InstanceCullingResult.GetDrawParameters(PassParameters->InstanceCullingDrawParams);
GraphBuilder.AddPass(
RDG_EVENT_NAME("WaterInfoTexture(WaterBodies)"),
PassParameters,
ERDGPassFlags::Raster,
[MeshDrawCmds = MoveTemp(PassDraws[PassIndex].VisibleMeshCommands), Scene = Scene, Viewport,
PassParameters, InstanceCullingContext = PassDraws[PassIndex].InstanceCullingContext](FRHICommandList& RHICmdList)
{
QUICK_SCOPE_CYCLE_COUNTER(MeshPass);
RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f);
FGraphicsMinimalPipelineStateSet PipelineStateSet;
if (Scene->GPUScene.IsEnabled())
{
InstanceCullingContext->SubmitDrawCommands(MeshDrawCmds, PipelineStateSet, GetMeshDrawCommandOverrideArgs(PassParameters->InstanceCullingDrawParams), 0, MeshDrawCmds.Num(), 1, RHICmdList);
}
else
{
FMeshDrawCommandSceneArgs SceneArgs;
SubmitMeshDrawCommandsRange(MeshDrawCmds, PipelineStateSet, SceneArgs, FInstanceCullingContext::GetInstanceIdBufferStride(Scene->GetShaderPlatform()), false, 0, MeshDrawCmds.Num(), 1, RHICmdList);
}
});
}
}
// Merge terrain depth, dilated water body depth, water body depth and velocity into a single texture
{
FWaterInfoTextureMergePS::FPermutationDomain PixelPermutationVector;
// The output format depends on the user configurable format of the passed in render target. Since we store depth data in the texture,
// it is sometimes desirable to have full 32bit float precision.
PixelPermutationVector.Set<FWaterInfoTextureMergePS::FEnable128BitRT>(OutputTexture->Desc.Format == PF_A32B32G32R32F);
TShaderMapRef<FWaterInfoTextureMergePS> PixelShader(ShaderMap, PixelPermutationVector);
FWaterInfoTextureMergePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FWaterInfoTextureMergePS::FParameters>();
PassParameters->View = WaterView.GetShaderParameters();
PassParameters->RenderTargets[0] = FRenderTargetBinding(MergedTexture, ERenderTargetLoadAction::ENoAction);
PassParameters->SceneTextures = GetSceneTextureShaderParameters(WaterView);
PassParameters->GroundDepthTexture = TerrainDepthBuffer;
PassParameters->GroundDepthTextureSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
PassParameters->WaterBodyTexture = WaterInfoColorTexture;
PassParameters->WaterBodyTextureSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
PassParameters->WaterBodyDepthTexture = WaterInfoDepthBuffer;
PassParameters->WaterBodyDepthTextureSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
PassParameters->DilatedWaterBodyDepthTexture = DilatedDepthBuffer;
PassParameters->DilatedWaterBodyDepthTextureSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
PassParameters->CaptureZ = RenderingParams.CaptureZ;
PassParameters->WaterHeightExtents = RenderingParams.WaterHeightExtents;
PassParameters->GroundZMin = RenderingParams.GroundZMin;
PassParameters->DilationOverwriteMinimumDistance = CVarWaterInfoDilationOverwriteMinimumDistance.GetValueOnRenderThread();
PassParameters->UndergroundDilationDepthOffset = CVarWaterInfoUndergroundDilationDepthOffset.GetValueOnRenderThread();
FPixelShaderUtils::AddFullscreenPass(
GraphBuilder,
ShaderMap,
RDG_EVENT_NAME("WaterInfoTextureMerge"),
PixelShader,
PassParameters,
Viewport);
}
// Blur the velocity component in the water info texture to get a smooth gradient between water body transitions
{
FWaterInfoTextureBlurPS::FPermutationDomain PixelPermutationVector;
// The output format depends on the user configurable format of the passed in render target. Since we store depth data in the texture,
// it is sometimes desirable to have full 32bit float precision.
PixelPermutationVector.Set<FWaterInfoTextureBlurPS::FEnable128BitRT>(OutputTexture->Desc.Format == PF_A32B32G32R32F);
TShaderMapRef<FWaterInfoTextureBlurPS> PixelShader(ShaderMap, PixelPermutationVector);
FWaterInfoTextureBlurPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FWaterInfoTextureBlurPS::FParameters>();
PassParameters->View = WaterView.GetShaderParameters();
PassParameters->RenderTargets[0] = FRenderTargetBinding(OutputTexture, ERenderTargetLoadAction::ENoAction, 0, RenderingParams.RenderTargetArrayLayer);
PassParameters->SceneTextures = GetSceneTextureShaderParameters(WaterView);
PassParameters->WaterInfoTexture = MergedTexture;
PassParameters->WaterZMin = RenderingParams.WaterHeightExtents.X;
PassParameters->WaterZMax = RenderingParams.WaterHeightExtents.Y;
PassParameters->GroundZMin = RenderingParams.GroundZMin;
PassParameters->CaptureZ = RenderingParams.CaptureZ;
PassParameters->BlurRadius = RenderingParams.VelocityBlurRadius;
FPixelShaderUtils::AddFullscreenPass(
GraphBuilder,
ShaderMap,
RDG_EVENT_NAME("WaterInfoTextureBlur"),
PixelShader,
PassParameters,
Viewport);
}
// Add the output texture to an array to batch call UseExternalAccessMode() on all of them.
OutputTextures.AddUnique(OutputTexture);
}
}
// Make sure the textures are in the SRV state by the time they are used in water draws (referenced outside of RDG).
GraphBuilder.UseExternalAccessMode(OutputTextures, ERHIAccess::SRVMask);
// Reset the WIT updates on all the views to ensure we don't accidentally keep executing the same updates.
for (FViewInfo& View : SceneRenderer.Views)
{
View.WaterInfoTextureRenderingParams.Reset();
}
}