Add Vulkan descriptor and layout unit coverage

This commit is contained in:
2026-03-27 22:33:26 +08:00
parent 77ff318558
commit b1ddf77e51
4 changed files with 210 additions and 0 deletions

View File

@@ -34,6 +34,8 @@ tests/RHI/Vulkan/
- SPIR-V / GLSL 两条 shader 创建路径
- 基于 GLSL 的 graphics pipeline 创建
- UAV 视图创建与 compute dispatch 写纹理链路
- `DescriptorSet` 的原生句柄、绑定布局元数据与常量缓冲脏标记行为
- `PipelineLayout` 的显式 set-layout 聚合与 flat-count 合成逻辑
当前这些 Vulkan backend unit 用例已经按职责拆分为 fixture / render-pass / shader / pipeline / compute 几个文件,避免后续继续堆到单个巨型测试文件中。

View File

@@ -11,7 +11,9 @@ endif()
set(TEST_SOURCES
fixtures/VulkanTestFixture.cpp
test_compute.cpp
test_descriptor_set.cpp
test_pipeline_state.cpp
test_pipeline_layout.cpp
test_render_pass.cpp
test_shader.cpp
)

View File

@@ -0,0 +1,108 @@
#if defined(XCENGINE_SUPPORT_VULKAN)
#include "fixtures/VulkanTestFixture.h"
#include "XCEngine/RHI/RHIDescriptorPool.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
using namespace XCEngine::RHI;
namespace {
TEST_F(VulkanGraphicsFixture, DescriptorSet_MixedBindingsRetainLayoutMetadata) {
DescriptorPoolDesc poolDesc = {};
poolDesc.type = DescriptorHeapType::CBV_SRV_UAV;
poolDesc.descriptorCount = 4;
poolDesc.shaderVisible = true;
RHIDescriptorPool* pool = m_device->CreateDescriptorPool(poolDesc);
ASSERT_NE(pool, nullptr);
DescriptorSetLayoutBinding bindings[3] = {};
bindings[0].binding = 2;
bindings[0].type = static_cast<uint32_t>(DescriptorType::UAV);
bindings[0].count = 1;
bindings[1].binding = 0;
bindings[1].type = static_cast<uint32_t>(DescriptorType::CBV);
bindings[1].count = 1;
bindings[2].binding = 1;
bindings[2].type = static_cast<uint32_t>(DescriptorType::SRV);
bindings[2].count = 1;
DescriptorSetLayoutDesc layoutDesc = {};
layoutDesc.bindings = bindings;
layoutDesc.bindingCount = 3;
RHIDescriptorSet* set = pool->AllocateSet(layoutDesc);
ASSERT_NE(set, nullptr);
auto* vulkanSet = static_cast<VulkanDescriptorSet*>(set);
ASSERT_NE(vulkanSet->GetDescriptorSet(), VK_NULL_HANDLE);
ASSERT_NE(vulkanSet->GetDescriptorSetLayout(), VK_NULL_HANDLE);
ASSERT_EQ(vulkanSet->GetBindingCount(), 3u);
const DescriptorSetLayoutBinding* storedBindings = vulkanSet->GetBindings();
ASSERT_NE(storedBindings, nullptr);
EXPECT_EQ(storedBindings[0].binding, 2u);
EXPECT_EQ(storedBindings[0].type, static_cast<uint32_t>(DescriptorType::UAV));
EXPECT_EQ(storedBindings[1].binding, 0u);
EXPECT_EQ(storedBindings[1].type, static_cast<uint32_t>(DescriptorType::CBV));
EXPECT_EQ(storedBindings[2].binding, 1u);
EXPECT_EQ(storedBindings[2].type, static_cast<uint32_t>(DescriptorType::SRV));
set->Shutdown();
delete set;
pool->Shutdown();
delete pool;
}
TEST_F(VulkanGraphicsFixture, DescriptorSet_WriteConstantTracksDirtyStateAndBacksData) {
DescriptorPoolDesc poolDesc = {};
poolDesc.type = DescriptorHeapType::CBV_SRV_UAV;
poolDesc.descriptorCount = 1;
poolDesc.shaderVisible = true;
RHIDescriptorPool* pool = m_device->CreateDescriptorPool(poolDesc);
ASSERT_NE(pool, nullptr);
DescriptorSetLayoutBinding binding = {};
binding.binding = 0;
binding.type = static_cast<uint32_t>(DescriptorType::CBV);
binding.count = 1;
DescriptorSetLayoutDesc layoutDesc = {};
layoutDesc.bindings = &binding;
layoutDesc.bindingCount = 1;
RHIDescriptorSet* set = pool->AllocateSet(layoutDesc);
ASSERT_NE(set, nullptr);
auto* vulkanSet = static_cast<VulkanDescriptorSet*>(set);
EXPECT_FALSE(vulkanSet->IsConstantDirty());
EXPECT_EQ(vulkanSet->GetConstantBufferData(), nullptr);
EXPECT_EQ(vulkanSet->GetConstantBufferSize(), 0u);
const float constants[4] = { 1.0f, 2.0f, 3.0f, 4.0f };
vulkanSet->WriteConstant(0, constants, sizeof(constants));
EXPECT_TRUE(vulkanSet->IsConstantDirty());
ASSERT_NE(vulkanSet->GetConstantBufferData(), nullptr);
ASSERT_EQ(vulkanSet->GetConstantBufferSize(), sizeof(constants));
auto* storedConstants = static_cast<const float*>(vulkanSet->GetConstantBufferData());
EXPECT_FLOAT_EQ(storedConstants[0], 1.0f);
EXPECT_FLOAT_EQ(storedConstants[1], 2.0f);
EXPECT_FLOAT_EQ(storedConstants[2], 3.0f);
EXPECT_FLOAT_EQ(storedConstants[3], 4.0f);
vulkanSet->MarkConstantClean();
EXPECT_FALSE(vulkanSet->IsConstantDirty());
set->Shutdown();
delete set;
pool->Shutdown();
delete pool;
}
} // namespace
#endif

View File

@@ -0,0 +1,98 @@
#if defined(XCENGINE_SUPPORT_VULKAN)
#include "fixtures/VulkanTestFixture.h"
#include "XCEngine/RHI/RHIPipelineLayout.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
using namespace XCEngine::RHI;
namespace {
TEST_F(VulkanGraphicsFixture, PipelineLayout_ExplicitSetLayoutsAggregateBindingCounts) {
DescriptorSetLayoutBinding set0Bindings[1] = {};
set0Bindings[0].binding = 0;
set0Bindings[0].type = static_cast<uint32_t>(DescriptorType::CBV);
set0Bindings[0].count = 1;
DescriptorSetLayoutBinding set1Bindings[1] = {};
set1Bindings[0].binding = 3;
set1Bindings[0].type = static_cast<uint32_t>(DescriptorType::SRV);
set1Bindings[0].count = 2;
DescriptorSetLayoutBinding set2Bindings[1] = {};
set2Bindings[0].binding = 1;
set2Bindings[0].type = static_cast<uint32_t>(DescriptorType::UAV);
set2Bindings[0].count = 1;
DescriptorSetLayoutBinding set3Bindings[1] = {};
set3Bindings[0].binding = 5;
set3Bindings[0].type = static_cast<uint32_t>(DescriptorType::Sampler);
set3Bindings[0].count = 1;
DescriptorSetLayoutDesc setLayouts[4] = {};
setLayouts[0].bindings = set0Bindings;
setLayouts[0].bindingCount = 1;
setLayouts[1].bindings = set1Bindings;
setLayouts[1].bindingCount = 1;
setLayouts[2].bindings = set2Bindings;
setLayouts[2].bindingCount = 1;
setLayouts[3].bindings = set3Bindings;
setLayouts[3].bindingCount = 1;
RHIPipelineLayoutDesc desc = {};
desc.setLayouts = setLayouts;
desc.setLayoutCount = 4;
RHIPipelineLayout* layout = m_device->CreatePipelineLayout(desc);
ASSERT_NE(layout, nullptr);
auto* vulkanLayout = static_cast<VulkanPipelineLayout*>(layout);
EXPECT_TRUE(vulkanLayout->UsesSetLayouts());
EXPECT_NE(vulkanLayout->GetPipelineLayout(), VK_NULL_HANDLE);
const RHIPipelineLayoutDesc& resolvedDesc = vulkanLayout->GetDesc();
ASSERT_NE(resolvedDesc.setLayouts, nullptr);
EXPECT_EQ(resolvedDesc.setLayoutCount, 4u);
EXPECT_EQ(resolvedDesc.constantBufferCount, 1u);
EXPECT_EQ(resolvedDesc.textureCount, 2u);
EXPECT_EQ(resolvedDesc.uavCount, 1u);
EXPECT_EQ(resolvedDesc.samplerCount, 1u);
EXPECT_EQ(resolvedDesc.setLayouts[1].bindings[0].binding, 3u);
EXPECT_EQ(resolvedDesc.setLayouts[1].bindings[0].count, 2u);
EXPECT_EQ(resolvedDesc.setLayouts[3].bindings[0].type, static_cast<uint32_t>(DescriptorType::Sampler));
layout->Shutdown();
delete layout;
}
TEST_F(VulkanGraphicsFixture, PipelineLayout_FlatCountsSynthesizePerTypeSetLayouts) {
RHIPipelineLayoutDesc desc = {};
desc.constantBufferCount = 2;
desc.textureCount = 1;
desc.uavCount = 1;
desc.samplerCount = 1;
RHIPipelineLayout* layout = m_device->CreatePipelineLayout(desc);
ASSERT_NE(layout, nullptr);
auto* vulkanLayout = static_cast<VulkanPipelineLayout*>(layout);
EXPECT_NE(vulkanLayout->GetPipelineLayout(), VK_NULL_HANDLE);
const RHIPipelineLayoutDesc& resolvedDesc = vulkanLayout->GetDesc();
ASSERT_NE(resolvedDesc.setLayouts, nullptr);
EXPECT_TRUE(vulkanLayout->UsesSetLayouts());
EXPECT_EQ(resolvedDesc.setLayoutCount, 4u);
EXPECT_EQ(resolvedDesc.setLayouts[0].bindings[0].type, static_cast<uint32_t>(DescriptorType::CBV));
EXPECT_EQ(resolvedDesc.setLayouts[0].bindings[0].count, 2u);
EXPECT_EQ(resolvedDesc.setLayouts[1].bindings[0].type, static_cast<uint32_t>(DescriptorType::SRV));
EXPECT_EQ(resolvedDesc.setLayouts[1].bindings[0].count, 1u);
EXPECT_EQ(resolvedDesc.setLayouts[2].bindings[0].type, static_cast<uint32_t>(DescriptorType::UAV));
EXPECT_EQ(resolvedDesc.setLayouts[3].bindings[0].type, static_cast<uint32_t>(DescriptorType::Sampler));
layout->Shutdown();
delete layout;
}
} // namespace
#endif