@forgeax/engine-runtime 0.1.7 → 0.1.19
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 +45 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/helpers/standard-material-manifest.d.ts +12 -0
- package/dist/__tests__/helpers/standard-material-manifest.d.ts.map +1 -0
- package/dist/__tests__/render-environment-consumer.integration.test.d.ts +2 -0
- package/dist/__tests__/render-environment-consumer.integration.test.d.ts.map +1 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts.map +1 -1
- package/dist/__tests__/renderer-host-fallback.unit.test.d.ts +2 -0
- package/dist/__tests__/renderer-host-fallback.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skinned-shadow-mixed.dawn.test.d.ts +2 -0
- package/dist/__tests__/skinned-shadow-mixed.dawn.test.d.ts.map +1 -0
- package/dist/backend-selection.d.ts.map +1 -1
- package/dist/index.mjs +20 -1
- package/dist/index.mjs.map +1 -1
- package/dist/renderer-host.d.ts +2 -1
- package/dist/renderer-host.d.ts.map +1 -1
- package/dist/renderer-host.mjs +21 -2
- package/dist/renderer-host.mjs.map +1 -1
- package/package.json +30 -30
- package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +12 -11
- package/src/__tests__/errors.unit.test.ts +36 -0
- package/src/__tests__/extract-record-no-hardcoded-texture-fields.test.ts +4 -5
- package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +461 -18
- package/src/__tests__/geometry.unit.test.ts +3 -1
- package/src/__tests__/helpers/standard-material-manifest.ts +45 -0
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +8 -14
- package/src/__tests__/materials.unit.test.ts +2 -0
- package/src/__tests__/pbr-pipeline.unit.test.ts +8 -8
- package/src/__tests__/pipeline-cache-keying.unit.test.ts +6 -4
- package/src/__tests__/pipeline.unit.test.ts +5 -2
- package/src/__tests__/render-environment-consumer.integration.test.ts +33 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +57 -0
- package/src/__tests__/render-feature-prepared-graphics.fixture.ts +10 -0
- package/src/__tests__/render-system-mega.test.ts +3 -1
- package/src/__tests__/render-system-record-multi-material-textureview.test.ts +3 -1
- package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +3 -1
- package/src/__tests__/render-system-record.test.ts +8 -6
- package/src/__tests__/renderer-host-fallback.unit.test.ts +112 -0
- package/src/__tests__/renderer-lifecycle.integration.test.ts +10 -0
- package/src/__tests__/renderer-surface.unit.test.ts +66 -0
- package/src/__tests__/shadow-csm-cascade-loadop.test.ts +5 -1
- package/src/__tests__/shadow-csm-tile-consistency.test.ts +5 -1
- package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +383 -0
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +7 -7
- package/src/__tests__/ssao-passes.test.ts +28 -12
- package/src/__tests__/systems.unit.test.ts +48 -31
- package/src/backend-selection.ts +4 -0
- package/src/renderer-host.ts +30 -3
|
@@ -120,6 +120,7 @@ import { INSTANCE_STORAGE_STRIDE_FLOATS } from '../../../render/src/record/mesh-
|
|
|
120
120
|
import { selectSwapChainFormat } from '../../../render/src/render-system';
|
|
121
121
|
import { createSkinPaletteAllocator } from '../../../render/src/systems/skin-palette-allocator';
|
|
122
122
|
import type { TransparentEntry } from '../../../render/src/systems/transparent-sort-config';
|
|
123
|
+
import { standardMaterialShaderVariants } from './helpers/standard-material-manifest';
|
|
123
124
|
import { drawWithOwners } from './renderer-test-utils';
|
|
124
125
|
|
|
125
126
|
function componentFieldType(component: { fields: Record<string, { type: string }> }, name: string) {
|
|
@@ -1645,7 +1646,8 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
1645
1646
|
sourcePath: `${identifier}.wgsl`,
|
|
1646
1647
|
composedWgsl,
|
|
1647
1648
|
paramSchema: '[]',
|
|
1648
|
-
variants:
|
|
1649
|
+
variants:
|
|
1650
|
+
identifier === 'forgeax::default-standard-pbr' ? standardMaterialShaderVariants() : [],
|
|
1649
1651
|
});
|
|
1650
1652
|
const manifest = {
|
|
1651
1653
|
schemaVersion: '1.0.0',
|
|
@@ -2359,10 +2361,10 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2359
2361
|
});
|
|
2360
2362
|
});
|
|
2361
2363
|
|
|
2362
|
-
// ─── Assertion (d): assembleMaterialWithSkylightEntries
|
|
2364
|
+
// ─── Assertion (d): assembleMaterialWithSkylightEntries adds the engine-owned pair ───
|
|
2363
2365
|
|
|
2364
2366
|
describe('t40 round-4 (d) assembleMaterialWithSkylightEntries shape', () => {
|
|
2365
|
-
it('returns
|
|
2367
|
+
it('returns 16 BindGroupEntry values; entry 7..13 reference the skylight resources', () => {
|
|
2366
2368
|
const materialEntries = makeMaterialBindGroupEntries();
|
|
2367
2369
|
const irrView = { __brand: 'TextureView', id: 1 } as unknown as TextureView;
|
|
2368
2370
|
const irrSampler = { __brand: 'Sampler', id: 1 } as unknown as Sampler;
|
|
@@ -2382,7 +2384,7 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2382
2384
|
intensityBuffer: intensityBuf,
|
|
2383
2385
|
});
|
|
2384
2386
|
|
|
2385
|
-
expect(merged).toHaveLength(
|
|
2387
|
+
expect(merged).toHaveLength(16);
|
|
2386
2388
|
|
|
2387
2389
|
// Skylight entries at binding 7..13 in D-5 order
|
|
2388
2390
|
expect(merged[7]?.binding).toBe(7);
|
|
@@ -2402,6 +2404,8 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2402
2404
|
kind: 'buffer',
|
|
2403
2405
|
value: { buffer: intensityBuf },
|
|
2404
2406
|
});
|
|
2407
|
+
expect(merged[14]?.binding).toBe(14);
|
|
2408
|
+
expect(merged[15]?.binding).toBe(15);
|
|
2405
2409
|
});
|
|
2406
2410
|
|
|
2407
2411
|
it('derives IBL injection start from materialEntries.length (per-shader user-region, not a fixed 7)', () => {
|
|
@@ -2418,9 +2422,11 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2418
2422
|
brdfLutSampler: {} as Sampler,
|
|
2419
2423
|
intensityBuffer: {} as Buffer,
|
|
2420
2424
|
});
|
|
2421
|
-
expect(merged).toHaveLength(
|
|
2425
|
+
expect(merged).toHaveLength(9);
|
|
2422
2426
|
expect(merged[0]?.binding).toBe(0);
|
|
2423
2427
|
expect(merged[6]?.binding).toBe(6);
|
|
2428
|
+
expect(merged[7]?.binding).toBe(7);
|
|
2429
|
+
expect(merged[8]?.binding).toBe(8);
|
|
2424
2430
|
});
|
|
2425
2431
|
});
|
|
2426
2432
|
}
|
|
@@ -2515,7 +2521,7 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2515
2521
|
}
|
|
2516
2522
|
|
|
2517
2523
|
describe('t58 (M4 round-4) -- material BG fallback path', () => {
|
|
2518
|
-
it('(a) assemble with fallback returns
|
|
2524
|
+
it('(a) assemble with fallback returns 16 entries; binding 7..13 reference fallback resources', () => {
|
|
2519
2525
|
const materialEntries = makeMaterialBindGroupEntries();
|
|
2520
2526
|
const fallback = makeFallback();
|
|
2521
2527
|
const fallbackAsActive: SkylightBindGroupResources = {
|
|
@@ -2528,7 +2534,7 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2528
2534
|
intensityBuffer: fallback.intensityBuffer,
|
|
2529
2535
|
};
|
|
2530
2536
|
const merged = assembleMaterialWithSkylightEntries(materialEntries, fallbackAsActive);
|
|
2531
|
-
expect(merged).toHaveLength(
|
|
2537
|
+
expect(merged).toHaveLength(16);
|
|
2532
2538
|
expect(merged[7]?.binding).toBe(7);
|
|
2533
2539
|
expect((merged[7]?.resource as { value: unknown }).value).toBe(fallback.irradianceView);
|
|
2534
2540
|
expect((merged[8]?.resource as { value: unknown }).value).toBe(fallback.sampler);
|
|
@@ -2541,11 +2547,11 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2541
2547
|
);
|
|
2542
2548
|
});
|
|
2543
2549
|
|
|
2544
|
-
it('(b) assemble with active returns
|
|
2550
|
+
it('(b) assemble with active returns 16 entries; binding 7..13 reference active IblPipelineCache resources', () => {
|
|
2545
2551
|
const materialEntries = makeMaterialBindGroupEntries();
|
|
2546
2552
|
const active = makeActive();
|
|
2547
2553
|
const merged = assembleMaterialWithSkylightEntries(materialEntries, active);
|
|
2548
|
-
expect(merged).toHaveLength(
|
|
2554
|
+
expect(merged).toHaveLength(16);
|
|
2549
2555
|
expect((merged[7]?.resource as { value: unknown }).value).toBe(active.irradianceView);
|
|
2550
2556
|
expect((merged[8]?.resource as { value: unknown }).value).toBe(active.irradianceSampler);
|
|
2551
2557
|
expect((merged[9]?.resource as { value: unknown }).value).toBe(active.prefilterView);
|
|
@@ -2754,47 +2760,43 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2754
2760
|
expect(layout?.label).toBe('pbr-pl');
|
|
2755
2761
|
});
|
|
2756
2762
|
|
|
2757
|
-
it('(b) PBR material BGL entry count ===
|
|
2763
|
+
it('(b) PBR material BGL entry count === 26 (user region 0..16 + Skylight 17..23 + transmission 24..25)', () => {
|
|
2758
2764
|
const device = makeMockDevice();
|
|
2759
2765
|
// biome-ignore lint/suspicious/noExplicitAny: structural mock
|
|
2760
2766
|
buildPbrPipelineLayouts(device as any, STORAGE_CAPS);
|
|
2761
2767
|
const materialBgl = device.capturedBgls.find((b) => b.label === 'pbr-material-skylight-bgl');
|
|
2762
2768
|
expect(materialBgl).toBeDefined();
|
|
2763
|
-
expect(materialBgl?.entries).toHaveLength(
|
|
2769
|
+
expect(materialBgl?.entries).toHaveLength(26);
|
|
2764
2770
|
});
|
|
2765
2771
|
|
|
2766
|
-
it('(c) binding indices 0..
|
|
2772
|
+
it('(c) binding indices 0..25 in order; 17..23 resource types in D-5 round-4 order; 24..25 transmission', () => {
|
|
2767
2773
|
const device = makeMockDevice();
|
|
2768
2774
|
// biome-ignore lint/suspicious/noExplicitAny: structural mock
|
|
2769
2775
|
buildPbrPipelineLayouts(device as any, STORAGE_CAPS);
|
|
2770
2776
|
const materialBgl = device.capturedBgls.find((b) => b.label === 'pbr-material-skylight-bgl');
|
|
2771
2777
|
expect(materialBgl).toBeDefined();
|
|
2772
2778
|
const entries = materialBgl?.entries ?? [];
|
|
2773
|
-
// binding indices 0..
|
|
2774
|
-
for (let i = 0; i <
|
|
2779
|
+
// binding indices 0..25 in order
|
|
2780
|
+
for (let i = 0; i < 26; i++) {
|
|
2775
2781
|
expect(entries[i]?.binding).toBe(i);
|
|
2776
2782
|
}
|
|
2777
|
-
//
|
|
2778
|
-
expect((entries[
|
|
2783
|
+
// 17..23 resource types
|
|
2784
|
+
expect((entries[17] as { texture?: { viewDimension: string } }).texture?.viewDimension).toBe(
|
|
2779
2785
|
'cube',
|
|
2780
2786
|
);
|
|
2781
|
-
expect((entries[
|
|
2782
|
-
expect((entries[
|
|
2787
|
+
expect((entries[18] as { sampler?: { type: string } }).sampler?.type).toBe('filtering');
|
|
2788
|
+
expect((entries[19] as { texture?: { viewDimension: string } }).texture?.viewDimension).toBe(
|
|
2783
2789
|
'cube',
|
|
2784
2790
|
);
|
|
2785
|
-
expect((entries[16] as { sampler?: { type: string } }).sampler?.type).toBe('filtering');
|
|
2786
|
-
expect((entries[17] as { texture?: { viewDimension: string } }).texture?.viewDimension).toBe(
|
|
2787
|
-
'2d',
|
|
2788
|
-
);
|
|
2789
|
-
expect((entries[18] as { sampler?: { type: string } }).sampler?.type).toBe('filtering');
|
|
2790
|
-
expect((entries[19] as { buffer?: { type: string } }).buffer?.type).toBe('uniform');
|
|
2791
|
-
// 20..23 lightmap resource types
|
|
2792
2791
|
expect((entries[20] as { sampler?: { type: string } }).sampler?.type).toBe('filtering');
|
|
2793
2792
|
expect((entries[21] as { texture?: { viewDimension: string } }).texture?.viewDimension).toBe(
|
|
2794
2793
|
'2d',
|
|
2795
2794
|
);
|
|
2796
2795
|
expect((entries[22] as { sampler?: { type: string } }).sampler?.type).toBe('filtering');
|
|
2797
|
-
expect((entries[23] as {
|
|
2796
|
+
expect((entries[23] as { buffer?: { type: string } }).buffer?.type).toBe('uniform');
|
|
2797
|
+
// 24..25 transmission resource types
|
|
2798
|
+
expect((entries[24] as { sampler?: { type: string } }).sampler?.type).toBe('filtering');
|
|
2799
|
+
expect((entries[25] as { texture?: { viewDimension: string } }).texture?.viewDimension).toBe(
|
|
2798
2800
|
'2d',
|
|
2799
2801
|
);
|
|
2800
2802
|
});
|
|
@@ -2820,13 +2822,13 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2820
2822
|
// ─── (e)(f) Unlit material BGL shape + name ─────────────────────────────────
|
|
2821
2823
|
|
|
2822
2824
|
describe('t57 (M4 round-4) -- unlitPipeline material BGL shape', () => {
|
|
2823
|
-
it('(e) unlit material BGL entry count ===
|
|
2825
|
+
it('(e) unlit material BGL entry count === 17 (no Skylight binding contamination)', () => {
|
|
2824
2826
|
const device = makeMockDevice();
|
|
2825
2827
|
// biome-ignore lint/suspicious/noExplicitAny: structural mock
|
|
2826
2828
|
buildUnlitMaterialBgl(device as any);
|
|
2827
2829
|
const unlitBgl = device.capturedBgls.find((b) => b.label === 'unlit-material-bgl');
|
|
2828
2830
|
expect(unlitBgl).toBeDefined();
|
|
2829
|
-
expect(unlitBgl?.entries).toHaveLength(
|
|
2831
|
+
expect(unlitBgl?.entries).toHaveLength(17);
|
|
2830
2832
|
});
|
|
2831
2833
|
|
|
2832
2834
|
it("(f) PBR material BGL labelled 'pbr-material-skylight-bgl'; unlit labelled 'unlit-material-bgl'", () => {
|
|
@@ -2986,6 +2988,14 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
2986
2988
|
}
|
|
2987
2989
|
|
|
2988
2990
|
function buildManifestDataUrl(): string {
|
|
2991
|
+
const materialShaderStub = (identifier: string) => ({
|
|
2992
|
+
identifier,
|
|
2993
|
+
sourcePath: `${identifier}.wgsl`,
|
|
2994
|
+
composedWgsl: '/* stub */',
|
|
2995
|
+
paramSchema: '[]',
|
|
2996
|
+
variants:
|
|
2997
|
+
identifier === 'forgeax::default-standard-pbr' ? standardMaterialShaderVariants() : [],
|
|
2998
|
+
});
|
|
2989
2999
|
const manifest = {
|
|
2990
3000
|
schemaVersion: '1.0.0',
|
|
2991
3001
|
entries: [
|
|
@@ -3008,6 +3018,10 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
3008
3018
|
bindings: '',
|
|
3009
3019
|
},
|
|
3010
3020
|
],
|
|
3021
|
+
materialShaders: [
|
|
3022
|
+
materialShaderStub('forgeax::default-standard-pbr'),
|
|
3023
|
+
materialShaderStub('forgeax::default-unlit'),
|
|
3024
|
+
],
|
|
3011
3025
|
};
|
|
3012
3026
|
return `data:application/json,${encodeURIComponent(JSON.stringify(manifest))}`;
|
|
3013
3027
|
}
|
|
@@ -7292,7 +7306,8 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
7292
7306
|
sourcePath: `${identifier}.wgsl`,
|
|
7293
7307
|
composedWgsl,
|
|
7294
7308
|
paramSchema: '[]',
|
|
7295
|
-
variants:
|
|
7309
|
+
variants:
|
|
7310
|
+
identifier === 'forgeax::default-standard-pbr' ? standardMaterialShaderVariants() : [],
|
|
7296
7311
|
});
|
|
7297
7312
|
const manifest = {
|
|
7298
7313
|
schemaVersion: '1.0.0',
|
|
@@ -7608,7 +7623,8 @@ import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
|
7608
7623
|
sourcePath: `${identifier}.wgsl`,
|
|
7609
7624
|
composedWgsl: '/* stub */',
|
|
7610
7625
|
paramSchema: '[]',
|
|
7611
|
-
variants:
|
|
7626
|
+
variants:
|
|
7627
|
+
identifier === 'forgeax::default-standard-pbr' ? standardMaterialShaderVariants() : [],
|
|
7612
7628
|
});
|
|
7613
7629
|
const manifest = {
|
|
7614
7630
|
schemaVersion: '1.0.0',
|
|
@@ -8693,7 +8709,8 @@ type ExtractFramesWithPipeline = (
|
|
|
8693
8709
|
sourcePath: `${identifier}.wgsl`,
|
|
8694
8710
|
composedWgsl: '/* stub */',
|
|
8695
8711
|
paramSchema: '[]',
|
|
8696
|
-
variants:
|
|
8712
|
+
variants:
|
|
8713
|
+
identifier === 'forgeax::default-standard-pbr' ? standardMaterialShaderVariants() : [],
|
|
8697
8714
|
});
|
|
8698
8715
|
const entries: Array<{ hash: string; wgsl: string; glsl: string; bindings: string }> = [
|
|
8699
8716
|
{ hash: 'pbr00000', wgsl: '/* pbr stub - calls f_schlick( */', glsl: '', bindings: '' },
|
package/src/backend-selection.ts
CHANGED
|
@@ -11,6 +11,9 @@ export function loadRhiPack(
|
|
|
11
11
|
const createShaderModule = mod.createShaderModule as
|
|
12
12
|
| RhiBackendPack['createShaderModule']
|
|
13
13
|
| undefined;
|
|
14
|
+
const createShaderModuleImmediate = mod.createShaderModuleImmediate as
|
|
15
|
+
| RhiBackendPack['createShaderModuleImmediate']
|
|
16
|
+
| undefined;
|
|
14
17
|
const translateErrorEventToRhiError = mod.translateErrorEventToRhiError as
|
|
15
18
|
| RhiBackendPack['translateErrorEventToRhiError']
|
|
16
19
|
| undefined;
|
|
@@ -23,6 +26,7 @@ export function loadRhiPack(
|
|
|
23
26
|
const pack: RhiBackendPack = {
|
|
24
27
|
rhi,
|
|
25
28
|
...(createShaderModule === undefined ? {} : { createShaderModule }),
|
|
29
|
+
...(createShaderModuleImmediate === undefined ? {} : { createShaderModuleImmediate }),
|
|
26
30
|
...(translateErrorEventToRhiError === undefined ? {} : { translateErrorEventToRhiError }),
|
|
27
31
|
...(rawDeviceAccessor === undefined ? {} : { _internal_getRawDevice: rawDeviceAccessor }),
|
|
28
32
|
};
|
package/src/renderer-host.ts
CHANGED
|
@@ -4,7 +4,10 @@ import type {
|
|
|
4
4
|
BundlerOptions,
|
|
5
5
|
RendererHostAssembly,
|
|
6
6
|
} from '@forgeax/engine-render/internal/construct-renderer';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
constructRendererHost,
|
|
9
|
+
EngineEnvironmentError,
|
|
10
|
+
} from '@forgeax/engine-render/internal/construct-renderer';
|
|
8
11
|
import { loadBackendPack } from './backend-selection';
|
|
9
12
|
|
|
10
13
|
export type {
|
|
@@ -12,16 +15,40 @@ export type {
|
|
|
12
15
|
RendererHostAssembly,
|
|
13
16
|
} from '@forgeax/engine-render/internal/construct-renderer';
|
|
14
17
|
|
|
18
|
+
const FALLBACK_ERROR_CODES = new Set([
|
|
19
|
+
'adapter-unavailable',
|
|
20
|
+
'request-adapter-threw',
|
|
21
|
+
'feature-not-enabled',
|
|
22
|
+
'limit-exceeded',
|
|
23
|
+
'rhi-not-available',
|
|
24
|
+
'device-lost',
|
|
25
|
+
'oom',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
function canFallbackToWgpu(error: unknown): boolean {
|
|
29
|
+
if (!(error instanceof EngineEnvironmentError)) return false;
|
|
30
|
+
const webgpuError = error.detail.webgpuError;
|
|
31
|
+
if (webgpuError === undefined || typeof webgpuError !== 'object') return false;
|
|
32
|
+
if (!('code' in webgpuError)) return false;
|
|
33
|
+
const code = webgpuError.code;
|
|
34
|
+
return typeof code === 'string' && FALLBACK_ERROR_CODES.has(code);
|
|
35
|
+
}
|
|
36
|
+
|
|
15
37
|
/** Assemble renderer plus asset services in the Runtime-owned backend host. */
|
|
16
38
|
export async function constructRuntimeRendererHost(
|
|
17
39
|
canvas: unknown,
|
|
18
40
|
options?: RendererOptions,
|
|
19
41
|
bundler?: BundlerOptions,
|
|
20
|
-
): Promise<RenderResult<RendererHostAssembly, RenderError>> {
|
|
42
|
+
): Promise<RenderResult<RendererHostAssembly, RenderError | EngineEnvironmentError>> {
|
|
21
43
|
const first = await loadBackendPack(options);
|
|
22
44
|
if (!first.ok) throw first.error;
|
|
23
45
|
const constructed = await constructRendererHost(canvas, options, bundler, first.value);
|
|
24
|
-
if (
|
|
46
|
+
if (
|
|
47
|
+
constructed.ok ||
|
|
48
|
+
options?.rhi !== undefined ||
|
|
49
|
+
typeof globalThis === 'undefined' ||
|
|
50
|
+
!canFallbackToWgpu(constructed.error)
|
|
51
|
+
) {
|
|
25
52
|
return constructed;
|
|
26
53
|
}
|
|
27
54
|
const fallback = await loadBackendPack(options, true);
|