// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= VulkanResources.h: Vulkan resource RHI definitions. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "VulkanPlatform.h" #include "VulkanConfiguration.h" #include "VulkanState.h" #include "VulkanUtil.h" #include "BoundShaderStateCache.h" #include "VulkanShaderResources.h" #include "VulkanMemory.h" #include "Misc/ScopeRWLock.h" #include "IVulkanDynamicRHI.h" class FVulkanDevice; class FVulkanQueue; class FVulkanContextCommon; class FVulkanCommandBuffer; class FVulkanTexture; class FVulkanBuffer; class FVulkanLayout; class FVulkanOcclusionQuery; struct FRHITransientHeapAllocation; struct FVulkanPendingBufferLock; class FVulkanView; class FVulkanViewableResource; class FVulkanShaderResourceView; class FVulkanUnorderedAccessView; class FVulkanRenderQuery; class FVulkanDynamicRHI; using FVulkanSyncPoint = FGraphEvent; using FVulkanSyncPointRef = TRefCountPtr; namespace VulkanRHI { class FDeviceMemoryAllocation; } enum { NUM_OCCLUSION_QUERIES_PER_POOL = 4096, NUM_TIMESTAMP_QUERIES_PER_POOL = 1024, }; // Mirror GPixelFormats with format information for buffers extern VkFormat GVulkanBufferFormat[PF_MAX]; // Converts the internal texture dimension to Vulkan view type inline VkImageViewType UETextureDimensionToVkImageViewType(ETextureDimension Dimension) { switch (Dimension) { case ETextureDimension::Texture2D: return VK_IMAGE_VIEW_TYPE_2D; case ETextureDimension::Texture2DArray: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; case ETextureDimension::Texture3D: return VK_IMAGE_VIEW_TYPE_3D; case ETextureDimension::TextureCube: return VK_IMAGE_VIEW_TYPE_CUBE; case ETextureDimension::TextureCubeArray: return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; default: checkNoEntry(); return VK_IMAGE_VIEW_TYPE_MAX_ENUM; } } /** This represents a vertex declaration that hasn't been combined with a specific shader to create a bound shader. */ class FVulkanVertexDeclaration : public FRHIVertexDeclaration { public: FVertexDeclarationElementList Elements; uint32 Hash; uint32 HashNoStrides; FVulkanVertexDeclaration(const FVertexDeclarationElementList& InElements, uint32 InHash, uint32 InHashNoStrides); virtual bool GetInitializer(FVertexDeclarationElementList& Out) final override { Out = Elements; return true; } static void EmptyCache(); virtual uint32 GetPrecachePSOHash() const final override { return HashNoStrides; } }; struct FGfxPipelineDesc; class FVulkanShaderModule : public FThreadSafeRefCountedObject { static FVulkanDevice* Device; VkShaderModule ActualShaderModule; public: FVulkanShaderModule(FVulkanDevice* DeviceIn, VkShaderModule ShaderModuleIn) : ActualShaderModule(ShaderModuleIn) { check(DeviceIn && (Device == DeviceIn || !Device)); Device = DeviceIn; } virtual ~FVulkanShaderModule(); VkShaderModule& GetVkShaderModule() { return ActualShaderModule; } }; class FVulkanShader : public IRefCountedObject { protected: static FCriticalSection VulkanShaderModulesMapCS; public: virtual ~FVulkanShader(); void PurgeShaderModules(); TRefCountPtr GetOrCreateHandle(); TRefCountPtr GetOrCreateHandle(const FVulkanLayout* Layout, uint32 LayoutHash) { FScopeLock Lock(&VulkanShaderModulesMapCS); TRefCountPtr* Found = ShaderModules.Find(LayoutHash); if (Found) { return *Found; } return CreateHandle(Layout, LayoutHash); } TRefCountPtr GetOrCreateHandle(const FGfxPipelineDesc& Desc, const FVulkanLayout* Layout, uint32 LayoutHash) { FScopeLock Lock(&VulkanShaderModulesMapCS); if (NeedsSpirvInputAttachmentPatching(Desc)) { LayoutHash = HashCombine(LayoutHash, 1); } TRefCountPtr* Found = ShaderModules.Find(LayoutHash); if (Found) { return *Found; } return CreateHandle(Desc, Layout, LayoutHash); } inline const FString& GetDebugName() const { return CodeHeader.DebugName; } // Name should be pointing to "main_" void GetEntryPoint(ANSICHAR* Name, int32 NameLength) const { FCStringAnsi::Snprintf(Name, NameLength, "main_%0.8x_%0.8x", SpirvContainer.GetSizeBytes(), CodeHeader.SpirvCRC); } FORCEINLINE const FVulkanShaderHeader& GetCodeHeader() const { return CodeHeader; } inline uint64 GetShaderKey() const { return ShaderKey; } // This provides a view of the raw spirv bytecode. // If it is stored compressed then the result of GetSpirvCode will contain the decompressed spirv. class FSpirvCode { friend class FVulkanShader; explicit FSpirvCode(TArray&& UncompressedCodeIn) : UncompressedCode(MoveTemp(UncompressedCodeIn)) { CodeView = UncompressedCode; } explicit FSpirvCode(TArrayView UncompressedCodeView) : CodeView(UncompressedCodeView) { } TArrayView CodeView; TArray UncompressedCode; public: TArrayView GetCodeView() {return CodeView;} }; inline FSpirvCode GetSpirvCode() const { return GetSpirvCode(SpirvContainer); } FSpirvCode GetPatchedSpirvCode(const FGfxPipelineDesc& Desc, const FVulkanLayout* Layout); TArray& StaticSlots; void SetUsesBindless(bool bValue) { bUsesBindless = bValue; } bool UsesBindless() const { return bUsesBindless; } protected: #if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT FString DebugEntryPoint; #endif uint64 ShaderKey; /** External bindings for this shader. */ FVulkanShaderHeader CodeHeader; TMap> ShaderModules; const EShaderFrequency Frequency; bool bUsesBindless = false; protected: class FSpirvContainer { friend class FVulkanShader; TArray SpirvCode; int32 UncompressedSizeBytes = -1; public: bool IsCompressed() const { return UncompressedSizeBytes != -1; } int32 GetSizeBytes() const { return UncompressedSizeBytes >= 0 ? UncompressedSizeBytes : SpirvCode.Num(); } friend FArchive& operator<<(FArchive& Ar, class FVulkanShader::FSpirvContainer& SpirvContainer); } SpirvContainer; friend FArchive& operator<<(FArchive& Ar, class FVulkanShader::FSpirvContainer& SpirvContainer); static FSpirvCode PatchSpirvInputAttachments(FSpirvCode& SpirvCode); static FSpirvCode GetSpirvCode(const FSpirvContainer& Container); protected: FVulkanDevice* Device; TRefCountPtr CreateHandle(const FVulkanLayout* Layout, uint32 LayoutHash); TRefCountPtr CreateHandle(const FGfxPipelineDesc& Desc, const FVulkanLayout* Layout, uint32 LayoutHash); bool NeedsSpirvInputAttachmentPatching(const FGfxPipelineDesc& Desc) const; FVulkanShader(FVulkanDevice* InDevice, EShaderFrequency InFrequency, FVulkanShaderHeader&& InCodeHeader, FSpirvContainer&& InSpirvContainer, uint64 InShaderKey, TArray& InStaticSlots); friend class FVulkanCommandListContext; friend class FVulkanPipelineStateCacheManager; friend class FVulkanComputeShaderState; friend class FVulkanComputePipeline; friend class FVulkanShaderFactory; }; /** This represents a vertex shader that hasn't been combined with a specific declaration to create a bound shader. */ template class TVulkanBaseShader : public BaseResourceType, public FVulkanShader { private: TVulkanBaseShader(FVulkanDevice* InDevice, FShaderResourceTable&& InSRT, FVulkanShaderHeader&& InCodeHeader, FSpirvContainer&& InSpirvContainer, uint64 InShaderKey) : BaseResourceType() , FVulkanShader(InDevice, ShaderType, MoveTemp(InCodeHeader), MoveTemp(InSpirvContainer), InShaderKey, BaseResourceType::StaticSlots) { BaseResourceType::ShaderResourceTable = MoveTemp(InSRT); } friend class FVulkanShaderFactory; public: enum { StaticFrequency = ShaderType }; // IRefCountedObject interface. virtual FReturnedRefCountValue AddRef() const override final { return FReturnedRefCountValue{FRHIResource::AddRef()}; } virtual uint32 Release() const override final { return FRHIResource::Release(); } virtual uint32 GetRefCount() const override final { return FRHIResource::GetRefCount(); } }; typedef TVulkanBaseShader FVulkanVertexShader; typedef TVulkanBaseShader FVulkanPixelShader; typedef TVulkanBaseShader FVulkanComputeShader; typedef TVulkanBaseShader FVulkanGeometryShader; typedef TVulkanBaseShader FVulkanMeshShader; typedef TVulkanBaseShader FVulkanTaskShader; class FVulkanRayTracingShader : public FRHIRayTracingShader, public FVulkanShader { private: FVulkanRayTracingShader(FVulkanDevice* InDevice, EShaderFrequency InFrequency, FShaderResourceTable&& InSRT, FVulkanShaderHeader&& InCodeHeader, FSpirvContainer&& InSpirvContainer, uint64 InShaderKey) : FRHIRayTracingShader(InFrequency) , FVulkanShader(InDevice, InFrequency, MoveTemp(InCodeHeader), MoveTemp(InSpirvContainer), InShaderKey, FRHIRayTracingShader::StaticSlots) { ShaderResourceTable = MoveTemp(InSRT); } FSpirvContainer AnyHitSpirvContainer; FSpirvContainer IntersectionSpirvContainer; friend class FVulkanShaderFactory; public: static const uint32 MainModuleIdentifier = 0; static const uint32 ClosestHitModuleIdentifier = MainModuleIdentifier; static const uint32 AnyHitModuleIdentifier = 1; static const uint32 IntersectionModuleIdentifier = 2; TRefCountPtr GetOrCreateHandle(uint32 ModuleIdentifier); // IRefCountedObject interface. virtual FReturnedRefCountValue AddRef() const override final { return FReturnedRefCountValue{FRHIResource::AddRef()}; } virtual uint32 Release() const override final { return FRHIResource::Release(); } virtual uint32 GetRefCount() const override final { return FRHIResource::GetRefCount(); } }; class FVulkanShaderFactory { public: ~FVulkanShaderFactory(); template ShaderType* CreateShader(TArrayView Code, FVulkanDevice* Device); template ShaderType* LookupShader(uint64 ShaderKey) const { if (ShaderKey) { FRWScopeLock ScopedLock(RWLock[ShaderType::StaticFrequency], SLT_ReadOnly); FVulkanShader* const * FoundShaderPtr = ShaderMap[ShaderType::StaticFrequency].Find(ShaderKey); if (FoundShaderPtr) { return static_cast(*FoundShaderPtr); } } return nullptr; } template FVulkanRayTracingShader* CreateRayTracingShader(TArrayView Code, FVulkanDevice* Device); void LookupGfxShaders(const uint64 InShaderKeys[ShaderStage::NumGraphicsStages], FVulkanShader* OutShaders[ShaderStage::NumGraphicsStages]) const; void OnDeleteShader(const FVulkanShader& Shader); private: mutable FRWLock RWLock[SF_NumFrequencies]; TMap ShaderMap[SF_NumFrequencies]; }; class FVulkanBoundShaderState : public FRHIBoundShaderState { public: FVulkanBoundShaderState( FRHIVertexDeclaration* InVertexDeclarationRHI, FRHIVertexShader* InVertexShaderRHI, FRHIPixelShader* InPixelShaderRHI, FRHIGeometryShader* InGeometryShaderRHI ); virtual ~FVulkanBoundShaderState(); FORCEINLINE FVulkanVertexShader* GetVertexShader() const { return (FVulkanVertexShader*)CacheLink.GetVertexShader(); } FORCEINLINE FVulkanPixelShader* GetPixelShader() const { return (FVulkanPixelShader*)CacheLink.GetPixelShader(); } FORCEINLINE FVulkanMeshShader* GetMeshShader() const { return (FVulkanMeshShader*)CacheLink.GetMeshShader(); } FORCEINLINE FVulkanTaskShader* GetTaskShader() const { return (FVulkanTaskShader*)CacheLink.GetAmplificationShader(); } FORCEINLINE FVulkanGeometryShader* GetGeometryShader() const { return (FVulkanGeometryShader*)CacheLink.GetGeometryShader(); } const FVulkanShader* GetShader(ShaderStage::EStage Stage) const { switch (Stage) { case ShaderStage::Vertex: return GetVertexShader(); case ShaderStage::Pixel: return GetPixelShader(); #if PLATFORM_SUPPORTS_MESH_SHADERS case ShaderStage::Mesh: return GetMeshShader(); case ShaderStage::Task: return GetTaskShader(); #endif #if VULKAN_SUPPORTS_GEOMETRY_SHADERS case ShaderStage::Geometry: return GetGeometryShader(); #endif default: break; } checkf(0, TEXT("Invalid Shader Frequency %d"), (int32)Stage); return nullptr; } private: FCachedBoundShaderStateLink_Threadsafe CacheLink; }; struct FVulkanCpuReadbackBuffer { VkBuffer Buffer; uint32 MipOffsets[MAX_TEXTURE_MIP_COUNT]; }; class FVulkanView { public: struct FInvalidatedState { bool bInitialized = false; }; struct FTypedBufferView { VkBufferView View = VK_NULL_HANDLE; uint32 ViewId = 0; bool bVolatile = false; // Whether source buffer is volatile }; struct FStructuredBufferView { VkBuffer Buffer = VK_NULL_HANDLE; uint32 HandleId = 0; uint32 Offset = 0; uint32 Size = 0; }; struct FAccelerationStructureView { VkAccelerationStructureKHR Handle = VK_NULL_HANDLE; }; struct FTextureView { VkImageView View = VK_NULL_HANDLE; VkImage Image = VK_NULL_HANDLE; uint32 ViewId = 0; }; typedef TVariant< FInvalidatedState , FTypedBufferView , FTextureView , FStructuredBufferView , FAccelerationStructureView > TStorage; enum EType { Null = TStorage::IndexOfType(), TypedBuffer = TStorage::IndexOfType(), Texture = TStorage::IndexOfType(), StructuredBuffer = TStorage::IndexOfType(), AccelerationStructure = TStorage::IndexOfType(), }; FVulkanView(FVulkanDevice& InDevice, VkDescriptorType InDescriptorType); ~FVulkanView(); void Invalidate(); EType GetViewType() const { return EType(Storage.GetIndex()); } bool IsInitialized() const { return (GetViewType() != Null) || Storage.Get().bInitialized; } FTypedBufferView const& GetTypedBufferView () const { return Storage.Get(); } FTextureView const& GetTextureView () const { return Storage.Get(); } FStructuredBufferView const& GetStructuredBufferView () const { return Storage.Get(); } FAccelerationStructureView const& GetAccelerationStructureView() const { return Storage.Get(); } // NOTE: The InOffset applies to the FVulkanBuffer (it does not include any internal Allocation offsets that may exist) FVulkanView* InitAsTypedBufferView( FVulkanBuffer* Buffer , EPixelFormat Format , uint32 InOffset , uint32 InSize); FVulkanView* InitAsTextureView( VkImage InImage , VkImageViewType ViewType , VkImageAspectFlags AspectFlags , EPixelFormat UEFormat , VkFormat Format , uint32 FirstMip , uint32 NumMips , uint32 ArraySliceIndex , uint32 NumArraySlices , bool bUseIdentitySwizzle = false , VkImageUsageFlags ImageUsageFlags = 0 , VkSamplerYcbcrConversion SamplerYcbcrConversion = nullptr); // NOTE: The InOffset applies to the FVulkanBuffer (it does not include any internal Allocation offsets that may exist) FVulkanView* InitAsStructuredBufferView( FVulkanBuffer* Buffer , uint32 InOffset , uint32 InSize); FVulkanView* InitAsAccelerationStructureView( FVulkanBuffer* Buffer , uint32 Offset , uint32 Size); // No moving or copying FVulkanView(FVulkanView &&) = delete; FVulkanView(FVulkanView const&) = delete; FVulkanView& operator = (FVulkanView &&) = delete; FVulkanView& operator = (FVulkanView const&) = delete; FRHIDescriptorHandle GetBindlessHandle() const { return BindlessHandle; } VkDescriptorType GetDescriptorType() const { return DescriptorType; } private: FVulkanDevice& Device; FRHIDescriptorHandle BindlessHandle; const VkDescriptorType DescriptorType; TStorage Storage; }; class FVulkanLinkedView : public FVulkanView, public TIntrusiveLinkedList { protected: FVulkanLinkedView(FVulkanDevice& Device, VkDescriptorType DescriptorType) : FVulkanView(Device, DescriptorType) {} ~FVulkanLinkedView() { Unlink(); } public: virtual void UpdateView() = 0; }; class FVulkanViewableResource { public: virtual ~FVulkanViewableResource() { checkf(!HasLinkedViews(), TEXT("All linked views must have been removed before the underlying resource can be deleted.")); } bool HasLinkedViews() const { return LinkedViews != nullptr; } // @todo convert views owned by the texture into proper // FVulkanView instances, then remove 'virtual' from this class virtual void UpdateLinkedViews(); private: friend FVulkanShaderResourceView; friend FVulkanUnorderedAccessView; FVulkanLinkedView* LinkedViews = nullptr; }; enum class EImageOwnerType : uint8 { None, LocalOwner, ExternalOwner, Aliased }; class FVulkanTexture : public FRHITexture, public FVulkanEvictable, public FVulkanViewableResource { public: // Constructor without command list: targets will not be cleared, cannot contain initial data, will be left in VK_IMAGE_LAYOUT_UNDEFINED. // Layout changes and initial data upload should be handled by FinalizeCreateTextureInternal FVulkanTexture(FVulkanDevice& InDevice, const FRHITextureCreateDesc& InCreateDesc, const FRHITransientHeapAllocation* InTransientHeapAllocation); // Construct from external resource. FVulkanTexture(FVulkanDevice& InDevice, const FRHITextureCreateDesc& InCreateDesc, VkImage InImage, const FVulkanRHIExternalImageDeleteCallbackInfo& InExternalImageDeleteCallbackInfo); #if PLATFORM_ANDROID // Construct from Android Hardware buffer. HardwareBuffer_Desc could be extracted from HardwareBuffer, but adding it here makes the function signature unambigious FVulkanTexture(FVulkanDevice& InDevice, const FRHITextureCreateDesc& InCreateDesc, const AHardwareBuffer_Desc& HardwareBufferDesc, AHardwareBuffer* HardwareBuffer); #endif // Aliasing constructor. FVulkanTexture(FVulkanDevice& InDevice, const FRHITextureCreateDesc& InCreateDesc, FTextureRHIRef& SrcTextureRHI); virtual ~FVulkanTexture(); void AliasTextureResources(FTextureRHIRef& SrcTextureRHI); void UploadInitialData(FRHICommandListBase& RHICmdList, VulkanRHI::FStagingBuffer* UploadBuffer); // View with all mips/layers FVulkanView* DefaultView = nullptr; // View with all mips/layers, but if it's a Depth/Stencil, only the Depth view FVulkanView* PartialView = nullptr; FTextureRHIRef AliasedTexture; template void DumpMemory(T Callback) { const FIntVector SizeXYZ = GetSizeXYZ(); Callback(TEXT("FVulkanTexture"), GetName(), this, static_cast(this), SizeXYZ.X, SizeXYZ.Y, SizeXYZ.Z, StorageFormat); } // FVulkanEvictable interface. bool CanMove() const override { return false; } bool CanEvict() const override { return false; } void Evict(FVulkanDevice& Device, FVulkanCommandListContext& Context) override; ///evict to system memory void Move(FVulkanDevice& Device, FVulkanCommandListContext& Context, VulkanRHI::FVulkanAllocation& NewAllocation) override; //move to a full new allocation FVulkanTexture* GetEvictableTexture() override { return CanEvict() ? this : nullptr; } bool GetTextureResourceInfo(FRHIResourceInfo& OutResourceInfo) const; void* GetNativeResource() const override final { return (void*)Image; } void* GetTextureBaseRHI() override final { return this; } virtual FRHIDescriptorHandle GetDefaultBindlessHandle() const override final { check(PartialView); return PartialView->GetBindlessHandle(); } struct FImageCreateInfo { VkImageCreateInfo ImageCreateInfo; //only used when HasImageFormatListKHR is supported. Otherise VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT is used. VkImageFormatListCreateInfoKHR ImageFormatListCreateInfo; //used when TexCreate_External is given VkExternalMemoryImageCreateInfoKHR ExternalMemImageCreateInfo; // Array of formats used for mutable formats TArray> FormatsUsed; VkImageCompressionFixedRateFlagsEXT CompressionFixedRateFlags; VkImageCompressionControlEXT CompressionControl; }; // Seperate method for creating VkImageCreateInfo static void GenerateImageCreateInfo( FImageCreateInfo& OutImageCreateInfo, FVulkanDevice& InDevice, const FRHITextureDesc& InDesc, VkFormat* OutStorageFormat = nullptr, VkFormat* OutViewFormat = nullptr, bool bForceLinearTexture = false); void DestroySurface(); void InvalidateMappedMemory(); void* GetMappedPointer(); /** * Returns how much memory is used by the surface */ uint32 GetMemorySize() const { return MemoryRequirements.size; } /** * Returns one of the texture's mip-maps stride. */ void GetMipStride(uint32 MipIndex, uint32& Stride); /** * Returns how much memory a single mip uses. */ void GetMipSize(uint32 MipIndex, uint64& MipBytes); VkImageViewType GetViewType() const { return UETextureDimensionToVkImageViewType(GetDesc().Dimension); } VkImageTiling GetTiling() const { return Tiling; } uint32 GetNumberOfArrayLevels() const { switch (GetViewType()) { case VK_IMAGE_VIEW_TYPE_1D: case VK_IMAGE_VIEW_TYPE_2D: case VK_IMAGE_VIEW_TYPE_3D: return 1; case VK_IMAGE_VIEW_TYPE_2D_ARRAY: return GetDesc().ArraySize; case VK_IMAGE_VIEW_TYPE_CUBE: return 6; case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return 6 * GetDesc().ArraySize; default: ErrorInvalidViewType(); return 1; } } VULKANRHI_API void ErrorInvalidViewType() const; // Full includes Depth+Stencil VkImageAspectFlags GetFullAspectMask() const { return FullAspectMask; } // Only Depth or Stencil VkImageAspectFlags GetPartialAspectMask() const { return PartialAspectMask; } bool IsDepthOrStencilAspect() const { return (FullAspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0; } bool IsImageOwner() const { return (ImageOwnerType == EImageOwnerType::LocalOwner); } bool SupportsSampling() const { return EnumHasAllFlags(GPixelFormats[GetDesc().Format].Capabilities, EPixelFormatCapabilities::TextureSample) && ((ImageUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) != 0); } VkImageLayout GetDefaultLayout() const { return DefaultLayout; } void SetDefaultLayout(VkImageLayout InDefaultLayout) { DefaultLayout = InDefaultLayout; } VULKANRHI_API VkDeviceMemory GetAllocationHandle() const; VULKANRHI_API uint64 GetAllocationOffset() const; static void InternalLockWrite(FVulkanContextCommon& Context, FVulkanTexture* Surface, const VkBufferImageCopy& Region, VulkanRHI::FStagingBuffer* StagingBuffer); const FVulkanCpuReadbackBuffer* GetCpuReadbackBuffer() const { return CpuReadbackBuffer; } virtual void UpdateLinkedViews() override; FVulkanDevice* const Device; VkImage Image = VK_NULL_HANDLE; VkImageUsageFlags ImageUsageFlags = 0; // Removes SRGB if requested, used to upload data VkFormat StorageFormat = VK_FORMAT_UNDEFINED; // Format for SRVs, render targets VkFormat ViewFormat = VK_FORMAT_UNDEFINED; VkMemoryPropertyFlags MemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkMemoryRequirements MemoryRequirements; FVulkanRHIExternalImageDeleteCallbackInfo ExternalImageDeleteCallbackInfo; // Only used when HasSeparateDepthStencilLayouts == false. TStaticArray AllPlanesTrackedAccess{ InPlace, ERHIAccess::Unknown }; void SetInitialImageState(FRHICommandListBase& RHICmdList, VkImageLayout InitialLayout, bool bClear, const FClearValueBinding& ClearValueBinding, bool bIsTransientResource); private: void InternalMoveSurface(FVulkanDevice& InDevice, FVulkanCommandListContext& Context, VulkanRHI::FVulkanAllocation& DestAllocation); VkImageTiling Tiling = VK_IMAGE_TILING_MAX_ENUM; VulkanRHI::FVulkanAllocation Allocation; VkImageAspectFlags FullAspectMask = 0; VkImageAspectFlags PartialAspectMask = 0; FVulkanCpuReadbackBuffer* CpuReadbackBuffer = nullptr; VkImageLayout DefaultLayout = VK_IMAGE_LAYOUT_UNDEFINED; protected: EImageOwnerType ImageOwnerType; }; class FVulkanBuffer : public FRHIBuffer, public VulkanRHI::FDeviceChild, public FVulkanViewableResource { public: FVulkanBuffer(FVulkanDevice* InDevice, const FRHIBufferCreateDesc& CreateDesc, const FRHITransientHeapAllocation* InTransientHeapAllocation = nullptr); virtual ~FVulkanBuffer(); inline const VulkanRHI::FVulkanAllocation& GetCurrentAllocation() const { return CurrentBufferAlloc.Alloc; } inline VkBuffer GetHandle() const { return (VkBuffer)GetCurrentAllocation().VulkanHandle; } inline bool IsVolatile() const { return EnumHasAnyFlags(GetUsage(), BUF_Volatile); } // Offset used for Binding a VkBuffer inline uint32 GetOffset() const { return GetCurrentAllocation().Offset; } // Remaining size from the current offset inline uint64 GetCurrentSize() const { return GetCurrentAllocation().Size; } inline VkDeviceAddress GetDeviceAddress() const { return CurrentBufferAlloc.DeviceAddress; } inline VkBufferUsageFlags GetBufferUsageFlags() const { return BufferUsageFlags; } inline VkIndexType GetIndexType() const { return (GetStride() == 4)? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16; } void* Lock(FRHICommandListBase& RHICmdList, EResourceLockMode LockMode, uint32 Size, uint32 Offset); void Unlock(FRHICommandListBase& RHICmdList); void TakeOwnership(FVulkanBuffer& Other); void ReleaseOwnership(); template void DumpMemory(T Callback) { Callback(TEXT("FVulkanBuffer"), FName(), this, 0, GetCurrentSize(), 1, 1, VK_FORMAT_UNDEFINED); } static VkBufferUsageFlags UEToVKBufferUsageFlags(FVulkanDevice* InDevice, EBufferUsageFlags InUEUsage, bool bZeroSize); struct FBufferAlloc { VulkanRHI::FVulkanAllocation Alloc; void* HostPtr = nullptr; VkDeviceAddress DeviceAddress = 0; }; void* GetCurrentHostPointer() { return CurrentBufferAlloc.HostPtr; } void IncrementLockCounter() { LockCounter++; } protected: // Will return a new allocation that can be used for this buffer (proper memory type and size) void AllocateMemory(FBufferAlloc& OutAlloc); VkBufferUsageFlags BufferUsageFlags; enum class ELockStatus : uint8 { Unlocked, Locked, PersistentMapping, } LockStatus = ELockStatus::Unlocked; FBufferAlloc CurrentBufferAlloc; uint32 LockCounter = 0; friend class FVulkanCommandListContext; friend struct FRHICommandMultiBufferUnlock; }; class FVulkanUniformBuffer : public FRHIUniformBuffer { public: FVulkanUniformBuffer(FVulkanDevice& Device, const FRHIUniformBufferLayout* InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation); virtual ~FVulkanUniformBuffer(); const TArray>& GetResourceTable() const { return ResourceTable; } void UpdateResourceTable(const FRHIUniformBufferLayout& InLayout, const void* Contents, int32 ResourceNum); void UpdateResourceTable(FRHIResource** Resources, int32 ResourceNum); inline VkBuffer GetBufferHandle() const { return Allocation.GetBufferHandle(); } inline uint32 GetOffset() const { return Allocation.Offset; } inline void UpdateAllocation(VulkanRHI::FVulkanAllocation& NewAlloc) { NewAlloc.Swap(Allocation); } inline bool IsUniformView() const { return UniformViewSRV != nullptr; } FRHIDescriptorHandle GetBindlessHandle(); VkDeviceAddress GetDeviceAddress() const; void SetupUniformBufferView(); public: FVulkanDevice* Device; VulkanRHI::FVulkanAllocation Allocation; EUniformBufferUsage Usage; FRHIDescriptorHandle BindlessHandle; VkDeviceAddress CachedDeviceAddress = 0; FRHIShaderResourceView* UniformViewSRV; }; class FVulkanUnorderedAccessView final : public FRHIUnorderedAccessView, public FVulkanLinkedView { public: FVulkanUnorderedAccessView(FRHICommandListBase& RHICmdList, FVulkanDevice& InDevice, FRHIViewableResource* InResource, FRHIViewDesc const& InViewDesc); FVulkanViewableResource* GetBaseResource() const; void UpdateView() override; virtual FRHIDescriptorHandle GetBindlessHandle() const override { return FVulkanLinkedView::GetBindlessHandle(); } void Clear(TRHICommandList_RecursiveHazardous& RHICmdList, const void* ClearValue, bool bFloat); }; class FVulkanShaderResourceView final : public FRHIShaderResourceView, public FVulkanLinkedView { public: FVulkanShaderResourceView(FRHICommandListBase& RHICmdList, FVulkanDevice& InDevice, FRHIViewableResource* InResource, FRHIViewDesc const& InViewDesc); FVulkanViewableResource* GetBaseResource() const; void UpdateView() override; virtual FRHIDescriptorHandle GetBindlessHandle() const override { return FVulkanLinkedView::GetBindlessHandle(); } }; class FVulkanVertexInputStateInfo { public: FVulkanVertexInputStateInfo(); ~FVulkanVertexInputStateInfo(); void Generate(FVulkanVertexDeclaration* VertexDeclaration, uint32 VertexHeaderInOutAttributeMask); inline uint32 GetHash() const { check(Info.sType == VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO); return Hash; } inline const VkPipelineVertexInputStateCreateInfo& GetInfo() const { return Info; } bool operator ==(const FVulkanVertexInputStateInfo& Other); protected: VkPipelineVertexInputStateCreateInfo Info; uint32 Hash; uint32 BindingsNum; uint32 BindingsMask; //#todo-rco: Remove these TMaps TMap BindingToStream; TMap StreamToBinding; VkVertexInputBindingDescription Bindings[MaxVertexElementCount]; uint32 AttributesNum; VkVertexInputAttributeDescription Attributes[MaxVertexElementCount]; friend class FVulkanPendingGfxState; friend class FVulkanPipelineStateCacheManager; }; // This class holds the staging area for packed global uniform buffers for a given shader class FPackedUniformBuffers { public: // One buffer is a chunk of bytes typedef TArray FPackedBuffer; void Init(const FVulkanShaderHeader& InCodeHeader, uint32& OutPackedUniformBufferStagingMask) { if (InCodeHeader.PackedGlobalsSize > 0) { check(PackedUniformBuffers.Num() == 0); PackedUniformBuffers.AddUninitialized(InCodeHeader.PackedGlobalsSize); OutPackedUniformBufferStagingMask = 1; } else { OutPackedUniformBufferStagingMask = 0; } } inline void SetPackedGlobalParameter(uint32 ByteOffset, uint32 NumBytes, const void* RESTRICT NewValue, uint32& InOutPackedUniformBufferStagingDirty) { check(ByteOffset + NumBytes <= (uint32)PackedUniformBuffers.Num()); check((NumBytes & 3) == 0 && (ByteOffset & 3) == 0); uint32* RESTRICT RawDst = (uint32*)(PackedUniformBuffers.GetData() + ByteOffset); uint32* RESTRICT RawSrc = (uint32*)NewValue; uint32* RESTRICT RawSrcEnd = RawSrc + (NumBytes >> 2); bool bChanged = false; while (RawSrc != RawSrcEnd) { bChanged |= CopyAndReturnNotEqual(*RawDst++, *RawSrc++); } if (bChanged) { InOutPackedUniformBufferStagingDirty = 1; } } inline const FPackedBuffer& GetBuffer() const { return PackedUniformBuffers; } protected: FPackedBuffer PackedUniformBuffers; }; class FVulkanStagingBuffer : public FRHIStagingBuffer { friend class FVulkanCommandListContext; public: FVulkanStagingBuffer() : FRHIStagingBuffer() { check(!bIsLocked); } virtual ~FVulkanStagingBuffer(); virtual void* Lock(uint32 Offset, uint32 NumBytes) final override; virtual void Unlock() final override; uint64 GetGPUSizeBytes() const override { return StagingBuffer ? StagingBuffer->GetSize() : 0; } private: VulkanRHI::FStagingBuffer* StagingBuffer = nullptr; uint32 QueuedNumBytes = 0; // The staging buffer was allocated from this device. FVulkanDevice* Device; }; class FVulkanGPUFence final : public FRHIGPUFence { public: FVulkanGPUFence(FVulkanDevice& InDevice, FName InName); virtual ~FVulkanGPUFence(); virtual void Clear() override; virtual bool Poll() const override; virtual void Wait(FRHICommandListImmediate& RHICmdList, FRHIGPUMask GPUMask) const override; protected: FVulkanDevice& Device; VkEvent Event = VK_NULL_HANDLE; FGraphEventRef SubmittedSyncPoint; FVulkanSyncPointRef CompletedSyncPoint; friend class FVulkanDynamicRHI; }; template struct TVulkanResourceTraits { }; template<> struct TVulkanResourceTraits { typedef FVulkanVertexDeclaration TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanVertexShader TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanMeshShader TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanTaskShader TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanGeometryShader TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanPixelShader TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanComputeShader TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanRenderQuery TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanUniformBuffer TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanBuffer TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanShaderResourceView TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanUnorderedAccessView TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanSamplerState TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanRasterizerState TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanDepthStencilState TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanBlendState TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanBoundShaderState TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanStagingBuffer TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanGPUFence TConcreteType; }; template static FORCEINLINE typename TVulkanResourceTraits::TConcreteType* ResourceCast(TRHIType* Resource) { return static_cast::TConcreteType*>(Resource); } template static FORCEINLINE const typename TVulkanResourceTraits::TConcreteType* ResourceCast(const TRHIType* Resource) { return static_cast::TConcreteType*>(Resource); } static FORCEINLINE FVulkanTexture* ResourceCast(FRHITexture* Texture) { return static_cast(Texture->GetTextureBaseRHI()); } class FVulkanRayTracingScene; class FVulkanRayTracingGeometry; class FVulkanRayTracingShaderTable; class FVulkanRayTracingPipelineState; template<> struct TVulkanResourceTraits { typedef FVulkanRayTracingScene TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanRayTracingGeometry TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanRayTracingShaderTable TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanRayTracingPipelineState TConcreteType; }; template<> struct TVulkanResourceTraits { typedef FVulkanRayTracingShader TConcreteType; };