Files
UnrealEngineUWP/Engine/Source/Runtime/Landscape/Private/LandscapeRenderMobile.cpp
dmitriy dyomin d5741f624b Added option to disable usage of a baked landscape mesh on mobile platforms (r.Mobile.LandscapeMesh)
#rb jack.porter

#ROBOMERGE-OWNER: dmitriy.dyomin
#ROBOMERGE-AUTHOR: dmitriy.dyomin
#ROBOMERGE-SOURCE: CL 19528072 via CL 19528235 via CL 19528695 via CL 19528703
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v937-19513599)

[CL 19532768 by dmitriy dyomin in ue5-main branch]
2022-03-28 16:28:57 -04:00

520 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LandscapeRenderMobile.cpp: Landscape Rendering without using vertex texture fetch
=============================================================================*/
#include "LandscapeRenderMobile.h"
#include "ShaderParameterUtils.h"
#include "Serialization/BufferArchive.h"
#include "Serialization/MemoryReader.h"
#include "PrimitiveSceneInfo.h"
#include "LandscapeLayerInfoObject.h"
#include "HAL/LowLevelMemTracker.h"
#include "MeshMaterialShader.h"
// Debug CVar for disabling the loading of landscape hole meshes
static TAutoConsoleVariable<int32> CVarMobileLandscapeHoleMesh(
TEXT("r.Mobile.LandscapeHoleMesh"),
1,
TEXT("Set to 0 to skip loading of landscape hole meshes on mobile."),
ECVF_Default);
static TAutoConsoleVariable<int32> CVarUseMobileLandscapeMesh(
TEXT("r.Mobile.LandscapeMesh"),
1,
TEXT("Whether to use baked landscape mesh for a mobile"),
ECVF_ReadOnly);
bool FLandscapeVertexFactoryMobile::ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters)
{
return UseMobileLandscapeMesh(Parameters.Platform) &&
(Parameters.MaterialParameters.bIsUsedWithLandscape || Parameters.MaterialParameters.bIsSpecialEngineMaterial);
}
void FLandscapeVertexFactoryMobile::InitRHI()
{
// list of declaration items
FVertexDeclarationElementList Elements;
// position decls
Elements.Add(AccessStreamComponent(MobileData.PositionComponent,0));
if (MobileData.LODHeightsComponent.Num())
{
const int32 BaseAttribute = 1;
for(int32 Index = 0;Index < MobileData.LODHeightsComponent.Num();Index++)
{
Elements.Add(AccessStreamComponent(MobileData.LODHeightsComponent[Index], BaseAttribute + Index));
}
}
// create the actual device decls
InitDeclaration(Elements);
}
/** Shader parameters for use with FLandscapeVertexFactory */
class FLandscapeVertexFactoryMobileVertexShaderParameters : public FVertexFactoryShaderParameters
{
DECLARE_TYPE_LAYOUT(FLandscapeVertexFactoryMobileVertexShaderParameters, NonVirtual);
public:
/**
* Bind shader constants by name
* @param ParameterMap - mapping of named shader constants to indices
*/
void Bind(const FShaderParameterMap& ParameterMap)
{
TexCoordOffsetParameter.Bind(ParameterMap,TEXT("TexCoordOffset"));
}
void GetElementShaderBindings(
const class FSceneInterface* Scene,
const FSceneView* InView,
const class FMeshMaterialShader* Shader,
const EVertexInputStreamType InputStreamType,
ERHIFeatureLevel::Type FeatureLevel,
const FVertexFactory* VertexFactory,
const FMeshBatchElement& BatchElement,
class FMeshDrawSingleShaderBindings& ShaderBindings,
FVertexInputStreamArray& VertexStreams
) const
{
SCOPE_CYCLE_COUNTER(STAT_LandscapeVFDrawTimeVS);
const FLandscapeBatchElementParams* BatchElementParams = (const FLandscapeBatchElementParams*)BatchElement.UserData;
check(BatchElementParams);
const FLandscapeComponentSceneProxyMobile* SceneProxy = (const FLandscapeComponentSceneProxyMobile*)BatchElementParams->SceneProxy;
ShaderBindings.Add(Shader->GetUniformBufferParameter<FLandscapeUniformShaderParameters>(),*BatchElementParams->LandscapeUniformShaderParametersResource);
ShaderBindings.Add(Shader->GetUniformBufferParameter<FLandscapeSectionLODUniformParameters>(), BatchElementParams->LandscapeSectionLODUniformParameters);
if (TexCoordOffsetParameter.IsBound())
{
FVector CameraLocalPos3D = SceneProxy->WorldToLocal.TransformPosition(InView->ViewMatrices.GetViewOrigin());
FVector2D TexCoordOffset(
CameraLocalPos3D.X + SceneProxy->SectionBase.X,
CameraLocalPos3D.Y + SceneProxy->SectionBase.Y
);
ShaderBindings.Add(TexCoordOffsetParameter, FVector2f(TexCoordOffset));
}
}
protected:
LAYOUT_FIELD(FShaderParameter, TexCoordOffsetParameter);
};
IMPLEMENT_TYPE_LAYOUT(FLandscapeVertexFactoryMobileVertexShaderParameters);
/** Shader parameters for use with FLandscapeVertexFactory */
class FLandscapeVertexFactoryMobilePixelShaderParameters : public FLandscapeVertexFactoryPixelShaderParameters
{
DECLARE_TYPE_LAYOUT(FLandscapeVertexFactoryMobilePixelShaderParameters, NonVirtual);
public:
/**
* Bind shader constants by name
* @param ParameterMap - mapping of named shader constants to indices
*/
void Bind(const FShaderParameterMap& ParameterMap)
{
FLandscapeVertexFactoryPixelShaderParameters::Bind(ParameterMap);
}
void GetElementShaderBindings(
const class FSceneInterface* Scene,
const FSceneView* InView,
const class FMeshMaterialShader* Shader,
const EVertexInputStreamType InputStreamType,
ERHIFeatureLevel::Type FeatureLevel,
const FVertexFactory* VertexFactory,
const FMeshBatchElement& BatchElement,
class FMeshDrawSingleShaderBindings& ShaderBindings,
FVertexInputStreamArray& VertexStreams
) const
{
SCOPE_CYCLE_COUNTER(STAT_LandscapeVFDrawTimePS);
FLandscapeVertexFactoryPixelShaderParameters::GetElementShaderBindings(Scene, InView, Shader, InputStreamType, FeatureLevel, VertexFactory, BatchElement, ShaderBindings, VertexStreams);
}
};
IMPLEMENT_TYPE_LAYOUT(FLandscapeVertexFactoryMobilePixelShaderParameters);
/**
* Shader parameters for use with FLandscapeFixedGridVertexFactory
* Simple grid rendering (without dynamic lod blend) needs a simpler fixed setup.
*/
class FLandscapeFixedGridVertexFactoryMobileVertexShaderParameters : public FLandscapeVertexFactoryMobileVertexShaderParameters
{
DECLARE_TYPE_LAYOUT(FLandscapeFixedGridVertexFactoryMobileVertexShaderParameters, NonVirtual);
public:
void GetElementShaderBindings(
const class FSceneInterface* Scene,
const FSceneView* InView,
const class FMeshMaterialShader* Shader,
const EVertexInputStreamType InputStreamType,
ERHIFeatureLevel::Type FeatureLevel,
const FVertexFactory* VertexFactory,
const FMeshBatchElement& BatchElement,
class FMeshDrawSingleShaderBindings& ShaderBindings,
FVertexInputStreamArray& VertexStreams
) const
{
SCOPE_CYCLE_COUNTER(STAT_LandscapeVFDrawTimeVS);
const FLandscapeBatchElementParams* BatchElementParams = (const FLandscapeBatchElementParams*)BatchElement.UserData;
check(BatchElementParams);
ShaderBindings.Add(Shader->GetUniformBufferParameter<FLandscapeUniformShaderParameters>(), *BatchElementParams->LandscapeUniformShaderParametersResource);
ShaderBindings.Add(Shader->GetUniformBufferParameter<FLandscapeFixedGridUniformShaderParameters>(), (*BatchElementParams->FixedGridUniformShaderParameters)[BatchElementParams->CurrentLOD]);
if (TexCoordOffsetParameter.IsBound())
{
ShaderBindings.Add(TexCoordOffsetParameter, FVector4f(ForceInitToZero));
}
}
};
IMPLEMENT_TYPE_LAYOUT(FLandscapeFixedGridVertexFactoryMobileVertexShaderParameters);
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLandscapeVertexFactoryMobile, SF_Vertex, FLandscapeVertexFactoryMobileVertexShaderParameters);
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLandscapeVertexFactoryMobile, SF_Pixel, FLandscapeVertexFactoryMobilePixelShaderParameters);
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLandscapeFixedGridVertexFactoryMobile, SF_Vertex, FLandscapeFixedGridVertexFactoryMobileVertexShaderParameters);
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLandscapeFixedGridVertexFactoryMobile, SF_Pixel, FLandscapeVertexFactoryMobilePixelShaderParameters);
IMPLEMENT_VERTEX_FACTORY_TYPE(FLandscapeVertexFactoryMobile, "/Engine/Private/LandscapeVertexFactory.ush",
EVertexFactoryFlags::UsedWithMaterials
| EVertexFactoryFlags::SupportsStaticLighting
| EVertexFactoryFlags::SupportsDynamicLighting
);
IMPLEMENT_VERTEX_FACTORY_TYPE(FLandscapeFixedGridVertexFactoryMobile, "/Engine/Private/LandscapeVertexFactory.ush",
EVertexFactoryFlags::UsedWithMaterials
| EVertexFactoryFlags::SupportsStaticLighting
| EVertexFactoryFlags::SupportsDynamicLighting
| EVertexFactoryFlags::SupportsCachingMeshDrawCommands
);
void FLandscapeFixedGridVertexFactoryMobile::ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FLandscapeVertexFactoryMobile::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("FIXED_GRID"), TEXT("1"));
}
bool FLandscapeFixedGridVertexFactoryMobile::ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters)
{
return UseMobileLandscapeMesh(Parameters.Platform) &&
(Parameters.MaterialParameters.bIsUsedWithLandscape || Parameters.MaterialParameters.bIsSpecialEngineMaterial);
}
void FLandscapeVertexBufferMobile::UpdateMemoryStat(int32 Delta)
{
INC_DWORD_STAT_BY(STAT_LandscapeVertexMem, Delta);
}
/**
* Initialize the RHI for this rendering resource
*/
void FLandscapeVertexBufferMobile::InitRHI()
{
// create a static vertex buffer
FRHIResourceCreateInfo CreateInfo(TEXT("FLandscapeVertexBufferMobile"));
VertexBufferRHI = RHICreateBuffer(VertexData.Num(), BUF_Static | BUF_VertexBuffer, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
// Copy stored platform data and free CPU copy
void* VertexDataPtr = RHILockBuffer(VertexBufferRHI, 0, VertexData.Num(), RLM_WriteOnly);
FMemory::Memcpy(VertexDataPtr, VertexData.GetData(), VertexData.Num());
VertexData.Empty();
RHIUnlockBuffer(VertexBufferRHI);
}
struct FLandscapeMobileHoleData
{
FRawStaticIndexBuffer16or32Interface* IndexBuffer = nullptr;
int32 NumHoleLods;
int32 IndexBufferSize;
int32 MinHoleIndex;
int32 MaxHoleIndex;
~FLandscapeMobileHoleData()
{
if (IndexBuffer != nullptr)
{
DEC_DWORD_STAT_BY(STAT_LandscapeHoleMem, IndexBuffer->GetResourceDataSize());
IndexBuffer->ReleaseResource();
delete IndexBuffer;
}
}
};
template <typename INDEX_TYPE>
void SerializeLandscapeMobileHoleData(FMemoryArchive& Ar, FLandscapeMobileHoleData& HoleData)
{
Ar << HoleData.MinHoleIndex;
Ar << HoleData.MaxHoleIndex;
TArray<INDEX_TYPE> IndexData;
Ar << HoleData.IndexBufferSize;
IndexData.SetNumUninitialized(HoleData.IndexBufferSize);
Ar.Serialize(IndexData.GetData(), HoleData.IndexBufferSize * sizeof(INDEX_TYPE));
const bool bLoadHoleMeshData = HoleData.IndexBufferSize > 0 && CVarMobileLandscapeHoleMesh.GetValueOnGameThread();
if (bLoadHoleMeshData)
{
FRawStaticIndexBuffer16or32<INDEX_TYPE>* IndexBuffer = new FRawStaticIndexBuffer16or32<INDEX_TYPE>(false);
IndexBuffer->AssignNewBuffer(IndexData);
HoleData.IndexBuffer = IndexBuffer;
BeginInitResource(HoleData.IndexBuffer);
INC_DWORD_STAT_BY(STAT_LandscapeHoleMem, HoleData.IndexBuffer->GetResourceDataSize());
}
}
FLandscapeMobileRenderData::FLandscapeMobileRenderData(const TArray<uint8>& InPlatformData, uint8 InCurFirstLODIdx)
: CurrentFirstLODIdx(InCurFirstLODIdx)
{
FMemoryReader MemAr(InPlatformData);
int32 NumHoleLods = 0;
MemAr << NumHoleLods;
if (NumHoleLods > 0)
{
HoleData = new FLandscapeMobileHoleData;
HoleData->NumHoleLods = NumHoleLods;
bool b16BitIndices = false;
MemAr << b16BitIndices;
if (b16BitIndices)
{
SerializeLandscapeMobileHoleData<uint16>(MemAr, *HoleData);
}
else
{
SerializeLandscapeMobileHoleData<uint32>(MemAr, *HoleData);
}
}
{
int32 VertexCount = 0;
MemAr << VertexCount;
TArray<uint8> VertexData;
VertexData.SetNumUninitialized(VertexCount * sizeof(FLandscapeMobileVertex));
MemAr.Serialize(VertexData.GetData(), VertexData.Num());
VertexBuffer = new FLandscapeVertexBufferMobile(MoveTemp(VertexData));
}
}
FLandscapeMobileRenderData::~FLandscapeMobileRenderData()
{
// Make sure the vertex buffer is always destroyed from the render thread
if (VertexBuffer != nullptr)
{
if (IsInRenderingThread())
{
delete VertexBuffer;
delete HoleData;
}
else
{
FLandscapeVertexBufferMobile* InVertexBuffer = VertexBuffer;
FLandscapeMobileHoleData* InHoleData = HoleData;
ENQUEUE_RENDER_COMMAND(InitCommand)(
[InVertexBuffer, InHoleData](FRHICommandListImmediate& RHICmdList)
{
delete InVertexBuffer;
delete InHoleData;
});
}
}
}
FLandscapeComponentSceneProxyMobile::FLandscapeComponentSceneProxyMobile(ULandscapeComponent* InComponent)
: FLandscapeComponentSceneProxy(InComponent)
, MobileRenderData(InComponent->PlatformData.GetRenderData())
{
check(InComponent);
check(InComponent->MobileMaterialInterfaces.Num() > 0);
check(InComponent->MobileWeightmapTextures.Num() > 0);
WeightmapTextures = InComponent->MobileWeightmapTextures;
NormalmapTexture = InComponent->MobileWeightmapTextures[0];
#if WITH_EDITOR
const TArray<FWeightmapLayerAllocationInfo>& LayerAllocations = InComponent->MobileWeightmapLayerAllocations.Num() ? InComponent->MobileWeightmapLayerAllocations : InComponent->GetWeightmapLayerAllocations();
LayerColors.Empty();
for (const FWeightmapLayerAllocationInfo& Allocation : LayerAllocations)
{
if (Allocation.LayerInfo != nullptr)
{
LayerColors.Add(Allocation.LayerInfo->LayerUsageDebugColor);
}
}
#endif
}
FLandscapeComponentSceneProxyMobile::~FLandscapeComponentSceneProxyMobile()
{
if (VertexFactory)
{
delete VertexFactory;
VertexFactory = NULL;
}
}
SIZE_T FLandscapeComponentSceneProxyMobile::GetTypeHash() const
{
static size_t UniquePointer;
return reinterpret_cast<size_t>(&UniquePointer);
}
void FLandscapeComponentSceneProxyMobile::CreateRenderThreadResources()
{
LLM_SCOPE(ELLMTag::Landscape);
FLandscapeRenderSystem::CreateResources(this);
if (VisibilityHelper.ShouldBeVisible())
{
RegisterSection();
}
auto FeatureLevel = GetScene().GetFeatureLevel();
// Use only index buffers from the shared buffers since the vertex buffers are unique per proxy on mobile
SharedBuffers = FLandscapeComponentSceneProxy::SharedBuffersMap.FindRef(SharedBuffersKey);
if (SharedBuffers == nullptr)
{
SharedBuffers = new FLandscapeSharedBuffers(
SharedBuffersKey, SubsectionSizeQuads, NumSubsections,
FeatureLevel, true);
FLandscapeComponentSceneProxy::SharedBuffersMap.Add(SharedBuffersKey, SharedBuffers);
}
SharedBuffers->AddRef();
// Init vertex buffer
{
check(MobileRenderData->VertexBuffer);
MobileRenderData->VertexBuffer->InitResource();
FLandscapeVertexFactoryMobile* LandscapeVertexFactory = new FLandscapeVertexFactoryMobile(FeatureLevel);
LandscapeVertexFactory->MobileData.PositionComponent = FVertexStreamComponent(MobileRenderData->VertexBuffer, STRUCT_OFFSET(FLandscapeMobileVertex, Position), sizeof(FLandscapeMobileVertex), VET_UByte4N);
for (uint32 Index = 0; Index < LANDSCAPE_MAX_ES_LOD_COMP; ++Index)
{
LandscapeVertexFactory->MobileData.LODHeightsComponent.Add
(FVertexStreamComponent(MobileRenderData->VertexBuffer, STRUCT_OFFSET(FLandscapeMobileVertex, LODHeights) + sizeof(uint8) * 4 * Index, sizeof(FLandscapeMobileVertex), VET_UByte4N));
}
LandscapeVertexFactory->InitResource();
VertexFactory = LandscapeVertexFactory;
}
// Init vertex buffer for rendering to virtual texture
if (UseVirtualTexturing(FeatureLevel))
{
FLandscapeFixedGridVertexFactoryMobile* LandscapeVertexFactory = new FLandscapeFixedGridVertexFactoryMobile(FeatureLevel);
LandscapeVertexFactory->MobileData.PositionComponent = FVertexStreamComponent(MobileRenderData->VertexBuffer, STRUCT_OFFSET(FLandscapeMobileVertex, Position), sizeof(FLandscapeMobileVertex), VET_UByte4N);
for (uint32 Index = 0; Index < LANDSCAPE_MAX_ES_LOD_COMP; ++Index)
{
LandscapeVertexFactory->MobileData.LODHeightsComponent.Add
(FVertexStreamComponent(MobileRenderData->VertexBuffer, STRUCT_OFFSET(FLandscapeMobileVertex, LODHeights) + sizeof(uint8) * 4 * Index, sizeof(FLandscapeMobileVertex), VET_UByte4N));
}
LandscapeVertexFactory->InitResource();
FixedGridVertexFactory = LandscapeVertexFactory;
}
// Assign LandscapeUniformShaderParameters
LandscapeUniformShaderParameters.InitResource();
// Create per Lod uniform buffers
LandscapeFixedGridUniformShaderParameters.AddDefaulted(MaxLOD + 1);
for (int32 LodIndex = 0; LodIndex <= MaxLOD; ++LodIndex)
{
LandscapeFixedGridUniformShaderParameters[LodIndex].InitResource();
FLandscapeFixedGridUniformShaderParameters Parameters;
Parameters.LodValues = FVector4f(
LodIndex,
0.f,
(float)((SubsectionSizeVerts >> LodIndex) - 1),
1.f / (float)((SubsectionSizeVerts >> LodIndex) - 1));
LandscapeFixedGridUniformShaderParameters[LodIndex].SetContents(Parameters);
}
MobileRenderData->bReadyForStreaming = true;
}
TSharedPtr<FLandscapeMobileRenderData, ESPMode::ThreadSafe> FLandscapeComponentDerivedData::GetRenderData()
{
// This function is expected to be called from either the GameThread or via ParallelFor from the GameThread
check(!IsInActualRenderingThread());
if (FPlatformProperties::RequiresCookedData() && CachedRenderData.IsValid())
{
// on device we can re-use the cached data if we are re-registering our component.
return CachedRenderData;
}
else
{
check(CompressedLandscapeData.Num() > 0);
CachedRenderData.Reset();
FMemoryReader Ar(CompressedLandscapeData);
// Note: change LANDSCAPE_FULL_DERIVEDDATA_VER when modifying the serialization layout
int32 UncompressedSize;
Ar << UncompressedSize;
int32 CompressedSize;
Ar << CompressedSize;
TArray<uint8> CompressedData;
CompressedData.Empty(CompressedSize);
CompressedData.AddUninitialized(CompressedSize);
Ar.Serialize(CompressedData.GetData(), CompressedSize);
TArray<uint8> UncompressedData;
UncompressedData.Empty(UncompressedSize);
UncompressedData.AddUninitialized(UncompressedSize);
verify(FCompression::UncompressMemory(NAME_Zlib, UncompressedData.GetData(), UncompressedSize, CompressedData.GetData(), CompressedSize));
TSharedPtr<FLandscapeMobileRenderData, ESPMode::ThreadSafe> RenderData = MakeShareable(new FLandscapeMobileRenderData(MoveTemp(UncompressedData), (uint8)StreamingLODDataArray.Num()));
// if running on device
if (FPlatformProperties::RequiresCookedData())
{
// free the compressed data now that we have used it to create the render data.
CompressedLandscapeData.Empty();
}
// store a reference to the render data so we can use it again should the component be reregistered.
CachedRenderData = RenderData;
return RenderData;
}
}
void FLandscapeComponentSceneProxyMobile::ApplyMeshElementModifier(FMeshBatchElement& InOutMeshElement, int32 InLodIndex) const
{
const bool bHoleDataExists = MobileRenderData->HoleData != nullptr && MobileRenderData->HoleData->IndexBuffer != nullptr && InLodIndex < MobileRenderData->HoleData->NumHoleLods;
if (bHoleDataExists)
{
FLandscapeMobileHoleData const& HoleData = *MobileRenderData->HoleData;
InOutMeshElement.IndexBuffer = HoleData.IndexBuffer;
InOutMeshElement.NumPrimitives = HoleData.IndexBufferSize / 3;
InOutMeshElement.FirstIndex = 0;
InOutMeshElement.MinVertexIndex = HoleData.MinHoleIndex;
InOutMeshElement.MaxVertexIndex = HoleData.MaxHoleIndex;
}
}
#if PLATFORM_SUPPORTS_LANDSCAPE_VISUAL_MESH_LOD_STREAMING
uint8 FLandscapeComponentSceneProxyMobile::GetCurrentFirstLODIdx_RenderThread() const
{
check(MobileRenderData.IsValid());
return MobileRenderData->CurrentFirstLODIdx;
}
#endif