在我的上一篇文章关于直接通过Visual Studio编译HLSL文件的更新中,我最终放弃并决定使用离线编译方法。我能够将两个 HLSL 着色器编译为
.spv
文件,但现在我收到以下验证错误:
Validation Error: [ VUID-VkShaderModuleCreateInfo-pCode-01379]
Object 0: handle = 0x1c63c99aa70, type = VK_OBJECT_TYPE_DEVICE;
| MessageID = 0x2a1bf17f |
SPIR-V module not valid: Invalid SPIR-V magic number. The Vulkan spec states:
If pCode is a pointer to GLSL code, it must be valid GLSL code written to the GL_KHR_vulkan_glsl GLSL extension specification
(https://vulkan.lunarg.com/doc/view/1.3.250.1/windows/1.3-extensions/vkspec.html#VUID-VkShaderModuleCreateInfo-pCode-01379)
不幸的是,
.spv
文件是从HLSL代码编译的,所以我不确定这里可能出现什么问题。
这些是我在编译
.hlsl
文件时使用的命令:
dxc.exe -spirv -T vs_6_4 -E main .\vertex_vert.hlsl -Fo .\vertex_vert.spv
dxc.exe -spirv -T ps_6_4 -E main .\pixel_frag.hlsl -Fo .\pixel_frag.spv
这是顶点着色器的内容:
struct VSInput
{
[[vk::location(0)]] float4 Position : POSITION0;
[[vk::location(1)]] float4 Color : COLOR0;
[[vk::location(2)]] float2 UVcoord : TEXCOORD0;
};
struct VSOutput
{
float4 Position : SV_POSITION;
[[vk::location(0)]] float4 Color : COLOR0;
[[vk::location(1)]] float2 UVcoord : TEXCOORD0;
};
struct Camera
{
float4x4 view;
float4x4 proj;
float3 position;
};
struct UBO
{
float dt;
float4x4 model;
Camera cam;
};
/* neither of the below options changed the outcome */
//cbuffer ubo : register(b0, space0);
//{ UBO ubo; }
ConstantBuffer <UBO> ubo : register(b0, space0);
VSOutput main(VSInput input, uint VertexIndex : SV_VertexID)
{
VSOutput output = (VSOutput) 0;
float4x4 camMatrix = mul(ubo.cam.proj, ubo.cam.view);
output.Position = mul(camMatrix, mul(ubo.model, input.Position));
output.Color = input.Color;
output.UVcoord = input.UVcoord;
return output;
}
这是像素/片段着色器 HLSL 代码:
struct PSInput
{
float4 Position : SV_Position;
[[vk::location(0)]] float4 Color : COLOR0;
[[vk::location(1)]] float2 UVcoord : TEXCOORD0;
};
// Descriptor loading in HLSL
//Texture2D tex2D : register(t0); // TODO: Figure out texture loading and sampling in HLSL
//sampler sample2D : register(s0, space1); // texture sampler at (binding = 0, set = 1)
struct PSOutput
{
[[vk::location(0)]] float4 Color : COLOR0;
};
PSOutput main(PSInput input) : SV_TARGET
{
PSOutput output = (PSOutput) 0;
//output.Color = tex2D.Sample(sample2D, input.UVcoord); //TODO: figure out the right syntax for this
output.Color = input.Color;
return output;
}
最后是我的着色器结构:
struct Shader {
Shader(const std::string& filename, VkShaderStageFlagBits shaderStage) {
try {
auto shaderCode = readFile(".\\shaders\\" + filename);
createShaderModule(shaderCode, filename);
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
stageInfo.stage = shaderStage;
stageInfo.module = shaderModule;
stageInfo.pName = "main";
}
~Shader() {
vkDestroyShaderModule(GPU::device, shaderModule, nullptr);
}
public:
VkShaderModule shaderModule;
VkPipelineShaderStageCreateInfo stageInfo
{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
private:
void createShaderModule(const std::vector<char>& code, std::string filename) {
VkShaderModuleCreateInfo createInfo
{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
createInfo.codeSize = code.size();// also tried code.size()*sizeof(uint32_t) and code.size()*4
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
if (vkCreateShaderModule(GPU::device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
throw std::runtime_error("failed to create shader module from " + filename + "!");
}
}
// File Reader
static std::vector<char> readFile(const std::string& filename) {
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error(std::format("failed to open {}!", filename));
}
size_t fileSize = (size_t)file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
};
着色器是通过以下方式创建的:
Shader vertShader("vertex_vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
Shader fragShader("pixel_frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
我知道我的像素/片段着色器会有一些问题,因为我还没有在 C++ 方面进行纹理加载,但即使没有像素着色器,顶点着色器仍然会产生验证错误。我该如何解决这个问题?
编辑:更改了包含着色器模块功能的部分,以显示着色器加载背后的所有代码,其中大部分来自Vulkan-Tutorial指南。
编辑2: 我不确定运行调试器后要查看哪个变量,但以下是我认为感兴趣的一些变量:
当使用
code.size() * uint32_t
运行时,createInfo.pCode
的值为0x000001c2da20ff60
或119734787
,code
的值为
\x3\x2#\a\0\0\x1\0\0\0\xe\0*\0\0\0\0\0\0\0\x11\0\x2\0\x1\0\0\0\xe\0\x3\0\0\0\0\0\x1\0\0\0\xf\0\v\0\0\0\0\0\x1\0\0\0main\0\0\0\0\x2\0\0\0\x3\0\0\0\x4\0\0\0\x5\0\0\0\x6\0\0\0\a\0\0\0\x3\0\x3\0\x5\0\0\0€\x2\0\0\x5\0\x5\0\b\0\0\0type.ubo\0\0\0\0\x6\0\x4\0\b\0\0\0\0\0\0\0ubo\0\x5\0\x3\0\t\0\0\0UBO\0\x6\0\x4\0\t\0\0\0\0\0\0\0dt\0\0\x6\0\x5\0\t\0\0\0\x1\0\0\0model\0\0\0\x6\0\x4\0\t\0\0\0\x2\0\0\0cam\0\x5\0\x4\0...
当使用
code.size()
运行时,createInfo.pCode
的值为0x00000196624fff60
或119734787
,code
的值为
\x3\x2#\a\0\0\x1\0\0\0\xe\0*\0\0\0\0\0\0\0\x11\0\x2\0\x1\0\0\0\xe\0\x3\0\0\0\0\0\x1\0\0\0\xf\0\v\0\0\0\0\0\x1\0\0\0main\0\0\0\0\x2\0\0\0\x3\0\0\0\x4\0\0\0\x5\0\0\0\x6\0\0\0\a\0\0\0\x3\0\x3\0\x5\0\0\0€\x2\0\0\x5\0\x5\0\b\0\0\0type.ubo\0\0\0\0\x6\0\x4\0\b\0\0\0\0\0\0\0ubo\0\x5\0\x3\0\t\0\0\0UBO\0\x6\0\x4\0\t\0\0\0\0\0\0\0dt\0\0\x6\0\x5\0\t\0\0\0\x1\0\0\0model\0\0\0\x6\0\x4\0\t\0\0\0\x2\0\0\0cam\0\x5\0\x4\0...
这里的实际问题是您的 Visual Studio 项目正在使用“HLSL 编译器”设置来编译着色器。 GitHub 中的
*.spv
文件没问题(我在长评论中错了)。当您构建解决方案时,Visual Studio 将调用 MSFT 编译器,该编译器可能不了解spirv。构建解决方案会导致 *.spv
文件被不正确的内容覆盖。
我认为解决此问题的最佳方法是不要在项目属性中使用“HLSL 编译器”构建步骤,因为这不是 DX 应用程序。而是使用自定义构建步骤从 Vulkan SDK 调用
dxc
编译器。