Skip to content

Commit da9e09f

Browse files
authored
refactor: Use internal symbol to identify TypeGPU-originating values (#1171)
* refactor: Use internal symbol to identify TypeGPU-originating values * Fixed dual impl internal types * Smaller builtins
1 parent bb223d1 commit da9e09f

20 files changed

+265
-171
lines changed

packages/typegpu/src/builtin.ts

Lines changed: 45 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { arrayOf } from './data/array.ts';
22
import { attribute } from './data/attributes.ts';
3+
import type { LooseDecorated } from './data/dataTypes.ts';
34
import { f32, u32 } from './data/numeric.ts';
45
import { vec3u, vec4f } from './data/vector.ts';
56
import type {
7+
AnyWgslData,
68
BaseData,
79
Builtin,
810
Decorated,
@@ -12,6 +14,7 @@ import type {
1214
Vec4f,
1315
WgslArray,
1416
} from './data/wgslTypes.ts';
17+
import { $internal } from './shared/symbols.ts';
1518

1619
// ----------
1720
// Public API
@@ -51,67 +54,49 @@ export type BuiltinSubgroupInvocationId = Decorated<
5154
>;
5255
export type BuiltinSubgroupSize = Decorated<U32, [Builtin<'subgroup_size'>]>;
5356

54-
export const builtin = {
55-
vertexIndex: attribute(u32, {
56-
type: '@builtin',
57-
value: 'vertex_index',
58-
}) as BuiltinVertexIndex,
59-
instanceIndex: attribute(u32, {
60-
type: '@builtin',
61-
value: 'instance_index',
62-
}) as BuiltinInstanceIndex,
63-
position: attribute(vec4f, {
64-
type: '@builtin',
65-
value: 'position',
66-
}) as BuiltinPosition,
67-
clipDistances: attribute(arrayOf(u32, 8), {
68-
type: '@builtin',
69-
value: 'clip_distances',
70-
}) as BuiltinClipDistances,
71-
frontFacing: attribute(f32, {
72-
type: '@builtin',
73-
value: 'front_facing',
74-
}) as BuiltinFrontFacing,
75-
fragDepth: attribute(f32, {
76-
type: '@builtin',
77-
value: 'frag_depth',
78-
}) as BuiltinFragDepth,
79-
sampleIndex: attribute(u32, {
80-
type: '@builtin',
81-
value: 'sample_index',
82-
}) as BuiltinSampleIndex,
83-
sampleMask: attribute(u32, {
84-
type: '@builtin',
85-
value: 'sample_mask',
86-
}) as BuiltinSampleMask,
87-
localInvocationId: attribute(vec3u, {
57+
function defineBuiltin<T extends Decorated | LooseDecorated>(
58+
dataType: AnyWgslData,
59+
value: T['attribs'][0] extends { value: infer TValue } ? TValue : never,
60+
): T {
61+
return attribute(dataType, {
62+
[$internal]: true,
8863
type: '@builtin',
89-
value: 'local_invocation_id',
90-
}) as BuiltinLocalInvocationId,
91-
localInvocationIndex: attribute(u32, {
92-
type: '@builtin',
93-
value: 'local_invocation_index',
94-
}) as BuiltinLocalInvocationIndex,
95-
globalInvocationId: attribute(vec3u, {
96-
type: '@builtin',
97-
value: 'global_invocation_id',
98-
}) as BuiltinGlobalInvocationId,
99-
workgroupId: attribute(vec3u, {
100-
type: '@builtin',
101-
value: 'workgroup_id',
102-
}) as BuiltinWorkgroupId,
103-
numWorkgroups: attribute(vec3u, {
104-
type: '@builtin',
105-
value: 'num_workgroups',
106-
}) as BuiltinNumWorkgroups,
107-
subgroupInvocationId: attribute(u32, {
108-
type: '@builtin',
109-
value: 'subgroup_invocation_id',
110-
}) as BuiltinSubgroupInvocationId,
111-
subgroupSize: attribute(u32, {
112-
type: '@builtin',
113-
value: 'subgroup_size',
114-
}) as BuiltinSubgroupSize,
64+
// biome-ignore lint/suspicious/noExplicitAny: it's fine
65+
value: value as any,
66+
}) as T;
67+
}
68+
69+
export const builtin = {
70+
vertexIndex: defineBuiltin<BuiltinVertexIndex>(u32, 'vertex_index'),
71+
instanceIndex: defineBuiltin<BuiltinInstanceIndex>(u32, 'instance_index'),
72+
position: defineBuiltin<BuiltinPosition>(vec4f, 'position'),
73+
clipDistances: defineBuiltin<BuiltinClipDistances>(
74+
arrayOf(u32, 8),
75+
'clip_distances',
76+
),
77+
frontFacing: defineBuiltin<BuiltinFrontFacing>(f32, 'front_facing'),
78+
fragDepth: defineBuiltin<BuiltinFragDepth>(f32, 'frag_depth'),
79+
sampleIndex: defineBuiltin<BuiltinSampleIndex>(u32, 'sample_index'),
80+
sampleMask: defineBuiltin<BuiltinSampleMask>(u32, 'sample_mask'),
81+
localInvocationId: defineBuiltin<BuiltinLocalInvocationId>(
82+
vec3u,
83+
'local_invocation_id',
84+
),
85+
localInvocationIndex: defineBuiltin<BuiltinLocalInvocationIndex>(
86+
u32,
87+
'local_invocation_index',
88+
),
89+
globalInvocationId: defineBuiltin<BuiltinGlobalInvocationId>(
90+
vec3u,
91+
'global_invocation_id',
92+
),
93+
workgroupId: defineBuiltin<BuiltinWorkgroupId>(vec3u, 'workgroup_id'),
94+
numWorkgroups: defineBuiltin<BuiltinNumWorkgroups>(vec3u, 'num_workgroups'),
95+
subgroupInvocationId: defineBuiltin<BuiltinSubgroupInvocationId>(
96+
u32,
97+
'subgroup_invocation_id',
98+
),
99+
subgroupSize: defineBuiltin<BuiltinSubgroupSize>(u32, 'subgroup_size'),
115100
} as const;
116101

117102
export type AnyBuiltin = (typeof builtin)[keyof typeof builtin];

packages/typegpu/src/core/function/fnCore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export interface FnCore {
3434

3535
export function createFnCore(
3636
shell: TgpuFnShellBase<unknown[] | Record<string, unknown>, unknown>,
37-
implementation: Implementation<unknown[], unknown>,
37+
implementation: Implementation,
3838
): FnCore {
3939
/**
4040
* External application has to be deferred until resolution because

packages/typegpu/src/core/function/fnTypes.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,25 @@ export type InferReturn<T> = T extends undefined
4343
void
4444
: Infer<T>;
4545

46+
export type JsImplementation<
47+
Args extends unknown[] | Record<string, unknown> =
48+
| unknown[]
49+
| Record<string, unknown>,
50+
Return = unknown,
51+
> = (
52+
...args: Args extends unknown[]
53+
? InferArgs<Args>
54+
: Args extends Record<string, never>
55+
? []
56+
: [InferIO<Args>]
57+
) => InferReturn<Return>;
58+
4659
export type Implementation<
47-
Args extends unknown[] = unknown[],
60+
Args extends unknown[] | Record<string, unknown> =
61+
| unknown[]
62+
| Record<string, unknown>,
4863
Return = unknown,
49-
> = string | ((...args: Args) => Return);
64+
> = string | JsImplementation<Args, Return>;
5065

5166
export type BaseIOData =
5267
| F32

packages/typegpu/src/core/function/ioOutputType.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
type Decorate,
33
type HasCustomLocation,
44
type IsBuiltin,
5-
attribute,
65
location,
76
} from '../../data/attributes.ts';
87
import { isBuiltin } from '../../data/attributes.ts';
@@ -44,10 +43,7 @@ export function withLocations<T extends IOData>(
4443
return [key, member];
4544
}
4645

47-
return [
48-
key,
49-
attribute(member, { type: '@location', value: nextLocation++ }),
50-
];
46+
return [key, location(nextLocation++, member)];
5147
}),
5248
);
5349
}

packages/typegpu/src/core/function/tgpuFn.ts

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type {
2626
InferArgs,
2727
InferIO,
2828
InferReturn,
29+
JsImplementation,
2930
} from './fnTypes.ts';
3031
import { stripTemplate } from './templateUtils.ts';
3132

@@ -80,6 +81,9 @@ interface TgpuFnBase<
8081
Return extends AnyWgslData | undefined = undefined,
8182
> extends TgpuNamable,
8283
Labelled {
84+
readonly [$internal]: {
85+
implementation: Implementation<Args, Return>;
86+
};
8387
readonly resourceType: 'function';
8488
readonly shell: TgpuFnShellHeader<Args, Return>;
8589
readonly '~providing'?: Providing | undefined;
@@ -102,18 +106,7 @@ export type TgpuFn<
102106
: Args extends Record<string, never>
103107
? []
104108
: [InferIO<Args>]
105-
) => InferReturn<Return>) & {
106-
readonly [$internal]: {
107-
implementation: Implementation<
108-
Args extends AnyWgslData[]
109-
? InferArgs<Args>
110-
: Args extends Record<string, never>
111-
? []
112-
: [InferIO<Args>],
113-
InferReturn<Return>
114-
>;
115-
};
116-
};
109+
) => InferReturn<Return>);
117110

118111
export function fn<
119112
Args extends AnyWgslData[] | Record<string, AnyWgslData> | [],
@@ -164,13 +157,16 @@ function createFn<
164157
Return extends AnyWgslData | undefined,
165158
>(
166159
shell: TgpuFnShellHeader<Args, Return>,
167-
implementation: Implementation,
160+
implementation: Implementation<Args, Return>,
168161
): TgpuFn<Args, Return> {
169162
type This = TgpuFnBase<Args, Return> & SelfResolvable;
170163

171-
const core = createFnCore(shell, implementation);
164+
const core = createFnCore(shell, implementation as Implementation);
172165

173166
const fnBase: This = {
167+
[$internal]: {
168+
implementation,
169+
},
174170
shell,
175171
resourceType: 'function' as const,
176172

@@ -214,8 +210,8 @@ function createFn<
214210
},
215211
};
216212

217-
const call = createDualImpl(
218-
(...args: unknown[]): unknown => {
213+
const call = createDualImpl<JsImplementation<Args, Return>>(
214+
(...args) => {
219215
if (typeof implementation === 'string') {
220216
throw new Error(
221217
'Cannot execute on the CPU functions constructed with raw WGSL',
@@ -229,9 +225,6 @@ function createFn<
229225
dataType: shell.returnType ?? UnknownData,
230226
}),
231227
);
232-
233-
call[$internal].implementation = implementation;
234-
235228
const fn = Object.assign(call, fnBase as This) as unknown as TgpuFn<
236229
Args,
237230
Return
@@ -256,6 +249,9 @@ function createBoundFunction<
256249
type This = TgpuFnBase<Args, Return>;
257250

258251
const fnBase: This = {
252+
[$internal]: {
253+
implementation: innerFn[$internal].implementation,
254+
},
259255
resourceType: 'function',
260256
shell: innerFn.shell,
261257
'~providing': {

packages/typegpu/src/data/array.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
InferPartial,
66
MemIdentity,
77
} from '../shared/repr.ts';
8+
import { $internal } from '../shared/symbols.ts';
89
import { sizeOf } from './sizeOf.ts';
910
import type { AnyWgslData, BaseData, WgslArray } from './wgslTypes.ts';
1011

@@ -35,18 +36,19 @@ export function arrayOf<TElement extends AnyWgslData>(
3536
// --------------
3637

3738
class WgslArrayImpl<TElement extends BaseData> implements WgslArray<TElement> {
39+
public readonly [$internal] = true;
3840
public readonly type = 'array';
3941
/** Type-token, not available at runtime */
4042
public declare readonly [$repr]: Infer<TElement>[];
4143
/** Type-token, not available at runtime */
42-
public readonly '~gpuRepr'!: InferGPU<TElement>[];
44+
public declare readonly '~gpuRepr': InferGPU<TElement>[];
4345
/** Type-token, not available at runtime */
44-
public readonly '~reprPartial'!: {
46+
public declare readonly '~reprPartial': {
4547
idx: number;
4648
value: InferPartial<TElement>;
4749
}[];
4850
/** Type-token, not available at runtime */
49-
public readonly '~memIdent'!: WgslArray<MemIdentity<TElement>>;
51+
public declare readonly '~memIdent': WgslArray<MemIdentity<TElement>>;
5052

5153
constructor(
5254
public readonly elementType: TElement,

packages/typegpu/src/data/atomic.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { $repr, Infer, MemIdentity } from '../shared/repr.ts';
2+
import { $internal } from '../shared/symbols.ts';
23
import type { Atomic, I32, U32, atomicI32, atomicU32 } from './wgslTypes.ts';
34

45
// ----------
@@ -25,6 +26,7 @@ export function atomic<TSchema extends U32 | I32>(
2526
// --------------
2627

2728
class AtomicImpl<TSchema extends U32 | I32> implements Atomic<TSchema> {
29+
public readonly [$internal] = true;
2830
public readonly type = 'atomic';
2931
/** Type-token, not available at runtime */
3032
public declare readonly [$repr]: Infer<TSchema>;

0 commit comments

Comments
 (0)