@forgeax/engine-runtime 0.1.4 → 0.1.6
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 +49 -83
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-serialize.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-serialize.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts +2 -0
- package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts.map +1 -0
- package/dist/__tests__/asset-error-exhaustive.test-d.d.ts +11 -0
- package/dist/__tests__/asset-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-guid-reverse.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-guid-reverse.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mounts.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mounts.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.browser.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.browser.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.dawn.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.listCatalog.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.listCatalog.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.mesh-bin.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.mesh-bin.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.recursive.spec.d.ts +2 -0
- package/dist/__tests__/asset-registry.recursive.spec.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset.unit.test.d.ts +2 -0
- package/dist/__tests__/asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/audio-load-by-guid.unit.test.d.ts +2 -0
- package/dist/__tests__/audio-load-by-guid.unit.test.d.ts.map +1 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts +2 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts.map +1 -0
- package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts +2 -0
- package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-asset-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/builtin-asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/collect-scene-asset.test.d.ts +1 -2
- package/dist/__tests__/collect-scene-asset.test.d.ts.map +1 -1
- package/dist/__tests__/collect-scene-mount-collapse.test.d.ts +2 -0
- package/dist/__tests__/collect-scene-mount-collapse.test.d.ts.map +1 -0
- package/dist/__tests__/collect-transient-roundtrip.test.d.ts +1 -1
- package/dist/__tests__/collect-transient-roundtrip.test.d.ts.map +1 -1
- package/dist/__tests__/collect-transient-skip.test.d.ts +1 -2
- package/dist/__tests__/collect-transient-skip.test.d.ts.map +1 -1
- package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts +2 -0
- package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/draw-published.d.ts +2 -2
- package/dist/__tests__/draw-published.d.ts.map +1 -1
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts +2 -0
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts +2 -0
- package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fetch-binary-decompress.test.d.ts +2 -0
- package/dist/__tests__/fetch-binary-decompress.test.d.ts.map +1 -0
- package/dist/__tests__/four-path-compression.integration.test.d.ts +2 -0
- package/dist/__tests__/four-path-compression.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/guid-for-asset.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-for-asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/handle-migration.test.d.ts +2 -0
- package/dist/__tests__/handle-migration.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/register-runtime-components.d.ts.map +1 -1
- package/dist/__tests__/host-custom-kind-contract.test-d.d.ts +2 -0
- package/dist/__tests__/host-custom-kind-contract.test-d.d.ts.map +1 -0
- package/dist/__tests__/host-custom-kind-contract.test.d.ts +2 -0
- package/dist/__tests__/host-custom-kind-contract.test.d.ts.map +1 -0
- package/dist/__tests__/inspect-name.test.d.ts +2 -0
- package/dist/__tests__/inspect-name.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts +2 -0
- package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts.map +1 -0
- package/dist/__tests__/loadbyguid-payload.unit.test.d.ts +2 -0
- package/dist/__tests__/loadbyguid-payload.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts +2 -0
- package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-extract-graceful.test.d.ts +2 -0
- package/dist/__tests__/loader-extract-graceful.test.d.ts.map +1 -0
- package/dist/__tests__/loader-name-survival.test.d.ts +2 -0
- package/dist/__tests__/loader-name-survival.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/new-kind-refs-contract.test.d.ts +2 -0
- package/dist/__tests__/new-kind-refs-contract.test.d.ts.map +1 -0
- package/dist/__tests__/package-promotion.test.d.ts +2 -0
- package/dist/__tests__/package-promotion.test.d.ts.map +1 -0
- package/dist/__tests__/package-rename.test.d.ts +2 -0
- package/dist/__tests__/package-rename.test.d.ts.map +1 -0
- package/dist/__tests__/recover-error-exhaustive.test-d.d.ts +1 -1
- package/dist/__tests__/recover-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts +1 -1
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts +10 -0
- package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts.map +1 -0
- package/dist/__tests__/render-system-extract.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-extract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-test-utils.d.ts +10 -0
- package/dist/__tests__/renderer-test-utils.d.ts.map +1 -0
- package/dist/__tests__/renderer.test-d.d.ts +2 -0
- package/dist/__tests__/renderer.test-d.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-handle-err.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-handle-err.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-fallback.test.d.ts +2 -0
- package/dist/__tests__/resolveName-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-null-package.test.d.ts +2 -0
- package/dist/__tests__/resolveName-null-package.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-unified-entry.test.d.ts +2 -0
- package/dist/__tests__/resolveName-unified-entry.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName.test.d.ts +2 -0
- package/dist/__tests__/resolveName.test.d.ts.map +1 -0
- package/dist/__tests__/roots-to-scene-asset.test.d.ts +1 -1
- package/dist/__tests__/roots-to-scene-asset.test.d.ts.map +1 -1
- package/dist/__tests__/roundtrip-primitive.test.d.ts +2 -0
- package/dist/__tests__/roundtrip-primitive.test.d.ts.map +1 -0
- package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts +2 -0
- package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts.map +1 -0
- package/dist/__tests__/scene-instance-authoring.integration.test.d.ts +2 -0
- package/dist/__tests__/scene-instance-authoring.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-fixed-point.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-fixed-point.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/scene-sugar-instantiate.test.d.ts +2 -0
- package/dist/__tests__/scene-sugar-instantiate.test.d.ts.map +1 -0
- package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/serialize-scene-asset.test.d.ts +1 -1
- package/dist/__tests__/serialize-scene-asset.test.d.ts.map +1 -1
- package/dist/__tests__/template-game-default-api-contract.test.d.ts +2 -0
- package/dist/__tests__/template-game-default-api-contract.test.d.ts.map +1 -0
- package/dist/__tests__/tileset-asset-validate.test.d.ts +2 -0
- package/dist/__tests__/tileset-asset-validate.test.d.ts.map +1 -0
- package/dist/__tests__/transcode-fallback.unit.test.d.ts +2 -0
- package/dist/__tests__/transcode-fallback.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transient-runtime-behavior.test.d.ts +2 -0
- package/dist/__tests__/transient-runtime-behavior.test.d.ts.map +1 -0
- package/dist/__tests__/video-loader-consumption.test.d.ts +2 -0
- package/dist/__tests__/video-loader-consumption.test.d.ts.map +1 -0
- package/dist/__tests__/video-loader-smoke.dawn.test.d.ts +2 -0
- package/dist/__tests__/video-loader-smoke.dawn.test.d.ts.map +1 -0
- package/dist/backend-selection.d.ts +13 -0
- package/dist/backend-selection.d.ts.map +1 -0
- package/dist/collect-scene-asset.d.ts +3 -3
- package/dist/collect-scene-asset.d.ts.map +1 -1
- package/dist/createRenderer.d.ts +14 -2
- package/dist/createRenderer.d.ts.map +1 -1
- package/dist/errors/environment.d.ts +1 -1
- package/dist/errors/environment.d.ts.map +1 -1
- package/dist/index.d.ts +19 -81
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +105 -108
- package/dist/index.mjs.map +1 -1
- package/dist/renderer-host.d.ts +9 -0
- package/dist/renderer-host.d.ts.map +1 -0
- package/dist/renderer-host.mjs +62 -0
- package/dist/renderer-host.mjs.map +1 -0
- package/dist/scene-instances/index.d.ts +2 -0
- package/dist/scene-instances/index.d.ts.map +1 -0
- package/dist/scene-utils/mount-override-fold.d.ts +1 -2
- package/dist/scene-utils/mount-override-fold.d.ts.map +1 -1
- package/dist/systems/sprite-animation-tick.d.ts +1 -1
- package/dist/systems/sprite-animation-tick.d.ts.map +1 -1
- package/dist/systems/transparent-sort.d.ts +1 -2
- package/dist/systems/transparent-sort.d.ts.map +1 -1
- package/dist/tilemap-register-validate.d.ts.map +1 -1
- package/package.json +34 -33
- package/src/__tests__/active-camera.unit.test.ts +19 -9
- package/src/__tests__/animation-graph-collect-roundtrip.test.ts +213 -0
- package/src/__tests__/animation-graph-serialize.unit.test.ts +171 -0
- package/src/__tests__/append-injection.test.ts +1 -1
- package/src/__tests__/asset-brand-and-name-retirement.test.ts +102 -0
- package/src/__tests__/asset-error-exhaustive.test-d.ts +62 -0
- package/src/__tests__/asset-registry-guid-reverse.test.ts +575 -0
- package/src/__tests__/asset-registry-hdr-equirect.test.ts +78 -0
- package/src/__tests__/asset-registry-mesh-pack-payload.dawn.test.ts +232 -0
- package/src/__tests__/asset-registry-mounts.test.ts +806 -0
- package/src/__tests__/asset-registry.browser.test.ts +81 -0
- package/src/__tests__/asset-registry.dawn.test.ts +94 -0
- package/src/__tests__/asset-registry.listCatalog.test.ts +170 -0
- package/src/__tests__/asset-registry.mesh-bin.test.ts +316 -0
- package/src/__tests__/asset-registry.pack-cache.test.ts +969 -0
- package/src/__tests__/asset-registry.raw-container-failfast.test.ts +225 -0
- package/src/__tests__/asset-registry.recursive.spec.ts +1397 -0
- package/src/__tests__/asset-registry.unit.test.ts +1030 -0
- package/src/__tests__/asset.unit.test.ts +7107 -0
- package/src/__tests__/audio-load-by-guid.unit.test.ts +126 -0
- package/src/__tests__/backend-selection.integration.test.ts +11 -0
- package/src/__tests__/basis-catalog-dispatch.integration.test.ts +320 -0
- package/src/__tests__/bind-group-cache-keying.test.ts +4 -4
- package/src/__tests__/block-upload-math.unit.test.ts +2 -2
- package/src/__tests__/builtin-asset-registry.unit.test.ts +112 -0
- package/src/__tests__/builtin-shader-register-e2e.test.ts +1 -1
- package/src/__tests__/cache-false-eviction.test.ts +2 -1
- package/src/__tests__/cache-isolation-matrix.test.ts +2 -1
- package/src/__tests__/camera-clear-snapshot-equivalence.unit.test.ts +5 -6
- package/src/__tests__/clamp-to-last.e2e.browser.test.ts +29 -19
- package/src/__tests__/collect-scene-asset.test.ts +49 -52
- package/src/__tests__/collect-scene-mount-collapse.test.ts +391 -0
- package/src/__tests__/collect-subtree.unit.test.ts +1 -2
- package/src/__tests__/collect-transient-children-absent.test.ts +24 -10
- package/src/__tests__/collect-transient-roundtrip.test.ts +44 -20
- package/src/__tests__/collect-transient-skip.test.ts +24 -13
- package/src/__tests__/components.test-d.ts +1 -6
- package/src/__tests__/components.unit.test.ts +20 -19
- package/src/__tests__/composite-skybox-cross-world.browser.test.ts +59 -27
- package/src/__tests__/compressed-texture-physical-extent.integration.test.ts +139 -0
- package/src/__tests__/create-renderer-assembly.integration.test.ts +42 -29
- package/src/__tests__/createRenderer-dispose.test.ts +35 -402
- package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +25 -30
- package/src/__tests__/createRenderer.unit.test.ts +1 -1
- package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +27 -63
- package/src/__tests__/dawn/ibl-precompute-readback.dawn.test.ts +56 -47
- package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +49 -29
- package/src/__tests__/dawn/instances-shadow.dawn.test.ts +26 -15
- package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +13 -14
- package/src/__tests__/dawn/material-alpha.dawn.test.ts +23 -31
- package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +0 -18
- package/src/__tests__/dawn/{mesh-update.dawn.test.ts → mesh-update-dawn.test.ts} +42 -134
- package/src/__tests__/dawn/mipmap-srgb-linear.dawn.test.ts +14 -7
- package/src/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.ts +16 -28
- package/src/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.ts +23 -39
- package/src/__tests__/dawn/upload-texture.dawn.test.ts +198 -0
- package/src/__tests__/dawn/user-handle-mesh-render.dawn.test.ts +12 -14
- package/src/__tests__/dawn/user-mesh-upload.dawn.test.ts +27 -22
- package/src/__tests__/dawn/zero-manifest-mesh-fail.dawn.test.ts +25 -28
- package/src/__tests__/dawn/zero-manifest-ready.dawn.test.ts +10 -11
- package/src/__tests__/demo-sortscope-migration.unit.test.ts +5 -5
- package/src/__tests__/derive-bgl-integration.test.ts +34 -44
- package/src/__tests__/derive-render-data-skin.unit.test.ts +1 -1
- package/src/__tests__/draw-published.ts +11 -4
- package/src/__tests__/dynamic-texture-store.unit.test.ts +190 -0
- package/src/__tests__/equirect-bc6h.integration.test.ts +144 -21
- package/src/__tests__/errors.unit.test.ts +1203 -0
- package/src/__tests__/extract-frames-graceful-degradation.test.ts +3 -7
- package/src/__tests__/extract-frames-merge.test.ts +35 -22
- package/src/__tests__/extract-record-no-hardcoded-texture-fields.test.ts +5 -16
- package/src/__tests__/extract-skin-mismatch.unit.test.ts +322 -0
- package/src/__tests__/fetch-binary-decompress.test.ts +356 -0
- package/src/__tests__/four-path-compression.integration.test.ts +395 -0
- package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +1 -1
- package/src/__tests__/fullscreen-feature-host.dawn.test.ts +80 -0
- package/src/__tests__/fullscreen-post-bgl-depth.test.ts +4 -2
- package/src/__tests__/fullscreen-post-depth-params-arity.test.ts +3 -3
- package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +39 -23
- package/src/__tests__/fullscreen-post-process-pass.unit.test.ts +4 -2
- package/src/__tests__/fxaa-pixel-diff.dawn.test.ts +28 -17
- package/src/__tests__/fxaa-validation-and-resize.dawn.test.ts +29 -18
- package/src/__tests__/fxaa-zero-overhead.dawn.test.ts +16 -12
- package/src/__tests__/geometry.unit.test.ts +525 -159
- package/src/__tests__/glyph-tilemap-worldid.test.ts +1 -1
- package/src/__tests__/gpu-resource-cow-survivor.test.ts +6 -4
- package/src/__tests__/guid-for-asset.unit.test.ts +102 -0
- package/src/__tests__/handle-migration.test.ts +136 -0
- package/src/__tests__/helpers/register-runtime-components.ts +4 -2
- package/src/__tests__/helpers/register-scene-components.ts +1 -1
- package/src/__tests__/host-custom-kind-contract.test-d.ts +47 -0
- package/src/__tests__/host-custom-kind-contract.test.ts +578 -0
- package/src/__tests__/inspect-name.test.ts +139 -0
- package/src/__tests__/instances.test-d.ts +1 -1
- package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +112 -0
- package/src/__tests__/light-casters-9-light.browser.test.ts +73 -76
- package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +7 -10
- package/src/__tests__/light-snapshot.test-d.ts +2 -2
- package/src/__tests__/lights-vec-negative.unit.test.ts +1 -1
- package/src/__tests__/lights.unit.test.ts +16 -500
- package/src/__tests__/loadbyguid-payload.unit.test.ts +117 -0
- package/src/__tests__/loadbyguid-texture-intern.unit.test.ts +213 -0
- package/src/__tests__/loader-extract-graceful.test.ts +146 -0
- package/src/__tests__/loader-name-survival.test.ts +213 -0
- package/src/__tests__/m3-vec-negative.unit.test.ts +3 -2
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +101 -31
- package/src/__tests__/materials.unit.test.ts +10 -27
- package/src/__tests__/mesh-bin-contract.test.ts +28 -0
- package/src/__tests__/mesh-bin-skin-roundtrip.unit.test.ts +149 -0
- package/src/__tests__/mesh-bin.test.ts +38 -0
- package/src/__tests__/mesh-loader-skin-contract.unit.test.ts +120 -0
- package/src/__tests__/mesh-ssbo-grow.unit.test.ts +3 -3
- package/src/__tests__/mip-gate.unit.test.ts +1 -1
- package/src/__tests__/msaa-sprite-pixel-diff.dawn.test.ts +29 -68
- package/src/__tests__/new-kind-refs-contract.test.ts +132 -0
- package/src/__tests__/node-shims.d.ts +4 -6
- package/src/__tests__/package-promotion.test.ts +88 -0
- package/src/__tests__/package-rename.test.ts +103 -0
- package/src/__tests__/pass-selector.unit.test.ts +2 -2
- package/src/__tests__/pbr-pipeline.unit.test.ts +14 -21
- package/src/__tests__/pipeline-builder-msaa.unit.test.ts +6 -9
- package/src/__tests__/pipeline-cache-keying.unit.test.ts +6 -9
- package/src/__tests__/pipeline-spec.test.ts +16 -177
- package/src/__tests__/pipeline.unit.test.ts +154 -1693
- package/src/__tests__/point-light-shadow.dawn.test.ts +33 -42
- package/src/__tests__/point-light-shadow.unit.test.ts +19 -60
- package/src/__tests__/post-process-error-codes.unit.test.ts +4 -1
- package/src/__tests__/post-process-params-component.unit.test.ts +1 -1
- package/src/__tests__/post-process-params-extract.unit.test.ts +2 -5
- package/src/__tests__/postprocess-camera-consistency.test.ts +4 -7
- package/src/__tests__/profiler-assembly.integration.test.ts +1 -1
- package/src/__tests__/propagate-transforms.dawn.test.ts +12 -11
- package/src/__tests__/record-worldid-isolation.test.ts +7 -29
- package/src/__tests__/recover-error-exhaustive.test-d.ts +1 -1
- package/src/__tests__/render-error-exhaustive.test-d.ts +81 -71
- package/src/__tests__/render-feature-assembly.test-d.ts +20 -9
- package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +8 -6
- package/src/__tests__/render-feature-prepared-graphics.browser.test.ts +60 -558
- package/src/__tests__/render-feature-prepared-graphics.dawn.test.ts +57 -544
- package/src/__tests__/render-feature-prepared-graphics.fixture.ts +104 -0
- package/src/__tests__/render-feature-prepared-graphics.integration.test.ts +40 -248
- package/src/__tests__/render-feature-prepared-graphics.regression.integration.test.ts +83 -340
- package/src/__tests__/render-feature-recover.integration.test.ts +9 -49
- package/src/__tests__/render-feature-runtime-surface.test-d.ts +17 -20
- package/src/__tests__/render-schema-consumer.integration.test.ts +20 -13
- package/src/__tests__/render-schema-owner-boundary.unit.test.ts +2 -6
- package/src/__tests__/render-system-cross-world-overlay-queue.test.ts +5 -5
- package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +16 -31
- package/src/__tests__/render-system-extract.test.ts +36 -51
- package/src/__tests__/render-system-extract.unit.test.ts +360 -0
- package/src/__tests__/render-system-fold-bucket.unit.test.ts +3 -5
- package/src/__tests__/render-system-fold-fallback.unit.test.ts +6 -3
- package/src/__tests__/render-system-fold-material.unit.test.ts +3 -5
- package/src/__tests__/render-system-fold-metrics.unit.test.ts +5 -5
- package/src/__tests__/render-system-fold-modegate.unit.test.ts +6 -6
- package/src/__tests__/render-system-fold.dawn.test.ts +7 -5
- package/src/__tests__/render-system-mega.test.ts +292 -190
- package/src/__tests__/render-system-owner-split-contract.test.ts +10 -32
- package/src/__tests__/render-system-record-multi-material-textureview.test.ts +59 -45
- package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +23 -29
- package/src/__tests__/render-system-record.test.ts +398 -235
- package/src/__tests__/render-system-record.unit.test.ts +25 -26
- package/src/__tests__/render-system-skin-bg.unit.test.ts +8 -12
- package/src/__tests__/renderer-consumer.test-d.ts +16 -3
- package/src/__tests__/renderer-health.unit.test.ts +26 -916
- package/src/__tests__/renderer-lifecycle.integration.test.ts +19 -6
- package/src/__tests__/renderer-recover.unit.test.ts +49 -1069
- package/src/__tests__/renderer-result.test-d.ts +19 -9
- package/src/__tests__/renderer-surface.unit.test.ts +3 -5
- package/src/__tests__/renderer-test-utils.ts +56 -0
- package/src/__tests__/renderer.test-d.ts +59 -0
- package/src/__tests__/resolve-asset-handle-err.test.ts +111 -0
- package/src/__tests__/resolve-asset-handle.unit.test.ts +64 -0
- package/src/__tests__/resolve-depth-only-view.test.ts +1 -1
- package/src/__tests__/resolveName-fallback.test.ts +64 -0
- package/src/__tests__/resolveName-null-package.test.ts +38 -0
- package/src/__tests__/resolveName-unified-entry.test.ts +65 -0
- package/src/__tests__/resolveName.test.ts +64 -0
- package/src/__tests__/rhi-null-command-flow.unit.test.ts +43 -261
- package/src/__tests__/rhi-null-renderer-lifecycle.unit.test.ts +41 -169
- package/src/__tests__/roots-to-scene-asset.test.ts +199 -93
- package/src/__tests__/roundtrip-primitive.test.ts +351 -0
- package/src/__tests__/scene-bare-guid-refs-instantiate.test.ts +123 -0
- package/src/__tests__/scene-equirect-roundtrip.test.ts +103 -16
- package/src/__tests__/scene-instance-authoring.integration.test.ts +8 -0
- package/src/__tests__/scene-instance-bridge.integration.test.ts +1 -1
- package/src/__tests__/scene-instance-component.test.ts +1 -1
- package/src/__tests__/scene-mount-fixed-point.test.ts +257 -0
- package/src/__tests__/scene-mount-roundtrip.test.ts +987 -0
- package/src/__tests__/scene-mount-wrapper-roundtrip.test.ts +202 -0
- package/src/__tests__/scene-sugar-instantiate.test.ts +324 -0
- package/src/__tests__/serialize-mounts-roundtrip.test.ts +533 -0
- package/src/__tests__/serialize-scene-asset.test.ts +219 -22
- package/src/__tests__/shadow-caster-empty-schema.test.ts +3 -3
- package/src/__tests__/shadow-csm-cascade-loadop.test.ts +37 -17
- package/src/__tests__/shadow-csm-component.test.ts +3 -5
- package/src/__tests__/shadow-csm-extract.test.ts +2 -100
- package/src/__tests__/shadow-csm-orthoz.test.ts +2 -16
- package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +19 -14
- package/src/__tests__/shadow-csm-tile-consistency.test.ts +32 -26
- package/src/__tests__/shadow-csm-ubo.dawn.test.ts +8 -49
- package/src/__tests__/shadow-csm-ubo.test.ts +3 -25
- package/src/__tests__/shadow-csm-viewport.browser.test.ts +1 -1
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +67 -56
- package/src/__tests__/shadow-m1.dawn.test.ts +8 -9
- package/src/__tests__/shadow-mesh-ssbo-order.test.ts +11 -27
- package/src/__tests__/skin-errors.unit.test.ts +1 -1
- package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +7 -5
- package/src/__tests__/skylight-lazy-projection.test.ts +36 -15
- package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +5 -5
- package/src/__tests__/sprite-cullmode-flip.dawn.test.ts +47 -64
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +12 -11
- package/src/__tests__/sprite-lit-extract-branch.test.ts +1 -1
- package/src/__tests__/sprite-lit-pipeline-cache-key.test.ts +1 -1
- package/src/__tests__/ssao-bgl.test.ts +16 -9
- package/src/__tests__/ssao-buffers.test.ts +2 -2
- package/src/__tests__/ssao-data.test.ts +2 -2
- package/src/__tests__/ssao-errors.test.ts +1 -1
- package/src/__tests__/ssao-passes.test.ts +64 -392
- package/src/__tests__/ssao-pipelines.test.ts +1 -1
- package/src/__tests__/ssao-uniform-layout.test.ts +8 -6
- package/src/__tests__/stencil-outline-pixel.dawn.test.ts +25 -17
- package/src/__tests__/swap-chain-format.unit.test.ts +5 -7
- package/src/__tests__/systems.unit.test.ts +536 -1326
- package/src/__tests__/template-game-default-api-contract.test.ts +362 -0
- package/src/__tests__/text.unit.test.ts +56 -33
- package/src/__tests__/tilemap-childed-despawn.test.ts +8 -14
- package/src/__tests__/tilemap-chunk-extract.overhang-bounds.test.ts +8 -12
- package/src/__tests__/tilemap-chunk-extract.propagate-order.test.ts +2 -1
- package/src/__tests__/tilemap-chunk-extract.test.ts +13 -17
- package/src/__tests__/tilemap-dirty-rebuild.test.ts +7 -11
- package/src/__tests__/tilemap-material-cache.test.ts +8 -13
- package/src/__tests__/tilemap-multi-atlas.test.ts +12 -32
- package/src/__tests__/tilemap-owner-boundary.unit.test.ts +5 -2
- package/src/__tests__/tilemap-region-pack.test.ts +8 -12
- package/src/__tests__/tilemap-spawn-flip-pivot.test.ts +8 -12
- package/src/__tests__/tilemap-uv-inset.test.ts +11 -14
- package/src/__tests__/tilemap-validate.test.ts +1 -1
- package/src/__tests__/tileset-asset-validate.test.ts +638 -0
- package/src/__tests__/transcode-fallback.unit.test.ts +207 -0
- package/src/__tests__/transform-hierarchy-pixel-diff.dawn.test.ts +36 -28
- package/src/__tests__/transient-fields-collect.test.ts +28 -21
- package/src/__tests__/transient-runtime-behavior.test.ts +227 -0
- package/src/__tests__/transparent-sort-tilemap-sprite-interleave.test.ts +4 -4
- package/src/__tests__/variant-selection.unit.test.ts +1 -1
- package/src/__tests__/video-extract-bindgroup.dawn.test.ts +89 -136
- package/src/__tests__/video-loader-consumption.test.ts +86 -0
- package/src/__tests__/video-loader-smoke.dawn.test.ts +60 -0
- package/src/__tests__/view-ubo-layout.browser.test.ts +0 -22
- package/src/__tests__/world-entity-key.test.ts +1 -1
- package/src/__tests__/world-scene-despawn.test.ts +1 -1
- package/src/__tests__/world-scene-instantiate.test.ts +2 -36
- package/src/__tests__/world-scene-nested.test.ts +1 -1
- package/src/__tests__/world-scene-override.test.ts +1 -1
- package/src/backend-selection.ts +69 -0
- package/src/collect-scene-asset.ts +28 -23
- package/src/components/__tests__/camera.test-d.ts +1 -1
- package/src/components/__tests__/camera.test.ts +1 -1
- package/src/components/__tests__/sprite-animation.test-d.ts +2 -2
- package/src/components/__tests__/tile-layer.test-d.ts +1 -1
- package/src/components/__tests__/tile-layer.unit.test.ts +2 -2
- package/src/createRenderer.ts +24 -36
- package/src/errors/environment.ts +1 -1
- package/src/index.ts +47 -113
- package/src/renderer-host.ts +33 -0
- package/src/scene-instances/__tests__/post-spawn-resolve-joints.unit.test.ts +1 -1
- package/src/scene-instances/index.ts +4 -0
- package/src/scene-utils/mount-override-fold.ts +3 -7
- package/src/systems/__tests__/transparent-sort.bench.ts +3 -3
- package/src/systems/sprite-animation-tick.ts +1 -1
- package/src/systems/transparent-sort.ts +38 -38
- package/src/tilemap-register-validate.ts +4 -4
- package/dist/__tests__/bind-group-cache-binding.test.d.ts +0 -2
- package/dist/__tests__/bind-group-cache-binding.test.d.ts.map +0 -1
- package/dist/__tests__/bind-group-cache-frame.test.d.ts +0 -2
- package/dist/__tests__/bind-group-cache-frame.test.d.ts.map +0 -1
- package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts +0 -2
- package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-manifest.d.ts +0 -131
- package/dist/__tests__/fog-producer-manifest.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel-probe.d.ts +0 -42
- package/dist/__tests__/fog-producer-pixel-probe.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel.browser.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-pixel.browser.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-real-fixture.d.ts +0 -5
- package/dist/__tests__/fog-producer-real-fixture.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-vfx-effect.d.ts +0 -3
- package/dist/__tests__/fog-producer-vfx-effect.d.ts.map +0 -1
- package/dist/__tests__/fog-scene-asset.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-scene-asset.integration.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts +0 -2
- package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-resource.test.d.ts +0 -2
- package/dist/__tests__/gpu-resource.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts +0 -2
- package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-buffers.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-buffers.unit.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts +0 -2
- package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts +0 -2
- package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-boundary.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-boundary.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-falsification.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-falsification.test.d.ts.map +0 -1
- package/dist/__tests__/helpers/primitive-world.d.ts +0 -8
- package/dist/__tests__/helpers/primitive-world.d.ts.map +0 -1
- package/dist/__tests__/helpers/render-asset-port.d.ts +0 -15
- package/dist/__tests__/helpers/render-asset-port.d.ts.map +0 -1
- package/dist/__tests__/helpers/scene-asset-port.d.ts +0 -9
- package/dist/__tests__/helpers/scene-asset-port.d.ts.map +0 -1
- package/dist/__tests__/material-derived-coordinates.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-coordinates.integration.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-layout-selection.test.d.ts +0 -2
- package/dist/__tests__/pipeline-layout-selection.test.d.ts.map +0 -1
- package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts +0 -2
- package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts.map +0 -1
- package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts +0 -2
- package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts +0 -2
- package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts.map +0 -1
- package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts +0 -2
- package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-caster-membership.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-caster-membership.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-pssm.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-pssm.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-viewport.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-viewport.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m2.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m2.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3-calibrate.d.ts +0 -83
- package/dist/__tests__/shadow-m3-calibrate.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m3.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/temporal-off-path.unit.test.d.ts +0 -2
- package/dist/__tests__/temporal-off-path.unit.test.d.ts.map +0 -1
- package/dist/__tests__/video-cache-regression.unit.test.d.ts +0 -2
- package/dist/__tests__/video-cache-regression.unit.test.d.ts.map +0 -1
- package/dist/asset-kind.d.ts +0 -4
- package/dist/asset-kind.d.ts.map +0 -1
- package/dist/errors/sprite-animation.d.ts +0 -3
- package/dist/errors/sprite-animation.d.ts.map +0 -1
- package/dist/scene-asset-port.d.ts +0 -17
- package/dist/scene-asset-port.d.ts.map +0 -1
- package/dist/scene-collect-errors.d.ts +0 -22
- package/dist/scene-collect-errors.d.ts.map +0 -1
- package/dist/shadow-m3-calibrate.d.ts +0 -40
- package/dist/shadow-m3-calibrate.d.ts.map +0 -1
- package/src/__tests__/bind-group-cache-binding.test.ts +0 -2513
- package/src/__tests__/bind-group-cache-frame.test.ts +0 -1511
- package/src/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.ts +0 -204
- package/src/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.ts +0 -297
- package/src/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.ts +0 -271
- package/src/__tests__/dispatch-fullscreen-pass-params.unit.test.ts +0 -321
- package/src/__tests__/fog-producer-manifest.ts +0 -162
- package/src/__tests__/fog-producer-pixel-probe.ts +0 -263
- package/src/__tests__/fog-producer-pixel.browser.test.ts +0 -26
- package/src/__tests__/fog-producer-pixel.dawn.test.ts +0 -55
- package/src/__tests__/fog-producer-real-fixture.ts +0 -805
- package/src/__tests__/fog-producer-vfx-effect.ts +0 -272
- package/src/__tests__/fog-scene-asset.integration.test.ts +0 -123
- package/src/__tests__/gpu-resource-store-cubemap-status.test.ts +0 -207
- package/src/__tests__/gpu-resource.test.ts +0 -1206
- package/src/__tests__/hdrp-bgl-slots.browser.test.ts +0 -50
- package/src/__tests__/hdrp-buffers.unit.test.ts +0 -70
- package/src/__tests__/hdrp-execute-filter.unit.test.ts +0 -200
- package/src/__tests__/hdrp-gbuffer-attachments.browser.test.ts +0 -64
- package/src/__tests__/hdrp-pipeline-asset-type.test-d.ts +0 -139
- package/src/__tests__/hdrp-ssao-boundary.test.ts +0 -237
- package/src/__tests__/hdrp-ssao-falsification.test.ts +0 -289
- package/src/__tests__/helpers/primitive-world.ts +0 -91
- package/src/__tests__/helpers/render-asset-port.ts +0 -65
- package/src/__tests__/helpers/scene-asset-port.ts +0 -23
- package/src/__tests__/material-derived-coordinates.integration.test.ts +0 -100
- package/src/__tests__/pipeline-layout-selection.test.ts +0 -298
- package/src/__tests__/post-process-reads-narrowing.test-d.ts +0 -52
- package/src/__tests__/post-process-register-roundtrip.dawn.test.ts +0 -230
- package/src/__tests__/render-feature-zero-feature.browser.test.ts +0 -152
- package/src/__tests__/render-pipeline-trivial.dawn.test.ts +0 -525
- package/src/__tests__/shadow-csm-caster-membership.test.ts +0 -232
- package/src/__tests__/shadow-csm-pssm.test.ts +0 -142
- package/src/__tests__/shadow-csm-temporal.browser.test.ts +0 -145
- package/src/__tests__/shadow-csm-viewport.test.ts +0 -74
- package/src/__tests__/shadow-m2.dawn.test.ts +0 -394
- package/src/__tests__/shadow-m3-calibrate-run.dawn.test.ts +0 -248
- package/src/__tests__/shadow-m3-calibrate.ts +0 -203
- package/src/__tests__/shadow-m3.dawn.test.ts +0 -288
- package/src/__tests__/shadow-probe-control-flow.dawn.test.ts +0 -264
- package/src/__tests__/shadow-quality-metrics.dawn.test.ts +0 -324
- package/src/__tests__/temporal-off-path.unit.test.ts +0 -69
- package/src/__tests__/video-cache-regression.unit.test.ts +0 -186
- package/src/asset-kind.ts +0 -7
- package/src/errors/sprite-animation.ts +0 -5
- package/src/scene-asset-port.ts +0 -32
- package/src/scene-collect-errors.ts +0 -30
- package/src/shadow-m3-calibrate.ts +0 -171
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { SHADOW_PROBE_WGSL } from '../../../render/src/renderer/renderer-factory';
|
|
3
|
-
|
|
4
|
-
const dawnReady = typeof navigator !== 'undefined' && navigator.gpu !== undefined;
|
|
5
|
-
|
|
6
|
-
const PROBE_MAX_COUNT = 64;
|
|
7
|
-
const PROBE_LSM_UBO_BYTES = 352;
|
|
8
|
-
const PROBE_OUTPUT_WIDTH = PROBE_MAX_COUNT;
|
|
9
|
-
const PROBE_OUTPUT_ROW_BYTES = PROBE_OUTPUT_WIDTH * 16;
|
|
10
|
-
const DEPTH_ATLAS_WIDTH = 64;
|
|
11
|
-
const DEPTH_ATLAS_HEIGHT = 32;
|
|
12
|
-
const DEPTH_TILE_SIZE = DEPTH_ATLAS_WIDTH / 2;
|
|
13
|
-
|
|
14
|
-
const GPU_BUFFER_MAP_READ = 0x0001;
|
|
15
|
-
const GPU_BUFFER_COPY_DST = 0x0008;
|
|
16
|
-
const GPU_BUFFER_UNIFORM = 0x0040;
|
|
17
|
-
const GPU_BUFFER_STORAGE = 0x0080;
|
|
18
|
-
const GPU_TEXTURE_COPY_SRC = 0x0001;
|
|
19
|
-
const GPU_TEXTURE_TEXTURE_BINDING = 0x0004;
|
|
20
|
-
const GPU_TEXTURE_RENDER_ATTACHMENT = 0x0010;
|
|
21
|
-
|
|
22
|
-
const DEPTH_WRITE_WGSL = `
|
|
23
|
-
struct VsOut {
|
|
24
|
-
@builtin(position) position : vec4<f32>,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
@vertex fn vs_main(@builtin(vertex_index) index : u32) -> VsOut {
|
|
28
|
-
var positions = array<vec2<f32>, 3>(
|
|
29
|
-
vec2<f32>(-1.0, -1.0),
|
|
30
|
-
vec2<f32>( 3.0, -1.0),
|
|
31
|
-
vec2<f32>(-1.0, 3.0),
|
|
32
|
-
);
|
|
33
|
-
var out : VsOut;
|
|
34
|
-
out.position = vec4<f32>(positions[index], 0.1, 1.0);
|
|
35
|
-
return out;
|
|
36
|
-
}
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
interface ProbeResult {
|
|
40
|
-
readonly shadowFactor: number;
|
|
41
|
-
readonly sampledDepth: number;
|
|
42
|
-
readonly cascadeIndex: number;
|
|
43
|
-
readonly receiverDepth: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function makeLightMatrix(): Float32Array {
|
|
47
|
-
const matrix = new Float32Array(16);
|
|
48
|
-
matrix[0] = 1;
|
|
49
|
-
matrix[5] = 1;
|
|
50
|
-
// Keep XY in clip space while pinning all receivers to a known depth.
|
|
51
|
-
matrix[10] = 0;
|
|
52
|
-
matrix[14] = 0.5;
|
|
53
|
-
matrix[15] = 1;
|
|
54
|
-
return matrix;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function makeIdentityMatrix(): Float32Array {
|
|
58
|
-
const matrix = new Float32Array(16);
|
|
59
|
-
matrix[0] = 1;
|
|
60
|
-
matrix[5] = 1;
|
|
61
|
-
matrix[10] = 1;
|
|
62
|
-
matrix[15] = 1;
|
|
63
|
-
return matrix;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function runControlledProbe(): Promise<ProbeResult[]> {
|
|
67
|
-
const gpu = navigator.gpu;
|
|
68
|
-
const adapter = await gpu.requestAdapter();
|
|
69
|
-
if (adapter === null) throw new Error('no Dawn adapter');
|
|
70
|
-
const device = await adapter.requestDevice();
|
|
71
|
-
|
|
72
|
-
const shaderModule = device.createShaderModule({ code: SHADOW_PROBE_WGSL });
|
|
73
|
-
const pipeline = device.createRenderPipeline({
|
|
74
|
-
layout: 'auto',
|
|
75
|
-
vertex: { module: shaderModule, entryPoint: 'vs_main' },
|
|
76
|
-
fragment: {
|
|
77
|
-
module: shaderModule,
|
|
78
|
-
entryPoint: 'fs_main',
|
|
79
|
-
targets: [{ format: 'rgba32float' }],
|
|
80
|
-
},
|
|
81
|
-
primitive: { topology: 'triangle-list' },
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const depthModule = device.createShaderModule({ code: DEPTH_WRITE_WGSL });
|
|
85
|
-
const depthPipeline = device.createRenderPipeline({
|
|
86
|
-
layout: 'auto',
|
|
87
|
-
vertex: { module: depthModule, entryPoint: 'vs_main' },
|
|
88
|
-
primitive: { topology: 'triangle-list' },
|
|
89
|
-
depthStencil: {
|
|
90
|
-
format: 'depth32float',
|
|
91
|
-
depthWriteEnabled: true,
|
|
92
|
-
depthCompare: 'less',
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const depthTexture = device.createTexture({
|
|
97
|
-
size: { width: DEPTH_ATLAS_WIDTH, height: DEPTH_ATLAS_HEIGHT, depthOrArrayLayers: 1 },
|
|
98
|
-
format: 'depth32float',
|
|
99
|
-
usage: GPU_TEXTURE_TEXTURE_BINDING | GPU_TEXTURE_RENDER_ATTACHMENT,
|
|
100
|
-
});
|
|
101
|
-
const depthView = depthTexture.createView();
|
|
102
|
-
const depthEncoder = device.createCommandEncoder();
|
|
103
|
-
const depthPass = depthEncoder.beginRenderPass({
|
|
104
|
-
colorAttachments: [],
|
|
105
|
-
depthStencilAttachment: {
|
|
106
|
-
view: depthView,
|
|
107
|
-
depthLoadOp: 'clear',
|
|
108
|
-
depthStoreOp: 'store',
|
|
109
|
-
depthClearValue: 0.9,
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
depthPass.setPipeline(depthPipeline);
|
|
113
|
-
// Only sibling tile 1 contains a caster. Tile 0 stays at the clear depth.
|
|
114
|
-
depthPass.setViewport(0, 0, DEPTH_ATLAS_WIDTH, DEPTH_ATLAS_HEIGHT, 0, 1);
|
|
115
|
-
depthPass.setScissorRect(DEPTH_TILE_SIZE, 0, DEPTH_TILE_SIZE, DEPTH_TILE_SIZE);
|
|
116
|
-
depthPass.draw(3);
|
|
117
|
-
depthPass.end();
|
|
118
|
-
device.queue.submit([depthEncoder.finish()]);
|
|
119
|
-
|
|
120
|
-
const lsm = new Float32Array(PROBE_LSM_UBO_BYTES / 4);
|
|
121
|
-
const lightMatrix = makeLightMatrix();
|
|
122
|
-
const viewMatrix = makeIdentityMatrix();
|
|
123
|
-
lsm.set(lightMatrix, 0);
|
|
124
|
-
lsm.set(lightMatrix, 16);
|
|
125
|
-
lsm.set(lightMatrix, 32);
|
|
126
|
-
lsm.set(lightMatrix, 48);
|
|
127
|
-
lsm.set(viewMatrix, 64);
|
|
128
|
-
// Cascade 0 spans viewDepth < 10; cascade 1 ends at shadowDistance 15.
|
|
129
|
-
lsm[80] = 10;
|
|
130
|
-
lsm[81] = 15;
|
|
131
|
-
const lsmU32 = new Uint32Array(lsm.buffer);
|
|
132
|
-
lsmU32[84] = 2;
|
|
133
|
-
lsmU32[85] = 3;
|
|
134
|
-
lsm[86] = 0.2;
|
|
135
|
-
|
|
136
|
-
// Three probes are deliberately chosen to falsify three independent
|
|
137
|
-
// control-flow regressions:
|
|
138
|
-
// 1. local XY outside cascade 0 but inside atlas-space UV;
|
|
139
|
-
// 2. beyond the final split / shadowDistance;
|
|
140
|
-
// 3. inside the cascade-0 blend band with different sibling depth.
|
|
141
|
-
const positions = [
|
|
142
|
-
[1.25, 0, 0.5, 0],
|
|
143
|
-
[0, 0, -20, 0],
|
|
144
|
-
[0, 0, -9, 0],
|
|
145
|
-
] as const;
|
|
146
|
-
const input = new Float32Array(PROBE_MAX_COUNT * 4);
|
|
147
|
-
for (let index = 0; index < positions.length; index += 1) {
|
|
148
|
-
const [x, y, z, bias] = positions[index] ?? [0, 0, 0, 0];
|
|
149
|
-
const base = index * 4;
|
|
150
|
-
input[base] = x;
|
|
151
|
-
input[base + 1] = y;
|
|
152
|
-
input[base + 2] = z;
|
|
153
|
-
input[base + 3] = bias;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const lsmBuffer = device.createBuffer({
|
|
157
|
-
size: PROBE_LSM_UBO_BYTES,
|
|
158
|
-
usage: GPU_BUFFER_UNIFORM | GPU_BUFFER_COPY_DST,
|
|
159
|
-
});
|
|
160
|
-
const inputBuffer = device.createBuffer({
|
|
161
|
-
size: input.byteLength,
|
|
162
|
-
usage: GPU_BUFFER_STORAGE | GPU_BUFFER_COPY_DST,
|
|
163
|
-
});
|
|
164
|
-
device.queue.writeBuffer(lsmBuffer, 0, lsm);
|
|
165
|
-
device.queue.writeBuffer(inputBuffer, 0, input);
|
|
166
|
-
|
|
167
|
-
const sampler = device.createSampler({
|
|
168
|
-
addressModeU: 'clamp-to-edge',
|
|
169
|
-
addressModeV: 'clamp-to-edge',
|
|
170
|
-
magFilter: 'linear',
|
|
171
|
-
minFilter: 'linear',
|
|
172
|
-
compare: 'less',
|
|
173
|
-
});
|
|
174
|
-
const bindGroup = device.createBindGroup({
|
|
175
|
-
layout: pipeline.getBindGroupLayout(0),
|
|
176
|
-
entries: [
|
|
177
|
-
{ binding: 0, resource: { buffer: lsmBuffer } },
|
|
178
|
-
{ binding: 1, resource: { buffer: inputBuffer } },
|
|
179
|
-
{ binding: 2, resource: depthView },
|
|
180
|
-
{ binding: 3, resource: sampler },
|
|
181
|
-
],
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const outputTexture = device.createTexture({
|
|
185
|
-
size: { width: PROBE_OUTPUT_WIDTH, height: 1, depthOrArrayLayers: 1 },
|
|
186
|
-
format: 'rgba32float',
|
|
187
|
-
usage: GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_RENDER_ATTACHMENT,
|
|
188
|
-
});
|
|
189
|
-
const outputView = outputTexture.createView();
|
|
190
|
-
const staging = device.createBuffer({
|
|
191
|
-
size: PROBE_OUTPUT_ROW_BYTES,
|
|
192
|
-
usage: GPU_BUFFER_COPY_DST | GPU_BUFFER_MAP_READ,
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
const encoder = device.createCommandEncoder();
|
|
196
|
-
const pass = encoder.beginRenderPass({
|
|
197
|
-
colorAttachments: [
|
|
198
|
-
{
|
|
199
|
-
view: outputView,
|
|
200
|
-
loadOp: 'clear',
|
|
201
|
-
storeOp: 'store',
|
|
202
|
-
clearValue: { r: 1, g: 1, b: -1, a: 1 },
|
|
203
|
-
},
|
|
204
|
-
],
|
|
205
|
-
});
|
|
206
|
-
pass.setPipeline(pipeline);
|
|
207
|
-
pass.setBindGroup(0, bindGroup);
|
|
208
|
-
pass.draw(6, positions.length);
|
|
209
|
-
pass.end();
|
|
210
|
-
encoder.copyTextureToBuffer(
|
|
211
|
-
{ texture: outputTexture },
|
|
212
|
-
{ buffer: staging, bytesPerRow: PROBE_OUTPUT_ROW_BYTES, rowsPerImage: 1 },
|
|
213
|
-
{ width: PROBE_OUTPUT_WIDTH, height: 1, depthOrArrayLayers: 1 },
|
|
214
|
-
);
|
|
215
|
-
device.queue.submit([encoder.finish()]);
|
|
216
|
-
await staging.mapAsync(GPU_BUFFER_MAP_READ);
|
|
217
|
-
const readback = new Float32Array(staging.getMappedRange().slice(0));
|
|
218
|
-
staging.unmap();
|
|
219
|
-
|
|
220
|
-
const result: ProbeResult[] = [];
|
|
221
|
-
for (let index = 0; index < positions.length; index += 1) {
|
|
222
|
-
const base = index * 4;
|
|
223
|
-
result.push({
|
|
224
|
-
shadowFactor: readback[base] ?? 1,
|
|
225
|
-
sampledDepth: readback[base + 1] ?? 1,
|
|
226
|
-
cascadeIndex: Math.round(readback[base + 2] ?? -1),
|
|
227
|
-
receiverDepth: readback[base + 3] ?? 1,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
staging.destroy();
|
|
232
|
-
outputTexture.destroy();
|
|
233
|
-
inputBuffer.destroy();
|
|
234
|
-
lsmBuffer.destroy();
|
|
235
|
-
depthTexture.destroy();
|
|
236
|
-
device.destroy();
|
|
237
|
-
return result;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
describe('production shadow probe control-flow falsifiers', () => {
|
|
241
|
-
it.skipIf(!dawnReady)(
|
|
242
|
-
'rejects local-tile OOB, final-split OOB, and blends sibling tiles',
|
|
243
|
-
async () => {
|
|
244
|
-
const [localOob, finalSplitOob, blendBand] = await runControlledProbe();
|
|
245
|
-
|
|
246
|
-
// The local-tile point maps to atlas UV x=0.5625, where the sibling tile
|
|
247
|
-
// contains depth 0.1. A global-atlas guard would read that non-clear depth;
|
|
248
|
-
// the producer-local guard must fail closed before any texture read.
|
|
249
|
-
expect(localOob?.shadowFactor).toBe(1);
|
|
250
|
-
expect(localOob?.sampledDepth).toBe(1);
|
|
251
|
-
expect(localOob?.cascadeIndex).toBe(0);
|
|
252
|
-
|
|
253
|
-
expect(finalSplitOob?.cascadeIndex).toBe(-1);
|
|
254
|
-
expect(finalSplitOob?.shadowFactor).toBe(1);
|
|
255
|
-
|
|
256
|
-
// At viewDepth=9, split=10 and blend=0.2, t=0.5. Tile 0 is lit (0.9)
|
|
257
|
-
// and sibling tile 1 is blocked (0.1), so the exact production blend is
|
|
258
|
-
// 0.5. Removing the adjacent-layer sample or the mix makes this RED.
|
|
259
|
-
expect(blendBand?.cascadeIndex).toBe(0);
|
|
260
|
-
expect(blendBand?.shadowFactor).toBeCloseTo(0.5, 3);
|
|
261
|
-
expect(blendBand?.sampledDepth).toBeCloseTo(0.9, 3);
|
|
262
|
-
},
|
|
263
|
-
);
|
|
264
|
-
});
|
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
// shadow-quality-metrics.dawn.test.ts — L1 shadow-quality metric probes.
|
|
2
|
-
//
|
|
3
|
-
// WHY THIS EXISTS
|
|
4
|
-
// ---------------
|
|
5
|
-
// Structural smoke ("4 shadowCascade passes, no crash") proves the pipeline is
|
|
6
|
-
// wired, not that the shadows look right. Acne, missed occlusion, cascade seams
|
|
7
|
-
// and peter-panning all pass a structural gate while looking broken. This suite
|
|
8
|
-
// turns "does the shadow look good" into a handful of scalar metrics sampled off
|
|
9
|
-
// the real shadow atlas via `renderer.debugSampleShadowFactor`, each with a
|
|
10
|
-
// falsifiable assertion band. Regressions move a number; the number has a gate.
|
|
11
|
-
//
|
|
12
|
-
// WHAT THE PROBE MEASURES (and its honest limits)
|
|
13
|
-
// -----------------------------------------------
|
|
14
|
-
// `debugSampleShadowFactor(worldPositions)` reads the SAME shadow atlas the
|
|
15
|
-
// forward pass samples, selects the cascade by frustum containment (mirrors the
|
|
16
|
-
// shader's viewZ+splitPlanes selection), and returns a fixed-bias 3x3-PCF factor
|
|
17
|
-
// per point (1 = fully lit, 0 = fully shadowed). That makes these metrics honest:
|
|
18
|
-
// - acne — lit ground reads ~1 (no spurious self-shadow)
|
|
19
|
-
// - coverage — ground under the occluder reads low (shadow is present)
|
|
20
|
-
// - contact — the lit->shadowed transition sits at the occluder edge
|
|
21
|
-
// (peter-panning = shadow starts too far from the caster)
|
|
22
|
-
// - cascadeSeam — no factor discontinuity as depth crosses a split plane
|
|
23
|
-
// NOT measurable here (needs L3 lit-framebuffer readback): penumbra WIDTH driven
|
|
24
|
-
// by the material `pcfKernelSize`, because the probe uses its own fixed 3x3 tap,
|
|
25
|
-
// not the forward shader's variable kernel. We do not fake that metric.
|
|
26
|
-
//
|
|
27
|
-
// FALSIFY DISCIPLINE
|
|
28
|
-
// ------------------
|
|
29
|
-
// Each metric has a paired "must move the right way when the scene breaks" check
|
|
30
|
-
// (castShadow:false collapses coverage; a huge shadowDistance degrades near acne)
|
|
31
|
-
// so a vacuous all-green (empty atlas, probe returning a constant) cannot pass.
|
|
32
|
-
|
|
33
|
-
import { World } from '@forgeax/engine-ecs';
|
|
34
|
-
import { createPrimitiveMesh } from '@forgeax/engine-geometry';
|
|
35
|
-
import {
|
|
36
|
-
Camera,
|
|
37
|
-
DirectionalLight,
|
|
38
|
-
MeshFilter,
|
|
39
|
-
MeshRenderer,
|
|
40
|
-
} from '@forgeax/engine-render/internal';
|
|
41
|
-
import { Transform } from '@forgeax/engine-scene';
|
|
42
|
-
import { describe, expect, it } from 'vitest';
|
|
43
|
-
import { createRenderer } from '../index';
|
|
44
|
-
import { drawPublished } from './draw-published';
|
|
45
|
-
|
|
46
|
-
const ENGINE_MANIFEST = await (async () => {
|
|
47
|
-
const { buildEngineShaderManifest } = await import('@forgeax/engine-vite-plugin-shader');
|
|
48
|
-
return buildEngineShaderManifest();
|
|
49
|
-
})();
|
|
50
|
-
const ENGINE_MANIFEST_URL = `data:application/json,${encodeURIComponent(
|
|
51
|
-
JSON.stringify(ENGINE_MANIFEST),
|
|
52
|
-
)}`;
|
|
53
|
-
|
|
54
|
-
// biome-ignore lint/suspicious/noExplicitAny: dawn-node detection guard
|
|
55
|
-
const dawnReady = typeof navigator !== 'undefined' && (navigator as any).gpu !== undefined;
|
|
56
|
-
|
|
57
|
-
const GROUND_SIZE = 40;
|
|
58
|
-
const RT_W = 256;
|
|
59
|
-
const RT_H = 256;
|
|
60
|
-
const TEXTURE_USAGE_COPY_SRC = 0x01;
|
|
61
|
-
const TEXTURE_USAGE_RENDER_ATTACHMENT = 0x10;
|
|
62
|
-
|
|
63
|
-
// Mock canvas that exposes a real configurable WebGPU context + captures the
|
|
64
|
-
// device, mirroring shadow-m3.dawn.test.ts exactly (per-canvas adapter patch).
|
|
65
|
-
function createMockCanvas(): HTMLCanvasElement {
|
|
66
|
-
let sharedDevice: GPUDevice | undefined;
|
|
67
|
-
const originalRequestAdapter = globalThis.navigator.gpu.requestAdapter.bind(
|
|
68
|
-
globalThis.navigator.gpu,
|
|
69
|
-
);
|
|
70
|
-
globalThis.navigator.gpu.requestAdapter = async (opts) => {
|
|
71
|
-
const rawAdapter = await originalRequestAdapter(opts);
|
|
72
|
-
if (rawAdapter === null) return rawAdapter;
|
|
73
|
-
const originalRequestDevice = rawAdapter.requestDevice.bind(rawAdapter);
|
|
74
|
-
rawAdapter.requestDevice = async (desc) => {
|
|
75
|
-
const dev = await originalRequestDevice(desc);
|
|
76
|
-
if (sharedDevice === undefined) sharedDevice = dev;
|
|
77
|
-
return dev;
|
|
78
|
-
};
|
|
79
|
-
return rawAdapter;
|
|
80
|
-
};
|
|
81
|
-
let renderTarget: GPUTexture | undefined;
|
|
82
|
-
const ensureRenderTarget = (device: GPUDevice, format: GPUTextureFormat): GPUTexture => {
|
|
83
|
-
if (renderTarget !== undefined) return renderTarget;
|
|
84
|
-
renderTarget = device.createTexture({
|
|
85
|
-
size: { width: RT_W, height: RT_H, depthOrArrayLayers: 1 },
|
|
86
|
-
format,
|
|
87
|
-
usage: TEXTURE_USAGE_RENDER_ATTACHMENT | TEXTURE_USAGE_COPY_SRC,
|
|
88
|
-
viewFormats: ['rgba8unorm-srgb'],
|
|
89
|
-
});
|
|
90
|
-
return renderTarget;
|
|
91
|
-
};
|
|
92
|
-
return {
|
|
93
|
-
width: RT_W,
|
|
94
|
-
height: RT_H,
|
|
95
|
-
getContext(kind: string): unknown {
|
|
96
|
-
if (kind !== 'webgpu') return null;
|
|
97
|
-
return {
|
|
98
|
-
configure(desc: { device: GPUDevice; format?: GPUTextureFormat }) {
|
|
99
|
-
ensureRenderTarget(desc.device, desc.format ?? 'rgba8unorm');
|
|
100
|
-
},
|
|
101
|
-
unconfigure() {},
|
|
102
|
-
getCurrentTexture(): GPUTexture {
|
|
103
|
-
if (renderTarget === undefined) {
|
|
104
|
-
if (sharedDevice === undefined)
|
|
105
|
-
throw new Error('render target requested before device captured');
|
|
106
|
-
return ensureRenderTarget(sharedDevice, 'rgba8unorm');
|
|
107
|
-
}
|
|
108
|
-
return renderTarget;
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
addEventListener() {},
|
|
113
|
-
removeEventListener() {},
|
|
114
|
-
} as unknown as HTMLCanvasElement;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
interface SceneConfig {
|
|
118
|
-
readonly castShadow?: boolean;
|
|
119
|
-
readonly cascadeCount?: number;
|
|
120
|
-
readonly shadowDistance?: number;
|
|
121
|
-
readonly mapSize?: number;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Ground plane at y=0 with a 2x2x2 occluder centred at y=1.5 (bottom face at
|
|
125
|
-
// y=0.5), lit by a straight-down directional light. Orthographic camera framing
|
|
126
|
-
// the scene so every probe point lands in the atlas fit. Occluder edges are at
|
|
127
|
-
// x = +-1, so the ground shadow footprint is ~[-1,1] in x (light is near-vertical).
|
|
128
|
-
function buildScene(world: World, cfg: SceneConfig): void {
|
|
129
|
-
const cubeHandle = world.internSharedRef('MeshAsset', createPrimitiveMesh('cube').unwrap());
|
|
130
|
-
world
|
|
131
|
-
.spawn(
|
|
132
|
-
{
|
|
133
|
-
component: Transform,
|
|
134
|
-
data: {
|
|
135
|
-
pos: [0, -0.005, 0],
|
|
136
|
-
quat: [0, 0, 0, 1],
|
|
137
|
-
scale: [GROUND_SIZE, 0.01, GROUND_SIZE],
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
{ component: MeshFilter, data: { assetHandle: cubeHandle } },
|
|
141
|
-
{ component: MeshRenderer, data: {} },
|
|
142
|
-
)
|
|
143
|
-
.unwrap();
|
|
144
|
-
|
|
145
|
-
world
|
|
146
|
-
.spawn(
|
|
147
|
-
{
|
|
148
|
-
component: Transform,
|
|
149
|
-
data: { pos: [0, 1.5, 0], quat: [0, 0, 0, 1], scale: [2, 2, 2] },
|
|
150
|
-
},
|
|
151
|
-
{ component: MeshFilter, data: { assetHandle: cubeHandle } },
|
|
152
|
-
{ component: MeshRenderer, data: {} },
|
|
153
|
-
)
|
|
154
|
-
.unwrap();
|
|
155
|
-
|
|
156
|
-
world
|
|
157
|
-
.spawn({
|
|
158
|
-
component: DirectionalLight,
|
|
159
|
-
data: {
|
|
160
|
-
direction: [0, -1, 0],
|
|
161
|
-
color: [1, 1, 1],
|
|
162
|
-
intensity: 1.5,
|
|
163
|
-
castShadow: cfg.castShadow ?? true,
|
|
164
|
-
cascadeCount: cfg.cascadeCount ?? 1,
|
|
165
|
-
mapSize: cfg.mapSize ?? 1024,
|
|
166
|
-
shadowDistance: cfg.shadowDistance ?? 50,
|
|
167
|
-
},
|
|
168
|
-
})
|
|
169
|
-
.unwrap();
|
|
170
|
-
|
|
171
|
-
world
|
|
172
|
-
.spawn(
|
|
173
|
-
{ component: Transform, data: { pos: [0, 0, 10], quat: [0, 0, 0, 1] } },
|
|
174
|
-
{
|
|
175
|
-
component: Camera,
|
|
176
|
-
data: {
|
|
177
|
-
projection: 1,
|
|
178
|
-
left: -GROUND_SIZE / 2,
|
|
179
|
-
right: GROUND_SIZE / 2,
|
|
180
|
-
bottom: -GROUND_SIZE / 2,
|
|
181
|
-
top: GROUND_SIZE / 2,
|
|
182
|
-
near: 0.1,
|
|
183
|
-
far: 100,
|
|
184
|
-
fov: 0,
|
|
185
|
-
aspect: 1,
|
|
186
|
-
} as Record<string, unknown> as never,
|
|
187
|
-
},
|
|
188
|
-
)
|
|
189
|
-
.unwrap();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ONE renderer for the whole suite. Creating a second createRenderer in the
|
|
193
|
-
// same dawn-node process trips `manifest-malformed` (shared shader-module cache
|
|
194
|
-
// state), so every sample() re-draws a fresh world through this single renderer
|
|
195
|
-
// and re-reads the atlas — the extract stage rebuilds shadow matrices per frame.
|
|
196
|
-
type Renderer = Awaited<ReturnType<typeof createRenderer>>;
|
|
197
|
-
let sharedRenderer: Renderer | undefined;
|
|
198
|
-
async function getRenderer(): Promise<Renderer> {
|
|
199
|
-
if (sharedRenderer !== undefined) return sharedRenderer;
|
|
200
|
-
const canvas = createMockCanvas();
|
|
201
|
-
const renderer = await createRenderer(canvas, {}, { shaderManifestUrl: ENGINE_MANIFEST_URL });
|
|
202
|
-
const ready = await renderer.ready;
|
|
203
|
-
if (!ready.ok) throw new Error('renderer not ready');
|
|
204
|
-
sharedRenderer = renderer;
|
|
205
|
-
return renderer;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
async function sample(
|
|
209
|
-
cfg: SceneConfig,
|
|
210
|
-
points: ReadonlyArray<readonly [number, number, number]>,
|
|
211
|
-
): Promise<number[]> {
|
|
212
|
-
const renderer = await getRenderer();
|
|
213
|
-
const world = new World();
|
|
214
|
-
buildScene(world, cfg);
|
|
215
|
-
world.update(1 / 60).unwrap();
|
|
216
|
-
const draw = drawPublished(renderer, world);
|
|
217
|
-
if (!draw.ok) throw new Error('draw failed');
|
|
218
|
-
const res = await renderer.debugSampleShadowFactor?.(points);
|
|
219
|
-
if (!res) throw new Error('debugSampleShadowFactor returned null');
|
|
220
|
-
return res.map((r) => r.shadowFactor);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const mean = (xs: number[]): number => xs.reduce((a, b) => a + b, 0) / xs.length;
|
|
224
|
-
|
|
225
|
-
describe('shadow quality metrics (L1 — off-atlas probe)', () => {
|
|
226
|
-
it.skipIf(!dawnReady)('dawn-binding present', () => {
|
|
227
|
-
expect(dawnReady).toBe(true);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// ── Metric: ACNE — lit ground far from the occluder must read ~fully lit ──
|
|
231
|
-
it.skipIf(!dawnReady)('acne: unoccluded ground reads ~1 (no self-shadow noise)', async () => {
|
|
232
|
-
const litPoints: [number, number, number][] = [
|
|
233
|
-
[8, 0, 8],
|
|
234
|
-
[-8, 0, 8],
|
|
235
|
-
[8, 0, -8],
|
|
236
|
-
[-8, 0, -8],
|
|
237
|
-
[12, 0, 0],
|
|
238
|
-
[-12, 0, 0],
|
|
239
|
-
];
|
|
240
|
-
const factors = await sample({}, litPoints);
|
|
241
|
-
const acneFraction = factors.filter((f) => f < 0.99).length / factors.length;
|
|
242
|
-
// METRIC: fraction of clearly-unoccluded samples showing spurious shadow.
|
|
243
|
-
// Gate: none should. A future acne regression pushes this above 0.
|
|
244
|
-
expect(acneFraction).toBe(0);
|
|
245
|
-
expect(mean(factors)).toBeGreaterThan(0.99);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// ── Metric: COVERAGE — ground under the occluder must be shadowed ──
|
|
249
|
-
it.skipIf(!dawnReady)('coverage: ground under occluder reads shadowed', async () => {
|
|
250
|
-
const under: [number, number, number][] = [
|
|
251
|
-
[0, 0, 0],
|
|
252
|
-
[0.5, 0, 0],
|
|
253
|
-
[0, 0, 0.5],
|
|
254
|
-
[-0.5, 0, -0.5],
|
|
255
|
-
];
|
|
256
|
-
const factors = await sample({}, under);
|
|
257
|
-
// METRIC: mean shadow factor directly under the caster. Gate: strongly
|
|
258
|
-
// shadowed. Missed occlusion (bad ortho Z reach / too-small map) raises this.
|
|
259
|
-
expect(mean(factors)).toBeLessThan(0.1);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// ── FALSIFY coverage: castShadow=false must collapse the shadow ──
|
|
263
|
-
it.skipIf(!dawnReady)(
|
|
264
|
-
'coverage falsify: castShadow=false leaves under-occluder lit',
|
|
265
|
-
async () => {
|
|
266
|
-
const under: [number, number, number][] = [
|
|
267
|
-
[0, 0, 0],
|
|
268
|
-
[0.5, 0, 0],
|
|
269
|
-
];
|
|
270
|
-
const on = mean(await sample({ castShadow: true }, under));
|
|
271
|
-
const off = mean(await sample({ castShadow: false }, under));
|
|
272
|
-
// The same points flip from shadowed to lit — proves the probe reads a real
|
|
273
|
-
// atlas, not a constant. Guards against a vacuous coverage pass.
|
|
274
|
-
expect(on).toBeLessThan(0.1);
|
|
275
|
-
expect(off).toBeGreaterThan(0.9);
|
|
276
|
-
},
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
// ── Metric: CONTACT / peter-panning — transition sits at the occluder edge ──
|
|
280
|
-
it.skipIf(!dawnReady)('contact: shadow edge tracks the occluder footprint (x≈1)', async () => {
|
|
281
|
-
// Scan x from deep-shadow (0) outward past the occluder edge (1.0) into
|
|
282
|
-
// full light. The last shadowed sample and first lit sample must straddle
|
|
283
|
-
// x≈1. Peter-panning would push the transition well past the edge.
|
|
284
|
-
const xs = [0, 0.4, 0.8, 1.0, 1.2, 1.6, 2.0, 3.0];
|
|
285
|
-
const factors = await sample(
|
|
286
|
-
{},
|
|
287
|
-
xs.map((x) => [x, 0, 0] as [number, number, number]),
|
|
288
|
-
);
|
|
289
|
-
const scan = xs.map((x, i) => ({ x, f: factors[i] ?? 1 }));
|
|
290
|
-
const lastShadowedX = scan.filter((s) => s.f < 0.5).at(-1)?.x ?? -1;
|
|
291
|
-
const firstLitX = scan.find((s) => s.f > 0.5)?.x ?? 999;
|
|
292
|
-
// METRIC: transition band position. Measured contact profile is
|
|
293
|
-
// [0,0,0.33,1,1,1,1] over xs => lastShadowedX=1.0, firstLitX=1.2, i.e. a
|
|
294
|
-
// one-sample transition straddling the occluder edge (x=1). Gate: shadow
|
|
295
|
-
// persists to the edge and clears within ~1 texel of it. Peter-panning
|
|
296
|
-
// (shadow detached from the caster) pushes firstLitX out; this catches it.
|
|
297
|
-
expect(lastShadowedX).toBeGreaterThanOrEqual(0.8);
|
|
298
|
-
expect(firstLitX).toBeLessThanOrEqual(1.2);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
// ── Metric: CASCADE SEAM — no factor jump across a split boundary ──
|
|
302
|
-
it.skipIf(!dawnReady)(
|
|
303
|
-
'cascadeSeam: 4-cascade lit ground has no discontinuity vs 1-cascade',
|
|
304
|
-
async () => {
|
|
305
|
-
// Sample lit ground along increasing depth (spanning cascade splits under a
|
|
306
|
-
// 4-cascade config). All should stay ~1; a seam would show a sudden dip at a
|
|
307
|
-
// split plane. Compare the 4-cascade profile to the single-cascade baseline.
|
|
308
|
-
const line: [number, number, number][] = [];
|
|
309
|
-
for (let z = -18; z <= 18; z += 3) line.push([6, 0, z]);
|
|
310
|
-
const one = await sample({ cascadeCount: 1 }, line);
|
|
311
|
-
const four = await sample({ cascadeCount: 4 }, line);
|
|
312
|
-
const maxAdjacentJump = (xs: number[]): number =>
|
|
313
|
-
xs.reduce(
|
|
314
|
-
(m, cur, i) => (i === 0 ? m : Math.max(m, Math.abs(cur - (xs[i - 1] ?? cur)))),
|
|
315
|
-
0,
|
|
316
|
-
);
|
|
317
|
-
// METRIC: largest factor step between adjacent lit samples. Gate: small for
|
|
318
|
-
// both; the 4-cascade path must not introduce a seam the 1-cascade lacks.
|
|
319
|
-
expect(maxAdjacentJump(one)).toBeLessThan(0.1);
|
|
320
|
-
expect(maxAdjacentJump(four)).toBeLessThan(0.1);
|
|
321
|
-
expect(mean(four)).toBeGreaterThan(0.95);
|
|
322
|
-
},
|
|
323
|
-
);
|
|
324
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { World } from '@forgeax/engine-ecs';
|
|
2
|
-
import {
|
|
3
|
-
ANTIALIAS_FXAA,
|
|
4
|
-
ANTIALIAS_MSAA,
|
|
5
|
-
ANTIALIAS_NONE,
|
|
6
|
-
Camera,
|
|
7
|
-
perspective,
|
|
8
|
-
} from '@forgeax/engine-render';
|
|
9
|
-
import { rhi } from '@forgeax/engine-rhi-null';
|
|
10
|
-
import { Transform } from '@forgeax/engine-scene';
|
|
11
|
-
import { describe, expect, it } from 'vitest';
|
|
12
|
-
import { createRenderer } from '../createRenderer';
|
|
13
|
-
|
|
14
|
-
const manifest = `data:application/json,${encodeURIComponent(
|
|
15
|
-
JSON.stringify({
|
|
16
|
-
schemaVersion: '1.0.0',
|
|
17
|
-
entries: [
|
|
18
|
-
{ hash: 'pbr00000', wgsl: '/* pbr stub */', glsl: '', bindings: '' },
|
|
19
|
-
{ hash: 'unlit000', wgsl: '/* unlit stub */', glsl: '', bindings: '' },
|
|
20
|
-
{ hash: 'tonemap0', wgsl: '/* tonemap stub */', glsl: '', bindings: '' },
|
|
21
|
-
],
|
|
22
|
-
}),
|
|
23
|
-
)}`;
|
|
24
|
-
|
|
25
|
-
function canvas(): HTMLCanvasElement {
|
|
26
|
-
return {
|
|
27
|
-
width: 64,
|
|
28
|
-
height: 64,
|
|
29
|
-
getContext: () => null,
|
|
30
|
-
addEventListener: () => undefined,
|
|
31
|
-
removeEventListener: () => undefined,
|
|
32
|
-
} as unknown as HTMLCanvasElement;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe.each([
|
|
36
|
-
['none', ANTIALIAS_NONE],
|
|
37
|
-
['fxaa', ANTIALIAS_FXAA],
|
|
38
|
-
['msaa', ANTIALIAS_MSAA],
|
|
39
|
-
] as const)('temporal off-path: %s', (_name, antialias) => {
|
|
40
|
-
it('allocates no history and records no temporal pass', async () => {
|
|
41
|
-
const renderer = await createRenderer(canvas(), { rhi }, { shaderManifestUrl: manifest });
|
|
42
|
-
const ready = await renderer.ready;
|
|
43
|
-
expect(ready.ok).toBe(true);
|
|
44
|
-
const world = new World();
|
|
45
|
-
const attached = renderer.attachWorld(world);
|
|
46
|
-
expect(attached.ok).toBe(true);
|
|
47
|
-
world
|
|
48
|
-
.spawn(
|
|
49
|
-
{ component: Transform, data: { pos: [0, 0, 5] } },
|
|
50
|
-
{
|
|
51
|
-
component: Camera,
|
|
52
|
-
data: { ...perspective({ fov: Math.PI / 4, aspect: 1 }), antialias },
|
|
53
|
-
},
|
|
54
|
-
)
|
|
55
|
-
.unwrap();
|
|
56
|
-
world.update(1 / 60).unwrap();
|
|
57
|
-
expect(renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 }).ok).toBe(true);
|
|
58
|
-
|
|
59
|
-
expect(renderer.renderScene.temporal).toMatchObject({
|
|
60
|
-
enabled: false,
|
|
61
|
-
status: 'off',
|
|
62
|
-
historyBytes: 0,
|
|
63
|
-
historyOwnerGeneration: 0,
|
|
64
|
-
});
|
|
65
|
-
expect(renderer.perFramePassNames).not.toContain('scene-temporal-clear');
|
|
66
|
-
expect(renderer.perFramePassNames).not.toContain('taa-resolve');
|
|
67
|
-
renderer.dispose();
|
|
68
|
-
});
|
|
69
|
-
});
|