typegpu 0.11.2 → 0.11.4

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 (51) hide show
  1. package/README.md +1 -1
  2. package/core/buffer/buffer.js +17 -53
  3. package/core/function/autoIO.js +2 -2
  4. package/core/function/entryInputRouter.js +10 -16
  5. package/core/function/fnCore.js +16 -12
  6. package/core/function/shelllessImpl.js +1 -1
  7. package/core/function/tgpuComputeFn.js +1 -1
  8. package/core/function/tgpuFn.js +1 -1
  9. package/core/function/tgpuFragmentFn.d.ts +1 -1
  10. package/core/function/tgpuFragmentFn.js +1 -1
  11. package/core/function/tgpuVertexFn.js +1 -1
  12. package/core/pipeline/computePipeline.d.ts +1 -1
  13. package/core/root/init.d.ts +1 -0
  14. package/core/root/init.js +6 -0
  15. package/core/root/rootTypes.d.ts +21 -15
  16. package/core/slot/accessor.js +1 -1
  17. package/data/dataIO.d.ts +11 -0
  18. package/data/dataIO.js +45 -1
  19. package/data/dataTypes.js +2 -11
  20. package/data/index.d.ts +3 -3
  21. package/data/numeric.js +16 -14
  22. package/data/partialIO.d.ts +8 -0
  23. package/data/partialIO.js +6 -1
  24. package/data/snippet.d.ts +1 -6
  25. package/data/wgslTypes.d.ts +2 -0
  26. package/index.d.ts +3 -1
  27. package/index.js +3 -1
  28. package/indexNamedExports.d.ts +2 -0
  29. package/package.js +1 -1
  30. package/package.json +1 -1
  31. package/resolutionCtx.js +82 -86
  32. package/shared/stringify.js +1 -0
  33. package/shared/tseynit.js +90 -0
  34. package/tgsl/accessIndex.js +4 -3
  35. package/tgsl/accessProp.js +7 -2
  36. package/tgsl/consoleLog/deserializers.js +1 -1
  37. package/tgsl/consoleLog/logGenerator.js +16 -14
  38. package/tgsl/consoleLog/types.d.ts +4 -5
  39. package/tgsl/conversion.js +15 -5
  40. package/tgsl/forOfUtils.js +14 -6
  41. package/tgsl/generationHelpers.d.ts +2 -1
  42. package/tgsl/generationHelpers.js +1 -4
  43. package/tgsl/jsPolyfills.d.ts +25 -0
  44. package/tgsl/jsPolyfills.js +44 -0
  45. package/tgsl/shaderGenerator.d.ts +2 -3
  46. package/tgsl/shaderGenerator_members.d.ts +15 -2
  47. package/tgsl/wgslGenerator.d.ts +3 -1
  48. package/tgsl/wgslGenerator.js +64 -59
  49. package/types.d.ts +11 -6
  50. package/tgsl/consoleLog/types.js +0 -12
  51. package/tgsl/math.js +0 -45
package/data/numeric.js CHANGED
@@ -2,20 +2,6 @@ import { $internal } from "../shared/symbols.js";
2
2
  import { callableSchema } from "../core/function/createCallableSchema.js";
3
3
 
4
4
  //#region src/data/numeric.ts
5
- const abstractInt = {
6
- [$internal]: {},
7
- type: "abstractInt",
8
- toString() {
9
- return "abstractInt";
10
- }
11
- };
12
- const abstractFloat = {
13
- [$internal]: {},
14
- type: "abstractFloat",
15
- toString() {
16
- return "abstractFloat";
17
- }
18
- };
19
5
  const boolCast = callableSchema({
20
6
  name: "bool",
21
7
  schema: () => bool,
@@ -218,6 +204,22 @@ const f16 = Object.assign(f16Cast, {
218
204
  [$internal]: {},
219
205
  type: "f16"
220
206
  });
207
+ const abstractInt = {
208
+ [$internal]: {},
209
+ type: "abstractInt",
210
+ toString() {
211
+ return "abstractInt";
212
+ },
213
+ concretized: i32
214
+ };
215
+ const abstractFloat = {
216
+ [$internal]: {},
217
+ type: "abstractFloat",
218
+ toString() {
219
+ return "abstractFloat";
220
+ },
221
+ concretized: f32
222
+ };
221
223
 
222
224
  //#endregion
223
225
  export { abstractFloat, abstractInt, bool, f16, f32, i32, u16, u32 };
@@ -0,0 +1,8 @@
1
+ import { BaseData } from "./wgslTypes.js";
2
+ import { InferPatch } from "../shared/repr.js";
3
+
4
+ //#region src/data/partialIO.d.ts
5
+
6
+ declare function patchArrayBuffer<T extends BaseData>(buffer: ArrayBuffer, schema: T, data: InferPatch<T>): void;
7
+ //#endregion
8
+ export { patchArrayBuffer };
package/data/partialIO.js CHANGED
@@ -110,6 +110,11 @@ function getPatchInstructions(schema, data, targetBuffer) {
110
110
  });
111
111
  return instructions;
112
112
  }
113
+ function patchArrayBuffer(buffer, schema, data) {
114
+ const instructions = getPatchInstructions(schema, data, buffer);
115
+ const mappedView = new Uint8Array(buffer);
116
+ for (const { data: data$1, gpuOffset } of instructions) mappedView.set(data$1, gpuOffset);
117
+ }
113
118
 
114
119
  //#endregion
115
- export { convertPartialToPatch, getPatchInstructions };
120
+ export { convertPartialToPatch, getPatchInstructions, patchArrayBuffer };
package/data/snippet.d.ts CHANGED
@@ -12,14 +12,9 @@ interface Snippet {
12
12
  readonly dataType: BaseData | UnknownData;
13
13
  readonly origin: Origin;
14
14
  }
15
- interface ResolvedSnippet {
15
+ interface ResolvedSnippet extends Snippet {
16
16
  readonly value: string;
17
- /**
18
- * The type that `value` is assignable to (not necessary exactly inferred as).
19
- * E.g. `1.1` is assignable to `f32`, but `1.1` itself is an abstract float
20
- */
21
17
  readonly dataType: BaseData;
22
- readonly origin: Origin;
23
18
  }
24
19
  type MapValueToSnippet<T> = { [K in keyof T]: Snippet };
25
20
  //#endregion
@@ -72,6 +72,7 @@ interface matInfixNotation<T extends matBase> {
72
72
  */
73
73
  interface AbstractInt extends BaseData {
74
74
  readonly type: 'abstractInt';
75
+ readonly concretized: I32;
75
76
  readonly [$repr]: number;
76
77
  readonly [$invalidSchemaReason]: 'Abstract numerics are not host-shareable';
77
78
  }
@@ -80,6 +81,7 @@ interface AbstractInt extends BaseData {
80
81
  */
81
82
  interface AbstractFloat extends BaseData {
82
83
  readonly type: 'abstractFloat';
84
+ readonly concretized: F32;
83
85
  readonly [$repr]: number;
84
86
  readonly [$invalidSchemaReason]: 'Abstract numerics are not host-shareable';
85
87
  }
package/index.d.ts CHANGED
@@ -38,6 +38,8 @@ import { index_d_exports as index_d_exports$2 } from "./std/index.js";
38
38
  import { index_d_exports } from "./common/index.js";
39
39
  import { MissingBindGroupsError, MissingLinksError, MissingSlotValueError, MissingVertexBuffersError, NotUniformError, ResolutionError } from "./errors.js";
40
40
  import { WgslGenerator } from "./tgsl/wgslGenerator.js";
41
+ import { readFromArrayBuffer, writeToArrayBuffer } from "./data/dataIO.js";
42
+ import { patchArrayBuffer } from "./data/partialIO.js";
41
43
  import { TgpuRenderPipelineDescriptor } from "./indexNamedExports.js";
42
44
 
43
45
  //#region src/index.d.ts
@@ -68,4 +70,4 @@ declare const tgpu: {
68
70
  '~unstable': typeof tgpuUnstable_d_exports;
69
71
  };
70
72
  //#endregion
71
- export { AutoFragmentIn, AutoFragmentOut, _AutoVertexIn as AutoVertexIn, AutoVertexOut, BindLayoutEntry, ColorAttachment, Configurable, Eventual, ExtractBindGroupInputFromLayout, INTERNAL_GlobalExt, IndexFlag, InitFromDeviceOptions, InitOptions, LayoutEntryToInput, MissingBindGroupsError, MissingLinksError, MissingSlotValueError, MissingVertexBuffersError, Namespace, NotUniformError, RawCodeSnippetOrigin, RenderFlag, ResolutionError, SampledFlag, ShaderGenerator, Storage, StorageFlag, TextureProps, TgpuAccessor, TgpuBindGroup, TgpuBindGroupLayout, TgpuBuffer, TgpuBufferMutable, TgpuBufferReadonly, TgpuBufferUniform, TgpuComparisonSampler, TgpuComptime, TgpuComputeFn, TgpuComputeFnShell, TgpuComputePipeline, TgpuConst, TgpuDeclare, TgpuFixedComparisonSampler, TgpuFixedSampler, TgpuFn, TgpuFnShell, TgpuFragmentFn, TgpuFragmentFnShell, TgpuGenericFn, TgpuGuardedComputePipeline, TgpuLayoutComparisonSampler, TgpuLayoutEntry, TgpuLayoutExternalTexture, TgpuLayoutSampler, TgpuLayoutStorage, TgpuLayoutTexture, TgpuLayoutUniform, TgpuLazy, TgpuMutable, TgpuMutableAccessor, TgpuPrimitiveState, TgpuQuerySet, TgpuRawCodeSnippet, TgpuReadonly, TgpuRenderPipeline, TgpuRenderPipelineDescriptor, TgpuRoot, TgpuSampler, TgpuSlot, TgpuTexture, TgpuTextureView, TgpuUniform, TgpuVar, TgpuVertexFn, TgpuVertexFnShell, TgpuVertexLayout, Uniform, UniformFlag, ValidUsagesFor, ValidateBufferSchema, ValidateStorageSchema, ValidateUniformSchema, VariableScope, Vertex, VertexFlag, WgslGenerator, WithBinding, WithCompute, WithFragment, WithVertex, Withable, index_d_exports as common, index_d_exports$1 as d, tgpu as default, tgpu, isAccessor, isBuffer, isBufferShorthand, isComparisonSampler, isLazy, isMutableAccessor, isSampler, isSlot, isTexture, isTgpuComputeFn, isTgpuFn, isTgpuFragmentFn, isTgpuVertexFn, isUsableAsRender, isUsableAsSampled, isUsableAsStorage, isUsableAsUniform, isUsableAsVertex, isVariable, index_d_exports$2 as std };
73
+ export { AutoFragmentIn, AutoFragmentOut, _AutoVertexIn as AutoVertexIn, AutoVertexOut, BindLayoutEntry, ColorAttachment, Configurable, Eventual, ExtractBindGroupInputFromLayout, INTERNAL_GlobalExt, IndexFlag, InitFromDeviceOptions, InitOptions, LayoutEntryToInput, MissingBindGroupsError, MissingLinksError, MissingSlotValueError, MissingVertexBuffersError, Namespace, NotUniformError, RawCodeSnippetOrigin, RenderFlag, ResolutionError, SampledFlag, ShaderGenerator, Storage, StorageFlag, TextureProps, TgpuAccessor, TgpuBindGroup, TgpuBindGroupLayout, TgpuBuffer, TgpuBufferMutable, TgpuBufferReadonly, TgpuBufferUniform, TgpuComparisonSampler, TgpuComptime, TgpuComputeFn, TgpuComputeFnShell, TgpuComputePipeline, TgpuConst, TgpuDeclare, TgpuFixedComparisonSampler, TgpuFixedSampler, TgpuFn, TgpuFnShell, TgpuFragmentFn, TgpuFragmentFnShell, TgpuGenericFn, TgpuGuardedComputePipeline, TgpuLayoutComparisonSampler, TgpuLayoutEntry, TgpuLayoutExternalTexture, TgpuLayoutSampler, TgpuLayoutStorage, TgpuLayoutTexture, TgpuLayoutUniform, TgpuLazy, TgpuMutable, TgpuMutableAccessor, TgpuPrimitiveState, TgpuQuerySet, TgpuRawCodeSnippet, TgpuReadonly, TgpuRenderPipeline, TgpuRenderPipelineDescriptor, TgpuRoot, TgpuSampler, TgpuSlot, TgpuTexture, TgpuTextureView, TgpuUniform, TgpuVar, TgpuVertexFn, TgpuVertexFnShell, TgpuVertexLayout, Uniform, UniformFlag, ValidUsagesFor, ValidateBufferSchema, ValidateStorageSchema, ValidateUniformSchema, VariableScope, Vertex, VertexFlag, WgslGenerator, WithBinding, WithCompute, WithFragment, WithVertex, Withable, index_d_exports as common, index_d_exports$1 as d, tgpu as default, tgpu, isAccessor, isBuffer, isBufferShorthand, isComparisonSampler, isLazy, isMutableAccessor, isSampler, isSlot, isTexture, isTgpuComputeFn, isTgpuFn, isTgpuFragmentFn, isTgpuVertexFn, isUsableAsRender, isUsableAsSampled, isUsableAsStorage, isUsableAsUniform, isUsableAsVertex, isVariable, patchArrayBuffer, readFromArrayBuffer, index_d_exports$2 as std, writeToArrayBuffer };
package/index.js CHANGED
@@ -2,6 +2,8 @@ import { MissingBindGroupsError, MissingLinksError, MissingSlotValueError, Missi
2
2
  import { isAccessor, isLazy, isMutableAccessor, isSlot } from "./core/slot/slotTypes.js";
3
3
  import { shaderGenerator_members_exports } from "./tgsl/shaderGenerator_members.js";
4
4
  import { isTgpuFn } from "./core/function/tgpuFn.js";
5
+ import { readFromArrayBuffer, writeToArrayBuffer } from "./data/dataIO.js";
6
+ import { patchArrayBuffer } from "./data/partialIO.js";
5
7
  import { isUsableAsStorage } from "./extension.js";
6
8
  import { isUsableAsUniform } from "./core/buffer/bufferUsage.js";
7
9
  import { isBuffer, isUsableAsVertex } from "./core/buffer/buffer.js";
@@ -23,4 +25,4 @@ import { common_exports } from "./common/index.js";
23
25
  var src_default = tgpu_exports;
24
26
 
25
27
  //#endregion
26
- export { MissingBindGroupsError, MissingLinksError, MissingSlotValueError, MissingVertexBuffersError, NotUniformError, ResolutionError, shaderGenerator_members_exports as ShaderGenerator, WgslGenerator, common_exports as common, data_exports as d, src_default as default, isAccessor, isBuffer, isBufferShorthand, isComparisonSampler, isLazy, isMutableAccessor, isSampler, isSlot, isTexture, isTgpuComputeFn, isTgpuFn, isTgpuFragmentFn, isTgpuVertexFn, isUsableAsRender, isUsableAsSampled, isUsableAsStorage, isUsableAsUniform, isUsableAsVertex, isVariable, std_exports as std, tgpu_exports as tgpu };
28
+ export { MissingBindGroupsError, MissingLinksError, MissingSlotValueError, MissingVertexBuffersError, NotUniformError, ResolutionError, shaderGenerator_members_exports as ShaderGenerator, WgslGenerator, common_exports as common, data_exports as d, src_default as default, isAccessor, isBuffer, isBufferShorthand, isComparisonSampler, isLazy, isMutableAccessor, isSampler, isSlot, isTexture, isTgpuComputeFn, isTgpuFn, isTgpuFragmentFn, isTgpuVertexFn, isUsableAsRender, isUsableAsSampled, isUsableAsStorage, isUsableAsUniform, isUsableAsVertex, isVariable, patchArrayBuffer, readFromArrayBuffer, std_exports as std, tgpu_exports as tgpu, writeToArrayBuffer };
@@ -32,6 +32,8 @@ import { index_d_exports as index_d_exports$1 } from "./std/index.js";
32
32
  import { index_d_exports as index_d_exports$2 } from "./common/index.js";
33
33
  import { MissingBindGroupsError, MissingLinksError, MissingSlotValueError, MissingVertexBuffersError, NotUniformError, ResolutionError } from "./errors.js";
34
34
  import { WgslGenerator } from "./tgsl/wgslGenerator.js";
35
+ import { readFromArrayBuffer, writeToArrayBuffer } from "./data/dataIO.js";
36
+ import { patchArrayBuffer } from "./data/partialIO.js";
35
37
 
36
38
  //#region src/indexNamedExports.d.ts
37
39
 
package/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.11.2";
2
+ var version = "0.11.4";
3
3
 
4
4
  //#endregion
5
5
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typegpu",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "description": "A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.",
5
5
  "keywords": [
6
6
  "compute",
package/resolutionCtx.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { $internal, $providing, $resolve, isMarkedInternal } from "./shared/symbols.js";
2
2
  import { getName, hasTinyestMetadata, setName } from "./shared/meta.js";
3
3
  import { Void, isPtr, isWgslArray, isWgslStruct } from "./data/wgslTypes.js";
4
- import { UnknownData, isData, undecorate } from "./data/dataTypes.js";
4
+ import { UnknownData, isData } from "./data/dataTypes.js";
5
5
  import { snip } from "./data/snippet.js";
6
6
  import { MissingSlotValueError, ResolutionError, WgslTypeError, invariant } from "./errors.js";
7
7
  import { isLazy, isProviding, isSlot } from "./core/slot/slotTypes.js";
@@ -12,7 +12,6 @@ import { safeStringify } from "./shared/stringify.js";
12
12
  import { getBestConversion } from "./tgsl/conversion.js";
13
13
  import { bool } from "./data/numeric.js";
14
14
  import { coerceToSnippet, concretize, numericLiteralToSnippet } from "./tgsl/generationHelpers.js";
15
- import { getAttributesString } from "./data/attributes.js";
16
15
  import { createIoSchema } from "./core/function/ioSchema.js";
17
16
  import { AutoStruct } from "./data/autoStruct.js";
18
17
  import { EntryInputRouter } from "./core/function/entryInputRouter.js";
@@ -64,12 +63,11 @@ var ItemStateStackImpl = class {
64
63
  bindingMap: new WeakMap(pairs)
65
64
  });
66
65
  }
67
- pushFunctionScope(functionType, args, argAliases, returnType, externalMap) {
66
+ pushFunctionScope(functionType, argAccess, returnType, externalMap) {
68
67
  const scope = {
69
68
  type: "functionScope",
70
69
  functionType,
71
- args,
72
- argAliases,
70
+ argAccess,
73
71
  returnType,
74
72
  externalMap,
75
73
  reportedReturnTypes: /* @__PURE__ */ new Set()
@@ -106,9 +104,8 @@ var ItemStateStackImpl = class {
106
104
  for (let i = this._stack.length - 1; i >= 0; --i) {
107
105
  const layer = this._stack[i];
108
106
  if (layer?.type === "functionScope") {
109
- const arg = layer.args.find((a) => a.value === id);
110
- if (arg !== void 0) return arg;
111
- if (layer.argAliases[id]) return layer.argAliases[id];
107
+ const access = layer.argAccess[id];
108
+ if (access) return access();
112
109
  const external = layer.externalMap[id];
113
110
  if (external !== void 0 && external !== null) return coerceToSnippet(external);
114
111
  return;
@@ -189,6 +186,27 @@ var IndentController = class {
189
186
  }
190
187
  }
191
188
  };
189
+ function createArgument(name, type, origin = "argument") {
190
+ let used = false;
191
+ return {
192
+ name,
193
+ access: () => {
194
+ used = true;
195
+ return snip(name, type, origin);
196
+ },
197
+ decoratedType: type,
198
+ get used() {
199
+ return used;
200
+ }
201
+ };
202
+ }
203
+ function createArgumentPropAccess(argAccess, prop) {
204
+ return () => {
205
+ const argSnippet = argAccess();
206
+ if (!argSnippet) return;
207
+ return accessProp(argSnippet, prop);
208
+ };
209
+ }
192
210
  var ResolutionCtxImpl = class {
193
211
  #namespaceInternal;
194
212
  _indentController = new IndentController();
@@ -287,107 +305,93 @@ var ResolutionCtxImpl = class {
287
305
  try {
288
306
  this.#namespaceInternal.nameRegistry.pushFunctionScope();
289
307
  const args = [];
290
- const argAliases = [];
291
- const pendingHeaderEntries = [];
308
+ const argAccess = {};
292
309
  if (options.entryInput) {
293
310
  const { dataSchema, positionalArgs } = options.entryInput;
294
311
  const firstParam = options.params[0];
295
- const structArgName = this.makeNameValid("_arg_0");
296
- const structArg = dataSchema ? snip(structArgName, dataSchema, "argument") : void 0;
297
- if (structArg) {
298
- args.push(structArg);
299
- pendingHeaderEntries.push({
300
- argName: structArgName,
301
- header: `${structArgName}: ${this.resolve(dataSchema).value}`
302
- });
303
- }
312
+ const structArg = dataSchema ? createArgument(this.makeNameValid("_arg_0"), dataSchema) : void 0;
313
+ if (structArg) args.push(structArg);
304
314
  if (firstParam?.type === FuncParameterType.destructuredObject) for (const { name, alias } of firstParam.props) {
305
315
  const argInfo = positionalArgs.find((a) => a.schemaKey === name);
306
316
  if (argInfo) {
307
- const argName = this.makeNameValid(alias);
308
- const argSnippet = snip(argName, argInfo.type, "argument");
309
- args.push(argSnippet);
310
- argAliases.push([alias, argSnippet]);
311
- pendingHeaderEntries.push({
312
- argName,
313
- header: `${getAttributesString(argInfo.type)}${argName}: ${this.resolve(undecorate(argInfo.type)).value}`
314
- });
315
- } else if (structArg) {
316
- const propSnippet = accessProp(structArg, name);
317
- if (propSnippet) argAliases.push([alias, propSnippet]);
318
- }
317
+ const arg = createArgument(this.makeNameValid(alias), argInfo.type);
318
+ args.push(arg);
319
+ argAccess[alias] = arg.access;
320
+ } else if (structArg) argAccess[alias] = createArgumentPropAccess(structArg.access, name);
319
321
  }
320
322
  else if (firstParam?.type === FuncParameterType.identifier) {
321
323
  const proxyEntries = [];
322
324
  for (const a of positionalArgs) {
323
- const argName = this.makeNameValid(`_arg_${a.schemaKey}`);
324
- const s = snip(argName, a.type, "argument");
325
- args.push(s);
325
+ const arg = createArgument(this.makeNameValid(`_arg_${a.schemaKey}`), a.type);
326
+ args.push(arg);
326
327
  proxyEntries.push({
327
328
  schemaKey: a.schemaKey,
328
- argName,
329
- type: a.type
330
- });
331
- pendingHeaderEntries.push({
332
- argName,
333
- header: `${getAttributesString(a.type)}${argName}: ${this.resolve(undecorate(a.type)).value}`
329
+ arg: arg.access
334
330
  });
335
331
  }
336
- const router = new EntryInputRouter(structArgName, dataSchema, proxyEntries);
337
- argAliases.push([firstParam.name, snip(firstParam.name, router, "argument")]);
332
+ const router = new EntryInputRouter(structArg?.access, proxyEntries);
333
+ argAccess[firstParam.name] = () => snip("N/A", router, "argument");
338
334
  } else for (const a of positionalArgs) {
339
335
  const argName = this.makeNameValid(`_arg_${a.schemaKey}`);
340
- args.push(snip(argName, a.type, "argument"));
341
- pendingHeaderEntries.push({
342
- argName,
343
- header: `${getAttributesString(a.type)}${argName}: ${this.resolve(undecorate(a.type)).value}`
344
- });
336
+ const arg = createArgument(argName, a.type);
337
+ args.push(arg);
338
+ argAccess[argName] = arg.access;
345
339
  }
346
340
  } else for (const [i, argType] of options.argTypes.entries()) {
347
341
  const astParam = options.params[i];
348
342
  const origin = isPtr(argType) ? argType.addressSpace === "storage" ? argType.access === "read" ? "readonly" : "mutable" : argType.addressSpace : "argument";
349
343
  switch (astParam?.type) {
350
344
  case FuncParameterType.identifier: {
351
- const rawName = astParam.name;
352
- const snippet = snip(this.makeNameValid(rawName), argType, origin);
353
- args.push(snippet);
354
- if (snippet.value !== rawName) argAliases.push([rawName, snippet]);
345
+ const arg = createArgument(this.makeNameValid(astParam.name), argType, origin);
346
+ args.push(arg);
347
+ argAccess[astParam.name] = arg.access;
355
348
  break;
356
349
  }
357
350
  case FuncParameterType.destructuredObject: {
358
- const objSnippet = snip(`_arg_${i}`, argType, origin);
359
- args.push(objSnippet);
360
- argAliases.push(...astParam.props.map(({ name, alias }) => [alias, accessProp(objSnippet, name)]));
351
+ const objArg = createArgument(this.makeNameValid(`_arg_${i}`), argType, origin);
352
+ args.push(objArg);
353
+ for (const { name, alias } of astParam.props) argAccess[alias] = createArgumentPropAccess(objArg.access, name);
361
354
  break;
362
355
  }
363
- case void 0: if (!(argType instanceof AutoStruct)) args.push(snip(`_arg_${i}`, argType, origin));
356
+ case void 0: if (!(argType instanceof AutoStruct)) args.push({
357
+ name: this.makeNameValid(`_arg_${i}`),
358
+ access: () => {
359
+ throw new Error(`Unreachable: Accessing an argument that wasn't named in the function signature`);
360
+ },
361
+ decoratedType: argType,
362
+ used: false
363
+ });
364
364
  }
365
365
  }
366
- const scope = this._itemStateStack.pushFunctionScope(options.functionType, args, Object.fromEntries(argAliases), options.returnType, options.externalMap);
366
+ const scope = this._itemStateStack.pushFunctionScope(options.functionType, argAccess, options.returnType, options.externalMap);
367
367
  fnScopePushed = true;
368
- const body = this.gen.functionDefinition(options.body);
369
- let returnType = options.returnType;
370
- if (returnType instanceof AutoStruct) if (isWgslStruct(scope.reportedReturnTypes.values().next().value)) returnType = returnType.completeStruct;
371
- else returnType = void 0;
372
- if (!returnType) {
373
- const returnTypes = [...scope.reportedReturnTypes];
374
- if (returnTypes.length === 0) returnType = Void;
375
- else {
376
- const conversion = getBestConversion(returnTypes);
377
- if (conversion && !conversion.hasImplicitConversions) returnType = conversion.targetType;
368
+ let returnType;
369
+ const code = this.gen.functionDefinition({
370
+ functionType: options.functionType,
371
+ args,
372
+ body: options.body,
373
+ determineReturnType: () => {
374
+ if (returnType) return returnType;
375
+ returnType = options.returnType;
376
+ if (returnType instanceof AutoStruct) if (isWgslStruct(scope.reportedReturnTypes.values().next().value)) returnType = returnType.completeStruct;
377
+ else returnType = void 0;
378
+ if (!returnType) {
379
+ const returnTypes = [...scope.reportedReturnTypes];
380
+ if (returnTypes.length === 0) returnType = Void;
381
+ else {
382
+ const conversion = getBestConversion(returnTypes);
383
+ if (conversion && !conversion.hasImplicitConversions) returnType = conversion.targetType;
384
+ }
385
+ if (!returnType) throw new Error(`Expected function to have a single return type, got [${returnTypes.join(", ")}]. Cast explicitly to the desired type.`);
386
+ returnType = concretize(returnType);
387
+ if (options.functionType === "vertex" || options.functionType === "fragment") returnType = createIoSchema(returnType);
388
+ }
389
+ return returnType;
378
390
  }
379
- if (!returnType) throw new Error(`Expected function to have a single return type, got [${returnTypes.join(", ")}]. Cast explicitly to the desired type.`);
380
- returnType = concretize(returnType);
381
- if (options.functionType === "vertex" || options.functionType === "fragment") returnType = createIoSchema(returnType);
382
- }
383
- if (options.entryInput) return {
384
- head: `(${pendingHeaderEntries.filter(({ argName }) => isArgUsedInBody(argName, body)).map(({ header }) => header).join(", ")}) ${returnType.type !== "void" ? `-> ${getAttributesString(returnType)}${this.resolve(returnType).value} ` : ""}`,
385
- body,
386
- returnType
387
- };
391
+ });
392
+ if (!returnType) throw new Error(`Failed to determine return type`);
388
393
  return {
389
- head: resolveFunctionHeader(this, args, returnType),
390
- body,
394
+ code,
391
395
  returnType
392
396
  };
393
397
  } finally {
@@ -556,9 +560,8 @@ var ResolutionCtxImpl = class {
556
560
  if (schema.elementCount !== item.length) throw new WgslTypeError(`Cannot create value of type '${schema}' from an array of length: ${item.length}`);
557
561
  return snip(stitch`array<${this.resolve(schema.elementType)}, ${schema.elementCount}>(${item.map((element) => snip(element, schema.elementType, "runtime"))})`, schema, "runtime");
558
562
  }
559
- if (Array.isArray(item)) return snip(stitch`array(${item.map((element) => this.resolve(element))})`, UnknownData, "runtime");
560
563
  if (schema && isWgslStruct(schema)) return snip(stitch`${this.resolve(schema)}(${Object.entries(schema.propTypes).map(([key, propType]) => snip(item[key], propType, "runtime"))})`, schema, "runtime");
561
- throw new WgslTypeError(`Value ${item} (as json: ${safeStringify(item)}) is not resolvable${schema ? ` to type ${safeStringify(schema)}` : ""}`);
564
+ throw new WgslTypeError(`Value ${safeStringify(item)} is not resolvable${schema ? ` to type ${safeStringify(schema)}` : ""}`);
562
565
  }
563
566
  resolveSnippet(snippet) {
564
567
  return snip(this.resolve(snippet.value, snippet.dataType).value, snippet.dataType, snippet.origin);
@@ -602,13 +605,6 @@ function resolve(item, options) {
602
605
  logResources: ctx.logResources
603
606
  };
604
607
  }
605
- function isArgUsedInBody(argName, body) {
606
- return (/* @__PURE__ */ new RegExp(`\\b${argName}\\b`)).test(body);
607
- }
608
- function resolveFunctionHeader(ctx, args, returnType) {
609
- const argList = args.map((arg) => `${arg.value}: ${ctx.resolve(arg.dataType).value}`).join(", ");
610
- return returnType.type !== "void" ? `(${argList}) -> ${getAttributesString(returnType)}${ctx.resolve(returnType).value} ` : `(${argList}) `;
611
- }
612
608
 
613
609
  //#endregion
614
610
  export { ResolutionCtxImpl, resolve };
@@ -2,6 +2,7 @@ import { isMatInstance, isVecInstance } from "../data/wgslTypes.js";
2
2
 
3
3
  //#region src/shared/stringify.ts
4
4
  function safeStringify(item) {
5
+ if (Array.isArray(item)) return `[${item.map(safeStringify).join(", ")}]`;
5
6
  const asString = String(item);
6
7
  if (asString !== "[object Object]") return asString;
7
8
  try {
@@ -0,0 +1,90 @@
1
+ import * as tinyest from "tinyest";
2
+
3
+ //#region src/shared/tseynit.ts
4
+ const { NodeTypeCatalog: NODE } = tinyest;
5
+ function stringifyNode(node) {
6
+ if (isExpression(node)) return stringifyExpression(node, "");
7
+ return stringifyStatement(node, "");
8
+ }
9
+ function stringifyStatement(node, ident) {
10
+ if (isExpression(node)) return `${ident}${stringifyExpression(node, ident)};`;
11
+ if (node[0] === NODE.block) return `{\n${node[1].map((n) => stringifyStatement(n, ident + " ")).join("\n")}\n${ident}}`;
12
+ if (node[0] === NODE.return) return `${ident}return${node[1] === void 0 ? "" : ` ${stringifyExpression(node[1], "")}`};`;
13
+ if (node[0] === NODE.if) {
14
+ const base = `${ident}if (${stringifyExpression(node[1], ident)}) ${stringifyStatement(node[2], ident)}`;
15
+ if (node[3] !== void 0) return `${base} else ${stringifyStatement(node[3], ident)}`;
16
+ return base;
17
+ }
18
+ if (node[0] === NODE.let) {
19
+ if (node[2] !== void 0) return `${ident}let ${node[1]} = ${stringifyExpression(node[2], ident)};`;
20
+ return `${ident}let ${node[1]};`;
21
+ }
22
+ if (node[0] === NODE.const) {
23
+ if (node[2] !== void 0) return `${ident}const ${node[1]} = ${stringifyExpression(node[2], ident)};`;
24
+ return `${ident}const ${node[1]};`;
25
+ }
26
+ if (node[0] === NODE.for) {
27
+ const init = node[1] ? stringifyStatement(node[1], "") : ";";
28
+ const cond = node[2] ? stringifyExpression(node[2], ident) : "";
29
+ const update = node[3] ? stringifyStatement(node[3], "") : "";
30
+ const body = stringifyStatement(node[4], ident);
31
+ return `${ident}for (${init} ${cond}; ${update.slice(0, -1)}) ${body}`;
32
+ }
33
+ if (node[0] === NODE.while) return `${ident}while (${stringifyExpression(node[1], ident)}) ${stringifyStatement(node[2], ident)}`;
34
+ if (node[0] === NODE.continue) return `${ident}continue;`;
35
+ if (node[0] === NODE.break) return `${ident}break;`;
36
+ if (node[0] === NODE.forOf) return `${ident}for (${node[1][0] === NODE.const ? "const" : "let"} ${node[1][1]} of ${stringifyExpression(node[2], ident)}) ${stringifyStatement(node[3], ident)}`;
37
+ assertExhaustive(node);
38
+ }
39
+ function stringifyExpression(node, ident) {
40
+ if (typeof node === "string") return node;
41
+ if (typeof node === "boolean") return `${node}`;
42
+ if (node[0] === NODE.numericLiteral) return node[1];
43
+ if (node[0] === NODE.stringLiteral) return JSON.stringify(node[1]);
44
+ if (node[0] === NODE.arrayExpr) return `[${node[1].map((n) => stringifyExpression(n, ident)).join(", ")}]`;
45
+ if (node[0] === NODE.binaryExpr) return `${wrapIfComplex(node[1], ident)} ${node[2]} ${wrapIfComplex(node[3], ident)}`;
46
+ if (node[0] === NODE.assignmentExpr) return `${stringifyExpression(node[1], ident)} ${node[2]} ${stringifyExpression(node[3], ident)}`;
47
+ if (node[0] === NODE.logicalExpr) return `${wrapIfComplex(node[1], ident)} ${node[2]} ${wrapIfComplex(node[3], ident)}`;
48
+ if (node[0] === NODE.unaryExpr) {
49
+ const sep = node[1].length > 1 ? " " : "";
50
+ return `${node[1]}${sep}${wrapIfComplex(node[2], ident)}`;
51
+ }
52
+ if (node[0] === NODE.call) return `${wrapIfComplex(node[1], ident)}(${node[2].map((n) => stringifyExpression(n, ident)).join(", ")})`;
53
+ if (node[0] === NODE.memberAccess) {
54
+ if (Array.isArray(node[1]) && node[1][0] === NODE.numericLiteral) return `(${stringifyExpression(node[1], ident)}).${node[2]}`;
55
+ return `${wrapIfComplex(node[1], ident)}.${node[2]}`;
56
+ }
57
+ if (node[0] === NODE.indexAccess) return `${wrapIfComplex(node[1], ident)}[${stringifyExpression(node[2], ident)}]`;
58
+ if (node[0] === NODE.preUpdate) return `${node[1]}${wrapIfComplex(node[2], ident)}`;
59
+ if (node[0] === NODE.postUpdate) return `${wrapIfComplex(node[2], ident)}${node[1]}`;
60
+ if (node[0] === NODE.objectExpr) return `{ ${Object.entries(node[1]).map(([key, val]) => `${key}: ${stringifyExpression(val, ident)}`).join(", ")} }`;
61
+ if (node[0] === NODE.conditionalExpr) return `${wrapIfComplex(node[1], ident)} ? ${wrapIfComplex(node[2], ident)} : ${wrapIfComplex(node[3], ident)}`;
62
+ assertExhaustive(node);
63
+ }
64
+ function assertExhaustive(value) {
65
+ throw new Error(`'${JSON.stringify(value)}' was not handled by the stringify function.`);
66
+ }
67
+ function isExpression(node) {
68
+ if (typeof node === "string" || typeof node === "boolean" || node[0] === NODE.numericLiteral || node[0] === NODE.stringLiteral || node[0] === NODE.arrayExpr || node[0] === NODE.binaryExpr || node[0] === NODE.assignmentExpr || node[0] === NODE.logicalExpr || node[0] === NODE.unaryExpr || node[0] === NODE.call || node[0] === NODE.memberAccess || node[0] === NODE.indexAccess || node[0] === NODE.preUpdate || node[0] === NODE.postUpdate || node[0] === NODE.objectExpr || node[0] === NODE.conditionalExpr) return true;
69
+ return false;
70
+ }
71
+ const SIMPLE_NODES = [
72
+ NODE.memberAccess,
73
+ NODE.indexAccess,
74
+ NODE.call,
75
+ NODE.arrayExpr,
76
+ NODE.stringLiteral,
77
+ NODE.numericLiteral
78
+ ];
79
+ /**
80
+ * Stringifies expression, and wraps it in parentheses if they cannot be trivially omitted
81
+ */
82
+ function wrapIfComplex(node, ident) {
83
+ const s = stringifyExpression(node, ident);
84
+ if (typeof node === "string" || typeof node === "boolean") return s;
85
+ if (SIMPLE_NODES.includes(node[0])) return s;
86
+ return `(${s})`;
87
+ }
88
+
89
+ //#endregion
90
+ export { stringifyNode };
@@ -1,10 +1,10 @@
1
1
  import { isNaturallyEphemeral, isPtr, isVec, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
2
- import { MatrixColumnsAccess, UnknownData, isDisarray } from "../data/dataTypes.js";
2
+ import { MatrixColumnsAccess, isDisarray } from "../data/dataTypes.js";
3
3
  import { isEphemeralSnippet, snip } from "../data/snippet.js";
4
4
  import { isKnownAtComptime } from "../types.js";
5
5
  import { stitch } from "../core/resolve/stitch.js";
6
6
  import { derefSnippet } from "../data/ref.js";
7
- import { coerceToSnippet } from "./generationHelpers.js";
7
+ import { ArrayExpression, coerceToSnippet } from "./generationHelpers.js";
8
8
  import { vec2f, vec3f, vec4f } from "../data/vector.js";
9
9
  import { accessProp } from "./accessProp.js";
10
10
 
@@ -28,6 +28,7 @@ function accessIndex(target, indexArg) {
28
28
  else if (!isTargetEphemeral && !isElementNatEph) origin = target.origin;
29
29
  else if (isIndexConstant && target.origin === "constant") origin = "constant";
30
30
  else origin = "runtime";
31
+ if (target.value instanceof ArrayExpression && isKnownAtComptime(index)) return target.value.elements[index.value];
31
32
  return snip(isKnownAtComptime(target) && isKnownAtComptime(index) ? target.value[index.value] : stitch`${target}[${index}]`, elementType, origin);
32
33
  }
33
34
  if (isVec(target.dataType)) return snip(isKnownAtComptime(target) && isKnownAtComptime(index) ? target.value[index.value] : stitch`${target}[${index}]`, target.dataType.primitive, target.origin === "constant" || target.origin === "constant-tgpu-const-ref" ? "constant" : "runtime");
@@ -37,7 +38,7 @@ function accessIndex(target, indexArg) {
37
38
  return snip(stitch`${target.value.matrix}[${index}]`, propType, target.origin);
38
39
  }
39
40
  if (target.dataType.type in indexableTypeToResult) throw new Error("The only way of accessing matrix elements in TypeGPU functions is through the 'columns' property.");
40
- if (isKnownAtComptime(target) && isKnownAtComptime(index) || target.dataType === UnknownData) return coerceToSnippet(target.value[index.value]);
41
+ if (isKnownAtComptime(target) && isKnownAtComptime(index)) return coerceToSnippet(target.value[index.value]);
41
42
  if (isWgslStruct(target.dataType) && isKnownAtComptime(index) && typeof index.value === "string") return accessProp(target, index.value);
42
43
  }
43
44
 
@@ -1,7 +1,7 @@
1
1
  import { $gpuCallable } from "../shared/symbols.js";
2
2
  import { isMat, isNaturallyEphemeral, isPtr, isVec, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
3
3
  import { InfixDispatch, MatrixColumnsAccess, UnknownData, isUnstruct, undecorate } from "../data/dataTypes.js";
4
- import { isEphemeralSnippet, snip } from "../data/snippet.js";
4
+ import { isEphemeralSnippet, isSnippet, snip } from "../data/snippet.js";
5
5
  import { isKnownAtComptime } from "../types.js";
6
6
  import { stitch } from "../core/resolve/stitch.js";
7
7
  import { derefSnippet } from "../data/ref.js";
@@ -92,7 +92,12 @@ function accessProp(target, propName) {
92
92
  if (!result) return;
93
93
  return snip(stitch`${target}.${result.prop}`, result.type, "argument");
94
94
  }
95
- if (target.dataType instanceof EntryInputRouter) return target.dataType.accessProp(propName);
95
+ if (target.dataType instanceof EntryInputRouter) {
96
+ const result = target.dataType.accessProp(propName);
97
+ if (isSnippet(result)) return result;
98
+ if (result) return accessProp(result.target, result.prop);
99
+ return;
100
+ }
96
101
  if (isPtr(target.dataType)) {
97
102
  const derefed = derefSnippet(target);
98
103
  if (propName === "$") return derefed;
@@ -103,7 +103,7 @@ function logDataFromGPU(resources) {
103
103
  const { argTypes, op } = logIdToMeta.get(id);
104
104
  const results = deserializeAndStringify(new Uint32Array(serializedData), argTypes);
105
105
  if (results.length === 0) results.push("");
106
- console[op](`%c${options.messagePrefix}%c ${results[0]}`, "background: #936ff5; color: white;", "color: inherit; background: none", ...results.slice(1));
106
+ op.bind(console)(`%c${options.messagePrefix}%c ${results[0]}`, "background: #936ff5; color: white;", "color: inherit; background: none", ...results.slice(1));
107
107
  });
108
108
  });
109
109
  indexBuffer.read().then((totalCalls) => {