WebGPU Shading Language Reflection Library
A WebGPU Shading Language parser and reflection library for Typescript and Javascript.
wgsl_reflect can parse a WGSL shader and analyze its contents, providing information about the shader. It can determine the bind group layout of the shader, resource bindings, uniform buffers, the members of a uniform buffer, their names, types, sizes, offsets into the buffer.
Usage
From NPM
npm install wgsl_reflect
The wgsl_reflect.module.js file is a self-contained roll-up of the library that can be included in your project and imported with:
import { WgslReflect } from "wgsl_reflect/wgsl_reflect.module.js";
const reflect = new WgslReflect(shader_code);
Example
WGSL Reflect Example
Documentation
class WgslReflect {
uniforms: Array<VariableInfo>;
storage: Array<VariableInfo>;
textures: Array<VariableInfo>;
samplers: Array<VariableInfo>;
aliases: Array<AliasInfo>;
overrides: Array<OverrideInfo> = [];
structs: Array<StructInfo>;
entry: EntryFunctions;
functions: Array<FunctionInfo>;
findResource(group: number, binding: number): VariableInfo | null;
getBindGroups(): Array<Array<VariableInfo>>;
}
enum ResourceType {
Uniform,
Storage,
Texture,
Sampler,
StorageTexture
}
class VariableInfo {
name: string;
type: TypeInfo;
group: number;
binding: number;
resourceType: ResourceType;
access: string;
get isArray(): boolean;
get isStruct(): boolean;
get isTemplate(): boolean;
get size(): number;
get align(): number;
get members(): Array<MemberInfo> | null;
get format(): TypeInfo | null;
get count(): number;
get stride(): number;
}
class TypeInfo {
name: string;
size: number;
get isArray(): boolean;
get isStruct(): boolean;
get isTemplate(): boolean;
}
class StructInfo extends TypeInfo {
members: Array<MemberInfo>;
align: number;
startLine: number;
endLine: number;
inUse: boolean;
}
class ArrayInfo extends TypeInfo {
format: TypeInfo;
count: number;
stride: number;
}
class TemplateInfo extends TypeInfo {
format: TypeInfo;
access: string;
}
class MemberInfo {
name: string;
type: TypeInfo;
offset: number;
size: number;
get isArray(): boolean;
get isStruct(): boolean;
get isTemplate(): boolean;
get align(): number;
get members(): Array<MemberInfo> | null;
get format(): TypeInfo | null;
get count(): number;
get stride(): number;
}
class AliasInfo {
name: string;
type: TypeInfo;
}
class EntryFunctions {
vertex: Array<FunctionInfo>;
fragment: Array<FunctionInfo>;
compute: Array<FunctionInfo>;
}
class FunctionInfo {
name: string;
stage: string | null;
inputs: Array<InputInfo>;
outputs: Array<OutputInfo>;
arguments: Array<ArgumentInfo>;
returnType: TypeInfo | null;
resources: Array<VariableInfo>;
startLine: number;
endLine: number;
inUse: boolean;
calls: Set<FunctionInfo>;
}
class InputInfo {
name: string;
type: TypeInfo | null;
locationType: string;
location: number | string;
interpolation: string | null;
}
class OutputInfo {
name: string;
type: TypeInfo | null;
locationType: string;
location: number | string;
}
class OverrideInfo {
name: string;
type: TypeInfo | null;
id: number;
}
class ArgumentInfo {
name: string;
type: TypeInfo;
}
Examples
Calculate the bind group information in the shader:
import { WgslReflect } from "./wgsl_reflect.module.js";
const shader = `
struct ViewUniforms {
viewProjection: mat4x4<f32>
}
struct ModelUniforms {
model: mat4x4<f32>,
color: vec4<f32>,
intensity: f32
}
@binding(0) @group(0) var<uniform> viewUniforms: ViewUniforms;
@binding(1) @group(0) var<uniform> modelUniforms: ModelUniforms;
@binding(2) @group(0) var u_sampler: sampler;
@binding(3) @group(0) var u_texture: texture_2d<f32>;
struct VertexInput {
@location(0) a_position: vec3<f32>,
@location(1) a_normal: vec3<f32>,
@location(2) a_color: vec4<f32>,
@location(3) a_uv: vec2<f32>
}
struct VertexOutput {
@builtin(position) Position: vec4<f32>,
@location(0) v_position: vec4<f32>,
@location(1) v_normal: vec3<f32>,
@location(2) v_color: vec4<f32>,
@location(3) v_uv: vec2<f32>
}
@vertex
fn main(input: VertexInput) -> VertexOutput {
var output: VertexOutput;
output.Position = viewUniforms.viewProjection * modelUniforms.model * vec4<f32>(input.a_position, 1.0);
output.v_position = output.Position;
output.v_normal = input.a_normal;
output.v_color = input.a_color * modelUniforms.color * modelUniforms.intensity;
output.v_uv = input.a_uv;
return output;
}`;
const reflect = new WgslReflect(shader);
console.log(reflect.functions.length);
console.log(reflect.structs.length);
console.log(reflect.uniforms.length);
console.log(reflect.entry.vertex.length);
console.log(reflect.entry.fragment.length);
console.log(reflect.entry.compute.length);
console.log(reflect.entry.vertex[0].name);
console.log(reflect.entry.vertex[0].resources.length);
console.log(reflect.entry.vertex[0].resources[0].name);
console.log(reflect.entry.vertex[0].resources[1].name);
console.log(reflect.entry.vertex[0].inputs.length);
console.log(reflect.entry.vertex[0].inputs[0].name);
console.log(reflect.entry.vertex[0].inputs[0].location);
console.log(reflect.entry.vertex[0].inputs[0].locationType);
console.log(reflect.entry.vertex[0].inputs[0].type.name);
console.log(reflect.entry.vertex[0].inputs[0].type.format.name);
const groups = reflect.getBindGroups();
console.log(groups.length);
console.log(groups[0].length);
console.log(groups[0][1].resourceType);
console.log(groups[0][1].size);
console.log(groups[0][1].members.length);
console.log(groups[0][1].members[0].name);
console.log(groups[0][1].members[0].offset);
console.log(groups[0][1].members[0].size);
console.log(groups[0][1].members[0].type.name);
console.log(groups[0][1].members[0].type.format.name);
console.log(groups[0][2].resourceType);
console.log(groups[0][3].resourceType);
console.log(groups[0][3].type.name);
console.log(groups[0][3].type.format.name);
Calculate the member information for a uniform buffer block:
import { WgslReflect } from "./wgsl_reflect.module.js";
const shader = `
struct A { // align(8) size(32)
u: f32, // offset(0) align(4) size(4)
v: f32, // offset(4) align(4) size(4)
w: vec2<f32>, // offset(8) align(8) size(8)
@size(16) x: f32 // offset(16) align(4) size(16)
}
struct B { // align(16) size(208)
a: vec2<f32>, // offset(0) align(8) size(8)
// -- implicit member alignment padding -- // offset(8) size(8)
b: vec3<f32>, // offset(16) align(16) size(12)
c: f32, // offset(28) align(4) size(4)
d: f32, // offset(32) align(4) size(4)
// -- implicit member alignment padding -- // offset(36) size(12)
@align(16) e: A, // offset(48) align(16) size(32)
f: vec3<f32>, // offset(80) align(16) size(12)
// -- implicit member alignment padding -- // offset(92) size(4)
g: @stride(32) array<A, 3>, // offset(96) align(8) size(96)
h: i32, // offset(192) align(4) size(4)
// -- implicit struct size padding -- // offset(196) size(12)
}
@group(0) @binding(0)
var<uniform> uniform_buffer: B;`;
const reflect = new WgslReflect(shader);
const u = reflect.uniforms[0];
console.log(u.size);
console.log(u.group);
console.log(u.binding);
console.log(u.members.length);
console.log(u.members[0].name);
console.log(u.members[0].offset);
console.log(u.members[0].size);
console.log(u.members[0].type.name);
console.log(u.members[0].type.format.name);
console.log(u.members[4].name);
console.log(u.members[4].offset);
console.log(u.members[4].size);