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
/*=============================================================================
RenderingCompositionGraph . cpp : Scene pass order and dependency system .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "RendererPrivate.h"
# include "RenderingCompositionGraph.h"
2014-04-02 18:09:23 -04:00
# include "HighResScreenshot.h"
2014-03-14 14:13:41 -04:00
2015-07-07 13:58:55 -04:00
void ExecuteCompositionGraphDebug ( ) ;
static TAutoConsoleVariable < int32 > CVarCompositionGraphOrder (
TEXT ( " r.CompositionGraphOrder " ) ,
2015-07-07 14:09:58 -04:00
1 ,
2015-07-07 13:58:55 -04:00
TEXT ( " Defines in which order the nodes in the CompositionGraph are executed (affects postprocess and some lighting). \n " )
TEXT ( " Option 1 provides more control, which can be useful for preserving ESRAM, avoid GPU sync, cluster up compute shaders for performance and control AsyncCompute. \n " )
TEXT ( " 0: tree order starting with the root, first all inputs then dependencies (classic UE4, unconnected nodes are not getting executed) \n " )
TEXT ( " 1: RegisterPass() call order, unless the dependencies (input and additional) require a different order (might become new default as it provides more control, executes all registered nodes) " ) ,
ECVF_RenderThreadSafe ) ;
# if !UE_BUILD_SHIPPING
FAutoConsoleCommand CmdCompositionGraphDebug (
TEXT ( " r.CompositionGraphDebug " ) ,
TEXT ( " Execute this command to get a single frame dump of the composition graph of one frame (post processing and lighting). " ) ,
FConsoleCommandDelegate : : CreateStatic ( ExecuteCompositionGraphDebug )
) ;
# endif
2014-03-14 14:13:41 -04:00
// render thread, 0:off, >0 next n frames should be debugged
uint32 GDebugCompositionGraphFrames = 0 ;
class FGMLFileWriter
{
public :
// constructor
FGMLFileWriter ( )
: GMLFile ( 0 )
{
}
void OpenGMLFile ( const TCHAR * Name )
{
# if !UE_BUILD_SHIPPING
// todo: do we need to create the directory?
FString FilePath = FPaths : : ScreenShotDir ( ) + TEXT ( " / " ) + Name + TEXT ( " .gml " ) ;
GMLFile = IFileManager : : Get ( ) . CreateDebugFileWriter ( * FilePath ) ;
# endif
}
void CloseGMLFile ( )
{
# if !UE_BUILD_SHIPPING
if ( GMLFile )
{
delete GMLFile ;
GMLFile = 0 ;
}
# endif
}
// .GML file is to visualize the post processing graph as a 2d graph
void WriteLine ( const char * Line )
{
# if !UE_BUILD_SHIPPING
if ( GMLFile )
{
GMLFile - > Serialize ( ( void * ) Line , FCStringAnsi : : Strlen ( Line ) ) ;
GMLFile - > Serialize ( ( void * ) " \r \n " , 2 ) ;
}
# endif
}
private :
FArchive * GMLFile ;
} ;
FGMLFileWriter GGMLFileWriter ;
bool ShouldDebugCompositionGraph ( )
{
# if !UE_BUILD_SHIPPING
return GDebugCompositionGraphFrames > 0 ;
# else
return false ;
# endif
}
void ExecuteCompositionGraphDebug ( )
{
ENQUEUE_UNIQUE_RENDER_COMMAND (
StartDebugCompositionGraph ,
{
GDebugCompositionGraphFrames = 1 ;
}
) ;
}
// main thread
void CompositionGraph_OnStartFrame ( )
{
# if !UE_BUILD_SHIPPING
ENQUEUE_UNIQUE_RENDER_COMMAND (
DebugCompositionGraphDec ,
{
if ( GDebugCompositionGraphFrames )
{
- - GDebugCompositionGraphFrames ;
}
}
) ;
# endif
}
2014-08-12 18:24:52 -04:00
FRenderingCompositePassContext : : FRenderingCompositePassContext ( FRHICommandListImmediate & InRHICmdList , FViewInfo & InView /*, const FSceneRenderTargetItem& InRenderTargetItem*/ )
2014-03-14 14:13:41 -04:00
: View ( InView )
2014-08-12 18:24:52 -04:00
, ViewState ( ( FSceneViewState * ) InView . State )
2014-03-14 14:13:41 -04:00
, Pass ( 0 )
2014-06-27 11:07:13 -04:00
, RHICmdList ( InRHICmdList )
2014-03-14 14:13:41 -04:00
, ViewPortRect ( 0 , 0 , 0 , 0 )
2014-08-28 06:22:54 -04:00
, FeatureLevel ( View . GetFeatureLevel ( ) )
, ShaderMap ( InView . ShaderMap )
2015-07-07 13:58:55 -04:00
, bWasProcessed ( false )
2014-03-14 14:13:41 -04:00
{
check ( ! IsViewportValid ( ) ) ;
}
FRenderingCompositePassContext : : ~ FRenderingCompositePassContext ( )
{
Graph . Free ( ) ;
}
2015-05-11 13:36:07 -04:00
void FRenderingCompositePassContext : : Process ( FRenderingCompositePass * Root , const TCHAR * GraphDebugName )
2014-03-14 14:13:41 -04:00
{
2015-07-07 13:58:55 -04:00
// call this method only once afetr the graph is finished
check ( ! bWasProcessed ) ;
bWasProcessed = true ;
2014-03-14 14:13:41 -04:00
if ( Root )
{
if ( ShouldDebugCompositionGraph ( ) )
{
UE_LOG ( LogConsoleResponse , Log , TEXT ( " " ) ) ;
UE_LOG ( LogConsoleResponse , Log , TEXT ( " FRenderingCompositePassContext:Debug '%s' --------- " ) , GraphDebugName ) ;
UE_LOG ( LogConsoleResponse , Log , TEXT ( " " ) ) ;
GGMLFileWriter . OpenGMLFile ( GraphDebugName ) ;
GGMLFileWriter . WriteLine ( " Creator \" UnrealEngine4 \" " ) ;
GGMLFileWriter . WriteLine ( " Version \" 2.10 \" " ) ;
GGMLFileWriter . WriteLine ( " graph " ) ;
GGMLFileWriter . WriteLine ( " [ " ) ;
GGMLFileWriter . WriteLine ( " \t comment \t \" This file can be viewed with yEd from yWorks. Run Layout/Hierarchical after loading. \" " ) ;
GGMLFileWriter . WriteLine ( " \t hierarchic \t 1 " ) ;
GGMLFileWriter . WriteLine ( " \t directed \t 1 " ) ;
}
2015-07-07 13:58:55 -04:00
bool bNewOrder = CVarCompositionGraphOrder . GetValueOnRenderThread ( ) ! = 0 ;
2014-03-14 14:13:41 -04:00
2015-07-07 13:58:55 -04:00
if ( bNewOrder )
{
for ( FRenderingCompositePass * Node : Graph . Nodes )
{
Graph . RecursivelyGatherDependencies ( Node ) ;
}
for ( FRenderingCompositePass * Node : Graph . Nodes )
{
Graph . RecursivelyProcess ( Node , * this ) ;
}
}
else
{
Graph . RecursivelyGatherDependencies ( Root ) ;
Graph . RecursivelyProcess ( Root , * this ) ;
}
2014-03-14 14:13:41 -04:00
if ( ShouldDebugCompositionGraph ( ) )
{
UE_LOG ( LogConsoleResponse , Log , TEXT ( " " ) ) ;
GGMLFileWriter . WriteLine ( " ] " ) ;
GGMLFileWriter . CloseGMLFile ( ) ;
}
}
}
// --------------------------------------------------------------------------
FRenderingCompositionGraph : : FRenderingCompositionGraph ( )
{
}
FRenderingCompositionGraph : : ~ FRenderingCompositionGraph ( )
{
Free ( ) ;
}
void FRenderingCompositionGraph : : Free ( )
{
for ( uint32 i = 0 ; i < ( uint32 ) Nodes . Num ( ) ; + + i )
{
FRenderingCompositePass * Element = Nodes [ i ] ;
if ( FMemStack : : Get ( ) . ContainsPointer ( Element ) )
{
Element - > ~ FRenderingCompositePass ( ) ;
}
else
{
// Call release on non-stack allocated elements
Element - > Release ( ) ;
}
}
Nodes . Empty ( ) ;
}
2015-07-07 13:58:55 -04:00
void FRenderingCompositionGraph : : RecursivelyGatherDependencies ( FRenderingCompositePass * Pass )
2014-03-14 14:13:41 -04:00
{
2015-07-07 13:58:55 -04:00
checkSlow ( Pass ) ;
2014-03-14 14:13:41 -04:00
2015-05-11 13:36:07 -04:00
if ( Pass - > bComputeOutputDescWasCalled )
2014-03-14 14:13:41 -04:00
{
// already processed
return ;
}
2015-05-11 13:36:07 -04:00
Pass - > bComputeOutputDescWasCalled = true ;
2014-03-14 14:13:41 -04:00
// iterate through all inputs and additional dependencies of this pass
uint32 Index = 0 ;
while ( const FRenderingCompositeOutputRef * OutputRefIt = Pass - > GetDependency ( Index + + ) )
{
FRenderingCompositeOutput * InputOutput = OutputRefIt - > GetOutput ( ) ;
if ( InputOutput )
{
// add a dependency to this output as we are referencing to it
InputOutput - > AddDependency ( ) ;
}
2015-07-08 06:06:55 -04:00
if ( FRenderingCompositePass * OutputRefItPass = OutputRefIt - > GetPass ( ) )
2014-03-14 14:13:41 -04:00
{
// recursively process all inputs of this Pass
2015-07-08 06:06:55 -04:00
RecursivelyGatherDependencies ( OutputRefItPass ) ;
2014-03-14 14:13:41 -04:00
}
}
// the pass is asked what the intermediate surface/texture format needs to be for all its outputs.
for ( uint32 OutputId = 0 ; ; + + OutputId )
{
EPassOutputId PassOutputId = ( EPassOutputId ) ( OutputId ) ;
FRenderingCompositeOutput * Output = Pass - > GetOutput ( PassOutputId ) ;
if ( ! Output )
{
break ;
}
Output - > RenderTargetDesc = Pass - > ComputeOutputDesc ( PassOutputId ) ;
}
}
2014-08-12 08:36:24 -04:00
void FRenderingCompositionGraph : : DumpOutputToFile ( FRenderingCompositePassContext & Context , const FString & Filename , FRenderingCompositeOutput * Output ) const
2014-03-14 14:13:41 -04:00
{
FSceneRenderTargetItem & RenderTargetItem = Output - > PooledRenderTarget - > GetRenderTargetItem ( ) ;
2014-10-08 02:52:01 -04:00
FHighResScreenshotConfig & HighResScreenshotConfig = GetHighResScreenshotConfig ( ) ;
2014-03-14 14:13:41 -04:00
FTextureRHIRef Texture = RenderTargetItem . TargetableTexture ? RenderTargetItem . TargetableTexture : RenderTargetItem . ShaderResourceTexture ;
check ( Texture ) ;
check ( Texture - > GetTexture2D ( ) ) ;
FIntRect SourceRect ;
2014-08-12 08:36:24 -04:00
int32 MSAAXSamples = Texture - > GetNumSamples ( ) ;
2014-03-14 14:13:41 -04:00
if ( GIsHighResScreenshot )
{
2014-10-08 02:52:01 -04:00
SourceRect = HighResScreenshotConfig . CaptureRegion ;
2014-08-08 12:28:35 -04:00
if ( SourceRect . Area ( ) = = 0 )
2014-07-29 07:09:41 -04:00
{
SourceRect = Context . View . ViewRect ;
}
else
{
2014-08-12 08:36:24 -04:00
SourceRect . Min . X * = MSAAXSamples ;
SourceRect . Max . X * = MSAAXSamples ;
2014-07-29 07:09:41 -04:00
}
2014-03-14 14:13:41 -04:00
}
2014-08-12 08:36:24 -04:00
FIntPoint DestSize ( SourceRect . Width ( ) , SourceRect . Height ( ) ) ;
EPixelFormat PixelFormat = Texture - > GetFormat ( ) ;
FString ResultPath ;
switch ( PixelFormat )
{
case PF_FloatRGBA :
{
TArray < FFloat16Color > Bitmap ;
Context . RHICmdList . ReadSurfaceFloatData ( Texture , SourceRect , Bitmap , ( ECubeFace ) 0 , 0 , 0 ) ;
2014-10-08 02:52:01 -04:00
HighResScreenshotConfig . SaveImage ( Filename , Bitmap , DestSize , & ResultPath ) ;
2014-08-12 08:36:24 -04:00
}
break ;
case PF_R8G8B8A8 :
case PF_B8G8R8A8 :
{
FReadSurfaceDataFlags ReadDataFlags ;
ReadDataFlags . SetLinearToGamma ( false ) ;
TArray < FColor > Bitmap ;
Context . RHICmdList . ReadSurfaceData ( Texture , SourceRect , Bitmap , ReadDataFlags ) ;
FColor * Pixel = Bitmap . GetData ( ) ;
for ( int32 i = 0 , Count = Bitmap . Num ( ) ; i < Count ; i + + , Pixel + + )
{
Pixel - > A = 255 ;
}
2014-10-08 02:52:01 -04:00
HighResScreenshotConfig . SaveImage ( Filename , Bitmap , DestSize , & ResultPath ) ;
2014-08-12 08:36:24 -04:00
}
break ;
}
UE_LOG ( LogConsoleResponse , Display , TEXT ( " Content was saved to \" %s \" " ) , * ResultPath ) ;
2014-03-14 14:13:41 -04:00
}
void FRenderingCompositionGraph : : RecursivelyProcess ( const FRenderingCompositeOutputRef & InOutputRef , FRenderingCompositePassContext & Context ) const
{
FRenderingCompositePass * Pass = InOutputRef . GetPass ( ) ;
FRenderingCompositeOutput * Output = InOutputRef . GetOutput ( ) ;
# if !UE_BUILD_SHIPPING
if ( ! Pass | | ! Output )
{
// to track down a crash bug
if ( Context . Pass )
{
UE_LOG ( LogRenderer , Fatal , TEXT ( " FRenderingCompositionGraph::RecursivelyProcess %s " ) , * Context . Pass - > ConstructDebugName ( ) ) ;
}
}
# endif
check ( Pass ) ;
check ( Output ) ;
2015-05-11 13:36:07 -04:00
if ( Pass - > bProcessWasCalled )
2014-03-14 14:13:41 -04:00
{
// already processed
return ;
}
2015-05-11 13:36:07 -04:00
Pass - > bProcessWasCalled = true ;
2014-03-14 14:13:41 -04:00
// iterate through all inputs and additional dependencies of this pass
{
uint32 Index = 0 ;
while ( const FRenderingCompositeOutputRef * OutputRefIt = Pass - > GetDependency ( Index + + ) )
{
if ( OutputRefIt - > GetPass ( ) )
{
if ( ! OutputRefIt )
{
// Pass doesn't have more inputs
break ;
}
FRenderingCompositeOutput * Input = OutputRefIt - > GetOutput ( ) ;
// to track down an issue, should never happen
check ( OutputRefIt - > GetPass ( ) ) ;
2015-05-11 13:36:07 -04:00
if ( GRenderTargetPool . IsEventRecordingEnabled ( ) )
2014-04-23 19:52:11 -04:00
{
GRenderTargetPool . AddPhaseEvent ( * Pass - > ConstructDebugName ( ) ) ;
}
2014-03-14 14:13:41 -04:00
Context . Pass = Pass ;
RecursivelyProcess ( * OutputRefIt , Context ) ;
}
}
}
2014-04-02 18:09:23 -04:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
2014-03-14 14:13:41 -04:00
if ( ShouldDebugCompositionGraph ( ) )
{
GGMLFileWriter . WriteLine ( " \t node " ) ;
GGMLFileWriter . WriteLine ( " \t [ " ) ;
int32 PassId = ComputeUniquePassId ( Pass ) ;
FString PassDebugName = Pass - > ConstructDebugName ( ) ;
ANSICHAR Line [ MAX_SPRINTF ] ;
{
GGMLFileWriter . WriteLine ( " \t \t graphics " ) ;
GGMLFileWriter . WriteLine ( " \t \t [ " ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t w \t %d " , 200 ) ;
GGMLFileWriter . WriteLine ( Line ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t h \t %d " , 80 ) ;
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t \t fill \t \" #FFCCCC \" " ) ;
GGMLFileWriter . WriteLine ( " \t \t ] " ) ;
}
{
FCStringAnsi : : Sprintf ( Line , " \t \t id \t %d " , PassId ) ;
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t LabelGraphics " ) ;
GGMLFileWriter . WriteLine ( " \t \t [ " ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t text \t \" #%d \r %s \" " , PassId , ( const char * ) TCHAR_TO_ANSI ( * PassDebugName ) ) ;
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t \t anchor \t \" t \" " ) ; // put label internally on top
GGMLFileWriter . WriteLine ( " \t \t \t fontSize \t 14 " ) ;
GGMLFileWriter . WriteLine ( " \t \t \t fontStyle \t \" bold \" " ) ;
GGMLFileWriter . WriteLine ( " \t \t ] " ) ;
}
UE_LOG ( LogConsoleResponse , Log , TEXT ( " Node#%d '%s' " ) , PassId , * PassDebugName ) ;
GGMLFileWriter . WriteLine ( " \t \t isGroup \t 1 " ) ;
GGMLFileWriter . WriteLine ( " \t ] " ) ;
uint32 InputId = 0 ;
while ( FRenderingCompositeOutputRef * OutputRefIt = Pass - > GetInput ( ( EPassInputId ) ( InputId + + ) ) )
{
if ( OutputRefIt - > Source )
{
// source is hooked up
FString InputName = OutputRefIt - > Source - > ConstructDebugName ( ) ;
int32 TargetPassId = ComputeUniquePassId ( OutputRefIt - > Source ) ;
UE_LOG ( LogConsoleResponse , Log , TEXT ( " ePId_Input%d: Node#%d @ ePId_Output%d '%s' " ) , InputId - 1 , TargetPassId , ( uint32 ) OutputRefIt - > PassOutputId , * InputName ) ;
// input connection to another node
{
GGMLFileWriter . WriteLine ( " \t edge " ) ;
GGMLFileWriter . WriteLine ( " \t [ " ) ;
{
FCStringAnsi : : Sprintf ( Line , " \t \t source \t %d " , ComputeUniqueOutputId ( OutputRefIt - > Source , OutputRefIt - > PassOutputId ) ) ;
GGMLFileWriter . WriteLine ( Line ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t target \t %d " , PassId ) ;
GGMLFileWriter . WriteLine ( Line ) ;
}
{
FString EdgeName = FString : : Printf ( TEXT ( " ePId_Input%d " ) , InputId - 1 ) ;
GGMLFileWriter . WriteLine ( " \t \t LabelGraphics " ) ;
GGMLFileWriter . WriteLine ( " \t \t [ " ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t text \t \" %s \" " , ( const char * ) TCHAR_TO_ANSI ( * EdgeName ) ) ;
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t \t model \t \" three_center \" " ) ;
GGMLFileWriter . WriteLine ( " \t \t \t position \t \" tcentr \" " ) ;
GGMLFileWriter . WriteLine ( " \t \t ] " ) ;
}
GGMLFileWriter . WriteLine ( " \t ] " ) ;
}
}
else
{
// source is not hooked up
UE_LOG ( LogConsoleResponse , Log , TEXT ( " ePId_Input%d: " ) , InputId - 1 ) ;
}
}
uint32 DepId = 0 ;
while ( FRenderingCompositeOutputRef * OutputRefIt = Pass - > GetAdditionalDependency ( DepId + + ) )
{
check ( OutputRefIt - > Source ) ;
FString InputName = OutputRefIt - > Source - > ConstructDebugName ( ) ;
int32 TargetPassId = ComputeUniquePassId ( OutputRefIt - > Source ) ;
UE_LOG ( LogConsoleResponse , Log , TEXT ( " Dependency: Node#%d @ ePId_Output%d '%s' " ) , TargetPassId , ( uint32 ) OutputRefIt - > PassOutputId , * InputName ) ;
// dependency connection to another node
{
GGMLFileWriter . WriteLine ( " \t edge " ) ;
GGMLFileWriter . WriteLine ( " \t [ " ) ;
{
FCStringAnsi : : Sprintf ( Line , " \t \t source \t %d " , ComputeUniqueOutputId ( OutputRefIt - > Source , OutputRefIt - > PassOutputId ) ) ;
GGMLFileWriter . WriteLine ( Line ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t target \t %d " , PassId ) ;
GGMLFileWriter . WriteLine ( Line ) ;
}
// dashed line
{
GGMLFileWriter . WriteLine ( " \t \t graphics " ) ;
GGMLFileWriter . WriteLine ( " \t \t [ " ) ;
GGMLFileWriter . WriteLine ( " \t \t \t style \t \" dashed \" " ) ;
GGMLFileWriter . WriteLine ( " \t \t ] " ) ;
}
{
FString EdgeName = TEXT ( " Dependency " ) ;
GGMLFileWriter . WriteLine ( " \t \t LabelGraphics " ) ;
GGMLFileWriter . WriteLine ( " \t \t [ " ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t text \t \" %s \" " , ( const char * ) TCHAR_TO_ANSI ( * EdgeName ) ) ;
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t \t model \t \" three_center \" " ) ;
GGMLFileWriter . WriteLine ( " \t \t \t position \t \" tcentr \" " ) ;
GGMLFileWriter . WriteLine ( " \t \t ] " ) ;
}
GGMLFileWriter . WriteLine ( " \t ] " ) ;
}
}
uint32 OutputId = 0 ;
2015-03-06 15:13:38 -05:00
while ( FRenderingCompositeOutput * PassOutput = Pass - > GetOutput ( ( EPassOutputId ) ( OutputId ) ) )
2014-03-14 14:13:41 -04:00
{
2015-03-06 15:13:38 -05:00
UE_LOG ( LogConsoleResponse , Log , TEXT ( " ePId_Output%d %s %s Dep: %d " ) , OutputId , * PassOutput - > RenderTargetDesc . GenerateInfoString ( ) , PassOutput - > RenderTargetDesc . DebugName , PassOutput - > GetDependencyCount ( ) ) ;
2014-03-14 14:13:41 -04:00
GGMLFileWriter . WriteLine ( " \t node " ) ;
GGMLFileWriter . WriteLine ( " \t [ " ) ;
{
GGMLFileWriter . WriteLine ( " \t \t graphics " ) ;
GGMLFileWriter . WriteLine ( " \t \t [ " ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t w \t %d " , 220 ) ;
GGMLFileWriter . WriteLine ( Line ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t h \t %d " , 40 ) ;
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t ] " ) ;
}
{
FCStringAnsi : : Sprintf ( Line , " \t \t id \t %d " , ComputeUniqueOutputId ( Pass , ( EPassOutputId ) ( OutputId ) ) ) ;
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t LabelGraphics " ) ;
GGMLFileWriter . WriteLine ( " \t \t [ " ) ;
FCStringAnsi : : Sprintf ( Line , " \t \t \t text \t \" ePId_Output%d '%s' \r %s \" " ,
OutputId ,
2015-03-06 15:13:38 -05:00
( const char * ) TCHAR_TO_ANSI ( PassOutput - > RenderTargetDesc . DebugName ) ,
( const char * ) TCHAR_TO_ANSI ( * PassOutput - > RenderTargetDesc . GenerateInfoString ( ) ) ) ;
2014-03-14 14:13:41 -04:00
GGMLFileWriter . WriteLine ( Line ) ;
GGMLFileWriter . WriteLine ( " \t \t ] " ) ;
}
{
FCStringAnsi : : Sprintf ( Line , " \t \t gid \t %d " , PassId ) ;
GGMLFileWriter . WriteLine ( Line ) ;
}
GGMLFileWriter . WriteLine ( " \t ] " ) ;
+ + OutputId ;
}
UE_LOG ( LogConsoleResponse , Log , TEXT ( " " ) ) ;
}
2014-04-02 18:09:23 -04:00
# endif
2014-03-14 14:13:41 -04:00
Context . Pass = Pass ;
Context . SetViewportInvalid ( ) ;
// then process the pass itself
Pass - > Process ( Context ) ;
// for VisualizeTexture and output buffer dumping
2014-04-02 18:09:23 -04:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
2014-03-14 14:13:41 -04:00
{
uint32 OutputId = 0 ;
while ( FRenderingCompositeOutput * PassOutput = Pass - > GetOutput ( ( EPassOutputId ) OutputId ) )
{
// use intermediate texture unless it's the last one where we render to the final output
if ( PassOutput - > PooledRenderTarget )
{
2014-06-12 07:13:34 -04:00
GRenderTargetPool . VisualizeTexture . SetCheckPoint ( Context . RHICmdList , PassOutput - > PooledRenderTarget ) ;
2014-03-14 14:13:41 -04:00
// If this buffer was given a dump filename, write it out
const FString & Filename = Pass - > GetOutputDumpFilename ( ( EPassOutputId ) OutputId ) ;
if ( ! Filename . IsEmpty ( ) )
{
DumpOutputToFile ( Context , Filename , PassOutput ) ;
}
// If we've been asked to write out the pixel data for this pass to an external array, do it now
TArray < FColor > * OutputColorArray = Pass - > GetOutputColorArray ( ( EPassOutputId ) OutputId ) ;
if ( OutputColorArray )
{
2014-06-27 11:07:13 -04:00
Context . RHICmdList . ReadSurfaceData (
2014-03-14 14:13:41 -04:00
PassOutput - > PooledRenderTarget - > GetRenderTargetItem ( ) . TargetableTexture ,
Context . View . ViewRect ,
* OutputColorArray ,
FReadSurfaceDataFlags ( )
) ;
}
}
OutputId + + ;
}
}
2014-04-02 18:09:23 -04:00
# endif
2014-03-14 14:13:41 -04:00
2014-07-29 07:09:41 -04:00
// iterate through all inputs of tghis pass and decrement the references for it's inputs
2014-03-14 14:13:41 -04:00
// this can release some intermediate RT so they can be reused
{
uint32 InputId = 0 ;
while ( const FRenderingCompositeOutputRef * OutputRefIt = Pass - > GetDependency ( InputId + + ) )
{
FRenderingCompositeOutput * Input = OutputRefIt - > GetOutput ( ) ;
if ( Input )
{
Input - > ResolveDependencies ( ) ;
}
}
}
}
// for debugging purpose O(n)
int32 FRenderingCompositionGraph : : ComputeUniquePassId ( FRenderingCompositePass * Pass ) const
{
for ( uint32 i = 0 ; i < ( uint32 ) Nodes . Num ( ) ; + + i )
{
FRenderingCompositePass * Element = Nodes [ i ] ;
if ( Element = = Pass )
{
return i ;
}
}
return - 1 ;
}
int32 FRenderingCompositionGraph : : ComputeUniqueOutputId ( FRenderingCompositePass * Pass , EPassOutputId OutputId ) const
{
uint32 Ret = Nodes . Num ( ) ;
for ( uint32 i = 0 ; i < ( uint32 ) Nodes . Num ( ) ; + + i )
{
FRenderingCompositePass * Element = Nodes [ i ] ;
if ( Element = = Pass )
{
return ( int32 ) ( Ret + ( uint32 ) OutputId ) ;
}
uint32 OutputCount = 0 ;
while ( Pass - > GetOutput ( ( EPassOutputId ) OutputCount ) )
{
+ + OutputCount ;
}
Ret + = OutputCount ;
}
return - 1 ;
}
FRenderingCompositeOutput * FRenderingCompositeOutputRef : : GetOutput ( ) const
{
if ( Source = = 0 )
{
return 0 ;
}
return Source - > GetOutput ( PassOutputId ) ;
}
FRenderingCompositePass * FRenderingCompositeOutputRef : : GetPass ( ) const
{
return Source ;
}
// -----------------------------------------------------------------
void FPostProcessPassParameters : : Bind ( const FShaderParameterMap & ParameterMap )
{
BilinearTextureSampler0 . Bind ( ParameterMap , TEXT ( " BilinearTextureSampler0 " ) ) ;
BilinearTextureSampler1 . Bind ( ParameterMap , TEXT ( " BilinearTextureSampler1 " ) ) ;
ViewportSize . Bind ( ParameterMap , TEXT ( " ViewportSize " ) ) ;
ViewportRect . Bind ( ParameterMap , TEXT ( " ViewportRect " ) ) ;
ScreenPosToPixel . Bind ( ParameterMap , TEXT ( " ScreenPosToPixel " ) ) ;
for ( uint32 i = 0 ; i < ePId_Input_MAX ; + + i )
{
PostprocessInputParameter [ i ] . Bind ( ParameterMap , * FString : : Printf ( TEXT ( " PostprocessInput%d " ) , i ) ) ;
PostprocessInputParameterSampler [ i ] . Bind ( ParameterMap , * FString : : Printf ( TEXT ( " PostprocessInput%dSampler " ) , i ) ) ;
PostprocessInputSizeParameter [ i ] . Bind ( ParameterMap , * FString : : Printf ( TEXT ( " PostprocessInput%dSize " ) , i ) ) ;
PostProcessInputMinMaxParameter [ i ] . Bind ( ParameterMap , * FString : : Printf ( TEXT ( " PostprocessInput%dMinMax " ) , i ) ) ;
}
}
2014-06-12 07:13:34 -04:00
void FPostProcessPassParameters : : SetPS ( const FPixelShaderRHIParamRef & ShaderRHI , const FRenderingCompositePassContext & Context , FSamplerStateRHIParamRef Filter , bool bWhiteIfNoTexture , FSamplerStateRHIParamRef * FilterOverrideArray )
2014-03-14 14:13:41 -04:00
{
2014-06-27 11:07:13 -04:00
Set ( ShaderRHI , Context , Filter , bWhiteIfNoTexture , FilterOverrideArray ) ;
2014-03-14 14:13:41 -04:00
}
2014-06-12 07:13:34 -04:00
void FPostProcessPassParameters : : SetCS ( const FComputeShaderRHIParamRef & ShaderRHI , const FRenderingCompositePassContext & Context , FSamplerStateRHIParamRef Filter , bool bWhiteIfNoTexture , FSamplerStateRHIParamRef * FilterOverrideArray )
2014-03-14 14:13:41 -04:00
{
2014-06-27 11:07:13 -04:00
Set ( ShaderRHI , Context , Filter , bWhiteIfNoTexture , FilterOverrideArray ) ;
2014-03-14 14:13:41 -04:00
}
2014-06-12 07:13:34 -04:00
void FPostProcessPassParameters : : SetVS ( const FVertexShaderRHIParamRef & ShaderRHI , const FRenderingCompositePassContext & Context , FSamplerStateRHIParamRef Filter , bool bWhiteIfNoTexture , FSamplerStateRHIParamRef * FilterOverrideArray )
2014-03-14 14:13:41 -04:00
{
2014-06-27 11:07:13 -04:00
Set ( ShaderRHI , Context , Filter , bWhiteIfNoTexture , FilterOverrideArray ) ;
2014-03-14 14:13:41 -04:00
}
2014-06-24 14:37:21 -04:00
template < typename ShaderRHIParamRef >
void FPostProcessPassParameters : : Set (
const ShaderRHIParamRef & ShaderRHI ,
const FRenderingCompositePassContext & Context ,
FSamplerStateRHIParamRef Filter ,
bool bWhiteIfNoTexture ,
FSamplerStateRHIParamRef * FilterOverrideArray )
{
// assuming all outputs have the same size
FRenderingCompositeOutput * Output = Context . Pass - > GetOutput ( ePId_Output0 ) ;
// Output0 should always exist
check ( Output ) ;
// one should be on
check ( FilterOverrideArray | | Filter ) ;
// but not both
check ( ! FilterOverrideArray | | ! Filter ) ;
2014-06-27 11:07:13 -04:00
FRHICommandListImmediate & RHICmdList = Context . RHICmdList ;
2014-06-24 14:37:21 -04:00
if ( BilinearTextureSampler0 . IsBound ( ) )
{
2014-06-27 11:07:13 -04:00
RHICmdList . SetShaderSampler (
2014-06-24 14:37:21 -04:00
ShaderRHI ,
BilinearTextureSampler0 . GetBaseIndex ( ) ,
TStaticSamplerState < SF_Bilinear > : : GetRHI ( )
) ;
}
if ( BilinearTextureSampler1 . IsBound ( ) )
{
2014-06-27 11:07:13 -04:00
RHICmdList . SetShaderSampler (
2014-06-24 14:37:21 -04:00
ShaderRHI ,
BilinearTextureSampler1 . GetBaseIndex ( ) ,
TStaticSamplerState < SF_Bilinear > : : GetRHI ( )
) ;
}
if ( ViewportSize . IsBound ( ) | | ScreenPosToPixel . IsBound ( ) | | ViewportRect . IsBound ( ) )
{
FIntRect LocalViewport = Context . GetViewport ( ) ;
FIntPoint ViewportOffset = LocalViewport . Min ;
FIntPoint ViewportExtent = LocalViewport . Size ( ) ;
{
FVector4 Value ( ViewportExtent . X , ViewportExtent . Y , 1.0f / ViewportExtent . X , 1.0f / ViewportExtent . Y ) ;
SetShaderValue ( RHICmdList , ShaderRHI , ViewportSize , Value ) ;
}
{
2015-06-22 19:08:33 -04:00
SetShaderValue ( RHICmdList , ShaderRHI , ViewportRect , Context . GetViewport ( ) ) ;
2014-06-24 14:37:21 -04:00
}
{
FVector4 ScreenPosToPixelValue (
ViewportExtent . X * 0.5f ,
- ViewportExtent . Y * 0.5f ,
ViewportExtent . X * 0.5f - 0.5f + ViewportOffset . X ,
ViewportExtent . Y * 0.5f - 0.5f + ViewportOffset . Y ) ;
SetShaderValue ( RHICmdList , ShaderRHI , ScreenPosToPixel , ScreenPosToPixelValue ) ;
}
}
//Calculate a base scene texture min max which will be pulled in by a pixel for each PP input.
FIntRect ContextViewportRect = Context . IsViewportValid ( ) ? Context . GetViewport ( ) : FIntRect ( 0 , 0 , 0 , 0 ) ;
2015-05-29 10:47:57 -04:00
const FIntPoint SceneRTSize = FSceneRenderTargets : : Get ( RHICmdList ) . GetBufferSizeXY ( ) ;
2014-06-24 14:37:21 -04:00
FVector4 BaseSceneTexMinMax ( ( ( float ) ContextViewportRect . Min . X / SceneRTSize . X ) ,
( ( float ) ContextViewportRect . Min . Y / SceneRTSize . Y ) ,
( ( float ) ContextViewportRect . Max . X / SceneRTSize . X ) ,
( ( float ) ContextViewportRect . Max . Y / SceneRTSize . Y ) ) ;
// ePId_Input0, ePId_Input1, ...
for ( uint32 Id = 0 ; Id < ( uint32 ) ePId_Input_MAX ; + + Id )
{
FRenderingCompositeOutputRef * OutputRef = Context . Pass - > GetInput ( ( EPassInputId ) Id ) ;
if ( ! OutputRef )
{
// Pass doesn't have more inputs
break ;
}
const auto FeatureLevel = Context . GetFeatureLevel ( ) ;
FRenderingCompositeOutput * Input = OutputRef - > GetOutput ( ) ;
TRefCountPtr < IPooledRenderTarget > InputPooledElement ;
if ( Input )
{
InputPooledElement = Input - > RequestInput ( ) ;
}
FSamplerStateRHIParamRef LocalFilter = FilterOverrideArray ? FilterOverrideArray [ Id ] : Filter ;
if ( InputPooledElement )
{
check ( ! InputPooledElement - > IsFree ( ) ) ;
const FTextureRHIRef & SrcTexture = InputPooledElement - > GetRenderTargetItem ( ) . ShaderResourceTexture ;
SetTextureParameter ( RHICmdList , ShaderRHI , PostprocessInputParameter [ Id ] , PostprocessInputParameterSampler [ Id ] , LocalFilter , SrcTexture ) ;
2014-10-01 12:14:57 -04:00
if ( PostprocessInputSizeParameter [ Id ] . IsBound ( ) | | PostProcessInputMinMaxParameter [ Id ] . IsBound ( ) )
2014-06-24 14:37:21 -04:00
{
float Width = InputPooledElement - > GetDesc ( ) . Extent . X ;
float Height = InputPooledElement - > GetDesc ( ) . Extent . Y ;
2014-10-01 12:14:57 -04:00
2014-06-24 14:37:21 -04:00
FVector2D OnePPInputPixelUVSize = FVector2D ( 1.0f / Width , 1.0f / Height ) ;
2014-10-01 12:14:57 -04:00
FVector4 TextureSize ( Width , Height , OnePPInputPixelUVSize . X , OnePPInputPixelUVSize . Y ) ;
SetShaderValue ( RHICmdList , ShaderRHI , PostprocessInputSizeParameter [ Id ] , TextureSize ) ;
//We could use the main scene min max here if it weren't that we need to pull the max in by a pixel on a per input basis.
2014-06-24 14:37:21 -04:00
FVector4 PPInputMinMax = BaseSceneTexMinMax ;
PPInputMinMax . Z - = OnePPInputPixelUVSize . X ;
PPInputMinMax . W - = OnePPInputPixelUVSize . Y ;
SetShaderValue ( RHICmdList , ShaderRHI , PostProcessInputMinMaxParameter [ Id ] , PPInputMinMax ) ;
}
}
else
{
IPooledRenderTarget * Texture = bWhiteIfNoTexture ? GSystemTextures . WhiteDummy : GSystemTextures . BlackDummy ;
// if the input is not there but the shader request it we give it at least some data to avoid d3ddebug errors and shader permutations
// to make features optional we use default black for additive passes without shader permutations
SetTextureParameter ( RHICmdList , ShaderRHI , PostprocessInputParameter [ Id ] , PostprocessInputParameterSampler [ Id ] , LocalFilter , Texture - > GetRenderTargetItem ( ) . TargetableTexture ) ;
FVector4 Dummy ( 1 , 1 , 1 , 1 ) ;
SetShaderValue ( RHICmdList , ShaderRHI , PostprocessInputSizeParameter [ Id ] , Dummy ) ;
SetShaderValue ( RHICmdList , ShaderRHI , PostProcessInputMinMaxParameter [ Id ] , Dummy ) ;
}
}
// todo warning if Input[] or InputSize[] is bound but not available, maybe set a specific input texture (blinking?)
}
# define IMPLEMENT_POST_PROCESS_PARAM_SET( ShaderRHIParamRef ) \
template void FPostProcessPassParameters : : Set < ShaderRHIParamRef > ( \
const ShaderRHIParamRef & ShaderRHI , \
const FRenderingCompositePassContext & Context , \
FSamplerStateRHIParamRef Filter , \
bool bWhiteIfNoTexture , \
FSamplerStateRHIParamRef * FilterOverrideArray \
) ;
IMPLEMENT_POST_PROCESS_PARAM_SET ( FVertexShaderRHIParamRef ) ;
IMPLEMENT_POST_PROCESS_PARAM_SET ( FHullShaderRHIParamRef ) ;
IMPLEMENT_POST_PROCESS_PARAM_SET ( FDomainShaderRHIParamRef ) ;
IMPLEMENT_POST_PROCESS_PARAM_SET ( FGeometryShaderRHIParamRef ) ;
IMPLEMENT_POST_PROCESS_PARAM_SET ( FPixelShaderRHIParamRef ) ;
IMPLEMENT_POST_PROCESS_PARAM_SET ( FComputeShaderRHIParamRef ) ;
2014-03-14 14:13:41 -04:00
FArchive & operator < < ( FArchive & Ar , FPostProcessPassParameters & P )
{
Ar < < P . BilinearTextureSampler0 < < P . BilinearTextureSampler1 < < P . ViewportSize < < P . ScreenPosToPixel < < P . ViewportRect ;
for ( uint32 i = 0 ; i < ePId_Input_MAX ; + + i )
{
Ar < < P . PostprocessInputParameter [ i ] ;
Ar < < P . PostprocessInputParameterSampler [ i ] ;
Ar < < P . PostprocessInputSizeParameter [ i ] ;
Ar < < P . PostProcessInputMinMaxParameter [ i ] ;
}
return Ar ;
}
// -----------------------------------------------------------------
const FSceneRenderTargetItem & FRenderingCompositeOutput : : RequestSurface ( const FRenderingCompositePassContext & Context )
{
if ( PooledRenderTarget )
{
return PooledRenderTarget - > GetRenderTargetItem ( ) ;
}
if ( ! RenderTargetDesc . IsValid ( ) )
{
// useful to use the CompositingGraph dependency resolve but pass the data between nodes differently
static FSceneRenderTargetItem Null ;
return Null ;
}
if ( ! PooledRenderTarget )
{
GRenderTargetPool . FindFreeElement ( RenderTargetDesc , PooledRenderTarget , RenderTargetDesc . DebugName ) ;
}
check ( ! PooledRenderTarget - > IsFree ( ) ) ;
FSceneRenderTargetItem & RenderTargetItem = PooledRenderTarget - > GetRenderTargetItem ( ) ;
return RenderTargetItem ;
}
const FPooledRenderTargetDesc * FRenderingCompositePass : : GetInputDesc ( EPassInputId InPassInputId ) const
{
// to overcome const issues, this way it's kept local
FRenderingCompositePass * This = ( FRenderingCompositePass * ) this ;
const FRenderingCompositeOutputRef * OutputRef = This - > GetInput ( InPassInputId ) ;
if ( ! OutputRef )
{
return 0 ;
}
FRenderingCompositeOutput * Input = OutputRef - > GetOutput ( ) ;
if ( ! Input )
{
return 0 ;
}
return & Input - > RenderTargetDesc ;
}
uint32 FRenderingCompositePass : : ComputeInputCount ( )
{
for ( uint32 i = 0 ; ; + + i )
{
if ( ! GetInput ( ( EPassInputId ) i ) )
{
return i ;
}
}
}
uint32 FRenderingCompositePass : : ComputeOutputCount ( )
{
for ( uint32 i = 0 ; ; + + i )
{
if ( ! GetOutput ( ( EPassOutputId ) i ) )
{
return i ;
}
}
}
FString FRenderingCompositePass : : ConstructDebugName ( )
{
FString Name ;
uint32 OutputId = 0 ;
while ( FRenderingCompositeOutput * Output = GetOutput ( ( EPassOutputId ) OutputId ) )
{
Name + = Output - > RenderTargetDesc . DebugName ;
+ + OutputId ;
}
if ( Name . IsEmpty ( ) )
{
Name = TEXT ( " UnknownName " ) ;
}
return Name ;
}