typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
65 lines (64 loc) • 2.12 kB
JavaScript
import { WgslTypeError } from "../../errors.js";
import { setName } from "../../shared/meta.js";
import { $getNameForward, $gpuCallable, $internal } from "../../shared/symbols.js";
import { coerceToSnippet } from "../../tgsl/generationHelpers.js";
import { isKnownAtComptime, NormalState } from "../../types.js";
export function isComptimeFn(value) {
return !!value?.[$internal]?.isComptime;
}
/**
* Creates a version of `func` that can called safely in a TypeGPU function to
* precompute and inject a value into the final shader code.
*
* Note how the function passed into `comptime` doesn't have to be marked with
* 'use gpu'. That's because the function doesn't execute on the GPU, it gets
* executed before the shader code gets sent to the GPU.
*
* @example
* ```ts
* const color = tgpu.comptime((int: number) => {
* const r = (int >> 16) & 0xff;
* const g = (int >> 8) & 0xff;
* const b = int & 0xff;
* return d.vec3f(r / 255, g / 255, b / 255);
* });
*
* const material = (diffuse: d.v3f): d.v3f => {
* 'use gpu';
* const albedo = color(0xff00ff);
* return albedo.mul(diffuse);
* };
* ```
*/
export function comptime(func) {
const impl = ((...args) => {
return func(...args);
});
impl.toString = () => 'comptime';
impl[$getNameForward] = func;
impl[$gpuCallable] = {
call(ctx, args) {
if (!args.every((s) => isKnownAtComptime(s))) {
throw new WgslTypeError(`Called comptime function with runtime-known values: ${args
.filter((s) => !isKnownAtComptime(s))
.map((s) => `'${s.value}'`)
.join(', ')}`);
}
ctx.pushMode(new NormalState());
try {
return coerceToSnippet(func(...args.map((s) => s.value)));
}
finally {
ctx.popMode();
}
},
};
impl.$name = (label) => {
setName(func, label);
return impl;
};
Object.defineProperty(impl, $internal, {
value: { isComptime: true },
});
return impl;
}