typegpu 0.12.6 → 0.12.8
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/common/writeSoA.js +16 -0
- package/core/buffer/buffer.js +47 -11
- package/core/commandEncoder/attachments.js +5 -2
- package/core/function/autoIO.js +5 -3
- package/core/function/comptime.js +7 -5
- package/core/function/createCallableSchema.js +4 -3
- package/core/function/dualImpl.js +20 -19
- package/core/function/ioSchema.d.ts +17 -2
- package/core/function/ioSchema.js +74 -27
- package/core/function/tgpuFn.d.ts +11 -1
- package/core/function/tgpuFn.js +9 -2
- package/core/function/tgpuFragmentFn.d.ts +1 -1
- package/core/function/tgpuFragmentFn.js +2 -1
- package/core/function/tgpuVertexFn.js +2 -1
- package/core/pipeline/connectAttachmentToShader.d.ts +2 -2
- package/core/pipeline/connectAttachmentToShader.js +16 -21
- package/core/pipeline/connectTargetsToShader.d.ts +5 -1
- package/core/pipeline/connectTargetsToShader.js +23 -18
- package/core/pipeline/renderPipeline.d.ts +9 -6
- package/core/pipeline/renderPipeline.js +22 -22
- package/core/pipeline/timeable.js +15 -2
- package/core/resolve/namespace.js +2 -2
- package/core/root/configurableImpl.js +1 -2
- package/core/root/rootTypes.d.ts +6 -2
- package/core/slot/accessor.d.ts +1 -1
- package/core/slot/accessor.js +72 -5
- package/core/texture/externalTexture.d.ts +1 -1
- package/core/texture/externalTexture.js +3 -1
- package/core/texture/texture.d.ts +1 -1
- package/core/texture/texture.js +12 -5
- package/core/unroll/tgpuUnroll.d.ts +4 -3
- package/core/unroll/tgpuUnroll.js +7 -1
- package/core/valueProxyUtils.d.ts +4 -2
- package/core/valueProxyUtils.js +4 -2
- package/data/autoStruct.d.ts +1 -1
- package/data/autoStruct.js +8 -4
- package/data/compiledIO.js +12 -9
- package/data/dataIO.d.ts +10 -6
- package/data/dataIO.js +19 -4
- package/data/dataTypes.d.ts +0 -5
- package/data/dataTypes.js +0 -6
- package/data/deepEqual.js +19 -9
- package/data/matrix.js +10 -3
- package/data/matrixColumnsAccess.d.ts +8 -0
- package/data/matrixColumnsAccess.js +15 -0
- package/data/offsetUtils.js +181 -102
- package/data/partialIO.d.ts +8 -1
- package/data/partialIO.js +36 -19
- package/data/ref.d.ts +12 -5
- package/data/ref.js +51 -24
- package/data/snippet.d.ts +13 -4
- package/data/snippet.js +17 -4
- package/data/struct.js +3 -0
- package/data/wgslTypes.d.ts +6 -3
- package/internal.d.ts +1 -0
- package/nameUtils.d.ts +7 -0
- package/nameUtils.js +60 -0
- package/package.json +1 -1
- package/resolutionCtx.js +1 -1
- package/shared/meta.js +1 -1
- package/shared/symbols.d.ts +5 -0
- package/shared/symbols.js +6 -1
- package/shared/tseynit.js +54 -50
- package/std/boolean.js +9 -10
- package/std/numeric.js +1 -4
- package/std/operators.js +9 -4
- package/std/packing.js +10 -10
- package/std/texture.d.ts +2 -2
- package/tgpuLogger.d.ts +1 -1
- package/tgpuLogger.js +0 -2
- package/tgsl/accessIndex.js +37 -38
- package/tgsl/accessProp.js +10 -7
- package/tgsl/conversion.js +3 -2
- package/tgsl/forOfUtils.js +8 -8
- package/tgsl/generationHelpers.d.ts +4 -3
- package/tgsl/generationHelpers.js +18 -5
- package/tgsl/infixDispatch.d.ts +4 -1
- package/tgsl/infixDispatch.js +13 -1
- package/tgsl/makeDereferenceable.d.ts +1 -1
- package/tgsl/makeDereferenceable.js +3 -1
- package/tgsl/shellless.js +5 -9
- package/tgsl/stringifySnippet.d.ts +9 -0
- package/tgsl/stringifySnippet.js +24 -0
- package/tgsl/wgslGenerator.d.ts +1 -0
- package/tgsl/wgslGenerator.js +180 -86
- package/types.d.ts +23 -2
- package/types.js +30 -5
package/std/packing.js
CHANGED
|
@@ -96,7 +96,7 @@ export const unpack4x8unorm = dualImpl({
|
|
|
96
96
|
return vec4f(reader.readUint8() / 255, reader.readUint8() / 255, reader.readUint8() / 255, reader.readUint8() / 255);
|
|
97
97
|
},
|
|
98
98
|
signature: { argTypes: [u32], returnType: vec4f },
|
|
99
|
-
codegenImpl: (
|
|
99
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('unpack4x8unorm', [], [e]),
|
|
100
100
|
sideEffects: false,
|
|
101
101
|
});
|
|
102
102
|
/**
|
|
@@ -110,7 +110,7 @@ export const unpack4x8snorm = dualImpl({
|
|
|
110
110
|
return vec4f(Math.max(reader.readInt8() / 127, -1), Math.max(reader.readInt8() / 127, -1), Math.max(reader.readInt8() / 127, -1), Math.max(reader.readInt8() / 127, -1));
|
|
111
111
|
},
|
|
112
112
|
signature: { argTypes: [u32], returnType: vec4f },
|
|
113
|
-
codegenImpl: (
|
|
113
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('unpack4x8snorm', [], [e]),
|
|
114
114
|
sideEffects: false,
|
|
115
115
|
});
|
|
116
116
|
/**
|
|
@@ -124,7 +124,7 @@ export const unpack4xI8 = dualImpl({
|
|
|
124
124
|
return vec4i(reader.readInt8(), reader.readInt8(), reader.readInt8(), reader.readInt8());
|
|
125
125
|
},
|
|
126
126
|
signature: { argTypes: [u32], returnType: vec4i },
|
|
127
|
-
codegenImpl: (
|
|
127
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('unpack4xI8', [], [e]),
|
|
128
128
|
sideEffects: false,
|
|
129
129
|
});
|
|
130
130
|
/**
|
|
@@ -138,7 +138,7 @@ export const unpack4xU8 = dualImpl({
|
|
|
138
138
|
return vec4u(reader.readUint8(), reader.readUint8(), reader.readUint8(), reader.readUint8());
|
|
139
139
|
},
|
|
140
140
|
signature: { argTypes: [u32], returnType: vec4u },
|
|
141
|
-
codegenImpl: (
|
|
141
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('unpack4xU8', [], [e]),
|
|
142
142
|
sideEffects: false,
|
|
143
143
|
});
|
|
144
144
|
/**
|
|
@@ -182,7 +182,7 @@ export const pack4x8unorm = dualImpl({
|
|
|
182
182
|
writer.writeUint8(Math.floor(0.5 + 255 * clampScalar(e.w, 0, 1)));
|
|
183
183
|
}),
|
|
184
184
|
signature: { argTypes: [vec4f], returnType: u32 },
|
|
185
|
-
codegenImpl: (
|
|
185
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('pack4x8unorm', [], [e]),
|
|
186
186
|
sideEffects: false,
|
|
187
187
|
});
|
|
188
188
|
/**
|
|
@@ -198,7 +198,7 @@ export const pack4x8snorm = dualImpl({
|
|
|
198
198
|
writer.writeInt8(Math.floor(0.5 + 127 * clampScalar(e.w, -1, 1)));
|
|
199
199
|
}),
|
|
200
200
|
signature: { argTypes: [vec4f], returnType: u32 },
|
|
201
|
-
codegenImpl: (
|
|
201
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('pack4x8snorm', [], [e]),
|
|
202
202
|
sideEffects: false,
|
|
203
203
|
});
|
|
204
204
|
/**
|
|
@@ -214,7 +214,7 @@ export const pack4xI8 = dualImpl({
|
|
|
214
214
|
writer.writeInt8(e.w);
|
|
215
215
|
}),
|
|
216
216
|
signature: { argTypes: [vec4i], returnType: u32 },
|
|
217
|
-
codegenImpl: (
|
|
217
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('pack4xI8', [], [e]),
|
|
218
218
|
sideEffects: false,
|
|
219
219
|
});
|
|
220
220
|
/**
|
|
@@ -230,7 +230,7 @@ export const pack4xU8 = dualImpl({
|
|
|
230
230
|
writer.writeUint8(e.w);
|
|
231
231
|
}),
|
|
232
232
|
signature: { argTypes: [vec4u], returnType: u32 },
|
|
233
|
-
codegenImpl: (
|
|
233
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('pack4xU8', [], [e]),
|
|
234
234
|
sideEffects: false,
|
|
235
235
|
});
|
|
236
236
|
/**
|
|
@@ -246,7 +246,7 @@ export const pack4xI8Clamp = dualImpl({
|
|
|
246
246
|
writer.writeInt8(clampScalar(e.w, -128, 127));
|
|
247
247
|
}),
|
|
248
248
|
signature: { argTypes: [vec4i], returnType: u32 },
|
|
249
|
-
codegenImpl: (
|
|
249
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('pack4xI8Clamp', [], [e]),
|
|
250
250
|
sideEffects: false,
|
|
251
251
|
});
|
|
252
252
|
/**
|
|
@@ -262,6 +262,6 @@ export const pack4xU8Clamp = dualImpl({
|
|
|
262
262
|
writer.writeUint8(clampScalar(e.w, 0, 255));
|
|
263
263
|
}),
|
|
264
264
|
signature: { argTypes: [vec4u], returnType: u32 },
|
|
265
|
-
codegenImpl: (
|
|
265
|
+
codegenImpl: (ctx, [e]) => ctx.gen.emitCall('pack4xU8Clamp', [], [e]),
|
|
266
266
|
sideEffects: false,
|
|
267
267
|
});
|
package/std/texture.d.ts
CHANGED
|
@@ -61,8 +61,8 @@ declare function textureStoreCpu<T extends textureStorage3d>(texture: T, coords:
|
|
|
61
61
|
export declare const textureStore: import("../types.ts").DualFn<typeof textureStoreCpu>;
|
|
62
62
|
declare function textureDimensionsCpu<T extends texture1d | textureStorage1d>(texture: T): number;
|
|
63
63
|
declare function textureDimensionsCpu<T extends texture1d>(texture: T, level: number): number;
|
|
64
|
-
declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray | textureStorage2d | textureStorage2dArray | textureExternal>(texture: T): v2u;
|
|
65
|
-
declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray>(texture: T, level: number): v2u;
|
|
64
|
+
declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray | textureMultisampled2d | textureDepth2d | textureDepth2dArray | textureDepthCube | textureDepthCubeArray | textureDepthMultisampled2d | textureStorage2d | textureStorage2dArray | textureExternal>(texture: T): v2u;
|
|
65
|
+
declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray | textureDepth2d | textureDepth2dArray | textureDepthCube | textureDepthCubeArray>(texture: T, level: number): v2u;
|
|
66
66
|
declare function textureDimensionsCpu<T extends texture3d | textureStorage3d>(texture: T): v3u;
|
|
67
67
|
declare function textureDimensionsCpu<T extends texture3d>(texture: T, level: number): v3u;
|
|
68
68
|
export declare const textureDimensions: import("../types.ts").DualFn<typeof textureDimensionsCpu>;
|
package/tgpuLogger.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const warningTypes: readonly ["deprecated", "suspicious", "fallback", "precision-loss", "implicit-conversion", "webgpu-feature-missing", "webgpu-limits-exceeded", "
|
|
1
|
+
declare const warningTypes: readonly ["deprecated", "suspicious", "fallback", "precision-loss", "implicit-conversion", "webgpu-feature-missing", "webgpu-limits-exceeded", "log-limit-exceeded", "external-omitted", "uniform-schema-misaligned"];
|
|
2
2
|
type WarningType = (typeof warningTypes)[number];
|
|
3
3
|
interface Logger {
|
|
4
4
|
warn(type: WarningType, ...args: unknown[]): void;
|
package/tgpuLogger.js
CHANGED
|
@@ -7,7 +7,6 @@ const warningTypes = [
|
|
|
7
7
|
'implicit-conversion',
|
|
8
8
|
'webgpu-feature-missing',
|
|
9
9
|
'webgpu-limits-exceeded',
|
|
10
|
-
'locations-mismatched',
|
|
11
10
|
'log-limit-exceeded',
|
|
12
11
|
'external-omitted',
|
|
13
12
|
'uniform-schema-misaligned',
|
|
@@ -21,7 +20,6 @@ export class TgpuLogger {
|
|
|
21
20
|
this.#initialEnabledWarnings = [
|
|
22
21
|
'webgpu-feature-missing',
|
|
23
22
|
'webgpu-limits-exceeded',
|
|
24
|
-
'locations-mismatched',
|
|
25
23
|
'log-limit-exceeded',
|
|
26
24
|
'external-omitted',
|
|
27
25
|
];
|
package/tgsl/accessIndex.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { stitch } from "../core/resolve/stitch.js";
|
|
2
|
-
import { isDisarray
|
|
2
|
+
import { isDisarray } from "../data/dataTypes.js";
|
|
3
|
+
import { MatrixColumnsAccess } from "../data/matrixColumnsAccess.js";
|
|
3
4
|
import { derefSnippet } from "../data/ref.js";
|
|
4
5
|
import { snip } from "../data/snippet.js";
|
|
5
6
|
import { vec2f, vec3f, vec4f } from "../data/vector.js";
|
|
6
|
-
import { isPtr, isVec, isWgslArray
|
|
7
|
-
import {
|
|
7
|
+
import { isPtr, isVec, isWgslArray } from "../data/wgslTypes.js";
|
|
8
|
+
import { asComptime } from "../types.js";
|
|
8
9
|
import { accessProp } from "./accessProp.js";
|
|
9
10
|
import { ArrayExpression, coerceToSnippet } from "./generationHelpers.js";
|
|
10
11
|
const indexableTypeToResult = {
|
|
@@ -12,46 +13,46 @@ const indexableTypeToResult = {
|
|
|
12
13
|
mat3x3f: vec3f,
|
|
13
14
|
mat4x4f: vec4f,
|
|
14
15
|
};
|
|
16
|
+
function deriveIndexAccessOrigin(targetOrigin, indexOrigin) {
|
|
17
|
+
if (targetOrigin === 'constant-immutable-def') {
|
|
18
|
+
// Constant refs stay const unless the index forces runtime materialization
|
|
19
|
+
return indexOrigin === 'constant' || indexOrigin === 'constant-immutable-def'
|
|
20
|
+
? 'constant-immutable-def'
|
|
21
|
+
: 'runtime-immutable-def';
|
|
22
|
+
}
|
|
23
|
+
if (targetOrigin === 'constant') {
|
|
24
|
+
// Ephemeral constants indexed with constants stay constant, otherwise they become runtime-known
|
|
25
|
+
return indexOrigin === 'constant' || indexOrigin === 'constant-immutable-def'
|
|
26
|
+
? 'constant'
|
|
27
|
+
: 'runtime';
|
|
28
|
+
}
|
|
29
|
+
// Fallthrough
|
|
30
|
+
return targetOrigin;
|
|
31
|
+
}
|
|
15
32
|
export function accessIndex(target, indexArg) {
|
|
16
33
|
const index = typeof indexArg === 'number' ? coerceToSnippet(indexArg) : indexArg;
|
|
34
|
+
const knownIndex = asComptime(index);
|
|
17
35
|
// array
|
|
18
36
|
if (isWgslArray(target.dataType) || isDisarray(target.dataType)) {
|
|
19
37
|
const elementType = target.dataType.elementType;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Constant refs stay const unless the element/index forces runtime materialization
|
|
23
|
-
origin =
|
|
24
|
-
index.origin === 'constant' || index.origin === 'constant-immutable-def'
|
|
25
|
-
? 'constant-immutable-def'
|
|
26
|
-
: 'runtime-immutable-def';
|
|
27
|
-
}
|
|
28
|
-
else if (target.origin === 'constant') {
|
|
29
|
-
// Ephemeral constants indexed with constants stay constant, otherwise they become runtime-known
|
|
30
|
-
origin =
|
|
31
|
-
index.origin === 'constant' || index.origin === 'constant-immutable-def'
|
|
32
|
-
? 'constant'
|
|
33
|
-
: 'runtime';
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
// Fallthrough
|
|
37
|
-
origin = target.origin;
|
|
38
|
-
}
|
|
39
|
-
if (target.value instanceof ArrayExpression && isKnownAtComptime(index)) {
|
|
40
|
-
return target.value.elements[index.value];
|
|
38
|
+
if (target.value instanceof ArrayExpression && knownIndex) {
|
|
39
|
+
return target.value.elements[knownIndex.value];
|
|
41
40
|
}
|
|
42
|
-
|
|
41
|
+
const origin = deriveIndexAccessOrigin(target.origin, index.origin);
|
|
42
|
+
const knownTarget = knownIndex && asComptime(target);
|
|
43
|
+
return snip(knownTarget && knownIndex
|
|
43
44
|
? // oxlint-disable-next-line typescript/no-explicit-any -- it's fine, it's there
|
|
44
|
-
|
|
45
|
+
knownTarget.value[knownIndex.value]
|
|
45
46
|
: stitch `${target}[${index}]`, elementType,
|
|
46
47
|
/* origin */ origin, target.possibleSideEffects || index.possibleSideEffects);
|
|
47
48
|
}
|
|
48
49
|
// vector
|
|
49
50
|
if (isVec(target.dataType)) {
|
|
50
|
-
|
|
51
|
+
const knownTarget = knownIndex && asComptime(target);
|
|
52
|
+
return snip(knownTarget && knownIndex
|
|
51
53
|
? // oxlint-disable-next-line typescript/no-explicit-any -- it's fine, it's there
|
|
52
|
-
|
|
53
|
-
: stitch `${target}[${index}]`, target.dataType.primitive,
|
|
54
|
-
/* origin */ target.origin, target.possibleSideEffects || index.possibleSideEffects);
|
|
54
|
+
knownTarget.value[knownIndex.value]
|
|
55
|
+
: stitch `${target}[${index}]`, target.dataType.primitive, deriveIndexAccessOrigin(target.origin, index.origin), target.possibleSideEffects || index.possibleSideEffects);
|
|
55
56
|
}
|
|
56
57
|
if (isPtr(target.dataType)) {
|
|
57
58
|
// Sometimes values that are typed as pointers aren't instances of `d.ref`, so we
|
|
@@ -61,23 +62,21 @@ export function accessIndex(target, indexArg) {
|
|
|
61
62
|
// matrix.columns
|
|
62
63
|
if (target.value instanceof MatrixColumnsAccess) {
|
|
63
64
|
const propType = indexableTypeToResult[target.value.matrix.dataType.type];
|
|
64
|
-
return snip(stitch `${target.value.matrix}[${index}]`, propType,
|
|
65
|
-
/* origin */ target.origin, target.possibleSideEffects || index.possibleSideEffects);
|
|
65
|
+
return snip(stitch `${target.value.matrix}[${index}]`, propType, deriveIndexAccessOrigin(target.origin, index.origin), target.possibleSideEffects || index.possibleSideEffects);
|
|
66
66
|
}
|
|
67
67
|
// matrix
|
|
68
68
|
if (target.dataType.type in indexableTypeToResult) {
|
|
69
69
|
throw new Error("The only way of accessing matrix elements in TypeGPU functions is through the 'columns' property.");
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
const knownTarget = knownIndex && asComptime(target);
|
|
72
|
+
if (knownTarget && knownIndex) {
|
|
72
73
|
// No idea what the type is, so we act on the snippet's value and try to guess
|
|
73
74
|
return coerceToSnippet(
|
|
74
75
|
// oxlint-disable-next-line typescript/no-explicit-any -- we're inspecting the value, and it could be any value
|
|
75
|
-
|
|
76
|
+
knownTarget.value[knownIndex.value]);
|
|
76
77
|
}
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
typeof index.value === 'string') {
|
|
80
|
-
return accessProp(target, index.value);
|
|
78
|
+
if (typeof knownIndex?.value === 'string') {
|
|
79
|
+
return accessProp(target, knownIndex.value);
|
|
81
80
|
}
|
|
82
81
|
return undefined;
|
|
83
82
|
}
|
package/tgsl/accessProp.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { stitch } from "../core/resolve/stitch.js";
|
|
2
2
|
import { AutoStruct } from "../data/autoStruct.js";
|
|
3
3
|
import { EntryInputRouter } from "../core/function/entryInputRouter.js";
|
|
4
|
-
import { isUnstruct,
|
|
4
|
+
import { isUnstruct, UnknownData } from "../data/dataTypes.js";
|
|
5
|
+
import { MatrixColumnsAccess } from "../data/matrixColumnsAccess.js";
|
|
5
6
|
import { bool, f16, f32, i32, u32 } from "../data/numeric.js";
|
|
6
7
|
import { derefSnippet } from "../data/ref.js";
|
|
7
8
|
import { isSnippet, snip } from "../data/snippet.js";
|
|
8
9
|
import { vec2b, vec2f, vec2h, vec2i, vec2u, vec3b, vec3f, vec3h, vec3i, vec3u, vec4b, vec4f, vec4h, vec4i, vec4u, } from "../data/vector.js";
|
|
9
10
|
import { isMat, isPtr, isVec, isWgslArray, isWgslStruct, } from "../data/wgslTypes.js";
|
|
10
|
-
import {
|
|
11
|
+
import { asComptime } from "../types.js";
|
|
11
12
|
import { coerceToSnippet, numericLiteralToSnippet } from "./generationHelpers.js";
|
|
12
13
|
import { InfixDispatch, infixOperators } from "./infixDispatch.js";
|
|
13
14
|
import { accessStructProp } from "./accessStructProp.js";
|
|
@@ -128,9 +129,10 @@ export function accessProp(target, propName) {
|
|
|
128
129
|
if (!swizzleType) {
|
|
129
130
|
return undefined;
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
+
const knownTarget = asComptime(target);
|
|
133
|
+
return snip(knownTarget
|
|
132
134
|
? // oxlint-disable-next-line typescript/no-explicit-any -- it's fine, the prop is there
|
|
133
|
-
|
|
135
|
+
knownTarget.value[propName]
|
|
134
136
|
: stitch `${target}.${propName}`, swizzleType,
|
|
135
137
|
// Swizzling creates new vectors (unless they're on the lhs of an assignment, but that's not yet supported in WGSL)
|
|
136
138
|
/* origin */ propLength === 1
|
|
@@ -139,9 +141,10 @@ export function accessProp(target, propName) {
|
|
|
139
141
|
? 'constant'
|
|
140
142
|
: 'runtime', target.possibleSideEffects);
|
|
141
143
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
const knownTarget = asComptime(target);
|
|
145
|
+
if (knownTarget) {
|
|
146
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
147
|
+
const prop = knownTarget.value[propName];
|
|
145
148
|
if (isNamable(prop) && getName(prop) === undefined) {
|
|
146
149
|
setName(prop, propName);
|
|
147
150
|
}
|
package/tgsl/conversion.js
CHANGED
|
@@ -10,6 +10,7 @@ import { safeStringify } from "../shared/stringify.js";
|
|
|
10
10
|
import { assertExhaustive } from "../shared/utilityTypes.js";
|
|
11
11
|
import { logger } from "../tgpuLogger.js";
|
|
12
12
|
import { accessStructProp } from "./accessStructProp.js";
|
|
13
|
+
import { stringifySnippet } from "./stringifySnippet.js";
|
|
13
14
|
const INFINITE_RANK = {
|
|
14
15
|
rank: Number.POSITIVE_INFINITY,
|
|
15
16
|
action: 'none',
|
|
@@ -254,7 +255,7 @@ export function convertToCommonType(ctx, values, restrictTo, verbose = true) {
|
|
|
254
255
|
}
|
|
255
256
|
if (verbose && conversion.hasImplicitConversions) {
|
|
256
257
|
logger.warn('implicit-conversion', `Implicit conversions from [\n${values
|
|
257
|
-
.map((v) => ` ${
|
|
258
|
+
.map((v) => ` ${stringifySnippet(v)}: ${safeStringify(v.dataType)}`)
|
|
258
259
|
.join(',\n')}\n] to ${conversion.targetType.type} are supported, but not recommended.
|
|
259
260
|
Consider using explicit conversions instead.`);
|
|
260
261
|
}
|
|
@@ -281,7 +282,7 @@ export function tryConvertSnippet(ctx, snippet, targetDataTypes, verbose = true)
|
|
|
281
282
|
if (converted) {
|
|
282
283
|
return converted[0];
|
|
283
284
|
}
|
|
284
|
-
throw new WgslTypeError(`Cannot convert
|
|
285
|
+
throw new WgslTypeError(`Cannot convert '${stringifySnippet(snippet)}' of type '${String(dataType)}' to any of the target types: [${targets.map((t) => t.type).join(', ')}]`);
|
|
285
286
|
}
|
|
286
287
|
export function convertStructValues(ctx, structType, values) {
|
|
287
288
|
return Object.entries(structType.propTypes).map(([key, targetType]) => {
|
package/tgsl/forOfUtils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { UnknownData } from "../data/dataTypes.js";
|
|
2
|
-
import {
|
|
3
|
-
import { stitch } from "../core/resolve/stitch.js";
|
|
2
|
+
import { isAddressableOrigin, isStoredInMemory, snip } from "../data/snippet.js";
|
|
4
3
|
import * as wgsl from "../data/wgslTypes.js";
|
|
5
4
|
import { i32, u32 } from "../data/numeric.js";
|
|
6
5
|
import { invariant, WgslTypeError } from "../errors.js";
|
|
@@ -9,6 +8,7 @@ import { accessIndex } from "./accessIndex.js";
|
|
|
9
8
|
import { createPtrFromOrigin, implicitFrom } from "../data/ptr.js";
|
|
10
9
|
import { $gpuCallable } from "../shared/symbols.js";
|
|
11
10
|
import { ArrayExpression, concretize } from "./generationHelpers.js";
|
|
11
|
+
import { stringifySnippet } from "./stringifySnippet.js";
|
|
12
12
|
import { isTgpuRange } from "../std/range.js";
|
|
13
13
|
export function getLoopVarKind(elementSnippet) {
|
|
14
14
|
// If it's ephemeral, it's a value that cannot change. If it's a reference, we take
|
|
@@ -28,13 +28,10 @@ export function getElementSnippet(iterableSnippet, index) {
|
|
|
28
28
|
export function getElementType(elementSnippet, iterableSnippet) {
|
|
29
29
|
let elementType = elementSnippet.dataType;
|
|
30
30
|
if (elementType === UnknownData) {
|
|
31
|
-
throw new WgslTypeError(
|
|
31
|
+
throw new WgslTypeError(`The elements in iterable '${stringifySnippet(iterableSnippet)}' are of unknown type`);
|
|
32
32
|
}
|
|
33
33
|
if (wgsl.isNaturallyEphemeral(elementSnippet.dataType) ||
|
|
34
|
-
elementSnippet.origin
|
|
35
|
-
elementSnippet.origin === 'constant' ||
|
|
36
|
-
elementSnippet.origin === 'constant-immutable-def' ||
|
|
37
|
-
elementSnippet.origin === 'runtime-immutable-def') {
|
|
34
|
+
!isAddressableOrigin(elementSnippet.origin)) {
|
|
38
35
|
return elementType;
|
|
39
36
|
}
|
|
40
37
|
if (!wgsl.isPtr(elementType)) {
|
|
@@ -56,7 +53,10 @@ export function getRangeSnippets(ctx, iterableSnippet, unroll = false) {
|
|
|
56
53
|
comparison: step < 0 ? '>' : '<',
|
|
57
54
|
};
|
|
58
55
|
}
|
|
59
|
-
if (
|
|
56
|
+
if (iterableSnippet.possibleSideEffects) {
|
|
57
|
+
throw new Error('`for ... of ...` loops do not support iterables with possible side effects. Store the iterable in a variable first.');
|
|
58
|
+
}
|
|
59
|
+
if (!unroll && !isStoredInMemory(iterableSnippet)) {
|
|
60
60
|
throw new Error(`\`for ... of ...\` loops only support std.range or iterables stored in variables.
|
|
61
61
|
-----
|
|
62
62
|
You can wrap iterable with \`tgpu.unroll(...)\`. If iterable is known at comptime, the loop will be unrolled.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ResolvedSnippet, Snippet } from '../data/snippet.ts';
|
|
2
2
|
import { type AnyWgslData, type BaseData, type F32, type I32, type WgslArray } from '../data/wgslTypes.ts';
|
|
3
|
-
import { type ResolutionCtx, type SelfResolvable } from '../types.ts';
|
|
4
|
-
import { $internal, $resolve } from '../shared/symbols.ts';
|
|
3
|
+
import { type ComptimeValue, type ResolutionCtx, type SelfResolvable, type WithComptimeValue } from '../types.ts';
|
|
4
|
+
import { $comptimeValueOf, $internal, $resolve } from '../shared/symbols.ts';
|
|
5
5
|
export declare function numericLiteralToSnippet(value: number): Snippet;
|
|
6
6
|
export declare function concretize<T extends BaseData>(type: T): T | F32 | I32;
|
|
7
7
|
export declare function concretizeSnippet(snippet: Snippet): Snippet;
|
|
@@ -12,11 +12,12 @@ export declare function coerceToSnippet(value: unknown): Snippet;
|
|
|
12
12
|
* Defers resolution. Stores array elements as snippets so the
|
|
13
13
|
* generator can access them when needed.
|
|
14
14
|
*/
|
|
15
|
-
export declare class ArrayExpression implements SelfResolvable {
|
|
15
|
+
export declare class ArrayExpression implements SelfResolvable, WithComptimeValue {
|
|
16
16
|
readonly [$internal] = true;
|
|
17
17
|
readonly type: WgslArray<AnyWgslData>;
|
|
18
18
|
readonly elements: Snippet[];
|
|
19
19
|
constructor(type: WgslArray<AnyWgslData>, elements: Snippet[]);
|
|
20
20
|
toString(): string;
|
|
21
|
+
[$comptimeValueOf](): ComptimeValue;
|
|
21
22
|
[$resolve](ctx: ResolutionCtx): ResolvedSnippet;
|
|
22
23
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { UnknownData } from "../data/dataTypes.js";
|
|
2
2
|
import { abstractFloat, abstractInt, bool, f32, i32 } from "../data/numeric.js";
|
|
3
3
|
import { isRef } from "../data/ref.js";
|
|
4
|
-
import {
|
|
4
|
+
import { isStoredInMemory, isSnippet, snip, withDataType } from "../data/snippet.js";
|
|
5
5
|
import { isMatInstance, isNaturallyEphemeral, isVecInstance, WORKAROUND_getSchema, } from "../data/wgslTypes.js";
|
|
6
|
-
import { getOwnSnippet } from "../types.js";
|
|
6
|
+
import { asComptime, getOwnSnippet, } from "../types.js";
|
|
7
7
|
import { WgslTypeError } from "../errors.js";
|
|
8
|
-
import { $internal, $resolve } from "../shared/symbols.js";
|
|
8
|
+
import { $comptimeValueOf, $internal, $resolve } from "../shared/symbols.js";
|
|
9
9
|
import { logger } from "../tgpuLogger.js";
|
|
10
|
+
import { stringifySnippet } from "./stringifySnippet.js";
|
|
10
11
|
export function numericLiteralToSnippet(value) {
|
|
11
12
|
if (value >= 2 ** 63 || value < -(2 ** 63)) {
|
|
12
13
|
return snip(value, abstractFloat, /* origin */ 'constant', /* possibleSideEffects */ false);
|
|
@@ -78,11 +79,23 @@ export class ArrayExpression {
|
|
|
78
79
|
toString() {
|
|
79
80
|
return 'ArrayExpression';
|
|
80
81
|
}
|
|
82
|
+
[$comptimeValueOf]() {
|
|
83
|
+
// The array is known at comptime only if every element is known at comptime
|
|
84
|
+
const values = [];
|
|
85
|
+
for (const elem of this.elements) {
|
|
86
|
+
const known = asComptime(elem);
|
|
87
|
+
if (!known) {
|
|
88
|
+
return { comptime: false };
|
|
89
|
+
}
|
|
90
|
+
values.push(known.value);
|
|
91
|
+
}
|
|
92
|
+
return { comptime: true, value: values };
|
|
93
|
+
}
|
|
81
94
|
[$resolve](ctx) {
|
|
82
95
|
for (const elem of this.elements) {
|
|
83
96
|
// We check if there are no references among the elements
|
|
84
|
-
if (
|
|
85
|
-
const snippetStr =
|
|
97
|
+
if (isStoredInMemory(elem) && !isNaturallyEphemeral(elem.dataType)) {
|
|
98
|
+
const snippetStr = stringifySnippet(elem);
|
|
86
99
|
const snippetType = ctx.resolve(concretize(elem.dataType)).value;
|
|
87
100
|
throw new WgslTypeError(`'${snippetStr}' reference cannot be used in an array constructor.\n-----\nTry '${snippetType}(${snippetStr})' or 'arrayOf(${snippetType}, count)([...])' to copy the value instead.\n-----`);
|
|
88
101
|
}
|
package/tgsl/infixDispatch.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { MatBase } from '../data/matrix.ts';
|
|
|
2
2
|
import { type Snippet } from '../data/snippet.ts';
|
|
3
3
|
import type { VecBase } from '../data/vectorImpl.ts';
|
|
4
4
|
import type { AnyMatInstance, AnyNumericVecInstance } from '../data/wgslTypes.ts';
|
|
5
|
+
import { $comptimeValueOf } from '../shared/symbols.ts';
|
|
6
|
+
import { type ComptimeValue, type WithComptimeValue } from '../types.ts';
|
|
5
7
|
export declare const infixOperators: {
|
|
6
8
|
readonly add: import("../types.ts").DualFn<{
|
|
7
9
|
(lhs: number, rhs: number): number;
|
|
@@ -56,10 +58,11 @@ export type InfixOperator = (typeof infixOperators)[InfixOperatorName];
|
|
|
56
58
|
* d.vec2u(1).mul(2) // lhs is a snippet
|
|
57
59
|
* }
|
|
58
60
|
*/
|
|
59
|
-
export declare class InfixDispatch {
|
|
61
|
+
export declare class InfixDispatch implements WithComptimeValue {
|
|
60
62
|
readonly lhs: Snippet | Numeric;
|
|
61
63
|
readonly operator: InfixOperator;
|
|
62
64
|
constructor(lhs: Snippet | Numeric, operator: InfixOperator);
|
|
65
|
+
[$comptimeValueOf](): ComptimeValue;
|
|
63
66
|
}
|
|
64
67
|
export declare function isInfixDispatch(o: unknown): o is InfixDispatch;
|
|
65
68
|
/**
|
package/tgsl/infixDispatch.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {} from "../data/snippet.js";
|
|
1
|
+
import { isSnippet } from "../data/snippet.js";
|
|
2
2
|
import { inCodegenMode } from "../execMode.js";
|
|
3
|
+
import { $comptimeValueOf } from "../shared/symbols.js";
|
|
4
|
+
import { asComptime } from "../types.js";
|
|
3
5
|
import { add, bitShiftLeft, bitShiftRight, div, mod, mul, sub } from "../std/operators.js";
|
|
4
6
|
export const infixOperators = {
|
|
5
7
|
add,
|
|
@@ -28,6 +30,16 @@ export class InfixDispatch {
|
|
|
28
30
|
this.lhs = lhs;
|
|
29
31
|
this.operator = operator;
|
|
30
32
|
}
|
|
33
|
+
[$comptimeValueOf]() {
|
|
34
|
+
const known = isSnippet(this.lhs) ? asComptime(this.lhs) : { value: this.lhs };
|
|
35
|
+
if (!known) {
|
|
36
|
+
return { comptime: false };
|
|
37
|
+
}
|
|
38
|
+
// Same as accessing e.g. `.mul` on a vector in JS, but bound to `lhs`
|
|
39
|
+
const lhs = known.value;
|
|
40
|
+
const operator = this.operator;
|
|
41
|
+
return { comptime: true, value: (rhs) => operator(lhs, rhs) };
|
|
42
|
+
}
|
|
31
43
|
}
|
|
32
44
|
export function isInfixDispatch(o) {
|
|
33
45
|
return o instanceof InfixDispatch;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from '../data/snippet.ts';
|
|
2
|
-
import type
|
|
2
|
+
import { type SimulationState } from '../types.ts';
|
|
3
3
|
import type { makeResolvable } from './makeResolvable.ts';
|
|
4
4
|
/**
|
|
5
5
|
* WARNING: This is an API that touches a lot of internals, and is not stable
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { $gpuValueOf, $internal, $ownSnippet, $resolve } from "../shared/symbols.js";
|
|
1
|
+
import { $comptimeValueOf, $gpuValueOf, $internal, $ownSnippet, $resolve, } from "../shared/symbols.js";
|
|
2
2
|
import { valueProxyHandler } from "../core/valueProxyUtils.js";
|
|
3
|
+
import { runtimeOnly } from "../types.js";
|
|
3
4
|
import { getExecMode } from "../execMode.js";
|
|
4
5
|
import { assertExhaustive } from "../shared/utilityTypes.js";
|
|
5
6
|
/**
|
|
@@ -30,6 +31,7 @@ export function makeDereferenceable(value, options) {
|
|
|
30
31
|
}
|
|
31
32
|
return options.codegenMode.getBaseSnippet.apply(resource, [this]);
|
|
32
33
|
},
|
|
34
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
33
35
|
[$resolve]: (ctx) => ctx.resolve(resource),
|
|
34
36
|
toString: () => `${resource.toString()}.$`,
|
|
35
37
|
}, valueProxyHandler);
|
package/tgsl/shellless.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { createShelllessImpl } from "../core/function/shelllessImpl.js";
|
|
2
2
|
import { UnknownData } from "../data/dataTypes.js";
|
|
3
|
-
import { RefOperator } from "../data/ref.js";
|
|
4
3
|
import { isPtr, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
|
|
5
|
-
import { WgslTypeError } from "../errors.js";
|
|
6
4
|
import { getFunctionMetadata, getName } from "../shared/meta.js";
|
|
7
5
|
import { concretize } from "./generationHelpers.js";
|
|
6
|
+
import { stringifySnippet } from "./stringifySnippet.js";
|
|
8
7
|
function shallowEqualSchemas(a, b) {
|
|
9
8
|
if (a.type !== b.type)
|
|
10
9
|
return false;
|
|
@@ -34,14 +33,11 @@ export class ShelllessRepository {
|
|
|
34
33
|
throw new Error(`Cannot resolve '${getName(fn)}' directly, because it expects arguments. Either call it from another function, or wrap it in a shell`);
|
|
35
34
|
}
|
|
36
35
|
const argTypes = (argSnippets ?? []).map((s, index) => {
|
|
37
|
-
if (s.value instanceof RefOperator) {
|
|
38
|
-
if (s.dataType === UnknownData) {
|
|
39
|
-
throw new WgslTypeError(`d.ref() created with primitive types must be stored in a variable before use`);
|
|
40
|
-
}
|
|
41
|
-
return s.dataType;
|
|
42
|
-
}
|
|
43
36
|
if (s.dataType === UnknownData) {
|
|
44
|
-
|
|
37
|
+
const snippetStr = stringifySnippet(s);
|
|
38
|
+
const valueStr = String(s.value);
|
|
39
|
+
const description = snippetStr === valueStr ? `'${snippetStr}'` : `'${snippetStr}' (${valueStr})`;
|
|
40
|
+
throw new Error(`Passed illegal value ${description} as the #${index} argument to ${getName(fn) ?? '<unnamed>'}(...)\n` +
|
|
45
41
|
`Shellless functions can only accept arguments representing WGSL resources: constructible WGSL types, d.refs, samplers or texture views.\n` +
|
|
46
42
|
`Remember, that arguments such as samplers, texture views, accessors, slots etc. should be dereferenced via '.$' first.`);
|
|
47
43
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type * as tinyest from 'tinyest';
|
|
2
|
+
import type { Snippet } from '../data/snippet.ts';
|
|
3
|
+
export declare function setSourceNode(snippet: Snippet, node: tinyest.Expression): Snippet;
|
|
4
|
+
export declare function getSourceNode(snippet: Snippet): tinyest.Expression | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* Describes a snippet in a human-readable way, meant to be used in error messages.
|
|
7
|
+
* Prefers the JS expression that produced the snippet, falling back to its value.
|
|
8
|
+
*/
|
|
9
|
+
export declare function stringifySnippet(snippet: Snippet): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { stringifyNode } from "../shared/tseynit.js";
|
|
2
|
+
/**
|
|
3
|
+
* The JS expression that first produced a given snippet.
|
|
4
|
+
* Snippets can be shared (e.g. every reference to a variable returns the same snippet),
|
|
5
|
+
* so only the first, most specific node is remembered.
|
|
6
|
+
*/
|
|
7
|
+
const sourceNodes = new WeakMap();
|
|
8
|
+
export function setSourceNode(snippet, node) {
|
|
9
|
+
if (!sourceNodes.has(snippet)) {
|
|
10
|
+
sourceNodes.set(snippet, node);
|
|
11
|
+
}
|
|
12
|
+
return snippet;
|
|
13
|
+
}
|
|
14
|
+
export function getSourceNode(snippet) {
|
|
15
|
+
return sourceNodes.get(snippet);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Describes a snippet in a human-readable way, meant to be used in error messages.
|
|
19
|
+
* Prefers the JS expression that produced the snippet, falling back to its value.
|
|
20
|
+
*/
|
|
21
|
+
export function stringifySnippet(snippet) {
|
|
22
|
+
const node = getSourceNode(snippet);
|
|
23
|
+
return node !== undefined ? stringifyNode(node) : String(snippet.value);
|
|
24
|
+
}
|
package/tgsl/wgslGenerator.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export declare class WgslGenerator implements ShaderGenerator {
|
|
|
27
27
|
*/
|
|
28
28
|
protected _typedExpression(expression: tinyest.Expression, expectedType: wgsl.BaseData | wgsl.BaseData[]): Snippet;
|
|
29
29
|
protected _expression(expression: tinyest.Expression): Snippet;
|
|
30
|
+
private _expressionImpl;
|
|
30
31
|
declareGlobalConst(options: ConstantDefinitionOptions): ResolvedSnippet;
|
|
31
32
|
declareGlobalVar(options: VariableDefinitionOptions): ResolvedSnippet;
|
|
32
33
|
functionDefinition(options: FunctionDefinitionOptions): string;
|