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.
Files changed (87) hide show
  1. package/common/writeSoA.js +16 -0
  2. package/core/buffer/buffer.js +47 -11
  3. package/core/commandEncoder/attachments.js +5 -2
  4. package/core/function/autoIO.js +5 -3
  5. package/core/function/comptime.js +7 -5
  6. package/core/function/createCallableSchema.js +4 -3
  7. package/core/function/dualImpl.js +20 -19
  8. package/core/function/ioSchema.d.ts +17 -2
  9. package/core/function/ioSchema.js +74 -27
  10. package/core/function/tgpuFn.d.ts +11 -1
  11. package/core/function/tgpuFn.js +9 -2
  12. package/core/function/tgpuFragmentFn.d.ts +1 -1
  13. package/core/function/tgpuFragmentFn.js +2 -1
  14. package/core/function/tgpuVertexFn.js +2 -1
  15. package/core/pipeline/connectAttachmentToShader.d.ts +2 -2
  16. package/core/pipeline/connectAttachmentToShader.js +16 -21
  17. package/core/pipeline/connectTargetsToShader.d.ts +5 -1
  18. package/core/pipeline/connectTargetsToShader.js +23 -18
  19. package/core/pipeline/renderPipeline.d.ts +9 -6
  20. package/core/pipeline/renderPipeline.js +22 -22
  21. package/core/pipeline/timeable.js +15 -2
  22. package/core/resolve/namespace.js +2 -2
  23. package/core/root/configurableImpl.js +1 -2
  24. package/core/root/rootTypes.d.ts +6 -2
  25. package/core/slot/accessor.d.ts +1 -1
  26. package/core/slot/accessor.js +72 -5
  27. package/core/texture/externalTexture.d.ts +1 -1
  28. package/core/texture/externalTexture.js +3 -1
  29. package/core/texture/texture.d.ts +1 -1
  30. package/core/texture/texture.js +12 -5
  31. package/core/unroll/tgpuUnroll.d.ts +4 -3
  32. package/core/unroll/tgpuUnroll.js +7 -1
  33. package/core/valueProxyUtils.d.ts +4 -2
  34. package/core/valueProxyUtils.js +4 -2
  35. package/data/autoStruct.d.ts +1 -1
  36. package/data/autoStruct.js +8 -4
  37. package/data/compiledIO.js +12 -9
  38. package/data/dataIO.d.ts +10 -6
  39. package/data/dataIO.js +19 -4
  40. package/data/dataTypes.d.ts +0 -5
  41. package/data/dataTypes.js +0 -6
  42. package/data/deepEqual.js +19 -9
  43. package/data/matrix.js +10 -3
  44. package/data/matrixColumnsAccess.d.ts +8 -0
  45. package/data/matrixColumnsAccess.js +15 -0
  46. package/data/offsetUtils.js +181 -102
  47. package/data/partialIO.d.ts +8 -1
  48. package/data/partialIO.js +36 -19
  49. package/data/ref.d.ts +12 -5
  50. package/data/ref.js +51 -24
  51. package/data/snippet.d.ts +13 -4
  52. package/data/snippet.js +17 -4
  53. package/data/struct.js +3 -0
  54. package/data/wgslTypes.d.ts +6 -3
  55. package/internal.d.ts +1 -0
  56. package/nameUtils.d.ts +7 -0
  57. package/nameUtils.js +60 -0
  58. package/package.json +1 -1
  59. package/resolutionCtx.js +1 -1
  60. package/shared/meta.js +1 -1
  61. package/shared/symbols.d.ts +5 -0
  62. package/shared/symbols.js +6 -1
  63. package/shared/tseynit.js +54 -50
  64. package/std/boolean.js +9 -10
  65. package/std/numeric.js +1 -4
  66. package/std/operators.js +9 -4
  67. package/std/packing.js +10 -10
  68. package/std/texture.d.ts +2 -2
  69. package/tgpuLogger.d.ts +1 -1
  70. package/tgpuLogger.js +0 -2
  71. package/tgsl/accessIndex.js +37 -38
  72. package/tgsl/accessProp.js +10 -7
  73. package/tgsl/conversion.js +3 -2
  74. package/tgsl/forOfUtils.js +8 -8
  75. package/tgsl/generationHelpers.d.ts +4 -3
  76. package/tgsl/generationHelpers.js +18 -5
  77. package/tgsl/infixDispatch.d.ts +4 -1
  78. package/tgsl/infixDispatch.js +13 -1
  79. package/tgsl/makeDereferenceable.d.ts +1 -1
  80. package/tgsl/makeDereferenceable.js +3 -1
  81. package/tgsl/shellless.js +5 -9
  82. package/tgsl/stringifySnippet.d.ts +9 -0
  83. package/tgsl/stringifySnippet.js +24 -0
  84. package/tgsl/wgslGenerator.d.ts +1 -0
  85. package/tgsl/wgslGenerator.js +180 -86
  86. package/types.d.ts +23 -2
  87. 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: (_ctx, [e]) => stitch `unpack4x8unorm(${e})`,
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: (_ctx, [e]) => stitch `unpack4x8snorm(${e})`,
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: (_ctx, [e]) => stitch `unpack4xI8(${e})`,
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: (_ctx, [e]) => stitch `unpack4xU8(${e})`,
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: (_ctx, [e]) => stitch `pack4x8unorm(${e})`,
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: (_ctx, [e]) => stitch `pack4x8snorm(${e})`,
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: (_ctx, [e]) => stitch `pack4xI8(${e})`,
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: (_ctx, [e]) => stitch `pack4xU8(${e})`,
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: (_ctx, [e]) => stitch `pack4xI8Clamp(${e})`,
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: (_ctx, [e]) => stitch `pack4xU8Clamp(${e})`,
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", "locations-mismatched", "log-limit-exceeded", "external-omitted", "uniform-schema-misaligned"];
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
  ];
@@ -1,10 +1,11 @@
1
1
  import { stitch } from "../core/resolve/stitch.js";
2
- import { isDisarray, MatrixColumnsAccess } from "../data/dataTypes.js";
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, isWgslStruct } from "../data/wgslTypes.js";
7
- import { isKnownAtComptime } from "../types.js";
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
- let origin;
21
- if (target.origin === 'constant-immutable-def') {
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
- return snip(isKnownAtComptime(target) && isKnownAtComptime(index)
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
- target.value[index.value]
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
- return snip(isKnownAtComptime(target) && isKnownAtComptime(index)
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
- target.value[index.value]
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
- if (isKnownAtComptime(target) && isKnownAtComptime(index)) {
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
- target.value[index.value]);
76
+ knownTarget.value[knownIndex.value]);
76
77
  }
77
- if (isWgslStruct(target.dataType) &&
78
- isKnownAtComptime(index) &&
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
  }
@@ -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, MatrixColumnsAccess, UnknownData } from "../data/dataTypes.js";
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 { isKnownAtComptime } from "../types.js";
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
- return snip(isKnownAtComptime(target)
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
- target.value[propName]
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
- if (isKnownAtComptime(target) || target.dataType === UnknownData) {
143
- // oxlint-disable-next-line typescript/no-explicit-any -- we either know exactly what it is, or have no idea at all
144
- const prop = target.value[propName];
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
  }
@@ -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) => ` ${ctx.resolveSnippet(v).value}: ${safeStringify(v.dataType)}`)
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 value of type '${String(dataType)}' to any of the target types: [${targets.map((t) => t.type).join(', ')}]`);
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]) => {
@@ -1,6 +1,5 @@
1
1
  import { UnknownData } from "../data/dataTypes.js";
2
- import { isAlias, snip } from "../data/snippet.js";
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(stitch `The elements in iterable ${iterableSnippet} are of unknown type`);
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 === 'runtime' ||
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 (!unroll && !isAlias(iterableSnippet)) {
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 { isAlias, isSnippet, snip, withDataType } from "../data/snippet.js";
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 (isAlias(elem) && !isNaturallyEphemeral(elem.dataType)) {
85
- const snippetStr = ctx.resolveSnippet(elem).value;
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
  }
@@ -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
  /**
@@ -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 { SimulationState } from '../types.ts';
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
- throw new Error(`Passed illegal value ${s.value} as the #${index} argument to ${getName(fn) ?? '<unnamed>'}(...)\n` +
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
+ }
@@ -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;