Files
UnrealEngineUWP/Engine/Source/Editor/VirtualTexturingEditor/Private/RuntimeVirtualTextureBuild.cpp
jeremy moore 66f3fcb592 Fix RVT being marked modified when it shouldn't be on building streaming mips
Fix incorrect asset name being shown in progress when building streaming mips
Fix RVT volume bounds actor not being set for actors outside of current package

#ROBOMERGE-SOURCE: CL 12800514 via CL 12800562 via CL 12800582 via CL 12800589
#ROBOMERGE-BOT: RELEASE (Release-Engine-Staging -> Main) (v681-12776863)

[CL 12800599 by jeremy moore in Main branch]
2020-04-15 10:23:37 -04:00

295 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RuntimeVirtualTextureBuild.h"
#include "Components/RuntimeVirtualTextureComponent.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Misc/ScopedSlowTask.h"
#include "RendererInterface.h"
#include "RenderTargetPool.h"
#include "SceneInterface.h"
#include "VT/RuntimeVirtualTexture.h"
#include "VT/RuntimeVirtualTextureRender.h"
#include "VT/VirtualTextureBuilder.h"
namespace
{
/** Container for render resources needed to render the runtime virtual texture. */
class FRenderTileResources : public FRenderResource
{
public:
FRenderTileResources(int32 InTileSize, int32 InNumTilesX, int32 InNumTilesY, int32 InNumLayers, TArrayView<EPixelFormat> const& InLayerFormats)
: TileSize(InTileSize)
, NumLayers(InNumLayers)
, TotalSizeBytes(0)
{
LayerFormats.SetNumZeroed(InNumLayers);
LayerOffsets.SetNumZeroed(InNumLayers);
for (int32 Layer = 0; Layer < NumLayers; ++Layer)
{
check(InLayerFormats[Layer] == PF_G16 || InLayerFormats[Layer] == PF_B8G8R8A8 || InLayerFormats[Layer] == PF_DXT1 || InLayerFormats[Layer] == PF_DXT5 || InLayerFormats[Layer] == PF_BC5);
LayerFormats[Layer] = InLayerFormats[Layer] == PF_G16 ? PF_G16 : PF_B8G8R8A8;
LayerOffsets[Layer] = TotalSizeBytes;
TotalSizeBytes += CalculateImageBytes(InTileSize, InTileSize, 0, LayerFormats[Layer]) * InNumTilesX * InNumTilesY;
}
}
//~ Begin FRenderResource Interface.
virtual void InitRHI() override
{
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
RenderTargets.Init(nullptr, NumLayers);
StagingTextures.Init(nullptr, NumLayers);
for (int32 Layer = 0; Layer < NumLayers; ++Layer)
{
FRHIResourceCreateInfo CreateInfo;
RenderTargets[Layer] = RHICmdList.CreateTexture2D(TileSize, TileSize, LayerFormats[Layer], 1, 1, TexCreate_RenderTargetable, CreateInfo);
StagingTextures[Layer] = RHICmdList.CreateTexture2D(TileSize, TileSize, LayerFormats[Layer], 1, 1, TexCreate_CPUReadback, CreateInfo);
}
Fence = RHICmdList.CreateGPUFence(TEXT("Runtime Virtual Texture Build"));
}
virtual void ReleaseRHI() override
{
RenderTargets.Empty();
StagingTextures.Empty();
Fence.SafeRelease();
}
//~ End FRenderResource Interface.
int32 GetNumLayers() const { return NumLayers; }
int32 GetTotalSizeBytes() const { return TotalSizeBytes; }
EPixelFormat GetLayerFormat(int32 Index) const { return LayerFormats[Index]; }
int32 GetLayerOffset(int32 Index) const { return LayerOffsets[Index]; }
FRHITexture2D* GetRenderTarget(int32 Index) const { return Index < NumLayers ? RenderTargets[Index] : nullptr; }
FRHITexture2D* GetStagingTexture(int32 Index) const { return Index < NumLayers ? StagingTextures[Index] : nullptr; }
FRHIGPUFence* GetFence() const { return Fence; }
private:
int32 TileSize;
int32 NumLayers;
int32 TotalSizeBytes;
TArray<EPixelFormat> LayerFormats;
TArray<int32> LayerOffsets;
TArray<FTexture2DRHIRef> RenderTargets;
TArray<FTexture2DRHIRef> StagingTextures;
FGPUFenceRHIRef Fence;
};
/** Templatized helper function for copying a rendered tile to the final composited image data. */
template<typename T>
void TCopyTile(T* TilePixels, int32 TileSize, T* DestPixels, int32 DestStride, int32 DestLayerStride, FIntPoint const& DestPos)
{
for (int32 y = 0; y < TileSize; y++)
{
memcpy(
DestPixels + DestStride * (DestPos[1] + y) + DestPos[0],
TilePixels + TileSize * y,
TileSize * sizeof(T));
}
}
/** Function for copying a rendered tile to the final composited image data. Needs ERuntimeVirtualTextureMaterialType to know what type of data is being copied. */
void CopyTile(void* TilePixels, int32 TileSize, void* DestPixels, int32 DestStride, int32 DestLayerStride, FIntPoint const& DestPos, EPixelFormat Format)
{
check(Format == PF_G16 || Format == PF_B8G8R8A8);
if (Format == PF_G16)
{
TCopyTile((uint16*)TilePixels, TileSize, (uint16*)DestPixels, DestStride, DestLayerStride, DestPos);
}
else if (Format == PF_B8G8R8A8)
{
TCopyTile((FColor*)TilePixels, TileSize, (FColor*)DestPixels, DestStride, DestLayerStride, DestPos);
}
}
}
namespace RuntimeVirtualTexture
{
bool HasStreamedMips(URuntimeVirtualTextureComponent* InComponent)
{
if (InComponent == nullptr)
{
return false;
}
if (InComponent->GetVirtualTexture() == nullptr || InComponent->GetStreamingTexture() == nullptr)
{
return false;
}
if (InComponent->NumStreamingMips() <= 0)
{
return false;
}
return true;
}
bool BuildStreamedMips(URuntimeVirtualTextureComponent* InComponent, ERuntimeVirtualTextureDebugType DebugType)
{
if (!HasStreamedMips(InComponent))
{
return true;
}
URuntimeVirtualTexture* RuntimeVirtualTexture = InComponent->GetVirtualTexture();
FSceneInterface* Scene = InComponent->GetScene();
const uint32 VirtualTextureSceneIndex = RuntimeVirtualTexture::GetRuntimeVirtualTextureSceneIndex_GameThread(InComponent);
const FTransform Transform = InComponent->GetVirtualTextureTransform();
const FBox Bounds = InComponent->Bounds.GetBox();
FVTProducerDescription VTDesc;
RuntimeVirtualTexture->GetProducerDescription(VTDesc, Transform);
const int32 TileSize = VTDesc.TileSize;
const int32 TileBorderSize = VTDesc.TileBorderSize;
const int32 TextureSizeX = VTDesc.WidthInBlocks * VTDesc.BlockWidthInTiles * TileSize;
const int32 TextureSizeY = VTDesc.HeightInBlocks * VTDesc.BlockHeightInTiles * TileSize;
const int32 MaxLevel = (int32)FMath::CeilLogTwo(FMath::Max(VTDesc.BlockWidthInTiles, VTDesc.BlockHeightInTiles));
const int32 RenderLevel = FMath::Max(MaxLevel - InComponent->NumStreamingMips() + 1, 0);
const int32 ImageSizeX = FMath::Max(TileSize, TextureSizeX >> RenderLevel);
const int32 ImageSizeY = FMath::Max(TileSize, TextureSizeY >> RenderLevel);
const int32 NumTilesX = ImageSizeX / TileSize;
const int32 NumTilesY = ImageSizeY / TileSize;
const int32 NumLayers = RuntimeVirtualTexture->GetLayerCount();
const ERuntimeVirtualTextureMaterialType MaterialType = RuntimeVirtualTexture->GetMaterialType();
TArray<EPixelFormat, TInlineAllocator<4>> LayerFormats;
for (int32 Layer = 0; Layer < NumLayers; ++Layer)
{
LayerFormats.Add(RuntimeVirtualTexture->GetLayerFormat(Layer));
}
// Spin up slow task UI
const float TaskWorkRender = NumTilesX * NumTilesY;
const float TaskWorkBuildBulkData = NumTilesX * NumTilesY / 2;
FScopedSlowTask Task(TaskWorkRender + TaskWorkBuildBulkData, FText::AsCultureInvariant(InComponent->GetStreamingTexture()->GetName()));
Task.MakeDialog(true);
// Allocate render targets for rendering out the runtime virtual texture tiles
FRenderTileResources RenderTileResources(TileSize, NumTilesX, NumTilesY, NumLayers, LayerFormats);
BeginInitResource(&RenderTileResources);
// Final pixels will contain image data for each virtual texture layer in order
TArray64<uint8> FinalPixels;
FinalPixels.SetNumUninitialized(RenderTileResources.GetTotalSizeBytes());
// Iterate over all tiles and render/store each one to the final image
for (int32 TileY = 0; TileY < NumTilesY && !Task.ShouldCancel(); TileY++)
{
for (int32 TileX = 0; TileX < NumTilesX && !Task.ShouldCancel(); TileX++)
{
Task.EnterProgressFrame();
// Render tile
const FBox2D UVRange = FBox2D(
FVector2D((float)TileX / (float)NumTilesX, (float)TileY / (float)NumTilesY),
FVector2D((float)(TileX + 1) / (float)NumTilesX, (float)(TileY + 1) / (float)NumTilesY));
ENQUEUE_RENDER_COMMAND(BakeStreamingTextureTileCommand)([
Scene, VirtualTextureSceneIndex,
&RenderTileResources,
MaterialType, NumLayers,
Transform, Bounds, UVRange,
RenderLevel, MaxLevel,
TileX, TileY,
TileSize, ImageSizeX, ImageSizeY,
&FinalPixels,
DebugType
](FRHICommandListImmediate& RHICmdList)
{
const FBox2D TileBox(FVector2D(0, 0), FVector2D(TileSize, TileSize));
const FIntRect TileRect(0, 0, TileSize, TileSize);
// Transition render targets for writing
for (int32 Layer = 0; Layer < NumLayers; Layer++)
{
RHICmdList.TransitionResource(EResourceTransitionAccess::EWritable, RenderTileResources.GetRenderTarget(Layer));
}
RuntimeVirtualTexture::FRenderPageBatchDesc Desc;
Desc.Scene = Scene->GetRenderScene();
Desc.RuntimeVirtualTextureMask = 1 << VirtualTextureSceneIndex;
Desc.UVToWorld = Transform;
Desc.WorldBounds = Bounds;
Desc.MaterialType = MaterialType;
Desc.MaxLevel = MaxLevel;
Desc.bClearTextures = true;
Desc.bIsThumbnails = false;
Desc.DebugType = DebugType;
Desc.NumPageDescs = 1;
Desc.Targets[0].Texture = RenderTileResources.GetRenderTarget(0);
Desc.Targets[1].Texture = RenderTileResources.GetRenderTarget(1);
Desc.Targets[2].Texture = RenderTileResources.GetRenderTarget(2);
Desc.PageDescs[0].DestBox[0] = TileBox;
Desc.PageDescs[0].DestBox[1] = TileBox;
Desc.PageDescs[0].DestBox[2] = TileBox;
Desc.PageDescs[0].UVRange = UVRange;
Desc.PageDescs[0].vLevel = RenderLevel;
RuntimeVirtualTexture::RenderPages(RHICmdList, Desc);
// Transition render targets for copying
for (int32 Layer = 0; Layer < NumLayers; Layer++)
{
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, RenderTileResources.GetRenderTarget(Layer));
}
// Copy to staging
for (int32 Layer = 0; Layer < NumLayers; Layer++)
{
RHICmdList.CopyTexture(RenderTileResources.GetRenderTarget(Layer), RenderTileResources.GetStagingTexture(Layer), FRHICopyTextureInfo());
}
//todo[vt]: Insert fence for immediate read back. But is there no API to wait on it?
RHICmdList.WriteGPUFence(RenderTileResources.GetFence());
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
// Read back tile data and copy into final destination
for (int32 Layer = 0; Layer < NumLayers; Layer++)
{
void* TilePixels = nullptr;
int32 OutWidth, OutHeight;
RHICmdList.MapStagingSurface(RenderTileResources.GetStagingTexture(Layer), TilePixels, OutWidth, OutHeight);
check(TilePixels != nullptr);
check(OutWidth == TileSize && OutHeight == TileSize);
const int32 LayerOffset = RenderTileResources.GetLayerOffset(Layer);
const EPixelFormat LayerFormat = RenderTileResources.GetLayerFormat(Layer);
const FIntPoint DestPos(TileX * TileSize, TileY * TileSize);
CopyTile(TilePixels, TileSize, FinalPixels.GetData() + LayerOffset, ImageSizeX, ImageSizeX * ImageSizeY, DestPos, LayerFormat);
RHICmdList.UnmapStagingSurface(RenderTileResources.GetStagingTexture(Layer));
}
});
}
}
BeginReleaseResource(&RenderTileResources);
FlushRenderingCommands();
if (Task.ShouldCancel())
{
return false;
}
// Place final pixel data into the runtime virtual texture
Task.EnterProgressFrame(TaskWorkBuildBulkData);
InComponent->InitializeStreamingTexture(ImageSizeX, ImageSizeY, (uint8*)FinalPixels.GetData());
return true;
}
}