Giter Club home page Giter Club logo

spirq-rs's Introduction

PENGUINLIONG

B.S. alumni at UCI; Vulkan & Rust enthusiast; previous Research intern at MSRA, previous Game Engine RnD Engineer at Tencent, current GPU RnD Engineer at Taichi Graphics. I created the Taichi Runtime C-API.

I'm looking for PhD position in some hybrid zone among deep learning, graphics and their systems. Please feel free to reach out.

penguinliong [at] gmail [dot] com

My resume is available here.

spirq-rs's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

spirq-rs's Issues

OpTypeImage sampled=1 depth=2 should be recognized as a sampled image to support HLSL

apologies if this is a duplicate, but HLSL generates the following spirv typedef for

Texture2D<float4> albedo_tex;

this yields:

%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown

Currently this makes spirq spit out an unsupported img cfg error, it should instead be recognized as a sampled image, because 2 for depth according to the spirv spec is unspecified with regards to whether it is a depth tex or not (same with sampled). However, 1 is "definitely sampled", therefore this type of texture should be recognized as a sampled texture. I tried this by just changing the image type code to:

    pub fn from_spv_def(
        is_sampled: u32,
        is_depth: u32,
        color_fmt: ImageFormat,
    ) -> Result<ImageUnitFormat> {
        let img_unit_fmt = match (is_sampled, is_depth, color_fmt) {
            (1, 0, _) => ImageUnitFormat::Sampled,
            (1, 2, _) => ImageUnitFormat::Sampled,
            (1, 1, _) => ImageUnitFormat::Depth,
            (2, 0, color_fmt) => ImageUnitFormat::Color(color_fmt),
            (2, 2, color_fmt) => ImageUnitFormat::Color(color_fmt),
            _ => return Err(Error::UNSUPPORTED_IMG_CFG),
        };
        Ok(img_unit_fmt)
    }

But i am not sure if there are other considerations that should be taken. The reverse should probably also be done, i.e. (2, 1, _) => ImageUnitFormat::Depth. This code also maps (2, 2) to a color image since the approach is to assume 2 means "no".

This is the same behavior as rspirv-reflect btw.

Erroneous association of outputs when code is in a single spirv file.

I am using rust gpu to compile shaders. rust-gpu generates a single spirv for all stages. I am having the following issue I have this shader:

#![deny(warnings)]

mod shared;

extern crate spirv_std;
extern crate bytemuck;

use shared::glam::{Vec4, Vec3};
use spirv_std::spirv;

#[spirv(fragment)]
// pub fn main_fs(next_pos: Vec4, output: &mut Vec4)
pub fn main_fs(output: &mut Vec4)
{
    // *output = Vec4::new(1.0, 0.0001 * next_pos.x, 0.0, 1.0);
    *output = Vec4::new(1.0, 0.0001, 0.0, 1.0);
}

#[spirv(vertex)]
pub fn main_vs(
    #[spirv(vertex_index)] vert_id: i32,
    #[spirv(uniform, descriptor_set = 1, binding = 0)] toggle: &u32,
    #[spirv(position, invariant)] out_pos: &mut Vec4,
    position: Vec3,
    offset_bind2: Vec3,
    n_pos: &mut Vec4,
) {
    *out_pos = Vec4::new(
        (vert_id - 1) as f32 + (position.x + offset_bind2.x) * 0.000001,
        ((vert_id & 1) * 2 - 1) as f32,
        0.0,
        if *toggle == 0 { 1.0 } else { 0.0 },
    );

    *n_pos = *out_pos;
}

I am then parsing the generated spirv with spirq:

 Variable::Output { name, location, ty } =>
                    {
                        if entry_point.exec_model != ExecutionModel::Fragment
                        {
                            continue;
                        }

                        println!("{:?}", ty);
                        println!("{:?}", name);
                        println!("{:?}", entry_point.exec_model);
                        attachment_outputs.push(parse_fragment_output(&ty));
                    }

Which is printing:

Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 })
Some("output")
Fragment
Vector(VectorType { scalar_ty: Float { bits: 32 }, nscalar: 4 })
Some("n_pos")
Fragment

So it seems that spirq is assigning all outputs from all shader stages to each entry point, instead of just those defined for that entry point alone.

Roadmap for v0.5.x

Just a todo list for future development.

  • Deprecate poorly designed APIs
  • Separate pipeline procedures and error
  • no_std
  • Compile to WASM
  • Raytracing pipeline
  • Mesh pipeline

CorruptedSpirv("missing decoration") on valid spir-v

Structs with matrix members in uniform buffers cause errors when loading the struct whole struct from the buffer.

glsl that causes the error:

#version 450

struct Foo {
    mat4 matrix;
};

layout(set = 0, binding = 0) uniform FooData {
    Foo data;
} u_foo_data;

void main() {
    Foo f = u_foo_data.data;
}

spir-v that causes the error (compiled with glslc, displayed with rspirv)

; SPIR-V
; Version: 1.0
; Generator: rspirv
; Bound: 24
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
OpSourceExtension "GL_GOOGLE_include_directive"
OpName %4 "main"
OpName %9 "Foo"
OpMemberName %9 0 "matrix"
OpName %11 "f"
OpName %12 "Foo"
OpMemberName %12 0 "matrix"
OpName %13 "FooData"
OpMemberName %13 0 "data"
OpName %15 "u_foo_data"
OpMemberDecorate %12 0 ColMajor
OpMemberDecorate %12 0 Offset 0
OpMemberDecorate %12 0 MatrixStride 16
OpMemberDecorate %13 0 Offset 0
OpDecorate %13 Block
OpDecorate %15 DescriptorSet 0
OpDecorate %15 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypeMatrix %7 4
%9 = OpTypeStruct %8
%10 = OpTypePointer Function %9
%12 = OpTypeStruct %8
%13 = OpTypeStruct %12
%14 = OpTypePointer Uniform %13
%15 = OpVariable  %14  Uniform
%16 = OpTypeInt 32 1
%17 = OpConstant  %16  0
%18 = OpTypePointer Uniform %12
%22 = OpTypePointer Function %8
%4 = OpFunction  %2  None %3
%5 = OpLabel
%11 = OpVariable  %10  Function
%19 = OpAccessChain  %18  %15 %17
%20 = OpLoad  %12  %19
%21 = OpCompositeExtract  %8  %20 0
%23 = OpAccessChain  %22  %11 %17
OpStore %23 %21
OpReturn
OpFunctionEnd

My speculation as to the cause:
The way glslc compiles the following glsl causes the error.
Because glslc generates two identical structs one for interface blocks and one for internal usage, since only the first gets matrix stride decorations and the second one doesn't I think this causes an incorrect error return on line 1045 of src/reflect.rs when parsing the second struct type. Specifically %9 and %12 both represent the same struct but only %12 is decorated with matrix stride as only %12 is exposed in interface blocks so when parsing %9 an incorrect error is caused.

Expose decorations on descriptors

Currently it doesn't seem to be possible to expose what decorations are associated with a specific descriptor, but this would be very helpful information to expose. Right now I'm interested in knowing whether storage images are tagged as NonWritable or NonReadable, but there are surely other uses that people could be interested in.

Reflection does not include built-in variables with DXC

Hello! Thank you so much for making this crate! I currently found an issue while generating reflection data from SPIR-V code compiled in DXC (v1.7.2212).

I have been using this (simple) HLSL code for testing:

struct ia2v {
    float4 pos : POSITION;
};

struct v2f {
    float4 pos : SV_POSITION;
};

v2f vert(ia2v input) {
    v2f o;
    o.pos = input.pos;
    return o;
}
    
float4 frag(v2f input) : SV_Target0 {
    return input.pos;
}

with the following command to compile in DXC:

$ dxc.exe -T vs_6_0  -E vert -spirv  -O3 -fspv-reflect 

and get the following SPIRV result (also visible here: https://godbolt.org/z/jfoM7h6ja)

               OpCapability Shader
               OpExtension "SPV_GOOGLE_hlsl_functionality1"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Vertex %vert "vert" %in_var_POSITION %gl_Position
               OpDecorateString %in_var_POSITION UserSemantic "POSITION"
               OpDecorate %gl_Position BuiltIn Position
               OpDecorateString %gl_Position UserSemantic "SV_POSITION"
               OpDecorate %in_var_POSITION Location 0
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
       %void = OpTypeVoid
         %10 = OpTypeFunction %void
%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
%gl_Position = OpVariable %_ptr_Output_v4float Output
       %vert = OpFunction %void None %10
         %11 = OpLabel
         %12 = OpLoad %v4float %in_var_POSITION
               OpStore %gl_Position %12
               OpReturn
               OpFunctionEnd

Everything looks OK so far, but once i try to reflect I get this:

vert {
    exec_model: Vertex,
    name: "vert",
    vars: [
        Input {
            name: Some(
                "in.var.POSITION",
            ),
            location: (loc=0, comp=0),
            ty: Vector(
                VectorType {
                    scalar_ty: Float {
                        bits: 32,
                    },
                    nscalar: 4,
                },
            ),
        },
    ],
    exec_modes: [],
}

It seems to completely skip the output variable for this vertex shader, which looks like it represents the built-in gl_Position from glsl. If I change the semantics of the output variable to POSITION instead of SV_POSITION we get the following output (which is intended):

vert {
    exec_model: Vertex,
    name: "vert",
    vars: [
        Input {
            name: Some(
                "in.var.POSITION",
            ),
            location: (loc=0, comp=0),
            ty: Vector(
                VectorType {
                    scalar_ty: Float {
                        bits: 32,
                    },
                    nscalar: 4,
                },
            ),
        },
        Output {
            name: Some(
                "out.var.POSITION",
            ),
            location: (loc=0, comp=0),
            ty: Vector(
                VectorType {
                    scalar_ty: Float {
                        bits: 32,
                    },
                    nscalar: 4,
                },
            ),
        },
    ],
    exec_modes: [],
}

This also happens in fragment shaders under the same code under the same semantics (SV_POSITION). The built in variable is equivalent to gl_FragCoord under the fragment shader however. Note that I have toggled ref_all_rscs() with no change whatsoever.

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

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 <[email protected]>"]
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)
}

Cannot load push_constant vert shader compiled by naga-cli correctly

shader.vert

#version 450

layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
layout (location = 2) in vec2 inTexCoord;

layout (location = 0) out vec3 fragColor;
layout (location = 1) out vec2 fragTexCoord;

layout (set = 0, binding = 0) uniform UniformBufferObject {
    mat4 view;
    mat4 proj;
} ubo;

layout (push_constant) uniform PushConstants {
    mat4 model;
} pcs;

void main() {
    gl_Position = ubo.proj * ubo.view * pcs.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}

spirq can load spv file compiled by glslangValidator.exe

❯ glslangValidator.exe -V .\shader.vert -o .\shader.vert.spv

I inspect this shader.vert.spv with your 'gallery\main.rs' example, it works well:

[
    main {
        exec_model: Vertex,
        name: "main",
        vars: [
            Descriptor {
                name: Some(
                    "ubo",
                ),
...

However, I try to inspect the spv file compile by naga-cli, it doesn't work.

❯ naga .\shader.vert .\shader_naga.vert.spv
[
    main {
        exec_model: Vertex,
        name: "main",
        vars: [],
        exec_modes: [],
    },
]

I have attched the shader and both spv files below.

shader_and_spv.zip

Error on OpSpecConstantOp

Hi,

This is a similar issue to #41, the following fragment trips up spirq's reflection:

%493 = OpSpecConstant %uint 1
%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %493 %uint_1 %uint_1
; The next one is the culprit?
%495 = OpSpecConstantOp %uint CompositeExtract %gl_WorkGroupSize 0
%_arr_uint_495 = OpTypeArray %uint %495
%_ptr_Workgroup__arr_uint_495 = OpTypePointer Workgroup %_arr_uint_495
%test_shared_variable = OpVariable %_ptr_Workgroup__arr_uint_495 Workgroup

Which is generated from a GLSL fragment like this:

layout (local_size_x_id = 1) in;

shared uint test_shared_variable[gl_WorkGroupSize.x];

Error on specialization constant usage as compute dimension specifier

I believe this part is insufficient:

spirq-rs/src/reflect.rs

Lines 391 to 394 in 09d9034

OP_SPEC_CONSTANT_COMPOSITE => {
let op = OpSpecConstantComposite::try_from(instr)?;
let spec_id = self.get_deco_u32(op.spec_const_id, None, Decoration::SpecId)
.ok_or(Error::MISSING_DECO)?;

The library can't seem to load a compute shader like this:

#version 450

layout (local_size_x_id = 123) in;

void main() {
  // ...
}

I think it's expecting a direct reference to a decoration with the spec constant id, one that looks like this:

OpDecorate %variable_name SpecId 74
...
%variable_name = OpSpecConstant %uint 3

This gets generated when you use your typical layout (constant_id = 74) const uint variable_name = 3;

But the code generated for local_size_x_id breaks the pattern as follows:

                OpDecorate %494 SpecId 123
         %494 = OpSpecConstant %uint 1
 %gl_WorkGroupSize = OpSpecConstantComposite %v3uint %494 %uint_1 %uint_1

So I think there's one more jump needed.

Reflection gets confused by DXC's debug outputs

Hi, I saw your announcement on /r/rust_gamedev and tried out your library to replace spirv_reflect. I love the API and Debug instances, the types in spirq::ty are also idiomatic to work with!

I have a problem where enabling -Zi and/or -fspv-reflect affects the reflection (negatively in both cases). I don't need these for now so I'm good, but it would be a good idea to look into why these annotations break things. From what I could gather, -fspv-reflect only added a couple OpDecorateString entries which caused spirq to not see any inputs for that entrypoint.

I'm attaching my shader, dxc compile options and Debug output from spirq:

struct PushConstants {
  float2 scale;
  float2 translate;
};

[[vk::push_constant]] PushConstants pushConstants;

float4 main(in float2 pos : POSITION,
            in float2 uv : TEXCOORD,
            in float4 col : COLOR,
            out float4 outColor : COLOR,
            out float2 outUv : TEXCOORD): SV_POSITION {
  outColor = col;
  outUv = uv;
  float4 outPos = float4(pos * pushConstants.scale + pushConstants.translate, 0, 1);
  outPos.y *= -1.0;
  return outPos;
}
$ dxc -spirv -T vs_6_0 -E main -fspv-target-env=vulkan1.1 -Fo whatever.spv

Debug output:

    main {
        push_const: Some(
            { scale: vec2<f32>, translate: vec2<f32> },
        ),
        inputs: {
            (loc=0, comp=0): vec2<f32>,
            (loc=1, comp=0): vec2<f32>,
            (loc=2, comp=0): vec4<f32>,
        },
        outputs: {
            (loc=0, comp=0): vec4<f32>,
            (loc=1, comp=0): vec2<f32>,
        },
        descriptors: {},
        spec_consts: {},
    },

Perfect!

Now adding -fspv-reflect as an option to dxc, SPIR-V diff is now:

diff --git a/a.spv b/b.spv
index a3e8063..f22924c 100644
--- a/bare.spv
+++ b/test.spv
@@ -4,6 +4,7 @@
 ; Bound: 42
 ; Schema: 0
                OpCapability Shader
+               OpExtension "SPV_GOOGLE_hlsl_functionality1"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_TEXCOORD %in_var_COLOR %gl_Position %out_var_COLOR %out_var_TEXCOORD
                OpSource HLSL 600
@@ -17,7 +18,13 @@
                OpName %out_var_COLOR "out.var.COLOR"
                OpName %out_var_TEXCOORD "out.var.TEXCOORD"
                OpName %main "main"
+               OpDecorateString %in_var_POSITION UserSemantic "POSITION"
+               OpDecorateString %in_var_TEXCOORD UserSemantic "TEXCOORD"
+               OpDecorateString %in_var_COLOR UserSemantic "COLOR"
                OpDecorate %gl_Position BuiltIn Position
+               OpDecorateString %gl_Position UserSemantic "SV_POSITION"
+               OpDecorateString %out_var_COLOR UserSemantic "COLOR"
+               OpDecorateString %out_var_TEXCOORD UserSemantic "TEXCOORD"
                OpDecorate %in_var_POSITION Location 0
                OpDecorate %in_var_TEXCOORD Location 1
                OpDecorate %in_var_COLOR Location 2

But the output is:

    main {
        push_const: None,
        inputs: {},
        outputs: {},
        descriptors: {},
        spec_consts: {},
    },

It gets similarly confused by the -Zi flag, but not to such a great extent. It would be nice to support these flags as they're seemingly becoming standard for HLSL on Vulkan debugging and other tools.

Storage buffer recognised as unifom buffer.

In my shader i have defined an array of buffers like this:

layout(set = 0, binding = 0) buffer BVH{
    BVHNode nodes[];
}bvh[2];

But when reflecting the spirv code I get this ad the Desciptor:

Descriptor { name: Some("bvh"), desc_bind: (set=0, bind=0), desc_ty: UniformBuffer, ty: BVH { nodes: [BVHNode { min: vec4<f32>, max: vec4<f32>, ty: u32, right: u32, miss: u32, _pad: u32 }] }, nbind: 2 }

I have also tried it with spirv-reflect-rs and it outputs the following:

ReflectDescriptorBinding { spirv_id: 499, name: "bvh", binding: 0, input_attachment_index: 0, set: 0, descriptor_type: StorageBuffer, resource_type: UnorderedAccessView, ...

Where it is recognized correctly as a StorageBuffer. I came to the conclusion that the error couldn't lie in the spirv compiler I'm using.
Unfortunately I'm not relatively new to rust and don't quite understand how your project is working (especially the auto generated code) so I can't fix it myself. Thanks for your help.

Undefined behavior from MaybeUninit

Attempting to compile the crate with a recent nighly produces the following warning:

warning: the type `&mut reflect::Function` does not permit being left uninitialized
   --> src/reflect.rs:418:52
    |
418 |             let mut func: &mut Function = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
    |                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                                                    |
    |                                                    this code causes undefined behavior when executed
    |                                                    help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
    = note: `#[warn(invalid_value)]` on by default
    = note: References must be non-null

When is a block not a block?

I was writing up an issue for OpForwardPointer... but you literally fixed it in that period 🥇
It's taken me a while to get my head around my question, so maybe this one's been resolved also 😄

So, this is more of a use-question:
If you are using EXT_buffer_reference, reference-bound buffers are decorated as Block type. If the block is used, there is a pointer declared for that type. So, if this library were intended to map these types, it would seem like the place to do it is in populate_one_ty when Pointer types are captured: see if the pointer is to a Block decorated type with PhysicalStorageBuffer as the storage class.

Ok, but I can argue to myself that Block types are not Descriptors (because they aren't) or any of the other Variable types. Should there be a Variable::Block variant?

I need this feature, so I'm going to try to work it out, but I'd rather not trip up any of the overall design if this is something you think this lib needs also. So, I'd love to hear what you think of this.

I titled this issue so that it was clear this is more of a "discuss" issue, but also because I wanted to make sure that Block decorated types which have pointers to them only occur when you're using reference buffers.

Thanks for your hard work on this useful project!

Edit: I have since answered my own silly question, as it's Block types with PhysicalStorageBuffer storage classes that exactly describe what's being captured here, although there may be other possible configurations.

Access is not correctly calculated for some storage buffers

Hi! Thanks for this fantastic crate, it's worked so well for my usecases. I'm running into a storage buffer problem with the attached shader where the descriptor type is StorageBuffer(ReadWrite) when it should be ReadOnly.

moon.spv.zip

I've looked at the visualization from https://www.khronos.org/spir/visualizer/ and inserted some dbg! statements which showed that get_desc_access is being called on id %16 and not %14, which is the thing actually being decorated by NonWritable:

spirq-rs/spirq/src/reflect.rs

Lines 1340 to 1345 in f77af02

// For SPIR-V versions <= 1.3 in which `BufferBlock` is not
// deprecated.
let var = if self.contains_deco(ty_id, None, Decoration::BufferBlock) {
let access = self
.get_desc_access(op.var_id)
.ok_or(Error::ACCESS_CONFLICT)?;

20230526_14h05m26s_grim

ImageType is private in ty::SampledImageType?

ImageType is accessible in Type::Image(ImageType), but is a private field in
ty::SampledImageType, so the image details cant be extracted from Type::SampledImage(SampledImageType).

Is there a particular reason for this? Or for having ty::SampledImageType at all, rather than just using Type::SampledImage(ImageType)?

gl_VertexIndex is treated as having location=0

Due to this line, input/output variables without a location are treated as having location 0. This breaks code that also tries to use location 0 for its own purposes, because the real variable at that location can be overwritten by gl_VertexIndex or similar. I have a commit in my fork which discards these variables instead, but that doesn't seem like a great solution either.

Modifying the shader interface

Is changing certain aspects of an EntryPoint planned as a feature? Would it be out of scope of this library? I think it would be great to have this as an option, allowing users of spirq to rebind especially descriptor sets, since it's impossible to do that with specialization constants.

My layman opinion is that these would require only slight changes in the bytestream to literal values. These should not modify the sequence of the instruction stream, but there's probably something I'm not seeing.

Descriptor sets ignored in reflection

I noticed that if a descriptor binding is not used by a simple load/store, spirq seems to think it's not used and drops it. Here's an example shader that illustrates the problem:

#version 450

layout(set = 1, binding = 0) buffer TestBinding {
    uint count;
} test;

void main() {
    atomicAdd(test.count, 1); // spirq entry_point.desc() is empty
    // test.count = 5; // This will make spirq "notice" it and the descriptor binding will be reflected
}

I was only using a certain descriptor binding with these atomics and I noticed spirq dropping it completely. I had to work around it by doing something that can be optimized away by the runtime compiler, like:

if (gl_LocalInvocationIndex == 2048) // unreachable
  test.count = 0;

Conditional statements are reported as UnsupportedSpirv

The following line causes all boolean temporaries (i.e all conditions in if statements, for loops, etc) to be rejected as UnsupportedSpirv:

OP_TYPE_BOOL => return Err(Error::UnsupportedSpirv)

I presume this is here because boolean types aren't allowed in structs/buffers per the spec, but they are allowed as temporaries.

As a concrete example, this shader (taken from the SPIRV spec) currently can't be reflected:

#version 450

layout(location=0) in vec4 color1;
layout(location=1) in vec4 multiplier;
layout(location=2) noperspective in vec4 color2;

layout(location=0) out vec4 color;

struct S {
    bool b;
    vec4 v[5];
    int i;
};

layout(binding=0) uniform blockName {
    S s;
    bool cond;
};

void main()
{
    vec4 scale = vec4(1.0, 1.0, 2.0, 1.0);

    if (cond)
        color = color1 + s.v[2];
    else
        color = sqrt(color2) * scale;

    for (int i = 0; i < 4; ++i)
        color *= multiplier;
}

Any pointers on how to implement the boolean op code?

Bindless Textures ignored when using GL_EXT_buffer_reference2.

I have tried to reflect this glsl code:

#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_EXT_buffer_reference2 : require
#extension GL_EXT_scalar_block_layout : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require

layout(set = 0, binding = 0) buffer Instances{
    uint64_t refs[];
};

layout(set = 0, binding = 1) uniform sampler2D textures[];

layout(buffer_reference, scalar) buffer Indices{
    uint i[];
};

void main(){
    uint64_t ref = refs[gl_InstanceCustomIndexEXT];

    Indices indices = Indices(refs[0]);
}

Using the GL_EXT_buffer_reference2 extension. The code is compiled to the spirv1.5 spec which gives this spriv code for the "textures " bindless samplers:

OpName %textures "textures"
OpDecorate %_runtimearr_ulong ArrayStride 8
OpMemberDecorate %Instances 0 Offset 0
OpDecorate %Instances Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %gl_InstanceCustomIndexEXT BuiltIn InstanceCustomIndexNV
OpDecorate %_runtimearr_uint ArrayStride 4
OpMemberDecorate %Indices 0 Offset 0
OpDecorate %Indices Block
OpDecorate %indices AliasedPointer
OpDecorate %textures DescriptorSet 0
OpDecorate %textures Binding 1

With the only difference beeing this:

OpDecorate %Indices Block
OpDecorate %indices AliasedPointer

But spirq does not recognize the "textures" descriptor on reflection if this line exists:

Indices indices = Indices(refs[0]);

Debug from spriq entry points:
With the line:

    main {
        exec_model: ClosestHitNV,
        name: "main",
        vars: [
            Descriptor {
                name: None,
                desc_bind: (set=0, bind=0),
                desc_ty: StorageBuffer(
                    ReadWrite,
                ),
                ty: Instances { refs: [u64] },
                nbind: 1,
            },
        ],
        exec_modes: [],
    },

Without the line:

    main {
        exec_model: ClosestHitNV,
        name: "main",
        vars: [
            Descriptor {
                name: None,
                desc_bind: (set=0, bind=0),
                desc_ty: StorageBuffer(
                    ReadWrite,
                ),
                ty: Instances { refs: [u64] },
                nbind: 1,
            },
            Descriptor {
                name: Some(
                    "textures",
                ),
                desc_bind: (set=0, bind=1),
                desc_ty: CombinedImageSampler,
                ty: sampler2D<f32>,
                nbind: 1,
            },
        ],
        exec_modes: [],
    },

Thanks for any help in advance.

"CorruptedSpirv: cannot find a suitable type" error when structure contains array of structures

Here is an example of a shader code (compute shader in my case):

#version 450

layout (set=0, binding=0, rgba8) writeonly uniform image2D frame;

struct Object {
    vec3 position;
    uint type;
    vec4 rotation;
    vec3 half_size;
    uint dummy;
};

struct Scene {
    uint object_count;
    Object object; // this works fine
//    Object objects[10]; // this causes an error: "cannot find a suitable type"
};

void main() {
    const ivec2 xy = gl_GlobalInvocationID.xy;

    Scene scene;
    scene.object_count = 5;

    vec4 pixel = vec4(0, 0, 0, 1);
    pixel.r = scene.object_count * 0.1;

    imageStore(frame, xy, pixel);
}

Contents of the main() function are not so important, please pay attention to the Scene structure - it contains a uint member and another member that is either structure (Object) or an array of structures (Object[10]).

When Scene contains just a structure, the shader is reflected correctly. But when using an array of structures, SpirvBinary::reflect() returns an error: "CorruptedSpirv: cannot find a suitable type".

Can we fix that?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.