// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "RenderGraphParameter.h" #include "RenderGraphTextureSubresource.h" #include "RendererInterface.h" #include "RHITransientResourceAllocator.h" #include "RHIResources.h" struct FPooledRenderTarget; class FRenderTargetPool; /** Used for tracking pass producer / consumer edges in the graph for culling and pipe fencing. */ struct FRDGProducerState { /** Returns whether the next state is dependent on the last producer in the producer graph. */ static bool IsDependencyRequired(FRDGProducerState LastProducer, ERHIPipeline LastPipeline, FRDGProducerState NextState, ERHIPipeline NextPipeline); FRDGProducerState() = default; ERHIAccess Access = ERHIAccess::Unknown; FRDGPassHandle PassHandle; FRDGViewHandle NoUAVBarrierHandle; }; using FRDGProducerStatesByPipeline = TRHIPipelineArray; /** Used for tracking the state of an individual subresource during execution. */ struct FRDGSubresourceState { /** Given a before and after state, returns whether a resource barrier is required. */ static bool IsTransitionRequired(const FRDGSubresourceState& Previous, const FRDGSubresourceState& Next); /** Given a before and after state, returns whether they can be merged into a single state. */ static bool IsMergeAllowed(ERDGViewableResourceType ResourceType, const FRDGSubresourceState& Previous, const FRDGSubresourceState& Next); FRDGSubresourceState() = default; explicit FRDGSubresourceState(ERHIAccess InAccess) : Access(InAccess) {} /** Initializes the first and last pass and the pipeline. Clears any other pass state. */ void SetPass(ERHIPipeline Pipeline, FRDGPassHandle PassHandle); /** Finalizes the state at the end of the transition chain; keeps access intact. */ void Finalize(); /** Validates that the state is in a correct configuration for use. */ void Validate(); /** Returns whether the state is used by the pipeline. */ bool IsUsedBy(ERHIPipeline Pipeline) const; /** Returns the last pass across either pipe. */ FRDGPassHandle GetLastPass() const; /** Returns the first pass across either pipe. */ FRDGPassHandle GetFirstPass() const; /** Returns the pipeline mask this state is used on. */ ERHIPipeline GetPipelines() const; /** The last used access on the pass. */ ERHIAccess Access = ERHIAccess::Unknown; /** The last used transition flags on the pass. */ EResourceTransitionFlags Flags = EResourceTransitionFlags::None; /** The first pass in this state. */ FRDGPassHandlesByPipeline FirstPass; /** The last pass in this state. */ FRDGPassHandlesByPipeline LastPass; /** The last no-UAV barrier to be used by this subresource. */ FRDGViewUniqueFilter NoUAVBarrierFilter; }; using FRDGTextureSubresourceState = TRDGTextureSubresourceArray; using FRDGTextureSubresourceStateIndirect = TRDGTextureSubresourceArray; /** Generic graph resource. */ class RENDERCORE_API FRDGResource { public: FRDGResource(const FRDGResource&) = delete; virtual ~FRDGResource() = default; // Name of the resource for debugging purpose. const TCHAR* const Name = nullptr; ////////////////////////////////////////////////////////////////////////// //! The following methods may only be called during pass execution. /** Marks this resource as actually used by a resource. This is to track what dependencies on pass was actually unnecessary. */ #if RDG_ENABLE_DEBUG virtual void MarkResourceAsUsed(); #else inline void MarkResourceAsUsed() {} #endif FRHIResource* GetRHI() const { IF_RDG_ENABLE_DEBUG(ValidateRHIAccess()); return ResourceRHI; } ////////////////////////////////////////////////////////////////////////// protected: FRDGResource(const TCHAR* InName) : Name(InName) {} FRHIResource* GetRHIUnchecked() const { return ResourceRHI; } bool HasRHI() const { return ResourceRHI != nullptr; } FRHIResource* ResourceRHI = nullptr; #if RDG_ENABLE_DEBUG void ValidateRHIAccess() const; #endif private: #if RDG_ENABLE_DEBUG struct FRDGResourceDebugData* DebugData = nullptr; FRDGResourceDebugData& GetDebugData() const; #endif friend FRDGBuilder; friend FRDGUserValidation; friend FRDGBarrierValidation; }; class RENDERCORE_API FRDGUniformBuffer : public FRDGResource { public: virtual ~FRDGUniformBuffer() {}; FORCEINLINE const FRDGParameterStruct& GetParameters() const { return ParameterStruct; } #if RDG_ENABLE_DEBUG void MarkResourceAsUsed() override; #else inline void MarkResourceAsUsed() {} #endif ////////////////////////////////////////////////////////////////////////// //! The following methods may only be called during pass execution. FRHIUniformBuffer* GetRHI() const { return static_cast(FRDGResource::GetRHI()); } ////////////////////////////////////////////////////////////////////////// protected: template explicit FRDGUniformBuffer(const TParameterStruct* InParameters, const TCHAR* InName) : FRDGResource(InName) , ParameterStruct(InParameters, TParameterStruct::FTypeInfo::GetStructMetadata()) {} private: FRHIUniformBuffer* GetRHIUnchecked() const { return static_cast(FRDGResource::GetRHIUnchecked()); } void InitRHI(); const FRDGParameterStruct ParameterStruct; TRefCountPtr UniformBufferRHI; FRDGUniformBufferHandle Handle; bool bQueuedForCreate = false; friend FRDGBuilder; friend FRDGUniformBufferRegistry; friend FRDGAllocator; }; template class TRDGUniformBuffer : public FRDGUniformBuffer { public: virtual ~TRDGUniformBuffer() {}; FORCEINLINE const TRDGParameterStruct& GetParameters() const { return static_cast&>(FRDGUniformBuffer::GetParameters()); } FORCEINLINE const ParameterStructType* GetContents() const { return Parameters; } FORCEINLINE TUniformBufferRef GetRHIRef() const { return TUniformBufferRef(GetRHI()); } FORCEINLINE const ParameterStructType* operator->() const { return Parameters; } private: explicit TRDGUniformBuffer(const ParameterStructType* InParameters, const TCHAR* InName) : FRDGUniformBuffer(InParameters, InName) , Parameters(InParameters) {} const ParameterStructType* Parameters; friend FRDGBuilder; friend FRDGUniformBufferRegistry; friend FRDGAllocator; }; /** A render graph resource with an allocation lifetime tracked by the graph. May have child resources which reference it (e.g. views). */ class RENDERCORE_API FRDGViewableResource : public FRDGResource { public: /** The type of this resource; useful for casting between types. */ const ERDGViewableResourceType Type; /** Whether this resource is externally registered with the graph (i.e. the user holds a reference to the underlying resource outside the graph). */ bool IsExternal() const { return bExternal; } /** Whether this resource is has been queued for extraction at the end of graph execution. */ bool IsExtracted() const { return bExtracted; } bool IsCulled() const { return bCulled; } /** Whether a prior pass added to the graph produced contents for this resource. External resources are not considered produced * until used for a write operation. This is a union of all subresources, so any subresource write will set this to true. */ bool HasBeenProduced() const { return bProduced; } protected: FRDGViewableResource(const TCHAR* InName, ERDGViewableResourceType InType, bool bSkipTracking); static const ERHIAccess DefaultEpilogueAccess = ERHIAccess::SRVMask; enum class ETransientExtractionHint : uint8 { None, Disable, Enable }; enum class EFirstBarrier : uint8 { Split, ImmediateRequested, ImmediateConfirmed }; enum class EAccessMode : uint8 { Internal, External }; struct FAccessModeState { bool IsExternalAccess() const { return Mode == EAccessMode::External && !bQueued; } FAccessModeState() : Pipelines(ERHIPipeline::None) , Mode(EAccessMode::Internal) , bLocked(0) , bQueued(0) {} ERHIAccess Access = ERHIAccess::None; ERHIPipeline Pipelines : 2; EAccessMode Mode : 1; uint8 bLocked : 1; uint8 bQueued : 1; } AccessModeState; /** Whether this is an externally registered resource. */ uint8 bExternal : 1; /** Whether this is an extracted resource. */ uint8 bExtracted : 1; /** Whether any sub-resource has been used for write by a pass. */ uint8 bProduced : 1; /** Whether this resource is allocated through the transient resource allocator. */ uint8 bTransient : 1; /** Whether this resource cannot be made transient. */ uint8 bForceNonTransient : 1; /** Whether this resource is allowed to be both transient and extracted. */ ETransientExtractionHint TransientExtractionHint : 2; /** Whether this resource is the last owner of its allocation (i.e. nothing aliases the allocation later in the execution timeline). */ uint8 bLastOwner : 1; /** If true, the resource was not used by any pass not culled by the graph. */ uint8 bCulled : 1; /** If true, the resource has been used on an async compute pass and may have async compute states. */ uint8 bUsedByAsyncComputePass : 1; /** If true, the resource has been queued for an upload operation. */ uint8 bQueuedForUpload : 1; /** If true, this resource should skip the prologue split barrier and perform transition right away. */ EFirstBarrier FirstBarrier : 2; /** If true, the resource is access through at least one UAV. */ uint8 bUAVAccessed : 1; FRDGPassHandle FirstPass; FRDGPassHandle LastPass; /** The state of the resource at the graph epilogue. */ ERHIAccess EpilogueAccess = DefaultEpilogueAccess; private: /** Number of references in passes and deferred queries. */ uint16 ReferenceCount = 0; /** Scratch index allocated for the resource in the pass being setup. */ uint16 PassStateIndex = 0; void SetExternalAccessMode(ERHIAccess InReadOnlyAccess, ERHIPipeline InPipelines) { check(!AccessModeState.bLocked); AccessModeState.Mode = EAccessMode::External; AccessModeState.Access = InReadOnlyAccess; AccessModeState.Pipelines = InPipelines; // External access resources are not always added to the pass states (unless marked as such within the graph), so marked as not culled here. bCulled = 0; EpilogueAccess = InReadOnlyAccess; } #if RDG_ENABLE_TRACE uint16 TraceOrder = 0; TArray TracePasses; #endif #if RDG_ENABLE_DEBUG struct FRDGViewableResourceDebugData* ViewableDebugData = nullptr; FRDGViewableResourceDebugData& GetViewableDebugData() const; #endif friend FRDGBuilder; friend FRDGUserValidation; friend FRDGBarrierBatchBegin; friend FRDGResourceDumpContext; friend FRDGTrace; }; /** A render graph resource (e.g. a view) which references a single viewable resource (e.g. a texture / buffer). Provides an abstract way to access the viewable resource. */ class FRDGView : public FRDGResource { public: /** The type of this child resource; useful for casting between types. */ const ERDGViewType Type; /** Returns the referenced parent render graph resource. */ virtual FRDGViewableResource* GetParent() const = 0; ERDGViewableResourceType GetParentType() const { return ::GetParentType(Type); } FRDGViewHandle GetHandle() const { return Handle; } protected: FRDGView(const TCHAR* Name, ERDGViewType InType) : FRDGResource(Name) , Type(InType) {} private: FRDGViewHandle Handle; FRDGPassHandle LastPass; friend FRDGBuilder; friend FRDGViewRegistry; friend FRDGAllocator; }; /** Translates from a pooled render target descriptor to an RDG texture descriptor. */ inline FRDGTextureDesc Translate(const FPooledRenderTargetDesc& InDesc); /** Translates from an RHI/RDG texture descriptor to a pooled render target descriptor. */ inline FPooledRenderTargetDesc Translate(const FRHITextureDesc& InDesc); UE_DEPRECATED(5.0, "Translate with ERenderTargetTexture is deprecated. Please use the single parameter variant.") PRAGMA_DISABLE_DEPRECATION_WARNINGS inline FRDGTextureDesc Translate(const FPooledRenderTargetDesc& InDesc, ERenderTargetTexture InTexture) PRAGMA_ENABLE_DEPRECATION_WARNINGS { return Translate(InDesc); } class RENDERCORE_API FRDGPooledTexture final : public FRefCountedObject { public: FRDGPooledTexture(FRHITexture* InTexture) : Texture(InTexture) {} /** Finds a UAV matching the descriptor in the cache or creates a new one and updates the cache. */ FORCEINLINE FRHIUnorderedAccessView* GetOrCreateUAV(const FRHITextureUAVCreateInfo& UAVDesc) { return ViewCache.GetOrCreateUAV(Texture, UAVDesc); } /** Finds a SRV matching the descriptor in the cache or creates a new one and updates the cache. */ FORCEINLINE FRHIShaderResourceView* GetOrCreateSRV(const FRHITextureSRVCreateInfo& SRVDesc) { return ViewCache.GetOrCreateSRV(Texture, SRVDesc); } FORCEINLINE FRHITexture* GetRHI() const { return Texture; } private: TRefCountPtr Texture; FRHITextureViewCache ViewCache; friend FRDGBuilder; }; /** Render graph tracked Texture. */ class RENDERCORE_API FRDGTexture final : public FRDGViewableResource { public: static const ERDGViewableResourceType StaticType = ERDGViewableResourceType::Texture; const FRDGTextureDesc Desc; const ERDGTextureFlags Flags; ////////////////////////////////////////////////////////////////////////// //! The following methods may only be called during pass execution. /** Returns the allocated pooled render target. */ UE_DEPRECATED(5.0, "Accessing the underlying pooled render target has been deprecated. Use GetRHI() instead.") IPooledRenderTarget* GetPooledRenderTarget() const; /** Returns the allocated RHI texture. */ FORCEINLINE FRHITexture* GetRHI() const { return static_cast(FRDGResource::GetRHI()); } ////////////////////////////////////////////////////////////////////////// FORCEINLINE FRDGTextureHandle GetHandle() const { return Handle; } FORCEINLINE FRDGTextureSubresourceLayout GetSubresourceLayout() const { return Layout; } FORCEINLINE FRDGTextureSubresourceRange GetSubresourceRange() const { return WholeRange; } FORCEINLINE uint32 GetSubresourceCount() const { return SubresourceCount; } FORCEINLINE FRDGTextureSubresource GetSubresource(uint32 SubresourceIndex) const { return Layout.GetSubresource(SubresourceIndex); } FRDGTextureSubresourceRange GetSubresourceRangeSRV() const; private: FRDGTexture(const TCHAR* InName, const FRDGTextureDesc& InDesc, ERDGTextureFlags InFlags) : FRDGViewableResource(InName, ERDGViewableResourceType::Texture, EnumHasAnyFlags(InFlags, ERDGTextureFlags::SkipTracking)) , Desc(InDesc) , Flags(InFlags) , Layout(InDesc) , WholeRange(Layout) , SubresourceCount(Layout.GetSubresourceCount()) { MergeState.Reserve(SubresourceCount); MergeState.SetNum(SubresourceCount); LastProducers.Reserve(SubresourceCount); LastProducers.SetNum(SubresourceCount); if (EnumHasAnyFlags(Desc.Flags, ETextureCreateFlags::Presentable)) { FirstBarrier = EFirstBarrier::ImmediateRequested; } if (EnumHasAnyFlags(Desc.Flags, ETextureCreateFlags::Foveation)) { EpilogueAccess = ERHIAccess::ShadingRateSource; } } /** Returns RHI texture without access checks. */ FRHITexture* GetRHIUnchecked() const { return static_cast(FRDGResource::GetRHIUnchecked()); } /** Returns the current texture state. Only valid to call after SetRHI. */ FRDGTextureSubresourceState& GetState() { check(State); return *State; } /** The next texture to own the PooledTexture allocation during execution. */ FRDGTextureHandle NextOwner; /** The handle registered with the builder. */ FRDGTextureHandle Handle; /** The layout used to facilitate subresource transitions. */ FRDGTextureSubresourceLayout Layout; FRDGTextureSubresourceRange WholeRange; const uint32 SubresourceCount; /** The assigned render target to use during execution. Never reset. */ IPooledRenderTarget* RenderTarget = nullptr; union { /** The assigned pooled texture to use during execution. Never reset. */ FRDGPooledTexture* PooledTexture = nullptr; /** The assigned transient texture to use during execution. Never reset. */ FRHITransientTexture* TransientTexture; }; /** The assigned view cache for this texture (sourced from transient / pooled texture). Never reset. */ FRHITextureViewCache* ViewCache = nullptr; /** Valid strictly when holding a strong reference; use PooledRenderTarget instead. */ TRefCountPtr Allocation; /** Tracks subresource states as the graph is built. */ FRDGTextureSubresourceState* State = nullptr; /** Tracks merged subresource states as the graph is built. */ FRDGTextureSubresourceStateIndirect MergeState; /** Tracks pass producers for each subresource as the graph is built. */ TRDGTextureSubresourceArray LastProducers; #if RDG_ENABLE_DEBUG struct FRDGTextureDebugData* TextureDebugData = nullptr; FRDGTextureDebugData& GetTextureDebugData() const; #endif friend FRDGBuilder; friend FRDGUserValidation; friend FRDGBarrierValidation; friend FRDGTextureRegistry; friend FRDGAllocator; friend FPooledRenderTarget; friend FRDGTrace; friend FRDGTextureUAV; }; /** Render graph tracked SRV. */ class FRDGShaderResourceView : public FRDGView { public: /** Returns the allocated RHI SRV. */ FRHIShaderResourceView* GetRHI() const { return static_cast(FRDGResource::GetRHI()); } protected: FRDGShaderResourceView(const TCHAR* InName, ERDGViewType InType) : FRDGView(InName, InType) {} /** Returns the allocated RHI SRV without access checks. */ FRHIShaderResourceView* GetRHIUnchecked() const { return static_cast(FRDGResource::GetRHIUnchecked()); } }; /** Render graph tracked UAV. */ class FRDGUnorderedAccessView : public FRDGView { public: const ERDGUnorderedAccessViewFlags Flags; /** Returns the allocated RHI UAV. */ FRHIUnorderedAccessView* GetRHI() const { return static_cast(FRDGResource::GetRHI()); } protected: FRDGUnorderedAccessView(const TCHAR* InName, ERDGViewType InType, ERDGUnorderedAccessViewFlags InFlags) : FRDGView(InName, InType) , Flags(InFlags) {} /** Returns the allocated RHI UAV without access checks. */ FRHIUnorderedAccessView* GetRHIUnchecked() const { return static_cast(FRDGResource::GetRHIUnchecked()); } }; /** Descriptor for render graph tracked SRV. */ class FRDGTextureSRVDesc final : public FRHITextureSRVCreateInfo { public: FRDGTextureSRVDesc() = default; FRDGTextureSRVDesc(FRDGTexture* InTexture) { Texture = InTexture; NumMipLevels = InTexture->Desc.NumMips; if (InTexture->Desc.IsTextureArray()) { NumArraySlices = InTexture->Desc.ArraySize; } } /** Create SRV that access all sub resources of texture. */ static FRDGTextureSRVDesc Create(FRDGTextureRef Texture) { return FRDGTextureSRVDesc(Texture); } /** Create SRV that access one specific mip level. */ static FRDGTextureSRVDesc CreateForMipLevel(FRDGTextureRef Texture, int32 MipLevel) { check(MipLevel >= -1 && MipLevel <= TNumericLimits::Max()); FRDGTextureSRVDesc Desc = FRDGTextureSRVDesc::Create(Texture); Desc.MipLevel = (int8)MipLevel; Desc.NumMipLevels = 1; return Desc; } /** Create SRV that access one specific slice. */ static FRDGTextureSRVDesc CreateForSlice(FRDGTextureRef Texture, int32 SliceIndex) { check(Texture); check(Texture->Desc.IsTextureArray()); check(SliceIndex >= 0 && SliceIndex < Texture->Desc.ArraySize); FRDGTextureSRVDesc Desc = FRDGTextureSRVDesc::Create(Texture); Desc.FirstArraySlice = SliceIndex; Desc.NumArraySlices = 1; return Desc; } /** Create SRV that access all sub resources of texture with a specific pixel format. */ static FRDGTextureSRVDesc CreateWithPixelFormat(FRDGTextureRef Texture, EPixelFormat PixelFormat) { FRDGTextureSRVDesc Desc = FRDGTextureSRVDesc::Create(Texture); Desc.Format = PixelFormat; return Desc; } /** Create SRV with access to a specific meta data plane */ static FRDGTextureSRVDesc CreateForMetaData(FRDGTextureRef Texture, ERDGTextureMetaDataAccess MetaData) { FRDGTextureSRVDesc Desc = FRDGTextureSRVDesc::Create(Texture); Desc.MetaData = MetaData; return Desc; } bool operator == (const FRDGTextureSRVDesc& Other) const { return Texture == Other.Texture && FRHITextureSRVCreateInfo::operator==(Other); } bool operator != (const FRDGTextureSRVDesc& Other) const { return !(*this == Other); } friend uint32 GetTypeHash(const FRDGTextureSRVDesc& Desc) { return HashCombine(GetTypeHash(static_cast(Desc)), GetTypeHash(Desc.Texture)); } /** Returns whether this descriptor conforms to requirements. */ bool IsValid() const { if (!Texture) { return false; } return FRHITextureSRVCreateInfo::Validate(Texture->Desc, *this, Texture->Name, /* bFatal = */ false); } FRDGTextureRef Texture = nullptr; }; /** Render graph tracked SRV. */ class FRDGTextureSRV final : public FRDGShaderResourceView { public: static const ERDGViewType StaticType = ERDGViewType::TextureSRV; /** Descriptor of the graph tracked SRV. */ const FRDGTextureSRVDesc Desc; FRDGTextureRef GetParent() const override { return Desc.Texture; } FRDGTextureSubresourceRange GetSubresourceRange() const; private: FRDGTextureSRV(const TCHAR* InName, const FRDGTextureSRVDesc& InDesc) : FRDGShaderResourceView(InName, ERDGViewType::TextureSRV) , Desc(InDesc) {} friend FRDGBuilder; friend FRDGViewRegistry; friend FRDGAllocator; }; /** Descriptor for render graph tracked UAV. */ class FRDGTextureUAVDesc final : public FRHITextureUAVCreateInfo { public: FRDGTextureUAVDesc() = default; FRDGTextureUAVDesc(FRDGTextureRef InTexture, uint8 InMipLevel = 0, EPixelFormat InFormat = PF_Unknown, uint16 InFirstArraySlice = 0, uint16 InNumArraySlices = 0) : FRHITextureUAVCreateInfo(InMipLevel, InFormat != PF_Unknown ? InFormat : InTexture->Desc.UAVFormat, InFirstArraySlice, InNumArraySlices) , Texture(InTexture) {} /** Create UAV with access to a specific meta data plane */ static FRDGTextureUAVDesc CreateForMetaData(FRDGTextureRef Texture, ERDGTextureMetaDataAccess MetaData) { FRDGTextureUAVDesc Desc = FRDGTextureUAVDesc(Texture, 0); Desc.MetaData = MetaData; return Desc; } bool operator == (const FRDGTextureUAVDesc& Other) const { return Texture == Other.Texture && FRHITextureUAVCreateInfo::operator==(Other); } bool operator != (const FRDGTextureUAVDesc& Other) const { return !(*this == Other); } friend uint32 GetTypeHash(const FRDGTextureUAVDesc& Desc) { return HashCombine(GetTypeHash(static_cast(Desc)), GetTypeHash(Desc.Texture)); } FRDGTextureRef Texture = nullptr; }; /** Render graph tracked texture UAV. */ class FRDGTextureUAV final : public FRDGUnorderedAccessView { public: static const ERDGViewType StaticType = ERDGViewType::TextureUAV; /** Descriptor of the graph tracked UAV. */ const FRDGTextureUAVDesc Desc; FRDGTextureRef GetParent() const override { return Desc.Texture; } // Can be used instead of GetParent()->GetRHI() to access the underlying texture for a UAV resource in a Pass, without triggering // validation errors. The RDG validation logic only flags the UAV as accessible, not the parent texture. FRHITexture* GetParentRHI() const { IF_RDG_ENABLE_DEBUG(ValidateRHIAccess()); return Desc.Texture->GetRHIUnchecked(); } FRDGTextureSubresourceRange GetSubresourceRange() const; private: FRDGTextureUAV(const TCHAR* InName, const FRDGTextureUAVDesc& InDesc, ERDGUnorderedAccessViewFlags InFlags) : FRDGUnorderedAccessView(InName, ERDGViewType::TextureUAV, InFlags) , Desc(InDesc) {} friend FRDGBuilder; friend FRDGViewRegistry; friend FRDGAllocator; }; /** Descriptor for render graph tracked Buffer. */ struct FRDGBufferDesc { enum class UE_DEPRECATED(5.1, "EUnderlying type has been deprecated.") EUnderlyingType { VertexBuffer, StructuredBuffer, AccelerationStructure }; PRAGMA_DISABLE_DEPRECATION_WARNINGS FRDGBufferDesc() = default; FRDGBufferDesc(FRDGBufferDesc&&) = default; FRDGBufferDesc(const FRDGBufferDesc&) = default; FRDGBufferDesc& operator=(FRDGBufferDesc&&) = default; FRDGBufferDesc& operator=(const FRDGBufferDesc&) = default; PRAGMA_ENABLE_DEPRECATION_WARNINGS /** Create the descriptor for an indirect RHI call. * * Note, IndirectParameterStruct should be one of the: * struct FRHIDispatchIndirectParameters * struct FRHIDrawIndirectParameters * struct FRHIDrawIndexedIndirectParameters */ template static FRDGBufferDesc CreateIndirectDesc(uint32 NumElements = 1) { FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::Static | EBufferUsageFlags::DrawIndirect | EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::VertexBuffer; Desc.BytesPerElement = sizeof(IndirectParameterStruct); Desc.NumElements = NumElements; return Desc; } static FRDGBufferDesc CreateIndirectDesc(uint32 NumElements = 1) { FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::Static | EBufferUsageFlags::DrawIndirect | EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::VertexBuffer; Desc.BytesPerElement = 4; Desc.NumElements = NumElements; return Desc; } static FRDGBufferDesc CreateStructuredDesc(uint32 BytesPerElement, uint32 NumElements) { FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::Static | EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer; Desc.BytesPerElement = BytesPerElement; Desc.NumElements = NumElements; return Desc; } template static FRDGBufferDesc CreateStructuredDesc(uint32 NumElements) { FRDGBufferDesc Desc = CreateStructuredDesc(sizeof(ParameterStruct), NumElements); Desc.Metadata = ParameterStruct::FTypeInfo::GetStructMetadata(); return Desc; } static FRDGBufferDesc CreateBufferDesc(uint32 BytesPerElement, uint32 NumElements) { FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::Static | EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::VertexBuffer; Desc.BytesPerElement = BytesPerElement; Desc.NumElements = NumElements; return Desc; } template static FRDGBufferDesc CreateBufferDesc(uint32 NumElements) { FRDGBufferDesc Desc = CreateBufferDesc(sizeof(ParameterStruct), NumElements); Desc.Metadata = ParameterStruct::FTypeInfo::GetStructMetadata(); return Desc; } static FRDGBufferDesc CreateByteAddressDesc(uint32 NumBytes) { check(NumBytes % 4 == 0); FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::Static | EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer | EBufferUsageFlags::ByteAddressBuffer; Desc.BytesPerElement = 4; Desc.NumElements = NumBytes / 4; return Desc; } template static FRDGBufferDesc CreateByteAddressDesc(uint32 NumElements) { FRDGBufferDesc Desc = CreateByteAddressDesc(sizeof(ParameterStruct) * NumElements); Desc.Metadata = ParameterStruct::FTypeInfo::GetStructMetadata(); return Desc; } static FRDGBufferDesc CreateUploadDesc(uint32 BytesPerElement, uint32 NumElements) { FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::ShaderResource | EBufferUsageFlags::VertexBuffer; Desc.BytesPerElement = BytesPerElement; Desc.NumElements = NumElements; return Desc; } template static FRDGBufferDesc CreateUploadDesc(uint32 NumElements) { FRDGBufferDesc Desc = CreateUploadDesc(sizeof(ParameterStruct), NumElements); Desc.Metadata = ParameterStruct::FTypeInfo::GetStructMetadata(); return Desc; } static FRDGBufferDesc CreateStructuredUploadDesc(uint32 BytesPerElement, uint32 NumElements) { FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer; Desc.BytesPerElement = BytesPerElement; Desc.NumElements = NumElements; return Desc; } template static FRDGBufferDesc CreateStructuredUploadDesc(uint32 NumElements) { FRDGBufferDesc Desc = CreateStructuredUploadDesc(sizeof(ParameterStruct), NumElements); Desc.Metadata = ParameterStruct::FTypeInfo::GetStructMetadata(); return Desc; } static FRDGBufferDesc CreateByteAddressUploadDesc(uint32 NumBytes) { check(NumBytes % 4 == 0); FRDGBufferDesc Desc; Desc.Usage = EBufferUsageFlags::ShaderResource | EBufferUsageFlags::ByteAddressBuffer | EBufferUsageFlags::StructuredBuffer; Desc.BytesPerElement = 4; Desc.NumElements = NumBytes / 4; return Desc; } template static FRDGBufferDesc CreateByteAddressUploadDesc(uint32 NumElements) { FRDGBufferDesc Desc = CreateByteAddressUploadDesc(sizeof(ParameterStruct) * NumElements); Desc.Metadata = ParameterStruct::FTypeInfo::GetStructMetadata(); return Desc; } /** Returns the total number of bytes allocated for a such buffer. */ uint32 GetSize() const { return BytesPerElement * NumElements; } UE_DEPRECATED(5.1, "GetTotalNumBytes is deprecated, use GetSize instead.") uint32 GetTotalNumBytes() const { return BytesPerElement * NumElements; } friend uint32 GetTypeHash(const FRDGBufferDesc& Desc) { uint32 Hash = GetTypeHash(Desc.BytesPerElement); Hash = HashCombine(Hash, GetTypeHash(Desc.NumElements)); Hash = HashCombine(Hash, GetTypeHash(Desc.Usage)); Hash = HashCombine(Hash, GetTypeHash(Desc.Metadata)); return Hash; } bool operator == (const FRDGBufferDesc& Other) const { return BytesPerElement == Other.BytesPerElement && NumElements == Other.NumElements && Usage == Other.Usage; } bool operator != (const FRDGBufferDesc& Other) const { return !(*this == Other); } /** Stride in bytes for index and structured buffers. */ uint32 BytesPerElement = 1; /** Number of elements. */ uint32 NumElements = 1; /** Bitfields describing the uses of that buffer. */ EBufferUsageFlags Usage = EBufferUsageFlags::None; /** The underlying RHI type to use. */ PRAGMA_DISABLE_DEPRECATION_WARNINGS UE_DEPRECATED(5.1, "EUnderlyingType has been deprecated. Use explicit EBufferUsageFlags instead.") EUnderlyingType UnderlyingType = EUnderlyingType::VertexBuffer; PRAGMA_ENABLE_DEPRECATION_WARNINGS /** Meta data of the layout of the buffer for debugging purposes. */ const FShaderParametersMetadata* Metadata = nullptr; }; UE_DEPRECATED(5.1, "FRDGBuffer::EUnderylingType has been deprecated. Use EBufferCreateFlags instead.") PRAGMA_DISABLE_DEPRECATION_WARNINGS inline const TCHAR* GetBufferUnderlyingTypeName(FRDGBufferDesc::EUnderlyingType) PRAGMA_ENABLE_DEPRECATION_WARNINGS { return TEXT(""); } struct FRDGBufferSRVDesc final : public FRHIBufferSRVCreateInfo { FRDGBufferSRVDesc() = default; FRDGBufferSRVDesc(FRDGBufferRef InBuffer); FRDGBufferSRVDesc(FRDGBufferRef InBuffer, EPixelFormat InFormat) : FRHIBufferSRVCreateInfo(InFormat) , Buffer(InBuffer) { BytesPerElement = GPixelFormats[Format].BlockBytes; } bool operator == (const FRDGBufferSRVDesc& Other) const { return Buffer == Other.Buffer && FRHIBufferSRVCreateInfo::operator==(Other); } bool operator != (const FRDGBufferSRVDesc& Other) const { return !(*this == Other); } friend uint32 GetTypeHash(const FRDGBufferSRVDesc& Desc) { return HashCombine(GetTypeHash(static_cast(Desc)), GetTypeHash(Desc.Buffer)); } FRDGBufferRef Buffer = nullptr; }; struct FRDGBufferUAVDesc final : public FRHIBufferUAVCreateInfo { FRDGBufferUAVDesc() = default; FRDGBufferUAVDesc(FRDGBufferRef InBuffer); FRDGBufferUAVDesc(FRDGBufferRef InBuffer, EPixelFormat InFormat) : FRHIBufferUAVCreateInfo(InFormat) , Buffer(InBuffer) {} bool operator == (const FRDGBufferUAVDesc& Other) const { return Buffer == Other.Buffer && FRHIBufferUAVCreateInfo::operator==(Other); } bool operator != (const FRDGBufferUAVDesc& Other) const { return !(*this == Other); } friend uint32 GetTypeHash(const FRDGBufferUAVDesc& Desc) { return HashCombine(GetTypeHash(static_cast(Desc)), GetTypeHash(Desc.Buffer)); } FRDGBufferRef Buffer = nullptr; }; /** Translates from a RDG buffer descriptor to a RHI buffer creation info */ inline FRHIBufferCreateInfo Translate(const FRDGBufferDesc& InDesc); class RENDERCORE_API FRDGPooledBuffer final : public FRefCountedObject { public: FRDGPooledBuffer(TRefCountPtr InBuffer, const FRDGBufferDesc& InDesc, uint32 InNumAllocatedElements, const TCHAR* InName) : Desc(InDesc) , Buffer(MoveTemp(InBuffer)) , Name(InName) , NumAllocatedElements(InNumAllocatedElements) {} const FRDGBufferDesc Desc; /** Finds a UAV matching the descriptor in the cache or creates a new one and updates the cache. */ FORCEINLINE FRHIUnorderedAccessView* GetOrCreateUAV(const FRHIBufferUAVCreateInfo& UAVDesc) { return ViewCache.GetOrCreateUAV(Buffer, UAVDesc); } /** Finds a SRV matching the descriptor in the cache or creates a new one and updates the cache. */ FORCEINLINE FRHIShaderResourceView* GetOrCreateSRV(const FRHIBufferSRVCreateInfo& SRVDesc) { return ViewCache.GetOrCreateSRV(Buffer, SRVDesc); } /** Returns the RHI buffer. */ FORCEINLINE FRHIBuffer* GetRHI() const { return Buffer; } /** Returns the default SRV. */ FORCEINLINE FRHIShaderResourceView* GetSRV() { if (!CachedSRV) { CachedSRV = GetOrCreateSRV(FRHIBufferSRVCreateInfo()); } return CachedSRV; } FORCEINLINE uint32 GetSize() const { return Desc.GetSize(); } FORCEINLINE uint32 GetAlignedSize() const { return Desc.BytesPerElement * NumAllocatedElements; } private: TRefCountPtr Buffer; FRHIShaderResourceView* CachedSRV = nullptr; FRHIBufferViewCache ViewCache; FRDGBufferDesc GetAlignedDesc() const { FRDGBufferDesc AlignedDesc = Desc; AlignedDesc.NumElements = NumAllocatedElements; return AlignedDesc; } const TCHAR* Name = nullptr; const uint32 NumAllocatedElements; uint32 LastUsedFrame = 0; friend FRDGBuilder; friend FRDGBufferPool; }; /** A render graph tracked buffer. */ class RENDERCORE_API FRDGBuffer final : public FRDGViewableResource { public: static const ERDGViewableResourceType StaticType = ERDGViewableResourceType::Buffer; FRDGBufferDesc Desc; const ERDGBufferFlags Flags; ////////////////////////////////////////////////////////////////////////// //! The following methods may only be called during pass execution. /** Returns the underlying RHI buffer resource */ FRHIBuffer* GetRHI() const { return static_cast(FRDGViewableResource::GetRHI()); } /** Returns the buffer to use for indirect RHI calls. */ FORCEINLINE FRHIBuffer* GetIndirectRHICallBuffer() const { checkf(Desc.Usage & BUF_DrawIndirect, TEXT("Buffer %s was not flagged for indirect draw usage."), Name); return GetRHI(); } /** Returns the buffer to use for RHI calls, eg RHILockBuffer. */ UE_DEPRECATED(5.0, "Buffers types have been consolidated; use GetRHI() instead.") FORCEINLINE FRHIBuffer* GetRHIVertexBuffer() const { return GetRHI(); } /** Returns the buffer to use for structured buffer calls. */ UE_DEPRECATED(5.0, "Buffers types have been consolidated; use GetRHI() instead.") FORCEINLINE FRHIBuffer* GetRHIStructuredBuffer() const { return GetRHI(); } ////////////////////////////////////////////////////////////////////////// FRDGBufferHandle GetHandle() const { return Handle; } FORCEINLINE uint32 GetSize() const { return Desc.GetSize(); } FORCEINLINE uint32 GetStride() const { return Desc.BytesPerElement; } private: FRDGBuffer(const TCHAR* InName, const FRDGBufferDesc& InDesc, ERDGBufferFlags InFlags) : FRDGViewableResource(InName, ERDGViewableResourceType::Buffer, EnumHasAnyFlags(InFlags, ERDGBufferFlags::SkipTracking)) , Desc(InDesc) , Flags(InFlags) {} FRDGBuffer(const TCHAR* InName, const FRDGBufferDesc& InDesc, ERDGBufferFlags InFlags, FRDGBufferNumElementsCallback&& InNumElementsCallback) : FRDGBuffer(InName, InDesc, InFlags) { NumElementsCallback = MoveTemp(InNumElementsCallback); } /** Finalizes any pending field of the buffer descriptor. */ void FinalizeDesc() { if (NumElementsCallback) { Desc.NumElements = FMath::Max(NumElementsCallback(), 1u); } } FRHIBuffer* GetRHIUnchecked() const { return static_cast(FRDGResource::GetRHIUnchecked()); } /** Returns the current buffer state. Only valid to call after SetRHI. */ FRDGSubresourceState& GetState() const { check(State); return *State; } /** Registered handle set by the builder. */ FRDGBufferHandle Handle; /** The next buffer to own the PooledBuffer allocation during execution. */ FRDGBufferHandle NextOwner; union { /** Assigned pooled buffer pointer. Never reset once assigned. */ FRDGPooledBuffer* PooledBuffer = nullptr; /** Assigned transient buffer pointer. Never reset once assigned. */ FRHITransientBuffer* TransientBuffer; }; /** The assigned buffer view cache (sourced from the pooled / transient buffer. Never reset. */ FRHIBufferViewCache* ViewCache = nullptr; /** Valid strictly when holding a strong reference; use PooledBuffer instead. */ TRefCountPtr Allocation; /** Cached state pointer from the pooled / transient buffer. */ FRDGSubresourceState* State = nullptr; /** Tracks the merged subresource state as the graph is built. */ FRDGSubresourceState* MergeState = nullptr; /** Tracks the last pass that produced this resource as the graph is built. */ FRDGProducerStatesByPipeline LastProducer; /** Optional callback to supply NumElements after the creation of this FRDGBuffer. */ FRDGBufferNumElementsCallback NumElementsCallback; #if RDG_ENABLE_DEBUG struct FRDGBufferDebugData* BufferDebugData = nullptr; FRDGBufferDebugData& GetBufferDebugData() const; #endif friend FRDGBuilder; friend FRDGBarrierValidation; friend FRDGUserValidation; friend FRDGBufferRegistry; friend FRDGAllocator; friend FRDGTrace; }; /** Render graph tracked buffer SRV. */ class FRDGBufferSRV final : public FRDGShaderResourceView { public: static const ERDGViewType StaticType = ERDGViewType::BufferSRV; /** Descriptor of the graph tracked SRV. */ const FRDGBufferSRVDesc Desc; FRDGBufferRef GetParent() const override { return Desc.Buffer; } private: FRDGBufferSRV(const TCHAR* InName, const FRDGBufferSRVDesc& InDesc) : FRDGShaderResourceView(InName, ERDGViewType::BufferSRV) , Desc(InDesc) {} friend FRDGBuilder; friend FRDGViewRegistry; friend FRDGAllocator; }; /** Render graph tracked buffer UAV. */ class FRDGBufferUAV final : public FRDGUnorderedAccessView { public: static const ERDGViewType StaticType = ERDGViewType::BufferUAV; /** Descriptor of the graph tracked UAV. */ const FRDGBufferUAVDesc Desc; FRDGBufferRef GetParent() const override { return Desc.Buffer; } private: FRDGBufferUAV(const TCHAR* InName, const FRDGBufferUAVDesc& InDesc, ERDGUnorderedAccessViewFlags InFlags) : FRDGUnorderedAccessView(InName, ERDGViewType::BufferUAV, InFlags) , Desc(InDesc) {} friend FRDGBuilder; friend FRDGViewRegistry; friend FRDGAllocator; }; template inline ViewableResourceType* GetAs(FRDGViewableResource* Resource) { check(ViewableResourceType::StaticType == Resource->Type); return static_cast(Resource); } template inline ViewType* GetAs(FRDGView* View) { check(ViewType::StaticType == View->Type); return static_cast(View); } inline FRDGBuffer* GetAsBuffer(FRDGViewableResource* Resource) { return GetAs(Resource); } inline FRDGTexture* GetAsTexture(FRDGViewableResource* Resource) { return GetAs(Resource); } inline FRDGBufferUAV* GetAsBufferUAV(FRDGView* View) { return GetAs(View); } inline FRDGBufferSRV* GetAsBufferSRV(FRDGView* View) { return GetAs(View); } inline FRDGTextureUAV* GetAsTextureUAV(FRDGView* View) { return GetAs(View); } inline FRDGTextureSRV* GetAsTextureSRV(FRDGView* View) { return GetAs(View); } inline FGraphicsPipelineRenderTargetsInfo ExtractRenderTargetsInfo(const FRDGParameterStruct& ParameterStruct); #include "RenderGraphResources.inl"