Search code examples
webgpuwgsl

How can I declare and use a *constant* array in a WGSL vertex shader?


Background

I'm trying to render a single triangle by encoding the vertices directly in my WGSL vertex shader.

My idea was to have a global constant array, TRI_VERTICES contain the vertices of the triangle, from which I will look up the appropriate vertex coordinates using the builtin vertex_index.

let TRI_VERTICES: array<vec4<f32>, 3> = array<vec4<f32>, 3>(
  vec4<f32>(0., 0., 0., 1.0),
  vec4<f32>(0., 1., 0., 1.0),
  vec4<f32>(1., 1., 0., 1.0),
);

@vertex
fn vs_main(
  @builtin(vertex_index) in_vertex_index: u32,
) -> @builtin(position) vec4<f32> {
  return TRI_VERTICES[in_vertex_index];
}

@fragment
fn fs_main(@builtin(position) in: vec4<f32>) -> @location(0) vec4<f32> {
  return vec4<f32>(in.x, in.y, 0.1, 1.0);
}

I am running a draw call (in Rust and wgpu) using 3 vertices and 1 instance as follows:

render_pass.draw(0..3, 0..1);

Unfortunately, I get the following error:

Shader validation error:
   ┌─ Shader:13:9
   │
13 │   return TRI_VERTICES[in_vertex_index];
   │         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ naga::Expression [3]


    Entry point vs_main at Vertex is invalid
    Expression [3] is invalid
    The expression [1] may only be indexed by a constant

Question

The above seems easily fixed if I just change let TRI_VERTICES to var<private> TRI_VERTICES, but I'm not sure if this is the "correct" solution. What I would like to know is:

  • Does using var<private> mean that the array entries are mutable within vs_main?
  • If so, is there a way to declare TRI_VERTICES as "constant" somehow?
  • What is the most appropriate way to declare TRI_VERTICES?

Solution

  • The following compiles correctly in Tint. This maybe a case of Naga needing to catchup to spec changes, you can file Naga issues at https://github.com/gfx-rs/naga.

    const TRI_VERTICES = array(
      vec4(0., 0., 0., 1.),
      vec4(0., 1., 0., 1.),
      vec4(1., 1., 0., 1.),
    );
    
    @vertex
    fn vs_main(
      @builtin(vertex_index) in_vertex_index: u32,
    ) -> @builtin(position) vec4<f32> {
      return TRI_VERTICES[in_vertex_index];
    }
    
    @fragment
    fn fs_main(@builtin(position) in: vec4<f32>) -> @location(0) vec4<f32> {
      return vec4(in.x, in.y, .1, 1);
    }
    

    To answer the questions:

    1. A var<private> is mutable in the vertex shader but only visible to the current invocation.

    2. The new const keyword is the new constant.

    3. The spec allows dropping most of the type annotations, so declaring as above should be sufficient.