brendan-duncan / wgsl_reflect

A WebGPU Shading Language parser and reflection library for Javascript.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for var usage by entry

greggman opened this issue · comments

AFAICT, right now, entry is EntryFunctions as defined below.

class FunctionInfo {
    constructor(name, stage = null) {
        this.stage = null;
        this.inputs = [];
        this.outputs = [];
        this.name = name;
        this.stage = stage;
    }
}
class EntryFunctions {
    constructor() {
        this.vertex = [];
        this.fragment = [];
        this.compute = [];
    }
}

It would be useful if each entry point listed which bindings it uses

For example

@group(0) @binding(0) var<uniform> u1: vec4f;
@group(0) @binding(1) var<uniform> u2: vec4f;
@group(0) @binding(1) var<uniform> u3: vec4f;

@vertex fn vs1() -> @builtin(position) vec4f {
  return u1;
}

@vertex fn vs2() -> @builtin(position) vec4f {
  return u2;
}

@vertex fn vs3() -> @builtin(position) vec4f {
  return u1 + u2;
}

@vertex fn vs4() -> @builtin(position) vec4f {
  _ = u1;
  _ = u2;
  return vec4f(0);
}

@vertex fn vs5() -> @builtin(position) vec4f {
  return u1 + u3;
}

It would be nice if some where it told me

  • vs1 uses u1
  • vs2 uses u2
  • vs3 uses u1 and u2
  • vs4 uses u1 and u2
  • vs5 uses u1 and u3

etc... where "uses" is the storage, uniform, sampler, texture info. Then I could generate GPUBindGroupLayoutDescription from the info.

As it is now I don't see that info. I can see this is not entirely trivial since the usage might not appear until multiple function calls and aliases etc make it more complicated. Right now I can guess that all uniforms, samplers, textures, storage are used but that wouldn't work for more complicated cases

I can look into that. I agree it won't be trivial. When you say aliasing be an issue, can you give an example of how aliasing would? I would look to see if a resource variable is used in an expression in an entry function, and also look at any function calls made in that entry function and check the expressions in those functions recursively. If there are some edge cases I'm not thinking about, it would be helpful to know about those.

Came up with another edge case to handle, variable shadowing. I'll have to make sure the variable in the expression is the module scoped resource and not some local variable.

Does the spec say anything about optimization of unused variables? I'm guessing not (and don't see anything). In the case of vs4, the uniforms are referenced but not used. So should the reflection say they are used or not? It would certainly be easier to say they are and not track the expression to something that writes to an output.

Managed to get in a first draft before having to go out for family new years stuff. FunctionInfo has resources, as an array of VarInfo, for the resoruces used in that function.

const reflect = new WgslReflect(`
      @group(0) @binding(0) var<uniform> u1: vec4f;
      @group(0) @binding(1) var<uniform> u2: vec4f;
      @group(0) @binding(1) var<uniform> u3: vec4f;

      // resources [u1]
      @vertex fn vs1() -> @builtin(position) vec4f {
        return u1;
      }

      // resources [u1, u2]
      @vertex fn vs2() -> @builtin(position) vec4f {
        var v1 = u1 + u2;
        return v1;
      }

      fn getU3() -> vec4f {
        return u3;
      }
      
      // resources [u1, u2, u3]
      @vertex fn vs3() -> @builtin(position) vec4f {
        return u1 + u2 + getU3();
      }

      // resources [u1, u3]. u2 is shadowed by a local variable.
      @vertex fn vs4() -> @builtin(position) vec4f {
        var u2 = vec4f(0);
        return u1 + u2 + getU3();
      }

      // resources [u1, u3]. u2 is shadowed by a local variable.
      @vertex fn vs5() -> @builtin(position) vec4f {
        var u2 = u1;
        return u2 + getU3();
      }
      
      // resources [u1, u2]
      @vertex fn vs6() -> @builtin(position) vec4f {
        {
          var u1 = vec4f(0);
        }
        _ = u1;
        _ = u2;
        return vec4f(0);
      }`);
    test.equals(reflect.entry.vertex.length, 6);
    
    test.equals(reflect.entry.vertex[0].resources.length, 1);
    test.equals(reflect.entry.vertex[0].resources[0].name, "u1");
    test.equals(reflect.entry.vertex[0].resources[0].type.name, "vec4f");
    test.equals(reflect.entry.vertex[0].resources[0].size, 16);

    test.equals(reflect.entry.vertex[1].resources.length, 2);

    test.equals(reflect.entry.vertex[2].resources.length, 3);

    test.equals(reflect.entry.vertex[3].resources.length, 2);

    test.equals(reflect.entry.vertex[4].resources.length, 2);

    test.equals(reflect.entry.vertex[5].resources.length, 2);

wow! that was fast!

When you say aliasing be an issue, can you give an example of how aliasing would?

Looking again, I don't think there are any alias issues. I haven't used alias but apparently they only alias types so it seems like they wouldn't affect this.

Does the spec say anything about optimization of unused variables?

For variables the ones that take a @group @binding, yes, the spec requires them to be used to show up as binding in an auto bind group layout.

In other words

@group(0) @binding(0) var<uniform> u1: vec4f;
@group(0) @binding(1) var<uniform> u2: vec4f;
@group(0) @binding(2) var<uniform> u3: vec4f;

@compute fs cs() {
  _ = u3;
}

If you make a pipeline from 'cs' tried to use it with an auto layout bindGroupLayout it would only require a uniform buffer bound to @group(0) @binding(2). It would not require or use u2 or u1.

This is also valid

@group(0) @binding(0) var<uniform> u1: vec4f; // <-- same binding
@group(0) @binding(0) var<uniform> u2: vec4f; // <-- same binding
@group(0) @binding(0) var<uniform> u3: vec4f; // <-- same binding

@compute fs cs1() {
  _ = u3;
}

@compute fs cs2() {
  _ = u2;
}

Above all 3 variables are declared to use the same group and binding but it's fine as long as only one of them is used. cs1 only uses u3 and cs2 only uses u2 so it will work.

I pushed the update to git. I'll fix whatever's broken next year 🙂

I'll close this, and we can open new issues if there are bugs with it.