typegpu 0.12.6 → 0.12.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/common/writeSoA.js +16 -0
- package/core/buffer/buffer.js +47 -11
- package/core/commandEncoder/attachments.js +5 -2
- package/core/function/autoIO.js +5 -3
- package/core/function/comptime.js +7 -5
- package/core/function/createCallableSchema.js +4 -3
- package/core/function/dualImpl.js +20 -19
- package/core/function/ioSchema.d.ts +17 -2
- package/core/function/ioSchema.js +74 -27
- package/core/function/tgpuFn.d.ts +11 -1
- package/core/function/tgpuFn.js +9 -2
- package/core/function/tgpuFragmentFn.d.ts +1 -1
- package/core/function/tgpuFragmentFn.js +2 -1
- package/core/function/tgpuVertexFn.js +2 -1
- package/core/pipeline/connectAttachmentToShader.d.ts +2 -2
- package/core/pipeline/connectAttachmentToShader.js +16 -21
- package/core/pipeline/connectTargetsToShader.d.ts +5 -1
- package/core/pipeline/connectTargetsToShader.js +23 -18
- package/core/pipeline/renderPipeline.d.ts +9 -6
- package/core/pipeline/renderPipeline.js +22 -22
- package/core/pipeline/timeable.js +15 -2
- package/core/resolve/namespace.js +2 -2
- package/core/root/configurableImpl.js +1 -2
- package/core/root/rootTypes.d.ts +6 -2
- package/core/slot/accessor.d.ts +1 -1
- package/core/slot/accessor.js +72 -5
- package/core/texture/externalTexture.d.ts +1 -1
- package/core/texture/externalTexture.js +3 -1
- package/core/texture/texture.d.ts +1 -1
- package/core/texture/texture.js +12 -5
- package/core/unroll/tgpuUnroll.d.ts +4 -3
- package/core/unroll/tgpuUnroll.js +7 -1
- package/core/valueProxyUtils.d.ts +4 -2
- package/core/valueProxyUtils.js +4 -2
- package/data/autoStruct.d.ts +1 -1
- package/data/autoStruct.js +8 -4
- package/data/compiledIO.js +12 -9
- package/data/dataIO.d.ts +10 -6
- package/data/dataIO.js +19 -4
- package/data/dataTypes.d.ts +0 -5
- package/data/dataTypes.js +0 -6
- package/data/deepEqual.js +19 -9
- package/data/matrix.js +10 -3
- package/data/matrixColumnsAccess.d.ts +8 -0
- package/data/matrixColumnsAccess.js +15 -0
- package/data/offsetUtils.js +181 -102
- package/data/partialIO.d.ts +8 -1
- package/data/partialIO.js +36 -19
- package/data/ref.d.ts +12 -5
- package/data/ref.js +51 -24
- package/data/snippet.d.ts +13 -4
- package/data/snippet.js +17 -4
- package/data/struct.js +3 -0
- package/data/wgslTypes.d.ts +6 -3
- package/internal.d.ts +1 -0
- package/nameUtils.d.ts +7 -0
- package/nameUtils.js +60 -0
- package/package.json +1 -1
- package/resolutionCtx.js +1 -1
- package/shared/meta.js +1 -1
- package/shared/symbols.d.ts +5 -0
- package/shared/symbols.js +6 -1
- package/shared/tseynit.js +54 -50
- package/std/boolean.js +9 -10
- package/std/numeric.js +1 -4
- package/std/operators.js +9 -4
- package/std/packing.js +10 -10
- package/std/texture.d.ts +2 -2
- package/tgpuLogger.d.ts +1 -1
- package/tgpuLogger.js +0 -2
- package/tgsl/accessIndex.js +37 -38
- package/tgsl/accessProp.js +10 -7
- package/tgsl/conversion.js +3 -2
- package/tgsl/forOfUtils.js +8 -8
- package/tgsl/generationHelpers.d.ts +4 -3
- package/tgsl/generationHelpers.js +18 -5
- package/tgsl/infixDispatch.d.ts +4 -1
- package/tgsl/infixDispatch.js +13 -1
- package/tgsl/makeDereferenceable.d.ts +1 -1
- package/tgsl/makeDereferenceable.js +3 -1
- package/tgsl/shellless.js +5 -9
- package/tgsl/stringifySnippet.d.ts +9 -0
- package/tgsl/stringifySnippet.js +24 -0
- package/tgsl/wgslGenerator.d.ts +1 -0
- package/tgsl/wgslGenerator.js +180 -86
- package/types.d.ts +23 -2
- package/types.js +30 -5
|
@@ -1,29 +1,24 @@
|
|
|
1
|
-
import { isBuiltin } from "../../data/attributes.js";
|
|
2
|
-
import { isVoid, isWgslStruct } from "../../data/wgslTypes.js";
|
|
3
1
|
function isColorAttachment(value) {
|
|
4
2
|
return !!value?.view;
|
|
5
3
|
}
|
|
6
|
-
export function connectAttachmentToShader(
|
|
7
|
-
if (
|
|
4
|
+
export function connectAttachmentToShader(connectedTargets, attachment) {
|
|
5
|
+
if (!connectedTargets) {
|
|
8
6
|
return [];
|
|
9
7
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const matching = attachment[key];
|
|
18
|
-
if (!matching) {
|
|
19
|
-
throw new Error(`A color attachment by the name of '${key}' was not provided to the shader.`);
|
|
8
|
+
return connectedTargets.map((target) => {
|
|
9
|
+
if (!target) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
if (target.key === undefined) {
|
|
13
|
+
if (!isColorAttachment(attachment)) {
|
|
14
|
+
throw new Error('Expected a single color attachment, not a record.');
|
|
20
15
|
}
|
|
21
|
-
|
|
16
|
+
return attachment;
|
|
22
17
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
const matching = attachment[target.key];
|
|
19
|
+
if (!matching) {
|
|
20
|
+
throw new Error(`A color attachment by the name of '${target.key}' was not provided to the shader.`);
|
|
21
|
+
}
|
|
22
|
+
return matching;
|
|
23
|
+
});
|
|
29
24
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { type BaseData } from '../../data/wgslTypes.ts';
|
|
2
2
|
import type { AnyFragmentTargets } from './renderPipeline.ts';
|
|
3
|
-
export
|
|
3
|
+
export interface ConnectedTarget {
|
|
4
|
+
key: string | undefined;
|
|
5
|
+
target: GPUColorTargetState;
|
|
6
|
+
}
|
|
7
|
+
export declare function connectTargetsToShader(fragmentOut: BaseData, targets: AnyFragmentTargets): (ConnectedTarget | null)[];
|
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
import { isBuiltin } from "../../data/attributes.js";
|
|
2
2
|
import { isVoid, isWgslStruct } from "../../data/wgslTypes.js";
|
|
3
|
+
import { invariant } from "../../errors.js";
|
|
4
|
+
import { getCustomLocation } from "../../data/dataTypes.js";
|
|
3
5
|
export function connectTargetsToShader(fragmentOut, targets) {
|
|
4
|
-
let presentationFormat;
|
|
5
6
|
if (isVoid(fragmentOut) || isBuiltin(fragmentOut)) {
|
|
6
7
|
return [];
|
|
7
8
|
}
|
|
9
|
+
const result = [];
|
|
10
|
+
function putInResult(index, key, target) {
|
|
11
|
+
while (index > result.length) {
|
|
12
|
+
result.push(null);
|
|
13
|
+
}
|
|
14
|
+
result[index] = { key, target };
|
|
15
|
+
}
|
|
8
16
|
if (isWgslStruct(fragmentOut)) {
|
|
9
|
-
const
|
|
10
|
-
for (const key of
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const matchingTarget = targets[key];
|
|
16
|
-
result.push({
|
|
17
|
+
const varyings = Object.entries(fragmentOut.propTypes).filter(([, value]) => !isBuiltin(value));
|
|
18
|
+
for (const [key, outputValue] of varyings) {
|
|
19
|
+
const matchingTarget = targets?.[key];
|
|
20
|
+
const location = getCustomLocation(outputValue);
|
|
21
|
+
invariant(location !== undefined, `'withLocations' failed or was not called.`);
|
|
22
|
+
putInResult(location, key, {
|
|
17
23
|
...matchingTarget,
|
|
18
|
-
format: matchingTarget?.format ??
|
|
19
|
-
(presentationFormat ??= navigator.gpu.getPreferredCanvasFormat()),
|
|
24
|
+
format: matchingTarget?.format ?? navigator.gpu.getPreferredCanvasFormat(),
|
|
20
25
|
});
|
|
21
26
|
}
|
|
22
|
-
return result;
|
|
23
27
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
28
|
+
else {
|
|
29
|
+
const singleTarget = targets;
|
|
30
|
+
putInResult(getCustomLocation(fragmentOut) ?? 0, undefined, {
|
|
27
31
|
...singleTarget,
|
|
28
|
-
format: singleTarget?.format ??
|
|
29
|
-
}
|
|
30
|
-
|
|
32
|
+
format: singleTarget?.format ?? navigator.gpu.getPreferredCanvasFormat(),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
31
36
|
}
|
|
@@ -3,7 +3,7 @@ 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 AnyWgslData, type BaseData, type U16, type U32, type v4f, Void, type WgslArray
|
|
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
8
|
import type { InferInput } from '../../shared/repr.ts';
|
|
9
9
|
import type { TgpuDeviceOwningSoul } from '../../shared/soul.ts';
|
|
@@ -18,6 +18,7 @@ import type { TgpuVertexFn } from '../function/tgpuVertexFn.ts';
|
|
|
18
18
|
import type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';
|
|
19
19
|
import type { TgpuSlot } from '../slot/slotTypes.ts';
|
|
20
20
|
import { type TgpuVertexLayout } from '../vertexLayout/vertexLayout.ts';
|
|
21
|
+
import { type ConnectedTarget } from './connectTargetsToShader.ts';
|
|
21
22
|
import { type TgpuCommandEncoder } from '../commandEncoder/commandEncoder.ts';
|
|
22
23
|
import type { ColorAttachment, DepthStencilAttachment } from '../commandEncoder/attachments.ts';
|
|
23
24
|
import { type ImmediateSnapshot, type TgpuImmediateVar } from '../immediate/immediateVar.ts';
|
|
@@ -34,7 +35,7 @@ export interface RenderPipelineInternals {
|
|
|
34
35
|
export interface TgpuRenderPipelineSoul extends TgpuDeviceOwningSoul<'render-pipeline', GPURenderPipeline> {
|
|
35
36
|
usedBindGroupLayouts?: TgpuBindGroupLayout[] | undefined;
|
|
36
37
|
usedVertexLayouts?: TgpuVertexLayout[] | undefined;
|
|
37
|
-
|
|
38
|
+
connectedTargets?: (ConnectedTarget | null)[] | undefined;
|
|
38
39
|
bindGroups?: [TgpuBindGroupLayout, TgpuBindGroup | GPUBindGroup][] | undefined;
|
|
39
40
|
usedImmediate?: TgpuImmediateVar | undefined;
|
|
40
41
|
immediates?: [TgpuImmediateVar, ImmediateSnapshot][] | undefined;
|
|
@@ -221,7 +222,7 @@ type Memo = {
|
|
|
221
222
|
catchall: [number, TgpuBindGroup] | undefined;
|
|
222
223
|
logResources: LogResources | undefined;
|
|
223
224
|
usedVertexLayouts: TgpuVertexLayout[];
|
|
224
|
-
|
|
225
|
+
connectedTargets: (ConnectedTarget | null)[] | undefined;
|
|
225
226
|
usedImmediate: TgpuImmediateVar | undefined;
|
|
226
227
|
};
|
|
227
228
|
declare class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
|
|
@@ -280,13 +281,15 @@ declare class RenderPipelineCore implements SelfResolvable {
|
|
|
280
281
|
resolveAndCreateShaderModule(): {
|
|
281
282
|
resolutionResult: import("../../resolutionCtx.ts").ResolutionResult;
|
|
282
283
|
descriptor: GPURenderPipelineDescriptor;
|
|
284
|
+
connectedTargets: (ConnectedTarget | null)[];
|
|
283
285
|
connectedAttribs: import("../vertexLayout/connectAttributesToShader.ts").ConnectAttributesToShaderResult;
|
|
284
|
-
fragmentOut: WgslStruct<import("../function/ioSchema.ts").WithLocations<Record<string, BaseData>>>;
|
|
285
286
|
};
|
|
286
287
|
}
|
|
287
288
|
/**
|
|
288
|
-
*
|
|
289
|
-
*
|
|
289
|
+
* Throws, when vertexOut and fragmentIn don't match in terms of custom locations.
|
|
290
|
+
*
|
|
291
|
+
* @privateRemarks
|
|
292
|
+
* Note that vertexOut is undefined in shellless functions.
|
|
290
293
|
*/
|
|
291
294
|
export declare function matchUpVaryingLocations(vertexOut: TgpuVertexFn.Out | undefined, fragmentIn: TgpuFragmentFn.In | undefined, vertexFnName: string, fragmentFnName: string): Record<string, number>;
|
|
292
295
|
export {};
|
|
@@ -42,7 +42,7 @@ export function INTERNAL_restoreRenderPipeline(soul, ctx) {
|
|
|
42
42
|
catchall: undefined,
|
|
43
43
|
logResources: undefined,
|
|
44
44
|
usedVertexLayouts: soul.usedVertexLayouts ?? [],
|
|
45
|
-
|
|
45
|
+
connectedTargets: soul.connectedTargets,
|
|
46
46
|
usedImmediate: soul.usedImmediate,
|
|
47
47
|
});
|
|
48
48
|
const pipeline = new TgpuRenderPipelineImpl(core, {
|
|
@@ -77,7 +77,7 @@ class TgpuRenderPipelineImpl {
|
|
|
77
77
|
soul.raw = memo.pipeline;
|
|
78
78
|
soul.usedBindGroupLayouts = memo.usedBindGroupLayouts;
|
|
79
79
|
soul.usedVertexLayouts = memo.usedVertexLayouts;
|
|
80
|
-
soul.
|
|
80
|
+
soul.connectedTargets = memo.connectedTargets;
|
|
81
81
|
soul.bindGroups = collectBindGroupPairs(memo.usedBindGroupLayouts, memo.catchall, priors.bindGroupLayoutMap);
|
|
82
82
|
soul.usedImmediate = memo.usedImmediate;
|
|
83
83
|
soul.immediates = [...(priors.immediatesMap ?? [])];
|
|
@@ -181,7 +181,7 @@ class TgpuRenderPipelineImpl {
|
|
|
181
181
|
withStencilReference(reference) {
|
|
182
182
|
return this.#withPriors({ stencilReference: reference });
|
|
183
183
|
}
|
|
184
|
-
withIndexBuffer(buffer, indexFormatOrOffset,
|
|
184
|
+
withIndexBuffer(buffer, indexFormatOrOffset, offsetBytesOrSizeElements, sizeBytesOrUndefined) {
|
|
185
185
|
if (isGPUBuffer(buffer)) {
|
|
186
186
|
if (typeof indexFormatOrOffset !== 'string') {
|
|
187
187
|
throw new Error('If a GPUBuffer is passed, indexFormat must be provided.');
|
|
@@ -190,8 +190,8 @@ class TgpuRenderPipelineImpl {
|
|
|
190
190
|
indexBuffer: {
|
|
191
191
|
buffer,
|
|
192
192
|
indexFormat: indexFormatOrOffset,
|
|
193
|
-
offsetBytes:
|
|
194
|
-
sizeBytes:
|
|
193
|
+
offsetBytes: offsetBytesOrSizeElements,
|
|
194
|
+
sizeBytes: sizeBytesOrUndefined,
|
|
195
195
|
},
|
|
196
196
|
});
|
|
197
197
|
}
|
|
@@ -207,8 +207,8 @@ class TgpuRenderPipelineImpl {
|
|
|
207
207
|
offsetBytes: indexFormatOrOffset !== undefined
|
|
208
208
|
? indexFormatOrOffset * sizeOf(elementType)
|
|
209
209
|
: undefined,
|
|
210
|
-
sizeBytes:
|
|
211
|
-
?
|
|
210
|
+
sizeBytes: offsetBytesOrSizeElements !== undefined
|
|
211
|
+
? offsetBytesOrSizeElements * sizeOf(elementType)
|
|
212
212
|
: undefined,
|
|
213
213
|
},
|
|
214
214
|
});
|
|
@@ -224,12 +224,10 @@ class TgpuRenderPipelineImpl {
|
|
|
224
224
|
}
|
|
225
225
|
#ownPassDescriptor() {
|
|
226
226
|
const { core, priors } = this[$internal];
|
|
227
|
-
const {
|
|
227
|
+
const { connectedTargets } = core.unwrap();
|
|
228
228
|
return {
|
|
229
229
|
label: getName(core) ?? '<unnamed>',
|
|
230
|
-
colorAttachments:
|
|
231
|
-
? connectAttachmentToShader(fragmentOut, priors.colorAttachment ?? {})
|
|
232
|
-
: [],
|
|
230
|
+
colorAttachments: connectAttachmentToShader(connectedTargets, priors.colorAttachment ?? {}),
|
|
233
231
|
depthStencilAttachment: priors.depthStencilAttachment,
|
|
234
232
|
timestampWrites: priors.timestampWrites,
|
|
235
233
|
};
|
|
@@ -345,7 +343,7 @@ class RenderPipelineCore {
|
|
|
345
343
|
if (this.#initAsyncPromise === undefined) {
|
|
346
344
|
// the pipeline did not start resolution & compilation
|
|
347
345
|
const device = this.options.root.device;
|
|
348
|
-
const { resolutionResult, descriptor,
|
|
346
|
+
const { resolutionResult, descriptor, connectedTargets, connectedAttribs } = this.resolveAndCreateShaderModule();
|
|
349
347
|
const { usedBindGroupLayouts, catchall, logResources } = resolutionResult;
|
|
350
348
|
this.#initAsyncPromise = device
|
|
351
349
|
.createRenderPipelineAsync(descriptor)
|
|
@@ -356,7 +354,7 @@ class RenderPipelineCore {
|
|
|
356
354
|
catchall,
|
|
357
355
|
logResources,
|
|
358
356
|
usedVertexLayouts: connectedAttribs.usedVertexLayouts,
|
|
359
|
-
|
|
357
|
+
connectedTargets,
|
|
360
358
|
usedImmediate: resolutionResult.usedImmediate,
|
|
361
359
|
};
|
|
362
360
|
this.#performanceTracker.measureCompile(device);
|
|
@@ -375,7 +373,7 @@ class RenderPipelineCore {
|
|
|
375
373
|
throw new Error("'pipeline.initAsync()' was called and is not yet resolved.");
|
|
376
374
|
}
|
|
377
375
|
const device = this.options.root.device;
|
|
378
|
-
const { resolutionResult, descriptor,
|
|
376
|
+
const { resolutionResult, descriptor, connectedTargets, connectedAttribs } = this.resolveAndCreateShaderModule();
|
|
379
377
|
const { usedBindGroupLayouts, catchall, logResources } = resolutionResult;
|
|
380
378
|
this.#memo = {
|
|
381
379
|
pipeline: device.createRenderPipeline(descriptor),
|
|
@@ -383,7 +381,7 @@ class RenderPipelineCore {
|
|
|
383
381
|
catchall,
|
|
384
382
|
logResources,
|
|
385
383
|
usedVertexLayouts: connectedAttribs.usedVertexLayouts,
|
|
386
|
-
|
|
384
|
+
connectedTargets,
|
|
387
385
|
usedImmediate: resolutionResult.usedImmediate,
|
|
388
386
|
};
|
|
389
387
|
this.#performanceTracker.measureCompile(device);
|
|
@@ -444,7 +442,7 @@ class RenderPipelineCore {
|
|
|
444
442
|
if (tgpuDescriptor.fragment) {
|
|
445
443
|
descriptor.fragment = {
|
|
446
444
|
module,
|
|
447
|
-
targets: connectedTargets,
|
|
445
|
+
targets: connectedTargets.map((target) => (target ? target.target : null)),
|
|
448
446
|
};
|
|
449
447
|
}
|
|
450
448
|
if (tgpuDescriptor.primitive) {
|
|
@@ -467,12 +465,14 @@ class RenderPipelineCore {
|
|
|
467
465
|
if (tgpuDescriptor.multisample) {
|
|
468
466
|
descriptor.multisample = tgpuDescriptor.multisample;
|
|
469
467
|
}
|
|
470
|
-
return { resolutionResult, descriptor,
|
|
468
|
+
return { resolutionResult, descriptor, connectedTargets, connectedAttribs };
|
|
471
469
|
}
|
|
472
470
|
}
|
|
473
471
|
/**
|
|
474
|
-
*
|
|
475
|
-
*
|
|
472
|
+
* Throws, when vertexOut and fragmentIn don't match in terms of custom locations.
|
|
473
|
+
*
|
|
474
|
+
* @privateRemarks
|
|
475
|
+
* Note that vertexOut is undefined in shellless functions.
|
|
476
476
|
*/
|
|
477
477
|
export function matchUpVaryingLocations(vertexOut = {}, fragmentIn = {}, vertexFnName, fragmentFnName) {
|
|
478
478
|
const locations = {};
|
|
@@ -497,13 +497,13 @@ export function matchUpVaryingLocations(vertexOut = {}, fragmentIn = {}, vertexF
|
|
|
497
497
|
saveLocation(key, customLocation);
|
|
498
498
|
}
|
|
499
499
|
else if (locations[key] !== customLocation) {
|
|
500
|
-
|
|
500
|
+
throw new Error(`Mismatched location between vertexFn (${vertexFnName}) output (${locations[key]}) and fragmentFn (${fragmentFnName}) input (${customLocation}) for the key "${key}".`);
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
// automatically assign remaining locations to the rest
|
|
504
504
|
let nextLocation = 0;
|
|
505
|
-
for (const key of Object.
|
|
506
|
-
if (isBuiltin(
|
|
505
|
+
for (const [key, value] of [...Object.entries(vertexOut), ...Object.entries(fragmentIn)]) {
|
|
506
|
+
if (isBuiltin(value) || locations[key] !== undefined) {
|
|
507
507
|
continue;
|
|
508
508
|
}
|
|
509
509
|
while (usedLocations.has(nextLocation)) {
|
|
@@ -41,14 +41,27 @@ async function readTimestamps(querySet, registrations) {
|
|
|
41
41
|
if (!querySet.available) {
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
let result;
|
|
45
|
+
try {
|
|
46
|
+
result = await querySet.read();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// The read was cancelled or failed (e.g. the device was destroyed or lost mid-read).
|
|
50
|
+
// Genuine validation errors are already reported through the device's error handling.
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
45
53
|
for (const { priors, callback } of registrations) {
|
|
46
54
|
const start = result[priors.timestampWrites?.beginningOfPassWriteIndex ?? 0];
|
|
47
55
|
const end = result[priors.timestampWrites?.endOfPassWriteIndex ?? 1];
|
|
48
56
|
if (start === undefined || end === undefined) {
|
|
49
57
|
throw new Error('QuerySet did not return valid timestamps.');
|
|
50
58
|
}
|
|
51
|
-
|
|
59
|
+
try {
|
|
60
|
+
await callback(start, end);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error('An error was thrown from a performance callback (withPerformanceCallback). Errors thrown here cannot be caught by the caller, so handle them within the callback itself.', err);
|
|
64
|
+
}
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
67
|
/** Returns false when the encoder is one we cannot defer work to, meaning the callback never fires */
|
|
@@ -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/rootTypes.d.ts
CHANGED
|
@@ -430,9 +430,13 @@ export interface TgpuRoot extends Unwrapper, WithBinding {
|
|
|
430
430
|
*/
|
|
431
431
|
get enabledWgslLanguageFeatures(): ReadonlySet<string>;
|
|
432
432
|
/**
|
|
433
|
-
*
|
|
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
|
+
*
|
|
434
438
|
* If the object is created via `tgpu.init` instead of `tgpu.initFromDevice`,
|
|
435
|
-
*
|
|
439
|
+
* destroys the underlying WebGPU device as well.
|
|
436
440
|
*/
|
|
437
441
|
destroy(): void;
|
|
438
442
|
'~unstable': Pick<ExperimentalTgpuRoot, 'createCommandEncoder' | 'createComparisonSampler' | 'createGuardedComputePipeline' | 'createRenderBundleEncoder' | 'createSampler' | 'createTexture' | 'nameRegistrySetting' | 'shaderGeneratorClass' | 'pipe' | 'with'>;
|
package/core/slot/accessor.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AnyData } from '../../data/dataTypes.ts';
|
|
2
|
-
import type
|
|
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';
|
package/core/slot/accessor.js
CHANGED
|
@@ -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
|
-
|
|
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}()`,
|
|
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(
|
|
54
|
-
return snip(cloned,
|
|
120
|
+
const cloned = schemaCallWrapper(schema, value);
|
|
121
|
+
return snip(cloned, schema, 'constant', /* possibleSideEffects */ false);
|
|
55
122
|
}
|
|
56
123
|
finally {
|
|
57
124
|
ctx.popMode('normal');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $gpuValueOf, $internal, $resolve } from '../../shared/symbols.ts';
|
|
2
2
|
import type { LayoutMembership } from '../../tgpuBindGroupLayout.ts';
|
|
3
3
|
import { type WgslExternalTexture } from '../../data/texture.ts';
|
|
4
|
-
import type
|
|
4
|
+
import { type ResolutionCtx, type SelfResolvable } from '../../types.ts';
|
|
5
5
|
import { type ResolvedSnippet } from '../../data/snippet.ts';
|
|
6
6
|
import type { Infer } from '../../shared/repr.ts';
|
|
7
7
|
export interface TgpuExternalTexture {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { $gpuValueOf, $internal, $ownSnippet, $resolve } from "../../shared/symbols.js";
|
|
1
|
+
import { $comptimeValueOf, $gpuValueOf, $internal, $ownSnippet, $resolve, } from "../../shared/symbols.js";
|
|
2
2
|
import { getName, setName } from "../../shared/meta.js";
|
|
3
3
|
import { textureExternal } from "../../data/texture.js";
|
|
4
4
|
import { valueProxyHandler } from "../valueProxyUtils.js";
|
|
5
5
|
import { inCodegenMode } from "../../execMode.js";
|
|
6
|
+
import { runtimeOnly } from "../../types.js";
|
|
6
7
|
import { snip } from "../../data/snippet.js";
|
|
7
8
|
export function isExternalTexture(value) {
|
|
8
9
|
return value?.resourceType === 'external-texture';
|
|
@@ -33,6 +34,7 @@ export class TgpuExternalTextureImpl {
|
|
|
33
34
|
get [$ownSnippet]() {
|
|
34
35
|
return snip(this, schema, 'handle', false);
|
|
35
36
|
},
|
|
37
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
36
38
|
[$resolve]: (ctx) => ctx.resolve(this),
|
|
37
39
|
toString: () => `textureExternal:${getName(this) ?? '<unnamed>'}.$`,
|
|
38
40
|
}, valueProxyHandler);
|
|
@@ -9,7 +9,7 @@ import type { TgpuDeviceOwningSoul, TgpuSoul } from '../../shared/soul.ts';
|
|
|
9
9
|
import { $gpuValueOf, $internal, $repr, $resolve, $soul } from '../../shared/symbols.ts';
|
|
10
10
|
import type { Default, TypedArray, UnionToIntersection } from '../../shared/utilityTypes.ts';
|
|
11
11
|
import type { LayoutMembership } from '../../tgpuBindGroupLayout.ts';
|
|
12
|
-
import type
|
|
12
|
+
import { type ResolutionCtx, type 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';
|
package/core/texture/texture.js
CHANGED
|
@@ -3,7 +3,8 @@ import { inCodegenMode } from "../../execMode.js";
|
|
|
3
3
|
import { snip } from "../../data/snippet.js";
|
|
4
4
|
import { getName, setName } from "../../shared/meta.js";
|
|
5
5
|
import { getTextureFormatInfo, } from "./textureFormats.js";
|
|
6
|
-
import { $gpuValueOf, $internal, $ownSnippet, $repr, $resolve, $soul, } from "../../shared/symbols.js";
|
|
6
|
+
import { $gpuValueOf, $internal, $ownSnippet, $comptimeValueOf, $repr, $resolve, $soul, } from "../../shared/symbols.js";
|
|
7
|
+
import { runtimeOnly } from "../../types.js";
|
|
7
8
|
import { valueProxyHandler } from "../valueProxyUtils.js";
|
|
8
9
|
import { clearTextureWithColor, generateTextureMipmaps, writeImages } from "./textureUtils.js";
|
|
9
10
|
import { mipLevelSize, origin3d, resolveImageWrite, writeRegionSize, } from "./textureWrite.js";
|
|
@@ -313,6 +314,7 @@ class TgpuFixedTextureViewImpl {
|
|
|
313
314
|
get [$ownSnippet]() {
|
|
314
315
|
return snip(this, schema, /* origin */ 'handle', false);
|
|
315
316
|
},
|
|
317
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
316
318
|
[$resolve]: (ctx) => ctx.resolve(this),
|
|
317
319
|
toString: () => `${this.toString()}.$`,
|
|
318
320
|
}, valueProxyHandler);
|
|
@@ -375,6 +377,7 @@ export class TgpuLaidOutTextureViewImpl {
|
|
|
375
377
|
get [$ownSnippet]() {
|
|
376
378
|
return snip(this, schema, /* origin */ 'handle', false);
|
|
377
379
|
},
|
|
380
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
378
381
|
[$resolve]: (ctx) => ctx.resolve(this),
|
|
379
382
|
toString: () => `${this.toString()}.$`,
|
|
380
383
|
}, valueProxyHandler);
|
|
@@ -405,10 +408,14 @@ export class TgpuTextureRenderViewImpl {
|
|
|
405
408
|
};
|
|
406
409
|
this[$internal] = {
|
|
407
410
|
unwrap: () => {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
411
|
+
const soul = this[$soul];
|
|
412
|
+
if (!soul.raw) {
|
|
413
|
+
soul.raw = soul.texture[$internal].materialize().createView({
|
|
414
|
+
label: getName(this) ?? '<unnamed>',
|
|
415
|
+
...soul.descriptor,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
return soul.raw;
|
|
412
419
|
},
|
|
413
420
|
format: descriptor.format ?? baseTexture.props.format,
|
|
414
421
|
aspect: descriptor.aspect,
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { $internal, $resolve } from '../../shared/symbols.ts';
|
|
1
|
+
import { $comptimeValueOf, $internal, $resolve } from '../../shared/symbols.ts';
|
|
2
|
+
import { type ComptimeValue, type ResolutionCtx, type SelfResolvable, type WithComptimeValue } from '../../types.ts';
|
|
2
3
|
import { type ResolvedSnippet, type Snippet } from '../../data/snippet.ts';
|
|
3
|
-
import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
|
|
4
4
|
/**
|
|
5
5
|
* The result of calling `tgpu.unroll(...)`. The code responsible for
|
|
6
6
|
* generating shader code can check if the value of a snippet is
|
|
7
7
|
* an instance of `UnrollableIterable`, and act accordingly.
|
|
8
8
|
*/
|
|
9
|
-
export declare class UnrollableIterable implements SelfResolvable {
|
|
9
|
+
export declare class UnrollableIterable implements SelfResolvable, WithComptimeValue {
|
|
10
10
|
readonly [$internal] = true;
|
|
11
11
|
readonly snippet: Snippet;
|
|
12
12
|
constructor(snippet: Snippet);
|
|
13
|
+
[$comptimeValueOf](): ComptimeValue;
|
|
13
14
|
[$resolve](ctx: ResolutionCtx): ResolvedSnippet;
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { $gpuCallable, $internal, $resolve } from "../../shared/symbols.js";
|
|
1
|
+
import { $comptimeValueOf, $gpuCallable, $internal, $resolve } from "../../shared/symbols.js";
|
|
2
2
|
import { setName } from "../../shared/meta.js";
|
|
3
|
+
import { asComptime, } from "../../types.js";
|
|
3
4
|
import { withValue } from "../../data/snippet.js";
|
|
4
5
|
/**
|
|
5
6
|
* The result of calling `tgpu.unroll(...)`. The code responsible for
|
|
@@ -12,6 +13,11 @@ export class UnrollableIterable {
|
|
|
12
13
|
constructor(snippet) {
|
|
13
14
|
this.snippet = snippet;
|
|
14
15
|
}
|
|
16
|
+
[$comptimeValueOf]() {
|
|
17
|
+
// Outside of for-of loops, `tgpu.unroll(...)` is transparent, just like in JS
|
|
18
|
+
const known = asComptime(this.snippet);
|
|
19
|
+
return known ? { comptime: true, value: known.value } : { comptime: false };
|
|
20
|
+
}
|
|
15
21
|
[$resolve](ctx) {
|
|
16
22
|
return ctx.resolveSnippet(this.snippet);
|
|
17
23
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import { type SelfResolvable, type WithOwnSnippet } from '../types.ts';
|
|
2
|
-
|
|
1
|
+
import { type SelfResolvable, type WithComptimeValue, type WithOwnSnippet } from '../types.ts';
|
|
2
|
+
type ValueProxyTarget = SelfResolvable & WithOwnSnippet & WithComptimeValue;
|
|
3
|
+
export declare const valueProxyHandler: ProxyHandler<ValueProxyTarget>;
|
|
3
4
|
export declare function getGpuValueRecursively<T>(value: unknown): T;
|
|
5
|
+
export {};
|