144 lines
3.5 KiB
JavaScript
144 lines
3.5 KiB
JavaScript
|
|
export class WGSLReflection {
|
||
|
|
|
||
|
|
constructor(wgslSource) {
|
||
|
|
|
||
|
|
this.src = wgslSource;
|
||
|
|
this.structs = {};
|
||
|
|
this.bindings = [];
|
||
|
|
|
||
|
|
this.parseStructs();
|
||
|
|
this.parseBindings();
|
||
|
|
}
|
||
|
|
|
||
|
|
//
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// STRUCT PARSING
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
//
|
||
|
|
parseStructs() {
|
||
|
|
|
||
|
|
const structRegex =
|
||
|
|
/struct\s+(\w+)\s*\{([^}]+)\}/g;
|
||
|
|
|
||
|
|
let match;
|
||
|
|
while ((match = structRegex.exec(this.src))) {
|
||
|
|
|
||
|
|
const structName = match[1];
|
||
|
|
const body = match[2].trim();
|
||
|
|
|
||
|
|
const fields = this.parseStructFields(body);
|
||
|
|
const size = fields.reduce((sum, f) => sum + f.size, 0);
|
||
|
|
|
||
|
|
this.structs[structName] = {
|
||
|
|
name: structName,
|
||
|
|
fields,
|
||
|
|
size
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
parseStructFields(body) {
|
||
|
|
|
||
|
|
const lines = body.split("\n");
|
||
|
|
const fields = [];
|
||
|
|
|
||
|
|
for (let line of lines) {
|
||
|
|
|
||
|
|
line = line.trim();
|
||
|
|
if (!line) continue;
|
||
|
|
|
||
|
|
const m = line.match(/(\w+)\s*:\s*([^;]+);/);
|
||
|
|
if (!m) continue;
|
||
|
|
|
||
|
|
const fieldName = m[1];
|
||
|
|
const wgslType = m[2].trim();
|
||
|
|
const size = this.computeTypeSize(wgslType);
|
||
|
|
|
||
|
|
fields.push({ fieldName, wgslType, size });
|
||
|
|
}
|
||
|
|
|
||
|
|
return fields;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// BINDING PARSING
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
//
|
||
|
|
parseBindings() {
|
||
|
|
|
||
|
|
const varRegex =
|
||
|
|
/@group\((\d+)\)\s*@binding\((\d+)\)\s*var<([^>]+)>\s+(\w+)\s*:\s*([^;]+);/g;
|
||
|
|
|
||
|
|
let match;
|
||
|
|
while ((match = varRegex.exec(this.src))) {
|
||
|
|
|
||
|
|
const group = parseInt(match[1]);
|
||
|
|
const binding = parseInt(match[2]);
|
||
|
|
const type = match[3].trim();
|
||
|
|
const varName = match[4].trim();
|
||
|
|
const varType = match[5].trim();
|
||
|
|
|
||
|
|
const size = this.computeTypeSize(varType);
|
||
|
|
|
||
|
|
this.bindings.push({
|
||
|
|
group,
|
||
|
|
binding,
|
||
|
|
type,
|
||
|
|
varName,
|
||
|
|
varType,
|
||
|
|
size
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// TYPE SIZE COMPUTATION (Simplified)
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
//
|
||
|
|
computeTypeSize(wgslType) {
|
||
|
|
|
||
|
|
// scalar
|
||
|
|
if (wgslType === "f32" || wgslType === "u32" || wgslType === "i32") {
|
||
|
|
return 4;
|
||
|
|
}
|
||
|
|
|
||
|
|
// array<f32, N>
|
||
|
|
const arrMatch = wgslType.match(/array<(\w+),\s*(\d+)>/);
|
||
|
|
if (arrMatch) {
|
||
|
|
const elementType = arrMatch[1];
|
||
|
|
const count = parseInt(arrMatch[2]);
|
||
|
|
return this.computeTypeSize(elementType) * count;
|
||
|
|
}
|
||
|
|
|
||
|
|
// array<f32> runtime-sized → minimal
|
||
|
|
const runtimeArr = wgslType.match(/array<(\w+)>/);
|
||
|
|
if (runtimeArr) {
|
||
|
|
return 4;
|
||
|
|
}
|
||
|
|
|
||
|
|
// struct
|
||
|
|
if (this.structs[wgslType]) {
|
||
|
|
return this.structs[wgslType].size;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 4;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// SUMMARY OUTPUT
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
//
|
||
|
|
reflect() {
|
||
|
|
return {
|
||
|
|
structs: this.structs,
|
||
|
|
bindings: this.bindings
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|