typegpu 0.11.4 → 0.11.5
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/README.md +4 -2
- package/core/buffer/bufferUsage.js +2 -2
- package/core/constant/tgpuConstant.js +1 -1
- package/core/function/fnCore.js +12 -6
- package/core/pipeline/computePipeline.js +1 -1
- package/core/pipeline/renderPipeline.js +1 -1
- package/core/resolve/namespace.d.ts +2 -11
- package/core/resolve/namespace.js +7 -24
- package/core/resolve/resolveData.js +3 -2
- package/core/resolve/tgpuResolve.js +1 -1
- package/core/sampler/sampler.js +2 -2
- package/core/simulate/tgpuSimulate.js +1 -1
- package/core/texture/externalTexture.js +1 -1
- package/core/texture/texture.js +2 -2
- package/core/variable/tgpuVariable.js +1 -1
- package/data/autoStruct.js +3 -2
- package/data/dataTypes.d.ts +1 -1
- package/data/struct.js +3 -2
- package/data/wgslTypes.d.ts +16 -16
- package/{nameRegistry.js → nameUtils.js} +46 -90
- package/package.js +1 -1
- package/package.json +1 -1
- package/resolutionCtx.js +63 -28
- package/std/copy.d.ts +7 -0
- package/std/copy.js +27 -0
- package/std/index.d.ts +3 -2
- package/std/index.js +3 -1
- package/tgpuUnstable.js +1 -1
- package/tgsl/conversion.js +4 -1
- package/tgsl/wgslGenerator.d.ts +20 -2
- package/tgsl/wgslGenerator.js +84 -25
- package/types.d.ts +29 -2
- package/nameRegistry.d.ts +0 -30
package/package.js
CHANGED
package/package.json
CHANGED
package/resolutionCtx.js
CHANGED
|
@@ -12,12 +12,12 @@ import { safeStringify } from "./shared/stringify.js";
|
|
|
12
12
|
import { getBestConversion } from "./tgsl/conversion.js";
|
|
13
13
|
import { bool } from "./data/numeric.js";
|
|
14
14
|
import { coerceToSnippet, concretize, numericLiteralToSnippet } from "./tgsl/generationHelpers.js";
|
|
15
|
+
import { sanitizePrimer, validateIdentifier } from "./nameUtils.js";
|
|
15
16
|
import { createIoSchema } from "./core/function/ioSchema.js";
|
|
16
17
|
import { AutoStruct } from "./data/autoStruct.js";
|
|
17
18
|
import { EntryInputRouter } from "./core/function/entryInputRouter.js";
|
|
18
19
|
import { accessProp } from "./tgsl/accessProp.js";
|
|
19
20
|
import { isTgpuFn } from "./core/function/tgpuFn.js";
|
|
20
|
-
import { getUniqueName } from "./core/resolve/namespace.js";
|
|
21
21
|
import { ConfigurableImpl } from "./core/root/configurableImpl.js";
|
|
22
22
|
import { naturalsExcept } from "./shared/generators.js";
|
|
23
23
|
import { TgpuBindGroupImpl, bindGroupLayout } from "./tgpuBindGroupLayout.js";
|
|
@@ -50,6 +50,9 @@ var ItemStateStackImpl = class {
|
|
|
50
50
|
get topFunctionScope() {
|
|
51
51
|
return this._stack.findLast((e) => e.type === "functionScope");
|
|
52
52
|
}
|
|
53
|
+
get topBlockScope() {
|
|
54
|
+
return this._stack.findLast((e) => e.type === "blockScope");
|
|
55
|
+
}
|
|
53
56
|
pushItem() {
|
|
54
57
|
this._itemDepth++;
|
|
55
58
|
this._stack.push({
|
|
@@ -70,7 +73,9 @@ var ItemStateStackImpl = class {
|
|
|
70
73
|
argAccess,
|
|
71
74
|
returnType,
|
|
72
75
|
externalMap,
|
|
73
|
-
reportedReturnTypes: /* @__PURE__ */ new Set()
|
|
76
|
+
reportedReturnTypes: /* @__PURE__ */ new Set(),
|
|
77
|
+
placeholderForVariable: /* @__PURE__ */ new Map(),
|
|
78
|
+
modifiedVariables: /* @__PURE__ */ new Set()
|
|
74
79
|
};
|
|
75
80
|
this._stack.push(scope);
|
|
76
81
|
return scope;
|
|
@@ -78,6 +83,7 @@ var ItemStateStackImpl = class {
|
|
|
78
83
|
pushBlockScope() {
|
|
79
84
|
this._stack.push({
|
|
80
85
|
type: "blockScope",
|
|
86
|
+
takenLocalIdentifiers: /* @__PURE__ */ new Set(),
|
|
81
87
|
declarations: /* @__PURE__ */ new Map(),
|
|
82
88
|
externals: /* @__PURE__ */ new Map()
|
|
83
89
|
});
|
|
@@ -116,6 +122,16 @@ var ItemStateStackImpl = class {
|
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
124
|
}
|
|
125
|
+
isIdentifierTakenLocally(id) {
|
|
126
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
127
|
+
const layer = this._stack[i];
|
|
128
|
+
if (layer?.type === "functionScope") return false;
|
|
129
|
+
if (layer?.type === "blockScope") {
|
|
130
|
+
if (layer.takenLocalIdentifiers.has(id)) return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
119
135
|
defineBlockVariable(id, snippet) {
|
|
120
136
|
if (snippet.dataType === UnknownData) throw Error(`Tried to define variable '${id}' of unknown type`);
|
|
121
137
|
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
@@ -232,17 +248,41 @@ var ResolutionCtxImpl = class {
|
|
|
232
248
|
fixedBindings = [];
|
|
233
249
|
enableExtensions;
|
|
234
250
|
expectedType;
|
|
251
|
+
/**
|
|
252
|
+
* A counter used to generate unique identifiers for globally-scoped definitions in the 'random' strategy.
|
|
253
|
+
*/
|
|
254
|
+
#lastUniqueId = 0;
|
|
235
255
|
constructor(opts) {
|
|
236
256
|
this.enableExtensions = opts.enableExtensions;
|
|
237
257
|
this.gen = opts.shaderGenerator ?? wgslGenerator_default;
|
|
238
258
|
this.#logGenerator = opts.root ? new LogGeneratorImpl(opts.root) : new LogGeneratorNullImpl();
|
|
239
259
|
this.#namespaceInternal = opts.namespace[$internal];
|
|
240
260
|
}
|
|
241
|
-
|
|
242
|
-
return
|
|
261
|
+
isIdentifierTaken(name) {
|
|
262
|
+
return this.#namespaceInternal.takenGlobalIdentifiers.has(name) || this._itemStateStack.isIdentifierTakenLocally(name);
|
|
243
263
|
}
|
|
244
|
-
|
|
245
|
-
|
|
264
|
+
makeUniqueIdentifier(primer = "item", scope) {
|
|
265
|
+
if (scope === "block" && (/* @__PURE__ */ validateIdentifier(primer)).success && !this.isIdentifierTaken(primer)) {
|
|
266
|
+
this.reserveIdentifier(primer, "block");
|
|
267
|
+
return primer;
|
|
268
|
+
}
|
|
269
|
+
const base = /* @__PURE__ */ sanitizePrimer(primer);
|
|
270
|
+
let index = 0;
|
|
271
|
+
const random = this.#namespaceInternal.strategy === "random";
|
|
272
|
+
let name = random ? `${base}_${this.#lastUniqueId++}` : base;
|
|
273
|
+
while (this.isIdentifierTaken(name)) name = random ? `${base}_${this.#lastUniqueId++}` : `${base}_${++index}`;
|
|
274
|
+
this.reserveIdentifier(name, scope);
|
|
275
|
+
return name;
|
|
276
|
+
}
|
|
277
|
+
reserveIdentifier(name, scope) {
|
|
278
|
+
if (scope === "block") {
|
|
279
|
+
const blockScope = this._itemStateStack.topBlockScope;
|
|
280
|
+
if (blockScope) {
|
|
281
|
+
blockScope.takenLocalIdentifiers.add(name);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
this.#namespaceInternal.takenGlobalIdentifiers.add(name);
|
|
246
286
|
}
|
|
247
287
|
get pre() {
|
|
248
288
|
return this._indentController.pre;
|
|
@@ -281,11 +321,9 @@ var ResolutionCtxImpl = class {
|
|
|
281
321
|
scope.reportedReturnTypes.add(dataType);
|
|
282
322
|
}
|
|
283
323
|
pushBlockScope() {
|
|
284
|
-
this.#namespaceInternal.nameRegistry.pushBlockScope();
|
|
285
324
|
this._itemStateStack.pushBlockScope();
|
|
286
325
|
}
|
|
287
326
|
popBlockScope() {
|
|
288
|
-
this.#namespaceInternal.nameRegistry.popBlockScope();
|
|
289
327
|
this._itemStateStack.pop("blockScope");
|
|
290
328
|
}
|
|
291
329
|
setBlockExternals(externals) {
|
|
@@ -301,28 +339,27 @@ var ResolutionCtxImpl = class {
|
|
|
301
339
|
return this.#logGenerator.logResources;
|
|
302
340
|
}
|
|
303
341
|
fnToWgsl(options) {
|
|
304
|
-
let fnScopePushed = false;
|
|
305
342
|
try {
|
|
306
|
-
this
|
|
343
|
+
const scope = this._itemStateStack.pushFunctionScope(options.functionType, {}, options.returnType, options.externalMap);
|
|
344
|
+
this._itemStateStack.pushBlockScope();
|
|
307
345
|
const args = [];
|
|
308
|
-
const argAccess = {};
|
|
309
346
|
if (options.entryInput) {
|
|
310
347
|
const { dataSchema, positionalArgs } = options.entryInput;
|
|
311
348
|
const firstParam = options.params[0];
|
|
312
|
-
const structArg = dataSchema ? createArgument(this.
|
|
349
|
+
const structArg = dataSchema ? createArgument(this.makeUniqueIdentifier("_arg_0", "block"), dataSchema) : void 0;
|
|
313
350
|
if (structArg) args.push(structArg);
|
|
314
351
|
if (firstParam?.type === FuncParameterType.destructuredObject) for (const { name, alias } of firstParam.props) {
|
|
315
352
|
const argInfo = positionalArgs.find((a) => a.schemaKey === name);
|
|
316
353
|
if (argInfo) {
|
|
317
|
-
const arg = createArgument(this.
|
|
354
|
+
const arg = createArgument(this.makeUniqueIdentifier(alias, "block"), argInfo.type);
|
|
318
355
|
args.push(arg);
|
|
319
|
-
argAccess[alias] = arg.access;
|
|
320
|
-
} else if (structArg) argAccess[alias] = createArgumentPropAccess(structArg.access, name);
|
|
356
|
+
scope.argAccess[alias] = arg.access;
|
|
357
|
+
} else if (structArg) scope.argAccess[alias] = createArgumentPropAccess(structArg.access, name);
|
|
321
358
|
}
|
|
322
359
|
else if (firstParam?.type === FuncParameterType.identifier) {
|
|
323
360
|
const proxyEntries = [];
|
|
324
361
|
for (const a of positionalArgs) {
|
|
325
|
-
const arg = createArgument(this.
|
|
362
|
+
const arg = createArgument(this.makeUniqueIdentifier(`_arg_${a.schemaKey}`, "block"), a.type);
|
|
326
363
|
args.push(arg);
|
|
327
364
|
proxyEntries.push({
|
|
328
365
|
schemaKey: a.schemaKey,
|
|
@@ -330,31 +367,31 @@ var ResolutionCtxImpl = class {
|
|
|
330
367
|
});
|
|
331
368
|
}
|
|
332
369
|
const router = new EntryInputRouter(structArg?.access, proxyEntries);
|
|
333
|
-
argAccess[firstParam.name] = () => snip("N/A", router, "argument");
|
|
370
|
+
scope.argAccess[firstParam.name] = () => snip("N/A", router, "argument");
|
|
334
371
|
} else for (const a of positionalArgs) {
|
|
335
|
-
const argName = this.
|
|
372
|
+
const argName = this.makeUniqueIdentifier(`_arg_${a.schemaKey}`, "block");
|
|
336
373
|
const arg = createArgument(argName, a.type);
|
|
337
374
|
args.push(arg);
|
|
338
|
-
argAccess[argName] = arg.access;
|
|
375
|
+
scope.argAccess[argName] = arg.access;
|
|
339
376
|
}
|
|
340
377
|
} else for (const [i, argType] of options.argTypes.entries()) {
|
|
341
378
|
const astParam = options.params[i];
|
|
342
379
|
const origin = isPtr(argType) ? argType.addressSpace === "storage" ? argType.access === "read" ? "readonly" : "mutable" : argType.addressSpace : "argument";
|
|
343
380
|
switch (astParam?.type) {
|
|
344
381
|
case FuncParameterType.identifier: {
|
|
345
|
-
const arg = createArgument(this.
|
|
382
|
+
const arg = createArgument(this.makeUniqueIdentifier(astParam.name, "block"), argType, origin);
|
|
346
383
|
args.push(arg);
|
|
347
|
-
argAccess[astParam.name] = arg.access;
|
|
384
|
+
scope.argAccess[astParam.name] = arg.access;
|
|
348
385
|
break;
|
|
349
386
|
}
|
|
350
387
|
case FuncParameterType.destructuredObject: {
|
|
351
|
-
const objArg = createArgument(this.
|
|
388
|
+
const objArg = createArgument(this.makeUniqueIdentifier(`_arg_${i}`, "block"), argType, origin);
|
|
352
389
|
args.push(objArg);
|
|
353
|
-
for (const { name, alias } of astParam.props) argAccess[alias] = createArgumentPropAccess(objArg.access, name);
|
|
390
|
+
for (const { name, alias } of astParam.props) scope.argAccess[alias] = createArgumentPropAccess(objArg.access, name);
|
|
354
391
|
break;
|
|
355
392
|
}
|
|
356
393
|
case void 0: if (!(argType instanceof AutoStruct)) args.push({
|
|
357
|
-
name: this.
|
|
394
|
+
name: this.makeUniqueIdentifier(`_arg_${i}`, "block"),
|
|
358
395
|
access: () => {
|
|
359
396
|
throw new Error(`Unreachable: Accessing an argument that wasn't named in the function signature`);
|
|
360
397
|
},
|
|
@@ -363,8 +400,6 @@ var ResolutionCtxImpl = class {
|
|
|
363
400
|
});
|
|
364
401
|
}
|
|
365
402
|
}
|
|
366
|
-
const scope = this._itemStateStack.pushFunctionScope(options.functionType, argAccess, options.returnType, options.externalMap);
|
|
367
|
-
fnScopePushed = true;
|
|
368
403
|
let returnType;
|
|
369
404
|
const code = this.gen.functionDefinition({
|
|
370
405
|
functionType: options.functionType,
|
|
@@ -395,8 +430,8 @@ var ResolutionCtxImpl = class {
|
|
|
395
430
|
returnType
|
|
396
431
|
};
|
|
397
432
|
} finally {
|
|
398
|
-
|
|
399
|
-
this
|
|
433
|
+
this._itemStateStack.pop("blockScope");
|
|
434
|
+
this._itemStateStack.pop("functionScope");
|
|
400
435
|
}
|
|
401
436
|
}
|
|
402
437
|
addDeclaration(declaration) {
|
package/std/copy.d.ts
ADDED
package/std/copy.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { WORKAROUND_getSchema, isMatInstance, isVecInstance } from "../data/wgslTypes.js";
|
|
2
|
+
import { stitch } from "../core/resolve/stitch.js";
|
|
3
|
+
import { dualImpl } from "../core/function/dualImpl.js";
|
|
4
|
+
|
|
5
|
+
//#region src/std/copy.ts
|
|
6
|
+
function cpuCopy(e) {
|
|
7
|
+
if (isVecInstance(e) || isMatInstance(e)) return WORKAROUND_getSchema(e)(e);
|
|
8
|
+
if (Array.isArray(e)) return e.map(cpuCopy);
|
|
9
|
+
if (typeof e === "object" && e !== null) return Object.fromEntries(Object.entries(e).map(([key, value]) => [key, cpuCopy(value)]));
|
|
10
|
+
return e;
|
|
11
|
+
}
|
|
12
|
+
const copy = dualImpl({
|
|
13
|
+
name: "copy",
|
|
14
|
+
signature: (arg) => {
|
|
15
|
+
return {
|
|
16
|
+
argTypes: [arg],
|
|
17
|
+
returnType: arg
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
normalImpl: cpuCopy,
|
|
21
|
+
codegenImpl(_ctx, [a]) {
|
|
22
|
+
return stitch`${a}`;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { copy };
|
package/std/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { identity2, identity3, identity4, rotationX4, rotationY4, rotationZ4, scaling4, translation4 } from "../data/matrix.js";
|
|
2
|
+
import { copy } from "./copy.js";
|
|
2
3
|
import { discard } from "./discard.js";
|
|
3
4
|
import { abs, acos, acosh, asin, asinh, atan, atan2, atanh, ceil, clamp, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, degrees, determinant, distance, dot, dot4I8Packed, dot4U8Packed, exp, exp2, extractBits, faceForward, firstLeadingBit, firstTrailingBit, floor, fma, fract, frexp, insertBits, inverseSqrt, ldexp, length, log, log2, max, min, mix, modf, normalize, pow, quantizeToF16, radians, reflect, refract, reverseBits, round, saturate, sign, sin, sinh, smoothstep, sqrt, step, tan, tanh, transpose, trunc } from "./numeric.js";
|
|
4
5
|
import { add, bitShiftLeft, bitShiftRight, div, mod, mul, neg, sub } from "./operators.js";
|
|
@@ -16,7 +17,7 @@ import { range } from "./range.js";
|
|
|
16
17
|
|
|
17
18
|
//#region src/std/index.d.ts
|
|
18
19
|
declare namespace index_d_exports {
|
|
19
|
-
export { abs, acos, acosh, add, all, allEq, and, any, arrayLength, asin, asinh, atan, atan2, atanh, atomicAdd, atomicAnd, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, bitShiftLeft, bitShiftRight, bitcastU32toF32, bitcastU32toI32, ceil, clamp, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, degrees, determinant, discard, distance, div, dot, dot4I8Packed, dot4U8Packed, dpdx, dpdxCoarse, dpdxFine, dpdy, dpdyCoarse, dpdyFine, eq, exp, exp2, extensionEnabled, extractBits, faceForward, firstLeadingBit, firstTrailingBit, floor, fma, fract, frexp, fwidth, fwidthCoarse, fwidthFine, ge, gt, identity2, identity3, identity4, insertBits, inverseSqrt, isCloseTo, ldexp, le, length, log, log2, lt, max, min, mix, mod, modf, mul, ne, neg, normalize, not, or, pack2x16float, pack4x8unorm, pow, quantizeToF16, radians, range, reflect, refract, reverseBits, rotateX4, rotateY4, rotateZ4, rotationX4, rotationY4, rotationZ4, round, saturate, scale4, scaling4, select, sign, sin, sinh, smoothstep, sqrt, step, storageBarrier, sub, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupXor, tan, tanh, textureBarrier, textureDimensions, textureGather, textureLoad, textureSample, textureSampleBaseClampToEdge, textureSampleBias, textureSampleCompare, textureSampleCompareLevel, textureSampleGrad, textureSampleLevel, textureStore, translate4, translation4, transpose, trunc, unpack2x16float, unpack4x8unorm, workgroupBarrier };
|
|
20
|
+
export { abs, acos, acosh, add, all, allEq, and, any, arrayLength, asin, asinh, atan, atan2, atanh, atomicAdd, atomicAnd, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, bitShiftLeft, bitShiftRight, bitcastU32toF32, bitcastU32toI32, ceil, clamp, copy, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, degrees, determinant, discard, distance, div, dot, dot4I8Packed, dot4U8Packed, dpdx, dpdxCoarse, dpdxFine, dpdy, dpdyCoarse, dpdyFine, eq, exp, exp2, extensionEnabled, extractBits, faceForward, firstLeadingBit, firstTrailingBit, floor, fma, fract, frexp, fwidth, fwidthCoarse, fwidthFine, ge, gt, identity2, identity3, identity4, insertBits, inverseSqrt, isCloseTo, ldexp, le, length, log, log2, lt, max, min, mix, mod, modf, mul, ne, neg, normalize, not, or, pack2x16float, pack4x8unorm, pow, quantizeToF16, radians, range, reflect, refract, reverseBits, rotateX4, rotateY4, rotateZ4, rotationX4, rotationY4, rotationZ4, round, saturate, scale4, scaling4, select, sign, sin, sinh, smoothstep, sqrt, step, storageBarrier, sub, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupXor, tan, tanh, textureBarrier, textureDimensions, textureGather, textureLoad, textureSample, textureSampleBaseClampToEdge, textureSampleBias, textureSampleCompare, textureSampleCompareLevel, textureSampleGrad, textureSampleLevel, textureStore, translate4, translation4, transpose, trunc, unpack2x16float, unpack4x8unorm, workgroupBarrier };
|
|
20
21
|
}
|
|
21
22
|
//#endregion
|
|
22
|
-
export { abs, acos, acosh, add, all, allEq, and, any, arrayLength, asin, asinh, atan, atan2, atanh, atomicAdd, atomicAnd, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, bitShiftLeft, bitShiftRight, bitcastU32toF32, bitcastU32toI32, ceil, clamp, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, degrees, determinant, discard, distance, div, dot, dot4I8Packed, dot4U8Packed, dpdx, dpdxCoarse, dpdxFine, dpdy, dpdyCoarse, dpdyFine, eq, exp, exp2, extensionEnabled, extractBits, faceForward, firstLeadingBit, firstTrailingBit, floor, fma, fract, frexp, fwidth, fwidthCoarse, fwidthFine, ge, gt, identity2, identity3, identity4, index_d_exports, insertBits, inverseSqrt, isCloseTo, ldexp, le, length, log, log2, lt, max, min, mix, mod, modf, mul, ne, neg, normalize, not, or, pack2x16float, pack4x8unorm, pow, quantizeToF16, radians, range, reflect, refract, reverseBits, rotateX4, rotateY4, rotateZ4, rotationX4, rotationY4, rotationZ4, round, saturate, scale4, scaling4, select, sign, sin, sinh, smoothstep, sqrt, step, storageBarrier, sub, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupXor, tan, tanh, textureBarrier, textureDimensions, textureGather, textureLoad, textureSample, textureSampleBaseClampToEdge, textureSampleBias, textureSampleCompare, textureSampleCompareLevel, textureSampleGrad, textureSampleLevel, textureStore, translate4, translation4, transpose, trunc, unpack2x16float, unpack4x8unorm, workgroupBarrier };
|
|
23
|
+
export { abs, acos, acosh, add, all, allEq, and, any, arrayLength, asin, asinh, atan, atan2, atanh, atomicAdd, atomicAnd, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, bitShiftLeft, bitShiftRight, bitcastU32toF32, bitcastU32toI32, ceil, clamp, copy, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, degrees, determinant, discard, distance, div, dot, dot4I8Packed, dot4U8Packed, dpdx, dpdxCoarse, dpdxFine, dpdy, dpdyCoarse, dpdyFine, eq, exp, exp2, extensionEnabled, extractBits, faceForward, firstLeadingBit, firstTrailingBit, floor, fma, fract, frexp, fwidth, fwidthCoarse, fwidthFine, ge, gt, identity2, identity3, identity4, index_d_exports, insertBits, inverseSqrt, isCloseTo, ldexp, le, length, log, log2, lt, max, min, mix, mod, modf, mul, ne, neg, normalize, not, or, pack2x16float, pack4x8unorm, pow, quantizeToF16, radians, range, reflect, refract, reverseBits, rotateX4, rotateY4, rotateZ4, rotationX4, rotationY4, rotationZ4, round, saturate, scale4, scaling4, select, sign, sin, sinh, smoothstep, sqrt, step, storageBarrier, sub, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupXor, tan, tanh, textureBarrier, textureDimensions, textureGather, textureLoad, textureSample, textureSampleBaseClampToEdge, textureSampleBias, textureSampleCompare, textureSampleCompareLevel, textureSampleGrad, textureSampleLevel, textureStore, translate4, translation4, transpose, trunc, unpack2x16float, unpack4x8unorm, workgroupBarrier };
|
package/std/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { range } from "./range.js";
|
|
|
7
7
|
import { bitcastU32toF32, bitcastU32toI32 } from "./bitcast.js";
|
|
8
8
|
import { pack2x16float, pack4x8unorm, unpack2x16float, unpack4x8unorm } from "./packing.js";
|
|
9
9
|
import { all, allEq, and, any, eq, ge, gt, isCloseTo, le, lt, ne, not, or, select } from "./boolean.js";
|
|
10
|
+
import { copy } from "./copy.js";
|
|
10
11
|
import { discard } from "./discard.js";
|
|
11
12
|
import { rotateX4, rotateY4, rotateZ4, scale4, translate4 } from "./matrix.js";
|
|
12
13
|
import { atomicAdd, atomicAnd, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, storageBarrier, textureBarrier, workgroupBarrier } from "./atomic.js";
|
|
@@ -46,6 +47,7 @@ var std_exports = /* @__PURE__ */ __export({
|
|
|
46
47
|
bitcastU32toI32: () => bitcastU32toI32,
|
|
47
48
|
ceil: () => ceil,
|
|
48
49
|
clamp: () => clamp,
|
|
50
|
+
copy: () => copy,
|
|
49
51
|
cos: () => cos,
|
|
50
52
|
cosh: () => cosh,
|
|
51
53
|
countLeadingZeros: () => countLeadingZeros,
|
|
@@ -179,4 +181,4 @@ var std_exports = /* @__PURE__ */ __export({
|
|
|
179
181
|
});
|
|
180
182
|
|
|
181
183
|
//#endregion
|
|
182
|
-
export { abs, acos, acosh, add, all, allEq, and, any, arrayLength, asin, asinh, atan, atan2, atanh, atomicAdd, atomicAnd, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, bitShiftLeft, bitShiftRight, bitcastU32toF32, bitcastU32toI32, ceil, clamp, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, degrees, determinant, discard, distance, div, dot, dot4I8Packed, dot4U8Packed, dpdx, dpdxCoarse, dpdxFine, dpdy, dpdyCoarse, dpdyFine, eq, exp, exp2, extensionEnabled, extractBits, faceForward, firstLeadingBit, firstTrailingBit, floor, fma, fract, frexp, fwidth, fwidthCoarse, fwidthFine, ge, gt, identity2, identity3, identity4, insertBits, inverseSqrt, isCloseTo, ldexp, le, length, log, log2, lt, max, min, mix, mod, modf, mul, ne, neg, normalize, not, or, pack2x16float, pack4x8unorm, pow, quantizeToF16, radians, range, reflect, refract, reverseBits, rotateX4, rotateY4, rotateZ4, rotationX4, rotationY4, rotationZ4, round, saturate, scale4, scaling4, select, sign, sin, sinh, smoothstep, sqrt, std_exports, step, storageBarrier, sub, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupXor, tan, tanh, textureBarrier, textureDimensions, textureGather, textureLoad, textureSample, textureSampleBaseClampToEdge, textureSampleBias, textureSampleCompare, textureSampleCompareLevel, textureSampleGrad, textureSampleLevel, textureStore, translate4, translation4, transpose, trunc, unpack2x16float, unpack4x8unorm, workgroupBarrier };
|
|
184
|
+
export { abs, acos, acosh, add, all, allEq, and, any, arrayLength, asin, asinh, atan, atan2, atanh, atomicAdd, atomicAnd, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, bitShiftLeft, bitShiftRight, bitcastU32toF32, bitcastU32toI32, ceil, clamp, copy, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, degrees, determinant, discard, distance, div, dot, dot4I8Packed, dot4U8Packed, dpdx, dpdxCoarse, dpdxFine, dpdy, dpdyCoarse, dpdyFine, eq, exp, exp2, extensionEnabled, extractBits, faceForward, firstLeadingBit, firstTrailingBit, floor, fma, fract, frexp, fwidth, fwidthCoarse, fwidthFine, ge, gt, identity2, identity3, identity4, insertBits, inverseSqrt, isCloseTo, ldexp, le, length, log, log2, lt, max, min, mix, mod, modf, mul, ne, neg, normalize, not, or, pack2x16float, pack4x8unorm, pow, quantizeToF16, radians, range, reflect, refract, reverseBits, rotateX4, rotateY4, rotateZ4, rotationX4, rotationY4, rotationZ4, round, saturate, scale4, scaling4, select, sign, sin, sinh, smoothstep, sqrt, std_exports, step, storageBarrier, sub, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupXor, tan, tanh, textureBarrier, textureDimensions, textureGather, textureLoad, textureSample, textureSampleBaseClampToEdge, textureSampleBias, textureSampleCompare, textureSampleCompareLevel, textureSampleGrad, textureSampleLevel, textureStore, translate4, translation4, transpose, trunc, unpack2x16float, unpack4x8unorm, workgroupBarrier };
|
package/tgpuUnstable.js
CHANGED
|
@@ -2,9 +2,9 @@ import { __export } from "./_virtual/rolldown_runtime.js";
|
|
|
2
2
|
import { comptime } from "./core/function/comptime.js";
|
|
3
3
|
import { constant } from "./core/constant/tgpuConstant.js";
|
|
4
4
|
import { fn } from "./core/function/tgpuFn.js";
|
|
5
|
-
import { namespace } from "./core/resolve/namespace.js";
|
|
6
5
|
import { slot } from "./core/slot/slot.js";
|
|
7
6
|
import { privateVar, workgroupVar } from "./core/variable/tgpuVariable.js";
|
|
7
|
+
import { namespace } from "./core/resolve/namespace.js";
|
|
8
8
|
import { computeFn } from "./core/function/tgpuComputeFn.js";
|
|
9
9
|
import { vertexLayout } from "./core/vertexLayout/vertexLayout.js";
|
|
10
10
|
import { lazy } from "./core/slot/lazy.js";
|
package/tgsl/conversion.js
CHANGED
|
@@ -158,7 +158,10 @@ function getBestConversion(types, targetTypes) {
|
|
|
158
158
|
if (implicitResult) return implicitResult;
|
|
159
159
|
}
|
|
160
160
|
function applyActionToSnippet(ctx, snippet, action, targetType) {
|
|
161
|
-
if (action.action === "none")
|
|
161
|
+
if (action.action === "none") {
|
|
162
|
+
if (targetType === snippet.dataType) return snippet;
|
|
163
|
+
return snip(snippet.value, targetType, snippet.origin);
|
|
164
|
+
}
|
|
162
165
|
switch (action.action) {
|
|
163
166
|
case "ref": return snip(new RefOperator(snippet, targetType), targetType, snippet.origin);
|
|
164
167
|
case "deref": return derefSnippet(snippet);
|
package/tgsl/wgslGenerator.d.ts
CHANGED
|
@@ -13,9 +13,14 @@ declare class WgslGenerator implements ShaderGenerator {
|
|
|
13
13
|
initGenerator(ctx: GenerationCtx): void;
|
|
14
14
|
protected get ctx(): GenerationCtx;
|
|
15
15
|
_block([_, statements]: tinyest.Block, externalMap?: ExternalMap): string;
|
|
16
|
+
_blockStatement(block: tinyest.Block, externalMap?: ExternalMap): string;
|
|
16
17
|
refVariable(id: string, dataType: StorableData): string;
|
|
17
|
-
blockVariable(varType: 'var' | 'let' | 'const', id: string, dataType: BaseData | UnknownData, origin: Origin): Snippet;
|
|
18
|
-
|
|
18
|
+
blockVariable(varType: 'var' | 'let' | 'const' | '<deferred>', id: string, dataType: BaseData | UnknownData, origin: Origin): Snippet;
|
|
19
|
+
/**
|
|
20
|
+
* Creates a variable declaration string.
|
|
21
|
+
* `keyword` may be a placeholder filled in later.
|
|
22
|
+
*/
|
|
23
|
+
protected emitVarDecl(pre: string, keyword: 'var' | 'let' | 'const' | `#VAR_${number}#`, name: string, _dataType: BaseData | UnknownData, rhsStr: string): string;
|
|
19
24
|
_identifier(id: string): Snippet;
|
|
20
25
|
/**
|
|
21
26
|
* A wrapper for `generateExpression` that updates `ctx.expectedType`
|
|
@@ -33,6 +38,19 @@ declare class WgslGenerator implements ShaderGenerator {
|
|
|
33
38
|
typeInstantiation(schema: BaseData, args: readonly Snippet[]): ResolvedSnippet;
|
|
34
39
|
_return(statement: tinyest.Return): string;
|
|
35
40
|
_statement(statement: tinyest.Statement): string;
|
|
41
|
+
/**
|
|
42
|
+
* Attempts a member access lookup to mark a variable as modified.
|
|
43
|
+
* @example
|
|
44
|
+
* // given `let a; a = 1;`
|
|
45
|
+
* tryMarkModified('a') // `a` is marked in the function scope
|
|
46
|
+
*
|
|
47
|
+
* // given `const obj; obj.prop = 1;`
|
|
48
|
+
* tryMarkModified('obj.prop') // `obj` is marked in the function scope
|
|
49
|
+
*
|
|
50
|
+
* // given `this.buffer.$;`
|
|
51
|
+
* tryMarkModified('this.buffer.$') // `this` is not marked, since there is no placeholder for it
|
|
52
|
+
*/
|
|
53
|
+
private tryMarkModified;
|
|
36
54
|
}
|
|
37
55
|
//#endregion
|
|
38
56
|
export { WgslGenerator };
|
package/tgsl/wgslGenerator.js
CHANGED
|
@@ -7,7 +7,7 @@ import { ResolutionError, WgslTypeError, invariant } from "../errors.js";
|
|
|
7
7
|
import { isGPUCallable, isKnownAtComptime } from "../types.js";
|
|
8
8
|
import { stitch } from "../core/resolve/stitch.js";
|
|
9
9
|
import { createPtrFromOrigin, implicitFrom, ptrFn } from "../data/ptr.js";
|
|
10
|
-
import { RefOperator } from "../data/ref.js";
|
|
10
|
+
import { RefOperator, _ref } from "../data/ref.js";
|
|
11
11
|
import { safeStringify } from "../shared/stringify.js";
|
|
12
12
|
import { convertStructValues, convertToCommonType, tryConvertSnippet } from "./conversion.js";
|
|
13
13
|
import { bool, i32, u32 } from "../data/numeric.js";
|
|
@@ -23,7 +23,7 @@ import { isGenericFn } from "../core/function/tgpuFn.js";
|
|
|
23
23
|
import { arrayOf } from "../data/array.js";
|
|
24
24
|
import { pow } from "../std/numeric.js";
|
|
25
25
|
import { resolveData } from "../core/resolve/resolveData.js";
|
|
26
|
-
import { UnrollableIterable } from "../core/unroll/tgpuUnroll.js";
|
|
26
|
+
import { UnrollableIterable, unroll } from "../core/unroll/tgpuUnroll.js";
|
|
27
27
|
import { mathToStd, supportedLogOps } from "./jsPolyfills.js";
|
|
28
28
|
import { isTgpuRange } from "../std/range.js";
|
|
29
29
|
import { getElementSnippet, getElementType, getLoopVarKind, getRangeSnippets } from "./forOfUtils.js";
|
|
@@ -165,8 +165,11 @@ ${this.ctx.pre}}`;
|
|
|
165
165
|
this.ctx.popBlockScope();
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
+
_blockStatement(block, externalMap) {
|
|
169
|
+
return `${this.ctx.pre}${this._block(block, externalMap)}`;
|
|
170
|
+
}
|
|
168
171
|
refVariable(id, dataType) {
|
|
169
|
-
const varName = this.ctx.
|
|
172
|
+
const varName = this.ctx.makeUniqueIdentifier(id, "block");
|
|
170
173
|
const ptrType = ptrFn(dataType);
|
|
171
174
|
const snippet = snip(new RefOperator(snip(varName, dataType, "function"), ptrType), ptrType, "function");
|
|
172
175
|
this.ctx.defineVariable(id, snippet);
|
|
@@ -181,10 +184,14 @@ ${this.ctx.pre}}`;
|
|
|
181
184
|
else if (!naturallyEphemeral) varOrigin = isEphemeralOrigin(origin) ? "this-function" : origin;
|
|
182
185
|
else if (origin === "constant" && varType === "const") varOrigin = "constant";
|
|
183
186
|
else varOrigin = "runtime";
|
|
184
|
-
const snippet = snip(this.ctx.
|
|
187
|
+
const snippet = snip(this.ctx.makeUniqueIdentifier(id, "block"), dataType, varOrigin);
|
|
185
188
|
this.ctx.defineVariable(id, snippet);
|
|
186
189
|
return snippet;
|
|
187
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Creates a variable declaration string.
|
|
193
|
+
* `keyword` may be a placeholder filled in later.
|
|
194
|
+
*/
|
|
188
195
|
emitVarDecl(pre, keyword, name, _dataType, rhsStr) {
|
|
189
196
|
return `${pre}${keyword} ${name} = ${rhsStr};`;
|
|
190
197
|
}
|
|
@@ -252,22 +259,14 @@ ${this.ctx.pre}}`;
|
|
|
252
259
|
const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
|
|
253
260
|
const type = operatorToType(convLhs.dataType, op, convRhs.dataType);
|
|
254
261
|
if (exprType === NODE.assignmentExpr) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because the left side is a constant.`);
|
|
258
|
-
}
|
|
259
|
-
if (lhsExpr.origin === "argument") throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because non-pointer arguments cannot be mutated.`);
|
|
262
|
+
validateSnippetMutation(convLhs, expression);
|
|
263
|
+
this.tryMarkModified(lhs);
|
|
260
264
|
if (op === "=" && rhsExpr.origin === "argument" && !isNaturallyEphemeral(rhsExpr.dataType)) throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because argument references cannot be assigned.\n-----\nTry '${stringifyNode(lhs)} = ${this.ctx.resolve(rhsExpr.dataType).value}(${stringifyNode(rhs)})' to copy the value instead.\n-----`);
|
|
261
265
|
if (op === "=" && !isEphemeralSnippet(rhsExpr)) throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because references cannot be assigned.\n-----\nTry '${stringifyNode(lhs)} = ${this.ctx.resolve(rhsExpr.dataType).value}(${stringifyNode(rhs)})' to copy the value instead.\n-----`);
|
|
262
266
|
}
|
|
263
267
|
return snip(parenthesizedOps.includes(op) ? `(${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr})` : `${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr}`, type, "runtime");
|
|
264
268
|
}
|
|
265
|
-
if (expression[0] === NODE.postUpdate) {
|
|
266
|
-
const [_, op, arg] = expression;
|
|
267
|
-
const argExpr = this._expression(arg);
|
|
268
|
-
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
269
|
-
return snip(`${argStr}${op}`, argExpr.dataType, "runtime");
|
|
270
|
-
}
|
|
269
|
+
if (expression[0] === NODE.postUpdate) throw new Error(`'${stringifyNode(expression)}' is invalid because update is only allowed as a statement.`);
|
|
271
270
|
if (expression[0] === NODE.unaryExpr) {
|
|
272
271
|
const [_, op, arg] = expression;
|
|
273
272
|
const argExpr = this._expression(arg);
|
|
@@ -320,6 +319,7 @@ ${this.ctx.pre}}`;
|
|
|
320
319
|
const rhs = this._expression(argNodes[0]);
|
|
321
320
|
return callee.value.operator(this.ctx, [callee.value.lhs, rhs]);
|
|
322
321
|
}
|
|
322
|
+
if ((callee.value === _ref || callee.value === unroll) && argNodes[0]) this.tryMarkModified(argNodes[0]);
|
|
323
323
|
if (isGPUCallable(callee.value)) {
|
|
324
324
|
const callable = callee.value[$gpuCallable];
|
|
325
325
|
const strictSignature = callable.strictSignature;
|
|
@@ -423,7 +423,14 @@ ${this.ctx.pre}}`;
|
|
|
423
423
|
assertExhaustive(expression);
|
|
424
424
|
}
|
|
425
425
|
functionDefinition(options) {
|
|
426
|
-
|
|
426
|
+
let body = this._block(options.body);
|
|
427
|
+
const scope = this.ctx.topFunctionScope;
|
|
428
|
+
invariant(scope, "Expected function scope to be present");
|
|
429
|
+
const replacements = Object.fromEntries(scope.placeholderForVariable.entries().map(([variable, placeholder]) => [placeholder, scope.modifiedVariables.has(variable) ? "var" : "let"]));
|
|
430
|
+
if (Object.keys(replacements).length > 0) {
|
|
431
|
+
const regex = new RegExp(Object.keys(replacements).join("|"), "gi");
|
|
432
|
+
body = body.replace(regex, (match) => replacements[match] ?? "#ERR");
|
|
433
|
+
}
|
|
427
434
|
const returnType = options.determineReturnType();
|
|
428
435
|
const argList = options.args.filter((arg) => arg.used || options.functionType === "normal").map((arg) => {
|
|
429
436
|
return `${getAttributesString(arg.decoratedType)}${arg.name}: ${this.ctx.resolve(arg.decoratedType).value}`;
|
|
@@ -481,7 +488,7 @@ Try 'return ${typeStr}(${str});' instead.
|
|
|
481
488
|
if (!Array.isArray(node)) node = blockifySingleStatement(node);
|
|
482
489
|
if (node[0] === NODE.block && node[1].length === 1 && node[1][0][0] === NODE.if) return this._statement(node[1][0]);
|
|
483
490
|
if (node[0] === NODE.if) return this._statement(node);
|
|
484
|
-
return
|
|
491
|
+
return this._blockStatement(blockifySingleStatement(node));
|
|
485
492
|
}
|
|
486
493
|
const consequent = this._block(blockifySingleStatement(consNode));
|
|
487
494
|
const alternate = !altNode ? void 0 : this._block(blockifySingleStatement(altNode));
|
|
@@ -491,7 +498,7 @@ ${this.ctx.pre}if (${condition}) ${consequent}
|
|
|
491
498
|
${this.ctx.pre}else ${alternate}`;
|
|
492
499
|
}
|
|
493
500
|
if (statement[0] === NODE.let || statement[0] === NODE.const) {
|
|
494
|
-
let varType = "
|
|
501
|
+
let varType = "<deferred>";
|
|
495
502
|
const [stmtType, rawId, rawValue] = statement;
|
|
496
503
|
const eq = rawValue !== void 0 ? this._expression(rawValue) : void 0;
|
|
497
504
|
if (!eq) throw new Error(`'${stringifyNode(statement)}' is invalid because all variables need initializers.`);
|
|
@@ -523,7 +530,10 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
523
530
|
invariant(ptrType !== void 0, `Creating pointer type from origin ${eq.origin}`);
|
|
524
531
|
dataType = ptrType;
|
|
525
532
|
}
|
|
526
|
-
if (!(eq.value instanceof RefOperator))
|
|
533
|
+
if (!(eq.value instanceof RefOperator)) {
|
|
534
|
+
dataType = implicitFrom(dataType);
|
|
535
|
+
this.tryMarkModified(rawValue);
|
|
536
|
+
}
|
|
527
537
|
}
|
|
528
538
|
} else if (stmtType === NODE.const) {
|
|
529
539
|
if (eq.origin === "argument") varType = "let";
|
|
@@ -542,9 +552,16 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
542
552
|
const snippet = this.blockVariable(varType, rawId, concretize(dataType), eq.origin);
|
|
543
553
|
const rhsSnippet = tryConvertSnippet(this.ctx, eq, dataType, false);
|
|
544
554
|
const rhsStr = this.ctx.resolve(rhsSnippet.value, rhsSnippet.dataType).value;
|
|
545
|
-
|
|
555
|
+
let emittedVarType;
|
|
556
|
+
if (varType === "<deferred>") {
|
|
557
|
+
const scope = this.ctx.topFunctionScope;
|
|
558
|
+
invariant(scope, `Expected function scope to be present for ${rawId}`);
|
|
559
|
+
emittedVarType = `#VAR_${scope.placeholderForVariable.size}#`;
|
|
560
|
+
scope.placeholderForVariable.set(snippet, emittedVarType);
|
|
561
|
+
} else emittedVarType = varType;
|
|
562
|
+
return this.emitVarDecl(this.ctx.pre, emittedVarType, snippet.value, concretize(dataType), rhsStr);
|
|
546
563
|
}
|
|
547
|
-
if (statement[0] === NODE.block) return
|
|
564
|
+
if (statement[0] === NODE.block) return this._blockStatement(statement);
|
|
548
565
|
if (statement[0] === NODE.for) {
|
|
549
566
|
const [_, init, condition, update, body] = statement;
|
|
550
567
|
const prevUnrollingFlag = this.#unrolling;
|
|
@@ -581,6 +598,7 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
581
598
|
if (statement[0] === NODE.forOf) {
|
|
582
599
|
const [_, loopVar, iterable, body] = statement;
|
|
583
600
|
if (loopVar[0] !== NODE.const) throw new WgslTypeError("Only `for (const ... of ... )` loops are supported");
|
|
601
|
+
this.tryMarkModified(iterable);
|
|
584
602
|
let ctxIndent = false;
|
|
585
603
|
const prevUnrollingFlag = this.#unrolling;
|
|
586
604
|
try {
|
|
@@ -599,21 +617,21 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
599
617
|
const { value } = iterableSnippet;
|
|
600
618
|
const elements = isTgpuRange(value) ? value.map((i) => coerceToSnippet(i)) : value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_$1, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
|
|
601
619
|
if (isEphemeralSnippet(elements[0]) && !isNaturallyEphemeral(elements[0]?.dataType)) throw new WgslTypeError(`Cannot unroll '${stringifyNode(iterable)}'. The elements of iterable are constructed in place but are not value types.`);
|
|
602
|
-
return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this.
|
|
620
|
+
return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this._blockStatement(blockified, { [originalLoopVarName]: e })}`).join("\n");
|
|
603
621
|
}
|
|
604
622
|
this.#unrolling = false;
|
|
605
|
-
const index = this.ctx.
|
|
623
|
+
const index = this.ctx.makeUniqueIdentifier("i", "block");
|
|
606
624
|
const forHeaderStr = stitch`${this.ctx.pre}for (var ${index} = ${range.start}; ${index} ${range.comparison} ${range.end}; ${index} += ${range.step})`;
|
|
607
625
|
let bodyStr = "";
|
|
608
626
|
if (isTgpuRange(iterableSnippet.value)) bodyStr = this._block(blockified, { [originalLoopVarName]: snip(index, range.start.dataType, "runtime") });
|
|
609
627
|
else {
|
|
610
628
|
this.ctx.indent();
|
|
611
629
|
ctxIndent = true;
|
|
612
|
-
const loopVarName = this.ctx.
|
|
630
|
+
const loopVarName = this.ctx.makeUniqueIdentifier(originalLoopVarName, "block");
|
|
613
631
|
const elementSnippet = getElementSnippet(iterableSnippet, snip(index, u32, "runtime"));
|
|
614
632
|
const loopVarKind = getLoopVarKind(elementSnippet);
|
|
615
633
|
const elementType = getElementType(elementSnippet, iterableSnippet);
|
|
616
|
-
bodyStr = `{\n${stitch`${this.ctx.pre}${loopVarKind} ${loopVarName} = ${tryConvertSnippet(this.ctx, elementSnippet, elementType, false)};`}\n${this.
|
|
634
|
+
bodyStr = `{\n${stitch`${this.ctx.pre}${loopVarKind} ${loopVarName} = ${tryConvertSnippet(this.ctx, elementSnippet, elementType, false)};`}\n${this._blockStatement(blockified, { [originalLoopVarName]: snip(loopVarName, elementType, elementSnippet.origin) })}\n`;
|
|
617
635
|
this.ctx.dedent();
|
|
618
636
|
bodyStr += `${this.ctx.pre}}`;
|
|
619
637
|
ctxIndent = false;
|
|
@@ -625,6 +643,14 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
625
643
|
this.ctx.popBlockScope();
|
|
626
644
|
}
|
|
627
645
|
}
|
|
646
|
+
if (statement[0] === NODE.postUpdate) {
|
|
647
|
+
const [_, op, arg] = statement;
|
|
648
|
+
const argExpr = this._expression(arg);
|
|
649
|
+
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
650
|
+
validateSnippetMutation(argExpr, statement);
|
|
651
|
+
this.tryMarkModified(arg);
|
|
652
|
+
return `${this.ctx.pre}${argStr}${op};`;
|
|
653
|
+
}
|
|
628
654
|
if (statement[0] === NODE.continue) {
|
|
629
655
|
if (this.#unrolling) throw new WgslTypeError("Cannot unroll loop containing `continue`");
|
|
630
656
|
return `${this.ctx.pre}continue;`;
|
|
@@ -637,7 +663,35 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
637
663
|
const resolved = expr.value && this.ctx.resolve(expr.value).value;
|
|
638
664
|
return resolved ? `${this.ctx.pre}${resolved};` : "";
|
|
639
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* Attempts a member access lookup to mark a variable as modified.
|
|
668
|
+
* @example
|
|
669
|
+
* // given `let a; a = 1;`
|
|
670
|
+
* tryMarkModified('a') // `a` is marked in the function scope
|
|
671
|
+
*
|
|
672
|
+
* // given `const obj; obj.prop = 1;`
|
|
673
|
+
* tryMarkModified('obj.prop') // `obj` is marked in the function scope
|
|
674
|
+
*
|
|
675
|
+
* // given `this.buffer.$;`
|
|
676
|
+
* tryMarkModified('this.buffer.$') // `this` is not marked, since there is no placeholder for it
|
|
677
|
+
*/
|
|
678
|
+
tryMarkModified(expr) {
|
|
679
|
+
if (!expr) return;
|
|
680
|
+
const maybeObject = extractObject(expr);
|
|
681
|
+
if (maybeObject !== void 0) {
|
|
682
|
+
const snippet = this.ctx.getById(maybeObject);
|
|
683
|
+
const scope = this.ctx.topFunctionScope;
|
|
684
|
+
if (snippet && scope && scope.placeholderForVariable.has(snippet)) scope.modifiedVariables.add(snippet);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
640
687
|
};
|
|
688
|
+
function validateSnippetMutation(mutated, expr) {
|
|
689
|
+
if (mutated.origin === "constant" || mutated.origin === "constant-tgpu-const-ref" || mutated.origin === "runtime-tgpu-const-ref") {
|
|
690
|
+
if (isKnownAtComptime(mutated)) throw new WgslTypeError(`'${stringifyNode(expr)}' is invalid, because the left side is defined outside of the shader, and therefore is immutable during its execution. Try using tgpu.privateVar or buffers.`);
|
|
691
|
+
throw new WgslTypeError(`'${stringifyNode(expr)}' is invalid, because the left side is a constant.`);
|
|
692
|
+
}
|
|
693
|
+
if (mutated.origin === "argument") throw new WgslTypeError(`'${stringifyNode(expr)}' is invalid, because non-pointer arguments cannot be mutated.`);
|
|
694
|
+
}
|
|
641
695
|
function assertExhaustive(value) {
|
|
642
696
|
throw new Error(`'${safeStringify(value)}' was not handled by the WGSL generator.`);
|
|
643
697
|
}
|
|
@@ -649,6 +703,11 @@ function parseNumericString(str) {
|
|
|
649
703
|
function blockifySingleStatement(statement) {
|
|
650
704
|
return typeof statement !== "object" || statement[0] !== NODE.block ? [NODE.block, [statement]] : statement;
|
|
651
705
|
}
|
|
706
|
+
function extractObject(expr) {
|
|
707
|
+
let object = expr;
|
|
708
|
+
while (Array.isArray(object) && (object[0] === NODE.memberAccess || object[0] === NODE.indexAccess)) object = object[1];
|
|
709
|
+
if (typeof object === "string") return object;
|
|
710
|
+
}
|
|
652
711
|
const wgslGenerator = new WgslGenerator();
|
|
653
712
|
var wgslGenerator_default = wgslGenerator;
|
|
654
713
|
|