@forgeax/engine-runtime 0.1.21 → 0.1.24
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 +18 -9
- package/dist/__tests__/advance-animation-player.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/advance-animation-player.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-aabb.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-aabb.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-bootstrap.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-bootstrap.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-builtin-nineslice.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-builtin-nineslice.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-d9-tile-sampler-soft-warn.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-d9-tile-sampler-soft-warn.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-guid.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-guid.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-material-validate.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-material-validate.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mesh-fail-fast.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mesh-fail-fast.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-scene.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-scene.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-sprite-slices-validate.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-sprite-slices-validate.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-renderer-assembly.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-renderer-assembly.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/bloom-gating.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/bloom-gating.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-guid-ssot.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/builtin-guid-ssot.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-pack.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/builtin-pack.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-antialias.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-antialias.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-bloom.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-bloom.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-clear-schema.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-clear-schema.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-ortho.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-ortho.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/cluster-binner.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/cluster-binner.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-fallback-shader-manifest.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-fallback-shader-manifest.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-uniform-fallback.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-uniform-fallback.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/cubemap-upload.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/cubemap-upload.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/dev-import-transport.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/dev-import-transport.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/engine-metrics.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/engine-metrics.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/equirect-projection-error.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/equirect-projection-error.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/font-asset-load.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/font-asset-load.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fxaa-intermediate-texture.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/fxaa-intermediate-texture.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fxaa-pipeline.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/fxaa-pipeline.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-residency.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-residency.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-resource-release.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-resource-release.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/handle-quad.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/handle-quad.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-bgl-slots.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-bgl-slots.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-grid-invalid.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-grid-invalid.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-index-list-overflow-once.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-index-list-overflow-once.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-light-budget.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-light-budget.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-pipeline-asset-config.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-pipeline-asset-config.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/register-runtime-components.d.ts.map +1 -1
- package/dist/__tests__/helpers/register-scene-components.d.ts.map +1 -1
- package/dist/__tests__/helpers/renderer-device-loss-dawn.d.ts +25 -0
- package/dist/__tests__/helpers/renderer-device-loss-dawn.d.ts.map +1 -0
- package/dist/__tests__/helpers/standard-material-manifest.d.ts +0 -1
- package/dist/__tests__/helpers/standard-material-manifest.d.ts.map +1 -1
- package/dist/__tests__/ibl-pipeline-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/ibl-pipeline-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/lazy-catalog.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/lazy-catalog.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-by-guid-hdr.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/load-by-guid-hdr.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-by-guid-prod-material-parent.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/load-by-guid-prod-material-parent.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-by-guid-prod.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/load-by-guid-prod.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-registry.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-registry.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/m7-hdrp-demo-shape.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/m7-hdrp-demo-shape.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mipmap-formula.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/mipmap-formula.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mipmap-pipeline-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/mipmap-pipeline-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parse-asset-payload-material.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/parse-asset-payload-material.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parse-asset-payload-texture.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/parse-asset-payload-texture.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parse-scene-payload-refs.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/parse-scene-payload-refs.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pass-selector.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pass-selector.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-builder.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-builder.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-cache-key-topology.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-cache-key-topology.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-vertex-stride-branch.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-vertex-stride-branch.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/post-process-register.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/post-process-register.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/propagate-transforms.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/propagate-transforms.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/record-all-topology.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/record-all-topology.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/record-draw-branch.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/record-draw-branch.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/record-strip-index-format.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/record-strip-index-format.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/register-with-guid-rgba16float.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/register-with-guid-rgba16float.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-data.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-data.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-query-regression.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-query-regression.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record-submesh.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-record-submesh.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record-warn-once.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-record-warn-once.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-device-loss-multi-world.integration.test.d.ts +2 -0
- package/dist/__tests__/renderer-device-loss-multi-world.integration.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-device-loss-provider-feasibility.dawn.test.d.ts +2 -0
- package/dist/__tests__/renderer-device-loss-provider-feasibility.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-device-loss-recovery.dawn.test.d.ts +2 -0
- package/dist/__tests__/renderer-device-loss-recovery.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-draw-world.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/renderer-draw-world.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-recovery-consumer.integration.test.d.ts +2 -0
- package/dist/__tests__/renderer-recovery-consumer.integration.test.d.ts.map +1 -0
- package/dist/__tests__/renderstate-pipeline-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/renderstate-pipeline-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-scene-guids.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/resolve-scene-guids.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-skip-non-triangle.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/shadow-skip-non-triangle.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-errors-kebab-case.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-errors-kebab-case.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-instances-coexist.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-instances-coexist.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-happy.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-happy.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-joint-error.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-joint-error.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-null-assets.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-null-assets.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m3-record-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m3-record-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-palette-extract.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-palette-extract.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-pipeline-routing.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-pipeline-routing.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skybox-extract.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skybox-extract.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skybox-shader-compile.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skybox-shader-compile.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-bind-group.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-bind-group.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-component.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-component.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-fallback-path.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-fallback-path.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-pipeline-layout.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-pipeline-layout.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-solid-color-extract.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-solid-color-extract.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-boundary.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-boundary.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-clamp.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-clamp.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-negative.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-negative.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-zero.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-zero.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-loop.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-loop.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-override-probe.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-override-probe.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-regions-mismatch.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-regions-mismatch.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/storage-buffer-caps.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/storage-buffer-caps.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/systems.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/systems.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tonemap-hdr-target.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/tonemap-hdr-target.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tonemap-pipeline-split.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/tonemap-pipeline-split.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tonemap.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/tonemap.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transparent-sort-config-get.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/transparent-sort-config-get.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transparent-sort-config-set.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/transparent-sort-config-set.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transparent-sort.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/transparent-sort.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/upload-texture-consistency.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/upload-texture-consistency.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/verify-revisions.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/verify-revisions.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/wire-default-loaders.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/wire-default-loaders.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/zero-camera-clear-fallback.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/zero-camera-clear-fallback.cases.unit.test.d.ts.map +1 -0
- package/dist/createRenderer.d.ts.map +1 -1
- package/dist/dev-import-transport.d.ts.map +1 -1
- package/dist/index.mjs +4 -3
- package/dist/index.mjs.map +1 -1
- package/dist/sprite-param-values.d.ts +1 -1
- package/package.json +30 -30
- package/src/__tests__/advance-animation-player.cases.unit.test.ts +1666 -0
- package/src/__tests__/animation-graph-asset-union.test-d.ts +2 -0
- package/src/__tests__/asset-brand-and-name-retirement.test.ts +4 -3
- package/src/__tests__/asset-registry-aabb.cases.unit.test.ts +676 -0
- package/src/__tests__/asset-registry-bootstrap.cases.unit.test.ts +472 -0
- package/src/__tests__/asset-registry-builtin-nineslice.cases.unit.test.ts +481 -0
- package/src/__tests__/asset-registry-d9-tile-sampler-soft-warn.cases.unit.test.ts +568 -0
- package/src/__tests__/asset-registry-guid.cases.unit.test.ts +500 -0
- package/src/__tests__/asset-registry-material-validate.cases.unit.test.ts +756 -0
- package/src/__tests__/asset-registry-mesh-fail-fast.cases.unit.test.ts +591 -0
- package/src/__tests__/asset-registry-scene.cases.unit.test.ts +501 -0
- package/src/__tests__/asset-registry-scene.test-d.ts +1 -0
- package/src/__tests__/asset-registry-sprite-slices-validate.cases.unit.test.ts +536 -0
- package/src/__tests__/asset-renderer-assembly.cases.unit.test.ts +496 -0
- package/src/__tests__/asset.cases.unit.test.ts +439 -0
- package/src/__tests__/bind-group-cache-keying.test.ts +6 -2
- package/src/__tests__/bloom-gating.cases.unit.test.ts +428 -0
- package/src/__tests__/builtin-guid-ssot.cases.unit.test.ts +472 -0
- package/src/__tests__/builtin-pack.cases.unit.test.ts +639 -0
- package/src/__tests__/builtin-shader-register-e2e.test.ts +1 -1
- package/src/__tests__/camera-antialias.cases.unit.test.ts +372 -0
- package/src/__tests__/camera-bloom.cases.unit.test.ts +396 -0
- package/src/__tests__/camera-clear-schema.cases.unit.test.ts +391 -0
- package/src/__tests__/camera-ortho.cases.unit.test.ts +484 -0
- package/src/__tests__/cluster-binner.cases.unit.test.ts +817 -0
- package/src/__tests__/collect-scene-mount-collapse.test.ts +5 -1
- package/src/__tests__/collect-transient-roundtrip.test.ts +14 -3
- package/src/__tests__/components.test-d.ts +9 -6
- package/src/__tests__/components.unit.test.ts +23 -19
- package/src/__tests__/create-renderer-fallback-shader-manifest.cases.unit.test.ts +345 -0
- package/src/__tests__/create-renderer-uniform-fallback.cases.unit.test.ts +445 -0
- package/src/__tests__/cubemap-upload.cases.unit.test.ts +593 -0
- package/src/__tests__/dawn/ibl-precompute-readback.dawn.test.ts +184 -17
- package/src/__tests__/dawn/material-alpha.dawn.test.ts +17 -4
- package/src/__tests__/derive-bgl-integration.test.ts +8 -8
- package/src/__tests__/dev-import-transport.cases.unit.test.ts +503 -0
- package/src/__tests__/engine-metrics.cases.unit.test.ts +371 -0
- package/src/__tests__/equirect-projection-error.cases.unit.test.ts +416 -0
- package/src/__tests__/errors.unit.test.ts +3 -0
- package/src/__tests__/extract-frames-merge.test.ts +3 -0
- package/src/__tests__/font-asset-load.cases.unit.test.ts +510 -0
- package/src/__tests__/fxaa-intermediate-texture.cases.unit.test.ts +690 -0
- package/src/__tests__/fxaa-pipeline.cases.unit.test.ts +546 -0
- package/src/__tests__/gpu-residency.cases.unit.test.ts +479 -0
- package/src/__tests__/gpu-resource-release.cases.unit.test.ts +929 -0
- package/src/__tests__/handle-quad.cases.unit.test.ts +491 -0
- package/src/__tests__/hdrp-bgl-slots.cases.unit.test.ts +451 -0
- package/src/__tests__/hdrp-grid-invalid.cases.unit.test.ts +408 -0
- package/src/__tests__/hdrp-index-list-overflow-once.cases.unit.test.ts +405 -0
- package/src/__tests__/hdrp-light-budget.cases.unit.test.ts +483 -0
- package/src/__tests__/hdrp-pipeline-asset-config.cases.unit.test.ts +441 -0
- package/src/__tests__/helpers/register-runtime-components.ts +9 -1
- package/src/__tests__/helpers/register-scene-components.ts +9 -2
- package/src/__tests__/helpers/renderer-device-loss-dawn.ts +69 -0
- package/src/__tests__/helpers/standard-material-manifest.ts +11 -15
- package/src/__tests__/ibl-pipeline-cache.cases.unit.test.ts +415 -0
- package/src/__tests__/lazy-catalog.cases.unit.test.ts +848 -0
- package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +14 -15
- package/src/__tests__/light-snapshot.test-d.ts +27 -1
- package/src/__tests__/lights-preservation.fixup.test.ts +19 -21
- package/src/__tests__/lights.unit.test.ts +2076 -1
- package/src/__tests__/load-by-guid-hdr.cases.unit.test.ts +582 -0
- package/src/__tests__/load-by-guid-prod-material-parent.cases.unit.test.ts +1514 -0
- package/src/__tests__/load-by-guid-prod.cases.unit.test.ts +557 -0
- package/src/__tests__/loader-name-survival.test.ts +8 -9
- package/src/__tests__/loader-registry.cases.unit.test.ts +767 -0
- package/src/__tests__/m7-hdrp-demo-shape.cases.unit.test.ts +361 -0
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +2 -2
- package/src/__tests__/materials.unit.test.ts +29 -10
- package/src/__tests__/mipmap-formula.cases.unit.test.ts +434 -0
- package/src/__tests__/mipmap-pipeline-cache.cases.unit.test.ts +490 -0
- package/src/__tests__/package-promotion.test.ts +7 -11
- package/src/__tests__/parse-asset-payload-material.cases.unit.test.ts +565 -0
- package/src/__tests__/parse-asset-payload-texture.cases.unit.test.ts +720 -0
- package/src/__tests__/parse-scene-payload-refs.cases.unit.test.ts +767 -0
- package/src/__tests__/pass-selector.cases.unit.test.ts +376 -0
- package/src/__tests__/pbr-pipeline.unit.test.ts +39 -21
- package/src/__tests__/pipeline-builder.cases.unit.test.ts +624 -0
- package/src/__tests__/pipeline-cache-key-topology.cases.unit.test.ts +807 -0
- package/src/__tests__/pipeline-vertex-stride-branch.cases.unit.test.ts +379 -0
- package/src/__tests__/pipeline.cases.unit.test.ts +370 -0
- package/src/__tests__/point-light-shadow.browser.test.ts +1 -0
- package/src/__tests__/point-light-shadow.unit.test.ts +18 -98
- package/src/__tests__/post-process-register.cases.unit.test.ts +405 -0
- package/src/__tests__/propagate-transforms.cases.unit.test.ts +502 -0
- package/src/__tests__/propagate-transforms.dawn.test.ts +34 -18
- package/src/__tests__/record-all-topology.cases.unit.test.ts +608 -0
- package/src/__tests__/record-draw-branch.cases.unit.test.ts +621 -0
- package/src/__tests__/record-strip-index-format.cases.unit.test.ts +419 -0
- package/src/__tests__/record-worldid-isolation.test.ts +16 -32
- package/src/__tests__/register-with-guid-rgba16float.cases.unit.test.ts +469 -0
- package/src/__tests__/render-data.cases.unit.test.ts +553 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +11 -0
- package/src/__tests__/render-query-regression.cases.unit.test.ts +531 -0
- package/src/__tests__/render-system-extract.test.ts +5 -5
- package/src/__tests__/render-system-record-multi-material-textureview.test.ts +1 -0
- package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +1 -0
- package/src/__tests__/render-system-record-submesh.cases.unit.test.ts +740 -0
- package/src/__tests__/render-system-record-warn-once.cases.unit.test.ts +418 -0
- package/src/__tests__/render-system-record.test.ts +15 -33
- package/src/__tests__/renderer-device-loss-multi-world.integration.test.ts +103 -0
- package/src/__tests__/renderer-device-loss-provider-feasibility.dawn.test.ts +124 -0
- package/src/__tests__/renderer-device-loss-recovery.dawn.test.ts +50 -0
- package/src/__tests__/renderer-draw-world.cases.unit.test.ts +514 -0
- package/src/__tests__/renderer-recovery-consumer.integration.test.ts +96 -0
- package/src/__tests__/renderer-surface.unit.test.ts +0 -2
- package/src/__tests__/renderstate-pipeline-cache.cases.unit.test.ts +631 -0
- package/src/__tests__/resolve-scene-guids.cases.unit.test.ts +1123 -0
- package/src/__tests__/resolveName-unified-entry.test.ts +6 -6
- package/src/__tests__/resolveName.test.ts +14 -2
- package/src/__tests__/shadow-caster-empty-schema.test.ts +1 -1
- package/src/__tests__/shadow-csm-cascade-loadop.test.ts +1 -0
- package/src/__tests__/shadow-csm-tile-consistency.test.ts +1 -0
- package/src/__tests__/shadow-csm-ubo.test.ts +3 -1
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +15 -4
- package/src/__tests__/shadow-m1.dawn.test.ts +1 -1
- package/src/__tests__/shadow-skip-non-triangle.cases.unit.test.ts +681 -0
- package/src/__tests__/skin-errors-kebab-case.cases.unit.test.ts +475 -0
- package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +5 -4
- package/src/__tests__/skin-instances-coexist.cases.unit.test.ts +375 -0
- package/src/__tests__/skin-m2-extract-happy.cases.unit.test.ts +592 -0
- package/src/__tests__/skin-m2-extract-joint-error.cases.unit.test.ts +619 -0
- package/src/__tests__/skin-m2-extract-null-assets.cases.unit.test.ts +569 -0
- package/src/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.ts +585 -0
- package/src/__tests__/skin-m3-record-cache.cases.unit.test.ts +457 -0
- package/src/__tests__/skin-palette-extract.cases.unit.test.ts +635 -0
- package/src/__tests__/skin-parented-double-transform.test.ts +5 -5
- package/src/__tests__/skin-pipeline-routing.cases.unit.test.ts +411 -0
- package/src/__tests__/skin.cases.unit.test.ts +421 -0
- package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +1 -6
- package/src/__tests__/skybox-extract.cases.unit.test.ts +511 -0
- package/src/__tests__/skybox-shader-compile.cases.unit.test.ts +437 -0
- package/src/__tests__/skylight-bind-group.cases.unit.test.ts +642 -0
- package/src/__tests__/skylight-component.cases.unit.test.ts +386 -0
- package/src/__tests__/skylight-fallback-path.cases.unit.test.ts +566 -0
- package/src/__tests__/skylight-lazy-projection.test.ts +35 -20
- package/src/__tests__/skylight-pipeline-layout.cases.unit.test.ts +506 -0
- package/src/__tests__/skylight-solid-color-extract.cases.unit.test.ts +399 -0
- package/src/__tests__/sprite-animation-tick-boundary.cases.unit.test.ts +521 -0
- package/src/__tests__/sprite-animation-tick-clamp.cases.unit.test.ts +453 -0
- package/src/__tests__/sprite-animation-tick-frame-duration-negative.cases.unit.test.ts +447 -0
- package/src/__tests__/sprite-animation-tick-frame-duration-zero.cases.unit.test.ts +395 -0
- package/src/__tests__/sprite-animation-tick-loop.cases.unit.test.ts +494 -0
- package/src/__tests__/sprite-animation-tick-override-probe.cases.unit.test.ts +439 -0
- package/src/__tests__/sprite-animation-tick-regions-mismatch.cases.unit.test.ts +466 -0
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +16 -18
- package/src/__tests__/sprite-lit-pipeline-cache-key.test.ts +1 -1
- package/src/__tests__/ssao-bgl.test.ts +2 -1
- package/src/__tests__/ssao-pipelines.test.ts +1 -1
- package/src/__tests__/storage-buffer-caps.cases.unit.test.ts +373 -0
- package/src/__tests__/systems.cases.unit.test.ts +425 -0
- package/src/__tests__/template-game-default-api-contract.test.ts +45 -23
- package/src/__tests__/tilemap-chunk-extract.overhang-bounds.test.ts +1 -1
- package/src/__tests__/tilemap-chunk-extract.propagate-order.test.ts +10 -4
- package/src/__tests__/tonemap-hdr-target.cases.unit.test.ts +670 -0
- package/src/__tests__/tonemap-pipeline-split.cases.unit.test.ts +867 -0
- package/src/__tests__/tonemap.cases.unit.test.ts +446 -0
- package/src/__tests__/transform-hierarchy-pixel-diff.dawn.test.ts +2 -2
- package/src/__tests__/transparent-sort-config-get.cases.unit.test.ts +395 -0
- package/src/__tests__/transparent-sort-config-set.cases.unit.test.ts +424 -0
- package/src/__tests__/transparent-sort.cases.unit.test.ts +684 -0
- package/src/__tests__/upload-texture-consistency.cases.unit.test.ts +478 -0
- package/src/__tests__/verify-revisions.cases.unit.test.ts +619 -0
- package/src/__tests__/wire-default-loaders.cases.unit.test.ts +461 -0
- package/src/__tests__/zero-camera-clear-fallback.cases.unit.test.ts +374 -0
- package/src/collect-scene-asset.ts +1 -1
- package/src/components/__tests__/transform-vec-schema.test.ts +2 -2
- package/src/createRenderer.ts +1 -0
- package/src/dev-import-transport.ts +2 -1
- package/src/sprite-param-values.ts +1 -1
- package/dist/.tsbuildinfo +0 -1
- package/dist/__tests__/asset.unit.test.d.ts +0 -2
- package/dist/__tests__/asset.unit.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline.unit.test.d.ts +0 -2
- package/dist/__tests__/pipeline.unit.test.d.ts.map +0 -1
- package/dist/__tests__/systems.unit.test.d.ts +0 -2
- package/dist/__tests__/systems.unit.test.d.ts.map +0 -1
- package/src/__tests__/asset.unit.test.ts +0 -7110
- package/src/__tests__/pipeline.unit.test.ts +0 -5283
- package/src/__tests__/systems.unit.test.ts +0 -9210
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
// @ts-nocheck — merged file: cross-source node:fs/node:path imports outside @types/node coverage in runtime tsconfig
|
|
2
|
+
// Consolidated by feat-20260609-test-pool-startup-reduction-merge-tiny-test-files
|
|
3
|
+
// biome-ignore-all lint/complexity/noUselessLoneBlockStatements: block-scope isolation between merged source files (consolidation paradigm)
|
|
4
|
+
//
|
|
5
|
+
// Source files (N=33):
|
|
6
|
+
// - packages/runtime/__tests__/create-renderer-fallback.test.ts
|
|
7
|
+
// - packages/runtime/src/__tests__/cluster-binner.test.ts
|
|
8
|
+
// - packages/runtime/src/__tests__/create-renderer-fallback-shader-manifest.test.ts
|
|
9
|
+
// - packages/runtime/src/__tests__/create-renderer-uniform-fallback.test.ts
|
|
10
|
+
// - packages/runtime/src/__tests__/createRenderer.test.ts
|
|
11
|
+
// - packages/runtime/src/__tests__/dispatch-sort.test.ts
|
|
12
|
+
// - packages/runtime/src/__tests__/engine-metrics.test.ts
|
|
13
|
+
// - packages/runtime/src/__tests__/device/gpu-residency-caps-guard.test.ts
|
|
14
|
+
// - packages/runtime/src/__tests__/pass-selector.test.ts
|
|
15
|
+
// - packages/runtime/src/__tests__/pipeline-builder.test.ts
|
|
16
|
+
// - packages/runtime/src/__tests__/pipeline-cache-key-topology.test.ts
|
|
17
|
+
// - packages/runtime/src/__tests__/pipeline-rename-grep-gate.test.ts
|
|
18
|
+
// - packages/runtime/src/__tests__/pipeline-vertex-stride-branch.test.ts
|
|
19
|
+
// - packages/runtime/src/__tests__/post-process-register.test.ts
|
|
20
|
+
// - packages/runtime/src/__tests__/record-all-topology.test.ts
|
|
21
|
+
// - packages/runtime/src/__tests__/record-draw-branch.test.ts
|
|
22
|
+
// - packages/runtime/src/__tests__/record-strip-index-format.test.ts
|
|
23
|
+
// - packages/runtime/src/__tests__/render-query-regression.test.ts
|
|
24
|
+
// - packages/runtime/src/__tests__/renderer-draw-world.test.ts
|
|
25
|
+
// - packages/runtime/src/__tests__/renderer-input-snapshot.test.ts
|
|
26
|
+
// - packages/runtime/src/__tests__/renderer-read-pixels.test.ts
|
|
27
|
+
// - packages/runtime/src/__tests__/renderer-ready.test.ts
|
|
28
|
+
// - packages/runtime/src/__tests__/renderstate-pipeline-cache.test.ts
|
|
29
|
+
// - packages/runtime/src/__tests__/storage-buffer-caps.test.ts
|
|
30
|
+
// - packages/runtime/src/__tests__/device/gpu-residency.test.ts
|
|
31
|
+
// - packages/runtime/src/__tests__/render-data.test.ts
|
|
32
|
+
// - packages/runtime/src/__tests__/hdrp-bgl-slots.test.ts
|
|
33
|
+
// - packages/runtime/src/__tests__/hdrp-caps-gate.test.ts
|
|
34
|
+
// - packages/runtime/src/__tests__/hdrp-grid-invalid.test.ts
|
|
35
|
+
// - packages/runtime/src/__tests__/hdrp-index-list-overflow-once.test.ts
|
|
36
|
+
// - packages/runtime/src/__tests__/hdrp-light-budget.test.ts
|
|
37
|
+
// - packages/runtime/src/__tests__/hdrp-pipeline-asset-config.test.ts
|
|
38
|
+
// - packages/runtime/src/__tests__/m7-hdrp-demo-shape.test.ts
|
|
39
|
+
//
|
|
40
|
+
// Paradigm: each block-scope wraps a source file. ancestorTitles[0] is the
|
|
41
|
+
// source-preserved inner describe (NOT the source filename for these 3 files
|
|
42
|
+
// — recovery path: vitest report ancestorTitles -> grep this file -> upstream
|
|
43
|
+
// `// ─── from <name>.test.ts ───` block separator -> source filename).
|
|
44
|
+
// Top-level imports merged + deduped.
|
|
45
|
+
|
|
46
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
47
|
+
import { dirname, resolve } from 'node:path';
|
|
48
|
+
import { fileURLToPath } from 'node:url';
|
|
49
|
+
import { HANDLE_CUBE, HANDLE_TRIANGLE, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
50
|
+
import type { World as WorldType } from '@forgeax/engine-ecs';
|
|
51
|
+
import { defineComponent, World } from '@forgeax/engine-ecs';
|
|
52
|
+
import {
|
|
53
|
+
buildMeshAttributeMapForUvSets,
|
|
54
|
+
createBoxGeometry,
|
|
55
|
+
createConeGeometry,
|
|
56
|
+
createCylinderGeometry,
|
|
57
|
+
createPlaneGeometry,
|
|
58
|
+
createSphereGeometry,
|
|
59
|
+
createTorusGeometry,
|
|
60
|
+
PROCEDURAL_FLOATS_PER_VERTEX,
|
|
61
|
+
} from '@forgeax/engine-geometry';
|
|
62
|
+
import { type Mat4, mat4, type Vec3, vec3 } from '@forgeax/engine-math';
|
|
63
|
+
import type { Renderer as RendererType } from '@forgeax/engine-render';
|
|
64
|
+
import { Camera, MeshFilter, MeshRenderer } from '@forgeax/engine-render';
|
|
65
|
+
import {
|
|
66
|
+
err,
|
|
67
|
+
ok,
|
|
68
|
+
type PipelineLayout,
|
|
69
|
+
type RenderPipeline,
|
|
70
|
+
type Result,
|
|
71
|
+
type RhiCaps,
|
|
72
|
+
type RhiDevice,
|
|
73
|
+
RhiError,
|
|
74
|
+
ok as rhiOk,
|
|
75
|
+
type ShaderModule,
|
|
76
|
+
} from '@forgeax/engine-rhi';
|
|
77
|
+
import {
|
|
78
|
+
acquireCanvasContext as acquireNullCanvasContext,
|
|
79
|
+
createShaderModule as createNullShaderModule,
|
|
80
|
+
RhiNullAdapter,
|
|
81
|
+
} from '@forgeax/engine-rhi-null';
|
|
82
|
+
import { ChildOf, Transform } from '@forgeax/engine-scene';
|
|
83
|
+
import {
|
|
84
|
+
findVariantByKey,
|
|
85
|
+
type MaterialShaderEntry,
|
|
86
|
+
type MaterialShaderManifestEntry,
|
|
87
|
+
type MaterialShaderManifestVariant,
|
|
88
|
+
} from '@forgeax/engine-shader';
|
|
89
|
+
import {
|
|
90
|
+
type AssetError,
|
|
91
|
+
type EquirectAsset,
|
|
92
|
+
type Handle,
|
|
93
|
+
type MaterialRenderState,
|
|
94
|
+
type MeshAsset,
|
|
95
|
+
type PassSelector,
|
|
96
|
+
type PrimitiveTopology,
|
|
97
|
+
type RenderPipelineAsset,
|
|
98
|
+
type TextureAsset,
|
|
99
|
+
toShared,
|
|
100
|
+
} from '@forgeax/engine-types';
|
|
101
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
102
|
+
import {
|
|
103
|
+
bin,
|
|
104
|
+
type ClusterBinError,
|
|
105
|
+
calculateSphereClusterBounds,
|
|
106
|
+
clusterSpaceObjectAabb,
|
|
107
|
+
deriveCullingRadius,
|
|
108
|
+
ndcPositionToCluster,
|
|
109
|
+
viewZToZSlice,
|
|
110
|
+
} from '../../../render/src/cluster-binner';
|
|
111
|
+
import { GpuResidencyCache } from '../../../render/src/device/gpu-residency';
|
|
112
|
+
import { createEngineMetrics } from '../../../render/src/engine-metrics';
|
|
113
|
+
import { assertStorageBufferCap } from '../../../render/src/light-buffer-layout';
|
|
114
|
+
import {
|
|
115
|
+
buildPbrPipelineLayouts,
|
|
116
|
+
buildPbrViewBglEntries,
|
|
117
|
+
type PbrCaps,
|
|
118
|
+
} from '../../../render/src/pbr-pipeline';
|
|
119
|
+
import {
|
|
120
|
+
HdrpInstallError,
|
|
121
|
+
validateClusterGrid,
|
|
122
|
+
} from '../../../render/src/pipeline/standard-pipeline';
|
|
123
|
+
import {
|
|
124
|
+
buildPipelineForMaterialShader,
|
|
125
|
+
type PipelineBuilderContext,
|
|
126
|
+
type PipelineBuilderShaderModuleFactory,
|
|
127
|
+
} from '../../../render/src/pipeline-builder';
|
|
128
|
+
import {
|
|
129
|
+
cacheKeyOf,
|
|
130
|
+
createHdrpBindGroupLayoutDescriptor,
|
|
131
|
+
type PipelineSpec,
|
|
132
|
+
} from '../../../render/src/pipeline-spec';
|
|
133
|
+
import {
|
|
134
|
+
deriveRenderDataCubemap,
|
|
135
|
+
deriveRenderDataMesh,
|
|
136
|
+
deriveRenderDataTexture,
|
|
137
|
+
} from '../../../render/src/render-data';
|
|
138
|
+
import { extractFrame, prepareExtractContext } from '../../../render/src/render-system-extract';
|
|
139
|
+
import { matchPass } from '../../../render/src/systems/pass-selector';
|
|
140
|
+
import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
141
|
+
import { standardMaterialShaderVariants } from './helpers/standard-material-manifest';
|
|
142
|
+
|
|
143
|
+
function makeExplicitNullRhi(spies: {
|
|
144
|
+
setIndexBuffer: ReturnType<typeof vi.fn>;
|
|
145
|
+
setVertexBuffer: ReturnType<typeof vi.fn>;
|
|
146
|
+
draw: ReturnType<typeof vi.fn>;
|
|
147
|
+
drawIndexed: ReturnType<typeof vi.fn>;
|
|
148
|
+
}): unknown {
|
|
149
|
+
const adapter = new RhiNullAdapter();
|
|
150
|
+
const requestDevice = adapter.requestDevice.bind(adapter);
|
|
151
|
+
adapter.requestDevice = async () => {
|
|
152
|
+
const result = await requestDevice();
|
|
153
|
+
if (result.ok) {
|
|
154
|
+
const device = result.value;
|
|
155
|
+
const createCommandEncoder = device.createCommandEncoder.bind(device);
|
|
156
|
+
device.createCommandEncoder = (desc) => {
|
|
157
|
+
const encoderResult = createCommandEncoder(desc);
|
|
158
|
+
if (!encoderResult.ok) return encoderResult;
|
|
159
|
+
const encoder = encoderResult.value;
|
|
160
|
+
const beginRenderPass = encoder.beginRenderPass.bind(encoder);
|
|
161
|
+
encoder.beginRenderPass = (passDesc) => {
|
|
162
|
+
const pass = beginRenderPass(passDesc);
|
|
163
|
+
const draw = pass.draw.bind(pass);
|
|
164
|
+
const drawIndexed = pass.drawIndexed.bind(pass);
|
|
165
|
+
const setIndexBuffer = pass.setIndexBuffer.bind(pass);
|
|
166
|
+
const setVertexBuffer = pass.setVertexBuffer.bind(pass);
|
|
167
|
+
pass.draw = (...args) => {
|
|
168
|
+
spies.draw(...args);
|
|
169
|
+
draw(...args);
|
|
170
|
+
};
|
|
171
|
+
pass.drawIndexed = (...args) => {
|
|
172
|
+
spies.drawIndexed(...args);
|
|
173
|
+
drawIndexed(...args);
|
|
174
|
+
};
|
|
175
|
+
pass.setIndexBuffer = (...args) => {
|
|
176
|
+
spies.setIndexBuffer(...args);
|
|
177
|
+
setIndexBuffer(...args);
|
|
178
|
+
};
|
|
179
|
+
pass.setVertexBuffer = (...args) => {
|
|
180
|
+
spies.setVertexBuffer(...args);
|
|
181
|
+
setVertexBuffer(...args);
|
|
182
|
+
};
|
|
183
|
+
return pass;
|
|
184
|
+
};
|
|
185
|
+
return { ok: true, value: encoder };
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
};
|
|
190
|
+
return {
|
|
191
|
+
requestAdapter: async () => ({ ok: true, value: adapter }),
|
|
192
|
+
acquireCanvasContext: acquireNullCanvasContext,
|
|
193
|
+
createShaderModule: (device: unknown, desc: { code: string; label?: string }) =>
|
|
194
|
+
createNullShaderModule(device as never, desc),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
vi.mock('@forgeax/engine-rhi-wgpu', () => {
|
|
199
|
+
return {
|
|
200
|
+
rhi: {
|
|
201
|
+
requestAdapter: async () => ({
|
|
202
|
+
ok: false,
|
|
203
|
+
error: {
|
|
204
|
+
code: 'adapter-unavailable',
|
|
205
|
+
expected: 'adapter available',
|
|
206
|
+
hint: 'default mock fail',
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
getPreferredCanvasFormat: () => 'bgra8unorm',
|
|
210
|
+
acquireCanvasContext: () => ({
|
|
211
|
+
ok: false,
|
|
212
|
+
error: {
|
|
213
|
+
code: 'webgpu-runtime-error',
|
|
214
|
+
expected: 'context available',
|
|
215
|
+
hint: 'default mock fail',
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
},
|
|
219
|
+
ensureReady: async () => undefined,
|
|
220
|
+
};
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
void [
|
|
224
|
+
Camera,
|
|
225
|
+
ChildOf,
|
|
226
|
+
GpuResidencyCache,
|
|
227
|
+
HANDLE_CUBE,
|
|
228
|
+
HANDLE_TRIANGLE,
|
|
229
|
+
HdrpInstallError,
|
|
230
|
+
MeshFilter,
|
|
231
|
+
MeshRenderer,
|
|
232
|
+
PROCEDURAL_FLOATS_PER_VERTEX,
|
|
233
|
+
RhiError,
|
|
234
|
+
RhiNullAdapter,
|
|
235
|
+
Transform,
|
|
236
|
+
World,
|
|
237
|
+
acquireNullCanvasContext,
|
|
238
|
+
afterEach,
|
|
239
|
+
assertStorageBufferCap,
|
|
240
|
+
beforeEach,
|
|
241
|
+
bin,
|
|
242
|
+
buildMeshAttributeMapForUvSets,
|
|
243
|
+
buildPbrPipelineLayouts,
|
|
244
|
+
buildPbrViewBglEntries,
|
|
245
|
+
buildPipelineForMaterialShader,
|
|
246
|
+
cacheKeyOf,
|
|
247
|
+
calculateSphereClusterBounds,
|
|
248
|
+
clusterSpaceObjectAabb,
|
|
249
|
+
createBoxGeometry,
|
|
250
|
+
createConeGeometry,
|
|
251
|
+
createCylinderGeometry,
|
|
252
|
+
createEngineMetrics,
|
|
253
|
+
createHdrpBindGroupLayoutDescriptor,
|
|
254
|
+
createNullShaderModule,
|
|
255
|
+
createPlaneGeometry,
|
|
256
|
+
createSphereGeometry,
|
|
257
|
+
createTorusGeometry,
|
|
258
|
+
defineComponent,
|
|
259
|
+
deriveCullingRadius,
|
|
260
|
+
deriveRenderDataCubemap,
|
|
261
|
+
deriveRenderDataMesh,
|
|
262
|
+
deriveRenderDataTexture,
|
|
263
|
+
describe,
|
|
264
|
+
dirname,
|
|
265
|
+
err,
|
|
266
|
+
existsSync,
|
|
267
|
+
expect,
|
|
268
|
+
extractFrame,
|
|
269
|
+
fileURLToPath,
|
|
270
|
+
findVariantByKey,
|
|
271
|
+
it,
|
|
272
|
+
makeExplicitNullRhi,
|
|
273
|
+
makeMockShaderRegistry,
|
|
274
|
+
mat4,
|
|
275
|
+
matchPass,
|
|
276
|
+
ndcPositionToCluster,
|
|
277
|
+
ok,
|
|
278
|
+
prepareExtractContext,
|
|
279
|
+
readFileSync,
|
|
280
|
+
resolve,
|
|
281
|
+
resolveAssetHandle,
|
|
282
|
+
rhiOk,
|
|
283
|
+
standardMaterialShaderVariants,
|
|
284
|
+
toShared,
|
|
285
|
+
validateClusterGrid,
|
|
286
|
+
vec3,
|
|
287
|
+
vi,
|
|
288
|
+
viewZToZSlice,
|
|
289
|
+
];
|
|
290
|
+
type __MergedKeep =
|
|
291
|
+
| AssetError
|
|
292
|
+
| ClusterBinError
|
|
293
|
+
| EquirectAsset
|
|
294
|
+
| Handle
|
|
295
|
+
| Mat4
|
|
296
|
+
| MaterialRenderState
|
|
297
|
+
| MaterialShaderEntry
|
|
298
|
+
| MaterialShaderManifestEntry
|
|
299
|
+
| MaterialShaderManifestVariant
|
|
300
|
+
| MeshAsset
|
|
301
|
+
| PassSelector
|
|
302
|
+
| PbrCaps
|
|
303
|
+
| PipelineBuilderContext
|
|
304
|
+
| PipelineBuilderShaderModuleFactory
|
|
305
|
+
| PipelineLayout
|
|
306
|
+
| PipelineSpec
|
|
307
|
+
| PrimitiveTopology
|
|
308
|
+
| RenderPipeline
|
|
309
|
+
| RenderPipelineAsset
|
|
310
|
+
| RendererType
|
|
311
|
+
| Result
|
|
312
|
+
| RhiCaps
|
|
313
|
+
| RhiDevice
|
|
314
|
+
| ShaderModule
|
|
315
|
+
| TextureAsset
|
|
316
|
+
| Vec3
|
|
317
|
+
| WorldType;
|
|
318
|
+
|
|
319
|
+
{
|
|
320
|
+
// --- from pipeline-cache-key-topology.test.ts ---
|
|
321
|
+
const ID = 'my-game::pulse-material';
|
|
322
|
+
|
|
323
|
+
// Helper: construct PipelineSpec for cache-key unit tests (M2-T2 migration).
|
|
324
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
325
|
+
function mkSpec(
|
|
326
|
+
id: string,
|
|
327
|
+
isHdr: boolean,
|
|
328
|
+
renderState?: MaterialRenderState,
|
|
329
|
+
topology?: PrimitiveTopology,
|
|
330
|
+
indexFormat?: 'uint16' | 'uint32',
|
|
331
|
+
variantSet?: string,
|
|
332
|
+
passKind: string = 'forward',
|
|
333
|
+
sampleCount: 1 | 4 = 1,
|
|
334
|
+
): PipelineSpec {
|
|
335
|
+
const colorFormat: GPUTextureFormat = isHdr
|
|
336
|
+
? ('rgba16float' as unknown as GPUTextureFormat)
|
|
337
|
+
: ('bgra8unorm-srgb' as unknown as GPUTextureFormat);
|
|
338
|
+
return {
|
|
339
|
+
shader: { id, passKind, variantSet },
|
|
340
|
+
attachments: {
|
|
341
|
+
colorFormats: passKind === 'shadow-caster' ? [] : [colorFormat],
|
|
342
|
+
depthFormat:
|
|
343
|
+
passKind === 'shadow-caster'
|
|
344
|
+
? ('depth32float' as unknown as GPUTextureFormat)
|
|
345
|
+
: ('depth24plus-stencil8' as unknown as GPUTextureFormat),
|
|
346
|
+
sampleCount,
|
|
347
|
+
},
|
|
348
|
+
geometry: {
|
|
349
|
+
topology: topology ?? 'triangle-list',
|
|
350
|
+
stripIndexFormat: indexFormat,
|
|
351
|
+
vertexLayout: {
|
|
352
|
+
position: new Float32Array(0),
|
|
353
|
+
normal: new Float32Array(0),
|
|
354
|
+
uv: new Float32Array(0),
|
|
355
|
+
tangent: new Float32Array(0),
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
renderState,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
describe('per-spec pipeline cache key — topology dimension (AC-03/05/06)', () => {
|
|
363
|
+
it('(a) AC-05: different topology -> different cache key (distinct PSO slots)', () => {
|
|
364
|
+
const line = cacheKeyOf(mkSpec(ID, false, undefined, 'line-list'));
|
|
365
|
+
const tri = cacheKeyOf(mkSpec(ID, false, undefined, 'triangle-list'));
|
|
366
|
+
|
|
367
|
+
expect(line).not.toBe(tri);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('(a) AC-05: topology difference holds with a renderState present', () => {
|
|
371
|
+
const rs: MaterialRenderState = { cullMode: 'none' };
|
|
372
|
+
const line = cacheKeyOf(mkSpec(ID, false, rs, 'line-list'));
|
|
373
|
+
const tri = cacheKeyOf(mkSpec(ID, false, rs, 'triangle-list'));
|
|
374
|
+
|
|
375
|
+
expect(line).not.toBe(tri);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('(b) AC-03: omitted topology == explicit triangle-list (byte-identical key)', () => {
|
|
379
|
+
const omitted = cacheKeyOf(mkSpec(ID, false, undefined, undefined));
|
|
380
|
+
const explicit = cacheKeyOf(mkSpec(ID, false, undefined, 'triangle-list'));
|
|
381
|
+
|
|
382
|
+
expect(omitted).toBe(explicit);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('(b) AC-03: omitted topology key contains the :triangle-list segment', () => {
|
|
386
|
+
const omitted = cacheKeyOf(mkSpec(ID, false, undefined, undefined));
|
|
387
|
+
|
|
388
|
+
// M2-T4: key format is id:passKind:variantSet:colorFormat:depthFormat:
|
|
389
|
+
// sampleCount:topology[:stripSegment]:vl:digest[:renderStateHash].
|
|
390
|
+
// Strip segment is '' for non-strip topo → topology: followed by :vl:.
|
|
391
|
+
expect(omitted.includes(':triangle-list:')).toBe(true);
|
|
392
|
+
expect(omitted.includes(':vl:')).toBe(true);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('(b) AC-03: omitted-topology + undefined-renderState key contains legacy shape prefix', () => {
|
|
396
|
+
// M2-T4: key format changed — passKind:variantSet:colorFormats replaces
|
|
397
|
+
// the pre-M2 :ldr segment. The prefix carries the full 4-axis structure;
|
|
398
|
+
// vl:<hash> follows the topology segment.
|
|
399
|
+
// bug-20260708 M2 (b): variantSet=undefined now serializes as sentinel
|
|
400
|
+
// `~` (was empty segment `::`) to decouple undefined from canonical `''`.
|
|
401
|
+
const key = cacheKeyOf(mkSpec('forgeax::default-pbr', false, undefined, undefined));
|
|
402
|
+
|
|
403
|
+
expect(
|
|
404
|
+
key.startsWith(
|
|
405
|
+
'forgeax::default-pbr:forward:~:bgra8unorm-srgb:depth24plus-stencil8:1:triangle-list',
|
|
406
|
+
),
|
|
407
|
+
).toBe(true);
|
|
408
|
+
// vl:<digest> segment present (vertex layout hash); may have trailing : from
|
|
409
|
+
// renderStateHash('') at end of join
|
|
410
|
+
expect(key).toMatch(/:vl:[^:]+/);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('(c) AC-06: same tuple -> identical key (idempotent, cache hit)', () => {
|
|
414
|
+
const rs: MaterialRenderState = { cullMode: 'front' };
|
|
415
|
+
const k1 = cacheKeyOf(mkSpec(ID, true, rs, 'line-list'));
|
|
416
|
+
const k2 = cacheKeyOf(mkSpec(ID, true, { ...rs }, 'line-list'));
|
|
417
|
+
|
|
418
|
+
expect(k1).toBe(k2);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('(d) topology is an independent segment, not folded into the renderState hash', () => {
|
|
422
|
+
// Moving topology while holding renderState fixed must change the key, and
|
|
423
|
+
// moving renderState while holding topology fixed must also change it --
|
|
424
|
+
// proving the two dimensions are orthogonal (D-2 / D-A3).
|
|
425
|
+
const base = cacheKeyOf(mkSpec(ID, false, { cullMode: 'none' }, 'triangle-list'));
|
|
426
|
+
const topoMoved = cacheKeyOf(mkSpec(ID, false, { cullMode: 'none' }, 'line-list'));
|
|
427
|
+
const rsMoved = cacheKeyOf(mkSpec(ID, false, { cullMode: 'front' }, 'triangle-list'));
|
|
428
|
+
|
|
429
|
+
expect(base).not.toBe(topoMoved);
|
|
430
|
+
expect(base).not.toBe(rsMoved);
|
|
431
|
+
expect(topoMoved).not.toBe(rsMoved);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('(d) every strip / list topology yields a distinct key segment', () => {
|
|
435
|
+
const topologies: PrimitiveTopology[] = [
|
|
436
|
+
'point-list',
|
|
437
|
+
'line-list',
|
|
438
|
+
'line-strip',
|
|
439
|
+
'triangle-list',
|
|
440
|
+
'triangle-strip',
|
|
441
|
+
];
|
|
442
|
+
const keys = topologies.map((t) => cacheKeyOf(mkSpec(ID, false, undefined, t)));
|
|
443
|
+
|
|
444
|
+
expect(new Set(keys).size).toBe(topologies.length);
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
// ============================================================================
|
|
448
|
+
// feat-20260609-hdrp-cluster-fragment-ggx M1 / w2
|
|
449
|
+
// PSO cache key -- variantSet dimension (TDD red phase).
|
|
450
|
+
// ============================================================================
|
|
451
|
+
|
|
452
|
+
describe('per-spec pipeline cache key -- variantSet dimension (feat-20260609 M1)', () => {
|
|
453
|
+
it('(a) different variantSet -> different cache key (distinct PSO slots)', () => {
|
|
454
|
+
const k1 = cacheKeyOf(
|
|
455
|
+
mkSpec(ID, false, undefined, 'triangle-list', undefined, 'STORAGE_BUFFER_AVAILABLE=true'),
|
|
456
|
+
);
|
|
457
|
+
const k2 = cacheKeyOf(
|
|
458
|
+
mkSpec(
|
|
459
|
+
ID,
|
|
460
|
+
false,
|
|
461
|
+
undefined,
|
|
462
|
+
'triangle-list',
|
|
463
|
+
undefined,
|
|
464
|
+
'CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true',
|
|
465
|
+
),
|
|
466
|
+
);
|
|
467
|
+
expect(k1).not.toBe(k2);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('(b) same variantSet -> identical cache key (idempotent, cache hit)', () => {
|
|
471
|
+
const vs = 'CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true';
|
|
472
|
+
const k1 = cacheKeyOf(mkSpec(ID, false, undefined, 'triangle-list', undefined, vs));
|
|
473
|
+
const k2 = cacheKeyOf(mkSpec(ID, false, undefined, 'triangle-list', undefined, vs));
|
|
474
|
+
expect(k1).toBe(k2);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it("(c) bug-20260708 M2 (b): variantSet '' (canonical all-true) and undefined produce DISTINCT keys via sentinel", () => {
|
|
478
|
+
// bug-20260708 M2 (b) REVERSES the prior M4.5 D-11 normalize-to-same
|
|
479
|
+
// behavior. `cacheKeyOf` now uses sentinel `~` for `variantSet=undefined`
|
|
480
|
+
// and preserves `''` verbatim, so undefined vs canonical `''` land in
|
|
481
|
+
// separate cache slots. This decoupling closes the R-11 collision that
|
|
482
|
+
// let boot-seeded PIR=true modules leak into character sprite pipeline
|
|
483
|
+
// requests (variantSet=undefined). See sentinel gate in
|
|
484
|
+
// pipeline-cache-keying.unit.test.ts §'variantSet sentinel' as SSOT.
|
|
485
|
+
const withUndefined = cacheKeyOf(
|
|
486
|
+
mkSpec(ID, false, undefined, 'triangle-list', undefined, undefined),
|
|
487
|
+
);
|
|
488
|
+
const withEmpty = cacheKeyOf(mkSpec(ID, false, undefined, 'triangle-list', undefined, ''));
|
|
489
|
+
expect(withUndefined).not.toBe(withEmpty);
|
|
490
|
+
// Undefined key contains the `~` sentinel segment (position: after
|
|
491
|
+
// shader.id + passKind, before colorFormats).
|
|
492
|
+
expect(withUndefined).toContain(':~:');
|
|
493
|
+
// Canonical `''` key contains an empty variantSet segment `::`.
|
|
494
|
+
expect(withEmpty).toContain('::');
|
|
495
|
+
expect(withUndefined.length).toBeGreaterThan(0);
|
|
496
|
+
expect(withEmpty.length).toBeGreaterThan(0);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it("(e) M4.5 / D-11: canonical '' and expanded 'AXIS=true+...' all-true produce DIFFERENT keys", () => {
|
|
500
|
+
const withEmpty = cacheKeyOf(mkSpec(ID, false, undefined, 'triangle-list', undefined, ''));
|
|
501
|
+
const withExpandedAllTrue = cacheKeyOf(
|
|
502
|
+
mkSpec(
|
|
503
|
+
ID,
|
|
504
|
+
false,
|
|
505
|
+
undefined,
|
|
506
|
+
'triangle-list',
|
|
507
|
+
undefined,
|
|
508
|
+
'CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true',
|
|
509
|
+
),
|
|
510
|
+
);
|
|
511
|
+
expect(withEmpty).not.toBe(withExpandedAllTrue);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('(d) variantSet is orthogonal to topology dimension', () => {
|
|
515
|
+
const base = cacheKeyOf(
|
|
516
|
+
mkSpec(ID, false, undefined, 'triangle-list', undefined, 'CLUSTER_FORWARD_AVAILABLE=true'),
|
|
517
|
+
);
|
|
518
|
+
const topoMoved = cacheKeyOf(
|
|
519
|
+
mkSpec(ID, false, undefined, 'line-list', undefined, 'CLUSTER_FORWARD_AVAILABLE=true'),
|
|
520
|
+
);
|
|
521
|
+
const variantMoved = cacheKeyOf(
|
|
522
|
+
mkSpec(ID, false, undefined, 'triangle-list', undefined, 'STORAGE_BUFFER_AVAILABLE=true'),
|
|
523
|
+
);
|
|
524
|
+
expect(base).not.toBe(topoMoved);
|
|
525
|
+
expect(base).not.toBe(variantMoved);
|
|
526
|
+
expect(topoMoved).not.toBe(variantMoved);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// ============================================================================
|
|
531
|
+
// feat-20260609-hdrp-cluster-fragment-ggx M4 / w30
|
|
532
|
+
// runtime variant WGSL resolution -- variantSet -> correct variant WGSL (TDD red phase).
|
|
533
|
+
// ============================================================================
|
|
534
|
+
|
|
535
|
+
describe('runtime variant WGSL resolution from manifest (feat-20260609 M4 / w30)', () => {
|
|
536
|
+
function makeEntry(
|
|
537
|
+
identifier: string,
|
|
538
|
+
defaultBindings: string,
|
|
539
|
+
variants: readonly { definesKey: string; source: string; bindings: string }[],
|
|
540
|
+
): MaterialShaderManifestEntry {
|
|
541
|
+
// M3 / w13: the binding-layout sidecar is gone from MaterialShader{Entry,ManifestEntry}
|
|
542
|
+
// — paramSchema is the SSOT. The fixture below sets the per-variant
|
|
543
|
+
// BGL hint into the composedWgsl as a comment marker so the
|
|
544
|
+
// variant-resolution assertions can still distinguish HDRP-vs-URP
|
|
545
|
+
// variants without the deleted binding-layout JSON-string.
|
|
546
|
+
return {
|
|
547
|
+
identifier,
|
|
548
|
+
sourcePath: `${identifier}.wgsl`,
|
|
549
|
+
composedWgsl: `// default wgsl for ${identifier} ${defaultBindings}`,
|
|
550
|
+
paramSchema: '[]',
|
|
551
|
+
variants: variants.map((v) => ({
|
|
552
|
+
definesKey: v.definesKey,
|
|
553
|
+
defines: Object.fromEntries(
|
|
554
|
+
v.definesKey
|
|
555
|
+
? v.definesKey.split('+').map((kv) => {
|
|
556
|
+
const [k, val] = kv.split('=');
|
|
557
|
+
return [k, val === 'true'] as [string, boolean];
|
|
558
|
+
})
|
|
559
|
+
: [],
|
|
560
|
+
),
|
|
561
|
+
composedWgsl: `${v.source} ${v.bindings}`,
|
|
562
|
+
})) as readonly MaterialShaderManifestVariant[],
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const BGL_WITH_CLUSTER = JSON.stringify([
|
|
567
|
+
{ entries: [{ binding: 0, buffer: { hasDynamicOffset: true } }] },
|
|
568
|
+
{ entries: [] },
|
|
569
|
+
{
|
|
570
|
+
entries: [
|
|
571
|
+
{ binding: 0, buffer: { hasDynamicOffset: true } },
|
|
572
|
+
{ binding: 3, buffer: { type: 'read-only-storage' } },
|
|
573
|
+
{ binding: 4, buffer: { type: 'read-only-storage' } },
|
|
574
|
+
{ binding: 5, buffer: { type: 'read-only-storage' } },
|
|
575
|
+
{ binding: 6, buffer: { type: 'uniform' } },
|
|
576
|
+
],
|
|
577
|
+
},
|
|
578
|
+
]);
|
|
579
|
+
|
|
580
|
+
const BGL_WITHOUT_CLUSTER = JSON.stringify([
|
|
581
|
+
{ entries: [{ binding: 0, buffer: { hasDynamicOffset: true } }] },
|
|
582
|
+
{ entries: [] },
|
|
583
|
+
{
|
|
584
|
+
entries: [{ binding: 0, buffer: { hasDynamicOffset: true } }],
|
|
585
|
+
},
|
|
586
|
+
]);
|
|
587
|
+
|
|
588
|
+
const PBR_ID = 'forgeax::default-standard-pbr';
|
|
589
|
+
const HDRP_DSK = 'CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true';
|
|
590
|
+
const URP_DSK = 'STORAGE_BUFFER_AVAILABLE=true';
|
|
591
|
+
|
|
592
|
+
// Helper to extract the per-variant BGL JSON the fixture appended to
|
|
593
|
+
// composedWgsl. The test fixture (makeEntry) embeds the BGL JSON
|
|
594
|
+
// string after the WGSL source body so variant resolution can be
|
|
595
|
+
// asserted without the deleted MaterialShaderManifestVariant binding-layout sidecar
|
|
596
|
+
// sidecar field (M3 / w13 grep gate).
|
|
597
|
+
const extractBglJson = (composedWgsl: string | undefined): string | undefined => {
|
|
598
|
+
if (composedWgsl === undefined) return undefined;
|
|
599
|
+
const idx = composedWgsl.indexOf('[');
|
|
600
|
+
return idx >= 0 ? composedWgsl.slice(idx) : undefined;
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
it('(a) HDRP variant resolves to WGSL with cluster bindings (binding 3..6 present)', () => {
|
|
604
|
+
const entry = makeEntry(PBR_ID, BGL_WITHOUT_CLUSTER, [
|
|
605
|
+
{ definesKey: HDRP_DSK, source: '// HDRP variant WGSL', bindings: BGL_WITH_CLUSTER },
|
|
606
|
+
{ definesKey: URP_DSK, source: '// URP variant WGSL', bindings: BGL_WITHOUT_CLUSTER },
|
|
607
|
+
]);
|
|
608
|
+
|
|
609
|
+
const variant = findVariantByKey(entry, HDRP_DSK);
|
|
610
|
+
expect(variant).toBeDefined();
|
|
611
|
+
expect(variant?.composedWgsl).toContain('// HDRP variant WGSL');
|
|
612
|
+
|
|
613
|
+
const bglJson = extractBglJson(variant?.composedWgsl);
|
|
614
|
+
expect(bglJson).toBeDefined();
|
|
615
|
+
const bgl = JSON.parse(bglJson as string) as ReadonlyArray<{
|
|
616
|
+
entries: ReadonlyArray<{ binding: number }>;
|
|
617
|
+
}>;
|
|
618
|
+
const group2 = bgl[2];
|
|
619
|
+
expect(group2).toBeDefined();
|
|
620
|
+
const bindings = group2?.entries.map((e) => e.binding);
|
|
621
|
+
expect(bindings).toContain(3);
|
|
622
|
+
expect(bindings).toContain(4);
|
|
623
|
+
expect(bindings).toContain(5);
|
|
624
|
+
expect(bindings).toContain(6);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it('(b) URP variant resolves to WGSL without cluster bindings (binding 3..6 absent)', () => {
|
|
628
|
+
const entry = makeEntry(PBR_ID, BGL_WITH_CLUSTER, [
|
|
629
|
+
{ definesKey: HDRP_DSK, source: '// HDRP variant WGSL', bindings: BGL_WITH_CLUSTER },
|
|
630
|
+
{ definesKey: URP_DSK, source: '// URP variant WGSL', bindings: BGL_WITHOUT_CLUSTER },
|
|
631
|
+
]);
|
|
632
|
+
|
|
633
|
+
const variant = findVariantByKey(entry, URP_DSK);
|
|
634
|
+
expect(variant).toBeDefined();
|
|
635
|
+
expect(variant?.composedWgsl).toContain('// URP variant WGSL');
|
|
636
|
+
|
|
637
|
+
const bglJson = extractBglJson(variant?.composedWgsl);
|
|
638
|
+
expect(bglJson).toBeDefined();
|
|
639
|
+
const bgl = JSON.parse(bglJson as string) as ReadonlyArray<{
|
|
640
|
+
entries: ReadonlyArray<{ binding: number }>;
|
|
641
|
+
}>;
|
|
642
|
+
const group2 = bgl[2];
|
|
643
|
+
expect(group2).toBeDefined();
|
|
644
|
+
const bindings = group2?.entries.map((e) => e.binding);
|
|
645
|
+
expect(bindings).not.toContain(3);
|
|
646
|
+
expect(bindings).not.toContain(4);
|
|
647
|
+
expect(bindings).not.toContain(5);
|
|
648
|
+
expect(bindings).not.toContain(6);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('(c) empty variantSet (definesKey="") falls back to entry default composedWgsl', () => {
|
|
652
|
+
const entry = makeEntry(PBR_ID, BGL_WITHOUT_CLUSTER, [
|
|
653
|
+
{ definesKey: HDRP_DSK, source: '// HDRP variant WGSL', bindings: BGL_WITH_CLUSTER },
|
|
654
|
+
{ definesKey: URP_DSK, source: '// URP variant WGSL', bindings: BGL_WITHOUT_CLUSTER },
|
|
655
|
+
]);
|
|
656
|
+
|
|
657
|
+
const variant = findVariantByKey(entry, '');
|
|
658
|
+
expect(variant).toBeUndefined();
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it('(d) unknown variantSet returns undefined (fail-soft)', () => {
|
|
662
|
+
const entry = makeEntry(PBR_ID, BGL_WITHOUT_CLUSTER, [
|
|
663
|
+
{ definesKey: URP_DSK, source: '// URP variant WGSL', bindings: BGL_WITHOUT_CLUSTER },
|
|
664
|
+
]);
|
|
665
|
+
|
|
666
|
+
const variant4 = findVariantByKey(entry, 'NONEXISTENT_KEY=true');
|
|
667
|
+
expect(variant4).toBeUndefined();
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
// ============================================================================
|
|
672
|
+
// bug-20260708 M2 (a) AC-03 / AC-04: sprite boot variant resolution.
|
|
673
|
+
// Simulates the createRenderer boot-time material-shader registration
|
|
674
|
+
// pattern (extended at line ~3687 to include PER_INSTANCE_REGION=false in
|
|
675
|
+
// variantDefines). Asserts that a sprite-like manifest entry (with the PIR
|
|
676
|
+
// axis) resolves to the PIR=false variant at boot, so per-entity spritePH
|
|
677
|
+
// path (variantSet=undefined) sees PIR=false shader source via
|
|
678
|
+
// installMaterialArtifact → lookup.value.source fallback.
|
|
679
|
+
// ============================================================================
|
|
680
|
+
describe('sprite boot variant resolution (bug-20260708 AC-03/AC-04)', () => {
|
|
681
|
+
const SPRITE_ID = 'forgeax::sprite';
|
|
682
|
+
|
|
683
|
+
function makeSpriteEntry(): MaterialShaderManifestEntry {
|
|
684
|
+
// Sprite manifest emits a 2-axis Cartesian: PER_INSTANCE_REGION x
|
|
685
|
+
// STORAGE_BUFFER_AVAILABLE. Canonical all-true `''` = PIR=true+SBA=true;
|
|
686
|
+
// other 3 combinations get sorted-key definesKey strings.
|
|
687
|
+
return {
|
|
688
|
+
identifier: SPRITE_ID,
|
|
689
|
+
sourcePath: 'sprite.wgsl',
|
|
690
|
+
composedWgsl: '// sprite all-true (PIR=true+SBA=true, 5653B) default composedWgsl',
|
|
691
|
+
paramSchema: '[]',
|
|
692
|
+
variants: [
|
|
693
|
+
{
|
|
694
|
+
definesKey: '',
|
|
695
|
+
defines: { PER_INSTANCE_REGION: true, STORAGE_BUFFER_AVAILABLE: true },
|
|
696
|
+
composedWgsl: '// sprite PIR=true SBA=true 5653B instances[idx].region',
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
definesKey: 'PER_INSTANCE_REGION=false+STORAGE_BUFFER_AVAILABLE=true',
|
|
700
|
+
defines: { PER_INSTANCE_REGION: false, STORAGE_BUFFER_AVAILABLE: true },
|
|
701
|
+
composedWgsl: '// sprite PIR=false SBA=true 5575B material.region',
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
definesKey: 'PER_INSTANCE_REGION=true+STORAGE_BUFFER_AVAILABLE=false',
|
|
705
|
+
defines: { PER_INSTANCE_REGION: true, STORAGE_BUFFER_AVAILABLE: false },
|
|
706
|
+
composedWgsl:
|
|
707
|
+
'// sprite PIR=true SBA=false 5653B (WebGL2 fallback) instances[idx].region',
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
definesKey: 'PER_INSTANCE_REGION=false+STORAGE_BUFFER_AVAILABLE=false',
|
|
711
|
+
defines: { PER_INSTANCE_REGION: false, STORAGE_BUFFER_AVAILABLE: false },
|
|
712
|
+
composedWgsl: '// sprite PIR=false SBA=false 5575B (WebGL2 fallback) material.region',
|
|
713
|
+
},
|
|
714
|
+
] as readonly MaterialShaderManifestVariant[],
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Mirrors the createRenderer boot resolution at line ~3680-3696 (with the
|
|
719
|
+
// bug-20260708 M2 (a) PER_INSTANCE_REGION=false extension). Extracted here
|
|
720
|
+
// for testability — the actual createRenderer implementation is inline.
|
|
721
|
+
// AC-03/AC-04 depend on this logic picking the PIR=false variant for
|
|
722
|
+
// sprite; the test locks the outcome so an inline-logic drift is caught.
|
|
723
|
+
function resolveBootDefinesKey(
|
|
724
|
+
msEntry: MaterialShaderManifestEntry,
|
|
725
|
+
opts: { readonly storageBufferCapable: boolean; readonly isHdrpActive: boolean },
|
|
726
|
+
): string {
|
|
727
|
+
const variantDefines: Record<string, boolean> = {
|
|
728
|
+
STORAGE_BUFFER_AVAILABLE: opts.storageBufferCapable,
|
|
729
|
+
};
|
|
730
|
+
if (msEntry.variants.some((v) => 'CLUSTER_FORWARD_AVAILABLE' in v.defines)) {
|
|
731
|
+
variantDefines.CLUSTER_FORWARD_AVAILABLE = opts.isHdrpActive;
|
|
732
|
+
}
|
|
733
|
+
if (msEntry.variants.some((v) => 'PER_INSTANCE_REGION' in v.defines)) {
|
|
734
|
+
variantDefines.PER_INSTANCE_REGION = false;
|
|
735
|
+
}
|
|
736
|
+
const sortedEntries = Object.entries(variantDefines).sort(([a], [b]) =>
|
|
737
|
+
a < b ? -1 : a > b ? 1 : 0,
|
|
738
|
+
);
|
|
739
|
+
return sortedEntries.every(([, v]) => v === true)
|
|
740
|
+
? ''
|
|
741
|
+
: sortedEntries.map(([k, v]) => `${k}=${v}`).join('+');
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
it('(a) boot resolves sprite to PIR=false variant when storageBufferCapable=true (AC-03)', () => {
|
|
745
|
+
const spriteEntry = makeSpriteEntry();
|
|
746
|
+
const definesKey = resolveBootDefinesKey(spriteEntry, {
|
|
747
|
+
storageBufferCapable: true,
|
|
748
|
+
isHdrpActive: false,
|
|
749
|
+
});
|
|
750
|
+
expect(definesKey).toBe('PER_INSTANCE_REGION=false+STORAGE_BUFFER_AVAILABLE=true');
|
|
751
|
+
const variant = findVariantByKey(spriteEntry, definesKey);
|
|
752
|
+
expect(variant).toBeDefined();
|
|
753
|
+
expect(variant?.defines.PER_INSTANCE_REGION).toBe(false);
|
|
754
|
+
expect(variant?.defines.STORAGE_BUFFER_AVAILABLE).toBe(true);
|
|
755
|
+
// The variant WGSL corresponds to the PIR=false 5575B variant that
|
|
756
|
+
// reads region from material.region (M1 anchor).
|
|
757
|
+
expect(variant?.composedWgsl).toContain('material.region');
|
|
758
|
+
expect(variant?.composedWgsl).not.toContain('instances[idx].region');
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
it('(b) boot resolves sprite to PIR=false+SBA=false variant on WebGL2 fallback (AC-03)', () => {
|
|
762
|
+
const spriteEntry = makeSpriteEntry();
|
|
763
|
+
const definesKey = resolveBootDefinesKey(spriteEntry, {
|
|
764
|
+
storageBufferCapable: false,
|
|
765
|
+
isHdrpActive: false,
|
|
766
|
+
});
|
|
767
|
+
expect(definesKey).toBe('PER_INSTANCE_REGION=false+STORAGE_BUFFER_AVAILABLE=false');
|
|
768
|
+
const variant = findVariantByKey(spriteEntry, definesKey);
|
|
769
|
+
expect(variant).toBeDefined();
|
|
770
|
+
expect(variant?.defines.PER_INSTANCE_REGION).toBe(false);
|
|
771
|
+
expect(variant?.defines.STORAGE_BUFFER_AVAILABLE).toBe(false);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it('(c) SpriteInstances canonical PIR=true variant resolvable via findVariantByKey("") (AC-04)', () => {
|
|
775
|
+
// SpriteInstances batches request `variantSet=''` via
|
|
776
|
+
// SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET (bug-20260708 M2 (c)).
|
|
777
|
+
// The runtime substitution branch in `getMaterialShaderPipeline`
|
|
778
|
+
// (`createRenderer.ts:1730`) resolves this via findVariantByKey with
|
|
779
|
+
// the canonical empty key — returning the PIR=true+SBA=true variant.
|
|
780
|
+
const spriteEntry = makeSpriteEntry();
|
|
781
|
+
const variant = findVariantByKey(spriteEntry, '');
|
|
782
|
+
expect(variant).toBeDefined();
|
|
783
|
+
expect(variant?.defines.PER_INSTANCE_REGION).toBe(true);
|
|
784
|
+
expect(variant?.defines.STORAGE_BUFFER_AVAILABLE).toBe(true);
|
|
785
|
+
// The variant WGSL corresponds to the PIR=true 5653B variant that
|
|
786
|
+
// reads region from instances[idx].region (M1 anchor).
|
|
787
|
+
expect(variant?.composedWgsl).toContain('instances[idx].region');
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
it('(d) boot PIR=false variant and canonical `""` PIR=true variant have distinct source (AC-04 module identity)', () => {
|
|
791
|
+
// AC-04: sprite entities with no SpriteInstances (variantSet=undefined,
|
|
792
|
+
// resolves via lookup.value to PIR=false source) and SpriteInstances
|
|
793
|
+
// batches (variantSet='', resolves via findVariantByKey to PIR=true
|
|
794
|
+
// source) MUST get different shader source strings — which in turn
|
|
795
|
+
// produce different compiled shader modules (5575B vs 5653B). Same
|
|
796
|
+
// materialShaderId, distinct module handles.
|
|
797
|
+
const spriteEntry = makeSpriteEntry();
|
|
798
|
+
const bootDefinesKey = resolveBootDefinesKey(spriteEntry, {
|
|
799
|
+
storageBufferCapable: true,
|
|
800
|
+
isHdrpActive: false,
|
|
801
|
+
});
|
|
802
|
+
const bootVariant = findVariantByKey(spriteEntry, bootDefinesKey);
|
|
803
|
+
const spriteInstancesVariant = findVariantByKey(spriteEntry, '');
|
|
804
|
+
expect(bootVariant?.composedWgsl).not.toBe(spriteInstancesVariant?.composedWgsl);
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
}
|