typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
93 lines (92 loc) • 3.73 kB
JavaScript
import { UnknownData } from "../data/dataTypes.js";
import { abstractFloat, abstractInt, bool, f32, i32 } from "../data/numeric.js";
import { isRef } from "../data/ref.js";
import { isAlias, isSnippet, snip, withDataType } from "../data/snippet.js";
import { isMatInstance, isNaturallyEphemeral, isVecInstance, WORKAROUND_getSchema, } from "../data/wgslTypes.js";
import { getOwnSnippet } from "../types.js";
import { WgslTypeError } from "../errors.js";
import { $internal, $resolve } from "../shared/symbols.js";
import { logger } from "../tgpuLogger.js";
export function numericLiteralToSnippet(value) {
if (value >= 2 ** 63 || value < -(2 ** 63)) {
return snip(value, abstractFloat, /* origin */ 'constant', /* possibleSideEffects */ false);
}
// WGSL AbstractInt uses 64-bit precision, but JS numbers are only safe up to 2^53 - 1.
// Warn when values exceed this range to prevent precision loss.
if (Number.isInteger(value)) {
if (!Number.isSafeInteger(value)) {
logger.warn('precision-loss', `The integer ${value} exceeds the safe integer range and may have lost precision.`);
}
return snip(value, abstractInt, /* origin */ 'constant', /* possibleSideEffects */ false);
}
return snip(value, abstractFloat, /* origin */ 'constant', /* possibleSideEffects */ false);
}
export function concretize(type) {
if (type.type === 'abstractFloat') {
return f32;
}
if (type.type === 'abstractInt') {
return i32;
}
return type;
}
export function concretizeSnippet(snippet) {
return withDataType(concretize(snippet.dataType), snippet);
}
export function concretizeSnippets(args) {
return args.map(concretizeSnippet);
}
export function coerceToSnippet(value) {
if (isSnippet(value)) {
// Already a snippet
return value;
}
if (isRef(value)) {
throw new Error('Cannot use refs (d.ref(...)) from the outer scope.');
}
// Maybe the value can tell us what snippet it is
const ownSnippet = getOwnSnippet(value);
if (ownSnippet) {
return ownSnippet;
}
if (isVecInstance(value) || isMatInstance(value)) {
return snip(value, WORKAROUND_getSchema(value),
/* origin */ 'constant',
/* possibleSideEffects */ false);
}
if (typeof value === 'number') {
return numericLiteralToSnippet(value);
}
if (typeof value === 'boolean') {
return snip(value, bool, /* origin */ 'constant', /* possibleSideEffects */ false);
}
return snip(value, UnknownData, /* origin */ 'constant', /* possibleSideEffects */ false);
}
/**
* Intermediate representation for WGSL array expressions.
* Defers resolution. Stores array elements as snippets so the
* generator can access them when needed.
*/
export class ArrayExpression {
[$internal] = true;
type;
elements;
constructor(type, elements) {
this.type = type;
this.elements = elements;
}
toString() {
return 'ArrayExpression';
}
[$resolve](ctx) {
for (const elem of this.elements) {
// We check if there are no references among the elements
if (isAlias(elem) && !isNaturallyEphemeral(elem.dataType)) {
const snippetStr = ctx.resolveSnippet(elem).value;
const snippetType = ctx.resolve(concretize(elem.dataType)).value;
throw new WgslTypeError(`'${snippetStr}' reference cannot be used in an array constructor.\n-----\nTry '${snippetType}(${snippetStr})' or 'arrayOf(${snippetType}, count)([...])' to copy the value instead.\n-----`);
}
}
return ctx.gen.typeInstantiation(this.type, this.elements);
}
}