gfx-rs / wgpu

A cross-platform, safe, pure-Rust graphics API.
https://wgpu.rs
Apache License 2.0
12.03k stars 873 forks source link

Naga translated SpirV shader cannot be passed through #4763

Open eadwu opened 9 months ago

eadwu commented 9 months ago

Description A clear and concise description of what the bug is.

Trying out SpirV passthrough just to see if it worked, but it doesn't seem to work.

What does work:

Translation to/from are handled using Naga

Repro steps Ideally, a runnable example we can check out.

cargo build && target/debug/hello-compute on v0.18.1

diff --git a/Cargo.lock b/Cargo.lock
index 5649288b0..a71025e04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3909,6 +3909,7 @@ dependencies = [
  "env_logger",
  "flume",
  "log",
+ "naga",
  "pollster",
  "wasm-bindgen-futures",
  "wasm-bindgen-test",
diff --git a/examples/hello-compute/Cargo.toml b/examples/hello-compute/Cargo.toml
index b9a485775..8bf19187f 100644
--- a/examples/hello-compute/Cargo.toml
+++ b/examples/hello-compute/Cargo.toml
@@ -18,6 +18,7 @@ flume.workspace = true
 pollster.workspace = true
 wgpu.workspace = true
 winit.workspace = true
+naga.workspace = true

 [target.'cfg(target_arch = "wasm32")'.dependencies]
 console_error_panic_hook.workspace = true
diff --git a/examples/hello-compute/src/main.rs b/examples/hello-compute/src/main.rs
index c7d09c6a4..ecb39ef77 100644
--- a/examples/hello-compute/src/main.rs
+++ b/examples/hello-compute/src/main.rs
@@ -35,7 +35,10 @@ async fn run() {
 #[cfg_attr(test, allow(dead_code))]
 async fn execute_gpu(numbers: &[u32]) -> Option<Vec<u32>> {
     // Instantiates instance of WebGPU
-    let instance = wgpu::Instance::default();
+    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
+        backends: wgpu::Backends::VULKAN,
+        ..wgpu::InstanceDescriptor::default()
+    });

     // `request_adapter` instantiates the general connection to the GPU
     let adapter = instance
@@ -48,7 +51,7 @@ async fn execute_gpu(numbers: &[u32]) -> Option<Vec<u32>> {
         .request_device(
             &wgpu::DeviceDescriptor {
                 label: None,
-                features: wgpu::Features::empty(),
+                features: wgpu::Features::SPIRV_SHADER_PASSTHROUGH,
                 limits: wgpu::Limits::downlevel_defaults(),
             },
             None,
@@ -64,11 +67,37 @@ async fn execute_gpu_inner(
     queue: &wgpu::Queue,
     numbers: &[u32],
 ) -> Option<Vec<u32>> {
+    let wgsl_module = naga::front::wgsl::parse_str(include_str!("shader.wgsl")).unwrap();
+    let info = naga::valid::Validator::new(
+        naga::valid::ValidationFlags::all(),
+        naga::valid::Capabilities::all(),
+    ).validate(&wgsl_module).unwrap();
+    let spirv_module = naga::back::spv::write_vec(
+        &wgsl_module,
+        &info,
+        &naga::back::spv::Options::default(),
+        Some(&naga::back::spv::PipelineOptions {
+            shader_stage: naga::ShaderStage::Compute,
+            entry_point: "main".to_string(),
+        }),
+    ).unwrap();
+
+    let bytes: &[u8] = bytemuck::cast_slice(&spirv_module[..]);
+
     // Loads the shader from WGSL
-    let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
-        label: None,
-        source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
-    });
+    let cs_module = unsafe {
+        device.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
+            label: None,
+            // source: Cow::Borrowed(&spirv_module[..]),
+            source: wgpu::util::make_spirv_raw(bytes),
+        })
+    };
+
+    // let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
+    //     label: None,
+    //     source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
+    //     // source: wgpu::util::make_spirv(bytes),
+    // });

     // Gets the size in bytes of the buffer.
     let size = std::mem::size_of_val(numbers) as wgpu::BufferAddress;
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index 343ed2b05..f08c3fc10 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -25,7 +25,7 @@ targets = [
 [lib]

 [features]
-default = ["wgsl"]
+default = ["wgsl", "spirv"]
 # Apply run-time checks, even in release builds. These are in addition
 # to the validation carried out at public APIs in all builds.
 strict_asserts = ["wgc?/strict_asserts", "wgt/strict_asserts"]

Expected vs observed behavior Clearly describe what you get, and how it goes across your expectations.

Expected (wgpu::util::make_spirv(bytes)):

No numbers were provided, defaulting to [1, 2, 3, 4]
Steps: [0, 1, 7, 2]

Observed:

[2023-11-24T01:50:47Z ERROR wgpu_hal::vulkan::instance] VALIDATION [VUID-VkComputePipelineCreateInfo-layout-07988 (0xd7bf5790)]
        Validation Error: [ VUID-VkComputePipelineCreateInfo-layout-07988 ] Object 0: handle = 0xead9370000000008, type = VK_OBJECT_TYPE_SHADER_MODULE; Object 1: handle = 0x967dd1000000000e, type = VK_OBJECT_TYPE_PIPELINE_LAYOUT; | MessageID = 0xd7bf5790 | vkCreateComputePipelines(): pCreateInfos[0] Set 0 Binding 0 in shader (VK_SHADER_STAGE_COMPUTE_BIT) uses descriptor slot (expected `VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC`) but not declared in pipeline layout The Vulkan spec states: If a resource variables is declared in a shader, a descriptor slot in layout must match the shader stage (https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkComputePipelineCreateInfo-layout-07988)
[2023-11-24T01:50:47Z ERROR wgpu_hal::vulkan::instance]         objects: (type: SHADER_MODULE, hndl: 0xead9370000000008, name: ?), (type: PIPELINE_LAYOUT, hndl: 0x967dd1000000000e, name: ?)

Extra materials Screenshots to help explain your problem. Validation logs can be attached in case there are warnings and errors. Zip-compressed API traces and GPU captures can also land here.

Platform Information about your OS, version of wgpu, your tech stack, etc.

eadwu commented 9 months ago

On master (86562e69) it is just a crash (SpirV passthrough), although passing it through wgpu::util::make_spirv still works.

 RUST_LOG=hello_compute cargo run --bin wgpu-examples hello_compute
No numbers were provided, defaulting to [1, 2, 3, 4]
zsh: segmentation fault (core dumped)  RUST_LOG=hello_compute cargo run --bin wgpu-examples hello_compute
teoxoy commented 9 months ago

The issue is that the example is using an auto layout (one that is derived from the shader). But we currently don't support being able to derive the layout from passthrough shaders.

https://github.com/gfx-rs/wgpu/blob/86562e69a68d2c67cc6f0b15a52a2f4327387227/wgpu-core/src/device/resource.rs#L1449

It should be possible to derive the layout from raw spir-v but considering we haven't gotten this feature request yet, we should at least error out early.