From 4728b09ae81c185408e17981464cb13f242a7a50 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 8 Apr 2026 18:05:00 +0800 Subject: [PATCH] Add RHI buffer SRV and UAV view support --- .../include/XCEngine/RHI/D3D12/D3D12Buffer.h | 7 +- .../include/XCEngine/RHI/D3D12/D3D12Device.h | 2 + .../XCEngine/RHI/D3D12/D3D12ResourceView.h | 14 ++ .../XCEngine/RHI/OpenGL/OpenGLBuffer.h | 3 +- .../XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h | 4 + .../XCEngine/RHI/OpenGL/OpenGLDevice.h | 2 + .../include/XCEngine/RHI/OpenGL/OpenGLEnums.h | 1 + .../XCEngine/RHI/OpenGL/OpenGLResourceView.h | 6 + engine/include/XCEngine/RHI/RHIDevice.h | 2 + engine/include/XCEngine/RHI/RHIEnums.h | 24 +- engine/include/XCEngine/RHI/RHITypes.h | 3 + .../XCEngine/RHI/Vulkan/VulkanCommon.h | 12 +- .../XCEngine/RHI/Vulkan/VulkanDevice.h | 2 + .../XCEngine/RHI/Vulkan/VulkanResourceView.h | 2 + engine/src/RHI/D3D12/D3D12Buffer.cpp | 9 +- engine/src/RHI/D3D12/D3D12Device.cpp | 212 +++++++++++++++++- engine/src/RHI/D3D12/D3D12ResourceView.cpp | 89 ++++++++ engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp | 91 +++++++- engine/src/RHI/OpenGL/OpenGLDevice.cpp | 57 ++++- engine/src/RHI/OpenGL/OpenGLResourceView.cpp | 108 ++++++++- .../src/RHI/Vulkan/VulkanDescriptorPool.cpp | 5 +- engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp | 63 ++++-- engine/src/RHI/Vulkan/VulkanDevice.cpp | 31 +++ .../src/RHI/Vulkan/VulkanPipelineLayout.cpp | 4 +- engine/src/RHI/Vulkan/VulkanResourceView.cpp | 103 ++++++++- tests/RHI/unit/CMakeLists.txt | 4 + tests/RHI/unit/test_descriptor_set.cpp | 120 ++++++++++ tests/RHI/unit/test_views.cpp | 51 +++++ 28 files changed, 988 insertions(+), 43 deletions(-) diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12Buffer.h b/engine/include/XCEngine/RHI/D3D12/D3D12Buffer.h index b02c718d..5b059a75 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12Buffer.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12Buffer.h @@ -17,7 +17,12 @@ public: D3D12Buffer(); ~D3D12Buffer() override; - bool Initialize(ID3D12Device* device, uint64_t size, D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON, D3D12_HEAP_TYPE heapType = D3D12_HEAP_TYPE_DEFAULT); + bool Initialize( + ID3D12Device* device, + uint64_t size, + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON, + D3D12_HEAP_TYPE heapType = D3D12_HEAP_TYPE_DEFAULT, + D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_NONE); bool InitializeFromExisting(ID3D12Resource* resource); bool InitializeWithData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList, const void* data, uint64_t size, D3D12_RESOURCE_STATES finalState); diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12Device.h b/engine/include/XCEngine/RHI/D3D12/D3D12Device.h index 8ef6b53c..506c3ab1 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12Device.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12Device.h @@ -93,7 +93,9 @@ public: RHIResourceView* CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) override; RHIResourceView* CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) override; + RHIResourceView* CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) override; + RHIResourceView* CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) override; bool ReadTexturePixelRGBA8( RHICommandQueue* commandQueue, diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h b/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h index 30401835..630dadb0 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h @@ -42,9 +42,23 @@ public: void InitializeAsShaderResource(ID3D12Device* device, ID3D12Resource* resource, const D3D12_SHADER_RESOURCE_VIEW_DESC* desc, D3D12DescriptorHeap* heap, uint32_t slotIndex); + void InitializeAsShaderResourceBuffer( + ID3D12Device* device, + D3D12Buffer* buffer, + const ResourceViewDesc& resourceViewDesc, + const D3D12_SHADER_RESOURCE_VIEW_DESC* desc, + D3D12DescriptorHeap* heap, + uint32_t slotIndex); void InitializeAsUnorderedAccess(ID3D12Device* device, ID3D12Resource* resource, const D3D12_UNORDERED_ACCESS_VIEW_DESC* desc, D3D12DescriptorHeap* heap, uint32_t slotIndex); + void InitializeAsUnorderedAccessBuffer( + ID3D12Device* device, + D3D12Buffer* buffer, + const ResourceViewDesc& resourceViewDesc, + const D3D12_UNORDERED_ACCESS_VIEW_DESC* desc, + D3D12DescriptorHeap* heap, + uint32_t slotIndex); void InitializeAsConstantBuffer(ID3D12Device* device, ID3D12Resource* resource, const D3D12_CONSTANT_BUFFER_VIEW_DESC* desc, D3D12DescriptorHeap* heap, uint32_t slotIndex); diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLBuffer.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLBuffer.h index b17b4618..2fef4792 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLBuffer.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLBuffer.h @@ -16,7 +16,8 @@ enum class OpenGLBufferType { AtomicCounter, DispatchIndirect, DrawIndirect, - ShaderBindingTable + ShaderBindingTable, + ShaderStorage }; class OpenGLBuffer : public RHIBuffer { diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h index b5925dff..036b8779 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h @@ -16,9 +16,13 @@ struct DescriptorBinding { uint32_t binding; DescriptorType type; uint32_t count; + ResourceViewDimension resourceDimension = ResourceViewDimension::Unknown; std::vector textureUnits; std::vector textureIds; std::vector textureTargets; + std::vector bufferIds; + std::vector bufferOffsets; + std::vector bufferSizes; std::vector samplerIds; }; diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h index 6f9bdf5c..1d386d8d 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h @@ -64,7 +64,9 @@ public: RHIResourceView* CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) override; RHIResourceView* CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) override; + RHIResourceView* CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) override; + RHIResourceView* CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) override; const RHICapabilities& GetCapabilities() const override; diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLEnums.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLEnums.h index f84d550c..0c933b3a 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLEnums.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLEnums.h @@ -164,6 +164,7 @@ inline GLenum ToOpenGL(OpenGLBufferType type) { case OpenGLBufferType::DispatchIndirect: return GL_DISPATCH_INDIRECT_BUFFER; case OpenGLBufferType::DrawIndirect: return GL_DRAW_INDIRECT_BUFFER; case OpenGLBufferType::ShaderBindingTable: return GL_SHADER_STORAGE_BUFFER; + case OpenGLBufferType::ShaderStorage: return GL_SHADER_STORAGE_BUFFER; default: return GL_ARRAY_BUFFER; } } diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h index c8666f12..57c24c73 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h @@ -36,11 +36,17 @@ public: OpenGLTexture* texture, const ResourceViewDesc& desc, OpenGLTextureUnitAllocator* allocator); + bool InitializeAsShaderResource( + OpenGLBuffer* buffer, + const ResourceViewDesc& desc); bool InitializeAsUnorderedAccess( OpenGLTexture* texture, const ResourceViewDesc& desc, OpenGLTextureUnitAllocator* allocator); + bool InitializeAsUnorderedAccess( + OpenGLBuffer* buffer, + const ResourceViewDesc& desc); bool InitializeAsConstantBuffer( OpenGLBuffer* buffer, diff --git a/engine/include/XCEngine/RHI/RHIDevice.h b/engine/include/XCEngine/RHI/RHIDevice.h index 00196380..2a3d8551 100644 --- a/engine/include/XCEngine/RHI/RHIDevice.h +++ b/engine/include/XCEngine/RHI/RHIDevice.h @@ -64,7 +64,9 @@ public: virtual RHIResourceView* CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) = 0; virtual RHIResourceView* CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) = 0; virtual RHIResourceView* CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) = 0; + virtual RHIResourceView* CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) = 0; virtual RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) = 0; + virtual RHIResourceView* CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) = 0; virtual RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) = 0; virtual bool ReadTexturePixelRGBA8( diff --git a/engine/include/XCEngine/RHI/RHIEnums.h b/engine/include/XCEngine/RHI/RHIEnums.h index dbd92f43..495f8763 100644 --- a/engine/include/XCEngine/RHI/RHIEnums.h +++ b/engine/include/XCEngine/RHI/RHIEnums.h @@ -108,9 +108,25 @@ enum class BufferType : uint8_t { ReadBack, Indirect, RaytracingAccelerationStructure, - ShaderBindingTable + ShaderBindingTable, + Storage }; +enum class BufferFlags : uint64_t { + None = 0, + AllowUnorderedAccess = 1ull << 0 +}; + +inline BufferFlags operator|(BufferFlags left, BufferFlags right) { + return static_cast( + static_cast(left) | static_cast(right)); +} + +inline BufferFlags operator&(BufferFlags left, BufferFlags right) { + return static_cast( + static_cast(left) & static_cast(right)); +} + enum class DescriptorType : uint8_t { CBV, SRV, @@ -277,6 +293,12 @@ enum class ResourceViewDimension : uint8_t { RawBuffer }; +inline bool IsBufferResourceViewDimension(ResourceViewDimension dimension) { + return dimension == ResourceViewDimension::Buffer || + dimension == ResourceViewDimension::StructuredBuffer || + dimension == ResourceViewDimension::RawBuffer; +} + enum class ResourceViewType : uint8_t { VertexBuffer, IndexBuffer, diff --git a/engine/include/XCEngine/RHI/RHITypes.h b/engine/include/XCEngine/RHI/RHITypes.h index 9c2e7967..cc14532d 100644 --- a/engine/include/XCEngine/RHI/RHITypes.h +++ b/engine/include/XCEngine/RHI/RHITypes.h @@ -370,6 +370,7 @@ struct DescriptorSetLayoutBinding { uint32_t type = 0; uint32_t count = 0; uint32_t visibility = 0; + ResourceViewDimension resourceDimension = ResourceViewDimension::Unknown; }; struct DescriptorSetLayoutDesc { @@ -394,6 +395,8 @@ struct ResourceViewDesc { uint32_t firstArraySlice = 0; uint32_t planeSlice = 0; uint64_t bufferLocation = 0; + uint32_t firstElement = 0; + uint32_t elementCount = 0; uint32_t structureByteStride = 0; }; diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanCommon.h b/engine/include/XCEngine/RHI/Vulkan/VulkanCommon.h index 96f77a79..37f4b772 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanCommon.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanCommon.h @@ -324,14 +324,20 @@ inline VkSampleCountFlagBits ToVulkanSampleCount(uint32_t sampleCount) { } } -inline VkDescriptorType ToVulkanDescriptorType(DescriptorType type) { +inline VkDescriptorType ToVulkanDescriptorType( + DescriptorType type, + ResourceViewDimension resourceDimension = ResourceViewDimension::Unknown) { switch (type) { case DescriptorType::CBV: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; case DescriptorType::SRV: - return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + return IsBufferResourceViewDimension(resourceDimension) + ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; case DescriptorType::UAV: - return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + return IsBufferResourceViewDimension(resourceDimension) + ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; case DescriptorType::Sampler: return VK_DESCRIPTOR_TYPE_SAMPLER; default: diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanDevice.h b/engine/include/XCEngine/RHI/Vulkan/VulkanDevice.h index c90721d5..92f9028c 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanDevice.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanDevice.h @@ -39,7 +39,9 @@ public: RHIResourceView* CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) override; RHIResourceView* CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) override; + RHIResourceView* CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) override; + RHIResourceView* CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) override; RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) override; const RHICapabilities& GetCapabilities() const override { return m_capabilities; } diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h b/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h index 137234c2..e4b881fd 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h @@ -17,7 +17,9 @@ public: bool InitializeAsRenderTarget(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc); bool InitializeAsDepthStencil(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc); bool InitializeAsShaderResource(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc); + bool InitializeAsShaderResource(VulkanBuffer* buffer, const ResourceViewDesc& desc); bool InitializeAsUnorderedAccess(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc); + bool InitializeAsUnorderedAccess(VulkanBuffer* buffer, const ResourceViewDesc& desc); bool InitializeAsVertexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc); bool InitializeAsIndexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc); diff --git a/engine/src/RHI/D3D12/D3D12Buffer.cpp b/engine/src/RHI/D3D12/D3D12Buffer.cpp index c55257c2..82479904 100644 --- a/engine/src/RHI/D3D12/D3D12Buffer.cpp +++ b/engine/src/RHI/D3D12/D3D12Buffer.cpp @@ -10,7 +10,12 @@ D3D12Buffer::~D3D12Buffer() { Shutdown(); } -bool D3D12Buffer::Initialize(ID3D12Device* device, uint64_t size, D3D12_RESOURCE_STATES initialState, D3D12_HEAP_TYPE heapType) { +bool D3D12Buffer::Initialize( + ID3D12Device* device, + uint64_t size, + D3D12_RESOURCE_STATES initialState, + D3D12_HEAP_TYPE heapType, + D3D12_RESOURCE_FLAGS resourceFlags) { D3D12_HEAP_PROPERTIES heapProperties = {}; heapProperties.Type = heapType; heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; @@ -29,7 +34,7 @@ bool D3D12Buffer::Initialize(ID3D12Device* device, uint64_t size, D3D12_RESOURCE bufferDesc.SampleDesc.Count = 1; bufferDesc.SampleDesc.Quality = 0; bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + bufferDesc.Flags = resourceFlags; HRESULT hResult = device->CreateCommittedResource( &heapProperties, diff --git a/engine/src/RHI/D3D12/D3D12Device.cpp b/engine/src/RHI/D3D12/D3D12Device.cpp index 93919d6f..7e1ea537 100644 --- a/engine/src/RHI/D3D12/D3D12Device.cpp +++ b/engine/src/RHI/D3D12/D3D12Device.cpp @@ -311,6 +311,89 @@ DXGI_FORMAT ResolveD3D12ShaderResourceViewFormat(Format format) { } } +bool IsSupportedBufferViewDimension(ResourceViewDimension dimension) { + return dimension == ResourceViewDimension::Buffer || + dimension == ResourceViewDimension::StructuredBuffer || + dimension == ResourceViewDimension::RawBuffer; +} + +uint32_t ResolveBufferViewElementStride(RHIBuffer* buffer, const ResourceViewDesc& desc) { + if (desc.dimension == ResourceViewDimension::RawBuffer) { + return 4u; + } + + if (desc.structureByteStride > 0) { + return desc.structureByteStride; + } + + if (desc.dimension == ResourceViewDimension::Buffer && desc.format != 0) { + return GetFormatBytesPerPixel(static_cast(desc.format)); + } + + return buffer != nullptr ? buffer->GetStride() : 0u; +} + +bool TryResolveBufferViewFirstElement( + RHIBuffer* buffer, + const ResourceViewDesc& desc, + uint32_t& outFirstElement, + uint32_t& outElementStride) { + outFirstElement = 0; + outElementStride = ResolveBufferViewElementStride(buffer, desc); + if (outElementStride == 0) { + return false; + } + + if ((desc.bufferLocation % outElementStride) != 0) { + return false; + } + + outFirstElement = + desc.firstElement + static_cast(desc.bufferLocation / outElementStride); + return true; +} + +bool TryResolveBufferViewElementCount( + RHIBuffer* buffer, + const ResourceViewDesc& desc, + uint32_t elementStride, + uint32_t firstElement, + uint32_t& outElementCount) { + outElementCount = 0; + if (desc.elementCount > 0) { + outElementCount = desc.elementCount; + return true; + } + + if (buffer == nullptr || elementStride == 0) { + return false; + } + + const uint64_t byteOffset = static_cast(firstElement) * elementStride; + if (byteOffset >= buffer->GetSize()) { + return false; + } + + outElementCount = static_cast((buffer->GetSize() - byteOffset) / elementStride); + return outElementCount > 0; +} + +DXGI_FORMAT ResolveD3D12BufferViewFormat(const ResourceViewDesc& desc) { + if (desc.dimension == ResourceViewDimension::RawBuffer) { + return DXGI_FORMAT_R32_TYPELESS; + } + + if (desc.dimension == ResourceViewDimension::StructuredBuffer) { + return DXGI_FORMAT_UNKNOWN; + } + + if (desc.dimension == ResourceViewDimension::Buffer && desc.format != 0) { + return ToD3D12(static_cast(desc.format)); + } + + return DXGI_FORMAT_UNKNOWN; +} + } // namespace D3D12Device::D3D12Device() @@ -687,17 +770,27 @@ const RHIDeviceInfo& D3D12Device::GetDeviceInfo() const { RHIBuffer* D3D12Device::CreateBuffer(const BufferDesc& desc) { auto* buffer = new D3D12Buffer(); + const BufferType bufferType = static_cast(desc.bufferType); D3D12_HEAP_TYPE heapType = D3D12_HEAP_TYPE_DEFAULT; - if (desc.bufferType == static_cast(BufferType::ReadBack)) { + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON; + D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_NONE; + + if (bufferType == BufferType::ReadBack) { heapType = D3D12_HEAP_TYPE_READBACK; - } else if (desc.bufferType == static_cast(BufferType::Constant) || - desc.bufferType == static_cast(BufferType::Vertex) || - desc.bufferType == static_cast(BufferType::Index)) { + initialState = D3D12_RESOURCE_STATE_COPY_DEST; + } else if (bufferType == BufferType::Constant || + bufferType == BufferType::Vertex || + bufferType == BufferType::Index) { heapType = D3D12_HEAP_TYPE_UPLOAD; + initialState = D3D12_RESOURCE_STATE_GENERIC_READ; + } else if (bufferType == BufferType::Storage) { + resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } - if (buffer->Initialize(m_device.Get(), desc.size, D3D12_RESOURCE_STATE_COMMON, heapType)) { + + if (buffer->Initialize(m_device.Get(), desc.size, initialState, heapType, resourceFlags)) { buffer->SetStride(desc.stride); - buffer->SetBufferType(static_cast(desc.bufferType)); + buffer->SetBufferType(bufferType); + buffer->SetState(ResourceStates::Common); return buffer; } delete buffer; @@ -1148,6 +1241,60 @@ RHIResourceView* D3D12Device::CreateDepthStencilView(RHITexture* texture, const return view; } +RHIResourceView* D3D12Device::CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) { + if (buffer == nullptr || m_device == nullptr || !IsSupportedBufferViewDimension(desc.dimension)) { + return nullptr; + } + if (desc.dimension == ResourceViewDimension::Buffer && desc.format == 0) { + return nullptr; + } + + uint32_t firstElement = 0; + uint32_t elementStride = 0; + if (!TryResolveBufferViewFirstElement(buffer, desc, firstElement, elementStride)) { + return nullptr; + } + + uint32_t elementCount = 0; + if (!TryResolveBufferViewElementCount(buffer, desc, elementStride, firstElement, elementCount)) { + return nullptr; + } + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = ResolveD3D12BufferViewFormat(desc); + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Buffer.FirstElement = firstElement; + srvDesc.Buffer.NumElements = elementCount; + srvDesc.Buffer.StructureByteStride = + desc.dimension == ResourceViewDimension::StructuredBuffer ? elementStride : 0u; + srvDesc.Buffer.Flags = + desc.dimension == ResourceViewDimension::RawBuffer + ? D3D12_BUFFER_SRV_FLAG_RAW + : D3D12_BUFFER_SRV_FLAG_NONE; + + auto heap = std::make_unique(); + if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) { + return nullptr; + } + + auto* view = new D3D12ResourceView(); + view->InitializeAsShaderResourceBuffer( + m_device.Get(), + static_cast(buffer), + desc, + &srvDesc, + heap.get(), + 0); + if (!view->IsValid()) { + delete view; + return nullptr; + } + + view->SetOwnedHeap(std::move(heap)); + return view; +} + RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) { auto* view = new D3D12ResourceView(); auto* d3d12Texture = static_cast(texture); @@ -1217,6 +1364,59 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons return view; } +RHIResourceView* D3D12Device::CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) { + if (buffer == nullptr || m_device == nullptr || !IsSupportedBufferViewDimension(desc.dimension)) { + return nullptr; + } + if (desc.dimension == ResourceViewDimension::Buffer && desc.format == 0) { + return nullptr; + } + + uint32_t firstElement = 0; + uint32_t elementStride = 0; + if (!TryResolveBufferViewFirstElement(buffer, desc, firstElement, elementStride)) { + return nullptr; + } + + uint32_t elementCount = 0; + if (!TryResolveBufferViewElementCount(buffer, desc, elementStride, firstElement, elementCount)) { + return nullptr; + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = ResolveD3D12BufferViewFormat(desc); + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.FirstElement = firstElement; + uavDesc.Buffer.NumElements = elementCount; + uavDesc.Buffer.StructureByteStride = + desc.dimension == ResourceViewDimension::StructuredBuffer ? elementStride : 0u; + uavDesc.Buffer.Flags = + desc.dimension == ResourceViewDimension::RawBuffer + ? D3D12_BUFFER_UAV_FLAG_RAW + : D3D12_BUFFER_UAV_FLAG_NONE; + + auto heap = std::make_unique(); + if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) { + return nullptr; + } + + auto* view = new D3D12ResourceView(); + view->InitializeAsUnorderedAccessBuffer( + m_device.Get(), + static_cast(buffer), + desc, + &uavDesc, + heap.get(), + 0); + if (!view->IsValid()) { + delete view; + return nullptr; + } + + view->SetOwnedHeap(std::move(heap)); + return view; +} + RHIResourceView* D3D12Device::CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) { auto* view = new D3D12ResourceView(); auto* d3d12Texture = static_cast(texture); diff --git a/engine/src/RHI/D3D12/D3D12ResourceView.cpp b/engine/src/RHI/D3D12/D3D12ResourceView.cpp index 67937ba2..47c44625 100644 --- a/engine/src/RHI/D3D12/D3D12ResourceView.cpp +++ b/engine/src/RHI/D3D12/D3D12ResourceView.cpp @@ -60,6 +60,49 @@ ResourceViewDimension ToResourceViewDimension(D3D12_UAV_DIMENSION dimension) { } } +uint32_t ResolveBufferElementStride(D3D12Buffer* buffer, const ResourceViewDesc& desc) { + if (desc.dimension == ResourceViewDimension::RawBuffer) { + return 4u; + } + + if (desc.structureByteStride > 0) { + return desc.structureByteStride; + } + + return buffer != nullptr ? buffer->GetStride() : 0u; +} + +uint64_t ResolveBufferByteOffset(D3D12Buffer* buffer, const ResourceViewDesc& desc) { + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return desc.bufferLocation; + } + + return desc.bufferLocation + static_cast(desc.firstElement) * elementStride; +} + +uint32_t ResolveBufferElementCount(D3D12Buffer* buffer, const ResourceViewDesc& desc) { + if (desc.elementCount > 0) { + return desc.elementCount; + } + + if (buffer == nullptr) { + return 0; + } + + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return 0; + } + + const uint64_t byteOffset = ResolveBufferByteOffset(buffer, desc); + if (byteOffset >= buffer->GetSize()) { + return 0; + } + + return static_cast((buffer->GetSize() - byteOffset) / elementStride); +} + } // namespace D3D12ResourceView::D3D12ResourceView() @@ -161,6 +204,29 @@ void D3D12ResourceView::InitializeAsShaderResource(ID3D12Device* device, ID3D12R device->CreateShaderResourceView(resource, desc, m_handle); } +void D3D12ResourceView::InitializeAsShaderResourceBuffer( + ID3D12Device* device, + D3D12Buffer* buffer, + const ResourceViewDesc& resourceViewDesc, + const D3D12_SHADER_RESOURCE_VIEW_DESC* desc, + D3D12DescriptorHeap* heap, + uint32_t slotIndex) { + if (buffer == nullptr) { + return; + } + + InitializeAsShaderResource(device, buffer->GetResource(), desc, heap, slotIndex); + m_dimension = + resourceViewDesc.dimension != ResourceViewDimension::Unknown + ? resourceViewDesc.dimension + : ResourceViewDimension::Buffer; + m_bufferOffset = ResolveBufferByteOffset(buffer, resourceViewDesc); + m_bufferLocation = buffer->GetGPUVirtualAddress() + m_bufferOffset; + m_bufferStride = ResolveBufferElementStride(buffer, resourceViewDesc); + const uint32_t elementCount = ResolveBufferElementCount(buffer, resourceViewDesc); + m_bufferSize = elementCount * m_bufferStride; +} + void D3D12ResourceView::InitializeAsUnorderedAccess(ID3D12Device* device, ID3D12Resource* resource, const D3D12_UNORDERED_ACCESS_VIEW_DESC* desc, D3D12DescriptorHeap* heap, uint32_t slotIndex) { @@ -176,6 +242,29 @@ void D3D12ResourceView::InitializeAsUnorderedAccess(ID3D12Device* device, ID3D12 device->CreateUnorderedAccessView(resource, nullptr, desc, m_handle); } +void D3D12ResourceView::InitializeAsUnorderedAccessBuffer( + ID3D12Device* device, + D3D12Buffer* buffer, + const ResourceViewDesc& resourceViewDesc, + const D3D12_UNORDERED_ACCESS_VIEW_DESC* desc, + D3D12DescriptorHeap* heap, + uint32_t slotIndex) { + if (buffer == nullptr) { + return; + } + + InitializeAsUnorderedAccess(device, buffer->GetResource(), desc, heap, slotIndex); + m_dimension = + resourceViewDesc.dimension != ResourceViewDimension::Unknown + ? resourceViewDesc.dimension + : ResourceViewDimension::Buffer; + m_bufferOffset = ResolveBufferByteOffset(buffer, resourceViewDesc); + m_bufferLocation = buffer->GetGPUVirtualAddress() + m_bufferOffset; + m_bufferStride = ResolveBufferElementStride(buffer, resourceViewDesc); + const uint32_t elementCount = ResolveBufferElementCount(buffer, resourceViewDesc); + m_bufferSize = elementCount * m_bufferStride; +} + void D3D12ResourceView::InitializeAsConstantBuffer(ID3D12Device* device, ID3D12Resource* resource, const D3D12_CONSTANT_BUFFER_VIEW_DESC* desc, D3D12DescriptorHeap* heap, uint32_t slotIndex) { diff --git a/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp b/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp index edc26648..d12729ad 100644 --- a/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp +++ b/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp @@ -34,6 +34,10 @@ uint32_t ResolveBindingPoint( } } +bool UsesBufferBinding(ResourceViewDimension dimension) { + return IsBufferResourceViewDimension(dimension); +} + } // namespace OpenGLDescriptorSet::OpenGLDescriptorSet() @@ -81,11 +85,20 @@ bool OpenGLDescriptorSet::Initialize(OpenGLTextureUnitAllocator* allocator, uint m_bindings[i].binding = layout.bindings[i].binding; m_bindings[i].type = static_cast(layout.bindings[i].type); m_bindings[i].count = layout.bindings[i].count; + m_bindings[i].resourceDimension = layout.bindings[i].resourceDimension; m_bindings[i].textureUnits.resize(layout.bindings[i].count); m_bindings[i].textureIds.resize(layout.bindings[i].count, 0); m_bindings[i].textureTargets.resize(layout.bindings[i].count, GL_TEXTURE_2D); + m_bindings[i].bufferIds.resize(layout.bindings[i].count, 0); + m_bindings[i].bufferOffsets.resize(layout.bindings[i].count, 0); + m_bindings[i].bufferSizes.resize(layout.bindings[i].count, 0); m_bindings[i].samplerIds.resize(layout.bindings[i].count, 0); + if (UsesBufferBinding(m_bindings[i].resourceDimension)) { + m_bindings[i].textureUnits.clear(); + continue; + } + if (m_bindings[i].type != DescriptorType::SRV && m_bindings[i].type != DescriptorType::Sampler && m_bindings[i].type != DescriptorType::UAV) { @@ -156,11 +169,32 @@ void OpenGLDescriptorSet::Update(uint32_t offset, RHIResourceView* view) { } DescriptorBinding* binding = FindBinding(offset); - if (binding == nullptr || binding->textureIds.empty()) { + if (binding == nullptr) { return; } OpenGLResourceView* glView = static_cast(view); + const bool expectsBuffer = UsesBufferBinding(binding->resourceDimension); + const bool viewIsBuffer = IsBufferResourceViewDimension(glView->GetDimension()); + if (expectsBuffer != viewIsBuffer) { + return; + } + + if (expectsBuffer) { + if (binding->bufferIds.empty()) { + return; + } + + binding->bufferIds[0] = glView->GetBuffer(); + binding->bufferOffsets[0] = glView->GetBufferOffset(); + binding->bufferSizes[0] = glView->GetBufferSize(); + return; + } + + if (binding->textureIds.empty()) { + return; + } + binding->textureIds[0] = glView->GetTexture(); if (const OpenGLTexture* texture = glView->GetTextureResource()) { binding->textureTargets[0] = static_cast(ToOpenGL(texture->GetOpenGLType())); @@ -199,6 +233,31 @@ void OpenGLDescriptorSet::Bind() { for (size_t i = 0; i < m_bindings.size(); ++i) { const auto& binding = m_bindings[i]; + if (UsesBufferBinding(binding.resourceDimension)) { + for (size_t j = 0; j < binding.bufferIds.size(); ++j) { + const GLuint bufferId = binding.bufferIds[j]; + if (bufferId == 0) { + continue; + } + + const GLuint rangeSize = j < binding.bufferSizes.size() ? binding.bufferSizes[j] : 0u; + if (rangeSize > 0) { + glBindBufferRange( + GL_SHADER_STORAGE_BUFFER, + binding.binding + static_cast(j), + bufferId, + static_cast(binding.bufferOffsets[j]), + static_cast(rangeSize)); + } else { + glBindBufferBase( + GL_SHADER_STORAGE_BUFFER, + binding.binding + static_cast(j), + bufferId); + } + } + continue; + } + for (size_t j = 0; j < binding.textureUnits.size(); ++j) { uint32_t unit = binding.textureUnits[j]; uint32_t textureId = binding.textureIds[j]; @@ -246,6 +305,29 @@ void OpenGLDescriptorSet::BindWithPipelineLayout(const OpenGLPipelineLayout* pip continue; } + if (UsesBufferBinding(binding.resourceDimension)) { + for (size_t i = 0; i < binding.bufferIds.size(); ++i) { + const GLuint bufferId = binding.bufferIds[i]; + if (bufferId == 0) { + continue; + } + + const uint32_t bindingPoint = baseBindingPoint + static_cast(i); + const GLuint rangeSize = i < binding.bufferSizes.size() ? binding.bufferSizes[i] : 0u; + if (rangeSize > 0) { + glBindBufferRange( + GL_SHADER_STORAGE_BUFFER, + bindingPoint, + bufferId, + static_cast(binding.bufferOffsets[i]), + static_cast(rangeSize)); + } else { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, bufferId); + } + } + continue; + } + for (size_t i = 0; i < binding.textureIds.size(); ++i) { const uint32_t bindingPoint = baseBindingPoint + static_cast(i); const uint32_t textureId = binding.textureIds[i]; @@ -282,6 +364,13 @@ void OpenGLDescriptorSet::Unbind() { } } + if (UsesBufferBinding(binding.resourceDimension)) { + for (size_t j = 0; j < binding.bufferIds.size(); ++j) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding.binding + static_cast(j), 0); + } + continue; + } + for (size_t j = 0; j < binding.textureUnits.size(); ++j) { uint32_t unit = binding.textureUnits[j]; diff --git a/engine/src/RHI/OpenGL/OpenGLDevice.cpp b/engine/src/RHI/OpenGL/OpenGLDevice.cpp index 196b065c..1cf1c1c9 100644 --- a/engine/src/RHI/OpenGL/OpenGLDevice.cpp +++ b/engine/src/RHI/OpenGL/OpenGLDevice.cpp @@ -1181,20 +1181,35 @@ void OpenGLDevice::SwapBuffers() { RHIBuffer* OpenGLDevice::CreateBuffer(const BufferDesc& desc) { auto* buffer = new OpenGLBuffer(); OpenGLBufferType bufferType = OpenGLBufferType::Vertex; - - switch (desc.bufferType) { - case 1: + + switch (static_cast(desc.bufferType)) { + case BufferType::Index: bufferType = OpenGLBufferType::Index; break; - case 2: + case BufferType::Constant: bufferType = OpenGLBufferType::Uniform; break; + case BufferType::ReadBack: + bufferType = OpenGLBufferType::CopyRead; + break; + case BufferType::Indirect: + bufferType = OpenGLBufferType::DrawIndirect; + break; + case BufferType::Storage: + bufferType = OpenGLBufferType::ShaderStorage; + break; + case BufferType::Vertex: default: bufferType = OpenGLBufferType::Vertex; break; } - - buffer->Initialize(bufferType, desc.size, nullptr, false); + + if (!buffer->Initialize(bufferType, desc.size, nullptr, false)) { + delete buffer; + return nullptr; + } + + buffer->SetBufferType(static_cast(desc.bufferType)); buffer->SetStride(desc.stride); return buffer; } @@ -1575,6 +1590,21 @@ RHIResourceView* OpenGLDevice::CreateDepthStencilView(RHITexture* texture, const return view; } +RHIResourceView* OpenGLDevice::CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) { + if (!buffer || !IsBufferResourceViewDimension(desc.dimension)) { + return nullptr; + } + + auto* glBuffer = static_cast(buffer); + auto* view = new OpenGLResourceView(); + if (!view->InitializeAsShaderResource(glBuffer, desc)) { + delete view; + return nullptr; + } + + return view; +} + RHIResourceView* OpenGLDevice::CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) { if (!texture) { return nullptr; @@ -1591,6 +1621,21 @@ RHIResourceView* OpenGLDevice::CreateShaderResourceView(RHITexture* texture, con return view; } +RHIResourceView* OpenGLDevice::CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) { + if (!buffer || !IsBufferResourceViewDimension(desc.dimension)) { + return nullptr; + } + + auto* glBuffer = static_cast(buffer); + auto* view = new OpenGLResourceView(); + if (!view->InitializeAsUnorderedAccess(glBuffer, desc)) { + delete view; + return nullptr; + } + + return view; +} + RHIResourceView* OpenGLDevice::CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) { if (!texture) { return nullptr; diff --git a/engine/src/RHI/OpenGL/OpenGLResourceView.cpp b/engine/src/RHI/OpenGL/OpenGLResourceView.cpp index 5880dd57..22d84023 100644 --- a/engine/src/RHI/OpenGL/OpenGLResourceView.cpp +++ b/engine/src/RHI/OpenGL/OpenGLResourceView.cpp @@ -55,6 +55,49 @@ FramebufferAttachmentType ResolveDepthAttachmentType(Format format) { : FramebufferAttachmentType::Depth; } +uint32_t ResolveBufferElementStride(OpenGLBuffer* buffer, const ResourceViewDesc& desc) { + if (desc.dimension == ResourceViewDimension::RawBuffer) { + return 4u; + } + + if (desc.structureByteStride > 0) { + return desc.structureByteStride; + } + + return buffer != nullptr ? buffer->GetStride() : 0u; +} + +uint64_t ResolveBufferByteOffset(OpenGLBuffer* buffer, const ResourceViewDesc& desc) { + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return desc.bufferLocation; + } + + return desc.bufferLocation + static_cast(desc.firstElement) * elementStride; +} + +uint32_t ResolveBufferElementCount(OpenGLBuffer* buffer, const ResourceViewDesc& desc) { + if (desc.elementCount > 0) { + return desc.elementCount; + } + + if (buffer == nullptr) { + return 0; + } + + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return 0; + } + + const uint64_t byteOffset = ResolveBufferByteOffset(buffer, desc); + if (byteOffset >= buffer->GetSize()) { + return 0; + } + + return static_cast((buffer->GetSize() - byteOffset) / elementStride); +} + } // namespace OpenGLResourceView::OpenGLResourceView() @@ -113,7 +156,8 @@ void* OpenGLResourceView::GetNativeHandle() { return reinterpret_cast(static_cast(m_framebufferID)); case ResourceViewType::ShaderResource: case ResourceViewType::UnorderedAccess: - return reinterpret_cast(static_cast(m_texture ? m_texture->GetID() : 0)); + return reinterpret_cast(static_cast( + m_texture != nullptr ? m_texture->GetID() : (m_buffer != nullptr ? m_buffer->GetID() : 0))); case ResourceViewType::ConstantBuffer: return reinterpret_cast(static_cast(m_buffer ? m_buffer->GetID() : 0)); default: @@ -130,9 +174,11 @@ bool OpenGLResourceView::IsValid() const { case ResourceViewType::DepthStencil: return m_framebufferID != 0; case ResourceViewType::ShaderResource: - return m_texture != nullptr && m_texture->GetID() != 0; + return (m_texture != nullptr && m_texture->GetID() != 0) || + (m_buffer != nullptr && m_buffer->GetID() != 0 && m_bufferSize > 0); case ResourceViewType::UnorderedAccess: - return m_texture != nullptr && m_textureUnit >= 0; + return (m_texture != nullptr && m_textureUnit >= 0) || + (m_buffer != nullptr && m_buffer->GetID() != 0 && m_bufferSize > 0); case ResourceViewType::ConstantBuffer: return m_buffer != nullptr && m_bindingPoint >= 0; default: @@ -230,6 +276,62 @@ bool OpenGLResourceView::InitializeAsUnorderedAccess( return true; } +bool OpenGLResourceView::InitializeAsShaderResource( + OpenGLBuffer* buffer, + const ResourceViewDesc& desc) { + if (!buffer || !IsBufferResourceViewDimension(desc.dimension)) { + return false; + } + + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return false; + } + + const uint32_t elementCount = ResolveBufferElementCount(buffer, desc); + if (elementCount == 0) { + return false; + } + + m_viewType = ResourceViewType::ShaderResource; + m_format = desc.format != 0 ? static_cast(desc.format) : Format::Unknown; + m_dimension = + desc.dimension != ResourceViewDimension::Unknown ? desc.dimension : ResourceViewDimension::Buffer; + m_buffer = buffer; + m_bufferOffset = ResolveBufferByteOffset(buffer, desc); + m_bufferStride = elementStride; + m_bufferSize = elementCount * elementStride; + return true; +} + +bool OpenGLResourceView::InitializeAsUnorderedAccess( + OpenGLBuffer* buffer, + const ResourceViewDesc& desc) { + if (!buffer || !IsBufferResourceViewDimension(desc.dimension)) { + return false; + } + + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return false; + } + + const uint32_t elementCount = ResolveBufferElementCount(buffer, desc); + if (elementCount == 0) { + return false; + } + + m_viewType = ResourceViewType::UnorderedAccess; + m_format = desc.format != 0 ? static_cast(desc.format) : Format::Unknown; + m_dimension = + desc.dimension != ResourceViewDimension::Unknown ? desc.dimension : ResourceViewDimension::Buffer; + m_buffer = buffer; + m_bufferOffset = ResolveBufferByteOffset(buffer, desc); + m_bufferStride = elementStride; + m_bufferSize = elementCount * elementStride; + return true; +} + bool OpenGLResourceView::InitializeAsConstantBuffer( OpenGLBuffer* buffer, const ResourceViewDesc& desc, diff --git a/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp b/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp index b8910b24..533c300c 100644 --- a/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp +++ b/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp @@ -17,7 +17,9 @@ std::vector BuildVulkanLayoutBindings(const Descri const DescriptorSetLayoutBinding& binding = layout.bindings[i]; VkDescriptorSetLayoutBinding vkBinding = {}; vkBinding.binding = binding.binding; - vkBinding.descriptorType = ToVulkanDescriptorType(static_cast(binding.type)); + vkBinding.descriptorType = ToVulkanDescriptorType( + static_cast(binding.type), + binding.resourceDimension); vkBinding.descriptorCount = binding.count > 0 ? binding.count : 1u; vkBinding.stageFlags = ToVulkanShaderStageFlags(binding.visibility); bindings.push_back(vkBinding); @@ -47,6 +49,7 @@ bool VulkanDescriptorPool::Initialize(VkDevice device, const DescriptorPoolDesc& poolSizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, desc.descriptorCount }); poolSizes.push_back({ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, desc.descriptorCount }); poolSizes.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, desc.descriptorCount }); + poolSizes.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, desc.descriptorCount }); break; case DescriptorHeapType::Sampler: poolSizes.push_back({ VK_DESCRIPTOR_TYPE_SAMPLER, desc.descriptorCount }); diff --git a/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp b/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp index b64536fc..79dc3833 100644 --- a/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp +++ b/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp @@ -80,32 +80,63 @@ void VulkanDescriptorSet::Update(uint32_t offset, RHIResourceView* view) { } auto* vulkanView = static_cast(view); - if (vulkanView == nullptr || vulkanView->GetImageView() == VK_NULL_HANDLE) { + if (vulkanView == nullptr) { return; } - VkDescriptorImageInfo imageInfo = {}; - imageInfo.imageView = vulkanView->GetImageView(); - VkWriteDescriptorSet write = {}; write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write.dstSet = m_descriptorSet; write.dstBinding = offset; write.descriptorCount = 1; - switch (static_cast(binding->type)) { - case DescriptorType::SRV: - imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - write.pImageInfo = &imageInfo; - break; - case DescriptorType::UAV: - imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - write.pImageInfo = &imageInfo; - break; - default: + const DescriptorType descriptorType = static_cast(binding->type); + const bool expectsBuffer = IsBufferResourceViewDimension(binding->resourceDimension); + const bool viewIsBuffer = IsBufferResourceViewDimension(vulkanView->GetDimension()); + if (viewIsBuffer) { + if (!expectsBuffer) { return; + } + + if (vulkanView->GetBuffer() == VK_NULL_HANDLE || vulkanView->GetBufferSize() == 0) { + return; + } + + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = vulkanView->GetBuffer(); + bufferInfo.offset = static_cast(vulkanView->GetBufferOffset()); + bufferInfo.range = static_cast(vulkanView->GetBufferSize()); + + write.descriptorType = ToVulkanDescriptorType(descriptorType, binding->resourceDimension); + write.pBufferInfo = &bufferInfo; + vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr); + return; + } + + if (expectsBuffer) { + return; + } + + if (vulkanView->GetImageView() == VK_NULL_HANDLE) { + return; + } + + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageView = vulkanView->GetImageView(); + + switch (descriptorType) { + case DescriptorType::SRV: + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + write.pImageInfo = &imageInfo; + break; + case DescriptorType::UAV: + imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + write.pImageInfo = &imageInfo; + break; + default: + return; } vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr); diff --git a/engine/src/RHI/Vulkan/VulkanDevice.cpp b/engine/src/RHI/Vulkan/VulkanDevice.cpp index 6aa013c5..62b71121 100644 --- a/engine/src/RHI/Vulkan/VulkanDevice.cpp +++ b/engine/src/RHI/Vulkan/VulkanDevice.cpp @@ -510,6 +510,9 @@ RHIBuffer* VulkanDevice::CreateBuffer(const BufferDesc& desc) { case BufferType::Constant: usageFlags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; break; + case BufferType::Storage: + usageFlags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + break; case BufferType::Vertex: default: usageFlags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; @@ -772,6 +775,20 @@ RHIResourceView* VulkanDevice::CreateDepthStencilView(RHITexture* texture, const return nullptr; } +RHIResourceView* VulkanDevice::CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) { + if (buffer == nullptr || !IsBufferResourceViewDimension(desc.dimension)) { + return nullptr; + } + + auto* view = new VulkanResourceView(); + if (view->InitializeAsShaderResource(static_cast(buffer), desc)) { + return view; + } + + delete view; + return nullptr; +} + RHIResourceView* VulkanDevice::CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) { auto* view = new VulkanResourceView(); if (view->InitializeAsShaderResource(m_device, static_cast(texture), desc)) { @@ -782,6 +799,20 @@ RHIResourceView* VulkanDevice::CreateShaderResourceView(RHITexture* texture, con return nullptr; } +RHIResourceView* VulkanDevice::CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) { + if (buffer == nullptr || !IsBufferResourceViewDimension(desc.dimension)) { + return nullptr; + } + + auto* view = new VulkanResourceView(); + if (view->InitializeAsUnorderedAccess(static_cast(buffer), desc)) { + return view; + } + + delete view; + return nullptr; +} + RHIResourceView* VulkanDevice::CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) { auto* view = new VulkanResourceView(); if (view->InitializeAsUnorderedAccess(m_device, static_cast(texture), desc)) { diff --git a/engine/src/RHI/Vulkan/VulkanPipelineLayout.cpp b/engine/src/RHI/Vulkan/VulkanPipelineLayout.cpp index 4e3b6a6f..70477875 100644 --- a/engine/src/RHI/Vulkan/VulkanPipelineLayout.cpp +++ b/engine/src/RHI/Vulkan/VulkanPipelineLayout.cpp @@ -145,7 +145,9 @@ bool VulkanPipelineLayout::CreateNativeSetLayouts() { const DescriptorSetLayoutBinding& binding = setLayout.bindings[i]; VkDescriptorSetLayoutBinding vkBinding = {}; vkBinding.binding = binding.binding; - vkBinding.descriptorType = ToVulkanDescriptorType(static_cast(binding.type)); + vkBinding.descriptorType = ToVulkanDescriptorType( + static_cast(binding.type), + binding.resourceDimension); vkBinding.descriptorCount = binding.count > 0 ? binding.count : 1u; vkBinding.stageFlags = ToVulkanShaderStageFlags(binding.visibility); vkBindings.push_back(vkBinding); diff --git a/engine/src/RHI/Vulkan/VulkanResourceView.cpp b/engine/src/RHI/Vulkan/VulkanResourceView.cpp index 8f085392..a1a18eba 100644 --- a/engine/src/RHI/Vulkan/VulkanResourceView.cpp +++ b/engine/src/RHI/Vulkan/VulkanResourceView.cpp @@ -6,6 +6,53 @@ namespace XCEngine { namespace RHI { +namespace { + +uint32_t ResolveBufferElementStride(VulkanBuffer* buffer, const ResourceViewDesc& desc) { + if (desc.dimension == ResourceViewDimension::RawBuffer) { + return 4u; + } + + if (desc.structureByteStride > 0) { + return desc.structureByteStride; + } + + return buffer != nullptr ? buffer->GetStride() : 0u; +} + +uint64_t ResolveBufferByteOffset(VulkanBuffer* buffer, const ResourceViewDesc& desc) { + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return desc.bufferLocation; + } + + return desc.bufferLocation + static_cast(desc.firstElement) * elementStride; +} + +uint32_t ResolveBufferElementCount(VulkanBuffer* buffer, const ResourceViewDesc& desc) { + if (desc.elementCount > 0) { + return desc.elementCount; + } + + if (buffer == nullptr) { + return 0; + } + + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return 0; + } + + const uint64_t byteOffset = ResolveBufferByteOffset(buffer, desc); + if (byteOffset >= buffer->GetSize()) { + return 0; + } + + return static_cast((buffer->GetSize() - byteOffset) / elementStride); +} + +} // namespace + VulkanResourceView::~VulkanResourceView() { Shutdown(); } @@ -85,6 +132,32 @@ bool VulkanResourceView::InitializeAsShaderResource(VkDevice device, VulkanTextu return vkCreateImageView(device, &viewInfo, nullptr, &m_imageView) == VK_SUCCESS; } +bool VulkanResourceView::InitializeAsShaderResource(VulkanBuffer* buffer, const ResourceViewDesc& desc) { + if (buffer == nullptr || buffer->GetBuffer() == VK_NULL_HANDLE || !IsBufferResourceViewDimension(desc.dimension)) { + return false; + } + + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return false; + } + + const uint32_t elementCount = ResolveBufferElementCount(buffer, desc); + if (elementCount == 0) { + return false; + } + + m_viewType = ResourceViewType::ShaderResource; + m_dimension = + desc.dimension != ResourceViewDimension::Unknown ? desc.dimension : ResourceViewDimension::Buffer; + m_format = desc.format != 0 ? static_cast(desc.format) : Format::Unknown; + m_buffer = buffer; + m_bufferOffset = ResolveBufferByteOffset(buffer, desc); + m_bufferStride = elementStride; + m_bufferSize = elementCount * elementStride; + return true; +} + bool VulkanResourceView::InitializeAsUnorderedAccess(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc) { if (device == VK_NULL_HANDLE || texture == nullptr || texture->GetImage() == VK_NULL_HANDLE) { return false; @@ -110,6 +183,32 @@ bool VulkanResourceView::InitializeAsUnorderedAccess(VkDevice device, VulkanText return vkCreateImageView(device, &viewInfo, nullptr, &m_imageView) == VK_SUCCESS; } +bool VulkanResourceView::InitializeAsUnorderedAccess(VulkanBuffer* buffer, const ResourceViewDesc& desc) { + if (buffer == nullptr || buffer->GetBuffer() == VK_NULL_HANDLE || !IsBufferResourceViewDimension(desc.dimension)) { + return false; + } + + const uint32_t elementStride = ResolveBufferElementStride(buffer, desc); + if (elementStride == 0) { + return false; + } + + const uint32_t elementCount = ResolveBufferElementCount(buffer, desc); + if (elementCount == 0) { + return false; + } + + m_viewType = ResourceViewType::UnorderedAccess; + m_dimension = + desc.dimension != ResourceViewDimension::Unknown ? desc.dimension : ResourceViewDimension::Buffer; + m_format = desc.format != 0 ? static_cast(desc.format) : Format::Unknown; + m_buffer = buffer; + m_bufferOffset = ResolveBufferByteOffset(buffer, desc); + m_bufferStride = elementStride; + m_bufferSize = elementCount * elementStride; + return true; +} + bool VulkanResourceView::InitializeAsVertexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc) { if (buffer == nullptr || buffer->GetBuffer() == VK_NULL_HANDLE) { return false; @@ -155,9 +254,11 @@ bool VulkanResourceView::IsValid() const { return m_buffer != nullptr && m_buffer->GetBuffer() != VK_NULL_HANDLE; case ResourceViewType::RenderTarget: case ResourceViewType::DepthStencil: + return m_imageView != VK_NULL_HANDLE; case ResourceViewType::ShaderResource: case ResourceViewType::UnorderedAccess: - return m_imageView != VK_NULL_HANDLE; + return m_imageView != VK_NULL_HANDLE || + (m_buffer != nullptr && m_buffer->GetBuffer() != VK_NULL_HANDLE && m_bufferSize > 0); default: return false; } diff --git a/tests/RHI/unit/CMakeLists.txt b/tests/RHI/unit/CMakeLists.txt index e0362e56..c59bd027 100644 --- a/tests/RHI/unit/CMakeLists.txt +++ b/tests/RHI/unit/CMakeLists.txt @@ -57,5 +57,9 @@ target_include_directories(rhi_unit_tests PRIVATE ${CMAKE_SOURCE_DIR}/mvs/OpenGL/package/include/ ) +if(MSVC) + target_compile_options(rhi_unit_tests PRIVATE /FS) +endif() + include(GoogleTest) gtest_discover_tests(rhi_unit_tests) diff --git a/tests/RHI/unit/test_descriptor_set.cpp b/tests/RHI/unit/test_descriptor_set.cpp index 03ad1547..c5a48ba2 100644 --- a/tests/RHI/unit/test_descriptor_set.cpp +++ b/tests/RHI/unit/test_descriptor_set.cpp @@ -2,6 +2,7 @@ #include "XCEngine/RHI/D3D12/D3D12DescriptorSet.h" #include "XCEngine/RHI/RHIDescriptorPool.h" #include "XCEngine/RHI/RHIDescriptorSet.h" +#include "XCEngine/RHI/RHIBuffer.h" #include "XCEngine/RHI/RHISampler.h" #include "XCEngine/RHI/RHIResourceView.h" #include "XCEngine/RHI/RHITexture.h" @@ -362,6 +363,59 @@ TEST_P(RHITestFixture, DescriptorSet_ConstantBufferSize) { delete pool; } +TEST_P(RHITestFixture, DescriptorSet_Update_BufferResourceView) { + DescriptorPoolDesc poolDesc = {}; + poolDesc.type = DescriptorHeapType::CBV_SRV_UAV; + poolDesc.descriptorCount = 10; + poolDesc.shaderVisible = true; + + RHIDescriptorPool* pool = GetDevice()->CreateDescriptorPool(poolDesc); + ASSERT_NE(pool, nullptr); + + BufferDesc bufferDesc = {}; + bufferDesc.size = 256; + bufferDesc.stride = 16; + bufferDesc.bufferType = static_cast(BufferType::Storage); + + RHIBuffer* buffer = GetDevice()->CreateBuffer(bufferDesc); + ASSERT_NE(buffer, nullptr); + + ResourceViewDesc viewDesc = {}; + viewDesc.dimension = ResourceViewDimension::StructuredBuffer; + viewDesc.structureByteStride = 16; + viewDesc.elementCount = 16; + + RHIResourceView* srv = GetDevice()->CreateShaderResourceView(buffer, viewDesc); + ASSERT_NE(srv, nullptr); + + DescriptorSetLayoutBinding binding = {}; + binding.binding = 0; + binding.type = static_cast(DescriptorType::SRV); + binding.count = 1; + binding.visibility = static_cast(ShaderVisibility::All); + binding.resourceDimension = ResourceViewDimension::StructuredBuffer; + + DescriptorSetLayoutDesc layoutDesc = {}; + layoutDesc.bindingCount = 1; + layoutDesc.bindings = &binding; + + RHIDescriptorSet* set = pool->AllocateSet(layoutDesc); + ASSERT_NE(set, nullptr); + ASSERT_NE(set->GetBindings(), nullptr); + EXPECT_EQ(set->GetBindings()[0].resourceDimension, ResourceViewDimension::StructuredBuffer); + + set->Update(0, srv); + + set->Shutdown(); + delete set; + srv->Shutdown(); + delete srv; + buffer->Shutdown(); + delete buffer; + pool->Shutdown(); + delete pool; +} + TEST_P(RHITestFixture, DescriptorSet_MultipleAllocations_AdvanceDescriptorOffsets) { DescriptorPoolDesc poolDesc = {}; poolDesc.type = DescriptorHeapType::CBV_SRV_UAV; @@ -402,3 +456,69 @@ TEST_P(RHITestFixture, DescriptorSet_MultipleAllocations_AdvanceDescriptorOffset pool->Shutdown(); delete pool; } + +TEST_P(RHITestFixture, DescriptorSet_Update_BufferViews) { + DescriptorPoolDesc poolDesc = {}; + poolDesc.type = DescriptorHeapType::CBV_SRV_UAV; + poolDesc.descriptorCount = 8; + poolDesc.shaderVisible = true; + + RHIDescriptorPool* pool = GetDevice()->CreateDescriptorPool(poolDesc); + ASSERT_NE(pool, nullptr); + + BufferDesc bufferDesc = {}; + bufferDesc.size = 256; + bufferDesc.stride = 16; + bufferDesc.bufferType = static_cast(BufferType::Storage); + + RHIBuffer* buffer = GetDevice()->CreateBuffer(bufferDesc); + ASSERT_NE(buffer, nullptr); + + ResourceViewDesc srvDesc = {}; + srvDesc.dimension = ResourceViewDimension::StructuredBuffer; + srvDesc.structureByteStride = 16; + srvDesc.elementCount = 8; + + ResourceViewDesc uavDesc = {}; + uavDesc.dimension = ResourceViewDimension::RawBuffer; + uavDesc.firstElement = 4; + uavDesc.elementCount = 8; + + RHIResourceView* srv = GetDevice()->CreateShaderResourceView(buffer, srvDesc); + RHIResourceView* uav = GetDevice()->CreateUnorderedAccessView(buffer, uavDesc); + ASSERT_NE(srv, nullptr); + ASSERT_NE(uav, nullptr); + + DescriptorSetLayoutBinding bindings[2] = {}; + bindings[0].binding = 0; + bindings[0].type = static_cast(DescriptorType::SRV); + bindings[0].count = 1; + bindings[0].resourceDimension = ResourceViewDimension::StructuredBuffer; + bindings[1].binding = 1; + bindings[1].type = static_cast(DescriptorType::UAV); + bindings[1].count = 1; + bindings[1].resourceDimension = ResourceViewDimension::RawBuffer; + + DescriptorSetLayoutDesc layoutDesc = {}; + layoutDesc.bindings = bindings; + layoutDesc.bindingCount = 2; + + RHIDescriptorSet* set = pool->AllocateSet(layoutDesc); + ASSERT_NE(set, nullptr); + + set->Update(0, srv); + set->Update(1, uav); + set->Bind(); + set->Unbind(); + + set->Shutdown(); + delete set; + srv->Shutdown(); + delete srv; + uav->Shutdown(); + delete uav; + buffer->Shutdown(); + delete buffer; + pool->Shutdown(); + delete pool; +} diff --git a/tests/RHI/unit/test_views.cpp b/tests/RHI/unit/test_views.cpp index 8a0c7cf5..73cba4a4 100644 --- a/tests/RHI/unit/test_views.cpp +++ b/tests/RHI/unit/test_views.cpp @@ -236,6 +236,57 @@ TEST_P(RHITestFixture, Device_CreateIndexBufferView) { delete buffer; } +TEST_P(RHITestFixture, Device_CreateStructuredBufferShaderResourceView) { + BufferDesc bufferDesc = {}; + bufferDesc.size = 256; + bufferDesc.stride = 16; + bufferDesc.bufferType = static_cast(BufferType::Storage); + + RHIBuffer* buffer = GetDevice()->CreateBuffer(bufferDesc); + ASSERT_NE(buffer, nullptr); + + ResourceViewDesc viewDesc = {}; + viewDesc.dimension = ResourceViewDimension::StructuredBuffer; + viewDesc.structureByteStride = 16; + viewDesc.elementCount = 16; + + RHIResourceView* srv = GetDevice()->CreateShaderResourceView(buffer, viewDesc); + ASSERT_NE(srv, nullptr); + EXPECT_TRUE(srv->IsValid()); + EXPECT_EQ(srv->GetViewType(), ResourceViewType::ShaderResource); + EXPECT_EQ(srv->GetDimension(), ResourceViewDimension::StructuredBuffer); + + srv->Shutdown(); + delete srv; + buffer->Shutdown(); + delete buffer; +} + +TEST_P(RHITestFixture, Device_CreateRawBufferUnorderedAccessView) { + BufferDesc bufferDesc = {}; + bufferDesc.size = 256; + bufferDesc.stride = 4; + bufferDesc.bufferType = static_cast(BufferType::Storage); + + RHIBuffer* buffer = GetDevice()->CreateBuffer(bufferDesc); + ASSERT_NE(buffer, nullptr); + + ResourceViewDesc viewDesc = {}; + viewDesc.dimension = ResourceViewDimension::RawBuffer; + viewDesc.elementCount = 64; + + RHIResourceView* uav = GetDevice()->CreateUnorderedAccessView(buffer, viewDesc); + ASSERT_NE(uav, nullptr); + EXPECT_TRUE(uav->IsValid()); + EXPECT_EQ(uav->GetViewType(), ResourceViewType::UnorderedAccess); + EXPECT_EQ(uav->GetDimension(), ResourceViewDimension::RawBuffer); + + uav->Shutdown(); + delete uav; + buffer->Shutdown(); + delete buffer; +} + TEST_P(RHITestFixture, Device_CreateRenderTargetView_Multiple) { TextureDesc texDesc = {}; texDesc.width = 256;