typegpu 0.10.2 → 0.11.0-rc.1
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/_virtual/_rolldown/runtime.js +6 -11
- package/builtin.js +1 -3
- package/common/fullScreenTriangle.d.ts +1 -5
- package/common/fullScreenTriangle.js +1 -3
- package/common/index.d.ts +3 -2
- package/common/index.js +6 -4
- package/common/writeSoA.d.ts +16 -0
- package/common/writeSoA.js +90 -0
- package/core/buffer/buffer.d.ts +12 -7
- package/core/buffer/buffer.js +102 -53
- package/core/buffer/bufferShorthand.d.ts +5 -5
- package/core/buffer/bufferShorthand.js +9 -5
- package/core/buffer/bufferUsage.d.ts +0 -2
- package/core/buffer/bufferUsage.js +6 -4
- package/core/constant/tgpuConstant.d.ts +2 -1
- package/core/constant/tgpuConstant.js +8 -7
- package/core/declare/tgpuDeclare.js +8 -9
- package/core/function/autoIO.d.ts +7 -6
- package/core/function/autoIO.js +1 -3
- package/core/function/comptime.js +1 -3
- package/core/function/createCallableSchema.js +4 -6
- package/core/function/dualImpl.js +1 -3
- package/core/function/entryInputRouter.js +39 -0
- package/core/function/extractArgs.js +2 -2
- package/core/function/fnCore.js +19 -8
- package/core/function/fnTypes.d.ts +14 -8
- package/core/function/ioSchema.js +24 -3
- package/core/function/shelllessImpl.js +1 -3
- package/core/function/templateUtils.js +1 -2
- package/core/function/tgpuComputeFn.d.ts +2 -3
- package/core/function/tgpuComputeFn.js +9 -16
- package/core/function/tgpuFn.d.ts +2 -2
- package/core/function/tgpuFn.js +1 -3
- package/core/function/tgpuFragmentFn.d.ts +5 -1
- package/core/function/tgpuFragmentFn.js +5 -10
- package/core/function/tgpuVertexFn.d.ts +4 -0
- package/core/function/tgpuVertexFn.js +6 -12
- package/core/pipeline/applyPipelineState.js +1 -3
- package/core/pipeline/computePipeline.d.ts +2 -6
- package/core/pipeline/computePipeline.js +64 -63
- package/core/pipeline/connectAttachmentToShader.js +1 -3
- package/core/pipeline/connectTargetsToShader.js +1 -3
- package/core/pipeline/limitsOverflow.js +1 -2
- package/core/pipeline/pipelineUtils.js +29 -0
- package/core/pipeline/renderPipeline.d.ts +23 -5
- package/core/pipeline/renderPipeline.js +32 -14
- package/core/pipeline/timeable.d.ts +0 -3
- package/core/pipeline/timeable.js +3 -9
- package/core/pipeline/typeGuards.js +1 -3
- package/core/querySet/querySet.d.ts +0 -2
- package/core/querySet/querySet.js +37 -36
- package/core/rawCodeSnippet/tgpuRawCodeSnippet.js +1 -3
- package/core/resolve/externals.d.ts +0 -2
- package/core/resolve/externals.js +2 -4
- package/core/resolve/namespace.js +1 -3
- package/core/resolve/resolveData.js +1 -3
- package/core/resolve/stitch.js +1 -3
- package/core/resolve/tgpuResolve.d.ts +3 -1
- package/core/resolve/tgpuResolve.js +3 -5
- package/core/root/configurableImpl.js +2 -3
- package/core/root/init.d.ts +0 -5
- package/core/root/init.js +35 -28
- package/core/root/rootTypes.d.ts +25 -5
- package/core/sampler/sampler.d.ts +0 -4
- package/core/sampler/sampler.js +3 -3
- package/core/simulate/tgpuSimulate.js +1 -3
- package/core/slot/accessor.d.ts +0 -4
- package/core/slot/accessor.js +1 -3
- package/core/slot/internalSlots.js +1 -3
- package/core/slot/lazy.js +1 -3
- package/core/slot/slot.js +2 -3
- package/core/slot/slotTypes.js +1 -3
- package/core/texture/externalTexture.d.ts +0 -6
- package/core/texture/externalTexture.js +2 -3
- package/core/texture/texture.d.ts +0 -4
- package/core/texture/texture.js +5 -3
- package/core/texture/textureFormats.js +1 -3
- package/core/texture/textureUtils.js +1 -3
- package/core/texture/usageExtension.js +1 -3
- package/core/unroll/tgpuUnroll.d.ts +58 -3
- package/core/unroll/tgpuUnroll.js +63 -5
- package/core/valueProxyUtils.js +1 -3
- package/core/variable/tgpuVariable.js +1 -3
- package/core/vertexLayout/connectAttributesToShader.js +1 -3
- package/core/vertexLayout/vertexLayout.js +9 -9
- package/data/alignIO.js +1 -2
- package/data/alignmentOf.d.ts +0 -1
- package/data/alignmentOf.js +1 -3
- package/data/array.d.ts +1 -3
- package/data/array.js +2 -4
- package/data/atomic.js +2 -3
- package/data/attributes.js +3 -3
- package/data/autoStruct.d.ts +1 -3
- package/data/autoStruct.js +1 -3
- package/data/compiledIO.js +83 -86
- package/data/dataIO.js +46 -39
- package/data/dataTypes.d.ts +7 -7
- package/data/dataTypes.js +6 -3
- package/data/deepEqual.js +1 -3
- package/data/disarray.d.ts +1 -3
- package/data/disarray.js +1 -3
- package/data/getLongestContiguousPrefix.d.ts +0 -1
- package/data/getLongestContiguousPrefix.js +1 -3
- package/data/index.d.ts +3 -3
- package/data/index.js +10 -3
- package/data/isContiguous.d.ts +0 -1
- package/data/isContiguous.js +1 -3
- package/data/matrix.d.ts +8 -10
- package/data/matrix.js +32 -18
- package/data/numberOps.js +1 -2
- package/data/numeric.js +16 -29
- package/data/offsetUtils.d.ts +2 -2
- package/data/offsetUtils.js +3 -5
- package/data/offsets.js +1 -3
- package/data/partialIO.js +84 -39
- package/data/ptr.d.ts +0 -1
- package/data/ptr.js +1 -3
- package/data/ref.d.ts +0 -3
- package/data/ref.js +1 -3
- package/data/sampler.js +1 -3
- package/data/schemaCallWrapper.js +1 -3
- package/data/schemaMemoryLayout.js +1 -3
- package/data/sizeOf.d.ts +0 -1
- package/data/sizeOf.js +1 -3
- package/data/snippet.js +12 -3
- package/data/struct.js +1 -3
- package/data/texture.js +1 -3
- package/data/unstruct.js +1 -3
- package/data/vector.js +4 -12
- package/data/vectorImpl.js +27 -28
- package/data/vectorOps.js +20 -3
- package/data/vertexFormatData.js +2 -3
- package/data/wgslTypes.d.ts +39 -11
- package/data/wgslTypes.js +10 -3
- package/errors.js +6 -3
- package/execMode.js +1 -3
- package/extension.js +1 -3
- package/getGPUValue.js +1 -3
- package/index.d.ts +4 -2
- package/index.js +3 -3
- package/indexNamedExports.d.ts +3 -1
- package/mathUtils.js +1 -2
- package/memo.js +8 -8
- package/nameRegistry.js +1 -3
- package/package.js +2 -3
- package/package.json +5 -5
- package/resolutionCtx.d.ts +0 -10
- package/resolutionCtx.js +84 -18
- package/shared/env.js +1 -2
- package/shared/generators.js +1 -2
- package/shared/meta.js +1 -3
- package/shared/repr.d.ts +32 -2
- package/shared/stringify.js +1 -3
- package/shared/symbols.d.ts +10 -1
- package/shared/symbols.js +10 -33
- package/shared/utilityTypes.d.ts +6 -2
- package/shared/utilityTypes.js +1 -2
- package/shared/vertexFormat.js +1 -2
- package/std/array.d.ts +1 -1
- package/std/array.js +1 -3
- package/std/atomic.d.ts +12 -12
- package/std/atomic.js +1 -3
- package/std/bitcast.d.ts +2 -2
- package/std/bitcast.js +1 -3
- package/std/boolean.d.ts +30 -16
- package/std/boolean.js +37 -12
- package/std/derivative.d.ts +9 -9
- package/std/derivative.js +1 -3
- package/std/discard.d.ts +1 -1
- package/std/discard.js +1 -3
- package/std/extensions.d.ts +1 -3
- package/std/extensions.js +1 -3
- package/std/index.d.ts +5 -4
- package/std/index.js +8 -5
- package/std/matrix.d.ts +5 -5
- package/std/matrix.js +1 -3
- package/std/numeric.d.ts +78 -132
- package/std/numeric.js +1 -3
- package/std/operators.d.ts +16 -8
- package/std/operators.js +80 -6
- package/std/packing.d.ts +4 -4
- package/std/packing.js +1 -3
- package/std/range.d.ts +24 -0
- package/std/range.js +38 -0
- package/std/subgroup.d.ts +21 -21
- package/std/subgroup.js +1 -3
- package/std/texture.d.ts +20 -11
- package/std/texture.js +13 -3
- package/tgpu.js +1 -3
- package/tgpuBindGroupLayout.js +9 -8
- package/tgpuUnstable.js +1 -3
- package/tgsl/accessIndex.js +2 -4
- package/tgsl/accessProp.js +8 -6
- package/tgsl/consoleLog/deserializers.js +1 -3
- package/tgsl/consoleLog/logGenerator.js +2 -4
- package/tgsl/consoleLog/serializers.js +24 -26
- package/tgsl/consoleLog/types.d.ts +0 -2
- package/tgsl/consoleLog/types.js +1 -2
- package/tgsl/conversion.js +1 -3
- package/tgsl/forOfUtils.js +35 -9
- package/tgsl/generationHelpers.js +3 -3
- package/tgsl/math.js +1 -3
- package/tgsl/shaderGenerator.d.ts +10 -8
- package/tgsl/shaderGenerator_members.d.ts +2 -0
- package/tgsl/shaderGenerator_members.js +6 -0
- package/tgsl/shellless.js +1 -8
- package/tgsl/wgslGenerator.d.ts +36 -0
- package/tgsl/wgslGenerator.js +135 -81
- package/types.d.ts +14 -4
- package/types.js +3 -3
- package/wgslExtensions.js +1 -2
|
@@ -7,7 +7,6 @@ import { getOwnSnippet } from "../types.js";
|
|
|
7
7
|
import { stitch } from "../core/resolve/stitch.js";
|
|
8
8
|
import { isRef } from "../data/ref.js";
|
|
9
9
|
import { abstractFloat, abstractInt, bool, f32, i32 } from "../data/numeric.js";
|
|
10
|
-
|
|
11
10
|
//#region src/tgsl/generationHelpers.ts
|
|
12
11
|
function numericLiteralToSnippet(value) {
|
|
13
12
|
if (value >= 2 ** 63 || value < -(2 ** 63)) return snip(value, abstractFloat, "constant");
|
|
@@ -46,6 +45,8 @@ function coerceToSnippet(value) {
|
|
|
46
45
|
*/
|
|
47
46
|
var ArrayExpression = class {
|
|
48
47
|
[$internal] = true;
|
|
48
|
+
type;
|
|
49
|
+
elements;
|
|
49
50
|
constructor(type, elements) {
|
|
50
51
|
this.type = type;
|
|
51
52
|
this.elements = elements;
|
|
@@ -62,6 +63,5 @@ var ArrayExpression = class {
|
|
|
62
63
|
return snip(stitch`${`array<${ctx.resolve(this.type.elementType).value}, ${this.elements.length}>`}(${this.elements})`, this.type, "runtime");
|
|
63
64
|
}
|
|
64
65
|
};
|
|
65
|
-
|
|
66
66
|
//#endregion
|
|
67
|
-
export { ArrayExpression, coerceToSnippet, concretize, concretizeSnippets, numericLiteralToSnippet };
|
|
67
|
+
export { ArrayExpression, coerceToSnippet, concretize, concretizeSnippets, numericLiteralToSnippet };
|
package/tgsl/math.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { f32 } from "../data/numeric.js";
|
|
2
2
|
import { abs, acos, acosh, asin, asinh, atan, atan2, atanh, ceil, cos, cosh, countLeadingZeros, exp, floor, log, log2, max, min, pow, sign, sin, sinh, sqrt, tan, tanh, trunc } from "../std/numeric.js";
|
|
3
|
-
|
|
4
3
|
//#region src/tgsl/math.ts
|
|
5
4
|
const mathToStd = {
|
|
6
5
|
abs,
|
|
@@ -40,6 +39,5 @@ const mathToStd = {
|
|
|
40
39
|
imul: void 0,
|
|
41
40
|
round: void 0
|
|
42
41
|
};
|
|
43
|
-
|
|
44
42
|
//#endregion
|
|
45
|
-
export { mathToStd };
|
|
43
|
+
export { mathToStd };
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { Snippet } from "../data/snippet.js";
|
|
1
|
+
import { ResolvedSnippet, Snippet } from "../data/snippet.js";
|
|
2
2
|
import { GenerationCtx } from "./generationHelpers.js";
|
|
3
|
-
import { ExternalMap } from "../core/resolve/externals.js";
|
|
4
3
|
import { BaseData } from "../data/wgslTypes.js";
|
|
5
|
-
import { Block
|
|
4
|
+
import { Block } from "tinyest";
|
|
6
5
|
|
|
7
6
|
//#region src/tgsl/shaderGenerator.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* **NOTE: This is an unstable API and may change in the future.**
|
|
9
|
+
*
|
|
10
|
+
* An interface meant to be used by other systems to generate snippets of
|
|
11
|
+
* shader code in the target language (WGSL, GLSL, etc.).
|
|
12
|
+
*/
|
|
8
13
|
interface ShaderGenerator {
|
|
9
14
|
initGenerator(ctx: GenerationCtx): void;
|
|
10
|
-
block(body: Block, externalMap?: ExternalMap): string;
|
|
11
|
-
identifier(id: string): Snippet;
|
|
12
|
-
typedExpression(expression: Expression, expectedType: BaseData): Snippet;
|
|
13
|
-
expression(expression: Expression): Snippet;
|
|
14
|
-
statement(statement: Statement): string;
|
|
15
15
|
functionDefinition(body: Block): string;
|
|
16
|
+
typeInstantiation(schema: BaseData, args: readonly Snippet[]): ResolvedSnippet;
|
|
17
|
+
typeAnnotation(schema: BaseData): string;
|
|
16
18
|
}
|
|
17
19
|
//#endregion
|
|
18
20
|
export { ShaderGenerator };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { UnknownData } from "../data/dataTypes.js";
|
|
3
|
+
//#region src/tgsl/shaderGenerator_members.ts
|
|
4
|
+
var shaderGenerator_members_exports = /* @__PURE__ */ __exportAll({ UnknownData: () => UnknownData });
|
|
5
|
+
//#endregion
|
|
6
|
+
export { shaderGenerator_members_exports };
|
package/tgsl/shellless.js
CHANGED
|
@@ -2,11 +2,9 @@ import { getMetaData, getName } from "../shared/meta.js";
|
|
|
2
2
|
import { isPtr, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
|
|
3
3
|
import { UnknownData } from "../data/dataTypes.js";
|
|
4
4
|
import { WgslTypeError } from "../errors.js";
|
|
5
|
-
import { getResolutionCtx } from "../execMode.js";
|
|
6
5
|
import { RefOperator } from "../data/ref.js";
|
|
7
6
|
import { concretize } from "./generationHelpers.js";
|
|
8
7
|
import { createShelllessImpl } from "../core/function/shelllessImpl.js";
|
|
9
|
-
|
|
10
8
|
//#region src/tgsl/shellless.ts
|
|
11
9
|
function shallowEqualSchemas(a, b) {
|
|
12
10
|
if (a.type !== b.type) return false;
|
|
@@ -28,10 +26,6 @@ var ShelllessRepository = class {
|
|
|
28
26
|
}
|
|
29
27
|
if (s.dataType === UnknownData) throw new Error(`Passed illegal value ${s.value} as the #${index} argument to ${meta.name}(...)\nShellless functions can only accept arguments representing WGSL resources: constructible WGSL types, d.refs, samplers or texture views.\nRemember, that arguments such as samplers, texture views, accessors, slots etc. should be dereferenced via '.$' first.`);
|
|
30
28
|
let type = concretize(s.dataType);
|
|
31
|
-
if (s.origin === "constant-tgpu-const-ref" || s.origin === "runtime-tgpu-const-ref") {
|
|
32
|
-
const ctx = getResolutionCtx();
|
|
33
|
-
throw new Error(`Cannot pass constant references as function arguments. Explicitly copy them by wrapping them in a schema: '${ctx.resolve(type).value}(...)'`);
|
|
34
|
-
}
|
|
35
29
|
if (isPtr(type) && type.implicit) type = type.inner;
|
|
36
30
|
return type;
|
|
37
31
|
});
|
|
@@ -48,6 +42,5 @@ var ShelllessRepository = class {
|
|
|
48
42
|
return shellless;
|
|
49
43
|
}
|
|
50
44
|
};
|
|
51
|
-
|
|
52
45
|
//#endregion
|
|
53
|
-
export { ShelllessRepository };
|
|
46
|
+
export { ShelllessRepository };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Origin, ResolvedSnippet, Snippet } from "../data/snippet.js";
|
|
2
|
+
import { GenerationCtx } from "./generationHelpers.js";
|
|
3
|
+
import { ShaderGenerator } from "./shaderGenerator.js";
|
|
4
|
+
import { BaseData, StorableData } from "../data/wgslTypes.js";
|
|
5
|
+
import { UnknownData } from "../data/dataTypes.js";
|
|
6
|
+
import { ExternalMap } from "../core/resolve/externals.js";
|
|
7
|
+
import * as tinyest from "tinyest";
|
|
8
|
+
|
|
9
|
+
//#region src/tgsl/wgslGenerator.d.ts
|
|
10
|
+
declare class WgslGenerator implements ShaderGenerator {
|
|
11
|
+
#private;
|
|
12
|
+
initGenerator(ctx: GenerationCtx): void;
|
|
13
|
+
protected get ctx(): GenerationCtx;
|
|
14
|
+
_block([_, statements]: tinyest.Block, externalMap?: ExternalMap): string;
|
|
15
|
+
refVariable(id: string, dataType: StorableData): string;
|
|
16
|
+
blockVariable(varType: 'var' | 'let' | 'const', id: string, dataType: BaseData | UnknownData, origin: Origin): Snippet;
|
|
17
|
+
protected emitVarDecl(pre: string, keyword: 'var' | 'let' | 'const', name: string, _dataType: BaseData | UnknownData, rhsStr: string): string;
|
|
18
|
+
_identifier(id: string): Snippet;
|
|
19
|
+
/**
|
|
20
|
+
* A wrapper for `generateExpression` that updates `ctx.expectedType`
|
|
21
|
+
* and tries to convert the result when it does not match the expected type.
|
|
22
|
+
*/
|
|
23
|
+
_typedExpression(expression: tinyest.Expression, expectedType: BaseData | BaseData[]): Snippet;
|
|
24
|
+
_expression(expression: tinyest.Expression): Snippet;
|
|
25
|
+
functionDefinition(body: tinyest.Block): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generates a WGSL type string for the given data type, and adds necessary
|
|
28
|
+
* definitions to the shader preamble. This shouldn't be called directly, only
|
|
29
|
+
* through `ctx.resolve` to properly cache the result.
|
|
30
|
+
*/
|
|
31
|
+
typeAnnotation(data: BaseData): string;
|
|
32
|
+
typeInstantiation(schema: BaseData, args: readonly Snippet[]): ResolvedSnippet;
|
|
33
|
+
_statement(statement: tinyest.Statement): string;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { WgslGenerator };
|
package/tgsl/wgslGenerator.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { $gpuCallable, $internal, $providing, isMarkedInternal } from "../shared/symbols.js";
|
|
2
2
|
import { getName } from "../shared/meta.js";
|
|
3
|
-
import { Void, isNaturallyEphemeral, isPtr, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
|
|
3
|
+
import { Void, isBool, isNaturallyEphemeral, isNumericSchema, isPtr, isVec, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
|
|
4
4
|
import { ConsoleLog, InfixDispatch, UnknownData, isLooseData, unptr } from "../data/dataTypes.js";
|
|
5
|
-
import { isEphemeralOrigin, isEphemeralSnippet, snip } from "../data/snippet.js";
|
|
5
|
+
import { fallthroughCopyOrigin, isEphemeralOrigin, isEphemeralSnippet, snip } from "../data/snippet.js";
|
|
6
6
|
import { ResolutionError, WgslTypeError, invariant } from "../errors.js";
|
|
7
7
|
import { isGPUCallable, isKnownAtComptime } from "../types.js";
|
|
8
8
|
import { stitch } from "../core/resolve/stitch.js";
|
|
@@ -11,8 +11,9 @@ import { RefOperator } 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";
|
|
14
|
-
import { AutoStruct } from "../data/autoStruct.js";
|
|
15
14
|
import { ArrayExpression, coerceToSnippet, concretize, numericLiteralToSnippet } from "./generationHelpers.js";
|
|
15
|
+
import { vec2u, vec3u, vec4u } from "../data/vector.js";
|
|
16
|
+
import { AutoStruct } from "../data/autoStruct.js";
|
|
16
17
|
import { add, div, mul, neg, sub } from "../std/operators.js";
|
|
17
18
|
import { accessProp } from "./accessProp.js";
|
|
18
19
|
import { accessIndex } from "./accessIndex.js";
|
|
@@ -20,11 +21,12 @@ import { constant } from "../core/constant/tgpuConstant.js";
|
|
|
20
21
|
import { isGenericFn } from "../core/function/tgpuFn.js";
|
|
21
22
|
import { arrayOf } from "../data/array.js";
|
|
22
23
|
import { pow } from "../std/numeric.js";
|
|
24
|
+
import { resolveData } from "../core/resolve/resolveData.js";
|
|
23
25
|
import { UnrollableIterable } from "../core/unroll/tgpuUnroll.js";
|
|
24
26
|
import { mathToStd } from "./math.js";
|
|
25
|
-
import {
|
|
27
|
+
import { isTgpuRange } from "../std/range.js";
|
|
28
|
+
import { getElementSnippet, getElementType, getLoopVarKind, getRangeSnippets } from "./forOfUtils.js";
|
|
26
29
|
import * as tinyest from "tinyest";
|
|
27
|
-
|
|
28
30
|
//#region src/tgsl/wgslGenerator.ts
|
|
29
31
|
const { NodeTypeCatalog: NODE } = tinyest;
|
|
30
32
|
const parenthesizedOps = [
|
|
@@ -61,6 +63,12 @@ const binaryLogicalOps = [
|
|
|
61
63
|
">",
|
|
62
64
|
">="
|
|
63
65
|
];
|
|
66
|
+
const bitShiftOps = [
|
|
67
|
+
"<<",
|
|
68
|
+
">>",
|
|
69
|
+
"<<=",
|
|
70
|
+
">>="
|
|
71
|
+
];
|
|
64
72
|
const OP_MAP = {
|
|
65
73
|
"===": "==",
|
|
66
74
|
"!==": "!=",
|
|
@@ -106,7 +114,19 @@ function operatorToType(lhs, op, rhs) {
|
|
|
106
114
|
}
|
|
107
115
|
const unaryOpCodeToCodegen = {
|
|
108
116
|
"-": neg[$gpuCallable].call.bind(neg),
|
|
109
|
-
void: () => snip(void 0, Void, "constant")
|
|
117
|
+
void: () => snip(void 0, Void, "constant"),
|
|
118
|
+
"!": (ctx, [argExpr]) => {
|
|
119
|
+
if (argExpr === void 0) throw new Error("The unary operator `!` expects 1 argument, but 0 were provided.");
|
|
120
|
+
if (isKnownAtComptime(argExpr)) return snip(!argExpr.value, bool, "constant");
|
|
121
|
+
const { value, dataType } = argExpr;
|
|
122
|
+
const argStr = ctx.resolve(value, dataType).value;
|
|
123
|
+
if (isBool(dataType)) return snip(`!${argStr}`, bool, "runtime");
|
|
124
|
+
if (isNumericSchema(dataType)) {
|
|
125
|
+
const resultStr = `!bool(${argStr})`;
|
|
126
|
+
return snip(dataType.type === "f32" ? `(((bitcast<u32>(${argStr}) & 0x7fffffff) > 0x7f800000) || ${resultStr})` : dataType.type === "f16" ? `(((bitcast<u32>(${argStr}) & 0x7fff) > 0x7c00) || ${resultStr})` : resultStr, bool, "runtime");
|
|
127
|
+
}
|
|
128
|
+
return snip(false, bool, "constant");
|
|
129
|
+
}
|
|
110
130
|
};
|
|
111
131
|
const binaryOpCodeToCodegen = {
|
|
112
132
|
"+": add[$gpuCallable].call.bind(add),
|
|
@@ -125,7 +145,7 @@ var WgslGenerator = class {
|
|
|
125
145
|
if (!this.#ctx) throw new Error("WGSL Generator has not yet been initialized. Please call initialize(ctx) before using the generator.");
|
|
126
146
|
return this.#ctx;
|
|
127
147
|
}
|
|
128
|
-
|
|
148
|
+
_block([_, statements], externalMap) {
|
|
129
149
|
this.ctx.pushBlockScope();
|
|
130
150
|
if (externalMap) {
|
|
131
151
|
const externals = Object.fromEntries(Object.entries(externalMap).map(([id, value]) => [id, coerceToSnippet(value)]));
|
|
@@ -133,7 +153,7 @@ var WgslGenerator = class {
|
|
|
133
153
|
}
|
|
134
154
|
try {
|
|
135
155
|
this.ctx.indent();
|
|
136
|
-
const body = statements.map((statement) => this.
|
|
156
|
+
const body = statements.map((statement) => this._statement(statement)).filter((statement) => statement.length > 0).join("\n");
|
|
137
157
|
this.ctx.dedent();
|
|
138
158
|
return `{
|
|
139
159
|
${body}
|
|
@@ -162,7 +182,10 @@ ${this.ctx.pre}}`;
|
|
|
162
182
|
this.ctx.defineVariable(id, snippet);
|
|
163
183
|
return snippet;
|
|
164
184
|
}
|
|
165
|
-
|
|
185
|
+
emitVarDecl(pre, keyword, name, _dataType, rhsStr) {
|
|
186
|
+
return `${pre}${keyword} ${name} = ${rhsStr};`;
|
|
187
|
+
}
|
|
188
|
+
_identifier(id) {
|
|
166
189
|
if (!id) throw new Error("Cannot resolve an empty identifier");
|
|
167
190
|
if (id === "undefined") return snip(void 0, Void, "constant");
|
|
168
191
|
const res = this.ctx.getById(id);
|
|
@@ -173,24 +196,24 @@ ${this.ctx.pre}}`;
|
|
|
173
196
|
* A wrapper for `generateExpression` that updates `ctx.expectedType`
|
|
174
197
|
* and tries to convert the result when it does not match the expected type.
|
|
175
198
|
*/
|
|
176
|
-
|
|
199
|
+
_typedExpression(expression, expectedType) {
|
|
177
200
|
const prevExpectedType = this.ctx.expectedType;
|
|
178
201
|
this.ctx.expectedType = expectedType;
|
|
179
202
|
try {
|
|
180
|
-
const result = this.
|
|
203
|
+
const result = this._expression(expression);
|
|
181
204
|
if (expectedType instanceof AutoStruct) return result;
|
|
182
205
|
return tryConvertSnippet(this.ctx, result, expectedType);
|
|
183
206
|
} finally {
|
|
184
207
|
this.ctx.expectedType = prevExpectedType;
|
|
185
208
|
}
|
|
186
209
|
}
|
|
187
|
-
|
|
188
|
-
if (typeof expression === "string") return this.
|
|
210
|
+
_expression(expression) {
|
|
211
|
+
if (typeof expression === "string") return this._identifier(expression);
|
|
189
212
|
if (typeof expression === "boolean") return snip(expression, bool, "constant");
|
|
190
213
|
if (expression[0] === NODE.logicalExpr || expression[0] === NODE.binaryExpr || expression[0] === NODE.assignmentExpr) {
|
|
191
214
|
const [exprType, lhs, op, rhs] = expression;
|
|
192
|
-
const lhsExpr = this.
|
|
193
|
-
const rhsExpr = this.
|
|
215
|
+
const lhsExpr = this._expression(lhs);
|
|
216
|
+
const rhsExpr = this._expression(rhs);
|
|
194
217
|
if (rhsExpr.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot assign a ref to an existing variable '${lhsExpr}', define a new variable instead.`);
|
|
195
218
|
if (op === "==") throw new Error("Please use the === operator instead of ==");
|
|
196
219
|
if (op === "===" && isKnownAtComptime(lhsExpr) && isKnownAtComptime(rhsExpr)) return snip(lhsExpr.value === rhsExpr.value, bool, "constant");
|
|
@@ -198,8 +221,20 @@ ${this.ctx.pre}}`;
|
|
|
198
221
|
if (rhsExpr.dataType === UnknownData) throw new WgslTypeError(`Right-hand side of '${op}' is of unknown type`);
|
|
199
222
|
const codegen = binaryOpCodeToCodegen[op];
|
|
200
223
|
if (codegen) return codegen(this.ctx, [lhsExpr, rhsExpr]);
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
let convLhs;
|
|
225
|
+
let convRhs;
|
|
226
|
+
if (bitShiftOps.includes(op)) {
|
|
227
|
+
let rhsTarget;
|
|
228
|
+
if (isVec(lhsExpr.dataType)) {
|
|
229
|
+
const cc = lhsExpr.dataType.componentCount;
|
|
230
|
+
rhsTarget = cc === 2 ? vec2u : cc === 3 ? vec3u : vec4u;
|
|
231
|
+
} else rhsTarget = u32;
|
|
232
|
+
convRhs = tryConvertSnippet(this.ctx, rhsExpr, rhsTarget, false);
|
|
233
|
+
convLhs = lhsExpr;
|
|
234
|
+
} else {
|
|
235
|
+
const forcedType = exprType === NODE.assignmentExpr ? [lhsExpr.dataType] : void 0;
|
|
236
|
+
[convLhs, convRhs] = convertToCommonType(this.ctx, [lhsExpr, rhsExpr], forcedType) ?? [lhsExpr, rhsExpr];
|
|
237
|
+
}
|
|
203
238
|
const lhsStr = this.ctx.resolve(convLhs.value, convLhs.dataType).value;
|
|
204
239
|
const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
|
|
205
240
|
const type = operatorToType(convLhs.dataType, op, convRhs.dataType);
|
|
@@ -213,13 +248,13 @@ ${this.ctx.pre}}`;
|
|
|
213
248
|
}
|
|
214
249
|
if (expression[0] === NODE.postUpdate) {
|
|
215
250
|
const [_, op, arg] = expression;
|
|
216
|
-
const argExpr = this.
|
|
251
|
+
const argExpr = this._expression(arg);
|
|
217
252
|
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
218
253
|
return snip(`${argStr}${op}`, argExpr.dataType, "runtime");
|
|
219
254
|
}
|
|
220
255
|
if (expression[0] === NODE.unaryExpr) {
|
|
221
256
|
const [_, op, arg] = expression;
|
|
222
|
-
const argExpr = this.
|
|
257
|
+
const argExpr = this._expression(arg);
|
|
223
258
|
const codegen = unaryOpCodeToCodegen[op];
|
|
224
259
|
if (codegen) return codegen(this.ctx, [argExpr]);
|
|
225
260
|
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
@@ -228,7 +263,7 @@ ${this.ctx.pre}}`;
|
|
|
228
263
|
}
|
|
229
264
|
if (expression[0] === NODE.memberAccess) {
|
|
230
265
|
const [_, targetNode, property] = expression;
|
|
231
|
-
const target = this.
|
|
266
|
+
const target = this._expression(targetNode);
|
|
232
267
|
if (target.value === console) return snip(new ConsoleLog(property), UnknownData, "runtime");
|
|
233
268
|
if (target.value === Math) {
|
|
234
269
|
if (property in mathToStd && mathToStd[property]) return snip(mathToStd[property], UnknownData, "runtime");
|
|
@@ -240,8 +275,8 @@ ${this.ctx.pre}}`;
|
|
|
240
275
|
}
|
|
241
276
|
if (expression[0] === NODE.indexAccess) {
|
|
242
277
|
const [_, targetNode, propertyNode] = expression;
|
|
243
|
-
const target = this.
|
|
244
|
-
const inProperty = this.
|
|
278
|
+
const target = this._expression(targetNode);
|
|
279
|
+
const inProperty = this._expression(propertyNode);
|
|
245
280
|
const property = convertToCommonType(this.ctx, [inProperty], [u32, i32], false)?.[0] ?? inProperty;
|
|
246
281
|
const accessed = accessIndex(target, property);
|
|
247
282
|
if (!accessed) {
|
|
@@ -258,27 +293,27 @@ ${this.ctx.pre}}`;
|
|
|
258
293
|
}
|
|
259
294
|
if (expression[0] === NODE.call) {
|
|
260
295
|
const [_, calleeNode, argNodes] = expression;
|
|
261
|
-
const callee = this.
|
|
296
|
+
const callee = this._expression(calleeNode);
|
|
262
297
|
if (isWgslStruct(callee.value)) {
|
|
263
298
|
if (argNodes.length > 1) throw new WgslTypeError("Struct schemas should always be called with at most 1 argument");
|
|
264
299
|
if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
|
|
265
|
-
const arg = this.
|
|
300
|
+
const arg = this._typedExpression(argNodes[0], callee.value);
|
|
266
301
|
return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
|
|
267
302
|
}
|
|
268
303
|
if (isWgslArray(callee.value)) {
|
|
269
304
|
if (argNodes.length > 1) throw new WgslTypeError("Array schemas should always be called with at most 1 argument");
|
|
270
305
|
if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
|
|
271
|
-
const arg = this.
|
|
306
|
+
const arg = this._typedExpression(argNodes[0], callee.value);
|
|
272
307
|
if (arg.value instanceof ArrayExpression) return snip(stitch`${this.ctx.resolve(callee.value).value}(${arg.value.elements})`, arg.dataType, "runtime");
|
|
273
308
|
return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
|
|
274
309
|
}
|
|
275
310
|
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
311
|
if (callee.value instanceof InfixDispatch) {
|
|
277
312
|
if (!argNodes[0]) throw new WgslTypeError(`An infix operator '${callee.value.name}' was called without any arguments`);
|
|
278
|
-
const rhs = this.
|
|
313
|
+
const rhs = this._expression(argNodes[0]);
|
|
279
314
|
return callee.value.operator(this.ctx, [callee.value.lhs, rhs]);
|
|
280
315
|
}
|
|
281
|
-
if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this.
|
|
316
|
+
if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this._expression(arg)));
|
|
282
317
|
if (isGPUCallable(callee.value)) {
|
|
283
318
|
const callable = callee.value[$gpuCallable];
|
|
284
319
|
const strictSignature = callable.strictSignature;
|
|
@@ -286,9 +321,9 @@ ${this.ctx.pre}}`;
|
|
|
286
321
|
if (strictSignature) convertedArguments = argNodes.map((arg, i) => {
|
|
287
322
|
const argType = strictSignature.argTypes[i];
|
|
288
323
|
if (!argType) throw new WgslTypeError(`Function '${getName(callee.value)}' was called with too many arguments`);
|
|
289
|
-
return this.
|
|
324
|
+
return this._typedExpression(arg, argType);
|
|
290
325
|
});
|
|
291
|
-
else convertedArguments = argNodes.map((arg) => this.
|
|
326
|
+
else convertedArguments = argNodes.map((arg) => this._expression(arg));
|
|
292
327
|
try {
|
|
293
328
|
return callable.call(this.ctx, convertedArguments);
|
|
294
329
|
} catch (err) {
|
|
@@ -301,7 +336,7 @@ ${this.ctx.pre}}`;
|
|
|
301
336
|
const slotPairs = isGeneric ? callee.value[$providing]?.pairs ?? [] : [];
|
|
302
337
|
const callback = isGeneric ? callee.value[$internal].inner : callee.value;
|
|
303
338
|
const shelllessCall = this.ctx.withRenamed(callback, getName(callee.value), () => this.ctx.withSlots(slotPairs, () => {
|
|
304
|
-
const args = argNodes.map((arg) => this.
|
|
339
|
+
const args = argNodes.map((arg) => this._expression(arg));
|
|
305
340
|
const shellless = this.ctx.shelllessRepo.get(callback, args);
|
|
306
341
|
if (!shellless) return;
|
|
307
342
|
const converted = args.map((s, idx) => {
|
|
@@ -324,11 +359,11 @@ ${this.ctx.pre}}`;
|
|
|
324
359
|
const entries = Object.fromEntries(Object.entries(obj).map(([key, value]) => {
|
|
325
360
|
let accessed = structType.accessProp(key);
|
|
326
361
|
let expr;
|
|
327
|
-
if (accessed) expr = this.
|
|
362
|
+
if (accessed) expr = this._typedExpression(value, accessed.type);
|
|
328
363
|
else {
|
|
329
|
-
expr = this.
|
|
364
|
+
expr = this._expression(value);
|
|
330
365
|
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));
|
|
366
|
+
accessed = structType.provideProp(key, unptr(concretize(expr.dataType)));
|
|
332
367
|
}
|
|
333
368
|
return [accessed.prop, expr];
|
|
334
369
|
}));
|
|
@@ -340,7 +375,7 @@ ${this.ctx.pre}}`;
|
|
|
340
375
|
const entries = Object.fromEntries(Object.entries(structType.propTypes).map(([key, value]) => {
|
|
341
376
|
const val = obj[key];
|
|
342
377
|
if (val === void 0) throw new WgslTypeError(`Missing property ${key} in object literal for struct ${structType}`);
|
|
343
|
-
return [key, this.
|
|
378
|
+
return [key, this._typedExpression(val, value)];
|
|
344
379
|
}));
|
|
345
380
|
const convertedSnippets = convertStructValues(this.ctx, structType, entries);
|
|
346
381
|
return snip(stitch`${this.ctx.resolve(structType).value}(${convertedSnippets})`, structType, "runtime");
|
|
@@ -354,10 +389,10 @@ ${this.ctx.pre}}`;
|
|
|
354
389
|
let values;
|
|
355
390
|
if (isWgslArray(arrType)) {
|
|
356
391
|
elemType = arrType.elementType;
|
|
357
|
-
values = valueNodes.map((value) => this.
|
|
392
|
+
values = valueNodes.map((value) => this._typedExpression(value, elemType));
|
|
358
393
|
if (values.length !== arrType.elementCount) throw new WgslTypeError(`Cannot create value of type '${arrType}' from an array of length: ${values.length}`);
|
|
359
394
|
} else {
|
|
360
|
-
const valuesSnippets = valueNodes.map((value) => this.
|
|
395
|
+
const valuesSnippets = valueNodes.map((value) => this._expression(value));
|
|
361
396
|
if (valuesSnippets.length === 0) throw new WgslTypeError("Cannot infer the type of an empty array literal.");
|
|
362
397
|
const converted = convertToCommonType(this.ctx, valuesSnippets);
|
|
363
398
|
if (!converted) throw new WgslTypeError("The given values cannot be automatically converted to a common type. Consider wrapping the array in an appropriate schema");
|
|
@@ -369,8 +404,8 @@ ${this.ctx.pre}}`;
|
|
|
369
404
|
}
|
|
370
405
|
if (expression[0] === NODE.conditionalExpr) {
|
|
371
406
|
const [_, test, consequent, alternative] = expression;
|
|
372
|
-
const testExpression = this.
|
|
373
|
-
if (isKnownAtComptime(testExpression)) return testExpression.value ? this.
|
|
407
|
+
const testExpression = this._expression(test);
|
|
408
|
+
if (isKnownAtComptime(testExpression)) return testExpression.value ? this._expression(consequent) : this._expression(alternative);
|
|
374
409
|
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
410
|
}
|
|
376
411
|
if (expression[0] === NODE.stringLiteral) return snip(expression[1], UnknownData, "constant");
|
|
@@ -378,11 +413,23 @@ ${this.ctx.pre}}`;
|
|
|
378
413
|
assertExhaustive(expression);
|
|
379
414
|
}
|
|
380
415
|
functionDefinition(body) {
|
|
381
|
-
return this.
|
|
416
|
+
return this._block(body);
|
|
382
417
|
}
|
|
383
|
-
|
|
418
|
+
/**
|
|
419
|
+
* Generates a WGSL type string for the given data type, and adds necessary
|
|
420
|
+
* definitions to the shader preamble. This shouldn't be called directly, only
|
|
421
|
+
* through `ctx.resolve` to properly cache the result.
|
|
422
|
+
*/
|
|
423
|
+
typeAnnotation(data) {
|
|
424
|
+
return resolveData(this.ctx, data);
|
|
425
|
+
}
|
|
426
|
+
typeInstantiation(schema, args) {
|
|
427
|
+
if (args.length === 1 && args[0]?.dataType === schema) return snip(stitch`${args[0]}`, schema, fallthroughCopyOrigin(args[0].origin));
|
|
428
|
+
return snip(stitch`${this.ctx.resolve(schema).value}(${args})`, schema, "runtime");
|
|
429
|
+
}
|
|
430
|
+
_statement(statement) {
|
|
384
431
|
if (typeof statement === "string") {
|
|
385
|
-
const id = this.
|
|
432
|
+
const id = this._identifier(statement);
|
|
386
433
|
const resolved = id.value && this.ctx.resolve(id.value).value;
|
|
387
434
|
return resolved ? `${this.ctx.pre}${resolved};` : "";
|
|
388
435
|
}
|
|
@@ -391,7 +438,7 @@ ${this.ctx.pre}}`;
|
|
|
391
438
|
const returnNode = statement[1];
|
|
392
439
|
if (returnNode !== void 0) {
|
|
393
440
|
const expectedReturnType = this.ctx.topFunctionReturnType;
|
|
394
|
-
let returnSnippet = expectedReturnType ? this.
|
|
441
|
+
let returnSnippet = expectedReturnType ? this._typedExpression(returnNode, expectedReturnType) : this._expression(returnNode);
|
|
395
442
|
if (returnSnippet.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot return references, returning '${returnSnippet.value.snippet}'`);
|
|
396
443
|
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
444
|
if (!expectedReturnType && !isEphemeralSnippet(returnSnippet) && returnSnippet.origin !== "this-function") {
|
|
@@ -411,11 +458,17 @@ Try 'return ${typeStr}(${str});' instead.
|
|
|
411
458
|
}
|
|
412
459
|
if (statement[0] === NODE.if) {
|
|
413
460
|
const [_, condNode, consNode, altNode] = statement;
|
|
414
|
-
const condition = this.
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
461
|
+
const condition = this._typedExpression(condNode, bool);
|
|
462
|
+
if (typeof condition.value === "boolean") {
|
|
463
|
+
let node = condition.value ? consNode : altNode;
|
|
464
|
+
if (node === void 0) return "";
|
|
465
|
+
if (!Array.isArray(node)) node = blockifySingleStatement(node);
|
|
466
|
+
if (node[0] === NODE.block && node[1].length === 1 && node[1][0][0] === NODE.if) return this._statement(node[1][0]);
|
|
467
|
+
if (node[0] === NODE.if) return this._statement(node);
|
|
468
|
+
return `${this.ctx.pre}${this._block(blockifySingleStatement(node))}`;
|
|
469
|
+
}
|
|
470
|
+
const consequent = this._block(blockifySingleStatement(consNode));
|
|
471
|
+
const alternate = !altNode ? void 0 : this._block(blockifySingleStatement(altNode));
|
|
419
472
|
if (!alternate) return stitch`${this.ctx.pre}if (${condition}) ${consequent}`;
|
|
420
473
|
return stitch`\
|
|
421
474
|
${this.ctx.pre}if (${condition}) ${consequent}
|
|
@@ -424,7 +477,7 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
424
477
|
if (statement[0] === NODE.let || statement[0] === NODE.const) {
|
|
425
478
|
let varType = "var";
|
|
426
479
|
const [stmtType, rawId, rawValue] = statement;
|
|
427
|
-
const eq = rawValue !== void 0 ? this.
|
|
480
|
+
const eq = rawValue !== void 0 ? this._expression(rawValue) : void 0;
|
|
428
481
|
if (!eq) throw new Error(`Cannot create variable '${rawId}' without an initial value.`);
|
|
429
482
|
const ephemeral = isEphemeralSnippet(eq);
|
|
430
483
|
let dataType = eq.dataType;
|
|
@@ -472,9 +525,11 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
472
525
|
}
|
|
473
526
|
}
|
|
474
527
|
const snippet = this.blockVariable(varType, rawId, concretize(dataType), eq.origin);
|
|
475
|
-
|
|
528
|
+
const rhsSnippet = tryConvertSnippet(this.ctx, eq, dataType, false);
|
|
529
|
+
const rhsStr = this.ctx.resolve(rhsSnippet.value, rhsSnippet.dataType).value;
|
|
530
|
+
return this.emitVarDecl(this.ctx.pre, varType, snippet.value, concretize(dataType), rhsStr);
|
|
476
531
|
}
|
|
477
|
-
if (statement[0] === NODE.block) return `${this.ctx.pre}${this.
|
|
532
|
+
if (statement[0] === NODE.block) return `${this.ctx.pre}${this._block(statement)}`;
|
|
478
533
|
if (statement[0] === NODE.for) {
|
|
479
534
|
const [_, init, condition, update, body] = statement;
|
|
480
535
|
const prevUnrollingFlag = this.#unrolling;
|
|
@@ -482,13 +537,13 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
482
537
|
try {
|
|
483
538
|
this.ctx.pushBlockScope();
|
|
484
539
|
const [initStatement, conditionExpr, updateStatement] = this.ctx.withResetIndentLevel(() => [
|
|
485
|
-
init ? this.
|
|
486
|
-
condition ? this.
|
|
487
|
-
update ? this.
|
|
540
|
+
init ? this._statement(init) : void 0,
|
|
541
|
+
condition ? this._typedExpression(condition, bool) : void 0,
|
|
542
|
+
update ? this._statement(update) : void 0
|
|
488
543
|
]);
|
|
489
544
|
const initStr = initStatement ? initStatement.slice(0, -1) : "";
|
|
490
545
|
const updateStr = updateStatement ? updateStatement.slice(0, -1) : "";
|
|
491
|
-
const bodyStr = this.
|
|
546
|
+
const bodyStr = this._block(blockifySingleStatement(body));
|
|
492
547
|
return stitch`${this.ctx.pre}for (${initStr}; ${conditionExpr}; ${updateStr}) ${bodyStr}`;
|
|
493
548
|
} finally {
|
|
494
549
|
this.#unrolling = prevUnrollingFlag;
|
|
@@ -500,9 +555,9 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
500
555
|
this.#unrolling = false;
|
|
501
556
|
try {
|
|
502
557
|
const [_, condition, body] = statement;
|
|
503
|
-
const condSnippet = this.
|
|
558
|
+
const condSnippet = this._typedExpression(condition, bool);
|
|
504
559
|
const conditionStr = this.ctx.resolve(condSnippet.value).value;
|
|
505
|
-
const bodyStr = this.
|
|
560
|
+
const bodyStr = this._block(blockifySingleStatement(body));
|
|
506
561
|
return `${this.ctx.pre}while (${conditionStr}) ${bodyStr}`;
|
|
507
562
|
} finally {
|
|
508
563
|
this.#unrolling = prevUnrollingFlag;
|
|
@@ -515,40 +570,40 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
515
570
|
const prevUnrollingFlag = this.#unrolling;
|
|
516
571
|
try {
|
|
517
572
|
this.ctx.pushBlockScope();
|
|
518
|
-
const iterableExpr = this.
|
|
573
|
+
const iterableExpr = this._expression(iterable);
|
|
519
574
|
const shouldUnroll = iterableExpr.value instanceof UnrollableIterable;
|
|
520
575
|
const iterableSnippet = shouldUnroll ? iterableExpr.value.snippet : iterableExpr;
|
|
521
|
-
const
|
|
576
|
+
const range = getRangeSnippets(this.ctx, iterableSnippet, shouldUnroll);
|
|
522
577
|
const originalLoopVarName = loopVar[1];
|
|
523
578
|
const blockified = blockifySingleStatement(body);
|
|
524
579
|
if (shouldUnroll) {
|
|
525
|
-
if (!isKnownAtComptime(
|
|
580
|
+
if (!isKnownAtComptime(range.end)) throw new Error("Cannot unroll loop. Length of iterable is unknown at comptime.");
|
|
526
581
|
this.#unrolling = true;
|
|
527
|
-
const length =
|
|
582
|
+
const length = range.end.value;
|
|
528
583
|
if (length === 0) return "";
|
|
529
584
|
const { value } = iterableSnippet;
|
|
530
|
-
const elements = value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
|
|
585
|
+
const elements = isTgpuRange(value) ? value.map((i) => coerceToSnippet(i)) : value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
|
|
531
586
|
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.
|
|
587
|
+
return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this.ctx.pre}${this._block(blockified, { [originalLoopVarName]: e })}`).join("\n");
|
|
533
588
|
}
|
|
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
589
|
this.#unrolling = false;
|
|
539
590
|
const index = this.ctx.makeNameValid("i");
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
591
|
+
const forHeaderStr = stitch`${this.ctx.pre}for (var ${index} = ${range.start}; ${index} ${range.comparison} ${range.end}; ${index} += ${range.step})`;
|
|
592
|
+
let bodyStr = "";
|
|
593
|
+
if (isTgpuRange(iterableSnippet.value)) bodyStr = this._block(blockified, { [originalLoopVarName]: snip(index, u32, "runtime") });
|
|
594
|
+
else {
|
|
595
|
+
this.ctx.indent();
|
|
596
|
+
ctxIndent = true;
|
|
597
|
+
const loopVarName = this.ctx.makeNameValid(originalLoopVarName);
|
|
598
|
+
const elementSnippet = getElementSnippet(iterableSnippet, snip(index, u32, "runtime"));
|
|
599
|
+
const loopVarKind = getLoopVarKind(elementSnippet);
|
|
600
|
+
const elementType = getElementType(elementSnippet, iterableSnippet);
|
|
601
|
+
bodyStr = `{\n${stitch`${this.ctx.pre}${loopVarKind} ${loopVarName} = ${tryConvertSnippet(this.ctx, elementSnippet, elementType, false)};`}\n${this.ctx.pre}${this._block(blockified, { [originalLoopVarName]: snip(loopVarName, elementType, elementSnippet.origin) })}\n`;
|
|
602
|
+
this.ctx.dedent();
|
|
603
|
+
bodyStr += `${this.ctx.pre}}`;
|
|
604
|
+
ctxIndent = false;
|
|
605
|
+
}
|
|
606
|
+
return stitch`${forHeaderStr} ${bodyStr.trim()}`;
|
|
552
607
|
} finally {
|
|
553
608
|
if (ctxIndent) this.ctx.dedent();
|
|
554
609
|
this.#unrolling = prevUnrollingFlag;
|
|
@@ -563,7 +618,7 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
563
618
|
if (this.#unrolling) throw new WgslTypeError("Cannot unroll loop containing `break`");
|
|
564
619
|
return `${this.ctx.pre}break;`;
|
|
565
620
|
}
|
|
566
|
-
const expr = this.
|
|
621
|
+
const expr = this._expression(statement);
|
|
567
622
|
const resolved = expr.value && this.ctx.resolve(expr.value).value;
|
|
568
623
|
return resolved ? `${this.ctx.pre}${resolved};` : "";
|
|
569
624
|
}
|
|
@@ -580,6 +635,5 @@ function blockifySingleStatement(statement) {
|
|
|
580
635
|
return typeof statement !== "object" || statement[0] !== NODE.block ? [NODE.block, [statement]] : statement;
|
|
581
636
|
}
|
|
582
637
|
const wgslGenerator = new WgslGenerator();
|
|
583
|
-
|
|
584
638
|
//#endregion
|
|
585
|
-
export { wgslGenerator as default };
|
|
639
|
+
export { WgslGenerator, wgslGenerator as default };
|