UNPKG

typegpu

Version:

A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.

79 lines (78 loc) 3.58 kB
import { createShelllessImpl } from "../core/function/shelllessImpl.js"; import { UnknownData } from "../data/dataTypes.js"; import { RefOperator } from "../data/ref.js"; import { isPtr, isWgslArray, isWgslStruct } from "../data/wgslTypes.js"; import { WgslTypeError } from "../errors.js"; import { getFunctionMetadata, getName } from "../shared/meta.js"; import { concretize } from "./generationHelpers.js"; function shallowEqualSchemas(a, b) { if (a.type !== b.type) return false; if (isPtr(a) && isPtr(b)) { return (a.access === b.access && a.addressSpace === b.addressSpace && a.implicit === b.implicit && shallowEqualSchemas(a.inner, b.inner)); } if (isWgslArray(a) && isWgslArray(b)) { return a.elementCount === b.elementCount && shallowEqualSchemas(a.elementType, b.elementType); } if (isWgslStruct(a) && isWgslStruct(b)) { // Only structs with the same identity are considered equal return a === b; } return true; } export class ShelllessRepository { cache = new Map(); get(fn, argSnippets) { const meta = getFunctionMetadata(fn); if (!meta) { return undefined; } if (!argSnippets && meta.ast.params.length > 0) { throw new Error(`Cannot resolve '${getName(fn)}' directly, because it expects arguments. Either call it from another function, or wrap it in a shell`); } const argTypes = (argSnippets ?? []).map((s, index) => { if (s.value instanceof RefOperator) { if (s.dataType === UnknownData) { throw new WgslTypeError(`d.ref() created with primitive types must be stored in a variable before use`); } return s.dataType; } if (s.dataType === UnknownData) { throw new Error(`Passed illegal value ${s.value} as the #${index} argument to ${getName(fn) ?? '<unnamed>'}(...)\n` + `Shellless functions can only accept arguments representing WGSL resources: constructible WGSL types, d.refs, samplers or texture views.\n` + `Remember, that arguments such as samplers, texture views, accessors, slots etc. should be dereferenced via '.$' first.`); } let type = concretize(s.dataType); if (isPtr(type) && type.implicit) { // If the pointer was made implicitly (e.g. by assigning a reference to a const variable), // then we dereference the pointer before passing it to the function. The main reason for this, // is that in TypeScript, the type of the function accepts a value, not the value wrapped in // d.ref<> (so it's not considered mutable from the perspective of the function) // Example: // const foo = layout.$.boids; // bar(foo) // ^^^ type = type.inner; } return type; }); let cache = this.cache.get(fn); if (cache) { const variant = cache.find((v) => v.argTypes.length === argTypes.length && v.argTypes.every((t, i) => shallowEqualSchemas(t, argTypes[i]))); if (variant) { return variant; } } else { cache = []; this.cache.set(fn, cache); } const shellless = createShelllessImpl(argTypes, fn); cache.push(shellless); return shellless; } }