You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
7064877 - RDG Resource Barrier Fixes and Improvements.
- Resource barriers are now batched and submitted as a group at the beginning of a pass.
- UAVs referencing the same underlying resource now only submit a single write barrier instead of a write + N UAV barriers.
- During a mip generation pass, barriers are not issued for textures (they are implicitly converted to the SubRWBarrier state).
- Depth / stencil read now transitions to the read state; this allows DSV + SRV to work correctly.
#jira UE-76196
#rb Guillaume.Abadie
7064884 - Fixed RHI resource transitions causing validation failures.
#rb Rolando.Caloca
#jira none
7064898 - Fixed issue where implementation of RHICopyTexture would only copy mip 0, breaking environment probe copies.
#jira UE-76203
#rb Jeremy.Moore
[CL 7066658 by zach bethel in Dev-Rendering branch]
259 lines
8.0 KiB
C++
259 lines
8.0 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RenderGraphBarrierBatcher.h"
|
|
#include "RenderGraphPass.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
/** Number of entries to reserve in the batch array. */
|
|
const uint32 kBatchReservationSize = 8;
|
|
|
|
/** Returns whether the resource is transitioning to a writable state. */
|
|
inline bool IsWriteAccessBegin(FRDGResourceState::EAccess AccessBefore, FRDGResourceState::EAccess AccessAfter)
|
|
{
|
|
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)
|
|
{
|
|
case FRDGResourceState::EPipeline::Graphics:
|
|
switch (PipelineAfter)
|
|
{
|
|
case FRDGResourceState::EPipeline::Graphics:
|
|
return EResourceTransitionPipeline::EGfxToGfx;
|
|
case FRDGResourceState::EPipeline::Compute:
|
|
return EResourceTransitionPipeline::EGfxToCompute;
|
|
}
|
|
break;
|
|
case FRDGResourceState::EPipeline::Compute:
|
|
switch (PipelineAfter)
|
|
{
|
|
case FRDGResourceState::EPipeline::Graphics:
|
|
return EResourceTransitionPipeline::EComputeToGfx;
|
|
case FRDGResourceState::EPipeline::Compute:
|
|
return EResourceTransitionPipeline::EComputeToCompute;
|
|
}
|
|
break;
|
|
}
|
|
check(false);
|
|
return EResourceTransitionPipeline::EGfxToGfx;
|
|
}
|
|
|
|
inline EResourceTransitionAccess GetResourceTransitionAccess(FRDGResourceState::EAccess AccessAfter)
|
|
{
|
|
return AccessAfter == FRDGResourceState::EAccess::Write ? EResourceTransitionAccess::EWritable : EResourceTransitionAccess::EReadable;
|
|
}
|
|
|
|
inline EResourceTransitionAccess GetResourceTransitionAccessForUAV(FRDGResourceState::EAccess AccessBefore, FRDGResourceState::EAccess AccessAfter)
|
|
{
|
|
switch (AccessAfter)
|
|
{
|
|
case FRDGResourceState::EAccess::Read:
|
|
return EResourceTransitionAccess::EReadable;
|
|
case FRDGResourceState::EAccess::Write:
|
|
// If doing a Write -> Write transition, we use a UAV barrier.
|
|
if (AccessBefore == FRDGResourceState::EAccess::Write)
|
|
{
|
|
return EResourceTransitionAccess::ERWBarrier;
|
|
}
|
|
else
|
|
{
|
|
return EResourceTransitionAccess::EWritable;
|
|
}
|
|
}
|
|
check(false);
|
|
return EResourceTransitionAccess::EMaxAccess;
|
|
}
|
|
|
|
} //! namespace
|
|
|
|
FRDGBarrierBatcher::~FRDGBarrierBatcher()
|
|
{
|
|
check(!bAllowQueueing);
|
|
}
|
|
|
|
void FRDGBarrierBatcher::Begin()
|
|
{
|
|
#if RDG_ENABLE_DEBUG
|
|
check(!bAllowQueueing);
|
|
|
|
check(!TextureUpdateMultiFrameBegins.Num());
|
|
check(!TextureUpdateMultiFrameEnds.Num());
|
|
check(!TextureBatchMap.Num());
|
|
|
|
check(!UAVUpdateMultiFrameBegins.Num());
|
|
check(!UAVUpdateMultiFrameEnds.Num());
|
|
check(!UAVBatchMap.Num());
|
|
#endif
|
|
|
|
bAllowQueueing = true;
|
|
}
|
|
|
|
void FRDGBarrierBatcher::QueueTransitionTexture(FRDGTexture* Texture, FRDGResourceState StateAfter)
|
|
{
|
|
check(Texture);
|
|
|
|
const FRDGResourceState StateBefore = Texture->State;
|
|
|
|
ValidateTransition(StateBefore, StateAfter, Texture);
|
|
|
|
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);
|
|
|
|
#if RDG_ENABLE_DEBUG
|
|
{
|
|
// We should have filtered out duplicates in the first branch of this function.
|
|
check(TextureBatch.Find(RHITexture) == INDEX_NONE);
|
|
}
|
|
#endif
|
|
|
|
TextureBatch.Add(RHITexture);
|
|
}
|
|
|
|
if (bIsMultiFrameResource && IsWriteAccessEnd(StateBefore.Access, StateAfter.Access))
|
|
{
|
|
TextureUpdateMultiFrameEnds.AddUnique(RHITexture);
|
|
}
|
|
|
|
Texture->State = StateAfter;
|
|
}
|
|
}
|
|
|
|
void FRDGBarrierBatcher::QueueTransitionUAV(
|
|
FRHIUnorderedAccessView* UAV,
|
|
FRDGTrackedResource* UnderlyingResource,
|
|
FRDGResourceState StateAfter)
|
|
{
|
|
check(UAV);
|
|
check(UnderlyingResource);
|
|
|
|
const FRDGResourceState StateBefore = UnderlyingResource->State;
|
|
|
|
ValidateTransition(StateBefore, StateAfter, UnderlyingResource);
|
|
|
|
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);
|
|
|
|
#if RDG_ENABLE_DEBUG
|
|
{
|
|
// We should have filtered out duplicates in the first branch of this function.
|
|
check(UAVBatch.Find(UAV) == INDEX_NONE);
|
|
}
|
|
#endif
|
|
|
|
UAVBatch.Add(UAV);
|
|
}
|
|
|
|
if (bIsMultiFrameResource && IsWriteAccessEnd(StateBefore.Access, StateAfter.Access))
|
|
{
|
|
UAVUpdateMultiFrameEnds.AddUnique(UAV);
|
|
}
|
|
|
|
UnderlyingResource->State = StateAfter;
|
|
}
|
|
}
|
|
|
|
void FRDGBarrierBatcher::End(FRHICommandList& RHICmdList)
|
|
{
|
|
check(bAllowQueueing);
|
|
bAllowQueueing = false;
|
|
|
|
for (FRHITexture* RHITexture : TextureUpdateMultiFrameBegins)
|
|
{
|
|
RHICmdList.BeginUpdateMultiFrameResource(RHITexture);
|
|
}
|
|
TextureUpdateMultiFrameBegins.Empty();
|
|
|
|
for (FRHIUnorderedAccessView* RHIUAV : UAVUpdateMultiFrameBegins)
|
|
{
|
|
RHICmdList.BeginUpdateMultiFrameResource(RHIUAV);
|
|
}
|
|
UAVUpdateMultiFrameBegins.Empty();
|
|
|
|
for (auto& Element : TextureBatchMap)
|
|
{
|
|
FTransitionParameters TransitionParameters = Element.Key;
|
|
FTextureBatch& Batch = Element.Value;
|
|
RHICmdList.TransitionResources(TransitionParameters.TransitionAccess, Batch.GetData(), Batch.Num());
|
|
}
|
|
TextureBatchMap.Empty();
|
|
|
|
for (auto& Element : UAVBatchMap)
|
|
{
|
|
FTransitionParameters TransitionParameters = Element.Key;
|
|
FUAVBatch& Batch = Element.Value;
|
|
RHICmdList.TransitionResources(TransitionParameters.TransitionAccess, TransitionParameters.TransitionPipeline, Batch.GetData(), Batch.Num());
|
|
}
|
|
UAVBatchMap.Empty();
|
|
|
|
for (FRHITexture* RHITexture : TextureUpdateMultiFrameEnds)
|
|
{
|
|
RHICmdList.EndUpdateMultiFrameResource(RHITexture);
|
|
}
|
|
TextureUpdateMultiFrameEnds.Empty();
|
|
|
|
for (FRHIUnorderedAccessView* RHIUAV : UAVUpdateMultiFrameEnds)
|
|
{
|
|
RHICmdList.EndUpdateMultiFrameResource(RHIUAV);
|
|
}
|
|
UAVUpdateMultiFrameEnds.Empty();
|
|
}
|
|
|
|
void FRDGBarrierBatcher::ValidateTransition(FRDGResourceState StateBefore, FRDGResourceState StateAfter, const FRDGTrackedResource* Resource)
|
|
{
|
|
#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.
|
|
checkf(
|
|
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")
|
|
TEXT("SRV and Render Target, for example.\n"), StateAfter.Pass->GetName(), Resource->Name);
|
|
}
|
|
#endif
|
|
} |