typegpu 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/core/buffer/buffer.js +17 -53
- package/core/function/autoIO.js +2 -2
- package/core/function/entryInputRouter.js +10 -16
- package/core/function/fnCore.js +16 -12
- package/core/function/shelllessImpl.js +1 -1
- package/core/function/tgpuComputeFn.js +1 -1
- package/core/function/tgpuFn.js +1 -1
- package/core/function/tgpuFragmentFn.d.ts +1 -1
- package/core/function/tgpuFragmentFn.js +1 -1
- package/core/function/tgpuVertexFn.js +1 -1
- package/core/pipeline/computePipeline.d.ts +1 -1
- package/core/root/init.d.ts +1 -0
- package/core/root/init.js +6 -0
- package/core/root/rootTypes.d.ts +21 -15
- package/core/slot/accessor.js +1 -1
- package/data/dataIO.d.ts +11 -0
- package/data/dataIO.js +45 -1
- package/data/dataTypes.js +2 -11
- package/data/index.d.ts +3 -3
- package/data/numeric.js +16 -14
- package/data/partialIO.d.ts +8 -0
- package/data/partialIO.js +6 -1
- package/data/snippet.d.ts +1 -6
- package/data/wgslTypes.d.ts +2 -0
- package/index.d.ts +3 -1
- package/index.js +3 -1
- package/indexNamedExports.d.ts +2 -0
- package/package.js +1 -1
- package/package.json +1 -1
- package/resolutionCtx.js +82 -86
- package/shared/stringify.js +1 -0
- package/shared/tseynit.js +90 -0
- package/tgsl/accessIndex.js +4 -3
- package/tgsl/accessProp.js +7 -2
- package/tgsl/consoleLog/deserializers.js +1 -1
- package/tgsl/consoleLog/logGenerator.js +16 -14
- package/tgsl/consoleLog/types.d.ts +4 -5
- package/tgsl/conversion.js +15 -5
- package/tgsl/forOfUtils.js +14 -6
- package/tgsl/generationHelpers.d.ts +2 -1
- package/tgsl/generationHelpers.js +1 -4
- package/tgsl/jsPolyfills.d.ts +25 -0
- package/tgsl/jsPolyfills.js +44 -0
- package/tgsl/shaderGenerator.d.ts +2 -3
- package/tgsl/shaderGenerator_members.d.ts +15 -2
- package/tgsl/wgslGenerator.d.ts +3 -1
- package/tgsl/wgslGenerator.js +64 -59
- package/types.d.ts +11 -6
- package/tgsl/consoleLog/types.js +0 -12
- package/tgsl/math.js +0 -45
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { $internal } from "../../shared/symbols.js";
|
|
2
2
|
import { Void } from "../../data/wgslTypes.js";
|
|
3
|
-
import { UnknownData } from "../../data/dataTypes.js";
|
|
3
|
+
import { UnknownData, unptr } from "../../data/dataTypes.js";
|
|
4
4
|
import { snip } from "../../data/snippet.js";
|
|
5
|
+
import { invariant } from "../../errors.js";
|
|
5
6
|
import { stitch } from "../../core/resolve/stitch.js";
|
|
7
|
+
import { convertToCommonType } from "../conversion.js";
|
|
6
8
|
import { u32 } from "../../data/numeric.js";
|
|
7
|
-
import {
|
|
9
|
+
import { concretizeSnippet } from "../generationHelpers.js";
|
|
8
10
|
import { struct } from "../../data/struct.js";
|
|
9
11
|
import { shaderStageSlot } from "../../core/slot/internalSlots.js";
|
|
10
12
|
import { arrayOf } from "../../data/array.js";
|
|
11
13
|
import { atomic } from "../../data/atomic.js";
|
|
12
14
|
import { createLoggingFunction } from "./serializers.js";
|
|
13
|
-
import { supportedLogOps } from "./types.js";
|
|
14
15
|
|
|
15
16
|
//#region src/tgsl/consoleLog/logGenerator.ts
|
|
16
17
|
const defaultOptions = {
|
|
@@ -54,23 +55,24 @@ var LogGeneratorImpl = class {
|
|
|
54
55
|
*/
|
|
55
56
|
generateLog(ctx, op, args) {
|
|
56
57
|
if (shaderStageSlot.$ === "vertex") {
|
|
57
|
-
console.warn(`'console
|
|
58
|
+
console.warn(`'console' operations are not supported in vertex shaders.`);
|
|
58
59
|
return fallbackSnippet;
|
|
59
60
|
}
|
|
60
|
-
if (!supportedLogOps.includes(op)) {
|
|
61
|
-
console.warn(`Unsupported log method '${op}'.`);
|
|
62
|
-
return fallbackSnippet;
|
|
63
|
-
}
|
|
64
|
-
const concreteArgs = concretizeSnippets(args);
|
|
65
61
|
const id = this.#firstUnusedId++;
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
const concreteArgsWithStrings = args.map((arg) => {
|
|
63
|
+
if (arg.dataType === UnknownData) return arg;
|
|
64
|
+
const converted = convertToCommonType(ctx, [arg], [unptr(arg.dataType)])?.[0];
|
|
65
|
+
invariant(converted, `Internal error. Expected type ${arg.dataType} to be convertible to ${unptr(arg.dataType)}`);
|
|
66
|
+
return converted;
|
|
67
|
+
}).map(concretizeSnippet);
|
|
68
|
+
const concreteArgs = concreteArgsWithStrings.filter((arg) => arg.dataType !== UnknownData);
|
|
69
|
+
const logFn = createLoggingFunction(id, concreteArgs.map((e) => e.dataType), this.#dataBuffer, this.#indexBuffer, this.#options);
|
|
70
|
+
const functionSnippet = snip(stitch`${ctx.resolve(logFn).value}(${concreteArgs})`, Void, "runtime");
|
|
69
71
|
this.#logIdToMeta.set(id, {
|
|
70
72
|
op,
|
|
71
|
-
argTypes
|
|
73
|
+
argTypes: concreteArgsWithStrings.map((e) => e?.dataType === UnknownData ? e?.value : e?.dataType)
|
|
72
74
|
});
|
|
73
|
-
return
|
|
75
|
+
return functionSnippet;
|
|
74
76
|
}
|
|
75
77
|
get logResources() {
|
|
76
78
|
return this.#firstUnusedId === 1 ? void 0 : {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import "../../data/snippet.js";
|
|
2
2
|
import { TgpuMutable } from "../../core/buffer/bufferShorthand.js";
|
|
3
3
|
import "../generationHelpers.js";
|
|
4
|
+
import { supportedLogOps } from "../jsPolyfills.js";
|
|
4
5
|
import { AnyWgslData, Atomic, U32, WgslArray, WgslStruct } from "../../data/wgslTypes.js";
|
|
5
6
|
|
|
6
7
|
//#region src/tgsl/consoleLog/types.d.ts
|
|
7
|
-
|
|
8
|
+
type SupportedLogOp = ReturnType<typeof supportedLogOps>[number];
|
|
8
9
|
/**
|
|
9
10
|
* Options for configuring GPU log generation.
|
|
10
11
|
*/
|
|
@@ -32,7 +33,7 @@ type SerializedLogCallData = WgslStruct<{
|
|
|
32
33
|
serializedData: WgslArray<U32>;
|
|
33
34
|
}>;
|
|
34
35
|
interface LogMeta {
|
|
35
|
-
op:
|
|
36
|
+
op: SupportedLogOp;
|
|
36
37
|
argTypes: (string | AnyWgslData)[];
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
@@ -49,7 +50,5 @@ interface LogResources {
|
|
|
49
50
|
options: Required<LogGeneratorOptions>;
|
|
50
51
|
logIdToMeta: Map<number, LogMeta>;
|
|
51
52
|
}
|
|
52
|
-
declare const supportedLogOps: readonly ["log", "debug", "info", "warn", "error", "clear"];
|
|
53
|
-
type SupportedLogOps = (typeof supportedLogOps)[number];
|
|
54
53
|
//#endregion
|
|
55
|
-
export { LogGeneratorOptions, LogResources };
|
|
54
|
+
export { LogGeneratorOptions, LogResources, SupportedLogOp };
|
package/tgsl/conversion.js
CHANGED
|
@@ -90,14 +90,19 @@ function getImplicitConversionRank(src, dest) {
|
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
if ((trueSrc.type === "u32" || trueSrc.type === "i32") && trueDst.type === "abstractFloat") return {
|
|
94
|
+
rank: 1,
|
|
95
|
+
action: "cast",
|
|
96
|
+
targetType: trueDst.concretized
|
|
97
|
+
};
|
|
93
98
|
if (trueSrc.type === "abstractFloat") {
|
|
94
|
-
if (trueDst.type === "
|
|
99
|
+
if (trueDst.type === "i32") return {
|
|
95
100
|
rank: 2,
|
|
96
101
|
action: "cast",
|
|
97
102
|
targetType: trueDst
|
|
98
103
|
};
|
|
99
|
-
if (trueDst.type === "
|
|
100
|
-
rank:
|
|
104
|
+
if (trueDst.type === "u32") return {
|
|
105
|
+
rank: 3,
|
|
101
106
|
action: "cast",
|
|
102
107
|
targetType: trueDst
|
|
103
108
|
};
|
|
@@ -113,6 +118,10 @@ function getConversionRank(src, dest, allowImplicit) {
|
|
|
113
118
|
function findBestType(types, uniqueTypes, allowImplicit) {
|
|
114
119
|
let bestResult;
|
|
115
120
|
for (const targetType of uniqueTypes) {
|
|
121
|
+
/**
|
|
122
|
+
* The type we end up converting to. Will be different than `targetType` if `targetType === abstractFloat`
|
|
123
|
+
*/
|
|
124
|
+
let destType = targetType;
|
|
116
125
|
const details = [];
|
|
117
126
|
let sum = 0;
|
|
118
127
|
for (const sourceType of types) {
|
|
@@ -120,9 +129,10 @@ function findBestType(types, uniqueTypes, allowImplicit) {
|
|
|
120
129
|
sum += conversion.rank;
|
|
121
130
|
if (conversion.rank === Number.POSITIVE_INFINITY) break;
|
|
122
131
|
details.push(conversion);
|
|
132
|
+
if (conversion.action === "cast") destType = conversion.targetType;
|
|
123
133
|
}
|
|
124
134
|
if (sum < (bestResult?.sum ?? Number.POSITIVE_INFINITY)) bestResult = {
|
|
125
|
-
type:
|
|
135
|
+
type: destType,
|
|
126
136
|
details,
|
|
127
137
|
sum
|
|
128
138
|
};
|
|
@@ -168,7 +178,7 @@ function convertToCommonType(ctx, values, restrictTo, verbose = true) {
|
|
|
168
178
|
if (DEV && Array.isArray(restrictTo) && restrictTo.length === 0) console.warn("convertToCommonType was called with an empty restrictTo array, which prevents any conversions from being made. If you intend to allow all conversions, pass undefined instead. If this was intended call the function conditionally since the result will always be undefined.");
|
|
169
179
|
const conversion = getBestConversion(types, restrictTo);
|
|
170
180
|
if (!conversion) return;
|
|
171
|
-
if ((TEST || DEV) && verbose && conversion.hasImplicitConversions) console.warn(`Implicit conversions from [\n${values.map((v) => ` ${v.value}: ${safeStringify(v.dataType)}`).join(",\n")}\n] to ${conversion.targetType.type} are supported, but not recommended.
|
|
181
|
+
if ((TEST || DEV) && verbose && conversion.hasImplicitConversions) console.warn(`Implicit conversions from [\n${values.map((v) => ` ${ctx.resolveSnippet(v).value}: ${safeStringify(v.dataType)}`).join(",\n")}\n] to ${conversion.targetType.type} are supported, but not recommended.
|
|
172
182
|
Consider using explicit conversions instead.`);
|
|
173
183
|
return values.map((value, index) => {
|
|
174
184
|
const action = conversion.actions[index];
|
package/tgsl/forOfUtils.js
CHANGED
|
@@ -33,12 +33,20 @@ function getElementType(elementSnippet, iterableSnippet) {
|
|
|
33
33
|
}
|
|
34
34
|
function getRangeSnippets(ctx, iterableSnippet, unroll = false) {
|
|
35
35
|
const { value, dataType } = iterableSnippet;
|
|
36
|
-
if (isTgpuRange(value))
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
if (isTgpuRange(value)) {
|
|
37
|
+
const { start, end, step } = value;
|
|
38
|
+
const dataType$1 = [
|
|
39
|
+
start,
|
|
40
|
+
end,
|
|
41
|
+
step
|
|
42
|
+
].every((v) => v >= 0) ? u32 : i32;
|
|
43
|
+
return {
|
|
44
|
+
start: snip(start, dataType$1, "constant"),
|
|
45
|
+
end: snip(end, dataType$1, "constant"),
|
|
46
|
+
step: snip(step, dataType$1, "constant"),
|
|
47
|
+
comparison: step < 0 ? ">" : "<"
|
|
48
|
+
};
|
|
49
|
+
}
|
|
42
50
|
if (!unroll && isEphemeralSnippet(iterableSnippet)) throw new Error(`\`for ... of ...\` loops only support std.range or iterables stored in variables.
|
|
43
51
|
-----
|
|
44
52
|
You can wrap iterable with \`tgpu.unroll(...)\`. If iterable is known at comptime, the loop will be unrolled.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Snippet } from "../data/snippet.js";
|
|
2
2
|
import { ShelllessRepository } from "./shellless.js";
|
|
3
|
+
import { SupportedLogOp } from "./consoleLog/types.js";
|
|
3
4
|
import { FunctionScopeLayer, ResolutionCtx } from "../types.js";
|
|
4
5
|
import { BaseData } from "../data/wgslTypes.js";
|
|
5
6
|
|
|
@@ -21,7 +22,7 @@ type GenerationCtx = ResolutionCtx & {
|
|
|
21
22
|
dedent(): string;
|
|
22
23
|
pushBlockScope(): void;
|
|
23
24
|
popBlockScope(): void;
|
|
24
|
-
generateLog(op:
|
|
25
|
+
generateLog(op: SupportedLogOp, args: Snippet[]): Snippet;
|
|
25
26
|
getById(id: string): Snippet | null;
|
|
26
27
|
defineVariable(id: string, snippet: Snippet): void;
|
|
27
28
|
setBlockExternals(externals: Record<string, Snippet>): void;
|
|
@@ -25,9 +25,6 @@ function concretize(type) {
|
|
|
25
25
|
function concretizeSnippet(snippet) {
|
|
26
26
|
return snip(snippet.value, concretize(snippet.dataType), snippet.origin);
|
|
27
27
|
}
|
|
28
|
-
function concretizeSnippets(args) {
|
|
29
|
-
return args.map(concretizeSnippet);
|
|
30
|
-
}
|
|
31
28
|
function coerceToSnippet(value) {
|
|
32
29
|
if (isSnippet(value)) return value;
|
|
33
30
|
if (isRef(value)) throw new Error("Cannot use refs (d.ref(...)) from the outer scope.");
|
|
@@ -66,4 +63,4 @@ var ArrayExpression = class {
|
|
|
66
63
|
};
|
|
67
64
|
|
|
68
65
|
//#endregion
|
|
69
|
-
export { ArrayExpression, coerceToSnippet, concretize,
|
|
66
|
+
export { ArrayExpression, coerceToSnippet, concretize, concretizeSnippet, numericLiteralToSnippet };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "../core/function/fnTypes.js";
|
|
2
|
+
import "../types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/tgsl/jsPolyfills.d.ts
|
|
5
|
+
declare const supportedLogOps: () => readonly [{
|
|
6
|
+
(...data: any[]): void;
|
|
7
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
8
|
+
}, {
|
|
9
|
+
(...data: any[]): void;
|
|
10
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
11
|
+
}, {
|
|
12
|
+
(...data: any[]): void;
|
|
13
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
14
|
+
}, {
|
|
15
|
+
(...data: any[]): void;
|
|
16
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
17
|
+
}, {
|
|
18
|
+
(...data: any[]): void;
|
|
19
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
20
|
+
}, {
|
|
21
|
+
(): void;
|
|
22
|
+
(): void;
|
|
23
|
+
}];
|
|
24
|
+
//#endregion
|
|
25
|
+
export { supportedLogOps };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { f32 } from "../data/numeric.js";
|
|
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
|
+
//#region src/tgsl/jsPolyfills.ts
|
|
5
|
+
const mathToStd = new Map([
|
|
6
|
+
[Math.abs, abs],
|
|
7
|
+
[Math.acos, acos],
|
|
8
|
+
[Math.acosh, acosh],
|
|
9
|
+
[Math.asin, asin],
|
|
10
|
+
[Math.asinh, asinh],
|
|
11
|
+
[Math.atan, atan],
|
|
12
|
+
[Math.atan2, atan2],
|
|
13
|
+
[Math.atanh, atanh],
|
|
14
|
+
[Math.ceil, ceil],
|
|
15
|
+
[Math.cos, cos],
|
|
16
|
+
[Math.cosh, cosh],
|
|
17
|
+
[Math.exp, exp],
|
|
18
|
+
[Math.floor, floor],
|
|
19
|
+
[Math.fround, f32],
|
|
20
|
+
[Math.clz32, countLeadingZeros],
|
|
21
|
+
[Math.trunc, trunc],
|
|
22
|
+
[Math.log, log],
|
|
23
|
+
[Math.log2, log2],
|
|
24
|
+
[Math.pow, pow],
|
|
25
|
+
[Math.sign, sign],
|
|
26
|
+
[Math.sin, sin],
|
|
27
|
+
[Math.sinh, sinh],
|
|
28
|
+
[Math.sqrt, sqrt],
|
|
29
|
+
[Math.tan, tan],
|
|
30
|
+
[Math.tanh, tanh],
|
|
31
|
+
[Math.max, max],
|
|
32
|
+
[Math.min, min]
|
|
33
|
+
]);
|
|
34
|
+
const supportedLogOps = () => [
|
|
35
|
+
console.log,
|
|
36
|
+
console.debug,
|
|
37
|
+
console.info,
|
|
38
|
+
console.warn,
|
|
39
|
+
console.error,
|
|
40
|
+
console.clear
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { mathToStd, supportedLogOps };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ResolvedSnippet, Snippet } from "../data/snippet.js";
|
|
2
2
|
import { GenerationCtx } from "./generationHelpers.js";
|
|
3
|
-
import "./shaderGenerator_members.js";
|
|
3
|
+
import { FunctionDefinitionOptions } from "./shaderGenerator_members.js";
|
|
4
4
|
import { BaseData } from "../data/wgslTypes.js";
|
|
5
|
-
import { Block } from "tinyest";
|
|
6
5
|
|
|
7
6
|
//#region src/tgsl/shaderGenerator.d.ts
|
|
8
7
|
|
|
@@ -14,7 +13,7 @@ import { Block } from "tinyest";
|
|
|
14
13
|
*/
|
|
15
14
|
interface ShaderGenerator {
|
|
16
15
|
initGenerator(ctx: GenerationCtx): void;
|
|
17
|
-
functionDefinition(
|
|
16
|
+
functionDefinition(options: FunctionDefinitionOptions): string;
|
|
18
17
|
typeInstantiation(schema: BaseData, args: readonly Snippet[]): ResolvedSnippet;
|
|
19
18
|
typeAnnotation(schema: BaseData): string;
|
|
20
19
|
}
|
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Origin, Snippet } from "../data/snippet.js";
|
|
2
|
+
import { FunctionArgument, ResolutionCtx, TgpuShaderStage } from "../types.js";
|
|
3
|
+
import { BaseData } from "../data/wgslTypes.js";
|
|
4
|
+
import { UnknownData } from "../data/dataTypes.js";
|
|
5
|
+
import { Block } from "tinyest";
|
|
6
|
+
|
|
7
|
+
//#region src/tgsl/shaderGenerator_members.d.ts
|
|
8
|
+
interface FunctionDefinitionOptions {
|
|
9
|
+
readonly functionType: 'normal' | TgpuShaderStage;
|
|
10
|
+
readonly args: readonly FunctionArgument[];
|
|
11
|
+
readonly body: Block;
|
|
12
|
+
determineReturnType(): BaseData;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { FunctionDefinitionOptions };
|
package/tgsl/wgslGenerator.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Origin, ResolvedSnippet, Snippet } from "../data/snippet.js";
|
|
2
2
|
import { GenerationCtx } from "./generationHelpers.js";
|
|
3
|
+
import { FunctionDefinitionOptions } from "./shaderGenerator_members.js";
|
|
3
4
|
import { ShaderGenerator } from "./shaderGenerator.js";
|
|
4
5
|
import { BaseData, StorableData } from "../data/wgslTypes.js";
|
|
5
6
|
import { UnknownData } from "../data/dataTypes.js";
|
|
@@ -22,7 +23,7 @@ declare class WgslGenerator implements ShaderGenerator {
|
|
|
22
23
|
*/
|
|
23
24
|
_typedExpression(expression: tinyest.Expression, expectedType: BaseData | BaseData[]): Snippet;
|
|
24
25
|
_expression(expression: tinyest.Expression): Snippet;
|
|
25
|
-
functionDefinition(
|
|
26
|
+
functionDefinition(options: FunctionDefinitionOptions): string;
|
|
26
27
|
/**
|
|
27
28
|
* Generates a WGSL type string for the given data type, and adds necessary
|
|
28
29
|
* definitions to the shader preamble. This shouldn't be called directly, only
|
|
@@ -30,6 +31,7 @@ declare class WgslGenerator implements ShaderGenerator {
|
|
|
30
31
|
*/
|
|
31
32
|
typeAnnotation(data: BaseData): string;
|
|
32
33
|
typeInstantiation(schema: BaseData, args: readonly Snippet[]): ResolvedSnippet;
|
|
34
|
+
_return(statement: tinyest.Return): string;
|
|
33
35
|
_statement(statement: tinyest.Statement): string;
|
|
34
36
|
}
|
|
35
37
|
//#endregion
|
package/tgsl/wgslGenerator.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $gpuCallable, $internal, $providing, isMarkedInternal } from "../shared/symbols.js";
|
|
2
2
|
import { getName } from "../shared/meta.js";
|
|
3
3
|
import { Void, isBool, isNaturallyEphemeral, isNumericSchema, isPtr, isVec, isWgslArray, isWgslStruct } from "../data/wgslTypes.js";
|
|
4
|
-
import {
|
|
4
|
+
import { InfixDispatch, UnknownData, unptr } from "../data/dataTypes.js";
|
|
5
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";
|
|
@@ -13,6 +13,7 @@ import { convertStructValues, convertToCommonType, tryConvertSnippet } from "./c
|
|
|
13
13
|
import { bool, i32, u32 } from "../data/numeric.js";
|
|
14
14
|
import { ArrayExpression, coerceToSnippet, concretize, numericLiteralToSnippet } from "./generationHelpers.js";
|
|
15
15
|
import { vec2u, vec3u, vec4u } from "../data/vector.js";
|
|
16
|
+
import { getAttributesString } from "../data/attributes.js";
|
|
16
17
|
import { AutoStruct } from "../data/autoStruct.js";
|
|
17
18
|
import { add, div, mul, neg, sub } from "../std/operators.js";
|
|
18
19
|
import { accessProp } from "./accessProp.js";
|
|
@@ -23,9 +24,10 @@ import { arrayOf } from "../data/array.js";
|
|
|
23
24
|
import { pow } from "../std/numeric.js";
|
|
24
25
|
import { resolveData } from "../core/resolve/resolveData.js";
|
|
25
26
|
import { UnrollableIterable } from "../core/unroll/tgpuUnroll.js";
|
|
26
|
-
import { mathToStd } from "./
|
|
27
|
+
import { mathToStd, supportedLogOps } from "./jsPolyfills.js";
|
|
27
28
|
import { isTgpuRange } from "../std/range.js";
|
|
28
29
|
import { getElementSnippet, getElementType, getLoopVarKind, getRangeSnippets } from "./forOfUtils.js";
|
|
30
|
+
import { stringifyNode } from "../shared/tseynit.js";
|
|
29
31
|
import * as tinyest from "tinyest";
|
|
30
32
|
|
|
31
33
|
//#region src/tgsl/wgslGenerator.ts
|
|
@@ -224,8 +226,9 @@ ${this.ctx.pre}}`;
|
|
|
224
226
|
return snip(rhsStr$1, bool, "runtime");
|
|
225
227
|
}
|
|
226
228
|
const rhsExpr = this._expression(rhs);
|
|
227
|
-
if (rhsExpr.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot assign a ref to an existing variable '${
|
|
229
|
+
if (rhsExpr.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot assign a ref to an existing variable '${stringifyNode(lhs)}', define a new variable instead.`);
|
|
228
230
|
if (op === "==") throw new Error("Please use the === operator instead of ==");
|
|
231
|
+
if (op === "!=") throw new Error("Please use the !== operator instead of !=");
|
|
229
232
|
if (op === "===" && isKnownAtComptime(lhsExpr) && isKnownAtComptime(rhsExpr)) return snip(lhsExpr.value === rhsExpr.value, bool, "constant");
|
|
230
233
|
if (lhsExpr.dataType === UnknownData) throw new WgslTypeError(`Left-hand side of '${op}' is of unknown type`);
|
|
231
234
|
if (rhsExpr.dataType === UnknownData) throw new WgslTypeError(`Right-hand side of '${op}' is of unknown type`);
|
|
@@ -249,10 +252,13 @@ ${this.ctx.pre}}`;
|
|
|
249
252
|
const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
|
|
250
253
|
const type = operatorToType(convLhs.dataType, op, convRhs.dataType);
|
|
251
254
|
if (exprType === NODE.assignmentExpr) {
|
|
252
|
-
if (convLhs.origin === "constant" || convLhs.origin === "constant-tgpu-const-ref" || convLhs.origin === "runtime-tgpu-const-ref")
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
255
|
+
if (convLhs.origin === "constant" || convLhs.origin === "constant-tgpu-const-ref" || convLhs.origin === "runtime-tgpu-const-ref") {
|
|
256
|
+
if (isKnownAtComptime(convLhs)) throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because the left side is defined outside of the shader, and therefore is immutable during its execution. Try using tgpu.privateVar or buffers.`);
|
|
257
|
+
throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because the left side is a constant.`);
|
|
258
|
+
}
|
|
259
|
+
if (lhsExpr.origin === "argument") throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because non-pointer arguments cannot be mutated.`);
|
|
260
|
+
if (op === "=" && rhsExpr.origin === "argument" && !isNaturallyEphemeral(rhsExpr.dataType)) throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because argument references cannot be assigned.\n-----\nTry '${stringifyNode(lhs)} = ${this.ctx.resolve(rhsExpr.dataType).value}(${stringifyNode(rhs)})' to copy the value instead.\n-----`);
|
|
261
|
+
if (op === "=" && !isEphemeralSnippet(rhsExpr)) throw new WgslTypeError(`'${stringifyNode(expression)}' is invalid, because references cannot be assigned.\n-----\nTry '${stringifyNode(lhs)} = ${this.ctx.resolve(rhsExpr.dataType).value}(${stringifyNode(rhs)})' to copy the value instead.\n-----`);
|
|
256
262
|
}
|
|
257
263
|
return snip(parenthesizedOps.includes(op) ? `(${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr})` : `${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr}`, type, "runtime");
|
|
258
264
|
}
|
|
@@ -273,37 +279,28 @@ ${this.ctx.pre}}`;
|
|
|
273
279
|
}
|
|
274
280
|
if (expression[0] === NODE.memberAccess) {
|
|
275
281
|
const [_, targetNode, property] = expression;
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
if (target.value === Math) {
|
|
279
|
-
if (property in mathToStd && mathToStd[property]) return snip(mathToStd[property], UnknownData, "runtime");
|
|
280
|
-
if (typeof Math[property] === "function") throw new Error(`Unsupported functionality 'Math.${property}'. Use an std alternative, or implement the function manually.`);
|
|
281
|
-
}
|
|
282
|
-
const accessed = accessProp(target, property);
|
|
283
|
-
if (!accessed) throw new Error(stitch`Property '${property}' not found on value '${target}' of type ${this.ctx.resolve(target.dataType)}`);
|
|
282
|
+
const accessed = accessProp(this._expression(targetNode), property);
|
|
283
|
+
if (!accessed) throw new Error(`Property '${property}' not found on '${stringifyNode(targetNode)}'`);
|
|
284
284
|
return accessed;
|
|
285
285
|
}
|
|
286
286
|
if (expression[0] === NODE.indexAccess) {
|
|
287
287
|
const [_, targetNode, propertyNode] = expression;
|
|
288
288
|
const target = this._expression(targetNode);
|
|
289
289
|
const inProperty = this._expression(propertyNode);
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
if (!accessed) {
|
|
293
|
-
const targetStr = this.ctx.resolve(target.value, target.dataType).value;
|
|
294
|
-
const propertyStr = this.ctx.resolve(property.value, property.dataType).value;
|
|
295
|
-
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.`);
|
|
296
|
-
}
|
|
290
|
+
const accessed = accessIndex(target, convertToCommonType(this.ctx, [inProperty], [u32, i32], false)?.[0] ?? inProperty);
|
|
291
|
+
if (!accessed) throw new Error(`Index access '${stringifyNode(expression)}' is invalid. 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.`);
|
|
297
292
|
return accessed;
|
|
298
293
|
}
|
|
299
294
|
if (expression[0] === NODE.numericLiteral) {
|
|
300
295
|
const type = typeof expression[1] === "string" ? numericLiteralToSnippet(parseNumericString(expression[1])) : numericLiteralToSnippet(expression[1]);
|
|
301
|
-
|
|
296
|
+
invariant(type, `Expected ${stringifyNode(expression)} to be valid numeric literal`);
|
|
302
297
|
return type;
|
|
303
298
|
}
|
|
304
299
|
if (expression[0] === NODE.call) {
|
|
305
300
|
const [_, calleeNode, argNodes] = expression;
|
|
306
|
-
const
|
|
301
|
+
const _callee = this._expression(calleeNode);
|
|
302
|
+
const callee = mathToStd.has(_callee.value) ? snip(mathToStd.get(_callee.value), UnknownData, "runtime") : _callee;
|
|
303
|
+
if (supportedLogOps().includes(callee.value)) return this.ctx.generateLog(callee.value, argNodes.map((arg) => this._expression(arg)));
|
|
307
304
|
if (isWgslStruct(callee.value)) {
|
|
308
305
|
if (argNodes.length > 1) throw new WgslTypeError("Struct schemas should always be called with at most 1 argument");
|
|
309
306
|
if (!argNodes[0]) return snip(`${this.ctx.resolve(callee.value).value}()`, callee.value, "runtime");
|
|
@@ -323,14 +320,13 @@ ${this.ctx.pre}}`;
|
|
|
323
320
|
const rhs = this._expression(argNodes[0]);
|
|
324
321
|
return callee.value.operator(this.ctx, [callee.value.lhs, rhs]);
|
|
325
322
|
}
|
|
326
|
-
if (callee.value instanceof ConsoleLog) return this.ctx.generateLog(callee.value.op, argNodes.map((arg) => this._expression(arg)));
|
|
327
323
|
if (isGPUCallable(callee.value)) {
|
|
328
324
|
const callable = callee.value[$gpuCallable];
|
|
329
325
|
const strictSignature = callable.strictSignature;
|
|
330
326
|
let convertedArguments;
|
|
331
327
|
if (strictSignature) convertedArguments = argNodes.map((arg, i) => {
|
|
332
328
|
const argType = strictSignature.argTypes[i];
|
|
333
|
-
if (!argType) throw new WgslTypeError(`
|
|
329
|
+
if (!argType) throw new WgslTypeError(`Call '${stringifyNode(expression)}' is invalid since the function expected fewer arguments`);
|
|
334
330
|
return this._typedExpression(arg, argType);
|
|
335
331
|
});
|
|
336
332
|
else convertedArguments = argNodes.map((arg) => this._expression(arg));
|
|
@@ -360,6 +356,10 @@ ${this.ctx.pre}}`;
|
|
|
360
356
|
}));
|
|
361
357
|
if (shelllessCall) return shelllessCall;
|
|
362
358
|
}
|
|
359
|
+
const maybeMathMethod = Object.getOwnPropertyNames(Math).find((prop) => Math[prop] === callee.value);
|
|
360
|
+
if (maybeMathMethod) throw new Error(`Unsupported Math functionality 'Math.${maybeMathMethod}()'. Use an std alternative, or implement the function manually.`);
|
|
361
|
+
const maybeConsoleMethod = Object.getOwnPropertyNames(console).find((prop) => console[prop] === callee.value);
|
|
362
|
+
if (maybeConsoleMethod) throw new Error(`Unsupported console functionality 'console.${maybeConsoleMethod}()'.`);
|
|
363
363
|
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`);
|
|
364
364
|
}
|
|
365
365
|
if (expression[0] === NODE.objectExpr) {
|
|
@@ -390,7 +390,7 @@ ${this.ctx.pre}}`;
|
|
|
390
390
|
const convertedSnippets = convertStructValues(this.ctx, structType, entries);
|
|
391
391
|
return snip(stitch`${this.ctx.resolve(structType).value}(${convertedSnippets})`, structType, "runtime");
|
|
392
392
|
}
|
|
393
|
-
throw new WgslTypeError(`No target type could be inferred for object
|
|
393
|
+
throw new WgslTypeError(`No target type could be inferred for object '${stringifyNode(expression)}', please wrap the object in the corresponding schema.`);
|
|
394
394
|
}
|
|
395
395
|
if (expression[0] === NODE.arrayExpr) {
|
|
396
396
|
const [_, valueNodes] = expression;
|
|
@@ -405,7 +405,7 @@ ${this.ctx.pre}}`;
|
|
|
405
405
|
const valuesSnippets = valueNodes.map((value) => this._expression(value));
|
|
406
406
|
if (valuesSnippets.length === 0) throw new WgslTypeError("Cannot infer the type of an empty array literal.");
|
|
407
407
|
const converted = convertToCommonType(this.ctx, valuesSnippets);
|
|
408
|
-
if (!converted) throw new WgslTypeError(
|
|
408
|
+
if (!converted) throw new WgslTypeError(`Values '${stringifyNode(expression)}' cannot be automatically converted to a common type. Consider wrapping the array in an appropriate schema`);
|
|
409
409
|
values = converted;
|
|
410
410
|
elemType = concretize(values[0]?.dataType);
|
|
411
411
|
}
|
|
@@ -416,14 +416,19 @@ ${this.ctx.pre}}`;
|
|
|
416
416
|
const [_, test, consequent, alternative] = expression;
|
|
417
417
|
const testExpression = this._expression(test);
|
|
418
418
|
if (isKnownAtComptime(testExpression)) return testExpression.value ? this._expression(consequent) : this._expression(alternative);
|
|
419
|
-
else throw new Error(`Ternary operator is
|
|
419
|
+
else throw new Error(`Ternary operator '${stringifyNode(expression)}' is invalid, because only comptime-known checks are supported. For runtime checks, please use 'std.select' or if/else statements.`);
|
|
420
420
|
}
|
|
421
421
|
if (expression[0] === NODE.stringLiteral) return snip(expression[1], UnknownData, "constant");
|
|
422
422
|
if (expression[0] === NODE.preUpdate) throw new Error("Cannot use pre-updates in TypeGPU functions.");
|
|
423
423
|
assertExhaustive(expression);
|
|
424
424
|
}
|
|
425
|
-
functionDefinition(
|
|
426
|
-
|
|
425
|
+
functionDefinition(options) {
|
|
426
|
+
const body = this._block(options.body);
|
|
427
|
+
const returnType = options.determineReturnType();
|
|
428
|
+
const argList = options.args.filter((arg) => arg.used || options.functionType === "normal").map((arg) => {
|
|
429
|
+
return `${getAttributesString(arg.decoratedType)}${arg.name}: ${this.ctx.resolve(arg.decoratedType).value}`;
|
|
430
|
+
}).join(", ");
|
|
431
|
+
return `${returnType.type !== "void" ? `(${argList}) -> ${getAttributesString(returnType)}${this.ctx.resolve(returnType).value} ` : `(${argList}) `}${body}`;
|
|
427
432
|
}
|
|
428
433
|
/**
|
|
429
434
|
* Generates a WGSL type string for the given data type, and adds necessary
|
|
@@ -437,6 +442,28 @@ ${this.ctx.pre}}`;
|
|
|
437
442
|
if (args.length === 1 && args[0]?.dataType === schema) return snip(stitch`${args[0]}`, schema, fallthroughCopyOrigin(args[0].origin));
|
|
438
443
|
return snip(stitch`${this.ctx.resolve(schema).value}(${args})`, schema, "runtime");
|
|
439
444
|
}
|
|
445
|
+
_return(statement) {
|
|
446
|
+
const returnNode = statement[1];
|
|
447
|
+
if (returnNode !== void 0) {
|
|
448
|
+
const expectedReturnType = this.ctx.topFunctionReturnType;
|
|
449
|
+
let returnSnippet = expectedReturnType ? this._typedExpression(returnNode, expectedReturnType) : this._expression(returnNode);
|
|
450
|
+
if (returnSnippet.value instanceof RefOperator) throw new WgslTypeError(`Cannot return '${stringifyNode(returnNode)}' because it is a d.ref`);
|
|
451
|
+
if (returnSnippet.origin === "argument" && !isNaturallyEphemeral(returnSnippet.dataType) && this.ctx.topFunctionScope?.functionType === "normal") throw new WgslTypeError(`'${stringifyNode(statement)}' is invalid, cannot return references to arguments. Copy the argument before returning it.`);
|
|
452
|
+
if (!expectedReturnType && !isEphemeralSnippet(returnSnippet) && returnSnippet.origin !== "this-function") {
|
|
453
|
+
const str = stringifyNode(returnNode);
|
|
454
|
+
const typeStr = this.ctx.resolve(unptr(returnSnippet.dataType)).value;
|
|
455
|
+
throw new WgslTypeError(`'return ${str};' is invalid, cannot return references.
|
|
456
|
+
-----
|
|
457
|
+
Try 'return ${typeStr}(${str});' instead.
|
|
458
|
+
-----`);
|
|
459
|
+
}
|
|
460
|
+
returnSnippet = tryConvertSnippet(this.ctx, returnSnippet, unptr(returnSnippet.dataType), false);
|
|
461
|
+
invariant(returnSnippet.dataType !== UnknownData, "Return type should be known");
|
|
462
|
+
this.ctx.reportReturnType(returnSnippet.dataType);
|
|
463
|
+
return stitch`${this.ctx.pre}return ${returnSnippet};`;
|
|
464
|
+
}
|
|
465
|
+
return `${this.ctx.pre}return;`;
|
|
466
|
+
}
|
|
440
467
|
_statement(statement) {
|
|
441
468
|
if (typeof statement === "string") {
|
|
442
469
|
const id = this._identifier(statement);
|
|
@@ -444,28 +471,7 @@ ${this.ctx.pre}}`;
|
|
|
444
471
|
return resolved$1 ? `${this.ctx.pre}${resolved$1};` : "";
|
|
445
472
|
}
|
|
446
473
|
if (typeof statement === "boolean") return `${this.ctx.pre}${statement ? "true" : "false"};`;
|
|
447
|
-
if (statement[0] === NODE.return)
|
|
448
|
-
const returnNode = statement[1];
|
|
449
|
-
if (returnNode !== void 0) {
|
|
450
|
-
const expectedReturnType = this.ctx.topFunctionReturnType;
|
|
451
|
-
let returnSnippet = expectedReturnType ? this._typedExpression(returnNode, expectedReturnType) : this._expression(returnNode);
|
|
452
|
-
if (returnSnippet.value instanceof RefOperator) throw new WgslTypeError(stitch`Cannot return references, returning '${returnSnippet.value.snippet}'`);
|
|
453
|
-
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.`);
|
|
454
|
-
if (!expectedReturnType && !isEphemeralSnippet(returnSnippet) && returnSnippet.origin !== "this-function") {
|
|
455
|
-
const str = this.ctx.resolve(returnSnippet.value, returnSnippet.dataType).value;
|
|
456
|
-
const typeStr = this.ctx.resolve(unptr(returnSnippet.dataType)).value;
|
|
457
|
-
throw new WgslTypeError(`'return ${str};' is invalid, cannot return references.
|
|
458
|
-
-----
|
|
459
|
-
Try 'return ${typeStr}(${str});' instead.
|
|
460
|
-
-----`);
|
|
461
|
-
}
|
|
462
|
-
returnSnippet = tryConvertSnippet(this.ctx, returnSnippet, unptr(returnSnippet.dataType), false);
|
|
463
|
-
invariant(returnSnippet.dataType !== UnknownData, "Return type should be known");
|
|
464
|
-
this.ctx.reportReturnType(returnSnippet.dataType);
|
|
465
|
-
return stitch`${this.ctx.pre}return ${returnSnippet};`;
|
|
466
|
-
}
|
|
467
|
-
return `${this.ctx.pre}return;`;
|
|
468
|
-
}
|
|
474
|
+
if (statement[0] === NODE.return) return this._return(statement);
|
|
469
475
|
if (statement[0] === NODE.if) {
|
|
470
476
|
const [_, condNode, consNode, altNode] = statement;
|
|
471
477
|
const condition = this._typedExpression(condNode, bool);
|
|
@@ -488,11 +494,10 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
488
494
|
let varType = "var";
|
|
489
495
|
const [stmtType, rawId, rawValue] = statement;
|
|
490
496
|
const eq = rawValue !== void 0 ? this._expression(rawValue) : void 0;
|
|
491
|
-
if (!eq) throw new Error(`
|
|
497
|
+
if (!eq) throw new Error(`'${stringifyNode(statement)}' is invalid because all variables need initializers.`);
|
|
492
498
|
const ephemeral = isEphemeralSnippet(eq);
|
|
493
499
|
let dataType = eq.dataType;
|
|
494
500
|
const naturallyEphemeral = isNaturallyEphemeral(dataType);
|
|
495
|
-
if (isLooseData(eq.dataType)) throw new Error(`Cannot create variable '${rawId}' with loose data type.`);
|
|
496
501
|
if (eq.value instanceof RefOperator) {
|
|
497
502
|
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.`);
|
|
498
503
|
const refSnippet = eq.value.snippet;
|
|
@@ -501,7 +506,7 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
501
506
|
}
|
|
502
507
|
if (!ephemeral) {
|
|
503
508
|
if (stmtType === NODE.let) {
|
|
504
|
-
const rhsStr$1 =
|
|
509
|
+
const rhsStr$1 = stringifyNode(rawValue ?? "");
|
|
505
510
|
const rhsTypeStr = this.ctx.resolve(unptr(eq.dataType)).value;
|
|
506
511
|
throw new WgslTypeError(`'let ${rawId} = ${rhsStr$1}' is invalid, because references cannot be assigned to 'let' variable declarations.
|
|
507
512
|
-----
|
|
@@ -525,7 +530,7 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
525
530
|
else if (naturallyEphemeral) varType = eq.origin === "constant" ? "const" : "let";
|
|
526
531
|
} else if (eq.origin === "argument") {
|
|
527
532
|
if (!naturallyEphemeral) {
|
|
528
|
-
const rhsStr$1 =
|
|
533
|
+
const rhsStr$1 = stringifyNode(rawValue ?? "");
|
|
529
534
|
const rhsTypeStr = this.ctx.resolve(unptr(eq.dataType)).value;
|
|
530
535
|
throw new WgslTypeError(`'let ${rawId} = ${rhsStr$1}' is invalid, because references to arguments cannot be assigned to 'let' variable declarations.
|
|
531
536
|
-----
|
|
@@ -593,14 +598,14 @@ ${this.ctx.pre}else ${alternate}`;
|
|
|
593
598
|
if (length === 0) return "";
|
|
594
599
|
const { value } = iterableSnippet;
|
|
595
600
|
const elements = isTgpuRange(value) ? value.map((i) => coerceToSnippet(i)) : value instanceof ArrayExpression ? value.elements : Array.from({ length }, (_$1, i) => getElementSnippet(iterableSnippet, snip(i, u32, "constant")));
|
|
596
|
-
if (isEphemeralSnippet(elements[0]) && !isNaturallyEphemeral(elements[0]?.dataType)) throw new WgslTypeError(
|
|
601
|
+
if (isEphemeralSnippet(elements[0]) && !isNaturallyEphemeral(elements[0]?.dataType)) throw new WgslTypeError(`Cannot unroll '${stringifyNode(iterable)}'. The elements of iterable are constructed in place but are not value types.`);
|
|
597
602
|
return elements.map((e, i) => `${this.ctx.pre}// unrolled iteration #${i}\n${this.ctx.pre}${this._block(blockified, { [originalLoopVarName]: e })}`).join("\n");
|
|
598
603
|
}
|
|
599
604
|
this.#unrolling = false;
|
|
600
605
|
const index = this.ctx.makeNameValid("i");
|
|
601
606
|
const forHeaderStr = stitch`${this.ctx.pre}for (var ${index} = ${range.start}; ${index} ${range.comparison} ${range.end}; ${index} += ${range.step})`;
|
|
602
607
|
let bodyStr = "";
|
|
603
|
-
if (isTgpuRange(iterableSnippet.value)) bodyStr = this._block(blockified, { [originalLoopVarName]: snip(index,
|
|
608
|
+
if (isTgpuRange(iterableSnippet.value)) bodyStr = this._block(blockified, { [originalLoopVarName]: snip(index, range.start.dataType, "runtime") });
|
|
604
609
|
else {
|
|
605
610
|
this.ctx.indent();
|
|
606
611
|
ctxIndent = true;
|