typegpu 0.12.4 → 0.12.5
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 +39 -67
- package/core/pipeline/computePipeline.d.ts +6 -0
- package/core/pipeline/computePipeline.js +3 -0
- package/core/pipeline/renderPipeline.d.ts +6 -0
- package/core/pipeline/renderPipeline.js +3 -0
- package/data/generalizeFn.d.ts +17 -1
- package/data/generalizeFn.js +39 -2
- package/package.json +1 -1
- package/shared/meta.js +1 -1
- package/shared/symbols.js +1 -1
- package/shared/utilityTypes.d.ts +1 -1
- package/std/bitcast.js +4 -0
- package/std/boolean.d.ts +1 -1
- package/std/boolean.js +35 -21
- package/std/derivative.js +3 -3
- package/std/numeric.js +72 -5
- package/std/operators.d.ts +2 -2
- package/std/operators.js +29 -17
package/bin.mjs
CHANGED
|
@@ -1,79 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
2
|
+
import { execFile, spawn } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
3
4
|
import pkg from './package.json' with { type: 'json' };
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* trigger the CLI, which then allows us to download the latest
|
|
8
|
-
* version matching the major and minor of the `typegpu` package.
|
|
9
|
-
*/
|
|
10
|
-
const versionPattern = /^(\d+)\.(\d+)\.(\d+)/;
|
|
11
|
-
|
|
12
|
-
const result = versionPattern.exec(pkg.version);
|
|
13
|
-
const [_, major, minor] = result;
|
|
6
|
+
const [major, minor] = pkg.version.split('.');
|
|
7
|
+
const semver = `^${major}.${minor}.0`;
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
const windows = process.platform === 'win32';
|
|
10
|
+
const npm = windows ? 'npm.cmd' : 'npm';
|
|
11
|
+
const npx = windows ? 'npx.cmd' : 'npx';
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
|
|
14
|
+
/** True only when the registry confirms no `@typegpu/cli` satisfies `semver` */
|
|
15
|
+
async function noMatchingCli() {
|
|
16
|
+
const args = ['view', `@typegpu/cli@${semver}`, 'version', '--json'];
|
|
17
|
+
try {
|
|
18
|
+
const { stdout } = await execFileAsync(npm, args, { shell: windows, timeout: 3_000 });
|
|
19
|
+
return ['', '[]'].includes(stdout.trim());
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return /E404|ETARGET/.test(err.stderr ?? '');
|
|
22
|
+
}
|
|
17
23
|
}
|
|
18
24
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
*/
|
|
22
|
-
const semver = `^${major}.${minor}.0`;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @returns {Promise<number | undefined>}
|
|
26
|
-
*/
|
|
27
|
-
function asyncSpawn(...args) {
|
|
25
|
+
/** Resolves with the child's exit code, re-raising a fatal signal on this process */
|
|
26
|
+
function run(command, args) {
|
|
28
27
|
return new Promise((resolve, reject) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
resolve(code);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
child.on('error', (err) => {
|
|
42
|
-
reject(err);
|
|
43
|
-
});
|
|
28
|
+
spawn(command, args, { stdio: 'inherit', shell: windows })
|
|
29
|
+
.on('error', reject)
|
|
30
|
+
.on('exit', (code, signal) => {
|
|
31
|
+
if (signal) {
|
|
32
|
+
process.kill(process.pid, signal);
|
|
33
|
+
} else {
|
|
34
|
+
resolve(code ?? 1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
44
37
|
});
|
|
45
38
|
}
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
function failedToRunErrHandler(label) {
|
|
51
|
-
return (err) => {
|
|
52
|
-
console.error(`Failed to run '${label}':`, err);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
};
|
|
40
|
+
const fallback = await noMatchingCli();
|
|
41
|
+
if (fallback) {
|
|
42
|
+
console.warn(`Couldn't find @typegpu/cli version matching ${semver}, falling back to latest...`);
|
|
55
43
|
}
|
|
44
|
+
const spec = fallback ? '@typegpu/cli@latest' : `@typegpu/cli@${semver}`;
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
shell: windows, // needs to be ran through the shell on Windows
|
|
64
|
-
}).catch(failedToRunErrHandler(`npx @typegpu/cli@${semver}`));
|
|
65
|
-
|
|
66
|
-
if (code !== 0) {
|
|
67
|
-
console.warn(
|
|
68
|
-
`Couldn't find @typegpu/cli version matching ${semver}, falling back to latest...`,
|
|
69
|
-
);
|
|
70
|
-
// Fallback to latest
|
|
71
|
-
const code = await asyncSpawn(npxCommand, [`@typegpu/cli@latest`, ...process.argv.slice(2)], {
|
|
72
|
-
stdio: 'inherit',
|
|
73
|
-
shell: windows, // needs to be ran through the shell on Windows
|
|
74
|
-
}).catch(failedToRunErrHandler('npx @typegpu/cli@latest'));
|
|
75
|
-
process.exit(code ?? 0);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
process.exit(code ?? 0);
|
|
79
|
-
})();
|
|
46
|
+
try {
|
|
47
|
+
process.exit(await run(npx, [spec, ...process.argv.slice(2)]));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(`Failed to run 'npx ${spec}':`, err);
|
|
50
|
+
process.exit(2);
|
|
51
|
+
}
|
|
@@ -54,6 +54,11 @@ export interface TgpuComputePipeline extends TgpuNamable, SelfResolvable, Timeab
|
|
|
54
54
|
with(encoder: TgpuCommandEncoder): this;
|
|
55
55
|
with(encoder: GPUCommandEncoder): this;
|
|
56
56
|
with(pass: GPUComputePassEncoder): this;
|
|
57
|
+
/**
|
|
58
|
+
* Applies a transform to this pipeline, letting packages hand out reusable
|
|
59
|
+
* configuration steps, e.g. `pipeline.pipe(cache.inject())`.
|
|
60
|
+
*/
|
|
61
|
+
pipe<T>(transform: (pipeline: this) => T): T;
|
|
57
62
|
dispatchWorkgroups(x: number, y?: number, z?: number): void;
|
|
58
63
|
/**
|
|
59
64
|
* Immediately resolves the pipeline, then awaits `device.createComputePipelineAsync()`.
|
|
@@ -111,6 +116,7 @@ declare class TgpuComputePipelineImpl implements TgpuComputePipeline {
|
|
|
111
116
|
with(encoder: TgpuCommandEncoder): this;
|
|
112
117
|
with(encoder: GPUCommandEncoder): this;
|
|
113
118
|
with(pass: GPUComputePassEncoder): this;
|
|
119
|
+
pipe<T>(transform: (pipeline: this) => T): T;
|
|
114
120
|
withPerformanceCallback(callback: (start: bigint, end: bigint) => void | Promise<void>): this;
|
|
115
121
|
withTimestampWrites(options: {
|
|
116
122
|
querySet: TgpuQuerySet<'timestamp'> | GPUQuerySet;
|
|
@@ -111,6 +111,9 @@ class TgpuComputePipelineImpl {
|
|
|
111
111
|
}
|
|
112
112
|
throw new Error('Unsupported value passed into .with()');
|
|
113
113
|
}
|
|
114
|
+
pipe(transform) {
|
|
115
|
+
return transform(this);
|
|
116
|
+
}
|
|
114
117
|
withPerformanceCallback(callback) {
|
|
115
118
|
const internals = this[$internal];
|
|
116
119
|
if (internals.priors.timestampWrites) {
|
|
@@ -89,6 +89,11 @@ export interface TgpuRenderPipeline<in Targets = never> extends TgpuNamable, Sel
|
|
|
89
89
|
with(encoder: GPUCommandEncoder): this;
|
|
90
90
|
with(pass: GPURenderPassEncoder): this;
|
|
91
91
|
with(bundleEncoder: GPURenderBundleEncoder): this;
|
|
92
|
+
/**
|
|
93
|
+
* Applies a transform to this pipeline, letting packages hand out reusable
|
|
94
|
+
* configuration steps, e.g. `pipeline.pipe(mesh.inject())`.
|
|
95
|
+
*/
|
|
96
|
+
pipe<T>(transform: (pipeline: this) => T): T;
|
|
92
97
|
/**
|
|
93
98
|
* Attaches texture views to the pipeline's targets (outputs).
|
|
94
99
|
*
|
|
@@ -223,6 +228,7 @@ declare class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
|
|
|
223
228
|
with(encoder: GPUCommandEncoder): this;
|
|
224
229
|
with(pass: GPURenderPassEncoder): this;
|
|
225
230
|
with(bundleEncoder: GPURenderBundleEncoder): this;
|
|
231
|
+
pipe<T>(transform: (pipeline: this) => T): T;
|
|
226
232
|
withPerformanceCallback(callback: (start: bigint, end: bigint) => void | Promise<void>): this;
|
|
227
233
|
withTimestampWrites(options: {
|
|
228
234
|
querySet: TgpuQuerySet<'timestamp'> | GPUQuerySet;
|
|
@@ -143,6 +143,9 @@ class TgpuRenderPipelineImpl {
|
|
|
143
143
|
}
|
|
144
144
|
throw new Error('Unsupported value passed into .with()');
|
|
145
145
|
}
|
|
146
|
+
pipe(transform) {
|
|
147
|
+
return transform(this);
|
|
148
|
+
}
|
|
146
149
|
withPerformanceCallback(callback) {
|
|
147
150
|
const internals = this[$internal];
|
|
148
151
|
if (internals.priors.timestampWrites) {
|
package/data/generalizeFn.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type AnyBooleanVecInstance, type AnyMatInstance, type AnyVec2Instance, type AnyVec3Instance, type AnyVec4Instance, type AnyVecInstance, type v2b, type v3b, type v4b } from './wgslTypes.ts';
|
|
2
2
|
type Vec = AnyVecInstance;
|
|
3
3
|
type Mat = AnyMatInstance;
|
|
4
|
+
type Kind = 'number' | 'boolean' | Vec['kind'] | Mat['kind'];
|
|
4
5
|
type Algebraic = number | boolean | Vec | Mat;
|
|
5
6
|
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
|
/**
|
|
@@ -15,11 +16,26 @@ export declare function generalizeFn<T extends Algebraic>(fn: (a: number, b: num
|
|
|
15
16
|
/**
|
|
16
17
|
* Analogous to `generalizeFn`, but the return type is a boolean vector instead.
|
|
17
18
|
*/
|
|
19
|
+
export declare function generalizeBoolFn<T extends boolean | AnyBooleanVecInstance>(fn: (a: boolean) => boolean, args: [T]): T;
|
|
18
20
|
export declare function generalizeBoolFn<T extends Algebraic>(fn: (a: number, b: number) => boolean, args: [T, T]): ToBool<T>;
|
|
19
21
|
export declare function generalizeBoolFn<T extends boolean | AnyBooleanVecInstance>(fn: (a: boolean, b: boolean) => boolean, args: [T, T]): ToBool<T>;
|
|
22
|
+
export declare function kindOf(v: Algebraic): Kind;
|
|
23
|
+
export declare const u32Kind: Set<Kind>;
|
|
24
|
+
export declare const f32Kind: Set<Kind>;
|
|
25
|
+
export declare const f16Kind: Set<Kind>;
|
|
26
|
+
export declare const matrixKind: Set<Kind>;
|
|
27
|
+
export declare const booleanKind: Set<Kind>;
|
|
28
|
+
export declare const floatKind: Set<Kind>;
|
|
29
|
+
export declare const signedKind: Set<Kind>;
|
|
30
|
+
export declare const numericKind: Set<Kind>;
|
|
31
|
+
export declare const numericOrBooleanKind: Set<Kind>;
|
|
32
|
+
export declare const numericOrMatrixKind: Set<Kind>;
|
|
33
|
+
export declare const crossKind: Set<Kind>;
|
|
34
|
+
export declare function assertKind(v: Algebraic | Algebraic[], valid: Set<Kind>, excludeScalar?: boolean): void;
|
|
35
|
+
export declare function assertEqualKinds(...values: Algebraic[]): void;
|
|
20
36
|
/**
|
|
21
37
|
* If one of the arguments is a vector and other is a number,
|
|
22
38
|
* the number is up-cased to a vector.
|
|
23
39
|
*/
|
|
24
|
-
export declare function upCast<T extends number | Vec>(args: [T, T]): [Exclude<T, number>, Exclude<T, number>];
|
|
40
|
+
export declare function upCast<T extends number | Vec | Mat>(args: [T, T]): [Exclude<T, number>, Exclude<T, number>];
|
|
25
41
|
export {};
|
package/data/generalizeFn.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { vec2b, vec3b, vec4b, vecTypeToConstructor } from "./vector.js";
|
|
2
2
|
import { mat2x2f, mat3x3f, mat4x4f } from "./matrix.js";
|
|
3
3
|
import { isVecInstance, } from "./wgslTypes.js";
|
|
4
|
-
import { invariant } from "../errors.js";
|
|
4
|
+
import { invariant, WgslTypeError } from "../errors.js";
|
|
5
5
|
const booleanFor = {
|
|
6
6
|
vec2f: vec2b,
|
|
7
7
|
vec2h: vec2b,
|
|
@@ -62,7 +62,7 @@ export function generalizeFn(fn, args) {
|
|
|
62
62
|
export function generalizeBoolFn(fn, args) {
|
|
63
63
|
return applyArgs(fn, args, 'boolean');
|
|
64
64
|
}
|
|
65
|
-
function kindOf(v) {
|
|
65
|
+
export function kindOf(v) {
|
|
66
66
|
if (typeof v === 'number') {
|
|
67
67
|
return 'number';
|
|
68
68
|
}
|
|
@@ -71,6 +71,43 @@ function kindOf(v) {
|
|
|
71
71
|
}
|
|
72
72
|
return v.kind;
|
|
73
73
|
}
|
|
74
|
+
// Unless matrix is mentioned in the name, it is not included.
|
|
75
|
+
const i32Kind = new Set(['number', 'vec2i', 'vec3i', 'vec4i']);
|
|
76
|
+
export const u32Kind = new Set(['number', 'vec2u', 'vec3u', 'vec4u']);
|
|
77
|
+
export const f32Kind = new Set(['number', 'vec2f', 'vec3f', 'vec4f']);
|
|
78
|
+
export const f16Kind = new Set(['number', 'vec2h', 'vec3h', 'vec4h']);
|
|
79
|
+
export const matrixKind = new Set(['mat2x2f', 'mat3x3f', 'mat4x4f']);
|
|
80
|
+
export const booleanKind = new Set([
|
|
81
|
+
'boolean',
|
|
82
|
+
'vec2<bool>',
|
|
83
|
+
'vec3<bool>',
|
|
84
|
+
'vec4<bool>',
|
|
85
|
+
]);
|
|
86
|
+
export const floatKind = new Set([...f32Kind, ...f16Kind]);
|
|
87
|
+
export const signedKind = new Set([...i32Kind, ...f32Kind, ...f16Kind]);
|
|
88
|
+
export const numericKind = new Set([...signedKind, ...u32Kind]);
|
|
89
|
+
export const numericOrBooleanKind = new Set([...numericKind, ...booleanKind]);
|
|
90
|
+
export const numericOrMatrixKind = new Set([...numericKind, ...matrixKind]);
|
|
91
|
+
export const crossKind = new Set(['vec3f', 'vec3h']);
|
|
92
|
+
export function assertKind(v, valid, excludeScalar = false) {
|
|
93
|
+
if (!isVecInstance(v) && Array.isArray(v)) {
|
|
94
|
+
v.forEach((item) => assertKind(item, valid, excludeScalar));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const kind = kindOf(v);
|
|
98
|
+
if (!valid.has(kind)) {
|
|
99
|
+
throw new WgslTypeError(`Unsupported signature. Expected one of '${[...valid].join(', ')}', got '${kind}'.`);
|
|
100
|
+
}
|
|
101
|
+
if (excludeScalar && (kind === 'number' || kind === 'boolean')) {
|
|
102
|
+
throw new WgslTypeError(`Unsupported signature. Expected kind to not be scalar, got '${kind}'.`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export function assertEqualKinds(...values) {
|
|
106
|
+
const kinds = new Set(values.map(kindOf));
|
|
107
|
+
if (kinds.size !== 1) {
|
|
108
|
+
throw new WgslTypeError(`Unsupported signature. Expected the following kinds to be equal: '${[...kinds].join(', ')}'.`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
74
111
|
/**
|
|
75
112
|
* If one of the arguments is a vector and other is a number,
|
|
76
113
|
* the number is up-cased to a vector.
|
package/package.json
CHANGED
package/shared/meta.js
CHANGED
package/shared/symbols.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const version = "0.12.
|
|
1
|
+
const version = "0.12.5";
|
|
2
2
|
export const $internal = Symbol(`typegpu:${version}:$internal`);
|
|
3
3
|
/** A plain record of all definitional state of a resource, surviving transfer between runtimes */
|
|
4
4
|
export const $soul = Symbol(`typegpu:${version}:$soul`);
|
package/shared/utilityTypes.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export type Assume<T, U> = T extends U ? T : U;
|
|
|
39
39
|
/**
|
|
40
40
|
* Any typed array
|
|
41
41
|
*/
|
|
42
|
-
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Int32Array | Float32Array | Float64Array;
|
|
42
|
+
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Int32Array | Float16Array | Float32Array | Float64Array;
|
|
43
43
|
export declare function assertExhaustive(x: never, location: string): never;
|
|
44
44
|
/**
|
|
45
45
|
* Source: https://futurestud.io/tutorials/typescript-how-to-remove-index-signature-from-a-type
|
package/std/bitcast.js
CHANGED
|
@@ -9,6 +9,7 @@ import { SignatureNotSupportedError } from "../errors.js";
|
|
|
9
9
|
import { getName } from "../shared/meta.js";
|
|
10
10
|
import { comptime } from "../core/function/comptime.js";
|
|
11
11
|
import { coerceToSnippet } from "../tgsl/generationHelpers.js";
|
|
12
|
+
import { f32Kind, u32Kind, assertKind } from "../data/generalizeFn.js";
|
|
12
13
|
const u32AllowedSchemas = [u32, vec2u, vec3u, vec4u];
|
|
13
14
|
// TODO(#2731): Remove deprecated bitcasts. Remember about cpu implementations.
|
|
14
15
|
/**
|
|
@@ -17,6 +18,7 @@ const u32AllowedSchemas = [u32, vec2u, vec3u, vec4u];
|
|
|
17
18
|
export const bitcastU32toF32 = dualImpl({
|
|
18
19
|
name: 'bitcastU32toF32',
|
|
19
20
|
normalImpl: ((value) => {
|
|
21
|
+
assertKind(value, u32Kind);
|
|
20
22
|
if (typeof value === 'number') {
|
|
21
23
|
return bitcastU32toF32Impl(value);
|
|
22
24
|
}
|
|
@@ -49,6 +51,7 @@ export const bitcastU32toF32 = dualImpl({
|
|
|
49
51
|
export const bitcastU32toI32 = dualImpl({
|
|
50
52
|
name: 'bitcastU32toI32',
|
|
51
53
|
normalImpl: ((value) => {
|
|
54
|
+
assertKind(value, u32Kind);
|
|
52
55
|
if (typeof value === 'number') {
|
|
53
56
|
return bitcastU32toI32Impl(value);
|
|
54
57
|
}
|
|
@@ -82,6 +85,7 @@ const f32AllowedSchemas = [f32, vec2f, vec3f, vec4f];
|
|
|
82
85
|
export const bitcastF32toU32 = dualImpl({
|
|
83
86
|
name: 'bitcastF32toU32',
|
|
84
87
|
normalImpl: ((value) => {
|
|
88
|
+
assertKind(value, f32Kind);
|
|
85
89
|
if (typeof value === 'number') {
|
|
86
90
|
return bitcastF32toU32Impl(value);
|
|
87
91
|
}
|
package/std/boolean.d.ts
CHANGED
|
@@ -93,7 +93,7 @@ export declare const and: import("../types.ts").DualFn<(<T extends AnyBooleanVec
|
|
|
93
93
|
* all(vec2b(false, true)) // returns false
|
|
94
94
|
* all(vec3b(true, true, true)) // returns true
|
|
95
95
|
*/
|
|
96
|
-
export declare const all: import("../types.ts").DualFn<(value: AnyBooleanVecInstance) => boolean>;
|
|
96
|
+
export declare const all: import("../types.ts").DualFn<(value: boolean | AnyBooleanVecInstance) => boolean>;
|
|
97
97
|
/**
|
|
98
98
|
* Returns `true` if any component of `value` is true.
|
|
99
99
|
* @example
|
package/std/boolean.js
CHANGED
|
@@ -4,9 +4,9 @@ import { bool, f16, f32, i32, u32 } from "../data/numeric.js";
|
|
|
4
4
|
import { isSnippetNumeric, snip } from "../data/snippet.js";
|
|
5
5
|
import { vec2b, vec2f, vec2h, vec2i, vec2u, vec3b, vec3f, vec3h, vec3i, vec3u, vec4b, vec4f, vec4h, vec4i, vec4u, } from "../data/vector.js";
|
|
6
6
|
import { VectorOps } from "../data/vectorOps.js";
|
|
7
|
-
import { generalizeBoolFn, generalizeFn } from "../data/generalizeFn.js";
|
|
8
|
-
import { isBool, isVecBool,
|
|
9
|
-
import { SignatureNotSupportedError } from "../errors.js";
|
|
7
|
+
import { booleanKind, floatKind, generalizeBoolFn, generalizeFn, kindOf, numericKind, numericOrBooleanKind, assertEqualKinds, assertKind, } from "../data/generalizeFn.js";
|
|
8
|
+
import { isBool, isVecBool, isVecInstance, } from "../data/wgslTypes.js";
|
|
9
|
+
import { SignatureNotSupportedError, WgslTypeError } from "../errors.js";
|
|
10
10
|
import { unify } from "../tgsl/conversion.js";
|
|
11
11
|
import { cpuCopy } from "./copy.js";
|
|
12
12
|
function correspondingBooleanVectorSchema(dataType) {
|
|
@@ -33,7 +33,11 @@ export const allEq = dualImpl({
|
|
|
33
33
|
codegenImpl: (_ctx, [lhs, rhs]) => stitch `all(${lhs} == ${rhs})`,
|
|
34
34
|
sideEffects: false,
|
|
35
35
|
});
|
|
36
|
-
const cpuEq = (lhs, rhs) =>
|
|
36
|
+
const cpuEq = (lhs, rhs) => {
|
|
37
|
+
assertKind([lhs, rhs], numericOrBooleanKind);
|
|
38
|
+
assertEqualKinds(lhs, rhs);
|
|
39
|
+
return generalizeBoolFn((a, b) => a === b, [lhs, rhs]);
|
|
40
|
+
};
|
|
37
41
|
/**
|
|
38
42
|
* Checks **component-wise** whether `lhs == rhs`.
|
|
39
43
|
* This function does **not** return `bool`, for that use-case, wrap the result in `all`, or use `allEq`.
|
|
@@ -71,7 +75,11 @@ export const ne = dualImpl({
|
|
|
71
75
|
codegenImpl: (_ctx, [lhs, rhs]) => stitch `(${lhs} != ${rhs})`,
|
|
72
76
|
sideEffects: false,
|
|
73
77
|
});
|
|
74
|
-
const cpuLt = (lhs, rhs) =>
|
|
78
|
+
const cpuLt = (lhs, rhs) => {
|
|
79
|
+
assertKind([lhs, rhs], numericKind);
|
|
80
|
+
assertEqualKinds(lhs, rhs);
|
|
81
|
+
return generalizeBoolFn((a, b) => a < b, [lhs, rhs]);
|
|
82
|
+
};
|
|
75
83
|
/**
|
|
76
84
|
* Checks **component-wise** whether `lhs < rhs`.
|
|
77
85
|
* This function does **not** return `bool`, for that use-case, wrap the result in `all`.
|
|
@@ -145,20 +153,8 @@ export const ge = dualImpl({
|
|
|
145
153
|
sideEffects: false,
|
|
146
154
|
});
|
|
147
155
|
function cpuNot(value) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
if (!isVecBoolInstance(value)) {
|
|
152
|
-
throw new Error(`'std.not' requires a boolean or boolean vector.`);
|
|
153
|
-
}
|
|
154
|
-
switch (value.length) {
|
|
155
|
-
case 2:
|
|
156
|
-
return vec2b(cpuNot(value.x), cpuNot(value.y));
|
|
157
|
-
case 3:
|
|
158
|
-
return vec3b(cpuNot(value.x), cpuNot(value.y), cpuNot(value.z));
|
|
159
|
-
case 4:
|
|
160
|
-
return vec4b(cpuNot(value.x), cpuNot(value.y), cpuNot(value.z), cpuNot(value.w));
|
|
161
|
-
}
|
|
156
|
+
assertKind(value, booleanKind);
|
|
157
|
+
return generalizeBoolFn((a) => !a, [value]);
|
|
162
158
|
}
|
|
163
159
|
/**
|
|
164
160
|
* Returns the logical negation of the given value.
|
|
@@ -183,7 +179,11 @@ export const not = dualImpl({
|
|
|
183
179
|
codegenImpl: (_ctx, [arg]) => stitch `!(${arg})`,
|
|
184
180
|
sideEffects: false,
|
|
185
181
|
});
|
|
186
|
-
const cpuOr = (lhs, rhs) =>
|
|
182
|
+
const cpuOr = (lhs, rhs) => {
|
|
183
|
+
assertKind([lhs, rhs], booleanKind);
|
|
184
|
+
assertEqualKinds(lhs, rhs);
|
|
185
|
+
return generalizeBoolFn((a, b) => a || b, [lhs, rhs]);
|
|
186
|
+
};
|
|
187
187
|
/**
|
|
188
188
|
* Returns **component-wise** logical `or` result.
|
|
189
189
|
* @example
|
|
@@ -212,7 +212,13 @@ export const and = dualImpl({
|
|
|
212
212
|
sideEffects: false,
|
|
213
213
|
});
|
|
214
214
|
// logical aggregation
|
|
215
|
-
const cpuAll = (value) =>
|
|
215
|
+
const cpuAll = (value) => {
|
|
216
|
+
assertKind(value, booleanKind);
|
|
217
|
+
if (typeof value === 'boolean') {
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
220
|
+
return VectorOps.all[value.kind](value);
|
|
221
|
+
};
|
|
216
222
|
/**
|
|
217
223
|
* Returns `true` if each component of `value` is true.
|
|
218
224
|
* @example
|
|
@@ -257,6 +263,8 @@ export const isCloseTo = dualImpl({
|
|
|
257
263
|
}),
|
|
258
264
|
// CPU implementation
|
|
259
265
|
normalImpl: (lhs, rhs, precision = 0.01) => {
|
|
266
|
+
assertKind([lhs, rhs], floatKind);
|
|
267
|
+
assertEqualKinds(lhs, rhs);
|
|
260
268
|
const componentResult = generalizeBoolFn((lhs, rhs) => Math.abs(lhs - rhs) < precision, [lhs, rhs]);
|
|
261
269
|
return typeof componentResult === 'boolean' ? componentResult : all(componentResult);
|
|
262
270
|
},
|
|
@@ -275,9 +283,15 @@ export const isCloseTo = dualImpl({
|
|
|
275
283
|
sideEffects: false,
|
|
276
284
|
});
|
|
277
285
|
function cpuSelect(f, t, cond) {
|
|
286
|
+
assertKind([f, t], numericOrBooleanKind);
|
|
287
|
+
assertEqualKinds(f, t);
|
|
288
|
+
assertKind(cond, booleanKind);
|
|
278
289
|
if (typeof cond === 'boolean') {
|
|
279
290
|
return cpuCopy(cond ? t : f);
|
|
280
291
|
}
|
|
292
|
+
if (!isVecInstance(f) || f.length !== cond.length) {
|
|
293
|
+
throw new WgslTypeError(`Select shape '(${kindOf(f)}, ${kindOf(t)}, ${kindOf(cond)})' is invalid.`);
|
|
294
|
+
}
|
|
281
295
|
// generalizeFn will handle this fine, it just has no mixed type overload.
|
|
282
296
|
return generalizeFn((f, t, c) => (c ? t : f), [f, t, cond]);
|
|
283
297
|
}
|
package/std/derivative.js
CHANGED
|
@@ -5,7 +5,7 @@ export const dpdx = dualImpl({
|
|
|
5
5
|
name: 'dpdx',
|
|
6
6
|
normalImpl: derivativeNormalError,
|
|
7
7
|
signature: (value) => ({ argTypes: [value], returnType: value }),
|
|
8
|
-
codegenImpl: (
|
|
8
|
+
codegenImpl: (ctx, [value]) => ctx.gen.emitCall('dpdx', [], [value]),
|
|
9
9
|
sideEffects: false,
|
|
10
10
|
});
|
|
11
11
|
export const dpdxCoarse = dualImpl({
|
|
@@ -26,7 +26,7 @@ export const dpdy = dualImpl({
|
|
|
26
26
|
name: 'dpdy',
|
|
27
27
|
normalImpl: derivativeNormalError,
|
|
28
28
|
signature: (value) => ({ argTypes: [value], returnType: value }),
|
|
29
|
-
codegenImpl: (
|
|
29
|
+
codegenImpl: (ctx, [value]) => ctx.gen.emitCall('dpdy', [], [value]),
|
|
30
30
|
sideEffects: false,
|
|
31
31
|
});
|
|
32
32
|
export const dpdyCoarse = dualImpl({
|
|
@@ -47,7 +47,7 @@ export const fwidth = dualImpl({
|
|
|
47
47
|
name: 'fwidth',
|
|
48
48
|
normalImpl: derivativeNormalError,
|
|
49
49
|
signature: (value) => ({ argTypes: [value], returnType: value }),
|
|
50
|
-
codegenImpl: (
|
|
50
|
+
codegenImpl: (ctx, [value]) => ctx.gen.emitCall('fwidth', [], [value]),
|
|
51
51
|
sideEffects: false,
|
|
52
52
|
});
|
|
53
53
|
export const fwidthCoarse = dualImpl({
|
package/std/numeric.js
CHANGED
|
@@ -6,7 +6,7 @@ import { abstractFloat, abstractInt, f16, f32, i32, u32 } from "../data/numeric.
|
|
|
6
6
|
import { abstruct } from "../data/struct.js";
|
|
7
7
|
import { vec2f, vec2h, vec2i, vec2u, vec3f, vec3h, vec3i, vec3u, vec4f, vec4h, vec4i, vec4u, } from "../data/vector.js";
|
|
8
8
|
import { VectorOps } from "../data/vectorOps.js";
|
|
9
|
-
import {
|
|
9
|
+
import { floatKind, matrixKind, numericKind, signedKind, crossKind, assertEqualKinds, assertKind, upCast, generalizeFn, } from "../data/generalizeFn.js";
|
|
10
10
|
import { isHalfPrecisionSchema, WORKAROUND_getSchema, } from "../data/wgslTypes.js";
|
|
11
11
|
import { SignatureNotSupportedError } from "../errors.js";
|
|
12
12
|
import { assertExhaustive } from "../shared/utilityTypes.js";
|
|
@@ -61,6 +61,7 @@ const anyConcreteIntegerPrimitive = [i32, u32];
|
|
|
61
61
|
const anyConcreteIntegerVec = [vec2i, vec3i, vec4i, vec2u, vec3u, vec4u];
|
|
62
62
|
const anyConcreteInteger = [...anyConcreteIntegerPrimitive, ...anyConcreteIntegerVec];
|
|
63
63
|
function cpuAbs(value) {
|
|
64
|
+
assertKind(value, numericKind);
|
|
64
65
|
return generalizeFn(Math.abs, [value]);
|
|
65
66
|
}
|
|
66
67
|
export const abs = dualImpl({
|
|
@@ -71,6 +72,7 @@ export const abs = dualImpl({
|
|
|
71
72
|
sideEffects: false,
|
|
72
73
|
});
|
|
73
74
|
function cpuAcos(value) {
|
|
75
|
+
assertKind(value, floatKind);
|
|
74
76
|
return generalizeFn(Math.acos, [value]);
|
|
75
77
|
}
|
|
76
78
|
export const acos = dualImpl({
|
|
@@ -81,6 +83,7 @@ export const acos = dualImpl({
|
|
|
81
83
|
sideEffects: false,
|
|
82
84
|
});
|
|
83
85
|
function cpuAcosh(value) {
|
|
86
|
+
assertKind(value, floatKind);
|
|
84
87
|
return generalizeFn(Math.acosh, [value]);
|
|
85
88
|
}
|
|
86
89
|
export const acosh = dualImpl({
|
|
@@ -91,6 +94,7 @@ export const acosh = dualImpl({
|
|
|
91
94
|
sideEffects: false,
|
|
92
95
|
});
|
|
93
96
|
function cpuAsin(value) {
|
|
97
|
+
assertKind(value, floatKind);
|
|
94
98
|
return generalizeFn(Math.asin, [value]);
|
|
95
99
|
}
|
|
96
100
|
export const asin = dualImpl({
|
|
@@ -101,6 +105,7 @@ export const asin = dualImpl({
|
|
|
101
105
|
sideEffects: false,
|
|
102
106
|
});
|
|
103
107
|
function cpuAsinh(value) {
|
|
108
|
+
assertKind(value, floatKind);
|
|
104
109
|
return generalizeFn(Math.asinh, [value]);
|
|
105
110
|
}
|
|
106
111
|
export const asinh = dualImpl({
|
|
@@ -111,6 +116,7 @@ export const asinh = dualImpl({
|
|
|
111
116
|
sideEffects: false,
|
|
112
117
|
});
|
|
113
118
|
function cpuAtan(value) {
|
|
119
|
+
assertKind(value, floatKind);
|
|
114
120
|
return generalizeFn(Math.atan, [value]);
|
|
115
121
|
}
|
|
116
122
|
export const atan = dualImpl({
|
|
@@ -121,6 +127,7 @@ export const atan = dualImpl({
|
|
|
121
127
|
sideEffects: false,
|
|
122
128
|
});
|
|
123
129
|
function cpuAtanh(value) {
|
|
130
|
+
assertKind(value, floatKind);
|
|
124
131
|
return generalizeFn(Math.atanh, [value]);
|
|
125
132
|
}
|
|
126
133
|
export const atanh = dualImpl({
|
|
@@ -131,6 +138,8 @@ export const atanh = dualImpl({
|
|
|
131
138
|
sideEffects: false,
|
|
132
139
|
});
|
|
133
140
|
function cpuAtan2(y, x) {
|
|
141
|
+
assertKind([y, x], floatKind);
|
|
142
|
+
assertEqualKinds(y, x);
|
|
134
143
|
return generalizeFn(Math.atan2, [y, x]);
|
|
135
144
|
}
|
|
136
145
|
export const atan2 = dualImpl({
|
|
@@ -141,6 +150,7 @@ export const atan2 = dualImpl({
|
|
|
141
150
|
sideEffects: false,
|
|
142
151
|
});
|
|
143
152
|
function cpuCeil(value) {
|
|
153
|
+
assertKind(value, floatKind);
|
|
144
154
|
return generalizeFn(Math.ceil, [value]);
|
|
145
155
|
}
|
|
146
156
|
export const ceil = dualImpl({
|
|
@@ -151,6 +161,8 @@ export const ceil = dualImpl({
|
|
|
151
161
|
sideEffects: false,
|
|
152
162
|
});
|
|
153
163
|
function cpuClamp(value, low, high) {
|
|
164
|
+
assertKind([value, low, high], numericKind);
|
|
165
|
+
assertEqualKinds(value, low, high);
|
|
154
166
|
return generalizeFn(clampScalar, [value, low, high]);
|
|
155
167
|
}
|
|
156
168
|
export const clamp = dualImpl({
|
|
@@ -161,6 +173,7 @@ export const clamp = dualImpl({
|
|
|
161
173
|
sideEffects: false,
|
|
162
174
|
});
|
|
163
175
|
function cpuCos(value) {
|
|
176
|
+
assertKind(value, floatKind);
|
|
164
177
|
return generalizeFn(Math.cos, [value]);
|
|
165
178
|
}
|
|
166
179
|
export const cos = dualImpl({
|
|
@@ -171,6 +184,7 @@ export const cos = dualImpl({
|
|
|
171
184
|
sideEffects: false,
|
|
172
185
|
});
|
|
173
186
|
function cpuCosh(value) {
|
|
187
|
+
assertKind(value, floatKind);
|
|
174
188
|
return generalizeFn(Math.cosh, [value]);
|
|
175
189
|
}
|
|
176
190
|
export const cosh = dualImpl({
|
|
@@ -213,11 +227,16 @@ export const countTrailingZeros = dualImpl({
|
|
|
213
227
|
export const cross = dualImpl({
|
|
214
228
|
name: 'cross',
|
|
215
229
|
signature: unifyRestrictedSignature([vec3f, vec3h]),
|
|
216
|
-
normalImpl: (a, b) =>
|
|
230
|
+
normalImpl: (a, b) => {
|
|
231
|
+
assertKind([a, b], crossKind);
|
|
232
|
+
assertEqualKinds(a, b);
|
|
233
|
+
return VectorOps.cross[a.kind](a, b);
|
|
234
|
+
},
|
|
217
235
|
codegenImpl: (_ctx, [a, b]) => stitch `cross(${a}, ${b})`,
|
|
218
236
|
sideEffects: false,
|
|
219
237
|
});
|
|
220
238
|
function cpuDegrees(value) {
|
|
239
|
+
assertKind(value, floatKind);
|
|
221
240
|
if (typeof value === 'number') {
|
|
222
241
|
return ((value * 180) / Math.PI);
|
|
223
242
|
}
|
|
@@ -243,6 +262,8 @@ export const determinant = dualImpl({
|
|
|
243
262
|
sideEffects: false,
|
|
244
263
|
});
|
|
245
264
|
function cpuDistance(a, b) {
|
|
265
|
+
assertKind([a, b], floatKind);
|
|
266
|
+
assertEqualKinds(a, b);
|
|
246
267
|
if (typeof a === 'number' && typeof b === 'number') {
|
|
247
268
|
return Math.abs(a - b);
|
|
248
269
|
}
|
|
@@ -270,7 +291,11 @@ export const dot = dualImpl({
|
|
|
270
291
|
argTypes: args,
|
|
271
292
|
returnType: args[0].primitive,
|
|
272
293
|
}),
|
|
273
|
-
normalImpl: (lhs, rhs) =>
|
|
294
|
+
normalImpl: (lhs, rhs) => {
|
|
295
|
+
assertKind([lhs, rhs], numericKind, true);
|
|
296
|
+
assertEqualKinds(lhs, rhs);
|
|
297
|
+
return VectorOps.dot[lhs.kind](lhs, rhs);
|
|
298
|
+
},
|
|
274
299
|
codegenImpl: (_ctx, [lhs, rhs]) => stitch `dot(${lhs}, ${rhs})`,
|
|
275
300
|
sideEffects: false,
|
|
276
301
|
});
|
|
@@ -289,6 +314,7 @@ export const dot4I8Packed = dualImpl({
|
|
|
289
314
|
sideEffects: false,
|
|
290
315
|
});
|
|
291
316
|
function cpuExp(value) {
|
|
317
|
+
assertKind(value, floatKind);
|
|
292
318
|
return generalizeFn(Math.exp, [value]);
|
|
293
319
|
}
|
|
294
320
|
export const exp = dualImpl({
|
|
@@ -299,6 +325,7 @@ export const exp = dualImpl({
|
|
|
299
325
|
sideEffects: false,
|
|
300
326
|
});
|
|
301
327
|
function cpuExp2(value) {
|
|
328
|
+
assertKind(value, floatKind);
|
|
302
329
|
return generalizeFn((val) => 2 ** val, [value]);
|
|
303
330
|
}
|
|
304
331
|
export const exp2 = dualImpl({
|
|
@@ -355,6 +382,7 @@ export const firstTrailingBit = dualImpl({
|
|
|
355
382
|
sideEffects: false,
|
|
356
383
|
});
|
|
357
384
|
function cpuFloor(value) {
|
|
385
|
+
assertKind(value, floatKind);
|
|
358
386
|
return generalizeFn(Math.floor, [value]);
|
|
359
387
|
}
|
|
360
388
|
export const floor = dualImpl({
|
|
@@ -365,6 +393,8 @@ export const floor = dualImpl({
|
|
|
365
393
|
sideEffects: false,
|
|
366
394
|
});
|
|
367
395
|
function cpuFma(e1, e2, e3) {
|
|
396
|
+
assertKind([e1, e2, e3], floatKind);
|
|
397
|
+
assertEqualKinds(e1, e2, e3);
|
|
368
398
|
if (typeof e1 === 'number') {
|
|
369
399
|
return (e1 * e2 + e3);
|
|
370
400
|
}
|
|
@@ -378,6 +408,7 @@ export const fma = dualImpl({
|
|
|
378
408
|
sideEffects: false,
|
|
379
409
|
});
|
|
380
410
|
function cpuFract(value) {
|
|
411
|
+
assertKind(value, floatKind);
|
|
381
412
|
return generalizeFn((value) => value - Math.floor(value), [value]);
|
|
382
413
|
}
|
|
383
414
|
export const fract = dualImpl({
|
|
@@ -431,6 +462,7 @@ export const insertBits = dualImpl({
|
|
|
431
462
|
sideEffects: false,
|
|
432
463
|
});
|
|
433
464
|
function cpuInverseSqrt(value) {
|
|
465
|
+
assertKind(value, floatKind);
|
|
434
466
|
if (typeof value === 'number') {
|
|
435
467
|
return (1 / Math.sqrt(value));
|
|
436
468
|
}
|
|
@@ -440,7 +472,7 @@ export const inverseSqrt = dualImpl({
|
|
|
440
472
|
name: 'inverseSqrt',
|
|
441
473
|
signature: unifyRestrictedSignature(anyFloat),
|
|
442
474
|
normalImpl: cpuInverseSqrt,
|
|
443
|
-
codegenImpl: (
|
|
475
|
+
codegenImpl: (ctx, [value]) => ctx.gen.emitCall('inverseSqrt', [], [value]),
|
|
444
476
|
sideEffects: false,
|
|
445
477
|
});
|
|
446
478
|
function cpuLdexp(_e1, _e2) {
|
|
@@ -473,6 +505,7 @@ export const ldexp = dualImpl({
|
|
|
473
505
|
sideEffects: false,
|
|
474
506
|
});
|
|
475
507
|
function cpuLength(value) {
|
|
508
|
+
assertKind(value, floatKind);
|
|
476
509
|
if (typeof value === 'number') {
|
|
477
510
|
return Math.abs(value);
|
|
478
511
|
}
|
|
@@ -495,6 +528,7 @@ export const length = dualImpl({
|
|
|
495
528
|
sideEffects: false,
|
|
496
529
|
});
|
|
497
530
|
function cpuLog(value) {
|
|
531
|
+
assertKind(value, floatKind);
|
|
498
532
|
return generalizeFn(Math.log, [value]);
|
|
499
533
|
}
|
|
500
534
|
export const log = dualImpl({
|
|
@@ -505,6 +539,7 @@ export const log = dualImpl({
|
|
|
505
539
|
sideEffects: false,
|
|
506
540
|
});
|
|
507
541
|
function cpuLog2(value) {
|
|
542
|
+
assertKind(value, floatKind);
|
|
508
543
|
return generalizeFn(Math.log2, [value]);
|
|
509
544
|
}
|
|
510
545
|
export const log2 = dualImpl({
|
|
@@ -515,6 +550,8 @@ export const log2 = dualImpl({
|
|
|
515
550
|
sideEffects: false,
|
|
516
551
|
});
|
|
517
552
|
function cpuMax(a, b) {
|
|
553
|
+
assertKind([a, b], numericKind);
|
|
554
|
+
assertEqualKinds(a, b);
|
|
518
555
|
return generalizeFn(Math.max, [a, b]);
|
|
519
556
|
}
|
|
520
557
|
export const max = dualImpl({
|
|
@@ -525,6 +562,8 @@ export const max = dualImpl({
|
|
|
525
562
|
sideEffects: false,
|
|
526
563
|
});
|
|
527
564
|
function cpuMin(a, b) {
|
|
565
|
+
assertKind([a, b], numericKind);
|
|
566
|
+
assertEqualKinds(a, b);
|
|
528
567
|
return generalizeFn(Math.min, [a, b]);
|
|
529
568
|
}
|
|
530
569
|
export const min = dualImpl({
|
|
@@ -535,6 +574,13 @@ export const min = dualImpl({
|
|
|
535
574
|
sideEffects: false,
|
|
536
575
|
});
|
|
537
576
|
function cpuMix(e1, e2, e3) {
|
|
577
|
+
assertKind([e1, e2, e3], floatKind);
|
|
578
|
+
if (typeof e3 === 'number') {
|
|
579
|
+
assertEqualKinds(e1, e2);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
assertEqualKinds(e1, e2, e3);
|
|
583
|
+
}
|
|
538
584
|
return generalizeFn((e1, e2, e3) => e1 * (1 - e3) + e2 * e3, [e1, ...upCast([e2, e3])]);
|
|
539
585
|
}
|
|
540
586
|
export const mix = dualImpl({
|
|
@@ -588,6 +634,7 @@ export const normalize = dualImpl({
|
|
|
588
634
|
name: 'normalize',
|
|
589
635
|
signature: unifyRestrictedSignature(anyFloatVec),
|
|
590
636
|
normalImpl: (v) => {
|
|
637
|
+
assertKind(v, floatKind);
|
|
591
638
|
const len = length(v);
|
|
592
639
|
return generalizeFn((e) => e / len, [v]);
|
|
593
640
|
},
|
|
@@ -595,6 +642,8 @@ export const normalize = dualImpl({
|
|
|
595
642
|
sideEffects: false,
|
|
596
643
|
});
|
|
597
644
|
function powCpu(base, exponent) {
|
|
645
|
+
assertKind([base, exponent], floatKind);
|
|
646
|
+
assertEqualKinds(base, exponent);
|
|
598
647
|
return generalizeFn((a, b) => a ** b, [base, exponent]);
|
|
599
648
|
}
|
|
600
649
|
export const pow = dualImpl({
|
|
@@ -622,6 +671,7 @@ export const quantizeToF16 = dualImpl({
|
|
|
622
671
|
sideEffects: false,
|
|
623
672
|
});
|
|
624
673
|
function cpuRadians(value) {
|
|
674
|
+
assertKind(value, floatKind);
|
|
625
675
|
if (typeof value === 'number') {
|
|
626
676
|
return ((value * Math.PI) / 180);
|
|
627
677
|
}
|
|
@@ -646,7 +696,11 @@ export const reflect = dualImpl({
|
|
|
646
696
|
returnType: uargs[0],
|
|
647
697
|
};
|
|
648
698
|
},
|
|
649
|
-
normalImpl: (e1, e2) =>
|
|
699
|
+
normalImpl: (e1, e2) => {
|
|
700
|
+
assertKind([e1, e2], floatKind, true);
|
|
701
|
+
assertEqualKinds(e1, e2);
|
|
702
|
+
return sub(e1, mul(2 * dot(e2, e1), e2));
|
|
703
|
+
},
|
|
650
704
|
codegenImpl: (_ctx, [e1, e2]) => stitch `reflect(${e1}, ${e2})`,
|
|
651
705
|
sideEffects: false,
|
|
652
706
|
});
|
|
@@ -671,6 +725,7 @@ export const reverseBits = dualImpl({
|
|
|
671
725
|
sideEffects: false,
|
|
672
726
|
});
|
|
673
727
|
function cpuRound(value) {
|
|
728
|
+
assertKind(value, floatKind);
|
|
674
729
|
if (typeof value === 'number') {
|
|
675
730
|
const floor = Math.floor(value);
|
|
676
731
|
if (value === floor + 0.5) {
|
|
@@ -691,6 +746,7 @@ export const round = dualImpl({
|
|
|
691
746
|
sideEffects: false,
|
|
692
747
|
});
|
|
693
748
|
function cpuSaturate(value) {
|
|
749
|
+
assertKind(value, floatKind);
|
|
694
750
|
if (typeof value === 'number') {
|
|
695
751
|
return Math.max(0, Math.min(1, value));
|
|
696
752
|
}
|
|
@@ -704,6 +760,7 @@ export const saturate = dualImpl({
|
|
|
704
760
|
sideEffects: false,
|
|
705
761
|
});
|
|
706
762
|
function cpuSign(e) {
|
|
763
|
+
assertKind(e, signedKind);
|
|
707
764
|
return generalizeFn(Math.sign, [e]);
|
|
708
765
|
}
|
|
709
766
|
export const sign = dualImpl({
|
|
@@ -721,6 +778,7 @@ export const sign = dualImpl({
|
|
|
721
778
|
sideEffects: false,
|
|
722
779
|
});
|
|
723
780
|
function cpuSin(value) {
|
|
781
|
+
assertKind(value, floatKind);
|
|
724
782
|
return generalizeFn(Math.sin, [value]);
|
|
725
783
|
}
|
|
726
784
|
export const sin = dualImpl({
|
|
@@ -731,6 +789,7 @@ export const sin = dualImpl({
|
|
|
731
789
|
sideEffects: false,
|
|
732
790
|
});
|
|
733
791
|
function cpuSinh(value) {
|
|
792
|
+
assertKind(value, floatKind);
|
|
734
793
|
return generalizeFn(Math.sinh, [value]);
|
|
735
794
|
}
|
|
736
795
|
export const sinh = dualImpl({
|
|
@@ -741,6 +800,8 @@ export const sinh = dualImpl({
|
|
|
741
800
|
sideEffects: false,
|
|
742
801
|
});
|
|
743
802
|
function cpuSmoothstep(edge0, edge1, x) {
|
|
803
|
+
assertKind([edge0, edge1, x], floatKind);
|
|
804
|
+
assertEqualKinds(edge0, edge1, x);
|
|
744
805
|
return generalizeFn(smoothstepScalar, [edge0, edge1, x]);
|
|
745
806
|
}
|
|
746
807
|
export const smoothstep = dualImpl({
|
|
@@ -751,6 +812,7 @@ export const smoothstep = dualImpl({
|
|
|
751
812
|
sideEffects: false,
|
|
752
813
|
});
|
|
753
814
|
function cpuSqrt(value) {
|
|
815
|
+
assertKind(value, floatKind);
|
|
754
816
|
return generalizeFn(Math.sqrt, [value]);
|
|
755
817
|
}
|
|
756
818
|
export const sqrt = dualImpl({
|
|
@@ -761,6 +823,8 @@ export const sqrt = dualImpl({
|
|
|
761
823
|
sideEffects: false,
|
|
762
824
|
});
|
|
763
825
|
function cpuStep(edge, x) {
|
|
826
|
+
assertKind([edge, x], floatKind);
|
|
827
|
+
assertEqualKinds(edge, x);
|
|
764
828
|
if (typeof edge === 'number') {
|
|
765
829
|
return (edge <= x ? 1.0 : 0.0);
|
|
766
830
|
}
|
|
@@ -774,6 +838,7 @@ export const step = dualImpl({
|
|
|
774
838
|
sideEffects: false,
|
|
775
839
|
});
|
|
776
840
|
function cpuTan(value) {
|
|
841
|
+
assertKind(value, floatKind);
|
|
777
842
|
if (typeof value === 'number') {
|
|
778
843
|
return Math.tan(value);
|
|
779
844
|
}
|
|
@@ -787,6 +852,7 @@ export const tan = dualImpl({
|
|
|
787
852
|
sideEffects: false,
|
|
788
853
|
});
|
|
789
854
|
function cpuTanh(value) {
|
|
855
|
+
assertKind(value, floatKind);
|
|
790
856
|
return generalizeFn(Math.tanh, [value]);
|
|
791
857
|
}
|
|
792
858
|
export const tanh = dualImpl({
|
|
@@ -797,6 +863,7 @@ export const tanh = dualImpl({
|
|
|
797
863
|
sideEffects: false,
|
|
798
864
|
});
|
|
799
865
|
function cpuTranspose(value) {
|
|
866
|
+
assertKind(value, matrixKind);
|
|
800
867
|
const schema = WORKAROUND_getSchema(value);
|
|
801
868
|
// NOTE: This assumes all matrices are square
|
|
802
869
|
const transposed = schema();
|
package/std/operators.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AnyIntegerVecInstance, type AnyMatInstance, type AnyNumericVecInstance, type mBaseForVec, type vBaseForMat, type vecIToVecU } from '../data/wgslTypes.ts';
|
|
1
|
+
import { type AnyIntegerVecInstance, type AnyMatInstance, type AnyNumericVecInstance, type AnySignedVecInstance, type mBaseForVec, type vBaseForMat, type vecIToVecU } from '../data/wgslTypes.ts';
|
|
2
2
|
type NumVec = AnyNumericVecInstance;
|
|
3
3
|
type Mat = AnyMatInstance;
|
|
4
4
|
declare function cpuAdd(lhs: number, rhs: number): number;
|
|
@@ -39,7 +39,7 @@ type ModOverload = {
|
|
|
39
39
|
*/
|
|
40
40
|
export declare const mod: import("../types.ts").DualFn<ModOverload>;
|
|
41
41
|
declare function cpuNeg(value: number): number;
|
|
42
|
-
declare function cpuNeg<T extends
|
|
42
|
+
declare function cpuNeg<T extends AnySignedVecInstance>(value: T): T;
|
|
43
43
|
export declare const neg: import("../types.ts").DualFn<typeof cpuNeg>;
|
|
44
44
|
declare function cpuBitShiftLeft<T extends AnyIntegerVecInstance>(lhs: T, rhs: number | vecIToVecU<T>): T;
|
|
45
45
|
export declare const bitShiftLeft: import("../types.ts").DualFn<typeof cpuBitShiftLeft>;
|
package/std/operators.js
CHANGED
|
@@ -3,9 +3,9 @@ import { stitch } from "../core/resolve/stitch.js";
|
|
|
3
3
|
import { abstractFloat, f16, f32, u32 } from "../data/numeric.js";
|
|
4
4
|
import { vec2i, vec2u, vec3i, vec3u, vec4i, vec4u } from "../data/vector.js";
|
|
5
5
|
import { VectorOps } from "../data/vectorOps.js";
|
|
6
|
-
import { generalizeFn, upCast } from "../data/generalizeFn.js";
|
|
7
|
-
import { isFloat32VecInstance,
|
|
8
|
-
import { SignatureNotSupportedError } from "../errors.js";
|
|
6
|
+
import { generalizeFn, kindOf, numericKind, numericOrMatrixKind, signedKind, upCast, assertEqualKinds, assertKind, } from "../data/generalizeFn.js";
|
|
7
|
+
import { isFloat32VecInstance, isMat, isMatInstance, isVec, isVecInstance, isInteger32VecInstance, isUint32VecInstance, } from "../data/wgslTypes.js";
|
|
8
|
+
import { SignatureNotSupportedError, WgslTypeError } from "../errors.js";
|
|
9
9
|
import { unify } from "../tgsl/conversion.js";
|
|
10
10
|
const getPrimitive = (t) => ('primitive' in t ? t.primitive : t);
|
|
11
11
|
const makeBinarySignature = (opts) => (lhs, rhs) => {
|
|
@@ -56,19 +56,15 @@ const binaryDivSignature = makeBinarySignature({
|
|
|
56
56
|
restrict: [f32, f16, abstractFloat],
|
|
57
57
|
});
|
|
58
58
|
function cpuAdd(lhs, rhs) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (typeof lhs === 'number' && isVecInstance(rhs)) {
|
|
63
|
-
return generalizeFn((e) => lhs + e, [rhs]); // mixed addition
|
|
64
|
-
}
|
|
65
|
-
if (isVecInstance(lhs) && typeof rhs === 'number') {
|
|
66
|
-
return generalizeFn((e) => e + rhs, [lhs]); // mixed addition
|
|
59
|
+
assertKind([lhs, rhs], numericOrMatrixKind);
|
|
60
|
+
if (isMatInstance(lhs) !== isMatInstance(rhs)) {
|
|
61
|
+
throw new WgslTypeError('There is no matrix/non-matrix addition or subtraction in WGSL.');
|
|
67
62
|
}
|
|
68
|
-
if ((
|
|
69
|
-
|
|
63
|
+
if ((typeof lhs === 'number') === (typeof rhs === 'number')) {
|
|
64
|
+
// If exactly one is a number, then it's fine, since we already know the other one is not a matrix.
|
|
65
|
+
assertEqualKinds(lhs, rhs);
|
|
70
66
|
}
|
|
71
|
-
|
|
67
|
+
return generalizeFn((a, b) => a + b, upCast([lhs, rhs]));
|
|
72
68
|
}
|
|
73
69
|
export const add = dualImpl({
|
|
74
70
|
name: 'add',
|
|
@@ -89,6 +85,7 @@ export const sub = dualImpl({
|
|
|
89
85
|
sideEffects: false,
|
|
90
86
|
});
|
|
91
87
|
function cpuMul(lhs, rhs) {
|
|
88
|
+
assertKind([lhs, rhs], numericOrMatrixKind);
|
|
92
89
|
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
|
93
90
|
return lhs * rhs; // default multiplication
|
|
94
91
|
}
|
|
@@ -99,18 +96,26 @@ function cpuMul(lhs, rhs) {
|
|
|
99
96
|
return generalizeFn((e) => e * rhs, [lhs]); // scale
|
|
100
97
|
}
|
|
101
98
|
if (isVecInstance(lhs) && isVecInstance(rhs)) {
|
|
99
|
+
assertEqualKinds(lhs, rhs);
|
|
102
100
|
return generalizeFn((a, b) => a * b, [lhs, rhs]); // component-wise
|
|
103
101
|
}
|
|
104
102
|
if (isFloat32VecInstance(lhs) && isMatInstance(rhs)) {
|
|
103
|
+
if (lhs.length !== rhs.columns.length) {
|
|
104
|
+
throw new WgslTypeError(`Unsupported signature. Kind '${kindOf(lhs)}' cannot be multiplied by '${kindOf(rhs)}'.`);
|
|
105
|
+
}
|
|
105
106
|
return VectorOps.mulVxM[rhs.kind](lhs, rhs); // row-vector-matrix
|
|
106
107
|
}
|
|
107
108
|
if (isMatInstance(lhs) && isFloat32VecInstance(rhs)) {
|
|
109
|
+
if (lhs.columns.length !== rhs.length) {
|
|
110
|
+
throw new WgslTypeError(`Unsupported signature. Kind '${kindOf(lhs)}' cannot be multiplied by '${kindOf(rhs)}'.`);
|
|
111
|
+
}
|
|
108
112
|
return VectorOps.mulMxV[lhs.kind](lhs, rhs); // matrix-column-vector
|
|
109
113
|
}
|
|
110
114
|
if (isMatInstance(lhs) && isMatInstance(rhs)) {
|
|
115
|
+
assertEqualKinds(lhs, rhs);
|
|
111
116
|
return VectorOps.mulMxM[lhs.kind](lhs, rhs); // matrix multiplication
|
|
112
117
|
}
|
|
113
|
-
throw new
|
|
118
|
+
throw new WgslTypeError(`Unsupported signature. Kind '${kindOf(lhs)}' cannot be multiplied by '${kindOf(rhs)}'.`);
|
|
114
119
|
}
|
|
115
120
|
export const mul = dualImpl({
|
|
116
121
|
name: 'mul',
|
|
@@ -120,7 +125,10 @@ export const mul = dualImpl({
|
|
|
120
125
|
sideEffects: false,
|
|
121
126
|
});
|
|
122
127
|
function cpuDiv(lhs, rhs) {
|
|
123
|
-
|
|
128
|
+
assertKind([lhs, rhs], numericKind);
|
|
129
|
+
const cast = upCast([lhs, rhs]);
|
|
130
|
+
assertEqualKinds(...cast);
|
|
131
|
+
return generalizeFn((a, b) => a / b, cast);
|
|
124
132
|
}
|
|
125
133
|
export const div = dualImpl({
|
|
126
134
|
name: 'div',
|
|
@@ -138,12 +146,16 @@ export const mod = dualImpl({
|
|
|
138
146
|
name: 'mod',
|
|
139
147
|
signature: binaryDivSignature,
|
|
140
148
|
normalImpl: ((a, b) => {
|
|
141
|
-
|
|
149
|
+
assertKind([a, b], numericKind);
|
|
150
|
+
const cast = upCast([a, b]);
|
|
151
|
+
assertEqualKinds(...cast);
|
|
152
|
+
return generalizeFn((a, b) => a % b, cast);
|
|
142
153
|
}),
|
|
143
154
|
codegenImpl: (ctx, [lhs, rhs]) => ctx.gen.emitBinaryOp(lhs, '%', rhs),
|
|
144
155
|
sideEffects: false,
|
|
145
156
|
});
|
|
146
157
|
function cpuNeg(value) {
|
|
158
|
+
assertKind(value, signedKind);
|
|
147
159
|
return generalizeFn((value) => -value, [value]);
|
|
148
160
|
}
|
|
149
161
|
export const neg = dualImpl({
|