@forgeax/engine-render 0.1.20 → 0.1.23
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 +365 -29
- package/dist/assembly/backend-contract.d.ts +13 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/device-feature-admission.d.ts +18 -0
- package/dist/assembly/device-feature-admission.d.ts.map +1 -0
- package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
- package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +30 -19
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +19 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/render-target-host.d.ts +46 -0
- package/dist/assembly/render-target-host.d.ts.map +1 -0
- package/dist/assets/render-asset-port.d.ts.map +1 -1
- package/dist/authoring.mjs +4 -1
- package/dist/authoring.mjs.map +1 -1
- package/dist/capture/cube-views.d.ts +21 -0
- package/dist/capture/cube-views.d.ts.map +1 -0
- package/dist/capture/scheduler.d.ts +54 -0
- package/dist/capture/scheduler.d.ts.map +1 -0
- package/dist/{chunk-PCHBPYML.mjs → chunk-4L3P6DT5.mjs} +2 -2
- package/dist/chunk-4L3P6DT5.mjs.map +1 -0
- package/dist/{chunk-VFPLTKCU.mjs → chunk-4NPUASZR.mjs} +15 -4
- package/dist/chunk-4NPUASZR.mjs.map +1 -0
- package/dist/chunk-DIYW3M5D.mjs +5085 -0
- package/dist/chunk-DIYW3M5D.mjs.map +1 -0
- package/dist/chunk-FBC4R44J.mjs +1003 -0
- package/dist/chunk-FBC4R44J.mjs.map +1 -0
- package/dist/{chunk-CDTWTHZR.mjs → chunk-FF7K3FSO.mjs} +474 -168
- package/dist/chunk-FF7K3FSO.mjs.map +1 -0
- package/dist/chunk-HZUPSYW7.mjs +1104 -0
- package/dist/chunk-HZUPSYW7.mjs.map +1 -0
- package/dist/{chunk-TBQJOK2L.mjs → chunk-QODA5UKU.mjs} +321 -269
- package/dist/chunk-QODA5UKU.mjs.map +1 -0
- package/dist/chunk-YALPS44A.mjs +296 -0
- package/dist/chunk-YALPS44A.mjs.map +1 -0
- package/dist/{chunk-4XINDLAY.mjs → chunk-ZUWKAZ2I.mjs} +1963 -2178
- package/dist/chunk-ZUWKAZ2I.mjs.map +1 -0
- package/dist/cluster-binner.d.ts +5 -0
- package/dist/cluster-binner.d.ts.map +1 -1
- package/dist/components/camera.d.ts +1 -0
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/cube-camera.d.ts +20 -0
- package/dist/components/cube-camera.d.ts.map +1 -0
- package/dist/components/directional-light.d.ts +11 -2
- package/dist/components/directional-light.d.ts.map +1 -1
- package/dist/components/directional-shadow-filter.d.ts +25 -0
- package/dist/components/directional-shadow-filter.d.ts.map +1 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts +4 -0
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/light-probe.d.ts +9 -0
- package/dist/components/light-probe.d.ts.map +1 -0
- package/dist/components/rect-area-light.d.ts +12 -0
- package/dist/components/rect-area-light.d.ts.map +1 -0
- package/dist/components/reflection-probe.d.ts +17 -0
- package/dist/components/reflection-probe.d.ts.map +1 -0
- package/dist/components/scene-instance.d.ts +4 -0
- package/dist/components/scene-instance.d.ts.map +1 -1
- package/dist/components/spot-light.d.ts +3 -0
- package/dist/components/spot-light.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +14836 -9620
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +8 -1
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/device/lifecycle-transaction.d.ts +2 -0
- package/dist/device/lifecycle-transaction.d.ts.map +1 -0
- package/dist/environment/frame.d.ts.map +1 -1
- package/dist/environment/lifecycle.d.ts +5 -0
- package/dist/environment/lifecycle.d.ts.map +1 -1
- package/dist/errors/gpu-driven.d.ts +26 -0
- package/dist/errors/gpu-driven.d.ts.map +1 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/recover.d.ts +9 -1
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +166 -93
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/directional-shadow-projection.d.ts +23 -0
- package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
- package/dist/extract/environment.d.ts +14 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +50 -0
- package/dist/extract/gpu-driven.d.ts.map +1 -0
- package/dist/extract/visibility.d.ts.map +1 -1
- package/dist/features/fullscreen.d.ts.map +1 -1
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +10 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +13 -2
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +34 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +63 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +97 -2
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +64 -1
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts +14 -0
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +8 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-usage.d.ts +0 -1
- package/dist/gpu-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +21 -19
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +4 -0
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/kernel-cache.d.ts +36 -0
- package/dist/ibl/kernel-cache.d.ts.map +1 -0
- package/dist/index.d.ts +39 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +91 -6
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +214 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances-derived-bounds.d.ts +50 -0
- package/dist/instances-derived-bounds.d.ts.map +1 -0
- package/dist/internal.d.ts +9 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +23 -2
- package/dist/internal.mjs.map +1 -1
- package/dist/light-buffer-layout.d.ts +58 -140
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/materials.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +54 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +38 -14
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts +2 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
- package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
- package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
- package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
- package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
- package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
- package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts +4 -13
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +5 -26
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +2 -0
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +40 -7
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/point-shadow-inspection.d.ts +21 -0
- package/dist/point-shadow-inspection.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
- package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/resources.d.ts +72 -0
- package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
- package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/state.d.ts +24 -0
- package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +33 -36
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +78 -24
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts +5 -0
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +16 -6
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
- package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
- package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/index.d.ts +8 -0
- package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
- package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/session.d.ts +43 -0
- package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
- package/dist/record/gpu-timing.d.ts +20 -3
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +0 -20
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +6 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +25 -2
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +2 -0
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +98 -6
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +119 -4
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +7 -5
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/reflection/filter.d.ts +22 -0
- package/dist/reflection/filter.d.ts.map +1 -0
- package/dist/reflection/gpu-table.d.ts +25 -0
- package/dist/reflection/gpu-table.d.ts.map +1 -0
- package/dist/reflection/inspection.d.ts +10 -0
- package/dist/reflection/inspection.d.ts.map +1 -0
- package/dist/reflection/projection.d.ts +140 -0
- package/dist/reflection/projection.d.ts.map +1 -0
- package/dist/reflection/record-owner.d.ts +60 -0
- package/dist/reflection/record-owner.d.ts.map +1 -0
- package/dist/render-contract.d.ts +62 -14
- 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 +49 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +160 -31
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +28 -2
- package/dist/render-system.d.ts.map +1 -1
- package/dist/renderer.d.ts +43 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/scene/probe-blend-record.d.ts +22 -0
- package/dist/scene/probe-blend-record.d.ts.map +1 -0
- package/dist/scene/probe-blend.d.ts +142 -0
- package/dist/scene/probe-blend.d.ts.map +1 -0
- package/dist/scene/render-scene-types.d.ts +0 -6
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +117 -9
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/visibility/bounds.d.ts +20 -0
- package/dist/scene/visibility/bounds.d.ts.map +1 -0
- package/dist/scene/visibility/budget.d.ts +16 -0
- package/dist/scene/visibility/budget.d.ts.map +1 -0
- package/dist/scene/visibility/facet.d.ts +73 -0
- package/dist/scene/visibility/facet.d.ts.map +1 -0
- package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
- package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
- package/dist/scene/visibility/gpu-lod.d.ts +46 -0
- package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
- package/dist/scene/visibility/inspection.d.ts +132 -0
- package/dist/scene/visibility/inspection.d.ts.map +1 -0
- package/dist/scene/visibility/lod-selector.d.ts +25 -0
- package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
- package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
- package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
- package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
- package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
- package/dist/scene/visibility/types.d.ts +34 -0
- package/dist/scene/visibility/types.d.ts.map +1 -0
- package/dist/shadow-atlas.d.ts +3 -3
- package/dist/shadow-atlas.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +3 -0
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/ssr/admission.d.ts +122 -0
- package/dist/ssr/admission.d.ts.map +1 -0
- package/dist/ssr/composition.d.ts +32 -0
- package/dist/ssr/composition.d.ts.map +1 -0
- package/dist/ssr/errors.d.ts +39 -0
- package/dist/ssr/errors.d.ts.map +1 -0
- package/dist/ssr/identity.d.ts +13 -0
- package/dist/ssr/identity.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +1 -1
- package/dist/targets/contracts.d.ts +66 -0
- package/dist/targets/contracts.d.ts.map +1 -0
- package/dist/targets/graph-projection.d.ts +29 -0
- package/dist/targets/graph-projection.d.ts.map +1 -0
- package/dist/targets/material-source.d.ts +34 -0
- package/dist/targets/material-source.d.ts.map +1 -0
- package/dist/targets/owner.d.ts +32 -0
- package/dist/targets/owner.d.ts.map +1 -0
- package/dist/targets/physical.d.ts +18 -0
- package/dist/targets/physical.d.ts.map +1 -0
- package/dist/targets/readback.d.ts +39 -0
- package/dist/targets/readback.d.ts.map +1 -0
- package/dist/temporal/frame-coordinator.d.ts +13 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +2 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -5
- package/dist/temporal/view.d.ts +7 -0
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/tilemap-chunk-extract-system.d.ts +1 -1
- package/dist/typed-render-graph-primitives.d.ts +5 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +1 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +9 -5
- package/dist/volume/passes.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
- package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
- package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
- package/src/__tests__/cluster-binner.unit.test.ts +35 -0
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +12 -2
- package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
- package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
- package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
- package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
- package/src/__tests__/cube-camera.integration.test.ts +42 -0
- package/src/__tests__/detached-inspection.integration.test.ts +6 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +85 -0
- package/src/__tests__/direct-light-snapshot.unit.test.ts +62 -6
- package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
- package/src/__tests__/environment-generation.integration.test.ts +16 -0
- package/src/__tests__/evidence-identity.ts +13 -0
- package/src/__tests__/evidence-identity.unit.test.ts +24 -0
- package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
- package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
- package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
- package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
- package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
- package/src/__tests__/factory-contract.integration.test.ts +288 -29
- package/src/__tests__/fog-component.unit.test.ts +67 -0
- package/src/__tests__/fog.integration.test.ts +86 -0
- package/src/__tests__/frame-observation.contract.test.ts +15 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -8
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +18 -1
- package/src/__tests__/gpu-driven-production.integration.test.ts +579 -17
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +101 -6
- package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
- package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
- package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
- package/src/__tests__/gpu-lod-evidence.ts +188 -0
- package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
- package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
- package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
- package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
- package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
- package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
- package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
- package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
- package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
- package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
- package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
- package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
- package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
- package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
- package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
- package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
- package/src/__tests__/gpu-scene.unit.test.ts +49 -3
- package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
- package/src/__tests__/instances-culling.integration.test.ts +94 -0
- package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
- package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
- package/src/__tests__/light-inspection.unit.test.ts +55 -0
- package/src/__tests__/light-resource-error.unit.test.ts +43 -0
- package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
- package/src/__tests__/lod-bounds.unit.test.ts +42 -0
- package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
- package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
- package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
- package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
- package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
- package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
- package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
- package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
- package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
- package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
- package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
- package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
- package/src/__tests__/lod-selector.profile.test.ts +35 -0
- package/src/__tests__/lod-selector.unit.test.ts +68 -0
- package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
- package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
- package/src/__tests__/ltc-resources.unit.test.ts +32 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +17 -0
- package/src/__tests__/material-observation.integration.test.ts +101 -0
- package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
- package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
- package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
- package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
- package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
- package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
- package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
- package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
- package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
- package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
- package/src/__tests__/occlusion-query.evidence.ts +178 -0
- package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
- package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
- package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
- package/src/__tests__/probe-bindings.unit.test.ts +200 -0
- package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
- package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
- package/src/__tests__/probe-capacity.integration.test.ts +97 -0
- package/src/__tests__/probe-recovery.integration.test.ts +104 -0
- package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
- package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
- package/src/__tests__/profiler-phase-token.test.ts +1 -0
- package/src/__tests__/public-surface.test-d.ts +10 -0
- package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
- package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
- package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
- package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
- package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
- package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
- package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
- package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
- package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
- package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
- package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
- package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
- package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
- package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +19 -3
- package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
- package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
- package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
- package/src/__tests__/render-target-admission.unit.test.ts +80 -0
- package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
- package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
- package/src/__tests__/render-target-material-source.test-d.ts +30 -0
- package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
- package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
- package/src/__tests__/render-target-owner.unit.test.ts +122 -0
- package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
- package/src/__tests__/render-target-public-schema.json +107 -0
- package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
- package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
- package/src/__tests__/render-target-readback.integration.test.ts +90 -0
- package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +124 -0
- package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
- package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
- package/src/__tests__/skin-motion-regression.unit.test.ts +154 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +13 -4
- package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
- package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
- package/src/__tests__/ssr-composition.unit.test.ts +46 -0
- package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
- package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
- package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
- package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +299 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +15 -5
- package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
- package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
- package/src/__tests__/standard-lighting-write.unit.test.ts +441 -0
- package/src/__tests__/standard-lighting.integration.test.ts +67 -0
- package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
- package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
- package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
- package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -38
- package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
- package/src/__tests__/temporal-view.unit.test.ts +3 -3
- package/src/__tests__/transmission-prewarm.unit.test.ts +69 -0
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
- package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
- package/src/__tests__/visibility-budget.integration.test.ts +140 -0
- package/src/__tests__/visibility-budget.unit.test.ts +70 -0
- package/src/__tests__/visibility-extract.integration.test.ts +11 -6
- package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
- package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
- package/src/assembly/backend-contract.ts +13 -0
- package/src/assembly/device-feature-admission.ts +80 -0
- package/src/assembly/directional-shadow-inspection.ts +122 -0
- package/src/assembly/factory.ts +1411 -366
- package/src/assembly/host-contract.ts +35 -0
- package/src/assembly/render-target-host.ts +847 -0
- package/src/assets/asset-decoders.ts +1 -1
- package/src/assets/render-asset-port.ts +7 -1
- package/src/capture/cube-views.ts +69 -0
- package/src/capture/scheduler.ts +265 -0
- package/src/cluster-binner.ts +31 -0
- package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
- package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
- package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
- package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
- package/src/components/camera.ts +4 -0
- package/src/components/cube-camera.ts +52 -0
- package/src/components/directional-light.ts +22 -2
- package/src/components/directional-shadow-filter.ts +57 -0
- package/src/components/index.ts +5 -0
- package/src/components/light-helpers.ts +133 -8
- package/src/components/light-probe.ts +13 -0
- package/src/components/rect-area-light.ts +15 -0
- package/src/components/reflection-probe.ts +49 -0
- package/src/components/scene-instance.ts +6 -0
- package/src/components/spot-light.ts +3 -0
- package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
- package/src/device/gpu-residency.ts +140 -9
- package/src/device/lifecycle-transaction.ts +12 -0
- package/src/environment/frame.ts +6 -24
- package/src/environment/lifecycle.ts +20 -1
- package/src/errors/gpu-driven.ts +92 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/recover.ts +32 -0
- package/src/errors/render.ts +341 -143
- package/src/extract/camera.ts +3 -3
- package/src/extract/directional-shadow-projection.ts +61 -0
- package/src/extract/environment.ts +57 -0
- package/src/extract/gpu-driven.ts +108 -0
- package/src/extract/visibility.ts +2 -1
- package/src/features/fullscreen.ts +3 -0
- package/src/features/targets.ts +4 -1
- package/src/features/types.ts +10 -0
- package/src/fullscreen-post-process-pass.ts +43 -2
- package/src/gpu-driven/batch-topology.ts +207 -28
- package/src/gpu-driven/prepared-draw.ts +208 -0
- package/src/gpu-driven/production-raster.ts +953 -43
- package/src/gpu-driven/view-gpu.ts +411 -29
- package/src/gpu-scene-schema.ts +13 -0
- package/src/gpu-scene.ts +140 -64
- package/src/gpu-usage.ts +0 -1
- package/src/hdrp-buffers.ts +169 -63
- package/src/ibl/IblPipelineCache.ts +11 -0
- package/src/ibl/kernel-cache.ts +88 -0
- package/src/index.ts +222 -0
- package/src/inspection-types.ts +276 -1
- package/src/instances-derived-bounds.ts +171 -0
- package/src/internal.ts +38 -0
- package/src/light-buffer-layout.ts +155 -310
- package/src/materials.ts +16 -2
- package/src/mesh-material-bindings.ts +132 -0
- package/src/pbr-pipeline.ts +255 -60
- package/src/pipeline/standard-forward-lane.ts +93 -16
- package/src/pipeline/standard-lighting/errors.ts +6 -0
- package/src/pipeline/standard-lighting/graph.ts +158 -0
- package/src/pipeline/standard-lighting/inspection.ts +88 -0
- package/src/pipeline/standard-lighting/layout.ts +31 -0
- package/src/pipeline/standard-lighting/light-frame.ts +35 -0
- package/src/pipeline/standard-lighting/membership.ts +43 -0
- package/src/pipeline/standard-lighting/prepare.ts +37 -0
- package/src/pipeline/standard-lighting/topology.ts +120 -0
- package/src/pipeline/standard-lighting/transport.ts +78 -0
- package/src/pipeline/standard-pipeline.ts +124 -193
- package/src/pipeline/standard-post.ts +26 -7
- package/src/pipeline/standard-profile.ts +17 -47
- package/src/pipeline-builder.ts +5 -2
- package/src/pipeline-spec.ts +95 -21
- package/src/plugin.ts +4 -0
- package/src/point-shadow-inspection.ts +55 -0
- package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
- package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
- package/src/prepare/extended-lighting/resources.ts +389 -0
- package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
- package/src/prepare/extended-lighting/state.ts +92 -0
- package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
- package/src/record/__tests__/temporal-payload.unit.test.ts +64 -1
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
- package/src/record/frame-lighting.ts +591 -452
- package/src/record/frame-snapshot.ts +92 -39
- package/src/record/frame-targets.ts +17 -0
- package/src/record/frame.ts +662 -158
- package/src/record/gpu-pass-timing/contract.ts +180 -0
- package/src/record/gpu-pass-timing/errors.ts +34 -0
- package/src/record/gpu-pass-timing/index.ts +37 -0
- package/src/record/gpu-pass-timing/parser.ts +97 -0
- package/src/record/gpu-pass-timing/session.ts +938 -0
- package/src/record/gpu-timing.ts +112 -39
- package/src/record/helpers.ts +0 -58
- package/src/record/main-pass-geometry.ts +324 -119
- package/src/record/main-pass-material.ts +103 -10
- package/src/record/main-pass-sprite-draws.ts +99 -103
- package/src/record/main-pass.ts +71 -15
- package/src/record/mesh-ssbo.ts +47 -36
- package/src/record/render-context.ts +136 -3
- package/src/record/shadow-pass.ts +83 -68
- package/src/record/skybox-post-pass.ts +20 -4
- package/src/record/typed-frame-graph.ts +1034 -43
- package/src/record/view-ubo.ts +72 -47
- package/src/reflection/filter.ts +81 -0
- package/src/reflection/gpu-table.ts +44 -0
- package/src/reflection/inspection.ts +67 -0
- package/src/reflection/projection.ts +476 -0
- package/src/reflection/record-owner.ts +1283 -0
- package/src/render-contract.ts +93 -10
- package/src/render-graph-primitives.ts +9 -4
- package/src/render-pipeline.ts +122 -2
- package/src/render-system-extract.ts +1041 -225
- package/src/render-system.ts +1310 -85
- package/src/renderer.ts +222 -0
- package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
- package/src/scene/probe-blend-record.ts +54 -0
- package/src/scene/probe-blend.ts +813 -0
- package/src/scene/render-scene-types.ts +0 -7
- package/src/scene/render-scene.ts +942 -171
- package/src/scene/visibility/bounds.ts +57 -0
- package/src/scene/visibility/budget.ts +40 -0
- package/src/scene/visibility/facet.ts +400 -0
- package/src/scene/visibility/gpu-frame-timing.ts +101 -0
- package/src/scene/visibility/gpu-lod.ts +119 -0
- package/src/scene/visibility/inspection.ts +313 -0
- package/src/scene/visibility/lod-selector.ts +84 -0
- package/src/scene/visibility/occlusion-confidence.ts +478 -0
- package/src/scene/visibility/occlusion-pass.ts +165 -0
- package/src/scene/visibility/occlusion-query-pool.ts +178 -0
- package/src/scene/visibility/occlusion-runtime.ts +672 -0
- package/src/scene/visibility/types.ts +50 -0
- package/src/shadow-atlas.ts +5 -5
- package/src/ssao-buffers.ts +9 -2
- package/src/ssr/admission.ts +451 -0
- package/src/ssr/composition.ts +73 -0
- package/src/ssr/errors.ts +76 -0
- package/src/ssr/identity.ts +12 -0
- package/src/systems/skin-palette-allocator.ts +1 -1
- package/src/targets/contracts.ts +182 -0
- package/src/targets/graph-projection.ts +169 -0
- package/src/targets/material-source.ts +143 -0
- package/src/targets/owner.ts +214 -0
- package/src/targets/physical.ts +18 -0
- package/src/targets/readback.ts +153 -0
- package/src/temporal/frame-coordinator.ts +26 -0
- package/src/temporal/frame.ts +14 -1
- package/src/temporal/view.ts +11 -0
- package/src/tilemap-chunk-extract-system.ts +4 -4
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
- package/src/typed-render-graph-primitives.ts +53 -3
- package/src/typed-shadow-passes.ts +46 -40
- package/src/volume/passes.ts +61 -58
- package/dist/.tsbuildinfo +0 -1
- package/dist/chunk-4XINDLAY.mjs.map +0 -1
- package/dist/chunk-CDTWTHZR.mjs.map +0 -1
- package/dist/chunk-M2MP4ZOU.mjs +0 -73
- package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
- package/dist/chunk-PCHBPYML.mjs.map +0 -1
- package/dist/chunk-TBQJOK2L.mjs.map +0 -1
- package/dist/chunk-VFPLTKCU.mjs.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// @forgeax/engine-render - Directional CSM projection owner.
|
|
2
|
+
|
|
3
|
+
import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
|
|
4
|
+
import { directionalShadowQualityFromF32 } from '../components/directional-shadow-filter';
|
|
5
|
+
|
|
6
|
+
/** Light-space bounds produced by one existing orthographic CSM fit. */
|
|
7
|
+
export interface DirectionalShadowCascadeFit {
|
|
8
|
+
readonly split: number;
|
|
9
|
+
readonly minX: number;
|
|
10
|
+
readonly maxX: number;
|
|
11
|
+
readonly minY: number;
|
|
12
|
+
readonly maxY: number;
|
|
13
|
+
readonly minZ: number;
|
|
14
|
+
readonly maxZ: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Host projection sent to the shared View UBO. */
|
|
18
|
+
export interface DirectionalShadowProjection {
|
|
19
|
+
readonly splitPlanes: Float32Array;
|
|
20
|
+
readonly directionalShadowQuality: DirectionalShadowQuality;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Project accepted Directional author facts and the existing CSM fit into the
|
|
25
|
+
* shared split vec4 lanes. `.x` is the split, `.y` is world units per texel,
|
|
26
|
+
* `.z` is the light-space depth span, and `.w` is reserved for the ABI.
|
|
27
|
+
*/
|
|
28
|
+
export function projectDirectionalShadow(
|
|
29
|
+
fits: readonly DirectionalShadowCascadeFit[],
|
|
30
|
+
mapSize: number,
|
|
31
|
+
shadowFilter: number,
|
|
32
|
+
shadowAngularRadius: number,
|
|
33
|
+
maxPenumbraTexels: number,
|
|
34
|
+
): DirectionalShadowProjection | undefined {
|
|
35
|
+
if (!Number.isFinite(mapSize) || mapSize < 1) return undefined;
|
|
36
|
+
const directionalShadowQuality = directionalShadowQualityFromF32(
|
|
37
|
+
shadowFilter,
|
|
38
|
+
shadowAngularRadius,
|
|
39
|
+
maxPenumbraTexels,
|
|
40
|
+
);
|
|
41
|
+
if (directionalShadowQuality === undefined) return undefined;
|
|
42
|
+
|
|
43
|
+
const splitPlanes = new Float32Array(16);
|
|
44
|
+
for (let index = 0; index < fits.length && index < 4; index += 1) {
|
|
45
|
+
const fit = fits[index];
|
|
46
|
+
if (fit === undefined) continue;
|
|
47
|
+
const width = Math.abs(fit.maxX - fit.minX);
|
|
48
|
+
const height = Math.abs(fit.maxY - fit.minY);
|
|
49
|
+
const depth = Math.abs(fit.maxZ - fit.minZ);
|
|
50
|
+
const worldUnitsPerTexel = Math.max(width, height, Number.EPSILON) / mapSize;
|
|
51
|
+
const lightDepthWorldSpan = Math.max(depth, Number.EPSILON);
|
|
52
|
+
if (!Number.isFinite(worldUnitsPerTexel) || !Number.isFinite(lightDepthWorldSpan)) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const lane = index * 4;
|
|
56
|
+
splitPlanes[lane] = fit.split;
|
|
57
|
+
splitPlanes[lane + 1] = worldUnitsPerTexel;
|
|
58
|
+
splitPlanes[lane + 2] = lightDepthWorldSpan;
|
|
59
|
+
}
|
|
60
|
+
return { splitPlanes, directionalShadowQuality };
|
|
61
|
+
}
|
|
@@ -76,6 +76,20 @@ export type EnvironmentSource =
|
|
|
76
76
|
|
|
77
77
|
export interface FogFrame extends FogCandidate {}
|
|
78
78
|
|
|
79
|
+
export interface FogParameterIssue {
|
|
80
|
+
readonly field: string;
|
|
81
|
+
readonly value: number;
|
|
82
|
+
readonly expected: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Structured failure retained when an invalid Fog update falls back to LKG. */
|
|
86
|
+
export interface FogSelectionFailure {
|
|
87
|
+
readonly code: string;
|
|
88
|
+
readonly expected: string;
|
|
89
|
+
readonly hint: string;
|
|
90
|
+
readonly detail: Readonly<Record<string, unknown>>;
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
export interface EnvironmentFrame {
|
|
80
94
|
readonly source: EnvironmentSource;
|
|
81
95
|
/** Descriptor of the real environment resource adopted for this frame. */
|
|
@@ -103,6 +117,49 @@ function freezeFog(candidate: FogCandidate): FogFrame {
|
|
|
103
117
|
});
|
|
104
118
|
}
|
|
105
119
|
|
|
120
|
+
/** Validate the bounded Fog domain once for every frame-selection producer. */
|
|
121
|
+
export function validateFogParameters(candidate: FogCandidate): FogParameterIssue | undefined {
|
|
122
|
+
if (candidate.color.length !== 3) {
|
|
123
|
+
return {
|
|
124
|
+
field: 'color',
|
|
125
|
+
value: candidate.color.length,
|
|
126
|
+
expected: 'exactly three finite channels in [0, 1]',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
for (let index = 0; index < candidate.color.length; index += 1) {
|
|
130
|
+
const value = candidate.color[index];
|
|
131
|
+
if (value === undefined || !Number.isFinite(value) || value < 0 || value > 1) {
|
|
132
|
+
return {
|
|
133
|
+
field: `color[${index}]`,
|
|
134
|
+
value: value ?? Number.NaN,
|
|
135
|
+
expected: 'a finite number in [0, 1]',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!Number.isFinite(candidate.density) || candidate.density < 0) {
|
|
140
|
+
return { field: 'density', value: candidate.density, expected: 'a finite number >= 0' };
|
|
141
|
+
}
|
|
142
|
+
if (!Number.isFinite(candidate.heightFalloff) || candidate.heightFalloff < 0) {
|
|
143
|
+
return {
|
|
144
|
+
field: 'heightFalloff',
|
|
145
|
+
value: candidate.heightFalloff,
|
|
146
|
+
expected: 'a finite number >= 0',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
!Number.isFinite(candidate.maxOpacity) ||
|
|
151
|
+
candidate.maxOpacity < 0 ||
|
|
152
|
+
candidate.maxOpacity > 1
|
|
153
|
+
) {
|
|
154
|
+
return {
|
|
155
|
+
field: 'maxOpacity',
|
|
156
|
+
value: candidate.maxOpacity,
|
|
157
|
+
expected: 'a finite number in [0, 1]',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
106
163
|
function freezeAtmosphere(parameters: AtmosphereParameters): AtmosphereParameters {
|
|
107
164
|
return Object.freeze({ ...parameters });
|
|
108
165
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { MaterialShaderArtifact } from '@forgeax/engine-shader';
|
|
2
|
+
import type { MeshAsset } from '@forgeax/engine-types';
|
|
3
|
+
import {
|
|
4
|
+
type GpuDrivenDrawRange,
|
|
5
|
+
type PreparedGpuDrivenDraw,
|
|
6
|
+
type PrepareGpuDrivenDrawInput,
|
|
7
|
+
prepareGpuDrivenDraw,
|
|
8
|
+
} from '../gpu-driven/prepared-draw';
|
|
9
|
+
import type { MaterialSnapshot } from '../render-system-extract';
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
GpuDrivenPreparationError,
|
|
13
|
+
type GpuDrivenPreparationErrorCode,
|
|
14
|
+
type GpuDrivenPreparationErrorDetail,
|
|
15
|
+
type GpuDrivenPreparationReason,
|
|
16
|
+
} from '../errors/gpu-driven';
|
|
17
|
+
export {
|
|
18
|
+
type GpuDrivenDrawRange,
|
|
19
|
+
type GpuDrivenGeometryReceipt,
|
|
20
|
+
type GpuDrivenSkinReceipt,
|
|
21
|
+
type PreparedGpuDrivenDraw,
|
|
22
|
+
type PrepareGpuDrivenDrawInput,
|
|
23
|
+
prepareGpuDrivenDraw,
|
|
24
|
+
} from '../gpu-driven/prepared-draw';
|
|
25
|
+
|
|
26
|
+
export interface GpuDrivenDrawSnapshot {
|
|
27
|
+
readonly kind: GpuDrivenDrawRange['kind'];
|
|
28
|
+
readonly first: GpuDrivenDrawRange['first'];
|
|
29
|
+
readonly count: GpuDrivenDrawRange['count'];
|
|
30
|
+
readonly baseVertex: GpuDrivenDrawRange['baseVertex'];
|
|
31
|
+
readonly materialSlot: number;
|
|
32
|
+
readonly topology: GpuDrivenDrawRange['topology'];
|
|
33
|
+
readonly pipelineClass: string;
|
|
34
|
+
readonly materialResourceClass: string;
|
|
35
|
+
/** Producer-owned prepared ABI facts used by the PBR GPU lane. */
|
|
36
|
+
readonly prepared?: PreparedGpuDrivenDraw;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolves prepared facts once at extract time; record never re-reads assets.
|
|
41
|
+
* The resolver surfaces UV, reflection, vertex, alpha, resource, and
|
|
42
|
+
* generation failures as GpuDrivenPreparationError before recording.
|
|
43
|
+
*/
|
|
44
|
+
export function resolvePreparedGpuDrivenDraw(input: PrepareGpuDrivenDrawInput):
|
|
45
|
+
| { readonly ok: true; readonly value: PreparedGpuDrivenDraw }
|
|
46
|
+
| {
|
|
47
|
+
readonly ok: false;
|
|
48
|
+
readonly error: import('../errors/gpu-driven').GpuDrivenPreparationError;
|
|
49
|
+
} {
|
|
50
|
+
return prepareGpuDrivenDraw(input);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Keeps the artifact type visible at this owner boundary for contract checks. */
|
|
54
|
+
export type PreparedGpuDrivenArtifact = MaterialShaderArtifact;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Derives the resource identity consumed by the existing GPU-driven batch
|
|
58
|
+
* owner. The extract stage is the only place that resolves material values;
|
|
59
|
+
* this helper keeps the batch adapter from re-reading assets later.
|
|
60
|
+
*/
|
|
61
|
+
export function gpuDrivenMaterialResourceClass(material: MaterialSnapshot): string {
|
|
62
|
+
const textures = [...(material.textureHandles?.entries() ?? [])]
|
|
63
|
+
.map(([name, handle]) => [name, Number(handle)] as const)
|
|
64
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
65
|
+
const samplers = [...(material.samplerHandles?.entries() ?? [])]
|
|
66
|
+
.map(([name, handle]) => [name, Number(handle)] as const)
|
|
67
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
68
|
+
return JSON.stringify({
|
|
69
|
+
textures,
|
|
70
|
+
samplers,
|
|
71
|
+
video: [...(material.videoTextureFields?.keys() ?? [])].sort(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Projects already-resolved mesh facts into the existing GPU-driven draw
|
|
77
|
+
* snapshot. No World or AssetRegistry access is permitted at this boundary.
|
|
78
|
+
*/
|
|
79
|
+
export function buildGpuDrivenDraws(input: {
|
|
80
|
+
readonly submeshes: readonly MeshAsset['submeshes'][number][];
|
|
81
|
+
readonly indexed: boolean;
|
|
82
|
+
readonly materials: readonly MaterialSnapshot[];
|
|
83
|
+
readonly fallbackMaterial: MaterialSnapshot;
|
|
84
|
+
readonly prepare?: (
|
|
85
|
+
draw: GpuDrivenDrawSnapshot,
|
|
86
|
+
material: MaterialSnapshot,
|
|
87
|
+
) => PreparedGpuDrivenDraw | undefined;
|
|
88
|
+
}): GpuDrivenDrawSnapshot[] {
|
|
89
|
+
let nonIndexedFirst = 0;
|
|
90
|
+
return input.submeshes.flatMap((submesh) => {
|
|
91
|
+
const drawMaterial = input.materials[submesh.materialSlot] ?? input.fallbackMaterial;
|
|
92
|
+
const first = input.indexed ? submesh.indexOffset : nonIndexedFirst;
|
|
93
|
+
nonIndexedFirst += submesh.vertexCount;
|
|
94
|
+
if (drawMaterial.transparent === true) return [];
|
|
95
|
+
const draw: GpuDrivenDrawSnapshot = {
|
|
96
|
+
kind: input.indexed ? ('indexed' as const) : ('non-indexed' as const),
|
|
97
|
+
first,
|
|
98
|
+
count: input.indexed ? submesh.indexCount : submesh.vertexCount,
|
|
99
|
+
baseVertex: 0,
|
|
100
|
+
materialSlot: submesh.materialSlot,
|
|
101
|
+
topology: submesh.topology,
|
|
102
|
+
pipelineClass: `${drawMaterial.materialShaderId ?? 'forgeax::default-unlit'}|${submesh.topology}|${JSON.stringify(drawMaterial.renderState ?? null)}`,
|
|
103
|
+
materialResourceClass: gpuDrivenMaterialResourceClass(drawMaterial),
|
|
104
|
+
};
|
|
105
|
+
const prepared = input.prepare?.(draw, drawMaterial);
|
|
106
|
+
return [prepared === undefined ? draw : { ...draw, prepared }];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -123,7 +123,7 @@ export function resolveVisibility(
|
|
|
123
123
|
};
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
const snapshot: VisibilitySnapshot = {
|
|
127
127
|
diagnostics: hierarchy.diagnostics,
|
|
128
128
|
hasAnyIntent: intentCount > 0,
|
|
129
129
|
hasAnyHiddenIntent,
|
|
@@ -136,4 +136,5 @@ export function resolveVisibility(
|
|
|
136
136
|
return resolveEntity?.(entity)?.effective ?? 'visible';
|
|
137
137
|
},
|
|
138
138
|
};
|
|
139
|
+
return snapshot;
|
|
139
140
|
}
|
|
@@ -28,6 +28,9 @@ export function createFullscreenRenderFeature(
|
|
|
28
28
|
const resourceName = `fullscreen.${options.identity.replace(/[^a-z0-9.-]/gi, '-').toLowerCase()}`;
|
|
29
29
|
return Object.freeze({
|
|
30
30
|
identity: options.identity,
|
|
31
|
+
requiredFullscreenPostProcesses: Object.freeze([
|
|
32
|
+
Object.freeze({ identity: options.identity, source: options.source }),
|
|
33
|
+
]),
|
|
31
34
|
extract: (_context: RenderFeatureExtractContext) => ok(undefined),
|
|
32
35
|
plan: (_data: undefined, _context: RenderFeaturePlanContext) =>
|
|
33
36
|
ok({
|
package/src/features/targets.ts
CHANGED
|
@@ -57,8 +57,11 @@ export function resolveStandardRenderFeatureTargets(input: {
|
|
|
57
57
|
readonly colorAttachmentFormat: TextureFormat;
|
|
58
58
|
}): readonly RenderFeatureTargetHandle[] {
|
|
59
59
|
const sampleCount: 1 | 4 = input.antialias === 'msaa' && input.multisample ? 4 : 1;
|
|
60
|
+
// Standard always exposes the scene to features as a linear float target;
|
|
61
|
+
// the shared Output Transform owns the eventual RGBA8 write for every
|
|
62
|
+
// antialias mode, including no-AA/WebGL2.
|
|
60
63
|
const linearLdr =
|
|
61
|
-
|
|
64
|
+
input.tonemap === 'none' ||
|
|
62
65
|
input.antialias === 'fxaa' ||
|
|
63
66
|
(input.antialias === 'msaa' && input.multisample);
|
|
64
67
|
const colorFormat =
|
package/src/features/types.ts
CHANGED
|
@@ -98,6 +98,16 @@ export interface RenderFeature<FrameData> {
|
|
|
98
98
|
* the same lazy module cache used by prepared graphics.
|
|
99
99
|
*/
|
|
100
100
|
readonly requiredMaterialShaders?: readonly string[];
|
|
101
|
+
/**
|
|
102
|
+
* Fullscreen program sources that must be module-ready before the first
|
|
103
|
+
* frame. The renderer seeds the same lazy post-process module cache used by
|
|
104
|
+
* the typed graph, so a synchronous frame driver cannot observe a cleared
|
|
105
|
+
* target while a boot-installed effect is compiling.
|
|
106
|
+
*/
|
|
107
|
+
readonly requiredFullscreenPostProcesses?: readonly {
|
|
108
|
+
readonly identity: string;
|
|
109
|
+
readonly source: string;
|
|
110
|
+
}[];
|
|
101
111
|
extract(context: RenderFeatureExtractContext): Result<FrameData, RenderError>;
|
|
102
112
|
plan(data: FrameData, context: RenderFeaturePlanContext): Result<RenderFeaturePlan, RenderError>;
|
|
103
113
|
}
|
|
@@ -67,7 +67,8 @@ export type PostProcessReadSampleType = 'depth';
|
|
|
67
67
|
* no paramSchema (params are passed inline via a {type, value} struct — plan-strategy D-4).
|
|
68
68
|
*
|
|
69
69
|
* `params` carries the WGSL struct definition + default value. When `params` is
|
|
70
|
-
* `undefined`, the post-process shader has no UBO
|
|
70
|
+
* `undefined`, the post-process shader has no params UBO. Special graph-owned
|
|
71
|
+
* passes such as FXAA may still register an internal policy UBO separately.
|
|
71
72
|
*
|
|
72
73
|
* `reads` declares graph resource keys this pass samples as input texture(s).
|
|
73
74
|
* If omitted, the pass samples the swap-chain color attachment (default path).
|
|
@@ -81,7 +82,7 @@ export interface PostProcessShaderEntry {
|
|
|
81
82
|
/** Composed WGSL source for the fragment stage (post-naga_oil). */
|
|
82
83
|
readonly source: string;
|
|
83
84
|
/**
|
|
84
|
-
* Params UBO schema. When undefined, the shader has no uniform buffer
|
|
85
|
+
* Params UBO schema. When undefined, the generic shader has no uniform buffer
|
|
85
86
|
* and the BGL degrades to 2 entries (texture@0 + sampler@1). When present, the
|
|
86
87
|
* primitive eager-creates a params UBO (at register, sized `byteSize`) and binds
|
|
87
88
|
* it at bindgroup(1) binding(2) as part of a 3-entry BGL
|
|
@@ -104,6 +105,46 @@ export interface PostProcessShaderEntry {
|
|
|
104
105
|
readonly reads?: readonly (string | PostProcessReadEntry)[] | undefined;
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
function postProcessSourceDigest(source: string): string {
|
|
109
|
+
let first = 0x811c9dc5;
|
|
110
|
+
let second = 0x9e3779b9;
|
|
111
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
112
|
+
const code = source.charCodeAt(index);
|
|
113
|
+
first = Math.imul(first ^ code, 0x01000193);
|
|
114
|
+
second = Math.imul(second ^ (code + index), 0x01000193);
|
|
115
|
+
}
|
|
116
|
+
return `${source.length.toString(16)}-${(first >>> 0).toString(16).padStart(8, '0')}-${(second >>> 0).toString(16).padStart(8, '0')}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Content-addressed PSO label shared by the post-process pipeline factory. */
|
|
120
|
+
export function postProcessShaderPipelineLabel(identity: string, source: string): string {
|
|
121
|
+
return `post-process-${identity}-pso-${postProcessSourceDigest(source)}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Content-addressed shader-module label shared by prewarm and lazy paths. */
|
|
125
|
+
export function postProcessShaderModuleLabel(identity: string, source: string): string {
|
|
126
|
+
return `${postProcessShaderPipelineLabel(identity, source)}-module`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Stable declaration identity used by graph admission and the renderer's
|
|
131
|
+
* device-bound pipeline cache. Shader source, bind-group-affecting params,
|
|
132
|
+
* and graph reads must move together when a feature reuses its identity.
|
|
133
|
+
*/
|
|
134
|
+
export function postProcessShaderEntrySignature(entry: PostProcessShaderEntry): string {
|
|
135
|
+
return JSON.stringify({
|
|
136
|
+
source: entry.source,
|
|
137
|
+
params:
|
|
138
|
+
entry.params === undefined
|
|
139
|
+
? undefined
|
|
140
|
+
: {
|
|
141
|
+
byteSize: entry.params.byteSize,
|
|
142
|
+
defaultValue: Array.from(entry.params.defaultValue),
|
|
143
|
+
},
|
|
144
|
+
reads: entry.reads,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
107
148
|
/** Whether a feature binding opts into the temporal multi-read contract. */
|
|
108
149
|
export function isTemporalFullscreenBinding(values: Readonly<Record<string, unknown>>): boolean {
|
|
109
150
|
return (
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { PrimitiveTopology } from '@forgeax/engine-types';
|
|
2
2
|
import type { BatchTopologyInspection } from '../inspection-types';
|
|
3
|
+
import type { CameraSnapshot } from '../render-contract';
|
|
3
4
|
import type { RenderSceneApplyResult, RenderSceneSlot } from '../scene/render-scene-types';
|
|
5
|
+
import { type GpuLodRow, selectGpuLod } from '../scene/visibility/gpu-lod';
|
|
6
|
+
import { projectedHeight as measureProjectedHeight } from '../scene/visibility/lod-selector';
|
|
7
|
+
import type { PreparedGpuDrivenDraw } from './prepared-draw';
|
|
4
8
|
|
|
5
9
|
export type { BatchTopologyInspection } from '../inspection-types';
|
|
6
10
|
|
|
@@ -14,6 +18,12 @@ export interface GpuDrivenBatchKey {
|
|
|
14
18
|
readonly topology: PrimitiveTopology;
|
|
15
19
|
readonly pipelineClass: string;
|
|
16
20
|
readonly materialResourceClass: string;
|
|
21
|
+
/** Stable producer-owned identity; topology never derives this from a shader name. */
|
|
22
|
+
readonly preparedIdentity?: string;
|
|
23
|
+
/** Resource identity projected by the prepared material contract. */
|
|
24
|
+
readonly resourceIdentity?: string;
|
|
25
|
+
/** Closed admission lane for the prepared Standard PBR draw. */
|
|
26
|
+
readonly admission?: 'opaque' | 'alpha-mask';
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
export interface GpuDrivenCandidate {
|
|
@@ -21,6 +31,16 @@ export interface GpuDrivenCandidate {
|
|
|
21
31
|
readonly generation: number;
|
|
22
32
|
readonly drawItemIndex: number;
|
|
23
33
|
readonly instanceOrdinal: number;
|
|
34
|
+
/** Absolute coverage rows uploaded to the GPU selector; root is implicit. */
|
|
35
|
+
readonly lodCoverages?: readonly number[];
|
|
36
|
+
readonly lodHysteresis?: number;
|
|
37
|
+
readonly projectedHeight?: number;
|
|
38
|
+
readonly lodRanges?: readonly {
|
|
39
|
+
readonly first: number;
|
|
40
|
+
readonly count: number;
|
|
41
|
+
readonly baseVertex: number;
|
|
42
|
+
}[];
|
|
43
|
+
readonly prepared?: PreparedGpuDrivenDraw;
|
|
24
44
|
}
|
|
25
45
|
|
|
26
46
|
export interface GpuDrivenBatch {
|
|
@@ -40,6 +60,32 @@ export interface SubmissionPlan {
|
|
|
40
60
|
readonly visibleCapacity: number;
|
|
41
61
|
}
|
|
42
62
|
|
|
63
|
+
export interface GpuLodCandidateInput {
|
|
64
|
+
readonly candidate: GpuDrivenCandidate;
|
|
65
|
+
readonly rows: readonly GpuLodRow[];
|
|
66
|
+
readonly projectedHeight: number;
|
|
67
|
+
readonly previousLevel: number;
|
|
68
|
+
readonly historyValid: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface GpuLodCompactCandidate {
|
|
72
|
+
readonly candidate: GpuDrivenCandidate;
|
|
73
|
+
readonly level: number;
|
|
74
|
+
readonly confidence: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Selects one level per stable topology candidate without mutating membership. */
|
|
78
|
+
export function compactGpuLodCandidates(
|
|
79
|
+
inputs: readonly GpuLodCandidateInput[],
|
|
80
|
+
): readonly GpuLodCompactCandidate[] {
|
|
81
|
+
return Object.freeze(
|
|
82
|
+
inputs.map((input) => {
|
|
83
|
+
const selection = selectGpuLod(input.rows, input);
|
|
84
|
+
return Object.freeze({ candidate: input.candidate, ...selection });
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
43
89
|
interface MutableBatch {
|
|
44
90
|
readonly batchId: number;
|
|
45
91
|
readonly generation: number;
|
|
@@ -53,29 +99,100 @@ function eligibleKeys(slot: RenderSceneSlot): readonly GpuDrivenBatchKey[] {
|
|
|
53
99
|
if (
|
|
54
100
|
draws === undefined ||
|
|
55
101
|
draws.length === 0 ||
|
|
102
|
+
snapshot.instances?.instanceCount === 0 ||
|
|
56
103
|
snapshot.localAabb === undefined ||
|
|
57
104
|
snapshot.skin !== undefined ||
|
|
105
|
+
snapshot.morph !== undefined ||
|
|
58
106
|
snapshot.spriteInstances !== undefined
|
|
59
107
|
) {
|
|
60
108
|
return [];
|
|
61
109
|
}
|
|
62
|
-
return draws.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
110
|
+
return draws.flatMap((draw) => {
|
|
111
|
+
const prepared = draw.prepared;
|
|
112
|
+
const material = snapshot.materials[draw.materialSlot] ?? snapshot.material;
|
|
113
|
+
if (prepared !== undefined) {
|
|
114
|
+
const isStandardPbr =
|
|
115
|
+
(material.materialShaderId === 'forgeax::default-standard-pbr' &&
|
|
116
|
+
prepared.identity.material === 'forgeax::default-standard-pbr') ||
|
|
117
|
+
(material.materialShaderId === 'forgeax::default-standard-pbr-skin' &&
|
|
118
|
+
prepared.identity.material === 'forgeax::default-standard-pbr-skin');
|
|
119
|
+
const isAlphaBlend =
|
|
120
|
+
material.transparent === true || material.renderState?.blend !== undefined;
|
|
121
|
+
if (!isStandardPbr || isAlphaBlend || prepared.identity.deformation !== 'rigid') return [];
|
|
122
|
+
const preparedIdentity = `${prepared.identity.material}|${prepared.identity.geometry}|${prepared.identity.deformation}`;
|
|
123
|
+
const admission =
|
|
124
|
+
typeof material.paramSnapshot?.alphaCutoff === 'number' ? 'alpha-mask' : 'opaque';
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
assetHandle: snapshot.assetHandle,
|
|
128
|
+
drawKind: draw.kind,
|
|
129
|
+
first: prepared.first,
|
|
130
|
+
count: prepared.count,
|
|
131
|
+
baseVertex: prepared.baseVertex,
|
|
132
|
+
materialSlot: draw.materialSlot,
|
|
133
|
+
topology: prepared.topology,
|
|
134
|
+
pipelineClass: preparedIdentity,
|
|
135
|
+
materialResourceClass: draw.materialResourceClass,
|
|
136
|
+
preparedIdentity,
|
|
137
|
+
resourceIdentity: draw.materialResourceClass,
|
|
138
|
+
admission,
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
return [
|
|
143
|
+
{
|
|
144
|
+
assetHandle: snapshot.assetHandle,
|
|
145
|
+
drawKind: draw.kind,
|
|
146
|
+
first: draw.first,
|
|
147
|
+
count: draw.count,
|
|
148
|
+
baseVertex: draw.baseVertex,
|
|
149
|
+
materialSlot: draw.materialSlot,
|
|
150
|
+
topology: draw.topology,
|
|
151
|
+
pipelineClass: draw.pipelineClass,
|
|
152
|
+
materialResourceClass: draw.materialResourceClass,
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
});
|
|
73
156
|
}
|
|
74
157
|
|
|
75
158
|
function keyText(key: GpuDrivenBatchKey): string {
|
|
76
159
|
return JSON.stringify(key);
|
|
77
160
|
}
|
|
78
161
|
|
|
162
|
+
/**
|
|
163
|
+
* A GPU indirect draw has one index/vertex range for the whole batch. LOD
|
|
164
|
+
* selection is per candidate, so sharing a batch between LOD candidates would
|
|
165
|
+
* let `finalizeView` pick the first candidate's range for every visible
|
|
166
|
+
* instance. Keep LOD candidates in single-candidate batches; ordinary meshes
|
|
167
|
+
* retain the existing compatibility batching path.
|
|
168
|
+
*/
|
|
169
|
+
function candidateBatchText(
|
|
170
|
+
key: GpuDrivenBatchKey,
|
|
171
|
+
primitiveIndex: number,
|
|
172
|
+
drawItemIndex: number,
|
|
173
|
+
instanceOrdinal: number,
|
|
174
|
+
hasLod: boolean,
|
|
175
|
+
): string {
|
|
176
|
+
return hasLod
|
|
177
|
+
? `${keyText(key)}|lod:${primitiveIndex}:${drawItemIndex}:${instanceOrdinal}`
|
|
178
|
+
: keyText(key);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function membershipTexts(slot: RenderSceneSlot): readonly string[] {
|
|
182
|
+
const keys = eligibleKeys(slot);
|
|
183
|
+
const instanceCount = slot.snapshot.instances?.instanceCount ?? 1;
|
|
184
|
+
const hasLod = (slot.snapshot.lods?.length ?? 0) > 0;
|
|
185
|
+
const texts: string[] = [];
|
|
186
|
+
for (let drawItemIndex = 0; drawItemIndex < keys.length; drawItemIndex += 1) {
|
|
187
|
+
const key = keys[drawItemIndex];
|
|
188
|
+
if (key === undefined) continue;
|
|
189
|
+
for (let instanceOrdinal = 0; instanceOrdinal < instanceCount; instanceOrdinal += 1) {
|
|
190
|
+
texts.push(candidateBatchText(key, slot.slot, drawItemIndex, instanceOrdinal, hasLod));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return texts;
|
|
194
|
+
}
|
|
195
|
+
|
|
79
196
|
function alignInstanceBase(value: number): number {
|
|
80
197
|
// Raster binds each batch's visible-index and compact-transform segment at
|
|
81
198
|
// a static storage-buffer offset. WebGPU requires those offsets to satisfy
|
|
@@ -85,6 +202,42 @@ function alignInstanceBase(value: number): number {
|
|
|
85
202
|
return Math.ceil(value / 64) * 64;
|
|
86
203
|
}
|
|
87
204
|
|
|
205
|
+
export function projectedHeightForCandidate(slot: RenderSceneSlot, camera: CameraSnapshot): number {
|
|
206
|
+
const aabb = slot.snapshot.localAabb;
|
|
207
|
+
if (aabb === undefined || aabb.length < 6) return Number.NaN;
|
|
208
|
+
const halfX = Math.abs((aabb[3] ?? 0) - (aabb[0] ?? 0)) * 0.5;
|
|
209
|
+
const halfY = Math.abs((aabb[4] ?? 0) - (aabb[1] ?? 0)) * 0.5;
|
|
210
|
+
const halfZ = Math.abs((aabb[5] ?? 0) - (aabb[2] ?? 0)) * 0.5;
|
|
211
|
+
const world = slot.snapshot.transform.world;
|
|
212
|
+
// Transform the local AABB's half extents through the absolute value of the
|
|
213
|
+
// world 3x3. This is the conservative world-space sphere bound for every
|
|
214
|
+
// rotation and signed/non-uniform scale, and keeps CPU projection aligned
|
|
215
|
+
// with the world transform uploaded to the GPU selector.
|
|
216
|
+
const worldHalfX =
|
|
217
|
+
Math.abs(world[0] ?? 0) * halfX +
|
|
218
|
+
Math.abs(world[4] ?? 0) * halfY +
|
|
219
|
+
Math.abs(world[8] ?? 0) * halfZ;
|
|
220
|
+
const worldHalfY =
|
|
221
|
+
Math.abs(world[1] ?? 0) * halfX +
|
|
222
|
+
Math.abs(world[5] ?? 0) * halfY +
|
|
223
|
+
Math.abs(world[9] ?? 0) * halfZ;
|
|
224
|
+
const worldHalfZ =
|
|
225
|
+
Math.abs(world[2] ?? 0) * halfX +
|
|
226
|
+
Math.abs(world[6] ?? 0) * halfY +
|
|
227
|
+
Math.abs(world[10] ?? 0) * halfZ;
|
|
228
|
+
const radius = Math.hypot(worldHalfX, worldHalfY, worldHalfZ);
|
|
229
|
+
const dx = (world[12] ?? 0) - (camera.position[0] ?? 0);
|
|
230
|
+
const dy = (world[13] ?? 0) - (camera.position[1] ?? 0);
|
|
231
|
+
const dz = (world[14] ?? 0) - (camera.position[2] ?? 0);
|
|
232
|
+
return measureProjectedHeight({
|
|
233
|
+
radius,
|
|
234
|
+
depth: Math.hypot(dx, dy, dz),
|
|
235
|
+
projection: camera.projection,
|
|
236
|
+
fov: camera.fov,
|
|
237
|
+
orthoHeight: Math.abs(camera.orthoTop - camera.orthoBottom),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
88
241
|
/** Stable compatibility grouping; per-view visibility never mutates this owner. */
|
|
89
242
|
export class BatchTopology {
|
|
90
243
|
private readonly batches = new Map<string, MutableBatch>();
|
|
@@ -119,15 +272,17 @@ export class BatchTopology {
|
|
|
119
272
|
changed = this.remove(slot.slot) || changed;
|
|
120
273
|
changed = this.add(slot) || changed;
|
|
121
274
|
}
|
|
275
|
+
// Most updated slots are transform-only. A full renderable update can
|
|
276
|
+
// still carry a changed material/geometry/draw topology, so compare the
|
|
277
|
+
// stable batch memberships and patch only when that key actually changed.
|
|
278
|
+
// This keeps moving entities on the zero-work transform lane while making
|
|
279
|
+
// same-entity resource edits visible to the GPU-driven owner.
|
|
122
280
|
for (const slot of delta.updatedSlots) {
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
Array.from({ length: instanceCount }, () => keyText(key)),
|
|
126
|
-
);
|
|
127
|
-
const current = this.membershipByPrimitive.get(slot.slot) ?? [];
|
|
281
|
+
const previous = this.membershipByPrimitive.get(slot.slot) ?? [];
|
|
282
|
+
const next = membershipTexts(slot);
|
|
128
283
|
if (
|
|
129
|
-
|
|
130
|
-
|
|
284
|
+
previous.length === next.length &&
|
|
285
|
+
previous.every((membership, index) => membership.batchText === next[index])
|
|
131
286
|
) {
|
|
132
287
|
continue;
|
|
133
288
|
}
|
|
@@ -198,26 +353,50 @@ export class BatchTopology {
|
|
|
198
353
|
this.ineligiblePrimitives.delete(slot.slot);
|
|
199
354
|
const memberships: Array<{ batchText: string; candidateKey: string }> = [];
|
|
200
355
|
const instanceCount = slot.snapshot.instances?.instanceCount ?? 1;
|
|
356
|
+
const hasLod = (slot.snapshot.lods?.length ?? 0) > 0;
|
|
201
357
|
for (let drawItemIndex = 0; drawItemIndex < keys.length; drawItemIndex += 1) {
|
|
202
358
|
const key = keys[drawItemIndex];
|
|
203
359
|
if (key === undefined) continue;
|
|
204
|
-
const text = keyText(key);
|
|
205
|
-
let batch = this.batches.get(text);
|
|
206
|
-
if (batch === undefined) {
|
|
207
|
-
const reused = this.freeBatchIds.pop();
|
|
208
|
-
const batchId = reused ?? this.generationByBatchId.length;
|
|
209
|
-
const generation = reused === undefined ? 0 : (this.generationByBatchId[batchId] ?? -1) + 1;
|
|
210
|
-
this.generationByBatchId[batchId] = generation;
|
|
211
|
-
batch = { batchId, generation, key, candidates: new Map() };
|
|
212
|
-
this.batches.set(text, batch);
|
|
213
|
-
}
|
|
214
360
|
for (let instanceOrdinal = 0; instanceOrdinal < instanceCount; instanceOrdinal += 1) {
|
|
361
|
+
const text = candidateBatchText(key, slot.slot, drawItemIndex, instanceOrdinal, hasLod);
|
|
362
|
+
let batch = this.batches.get(text);
|
|
363
|
+
if (batch === undefined) {
|
|
364
|
+
const reused = this.freeBatchIds.pop();
|
|
365
|
+
const batchId = reused ?? this.generationByBatchId.length;
|
|
366
|
+
const generation =
|
|
367
|
+
reused === undefined ? 0 : (this.generationByBatchId[batchId] ?? -1) + 1;
|
|
368
|
+
this.generationByBatchId[batchId] = generation;
|
|
369
|
+
batch = { batchId, generation, key, candidates: new Map() };
|
|
370
|
+
this.batches.set(text, batch);
|
|
371
|
+
}
|
|
215
372
|
const candidateKey = `${slot.slot}:${drawItemIndex}:${instanceOrdinal}`;
|
|
373
|
+
const draw = slot.snapshot.gpuDrivenDraws?.[drawItemIndex];
|
|
374
|
+
const prepared = draw?.prepared;
|
|
216
375
|
batch.candidates.set(candidateKey, {
|
|
217
376
|
primitiveIndex: slot.slot,
|
|
218
377
|
generation: slot.generation,
|
|
219
378
|
drawItemIndex,
|
|
220
379
|
instanceOrdinal,
|
|
380
|
+
...(draw?.projectedHeight === undefined ? {} : { projectedHeight: draw.projectedHeight }),
|
|
381
|
+
...(slot.snapshot.lods === undefined
|
|
382
|
+
? {}
|
|
383
|
+
: {
|
|
384
|
+
lodCoverages: [1, ...slot.snapshot.lods.map((lod) => lod.screenCoverage)],
|
|
385
|
+
...(slot.snapshot.gpuDrivenDraws?.[drawItemIndex]?.lodRanges === undefined
|
|
386
|
+
? {}
|
|
387
|
+
: {
|
|
388
|
+
lodRanges: slot.snapshot.gpuDrivenDraws[drawItemIndex]
|
|
389
|
+
?.lodRanges as readonly {
|
|
390
|
+
readonly first: number;
|
|
391
|
+
readonly count: number;
|
|
392
|
+
readonly baseVertex: number;
|
|
393
|
+
}[],
|
|
394
|
+
}),
|
|
395
|
+
...(slot.snapshot.lodHysteresis === undefined
|
|
396
|
+
? {}
|
|
397
|
+
: { lodHysteresis: slot.snapshot.lodHysteresis }),
|
|
398
|
+
}),
|
|
399
|
+
...(prepared === undefined ? {} : { prepared }),
|
|
221
400
|
});
|
|
222
401
|
memberships.push({ batchText: text, candidateKey });
|
|
223
402
|
}
|