diff --git a/tests/RHI/Vulkan/TEST_SPEC.md b/tests/RHI/Vulkan/TEST_SPEC.md index a68ab338..9cf4f385 100644 --- a/tests/RHI/Vulkan/TEST_SPEC.md +++ b/tests/RHI/Vulkan/TEST_SPEC.md @@ -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 几个文件,避免后续继续堆到单个巨型测试文件中。 diff --git a/tests/RHI/Vulkan/unit/CMakeLists.txt b/tests/RHI/Vulkan/unit/CMakeLists.txt index 856c6a25..4b78fa68 100644 --- a/tests/RHI/Vulkan/unit/CMakeLists.txt +++ b/tests/RHI/Vulkan/unit/CMakeLists.txt @@ -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 ) diff --git a/tests/RHI/Vulkan/unit/test_descriptor_set.cpp b/tests/RHI/Vulkan/unit/test_descriptor_set.cpp new file mode 100644 index 00000000..f474af38 --- /dev/null +++ b/tests/RHI/Vulkan/unit/test_descriptor_set.cpp @@ -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(DescriptorType::UAV); + bindings[0].count = 1; + bindings[1].binding = 0; + bindings[1].type = static_cast(DescriptorType::CBV); + bindings[1].count = 1; + bindings[2].binding = 1; + bindings[2].type = static_cast(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(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(DescriptorType::UAV)); + EXPECT_EQ(storedBindings[1].binding, 0u); + EXPECT_EQ(storedBindings[1].type, static_cast(DescriptorType::CBV)); + EXPECT_EQ(storedBindings[2].binding, 1u); + EXPECT_EQ(storedBindings[2].type, static_cast(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(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(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(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 diff --git a/tests/RHI/Vulkan/unit/test_pipeline_layout.cpp b/tests/RHI/Vulkan/unit/test_pipeline_layout.cpp new file mode 100644 index 00000000..8a9f15cf --- /dev/null +++ b/tests/RHI/Vulkan/unit/test_pipeline_layout.cpp @@ -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(DescriptorType::CBV); + set0Bindings[0].count = 1; + + DescriptorSetLayoutBinding set1Bindings[1] = {}; + set1Bindings[0].binding = 3; + set1Bindings[0].type = static_cast(DescriptorType::SRV); + set1Bindings[0].count = 2; + + DescriptorSetLayoutBinding set2Bindings[1] = {}; + set2Bindings[0].binding = 1; + set2Bindings[0].type = static_cast(DescriptorType::UAV); + set2Bindings[0].count = 1; + + DescriptorSetLayoutBinding set3Bindings[1] = {}; + set3Bindings[0].binding = 5; + set3Bindings[0].type = static_cast(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(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(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(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(DescriptorType::CBV)); + EXPECT_EQ(resolvedDesc.setLayouts[0].bindings[0].count, 2u); + EXPECT_EQ(resolvedDesc.setLayouts[1].bindings[0].type, static_cast(DescriptorType::SRV)); + EXPECT_EQ(resolvedDesc.setLayouts[1].bindings[0].count, 1u); + EXPECT_EQ(resolvedDesc.setLayouts[2].bindings[0].type, static_cast(DescriptorType::UAV)); + EXPECT_EQ(resolvedDesc.setLayouts[3].bindings[0].type, static_cast(DescriptorType::Sampler)); + + layout->Shutdown(); + delete layout; +} + +} // namespace + +#endif