Files
UnrealEngine/Engine/Source/Runtime/VulkanRHI/Private/VulkanResources.h
2025-05-18 13:04:45 +08:00

1258 lines
35 KiB
C++

// 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<FVulkanSyncPoint>;
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<FVulkanShaderModule> GetOrCreateHandle();
TRefCountPtr<FVulkanShaderModule> GetOrCreateHandle(const FVulkanLayout* Layout, uint32 LayoutHash)
{
FScopeLock Lock(&VulkanShaderModulesMapCS);
TRefCountPtr<FVulkanShaderModule>* Found = ShaderModules.Find(LayoutHash);
if (Found)
{
return *Found;
}
return CreateHandle(Layout, LayoutHash);
}
TRefCountPtr<FVulkanShaderModule> GetOrCreateHandle(const FGfxPipelineDesc& Desc, const FVulkanLayout* Layout, uint32 LayoutHash)
{
FScopeLock Lock(&VulkanShaderModulesMapCS);
if (NeedsSpirvInputAttachmentPatching(Desc))
{
LayoutHash = HashCombine(LayoutHash, 1);
}
TRefCountPtr<FVulkanShaderModule>* 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<uint32>&& UncompressedCodeIn) : UncompressedCode(MoveTemp(UncompressedCodeIn))
{
CodeView = UncompressedCode;
}
explicit FSpirvCode(TArrayView<uint32> UncompressedCodeView) : CodeView(UncompressedCodeView) { }
TArrayView<uint32> CodeView;
TArray<uint32> UncompressedCode;
public:
TArrayView<uint32> GetCodeView() {return CodeView;}
};
inline FSpirvCode GetSpirvCode() const
{
return GetSpirvCode(SpirvContainer);
}
FSpirvCode GetPatchedSpirvCode(const FGfxPipelineDesc& Desc, const FVulkanLayout* Layout);
TArray<FUniformBufferStaticSlot>& 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<uint32, TRefCountPtr<FVulkanShaderModule>> ShaderModules;
const EShaderFrequency Frequency;
bool bUsesBindless = false;
protected:
class FSpirvContainer
{
friend class FVulkanShader;
TArray<uint8> 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<FVulkanShaderModule> CreateHandle(const FVulkanLayout* Layout, uint32 LayoutHash);
TRefCountPtr<FVulkanShaderModule> 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<FUniformBufferStaticSlot>& 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<typename BaseResourceType, EShaderFrequency ShaderType>
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<FRHIVertexShader, SF_Vertex> FVulkanVertexShader;
typedef TVulkanBaseShader<FRHIPixelShader, SF_Pixel> FVulkanPixelShader;
typedef TVulkanBaseShader<FRHIComputeShader, SF_Compute> FVulkanComputeShader;
typedef TVulkanBaseShader<FRHIGeometryShader, SF_Geometry> FVulkanGeometryShader;
typedef TVulkanBaseShader<FRHIMeshShader, SF_Mesh> FVulkanMeshShader;
typedef TVulkanBaseShader<FRHIAmplificationShader, SF_Amplification> 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<FVulkanShaderModule> 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 <typename ShaderType>
ShaderType* CreateShader(TArrayView<const uint8> Code, FVulkanDevice* Device);
template <typename ShaderType>
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<ShaderType*>(*FoundShaderPtr);
}
}
return nullptr;
}
template <EShaderFrequency ShaderFrequency>
FVulkanRayTracingShader* CreateRayTracingShader(TArrayView<const uint8> 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<uint64, FVulkanShader*> 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<FInvalidatedState >(),
TypedBuffer = TStorage::IndexOfType<FTypedBufferView >(),
Texture = TStorage::IndexOfType<FTextureView >(),
StructuredBuffer = TStorage::IndexOfType<FStructuredBufferView >(),
AccelerationStructure = TStorage::IndexOfType<FAccelerationStructureView>(),
};
FVulkanView(FVulkanDevice& InDevice, VkDescriptorType InDescriptorType);
~FVulkanView();
void Invalidate();
EType GetViewType() const
{
return EType(Storage.GetIndex());
}
bool IsInitialized() const
{
return (GetViewType() != Null) || Storage.Get<FInvalidatedState>().bInitialized;
}
FTypedBufferView const& GetTypedBufferView () const { return Storage.Get<FTypedBufferView >(); }
FTextureView const& GetTextureView () const { return Storage.Get<FTextureView >(); }
FStructuredBufferView const& GetStructuredBufferView () const { return Storage.Get<FStructuredBufferView >(); }
FAccelerationStructureView const& GetAccelerationStructureView() const { return Storage.Get<FAccelerationStructureView>(); }
// 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<FVulkanLinkedView>
{
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<typename T>
void DumpMemory(T Callback)
{
const FIntVector SizeXYZ = GetSizeXYZ();
Callback(TEXT("FVulkanTexture"), GetName(), this, static_cast<FRHIResource*>(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<VkFormat, TInlineAllocator<2>> 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<ERHIAccess, 2> 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<typename T>
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<TRefCountPtr<FRHIResource>>& 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<FVulkanCommandListContext>& 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<uint32, uint32> BindingToStream;
TMap<uint32, uint32> 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<uint8> 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<class T>
struct TVulkanResourceTraits
{
};
template<>
struct TVulkanResourceTraits<FRHIVertexDeclaration>
{
typedef FVulkanVertexDeclaration TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIVertexShader>
{
typedef FVulkanVertexShader TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIMeshShader>
{
typedef FVulkanMeshShader TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIAmplificationShader>
{
typedef FVulkanTaskShader TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIGeometryShader>
{
typedef FVulkanGeometryShader TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIPixelShader>
{
typedef FVulkanPixelShader TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIComputeShader>
{
typedef FVulkanComputeShader TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIRenderQuery>
{
typedef FVulkanRenderQuery TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIUniformBuffer>
{
typedef FVulkanUniformBuffer TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIBuffer>
{
typedef FVulkanBuffer TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIShaderResourceView>
{
typedef FVulkanShaderResourceView TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIUnorderedAccessView>
{
typedef FVulkanUnorderedAccessView TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHISamplerState>
{
typedef FVulkanSamplerState TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIRasterizerState>
{
typedef FVulkanRasterizerState TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIDepthStencilState>
{
typedef FVulkanDepthStencilState TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIBlendState>
{
typedef FVulkanBlendState TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIBoundShaderState>
{
typedef FVulkanBoundShaderState TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIStagingBuffer>
{
typedef FVulkanStagingBuffer TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIGPUFence>
{
typedef FVulkanGPUFence TConcreteType;
};
template<typename TRHIType>
static FORCEINLINE typename TVulkanResourceTraits<TRHIType>::TConcreteType* ResourceCast(TRHIType* Resource)
{
return static_cast<typename TVulkanResourceTraits<TRHIType>::TConcreteType*>(Resource);
}
template<typename TRHIType>
static FORCEINLINE const typename TVulkanResourceTraits<TRHIType>::TConcreteType* ResourceCast(const TRHIType* Resource)
{
return static_cast<const typename TVulkanResourceTraits<TRHIType>::TConcreteType*>(Resource);
}
static FORCEINLINE FVulkanTexture* ResourceCast(FRHITexture* Texture)
{
return static_cast<FVulkanTexture*>(Texture->GetTextureBaseRHI());
}
class FVulkanRayTracingScene;
class FVulkanRayTracingGeometry;
class FVulkanRayTracingShaderTable;
class FVulkanRayTracingPipelineState;
template<>
struct TVulkanResourceTraits<FRHIRayTracingScene>
{
typedef FVulkanRayTracingScene TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIRayTracingGeometry>
{
typedef FVulkanRayTracingGeometry TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIShaderBindingTable>
{
typedef FVulkanRayTracingShaderTable TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIRayTracingPipelineState>
{
typedef FVulkanRayTracingPipelineState TConcreteType;
};
template<>
struct TVulkanResourceTraits<FRHIRayTracingShader>
{
typedef FVulkanRayTracingShader TConcreteType;
};