typegpu 0.10.2 → 0.11.0
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 +7 -7
- 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 +144 -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,33 @@ ${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
|
-
|
|
215
|
+
const lhsExpr = this._expression(lhs);
|
|
216
|
+
if ((op === "||" || op === "&&") && isKnownAtComptime(lhsExpr)) {
|
|
217
|
+
if (!(op === "&&" ? lhsExpr.value : !lhsExpr.value)) return snip(op === "||", bool, "constant");
|
|
218
|
+
const rhsExpr = this._expression(rhs);
|
|
219
|
+
if (rhsExpr.dataType === UnknownData) throw new WgslTypeError(`Right-hand side of '${op}' is of unknown type`);
|
|
220
|
+
if (isKnownAtComptime(rhsExpr)) return snip(!!rhsExpr.value, bool, "constant");
|
|
221
|
+
const convRhs = tryConvertSnippet(this.ctx, rhsExpr, bool, false);
|
|
222
|
+
const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
|
|
223
|
+
return snip(rhsStr, bool, "runtime");
|
|
224
|
+
}
|
|
225
|
+
const rhsExpr = this._expression(rhs);
|
|
194
226
|
if (rhsExpr.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot assign a ref to an existing variable '${lhsExpr}', define a new variable instead.`);
|
|
195
227
|
if (op === "==") throw new Error("Please use the === operator instead of ==");
|
|
196
228
|
if (op === "===" && isKnownAtComptime(lhsExpr) && isKnownAtComptime(rhsExpr)) return snip(lhsExpr.value === rhsExpr.value, bool, "constant");
|
|
@@ -198,8 +230,20 @@ ${this.ctx.pre}}`;
|
|
|
198
230
|
if (rhsExpr.dataType === UnknownData) throw new WgslTypeError(`Right-hand side of '${op}' is of unknown type`);
|
|
199
231
|
const codegen = binaryOpCodeToCodegen[op];
|
|
200
232
|
if (codegen) return codegen(this.ctx, [lhsExpr, rhsExpr]);
|
|
201
|
-
|
|
202
|
-
|
|
233
|
+
let convLhs;
|
|
234
|
+
let convRhs;
|
|
235
|
+
if (bitShiftOps.includes(op)) {
|
|
236
|
+
let rhsTarget;
|
|
237
|
+
if (isVec(lhsExpr.dataType)) {
|
|
238
|
+
const cc = lhsExpr.dataType.componentCount;
|
|
239
|
+
rhsTarget = cc === 2 ? vec2u : cc === 3 ? vec3u : vec4u;
|
|
240
|
+
} else rhsTarget = u32;
|
|
241
|
+
convRhs = tryConvertSnippet(this.ctx, rhsExpr, rhsTarget, false);
|
|
242
|
+
convLhs = lhsExpr;
|
|
243
|
+
} else {
|
|
244
|
+
const forcedType = exprType === NODE.assignmentExpr ? [lhsExpr.dataType] : void 0;
|
|
245
|
+
[convLhs, convRhs] = convertToCommonType(this.ctx, [lhsExpr, rhsExpr], forcedType) ?? [lhsExpr, rhsExpr];
|
|
246
|
+
}
|
|
203
247
|
const lhsStr = this.ctx.resolve(convLhs.value, convLhs.dataType).value;
|
|
204
248
|
const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
|
|
205
249
|
const type = operatorToType(convLhs.dataType, op, convRhs.dataType);
|
|
@@ -213,13 +257,13 @@ ${this.ctx.pre}}`;
|
|
|
213
257
|
}
|
|
214
258
|
if (expression[0] === NODE.postUpdate) {
|
|
215
259
|
const [_, op, arg] = expression;
|
|
216
|
-
const argExpr = this.
|
|
260
|
+
const argExpr = this._expression(arg);
|
|
217
261
|
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
218
262
|
return snip(`${argStr}${op}`, argExpr.dataType, "runtime");
|
|
219
263
|
}
|
|
220
264
|
if (expression[0] === NODE.unaryExpr) {
|
|
221
265
|
const [_, op, arg] = expression;
|
|
222
|
-
const argExpr = this.
|
|
266
|
+
const argExpr = this._expression(arg);
|
|
223
267
|
const codegen = unaryOpCodeToCodegen[op];
|
|
224
268
|
if (codegen) return codegen(this.ctx, [argExpr]);
|
|
225
269
|
const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value;
|
|
@@ -228,7 +272,7 @@ ${this.ctx.pre}}`;
|
|
|
228
272
|
}
|
|
229
273
|
if (expression[0] === NODE.memberAccess) {
|
|
230
274
|
const [_, targetNode, property] = expression;
|
|
231
|
-
const target = this.
|
|
275
|
+
const target = this._expression(targetNode);
|
|
232
276
|
if (target.value === console) return snip(new ConsoleLog(property), UnknownData, "runtime");
|
|
233
277
|
if (target.value === Math) {
|
|
234
278
|
if (property in mathToStd && mathToStd[property]) return snip(mathToStd[property], UnknownData, "runtime");
|
|
@@ -240,8 +284,8 @@ ${this.ctx.pre}}`;
|
|
|
240
284
|
}
|
|
241
285
|
if (expression[0] === NODE.indexAccess) {
|
|
242
286
|
const [_, targetNode, propertyNode] = expression;
|
|
243
|
-
const target = this.
|
|
244
|
-
const inProperty = this.
|
|
287
|
+
const target = this._expression(targetNode);
|
|
288
|
+
const inProperty = this._expression(propertyNode);
|
|
245
289
|
const property = convertToCommonType(this.ctx, [inProperty], [u32, i32], false)?.[0] ?? inProperty;
|
|
246
290
|
const accessed = accessIndex(target, property);
|
|
247
291
|
if (!accessed) {
|
|
@@ -258,27 +302,27 @@ ${this.ctx.pre}}`;
|
|
|
258
302
|
}
|
|
259
303
|
if (expression[0] === NODE.call) {
|
|
260
304
|
const [_, calleeNode, argNodes] = expression;
|
|
261
|
-
const callee = this.
|
|
305
|
+
const callee = this._expression(calleeNode);
|
|
262
306
|
if (isWgslStruct(callee.value)) {
|
|
263
307
|
if (argNodes.length > 1) throw new WgslTypeError("Struct schemas should always be called with at most 1 argument");
|
|
264
308
|
if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
|
|
265
|
-
const arg = this.
|
|
309
|
+
const arg = this._typedExpression(argNodes[0], callee.value);
|
|
266
310
|
return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
|
|
267
311
|
}
|
|
268
312
|
if (isWgslArray(callee.value)) {
|
|
269
313
|
if (argNodes.length > 1) throw new WgslTypeError("Array schemas should always be called with at most 1 argument");
|
|
270
314
|
if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
|
|
271
|
-
const arg = this.
|
|
315
|
+
const arg = this._typedExpression(argNodes[0], callee.value);
|
|
272
316
|
if (arg.value instanceof ArrayExpression) return snip(stitch`${this.ctx.resolve(callee.value).value}(${arg.value.elements})`, arg.dataType, "runtime");
|
|
273
317
|
return snip(this.ctx.resolve(arg.value, callee.value).value, callee.value, "runtime");
|
|
274
318
|
}
|
|
275
319
|
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
320
|
if (callee.value instanceof InfixDispatch) {
|
|
277
321
|
if (!argNodes[0]) throw new WgslTypeError(`An infix operator '${callee.value.name}' was called without any arguments`);
|
|
278
|
-
const rhs = this.
|
|
322
|
+
const rhs = this._expression(argNodes[0]);
|
|
279
323
|
return callee.value.operator(this.ctx, [callee.value.lhs, rhs]);
|
|
280
324
|
}
|
|
281
|
-
if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this.
|
|
325
|
+
if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this._expression(arg)));
|
|
282
326
|
if (isGPUCallable(callee.value)) {
|
|
283
327
|
const callable = callee.value[$gpuCallable];
|
|
284
328
|
const strictSignature = callable.strictSignature;
|
|
@@ -286,9 +330,9 @@ ${this.ctx.pre}}`;
|
|
|
286
330
|
if (strictSignature) convertedArguments = argNodes.map((arg, i) => {
|
|
287
331
|
const argType = strictSignature.argTypes[i];
|
|
288
332
|
if (!argType) throw new WgslTypeError(`Function '${getName(callee.value)}' was called with too many arguments`);
|
|
289
|
-
return this.
|
|
333
|
+
return this._typedExpression(arg, argType);
|
|
290
334
|
});
|
|
291
|
-
else convertedArguments = argNodes.map((arg) => this.
|
|
335
|
+
else convertedArguments = argNodes.map((arg) => this._expression(arg));
|
|
292
336
|
try {
|
|
293
337
|
return callable.call(this.ctx, convertedArguments);
|
|
294
338
|
} catch (err) {
|
|
@@ -301,7 +345,7 @@ ${this.ctx.pre}}`;
|
|
|
301
345
|
const slotPairs = isGeneric ? callee.value[$providing]?.pairs ?? [] : [];
|
|
302
346
|
const callback = isGeneric ? callee.value[$internal].inner : callee.value;
|
|
303
347
|
const shelllessCall = this.ctx.withRenamed(callback, getName(callee.value), () => this.ctx.withSlots(slotPairs, () => {
|
|
304
|
-
const args = argNodes.map((arg) => this.
|
|
348
|
+
const args = argNodes.map((arg) => this._expression(arg));
|
|
305
349
|
const shellless = this.ctx.shelllessRepo.get(callback, args);
|
|
306
350
|
if (!shellless) return;
|
|
307
351
|
const converted = args.map((s, idx) => {
|
|
@@ -324,11 +368,11 @@ ${this.ctx.pre}}`;
|
|
|
324
368
|
const entries = Object.fromEntries(Object.entries(obj).map(([key, value]) => {
|
|
325
369
|
let accessed = structType.accessProp(key);
|
|
326
370
|
let expr;
|
|
327
|
-
if (accessed) expr = this.
|
|
371
|
+
if (accessed) expr = this._typedExpression(value, accessed.type);
|
|
328
372
|
else {
|
|
329
|
-
expr = this.
|
|
373
|
+
expr = this._expression(value);
|
|
330
374
|
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));
|
|
375
|
+
accessed = structType.provideProp(key, unptr(concretize(expr.dataType)));
|
|
332
376
|
}
|
|
333
377
|
return [accessed.prop, expr];
|
|
334
378
|
}));
|
|
@@ -340,7 +384,7 @@ ${this.ctx.pre}}`;
|
|
|
340
384
|
const entries = Object.fromEntries(Object.entries(structType.propTypes).map(([key, value]) => {
|
|
341
385
|
const val = obj[key];
|
|
342
386
|
if (val === void 0) throw new WgslTypeError(`Missing property ${key} in object literal for struct ${structType}`);
|
|
343
|
-
return [key, this.
|
|
387
|
+
return [key, this._typedExpression(val, value)];
|
|
344
388
|
}));
|
|
345
389
|
const convertedSnippets = convertStructValues(this.ctx, structType, entries);
|
|
346
390
|
return snip(stitch`${this.ctx.resolve(structType).value}(${convertedSnippets})`, structType, "runtime");
|
|
@@ -354,10 +398,10 @@ ${this.ctx.pre}}`;
|
|
|
354
398
|
let values;
|
|
355
399
|
if (isWgslArray(arrType)) {
|
|
356
400
|
elemType = arrType.elementType;
|
|
357
|
-
values = valueNodes.map((value) => this.
|
|
401
|
+
values = valueNodes.map((value) => this._typedExpression(value, elemType));
|
|
358
402
|
if (values.length !== arrType.elementCount) throw new WgslTypeError(`Cannot create value of type '${arrType}' from an array of length: ${values.length}`);
|
|
359
403
|
} else {
|
|
360
|
-
const valuesSnippets = valueNodes.map((value) => this.
|
|
404
|
+
const valuesSnippets = valueNodes.map((value) => this._expression(value));
|
|
361
405
|
if (valuesSnippets.length === 0) throw new WgslTypeError("Cannot infer the type of an empty array literal.");
|
|
362
406
|
const converted = convertToCommonType(this.ctx, valuesSnippets);
|
|
363
407
|
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 +413,8 @@ ${this.ctx.pre}}`;
|
|
|
369
413
|
}
|
|
370
414
|
if (expression[0] === NODE.conditionalExpr) {
|
|
371
415
|
const [_, test, consequent, alternative] = expression;
|
|
372
|
-
const testExpression = this.
|
|
373
|
-
if (isKnownAtComptime(testExpression)) return testExpression.value ? this.
|
|
416
|
+
const testExpression = this._expression(test);
|
|
417
|
+
if (isKnownAtComptime(testExpression)) return testExpression.value ? this._expression(consequent) : this._expression(alternative);
|
|
374
418
|
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
419
|
}
|
|
376
420
|
if (expression[0] === NODE.stringLiteral) return snip(expression[1], UnknownData, "constant");
|
|
@@ -378,11 +422,23 @@ ${this.ctx.pre}}`;
|
|
|
378
422
|
assertExhaustive(expression);
|
|
379
423
|
}
|
|
380
424
|
functionDefinition(body) {
|
|
381
|
-
return this.
|
|
425
|
+
return this._block(body);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Generates a WGSL type string for the given data type, and adds necessary
|
|
429
|
+
* definitions to the shader preamble. This shouldn't be called directly, only
|
|
430
|
+
* through `ctx.resolve` to properly cache the result.
|
|
431
|
+
*/
|
|
432
|
+
typeAnnotation(data) {
|
|
433
|
+
return resolveData(this.ctx, data);
|
|
434
|
+
}
|
|
435
|
+
typeInstantiation(schema, args) {
|
|
436
|
+
if (args.length === 1 && args[0]?.dataType === schema) return snip(stitch`${args[0]}`, schema, fallthroughCopyOrigin(args[0].origin));
|
|
437
|
+
return snip(stitch`${this.ctx.resolve(schema).value}(${args})`, schema, "runtime");
|
|
382
438
|
}
|
|
383
|
-
|
|
439
|
+
_statement(statement) {
|
|
384
440
|
if (typeof statement === "string") {
|
|
385
|
-
const id = this.
|
|
441
|
+
const id = this._identifier(statement);
|
|
386
442
|
const resolved = id.value && this.ctx.resolve(id.value).value;
|
|
387
443
|
return resolved ? `${this.ctx.pre}${resolved};` : "";
|
|
388
444
|
}
|
|
@@ -391,7 +447,7 @@ ${this.ctx.pre}}`;
|
|
|
391
447
|
const returnNode = statement[1];
|
|
392
448
|
if (returnNode !== void 0) {
|
|
393
449
|
const expectedReturnType = this.ctx.topFunctionReturnType;
|
|
394
|
-
let returnSnippet = expectedReturnType ? this.
|
|
450
|
+
let returnSnippet = expectedReturnType ? this._typedExpression(returnNode, expectedReturnType) : this._expression(returnNode);
|
|
395
451
|
if (returnSnippet.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot return references, returning '${returnSnippet.value.snippet}'`);
|
|
396
452
|
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
453
|
if (!expectedReturnType && !isEphemeralSnippet(returnSnippet) && returnSnippet.origin !== "this-function") {
|
|
@@ -411,11 +467,17 @@ Try 'return ${typeStr}(${str});' instead.
|
|
|
411
467
|
}
|
|
412
468
|
if (statement[0] === NODE.if) {
|
|
413
469
|
const [_, condNode, consNode, altNode] = statement;
|
|
414
|
-
const condition = this.
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
470
|
+
const condition = this._typedExpression(condNode, bool);
|
|
471
|
+
if (typeof condition.value === "boolean") {
|
|
472
|
+
let node = condition.value ? consNode : altNode;
|
|
473
|
+
if (node === void 0) return "";
|
|
474
|
+
if (!Array.isArray(node)) node = blockifySingleStatement(node);
|
|
475
|
+
if (node[0] === NODE.block && node[1].length === 1 && node[1][0][0] === NODE.if) return this._statement(node[1][0]);
|
|
476
|
+
if (node[0] === NODE.if) return this._statement(node);
|
|
477
|
+
return `${this.ctx.pre}${this._block(blockifySingleStatement(node))}`;
|
|
478
|
+
}
|
|
479
|
+
const consequent = this._block(blockifySingleStatement(consNode));
|
|
480
|
+
const alternate = !altNode ? void 0 : this._block(blockifySingleStatement(altNode));
|
|
419
481
|
if (!alternate) return stitch`${this.ctx.pre}if (${condition}) ${consequent}`;
|
|
420
482
|
return stitch`\
|
|
421
483
|
${this.ctx.pre}if (${condition}) ${consequent}
|
|
@@ -424,7 +486,7 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
424
486
|
if (statement[0] === NODE.let || statement[0] === NODE.const) {
|
|
425
487
|
let varType = "var";
|
|
426
488
|
const [stmtType, rawId, rawValue] = statement;
|
|
427
|
-
const eq = rawValue !== void 0 ? this.
|
|
489
|
+
const eq = rawValue !== void 0 ? this._expression(rawValue) : void 0;
|
|
428
490
|
if (!eq) throw new Error(`Cannot create variable '${rawId}' without an initial value.`);
|
|
429
491
|
const ephemeral = isEphemeralSnippet(eq);
|
|
430
492
|
let dataType = eq.dataType;
|
|
@@ -472,9 +534,11 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
472
534
|
}
|
|
473
535
|
}
|
|
474
536
|
const snippet = this.blockVariable(varType, rawId, concretize(dataType), eq.origin);
|
|
475
|
-
|
|
537
|
+
const rhsSnippet = tryConvertSnippet(this.ctx, eq, dataType, false);
|
|
538
|
+
const rhsStr = this.ctx.resolve(rhsSnippet.value, rhsSnippet.dataType).value;
|
|
539
|
+
return this.emitVarDecl(this.ctx.pre, varType, snippet.value, concretize(dataType), rhsStr);
|
|
476
540
|
}
|
|
477
|
-
if (statement[0] === NODE.block) return `${this.ctx.pre}${this.
|
|
541
|
+
if (statement[0] === NODE.block) return `${this.ctx.pre}${this._block(statement)}`;
|
|
478
542
|
if (statement[0] === NODE.for) {
|
|
479
543
|
const [_, init, condition, update, body] = statement;
|
|
480
544
|
const prevUnrollingFlag = this.#unrolling;
|
|
@@ -482,13 +546,13 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
482
546
|
try {
|
|
483
547
|
this.ctx.pushBlockScope();
|
|
484
548
|
const [initStatement, conditionExpr, updateStatement] = this.ctx.withResetIndentLevel(() => [
|
|
485
|
-
init ? this.
|
|
486
|
-
condition ? this.
|
|
487
|
-
update ? this.
|
|
549
|
+
init ? this._statement(init) : void 0,
|
|
550
|
+
condition ? this._typedExpression(condition, bool) : void 0,
|
|
551
|
+
update ? this._statement(update) : void 0
|
|
488
552
|
]);
|
|
489
553
|
const initStr = initStatement ? initStatement.slice(0, -1) : "";
|
|
490
554
|
const updateStr = updateStatement ? updateStatement.slice(0, -1) : "";
|
|
491
|
-
const bodyStr = this.
|
|
555
|
+
const bodyStr = this._block(blockifySingleStatement(body));
|
|
492
556
|
return stitch`${this.ctx.pre}for (${initStr}; ${conditionExpr}; ${updateStr}) ${bodyStr}`;
|
|
493
557
|
} finally {
|
|
494
558
|
this.#unrolling = prevUnrollingFlag;
|
|
@@ -500,9 +564,9 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
500
564
|
this.#unrolling = false;
|
|
501
565
|
try {
|
|
502
566
|
const [_, condition, body] = statement;
|
|
503
|
-
const condSnippet = this.
|
|
567
|
+
const condSnippet = this._typedExpression(condition, bool);
|
|
504
568
|
const conditionStr = this.ctx.resolve(condSnippet.value).value;
|
|
505
|
-
const bodyStr = this.
|
|
569
|
+
const bodyStr = this._block(blockifySingleStatement(body));
|
|
506
570
|
return `${this.ctx.pre}while (${conditionStr}) ${bodyStr}`;
|
|
507
571
|
} finally {
|
|
508
572
|
this.#unrolling = prevUnrollingFlag;
|
|
@@ -515,40 +579,40 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
515
579
|
const prevUnrollingFlag = this.#unrolling;
|
|
516
580
|
try {
|
|
517
581
|
this.ctx.pushBlockScope();
|
|
518
|
-
const iterableExpr = this.
|
|
582
|
+
const iterableExpr = this._expression(iterable);
|
|
519
583
|
const shouldUnroll = iterableExpr.value instanceof UnrollableIterable;
|
|
520
584
|
const iterableSnippet = shouldUnroll ? iterableExpr.value.snippet : iterableExpr;
|
|
521
|
-
const
|
|
585
|
+
const range = getRangeSnippets(this.ctx, iterableSnippet, shouldUnroll);
|
|
522
586
|
const originalLoopVarName = loopVar[1];
|
|
523
587
|
const blockified = blockifySingleStatement(body);
|
|
524
588
|
if (shouldUnroll) {
|
|
525
|
-
if (!isKnownAtComptime(
|
|
589
|
+
if (!isKnownAtComptime(range.end)) throw new Error("Cannot unroll loop. Length of iterable is unknown at comptime.");
|
|
526
590
|
this.#unrolling = true;
|
|
527
|
-
const length =
|
|
591
|
+
const length = range.end.value;
|
|
528
592
|
if (length === 0) return "";
|
|
529
593
|
const { value } = iterableSnippet;
|
|
530
|
-
const elements = value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
|
|
594
|
+
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
595
|
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.
|
|
596
|
+
return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this.ctx.pre}${this._block(blockified, { [originalLoopVarName]: e })}`).join("\n");
|
|
533
597
|
}
|
|
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
598
|
this.#unrolling = false;
|
|
539
599
|
const index = this.ctx.makeNameValid("i");
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
600
|
+
const forHeaderStr = stitch`${this.ctx.pre}for (var ${index} = ${range.start}; ${index} ${range.comparison} ${range.end}; ${index} += ${range.step})`;
|
|
601
|
+
let bodyStr = "";
|
|
602
|
+
if (isTgpuRange(iterableSnippet.value)) bodyStr = this._block(blockified, { [originalLoopVarName]: snip(index, u32, "runtime") });
|
|
603
|
+
else {
|
|
604
|
+
this.ctx.indent();
|
|
605
|
+
ctxIndent = true;
|
|
606
|
+
const loopVarName = this.ctx.makeNameValid(originalLoopVarName);
|
|
607
|
+
const elementSnippet = getElementSnippet(iterableSnippet, snip(index, u32, "runtime"));
|
|
608
|
+
const loopVarKind = getLoopVarKind(elementSnippet);
|
|
609
|
+
const elementType = getElementType(elementSnippet, iterableSnippet);
|
|
610
|
+
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`;
|
|
611
|
+
this.ctx.dedent();
|
|
612
|
+
bodyStr += `${this.ctx.pre}}`;
|
|
613
|
+
ctxIndent = false;
|
|
614
|
+
}
|
|
615
|
+
return stitch`${forHeaderStr} ${bodyStr.trim()}`;
|
|
552
616
|
} finally {
|
|
553
617
|
if (ctxIndent) this.ctx.dedent();
|
|
554
618
|
this.#unrolling = prevUnrollingFlag;
|
|
@@ -563,7 +627,7 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
563
627
|
if (this.#unrolling) throw new WgslTypeError("Cannot unroll loop containing `break`");
|
|
564
628
|
return `${this.ctx.pre}break;`;
|
|
565
629
|
}
|
|
566
|
-
const expr = this.
|
|
630
|
+
const expr = this._expression(statement);
|
|
567
631
|
const resolved = expr.value && this.ctx.resolve(expr.value).value;
|
|
568
632
|
return resolved ? `${this.ctx.pre}${resolved};` : "";
|
|
569
633
|
}
|
|
@@ -580,6 +644,5 @@ function blockifySingleStatement(statement) {
|
|
|
580
644
|
return typeof statement !== "object" || statement[0] !== NODE.block ? [NODE.block, [statement]] : statement;
|
|
581
645
|
}
|
|
582
646
|
const wgslGenerator = new WgslGenerator();
|
|
583
|
-
|
|
584
647
|
//#endregion
|
|
585
|
-
export { wgslGenerator as default };
|
|
648
|
+
export { WgslGenerator, wgslGenerator as default };
|