Files
UnrealEngineUWP/Engine/Source/Runtime/RenderCore/Private/RenderGraphBuilder.cpp
zach bethel 6008c51bbf Minor fixes to RenderGraph implementation.
#rb none
#jira none

[CL 7326766 by zach bethel in 4.23 branch]
2019-07-16 15:10:44 -04:00

929 lines
24 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "RenderGraphBuilder.h"
#include "RenderCore.h"
#include "RenderTargetPool.h"
#include "RenderGraphResourcePool.h"
#include "RenderGraphBarrierBatcher.h"
#include "VisualizeTexture.h"
#include "ProfilingDebugging/CsvProfiler.h"
namespace
{
const int32 kRDGEmitWarningsOnce = 1;
#if RDG_ENABLE_DEBUG
int32 GRDGImmediateMode = 0;
FAutoConsoleVariableRef CVarImmediateMode(
TEXT("r.RDG.ImmediateMode"),
GRDGImmediateMode,
TEXT("Executes passes as they get created. Useful to have a callstack of the wiring code when crashing in the pass' lambda."),
ECVF_RenderThreadSafe);
int32 GRDGDebug = 0;
FAutoConsoleVariableRef CVarRDGDebug(
TEXT("r.RDG.Debug"),
GRDGDebug,
TEXT("Allow to output warnings for inefficiencies found during wiring and execution of the passes.\n")
TEXT(" 0: disabled;\n")
TEXT(" 1: emit warning once (default);\n")
TEXT(" 2: emit warning everytime issue is detected."),
ECVF_RenderThreadSafe);
#else
const int32 GRDGImmediateMode = 0;
const int32 GRDGDebug = 0;
#endif
} //! namespace
bool GetEmitRDGEvents()
{
#if RDG_EVENTS != RDG_EVENTS_NONE
return GetEmitDrawEvents() || GRDGDebug;
#else
return false;
#endif
}
bool IsRDGDebugEnabled()
{
return GRDGDebug != 0;
}
bool IsRDGImmediateModeEnabled()
{
return GRDGImmediateMode != 0;
}
void InitRenderGraph()
{
#if RDG_ENABLE_DEBUG_WITH_ENGINE
if (FParse::Param(FCommandLine::Get(), TEXT("rdgimmediate")))
{
GRDGImmediateMode = 1;
}
if (FParse::Param(FCommandLine::Get(), TEXT("rdgdebug")))
{
GRDGDebug = 1;
}
#endif
}
void EmitRDGWarning(const FString& WarningMessage)
{
if (!GRDGDebug)
{
return;
}
static TSet<FString> GAlreadyEmittedWarnings;
if (GRDGDebug == kRDGEmitWarningsOnce)
{
if (!GAlreadyEmittedWarnings.Contains(WarningMessage))
{
GAlreadyEmittedWarnings.Add(WarningMessage);
UE_LOG(LogRendererCore, Warning, TEXT("%s"), *WarningMessage);
}
}
else
{
UE_LOG(LogRendererCore, Warning, TEXT("%s"), *WarningMessage);
}
}
void FRDGBuilder::TickPoolElements()
{
GRenderGraphResourcePool.TickPoolElements();
}
FRDGBuilder::FRDGBuilder(FRHICommandListImmediate& InRHICmdList)
: RHICmdList(InRHICmdList)
, MemStack(FMemStack::Get())
, EventScopeStack(RHICmdList)
, StatScopeStack(RHICmdList)
{}
void FRDGBuilder::Execute()
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(FRDGBuilder_Execute);
IF_RDG_ENABLE_DEBUG(Validation.ValidateExecuteBegin());
EventScopeStack.BeginExecute();
StatScopeStack.BeginExecute();
if (!GRDGImmediateMode)
{
WalkGraphDependencies();
QUICK_SCOPE_CYCLE_COUNTER(STAT_FRDGBuilder_Execute);
for (const FRDGPass* Pass : Passes)
{
ExecutePass(Pass);
}
}
EventScopeStack.EndExecute();
StatScopeStack.EndExecute();
ProcessDeferredInternalResourceQueries();
IF_RDG_ENABLE_DEBUG(Validation.ValidateExecuteEnd());
DestructPasses();
}
void FRDGBuilder::AddPassInternal(FRDGPass* Pass)
{
IF_RDG_ENABLE_DEBUG(Validation.ValidateAddPass(Pass));
Pass->EventScope = EventScopeStack.GetCurrentScope();
Pass->StatScope = StatScopeStack.GetCurrentScope();
Passes.Emplace(Pass);
if (GRDGImmediateMode)
{
ExecutePass(Pass);
}
VisualizePassOutputs(Pass);
}
void FRDGBuilder::VisualizePassOutputs(const FRDGPass* Pass)
{
#if SUPPORTS_VISUALIZE_TEXTURE
if (!GVisualizeTexture.bEnabled)
{
return;
}
FRDGPassParameterStruct ParameterStruct = Pass->GetParameters();
const uint32 ParameterCount = ParameterStruct.GetParameterCount();
for (uint32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
FRDGPassParameter Parameter = ParameterStruct.GetParameter(ParameterIndex);
switch (Parameter.GetType())
{
case UBMT_RDG_TEXTURE_UAV:
{
if (FRDGTextureUAVRef UAV = Parameter.GetAsTextureUAV())
{
if (FRDGTextureRef Texture = UAV->Desc.Texture)
{
if (GVisualizeTexture.ShouldCapture(Texture->Name))
{
GVisualizeTexture.CreateContentCapturePass(*this, Texture);
}
}
}
}
break;
case UBMT_RENDER_TARGET_BINDING_SLOTS:
{
const FRenderTargetBindingSlots& RenderTargetBindingSlots = Parameter.GetAsRenderTargetBindingSlots();
const auto& DepthStencil = RenderTargetBindingSlots.DepthStencil;
const auto& RenderTargets = RenderTargetBindingSlots.Output;
if (FRDGTextureRef Texture = DepthStencil.GetTexture())
{
const bool bHasStoreAction = DepthStencil.GetDepthStoreAction() != ERenderTargetStoreAction::ENoAction || DepthStencil.GetStencilStoreAction() != ERenderTargetStoreAction::ENoAction;
if (bHasStoreAction && GVisualizeTexture.ShouldCapture(Texture->Name))
{
GVisualizeTexture.CreateContentCapturePass(*this, Texture);
}
}
const uint32 RenderTargetCount = RenderTargets.Num();
for (uint32 RenderTargetIndex = 0; RenderTargetIndex < RenderTargetCount; ++RenderTargetIndex)
{
const FRenderTargetBinding& RenderTarget = RenderTargets[RenderTargetIndex];
if (FRDGTextureRef Texture = RenderTarget.GetTexture())
{
const bool bHasStoreAction = RenderTarget.GetStoreAction() != ERenderTargetStoreAction::ENoAction;
if (bHasStoreAction && GVisualizeTexture.ShouldCapture(Texture->Name))
{
GVisualizeTexture.CreateContentCapturePass(*this, Texture);
}
}
else
{
break;
}
}
}
break;
default:
break;
}
}
#endif
}
void FRDGBuilder::WalkGraphDependencies()
{
for (const FRDGPass* Pass : Passes)
{
FRDGPassParameterStruct ParameterStruct = Pass->GetParameters();
const uint32 ParameterCount = ParameterStruct.GetParameterCount();
for (uint32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
FRDGPassParameter Parameter = ParameterStruct.GetParameter(ParameterIndex);
switch (Parameter.GetType())
{
case UBMT_RDG_TEXTURE:
case UBMT_RDG_BUFFER:
{
if (FRDGTrackedResourceRef Resource = Parameter.GetAsTrackedResource())
{
Resource->ReferenceCount++;
}
}
break;
case UBMT_RDG_TEXTURE_SRV:
{
if (FRDGTextureSRVRef SRV = Parameter.GetAsTextureSRV())
{
SRV->Desc.Texture->ReferenceCount++;
}
}
break;
case UBMT_RDG_TEXTURE_UAV:
{
if (FRDGTextureUAVRef UAV = Parameter.GetAsTextureUAV())
{
UAV->Desc.Texture->ReferenceCount++;
}
}
break;
case UBMT_RDG_BUFFER_SRV:
{
if (FRDGBufferSRVRef SRV = Parameter.GetAsBufferSRV())
{
SRV->Desc.Buffer->ReferenceCount++;
}
}
break;
case UBMT_RDG_BUFFER_UAV:
{
if (FRDGBufferUAVRef UAV = Parameter.GetAsBufferUAV())
{
UAV->Desc.Buffer->ReferenceCount++;
}
}
break;
case UBMT_RENDER_TARGET_BINDING_SLOTS:
{
const FRenderTargetBindingSlots& RenderTargetBindingSlots = Parameter.GetAsRenderTargetBindingSlots();
const auto& DepthStencil = RenderTargetBindingSlots.DepthStencil;
const auto& RenderTargets = RenderTargetBindingSlots.Output;
const uint32 RenderTargetCount = RenderTargets.Num();
for (uint32 RenderTargetIndex = 0; RenderTargetIndex < RenderTargetCount; ++RenderTargetIndex)
{
const FRenderTargetBinding& RenderTarget = RenderTargets[RenderTargetIndex];
if (FRDGTextureRef Texture = RenderTarget.GetTexture())
{
Texture->ReferenceCount++;
}
else
{
break;
}
}
if (FRDGTextureRef Texture = DepthStencil.GetTexture())
{
Texture->ReferenceCount++;
}
}
break;
default:
break;
}
}
}
// Add additional dependencies from deferred queries.
for (const auto& Query : DeferredInternalTextureQueries)
{
Query.Texture->ReferenceCount++;
}
for (const auto& Query : DeferredInternalBufferQueries)
{
Query.Buffer->ReferenceCount++;
}
// Release external texture that have ReferenceCount == 0 and yet are already allocated.
for (auto Pair : AllocatedTextures)
{
if (Pair.Key->ReferenceCount == 0)
{
Pair.Value = nullptr;
Pair.Key->PooledRenderTarget = nullptr;
Pair.Key->ResourceRHI = nullptr;
}
}
// Release external buffers that have ReferenceCount == 0 and yet are already allocated.
for (auto Pair : AllocatedBuffers)
{
if (Pair.Key->ReferenceCount == 0)
{
Pair.Value = nullptr;
Pair.Key->PooledBuffer = nullptr;
Pair.Key->ResourceRHI = nullptr;
}
}
}
void FRDGBuilder::AllocateRHITextureIfNeeded(FRDGTexture* Texture)
{
check(Texture);
if (Texture->PooledRenderTarget)
{
return;
}
check(Texture->ReferenceCount > 0 || GRDGImmediateMode);
TRefCountPtr<IPooledRenderTarget>& PooledRenderTarget = AllocatedTextures.FindOrAdd(Texture);
const bool bDoWriteBarrier = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Texture->Desc, PooledRenderTarget, Texture->Name, bDoWriteBarrier);
Texture->PooledRenderTarget = PooledRenderTarget;
Texture->ResourceRHI = PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture;
check(Texture->ResourceRHI);
}
void FRDGBuilder::AllocateRHIBufferIfNeeded(FRDGBuffer* Buffer)
{
check(Buffer);
if (Buffer->PooledBuffer)
{
return;
}
check(Buffer->ReferenceCount > 0 || GRDGImmediateMode);
TRefCountPtr<FPooledRDGBuffer>& AllocatedBuffer = AllocatedBuffers.FindOrAdd(Buffer);
GRenderGraphResourcePool.FindFreeBuffer(RHICmdList, Buffer->Desc, AllocatedBuffer, Buffer->Name);
check(AllocatedBuffer);
Buffer->PooledBuffer = AllocatedBuffer;
}
void FRDGBuilder::AllocateRHITextureUAVIfNeeded(FRDGTextureUAV* UAV)
{
check(UAV);
if (UAV->ResourceRHI)
{
return;
}
AllocateRHITextureIfNeeded(UAV->Desc.Texture);
UAV->ResourceRHI = UAV->Desc.Texture->PooledRenderTarget->GetRenderTargetItem().MipUAVs[UAV->Desc.MipLevel];
}
void FRDGBuilder::AllocateRHIBufferSRVIfNeeded(FRDGBufferSRV* SRV)
{
check(SRV);
if (SRV->ResourceRHI)
{
return;
}
FRDGBufferRef Buffer = SRV->Desc.Buffer;
check(Buffer->PooledBuffer);
if (Buffer->PooledBuffer->SRVs.Contains(SRV->Desc))
{
SRV->ResourceRHI = Buffer->PooledBuffer->SRVs[SRV->Desc];
return;
}
FShaderResourceViewRHIRef RHIShaderResourceView;
if (Buffer->Desc.UnderlyingType == FRDGBufferDesc::EUnderlyingType::VertexBuffer)
{
RHIShaderResourceView = RHICreateShaderResourceView(Buffer->PooledBuffer->VertexBuffer, SRV->Desc.BytesPerElement, SRV->Desc.Format);
}
else if (Buffer->Desc.UnderlyingType == FRDGBufferDesc::EUnderlyingType::StructuredBuffer)
{
RHIShaderResourceView = RHICreateShaderResourceView(Buffer->PooledBuffer->StructuredBuffer);
}
else
{
check(0);
}
SRV->ResourceRHI = RHIShaderResourceView;
Buffer->PooledBuffer->SRVs.Add(SRV->Desc, RHIShaderResourceView);
}
void FRDGBuilder::AllocateRHIBufferUAVIfNeeded(FRDGBufferUAV* UAV)
{
check(UAV);
if (UAV->ResourceRHI)
{
return;
}
FRDGBufferRef Buffer = UAV->Desc.Buffer;
AllocateRHIBufferIfNeeded(Buffer);
if (Buffer->PooledBuffer->UAVs.Contains(UAV->Desc))
{
UAV->ResourceRHI = Buffer->PooledBuffer->UAVs[UAV->Desc];
return;
}
// Hack to make sure only one UAVs is around.
Buffer->PooledBuffer->UAVs.Empty();
FUnorderedAccessViewRHIRef RHIUnorderedAccessView;
if (Buffer->Desc.UnderlyingType == FRDGBufferDesc::EUnderlyingType::VertexBuffer)
{
RHIUnorderedAccessView = RHICreateUnorderedAccessView(Buffer->PooledBuffer->VertexBuffer, UAV->Desc.Format);
}
else if (Buffer->Desc.UnderlyingType == FRDGBufferDesc::EUnderlyingType::StructuredBuffer)
{
RHIUnorderedAccessView = RHICreateUnorderedAccessView(Buffer->PooledBuffer->StructuredBuffer, UAV->Desc.bSupportsAtomicCounter, UAV->Desc.bSupportsAppendBuffer);
}
else
{
check(0);
}
UAV->ResourceRHI = RHIUnorderedAccessView;
Buffer->PooledBuffer->UAVs.Add(UAV->Desc, RHIUnorderedAccessView);
}
void FRDGBuilder::ExecutePass(const FRDGPass* Pass)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FRDGBuilder_ExecutePass);
IF_RDG_ENABLE_DEBUG(Validation.ValidateExecutePassBegin(Pass));
FRHIRenderPassInfo RPInfo;
bool bHasRenderTargets = false;
PrepareResourcesForExecute(Pass, &RPInfo, &bHasRenderTargets);
EventScopeStack.BeginExecutePass(Pass);
StatScopeStack.BeginExecutePass(Pass);
if (!Pass->IsCompute())
{
check(bHasRenderTargets);
RHICmdList.BeginRenderPass( RPInfo, Pass->GetName() );
}
else
{
UnbindRenderTargets(RHICmdList);
}
Pass->Execute(RHICmdList);
if (bHasRenderTargets)
{
RHICmdList.EndRenderPass();
}
EventScopeStack.EndExecutePass();
IF_RDG_ENABLE_DEBUG(Validation.ValidateExecutePassEnd(Pass));
// Can't release resources with immediate mode, because don't know if whether they are gonna be used.
if (!GRDGImmediateMode)
{
ReleaseUnreferencedResources(Pass);
}
}
void FRDGBuilder::PrepareResourcesForExecute(const FRDGPass* Pass, struct FRHIRenderPassInfo* OutRPInfo, bool* bOutHasRenderTargets)
{
check(Pass);
OutRPInfo->NumUAVs = 0;
OutRPInfo->UAVIndex = 0;
const bool bIsCompute = Pass->IsCompute();
FRDGBarrierBatcher BarrierBatcher(RHICmdList, Pass);
// NOTE: When generating mips, we don't perform any transitions on textures. They are done implicitly by the RHI.
const bool bGeneratingMips = Pass->IsGenerateMips();
FRDGPassParameterStruct ParameterStruct = Pass->GetParameters();
const uint32 ParameterCount = ParameterStruct.GetParameterCount();
for (uint32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
FRDGPassParameter Parameter = ParameterStruct.GetParameter(ParameterIndex);
switch (Parameter.GetType())
{
case UBMT_RDG_TEXTURE:
{
if (FRDGTextureRef Texture = Parameter.GetAsTexture())
{
check(Texture->PooledRenderTarget);
check(Texture->ResourceRHI);
BarrierBatcher.QueueTransitionTexture(Texture, FRDGResourceState::EAccess::Read);
}
}
break;
case UBMT_RDG_TEXTURE_SRV:
{
if (FRDGTextureSRVRef SRV = Parameter.GetAsTextureSRV())
{
FRDGTextureRef Texture = SRV->Desc.Texture;
// Might be the first time using this render graph SRV, so need to setup the cached rhi resource.
if (!SRV->ResourceRHI)
{
SRV->ResourceRHI = Texture->PooledRenderTarget->GetRenderTargetItem().MipSRVs[SRV->Desc.MipLevel];
}
BarrierBatcher.QueueTransitionTexture(Texture, FRDGResourceState::EAccess::Read);
}
}
break;
case UBMT_RDG_TEXTURE_UAV:
{
if (FRDGTextureUAVRef UAV = Parameter.GetAsTextureUAV())
{
FRDGTextureRef Texture = UAV->Desc.Texture;
AllocateRHITextureUAVIfNeeded(UAV);
FRHIUnorderedAccessView* UAVRHI = UAV->GetRHI();
if (!bIsCompute)
{
OutRPInfo->UAVs[OutRPInfo->NumUAVs++] = UAVRHI; // Bind UAVs in declaration order
}
BarrierBatcher.QueueTransitionUAV(UAVRHI, Texture, FRDGResourceState::EAccess::Write);
}
}
break;
case UBMT_RDG_BUFFER:
{
if (FRDGBufferRef Buffer = Parameter.GetAsBuffer())
{
// TODO(RDG): super hacky, find the UAV and transition it. Hopefully there is one...
check(Buffer->PooledBuffer);
check(Buffer->PooledBuffer->UAVs.Num() == 1);
FRHIUnorderedAccessView* UAVRHI = Buffer->PooledBuffer->UAVs.CreateIterator().Value();
BarrierBatcher.QueueTransitionUAV(UAVRHI, Buffer, FRDGResourceState::EAccess::Read);
}
}
break;
case UBMT_RDG_BUFFER_SRV:
{
if (FRDGBufferSRVRef SRV = Parameter.GetAsBufferSRV())
{
FRDGBufferRef Buffer = SRV->Desc.Buffer;
AllocateRHIBufferSRVIfNeeded(SRV);
// TODO(RDG): super hacky, find the UAV and transition it. Hopefully there is one...
check(Buffer->PooledBuffer);
check(Buffer->PooledBuffer->UAVs.Num() == 1);
FRHIUnorderedAccessView* UAVRHI = Buffer->PooledBuffer->UAVs.CreateIterator().Value();
BarrierBatcher.QueueTransitionUAV(UAVRHI, Buffer, FRDGResourceState::EAccess::Read);
}
}
break;
case UBMT_RDG_BUFFER_UAV:
{
if (FRDGBufferUAVRef UAV = Parameter.GetAsBufferUAV())
{
FRDGBufferRef Buffer = UAV->Desc.Buffer;
AllocateRHIBufferUAVIfNeeded(UAV);
FRHIUnorderedAccessView* UAVRHI = UAV->GetRHI();
if (!bIsCompute)
{
OutRPInfo->UAVs[OutRPInfo->NumUAVs++] = UAVRHI; // Bind UAVs in declaration order
}
BarrierBatcher.QueueTransitionUAV(UAVRHI, Buffer, FRDGResourceState::EAccess::Write);
}
}
break;
case UBMT_RENDER_TARGET_BINDING_SLOTS:
{
const FRenderTargetBindingSlots& RenderTargetBindingSlots = Parameter.GetAsRenderTargetBindingSlots();
const auto& RenderTargets = RenderTargetBindingSlots.Output;
const auto& DepthStencil = RenderTargetBindingSlots.DepthStencil;
const uint32 RenderTargetCount = RenderTargets.Num();
uint32 ValidRenderTargetCount = 0;
uint32 ValidDepthStencilCount = 0;
uint32 SampleCount = 0;
for (uint32 RenderTargetIndex = 0; RenderTargetIndex < RenderTargetCount; RenderTargetIndex++)
{
const FRenderTargetBinding& RenderTarget = RenderTargets[RenderTargetIndex];
if (FRDGTextureRef Texture = RenderTarget.GetTexture())
{
AllocateRHITextureIfNeeded(Texture);
auto& OutRenderTarget = OutRPInfo->ColorRenderTargets[RenderTargetIndex];
// TODO(RDG): Clean up this legacy hack of the FPooledRenderTarget that can have TargetableTexture != ShaderResourceTexture
// for MSAA texture. Instead the two texture should be independent FRDGTexture explicitly handled by the user code.
FRHITexture* TargetableTexture = Texture->PooledRenderTarget->GetRenderTargetItem().TargetableTexture;
FRHITexture* ShaderResourceTexture = Texture->PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture;
// TODO(RDG): Looks like the store action on FRenderTargetBinding is not necessary, because: if want to bind a RT,
// that is most certainly to modify it as oposed to depth-stencil that might be for read only purposes. And if modify
// this resource, that certainly for being used by another pass. Otherwise this pass should be culled.
//
// TODO(RDG): The load store action could actually be optimised by render graph for tile hardware when there is multiple
// consecutive rasterizer passes that have RDG resource as render target, a bit like resource transitions.
ERenderTargetStoreAction StoreAction = RenderTarget.GetStoreAction();
// Automatically switch the store action to MSAA resolve when there is MSAA texture.
if (TargetableTexture != ShaderResourceTexture && Texture->Desc.NumSamples > 1 && StoreAction == ERenderTargetStoreAction::EStore)
{
StoreAction = ERenderTargetStoreAction::EMultisampleResolve;
}
// TODO(RDG): should force TargetableTexture == ShaderResourceTexture with MSAA, and instead have an explicit MSAA resolve pass.
OutRenderTarget.RenderTarget = TargetableTexture;
OutRenderTarget.ResolveTarget = ShaderResourceTexture != TargetableTexture ? ShaderResourceTexture : nullptr;
OutRenderTarget.ArraySlice = -1;
OutRenderTarget.MipIndex = RenderTarget.GetMipIndex();
OutRenderTarget.Action = MakeRenderTargetActions(RenderTarget.GetLoadAction(), StoreAction);
BarrierBatcher.QueueTransitionTexture(Texture, FRDGResourceState::EAccess::Write);
SampleCount |= OutRenderTarget.RenderTarget->GetNumSamples();
ValidRenderTargetCount++;
}
else
{
break;
}
}
OutRPInfo->UAVIndex = ValidRenderTargetCount;
if (FRDGTextureRef Texture = DepthStencil.GetTexture())
{
AllocateRHITextureIfNeeded(Texture);
auto& OutDepthStencil = OutRPInfo->DepthStencilRenderTarget;
// TODO(RDG): Addresses the TODO of the color scene render target.
ensureMsgf(Texture->Desc.NumSamples == 1, TEXT("MSAA dept-stencil render target not yet supported."));
OutDepthStencil.DepthStencilTarget = Texture->PooledRenderTarget->GetRenderTargetItem().TargetableTexture;
OutDepthStencil.ResolveTarget = nullptr;
OutDepthStencil.Action = MakeDepthStencilTargetActions(
MakeRenderTargetActions(DepthStencil.GetDepthLoadAction(), DepthStencil.GetDepthStoreAction()),
MakeRenderTargetActions(DepthStencil.GetStencilLoadAction(), DepthStencil.GetStencilStoreAction()));
OutDepthStencil.ExclusiveDepthStencil = DepthStencil.GetDepthStencilAccess();
BarrierBatcher.QueueTransitionTexture(Texture,
DepthStencil.GetDepthStencilAccess().IsAnyWrite() ?
FRDGResourceState::EAccess::Write :
FRDGResourceState::EAccess::Read);
SampleCount |= OutDepthStencil.DepthStencilTarget->GetNumSamples();
ValidDepthStencilCount++;
}
OutRPInfo->bIsMSAA = SampleCount > 1;
*bOutHasRenderTargets = ValidRenderTargetCount + ValidDepthStencilCount > 0;
}
break;
default:
break;
}
}
OutRPInfo->bGeneratingMips = bGeneratingMips;
}
void FRDGBuilder::ReleaseRHITextureIfUnreferenced(FRDGTexture* Texture)
{
check(Texture->ReferenceCount > 0);
Texture->ReferenceCount--;
if (Texture->ReferenceCount == 0)
{
Texture->PooledRenderTarget = nullptr;
Texture->ResourceRHI = nullptr;
AllocatedTextures.FindChecked(Texture) = nullptr;
}
}
void FRDGBuilder::ReleaseRHIBufferIfUnreferenced(FRDGBuffer* Buffer)
{
check(Buffer->ReferenceCount > 0);
Buffer->ReferenceCount--;
if (Buffer->ReferenceCount == 0)
{
Buffer->PooledBuffer = nullptr;
Buffer->ResourceRHI = nullptr;
AllocatedBuffers.FindChecked(Buffer) = nullptr;
}
}
void FRDGBuilder::ReleaseUnreferencedResources(const FRDGPass* Pass)
{
FRDGPassParameterStruct ParameterStruct = Pass->GetParameters();
const uint32 ParameterCount = ParameterStruct.GetParameterCount();
for (uint32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
FRDGPassParameter Parameter = ParameterStruct.GetParameter(ParameterIndex);
switch (Parameter.GetType())
{
case UBMT_RDG_TEXTURE:
{
if (FRDGTextureRef Texture = Parameter.GetAsTexture())
{
ReleaseRHITextureIfUnreferenced(Texture);
}
}
break;
case UBMT_RDG_TEXTURE_SRV:
{
if (FRDGTextureSRVRef SRV = Parameter.GetAsTextureSRV())
{
ReleaseRHITextureIfUnreferenced(SRV->Desc.Texture);
}
}
break;
case UBMT_RDG_TEXTURE_UAV:
{
if (FRDGTextureUAVRef UAV = Parameter.GetAsTextureUAV())
{
ReleaseRHITextureIfUnreferenced(UAV->Desc.Texture);
}
}
break;
case UBMT_RDG_BUFFER:
{
if (FRDGBufferRef Buffer = Parameter.GetAsBuffer())
{
ReleaseRHIBufferIfUnreferenced(Buffer);
}
}
break;
case UBMT_RDG_BUFFER_SRV:
{
if (FRDGBufferSRVRef SRV = Parameter.GetAsBufferSRV())
{
ReleaseRHIBufferIfUnreferenced(SRV->Desc.Buffer);
}
}
break;
case UBMT_RDG_BUFFER_UAV:
{
if (FRDGBufferUAVRef UAV = Parameter.GetAsBufferUAV())
{
ReleaseRHIBufferIfUnreferenced(UAV->Desc.Buffer);
}
}
break;
case UBMT_RENDER_TARGET_BINDING_SLOTS:
{
const FRenderTargetBindingSlots& RenderTargetBindingSlots = Parameter.GetAsRenderTargetBindingSlots();
const auto& RenderTargets = RenderTargetBindingSlots.Output;
const auto& DepthStencil = RenderTargetBindingSlots.DepthStencil;
const uint32 RenderTargetCount = RenderTargets.Num();
for (uint32 RenderTargetIndex = 0; RenderTargetIndex < RenderTargetCount; RenderTargetIndex++)
{
const FRenderTargetBinding& RenderTarget = RenderTargets[RenderTargetIndex];
if (FRDGTextureRef Texture = RenderTarget.GetTexture())
{
ReleaseRHITextureIfUnreferenced(Texture);
}
else
{
break;
}
}
if (FRDGTextureRef Texture = DepthStencil.GetTexture())
{
ReleaseRHITextureIfUnreferenced(Texture);
}
}
break;
default:
break;
}
}
}
void FRDGBuilder::ProcessDeferredInternalResourceQueries()
{
FRDGBarrierBatcher BarrierBatcher(RHICmdList, nullptr);
for (const auto& Query : DeferredInternalTextureQueries)
{
check(Query.Texture->PooledRenderTarget);
if (Query.bTransitionToRead)
{
BarrierBatcher.QueueTransitionTexture(Query.Texture, FRDGResourceState::EAccess::Read);
}
*Query.OutTexturePtr = AllocatedTextures.FindChecked(Query.Texture);
if (!GRDGImmediateMode)
{
ReleaseRHITextureIfUnreferenced(Query.Texture);
}
}
for (const auto& Query : DeferredInternalBufferQueries)
{
*Query.OutBufferPtr = AllocatedBuffers.FindChecked(Query.Buffer);
// No need to manually release in immediate mode, since it is done directly when emptying AllocatedTextures in DestructPasses().
if (!GRDGImmediateMode)
{
ReleaseRHIBufferIfUnreferenced(Query.Buffer);
}
}
}
void FRDGBuilder::DestructPasses()
{
for (int32 PassIndex = Passes.Num() - 1; PassIndex >= 0; --PassIndex)
{
Passes[PassIndex]->~FRDGPass();
}
Passes.Empty();
DeferredInternalTextureQueries.Empty();
DeferredInternalBufferQueries.Empty();
ExternalTextures.Empty();
ExternalBuffers.Empty();
AllocatedTextures.Empty();
AllocatedBuffers.Empty();
}
void FRDGBuilder::BeginEventScope(FRDGEventName&& ScopeName)
{
EventScopeStack.BeginScope(Forward<FRDGEventName&&>(ScopeName));
}
void FRDGBuilder::EndEventScope()
{
EventScopeStack.EndScope();
}
void FRDGBuilder::BeginStatScope(const FName& Name, const FName& StatName)
{
StatScopeStack.BeginScope(Name, StatName);
}
void FRDGBuilder::EndStatScope()
{
StatScopeStack.EndScope();
}