Add Vulkan GLSL shader compilation path
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
#if defined(XCENGINE_SUPPORT_VULKAN)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "XCEngine/RHI/RHICommandList.h"
|
||||
@@ -28,6 +31,17 @@ using namespace XCEngine::RHI;
|
||||
|
||||
namespace {
|
||||
|
||||
std::wstring ResolveShaderPath(const wchar_t* relativePath) {
|
||||
wchar_t exePath[MAX_PATH] = {};
|
||||
const DWORD length = GetModuleFileNameW(nullptr, exePath, MAX_PATH);
|
||||
std::filesystem::path rootPath =
|
||||
length > 0 ? std::filesystem::path(exePath).parent_path() : std::filesystem::current_path();
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
rootPath = rootPath.parent_path();
|
||||
}
|
||||
return (rootPath / relativePath).wstring();
|
||||
}
|
||||
|
||||
VkImageLayout ToImageLayout(ResourceStates state) {
|
||||
switch (state) {
|
||||
case ResourceStates::RenderTarget:
|
||||
@@ -206,6 +220,21 @@ protected:
|
||||
return m_device->CreateShader(shaderDesc);
|
||||
}
|
||||
|
||||
RHIShader* CreateWriteRedComputeShaderFromGlsl() const {
|
||||
static const char* computeSource = R"(#version 450
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
layout(set = 0, binding = 0, rgba8) uniform writeonly image2D uImage;
|
||||
void main() {
|
||||
imageStore(uImage, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
)";
|
||||
|
||||
ShaderCompileDesc shaderDesc = {};
|
||||
shaderDesc.sourceLanguage = ShaderLanguage::GLSL;
|
||||
shaderDesc.source.assign(computeSource, computeSource + std::strlen(computeSource));
|
||||
return m_device->CreateShader(shaderDesc);
|
||||
}
|
||||
|
||||
void SubmitAndWait(RHICommandList* commandList) {
|
||||
ASSERT_NE(commandList, nullptr);
|
||||
commandList->Close();
|
||||
@@ -464,6 +493,97 @@ TEST_F(VulkanGraphicsFixture, CreateShaderFromSpirvProducesValidComputeShader) {
|
||||
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_4_50";
|
||||
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_4_50";
|
||||
|
||||
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);
|
||||
@@ -567,6 +687,90 @@ TEST_F(VulkanGraphicsFixture, DispatchWritesUavTexture) {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user