eliemichel / LearnWebGPU-Code

The accompanying code of the Learn WebGPU C++ programming guide
https://eliemichel.github.io/LearnWebGPU
MIT License
114 stars 30 forks source link

step030 Can not load SPIR-V shader #39

Open MHDtA-dev opened 6 months ago

MHDtA-dev commented 6 months ago

I have a function that loads spv file:

std::vector<uint32_t> loadSpv(std::string path) {
    std::ifstream file(path, std::ios::ate | std::ios::binary);

    if (!file.is_open()) {
        throw std::runtime_error("Failed to load file");
    }

    size_t fileSize = (size_t)file.tellg();
    std::vector<uint32_t> buffer(fileSize / sizeof(uint32_t));
    file.seekg(0);
    file.read((char*) buffer.data(), fileSize);
    file.close();

    return buffer;
}

I am trying to create the shader as follows:

    auto vertexCode = loadSpv("Shaders/Triangle/v.spv");

    wgpu::ShaderModuleSPIRVDescriptor vert;
    vert.chain.next = nullptr;
    vert.chain.sType = wgpu::SType::ShaderModuleSPIRVDescriptor;
    vert.code = vertexCode.data();
    vert.codeSize = vertexCode.size() * sizeof(uint32_t);

    wgpu::ShaderModuleDescriptor vertDesc;
    vertDesc.nextInChain = &vert.chain;
    vertDesc.hintCount = 0;
    vertDesc.hints = nullptr;

    wgpu::ShaderModule vertex = device.createShaderModule(vertDesc);

But it crashes with error:

thread '<unnamed>' panicked at src/lib.rs:429:5:
wgpu uncaptured error:
Validation Error

Caused by:
    In wgpuDeviceCreateShaderModule
    invalid word count

I also tried to use this variant of loading:

std::vector<char> loadSpv(std::string path) {
    std::ifstream file(path, std::ios::ate | std::ios::binary);

    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file");
    }

    size_t fileSize = (size_t) file.tellg();
    std::vector<char> buffer(fileSize);

    file.seekg(0);
    file.read(buffer.data(), fileSize);

    file.close();

    return buffer;
}
    auto vertexCode = loadSpv("Shaders/Triangle/v.spv");

    wgpu::ShaderModuleSPIRVDescriptor vert;
    vert.chain.next = nullptr;
    vert.chain.sType = wgpu::SType::ShaderModuleSPIRVDescriptor;
    vert.code = reinterpret_cast<const uint32_t*>(vertexCode.data());
    vert.codeSize = vertexCode.size();

    wgpu::ShaderModuleDescriptor vertDesc;
    vertDesc.nextInChain = &vert.chain;
    vertDesc.hintCount = 0;
    vertDesc.hints = nullptr;

    wgpu::ShaderModule vertex = device.createShaderModule(vertDesc);

But it causes the same error.

Here is my shader code:

#version 460

layout (location = 0) in vec2 inPos;

void main() {
    gl_Position = vec4(inPos, 1, 1);
}

Compiled with glslc v.vert -o v.spv

System: MacOS Ventura (Intel)

How can I correctly load SPIR-V shader?

MHDtA-dev commented 5 months ago

Any update?

cjsimon commented 5 months ago

https://github.com/gpuweb/gpuweb/issues/847

Unfortunately, I don't think the WebGPU spec specifies loading SPIR-V directly. You'd have to use a transpiler like naga or tint to compile your shader code into wgsl.

Also note that WGSL was originally designed to be a human-editable version of SPIR-V programming model, so transpilation from SPIR-V to WGSL is in theory efficient and lossless (use Naga or Tint for this).

https://github.com/eliemichel/LearnWebGPU/blob/e53f715daee35ef09eb41110bcefe5e5ca00c982/basic-3d-rendering/hello-triangle.md#L368

The shader language officially used by WebGPU is called WGSL, namely the WebGPU Shading Language. Any implementation of WebGPU must support it, and the JavaScript version of the API only supports WGSL, but the native header webgpu.h also offers the possibility to provide shaders written in SPIR-V or GLSL (wgpu-native only).

https://github.com/eliemichel/LearnWebGPU/blob/e53f715daee35ef09eb41110bcefe5e5ca00c982/basic-3d-rendering/hello-triangle.md#L361

I assume this is referring to naga/spv-in?

These notes on SPIR-V to WGSL translation efforts in Tint make me wonder about the efficiency and losslessness on a high-level.

https://dawn.googlesource.com/dawn/+/ada5e7b0f9de129a54667f9e28f1865623261190/docs/tint/spirv-reader-overview.md

https://dawn.googlesource.com/dawn/+/ada5e7b0f9de129a54667f9e28f1865623261190/docs/tint/translations.md


Never mind... Just realized you're already using wgpu-native. This seems related: https://github.com/gfx-rs/wgpu/issues/4915. Have you tried compiling with glslangValidator instead?

eliemichel commented 5 months ago

Unfortunately, I don't think the WebGPU spec specifies loading SPIR-V directly.

The Web version of WebGPU (i.a., what browsers support) does not support loading SPIR-V directly indeed. However, the native version of WebGPU backends has extra features, and both Dawn and wgpu-native do support it actually. Actually, the WGPUShaderModuleSPIRVDescriptor chain extension is even part of the standard native header. Of course, this feature does not support cross-compilation to WebAssembly.

Note that in the FetchDawn.cmake script provided by my WebGPU-distribution, I turn TINT_BUILD_SPV_READER off to speed up compilation, but it is easy to turn it back on.

As for the original issue, it is a wgpu-native specific one. After searching in their codebase, it seems to come from this line in wgpu/naga/src/front/spv/mod.rs. The "word count" in question (wc in the code) is the 16 most significant bits of the next 32-bit "word" that is being loaded from the SPIR-V shader. This shouldn't be 0 so there is something wrong with your SPIR-V file. I suggest you try and compile it back, or inspecting using a dedicated tool like SPIR-V Visualizer. If the problem persists, fill an issue in the wgpu repo!

MHDtA-dev commented 5 months ago

I tried to compile with glslangValidator, but unfortunately it didn't work. I tested this on other systems(Windows 10 MSYS2 MinGW64 and Ubuntu 22.04 g++) and this issue also appears there. Also it can't be something with my spv file, because I was just able to successfully upload this shader to Vulkan using functions I have given above. The only one way out for me - use wgpu::ShaderModuleGLSLDescriptor which works correctly. Anyway, thanks for help.

P.S. Does not apply to this issue, after the last commit in the webgpu.hpp file of the wgpu branch, an error occurs that the CPP_VERSION macro is not defined. I suppose there should be __cplusplus instead

eliemichel commented 5 months ago

P.S. Does not apply to this issue, after the last commit in the webgpu.hpp file of the wgpu branch, an error occurs that the CPP_VERSION macro is not defined. I suppose there should be __cplusplus instead

Wow my bad, I fixed this thx for reporting!

Also it can't be something with my spv file, because I was just able to successfully upload this shader to Vulkan using functions I have given above.

Mmh ok, could you share your very spv file (+ the text version)? I'll try with both Dawn and wgpu-native, it may be a bug of the latter that we need to report.

MHDtA-dev commented 5 months ago

Of course, uploaded it here

eliemichel commented 5 months ago

Both wgpu-native and Dawn are unhappy with vert_glslc.spv or vert_glslangValidator.spv:

wgpu-native

Uncaptured device error: type 1 (Validation Error

Caused by:
    In wgpuDeviceCreateShaderModule
    unknown instruction 65021
)

Dawn

Uncaptured device error: type 1 (Produced invalid SPIRV. Please file a bug at https://crbug.com/tint.
 - While validating [ShaderModuleDescriptor]
 - While calling [Device "My Device"].CreateShaderModule([ShaderModuleDescriptor]).
)