UNPKG

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
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); } }