Files
UnrealEngineUWP/Engine/Source/Runtime/RenderCore/Private/RenderGraphResources.cpp
zach bethel 10131e1285 Refactored RDG in preparation for UnifiedBuffer conversions.
- Refactord 'Finalized Access' feature into a more flexible 'External' vs. 'Internal' access mode per resource toggle.
      - Resources can transition between modes multiple times within the graph.
      - Supports async compute pipeline.
      - Supports queueing of requests to avoid back-to-back helper passes.
      - This feature is needed to support conversion of GPU scene buffers.

 - Deprecated the ReadOnly and ForceTracking resource flags and added a 'SkipTracking' flag instead.
      - Previous semantics were confusing and error prone.
      - New model requires a manual flag to tell RDG never to transition a resource.
      - This flag is used for read-only dummy resources as an optimization.

 - Renamed some of the auxiliary 'FinalizedResource' utilities since the name no longer matches the semantics.

#preflight 6266cc6d0634d0904ce4ba46

[CL 19904734 by zach bethel in ue5-main branch]
2022-04-25 13:00:12 -04:00

198 lines
6.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RenderGraphResources.h"
#include "RenderGraphPass.h"
#include "RenderGraphPrivate.h"
#include "RenderGraphResourcePool.h"
inline bool NeedsUAVBarrier(FRDGViewHandle PreviousHandle, FRDGViewHandle NextHandle)
{
// Barrier if previous / next don't have a matching valid skip-barrier UAV handle.
if (GRDGOverlapUAVs != 0 && NextHandle.IsValid() && PreviousHandle == NextHandle)
{
return false;
}
return true;
}
inline bool NeedsUAVBarrier(const FRDGSubresourceState& Previous, const FRDGSubresourceState& Next)
{
return NeedsUAVBarrier(Previous.NoUAVBarrierFilter.GetUniqueHandle(), Next.NoUAVBarrierFilter.GetUniqueHandle());
}
FRDGViewableResource::FRDGViewableResource(const TCHAR* InName, const ERDGViewableResourceType InType, bool bSkipTracking)
: FRDGResource(InName)
, Type(InType)
, bExternal(0)
, bExtracted(0)
, bProduced(0)
, bTransient(0)
, bForceNonTransient(0)
, TransientExtractionHint(ETransientExtractionHint::None)
, bLastOwner(1)
, bCulled(!IsImmediateMode())
, bUsedByAsyncComputePass(0)
, bQueuedForUpload(0)
, FirstBarrier(EFirstBarrier::Split)
, bUAVAccessed(0)
{
if (bSkipTracking)
{
SetExternalAccessMode(ERHIAccess::ReadOnlyExclusiveMask, ERHIPipeline::All);
AccessModeState.bLocked = 1;
}
}
bool FRDGProducerState::IsDependencyRequired(FRDGProducerState LastProducer, ERHIPipeline LastPipeline, FRDGProducerState NextState, ERHIPipeline NextPipeline)
{
/** This function determines whether a producer-consumer relationship exists in the graph, which is used for culling and
* async-compute fence derivation. Producers are tracked per-pipeline, so it's safe to elide a cross-pipeline producer
* for the purposes of overlapping producers, as long as a dependency exists on the same pipeline. Eliding both will
* split the producer / consumer graph into two and break culling. The only current use case this is allowing multiple
* pipes to write UAVs.
*
* Producer / consumer dependencies take place independent of resource state merging / transitions, so the logic must
* be carefully aligned so that cross-pipe dependencies align with transitions.
*/
// The first needs to be known producers.
check(IsWritableAccess(LastProducer.Access));
// A dependency is always applied on the same pipe to ensure that connectivity is preserved for culling purposes.
if (LastPipeline == NextPipeline)
{
return true;
}
// Only certain platforms allow multi-pipe UAV access.
const ERHIAccess MultiPipelineUAVMask = ERHIAccess::UAVMask & GRHIMultiPipelineMergeableAccessMask;
// Skip the dependency if the states are used as UAV on different pipes and a UAV barrier can be skipped. This elides the async fence.
if (EnumHasAnyFlags(NextState.Access, MultiPipelineUAVMask) && !NeedsUAVBarrier(LastProducer.NoUAVBarrierHandle, NextState.NoUAVBarrierHandle))
{
return false;
}
// Everything else requires a dependency.
return true;
}
bool FRDGSubresourceState::IsMergeAllowed(ERDGViewableResourceType ResourceType, const FRDGSubresourceState& Previous, const FRDGSubresourceState& Next)
{
/** State merging occurs during compilation and before resource transitions are collected. It serves to remove the bulk
* of unnecessary transitions by looking ahead in the resource usage chain. A resource transition cannot occur within
* a merged state, so a merge is not allowed to proceed if a barrier might be required. Merging is also where multi-pipe
* transitions are determined, if supported by the platform.
*/
const ERHIAccess AccessUnion = Previous.Access | Next.Access;
const ERHIAccess DSVMask = ERHIAccess::DSVRead | ERHIAccess::DSVWrite;
// If we have the same access between the two states, we don't need to check for invalid access combinations.
if (Previous.Access != Next.Access)
{
// Not allowed to merge read-only and writable states.
if (EnumHasAnyFlags(Previous.Access, ERHIAccess::ReadOnlyExclusiveMask) && EnumHasAnyFlags(Next.Access, ERHIAccess::WritableMask))
{
return false;
}
// Not allowed to merge write-only and readable states.
if (EnumHasAnyFlags(Previous.Access, ERHIAccess::WriteOnlyExclusiveMask) && EnumHasAnyFlags(Next.Access, ERHIAccess::ReadableMask))
{
return false;
}
// UAVs will filter through the above checks because they are both read and write. UAV can only merge it itself.
if (EnumHasAnyFlags(AccessUnion, ERHIAccess::UAVMask) && EnumHasAnyFlags(AccessUnion, ~ERHIAccess::UAVMask))
{
return false;
}
// Depth Read / Write should never merge with anything other than itself.
if (EnumHasAllFlags(AccessUnion, DSVMask) && EnumHasAnyFlags(AccessUnion, ~DSVMask))
{
return false;
}
// Filter out platform-specific unsupported mergeable states.
if (EnumHasAnyFlags(AccessUnion, ~GRHIMergeableAccessMask))
{
return false;
}
}
// Not allowed if the resource is being used as a UAV and needs a barrier.
if (EnumHasAnyFlags(Next.Access, ERHIAccess::UAVMask) && NeedsUAVBarrier(Previous, Next))
{
return false;
}
// Filter out unsupported platform-specific multi-pipeline merged accesses.
if (EnumHasAnyFlags(AccessUnion, ~GRHIMultiPipelineMergeableAccessMask) && Previous.GetPipelines() != Next.GetPipelines())
{
return false;
}
// Not allowed to merge differing flags.
if (Previous.Flags != Next.Flags)
{
return false;
}
return true;
}
bool FRDGSubresourceState::IsTransitionRequired(const FRDGSubresourceState& Previous, const FRDGSubresourceState& Next)
{
// This function only needs to filter out identical states and handle UAV barriers.
check(Next.Access != ERHIAccess::Unknown);
if (Previous.Access != Next.Access || Previous.GetPipelines() != Next.GetPipelines() || Previous.Flags != Next.Flags)
{
return true;
}
// UAV is a special case as a barrier may still be required even if the states match.
if (EnumHasAnyFlags(Next.Access, ERHIAccess::UAVMask) && NeedsUAVBarrier(Previous, Next))
{
return true;
}
return false;
}
void FRDGUniformBuffer::InitRHI()
{
check(!HasRHI());
const EUniformBufferValidation Validation =
#if RDG_ENABLE_DEBUG
EUniformBufferValidation::ValidateResources;
#else
EUniformBufferValidation::None;
#endif
const FRDGParameterStruct& PassParameters = GetParameters();
UniformBufferRHI = RHICreateUniformBuffer(PassParameters.GetContents(), PassParameters.GetLayoutPtr(), UniformBuffer_SingleFrame, Validation);
ResourceRHI = UniformBufferRHI;
}
FRDGTextureSubresourceRange FRDGTexture::GetSubresourceRangeSRV() const
{
FRDGTextureSubresourceRange Range = GetSubresourceRange();
// When binding a whole texture for shader read (SRV), we only use the first plane.
// Other planes like stencil require a separate view to access for read in the shader.
Range.PlaneSlice = FRHITransitionInfo::kDepthPlaneSlice;
Range.NumPlaneSlices = 1;
return Range;
}
IPooledRenderTarget* FRDGTexture::GetPooledRenderTarget() const
{
IF_RDG_ENABLE_DEBUG(ValidateRHIAccess());
return RenderTarget;
}