typegpu 0.12.5 → 0.12.7

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 (99) hide show
  1. package/common/index.d.ts +1 -0
  2. package/common/index.js +1 -0
  3. package/common/writeChannels.d.ts +31 -0
  4. package/common/writeChannels.js +32 -0
  5. package/common/writeSoA.js +16 -0
  6. package/core/buffer/buffer.js +47 -11
  7. package/core/commandEncoder/attachments.js +5 -2
  8. package/core/commandEncoder/computePass.d.ts +13 -0
  9. package/core/commandEncoder/computePass.js +5 -2
  10. package/core/commandEncoder/renderPass.d.ts +18 -4
  11. package/core/commandEncoder/renderPass.js +6 -2
  12. package/core/declare/tgpuDeclare.d.ts +11 -0
  13. package/core/declare/tgpuDeclare.js +1 -1
  14. package/core/function/autoIO.js +4 -2
  15. package/core/function/ioSchema.d.ts +7 -2
  16. package/core/function/ioSchema.js +41 -15
  17. package/core/function/tgpuFragmentFn.d.ts +1 -1
  18. package/core/function/tgpuFragmentFn.js +2 -1
  19. package/core/function/tgpuVertexFn.js +2 -1
  20. package/core/immediate/immediateVar.d.ts +52 -0
  21. package/core/immediate/immediateVar.js +149 -0
  22. package/core/pipeline/computePipeline.d.ts +17 -0
  23. package/core/pipeline/computePipeline.js +23 -9
  24. package/core/pipeline/connectAttachmentToShader.d.ts +2 -2
  25. package/core/pipeline/connectAttachmentToShader.js +16 -21
  26. package/core/pipeline/connectTargetsToShader.d.ts +5 -1
  27. package/core/pipeline/connectTargetsToShader.js +23 -18
  28. package/core/pipeline/drawState.d.ts +8 -4
  29. package/core/pipeline/drawState.js +41 -51
  30. package/core/pipeline/priors.js +1 -0
  31. package/core/pipeline/renderPipeline.d.ts +22 -4
  32. package/core/pipeline/renderPipeline.js +25 -13
  33. package/core/resolve/namespace.js +2 -2
  34. package/core/root/init.js +3 -0
  35. package/core/root/rootTypes.d.ts +17 -2
  36. package/core/slot/accessor.d.ts +1 -1
  37. package/core/slot/accessor.js +72 -5
  38. package/core/slot/slotTypes.d.ts +2 -1
  39. package/core/texture/texture.d.ts +20 -14
  40. package/core/texture/texture.js +78 -69
  41. package/core/texture/textureUtils.d.ts +3 -6
  42. package/core/texture/textureUtils.js +142 -165
  43. package/core/texture/textureWrite.d.ts +61 -0
  44. package/core/texture/textureWrite.js +59 -0
  45. package/data/autoStruct.d.ts +1 -1
  46. package/data/autoStruct.js +8 -4
  47. package/data/dataIO.d.ts +2 -2
  48. package/data/deepEqual.js +19 -9
  49. package/data/matrix.js +10 -3
  50. package/data/partialIO.d.ts +8 -1
  51. package/data/partialIO.js +36 -19
  52. package/data/ref.js +2 -2
  53. package/data/snippet.d.ts +11 -5
  54. package/data/snippet.js +9 -4
  55. package/data/wgslTypes.d.ts +4 -1
  56. package/errors.d.ts +7 -0
  57. package/errors.js +15 -0
  58. package/indexNamedExports.d.ts +5 -2
  59. package/indexNamedExports.js +2 -1
  60. package/internal.d.ts +2 -1
  61. package/internal.js +1 -1
  62. package/nameUtils.d.ts +7 -0
  63. package/nameUtils.js +60 -0
  64. package/package.json +2 -2
  65. package/resolutionCtx.d.ts +5 -0
  66. package/resolutionCtx.js +11 -0
  67. package/serial/restore.d.ts +3 -1
  68. package/serial/restore.js +2 -0
  69. package/shared/meta.js +1 -1
  70. package/shared/symbols.js +1 -1
  71. package/shared/tseynit.d.ts +1 -0
  72. package/shared/tseynit.js +80 -44
  73. package/std/atomic.d.ts +13 -0
  74. package/std/atomic.js +63 -29
  75. package/std/boolean.js +9 -10
  76. package/std/derivative.js +14 -9
  77. package/std/index.d.ts +4 -4
  78. package/std/index.js +5 -5
  79. package/std/numeric.d.ts +5 -1
  80. package/std/numeric.js +3 -3
  81. package/std/operators.js +9 -4
  82. package/std/packing.d.ts +61 -1
  83. package/std/packing.js +214 -25
  84. package/std/subgroup.d.ts +4 -0
  85. package/std/subgroup.js +34 -0
  86. package/std/texture.d.ts +15 -2
  87. package/std/texture.js +48 -0
  88. package/tgpuUnstable.d.ts +1 -0
  89. package/tgpuUnstable.js +1 -0
  90. package/tgsl/accessIndex.js +19 -23
  91. package/tgsl/conversion.d.ts +2 -0
  92. package/tgsl/conversion.js +3 -0
  93. package/tgsl/forOfUtils.js +6 -6
  94. package/tgsl/generationHelpers.js +2 -2
  95. package/tgsl/shaderGenerator.d.ts +1 -1
  96. package/tgsl/wgslGenerator.d.ts +5 -0
  97. package/tgsl/wgslGenerator.js +288 -58
  98. package/types.d.ts +9 -1
  99. package/types.js +5 -0
@@ -3,8 +3,9 @@ import type { IndexFlag, IndirectFlag, TgpuBuffer, VertexFlag } from '../buffer/
3
3
  import type { TgpuQuerySet } from '../querySet/querySet.ts';
4
4
  import { type Disarray, type UndecorateRecord } from '../../data/dataTypes.ts';
5
5
  import { type ResolvedSnippet } from '../../data/snippet.ts';
6
- import { type AnyVecInstance, type BaseData, type U16, type U32, type v4f, Void, type WgslArray, type WgslStruct } from '../../data/wgslTypes.ts';
6
+ import { type AnyVecInstance, type AnyWgslData, type BaseData, type U16, type U32, type v4f, Void, type WgslArray } from '../../data/wgslTypes.ts';
7
7
  import type { TgpuNamable } from '../../shared/meta.ts';
8
+ import type { InferInput } from '../../shared/repr.ts';
8
9
  import type { TgpuDeviceOwningSoul } from '../../shared/soul.ts';
9
10
  import { $getNameForward, $internal, $resolve, $soul } from '../../shared/symbols.ts';
10
11
  import type { AnyVertexAttribs } from '../../shared/vertexFormat.ts';
@@ -17,8 +18,10 @@ import type { TgpuVertexFn } from '../function/tgpuVertexFn.ts';
17
18
  import type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';
18
19
  import type { TgpuSlot } from '../slot/slotTypes.ts';
19
20
  import { type TgpuVertexLayout } from '../vertexLayout/vertexLayout.ts';
21
+ import { type ConnectedTarget } from './connectTargetsToShader.ts';
20
22
  import { type TgpuCommandEncoder } from '../commandEncoder/commandEncoder.ts';
21
23
  import type { ColorAttachment, DepthStencilAttachment } from '../commandEncoder/attachments.ts';
24
+ import { type ImmediateSnapshot, type TgpuImmediateVar } from '../immediate/immediateVar.ts';
22
25
  import { type TgpuRenderCommands } from '../commandEncoder/renderPass.ts';
23
26
  import { type Timeable, type TimestampWritesPriors } from './timeable.ts';
24
27
  import { type PrimitiveOffsetInfo } from '../../data/offsetUtils.ts';
@@ -32,8 +35,10 @@ export interface RenderPipelineInternals {
32
35
  export interface TgpuRenderPipelineSoul extends TgpuDeviceOwningSoul<'render-pipeline', GPURenderPipeline> {
33
36
  usedBindGroupLayouts?: TgpuBindGroupLayout[] | undefined;
34
37
  usedVertexLayouts?: TgpuVertexLayout[] | undefined;
35
- fragmentOut?: BaseData | undefined;
38
+ connectedTargets?: (ConnectedTarget | null)[] | undefined;
36
39
  bindGroups?: [TgpuBindGroupLayout, TgpuBindGroup | GPUBindGroup][] | undefined;
40
+ usedImmediate?: TgpuImmediateVar | undefined;
41
+ immediates?: [TgpuImmediateVar, ImmediateSnapshot][] | undefined;
37
42
  vertexBuffers?: [TgpuVertexLayout, (TgpuBuffer<BaseData> & VertexFlag) | GPUBuffer][] | undefined;
38
43
  indexBuffer?: {
39
44
  buffer: (TgpuBuffer<BaseData> & IndexFlag) | GPUBuffer;
@@ -76,6 +81,16 @@ export interface TgpuRenderPipeline<in Targets = never> extends TgpuNamable, Sel
76
81
  with<Entries extends Record<string, TgpuLayoutEntry | null>>(bindGroupLayout: TgpuBindGroupLayout<Entries>, bindGroup: TgpuBindGroup<Entries>): this;
77
82
  with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: GPUBindGroup): this;
78
83
  with(bindGroup: TgpuBindGroup): this;
84
+ /**
85
+ * Provides a value for the given immediate variable, applied on draw like
86
+ * the rest of the pipeline-held state and overridden by `pass.setImmediates`.
87
+ * The value is captured (copied) at call time; mutating it afterwards has
88
+ * no effect.
89
+ *
90
+ * Passing an `ArrayBuffer` or typed array skips serialization entirely; the bytes
91
+ * are copied verbatim and the caller guarantees they match the schema's layout.
92
+ */
93
+ with<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): this;
79
94
  /**
80
95
  * Directs subsequent draw calls into the given render pass or render bundle
81
96
  * encoder, letting multiple pipelines share one pass (and one submission).
@@ -199,6 +214,7 @@ type TgpuRenderPipelinePriors = {
199
214
  readonly pass?: TgpuRenderCommands | undefined;
200
215
  /** An encoder the pipeline records its own passes into, but does not submit */
201
216
  readonly encoder?: TgpuCommandEncoder | undefined;
217
+ readonly immediatesMap?: Map<TgpuImmediateVar, ImmediateSnapshot> | undefined;
202
218
  } & TimestampWritesPriors;
203
219
  type Memo = {
204
220
  pipeline: GPURenderPipeline;
@@ -206,7 +222,8 @@ type Memo = {
206
222
  catchall: [number, TgpuBindGroup] | undefined;
207
223
  logResources: LogResources | undefined;
208
224
  usedVertexLayouts: TgpuVertexLayout[];
209
- fragmentOut: BaseData | undefined;
225
+ connectedTargets: (ConnectedTarget | null)[] | undefined;
226
+ usedImmediate: TgpuImmediateVar | undefined;
210
227
  };
211
228
  declare class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
212
229
  #private;
@@ -223,6 +240,7 @@ declare class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
223
240
  with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: GPUBindGroup): this;
224
241
  with(bindGroup: TgpuBindGroup): this;
225
242
  with<TData extends WgslArray | Disarray>(vertexLayout: TgpuVertexLayout<TData>, buffer: GPUBuffer): this;
243
+ with<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): this;
226
244
  with(pass: TgpuRenderCommands): this;
227
245
  with(encoder: TgpuCommandEncoder): this;
228
246
  with(encoder: GPUCommandEncoder): this;
@@ -263,8 +281,8 @@ declare class RenderPipelineCore implements SelfResolvable {
263
281
  resolveAndCreateShaderModule(): {
264
282
  resolutionResult: import("../../resolutionCtx.ts").ResolutionResult;
265
283
  descriptor: GPURenderPipelineDescriptor;
284
+ connectedTargets: (ConnectedTarget | null)[];
266
285
  connectedAttribs: import("../vertexLayout/connectAttributesToShader.ts").ConnectAttributesToShaderResult;
267
- fragmentOut: WgslStruct<import("../function/ioSchema.ts").WithLocations<Record<string, BaseData>>>;
268
286
  };
269
287
  }
270
288
  /**
@@ -18,6 +18,7 @@ import { isVertexLayout } from "../vertexLayout/vertexLayout.js";
18
18
  import { connectAttachmentToShader } from "./connectAttachmentToShader.js";
19
19
  import { connectTargetsToShader } from "./connectTargetsToShader.js";
20
20
  import { INTERNAL_adoptCommandEncoder, INTERNAL_createCommandEncoder, } from "../commandEncoder/commandEncoder.js";
21
+ import { createImmediateSnapshot, isImmediateVar, validateImmediateUsage, } from "../immediate/immediateVar.js";
21
22
  import { INTERNAL_adoptRenderCommands, } from "../commandEncoder/renderPass.js";
22
23
  import { emitRenderDraw, finalizeOwnEncoder, requireIndexBuffer } from "./drawState.js";
23
24
  import { isGPUCommandEncoder, isGPURenderBundleEncoder, isGPURenderPassEncoder, isTgpuCommandEncoder, isTgpuRenderCommands, } from "./typeGuards.js";
@@ -41,10 +42,12 @@ export function INTERNAL_restoreRenderPipeline(soul, ctx) {
41
42
  catchall: undefined,
42
43
  logResources: undefined,
43
44
  usedVertexLayouts: soul.usedVertexLayouts ?? [],
44
- fragmentOut: soul.fragmentOut,
45
+ connectedTargets: soul.connectedTargets,
46
+ usedImmediate: soul.usedImmediate,
45
47
  });
46
48
  const pipeline = new TgpuRenderPipelineImpl(core, {
47
49
  bindGroupLayoutMap: new Map(soul.bindGroups),
50
+ immediatesMap: new Map(soul.immediates),
48
51
  vertexLayoutMap: new Map(soul.vertexBuffers),
49
52
  indexBuffer: soul.indexBuffer,
50
53
  stencilReference: soul.stencilReference,
@@ -74,8 +77,10 @@ class TgpuRenderPipelineImpl {
74
77
  soul.raw = memo.pipeline;
75
78
  soul.usedBindGroupLayouts = memo.usedBindGroupLayouts;
76
79
  soul.usedVertexLayouts = memo.usedVertexLayouts;
77
- soul.fragmentOut = memo.fragmentOut;
80
+ soul.connectedTargets = memo.connectedTargets;
78
81
  soul.bindGroups = collectBindGroupPairs(memo.usedBindGroupLayouts, memo.catchall, priors.bindGroupLayoutMap);
82
+ soul.usedImmediate = memo.usedImmediate;
83
+ soul.immediates = [...(priors.immediatesMap ?? [])];
79
84
  soul.vertexBuffers = collectVertexBufferPairs(memo.usedVertexLayouts, priors.vertexLayoutMap);
80
85
  soul.indexBuffer = priors.indexBuffer;
81
86
  soul.stencilReference = priors.stencilReference;
@@ -141,6 +146,11 @@ class TgpuRenderPipelineImpl {
141
146
  ]),
142
147
  });
143
148
  }
149
+ if (isImmediateVar(first)) {
150
+ return this.#withPriors({
151
+ immediatesMap: new Map(internals.priors.immediatesMap).set(first, createImmediateSnapshot(first, resource)),
152
+ });
153
+ }
144
154
  throw new Error('Unsupported value passed into .with()');
145
155
  }
146
156
  pipe(transform) {
@@ -214,12 +224,10 @@ class TgpuRenderPipelineImpl {
214
224
  }
215
225
  #ownPassDescriptor() {
216
226
  const { core, priors } = this[$internal];
217
- const { fragmentOut } = core.unwrap();
227
+ const { connectedTargets } = core.unwrap();
218
228
  return {
219
229
  label: getName(core) ?? '<unnamed>',
220
- colorAttachments: fragmentOut
221
- ? connectAttachmentToShader(fragmentOut, priors.colorAttachment ?? {})
222
- : [],
230
+ colorAttachments: connectAttachmentToShader(connectedTargets, priors.colorAttachment ?? {}),
223
231
  depthStencilAttachment: priors.depthStencilAttachment,
224
232
  timestampWrites: priors.timestampWrites,
225
233
  };
@@ -335,7 +343,7 @@ class RenderPipelineCore {
335
343
  if (this.#initAsyncPromise === undefined) {
336
344
  // the pipeline did not start resolution & compilation
337
345
  const device = this.options.root.device;
338
- const { resolutionResult, descriptor, connectedAttribs, fragmentOut } = this.resolveAndCreateShaderModule();
346
+ const { resolutionResult, descriptor, connectedTargets, connectedAttribs } = this.resolveAndCreateShaderModule();
339
347
  const { usedBindGroupLayouts, catchall, logResources } = resolutionResult;
340
348
  this.#initAsyncPromise = device
341
349
  .createRenderPipelineAsync(descriptor)
@@ -346,7 +354,8 @@ class RenderPipelineCore {
346
354
  catchall,
347
355
  logResources,
348
356
  usedVertexLayouts: connectedAttribs.usedVertexLayouts,
349
- fragmentOut,
357
+ connectedTargets,
358
+ usedImmediate: resolutionResult.usedImmediate,
350
359
  };
351
360
  this.#performanceTracker.measureCompile(device);
352
361
  })
@@ -364,7 +373,7 @@ class RenderPipelineCore {
364
373
  throw new Error("'pipeline.initAsync()' was called and is not yet resolved.");
365
374
  }
366
375
  const device = this.options.root.device;
367
- const { resolutionResult, descriptor, connectedAttribs, fragmentOut } = this.resolveAndCreateShaderModule();
376
+ const { resolutionResult, descriptor, connectedTargets, connectedAttribs } = this.resolveAndCreateShaderModule();
368
377
  const { usedBindGroupLayouts, catchall, logResources } = resolutionResult;
369
378
  this.#memo = {
370
379
  pipeline: device.createRenderPipeline(descriptor),
@@ -372,7 +381,8 @@ class RenderPipelineCore {
372
381
  catchall,
373
382
  logResources,
374
383
  usedVertexLayouts: connectedAttribs.usedVertexLayouts,
375
- fragmentOut,
384
+ connectedTargets,
385
+ usedImmediate: resolutionResult.usedImmediate,
376
386
  };
377
387
  this.#performanceTracker.measureCompile(device);
378
388
  }
@@ -396,11 +406,12 @@ class RenderPipelineCore {
396
406
  shaderGenerator: root.shaderGeneratorClass ? new root.shaderGeneratorClass() : undefined,
397
407
  root,
398
408
  }));
399
- const { code, usedBindGroupLayouts, catchall } = resolutionResult;
409
+ const { code, usedBindGroupLayouts, catchall, usedImmediate } = resolutionResult;
400
410
  if (catchall !== undefined) {
401
411
  usedBindGroupLayouts[catchall[0]]?.$name(`${getName(this) ?? '<unnamed>'} - Automatic Bind Group & Layout`);
402
412
  }
403
413
  warnIfOverflow(usedBindGroupLayouts, device.limits);
414
+ const immediateSize = usedImmediate ? validateImmediateUsage(usedImmediate, root) : 0;
404
415
  const module = device.createShaderModule({
405
416
  label: `${getName(this) ?? '<unnamed>'} - Shader`,
406
417
  code,
@@ -417,6 +428,7 @@ class RenderPipelineCore {
417
428
  layout: device.createPipelineLayout({
418
429
  label: `${getName(this) ?? '<unnamed>'} - Pipeline Layout`,
419
430
  bindGroupLayouts: usedBindGroupLayouts.map((l) => root.unwrap(l)),
431
+ immediateSize,
420
432
  }),
421
433
  vertex: {
422
434
  module,
@@ -430,7 +442,7 @@ class RenderPipelineCore {
430
442
  if (tgpuDescriptor.fragment) {
431
443
  descriptor.fragment = {
432
444
  module,
433
- targets: connectedTargets,
445
+ targets: connectedTargets.map((target) => (target ? target.target : null)),
434
446
  };
435
447
  }
436
448
  if (tgpuDescriptor.primitive) {
@@ -453,7 +465,7 @@ class RenderPipelineCore {
453
465
  if (tgpuDescriptor.multisample) {
454
466
  descriptor.multisample = tgpuDescriptor.multisample;
455
467
  }
456
- return { resolutionResult, descriptor, connectedAttribs, fragmentOut };
468
+ return { resolutionResult, descriptor, connectedTargets, connectedAttribs };
457
469
  }
458
470
  }
459
471
  /**
@@ -1,4 +1,4 @@
1
- import { bannedTokens, builtins } from "../../nameUtils.js";
1
+ import { bannedTokens, builtins, templateEnumerants } from "../../nameUtils.js";
2
2
  import { $internal } from "../../shared/symbols.js";
3
3
  import { ShelllessRepository } from "../../tgsl/shellless.js";
4
4
  class NamespaceImpl {
@@ -6,7 +6,7 @@ class NamespaceImpl {
6
6
  constructor(strategy) {
7
7
  this[$internal] = {
8
8
  strategy,
9
- takenGlobalIdentifiers: new Set([...bannedTokens, ...builtins]),
9
+ takenGlobalIdentifiers: new Set([...bannedTokens, ...builtins, ...templateEnumerants]),
10
10
  shelllessRepo: new ShelllessRepository(),
11
11
  memoizedResolves: new WeakMap(),
12
12
  memoizedLazy: new WeakMap(),
package/core/root/init.js CHANGED
@@ -214,6 +214,9 @@ class TgpuRootImpl extends WithBindingImpl {
214
214
  get minify() {
215
215
  return this[$soul].minify;
216
216
  }
217
+ get enabledWgslLanguageFeatures() {
218
+ return navigator.gpu?.wgslLanguageFeatures ?? new Set();
219
+ }
217
220
  createBuffer(typeSchema, initialOrBuffer) {
218
221
  return INTERNAL_createBuffer(this, typeSchema, initialOrBuffer);
219
222
  }
@@ -419,9 +419,24 @@ export interface TgpuRoot extends Unwrapper, WithBinding {
419
419
  */
420
420
  get enabledFeatures(): ReadonlySet<GPUFeatureName>;
421
421
  /**
422
- * Destroys all underlying resources (i.e. buffers...) created through this root object.
422
+ * Retrieves a read-only list of WGSL language extensions supported in the
423
+ * current environment (`navigator.gpu.wgslLanguageFeatures`).
424
+ * Returns an empty set when WebGPU is unavailable.
425
+ *
426
+ * @example
427
+ * ```ts
428
+ * const canUseImmediates = root.enabledWgslLanguageFeatures.has('immediate_address_space');
429
+ * ```
430
+ */
431
+ get enabledWgslLanguageFeatures(): ReadonlySet<string>;
432
+ /**
433
+ * Releases internal resource caches (e.g. utility texture pipelines),
434
+ * letting the GC reclaim them.
435
+ *
436
+ * Buffers, textures and other resources created through the root are NOT destroyed.
437
+ *
423
438
  * If the object is created via `tgpu.init` instead of `tgpu.initFromDevice`,
424
- * then the inner GPU device is destroyed as well.
439
+ * destroys the underlying WebGPU device as well.
425
440
  */
426
441
  destroy(): void;
427
442
  '~unstable': Pick<ExperimentalTgpuRoot, 'createCommandEncoder' | 'createComparisonSampler' | 'createGuardedComputePipeline' | 'createRenderBundleEncoder' | 'createSampler' | 'createTexture' | 'nameRegistrySetting' | 'shaderGeneratorClass' | 'pipe' | 'with'>;
@@ -1,5 +1,5 @@
1
1
  import { type AnyData } from '../../data/dataTypes.ts';
2
- import type { BaseData } from '../../data/wgslTypes.ts';
2
+ import { type BaseData } from '../../data/wgslTypes.ts';
3
3
  import type { InferGPU } from '../../shared/repr.ts';
4
4
  import { $getNameForward, $gpuValueOf, $internal, $resolve, $soul } from '../../shared/symbols.ts';
5
5
  import type { UnwrapRuntimeConstructor } from '../../tgpuBindGroupLayout.ts';
@@ -1,6 +1,10 @@
1
- import { isData } from "../../data/dataTypes.js";
1
+ import { isData, undecorate } from "../../data/dataTypes.js";
2
2
  import { schemaCallWrapper } from "../../data/schemaCallWrapper.js";
3
3
  import { isSnippet, snip } from "../../data/snippet.js";
4
+ import { deepEqual } from "../../data/deepEqual.js";
5
+ import { arrayOf } from "../../data/array.js";
6
+ import { UnknownData } from "../../data/dataTypes.js";
7
+ import { isAbstract, isWgslArray } from "../../data/wgslTypes.js";
4
8
  import { getResolutionCtx } from "../../execMode.js";
5
9
  import { makeDereferenceable } from "../../tgsl/makeDereferenceable.js";
6
10
  import { makeResolvable } from "../../tgsl/makeResolvable.js";
@@ -19,10 +23,65 @@ export function mutableAccessor(schemaOrConstructor, defaultValue) {
19
23
  // --------------
20
24
  // Implementation
21
25
  // --------------
26
+ /**
27
+ * Accessors typed with a runtime-sized array schema (e.g. `d.arrayOf(d.f32)`) accept
28
+ * any array of that element type. If the provided JS value has a concrete length, we
29
+ * use a statically-sized schema so that the generated code can take advantage of it.
30
+ */
31
+ function concretizeSchema(accessorSchema, value) {
32
+ if (isWgslArray(accessorSchema) && accessorSchema.elementCount === 0 && Array.isArray(value)) {
33
+ if (value.length === 0) {
34
+ throw new Error(`Cannot use empty array as an accessor value. Empty arrays aren't representable in shader code.`);
35
+ }
36
+ return arrayOf(accessorSchema.elementType, value.length);
37
+ }
38
+ return accessorSchema;
39
+ }
40
+ /**
41
+ * Origins of values that can be assigned to (or are handles, like storage textures).
42
+ * Accessors can only refer to module-scope resources, so function-scope origins
43
+ * ('function', 'local-def') are never provided to them.
44
+ */
45
+ const mutableOrigins = new Set(['mutable', 'workgroup', 'private', 'handle']);
46
+ /**
47
+ * Whether a value of type `providedType` can be used in place of `accessorSchema` without
48
+ * any conversion. Runtime-sized array schemas accept arrays of any length with a matching element type.
49
+ */
50
+ function matchesSchema(accessorSchema, providedType) {
51
+ if (providedType === UnknownData) {
52
+ return false;
53
+ }
54
+ const expected = undecorate(accessorSchema);
55
+ const provided = undecorate(providedType);
56
+ if (isWgslArray(expected) && expected.elementCount === 0 && isWgslArray(provided)) {
57
+ return deepEqual(expected.elementType, provided.elementType);
58
+ }
59
+ return deepEqual(expected, provided);
60
+ }
61
+ /**
62
+ * Values with a concrete WGSL type (buffers, variables, GPU functions, ...) are used as-is,
63
+ * so they have to match the accessor's schema exactly. Mutable accessors are also assigned to,
64
+ * so the provided value has to be mutable.
65
+ */
66
+ function validateAccessorSnippet(accessor, snippet) {
67
+ const isMutable = accessor.resourceType === 'mutable-accessor';
68
+ const description = `${isMutable ? 'mutable accessor' : 'accessor'} '${getName(accessor) ?? '<unnamed>'}'`;
69
+ if (isMutable && !mutableOrigins.has(snippet.origin)) {
70
+ throw new Error(`Values provided to ${description} must be mutable (e.g. mutable buffers, private or workgroup variables), got a value with origin '${snippet.origin}'. Use tgpu.accessor for read-only access.`);
71
+ }
72
+ if (!matchesSchema(accessor.schema, snippet.dataType)) {
73
+ throw new Error(`Value of type '${String(snippet.dataType)}' does not match the schema of ${description}: '${String(accessor.schema)}'.`);
74
+ }
75
+ }
22
76
  /**
23
77
  * @returns A snippet representing the accessor.
24
78
  */
25
79
  function createAccessorSnippet(accessor) {
80
+ const snippet = createUnvalidatedAccessorSnippet(accessor);
81
+ validateAccessorSnippet(accessor, snippet);
82
+ return snippet;
83
+ }
84
+ function createUnvalidatedAccessorSnippet(accessor) {
26
85
  // oxlint-disable-next-line typescript/no-non-null-assertion -- it's there
27
86
  const ctx = getResolutionCtx();
28
87
  let value = getGpuValueRecursively(ctx.unwrap(accessor.slot));
@@ -35,23 +94,31 @@ function createAccessorSnippet(accessor) {
35
94
  if (isSnippet(value)) {
36
95
  value = value.value;
37
96
  }
97
+ value = getGpuValueRecursively(value);
38
98
  }
39
99
  const ownSnippet = getOwnSnippet(value);
40
100
  if (ownSnippet) {
41
101
  return ownSnippet;
42
102
  }
43
103
  if (isGPUCallable(value)) {
44
- return value[$gpuCallable].call(ctx, []);
104
+ const result = value[$gpuCallable].call(ctx, []);
105
+ if (result.dataType !== UnknownData && !isAbstract(result.dataType)) {
106
+ return result;
107
+ }
108
+ // The result is a plain JS value (e.g. an array literal or a number returned from
109
+ // `tgpu.comptime`), so we treat it the same way as a directly provided JS value.
110
+ value = result.value;
45
111
  }
46
112
  if (isTgpuFn(value) || hasTinyestMetadata(value)) {
47
113
  const fn = ctx.resolve(value);
48
- return ctx.withResetIndentLevel(() => snip(`${fn.value}()`, accessor.schema, /* origin */ 'runtime', fn.possibleSideEffects));
114
+ return ctx.withResetIndentLevel(() => snip(`${fn.value}()`, fn.dataType, /* origin */ 'runtime', fn.possibleSideEffects));
49
115
  }
50
116
  ctx.pushMode(new NormalState());
51
117
  try {
118
+ const schema = concretizeSchema(accessor.schema, value);
52
119
  // Doing a deep copy each time so that we don't have to deal with refs
53
- const cloned = schemaCallWrapper(accessor.schema, value);
54
- return snip(cloned, accessor.schema, 'constant', /* possibleSideEffects */ false);
120
+ const cloned = schemaCallWrapper(schema, value);
121
+ return snip(cloned, schema, 'constant', /* possibleSideEffects */ false);
55
122
  }
56
123
  finally {
57
124
  ctx.popMode('normal');
@@ -7,6 +7,7 @@ import { $gpuValueOf, $internal, $providing, $soul } from '../../shared/symbols.
7
7
  import type { UnwrapRuntimeConstructor } from '../../tgpuBindGroupLayout.ts';
8
8
  import type { TgpuBufferBinding } from '../buffer/bufferBinding.ts';
9
9
  import type { TgpuConst } from '../constant/tgpuConstant.ts';
10
+ import type { TgpuImmediateVar } from '../immediate/immediateVar.ts';
10
11
  import type { Withable } from '../root/rootTypes.ts';
11
12
  import type { TgpuTextureView } from '../texture/texture.ts';
12
13
  import type { TgpuVar, VariableScope } from '../variable/tgpuVariable.ts';
@@ -51,7 +52,7 @@ export interface TgpuAccessor<T extends BaseData = BaseData> extends TgpuNamable
51
52
  readonly $: InferGPU<T>;
52
53
  toString(): string;
53
54
  }
54
- type DataAccessorIn<T extends BaseData> = (() => DataAccessorIn<T>) | TgpuBufferBinding<T> | TgpuVar<VariableScope, T> | TgpuConst<T> | Infer<T>;
55
+ type DataAccessorIn<T extends BaseData> = (() => DataAccessorIn<T>) | TgpuBufferBinding<T> | TgpuVar<VariableScope, T> | TgpuConst<T> | TgpuImmediateVar<T> | Infer<T>;
55
56
  type TextureAccessorIn<T extends WgslTexture | WgslStorageTexture> = (() => TextureAccessorIn<T>) | Infer<T> | TgpuTextureView<T>;
56
57
  export declare namespace TgpuAccessor {
57
58
  type In<T extends BaseData | ((count: number) => BaseData)> = UnwrapRuntimeConstructor<T> extends WgslTexture | WgslStorageTexture ? TextureAccessorIn<UnwrapRuntimeConstructor<T>> : DataAccessorIn<UnwrapRuntimeConstructor<T>>;
@@ -13,6 +13,7 @@ import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
13
13
  import type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';
14
14
  import type { TextureProps } from './textureProps.ts';
15
15
  import type { AllowedUsages, LiteralToExtensionMap, RenderFlag, SampledFlag } from './usageExtension.ts';
16
+ import { type TextureBlobWriteOptions, type TextureCopyOptions, type TextureRawWriteOptions, type TextureWriteOptions } from './textureWrite.ts';
16
17
  export type TextureInternals = {
17
18
  root: ExperimentalTgpuRoot;
18
19
  materialize(): GPUTexture;
@@ -38,17 +39,7 @@ type TextureViewInternals = {
38
39
  readonly aspect?: GPUTextureAspect | undefined;
39
40
  };
40
41
  export type TexelData = Vec4u | Vec4i | Vec4f;
41
- export type ExternalImageSource = HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ImageBitmap | ImageData | OffscreenCanvas | VideoFrame;
42
- export type TextureWriteFit = 'stretch';
43
- export type TextureWriteOptions = {
44
- /**
45
- * How to handle a source whose dimensions do not match the texture.
46
- *
47
- * By default, mismatched writes throw. Use `'stretch'` to resample the
48
- * source to the texture's dimensions.
49
- */
50
- fit?: TextureWriteFit;
51
- };
42
+ export type { TextureBlobWriteOptions, TextureChannel, TextureCopyOptions, TextureRawWriteOptions, TextureWriteFit, TextureWriteOptions, } from './textureWrite.ts';
52
43
  type TgpuTextureViewDescriptor = {
53
44
  /**
54
45
  * Which {@link GPUTextureAspect | aspect(s)} of the texture are accessible to the texture view.
@@ -106,6 +97,12 @@ type CopyCompatibleTexture<T extends TextureProps> = TgpuTexture<{
106
97
  format: T['format'];
107
98
  sampleCount?: T['sampleCount'];
108
99
  }>;
100
+ type FormatCompatibleTexture<T extends TextureProps> = TgpuTexture<{
101
+ size: readonly number[];
102
+ format: T['format'];
103
+ dimension?: TextureProps['dimension'];
104
+ sampleCount?: T['sampleCount'];
105
+ }>;
109
106
  export interface TgpuTexture<TProps extends TextureProps = any> extends TgpuNamable {
110
107
  readonly [$internal]: TextureInternals;
111
108
  readonly [$soul]: TgpuTextureSoul<TProps>;
@@ -123,11 +120,21 @@ export interface TgpuTexture<TProps extends TextureProps = any> extends TgpuNama
123
120
  sampleType?: T['sampleType'] extends F32 ? 'float' | 'unfilterable-float' : never;
124
121
  }): TgpuTextureView<T>;
125
122
  createView<T extends WgslStorageTexture>(schema: ValidateTextureViewSchema<this, T>, viewDescriptor?: TgpuTextureViewDescriptor): TgpuTextureView<T>;
123
+ /** Clears the texture to zeros */
126
124
  clear(mipLevel?: number | 'all'): void;
125
+ /** Clears the texture to `color`. Requires the `'render'` usage flag */
126
+ clear(color: readonly [number, number, number, number], mipLevel?: number | 'all'): void;
127
127
  generateMipmaps(baseMipLevel?: number, mipLevels?: number): void;
128
- write(source: ExternalImageSource | ExternalImageSource[], options?: TextureWriteOptions): void;
129
- write(source: ArrayBuffer | TypedArray | DataView, mipLevel?: number): void;
128
+ /** Writes image sources to the texture, one per array layer. Requires the `'render'` usage flag */
129
+ write(source: GPUCopyExternalImageSource | GPUCopyExternalImageSource[], options?: TextureWriteOptions): void;
130
+ /** Writes raw texel data to the texture */
131
+ write(source: ArrayBuffer | TypedArray | DataView, options?: number | TextureRawWriteOptions): void;
132
+ /** Decodes an image blob and writes it to the texture. Requires the `'render'` usage flag */
133
+ writeAsync(source: Blob, options?: TextureBlobWriteOptions): Promise<void>;
134
+ /** Copies the contents of a texture with a matching size and format */
130
135
  copyFrom<T extends CopyCompatibleTexture<TProps>>(source: T): void;
136
+ /** Copies a region between textures of the same format */
137
+ copyFrom<T extends FormatCompatibleTexture<TProps>>(source: T, options: TextureCopyOptions): void;
131
138
  destroy(): void;
132
139
  }
133
140
  export interface TgpuTextureView<TSchema extends WgslStorageTexture | WgslTexture = WgslStorageTexture | WgslTexture> extends TgpuNamable {
@@ -170,4 +177,3 @@ export declare class TgpuTextureRenderViewImpl implements TgpuTextureRenderView
170
177
  constructor(baseTexture: TgpuTexture, descriptor?: TgpuTextureViewDescriptor);
171
178
  get descriptor(): TgpuTextureViewDescriptor;
172
179
  }
173
- export {};