Add Vulkan sphere integration support

This commit is contained in:
2026-03-27 15:07:21 +08:00
parent 18fd09b36f
commit 22ccdfb371
16 changed files with 429 additions and 63 deletions

View File

@@ -9,6 +9,7 @@ namespace XCEngine {
namespace RHI {
class VulkanDescriptorSet;
class VulkanDevice;
class VulkanDescriptorPool : public RHIDescriptorPool {
public:
@@ -29,8 +30,11 @@ public:
VkDevice GetDevice() const { return m_device; }
VkDescriptorPool GetDescriptorPool() const { return m_descriptorPool; }
VulkanDevice* GetDeviceOwner() const { return m_deviceOwner; }
void SetDeviceOwner(VulkanDevice* deviceOwner) { m_deviceOwner = deviceOwner; }
private:
VulkanDevice* m_deviceOwner = nullptr;
VkDevice m_device = VK_NULL_HANDLE;
VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
DescriptorHeapType m_type = DescriptorHeapType::CBV_SRV_UAV;

View File

@@ -1,8 +1,10 @@
#pragma once
#include "XCEngine/RHI/RHIDescriptorSet.h"
#include "XCEngine/RHI/Vulkan/VulkanBuffer.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
#include <memory>
#include <unordered_map>
#include <vector>
@@ -47,6 +49,12 @@ public:
VkDescriptorSetLayout GetDescriptorSetLayout() const { return m_descriptorSetLayout; }
private:
struct ConstantBindingRecord {
std::vector<uint8_t> data;
std::unique_ptr<VulkanBuffer> buffer;
size_t descriptorRange = 0;
};
const DescriptorSetLayoutBinding* FindBinding(uint32_t binding) const;
VkDevice m_device = VK_NULL_HANDLE;
@@ -55,6 +63,7 @@ private:
VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE;
std::vector<DescriptorSetLayoutBinding> m_bindings;
std::unordered_map<uint32_t, uint32_t> m_bindingToIndex;
std::unordered_map<uint32_t, ConstantBindingRecord> m_constantBindings;
std::vector<uint8_t> m_constantBufferData;
bool m_constantDirty = false;
};

View File

@@ -44,6 +44,9 @@ public:
VkPipeline GetPipeline() const { return m_pipeline; }
VkPipelineLayout GetPipelineLayout() const { return m_pipelineLayout; }
VkRenderPass GetRenderPass() const { return m_renderPass; }
bool HasDepthStencilAttachment() const {
return m_depthStencilFormat != 0 && static_cast<Format>(m_depthStencilFormat) != Format::Unknown;
}
private:
VulkanDevice* m_deviceOwner = nullptr;

View File

@@ -15,6 +15,7 @@ public:
~VulkanResourceView() override;
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 InitializeAsVertexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc);
bool InitializeAsIndexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc);

View File

@@ -73,6 +73,18 @@ VkPipelineStageFlags ToVulkanStageMask(ResourceStates state) {
}
}
VkImageAspectFlags ResolveClearAspectMask(Format format, uint32_t buffers) {
VkImageAspectFlags aspectMask = 0;
const VkImageAspectFlags formatAspectMask = GetImageAspectMask(format);
if ((buffers & 2) != 0) {
aspectMask |= formatAspectMask & VK_IMAGE_ASPECT_DEPTH_BIT;
}
if ((buffers & 4) != 0) {
aspectMask |= formatAspectMask & VK_IMAGE_ASPECT_STENCIL_BIT;
}
return aspectMask;
}
} // namespace
VulkanCommandList::~VulkanCommandList() {
@@ -398,40 +410,71 @@ void VulkanCommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount,
}
void VulkanCommandList::Clear(float r, float g, float b, float a, uint32_t buffers) {
(void)buffers;
if ((buffers & 1) != 0) {
auto* colorView = static_cast<VulkanResourceView*>(m_currentColorTarget);
if (colorView != nullptr && colorView->GetTexture() != nullptr) {
EndActiveRenderPass();
auto* colorView = static_cast<VulkanResourceView*>(m_currentColorTarget);
if (colorView == nullptr || colorView->GetTexture() == nullptr) {
return;
VulkanTexture* texture = colorView->GetTexture();
TransitionTexture(texture, ResourceStates::CopyDst);
VkClearColorValue clearColor = {};
clearColor.float32[0] = r;
clearColor.float32[1] = g;
clearColor.float32[2] = b;
clearColor.float32[3] = a;
VkImageSubresourceRange range = {};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = 1;
range.baseArrayLayer = 0;
range.layerCount = 1;
vkCmdClearColorImage(
m_commandBuffer,
texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clearColor,
1,
&range);
TransitionTexture(texture, ResourceStates::RenderTarget);
}
}
EndActiveRenderPass();
if ((buffers & (2 | 4)) != 0) {
auto* depthView = static_cast<VulkanResourceView*>(m_currentDepthTarget);
if (depthView != nullptr && depthView->GetTexture() != nullptr) {
EndActiveRenderPass();
VulkanTexture* texture = colorView->GetTexture();
TransitionTexture(texture, ResourceStates::CopyDst);
VulkanTexture* texture = depthView->GetTexture();
TransitionTexture(texture, ResourceStates::CopyDst);
VkClearColorValue clearColor = {};
clearColor.float32[0] = r;
clearColor.float32[1] = g;
clearColor.float32[2] = b;
clearColor.float32[3] = a;
VkImageSubresourceRange range = {};
range.aspectMask = ResolveClearAspectMask(texture->GetFormat(), buffers);
range.baseMipLevel = 0;
range.levelCount = 1;
range.baseArrayLayer = 0;
range.layerCount = 1;
VkImageSubresourceRange range = {};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = 1;
range.baseArrayLayer = 0;
range.layerCount = 1;
if (range.aspectMask != 0) {
VkClearDepthStencilValue clearValue = {};
clearValue.depth = 1.0f;
clearValue.stencil = 0;
vkCmdClearColorImage(
m_commandBuffer,
texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clearColor,
1,
&range);
vkCmdClearDepthStencilImage(
m_commandBuffer,
texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clearValue,
1,
&range);
}
TransitionTexture(texture, ResourceStates::RenderTarget);
TransitionTexture(texture, ResourceStates::DepthWrite);
}
}
}
void VulkanCommandList::ClearRenderTarget(RHIResourceView* renderTarget, const float color[4]) {
@@ -440,9 +483,36 @@ void VulkanCommandList::ClearRenderTarget(RHIResourceView* renderTarget, const f
}
void VulkanCommandList::ClearDepthStencil(RHIResourceView* depthStencil, float depth, uint8_t stencil) {
(void)depthStencil;
(void)depth;
(void)stencil;
auto* depthView = static_cast<VulkanResourceView*>(depthStencil != nullptr ? depthStencil : m_currentDepthTarget);
if (depthView == nullptr || depthView->GetTexture() == nullptr) {
return;
}
EndActiveRenderPass();
VulkanTexture* texture = depthView->GetTexture();
TransitionTexture(texture, ResourceStates::CopyDst);
VkClearDepthStencilValue clearValue = {};
clearValue.depth = depth;
clearValue.stencil = stencil;
VkImageSubresourceRange range = {};
range.aspectMask = GetImageAspectMask(texture->GetFormat());
range.baseMipLevel = 0;
range.levelCount = 1;
range.baseArrayLayer = 0;
range.layerCount = 1;
vkCmdClearDepthStencilImage(
m_commandBuffer,
texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clearValue,
1,
&range);
TransitionTexture(texture, ResourceStates::DepthWrite);
}
void VulkanCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src) {
@@ -470,15 +540,28 @@ bool VulkanCommandList::EnsureGraphicsRenderPass() {
return false;
}
auto* depthView = static_cast<VulkanResourceView*>(m_currentDepthTarget);
const bool expectsDepthAttachment = m_currentPipelineState->HasDepthStencilAttachment();
if (expectsDepthAttachment &&
(depthView == nullptr || depthView->GetTexture() == nullptr || depthView->GetImageView() == VK_NULL_HANDLE)) {
return false;
}
VulkanTexture* texture = colorView->GetTexture();
TransitionTexture(texture, ResourceStates::RenderTarget);
if (expectsDepthAttachment) {
TransitionTexture(depthView->GetTexture(), ResourceStates::DepthWrite);
}
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = m_currentPipelineState->GetRenderPass();
framebufferInfo.attachmentCount = 1;
const VkImageView attachment = colorView->GetImageView();
framebufferInfo.pAttachments = &attachment;
VkImageView attachments[2] = { colorView->GetImageView(), VK_NULL_HANDLE };
framebufferInfo.attachmentCount = expectsDepthAttachment ? 2 : 1;
if (expectsDepthAttachment) {
attachments[1] = depthView->GetImageView();
}
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = texture->GetWidth();
framebufferInfo.height = texture->GetHeight();
framebufferInfo.layers = 1;

View File

@@ -76,6 +76,7 @@ void VulkanDescriptorPool::Shutdown() {
vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr);
}
m_deviceOwner = nullptr;
m_descriptorPool = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_type = DescriptorHeapType::CBV_SRV_UAV;

View File

@@ -1,5 +1,7 @@
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
#include "XCEngine/RHI/Vulkan/VulkanBuffer.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorPool.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
@@ -56,6 +58,7 @@ void VulkanDescriptorSet::Shutdown() {
m_descriptorSet = VK_NULL_HANDLE;
m_bindings.clear();
m_bindingToIndex.clear();
m_constantBindings.clear();
m_constantBufferData.clear();
m_constantDirty = false;
}
@@ -128,17 +131,72 @@ void VulkanDescriptorSet::UpdateSampler(uint32_t offset, RHISampler* sampler) {
}
void VulkanDescriptorSet::WriteConstant(uint32_t binding, const void* data, size_t size, size_t offset) {
(void)binding;
if (data == nullptr || size == 0) {
return;
}
if (m_constantBufferData.size() < offset + size) {
m_constantBufferData.resize(offset + size);
const DescriptorSetLayoutBinding* layoutBinding = FindBinding(binding);
if (layoutBinding == nullptr ||
static_cast<DescriptorType>(layoutBinding->type) != DescriptorType::CBV) {
return;
}
auto& constantBinding = m_constantBindings[binding];
const size_t requiredSize = offset + size;
if (constantBinding.data.size() < requiredSize) {
constantBinding.data.resize(requiredSize);
}
if (m_constantBufferData.size() < requiredSize) {
m_constantBufferData.resize(requiredSize);
}
std::memcpy(constantBinding.data.data() + offset, data, size);
std::memcpy(m_constantBufferData.data() + offset, data, size);
VulkanDevice* deviceOwner = m_pool != nullptr ? m_pool->GetDeviceOwner() : nullptr;
if (deviceOwner != nullptr) {
const size_t bufferSize = constantBinding.data.size();
if (constantBinding.buffer == nullptr || constantBinding.buffer->GetSize() < bufferSize) {
if (constantBinding.buffer != nullptr) {
constantBinding.buffer->Shutdown();
constantBinding.buffer.reset();
}
BufferDesc bufferDesc = {};
bufferDesc.size = bufferSize;
bufferDesc.stride = static_cast<uint32_t>(bufferSize);
bufferDesc.bufferType = static_cast<uint32_t>(BufferType::Constant);
constantBinding.buffer = std::make_unique<VulkanBuffer>();
if (!constantBinding.buffer->Initialize(
deviceOwner,
bufferDesc,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
constantBinding.buffer.reset();
m_constantDirty = true;
return;
}
}
constantBinding.buffer->SetData(constantBinding.data.data(), constantBinding.data.size(), 0);
constantBinding.descriptorRange = constantBinding.data.size();
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = constantBinding.buffer->GetBuffer();
bufferInfo.offset = 0;
bufferInfo.range = static_cast<VkDeviceSize>(constantBinding.descriptorRange);
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = m_descriptorSet;
write.dstBinding = binding;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
write.pBufferInfo = &bufferInfo;
vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr);
}
m_constantDirty = true;
}

View File

@@ -666,6 +666,7 @@ RHIFramebuffer* VulkanDevice::CreateFramebuffer(class RHIRenderPass* renderPass,
RHIDescriptorPool* VulkanDevice::CreateDescriptorPool(const DescriptorPoolDesc& desc) {
auto* pool = new VulkanDescriptorPool();
pool->SetDeviceOwner(this);
if (pool->Initialize(m_device, desc)) {
return pool;
}
@@ -708,8 +709,12 @@ RHIResourceView* VulkanDevice::CreateRenderTargetView(RHITexture* texture, const
}
RHIResourceView* VulkanDevice::CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) {
(void)texture;
(void)desc;
auto* view = new VulkanResourceView();
if (view->InitializeAsDepthStencil(m_device, static_cast<VulkanTexture*>(texture), desc)) {
return view;
}
delete view;
return nullptr;
}

View File

@@ -145,6 +145,9 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
m_ownsPipelineLayout = true;
}
std::vector<VkAttachmentDescription> attachments;
attachments.reserve(2);
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = ToVulkanFormat(static_cast<Format>(m_renderTargetFormats[0]));
colorAttachment.samples = ToVulkanSampleCount(m_sampleCount);
@@ -154,20 +157,42 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments.push_back(colorAttachment);
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depthAttachmentRef = {};
const bool hasDepthAttachment = HasDepthStencilAttachment();
if (hasDepthAttachment) {
VkAttachmentDescription depthAttachment = {};
depthAttachment.format = ToVulkanFormat(static_cast<Format>(m_depthStencilFormat));
depthAttachment.samples = ToVulkanSampleCount(m_sampleCount);
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments.push_back(depthAttachment);
depthAttachmentRef.attachment = 1;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
if (hasDepthAttachment) {
subpass.pDepthStencilAttachment = &depthAttachmentRef;
}
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;

View File

@@ -35,6 +35,31 @@ bool VulkanResourceView::InitializeAsRenderTarget(VkDevice device, VulkanTexture
return vkCreateImageView(device, &viewInfo, nullptr, &m_imageView) == VK_SUCCESS;
}
bool VulkanResourceView::InitializeAsDepthStencil(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc) {
if (device == VK_NULL_HANDLE || texture == nullptr || texture->GetImage() == VK_NULL_HANDLE) {
return false;
}
m_device = device;
m_texture = texture;
m_viewType = ResourceViewType::DepthStencil;
m_dimension = desc.dimension != ResourceViewDimension::Unknown ? desc.dimension : ResourceViewDimension::Texture2D;
m_format = desc.format != 0 ? static_cast<Format>(desc.format) : texture->GetFormat();
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = texture->GetImage();
viewInfo.viewType = ToVulkanImageViewType(m_dimension, texture->GetTextureType());
viewInfo.format = m_format != Format::Unknown ? ToVulkanFormat(m_format) : texture->GetVkFormat();
viewInfo.subresourceRange.aspectMask = GetImageAspectMask(m_format != Format::Unknown ? m_format : texture->GetFormat());
viewInfo.subresourceRange.baseMipLevel = desc.mipLevel;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = desc.firstArraySlice;
viewInfo.subresourceRange.layerCount = 1;
return vkCreateImageView(device, &viewInfo, nullptr, &m_imageView) == VK_SUCCESS;
}
bool VulkanResourceView::InitializeAsShaderResource(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc) {
if (device == VK_NULL_HANDLE || texture == nullptr || texture->GetImage() == VK_NULL_HANDLE) {
return false;