@forgeax/engine-render 0.1.33 → 0.1.34
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 +1010 -124
- package/dist/assembly/backend-contract.d.ts +2 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
- package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
- package/dist/assembly/canvas-draw-guard.d.ts +3 -0
- package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
- package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
- package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +1 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +3 -3
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
- package/dist/assembly/material-shader-policy.d.ts +16 -2
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/render-feature-post-processes.d.ts +6 -0
- package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
- package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +11 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
- package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +1 -1
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/barrel-distortion.d.ts +47 -0
- package/dist/barrel-distortion.d.ts.map +1 -0
- package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
- package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
- package/dist/{chunk-ZDQWGQW7.mjs → chunk-HEB564WF.mjs} +4 -4
- package/dist/{chunk-ZDQWGQW7.mjs.map → chunk-HEB564WF.mjs.map} +1 -1
- package/dist/{chunk-VOZWDM3L.mjs → chunk-MKDIT2EM.mjs} +1307 -188
- package/dist/chunk-MKDIT2EM.mjs.map +1 -0
- package/dist/{chunk-LEAXOMYJ.mjs → chunk-OCDLEI6I.mjs} +3 -3
- package/dist/{chunk-LEAXOMYJ.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
- package/dist/chunk-ODZWR6JQ.mjs +4927 -0
- package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
- package/dist/{chunk-3RTCJTH4.mjs → chunk-SHFUFJ5N.mjs} +184 -13
- package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
- package/dist/{chunk-OO3DRYK7.mjs → chunk-UI5SFODX.mjs} +176 -96
- package/dist/chunk-UI5SFODX.mjs.map +1 -0
- package/dist/{chunk-PLAXOOX5.mjs → chunk-UINHLCZA.mjs} +561 -79
- package/dist/chunk-UINHLCZA.mjs.map +1 -0
- package/dist/{chunk-NB2S7ZRX.mjs → chunk-WEX45HKH.mjs} +127 -386
- package/dist/chunk-WEX45HKH.mjs.map +1 -0
- package/dist/cloud/density.d.ts +55 -0
- package/dist/cloud/density.d.ts.map +1 -0
- package/dist/cloud/extract.d.ts +25 -0
- package/dist/cloud/extract.d.ts.map +1 -0
- package/dist/cloud/feature.d.ts +57 -0
- package/dist/cloud/feature.d.ts.map +1 -0
- package/dist/cloud/frame-context.d.ts +8 -0
- package/dist/cloud/frame-context.d.ts.map +1 -0
- package/dist/cloud/inspection.d.ts +59 -0
- package/dist/cloud/inspection.d.ts.map +1 -0
- package/dist/cloud/optics.d.ts +56 -0
- package/dist/cloud/optics.d.ts.map +1 -0
- package/dist/cloud/parameters.d.ts +73 -0
- package/dist/cloud/parameters.d.ts.map +1 -0
- package/dist/cloud/resources.d.ts +60 -0
- package/dist/cloud/resources.d.ts.map +1 -0
- package/dist/cloud/shadow.d.ts +42 -0
- package/dist/cloud/shadow.d.ts.map +1 -0
- package/dist/cloud/temporal.d.ts +74 -0
- package/dist/cloud/temporal.d.ts.map +1 -0
- package/dist/components/barrel-distortion.d.ts +13 -0
- package/dist/components/barrel-distortion.d.ts.map +1 -0
- package/dist/components/cloud-layer.d.ts +29 -0
- package/dist/components/cloud-layer.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +20 -6
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/construct-renderer.d.ts +1 -1
- package/dist/construct-renderer.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +6759 -2875
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/dynamic-geometry.d.ts +5 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/errors/cloud.d.ts +59 -0
- package/dist/errors/cloud.d.ts.map +1 -0
- package/dist/errors/render.d.ts +18 -4
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/examples/single-layer-medium-surface.d.ts +47 -0
- package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +5 -1
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/features/barrel-distortion.d.ts +29 -0
- package/dist/features/barrel-distortion.d.ts.map +1 -0
- package/dist/features/error-reporting.d.ts +6 -2
- package/dist/features/error-reporting.d.ts.map +1 -1
- package/dist/features/host.d.ts +10 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/plan.d.ts +8 -0
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +5 -0
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +1 -0
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/targets.d.ts +2 -0
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +44 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +15 -7
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-dirty-ranges.d.ts +11 -0
- package/dist/gpu-dirty-ranges.d.ts.map +1 -0
- package/dist/gpu-driven/batch-topology.d.ts +17 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts +6 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster-material.d.ts +178 -0
- package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +47 -3
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/resource-allocation.d.ts +31 -0
- package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
- package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
- package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +26 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +5 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +2 -2
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +10 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +27 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +258 -8
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +135 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instance-buffer-cache.d.ts +4 -4
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/instances.d.ts +48 -100
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +7 -7
- package/dist/pbr-pipeline.d.ts +19 -1
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
- package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-output/types.d.ts +4 -2
- package/dist/pipeline/standard-output/types.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +1 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/barrel-distortion-frame.d.ts +15 -0
- package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
- package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
- package/dist/record/fold-buckets.d.ts +95 -0
- package/dist/record/fold-buckets.d.ts.map +1 -0
- package/dist/record/fold-instance-buffer.d.ts +6 -0
- package/dist/record/fold-instance-buffer.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +1 -0
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +37 -3
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +10 -61
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-draw-receipts.d.ts +11 -0
- package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
- package/dist/record/gpu-timing.d.ts +8 -0
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +21 -5
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +13 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/motion-blur-receipt.d.ts +11 -0
- package/dist/record/motion-blur-receipt.d.ts.map +1 -0
- package/dist/record/probe-blend-buffer.d.ts +16 -0
- package/dist/record/probe-blend-buffer.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +22 -3
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -0
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +22 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +11 -3
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/record/volume-params.d.ts +9 -0
- package/dist/record/volume-params.d.ts.map +1 -0
- package/dist/recovery/render-system-candidate.d.ts +10 -0
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +42 -4
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +103 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +3 -2
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +16 -9
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-presentation.d.ts +23 -0
- package/dist/render-system-presentation.d.ts.map +1 -0
- package/dist/render-system-producer-roots.d.ts +10 -0
- package/dist/render-system-producer-roots.d.ts.map +1 -0
- package/dist/render-system-residency.d.ts +12 -0
- package/dist/render-system-residency.d.ts.map +1 -0
- package/dist/render-system.d.ts +19 -14
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/probe-blend.d.ts +27 -0
- package/dist/scene/probe-blend.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +12 -5
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +9 -0
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/surface/dynamic-input.d.ts +133 -0
- package/dist/surface/dynamic-input.d.ts.map +1 -0
- package/dist/surface/gpu-driven.d.ts +41 -0
- package/dist/surface/gpu-driven.d.ts.map +1 -0
- package/dist/surface/submission-observation.d.ts +136 -0
- package/dist/surface/submission-observation.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +13 -1
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
- package/dist/transmission/single-layer-medium.d.ts +89 -0
- package/dist/transmission/single-layer-medium.d.ts.map +1 -0
- package/dist/typed-render-graph-primitives.d.ts +14 -2
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +9 -4
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/extract.d.ts +1 -1
- package/dist/volume/extract.d.ts.map +1 -1
- package/dist/volume/inspection.d.ts +2 -0
- package/dist/volume/inspection.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +4 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/resources.d.ts.map +1 -1
- package/package.json +22 -21
- package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
- package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
- package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
- package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
- package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
- package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
- package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
- package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
- package/src/__tests__/factory-contract.integration.test.ts +295 -45
- package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
- package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
- package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
- package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +6 -5
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +94 -6
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
- package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
- package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
- package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
- package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
- package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
- package/src/__tests__/gpu-scene.unit.test.ts +119 -9
- package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
- package/src/__tests__/instances-culling.integration.test.ts +10 -36
- package/src/__tests__/instances-store.unit.test.ts +97 -305
- package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
- package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
- package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
- package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
- package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
- package/src/__tests__/motion-blur-compute.dawn.test.ts +13 -7
- package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
- package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
- package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
- package/src/__tests__/probe-bindings.unit.test.ts +582 -1
- package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
- package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +7 -0
- package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +21 -1
- package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
- package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
- package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +20 -20
- package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
- package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
- package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
- package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +26 -10
- package/src/__tests__/resource-allocation.unit.test.ts +123 -0
- package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
- package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +15 -0
- package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
- package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +182 -1
- package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
- package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
- package/src/__tests__/temporal-performance.integration.test.ts +3 -3
- package/src/__tests__/visibility-instances.integration.test.ts +7 -19
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
- package/src/__tests__/volume-optics-oracle.unit.test.ts +4 -2
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +30 -20
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
- package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +113 -9
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +5 -3
- package/src/assembly/backend-contract.ts +30 -0
- package/src/assembly/barrel-distortion-inspection.ts +22 -0
- package/src/assembly/canvas-draw-guard.ts +11 -0
- package/src/assembly/cloud-shadow-entry.ts +22 -0
- package/src/assembly/dynamic-geometry-host.ts +65 -37
- package/src/assembly/dynamic-geometry-runtime.ts +7 -1
- package/src/assembly/factory.ts +2 -30
- package/src/assembly/host-contract.ts +3 -3
- package/src/assembly/material/assembly.ts +25 -13
- package/src/assembly/material/pipeline-helpers.ts +53 -12
- package/src/assembly/material-shader-policy.ts +55 -0
- package/src/assembly/material-shader-prewarm.ts +21 -10
- package/src/assembly/recovery/device-loss-fanout.ts +13 -0
- package/src/assembly/recovery/renderer-recover.ts +41 -18
- package/src/assembly/render-feature-post-processes.ts +54 -0
- package/src/assembly/renderer-facade.ts +4 -1
- package/src/assembly/renderer-feature-inspection.ts +8 -2
- package/src/assembly/renderer-frame-transaction.ts +23 -0
- package/src/assembly/renderer-inspection.ts +28 -0
- package/src/assembly/skin-palette-owner.ts +1 -1
- package/src/assembly/temporal-post-process-prewarm.ts +3 -3
- package/src/assembly/webgpu-pbr-ready.ts +9 -1
- package/src/assembly/webgpu-ready-per-pass.ts +104 -0
- package/src/assembly/webgpu-ready.ts +115 -61
- package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
- package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
- package/src/assembly/webgpu-renderer.ts +214 -252
- package/src/assembly/webgpu-vertex-layouts.ts +1 -1
- package/src/barrel-distortion.ts +247 -0
- package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
- package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
- package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
- package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
- package/src/cloud/density.ts +600 -0
- package/src/cloud/extract.ts +110 -0
- package/src/cloud/feature.ts +1810 -0
- package/src/cloud/frame-context.ts +42 -0
- package/src/cloud/inspection.ts +130 -0
- package/src/cloud/optics.ts +285 -0
- package/src/cloud/parameters.ts +255 -0
- package/src/cloud/resources.ts +122 -0
- package/src/cloud/shadow.ts +167 -0
- package/src/cloud/temporal.ts +226 -0
- package/src/components/barrel-distortion.ts +46 -0
- package/src/components/cloud-layer.ts +47 -0
- package/src/components/index.ts +2 -0
- package/src/components/instances.ts +18 -45
- package/src/components/sprite-instances.ts +1 -1
- package/src/construct-renderer.ts +1 -1
- package/src/dynamic-geometry.ts +12 -1
- package/src/errors/cloud.ts +103 -0
- package/src/errors/render.ts +44 -7
- package/src/examples/single-layer-medium-surface.ts +59 -0
- package/src/extract/camera.ts +15 -1
- package/src/extract/contracts.ts +5 -0
- package/src/features/barrel-distortion.ts +172 -0
- package/src/features/error-reporting.ts +26 -6
- package/src/features/host.ts +115 -14
- package/src/features/plan.ts +8 -0
- package/src/features/render-graph-contribution.ts +9 -0
- package/src/features/render-graph-raster.ts +45 -3
- package/src/features/targets.ts +66 -0
- package/src/features/types.ts +47 -0
- package/src/fullscreen-post-process-pass.ts +41 -9
- package/src/gpu-dirty-ranges.ts +48 -0
- package/src/gpu-driven/batch-topology.ts +53 -0
- package/src/gpu-driven/material-artifacts.ts +33 -25
- package/src/gpu-driven/prepared-draw.ts +12 -4
- package/src/gpu-driven/production-raster-material.ts +1894 -0
- package/src/gpu-driven/production-raster.ts +825 -784
- package/src/gpu-driven/resource-allocation.ts +142 -0
- package/src/gpu-driven/shadow-ownership.ts +71 -0
- package/src/gpu-driven/view-gpu.ts +164 -10
- package/src/gpu-scene.ts +75 -84
- package/src/hdrp-buffers.ts +4 -3
- package/src/ibl/skylight-bind-group.ts +49 -7
- package/src/index.ts +196 -9
- package/src/inspection-types.ts +162 -0
- package/src/instance-buffer-cache.ts +4 -4
- package/src/instances.ts +149 -569
- package/src/pbr-pipeline.ts +219 -13
- package/src/pipeline/single-layer-medium-passes.ts +267 -0
- package/src/pipeline/standard-forward-lane.ts +313 -57
- package/src/pipeline/standard-output/graph.ts +8 -2
- package/src/pipeline/standard-output/types.ts +12 -1
- package/src/pipeline/standard-pipeline.ts +291 -18
- package/src/pipeline/standard-post.ts +90 -24
- package/src/pipeline-builder.ts +1 -1
- package/src/plugin.ts +4 -0
- package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
- package/src/prepare/prepared-graphics-resolver.ts +2 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
- package/src/record/__tests__/temporal-payload.unit.test.ts +1 -1
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
- package/src/record/barrel-distortion-frame.ts +110 -0
- package/src/record/dynamic-geometry-consumption.ts +39 -9
- package/src/record/fold-buckets.ts +852 -0
- package/src/record/fold-instance-buffer.ts +51 -0
- package/src/record/frame-lighting.ts +36 -2
- package/src/record/frame-snapshot.ts +39 -3
- package/src/record/frame.ts +217 -1084
- package/src/record/gpu-draw-receipts.ts +38 -0
- package/src/record/gpu-timing.ts +48 -5
- package/src/record/main-pass-geometry.ts +251 -82
- package/src/record/main-pass-material.ts +66 -4
- package/src/record/main-pass-sprite-draws.ts +7 -65
- package/src/record/main-pass.ts +156 -20
- package/src/record/motion-blur-receipt.ts +91 -0
- package/src/record/probe-blend-buffer.ts +180 -0
- package/src/record/recovery-pipeline.ts +35 -3
- package/src/record/render-context.ts +39 -3
- package/src/record/shadow-pass.ts +30 -1
- package/src/record/skybox-post-pass.ts +28 -4
- package/src/record/typed-frame-graph.ts +770 -134
- package/src/record/view-ubo.ts +26 -3
- package/src/record/volume-params.ts +225 -0
- package/src/recovery/render-system-candidate.ts +235 -42
- package/src/reflection/record-owner.ts +7 -3
- package/src/render-contract.ts +48 -4
- package/src/render-graph-primitives.ts +7 -1
- package/src/render-pipeline.ts +130 -0
- package/src/render-system-extract-tail.ts +57 -61
- package/src/render-system-extract.ts +74 -76
- package/src/render-system-presentation.ts +56 -0
- package/src/render-system-producer-roots.ts +96 -0
- package/src/render-system-residency.ts +159 -0
- package/src/render-system.ts +347 -363
- package/src/scene/probe-blend.ts +96 -4
- package/src/scene/render-scene.ts +349 -280
- package/src/ssao-buffers.ts +95 -2
- package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
- package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
- package/src/surface/dynamic-input.ts +658 -0
- package/src/surface/gpu-driven.ts +147 -0
- package/src/surface/submission-observation.ts +517 -0
- package/src/systems/skin-palette-allocator.ts +4 -1
- package/src/temporal/gpu.ts +101 -17
- package/src/temporal/inspection.ts +7 -1
- package/src/temporal/standard-scene-data.ts +5 -0
- package/src/temporal/target-coverage-attachment.ts +4 -0
- package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
- package/src/transmission/single-layer-medium.ts +322 -0
- package/src/typed-render-graph-primitives.ts +70 -2
- package/src/volume/component.ts +10 -9
- package/src/volume/extract.ts +11 -5
- package/src/volume/inspection.ts +3 -0
- package/src/volume/passes.ts +87 -4
- package/src/volume/resources.ts +2 -1
- package/dist/chunk-3RTCJTH4.mjs.map +0 -1
- package/dist/chunk-NB2S7ZRX.mjs.map +0 -1
- package/dist/chunk-NNQL7CB3.mjs +0 -1713
- package/dist/chunk-NNQL7CB3.mjs.map +0 -1
- package/dist/chunk-OO3DRYK7.mjs.map +0 -1
- package/dist/chunk-PA5DRJNI.mjs.map +0 -1
- package/dist/chunk-PLAXOOX5.mjs.map +0 -1
- package/dist/chunk-VOZWDM3L.mjs.map +0 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Buffer, MappedBuffer, RhiDevice, Texture } from '@forgeax/engine-rhi';
|
|
2
|
+
import { ok } from '@forgeax/engine-types';
|
|
3
|
+
import { expect, it, vi } from 'vitest';
|
|
4
|
+
import { createRendererObservationCaptureOwner } from '../assembly/webgpu-renderer-observation-owner';
|
|
5
|
+
|
|
6
|
+
function deferred<T>(): { readonly promise: Promise<T>; readonly resolve: (value: T) => void } {
|
|
7
|
+
let resolve!: (value: T) => void;
|
|
8
|
+
const promise = new Promise<T>((next) => {
|
|
9
|
+
resolve = next;
|
|
10
|
+
});
|
|
11
|
+
return { promise, resolve };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
it('retires a receipt observation readback only after its pending map completes', async () => {
|
|
15
|
+
const mapStarted = deferred<void>();
|
|
16
|
+
const mapCompleted = deferred<ReturnType<typeof ok<MappedBuffer>>>();
|
|
17
|
+
const mappedBytes = new ArrayBuffer(256);
|
|
18
|
+
const unmap = vi.fn();
|
|
19
|
+
const buffer = {
|
|
20
|
+
mapAsync: async () => {
|
|
21
|
+
mapStarted.resolve();
|
|
22
|
+
return mapCompleted.promise;
|
|
23
|
+
},
|
|
24
|
+
} as unknown as Buffer;
|
|
25
|
+
const texture = {} as Texture;
|
|
26
|
+
const destroyBuffer = vi.fn(() => ok(undefined));
|
|
27
|
+
const device = { destroyBuffer } as unknown as RhiDevice;
|
|
28
|
+
const errors: unknown[] = [];
|
|
29
|
+
const owner = createRendererObservationCaptureOwner((error) => errors.push(error));
|
|
30
|
+
const capture = {
|
|
31
|
+
domain: 'linear-hdr' as const,
|
|
32
|
+
format: 'rgba8unorm' as const,
|
|
33
|
+
device,
|
|
34
|
+
texture,
|
|
35
|
+
buffer,
|
|
36
|
+
frameNumber: 17,
|
|
37
|
+
deviceGeneration: 3,
|
|
38
|
+
graphGeneration: 9,
|
|
39
|
+
backendId: 'null' as const,
|
|
40
|
+
width: 1,
|
|
41
|
+
height: 1,
|
|
42
|
+
bytesPerRow: 256,
|
|
43
|
+
};
|
|
44
|
+
owner.observationCaptureOwner.register(capture);
|
|
45
|
+
expect(owner.observationCaptureOwner.consume(17)).toEqual([capture]);
|
|
46
|
+
|
|
47
|
+
const pending = owner.readObservationCapture(capture);
|
|
48
|
+
await mapStarted.promise;
|
|
49
|
+
expect(destroyBuffer).not.toHaveBeenCalled();
|
|
50
|
+
expect(owner.stats).toMatchObject({ liveCount: 1, mapCount: 1, readbackCount: 0 });
|
|
51
|
+
|
|
52
|
+
mapCompleted.resolve(
|
|
53
|
+
ok({
|
|
54
|
+
getMappedRange: () => ok(mappedBytes),
|
|
55
|
+
unmap,
|
|
56
|
+
} as unknown as MappedBuffer),
|
|
57
|
+
);
|
|
58
|
+
await expect(pending).resolves.toMatchObject({ ok: true, value: new Uint8Array(256) });
|
|
59
|
+
expect(unmap).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(destroyBuffer).toHaveBeenCalledTimes(1);
|
|
61
|
+
expect(owner.stats).toMatchObject({ liveCount: 0, mapCount: 1, readbackCount: 1 });
|
|
62
|
+
expect(errors).toEqual([]);
|
|
63
|
+
expect(owner.disposeReceiptObservationCaptures()).toBeUndefined();
|
|
64
|
+
expect(destroyBuffer).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
@@ -22,6 +22,10 @@ const rendererRecoverySource = readFileSync(
|
|
|
22
22
|
resolve(import.meta.dirname, '../assembly/recovery/renderer-recover.ts'),
|
|
23
23
|
'utf8',
|
|
24
24
|
);
|
|
25
|
+
const recoveryPipelineSource = readFileSync(
|
|
26
|
+
resolve(import.meta.dirname, '../record/recovery-pipeline.ts'),
|
|
27
|
+
'utf8',
|
|
28
|
+
);
|
|
25
29
|
|
|
26
30
|
describe('recovery candidate preparation contract', () => {
|
|
27
31
|
it('does not restore the lost device SSR history when publishing a detached candidate', () => {
|
|
@@ -145,6 +149,31 @@ describe('recovery candidate preparation contract', () => {
|
|
|
145
149
|
expect(resolver).not.toContain('internals.device.limits');
|
|
146
150
|
});
|
|
147
151
|
|
|
152
|
+
it('rehydrates capability variants before copying late-installed material declarations', () => {
|
|
153
|
+
const preparation = rendererRecoverySource.indexOf('await prepareMaterialShaders(');
|
|
154
|
+
const copy = rendererRecoverySource.indexOf(
|
|
155
|
+
'const activeShaderCatalog = getActiveShaderState().shaderInstance',
|
|
156
|
+
);
|
|
157
|
+
expect(preparation).toBeGreaterThanOrEqual(0);
|
|
158
|
+
expect(copy).toBeGreaterThan(preparation);
|
|
159
|
+
const copyBlock = rendererRecoverySource.slice(copy);
|
|
160
|
+
expect(copyBlock).toContain('candidateShaderCatalog.findMaterialArtifact(identifier).ok');
|
|
161
|
+
expect(copyBlock).toContain('candidateShaderCatalog.installMaterialArtifact(identifier');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('rehydrates Surface dynamic page bytes before candidate GPU preparation', () => {
|
|
165
|
+
const start = recoveryCandidateSource.indexOf('let recoverySurfaceDynamicInput =');
|
|
166
|
+
const end = recoveryCandidateSource.indexOf(
|
|
167
|
+
'if (candidateGpuDrivenScene.state !== undefined)',
|
|
168
|
+
start,
|
|
169
|
+
);
|
|
170
|
+
const surface = recoveryCandidateSource.slice(start, end);
|
|
171
|
+
expect(surface).toContain('candidateSurfacePage.bytes.set(source.page.bytes)');
|
|
172
|
+
expect(surface.indexOf('candidateSurfacePage.bytes.set(source.page.bytes)')).toBeLessThan(
|
|
173
|
+
surface.indexOf('recoverySurfaceDynamicInput = {'),
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
148
177
|
it('keeps render-system residency bindings live across publication', () => {
|
|
149
178
|
const start = rendererSource.indexOf('const renderSystem: RenderSystem = createRenderSystem({');
|
|
150
179
|
const end = rendererSource.indexOf('errorRegistry: internals.errorRegistry', start);
|
|
@@ -155,4 +184,44 @@ describe('recovery candidate preparation contract', () => {
|
|
|
155
184
|
expect(assembly).not.toMatch(/\n\s*gpuStore,\n/);
|
|
156
185
|
expect(assembly).not.toMatch(/\n\s*dynamicTextureStore,\n/);
|
|
157
186
|
});
|
|
187
|
+
|
|
188
|
+
it('rebuilds candidate shaders from accepted receipts with candidate-bound adapters', () => {
|
|
189
|
+
expect(rendererRecoverySource).toContain('entry.value.receipt === undefined');
|
|
190
|
+
expect(rendererRecoverySource).toContain('{ receipt: entry.value.receipt }');
|
|
191
|
+
expect(rendererRecoverySource).toContain(
|
|
192
|
+
'getShaderModuleAdapter().createShaderModule(descriptor)',
|
|
193
|
+
);
|
|
194
|
+
expect(rendererRecoverySource).toContain(
|
|
195
|
+
'getImmediateShaderModuleAdapter().createShaderModule(descriptor)',
|
|
196
|
+
);
|
|
197
|
+
expect(renderSystemSource).toContain('materialArtifacts: frameState.recoveryMaterialArtifacts');
|
|
198
|
+
expect(recoveryCandidateSource).toContain('materialArtifacts.size === 0');
|
|
199
|
+
expect(recoveryCandidateSource).toContain('{ materialArtifacts }');
|
|
200
|
+
expect(recoveryCandidateSource).toContain('domain: range.domain');
|
|
201
|
+
expect(recoveryCandidateSource).not.toContain('recovery-water-domain');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('prepares the submitted forward specialization with the direct Surface ABI', () => {
|
|
205
|
+
expect(recoveryPipelineSource).toContain('readonly dispatch: readonly DispatchEntry[]');
|
|
206
|
+
expect(recoveryPipelineSource).toContain(
|
|
207
|
+
"candidate.tags.LightMode !== 'Forward' || candidate.materialShaderId === undefined",
|
|
208
|
+
);
|
|
209
|
+
expect(recoveryPipelineSource).toContain('forwardProgram?.materialShaderId');
|
|
210
|
+
expect(recoveryPipelineSource).toContain("'surface-direct-cluster-pbr' as const");
|
|
211
|
+
expect(recoveryPipelineSource).toContain("'surface-direct-pbr' as const");
|
|
212
|
+
expect(recoveryPipelineSource).toContain("'immediate',");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('retires the candidate display view before publishing its prepared owners', () => {
|
|
216
|
+
const start = recoveryCandidateSource.indexOf('const publishRecoveryGraphCandidate');
|
|
217
|
+
const publish = recoveryCandidateSource.slice(start);
|
|
218
|
+
const retire = publish.indexOf('candidateDisplayGraph?.retire()');
|
|
219
|
+
const publication = publish.indexOf('Object.assign(frameState, candidate.frameState)');
|
|
220
|
+
|
|
221
|
+
expect(retire).toBeGreaterThanOrEqual(0);
|
|
222
|
+
expect(publication).toBeGreaterThan(retire);
|
|
223
|
+
expect(publish).toContain('candidate.frameState.compiledFrameGraph = null');
|
|
224
|
+
expect(publish).toContain('candidate.frameState.compiledFrameGraphTopologyKey = null');
|
|
225
|
+
expect(publish).toContain('candidate.frameState.perFrameGraph = null');
|
|
226
|
+
});
|
|
158
227
|
});
|
|
@@ -39,7 +39,9 @@ const expectedCodes = [
|
|
|
39
39
|
'dynamic-resolution-invalid-parameter',
|
|
40
40
|
'dynamic-resolution-requires-taa',
|
|
41
41
|
'dynamic-resolution-timing-unavailable',
|
|
42
|
+
'barrel-distortion-invalid-parameter',
|
|
42
43
|
'motion-blur-invalid-params',
|
|
44
|
+
|
|
43
45
|
'shadow-invalid-config',
|
|
44
46
|
'equirect-projection-failed',
|
|
45
47
|
'standard-light-budget-exceeded',
|
|
@@ -87,6 +89,11 @@ const expectedCodes = [
|
|
|
87
89
|
'volume-invalid-bounds',
|
|
88
90
|
'volume-invalid-parameters',
|
|
89
91
|
'projector-binding-failed',
|
|
92
|
+
'cloud-layer-invalid-parameter',
|
|
93
|
+
'cloud-layer-owner-conflict',
|
|
94
|
+
'cloud-layer-cache-invalid',
|
|
95
|
+
'cloud-layer-capability-missing',
|
|
96
|
+
'cloud-layer-resource-failed',
|
|
90
97
|
] as const satisfies readonly RenderErrorCode[];
|
|
91
98
|
type ExpectedCodeUnion = (typeof expectedCodes)[number];
|
|
92
99
|
|
|
@@ -12,7 +12,17 @@ function gatedFeature(): RenderFeature<{ readonly frame: number }> {
|
|
|
12
12
|
identity: 'synthetic.gated',
|
|
13
13
|
requiredCapabilities: ['compute'],
|
|
14
14
|
extract: ({ frameNumber }) => ok({ frame: frameNumber }),
|
|
15
|
-
plan: () =>
|
|
15
|
+
plan: () =>
|
|
16
|
+
ok({
|
|
17
|
+
resources: [
|
|
18
|
+
{
|
|
19
|
+
kind: 'fullscreen-program' as const,
|
|
20
|
+
name: 'synthetic.gated.program',
|
|
21
|
+
source: 'synthetic',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
passes: [],
|
|
25
|
+
}),
|
|
16
26
|
};
|
|
17
27
|
}
|
|
18
28
|
|
|
@@ -82,7 +92,10 @@ describe('render feature diagnostics and capability gate', () => {
|
|
|
82
92
|
caps: caps(false),
|
|
83
93
|
});
|
|
84
94
|
|
|
85
|
-
expect(first.stageEvents).toEqual([
|
|
95
|
+
expect(first.stageEvents).toEqual([
|
|
96
|
+
{ featureIdentity: 'synthetic.gated', order: 0, stage: 'extract' },
|
|
97
|
+
{ featureIdentity: 'synthetic.gated', order: 0, stage: 'plan' },
|
|
98
|
+
]);
|
|
86
99
|
expect(first.errors).toHaveLength(1);
|
|
87
100
|
expect(first.errors[0]?.code).toBe('render-feature-capability-missing');
|
|
88
101
|
expect(host.diagnostics()[0]?.status).toBe('disabled');
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { type RenderError, RenderFeatureStageFailedError } from '../errors/render.js';
|
|
2
3
|
import {
|
|
3
4
|
PENDING_RENDER_FEATURE_ERROR_REPORT_INTERVAL_FRAMES,
|
|
5
|
+
type PendingRenderFeatureErrorReport,
|
|
6
|
+
reportPendingRenderFeatureErrors,
|
|
4
7
|
shouldReportPendingRenderFeatureError,
|
|
5
8
|
} from '../features/error-reporting.js';
|
|
6
9
|
|
|
7
10
|
describe('pending render-feature error reporting', () => {
|
|
8
11
|
it('reports the first failure, suppresses the hot-loop, and reopens the cooldown', () => {
|
|
9
|
-
const lastReportedFrame = new Map<string,
|
|
12
|
+
const lastReportedFrame = new Map<string, PendingRenderFeatureErrorReport>();
|
|
10
13
|
expect(shouldReportPendingRenderFeatureError(lastReportedFrame, 'feature\0plan', 10)).toBe(
|
|
11
14
|
true,
|
|
12
15
|
);
|
|
@@ -22,3 +25,20 @@ describe('pending render-feature error reporting', () => {
|
|
|
22
25
|
).toBe(true);
|
|
23
26
|
});
|
|
24
27
|
});
|
|
28
|
+
|
|
29
|
+
describe('changed render-feature failures', () => {
|
|
30
|
+
it('reports a new cause immediately without flooding repeats', () => {
|
|
31
|
+
const lastReportedFrame = new Map<string, PendingRenderFeatureErrorReport>();
|
|
32
|
+
const reported: RenderError[] = [];
|
|
33
|
+
const registry = { fire: (error: RenderError) => reported.push(error) };
|
|
34
|
+
const failure = (message: string) =>
|
|
35
|
+
new RenderFeatureStageFailedError('recovery', 0, 'plan', 'next-frame', new Error(message));
|
|
36
|
+
reportPendingRenderFeatureErrors([failure('invalid-format')], 1, lastReportedFrame, registry);
|
|
37
|
+
reportPendingRenderFeatureErrors([failure('invalid-format')], 2, lastReportedFrame, registry);
|
|
38
|
+
reportPendingRenderFeatureErrors([failure('cycle')], 3, lastReportedFrame, registry);
|
|
39
|
+
expect(reported).toHaveLength(2);
|
|
40
|
+
reportPendingRenderFeatureErrors([failure('cycle')], 4, lastReportedFrame, registry);
|
|
41
|
+
expect(reported).toHaveLength(2);
|
|
42
|
+
expect(lastReportedFrame.size).toBe(1);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -23,7 +23,16 @@ function feature(mode: 'healthy' | 'failed'): RenderFeature<{ readonly ready: tr
|
|
|
23
23
|
'next-frame',
|
|
24
24
|
),
|
|
25
25
|
)
|
|
26
|
-
: ok({
|
|
26
|
+
: ok({
|
|
27
|
+
resources: [
|
|
28
|
+
{
|
|
29
|
+
kind: 'fullscreen-program' as const,
|
|
30
|
+
name: 'synthetic.diagnostics.program',
|
|
31
|
+
source: 'synthetic',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
passes: [],
|
|
35
|
+
}),
|
|
27
36
|
};
|
|
28
37
|
}
|
|
29
38
|
|
|
@@ -55,7 +55,17 @@ describe('render feature retry rules', () => {
|
|
|
55
55
|
identity: 'synthetic.capability-retry',
|
|
56
56
|
requiredCapabilities: ['compute'],
|
|
57
57
|
extract: ({ frameNumber }) => ok({ frame: frameNumber }),
|
|
58
|
-
plan: () =>
|
|
58
|
+
plan: () =>
|
|
59
|
+
ok({
|
|
60
|
+
resources: [
|
|
61
|
+
{
|
|
62
|
+
kind: 'fullscreen-program' as const,
|
|
63
|
+
name: 'synthetic.capability-retry.program',
|
|
64
|
+
source: 'synthetic',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
passes: [],
|
|
68
|
+
}),
|
|
59
69
|
};
|
|
60
70
|
const host = createRenderFeatureHost([feature], caps(false)).unwrap();
|
|
61
71
|
|
|
@@ -65,6 +75,7 @@ describe('render feature retry rules', () => {
|
|
|
65
75
|
frameNumber: 1,
|
|
66
76
|
caps: caps(false),
|
|
67
77
|
});
|
|
78
|
+
expect(host.diagnostics()[0]?.status).toBe('disabled');
|
|
68
79
|
expect(
|
|
69
80
|
runRenderFeatureFrame(host, {
|
|
70
81
|
worlds: [],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AssetRegistry, HANDLE_CUBE } from '@forgeax/engine-assets-runtime';
|
|
1
|
+
import { AssetRegistry, HANDLE_CUBE, RuntimeMaterialValue } from '@forgeax/engine-assets-runtime';
|
|
2
2
|
import type { EntityHandle } from '@forgeax/engine-ecs';
|
|
3
3
|
import { World } from '@forgeax/engine-ecs';
|
|
4
4
|
import { createRenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
Visibility,
|
|
16
16
|
VisibilityStateValue,
|
|
17
17
|
} from '../components';
|
|
18
|
-
import {
|
|
18
|
+
import { InstanceProjectionStore } from '../instances';
|
|
19
19
|
import { type ExtractedFrame, extractFrames } from '../render-system-extract';
|
|
20
20
|
import {
|
|
21
21
|
type PersistentRenderCandidateRequest,
|
|
@@ -47,7 +47,7 @@ function material(values: Record<string, unknown>): MaterialAsset {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function createWorld(
|
|
50
|
+
function createWorld(withCamera: boolean) {
|
|
51
51
|
const world = new World();
|
|
52
52
|
registerPropagateTransforms(world);
|
|
53
53
|
const values: Record<string, unknown> = {
|
|
@@ -80,7 +80,6 @@ function createWorld(instanceCollections: InstanceCollectionStore, withCamera: b
|
|
|
80
80
|
{ component: Visibility, data: { state: VisibilityStateValue.visible } },
|
|
81
81
|
)
|
|
82
82
|
.unwrap();
|
|
83
|
-
const mixedCollection = instanceCollections.create({ transforms: matrices(2, 0) }).unwrap();
|
|
84
83
|
const mixed = world
|
|
85
84
|
.spawn(
|
|
86
85
|
{ component: Transform, data: { pos: [0, 0, 0] } },
|
|
@@ -88,7 +87,7 @@ function createWorld(instanceCollections: InstanceCollectionStore, withCamera: b
|
|
|
88
87
|
{ component: Visibility, data: { state: VisibilityStateValue.inherited } },
|
|
89
88
|
{ component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
|
|
90
89
|
{ component: MeshRenderer, data: { materials: [materialHandle] } },
|
|
91
|
-
{ component: Instances, data: {
|
|
90
|
+
{ component: Instances, data: { transforms: matrices(2, 0) } },
|
|
92
91
|
)
|
|
93
92
|
.unwrap();
|
|
94
93
|
const sharedConsumer = world
|
|
@@ -119,7 +118,6 @@ function createWorld(instanceCollections: InstanceCollectionStore, withCamera: b
|
|
|
119
118
|
world,
|
|
120
119
|
values,
|
|
121
120
|
materialHandle,
|
|
122
|
-
mixedCollectionId: mixedCollection.collectionId,
|
|
123
121
|
mixed,
|
|
124
122
|
sharedConsumer,
|
|
125
123
|
hiddenTarget,
|
|
@@ -167,10 +165,59 @@ function rowFor(frame: ExtractedFrame, worldId: number, entityKey: EntityHandle)
|
|
|
167
165
|
}
|
|
168
166
|
|
|
169
167
|
describe('PersistentRenderScene mixed World updates', () => {
|
|
168
|
+
it('publishes instance inspection only with the accepted Scene and retires removed components', () => {
|
|
169
|
+
const { world, mixed } = createWorld(true);
|
|
170
|
+
const instanceCollections = new InstanceProjectionStore();
|
|
171
|
+
const scene = new PersistentRenderScene({ instanceCollections });
|
|
172
|
+
const lease = createRenderReadLease(world);
|
|
173
|
+
const owner = { cameraOwner: 0, resourceOwner: 0 };
|
|
174
|
+
let rejectCandidate = false;
|
|
175
|
+
const build = (request: PersistentRenderCandidateRequest) => {
|
|
176
|
+
const frame = extractFrames([world], owner, undefined, undefined, undefined, {
|
|
177
|
+
cull: 'none',
|
|
178
|
+
retainHidden: true,
|
|
179
|
+
renderables: request,
|
|
180
|
+
instanceCollections,
|
|
181
|
+
});
|
|
182
|
+
if (rejectCandidate && request !== 'none') throw new Error('reject prepared candidate');
|
|
183
|
+
return frame;
|
|
184
|
+
};
|
|
185
|
+
const draw = () => scene.extractComposition([world], owner, 0, build, [lease]);
|
|
186
|
+
try {
|
|
187
|
+
draw();
|
|
188
|
+
const before = instanceCollections._inspections(0);
|
|
189
|
+
expect(before).toHaveLength(1);
|
|
190
|
+
world.set(mixed, Instances, { transforms: matrices(3, 9) }).unwrap();
|
|
191
|
+
rejectCandidate = true;
|
|
192
|
+
expect(draw).toThrow('reject prepared candidate');
|
|
193
|
+
expect(instanceCollections._inspections(0)).toEqual(before);
|
|
194
|
+
rejectCandidate = false;
|
|
195
|
+
expect(rowFor(draw(), 0, mixed)?.instances?.instanceCount).toBe(3);
|
|
196
|
+
expect(instanceCollections._inspections(0)[0]?.revision).toBe((before[0]?.revision ?? 0) + 1);
|
|
197
|
+
world.removeComponent(mixed, Instances).unwrap();
|
|
198
|
+
draw();
|
|
199
|
+
expect(instanceCollections._inspections(0)).toEqual([]);
|
|
200
|
+
world
|
|
201
|
+
.addComponent(mixed, { component: Instances, data: { transforms: matrices(1, 2) } })
|
|
202
|
+
.unwrap();
|
|
203
|
+
draw();
|
|
204
|
+
expect(instanceCollections._inspections(0)[0]?.collectionId).not.toBe(
|
|
205
|
+
before[0]?.collectionId,
|
|
206
|
+
);
|
|
207
|
+
world.despawn(mixed).unwrap();
|
|
208
|
+
draw();
|
|
209
|
+
expect(instanceCollections._inspections(0)).toEqual([]);
|
|
210
|
+
} finally {
|
|
211
|
+
lease.dispose();
|
|
212
|
+
scene.dispose();
|
|
213
|
+
instanceCollections.dispose();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
170
217
|
it('reconciles mixed fields against a fresh extract while retaining other slot history', () => {
|
|
171
|
-
const instanceCollections = new
|
|
172
|
-
const first = createWorld(
|
|
173
|
-
const second = createWorld(
|
|
218
|
+
const instanceCollections = new InstanceProjectionStore();
|
|
219
|
+
const first = createWorld(true);
|
|
220
|
+
const second = createWorld(false);
|
|
174
221
|
const worlds = [first.world, second.world] as const;
|
|
175
222
|
const leases = [
|
|
176
223
|
createRenderReadLease(first.world),
|
|
@@ -226,15 +273,24 @@ describe('PersistentRenderScene mixed World updates', () => {
|
|
|
226
273
|
expect(initialOtherWorld).toBeDefined();
|
|
227
274
|
|
|
228
275
|
requests.length = 0;
|
|
229
|
-
|
|
276
|
+
first.world.set(first.mixed, Instances, { transforms: matrices(3, 2) }).unwrap();
|
|
230
277
|
first.world.set(first.mixed, Transform, { pos: [0.25, 0, 0] }).unwrap();
|
|
231
278
|
first.world.set(first.mixed, ChildOf, { parent: first.parentB }).unwrap();
|
|
232
279
|
first.world.set(first.parentA, Visibility, { state: VisibilityStateValue.hidden }).unwrap();
|
|
233
280
|
first.world
|
|
234
281
|
.set(first.hiddenTarget, Visibility, { state: VisibilityStateValue.hidden })
|
|
235
282
|
.unwrap();
|
|
236
|
-
first.
|
|
237
|
-
|
|
283
|
+
first.world
|
|
284
|
+
.spawn({
|
|
285
|
+
component: RuntimeMaterialValue,
|
|
286
|
+
data: {
|
|
287
|
+
asset: first.materialHandle,
|
|
288
|
+
parameter: 'baseColor',
|
|
289
|
+
kind: 2,
|
|
290
|
+
value: [0, 1, 0, 1],
|
|
291
|
+
},
|
|
292
|
+
})
|
|
293
|
+
.unwrap();
|
|
238
294
|
first.world.update(0).unwrap();
|
|
239
295
|
|
|
240
296
|
const mixed = draw();
|
|
@@ -264,7 +320,7 @@ describe('PersistentRenderScene mixed World updates', () => {
|
|
|
264
320
|
submit(mixed);
|
|
265
321
|
|
|
266
322
|
requests.length = 0;
|
|
267
|
-
|
|
323
|
+
first.world.set(first.mixed, Instances, { transforms: matrices(0, 0) }).unwrap();
|
|
268
324
|
first.world.update(0).unwrap();
|
|
269
325
|
const zero = draw();
|
|
270
326
|
expect(frameFacts(zero)).toEqual(frameFacts(drawableOracle()));
|
|
@@ -274,7 +330,7 @@ describe('PersistentRenderScene mixed World updates', () => {
|
|
|
274
330
|
submit(zero);
|
|
275
331
|
|
|
276
332
|
requests.length = 0;
|
|
277
|
-
|
|
333
|
+
first.world.set(first.mixed, Instances, { transforms: matrices(1, -2) }).unwrap();
|
|
278
334
|
first.world.update(0).unwrap();
|
|
279
335
|
const one = draw();
|
|
280
336
|
expect(frameFacts(one)).toEqual(frameFacts(drawableOracle()));
|
|
@@ -290,9 +346,9 @@ describe('PersistentRenderScene mixed World updates', () => {
|
|
|
290
346
|
});
|
|
291
347
|
|
|
292
348
|
it('retains slot and temporal history across World reorder and catalog refresh', () => {
|
|
293
|
-
const instanceCollections = new
|
|
294
|
-
const first = createWorld(
|
|
295
|
-
const second = createWorld(
|
|
349
|
+
const instanceCollections = new InstanceProjectionStore();
|
|
350
|
+
const first = createWorld(true);
|
|
351
|
+
const second = createWorld(false);
|
|
296
352
|
let worlds: readonly World[] = [first.world, second.world];
|
|
297
353
|
let leases = [createRenderReadLease(first.world), createRenderReadLease(second.world)];
|
|
298
354
|
let owner = { cameraOwner: 0, resourceOwner: 0 };
|
|
@@ -373,8 +429,8 @@ describe('PersistentRenderScene mixed World updates', () => {
|
|
|
373
429
|
});
|
|
374
430
|
|
|
375
431
|
it('scopes parent and leaf visibility changes while preserving unrelated snapshots', () => {
|
|
376
|
-
const instanceCollections = new
|
|
377
|
-
const first = createWorld(
|
|
432
|
+
const instanceCollections = new InstanceProjectionStore();
|
|
433
|
+
const first = createWorld(true);
|
|
378
434
|
const lease = createRenderReadLease(first.world);
|
|
379
435
|
const scene = new PersistentRenderScene({ instanceCollections });
|
|
380
436
|
const assets = new AssetRegistry({
|
|
@@ -424,3 +480,73 @@ describe('PersistentRenderScene mixed World updates', () => {
|
|
|
424
480
|
}
|
|
425
481
|
});
|
|
426
482
|
});
|
|
483
|
+
|
|
484
|
+
it('two Renderers independently observe shared content replacement, rebinding, deletion and retry', () => {
|
|
485
|
+
const fixture = createWorld(true);
|
|
486
|
+
const { world, materialHandle, sharedConsumer } = fixture;
|
|
487
|
+
const owner = { cameraOwner: 0, resourceOwner: 0 };
|
|
488
|
+
const assets = new AssetRegistry({
|
|
489
|
+
findMaterialArtifact: () => ({ ok: false, error: new Error('not registered') }),
|
|
490
|
+
} as unknown as ShaderRegistry);
|
|
491
|
+
const consumers = [0, 1].map(() => {
|
|
492
|
+
const instanceCollections = new InstanceProjectionStore();
|
|
493
|
+
const scene = new PersistentRenderScene({ instanceCollections });
|
|
494
|
+
const lease = createRenderReadLease(world);
|
|
495
|
+
let fail = false;
|
|
496
|
+
const build = (request: PersistentRenderCandidateRequest) => {
|
|
497
|
+
if (fail) {
|
|
498
|
+
fail = false;
|
|
499
|
+
throw new Error('injected extraction failure');
|
|
500
|
+
}
|
|
501
|
+
return extractFrames([world], owner, assets, undefined, scene.materialSnapshotCacheStore(), {
|
|
502
|
+
cull: 'none',
|
|
503
|
+
retainHidden: true,
|
|
504
|
+
renderables: request,
|
|
505
|
+
instanceCollections,
|
|
506
|
+
});
|
|
507
|
+
};
|
|
508
|
+
return {
|
|
509
|
+
scene,
|
|
510
|
+
lease,
|
|
511
|
+
draw: () => scene.extractComposition([world], owner, 0, build, [lease]),
|
|
512
|
+
fail: () => {
|
|
513
|
+
fail = true;
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
});
|
|
517
|
+
const color = (frame: ExtractedFrame) =>
|
|
518
|
+
Array.from(rowFor(frame, 0, sharedConsumer)?.material.baseColor ?? []);
|
|
519
|
+
try {
|
|
520
|
+
for (const consumer of consumers) expect(color(consumer.draw())).toEqual([1, 0, 0]);
|
|
521
|
+
const content = world
|
|
522
|
+
.spawn({
|
|
523
|
+
component: RuntimeMaterialValue,
|
|
524
|
+
data: {
|
|
525
|
+
asset: materialHandle,
|
|
526
|
+
parameter: 'baseColor',
|
|
527
|
+
kind: 2,
|
|
528
|
+
value: [0, 1, 0, 1],
|
|
529
|
+
},
|
|
530
|
+
})
|
|
531
|
+
.unwrap();
|
|
532
|
+
for (const consumer of consumers) {
|
|
533
|
+
consumer.fail();
|
|
534
|
+
expect(() => consumer.draw()).toThrow('injected extraction failure');
|
|
535
|
+
expect(color(consumer.draw())).toEqual([0, 1, 0]);
|
|
536
|
+
}
|
|
537
|
+
const other = world.allocSharedRef('MaterialAsset', material({ baseColor: [0, 0, 1, 1] }));
|
|
538
|
+
world.set(content, RuntimeMaterialValue, { asset: other }).unwrap();
|
|
539
|
+
for (const consumer of consumers) expect(color(consumer.draw())).toEqual([1, 0, 0]);
|
|
540
|
+
world
|
|
541
|
+
.set(content, RuntimeMaterialValue, { asset: materialHandle, value: [0, 0, 1, 1] })
|
|
542
|
+
.unwrap();
|
|
543
|
+
for (const consumer of consumers) expect(color(consumer.draw())).toEqual([0, 0, 1]);
|
|
544
|
+
world.despawn(content).unwrap();
|
|
545
|
+
for (const consumer of consumers) expect(color(consumer.draw())).toEqual([1, 0, 0]);
|
|
546
|
+
} finally {
|
|
547
|
+
for (const consumer of consumers) {
|
|
548
|
+
consumer.lease.dispose();
|
|
549
|
+
consumer.scene.dispose();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RuntimeMeshVertices } from '@forgeax/engine-assets-runtime';
|
|
1
2
|
import { World } from '@forgeax/engine-ecs';
|
|
2
3
|
import { createRenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
3
4
|
import { vec3 } from '@forgeax/engine-math';
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
PointLightShadow,
|
|
17
18
|
SpotLight,
|
|
18
19
|
} from '../components';
|
|
19
|
-
import {
|
|
20
|
+
import { InstanceProjectionStore } from '../instances';
|
|
20
21
|
import { makeZeroCameraFallbackSnapshot } from '../record/frame-snapshot';
|
|
21
22
|
import type {
|
|
22
23
|
DispatchEntry,
|
|
@@ -302,13 +303,12 @@ describe('RenderScene canonical owner', () => {
|
|
|
302
303
|
|
|
303
304
|
it('classifies an Instances-only collection revision without widening the content update', () => {
|
|
304
305
|
const world = new World();
|
|
305
|
-
const instanceCollections = new
|
|
306
|
+
const instanceCollections = new InstanceProjectionStore();
|
|
306
307
|
const initialTransforms = new Float32Array(16);
|
|
307
308
|
initialTransforms[0] = 1;
|
|
308
309
|
initialTransforms[5] = 1;
|
|
309
310
|
initialTransforms[10] = 1;
|
|
310
311
|
initialTransforms[15] = 1;
|
|
311
|
-
const collection = instanceCollections.create({ transforms: initialTransforms }).unwrap();
|
|
312
312
|
const entity = world
|
|
313
313
|
.spawn(
|
|
314
314
|
{
|
|
@@ -317,14 +317,18 @@ describe('RenderScene canonical owner', () => {
|
|
|
317
317
|
},
|
|
318
318
|
{ component: MeshFilter, data: { assetHandle: 1 as never } },
|
|
319
319
|
{ component: MeshRenderer, data: { materials: [] } },
|
|
320
|
-
{ component: Instances, data: {
|
|
320
|
+
{ component: Instances, data: { transforms: initialTransforms } },
|
|
321
321
|
)
|
|
322
322
|
.unwrap();
|
|
323
323
|
const lease = createRenderReadLease(world);
|
|
324
324
|
const requests: PersistentRenderCandidateRequest[] = [];
|
|
325
325
|
const build = (request: PersistentRenderCandidateRequest): ExtractedFrame => {
|
|
326
326
|
requests.push(request);
|
|
327
|
-
const detached = instanceCollections.
|
|
327
|
+
const detached = instanceCollections.project(
|
|
328
|
+
world,
|
|
329
|
+
entity,
|
|
330
|
+
world.get(entity, Instances).unwrap().transforms,
|
|
331
|
+
);
|
|
328
332
|
return {
|
|
329
333
|
cameras: [{ ...makeZeroCameraFallbackSnapshot(), worldId: 0, entityKey: 1 }],
|
|
330
334
|
renderables: [
|
|
@@ -349,7 +353,7 @@ describe('RenderScene canonical owner', () => {
|
|
|
349
353
|
]);
|
|
350
354
|
const nextTransforms = new Float32Array(initialTransforms);
|
|
351
355
|
nextTransforms[12] = 4;
|
|
352
|
-
|
|
356
|
+
world.set(entity, Instances, { transforms: nextTransforms }).unwrap();
|
|
353
357
|
persistent.extractComposition([world], { cameraOwner: 0, resourceOwner: 0 }, 0, build, [
|
|
354
358
|
lease,
|
|
355
359
|
]);
|
|
@@ -734,8 +738,8 @@ describe('RenderScene canonical owner', () => {
|
|
|
734
738
|
const world = new World();
|
|
735
739
|
const lease = createRenderReadLease(world);
|
|
736
740
|
const handle = world.allocSharedRef('MeshAsset', { kind: 'mesh' });
|
|
737
|
-
const
|
|
738
|
-
const persistent = new PersistentRenderScene({
|
|
741
|
+
const onRuntimeAssetChange = vi.fn();
|
|
742
|
+
const persistent = new PersistentRenderScene({ onRuntimeAssetChange });
|
|
739
743
|
const frame = {
|
|
740
744
|
cameras: [],
|
|
741
745
|
renderables: [],
|
|
@@ -746,12 +750,12 @@ describe('RenderScene canonical owner', () => {
|
|
|
746
750
|
persistent.extractComposition([world], { cameraOwner: 0, resourceOwner: 0 }, 0, () => frame, [
|
|
747
751
|
lease,
|
|
748
752
|
]);
|
|
749
|
-
world.
|
|
753
|
+
world.spawn({ component: RuntimeMeshVertices, data: { asset: handle, vertices: [] } }).unwrap();
|
|
750
754
|
persistent.extractComposition([world], { cameraOwner: 0, resourceOwner: 0 }, 0, () => frame, [
|
|
751
755
|
lease,
|
|
752
756
|
]);
|
|
753
757
|
|
|
754
|
-
expect(
|
|
758
|
+
expect(onRuntimeAssetChange).toHaveBeenCalledWith(0, handle);
|
|
755
759
|
lease.dispose();
|
|
756
760
|
persistent.dispose();
|
|
757
761
|
});
|
|
@@ -974,6 +978,7 @@ describe('RenderScene canonical owner', () => {
|
|
|
974
978
|
expect(projected.renderables).toEqual(cameraMoved.renderables);
|
|
975
979
|
expect(projected.dispatch).toEqual(cameraMoved.dispatch);
|
|
976
980
|
expect(initial.renderables).toHaveLength(1);
|
|
981
|
+
// Transform spans update poses without a second source-identity scan.
|
|
977
982
|
expect(persistent.inspect()).toMatchObject({ fullRebuilds: 1, worldEntitiesScanned: 0 });
|
|
978
983
|
} finally {
|
|
979
984
|
lease.dispose();
|
|
@@ -1373,7 +1378,7 @@ describe('RenderScene', () => {
|
|
|
1373
1378
|
} as unknown as ExtractedFrame;
|
|
1374
1379
|
|
|
1375
1380
|
const projected = scene.extractComposition(
|
|
1376
|
-
[
|
|
1381
|
+
[new World()],
|
|
1377
1382
|
{ cameraOwner: 0, resourceOwner: 0 },
|
|
1378
1383
|
0,
|
|
1379
1384
|
() => frame,
|
|
@@ -1515,3 +1520,23 @@ describe('projected bounds query', () => {
|
|
|
1515
1520
|
scene.dispose();
|
|
1516
1521
|
});
|
|
1517
1522
|
});
|
|
1523
|
+
|
|
1524
|
+
it('does not publish an earlier transform when later CPU candidate preparation fails', () => {
|
|
1525
|
+
const scene = new RenderScene();
|
|
1526
|
+
scene.apply([updateSnapshot(snapshot(1, 0)), updateSnapshot(snapshot(2, 0))]);
|
|
1527
|
+
const changed = snapshot(1, 7).transform.world;
|
|
1528
|
+
const malformed = snapshot(2, 9);
|
|
1529
|
+
Object.defineProperty(malformed, 'transform', {
|
|
1530
|
+
get() {
|
|
1531
|
+
throw new Error('candidate copy failure');
|
|
1532
|
+
},
|
|
1533
|
+
});
|
|
1534
|
+
expect(() =>
|
|
1535
|
+
scene.apply([
|
|
1536
|
+
{ kind: 'update', worldId: 0, entityKey: 1, world: changed },
|
|
1537
|
+
updateSnapshot(malformed),
|
|
1538
|
+
]),
|
|
1539
|
+
).toThrow('candidate copy failure');
|
|
1540
|
+
expect(scene.slot(0, 1)?.snapshot.transform.world[12]).toBe(0);
|
|
1541
|
+
expect(scene.slot(0, 2)?.snapshot.transform.world[12]).toBe(0);
|
|
1542
|
+
});
|