wgsl-play 0.0.32 → 0.0.33
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 +3 -6
- package/dist/{WgslPlay-CrELe3mK.js → WgslPlay-Ben4VdsC.js} +2 -15
- package/dist/WgslPlay.js +27 -7
- package/dist/index.js +1 -1
- package/dist/wgsl-play.js +293 -305
- package/package.json +4 -4
- package/src/Renderer.ts +2 -25
- package/src/WgslPlay.ts +42 -7
- package/src/test/WgslPlay.e2e.ts +55 -0
- package/src/test/WgslPlay.e2e.ts-snapshots/connect-conditions-green-chromium-darwin.png +0 -0
- package/src/test/WgslPlay.e2e.ts-snapshots/connect-conditions-red-chromium-darwin.png +0 -0
- package/src/test/WgslPlay.e2e.ts-snapshots/connect-source-external-chromium-darwin.png +0 -0
- package/src/test/WgslPlay.e2e.ts-snapshots/connect-source-multifile-chromium-darwin.png +0 -0
package/README.md
CHANGED
|
@@ -18,12 +18,10 @@ That's it. The component auto-fetches dependencies and starts animating.
|
|
|
18
18
|
accepts **fragment shaders**. Write a single `@fragment` function.
|
|
19
19
|
WESL extensions are supported (imports, conditional compilation).
|
|
20
20
|
|
|
21
|
-
Standard uniforms are
|
|
21
|
+
Standard uniforms are available via `env::u`:
|
|
22
22
|
|
|
23
23
|
```wgsl
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
24
|
+
import env::u;
|
|
27
25
|
|
|
28
26
|
@fragment fn main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
|
|
29
27
|
let uv = pos.xy / u.resolution;
|
|
@@ -44,8 +42,7 @@ You can include shader code inline if you'd prefer. Use a `<script type="text/wg
|
|
|
44
42
|
```html
|
|
45
43
|
<wgsl-play>
|
|
46
44
|
<script type="text/wesl">
|
|
47
|
-
import
|
|
48
|
-
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
45
|
+
import env::u;
|
|
49
46
|
|
|
50
47
|
@fragment fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
|
|
51
48
|
let uv = pos.xy / u.resolution;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requestWeslDevice } from "wesl";
|
|
2
2
|
import { linkAndCreatePipeline, renderFrame, updateRenderUniforms } from "wesl-gpu";
|
|
3
3
|
|
|
4
4
|
//#region src/Config.ts
|
|
@@ -153,26 +153,13 @@ async function initWebGPU(canvas, alphaMode = "opaque") {
|
|
|
153
153
|
}
|
|
154
154
|
/** Compile WESL fragment shader and create render pipeline. */
|
|
155
155
|
async function createPipeline(state, fragmentSource, options) {
|
|
156
|
-
const { weslSrc, libs = [], conditions, constants } = options ?? {};
|
|
157
|
-
const { packageName, rootModuleName } = options ?? {};
|
|
158
|
-
let resolver;
|
|
159
|
-
if (weslSrc || libs.length > 0) {
|
|
160
|
-
const resolvers = [];
|
|
161
|
-
if (weslSrc) resolvers.push(new RecordResolver(weslSrc, { packageName }));
|
|
162
|
-
for (const bundle of libs) resolvers.push(new BundleResolver(bundle));
|
|
163
|
-
resolver = resolvers.length === 1 ? resolvers[0] : new CompositeResolver(resolvers);
|
|
164
|
-
}
|
|
165
156
|
state.device.pushErrorScope("validation");
|
|
166
157
|
const pipeline = await linkAndCreatePipeline({
|
|
167
158
|
device: state.device,
|
|
168
159
|
fragmentSource,
|
|
169
|
-
resolver,
|
|
170
160
|
format: state.presentationFormat,
|
|
171
161
|
layout: state.pipelineLayout,
|
|
172
|
-
|
|
173
|
-
constants,
|
|
174
|
-
packageName,
|
|
175
|
-
rootModuleName
|
|
162
|
+
...options
|
|
176
163
|
});
|
|
177
164
|
const gpuError = await state.device.popErrorScope();
|
|
178
165
|
if (gpuError) {
|
package/dist/WgslPlay.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as PlaybackControls, c as getConfig, i as startRenderLoop, l as resetConfig, n as createPipeline, o as ErrorOverlay, r as initWebGPU, s as defaults, t as WgslPlay_default } from "./WgslPlay-
|
|
1
|
+
import { a as PlaybackControls, c as getConfig, i as startRenderLoop, l as resetConfig, n as createPipeline, o as ErrorOverlay, r as initWebGPU, s as defaults, t as WgslPlay_default } from "./WgslPlay-Ben4VdsC.js";
|
|
2
2
|
import { fetchDependencies, loadShaderFromUrl } from "wesl-fetch";
|
|
3
3
|
import { WeslParseError, fileToModulePath } from "wesl";
|
|
4
4
|
|
|
@@ -155,7 +155,7 @@ var WgslPlay = class extends HTMLElement {
|
|
|
155
155
|
this._weslSrc = Object.fromEntries(entries);
|
|
156
156
|
this._rootModuleName = rootModuleName ? toModulePath(rootModuleName) : "package::main";
|
|
157
157
|
this._fromFullProject = true;
|
|
158
|
-
this.
|
|
158
|
+
this.rebuildPipeline();
|
|
159
159
|
}
|
|
160
160
|
/** Whether to auto-fetch missing library packages from npm (default: true). */
|
|
161
161
|
get fetchLibs() {
|
|
@@ -286,15 +286,26 @@ var WgslPlay = class extends HTMLElement {
|
|
|
286
286
|
return { [this._rootModuleName]: source };
|
|
287
287
|
};
|
|
288
288
|
const conditions = el.conditions;
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
const rootModuleName = el.rootModuleName;
|
|
290
|
+
const libs = el.libs;
|
|
291
|
+
const weslSrc = getSources();
|
|
292
|
+
const entries = Object.entries(weslSrc).map(([k, v]) => [toModulePath(k), v]);
|
|
293
|
+
this._weslSrc = Object.fromEntries(entries);
|
|
294
|
+
this._rootModuleName = rootModuleName ? toModulePath(rootModuleName) : "package::main";
|
|
295
|
+
if (conditions) this._linkOptions = {
|
|
296
|
+
...this._linkOptions,
|
|
291
297
|
conditions
|
|
292
298
|
};
|
|
299
|
+
if (libs) this._libs = libs;
|
|
300
|
+
this._fromFullProject = false;
|
|
301
|
+
await this.discoverAndRebuild();
|
|
302
|
+
this._fromFullProject = true;
|
|
293
303
|
this._sourceListener = (e) => {
|
|
294
304
|
const detail = e.detail;
|
|
295
305
|
const fallback = { [this._rootModuleName]: detail?.source ?? "" };
|
|
296
306
|
this.project = {
|
|
297
307
|
weslSrc: detail?.sources ?? fallback,
|
|
308
|
+
rootModuleName: detail?.rootModuleName,
|
|
298
309
|
conditions: detail?.conditions
|
|
299
310
|
};
|
|
300
311
|
};
|
|
@@ -311,7 +322,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
311
322
|
this._fromFullProject = false;
|
|
312
323
|
if (rootModuleName) this._rootModuleName = rootModuleName;
|
|
313
324
|
const mainSource = weslSrc[this._rootModuleName];
|
|
314
|
-
if (!mainSource)
|
|
325
|
+
if (!mainSource) {
|
|
326
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(weslSrc));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
315
329
|
await createPipeline(this.renderState, mainSource, {
|
|
316
330
|
...this._linkOptions,
|
|
317
331
|
weslSrc,
|
|
@@ -327,7 +341,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
327
341
|
async rebuildPipeline() {
|
|
328
342
|
if (!await this.initialize()) return;
|
|
329
343
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
330
|
-
if (!mainSource)
|
|
344
|
+
if (!mainSource) {
|
|
345
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
331
348
|
try {
|
|
332
349
|
this.errorOverlay.hide();
|
|
333
350
|
await createPipeline(this.renderState, mainSource, {
|
|
@@ -345,7 +362,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
345
362
|
async discoverAndRebuild() {
|
|
346
363
|
if (!await this.initialize()) return;
|
|
347
364
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
348
|
-
if (!mainSource)
|
|
365
|
+
if (!mainSource) {
|
|
366
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
349
369
|
try {
|
|
350
370
|
this.errorOverlay.hide();
|
|
351
371
|
const { weslSrc, libs } = await fetchDependencies(mainSource, {
|
package/dist/index.js
CHANGED
package/dist/wgsl-play.js
CHANGED
|
@@ -970,11 +970,78 @@ function emptyScope(parent, kind = "scope") {
|
|
|
970
970
|
}
|
|
971
971
|
|
|
972
972
|
//#endregion
|
|
973
|
-
//#region ../wesl/src/
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
973
|
+
//#region ../wesl/src/StandardTypes.ts
|
|
974
|
+
const stdFns = `bitcast all any select arrayLength
|
|
975
|
+
abs acos acosh asin asinh atan atanh atan2 ceil clamp cos cosh
|
|
976
|
+
countLeadingZeros countOneBits countTrailingZeros cross
|
|
977
|
+
degrees determinant distance dot dot4U8Packed dot4I8Packed
|
|
978
|
+
exp exp2 extractBits faceForward firstLeadingBit firstTrailingBit
|
|
979
|
+
floor fma fract frexp insertBits inverseSqrt ldexp length log log2
|
|
980
|
+
max min mix modf normalize pow quantizeToF16 radians reflect refract
|
|
981
|
+
reverseBits round saturate sign sin sinh smoothstep sqrt step tan tanh
|
|
982
|
+
transpose trunc
|
|
983
|
+
dpdx dpdxCoarse dpdxFine dpdy dpdyCoarse dpdyFine fwidth
|
|
984
|
+
fwidthCoarse fwidthFine
|
|
985
|
+
textureDimensions textureGather textureGatherCompare textureLoad
|
|
986
|
+
textureNumLayers textureNumLevels textureNumSamples
|
|
987
|
+
textureSample textureSampleBias textureSampleCompare textureSampleCompareLevel
|
|
988
|
+
textureSampleGrad textureSampleLevel textureSampleBaseClampToEdge
|
|
989
|
+
textureStore
|
|
990
|
+
atomicLoad atomicStore atomicAdd atomicSub atomicMax atomicMin
|
|
991
|
+
atomicAnd atomicOr atomicXor atomicExchange atomicCompareExchangeWeak
|
|
992
|
+
pack4x8snorm pack4x8unorm pack4xI8 pack4xU8 pack4xI8Clamp pack4xU8Clamp
|
|
993
|
+
pack2x16snorm pack2x16unorm pack2x16float
|
|
994
|
+
unpack4x8snorm unpack4x8unorm unpack4xI8 unpack4xU8
|
|
995
|
+
unpack2x16snorm unpack2x16unorm unpack2x16float
|
|
996
|
+
storageBarrier textureBarrier workgroupBarrier workgroupUniformLoad
|
|
997
|
+
subgroupAdd subgroupAll subgroupAnd subgroupAny subgroupBallot
|
|
998
|
+
subgroupBroadcast subgroupBroadcastFirst subgroupElect
|
|
999
|
+
subgroupExclusiveAdd subgroupExclusiveMul subgroupInclusiveAdd
|
|
1000
|
+
subgroupInclusiveMul subgroupMax subgroupMin subgroupMul subgroupOr
|
|
1001
|
+
subgroupShuffle subgroupShuffleUp subgroupShuffleXor subgroupXor
|
|
1002
|
+
quadBroadcast quadSwapDiagonal quadSwapX quadSwapY`.split(/\s+/);
|
|
1003
|
+
const sampledTextureTypes = `
|
|
1004
|
+
texture_1d texture_2d texture_2d_array texture_3d
|
|
1005
|
+
texture_cube texture_cube_array
|
|
1006
|
+
`;
|
|
1007
|
+
const multisampledTextureTypes = `
|
|
1008
|
+
texture_multisampled_2d texture_depth_multisampled_2d
|
|
1009
|
+
`;
|
|
1010
|
+
const textureStorageTypes = `
|
|
1011
|
+
texture_storage_1d texture_storage_2d texture_storage_2d_array
|
|
1012
|
+
texture_storage_3d
|
|
1013
|
+
`;
|
|
1014
|
+
const stdTypes = `array atomic bool f16 f32 i32
|
|
1015
|
+
mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4
|
|
1016
|
+
mat2x2f mat2x3f mat2x4f mat3x2f mat3x3f mat3x4f
|
|
1017
|
+
mat4x2f mat4x3f mat4x4f
|
|
1018
|
+
mat2x2h mat2x3h mat2x4h mat3x2h mat3x3h mat3x4h
|
|
1019
|
+
mat4x2h mat4x3h mat4x4h
|
|
1020
|
+
u32 vec2 vec3 vec4 ptr
|
|
1021
|
+
vec2i vec3i vec4i vec2u vec3u vec4u
|
|
1022
|
+
vec2f vec3f vec4f vec2h vec3h vec4h
|
|
1023
|
+
${sampledTextureTypes}
|
|
1024
|
+
${multisampledTextureTypes}
|
|
1025
|
+
texture_external
|
|
1026
|
+
${textureStorageTypes}
|
|
1027
|
+
texture_depth_2d texture_depth_2d_array texture_depth_cube
|
|
1028
|
+
texture_depth_cube_array
|
|
1029
|
+
sampler sampler_comparison
|
|
1030
|
+
rgba8unorm rgba8snorm rgba8uint rgba8sint
|
|
1031
|
+
rgba16uint rgba16sint rgba16float
|
|
1032
|
+
r32uint r32sint r32float rg32uint rg32sint rg32float
|
|
1033
|
+
rgba32uint rgba32sint rgba32float
|
|
1034
|
+
bgra8unorm`.split(/\s+/);
|
|
1035
|
+
/** https://www.w3.org/TR/WGSL/#predeclared-enumerants */
|
|
1036
|
+
const stdEnumerants = `read write read_write
|
|
1037
|
+
function private workgroup uniform storage
|
|
1038
|
+
rgba8unorm rgba8snorm rgba8uint rgba8sint
|
|
1039
|
+
rgba16uint rgba16sint rgba16float
|
|
1040
|
+
r32uint r32sint r32float rg32uint rg32sint rg32float
|
|
1041
|
+
rgba32uint rgba32sint rgba32float bgra8unorm`.split(/\s+/);
|
|
1042
|
+
/** WGSL standard attributes whose params need binding (e.g., @workgroup_size).
|
|
1043
|
+
* See: https://www.w3.org/TR/WGSL/#attributes */
|
|
1044
|
+
const wgslStandardAttributes = new Set([
|
|
978
1045
|
"align",
|
|
979
1046
|
"binding",
|
|
980
1047
|
"blend_src",
|
|
@@ -990,6 +1057,21 @@ const wgslStandardAttributes$1 = new Set([
|
|
|
990
1057
|
"vertex",
|
|
991
1058
|
"workgroup_size"
|
|
992
1059
|
]);
|
|
1060
|
+
/** return true if the name is for a built in type (not a user struct) */
|
|
1061
|
+
function stdType(name) {
|
|
1062
|
+
return stdTypes.includes(name);
|
|
1063
|
+
}
|
|
1064
|
+
/** return true if the name is for a built in fn (not a user function) */
|
|
1065
|
+
function stdFn(name) {
|
|
1066
|
+
return stdFns.includes(name) || stdType(name);
|
|
1067
|
+
}
|
|
1068
|
+
/** return true if the name is for a built in enumerant */
|
|
1069
|
+
function stdEnumerant(name) {
|
|
1070
|
+
return stdEnumerants.includes(name);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
//#endregion
|
|
1074
|
+
//#region ../wesl/src/LowerAndEmit.ts
|
|
993
1075
|
/** Traverse the AST, starting from root elements, emitting WGSL for each. */
|
|
994
1076
|
function lowerAndEmit(params) {
|
|
995
1077
|
const { srcBuilder, rootElems, conditions } = params;
|
|
@@ -1269,7 +1351,7 @@ function emitAttribute(e, ctx) {
|
|
|
1269
1351
|
const { kind } = e.attribute;
|
|
1270
1352
|
if (kind === "@if" || kind === "@elif" || kind === "@else") return false;
|
|
1271
1353
|
if (kind === "@attribute") {
|
|
1272
|
-
if (!wgslStandardAttributes
|
|
1354
|
+
if (!wgslStandardAttributes.has(e.attribute.name)) return false;
|
|
1273
1355
|
emitStandardAttribute(e, ctx);
|
|
1274
1356
|
return true;
|
|
1275
1357
|
}
|
|
@@ -3750,115 +3832,16 @@ function flatImports(ast, conditions) {
|
|
|
3750
3832
|
return flat;
|
|
3751
3833
|
}
|
|
3752
3834
|
|
|
3753
|
-
//#endregion
|
|
3754
|
-
//#region ../wesl/src/StandardTypes.ts
|
|
3755
|
-
const stdFns = `bitcast all any select arrayLength
|
|
3756
|
-
abs acos acosh asin asinh atan atanh atan2 ceil clamp cos cosh
|
|
3757
|
-
countLeadingZeros countOneBits countTrailingZeros cross
|
|
3758
|
-
degrees determinant distance dot dot4U8Packed dot4I8Packed
|
|
3759
|
-
exp exp2 extractBits faceForward firstLeadingBit firstTrailingBit
|
|
3760
|
-
floor fma fract frexp insertBits inverseSqrt ldexp length log log2
|
|
3761
|
-
max min mix modf normalize pow quantizeToF16 radians reflect refract
|
|
3762
|
-
reverseBits round saturate sign sin sinh smoothstep sqrt step tan tanh
|
|
3763
|
-
transpose trunc
|
|
3764
|
-
dpdx dpdxCoarse dpdxFine dpdy dpdyCoarse dpdyFine fwidth
|
|
3765
|
-
fwidthCoarse fwidthFine
|
|
3766
|
-
textureDimensions textureGather textureGatherCompare textureLoad
|
|
3767
|
-
textureNumLayers textureNumLevels textureNumSamples
|
|
3768
|
-
textureSample textureSampleBias textureSampleCompare textureSampleCompareLevel
|
|
3769
|
-
textureSampleGrad textureSampleLevel textureSampleBaseClampToEdge
|
|
3770
|
-
textureStore
|
|
3771
|
-
atomicLoad atomicStore atomicAdd atomicSub atomicMax atomicMin
|
|
3772
|
-
atomicAnd atomicOr atomicXor atomicExchange atomicCompareExchangeWeak
|
|
3773
|
-
pack4x8snorm pack4x8unorm pack4xI8 pack4xU8 pack4xI8Clamp pack4xU8Clamp
|
|
3774
|
-
pack2x16snorm pack2x16unorm pack2x16float
|
|
3775
|
-
unpack4x8snorm unpack4x8unorm unpack4xI8 unpack4xU8
|
|
3776
|
-
unpack2x16snorm unpack2x16unorm unpack2x16float
|
|
3777
|
-
storageBarrier textureBarrier workgroupBarrier workgroupUniformLoad
|
|
3778
|
-
subgroupAdd subgroupAll subgroupAnd subgroupAny subgroupBallot
|
|
3779
|
-
subgroupBroadcast subgroupBroadcastFirst subgroupElect
|
|
3780
|
-
subgroupExclusiveAdd subgroupExclusiveMul subgroupInclusiveAdd
|
|
3781
|
-
subgroupInclusiveMul subgroupMax subgroupMin subgroupMul subgroupOr
|
|
3782
|
-
subgroupShuffle subgroupShuffleUp subgroupShuffleXor subgroupXor
|
|
3783
|
-
quadBroadcast quadSwapDiagonal quadSwapX quadSwapY`.split(/\s+/);
|
|
3784
|
-
const sampledTextureTypes = `
|
|
3785
|
-
texture_1d texture_2d texture_2d_array texture_3d
|
|
3786
|
-
texture_cube texture_cube_array
|
|
3787
|
-
`;
|
|
3788
|
-
const multisampledTextureTypes = `
|
|
3789
|
-
texture_multisampled_2d texture_depth_multisampled_2d
|
|
3790
|
-
`;
|
|
3791
|
-
const textureStorageTypes = `
|
|
3792
|
-
texture_storage_1d texture_storage_2d texture_storage_2d_array
|
|
3793
|
-
texture_storage_3d
|
|
3794
|
-
`;
|
|
3795
|
-
const stdTypes = `array atomic bool f16 f32 i32
|
|
3796
|
-
mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4
|
|
3797
|
-
mat2x2f mat2x3f mat2x4f mat3x2f mat3x3f mat3x4f
|
|
3798
|
-
mat4x2f mat4x3f mat4x4f
|
|
3799
|
-
mat2x2h mat2x3h mat2x4h mat3x2h mat3x3h mat3x4h
|
|
3800
|
-
mat4x2h mat4x3h mat4x4h
|
|
3801
|
-
u32 vec2 vec3 vec4 ptr
|
|
3802
|
-
vec2i vec3i vec4i vec2u vec3u vec4u
|
|
3803
|
-
vec2f vec3f vec4f vec2h vec3h vec4h
|
|
3804
|
-
${sampledTextureTypes}
|
|
3805
|
-
${multisampledTextureTypes}
|
|
3806
|
-
texture_external
|
|
3807
|
-
${textureStorageTypes}
|
|
3808
|
-
texture_depth_2d texture_depth_2d_array texture_depth_cube
|
|
3809
|
-
texture_depth_cube_array
|
|
3810
|
-
sampler sampler_comparison
|
|
3811
|
-
rgba8unorm rgba8snorm rgba8uint rgba8sint
|
|
3812
|
-
rgba16uint rgba16sint rgba16float
|
|
3813
|
-
r32uint r32sint r32float rg32uint rg32sint rg32float
|
|
3814
|
-
rgba32uint rgba32sint rgba32float
|
|
3815
|
-
bgra8unorm`.split(/\s+/);
|
|
3816
|
-
/** https://www.w3.org/TR/WGSL/#predeclared-enumerants */
|
|
3817
|
-
const stdEnumerants = `read write read_write
|
|
3818
|
-
function private workgroup uniform storage
|
|
3819
|
-
rgba8unorm rgba8snorm rgba8uint rgba8sint
|
|
3820
|
-
rgba16uint rgba16sint rgba16float
|
|
3821
|
-
r32uint r32sint r32float rg32uint rg32sint rg32float
|
|
3822
|
-
rgba32uint rgba32sint rgba32float bgra8unorm`.split(/\s+/);
|
|
3823
|
-
/** return true if the name is for a built in type (not a user struct) */
|
|
3824
|
-
function stdType(name) {
|
|
3825
|
-
return stdTypes.includes(name);
|
|
3826
|
-
}
|
|
3827
|
-
/** return true if the name is for a built in fn (not a user function) */
|
|
3828
|
-
function stdFn(name) {
|
|
3829
|
-
return stdFns.includes(name) || stdType(name);
|
|
3830
|
-
}
|
|
3831
|
-
/** return true if the name is for a built in enumerant */
|
|
3832
|
-
function stdEnumerant(name) {
|
|
3833
|
-
return stdEnumerants.includes(name);
|
|
3834
|
-
}
|
|
3835
|
-
|
|
3836
3835
|
//#endregion
|
|
3837
3836
|
//#region ../wesl/src/BindIdents.ts
|
|
3838
|
-
/** WGSL standard attributes whose params need binding (e.g., @workgroup_size). */
|
|
3839
|
-
const wgslStandardAttributes = new Set([
|
|
3840
|
-
"align",
|
|
3841
|
-
"binding",
|
|
3842
|
-
"blend_src",
|
|
3843
|
-
"compute",
|
|
3844
|
-
"const",
|
|
3845
|
-
"fragment",
|
|
3846
|
-
"group",
|
|
3847
|
-
"id",
|
|
3848
|
-
"invariant",
|
|
3849
|
-
"location",
|
|
3850
|
-
"must_use",
|
|
3851
|
-
"size",
|
|
3852
|
-
"vertex",
|
|
3853
|
-
"workgroup_size"
|
|
3854
|
-
]);
|
|
3855
3837
|
/** Bind ref idents to declarations and mangle global declaration names. */
|
|
3856
3838
|
function bindIdents(params) {
|
|
3857
3839
|
const { rootAst, resolver, virtuals, accumulateUnbound } = params;
|
|
3858
3840
|
const { conditions = {}, mangler = minimalMangle } = params;
|
|
3841
|
+
const { discoveryMode } = params;
|
|
3859
3842
|
const packageName = rootAst.srcModule.modulePath.split("::")[0];
|
|
3860
|
-
const
|
|
3861
|
-
const { globalNames, knownDecls } = initRootDecls(
|
|
3843
|
+
const rootDecls = discoveryMode ? findAllRootDecls(rootAst.rootScope) : findValidRootDecls(rootAst.rootScope, conditions);
|
|
3844
|
+
const { globalNames, knownDecls } = initRootDecls(rootDecls);
|
|
3862
3845
|
const bindContext = {
|
|
3863
3846
|
resolver,
|
|
3864
3847
|
conditions,
|
|
@@ -3869,13 +3852,14 @@ function bindIdents(params) {
|
|
|
3869
3852
|
foundScopes: /* @__PURE__ */ new Set(),
|
|
3870
3853
|
globalNames,
|
|
3871
3854
|
globalStatements: /* @__PURE__ */ new Map(),
|
|
3872
|
-
unbound: accumulateUnbound ? [] : void 0
|
|
3855
|
+
unbound: accumulateUnbound ? [] : void 0,
|
|
3856
|
+
discoveryMode
|
|
3873
3857
|
};
|
|
3874
3858
|
const liveDecls = {
|
|
3875
|
-
decls: new Map(
|
|
3859
|
+
decls: new Map(rootDecls.map((d) => [d.originalName, d])),
|
|
3876
3860
|
parent: null
|
|
3877
3861
|
};
|
|
3878
|
-
const fromRootDecls =
|
|
3862
|
+
const fromRootDecls = rootDecls.flatMap((decl) => processDependentScope(decl, bindContext));
|
|
3879
3863
|
const fromRefs = bindIdentsRecursive(rootAst.rootScope, bindContext, liveDecls);
|
|
3880
3864
|
const newStatements = [...bindContext.globalStatements.values()];
|
|
3881
3865
|
return {
|
|
@@ -3911,17 +3895,11 @@ function* validItems(scope, conditions) {
|
|
|
3911
3895
|
}
|
|
3912
3896
|
/** Find all conditionally valid declarations at the root level. */
|
|
3913
3897
|
function findValidRootDecls(rootScope, conditions) {
|
|
3914
|
-
|
|
3915
|
-
for (const item of validItems(rootScope, conditions)) if (item.kind === "decl") found.push(item);
|
|
3916
|
-
else if (item.kind === "partial") collectDecls(item, found);
|
|
3917
|
-
return found;
|
|
3898
|
+
return collectDecls(validItems(rootScope, conditions));
|
|
3918
3899
|
}
|
|
3919
3900
|
/** Find all declarations at the root level, ignoring conditions. */
|
|
3920
3901
|
function findAllRootDecls(rootScope) {
|
|
3921
|
-
|
|
3922
|
-
for (const item of rootScope.contents) if (item.kind === "decl") found.push(item);
|
|
3923
|
-
else if (item.kind === "partial") collectDecls(item, found);
|
|
3924
|
-
return found;
|
|
3902
|
+
return collectDecls(rootScope.contents);
|
|
3925
3903
|
}
|
|
3926
3904
|
/** Find a public declaration with the given original name. */
|
|
3927
3905
|
function publicDecl(scope, name, conditions) {
|
|
@@ -3989,10 +3967,11 @@ function handleRef(ident, liveDecls, bindContext) {
|
|
|
3989
3967
|
}
|
|
3990
3968
|
if (!bindContext.unbound) failIdent(ident, `unresolved identifier '${ident.originalName}'`);
|
|
3991
3969
|
}
|
|
3970
|
+
/** Follow new global declarations into their dependent scopes. */
|
|
3992
3971
|
function handleDecls(newGlobals, bindContext) {
|
|
3993
3972
|
return newGlobals.flatMap((decl) => processDependentScope(decl, bindContext));
|
|
3994
3973
|
}
|
|
3995
|
-
/** If found declaration is new, mangle its name.
|
|
3974
|
+
/** If found declaration is new, mangle its name. @return the decl if it's global. */
|
|
3996
3975
|
function handleNewDecl(refIdent, foundDecl, ctx) {
|
|
3997
3976
|
const { decl, moduleAst } = foundDecl;
|
|
3998
3977
|
const { knownDecls, globalNames, mangler, globalStatements } = ctx;
|
|
@@ -4019,7 +3998,8 @@ function findDeclInModule(ident, liveDecls) {
|
|
|
4019
3998
|
/** Match a ref ident to a declaration in another module via import or qualified ident. */
|
|
4020
3999
|
function findQualifiedImport(refIdent, ctx) {
|
|
4021
4000
|
const { conditions, unbound, discoveryMode } = ctx;
|
|
4022
|
-
const
|
|
4001
|
+
const conds = discoveryMode ? void 0 : conditions;
|
|
4002
|
+
const flatImps = flatImports(refIdent.ast, conds);
|
|
4023
4003
|
const identParts = refIdent.originalName.split("::");
|
|
4024
4004
|
const pathParts = matchingImport(identParts, flatImps) ?? qualifiedIdent(identParts);
|
|
4025
4005
|
if (!pathParts) {
|
|
@@ -4073,7 +4053,7 @@ function virtualModule(moduleName, ctx) {
|
|
|
4073
4053
|
/** Get cached valid root declarations, computing on first access. */
|
|
4074
4054
|
function getValidRootDecls(rootScope, conditions) {
|
|
4075
4055
|
const lexScope = rootScope;
|
|
4076
|
-
|
|
4056
|
+
lexScope._validRootDecls ??= findValidRootDecls(rootScope, conditions);
|
|
4077
4057
|
return lexScope._validRootDecls;
|
|
4078
4058
|
}
|
|
4079
4059
|
/** Given a global declIdent, return the liveDecls for its root scope. */
|
|
@@ -4102,13 +4082,167 @@ function setMangledName(proposedName, decl, globalNames, srcModule, mangler) {
|
|
|
4102
4082
|
function stdWgsl(name) {
|
|
4103
4083
|
return stdType(name) || stdFn(name) || stdEnumerant(name);
|
|
4104
4084
|
}
|
|
4085
|
+
/** @return identParts if it's a qualified path (has ::). */
|
|
4105
4086
|
function qualifiedIdent(identParts) {
|
|
4106
4087
|
if (identParts.length > 1) return identParts;
|
|
4107
4088
|
}
|
|
4108
|
-
/** Collect all declarations
|
|
4109
|
-
function collectDecls(
|
|
4110
|
-
|
|
4111
|
-
|
|
4089
|
+
/** Collect all declarations from scope items, recursing into partial scopes. */
|
|
4090
|
+
function collectDecls(items) {
|
|
4091
|
+
return [...items].flatMap((item) => {
|
|
4092
|
+
const { kind } = item;
|
|
4093
|
+
if (kind === "decl") return [item];
|
|
4094
|
+
if (kind === "partial") return collectDecls(item.contents);
|
|
4095
|
+
return [];
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
//#endregion
|
|
4100
|
+
//#region ../wesl/src/PathUtil.ts
|
|
4101
|
+
/** simplistic path manipulation utilities */
|
|
4102
|
+
/** return path with ./ and foo/.. elements removed */
|
|
4103
|
+
function normalize(path) {
|
|
4104
|
+
const noDots = path.split("/").filter((s) => s !== ".");
|
|
4105
|
+
const noDbl = [];
|
|
4106
|
+
noDots.forEach((s) => {
|
|
4107
|
+
if (s !== "") if (s === ".." && noDbl.length && noDbl[noDbl.length - 1] !== "..") noDbl.pop();
|
|
4108
|
+
else noDbl.push(s);
|
|
4109
|
+
});
|
|
4110
|
+
return noDbl.join("/");
|
|
4111
|
+
}
|
|
4112
|
+
/** return path w/o a suffix.
|
|
4113
|
+
* e.g. /foo/bar.wgsl => /foo/bar */
|
|
4114
|
+
function noSuffix(path) {
|
|
4115
|
+
const lastSlash = path.lastIndexOf("/");
|
|
4116
|
+
const lastStart = lastSlash === -1 ? 0 : lastSlash + 1;
|
|
4117
|
+
const suffix = path.indexOf(".", lastStart);
|
|
4118
|
+
const suffixStart = suffix === -1 ? path.length : suffix;
|
|
4119
|
+
return path.slice(0, suffixStart);
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
//#endregion
|
|
4123
|
+
//#region ../wesl/src/ModuleResolver.ts
|
|
4124
|
+
const libRegex = /^lib\.w[eg]sl$/i;
|
|
4125
|
+
/** Module resolver for in-memory source records. Lazy by default. */
|
|
4126
|
+
var RecordResolver = class {
|
|
4127
|
+
astCache = /* @__PURE__ */ new Map();
|
|
4128
|
+
sources;
|
|
4129
|
+
packageName;
|
|
4130
|
+
debugWeslRoot;
|
|
4131
|
+
constructor(sources, options = {}) {
|
|
4132
|
+
const { packageName = "package", debugWeslRoot } = options;
|
|
4133
|
+
this.sources = sources;
|
|
4134
|
+
this.packageName = packageName;
|
|
4135
|
+
this.debugWeslRoot = normalizeDebugRoot(debugWeslRoot);
|
|
4136
|
+
}
|
|
4137
|
+
resolveModule(modulePath) {
|
|
4138
|
+
const cached = this.astCache.get(modulePath);
|
|
4139
|
+
if (cached) return cached;
|
|
4140
|
+
const source = this.findSource(modulePath);
|
|
4141
|
+
if (source === void 0) return void 0;
|
|
4142
|
+
const ast = parseSrcModule({
|
|
4143
|
+
modulePath,
|
|
4144
|
+
debugFilePath: this.modulePathToDebugPath(modulePath),
|
|
4145
|
+
src: source
|
|
4146
|
+
});
|
|
4147
|
+
this.astCache.set(modulePath, ast);
|
|
4148
|
+
return ast;
|
|
4149
|
+
}
|
|
4150
|
+
findSource(modulePath) {
|
|
4151
|
+
if (this.sources[modulePath] !== void 0) return this.sources[modulePath];
|
|
4152
|
+
const filePath = this.moduleToFilePath(modulePath);
|
|
4153
|
+
if (filePath === void 0) return void 0;
|
|
4154
|
+
return findInVariants(this.sources, filePath);
|
|
4155
|
+
}
|
|
4156
|
+
/** Convert module path to file path, or undefined if not local. */
|
|
4157
|
+
moduleToFilePath(modulePath) {
|
|
4158
|
+
return moduleToRelativePath(modulePath, this.packageName);
|
|
4159
|
+
}
|
|
4160
|
+
modulePathToDebugPath(modulePath) {
|
|
4161
|
+
const filePath = this.moduleToFilePath(modulePath) ?? modulePath;
|
|
4162
|
+
return this.debugWeslRoot + filePath + ".wesl";
|
|
4163
|
+
}
|
|
4164
|
+
/** Parse all modules and return entries. */
|
|
4165
|
+
allModules() {
|
|
4166
|
+
for (const filePath of Object.keys(this.sources)) {
|
|
4167
|
+
const treatLibAsRoot = this.packageName !== "package";
|
|
4168
|
+
const modulePath = fileToModulePath(filePath, this.packageName, treatLibAsRoot);
|
|
4169
|
+
this.resolveModule(modulePath);
|
|
4170
|
+
}
|
|
4171
|
+
return this.astCache.entries();
|
|
4172
|
+
}
|
|
4173
|
+
};
|
|
4174
|
+
/** Composite resolver that tries each resolver in order until one succeeds. */
|
|
4175
|
+
var CompositeResolver = class {
|
|
4176
|
+
resolvers;
|
|
4177
|
+
constructor(resolvers) {
|
|
4178
|
+
this.resolvers = resolvers;
|
|
4179
|
+
}
|
|
4180
|
+
resolveModule(modulePath) {
|
|
4181
|
+
for (const resolver of this.resolvers) {
|
|
4182
|
+
const ast = resolver.resolveModule(modulePath);
|
|
4183
|
+
if (ast) return ast;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
};
|
|
4187
|
+
/** Lazy resolver for WeslBundle library modules. */
|
|
4188
|
+
var BundleResolver = class {
|
|
4189
|
+
astCache = /* @__PURE__ */ new Map();
|
|
4190
|
+
sources;
|
|
4191
|
+
packageName;
|
|
4192
|
+
debugWeslRoot;
|
|
4193
|
+
constructor(bundle, debugWeslRoot) {
|
|
4194
|
+
this.sources = bundle.modules;
|
|
4195
|
+
this.packageName = bundle.name;
|
|
4196
|
+
this.debugWeslRoot = normalizeDebugRoot(debugWeslRoot);
|
|
4197
|
+
}
|
|
4198
|
+
resolveModule(modulePath) {
|
|
4199
|
+
const pkgPrefix = this.packageName + "::";
|
|
4200
|
+
if (modulePath !== this.packageName && !modulePath.startsWith(pkgPrefix)) return;
|
|
4201
|
+
const cached = this.astCache.get(modulePath);
|
|
4202
|
+
if (cached) return cached;
|
|
4203
|
+
const source = this.findSource(modulePath);
|
|
4204
|
+
if (!source) return void 0;
|
|
4205
|
+
const ast = parseSrcModule({
|
|
4206
|
+
modulePath,
|
|
4207
|
+
debugFilePath: this.modulePathToDebugPath(modulePath),
|
|
4208
|
+
src: source
|
|
4209
|
+
});
|
|
4210
|
+
this.astCache.set(modulePath, ast);
|
|
4211
|
+
return ast;
|
|
4212
|
+
}
|
|
4213
|
+
findSource(modulePath) {
|
|
4214
|
+
const filePath = this.moduleToFilePath(modulePath);
|
|
4215
|
+
if (modulePath === this.packageName) {
|
|
4216
|
+
const libSrc = findInVariants(this.sources, "lib", ["wesl", "wgsl"]);
|
|
4217
|
+
if (libSrc) return libSrc;
|
|
4218
|
+
}
|
|
4219
|
+
return findInVariants(this.sources, filePath);
|
|
4220
|
+
}
|
|
4221
|
+
moduleToFilePath(modulePath) {
|
|
4222
|
+
return moduleToRelativePath(modulePath, this.packageName) ?? modulePath;
|
|
4223
|
+
}
|
|
4224
|
+
modulePathToDebugPath(modulePath) {
|
|
4225
|
+
const filePath = this.moduleToFilePath(modulePath);
|
|
4226
|
+
return this.debugWeslRoot + this.packageName + "/" + filePath + ".wesl";
|
|
4227
|
+
}
|
|
4228
|
+
};
|
|
4229
|
+
/** Convert file path to module path (e.g., "foo/bar.wesl" to "package::foo::bar"). */
|
|
4230
|
+
function fileToModulePath(filePath, packageName, treatLibAsRoot) {
|
|
4231
|
+
if (filePath.includes("::")) return filePath;
|
|
4232
|
+
if (treatLibAsRoot && libRegex.test(filePath)) return packageName;
|
|
4233
|
+
const moduleSuffix = noSuffix(normalize(filePath)).replaceAll("/", "::");
|
|
4234
|
+
return packageName + "::" + moduleSuffix;
|
|
4235
|
+
}
|
|
4236
|
+
/** Try path variants with/without ./ prefix and extension suffixes. */
|
|
4237
|
+
function findInVariants(sources, basePath, extensions = ["wesl", "wgsl"]) {
|
|
4238
|
+
for (const prefix of ["", "./"]) {
|
|
4239
|
+
const path = prefix + basePath;
|
|
4240
|
+
if (sources[path] !== void 0) return sources[path];
|
|
4241
|
+
for (const ext of extensions) {
|
|
4242
|
+
const withExt = `${path}.${ext}`;
|
|
4243
|
+
if (sources[withExt] !== void 0) return sources[withExt];
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4112
4246
|
}
|
|
4113
4247
|
|
|
4114
4248
|
//#endregion
|
|
@@ -4147,9 +4281,7 @@ function findUnboundRefs(resolver) {
|
|
|
4147
4281
|
decls: new Map(rootDecls.map((d) => [d.originalName, d])),
|
|
4148
4282
|
parent: null
|
|
4149
4283
|
};
|
|
4150
|
-
filterMap(rootDecls, (decl) => decl.dependentScope)
|
|
4151
|
-
bindIdentsRecursive(s, bindContext, makeLiveDecls(liveDecls));
|
|
4152
|
-
});
|
|
4284
|
+
for (const s of filterMap(rootDecls, (decl) => decl.dependentScope)) bindIdentsRecursive(s, bindContext, makeLiveDecls(liveDecls));
|
|
4153
4285
|
bindIdentsRecursive(ast.rootScope, bindContext, liveDecls);
|
|
4154
4286
|
}
|
|
4155
4287
|
return bindContext.unbound;
|
|
@@ -4292,155 +4424,6 @@ function compilationInfoToErrorMessage(compilationInfo, shaderModule) {
|
|
|
4292
4424
|
return result;
|
|
4293
4425
|
}
|
|
4294
4426
|
|
|
4295
|
-
//#endregion
|
|
4296
|
-
//#region ../wesl/src/PathUtil.ts
|
|
4297
|
-
/** simplistic path manipulation utilities */
|
|
4298
|
-
/** return path with ./ and foo/.. elements removed */
|
|
4299
|
-
function normalize(path) {
|
|
4300
|
-
const noDots = path.split("/").filter((s) => s !== ".");
|
|
4301
|
-
const noDbl = [];
|
|
4302
|
-
noDots.forEach((s) => {
|
|
4303
|
-
if (s !== "") if (s === ".." && noDbl.length && noDbl[noDbl.length - 1] !== "..") noDbl.pop();
|
|
4304
|
-
else noDbl.push(s);
|
|
4305
|
-
});
|
|
4306
|
-
return noDbl.join("/");
|
|
4307
|
-
}
|
|
4308
|
-
/** return path w/o a suffix.
|
|
4309
|
-
* e.g. /foo/bar.wgsl => /foo/bar */
|
|
4310
|
-
function noSuffix(path) {
|
|
4311
|
-
const lastSlash = path.lastIndexOf("/");
|
|
4312
|
-
const lastStart = lastSlash === -1 ? 0 : lastSlash + 1;
|
|
4313
|
-
const suffix = path.indexOf(".", lastStart);
|
|
4314
|
-
const suffixStart = suffix === -1 ? path.length : suffix;
|
|
4315
|
-
return path.slice(0, suffixStart);
|
|
4316
|
-
}
|
|
4317
|
-
|
|
4318
|
-
//#endregion
|
|
4319
|
-
//#region ../wesl/src/ModuleResolver.ts
|
|
4320
|
-
const libRegex = /^lib\.w[eg]sl$/i;
|
|
4321
|
-
/** Module resolver for in-memory source records. Lazy by default. */
|
|
4322
|
-
var RecordResolver = class {
|
|
4323
|
-
astCache = /* @__PURE__ */ new Map();
|
|
4324
|
-
sources;
|
|
4325
|
-
packageName;
|
|
4326
|
-
debugWeslRoot;
|
|
4327
|
-
constructor(sources, options = {}) {
|
|
4328
|
-
const { packageName = "package", debugWeslRoot } = options;
|
|
4329
|
-
this.sources = sources;
|
|
4330
|
-
this.packageName = packageName;
|
|
4331
|
-
this.debugWeslRoot = normalizeDebugRoot(debugWeslRoot);
|
|
4332
|
-
}
|
|
4333
|
-
resolveModule(modulePath) {
|
|
4334
|
-
const cached = this.astCache.get(modulePath);
|
|
4335
|
-
if (cached) return cached;
|
|
4336
|
-
const source = this.findSource(modulePath);
|
|
4337
|
-
if (source === void 0) return void 0;
|
|
4338
|
-
const ast = parseSrcModule({
|
|
4339
|
-
modulePath,
|
|
4340
|
-
debugFilePath: this.modulePathToDebugPath(modulePath),
|
|
4341
|
-
src: source
|
|
4342
|
-
});
|
|
4343
|
-
this.astCache.set(modulePath, ast);
|
|
4344
|
-
return ast;
|
|
4345
|
-
}
|
|
4346
|
-
findSource(modulePath) {
|
|
4347
|
-
if (this.sources[modulePath] !== void 0) return this.sources[modulePath];
|
|
4348
|
-
const filePath = this.moduleToFilePath(modulePath);
|
|
4349
|
-
if (filePath === void 0) return void 0;
|
|
4350
|
-
return findInVariants(this.sources, filePath);
|
|
4351
|
-
}
|
|
4352
|
-
/** Convert module path to file path, or undefined if not local. */
|
|
4353
|
-
moduleToFilePath(modulePath) {
|
|
4354
|
-
return moduleToRelativePath(modulePath, this.packageName);
|
|
4355
|
-
}
|
|
4356
|
-
modulePathToDebugPath(modulePath) {
|
|
4357
|
-
const filePath = this.moduleToFilePath(modulePath) ?? modulePath;
|
|
4358
|
-
return this.debugWeslRoot + filePath + ".wesl";
|
|
4359
|
-
}
|
|
4360
|
-
/** Parse all modules and return entries. */
|
|
4361
|
-
allModules() {
|
|
4362
|
-
for (const filePath of Object.keys(this.sources)) {
|
|
4363
|
-
const treatLibAsRoot = this.packageName !== "package";
|
|
4364
|
-
const modulePath = fileToModulePath(filePath, this.packageName, treatLibAsRoot);
|
|
4365
|
-
this.resolveModule(modulePath);
|
|
4366
|
-
}
|
|
4367
|
-
return this.astCache.entries();
|
|
4368
|
-
}
|
|
4369
|
-
};
|
|
4370
|
-
/** Composite resolver that tries each resolver in order until one succeeds. */
|
|
4371
|
-
var CompositeResolver = class {
|
|
4372
|
-
resolvers;
|
|
4373
|
-
constructor(resolvers) {
|
|
4374
|
-
this.resolvers = resolvers;
|
|
4375
|
-
}
|
|
4376
|
-
resolveModule(modulePath) {
|
|
4377
|
-
for (const resolver of this.resolvers) {
|
|
4378
|
-
const ast = resolver.resolveModule(modulePath);
|
|
4379
|
-
if (ast) return ast;
|
|
4380
|
-
}
|
|
4381
|
-
}
|
|
4382
|
-
};
|
|
4383
|
-
/** Lazy resolver for WeslBundle library modules. */
|
|
4384
|
-
var BundleResolver = class {
|
|
4385
|
-
astCache = /* @__PURE__ */ new Map();
|
|
4386
|
-
sources;
|
|
4387
|
-
packageName;
|
|
4388
|
-
debugWeslRoot;
|
|
4389
|
-
constructor(bundle, debugWeslRoot) {
|
|
4390
|
-
this.sources = bundle.modules;
|
|
4391
|
-
this.packageName = bundle.name;
|
|
4392
|
-
this.debugWeslRoot = normalizeDebugRoot(debugWeslRoot);
|
|
4393
|
-
}
|
|
4394
|
-
resolveModule(modulePath) {
|
|
4395
|
-
const pkgPrefix = this.packageName + "::";
|
|
4396
|
-
if (modulePath !== this.packageName && !modulePath.startsWith(pkgPrefix)) return;
|
|
4397
|
-
const cached = this.astCache.get(modulePath);
|
|
4398
|
-
if (cached) return cached;
|
|
4399
|
-
const source = this.findSource(modulePath);
|
|
4400
|
-
if (!source) return void 0;
|
|
4401
|
-
const ast = parseSrcModule({
|
|
4402
|
-
modulePath,
|
|
4403
|
-
debugFilePath: this.modulePathToDebugPath(modulePath),
|
|
4404
|
-
src: source
|
|
4405
|
-
});
|
|
4406
|
-
this.astCache.set(modulePath, ast);
|
|
4407
|
-
return ast;
|
|
4408
|
-
}
|
|
4409
|
-
findSource(modulePath) {
|
|
4410
|
-
const filePath = this.moduleToFilePath(modulePath);
|
|
4411
|
-
if (modulePath === this.packageName) {
|
|
4412
|
-
const libSrc = findInVariants(this.sources, "lib", ["wesl", "wgsl"]);
|
|
4413
|
-
if (libSrc) return libSrc;
|
|
4414
|
-
}
|
|
4415
|
-
return findInVariants(this.sources, filePath);
|
|
4416
|
-
}
|
|
4417
|
-
moduleToFilePath(modulePath) {
|
|
4418
|
-
return moduleToRelativePath(modulePath, this.packageName) ?? modulePath;
|
|
4419
|
-
}
|
|
4420
|
-
modulePathToDebugPath(modulePath) {
|
|
4421
|
-
const filePath = this.moduleToFilePath(modulePath);
|
|
4422
|
-
return this.debugWeslRoot + this.packageName + "/" + filePath + ".wesl";
|
|
4423
|
-
}
|
|
4424
|
-
};
|
|
4425
|
-
/** Convert file path to module path (e.g., "foo/bar.wesl" to "package::foo::bar"). */
|
|
4426
|
-
function fileToModulePath(filePath, packageName, treatLibAsRoot) {
|
|
4427
|
-
if (filePath.includes("::")) return filePath;
|
|
4428
|
-
if (treatLibAsRoot && libRegex.test(filePath)) return packageName;
|
|
4429
|
-
const moduleSuffix = noSuffix(normalize(filePath)).replaceAll("/", "::");
|
|
4430
|
-
return packageName + "::" + moduleSuffix;
|
|
4431
|
-
}
|
|
4432
|
-
/** Try path variants with/without ./ prefix and extension suffixes. */
|
|
4433
|
-
function findInVariants(sources, basePath, extensions = ["wesl", "wgsl"]) {
|
|
4434
|
-
for (const prefix of ["", "./"]) {
|
|
4435
|
-
const path = prefix + basePath;
|
|
4436
|
-
if (sources[path] !== void 0) return sources[path];
|
|
4437
|
-
for (const ext of extensions) {
|
|
4438
|
-
const withExt = `${path}.${ext}`;
|
|
4439
|
-
if (sources[withExt] !== void 0) return sources[withExt];
|
|
4440
|
-
}
|
|
4441
|
-
}
|
|
4442
|
-
}
|
|
4443
|
-
|
|
4444
4427
|
//#endregion
|
|
4445
4428
|
//#region ../wesl/src/SrcMap.ts
|
|
4446
4429
|
/** map text ranges in multiple src texts to a single dest text */
|
|
@@ -5116,7 +5099,7 @@ function normalizeUrl(url) {
|
|
|
5116
5099
|
|
|
5117
5100
|
//#endregion
|
|
5118
5101
|
//#region ../wesl-fetch/src/PackageLoader.ts
|
|
5119
|
-
const virtualModules = ["constants", "
|
|
5102
|
+
const virtualModules = ["constants", "env"];
|
|
5120
5103
|
/** Resolve dependencies: internal modules via HTTP, external packages from npm. */
|
|
5121
5104
|
async function fetchDependencies(rootModuleSource, options) {
|
|
5122
5105
|
const shaderRoot = options?.shaderRoot ?? "/shaders";
|
|
@@ -5400,18 +5383,16 @@ function updateRenderUniforms(buffer, device, resolution, time, mouse = [0, 0])
|
|
|
5400
5383
|
]);
|
|
5401
5384
|
device.queue.writeBuffer(buffer, 0, data);
|
|
5402
5385
|
}
|
|
5403
|
-
/**
|
|
5404
|
-
* return the WGSL struct for use in shaders as test::Uniforms.
|
|
5405
|
-
*
|
|
5406
|
-
* @returns virtual library object for passing to compileShader()
|
|
5407
|
-
*/
|
|
5386
|
+
/** @returns virtual library providing env::u and env::Uniforms for shaders */
|
|
5408
5387
|
function createUniformsVirtualLib() {
|
|
5409
|
-
return {
|
|
5388
|
+
return { env: () => `
|
|
5410
5389
|
struct Uniforms {
|
|
5411
5390
|
resolution: vec2f, // Output viewport dimensions
|
|
5412
5391
|
time: f32, // Elapsed time in seconds
|
|
5413
5392
|
mouse: vec2f, // Mouse position [0,1] normalized coords
|
|
5414
5393
|
}
|
|
5394
|
+
|
|
5395
|
+
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
5415
5396
|
` };
|
|
5416
5397
|
}
|
|
5417
5398
|
|
|
@@ -5659,26 +5640,13 @@ async function initWebGPU(canvas, alphaMode = "opaque") {
|
|
|
5659
5640
|
}
|
|
5660
5641
|
/** Compile WESL fragment shader and create render pipeline. */
|
|
5661
5642
|
async function createPipeline(state, fragmentSource, options) {
|
|
5662
|
-
const { weslSrc, libs = [], conditions, constants } = options ?? {};
|
|
5663
|
-
const { packageName, rootModuleName } = options ?? {};
|
|
5664
|
-
let resolver;
|
|
5665
|
-
if (weslSrc || libs.length > 0) {
|
|
5666
|
-
const resolvers = [];
|
|
5667
|
-
if (weslSrc) resolvers.push(new RecordResolver(weslSrc, { packageName }));
|
|
5668
|
-
for (const bundle of libs) resolvers.push(new BundleResolver(bundle));
|
|
5669
|
-
resolver = resolvers.length === 1 ? resolvers[0] : new CompositeResolver(resolvers);
|
|
5670
|
-
}
|
|
5671
5643
|
state.device.pushErrorScope("validation");
|
|
5672
5644
|
const pipeline = await linkAndCreatePipeline({
|
|
5673
5645
|
device: state.device,
|
|
5674
5646
|
fragmentSource,
|
|
5675
|
-
resolver,
|
|
5676
5647
|
format: state.presentationFormat,
|
|
5677
5648
|
layout: state.pipelineLayout,
|
|
5678
|
-
|
|
5679
|
-
constants,
|
|
5680
|
-
packageName,
|
|
5681
|
-
rootModuleName
|
|
5649
|
+
...options
|
|
5682
5650
|
});
|
|
5683
5651
|
const gpuError = await state.device.popErrorScope();
|
|
5684
5652
|
if (gpuError) {
|
|
@@ -5872,7 +5840,7 @@ var WgslPlay = class extends HTMLElement {
|
|
|
5872
5840
|
this._weslSrc = Object.fromEntries(entries);
|
|
5873
5841
|
this._rootModuleName = rootModuleName ? toModulePath(rootModuleName) : "package::main";
|
|
5874
5842
|
this._fromFullProject = true;
|
|
5875
|
-
this.
|
|
5843
|
+
this.rebuildPipeline();
|
|
5876
5844
|
}
|
|
5877
5845
|
/** Whether to auto-fetch missing library packages from npm (default: true). */
|
|
5878
5846
|
get fetchLibs() {
|
|
@@ -6003,15 +5971,26 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6003
5971
|
return { [this._rootModuleName]: source };
|
|
6004
5972
|
};
|
|
6005
5973
|
const conditions = el.conditions;
|
|
6006
|
-
|
|
6007
|
-
|
|
5974
|
+
const rootModuleName = el.rootModuleName;
|
|
5975
|
+
const libs = el.libs;
|
|
5976
|
+
const weslSrc = getSources();
|
|
5977
|
+
const entries = Object.entries(weslSrc).map(([k, v]) => [toModulePath(k), v]);
|
|
5978
|
+
this._weslSrc = Object.fromEntries(entries);
|
|
5979
|
+
this._rootModuleName = rootModuleName ? toModulePath(rootModuleName) : "package::main";
|
|
5980
|
+
if (conditions) this._linkOptions = {
|
|
5981
|
+
...this._linkOptions,
|
|
6008
5982
|
conditions
|
|
6009
5983
|
};
|
|
5984
|
+
if (libs) this._libs = libs;
|
|
5985
|
+
this._fromFullProject = false;
|
|
5986
|
+
await this.discoverAndRebuild();
|
|
5987
|
+
this._fromFullProject = true;
|
|
6010
5988
|
this._sourceListener = (e) => {
|
|
6011
5989
|
const detail = e.detail;
|
|
6012
5990
|
const fallback = { [this._rootModuleName]: detail?.source ?? "" };
|
|
6013
5991
|
this.project = {
|
|
6014
5992
|
weslSrc: detail?.sources ?? fallback,
|
|
5993
|
+
rootModuleName: detail?.rootModuleName,
|
|
6015
5994
|
conditions: detail?.conditions
|
|
6016
5995
|
};
|
|
6017
5996
|
};
|
|
@@ -6028,7 +6007,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6028
6007
|
this._fromFullProject = false;
|
|
6029
6008
|
if (rootModuleName) this._rootModuleName = rootModuleName;
|
|
6030
6009
|
const mainSource = weslSrc[this._rootModuleName];
|
|
6031
|
-
if (!mainSource)
|
|
6010
|
+
if (!mainSource) {
|
|
6011
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(weslSrc));
|
|
6012
|
+
return;
|
|
6013
|
+
}
|
|
6032
6014
|
await createPipeline(this.renderState, mainSource, {
|
|
6033
6015
|
...this._linkOptions,
|
|
6034
6016
|
weslSrc,
|
|
@@ -6044,7 +6026,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6044
6026
|
async rebuildPipeline() {
|
|
6045
6027
|
if (!await this.initialize()) return;
|
|
6046
6028
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
6047
|
-
if (!mainSource)
|
|
6029
|
+
if (!mainSource) {
|
|
6030
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
6031
|
+
return;
|
|
6032
|
+
}
|
|
6048
6033
|
try {
|
|
6049
6034
|
this.errorOverlay.hide();
|
|
6050
6035
|
await createPipeline(this.renderState, mainSource, {
|
|
@@ -6062,7 +6047,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6062
6047
|
async discoverAndRebuild() {
|
|
6063
6048
|
if (!await this.initialize()) return;
|
|
6064
6049
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
6065
|
-
if (!mainSource)
|
|
6050
|
+
if (!mainSource) {
|
|
6051
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
6052
|
+
return;
|
|
6053
|
+
}
|
|
6066
6054
|
try {
|
|
6067
6055
|
this.errorOverlay.hide();
|
|
6068
6056
|
const { weslSrc, libs } = await fetchDependencies(mainSource, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wgsl-play",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"wesl": "0.7.
|
|
26
|
-
"wesl-fetch": "0.0.
|
|
27
|
-
"wesl-gpu": "0.1.
|
|
25
|
+
"wesl": "0.7.23",
|
|
26
|
+
"wesl-fetch": "0.0.11",
|
|
27
|
+
"wesl-gpu": "0.1.25"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@playwright/test": "^1.53.2",
|
package/src/Renderer.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
BundleResolver,
|
|
4
|
-
CompositeResolver,
|
|
5
|
-
RecordResolver,
|
|
6
|
-
requestWeslDevice,
|
|
7
|
-
} from "wesl";
|
|
1
|
+
import { requestWeslDevice } from "wesl";
|
|
8
2
|
import {
|
|
9
3
|
linkAndCreatePipeline,
|
|
10
4
|
renderFrame,
|
|
@@ -89,30 +83,13 @@ export async function createPipeline(
|
|
|
89
83
|
fragmentSource: string,
|
|
90
84
|
options?: LinkOptions,
|
|
91
85
|
): Promise<void> {
|
|
92
|
-
const { weslSrc, libs = [], conditions, constants } = options ?? {};
|
|
93
|
-
const { packageName, rootModuleName } = options ?? {};
|
|
94
|
-
|
|
95
|
-
// Build resolver from weslSrc/libs if provided
|
|
96
|
-
let resolver: ModuleResolver | undefined;
|
|
97
|
-
if (weslSrc || libs.length > 0) {
|
|
98
|
-
const resolvers: ModuleResolver[] = [];
|
|
99
|
-
if (weslSrc) resolvers.push(new RecordResolver(weslSrc, { packageName }));
|
|
100
|
-
for (const bundle of libs) resolvers.push(new BundleResolver(bundle));
|
|
101
|
-
resolver =
|
|
102
|
-
resolvers.length === 1 ? resolvers[0] : new CompositeResolver(resolvers);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
86
|
state.device.pushErrorScope("validation");
|
|
106
87
|
const pipeline = await linkAndCreatePipeline({
|
|
107
88
|
device: state.device,
|
|
108
89
|
fragmentSource,
|
|
109
|
-
resolver,
|
|
110
90
|
format: state.presentationFormat,
|
|
111
91
|
layout: state.pipelineLayout,
|
|
112
|
-
|
|
113
|
-
constants,
|
|
114
|
-
packageName,
|
|
115
|
-
rootModuleName,
|
|
92
|
+
...options,
|
|
116
93
|
});
|
|
117
94
|
const gpuError = await state.device.popErrorScope();
|
|
118
95
|
if (gpuError) {
|
package/src/WgslPlay.ts
CHANGED
|
@@ -251,7 +251,7 @@ export class WgslPlay extends HTMLElement {
|
|
|
251
251
|
? toModulePath(rootModuleName)
|
|
252
252
|
: "package::main";
|
|
253
253
|
this._fromFullProject = true;
|
|
254
|
-
this.
|
|
254
|
+
this.rebuildPipeline();
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
/** Whether to auto-fetch missing library packages from npm (default: true). */
|
|
@@ -425,16 +425,33 @@ export class WgslPlay extends HTMLElement {
|
|
|
425
425
|
return { [this._rootModuleName]: source };
|
|
426
426
|
};
|
|
427
427
|
|
|
428
|
-
// Load initial sources and
|
|
428
|
+
// Load initial sources, conditions, and libs from source element.
|
|
429
|
+
// Use discoverAndRebuild (not project setter) so external deps are fetched.
|
|
429
430
|
const conditions = (el as any).conditions;
|
|
430
|
-
|
|
431
|
+
const rootModuleName = (el as any).rootModuleName;
|
|
432
|
+
const libs = (el as any).libs;
|
|
433
|
+
const weslSrc = getSources();
|
|
434
|
+
const entries = Object.entries(weslSrc).map(([k, v]) => [
|
|
435
|
+
toModulePath(k),
|
|
436
|
+
v,
|
|
437
|
+
]);
|
|
438
|
+
this._weslSrc = Object.fromEntries(entries);
|
|
439
|
+
this._rootModuleName = rootModuleName
|
|
440
|
+
? toModulePath(rootModuleName)
|
|
441
|
+
: "package::main";
|
|
442
|
+
if (conditions) this._linkOptions = { ...this._linkOptions, conditions };
|
|
443
|
+
if (libs) this._libs = libs;
|
|
444
|
+
this._fromFullProject = false;
|
|
445
|
+
await this.discoverAndRebuild();
|
|
446
|
+
this._fromFullProject = true; // fast rebuilds on subsequent edits
|
|
431
447
|
|
|
432
|
-
// Listen for changes -
|
|
448
|
+
// Listen for changes - rebuild with cached libs
|
|
433
449
|
this._sourceListener = (e: Event) => {
|
|
434
450
|
const detail = (e as CustomEvent).detail;
|
|
435
451
|
const fallback = { [this._rootModuleName]: detail?.source ?? "" };
|
|
436
452
|
this.project = {
|
|
437
453
|
weslSrc: detail?.sources ?? fallback,
|
|
454
|
+
rootModuleName: detail?.rootModuleName,
|
|
438
455
|
conditions: detail?.conditions,
|
|
439
456
|
};
|
|
440
457
|
};
|
|
@@ -457,7 +474,13 @@ export class WgslPlay extends HTMLElement {
|
|
|
457
474
|
if (rootModuleName) this._rootModuleName = rootModuleName;
|
|
458
475
|
|
|
459
476
|
const mainSource = weslSrc[this._rootModuleName];
|
|
460
|
-
if (!mainSource)
|
|
477
|
+
if (!mainSource) {
|
|
478
|
+
console.warn(
|
|
479
|
+
`wgsl-play: root module "${this._rootModuleName}" not found in sources:`,
|
|
480
|
+
Object.keys(weslSrc),
|
|
481
|
+
);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
461
484
|
|
|
462
485
|
await createPipeline(this.renderState, mainSource, {
|
|
463
486
|
...this._linkOptions,
|
|
@@ -476,7 +499,13 @@ export class WgslPlay extends HTMLElement {
|
|
|
476
499
|
if (!(await this.initialize())) return;
|
|
477
500
|
|
|
478
501
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
479
|
-
if (!mainSource)
|
|
502
|
+
if (!mainSource) {
|
|
503
|
+
console.warn(
|
|
504
|
+
`wgsl-play: root module "${this._rootModuleName}" not found in sources:`,
|
|
505
|
+
Object.keys(this._weslSrc),
|
|
506
|
+
);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
480
509
|
|
|
481
510
|
try {
|
|
482
511
|
this.errorOverlay.hide();
|
|
@@ -497,7 +526,13 @@ export class WgslPlay extends HTMLElement {
|
|
|
497
526
|
if (!(await this.initialize())) return;
|
|
498
527
|
|
|
499
528
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
500
|
-
if (!mainSource)
|
|
529
|
+
if (!mainSource) {
|
|
530
|
+
console.warn(
|
|
531
|
+
`wgsl-play: root module "${this._rootModuleName}" not found in sources:`,
|
|
532
|
+
Object.keys(this._weslSrc),
|
|
533
|
+
);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
501
536
|
|
|
502
537
|
try {
|
|
503
538
|
this.errorOverlay.hide();
|
package/src/test/WgslPlay.e2e.ts
CHANGED
|
@@ -185,6 +185,61 @@ test("project.conditions - shows green initially, red after setting RED", async
|
|
|
185
185
|
await expectCanvasSnapshot(page, "#player7", "conditions-after-red.png");
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
+
test("connectToSource - multi-file from wgsl-edit renders", async ({
|
|
189
|
+
page,
|
|
190
|
+
}) => {
|
|
191
|
+
await page.goto("/");
|
|
192
|
+
await waitForFrame(page, "#player9");
|
|
193
|
+
await expectCanvasSnapshot(page, "#player9", "connect-source-multifile.png");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("connectToSource - condition toggle re-renders", async ({ page }) => {
|
|
197
|
+
await page.goto("/");
|
|
198
|
+
await waitForFrame(page, "#player10");
|
|
199
|
+
|
|
200
|
+
// Initial: green (@else branch)
|
|
201
|
+
await expectCanvasSnapshot(page, "#player10", "connect-conditions-green.png");
|
|
202
|
+
|
|
203
|
+
// Toggle RED condition via editor
|
|
204
|
+
await page.click("#toggle-condition10");
|
|
205
|
+
await waitForNewFrame(page, "#player10");
|
|
206
|
+
|
|
207
|
+
// After: red (@if branch)
|
|
208
|
+
await expectCanvasSnapshot(page, "#player10", "connect-conditions-red.png");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("connectToSource - external deps are fetched", async ({ page }) => {
|
|
212
|
+
await page.goto("/");
|
|
213
|
+
await page.waitForLoadState("networkidle");
|
|
214
|
+
await waitForFrame(page, "#player11");
|
|
215
|
+
|
|
216
|
+
// If discoverAndRebuild wasn't called, this would show an error overlay
|
|
217
|
+
const hasError = await page.evaluate(
|
|
218
|
+
() => (document.querySelector("#player11") as any)?.hasError ?? true,
|
|
219
|
+
);
|
|
220
|
+
expect(hasError).toBe(false);
|
|
221
|
+
await expectCanvasSnapshot(page, "#player11", "connect-source-external.png");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("editor.link() with virtualLibs resolves env:: module", async ({
|
|
225
|
+
page,
|
|
226
|
+
}) => {
|
|
227
|
+
await page.goto("/");
|
|
228
|
+
await waitForWgslPlay(page);
|
|
229
|
+
|
|
230
|
+
await page.click("#link-btn12");
|
|
231
|
+
await page.waitForFunction(
|
|
232
|
+
() =>
|
|
233
|
+
document.querySelector("#link-output12")?.textContent !==
|
|
234
|
+
"Not linked yet",
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const output = await page.textContent("#link-output12");
|
|
238
|
+
expect(output).toContain("var<uniform>");
|
|
239
|
+
expect(output).toContain("fs_main");
|
|
240
|
+
expect(output).not.toContain("Error");
|
|
241
|
+
});
|
|
242
|
+
|
|
188
243
|
test("no critical console errors on basic load", async ({ page }) => {
|
|
189
244
|
const errors: string[] = [];
|
|
190
245
|
page.on("console", msg => {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|