typegpu 0.11.8 → 0.12.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/builtin.d.ts +51 -53
- package/builtin.js +30 -34
- package/common/fullScreenTriangle.d.ts +2 -10
- package/common/fullScreenTriangle.js +20 -28
- package/common/index.d.ts +2 -9
- package/common/index.js +3 -12
- package/common/writeSoA.d.ts +11 -12
- package/common/writeSoA.js +88 -76
- package/core/buffer/buffer.d.ts +79 -62
- package/core/buffer/buffer.js +279 -205
- package/core/buffer/bufferBinding.d.ts +71 -0
- package/core/buffer/bufferBinding.js +154 -0
- package/core/buffer/bufferUsage.d.ts +14 -45
- package/core/buffer/bufferUsage.js +1 -171
- package/core/buffer/laidOutBuffer.d.ts +25 -0
- package/core/buffer/laidOutBuffer.js +55 -0
- package/core/commandEncoder/attachments.d.ts +125 -0
- package/core/commandEncoder/attachments.js +29 -0
- package/core/commandEncoder/commandEncoder.d.ts +53 -0
- package/core/commandEncoder/commandEncoder.js +56 -0
- package/core/commandEncoder/computePass.d.ts +58 -0
- package/core/commandEncoder/computePass.js +63 -0
- package/core/commandEncoder/renderPass.d.ts +121 -0
- package/core/commandEncoder/renderPass.js +196 -0
- package/core/constant/tgpuConstant.d.ts +26 -26
- package/core/constant/tgpuConstant.js +76 -65
- package/core/declare/tgpuDeclare.d.ts +3 -6
- package/core/declare/tgpuDeclare.js +37 -38
- package/core/function/autoIO.d.ts +61 -33
- package/core/function/autoIO.js +68 -73
- package/core/function/comptime.d.ts +11 -13
- package/core/function/comptime.js +60 -52
- package/core/function/createCallableSchema.d.ts +16 -0
- package/core/function/createCallableSchema.js +50 -40
- package/core/function/dualImpl.d.ts +44 -0
- package/core/function/dualImpl.js +71 -50
- package/core/function/entryInputRouter.d.ts +26 -0
- package/core/function/entryInputRouter.js +31 -33
- package/core/function/extractArgs.d.ts +38 -0
- package/core/function/extractArgs.js +202 -200
- package/core/function/fnCore.d.ts +44 -0
- package/core/function/fnCore.js +148 -92
- package/core/function/fnTypes.d.ts +31 -29
- package/core/function/fnTypes.js +1 -0
- package/core/function/ioSchema.d.ts +12 -10
- package/core/function/ioSchema.js +65 -47
- package/core/function/shelllessImpl.d.ts +9 -12
- package/core/function/shelllessImpl.js +18 -20
- package/core/function/templateUtils.d.ts +2 -0
- package/core/function/templateUtils.js +9 -8
- package/core/function/tgpuComputeFn.d.ts +26 -28
- package/core/function/tgpuComputeFn.js +47 -48
- package/core/function/tgpuFn.d.ts +34 -37
- package/core/function/tgpuFn.js +179 -153
- package/core/function/tgpuFragmentFn.d.ts +52 -53
- package/core/function/tgpuFragmentFn.js +63 -56
- package/core/function/tgpuVertexFn.d.ts +36 -39
- package/core/function/tgpuVertexFn.js +56 -53
- package/core/pipeline/computePipeline.d.ts +141 -53
- package/core/pipeline/computePipeline.js +291 -225
- package/core/pipeline/connectAttachmentToShader.d.ts +4 -0
- package/core/pipeline/connectAttachmentToShader.js +25 -22
- package/core/pipeline/connectTargetsToShader.d.ts +3 -0
- package/core/pipeline/connectTargetsToShader.js +29 -27
- package/core/pipeline/drawState.d.ts +52 -0
- package/core/pipeline/drawState.js +225 -0
- package/core/pipeline/limitsOverflow.d.ts +2 -0
- package/core/pipeline/limitsOverflow.js +13 -10
- package/core/pipeline/performanceTracker.d.ts +14 -0
- package/core/pipeline/performanceTracker.js +34 -0
- package/core/pipeline/pipelineUtils.d.ts +18 -0
- package/core/pipeline/pipelineUtils.js +54 -25
- package/core/pipeline/priors.d.ts +1 -0
- package/core/pipeline/priors.js +12 -0
- package/core/pipeline/renderPipeline.d.ts +246 -261
- package/core/pipeline/renderPipeline.js +487 -480
- package/core/pipeline/timeable.d.ts +28 -18
- package/core/pipeline/timeable.js +90 -52
- package/core/pipeline/typeGuards.d.ts +17 -0
- package/core/pipeline/typeGuards.js +52 -22
- package/core/querySet/querySet.d.ts +26 -21
- package/core/querySet/querySet.js +127 -103
- package/core/rawCodeSnippet/tgpuRawCodeSnippet.d.ts +13 -18
- package/core/rawCodeSnippet/tgpuRawCodeSnippet.js +90 -93
- package/core/resolve/externals.d.ts +25 -7
- package/core/resolve/externals.js +79 -62
- package/core/resolve/namespace.d.ts +22 -25
- package/core/resolve/namespace.js +16 -21
- package/core/resolve/resolvableString.d.ts +7 -0
- package/core/resolve/resolvableString.js +13 -0
- package/core/resolve/resolveData.d.ts +10 -0
- package/core/resolve/resolveData.js +162 -121
- package/core/resolve/stitch.d.ts +8 -0
- package/core/resolve/stitch.js +23 -21
- package/core/resolve/tgpuResolve.d.ts +55 -53
- package/core/resolve/tgpuResolve.js +106 -55
- package/core/root/configurableImpl.d.ts +10 -0
- package/core/root/configurableImpl.js +17 -19
- package/core/root/init.d.ts +76 -38
- package/core/root/init.js +346 -454
- package/core/root/rootTypes.d.ts +461 -627
- package/core/root/rootTypes.js +1 -0
- package/core/sampler/sampler.d.ts +50 -31
- package/core/sampler/sampler.js +120 -113
- package/core/simulate/tgpuSimulate.d.ts +9 -12
- package/core/simulate/tgpuSimulate.js +68 -71
- package/core/slot/accessor.d.ts +36 -13
- package/core/slot/accessor.js +138 -91
- package/core/slot/internalSlots.d.ts +2 -0
- package/core/slot/internalSlots.js +1 -6
- package/core/slot/lazy.d.ts +2 -6
- package/core/slot/lazy.js +71 -39
- package/core/slot/slot.d.ts +2 -6
- package/core/slot/slot.js +45 -38
- package/core/slot/slotTypes.d.ts +78 -80
- package/core/slot/slotTypes.js +11 -16
- package/core/texture/externalTexture.d.ts +20 -11
- package/core/texture/externalTexture.js +46 -46
- package/core/texture/texture.d.ts +152 -97
- package/core/texture/texture.js +407 -314
- package/core/texture/textureFormats.d.ts +42 -23
- package/core/texture/textureFormats.js +104 -75
- package/core/texture/textureProps.d.ts +7 -10
- package/core/texture/textureProps.js +1 -0
- package/core/texture/textureUtils.d.ts +8 -0
- package/core/texture/textureUtils.js +185 -179
- package/core/texture/usageExtension.d.ts +21 -18
- package/core/texture/usageExtension.js +13 -17
- package/core/unroll/tgpuUnroll.d.ts +20 -19
- package/core/unroll/tgpuUnroll.js +82 -91
- package/core/valueProxyUtils.d.ts +3 -0
- package/core/valueProxyUtils.js +52 -41
- package/core/variable/tgpuVariable.d.ts +26 -27
- package/core/variable/tgpuVariable.js +93 -96
- package/core/vertexLayout/connectAttributesToShader.d.ts +9 -0
- package/core/vertexLayout/connectAttributesToShader.js +61 -56
- package/core/vertexLayout/vertexAttribute.d.ts +21 -17
- package/core/vertexLayout/vertexAttribute.js +1 -0
- package/core/vertexLayout/vertexLayout.d.ts +24 -18
- package/core/vertexLayout/vertexLayout.js +130 -99
- package/core/whitespaces.d.ts +2 -0
- package/core/whitespaces.js +16 -0
- package/data/alignIO.d.ts +7 -0
- package/data/alignIO.js +13 -13
- package/data/alignmentOf.d.ts +5 -8
- package/data/alignmentOf.js +90 -75
- package/data/array.d.ts +6 -9
- package/data/array.js +48 -40
- package/data/atomic.d.ts +2 -7
- package/data/atomic.js +26 -23
- package/data/attributes.d.ts +19 -21
- package/data/attributes.js +192 -132
- package/data/autoStruct.d.ts +36 -3
- package/data/autoStruct.js +84 -81
- package/data/compiledIO.d.ts +5 -0
- package/data/compiledIO.js +231 -214
- package/data/dataIO.d.ts +13 -11
- package/data/dataIO.js +643 -589
- package/data/dataTypes.d.ts +83 -65
- package/data/dataTypes.js +66 -68
- package/data/deepEqual.d.ts +2 -7
- package/data/deepEqual.js +80 -55
- package/data/disarray.d.ts +6 -10
- package/data/disarray.js +52 -46
- package/data/float16Conversion.d.ts +4 -0
- package/data/float16Conversion.js +7 -0
- package/data/getLongestContiguousPrefix.d.ts +4 -8
- package/data/getLongestContiguousPrefix.js +6 -11
- package/data/index.d.ts +30 -31
- package/data/index.js +39 -172
- package/data/instanceToSchema.d.ts +24 -27
- package/data/instanceToSchema.js +1 -0
- package/data/isContiguous.d.ts +4 -8
- package/data/isContiguous.js +6 -11
- package/data/matrix.d.ts +21 -21
- package/data/matrix.js +513 -505
- package/data/numberOps.d.ts +6 -0
- package/data/numberOps.js +26 -20
- package/data/numeric.d.ts +21 -12
- package/data/numeric.js +287 -192
- package/data/offsetUtils.d.ts +9 -13
- package/data/offsetUtils.js +204 -144
- package/data/offsets.d.ts +8 -0
- package/data/offsets.js +33 -33
- package/data/partialIO.d.ts +13 -8
- package/data/partialIO.js +124 -113
- package/data/ptr.d.ts +12 -12
- package/data/ptr.js +42 -44
- package/data/ref.d.ts +40 -27
- package/data/ref.js +125 -95
- package/data/sampler.d.ts +99 -101
- package/data/sampler.js +17 -21
- package/data/schemaCallWrapper.d.ts +19 -0
- package/data/schemaCallWrapper.js +31 -26
- package/data/schemaMemoryLayout.d.ts +8 -0
- package/data/schemaMemoryLayout.js +213 -185
- package/data/sizeOf.d.ts +4 -8
- package/data/sizeOf.js +6 -11
- package/data/snippet.d.ts +96 -20
- package/data/snippet.js +75 -86
- package/data/struct.d.ts +4 -7
- package/data/struct.js +51 -39
- package/data/texture.d.ts +253 -250
- package/data/texture.js +238 -200
- package/data/unstruct.d.ts +2 -7
- package/data/unstruct.js +43 -37
- package/data/vector.d.ts +33 -21
- package/data/vector.js +234 -221
- package/data/vectorImpl.d.ts +231 -0
- package/data/vectorImpl.js +563 -515
- package/data/vectorOps.d.ts +63 -0
- package/data/vectorOps.js +642 -630
- package/data/vertexFormatData.d.ts +181 -184
- package/data/vertexFormatData.js +104 -106
- package/data/wgslTypes.d.ts +665 -600
- package/data/wgslTypes.js +220 -198
- package/errors.d.ts +50 -24
- package/errors.js +146 -120
- package/execMode.d.ts +16 -0
- package/execMode.js +47 -40
- package/extension.d.ts +13 -8
- package/extension.js +10 -16
- package/getGPUValue.d.ts +1 -0
- package/getGPUValue.js +2 -7
- package/index.d.ts +28 -68
- package/index.js +8 -28
- package/indexNamedExports.d.ts +69 -42
- package/indexNamedExports.js +25 -0
- package/internal.d.ts +16 -0
- package/internal.js +12 -0
- package/mathUtils.d.ts +5 -0
- package/mathUtils.js +7 -11
- package/memo.d.ts +11 -0
- package/memo.js +20 -22
- package/minify.d.ts +5 -0
- package/minify.js +93 -0
- package/nameUtils.d.ts +29 -0
- package/nameUtils.js +428 -398
- package/package.json +9 -3
- package/resolutionCtx.d.ts +162 -22
- package/resolutionCtx.js +911 -624
- package/serial/registry.d.ts +15 -0
- package/serial/registry.js +106 -0
- package/serial/restore.d.ts +49 -0
- package/serial/restore.js +68 -0
- package/serial/schema.d.ts +58 -0
- package/serial/schema.js +209 -0
- package/serial/types.d.ts +5 -0
- package/serial/types.js +1 -0
- package/shared/env.d.ts +9 -0
- package/shared/env.js +9 -12
- package/shared/generators.d.ts +4 -0
- package/shared/generators.js +10 -12
- package/shared/meta.d.ts +29 -14
- package/shared/meta.js +71 -55
- package/shared/normalizeMetadata.d.ts +32 -27
- package/shared/normalizeMetadata.js +23 -36
- package/shared/repr.d.ts +73 -55
- package/shared/repr.js +1 -0
- package/shared/soul.d.ts +17 -0
- package/shared/soul.js +1 -0
- package/shared/stringify.d.ts +2 -0
- package/shared/stringify.js +29 -20
- package/shared/symbols.d.ts +26 -20
- package/shared/symbols.js +79 -79
- package/shared/tseynit.d.ts +2 -0
- package/shared/tseynit.js +160 -75
- package/shared/utilityTypes.d.ts +30 -14
- package/shared/utilityTypes.js +2 -6
- package/shared/vertexFormat.d.ts +41 -43
- package/shared/vertexFormat.js +59 -62
- package/std/array.d.ts +2 -7
- package/std/array.js +18 -23
- package/std/atomic.d.ts +14 -18
- package/std/atomic.js +96 -95
- package/std/bitcast.d.ts +193 -10
- package/std/bitcast.js +309 -39
- package/std/boolean.d.ts +19 -33
- package/std/boolean.js +296 -288
- package/std/copy.d.ts +2 -7
- package/std/copy.js +27 -24
- package/std/derivative.d.ts +11 -15
- package/std/derivative.js +56 -79
- package/std/discard.d.ts +1 -6
- package/std/discard.js +7 -14
- package/std/environment.d.ts +19 -14
- package/std/environment.js +71 -51
- package/std/extensions.d.ts +2 -8
- package/std/extensions.js +10 -11
- package/std/index.d.ts +20 -24
- package/std/index.js +32 -187
- package/std/matrix.d.ts +6 -12
- package/std/matrix.js +60 -69
- package/std/numeric.d.ts +143 -146
- package/std/numeric.js +858 -749
- package/std/operators.d.ts +19 -27
- package/std/operators.js +227 -193
- package/std/packing.d.ts +5 -11
- package/std/packing.js +67 -75
- package/std/range.d.ts +6 -11
- package/std/range.js +48 -36
- package/std/subgroup.d.ts +33 -37
- package/std/subgroup.js +180 -217
- package/std/texture.d.ts +70 -38
- package/std/texture.js +176 -172
- package/taskQueue.d.ts +6 -0
- package/taskQueue.js +30 -0
- package/tgpu.d.ts +16 -0
- package/tgpu.js +17 -44
- package/tgpuBindGroupLayout.d.ts +132 -152
- package/tgpuBindGroupLayout.js +346 -268
- package/tgpuLogger.d.ts +34 -0
- package/tgpuLogger.js +63 -0
- package/tgpuUnstable.d.ts +16 -22
- package/tgpuUnstable.js +13 -35
- package/tgsl/accessIndex.d.ts +2 -0
- package/tgsl/accessIndex.js +76 -33
- package/tgsl/accessProp.d.ts +2 -0
- package/tgsl/accessProp.js +142 -100
- package/tgsl/accessStructProp.d.ts +5 -0
- package/tgsl/accessStructProp.js +18 -0
- package/tgsl/consoleLog/deserializers.d.ts +13 -0
- package/tgsl/consoleLog/deserializers.js +119 -103
- package/tgsl/consoleLog/logGenerator.d.ts +21 -0
- package/tgsl/consoleLog/logGenerator.js +85 -80
- package/tgsl/consoleLog/serializers.d.ts +21 -0
- package/tgsl/consoleLog/serializers.js +119 -100
- package/tgsl/consoleLog/types.d.ts +40 -39
- package/tgsl/consoleLog/types.js +1 -0
- package/tgsl/conversion.d.ts +33 -0
- package/tgsl/conversion.js +272 -193
- package/tgsl/forOfUtils.d.ts +15 -0
- package/tgsl/forOfUtils.js +85 -70
- package/tgsl/generationHelpers.d.ts +22 -39
- package/tgsl/generationHelpers.js +85 -58
- package/tgsl/infixDispatch.d.ts +70 -0
- package/tgsl/infixDispatch.js +48 -46
- package/tgsl/jsPolyfills.d.ts +16 -19
- package/tgsl/jsPolyfills.js +44 -42
- package/tgsl/makeDereferenceable.d.ts +48 -0
- package/tgsl/makeDereferenceable.js +86 -0
- package/tgsl/makeResolvable.d.ts +22 -0
- package/tgsl/makeResolvable.js +33 -0
- package/tgsl/shaderGenerator.d.ts +77 -17
- package/tgsl/shaderGenerator.js +1 -0
- package/tgsl/shellless.d.ts +6 -9
- package/tgsl/shellless.js +73 -43
- package/tgsl/wgslGenerator.d.ts +66 -59
- package/tgsl/wgslGenerator.js +1344 -747
- package/types.d.ts +287 -221
- package/types.js +61 -44
- package/unwrapper.d.ts +31 -26
- package/unwrapper.js +1 -0
- package/wgslExtensions.d.ts +3 -5
- package/wgslExtensions.js +12 -16
- package/_virtual/rolldown_runtime.js +0 -13
- package/core/buffer/bufferShorthand.d.ts +0 -50
- package/core/buffer/bufferShorthand.js +0 -55
- package/core/pipeline/applyPipelineState.js +0 -37
- package/package.js +0 -5
- package/tgsl/shaderGenerator_members.d.ts +0 -33
- package/tgsl/shaderGenerator_members.js +0 -12
package/resolutionCtx.js
CHANGED
|
@@ -1,638 +1,925 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { MissingSlotValueError, ResolutionError, WgslTypeError
|
|
8
|
-
import { isLazy, isProviding, isSlot } from "./core/slot/slotTypes.js";
|
|
9
|
-
import { CodegenState, NormalState, isSelfResolvable } from "./types.js";
|
|
1
|
+
import { ConfigurableImpl } from "./core/root/configurableImpl.js";
|
|
2
|
+
import { isLazy, isProviding, isSlot, } from "./core/slot/slotTypes.js";
|
|
3
|
+
import { isData, UnknownData } from "./data/dataTypes.js";
|
|
4
|
+
import { bool } from "./data/numeric.js";
|
|
5
|
+
import { snip, withValue, } from "./data/snippet.js";
|
|
6
|
+
import { isPtr, isWgslArray, isWgslStruct, Void } from "./data/wgslTypes.js";
|
|
7
|
+
import { invariant, MissingSlotValueError, ResolutionError, WgslTypeError } from "./errors.js";
|
|
10
8
|
import { provideCtx, topLevelState } from "./execMode.js";
|
|
9
|
+
import { naturalsExcept } from "./shared/generators.js";
|
|
10
|
+
import { isMarkedInternal } from "./shared/symbols.js";
|
|
11
|
+
import { safeStringify } from "./shared/stringify.js";
|
|
12
|
+
import { $internal, $providing, $resolve } from "./shared/symbols.js";
|
|
13
|
+
import { bindGroupLayout, TgpuBindGroupImpl, } from "./tgpuBindGroupLayout.js";
|
|
14
|
+
import { LogGeneratorImpl, LogGeneratorNullImpl } from "./tgsl/consoleLog/logGenerator.js";
|
|
11
15
|
import { getBestConversion } from "./tgsl/conversion.js";
|
|
12
|
-
import { bool } from "./data/numeric.js";
|
|
13
16
|
import { coerceToSnippet, concretize, numericLiteralToSnippet } from "./tgsl/generationHelpers.js";
|
|
14
|
-
import {
|
|
17
|
+
import { WgslGenerator } from "./tgsl/wgslGenerator.js";
|
|
18
|
+
import { CodegenState, isSelfResolvable, NormalState } from "./types.js";
|
|
19
|
+
import { getName, hasTinyestMetadata, isNamable, setName } from "./shared/meta.js";
|
|
20
|
+
import { FuncParameterType } from 'tinyest';
|
|
21
|
+
import { accessProp } from "./tgsl/accessProp.js";
|
|
15
22
|
import { createIoSchema } from "./core/function/ioSchema.js";
|
|
23
|
+
import { isShelllessImpl } from "./core/function/shelllessImpl.js";
|
|
24
|
+
import { isTgpuFn } from "./core/function/tgpuFn.js";
|
|
16
25
|
import { AutoStruct } from "./data/autoStruct.js";
|
|
17
26
|
import { EntryInputRouter } from "./core/function/entryInputRouter.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { ConfigurableImpl } from "./core/root/configurableImpl.js";
|
|
21
|
-
import { naturalsExcept } from "./shared/generators.js";
|
|
22
|
-
import { TgpuBindGroupImpl, bindGroupLayout } from "./tgpuBindGroupLayout.js";
|
|
23
|
-
import { LogGeneratorImpl, LogGeneratorNullImpl } from "./tgsl/consoleLog/logGenerator.js";
|
|
24
|
-
import wgslGenerator_default from "./tgsl/wgslGenerator.js";
|
|
25
|
-
import { FuncParameterType } from "tinyest";
|
|
26
|
-
|
|
27
|
-
//#region src/resolutionCtx.ts
|
|
27
|
+
import { validateIdentifier, sanitizePrimer, bannedTokens } from "./nameUtils.js";
|
|
28
|
+
import { minify } from "./minify.js";
|
|
28
29
|
/**
|
|
29
|
-
* Inserted into bind group entry definitions that belong
|
|
30
|
-
* to the automatically generated catch-all bind group.
|
|
31
|
-
*
|
|
32
|
-
* A non-occupied group index can only be determined after
|
|
33
|
-
* every resource has been resolved, so this acts as a placeholder
|
|
34
|
-
* to be replaced with an actual numeric index at the very end
|
|
35
|
-
* of the resolution process.
|
|
36
|
-
*/
|
|
37
|
-
const CATCHALL_BIND_GROUP_IDX_MARKER =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
30
|
+
* Inserted into bind group entry definitions that belong
|
|
31
|
+
* to the automatically generated catch-all bind group.
|
|
32
|
+
*
|
|
33
|
+
* A non-occupied group index can only be determined after
|
|
34
|
+
* every resource has been resolved, so this acts as a placeholder
|
|
35
|
+
* to be replaced with an actual numeric index at the very end
|
|
36
|
+
* of the resolution process.
|
|
37
|
+
*/
|
|
38
|
+
const CATCHALL_BIND_GROUP_IDX_MARKER = '#CATCHALL#';
|
|
39
|
+
class ItemStateStackImpl {
|
|
40
|
+
_stack = [];
|
|
41
|
+
_itemDepth = 0;
|
|
42
|
+
get itemDepth() {
|
|
43
|
+
return this._itemDepth;
|
|
44
|
+
}
|
|
45
|
+
get topItem() {
|
|
46
|
+
const state = this._stack[this._stack.length - 1];
|
|
47
|
+
if (!state || state.type !== 'item') {
|
|
48
|
+
throw new Error('Internal error, expected item layer to be on top.');
|
|
49
|
+
}
|
|
50
|
+
return state;
|
|
51
|
+
}
|
|
52
|
+
get topFunctionScope() {
|
|
53
|
+
return this._stack.findLast((e) => e.type === 'functionScope');
|
|
54
|
+
}
|
|
55
|
+
get topBlockScope() {
|
|
56
|
+
return this._stack.findLast((e) => e.type === 'blockScope');
|
|
57
|
+
}
|
|
58
|
+
get blockDepth() {
|
|
59
|
+
let depth = 0;
|
|
60
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
61
|
+
const layer = this._stack[i];
|
|
62
|
+
if (layer?.type === 'functionScope') {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
if (layer?.type === 'blockScope') {
|
|
66
|
+
depth++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return depth;
|
|
70
|
+
}
|
|
71
|
+
pushItem() {
|
|
72
|
+
this._itemDepth++;
|
|
73
|
+
this._stack.push({
|
|
74
|
+
type: 'item',
|
|
75
|
+
usedSlots: new Set(),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
pushSlotBindings(pairs) {
|
|
79
|
+
this._stack.push({
|
|
80
|
+
type: 'slotBinding',
|
|
81
|
+
bindingMap: new WeakMap(pairs),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
pushFunctionScope(functionType, argAccess, returnType, externalMap) {
|
|
85
|
+
const scope = {
|
|
86
|
+
type: 'functionScope',
|
|
87
|
+
functionType,
|
|
88
|
+
argAccess,
|
|
89
|
+
returnType,
|
|
90
|
+
externalMap,
|
|
91
|
+
reportedReturnTypes: new Set(),
|
|
92
|
+
placeholderForVariable: new Map(),
|
|
93
|
+
modifiedVariables: new Set(),
|
|
94
|
+
};
|
|
95
|
+
this._stack.push(scope);
|
|
96
|
+
return scope;
|
|
97
|
+
}
|
|
98
|
+
pushBlockScope() {
|
|
99
|
+
this._stack.push({
|
|
100
|
+
type: 'blockScope',
|
|
101
|
+
takenLocalIdentifiers: new Set(),
|
|
102
|
+
declarations: new Map(),
|
|
103
|
+
externals: new Map(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
pop(type) {
|
|
107
|
+
const layer = this._stack[this._stack.length - 1];
|
|
108
|
+
if (!layer || (type && layer.type !== type)) {
|
|
109
|
+
throw new Error(`Internal error, expected a ${type} layer to be on top.`);
|
|
110
|
+
}
|
|
111
|
+
const poppedValue = this._stack.pop();
|
|
112
|
+
if (type === 'item') {
|
|
113
|
+
this._itemDepth--;
|
|
114
|
+
}
|
|
115
|
+
return poppedValue;
|
|
116
|
+
}
|
|
117
|
+
readSlot(slot) {
|
|
118
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
119
|
+
const layer = this._stack[i];
|
|
120
|
+
if (layer?.type === 'item') {
|
|
121
|
+
// Binding not available yet, so this layer is dependent on the slot's value.
|
|
122
|
+
layer.usedSlots.add(slot);
|
|
123
|
+
}
|
|
124
|
+
else if (layer?.type === 'slotBinding') {
|
|
125
|
+
const boundValue = layer.bindingMap.get(slot);
|
|
126
|
+
if (boundValue !== undefined) {
|
|
127
|
+
return boundValue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (layer?.type === 'functionScope' || layer?.type === 'blockScope') {
|
|
131
|
+
// Skip
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new Error('Unknown layer type.');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return slot.defaultValue;
|
|
138
|
+
}
|
|
139
|
+
getSnippetById(id) {
|
|
140
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
141
|
+
const layer = this._stack[i];
|
|
142
|
+
if (layer?.type === 'functionScope') {
|
|
143
|
+
const access = layer.argAccess[id];
|
|
144
|
+
if (access) {
|
|
145
|
+
return access();
|
|
146
|
+
}
|
|
147
|
+
if (Object.hasOwn(layer.externalMap, id)) {
|
|
148
|
+
const external = layer.externalMap[id];
|
|
149
|
+
if (isNamable(external) && getName(external) === undefined) {
|
|
150
|
+
setName(external, id.replaceAll('.', '_'));
|
|
151
|
+
}
|
|
152
|
+
return coerceToSnippet(external);
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
if (layer?.type === 'blockScope') {
|
|
157
|
+
// the order matters
|
|
158
|
+
const snippet = layer.declarations.get(id) ?? layer.externals.get(id);
|
|
159
|
+
if (snippet !== undefined) {
|
|
160
|
+
return snippet;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// Skip
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Returns whether the given identifier is taken in any block scope up to the nearest function scope.
|
|
171
|
+
*/
|
|
172
|
+
isIdentifierTakenLocally(id) {
|
|
173
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
174
|
+
const layer = this._stack[i];
|
|
175
|
+
if (layer?.type === 'functionScope') {
|
|
176
|
+
// Since functions cannot access resources from the calling scope, we
|
|
177
|
+
// return early here.
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if (layer?.type === 'blockScope') {
|
|
181
|
+
if (layer.takenLocalIdentifiers.has(id)) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Returns whether the given identifier is taken in any block scope on the stack.
|
|
190
|
+
*
|
|
191
|
+
* This is useful when resolving a global identifier for the first time within a nested function.
|
|
192
|
+
*/
|
|
193
|
+
isIdentifierTakenInCallStack(id) {
|
|
194
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
195
|
+
const layer = this._stack[i];
|
|
196
|
+
if (layer?.type === 'blockScope') {
|
|
197
|
+
if (layer.takenLocalIdentifiers.has(id)) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
defineBlockVariable(id, snippet) {
|
|
205
|
+
if (snippet.dataType === UnknownData) {
|
|
206
|
+
throw Error(`Tried to define variable '${id}' of unknown type`);
|
|
207
|
+
}
|
|
208
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
209
|
+
const layer = this._stack[i];
|
|
210
|
+
if (layer?.type === 'blockScope') {
|
|
211
|
+
layer.declarations.set(id, snippet);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
throw new Error('No block scope found to define a variable in.');
|
|
216
|
+
}
|
|
217
|
+
setBlockExternals(externals) {
|
|
218
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
219
|
+
const layer = this._stack[i];
|
|
220
|
+
if (layer?.type === 'blockScope') {
|
|
221
|
+
Object.entries(externals).forEach(([id, snippet]) => {
|
|
222
|
+
layer.externals.set(id, snippet);
|
|
223
|
+
});
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
throw new Error('No block scope found to set externals in.');
|
|
228
|
+
}
|
|
229
|
+
clearBlockExternals() {
|
|
230
|
+
for (let i = this._stack.length - 1; i >= 0; --i) {
|
|
231
|
+
const layer = this._stack[i];
|
|
232
|
+
if (layer?.type === 'blockScope') {
|
|
233
|
+
layer.externals.clear();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
throw new Error('No block scope found to clear externals in.');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
168
240
|
const INDENT = [
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
241
|
+
'', // 0
|
|
242
|
+
' ', // 1
|
|
243
|
+
' ', // 2
|
|
244
|
+
' ', // 3
|
|
245
|
+
' ', // 4
|
|
246
|
+
' ', // 5
|
|
247
|
+
' ', // 6
|
|
248
|
+
' ', // 7
|
|
249
|
+
' ', // 8
|
|
178
250
|
];
|
|
179
251
|
const N = INDENT.length - 1;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
252
|
+
export class IndentController {
|
|
253
|
+
identLevel = 0;
|
|
254
|
+
get pre() {
|
|
255
|
+
return (INDENT[this.identLevel] ??
|
|
256
|
+
INDENT[N].repeat(this.identLevel / N) + INDENT[this.identLevel % N]);
|
|
257
|
+
}
|
|
258
|
+
indent() {
|
|
259
|
+
const str = this.pre;
|
|
260
|
+
this.identLevel++;
|
|
261
|
+
return str;
|
|
262
|
+
}
|
|
263
|
+
dedent() {
|
|
264
|
+
this.identLevel--;
|
|
265
|
+
return this.pre;
|
|
266
|
+
}
|
|
267
|
+
withResetLevel(callback) {
|
|
268
|
+
const savedLevel = this.identLevel;
|
|
269
|
+
this.identLevel = 0;
|
|
270
|
+
try {
|
|
271
|
+
return callback();
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
this.identLevel = savedLevel;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function createArgument(name, type, origin = 'argument') {
|
|
279
|
+
let used = false;
|
|
280
|
+
return {
|
|
281
|
+
name,
|
|
282
|
+
access: () => {
|
|
283
|
+
used = true;
|
|
284
|
+
return snip(name, type, origin, /* possibleSideEffects */ false);
|
|
285
|
+
},
|
|
286
|
+
decoratedType: type,
|
|
287
|
+
get used() {
|
|
288
|
+
return used;
|
|
289
|
+
},
|
|
290
|
+
};
|
|
217
291
|
}
|
|
218
292
|
function createArgumentPropAccess(argAccess, prop) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
293
|
+
return () => {
|
|
294
|
+
const argSnippet = argAccess();
|
|
295
|
+
if (!argSnippet) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
return accessProp(argSnippet, prop);
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
export class ResolutionCtxImpl {
|
|
302
|
+
#namespaceInternal;
|
|
303
|
+
_indentController = new IndentController();
|
|
304
|
+
_itemStateStack = new ItemStateStackImpl();
|
|
305
|
+
#modeStack = [];
|
|
306
|
+
_declarations = [];
|
|
307
|
+
_varyingLocations;
|
|
308
|
+
/**
|
|
309
|
+
* Holds a set of base (slot-less) functions that have started their resolution process.
|
|
310
|
+
* Used for recursion detection check - a function is recursive if:
|
|
311
|
+
* - it was passed to ctx.resolve while already present in this set,
|
|
312
|
+
* - it never finished resolution (<=> it does not appear in `memoizedResolves`).
|
|
313
|
+
* The set is NOT cleared after the resolution finishes.
|
|
314
|
+
*/
|
|
315
|
+
#startedFunctionResolves = new WeakSet();
|
|
316
|
+
#logGenerator;
|
|
317
|
+
gen;
|
|
318
|
+
get varyingLocations() {
|
|
319
|
+
return this._varyingLocations;
|
|
320
|
+
}
|
|
321
|
+
[$internal] = {
|
|
322
|
+
itemStateStack: this._itemStateStack,
|
|
323
|
+
};
|
|
324
|
+
// -- Bindings
|
|
325
|
+
/**
|
|
326
|
+
* A map from registered bind group layouts to random strings put in
|
|
327
|
+
* place of their group index. The whole tree has to be traversed to
|
|
328
|
+
* collect every use of a typed bind group layout, since they can be
|
|
329
|
+
* explicitly imposed group indices, and they cannot collide.
|
|
330
|
+
*/
|
|
331
|
+
bindGroupLayoutsToPlaceholderMap = new Map();
|
|
332
|
+
_nextFreeLayoutPlaceholderIdx = 0;
|
|
333
|
+
fixedBindings = [];
|
|
334
|
+
// --
|
|
335
|
+
enableExtensions;
|
|
336
|
+
expectedType;
|
|
337
|
+
/**
|
|
338
|
+
* A counter used to generate unique identifiers for globally-scoped definitions in the 'random' strategy.
|
|
339
|
+
*/
|
|
340
|
+
#lastUniqueId = 0;
|
|
341
|
+
constructor(opts) {
|
|
342
|
+
this.enableExtensions = opts.enableExtensions;
|
|
343
|
+
this.#logGenerator = opts.root ? new LogGeneratorImpl(opts.root) : new LogGeneratorNullImpl();
|
|
344
|
+
this.#namespaceInternal = opts.namespace[$internal];
|
|
345
|
+
this.gen = opts.shaderGenerator ?? new WgslGenerator();
|
|
346
|
+
this.gen.initGenerator(this);
|
|
347
|
+
}
|
|
348
|
+
isIdentifierBanned(name) {
|
|
349
|
+
return bannedTokens.has(name);
|
|
350
|
+
}
|
|
351
|
+
isIdentifierTaken(name, scope) {
|
|
352
|
+
return (this.#namespaceInternal.takenGlobalIdentifiers.has(name) ||
|
|
353
|
+
(scope === 'block'
|
|
354
|
+
? this._itemStateStack.isIdentifierTakenLocally(name)
|
|
355
|
+
: this._itemStateStack.isIdentifierTakenInCallStack(name)));
|
|
356
|
+
}
|
|
357
|
+
makeUniqueIdentifier(primer = 'item', scope) {
|
|
358
|
+
if (scope === 'block' &&
|
|
359
|
+
validateIdentifier(primer).success &&
|
|
360
|
+
!this.isIdentifierTaken(primer, scope)) {
|
|
361
|
+
// Preserving local definitions as they are, provided they are valid and not already taken.
|
|
362
|
+
this.reserveIdentifier(primer, 'block');
|
|
363
|
+
return primer;
|
|
364
|
+
}
|
|
365
|
+
const base = sanitizePrimer(primer);
|
|
366
|
+
let index = 0;
|
|
367
|
+
const random = this.#namespaceInternal.strategy === 'random';
|
|
368
|
+
let name = random ? `${base}_${this.#lastUniqueId++}` : base;
|
|
369
|
+
while (this.isIdentifierTaken(name, scope)) {
|
|
370
|
+
name = random ? `${base}_${this.#lastUniqueId++}` : `${base}_${++index}`;
|
|
371
|
+
}
|
|
372
|
+
this.reserveIdentifier(name, scope);
|
|
373
|
+
return name;
|
|
374
|
+
}
|
|
375
|
+
reserveIdentifier(name, scope) {
|
|
376
|
+
if (scope === 'block') {
|
|
377
|
+
const blockScope = this._itemStateStack.topBlockScope;
|
|
378
|
+
if (blockScope) {
|
|
379
|
+
blockScope.takenLocalIdentifiers.add(name);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// Fall through if no block scope is present, treating as global.
|
|
383
|
+
}
|
|
384
|
+
this.#namespaceInternal.takenGlobalIdentifiers.add(name);
|
|
385
|
+
}
|
|
386
|
+
get pre() {
|
|
387
|
+
return this._indentController.pre;
|
|
388
|
+
}
|
|
389
|
+
get topFunctionScope() {
|
|
390
|
+
return this._itemStateStack.topFunctionScope;
|
|
391
|
+
}
|
|
392
|
+
get topFunctionReturnType() {
|
|
393
|
+
const scope = this._itemStateStack.topFunctionScope;
|
|
394
|
+
invariant(scope, 'Internal error, expected function scope to be present.');
|
|
395
|
+
return scope.returnType;
|
|
396
|
+
}
|
|
397
|
+
get shelllessRepo() {
|
|
398
|
+
return this.#namespaceInternal.shelllessRepo;
|
|
399
|
+
}
|
|
400
|
+
get blockDepth() {
|
|
401
|
+
return this._itemStateStack.blockDepth;
|
|
402
|
+
}
|
|
403
|
+
indent() {
|
|
404
|
+
return this._indentController.indent();
|
|
405
|
+
}
|
|
406
|
+
dedent() {
|
|
407
|
+
return this._indentController.dedent();
|
|
408
|
+
}
|
|
409
|
+
getDedented(code) {
|
|
410
|
+
return code.replaceAll(`\n${INDENT[1]}`, '\n');
|
|
411
|
+
}
|
|
412
|
+
withResetIndentLevel(callback) {
|
|
413
|
+
return this._indentController.withResetLevel(callback);
|
|
414
|
+
}
|
|
415
|
+
getById(id) {
|
|
416
|
+
const item = this._itemStateStack.getSnippetById(id);
|
|
417
|
+
if (item === undefined) {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
return item;
|
|
421
|
+
}
|
|
422
|
+
defineVariable(id, snippet) {
|
|
423
|
+
this._itemStateStack.defineBlockVariable(id, snippet);
|
|
424
|
+
}
|
|
425
|
+
reportReturnType(dataType) {
|
|
426
|
+
const scope = this._itemStateStack.topFunctionScope;
|
|
427
|
+
invariant(scope, 'Internal error, expected function scope to be present.');
|
|
428
|
+
scope.reportedReturnTypes.add(dataType);
|
|
429
|
+
}
|
|
430
|
+
pushBlockScope() {
|
|
431
|
+
this._itemStateStack.pushBlockScope();
|
|
432
|
+
}
|
|
433
|
+
popBlockScope() {
|
|
434
|
+
this._itemStateStack.pop('blockScope');
|
|
435
|
+
}
|
|
436
|
+
setBlockExternals(externals) {
|
|
437
|
+
this._itemStateStack.setBlockExternals(externals);
|
|
438
|
+
}
|
|
439
|
+
clearBlockExternals() {
|
|
440
|
+
this._itemStateStack.clearBlockExternals();
|
|
441
|
+
}
|
|
442
|
+
generateLog(op, args) {
|
|
443
|
+
return this.#logGenerator.generateLog(this, op, args);
|
|
444
|
+
}
|
|
445
|
+
get logResources() {
|
|
446
|
+
return this.#logGenerator.logResources;
|
|
447
|
+
}
|
|
448
|
+
resolveFunction(options) {
|
|
449
|
+
try {
|
|
450
|
+
const scope = this._itemStateStack.pushFunctionScope(options.functionType, {}, options.returnType, options.externalMap);
|
|
451
|
+
// Pushing a block scope as well, so that any identifiers declared at this point will be scoped to the function body.
|
|
452
|
+
this._itemStateStack.pushBlockScope();
|
|
453
|
+
const args = [];
|
|
454
|
+
if (options.entryInput) {
|
|
455
|
+
const { dataSchema, positionalArgs } = options.entryInput;
|
|
456
|
+
const firstParam = options.params[0];
|
|
457
|
+
const structArg = dataSchema
|
|
458
|
+
? createArgument(this.makeUniqueIdentifier('_arg_0', 'block'), dataSchema)
|
|
459
|
+
: undefined;
|
|
460
|
+
if (structArg) {
|
|
461
|
+
args.push(structArg);
|
|
462
|
+
}
|
|
463
|
+
if (firstParam?.type === FuncParameterType.destructuredObject) {
|
|
464
|
+
// Route each destructured prop to a positional arg or struct field.
|
|
465
|
+
for (const { name, alias } of firstParam.props) {
|
|
466
|
+
const argInfo = positionalArgs.find((a) => a.schemaKey === name);
|
|
467
|
+
if (argInfo) {
|
|
468
|
+
const arg = createArgument(this.makeUniqueIdentifier(alias, 'block'), argInfo.type);
|
|
469
|
+
args.push(arg);
|
|
470
|
+
scope.argAccess[alias] = arg.access;
|
|
471
|
+
}
|
|
472
|
+
else if (structArg) {
|
|
473
|
+
scope.argAccess[alias] = createArgumentPropAccess(structArg.access, name);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
else if (firstParam?.type === FuncParameterType.identifier) {
|
|
478
|
+
// Create named arg snippets, then a proxy for property access routing.
|
|
479
|
+
const proxyEntries = [];
|
|
480
|
+
for (const a of positionalArgs) {
|
|
481
|
+
const argName = this.makeUniqueIdentifier(a.schemaKey, 'block');
|
|
482
|
+
const arg = createArgument(argName, a.type);
|
|
483
|
+
args.push(arg);
|
|
484
|
+
proxyEntries.push({ schemaKey: a.schemaKey, arg: arg.access });
|
|
485
|
+
}
|
|
486
|
+
const router = new EntryInputRouter(structArg?.access, proxyEntries);
|
|
487
|
+
scope.argAccess[firstParam.name] = () => snip('N/A', router, 'argument');
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
// No first param: push positional args with schema key names.
|
|
491
|
+
for (const a of positionalArgs) {
|
|
492
|
+
const argName = this.makeUniqueIdentifier(`_arg_${a.schemaKey}`, 'block');
|
|
493
|
+
const arg = createArgument(argName, a.type);
|
|
494
|
+
args.push(arg);
|
|
495
|
+
scope.argAccess[argName] = arg.access;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
for (const [i, argType] of options.argTypes.entries()) {
|
|
501
|
+
const astParam = options.params[i];
|
|
502
|
+
// We know if arguments are passed by reference or by value, because we
|
|
503
|
+
// enforce that based on the whether the argument is a pointer or not.
|
|
504
|
+
//
|
|
505
|
+
// It still applies for shell-less functions, since we determine the type
|
|
506
|
+
// of the argument based on the argument's referentiality.
|
|
507
|
+
// In other words, if we pass a reference to a function, it's typed as a pointer,
|
|
508
|
+
// otherwise it's typed as a value.
|
|
509
|
+
const origin = isPtr(argType)
|
|
510
|
+
? argType.addressSpace === 'storage'
|
|
511
|
+
? argType.access === 'read'
|
|
512
|
+
? 'readonly'
|
|
513
|
+
: 'mutable'
|
|
514
|
+
: argType.addressSpace
|
|
515
|
+
: 'argument';
|
|
516
|
+
switch (astParam?.type) {
|
|
517
|
+
case FuncParameterType.identifier: {
|
|
518
|
+
const arg = createArgument(this.makeUniqueIdentifier(astParam.name, 'block'), argType, origin);
|
|
519
|
+
args.push(arg);
|
|
520
|
+
scope.argAccess[astParam.name] = arg.access;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
case FuncParameterType.destructuredObject: {
|
|
524
|
+
const objArg = createArgument(this.makeUniqueIdentifier(`_arg_${i}`, 'block'), argType, origin);
|
|
525
|
+
args.push(objArg);
|
|
526
|
+
for (const { name, alias } of astParam.props) {
|
|
527
|
+
scope.argAccess[alias] = createArgumentPropAccess(objArg.access, name);
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case undefined: {
|
|
532
|
+
// Only push the argument if it's not an auto-struct.
|
|
533
|
+
// If we're not using an auto-struct, it's not going to
|
|
534
|
+
// have any properties anyway.
|
|
535
|
+
if (!(argType instanceof AutoStruct)) {
|
|
536
|
+
args.push({
|
|
537
|
+
name: this.makeUniqueIdentifier(`_arg_${i}`, 'block'),
|
|
538
|
+
access: () => {
|
|
539
|
+
throw new Error(`Unreachable: Accessing an argument that wasn't named in the function signature`);
|
|
540
|
+
},
|
|
541
|
+
decoratedType: argType,
|
|
542
|
+
used: false,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
let returnType;
|
|
550
|
+
const code = this.gen.functionDefinition({
|
|
551
|
+
functionType: options.functionType,
|
|
552
|
+
name: options.name,
|
|
553
|
+
workgroupSize: options.workgroupSize,
|
|
554
|
+
args,
|
|
555
|
+
body: options.body,
|
|
556
|
+
determineReturnType: () => {
|
|
557
|
+
if (returnType) {
|
|
558
|
+
// Already determined
|
|
559
|
+
return returnType;
|
|
560
|
+
}
|
|
561
|
+
returnType = options.returnType;
|
|
562
|
+
if (returnType instanceof AutoStruct) {
|
|
563
|
+
// We're expecting an "auto" return type, so if there were structs returned,
|
|
564
|
+
// we accept the struct, otherwise we let the rest of the code unify on a
|
|
565
|
+
// primitive type.
|
|
566
|
+
if (isWgslStruct(scope.reportedReturnTypes.values().next().value)) {
|
|
567
|
+
returnType = returnType.completeStruct;
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
returnType = undefined;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (!returnType) {
|
|
574
|
+
const returnTypes = [...scope.reportedReturnTypes];
|
|
575
|
+
if (returnTypes.length === 0) {
|
|
576
|
+
returnType = Void;
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
const conversion = getBestConversion(returnTypes);
|
|
580
|
+
if (conversion && !conversion.hasImplicitConversions) {
|
|
581
|
+
returnType = conversion.targetType;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (!returnType) {
|
|
585
|
+
throw new Error(`Expected function to have a single return type, got [${returnTypes.join(', ')}]. Cast explicitly to the desired type.`);
|
|
586
|
+
}
|
|
587
|
+
returnType = concretize(returnType);
|
|
588
|
+
if (options.functionType === 'vertex' || options.functionType === 'fragment') {
|
|
589
|
+
returnType = createIoSchema(returnType);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return returnType;
|
|
593
|
+
},
|
|
594
|
+
});
|
|
595
|
+
if (!returnType) {
|
|
596
|
+
throw new Error(`Failed to determine return type`);
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
code,
|
|
600
|
+
returnType,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
finally {
|
|
604
|
+
this._itemStateStack.pop('blockScope');
|
|
605
|
+
this._itemStateStack.pop('functionScope');
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
addDeclaration(declaration, name) {
|
|
609
|
+
this._declarations.push({ name, code: declaration });
|
|
610
|
+
}
|
|
611
|
+
get declarations() {
|
|
612
|
+
return this._declarations;
|
|
613
|
+
}
|
|
614
|
+
allocateLayoutEntry(layout) {
|
|
615
|
+
const memoMap = this.bindGroupLayoutsToPlaceholderMap;
|
|
616
|
+
let placeholderKey = memoMap.get(layout);
|
|
617
|
+
if (!placeholderKey) {
|
|
618
|
+
placeholderKey = `#BIND_GROUP_LAYOUT_${this._nextFreeLayoutPlaceholderIdx++}#`;
|
|
619
|
+
memoMap.set(layout, placeholderKey);
|
|
620
|
+
}
|
|
621
|
+
return placeholderKey;
|
|
622
|
+
}
|
|
623
|
+
allocateFixedEntry(layoutEntry, resource) {
|
|
624
|
+
const binding = this.fixedBindings.length;
|
|
625
|
+
this.fixedBindings.push({ layoutEntry, resource });
|
|
626
|
+
return {
|
|
627
|
+
group: CATCHALL_BIND_GROUP_IDX_MARKER,
|
|
628
|
+
binding,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
readSlot(slot) {
|
|
632
|
+
const value = this._itemStateStack.readSlot(slot);
|
|
633
|
+
if (value === undefined) {
|
|
634
|
+
throw new MissingSlotValueError(slot);
|
|
635
|
+
}
|
|
636
|
+
return value;
|
|
637
|
+
}
|
|
638
|
+
withSlots(pairs, callback) {
|
|
639
|
+
if (pairs.length === 0) {
|
|
640
|
+
return callback();
|
|
641
|
+
}
|
|
642
|
+
this._itemStateStack.pushSlotBindings(pairs);
|
|
643
|
+
try {
|
|
644
|
+
return callback();
|
|
645
|
+
}
|
|
646
|
+
finally {
|
|
647
|
+
this._itemStateStack.pop('slotBinding');
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
withVaryingLocations(locations, callback) {
|
|
651
|
+
this._varyingLocations = locations;
|
|
652
|
+
try {
|
|
653
|
+
return callback();
|
|
654
|
+
}
|
|
655
|
+
finally {
|
|
656
|
+
this._varyingLocations = undefined;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
withRenamed(item, name, callback) {
|
|
660
|
+
if (!name) {
|
|
661
|
+
return callback();
|
|
662
|
+
}
|
|
663
|
+
const oldName = getName(item);
|
|
664
|
+
try {
|
|
665
|
+
setName(item, name);
|
|
666
|
+
return callback();
|
|
667
|
+
}
|
|
668
|
+
finally {
|
|
669
|
+
if (oldName) {
|
|
670
|
+
setName(item, oldName);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
unwrap(eventual) {
|
|
675
|
+
if (isProviding(eventual)) {
|
|
676
|
+
return this.withRenamed(eventual[$providing].inner, getName(eventual), () => this.withSlots(eventual[$providing].pairs, () => this.unwrap(eventual[$providing].inner)));
|
|
677
|
+
}
|
|
678
|
+
let maybeEventual = eventual;
|
|
679
|
+
// Unwrapping all layers of slots.
|
|
680
|
+
while (true) {
|
|
681
|
+
if (isSlot(maybeEventual)) {
|
|
682
|
+
maybeEventual = this.readSlot(maybeEventual);
|
|
683
|
+
}
|
|
684
|
+
else if (isLazy(maybeEventual)) {
|
|
685
|
+
maybeEventual = this._getOrCompute(maybeEventual);
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return maybeEventual;
|
|
692
|
+
}
|
|
693
|
+
_getOrCompute(lazy) {
|
|
694
|
+
// All memoized versions of `lazy`
|
|
695
|
+
const instances = this.#namespaceInternal.memoizedLazy.get(lazy) ?? [];
|
|
696
|
+
this._itemStateStack.pushItem();
|
|
697
|
+
try {
|
|
698
|
+
for (const instance of instances) {
|
|
699
|
+
const slotValuePairs = [...instance.slotToValueMap.entries()];
|
|
700
|
+
if (slotValuePairs.every(([slot, expectedValue]) => slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue))) {
|
|
701
|
+
return instance.result;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
// If we got here, no item with the given slot-to-value combo exists in cache yet
|
|
705
|
+
// Getting out of codegen or simulation mode so we can execute JS normally.
|
|
706
|
+
this.pushMode(new NormalState());
|
|
707
|
+
let result;
|
|
708
|
+
try {
|
|
709
|
+
result = lazy[$internal].compute();
|
|
710
|
+
}
|
|
711
|
+
finally {
|
|
712
|
+
this.popMode('normal');
|
|
713
|
+
}
|
|
714
|
+
// We know which slots the item used while resolving
|
|
715
|
+
const slotToValueMap = new Map();
|
|
716
|
+
for (const usedSlot of this._itemStateStack.topItem.usedSlots) {
|
|
717
|
+
slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));
|
|
718
|
+
}
|
|
719
|
+
instances.push({ slotToValueMap, result });
|
|
720
|
+
this.#namespaceInternal.memoizedLazy.set(lazy, instances);
|
|
721
|
+
return result;
|
|
722
|
+
}
|
|
723
|
+
catch (err) {
|
|
724
|
+
if (err instanceof ResolutionError) {
|
|
725
|
+
throw err.appendToTrace(lazy);
|
|
726
|
+
}
|
|
727
|
+
throw new ResolutionError(err, [lazy]);
|
|
728
|
+
}
|
|
729
|
+
finally {
|
|
730
|
+
this._itemStateStack.pop('item');
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.
|
|
735
|
+
*/
|
|
736
|
+
_getOrInstantiate(item) {
|
|
737
|
+
// All memoized versions of `item`
|
|
738
|
+
const instances = this.#namespaceInternal.memoizedResolves.get(item) ?? [];
|
|
739
|
+
this._itemStateStack.pushItem();
|
|
740
|
+
try {
|
|
741
|
+
for (const instance of instances) {
|
|
742
|
+
const slotValuePairs = [...instance.slotToValueMap.entries()];
|
|
743
|
+
if (slotValuePairs.every(([slot, expectedValue]) => slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue))) {
|
|
744
|
+
return instance.result;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
// If we got here, no item with the given slot-to-value combo exists in cache yet
|
|
748
|
+
let result;
|
|
749
|
+
if (isData(item)) {
|
|
750
|
+
// Ref is arbitrary, as we're resolving a schema
|
|
751
|
+
result = snip(this.gen.emitTypeAnnotation(item), Void, /* origin */ 'runtime');
|
|
752
|
+
}
|
|
753
|
+
else if (isLazy(item) || isSlot(item)) {
|
|
754
|
+
result = this.resolve(this.unwrap(item));
|
|
755
|
+
}
|
|
756
|
+
else if (isSelfResolvable(item)) {
|
|
757
|
+
result = item[$resolve](this);
|
|
758
|
+
}
|
|
759
|
+
else if (hasTinyestMetadata(item)) {
|
|
760
|
+
// Resolving a function with tinyest metadata directly means calling it with no arguments, since
|
|
761
|
+
// we cannot infer the types of the arguments from a WGSL string.
|
|
762
|
+
const shellless = this.#namespaceInternal.shelllessRepo.get(item,
|
|
763
|
+
/* no arguments */ undefined);
|
|
764
|
+
if (!shellless) {
|
|
765
|
+
throw new Error(`Couldn't resolve ${item.name}. Make sure it's a function that accepts no arguments, or call it from another TypeGPU function.`);
|
|
766
|
+
}
|
|
767
|
+
return this.withResetIndentLevel(() => this.resolve(shellless));
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
throw new TypeError(`Unresolvable internal value: ${safeStringify(item)}`);
|
|
771
|
+
}
|
|
772
|
+
// We know which slots the item used while resolving
|
|
773
|
+
const slotToValueMap = new Map();
|
|
774
|
+
for (const usedSlot of this._itemStateStack.topItem.usedSlots) {
|
|
775
|
+
slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));
|
|
776
|
+
}
|
|
777
|
+
instances.push({ slotToValueMap, result });
|
|
778
|
+
this.#namespaceInternal.memoizedResolves.set(item, instances);
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
catch (err) {
|
|
782
|
+
if (err instanceof ResolutionError) {
|
|
783
|
+
throw err.appendToTrace(item);
|
|
784
|
+
}
|
|
785
|
+
throw new ResolutionError(err, [item]);
|
|
786
|
+
}
|
|
787
|
+
finally {
|
|
788
|
+
this._itemStateStack.pop('item');
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
resolve(item, schema) {
|
|
792
|
+
if (typeof item === 'string') {
|
|
793
|
+
if (!schema || schema === UnknownData) {
|
|
794
|
+
throw new Error(`Strings cannot be injected into WGSL directly (tried to inject '${item}'). Look for TypeGPU APIs that cover your use-case, or resort to using tgpu['~unstable'].rawCodeSnippet for raw code injection.`);
|
|
795
|
+
}
|
|
796
|
+
// For example:
|
|
797
|
+
// () => { 'use gpu'; const color = d.vec3f(); return color; }
|
|
798
|
+
// snip('color', d.vec3f) ^^^^^
|
|
799
|
+
return snip(item, schema, /* origin */ 'runtime');
|
|
800
|
+
}
|
|
801
|
+
if ((isTgpuFn(item) || isShelllessImpl(item)) && !isProviding(item)) {
|
|
802
|
+
// We skip providing functions to only perform the checks on slot-less functions.
|
|
803
|
+
if (this.#startedFunctionResolves.has(item) &&
|
|
804
|
+
!this.#namespaceInternal.memoizedResolves.has(item)) {
|
|
805
|
+
throw new Error(`Recursive function ${item} detected. Recursion is not allowed on the GPU.`);
|
|
806
|
+
}
|
|
807
|
+
this.#startedFunctionResolves.add(item);
|
|
808
|
+
}
|
|
809
|
+
if (isProviding(item)) {
|
|
810
|
+
return this.withRenamed(item[$providing].inner, getName(item), () => this.withSlots(item[$providing].pairs, () => this.resolve(item[$providing].inner, schema)));
|
|
811
|
+
}
|
|
812
|
+
if (isMarkedInternal(item) || hasTinyestMetadata(item)) {
|
|
813
|
+
// Top-level resolve
|
|
814
|
+
if (this._itemStateStack.itemDepth === 0) {
|
|
815
|
+
try {
|
|
816
|
+
this.pushMode(new CodegenState());
|
|
817
|
+
const result = provideCtx(this, () => this._getOrInstantiate(item));
|
|
818
|
+
return snip(`${this._declarations.map((decl) => decl.code).join('\n\n')}${result.value}`, Void,
|
|
819
|
+
/* origin */ 'runtime');
|
|
820
|
+
}
|
|
821
|
+
finally {
|
|
822
|
+
this.popMode('codegen');
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return this._getOrInstantiate(item);
|
|
826
|
+
}
|
|
827
|
+
// This is a value that comes from the outside, maybe we can coerce it
|
|
828
|
+
if (typeof item === 'number') {
|
|
829
|
+
const realSchema = schema ?? numericLiteralToSnippet(item).dataType;
|
|
830
|
+
invariant(realSchema !== UnknownData, 'Schema has to be known for resolving numbers');
|
|
831
|
+
return this.gen.numericLiteral(item, realSchema);
|
|
832
|
+
}
|
|
833
|
+
if (typeof item === 'boolean') {
|
|
834
|
+
return snip(item ? 'true' : 'false', bool, /* origin */ 'constant', false);
|
|
835
|
+
}
|
|
836
|
+
if (schema && isWgslArray(schema)) {
|
|
837
|
+
if (!Array.isArray(item)) {
|
|
838
|
+
throw new WgslTypeError(`Cannot coerce ${item} into value of type '${schema}'`);
|
|
839
|
+
}
|
|
840
|
+
if (schema.elementCount !== item.length) {
|
|
841
|
+
throw new WgslTypeError(`Cannot create value of type '${schema}' from an array of length: ${item.length}`);
|
|
842
|
+
}
|
|
843
|
+
return this.gen.typeInstantiation(schema, item.map((element) => snip(element, schema.elementType, /* origin */ 'runtime')));
|
|
844
|
+
}
|
|
845
|
+
if (schema && isWgslStruct(schema)) {
|
|
846
|
+
return this.gen.typeInstantiation(schema, Object.entries(schema.propTypes).map(([key, propType]) => snip(item[key], propType, /* origin */ 'runtime')));
|
|
847
|
+
}
|
|
848
|
+
throw new WgslTypeError(`Value ${safeStringify(item)} is not resolvable${schema && schema !== UnknownData ? ` to type ${safeStringify(schema)}` : ''}`);
|
|
849
|
+
}
|
|
850
|
+
resolveSnippet(snippet) {
|
|
851
|
+
return withValue(this.resolve(snippet.value, snippet.dataType).value, snippet);
|
|
852
|
+
}
|
|
853
|
+
pushMode(mode) {
|
|
854
|
+
this.#modeStack.push(mode);
|
|
855
|
+
}
|
|
856
|
+
popMode(expected) {
|
|
857
|
+
const mode = this.#modeStack.pop();
|
|
858
|
+
if (expected !== undefined) {
|
|
859
|
+
invariant(mode?.type === expected, 'Unexpected mode');
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
get mode() {
|
|
863
|
+
return this.#modeStack[this.#modeStack.length - 1] ?? topLevelState;
|
|
864
|
+
}
|
|
224
865
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
this.#namespaceInternal.takenGlobalIdentifiers.add(name);
|
|
285
|
-
}
|
|
286
|
-
get pre() {
|
|
287
|
-
return this._indentController.pre;
|
|
288
|
-
}
|
|
289
|
-
get topFunctionScope() {
|
|
290
|
-
return this._itemStateStack.topFunctionScope;
|
|
291
|
-
}
|
|
292
|
-
get topFunctionReturnType() {
|
|
293
|
-
const scope = this._itemStateStack.topFunctionScope;
|
|
294
|
-
invariant(scope, "Internal error, expected function scope to be present.");
|
|
295
|
-
return scope.returnType;
|
|
296
|
-
}
|
|
297
|
-
get shelllessRepo() {
|
|
298
|
-
return this.#namespaceInternal.shelllessRepo;
|
|
299
|
-
}
|
|
300
|
-
indent() {
|
|
301
|
-
return this._indentController.indent();
|
|
302
|
-
}
|
|
303
|
-
dedent() {
|
|
304
|
-
return this._indentController.dedent();
|
|
305
|
-
}
|
|
306
|
-
withResetIndentLevel(callback) {
|
|
307
|
-
return this._indentController.withResetLevel(callback);
|
|
308
|
-
}
|
|
309
|
-
getById(id) {
|
|
310
|
-
const item = this._itemStateStack.getSnippetById(id);
|
|
311
|
-
if (item === void 0) return null;
|
|
312
|
-
return item;
|
|
313
|
-
}
|
|
314
|
-
defineVariable(id, snippet) {
|
|
315
|
-
this._itemStateStack.defineBlockVariable(id, snippet);
|
|
316
|
-
}
|
|
317
|
-
reportReturnType(dataType) {
|
|
318
|
-
const scope = this._itemStateStack.topFunctionScope;
|
|
319
|
-
invariant(scope, "Internal error, expected function scope to be present.");
|
|
320
|
-
scope.reportedReturnTypes.add(dataType);
|
|
321
|
-
}
|
|
322
|
-
pushBlockScope() {
|
|
323
|
-
this._itemStateStack.pushBlockScope();
|
|
324
|
-
}
|
|
325
|
-
popBlockScope() {
|
|
326
|
-
this._itemStateStack.pop("blockScope");
|
|
327
|
-
}
|
|
328
|
-
setBlockExternals(externals) {
|
|
329
|
-
this._itemStateStack.setBlockExternals(externals);
|
|
330
|
-
}
|
|
331
|
-
clearBlockExternals() {
|
|
332
|
-
this._itemStateStack.clearBlockExternals();
|
|
333
|
-
}
|
|
334
|
-
generateLog(op, args) {
|
|
335
|
-
return this.#logGenerator.generateLog(this, op, args);
|
|
336
|
-
}
|
|
337
|
-
get logResources() {
|
|
338
|
-
return this.#logGenerator.logResources;
|
|
339
|
-
}
|
|
340
|
-
resolveFunction(options) {
|
|
341
|
-
try {
|
|
342
|
-
const scope = this._itemStateStack.pushFunctionScope(options.functionType, {}, options.returnType, options.externalMap);
|
|
343
|
-
this._itemStateStack.pushBlockScope();
|
|
344
|
-
const args = [];
|
|
345
|
-
if (options.entryInput) {
|
|
346
|
-
const { dataSchema, positionalArgs } = options.entryInput;
|
|
347
|
-
const firstParam = options.params[0];
|
|
348
|
-
const structArg = dataSchema ? createArgument(this.makeUniqueIdentifier("_arg_0", "block"), dataSchema) : void 0;
|
|
349
|
-
if (structArg) args.push(structArg);
|
|
350
|
-
if (firstParam?.type === FuncParameterType.destructuredObject) for (const { name, alias } of firstParam.props) {
|
|
351
|
-
const argInfo = positionalArgs.find((a) => a.schemaKey === name);
|
|
352
|
-
if (argInfo) {
|
|
353
|
-
const arg = createArgument(this.makeUniqueIdentifier(alias, "block"), argInfo.type);
|
|
354
|
-
args.push(arg);
|
|
355
|
-
scope.argAccess[alias] = arg.access;
|
|
356
|
-
} else if (structArg) scope.argAccess[alias] = createArgumentPropAccess(structArg.access, name);
|
|
357
|
-
}
|
|
358
|
-
else if (firstParam?.type === FuncParameterType.identifier) {
|
|
359
|
-
const proxyEntries = [];
|
|
360
|
-
for (const a of positionalArgs) {
|
|
361
|
-
const arg = createArgument(this.makeUniqueIdentifier(a.schemaKey, "block"), a.type);
|
|
362
|
-
args.push(arg);
|
|
363
|
-
proxyEntries.push({
|
|
364
|
-
schemaKey: a.schemaKey,
|
|
365
|
-
arg: arg.access
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
const router = new EntryInputRouter(structArg?.access, proxyEntries);
|
|
369
|
-
scope.argAccess[firstParam.name] = () => snip("N/A", router, "argument");
|
|
370
|
-
} else for (const a of positionalArgs) {
|
|
371
|
-
const argName = this.makeUniqueIdentifier(`_arg_${a.schemaKey}`, "block");
|
|
372
|
-
const arg = createArgument(argName, a.type);
|
|
373
|
-
args.push(arg);
|
|
374
|
-
scope.argAccess[argName] = arg.access;
|
|
375
|
-
}
|
|
376
|
-
} else for (const [i, argType] of options.argTypes.entries()) {
|
|
377
|
-
const astParam = options.params[i];
|
|
378
|
-
const origin = isPtr(argType) ? argType.addressSpace === "storage" ? argType.access === "read" ? "readonly" : "mutable" : argType.addressSpace : "argument";
|
|
379
|
-
switch (astParam?.type) {
|
|
380
|
-
case FuncParameterType.identifier: {
|
|
381
|
-
const arg = createArgument(this.makeUniqueIdentifier(astParam.name, "block"), argType, origin);
|
|
382
|
-
args.push(arg);
|
|
383
|
-
scope.argAccess[astParam.name] = arg.access;
|
|
384
|
-
break;
|
|
385
|
-
}
|
|
386
|
-
case FuncParameterType.destructuredObject: {
|
|
387
|
-
const objArg = createArgument(this.makeUniqueIdentifier(`_arg_${i}`, "block"), argType, origin);
|
|
388
|
-
args.push(objArg);
|
|
389
|
-
for (const { name, alias } of astParam.props) scope.argAccess[alias] = createArgumentPropAccess(objArg.access, name);
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
392
|
-
case void 0: if (!(argType instanceof AutoStruct)) args.push({
|
|
393
|
-
name: this.makeUniqueIdentifier(`_arg_${i}`, "block"),
|
|
394
|
-
access: () => {
|
|
395
|
-
throw new Error(`Unreachable: Accessing an argument that wasn't named in the function signature`);
|
|
396
|
-
},
|
|
397
|
-
decoratedType: argType,
|
|
398
|
-
used: false
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
let returnType;
|
|
403
|
-
const code = this.gen.functionDefinition({
|
|
404
|
-
functionType: options.functionType,
|
|
405
|
-
name: options.name,
|
|
406
|
-
workgroupSize: options.workgroupSize,
|
|
407
|
-
args,
|
|
408
|
-
body: options.body,
|
|
409
|
-
determineReturnType: () => {
|
|
410
|
-
if (returnType) return returnType;
|
|
411
|
-
returnType = options.returnType;
|
|
412
|
-
if (returnType instanceof AutoStruct) if (isWgslStruct(scope.reportedReturnTypes.values().next().value)) returnType = returnType.completeStruct;
|
|
413
|
-
else returnType = void 0;
|
|
414
|
-
if (!returnType) {
|
|
415
|
-
const returnTypes = [...scope.reportedReturnTypes];
|
|
416
|
-
if (returnTypes.length === 0) returnType = Void;
|
|
417
|
-
else {
|
|
418
|
-
const conversion = getBestConversion(returnTypes);
|
|
419
|
-
if (conversion && !conversion.hasImplicitConversions) returnType = conversion.targetType;
|
|
420
|
-
}
|
|
421
|
-
if (!returnType) throw new Error(`Expected function to have a single return type, got [${returnTypes.join(", ")}]. Cast explicitly to the desired type.`);
|
|
422
|
-
returnType = concretize(returnType);
|
|
423
|
-
if (options.functionType === "vertex" || options.functionType === "fragment") returnType = createIoSchema(returnType);
|
|
424
|
-
}
|
|
425
|
-
return returnType;
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
if (!returnType) throw new Error(`Failed to determine return type`);
|
|
429
|
-
return {
|
|
430
|
-
code,
|
|
431
|
-
returnType
|
|
432
|
-
};
|
|
433
|
-
} finally {
|
|
434
|
-
this._itemStateStack.pop("blockScope");
|
|
435
|
-
this._itemStateStack.pop("functionScope");
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
addDeclaration(declaration) {
|
|
439
|
-
this._declarations.push(declaration);
|
|
440
|
-
}
|
|
441
|
-
allocateLayoutEntry(layout) {
|
|
442
|
-
const memoMap = this.bindGroupLayoutsToPlaceholderMap;
|
|
443
|
-
let placeholderKey = memoMap.get(layout);
|
|
444
|
-
if (!placeholderKey) {
|
|
445
|
-
placeholderKey = `#BIND_GROUP_LAYOUT_${this._nextFreeLayoutPlaceholderIdx++}#`;
|
|
446
|
-
memoMap.set(layout, placeholderKey);
|
|
447
|
-
}
|
|
448
|
-
return placeholderKey;
|
|
449
|
-
}
|
|
450
|
-
allocateFixedEntry(layoutEntry, resource) {
|
|
451
|
-
const binding = this.fixedBindings.length;
|
|
452
|
-
this.fixedBindings.push({
|
|
453
|
-
layoutEntry,
|
|
454
|
-
resource
|
|
455
|
-
});
|
|
456
|
-
return {
|
|
457
|
-
group: CATCHALL_BIND_GROUP_IDX_MARKER,
|
|
458
|
-
binding
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
readSlot(slot) {
|
|
462
|
-
const value = this._itemStateStack.readSlot(slot);
|
|
463
|
-
if (value === void 0) throw new MissingSlotValueError(slot);
|
|
464
|
-
return value;
|
|
465
|
-
}
|
|
466
|
-
withSlots(pairs, callback) {
|
|
467
|
-
if (pairs.length === 0) return callback();
|
|
468
|
-
this._itemStateStack.pushSlotBindings(pairs);
|
|
469
|
-
try {
|
|
470
|
-
return callback();
|
|
471
|
-
} finally {
|
|
472
|
-
this._itemStateStack.pop("slotBinding");
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
withVaryingLocations(locations, callback) {
|
|
476
|
-
this._varyingLocations = locations;
|
|
477
|
-
try {
|
|
478
|
-
return callback();
|
|
479
|
-
} finally {
|
|
480
|
-
this._varyingLocations = void 0;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
withRenamed(item, name, callback) {
|
|
484
|
-
if (!name) return callback();
|
|
485
|
-
const oldName = getName(item);
|
|
486
|
-
try {
|
|
487
|
-
setName(item, name);
|
|
488
|
-
return callback();
|
|
489
|
-
} finally {
|
|
490
|
-
if (oldName) setName(item, oldName);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
unwrap(eventual) {
|
|
494
|
-
if (isProviding(eventual)) return this.withRenamed(eventual[$providing].inner, getName(eventual), () => this.withSlots(eventual[$providing].pairs, () => this.unwrap(eventual[$providing].inner)));
|
|
495
|
-
let maybeEventual = eventual;
|
|
496
|
-
while (true) if (isSlot(maybeEventual)) maybeEventual = this.readSlot(maybeEventual);
|
|
497
|
-
else if (isLazy(maybeEventual)) maybeEventual = this._getOrCompute(maybeEventual);
|
|
498
|
-
else break;
|
|
499
|
-
return maybeEventual;
|
|
500
|
-
}
|
|
501
|
-
_getOrCompute(lazy) {
|
|
502
|
-
const instances = this.#namespaceInternal.memoizedLazy.get(lazy) ?? [];
|
|
503
|
-
this._itemStateStack.pushItem();
|
|
504
|
-
try {
|
|
505
|
-
for (const instance of instances) if ([...instance.slotToValueMap.entries()].every(([slot, expectedValue]) => slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue))) return instance.result;
|
|
506
|
-
this.pushMode(new NormalState());
|
|
507
|
-
let result;
|
|
508
|
-
try {
|
|
509
|
-
result = lazy[$internal].compute();
|
|
510
|
-
} finally {
|
|
511
|
-
this.popMode("normal");
|
|
512
|
-
}
|
|
513
|
-
const slotToValueMap = /* @__PURE__ */ new Map();
|
|
514
|
-
for (const usedSlot of this._itemStateStack.topItem.usedSlots) slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));
|
|
515
|
-
instances.push({
|
|
516
|
-
slotToValueMap,
|
|
517
|
-
result
|
|
518
|
-
});
|
|
519
|
-
this.#namespaceInternal.memoizedLazy.set(lazy, instances);
|
|
520
|
-
return result;
|
|
521
|
-
} catch (err) {
|
|
522
|
-
if (err instanceof ResolutionError) throw err.appendToTrace(lazy);
|
|
523
|
-
throw new ResolutionError(err, [lazy]);
|
|
524
|
-
} finally {
|
|
525
|
-
this._itemStateStack.pop("item");
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.
|
|
530
|
-
*/
|
|
531
|
-
_getOrInstantiate(item) {
|
|
532
|
-
const instances = this.#namespaceInternal.memoizedResolves.get(item) ?? [];
|
|
533
|
-
this._itemStateStack.pushItem();
|
|
534
|
-
try {
|
|
535
|
-
for (const instance of instances) if ([...instance.slotToValueMap.entries()].every(([slot, expectedValue]) => slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue))) return instance.result;
|
|
536
|
-
let result;
|
|
537
|
-
if (isData(item)) result = snip(this.gen.typeAnnotation(item), Void, "runtime");
|
|
538
|
-
else if (isLazy(item) || isSlot(item)) result = this.resolve(this.unwrap(item));
|
|
539
|
-
else if (isSelfResolvable(item)) result = item[$resolve](this);
|
|
540
|
-
else if (hasTinyestMetadata(item)) {
|
|
541
|
-
const shellless = this.#namespaceInternal.shelllessRepo.get(item, void 0);
|
|
542
|
-
if (!shellless) throw new Error(`Couldn't resolve ${item.name}. Make sure it's a function that accepts no arguments, or call it from another TypeGPU function.`);
|
|
543
|
-
return this.withResetIndentLevel(() => this.resolve(shellless));
|
|
544
|
-
} else throw new TypeError(`Unresolvable internal value: ${safeStringify(item)}`);
|
|
545
|
-
const slotToValueMap = /* @__PURE__ */ new Map();
|
|
546
|
-
for (const usedSlot of this._itemStateStack.topItem.usedSlots) slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));
|
|
547
|
-
instances.push({
|
|
548
|
-
slotToValueMap,
|
|
549
|
-
result
|
|
550
|
-
});
|
|
551
|
-
this.#namespaceInternal.memoizedResolves.set(item, instances);
|
|
552
|
-
return result;
|
|
553
|
-
} catch (err) {
|
|
554
|
-
if (err instanceof ResolutionError) throw err.appendToTrace(item);
|
|
555
|
-
throw new ResolutionError(err, [item]);
|
|
556
|
-
} finally {
|
|
557
|
-
this._itemStateStack.pop("item");
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
resolve(item, schema) {
|
|
561
|
-
if (isTgpuFn(item) || hasTinyestMetadata(item)) {
|
|
562
|
-
if (this.#currentlyResolvedItems.has(item) && !this.#namespaceInternal.memoizedResolves.has(item)) throw new Error(`Recursive function ${item} detected. Recursion is not allowed on the GPU.`);
|
|
563
|
-
this.#currentlyResolvedItems.add(item);
|
|
564
|
-
}
|
|
565
|
-
if (isProviding(item)) return this.withRenamed(item[$providing].inner, getName(item), () => this.withSlots(item[$providing].pairs, () => this.resolve(item[$providing].inner, schema)));
|
|
566
|
-
if (isMarkedInternal(item) || hasTinyestMetadata(item)) {
|
|
567
|
-
if (this._itemStateStack.itemDepth === 0) {
|
|
568
|
-
this.gen.initGenerator(this);
|
|
569
|
-
try {
|
|
570
|
-
this.pushMode(new CodegenState());
|
|
571
|
-
const result = provideCtx(this, () => this._getOrInstantiate(item));
|
|
572
|
-
return snip(`${[...this._declarations].join("\n\n")}${result.value}`, Void, "runtime");
|
|
573
|
-
} finally {
|
|
574
|
-
this.popMode("codegen");
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
return this._getOrInstantiate(item);
|
|
578
|
-
}
|
|
579
|
-
if (typeof item === "number") {
|
|
580
|
-
const realSchema = schema ?? numericLiteralToSnippet(item).dataType;
|
|
581
|
-
invariant(realSchema !== UnknownData, "Schema has to be known for resolving numbers");
|
|
582
|
-
return this.gen.numericLiteral(item, realSchema);
|
|
583
|
-
}
|
|
584
|
-
if (typeof item === "boolean") return snip(item ? "true" : "false", bool, "constant");
|
|
585
|
-
if (typeof item === "string") return snip(item, Void, "runtime");
|
|
586
|
-
if (schema && isWgslArray(schema)) {
|
|
587
|
-
if (!Array.isArray(item)) throw new WgslTypeError(`Cannot coerce ${item} into value of type '${schema}'`);
|
|
588
|
-
if (schema.elementCount !== item.length) throw new WgslTypeError(`Cannot create value of type '${schema}' from an array of length: ${item.length}`);
|
|
589
|
-
return this.gen.typeInstantiation(schema, item.map((element) => snip(element, schema.elementType, "runtime")));
|
|
590
|
-
}
|
|
591
|
-
if (schema && isWgslStruct(schema)) return this.gen.typeInstantiation(schema, Object.entries(schema.propTypes).map(([key, propType]) => snip(item[key], propType, "runtime")));
|
|
592
|
-
throw new WgslTypeError(`Value ${safeStringify(item)} is not resolvable${schema ? ` to type ${safeStringify(schema)}` : ""}`);
|
|
593
|
-
}
|
|
594
|
-
resolveSnippet(snippet) {
|
|
595
|
-
return snip(this.resolve(snippet.value, snippet.dataType).value, snippet.dataType, snippet.origin);
|
|
596
|
-
}
|
|
597
|
-
pushMode(mode) {
|
|
598
|
-
this.#modeStack.push(mode);
|
|
599
|
-
}
|
|
600
|
-
popMode(expected) {
|
|
601
|
-
const mode = this.#modeStack.pop();
|
|
602
|
-
if (expected !== void 0) invariant(mode?.type === expected, "Unexpected mode");
|
|
603
|
-
}
|
|
604
|
-
get mode() {
|
|
605
|
-
return this.#modeStack[this.#modeStack.length - 1] ?? topLevelState;
|
|
606
|
-
}
|
|
607
|
-
};
|
|
608
|
-
function resolve(item, options) {
|
|
609
|
-
const ctx = new ResolutionCtxImpl(options);
|
|
610
|
-
let code = (options.config ? ctx.withSlots(options.config(new ConfigurableImpl([])).bindings, () => ctx.resolve(item)) : ctx.resolve(item)).value;
|
|
611
|
-
const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;
|
|
612
|
-
const usedBindGroupLayouts = [];
|
|
613
|
-
const automaticIds = naturalsExcept(new Set([...memoMap.keys()].map((layout) => layout.index).filter((v) => v !== void 0)));
|
|
614
|
-
const layoutEntries = ctx.fixedBindings.map((binding, idx) => [String(idx), binding.layoutEntry]);
|
|
615
|
-
const createCatchallGroup = () => {
|
|
616
|
-
const catchallIdx = automaticIds.next().value;
|
|
617
|
-
const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));
|
|
618
|
-
usedBindGroupLayouts[catchallIdx] = catchallLayout;
|
|
619
|
-
code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));
|
|
620
|
-
return [catchallIdx, new TgpuBindGroupImpl(catchallLayout, Object.fromEntries(ctx.fixedBindings.map((binding, idx) => [String(idx), binding.resource])))];
|
|
621
|
-
};
|
|
622
|
-
const catchall = layoutEntries.length > 0 ? createCatchallGroup() : void 0;
|
|
623
|
-
for (const [layout, placeholder] of memoMap.entries()) {
|
|
624
|
-
const idx = layout.index ?? automaticIds.next().value;
|
|
625
|
-
usedBindGroupLayouts[idx] = layout;
|
|
626
|
-
code = code.replaceAll(placeholder, String(idx));
|
|
627
|
-
}
|
|
628
|
-
if (options.enableExtensions && options.enableExtensions.length > 0) code = `${options.enableExtensions.map((ext) => `enable ${ext};`).join("\n")}\n\n${code}`;
|
|
629
|
-
return {
|
|
630
|
-
code,
|
|
631
|
-
usedBindGroupLayouts,
|
|
632
|
-
catchall,
|
|
633
|
-
logResources: ctx.logResources
|
|
634
|
-
};
|
|
866
|
+
export function resolve(item, options) {
|
|
867
|
+
const ctx = new ResolutionCtxImpl(options);
|
|
868
|
+
const snippet = options.config
|
|
869
|
+
? ctx.withSlots(options.config(new ConfigurableImpl([])).bindings, () => ctx.resolve(item))
|
|
870
|
+
: ctx.resolve(item);
|
|
871
|
+
let code = snippet.value;
|
|
872
|
+
const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;
|
|
873
|
+
const usedBindGroupLayouts = [];
|
|
874
|
+
const takenIndices = new Set([...memoMap.keys()].map((layout) => layout.index).filter((v) => v !== undefined));
|
|
875
|
+
const automaticIds = naturalsExcept(takenIndices);
|
|
876
|
+
const layoutEntries = ctx.fixedBindings.map((binding, idx) => [String(idx), binding.layoutEntry]);
|
|
877
|
+
// Bind group indices are only known now, so the same placeholder
|
|
878
|
+
// replacements applied to `code` are recorded and re-applied to each
|
|
879
|
+
// declaration's code.
|
|
880
|
+
const bindingReplacements = [];
|
|
881
|
+
const createCatchallGroup = () => {
|
|
882
|
+
const catchallIdx = automaticIds.next().value;
|
|
883
|
+
const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));
|
|
884
|
+
usedBindGroupLayouts[catchallIdx] = catchallLayout;
|
|
885
|
+
code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));
|
|
886
|
+
bindingReplacements.push([CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx)]);
|
|
887
|
+
return [
|
|
888
|
+
catchallIdx,
|
|
889
|
+
new TgpuBindGroupImpl(
|
|
890
|
+
// Undefined only in rootless `tgpu.resolve()`, where the group is never unwrapped
|
|
891
|
+
options.root, catchallLayout, Object.fromEntries(
|
|
892
|
+
// oxlint-disable-next-line typescript/no-explicit-any -- it's fine
|
|
893
|
+
ctx.fixedBindings.map((binding, idx) => [String(idx), binding.resource]))),
|
|
894
|
+
];
|
|
895
|
+
};
|
|
896
|
+
// Retrieving the catch-all binding index first, because it's inherently
|
|
897
|
+
// the least swapped bind group (fixed and cannot be swapped).
|
|
898
|
+
const catchall = layoutEntries.length > 0 ? createCatchallGroup() : undefined;
|
|
899
|
+
for (const [layout, placeholder] of memoMap.entries()) {
|
|
900
|
+
const idx = layout.index ?? automaticIds.next().value;
|
|
901
|
+
usedBindGroupLayouts[idx] = layout;
|
|
902
|
+
code = code.replaceAll(placeholder, String(idx));
|
|
903
|
+
bindingReplacements.push([placeholder, String(idx)]);
|
|
904
|
+
}
|
|
905
|
+
if (options.enableExtensions && options.enableExtensions.length > 0) {
|
|
906
|
+
const extensions = options.enableExtensions.map((ext) => `enable ${ext};`);
|
|
907
|
+
code = `${extensions.join('\n')}\n\n${code}`;
|
|
908
|
+
}
|
|
909
|
+
let declarations = ctx.declarations.map(({ name, code: declarationCode }) => ({
|
|
910
|
+
name,
|
|
911
|
+
code: bindingReplacements.reduce((acc, [placeholder, idx]) => acc.replaceAll(placeholder, idx), declarationCode),
|
|
912
|
+
}));
|
|
913
|
+
if (options.minify) {
|
|
914
|
+
code = minify(code);
|
|
915
|
+
// TODO(#2804): remove this workaround
|
|
916
|
+
declarations = declarations.map((entry) => ({ ...entry, code: minify(entry.code) }));
|
|
917
|
+
}
|
|
918
|
+
return {
|
|
919
|
+
code,
|
|
920
|
+
declarations,
|
|
921
|
+
usedBindGroupLayouts,
|
|
922
|
+
catchall,
|
|
923
|
+
logResources: ctx.logResources,
|
|
924
|
+
};
|
|
635
925
|
}
|
|
636
|
-
|
|
637
|
-
//#endregion
|
|
638
|
-
export { ResolutionCtxImpl, resolve };
|