2019-12-26 15:33:43 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-08-21 08:57:14 -04:00
2020-08-11 01:36:57 -04:00
# include "RuntimeVirtualTextureBuildStreamingMips.h"
2019-08-21 08:57:14 -04:00
# include "Components/RuntimeVirtualTextureComponent.h"
2020-09-01 14:07:48 -04:00
# include "ContentStreaming.h"
2019-08-21 08:57:14 -04:00
# 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"
2020-10-26 16:44:44 -04:00
# include "RenderGraphBuilder.h"
2019-08-21 08:57:14 -04:00
# include "VT/RuntimeVirtualTexture.h"
# include "VT/RuntimeVirtualTextureRender.h"
2020-04-15 10:23:37 -04:00
# include "VT/VirtualTextureBuilder.h"
2023-02-23 01:23:57 -05:00
# include "SceneUtils.h"
2019-08-21 08:57:14 -04:00
2019-09-22 16:27:41 -04:00
namespace
{
/** Container for render resources needed to render the runtime virtual texture. */
2020-08-11 01:36:57 -04:00
class FTileRenderResources : public FRenderResource
2019-09-22 16:27:41 -04:00
{
public :
2020-08-11 01:36:57 -04:00
FTileRenderResources ( int32 InTileSize , int32 InNumTilesX , int32 InNumTilesY , int32 InNumLayers , TArrayView < EPixelFormat > const & InLayerFormats )
2020-04-07 12:40:55 -04:00
: TileSize ( InTileSize )
, NumLayers ( InNumLayers )
, TotalSizeBytes ( 0 )
2019-09-22 16:27:41 -04:00
{
2020-04-07 12:40:55 -04:00
LayerFormats . SetNumZeroed ( InNumLayers ) ;
LayerOffsets . SetNumZeroed ( InNumLayers ) ;
for ( int32 Layer = 0 ; Layer < NumLayers ; + + Layer )
{
2021-07-27 22:06:06 -04:00
check ( InLayerFormats [ Layer ] = = PF_G16 | | InLayerFormats [ Layer ] = = PF_B8G8R8A8 | | InLayerFormats [ Layer ] = = PF_DXT1 | | InLayerFormats [ Layer ] = = PF_DXT5 | | InLayerFormats [ Layer ] = = PF_BC5
| | InLayerFormats [ Layer ] = = PF_R5G6B5_UNORM | | InLayerFormats [ Layer ] = = PF_B5G5R5A1_UNORM ) ;
2020-04-07 12:40:55 -04:00
LayerFormats [ Layer ] = InLayerFormats [ Layer ] = = PF_G16 ? PF_G16 : PF_B8G8R8A8 ;
LayerOffsets [ Layer ] = TotalSizeBytes ;
TotalSizeBytes + = CalculateImageBytes ( InTileSize , InTileSize , 0 , LayerFormats [ Layer ] ) * InNumTilesX * InNumTilesY ;
}
2019-09-22 16:27:41 -04:00
}
//~ Begin FRenderResource Interface.
virtual void InitRHI ( ) override
{
RenderTargets . Init ( nullptr , NumLayers ) ;
StagingTextures . Init ( nullptr , NumLayers ) ;
for ( int32 Layer = 0 ; Layer < NumLayers ; + + Layer )
{
2022-05-10 17:13:37 -04:00
FRHITextureCreateDesc Desc =
FRHITextureCreateDesc : : Create2D ( TEXT ( " FTileRenderResources " ) , TileSize , TileSize , LayerFormats [ Layer ] ) ;
Desc . SetFlags ( ETextureCreateFlags : : RenderTargetable ) ;
RenderTargets [ Layer ] = RHICreateTexture ( Desc ) ;
Desc . SetFlags ( ETextureCreateFlags : : CPUReadback ) ;
StagingTextures [ Layer ] = RHICreateTexture ( Desc ) ;
2019-09-22 16:27:41 -04:00
}
2023-01-19 06:13:08 -05:00
Fence = RHICreateGPUFence ( TEXT ( " Runtime Virtual Texture Build " ) ) ;
2019-09-22 16:27:41 -04:00
}
virtual void ReleaseRHI ( ) override
{
RenderTargets . Empty ( ) ;
StagingTextures . Empty ( ) ;
Fence . SafeRelease ( ) ;
}
//~ End FRenderResource Interface.
2020-04-07 12:40:55 -04:00
int32 GetNumLayers ( ) const { return NumLayers ; }
2020-09-24 00:43:27 -04:00
int64 GetTotalSizeBytes ( ) const { return TotalSizeBytes ; }
2020-04-07 12:40:55 -04:00
EPixelFormat GetLayerFormat ( int32 Index ) const { return LayerFormats [ Index ] ; }
2020-09-24 00:43:27 -04:00
int64 GetLayerOffset ( int32 Index ) const { return LayerOffsets [ Index ] ; }
2020-04-07 12:40:55 -04:00
2019-09-22 16:27:41 -04:00
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 ;
2020-04-07 12:40:55 -04:00
int32 NumLayers ;
2020-09-24 00:43:27 -04:00
int64 TotalSizeBytes ;
2020-04-07 12:40:55 -04:00
TArray < EPixelFormat > LayerFormats ;
2020-09-24 00:43:27 -04:00
TArray < int64 > LayerOffsets ;
2019-09-22 16:27:41 -04:00
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 >
2020-06-23 18:40:00 -04:00
void TCopyTile ( T * SrcPixels , int32 TileSize , int32 SrcStride , T * DestPixels , int32 DestStride , int32 DestLayerStride , FIntPoint const & DestPos )
2019-09-22 16:27:41 -04:00
{
for ( int32 y = 0 ; y < TileSize ; y + + )
{
memcpy (
2021-01-14 19:42:15 -04:00
DestPixels + ( SIZE_T ) DestStride * ( ( SIZE_T ) DestPos [ 1 ] + ( SIZE_T ) y ) + DestPos [ 0 ] ,
2020-06-23 18:40:00 -04:00
SrcPixels + SrcStride * y ,
2019-09-22 16:27:41 -04:00
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. */
2020-06-23 18:40:00 -04:00
void CopyTile ( void * SrcPixels , int32 TileSize , int32 SrcStride , void * DestPixels , int32 DestStride , int32 DestLayerStride , FIntPoint const & DestPos , EPixelFormat Format )
2019-09-22 16:27:41 -04:00
{
2020-04-07 12:40:55 -04:00
check ( Format = = PF_G16 | | Format = = PF_B8G8R8A8 ) ;
if ( Format = = PF_G16 )
2019-09-22 16:27:41 -04:00
{
2020-06-23 18:40:00 -04:00
TCopyTile ( ( uint16 * ) SrcPixels , TileSize , SrcStride , ( uint16 * ) DestPixels , DestStride , DestLayerStride , DestPos ) ;
2019-09-22 16:27:41 -04:00
}
2020-04-07 12:40:55 -04:00
else if ( Format = = PF_B8G8R8A8 )
2019-09-22 16:27:41 -04:00
{
2020-06-23 18:40:00 -04:00
TCopyTile ( ( FColor * ) SrcPixels , TileSize , SrcStride , ( FColor * ) DestPixels , DestStride , DestLayerStride , DestPos ) ;
2019-09-22 16:27:41 -04:00
}
}
}
2019-08-21 08:57:14 -04:00
namespace RuntimeVirtualTexture
{
2019-08-21 09:38:23 -04:00
bool HasStreamedMips ( URuntimeVirtualTextureComponent * InComponent )
2023-02-23 01:23:57 -05:00
{
EShadingPath ShadingPath = ( InComponent & & InComponent - > GetScene ( ) ) ? InComponent - > GetScene ( ) - > GetShadingPath ( ) : EShadingPath : : Deferred ;
return HasStreamedMips ( ShadingPath , InComponent ) ;
}
bool HasStreamedMips ( EShadingPath ShadingPath , URuntimeVirtualTextureComponent * InComponent )
2019-08-21 08:57:14 -04:00
{
if ( InComponent = = nullptr )
{
return false ;
}
2020-04-14 13:33:06 -04:00
if ( InComponent - > GetVirtualTexture ( ) = = nullptr | | InComponent - > GetStreamingTexture ( ) = = nullptr )
2019-08-21 08:57:14 -04:00
{
return false ;
}
2020-04-15 10:23:37 -04:00
if ( InComponent - > NumStreamingMips ( ) < = 0 )
2019-08-21 08:57:14 -04:00
{
return false ;
}
2023-02-23 01:23:57 -05:00
if ( ShadingPath = = EShadingPath : : Mobile & & ! InComponent - > GetStreamingTexture ( ) - > bSeparateTextureForMobile )
{
return false ;
}
2019-08-21 08:57:14 -04:00
return true ;
}
2023-02-23 01:23:57 -05:00
2019-08-21 09:38:23 -04:00
bool BuildStreamedMips ( URuntimeVirtualTextureComponent * InComponent , ERuntimeVirtualTextureDebugType DebugType )
2019-08-21 08:57:14 -04:00
{
2023-02-23 01:23:57 -05:00
EShadingPath ShadingPath = ( InComponent & & InComponent - > GetScene ( ) ) ? InComponent - > GetScene ( ) - > GetShadingPath ( ) : EShadingPath : : Deferred ;
return BuildStreamedMips ( ShadingPath , InComponent , DebugType ) ;
}
bool BuildStreamedMips ( EShadingPath ShadingPath , URuntimeVirtualTextureComponent * InComponent , ERuntimeVirtualTextureDebugType DebugType )
{
if ( ! HasStreamedMips ( ShadingPath , InComponent ) )
2019-08-21 08:57:14 -04:00
{
return true ;
}
URuntimeVirtualTexture * RuntimeVirtualTexture = InComponent - > GetVirtualTexture ( ) ;
FSceneInterface * Scene = InComponent - > GetScene ( ) ;
const uint32 VirtualTextureSceneIndex = RuntimeVirtualTexture : : GetRuntimeVirtualTextureSceneIndex_GameThread ( InComponent ) ;
2020-06-23 18:40:00 -04:00
const FTransform Transform = InComponent - > GetComponentTransform ( ) ;
2020-03-17 23:19:30 -04:00
const FBox Bounds = InComponent - > Bounds . GetBox ( ) ;
2019-08-21 08:57:14 -04:00
FVTProducerDescription VTDesc ;
2020-09-24 00:43:27 -04:00
RuntimeVirtualTexture - > GetProducerDescription ( VTDesc , URuntimeVirtualTexture : : FInitSettings ( ) , Transform ) ;
2019-08-21 08:57:14 -04:00
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 ) ) ;
2020-04-15 10:23:37 -04:00
const int32 RenderLevel = FMath : : Max ( MaxLevel - InComponent - > NumStreamingMips ( ) + 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 ( ) ;
2020-04-07 12:40:55 -04:00
TArray < EPixelFormat , TInlineAllocator < 4 > > LayerFormats ;
for ( int32 Layer = 0 ; Layer < NumLayers ; + + Layer )
{
LayerFormats . Add ( RuntimeVirtualTexture - > GetLayerFormat ( Layer ) ) ;
}
2019-08-21 08:57:14 -04:00
// Spin up slow task UI
2020-09-01 14:07:48 -04:00
const float TaskWorkRender = NumTilesX * NumTilesY ;
2022-02-28 17:59:11 -05:00
const float TextureBuildTaskMultiplier = 0.25f ;
2020-09-01 14:07:48 -04:00
const float TaskWorkBuildBulkData = TaskWorkRender * TextureBuildTaskMultiplier ;
2020-04-15 10:23:37 -04:00
FScopedSlowTask Task ( TaskWorkRender + TaskWorkBuildBulkData , FText : : AsCultureInvariant ( InComponent - > GetStreamingTexture ( ) - > GetName ( ) ) ) ;
2019-08-21 08:57:14 -04:00
Task . MakeDialog ( true ) ;
// Allocate render targets for rendering out the runtime virtual texture tiles
2020-08-11 01:36:57 -04:00
FTileRenderResources RenderTileResources ( TileSize , NumTilesX , NumTilesY , NumLayers , LayerFormats ) ;
2019-09-22 16:27:41 -04:00
BeginInitResource ( & RenderTileResources ) ;
2019-08-21 08:57:14 -04:00
2022-06-22 20:21:16 -04:00
int64 RenderTileResourcesBytes = RenderTileResources . GetTotalSizeBytes ( ) ;
UE_LOG ( LogVirtualTexturing , Display , TEXT ( " Allocating %uMiB for RenderTileResourcesBytes " ) , ( uint32 ) ( RenderTileResourcesBytes / ( 1024 * 1024 ) ) ) ;
2019-09-22 16:27:41 -04:00
// Final pixels will contain image data for each virtual texture layer in order
TArray64 < uint8 > FinalPixels ;
2022-06-22 20:21:16 -04:00
FinalPixels . SetNumUninitialized ( RenderTileResourcesBytes ) ;
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
{
2020-08-11 01:36:57 -04:00
for ( int32 TileX = 0 ; TileX < NumTilesX ; TileX + + )
{
2019-08-21 08:57:14 -04:00
// Render tile
2020-09-01 14:07:48 -04:00
Task . EnterProgressFrame ( ) ;
2019-08-21 08:57:14 -04:00
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
2020-09-01 14:07:48 -04:00
// Stream textures for this tile. This triggers a render flush internally.
//todo[vt]: Batch groups of streaming locations and render commands to reduce number of flushes.
const FVector StreamingWorldPos = Transform . TransformPosition ( FVector ( UVRange . GetCenter ( ) , 0.5f ) ) ;
IStreamingManager : : Get ( ) . Tick ( 0.f ) ;
2022-07-12 11:18:47 -04:00
IStreamingManager : : Get ( ) . AddViewLocation ( StreamingWorldPos ) ;
2020-09-01 14:07:48 -04:00
IStreamingManager : : Get ( ) . StreamAllResources ( 0 ) ;
2019-09-22 16:27:41 -04:00
ENQUEUE_RENDER_COMMAND ( BakeStreamingTextureTileCommand ) ( [
Scene , VirtualTextureSceneIndex ,
& RenderTileResources ,
MaterialType , NumLayers ,
2020-03-17 23:19:30 -04:00
Transform , Bounds , UVRange ,
2019-09-22 16:27:41 -04:00
RenderLevel , MaxLevel ,
TileX , TileY ,
TileSize , ImageSizeX , ImageSizeY ,
& FinalPixels ,
2020-08-11 01:36:57 -04:00
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 + + )
{
2020-09-24 00:43:27 -04:00
RHICmdList . Transition ( FRHITransitionInfo ( RenderTileResources . GetRenderTarget ( Layer ) , ERHIAccess : : Unknown , ERHIAccess : : RTV ) ) ;
2019-09-22 16:27:41 -04:00
}
2020-10-26 16:44:44 -04:00
{
FRDGBuilder GraphBuilder ( RHICmdList ) ;
2019-08-21 08:57:14 -04:00
2021-01-14 20:15:23 -04:00
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 : : RenderPagesStandAlone ( GraphBuilder , Desc ) ;
GraphBuilder . Execute ( ) ;
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 + + )
{
2021-01-14 20:15:23 -04:00
RHICmdList . Transition ( FRHITransitionInfo ( RenderTileResources . GetRenderTarget ( Layer ) , ERHIAccess : : RTV , ERHIAccess : : CopySrc ) ) ;
2019-09-22 16:27:41 -04:00
RHICmdList . CopyTexture ( RenderTileResources . GetRenderTarget ( Layer ) , RenderTileResources . GetStagingTexture ( Layer ) , FRHICopyTextureInfo ( ) ) ;
}
2022-10-14 22:20:40 -04:00
RenderTileResources . GetFence ( ) - > Clear ( ) ;
2019-09-22 16:27:41 -04:00
RHICmdList . WriteGPUFence ( RenderTileResources . GetFence ( ) ) ;
// Read back tile data and copy into final destination
for ( int32 Layer = 0 ; Layer < NumLayers ; Layer + + )
{
void * TilePixels = nullptr ;
int32 OutWidth , OutHeight ;
2021-01-14 20:15:23 -04:00
RHICmdList . MapStagingSurface ( RenderTileResources . GetStagingTexture ( Layer ) , RenderTileResources . GetFence ( ) , TilePixels , OutWidth , OutHeight ) ;
2019-09-22 16:27:41 -04:00
check ( TilePixels ! = nullptr ) ;
2020-06-23 18:40:00 -04:00
check ( OutHeight = = TileSize ) ;
2019-09-22 16:27:41 -04:00
2020-09-24 00:43:27 -04:00
const int64 LayerOffset = RenderTileResources . GetLayerOffset ( Layer ) ;
2020-04-07 12:40:55 -04:00
const EPixelFormat LayerFormat = RenderTileResources . GetLayerFormat ( Layer ) ;
const FIntPoint DestPos ( TileX * TileSize , TileY * TileSize ) ;
2020-06-23 18:40:00 -04:00
CopyTile ( TilePixels , TileSize , OutWidth , FinalPixels . GetData ( ) + LayerOffset , ImageSizeX , ImageSizeX * ImageSizeY , DestPos , LayerFormat ) ;
2019-09-22 16:27:41 -04:00
RHICmdList . UnmapStagingSurface ( RenderTileResources . GetStagingTexture ( Layer ) ) ;
}
} ) ;
2019-08-21 08:57:14 -04:00
}
}
2020-04-14 13:33:06 -04:00
BeginReleaseResource ( & RenderTileResources ) ;
2020-09-01 14:07:48 -04:00
FlushRenderingCommands ( ) ;
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 ) ;
2020-04-14 13:33:06 -04:00
2023-02-23 01:23:57 -05:00
InComponent - > InitializeStreamingTexture ( ShadingPath , ImageSizeX , ImageSizeY , ( uint8 * ) FinalPixels . GetData ( ) ) ;
2019-08-21 08:57:14 -04:00
return true ;
}
}