Skip to content

Commit 0f700d0

Browse files
authored
fix: Better exotic inference (#607)
1 parent 5d2f52f commit 0f700d0

File tree

14 files changed

+139
-106
lines changed

14 files changed

+139
-106
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { AnyData } from '../../data/dataTypes';
2+
import type { Exotic } from '../../data/exotic';
3+
import type { TgpuBuffer as INTERNAL_TgpuBuffer } from './buffer';
4+
import type {
5+
TgpuBufferMutable as INTERNAL_TgpuBufferMutable,
6+
TgpuBufferReadonly as INTERNAL_TgpuBufferReadonly,
7+
TgpuBufferUniform as INTERNAL_TgpuBufferUniform,
8+
} from './bufferUsage';
9+
10+
export type TgpuBuffer<TData extends AnyData> = INTERNAL_TgpuBuffer<
11+
Exotic<TData>
12+
>;
13+
14+
export type TgpuBufferMutable<TData extends AnyData> =
15+
INTERNAL_TgpuBufferMutable<Exotic<TData>>;
16+
17+
export type TgpuBufferReadonly<TData extends AnyData> =
18+
INTERNAL_TgpuBufferReadonly<Exotic<TData>>;
19+
20+
export type TgpuBufferUniform<TData extends AnyData> =
21+
INTERNAL_TgpuBufferUniform<Exotic<TData>>;
22+
23+
// Reexporting as-is
24+
export type { Uniform, Vertex } from './buffer';

packages/typegpu/src/core/texture/usageExtension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function isUsableAsRender<T>(value: T): value is T & Render {
3535
* @category Errors
3636
*/
3737
export class NotSampledError extends Error {
38-
constructor(value: { readonly label: string | undefined }) {
38+
constructor(value: { readonly label?: string | undefined }) {
3939
super(
4040
`Resource '${value.label ?? '<unnamed>'}' cannot be bound as 'sampled'. Use .$usage('sampled') to allow it.`,
4141
);
@@ -49,7 +49,7 @@ export class NotSampledError extends Error {
4949
* @category Errors
5050
*/
5151
export class NotRenderError extends Error {
52-
constructor(value: { readonly label: string | undefined }) {
52+
constructor(value: { readonly label?: string | undefined }) {
5353
super(
5454
`Resource '${value.label ?? '<unnamed>'}' cannot be bound as 'render'. Use .$usage('render') to allow it.`,
5555
);

packages/typegpu/src/data/array.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import type { AnyWgslData, WgslArray } from './wgslTypes';
1515
* the `byteAlignment` requirement of its elementType.
1616
*/
1717
export interface TgpuArray<TElement extends AnyWgslData>
18-
extends WgslArray<TElement> {}
18+
extends WgslArray<TElement> {
19+
readonly '~exotic': WgslArray<Exotic<TElement>>;
20+
}
1921

2022
/**
2123
* Creates an array schema that can be used to construct gpu buffers.
@@ -44,6 +46,8 @@ class TgpuArrayImpl<TElement extends AnyWgslData>
4446
public readonly type = 'array';
4547
/** Type-token, not available at runtime */
4648
public readonly '~repr'!: Infer<TElement>[];
49+
/** Type-token, not available at runtime */
50+
public readonly '~exotic'!: WgslArray<Exotic<TElement>>;
4751

4852
constructor(
4953
public readonly elementType: TElement,

packages/typegpu/src/data/exotic.ts

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,11 @@
1-
import type * as wgsl from './wgslTypes';
2-
3-
type TypeToPrimitiveMap = {
4-
bool: wgsl.Bool;
5-
f32: wgsl.F32;
6-
i32: wgsl.I32;
7-
u32: wgsl.U32;
8-
vec2f: wgsl.Vec2f;
9-
vec2i: wgsl.Vec2i;
10-
vec2u: wgsl.Vec2u;
11-
vec3f: wgsl.Vec3f;
12-
vec3i: wgsl.Vec3i;
13-
vec3u: wgsl.Vec3u;
14-
vec4f: wgsl.Vec4f;
15-
vec4i: wgsl.Vec4i;
16-
vec4u: wgsl.Vec4u;
17-
mat2x2f: wgsl.Mat2x2f;
18-
mat3x3f: wgsl.Mat3x3f;
19-
mat4x4f: wgsl.Mat4x4f;
20-
};
21-
221
/**
232
* Strips schema types down to their most basic forms. (native -> exotic)
243
* This is used by schema constructors to be able to ingest native schemas (created by TypeGPU), and
254
* spit out a type that matches non-native schemas as well.
265
*/
27-
export type Exotic<T> =
28-
// primitives
29-
T extends { type: keyof TypeToPrimitiveMap }
30-
? TypeToPrimitiveMap[T['type']]
31-
: // arrays
32-
T extends wgsl.WgslArray<infer TElement>
33-
? wgsl.WgslArray<TElement>
34-
: // structs
35-
T extends wgsl.WgslStruct<infer TProps>
36-
? wgsl.WgslStruct<TProps>
37-
: // atomics
38-
T extends wgsl.Atomic<infer TInner>
39-
? wgsl.Atomic<TInner>
40-
: // decorated
41-
T extends wgsl.Decorated<infer TInner, infer TAttribs>
42-
? wgsl.Decorated<TInner, TAttribs>
43-
: T;
6+
export type Exotic<T> = T extends { readonly '~exotic': infer TExotic }
7+
? TExotic
8+
: T;
449

4510
export type ExoticArray<T> = T extends unknown[] | []
4611
? {

packages/typegpu/src/data/matrix.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,9 @@ class mat4x4fImpl extends mat4x4Impl<v4f> implements m4x4f {
439439
/**
440440
* Type of the `d.mat2x2f` object/function: matrix data type schema/constructor
441441
*/
442-
export type Mat2x2fConstructor = ((...elements: number[]) => m2x2f) &
442+
export type NativeMat2x2f = Mat2x2f & { '~exotic': Mat2x2f } & ((
443+
...elements: number[]
444+
) => m2x2f) &
443445
((...columns: v2f[]) => m2x2f) &
444446
(() => m2x2f);
445447

@@ -470,12 +472,14 @@ export const mat2x2f = createMatSchema<'mat2x2f', m2x2f, v2f>({
470472
rows: 2,
471473
columns: 2,
472474
makeFromElements: (...elements: number[]) => new mat2x2fImpl(...elements),
473-
}) as Mat2x2f & Mat2x2fConstructor;
475+
}) as NativeMat2x2f;
474476

475477
/**
476478
* Type of the `d.mat3x3f` object/function: matrix data type schema/constructor
477479
*/
478-
export type Mat3x3fConstructor = ((...elements: number[]) => m3x3f) &
480+
export type NativeMat3x3f = Mat3x3f & { '~exotic': Mat3x3f } & ((
481+
...elements: number[]
482+
) => m3x3f) &
479483
((...columns: v3f[]) => m3x3f) &
480484
(() => m3x3f);
481485

@@ -508,12 +512,14 @@ export const mat3x3f = createMatSchema<'mat3x3f', m3x3f, v3f>({
508512
rows: 3,
509513
columns: 3,
510514
makeFromElements: (...elements: number[]) => new mat3x3fImpl(...elements),
511-
}) as Mat3x3f & Mat3x3fConstructor;
515+
}) as NativeMat3x3f;
512516

513517
/**
514518
* Type of the `d.mat4x4f` object/function: matrix data type schema/constructor
515519
*/
516-
export type Mat4x4fConstructor = ((...elements: number[]) => m4x4f) &
520+
export type NativeMat4x4f = Mat4x4f & { '~exotic': Mat4x4f } & ((
521+
...elements: number[]
522+
) => m4x4f) &
517523
((...columns: v4f[]) => m4x4f) &
518524
(() => m4x4f);
519525

@@ -548,7 +554,7 @@ export const mat4x4f = createMatSchema<'mat4x4f', m4x4f, v4f>({
548554
rows: 4,
549555
columns: 4,
550556
makeFromElements: (...elements: number[]) => new mat4x4fImpl(...elements),
551-
}) as Mat4x4f & Mat4x4fConstructor;
557+
}) as NativeMat4x4f;
552558

553559
export function matToArray(mat: m2x2f | m3x3f | m4x4f): number[] {
554560
if (mat.kind === 'mat3x3f') {

packages/typegpu/src/data/numeric.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ export const bool: Bool = {
1111
/**
1212
* Unsigned 32-bit integer schema representing a single WGSL u32 value.
1313
*/
14-
export type U32Cast = (v: number | boolean) => number;
14+
export type NativeU32 = U32 & { '~exotic': U32 } & ((
15+
v: number | boolean,
16+
) => number);
17+
1518
const u32Cast = (v: number | boolean) => {
1619
if (inGPUMode()) {
1720
return `u32(${v})` as unknown as number;
@@ -42,14 +45,17 @@ const u32Cast = (v: number | boolean) => {
4245
* @example
4346
* const value = u32(-3.1); // 0
4447
*/
45-
export const u32: U32 & U32Cast = Object.assign(u32Cast, {
48+
export const u32: NativeU32 = Object.assign(u32Cast, {
4649
type: 'u32',
47-
} as U32);
50+
}) as NativeU32;
4851

4952
/**
5053
* Signed 32-bit integer schema representing a single WGSL i32 value.
5154
*/
52-
export type I32Cast = (v: number | boolean) => number;
55+
export type NativeI32 = I32 & { '~exotic': I32 } & ((
56+
v: number | boolean,
57+
) => number);
58+
5359
const i32Cast = (v: number | boolean) => {
5460
if (inGPUMode()) {
5561
return `i32(${v})` as unknown as number;
@@ -82,14 +88,17 @@ const i32Cast = (v: number | boolean) => {
8288
* @example
8389
* const value = i32(10000000000) // 1410065408
8490
*/
85-
export const i32: I32 & I32Cast = Object.assign(i32Cast, {
91+
export const i32: NativeI32 = Object.assign(i32Cast, {
8692
type: 'i32',
87-
} as I32);
93+
}) as NativeI32;
8894

8995
/**
9096
* 32-bit float schema representing a single WGSL f32 value.
9197
*/
92-
export type F32Cast = (v: number | boolean) => number;
98+
export type NativeF32 = F32 & { '~exotic': F32 } & ((
99+
v: number | boolean,
100+
) => number);
101+
93102
const f32Cast = (v: number | boolean) => {
94103
if (inGPUMode()) {
95104
return `f32(${v})` as unknown as number;
@@ -108,6 +117,6 @@ const f32Cast = (v: number | boolean) => {
108117
* @example
109118
* const value = f32(true); // 1
110119
*/
111-
export const f32: F32 & F32Cast = Object.assign(f32Cast, {
120+
export const f32: NativeF32 = Object.assign(f32Cast, {
112121
type: 'f32',
113-
} as F32);
122+
}) as NativeF32;

packages/typegpu/src/data/struct.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import type { AnyWgslData, BaseWgslData, WgslStruct } from './wgslTypes';
1717
*/
1818
export interface TgpuStruct<TProps extends Record<string, BaseWgslData>>
1919
extends WgslStruct<TProps>,
20-
TgpuNamable {}
20+
TgpuNamable {
21+
readonly '~exotic': WgslStruct<ExoticRecord<TProps>>;
22+
}
2123

2224
/**
2325
* Creates a struct schema that can be used to construct GPU buffers.
@@ -47,6 +49,8 @@ class TgpuStructImpl<TProps extends Record<string, AnyWgslData>>
4749
public readonly type = 'struct';
4850
/** Type-token, not available at runtime */
4951
public readonly '~repr'!: InferRecord<TProps>;
52+
/** Type-token, not available at runtime */
53+
public readonly '~exotic'!: WgslStruct<ExoticRecord<TProps>>;
5054

5155
constructor(public readonly propTypes: TProps) {}
5256

0 commit comments

Comments
 (0)