UNPKG

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
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';