2020-07-06 18:58:26 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "RenderGraphResources.h"
|
2020-09-24 00:43:27 -04:00
|
|
|
#include "RenderGraphPass.h"
|
2020-07-06 18:58:26 -04:00
|
|
|
#include "RenderGraphPrivate.h"
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
FRDGParentResource::FRDGParentResource(const TCHAR* InName, const ERDGParentResourceType InType)
|
2020-07-06 18:58:26 -04:00
|
|
|
: FRDGResource(InName)
|
|
|
|
|
, Type(InType)
|
2020-09-24 00:43:27 -04:00
|
|
|
, bExternal(0)
|
|
|
|
|
, bExtracted(0)
|
2020-11-17 17:04:48 -04:00
|
|
|
, bProduced(0)
|
2020-09-24 00:43:27 -04:00
|
|
|
, bTransient(0)
|
2021-04-21 13:03:28 -04:00
|
|
|
, bUserSetNonTransient(0)
|
2021-04-06 11:45:09 -04:00
|
|
|
, bFinalizedAccess(0)
|
2020-09-24 00:43:27 -04:00
|
|
|
, bLastOwner(1)
|
2021-05-20 19:00:29 -04:00
|
|
|
, bCulled(1)
|
2020-09-24 00:43:27 -04:00
|
|
|
, bUsedByAsyncComputePass(0)
|
2021-05-25 20:46:17 -04:00
|
|
|
, bQueuedForUpload(0)
|
2021-07-14 11:51:24 -04:00
|
|
|
, bSwapChain(0)
|
|
|
|
|
, bSwapChainAlreadyMoved(0)
|
2021-07-22 22:31:13 -04:00
|
|
|
, bUAVAccessed(0)
|
2020-07-06 18:58:26 -04:00
|
|
|
{}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
bool FRDGProducerState::IsDependencyRequired(FRDGProducerState LastProducer, ERHIPipeline LastPipeline, FRDGProducerState NextState, ERHIPipeline NextPipeline)
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-11-30 13:27:08 -04:00
|
|
|
/** 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.
|
|
|
|
|
*/
|
2020-07-06 18:58:26 -04:00
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// 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)
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// Only certain platforms allow multi-pipe UAV access.
|
|
|
|
|
const ERHIAccess MultiPipelineUAVMask = ERHIAccess::UAVMask & GRHIMultiPipelineMergeableAccessMask;
|
2020-07-06 18:58:26 -04:00
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// 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))
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-11-30 13:27:08 -04:00
|
|
|
|
|
|
|
|
// Everything else requires a dependency.
|
|
|
|
|
return true;
|
2020-07-06 18:58:26 -04:00
|
|
|
}
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
bool FRDGSubresourceState::IsMergeAllowed(ERDGParentResourceType ResourceType, const FRDGSubresourceState& Previous, const FRDGSubresourceState& Next)
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-11-30 13:27:08 -04:00
|
|
|
/** 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
const ERHIAccess AccessUnion = Previous.Access | Next.Access;
|
2020-12-07 18:45:31 -04:00
|
|
|
const ERHIAccess DSVMask = ERHIAccess::DSVRead | ERHIAccess::DSVWrite;
|
2020-07-06 18:58:26 -04:00
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
// 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)
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
// Not allowed to merge read-only and writable states.
|
|
|
|
|
if (EnumHasAnyFlags(Previous.Access, ERHIAccess::ReadOnlyExclusiveMask) && EnumHasAnyFlags(Next.Access, ERHIAccess::WritableMask))
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
return false;
|
2020-07-06 18:58:26 -04:00
|
|
|
}
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
// Not allowed to merge write-only and readable states.
|
|
|
|
|
if (EnumHasAnyFlags(Previous.Access, ERHIAccess::WriteOnlyExclusiveMask) && EnumHasAnyFlags(Next.Access, ERHIAccess::ReadableMask))
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 18:45:31 -04:00
|
|
|
// Depth Read / Write should never merge with anything other than itself.
|
|
|
|
|
if (EnumHasAllFlags(AccessUnion, DSVMask) && EnumHasAnyFlags(AccessUnion, ~DSVMask))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// Filter out platform-specific unsupported mergeable states.
|
|
|
|
|
if (EnumHasAnyFlags(AccessUnion, ~GRHIMergeableAccessMask))
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// Not allowed if the resource is being used as a UAV and needs a barrier.
|
|
|
|
|
if (EnumHasAnyFlags(Next.Access, ERHIAccess::UAVMask) && NeedsUAVBarrier(Previous, Next))
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// Filter out unsupported platform-specific multi-pipeline merged accesses.
|
|
|
|
|
if (EnumHasAnyFlags(AccessUnion, ~GRHIMultiPipelineMergeableAccessMask) && Previous.GetPipelines() != Next.GetPipelines())
|
2020-11-11 19:22:36 -04:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
// Not allowed to merge differing flags.
|
2020-11-11 19:22:36 -04:00
|
|
|
if (Previous.Flags != Next.Flags)
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 13:27:08 -04:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-19 17:54:58 -04:00
|
|
|
void FRDGUniformBuffer::InitRHI()
|
|
|
|
|
{
|
|
|
|
|
check(!HasRHI());
|
|
|
|
|
|
|
|
|
|
const EUniformBufferValidation Validation =
|
|
|
|
|
#if RDG_ENABLE_DEBUG
|
|
|
|
|
EUniformBufferValidation::ValidateResources;
|
|
|
|
|
#else
|
|
|
|
|
EUniformBufferValidation::None;
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-08-19 23:56:04 -04:00
|
|
|
const FRDGParameterStruct& PassParameters = GetParameters();
|
|
|
|
|
UniformBufferRHI = RHICreateUniformBuffer(PassParameters.GetContents(), PassParameters.GetLayoutPtr(), UniformBuffer_SingleFrame, Validation);
|
2021-05-19 17:54:58 -04:00
|
|
|
ResourceRHI = UniformBufferRHI;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-17 12:44:59 -04:00
|
|
|
void FRDGPooledTexture::Finalize()
|
|
|
|
|
{
|
|
|
|
|
for (FRDGSubresourceState& SubresourceState : State)
|
|
|
|
|
{
|
|
|
|
|
SubresourceState.Finalize();
|
|
|
|
|
}
|
|
|
|
|
Owner = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FRDGPooledTexture::Reset()
|
|
|
|
|
{
|
|
|
|
|
InitAsWholeResource(State);
|
|
|
|
|
Owner = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
FRDGTextureSubresourceRange FRDGTexture::GetSubresourceRangeSRV() const
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
FRDGTextureSubresourceRange Range = GetSubresourceRange();
|
2020-07-06 18:58:26 -04:00
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
// 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;
|
2020-07-06 18:58:26 -04:00
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
return Range;
|
2020-07-06 18:58:26 -04:00
|
|
|
}
|
|
|
|
|
|
2021-05-19 17:54:58 -04:00
|
|
|
IPooledRenderTarget* FRDGTexture::GetPooledRenderTarget() const
|
|
|
|
|
{
|
|
|
|
|
IF_RDG_ENABLE_DEBUG(ValidateRHIAccess());
|
|
|
|
|
return PooledRenderTarget;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-17 12:44:59 -04:00
|
|
|
void FRDGTexture::SetRHI(FPooledRenderTarget* InPooledRenderTarget)
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2021-05-19 17:54:58 -04:00
|
|
|
Allocation = TRefCountPtr<FPooledRenderTarget>(InPooledRenderTarget);
|
2021-03-17 12:44:59 -04:00
|
|
|
PooledRenderTarget = InPooledRenderTarget;
|
2020-07-06 18:58:26 -04:00
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
if (!InPooledRenderTarget->HasRDG())
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
InPooledRenderTarget->InitRDG();
|
2020-07-06 18:58:26 -04:00
|
|
|
}
|
2020-09-24 00:43:27 -04:00
|
|
|
|
2021-03-17 12:44:59 -04:00
|
|
|
SetRHI(InPooledRenderTarget->GetRDG(RenderTargetTexture));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FRDGTexture::SetRHI(FRDGPooledTexture* InPooledTexture)
|
|
|
|
|
{
|
|
|
|
|
PooledTexture = InPooledTexture;
|
2020-09-24 00:43:27 -04:00
|
|
|
State = &PooledTexture->State;
|
2021-04-26 14:12:08 -04:00
|
|
|
ViewCache = &PooledTexture->ViewCache;
|
2020-09-24 00:43:27 -04:00
|
|
|
|
|
|
|
|
// Return the previous owner and assign this texture as the new one.
|
2021-03-17 12:44:59 -04:00
|
|
|
FRDGTextureRef PreviousOwner = PooledTexture->Owner;
|
2020-09-24 00:43:27 -04:00
|
|
|
PooledTexture->Owner = this;
|
|
|
|
|
|
|
|
|
|
// Link the previous alias to this one.
|
2021-03-17 12:44:59 -04:00
|
|
|
if (PreviousOwner)
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2021-03-17 12:44:59 -04:00
|
|
|
PreviousOwner->NextOwner = Handle;
|
|
|
|
|
PreviousOwner->bLastOwner = false;
|
2020-07-06 18:58:26 -04:00
|
|
|
}
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
ResourceRHI = PooledTexture->GetRHI();
|
2021-04-21 13:03:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FRDGTexture::SetRHI(FRHITransientTexture* InTransientTexture, FRDGAllocator& Allocator)
|
|
|
|
|
{
|
|
|
|
|
TransientTexture = InTransientTexture;
|
|
|
|
|
State = Allocator.AllocNoDestruct<FRDGTextureSubresourceState>();
|
2021-04-26 14:12:08 -04:00
|
|
|
ViewCache = &InTransientTexture->ViewCache;
|
|
|
|
|
ResourceRHI = InTransientTexture->GetRHI();
|
2021-04-21 13:03:28 -04:00
|
|
|
|
|
|
|
|
bTransient = true;
|
2020-07-06 18:58:26 -04:00
|
|
|
}
|
|
|
|
|
|
2021-05-19 17:54:58 -04:00
|
|
|
void FRDGTexture::Finalize(FRDGPooledTextureArray& PooledTextureArray)
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
|
|
|
|
checkf(NextOwner.IsNull() == !!bLastOwner, TEXT("NextOwner must match bLastOwner."));
|
2021-04-08 00:39:34 -04:00
|
|
|
checkf(!bExtracted || bLastOwner, TEXT("Extracted resources must be the last owner of a resource."));
|
2020-07-06 18:58:26 -04:00
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
if (bLastOwner)
|
|
|
|
|
{
|
2021-04-21 13:03:28 -04:00
|
|
|
if (bTransient)
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
2021-04-21 13:03:28 -04:00
|
|
|
// Manually deconstruct the allocated state so as not to invoke overhead from the allocators destructor tracking.
|
|
|
|
|
State->~FRDGTextureSubresourceState();
|
|
|
|
|
State = nullptr;
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-04-21 13:03:28 -04:00
|
|
|
// External and extracted resources are user controlled, so we cannot assume the texture stays in its final state.
|
|
|
|
|
if (bExternal || bExtracted)
|
|
|
|
|
{
|
|
|
|
|
PooledTexture->Reset();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PooledTexture->Finalize();
|
|
|
|
|
}
|
2020-09-24 00:43:27 -04:00
|
|
|
|
2021-04-21 13:03:28 -04:00
|
|
|
// Restore the reference to the last owner in the aliasing chain; not necessary for the transient resource allocator.
|
|
|
|
|
if (PooledRenderTarget)
|
|
|
|
|
{
|
2021-05-19 17:54:58 -04:00
|
|
|
PooledTextureArray.Emplace(PooledRenderTarget);
|
2021-04-21 13:03:28 -04:00
|
|
|
}
|
2021-03-17 12:44:59 -04:00
|
|
|
}
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
2021-05-19 17:54:58 -04:00
|
|
|
|
|
|
|
|
// This releases the reference without invoking a virtual function call.
|
|
|
|
|
TRefCountPtr<FPooledRenderTarget>(MoveTemp(Allocation));
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
|
|
|
|
|
2021-03-17 12:44:59 -04:00
|
|
|
void FRDGBuffer::SetRHI(FRDGPooledBuffer* InPooledBuffer)
|
2020-07-06 18:58:26 -04:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
// Return the previous owner and assign this buffer as the new one.
|
2021-03-17 12:44:59 -04:00
|
|
|
FRDGBuffer* PreviousOwner = InPooledBuffer->Owner;
|
2020-09-24 00:43:27 -04:00
|
|
|
InPooledBuffer->Owner = this;
|
|
|
|
|
|
|
|
|
|
// Link the previous owner to this one.
|
2021-03-17 12:44:59 -04:00
|
|
|
if (PreviousOwner)
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
2021-03-17 12:44:59 -04:00
|
|
|
PreviousOwner->NextOwner = Handle;
|
|
|
|
|
PreviousOwner->bLastOwner = false;
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
|
|
|
|
|
2020-07-06 18:58:26 -04:00
|
|
|
PooledBuffer = InPooledBuffer;
|
2020-09-24 00:43:27 -04:00
|
|
|
Allocation = InPooledBuffer;
|
|
|
|
|
State = &PooledBuffer->State;
|
2021-04-26 14:12:08 -04:00
|
|
|
ViewCache = &PooledBuffer->ViewCache;
|
2021-04-21 13:03:28 -04:00
|
|
|
ResourceRHI = InPooledBuffer->GetRHI();
|
2021-07-29 13:02:16 -04:00
|
|
|
|
|
|
|
|
// The upload with UAV workaround performs its own transitions outside of RDG, so fall back to Unknown for simplicity.
|
|
|
|
|
#if PLATFORM_NEEDS_GPU_UAV_RESOURCE_INIT_WORKAROUND
|
|
|
|
|
if (bUAVAccessed && bQueuedForUpload)
|
|
|
|
|
{
|
|
|
|
|
*State = FRDGSubresourceState();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2021-04-21 13:03:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FRDGBuffer::SetRHI(FRHITransientBuffer* InTransientBuffer, FRDGAllocator& Allocator)
|
|
|
|
|
{
|
|
|
|
|
TransientBuffer = InTransientBuffer;
|
|
|
|
|
State = Allocator.AllocNoDestruct<FRDGSubresourceState>();
|
2021-04-26 14:12:08 -04:00
|
|
|
ViewCache = &InTransientBuffer->ViewCache;
|
|
|
|
|
ResourceRHI = InTransientBuffer->GetRHI();
|
2021-04-21 13:03:28 -04:00
|
|
|
|
2021-05-25 17:11:34 -04:00
|
|
|
bTransient = true;
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
|
|
|
|
|
2021-05-19 17:54:58 -04:00
|
|
|
void FRDGBuffer::Finalize(FRDGPooledBufferArray& PooledBufferArray)
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
|
|
|
|
// If these fire, the graph is not tracking state properly.
|
2021-04-08 00:39:34 -04:00
|
|
|
checkf(NextOwner.IsNull() == !!bLastOwner, TEXT("NextOwner must match bLastOwner."));
|
|
|
|
|
checkf(!bExtracted || bLastOwner, TEXT("Extracted resources must be the last owner of a resource."));
|
2020-09-24 00:43:27 -04:00
|
|
|
|
|
|
|
|
if (bLastOwner)
|
|
|
|
|
{
|
2021-04-21 13:03:28 -04:00
|
|
|
if (bTransient)
|
2020-09-24 00:43:27 -04:00
|
|
|
{
|
2021-04-21 13:03:28 -04:00
|
|
|
State = nullptr;
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-04-21 13:03:28 -04:00
|
|
|
if (bExternal || bExtracted)
|
|
|
|
|
{
|
|
|
|
|
PooledBuffer->Reset();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PooledBuffer->Finalize();
|
|
|
|
|
}
|
2020-09-24 00:43:27 -04:00
|
|
|
|
2021-05-19 17:54:58 -04:00
|
|
|
PooledBufferArray.Emplace(PooledBuffer);
|
2021-04-21 13:03:28 -04:00
|
|
|
}
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
2021-05-19 17:54:58 -04:00
|
|
|
|
|
|
|
|
Allocation = nullptr;
|
2020-07-06 18:58:26 -04:00
|
|
|
}
|