Files
UnrealEngine/Engine/Plugins/Experimental/Water/Source/Runtime/Public/WaterVertexFactory.inl
2025-05-18 13:04:45 +08:00

255 lines
9.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ShaderParameterUtils.h"
#include "RHIStaticStates.h"
#include "EngineGlobals.h"
#include "Engine/Engine.h"
#include "MaterialDomain.h"
#include "Materials/Material.h"
#include "MeshMaterialShader.h"
#include "WaterInstanceDataBuffer.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "StereoRenderUtils.h"
// ----------------------------------------------------------------------------------
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::TWaterVertexFactory(ERHIFeatureLevel::Type InFeatureLevel, int32 InNumQuadsPerSide, int32 InNumQuadsLOD0, int32 InNumDensities, float InLeafSize, float InLODScale)
: FVertexFactory(InFeatureLevel)
, NumQuadsPerSide(InNumQuadsPerSide)
, NumQuadsLOD0(InNumQuadsLOD0)
, NumDensities(InNumDensities)
, LeafSize(InLeafSize)
, LODScale(InLODScale)
{
VertexBuffer = new FWaterMeshVertexBuffer(NumQuadsPerSide);
IndexBuffer = new FWaterMeshIndexBuffer(NumQuadsPerSide);
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::~TWaterVertexFactory()
{
delete VertexBuffer;
delete IndexBuffer;
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
void TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::InitRHI(FRHICommandListBase& RHICmdList)
{
Super::InitRHI(RHICmdList);
// Setup the uniform data:
SetupUniformDataForGroup(EWaterMeshRenderGroupType::RG_RenderWaterTiles);
#if WITH_WATER_SELECTION_SUPPORT
SetupUniformDataForGroup(EWaterMeshRenderGroupType::RG_RenderSelectedWaterTilesOnly);
SetupUniformDataForGroup(EWaterMeshRenderGroupType::RG_RenderUnselectedWaterTilesOnly);
#endif // WITH_WATER_SELECTION_SUPPORT
VertexBuffer->InitResource(RHICmdList);
IndexBuffer->InitResource(RHICmdList);
// No streams should currently exist.
check(Streams.Num() == 0);
// Position stream for per vertex local position
FVertexStream PositionVertexStream;
PositionVertexStream.VertexBuffer = VertexBuffer;
PositionVertexStream.Stride = sizeof(FVector4f);
PositionVertexStream.Offset = 0;
PositionVertexStream.VertexStreamUsage = EVertexStreamUsage::Default;
FVertexElement VertexPositionElement(Streams.Add(PositionVertexStream), 0, VET_Float4, 0, PositionVertexStream.Stride, false);
// Vertex declaration
FVertexDeclarationElementList Elements;
Elements.Add(VertexPositionElement);
// Adds all streams
if constexpr (UsesIndirectDraws())
{
// Instanced stereo manually fetches instance data from buffers instead
if (!UsesInstancedStereo())
{
FVertexStream InstanceDataVertexStream;
InstanceDataVertexStream.VertexBuffer = nullptr;
InstanceDataVertexStream.Stride = sizeof(uint32);
InstanceDataVertexStream.Offset = 0;
InstanceDataVertexStream.VertexStreamUsage = EVertexStreamUsage::Instancing;
constexpr int NumBuffers = bWithWaterSelectionSupport ? 4 : 3;
for (int i = 0; i < NumBuffers; ++i)
{
Elements.Add(FVertexElement(Streams.Add(InstanceDataVertexStream), 0, VET_UInt, 8 + i, InstanceDataVertexStream.Stride, true));
}
}
}
else if constexpr (NumAdditionalVertexStreams > 0)
{
// Simple instancing vertex stream with nullptr vertex buffer to be set at binding time
FVertexStream InstanceDataVertexStream;
InstanceDataVertexStream.VertexBuffer = nullptr;
InstanceDataVertexStream.Stride = sizeof(FVector4f);
InstanceDataVertexStream.Offset = 0;
InstanceDataVertexStream.VertexStreamUsage = EVertexStreamUsage::Instancing;
for (int32 StreamIdx = 0; StreamIdx < NumAdditionalVertexStreams; ++StreamIdx)
{
FVertexElement InstanceElement(Streams.Add(InstanceDataVertexStream), 0, VET_Float4, 8 + StreamIdx, InstanceDataVertexStream.Stride, true);
Elements.Add(InstanceElement);
}
}
#if 0
const bool bCanUseGPUScene = UseGPUScene(GMaxRHIShaderPlatform, GMaxRHIFeatureLevel);
// Add support for primitiveID
const uint8 Index = static_cast<uint8>(EVertexInputStreamType::Default);
PrimitiveIdStreamIndex[Index] = -1;
if (GetType()->SupportsPrimitiveIdStream() && bCanUseGPUScene)
{
// When the VF is used for rendering in normal mesh passes, this vertex buffer and offset will be overridden
Elements.Add(AccessStreamComponent(FVertexStreamComponent(&GPrimitiveIdDummy, 0, 0, sizeof(uint32), VET_UInt, EVertexStreamUsage::Instancing), 13));
PrimitiveIdStreamIndex[Index] = Elements.Last().StreamIndex;
}
#endif
InitDeclaration(Elements);
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
void TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::ReleaseRHI()
{
for (auto& UniformBuffer : UniformBuffers)
{
UniformBuffer.SafeRelease();
}
if (VertexBuffer)
{
VertexBuffer->ReleaseResource();
}
if (IndexBuffer)
{
IndexBuffer->ReleaseResource();
}
Super::ReleaseRHI();
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
void TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::SetupUniformDataForGroup(EWaterMeshRenderGroupType InRenderGroupType)
{
FWaterVertexFactoryParameters UniformParams;
UniformParams.NumQuadsPerTileSide = NumQuadsPerSide;
UniformParams.NumQuadsLOD0 = NumQuadsLOD0;
UniformParams.NumDensities = NumDensities;
UniformParams.LODScale = LODScale;
UniformParams.LeafSize = LeafSize;
UniformParams.bRenderSelected = true;
UniformParams.bRenderUnselected = true;
#if WITH_WATER_SELECTION_SUPPORT
UniformParams.bRenderSelected = (InRenderGroupType != EWaterMeshRenderGroupType::RG_RenderUnselectedWaterTilesOnly);
UniformParams.bRenderUnselected = (InRenderGroupType != EWaterMeshRenderGroupType::RG_RenderSelectedWaterTilesOnly);
#endif // WITH_WATER_SELECTION_SUPPORT
UniformBuffers[(int32)InRenderGroupType] = FWaterVertexFactoryBufferRef::CreateUniformBufferImmediate(UniformParams, UniformBuffer_MultiFrame);
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
bool TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters)
{
const bool bIsCompatibleWithWater = ((Parameters.MaterialParameters.MaterialDomain == MD_Surface) && Parameters.MaterialParameters.bIsUsedWithWater) || Parameters.MaterialParameters.bIsSpecialEngineMaterial;
if (bIsCompatibleWithWater)
{
// Only compile the ISR/non-ISR indirect draw version if ISR is enabled/disabled
if (UsesIndirectDraws())
{
const UE::StereoRenderUtils::FStereoShaderAspects Aspects(Parameters.Platform);
const bool bMatchingISRConfig = UsesInstancedStereo() == Aspects.IsInstancedStereoEnabled();
if (!bMatchingISRConfig)
{
return false;
}
}
// Only let the PC platform compile the permutations supporting selection :
return (!bWithWaterSelectionSupport || IsPCPlatform(Parameters.Platform));
}
return false;
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
void TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("WATER_MESH_FACTORY"), 1);
#if 0
const bool bUseGPUSceneAndPrimitiveIdStream =
Type->SupportsPrimitiveIdStream()
&& UseGPUScene(Parameters.Platform, GetMaxSupportedFeatureLevel(Parameters.Platform))
// TODO: support GPUScene on mobile
&& !IsMobilePlatform(Parameters.Platform);
OutEnvironment.SetDefine(TEXT("VF_SUPPORTS_PRIMITIVE_SCENE_DATA"), bUseGPUSceneAndPrimitiveIdStream);
#endif
if (bWithWaterSelectionSupport)
{
OutEnvironment.SetDefine(TEXT("USE_VERTEXFACTORY_HITPROXY_ID"), TEXT("1"));
OutEnvironment.SetDefine(TEXT("WITH_WATER_SELECTION_SUPPORT_VF"), TEXT("1"));
}
if (UsesIndirectDraws())
{
OutEnvironment.SetDefine(TEXT("WATER_MESH_DRAW_INDIRECT"), TEXT("1"));
OutEnvironment.CompilerFlags.Add(CFLAG_IndirectDraw);
}
OutEnvironment.SetDefine(TEXT("RAY_TRACING_DYNAMIC_MESH_IN_LOCAL_SPACE"), TEXT("1"));
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
void TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::ValidateCompiledResult(const FVertexFactoryType* Type, EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutErrors)
{
#if 0
if (Type->SupportsPrimitiveIdStream()
&& UseGPUScene(Platform, GetMaxSupportedFeatureLevel(Platform))
&& ParameterMap.ContainsParameterAllocation(FPrimitiveUniformShaderParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName()))
{
OutErrors.AddUnique(*FString::Printf(TEXT("Shader attempted to bind the Primitive uniform buffer even though Vertex Factory %s computes a PrimitiveId per-instance. This will break auto-instancing. Shaders should use GetPrimitiveData(Parameters).Member instead of Primitive.Member."), Type->GetName()));
}
#endif
}
template <bool bWithWaterSelectionSupport, EWaterVertexFactoryDrawMode DrawMode>
void TWaterVertexFactory<bWithWaterSelectionSupport, DrawMode>::GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements)
{
// Add position stream
Elements.Add(FVertexElement(0, 0, VET_Float4, 0, sizeof(FVector4f), false));
// Add all the additional streams
if constexpr (UsesIndirectDraws())
{
// Instanced stereo manually fetches instance data from buffers instead
if (!UsesInstancedStereo())
{
Elements.Add(FVertexElement(1, 0, VET_UInt, 8, sizeof(uint32), true));
Elements.Add(FVertexElement(2, 0, VET_UInt, 9, sizeof(uint32), true));
Elements.Add(FVertexElement(3, 0, VET_UInt, 10, sizeof(uint32), true));
if (bWithWaterSelectionSupport)
{
Elements.Add(FVertexElement(4, 0, VET_UInt, 11, sizeof(uint32), true));
}
}
}
else if constexpr (NumAdditionalVertexStreams > 0)
{
for (int32 StreamIdx = 0; StreamIdx < NumAdditionalVertexStreams; ++StreamIdx)
{
Elements.Add(FVertexElement(1 + StreamIdx, 0, VET_Float4, 8 + StreamIdx, sizeof(FVector4f), true));
}
}
}