wgsl-play 0.0.32 → 0.0.34
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 +34 -14
- package/dist/index.js +1 -1
- package/dist/wgsl-play.js +300 -312
- package/package.json +4 -4
- package/src/Renderer.ts +2 -25
- package/src/WgslPlay.ts +48 -20
- 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
|
|
|
@@ -151,11 +151,12 @@ var WgslPlay = class extends HTMLElement {
|
|
|
151
151
|
}
|
|
152
152
|
/** Set sources from a full project with weslSrc. */
|
|
153
153
|
setProjectSources(weslSrc, rootModuleName) {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
this.
|
|
154
|
+
const pkg = this._linkOptions.packageName || "package";
|
|
155
|
+
const root = rootModuleName ?? "main";
|
|
156
|
+
this._weslSrc = toModulePaths(weslSrc, pkg);
|
|
157
|
+
this._rootModuleName = fileToModulePath(root, pkg, false);
|
|
157
158
|
this._fromFullProject = true;
|
|
158
|
-
this.
|
|
159
|
+
this.rebuildPipeline();
|
|
159
160
|
}
|
|
160
161
|
/** Whether to auto-fetch missing library packages from npm (default: true). */
|
|
161
162
|
get fetchLibs() {
|
|
@@ -285,16 +286,24 @@ var WgslPlay = class extends HTMLElement {
|
|
|
285
286
|
const source = el.source ?? el.textContent ?? "";
|
|
286
287
|
return { [this._rootModuleName]: source };
|
|
287
288
|
};
|
|
288
|
-
const conditions = el
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
const { conditions, rootModuleName, libs } = el;
|
|
290
|
+
const root = rootModuleName ?? "main";
|
|
291
|
+
this._weslSrc = getSources();
|
|
292
|
+
this._rootModuleName = fileToModulePath(root, "package", false);
|
|
293
|
+
if (conditions) this._linkOptions = {
|
|
294
|
+
...this._linkOptions,
|
|
291
295
|
conditions
|
|
292
296
|
};
|
|
297
|
+
if (libs) this._libs = libs;
|
|
298
|
+
this._fromFullProject = false;
|
|
299
|
+
await this.discoverAndRebuild();
|
|
300
|
+
this._fromFullProject = true;
|
|
293
301
|
this._sourceListener = (e) => {
|
|
294
302
|
const detail = e.detail;
|
|
295
303
|
const fallback = { [this._rootModuleName]: detail?.source ?? "" };
|
|
296
304
|
this.project = {
|
|
297
305
|
weslSrc: detail?.sources ?? fallback,
|
|
306
|
+
rootModuleName: detail?.rootModuleName,
|
|
298
307
|
conditions: detail?.conditions
|
|
299
308
|
};
|
|
300
309
|
};
|
|
@@ -311,7 +320,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
311
320
|
this._fromFullProject = false;
|
|
312
321
|
if (rootModuleName) this._rootModuleName = rootModuleName;
|
|
313
322
|
const mainSource = weslSrc[this._rootModuleName];
|
|
314
|
-
if (!mainSource)
|
|
323
|
+
if (!mainSource) {
|
|
324
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(weslSrc));
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
315
327
|
await createPipeline(this.renderState, mainSource, {
|
|
316
328
|
...this._linkOptions,
|
|
317
329
|
weslSrc,
|
|
@@ -327,7 +339,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
327
339
|
async rebuildPipeline() {
|
|
328
340
|
if (!await this.initialize()) return;
|
|
329
341
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
330
|
-
if (!mainSource)
|
|
342
|
+
if (!mainSource) {
|
|
343
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
331
346
|
try {
|
|
332
347
|
this.errorOverlay.hide();
|
|
333
348
|
await createPipeline(this.renderState, mainSource, {
|
|
@@ -345,7 +360,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
345
360
|
async discoverAndRebuild() {
|
|
346
361
|
if (!await this.initialize()) return;
|
|
347
362
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
348
|
-
if (!mainSource)
|
|
363
|
+
if (!mainSource) {
|
|
364
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
349
367
|
try {
|
|
350
368
|
this.errorOverlay.hide();
|
|
351
369
|
const { weslSrc, libs } = await fetchDependencies(mainSource, {
|
|
@@ -424,9 +442,11 @@ function upgradeProperty(el, prop) {
|
|
|
424
442
|
el[prop] = value;
|
|
425
443
|
}
|
|
426
444
|
}
|
|
427
|
-
/**
|
|
428
|
-
function
|
|
429
|
-
|
|
445
|
+
/** Normalize all keys in a weslSrc record to module paths. */
|
|
446
|
+
function toModulePaths(weslSrc, pkg) {
|
|
447
|
+
const result = {};
|
|
448
|
+
for (const [key, value] of Object.entries(weslSrc)) result[fileToModulePath(key, pkg, false)] = value;
|
|
449
|
+
return result;
|
|
430
450
|
}
|
|
431
451
|
|
|
432
452
|
//#endregion
|
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) {
|
|
@@ -5868,11 +5836,12 @@ var WgslPlay = class extends HTMLElement {
|
|
|
5868
5836
|
}
|
|
5869
5837
|
/** Set sources from a full project with weslSrc. */
|
|
5870
5838
|
setProjectSources(weslSrc, rootModuleName) {
|
|
5871
|
-
const
|
|
5872
|
-
|
|
5873
|
-
this.
|
|
5839
|
+
const pkg = this._linkOptions.packageName || "package";
|
|
5840
|
+
const root = rootModuleName ?? "main";
|
|
5841
|
+
this._weslSrc = toModulePaths(weslSrc, pkg);
|
|
5842
|
+
this._rootModuleName = fileToModulePath(root, pkg, false);
|
|
5874
5843
|
this._fromFullProject = true;
|
|
5875
|
-
this.
|
|
5844
|
+
this.rebuildPipeline();
|
|
5876
5845
|
}
|
|
5877
5846
|
/** Whether to auto-fetch missing library packages from npm (default: true). */
|
|
5878
5847
|
get fetchLibs() {
|
|
@@ -6002,16 +5971,24 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6002
5971
|
const source = el.source ?? el.textContent ?? "";
|
|
6003
5972
|
return { [this._rootModuleName]: source };
|
|
6004
5973
|
};
|
|
6005
|
-
const conditions = el
|
|
6006
|
-
|
|
6007
|
-
|
|
5974
|
+
const { conditions, rootModuleName, libs } = el;
|
|
5975
|
+
const root = rootModuleName ?? "main";
|
|
5976
|
+
this._weslSrc = getSources();
|
|
5977
|
+
this._rootModuleName = fileToModulePath(root, "package", false);
|
|
5978
|
+
if (conditions) this._linkOptions = {
|
|
5979
|
+
...this._linkOptions,
|
|
6008
5980
|
conditions
|
|
6009
5981
|
};
|
|
5982
|
+
if (libs) this._libs = libs;
|
|
5983
|
+
this._fromFullProject = false;
|
|
5984
|
+
await this.discoverAndRebuild();
|
|
5985
|
+
this._fromFullProject = true;
|
|
6010
5986
|
this._sourceListener = (e) => {
|
|
6011
5987
|
const detail = e.detail;
|
|
6012
5988
|
const fallback = { [this._rootModuleName]: detail?.source ?? "" };
|
|
6013
5989
|
this.project = {
|
|
6014
5990
|
weslSrc: detail?.sources ?? fallback,
|
|
5991
|
+
rootModuleName: detail?.rootModuleName,
|
|
6015
5992
|
conditions: detail?.conditions
|
|
6016
5993
|
};
|
|
6017
5994
|
};
|
|
@@ -6028,7 +6005,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6028
6005
|
this._fromFullProject = false;
|
|
6029
6006
|
if (rootModuleName) this._rootModuleName = rootModuleName;
|
|
6030
6007
|
const mainSource = weslSrc[this._rootModuleName];
|
|
6031
|
-
if (!mainSource)
|
|
6008
|
+
if (!mainSource) {
|
|
6009
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(weslSrc));
|
|
6010
|
+
return;
|
|
6011
|
+
}
|
|
6032
6012
|
await createPipeline(this.renderState, mainSource, {
|
|
6033
6013
|
...this._linkOptions,
|
|
6034
6014
|
weslSrc,
|
|
@@ -6044,7 +6024,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6044
6024
|
async rebuildPipeline() {
|
|
6045
6025
|
if (!await this.initialize()) return;
|
|
6046
6026
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
6047
|
-
if (!mainSource)
|
|
6027
|
+
if (!mainSource) {
|
|
6028
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
6029
|
+
return;
|
|
6030
|
+
}
|
|
6048
6031
|
try {
|
|
6049
6032
|
this.errorOverlay.hide();
|
|
6050
6033
|
await createPipeline(this.renderState, mainSource, {
|
|
@@ -6062,7 +6045,10 @@ var WgslPlay = class extends HTMLElement {
|
|
|
6062
6045
|
async discoverAndRebuild() {
|
|
6063
6046
|
if (!await this.initialize()) return;
|
|
6064
6047
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
6065
|
-
if (!mainSource)
|
|
6048
|
+
if (!mainSource) {
|
|
6049
|
+
console.warn(`wgsl-play: root module "${this._rootModuleName}" not found in sources:`, Object.keys(this._weslSrc));
|
|
6050
|
+
return;
|
|
6051
|
+
}
|
|
6066
6052
|
try {
|
|
6067
6053
|
this.errorOverlay.hide();
|
|
6068
6054
|
const { weslSrc, libs } = await fetchDependencies(mainSource, {
|
|
@@ -6141,9 +6127,11 @@ function upgradeProperty(el, prop) {
|
|
|
6141
6127
|
el[prop] = value;
|
|
6142
6128
|
}
|
|
6143
6129
|
}
|
|
6144
|
-
/**
|
|
6145
|
-
function
|
|
6146
|
-
|
|
6130
|
+
/** Normalize all keys in a weslSrc record to module paths. */
|
|
6131
|
+
function toModulePaths(weslSrc, pkg) {
|
|
6132
|
+
const result = {};
|
|
6133
|
+
for (const [key, value] of Object.entries(weslSrc)) result[fileToModulePath(key, pkg, false)] = value;
|
|
6134
|
+
return result;
|
|
6147
6135
|
}
|
|
6148
6136
|
|
|
6149
6137
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wgsl-play",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
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-
|
|
27
|
-
"wesl-
|
|
25
|
+
"wesl": "0.7.23",
|
|
26
|
+
"wesl-gpu": "0.1.25",
|
|
27
|
+
"wesl-fetch": "0.0.11"
|
|
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
|
@@ -241,17 +241,12 @@ export class WgslPlay extends HTMLElement {
|
|
|
241
241
|
weslSrc: Record<string, string>,
|
|
242
242
|
rootModuleName?: string,
|
|
243
243
|
): void {
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
]);
|
|
249
|
-
this._weslSrc = Object.fromEntries(entries);
|
|
250
|
-
this._rootModuleName = rootModuleName
|
|
251
|
-
? toModulePath(rootModuleName)
|
|
252
|
-
: "package::main";
|
|
244
|
+
const pkg = this._linkOptions.packageName || "package";
|
|
245
|
+
const root = rootModuleName ?? "main";
|
|
246
|
+
this._weslSrc = toModulePaths(weslSrc, pkg);
|
|
247
|
+
this._rootModuleName = fileToModulePath(root, pkg, false);
|
|
253
248
|
this._fromFullProject = true;
|
|
254
|
-
this.
|
|
249
|
+
this.rebuildPipeline();
|
|
255
250
|
}
|
|
256
251
|
|
|
257
252
|
/** Whether to auto-fetch missing library packages from npm (default: true). */
|
|
@@ -425,16 +420,25 @@ export class WgslPlay extends HTMLElement {
|
|
|
425
420
|
return { [this._rootModuleName]: source };
|
|
426
421
|
};
|
|
427
422
|
|
|
428
|
-
// Load initial sources and
|
|
429
|
-
|
|
430
|
-
|
|
423
|
+
// Load initial sources, conditions, and libs from source element.
|
|
424
|
+
// Use discoverAndRebuild (not project setter) so external deps are fetched.
|
|
425
|
+
const { conditions, rootModuleName, libs } = el as any;
|
|
426
|
+
const root = rootModuleName ?? "main";
|
|
427
|
+
this._weslSrc = getSources();
|
|
428
|
+
this._rootModuleName = fileToModulePath(root, "package", false);
|
|
429
|
+
if (conditions) this._linkOptions = { ...this._linkOptions, conditions };
|
|
430
|
+
if (libs) this._libs = libs;
|
|
431
|
+
this._fromFullProject = false;
|
|
432
|
+
await this.discoverAndRebuild();
|
|
433
|
+
this._fromFullProject = true; // fast rebuilds on subsequent edits
|
|
431
434
|
|
|
432
|
-
// Listen for changes -
|
|
435
|
+
// Listen for changes - rebuild with cached libs
|
|
433
436
|
this._sourceListener = (e: Event) => {
|
|
434
437
|
const detail = (e as CustomEvent).detail;
|
|
435
438
|
const fallback = { [this._rootModuleName]: detail?.source ?? "" };
|
|
436
439
|
this.project = {
|
|
437
440
|
weslSrc: detail?.sources ?? fallback,
|
|
441
|
+
rootModuleName: detail?.rootModuleName,
|
|
438
442
|
conditions: detail?.conditions,
|
|
439
443
|
};
|
|
440
444
|
};
|
|
@@ -457,7 +461,13 @@ export class WgslPlay extends HTMLElement {
|
|
|
457
461
|
if (rootModuleName) this._rootModuleName = rootModuleName;
|
|
458
462
|
|
|
459
463
|
const mainSource = weslSrc[this._rootModuleName];
|
|
460
|
-
if (!mainSource)
|
|
464
|
+
if (!mainSource) {
|
|
465
|
+
console.warn(
|
|
466
|
+
`wgsl-play: root module "${this._rootModuleName}" not found in sources:`,
|
|
467
|
+
Object.keys(weslSrc),
|
|
468
|
+
);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
461
471
|
|
|
462
472
|
await createPipeline(this.renderState, mainSource, {
|
|
463
473
|
...this._linkOptions,
|
|
@@ -476,7 +486,13 @@ export class WgslPlay extends HTMLElement {
|
|
|
476
486
|
if (!(await this.initialize())) return;
|
|
477
487
|
|
|
478
488
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
479
|
-
if (!mainSource)
|
|
489
|
+
if (!mainSource) {
|
|
490
|
+
console.warn(
|
|
491
|
+
`wgsl-play: root module "${this._rootModuleName}" not found in sources:`,
|
|
492
|
+
Object.keys(this._weslSrc),
|
|
493
|
+
);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
480
496
|
|
|
481
497
|
try {
|
|
482
498
|
this.errorOverlay.hide();
|
|
@@ -497,7 +513,13 @@ export class WgslPlay extends HTMLElement {
|
|
|
497
513
|
if (!(await this.initialize())) return;
|
|
498
514
|
|
|
499
515
|
const mainSource = this._weslSrc[this._rootModuleName];
|
|
500
|
-
if (!mainSource)
|
|
516
|
+
if (!mainSource) {
|
|
517
|
+
console.warn(
|
|
518
|
+
`wgsl-play: root module "${this._rootModuleName}" not found in sources:`,
|
|
519
|
+
Object.keys(this._weslSrc),
|
|
520
|
+
);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
501
523
|
|
|
502
524
|
try {
|
|
503
525
|
this.errorOverlay.hide();
|
|
@@ -594,7 +616,13 @@ function upgradeProperty(el: HTMLElement, prop: string): void {
|
|
|
594
616
|
}
|
|
595
617
|
}
|
|
596
618
|
|
|
597
|
-
/**
|
|
598
|
-
function
|
|
599
|
-
|
|
619
|
+
/** Normalize all keys in a weslSrc record to module paths. */
|
|
620
|
+
function toModulePaths(
|
|
621
|
+
weslSrc: Record<string, string>,
|
|
622
|
+
pkg: string,
|
|
623
|
+
): Record<string, string> {
|
|
624
|
+
const result: Record<string, string> = {};
|
|
625
|
+
for (const [key, value] of Object.entries(weslSrc))
|
|
626
|
+
result[fileToModulePath(key, pkg, false)] = value;
|
|
627
|
+
return result;
|
|
600
628
|
}
|
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
|