typegpu 0.12.0 → 0.12.1
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 +28 -6
- package/core/buffer/buffer.js +4 -0
- package/core/pipeline/computePipeline.js +1 -1
- package/core/pipeline/renderPipeline.js +1 -1
- package/core/pipeline/webgpuLimitations.d.ts +10 -0
- package/core/pipeline/webgpuLimitations.js +81 -0
- package/data/array.d.ts +12 -1
- package/data/array.js +9 -0
- package/data/generalizeFn.d.ts +25 -0
- package/data/generalizeFn.js +89 -0
- package/data/numberOps.d.ts +1 -2
- package/data/numberOps.js +2 -8
- package/data/vectorOps.d.ts +5 -43
- package/data/vectorOps.js +7 -570
- package/indexNamedExports.d.ts +1 -1
- package/package.json +1 -1
- package/shared/meta.js +1 -1
- package/shared/symbols.js +1 -1
- package/std/boolean.d.ts +8 -8
- package/std/boolean.js +9 -13
- package/std/index.d.ts +1 -1
- package/std/index.js +1 -1
- package/std/numeric.d.ts +7 -0
- package/std/numeric.js +59 -124
- package/std/operators.js +12 -45
- package/tgpuLogger.d.ts +1 -1
- package/tgpuLogger.js +1 -0
- package/tgsl/wgslGenerator.js +2 -1
- package/core/pipeline/limitsOverflow.d.ts +0 -2
- package/core/pipeline/limitsOverflow.js +0 -16
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,
|
|
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
|
|
42
|
-
|
|
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(
|
|
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
|
|
package/core/buffer/buffer.js
CHANGED
|
@@ -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 "./
|
|
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 "./
|
|
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,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/numberOps.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export declare const smoothstepScalar: (edge0: number, edge1: number, x: number) => number;
|
|
2
|
-
export declare const
|
|
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 =
|
|
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
|
|
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);
|
package/data/vectorOps.d.ts
CHANGED
|
@@ -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
|
-
|
|
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>;
|