2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
/*=============================================================================
RenderTargetPool . cpp : Scene render target pool manager .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "RendererPrivate.h"
# include "ScenePrivate.h"
# include "RenderTargetPool.h"
/** The global render targets pool. */
TGlobalResource < FRenderTargetPool > GRenderTargetPool ;
DEFINE_LOG_CATEGORY_STATIC ( LogRenderTargetPool , Warning , All ) ;
2014-04-23 19:52:11 -04:00
static void DumpRenderTargetPoolMemory ( FOutputDevice & OutputDevice )
{
GRenderTargetPool . DumpMemoryUsage ( OutputDevice ) ;
}
static FAutoConsoleCommandWithOutputDevice GDumpRenderTargetPoolMemoryCmd (
TEXT ( " r.DumpRenderTargetPoolMemory " ) ,
TEXT ( " Dump allocation information for the render target pool. " ) ,
FConsoleCommandWithOutputDeviceDelegate : : CreateStatic ( DumpRenderTargetPoolMemory )
) ;
2014-05-16 18:09:23 -04:00
void RenderTargetPoolEvents ( const TArray < FString > & Args )
2014-04-23 19:52:11 -04:00
{
uint32 SizeInKBThreshold = - 1 ;
if ( Args . Num ( ) & & Args [ 0 ] . IsNumeric ( ) )
{
SizeInKBThreshold = FCString : : Atof ( * Args [ 0 ] ) ;
}
if ( SizeInKBThreshold ! = - 1 )
{
UE_LOG ( LogRenderTargetPool , Display , TEXT ( " r.DumpRenderTargetPoolEvents is now enabled, use r.DumpRenderTargetPoolEvents ? for help " ) ) ;
2014-05-16 17:29:16 -04:00
GRenderTargetPool . EventRecordingSizeThreshold = SizeInKBThreshold ;
GRenderTargetPool . bStartEventRecordingNextTick = true ;
2014-04-23 19:52:11 -04:00
}
else
{
GRenderTargetPool . DisableEventDisplay ( ) ;
UE_LOG ( LogRenderTargetPool , Display , TEXT ( " r.DumpRenderTargetPoolEvents is now disabled, use r.DumpRenderTargetPoolEvents <SizeInKB> to enable or r.DumpRenderTargetPoolEvents ? for help " ) ) ;
}
}
2014-06-25 05:47:33 -04:00
// CVars and commands
2014-04-23 20:00:52 -04:00
static FAutoConsoleCommand GRenderTargetPoolEventsCmd (
TEXT ( " r.RenderTargetPool.Events " ) ,
TEXT ( " Visualize the render target pool events over time in one frame. Optional parameter defines threshold in KB. \n " )
TEXT ( " To disable the view use the command without any parameter " ) ,
FConsoleCommandWithArgsDelegate : : CreateStatic ( RenderTargetPoolEvents )
2014-04-23 19:52:11 -04:00
) ;
2014-06-25 05:47:33 -04:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
static TAutoConsoleVariable < int32 > CVarRenderTargetPoolTest (
TEXT ( " r.RenderTargetPoolTest " ) ,
0 ,
TEXT ( " Clears the texture returned by the rendertarget pool with a special color \n " )
TEXT ( " so we can see better which passes would need to clear. Doesn't work on volume textures and non rendertargets yet. \n " )
TEXT ( " 0:off (default), 1:on " ) ,
ECVF_Cheat | ECVF_RenderThreadSafe ) ;
# endif
2014-04-23 19:52:11 -04:00
bool FRenderTargetPool : : IsEventRecordingEnabled ( ) const
{
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
2014-05-16 17:29:16 -04:00
return bEventRecordingStarted & & bEventRecordingActive ;
2014-04-23 19:52:11 -04:00
# else
return false ;
# endif
}
IPooledRenderTarget * FRenderTargetPoolEvent : : GetValidatedPointer ( ) const
{
int32 Index = GRenderTargetPool . FindIndex ( Pointer ) ;
if ( Index > = 0 )
{
return Pointer ;
}
return 0 ;
}
bool FRenderTargetPoolEvent : : NeedsDeallocEvent ( )
{
if ( GetEventType ( ) = = ERTPE_Alloc )
{
if ( Pointer )
{
IPooledRenderTarget * ValidPointer = GetValidatedPointer ( ) ;
2014-05-06 10:57:40 -04:00
if ( ! ValidPointer | | ValidPointer - > IsFree ( ) )
2014-04-23 19:52:11 -04:00
{
Pointer = 0 ;
return true ;
}
}
}
return false ;
}
2014-03-14 14:13:41 -04:00
static uint32 ComputeSizeInKB ( FPooledRenderTarget & Element )
{
return ( Element . ComputeMemorySize ( ) + 1023 ) / 1024 ;
}
FRenderTargetPool : : FRenderTargetPool ( )
: AllocationLevelInKB ( 0 )
, bCurrentlyOverBudget ( false )
2014-05-16 17:29:16 -04:00
, bStartEventRecordingNextTick ( false )
2014-04-23 20:00:52 -04:00
, EventRecordingSizeThreshold ( 0 )
2014-05-16 17:29:16 -04:00
, bEventRecordingActive ( false )
, bEventRecordingStarted ( false )
2014-04-23 20:00:52 -04:00
, CurrentEventRecordingTime ( 0 )
2014-03-14 14:13:41 -04:00
{
}
2014-09-26 12:37:06 -04:00
static void LogVRamUsage ( FPooledRenderTarget & Ref , FTextureRHIParamRef TexRef )
{
2014-09-29 16:06:34 -04:00
if ( FPlatformProperties : : SupportsFastVRAMMemory ( ) & & TexRef )
2014-09-26 12:37:06 -04:00
{
FRHIResourceInfo Info ;
RHIGetResourceInfo ( TexRef , Info ) ;
if ( Info . VRamAllocation . AllocationSize )
{
// note we do KB for more readable numbers but this can cause quantization loss
UE_LOG ( LogShaders , Log , TEXT ( " VRamInKB(Start/Size):%d/%d %s '%s' " ) ,
Info . VRamAllocation . AllocationStart / 1024 ,
( Info . VRamAllocation . AllocationSize + 1023 ) / 1024 ,
* Ref . GetDesc ( ) . GenerateInfoString ( ) ,
Ref . GetDesc ( ) . DebugName ) ;
}
else
{
UE_LOG ( LogShaders , Log , TEXT ( " VRamInKB request failed %s '%s' " ) ,
* Ref . GetDesc ( ) . GenerateInfoString ( ) ,
Ref . GetDesc ( ) . DebugName ) ;
}
}
}
static void LogVRamUsage ( FPooledRenderTarget & Ref )
{
if ( Ref . GetDesc ( ) . Flags & TexCreate_FastVRAM )
{
LogVRamUsage ( Ref , Ref . GetRenderTargetItem ( ) . TargetableTexture ) ;
2014-10-01 15:25:07 -04:00
if ( Ref . GetRenderTargetItem ( ) . TargetableTexture ! = Ref . GetRenderTargetItem ( ) . ShaderResourceTexture )
{
LogVRamUsage ( Ref , Ref . GetRenderTargetItem ( ) . ShaderResourceTexture ) ;
}
2014-09-26 12:37:06 -04:00
}
}
2014-03-14 14:13:41 -04:00
bool FRenderTargetPool : : FindFreeElement ( const FPooledRenderTargetDesc & Desc , TRefCountPtr < IPooledRenderTarget > & Out , const TCHAR * InDebugName )
{
check ( IsInRenderingThread ( ) ) ;
if ( ! Desc . IsValid ( ) )
{
// no need to do anything
return true ;
}
// if we can keep the current one, do that
if ( Out )
{
FPooledRenderTarget * Current = ( FPooledRenderTarget * ) Out . GetReference ( ) ;
2014-09-26 12:37:06 -04:00
const bool bExactMatch = true ;
if ( Out - > GetDesc ( ) . Compare ( Desc , bExactMatch ) )
2014-03-14 14:13:41 -04:00
{
// we can reuse the same, but the debug name might have changed
Current - > Desc . DebugName = InDebugName ;
RHIBindDebugLabelName ( Current - > GetRenderTargetItem ( ) . TargetableTexture , InDebugName ) ;
check ( ! Out - > IsFree ( ) ) ;
return true ;
}
else
{
// release old reference, it might free a RT we can use
Out = 0 ;
if ( Current - > IsFree ( ) )
{
2014-04-23 19:52:11 -04:00
int32 Index = FindIndex ( Current ) ;
check ( Index > = 0 ) ;
// we don't use Remove() to not shuffle around the elements for better transparency on RenderTargetPoolEvents
PooledRenderTargets [ Index ] = 0 ;
2014-03-14 14:13:41 -04:00
}
}
}
FPooledRenderTarget * Found = 0 ;
2014-04-23 19:52:11 -04:00
uint32 FoundIndex = - 1 ;
2014-03-14 14:13:41 -04:00
// try to find a suitable element in the pool
{
2014-09-26 12:37:06 -04:00
uint32 PassCount = ( Desc . Flags & TexCreate_FastVRAM ) ? 2 : 1 ;
2014-03-14 14:13:41 -04:00
2014-09-26 12:37:06 -04:00
// first we try exact, if that fails we try without TexCreate_FastVRAM
2014-09-26 12:41:57 -04:00
// (easily we can run out of VRam, if this search becomes a performance problem we can optimize or we should use less TexCreate_FastVRAM)
2014-09-26 12:37:06 -04:00
for ( uint32 Pass = 0 ; Pass < PassCount ; + + Pass )
2014-03-14 14:13:41 -04:00
{
2014-09-26 12:37:06 -04:00
bool bExactMatch = ( Pass = = 0 ) ;
for ( uint32 i = 0 , Num = ( uint32 ) PooledRenderTargets . Num ( ) ; i < Num ; + + i )
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
if ( Element & & Element - > IsFree ( ) & & Element - > GetDesc ( ) . Compare ( Desc , bExactMatch ) )
{
Found = Element ;
FoundIndex = i ;
break ;
}
}
2014-03-14 14:13:41 -04:00
}
}
if ( ! Found )
{
UE_LOG ( LogRenderTargetPool , Display , TEXT ( " %d MB, NewRT %s %s " ) , ( AllocationLevelInKB + 1023 ) / 1024 , * Desc . GenerateInfoString ( ) , InDebugName ) ;
// not found in the pool, create a new element
Found = new FPooledRenderTarget ( Desc ) ;
PooledRenderTargets . Add ( Found ) ;
2014-05-06 17:45:03 -04:00
// TexCreate_UAV should be used on Desc.TargetableFlags
check ( ! ( Desc . Flags & TexCreate_UAV ) ) ;
2014-03-14 14:13:41 -04:00
2014-05-20 15:09:51 -04:00
FRHIResourceCreateInfo CreateInfo ;
2014-03-14 14:13:41 -04:00
if ( Desc . TargetableFlags & ( TexCreate_RenderTargetable | TexCreate_DepthStencilTargetable | TexCreate_UAV ) )
{
if ( Desc . Is2DTexture ( ) )
{
RHICreateTargetableShaderResource2D (
Desc . Extent . X ,
Desc . Extent . Y ,
Desc . Format ,
Desc . NumMips ,
Desc . Flags ,
Desc . TargetableFlags ,
Desc . bForceSeparateTargetAndShaderResource ,
2014-05-20 15:09:51 -04:00
CreateInfo ,
2014-03-14 14:13:41 -04:00
( FTexture2DRHIRef & ) Found - > RenderTargetItem . TargetableTexture ,
( FTexture2DRHIRef & ) Found - > RenderTargetItem . ShaderResourceTexture ,
Desc . NumSamples
) ;
2015-01-09 20:56:36 -05:00
if ( Desc . NumMips > 1 )
{
Found - > RenderTargetItem . MipSRVs . SetNum ( Desc . NumMips ) ;
for ( uint16 i = 0 ; i < Desc . NumMips ; i + + )
{
Found - > RenderTargetItem . MipSRVs [ i ] = RHICreateShaderResourceView ( ( FTexture2DRHIRef & ) Found - > RenderTargetItem . ShaderResourceTexture , i ) ;
}
}
2014-03-14 14:13:41 -04:00
}
else if ( Desc . Is3DTexture ( ) )
{
Found - > RenderTargetItem . ShaderResourceTexture = RHICreateTexture3D (
Desc . Extent . X ,
Desc . Extent . Y ,
Desc . Depth ,
Desc . Format ,
Desc . NumMips ,
Desc . TargetableFlags ,
2014-05-20 15:09:51 -04:00
CreateInfo ) ;
2014-03-14 14:13:41 -04:00
// similar to RHICreateTargetableShaderResource2D
Found - > RenderTargetItem . TargetableTexture = Found - > RenderTargetItem . ShaderResourceTexture ;
}
else
{
check ( Desc . IsCubemap ( ) ) ;
if ( Desc . IsArray ( ) )
{
RHICreateTargetableShaderResourceCubeArray (
Desc . Extent . X ,
Desc . ArraySize ,
Desc . Format ,
Desc . NumMips ,
Desc . Flags ,
Desc . TargetableFlags ,
false ,
2014-05-20 15:09:51 -04:00
CreateInfo ,
2014-03-14 14:13:41 -04:00
( FTextureCubeRHIRef & ) Found - > RenderTargetItem . TargetableTexture ,
( FTextureCubeRHIRef & ) Found - > RenderTargetItem . ShaderResourceTexture
) ;
}
else
{
RHICreateTargetableShaderResourceCube (
Desc . Extent . X ,
Desc . Format ,
Desc . NumMips ,
Desc . Flags ,
Desc . TargetableFlags ,
false ,
2014-05-20 15:09:51 -04:00
CreateInfo ,
2014-03-14 14:13:41 -04:00
( FTextureCubeRHIRef & ) Found - > RenderTargetItem . TargetableTexture ,
( FTextureCubeRHIRef & ) Found - > RenderTargetItem . ShaderResourceTexture
) ;
}
}
RHIBindDebugLabelName ( Found - > RenderTargetItem . TargetableTexture , InDebugName ) ;
}
else
{
if ( Desc . Is2DTexture ( ) )
{
// this is useful to get a CPU lockable texture through the same interface
Found - > RenderTargetItem . ShaderResourceTexture = RHICreateTexture2D (
Desc . Extent . X ,
Desc . Extent . Y ,
Desc . Format ,
Desc . NumMips ,
Desc . NumSamples ,
Desc . Flags ,
2014-05-20 15:09:51 -04:00
CreateInfo ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Desc . Is3DTexture ( ) )
{
Found - > RenderTargetItem . ShaderResourceTexture = RHICreateTexture3D (
Desc . Extent . X ,
Desc . Extent . Y ,
Desc . Depth ,
Desc . Format ,
Desc . NumMips ,
Desc . Flags ,
2014-05-20 15:09:51 -04:00
CreateInfo ) ;
2014-03-14 14:13:41 -04:00
}
else
{
check ( Desc . IsCubemap ( ) ) ;
if ( Desc . IsArray ( ) )
{
2014-05-20 15:09:51 -04:00
FTextureCubeRHIRef CubeTexture = RHICreateTextureCubeArray ( Desc . Extent . X , Desc . ArraySize , Desc . Format , Desc . NumMips , Desc . Flags | Desc . TargetableFlags | TexCreate_ShaderResource , CreateInfo ) ;
2014-03-14 14:13:41 -04:00
Found - > RenderTargetItem . TargetableTexture = Found - > RenderTargetItem . ShaderResourceTexture = CubeTexture ;
}
else
{
2014-05-20 15:09:51 -04:00
FTextureCubeRHIRef CubeTexture = RHICreateTextureCube ( Desc . Extent . X , Desc . Format , Desc . NumMips , Desc . Flags | Desc . TargetableFlags | TexCreate_ShaderResource , CreateInfo ) ;
2014-03-14 14:13:41 -04:00
Found - > RenderTargetItem . TargetableTexture = Found - > RenderTargetItem . ShaderResourceTexture = CubeTexture ;
}
}
RHIBindDebugLabelName ( Found - > RenderTargetItem . ShaderResourceTexture , InDebugName ) ;
}
if ( Desc . TargetableFlags & TexCreate_UAV )
{
// The render target desc is invalid if a UAV is requested with an RHI that doesn't support the high-end feature level.
2014-07-24 05:23:50 -04:00
check ( GMaxRHIFeatureLevel = = ERHIFeatureLevel : : SM5 ) ;
2014-03-14 14:13:41 -04:00
Found - > RenderTargetItem . UAV = RHICreateUnorderedAccessView ( Found - > RenderTargetItem . TargetableTexture ) ;
}
AllocationLevelInKB + = ComputeSizeInKB ( * Found ) ;
VerifyAllocationLevel ( ) ;
2014-04-23 19:52:11 -04:00
FoundIndex = PooledRenderTargets . Num ( ) - 1 ;
2014-09-26 12:37:06 -04:00
// done twice but it doesn't hurt an LogVRamUsage gets the new name this way
Found - > Desc . DebugName = InDebugName ;
LogVRamUsage ( * Found ) ;
2014-03-14 14:13:41 -04:00
}
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
{
2014-06-27 11:07:13 -04:00
FRHICommandListImmediate & RHICmdList = FRHICommandListExecutor : : GetImmediateCommandList ( ) ;
2014-06-12 07:13:34 -04:00
2014-06-25 05:47:33 -04:00
if ( CVarRenderTargetPoolTest . GetValueOnRenderThread ( ) )
2014-03-14 14:13:41 -04:00
{
if ( Found - > GetDesc ( ) . TargetableFlags & TexCreate_RenderTargetable )
{
2014-06-12 07:13:34 -04:00
SetRenderTarget ( RHICmdList , Found - > RenderTargetItem . TargetableTexture , FTextureRHIRef ( ) ) ;
RHICmdList . Clear ( true , FLinearColor ( 1000 , 1000 , 1000 , 1000 ) , false , 1.0f , false , 0 , FIntRect ( ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Found - > GetDesc ( ) . TargetableFlags & TexCreate_UAV )
{
const uint32 ZeroClearValue [ 4 ] = { 1000 , 1000 , 1000 , 1000 } ;
2014-06-12 07:13:34 -04:00
RHICmdList . ClearUAV ( Found - > RenderTargetItem . UAV , ZeroClearValue ) ;
2014-03-14 14:13:41 -04:00
}
if ( Desc . TargetableFlags & TexCreate_DepthStencilTargetable )
{
2014-06-12 07:13:34 -04:00
SetRenderTarget ( RHICmdList , FTextureRHIRef ( ) , Found - > RenderTargetItem . TargetableTexture ) ;
RHICmdList . Clear ( false , FLinearColor ( 0 , 0 , 0 , 0 ) , true , 0.0f , false , 0 , FIntRect ( ) ) ;
2014-03-14 14:13:41 -04:00
}
}
}
# endif
check ( Found - > IsFree ( ) ) ;
Found - > Desc . DebugName = InDebugName ;
Found - > UnusedForNFrames = 0 ;
2014-04-23 19:52:11 -04:00
AddAllocEvent ( FoundIndex , Found ) ;
2014-03-14 14:13:41 -04:00
// assign to the reference counted variable
Out = Found ;
check ( ! Found - > IsFree ( ) ) ;
return false ;
}
void FRenderTargetPool : : CreateUntrackedElement ( const FPooledRenderTargetDesc & Desc , TRefCountPtr < IPooledRenderTarget > & Out , const FSceneRenderTargetItem & Item )
{
check ( IsInRenderingThread ( ) ) ;
Out = 0 ;
// not found in the pool, create a new element
FPooledRenderTarget * Found = new FPooledRenderTarget ( Desc ) ;
Found - > RenderTargetItem = Item ;
// assign to the reference counted variable
Out = Found ;
}
void FRenderTargetPool : : GetStats ( uint32 & OutWholeCount , uint32 & OutWholePoolInKB , uint32 & OutUsedInKB ) const
{
OutWholeCount = ( uint32 ) PooledRenderTargets . Num ( ) ;
OutUsedInKB = 0 ;
OutWholePoolInKB = 0 ;
for ( uint32 i = 0 ; i < ( uint32 ) PooledRenderTargets . Num ( ) ; + + i )
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
2014-04-23 19:52:11 -04:00
if ( Element )
2014-03-14 14:13:41 -04:00
{
2014-04-23 19:52:11 -04:00
uint32 SizeInKB = ComputeSizeInKB ( * Element ) ;
OutWholePoolInKB + = SizeInKB ;
if ( ! Element - > IsFree ( ) )
{
OutUsedInKB + = SizeInKB ;
}
2014-03-14 14:13:41 -04:00
}
}
check ( AllocationLevelInKB = = OutWholePoolInKB ) ;
}
2014-04-23 19:52:11 -04:00
void FRenderTargetPool : : AddPhaseEvent ( const TCHAR * InPhaseName )
{
if ( IsEventRecordingEnabled ( ) )
{
AddDeallocEvents ( ) ;
const FString * LastName = GetLastEventPhaseName ( ) ;
if ( ! LastName | | * LastName ! = InPhaseName )
{
2014-04-23 20:00:52 -04:00
if ( CurrentEventRecordingTime )
2014-04-23 19:52:11 -04:00
{
// put a break to former data
2014-04-23 20:00:52 -04:00
+ + CurrentEventRecordingTime ;
2014-04-23 19:52:11 -04:00
}
2014-04-23 20:00:52 -04:00
FRenderTargetPoolEvent NewEvent ( InPhaseName , CurrentEventRecordingTime ) ;
2014-04-23 19:52:11 -04:00
RenderTargetPoolEvents . Add ( NewEvent ) ;
}
}
}
// helper class to get a consistent layout in multiple functions
// MaxX and Y are the output value that can be requested during or after iteration
// Examples usages:
// FRenderTargetPoolEventIterator It(RenderTargetPoolEvents, OptionalStartIndex);
// while(FRenderTargetPoolEvent* Event = It.Iterate()) {}
struct FRenderTargetPoolEventIterator
{
int32 Index ;
TArray < FRenderTargetPoolEvent > & RenderTargetPoolEvents ;
bool bLineContent ;
uint32 TotalWidth ;
int32 Y ;
// constructor
FRenderTargetPoolEventIterator ( TArray < FRenderTargetPoolEvent > & InRenderTargetPoolEvents , int32 InIndex = 0 )
: Index ( InIndex )
, RenderTargetPoolEvents ( InRenderTargetPoolEvents )
, bLineContent ( false )
, TotalWidth ( 1 )
, Y ( 0 )
{
Touch ( ) ;
}
FRenderTargetPoolEvent * operator * ( )
{
if ( Index < RenderTargetPoolEvents . Num ( ) )
{
return & RenderTargetPoolEvents [ Index ] ;
}
return 0 ;
}
// @return 0 if end was reached
FRenderTargetPoolEventIterator & operator + + ( )
{
if ( Index < RenderTargetPoolEvents . Num ( ) )
{
+ + Index ;
}
Touch ( ) ;
return * this ;
}
int32 FindClosingEventY ( ) const
{
FRenderTargetPoolEventIterator It = * this ;
const ERenderTargetPoolEventType StartType = ( * It ) - > GetEventType ( ) ;
if ( StartType = = ERTPE_Alloc )
{
int32 PoolEntryId = RenderTargetPoolEvents [ Index ] . GetPoolEntryId ( ) ;
+ + It ;
// search for next Dealloc of the same PoolEntryId
for ( ; * It ; + + It )
{
FRenderTargetPoolEvent * Event = * It ;
if ( Event - > GetEventType ( ) = = ERTPE_Dealloc & & Event - > GetPoolEntryId ( ) = = PoolEntryId )
{
break ;
}
}
}
else if ( StartType = = ERTPE_Phase )
{
+ + It ;
// search for next Phase
for ( ; * It ; + + It )
{
FRenderTargetPoolEvent * Event = * It ;
if ( Event - > GetEventType ( ) = = ERTPE_Phase )
{
break ;
}
}
}
else
{
check ( 0 ) ;
}
return It . Y ;
}
private :
void Touch ( )
{
if ( Index < RenderTargetPoolEvents . Num ( ) )
{
const FRenderTargetPoolEvent & Event = RenderTargetPoolEvents [ Index ] ;
const ERenderTargetPoolEventType Type = Event . GetEventType ( ) ;
if ( Type = = ERTPE_Alloc )
{
// for now they are all equal width
TotalWidth = FMath : : Max ( TotalWidth , Event . GetColumnX ( ) + Event . GetColumnSize ( ) ) ;
}
Y = Event . GetTimeStep ( ) ;
}
}
} ;
2014-04-23 20:00:52 -04:00
uint32 FRenderTargetPool : : ComputeEventDisplayHeight ( )
2014-04-23 19:52:11 -04:00
{
FRenderTargetPoolEventIterator It ( RenderTargetPoolEvents ) ;
for ( ; * It ; + + It )
{
}
2014-05-06 12:08:01 -04:00
return It . Y ;
2014-04-23 19:52:11 -04:00
}
const FString * FRenderTargetPool : : GetLastEventPhaseName ( )
{
// could be optimized but this is a debug view
// start from the end for better performance
for ( int32 i = RenderTargetPoolEvents . Num ( ) - 1 ; i > = 0 ; - - i )
{
const FRenderTargetPoolEvent * Event = & RenderTargetPoolEvents [ i ] ;
if ( Event - > GetEventType ( ) = = ERTPE_Phase )
{
return & Event - > GetPhaseName ( ) ;
}
}
return 0 ;
}
FRenderTargetPool : : SMemoryStats FRenderTargetPool : : ComputeView ( )
{
SMemoryStats MemoryStats ;
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
{
struct FRTPColumn
{
// index into the column, -1 if this is no valid column
uint32 PoolEntryId ;
// for sorting
uint64 SizeInBytes ;
// for sorting
bool bVRam ;
// default constructor
FRTPColumn ( )
: PoolEntryId ( - 1 )
, SizeInBytes ( 0 )
{
}
// constructor
FRTPColumn ( const FRenderTargetPoolEvent & Event )
: PoolEntryId ( Event . GetPoolEntryId ( ) )
, bVRam ( ( Event . GetDesc ( ) . Flags & TexCreate_FastVRAM ) ! = 0 )
{
2014-04-23 20:00:52 -04:00
SizeInBytes = Event . GetSizeInBytes ( ) ;
2014-04-23 19:52:11 -04:00
}
// sort criteria
bool operator < ( const FRTPColumn & rhs ) const
{
2014-04-23 19:58:00 -04:00
// sort VRam first (only matters on XboxOne but nice to always see it)
// sorting only useful for XboxOne if(bVRam != rhs.bVRam) return bVRam > rhs.bVRam;
2014-04-23 19:52:11 -04:00
// we want the large ones first
return SizeInBytes > rhs . SizeInBytes ;
}
} ;
TArray < FRTPColumn > Colums ;
// generate Colums
for ( int32 i = 0 , Num = RenderTargetPoolEvents . Num ( ) ; i < Num ; i + + )
{
FRenderTargetPoolEvent * Event = & RenderTargetPoolEvents [ i ] ;
if ( Event - > GetEventType ( ) = = ERTPE_Alloc )
{
uint32 PoolEntryId = Event - > GetPoolEntryId ( ) ;
if ( PoolEntryId > = ( uint32 ) Colums . Num ( ) )
{
Colums . SetNum ( PoolEntryId + 1 ) ;
}
Colums [ PoolEntryId ] = FRTPColumn ( * Event ) ;
}
}
Colums . Sort ( ) ;
{
uint32 ColumnX = 0 ;
2015-03-13 10:01:08 -04:00
for ( int32 ColumnIndex = 0 , ColumnsNum = Colums . Num ( ) ; ColumnIndex < ColumnsNum ; + + ColumnIndex )
2014-04-23 19:52:11 -04:00
{
const FRTPColumn & RTPColumn = Colums [ ColumnIndex ] ;
uint32 ColumnSize = RTPColumn . SizeInBytes ;
// hide columns that are too small to make a difference (e.g. <1 MB)
2014-04-23 20:00:52 -04:00
if ( RTPColumn . SizeInBytes < = EventRecordingSizeThreshold * 1024 )
2014-04-23 19:52:11 -04:00
{
ColumnSize = 0 ;
}
else
{
MemoryStats . DisplayedUsageInBytes + = RTPColumn . SizeInBytes ;
// give an entry some size to be more UI friendly (if we get mouse UI for zooming in we might not want that any more)
ColumnSize = FMath : : Max ( ( uint32 ) ( 1024 * 1024 ) , ColumnSize ) ;
}
2014-04-23 20:00:52 -04:00
MemoryStats . TotalColumnSize + = ColumnSize ;
2014-04-23 19:52:11 -04:00
MemoryStats . TotalUsageInBytes + = RTPColumn . SizeInBytes ;
2015-03-13 10:01:08 -04:00
for ( int32 EventIndex = 0 , PoolEventsNum = RenderTargetPoolEvents . Num ( ) ; EventIndex < PoolEventsNum ; EventIndex + + )
2014-04-23 19:52:11 -04:00
{
FRenderTargetPoolEvent * Event = & RenderTargetPoolEvents [ EventIndex ] ;
if ( Event - > GetEventType ( ) ! = ERTPE_Phase )
{
uint32 PoolEntryId = Event - > GetPoolEntryId ( ) ;
if ( RTPColumn . PoolEntryId = = PoolEntryId )
{
Event - > SetColumn ( ColumnIndex , ColumnX , ColumnSize ) ;
}
}
}
ColumnX + = ColumnSize ;
}
}
}
# endif
return MemoryStats ;
}
2014-04-23 20:01:03 -04:00
// draw a single pixel sized rectangle using 4 sub elements
2014-04-23 19:52:11 -04:00
inline void DrawBorder ( FCanvas & Canvas , const FIntRect Rect , FLinearColor Color )
{
// top
Canvas . DrawTile ( Rect . Min . X , Rect . Min . Y , Rect . Max . X - Rect . Min . X , 1 , 0 , 0 , 1 , 1 , Color ) ;
// bottom
Canvas . DrawTile ( Rect . Min . X , Rect . Max . Y - 1 , Rect . Max . X - Rect . Min . X , 1 , 0 , 0 , 1 , 1 , Color ) ;
// left
Canvas . DrawTile ( Rect . Min . X , Rect . Min . Y + 1 , 1 , Rect . Max . Y - Rect . Min . Y - 2 , 0 , 0 , 1 , 1 , Color ) ;
// right
Canvas . DrawTile ( Rect . Max . X - 1 , Rect . Min . Y + 1 , 1 , Rect . Max . Y - Rect . Min . Y - 2 , 0 , 0 , 1 , 1 , Color ) ;
}
2014-08-28 06:22:54 -04:00
void FRenderTargetPool : : PresentContent ( FRHICommandListImmediate & RHICmdList , const FViewInfo & View )
2014-04-23 19:52:11 -04:00
{
2014-06-12 07:13:34 -04:00
if ( RenderTargetPoolEvents . Num ( ) )
2014-04-23 19:52:11 -04:00
{
2014-05-06 12:08:01 -04:00
AddPhaseEvent ( TEXT ( " FrameEnd " ) ) ;
2014-04-23 19:52:11 -04:00
FIntPoint DisplayLeftTop ( 20 , 50 ) ;
// on the right we leave more space to make the mouse tooltip readable
FIntPoint DisplayExtent ( View . ViewRect . Width ( ) - DisplayLeftTop . X * 2 - 140 , View . ViewRect . Height ( ) - DisplayLeftTop . Y * 2 ) ;
// if the area is not too small
if ( DisplayExtent . X > 50 & & DisplayExtent . Y > 50 )
{
SMemoryStats MemoryStats = ComputeView ( ) ;
2014-06-12 07:13:34 -04:00
SetRenderTarget ( RHICmdList , View . Family - > RenderTarget - > GetRenderTargetTexture ( ) , FTextureRHIRef ( ) ) ;
RHICmdList . SetViewport ( 0 , 0 , 0.0f , GSceneRenderTargets . GetBufferSizeXY ( ) . X , GSceneRenderTargets . GetBufferSizeXY ( ) . Y , 1.0f ) ;
2014-04-23 19:52:11 -04:00
2014-06-12 07:13:34 -04:00
RHICmdList . SetBlendState ( TStaticBlendState < > : : GetRHI ( ) ) ;
RHICmdList . SetRasterizerState ( TStaticRasterizerState < > : : GetRHI ( ) ) ;
RHICmdList . SetDepthStencilState ( TStaticDepthStencilState < false , CF_Always > : : GetRHI ( ) ) ;
2014-04-23 19:52:11 -04:00
// this is a helper class for FCanvas to be able to get screen size
class FRenderTargetTemp : public FRenderTarget
{
public :
const FSceneView & View ;
FRenderTargetTemp ( const FSceneView & InView ) : View ( InView )
{
}
virtual FIntPoint GetSizeXY ( ) const
{
return View . UnscaledViewRect . Size ( ) ;
} ;
virtual const FTexture2DRHIRef & GetRenderTargetTexture ( ) const
{
return View . Family - > RenderTarget - > GetRenderTargetTexture ( ) ;
}
} TempRenderTarget ( View ) ;
2014-08-19 10:41:34 -04:00
FCanvas Canvas ( & TempRenderTarget , NULL , View . Family - > CurrentRealTime , View . Family - > CurrentWorldTime , View . Family - > DeltaWorldTime , View . GetFeatureLevel ( ) ) ;
2014-04-23 19:52:11 -04:00
// TinyFont property
const int32 FontHeight = 12 ;
FIntPoint MousePos = View . CursorPos ;
FLinearColor BackgroundColor = FLinearColor ( 0.0f , 0.0f , 0.0f , 0.7f ) ;
FLinearColor PhaseColor = FLinearColor ( 0.2f , 0.1f , 0.05f , 0.8f ) ;
FLinearColor ElementColor = FLinearColor ( 0.3f , 0.3f , 0.3f , 0.9f ) ;
FLinearColor ElementColorVRam = FLinearColor ( 0.4f , 0.25f , 0.25f , 0.9f ) ;
UTexture2D * GradientTexture = UCanvas : : StaticClass ( ) - > GetDefaultObject < UCanvas > ( ) - > GradientTexture0 ;
// background rectangle
Canvas . DrawTile ( DisplayLeftTop . X , DisplayLeftTop . Y - 1 * FontHeight - 1 , DisplayExtent . X , DisplayExtent . Y + FontHeight , 0 , 0 , 1 , 1 , BackgroundColor ) ;
{
uint32 MB = 1024 * 1024 ;
uint32 MBm1 = MB - 1 ;
FString Headline = * FString : : Printf ( TEXT ( " RenderTargetPool elements(x) over time(y) >= %dKB, Displayed/Total:%d/%dMB " ) ,
2014-04-23 20:00:52 -04:00
EventRecordingSizeThreshold ,
2014-04-23 19:52:11 -04:00
( uint32 ) ( ( MemoryStats . DisplayedUsageInBytes + MBm1 ) / MB ) ,
( uint32 ) ( ( MemoryStats . TotalUsageInBytes + MBm1 ) / MB ) ) ;
Canvas . DrawShadowedString ( DisplayLeftTop . X , DisplayLeftTop . Y - 1 * FontHeight - 1 , * Headline , GEngine - > GetTinyFont ( ) , FLinearColor ( 1 , 1 , 1 ) ) ;
}
2014-04-23 20:00:52 -04:00
uint32 EventDisplayHeight = ComputeEventDisplayHeight ( ) ;
float ScaleX = DisplayExtent . X / ( float ) MemoryStats . TotalColumnSize ;
float ScaleY = DisplayExtent . Y / ( float ) EventDisplayHeight ;
2014-04-23 19:52:11 -04:00
// 0 if none
FRenderTargetPoolEvent * HighlightedEvent = 0 ;
FIntRect HighlightedRect ;
// Phase events
for ( FRenderTargetPoolEventIterator It ( RenderTargetPoolEvents ) ; * It ; + + It )
{
FRenderTargetPoolEvent * Event = * It ;
if ( Event - > GetEventType ( ) = = ERTPE_Phase )
{
int32 Y0 = It . Y ;
int32 Y1 = It . FindClosingEventY ( ) ;
FIntPoint PixelLeftTop ( ( int32 ) ( DisplayLeftTop . X ) , ( int32 ) ( DisplayLeftTop . Y + ScaleY * Y0 ) ) ;
FIntPoint PixelRightBottom ( ( int32 ) ( DisplayLeftTop . X + DisplayExtent . X ) , ( int32 ) ( DisplayLeftTop . Y + ScaleY * Y1 ) ) ;
bool bHighlight = MousePos . X > = PixelLeftTop . X & & MousePos . X < PixelRightBottom . X & & MousePos . Y > = PixelLeftTop . Y & & MousePos . Y < = PixelRightBottom . Y ;
if ( bHighlight )
{
HighlightedEvent = Event ;
HighlightedRect = FIntRect ( PixelLeftTop , PixelRightBottom ) ;
}
// UMax is 0.9f to avoid getting some wrap texture leaking in at the bottom
Canvas . DrawTile ( PixelLeftTop . X , PixelLeftTop . Y , PixelRightBottom . X - PixelLeftTop . X , PixelRightBottom . Y - PixelLeftTop . Y , 0 , 0 , 1 , 0.9f , PhaseColor , GradientTexture - > Resource ) ;
}
}
// Alloc / Dealloc events
for ( FRenderTargetPoolEventIterator It ( RenderTargetPoolEvents ) ; * It ; + + It )
{
FRenderTargetPoolEvent * Event = * It ;
if ( Event - > GetEventType ( ) = = ERTPE_Alloc & & Event - > GetColumnSize ( ) )
{
int32 Y0 = It . Y ;
int32 Y1 = It . FindClosingEventY ( ) ;
int32 X0 = Event - > GetColumnX ( ) ;
// for now they are all equal width
int32 X1 = X0 + Event - > GetColumnSize ( ) ;
FIntPoint PixelLeftTop ( ( int32 ) ( DisplayLeftTop . X + ScaleX * X0 ) , ( int32 ) ( DisplayLeftTop . Y + ScaleY * Y0 ) ) ;
FIntPoint PixelRightBottom ( ( int32 ) ( DisplayLeftTop . X + ScaleX * X1 ) , ( int32 ) ( DisplayLeftTop . Y + ScaleY * Y1 ) ) ;
bool bHighlight = MousePos . X > = PixelLeftTop . X & & MousePos . X < PixelRightBottom . X & & MousePos . Y > = PixelLeftTop . Y & & MousePos . Y < = PixelRightBottom . Y ;
if ( bHighlight )
{
HighlightedEvent = Event ;
HighlightedRect = FIntRect ( PixelLeftTop , PixelRightBottom ) ;
}
FLinearColor Color = ElementColor ;
// Highlight EDRAM/FastVRAM usage
if ( Event - > GetDesc ( ) . Flags & TexCreate_FastVRAM )
{
Color = ElementColorVRam ;
}
Canvas . DrawTile (
PixelLeftTop . X , PixelLeftTop . Y ,
PixelRightBottom . X - PixelLeftTop . X - 1 , PixelRightBottom . Y - PixelLeftTop . Y - 1 ,
0 , 0 , 1 , 1 , Color ) ;
}
}
if ( HighlightedEvent )
{
DrawBorder ( Canvas , HighlightedRect , FLinearColor ( 0.8f , 0 , 0 , 0.5f ) ) ;
// Offset to not intersect with crosshair (in editor) or arrow (in game).
FIntPoint Pos = MousePos + FIntPoint ( 12 , 4 ) ;
if ( HighlightedEvent - > GetEventType ( ) = = ERTPE_Phase )
{
2014-05-06 15:49:36 -04:00
FString PhaseText = * FString : : Printf ( TEXT ( " Phase: %s " ) , * HighlightedEvent - > GetPhaseName ( ) ) ;
Canvas . DrawShadowedString ( Pos . X , Pos . Y + 0 * FontHeight , * PhaseText , GEngine - > GetTinyFont ( ) , FLinearColor ( 0.5f , 0.5f , 1 ) ) ;
2014-04-23 19:52:11 -04:00
}
else
{
FString SizeString = FString : : Printf ( TEXT ( " %d KB " ) , ( HighlightedEvent - > GetSizeInBytes ( ) + 1024 ) / 1024 ) ;
Canvas . DrawShadowedString ( Pos . X , Pos . Y + 0 * FontHeight , HighlightedEvent - > GetDesc ( ) . DebugName , GEngine - > GetTinyFont ( ) , FLinearColor ( 1 , 1 , 0 ) ) ;
Canvas . DrawShadowedString ( Pos . X , Pos . Y + 1 * FontHeight , * HighlightedEvent - > GetDesc ( ) . GenerateInfoString ( ) , GEngine - > GetTinyFont ( ) , FLinearColor ( 1 , 1 , 0 ) ) ;
Canvas . DrawShadowedString ( Pos . X , Pos . Y + 2 * FontHeight , * SizeString , GEngine - > GetTinyFont ( ) , FLinearColor ( 1 , 1 , 0 ) ) ;
}
}
2014-07-02 14:13:59 -04:00
Canvas . Flush_RenderThread ( RHICmdList ) ;
2014-04-23 20:00:52 -04:00
CurrentEventRecordingTime = 0 ;
RenderTargetPoolEvents . Empty ( ) ;
2014-04-23 19:52:11 -04:00
}
}
2014-06-12 07:13:34 -04:00
VisualizeTexture . PresentContent ( RHICmdList , View ) ;
2014-04-23 19:52:11 -04:00
}
void FRenderTargetPool : : AddDeallocEvents ( )
{
check ( IsInRenderingThread ( ) ) ;
bool bWorkWasDone = false ;
for ( uint32 i = 0 , Num = ( uint32 ) RenderTargetPoolEvents . Num ( ) ; i < Num ; + + i )
{
FRenderTargetPoolEvent & Event = RenderTargetPoolEvents [ i ] ;
if ( Event . NeedsDeallocEvent ( ) )
{
2014-04-23 20:00:52 -04:00
FRenderTargetPoolEvent NewEvent ( Event . GetPoolEntryId ( ) , CurrentEventRecordingTime ) ;
2014-04-23 19:52:11 -04:00
// for convenience - is actually redundant
NewEvent . SetDesc ( Event . GetDesc ( ) ) ;
RenderTargetPoolEvents . Add ( NewEvent ) ;
bWorkWasDone = true ;
}
}
if ( bWorkWasDone )
{
2014-04-23 20:00:52 -04:00
+ + CurrentEventRecordingTime ;
2014-04-23 19:52:11 -04:00
}
}
void FRenderTargetPool : : AddAllocEvent ( uint32 InPoolEntryId , FPooledRenderTarget * In )
{
check ( In ) ;
if ( IsEventRecordingEnabled ( ) )
{
AddDeallocEvents ( ) ;
check ( IsInRenderingThread ( ) ) ;
2014-04-23 20:00:52 -04:00
FRenderTargetPoolEvent NewEvent ( InPoolEntryId , CurrentEventRecordingTime + + , In ) ;
2014-04-23 19:52:11 -04:00
RenderTargetPoolEvents . Add ( NewEvent ) ;
}
}
void FRenderTargetPool : : AddAllocEventsFromCurrentState ( )
{
if ( ! IsEventRecordingEnabled ( ) )
{
return ;
}
check ( IsInRenderingThread ( ) ) ;
bool bWorkWasDone = false ;
for ( uint32 i = 0 ; i < ( uint32 ) PooledRenderTargets . Num ( ) ; + + i )
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
if ( Element & & ! Element - > IsFree ( ) )
{
2014-04-23 20:00:52 -04:00
FRenderTargetPoolEvent NewEvent ( i , CurrentEventRecordingTime , Element ) ;
2014-04-23 19:52:11 -04:00
RenderTargetPoolEvents . Add ( NewEvent ) ;
bWorkWasDone = true ;
}
}
if ( bWorkWasDone )
{
2014-04-23 20:00:52 -04:00
+ + CurrentEventRecordingTime ;
2014-04-23 19:52:11 -04:00
}
}
2014-03-14 14:13:41 -04:00
void FRenderTargetPool : : TickPoolElements ( )
{
check ( IsInRenderingThread ( ) ) ;
2014-05-16 17:29:16 -04:00
if ( bStartEventRecordingNextTick )
2014-04-23 19:52:11 -04:00
{
2014-05-16 17:29:16 -04:00
bStartEventRecordingNextTick = false ;
bEventRecordingStarted = true ;
2014-04-23 19:52:11 -04:00
}
2014-03-14 14:13:41 -04:00
uint32 MinimumPoolSizeInKB ;
{
static const auto CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.RenderTargetPoolMin " ) ) ;
MinimumPoolSizeInKB = FMath : : Clamp ( CVar - > GetValueOnRenderThread ( ) , 0 , 2000 ) * 1024 ;
}
2014-04-23 19:58:00 -04:00
CompactPool ( ) ;
2014-03-14 14:13:41 -04:00
for ( uint32 i = 0 ; i < ( uint32 ) PooledRenderTargets . Num ( ) ; + + i )
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
2014-04-23 19:52:11 -04:00
if ( Element )
{
Element - > OnFrameStart ( ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-04-23 19:52:11 -04:00
2014-03-14 14:13:41 -04:00
// we need to release something, take the oldest ones first
while ( AllocationLevelInKB > MinimumPoolSizeInKB )
{
// -1: not set
int32 OldestElementIndex = - 1 ;
// find oldest element we can remove
2014-04-23 19:52:11 -04:00
for ( uint32 i = 0 , Num = ( uint32 ) PooledRenderTargets . Num ( ) ; i < Num ; + + i )
2014-03-14 14:13:41 -04:00
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
2014-04-23 19:52:11 -04:00
if ( Element & & Element - > UnusedForNFrames > 2 )
2014-03-14 14:13:41 -04:00
{
if ( OldestElementIndex ! = - 1 )
{
if ( PooledRenderTargets [ OldestElementIndex ] - > UnusedForNFrames < Element - > UnusedForNFrames )
{
OldestElementIndex = i ;
}
}
else
{
OldestElementIndex = i ;
}
}
}
if ( OldestElementIndex ! = - 1 )
{
AllocationLevelInKB - = ComputeSizeInKB ( * PooledRenderTargets [ OldestElementIndex ] ) ;
// we assume because of reference counting the resource gets released when not needed any more
2014-04-23 19:52:11 -04:00
// we don't use Remove() to not shuffle around the elements for better transparency on RenderTargetPoolEvents
PooledRenderTargets [ OldestElementIndex ] = 0 ;
2014-03-14 14:13:41 -04:00
VerifyAllocationLevel ( ) ;
}
else
{
// There is no element we can remove but we are over budget, better we log that.
// Options:
// * Increase the pool
// * Reduce rendering features or resolution
// * Investigate allocations, order or reusing other render targets can help
// * Ignore (editor case, might start using slow memory which can be ok)
if ( ! bCurrentlyOverBudget )
{
UE_LOG ( LogRenderTargetPool , Warning , TEXT ( " r.RenderTargetPoolMin exceeded %d/%d MB (ok in editor, bad on fixed memory platform) " ) , ( AllocationLevelInKB + 1023 ) / 1024 , MinimumPoolSizeInKB / 1024 ) ;
bCurrentlyOverBudget = true ;
}
// at this point we need to give up
break ;
}
/*
// confused more than it helps (often a name is used on two elements in the pool and some pool elements are not rendered to this frame)
else
{
// initial state of a render target (e.g. Velocity@0)
GRenderTargetPool . VisualizeTexture . SetCheckPoint ( Element ) ;
*/ }
if ( AllocationLevelInKB < = MinimumPoolSizeInKB )
{
if ( bCurrentlyOverBudget )
{
UE_LOG ( LogRenderTargetPool , Display , TEXT ( " r.RenderTargetPoolMin resolved %d/%d MB " ) , ( AllocationLevelInKB + 1023 ) / 1024 , MinimumPoolSizeInKB / 1024 ) ;
bCurrentlyOverBudget = false ;
}
}
2014-04-23 19:52:11 -04:00
// CompactEventArray();
AddPhaseEvent ( TEXT ( " FromLastFrame " ) ) ;
AddAllocEventsFromCurrentState ( ) ;
AddPhaseEvent ( TEXT ( " Rendering " ) ) ;
}
int32 FRenderTargetPool : : FindIndex ( IPooledRenderTarget * In ) const
{
check ( IsInRenderingThread ( ) ) ;
if ( In )
{
for ( uint32 i = 0 , Num = ( uint32 ) PooledRenderTargets . Num ( ) ; i < Num ; + + i )
{
const FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
if ( Element = = In )
{
return i ;
}
}
}
// not found
return - 1 ;
2014-03-14 14:13:41 -04:00
}
void FRenderTargetPool : : FreeUnusedResource ( TRefCountPtr < IPooledRenderTarget > & In )
{
check ( IsInRenderingThread ( ) ) ;
2014-04-23 19:52:11 -04:00
int32 Index = FindIndex ( In ) ;
2014-03-14 14:13:41 -04:00
2014-04-23 19:52:11 -04:00
if ( Index ! = - 1 )
2014-03-14 14:13:41 -04:00
{
2014-04-23 19:52:11 -04:00
FPooledRenderTarget * Element = PooledRenderTargets [ Index ] ;
2014-03-14 14:13:41 -04:00
2014-04-23 19:52:11 -04:00
if ( Element )
2014-03-14 14:13:41 -04:00
{
AllocationLevelInKB - = ComputeSizeInKB ( * Element ) ;
// we assume because of reference counting the resource gets released when not needed any more
2014-04-23 19:52:11 -04:00
// we don't use Remove() to not shuffle around the elements for better transparency on RenderTargetPoolEvents
PooledRenderTargets [ Index ] = 0 ;
In . SafeRelease ( ) ;
VerifyAllocationLevel ( ) ;
2014-03-14 14:13:41 -04:00
}
}
}
void FRenderTargetPool : : FreeUnusedResources ( )
{
check ( IsInRenderingThread ( ) ) ;
2014-04-23 19:52:11 -04:00
for ( uint32 i = 0 , Num = ( uint32 ) PooledRenderTargets . Num ( ) ; i < Num ; + + i )
2014-03-14 14:13:41 -04:00
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
2014-04-23 19:52:11 -04:00
if ( Element & & Element - > IsFree ( ) )
2014-03-14 14:13:41 -04:00
{
AllocationLevelInKB - = ComputeSizeInKB ( * Element ) ;
// we assume because of reference counting the resource gets released when not needed any more
2014-04-23 19:52:11 -04:00
// we don't use Remove() to not shuffle around the elements for better transparency on RenderTargetPoolEvents
PooledRenderTargets [ i ] = 0 ;
2014-03-14 14:13:41 -04:00
}
}
VerifyAllocationLevel ( ) ;
}
void FRenderTargetPool : : DumpMemoryUsage ( FOutputDevice & OutputDevice )
{
OutputDevice . Logf ( TEXT ( " Pooled Render Targets: " ) ) ;
for ( int32 i = 0 ; i < PooledRenderTargets . Num ( ) ; + + i )
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
2014-04-23 19:52:11 -04:00
if ( Element )
{
OutputDevice . Logf (
TEXT ( " %6.3fMB %4dx%4d%s%s %2dmip(s) %s (%s) " ) ,
ComputeSizeInKB ( * Element ) / 1024.0f ,
Element - > Desc . Extent . X ,
Element - > Desc . IsCubemap ( ) ? Element - > Desc . Extent . X : Element - > Desc . Extent . Y ,
Element - > Desc . Depth > 1 ? * FString : : Printf ( TEXT ( " x%3d " ) , Element - > Desc . Depth ) : ( Element - > Desc . IsCubemap ( ) ? TEXT ( " cube " ) : TEXT ( " " ) ) ,
Element - > Desc . bIsArray ? * FString : : Printf ( TEXT ( " [%3d] " ) , Element - > Desc . ArraySize ) : TEXT ( " " ) ,
Element - > Desc . NumMips ,
Element - > Desc . DebugName ,
GPixelFormats [ Element - > Desc . Format ] . Name
) ;
}
2014-03-14 14:13:41 -04:00
}
uint32 NumTargets = 0 ;
uint32 UsedKB = 0 ;
uint32 PoolKB = 0 ;
GetStats ( NumTargets , PoolKB , UsedKB ) ;
OutputDevice . Logf ( TEXT ( " %.3fMB total, %.3fMB used, %d render targets " ) , PoolKB / 1024.f , UsedKB / 1024.f , NumTargets ) ;
}
uint32 FPooledRenderTarget : : AddRef ( ) const
{
2015-01-14 10:56:44 -05:00
check ( IsInRenderingThread ( ) ) ;
2014-03-14 14:13:41 -04:00
return uint32 ( + + NumRefs ) ;
}
uint32 FPooledRenderTarget : : Release ( ) const
{
2015-01-14 10:56:44 -05:00
check ( IsInRenderingThread ( ) ) ;
2014-03-14 14:13:41 -04:00
uint32 Refs = uint32 ( - - NumRefs ) ;
if ( Refs = = 0 )
{
// better we remove const from Release()
FSceneRenderTargetItem & NonConstItem = ( FSceneRenderTargetItem & ) RenderTargetItem ;
NonConstItem . SafeRelease ( ) ;
delete this ;
}
return Refs ;
}
uint32 FPooledRenderTarget : : GetRefCount ( ) const
{
return uint32 ( NumRefs ) ;
}
void FPooledRenderTarget : : SetDebugName ( const TCHAR * InName )
{
check ( InName ) ;
Desc . DebugName = InName ;
}
const FPooledRenderTargetDesc & FPooledRenderTarget : : GetDesc ( ) const
{
return Desc ;
}
void FRenderTargetPool : : ReleaseDynamicRHI ( )
{
check ( IsInRenderingThread ( ) ) ;
PooledRenderTargets . Empty ( ) ;
}
// for debugging purpose
FPooledRenderTarget * FRenderTargetPool : : GetElementById ( uint32 Id ) const
{
// is used in game and render thread
if ( Id > = ( uint32 ) PooledRenderTargets . Num ( ) )
{
return 0 ;
}
return PooledRenderTargets [ Id ] ;
}
void FRenderTargetPool : : VerifyAllocationLevel ( ) const
{
/*
// to verify internal consistency
uint32 OutWholeCount ;
uint32 OutWholePoolInKB ;
uint32 OutUsedInKB ;
GetStats ( OutWholeCount , OutWholePoolInKB , OutUsedInKB ) ;
*/
}
2014-04-23 19:52:11 -04:00
void FRenderTargetPool : : CompactPool ( )
{
for ( uint32 i = 0 , Num = ( uint32 ) PooledRenderTargets . Num ( ) ; i < Num ; + + i )
{
FPooledRenderTarget * Element = PooledRenderTargets [ i ] ;
if ( ! Element )
{
PooledRenderTargets . RemoveAtSwap ( i ) ;
- - Num ;
}
}
}
2014-03-14 14:13:41 -04:00
bool FPooledRenderTarget : : OnFrameStart ( )
{
check ( IsInRenderingThread ( ) ) ;
// If there are any references to the pooled render target other than the pool itself, then it may not be freed.
if ( ! IsFree ( ) )
{
check ( ! UnusedForNFrames ) ;
return false ;
}
+ + UnusedForNFrames ;
// this logic can be improved
if ( UnusedForNFrames > 10 )
{
// release
return true ;
}
return false ;
}
uint32 FPooledRenderTarget : : ComputeMemorySize ( ) const
{
uint32 Size = 0 ;
if ( Desc . Is2DTexture ( ) )
{
Size + = RHIComputeMemorySize ( ( const FTexture2DRHIRef & ) RenderTargetItem . TargetableTexture ) ;
if ( RenderTargetItem . ShaderResourceTexture ! = RenderTargetItem . TargetableTexture )
{
Size + = RHIComputeMemorySize ( ( const FTexture2DRHIRef & ) RenderTargetItem . ShaderResourceTexture ) ;
}
}
else if ( Desc . Is3DTexture ( ) )
{
Size + = RHIComputeMemorySize ( ( const FTexture3DRHIRef & ) RenderTargetItem . TargetableTexture ) ;
if ( RenderTargetItem . ShaderResourceTexture ! = RenderTargetItem . TargetableTexture )
{
Size + = RHIComputeMemorySize ( ( const FTexture3DRHIRef & ) RenderTargetItem . ShaderResourceTexture ) ;
}
}
else
{
Size + = RHIComputeMemorySize ( ( const FTextureCubeRHIRef & ) RenderTargetItem . TargetableTexture ) ;
if ( RenderTargetItem . ShaderResourceTexture ! = RenderTargetItem . TargetableTexture )
{
Size + = RHIComputeMemorySize ( ( const FTextureCubeRHIRef & ) RenderTargetItem . ShaderResourceTexture ) ;
}
}
return Size ;
}
bool FPooledRenderTarget : : IsFree ( ) const
{
check ( GetRefCount ( ) > = 1 ) ;
// If the only reference to the pooled render target is from the pool, then it's unused.
return GetRefCount ( ) = = 1 ;
}