Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
7d95880
POC
aleksanderkatan Aug 13, 2026
b3edb44
Add type verification
aleksanderkatan Aug 13, 2026
20caf75
Update sqrt
aleksanderkatan Aug 13, 2026
2b8a614
Update trigo functions
aleksanderkatan Aug 13, 2026
369a70f
Remove remaining simple unary ops
aleksanderkatan Aug 13, 2026
998783e
Support matrices, remove remaining unary calls from vectorOps
aleksanderkatan Aug 13, 2026
14e08f8
Add binary op
aleksanderkatan Aug 13, 2026
2eeda52
Add verifyEqualTypes
aleksanderkatan Aug 13, 2026
e616432
Allow for upcasts
aleksanderkatan Aug 13, 2026
4407a2a
Rewrite simple component-wise ops
aleksanderkatan Aug 13, 2026
6119404
Add boolean mode
aleksanderkatan Aug 13, 2026
c7d4704
Simplify binary vector ops
aleksanderkatan Aug 13, 2026
a270fe8
Remove normalize from vectorOps
aleksanderkatan Aug 13, 2026
ce3a45a
Accept an array of arguments
aleksanderkatan Aug 13, 2026
a228ea2
Extract upcast, change mode to enum
aleksanderkatan Aug 13, 2026
7fcdf91
Prepare binaryuniformInput for generalization
aleksanderkatan Aug 13, 2026
96719fa
Make generalization work with any arity (except for types)
aleksanderkatan Aug 13, 2026
b45101a
Remove unary generalization
aleksanderkatan Aug 13, 2026
fd1fb88
Cleanup
aleksanderkatan Aug 13, 2026
5a99b4e
Add overload for 3 values
aleksanderkatan Aug 13, 2026
4144e69
Rename to generalizeFn
aleksanderkatan Aug 13, 2026
75374f9
Remove mix from vectorOps
aleksanderkatan Aug 13, 2026
46e52f0
Remove select from vectorOps
aleksanderkatan Aug 13, 2026
8c54d6a
Remove bitcasts from vectorOps
aleksanderkatan Aug 13, 2026
8ef19c5
Remove remaining simple vector ops
aleksanderkatan Aug 13, 2026
d4d56c0
Clean up vector ops imports
aleksanderkatan Aug 13, 2026
40cf5de
Clean up vectorOps2 types
aleksanderkatan Aug 13, 2026
2857f03
Fix test
aleksanderkatan Aug 13, 2026
446e12d
Cleanup
aleksanderkatan Aug 14, 2026
779007e
Generalize verifyKind for multiple arguments
aleksanderkatan Aug 14, 2026
50966bc
Refactor generalizeFn.ts
aleksanderkatan Aug 14, 2026
3367a2e
Simplify types
aleksanderkatan Aug 14, 2026
88423ed
Simplify types
aleksanderkatan Aug 14, 2026
a2dd774
Simplify types even further
aleksanderkatan Aug 14, 2026
080788a
More cleanup
aleksanderkatan Aug 14, 2026
c3a3a00
Remove type verification
aleksanderkatan Aug 14, 2026
a953c5a
Update snapshot
aleksanderkatan Aug 14, 2026
9b5e1eb
Merge remote-tracking branch 'origin/main' into impr/clean-up-vectorops
aleksanderkatan Aug 14, 2026
0348be6
Revert "Remove type verification"
aleksanderkatan Aug 14, 2026
cd13b03
Update existing tests to handle throws
aleksanderkatan Aug 14, 2026
6f63e19
Cleanup divInteger
aleksanderkatan Aug 14, 2026
2ca23b7
Revert bitcasts
aleksanderkatan Aug 14, 2026
cf3e123
Fix isCloseTo
aleksanderkatan Aug 14, 2026
cfee895
Revert changes to isCloseTo
aleksanderkatan Aug 17, 2026
991c5c1
nr fix
aleksanderkatan Aug 17, 2026
3ee701a
Update bitcasts
aleksanderkatan Aug 17, 2026
8c2528f
Update boolean
aleksanderkatan Aug 17, 2026
e7257d9
Update numeric
aleksanderkatan Aug 17, 2026
dd92b46
Update operators
aleksanderkatan Aug 17, 2026
c302ecc
Update generalizeFn
aleksanderkatan Aug 17, 2026
eee7d6a
Add some coverage tests
aleksanderkatan Aug 18, 2026
9d5a33b
Add missing std tests
aleksanderkatan Aug 18, 2026
590690b
Add select tests
aleksanderkatan Aug 18, 2026
abff7c2
Update snapshots
aleksanderkatan Aug 18, 2026
9adf8fe
Inline matrix error
aleksanderkatan Aug 18, 2026
2ffcd6d
Change errors to wgslTypeErrors
aleksanderkatan Aug 18, 2026
99ee430
Simplify cpuNot
aleksanderkatan Aug 18, 2026
9d8e65d
Generalize all
aleksanderkatan Aug 18, 2026
7e62cc6
Rename kinds
aleksanderkatan Aug 18, 2026
4d7a044
Update error messages
aleksanderkatan Aug 18, 2026
78fffbb
nr fix
aleksanderkatan Aug 18, 2026
68fe2a1
🔧
aleksanderkatan Aug 18, 2026
a3691fb
Merge branch 'impr/clean-up-vectorops' into chore/add-type-checks-to-std
aleksanderkatan Aug 18, 2026
8f4a717
Merge branch 'main' into impr/clean-up-vectorops
aleksanderkatan Aug 19, 2026
3ff3d40
Fix normalize
aleksanderkatan Aug 19, 2026
bfa48f5
Remove array creation from applyArgs
aleksanderkatan Aug 19, 2026
5e5d55f
🦖
aleksanderkatan Aug 19, 2026
cf118e2
Fix scalar handling
aleksanderkatan Aug 19, 2026
7611d7e
Remove unused kind sets
aleksanderkatan Aug 19, 2026
9c678a8
Add mul kind verification
aleksanderkatan Aug 19, 2026
a714625
nr fix
aleksanderkatan Aug 19, 2026
84667d0
Merge branch 'impr/clean-up-vectorops' into chore/add-type-checks-to-std
aleksanderkatan Aug 19, 2026
d4631b8
Merge remote-tracking branch 'origin/main' into chore/add-type-checks…
aleksanderkatan Aug 25, 2026
bd78da4
Update error messages
aleksanderkatan Aug 25, 2026
3dca480
Update packages/typegpu/src/data/generalizeFn.ts
aleksanderkatan Aug 27, 2026
cdd64a1
Merge remote-tracking branch 'origin/main' into chore/add-type-checks…
aleksanderkatan Sep 3, 2026
03ffb32
Update snapshots
aleksanderkatan Sep 3, 2026
b9be3a4
Merge branch 'main' into chore/add-type-checks-to-std
aleksanderkatan Sep 7, 2026
8d57e9f
Rename verify functions to assert
aleksanderkatan Sep 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions packages/typegpu/src/data/generalizeFn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
type v3b,
type v4b,
} from './wgslTypes.ts';
import { invariant } from '../errors.ts';
import { invariant, WgslTypeError } from '../errors.ts';

type Vec = AnyVecInstance; // alias
type Mat = AnyMatInstance; // alias
Expand Down Expand Up @@ -117,6 +117,10 @@ export function generalizeFn<T extends Algebraic>(fn: (...args: number[]) => num
/**
* Analogous to `generalizeFn`, but the return type is a boolean vector instead.
*/
export function generalizeBoolFn<T extends boolean | AnyBooleanVecInstance>(
fn: (a: boolean) => boolean,
args: [T],
): T;
export function generalizeBoolFn<T extends Algebraic>(
fn: (a: number, b: number) => boolean,
args: [T, T],
Expand All @@ -132,7 +136,7 @@ export function generalizeBoolFn<T extends Algebraic>(
return applyArgs(fn, args, 'boolean') as ToBool<T>;
}

function kindOf(v: Algebraic): Kind {
export function kindOf(v: Algebraic): Kind {
if (typeof v === 'number') {
return 'number';
}
Expand All @@ -142,11 +146,61 @@ function kindOf(v: Algebraic): Kind {
return v.kind;
}

// Unless matrix is mentioned in the name, it is not included.
const i32Kind: Set<Kind> = new Set(['number', 'vec2i', 'vec3i', 'vec4i']);
Comment thread
aleksanderkatan marked this conversation as resolved.
Comment thread
aleksanderkatan marked this conversation as resolved.
export const u32Kind: Set<Kind> = new Set(['number', 'vec2u', 'vec3u', 'vec4u']);
export const f32Kind: Set<Kind> = new Set(['number', 'vec2f', 'vec3f', 'vec4f']);
export const f16Kind: Set<Kind> = new Set(['number', 'vec2h', 'vec3h', 'vec4h']);
export const matrixKind: Set<Kind> = new Set(['mat2x2f', 'mat3x3f', 'mat4x4f']);
export const booleanKind: Set<Kind> = new Set([
'boolean',
'vec2<bool>',
'vec3<bool>',
'vec4<bool>',
]);
export const floatKind: Set<Kind> = new Set([...f32Kind, ...f16Kind]);
export const signedKind: Set<Kind> = new Set([...i32Kind, ...f32Kind, ...f16Kind]);
export const numericKind: Set<Kind> = new Set([...signedKind, ...u32Kind]);
export const numericOrBooleanKind: Set<Kind> = new Set([...numericKind, ...booleanKind]);
export const numericOrMatrixKind: Set<Kind> = new Set([...numericKind, ...matrixKind]);
export const crossKind: Set<Kind> = new Set(['vec3f', 'vec3h']);

export function assertKind(
v: Algebraic | Algebraic[],
valid: Set<Kind>,
excludeScalar: boolean = false,
) {
if (!isVecInstance(v) && Array.isArray(v)) {
v.forEach((item) => assertKind(item, valid, excludeScalar));
return;
}
const kind = kindOf(v);
if (!valid.has(kind)) {
throw new WgslTypeError(
`Unsupported signature. Expected one of '${[...valid].join(', ')}', got '${kind}'.`,
);
}
if (excludeScalar && (kind === 'number' || kind === 'boolean')) {
throw new WgslTypeError(
`Unsupported signature. Expected kind to not be scalar, got '${kind}'.`,
);
}
}

export function assertEqualKinds(...values: Algebraic[]) {
const kinds = new Set(values.map(kindOf));
if (kinds.size !== 1) {
throw new WgslTypeError(
`Unsupported signature. Expected the following kinds to be equal: '${[...kinds].join(', ')}'.`,
);
}
}

/**
* If one of the arguments is a vector and other is a number,
* the number is up-cased to a vector.
*/
export function upCast<T extends number | Vec>(
export function upCast<T extends number | Vec | Mat>(
args: [T, T],
): [Exclude<T, number>, Exclude<T, number>] {
const [lhs, rhs] = args;
Expand Down
4 changes: 4 additions & 0 deletions packages/typegpu/src/std/bitcast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { getName } from '../shared/meta.ts';
import type { Infer } from '../shared/repr.ts';
import { comptime } from '../core/function/comptime.ts';
import { coerceToSnippet } from '../tgsl/generationHelpers.ts';
import { f32Kind, u32Kind, assertKind } from '../data/generalizeFn.ts';

type BitcastU32toF32Overload = <T extends number | v2u | v3u | v4u>(
value: T,
Expand All @@ -59,6 +60,7 @@ const u32AllowedSchemas = [u32, vec2u, vec3u, vec4u];
export const bitcastU32toF32 = dualImpl({
name: 'bitcastU32toF32',
normalImpl: ((value) => {
assertKind(value, u32Kind);
if (typeof value === 'number') {
return bitcastU32toF32Impl(value);
}
Expand Down Expand Up @@ -96,6 +98,7 @@ type BitcastU32toI32Overload = <T extends number | v2u | v3u | v4u>(
export const bitcastU32toI32 = dualImpl({
name: 'bitcastU32toI32',
normalImpl: ((value) => {
assertKind(value, u32Kind);
if (typeof value === 'number') {
return bitcastU32toI32Impl(value);
}
Expand Down Expand Up @@ -135,6 +138,7 @@ const f32AllowedSchemas = [f32, vec2f, vec3f, vec4f];
export const bitcastF32toU32 = dualImpl({
name: 'bitcastF32toU32',
normalImpl: ((value) => {
assertKind(value, f32Kind);
if (typeof value === 'number') {
return bitcastF32toU32Impl(value);
}
Expand Down
73 changes: 47 additions & 26 deletions packages/typegpu/src/std/boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@ import {
vec4u,
} from '../data/vector.ts';
import { VectorOps } from '../data/vectorOps.ts';
import { generalizeBoolFn, generalizeFn } from '../data/generalizeFn.ts';
import {
booleanKind,
floatKind,
generalizeBoolFn,
generalizeFn,
kindOf,
numericKind,
numericOrBooleanKind,
assertEqualKinds,
assertKind,
} from '../data/generalizeFn.ts';
import {
type AnyBooleanVecInstance,
type AnyFloatVecInstance,
Expand All @@ -32,12 +42,12 @@ import {
type BaseData,
isBool,
isVecBool,
isVecBoolInstance,
isVecInstance,
type v2b,
type v3b,
type v4b,
} from '../data/wgslTypes.ts';
import { SignatureNotSupportedError } from '../errors.ts';
import { SignatureNotSupportedError, WgslTypeError } from '../errors.ts';
import { unify } from '../tgsl/conversion.ts';
import { cpuCopy } from './copy.ts';

Expand Down Expand Up @@ -68,8 +78,11 @@ export const allEq = dualImpl({
sideEffects: false,
});

const cpuEq = <T extends AnyVecInstance>(lhs: T, rhs: T) =>
generalizeBoolFn((a, b) => a === b, [lhs, rhs]);
const cpuEq = <T extends AnyVecInstance>(lhs: T, rhs: T) => {
assertKind([lhs, rhs], numericOrBooleanKind);
assertEqualKinds(lhs, rhs);
return generalizeBoolFn((a, b) => a === b, [lhs, rhs]);
};

/**
* Checks **component-wise** whether `lhs == rhs`.
Expand Down Expand Up @@ -110,8 +123,11 @@ export const ne = dualImpl({
sideEffects: false,
});

const cpuLt = <T extends AnyNumericVecInstance>(lhs: T, rhs: T) =>
generalizeBoolFn((a, b) => a < b, [lhs, rhs]);
const cpuLt = <T extends AnyNumericVecInstance>(lhs: T, rhs: T) => {
assertKind([lhs, rhs], numericKind);
assertEqualKinds(lhs, rhs);
return generalizeBoolFn((a, b) => a < b, [lhs, rhs]);
};

/**
* Checks **component-wise** whether `lhs < rhs`.
Expand Down Expand Up @@ -196,22 +212,8 @@ export const ge = dualImpl({
function cpuNot(value: boolean): boolean;
function cpuNot<T extends AnyBooleanVecInstance>(value: T): T;
function cpuNot<T extends AnyBooleanVecInstance | boolean>(value: T): T {
if (typeof value === 'boolean') {
return !value as T;
}

if (!isVecBoolInstance(value)) {
throw new Error(`'std.not' requires a boolean or boolean vector.`);
}

switch (value.length) {
case 2:
return vec2b(cpuNot(value.x), cpuNot(value.y)) as T;
case 3:
return vec3b(cpuNot(value.x), cpuNot(value.y), cpuNot(value.z)) as T;
case 4:
return vec4b(cpuNot(value.x), cpuNot(value.y), cpuNot(value.z), cpuNot(value.w)) as T;
}
assertKind(value, booleanKind);
return generalizeBoolFn((a: boolean) => !a, [value]);
}

/**
Expand Down Expand Up @@ -239,8 +241,11 @@ export const not = dualImpl({
sideEffects: false,
});

const cpuOr = <T extends AnyBooleanVecInstance>(lhs: T, rhs: T) =>
generalizeBoolFn((a: boolean, b: boolean) => a || b, [lhs, rhs]);
const cpuOr = <T extends AnyBooleanVecInstance>(lhs: T, rhs: T) => {
assertKind([lhs, rhs], booleanKind);
assertEqualKinds(lhs, rhs);
return generalizeBoolFn((a: boolean, b: boolean) => a || b, [lhs, rhs]);
};

/**
* Returns **component-wise** logical `or` result.
Expand Down Expand Up @@ -275,7 +280,13 @@ export const and = dualImpl({

// logical aggregation

const cpuAll = (value: AnyBooleanVecInstance) => VectorOps.all[value.kind](value);
const cpuAll = (value: boolean | AnyBooleanVecInstance) => {
assertKind(value, booleanKind);
if (typeof value === 'boolean') {
return value;
}
return VectorOps.all[value.kind](value);
};

/**
* Returns `true` if each component of `value` is true.
Expand Down Expand Up @@ -328,6 +339,8 @@ export const isCloseTo = dualImpl({
rhs: T,
precision = 0.01,
): boolean => {
assertKind([lhs, rhs], floatKind);
assertEqualKinds(lhs, rhs);
const componentResult = generalizeBoolFn(
(lhs, rhs) => Math.abs(lhs - rhs) < precision,
[lhs, rhs],
Expand Down Expand Up @@ -361,9 +374,17 @@ function cpuSelect<T extends number | boolean | AnyVecInstance>(
t: T,
cond: AnyBooleanVecInstance | boolean,
) {
assertKind([f, t], numericOrBooleanKind);
assertEqualKinds(f, t);
assertKind(cond, booleanKind);
if (typeof cond === 'boolean') {
return cpuCopy(cond ? t : f);
}
if (!isVecInstance(f) || f.length !== cond.length) {
throw new WgslTypeError(
`Select shape '(${kindOf(f)}, ${kindOf(t)}, ${kindOf(cond)})' is invalid.`,
);
}
// generalizeFn will handle this fine, it just has no mixed type overload.
return generalizeFn((f, t, c) => (c ? t : f), [f, t, cond as T]);
}
Expand Down
Loading
Loading