@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.
Files changed (49) hide show
  1. package/README.md +45 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/helpers/standard-material-manifest.d.ts +12 -0
  4. package/dist/__tests__/helpers/standard-material-manifest.d.ts.map +1 -0
  5. package/dist/__tests__/render-environment-consumer.integration.test.d.ts +2 -0
  6. package/dist/__tests__/render-environment-consumer.integration.test.d.ts.map +1 -0
  7. package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
  8. package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts.map +1 -1
  9. package/dist/__tests__/renderer-host-fallback.unit.test.d.ts +2 -0
  10. package/dist/__tests__/renderer-host-fallback.unit.test.d.ts.map +1 -0
  11. package/dist/__tests__/skinned-shadow-mixed.dawn.test.d.ts +2 -0
  12. package/dist/__tests__/skinned-shadow-mixed.dawn.test.d.ts.map +1 -0
  13. package/dist/backend-selection.d.ts.map +1 -1
  14. package/dist/index.mjs +20 -1
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/renderer-host.d.ts +2 -1
  17. package/dist/renderer-host.d.ts.map +1 -1
  18. package/dist/renderer-host.mjs +21 -2
  19. package/dist/renderer-host.mjs.map +1 -1
  20. package/package.json +30 -30
  21. package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +12 -11
  22. package/src/__tests__/errors.unit.test.ts +36 -0
  23. package/src/__tests__/extract-record-no-hardcoded-texture-fields.test.ts +4 -5
  24. package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +461 -18
  25. package/src/__tests__/geometry.unit.test.ts +3 -1
  26. package/src/__tests__/helpers/standard-material-manifest.ts +45 -0
  27. package/src/__tests__/material-texture-uv-scale.unit.test.ts +8 -14
  28. package/src/__tests__/materials.unit.test.ts +2 -0
  29. package/src/__tests__/pbr-pipeline.unit.test.ts +8 -8
  30. package/src/__tests__/pipeline-cache-keying.unit.test.ts +6 -4
  31. package/src/__tests__/pipeline.unit.test.ts +5 -2
  32. package/src/__tests__/render-environment-consumer.integration.test.ts +33 -0
  33. package/src/__tests__/render-error-exhaustive.test-d.ts +57 -0
  34. package/src/__tests__/render-feature-prepared-graphics.fixture.ts +10 -0
  35. package/src/__tests__/render-system-mega.test.ts +3 -1
  36. package/src/__tests__/render-system-record-multi-material-textureview.test.ts +3 -1
  37. package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +3 -1
  38. package/src/__tests__/render-system-record.test.ts +8 -6
  39. package/src/__tests__/renderer-host-fallback.unit.test.ts +112 -0
  40. package/src/__tests__/renderer-lifecycle.integration.test.ts +10 -0
  41. package/src/__tests__/renderer-surface.unit.test.ts +66 -0
  42. package/src/__tests__/shadow-csm-cascade-loadop.test.ts +5 -1
  43. package/src/__tests__/shadow-csm-tile-consistency.test.ts +5 -1
  44. package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +383 -0
  45. package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +7 -7
  46. package/src/__tests__/ssao-passes.test.ts +28 -12
  47. package/src/__tests__/systems.unit.test.ts +48 -31
  48. package/src/backend-selection.ts +4 -0
  49. package/src/renderer-host.ts +30 -3
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/backend-selection.ts","../src/renderer-host.ts"],"names":[],"mappings":"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","file":"renderer-host.mjs","sourcesContent":["import type { RendererOptions } from '@forgeax/engine-render';\nimport type { RhiBackendPack } from '@forgeax/engine-render/internal/construct-renderer';\nimport { err, ok, RhiError } from '@forgeax/engine-rhi';\nimport * as rhiWebgpu from '@forgeax/engine-rhi-webgpu';\n\nexport function loadRhiPack(\n mod: Record<string, unknown>,\n instrumentation?: RhiBackendPack['instrumentation'],\n): RhiBackendPack {\n const rhi = mod.rhi as unknown as RhiBackendPack['rhi'];\n const createShaderModule = mod.createShaderModule as\n | RhiBackendPack['createShaderModule']\n | undefined;\n const translateErrorEventToRhiError = mod.translateErrorEventToRhiError as\n | RhiBackendPack['translateErrorEventToRhiError']\n | undefined;\n const rawDeviceAccessor = mod._internal_getRawDevice as unknown as\n | RhiBackendPack['_internal_getRawDevice']\n | undefined;\n const backendInstrumentation = mod.instrumentation as\n | RhiBackendPack['instrumentation']\n | undefined;\n const pack: RhiBackendPack = {\n rhi,\n ...(createShaderModule === undefined ? {} : { createShaderModule }),\n ...(translateErrorEventToRhiError === undefined ? {} : { translateErrorEventToRhiError }),\n ...(rawDeviceAccessor === undefined ? {} : { _internal_getRawDevice: rawDeviceAccessor }),\n };\n const resolvedInstrumentation = instrumentation ?? backendInstrumentation;\n return resolvedInstrumentation === undefined\n ? pack\n : { ...pack, instrumentation: resolvedInstrumentation };\n}\n\n/** Selects one backend pack; render receives only this typed owner contract. */\nexport async function loadBackendPack(\n options: RendererOptions | undefined,\n preferWgpu = false,\n): Promise<\n | { readonly ok: true; readonly value: RhiBackendPack }\n | { readonly ok: false; readonly error: RhiError }\n> {\n const explicit = options?.rhi;\n if (explicit !== undefined && explicit !== null) {\n return ok(loadRhiPack({ rhi: explicit, ...(explicit as object) }, options?.rhiInstrumentation));\n }\n const nav =\n typeof globalThis === 'undefined'\n ? undefined\n : (globalThis as { navigator?: { gpu?: unknown } }).navigator;\n if (!preferWgpu && nav?.gpu !== undefined && nav.gpu !== null) {\n return ok(\n loadRhiPack(rhiWebgpu as unknown as Record<string, unknown>, options?.rhiInstrumentation),\n );\n }\n try {\n const mod = (await import('@forgeax/engine-rhi-wgpu')) as Record<string, unknown>;\n await (mod.ensureReady as () => Promise<unknown>)();\n return ok(loadRhiPack(mod, options?.rhiInstrumentation));\n } catch (cause) {\n return err(\n new RhiError({\n code: 'rhi-not-available',\n expected: 'a usable RHI backend is available',\n hint: `failed to load wgpu backend: ${String(cause)}`,\n }),\n );\n }\n}\n","import type { AssetRegistry } from '@forgeax/engine-assets-runtime';\nimport type { RenderError, RendererOptions, RenderResult } from '@forgeax/engine-render';\nimport type {\n BundlerOptions,\n RendererHostAssembly,\n} from '@forgeax/engine-render/internal/construct-renderer';\nimport { constructRendererHost } from '@forgeax/engine-render/internal/construct-renderer';\nimport { loadBackendPack } from './backend-selection';\n\nexport type {\n BundlerOptions,\n RendererHostAssembly,\n} from '@forgeax/engine-render/internal/construct-renderer';\n\n/** Assemble renderer plus asset services in the Runtime-owned backend host. */\nexport async function constructRuntimeRendererHost(\n canvas: unknown,\n options?: RendererOptions,\n bundler?: BundlerOptions,\n): Promise<RenderResult<RendererHostAssembly, RenderError>> {\n const first = await loadBackendPack(options);\n if (!first.ok) throw first.error;\n const constructed = await constructRendererHost(canvas, options, bundler, first.value);\n if (constructed.ok || options?.rhi !== undefined || typeof globalThis === 'undefined') {\n return constructed;\n }\n const fallback = await loadBackendPack(options, true);\n if (!fallback.ok) return constructed;\n return constructRendererHost(canvas, options, bundler, fallback.value);\n}\n\nexport { loadRhiPack } from './backend-selection';\nexport type { AssetRegistry };\n"]}
1
+ {"version":3,"sources":["../src/backend-selection.ts","../src/renderer-host.ts"],"names":[],"mappings":"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","file":"renderer-host.mjs","sourcesContent":["import type { RendererOptions } from '@forgeax/engine-render';\nimport type { RhiBackendPack } from '@forgeax/engine-render/internal/construct-renderer';\nimport { err, ok, RhiError } from '@forgeax/engine-rhi';\nimport * as rhiWebgpu from '@forgeax/engine-rhi-webgpu';\n\nexport function loadRhiPack(\n mod: Record<string, unknown>,\n instrumentation?: RhiBackendPack['instrumentation'],\n): RhiBackendPack {\n const rhi = mod.rhi as unknown as RhiBackendPack['rhi'];\n const createShaderModule = mod.createShaderModule as\n | RhiBackendPack['createShaderModule']\n | undefined;\n const createShaderModuleImmediate = mod.createShaderModuleImmediate as\n | RhiBackendPack['createShaderModuleImmediate']\n | undefined;\n const translateErrorEventToRhiError = mod.translateErrorEventToRhiError as\n | RhiBackendPack['translateErrorEventToRhiError']\n | undefined;\n const rawDeviceAccessor = mod._internal_getRawDevice as unknown as\n | RhiBackendPack['_internal_getRawDevice']\n | undefined;\n const backendInstrumentation = mod.instrumentation as\n | RhiBackendPack['instrumentation']\n | undefined;\n const pack: RhiBackendPack = {\n rhi,\n ...(createShaderModule === undefined ? {} : { createShaderModule }),\n ...(createShaderModuleImmediate === undefined ? {} : { createShaderModuleImmediate }),\n ...(translateErrorEventToRhiError === undefined ? {} : { translateErrorEventToRhiError }),\n ...(rawDeviceAccessor === undefined ? {} : { _internal_getRawDevice: rawDeviceAccessor }),\n };\n const resolvedInstrumentation = instrumentation ?? backendInstrumentation;\n return resolvedInstrumentation === undefined\n ? pack\n : { ...pack, instrumentation: resolvedInstrumentation };\n}\n\n/** Selects one backend pack; render receives only this typed owner contract. */\nexport async function loadBackendPack(\n options: RendererOptions | undefined,\n preferWgpu = false,\n): Promise<\n | { readonly ok: true; readonly value: RhiBackendPack }\n | { readonly ok: false; readonly error: RhiError }\n> {\n const explicit = options?.rhi;\n if (explicit !== undefined && explicit !== null) {\n return ok(loadRhiPack({ rhi: explicit, ...(explicit as object) }, options?.rhiInstrumentation));\n }\n const nav =\n typeof globalThis === 'undefined'\n ? undefined\n : (globalThis as { navigator?: { gpu?: unknown } }).navigator;\n if (!preferWgpu && nav?.gpu !== undefined && nav.gpu !== null) {\n return ok(\n loadRhiPack(rhiWebgpu as unknown as Record<string, unknown>, options?.rhiInstrumentation),\n );\n }\n try {\n const mod = (await import('@forgeax/engine-rhi-wgpu')) as Record<string, unknown>;\n await (mod.ensureReady as () => Promise<unknown>)();\n return ok(loadRhiPack(mod, options?.rhiInstrumentation));\n } catch (cause) {\n return err(\n new RhiError({\n code: 'rhi-not-available',\n expected: 'a usable RHI backend is available',\n hint: `failed to load wgpu backend: ${String(cause)}`,\n }),\n );\n }\n}\n","import type { AssetRegistry } from '@forgeax/engine-assets-runtime';\nimport type { RenderError, RendererOptions, RenderResult } from '@forgeax/engine-render';\nimport type {\n BundlerOptions,\n RendererHostAssembly,\n} from '@forgeax/engine-render/internal/construct-renderer';\nimport {\n constructRendererHost,\n EngineEnvironmentError,\n} from '@forgeax/engine-render/internal/construct-renderer';\nimport { loadBackendPack } from './backend-selection';\n\nexport type {\n BundlerOptions,\n RendererHostAssembly,\n} from '@forgeax/engine-render/internal/construct-renderer';\n\nconst FALLBACK_ERROR_CODES = new Set([\n 'adapter-unavailable',\n 'request-adapter-threw',\n 'feature-not-enabled',\n 'limit-exceeded',\n 'rhi-not-available',\n 'device-lost',\n 'oom',\n]);\n\nfunction canFallbackToWgpu(error: unknown): boolean {\n if (!(error instanceof EngineEnvironmentError)) return false;\n const webgpuError = error.detail.webgpuError;\n if (webgpuError === undefined || typeof webgpuError !== 'object') return false;\n if (!('code' in webgpuError)) return false;\n const code = webgpuError.code;\n return typeof code === 'string' && FALLBACK_ERROR_CODES.has(code);\n}\n\n/** Assemble renderer plus asset services in the Runtime-owned backend host. */\nexport async function constructRuntimeRendererHost(\n canvas: unknown,\n options?: RendererOptions,\n bundler?: BundlerOptions,\n): Promise<RenderResult<RendererHostAssembly, RenderError | EngineEnvironmentError>> {\n const first = await loadBackendPack(options);\n if (!first.ok) throw first.error;\n const constructed = await constructRendererHost(canvas, options, bundler, first.value);\n if (\n constructed.ok ||\n options?.rhi !== undefined ||\n typeof globalThis === 'undefined' ||\n !canFallbackToWgpu(constructed.error)\n ) {\n return constructed;\n }\n const fallback = await loadBackendPack(options, true);\n if (!fallback.ok) return constructed;\n return constructRendererHost(canvas, options, bundler, fallback.value);\n}\n\nexport { loadRhiPack } from './backend-selection';\nexport type { AssetRegistry };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/engine-runtime",
3
- "version": "0.1.7",
3
+ "version": "0.1.19",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -26,37 +26,37 @@
26
26
  "LICENSE"
27
27
  ],
28
28
  "dependencies": {
29
- "@forgeax/engine-animation": "0.1.7",
30
- "@forgeax/engine-assets-runtime": "0.1.7",
31
- "@forgeax/engine-audio-webaudio": "0.1.7",
32
- "@forgeax/engine-codec": "0.1.7",
33
- "@forgeax/engine-debug-draw": "0.1.7",
34
- "@forgeax/engine-ecs": "0.1.7",
35
- "@forgeax/engine-font": "0.1.7",
36
- "@forgeax/engine-geometry": "0.1.7",
37
- "@forgeax/engine-graphics-extras": "0.1.7",
38
- "@forgeax/engine-image": "0.1.7",
39
- "@forgeax/engine-input": "0.1.7",
40
- "@forgeax/engine-math": "0.1.7",
41
- "@forgeax/engine-pack": "0.1.7",
42
- "@forgeax/engine-profiler": "0.1.7",
43
- "@forgeax/engine-render": "0.1.7",
44
- "@forgeax/engine-render-graph": "0.1.7",
45
- "@forgeax/engine-rhi": "0.1.7",
46
- "@forgeax/engine-rhi-null": "0.1.7",
47
- "@forgeax/engine-rhi-webgpu": "0.1.7",
48
- "@forgeax/engine-rhi-wgpu": "0.1.7",
49
- "@forgeax/engine-scene": "0.1.7",
50
- "@forgeax/engine-shader": "0.1.7",
51
- "@forgeax/engine-skinning": "0.1.7",
52
- "@forgeax/engine-types": "0.1.7",
53
- "@forgeax/engine-vfx": "0.1.7"
29
+ "@forgeax/engine-animation": "0.1.19",
30
+ "@forgeax/engine-assets-runtime": "0.1.19",
31
+ "@forgeax/engine-audio-webaudio": "0.1.19",
32
+ "@forgeax/engine-codec": "0.1.19",
33
+ "@forgeax/engine-debug-draw": "0.1.19",
34
+ "@forgeax/engine-ecs": "0.1.19",
35
+ "@forgeax/engine-font": "0.1.19",
36
+ "@forgeax/engine-geometry": "0.1.19",
37
+ "@forgeax/engine-graphics-extras": "0.1.19",
38
+ "@forgeax/engine-image": "0.1.19",
39
+ "@forgeax/engine-input": "0.1.19",
40
+ "@forgeax/engine-math": "0.1.19",
41
+ "@forgeax/engine-pack": "0.1.19",
42
+ "@forgeax/engine-profiler": "0.1.19",
43
+ "@forgeax/engine-render": "0.1.19",
44
+ "@forgeax/engine-render-graph": "0.1.19",
45
+ "@forgeax/engine-rhi": "0.1.19",
46
+ "@forgeax/engine-rhi-null": "0.1.19",
47
+ "@forgeax/engine-rhi-webgpu": "0.1.19",
48
+ "@forgeax/engine-rhi-wgpu": "0.1.19",
49
+ "@forgeax/engine-scene": "0.1.19",
50
+ "@forgeax/engine-shader": "0.1.19",
51
+ "@forgeax/engine-skinning": "0.1.19",
52
+ "@forgeax/engine-types": "0.1.19",
53
+ "@forgeax/engine-vfx": "0.1.19"
54
54
  },
55
55
  "devDependencies": {
56
- "@forgeax/engine-import": "0.1.7",
57
- "@forgeax/engine-naga": "0.1.7",
58
- "@forgeax/engine-state": "0.1.7",
59
- "@forgeax/engine-vite-plugin-shader": "0.1.7",
56
+ "@forgeax/engine-import": "0.1.19",
57
+ "@forgeax/engine-naga": "0.1.19",
58
+ "@forgeax/engine-state": "0.1.19",
59
+ "@forgeax/engine-vite-plugin-shader": "0.1.19",
60
60
  "@types/node": "^20.14.0"
61
61
  },
62
62
  "forgeax": {
@@ -10,11 +10,8 @@
10
10
  // AC-01: PBR instances=N vs instances=1 frames differ (diffCount>0).
11
11
  // AC-06: single entity + N instance reads meshes[0] (not meshes[instance_index]).
12
12
  //
13
- // Red phase (current): the shader reads meshes[instance_index].worldFromLocal
14
- // -- all N copies collapse to entity origin. The two frames are identical
15
- // (diffCount===0) because instance=1 at origin and instances=N all at origin
16
- // produce the same pixel pattern. GREEN after w4 lands (PBR vs_main reads
17
- // meshes[0] + instances[idx]).
13
+ // The PBR vertex path must combine the entity transform from meshes[0] with
14
+ // the per-instance transform selected by instance_index.
18
15
  //
19
16
  // Dual-state methodology: render to two textures (instances=N vs instances=1),
20
17
  // read back both, count pixels that differ by more than epsilon. The
@@ -52,6 +49,11 @@ const INSTANCE_GRID_Y = 3;
52
49
  const INSTANCE_GRID_Z = 2;
53
50
  const INSTANCE_COUNT = INSTANCE_GRID_X * INSTANCE_GRID_Y * INSTANCE_GRID_Z; // 30
54
51
  const SPACING = 2.5;
52
+ // Windows Dawn first-touch attempts have reached about 80 seconds for this
53
+ // two-renderer PBR/readback probe. Keep the test bounded, but above the
54
+ // project-wide 30-second budget so retries do not turn a slow valid probe into
55
+ // a cancelled nightly job.
56
+ const PBR_DAWN_TEST_TIMEOUT_MS = 120_000;
55
57
 
56
58
  function buildTranslationGrid(): Float32Array {
57
59
  const out = new Float32Array(INSTANCE_COUNT * 16);
@@ -85,8 +87,10 @@ const ENGINE_MANIFEST_URL = `data:application/json,${encodeURIComponent(
85
87
  JSON.stringify(ENGINE_MANIFEST),
86
88
  )}`;
87
89
 
88
- describe('w2 -- PBR dual-state dawn smoke (AC-01 / AC-06, RED before w4)', () => {
89
- it('instances=N vs instances=1: frames differ in pixel diff count (currently RED)', async () => {
90
+ describe('w2 -- PBR dual-state dawn smoke (AC-01 / AC-06)', () => {
91
+ it('instances=N vs instances=1: frames differ in pixel diff count', {
92
+ timeout: PBR_DAWN_TEST_TIMEOUT_MS,
93
+ }, async () => {
90
94
  const dawnAvailable = typeof globalThis.navigator?.gpu?.requestAdapter === 'function';
91
95
  if (!dawnAvailable) {
92
96
  throw new Error('dawn-node navigator.gpu not injected; vitest.setup-webgpu.ts regressed');
@@ -366,9 +370,6 @@ describe('w2 -- PBR dual-state dawn smoke (AC-01 / AC-06, RED before w4)', () =>
366
370
 
367
371
  // AC-01: diffCount>0 -- the two frames must differ (instances=N spreads
368
372
  // to distinct positions, instances=1 renders at entity origin only).
369
- // RED before w4: all N copies collapse to entity origin, so both frames
370
- // render the same cube at the same position -> diffCount===0.
371
- // Currently RED: diffCount===0 because shader doesn't read @group(3)
372
373
  expect(diffCount).toBeGreaterThan(0);
373
374
  // Also verify the N-instance frame has substantial non-clear pixels
374
375
  // (proves instances rendered, not just an empty frame)
@@ -377,7 +378,7 @@ describe('w2 -- PBR dual-state dawn smoke (AC-01 / AC-06, RED before w4)', () =>
377
378
  });
378
379
 
379
380
  // w5 -- unlit dual-state: N unlit instances at distinct NDC positions (AC-02)
380
- it('unlit Instances=N vs instances=1: frames differ (RED before w5)', async () => {
381
+ it('unlit Instances=N vs instances=1: frames differ', async () => {
381
382
  const dawnAvailable_2 = typeof globalThis.navigator?.gpu?.requestAdapter === 'function';
382
383
  if (!dawnAvailable_2) {
383
384
  throw new Error('dawn-node navigator.gpu not injected');
@@ -418,14 +418,30 @@ type RuntimeLayerError = RenderError | AssetRuntimeError | SkinError;
418
418
  return 'recovery failed';
419
419
  case 'cleanup-failed':
420
420
  return 'cleanup failed';
421
+ case 'scene-data-unavailable':
422
+ return 'scene data unavailable';
421
423
  case 'frame-receipt-stale':
422
424
  return 'frame receipt stale';
423
425
  case 'renderer-contract-failed':
424
426
  return 'renderer contract failed';
425
427
  case 'observation-unavailable':
426
428
  return 'observation unavailable';
429
+ case 'taa-unavailable':
430
+ return 'taa unavailable';
427
431
  case 'shadow-invalid-config':
428
432
  return 'shadow invalid config';
433
+ case 'environment-source-conflict':
434
+ return 'environment source conflict';
435
+ case 'fog-cardinality':
436
+ return 'fog cardinality';
437
+ case 'taa-caps-insufficient':
438
+ return 'taa caps insufficient';
439
+ case 'environment-generation-failed':
440
+ return 'environment generation failed';
441
+ case 'atmosphere-invalid-parameter':
442
+ return 'atmosphere invalid parameter';
443
+ case 'owner-stage-failed':
444
+ return 'owner stage failed';
429
445
  case 'skin-joint-count-exceeded':
430
446
  return 'skin joint count exceeded';
431
447
  case 'skin-joint-despawned':
@@ -512,6 +528,8 @@ type RuntimeLayerError = RenderError | AssetRuntimeError | SkinError;
512
528
  return 'points lines budget exceeded';
513
529
  case 'points-lines-prepare-failed':
514
530
  return 'points lines prepare failed';
531
+ case 'transmission-capability-missing':
532
+ return 'transmission capability missing';
515
533
  }
516
534
  }
517
535
  expect(exhaustive('equirect-projection-failed')).toBe('equirect projection failed');
@@ -562,14 +580,30 @@ type RuntimeLayerError = RenderError | AssetRuntimeError | SkinError;
562
580
  return 'ok';
563
581
  case 'cleanup-failed':
564
582
  return 'ok';
583
+ case 'scene-data-unavailable':
584
+ return 'ok';
565
585
  case 'frame-receipt-stale':
566
586
  return 'ok';
567
587
  case 'renderer-contract-failed':
568
588
  return 'ok';
569
589
  case 'observation-unavailable':
570
590
  return 'ok';
591
+ case 'taa-unavailable':
592
+ return 'ok';
571
593
  case 'shadow-invalid-config':
572
594
  return 'ok';
595
+ case 'environment-source-conflict':
596
+ return 'ok';
597
+ case 'fog-cardinality':
598
+ return 'ok';
599
+ case 'taa-caps-insufficient':
600
+ return 'ok';
601
+ case 'environment-generation-failed':
602
+ return 'ok';
603
+ case 'atmosphere-invalid-parameter':
604
+ return 'ok';
605
+ case 'owner-stage-failed':
606
+ return 'ok';
573
607
  case 'skin-joint-count-exceeded':
574
608
  return 'ok';
575
609
  case 'skin-joint-despawned':
@@ -644,6 +678,8 @@ type RuntimeLayerError = RenderError | AssetRuntimeError | SkinError;
644
678
  return 'ok';
645
679
  case 'points-lines-prepare-failed':
646
680
  return 'ok';
681
+ case 'transmission-capability-missing':
682
+ return 'ok';
647
683
  }
648
684
  }
649
685
  expect(exhaustive('hdrp-deferred-caps-insufficient')).toBe('ok');
@@ -12,16 +12,15 @@
12
12
  // not actually happen — the 4th texture (heightTexture) would silently fall
13
13
  // through.
14
14
  //
15
- // Scope of the gate — the three USER-REGION texture fields only:
15
+ // Scope of the gate — the three user-authored base texture fields:
16
16
  // baseColorTexture / metallicRoughnessTexture / normalTexture
17
17
  // These are the fields `derive(default-standard-pbr).textureFieldNames`
18
18
  // produces and that w7/w8 route through iteration.
19
19
  //
20
20
  // Deliberately NOT gated (legitimate residual naming):
21
- // - emissiveTexture / occlusionTexture: these live in the engine-injection
22
- // LIGHTMAP region (appendInjection 'lightmap'), are NOT in any paramSchema
23
- // textureFieldNames set, and are assembled by the injection path — naming
24
- // them there is the engine-managed region, not user-region hardcoding.
21
+ // - emissiveTexture / occlusionTexture: legacy engine-owned snapshot fields
22
+ // still have named reads, while the production Standard BGL no longer adds
23
+ // a duplicate lightmap region.
25
24
  // - USER_REGION_TEXTURE_FIELDS in derive-paramschema.ts: the 3-field
26
25
  // register-time under-declaration validator (charter P3 safety net from
27
26
  // the 4.3 blending regression). Orthogonal to binding; lives in a