typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
88 lines (87 loc) • 3.01 kB
JavaScript
import { createIoSchema } from "../core/function/ioSchema.js";
import { validateProp } from "../nameUtils.js";
import { getName, setName } from "../shared/meta.js";
import { $internal, $repr, $resolve } from "../shared/symbols.js";
/**
* A requirement for the generated struct is that non-builtin properties need to
* have the same name in WGSL as they do in JS. This allows locations to be properly
* matched between the Vertex output and Fragment input.
*/
export class AutoStruct {
// ---
/**
* js key -> data type
*/
#validProps;
/**
* js key -> { prop: 'wgsl key', type: ... }
* @example '$position' -> { prop: 'position', type: ... }
*/
#allocated;
#usedWgslKeys;
#locations;
#cachedStruct;
#typeForExtraProps;
constructor(validProps, typeForExtraProps, locations) {
this.#validProps = validProps;
this.#typeForExtraProps = typeForExtraProps;
this.#allocated = {};
this.#locations = locations;
this.#usedWgslKeys = new Set();
}
/**
* Used for accessing builtins, varying and attributes in code.
*/
accessProp(key) {
// If the prop is not found in validProps, we consider it an extra property
const dataType = this.#validProps[key] ?? this.#typeForExtraProps;
if (!dataType) {
return undefined;
}
return this.provideProp(key, dataType);
}
/**
* Used for providing new varyings.
*
* @privateRemarks
* Internally used by `accessProp`.
*/
provideProp(key, dataType) {
let alloc = this.#allocated[key];
if (!alloc) {
const wgslKey = key.replaceAll('$', '');
if (this.#usedWgslKeys.has(wgslKey)) {
throw new Error(`Property name '${wgslKey}' causes naming clashes. Choose a different name.`);
}
const result = validateProp(wgslKey);
if (!result.success) {
throw new Error(`Invalid property key '${key}'${result.error ? `: ${result.error}` : ''}`);
}
this.#usedWgslKeys.add(wgslKey);
alloc = { prop: wgslKey, type: dataType };
this.#allocated[key] = alloc;
}
return alloc;
}
get completeStruct() {
if (!this.#cachedStruct) {
this.#cachedStruct = createIoSchema(Object.fromEntries(Object.values(this.#allocated).map((alloc) => {
return [alloc.prop, alloc.type];
})), this.#locations);
const ownName = getName(this);
// Passing the given name forward
if (ownName) {
setName(this.#cachedStruct, ownName);
}
}
return this.#cachedStruct;
}
[$resolve](ctx) {
return ctx.resolve(this.completeStruct);
}
toString() {
return `auto-struct:${getName(this) ?? '<unnamed>'}`;
}
}
AutoStruct.prototype[$internal] = {};
AutoStruct.prototype.type = 'auto-struct';