gfx-rs / naga

Universal shader translation in Rust

Home Page:https://github.com/gfx-rs/wgpu/tree/trunk/naga

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[wgsl-in] Why can't I use mat2x2<f32> as an instance input?

AndriBaal opened this issue · comments

I have a shader with a mat2x2 as a part of my instance:

struct Camera {
    view_proj: mat4x4<f32>
}

@group(0) @binding(0)
var<uniform> camera: Camera;

struct VertexInput {
    @location(0) v_position: vec2<f32>,
    @location(1) tex: vec2<f32>,
}

struct InstanceInput {
    @location(2) i_position: vec2<f32>,
    @location(3) rotation: mat2x2<f32>,
}

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) tex: vec2<f32>,
}

@vertex
fn main(
    model: VertexInput,
    instance: InstanceInput,
) -> VertexOutput {
    var out: VertexOutput;

    let pos = model.v_position * instance.rotation + instance.i_position;
    out.clip_position = camera.view_proj * vec4<f32>(pos, 0.0, 1.0);
    out.tex = model.tex;

    return out;
}

However, this is not possible because I receive the following error:

thread 'main' panicked at 'wgpu error: Validation Error

Caused by:
    In Device::create_shader_module
      note: label = `sprite_sheet`
    
Shader validation error: 
   ┌─ sprite_sheet:15:1
   │  
15 │ ╭ struct InstanceInput {
16 │ │     @location(2) i_position: vec2<f32>,
17 │ │     @location(3) rotation: mat2x2f,
18 │ │     
19 │ │     @location(4) sprite: u32,
   │ ╰────────────────────────────^ naga::Type [7]


    Entry point main at Vertex is invalid
    Argument 1 varying error
    The type [5] cannot be used for user-defined entry point inputs or outputs

', /home/andri/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.17.1/src/backend/direct.rs:3056:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I have developed the followng workaround:

struct Camera {
    view_proj: mat4x4<f32>
}

@group(0) @binding(0)
var<uniform> camera: Camera;

struct VertexInput {
    @location(0) v_position: vec2<f32>,
    @location(1) tex: vec2<f32>,
}

struct InstanceInput {
    @location(2) i_position: vec2<f32>,
    @location(3) rotation: vec4<f32>,
}

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) tex: vec2<f32>,
}

@vertex
fn main(
    model: VertexInput,
    instance: InstanceInput,
) -> VertexOutput {
    var out: VertexOutput;

    let pos = model.v_position * mat2x2<f32>(instance.rotation.xy, instance.rotation.zw) + instance.i_position;
    out.clip_position = camera.view_proj * vec4<f32>(pos, 0.0, 1.0);
    out.tex = model.tex;

    return out;
}

This workaround replaces the mat2x2 with a vec4 since vectors are allowed. The reason I need a mat2x2 is because I need to multiply with a vec2 which is not possible with a vec4. Later in the shader I build my matrix from the vector. My concerns are that this might be slower than just accepting the mat2x2 directly from the instance input. Is there any reason why mat2x2 is not allowed here? Or might the compiler optimize it anyway, so it does not make a difference?

It's a restriction imposed by the WGSL spec. Most likely due to the shading languages we target also not supporting matrices for vertex/instance inputs.

Or might the compiler optimize it anyway, so it does not make a difference?

It's most likely that it doesn't make a difference. Most compilers will scalarize matrices and vectors anyway.