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

207 lines
7.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RuntimeVirtualTextureProducer.h"
#include "RendererInterface.h"
#include "ScenePrivate.h"
#include "VT/RuntimeVirtualTextureRender.h"
#include "VT/RuntimeVirtualTextureSceneProxy.h"
FRuntimeVirtualTextureFinalizer::FRuntimeVirtualTextureFinalizer(
FVTProducerDescription const& InDesc,
int32 InRuntimeVirtualTextureId,
ERuntimeVirtualTextureMaterialType InMaterialType,
bool InClearTextures,
FSceneInterface* InScene,
FTransform const& InUVToWorld,
FBox const& InWorldBounds,
FVector4f const& InCustomMaterialData)
: Desc(InDesc)
, RuntimeVirtualTextureId(InRuntimeVirtualTextureId)
, MaterialType(InMaterialType)
, bClearTextures(InClearTextures)
, Scene(InScene)
, UVToWorld(InUVToWorld)
, WorldBounds(InWorldBounds)
, CustomMaterialData(InCustomMaterialData)
{
}
bool FRuntimeVirtualTextureFinalizer::IsReady()
{
return RuntimeVirtualTexture::IsSceneReadyToRender(Scene);
}
void FRuntimeVirtualTextureFinalizer::AddTile(FTileEntry& Tile)
{
Tiles.Add(Tile);
}
void FRuntimeVirtualTextureFinalizer::RenderFinalize(FRDGBuilder& GraphBuilder, ISceneRenderer* SceneRenderer)
{
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
RuntimeVirtualTexture::FRenderPageBatchDesc RenderPageBatchDesc;
RenderPageBatchDesc.SceneRenderer = SceneRenderer;
RenderPageBatchDesc.RuntimeVirtualTextureId = RuntimeVirtualTextureId;
RenderPageBatchDesc.UVToWorld = UVToWorld;
RenderPageBatchDesc.WorldBounds = WorldBounds;
RenderPageBatchDesc.MaterialType = MaterialType;
RenderPageBatchDesc.MaxLevel = Desc.MaxLevel;
RenderPageBatchDesc.bClearTextures = bClearTextures;
RenderPageBatchDesc.bIsThumbnails = false;
RenderPageBatchDesc.FixedColor = FLinearColor::Transparent;
RenderPageBatchDesc.CustomMaterialData = CustomMaterialData;
for (int LayerIndex = 0; LayerIndex < RuntimeVirtualTexture::MaxTextureLayers; ++LayerIndex)
{
RenderPageBatchDesc.Targets[LayerIndex].PooledRenderTarget = Tiles[0].Targets[LayerIndex].PooledRenderTarget;
}
int32 BatchSize = 0;
for (auto Entry : Tiles)
{
bool bBreakBatchForTextures = false;
for (int LayerIndex = 0; LayerIndex < RuntimeVirtualTexture::MaxTextureLayers; ++LayerIndex)
{
// This should never happen which is why we don't bother sorting to maximize batch size
bBreakBatchForTextures |= (RenderPageBatchDesc.Targets[LayerIndex].PooledRenderTarget != Entry.Targets[LayerIndex].PooledRenderTarget);
}
if (BatchSize == RuntimeVirtualTexture::MaxRenderPageBatch || bBreakBatchForTextures)
{
RenderPageBatchDesc.NumPageDescs = BatchSize;
Batches.Add(RuntimeVirtualTexture::InitPageBatch(GraphBuilder, RenderPageBatchDesc));
BatchSize = 0;
}
if (bBreakBatchForTextures)
{
for (int LayerIndex = 0; LayerIndex < RuntimeVirtualTexture::MaxTextureLayers; ++LayerIndex)
{
RenderPageBatchDesc.Targets[LayerIndex].PooledRenderTarget = Entry.Targets[LayerIndex].PooledRenderTarget;
}
}
RuntimeVirtualTexture::FRenderPageDesc& RenderPageDesc = RenderPageBatchDesc.PageDescs[BatchSize++];
const float X = (float)FMath::ReverseMortonCode2_64(Entry.vAddress);
const float Y = (float)FMath::ReverseMortonCode2_64(Entry.vAddress >> 1);
const float DivisorX = (float)Desc.BlockWidthInTiles / (float)(1 << Entry.vLevel);
const float DivisorY = (float)Desc.BlockHeightInTiles / (float)(1 << Entry.vLevel);
const FVector2D UV(X / DivisorX, Y / DivisorY);
const FVector2D UVSize(1.f / DivisorX, 1.f / DivisorY);
const FVector2D UVBorder = UVSize * ((float)Desc.TileBorderSize / (float)Desc.TileSize);
const FBox2D UVRange(UV - UVBorder, UV + UVSize + UVBorder);
RenderPageDesc.vLevel = Entry.vLevel;
RenderPageDesc.UVRange = UVRange;
const int32 TileSize = Desc.TileSize + 2 * Desc.TileBorderSize;
for (int LayerIndex = 0; LayerIndex < RuntimeVirtualTexture::MaxTextureLayers; ++LayerIndex)
{
const FIntPoint DestinationRectStart0(Entry.Targets[LayerIndex].pPageLocation.X * TileSize, Entry.Targets[LayerIndex].pPageLocation.Y * TileSize);
RenderPageDesc.DestRect[LayerIndex] = FIntRect(DestinationRectStart0, DestinationRectStart0 + FIntPoint(TileSize, TileSize));
}
}
if (BatchSize > 0)
{
RenderPageBatchDesc.NumPageDescs = BatchSize;
Batches.Add(RuntimeVirtualTexture::InitPageBatch(GraphBuilder, RenderPageBatchDesc));
}
for (RuntimeVirtualTexture::FBatchRenderContext const* Batch : Batches)
{
RuntimeVirtualTexture::RenderPageBatch(GraphBuilder, *Batch);
}
}
void FRuntimeVirtualTextureFinalizer::Finalize(FRDGBuilder& GraphBuilder)
{
for (RuntimeVirtualTexture::FBatchRenderContext const* Batch : Batches)
{
RuntimeVirtualTexture::FinalizePageBatch(GraphBuilder, *Batch);
}
Tiles.SetNumUnsafeInternal(0);
Batches.SetNumUnsafeInternal(0);
}
FRuntimeVirtualTextureProducer::FRuntimeVirtualTextureProducer(
FVTProducerDescription const& InDesc,
int32 InRuntimeVirtualTextureId,
ERuntimeVirtualTextureMaterialType InMaterialType,
bool InClearTextures,
FSceneInterface* InScene,
FTransform const& InUVToWorld,
FBox const& InWorldBounds)
: Finalizer(InDesc, InRuntimeVirtualTextureId, InMaterialType, InClearTextures, InScene, InUVToWorld, InWorldBounds, FVector4f::Zero())
{
}
FRuntimeVirtualTextureProducer::FRuntimeVirtualTextureProducer(
FVTProducerDescription const& InDesc,
int32 InRuntimeVirtualTextureId,
ERuntimeVirtualTextureMaterialType InMaterialType,
bool InClearTextures,
FSceneInterface* InScene,
FTransform const& InUVToWorld,
FBox const& InWorldBounds,
FVector4f const& InCustomMaterialData)
: Finalizer(InDesc, InRuntimeVirtualTextureId, InMaterialType, InClearTextures, InScene, InUVToWorld, InWorldBounds, InCustomMaterialData)
{
}
FVTRequestPageResult FRuntimeVirtualTextureProducer::RequestPageData(
FRHICommandListBase& RHICmdList,
const FVirtualTextureProducerHandle& ProducerHandle,
uint8 LayerMask,
uint8 vLevel,
uint64 vAddress,
EVTRequestPagePriority Priority)
{
// Note that when the finalizer is not ready (outside of the Begin/End Scene Render) we return the Saturated status here.
// This is to indicate that the RVT can't render at this time (because we require the GPU Scene to be up to date).
// This will happen for DrawTileMesh() style rendering used by material/HLOD baking.
// It's best to avoid sampling RVT in material baking, but if it is necessary then an option is to have streaming mips built and enabled.
FVTRequestPageResult result;
result.Handle = 0;
result.Status = Finalizer.IsReady() ? EVTRequestPageStatus::Available : EVTRequestPageStatus::Saturated;
return result;
}
IVirtualTextureFinalizer* FRuntimeVirtualTextureProducer::ProducePageData(
FRHICommandListBase& RHICmdList,
ERHIFeatureLevel::Type FeatureLevel,
EVTProducePageFlags Flags,
const FVirtualTextureProducerHandle& ProducerHandle,
uint8 LayerMask,
uint8 vLevel,
uint64 vAddress,
uint64 RequestHandle,
const FVTProduceTargetLayer* TargetLayers)
{
FRuntimeVirtualTextureFinalizer::FTileEntry Tile;
Tile.vAddress = vAddress;
Tile.vLevel = vLevel;
// Partial layer masks can happen when one layer has more physical space available so that old pages are evicted at different rates.
// We currently render all layers even for these partial requests. That might be considered inefficient?
// But since the problem is avoided by setting bSinglePhysicalSpace on the URuntimeVirtualTexture we can live with it.
for (int LayerIndex = 0; LayerIndex < RuntimeVirtualTexture::MaxTextureLayers; ++LayerIndex)
{
if (TargetLayers[LayerIndex].PooledRenderTarget != nullptr)
{
Tile.Targets[LayerIndex] = TargetLayers[LayerIndex];
}
}
Finalizer.AddTile(Tile);
return &Finalizer;
}