@forgeax/engine-render 0.1.27 → 0.1.29
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 +842 -124
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-host.d.ts +52 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +3 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/gpu-driven-owner.d.ts +10 -0
- package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +19 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +7 -0
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material-shader-policy.d.ts +3 -1
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/recovery/failure-location.d.ts +12 -0
- package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts +6 -4
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
- package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-helpers.d.ts +3 -0
- package/dist/assembly/renderer-helpers.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +4 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +6 -0
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
- package/dist/assembly/skin-palette-owner.d.ts +8 -0
- package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-device-loss.d.ts +15 -0
- package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +7 -3
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +7 -141
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
- package/dist/authoring.mjs +3 -3
- package/dist/{chunk-ADHHVYLW.mjs → chunk-33XZFTQU.mjs} +245 -39
- package/dist/chunk-33XZFTQU.mjs.map +1 -0
- package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
- package/dist/chunk-BLXGONR2.mjs.map +1 -0
- package/dist/{chunk-KM2NOX2I.mjs → chunk-BQVQOQJH.mjs} +958 -254
- package/dist/chunk-BQVQOQJH.mjs.map +1 -0
- package/dist/{chunk-HKXTW355.mjs → chunk-BTWH7BP4.mjs} +386 -17
- package/dist/chunk-BTWH7BP4.mjs.map +1 -0
- package/dist/{chunk-TMIASV2N.mjs → chunk-FUODTTIA.mjs} +3934 -676
- package/dist/chunk-FUODTTIA.mjs.map +1 -0
- package/dist/{chunk-FJ6P52EE.mjs → chunk-ILQ5MEZF.mjs} +3 -17
- package/dist/chunk-ILQ5MEZF.mjs.map +1 -0
- package/dist/{chunk-E34VL5VI.mjs → chunk-JQTSEI3Z.mjs} +3 -3
- package/dist/{chunk-E34VL5VI.mjs.map → chunk-JQTSEI3Z.mjs.map} +1 -1
- package/dist/{chunk-GE3SDD72.mjs → chunk-NICXSHAT.mjs} +16 -38
- package/dist/chunk-NICXSHAT.mjs.map +1 -0
- package/dist/{chunk-ZZ4YQ474.mjs → chunk-NRNXBLIJ.mjs} +577 -7
- package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
- package/dist/{chunk-JDWARUOI.mjs → chunk-PA5DRJNI.mjs} +17 -3
- package/dist/chunk-PA5DRJNI.mjs.map +1 -0
- package/dist/{chunk-XDP6XE5V.mjs → chunk-S4DNKQYQ.mjs} +1118 -446
- package/dist/chunk-S4DNKQYQ.mjs.map +1 -0
- package/dist/components/camera.d.ts +62 -2
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +6 -20
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/scene-instance.d.ts +2 -1
- package/dist/components/scene-instance.d.ts.map +1 -1
- package/dist/components/screen-space-reflection.d.ts +14 -0
- package/dist/components/screen-space-reflection.d.ts.map +1 -0
- package/dist/construct-renderer.mjs +13805 -7186
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +11 -17
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/device/mesh-residency-lifetime.d.ts +21 -0
- package/dist/device/mesh-residency-lifetime.d.ts.map +1 -0
- package/dist/dynamic-geometry.d.ts +125 -0
- package/dist/dynamic-geometry.d.ts.map +1 -0
- package/dist/environment/background.d.ts +6 -0
- package/dist/environment/background.d.ts.map +1 -0
- package/dist/errors/gpu-driven.d.ts +6 -3
- package/dist/errors/gpu-driven.d.ts.map +1 -1
- package/dist/errors/render.d.ts +7 -4
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +33 -3
- package/dist/extract/gpu-driven.d.ts.map +1 -1
- package/dist/features/host.d.ts +23 -1
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/noise-texture.d.ts +17 -0
- package/dist/features/noise-texture.d.ts.map +1 -0
- package/dist/features/plan.d.ts +83 -2
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +37 -4
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/features/prepared-graphics.d.ts +5 -0
- package/dist/features/prepared-graphics.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts +5 -3
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +11 -3
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +19 -3
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/types.d.ts +11 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +2 -0
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +34 -1
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/pbr-program.d.ts +10 -0
- package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +144 -23
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-views.d.ts +126 -0
- package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +18 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts +2 -8
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +14 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-texture-usage.d.ts +1 -0
- package/dist/gpu-texture-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +6 -0
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts +8 -0
- package/dist/ibl/frame-resources.d.ts.map +1 -0
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +21 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +8 -7
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +164 -0
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances-derived-bounds.d.ts +11 -0
- package/dist/instances-derived-bounds.d.ts.map +1 -1
- package/dist/instances.d.ts +89 -44
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +118 -10
- package/dist/internal.mjs.map +1 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/material-row.d.ts +5 -0
- package/dist/material-row.d.ts.map +1 -0
- package/dist/materials.d.ts +15 -0
- package/dist/materials.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +21 -5
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
- package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
- package/dist/pipeline/standard-output/graph.d.ts +15 -0
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
- package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/resources.d.ts +10 -0
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
- package/dist/pipeline/standard-output/types.d.ts +39 -0
- package/dist/pipeline/standard-output/types.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +2 -2
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +2 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec-types.d.ts +4 -0
- package/dist/pipeline-spec-types.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +1 -9
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/points-lines/standard-owner.d.ts +1 -1
- package/dist/points-lines/standard-owner.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +3 -3
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +106 -5
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +39 -4
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +7 -8
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +38 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +2 -10
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +12 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +15 -6
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts +19 -0
- package/dist/record/prepared-material-bindings.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts +2 -9
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +76 -26
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +62 -7
- 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 +35 -7
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +1 -0
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +10 -2
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/recovery/render-system-roots.d.ts +11 -0
- package/dist/recovery/render-system-roots.d.ts.map +1 -0
- package/dist/recovery/types.d.ts +76 -0
- package/dist/recovery/types.d.ts.map +1 -0
- package/dist/reflection/record-owner.d.ts +0 -2
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +185 -30
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +1 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +64 -5
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +7 -5
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +101 -41
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-projections.d.ts +22 -0
- package/dist/render-system-projections.d.ts.map +1 -0
- package/dist/render-system.d.ts +34 -77
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +14 -7
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +29 -11
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/shadow-visibility.d.ts +5 -0
- package/dist/scene/shadow-visibility.d.ts.map +1 -0
- package/dist/ssr/admission.d.ts +70 -1
- package/dist/ssr/admission.d.ts.map +1 -1
- package/dist/ssr/compose.d.ts +13 -0
- package/dist/ssr/compose.d.ts.map +1 -0
- package/dist/ssr/errors.d.ts +35 -0
- package/dist/ssr/errors.d.ts.map +1 -1
- package/dist/ssr/graph.d.ts +60 -0
- package/dist/ssr/graph.d.ts.map +1 -0
- package/dist/ssr/history.d.ts +97 -0
- package/dist/ssr/history.d.ts.map +1 -0
- package/dist/ssr/hiz.d.ts +37 -0
- package/dist/ssr/hiz.d.ts.map +1 -0
- package/dist/ssr/inspection.d.ts +42 -0
- package/dist/ssr/inspection.d.ts.map +1 -0
- package/dist/ssr/resources.d.ts +58 -0
- package/dist/ssr/resources.d.ts.map +1 -0
- package/dist/ssr/temporal.d.ts +38 -0
- package/dist/ssr/temporal.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +32 -10
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +15 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +9 -0
- package/dist/systems/transparent-dispatch.d.ts.map +1 -0
- package/dist/targets/material-source.d.ts +7 -0
- package/dist/targets/material-source.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts +7 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +8 -0
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/history.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -6
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts +2 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/temporal-view.d.ts +2 -0
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +35 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +3 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +6 -0
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -20
- package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
- package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
- package/src/__tests__/auto-exposure.browser.test.ts +33 -0
- package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
- package/src/__tests__/detached-inspection.integration.test.ts +5 -0
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
- package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
- package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
- package/src/__tests__/dynamic-geometry-host.unit.test.ts +1123 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +233 -0
- package/src/__tests__/factory-contract.integration.test.ts +73 -22
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
- package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
- package/src/__tests__/feature-noise.unit.test.ts +38 -0
- package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
- package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
- package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
- package/src/__tests__/frame-targets.unit.test.ts +24 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +287 -6
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
- package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
- package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +264 -0
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +349 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1417 -0
- package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +1075 -93
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
- package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
- package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
- package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
- package/src/__tests__/gpu-driven-view.unit.test.ts +159 -52
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -1
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
- package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
- package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
- package/src/__tests__/gpu-scene.unit.test.ts +559 -33
- package/src/__tests__/instances-culling.integration.test.ts +36 -10
- package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
- package/src/__tests__/instances-store.unit.test.ts +242 -39
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
- package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
- package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
- package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
- package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
- package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
- package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
- package/src/__tests__/material-texture-source.unit.test.ts +87 -0
- package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +52 -2
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +53 -0
- package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
- package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +37 -3
- package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
- package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
- package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +35 -4
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
- package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
- package/src/__tests__/public-surface.test-d.ts +3 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
- package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +104 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +2 -1
- package/src/__tests__/render-error-code-owner.test-d.ts +4 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +22 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
- package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
- package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
- package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
- package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
- package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +914 -23
- package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
- package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
- package/src/__tests__/render-target-material-source.test-d.ts +27 -3
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +61 -1
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
- package/src/__tests__/shader-manifest-fixture.ts +39 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
- package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
- package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +118 -14
- package/src/__tests__/specular-environment-scale.dawn.test.ts +156 -0
- package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
- package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
- package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
- package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +91 -0
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +112 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +946 -0
- package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
- package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
- package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
- package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
- package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
- package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
- package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
- package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
- package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
- package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
- package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
- package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
- package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
- package/src/__tests__/ssr-reprojection-probe.ts +90 -0
- package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
- package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
- package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
- package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
- package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
- package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +44 -12
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
- package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
- package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
- package/src/__tests__/standard-output.browser.test.ts +15 -0
- package/src/__tests__/standard-output.dawn.test.ts +15 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +38 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
- package/src/__tests__/standard-profile-oracle.unit.test.ts +22 -0
- package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
- package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
- package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
- package/src/__tests__/taa-coverage-edge.dawn.test.ts +193 -0
- package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +778 -0
- package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
- package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
- package/src/__tests__/taa-stability-feedback.ts +341 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
- package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
- package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
- package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
- package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
- package/src/__tests__/visibility-extract.perf.test.ts +20 -8
- package/src/__tests__/visibility-instances.integration.test.ts +19 -7
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
- package/src/assembly/atmosphere-shader-prewarm.ts +38 -0
- package/src/assembly/dynamic-geometry-host.ts +887 -0
- package/src/assembly/dynamic-geometry-runtime.ts +71 -0
- package/src/assembly/factory.ts +4 -0
- package/src/assembly/gpu-driven-owner.ts +29 -0
- package/src/assembly/host-contract.ts +36 -0
- package/src/assembly/material/assembly.ts +38 -0
- package/src/assembly/material-shader-policy.ts +71 -28
- package/src/assembly/recovery/failure-location.ts +62 -0
- package/src/assembly/recovery/renderer-recover.ts +19 -17
- package/src/assembly/renderer-facade.ts +29 -0
- package/src/assembly/renderer-feature-inspection.ts +13 -0
- package/src/assembly/renderer-frame-transaction.ts +63 -0
- package/src/assembly/renderer-helpers.ts +26 -0
- package/src/assembly/renderer-inspection.ts +6 -0
- package/src/assembly/shader-prewarm-policy.ts +32 -0
- package/src/assembly/skin-palette-owner.ts +21 -0
- package/src/assembly/ssr-shader-prewarm.ts +41 -0
- package/src/assembly/webgpu-device-loss.ts +75 -0
- package/src/assembly/webgpu-pbr-ready.ts +232 -0
- package/src/assembly/webgpu-ready-mipmap.ts +26 -0
- package/src/assembly/webgpu-ready.ts +142 -121
- package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
- package/src/assembly/webgpu-renderer-contract.ts +69 -0
- package/src/assembly/webgpu-renderer-guards.ts +32 -0
- package/src/assembly/webgpu-renderer-observation.ts +43 -0
- package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
- package/src/assembly/webgpu-renderer.ts +675 -614
- package/src/assembly/webgpu-vertex-layouts.ts +233 -87
- package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
- package/src/components/camera.ts +287 -7
- package/src/components/index.ts +1 -0
- package/src/components/instances.ts +45 -18
- package/src/components/scene-instance.ts +2 -1
- package/src/components/screen-space-reflection.ts +21 -0
- package/src/components/sprite-animation.ts +3 -3
- package/src/components/sprite-instances.ts +1 -1
- package/src/device/gpu-residency.ts +96 -93
- package/src/device/mesh-residency-lifetime.ts +73 -0
- package/src/dynamic-geometry.ts +736 -0
- package/src/environment/background.ts +263 -0
- package/src/errors/gpu-driven.ts +39 -5
- package/src/errors/render.ts +27 -3
- package/src/extract/camera.ts +26 -1
- package/src/extract/gpu-driven.ts +185 -10
- package/src/features/host.ts +237 -19
- package/src/features/noise-texture.ts +91 -0
- package/src/features/plan.ts +553 -24
- package/src/features/prepared-gpu-work.ts +631 -49
- package/src/features/prepared-graphics-store.ts +31 -0
- package/src/features/prepared-graphics.ts +5 -0
- package/src/features/render-graph-compute.ts +41 -8
- package/src/features/render-graph-contribution.ts +141 -3
- package/src/features/render-graph-raster.ts +143 -30
- package/src/features/types.ts +11 -0
- package/src/fullscreen-post-process-pass.ts +3 -0
- package/src/gpu-driven/batch-topology.ts +424 -102
- package/src/gpu-driven/pbr-program.ts +31 -0
- package/src/gpu-driven/prepared-draw.ts +29 -4
- package/src/gpu-driven/production-raster.ts +2267 -694
- package/src/gpu-driven/shadow-views.ts +539 -0
- package/src/gpu-driven/view-gpu.ts +188 -41
- package/src/gpu-scene-schema.ts +52 -11
- package/src/gpu-scene.ts +186 -99
- package/src/gpu-texture-usage.ts +1 -0
- package/src/hdrp-buffers.ts +72 -36
- package/src/ibl/frame-resources.ts +38 -0
- package/src/ibl/skylight-bind-group.ts +5 -4
- package/src/index.ts +125 -1
- package/src/inspection-types.ts +191 -0
- package/src/instances-derived-bounds.ts +258 -34
- package/src/instances.ts +459 -150
- package/src/internal.ts +22 -0
- package/src/lifecycle.ts +2 -0
- package/src/material-row.ts +145 -0
- package/src/materials.ts +44 -8
- package/src/pbr-pipeline.ts +159 -17
- package/src/pipeline/standard-forward-lane.ts +52 -24
- package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
- package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
- package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
- package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
- package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
- package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
- package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
- package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
- package/src/pipeline/standard-output/color-transform.ts +124 -0
- package/src/pipeline/standard-output/graph.ts +139 -0
- package/src/pipeline/standard-output/lut-admission.ts +121 -0
- package/src/pipeline/standard-output/lut-gpu.ts +399 -0
- package/src/pipeline/standard-output/lut-state.ts +176 -0
- package/src/pipeline/standard-output/resources.ts +121 -0
- package/src/pipeline/standard-output/types.ts +58 -0
- package/src/pipeline/standard-pipeline.ts +192 -59
- package/src/pipeline/standard-post.ts +186 -27
- package/src/pipeline-builder.ts +16 -6
- package/src/pipeline-spec-types.ts +4 -0
- package/src/pipeline-spec.ts +37 -29
- package/src/points-lines/standard-owner.ts +6 -14
- package/src/prepare/prepared-graphics-resolver.ts +9 -2
- package/src/record/__tests__/material-slot-plan.unit.test.ts +48 -0
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +2 -29
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +36 -5
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
- package/src/record/dynamic-geometry-consumption.ts +59 -0
- package/src/record/frame-lighting.ts +12 -11
- package/src/record/frame-snapshot.ts +133 -23
- package/src/record/frame-targets.ts +9 -0
- package/src/record/frame.ts +727 -133
- package/src/record/main-pass-geometry.ts +162 -98
- package/src/record/main-pass-material.ts +115 -26
- package/src/record/main-pass-sprite-draws.ts +18 -107
- package/src/record/main-pass.ts +78 -8
- package/src/record/material-uniforms.ts +1 -1
- package/src/record/mesh-ssbo.ts +59 -15
- package/src/record/prepared-material-bindings.ts +234 -0
- package/src/record/recovery-pipeline.ts +59 -136
- package/src/record/render-context.ts +92 -22
- package/src/record/shadow-pass.ts +1231 -209
- package/src/record/skybox-post-pass.ts +14 -3
- package/src/record/typed-frame-graph.ts +1006 -53
- package/src/record/view-ubo.ts +11 -0
- package/src/recovery/render-system-candidate.ts +90 -66
- package/src/recovery/render-system-roots.ts +113 -0
- package/src/recovery/types.ts +77 -0
- package/src/reflection/record-owner.ts +26 -54
- package/src/render-contract.ts +219 -25
- package/src/render-graph-primitives.ts +6 -2
- package/src/render-pipeline.ts +70 -1
- package/src/render-system-extract-tail.ts +409 -160
- package/src/render-system-extract.ts +376 -102
- package/src/render-system-projections.ts +241 -0
- package/src/render-system.ts +692 -791
- package/src/scene/render-scene-types.ts +13 -4
- package/src/scene/render-scene.ts +569 -355
- package/src/scene/shadow-visibility.ts +21 -0
- package/src/ssr/admission.ts +380 -14
- package/src/ssr/compose.ts +144 -0
- package/src/ssr/errors.ts +48 -0
- package/src/ssr/graph.ts +777 -0
- package/src/ssr/history.ts +436 -0
- package/src/ssr/hiz.ts +124 -0
- package/src/ssr/inspection.ts +124 -0
- package/src/ssr/resources.ts +202 -0
- package/src/ssr/temporal.ts +164 -0
- package/src/systems/skin-palette-allocator.ts +495 -148
- package/src/systems/skin-palette-types.ts +17 -0
- package/src/systems/transparent-dispatch.ts +90 -0
- package/src/targets/material-source.ts +34 -0
- package/src/temporal/coverage.ts +1 -1
- package/src/temporal/frame-coordinator.ts +23 -2
- package/src/temporal/gpu.ts +74 -12
- package/src/temporal/history.ts +3 -1
- package/src/temporal/inspection.ts +2 -1
- package/src/temporal/standard-scene-data.ts +3 -1
- package/src/temporal/temporal-view.ts +3 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +204 -139
- package/src/typed-render-graph-primitives.ts +266 -74
- package/src/typed-shadow-passes.ts +122 -12
- package/src/volume/temporal.ts +10 -0
- package/dist/chunk-ADHHVYLW.mjs.map +0 -1
- package/dist/chunk-FJ6P52EE.mjs.map +0 -1
- package/dist/chunk-GE3SDD72.mjs.map +0 -1
- package/dist/chunk-GF523LHF.mjs.map +0 -1
- package/dist/chunk-HKXTW355.mjs.map +0 -1
- package/dist/chunk-JDWARUOI.mjs.map +0 -1
- package/dist/chunk-KM2NOX2I.mjs.map +0 -1
- package/dist/chunk-TMIASV2N.mjs.map +0 -1
- package/dist/chunk-XDP6XE5V.mjs.map +0 -1
- package/dist/chunk-ZZ4YQ474.mjs.map +0 -1
- package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
- package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
import type { Handle, MeshAsset } from '@forgeax/engine-types';
|
|
2
|
+
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
3
|
+
|
|
4
|
+
/** A standard MeshAsset candidate prepared for one attached World generation. */
|
|
5
|
+
export interface DynamicGeometryPrepareInput {
|
|
6
|
+
readonly world: object;
|
|
7
|
+
/** Live ECS entity carrying MeshFilter + MeshRenderer on the attached World. */
|
|
8
|
+
readonly entity?: number;
|
|
9
|
+
/** Optional derived-physics entity whose fixed-step publication gates draw. */
|
|
10
|
+
readonly physicsEntity?: number;
|
|
11
|
+
readonly mesh: MeshAsset;
|
|
12
|
+
/** Existing World.sharedRefs handle whose standard GPU residency is prepared. */
|
|
13
|
+
readonly meshHandle?: Handle<'MeshAsset', 'shared'>;
|
|
14
|
+
readonly revision: number;
|
|
15
|
+
/** Material identity is a consumer fact; MaterialAsset stays in the World. */
|
|
16
|
+
readonly materialIdentity?: string;
|
|
17
|
+
/** Topology changes invalidate motion/history consumers. */
|
|
18
|
+
readonly topologyRevision?: number;
|
|
19
|
+
/** Captured by the Renderer host from the World FixedTime resource. */
|
|
20
|
+
readonly fixedStep?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Explicit ECS/fixed-step ordering proof used by Renderer admission. */
|
|
24
|
+
export interface DynamicGeometryOrdering {
|
|
25
|
+
readonly world: object;
|
|
26
|
+
readonly fixedStep: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type DynamicGeometryCandidateState =
|
|
30
|
+
| 'prepared'
|
|
31
|
+
| 'accepted'
|
|
32
|
+
| 'published'
|
|
33
|
+
| 'cancelled'
|
|
34
|
+
| 'retired';
|
|
35
|
+
|
|
36
|
+
export interface DynamicGeometryCandidate {
|
|
37
|
+
readonly candidateId: string;
|
|
38
|
+
readonly generation: number;
|
|
39
|
+
readonly owner: object;
|
|
40
|
+
readonly world: object;
|
|
41
|
+
readonly entity?: number;
|
|
42
|
+
readonly physicsEntity?: number;
|
|
43
|
+
/** False when the pure lifecycle was used without a shared MeshAsset handle. */
|
|
44
|
+
readonly gpuReady: boolean;
|
|
45
|
+
/** CPU/GPU geometry bytes reserved by this candidate budget. */
|
|
46
|
+
readonly meshBytes: number;
|
|
47
|
+
readonly revision: number;
|
|
48
|
+
readonly topologyRevision: number;
|
|
49
|
+
readonly materialIdentity?: string;
|
|
50
|
+
readonly meshHandle?: Handle<'MeshAsset', 'shared'>;
|
|
51
|
+
readonly fixedStep?: number;
|
|
52
|
+
readonly mesh: MeshAsset;
|
|
53
|
+
readonly state: DynamicGeometryCandidateState;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Receipt that ties candidate publication to the sole Renderer FrameReceipt. */
|
|
57
|
+
export interface DynamicGeometryReceipt {
|
|
58
|
+
readonly candidateId: string;
|
|
59
|
+
readonly generation: number;
|
|
60
|
+
readonly revision: number;
|
|
61
|
+
readonly topologyRevision: number;
|
|
62
|
+
readonly fixedStep?: number;
|
|
63
|
+
readonly frameId: number;
|
|
64
|
+
readonly deviceGeneration: number;
|
|
65
|
+
readonly frame: {
|
|
66
|
+
readonly frameId: number;
|
|
67
|
+
readonly deviceGeneration: number;
|
|
68
|
+
readonly completed?: Promise<unknown>;
|
|
69
|
+
};
|
|
70
|
+
/** Whether the Renderer record stage consumed this candidate for this frame. */
|
|
71
|
+
readonly recordStageConsumed: boolean;
|
|
72
|
+
/** Actual frame fixed step; may be newer than the preparation step. */
|
|
73
|
+
readonly publicationFixedStep?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface DynamicGeometryInspection {
|
|
77
|
+
readonly generation: number;
|
|
78
|
+
readonly prepared: number;
|
|
79
|
+
readonly accepted: number;
|
|
80
|
+
readonly published: number;
|
|
81
|
+
readonly retired: number;
|
|
82
|
+
readonly latestRevision?: number;
|
|
83
|
+
readonly latestFrameId?: number;
|
|
84
|
+
readonly historyInvalidations: number;
|
|
85
|
+
readonly invalidated: number;
|
|
86
|
+
readonly meshBytes: number;
|
|
87
|
+
readonly maxMeshBytes: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type DynamicGeometryErrorCode =
|
|
91
|
+
| 'dynamic-geometry-disposed'
|
|
92
|
+
| 'dynamic-geometry-world-not-attached'
|
|
93
|
+
| 'dynamic-geometry-invalid'
|
|
94
|
+
| 'dynamic-geometry-gpu-failed'
|
|
95
|
+
| 'dynamic-geometry-gpu-not-ready'
|
|
96
|
+
| 'dynamic-geometry-budget-exceeded'
|
|
97
|
+
| 'dynamic-geometry-stale'
|
|
98
|
+
| 'dynamic-geometry-generation-mismatch'
|
|
99
|
+
| 'dynamic-geometry-candidate-not-found'
|
|
100
|
+
| 'dynamic-geometry-candidate-state'
|
|
101
|
+
| 'dynamic-geometry-receipt-mismatch'
|
|
102
|
+
| 'dynamic-geometry-ordering-required';
|
|
103
|
+
|
|
104
|
+
export interface DynamicGeometryErrorDetail {
|
|
105
|
+
readonly code: DynamicGeometryErrorCode;
|
|
106
|
+
readonly candidateId?: string;
|
|
107
|
+
readonly revision?: number;
|
|
108
|
+
readonly generation?: number;
|
|
109
|
+
readonly actual?: unknown;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export class DynamicGeometryError extends Error {
|
|
113
|
+
readonly code: DynamicGeometryErrorCode;
|
|
114
|
+
readonly expected: string;
|
|
115
|
+
readonly hint: string;
|
|
116
|
+
readonly detail: DynamicGeometryErrorDetail;
|
|
117
|
+
|
|
118
|
+
constructor(
|
|
119
|
+
code: DynamicGeometryErrorCode,
|
|
120
|
+
expected: string,
|
|
121
|
+
hint: string,
|
|
122
|
+
detail: Omit<DynamicGeometryErrorDetail, 'code'> = {},
|
|
123
|
+
) {
|
|
124
|
+
super(`${code}: ${expected}`);
|
|
125
|
+
this.name = 'DynamicGeometryError';
|
|
126
|
+
this.code = code;
|
|
127
|
+
this.expected = expected;
|
|
128
|
+
this.hint = hint;
|
|
129
|
+
this.detail = Object.freeze({ code, ...detail });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
interface CandidateRecord {
|
|
134
|
+
readonly world: object;
|
|
135
|
+
candidate: DynamicGeometryCandidate;
|
|
136
|
+
/** Published history remains queryable for its completion fence, but a
|
|
137
|
+
* newer topology must prevent that history from being consumed again. */
|
|
138
|
+
historyInvalidated: boolean;
|
|
139
|
+
acceptedFrame?: { readonly frameId: number; readonly deviceGeneration: number };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* A public candidate is a credential, not a mutable state object. Keep the
|
|
144
|
+
* owner/world check in `current` cheap, then compare every scalar admission
|
|
145
|
+
* fact before a caller can cancel, retire, or inspect another record by
|
|
146
|
+
* copying its candidate id. The renderer-owned MeshAsset copy remains the
|
|
147
|
+
* content source for the actual draw.
|
|
148
|
+
*/
|
|
149
|
+
export function sameCandidateCredential(
|
|
150
|
+
candidate: DynamicGeometryCandidate,
|
|
151
|
+
committed: DynamicGeometryCandidate,
|
|
152
|
+
): boolean {
|
|
153
|
+
return (
|
|
154
|
+
candidate.candidateId === committed.candidateId &&
|
|
155
|
+
candidate.owner === committed.owner &&
|
|
156
|
+
candidate.world === committed.world &&
|
|
157
|
+
candidate.generation === committed.generation &&
|
|
158
|
+
candidate.entity === committed.entity &&
|
|
159
|
+
candidate.physicsEntity === committed.physicsEntity &&
|
|
160
|
+
candidate.gpuReady === committed.gpuReady &&
|
|
161
|
+
candidate.meshBytes === committed.meshBytes &&
|
|
162
|
+
candidate.revision === committed.revision &&
|
|
163
|
+
candidate.topologyRevision === committed.topologyRevision &&
|
|
164
|
+
candidate.materialIdentity === committed.materialIdentity &&
|
|
165
|
+
candidate.meshHandle === committed.meshHandle &&
|
|
166
|
+
candidate.fixedStep === committed.fixedStep
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function cloneTypedArray<T extends Float32Array | Uint16Array | Uint32Array>(value: T): T {
|
|
171
|
+
return new (value.constructor as new (source: T) => T)(value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function cloneMesh(mesh: MeshAsset): MeshAsset {
|
|
175
|
+
const attributes = Object.fromEntries(
|
|
176
|
+
Object.entries(mesh.attributes).map(([key, value]) => [
|
|
177
|
+
key,
|
|
178
|
+
value instanceof ArrayBuffer
|
|
179
|
+
? value.slice(0)
|
|
180
|
+
: value === undefined
|
|
181
|
+
? undefined
|
|
182
|
+
: cloneTypedArray(value as never),
|
|
183
|
+
]),
|
|
184
|
+
) as MeshAsset['attributes'];
|
|
185
|
+
return Object.freeze({
|
|
186
|
+
...mesh,
|
|
187
|
+
vertices: new Float32Array(mesh.vertices),
|
|
188
|
+
...(mesh.indices === undefined ? {} : { indices: cloneTypedArray(mesh.indices) }),
|
|
189
|
+
attributes,
|
|
190
|
+
...(mesh.aabb === undefined ? {} : { aabb: new Float32Array(mesh.aabb) }),
|
|
191
|
+
submeshes: Object.freeze(mesh.submeshes.map((submesh) => Object.freeze({ ...submesh }))),
|
|
192
|
+
materialSlots: Object.freeze(mesh.materialSlots.map((slot) => Object.freeze({ ...slot }))),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function cloneCandidate(candidate: DynamicGeometryCandidate): DynamicGeometryCandidate {
|
|
197
|
+
return Object.freeze({ ...candidate, mesh: cloneMesh(candidate.mesh) });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function validateMesh(mesh: MeshAsset): string | undefined {
|
|
201
|
+
if (mesh.kind !== 'mesh') return 'MeshAsset.kind must be mesh';
|
|
202
|
+
if (
|
|
203
|
+
!(mesh.vertices instanceof Float32Array) ||
|
|
204
|
+
mesh.vertices.length === 0 ||
|
|
205
|
+
mesh.vertices.length % 3 !== 0
|
|
206
|
+
) {
|
|
207
|
+
return 'MeshAsset.vertices must be a non-empty Float32Array with xyz triples';
|
|
208
|
+
}
|
|
209
|
+
if (
|
|
210
|
+
mesh.indices !== undefined &&
|
|
211
|
+
!(mesh.indices instanceof Uint16Array || mesh.indices instanceof Uint32Array)
|
|
212
|
+
) {
|
|
213
|
+
return 'MeshAsset.indices must be Uint16Array or Uint32Array';
|
|
214
|
+
}
|
|
215
|
+
if (mesh.submeshes.length === 0)
|
|
216
|
+
return 'MeshAsset.submeshes must contain at least one topology range';
|
|
217
|
+
if (mesh.materialSlots.length < mesh.submeshes.length) {
|
|
218
|
+
return 'MeshAsset.materialSlots must cover every submesh';
|
|
219
|
+
}
|
|
220
|
+
if (mesh.vertices.some((value) => !Number.isFinite(value)))
|
|
221
|
+
return 'MeshAsset.vertices must be finite';
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function meshByteSize(mesh: MeshAsset): number {
|
|
226
|
+
let bytes = mesh.vertices.byteLength + (mesh.indices?.byteLength ?? 0);
|
|
227
|
+
for (const value of Object.values(mesh.attributes)) {
|
|
228
|
+
if (value instanceof ArrayBuffer) bytes += value.byteLength;
|
|
229
|
+
else if (value !== undefined && ArrayBuffer.isView(value)) bytes += value.byteLength;
|
|
230
|
+
}
|
|
231
|
+
if (mesh.aabb !== undefined) bytes += mesh.aabb.byteLength;
|
|
232
|
+
// Submesh/material metadata is intentionally bounded by the candidate
|
|
233
|
+
// count. The reserved byte budget is for typed geometry and GPU upload
|
|
234
|
+
// inputs, which are the resources that can exhaust the renderer.
|
|
235
|
+
return bytes;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Renderer-owned lifecycle for standard MeshAsset candidates. It deliberately
|
|
240
|
+
* has no device, buffer, queue, or frame clock; the host supplies generation
|
|
241
|
+
* and FrameReceipt facts at the boundaries below.
|
|
242
|
+
*/
|
|
243
|
+
export interface DynamicGeometryLifecycle {
|
|
244
|
+
prepare(
|
|
245
|
+
input: DynamicGeometryPrepareInput,
|
|
246
|
+
generation: number,
|
|
247
|
+
): Result<DynamicGeometryCandidate, DynamicGeometryError>;
|
|
248
|
+
accept(
|
|
249
|
+
candidate: DynamicGeometryCandidate,
|
|
250
|
+
ordering?: DynamicGeometryOrdering,
|
|
251
|
+
): Result<DynamicGeometryCandidate, DynamicGeometryError>;
|
|
252
|
+
cancel(
|
|
253
|
+
candidate: DynamicGeometryCandidate,
|
|
254
|
+
completion?: Promise<unknown>,
|
|
255
|
+
): Result<void, DynamicGeometryError>;
|
|
256
|
+
retire(candidate: DynamicGeometryCandidate): Result<void, DynamicGeometryError>;
|
|
257
|
+
/** Release the count/byte budget after receipt-bound GPU cleanup completes. */
|
|
258
|
+
finalizeRetirement(candidate: DynamicGeometryCandidate): Result<void, DynamicGeometryError>;
|
|
259
|
+
receipt(candidate: DynamicGeometryCandidate): DynamicGeometryReceipt | undefined;
|
|
260
|
+
publishFrame(
|
|
261
|
+
frame: {
|
|
262
|
+
readonly frameId: number;
|
|
263
|
+
readonly deviceGeneration: number;
|
|
264
|
+
readonly completed?: Promise<unknown>;
|
|
265
|
+
},
|
|
266
|
+
worlds?: readonly object[],
|
|
267
|
+
fixedStep?: number,
|
|
268
|
+
canPublish?: (candidate: DynamicGeometryCandidate) => boolean,
|
|
269
|
+
): readonly DynamicGeometryReceipt[];
|
|
270
|
+
invalidateWorld(world: object, completion?: Promise<unknown>): void;
|
|
271
|
+
invalidateGeneration(generation: number): void;
|
|
272
|
+
inspect(): DynamicGeometryInspection;
|
|
273
|
+
dispose(): void;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function createDynamicGeometryLifecycle(
|
|
277
|
+
maxCandidates = 32,
|
|
278
|
+
maxMeshBytes = 64 * 1024 * 1024,
|
|
279
|
+
): DynamicGeometryLifecycle {
|
|
280
|
+
const records = new Map<string, CandidateRecord>();
|
|
281
|
+
const owner = {};
|
|
282
|
+
let sequence = 0;
|
|
283
|
+
let currentGeneration = 0;
|
|
284
|
+
let disposed = false;
|
|
285
|
+
let historyInvalidations = 0;
|
|
286
|
+
let latestRevision: number | undefined;
|
|
287
|
+
let latestFrameId: number | undefined;
|
|
288
|
+
let retired = 0;
|
|
289
|
+
let invalidated = 0;
|
|
290
|
+
let meshBytes = 0;
|
|
291
|
+
const latestRevisionByWorld = new WeakMap<object, Map<number | undefined, number>>();
|
|
292
|
+
const latestTopologyRevisionByWorld = new WeakMap<object, Map<number | undefined, number>>();
|
|
293
|
+
const publishedReceipts = new Map<string, DynamicGeometryReceipt>();
|
|
294
|
+
|
|
295
|
+
const failure = (
|
|
296
|
+
code: DynamicGeometryErrorCode,
|
|
297
|
+
expected: string,
|
|
298
|
+
hint: string,
|
|
299
|
+
detail: Omit<DynamicGeometryErrorDetail, 'code'> = {},
|
|
300
|
+
): Result<never, DynamicGeometryError> =>
|
|
301
|
+
err(new DynamicGeometryError(code, expected, hint, detail));
|
|
302
|
+
|
|
303
|
+
const current = (candidate: DynamicGeometryCandidate): CandidateRecord | undefined => {
|
|
304
|
+
const record = records.get(candidate.candidateId);
|
|
305
|
+
if (
|
|
306
|
+
record === undefined ||
|
|
307
|
+
candidate.owner !== owner ||
|
|
308
|
+
record.candidate.owner !== candidate.owner ||
|
|
309
|
+
record.candidate.world !== candidate.world ||
|
|
310
|
+
record.candidate.generation !== candidate.generation
|
|
311
|
+
)
|
|
312
|
+
return undefined;
|
|
313
|
+
return record;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
prepare(input, generation) {
|
|
318
|
+
if (disposed)
|
|
319
|
+
return failure('dynamic-geometry-disposed', 'renderer is alive', 'rebuild the Renderer');
|
|
320
|
+
if (input.world === null || typeof input.world !== 'object') {
|
|
321
|
+
return failure(
|
|
322
|
+
'dynamic-geometry-world-not-attached',
|
|
323
|
+
'candidate belongs to an attached World',
|
|
324
|
+
'attach the World before preparing geometry',
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
if (!Number.isInteger(input.revision) || input.revision < 0) {
|
|
328
|
+
return failure(
|
|
329
|
+
'dynamic-geometry-invalid',
|
|
330
|
+
'geometry revision is a non-negative integer',
|
|
331
|
+
'advance the consumer revision monotonically',
|
|
332
|
+
{ revision: input.revision },
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
if (generation !== currentGeneration && currentGeneration !== 0) {
|
|
336
|
+
return failure(
|
|
337
|
+
'dynamic-geometry-generation-mismatch',
|
|
338
|
+
'candidate uses the active renderer generation',
|
|
339
|
+
'discard stale generation work and prepare again',
|
|
340
|
+
{ generation, actual: currentGeneration },
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
if (records.size >= maxCandidates) {
|
|
344
|
+
return failure(
|
|
345
|
+
'dynamic-geometry-budget-exceeded',
|
|
346
|
+
'candidate count remains within the bounded renderer budget',
|
|
347
|
+
'cancel or retire an earlier candidate before retrying',
|
|
348
|
+
{ actual: records.size },
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
const invalid = validateMesh(input.mesh);
|
|
352
|
+
if (invalid !== undefined)
|
|
353
|
+
return failure(
|
|
354
|
+
'dynamic-geometry-invalid',
|
|
355
|
+
'MeshAsset satisfies the standard geometry contract',
|
|
356
|
+
invalid,
|
|
357
|
+
);
|
|
358
|
+
const candidateBytes = meshByteSize(input.mesh);
|
|
359
|
+
if (
|
|
360
|
+
!Number.isSafeInteger(candidateBytes) ||
|
|
361
|
+
candidateBytes < 0 ||
|
|
362
|
+
meshBytes > maxMeshBytes - candidateBytes
|
|
363
|
+
) {
|
|
364
|
+
return failure(
|
|
365
|
+
'dynamic-geometry-budget-exceeded',
|
|
366
|
+
'candidate mesh bytes remain within the bounded renderer resource budget',
|
|
367
|
+
'cancel or retire an earlier candidate before retrying',
|
|
368
|
+
{ actual: { meshBytes, candidateBytes, maxMeshBytes } },
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
const candidateId = `mesh:${generation}:${++sequence}:${input.revision}`;
|
|
372
|
+
const candidate: DynamicGeometryCandidate = Object.freeze({
|
|
373
|
+
candidateId,
|
|
374
|
+
generation,
|
|
375
|
+
owner,
|
|
376
|
+
world: input.world,
|
|
377
|
+
...(input.entity === undefined ? {} : { entity: input.entity }),
|
|
378
|
+
...(input.physicsEntity === undefined ? {} : { physicsEntity: input.physicsEntity }),
|
|
379
|
+
gpuReady: input.meshHandle !== undefined,
|
|
380
|
+
meshBytes: candidateBytes,
|
|
381
|
+
revision: input.revision,
|
|
382
|
+
topologyRevision: input.topologyRevision ?? input.revision,
|
|
383
|
+
...(input.materialIdentity === undefined
|
|
384
|
+
? {}
|
|
385
|
+
: { materialIdentity: input.materialIdentity }),
|
|
386
|
+
...(input.meshHandle === undefined ? {} : { meshHandle: input.meshHandle }),
|
|
387
|
+
...(input.fixedStep === undefined ? {} : { fixedStep: input.fixedStep }),
|
|
388
|
+
mesh: cloneMesh(input.mesh),
|
|
389
|
+
state: 'prepared',
|
|
390
|
+
});
|
|
391
|
+
records.set(candidateId, {
|
|
392
|
+
world: input.world,
|
|
393
|
+
candidate,
|
|
394
|
+
historyInvalidated: false,
|
|
395
|
+
});
|
|
396
|
+
meshBytes += candidateBytes;
|
|
397
|
+
currentGeneration = generation;
|
|
398
|
+
// Keep the credential handed to the consumer separate from the
|
|
399
|
+
// renderer-owned copy. Mesh typed arrays remain mutable at runtime even
|
|
400
|
+
// when exposed through a readonly TypeScript field.
|
|
401
|
+
return ok(cloneCandidate(candidate));
|
|
402
|
+
},
|
|
403
|
+
accept(candidate, ordering) {
|
|
404
|
+
if (disposed)
|
|
405
|
+
return failure('dynamic-geometry-disposed', 'renderer is alive', 'rebuild the Renderer');
|
|
406
|
+
const record = current(candidate);
|
|
407
|
+
if (record === undefined)
|
|
408
|
+
return failure(
|
|
409
|
+
'dynamic-geometry-candidate-not-found',
|
|
410
|
+
'candidate is owned by this renderer',
|
|
411
|
+
'discard the stale credential',
|
|
412
|
+
);
|
|
413
|
+
if (candidate.generation !== currentGeneration) {
|
|
414
|
+
return failure(
|
|
415
|
+
'dynamic-geometry-generation-mismatch',
|
|
416
|
+
'candidate generation matches the active renderer generation',
|
|
417
|
+
'prepare against the current renderer generation',
|
|
418
|
+
{ candidateId: candidate.candidateId },
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
if (record.candidate.state !== 'prepared') {
|
|
422
|
+
return failure(
|
|
423
|
+
'dynamic-geometry-candidate-state',
|
|
424
|
+
'candidate is prepared exactly once before acceptance',
|
|
425
|
+
'retain the accepted or published receipt',
|
|
426
|
+
{ candidateId: candidate.candidateId },
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
if (!sameCandidateCredential(candidate, record.candidate)) {
|
|
430
|
+
return failure(
|
|
431
|
+
'dynamic-geometry-receipt-mismatch',
|
|
432
|
+
'acceptance uses the exact prepared candidate credential',
|
|
433
|
+
'discard the altered credential and use the value returned by prepare',
|
|
434
|
+
{ candidateId: candidate.candidateId },
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
if (ordering !== undefined) {
|
|
438
|
+
if (
|
|
439
|
+
ordering.world !== record.world ||
|
|
440
|
+
!Number.isInteger(ordering.fixedStep) ||
|
|
441
|
+
ordering.fixedStep < 0
|
|
442
|
+
) {
|
|
443
|
+
return failure(
|
|
444
|
+
'dynamic-geometry-ordering-required',
|
|
445
|
+
'acceptance ordering names the candidate World and a non-negative fixed step',
|
|
446
|
+
'submit the PhysicsWorld publication ordering for the same attached World',
|
|
447
|
+
{ candidateId: candidate.candidateId, actual: ordering },
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const newest = [...records.values()]
|
|
452
|
+
.filter(
|
|
453
|
+
(entry) =>
|
|
454
|
+
entry.candidate.candidateId !== candidate.candidateId &&
|
|
455
|
+
entry.world === record.world &&
|
|
456
|
+
entry.candidate.entity === record.candidate.entity &&
|
|
457
|
+
(entry.candidate.state === 'prepared' || entry.candidate.state === 'accepted'),
|
|
458
|
+
)
|
|
459
|
+
.map((entry) => entry.candidate.revision)
|
|
460
|
+
.concat(latestRevisionByWorld.get(record.world)?.get(record.candidate.entity) ?? -1)
|
|
461
|
+
.sort((a, b) => b - a)[0];
|
|
462
|
+
if (newest !== undefined && record.candidate.revision <= newest) {
|
|
463
|
+
return failure(
|
|
464
|
+
'dynamic-geometry-stale',
|
|
465
|
+
'accepted geometry revision is newer than the active candidate',
|
|
466
|
+
'advance topology revision before accepting',
|
|
467
|
+
{ candidateId: candidate.candidateId },
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
const accepted = Object.freeze({
|
|
471
|
+
...record.candidate,
|
|
472
|
+
...(ordering === undefined ? {} : { fixedStep: ordering.fixedStep }),
|
|
473
|
+
state: 'accepted' as const,
|
|
474
|
+
});
|
|
475
|
+
const previousTopologies = [...records.values()]
|
|
476
|
+
.filter(
|
|
477
|
+
(entry) =>
|
|
478
|
+
entry.candidate.candidateId !== candidate.candidateId &&
|
|
479
|
+
entry.world === record.world &&
|
|
480
|
+
entry.candidate.entity === record.candidate.entity,
|
|
481
|
+
)
|
|
482
|
+
.map((entry) => entry.candidate.topologyRevision);
|
|
483
|
+
const previousLatestTopology = latestTopologyRevisionByWorld
|
|
484
|
+
.get(record.world)
|
|
485
|
+
?.get(record.candidate.entity);
|
|
486
|
+
const newestTopology = previousTopologies
|
|
487
|
+
.concat(previousLatestTopology === undefined ? [] : [previousLatestTopology])
|
|
488
|
+
.sort((a, b) => b - a)[0];
|
|
489
|
+
if (newestTopology !== undefined && accepted.topologyRevision < newestTopology) {
|
|
490
|
+
return failure(
|
|
491
|
+
'dynamic-geometry-stale',
|
|
492
|
+
'accepted topology revision is not older than the active geometry history',
|
|
493
|
+
'advance topology revision before accepting a replacement candidate',
|
|
494
|
+
{ candidateId: candidate.candidateId },
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
if (newestTopology !== undefined && accepted.topologyRevision > newestTopology) {
|
|
498
|
+
historyInvalidations += 1;
|
|
499
|
+
// Invalidate publication eligibility, not resource ownership. The host
|
|
500
|
+
// cancels superseded candidates with their allocation completion fence.
|
|
501
|
+
for (const previous of records.values()) {
|
|
502
|
+
if (
|
|
503
|
+
previous.world !== record.world ||
|
|
504
|
+
previous.candidate.entity !== record.candidate.entity ||
|
|
505
|
+
previous.candidate.candidateId === candidate.candidateId ||
|
|
506
|
+
previous.candidate.topologyRevision >= accepted.topologyRevision
|
|
507
|
+
)
|
|
508
|
+
continue;
|
|
509
|
+
if (!previous.historyInvalidated) {
|
|
510
|
+
previous.historyInvalidated = true;
|
|
511
|
+
invalidated += 1;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
record.candidate = accepted;
|
|
516
|
+
return ok(cloneCandidate(accepted));
|
|
517
|
+
},
|
|
518
|
+
cancel(candidate, completion) {
|
|
519
|
+
const record = current(candidate);
|
|
520
|
+
if (record === undefined)
|
|
521
|
+
return failure(
|
|
522
|
+
'dynamic-geometry-candidate-not-found',
|
|
523
|
+
'candidate is owned by this renderer',
|
|
524
|
+
'discard the stale credential',
|
|
525
|
+
);
|
|
526
|
+
if (!sameCandidateCredential(candidate, record.candidate))
|
|
527
|
+
return failure(
|
|
528
|
+
'dynamic-geometry-receipt-mismatch',
|
|
529
|
+
'cancellation uses the exact prepared candidate credential',
|
|
530
|
+
'discard the altered credential and use the value returned by prepare',
|
|
531
|
+
{ candidateId: candidate.candidateId },
|
|
532
|
+
);
|
|
533
|
+
if (record.candidate.state !== 'prepared' && record.candidate.state !== 'accepted') {
|
|
534
|
+
return failure(
|
|
535
|
+
'dynamic-geometry-candidate-state',
|
|
536
|
+
'only prepared or accepted geometry can be cancelled once',
|
|
537
|
+
'retire published geometry or await the existing terminal cleanup',
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
record.candidate = Object.freeze({ ...record.candidate, state: 'cancelled' as const });
|
|
541
|
+
const release = (): void => {
|
|
542
|
+
if (records.get(candidate.candidateId) !== record) return;
|
|
543
|
+
records.delete(candidate.candidateId);
|
|
544
|
+
meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
|
|
545
|
+
};
|
|
546
|
+
if (completion === undefined) release();
|
|
547
|
+
else void completion.then(release, release);
|
|
548
|
+
return ok(undefined);
|
|
549
|
+
},
|
|
550
|
+
retire(candidate) {
|
|
551
|
+
const record = current(candidate);
|
|
552
|
+
if (record === undefined)
|
|
553
|
+
return failure(
|
|
554
|
+
'dynamic-geometry-candidate-not-found',
|
|
555
|
+
'retirement uses a published candidate owned by this renderer',
|
|
556
|
+
'discard the stale credential and do not evict any unrelated mesh',
|
|
557
|
+
{ candidateId: candidate.candidateId },
|
|
558
|
+
);
|
|
559
|
+
if (!sameCandidateCredential(candidate, record.candidate))
|
|
560
|
+
return failure(
|
|
561
|
+
'dynamic-geometry-receipt-mismatch',
|
|
562
|
+
'retirement uses the exact published candidate credential',
|
|
563
|
+
'discard the altered credential and use the value returned by publish',
|
|
564
|
+
{ candidateId: candidate.candidateId },
|
|
565
|
+
);
|
|
566
|
+
if (record.candidate.state !== 'published') {
|
|
567
|
+
return failure(
|
|
568
|
+
'dynamic-geometry-candidate-state',
|
|
569
|
+
'only published geometry can enter receipt-bound retirement',
|
|
570
|
+
'publish the candidate from a submitted frame first',
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
record.candidate = Object.freeze({ ...record.candidate, state: 'retired' as const });
|
|
574
|
+
publishedReceipts.delete(candidate.candidateId);
|
|
575
|
+
retired += 1;
|
|
576
|
+
// Retiring is the logical state transition. Keep the candidate record
|
|
577
|
+
// and its typed bytes until the owner calls `finalizeRetirement` after
|
|
578
|
+
// every record-stage completion fence and GPU lease are gone. This is
|
|
579
|
+
// what makes the declared count/byte budget include in-flight work.
|
|
580
|
+
return ok(undefined);
|
|
581
|
+
},
|
|
582
|
+
finalizeRetirement(candidate) {
|
|
583
|
+
const record = current(candidate);
|
|
584
|
+
if (record === undefined)
|
|
585
|
+
return failure(
|
|
586
|
+
'dynamic-geometry-candidate-not-found',
|
|
587
|
+
'retirement finalization uses a candidate retained by this lifecycle',
|
|
588
|
+
'discard the stale credential or finish the current receipt-bound retirement',
|
|
589
|
+
);
|
|
590
|
+
if (!sameCandidateCredential(candidate, record.candidate))
|
|
591
|
+
return failure(
|
|
592
|
+
'dynamic-geometry-receipt-mismatch',
|
|
593
|
+
'retirement finalization uses the exact published candidate credential',
|
|
594
|
+
'discard the altered credential and use the value returned by publication',
|
|
595
|
+
{ candidateId: candidate.candidateId },
|
|
596
|
+
);
|
|
597
|
+
if (record.candidate.state !== 'retired')
|
|
598
|
+
return failure(
|
|
599
|
+
'dynamic-geometry-candidate-state',
|
|
600
|
+
'only a retired candidate can release its deferred budget',
|
|
601
|
+
'retire the published candidate before finalizing its receipt-bound cleanup',
|
|
602
|
+
{ candidateId: candidate.candidateId },
|
|
603
|
+
);
|
|
604
|
+
records.delete(candidate.candidateId);
|
|
605
|
+
meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
|
|
606
|
+
return ok(undefined);
|
|
607
|
+
},
|
|
608
|
+
publishFrame(frame, worlds, fixedStep, canPublish) {
|
|
609
|
+
if (disposed || frame.deviceGeneration !== currentGeneration) return [];
|
|
610
|
+
const receipts: DynamicGeometryReceipt[] = [];
|
|
611
|
+
for (const record of records.values()) {
|
|
612
|
+
const canRepeatPublished =
|
|
613
|
+
record.candidate.state === 'published' &&
|
|
614
|
+
// Attached Renderer publication revalidates the entity binding in
|
|
615
|
+
// `canPublish`, so a history-invalidated candidate can still record
|
|
616
|
+
// a real draw for its live entity. Detached lifecycle publication
|
|
617
|
+
// remains one-shot once that history is invalidated.
|
|
618
|
+
(canPublish !== undefined || !record.historyInvalidated);
|
|
619
|
+
if (record.candidate.state !== 'accepted' && !canRepeatPublished) continue;
|
|
620
|
+
if (worlds !== undefined && !worlds.includes(record.world)) continue;
|
|
621
|
+
if (
|
|
622
|
+
record.candidate.fixedStep !== undefined &&
|
|
623
|
+
(fixedStep === undefined || record.candidate.fixedStep > fixedStep)
|
|
624
|
+
) {
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (canPublish !== undefined && !canPublish(record.candidate)) continue;
|
|
628
|
+
record.candidate = Object.freeze({ ...record.candidate, state: 'published' as const });
|
|
629
|
+
const receipt = Object.freeze({
|
|
630
|
+
candidateId: record.candidate.candidateId,
|
|
631
|
+
generation: record.candidate.generation,
|
|
632
|
+
revision: record.candidate.revision,
|
|
633
|
+
topologyRevision: record.candidate.topologyRevision,
|
|
634
|
+
...(record.candidate.fixedStep === undefined
|
|
635
|
+
? {}
|
|
636
|
+
: { fixedStep: record.candidate.fixedStep }),
|
|
637
|
+
frameId: frame.frameId,
|
|
638
|
+
deviceGeneration: frame.deviceGeneration,
|
|
639
|
+
recordStageConsumed: canPublish !== undefined,
|
|
640
|
+
...(fixedStep === undefined ? {} : { publicationFixedStep: fixedStep }),
|
|
641
|
+
frame: Object.freeze({ ...frame }),
|
|
642
|
+
});
|
|
643
|
+
receipts.push(receipt);
|
|
644
|
+
publishedReceipts.set(record.candidate.candidateId, receipt);
|
|
645
|
+
latestRevision = record.candidate.revision;
|
|
646
|
+
latestFrameId = frame.frameId;
|
|
647
|
+
if (!latestRevisionByWorld.has(record.world))
|
|
648
|
+
latestRevisionByWorld.set(record.world, new Map());
|
|
649
|
+
latestRevisionByWorld
|
|
650
|
+
.get(record.world)
|
|
651
|
+
?.set(record.candidate.entity, record.candidate.revision);
|
|
652
|
+
if (!latestTopologyRevisionByWorld.has(record.world))
|
|
653
|
+
latestTopologyRevisionByWorld.set(record.world, new Map());
|
|
654
|
+
latestTopologyRevisionByWorld
|
|
655
|
+
.get(record.world)
|
|
656
|
+
?.set(record.candidate.entity, record.candidate.topologyRevision);
|
|
657
|
+
}
|
|
658
|
+
return receipts;
|
|
659
|
+
},
|
|
660
|
+
invalidateWorld(world, completion) {
|
|
661
|
+
for (const [id, record] of records) {
|
|
662
|
+
if (record.world !== world) continue;
|
|
663
|
+
// An earlier terminal operation already owns this reservation's fence.
|
|
664
|
+
if (record.candidate.state === 'cancelled' || record.candidate.state === 'retired')
|
|
665
|
+
continue;
|
|
666
|
+
publishedReceipts.delete(id);
|
|
667
|
+
record.candidate = Object.freeze({ ...record.candidate, state: 'cancelled' as const });
|
|
668
|
+
const release = (): void => {
|
|
669
|
+
if (records.get(id) !== record) return;
|
|
670
|
+
records.delete(id);
|
|
671
|
+
meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
|
|
672
|
+
};
|
|
673
|
+
if (completion === undefined) release();
|
|
674
|
+
else void completion.then(release, release);
|
|
675
|
+
invalidated += 1;
|
|
676
|
+
}
|
|
677
|
+
latestRevisionByWorld.delete(world);
|
|
678
|
+
latestTopologyRevisionByWorld.delete(world);
|
|
679
|
+
},
|
|
680
|
+
invalidateGeneration(generation) {
|
|
681
|
+
if (generation === currentGeneration) return;
|
|
682
|
+
currentGeneration = generation;
|
|
683
|
+
const invalidatedWorlds = new Set<object>();
|
|
684
|
+
for (const [id, record] of records) {
|
|
685
|
+
if (record.candidate.generation !== generation) {
|
|
686
|
+
records.delete(id);
|
|
687
|
+
publishedReceipts.delete(id);
|
|
688
|
+
meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
|
|
689
|
+
invalidatedWorlds.add(record.world);
|
|
690
|
+
invalidated += 1;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
for (const world of invalidatedWorlds) {
|
|
694
|
+
latestRevisionByWorld.delete(world);
|
|
695
|
+
latestTopologyRevisionByWorld.delete(world);
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
receipt(candidate) {
|
|
699
|
+
const receipt = publishedReceipts.get(candidate.candidateId);
|
|
700
|
+
const record = records.get(candidate.candidateId);
|
|
701
|
+
if (record === undefined || !sameCandidateCredential(candidate, record.candidate)) {
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
return receipt?.generation === candidate.generation ? receipt : undefined;
|
|
705
|
+
},
|
|
706
|
+
inspect() {
|
|
707
|
+
let prepared = 0;
|
|
708
|
+
let accepted = 0;
|
|
709
|
+
let published = 0;
|
|
710
|
+
for (const record of records.values()) {
|
|
711
|
+
if (record.candidate.state === 'prepared') prepared += 1;
|
|
712
|
+
else if (record.candidate.state === 'accepted') accepted += 1;
|
|
713
|
+
else if (record.candidate.state === 'published') published += 1;
|
|
714
|
+
}
|
|
715
|
+
return Object.freeze({
|
|
716
|
+
generation: currentGeneration,
|
|
717
|
+
prepared,
|
|
718
|
+
accepted,
|
|
719
|
+
published,
|
|
720
|
+
retired,
|
|
721
|
+
...(latestRevision === undefined ? {} : { latestRevision }),
|
|
722
|
+
...(latestFrameId === undefined ? {} : { latestFrameId }),
|
|
723
|
+
historyInvalidations,
|
|
724
|
+
invalidated,
|
|
725
|
+
meshBytes,
|
|
726
|
+
maxMeshBytes,
|
|
727
|
+
});
|
|
728
|
+
},
|
|
729
|
+
dispose() {
|
|
730
|
+
disposed = true;
|
|
731
|
+
records.clear();
|
|
732
|
+
publishedReceipts.clear();
|
|
733
|
+
meshBytes = 0;
|
|
734
|
+
},
|
|
735
|
+
};
|
|
736
|
+
}
|