Skip to content

feat: Cast elements to correct type when creating vectors #1311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3a99bda
Add js impl for default numeric constructors
May 28, 2025
d44e296
Add wgsl impl for -||-
May 28, 2025
18eca1d
Add bool constructor impls
May 28, 2025
b951abd
Add `elementSchema` prop to Vec
May 28, 2025
8b9fe15
Push getDefaultValue down to VecBase
May 28, 2025
eeda11b
Add casting to vector constructors
May 28, 2025
871a27b
Remove `getDefaultValue`
May 28, 2025
9b06df0
Add tests for integer vector constructor casts
May 28, 2025
6577c7e
Merge remote-tracking branch 'origin/main' into feat/cast-elements-to…
May 28, 2025
125e229
Merge fixes
May 28, 2025
435ffb0
Add tests for scalar coercion
May 28, 2025
49d3904
Nits
May 28, 2025
86f275c
Merge branch 'main' into feat/cast-elements-to-correct-type-when-crea…
aleksanderkatan Jun 3, 2025
c4ee539
Merge branch 'main' into feat/cast-elements-to-correct-type-when-crea…
aleksanderkatan Jun 3, 2025
a9892cd
Remove failing test (it is removed anyway in #1319)
Jun 3, 2025
5d2eb79
Merge branch 'main' into feat/cast-elements-to-correct-type-when-crea…
aleksanderkatan Jun 4, 2025
1f3fd30
Merge remote-tracking branch 'origin/main' into feat/cast-elements-to…
Jun 6, 2025
6c05f9d
Merge branch 'main' into feat/cast-elements-to-correct-type-when-crea…
aleksanderkatan Jun 10, 2025
0b1be63
Simplify arrow functions, an->a
Jun 12, 2025
ec2f918
Merge remote-tracking branch 'origin/main' into feat/cast-elements-to…
Jun 17, 2025
c2c9204
Merge fixes
Jun 17, 2025
ca17ee8
Simplify all casts
Jun 17, 2025
69145e0
Corerce values on vector field assignment
Jun 17, 2025
89b285c
Merge branch 'main' into feat/cast-elements-to-correct-type-when-crea…
aleksanderkatan Jun 17, 2025
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
115 changes: 67 additions & 48 deletions packages/typegpu/src/data/numeric.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import bin from 'typed-binary';
import { createDualImpl } from '../shared/generators.ts';
import { $internal } from '../shared/symbols.ts';
import { snip } from './dataTypes.ts';
import type {
AbstractFloat,
AbstractInt,
Expand All @@ -10,7 +10,6 @@ import type {
I32,
U32,
} from './wgslTypes.ts';
import { snip } from './dataTypes.ts';

export const abstractInt = {
[$internal]: true,
Expand All @@ -22,39 +21,61 @@ export const abstractFloat = {
type: 'abstractFloat',
} as AbstractFloat;

const boolCast = createDualImpl(
// CPU implementation
(v?: number | boolean) => {
if (v === undefined) {
return false;
}
if (typeof v === 'boolean') {
return v;
}
return !!v;
},
// GPU implementation
(v) => snip(`bool(${v?.value ?? ''})`, bool),
'boolCast',
);

/**
* A schema that represents a boolean value. (equivalent to `bool` in WGSL)
*
* @example
* const value = bool(); // false
* @example
* const value = bool(0); // false
* @example
* const value = bool(-0); // false
* @example
* const value = bool(21.37); // true
*/
export const bool: Bool = {
[$internal]: true,
export const bool: Bool = Object.assign(boolCast, {
type: 'bool',
} as Bool;
}) as unknown as Bool;

const u32Cast = createDualImpl(
// CPU implementation
(v: number | boolean) => {
(v?: number | boolean) => {
if (v === undefined) {
return 0;
}
if (typeof v === 'boolean') {
return v ? 1 : 0;
}
if (Number.isInteger(v)) {
if (v < 0 || v > 0xffffffff) {
console.warn(`u32 value ${v} overflowed`);
}
const value = v & 0xffffffff;
return value >>> 0;
}
return Math.max(0, Math.min(0xffffffff, Math.floor(v)));
return (v & 0xffffffff) >>> 0;
},
// GPU implementation
(v) => snip(`u32(${v.value})`, u32),
(v) => snip(`u32(${v?.value ?? ''})`, u32),
'u32Cast',
);

/**
* A schema that represents an unsigned 32-bit integer value. (equivalent to `u32` in WGSL)
*
* Can also be called to cast a value to an u32 in accordance with WGSL casting rules.
*
* @example
* const value = u32(); // 0
* @example
* const value = u32(7); // 7
* @example
* const value = u32(3.14); // 3
* @example
Expand All @@ -68,33 +89,25 @@ export const u32: U32 = Object.assign(u32Cast, {

const i32Cast = createDualImpl(
// CPU implementation
(v: number | boolean) => {
(v?: number | boolean) => {
if (v === undefined) {
return 0;
}
if (typeof v === 'boolean') {
return v ? 1 : 0;
}
if (Number.isInteger(v)) {
if (v < -0x80000000 || v > 0x7fffffff) {
console.warn(`i32 value ${v} overflowed`);
}
const value = v | 0;
return value & 0xffffffff;
}
// round towards zero
const value = v < 0 ? Math.ceil(v) : Math.floor(v);
return Math.max(-0x80000000, Math.min(0x7fffffff, value));
return v | 0;
},
// GPU implementation
(v) => {
return snip(`i32(${v.value})`, i32);
},
(v) => snip(`i32(${v?.value ?? ''})`, i32),
'i32Cast',
);

/**
* A schema that represents a signed 32-bit integer value. (equivalent to `i32` in WGSL)
*
* Can also be called to cast a value to an i32 in accordance with WGSL casting rules.
*
* @example
* const value = i32(); // 0
* @example
* const value = i32(3.14); // 3
* @example
Expand All @@ -108,24 +121,27 @@ export const i32: I32 = Object.assign(i32Cast, {

const f32Cast = createDualImpl(
// CPU implementation
(v: number | boolean) => {
(v?: number | boolean) => {
if (v === undefined) {
return 0;
}
if (typeof v === 'boolean') {
return v ? 1 : 0;
}
const arr = new Float32Array(1);
arr[0] = v;
return arr[0];
return v;
},
// GPU implementation
(v) => snip(`f32(${v.value})`, f32),
(v) => snip(`f32(${v?.value ?? ''})`, f32),
'f32Cast',
);

/**
* A schema that represents a 32-bit float value. (equivalent to `f32` in WGSL)
*
* Can also be called to cast a value to an f32.
*
* @example
* const value = f32(); // 0
* @example
* const value = f32(1.23); // 1.23
* @example
* const value = f32(true); // 1
*/
Expand All @@ -135,29 +151,32 @@ export const f32: F32 = Object.assign(f32Cast, {

const f16Cast = createDualImpl(
// CPU implementation
(v: number | boolean) => {
(v?: number | boolean) => {
if (v === undefined) {
return 0;
}
if (typeof v === 'boolean') {
return v ? 1 : 0;
}
const arr = new ArrayBuffer(2);
bin.f16.write(new bin.BufferWriter(arr), v);
return bin.f16.read(new bin.BufferReader(arr));
return v;
},
// GPU implementation
// TODO: make usage of f16() in GPU mode check for feature availability and throw if not available
(v) => snip(`f16(${v.value})`, f16),
(v) => snip(`f16(${v?.value ?? ''})`, f16),
'f16Cast',
);

/**
* A schema that represents a 16-bit float value. (equivalent to `f16` in WGSL)
*
* Can also be called to cast a value to an f16.
*
* @example
* const value = f16(); // 0
* @example
* const value = f32(1.23); // 1.23
* @example
* const value = f16(true); // 1
* @example
* const value = f16(21877.5); // 21872
* const value = f16(21877.5); // 21877.5
*/
export const f16: F16 = Object.assign(f16Cast, {
type: 'f16',
Expand Down
Loading