2019-06-18 13:23:41 -04:00
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
# include "RenderGraphBarrierBatcher.h"
# include "RenderGraphPass.h"
namespace
{
2019-06-19 00:23:15 -04:00
/** Number of entries to reserve in the batch array. */
const uint32 kBatchReservationSize = 8 ;
2019-06-18 13:23:41 -04:00
2019-06-19 00:23:15 -04:00
/** Returns whether the resource is transitioning to a writable state. */
inline bool IsWriteAccessBegin ( FRDGResourceState : : EAccess AccessBefore , FRDGResourceState : : EAccess AccessAfter )
2019-06-18 13:23:41 -04:00
{
2019-06-19 00:23:15 -04:00
return AccessBefore = = FRDGResourceState : : EAccess : : Read & & AccessAfter = = FRDGResourceState : : EAccess : : Write ;
}
/** Returns whether the resource is transitioning from a writable state. */
inline bool IsWriteAccessEnd ( FRDGResourceState : : EAccess AccessBefore , FRDGResourceState : : EAccess AccessAfter )
{
return AccessBefore = = FRDGResourceState : : EAccess : : Write & & AccessAfter = = FRDGResourceState : : EAccess : : Read ;
}
inline EResourceTransitionPipeline GetResourceTransitionPipeline ( FRDGResourceState : : EPipeline PipelineBefore , FRDGResourceState : : EPipeline PipelineAfter )
{
switch ( PipelineBefore )
2019-06-18 13:23:41 -04:00
{
case FRDGResourceState : : EPipeline : : Graphics :
2019-06-19 00:23:15 -04:00
switch ( PipelineAfter )
{
case FRDGResourceState : : EPipeline : : Graphics :
return EResourceTransitionPipeline : : EGfxToGfx ;
case FRDGResourceState : : EPipeline : : Compute :
return EResourceTransitionPipeline : : EGfxToCompute ;
}
break ;
2019-06-18 13:23:41 -04:00
case FRDGResourceState : : EPipeline : : Compute :
2019-06-19 00:23:15 -04:00
switch ( PipelineAfter )
{
case FRDGResourceState : : EPipeline : : Graphics :
return EResourceTransitionPipeline : : EComputeToGfx ;
case FRDGResourceState : : EPipeline : : Compute :
return EResourceTransitionPipeline : : EComputeToCompute ;
}
break ;
2019-06-18 13:23:41 -04:00
}
2019-06-19 00:23:15 -04:00
check ( false ) ;
return EResourceTransitionPipeline : : EGfxToGfx ;
2019-06-18 13:23:41 -04:00
}
} //! namespace
2019-07-16 13:08:56 -04:00
FRDGBarrierBatcher : : FRDGBarrierBatcher ( FRHICommandList & InRHICmdList , const FRDGPass * InPass )
: RHICmdList ( InRHICmdList )
, Pass ( InPass )
{
if ( Pass )
{
bIsGeneratingMips = Pass - > IsGenerateMips ( ) ;
Pipeline = Pass - > IsCompute ( ) ? FRDGResourceState : : EPipeline : : Compute : FRDGResourceState : : EPipeline : : Graphics ;
}
}
2019-06-18 13:23:41 -04:00
FRDGBarrierBatcher : : ~ FRDGBarrierBatcher ( )
{
2019-07-16 13:08:56 -04:00
for ( FRHITexture * RHITexture : TextureUpdateMultiFrameBegins )
{
RHICmdList . BeginUpdateMultiFrameResource ( RHITexture ) ;
}
for ( FRHIUnorderedAccessView * RHIUAV : UAVUpdateMultiFrameBegins )
{
RHICmdList . BeginUpdateMultiFrameResource ( RHIUAV ) ;
}
for ( auto & Element : TextureBatchMap )
{
FTransitionParameters TransitionParameters = Element . Key ;
FTextureBatch & Batch = Element . Value ;
RHICmdList . TransitionResources ( TransitionParameters . TransitionAccess , Batch . GetData ( ) , Batch . Num ( ) ) ;
}
for ( auto & Element : UAVBatchMap )
{
FTransitionParameters TransitionParameters = Element . Key ;
FUAVBatch & Batch = Element . Value ;
RHICmdList . TransitionResources ( TransitionParameters . TransitionAccess , TransitionParameters . TransitionPipeline , Batch . GetData ( ) , Batch . Num ( ) ) ;
}
for ( FRHITexture * RHITexture : TextureUpdateMultiFrameEnds )
{
RHICmdList . EndUpdateMultiFrameResource ( RHITexture ) ;
}
for ( FRHIUnorderedAccessView * RHIUAV : UAVUpdateMultiFrameEnds )
{
RHICmdList . EndUpdateMultiFrameResource ( RHIUAV ) ;
}
2019-06-18 13:23:41 -04:00
}
2019-07-16 13:08:56 -04:00
void FRDGBarrierBatcher : : QueueTransitionTexture ( FRDGTexture * Texture , FRDGResourceState : : EAccess AccessAfter )
2019-06-18 13:23:41 -04:00
{
check ( Texture ) ;
2019-07-16 13:08:56 -04:00
// Texture transitions are ignored when generating mips, since the render target binding call or UAV will
// perform the subresource transition.
if ( bIsGeneratingMips )
{
return ;
}
2019-06-18 13:23:41 -04:00
2019-07-16 13:08:56 -04:00
const FRDGResourceState StateBefore = Texture - > State ;
const FRDGResourceState StateAfter ( Pass , Pipeline , AccessAfter ) ;
ValidateTransition ( Texture , StateBefore , StateAfter ) ;
2019-06-18 13:23:41 -04:00
if ( StateBefore ! = StateAfter )
{
FRHITexture * RHITexture = Texture - > GetRHIUnchecked ( ) ;
const bool bIsMultiFrameResource = ( Texture - > Flags & ERDGResourceFlags : : MultiFrame ) = = ERDGResourceFlags : : MultiFrame ;
if ( bIsMultiFrameResource & & IsWriteAccessBegin ( StateBefore . Access , StateAfter . Access ) )
{
TextureUpdateMultiFrameBegins . AddUnique ( RHITexture ) ;
}
// Add transition to the respective batch bucket.
{
FTransitionParameters TransitionParameters ;
TransitionParameters . TransitionAccess = GetResourceTransitionAccess ( StateAfter . Access ) ;
TransitionParameters . TransitionPipeline = EResourceTransitionPipeline : : EGfxToGfx ; // NOTE: Transition API for textures doesn't currently expose pipeline transitions.
FTextureBatch & TextureBatch = TextureBatchMap . FindOrAdd ( TransitionParameters ) ;
TextureBatch . Reserve ( kBatchReservationSize ) ;
2019-07-16 13:08:56 -04:00
# if RDG_ENABLE_DEBUG
2019-06-18 13:23:41 -04:00
{
// We should have filtered out duplicates in the first branch of this function.
2019-07-16 13:08:56 -04:00
ensure ( TextureBatch . Find ( RHITexture ) = = INDEX_NONE ) ;
2019-06-18 13:23:41 -04:00
}
2019-07-16 13:08:56 -04:00
# endif
2019-06-18 13:23:41 -04:00
TextureBatch . Add ( RHITexture ) ;
}
if ( bIsMultiFrameResource & & IsWriteAccessEnd ( StateBefore . Access , StateAfter . Access ) )
{
TextureUpdateMultiFrameEnds . AddUnique ( RHITexture ) ;
}
Texture - > State = StateAfter ;
}
}
void FRDGBarrierBatcher : : QueueTransitionUAV (
FRHIUnorderedAccessView * UAV ,
FRDGTrackedResource * UnderlyingResource ,
2019-07-16 13:08:56 -04:00
FRDGResourceState : : EAccess AccessAfter )
2019-06-18 13:23:41 -04:00
{
check ( UAV ) ;
check ( UnderlyingResource ) ;
const FRDGResourceState StateBefore = UnderlyingResource - > State ;
2019-07-16 13:08:56 -04:00
const FRDGResourceState StateAfter ( Pass , Pipeline , AccessAfter ) ;
2019-06-18 13:23:41 -04:00
2019-07-16 13:08:56 -04:00
ValidateTransition ( UnderlyingResource , StateBefore , StateAfter ) ;
2019-06-18 13:23:41 -04:00
if ( StateBefore ! = StateAfter )
{
const bool bIsMultiFrameResource = ( UnderlyingResource - > Flags & ERDGResourceFlags : : MultiFrame ) = = ERDGResourceFlags : : MultiFrame ;
if ( bIsMultiFrameResource & & IsWriteAccessBegin ( StateBefore . Access , StateAfter . Access ) )
{
UAVUpdateMultiFrameBegins . AddUnique ( UAV ) ;
}
// Add transition to the correct batch bucket.
{
FTransitionParameters TransitionParameters ;
TransitionParameters . TransitionAccess = GetResourceTransitionAccessForUAV ( StateBefore . Access , StateAfter . Access ) ;
TransitionParameters . TransitionPipeline = GetResourceTransitionPipeline ( StateBefore . Pipeline , StateAfter . Pipeline ) ;
FUAVBatch & UAVBatch = UAVBatchMap . FindOrAdd ( TransitionParameters ) ;
UAVBatch . Reserve ( kBatchReservationSize ) ;
2019-07-16 13:08:56 -04:00
# if RDG_ENABLE_DEBUG
2019-06-18 13:23:41 -04:00
{
// We should have filtered out duplicates in the first branch of this function.
2019-07-16 13:08:56 -04:00
ensure ( UAVBatch . Find ( UAV ) = = INDEX_NONE ) ;
2019-06-18 13:23:41 -04:00
}
2019-07-16 13:08:56 -04:00
# endif
2019-06-18 13:23:41 -04:00
UAVBatch . Add ( UAV ) ;
}
if ( bIsMultiFrameResource & & IsWriteAccessEnd ( StateBefore . Access , StateAfter . Access ) )
{
UAVUpdateMultiFrameEnds . AddUnique ( UAV ) ;
}
UnderlyingResource - > State = StateAfter ;
}
}
2019-07-16 13:08:56 -04:00
void FRDGBarrierBatcher : : ValidateTransition ( const FRDGTrackedResource * Resource , FRDGResourceState StateBefore , FRDGResourceState StateAfter )
2019-06-18 13:23:41 -04:00
{
# if RDG_ENABLE_DEBUG
check ( StateAfter . Pipeline ! = FRDGResourceState : : EPipeline : : MAX ) ;
check ( StateAfter . Access ! = FRDGResourceState : : EAccess : : Unknown ) ;
if ( StateBefore ! = StateAfter & & StateAfter . Pass )
{
// We allow duplicate transitions of the same resource within the same pass, but not conflicting ones.
2019-07-16 13:08:56 -04:00
ensureMsgf (
2019-06-18 13:23:41 -04:00
StateBefore . Pass ! = StateAfter . Pass ,
TEXT ( " Pass %s attempted to transition resource %s to different states. Make sure the resource isn't being used \n " )
TEXT ( " for both read and write at the same time. This can occur if the resource is used as both an SRV and UAV, or \n " )
2019-07-16 13:08:56 -04:00
TEXT ( " SRV and Render Target, for example. If this pass is meant to generate mip maps, make sure the GenerateMips flag \n " )
TEXT ( " is set. \n " ) , StateAfter . Pass - > GetName ( ) , Resource - > Name ) ;
2019-06-18 13:23:41 -04:00
}
# endif
2019-07-16 13:08:56 -04:00
}
EResourceTransitionAccess FRDGBarrierBatcher : : GetResourceTransitionAccess ( FRDGResourceState : : EAccess AccessAfter ) const
{
return AccessAfter = = FRDGResourceState : : EAccess : : Write ? EResourceTransitionAccess : : EWritable : EResourceTransitionAccess : : EReadable ;
}
EResourceTransitionAccess FRDGBarrierBatcher : : GetResourceTransitionAccessForUAV ( FRDGResourceState : : EAccess AccessBefore , FRDGResourceState : : EAccess AccessAfter ) const
{
switch ( AccessAfter )
{
case FRDGResourceState : : EAccess : : Read :
return EResourceTransitionAccess : : EReadable ;
case FRDGResourceState : : EAccess : : Write :
// Mip-map generation uses its own barrier.
if ( bIsGeneratingMips )
{
return EResourceTransitionAccess : : ERWSubResBarrier ;
}
// If doing a Write -> Write transition, we use a UAV barrier.
else if ( AccessBefore = = FRDGResourceState : : EAccess : : Write )
{
return EResourceTransitionAccess : : ERWBarrier ;
}
else
{
return EResourceTransitionAccess : : EWritable ;
}
}
check ( false ) ;
return EResourceTransitionAccess : : EMaxAccess ;
}