You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This change includes significant refactor work performed in //UE5/Dev-ParallelRendering. A brief summary of the work is as follows: Refactored RHI command lists - Removal of the "immediate" async compute command list - Introduced an "active pipe" on each command list, allowing RHICmdLists to record work for either graphics or async compute. Pipes can be selected using the SwitchPipeline() function, or the FRHICommandListScopedPipeline helper. - New explicit command list submission RHI API (RHIFinalizeContext, RHISubmitCommandLists). The IRHICommandContextContainer type has been removed. - Explicit GPU submission is automatically appended to the immediate command list when it is dispatched to the RHI thread. Platform RHI implementations - The new submission API has been implemented across all platforms. Some platforms required a significant refactor. #rb Mihnea.Balta,Kenzo.Terelst #jira UE-139550 #preflight 6332e3641003050806d802ef [CL 22239063 by luke thatcher in ue5-main branch]
202 lines
7.0 KiB
C++
202 lines
7.0 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, bool bImmediateFirstBarrier)
|
|
: FRDGResource(InName)
|
|
, Type(InType)
|
|
, bExternal(0)
|
|
, bExtracted(0)
|
|
, bProduced(0)
|
|
, bTransient(0)
|
|
, bForceNonTransient(0)
|
|
, TransientExtractionHint(ETransientExtractionHint::None)
|
|
, bLastOwner(1)
|
|
, bQueuedForUpload(0)
|
|
, FirstBarrier(EFirstBarrier::Split)
|
|
, ReferenceCount(IsImmediateMode() ? 1 : 0)
|
|
{
|
|
if (bSkipTracking)
|
|
{
|
|
SetExternalAccessMode(ERHIAccess::ReadOnlyExclusiveMask, ERHIPipeline::All);
|
|
AccessModeState.bLocked = 1;
|
|
AccessModeState.ActiveMode = AccessModeState.Mode;
|
|
}
|
|
|
|
if (bImmediateFirstBarrier)
|
|
{
|
|
FirstBarrier = EFirstBarrier::ImmediateRequested;
|
|
}
|
|
}
|
|
|
|
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;
|
|
} |