typegpu 0.10.0 → 0.10.2
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/{chunk-BYypO7fO.js → _virtual/_rolldown/runtime.js} +1 -1
- package/builtin.d.ts +50 -0
- package/builtin.js +37 -0
- package/common/fullScreenTriangle.d.ts +26 -0
- package/common/fullScreenTriangle.js +36 -0
- package/common/index.d.ts +2 -3
- package/common/index.js +3 -4
- package/core/buffer/buffer.d.ts +74 -0
- package/core/buffer/buffer.js +197 -0
- package/core/buffer/bufferShorthand.d.ts +48 -0
- package/core/buffer/bufferShorthand.js +49 -0
- package/core/buffer/bufferUsage.d.ts +45 -0
- package/core/buffer/bufferUsage.js +163 -0
- package/core/constant/tgpuConstant.d.ts +28 -0
- package/core/constant/tgpuConstant.js +67 -0
- package/core/declare/tgpuDeclare.d.ts +18 -0
- package/core/declare/tgpuDeclare.js +40 -0
- package/core/function/autoIO.d.ts +37 -0
- package/core/function/autoIO.js +87 -0
- package/core/function/comptime.d.ts +39 -0
- package/core/function/comptime.js +51 -0
- package/core/function/createCallableSchema.js +42 -0
- package/core/function/dualImpl.js +54 -0
- package/core/function/extractArgs.js +204 -0
- package/core/function/fnCore.js +79 -0
- package/core/function/fnTypes.d.ts +34 -0
- package/core/function/ioSchema.d.ts +10 -0
- package/core/function/ioSchema.js +30 -0
- package/core/function/shelllessImpl.d.ts +28 -0
- package/core/function/shelllessImpl.js +23 -0
- package/core/function/templateUtils.js +13 -0
- package/core/function/tgpuComputeFn.d.ts +49 -0
- package/core/function/tgpuComputeFn.js +62 -0
- package/core/function/tgpuFn.d.ts +52 -0
- package/core/function/tgpuFn.js +170 -0
- package/core/function/tgpuFragmentFn.d.ts +68 -0
- package/core/function/tgpuFragmentFn.js +68 -0
- package/core/function/tgpuVertexFn.d.ts +55 -0
- package/core/function/tgpuVertexFn.js +65 -0
- package/core/pipeline/applyPipelineState.js +37 -0
- package/core/pipeline/computePipeline.d.ts +58 -0
- package/core/pipeline/computePipeline.js +226 -0
- package/core/pipeline/connectAttachmentToShader.js +26 -0
- package/core/pipeline/connectTargetsToShader.js +29 -0
- package/core/pipeline/limitsOverflow.js +13 -0
- package/core/pipeline/renderPipeline.d.ts +266 -0
- package/core/pipeline/renderPipeline.js +471 -0
- package/core/pipeline/timeable.d.ts +23 -0
- package/core/pipeline/timeable.js +61 -0
- package/core/pipeline/typeGuards.js +29 -0
- package/core/querySet/querySet.d.ts +22 -0
- package/core/querySet/querySet.js +103 -0
- package/core/rawCodeSnippet/tgpuRawCodeSnippet.d.ts +59 -0
- package/core/rawCodeSnippet/tgpuRawCodeSnippet.js +96 -0
- package/core/resolve/externals.d.ts +10 -0
- package/core/resolve/externals.js +58 -0
- package/core/resolve/namespace.d.ts +38 -0
- package/core/resolve/namespace.js +41 -0
- package/core/resolve/resolveData.js +146 -0
- package/core/resolve/stitch.js +25 -0
- package/core/resolve/tgpuResolve.d.ts +151 -0
- package/core/resolve/tgpuResolve.js +68 -0
- package/core/root/configurableImpl.js +18 -0
- package/core/root/init.d.ts +69 -0
- package/core/root/init.js +457 -0
- package/core/root/rootTypes.d.ts +622 -0
- package/core/sampler/sampler.d.ts +35 -0
- package/core/sampler/sampler.js +116 -0
- package/core/simulate/tgpuSimulate.d.ts +36 -0
- package/core/simulate/tgpuSimulate.js +76 -0
- package/core/slot/accessor.d.ts +13 -0
- package/core/slot/accessor.js +97 -0
- package/core/slot/internalSlots.js +7 -0
- package/core/slot/lazy.d.ts +6 -0
- package/core/slot/lazy.js +42 -0
- package/core/slot/slot.d.ts +6 -0
- package/core/slot/slot.js +40 -0
- package/core/slot/slotTypes.d.ts +92 -0
- package/core/slot/slotTypes.js +21 -0
- package/core/texture/externalTexture.d.ts +12 -0
- package/core/texture/externalTexture.js +48 -0
- package/core/texture/texture.d.ts +118 -0
- package/core/texture/texture.js +312 -0
- package/core/texture/textureFormats.d.ts +29 -0
- package/core/texture/textureFormats.js +99 -0
- package/core/texture/textureProps.d.ts +11 -0
- package/core/texture/textureUtils.js +224 -0
- package/core/texture/usageExtension.d.ts +21 -0
- package/core/texture/usageExtension.js +21 -0
- package/core/unroll/tgpuUnroll.d.ts +13 -0
- package/core/unroll/tgpuUnroll.js +36 -0
- package/core/valueProxyUtils.js +44 -0
- package/core/variable/tgpuVariable.d.ts +38 -0
- package/core/variable/tgpuVariable.js +101 -0
- package/core/vertexLayout/connectAttributesToShader.js +59 -0
- package/core/vertexLayout/vertexAttribute.d.ts +29 -0
- package/core/vertexLayout/vertexLayout.d.ts +19 -0
- package/core/vertexLayout/vertexLayout.js +103 -0
- package/data/alignIO.js +15 -0
- package/data/alignmentOf.d.ts +10 -0
- package/data/alignmentOf.js +88 -0
- package/data/array.d.ts +28 -0
- package/data/array.js +48 -0
- package/data/atomic.d.ts +15 -0
- package/data/atomic.js +25 -0
- package/data/attributes.d.ts +121 -0
- package/data/attributes.js +145 -0
- package/data/autoStruct.d.ts +3 -0
- package/data/autoStruct.js +83 -0
- package/data/compiledIO.js +231 -0
- package/data/dataIO.js +549 -0
- package/data/dataTypes.d.ts +115 -0
- package/data/dataTypes.js +97 -0
- package/data/deepEqual.d.ts +25 -0
- package/data/deepEqual.js +58 -0
- package/data/disarray.d.ts +34 -0
- package/data/disarray.js +52 -0
- package/data/getLongestContiguousPrefix.d.ts +10 -0
- package/data/getLongestContiguousPrefix.js +15 -0
- package/data/index.d.ts +26 -4
- package/data/index.js +27 -7
- package/data/instanceToSchema.d.ts +33 -0
- package/data/isContiguous.d.ts +10 -0
- package/data/isContiguous.js +15 -0
- package/data/matrix.d.ts +126 -0
- package/data/matrix.js +517 -0
- package/data/numberOps.js +24 -0
- package/data/numeric.d.ts +81 -0
- package/data/numeric.js +234 -0
- package/data/offsetUtils.d.ts +33 -0
- package/data/offsetUtils.js +167 -0
- package/data/offsets.js +36 -0
- package/data/partialIO.js +68 -0
- package/data/ptr.d.ts +12 -0
- package/data/ptr.js +46 -0
- package/data/ref.d.ts +37 -0
- package/data/ref.js +96 -0
- package/data/sampler.d.ts +107 -0
- package/data/sampler.js +26 -0
- package/data/schemaCallWrapper.js +32 -0
- package/data/schemaMemoryLayout.js +200 -0
- package/data/sizeOf.d.ts +10 -0
- package/data/sizeOf.js +15 -0
- package/data/snippet.d.ts +26 -0
- package/data/snippet.js +61 -0
- package/data/struct.d.ts +17 -0
- package/data/struct.js +46 -0
- package/data/texture.d.ts +292 -0
- package/{texture-Dg5ybJro.js → data/texture.js} +6 -3
- package/data/unstruct.d.ts +24 -0
- package/data/unstruct.js +43 -0
- package/data/vector.d.ts +191 -0
- package/data/vector.js +247 -0
- package/data/vectorImpl.js +516 -0
- package/data/vectorOps.js +664 -0
- package/data/vertexFormatData.d.ts +190 -0
- package/data/vertexFormatData.js +110 -0
- package/data/wgslTypes.d.ts +896 -0
- package/data/wgslTypes.js +215 -0
- package/errors.d.ts +44 -0
- package/errors.js +128 -0
- package/execMode.js +51 -0
- package/extension.d.ts +11 -0
- package/extension.js +18 -0
- package/getGPUValue.js +9 -0
- package/index.d.ts +40 -243
- package/index.js +19 -6318
- package/indexNamedExports.d.ts +38 -0
- package/mathUtils.js +13 -0
- package/memo.js +22 -0
- package/nameRegistry.d.ts +30 -0
- package/nameRegistry.js +449 -0
- package/package.js +5 -0
- package/package.json +23 -23
- package/resolutionCtx.d.ts +29 -0
- package/resolutionCtx.js +546 -0
- package/shared/env.js +13 -0
- package/shared/generators.js +14 -0
- package/shared/meta.d.ts +39 -0
- package/shared/meta.js +63 -0
- package/shared/repr.d.ts +108 -0
- package/shared/stringify.js +22 -0
- package/shared/symbols.d.ts +61 -0
- package/shared/symbols.js +71 -0
- package/shared/utilityTypes.d.ts +29 -0
- package/shared/utilityTypes.js +7 -0
- package/shared/vertexFormat.d.ts +70 -0
- package/shared/vertexFormat.js +64 -0
- package/std/array.d.ts +7 -0
- package/std/array.js +27 -0
- package/std/atomic.d.ts +19 -0
- package/std/atomic.js +113 -0
- package/std/bitcast.d.ts +10 -0
- package/std/bitcast.js +43 -0
- package/std/boolean.d.ts +127 -0
- package/std/boolean.js +274 -0
- package/std/derivative.d.ts +16 -0
- package/std/derivative.js +89 -0
- package/std/discard.d.ts +6 -0
- package/std/discard.js +16 -0
- package/std/extensions.d.ts +8 -0
- package/std/extensions.js +14 -0
- package/std/index.d.ts +15 -3
- package/std/index.js +16 -5
- package/std/matrix.d.ts +41 -0
- package/std/matrix.js +87 -0
- package/std/numeric.d.ts +254 -0
- package/std/numeric.js +847 -0
- package/std/operators.d.ts +48 -0
- package/std/operators.js +153 -0
- package/std/packing.d.ts +26 -0
- package/std/packing.js +86 -0
- package/std/subgroup.d.ts +47 -0
- package/std/subgroup.js +220 -0
- package/std/texture.d.ts +108 -0
- package/std/texture.js +197 -0
- package/tgpu.js +44 -0
- package/tgpuBindGroupLayout.d.ts +161 -0
- package/tgpuBindGroupLayout.js +271 -0
- package/tgpuUnstable.d.ts +48 -0
- package/tgpuUnstable.js +66 -0
- package/tgsl/accessIndex.js +45 -0
- package/tgsl/accessProp.js +113 -0
- package/tgsl/consoleLog/deserializers.js +117 -0
- package/tgsl/consoleLog/logGenerator.js +86 -0
- package/tgsl/consoleLog/serializers.js +225 -0
- package/tgsl/consoleLog/types.d.ts +54 -0
- package/tgsl/consoleLog/types.js +12 -0
- package/tgsl/conversion.js +200 -0
- package/tgsl/forOfUtils.js +45 -0
- package/tgsl/generationHelpers.d.ts +37 -0
- package/tgsl/generationHelpers.js +67 -0
- package/tgsl/math.js +45 -0
- package/tgsl/shaderGenerator.d.ts +18 -0
- package/tgsl/shellless.d.ts +11 -0
- package/tgsl/shellless.js +53 -0
- package/tgsl/wgslGenerator.js +585 -0
- package/types.d.ts +255 -0
- package/types.js +43 -0
- package/unwrapper.d.ts +27 -0
- package/wgslExtensions.d.ts +5 -0
- package/wgslExtensions.js +18 -0
- package/builtin-ClEnM-Ye.js +0 -818
- package/builtin-ClEnM-Ye.js.map +0 -1
- package/common/index.d.ts.map +0 -1
- package/common/index.js.map +0 -1
- package/data/index.d.ts.map +0 -1
- package/data/index.js.map +0 -1
- package/deepEqual-yZXvaV2C.js +0 -413
- package/deepEqual-yZXvaV2C.js.map +0 -1
- package/extensions-0SFbU9FH.js +0 -2032
- package/extensions-0SFbU9FH.js.map +0 -1
- package/fullScreenTriangle-MdLGaAMR.js +0 -543
- package/fullScreenTriangle-MdLGaAMR.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/indexNamedExports-Cdy7USiY.d.ts +0 -5696
- package/indexNamedExports-Cdy7USiY.d.ts.map +0 -1
- package/operators-HTxa_0k9.js +0 -4156
- package/operators-HTxa_0k9.js.map +0 -1
- package/std/index.d.ts.map +0 -1
- package/std/index.js.map +0 -1
- package/texture-Dg5ybJro.js.map +0 -1
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import { $gpuCallable, $internal, $providing, isMarkedInternal } from "../shared/symbols.js";
|
|
2
|
+
import { getName } from "../shared/meta.js";
|
|
3
|
+
import { Void, isNaturallyEphemeral, isPtr, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
|
|
4
|
+
import { ConsoleLog, InfixDispatch, UnknownData, isLooseData, unptr } from "../data/dataTypes.js";
|
|
5
|
+
import { isEphemeralOrigin, isEphemeralSnippet, snip } from "../data/snippet.js";
|
|
6
|
+
import { ResolutionError, WgslTypeError, invariant } from "../errors.js";
|
|
7
|
+
import { isGPUCallable, isKnownAtComptime } from "../types.js";
|
|
8
|
+
import { stitch } from "../core/resolve/stitch.js";
|
|
9
|
+
import { createPtrFromOrigin, implicitFrom, ptrFn } from "../data/ptr.js";
|
|
10
|
+
import { RefOperator } from "../data/ref.js";
|
|
11
|
+
import { safeStringify } from "../shared/stringify.js";
|
|
12
|
+
import { convertStructValues, convertToCommonType, tryConvertSnippet } from "./conversion.js";
|
|
13
|
+
import { bool, i32, u32 } from "../data/numeric.js";
|
|
14
|
+
import { AutoStruct } from "../data/autoStruct.js";
|
|
15
|
+
import { ArrayExpression, coerceToSnippet, concretize, numericLiteralToSnippet } from "./generationHelpers.js";
|
|
16
|
+
import { add, div, mul, neg, sub } from "../std/operators.js";
|
|
17
|
+
import { accessProp } from "./accessProp.js";
|
|
18
|
+
import { accessIndex } from "./accessIndex.js";
|
|
19
|
+
import { constant } from "../core/constant/tgpuConstant.js";
|
|
20
|
+
import { isGenericFn } from "../core/function/tgpuFn.js";
|
|
21
|
+
import { arrayOf } from "../data/array.js";
|
|
22
|
+
import { pow } from "../std/numeric.js";
|
|
23
|
+
import { UnrollableIterable } from "../core/unroll/tgpuUnroll.js";
|
|
24
|
+
import { mathToStd } from "./math.js";
|
|
25
|
+
import { getElementCountSnippet, getElementSnippet, getElementType, getLoopVarKind } from "./forOfUtils.js";
|
|
26
|
+
import * as tinyest from "tinyest";
|
|
27
|
+
|
|
28
|
+
//#region src/tgsl/wgslGenerator.ts
|
|
29
|
+
const { NodeTypeCatalog: NODE } = tinyest;
|
|
30
|
+
const parenthesizedOps = [
|
|
31
|
+
"==",
|
|
32
|
+
"!=",
|
|
33
|
+
"===",
|
|
34
|
+
"!==",
|
|
35
|
+
"<",
|
|
36
|
+
"<=",
|
|
37
|
+
">",
|
|
38
|
+
">=",
|
|
39
|
+
"<<",
|
|
40
|
+
">>",
|
|
41
|
+
"+",
|
|
42
|
+
"-",
|
|
43
|
+
"*",
|
|
44
|
+
"/",
|
|
45
|
+
"%",
|
|
46
|
+
"|",
|
|
47
|
+
"^",
|
|
48
|
+
"&",
|
|
49
|
+
"&&",
|
|
50
|
+
"||"
|
|
51
|
+
];
|
|
52
|
+
const binaryLogicalOps = [
|
|
53
|
+
"&&",
|
|
54
|
+
"||",
|
|
55
|
+
"==",
|
|
56
|
+
"!=",
|
|
57
|
+
"===",
|
|
58
|
+
"!==",
|
|
59
|
+
"<",
|
|
60
|
+
"<=",
|
|
61
|
+
">",
|
|
62
|
+
">="
|
|
63
|
+
];
|
|
64
|
+
const OP_MAP = {
|
|
65
|
+
"===": "==",
|
|
66
|
+
"!==": "!=",
|
|
67
|
+
get ">>>"() {
|
|
68
|
+
throw new Error("The `>>>` operator is unsupported in TypeGPU functions.");
|
|
69
|
+
},
|
|
70
|
+
get in() {
|
|
71
|
+
throw new Error("The `in` operator is unsupported in TypeGPU functions.");
|
|
72
|
+
},
|
|
73
|
+
get instanceof() {
|
|
74
|
+
throw new Error("The `instanceof` operator is unsupported in TypeGPU functions.");
|
|
75
|
+
},
|
|
76
|
+
get "|>"() {
|
|
77
|
+
throw new Error("The `|>` operator is unsupported in TypeGPU functions.");
|
|
78
|
+
},
|
|
79
|
+
get "??"() {
|
|
80
|
+
throw new Error("The `??` operator is unsupported in TypeGPU functions.");
|
|
81
|
+
},
|
|
82
|
+
get ">>>="() {
|
|
83
|
+
throw new Error("The `>>>=` operator is unsupported in TypeGPU functions.");
|
|
84
|
+
},
|
|
85
|
+
get "**="() {
|
|
86
|
+
throw new Error("The `**=` operator is unsupported in TypeGPU functions.");
|
|
87
|
+
},
|
|
88
|
+
get "??="() {
|
|
89
|
+
throw new Error("The `??=` operator is unsupported in TypeGPU functions.");
|
|
90
|
+
},
|
|
91
|
+
get "&&="() {
|
|
92
|
+
throw new Error("The `&&=` operator is unsupported in TypeGPU functions.");
|
|
93
|
+
},
|
|
94
|
+
get "||="() {
|
|
95
|
+
throw new Error("The `||=` operator is unsupported in TypeGPU functions.");
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
function operatorToType(lhs, op, rhs) {
|
|
99
|
+
if (!rhs) {
|
|
100
|
+
if (op === "!") return bool;
|
|
101
|
+
return lhs;
|
|
102
|
+
}
|
|
103
|
+
if (binaryLogicalOps.includes(op)) return bool;
|
|
104
|
+
if (op === "=") return rhs;
|
|
105
|
+
return lhs;
|
|
106
|
+
}
|
|
107
|
+
const unaryOpCodeToCodegen = {
|
|
108
|
+
"-": neg[$gpuCallable].call.bind(neg),
|
|
109
|
+
void: () => snip(void 0, Void, "constant")
|
|
110
|
+
};
|
|
111
|
+
const binaryOpCodeToCodegen = {
|
|
112
|
+
"+": add[$gpuCallable].call.bind(add),
|
|
113
|
+
"-": sub[$gpuCallable].call.bind(sub),
|
|
114
|
+
"*": mul[$gpuCallable].call.bind(mul),
|
|
115
|
+
"/": div[$gpuCallable].call.bind(div),
|
|
116
|
+
"**": pow[$gpuCallable].call.bind(pow)
|
|
117
|
+
};
|
|
118
|
+
var WgslGenerator = class {
|
|
119
|
+
#ctx = void 0;
|
|
120
|
+
#unrolling = false;
|
|
121
|
+
initGenerator(ctx) {
|
|
122
|
+
this.#ctx = ctx;
|
|
123
|
+
}
|
|
124
|
+
get ctx() {
|
|
125
|
+
if (!this.#ctx) throw new Error("WGSL Generator has not yet been initialized. Please call initialize(ctx) before using the generator.");
|
|
126
|
+
return this.#ctx;
|
|
127
|
+
}
|
|
128
|
+
block([_, statements], externalMap) {
|
|
129
|
+
this.ctx.pushBlockScope();
|
|
130
|
+
if (externalMap) {
|
|
131
|
+
const externals = Object.fromEntries(Object.entries(externalMap).map(([id, value]) => [id, coerceToSnippet(value)]));
|
|
132
|
+
this.ctx.setBlockExternals(externals);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
this.ctx.indent();
|
|
136
|
+
const body = statements.map((statement) => this.statement(statement)).filter((statement) => statement.length > 0).join("\n");
|
|
137
|
+
this.ctx.dedent();
|
|
138
|
+
return `{
|
|
139
|
+
${body}
|
|
140
|
+
${this.ctx.pre}}`;
|
|
141
|
+
} finally {
|
|
142
|
+
this.ctx.popBlockScope();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
refVariable(id, dataType) {
|
|
146
|
+
const varName = this.ctx.makeNameValid(id);
|
|
147
|
+
const ptrType = ptrFn(dataType);
|
|
148
|
+
const snippet = snip(new RefOperator(snip(varName, dataType, "function"), ptrType), ptrType, "function");
|
|
149
|
+
this.ctx.defineVariable(id, snippet);
|
|
150
|
+
return varName;
|
|
151
|
+
}
|
|
152
|
+
blockVariable(varType, id, dataType, origin) {
|
|
153
|
+
const naturallyEphemeral = isNaturallyEphemeral(dataType);
|
|
154
|
+
let varOrigin;
|
|
155
|
+
if (origin === "constant-tgpu-const-ref" || origin === "runtime-tgpu-const-ref") varOrigin = origin;
|
|
156
|
+
else if (origin === "argument") if (naturallyEphemeral) varOrigin = "runtime";
|
|
157
|
+
else varOrigin = "argument";
|
|
158
|
+
else if (!naturallyEphemeral) varOrigin = isEphemeralOrigin(origin) ? "this-function" : origin;
|
|
159
|
+
else if (origin === "constant" && varType === "const") varOrigin = "constant";
|
|
160
|
+
else varOrigin = "runtime";
|
|
161
|
+
const snippet = snip(this.ctx.makeNameValid(id), dataType, varOrigin);
|
|
162
|
+
this.ctx.defineVariable(id, snippet);
|
|
163
|
+
return snippet;
|
|
164
|
+
}
|
|
165
|
+
identifier(id) {
|
|
166
|
+
if (!id) throw new Error("Cannot resolve an empty identifier");
|
|
167
|
+
if (id === "undefined") return snip(void 0, Void, "constant");
|
|
168
|
+
const res = this.ctx.getById(id);
|
|
169
|
+
if (!res) throw new Error(`Identifier ${id} not found`);
|
|
170
|
+
return res;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* A wrapper for `generateExpression` that updates `ctx.expectedType`
|
|
174
|
+
* and tries to convert the result when it does not match the expected type.
|
|
175
|
+
*/
|
|
176
|
+
typedExpression(expression, expectedType) {
|
|
177
|
+
const prevExpectedType = this.ctx.expectedType;
|
|
178
|
+
this.ctx.expectedType = expectedType;
|
|
179
|
+
try {
|
|
180
|
+
const result = this.expression(expression);
|
|
181
|
+
if (expectedType instanceof AutoStruct) return result;
|
|
182
|
+
return tryConvertSnippet(this.ctx, result, expectedType);
|
|
183
|
+
} finally {
|
|
184
|
+
this.ctx.expectedType = prevExpectedType;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
expression(expression) {
|
|
188
|
+
if (typeof expression === "string") return this.identifier(expression);
|
|
189
|
+
if (typeof expression === "boolean") return snip(expression, bool, "constant");
|
|
190
|
+
if (expression[0] === NODE.logicalExpr || expression[0] === NODE.binaryExpr || expression[0] === NODE.assignmentExpr) {
|
|
191
|
+
const [exprType, lhs, op, rhs] = expression;
|
|
192
|
+
const lhsExpr = this.expression(lhs);
|
|
193
|
+
const rhsExpr = this.expression(rhs);
|
|
194
|
+
if (rhsExpr.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot assign a ref to an existing variable '${lhsExpr}', define a new variable instead.`);
|
|
195
|
+
if (op === "==") throw new Error("Please use the === operator instead of ==");
|
|
196
|
+
if (op === "===" && isKnownAtComptime(lhsExpr) && isKnownAtComptime(rhsExpr)) return snip(lhsExpr.value === rhsExpr.value, bool, "constant");
|
|
197
|
+
if (lhsExpr.dataType === UnknownData) throw new WgslTypeError(`Left-hand side of '${op}' is of unknown type`);
|
|
198
|
+
if (rhsExpr.dataType === UnknownData) throw new WgslTypeError(`Right-hand side of '${op}' is of unknown type`);
|
|
199
|
+
const codegen = binaryOpCodeToCodegen[op];
|
|
200
|
+
if (codegen) return codegen(this.ctx, [lhsExpr, rhsExpr]);
|
|
201
|
+
const forcedType = exprType === NODE.assignmentExpr ? [lhsExpr.dataType] : void 0;
|
|
202
|
+
const [convLhs, convRhs] = convertToCommonType(this.ctx, [lhsExpr, rhsExpr], forcedType) ?? [lhsExpr, rhsExpr];
|
|
203
|
+
const lhsStr = this.ctx.resolve(convLhs.value, convLhs.dataType).value;
|
|
204
|
+
const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
|
|
205
|
+
const type = operatorToType(convLhs.dataType, op, convRhs.dataType);
|
|
206
|
+
if (exprType === NODE.assignmentExpr) {
|
|
207
|
+
if (convLhs.origin === "constant" || convLhs.origin === "constant-tgpu-const-ref" || convLhs.origin === "runtime-tgpu-const-ref") throw new WgslTypeError(`'${lhsStr} = ${rhsStr}' is invalid, because ${lhsStr} is a constant. This error may also occur when assigning to a value defined outside of a TypeGPU function's scope.`);
|
|
208
|
+
if (lhsExpr.origin === "argument") throw new WgslTypeError(`'${lhsStr} ${op} ${rhsStr}' is invalid, because non-pointer arguments cannot be mutated.`);
|
|
209
|
+
if (op === "=" && rhsExpr.origin === "argument" && !isNaturallyEphemeral(rhsExpr.dataType)) throw new WgslTypeError(`'${lhsStr} = ${rhsStr}' is invalid, because argument references cannot be assigned.\n-----\nTry '${lhsStr} = ${this.ctx.resolve(rhsExpr.dataType).value}(${rhsStr})' to copy the value instead.\n-----`);
|
|
210
|
+
if (op === "=" && !isEphemeralSnippet(rhsExpr)) throw new WgslTypeError(`'${lhsStr} = ${rhsStr}' is invalid, because references cannot be assigned.\n-----\nTry '${lhsStr} = ${this.ctx.resolve(rhsExpr.dataType).value}(${rhsStr})' to copy the value instead.\n-----`);
|
|
211
|
+
}
|
|
212
|
+
return snip(parenthesizedOps.includes(op) ? `(${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr})` : `${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr}`, type, "runtime");
|
|
213
|
+
}
|
|
214
|
+
if (expression[0] === NODE.postUpdate) {
|
|
215
|
+
const [_, op, arg] = expression;
|
|
216
|
+
const argExpr = this.expression(arg);
|
|
217
|
+
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
218
|
+
return snip(`${argStr}${op}`, argExpr.dataType, "runtime");
|
|
219
|
+
}
|
|
220
|
+
if (expression[0] === NODE.unaryExpr) {
|
|
221
|
+
const [_, op, arg] = expression;
|
|
222
|
+
const argExpr = this.expression(arg);
|
|
223
|
+
const codegen = unaryOpCodeToCodegen[op];
|
|
224
|
+
if (codegen) return codegen(this.ctx, [argExpr]);
|
|
225
|
+
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
226
|
+
const type = operatorToType(argExpr.dataType, op);
|
|
227
|
+
return snip(`${op}${argStr}`, type, "runtime");
|
|
228
|
+
}
|
|
229
|
+
if (expression[0] === NODE.memberAccess) {
|
|
230
|
+
const [_, targetNode, property] = expression;
|
|
231
|
+
const target = this.expression(targetNode);
|
|
232
|
+
if (target.value === console) return snip(new ConsoleLog(property), UnknownData, "runtime");
|
|
233
|
+
if (target.value === Math) {
|
|
234
|
+
if (property in mathToStd && mathToStd[property]) return snip(mathToStd[property], UnknownData, "runtime");
|
|
235
|
+
if (typeof Math[property] === "function") throw new Error(`Unsupported functionality 'Math.${property}'. Use an std alternative, or implement the function manually.`);
|
|
236
|
+
}
|
|
237
|
+
const accessed = accessProp(target, property);
|
|
238
|
+
if (!accessed) throw new Error(stitch`Property '${property}' not found on value '${target}' of type ${this.ctx.resolve(target.dataType)}`);
|
|
239
|
+
return accessed;
|
|
240
|
+
}
|
|
241
|
+
if (expression[0] === NODE.indexAccess) {
|
|
242
|
+
const [_, targetNode, propertyNode] = expression;
|
|
243
|
+
const target = this.expression(targetNode);
|
|
244
|
+
const inProperty = this.expression(propertyNode);
|
|
245
|
+
const property = convertToCommonType(this.ctx, [inProperty], [u32, i32], false)?.[0] ?? inProperty;
|
|
246
|
+
const accessed = accessIndex(target, property);
|
|
247
|
+
if (!accessed) {
|
|
248
|
+
const targetStr = this.ctx.resolve(target.value, target.dataType).value;
|
|
249
|
+
const propertyStr = this.ctx.resolve(property.value, property.dataType).value;
|
|
250
|
+
throw new Error(`Unable to index value ${targetStr} of unknown type with index ${propertyStr}. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.`);
|
|
251
|
+
}
|
|
252
|
+
return accessed;
|
|
253
|
+
}
|
|
254
|
+
if (expression[0] === NODE.numericLiteral) {
|
|
255
|
+
const type = typeof expression[1] === "string" ? numericLiteralToSnippet(parseNumericString(expression[1])) : numericLiteralToSnippet(expression[1]);
|
|
256
|
+
if (!type) throw new Error(`Invalid numeric literal ${expression[1]}`);
|
|
257
|
+
return type;
|
|
258
|
+
}
|
|
259
|
+
if (expression[0] === NODE.call) {
|
|
260
|
+
const [_, calleeNode, argNodes] = expression;
|
|
261
|
+
const callee = this.expression(calleeNode);
|
|
262
|
+
if (isWgslStruct(callee.value)) {
|
|
263
|
+
if (argNodes.length > 1) throw new WgslTypeError("Struct schemas should always be called with at most 1 argument");
|
|
264
|
+
if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
|
|
265
|
+
const arg = this.typedExpression(argNodes[0], callee.value);
|
|
266
|
+
return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
|
|
267
|
+
}
|
|
268
|
+
if (isWgslArray(callee.value)) {
|
|
269
|
+
if (argNodes.length > 1) throw new WgslTypeError("Array schemas should always be called with at most 1 argument");
|
|
270
|
+
if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
|
|
271
|
+
const arg = this.typedExpression(argNodes[0], callee.value);
|
|
272
|
+
if (arg.value instanceof ArrayExpression) return snip(stitch`${this.ctx.resolve(callee.value).value}(${arg.value.elements})`, arg.dataType, "runtime");
|
|
273
|
+
return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
|
|
274
|
+
}
|
|
275
|
+
if (callee.value === constant) throw new Error("Constants cannot be defined within TypeGPU function scope. To address this, move the constant definition outside the function scope.");
|
|
276
|
+
if (callee.value instanceof InfixDispatch) {
|
|
277
|
+
if (!argNodes[0]) throw new WgslTypeError(`An infix operator '${callee.value.name}' was called without any arguments`);
|
|
278
|
+
const rhs = this.expression(argNodes[0]);
|
|
279
|
+
return callee.value.operator(this.ctx, [callee.value.lhs, rhs]);
|
|
280
|
+
}
|
|
281
|
+
if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this.expression(arg)));
|
|
282
|
+
if (isGPUCallable(callee.value)) {
|
|
283
|
+
const callable = callee.value[$gpuCallable];
|
|
284
|
+
const strictSignature = callable.strictSignature;
|
|
285
|
+
let convertedArguments;
|
|
286
|
+
if (strictSignature) convertedArguments = argNodes.map((arg, i) => {
|
|
287
|
+
const argType = strictSignature.argTypes[i];
|
|
288
|
+
if (!argType) throw new WgslTypeError(`Function '${getName(callee.value)}' was called with too many arguments`);
|
|
289
|
+
return this.typedExpression(arg, argType);
|
|
290
|
+
});
|
|
291
|
+
else convertedArguments = argNodes.map((arg) => this.expression(arg));
|
|
292
|
+
try {
|
|
293
|
+
return callable.call(this.ctx, convertedArguments);
|
|
294
|
+
} catch (err) {
|
|
295
|
+
if (err instanceof ResolutionError) throw err;
|
|
296
|
+
throw new ResolutionError(err, [{ toString: () => `fn:${getName(callee.value)}` }]);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const isGeneric = isGenericFn(callee.value);
|
|
300
|
+
if (!isMarkedInternal(callee.value) || isGeneric) {
|
|
301
|
+
const slotPairs = isGeneric ? callee.value[$providing]?.pairs ?? [] : [];
|
|
302
|
+
const callback = isGeneric ? callee.value[$internal].inner : callee.value;
|
|
303
|
+
const shelllessCall = this.ctx.withRenamed(callback, getName(callee.value), () => this.ctx.withSlots(slotPairs, () => {
|
|
304
|
+
const args = argNodes.map((arg) => this.expression(arg));
|
|
305
|
+
const shellless = this.ctx.shelllessRepo.get(callback, args);
|
|
306
|
+
if (!shellless) return;
|
|
307
|
+
const converted = args.map((s, idx) => {
|
|
308
|
+
const argType = shellless.argTypes[idx];
|
|
309
|
+
return tryConvertSnippet(this.ctx, s, argType, false);
|
|
310
|
+
});
|
|
311
|
+
return this.ctx.withResetIndentLevel(() => {
|
|
312
|
+
const snippet = this.ctx.resolve(shellless);
|
|
313
|
+
return snip(stitch`${snippet.value}(${converted})`, snippet.dataType, "runtime");
|
|
314
|
+
});
|
|
315
|
+
}));
|
|
316
|
+
if (shelllessCall) return shelllessCall;
|
|
317
|
+
}
|
|
318
|
+
throw new Error(`Function '${getName(callee.value) ?? String(callee.value)}' is not marked with the 'use gpu' directive and cannot be used in a shader`);
|
|
319
|
+
}
|
|
320
|
+
if (expression[0] === NODE.objectExpr) {
|
|
321
|
+
const obj = expression[1];
|
|
322
|
+
const structType = this.ctx.expectedType;
|
|
323
|
+
if (structType instanceof AutoStruct) {
|
|
324
|
+
const entries = Object.fromEntries(Object.entries(obj).map(([key, value]) => {
|
|
325
|
+
let accessed = structType.accessProp(key);
|
|
326
|
+
let expr;
|
|
327
|
+
if (accessed) expr = this.typedExpression(value, accessed.type);
|
|
328
|
+
else {
|
|
329
|
+
expr = this.expression(value);
|
|
330
|
+
if (expr.dataType === UnknownData) throw new WgslTypeError(stitch`Property ${key} in object literal has a value of unknown type: '${expr}'`);
|
|
331
|
+
accessed = structType.provideProp(key, concretize(expr.dataType));
|
|
332
|
+
}
|
|
333
|
+
return [accessed.prop, expr];
|
|
334
|
+
}));
|
|
335
|
+
const completeStruct = structType.completeStruct;
|
|
336
|
+
const convertedSnippets = convertStructValues(this.ctx, completeStruct, entries);
|
|
337
|
+
return snip(stitch`${this.ctx.resolve(structType).value}(${convertedSnippets})`, completeStruct, "runtime");
|
|
338
|
+
}
|
|
339
|
+
if (isWgslStruct(structType)) {
|
|
340
|
+
const entries = Object.fromEntries(Object.entries(structType.propTypes).map(([key, value]) => {
|
|
341
|
+
const val = obj[key];
|
|
342
|
+
if (val === void 0) throw new WgslTypeError(`Missing property ${key} in object literal for struct ${structType}`);
|
|
343
|
+
return [key, this.typedExpression(val, value)];
|
|
344
|
+
}));
|
|
345
|
+
const convertedSnippets = convertStructValues(this.ctx, structType, entries);
|
|
346
|
+
return snip(stitch`${this.ctx.resolve(structType).value}(${convertedSnippets})`, structType, "runtime");
|
|
347
|
+
}
|
|
348
|
+
throw new WgslTypeError(`No target type could be inferred for object with keys [${Object.keys(obj).join(", ")}], please wrap the object in the corresponding schema.`);
|
|
349
|
+
}
|
|
350
|
+
if (expression[0] === NODE.arrayExpr) {
|
|
351
|
+
const [_, valueNodes] = expression;
|
|
352
|
+
const arrType = this.ctx.expectedType;
|
|
353
|
+
let elemType;
|
|
354
|
+
let values;
|
|
355
|
+
if (isWgslArray(arrType)) {
|
|
356
|
+
elemType = arrType.elementType;
|
|
357
|
+
values = valueNodes.map((value) => this.typedExpression(value, elemType));
|
|
358
|
+
if (values.length !== arrType.elementCount) throw new WgslTypeError(`Cannot create value of type '${arrType}' from an array of length: ${values.length}`);
|
|
359
|
+
} else {
|
|
360
|
+
const valuesSnippets = valueNodes.map((value) => this.expression(value));
|
|
361
|
+
if (valuesSnippets.length === 0) throw new WgslTypeError("Cannot infer the type of an empty array literal.");
|
|
362
|
+
const converted = convertToCommonType(this.ctx, valuesSnippets);
|
|
363
|
+
if (!converted) throw new WgslTypeError("The given values cannot be automatically converted to a common type. Consider wrapping the array in an appropriate schema");
|
|
364
|
+
values = converted;
|
|
365
|
+
elemType = concretize(values[0]?.dataType);
|
|
366
|
+
}
|
|
367
|
+
const arrayType = arrayOf(elemType, values.length);
|
|
368
|
+
return snip(new ArrayExpression(arrayType, values), arrayType, "runtime");
|
|
369
|
+
}
|
|
370
|
+
if (expression[0] === NODE.conditionalExpr) {
|
|
371
|
+
const [_, test, consequent, alternative] = expression;
|
|
372
|
+
const testExpression = this.expression(test);
|
|
373
|
+
if (isKnownAtComptime(testExpression)) return testExpression.value ? this.expression(consequent) : this.expression(alternative);
|
|
374
|
+
else throw new Error(`Ternary operator is only supported for comptime-known checks (used with '${testExpression.value}'). For runtime checks, please use 'std.select' or if/else statements.`);
|
|
375
|
+
}
|
|
376
|
+
if (expression[0] === NODE.stringLiteral) return snip(expression[1], UnknownData, "constant");
|
|
377
|
+
if (expression[0] === NODE.preUpdate) throw new Error("Cannot use pre-updates in TypeGPU functions.");
|
|
378
|
+
assertExhaustive(expression);
|
|
379
|
+
}
|
|
380
|
+
functionDefinition(body) {
|
|
381
|
+
return this.block(body);
|
|
382
|
+
}
|
|
383
|
+
statement(statement) {
|
|
384
|
+
if (typeof statement === "string") {
|
|
385
|
+
const id = this.identifier(statement);
|
|
386
|
+
const resolved = id.value && this.ctx.resolve(id.value).value;
|
|
387
|
+
return resolved ? `${this.ctx.pre}${resolved};` : "";
|
|
388
|
+
}
|
|
389
|
+
if (typeof statement === "boolean") return `${this.ctx.pre}${statement ? "true" : "false"};`;
|
|
390
|
+
if (statement[0] === NODE.return) {
|
|
391
|
+
const returnNode = statement[1];
|
|
392
|
+
if (returnNode !== void 0) {
|
|
393
|
+
const expectedReturnType = this.ctx.topFunctionReturnType;
|
|
394
|
+
let returnSnippet = expectedReturnType ? this.typedExpression(returnNode, expectedReturnType) : this.expression(returnNode);
|
|
395
|
+
if (returnSnippet.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot return references, returning '${returnSnippet.value.snippet}'`);
|
|
396
|
+
if (returnSnippet.origin === "argument" && !isNaturallyEphemeral(returnSnippet.dataType) && this.ctx.topFunctionScope?.functionType === "normal") throw new WgslTypeError(stitch`Cannot return references to arguments, returning '${returnSnippet}'. Copy the argument before returning it.`);
|
|
397
|
+
if (!expectedReturnType && !isEphemeralSnippet(returnSnippet) && returnSnippet.origin !== "this-function") {
|
|
398
|
+
const str = this.ctx.resolve(returnSnippet.value, returnSnippet.dataType).value;
|
|
399
|
+
const typeStr = this.ctx.resolve(unptr(returnSnippet.dataType)).value;
|
|
400
|
+
throw new WgslTypeError(`'return ${str};' is invalid, cannot return references.
|
|
401
|
+
-----
|
|
402
|
+
Try 'return ${typeStr}(${str});' instead.
|
|
403
|
+
-----`);
|
|
404
|
+
}
|
|
405
|
+
returnSnippet = tryConvertSnippet(this.ctx, returnSnippet, unptr(returnSnippet.dataType), false);
|
|
406
|
+
invariant(returnSnippet.dataType !== UnknownData, "Return type should be known");
|
|
407
|
+
this.ctx.reportReturnType(returnSnippet.dataType);
|
|
408
|
+
return stitch`${this.ctx.pre}return ${returnSnippet};`;
|
|
409
|
+
}
|
|
410
|
+
return `${this.ctx.pre}return;`;
|
|
411
|
+
}
|
|
412
|
+
if (statement[0] === NODE.if) {
|
|
413
|
+
const [_, condNode, consNode, altNode] = statement;
|
|
414
|
+
const condition = this.typedExpression(condNode, bool);
|
|
415
|
+
const consequent = condition.value === false ? void 0 : this.block(blockifySingleStatement(consNode));
|
|
416
|
+
const alternate = condition.value === true || !altNode ? void 0 : this.block(blockifySingleStatement(altNode));
|
|
417
|
+
if (condition.value === true) return `${this.ctx.pre}${consequent}`;
|
|
418
|
+
if (condition.value === false) return alternate ? `${this.ctx.pre}${alternate}` : "";
|
|
419
|
+
if (!alternate) return stitch`${this.ctx.pre}if (${condition}) ${consequent}`;
|
|
420
|
+
return stitch`\
|
|
421
|
+
${this.ctx.pre}if (${condition}) ${consequent}
|
|
422
|
+
${this.ctx.pre}else ${alternate}`;
|
|
423
|
+
}
|
|
424
|
+
if (statement[0] === NODE.let || statement[0] === NODE.const) {
|
|
425
|
+
let varType = "var";
|
|
426
|
+
const [stmtType, rawId, rawValue] = statement;
|
|
427
|
+
const eq = rawValue !== void 0 ? this.expression(rawValue) : void 0;
|
|
428
|
+
if (!eq) throw new Error(`Cannot create variable '${rawId}' without an initial value.`);
|
|
429
|
+
const ephemeral = isEphemeralSnippet(eq);
|
|
430
|
+
let dataType = eq.dataType;
|
|
431
|
+
const naturallyEphemeral = isNaturallyEphemeral(dataType);
|
|
432
|
+
if (isLooseData(eq.dataType)) throw new Error(`Cannot create variable '${rawId}' with loose data type.`);
|
|
433
|
+
if (eq.value instanceof RefOperator) {
|
|
434
|
+
if (eq.dataType !== UnknownData) throw new WgslTypeError(`Cannot store d.ref() in a variable if it references another value. Copy the value passed into d.ref() instead.`);
|
|
435
|
+
const refSnippet = eq.value.snippet;
|
|
436
|
+
const varName = this.refVariable(rawId, concretize(refSnippet.dataType));
|
|
437
|
+
return stitch`${this.ctx.pre}var ${varName} = ${tryConvertSnippet(this.ctx, refSnippet, refSnippet.dataType, false)};`;
|
|
438
|
+
}
|
|
439
|
+
if (!ephemeral) {
|
|
440
|
+
if (stmtType === NODE.let) {
|
|
441
|
+
const rhsStr = this.ctx.resolve(eq.value).value;
|
|
442
|
+
const rhsTypeStr = this.ctx.resolve(unptr(eq.dataType)).value;
|
|
443
|
+
throw new WgslTypeError(`'let ${rawId} = ${rhsStr}' is invalid, because references cannot be assigned to 'let' variable declarations.
|
|
444
|
+
-----
|
|
445
|
+
- Try 'let ${rawId} = ${rhsTypeStr}(${rhsStr})' if you need to reassign '${rawId}' later
|
|
446
|
+
- Try 'const ${rawId} = ${rhsStr}' if you won't reassign '${rawId}' later.
|
|
447
|
+
-----`);
|
|
448
|
+
}
|
|
449
|
+
if (eq.origin === "constant-tgpu-const-ref") varType = "const";
|
|
450
|
+
else if (eq.origin === "runtime-tgpu-const-ref") varType = "let";
|
|
451
|
+
else {
|
|
452
|
+
varType = "let";
|
|
453
|
+
if (!isPtr(dataType)) {
|
|
454
|
+
const ptrType = createPtrFromOrigin(eq.origin, concretize(dataType));
|
|
455
|
+
invariant(ptrType !== void 0, `Creating pointer type from origin ${eq.origin}`);
|
|
456
|
+
dataType = ptrType;
|
|
457
|
+
}
|
|
458
|
+
if (!(eq.value instanceof RefOperator)) dataType = implicitFrom(dataType);
|
|
459
|
+
}
|
|
460
|
+
} else if (stmtType === NODE.const) {
|
|
461
|
+
if (eq.origin === "argument") varType = "let";
|
|
462
|
+
else if (naturallyEphemeral) varType = eq.origin === "constant" ? "const" : "let";
|
|
463
|
+
} else if (eq.origin === "argument") {
|
|
464
|
+
if (!naturallyEphemeral) {
|
|
465
|
+
const rhsStr = this.ctx.resolve(eq.value).value;
|
|
466
|
+
const rhsTypeStr = this.ctx.resolve(unptr(eq.dataType)).value;
|
|
467
|
+
throw new WgslTypeError(`'let ${rawId} = ${rhsStr}' is invalid, because references to arguments cannot be assigned to 'let' variable declarations.
|
|
468
|
+
-----
|
|
469
|
+
- Try 'let ${rawId} = ${rhsTypeStr}(${rhsStr})' if you need to reassign '${rawId}' later
|
|
470
|
+
- Try 'const ${rawId} = ${rhsStr}' if you won't reassign '${rawId}' later.
|
|
471
|
+
-----`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const snippet = this.blockVariable(varType, rawId, concretize(dataType), eq.origin);
|
|
475
|
+
return stitch`${this.ctx.pre}${varType} ${snippet.value} = ${tryConvertSnippet(this.ctx, eq, dataType, false)};`;
|
|
476
|
+
}
|
|
477
|
+
if (statement[0] === NODE.block) return `${this.ctx.pre}${this.block(statement)}`;
|
|
478
|
+
if (statement[0] === NODE.for) {
|
|
479
|
+
const [_, init, condition, update, body] = statement;
|
|
480
|
+
const prevUnrollingFlag = this.#unrolling;
|
|
481
|
+
this.#unrolling = false;
|
|
482
|
+
try {
|
|
483
|
+
this.ctx.pushBlockScope();
|
|
484
|
+
const [initStatement, conditionExpr, updateStatement] = this.ctx.withResetIndentLevel(() => [
|
|
485
|
+
init ? this.statement(init) : void 0,
|
|
486
|
+
condition ? this.typedExpression(condition, bool) : void 0,
|
|
487
|
+
update ? this.statement(update) : void 0
|
|
488
|
+
]);
|
|
489
|
+
const initStr = initStatement ? initStatement.slice(0, -1) : "";
|
|
490
|
+
const updateStr = updateStatement ? updateStatement.slice(0, -1) : "";
|
|
491
|
+
const bodyStr = this.block(blockifySingleStatement(body));
|
|
492
|
+
return stitch`${this.ctx.pre}for (${initStr}; ${conditionExpr}; ${updateStr}) ${bodyStr}`;
|
|
493
|
+
} finally {
|
|
494
|
+
this.#unrolling = prevUnrollingFlag;
|
|
495
|
+
this.ctx.popBlockScope();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (statement[0] === NODE.while) {
|
|
499
|
+
const prevUnrollingFlag = this.#unrolling;
|
|
500
|
+
this.#unrolling = false;
|
|
501
|
+
try {
|
|
502
|
+
const [_, condition, body] = statement;
|
|
503
|
+
const condSnippet = this.typedExpression(condition, bool);
|
|
504
|
+
const conditionStr = this.ctx.resolve(condSnippet.value).value;
|
|
505
|
+
const bodyStr = this.block(blockifySingleStatement(body));
|
|
506
|
+
return `${this.ctx.pre}while (${conditionStr}) ${bodyStr}`;
|
|
507
|
+
} finally {
|
|
508
|
+
this.#unrolling = prevUnrollingFlag;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (statement[0] === NODE.forOf) {
|
|
512
|
+
const [_, loopVar, iterable, body] = statement;
|
|
513
|
+
if (loopVar[0] !== NODE.const) throw new WgslTypeError("Only `for (const ... of ... )` loops are supported");
|
|
514
|
+
let ctxIndent = false;
|
|
515
|
+
const prevUnrollingFlag = this.#unrolling;
|
|
516
|
+
try {
|
|
517
|
+
this.ctx.pushBlockScope();
|
|
518
|
+
const iterableExpr = this.expression(iterable);
|
|
519
|
+
const shouldUnroll = iterableExpr.value instanceof UnrollableIterable;
|
|
520
|
+
const iterableSnippet = shouldUnroll ? iterableExpr.value.snippet : iterableExpr;
|
|
521
|
+
const elementCountSnippet = getElementCountSnippet(this.ctx, iterableSnippet, shouldUnroll);
|
|
522
|
+
const originalLoopVarName = loopVar[1];
|
|
523
|
+
const blockified = blockifySingleStatement(body);
|
|
524
|
+
if (shouldUnroll) {
|
|
525
|
+
if (!isKnownAtComptime(elementCountSnippet)) throw new Error("Cannot unroll loop. Length of iterable is unknown at comptime.");
|
|
526
|
+
this.#unrolling = true;
|
|
527
|
+
const length = elementCountSnippet.value;
|
|
528
|
+
if (length === 0) return "";
|
|
529
|
+
const { value } = iterableSnippet;
|
|
530
|
+
const elements = value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
|
|
531
|
+
if (isEphemeralSnippet(elements[0]) && !isNaturallyEphemeral(elements[0]?.dataType)) throw new WgslTypeError("Cannot unroll loop. The elements of iterable are emphemeral but not naturally ephemeral.");
|
|
532
|
+
return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this.ctx.pre}${this.block(blockified, { [originalLoopVarName]: e })}`).join("\n");
|
|
533
|
+
}
|
|
534
|
+
if (isEphemeralSnippet(iterableSnippet)) throw new Error(`\`for ... of ...\` loops only support iterables stored in variables.
|
|
535
|
+
-----
|
|
536
|
+
You can wrap iterable with \`tgpu.unroll(...)\`. If iterable is known at comptime, the loop will be unrolled.
|
|
537
|
+
-----`);
|
|
538
|
+
this.#unrolling = false;
|
|
539
|
+
const index = this.ctx.makeNameValid("i");
|
|
540
|
+
const elementSnippet = getElementSnippet(iterableSnippet, snip(index, u32, "runtime"));
|
|
541
|
+
const loopVarName = this.ctx.makeNameValid(originalLoopVarName);
|
|
542
|
+
const loopVarKind = getLoopVarKind(elementSnippet);
|
|
543
|
+
const elementType = getElementType(elementSnippet, iterableSnippet);
|
|
544
|
+
const forHeaderStr = stitch`${this.ctx.pre}for (var ${index} = 0u; ${index} < ${elementCountSnippet}; ${index}++) {`;
|
|
545
|
+
this.ctx.indent();
|
|
546
|
+
ctxIndent = true;
|
|
547
|
+
const loopVarDeclStr = stitch`${this.ctx.pre}${loopVarKind} ${loopVarName} = ${tryConvertSnippet(this.ctx, elementSnippet, elementType, false)};`;
|
|
548
|
+
const bodyStr = `${this.ctx.pre}${this.block(blockified, { [originalLoopVarName]: snip(loopVarName, elementType, elementSnippet.origin) })}`;
|
|
549
|
+
this.ctx.dedent();
|
|
550
|
+
ctxIndent = false;
|
|
551
|
+
return stitch`${forHeaderStr}\n${loopVarDeclStr}\n${bodyStr}\n${this.ctx.pre}}`;
|
|
552
|
+
} finally {
|
|
553
|
+
if (ctxIndent) this.ctx.dedent();
|
|
554
|
+
this.#unrolling = prevUnrollingFlag;
|
|
555
|
+
this.ctx.popBlockScope();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (statement[0] === NODE.continue) {
|
|
559
|
+
if (this.#unrolling) throw new WgslTypeError("Cannot unroll loop containing `continue`");
|
|
560
|
+
return `${this.ctx.pre}continue;`;
|
|
561
|
+
}
|
|
562
|
+
if (statement[0] === NODE.break) {
|
|
563
|
+
if (this.#unrolling) throw new WgslTypeError("Cannot unroll loop containing `break`");
|
|
564
|
+
return `${this.ctx.pre}break;`;
|
|
565
|
+
}
|
|
566
|
+
const expr = this.expression(statement);
|
|
567
|
+
const resolved = expr.value && this.ctx.resolve(expr.value).value;
|
|
568
|
+
return resolved ? `${this.ctx.pre}${resolved};` : "";
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
function assertExhaustive(value) {
|
|
572
|
+
throw new Error(`'${safeStringify(value)}' was not handled by the WGSL generator.`);
|
|
573
|
+
}
|
|
574
|
+
function parseNumericString(str) {
|
|
575
|
+
if (/^0x[0-9a-f]+$/i.test(str)) return Number.parseInt(str);
|
|
576
|
+
if (/^0b[01]+$/i.test(str)) return Number.parseInt(str.slice(2), 2);
|
|
577
|
+
return Number.parseFloat(str);
|
|
578
|
+
}
|
|
579
|
+
function blockifySingleStatement(statement) {
|
|
580
|
+
return typeof statement !== "object" || statement[0] !== NODE.block ? [NODE.block, [statement]] : statement;
|
|
581
|
+
}
|
|
582
|
+
const wgslGenerator = new WgslGenerator();
|
|
583
|
+
|
|
584
|
+
//#endregion
|
|
585
|
+
export { wgslGenerator as default };
|