PENGUINLIONG / spirq-rs

Light weight SPIR-V reflection library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ExtInst opcode error with VK_KHR_shader_non_semantic_info when .spv has embedded GLSL inside

Scthe opened this issue · comments

Hi, this is a follow up to "Question about "A typical Vulkan frame".

I remember I used VK_KHR_shader_non_semantic_info not for debugPrintfEXT, but to embed .glsl text into .spv file. RenderDoc can then use the glsl text for step-by-step debugging. This requires to compile GLSL into SPIR-V a bit differently than usual.

Reproduction steps

  1. glslangValidator.exe -e main -gVS -V -o "assets/my_shader.frag.spv" "assets/my_shader.frag.glsl". Compile GLSL into SPIR-V with embedded GLSL code inside .spv file.
  2. cargo run. See attached code below, but it's basically your 'walk' example.
    1. Crashes with:
collected spirvs: ["my_shader.frag"]
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: unexpected opcode ExtInst', src\main.rs:16:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\spirq-rs-test.exe` (exit code: 101)

The above code works fine if we compile GLSL normally (no embedded shader code): glslc.exe -O -fshader-stage=frag "assets/my_shader.frag.glsl" -o "assets/my_shader.frag.spv".

The error mentions ExtInst, known as a 'NonSemanticInstruction'. Since it's just a raw text inside .spv, it's indeed non-semantic.

My notes

Ofc. if anyone else has similar problems they can build both variants of .spv at the same time (separate files). One used for reflection and production release, while other is for debug mode inside RenderDoc. The debug build would have to read both files. TBH. I'm not sure if fixing this behaviour is in the scope of this project, although debugging shaders using RenderDoc is quite pleasant.

Specs

  • cargo 1.72.1 (103a7ff2e 2023-08-15)

  • rustc 1.72.1 (d5c2e9c34 2023-09-13)

  • glslc.exe --version:

shaderc v2023.6 v2023.6
spirv-tools v2023.4 v2022.4-296-ge553b884
glslang 11.1.0-763-g76b52ebf

Target: SPIR-V 1.0
  • glslangValidator.exe --version:
Glslang Version: 11:12.3.1
ESSL Version: OpenGL ES GLSL 3.20 glslang Khronos. 12.3.1
GLSL Version: 4.60 glslang Khronos. 12.3.1
SPIR-V Version 0x00010600, Revision 1
GLSL.std.450 Version 100, Revision 1
Khronos Tool ID 8
SPIR-V Generator Version 11
GL_KHR_vulkan_glsl version 100
ARB_GL_gl_spirv version 100

Files

Whole project attached in .zip.

spirq-rs-error.zip

Code_NmrJTmKGBe

assets/my_shader.frag.glsl

#version 450

// assets\my_shader.frag.glsl
layout(binding = 0)
uniform GlobalConfigUniformBuffer {
  vec4 u_stuff;
};

layout(binding = 1)
uniform sampler2D u_depthBufferTex;


layout(location = 0) in vec2 v_position;
layout(location = 0) out vec4 outDepth;


void main() {
  // use uniforms so that compiler does not remove them
  float depthBufferZ = texture(u_depthBufferTex, v_position).r;
  outDepth = vec4(vec3(depthBufferZ), u_stuff.x);
}

Cargo.toml

[package]
name = "spirq-rs-test"
version = "1.0.0"
authors = ["Scthe <marcin1113C@gmail.com>"]
edition = "2018"
license = "MIT"

[dependencies]
spirq = "1.2.0"

src/main.rs

// Based on: https://github.com/PENGUINLIONG/spirq-rs/blob/master/spirq/examples/walk/main.rs .
// Replace `fn main()` with (just changed name to 'my_shader.frag'):

// (imports same as original)

fn main() {
    let spvs = collect_spirv_binaries("assets");

    println!(
        "collected spirvs: {:?}",
        spvs.iter().map(|x| x.0.as_ref()).collect::<Vec<&str>>()
    );
    let entry_points = ReflectConfig::new()
        .spv(spvs.get("my_shader.frag").unwrap() as &[u8])
        .ref_all_rscs(true)
        .reflect()
        .unwrap();
    println!("{:?}", entry_points);
    for var in entry_points[0].vars.iter() {
        println!("{:?}", var);
        for route in var.walk() {
            println!("- {:?}", route);
        }
    }
}

fn collect_spirv_binaries<P: AsRef<Path>>(path: P) -> BTreeMap<String, Vec<u8>> {
  // (same as original)
}

Thanks a lot for such detailed repro steps! :) I will take a close look at it.

Turns out the fix was an one-liner. Would you mind I use this shader as a test case in this crate?

collected spirvs: ["my_shader.frag"]
[main { exec_model: Fragment, name: "main", vars: [Descriptor { name: Some("u_depthBufferTex"), desc_bind: (set=0, bind=1), desc_ty: CombinedImageSampler, ty: CombinedImageSampler(CombinedImageSamplerType { sampled_image_ty: SampledImageType { scalar_ty: Float { bits: 32 }, dim: Dim2D, is_depth: Some(false), is_array: false, is_multisampled: false } }), nbind: 1 }, Input { name: Some("v_position"), location: (loc=0, comp=0), ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 2 }) }, Output { name: Some("outDepth"), location: (loc=0, comp=0), ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 }) }, Descriptor { name: None, desc_bind: (set=0, bind=0), desc_ty: UniformBuffer, ty: Struct(StructType { name: Some("GlobalConfigUniformBuffer"), members: [StructMember { name: Some("u_stuff"), offset: Some(0), ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 }), access_ty: ReadWrite }] }), nbind: 1 }], exec_modes: [ExecutionMode { exec_mode: OriginUpperLeft, operands: [] }] }]
Descriptor { name: Some("u_depthBufferTex"), desc_bind: (set=0, bind=1), desc_ty: CombinedImageSampler, ty: CombinedImageSampler(CombinedImageSamplerType { sampled_image_ty: SampledImageType { scalar_ty: Float { bits: 32 }, dim: Dim2D, is_depth: Some(false), is_array: false, is_multisampled: false } }), nbind: 1 }
- MemberVariableRouting { sym: [], offset: 0, ty: CombinedImageSampler(CombinedImageSamplerType { sampled_image_ty: SampledImageType { scalar_ty: Float { bits: 32 }, dim: Dim2D, is_depth: Some(false), is_array: false, is_multisampled: false } }) }
Input { name: Some("v_position"), location: (loc=0, comp=0), ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 2 }) }
- MemberVariableRouting { sym: [], offset: 0, ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 2 }) }
Output { name: Some("outDepth"), location: (loc=0, comp=0), ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 }) }
- MemberVariableRouting { sym: [], offset: 0, ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 }) }
Descriptor { name: None, desc_bind: (set=0, bind=0), desc_ty: UniformBuffer, ty: Struct(StructType { name: Some("GlobalConfigUniformBuffer"), members: [StructMember { name: Some("u_stuff"), offset: Some(0), ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 }), access_ty: ReadWrite }] }), nbind: 1 }
- MemberVariableRouting { sym: [u_stuff], offset: 0, ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 }) }
- MemberVariableRouting { sym: [], offset: 0, ty: Struct(StructType { name: Some("GlobalConfigUniformBuffer"), members: [StructMember { name: Some("u_stuff"), offset: Some(0), ty: Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 }), access_ty: ReadWrite }] }) }

Feel free to use my code however you like. It's just everyone's first shader and doesn't even do anything. Though from what I've seen you just needed .spv for tests, so it doubly does not matter.

I will also update my blog post (that started the whole thing) once I have more time.