brendan-duncan / wgsl_reflect

A WebGPU Shading Language parser and reflection library for Javascript.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add support for const

greggman opened this issue · comments

Example:

          @vertex fn vs(
            @builtin(vertex_index) VertexIndex : u32
          ) -> @builtin(position) vec4<f32> {
            var pos = array<vec2<f32>, 3>(
              vec2(0.0, 0.5),
              vec2(-0.5, -0.5),
              vec2(0.5, -0.5)
            );

            return vec4(pos[VertexIndex], 0.0, 1.0);
          }

          const FOO = radians(90);
          const BAR = sin(FOO);
          const NUM_COLORS = u32(BAR + 3); // result 4
          @group(0) @binding(0) var<uniform> uni: array<vec4f, NUM_COLORS>;

          @fragment fn fs() -> @location(0) vec4<f32> {
            return uni[NUM_COLORS - 1];
          }

works: https://jsgist.org/?src=bcebe5c22fc9ed9ecc18f46fdf986ffc

I get that this is non-trivial as you actually have to do the math and that includes all of the math functions :(

Just an idea, it's probably not so common to do much math on const so maybe in order of priorities

  1. handle single numbers as in const NUM_LIGHTS = 12;
  2. handle operators as in const NUM_LIGHTS = 10 + 2;
  3. handle functions as in const NUM_LIGHTS = u32(sin(radians(90)) * 10 + 2;

I wonder if I can figure out 1.

Thanks. It shouldn't be too bad to evaluate the const AST to reduce it to a number or string, though it would be an annoying number of functions to implement assuming all the built-ins could be in const expressions (even if some like refract are highly unlikely).

But I won't have time to look into it for at least several weeks since I've been really busy with work.

So much for not having to write a preprocessor :-)

Hmmm.... I'm not sure how I'll deal with const-functions. Evaluating a const expression doesn't seem to bad, but to evaluate a const function seems more complicated. I suppose I could leave that unsupported for now.

WGSL got more complicated with the recent spec changes.

Oh, nevermind, "The const attribute cannot be applied to user-declared functions." False alarm.

I'm picking away at it whenever I get a free moment. I pushed some basic const handling. Still needs the rest of the WGSL const functions to evaluate, I just have basic trig functions in there currently. Also need to go through and find any other places const's should be evaluated. I currently added it for array counts.

I added a test for

const FOO = radians(90);
const BAR = sin(FOO);
const NUM_COLORS = u32(BAR + 3); // result 4
@group(0) @binding(0) var<uniform> uni: array<vec4f, NUM_COLORS>;

And reflect.getUniformBufferInfo(reflect.uniforms[0]) says the buffer size is 64.

Awesome Brendan!

I started writing some tests and looking at example. I cobbled this WGSL snippet together which shows some more issues

            alias foo = u32;
            alias bar = foo;

            struct Vehicle {
              num_wheels: bar,
              mass_kg: f32,
            }

            alias Car = Vehicle;

            struct Ship {
                cars: array<Car, 4>,
            };

            const a_bicycle = Car(2, 10.5);
            const bike_num_wheels = a_bicycle.num_wheels;

            struct Ocean {
                things: array<Ship, a_bicycle.num_wheels>,
            };

This appears to compile in WGSL but not the parser. I didn't know that you can create a structure at runtime and it gets an automatic constructor which you can use as a const.

if you're curious that example started here: https://google.github.io/tour-of-wgsl/types/structures/

I'm still picking away at it. Added some basic const struct handling, so the above example evaluates now.

    alias foo = u32;
    alias bar = foo;
    struct Vehicle {
      num_wheels: bar,
      mass_kg: f32,
    }
    alias Car = Vehicle;
    const num_cars = 2 * 2;
    struct Ship {
        cars: array<Car, num_cars>,
    };
    const a_bicycle = Car(2, 10.5);
    const bike_num_wheels = a_bicycle.num_wheels;
    struct Ocean {
        things: array<Ship, a_bicycle.num_wheels>,
    };
    @group(0) @binding(0) var<uniform> ocean: Ocean;

with

    const bufferInfo = reflect.getUniformBufferInfo(reflect.uniforms[0]);
    test.equals(bufferInfo.size, 64);

I feel like the code is getting a bit messy as I add in all these const and alias cases. At some point I'll do a clean up pass. And I still need to go through and make sure it's evaluating consts in all the places it needs to, and add the rest of the const functions.

Also would love to see support for consts; I've got some switch-cases that don't work without replacing the consts manually sadly.

If you have an example that isn't parsing correctly, please let me know what it is.

Here's a cut-down version of what I'm trying to parse:

alias material_type = u32;
const MATERIAL_TYPE_LAMBERTIAN : material_type = 0;
const MATERIAL_TYPE_METAL : material_type = 1;
const MATERIAL_TYPE_DIELECTRIC : material_type = 2;

fn scatter(ty: material_type) -> bool {
    switch (ty) {
        case MATERIAL_TYPE_LAMBERTIAN {
            return true;
        }
        case MATERIAL_TYPE_METAL {
            return false;
        }
        case MATERIAL_TYPE_DIELECTRIC {
            return true;
        }
        default {
            return false;
        }
    }
}

Thanks. I'll get it fixed up as soon as I can.

@edelmanjm I pushed a fix for switch case when it has a const expression.