2019-12-26 15:33:43 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-08-21 08:57:14 -04:00
# include "RuntimeVirtualTextureBuild.h"
# include "Components/RuntimeVirtualTextureComponent.h"
# include "Engine/TextureRenderTarget2D.h"
# include "Misc/ScopedSlowTask.h"
2019-09-22 16:27:41 -04:00
# include "RendererInterface.h"
# include "RenderTargetPool.h"
2019-08-21 09:38:23 -04:00
# include "SceneInterface.h"
2019-08-21 08:57:14 -04:00
# include "VT/RuntimeVirtualTexture.h"
# include "VT/RuntimeVirtualTextureRender.h"
2019-09-22 16:27:41 -04:00
namespace
{
/** Container for render resources needed to render the runtime virtual texture. */
class FRenderTileResources : public FRenderResource
{
public :
FRenderTileResources ( int32 InNumLayers , int32 InTileSize , EPixelFormat InFormat )
: NumLayers ( InNumLayers )
, TileSize ( InTileSize )
, Format ( InFormat )
{
}
//~ 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 , Format , 1 , 1 , TexCreate_RenderTargetable , CreateInfo ) ;
StagingTextures [ Layer ] = RHICmdList . CreateTexture2D ( TileSize , TileSize , Format , 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.
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 NumLayers ;
int32 TileSize ;
EPixelFormat Format ;
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 , FIntVector const & DestPos )
{
for ( int32 y = 0 ; y < TileSize ; y + + )
{
memcpy (
DestPixels + DestLayerStride * DestPos [ 2 ] + 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 , FIntVector const & DestPos , ERuntimeVirtualTextureMaterialType MaterialType )
{
if ( MaterialType = = ERuntimeVirtualTextureMaterialType : : WorldHeight )
{
TCopyTile ( ( uint16 * ) TilePixels , TileSize , ( uint16 * ) DestPixels , DestStride , DestLayerStride , DestPos ) ;
}
else
{
TCopyTile ( ( FColor * ) TilePixels , TileSize , ( FColor * ) DestPixels , DestStride , DestLayerStride , DestPos ) ;
}
}
}
2019-08-21 08:57:14 -04:00
namespace RuntimeVirtualTexture
{
2019-08-21 09:38:23 -04:00
bool HasStreamedMips ( URuntimeVirtualTextureComponent * InComponent )
2019-08-21 08:57:14 -04:00
{
if ( InComponent = = nullptr )
{
return false ;
}
URuntimeVirtualTexture * RuntimeVirtualTexture = InComponent - > GetVirtualTexture ( ) ;
if ( RuntimeVirtualTexture = = nullptr )
{
return false ;
}
if ( RuntimeVirtualTexture - > GetStreamLowMips ( ) < = 0 )
{
return false ;
}
return true ;
}
2019-08-21 09:38:23 -04:00
bool BuildStreamedMips ( URuntimeVirtualTextureComponent * InComponent , ERuntimeVirtualTextureDebugType DebugType )
2019-08-21 08:57:14 -04:00
{
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 ( ) ;
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 ) ) ;
2019-08-21 11:53:10 -04:00
const int32 RenderLevel = FMath : : Max ( MaxLevel - RuntimeVirtualTexture - > GetStreamLowMips ( ) + 1 , 0 ) ;
2019-09-22 16:27:41 -04:00
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 ( ) ;
2019-08-21 08:57:14 -04:00
const ERuntimeVirtualTextureMaterialType MaterialType = RuntimeVirtualTexture - > GetMaterialType ( ) ;
2019-09-22 16:27:41 -04:00
const EPixelFormat RenderTargetFormat = ( MaterialType = = ERuntimeVirtualTextureMaterialType : : WorldHeight ) ? PF_G16 : PF_B8G8R8A8 ;
const int32 BytesPerPixel = ( MaterialType = = ERuntimeVirtualTextureMaterialType : : WorldHeight ) ? 2 : 4 ;
2019-08-21 08:57:14 -04:00
// Spin up slow task UI
const float TaskWorkRender = NumTilesX * NumTilesY ;
const float TaskWorkBuildBulkData = NumTilesX * NumTilesY / 2 ;
FScopedSlowTask Task ( TaskWorkRender + TaskWorkBuildBulkData , FText : : AsCultureInvariant ( RuntimeVirtualTexture - > GetName ( ) ) ) ;
Task . MakeDialog ( true ) ;
// Allocate render targets for rendering out the runtime virtual texture tiles
2019-09-22 16:27:41 -04:00
FRenderTileResources RenderTileResources ( NumLayers , TileSize , RenderTargetFormat ) ;
BeginInitResource ( & RenderTileResources ) ;
2019-08-21 08:57:14 -04:00
2019-09-22 16:27:41 -04:00
// Final pixels will contain image data for each virtual texture layer in order
TArray64 < uint8 > FinalPixels ;
FinalPixels . InsertUninitialized ( 0 , ImageSizeX * ImageSizeY * NumLayers * BytesPerPixel ) ;
2019-08-21 08:57:14 -04:00
2019-09-22 16:27:41 -04:00
// Iterate over all tiles and render/store each one to the final image
for ( int32 TileY = 0 ; TileY < NumTilesY & & ! Task . ShouldCancel ( ) ; TileY + + )
2019-08-21 08:57:14 -04:00
{
2019-09-22 16:27:41 -04:00
for ( int32 TileX = 0 ; TileX < NumTilesX & & ! Task . ShouldCancel ( ) ; TileX + + )
2019-08-21 08:57:14 -04:00
{
Task . EnterProgressFrame ( ) ;
// Render tile
const FBox2D UVRange = FBox2D (
2019-09-22 16:27:41 -04:00
FVector2D ( ( float ) TileX / ( float ) NumTilesX , ( float ) TileY / ( float ) NumTilesY ) ,
FVector2D ( ( float ) ( TileX + 1 ) / ( float ) NumTilesX , ( float ) ( TileY + 1 ) / ( float ) NumTilesY ) ) ;
2019-08-21 08:57:14 -04:00
2019-09-22 16:27:41 -04:00
ENQUEUE_RENDER_COMMAND ( BakeStreamingTextureTileCommand ) ( [
Scene , VirtualTextureSceneIndex ,
& RenderTileResources ,
MaterialType , NumLayers ,
Transform , UVRange ,
RenderLevel , MaxLevel ,
TileX , TileY ,
TileSize , ImageSizeX , ImageSizeY ,
& FinalPixels ,
DebugType
] ( FRHICommandListImmediate & RHICmdList )
2019-08-21 08:57:14 -04:00
{
2019-09-22 16:27:41 -04:00
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 ) ) ;
}
2019-09-29 16:50:18 -04:00
RuntimeVirtualTexture : : FRenderPageBatchDesc Desc ;
Desc . Scene = Scene - > GetRenderScene ( ) ;
Desc . RuntimeVirtualTextureMask = 1 < < VirtualTextureSceneIndex ;
Desc . UVToWorld = Transform ;
Desc . MaterialType = MaterialType ;
Desc . MaxLevel = MaxLevel ;
Desc . bClearTextures = true ;
2019-10-31 16:06:30 -04:00
Desc . bIsThumbnails = false ;
2019-09-29 16:50:18 -04:00
Desc . DebugType = DebugType ;
Desc . NumPageDescs = 1 ;
2019-09-29 17:29:22 -04:00
Desc . Targets [ 0 ] . Texture = RenderTileResources . GetRenderTarget ( 0 ) ;
Desc . Targets [ 1 ] . Texture = RenderTileResources . GetRenderTarget ( 1 ) ;
Desc . Targets [ 2 ] . Texture = RenderTileResources . GetRenderTarget ( 2 ) ;
2019-09-29 16:50:18 -04:00
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 ) ;
2019-08-21 08:57:14 -04:00
2019-09-22 16:27:41 -04:00
// Transition render targets for copying
for ( int32 Layer = 0 ; Layer < NumLayers ; Layer + + )
2019-08-21 08:57:14 -04:00
{
2019-09-22 16:27:41 -04:00
RHICmdList . TransitionResource ( EResourceTransitionAccess : : EReadable , RenderTileResources . GetRenderTarget ( Layer ) ) ;
2019-08-21 08:57:14 -04:00
}
2019-09-22 16:27:41 -04:00
// 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 FIntVector DestPos ( TileX * TileSize , TileY * TileSize , Layer ) ;
CopyTile ( TilePixels , TileSize , FinalPixels . GetData ( ) , ImageSizeX , ImageSizeX * ImageSizeY , DestPos , MaterialType ) ;
RHICmdList . UnmapStagingSurface ( RenderTileResources . GetStagingTexture ( Layer ) ) ;
}
} ) ;
2019-08-21 08:57:14 -04:00
}
}
2019-09-22 16:27:41 -04:00
ReleaseResourceAndFlush ( & RenderTileResources ) ;
2019-08-21 08:57:14 -04:00
if ( Task . ShouldCancel ( ) )
{
return false ;
}
// Place final pixel data into the runtime virtual texture
Task . EnterProgressFrame ( TaskWorkBuildBulkData ) ;
RuntimeVirtualTexture - > Modify ( ) ;
2019-09-22 16:27:41 -04:00
RuntimeVirtualTexture - > InitializeStreamingTexture ( ImageSizeX , ImageSizeY , ( uint8 * ) FinalPixels . GetData ( ) ) ;
2019-08-21 08:57:14 -04:00
RuntimeVirtualTexture - > PostEditChange ( ) ;
return true ;
}
}