Split Vulkan backend tests by responsibility
This commit is contained in:
@@ -10,7 +10,13 @@ tests/RHI/Vulkan/
|
|||||||
|- TEST_SPEC.md
|
|- TEST_SPEC.md
|
||||||
\- unit/
|
\- unit/
|
||||||
|- CMakeLists.txt
|
|- 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. 当前目标
|
## 2. 当前目标
|
||||||
@@ -29,6 +35,8 @@ tests/RHI/Vulkan/
|
|||||||
- 基于 GLSL 的 graphics pipeline 创建
|
- 基于 GLSL 的 graphics pipeline 创建
|
||||||
- UAV 视图创建与 compute dispatch 写纹理链路
|
- UAV 视图创建与 compute dispatch 写纹理链路
|
||||||
|
|
||||||
|
当前这些 Vulkan backend unit 用例已经按职责拆分为 fixture / render-pass / shader / pipeline / compute 几个文件,避免后续继续堆到单个巨型测试文件中。
|
||||||
|
|
||||||
这些测试允许直接依赖 Vulkan 具体类型、原生句柄和 Vulkan API;这类断言不应再回流到 `tests/RHI/unit/`。
|
这些测试允许直接依赖 Vulkan 具体类型、原生句柄和 Vulkan API;这类断言不应再回流到 `tests/RHI/unit/`。
|
||||||
|
|
||||||
## 4. 分层约束
|
## 4. 分层约束
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ endif()
|
|||||||
|
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
fixtures/VulkanTestFixture.cpp
|
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})
|
add_executable(rhi_vulkan_tests ${TEST_SOURCES})
|
||||||
|
|||||||
@@ -1,426 +0,0 @@
|
|||||||
#if defined(XCENGINE_SUPPORT_VULKAN)
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#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<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(colorTexture));
|
|
||||||
ASSERT_GE(pixels.size(), 4u);
|
|
||||||
EXPECT_NEAR(static_cast<int>(pixels[0]), 64, 1);
|
|
||||||
EXPECT_NEAR(static_cast<int>(pixels[1]), 128, 1);
|
|
||||||
EXPECT_NEAR(static_cast<int>(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<uint8_t> 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<size_t>((y * kWidth + x) * 4);
|
|
||||||
sourcePixels[index + 0] = static_cast<uint8_t>((x * 13) & 0xFF);
|
|
||||||
sourcePixels[index + 1] = static_cast<uint8_t>((y * 29) & 0xFF);
|
|
||||||
sourcePixels[index + 2] = static_cast<uint8_t>(((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<uint8_t> copiedPixels = ReadTextureRgba8(static_cast<VulkanTexture*>(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<uint32_t>(PrimitiveTopologyType::Triangle);
|
|
||||||
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
|
||||||
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(Format::Unknown);
|
|
||||||
|
|
||||||
InputElementDesc position = {};
|
|
||||||
position.semanticName = "POSITION";
|
|
||||||
position.semanticIndex = 0;
|
|
||||||
position.format = static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(DescriptorType::UAV);
|
|
||||||
uavBinding.count = 1;
|
|
||||||
uavBinding.visibility = static_cast<uint32_t>(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<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(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<uint32_t>(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<uint32_t>(DescriptorType::UAV);
|
|
||||||
uavBinding.count = 1;
|
|
||||||
uavBinding.visibility = static_cast<uint32_t>(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<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(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
|
|
||||||
206
tests/RHI/Vulkan/unit/test_compute.cpp
Normal file
206
tests/RHI/Vulkan/unit/test_compute.cpp
Normal file
@@ -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 <vector>
|
||||||
|
|
||||||
|
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<uint32_t>(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<uint32_t>(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<uint32_t>(DescriptorType::UAV);
|
||||||
|
uavBinding.count = 1;
|
||||||
|
uavBinding.visibility = static_cast<uint32_t>(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<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(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<uint32_t>(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<uint32_t>(DescriptorType::UAV);
|
||||||
|
uavBinding.count = 1;
|
||||||
|
uavBinding.visibility = static_cast<uint32_t>(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<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(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
|
||||||
58
tests/RHI/Vulkan/unit/test_pipeline_state.cpp
Normal file
58
tests/RHI/Vulkan/unit/test_pipeline_state.cpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#if defined(XCENGINE_SUPPORT_VULKAN)
|
||||||
|
|
||||||
|
#include "fixtures/VulkanTestFixture.h"
|
||||||
|
#include "XCEngine/RHI/RHIPipelineState.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
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<uint32_t>(PrimitiveTopologyType::Triangle);
|
||||||
|
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(Format::Unknown);
|
||||||
|
|
||||||
|
InputElementDesc position = {};
|
||||||
|
position.semanticName = "POSITION";
|
||||||
|
position.semanticIndex = 0;
|
||||||
|
position.format = static_cast<uint32_t>(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
|
||||||
127
tests/RHI/Vulkan/unit/test_render_pass.cpp
Normal file
127
tests/RHI/Vulkan/unit/test_render_pass.cpp
Normal file
@@ -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 <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(colorTexture));
|
||||||
|
ASSERT_GE(pixels.size(), 4u);
|
||||||
|
EXPECT_NEAR(static_cast<int>(pixels[0]), 64, 1);
|
||||||
|
EXPECT_NEAR(static_cast<int>(pixels[1]), 128, 1);
|
||||||
|
EXPECT_NEAR(static_cast<int>(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<uint8_t> 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<size_t>((y * kWidth + x) * 4);
|
||||||
|
sourcePixels[index + 0] = static_cast<uint8_t>((x * 13) & 0xFF);
|
||||||
|
sourcePixels[index + 1] = static_cast<uint8_t>((y * 29) & 0xFF);
|
||||||
|
sourcePixels[index + 2] = static_cast<uint8_t>(((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<uint8_t> copiedPixels = ReadTextureRgba8(static_cast<VulkanTexture*>(destinationTexture));
|
||||||
|
EXPECT_EQ(copiedPixels, sourcePixels);
|
||||||
|
|
||||||
|
commandList->Shutdown();
|
||||||
|
delete commandList;
|
||||||
|
delete destinationView;
|
||||||
|
delete sourceView;
|
||||||
|
destinationTexture->Shutdown();
|
||||||
|
delete destinationTexture;
|
||||||
|
sourceTexture->Shutdown();
|
||||||
|
delete sourceTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
70
tests/RHI/Vulkan/unit/test_shader.cpp
Normal file
70
tests/RHI/Vulkan/unit/test_shader.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#if defined(XCENGINE_SUPPORT_VULKAN)
|
||||||
|
|
||||||
|
#include "fixtures/VulkanTestFixture.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user