diff --git a/tests/RHI/Vulkan/TEST_SPEC.md b/tests/RHI/Vulkan/TEST_SPEC.md index e723b52b..a68ab338 100644 --- a/tests/RHI/Vulkan/TEST_SPEC.md +++ b/tests/RHI/Vulkan/TEST_SPEC.md @@ -10,7 +10,13 @@ tests/RHI/Vulkan/ |- TEST_SPEC.md \- unit/ |- CMakeLists.txt - \- test_backend_specific.cpp + |- fixtures/ + | |- VulkanTestFixture.cpp + | \- VulkanTestFixture.h + |- test_compute.cpp + |- test_pipeline_state.cpp + |- test_render_pass.cpp + \- test_shader.cpp ``` ## 2. 当前目标 @@ -29,6 +35,8 @@ tests/RHI/Vulkan/ - 基于 GLSL 的 graphics pipeline 创建 - UAV 视图创建与 compute dispatch 写纹理链路 +当前这些 Vulkan backend unit 用例已经按职责拆分为 fixture / render-pass / shader / pipeline / compute 几个文件,避免后续继续堆到单个巨型测试文件中。 + 这些测试允许直接依赖 Vulkan 具体类型、原生句柄和 Vulkan API;这类断言不应再回流到 `tests/RHI/unit/`。 ## 4. 分层约束 diff --git a/tests/RHI/Vulkan/unit/CMakeLists.txt b/tests/RHI/Vulkan/unit/CMakeLists.txt index a8964f96..856c6a25 100644 --- a/tests/RHI/Vulkan/unit/CMakeLists.txt +++ b/tests/RHI/Vulkan/unit/CMakeLists.txt @@ -10,7 +10,10 @@ endif() set(TEST_SOURCES fixtures/VulkanTestFixture.cpp - test_backend_specific.cpp + test_compute.cpp + test_pipeline_state.cpp + test_render_pass.cpp + test_shader.cpp ) add_executable(rhi_vulkan_tests ${TEST_SOURCES}) diff --git a/tests/RHI/Vulkan/unit/test_backend_specific.cpp b/tests/RHI/Vulkan/unit/test_backend_specific.cpp deleted file mode 100644 index 69476504..00000000 --- a/tests/RHI/Vulkan/unit/test_backend_specific.cpp +++ /dev/null @@ -1,426 +0,0 @@ -#if defined(XCENGINE_SUPPORT_VULKAN) - -#include - -#include - -#include -#include -#include -#include -#include - -#include "fixtures/VulkanTestFixture.h" -#include "XCEngine/RHI/RHIDescriptorPool.h" -#include "XCEngine/RHI/RHIDescriptorSet.h" -#include "XCEngine/RHI/RHIFramebuffer.h" -#include "XCEngine/RHI/RHIPipelineLayout.h" -#include "XCEngine/RHI/RHIPipelineState.h" -#include "XCEngine/RHI/RHIRenderPass.h" -#include "XCEngine/RHI/RHIResourceView.h" -#include "XCEngine/RHI/Vulkan/VulkanTexture.h" - -using namespace XCEngine::RHI; - -namespace { - -TEST_F(VulkanGraphicsFixture, RenderPassFramebufferBeginRenderPassClearWritesColor) { - AttachmentDesc colorAttachment = {}; - colorAttachment.format = Format::R8G8B8A8_UNorm; - colorAttachment.loadOp = LoadAction::Clear; - colorAttachment.storeOp = StoreAction::Store; - - AttachmentDesc depthAttachment = {}; - depthAttachment.format = Format::D24_UNorm_S8_UInt; - depthAttachment.loadOp = LoadAction::Clear; - depthAttachment.storeOp = StoreAction::Store; - depthAttachment.stencilLoadOp = LoadAction::Clear; - depthAttachment.stencilStoreOp = StoreAction::Store; - - RHIRenderPass* renderPass = m_device->CreateRenderPass(1, &colorAttachment, &depthAttachment); - ASSERT_NE(renderPass, nullptr); - - RHITexture* colorTexture = m_device->CreateTexture(CreateColorTextureDesc(64, 64)); - RHITexture* depthTexture = m_device->CreateTexture(CreateDepthTextureDesc(64, 64)); - ASSERT_NE(colorTexture, nullptr); - ASSERT_NE(depthTexture, nullptr); - - RHIResourceView* colorView = m_device->CreateRenderTargetView(colorTexture, {}); - RHIResourceView* depthView = m_device->CreateDepthStencilView(depthTexture, {}); - ASSERT_NE(colorView, nullptr); - ASSERT_NE(depthView, nullptr); - - RHIFramebuffer* framebuffer = m_device->CreateFramebuffer(renderPass, 64, 64, 1, &colorView, depthView); - ASSERT_NE(framebuffer, nullptr); - - RHICommandList* commandList = CreateCommandList(); - ASSERT_NE(commandList, nullptr); - - ClearValue clearValues[2] = {}; - clearValues[0].color = { 0.25f, 0.5f, 0.75f, 1.0f }; - clearValues[1].depth = 1.0f; - clearValues[1].stencil = 0; - - commandList->Reset(); - commandList->BeginRenderPass(renderPass, framebuffer, Rect{0, 0, 64, 64}, 2, clearValues); - commandList->EndRenderPass(); - SubmitAndWait(commandList); - - const std::vector pixels = ReadTextureRgba8(static_cast(colorTexture)); - ASSERT_GE(pixels.size(), 4u); - EXPECT_NEAR(static_cast(pixels[0]), 64, 1); - EXPECT_NEAR(static_cast(pixels[1]), 128, 1); - EXPECT_NEAR(static_cast(pixels[2]), 191, 1); - EXPECT_EQ(pixels[3], 255u); - - commandList->Shutdown(); - delete commandList; - framebuffer->Shutdown(); - delete framebuffer; - delete depthView; - delete colorView; - depthTexture->Shutdown(); - delete depthTexture; - colorTexture->Shutdown(); - delete colorTexture; - renderPass->Shutdown(); - delete renderPass; -} - -TEST_F(VulkanGraphicsFixture, CopyResourceCopiesTexturePixels) { - constexpr uint32_t kWidth = 32; - constexpr uint32_t kHeight = 16; - - std::vector sourcePixels(kWidth * kHeight * 4); - for (uint32_t y = 0; y < kHeight; ++y) { - for (uint32_t x = 0; x < kWidth; ++x) { - const size_t index = static_cast((y * kWidth + x) * 4); - sourcePixels[index + 0] = static_cast((x * 13) & 0xFF); - sourcePixels[index + 1] = static_cast((y * 29) & 0xFF); - sourcePixels[index + 2] = static_cast(((x + y) * 17) & 0xFF); - sourcePixels[index + 3] = 255; - } - } - - TextureDesc textureDesc = CreateColorTextureDesc(kWidth, kHeight); - RHITexture* sourceTexture = m_device->CreateTexture(textureDesc, sourcePixels.data(), sourcePixels.size(), kWidth * 4); - RHITexture* destinationTexture = m_device->CreateTexture(textureDesc); - ASSERT_NE(sourceTexture, nullptr); - ASSERT_NE(destinationTexture, nullptr); - - RHIResourceView* sourceView = m_device->CreateShaderResourceView(sourceTexture, {}); - RHIResourceView* destinationView = m_device->CreateShaderResourceView(destinationTexture, {}); - ASSERT_NE(sourceView, nullptr); - ASSERT_NE(destinationView, nullptr); - - RHICommandList* commandList = CreateCommandList(); - ASSERT_NE(commandList, nullptr); - - commandList->Reset(); - commandList->CopyResource(destinationView, sourceView); - SubmitAndWait(commandList); - - const std::vector copiedPixels = ReadTextureRgba8(static_cast(destinationTexture)); - EXPECT_EQ(copiedPixels, sourcePixels); - - commandList->Shutdown(); - delete commandList; - delete destinationView; - delete sourceView; - destinationTexture->Shutdown(); - delete destinationTexture; - sourceTexture->Shutdown(); - delete sourceTexture; -} - -TEST_F(VulkanGraphicsFixture, CreateShaderFromSpirvProducesValidComputeShader) { - RHIShader* shader = CreateWriteRedComputeShader(); - ASSERT_NE(shader, nullptr); - EXPECT_TRUE(shader->IsValid()); - EXPECT_EQ(shader->GetType(), ShaderType::Compute); - EXPECT_NE(shader->GetNativeHandle(), nullptr); - shader->Shutdown(); - delete shader; -} - -TEST_F(VulkanGraphicsFixture, CreateShaderFromGlslSourceProducesValidVertexShader) { - static const char* vertexSource = R"(#version 450 -layout(location = 0) in vec4 aPosition; -void main() { - gl_Position = aPosition; -} -)"; - - ShaderCompileDesc shaderDesc = {}; - shaderDesc.sourceLanguage = ShaderLanguage::GLSL; - shaderDesc.profile = L"vs"; - shaderDesc.source.assign(vertexSource, vertexSource + std::strlen(vertexSource)); - - RHIShader* shader = m_device->CreateShader(shaderDesc); - ASSERT_NE(shader, nullptr); - EXPECT_TRUE(shader->IsValid()); - EXPECT_EQ(shader->GetType(), ShaderType::Vertex); - EXPECT_NE(shader->GetNativeHandle(), nullptr); - shader->Shutdown(); - delete shader; -} - -TEST_F(VulkanGraphicsFixture, CreateShaderFromGlslFileProducesValidVertexShader) { - ShaderCompileDesc shaderDesc = {}; - shaderDesc.fileName = ResolveShaderPath(L"tests/RHI/integration/triangle/Res/Shader/triangle_vulkan.vert"); - shaderDesc.entryPoint = L"main"; - shaderDesc.profile = L"vs"; - - RHIShader* shader = m_device->CreateShader(shaderDesc); - ASSERT_NE(shader, nullptr); - EXPECT_TRUE(shader->IsValid()); - EXPECT_EQ(shader->GetType(), ShaderType::Vertex); - EXPECT_NE(shader->GetNativeHandle(), nullptr); - shader->Shutdown(); - delete shader; -} - -TEST_F(VulkanGraphicsFixture, CreateShaderFromGlslSourceInfersComputeShader) { - RHIShader* shader = CreateWriteRedComputeShaderFromGlsl(); - ASSERT_NE(shader, nullptr); - EXPECT_TRUE(shader->IsValid()); - EXPECT_EQ(shader->GetType(), ShaderType::Compute); - EXPECT_NE(shader->GetNativeHandle(), nullptr); - shader->Shutdown(); - delete shader; -} - -TEST_F(VulkanGraphicsFixture, CreateGraphicsPipelineFromGlslShadersProducesValidPipeline) { - static const char* vertexSource = R"(#version 450 -layout(location = 0) in vec4 aPosition; -void main() { - gl_Position = aPosition; -} -)"; - - static const char* fragmentSource = R"(#version 450 -layout(location = 0) out vec4 fragColor; -void main() { - fragColor = vec4(1.0, 0.0, 0.0, 1.0); -} -)"; - - GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc.topologyType = static_cast(PrimitiveTopologyType::Triangle); - pipelineDesc.renderTargetFormats[0] = static_cast(Format::R8G8B8A8_UNorm); - pipelineDesc.depthStencilFormat = static_cast(Format::Unknown); - - InputElementDesc position = {}; - position.semanticName = "POSITION"; - position.semanticIndex = 0; - position.format = static_cast(Format::R32G32B32A32_Float); - position.inputSlot = 0; - position.alignedByteOffset = 0; - pipelineDesc.inputLayout.elements.push_back(position); - - pipelineDesc.vertexShader.sourceLanguage = ShaderLanguage::GLSL; - pipelineDesc.vertexShader.profile = L"vs_4_50"; - pipelineDesc.vertexShader.source.assign(vertexSource, vertexSource + std::strlen(vertexSource)); - - pipelineDesc.fragmentShader.sourceLanguage = ShaderLanguage::GLSL; - pipelineDesc.fragmentShader.profile = L"fs_4_50"; - pipelineDesc.fragmentShader.source.assign(fragmentSource, fragmentSource + std::strlen(fragmentSource)); - - RHIPipelineState* pipelineState = m_device->CreatePipelineState(pipelineDesc); - ASSERT_NE(pipelineState, nullptr); - EXPECT_TRUE(pipelineState->IsValid()); - EXPECT_NE(pipelineState->GetNativeHandle(), nullptr); - pipelineState->Shutdown(); - delete pipelineState; -} - -TEST_F(VulkanGraphicsFixture, CreateUnorderedAccessViewProducesValidView) { - RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4)); - ASSERT_NE(texture, nullptr); - - ResourceViewDesc viewDesc = {}; - viewDesc.format = static_cast(Format::R8G8B8A8_UNorm); - viewDesc.dimension = ResourceViewDimension::Texture2D; - - RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, viewDesc); - ASSERT_NE(uav, nullptr); - EXPECT_TRUE(uav->IsValid()); - EXPECT_EQ(uav->GetViewType(), ResourceViewType::UnorderedAccess); - - uav->Shutdown(); - delete uav; - texture->Shutdown(); - delete texture; -} - -TEST_F(VulkanGraphicsFixture, DispatchWritesUavTexture) { - RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4)); - ASSERT_NE(texture, nullptr); - - ResourceViewDesc uavDesc = {}; - uavDesc.format = static_cast(Format::R8G8B8A8_UNorm); - uavDesc.dimension = ResourceViewDimension::Texture2D; - RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, uavDesc); - ASSERT_NE(uav, nullptr); - - 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 uavBinding = {}; - uavBinding.binding = 0; - uavBinding.type = static_cast(DescriptorType::UAV); - uavBinding.count = 1; - uavBinding.visibility = static_cast(ShaderVisibility::All); - - DescriptorSetLayoutDesc setLayout = {}; - setLayout.bindings = &uavBinding; - setLayout.bindingCount = 1; - - RHIPipelineLayoutDesc pipelineLayoutDesc = {}; - pipelineLayoutDesc.setLayouts = &setLayout; - pipelineLayoutDesc.setLayoutCount = 1; - RHIPipelineLayout* pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); - ASSERT_NE(pipelineLayout, nullptr); - - RHIDescriptorSet* descriptorSet = pool->AllocateSet(setLayout); - ASSERT_NE(descriptorSet, nullptr); - descriptorSet->Update(0, uav); - - GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc.pipelineLayout = pipelineLayout; - RHIPipelineState* pipelineState = m_device->CreatePipelineState(pipelineDesc); - ASSERT_NE(pipelineState, nullptr); - - RHIShader* shader = CreateWriteRedComputeShader(); - ASSERT_NE(shader, nullptr); - pipelineState->SetComputeShader(shader); - EXPECT_TRUE(pipelineState->HasComputeShader()); - EXPECT_EQ(pipelineState->GetType(), PipelineType::Compute); - - RHICommandList* commandList = CreateCommandList(); - ASSERT_NE(commandList, nullptr); - - commandList->Reset(); - commandList->TransitionBarrier(uav, ResourceStates::Common, ResourceStates::UnorderedAccess); - commandList->SetPipelineState(pipelineState); - RHIDescriptorSet* descriptorSets[] = { descriptorSet }; - commandList->SetComputeDescriptorSets(0, 1, descriptorSets, pipelineLayout); - commandList->Dispatch(1, 1, 1); - SubmitAndWait(commandList); - - const std::vector pixels = ReadTextureRgba8(static_cast(texture)); - ASSERT_GE(pixels.size(), 4u); - EXPECT_EQ(pixels[0], 255u); - EXPECT_EQ(pixels[1], 0u); - EXPECT_EQ(pixels[2], 0u); - EXPECT_EQ(pixels[3], 255u); - - commandList->Shutdown(); - delete commandList; - shader->Shutdown(); - delete shader; - pipelineState->Shutdown(); - delete pipelineState; - descriptorSet->Shutdown(); - delete descriptorSet; - pipelineLayout->Shutdown(); - delete pipelineLayout; - pool->Shutdown(); - delete pool; - uav->Shutdown(); - delete uav; - texture->Shutdown(); - delete texture; -} - -TEST_F(VulkanGraphicsFixture, DispatchWritesUavTextureWithGlslComputeShader) { - RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4)); - ASSERT_NE(texture, nullptr); - - ResourceViewDesc uavDesc = {}; - uavDesc.format = static_cast(Format::R8G8B8A8_UNorm); - uavDesc.dimension = ResourceViewDimension::Texture2D; - RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, uavDesc); - ASSERT_NE(uav, nullptr); - - 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 uavBinding = {}; - uavBinding.binding = 0; - uavBinding.type = static_cast(DescriptorType::UAV); - uavBinding.count = 1; - uavBinding.visibility = static_cast(ShaderVisibility::All); - - DescriptorSetLayoutDesc setLayout = {}; - setLayout.bindings = &uavBinding; - setLayout.bindingCount = 1; - - RHIPipelineLayoutDesc pipelineLayoutDesc = {}; - pipelineLayoutDesc.setLayouts = &setLayout; - pipelineLayoutDesc.setLayoutCount = 1; - RHIPipelineLayout* pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); - ASSERT_NE(pipelineLayout, nullptr); - - RHIDescriptorSet* descriptorSet = pool->AllocateSet(setLayout); - ASSERT_NE(descriptorSet, nullptr); - descriptorSet->Update(0, uav); - - GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc.pipelineLayout = pipelineLayout; - RHIPipelineState* pipelineState = m_device->CreatePipelineState(pipelineDesc); - ASSERT_NE(pipelineState, nullptr); - - RHIShader* shader = CreateWriteRedComputeShaderFromGlsl(); - ASSERT_NE(shader, nullptr); - pipelineState->SetComputeShader(shader); - EXPECT_TRUE(pipelineState->HasComputeShader()); - EXPECT_EQ(pipelineState->GetType(), PipelineType::Compute); - - RHICommandList* commandList = CreateCommandList(); - ASSERT_NE(commandList, nullptr); - - commandList->Reset(); - commandList->TransitionBarrier(uav, ResourceStates::Common, ResourceStates::UnorderedAccess); - commandList->SetPipelineState(pipelineState); - RHIDescriptorSet* descriptorSets[] = { descriptorSet }; - commandList->SetComputeDescriptorSets(0, 1, descriptorSets, pipelineLayout); - commandList->Dispatch(1, 1, 1); - SubmitAndWait(commandList); - - const std::vector pixels = ReadTextureRgba8(static_cast(texture)); - ASSERT_GE(pixels.size(), 4u); - EXPECT_EQ(pixels[0], 255u); - EXPECT_EQ(pixels[1], 0u); - EXPECT_EQ(pixels[2], 0u); - EXPECT_EQ(pixels[3], 255u); - - commandList->Shutdown(); - delete commandList; - shader->Shutdown(); - delete shader; - pipelineState->Shutdown(); - delete pipelineState; - descriptorSet->Shutdown(); - delete descriptorSet; - pipelineLayout->Shutdown(); - delete pipelineLayout; - pool->Shutdown(); - delete pool; - uav->Shutdown(); - delete uav; - texture->Shutdown(); - delete texture; -} - -} // namespace - -#endif diff --git a/tests/RHI/Vulkan/unit/test_compute.cpp b/tests/RHI/Vulkan/unit/test_compute.cpp new file mode 100644 index 00000000..e06a2786 --- /dev/null +++ b/tests/RHI/Vulkan/unit/test_compute.cpp @@ -0,0 +1,206 @@ +#if defined(XCENGINE_SUPPORT_VULKAN) + +#include "fixtures/VulkanTestFixture.h" +#include "XCEngine/RHI/RHIDescriptorPool.h" +#include "XCEngine/RHI/RHIDescriptorSet.h" +#include "XCEngine/RHI/RHIPipelineLayout.h" +#include "XCEngine/RHI/RHIPipelineState.h" +#include "XCEngine/RHI/RHIResourceView.h" +#include "XCEngine/RHI/Vulkan/VulkanTexture.h" + +#include + +using namespace XCEngine::RHI; + +namespace { + +TEST_F(VulkanGraphicsFixture, CreateUnorderedAccessViewProducesValidView) { + RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4)); + ASSERT_NE(texture, nullptr); + + ResourceViewDesc viewDesc = {}; + viewDesc.format = static_cast(Format::R8G8B8A8_UNorm); + viewDesc.dimension = ResourceViewDimension::Texture2D; + + RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, viewDesc); + ASSERT_NE(uav, nullptr); + EXPECT_TRUE(uav->IsValid()); + EXPECT_EQ(uav->GetViewType(), ResourceViewType::UnorderedAccess); + + uav->Shutdown(); + delete uav; + texture->Shutdown(); + delete texture; +} + +TEST_F(VulkanGraphicsFixture, DispatchWritesUavTexture) { + RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4)); + ASSERT_NE(texture, nullptr); + + ResourceViewDesc uavDesc = {}; + uavDesc.format = static_cast(Format::R8G8B8A8_UNorm); + uavDesc.dimension = ResourceViewDimension::Texture2D; + RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, uavDesc); + ASSERT_NE(uav, nullptr); + + 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 uavBinding = {}; + uavBinding.binding = 0; + uavBinding.type = static_cast(DescriptorType::UAV); + uavBinding.count = 1; + uavBinding.visibility = static_cast(ShaderVisibility::All); + + DescriptorSetLayoutDesc setLayout = {}; + setLayout.bindings = &uavBinding; + setLayout.bindingCount = 1; + + RHIPipelineLayoutDesc pipelineLayoutDesc = {}; + pipelineLayoutDesc.setLayouts = &setLayout; + pipelineLayoutDesc.setLayoutCount = 1; + RHIPipelineLayout* pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); + ASSERT_NE(pipelineLayout, nullptr); + + RHIDescriptorSet* descriptorSet = pool->AllocateSet(setLayout); + ASSERT_NE(descriptorSet, nullptr); + descriptorSet->Update(0, uav); + + GraphicsPipelineDesc pipelineDesc = {}; + pipelineDesc.pipelineLayout = pipelineLayout; + RHIPipelineState* pipelineState = m_device->CreatePipelineState(pipelineDesc); + ASSERT_NE(pipelineState, nullptr); + + RHIShader* shader = CreateWriteRedComputeShader(); + ASSERT_NE(shader, nullptr); + pipelineState->SetComputeShader(shader); + EXPECT_TRUE(pipelineState->HasComputeShader()); + EXPECT_EQ(pipelineState->GetType(), PipelineType::Compute); + + RHICommandList* commandList = CreateCommandList(); + ASSERT_NE(commandList, nullptr); + + commandList->Reset(); + commandList->TransitionBarrier(uav, ResourceStates::Common, ResourceStates::UnorderedAccess); + commandList->SetPipelineState(pipelineState); + RHIDescriptorSet* descriptorSets[] = { descriptorSet }; + commandList->SetComputeDescriptorSets(0, 1, descriptorSets, pipelineLayout); + commandList->Dispatch(1, 1, 1); + SubmitAndWait(commandList); + + const std::vector pixels = ReadTextureRgba8(static_cast(texture)); + ASSERT_GE(pixels.size(), 4u); + EXPECT_EQ(pixels[0], 255u); + EXPECT_EQ(pixels[1], 0u); + EXPECT_EQ(pixels[2], 0u); + EXPECT_EQ(pixels[3], 255u); + + commandList->Shutdown(); + delete commandList; + shader->Shutdown(); + delete shader; + pipelineState->Shutdown(); + delete pipelineState; + descriptorSet->Shutdown(); + delete descriptorSet; + pipelineLayout->Shutdown(); + delete pipelineLayout; + pool->Shutdown(); + delete pool; + uav->Shutdown(); + delete uav; + texture->Shutdown(); + delete texture; +} + +TEST_F(VulkanGraphicsFixture, DispatchWritesUavTextureWithGlslComputeShader) { + RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4)); + ASSERT_NE(texture, nullptr); + + ResourceViewDesc uavDesc = {}; + uavDesc.format = static_cast(Format::R8G8B8A8_UNorm); + uavDesc.dimension = ResourceViewDimension::Texture2D; + RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, uavDesc); + ASSERT_NE(uav, nullptr); + + 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 uavBinding = {}; + uavBinding.binding = 0; + uavBinding.type = static_cast(DescriptorType::UAV); + uavBinding.count = 1; + uavBinding.visibility = static_cast(ShaderVisibility::All); + + DescriptorSetLayoutDesc setLayout = {}; + setLayout.bindings = &uavBinding; + setLayout.bindingCount = 1; + + RHIPipelineLayoutDesc pipelineLayoutDesc = {}; + pipelineLayoutDesc.setLayouts = &setLayout; + pipelineLayoutDesc.setLayoutCount = 1; + RHIPipelineLayout* pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); + ASSERT_NE(pipelineLayout, nullptr); + + RHIDescriptorSet* descriptorSet = pool->AllocateSet(setLayout); + ASSERT_NE(descriptorSet, nullptr); + descriptorSet->Update(0, uav); + + GraphicsPipelineDesc pipelineDesc = {}; + pipelineDesc.pipelineLayout = pipelineLayout; + RHIPipelineState* pipelineState = m_device->CreatePipelineState(pipelineDesc); + ASSERT_NE(pipelineState, nullptr); + + RHIShader* shader = CreateWriteRedComputeShaderFromGlsl(); + ASSERT_NE(shader, nullptr); + pipelineState->SetComputeShader(shader); + EXPECT_TRUE(pipelineState->HasComputeShader()); + EXPECT_EQ(pipelineState->GetType(), PipelineType::Compute); + + RHICommandList* commandList = CreateCommandList(); + ASSERT_NE(commandList, nullptr); + + commandList->Reset(); + commandList->TransitionBarrier(uav, ResourceStates::Common, ResourceStates::UnorderedAccess); + commandList->SetPipelineState(pipelineState); + RHIDescriptorSet* descriptorSets[] = { descriptorSet }; + commandList->SetComputeDescriptorSets(0, 1, descriptorSets, pipelineLayout); + commandList->Dispatch(1, 1, 1); + SubmitAndWait(commandList); + + const std::vector pixels = ReadTextureRgba8(static_cast(texture)); + ASSERT_GE(pixels.size(), 4u); + EXPECT_EQ(pixels[0], 255u); + EXPECT_EQ(pixels[1], 0u); + EXPECT_EQ(pixels[2], 0u); + EXPECT_EQ(pixels[3], 255u); + + commandList->Shutdown(); + delete commandList; + shader->Shutdown(); + delete shader; + pipelineState->Shutdown(); + delete pipelineState; + descriptorSet->Shutdown(); + delete descriptorSet; + pipelineLayout->Shutdown(); + delete pipelineLayout; + pool->Shutdown(); + delete pool; + uav->Shutdown(); + delete uav; + texture->Shutdown(); + delete texture; +} + +} // namespace + +#endif diff --git a/tests/RHI/Vulkan/unit/test_pipeline_state.cpp b/tests/RHI/Vulkan/unit/test_pipeline_state.cpp new file mode 100644 index 00000000..c8962c6e --- /dev/null +++ b/tests/RHI/Vulkan/unit/test_pipeline_state.cpp @@ -0,0 +1,58 @@ +#if defined(XCENGINE_SUPPORT_VULKAN) + +#include "fixtures/VulkanTestFixture.h" +#include "XCEngine/RHI/RHIPipelineState.h" + +#include + +using namespace XCEngine::RHI; + +namespace { + +TEST_F(VulkanGraphicsFixture, CreateGraphicsPipelineFromGlslShadersProducesValidPipeline) { + static const char* vertexSource = R"(#version 450 +layout(location = 0) in vec4 aPosition; +void main() { + gl_Position = aPosition; +} +)"; + + static const char* fragmentSource = R"(#version 450 +layout(location = 0) out vec4 fragColor; +void main() { + fragColor = vec4(1.0, 0.0, 0.0, 1.0); +} +)"; + + GraphicsPipelineDesc pipelineDesc = {}; + pipelineDesc.topologyType = static_cast(PrimitiveTopologyType::Triangle); + pipelineDesc.renderTargetFormats[0] = static_cast(Format::R8G8B8A8_UNorm); + pipelineDesc.depthStencilFormat = static_cast(Format::Unknown); + + InputElementDesc position = {}; + position.semanticName = "POSITION"; + position.semanticIndex = 0; + position.format = static_cast(Format::R32G32B32A32_Float); + position.inputSlot = 0; + position.alignedByteOffset = 0; + pipelineDesc.inputLayout.elements.push_back(position); + + pipelineDesc.vertexShader.sourceLanguage = ShaderLanguage::GLSL; + pipelineDesc.vertexShader.profile = L"vs_4_50"; + pipelineDesc.vertexShader.source.assign(vertexSource, vertexSource + std::strlen(vertexSource)); + + pipelineDesc.fragmentShader.sourceLanguage = ShaderLanguage::GLSL; + pipelineDesc.fragmentShader.profile = L"fs_4_50"; + pipelineDesc.fragmentShader.source.assign(fragmentSource, fragmentSource + std::strlen(fragmentSource)); + + RHIPipelineState* pipelineState = m_device->CreatePipelineState(pipelineDesc); + ASSERT_NE(pipelineState, nullptr); + EXPECT_TRUE(pipelineState->IsValid()); + EXPECT_NE(pipelineState->GetNativeHandle(), nullptr); + pipelineState->Shutdown(); + delete pipelineState; +} + +} // namespace + +#endif diff --git a/tests/RHI/Vulkan/unit/test_render_pass.cpp b/tests/RHI/Vulkan/unit/test_render_pass.cpp new file mode 100644 index 00000000..ec1782b6 --- /dev/null +++ b/tests/RHI/Vulkan/unit/test_render_pass.cpp @@ -0,0 +1,127 @@ +#if defined(XCENGINE_SUPPORT_VULKAN) + +#include "fixtures/VulkanTestFixture.h" +#include "XCEngine/RHI/RHIFramebuffer.h" +#include "XCEngine/RHI/RHIRenderPass.h" +#include "XCEngine/RHI/RHIResourceView.h" +#include "XCEngine/RHI/Vulkan/VulkanTexture.h" + +#include +#include + +using namespace XCEngine::RHI; + +namespace { + +TEST_F(VulkanGraphicsFixture, RenderPassFramebufferBeginRenderPassClearWritesColor) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + colorAttachment.loadOp = LoadAction::Clear; + colorAttachment.storeOp = StoreAction::Store; + + AttachmentDesc depthAttachment = {}; + depthAttachment.format = Format::D24_UNorm_S8_UInt; + depthAttachment.loadOp = LoadAction::Clear; + depthAttachment.storeOp = StoreAction::Store; + depthAttachment.stencilLoadOp = LoadAction::Clear; + depthAttachment.stencilStoreOp = StoreAction::Store; + + RHIRenderPass* renderPass = m_device->CreateRenderPass(1, &colorAttachment, &depthAttachment); + ASSERT_NE(renderPass, nullptr); + + RHITexture* colorTexture = m_device->CreateTexture(CreateColorTextureDesc(64, 64)); + RHITexture* depthTexture = m_device->CreateTexture(CreateDepthTextureDesc(64, 64)); + ASSERT_NE(colorTexture, nullptr); + ASSERT_NE(depthTexture, nullptr); + + RHIResourceView* colorView = m_device->CreateRenderTargetView(colorTexture, {}); + RHIResourceView* depthView = m_device->CreateDepthStencilView(depthTexture, {}); + ASSERT_NE(colorView, nullptr); + ASSERT_NE(depthView, nullptr); + + RHIFramebuffer* framebuffer = m_device->CreateFramebuffer(renderPass, 64, 64, 1, &colorView, depthView); + ASSERT_NE(framebuffer, nullptr); + + RHICommandList* commandList = CreateCommandList(); + ASSERT_NE(commandList, nullptr); + + ClearValue clearValues[2] = {}; + clearValues[0].color = { 0.25f, 0.5f, 0.75f, 1.0f }; + clearValues[1].depth = 1.0f; + clearValues[1].stencil = 0; + + commandList->Reset(); + commandList->BeginRenderPass(renderPass, framebuffer, Rect{0, 0, 64, 64}, 2, clearValues); + commandList->EndRenderPass(); + SubmitAndWait(commandList); + + const std::vector pixels = ReadTextureRgba8(static_cast(colorTexture)); + ASSERT_GE(pixels.size(), 4u); + EXPECT_NEAR(static_cast(pixels[0]), 64, 1); + EXPECT_NEAR(static_cast(pixels[1]), 128, 1); + EXPECT_NEAR(static_cast(pixels[2]), 191, 1); + EXPECT_EQ(pixels[3], 255u); + + commandList->Shutdown(); + delete commandList; + framebuffer->Shutdown(); + delete framebuffer; + delete depthView; + delete colorView; + depthTexture->Shutdown(); + delete depthTexture; + colorTexture->Shutdown(); + delete colorTexture; + renderPass->Shutdown(); + delete renderPass; +} + +TEST_F(VulkanGraphicsFixture, CopyResourceCopiesTexturePixels) { + constexpr uint32_t kWidth = 32; + constexpr uint32_t kHeight = 16; + + std::vector sourcePixels(kWidth * kHeight * 4); + for (uint32_t y = 0; y < kHeight; ++y) { + for (uint32_t x = 0; x < kWidth; ++x) { + const size_t index = static_cast((y * kWidth + x) * 4); + sourcePixels[index + 0] = static_cast((x * 13) & 0xFF); + sourcePixels[index + 1] = static_cast((y * 29) & 0xFF); + sourcePixels[index + 2] = static_cast(((x + y) * 17) & 0xFF); + sourcePixels[index + 3] = 255; + } + } + + TextureDesc textureDesc = CreateColorTextureDesc(kWidth, kHeight); + RHITexture* sourceTexture = m_device->CreateTexture(textureDesc, sourcePixels.data(), sourcePixels.size(), kWidth * 4); + RHITexture* destinationTexture = m_device->CreateTexture(textureDesc); + ASSERT_NE(sourceTexture, nullptr); + ASSERT_NE(destinationTexture, nullptr); + + RHIResourceView* sourceView = m_device->CreateShaderResourceView(sourceTexture, {}); + RHIResourceView* destinationView = m_device->CreateShaderResourceView(destinationTexture, {}); + ASSERT_NE(sourceView, nullptr); + ASSERT_NE(destinationView, nullptr); + + RHICommandList* commandList = CreateCommandList(); + ASSERT_NE(commandList, nullptr); + + commandList->Reset(); + commandList->CopyResource(destinationView, sourceView); + SubmitAndWait(commandList); + + const std::vector copiedPixels = ReadTextureRgba8(static_cast(destinationTexture)); + EXPECT_EQ(copiedPixels, sourcePixels); + + commandList->Shutdown(); + delete commandList; + delete destinationView; + delete sourceView; + destinationTexture->Shutdown(); + delete destinationTexture; + sourceTexture->Shutdown(); + delete sourceTexture; +} + +} // namespace + +#endif diff --git a/tests/RHI/Vulkan/unit/test_shader.cpp b/tests/RHI/Vulkan/unit/test_shader.cpp new file mode 100644 index 00000000..397091cf --- /dev/null +++ b/tests/RHI/Vulkan/unit/test_shader.cpp @@ -0,0 +1,70 @@ +#if defined(XCENGINE_SUPPORT_VULKAN) + +#include "fixtures/VulkanTestFixture.h" + +#include + +using namespace XCEngine::RHI; + +namespace { + +TEST_F(VulkanGraphicsFixture, CreateShaderFromSpirvProducesValidComputeShader) { + RHIShader* shader = CreateWriteRedComputeShader(); + ASSERT_NE(shader, nullptr); + EXPECT_TRUE(shader->IsValid()); + EXPECT_EQ(shader->GetType(), ShaderType::Compute); + EXPECT_NE(shader->GetNativeHandle(), nullptr); + shader->Shutdown(); + delete shader; +} + +TEST_F(VulkanGraphicsFixture, CreateShaderFromGlslSourceProducesValidVertexShader) { + static const char* vertexSource = R"(#version 450 +layout(location = 0) in vec4 aPosition; +void main() { + gl_Position = aPosition; +} +)"; + + ShaderCompileDesc shaderDesc = {}; + shaderDesc.sourceLanguage = ShaderLanguage::GLSL; + shaderDesc.profile = L"vs"; + shaderDesc.source.assign(vertexSource, vertexSource + std::strlen(vertexSource)); + + RHIShader* shader = m_device->CreateShader(shaderDesc); + ASSERT_NE(shader, nullptr); + EXPECT_TRUE(shader->IsValid()); + EXPECT_EQ(shader->GetType(), ShaderType::Vertex); + EXPECT_NE(shader->GetNativeHandle(), nullptr); + shader->Shutdown(); + delete shader; +} + +TEST_F(VulkanGraphicsFixture, CreateShaderFromGlslFileProducesValidVertexShader) { + ShaderCompileDesc shaderDesc = {}; + shaderDesc.fileName = ResolveShaderPath(L"tests/RHI/integration/triangle/Res/Shader/triangle_vulkan.vert"); + shaderDesc.entryPoint = L"main"; + shaderDesc.profile = L"vs"; + + RHIShader* shader = m_device->CreateShader(shaderDesc); + ASSERT_NE(shader, nullptr); + EXPECT_TRUE(shader->IsValid()); + EXPECT_EQ(shader->GetType(), ShaderType::Vertex); + EXPECT_NE(shader->GetNativeHandle(), nullptr); + shader->Shutdown(); + delete shader; +} + +TEST_F(VulkanGraphicsFixture, CreateShaderFromGlslSourceInfersComputeShader) { + RHIShader* shader = CreateWriteRedComputeShaderFromGlsl(); + ASSERT_NE(shader, nullptr); + EXPECT_TRUE(shader->IsValid()); + EXPECT_EQ(shader->GetType(), ShaderType::Compute); + EXPECT_NE(shader->GetNativeHandle(), nullptr); + shader->Shutdown(); + delete shader; +} + +} // namespace + +#endif