diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorPool.h b/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorPool.h index d97702f1..cc025604 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorPool.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorPool.h @@ -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; diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorSet.h b/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorSet.h index a2f6db5a..9ad1f9a5 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorSet.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanDescriptorSet.h @@ -1,8 +1,10 @@ #pragma once #include "XCEngine/RHI/RHIDescriptorSet.h" +#include "XCEngine/RHI/Vulkan/VulkanBuffer.h" #include "XCEngine/RHI/Vulkan/VulkanCommon.h" +#include #include #include @@ -47,6 +49,12 @@ public: VkDescriptorSetLayout GetDescriptorSetLayout() const { return m_descriptorSetLayout; } private: + struct ConstantBindingRecord { + std::vector data; + std::unique_ptr 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 m_bindings; std::unordered_map m_bindingToIndex; + std::unordered_map m_constantBindings; std::vector m_constantBufferData; bool m_constantDirty = false; }; diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanPipelineState.h b/engine/include/XCEngine/RHI/Vulkan/VulkanPipelineState.h index 4391a7b7..9970daa3 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanPipelineState.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanPipelineState.h @@ -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(m_depthStencilFormat) != Format::Unknown; + } private: VulkanDevice* m_deviceOwner = nullptr; diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h b/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h index 4f3e2dce..c1146a4a 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h @@ -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); diff --git a/engine/src/RHI/Vulkan/VulkanCommandList.cpp b/engine/src/RHI/Vulkan/VulkanCommandList.cpp index a5540959..3dd17762 100644 --- a/engine/src/RHI/Vulkan/VulkanCommandList.cpp +++ b/engine/src/RHI/Vulkan/VulkanCommandList.cpp @@ -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(m_currentColorTarget); + if (colorView != nullptr && colorView->GetTexture() != nullptr) { + EndActiveRenderPass(); - auto* colorView = static_cast(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(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(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(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; diff --git a/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp b/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp index 0d913fed..b8910b24 100644 --- a/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp +++ b/engine/src/RHI/Vulkan/VulkanDescriptorPool.cpp @@ -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; diff --git a/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp b/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp index 9055c3da..45a113f9 100644 --- a/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp +++ b/engine/src/RHI/Vulkan/VulkanDescriptorSet.cpp @@ -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(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(bufferSize); + bufferDesc.bufferType = static_cast(BufferType::Constant); + + constantBinding.buffer = std::make_unique(); + 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(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; } diff --git a/engine/src/RHI/Vulkan/VulkanDevice.cpp b/engine/src/RHI/Vulkan/VulkanDevice.cpp index cdb573de..44a2e1e9 100644 --- a/engine/src/RHI/Vulkan/VulkanDevice.cpp +++ b/engine/src/RHI/Vulkan/VulkanDevice.cpp @@ -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(texture), desc)) { + return view; + } + + delete view; return nullptr; } diff --git a/engine/src/RHI/Vulkan/VulkanPipelineState.cpp b/engine/src/RHI/Vulkan/VulkanPipelineState.cpp index 20ec31ae..f4e50dd4 100644 --- a/engine/src/RHI/Vulkan/VulkanPipelineState.cpp +++ b/engine/src/RHI/Vulkan/VulkanPipelineState.cpp @@ -145,6 +145,9 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin m_ownsPipelineLayout = true; } + std::vector attachments; + attachments.reserve(2); + VkAttachmentDescription colorAttachment = {}; colorAttachment.format = ToVulkanFormat(static_cast(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(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(attachments.size()); + renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; diff --git a/engine/src/RHI/Vulkan/VulkanResourceView.cpp b/engine/src/RHI/Vulkan/VulkanResourceView.cpp index 22dc6836..07b196bc 100644 --- a/engine/src/RHI/Vulkan/VulkanResourceView.cpp +++ b/engine/src/RHI/Vulkan/VulkanResourceView.cpp @@ -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(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; diff --git a/tests/RHI/integration/fixtures/RHIIntegrationFixture.cpp b/tests/RHI/integration/fixtures/RHIIntegrationFixture.cpp index f508140d..2b663356 100644 --- a/tests/RHI/integration/fixtures/RHIIntegrationFixture.cpp +++ b/tests/RHI/integration/fixtures/RHIIntegrationFixture.cpp @@ -139,13 +139,35 @@ void RHIIntegrationFixture::SetUp() { mBackBufferViews.push_back(rtv); } - mDepthStencilTexture = new D3D12Texture(); - ASSERT_NE(mDepthStencilTexture, nullptr); - ASSERT_TRUE(mDepthStencilTexture->InitializeDepthStencil(device, width, height)); + auto* depthStencilTexture = new D3D12Texture(); + ASSERT_NE(depthStencilTexture, nullptr); + ASSERT_TRUE(depthStencilTexture->InitializeDepthStencil(device, width, height)); + mDepthStencilTexture = depthStencilTexture; D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = D3D12ResourceView::CreateDepthStencilDesc(Format::D24_UNorm_S8_UInt, D3D12_DSV_DIMENSION_TEXTURE2D); auto* d3d12DSV = new D3D12ResourceView(); - d3d12DSV->InitializeAsDepthStencil(device, mDepthStencilTexture->GetResource(), &dsvDesc, mDSVHeap, 0); - mDSV = d3d12DSV; + d3d12DSV->InitializeAsDepthStencil(device, depthStencilTexture->GetResource(), &dsvDesc, mDSVHeap, 0); + mDepthStencilView = d3d12DSV; + } else if (GetParam() == RHIType::Vulkan) { + TextureDesc depthDesc = {}; + depthDesc.width = static_cast(width); + depthDesc.height = static_cast(height); + depthDesc.depth = 1; + depthDesc.mipLevels = 1; + depthDesc.arraySize = 1; + depthDesc.format = static_cast(Format::D24_UNorm_S8_UInt); + depthDesc.textureType = static_cast(TextureType::Texture2D); + depthDesc.sampleCount = 1; + depthDesc.sampleQuality = 0; + depthDesc.flags = 0; + + mDepthStencilTexture = mDevice->CreateTexture(depthDesc); + ASSERT_NE(mDepthStencilTexture, nullptr); + + ResourceViewDesc depthViewDesc = {}; + depthViewDesc.dimension = ResourceViewDimension::Texture2D; + depthViewDesc.format = static_cast(Format::D24_UNorm_S8_UInt); + mDepthStencilView = mDevice->CreateDepthStencilView(mDepthStencilTexture, depthViewDesc); + ASSERT_NE(mDepthStencilView, nullptr); } mScreenshot = RHIScreenshot::Create(GetParam()); @@ -179,7 +201,7 @@ void RHIIntegrationFixture::SetRenderTargetForClear(bool includeDepthStencil) { d3d12CmdList->TransitionBarrier(backBuffer->GetResource(), ResourceStates::Present, ResourceStates::RenderTarget); RHIResourceView* rtv = mBackBufferViews[mCurrentBackBufferIndex]; Log("[TEST] SetRenderTargetForClear: calling SetRenderTargets, rtv=%p", (void*)rtv); - mCommandList->SetRenderTargets(1, &rtv, includeDepthStencil ? mDSV : nullptr); + mCommandList->SetRenderTargets(1, &rtv, includeDepthStencil ? mDepthStencilView : nullptr); Log("[TEST] SetRenderTargetForClear: done"); } else { Log("[TEST] SetRenderTargetForClear: skipped - condition failed"); @@ -204,7 +226,7 @@ void RHIIntegrationFixture::SetRenderTargetForClear(bool includeDepthStencil) { ASSERT_NE(mBackBufferViews[backBufferIndex], nullptr); RHIResourceView* rtv = mBackBufferViews[backBufferIndex]; - mCommandList->SetRenderTargets(1, &rtv, nullptr); + mCommandList->SetRenderTargets(1, &rtv, includeDepthStencil ? mDepthStencilView : nullptr); } } @@ -232,19 +254,19 @@ void RHIIntegrationFixture::TearDown() { } mBackBufferViews.clear(); + if (mDepthStencilView) { + mDepthStencilView->Shutdown(); + delete mDepthStencilView; + mDepthStencilView = nullptr; + } + + if (mDepthStencilTexture) { + mDepthStencilTexture->Shutdown(); + delete mDepthStencilTexture; + mDepthStencilTexture = nullptr; + } + if (GetParam() == RHIType::D3D12) { - if (mDSV) { - mDSV->Shutdown(); - delete mDSV; - mDSV = nullptr; - } - - if (mDepthStencilTexture) { - mDepthStencilTexture->Shutdown(); - delete mDepthStencilTexture; - mDepthStencilTexture = nullptr; - } - if (mRTVHeap) { mRTVHeap->Shutdown(); delete mRTVHeap; diff --git a/tests/RHI/integration/fixtures/RHIIntegrationFixture.h b/tests/RHI/integration/fixtures/RHIIntegrationFixture.h index b490f5c6..51b1d82e 100644 --- a/tests/RHI/integration/fixtures/RHIIntegrationFixture.h +++ b/tests/RHI/integration/fixtures/RHIIntegrationFixture.h @@ -11,7 +11,9 @@ #include "XCEngine/RHI/RHICommandList.h" #include "XCEngine/RHI/RHISwapChain.h" #include "XCEngine/RHI/RHIScreenshot.h" +#include "XCEngine/RHI/RHIResourceView.h" #include "XCEngine/RHI/RHIEnums.h" +#include "XCEngine/RHI/RHITexture.h" #include "XCEngine/RHI/OpenGL/OpenGLDevice.h" #include "XCEngine/Debug/Logger.h" #include "XCEngine/Debug/ConsoleLogSink.h" @@ -68,12 +70,12 @@ private: HWND mWindow = nullptr; int mCurrentBackBufferIndex = 0; std::vector mBackBufferViews; + RHITexture* mDepthStencilTexture = nullptr; + RHIResourceView* mDepthStencilView = nullptr; #if defined(XCENGINE_SUPPORT_D3D12) D3D12DescriptorHeap* mRTVHeap = nullptr; D3D12DescriptorHeap* mDSVHeap = nullptr; - D3D12Texture* mDepthStencilTexture = nullptr; - RHIResourceView* mDSV = nullptr; #endif }; diff --git a/tests/RHI/integration/sphere/CMakeLists.txt b/tests/RHI/integration/sphere/CMakeLists.txt index 6eb78628..01373ffe 100644 --- a/tests/RHI/integration/sphere/CMakeLists.txt +++ b/tests/RHI/integration/sphere/CMakeLists.txt @@ -9,6 +9,8 @@ set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package) get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE) +find_package(Vulkan QUIET) + add_executable(rhi_integration_sphere main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../fixtures/RHIIntegrationFixture.cpp @@ -33,6 +35,60 @@ target_link_libraries(rhi_integration_sphere PRIVATE GTest::gtest ) +if(Vulkan_FOUND) + set(XCENGINE_GLSLANG_VALIDATOR_HINT "$ENV{VULKAN_SDK}") + find_program( + XCENGINE_GLSLANG_VALIDATOR + NAMES glslangValidator glslangValidator.exe + HINTS + "${XCENGINE_GLSLANG_VALIDATOR_HINT}/Bin" + "${Vulkan_ROOT}/Bin") + + if(NOT XCENGINE_GLSLANG_VALIDATOR) + file(GLOB XCENGINE_VULKAN_BIN_DIRS "D:/VulkanSDK/*/Bin") + if(XCENGINE_VULKAN_BIN_DIRS) + list(SORT XCENGINE_VULKAN_BIN_DIRS COMPARE NATURAL ORDER DESCENDING) + foreach(XCENGINE_VULKAN_BIN_DIR IN LISTS XCENGINE_VULKAN_BIN_DIRS) + find_program( + XCENGINE_GLSLANG_VALIDATOR + NAMES glslangValidator glslangValidator.exe + PATHS "${XCENGINE_VULKAN_BIN_DIR}" + NO_DEFAULT_PATH) + if(XCENGINE_GLSLANG_VALIDATOR) + break() + endif() + endforeach() + endif() + endif() + + if(NOT XCENGINE_GLSLANG_VALIDATOR) + message(FATAL_ERROR "glslangValidator not found for Vulkan sphere shaders") + endif() + + set(SPHERE_VULKAN_VERTEX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Res/Shader/sphere_vulkan.vert) + set(SPHERE_VULKAN_FRAGMENT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Res/Shader/sphere_vulkan.frag) + + add_custom_command(TARGET rhi_integration_sphere PRE_BUILD + COMMAND ${XCENGINE_GLSLANG_VALIDATOR} + -V + -S + vert + -o + $/sphere_vulkan.vert.spv + ${SPHERE_VULKAN_VERTEX_SOURCE} + COMMAND ${XCENGINE_GLSLANG_VALIDATOR} + -V + -S + frag + -o + $/sphere_vulkan.frag.spv + ${SPHERE_VULKAN_FRAGMENT_SOURCE} + VERBATIM) + + target_link_libraries(rhi_integration_sphere PRIVATE Vulkan::Vulkan) + target_compile_definitions(rhi_integration_sphere PRIVATE XCENGINE_SUPPORT_VULKAN) +endif() + target_compile_definitions(rhi_integration_sphere PRIVATE UNICODE _UNICODE diff --git a/tests/RHI/integration/sphere/Res/Shader/sphere_vulkan.frag b/tests/RHI/integration/sphere/Res/Shader/sphere_vulkan.frag new file mode 100644 index 00000000..57ca55c8 --- /dev/null +++ b/tests/RHI/integration/sphere/Res/Shader/sphere_vulkan.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(set = 2, binding = 0) uniform texture2D uTexture; +layout(set = 3, binding = 0) uniform sampler uSampler; + +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(sampler2D(uTexture, uSampler), vTexCoord); +} diff --git a/tests/RHI/integration/sphere/Res/Shader/sphere_vulkan.vert b/tests/RHI/integration/sphere/Res/Shader/sphere_vulkan.vert new file mode 100644 index 00000000..604744cd --- /dev/null +++ b/tests/RHI/integration/sphere/Res/Shader/sphere_vulkan.vert @@ -0,0 +1,19 @@ +#version 450 + +layout(location = 0) in vec4 aPosition; +layout(location = 1) in vec2 aTexCoord; + +layout(set = 1, binding = 0, std140) uniform MatrixBuffer { + mat4 gProjectionMatrix; + mat4 gViewMatrix; + mat4 gModelMatrix; +}; + +layout(location = 0) out vec2 vTexCoord; + +void main() { + vec4 positionWS = gModelMatrix * aPosition; + vec4 positionVS = gViewMatrix * positionWS; + gl_Position = gProjectionMatrix * positionVS; + vTexCoord = aTexCoord; +} diff --git a/tests/RHI/integration/sphere/main.cpp b/tests/RHI/integration/sphere/main.cpp index dd173bcb..f9255550 100644 --- a/tests/RHI/integration/sphere/main.cpp +++ b/tests/RHI/integration/sphere/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -61,6 +62,27 @@ std::filesystem::path ResolveRuntimePath(const char* relativePath) { return GetExecutableDirectory() / relativePath; } +std::vector LoadBinaryFileRelative(const char* filename) { + const std::filesystem::path path = ResolveRuntimePath(filename); + std::ifstream file(path, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + return {}; + } + + const std::streamsize size = file.tellg(); + if (size <= 0) { + return {}; + } + + std::vector bytes(static_cast(size)); + file.seekg(0, std::ios::beg); + if (!file.read(reinterpret_cast(bytes.data()), size)) { + return {}; + } + + return bytes; +} + void GenerateSphere(std::vector& vertices, std::vector& indices, float radius, int segments) { vertices.clear(); indices.clear(); @@ -188,7 +210,16 @@ void main() { )"; const char* GetScreenshotFilename(RHIType type) { - return type == RHIType::D3D12 ? "sphere_d3d12.ppm" : "sphere_opengl.ppm"; + switch (type) { + case RHIType::D3D12: + return "sphere_d3d12.ppm"; + case RHIType::OpenGL: + return "sphere_opengl.ppm"; + case RHIType::Vulkan: + return "sphere_vulkan.ppm"; + default: + return "sphere_unknown.ppm"; + } } int GetComparisonThreshold(RHIType type) { @@ -275,7 +306,7 @@ GraphicsPipelineDesc CreateSpherePipelineDesc(RHIType type, RHIPipelineLayout* p desc.fragmentShader.sourceLanguage = ShaderLanguage::HLSL; desc.fragmentShader.entryPoint = L"MainPS"; desc.fragmentShader.profile = L"ps_5_0"; - } else { + } else if (type == RHIType::OpenGL) { desc.vertexShader.source.assign(kSphereVertexShader, kSphereVertexShader + strlen(kSphereVertexShader)); desc.vertexShader.sourceLanguage = ShaderLanguage::GLSL; desc.vertexShader.profile = L"vs_4_30"; @@ -283,6 +314,14 @@ GraphicsPipelineDesc CreateSpherePipelineDesc(RHIType type, RHIPipelineLayout* p desc.fragmentShader.source.assign(kSphereFragmentShader, kSphereFragmentShader + strlen(kSphereFragmentShader)); desc.fragmentShader.sourceLanguage = ShaderLanguage::GLSL; desc.fragmentShader.profile = L"fs_4_30"; + } else if (type == RHIType::Vulkan) { + desc.vertexShader.source = LoadBinaryFileRelative("sphere_vulkan.vert.spv"); + desc.vertexShader.sourceLanguage = ShaderLanguage::SPIRV; + desc.vertexShader.entryPoint = L"main"; + + desc.fragmentShader.source = LoadBinaryFileRelative("sphere_vulkan.frag.spv"); + desc.fragmentShader.sourceLanguage = ShaderLanguage::SPIRV; + desc.fragmentShader.entryPoint = L"main"; } return desc; @@ -657,6 +696,9 @@ TEST_P(SphereTest, RenderSphere) { INSTANTIATE_TEST_SUITE_P(D3D12, SphereTest, ::testing::Values(RHIType::D3D12)); INSTANTIATE_TEST_SUITE_P(OpenGL, SphereTest, ::testing::Values(RHIType::OpenGL)); +#if defined(XCENGINE_SUPPORT_VULKAN) +INSTANTIATE_TEST_SUITE_P(Vulkan, SphereTest, ::testing::Values(RHIType::Vulkan)); +#endif GTEST_API_ int main(int argc, char** argv) { Logger::Get().Initialize();