typegpu 0.12.4 → 0.12.6

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 (78) hide show
  1. package/bin.mjs +39 -67
  2. package/common/index.d.ts +1 -0
  3. package/common/index.js +1 -0
  4. package/common/writeChannels.d.ts +31 -0
  5. package/common/writeChannels.js +32 -0
  6. package/core/commandEncoder/computePass.d.ts +13 -0
  7. package/core/commandEncoder/computePass.js +5 -2
  8. package/core/commandEncoder/renderPass.d.ts +18 -4
  9. package/core/commandEncoder/renderPass.js +6 -2
  10. package/core/declare/tgpuDeclare.d.ts +11 -0
  11. package/core/declare/tgpuDeclare.js +1 -1
  12. package/core/immediate/immediateVar.d.ts +52 -0
  13. package/core/immediate/immediateVar.js +149 -0
  14. package/core/pipeline/computePipeline.d.ts +23 -0
  15. package/core/pipeline/computePipeline.js +26 -9
  16. package/core/pipeline/drawState.d.ts +8 -4
  17. package/core/pipeline/drawState.js +41 -51
  18. package/core/pipeline/priors.js +1 -0
  19. package/core/pipeline/renderPipeline.d.ts +24 -1
  20. package/core/pipeline/renderPipeline.js +18 -1
  21. package/core/root/init.js +3 -0
  22. package/core/root/rootTypes.d.ts +11 -0
  23. package/core/slot/slotTypes.d.ts +2 -1
  24. package/core/texture/texture.d.ts +20 -14
  25. package/core/texture/texture.js +70 -65
  26. package/core/texture/textureUtils.d.ts +3 -6
  27. package/core/texture/textureUtils.js +142 -165
  28. package/core/texture/textureWrite.d.ts +61 -0
  29. package/core/texture/textureWrite.js +59 -0
  30. package/data/dataIO.d.ts +2 -2
  31. package/data/generalizeFn.d.ts +17 -1
  32. package/data/generalizeFn.js +39 -2
  33. package/data/snippet.d.ts +5 -1
  34. package/data/snippet.js +1 -0
  35. package/data/wgslTypes.d.ts +1 -1
  36. package/errors.d.ts +7 -0
  37. package/errors.js +15 -0
  38. package/indexNamedExports.d.ts +5 -2
  39. package/indexNamedExports.js +2 -1
  40. package/internal.d.ts +1 -1
  41. package/internal.js +1 -1
  42. package/package.json +2 -2
  43. package/resolutionCtx.d.ts +5 -0
  44. package/resolutionCtx.js +11 -0
  45. package/serial/restore.d.ts +3 -1
  46. package/serial/restore.js +2 -0
  47. package/shared/meta.js +1 -1
  48. package/shared/symbols.js +1 -1
  49. package/shared/tseynit.d.ts +1 -0
  50. package/shared/tseynit.js +39 -7
  51. package/shared/utilityTypes.d.ts +1 -1
  52. package/std/atomic.d.ts +13 -0
  53. package/std/atomic.js +63 -29
  54. package/std/bitcast.js +4 -0
  55. package/std/boolean.d.ts +1 -1
  56. package/std/boolean.js +35 -21
  57. package/std/derivative.js +17 -12
  58. package/std/index.d.ts +4 -4
  59. package/std/index.js +5 -5
  60. package/std/numeric.d.ts +5 -1
  61. package/std/numeric.js +75 -8
  62. package/std/operators.d.ts +2 -2
  63. package/std/operators.js +29 -17
  64. package/std/packing.d.ts +61 -1
  65. package/std/packing.js +214 -25
  66. package/std/subgroup.d.ts +4 -0
  67. package/std/subgroup.js +34 -0
  68. package/std/texture.d.ts +13 -0
  69. package/std/texture.js +48 -0
  70. package/tgpuUnstable.d.ts +1 -0
  71. package/tgpuUnstable.js +1 -0
  72. package/tgsl/conversion.d.ts +2 -0
  73. package/tgsl/conversion.js +3 -0
  74. package/tgsl/shaderGenerator.d.ts +1 -1
  75. package/tgsl/wgslGenerator.d.ts +5 -0
  76. package/tgsl/wgslGenerator.js +218 -50
  77. package/types.d.ts +9 -1
  78. package/types.js +5 -0
@@ -6,6 +6,7 @@ import { $getNameForward, $internal, $resolve, $soul } from "../../shared/symbol
6
6
  import { isBindGroup, isBindGroupLayout, } from "../../tgpuBindGroupLayout.js";
7
7
  import { INTERNAL_adoptCommandEncoder, INTERNAL_createCommandEncoder, } from "../commandEncoder/commandEncoder.js";
8
8
  import { INTERNAL_adoptComputePass } from "../commandEncoder/computePass.js";
9
+ import { createImmediateSnapshot, isImmediateVar, validateImmediateUsage, } from "../immediate/immediateVar.js";
9
10
  import { emitComputeDispatch, finalizeOwnEncoder } from "./drawState.js";
10
11
  import { isGPUCommandEncoder, isGPUComputePassEncoder, isTgpuCommandEncoder, isTgpuComputePass, } from "./typeGuards.js";
11
12
  import { isGPUBuffer } from "../../types.js";
@@ -30,9 +31,11 @@ export function INTERNAL_restoreComputePipeline(soul, ctx) {
30
31
  // The catchall group is already one of `bindGroups`, keyed by the layout it was resolved with
31
32
  catchall: undefined,
32
33
  logResources: undefined,
34
+ usedImmediate: soul.usedImmediate,
33
35
  });
34
36
  const pipeline = new TgpuComputePipelineImpl(core, {
35
37
  bindGroupLayoutMap: new Map(soul.bindGroups),
38
+ immediatesMap: new Map(soul.immediates),
36
39
  });
37
40
  return restoreTimestampPriors(pipeline, soul);
38
41
  }
@@ -59,6 +62,8 @@ class TgpuComputePipelineImpl {
59
62
  soul.raw = memo.pipeline;
60
63
  soul.usedBindGroupLayouts = memo.usedBindGroupLayouts;
61
64
  soul.bindGroups = collectBindGroupPairs(memo.usedBindGroupLayouts, memo.catchall, priors.bindGroupLayoutMap);
65
+ soul.usedImmediate = memo.usedImmediate;
66
+ soul.immediates = [...(priors.immediatesMap ?? [])];
62
67
  soul.timestampWrites = priors.timestampWrites;
63
68
  soul.performanceCallback = priors.performanceCallback;
64
69
  soul.nonTransferablePriors = nonTransferablePriorsOf(priors);
@@ -78,7 +83,7 @@ class TgpuComputePipelineImpl {
78
83
  const { core, priors } = this[$internal];
79
84
  return new TgpuComputePipelineImpl(core, { ...priors, ...patch });
80
85
  }
81
- with(first, bindGroup) {
86
+ with(first, resource) {
82
87
  const internals = this[$internal];
83
88
  if (isTgpuComputePass(first)) {
84
89
  return this.#withPriors({ pass: first, encoder: undefined });
@@ -98,10 +103,15 @@ class TgpuComputePipelineImpl {
98
103
  encoder: INTERNAL_adoptCommandEncoder(internals.root, first),
99
104
  });
100
105
  }
106
+ if (isImmediateVar(first)) {
107
+ return this.#withPriors({
108
+ immediatesMap: new Map(internals.priors.immediatesMap).set(first, createImmediateSnapshot(first, resource)),
109
+ });
110
+ }
101
111
  if (isBindGroup(first) || isBindGroupLayout(first)) {
102
112
  const [layout, group] = isBindGroup(first)
103
113
  ? [first.layout, first]
104
- : [first, bindGroup];
114
+ : [first, resource];
105
115
  return this.#withPriors({
106
116
  bindGroupLayoutMap: new Map([
107
117
  ...(internals.priors.bindGroupLayoutMap ?? []),
@@ -111,6 +121,9 @@ class TgpuComputePipelineImpl {
111
121
  }
112
122
  throw new Error('Unsupported value passed into .with()');
113
123
  }
124
+ pipe(transform) {
125
+ return transform(this);
126
+ }
114
127
  withPerformanceCallback(callback) {
115
128
  const internals = this[$internal];
116
129
  if (internals.priors.timestampWrites) {
@@ -217,19 +230,20 @@ class ComputePipelineCore {
217
230
  if (this.#initAsyncPromise === undefined) {
218
231
  // the pipeline did not start resolution & compilation
219
232
  const device = this.root.device;
220
- const { resolutionResult, module } = this.resolveAndCreateShaderModule();
221
- const { usedBindGroupLayouts, catchall, logResources } = resolutionResult;
233
+ const { resolutionResult, module, immediateSize } = this.resolveAndCreateShaderModule();
234
+ const { usedBindGroupLayouts, catchall, logResources, usedImmediate } = resolutionResult;
222
235
  this.#initAsyncPromise = device
223
236
  .createComputePipelineAsync({
224
237
  label: getName(this) ?? '<unnamed>',
225
238
  layout: device.createPipelineLayout({
226
239
  label: `${getName(this) ?? '<unnamed>'} - Pipeline Layout`,
227
240
  bindGroupLayouts: usedBindGroupLayouts.map((l) => this.root.unwrap(l)),
241
+ immediateSize,
228
242
  }),
229
243
  compute: { module },
230
244
  })
231
245
  .then((pipeline) => {
232
- this.#memo = { pipeline, usedBindGroupLayouts, catchall, logResources };
246
+ this.#memo = { pipeline, usedBindGroupLayouts, catchall, logResources, usedImmediate };
233
247
  this.#performanceTracker.measureCompile(device);
234
248
  })
235
249
  .finally(() => {
@@ -246,20 +260,22 @@ class ComputePipelineCore {
246
260
  throw new Error("'pipeline.initAsync()' was called and is not yet resolved.");
247
261
  }
248
262
  const device = this.root.device;
249
- const { resolutionResult, module } = this.resolveAndCreateShaderModule();
250
- const { usedBindGroupLayouts, catchall, logResources } = resolutionResult;
263
+ const { resolutionResult, module, immediateSize } = this.resolveAndCreateShaderModule();
264
+ const { usedBindGroupLayouts, catchall, logResources, usedImmediate } = resolutionResult;
251
265
  this.#memo = {
252
266
  pipeline: device.createComputePipeline({
253
267
  label: getName(this) ?? '<unnamed>',
254
268
  layout: device.createPipelineLayout({
255
269
  label: `${getName(this) ?? '<unnamed>'} - Pipeline Layout`,
256
270
  bindGroupLayouts: usedBindGroupLayouts.map((l) => this.root.unwrap(l)),
271
+ immediateSize,
257
272
  }),
258
273
  compute: { module },
259
274
  }),
260
275
  usedBindGroupLayouts,
261
276
  catchall,
262
277
  logResources,
278
+ usedImmediate,
263
279
  };
264
280
  this.#performanceTracker.measureCompile(device);
265
281
  }
@@ -281,15 +297,16 @@ class ComputePipelineCore {
281
297
  : undefined,
282
298
  root: this.root,
283
299
  }));
284
- const { code, usedBindGroupLayouts, catchall } = resolutionResult;
300
+ const { code, usedBindGroupLayouts, catchall, usedImmediate } = resolutionResult;
285
301
  if (catchall !== undefined) {
286
302
  usedBindGroupLayouts[catchall[0]]?.$name(`${getName(this) ?? '<unnamed>'} - Automatic Bind Group & Layout`);
287
303
  }
288
304
  warnIfOverflow(usedBindGroupLayouts, device.limits);
305
+ const immediateSize = usedImmediate ? validateImmediateUsage(usedImmediate, this.root) : 0;
289
306
  const module = device.createShaderModule({
290
307
  label: `${getName(this) ?? '<unnamed>'} - Shader`,
291
308
  code,
292
309
  });
293
- return { resolutionResult, module };
310
+ return { resolutionResult, module, immediateSize };
294
311
  }
295
312
  }
@@ -5,6 +5,7 @@ import type { IndexFlag, TgpuBuffer, VertexFlag } from '../buffer/buffer.ts';
5
5
  import type { TgpuCommandEncoder } from '../commandEncoder/commandEncoder.ts';
6
6
  import type { ComputePassInternals } from '../commandEncoder/computePass.ts';
7
7
  import type { RenderPassInternals } from '../commandEncoder/renderPass.ts';
8
+ import { ImmediatePassState } from '../immediate/immediateVar.ts';
8
9
  import type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';
9
10
  import type { TgpuVertexLayout } from '../vertexLayout/vertexLayout.ts';
10
11
  import type { TgpuComputePipeline } from './computePipeline.ts';
@@ -24,6 +25,7 @@ export interface IndexBufferEntry {
24
25
  export declare class RenderDrawState {
25
26
  readonly bindGroups: Map<TgpuBindGroupLayout<Record<string, import("../../tgpuBindGroupLayout.ts").TgpuLayoutEntry | null>>, TgpuBindGroup | GPUBindGroup>;
26
27
  readonly vertexBuffers: Map<TgpuVertexLayout<import("../../data/wgslTypes.ts").WgslArray<BaseData> | import("../../data/dataTypes.ts").Disarray<BaseData>>, VertexBufferEntry>;
28
+ readonly immediates: ImmediatePassState;
27
29
  currentPipeline: TgpuRenderPipeline | undefined;
28
30
  indexBuffer: IndexBufferEntry | undefined;
29
31
  stencilReference: GPUStencilValue | undefined;
@@ -35,15 +37,17 @@ export declare class RenderDrawState {
35
37
  }
36
38
  export declare class ComputeDrawState {
37
39
  readonly bindGroups: Map<TgpuBindGroupLayout<Record<string, import("../../tgpuBindGroupLayout.ts").TgpuLayoutEntry | null>>, TgpuBindGroup | GPUBindGroup>;
40
+ readonly immediates: ImmediatePassState;
38
41
  currentPipeline: TgpuComputePipeline | undefined;
39
42
  version: number;
40
43
  rawAccessed: boolean;
41
44
  }
42
45
  export declare function recordBindGroup(state: RenderDrawState | ComputeDrawState, first: TgpuBindGroup | TgpuBindGroupLayout, bindGroup: TgpuBindGroup | GPUBindGroup | undefined): void;
43
- /** Writes the pipeline and its bound resources into pass state; later set* calls overwrite them */
44
- export declare function stampRenderPipeline(state: RenderDrawState, pipeline: TgpuRenderPipeline): void;
45
- /** The compute counterpart of {@link stampRenderPipeline} */
46
- export declare function stampComputePipeline(state: ComputeDrawState, pipeline: TgpuComputePipeline): void;
46
+ /** Makes the pipeline current; its held state is resolved at draw time, below what the pass holds */
47
+ export declare function selectPipeline<TPipeline>(state: {
48
+ currentPipeline: TPipeline | undefined;
49
+ version: number;
50
+ }, pipeline: TPipeline): void;
47
51
  export declare function requireIndexBuffer(indexBuffer: IndexBufferEntry | undefined): void;
48
52
  export declare function finalizeOwnEncoder(encoder: TgpuCommandEncoder, core: object, logResources: LogResources | undefined, priors: TimestampWritesPriors & {
49
53
  readonly encoder?: TgpuCommandEncoder | undefined;
@@ -4,10 +4,12 @@ import { logger } from "../../tgpuLogger.js";
4
4
  import { isBindGroup, } from "../../tgpuBindGroupLayout.js";
5
5
  import { logDataFromGPU } from "../../tgsl/consoleLog/deserializers.js";
6
6
  import { isBuffer } from "../../types.js";
7
+ import { ImmediatePassState } from "../immediate/immediateVar.js";
7
8
  import { queueTimestampResolve } from "./timeable.js";
8
9
  export class RenderDrawState {
9
10
  bindGroups = new Map();
10
11
  vertexBuffers = new Map();
12
+ immediates = new ImmediatePassState();
11
13
  currentPipeline;
12
14
  indexBuffer;
13
15
  stencilReference;
@@ -19,6 +21,7 @@ export class RenderDrawState {
19
21
  }
20
22
  export class ComputeDrawState {
21
23
  bindGroups = new Map();
24
+ immediates = new ImmediatePassState();
22
25
  currentPipeline;
23
26
  version = 0;
24
27
  rawAccessed = false;
@@ -32,38 +35,12 @@ export function recordBindGroup(state, first, bindGroup) {
32
35
  }
33
36
  state.version++;
34
37
  }
35
- /** Writes the pipeline and its bound resources into pass state; later set* calls overwrite them */
36
- export function stampRenderPipeline(state, pipeline) {
37
- const { priors } = pipeline[$internal];
38
- state.currentPipeline = pipeline;
39
- if (priors.bindGroupLayoutMap) {
40
- for (const [layout, group] of priors.bindGroupLayoutMap) {
41
- state.bindGroups.set(layout, group);
42
- }
43
- }
44
- if (priors.vertexLayoutMap) {
45
- for (const [layout, buffer] of priors.vertexLayoutMap) {
46
- state.vertexBuffers.set(layout, { buffer, offset: undefined, size: undefined });
47
- }
48
- }
49
- if (priors.indexBuffer) {
50
- state.indexBuffer = priors.indexBuffer;
51
- }
52
- if (priors.stencilReference !== undefined) {
53
- state.stencilReference = priors.stencilReference;
54
- }
55
- state.version++;
56
- }
57
- /** The compute counterpart of {@link stampRenderPipeline} */
58
- export function stampComputePipeline(state, pipeline) {
59
- const { priors } = pipeline[$internal];
60
- state.currentPipeline = pipeline;
61
- if (priors.bindGroupLayoutMap) {
62
- for (const [layout, group] of priors.bindGroupLayoutMap) {
63
- state.bindGroups.set(layout, group);
64
- }
38
+ /** Makes the pipeline current; its held state is resolved at draw time, below what the pass holds */
39
+ export function selectPipeline(state, pipeline) {
40
+ if (state.currentPipeline !== pipeline) {
41
+ state.currentPipeline = pipeline;
42
+ state.version++;
65
43
  }
66
- state.version++;
67
44
  }
68
45
  function applyIndexBuffer(encoder, root, entry) {
69
46
  const { buffer, indexFormat, offsetBytes, sizeBytes } = entry;
@@ -117,25 +94,36 @@ function applyVertexBuffers(encoder, root, usedVertexLayouts, resolveVertexBuffe
117
94
  }
118
95
  }
119
96
  function applyRenderPipelineState(encoder, root, pipeline, passState) {
120
- const memo = pipeline[$internal].core.unwrap();
97
+ const { core, priors } = pipeline[$internal];
98
+ const memo = core.unwrap();
121
99
  encoder.setPipeline(memo.pipeline);
122
- applyBindGroups(encoder, root, memo.usedBindGroupLayouts, memo.catchall, (layout) => passState.bindGroups.get(layout));
123
- applyVertexBuffers(encoder, root, memo.usedVertexLayouts, (vertexLayout) => passState.vertexBuffers.get(vertexLayout));
124
- if (passState.indexBuffer !== undefined) {
125
- applyIndexBuffer(encoder, root, passState.indexBuffer);
126
- }
127
- if (typeof encoder.setStencilReference === 'function' &&
128
- passState.stencilReference !== undefined) {
129
- if (passState.rawAccessed || passState.stencilReference !== passState.appliedStencilReference) {
130
- encoder.setStencilReference(passState.stencilReference);
131
- passState.appliedStencilReference = passState.stencilReference;
100
+ applyBindGroups(encoder, root, memo.usedBindGroupLayouts, memo.catchall, (layout) => passState.bindGroups.get(layout) ?? priors.bindGroupLayoutMap?.get(layout));
101
+ applyVertexBuffers(encoder, root, memo.usedVertexLayouts, (vertexLayout) => {
102
+ const passEntry = passState.vertexBuffers.get(vertexLayout);
103
+ if (passEntry !== undefined) {
104
+ return passEntry;
105
+ }
106
+ const buffer = priors.vertexLayoutMap?.get(vertexLayout);
107
+ return buffer !== undefined ? { buffer } : undefined;
108
+ });
109
+ const indexBuffer = passState.indexBuffer ?? priors.indexBuffer;
110
+ if (indexBuffer !== undefined) {
111
+ applyIndexBuffer(encoder, root, indexBuffer);
112
+ }
113
+ if (typeof encoder.setStencilReference === 'function') {
114
+ const stencilReference = passState.stencilReference ?? priors.stencilReference ?? 0;
115
+ const dirty = passState.rawAccessed || stencilReference !== passState.appliedStencilReference;
116
+ if (dirty) {
117
+ encoder.setStencilReference(stencilReference);
118
+ passState.appliedStencilReference = stencilReference;
132
119
  }
133
120
  }
134
121
  }
135
122
  function applyComputePipelineState(encoder, root, pipeline, passState) {
136
- const memo = pipeline[$internal].core.unwrap();
123
+ const { core, priors } = pipeline[$internal];
124
+ const memo = core.unwrap();
137
125
  encoder.setPipeline(memo.pipeline);
138
- applyBindGroups(encoder, root, memo.usedBindGroupLayouts, memo.catchall, (layout) => passState.bindGroups.get(layout));
126
+ applyBindGroups(encoder, root, memo.usedBindGroupLayouts, memo.catchall, (layout) => passState.bindGroups.get(layout) ?? priors.bindGroupLayoutMap?.get(layout));
139
127
  }
140
128
  export function requireIndexBuffer(indexBuffer) {
141
129
  if (!indexBuffer) {
@@ -191,11 +179,9 @@ function reportIgnoredPriors(core, owner, hasTimestampWrites, logResources, pass
191
179
  export function emitRenderDraw(root, passInternals, pipeline, usesIndexBuffer, emit, ownsPass = false) {
192
180
  const { state, rawPass } = passInternals;
193
181
  const { core, priors } = pipeline[$internal];
194
- if (state.currentPipeline !== pipeline) {
195
- stampRenderPipeline(state, pipeline);
196
- }
182
+ selectPipeline(state, pipeline);
197
183
  if (usesIndexBuffer) {
198
- requireIndexBuffer(state.indexBuffer);
184
+ requireIndexBuffer(state.indexBuffer ?? priors.indexBuffer);
199
185
  }
200
186
  const memo = core.unwrap();
201
187
  if (!ownsPass) {
@@ -205,14 +191,15 @@ export function emitRenderDraw(root, passInternals, pipeline, usesIndexBuffer, e
205
191
  applyRenderPipelineState(rawPass, root, pipeline, state);
206
192
  passInternals.appliedVersion = state.version;
207
193
  }
194
+ if (memo.usedImmediate !== undefined) {
195
+ state.immediates.write(rawPass, memo.usedImmediate, priors.immediatesMap, !state.rawAccessed);
196
+ }
208
197
  emit(rawPass);
209
198
  }
210
199
  export function emitComputeDispatch(root, passInternals, pipeline, emit, ownsPass = false) {
211
200
  const { state, rawPass } = passInternals;
212
201
  const { core, priors } = pipeline[$internal];
213
- if (state.currentPipeline !== pipeline) {
214
- stampComputePipeline(state, pipeline);
215
- }
202
+ selectPipeline(state, pipeline);
216
203
  const memo = core.unwrap();
217
204
  if (!ownsPass) {
218
205
  reportIgnoredPriors(core, passInternals.owner, !!priors.timestampWrites, memo.logResources, 'compute');
@@ -221,5 +208,8 @@ export function emitComputeDispatch(root, passInternals, pipeline, emit, ownsPas
221
208
  applyComputePipelineState(rawPass, root, pipeline, state);
222
209
  passInternals.appliedVersion = state.version;
223
210
  }
211
+ if (memo.usedImmediate !== undefined) {
212
+ state.immediates.write(rawPass, memo.usedImmediate, priors.immediatesMap, !state.rawAccessed);
213
+ }
224
214
  emit(rawPass);
225
215
  }
@@ -1,6 +1,7 @@
1
1
  // GPU objects like query sets cross runtimes as shareable host objects, performanceCallback crosses only if it is a worklet
2
2
  const TRANSFERABLE_PRIORS = new Set([
3
3
  'bindGroupLayoutMap',
4
+ 'immediatesMap',
4
5
  'vertexLayoutMap',
5
6
  'indexBuffer',
6
7
  'stencilReference',
@@ -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, type WgslStruct } 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';
@@ -19,6 +20,7 @@ import type { TgpuSlot } from '../slot/slotTypes.ts';
19
20
  import { type TgpuVertexLayout } from '../vertexLayout/vertexLayout.ts';
20
21
  import { type TgpuCommandEncoder } from '../commandEncoder/commandEncoder.ts';
21
22
  import type { ColorAttachment, DepthStencilAttachment } from '../commandEncoder/attachments.ts';
23
+ import { type ImmediateSnapshot, type TgpuImmediateVar } from '../immediate/immediateVar.ts';
22
24
  import { type TgpuRenderCommands } from '../commandEncoder/renderPass.ts';
23
25
  import { type Timeable, type TimestampWritesPriors } from './timeable.ts';
24
26
  import { type PrimitiveOffsetInfo } from '../../data/offsetUtils.ts';
@@ -34,6 +36,8 @@ export interface TgpuRenderPipelineSoul extends TgpuDeviceOwningSoul<'render-pip
34
36
  usedVertexLayouts?: TgpuVertexLayout[] | undefined;
35
37
  fragmentOut?: BaseData | undefined;
36
38
  bindGroups?: [TgpuBindGroupLayout, TgpuBindGroup | GPUBindGroup][] | undefined;
39
+ usedImmediate?: TgpuImmediateVar | undefined;
40
+ immediates?: [TgpuImmediateVar, ImmediateSnapshot][] | undefined;
37
41
  vertexBuffers?: [TgpuVertexLayout, (TgpuBuffer<BaseData> & VertexFlag) | GPUBuffer][] | undefined;
38
42
  indexBuffer?: {
39
43
  buffer: (TgpuBuffer<BaseData> & IndexFlag) | GPUBuffer;
@@ -76,6 +80,16 @@ export interface TgpuRenderPipeline<in Targets = never> extends TgpuNamable, Sel
76
80
  with<Entries extends Record<string, TgpuLayoutEntry | null>>(bindGroupLayout: TgpuBindGroupLayout<Entries>, bindGroup: TgpuBindGroup<Entries>): this;
77
81
  with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: GPUBindGroup): this;
78
82
  with(bindGroup: TgpuBindGroup): this;
83
+ /**
84
+ * Provides a value for the given immediate variable, applied on draw like
85
+ * the rest of the pipeline-held state and overridden by `pass.setImmediates`.
86
+ * The value is captured (copied) at call time; mutating it afterwards has
87
+ * no effect.
88
+ *
89
+ * Passing an `ArrayBuffer` or typed array skips serialization entirely; the bytes
90
+ * are copied verbatim and the caller guarantees they match the schema's layout.
91
+ */
92
+ with<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): this;
79
93
  /**
80
94
  * Directs subsequent draw calls into the given render pass or render bundle
81
95
  * encoder, letting multiple pipelines share one pass (and one submission).
@@ -89,6 +103,11 @@ export interface TgpuRenderPipeline<in Targets = never> extends TgpuNamable, Sel
89
103
  with(encoder: GPUCommandEncoder): this;
90
104
  with(pass: GPURenderPassEncoder): this;
91
105
  with(bundleEncoder: GPURenderBundleEncoder): this;
106
+ /**
107
+ * Applies a transform to this pipeline, letting packages hand out reusable
108
+ * configuration steps, e.g. `pipeline.pipe(mesh.inject())`.
109
+ */
110
+ pipe<T>(transform: (pipeline: this) => T): T;
92
111
  /**
93
112
  * Attaches texture views to the pipeline's targets (outputs).
94
113
  *
@@ -194,6 +213,7 @@ type TgpuRenderPipelinePriors = {
194
213
  readonly pass?: TgpuRenderCommands | undefined;
195
214
  /** An encoder the pipeline records its own passes into, but does not submit */
196
215
  readonly encoder?: TgpuCommandEncoder | undefined;
216
+ readonly immediatesMap?: Map<TgpuImmediateVar, ImmediateSnapshot> | undefined;
197
217
  } & TimestampWritesPriors;
198
218
  type Memo = {
199
219
  pipeline: GPURenderPipeline;
@@ -202,6 +222,7 @@ type Memo = {
202
222
  logResources: LogResources | undefined;
203
223
  usedVertexLayouts: TgpuVertexLayout[];
204
224
  fragmentOut: BaseData | undefined;
225
+ usedImmediate: TgpuImmediateVar | undefined;
205
226
  };
206
227
  declare class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
207
228
  #private;
@@ -218,11 +239,13 @@ declare class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
218
239
  with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: GPUBindGroup): this;
219
240
  with(bindGroup: TgpuBindGroup): this;
220
241
  with<TData extends WgslArray | Disarray>(vertexLayout: TgpuVertexLayout<TData>, buffer: GPUBuffer): this;
242
+ with<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): this;
221
243
  with(pass: TgpuRenderCommands): this;
222
244
  with(encoder: TgpuCommandEncoder): this;
223
245
  with(encoder: GPUCommandEncoder): this;
224
246
  with(pass: GPURenderPassEncoder): this;
225
247
  with(bundleEncoder: GPURenderBundleEncoder): this;
248
+ pipe<T>(transform: (pipeline: this) => T): T;
226
249
  withPerformanceCallback(callback: (start: bigint, end: bigint) => void | Promise<void>): this;
227
250
  withTimestampWrites(options: {
228
251
  querySet: TgpuQuerySet<'timestamp'> | GPUQuerySet;
@@ -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";
@@ -42,9 +43,11 @@ export function INTERNAL_restoreRenderPipeline(soul, ctx) {
42
43
  logResources: undefined,
43
44
  usedVertexLayouts: soul.usedVertexLayouts ?? [],
44
45
  fragmentOut: soul.fragmentOut,
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,
@@ -76,6 +79,8 @@ class TgpuRenderPipelineImpl {
76
79
  soul.usedVertexLayouts = memo.usedVertexLayouts;
77
80
  soul.fragmentOut = memo.fragmentOut;
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,8 +146,16 @@ 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
  }
156
+ pipe(transform) {
157
+ return transform(this);
158
+ }
146
159
  withPerformanceCallback(callback) {
147
160
  const internals = this[$internal];
148
161
  if (internals.priors.timestampWrites) {
@@ -344,6 +357,7 @@ class RenderPipelineCore {
344
357
  logResources,
345
358
  usedVertexLayouts: connectedAttribs.usedVertexLayouts,
346
359
  fragmentOut,
360
+ usedImmediate: resolutionResult.usedImmediate,
347
361
  };
348
362
  this.#performanceTracker.measureCompile(device);
349
363
  })
@@ -370,6 +384,7 @@ class RenderPipelineCore {
370
384
  logResources,
371
385
  usedVertexLayouts: connectedAttribs.usedVertexLayouts,
372
386
  fragmentOut,
387
+ usedImmediate: resolutionResult.usedImmediate,
373
388
  };
374
389
  this.#performanceTracker.measureCompile(device);
375
390
  }
@@ -393,11 +408,12 @@ class RenderPipelineCore {
393
408
  shaderGenerator: root.shaderGeneratorClass ? new root.shaderGeneratorClass() : undefined,
394
409
  root,
395
410
  }));
396
- const { code, usedBindGroupLayouts, catchall } = resolutionResult;
411
+ const { code, usedBindGroupLayouts, catchall, usedImmediate } = resolutionResult;
397
412
  if (catchall !== undefined) {
398
413
  usedBindGroupLayouts[catchall[0]]?.$name(`${getName(this) ?? '<unnamed>'} - Automatic Bind Group & Layout`);
399
414
  }
400
415
  warnIfOverflow(usedBindGroupLayouts, device.limits);
416
+ const immediateSize = usedImmediate ? validateImmediateUsage(usedImmediate, root) : 0;
401
417
  const module = device.createShaderModule({
402
418
  label: `${getName(this) ?? '<unnamed>'} - Shader`,
403
419
  code,
@@ -414,6 +430,7 @@ class RenderPipelineCore {
414
430
  layout: device.createPipelineLayout({
415
431
  label: `${getName(this) ?? '<unnamed>'} - Pipeline Layout`,
416
432
  bindGroupLayouts: usedBindGroupLayouts.map((l) => root.unwrap(l)),
433
+ immediateSize,
417
434
  }),
418
435
  vertex: {
419
436
  module,
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
  }
@@ -418,6 +418,17 @@ export interface TgpuRoot extends Unwrapper, WithBinding {
418
418
  * @returns A set of strings representing the enabled features.
419
419
  */
420
420
  get enabledFeatures(): ReadonlySet<GPUFeatureName>;
421
+ /**
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>;
421
432
  /**
422
433
  * Destroys all underlying resources (i.e. buffers...) created through this root object.
423
434
  * If the object is created via `tgpu.init` instead of `tgpu.initFromDevice`,
@@ -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 {};