@forgeax/engine-render 0.1.21 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -38
- package/dist/assembly/backend-contract.d.ts +13 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/bundler-contract.d.ts +9 -0
- package/dist/assembly/bundler-contract.d.ts.map +1 -0
- package/dist/assembly/device-feature-admission.d.ts +8 -0
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
- package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +7 -235
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +10 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +3 -2
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
- package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
- package/dist/assembly/material-shader-policy.d.ts +168 -0
- package/dist/assembly/material-shader-policy.d.ts.map +1 -0
- package/dist/assembly/material-shader-prewarm.d.ts +21 -0
- package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
- package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
- package/dist/assembly/recovery/generation.d.ts +46 -0
- package/dist/assembly/recovery/generation.d.ts.map +1 -0
- package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
- package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts +10 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -0
- package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-helpers.d.ts +19 -0
- package/dist/assembly/renderer-helpers.d.ts.map +1 -0
- package/dist/assembly/renderer-lifecycle.d.ts +34 -0
- package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +55 -0
- package/dist/assembly/webgpu-ready.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +152 -0
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-PCHBPYML.mjs → chunk-GE3SDD72.mjs} +37 -5
- package/dist/chunk-GE3SDD72.mjs.map +1 -0
- package/dist/chunk-KZFTK6DO.mjs +1191 -0
- package/dist/chunk-KZFTK6DO.mjs.map +1 -0
- package/dist/{chunk-FS2KNUQH.mjs → chunk-Q752SHI5.mjs} +332 -568
- package/dist/chunk-Q752SHI5.mjs.map +1 -0
- package/dist/{chunk-6OPXDQ2E.mjs → chunk-RR5L4SRS.mjs} +33 -294
- package/dist/chunk-RR5L4SRS.mjs.map +1 -0
- package/dist/chunk-TFH5I7JJ.mjs +296 -0
- package/dist/chunk-TFH5I7JJ.mjs.map +1 -0
- package/dist/chunk-UQYS7FRM.mjs +1003 -0
- package/dist/chunk-UQYS7FRM.mjs.map +1 -0
- package/dist/chunk-YKB5DQV7.mjs +345 -0
- package/dist/chunk-YKB5DQV7.mjs.map +1 -0
- package/dist/{chunk-5YADUCA7.mjs → chunk-YRUYGA4A.mjs} +1189 -1217
- package/dist/chunk-YRUYGA4A.mjs.map +1 -0
- package/dist/chunk-Z3YYH2MB.mjs +5082 -0
- package/dist/chunk-Z3YYH2MB.mjs.map +1 -0
- package/dist/cluster-binner.d.ts +5 -0
- package/dist/cluster-binner.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -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/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 +27276 -23232
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts +2 -0
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +32 -5
- 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 +5 -0
- package/dist/environment/frame.d.ts.map +1 -1
- package/dist/environment/inspection.d.ts +7 -0
- package/dist/environment/inspection.d.ts.map +1 -1
- package/dist/environment/lifecycle.d.ts +20 -2
- 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 +67 -3
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +39 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +899 -0
- package/dist/extract/contracts.d.ts.map +1 -0
- 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/host.d.ts +2 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +3 -0
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts +2 -0
- package/dist/features/prepared-graphics-store.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 +126 -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 +9 -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 +3 -3
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +7 -6
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +7 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +90 -5
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +163 -1
- package/dist/inspection-types.d.ts.map +1 -1
- 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 +60 -148
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/materials.d.ts +34 -10
- package/dist/materials.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +56 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/occlusion-proxy.d.ts +4 -0
- package/dist/occlusion-proxy.d.ts.map +1 -0
- package/dist/pbr-pipeline.d.ts +58 -21
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
- package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
- package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.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 +1 -1
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/points-lines/prepare.d.ts +3 -0
- package/dist/points-lines/prepare.d.ts.map +1 -1
- package/dist/points-lines/standard-owner.d.ts +33 -0
- package/dist/points-lines/standard-owner.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 +8 -6
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +55 -10
- 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 +21 -5
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-timing.d.ts +19 -7
- 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 +20 -2
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +10 -18
- 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.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +11 -3
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/recovery-pipeline.d.ts +30 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -0
- package/dist/record/render-context.d.ts +82 -9
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +49 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +1 -2
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +65 -0
- package/dist/recovery/render-system-candidate.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 +49 -0
- package/dist/reflection/projection.d.ts.map +1 -1
- 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 +78 -15
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +7 -0
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +12 -0
- package/dist/render-system-extract-tail.d.ts.map +1 -0
- package/dist/render-system-extract.d.ts +252 -30
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +140 -5
- package/dist/render-system.d.ts.map +1 -1
- 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 +122 -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/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/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/taa-history-store.d.ts +15 -1
- package/dist/temporal/taa-history-store.d.ts.map +1 -1
- package/dist/temporal/transaction.d.ts +5 -0
- package/dist/temporal/transaction.d.ts.map +1 -1
- 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 +4 -0
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +8 -4
- package/dist/volume/passes.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
- package/src/__tests__/detached-inspection.integration.test.ts +6 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +19 -1
- package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
- package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
- package/src/__tests__/environment-generation.integration.test.ts +39 -0
- package/src/__tests__/evidence-identity.ts +13 -0
- package/src/__tests__/evidence-identity.unit.test.ts +24 -0
- package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -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 +154 -6
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
- package/src/__tests__/frame-observation.contract.test.ts +15 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
- 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 +89 -2
- 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-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__/ibl/face-uniforms.unit.test.ts +2 -0
- package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -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 +41 -9
- package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
- package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
- package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
- 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__/physical-clearcoat.integration.test.ts +89 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
- 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__/recover-error-policy-owner.unit.test.ts +6 -3
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -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-owner-placement.unit.test.ts +39 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
- 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__/renderer-factory-material-contract.unit.test.ts +160 -17
- package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
- package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
- package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
- package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
- package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
- package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
- package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
- package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
- package/src/__tests__/shader-manifest-fixture.ts +13 -16
- package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
- package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
- 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-variant-resolution.unit.test.ts +67 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
- package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
- package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
- package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
- 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 +111 -32
- 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__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
- package/src/assembly/backend-contract.ts +13 -0
- package/src/assembly/bundler-contract.ts +9 -0
- package/src/assembly/device-feature-admission.ts +26 -9
- package/src/assembly/extended-lighting-fallback.ts +216 -0
- package/src/assembly/factory.ts +81 -9634
- package/src/assembly/host-contract.ts +10 -0
- package/src/assembly/material/assembly.ts +18 -2
- package/src/assembly/material/standard-layer-projection.ts +16 -0
- package/src/assembly/material-shader-policy.ts +854 -0
- package/src/assembly/material-shader-prewarm.ts +88 -0
- package/src/assembly/mesh-ssbo-grow.ts +270 -0
- package/src/assembly/recovery/device-loss-fanout.ts +89 -0
- package/src/assembly/recovery/generation.ts +141 -0
- package/src/assembly/recovery/recovery-attempt.ts +800 -0
- package/src/assembly/recovery/renderer-recover.ts +906 -0
- package/src/assembly/renderer-facade.ts +342 -0
- package/src/assembly/renderer-frame-transaction.ts +45 -0
- package/src/assembly/renderer-helpers.ts +99 -0
- package/src/assembly/renderer-lifecycle.ts +159 -0
- package/src/assembly/shader-prewarm-policy.ts +157 -0
- package/src/assembly/webgpu-ready-contract.ts +14 -0
- package/src/assembly/webgpu-ready.ts +4079 -0
- package/src/assembly/webgpu-renderer.ts +4095 -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/camera.ts +1 -1
- package/src/components/index.ts +3 -0
- package/src/components/light-helpers.ts +52 -1
- package/src/components/light-probe.ts +13 -0
- package/src/components/rect-area-light.ts +15 -0
- package/src/components/scene-instance.ts +6 -0
- package/src/components/spot-light.ts +3 -0
- package/src/debug-draw-glue.ts +1 -1
- package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
- package/src/device/device-scope.ts +35 -6
- package/src/device/gpu-residency.ts +334 -35
- package/src/device/lifecycle-transaction.ts +12 -0
- package/src/environment/frame.ts +6 -0
- package/src/environment/inspection.ts +10 -0
- package/src/environment/lifecycle.ts +122 -9
- package/src/errors/gpu-driven.ts +92 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/recover.ts +108 -5
- package/src/errors/render.ts +72 -1
- package/src/extract/camera.ts +3 -3
- package/src/extract/contracts.ts +974 -0
- package/src/extract/gpu-driven.ts +108 -0
- package/src/extract/visibility.ts +0 -23
- package/src/features/host.ts +16 -0
- package/src/features/prepared-gpu-work.ts +20 -2
- package/src/features/prepared-graphics-store.ts +17 -0
- package/src/gpu-driven/batch-topology.ts +206 -28
- package/src/gpu-driven/prepared-draw.ts +208 -0
- package/src/gpu-driven/production-raster.ts +1046 -39
- package/src/gpu-driven/view-gpu.ts +411 -29
- package/src/gpu-scene-schema.ts +13 -0
- package/src/gpu-scene.ts +137 -67
- package/src/gpu-usage.ts +0 -1
- package/src/hdrp-buffers.ts +6 -6
- package/src/ibl/IblPipelineCache.ts +134 -50
- package/src/ibl/skylight-bind-group.ts +26 -0
- package/src/index.ts +83 -1
- package/src/inspection-types.ts +220 -1
- package/src/internal.ts +38 -0
- package/src/light-buffer-layout.ts +157 -324
- package/src/materials.ts +278 -47
- package/src/mesh-material-bindings.ts +59 -0
- package/src/occlusion-proxy.ts +64 -0
- package/src/pbr-pipeline.ts +290 -60
- package/src/pipeline/standard-forward-lane.ts +47 -0
- package/src/pipeline/standard-lighting/graph.ts +2 -1
- package/src/pipeline/standard-lighting/light-frame.ts +3 -2
- package/src/pipeline/standard-lighting/topology.ts +1 -1
- package/src/pipeline/standard-lighting/transport.ts +2 -1
- package/src/pipeline/standard-pipeline.ts +2 -0
- package/src/pipeline-builder.ts +5 -2
- package/src/pipeline-spec.ts +12 -1
- package/src/plugin.ts +4 -0
- package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
- package/src/points-lines/prepare.ts +9 -0
- package/src/points-lines/standard-owner.ts +403 -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__/frame-targets-recovery.unit.test.ts +60 -0
- package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
- package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
- package/src/record/__tests__/temporal-payload.unit.test.ts +29 -28
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +21 -2
- package/src/record/frame-lighting.ts +252 -53
- package/src/record/frame-snapshot.ts +72 -12
- package/src/record/frame-targets.ts +11 -1
- package/src/record/frame.ts +416 -95
- package/src/record/gpu-timing.ts +108 -44
- package/src/record/helpers.ts +1 -59
- package/src/record/main-pass-geometry.ts +434 -44
- package/src/record/main-pass-material.ts +324 -210
- package/src/record/main-pass-sprite-draws.ts +58 -92
- package/src/record/main-pass.ts +94 -6
- package/src/record/mesh-ssbo.ts +79 -46
- package/src/record/recovery-pipeline.ts +695 -0
- package/src/record/render-context.ts +99 -7
- package/src/record/shadow-pass.ts +66 -40
- package/src/record/typed-frame-graph.ts +270 -84
- package/src/record/view-ubo.ts +17 -27
- package/src/recovery/render-system-candidate.ts +839 -0
- package/src/reflection/inspection.ts +67 -0
- package/src/reflection/projection.ts +196 -1
- package/src/reflection/record-owner.ts +1283 -0
- package/src/render-contract.ts +82 -12
- package/src/render-pipeline.ts +5 -0
- package/src/render-system-extract-tail.ts +2983 -0
- package/src/render-system-extract.ts +290 -2784
- package/src/render-system.ts +1597 -1192
- 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 +972 -184
- 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/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/temporal/frame-coordinator.ts +30 -1
- package/src/temporal/frame.ts +14 -1
- package/src/temporal/taa-history-store.ts +46 -2
- package/src/temporal/temporal-view.ts +1 -1
- package/src/temporal/transaction.ts +9 -0
- package/src/temporal/view.ts +11 -0
- package/src/tilemap-chunk-extract-system.ts +5 -5
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
- package/src/typed-render-graph-primitives.ts +48 -0
- package/src/volume/passes.ts +46 -49
- package/dist/.tsbuildinfo +0 -1
- package/dist/chunk-5YADUCA7.mjs.map +0 -1
- package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
- package/dist/chunk-BXBQQKFQ.mjs +0 -67
- package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
- package/dist/chunk-FS2KNUQH.mjs.map +0 -1
- package/dist/chunk-PCHBPYML.mjs.map +0 -1
- package/dist/chunk-VFPLTKCU.mjs +0 -192
- package/dist/chunk-VFPLTKCU.mjs.map +0 -1
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Buffer,
|
|
3
|
+
QuerySet,
|
|
4
|
+
RenderPipeline,
|
|
5
|
+
Result,
|
|
6
|
+
RhiCommandEncoder,
|
|
7
|
+
RhiDevice,
|
|
8
|
+
RhiError,
|
|
9
|
+
RhiRenderPassEncoder,
|
|
10
|
+
} from '@forgeax/engine-rhi';
|
|
11
|
+
import { err, ok, RhiError as RhiErrorClass } from '@forgeax/engine-rhi';
|
|
12
|
+
import {
|
|
13
|
+
GPU_BUFFER_USAGE_COPY_DST,
|
|
14
|
+
GPU_BUFFER_USAGE_MAP_READ,
|
|
15
|
+
GPU_BUFFER_USAGE_VERTEX,
|
|
16
|
+
} from '../../gpu-usage';
|
|
17
|
+
import type { PipelineBuilderShaderModuleFactory } from '../../pipeline-builder';
|
|
18
|
+
import type { VisibilityBudget } from './budget';
|
|
19
|
+
import type { VisibilityFacetStore } from './facet';
|
|
20
|
+
import {
|
|
21
|
+
createOcclusionQueryResources,
|
|
22
|
+
type OcclusionBounds,
|
|
23
|
+
type OcclusionQueryResources,
|
|
24
|
+
recordOcclusionResolve,
|
|
25
|
+
} from './occlusion-pass';
|
|
26
|
+
import {
|
|
27
|
+
OCCLUSION_QUERY_PAGE_INDEX_LIMIT,
|
|
28
|
+
OcclusionQueryPool,
|
|
29
|
+
type OcclusionQueryReservation,
|
|
30
|
+
type OcclusionQueryTicket,
|
|
31
|
+
} from './occlusion-query-pool';
|
|
32
|
+
import {
|
|
33
|
+
type PrimitiveKey,
|
|
34
|
+
primitiveKeyId,
|
|
35
|
+
type ViewKey,
|
|
36
|
+
type VisibilityCandidate,
|
|
37
|
+
viewKeyId,
|
|
38
|
+
} from './types';
|
|
39
|
+
|
|
40
|
+
const PROXY_VERTEX_FLOATS_PER_CANDIDATE = 36 * 4;
|
|
41
|
+
const PROXY_GEOMETRY_BYTES =
|
|
42
|
+
OCCLUSION_QUERY_PAGE_INDEX_LIMIT * PROXY_VERTEX_FLOATS_PER_CANDIDATE * 4;
|
|
43
|
+
|
|
44
|
+
/** @internal Deterministic producer hook; never configured by the player API. */
|
|
45
|
+
export interface OcclusionRuntimeTestHooks {
|
|
46
|
+
readonly beforeMap?: () => Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let runtimeTestHooks: OcclusionRuntimeTestHooks | undefined;
|
|
50
|
+
|
|
51
|
+
/** @internal Used only by the production evidence falsification harness. */
|
|
52
|
+
export function setOcclusionRuntimeTestHooks(hooks: OcclusionRuntimeTestHooks | undefined): void {
|
|
53
|
+
runtimeTestHooks = hooks;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface OcclusionRuntimeCandidate {
|
|
57
|
+
readonly view: ViewKey;
|
|
58
|
+
readonly primitive: PrimitiveKey;
|
|
59
|
+
readonly epoch: number;
|
|
60
|
+
readonly bounds: OcclusionBounds;
|
|
61
|
+
readonly candidate: VisibilityCandidate;
|
|
62
|
+
readonly deviceGeneration: number;
|
|
63
|
+
/** Clip-space triangles for the conservative bounds proxy. */
|
|
64
|
+
readonly proxyVertices?: Float32Array;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface OcclusionFrameProjection {
|
|
68
|
+
readonly querySet: QuerySet;
|
|
69
|
+
readonly queryIndex: number;
|
|
70
|
+
readonly queryCount: number;
|
|
71
|
+
readonly sampleCount: 1 | 4;
|
|
72
|
+
readonly pageIndex: number;
|
|
73
|
+
readonly reservationId: number;
|
|
74
|
+
readonly encodeProxyBounds: (pass: RhiRenderPassEncoder) => Result<void, RhiError>;
|
|
75
|
+
readonly beginCandidate: (pass: RhiRenderPassEncoder) => Result<void, RhiError>;
|
|
76
|
+
readonly endCandidate: (pass: RhiRenderPassEncoder) => Result<void, RhiError>;
|
|
77
|
+
readonly recordResolve: (encoder: RhiCommandEncoder) => Result<void, RhiError>;
|
|
78
|
+
readonly commit: (
|
|
79
|
+
submitted: boolean,
|
|
80
|
+
submissionGeneration: number,
|
|
81
|
+
profilePhase?: OcclusionProfilePhaseRunner,
|
|
82
|
+
) => void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type OcclusionProfilePhaseRunner = <T>(
|
|
86
|
+
phase: 'record/occlusion-query-submit' | 'record/occlusion-global-advance',
|
|
87
|
+
action: () => T,
|
|
88
|
+
) => T;
|
|
89
|
+
|
|
90
|
+
interface PendingFrame {
|
|
91
|
+
readonly reservations: readonly OcclusionQueryReservation[];
|
|
92
|
+
readonly resources: OcclusionQueryResources;
|
|
93
|
+
readonly candidates: readonly OcclusionRuntimeCandidate[];
|
|
94
|
+
readonly sampleCount: 1 | 4;
|
|
95
|
+
endedCount: number;
|
|
96
|
+
readonly proxyFloatCount: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* RenderSystem-owned query transport. It is deliberately the only bridge
|
|
101
|
+
* between the logical pool, the typed graph raster pass, and facet completion.
|
|
102
|
+
*/
|
|
103
|
+
export class OcclusionRenderRuntime {
|
|
104
|
+
private readonly pool = new OcclusionQueryPool();
|
|
105
|
+
private readonly resources = new Map<number, OcclusionQueryResources>();
|
|
106
|
+
private readonly activeCompletions = new Map<OcclusionQueryResources, Promise<void>>();
|
|
107
|
+
private readonly retiredResources = new Set<OcclusionQueryResources>();
|
|
108
|
+
/** Candidate descriptors are rebuilt only when the extracted frame changes. */
|
|
109
|
+
private candidateSource: readonly OcclusionRuntimeCandidate[] | undefined;
|
|
110
|
+
private validCandidates: readonly OcclusionRuntimeCandidate[] = [];
|
|
111
|
+
private readonly candidateByKey = new Map<string, OcclusionRuntimeCandidate>();
|
|
112
|
+
private lastPrepareUnavailable = false;
|
|
113
|
+
private pendingFrame: PendingFrame | undefined;
|
|
114
|
+
private disposed = false;
|
|
115
|
+
private proxyBuffer: Buffer | undefined;
|
|
116
|
+
private readonly proxyScratch: Float32Array;
|
|
117
|
+
private proxyPipeline: RenderPipeline | undefined;
|
|
118
|
+
private proxyPipelineSampleCount: 1 | 4 | undefined;
|
|
119
|
+
private readonly queryLatencySamplesUs: number[] = [];
|
|
120
|
+
private lastQueryLatencyUs = 0;
|
|
121
|
+
private readonly projection: OcclusionFrameProjection;
|
|
122
|
+
|
|
123
|
+
constructor(
|
|
124
|
+
private readonly device: RhiDevice,
|
|
125
|
+
private readonly facets: VisibilityFacetStore,
|
|
126
|
+
private readonly shaderModuleFactory?: PipelineBuilderShaderModuleFactory,
|
|
127
|
+
private readonly budget: VisibilityBudget = facets.visibilityBudget(),
|
|
128
|
+
) {
|
|
129
|
+
const runtime = this;
|
|
130
|
+
this.proxyScratch = new Float32Array(
|
|
131
|
+
Math.min(this.budget.effectiveQueryBudget, OCCLUSION_QUERY_PAGE_INDEX_LIMIT) *
|
|
132
|
+
PROXY_VERTEX_FLOATS_PER_CANDIDATE,
|
|
133
|
+
);
|
|
134
|
+
this.projection = {
|
|
135
|
+
get querySet() {
|
|
136
|
+
return runtime.pendingFrame?.resources.querySet as QuerySet;
|
|
137
|
+
},
|
|
138
|
+
get queryIndex() {
|
|
139
|
+
return runtime.pendingFrame?.reservations[0]?.queryIndex ?? 0;
|
|
140
|
+
},
|
|
141
|
+
get queryCount() {
|
|
142
|
+
return runtime.pendingFrame?.reservations.length ?? 0;
|
|
143
|
+
},
|
|
144
|
+
get sampleCount() {
|
|
145
|
+
return runtime.pendingFrame?.sampleCount ?? 1;
|
|
146
|
+
},
|
|
147
|
+
get pageIndex() {
|
|
148
|
+
return runtime.pendingFrame?.reservations[0]?.pageIndex ?? 0;
|
|
149
|
+
},
|
|
150
|
+
get reservationId() {
|
|
151
|
+
return runtime.pendingFrame?.reservations[0]?.reservationId ?? 0;
|
|
152
|
+
},
|
|
153
|
+
encodeProxyBounds: (pass) => {
|
|
154
|
+
const pending = runtime.pendingFrame;
|
|
155
|
+
return pending === undefined
|
|
156
|
+
? err(runtimeError('the current RenderSystem frame owns the occlusion candidate'))
|
|
157
|
+
: runtime.encodeProxyBounds(pass, pending);
|
|
158
|
+
},
|
|
159
|
+
beginCandidate: (pass) => {
|
|
160
|
+
const pending = runtime.pendingFrame;
|
|
161
|
+
if (pending === undefined || pending.endedCount >= pending.reservations.length) {
|
|
162
|
+
return err(runtimeError('the current RenderSystem frame owns the occlusion candidate'));
|
|
163
|
+
}
|
|
164
|
+
return pass.beginOcclusionQuery(pending.reservations[pending.endedCount]?.queryIndex ?? 0);
|
|
165
|
+
},
|
|
166
|
+
endCandidate: (pass) => {
|
|
167
|
+
const pending = runtime.pendingFrame;
|
|
168
|
+
if (pending === undefined || pending.endedCount >= pending.reservations.length) {
|
|
169
|
+
return err(runtimeError('the current RenderSystem frame owns the occlusion candidate'));
|
|
170
|
+
}
|
|
171
|
+
const ended = pass.endOcclusionQuery();
|
|
172
|
+
if (ended.ok) pending.endedCount += 1;
|
|
173
|
+
return ended;
|
|
174
|
+
},
|
|
175
|
+
recordResolve: (encoder) => {
|
|
176
|
+
const pending = runtime.pendingFrame;
|
|
177
|
+
if (pending === undefined || pending.endedCount !== pending.reservations.length) {
|
|
178
|
+
return err(runtimeError('the real geometry pass must end its occlusion query first'));
|
|
179
|
+
}
|
|
180
|
+
return recordOcclusionResolve(
|
|
181
|
+
encoder,
|
|
182
|
+
pending.resources,
|
|
183
|
+
pending.reservations[0]?.queryIndex ?? 0,
|
|
184
|
+
pending.reservations.length,
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
commit: (submitted, submissionGeneration, profilePhase) => {
|
|
188
|
+
const pending = runtime.pendingFrame;
|
|
189
|
+
if (pending !== undefined)
|
|
190
|
+
runtime.commit(pending, submitted, submissionGeneration, profilePhase);
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
prepare(candidate: OcclusionRuntimeCandidate): OcclusionFrameProjection | undefined {
|
|
196
|
+
// Keep the single-candidate helper useful for transport unit tests and
|
|
197
|
+
// direct callers. The RenderSystem uses prepareBatch's facet-owned queue.
|
|
198
|
+
return this.prepareBatch([candidate], 1, false);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
prepareBatch(
|
|
202
|
+
candidates: readonly OcclusionRuntimeCandidate[],
|
|
203
|
+
sampleCount: 1 | 4 = 1,
|
|
204
|
+
useReadyQueue = true,
|
|
205
|
+
): OcclusionFrameProjection | undefined {
|
|
206
|
+
this.lastPrepareUnavailable = false;
|
|
207
|
+
if (this.disposed || candidates.length === 0) return undefined;
|
|
208
|
+
if (this.pendingFrame !== undefined) {
|
|
209
|
+
this.lastPrepareUnavailable = true;
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
if (this.candidateSource !== candidates) {
|
|
213
|
+
this.candidateSource = candidates;
|
|
214
|
+
this.candidateByKey.clear();
|
|
215
|
+
const valid: OcclusionRuntimeCandidate[] = [];
|
|
216
|
+
for (const candidate of candidates) {
|
|
217
|
+
this.candidateByKey.set(candidateKey(candidate.view, candidate.primitive), candidate);
|
|
218
|
+
if (finiteBounds(candidate.bounds)) valid.push(candidate);
|
|
219
|
+
}
|
|
220
|
+
this.validCandidates = valid;
|
|
221
|
+
}
|
|
222
|
+
const valid = this.validCandidates;
|
|
223
|
+
if (valid.length === 0) return undefined;
|
|
224
|
+
|
|
225
|
+
let selectedCandidates: OcclusionRuntimeCandidate[];
|
|
226
|
+
if (!useReadyQueue) {
|
|
227
|
+
selectedCandidates = valid.slice(0, this.budget.effectiveQueryBudget);
|
|
228
|
+
} else {
|
|
229
|
+
const firstView = valid[0]?.view;
|
|
230
|
+
if (firstView === undefined) return undefined;
|
|
231
|
+
selectedCandidates = [];
|
|
232
|
+
for (const entry of this.facets.dequeueQueryCandidates(
|
|
233
|
+
firstView,
|
|
234
|
+
this.budget.effectiveQueryBudget,
|
|
235
|
+
)) {
|
|
236
|
+
const candidate = this.candidateByKey.get(candidateKey(entry.view, entry.primitive));
|
|
237
|
+
if (candidate === undefined || !finiteBounds(candidate.bounds)) {
|
|
238
|
+
this.facets.requeueQueryCandidate(entry.view, entry.primitive);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
selectedCandidates.push(candidate);
|
|
242
|
+
}
|
|
243
|
+
// No ready work is a normal bounded-query frame, not a transport error.
|
|
244
|
+
if (selectedCandidates.length === 0) return undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const reservations: OcclusionQueryReservation[] = [];
|
|
248
|
+
let selectedCount = 0;
|
|
249
|
+
while (selectedCount < selectedCandidates.length) {
|
|
250
|
+
const candidate = selectedCandidates[selectedCount];
|
|
251
|
+
if (candidate === undefined) break;
|
|
252
|
+
const reservation = this.pool.reserve({
|
|
253
|
+
viewKey: viewKeyId(candidate.view),
|
|
254
|
+
attachmentId: candidate.view.attachmentId,
|
|
255
|
+
deviceGeneration: candidate.deviceGeneration,
|
|
256
|
+
worldGeneration: candidate.primitive.worldGeneration,
|
|
257
|
+
primitiveSlot: candidate.primitive.primitiveSlot,
|
|
258
|
+
slotGeneration: candidate.primitive.slotGeneration,
|
|
259
|
+
});
|
|
260
|
+
if (reservation === undefined) {
|
|
261
|
+
this.lastPrepareUnavailable = true;
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
if (reservations.length > 0 && reservation.pageIndex !== reservations[0]?.pageIndex) {
|
|
265
|
+
this.pool.cancel(reservation);
|
|
266
|
+
this.lastPrepareUnavailable = true;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
reservations.push(reservation);
|
|
270
|
+
selectedCount += 1;
|
|
271
|
+
if (reservations.length >= this.budget.effectiveQueryBudget) break;
|
|
272
|
+
}
|
|
273
|
+
if (reservations.length === 0) {
|
|
274
|
+
for (const candidate of selectedCandidates) {
|
|
275
|
+
if (useReadyQueue) this.facets.requeueQueryCandidate(candidate.view, candidate.primitive);
|
|
276
|
+
}
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
if (reservations.length < selectedCandidates.length) {
|
|
280
|
+
for (const reservation of reservations) this.pool.cancel(reservation);
|
|
281
|
+
for (const candidate of selectedCandidates) {
|
|
282
|
+
if (useReadyQueue) this.facets.requeueQueryCandidate(candidate.view, candidate.primitive);
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
const resources = this.ensureResources(reservations[0]?.pageIndex ?? 0);
|
|
287
|
+
if (resources === undefined) {
|
|
288
|
+
for (const reservation of reservations) this.pool.cancel(reservation);
|
|
289
|
+
this.lastPrepareUnavailable = true;
|
|
290
|
+
for (const candidate of selectedCandidates) {
|
|
291
|
+
if (useReadyQueue) this.facets.requeueQueryCandidate(candidate.view, candidate.primitive);
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
const selected = selectedCandidates;
|
|
296
|
+
let proxyFloatCount = 0;
|
|
297
|
+
for (const candidate of selected) {
|
|
298
|
+
const vertices = candidate.proxyVertices;
|
|
299
|
+
if (vertices === undefined) continue;
|
|
300
|
+
if (proxyFloatCount + vertices.length > this.proxyScratch.length) {
|
|
301
|
+
for (const reservation of reservations) this.pool.cancel(reservation);
|
|
302
|
+
for (const queuedCandidate of selectedCandidates) {
|
|
303
|
+
if (useReadyQueue) {
|
|
304
|
+
this.facets.requeueQueryCandidate(queuedCandidate.view, queuedCandidate.primitive);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
this.lastPrepareUnavailable = true;
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
this.proxyScratch.set(vertices, proxyFloatCount);
|
|
311
|
+
proxyFloatCount += vertices.length;
|
|
312
|
+
}
|
|
313
|
+
const pending: PendingFrame = {
|
|
314
|
+
reservations,
|
|
315
|
+
resources,
|
|
316
|
+
candidates: selected,
|
|
317
|
+
sampleCount,
|
|
318
|
+
endedCount: 0,
|
|
319
|
+
proxyFloatCount,
|
|
320
|
+
};
|
|
321
|
+
this.pendingFrame = pending;
|
|
322
|
+
return this.projection;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
inspect(): ReturnType<OcclusionQueryPool['inspect']> {
|
|
326
|
+
return this.pool.inspect();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
get prepareUnavailable(): boolean {
|
|
330
|
+
return this.lastPrepareUnavailable;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Map-only latency from the submitted query page; queue wait is excluded. */
|
|
334
|
+
inspectQueryLatency(): { readonly median: number; readonly p95: number; readonly last: number } {
|
|
335
|
+
if (this.queryLatencySamplesUs.length === 0) {
|
|
336
|
+
return { median: 0, p95: 0, last: this.lastQueryLatencyUs };
|
|
337
|
+
}
|
|
338
|
+
const sorted = [...this.queryLatencySamplesUs].sort((left, right) => left - right);
|
|
339
|
+
const rank = (percentile: number): number =>
|
|
340
|
+
sorted[Math.max(0, Math.ceil(sorted.length * percentile) - 1)] ?? 0;
|
|
341
|
+
return {
|
|
342
|
+
median: rank(0.5),
|
|
343
|
+
p95: rank(0.95),
|
|
344
|
+
last: this.lastQueryLatencyUs,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Wait until query readbacks submitted before the current observation have
|
|
350
|
+
* settled. The renderer receipt only fences command submission; query
|
|
351
|
+
* completion also includes the asynchronous map/unmap transition. Keeping
|
|
352
|
+
* that fence here makes `renderer.observe()` publish the latest visibility
|
|
353
|
+
* result and measured map latency instead of racing the readback promise.
|
|
354
|
+
*/
|
|
355
|
+
async waitForCompletions(): Promise<void> {
|
|
356
|
+
// A submission callback can publish the next page while the previous
|
|
357
|
+
// completion is settling. Drain until the map stays empty so observation
|
|
358
|
+
// cannot return with a newer query still racing the inspection update.
|
|
359
|
+
for (;;) {
|
|
360
|
+
const completions = [...this.activeCompletions.values()];
|
|
361
|
+
if (completions.length === 0) return;
|
|
362
|
+
await Promise.all(completions);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Count a successful frame for hidden rows that were not queried this frame. */
|
|
367
|
+
advanceSuccessfulSubmit(submissionGeneration: number): void {
|
|
368
|
+
this.facets.advanceSuccessfulSubmits(submissionGeneration);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
dispose(): void {
|
|
372
|
+
if (this.disposed) return;
|
|
373
|
+
this.disposed = true;
|
|
374
|
+
this.pool.dispose();
|
|
375
|
+
for (const resource of this.resources.values()) {
|
|
376
|
+
const completion = this.activeCompletions.get(resource);
|
|
377
|
+
if (completion === undefined) {
|
|
378
|
+
this.destroyResources(resource);
|
|
379
|
+
} else {
|
|
380
|
+
// A completion may still be awaiting mapAsync. Destroying its staging
|
|
381
|
+
// buffer here makes Dawn report a use-after-destroy validation error.
|
|
382
|
+
void completion.then(() => this.destroyResources(resource));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
this.resources.clear();
|
|
386
|
+
if (this.proxyBuffer !== undefined) this.device.destroyBuffer(this.proxyBuffer);
|
|
387
|
+
this.proxyBuffer = undefined;
|
|
388
|
+
this.proxyPipeline = undefined;
|
|
389
|
+
this.proxyPipelineSampleCount = undefined;
|
|
390
|
+
this.candidateSource = undefined;
|
|
391
|
+
this.validCandidates = [];
|
|
392
|
+
this.candidateByKey.clear();
|
|
393
|
+
this.lastPrepareUnavailable = false;
|
|
394
|
+
this.pendingFrame = undefined;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private ensureResources(pageIndex: number): OcclusionQueryResources | undefined {
|
|
398
|
+
const existing = this.resources.get(pageIndex);
|
|
399
|
+
if (existing !== undefined) return existing;
|
|
400
|
+
const created = createOcclusionQueryResources(this.device, pageIndex);
|
|
401
|
+
if (!created.ok) return undefined;
|
|
402
|
+
this.resources.set(pageIndex, created.value);
|
|
403
|
+
return created.value;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private commit(
|
|
407
|
+
pending: PendingFrame,
|
|
408
|
+
submitted: boolean,
|
|
409
|
+
submissionGeneration: number,
|
|
410
|
+
profilePhase?: OcclusionProfilePhaseRunner,
|
|
411
|
+
): void {
|
|
412
|
+
if (this.pendingFrame !== pending) return;
|
|
413
|
+
this.pendingFrame = undefined;
|
|
414
|
+
const tickets: OcclusionQueryTicket[] = [];
|
|
415
|
+
const accepted =
|
|
416
|
+
profilePhase === undefined
|
|
417
|
+
? this.publishAndApplySubmit(pending, submitted, submissionGeneration, tickets)
|
|
418
|
+
: profilePhase('record/occlusion-query-submit', () =>
|
|
419
|
+
this.publishAndApplySubmit(pending, submitted, submissionGeneration, tickets),
|
|
420
|
+
);
|
|
421
|
+
if (!accepted) {
|
|
422
|
+
for (const reservation of pending.reservations) this.pool.cancel(reservation);
|
|
423
|
+
for (const candidate of pending.candidates) {
|
|
424
|
+
this.facets.applyConfidence(candidate.view, candidate.primitive, {
|
|
425
|
+
type: 'failure',
|
|
426
|
+
submissionGeneration,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (profilePhase === undefined) {
|
|
433
|
+
this.facets.advanceSuccessfulSubmits(submissionGeneration);
|
|
434
|
+
} else {
|
|
435
|
+
profilePhase('record/occlusion-global-advance', () =>
|
|
436
|
+
this.facets.advanceSuccessfulSubmits(submissionGeneration),
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
const completion = this.completeAfterSubmit(tickets, pending);
|
|
440
|
+
this.activeCompletions.set(pending.resources, completion);
|
|
441
|
+
void completion.then(() => {
|
|
442
|
+
if (this.activeCompletions.get(pending.resources) === completion) {
|
|
443
|
+
this.activeCompletions.delete(pending.resources);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private publishAndApplySubmit(
|
|
449
|
+
pending: PendingFrame,
|
|
450
|
+
submitted: boolean,
|
|
451
|
+
submissionGeneration: number,
|
|
452
|
+
tickets: OcclusionQueryTicket[],
|
|
453
|
+
): boolean {
|
|
454
|
+
for (const reservation of pending.reservations) {
|
|
455
|
+
const ticket = this.pool.publish(reservation, { submitted, submissionGeneration });
|
|
456
|
+
if (ticket !== undefined) tickets.push(ticket);
|
|
457
|
+
}
|
|
458
|
+
if (!submitted || tickets.length !== pending.reservations.length) return false;
|
|
459
|
+
const submitEvent = Object.freeze({ type: 'submit' as const, submissionGeneration });
|
|
460
|
+
for (const candidate of pending.candidates) {
|
|
461
|
+
this.facets.applyConfidence(candidate.view, candidate.primitive, submitEvent);
|
|
462
|
+
}
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private destroyResources(resource: OcclusionQueryResources): void {
|
|
467
|
+
if (this.retiredResources.has(resource)) return;
|
|
468
|
+
this.retiredResources.add(resource);
|
|
469
|
+
this.device.destroyBuffer(resource.resolveBuffer);
|
|
470
|
+
this.device.destroyBuffer(resource.stagingBuffer);
|
|
471
|
+
this.device.destroyQuerySet(resource.querySet);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private async completeAfterSubmit(
|
|
475
|
+
tickets: readonly OcclusionQueryTicket[],
|
|
476
|
+
pending: PendingFrame,
|
|
477
|
+
): Promise<void> {
|
|
478
|
+
try {
|
|
479
|
+
await this.device.queue.onSubmittedWorkDone();
|
|
480
|
+
const mapStart = performance.now();
|
|
481
|
+
await runtimeTestHooks?.beforeMap?.();
|
|
482
|
+
const mapped = await pending.resources.stagingBuffer.mapAsync(
|
|
483
|
+
GPU_BUFFER_USAGE_MAP_READ,
|
|
484
|
+
0,
|
|
485
|
+
pending.reservations.length * 8,
|
|
486
|
+
);
|
|
487
|
+
if (!mapped.ok) throw mapped.error;
|
|
488
|
+
// Inspection is a bounded integer POD; keep the real map-only duration
|
|
489
|
+
// while rounding sub-microsecond timer noise at the transport boundary.
|
|
490
|
+
this.lastQueryLatencyUs = Math.max(0, Math.round((performance.now() - mapStart) * 1000));
|
|
491
|
+
this.queryLatencySamplesUs.push(this.lastQueryLatencyUs);
|
|
492
|
+
if (this.queryLatencySamplesUs.length > 64) this.queryLatencySamplesUs.shift();
|
|
493
|
+
const bytes = mapped.value.getMappedRange(0, pending.reservations.length * 8);
|
|
494
|
+
if (!bytes.ok) throw bytes.error;
|
|
495
|
+
// WebGPU detaches the mapped range when unmap() is called. Copy the
|
|
496
|
+
// bounded query payload while it is still mapped, then release the
|
|
497
|
+
// staging buffer before applying any facet mutations. Reading the view
|
|
498
|
+
// after unmap would throw on real Dawn and turn every result into the
|
|
499
|
+
// fail-open failure path (leaving candidates permanently visible).
|
|
500
|
+
const data = new Uint8Array(bytes.value.slice(0));
|
|
501
|
+
const dataView = new DataView(data.buffer);
|
|
502
|
+
const firstQuery = pending.reservations[0]?.queryIndex ?? 0;
|
|
503
|
+
mapped.value.unmap();
|
|
504
|
+
for (let ticketIndex = tickets.length - 1; ticketIndex >= 0; ticketIndex -= 1) {
|
|
505
|
+
const ticket = tickets[ticketIndex];
|
|
506
|
+
if (ticket === undefined) continue;
|
|
507
|
+
const candidateIndex = ticket.queryIndex - firstQuery;
|
|
508
|
+
const samples = dataView.getUint32(candidateIndex * 8, true);
|
|
509
|
+
const completion = this.pool.complete(ticket, samples);
|
|
510
|
+
if (completion.status !== 'accepted') continue;
|
|
511
|
+
const candidate = pending.candidates[candidateIndex];
|
|
512
|
+
if (candidate === undefined) continue;
|
|
513
|
+
this.facets.applyConfidence(candidate.view, candidate.primitive, {
|
|
514
|
+
type: 'result',
|
|
515
|
+
samples,
|
|
516
|
+
submissionGeneration: ticket.submissionGeneration,
|
|
517
|
+
});
|
|
518
|
+
this.facets.applyCompletion(
|
|
519
|
+
candidate.view,
|
|
520
|
+
candidate.primitive,
|
|
521
|
+
candidate.epoch,
|
|
522
|
+
candidate.candidate,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
} catch {
|
|
526
|
+
for (const ticket of tickets) {
|
|
527
|
+
this.pool.complete(ticket, Number.NaN);
|
|
528
|
+
const candidate =
|
|
529
|
+
pending.candidates[ticket.queryIndex - (pending.reservations[0]?.queryIndex ?? 0)];
|
|
530
|
+
if (candidate !== undefined) {
|
|
531
|
+
this.facets.applyConfidence(candidate.view, candidate.primitive, {
|
|
532
|
+
type: 'failure',
|
|
533
|
+
submissionGeneration: ticket.submissionGeneration,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
private encodeProxyBounds(
|
|
541
|
+
pass: RhiRenderPassEncoder,
|
|
542
|
+
pending: PendingFrame,
|
|
543
|
+
): Result<void, RhiError> {
|
|
544
|
+
if (pending.proxyFloatCount === 0) {
|
|
545
|
+
return err(runtimeError('every occlusion candidate needs conservative proxy geometry'));
|
|
546
|
+
}
|
|
547
|
+
const pipeline = this.ensureProxyPipeline(pending.sampleCount);
|
|
548
|
+
if (pipeline === undefined) {
|
|
549
|
+
// RhiNull has no shader-module factory. It still records the complete
|
|
550
|
+
// query fan-out structurally so lifecycle and identity tests exercise
|
|
551
|
+
// the same bounded transport; real backends take the mesh path below.
|
|
552
|
+
for (const reservation of pending.reservations) {
|
|
553
|
+
const began = pass.beginOcclusionQuery(reservation.queryIndex);
|
|
554
|
+
if (!began.ok) return began;
|
|
555
|
+
const ended = pass.endOcclusionQuery();
|
|
556
|
+
if (!ended.ok) return ended;
|
|
557
|
+
}
|
|
558
|
+
pending.endedCount = pending.reservations.length;
|
|
559
|
+
return ok(undefined);
|
|
560
|
+
}
|
|
561
|
+
const buffer = this.ensureProxyBuffer(pending.proxyFloatCount * Float32Array.BYTES_PER_ELEMENT);
|
|
562
|
+
if (buffer === undefined) {
|
|
563
|
+
return err(runtimeError('the backend must allocate the bounded proxy geometry buffer'));
|
|
564
|
+
}
|
|
565
|
+
const uploaded = this.device.queue.writeBuffer(
|
|
566
|
+
buffer,
|
|
567
|
+
0,
|
|
568
|
+
this.proxyScratch.subarray(0, pending.proxyFloatCount),
|
|
569
|
+
);
|
|
570
|
+
if (!uploaded.ok) return uploaded;
|
|
571
|
+
pass.setPipeline(pipeline);
|
|
572
|
+
pass.setVertexBuffer(0, buffer);
|
|
573
|
+
let vertexOffset = 0;
|
|
574
|
+
for (let index = 0; index < pending.reservations.length; index += 1) {
|
|
575
|
+
const vertices = pending.candidates[index]?.proxyVertices;
|
|
576
|
+
if (vertices === undefined || vertices.length === 0) continue;
|
|
577
|
+
const began = pass.beginOcclusionQuery(pending.reservations[index]?.queryIndex ?? 0);
|
|
578
|
+
if (!began.ok) return began;
|
|
579
|
+
pass.draw(vertices.length / 4, 1, vertexOffset / 4, 0);
|
|
580
|
+
const ended = pass.endOcclusionQuery();
|
|
581
|
+
if (!ended.ok) return ended;
|
|
582
|
+
vertexOffset += vertices.length;
|
|
583
|
+
}
|
|
584
|
+
pending.endedCount = pending.reservations.length;
|
|
585
|
+
return ok(undefined);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private ensureProxyBuffer(byteLength: number): Buffer | undefined {
|
|
589
|
+
if (this.proxyBuffer !== undefined) return this.proxyBuffer;
|
|
590
|
+
const created = this.device.createBuffer({
|
|
591
|
+
label: 'occlusion-bounds-proxy-geometry',
|
|
592
|
+
size: Math.max(16, PROXY_GEOMETRY_BYTES, byteLength),
|
|
593
|
+
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
594
|
+
});
|
|
595
|
+
if (!created.ok) return undefined;
|
|
596
|
+
this.proxyBuffer = created.value;
|
|
597
|
+
return created.value;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
private ensureProxyPipeline(sampleCount: 1 | 4): RenderPipeline | undefined {
|
|
601
|
+
if (this.proxyPipelineSampleCount === sampleCount && this.proxyPipeline !== undefined) {
|
|
602
|
+
return this.proxyPipeline;
|
|
603
|
+
}
|
|
604
|
+
if (this.shaderModuleFactory === undefined) return undefined;
|
|
605
|
+
const module = this.shaderModuleFactory.createShaderModule({
|
|
606
|
+
label: 'occlusion-bounds-proxy',
|
|
607
|
+
code: PROXY_BOUNDS_WGSL,
|
|
608
|
+
});
|
|
609
|
+
if (!module.ok) return undefined;
|
|
610
|
+
const created = this.device.createRenderPipeline({
|
|
611
|
+
label: 'occlusion-bounds-proxy',
|
|
612
|
+
layout: 'auto',
|
|
613
|
+
vertex: {
|
|
614
|
+
module: module.value,
|
|
615
|
+
entryPoint: 'vs_main',
|
|
616
|
+
buffers: [
|
|
617
|
+
{
|
|
618
|
+
arrayStride: 16,
|
|
619
|
+
stepMode: 'vertex',
|
|
620
|
+
attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x4' }],
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
},
|
|
624
|
+
// The proxy is recorded in the typed main pass, whose color attachment
|
|
625
|
+
// remains RGBA16Float. Keep the pass compatible while suppressing all
|
|
626
|
+
// color writes; occlusion only consumes depth samples.
|
|
627
|
+
fragment: {
|
|
628
|
+
module: module.value,
|
|
629
|
+
entryPoint: 'fs_main',
|
|
630
|
+
targets: [{ format: 'rgba16float', writeMask: 0 }],
|
|
631
|
+
},
|
|
632
|
+
primitive: { topology: 'triangle-list' },
|
|
633
|
+
depthStencil: {
|
|
634
|
+
format: 'depth24plus-stencil8',
|
|
635
|
+
depthWriteEnabled: false,
|
|
636
|
+
depthCompare: 'less-equal',
|
|
637
|
+
},
|
|
638
|
+
multisample: { count: sampleCount },
|
|
639
|
+
});
|
|
640
|
+
if (!created.ok) return undefined;
|
|
641
|
+
this.proxyPipeline = created.value;
|
|
642
|
+
this.proxyPipelineSampleCount = sampleCount;
|
|
643
|
+
return created.value;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const PROXY_BOUNDS_WGSL = `
|
|
648
|
+
@vertex
|
|
649
|
+
fn vs_main(@location(0) position: vec4f) -> @builtin(position) vec4f {
|
|
650
|
+
return position;
|
|
651
|
+
}
|
|
652
|
+
@fragment
|
|
653
|
+
fn fs_main() -> @location(0) vec4f {
|
|
654
|
+
return vec4f(0.0);
|
|
655
|
+
}
|
|
656
|
+
`;
|
|
657
|
+
|
|
658
|
+
function candidateKey(view: ViewKey, primitive: PrimitiveKey): string {
|
|
659
|
+
return `${viewKeyId(view)}|${primitiveKeyId(primitive)}`;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function finiteBounds(bounds: OcclusionBounds): boolean {
|
|
663
|
+
return [...bounds.min, ...bounds.max].every((value) => Number.isFinite(value));
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function runtimeError(hint: string): RhiError {
|
|
667
|
+
return new RhiErrorClass({
|
|
668
|
+
code: 'webgpu-runtime-error',
|
|
669
|
+
expected: 'RenderSystem owns one active occlusion candidate for the shared frame',
|
|
670
|
+
hint,
|
|
671
|
+
});
|
|
672
|
+
}
|