@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
|
@@ -9,13 +9,12 @@
|
|
|
9
9
|
// >= 8` — i.e. WebGPU spec default device).
|
|
10
10
|
// One shared GPU buffer grows at 1.5x (Bevy skin.rs alignment,
|
|
11
11
|
// plan-strategy D-4). Every entity's slice is `{ buffer: shared,
|
|
12
|
-
// byteOffset: cursor }`.
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// after every slice.
|
|
12
|
+
// byteOffset: cursor }`. The direct CPU caster keeps the fixed
|
|
13
|
+
// `bindingWindowBytes` view and dynamic offset; the scene-index GPU
|
|
14
|
+
// projection binds the complete arena and addresses this slice through
|
|
15
|
+
// `InstanceData.customDataStart`. WebGPU's dynamic-offset bound remains
|
|
16
|
+
// satisfied for the direct path because the allocator extends
|
|
17
|
+
// `buffer.size` to `cursor + bindingWindowBytes` after every slice.
|
|
19
18
|
//
|
|
20
19
|
// UNIFORM PATH (`useStorageBuffer = false`, browser uniform fallback —
|
|
21
20
|
// `maxStorageBuffersPerShaderStage = 0`).
|
|
@@ -29,12 +28,12 @@
|
|
|
29
28
|
// BG (cached per-buffer pointer; pool reuse keeps createBuffer
|
|
30
29
|
// amortized across frames).
|
|
31
30
|
//
|
|
32
|
-
// Concept that does NOT branch by path: `bindingWindowBytes`.
|
|
33
|
-
// the static `pbr-skin-mesh-array-bgl @binding(1)`
|
|
34
|
-
// identical
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
31
|
+
// Concept that does NOT branch by allocator path: `bindingWindowBytes`.
|
|
32
|
+
// It is the static direct/residual `pbr-skin-mesh-array-bgl @binding(1)`
|
|
33
|
+
// entry size, identical for the fixed-window WGSL layout. dynOffset[1]
|
|
34
|
+
// on the uniform path is always 0; on the storage direct path it walks
|
|
35
|
+
// aligned slices. The scene-index GPU path intentionally binds the whole
|
|
36
|
+
// storage arena and uses `customDataStart` rather than this fixed window.
|
|
38
37
|
//
|
|
39
38
|
// CPU pre-multiply (path-agnostic): writeJointPalette(slice, ibm, jointWorld)
|
|
40
39
|
// -> mat4 per joint: M_i = joint_world_i * IBM_i
|
|
@@ -52,7 +51,11 @@ import {
|
|
|
52
51
|
GPU_BUFFER_USAGE_STORAGE,
|
|
53
52
|
GPU_BUFFER_USAGE_UNIFORM,
|
|
54
53
|
} from '../gpu-usage';
|
|
55
|
-
import type {
|
|
54
|
+
import type {
|
|
55
|
+
SkinPaletteDirtyRange,
|
|
56
|
+
SkinPaletteReceipt,
|
|
57
|
+
SkinPaletteSlice,
|
|
58
|
+
} from './skin-palette-types';
|
|
56
59
|
|
|
57
60
|
const MAT4_BYTES = 64; // 16 f32 * 4 bytes
|
|
58
61
|
// MAX_JOINTS = 255 matches `pbr-skin-mesh-array-bgl @binding(1)` static BG
|
|
@@ -71,11 +74,18 @@ const BINDING_WINDOW_BYTES = MAX_JOINTS * MAT4_BYTES; // 16320
|
|
|
71
74
|
// aligned` at draw time.
|
|
72
75
|
const PALETTE_OFFSET_ALIGN = 256;
|
|
73
76
|
|
|
77
|
+
function alignedPaletteBytes(jointCount: number): number {
|
|
78
|
+
return (jointCount * MAT4_BYTES + (PALETTE_OFFSET_ALIGN - 1)) & ~(PALETTE_OFFSET_ALIGN - 1);
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
export interface SkinPaletteAllocator {
|
|
82
|
+
/** Monotonic successful GPU content writes, independent of allocation identity. */
|
|
83
|
+
readonly contentRevision: number;
|
|
75
84
|
/**
|
|
76
|
-
* Static BG entry size for
|
|
77
|
-
* `MAX_JOINTS * 64 =
|
|
78
|
-
*
|
|
85
|
+
* Static BG entry size for the direct/residual
|
|
86
|
+
* `pbr-skin-mesh-array-bgl @binding(1)` path. Always `MAX_JOINTS * 64 =
|
|
87
|
+
* 16320`; the scene-index GPU path binds the complete storage arena
|
|
88
|
+
* instead. Path-independent for the fixed-window consumer.
|
|
79
89
|
*/
|
|
80
90
|
readonly bindingWindowBytes: number;
|
|
81
91
|
/**
|
|
@@ -86,6 +96,31 @@ export interface SkinPaletteAllocator {
|
|
|
86
96
|
* `slice.buffer` identity (shared vs per-entity), not this flag.
|
|
87
97
|
*/
|
|
88
98
|
readonly useStorageBuffer: boolean;
|
|
99
|
+
/** Allocate or reuse a persistent identity/generation-scoped palette range. */
|
|
100
|
+
allocatePersistentSlice(input: {
|
|
101
|
+
readonly identity: string;
|
|
102
|
+
readonly generation: number;
|
|
103
|
+
readonly jointCount: number;
|
|
104
|
+
readonly bounds?: Float32Array;
|
|
105
|
+
}): SkinPaletteReceipt;
|
|
106
|
+
/**
|
|
107
|
+
* Compare the producer's current joint matrices with the last observed
|
|
108
|
+
* pose and mark only changed joints dirty. The allocator owns this small
|
|
109
|
+
* pose observation cache so extract does not grow a second palette registry.
|
|
110
|
+
*/
|
|
111
|
+
observePersistentJoints(
|
|
112
|
+
identity: string,
|
|
113
|
+
ibms: readonly Float32Array[],
|
|
114
|
+
jointWorlds: readonly Mat4[],
|
|
115
|
+
): readonly number[];
|
|
116
|
+
/** Mark producer-owned joints dirty; adjacent joints are merged on receipt. */
|
|
117
|
+
markDirtyJoints(identity: string, joints: readonly number[]): void;
|
|
118
|
+
/** Start a frame without discarding persistent ranges or their fences. */
|
|
119
|
+
beginFrame(): void;
|
|
120
|
+
/** Reconcile persistent identities after the frame's extract pass. */
|
|
121
|
+
endFrame(): void;
|
|
122
|
+
/** Retire one producer identity after its owning scene is detached. */
|
|
123
|
+
releasePersistentSlice(identity: string): void;
|
|
89
124
|
/**
|
|
90
125
|
* Allocate a slice for `jointCount` joints. Returns the buffer + byte
|
|
91
126
|
* offset the record stage should bind. Storage path returns the same
|
|
@@ -98,12 +133,6 @@ export interface SkinPaletteAllocator {
|
|
|
98
133
|
* `device.limits.maxStorageBufferBindingSize`
|
|
99
134
|
*/
|
|
100
135
|
allocateSlice(jointCount: number): SkinPaletteSlice;
|
|
101
|
-
/** Allocate one stable slice for a retained render source. */
|
|
102
|
-
allocateSliceFor(key: string, jointCount: number): SkinPaletteSlice;
|
|
103
|
-
/** Release a stable slice when its render source leaves the scene. */
|
|
104
|
-
releaseSliceFor(key: string): void;
|
|
105
|
-
/** Reconcile stable slices after a full render-source extraction. */
|
|
106
|
-
retainSliceKeys(keys: ReadonlySet<string>): void;
|
|
107
136
|
/**
|
|
108
137
|
* Write joint matrices into the slice's buffer at the slice's offset.
|
|
109
138
|
* Computes M_i = jointWorldTransforms[i] * ibm[i] per joint.
|
|
@@ -118,12 +147,20 @@ export interface SkinPaletteAllocator {
|
|
|
118
147
|
ibms: readonly Float32Array[],
|
|
119
148
|
jointWorlds: readonly Mat4[],
|
|
120
149
|
): void;
|
|
150
|
+
/** Write only the dirty ranges carried by a persistent receipt. */
|
|
151
|
+
writePersistentJointPalette(
|
|
152
|
+
receipt: SkinPaletteReceipt,
|
|
153
|
+
ibms: readonly Float32Array[],
|
|
154
|
+
jointWorlds: readonly Mat4[],
|
|
155
|
+
): void;
|
|
121
156
|
/**
|
|
122
157
|
* Reset for next frame. Storage path: cursor rewinds, shared buffer
|
|
123
158
|
* stays. Uniform path: per-entity buffer cursor rewinds (pool entries
|
|
124
159
|
* stay allocated and round-robin to the next frame's first entity).
|
|
125
160
|
*/
|
|
126
161
|
resetForFrame(): void;
|
|
162
|
+
/** Release all allocator-owned GPU buffers during renderer teardown. */
|
|
163
|
+
dispose(): void;
|
|
127
164
|
}
|
|
128
165
|
|
|
129
166
|
export function createSkinPaletteAllocator(
|
|
@@ -135,16 +172,7 @@ export function createSkinPaletteAllocator(
|
|
|
135
172
|
let storageBuffer: Buffer | null = null;
|
|
136
173
|
let storageCapacity = 0;
|
|
137
174
|
let storageCursor = 0;
|
|
138
|
-
let
|
|
139
|
-
const stableSlices = new Map<string, SkinPaletteSlice>();
|
|
140
|
-
const stableUniformBuffers = new Map<string, Buffer>();
|
|
141
|
-
const storageBufferCapacities = new WeakMap<Buffer, number>();
|
|
142
|
-
const stableFreeRanges: Array<{
|
|
143
|
-
start: number;
|
|
144
|
-
count: number;
|
|
145
|
-
buffer: Buffer;
|
|
146
|
-
capacity: number;
|
|
147
|
-
}> = [];
|
|
175
|
+
let contentRevision = 0;
|
|
148
176
|
|
|
149
177
|
// Uniform path: per-entity buffer pool. `pool` is the pre-allocated
|
|
150
178
|
// 16320 B UBO ring; `poolCursor` advances per allocateSlice within a
|
|
@@ -154,6 +182,347 @@ export function createSkinPaletteAllocator(
|
|
|
154
182
|
// bounded by maxUniformBufferBindingSize (>= 16384 by spec floor).
|
|
155
183
|
const pool: Buffer[] = [];
|
|
156
184
|
let poolCursor = 0;
|
|
185
|
+
const persistentUniformBuffers = new Map<string, Buffer>();
|
|
186
|
+
|
|
187
|
+
interface PersistentAllocation {
|
|
188
|
+
readonly identity: string;
|
|
189
|
+
readonly generation: number;
|
|
190
|
+
readonly jointCount: number;
|
|
191
|
+
readonly byteSize: number;
|
|
192
|
+
slice: SkinPaletteSlice;
|
|
193
|
+
readonly fence: number;
|
|
194
|
+
readonly bounds?: Float32Array;
|
|
195
|
+
dirtyJoints: Set<number>;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Receipts retain the allocation owner rather than copying its buffer
|
|
200
|
+
* handle. A same-frame arena grow rebases every active allocation; the
|
|
201
|
+
* getter keeps receipts already handed to extract/record on that one
|
|
202
|
+
* current buffer instead of leaving an earlier draw bound to a retired
|
|
203
|
+
* generation.
|
|
204
|
+
*/
|
|
205
|
+
function persistentReceipt(
|
|
206
|
+
allocation: PersistentAllocation,
|
|
207
|
+
dirtyRanges: readonly SkinPaletteDirtyRange[],
|
|
208
|
+
uploadBytes: number,
|
|
209
|
+
retiredByteOffset?: number,
|
|
210
|
+
): SkinPaletteReceipt {
|
|
211
|
+
return {
|
|
212
|
+
get buffer() {
|
|
213
|
+
return allocation.slice.buffer;
|
|
214
|
+
},
|
|
215
|
+
get byteOffset() {
|
|
216
|
+
return allocation.slice.byteOffset;
|
|
217
|
+
},
|
|
218
|
+
jointCount: allocation.slice.jointCount,
|
|
219
|
+
identity: allocation.identity,
|
|
220
|
+
generation: allocation.generation,
|
|
221
|
+
fence: allocation.fence,
|
|
222
|
+
customDataStart: useStorageBuffer ? allocation.slice.byteOffset / MAT4_BYTES : 0,
|
|
223
|
+
storageOrUniform: useStorageBuffer ? 'storage' : 'uniform',
|
|
224
|
+
...(allocation.bounds === undefined ? {} : { bounds: new Float32Array(allocation.bounds) }),
|
|
225
|
+
dirtyRanges,
|
|
226
|
+
uploadBytes,
|
|
227
|
+
...(retiredByteOffset === undefined ? {} : { retiredByteOffset }),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const persistent = new Map<string, PersistentAllocation>();
|
|
231
|
+
interface StorageRange {
|
|
232
|
+
buffer: Buffer;
|
|
233
|
+
offset: number;
|
|
234
|
+
byteSize: number;
|
|
235
|
+
}
|
|
236
|
+
const freeStorageRanges: StorageRange[] = [];
|
|
237
|
+
const pendingStorageRetirements = new Set<StorageRange>();
|
|
238
|
+
const destroyedBuffers = new Set<Buffer>();
|
|
239
|
+
let allocatorDisposed = false;
|
|
240
|
+
const pendingDirty = new Map<string, Set<number>>();
|
|
241
|
+
const observedPose = new Map<string, readonly Float32Array[]>();
|
|
242
|
+
const frameSeenPersistent = new Set<string>();
|
|
243
|
+
let persistentCursor = 0;
|
|
244
|
+
let persistentFence = 0;
|
|
245
|
+
|
|
246
|
+
const queueWriteBuffer = (buffer: Buffer, byteOffset: number, payload: Float32Array): void => {
|
|
247
|
+
const result = device.queue.writeBuffer(buffer, byteOffset, payload);
|
|
248
|
+
if (!result.ok) throw result.error;
|
|
249
|
+
contentRevision += 1;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
function normalizedDirtyRanges(
|
|
253
|
+
allocation: PersistentAllocation,
|
|
254
|
+
): readonly SkinPaletteDirtyRange[] {
|
|
255
|
+
const joints = [...allocation.dirtyJoints]
|
|
256
|
+
.filter((joint) => joint >= 0 && joint < allocation.jointCount)
|
|
257
|
+
.sort((left, right) => left - right);
|
|
258
|
+
const ranges: SkinPaletteDirtyRange[] = [];
|
|
259
|
+
for (const joint of joints) {
|
|
260
|
+
const previous = ranges[ranges.length - 1];
|
|
261
|
+
if (previous !== undefined && previous.startJoint + previous.jointCount === joint) {
|
|
262
|
+
ranges[ranges.length - 1] = {
|
|
263
|
+
startJoint: previous.startJoint,
|
|
264
|
+
jointCount: previous.jointCount + 1,
|
|
265
|
+
};
|
|
266
|
+
} else {
|
|
267
|
+
ranges.push({ startJoint: joint, jointCount: 1 });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return Object.freeze(ranges);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function mergeFreeStorageRange(range: StorageRange): void {
|
|
274
|
+
if (allocatorDisposed || storageBuffer !== range.buffer || range.byteSize <= 0) return;
|
|
275
|
+
freeStorageRanges.push({ ...range });
|
|
276
|
+
freeStorageRanges.sort((left, right) => left.offset - right.offset);
|
|
277
|
+
for (let index = freeStorageRanges.length - 1; index > 0; index -= 1) {
|
|
278
|
+
const current = freeStorageRanges[index];
|
|
279
|
+
const previous = freeStorageRanges[index - 1];
|
|
280
|
+
if (current === undefined || previous === undefined) continue;
|
|
281
|
+
if (previous.offset + previous.byteSize !== current.offset) continue;
|
|
282
|
+
previous.byteSize += current.byteSize;
|
|
283
|
+
freeStorageRanges.splice(index, 1);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function retireStorageRange(range: StorageRange): void {
|
|
288
|
+
if (allocatorDisposed) return;
|
|
289
|
+
pendingStorageRetirements.add(range);
|
|
290
|
+
void device.queue.onSubmittedWorkDone().then(
|
|
291
|
+
() => {
|
|
292
|
+
pendingStorageRetirements.delete(range);
|
|
293
|
+
mergeFreeStorageRange(range);
|
|
294
|
+
},
|
|
295
|
+
() => {
|
|
296
|
+
pendingStorageRetirements.delete(range);
|
|
297
|
+
mergeFreeStorageRange(range);
|
|
298
|
+
},
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function takeFreeStorageRange(byteSize: number): StorageRange | undefined {
|
|
303
|
+
if (!useStorageBuffer || storageBuffer === null) return undefined;
|
|
304
|
+
let candidateIndex = -1;
|
|
305
|
+
for (let index = 0; index < freeStorageRanges.length; index += 1) {
|
|
306
|
+
const candidate = freeStorageRanges[index];
|
|
307
|
+
if (
|
|
308
|
+
candidate !== undefined &&
|
|
309
|
+
candidate.buffer === storageBuffer &&
|
|
310
|
+
candidate.byteSize >= byteSize
|
|
311
|
+
) {
|
|
312
|
+
candidateIndex = index;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (candidateIndex < 0) return undefined;
|
|
317
|
+
const candidate = freeStorageRanges[candidateIndex];
|
|
318
|
+
if (candidate === undefined) return undefined;
|
|
319
|
+
freeStorageRanges.splice(candidateIndex, 1);
|
|
320
|
+
if (candidate.byteSize > byteSize) {
|
|
321
|
+
freeStorageRanges.push({
|
|
322
|
+
buffer: candidate.buffer,
|
|
323
|
+
offset: candidate.offset + byteSize,
|
|
324
|
+
byteSize: candidate.byteSize - byteSize,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
return { buffer: candidate.buffer, offset: candidate.offset, byteSize };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function destroyBufferOnce(buffer: Buffer): void {
|
|
331
|
+
if (destroyedBuffers.has(buffer)) return;
|
|
332
|
+
destroyedBuffers.add(buffer);
|
|
333
|
+
device.destroyBuffer(buffer);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function allocatePersistentSlice(input: {
|
|
337
|
+
readonly identity: string;
|
|
338
|
+
readonly generation: number;
|
|
339
|
+
readonly jointCount: number;
|
|
340
|
+
readonly bounds?: Float32Array;
|
|
341
|
+
}): SkinPaletteReceipt {
|
|
342
|
+
frameSeenPersistent.add(input.identity);
|
|
343
|
+
if (
|
|
344
|
+
!Number.isInteger(input.jointCount) ||
|
|
345
|
+
input.jointCount <= 0 ||
|
|
346
|
+
input.jointCount > MAX_JOINTS
|
|
347
|
+
) {
|
|
348
|
+
throw new RangeError(`skin joint count must be between 1 and ${MAX_JOINTS}`);
|
|
349
|
+
}
|
|
350
|
+
const prior = persistent.get(input.identity);
|
|
351
|
+
if (
|
|
352
|
+
prior !== undefined &&
|
|
353
|
+
prior.generation === input.generation &&
|
|
354
|
+
prior.jointCount === input.jointCount
|
|
355
|
+
) {
|
|
356
|
+
const dirtyRanges = normalizedDirtyRanges(prior);
|
|
357
|
+
const uploadBytes = dirtyRanges.reduce(
|
|
358
|
+
(total, range) => total + range.jointCount * MAT4_BYTES,
|
|
359
|
+
0,
|
|
360
|
+
);
|
|
361
|
+
// Observation happens before allocation during extract. Consume the
|
|
362
|
+
// producer-owned dirty set exactly once; the receipt carries the write
|
|
363
|
+
// ranges to the palette writer below.
|
|
364
|
+
prior.dirtyJoints.clear();
|
|
365
|
+
return persistentReceipt(prior, dirtyRanges, uploadBytes);
|
|
366
|
+
}
|
|
367
|
+
const retiredByteOffset = prior?.slice.byteOffset;
|
|
368
|
+
if (prior !== undefined) {
|
|
369
|
+
persistent.delete(input.identity);
|
|
370
|
+
if (useStorageBuffer) {
|
|
371
|
+
retireStorageRange({
|
|
372
|
+
buffer: prior.slice.buffer,
|
|
373
|
+
offset: prior.slice.byteOffset,
|
|
374
|
+
byteSize: prior.byteSize,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
let slice: SkinPaletteSlice;
|
|
379
|
+
if (useStorageBuffer) {
|
|
380
|
+
const byteSize = alignedPaletteBytes(input.jointCount);
|
|
381
|
+
const recycled = takeFreeStorageRange(byteSize);
|
|
382
|
+
if (recycled !== undefined) {
|
|
383
|
+
slice = {
|
|
384
|
+
jointCount: input.jointCount,
|
|
385
|
+
byteOffset: recycled.offset,
|
|
386
|
+
buffer: recycled.buffer,
|
|
387
|
+
};
|
|
388
|
+
} else {
|
|
389
|
+
const offset = persistentCursor;
|
|
390
|
+
const needed = offset + BINDING_WINDOW_BYTES;
|
|
391
|
+
ensureStorageCapacity(needed);
|
|
392
|
+
// biome-ignore lint/style/noNonNullAssertion: ensureStorageCapacity creates the buffer
|
|
393
|
+
const buffer = storageBuffer!;
|
|
394
|
+
persistentCursor = offset + byteSize;
|
|
395
|
+
slice = { jointCount: input.jointCount, byteOffset: offset, buffer };
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
let buffer = persistentUniformBuffers.get(input.identity);
|
|
399
|
+
if (buffer === undefined) {
|
|
400
|
+
if (BINDING_WINDOW_BYTES > maxBindingSize) {
|
|
401
|
+
throw new SkinPaletteOverflowError(BINDING_WINDOW_BYTES, maxBindingSize);
|
|
402
|
+
}
|
|
403
|
+
const created = device.createBuffer({
|
|
404
|
+
label: 'skin-palette-persistent',
|
|
405
|
+
size: BINDING_WINDOW_BYTES,
|
|
406
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
407
|
+
mappedAtCreation: false,
|
|
408
|
+
});
|
|
409
|
+
if (!created.ok) throw created.error;
|
|
410
|
+
buffer = created.value;
|
|
411
|
+
persistentUniformBuffers.set(input.identity, buffer);
|
|
412
|
+
}
|
|
413
|
+
slice = { jointCount: input.jointCount, byteOffset: 0, buffer };
|
|
414
|
+
}
|
|
415
|
+
// A replacement generation owns a fresh slice. Even when the producer
|
|
416
|
+
// pose is unchanged, the new address has no committed GPU contents, so it
|
|
417
|
+
// must receive a complete upload before the receipt is published.
|
|
418
|
+
const carriedDirty =
|
|
419
|
+
prior !== undefined
|
|
420
|
+
? new Set(Array.from({ length: input.jointCount }, (_, index) => index))
|
|
421
|
+
: (pendingDirty.get(input.identity) ??
|
|
422
|
+
new Set(Array.from({ length: input.jointCount }, (_, index) => index)));
|
|
423
|
+
const allocation: PersistentAllocation = {
|
|
424
|
+
identity: input.identity,
|
|
425
|
+
generation: input.generation,
|
|
426
|
+
jointCount: input.jointCount,
|
|
427
|
+
byteSize: alignedPaletteBytes(input.jointCount),
|
|
428
|
+
slice,
|
|
429
|
+
fence: ++persistentFence,
|
|
430
|
+
...(input.bounds === undefined ? {} : { bounds: new Float32Array(input.bounds) }),
|
|
431
|
+
dirtyJoints: new Set(carriedDirty),
|
|
432
|
+
};
|
|
433
|
+
pendingDirty.delete(input.identity);
|
|
434
|
+
persistent.set(input.identity, allocation);
|
|
435
|
+
const dirtyRanges = normalizedDirtyRanges(allocation);
|
|
436
|
+
const uploadBytes = dirtyRanges.reduce(
|
|
437
|
+
(total, range) => total + range.jointCount * MAT4_BYTES,
|
|
438
|
+
0,
|
|
439
|
+
);
|
|
440
|
+
allocation.dirtyJoints.clear();
|
|
441
|
+
return persistentReceipt(allocation, dirtyRanges, uploadBytes, retiredByteOffset);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function markDirtyJoints(identity: string, joints: readonly number[]): void {
|
|
445
|
+
const allocation = persistent.get(identity);
|
|
446
|
+
const dirty = allocation?.dirtyJoints ?? pendingDirty.get(identity) ?? new Set<number>();
|
|
447
|
+
for (const joint of joints) {
|
|
448
|
+
if (
|
|
449
|
+
Number.isInteger(joint) &&
|
|
450
|
+
joint >= 0 &&
|
|
451
|
+
(allocation === undefined || joint < allocation.jointCount)
|
|
452
|
+
) {
|
|
453
|
+
dirty.add(joint);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (allocation === undefined) pendingDirty.set(identity, dirty);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function releasePersistentSlice(identity: string): void {
|
|
460
|
+
const allocation = persistent.get(identity);
|
|
461
|
+
persistent.delete(identity);
|
|
462
|
+
pendingDirty.delete(identity);
|
|
463
|
+
observedPose.delete(identity);
|
|
464
|
+
if (allocation === undefined) return;
|
|
465
|
+
if (useStorageBuffer) {
|
|
466
|
+
retireStorageRange({
|
|
467
|
+
buffer: allocation.slice.buffer,
|
|
468
|
+
offset: allocation.slice.byteOffset,
|
|
469
|
+
byteSize: allocation.byteSize,
|
|
470
|
+
});
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const buffer = persistentUniformBuffers.get(identity);
|
|
474
|
+
if (buffer === undefined) return;
|
|
475
|
+
persistentUniformBuffers.delete(identity);
|
|
476
|
+
void device.queue.onSubmittedWorkDone().then(
|
|
477
|
+
() => destroyBufferOnce(buffer),
|
|
478
|
+
() => destroyBufferOnce(buffer),
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function observePersistentJoints(
|
|
483
|
+
identity: string,
|
|
484
|
+
ibms: readonly Float32Array[],
|
|
485
|
+
jointWorlds: readonly Mat4[],
|
|
486
|
+
): readonly number[] {
|
|
487
|
+
const count = Math.min(ibms.length, jointWorlds.length);
|
|
488
|
+
const previous = observedPose.get(identity);
|
|
489
|
+
const next: Float32Array[] = new Array(count);
|
|
490
|
+
const changed: number[] = [];
|
|
491
|
+
const temp = mat4.create();
|
|
492
|
+
const ibm = mat4.create();
|
|
493
|
+
for (let index = 0; index < count; index += 1) {
|
|
494
|
+
const ibmFlat = ibms[index];
|
|
495
|
+
const jointWorld = jointWorlds[index];
|
|
496
|
+
if (ibmFlat === undefined || jointWorld === undefined) continue;
|
|
497
|
+
for (let lane = 0; lane < 16; lane += 1) ibm[lane] = ibmFlat[lane] ?? 0;
|
|
498
|
+
mat4.multiply(temp, jointWorld, ibm);
|
|
499
|
+
const copy = new Float32Array(temp);
|
|
500
|
+
next[index] = copy;
|
|
501
|
+
const prior = previous?.[index];
|
|
502
|
+
if (
|
|
503
|
+
prior === undefined ||
|
|
504
|
+
prior.length !== copy.length ||
|
|
505
|
+
prior.some((v, lane) => v !== copy[lane])
|
|
506
|
+
) {
|
|
507
|
+
changed.push(index);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
observedPose.set(identity, Object.freeze(next));
|
|
511
|
+
if (changed.length > 0) markDirtyJoints(identity, changed);
|
|
512
|
+
return Object.freeze(changed);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function beginFrame(): void {
|
|
516
|
+
frameSeenPersistent.clear();
|
|
517
|
+
persistentCursor = Math.max(persistentCursor, storageCursor);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function endFrame(): void {
|
|
521
|
+
for (const identity of [...persistent.keys()]) {
|
|
522
|
+
if (!frameSeenPersistent.has(identity)) releasePersistentSlice(identity);
|
|
523
|
+
}
|
|
524
|
+
frameSeenPersistent.clear();
|
|
525
|
+
}
|
|
157
526
|
|
|
158
527
|
function ensureStorageCapacity(needed: number): void {
|
|
159
528
|
if (storageBuffer !== null && needed <= storageCapacity) return;
|
|
@@ -172,12 +541,45 @@ export function createSkinPaletteAllocator(
|
|
|
172
541
|
mappedAtCreation: false,
|
|
173
542
|
});
|
|
174
543
|
if (!bufRes.ok) throw bufRes.error;
|
|
544
|
+
const previous = storageBuffer;
|
|
175
545
|
storageBuffer = bufRes.value;
|
|
176
546
|
storageCapacity = newCapacity;
|
|
177
|
-
|
|
547
|
+
for (const range of freeStorageRanges) {
|
|
548
|
+
if (range.buffer === previous) range.buffer = storageBuffer;
|
|
549
|
+
}
|
|
550
|
+
for (const range of pendingStorageRetirements) {
|
|
551
|
+
if (range.buffer === previous) range.buffer = storageBuffer;
|
|
552
|
+
}
|
|
553
|
+
// A persistent arena can grow after earlier identities have already been
|
|
554
|
+
// published. Rebase those slices into the new arena and restore their
|
|
555
|
+
// last producer-observed matrices before the next frame can bind it;
|
|
556
|
+
// otherwise one frame would bind two palette buffers through one BG.
|
|
557
|
+
for (const allocation of persistent.values()) {
|
|
558
|
+
const pose = observedPose.get(allocation.identity);
|
|
559
|
+
if (pose !== undefined) {
|
|
560
|
+
const payload = new Float32Array(allocation.jointCount * 16);
|
|
561
|
+
for (let joint = 0; joint < allocation.jointCount; joint += 1) {
|
|
562
|
+
const matrix = pose[joint];
|
|
563
|
+
if (matrix !== undefined) payload.set(matrix, joint * 16);
|
|
564
|
+
}
|
|
565
|
+
queueWriteBuffer(storageBuffer, allocation.slice.byteOffset, payload);
|
|
566
|
+
}
|
|
567
|
+
allocation.slice = { ...allocation.slice, buffer: storageBuffer };
|
|
568
|
+
}
|
|
569
|
+
if (previous !== null) {
|
|
570
|
+
void device.queue.onSubmittedWorkDone().then(
|
|
571
|
+
() => destroyBufferOnce(previous),
|
|
572
|
+
() => destroyBufferOnce(previous),
|
|
573
|
+
);
|
|
574
|
+
}
|
|
178
575
|
}
|
|
179
576
|
|
|
180
|
-
function
|
|
577
|
+
function acquirePoolBuffer(): Buffer {
|
|
578
|
+
if (poolCursor < pool.length) {
|
|
579
|
+
const reused = pool[poolCursor];
|
|
580
|
+
// biome-ignore lint/style/noNonNullAssertion: poolCursor < pool.length guarantees the slot is set
|
|
581
|
+
return reused!;
|
|
582
|
+
}
|
|
181
583
|
if (BINDING_WINDOW_BYTES > maxBindingSize) {
|
|
182
584
|
throw new SkinPaletteOverflowError(BINDING_WINDOW_BYTES, maxBindingSize);
|
|
183
585
|
}
|
|
@@ -188,20 +590,10 @@ export function createSkinPaletteAllocator(
|
|
|
188
590
|
mappedAtCreation: false,
|
|
189
591
|
});
|
|
190
592
|
if (!bufRes.ok) throw bufRes.error;
|
|
593
|
+
pool.push(bufRes.value);
|
|
191
594
|
return bufRes.value;
|
|
192
595
|
}
|
|
193
596
|
|
|
194
|
-
function acquirePoolBuffer(): Buffer {
|
|
195
|
-
if (poolCursor < pool.length) {
|
|
196
|
-
const reused = pool[poolCursor];
|
|
197
|
-
// biome-ignore lint/style/noNonNullAssertion: poolCursor < pool.length guarantees the slot is set
|
|
198
|
-
return reused!;
|
|
199
|
-
}
|
|
200
|
-
const buffer = createUniformBuffer();
|
|
201
|
-
pool.push(buffer);
|
|
202
|
-
return buffer;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
597
|
function allocateSlice(jointCount: number): SkinPaletteSlice {
|
|
206
598
|
if (useStorageBuffer) {
|
|
207
599
|
// Storage path: shared buffer + dynOffset window.
|
|
@@ -231,103 +623,6 @@ export function createSkinPaletteAllocator(
|
|
|
231
623
|
return { jointCount, byteOffset: 0, buffer };
|
|
232
624
|
}
|
|
233
625
|
|
|
234
|
-
function allocateSliceFor(key: string, jointCount: number): SkinPaletteSlice {
|
|
235
|
-
const existing = stableSlices.get(key);
|
|
236
|
-
if (existing !== undefined && existing.jointCount === jointCount) return existing;
|
|
237
|
-
if (existing !== undefined) releaseSliceFor(key);
|
|
238
|
-
if (useStorageBuffer) {
|
|
239
|
-
const span =
|
|
240
|
-
(jointCount * MAT4_BYTES + (PALETTE_OFFSET_ALIGN - 1)) & ~(PALETTE_OFFSET_ALIGN - 1);
|
|
241
|
-
const freeIndex = stableFreeRanges.findIndex(
|
|
242
|
-
(range) =>
|
|
243
|
-
range.buffer === storageBuffer &&
|
|
244
|
-
range.count >= span &&
|
|
245
|
-
range.start + BINDING_WINDOW_BYTES <= range.capacity,
|
|
246
|
-
);
|
|
247
|
-
let offset: number;
|
|
248
|
-
let buffer: Buffer;
|
|
249
|
-
if (freeIndex >= 0) {
|
|
250
|
-
const range = stableFreeRanges[freeIndex];
|
|
251
|
-
if (range === undefined) throw new Error('Skin palette free range disappeared.');
|
|
252
|
-
offset = range.start;
|
|
253
|
-
buffer = range.buffer;
|
|
254
|
-
if (range.count === span) stableFreeRanges.splice(freeIndex, 1);
|
|
255
|
-
else {
|
|
256
|
-
stableFreeRanges[freeIndex] = {
|
|
257
|
-
...range,
|
|
258
|
-
start: range.start + span,
|
|
259
|
-
count: range.count - span,
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
} else {
|
|
263
|
-
offset = stableStorageCursor;
|
|
264
|
-
ensureStorageCapacity(offset + BINDING_WINDOW_BYTES);
|
|
265
|
-
const currentBuffer = storageBuffer;
|
|
266
|
-
if (currentBuffer === null) throw new Error('Skin palette storage buffer was not created.');
|
|
267
|
-
buffer = currentBuffer;
|
|
268
|
-
stableStorageCursor = offset + span;
|
|
269
|
-
}
|
|
270
|
-
const slice = { jointCount, byteOffset: offset, buffer };
|
|
271
|
-
stableSlices.set(key, slice);
|
|
272
|
-
return slice;
|
|
273
|
-
}
|
|
274
|
-
let buffer = stableUniformBuffers.get(key);
|
|
275
|
-
if (buffer === undefined) {
|
|
276
|
-
// Stable slices have a lifetime longer than the frame pool. Allocate a
|
|
277
|
-
// dedicated UBO so resetForFrame() can never hand it to another entity.
|
|
278
|
-
buffer = createUniformBuffer();
|
|
279
|
-
stableUniformBuffers.set(key, buffer);
|
|
280
|
-
}
|
|
281
|
-
const slice = { jointCount, byteOffset: 0, buffer };
|
|
282
|
-
stableSlices.set(key, slice);
|
|
283
|
-
return slice;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function releaseSliceFor(key: string): void {
|
|
287
|
-
const slice = stableSlices.get(key);
|
|
288
|
-
if (slice === undefined) return;
|
|
289
|
-
stableSlices.delete(key);
|
|
290
|
-
if (!useStorageBuffer) {
|
|
291
|
-
stableUniformBuffers.delete(key);
|
|
292
|
-
device.destroyBuffer(slice.buffer);
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const count =
|
|
296
|
-
(slice.jointCount * MAT4_BYTES + (PALETTE_OFFSET_ALIGN - 1)) & ~(PALETTE_OFFSET_ALIGN - 1);
|
|
297
|
-
stableFreeRanges.push({
|
|
298
|
-
start: slice.byteOffset,
|
|
299
|
-
count,
|
|
300
|
-
buffer: slice.buffer,
|
|
301
|
-
capacity: storageBufferCapacities.get(slice.buffer) ?? storageCapacity,
|
|
302
|
-
});
|
|
303
|
-
stableFreeRanges.sort((left, right) => left.start - right.start);
|
|
304
|
-
for (let index = stableFreeRanges.length - 1; index > 0; index -= 1) {
|
|
305
|
-
const current = stableFreeRanges[index];
|
|
306
|
-
const previous = stableFreeRanges[index - 1];
|
|
307
|
-
if (
|
|
308
|
-
current === undefined ||
|
|
309
|
-
previous === undefined ||
|
|
310
|
-
current.buffer !== previous.buffer ||
|
|
311
|
-
previous.start + previous.count !== current.start
|
|
312
|
-
) {
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
stableFreeRanges[index - 1] = {
|
|
316
|
-
start: previous.start,
|
|
317
|
-
count: previous.count + current.count,
|
|
318
|
-
buffer: previous.buffer,
|
|
319
|
-
capacity: previous.capacity,
|
|
320
|
-
};
|
|
321
|
-
stableFreeRanges.splice(index, 1);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function retainSliceKeys(keys: ReadonlySet<string>): void {
|
|
326
|
-
for (const key of stableSlices.keys()) {
|
|
327
|
-
if (!keys.has(key)) releaseSliceFor(key);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
626
|
function writeJointPalette(
|
|
332
627
|
slice: SkinPaletteSlice,
|
|
333
628
|
ibms: readonly Float32Array[],
|
|
@@ -351,8 +646,36 @@ export function createSkinPaletteAllocator(
|
|
|
351
646
|
payload[base + j] = temp[j] ?? 0;
|
|
352
647
|
}
|
|
353
648
|
}
|
|
354
|
-
|
|
355
|
-
|
|
649
|
+
queueWriteBuffer(slice.buffer, slice.byteOffset, payload);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function writePersistentJointPalette(
|
|
653
|
+
receipt: SkinPaletteReceipt,
|
|
654
|
+
ibms: readonly Float32Array[],
|
|
655
|
+
jointWorlds: readonly Mat4[],
|
|
656
|
+
): void {
|
|
657
|
+
if (receipt.uploadBytes === 0 || receipt.dirtyRanges.length === 0) return;
|
|
658
|
+
const temp = mat4.create();
|
|
659
|
+
const ibm = mat4.create();
|
|
660
|
+
for (const range of receipt.dirtyRanges) {
|
|
661
|
+
const count = Math.min(
|
|
662
|
+
range.jointCount,
|
|
663
|
+
ibms.length - range.startJoint,
|
|
664
|
+
jointWorlds.length - range.startJoint,
|
|
665
|
+
);
|
|
666
|
+
if (count <= 0) continue;
|
|
667
|
+
const payload = new Float32Array(count * 16);
|
|
668
|
+
for (let local = 0; local < count; local += 1) {
|
|
669
|
+
const index = range.startJoint + local;
|
|
670
|
+
const ibmFlat = ibms[index];
|
|
671
|
+
const jointWorld = jointWorlds[index];
|
|
672
|
+
if (ibmFlat === undefined || jointWorld === undefined) continue;
|
|
673
|
+
for (let lane = 0; lane < 16; lane += 1) ibm[lane] = ibmFlat[lane] ?? 0;
|
|
674
|
+
mat4.multiply(temp, jointWorld, ibm);
|
|
675
|
+
payload.set(temp, local * 16);
|
|
676
|
+
}
|
|
677
|
+
queueWriteBuffer(receipt.buffer, receipt.byteOffset + range.startJoint * MAT4_BYTES, payload);
|
|
678
|
+
}
|
|
356
679
|
}
|
|
357
680
|
|
|
358
681
|
function resetForFrame(): void {
|
|
@@ -360,14 +683,38 @@ export function createSkinPaletteAllocator(
|
|
|
360
683
|
poolCursor = 0;
|
|
361
684
|
}
|
|
362
685
|
|
|
686
|
+
function dispose(): void {
|
|
687
|
+
if (allocatorDisposed) return;
|
|
688
|
+
allocatorDisposed = true;
|
|
689
|
+
if (storageBuffer !== null) destroyBufferOnce(storageBuffer);
|
|
690
|
+
for (const buffer of pool) destroyBufferOnce(buffer);
|
|
691
|
+
for (const buffer of persistentUniformBuffers.values()) destroyBufferOnce(buffer);
|
|
692
|
+
storageBuffer = null;
|
|
693
|
+
persistent.clear();
|
|
694
|
+
pendingDirty.clear();
|
|
695
|
+
observedPose.clear();
|
|
696
|
+
persistentUniformBuffers.clear();
|
|
697
|
+
freeStorageRanges.length = 0;
|
|
698
|
+
pendingStorageRetirements.clear();
|
|
699
|
+
pool.length = 0;
|
|
700
|
+
}
|
|
701
|
+
|
|
363
702
|
return {
|
|
703
|
+
get contentRevision() {
|
|
704
|
+
return contentRevision;
|
|
705
|
+
},
|
|
364
706
|
bindingWindowBytes: BINDING_WINDOW_BYTES,
|
|
365
707
|
useStorageBuffer,
|
|
708
|
+
allocatePersistentSlice,
|
|
709
|
+
observePersistentJoints,
|
|
710
|
+
markDirtyJoints,
|
|
711
|
+
releasePersistentSlice,
|
|
712
|
+
beginFrame,
|
|
713
|
+
endFrame,
|
|
366
714
|
allocateSlice,
|
|
367
|
-
allocateSliceFor,
|
|
368
|
-
releaseSliceFor,
|
|
369
|
-
retainSliceKeys,
|
|
370
715
|
writeJointPalette,
|
|
716
|
+
writePersistentJointPalette,
|
|
371
717
|
resetForFrame,
|
|
718
|
+
dispose,
|
|
372
719
|
};
|
|
373
720
|
}
|