@forgeax/engine-runtime 0.1.4 → 0.1.7
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 +65 -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 +7 -4
- 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 +20 -82
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +107 -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 +608 -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 +24 -9
- 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 +1227 -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 +31 -511
- 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 +21 -25
- 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 +117 -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 +65 -559
- 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 +61 -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 +56 -260
- 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 +15 -14
- 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 +68 -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 +35 -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 +51 -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
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
// Consolidated by feat-20260609-test-pool-startup-reduction-merge-tiny-test-files
|
|
3
3
|
// biome-ignore-all lint/complexity/noUselessLoneBlockStatements: block-scope isolation between merged source files (consolidation paradigm)
|
|
4
4
|
//
|
|
5
|
-
// Source files (N=
|
|
5
|
+
// Source files (N=9):
|
|
6
6
|
// - packages/runtime/src/__tests__/builder-topology-stripIndexFormat.test.ts
|
|
7
7
|
// - packages/runtime/src/__tests__/frustum-culling.test.ts
|
|
8
8
|
// - packages/runtime/src/__tests__/instances.test.ts
|
|
9
9
|
// - packages/runtime/src/__tests__/mesh-gpu-handles-vertex-only.test.ts
|
|
10
10
|
// - packages/runtime/src/__tests__/mesh-ssbo-grow.test.ts
|
|
11
11
|
// - packages/runtime/src/__tests__/mesh-update-no-leak.test.ts
|
|
12
|
+
// - packages/runtime/src/__tests__/validate-mesh-topology.test.ts
|
|
12
13
|
// - packages/runtime/src/__tests__/instances-with-submeshes.test.ts
|
|
14
|
+
// - packages/runtime/src/__tests__/mesh-asset-submeshes-validation.test.ts
|
|
13
15
|
//
|
|
14
16
|
// feat-20260704-runtime-tier1-decomposition (w6): the geometry-pure blocks
|
|
15
17
|
// (geometry-tangent / geometry-winding / geometry / vertex-attribute-layout)
|
|
@@ -20,66 +22,91 @@
|
|
|
20
22
|
// Paradigm: each block-scoped describe('<source-filename>.test.ts', ...) preserves
|
|
21
23
|
// source as ancestorTitles[0]. Top-level imports merged + deduped.
|
|
22
24
|
|
|
25
|
+
import type { AssetRuntimeError } from '@forgeax/engine-assets-runtime';
|
|
26
|
+
import { AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
23
27
|
import type { World as WorldType } from '@forgeax/engine-ecs';
|
|
24
28
|
import { World } from '@forgeax/engine-ecs';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import type { Renderer as RendererType } from '@forgeax/engine-render';
|
|
28
|
-
import type { RenderAssetPort, RenderError, SkinError } from '@forgeax/engine-render/internal';
|
|
29
|
-
import {
|
|
30
|
-
Camera,
|
|
31
|
-
createMeshSsboGrowController,
|
|
32
|
-
Instances,
|
|
33
|
-
MeshFilter,
|
|
34
|
-
MeshRenderer,
|
|
35
|
-
} from '@forgeax/engine-render/internal';
|
|
29
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
30
|
+
import type { RenderError, Renderer as RendererType, SkinError } from '@forgeax/engine-render';
|
|
36
31
|
import type { PipelineLayout, RenderPipeline, RhiDevice, ShaderModule } from '@forgeax/engine-rhi';
|
|
37
32
|
import { Transform } from '@forgeax/engine-scene';
|
|
38
|
-
import type { AssetErrorDetail, Handle, MeshAsset } from '@forgeax/engine-types';
|
|
39
|
-
import { ASSET_ERROR_HINTS,
|
|
33
|
+
import type { AssetErrorDetail, Handle, MaterialAsset, MeshAsset } from '@forgeax/engine-types';
|
|
34
|
+
import { ASSET_ERROR_HINTS, AssetError, unwrapHandle } from '@forgeax/engine-types';
|
|
40
35
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
41
36
|
|
|
42
37
|
// feat-20260704-runtime-tier1-decomposition M2 / w12: reconstitute the
|
|
43
38
|
// eliminated top-level RuntimeError aggregate union (D-3) as a test-local alias
|
|
44
39
|
// so the errorRegistry.fire mock callback signatures below stay byte-identical
|
|
45
|
-
// (AC-09). Equal to RenderError |
|
|
46
|
-
type RuntimeLayerError = RenderError |
|
|
40
|
+
// (AC-09). Equal to RenderError | AssetRuntimeError | SkinError.
|
|
41
|
+
type RuntimeLayerError = RenderError | AssetRuntimeError | SkinError;
|
|
47
42
|
|
|
43
|
+
import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
44
|
+
import { propagateTransforms } from '@forgeax/engine-scene';
|
|
45
|
+
import { buildMeshAttributeMapForUvSets } from '../../../geometry/src/vertex-attribute-layout';
|
|
46
|
+
import { createMeshSsboGrowController } from '../../../render/src/assembly/factory';
|
|
47
|
+
import { Camera } from '../../../render/src/components/camera';
|
|
48
|
+
import { Instances } from '../../../render/src/components/instances';
|
|
49
|
+
import { MeshFilter } from '../../../render/src/components/mesh-filter';
|
|
50
|
+
import { MeshRenderer } from '../../../render/src/components/mesh-renderer';
|
|
51
|
+
import type { MeshGpuHandles } from '../../../render/src/device/gpu-residency';
|
|
52
|
+
import { GpuResidencyCache } from '../../../render/src/device/gpu-residency';
|
|
53
|
+
import type { GpuBuffer } from '../../../render/src/gpu-resource';
|
|
48
54
|
import type {
|
|
49
|
-
ExtractedFrame,
|
|
50
|
-
GpuBuffer,
|
|
51
|
-
MeshGpuHandles,
|
|
52
55
|
PipelineBuilderContext,
|
|
53
56
|
PipelineBuilderShaderModuleFactory,
|
|
54
|
-
} from '
|
|
57
|
+
} from '../../../render/src/pipeline-builder';
|
|
58
|
+
import { buildPipelineForMaterialShader } from '../../../render/src/pipeline-builder';
|
|
55
59
|
import {
|
|
56
|
-
buildPipelineForMaterialShader,
|
|
57
60
|
ensureMeshSsboCapacity,
|
|
58
|
-
extractFrame,
|
|
59
|
-
GpuResourceStore,
|
|
60
61
|
isMeshSsboDevMode,
|
|
61
|
-
prepareExtractContext,
|
|
62
62
|
setMeshSsboDevModeProbeForTests,
|
|
63
|
-
} from '
|
|
64
|
-
import {
|
|
65
|
-
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
63
|
+
} from '../../../render/src/record/mesh-ssbo';
|
|
64
|
+
import type { ExtractedFrame } from '../../../render/src/render-system-extract';
|
|
65
|
+
import { extractFrame, prepareExtractContext } from '../../../render/src/render-system-extract';
|
|
66
|
+
import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
67
|
+
import { drawWithOwners } from './renderer-test-utils';
|
|
68
|
+
|
|
69
|
+
type RendererErrorObservation = {
|
|
70
|
+
readonly code: string;
|
|
71
|
+
readonly detail?: unknown;
|
|
72
|
+
readonly hint?: string;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function unwrapRendererError(value: unknown): RendererErrorObservation {
|
|
76
|
+
let current = value as RendererErrorObservation;
|
|
77
|
+
while (
|
|
78
|
+
current.detail !== undefined &&
|
|
79
|
+
typeof current.detail === 'object' &&
|
|
80
|
+
current.detail !== null
|
|
81
|
+
) {
|
|
82
|
+
const cause = (current.detail as { cause?: unknown }).cause;
|
|
83
|
+
if (
|
|
84
|
+
cause === undefined ||
|
|
85
|
+
typeof cause !== 'object' ||
|
|
86
|
+
cause === null ||
|
|
87
|
+
typeof (cause as { code?: unknown }).code !== 'string'
|
|
88
|
+
) {
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
current = cause as RendererErrorObservation;
|
|
81
92
|
}
|
|
82
|
-
return
|
|
93
|
+
return current;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function subscribeRendererErrors(
|
|
97
|
+
renderer: RendererType,
|
|
98
|
+
listener: (error: RendererErrorObservation) => void,
|
|
99
|
+
): () => void {
|
|
100
|
+
return renderer.subscribe((event) => {
|
|
101
|
+
if (event.kind === 'error') listener(unwrapRendererError(event.error));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function drawPublished(renderer: RendererType, world: WorldType) {
|
|
106
|
+
const attached = renderer.attach(world);
|
|
107
|
+
if (!attached.ok) throw attached.error;
|
|
108
|
+
world.update().unwrap();
|
|
109
|
+
return drawWithOwners(renderer, world);
|
|
83
110
|
}
|
|
84
111
|
|
|
85
112
|
{
|
|
@@ -334,17 +361,25 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
334
361
|
return { fov, aspect, near, far, projection: 0, left: -1, right: 1, bottom: -1, top: 1 };
|
|
335
362
|
}
|
|
336
363
|
|
|
337
|
-
function registerMesh(
|
|
364
|
+
function registerMesh(
|
|
365
|
+
world: World,
|
|
366
|
+
assets: AssetRegistry,
|
|
367
|
+
aabb: Float32Array,
|
|
368
|
+
): Handle<'MeshAsset', 'shared'> {
|
|
338
369
|
// Minimal mesh: 1 triangle at origin with AABB
|
|
339
370
|
const vertices = new Float32Array([
|
|
340
371
|
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
|
|
341
372
|
1, 0, 0, 0, 0,
|
|
342
373
|
]);
|
|
343
|
-
|
|
374
|
+
const positions = new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]);
|
|
375
|
+
// catalog recomputes the AABB from positions (withMeshAabb), matching the
|
|
376
|
+
// prior register() behavior; mint the augmented payload on the world so
|
|
377
|
+
// the extract-stage frustum cull (via resolveAssetHandle) reads .aabb.
|
|
378
|
+
const result = assets.catalog<MeshAsset>(AssetGuid.format(AssetGuid.random()), {
|
|
344
379
|
kind: 'mesh',
|
|
345
380
|
vertices,
|
|
346
381
|
indices: new Uint16Array([0, 1, 2]),
|
|
347
|
-
attributes: { position:
|
|
382
|
+
attributes: { position: positions },
|
|
348
383
|
aabb,
|
|
349
384
|
materialSlots: [{ slotName: 'Default' }],
|
|
350
385
|
submeshes: [
|
|
@@ -357,10 +392,15 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
357
392
|
},
|
|
358
393
|
],
|
|
359
394
|
});
|
|
395
|
+
if (!result.ok) throw new Error('catalog failed');
|
|
396
|
+
return world.allocSharedRef('MeshAsset', result.value);
|
|
360
397
|
}
|
|
361
398
|
|
|
362
|
-
function registerUnlitMaterial(
|
|
363
|
-
|
|
399
|
+
function registerUnlitMaterial(
|
|
400
|
+
world: World,
|
|
401
|
+
assets: AssetRegistry,
|
|
402
|
+
): Handle<'MaterialAsset', 'shared'> {
|
|
403
|
+
const result = assets.catalog<MaterialAsset>(AssetGuid.format(AssetGuid.random()), {
|
|
364
404
|
kind: 'material',
|
|
365
405
|
passes: [
|
|
366
406
|
{
|
|
@@ -371,32 +411,23 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
371
411
|
],
|
|
372
412
|
values: { baseColor: [1, 1, 1] },
|
|
373
413
|
});
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
function makeRenderAssetPort(): RenderAssetPort {
|
|
377
|
-
const shaderRegistry = makeMockShaderRegistry();
|
|
378
|
-
return {
|
|
379
|
-
epoch: 0,
|
|
380
|
-
shaderRegistry,
|
|
381
|
-
lookup: () => undefined,
|
|
382
|
-
guidOf: () => undefined,
|
|
383
|
-
materialShaderTextureFieldNames: () => [],
|
|
384
|
-
};
|
|
414
|
+
if (!result.ok) throw new Error('catalog failed');
|
|
415
|
+
return world.allocSharedRef('MaterialAsset', result.value);
|
|
385
416
|
}
|
|
386
417
|
|
|
387
418
|
function makeWorldWithAssets(): {
|
|
388
419
|
world: World;
|
|
389
|
-
assets:
|
|
420
|
+
assets: AssetRegistry;
|
|
390
421
|
meshHandle: Handle<'MeshAsset', 'shared'>;
|
|
391
422
|
matHandle: Handle<'MaterialAsset', 'shared'>;
|
|
392
423
|
} {
|
|
393
424
|
const world = new World();
|
|
394
|
-
const assets =
|
|
425
|
+
const assets = new AssetRegistry(makeMockShaderRegistry());
|
|
395
426
|
|
|
396
427
|
// AABB: unit cube [-1,1] in each axis
|
|
397
428
|
const aabb = new Float32Array([-1, -1, -1, 1, 1, 1]);
|
|
398
|
-
const meshHandle = registerMesh(world, aabb);
|
|
399
|
-
const matHandle = registerUnlitMaterial(world);
|
|
429
|
+
const meshHandle = registerMesh(world, assets, aabb);
|
|
430
|
+
const matHandle = registerUnlitMaterial(world, assets);
|
|
400
431
|
return { world, assets, meshHandle, matHandle };
|
|
401
432
|
}
|
|
402
433
|
|
|
@@ -485,14 +516,15 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
485
516
|
|
|
486
517
|
it('(4) entity with no position attribute (AABB is inverted-infinity) is always visible', () => {
|
|
487
518
|
const world = new World();
|
|
488
|
-
const assets =
|
|
519
|
+
const assets = new AssetRegistry(makeMockShaderRegistry());
|
|
489
520
|
|
|
490
|
-
//
|
|
521
|
+
// Catalog a mesh with NO position attribute -> computeAABB returns empty box.
|
|
522
|
+
// The entity should be always-visible even far away from camera.
|
|
491
523
|
const vertices = new Float32Array([
|
|
492
524
|
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
|
|
493
525
|
1, 0, 0, 0, 0,
|
|
494
526
|
]);
|
|
495
|
-
const
|
|
527
|
+
const meshResult = assets.catalog<MeshAsset>(AssetGuid.format(AssetGuid.random()), {
|
|
496
528
|
kind: 'mesh',
|
|
497
529
|
vertices,
|
|
498
530
|
indices: new Uint16Array([0, 1, 2]),
|
|
@@ -508,7 +540,9 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
508
540
|
},
|
|
509
541
|
],
|
|
510
542
|
});
|
|
511
|
-
|
|
543
|
+
if (!meshResult.ok) throw new Error('catalog failed');
|
|
544
|
+
const meshHandle = world.allocSharedRef('MeshAsset', meshResult.value);
|
|
545
|
+
const matHandle = registerUnlitMaterial(world, assets);
|
|
512
546
|
|
|
513
547
|
spawnCameraAt(world, 0, 0, 5);
|
|
514
548
|
world
|
|
@@ -527,12 +561,13 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
527
561
|
|
|
528
562
|
it('(4b) entity with inverted-infinity AABB (no position attribute) is always visible', () => {
|
|
529
563
|
const { world, assets, matHandle } = makeWorldWithAssets();
|
|
530
|
-
//
|
|
564
|
+
// Catalog a mesh with NO position attribute -> computeAABB returns
|
|
565
|
+
// inverted-infinity empty box, which the culling path treats as always-visible.
|
|
531
566
|
const vertices = new Float32Array([
|
|
532
567
|
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
|
|
533
568
|
1, 0, 0, 0, 0,
|
|
534
569
|
]);
|
|
535
|
-
const
|
|
570
|
+
const meshResult = assets.catalog<MeshAsset>(AssetGuid.format(AssetGuid.random()), {
|
|
536
571
|
kind: 'mesh',
|
|
537
572
|
vertices,
|
|
538
573
|
indices: new Uint16Array([0, 1, 2]),
|
|
@@ -548,6 +583,8 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
548
583
|
},
|
|
549
584
|
],
|
|
550
585
|
});
|
|
586
|
+
if (!meshResult.ok) throw new Error('catalog failed');
|
|
587
|
+
const meshHandle = world.allocSharedRef('MeshAsset', meshResult.value);
|
|
551
588
|
|
|
552
589
|
spawnCameraAt(world, 0, 0, 5);
|
|
553
590
|
spawnEntity(world, meshHandle, matHandle, 100, 100, 100);
|
|
@@ -666,18 +703,25 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
666
703
|
// Test path note: project convention places runtime tests under
|
|
667
704
|
// `src/__tests__/` (see `instances.test-d.ts` header for the rationale).
|
|
668
705
|
|
|
669
|
-
|
|
670
|
-
|
|
706
|
+
interface CollectedError {
|
|
707
|
+
readonly code: string;
|
|
708
|
+
readonly detail: unknown;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function makeHarness(): { world: World; collected: CollectedError[] } {
|
|
712
|
+
const collected: CollectedError[] = [];
|
|
713
|
+
const world = new World();
|
|
714
|
+
return { world, collected };
|
|
671
715
|
}
|
|
672
716
|
|
|
673
717
|
describe('w14 - Instances { transforms: array<f32> } schema (AC-06)', () => {
|
|
674
|
-
it('
|
|
718
|
+
it('Instances.schema.transforms is the array<f32> keyword (no legacy buffer/count fields)', () => {
|
|
675
719
|
expect(Instances.name).toBe('Instances');
|
|
676
|
-
expect(Object.keys(
|
|
677
|
-
expect(
|
|
720
|
+
expect(Object.keys(Instances.fields).length).toBe(1);
|
|
721
|
+
expect(Instances.fields.transforms?.type).toBe('array<f32>');
|
|
678
722
|
// Legacy fields retired in M3.
|
|
679
|
-
expect(
|
|
680
|
-
expect(
|
|
723
|
+
expect(Instances.fields.buffer).toBeUndefined();
|
|
724
|
+
expect(Instances.fields.count).toBeUndefined();
|
|
681
725
|
});
|
|
682
726
|
|
|
683
727
|
it('Instances component carries no per-component stride option (decision §2.3 migration)', () => {
|
|
@@ -688,7 +732,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
688
732
|
});
|
|
689
733
|
|
|
690
734
|
it('(legal) spawn with 16 f32 multiples succeeds; snapshot length reflects element count', () => {
|
|
691
|
-
const { world } = makeHarness();
|
|
735
|
+
const { world, collected } = makeHarness();
|
|
692
736
|
const N = 4;
|
|
693
737
|
const transforms = new Float32Array(N * 16);
|
|
694
738
|
// Fill column 3 with distinguishable translations so the read path can
|
|
@@ -698,6 +742,10 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
698
742
|
transforms[i * 16 + 15] = 1;
|
|
699
743
|
}
|
|
700
744
|
const e = world.spawn({ component: Instances, data: { transforms } }).unwrap();
|
|
745
|
+
// No stride error fires on the ECS write path post-feat-20260515.
|
|
746
|
+
expect(collected.filter((c) => c.code === 'instance-transforms-stride-mismatch').length).toBe(
|
|
747
|
+
0,
|
|
748
|
+
);
|
|
701
749
|
const snap = world.get(e, Instances).unwrap().transforms;
|
|
702
750
|
expect(snap.length).toBe(N * 16);
|
|
703
751
|
// Sanity: column 3 translation x of instance 2 reads back as 3.
|
|
@@ -705,10 +753,15 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
705
753
|
});
|
|
706
754
|
|
|
707
755
|
it('(ECS layer is stride-agnostic) spawn with 17 f32 does NOT route a stride error from ECS', () => {
|
|
708
|
-
const { world } = makeHarness();
|
|
756
|
+
const { world, collected } = makeHarness();
|
|
709
757
|
world.spawn({ component: Instances, data: { transforms: new Float32Array(17) } });
|
|
710
758
|
// The runtime-side defensive lives in render-system-extract; the ECS
|
|
711
|
-
// write path
|
|
759
|
+
// write path itself MUST NOT route any stride error code.
|
|
760
|
+
expect(collected.filter((c) => c.code === 'instance-transforms-stride-mismatch').length).toBe(
|
|
761
|
+
0,
|
|
762
|
+
);
|
|
763
|
+
// Negative anchor for the retired ECS-layer code.
|
|
764
|
+
expect(collected.filter((c) => c.code === 'managed-array-stride-mismatch').length).toBe(0);
|
|
712
765
|
});
|
|
713
766
|
});
|
|
714
767
|
}
|
|
@@ -717,7 +770,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
717
770
|
// --- from mesh-gpu-handles-vertex-only.test.ts ---
|
|
718
771
|
// mesh-gpu-handles-vertex-only.test - M2 / w3 (TDD).
|
|
719
772
|
//
|
|
720
|
-
// Coverage (vertex-only mesh upload chain through
|
|
773
|
+
// Coverage (vertex-only mesh upload chain through GpuResidencyCache):
|
|
721
774
|
// (a) MeshGpuHandles type carries `vertexCount: number` + `indexed: boolean`
|
|
722
775
|
// and `indexBuffer: Buffer | null` (type-level via a const-assigned literal
|
|
723
776
|
// that must compile).
|
|
@@ -762,15 +815,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
762
815
|
createdLabels: string[];
|
|
763
816
|
}
|
|
764
817
|
|
|
765
|
-
function canonicalAttributes(vertexCount: number): MeshAsset['attributes'] {
|
|
766
|
-
return {
|
|
767
|
-
position: new Float32Array(vertexCount * 3),
|
|
768
|
-
normal: new Float32Array(vertexCount * 3),
|
|
769
|
-
uv: new Float32Array(vertexCount * 2),
|
|
770
|
-
tangent: new Float32Array(vertexCount * 4),
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
|
|
774
818
|
// biome-ignore lint/suspicious/noExplicitAny: opaque mock device surface
|
|
775
819
|
function makeMockDevice(probe: BufferProbe): any {
|
|
776
820
|
const okShim = <T>(v: T) => ({ ok: true as const, value: v });
|
|
@@ -792,7 +836,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
792
836
|
kind: 'mesh',
|
|
793
837
|
vertices,
|
|
794
838
|
indices: new Uint16Array([0, 1, 2, 0, 2, 3]),
|
|
795
|
-
attributes:
|
|
839
|
+
attributes: buildMeshAttributeMapForUvSets(1),
|
|
796
840
|
submeshes: [
|
|
797
841
|
{
|
|
798
842
|
indexOffset: 0,
|
|
@@ -809,7 +853,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
809
853
|
return {
|
|
810
854
|
kind: 'mesh',
|
|
811
855
|
vertices,
|
|
812
|
-
attributes:
|
|
856
|
+
attributes: buildMeshAttributeMapForUvSets(1),
|
|
813
857
|
submeshes: [
|
|
814
858
|
{
|
|
815
859
|
indexOffset: 0,
|
|
@@ -834,12 +878,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
834
878
|
iboBytes: 0,
|
|
835
879
|
indexCount: 0,
|
|
836
880
|
indexFormat: 'uint16',
|
|
837
|
-
|
|
838
|
-
position: new Float32Array(6),
|
|
839
|
-
normal: new Float32Array(6),
|
|
840
|
-
uv: new Float32Array(4),
|
|
841
|
-
tangent: new Float32Array(8),
|
|
842
|
-
}),
|
|
881
|
+
layout: '12F',
|
|
843
882
|
vertexCount: 2,
|
|
844
883
|
indexed: false,
|
|
845
884
|
topology: 'line-list',
|
|
@@ -854,7 +893,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
854
893
|
const probe: BufferProbe = { createdLabels: [] };
|
|
855
894
|
const device = makeMockDevice(probe);
|
|
856
895
|
const world = new World();
|
|
857
|
-
const store = new
|
|
896
|
+
const store = new GpuResidencyCache();
|
|
858
897
|
store.configureGpuDevice(
|
|
859
898
|
device,
|
|
860
899
|
undefined,
|
|
@@ -885,7 +924,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
885
924
|
const probe: BufferProbe = { createdLabels: [] };
|
|
886
925
|
const device = makeMockDevice(probe);
|
|
887
926
|
const world = new World();
|
|
888
|
-
const store = new
|
|
927
|
+
const store = new GpuResidencyCache();
|
|
889
928
|
store.configureGpuDevice(
|
|
890
929
|
device,
|
|
891
930
|
undefined,
|
|
@@ -1652,7 +1691,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1652
1691
|
// (c) updateMesh on non-existent handle is a silent no-op (guards check
|
|
1653
1692
|
// meshGpuHandles.has + device existence).
|
|
1654
1693
|
|
|
1655
|
-
// feat-20260601-gpu-
|
|
1694
|
+
// feat-20260601-device/gpu-residency-extraction M1: updateMesh moved to the GPU
|
|
1656
1695
|
// store. The no-leak invariant is now structural-by-construction (the store
|
|
1657
1696
|
// holds no registry reference, D-2), but the test still asserts that driving
|
|
1658
1697
|
// updateMesh never grows the registry. Without a wired device the store
|
|
@@ -1666,7 +1705,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1666
1705
|
kind: 'mesh',
|
|
1667
1706
|
vertices,
|
|
1668
1707
|
indices,
|
|
1669
|
-
attributes:
|
|
1708
|
+
attributes: buildMeshAttributeMapForUvSets(1),
|
|
1670
1709
|
aabb: new Float32Array(6),
|
|
1671
1710
|
submeshes: [
|
|
1672
1711
|
{
|
|
@@ -1705,7 +1744,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1705
1744
|
const world = new World();
|
|
1706
1745
|
const mesh = makeQuadMesh();
|
|
1707
1746
|
const handle = world.allocSharedRef('MeshAsset', mesh);
|
|
1708
|
-
const store = new
|
|
1747
|
+
const store = new GpuResidencyCache();
|
|
1709
1748
|
void unwrapHandle(handle);
|
|
1710
1749
|
|
|
1711
1750
|
// Save the initial live shared-ref slot count.
|
|
@@ -1728,7 +1767,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1728
1767
|
const world = new World();
|
|
1729
1768
|
const mesh = makeQuadMesh();
|
|
1730
1769
|
const handle = world.allocSharedRef('MeshAsset', mesh);
|
|
1731
|
-
const store = new
|
|
1770
|
+
const store = new GpuResidencyCache();
|
|
1732
1771
|
|
|
1733
1772
|
const before = world.sharedRefs._liveCount();
|
|
1734
1773
|
if (!(mesh.indices instanceof Uint16Array)) return;
|
|
@@ -1741,7 +1780,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1741
1780
|
const world = new World();
|
|
1742
1781
|
const mesh = makeQuadMesh();
|
|
1743
1782
|
const handle = world.allocSharedRef('MeshAsset', mesh);
|
|
1744
|
-
const store = new
|
|
1783
|
+
const store = new GpuResidencyCache();
|
|
1745
1784
|
const id = unwrapHandle(handle);
|
|
1746
1785
|
|
|
1747
1786
|
// updateMesh with larger data (expansion path)
|
|
@@ -1749,7 +1788,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1749
1788
|
if (!(bigger.indices instanceof Uint16Array)) return;
|
|
1750
1789
|
store.updateMesh(handle, bigger.vertices, bigger.indices);
|
|
1751
1790
|
// The id should still be the same — no new mint was performed.
|
|
1752
|
-
const result =
|
|
1791
|
+
const result = resolveAssetHandle<MeshAsset>(world, handle);
|
|
1753
1792
|
expect(result.ok).toBe(true);
|
|
1754
1793
|
if (!result.ok) return;
|
|
1755
1794
|
// The payload behind the handle is unchanged (updateMesh only touches the
|
|
@@ -1760,6 +1799,231 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1760
1799
|
});
|
|
1761
1800
|
}
|
|
1762
1801
|
|
|
1802
|
+
{
|
|
1803
|
+
// --- from validate-mesh-topology.test.ts ---
|
|
1804
|
+
// validate-mesh-topology.test - feat-20260604-mesh-topology-debug-draw M5 / w12 (TDD red).
|
|
1805
|
+
//
|
|
1806
|
+
// Coverage (AssetRegistry.catalog -> validateMeshPayload topology rules, AC-10):
|
|
1807
|
+
// (a) strip topology (line-strip / triangle-strip) with NO indices ->
|
|
1808
|
+
// Result.err code='asset-invalid-value', detail.field='topology' +
|
|
1809
|
+
// detail.value=<topology>, .hint non-empty (strip needs indices to
|
|
1810
|
+
// resolve stripIndexFormat).
|
|
1811
|
+
// (b) empty geometry (vertices.length === 0) with a non-default topology
|
|
1812
|
+
// (topology defined && !== 'triangle-list') -> same err shape.
|
|
1813
|
+
// (c) LEGAL combos pass (Result.ok): indexed point-list, indexed line-list,
|
|
1814
|
+
// vertex-only line-list, strip + indices.
|
|
1815
|
+
// (e) D-A4: a vertex-only mesh (indices omitted) does NOT trip the
|
|
1816
|
+
// :588 maxIndex+1===vertexCount invariant (it is index-only).
|
|
1817
|
+
//
|
|
1818
|
+
// Anchors: requirements AC-10 + constraint #8 (reuse asset-invalid-value, no new code);
|
|
1819
|
+
// plan-strategy D-A2 (illegal set = strip-no-indices + empty-non-default-topology);
|
|
1820
|
+
// plan-strategy D-A4 (maxIndex invariant gated on indices present);
|
|
1821
|
+
// research Finding 10 (validateMeshPayload :565-601) + Finding 5 (asset-invalid-value precedent).
|
|
1822
|
+
|
|
1823
|
+
function reg(): AssetRegistry {
|
|
1824
|
+
return new AssetRegistry(makeMockShaderRegistry());
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
describe('validateMeshPayload topology rules (M5 w12 - AC-10)', () => {
|
|
1828
|
+
it('(a1) line-strip with no indices -> asset-invalid-value + detail.field=topology + non-empty hint', () => {
|
|
1829
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1830
|
+
kind: 'mesh',
|
|
1831
|
+
vertices: new Float32Array(2 * 12),
|
|
1832
|
+
attributes: {},
|
|
1833
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1834
|
+
submeshes: [
|
|
1835
|
+
{
|
|
1836
|
+
indexOffset: 0,
|
|
1837
|
+
indexCount: 0,
|
|
1838
|
+
vertexCount: 0,
|
|
1839
|
+
materialSlot: 0,
|
|
1840
|
+
topology: 'line-strip',
|
|
1841
|
+
},
|
|
1842
|
+
],
|
|
1843
|
+
} as MeshAsset);
|
|
1844
|
+
expect(result.ok).toBe(false);
|
|
1845
|
+
if (!result.ok) {
|
|
1846
|
+
expect(result.error.code).toBe('asset-invalid-value');
|
|
1847
|
+
const d = result.error.detail as { field: string; value: string };
|
|
1848
|
+
expect(d.field).toBe('submeshes[0].topology');
|
|
1849
|
+
expect(d.value).toBe('line-strip');
|
|
1850
|
+
expect(result.error.hint.length).toBeGreaterThan(0);
|
|
1851
|
+
}
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
it('(a2) triangle-strip with no indices -> asset-invalid-value + detail.value=triangle-strip', () => {
|
|
1855
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1856
|
+
kind: 'mesh',
|
|
1857
|
+
vertices: new Float32Array(3 * 12),
|
|
1858
|
+
attributes: {},
|
|
1859
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1860
|
+
submeshes: [
|
|
1861
|
+
{
|
|
1862
|
+
indexOffset: 0,
|
|
1863
|
+
indexCount: 0,
|
|
1864
|
+
vertexCount: 0,
|
|
1865
|
+
materialSlot: 0,
|
|
1866
|
+
topology: 'triangle-strip',
|
|
1867
|
+
},
|
|
1868
|
+
],
|
|
1869
|
+
} as MeshAsset);
|
|
1870
|
+
expect(result.ok).toBe(false);
|
|
1871
|
+
if (!result.ok) {
|
|
1872
|
+
expect(result.error.code).toBe('asset-invalid-value');
|
|
1873
|
+
const d = result.error.detail as { field: string; value: string };
|
|
1874
|
+
expect(d.field).toBe('submeshes[0].topology');
|
|
1875
|
+
expect(d.value).toBe('triangle-strip');
|
|
1876
|
+
expect(result.error.hint.length).toBeGreaterThan(0);
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1879
|
+
|
|
1880
|
+
it('(b) empty geometry with non-default topology -> asset-invalid-value', () => {
|
|
1881
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1882
|
+
kind: 'mesh',
|
|
1883
|
+
vertices: new Float32Array(0),
|
|
1884
|
+
attributes: {},
|
|
1885
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1886
|
+
submeshes: [
|
|
1887
|
+
{
|
|
1888
|
+
indexOffset: 0,
|
|
1889
|
+
indexCount: 0,
|
|
1890
|
+
vertexCount: 0,
|
|
1891
|
+
materialSlot: 0,
|
|
1892
|
+
topology: 'line-list',
|
|
1893
|
+
},
|
|
1894
|
+
],
|
|
1895
|
+
} as MeshAsset);
|
|
1896
|
+
expect(result.ok).toBe(false);
|
|
1897
|
+
if (!result.ok) {
|
|
1898
|
+
expect(result.error.code).toBe('asset-invalid-value');
|
|
1899
|
+
const d = result.error.detail as { field: string; value: string };
|
|
1900
|
+
expect(d.field).toBe('submeshes[0].topology');
|
|
1901
|
+
expect(d.value).toBe('line-list');
|
|
1902
|
+
expect(result.error.hint.length).toBeGreaterThan(0);
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1906
|
+
it('(b-legal) empty geometry with default/omitted topology stays legal (Result.ok)', () => {
|
|
1907
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1908
|
+
kind: 'mesh',
|
|
1909
|
+
vertices: new Float32Array(0),
|
|
1910
|
+
attributes: {},
|
|
1911
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1912
|
+
submeshes: [
|
|
1913
|
+
{
|
|
1914
|
+
indexOffset: 0,
|
|
1915
|
+
indexCount: 0,
|
|
1916
|
+
vertexCount: 0,
|
|
1917
|
+
materialSlot: 0,
|
|
1918
|
+
topology: 'triangle-list',
|
|
1919
|
+
},
|
|
1920
|
+
],
|
|
1921
|
+
} as MeshAsset);
|
|
1922
|
+
expect(result.ok).toBe(true);
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1925
|
+
it('(c1) indexed point-list is legal (Result.ok)', () => {
|
|
1926
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1927
|
+
kind: 'mesh',
|
|
1928
|
+
vertices: new Float32Array(3 * 12),
|
|
1929
|
+
indices: new Uint16Array([0, 1, 2]),
|
|
1930
|
+
attributes: {},
|
|
1931
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1932
|
+
submeshes: [
|
|
1933
|
+
{
|
|
1934
|
+
indexOffset: 0,
|
|
1935
|
+
indexCount: 3,
|
|
1936
|
+
vertexCount: 0,
|
|
1937
|
+
materialSlot: 0,
|
|
1938
|
+
topology: 'point-list',
|
|
1939
|
+
},
|
|
1940
|
+
],
|
|
1941
|
+
} as MeshAsset);
|
|
1942
|
+
expect(result.ok).toBe(true);
|
|
1943
|
+
});
|
|
1944
|
+
|
|
1945
|
+
it('(c2) indexed line-list is legal (Result.ok)', () => {
|
|
1946
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1947
|
+
kind: 'mesh',
|
|
1948
|
+
vertices: new Float32Array(2 * 12),
|
|
1949
|
+
indices: new Uint16Array([0, 1]),
|
|
1950
|
+
attributes: {},
|
|
1951
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1952
|
+
submeshes: [
|
|
1953
|
+
{
|
|
1954
|
+
indexOffset: 0,
|
|
1955
|
+
indexCount: 2,
|
|
1956
|
+
vertexCount: 0,
|
|
1957
|
+
materialSlot: 0,
|
|
1958
|
+
topology: 'line-list',
|
|
1959
|
+
},
|
|
1960
|
+
],
|
|
1961
|
+
} as MeshAsset);
|
|
1962
|
+
expect(result.ok).toBe(true);
|
|
1963
|
+
});
|
|
1964
|
+
|
|
1965
|
+
it('(c3) vertex-only line-list (no indices) is legal (Result.ok)', () => {
|
|
1966
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1967
|
+
kind: 'mesh',
|
|
1968
|
+
vertices: new Float32Array(2 * 12),
|
|
1969
|
+
attributes: {},
|
|
1970
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1971
|
+
submeshes: [
|
|
1972
|
+
{
|
|
1973
|
+
indexOffset: 0,
|
|
1974
|
+
indexCount: 0,
|
|
1975
|
+
vertexCount: 0,
|
|
1976
|
+
materialSlot: 0,
|
|
1977
|
+
topology: 'line-list',
|
|
1978
|
+
},
|
|
1979
|
+
],
|
|
1980
|
+
} as MeshAsset);
|
|
1981
|
+
expect(result.ok).toBe(true);
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1984
|
+
it('(c4) triangle-strip WITH indices is legal (Result.ok)', () => {
|
|
1985
|
+
// 4 verts -> 2 triangles via strip; indices reference all 4 (maxIndex+1===vertexCount)
|
|
1986
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
1987
|
+
kind: 'mesh',
|
|
1988
|
+
vertices: new Float32Array(4 * 12),
|
|
1989
|
+
indices: new Uint32Array([0, 1, 2, 3]),
|
|
1990
|
+
attributes: {},
|
|
1991
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
1992
|
+
submeshes: [
|
|
1993
|
+
{
|
|
1994
|
+
indexOffset: 0,
|
|
1995
|
+
indexCount: 4,
|
|
1996
|
+
vertexCount: 0,
|
|
1997
|
+
materialSlot: 0,
|
|
1998
|
+
topology: 'triangle-strip',
|
|
1999
|
+
},
|
|
2000
|
+
],
|
|
2001
|
+
} as MeshAsset);
|
|
2002
|
+
expect(result.ok).toBe(true);
|
|
2003
|
+
});
|
|
2004
|
+
|
|
2005
|
+
it('(e) D-A4: vertex-only triangle-list (no indices) does NOT trip maxIndex invariant', () => {
|
|
2006
|
+
// 3 verts, no indices: the :588 maxIndex+1===vertexCount check must be skipped.
|
|
2007
|
+
const result = reg().catalog(AssetGuid.format(AssetGuid.random()), {
|
|
2008
|
+
kind: 'mesh',
|
|
2009
|
+
vertices: new Float32Array(3 * 12),
|
|
2010
|
+
attributes: {},
|
|
2011
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
2012
|
+
submeshes: [
|
|
2013
|
+
{
|
|
2014
|
+
indexOffset: 0,
|
|
2015
|
+
indexCount: 0,
|
|
2016
|
+
vertexCount: 0,
|
|
2017
|
+
materialSlot: 0,
|
|
2018
|
+
topology: 'triangle-list',
|
|
2019
|
+
},
|
|
2020
|
+
],
|
|
2021
|
+
} as MeshAsset);
|
|
2022
|
+
expect(result.ok).toBe(true);
|
|
2023
|
+
});
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
|
|
1763
2027
|
{
|
|
1764
2028
|
// --- from instances-with-submeshes.test.ts ---
|
|
1765
2029
|
// instances-with-submeshes.test.ts -- unit tests for Instances x submesh
|
|
@@ -1769,15 +2033,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1769
2033
|
// (shared instanceCount, not per-submesh); OOS-5 (per-submesh independent
|
|
1770
2034
|
// instances explicitly excluded).
|
|
1771
2035
|
|
|
1772
|
-
function canonicalAttributes(vertexCount: number): MeshAsset['attributes'] {
|
|
1773
|
-
return {
|
|
1774
|
-
position: new Float32Array(vertexCount * 3),
|
|
1775
|
-
normal: new Float32Array(vertexCount * 3),
|
|
1776
|
-
uv: new Float32Array(vertexCount * 2),
|
|
1777
|
-
tangent: new Float32Array(vertexCount * 4),
|
|
1778
|
-
};
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
2036
|
const ENGINE = '../createRenderer';
|
|
1782
2037
|
|
|
1783
2038
|
interface PassSpies {
|
|
@@ -1827,6 +2082,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1827
2082
|
limits: {},
|
|
1828
2083
|
queue: {
|
|
1829
2084
|
submit: () => undefined,
|
|
2085
|
+
onSubmittedWorkDone: () => Promise.resolve(undefined),
|
|
1830
2086
|
writeBuffer: () => undefined,
|
|
1831
2087
|
writeTexture: () => undefined,
|
|
1832
2088
|
},
|
|
@@ -1878,8 +2134,8 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1878
2134
|
identifier,
|
|
1879
2135
|
sourcePath: `${identifier}.wgsl`,
|
|
1880
2136
|
composedWgsl: '/* stub */',
|
|
1881
|
-
paramSchema:
|
|
1882
|
-
variants:
|
|
2137
|
+
paramSchema: '[]',
|
|
2138
|
+
variants: [],
|
|
1883
2139
|
});
|
|
1884
2140
|
const manifest = {
|
|
1885
2141
|
schemaVersion: '1.0.0',
|
|
@@ -1910,15 +2166,10 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1910
2166
|
};
|
|
1911
2167
|
}
|
|
1912
2168
|
|
|
1913
|
-
|
|
1914
|
-
ready: Promise<void>;
|
|
1915
|
-
draw: (worlds: unknown, opts: { cameraOwner: number; resourceOwner: number }) => void;
|
|
1916
|
-
onError: (cb: (err: { code: string }) => void) => () => void;
|
|
1917
|
-
assets: { register: (asset: unknown) => { ok: boolean; value: unknown } };
|
|
1918
|
-
}
|
|
2169
|
+
type RendererLike = RendererType;
|
|
1919
2170
|
|
|
1920
2171
|
async function importEngine(): Promise<{
|
|
1921
|
-
createRenderer: (
|
|
2172
|
+
createRenderer: (...args: unknown[]) => Promise<{ unwrap(): RendererLike }>;
|
|
1922
2173
|
}> {
|
|
1923
2174
|
return (await import(ENGINE)) as never;
|
|
1924
2175
|
}
|
|
@@ -1941,7 +2192,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1941
2192
|
Instances: unknown;
|
|
1942
2193
|
}> {
|
|
1943
2194
|
return {
|
|
1944
|
-
...(await import('@forgeax/engine-render
|
|
2195
|
+
...(await import('@forgeax/engine-render')),
|
|
1945
2196
|
...(await import('@forgeax/engine-scene')),
|
|
1946
2197
|
} as never;
|
|
1947
2198
|
}
|
|
@@ -1977,12 +2228,11 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
1977
2228
|
}
|
|
1978
2229
|
|
|
1979
2230
|
function twoSubmeshMesh(): MeshAsset {
|
|
1980
|
-
const vertexCount = 6;
|
|
1981
2231
|
return {
|
|
1982
2232
|
kind: 'mesh',
|
|
1983
|
-
vertices: new Float32Array(
|
|
2233
|
+
vertices: new Float32Array(6 * 12),
|
|
1984
2234
|
indices: new Uint16Array([0, 1, 2, 3, 4, 5]),
|
|
1985
|
-
attributes:
|
|
2235
|
+
attributes: buildMeshAttributeMapForUvSets(1),
|
|
1986
2236
|
materialSlots: [{ slotName: 'First' }, { slotName: 'Second' }],
|
|
1987
2237
|
submeshes: [
|
|
1988
2238
|
{
|
|
@@ -2020,14 +2270,15 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2020
2270
|
const { device } = makeMockGPUDevice(spies);
|
|
2021
2271
|
vi.stubGlobal('navigator', { ...baseNavigator, gpu: makeMockGPU(device) });
|
|
2022
2272
|
const { createRenderer } = await importEngine();
|
|
2023
|
-
const renderer =
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2273
|
+
const renderer = (
|
|
2274
|
+
await createRenderer(
|
|
2275
|
+
makeMockCanvas(),
|
|
2276
|
+
{},
|
|
2277
|
+
{
|
|
2278
|
+
shaderManifestUrl: buildManifestDataUrl(),
|
|
2279
|
+
},
|
|
2280
|
+
)
|
|
2281
|
+
).unwrap();
|
|
2031
2282
|
return { renderer };
|
|
2032
2283
|
}
|
|
2033
2284
|
|
|
@@ -2040,7 +2291,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2040
2291
|
const { World } = await importEcs();
|
|
2041
2292
|
const C = await importComponents();
|
|
2042
2293
|
const world = new World();
|
|
2043
|
-
registerRuntimeComponents(world);
|
|
2044
2294
|
const meshHandle = world.allocSharedRef('MeshAsset', meshAsset) as Handle<
|
|
2045
2295
|
'MeshAsset',
|
|
2046
2296
|
'shared'
|
|
@@ -2082,7 +2332,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2082
2332
|
{ component: C.Transform, data: cameraTransform() },
|
|
2083
2333
|
);
|
|
2084
2334
|
world.spawn(
|
|
2085
|
-
{ component: C.DirectionalLight, data: {
|
|
2335
|
+
{ component: C.DirectionalLight, data: {} },
|
|
2086
2336
|
{ component: C.Transform, data: cameraTransform() },
|
|
2087
2337
|
);
|
|
2088
2338
|
world.spawn(
|
|
@@ -2107,14 +2357,14 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2107
2357
|
const spies = makePassSpies();
|
|
2108
2358
|
const { renderer } = await setupRenderer(spies);
|
|
2109
2359
|
const errors: string[] = [];
|
|
2110
|
-
renderer
|
|
2360
|
+
subscribeRendererErrors(renderer, (e) => errors.push(e.code));
|
|
2111
2361
|
|
|
2112
2362
|
const world = await spawnInstancedScene(renderer, twoSubmeshMesh(), 2, 4);
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
renderer
|
|
2363
|
+
drawPublished(renderer, world as WorldType);
|
|
2364
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2365
|
+
spies.draw.mockClear();
|
|
2366
|
+
spies.drawIndexed.mockClear();
|
|
2367
|
+
drawPublished(renderer, world as WorldType);
|
|
2118
2368
|
|
|
2119
2369
|
// Each submesh draws M times (4 instances each, shared instanceCount).
|
|
2120
2370
|
// N=2 submeshes * M=1 draw each = 2 drawIndexed calls.
|
|
@@ -2129,14 +2379,14 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2129
2379
|
const spies = makePassSpies();
|
|
2130
2380
|
const { renderer } = await setupRenderer(spies);
|
|
2131
2381
|
const errors: string[] = [];
|
|
2132
|
-
renderer
|
|
2382
|
+
subscribeRendererErrors(renderer, (e) => errors.push(e.code));
|
|
2133
2383
|
|
|
2134
2384
|
const world = await spawnInstancedScene(renderer, twoSubmeshMesh(), 2, 4);
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
renderer
|
|
2385
|
+
drawPublished(renderer, world as WorldType);
|
|
2386
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2387
|
+
spies.draw.mockClear();
|
|
2388
|
+
spies.drawIndexed.mockClear();
|
|
2389
|
+
drawPublished(renderer, world as WorldType);
|
|
2140
2390
|
|
|
2141
2391
|
// All submesh draws share the same instanceCount (D-8).
|
|
2142
2392
|
// Verify drawIndexed calls carry instanceCount parameter at index 1.
|
|
@@ -2159,13 +2409,12 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2159
2409
|
const spies = makePassSpies();
|
|
2160
2410
|
const { renderer } = await setupRenderer(spies);
|
|
2161
2411
|
const errors: string[] = [];
|
|
2162
|
-
renderer
|
|
2412
|
+
subscribeRendererErrors(renderer, (e) => errors.push(e.code));
|
|
2163
2413
|
|
|
2164
2414
|
// Spawn without Instances component.
|
|
2165
2415
|
const { World } = await importEcs();
|
|
2166
2416
|
const C = await importComponents();
|
|
2167
2417
|
const world = new World();
|
|
2168
|
-
registerRuntimeComponents(world);
|
|
2169
2418
|
const meshHandle = world.allocSharedRef('MeshAsset', twoSubmeshMesh()) as Handle<
|
|
2170
2419
|
'MeshAsset',
|
|
2171
2420
|
'shared'
|
|
@@ -2199,7 +2448,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2199
2448
|
{ component: C.Transform, data: cameraTransform() },
|
|
2200
2449
|
);
|
|
2201
2450
|
world.spawn(
|
|
2202
|
-
{ component: C.DirectionalLight, data: {
|
|
2451
|
+
{ component: C.DirectionalLight, data: {} },
|
|
2203
2452
|
{ component: C.Transform, data: cameraTransform() },
|
|
2204
2453
|
);
|
|
2205
2454
|
world.spawn(
|
|
@@ -2208,13 +2457,11 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2208
2457
|
{ component: C.Transform, data: originTransform() },
|
|
2209
2458
|
);
|
|
2210
2459
|
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
(world as WorldType)
|
|
2216
|
-
|
|
2217
|
-
renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
|
|
2460
|
+
drawPublished(renderer, world as WorldType);
|
|
2461
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2462
|
+
spies.draw.mockClear();
|
|
2463
|
+
spies.drawIndexed.mockClear();
|
|
2464
|
+
drawPublished(renderer, world as WorldType);
|
|
2218
2465
|
|
|
2219
2466
|
// Without Instances, instanceCount defaults to 1.
|
|
2220
2467
|
const indexedCalls = spies.drawIndexed.mock.calls as Array<
|
|
@@ -2240,6 +2487,125 @@ function canonicalMaterialParamSchema(identifier: string): string {
|
|
|
2240
2487
|
// read-side interception); plan-strategy §5.3 key test points "AC-05 three
|
|
2241
2488
|
// triggers".
|
|
2242
2489
|
|
|
2490
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
2491
|
+
|
|
2492
|
+
function freshRegistry() {
|
|
2493
|
+
return new AssetRegistry(
|
|
2494
|
+
// biome-ignore lint/suspicious/noExplicitAny: mock ShaderRegistry
|
|
2495
|
+
{ findMaterialArtifact: () => ({ ok: false }) } as any,
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
function makeMeshPayload(overrides: {
|
|
2500
|
+
submeshes?: {
|
|
2501
|
+
indexOffset: number;
|
|
2502
|
+
indexCount: number;
|
|
2503
|
+
vertexCount: number;
|
|
2504
|
+
topology: string;
|
|
2505
|
+
}[];
|
|
2506
|
+
indices?: Uint16Array;
|
|
2507
|
+
}) {
|
|
2508
|
+
const indices = overrides.indices ?? new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
2509
|
+
const submeshes = overrides.submeshes ?? [
|
|
2510
|
+
{
|
|
2511
|
+
indexOffset: 0,
|
|
2512
|
+
indexCount: 6,
|
|
2513
|
+
vertexCount: 4,
|
|
2514
|
+
topology: 'triangle-list' as const,
|
|
2515
|
+
},
|
|
2516
|
+
];
|
|
2517
|
+
return {
|
|
2518
|
+
kind: 'mesh' as const,
|
|
2519
|
+
vertices: new Float32Array(4 * 12), // 4 verts x 12 floats
|
|
2520
|
+
indices,
|
|
2521
|
+
attributes: {
|
|
2522
|
+
position: new Float32Array(4 * 3),
|
|
2523
|
+
},
|
|
2524
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
2525
|
+
submeshes: submeshes.map((submesh) => ({ ...submesh, materialSlot: 0 })),
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
// ── write-side: submeshes-empty ──────────────────────────────────────────────
|
|
2530
|
+
|
|
2531
|
+
describe('write-side: mesh-asset-submeshes-empty (AC-05 b)', () => {
|
|
2532
|
+
it('register with submeshes: [] yields err with code mesh-asset-submeshes-empty', () => {
|
|
2533
|
+
const registry = freshRegistry();
|
|
2534
|
+
const payload = makeMeshPayload({ submeshes: [] });
|
|
2535
|
+
|
|
2536
|
+
const result = registry.catalog(AssetGuid.format(AssetGuid.random()), payload as never);
|
|
2537
|
+
expect(result.ok).toBe(false);
|
|
2538
|
+
if (!result.ok) {
|
|
2539
|
+
expect(result.error).toBeInstanceOf(AssetError);
|
|
2540
|
+
expect(result.error.code).toBe('mesh-asset-submeshes-empty');
|
|
2541
|
+
expect(result.error.expected).toBeTruthy();
|
|
2542
|
+
expect(result.error.hint).toBeTruthy();
|
|
2543
|
+
expect(result.error.detail).toBeDefined();
|
|
2544
|
+
expect((result.error.detail as { meshAssetGuid?: string }).meshAssetGuid).toBeTruthy();
|
|
2545
|
+
}
|
|
2546
|
+
});
|
|
2547
|
+
});
|
|
2548
|
+
|
|
2549
|
+
// ── write-side: index-range-out-of-bounds ────────────────────────────────────
|
|
2550
|
+
|
|
2551
|
+
describe('write-side: mesh-submesh-index-range-out-of-bounds (AC-05 c)', () => {
|
|
2552
|
+
it('register with submesh index range exceeding index buffer yields err', () => {
|
|
2553
|
+
const registry = freshRegistry();
|
|
2554
|
+
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
2555
|
+
const payload = makeMeshPayload({
|
|
2556
|
+
indices,
|
|
2557
|
+
submeshes: [
|
|
2558
|
+
{
|
|
2559
|
+
indexOffset: 4,
|
|
2560
|
+
indexCount: 4,
|
|
2561
|
+
vertexCount: 4,
|
|
2562
|
+
topology: 'triangle-list' as const,
|
|
2563
|
+
},
|
|
2564
|
+
],
|
|
2565
|
+
});
|
|
2566
|
+
|
|
2567
|
+
const result = registry.catalog(AssetGuid.format(AssetGuid.random()), payload as never);
|
|
2568
|
+
expect(result.ok).toBe(false);
|
|
2569
|
+
if (!result.ok) {
|
|
2570
|
+
expect(result.error).toBeInstanceOf(AssetError);
|
|
2571
|
+
expect(result.error.code).toBe('mesh-submesh-index-range-out-of-bounds');
|
|
2572
|
+
expect(result.error.expected).toBeTruthy();
|
|
2573
|
+
expect(result.error.hint).toBeTruthy();
|
|
2574
|
+
const detail = result.error.detail as {
|
|
2575
|
+
submeshIndex?: number;
|
|
2576
|
+
indexOffset?: number;
|
|
2577
|
+
indexCount?: number;
|
|
2578
|
+
indexBufferLength?: number;
|
|
2579
|
+
meshAssetGuid?: string;
|
|
2580
|
+
};
|
|
2581
|
+
expect(detail.submeshIndex).toBe(0);
|
|
2582
|
+
expect(detail.indexOffset).toBe(4);
|
|
2583
|
+
expect(detail.indexCount).toBe(4);
|
|
2584
|
+
expect(detail.indexBufferLength).toBe(6);
|
|
2585
|
+
expect(detail.meshAssetGuid).toBeTruthy();
|
|
2586
|
+
}
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
it('register with valid submesh index range passes', () => {
|
|
2590
|
+
const registry = freshRegistry();
|
|
2591
|
+
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
2592
|
+
const payload = makeMeshPayload({
|
|
2593
|
+
indices,
|
|
2594
|
+
submeshes: [
|
|
2595
|
+
{
|
|
2596
|
+
indexOffset: 0,
|
|
2597
|
+
indexCount: 6,
|
|
2598
|
+
vertexCount: 4,
|
|
2599
|
+
topology: 'triangle-list' as const,
|
|
2600
|
+
},
|
|
2601
|
+
],
|
|
2602
|
+
});
|
|
2603
|
+
|
|
2604
|
+
const result = registry.catalog(AssetGuid.format(AssetGuid.random()), payload as never);
|
|
2605
|
+
expect(result.ok).toBe(true);
|
|
2606
|
+
});
|
|
2607
|
+
});
|
|
2608
|
+
|
|
2243
2609
|
// ── read-side: renderer override diagnostics ─────────────────────────────────
|
|
2244
2610
|
|
|
2245
2611
|
describe('read-side: MeshRenderer override diagnostics', () => {
|