typegpu 0.10.2 → 0.11.0-rc.1

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 (211) hide show
  1. package/_virtual/_rolldown/runtime.js +6 -11
  2. package/builtin.js +1 -3
  3. package/common/fullScreenTriangle.d.ts +1 -5
  4. package/common/fullScreenTriangle.js +1 -3
  5. package/common/index.d.ts +3 -2
  6. package/common/index.js +6 -4
  7. package/common/writeSoA.d.ts +16 -0
  8. package/common/writeSoA.js +90 -0
  9. package/core/buffer/buffer.d.ts +12 -7
  10. package/core/buffer/buffer.js +102 -53
  11. package/core/buffer/bufferShorthand.d.ts +5 -5
  12. package/core/buffer/bufferShorthand.js +9 -5
  13. package/core/buffer/bufferUsage.d.ts +0 -2
  14. package/core/buffer/bufferUsage.js +6 -4
  15. package/core/constant/tgpuConstant.d.ts +2 -1
  16. package/core/constant/tgpuConstant.js +8 -7
  17. package/core/declare/tgpuDeclare.js +8 -9
  18. package/core/function/autoIO.d.ts +7 -6
  19. package/core/function/autoIO.js +1 -3
  20. package/core/function/comptime.js +1 -3
  21. package/core/function/createCallableSchema.js +4 -6
  22. package/core/function/dualImpl.js +1 -3
  23. package/core/function/entryInputRouter.js +39 -0
  24. package/core/function/extractArgs.js +2 -2
  25. package/core/function/fnCore.js +19 -8
  26. package/core/function/fnTypes.d.ts +14 -8
  27. package/core/function/ioSchema.js +24 -3
  28. package/core/function/shelllessImpl.js +1 -3
  29. package/core/function/templateUtils.js +1 -2
  30. package/core/function/tgpuComputeFn.d.ts +2 -3
  31. package/core/function/tgpuComputeFn.js +9 -16
  32. package/core/function/tgpuFn.d.ts +2 -2
  33. package/core/function/tgpuFn.js +1 -3
  34. package/core/function/tgpuFragmentFn.d.ts +5 -1
  35. package/core/function/tgpuFragmentFn.js +5 -10
  36. package/core/function/tgpuVertexFn.d.ts +4 -0
  37. package/core/function/tgpuVertexFn.js +6 -12
  38. package/core/pipeline/applyPipelineState.js +1 -3
  39. package/core/pipeline/computePipeline.d.ts +2 -6
  40. package/core/pipeline/computePipeline.js +64 -63
  41. package/core/pipeline/connectAttachmentToShader.js +1 -3
  42. package/core/pipeline/connectTargetsToShader.js +1 -3
  43. package/core/pipeline/limitsOverflow.js +1 -2
  44. package/core/pipeline/pipelineUtils.js +29 -0
  45. package/core/pipeline/renderPipeline.d.ts +23 -5
  46. package/core/pipeline/renderPipeline.js +32 -14
  47. package/core/pipeline/timeable.d.ts +0 -3
  48. package/core/pipeline/timeable.js +3 -9
  49. package/core/pipeline/typeGuards.js +1 -3
  50. package/core/querySet/querySet.d.ts +0 -2
  51. package/core/querySet/querySet.js +37 -36
  52. package/core/rawCodeSnippet/tgpuRawCodeSnippet.js +1 -3
  53. package/core/resolve/externals.d.ts +0 -2
  54. package/core/resolve/externals.js +2 -4
  55. package/core/resolve/namespace.js +1 -3
  56. package/core/resolve/resolveData.js +1 -3
  57. package/core/resolve/stitch.js +1 -3
  58. package/core/resolve/tgpuResolve.d.ts +3 -1
  59. package/core/resolve/tgpuResolve.js +3 -5
  60. package/core/root/configurableImpl.js +2 -3
  61. package/core/root/init.d.ts +0 -5
  62. package/core/root/init.js +35 -28
  63. package/core/root/rootTypes.d.ts +25 -5
  64. package/core/sampler/sampler.d.ts +0 -4
  65. package/core/sampler/sampler.js +3 -3
  66. package/core/simulate/tgpuSimulate.js +1 -3
  67. package/core/slot/accessor.d.ts +0 -4
  68. package/core/slot/accessor.js +1 -3
  69. package/core/slot/internalSlots.js +1 -3
  70. package/core/slot/lazy.js +1 -3
  71. package/core/slot/slot.js +2 -3
  72. package/core/slot/slotTypes.js +1 -3
  73. package/core/texture/externalTexture.d.ts +0 -6
  74. package/core/texture/externalTexture.js +2 -3
  75. package/core/texture/texture.d.ts +0 -4
  76. package/core/texture/texture.js +5 -3
  77. package/core/texture/textureFormats.js +1 -3
  78. package/core/texture/textureUtils.js +1 -3
  79. package/core/texture/usageExtension.js +1 -3
  80. package/core/unroll/tgpuUnroll.d.ts +58 -3
  81. package/core/unroll/tgpuUnroll.js +63 -5
  82. package/core/valueProxyUtils.js +1 -3
  83. package/core/variable/tgpuVariable.js +1 -3
  84. package/core/vertexLayout/connectAttributesToShader.js +1 -3
  85. package/core/vertexLayout/vertexLayout.js +9 -9
  86. package/data/alignIO.js +1 -2
  87. package/data/alignmentOf.d.ts +0 -1
  88. package/data/alignmentOf.js +1 -3
  89. package/data/array.d.ts +1 -3
  90. package/data/array.js +2 -4
  91. package/data/atomic.js +2 -3
  92. package/data/attributes.js +3 -3
  93. package/data/autoStruct.d.ts +1 -3
  94. package/data/autoStruct.js +1 -3
  95. package/data/compiledIO.js +83 -86
  96. package/data/dataIO.js +46 -39
  97. package/data/dataTypes.d.ts +7 -7
  98. package/data/dataTypes.js +6 -3
  99. package/data/deepEqual.js +1 -3
  100. package/data/disarray.d.ts +1 -3
  101. package/data/disarray.js +1 -3
  102. package/data/getLongestContiguousPrefix.d.ts +0 -1
  103. package/data/getLongestContiguousPrefix.js +1 -3
  104. package/data/index.d.ts +3 -3
  105. package/data/index.js +10 -3
  106. package/data/isContiguous.d.ts +0 -1
  107. package/data/isContiguous.js +1 -3
  108. package/data/matrix.d.ts +8 -10
  109. package/data/matrix.js +32 -18
  110. package/data/numberOps.js +1 -2
  111. package/data/numeric.js +16 -29
  112. package/data/offsetUtils.d.ts +2 -2
  113. package/data/offsetUtils.js +3 -5
  114. package/data/offsets.js +1 -3
  115. package/data/partialIO.js +84 -39
  116. package/data/ptr.d.ts +0 -1
  117. package/data/ptr.js +1 -3
  118. package/data/ref.d.ts +0 -3
  119. package/data/ref.js +1 -3
  120. package/data/sampler.js +1 -3
  121. package/data/schemaCallWrapper.js +1 -3
  122. package/data/schemaMemoryLayout.js +1 -3
  123. package/data/sizeOf.d.ts +0 -1
  124. package/data/sizeOf.js +1 -3
  125. package/data/snippet.js +12 -3
  126. package/data/struct.js +1 -3
  127. package/data/texture.js +1 -3
  128. package/data/unstruct.js +1 -3
  129. package/data/vector.js +4 -12
  130. package/data/vectorImpl.js +27 -28
  131. package/data/vectorOps.js +20 -3
  132. package/data/vertexFormatData.js +2 -3
  133. package/data/wgslTypes.d.ts +39 -11
  134. package/data/wgslTypes.js +10 -3
  135. package/errors.js +6 -3
  136. package/execMode.js +1 -3
  137. package/extension.js +1 -3
  138. package/getGPUValue.js +1 -3
  139. package/index.d.ts +4 -2
  140. package/index.js +3 -3
  141. package/indexNamedExports.d.ts +3 -1
  142. package/mathUtils.js +1 -2
  143. package/memo.js +8 -8
  144. package/nameRegistry.js +1 -3
  145. package/package.js +2 -3
  146. package/package.json +5 -5
  147. package/resolutionCtx.d.ts +0 -10
  148. package/resolutionCtx.js +84 -18
  149. package/shared/env.js +1 -2
  150. package/shared/generators.js +1 -2
  151. package/shared/meta.js +1 -3
  152. package/shared/repr.d.ts +32 -2
  153. package/shared/stringify.js +1 -3
  154. package/shared/symbols.d.ts +10 -1
  155. package/shared/symbols.js +10 -33
  156. package/shared/utilityTypes.d.ts +6 -2
  157. package/shared/utilityTypes.js +1 -2
  158. package/shared/vertexFormat.js +1 -2
  159. package/std/array.d.ts +1 -1
  160. package/std/array.js +1 -3
  161. package/std/atomic.d.ts +12 -12
  162. package/std/atomic.js +1 -3
  163. package/std/bitcast.d.ts +2 -2
  164. package/std/bitcast.js +1 -3
  165. package/std/boolean.d.ts +30 -16
  166. package/std/boolean.js +37 -12
  167. package/std/derivative.d.ts +9 -9
  168. package/std/derivative.js +1 -3
  169. package/std/discard.d.ts +1 -1
  170. package/std/discard.js +1 -3
  171. package/std/extensions.d.ts +1 -3
  172. package/std/extensions.js +1 -3
  173. package/std/index.d.ts +5 -4
  174. package/std/index.js +8 -5
  175. package/std/matrix.d.ts +5 -5
  176. package/std/matrix.js +1 -3
  177. package/std/numeric.d.ts +78 -132
  178. package/std/numeric.js +1 -3
  179. package/std/operators.d.ts +16 -8
  180. package/std/operators.js +80 -6
  181. package/std/packing.d.ts +4 -4
  182. package/std/packing.js +1 -3
  183. package/std/range.d.ts +24 -0
  184. package/std/range.js +38 -0
  185. package/std/subgroup.d.ts +21 -21
  186. package/std/subgroup.js +1 -3
  187. package/std/texture.d.ts +20 -11
  188. package/std/texture.js +13 -3
  189. package/tgpu.js +1 -3
  190. package/tgpuBindGroupLayout.js +9 -8
  191. package/tgpuUnstable.js +1 -3
  192. package/tgsl/accessIndex.js +2 -4
  193. package/tgsl/accessProp.js +8 -6
  194. package/tgsl/consoleLog/deserializers.js +1 -3
  195. package/tgsl/consoleLog/logGenerator.js +2 -4
  196. package/tgsl/consoleLog/serializers.js +24 -26
  197. package/tgsl/consoleLog/types.d.ts +0 -2
  198. package/tgsl/consoleLog/types.js +1 -2
  199. package/tgsl/conversion.js +1 -3
  200. package/tgsl/forOfUtils.js +35 -9
  201. package/tgsl/generationHelpers.js +3 -3
  202. package/tgsl/math.js +1 -3
  203. package/tgsl/shaderGenerator.d.ts +10 -8
  204. package/tgsl/shaderGenerator_members.d.ts +2 -0
  205. package/tgsl/shaderGenerator_members.js +6 -0
  206. package/tgsl/shellless.js +1 -8
  207. package/tgsl/wgslGenerator.d.ts +36 -0
  208. package/tgsl/wgslGenerator.js +135 -81
  209. package/types.d.ts +14 -4
  210. package/types.js +3 -3
  211. package/wgslExtensions.js +1 -2
@@ -7,7 +7,6 @@ import { getOwnSnippet } from "../types.js";
7
7
  import { stitch } from "../core/resolve/stitch.js";
8
8
  import { isRef } from "../data/ref.js";
9
9
  import { abstractFloat, abstractInt, bool, f32, i32 } from "../data/numeric.js";
10
-
11
10
  //#region src/tgsl/generationHelpers.ts
12
11
  function numericLiteralToSnippet(value) {
13
12
  if (value >= 2 ** 63 || value < -(2 ** 63)) return snip(value, abstractFloat, "constant");
@@ -46,6 +45,8 @@ function coerceToSnippet(value) {
46
45
  */
47
46
  var ArrayExpression = class {
48
47
  [$internal] = true;
48
+ type;
49
+ elements;
49
50
  constructor(type, elements) {
50
51
  this.type = type;
51
52
  this.elements = elements;
@@ -62,6 +63,5 @@ var ArrayExpression = class {
62
63
  return snip(stitch`${`array<${ctx.resolve(this.type.elementType).value}, ${this.elements.length}>`}(${this.elements})`, this.type, "runtime");
63
64
  }
64
65
  };
65
-
66
66
  //#endregion
67
- export { ArrayExpression, coerceToSnippet, concretize, concretizeSnippets, numericLiteralToSnippet };
67
+ export { ArrayExpression, coerceToSnippet, concretize, concretizeSnippets, numericLiteralToSnippet };
package/tgsl/math.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { f32 } from "../data/numeric.js";
2
2
  import { abs, acos, acosh, asin, asinh, atan, atan2, atanh, ceil, cos, cosh, countLeadingZeros, exp, floor, log, log2, max, min, pow, sign, sin, sinh, sqrt, tan, tanh, trunc } from "../std/numeric.js";
3
-
4
3
  //#region src/tgsl/math.ts
5
4
  const mathToStd = {
6
5
  abs,
@@ -40,6 +39,5 @@ const mathToStd = {
40
39
  imul: void 0,
41
40
  round: void 0
42
41
  };
43
-
44
42
  //#endregion
45
- export { mathToStd };
43
+ export { mathToStd };
@@ -1,18 +1,20 @@
1
- import { Snippet } from "../data/snippet.js";
1
+ import { ResolvedSnippet, Snippet } from "../data/snippet.js";
2
2
  import { GenerationCtx } from "./generationHelpers.js";
3
- import { ExternalMap } from "../core/resolve/externals.js";
4
3
  import { BaseData } from "../data/wgslTypes.js";
5
- import { Block, Expression, Statement } from "tinyest";
4
+ import { Block } from "tinyest";
6
5
 
7
6
  //#region src/tgsl/shaderGenerator.d.ts
7
+ /**
8
+ * **NOTE: This is an unstable API and may change in the future.**
9
+ *
10
+ * An interface meant to be used by other systems to generate snippets of
11
+ * shader code in the target language (WGSL, GLSL, etc.).
12
+ */
8
13
  interface ShaderGenerator {
9
14
  initGenerator(ctx: GenerationCtx): void;
10
- block(body: Block, externalMap?: ExternalMap): string;
11
- identifier(id: string): Snippet;
12
- typedExpression(expression: Expression, expectedType: BaseData): Snippet;
13
- expression(expression: Expression): Snippet;
14
- statement(statement: Statement): string;
15
15
  functionDefinition(body: Block): string;
16
+ typeInstantiation(schema: BaseData, args: readonly Snippet[]): ResolvedSnippet;
17
+ typeAnnotation(schema: BaseData): string;
16
18
  }
17
19
  //#endregion
18
20
  export { ShaderGenerator };
@@ -0,0 +1,2 @@
1
+ import { ResolutionCtx } from "../types.js";
2
+ import { UnknownData } from "../data/dataTypes.js";
@@ -0,0 +1,6 @@
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { UnknownData } from "../data/dataTypes.js";
3
+ //#region src/tgsl/shaderGenerator_members.ts
4
+ var shaderGenerator_members_exports = /* @__PURE__ */ __exportAll({ UnknownData: () => UnknownData });
5
+ //#endregion
6
+ export { shaderGenerator_members_exports };
package/tgsl/shellless.js CHANGED
@@ -2,11 +2,9 @@ import { getMetaData, getName } from "../shared/meta.js";
2
2
  import { isPtr, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
3
3
  import { UnknownData } from "../data/dataTypes.js";
4
4
  import { WgslTypeError } from "../errors.js";
5
- import { getResolutionCtx } from "../execMode.js";
6
5
  import { RefOperator } from "../data/ref.js";
7
6
  import { concretize } from "./generationHelpers.js";
8
7
  import { createShelllessImpl } from "../core/function/shelllessImpl.js";
9
-
10
8
  //#region src/tgsl/shellless.ts
11
9
  function shallowEqualSchemas(a, b) {
12
10
  if (a.type !== b.type) return false;
@@ -28,10 +26,6 @@ var ShelllessRepository = class {
28
26
  }
29
27
  if (s.dataType === UnknownData) throw new Error(`Passed illegal value ${s.value} as the #${index} argument to ${meta.name}(...)\nShellless functions can only accept arguments representing WGSL resources: constructible WGSL types, d.refs, samplers or texture views.\nRemember, that arguments such as samplers, texture views, accessors, slots etc. should be dereferenced via '.$' first.`);
30
28
  let type = concretize(s.dataType);
31
- if (s.origin === "constant-tgpu-const-ref" || s.origin === "runtime-tgpu-const-ref") {
32
- const ctx = getResolutionCtx();
33
- throw new Error(`Cannot pass constant references as function arguments. Explicitly copy them by wrapping them in a schema: '${ctx.resolve(type).value}(...)'`);
34
- }
35
29
  if (isPtr(type) && type.implicit) type = type.inner;
36
30
  return type;
37
31
  });
@@ -48,6 +42,5 @@ var ShelllessRepository = class {
48
42
  return shellless;
49
43
  }
50
44
  };
51
-
52
45
  //#endregion
53
- export { ShelllessRepository };
46
+ export { ShelllessRepository };
@@ -0,0 +1,36 @@
1
+ import { Origin, ResolvedSnippet, Snippet } from "../data/snippet.js";
2
+ import { GenerationCtx } from "./generationHelpers.js";
3
+ import { ShaderGenerator } from "./shaderGenerator.js";
4
+ import { BaseData, StorableData } from "../data/wgslTypes.js";
5
+ import { UnknownData } from "../data/dataTypes.js";
6
+ import { ExternalMap } from "../core/resolve/externals.js";
7
+ import * as tinyest from "tinyest";
8
+
9
+ //#region src/tgsl/wgslGenerator.d.ts
10
+ declare class WgslGenerator implements ShaderGenerator {
11
+ #private;
12
+ initGenerator(ctx: GenerationCtx): void;
13
+ protected get ctx(): GenerationCtx;
14
+ _block([_, statements]: tinyest.Block, externalMap?: ExternalMap): string;
15
+ refVariable(id: string, dataType: StorableData): string;
16
+ blockVariable(varType: 'var' | 'let' | 'const', id: string, dataType: BaseData | UnknownData, origin: Origin): Snippet;
17
+ protected emitVarDecl(pre: string, keyword: 'var' | 'let' | 'const', name: string, _dataType: BaseData | UnknownData, rhsStr: string): string;
18
+ _identifier(id: string): Snippet;
19
+ /**
20
+ * A wrapper for `generateExpression` that updates `ctx.expectedType`
21
+ * and tries to convert the result when it does not match the expected type.
22
+ */
23
+ _typedExpression(expression: tinyest.Expression, expectedType: BaseData | BaseData[]): Snippet;
24
+ _expression(expression: tinyest.Expression): Snippet;
25
+ functionDefinition(body: tinyest.Block): string;
26
+ /**
27
+ * Generates a WGSL type string for the given data type, and adds necessary
28
+ * definitions to the shader preamble. This shouldn't be called directly, only
29
+ * through `ctx.resolve` to properly cache the result.
30
+ */
31
+ typeAnnotation(data: BaseData): string;
32
+ typeInstantiation(schema: BaseData, args: readonly Snippet[]): ResolvedSnippet;
33
+ _statement(statement: tinyest.Statement): string;
34
+ }
35
+ //#endregion
36
+ export { WgslGenerator };
@@ -1,8 +1,8 @@
1
1
  import { $gpuCallable, $internal, $providing, isMarkedInternal } from "../shared/symbols.js";
2
2
  import { getName } from "../shared/meta.js";
3
- import { Void, isNaturallyEphemeral, isPtr, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
3
+ import { Void, isBool, isNaturallyEphemeral, isNumericSchema, isPtr, isVec, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
4
4
  import { ConsoleLog, InfixDispatch, UnknownData, isLooseData, unptr } from "../data/dataTypes.js";
5
- import { isEphemeralOrigin, isEphemeralSnippet, snip } from "../data/snippet.js";
5
+ import { fallthroughCopyOrigin, isEphemeralOrigin, isEphemeralSnippet, snip } from "../data/snippet.js";
6
6
  import { ResolutionError, WgslTypeError, invariant } from "../errors.js";
7
7
  import { isGPUCallable, isKnownAtComptime } from "../types.js";
8
8
  import { stitch } from "../core/resolve/stitch.js";
@@ -11,8 +11,9 @@ import { RefOperator } from "../data/ref.js";
11
11
  import { safeStringify } from "../shared/stringify.js";
12
12
  import { convertStructValues, convertToCommonType, tryConvertSnippet } from "./conversion.js";
13
13
  import { bool, i32, u32 } from "../data/numeric.js";
14
- import { AutoStruct } from "../data/autoStruct.js";
15
14
  import { ArrayExpression, coerceToSnippet, concretize, numericLiteralToSnippet } from "./generationHelpers.js";
15
+ import { vec2u, vec3u, vec4u } from "../data/vector.js";
16
+ import { AutoStruct } from "../data/autoStruct.js";
16
17
  import { add, div, mul, neg, sub } from "../std/operators.js";
17
18
  import { accessProp } from "./accessProp.js";
18
19
  import { accessIndex } from "./accessIndex.js";
@@ -20,11 +21,12 @@ import { constant } from "../core/constant/tgpuConstant.js";
20
21
  import { isGenericFn } from "../core/function/tgpuFn.js";
21
22
  import { arrayOf } from "../data/array.js";
22
23
  import { pow } from "../std/numeric.js";
24
+ import { resolveData } from "../core/resolve/resolveData.js";
23
25
  import { UnrollableIterable } from "../core/unroll/tgpuUnroll.js";
24
26
  import { mathToStd } from "./math.js";
25
- import { getElementCountSnippet, getElementSnippet, getElementType, getLoopVarKind } from "./forOfUtils.js";
27
+ import { isTgpuRange } from "../std/range.js";
28
+ import { getElementSnippet, getElementType, getLoopVarKind, getRangeSnippets } from "./forOfUtils.js";
26
29
  import * as tinyest from "tinyest";
27
-
28
30
  //#region src/tgsl/wgslGenerator.ts
29
31
  const { NodeTypeCatalog: NODE } = tinyest;
30
32
  const parenthesizedOps = [
@@ -61,6 +63,12 @@ const binaryLogicalOps = [
61
63
  ">",
62
64
  ">="
63
65
  ];
66
+ const bitShiftOps = [
67
+ "<<",
68
+ ">>",
69
+ "<<=",
70
+ ">>="
71
+ ];
64
72
  const OP_MAP = {
65
73
  "===": "==",
66
74
  "!==": "!=",
@@ -106,7 +114,19 @@ function operatorToType(lhs, op, rhs) {
106
114
  }
107
115
  const unaryOpCodeToCodegen = {
108
116
  "-": neg[$gpuCallable].call.bind(neg),
109
- void: () => snip(void 0, Void, "constant")
117
+ void: () => snip(void 0, Void, "constant"),
118
+ "!": (ctx, [argExpr]) => {
119
+ if (argExpr === void 0) throw new Error("The unary operator `!` expects 1 argument, but 0 were provided.");
120
+ if (isKnownAtComptime(argExpr)) return snip(!argExpr.value, bool, "constant");
121
+ const { value, dataType } = argExpr;
122
+ const argStr = ctx.resolve(value, dataType).value;
123
+ if (isBool(dataType)) return snip(`!${argStr}`, bool, "runtime");
124
+ if (isNumericSchema(dataType)) {
125
+ const resultStr = `!bool(${argStr})`;
126
+ return snip(dataType.type === "f32" ? `(((bitcast<u32>(${argStr}) & 0x7fffffff) > 0x7f800000) || ${resultStr})` : dataType.type === "f16" ? `(((bitcast<u32>(${argStr}) & 0x7fff) > 0x7c00) || ${resultStr})` : resultStr, bool, "runtime");
127
+ }
128
+ return snip(false, bool, "constant");
129
+ }
110
130
  };
111
131
  const binaryOpCodeToCodegen = {
112
132
  "+": add[$gpuCallable].call.bind(add),
@@ -125,7 +145,7 @@ var WgslGenerator = class {
125
145
  if (!this.#ctx) throw new Error("WGSL Generator has not yet been initialized. Please call initialize(ctx) before using the generator.");
126
146
  return this.#ctx;
127
147
  }
128
- block([_, statements], externalMap) {
148
+ _block([_, statements], externalMap) {
129
149
  this.ctx.pushBlockScope();
130
150
  if (externalMap) {
131
151
  const externals = Object.fromEntries(Object.entries(externalMap).map(([id, value]) => [id, coerceToSnippet(value)]));
@@ -133,7 +153,7 @@ var WgslGenerator = class {
133
153
  }
134
154
  try {
135
155
  this.ctx.indent();
136
- const body = statements.map((statement) => this.statement(statement)).filter((statement) => statement.length > 0).join("\n");
156
+ const body = statements.map((statement) => this._statement(statement)).filter((statement) => statement.length > 0).join("\n");
137
157
  this.ctx.dedent();
138
158
  return `{
139
159
  ${body}
@@ -162,7 +182,10 @@ ${this.ctx.pre}}`;
162
182
  this.ctx.defineVariable(id, snippet);
163
183
  return snippet;
164
184
  }
165
- identifier(id) {
185
+ emitVarDecl(pre, keyword, name, _dataType, rhsStr) {
186
+ return `${pre}${keyword} ${name} = ${rhsStr};`;
187
+ }
188
+ _identifier(id) {
166
189
  if (!id) throw new Error("Cannot resolve an empty identifier");
167
190
  if (id === "undefined") return snip(void 0, Void, "constant");
168
191
  const res = this.ctx.getById(id);
@@ -173,24 +196,24 @@ ${this.ctx.pre}}`;
173
196
  * A wrapper for `generateExpression` that updates `ctx.expectedType`
174
197
  * and tries to convert the result when it does not match the expected type.
175
198
  */
176
- typedExpression(expression, expectedType) {
199
+ _typedExpression(expression, expectedType) {
177
200
  const prevExpectedType = this.ctx.expectedType;
178
201
  this.ctx.expectedType = expectedType;
179
202
  try {
180
- const result = this.expression(expression);
203
+ const result = this._expression(expression);
181
204
  if (expectedType instanceof AutoStruct) return result;
182
205
  return tryConvertSnippet(this.ctx, result, expectedType);
183
206
  } finally {
184
207
  this.ctx.expectedType = prevExpectedType;
185
208
  }
186
209
  }
187
- expression(expression) {
188
- if (typeof expression === "string") return this.identifier(expression);
210
+ _expression(expression) {
211
+ if (typeof expression === "string") return this._identifier(expression);
189
212
  if (typeof expression === "boolean") return snip(expression, bool, "constant");
190
213
  if (expression[0] === NODE.logicalExpr || expression[0] === NODE.binaryExpr || expression[0] === NODE.assignmentExpr) {
191
214
  const [exprType, lhs, op, rhs] = expression;
192
- const lhsExpr = this.expression(lhs);
193
- const rhsExpr = this.expression(rhs);
215
+ const lhsExpr = this._expression(lhs);
216
+ const rhsExpr = this._expression(rhs);
194
217
  if (rhsExpr.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot assign a ref to an existing variable '${lhsExpr}', define a new variable instead.`);
195
218
  if (op === "==") throw new Error("Please use the === operator instead of ==");
196
219
  if (op === "===" && isKnownAtComptime(lhsExpr) && isKnownAtComptime(rhsExpr)) return snip(lhsExpr.value === rhsExpr.value, bool, "constant");
@@ -198,8 +221,20 @@ ${this.ctx.pre}}`;
198
221
  if (rhsExpr.dataType === UnknownData) throw new WgslTypeError(`Right-hand side of '${op}' is of unknown type`);
199
222
  const codegen = binaryOpCodeToCodegen[op];
200
223
  if (codegen) return codegen(this.ctx, [lhsExpr, rhsExpr]);
201
- const forcedType = exprType === NODE.assignmentExpr ? [lhsExpr.dataType] : void 0;
202
- const [convLhs, convRhs] = convertToCommonType(this.ctx, [lhsExpr, rhsExpr], forcedType) ?? [lhsExpr, rhsExpr];
224
+ let convLhs;
225
+ let convRhs;
226
+ if (bitShiftOps.includes(op)) {
227
+ let rhsTarget;
228
+ if (isVec(lhsExpr.dataType)) {
229
+ const cc = lhsExpr.dataType.componentCount;
230
+ rhsTarget = cc === 2 ? vec2u : cc === 3 ? vec3u : vec4u;
231
+ } else rhsTarget = u32;
232
+ convRhs = tryConvertSnippet(this.ctx, rhsExpr, rhsTarget, false);
233
+ convLhs = lhsExpr;
234
+ } else {
235
+ const forcedType = exprType === NODE.assignmentExpr ? [lhsExpr.dataType] : void 0;
236
+ [convLhs, convRhs] = convertToCommonType(this.ctx, [lhsExpr, rhsExpr], forcedType) ?? [lhsExpr, rhsExpr];
237
+ }
203
238
  const lhsStr = this.ctx.resolve(convLhs.value, convLhs.dataType).value;
204
239
  const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
205
240
  const type = operatorToType(convLhs.dataType, op, convRhs.dataType);
@@ -213,13 +248,13 @@ ${this.ctx.pre}}`;
213
248
  }
214
249
  if (expression[0] === NODE.postUpdate) {
215
250
  const [_, op, arg] = expression;
216
- const argExpr = this.expression(arg);
251
+ const argExpr = this._expression(arg);
217
252
  const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
218
253
  return snip(`${argStr}${op}`, argExpr.dataType, "runtime");
219
254
  }
220
255
  if (expression[0] === NODE.unaryExpr) {
221
256
  const [_, op, arg] = expression;
222
- const argExpr = this.expression(arg);
257
+ const argExpr = this._expression(arg);
223
258
  const codegen = unaryOpCodeToCodegen[op];
224
259
  if (codegen) return codegen(this.ctx, [argExpr]);
225
260
  const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
@@ -228,7 +263,7 @@ ${this.ctx.pre}}`;
228
263
  }
229
264
  if (expression[0] === NODE.memberAccess) {
230
265
  const [_, targetNode, property] = expression;
231
- const target = this.expression(targetNode);
266
+ const target = this._expression(targetNode);
232
267
  if (target.value === console) return snip(new ConsoleLog(property), UnknownData, "runtime");
233
268
  if (target.value === Math) {
234
269
  if (property in mathToStd && mathToStd[property]) return snip(mathToStd[property], UnknownData, "runtime");
@@ -240,8 +275,8 @@ ${this.ctx.pre}}`;
240
275
  }
241
276
  if (expression[0] === NODE.indexAccess) {
242
277
  const [_, targetNode, propertyNode] = expression;
243
- const target = this.expression(targetNode);
244
- const inProperty = this.expression(propertyNode);
278
+ const target = this._expression(targetNode);
279
+ const inProperty = this._expression(propertyNode);
245
280
  const property = convertToCommonType(this.ctx, [inProperty], [u32, i32], false)?.[0] ?? inProperty;
246
281
  const accessed = accessIndex(target, property);
247
282
  if (!accessed) {
@@ -258,27 +293,27 @@ ${this.ctx.pre}}`;
258
293
  }
259
294
  if (expression[0] === NODE.call) {
260
295
  const [_, calleeNode, argNodes] = expression;
261
- const callee = this.expression(calleeNode);
296
+ const callee = this._expression(calleeNode);
262
297
  if (isWgslStruct(callee.value)) {
263
298
  if (argNodes.length > 1) throw new WgslTypeError("Struct schemas should always be called with at most 1 argument");
264
299
  if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
265
- const arg = this.typedExpression(argNodes[0], callee.value);
300
+ const arg = this._typedExpression(argNodes[0], callee.value);
266
301
  return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
267
302
  }
268
303
  if (isWgslArray(callee.value)) {
269
304
  if (argNodes.length > 1) throw new WgslTypeError("Array schemas should always be called with at most 1 argument");
270
305
  if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
271
- const arg = this.typedExpression(argNodes[0], callee.value);
306
+ const arg = this._typedExpression(argNodes[0], callee.value);
272
307
  if (arg.value instanceof ArrayExpression) return snip(stitch`${this.ctx.resolve(callee.value).value}(${arg.value.elements})`, arg.dataType, "runtime");
273
308
  return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
274
309
  }
275
310
  if (callee.value === constant) throw new Error("Constants cannot be defined within TypeGPU function scope. To address this, move the constant definition outside the function scope.");
276
311
  if (callee.value instanceof InfixDispatch) {
277
312
  if (!argNodes[0]) throw new WgslTypeError(`An infix operator '${callee.value.name}' was called without any arguments`);
278
- const rhs = this.expression(argNodes[0]);
313
+ const rhs = this._expression(argNodes[0]);
279
314
  return callee.value.operator(this.ctx, [callee.value.lhs, rhs]);
280
315
  }
281
- if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this.expression(arg)));
316
+ if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this._expression(arg)));
282
317
  if (isGPUCallable(callee.value)) {
283
318
  const callable = callee.value[$gpuCallable];
284
319
  const strictSignature = callable.strictSignature;
@@ -286,9 +321,9 @@ ${this.ctx.pre}}`;
286
321
  if (strictSignature) convertedArguments = argNodes.map((arg, i) => {
287
322
  const argType = strictSignature.argTypes[i];
288
323
  if (!argType) throw new WgslTypeError(`Function '${getName(callee.value)}' was called with too many arguments`);
289
- return this.typedExpression(arg, argType);
324
+ return this._typedExpression(arg, argType);
290
325
  });
291
- else convertedArguments = argNodes.map((arg) => this.expression(arg));
326
+ else convertedArguments = argNodes.map((arg) => this._expression(arg));
292
327
  try {
293
328
  return callable.call(this.ctx, convertedArguments);
294
329
  } catch (err) {
@@ -301,7 +336,7 @@ ${this.ctx.pre}}`;
301
336
  const slotPairs = isGeneric ? callee.value[$providing]?.pairs ?? [] : [];
302
337
  const callback = isGeneric ? callee.value[$internal].inner : callee.value;
303
338
  const shelllessCall = this.ctx.withRenamed(callback, getName(callee.value), () => this.ctx.withSlots(slotPairs, () => {
304
- const args = argNodes.map((arg) => this.expression(arg));
339
+ const args = argNodes.map((arg) => this._expression(arg));
305
340
  const shellless = this.ctx.shelllessRepo.get(callback, args);
306
341
  if (!shellless) return;
307
342
  const converted = args.map((s, idx) => {
@@ -324,11 +359,11 @@ ${this.ctx.pre}}`;
324
359
  const entries = Object.fromEntries(Object.entries(obj).map(([key, value]) => {
325
360
  let accessed = structType.accessProp(key);
326
361
  let expr;
327
- if (accessed) expr = this.typedExpression(value, accessed.type);
362
+ if (accessed) expr = this._typedExpression(value, accessed.type);
328
363
  else {
329
- expr = this.expression(value);
364
+ expr = this._expression(value);
330
365
  if (expr.dataType === UnknownData) throw new WgslTypeError(stitch`Property ${key} in object literal has a value of unknown type: '${expr}'`);
331
- accessed = structType.provideProp(key, concretize(expr.dataType));
366
+ accessed = structType.provideProp(key, unptr(concretize(expr.dataType)));
332
367
  }
333
368
  return [accessed.prop, expr];
334
369
  }));
@@ -340,7 +375,7 @@ ${this.ctx.pre}}`;
340
375
  const entries = Object.fromEntries(Object.entries(structType.propTypes).map(([key, value]) => {
341
376
  const val = obj[key];
342
377
  if (val === void 0) throw new WgslTypeError(`Missing property ${key} in object literal for struct ${structType}`);
343
- return [key, this.typedExpression(val, value)];
378
+ return [key, this._typedExpression(val, value)];
344
379
  }));
345
380
  const convertedSnippets = convertStructValues(this.ctx, structType, entries);
346
381
  return snip(stitch`${this.ctx.resolve(structType).value}(${convertedSnippets})`, structType, "runtime");
@@ -354,10 +389,10 @@ ${this.ctx.pre}}`;
354
389
  let values;
355
390
  if (isWgslArray(arrType)) {
356
391
  elemType = arrType.elementType;
357
- values = valueNodes.map((value) => this.typedExpression(value, elemType));
392
+ values = valueNodes.map((value) => this._typedExpression(value, elemType));
358
393
  if (values.length !== arrType.elementCount) throw new WgslTypeError(`Cannot create value of type '${arrType}' from an array of length: ${values.length}`);
359
394
  } else {
360
- const valuesSnippets = valueNodes.map((value) => this.expression(value));
395
+ const valuesSnippets = valueNodes.map((value) => this._expression(value));
361
396
  if (valuesSnippets.length === 0) throw new WgslTypeError("Cannot infer the type of an empty array literal.");
362
397
  const converted = convertToCommonType(this.ctx, valuesSnippets);
363
398
  if (!converted) throw new WgslTypeError("The given values cannot be automatically converted to a common type. Consider wrapping the array in an appropriate schema");
@@ -369,8 +404,8 @@ ${this.ctx.pre}}`;
369
404
  }
370
405
  if (expression[0] === NODE.conditionalExpr) {
371
406
  const [_, test, consequent, alternative] = expression;
372
- const testExpression = this.expression(test);
373
- if (isKnownAtComptime(testExpression)) return testExpression.value ? this.expression(consequent) : this.expression(alternative);
407
+ const testExpression = this._expression(test);
408
+ if (isKnownAtComptime(testExpression)) return testExpression.value ? this._expression(consequent) : this._expression(alternative);
374
409
  else throw new Error(`Ternary operator is only supported for comptime-known checks (used with '${testExpression.value}'). For runtime checks, please use 'std.select' or if/else statements.`);
375
410
  }
376
411
  if (expression[0] === NODE.stringLiteral) return snip(expression[1], UnknownData, "constant");
@@ -378,11 +413,23 @@ ${this.ctx.pre}}`;
378
413
  assertExhaustive(expression);
379
414
  }
380
415
  functionDefinition(body) {
381
- return this.block(body);
416
+ return this._block(body);
382
417
  }
383
- statement(statement) {
418
+ /**
419
+ * Generates a WGSL type string for the given data type, and adds necessary
420
+ * definitions to the shader preamble. This shouldn't be called directly, only
421
+ * through `ctx.resolve` to properly cache the result.
422
+ */
423
+ typeAnnotation(data) {
424
+ return resolveData(this.ctx, data);
425
+ }
426
+ typeInstantiation(schema, args) {
427
+ if (args.length === 1 && args[0]?.dataType === schema) return snip(stitch`${args[0]}`, schema, fallthroughCopyOrigin(args[0].origin));
428
+ return snip(stitch`${this.ctx.resolve(schema).value}(${args})`, schema, "runtime");
429
+ }
430
+ _statement(statement) {
384
431
  if (typeof statement === "string") {
385
- const id = this.identifier(statement);
432
+ const id = this._identifier(statement);
386
433
  const resolved = id.value && this.ctx.resolve(id.value).value;
387
434
  return resolved ? `${this.ctx.pre}${resolved};` : "";
388
435
  }
@@ -391,7 +438,7 @@ ${this.ctx.pre}}`;
391
438
  const returnNode = statement[1];
392
439
  if (returnNode !== void 0) {
393
440
  const expectedReturnType = this.ctx.topFunctionReturnType;
394
- let returnSnippet = expectedReturnType ? this.typedExpression(returnNode, expectedReturnType) : this.expression(returnNode);
441
+ let returnSnippet = expectedReturnType ? this._typedExpression(returnNode, expectedReturnType) : this._expression(returnNode);
395
442
  if (returnSnippet.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot return references, returning '${returnSnippet.value.snippet}'`);
396
443
  if (returnSnippet.origin === "argument" && !isNaturallyEphemeral(returnSnippet.dataType) && this.ctx.topFunctionScope?.functionType === "normal") throw new WgslTypeError(stitch`Cannot return references to arguments, returning '${returnSnippet}'. Copy the argument before returning it.`);
397
444
  if (!expectedReturnType && !isEphemeralSnippet(returnSnippet) && returnSnippet.origin !== "this-function") {
@@ -411,11 +458,17 @@ Try 'return ${typeStr}(${str});' instead.
411
458
  }
412
459
  if (statement[0] === NODE.if) {
413
460
  const [_, condNode, consNode, altNode] = statement;
414
- const condition = this.typedExpression(condNode, bool);
415
- const consequent = condition.value === false ? void 0 : this.block(blockifySingleStatement(consNode));
416
- const alternate = condition.value === true || !altNode ? void 0 : this.block(blockifySingleStatement(altNode));
417
- if (condition.value === true) return `${this.ctx.pre}${consequent}`;
418
- if (condition.value === false) return alternate ? `${this.ctx.pre}${alternate}` : "";
461
+ const condition = this._typedExpression(condNode, bool);
462
+ if (typeof condition.value === "boolean") {
463
+ let node = condition.value ? consNode : altNode;
464
+ if (node === void 0) return "";
465
+ if (!Array.isArray(node)) node = blockifySingleStatement(node);
466
+ if (node[0] === NODE.block && node[1].length === 1 && node[1][0][0] === NODE.if) return this._statement(node[1][0]);
467
+ if (node[0] === NODE.if) return this._statement(node);
468
+ return `${this.ctx.pre}${this._block(blockifySingleStatement(node))}`;
469
+ }
470
+ const consequent = this._block(blockifySingleStatement(consNode));
471
+ const alternate = !altNode ? void 0 : this._block(blockifySingleStatement(altNode));
419
472
  if (!alternate) return stitch`${this.ctx.pre}if (${condition}) ${consequent}`;
420
473
  return stitch`\
421
474
  ${this.ctx.pre}if (${condition}) ${consequent}
@@ -424,7 +477,7 @@ ${this.ctx.pre}else ${alternate}`;
424
477
  if (statement[0] === NODE.let || statement[0] === NODE.const) {
425
478
  let varType = "var";
426
479
  const [stmtType, rawId, rawValue] = statement;
427
- const eq = rawValue !== void 0 ? this.expression(rawValue) : void 0;
480
+ const eq = rawValue !== void 0 ? this._expression(rawValue) : void 0;
428
481
  if (!eq) throw new Error(`Cannot create variable '${rawId}' without an initial value.`);
429
482
  const ephemeral = isEphemeralSnippet(eq);
430
483
  let dataType = eq.dataType;
@@ -472,9 +525,11 @@ ${this.ctx.pre}else ${alternate}`;
472
525
  }
473
526
  }
474
527
  const snippet = this.blockVariable(varType, rawId, concretize(dataType), eq.origin);
475
- return stitch`${this.ctx.pre}${varType} ${snippet.value} = ${tryConvertSnippet(this.ctx, eq, dataType, false)};`;
528
+ const rhsSnippet = tryConvertSnippet(this.ctx, eq, dataType, false);
529
+ const rhsStr = this.ctx.resolve(rhsSnippet.value, rhsSnippet.dataType).value;
530
+ return this.emitVarDecl(this.ctx.pre, varType, snippet.value, concretize(dataType), rhsStr);
476
531
  }
477
- if (statement[0] === NODE.block) return `${this.ctx.pre}${this.block(statement)}`;
532
+ if (statement[0] === NODE.block) return `${this.ctx.pre}${this._block(statement)}`;
478
533
  if (statement[0] === NODE.for) {
479
534
  const [_, init, condition, update, body] = statement;
480
535
  const prevUnrollingFlag = this.#unrolling;
@@ -482,13 +537,13 @@ ${this.ctx.pre}else ${alternate}`;
482
537
  try {
483
538
  this.ctx.pushBlockScope();
484
539
  const [initStatement, conditionExpr, updateStatement] = this.ctx.withResetIndentLevel(() => [
485
- init ? this.statement(init) : void 0,
486
- condition ? this.typedExpression(condition, bool) : void 0,
487
- update ? this.statement(update) : void 0
540
+ init ? this._statement(init) : void 0,
541
+ condition ? this._typedExpression(condition, bool) : void 0,
542
+ update ? this._statement(update) : void 0
488
543
  ]);
489
544
  const initStr = initStatement ? initStatement.slice(0, -1) : "";
490
545
  const updateStr = updateStatement ? updateStatement.slice(0, -1) : "";
491
- const bodyStr = this.block(blockifySingleStatement(body));
546
+ const bodyStr = this._block(blockifySingleStatement(body));
492
547
  return stitch`${this.ctx.pre}for (${initStr}; ${conditionExpr}; ${updateStr}) ${bodyStr}`;
493
548
  } finally {
494
549
  this.#unrolling = prevUnrollingFlag;
@@ -500,9 +555,9 @@ ${this.ctx.pre}else ${alternate}`;
500
555
  this.#unrolling = false;
501
556
  try {
502
557
  const [_, condition, body] = statement;
503
- const condSnippet = this.typedExpression(condition, bool);
558
+ const condSnippet = this._typedExpression(condition, bool);
504
559
  const conditionStr = this.ctx.resolve(condSnippet.value).value;
505
- const bodyStr = this.block(blockifySingleStatement(body));
560
+ const bodyStr = this._block(blockifySingleStatement(body));
506
561
  return `${this.ctx.pre}while (${conditionStr}) ${bodyStr}`;
507
562
  } finally {
508
563
  this.#unrolling = prevUnrollingFlag;
@@ -515,40 +570,40 @@ ${this.ctx.pre}else ${alternate}`;
515
570
  const prevUnrollingFlag = this.#unrolling;
516
571
  try {
517
572
  this.ctx.pushBlockScope();
518
- const iterableExpr = this.expression(iterable);
573
+ const iterableExpr = this._expression(iterable);
519
574
  const shouldUnroll = iterableExpr.value instanceof UnrollableIterable;
520
575
  const iterableSnippet = shouldUnroll ? iterableExpr.value.snippet : iterableExpr;
521
- const elementCountSnippet = getElementCountSnippet(this.ctx, iterableSnippet, shouldUnroll);
576
+ const range = getRangeSnippets(this.ctx, iterableSnippet, shouldUnroll);
522
577
  const originalLoopVarName = loopVar[1];
523
578
  const blockified = blockifySingleStatement(body);
524
579
  if (shouldUnroll) {
525
- if (!isKnownAtComptime(elementCountSnippet)) throw new Error("Cannot unroll loop. Length of iterable is unknown at comptime.");
580
+ if (!isKnownAtComptime(range.end)) throw new Error("Cannot unroll loop. Length of iterable is unknown at comptime.");
526
581
  this.#unrolling = true;
527
- const length = elementCountSnippet.value;
582
+ const length = range.end.value;
528
583
  if (length === 0) return "";
529
584
  const { value } = iterableSnippet;
530
- const elements = value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
585
+ const elements = isTgpuRange(value) ? value.map((i) => coerceToSnippet(i)) : value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
531
586
  if (isEphemeralSnippet(elements[0]) && !isNaturallyEphemeral(elements[0]?.dataType)) throw new WgslTypeError("Cannot unroll loop. The elements of iterable are emphemeral but not naturally ephemeral.");
532
- return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this.ctx.pre}${this.block(blockified, { [originalLoopVarName]: e })}`).join("\n");
587
+ return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this.ctx.pre}${this._block(blockified, { [originalLoopVarName]: e })}`).join("\n");
533
588
  }
534
- if (isEphemeralSnippet(iterableSnippet)) throw new Error(`\`for ... of ...\` loops only support iterables stored in variables.
535
- -----
536
- You can wrap iterable with \`tgpu.unroll(...)\`. If iterable is known at comptime, the loop will be unrolled.
537
- -----`);
538
589
  this.#unrolling = false;
539
590
  const index = this.ctx.makeNameValid("i");
540
- const elementSnippet = getElementSnippet(iterableSnippet, snip(index, u32, "runtime"));
541
- const loopVarName = this.ctx.makeNameValid(originalLoopVarName);
542
- const loopVarKind = getLoopVarKind(elementSnippet);
543
- const elementType = getElementType(elementSnippet, iterableSnippet);
544
- const forHeaderStr = stitch`${this.ctx.pre}for (var ${index} = 0u; ${index} < ${elementCountSnippet}; ${index}++) {`;
545
- this.ctx.indent();
546
- ctxIndent = true;
547
- const loopVarDeclStr = stitch`${this.ctx.pre}${loopVarKind} ${loopVarName} = ${tryConvertSnippet(this.ctx, elementSnippet, elementType, false)};`;
548
- const bodyStr = `${this.ctx.pre}${this.block(blockified, { [originalLoopVarName]: snip(loopVarName, elementType, elementSnippet.origin) })}`;
549
- this.ctx.dedent();
550
- ctxIndent = false;
551
- return stitch`${forHeaderStr}\n${loopVarDeclStr}\n${bodyStr}\n${this.ctx.pre}}`;
591
+ const forHeaderStr = stitch`${this.ctx.pre}for (var ${index} = ${range.start}; ${index} ${range.comparison} ${range.end}; ${index} += ${range.step})`;
592
+ let bodyStr = "";
593
+ if (isTgpuRange(iterableSnippet.value)) bodyStr = this._block(blockified, { [originalLoopVarName]: snip(index, u32, "runtime") });
594
+ else {
595
+ this.ctx.indent();
596
+ ctxIndent = true;
597
+ const loopVarName = this.ctx.makeNameValid(originalLoopVarName);
598
+ const elementSnippet = getElementSnippet(iterableSnippet, snip(index, u32, "runtime"));
599
+ const loopVarKind = getLoopVarKind(elementSnippet);
600
+ const elementType = getElementType(elementSnippet, iterableSnippet);
601
+ bodyStr = `{\n${stitch`${this.ctx.pre}${loopVarKind} ${loopVarName} = ${tryConvertSnippet(this.ctx, elementSnippet, elementType, false)};`}\n${this.ctx.pre}${this._block(blockified, { [originalLoopVarName]: snip(loopVarName, elementType, elementSnippet.origin) })}\n`;
602
+ this.ctx.dedent();
603
+ bodyStr += `${this.ctx.pre}}`;
604
+ ctxIndent = false;
605
+ }
606
+ return stitch`${forHeaderStr} ${bodyStr.trim()}`;
552
607
  } finally {
553
608
  if (ctxIndent) this.ctx.dedent();
554
609
  this.#unrolling = prevUnrollingFlag;
@@ -563,7 +618,7 @@ ${this.ctx.pre}else ${alternate}`;
563
618
  if (this.#unrolling) throw new WgslTypeError("Cannot unroll loop containing `break`");
564
619
  return `${this.ctx.pre}break;`;
565
620
  }
566
- const expr = this.expression(statement);
621
+ const expr = this._expression(statement);
567
622
  const resolved = expr.value && this.ctx.resolve(expr.value).value;
568
623
  return resolved ? `${this.ctx.pre}${resolved};` : "";
569
624
  }
@@ -580,6 +635,5 @@ function blockifySingleStatement(statement) {
580
635
  return typeof statement !== "object" || statement[0] !== NODE.block ? [NODE.block, [statement]] : statement;
581
636
  }
582
637
  const wgslGenerator = new WgslGenerator();
583
-
584
638
  //#endregion
585
- export { wgslGenerator as default };
639
+ export { WgslGenerator, wgslGenerator as default };