typegpu 0.12.0 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin.mjs CHANGED
@@ -21,8 +21,11 @@ if (major === undefined || minor === undefined) {
21
21
  */
22
22
  const semver = `^${major}.${minor}.0`;
23
23
 
24
+ /**
25
+ * @returns {Promise<number | undefined>}
26
+ */
24
27
  function asyncSpawn(...args) {
25
- return new Promise((resolve, _reject) => {
28
+ return new Promise((resolve, reject) => {
26
29
  const child = spawn(...args);
27
30
 
28
31
  child.on('exit', (code, signal) => {
@@ -34,22 +37,41 @@ function asyncSpawn(...args) {
34
37
 
35
38
  resolve(code);
36
39
  });
40
+
41
+ child.on('error', (err) => {
42
+ reject(err);
43
+ });
37
44
  });
38
45
  }
39
46
 
47
+ /**
48
+ * @param {string} label
49
+ */
50
+ function failedToRunErrHandler(label) {
51
+ return (err) => {
52
+ console.error(`Failed to run '${label}':`, err);
53
+ process.exit(1);
54
+ };
55
+ }
56
+
40
57
  (async () => {
41
- const code = await asyncSpawn('npx', [`@typegpu/cli@${semver}`, ...process.argv.slice(2)], {
42
- stdio: ['inherit', 'inherit', 'ignore'],
43
- });
58
+ const windows = process.platform === 'win32';
59
+ const npxCommand = windows ? 'npx.cmd' : 'npx';
60
+
61
+ const code = await asyncSpawn(npxCommand, [`@typegpu/cli@${semver}`, ...process.argv.slice(2)], {
62
+ stdio: 'inherit',
63
+ shell: windows, // needs to be ran through the shell on Windows
64
+ }).catch(failedToRunErrHandler(`npx @typegpu/cli@${semver}`));
44
65
 
45
66
  if (code !== 0) {
46
67
  console.warn(
47
68
  `Couldn't find @typegpu/cli version matching ${semver}, falling back to latest...`,
48
69
  );
49
70
  // Fallback to latest
50
- const code = await asyncSpawn('npx', [`@typegpu/cli@latest`, ...process.argv.slice(2)], {
71
+ const code = await asyncSpawn(npxCommand, [`@typegpu/cli@latest`, ...process.argv.slice(2)], {
51
72
  stdio: 'inherit',
52
- });
73
+ shell: windows, // needs to be ran through the shell on Windows
74
+ }).catch(failedToRunErrHandler('npx @typegpu/cli@latest'));
53
75
  process.exit(code ?? 0);
54
76
  }
55
77
 
@@ -8,6 +8,7 @@ import { isGPUBuffer } from "../../types.js";
8
8
  import { calculateOffsets, readFromArrayBuffer, writeToArrayBuffer } from "../../data/dataIO.js";
9
9
  import { patchArrayBuffer } from "../../data/partialIO.js";
10
10
  import { mutable, readonly, uniform, } from "./bufferBinding.js";
11
+ import { warnIfNotUniformAligned } from "../pipeline/webgpuLimitations.js";
11
12
  const usageToUsageConstructor = { uniform, mutable, readonly };
12
13
  export function INTERNAL_createBuffer(group, typeSchema, initialOrBuffer) {
13
14
  if (!isWgslData(typeSchema)) {
@@ -145,6 +146,9 @@ class TgpuBufferImpl {
145
146
  if (this.#disallowedUsages?.includes(usage)) {
146
147
  throw new Error(`Buffer of type ${this.dataType.type} cannot be used as ${usage}`);
147
148
  }
149
+ if (usage === 'uniform') {
150
+ warnIfNotUniformAligned(this.dataType);
151
+ }
148
152
  this.flags |= usage === 'uniform' ? GPUBufferUsage.UNIFORM : 0;
149
153
  this.flags |= usage === 'storage' ? GPUBufferUsage.STORAGE : 0;
150
154
  this.flags |= usage === 'vertex' ? GPUBufferUsage.VERTEX : 0;
@@ -11,7 +11,7 @@ import { isGPUCommandEncoder, isGPUComputePassEncoder, isTgpuCommandEncoder, isT
11
11
  import { isGPUBuffer } from "../../types.js";
12
12
  import { wgslEnableExtensions, wgslEnableExtensionToFeatureName } from "../../wgslExtensions.js";
13
13
  import { namespace } from "../resolve/namespace.js";
14
- import { warnIfOverflow } from "./limitsOverflow.js";
14
+ import { warnIfOverflow } from "./webgpuLimitations.js";
15
15
  import { collectBindGroupPairs, DISPATCH_INDIRECT_SIZE, resolveIndirectOffset, restoreTimestampPriors, } from "./pipelineUtils.js";
16
16
  import { invariant } from "../../errors.js";
17
17
  import { createWithPerformanceCallback, createWithTimestampWrites, } from "./timeable.js";
@@ -24,7 +24,7 @@ import { isGPUCommandEncoder, isGPURenderBundleEncoder, isGPURenderPassEncoder,
24
24
  import { createWithPerformanceCallback, createWithTimestampWrites, } from "./timeable.js";
25
25
  import { nonTransferablePriorsOf } from "./priors.js";
26
26
  import {} from "../../data/offsetUtils.js";
27
- import { warnIfOverflow } from "./limitsOverflow.js";
27
+ import { warnIfOverflow } from "./webgpuLimitations.js";
28
28
  import { collectBindGroupPairs, collectVertexBufferPairs, DRAW_INDEXED_INDIRECT_SIZE, DRAW_INDIRECT_SIZE, resolveIndirectOffset, restoreTimestampPriors, } from "./pipelineUtils.js";
29
29
  import { NullPerformanceTracker, PerformanceTrackerImpl, } from "./performanceTracker.js";
30
30
  import { logger } from "../../tgpuLogger.js";
@@ -0,0 +1,10 @@
1
+ import { type BaseData } from '../../data/wgslTypes.ts';
2
+ import type { TgpuBindGroupLayout } from '../../tgpuBindGroupLayout.ts';
3
+ /**
4
+ * Warns if layout exceeds supported buffer count limits.
5
+ */
6
+ export declare function warnIfOverflow(layouts: TgpuBindGroupLayout[], limits: GPUSupportedLimits): void;
7
+ /**
8
+ * See https://www.w3.org/TR/WGSL/#address-space-layout-constraints
9
+ */
10
+ export declare function warnIfNotUniformAligned(schema: BaseData): void;
@@ -0,0 +1,81 @@
1
+ import { alignmentOf } from "../../data/alignmentOf.js";
2
+ import { memoryLayoutOf } from "../../data/offsetUtils.js";
3
+ import { sizeOf } from "../../data/sizeOf.js";
4
+ import { isWgslArray, isWgslStruct } from "../../data/wgslTypes.js";
5
+ import { invariant } from "../../errors.js";
6
+ import { roundUp } from "../../mathUtils.js";
7
+ import { getName } from "../../shared/meta.js";
8
+ import { logger } from "../../tgpuLogger.js";
9
+ /**
10
+ * Warns if layout exceeds supported buffer count limits.
11
+ */
12
+ export function warnIfOverflow(layouts, limits) {
13
+ const entries = Object.values(layouts)
14
+ .flatMap((layout) => Object.values(layout.entries))
15
+ .filter((entry) => entry !== null);
16
+ const uniform = entries.filter((entry) => 'uniform' in entry).length;
17
+ const storage = entries.filter((entry) => 'storage' in entry).length;
18
+ if (uniform > limits.maxUniformBuffersPerShaderStage) {
19
+ logger.warn('webgpu-limits-exceeded', `Total number of uniform buffers (${uniform}) exceeds maxUniformBuffersPerShaderStage (${limits.maxUniformBuffersPerShaderStage}). Consider:
20
+ 1. Grouping some of the uniforms into one using 'd.struct',
21
+ 2. Increasing the limit when requesting a device or creating a root.`);
22
+ }
23
+ if (storage > limits.maxStorageBuffersPerShaderStage) {
24
+ logger.warn('webgpu-limits-exceeded', `Total number of storage buffers (${storage}) exceeds maxStorageBuffersPerShaderStage (${limits.maxStorageBuffersPerShaderStage}).`);
25
+ }
26
+ }
27
+ function requiredAlignOf(schema) {
28
+ if (isWgslStruct(schema) || isWgslArray(schema)) {
29
+ return roundUp(alignmentOf(schema), 16);
30
+ }
31
+ return alignmentOf(schema);
32
+ }
33
+ /**
34
+ * See https://www.w3.org/TR/WGSL/#address-space-layout-constraints
35
+ */
36
+ export function warnIfNotUniformAligned(schema) {
37
+ if (isWgslArray(schema)) {
38
+ warnIfNotUniformAligned(schema.elementType);
39
+ const stride = roundUp(sizeOf(schema.elementType), alignmentOf(schema.elementType));
40
+ if (stride % 16 !== 0) {
41
+ logger.warn('uniform-schema-misaligned', `\
42
+ Schema '${getName(schema.elementType) ?? '<unnamed>'}' is used in an array in a uniform buffer, and its stride (${stride}) is not a multiple of 16.
43
+ This is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices.
44
+ To address this, put the element schema in a struct and wrap the prop in 'd.align(16, ...)', or use a different schema like 'vec4f'.`);
45
+ }
46
+ }
47
+ if (isWgslStruct(schema)) {
48
+ Object.values(schema.propTypes).forEach(warnIfNotUniformAligned);
49
+ Object.entries(schema.propTypes).forEach(([key, value]) => {
50
+ const offset = memoryLayoutOf(schema, (schema) => schema[key]).offset;
51
+ const requiredAlignment = requiredAlignOf(value);
52
+ if (offset % requiredAlignment) {
53
+ logger.warn('uniform-schema-misaligned', `\
54
+ Schema '${getName(schema) ?? '<unnamed>'}' is used in a uniform buffer, and its property '${key}' does not meet required alignment (offset is ${offset}, required alignment is ${requiredAlignment}).
55
+ This is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices.
56
+ To address this, wrap the property '${key}' in 'd.align(${requiredAlignment}, ...)'.`);
57
+ }
58
+ });
59
+ const keys = Object.keys(schema.propTypes);
60
+ for (let i = 0; i < keys.length - 1; i++) {
61
+ const thisKey = keys[i];
62
+ const nextKey = keys[i + 1];
63
+ invariant(thisKey && nextKey);
64
+ const thisValue = schema.propTypes[thisKey];
65
+ invariant(thisValue);
66
+ if (!isWgslStruct(thisValue)) {
67
+ continue;
68
+ }
69
+ const minimumDifference = roundUp(sizeOf(thisValue), 16);
70
+ const thisKeyOffset = memoryLayoutOf(schema, (schema) => schema[thisKey]).offset;
71
+ const nextKeyOffset = memoryLayoutOf(schema, (schema) => schema[nextKey]).offset;
72
+ const difference = nextKeyOffset - thisKeyOffset;
73
+ if (minimumDifference > difference) {
74
+ logger.warn('uniform-schema-misaligned', `\
75
+ Schema '${getName(schema) ?? '<unnamed>'}' is used in a uniform buffer, and the difference between memory offsets of '${thisKey}' and '${nextKey}' props (${difference}) is less than recommended (${minimumDifference}).
76
+ This is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices.
77
+ To address this, wrap the '${thisKey}' prop in 'd.size(${minimumDifference}, ...)'.`);
78
+ }
79
+ }
80
+ }
81
+ }
package/data/array.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { type TgpuComptime } from '../core/function/comptime.ts';
2
- import type { AnyWgslData, WgslArray } from './wgslTypes.ts';
2
+ import type { AnyWgslData, Decorated, Location, WgslArray } from './wgslTypes.ts';
3
+ type ForbiddenDecoratedArrayElement<T> = T extends Decorated<infer _, infer Attribs> ? Attribs[number] extends Location ? never : T : never;
3
4
  interface WgslArrayConstructor {
5
+ /**
6
+ * @deprecated Error: Arrays cannot hold decorated types other than @location.
7
+ * Wrap align/size in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).
8
+ */
9
+ <TElement extends AnyWgslData>(elementType: ForbiddenDecoratedArrayElement<TElement>, elementCount?: number): 'Error: Arrays cannot hold decorated types other than @location. Wrap it in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).';
4
10
  <TElement extends AnyWgslData>(elementType: TElement): (elementCount: number) => WgslArray<TElement>;
5
11
  <TElement extends AnyWgslData>(elementType: TElement, elementCount: number): WgslArray<TElement>;
6
12
  }
@@ -8,6 +14,10 @@ interface WgslArrayConstructor {
8
14
  * Creates an array schema that can be used to construct gpu buffers.
9
15
  * Describes arrays with fixed-size length, storing elements of the same type.
10
16
  *
17
+ * The only decoration allowed on element types is `d.location`. Decorators like
18
+ * `d.align` and `d.size` cannot be applied directly — wrap them in a struct instead,
19
+ * e.g. `d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n)`.
20
+ *
11
21
  * @example
12
22
  * const LENGTH = 3;
13
23
  * const array = d.arrayOf(d.u32, LENGTH);
@@ -19,6 +29,7 @@ interface WgslArrayConstructor {
19
29
  *
20
30
  * @param elementType The type of elements in the array.
21
31
  * @param elementCount The number of elements in the array.
32
+ * @throws If `elementType` is decorated with anything other than `d.location`.
22
33
  */
23
34
  export declare const arrayOf: TgpuComptime<WgslArrayConstructor>;
24
35
  export {};
package/data/array.js CHANGED
@@ -2,10 +2,15 @@ import { comptime } from "../core/function/comptime.js";
2
2
  import { $internal } from "../shared/symbols.js";
3
3
  import { schemaCallWrapper } from "./schemaCallWrapper.js";
4
4
  import { sizeOf } from "./sizeOf.js";
5
+ import { isDecorated, isLocationAttrib } from "./wgslTypes.js";
5
6
  /**
6
7
  * Creates an array schema that can be used to construct gpu buffers.
7
8
  * Describes arrays with fixed-size length, storing elements of the same type.
8
9
  *
10
+ * The only decoration allowed on element types is `d.location`. Decorators like
11
+ * `d.align` and `d.size` cannot be applied directly — wrap them in a struct instead,
12
+ * e.g. `d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n)`.
13
+ *
9
14
  * @example
10
15
  * const LENGTH = 3;
11
16
  * const array = d.arrayOf(d.u32, LENGTH);
@@ -17,8 +22,12 @@ import { sizeOf } from "./sizeOf.js";
17
22
  *
18
23
  * @param elementType The type of elements in the array.
19
24
  * @param elementCount The number of elements in the array.
25
+ * @throws If `elementType` is decorated with anything other than `d.location`.
20
26
  */
21
27
  export const arrayOf = comptime(((elementType, elementCount) => {
28
+ if (isDecorated(elementType) && !elementType.attribs.every(isLocationAttrib)) {
29
+ throw new Error('Arrays cannot hold decorated types other than @location. Wrap it in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).');
30
+ }
22
31
  if (elementCount === undefined) {
23
32
  return comptime((count) => cpu_arrayOf(elementType, count));
24
33
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { Operator } from 'tsover-runtime';
2
+ import { MatBase } from "./matrix.js";
3
+ import { VecBase } from "./vectorImpl.js";
4
+ import { assignInfixOperator } from "../tgsl/infixDispatch.js";
5
+ assignInfixOperator(VecBase, 'add', Operator.plus);
6
+ assignInfixOperator(MatBase, 'add', Operator.plus);
7
+ assignInfixOperator(VecBase, 'sub', Operator.minus);
8
+ assignInfixOperator(MatBase, 'sub', Operator.minus);
9
+ assignInfixOperator(VecBase, 'mul', Operator.star);
10
+ assignInfixOperator(MatBase, 'mul', Operator.star);
11
+ assignInfixOperator(VecBase, 'div', Operator.slash);
12
+ assignInfixOperator(VecBase, 'mod', Operator.percent);
13
+ assignInfixOperator(VecBase, 'bitShiftLeft', Symbol()); // bitShift does not yet have tsover operator symbol
14
+ assignInfixOperator(VecBase, 'bitShiftRight', Symbol()); // bitShift does not yet have tsover operator symbol
@@ -0,0 +1,25 @@
1
+ import { type AnyBooleanVecInstance, type AnyMatInstance, type AnyVec2Instance, type AnyVec3Instance, type AnyVec4Instance, type AnyVecInstance, type v2b, type v3b, type v4b } from './wgslTypes.ts';
2
+ type Vec = AnyVecInstance;
3
+ type Mat = AnyMatInstance;
4
+ type Algebraic = number | boolean | Vec | Mat;
5
+ export type ToBool<T extends Algebraic> = T extends number | boolean ? boolean : T extends AnyVec2Instance ? v2b : T extends AnyVec3Instance ? v3b : T extends AnyVec4Instance ? v4b : never;
6
+ /**
7
+ * Generalizes function of 1 to 3 arguments to work component-wise on vectors and matrices.
8
+ * Assumes the types are already correct (in particular, that they have the same length),
9
+ * and performs no additional checks.
10
+ * The return type is the same as the first argument's type.
11
+ */
12
+ export declare function generalizeFn<T extends Algebraic>(fn: (a: number) => number, args: [T]): T;
13
+ export declare function generalizeFn<T extends Algebraic>(fn: (a: number, b: number) => number, args: [T, T]): T;
14
+ export declare function generalizeFn<T extends Algebraic>(fn: (a: number, b: number, c: number) => number, args: [T, T, T]): T;
15
+ /**
16
+ * Analogous to `generalizeFn`, but the return type is a boolean vector instead.
17
+ */
18
+ export declare function generalizeBoolFn<T extends Algebraic>(fn: (a: number, b: number) => boolean, args: [T, T]): ToBool<T>;
19
+ export declare function generalizeBoolFn<T extends boolean | AnyBooleanVecInstance>(fn: (a: boolean, b: boolean) => boolean, args: [T, T]): ToBool<T>;
20
+ /**
21
+ * If one of the arguments is a vector and other is a number,
22
+ * the number is up-cased to a vector.
23
+ */
24
+ export declare function upCast<T extends number | Vec>(args: [T, T]): [Exclude<T, number>, Exclude<T, number>];
25
+ export {};
@@ -0,0 +1,89 @@
1
+ import { vec2b, vec3b, vec4b, vecTypeToConstructor } from "./vector.js";
2
+ import { mat2x2f, mat3x3f, mat4x4f } from "./matrix.js";
3
+ import { isVecInstance, } from "./wgslTypes.js";
4
+ import { invariant } from "../errors.js";
5
+ const booleanFor = {
6
+ vec2f: vec2b,
7
+ vec2h: vec2b,
8
+ vec2i: vec2b,
9
+ vec2u: vec2b,
10
+ 'vec2<bool>': vec2b,
11
+ vec3f: vec3b,
12
+ vec3h: vec3b,
13
+ vec3i: vec3b,
14
+ vec3u: vec3b,
15
+ 'vec3<bool>': vec3b,
16
+ vec4f: vec4b,
17
+ vec4h: vec4b,
18
+ vec4i: vec4b,
19
+ vec4u: vec4b,
20
+ 'vec4<bool>': vec4b,
21
+ };
22
+ const constructorFor = {
23
+ ...vecTypeToConstructor,
24
+ mat2x2f,
25
+ mat3x3f,
26
+ mat4x4f,
27
+ };
28
+ function getConstructorFor(mode, kind) {
29
+ const map = mode === 'boolean' ? booleanFor : constructorFor;
30
+ if (kind in map) {
31
+ return map[kind];
32
+ }
33
+ throw new Error(`No corresponding vector/matrix type for '${kind}' kind in '${mode}' mode.`);
34
+ }
35
+ function makeIterable(item) {
36
+ if (item.kind.startsWith('vec')) {
37
+ return item;
38
+ }
39
+ return item.columns.flat();
40
+ }
41
+ function applyArgs(fn, args, mode) {
42
+ // I'm sorry, TypeScript, I swear I won't lie to you no more ;-;
43
+ const kinds = args.map(kindOf);
44
+ if (kinds.every((type) => type === 'boolean' || type === 'number')) {
45
+ return fn(...args);
46
+ }
47
+ const kind = kinds[0];
48
+ invariant(kind, `Expected kind of the first argument to be present.`);
49
+ const constructor = getConstructorFor(mode, kind);
50
+ const iterableArgs = args.map(makeIterable);
51
+ const length = iterableArgs[0]?.length;
52
+ invariant(length !== undefined, `Expected constructor to have at least one argument.`);
53
+ const constructorArgs = Array.from({ length }, (_, i) => {
54
+ const args = iterableArgs.map((arg) => arg[i]);
55
+ return fn(...args);
56
+ });
57
+ return constructor(...constructorArgs);
58
+ }
59
+ export function generalizeFn(fn, args) {
60
+ return applyArgs(fn, args, 'first');
61
+ }
62
+ export function generalizeBoolFn(fn, args) {
63
+ return applyArgs(fn, args, 'boolean');
64
+ }
65
+ function kindOf(v) {
66
+ if (typeof v === 'number') {
67
+ return 'number';
68
+ }
69
+ if (typeof v === 'boolean') {
70
+ return 'boolean';
71
+ }
72
+ return v.kind;
73
+ }
74
+ /**
75
+ * If one of the arguments is a vector and other is a number,
76
+ * the number is up-cased to a vector.
77
+ */
78
+ export function upCast(args) {
79
+ const [lhs, rhs] = args;
80
+ if (typeof lhs === 'number' && isVecInstance(rhs)) {
81
+ const schema = constructorFor[rhs.kind];
82
+ return [schema(lhs), rhs];
83
+ }
84
+ else if (isVecInstance(lhs) && typeof rhs === 'number') {
85
+ const schema = constructorFor[lhs.kind];
86
+ return [lhs, schema(rhs)];
87
+ }
88
+ return [lhs, rhs];
89
+ }
package/data/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @module typegpu/data
3
3
  */
4
+ import './assignInfixOperators.ts';
4
5
  export { bool, f16, f32, i32, u16, u32 } from './numeric.ts';
5
6
  export { isAlignAttrib, isAtomic, isBuiltinAttrib, isDecorated, isInterpolateAttrib, isInvariantAttrib, isLocationAttrib, isPtr, isSizeAttrib, isWgslArray, isWgslData, isWgslStruct, Void, } from './wgslTypes.ts';
6
7
  export type { Align, AnyVecInstance, AnyWgslData, AnyWgslStruct, Atomic, atomicI32, atomicU32, BaseData, BaseData as BaseWgslData, Bool, Builtin, Decorated, F16, F32, I32, Interpolate, Invariant, Location, m2x2f, m3x3f, m4x4f, Mat2x2f, Mat3x3f, Mat4x4f, matBase, Ptr, Size, StorableData, U16, U32, v2b, v2f, v2h, v2i, v2u, v3b, v3f, v3h, v3i, v3u, v4b, v4f, v4h, v4i, v4u, Vec2b, Vec2f, Vec2h, Vec2i, Vec2u, Vec3b, Vec3f, Vec3h, Vec3i, Vec3u, Vec4b, Vec4f, Vec4h, Vec4i, Vec4u, vecBase, WgslArray, WgslStruct, } from './wgslTypes.ts';
package/data/index.js CHANGED
@@ -2,20 +2,8 @@
2
2
  * @module typegpu/data
3
3
  */
4
4
  // NOTE: This is a barrel file, internal files should not import things from this file
5
- import { Operator } from 'tsover-runtime';
6
- import { MatBase } from "./matrix.js";
7
- import { VecBase } from "./vectorImpl.js";
8
- import { assignInfixOperator } from "../tgsl/infixDispatch.js";
9
- assignInfixOperator(VecBase, 'add', Operator.plus);
10
- assignInfixOperator(MatBase, 'add', Operator.plus);
11
- assignInfixOperator(VecBase, 'sub', Operator.minus);
12
- assignInfixOperator(MatBase, 'sub', Operator.minus);
13
- assignInfixOperator(VecBase, 'mul', Operator.star);
14
- assignInfixOperator(MatBase, 'mul', Operator.star);
15
- assignInfixOperator(VecBase, 'div', Operator.slash);
16
- assignInfixOperator(VecBase, 'mod', Operator.percent);
17
- assignInfixOperator(VecBase, 'bitShiftLeft', Symbol()); // bitShift does not yet have tsover operator symbol
18
- assignInfixOperator(VecBase, 'bitShiftRight', Symbol()); // bitShift does not yet have tsover operator symbol
5
+ // oxlint-disable-next-line import/no-unassigned-import -- this import has side effects
6
+ import "./assignInfixOperators.js";
19
7
  export { bool, f16, f32, i32, u16, u32 } from "./numeric.js";
20
8
  export { isAlignAttrib, isAtomic, isBuiltinAttrib, isDecorated, isInterpolateAttrib, isInvariantAttrib, isLocationAttrib, isPtr, isSizeAttrib, isWgslArray, isWgslData, isWgslStruct, Void, } from "./wgslTypes.js";
21
9
  export { struct } from "./struct.js";
@@ -1,6 +1,5 @@
1
1
  export declare const smoothstepScalar: (edge0: number, edge1: number, x: number) => number;
2
- export declare const clamp: (value: number, low: number, high: number) => number;
3
- export declare const divInteger: (lhs: number, rhs: number) => number;
2
+ export declare const clampScalar: (value: number, low: number, high: number) => number;
4
3
  export declare function bitcastU32toF32Impl(n: number): number;
5
4
  export declare function bitcastU32toI32Impl(n: number): number;
6
5
  export declare function bitcastF32toU32Impl(n: number): number;
package/data/numberOps.js CHANGED
@@ -2,16 +2,10 @@ export const smoothstepScalar = (edge0, edge1, x) => {
2
2
  if (edge0 === edge1) {
3
3
  return 0; // WGSL spec says this is an indeterminate value
4
4
  }
5
- const t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
5
+ const t = clampScalar((x - edge0) / (edge1 - edge0), 0.0, 1.0);
6
6
  return t * t * (3 - 2 * t);
7
7
  };
8
- export const clamp = (value, low, high) => Math.min(Math.max(low, value), high);
9
- export const divInteger = (lhs, rhs) => {
10
- if (rhs === 0) {
11
- return lhs;
12
- }
13
- return Math.trunc(lhs / rhs);
14
- };
8
+ export const clampScalar = (value, low, high) => Math.min(Math.max(low, value), high);
15
9
  const buf32 = new ArrayBuffer(4);
16
10
  const f32arr = new Float32Array(buf32);
17
11
  const u32arr = new Uint32Array(buf32);
@@ -3,57 +3,19 @@ import type { VecKind } from './wgslTypes.ts';
3
3
  type vBase = {
4
4
  kind: VecKind;
5
5
  };
6
- type mBase = {
7
- kind: MatKind;
8
- };
9
6
  type MatKind = 'mat2x2f' | 'mat3x3f' | 'mat4x4f';
7
+ /**
8
+ * Functions that cannot be simply generalized via `generalizeFn`
9
+ * have their overloads listed explicitly here.
10
+ */
10
11
  export declare const VectorOps: {
11
- eq: Record<VecKind, <T extends wgsl.AnyVecInstance>(e1: T, e2: T) => T extends wgsl.AnyVec2Instance ? wgsl.v2b : T extends wgsl.AnyVec3Instance ? wgsl.v3b : wgsl.v4b>;
12
- lt: Record<VecKind, <T extends wgsl.AnyNumericVecInstance>(e1: T, e2: T) => T extends wgsl.AnyVec2Instance ? wgsl.v2b : T extends wgsl.AnyVec3Instance ? wgsl.v3b : wgsl.v4b>;
13
- or: Record<VecKind, <T extends wgsl.AnyBooleanVecInstance>(e1: T, e2: T) => T>;
14
12
  all: Record<VecKind, (v: wgsl.AnyBooleanVecInstance) => boolean>;
15
- abs: Record<VecKind, <T extends vBase>(v: T) => T>;
16
- atan2: Record<VecKind, <T extends vBase>(a: T, b: T) => T>;
17
- acos: Record<VecKind, <T extends vBase>(v: T) => T>;
18
- acosh: Record<VecKind, <T extends vBase>(v: T) => T>;
19
- asin: Record<VecKind, <T extends vBase>(v: T) => T>;
20
- asinh: Record<VecKind, <T extends vBase>(v: T) => T>;
21
- atan: Record<VecKind, <T extends vBase>(v: T) => T>;
22
- atanh: Record<VecKind, <T extends vBase>(v: T) => T>;
23
- ceil: Record<VecKind, <T extends vBase>(v: T) => T>;
24
- clamp: Record<VecKind, <T extends vBase>(v: T, low: T, high: T) => T>;
25
13
  length: Record<VecKind, (v: vBase) => number>;
26
- add: Record<VecKind | MatKind, <T extends vBase | mBase>(lhs: T, rhs: T) => T>;
27
- smoothstep: Record<VecKind, <T extends vBase>(edge0: T, edge1: T, x: T) => T extends wgsl.AnyVec2Instance ? wgsl.v2f : T extends wgsl.AnyVec3Instance ? wgsl.v3f : T extends wgsl.AnyVec4Instance ? wgsl.v4f : wgsl.AnyVecInstance>;
28
- addMixed: Record<VecKind | MatKind, <T extends vBase | mBase>(lhs: T, rhs: number) => T>;
29
- mulSxV: Record<VecKind | MatKind, <T extends vBase | wgsl.AnyMatInstance>(s: number, v: T) => T>;
30
- mulVxV: Record<VecKind | MatKind, <T extends vBase | wgsl.AnyMatInstance>(lhs: T, rhs: T) => T>;
14
+ mulMxM: Record<VecKind | MatKind, <T extends vBase | wgsl.AnyMatInstance>(lhs: T, rhs: T) => T>;
31
15
  mulMxV: Record<MatKind, <T extends wgsl.AnyMatInstance>(m: T, v: wgsl.vBaseForMat<T>) => wgsl.vBaseForMat<T>>;
32
16
  mulVxM: Record<MatKind, <T extends wgsl.AnyMatInstance>(v: wgsl.vBaseForMat<T>, m: T) => wgsl.vBaseForMat<T>>;
33
- div: Record<VecKind, <T extends vBase>(a: T, b: T) => T>;
34
17
  dot: Record<VecKind, <T extends vBase>(lhs: T, rhs: T) => number>;
35
- normalize: Record<VecKind, <T extends vBase>(v: T) => T>;
36
18
  cross: Record<"vec3f" | "vec3h", <T extends wgsl.v3f | wgsl.v3h>(a: T, b: T) => T>;
37
- mod: Record<VecKind, <T extends vBase>(a: T, b: T) => T>;
38
- floor: Record<VecKind, <T extends vBase>(v: T) => T>;
39
- max: Record<VecKind, <T extends vBase>(a: T, b: T) => T>;
40
- min: Record<VecKind, <T extends vBase>(a: T, b: T) => T>;
41
- pow: Record<"vec2f" | "vec3f" | "vec4f" | "vec2h" | "vec3h" | "vec4h" | "number", <T extends wgsl.AnyFloatVecInstance | number>(a: T, b: T) => T>;
42
- sign: Record<VecKind, <T extends vBase>(e: T) => T>;
43
- sqrt: Record<VecKind, <T extends vBase>(v: T) => T>;
44
- mix: Record<"vec2f" | "vec3f" | "vec4f" | "vec2h" | "vec3h" | "vec4h", <T extends wgsl.v2f | wgsl.v3f | wgsl.v4f | wgsl.v2h | wgsl.v3h | wgsl.v4h>(a: T, b: T, c: T | number) => T>;
45
- sin: Record<VecKind, <T extends vBase>(v: T) => T>;
46
- cos: Record<VecKind, <T extends vBase>(v: T) => T>;
47
- cosh: Record<VecKind, <T extends vBase>(v: T) => T>;
48
- exp: Record<VecKind, <T extends vBase>(v: T) => T>;
49
- exp2: Record<VecKind, <T extends vBase>(v: T) => T>;
50
- log: Record<VecKind, <T extends vBase>(v: T) => T>;
51
- log2: Record<VecKind, <T extends vBase>(v: T) => T>;
52
- fract: Record<VecKind, <T extends vBase>(v: T) => T>;
53
- isCloseToZero: Record<VecKind, <T extends vBase>(v: T, n: number) => boolean>;
54
- neg: Record<VecKind, <T extends vBase>(v: T) => T>;
55
- select: Record<VecKind, <T extends wgsl.AnyVecInstance>(f: T, t: T, c: T extends wgsl.AnyVec2Instance ? wgsl.v2b : T extends wgsl.AnyVec3Instance ? wgsl.v3b : wgsl.v4b) => T>;
56
- tanh: Record<VecKind, <T extends vBase>(v: T) => T>;
57
19
  bitShiftLeft: Record<VecKind, <T extends wgsl.AnyIntegerVecInstance, U extends wgsl.AnyUnsignedVecInstance>(a: T, b: U) => T>;
58
20
  bitShiftRight: Record<VecKind, <T extends wgsl.AnyIntegerVecInstance, U extends wgsl.AnyUnsignedVecInstance>(a: T, b: U) => T>;
59
21
  bitcastU32toF32: Record<VecKind, <T extends wgsl.AnyUnsignedVecInstance>(v: T) => T extends wgsl.v2u ? wgsl.v2f : T extends wgsl.v3u ? wgsl.v3f : wgsl.v4f>;