@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
package/src/record/frame.ts
CHANGED
|
@@ -21,19 +21,30 @@ import {
|
|
|
21
21
|
} from '@forgeax/engine-rhi';
|
|
22
22
|
import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
|
|
23
23
|
import { handleSlot, toShared } from '@forgeax/engine-types';
|
|
24
|
+
import { CUBE_CAMERA_FACE_ORDER } from '../components/cube-camera';
|
|
25
|
+
import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
|
|
24
26
|
import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
+
import {
|
|
28
|
+
ObservationUnavailableError,
|
|
29
|
+
type ObservationUnavailableReason,
|
|
30
|
+
RendererOperationError,
|
|
31
|
+
} from '../errors/render';
|
|
27
32
|
import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
|
|
28
33
|
import { GpuBuffer } from '../gpu-resource';
|
|
34
|
+
import { GPU_TEXTURE_USAGE_COPY_SRC } from '../gpu-texture-usage';
|
|
29
35
|
import {
|
|
30
36
|
GPU_BUFFER_USAGE_COPY_DST,
|
|
37
|
+
GPU_BUFFER_USAGE_MAP_READ,
|
|
31
38
|
GPU_BUFFER_USAGE_UNIFORM,
|
|
32
39
|
GPU_BUFFER_USAGE_VERTEX,
|
|
33
40
|
} from '../gpu-usage';
|
|
34
41
|
import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
|
|
42
|
+
import { inspectStandardLighting } from '../pipeline/standard-lighting/inspection';
|
|
43
|
+
import { standardLightingTopologySignature } from '../pipeline/standard-lighting/topology';
|
|
44
|
+
import { inspectPointShadow } from '../point-shadow-inspection';
|
|
35
45
|
import type {
|
|
36
46
|
CameraSnapshot,
|
|
47
|
+
CubeCameraSnapshot,
|
|
37
48
|
RenderRecordPhase,
|
|
38
49
|
SpotLightProjectorFrameContext,
|
|
39
50
|
VolumetricFogFrameContext,
|
|
@@ -48,6 +59,8 @@ import type {
|
|
|
48
59
|
SkylightSnapshot,
|
|
49
60
|
} from '../render-system-extract';
|
|
50
61
|
import type { PersistentGpuDrivenState } from '../scene/render-scene';
|
|
62
|
+
import type { OcclusionFrameProjection } from '../scene/visibility/occlusion-runtime';
|
|
63
|
+
import { SHADOW_ATLAS_DEFAULT_LAYERS } from '../shadow-atlas';
|
|
51
64
|
import {
|
|
52
65
|
getTransparentSortConfig,
|
|
53
66
|
TRANSPARENT_SORT_MODE_LAYER_Y,
|
|
@@ -63,8 +76,8 @@ import {
|
|
|
63
76
|
resolveSkyboxActive,
|
|
64
77
|
warnZeroLightStandard,
|
|
65
78
|
writeHdrpClusterAndSsaoBuffers,
|
|
66
|
-
writePointSpotLightBuffers,
|
|
67
79
|
writeShadowParamsBuffer,
|
|
80
|
+
writeSpotModifierTextures,
|
|
68
81
|
} from './frame-lighting';
|
|
69
82
|
import {
|
|
70
83
|
type BindGroupCounts,
|
|
@@ -72,9 +85,12 @@ import {
|
|
|
72
85
|
type DirectionalShadowWorldState,
|
|
73
86
|
type DispatchCounts,
|
|
74
87
|
type FrameObservationSource,
|
|
88
|
+
getTextureIdentity,
|
|
75
89
|
makeZeroCameraFallbackSnapshot,
|
|
90
|
+
type ReflectionFallbackReadbackRequest,
|
|
76
91
|
type RenderFrameState,
|
|
77
92
|
type ValidatedRenderable,
|
|
93
|
+
validateGraphTargetCaptureReadback,
|
|
78
94
|
worldEntityKey,
|
|
79
95
|
} from './frame-snapshot';
|
|
80
96
|
// feat-20260622-chunk-gpu-instancing-sprite-tilemap M1 / w4 (D-1 record-stage
|
|
@@ -223,12 +239,87 @@ import { computeSplitLdrSprite } from './main-pass-sprite-draws';
|
|
|
223
239
|
import { cleanPerEntityCache, ensureMeshSsboCapacity, uploadMeshSsboBatch } from './mesh-ssbo';
|
|
224
240
|
import { writeShadowCasterUniforms } from './shadow-pass';
|
|
225
241
|
import {
|
|
242
|
+
type CubeCaptureGraphState,
|
|
226
243
|
ensureCompiledFrameGraph,
|
|
227
244
|
executeCompiledFrameGraph,
|
|
228
245
|
type RenderFeatureGraphCandidate,
|
|
229
246
|
settleVolumetricFogGraphCandidate,
|
|
230
247
|
} from './typed-frame-graph';
|
|
231
|
-
import {
|
|
248
|
+
import {
|
|
249
|
+
CUBE_CAPTURE_VIEW_SLOT_BASE,
|
|
250
|
+
VIEW_UNIFORM_SLOT_STRIDE,
|
|
251
|
+
writePointsLinesViewUbo,
|
|
252
|
+
writeViewUbo,
|
|
253
|
+
} from './view-ubo';
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Resolve the GPU mesh view used by the optional production raster before its
|
|
257
|
+
* filtering pass runs. User meshes are pull-resident during record; relying
|
|
258
|
+
* only on the boot-time builtin map would make the first frame classify every
|
|
259
|
+
* imported mesh as a CPU fallback and would hide its LOD ranges from the GPU
|
|
260
|
+
* cull. Keep the map frame-local so the pipeline state remains the builtin
|
|
261
|
+
* compatibility owner while the residency cache remains the user-mesh SSOT.
|
|
262
|
+
*/
|
|
263
|
+
function gpuDrivenMeshesForFrame(
|
|
264
|
+
internals: RenderSystemInternals,
|
|
265
|
+
pipelineState: PipelineState,
|
|
266
|
+
worlds: readonly World[],
|
|
267
|
+
scene: PersistentGpuDrivenState | undefined,
|
|
268
|
+
): ReadonlyMap<number, MeshGpuHandles> {
|
|
269
|
+
const meshes = new Map(pipelineState.meshes);
|
|
270
|
+
if (scene === undefined) return meshes;
|
|
271
|
+
const cached = gpuDrivenMeshCache.get(scene.scene);
|
|
272
|
+
if (
|
|
273
|
+
cached !== undefined &&
|
|
274
|
+
cached.meshResidencyEpoch === internals.gpuStore.meshResidencyEpoch &&
|
|
275
|
+
cached.pipelineMeshes === pipelineState.meshes
|
|
276
|
+
) {
|
|
277
|
+
return cached.value;
|
|
278
|
+
}
|
|
279
|
+
for (const slot of scene.slots) {
|
|
280
|
+
const world = worlds[slot.snapshot.worldId];
|
|
281
|
+
if (world === undefined) continue;
|
|
282
|
+
const assetHandle = slot.snapshot.assetHandle;
|
|
283
|
+
const sharedHandle = toShared<'MeshAsset'>(assetHandle);
|
|
284
|
+
let gpu = internals.gpuStore.getMeshGpuHandles(sharedHandle, world);
|
|
285
|
+
if (gpu === undefined && !meshes.has(assetHandle)) {
|
|
286
|
+
const asset = resolveAssetHandle<MeshAsset>(world, sharedHandle);
|
|
287
|
+
if (asset.ok) {
|
|
288
|
+
const lodMeshes = slot.snapshot.lods?.map((lod) =>
|
|
289
|
+
internals.assets.lookup<MeshAsset>(lod.mesh),
|
|
290
|
+
);
|
|
291
|
+
const resolvedLodMeshes =
|
|
292
|
+
lodMeshes?.every((lod): lod is MeshAsset => lod?.kind === 'mesh') === true
|
|
293
|
+
? lodMeshes
|
|
294
|
+
: undefined;
|
|
295
|
+
const resident = internals.gpuStore.ensureResident(
|
|
296
|
+
sharedHandle,
|
|
297
|
+
asset.value,
|
|
298
|
+
world,
|
|
299
|
+
resolvedLodMeshes,
|
|
300
|
+
);
|
|
301
|
+
if (resident.ok) gpu = resident.value;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (gpu !== undefined) meshes.set(assetHandle, gpu);
|
|
305
|
+
}
|
|
306
|
+
const value = meshes;
|
|
307
|
+
gpuDrivenMeshCache.set(scene.scene, {
|
|
308
|
+
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
309
|
+
pipelineMeshes: pipelineState.meshes,
|
|
310
|
+
value,
|
|
311
|
+
});
|
|
312
|
+
return value;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const gpuDrivenMeshCache = new WeakMap<
|
|
316
|
+
object,
|
|
317
|
+
{
|
|
318
|
+
readonly meshResidencyEpoch: number;
|
|
319
|
+
readonly pipelineMeshes: ReadonlyMap<number, MeshGpuHandles>;
|
|
320
|
+
readonly value: ReadonlyMap<number, MeshGpuHandles>;
|
|
321
|
+
}
|
|
322
|
+
>();
|
|
232
323
|
|
|
233
324
|
/**
|
|
234
325
|
* A frame-local material table. Repeated snapshot objects share one UBO slot;
|
|
@@ -301,6 +392,67 @@ export function materialSlotCountForPrefix(
|
|
|
301
392
|
|
|
302
393
|
export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
|
|
303
394
|
|
|
395
|
+
export interface CubeCaptureFrameInput {
|
|
396
|
+
readonly snapshots: readonly CubeCameraSnapshot[];
|
|
397
|
+
readonly scheduler: import('../capture/scheduler').CubeCaptureScheduler;
|
|
398
|
+
readonly state: CubeCaptureGraphState;
|
|
399
|
+
readonly reflectionProbes?: import('./render-context').ReflectionProbeRecordState;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function prepareCubeCapture(
|
|
403
|
+
internals: RenderSystemInternals,
|
|
404
|
+
input: CubeCaptureFrameInput | undefined,
|
|
405
|
+
displayCamera: CameraSnapshot,
|
|
406
|
+
): readonly import('./typed-frame-graph').CubeCaptureGraphWork[] {
|
|
407
|
+
if (input === undefined) return [];
|
|
408
|
+
if (input.reflectionProbes === undefined) delete input.state.reflectionProbes;
|
|
409
|
+
else input.state.reflectionProbes = input.reflectionProbes.graph;
|
|
410
|
+
input.scheduler.beginFrame();
|
|
411
|
+
for (const snapshot of input.snapshots) {
|
|
412
|
+
const inspection = input.scheduler.inspect(snapshot.target);
|
|
413
|
+
if (inspection.candidateGeneration !== undefined) continue;
|
|
414
|
+
const requested = input.scheduler.request({
|
|
415
|
+
target: snapshot.target,
|
|
416
|
+
position: snapshot.position,
|
|
417
|
+
near: snapshot.near,
|
|
418
|
+
far: snapshot.far,
|
|
419
|
+
updateIntent: snapshot.updateIntent,
|
|
420
|
+
requestVersion: snapshot.requestVersion,
|
|
421
|
+
faceBudget: snapshot.faceBudget,
|
|
422
|
+
});
|
|
423
|
+
if (!requested.ok) internals.errorRegistry.fire(requested.error);
|
|
424
|
+
}
|
|
425
|
+
const work = input.scheduler.nextWork();
|
|
426
|
+
const physicalWork: import('./typed-frame-graph').CubeCaptureGraphWork[] = [];
|
|
427
|
+
for (const item of work) {
|
|
428
|
+
const physical = internals.getRenderTargetPhysical?.(item.target);
|
|
429
|
+
if (physical === undefined) continue;
|
|
430
|
+
const faceIndex = CUBE_CAMERA_FACE_ORDER.indexOf(item.face);
|
|
431
|
+
if (faceIndex < 0) continue;
|
|
432
|
+
const faceWorld = mat4.create();
|
|
433
|
+
mat4.invert(faceWorld, item.view);
|
|
434
|
+
physicalWork.push({
|
|
435
|
+
target: item.target,
|
|
436
|
+
faceIndex,
|
|
437
|
+
physical,
|
|
438
|
+
faceCamera: {
|
|
439
|
+
...displayCamera,
|
|
440
|
+
position: vec3.create(item.position[0] ?? 0, item.position[1] ?? 0, item.position[2] ?? 0),
|
|
441
|
+
world: faceWorld,
|
|
442
|
+
fov: Math.PI / 2,
|
|
443
|
+
aspect: 1,
|
|
444
|
+
near: item.near,
|
|
445
|
+
far: item.far,
|
|
446
|
+
projection: 'perspective',
|
|
447
|
+
},
|
|
448
|
+
viewBindGroupDynamicOffset:
|
|
449
|
+
(CUBE_CAPTURE_VIEW_SLOT_BASE + physicalWork.length) * VIEW_UNIFORM_SLOT_STRIDE,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
input.state.work = physicalWork;
|
|
453
|
+
return physicalWork;
|
|
454
|
+
}
|
|
455
|
+
|
|
304
456
|
const EMPTY_ENTITY_KEYS: ReadonlySet<number> = new Set<number>();
|
|
305
457
|
|
|
306
458
|
function runRecordProfilePhase<T>(
|
|
@@ -311,6 +463,135 @@ function runRecordProfilePhase<T>(
|
|
|
311
463
|
return runner === undefined ? action() : runner(phase, action);
|
|
312
464
|
}
|
|
313
465
|
|
|
466
|
+
function halfFloatToNumber(bits: number): number {
|
|
467
|
+
const sign = (bits & 0x8000) === 0 ? 1 : -1;
|
|
468
|
+
const exponent = (bits >>> 10) & 0x1f;
|
|
469
|
+
const fraction = bits & 0x03ff;
|
|
470
|
+
if (exponent === 0) return sign * 2 ** -14 * (fraction / 1024);
|
|
471
|
+
if (exponent === 0x1f) return fraction === 0 ? sign * Infinity : Number.NaN;
|
|
472
|
+
return sign * 2 ** (exponent - 15) * (1 + fraction / 1024);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function createReflectionFallbackReadbackRequest(
|
|
476
|
+
internals: RenderSystemInternals,
|
|
477
|
+
frameState: RenderFrameState,
|
|
478
|
+
): ReflectionFallbackReadbackRequest | undefined {
|
|
479
|
+
if (!frameState.reflectionFallbackDemand || frameState.reflectionFallbackReadback !== undefined) {
|
|
480
|
+
return undefined;
|
|
481
|
+
}
|
|
482
|
+
const graph = frameState.perFrameGraph;
|
|
483
|
+
if (graph === undefined || graph === null) return undefined;
|
|
484
|
+
const descriptor = graph.getColorTargetDescriptor('reflection-fallback-linear-hdr');
|
|
485
|
+
const texture = graph.getColorTargetTexture('reflection-fallback-linear-hdr');
|
|
486
|
+
if (
|
|
487
|
+
descriptor === undefined ||
|
|
488
|
+
texture === undefined ||
|
|
489
|
+
descriptor.format !== 'rgba16float' ||
|
|
490
|
+
descriptor.sample !== 1
|
|
491
|
+
) {
|
|
492
|
+
return undefined;
|
|
493
|
+
}
|
|
494
|
+
const bytesPerRow = Math.ceil((descriptor.size.width * 8) / 256) * 256;
|
|
495
|
+
const created = internals.device.createBuffer({
|
|
496
|
+
label: 'reflection-fallback-linear-hdr.readback',
|
|
497
|
+
size: bytesPerRow * descriptor.size.height,
|
|
498
|
+
usage: GPU_BUFFER_USAGE_COPY_DST | GPU_BUFFER_USAGE_MAP_READ,
|
|
499
|
+
mappedAtCreation: false,
|
|
500
|
+
});
|
|
501
|
+
if (!created.ok) {
|
|
502
|
+
internals.errorRegistry.fire(created.error);
|
|
503
|
+
return undefined;
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
name: 'reflection-fallback-linear-hdr',
|
|
507
|
+
buffer: created.value,
|
|
508
|
+
bytesPerRow,
|
|
509
|
+
width: descriptor.size.width,
|
|
510
|
+
height: descriptor.size.height,
|
|
511
|
+
expected: {
|
|
512
|
+
format: descriptor.format,
|
|
513
|
+
width: descriptor.size.width,
|
|
514
|
+
height: descriptor.size.height,
|
|
515
|
+
usage: GPU_TEXTURE_USAGE_COPY_SRC,
|
|
516
|
+
graphGeneration: graph.graphGeneration,
|
|
517
|
+
frameId: frameState.frameNumber,
|
|
518
|
+
textureIdentity: getTextureIdentity(texture),
|
|
519
|
+
},
|
|
520
|
+
encoded: false,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async function mapReflectionFallbackReadback(
|
|
525
|
+
internals: RenderSystemInternals,
|
|
526
|
+
request: ReflectionFallbackReadbackRequest,
|
|
527
|
+
allowZero: boolean,
|
|
528
|
+
): Promise<{
|
|
529
|
+
readonly linearHdr: readonly [number, number, number, number];
|
|
530
|
+
readonly hash: string;
|
|
531
|
+
readonly graphGeneration: number;
|
|
532
|
+
readonly textureIdentity: number;
|
|
533
|
+
}> {
|
|
534
|
+
try {
|
|
535
|
+
if (!request.encoded) throw new Error('reflection fallback copy was not encoded');
|
|
536
|
+
const mapped = await request.buffer.mapAsync(GPU_BUFFER_USAGE_MAP_READ);
|
|
537
|
+
if (!mapped.ok) throw mapped.error;
|
|
538
|
+
const range = mapped.value.getMappedRange();
|
|
539
|
+
if (!range.ok) throw range.error;
|
|
540
|
+
const bytes = new Uint8Array(range.value.slice(0));
|
|
541
|
+
mapped.value.unmap();
|
|
542
|
+
const validation = validateGraphTargetCaptureReadback({
|
|
543
|
+
bytes,
|
|
544
|
+
expectedByteLength: request.bytesPerRow * request.height,
|
|
545
|
+
allowZero,
|
|
546
|
+
});
|
|
547
|
+
if (!validation.ok) throw new Error(validation.code);
|
|
548
|
+
let linearHdr: readonly [number, number, number, number] = [0, 0, 0, 0];
|
|
549
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
550
|
+
for (let y = 0; y < request.height; y += 1) {
|
|
551
|
+
for (let x = 0; x < request.width; x += 1) {
|
|
552
|
+
const offset = y * request.bytesPerRow + x * 8;
|
|
553
|
+
const pixel = [0, 1, 2, 3].map((channel) =>
|
|
554
|
+
halfFloatToNumber(view.getUint16(offset + channel * 2, true)),
|
|
555
|
+
) as [number, number, number, number];
|
|
556
|
+
// The clear attachment carries alpha=1, so alpha alone is not
|
|
557
|
+
// evidence that the Standard BRDF wrote a fallback lobe. Decode the
|
|
558
|
+
// RGB channels from the mapped MRT and admit the first finite
|
|
559
|
+
// non-zero color sample only.
|
|
560
|
+
if (pixel.slice(0, 3).some((value) => Number.isFinite(value) && value !== 0)) {
|
|
561
|
+
linearHdr = pixel;
|
|
562
|
+
y = request.height;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// A cleared attachment has a non-zero alpha lane, so byte-level
|
|
568
|
+
// validation alone can incorrectly admit an otherwise empty MRT. When
|
|
569
|
+
// the selected producer is non-neutral, require an actual finite RGB
|
|
570
|
+
// sample before publishing a complete receipt; neutral rows explicitly
|
|
571
|
+
// retain the zero quartet through allowZero=true.
|
|
572
|
+
if (
|
|
573
|
+
!allowZero &&
|
|
574
|
+
!linearHdr.slice(0, 3).some((value) => Number.isFinite(value) && value !== 0)
|
|
575
|
+
) {
|
|
576
|
+
throw new Error('capture-readback-empty');
|
|
577
|
+
}
|
|
578
|
+
const subtle = globalThis.crypto?.subtle;
|
|
579
|
+
if (subtle === undefined) throw new Error('Web Crypto SHA-256 is unavailable');
|
|
580
|
+
const digest = await subtle.digest('SHA-256', bytes);
|
|
581
|
+
const hash = Array.from(new Uint8Array(digest), (value) =>
|
|
582
|
+
value.toString(16).padStart(2, '0'),
|
|
583
|
+
).join('');
|
|
584
|
+
return {
|
|
585
|
+
linearHdr,
|
|
586
|
+
hash: `sha256:${hash}`,
|
|
587
|
+
graphGeneration: request.expected.graphGeneration,
|
|
588
|
+
textureIdentity: request.expected.textureIdentity,
|
|
589
|
+
};
|
|
590
|
+
} finally {
|
|
591
|
+
internals.device.destroyBuffer(request.buffer);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
314
595
|
const VOLUMETRIC_FOG_PASS_COUNT = 4;
|
|
315
596
|
|
|
316
597
|
type VolumetricFogParamsSlot = 0 | 1;
|
|
@@ -619,16 +900,21 @@ function prepareVolumetricFogFrame(
|
|
|
619
900
|
// sub-froxel jitter. The temporal resolver consumes it without growing the
|
|
620
901
|
// public fog component or allocating another uniform buffer.
|
|
621
902
|
params[23] = frameState.frameNumber % 1024;
|
|
903
|
+
const selectedPointIndex =
|
|
904
|
+
selectedPoint === undefined
|
|
905
|
+
? -1
|
|
906
|
+
: lights.point.findIndex((light) => light.entity === selectedPoint.entity);
|
|
622
907
|
const selectedSpotIndex =
|
|
623
908
|
selectedSpot === undefined
|
|
624
909
|
? -1
|
|
625
910
|
: lights.spot.findIndex((light) => light.entity === selectedSpot.entity);
|
|
911
|
+
// Cluster light_data is one point/spot/rect array. Point slots are emitted
|
|
912
|
+
// first, so volume stores the global slot rather than a per-kind array index.
|
|
913
|
+
const selectedSpotClusterIndex =
|
|
914
|
+
selectedSpotIndex < 0 ? -1 : lights.point.length + selectedSpotIndex;
|
|
626
915
|
params.set(lightColor, 24);
|
|
627
|
-
params[19] =
|
|
628
|
-
|
|
629
|
-
? -1
|
|
630
|
-
: lights.point.findIndex((light) => light.entity === selectedPoint.entity);
|
|
631
|
-
params[27] = selectedSpotIndex;
|
|
916
|
+
params[19] = selectedPointIndex;
|
|
917
|
+
params[27] = selectedSpotClusterIndex;
|
|
632
918
|
params[28] = fog.fog.maxDistance;
|
|
633
919
|
params[29] = fog.fog.anisotropy;
|
|
634
920
|
params[30] =
|
|
@@ -739,6 +1025,8 @@ function prepareSpotLightProjectorFrame(
|
|
|
739
1025
|
texture: resident.value.texture.handle,
|
|
740
1026
|
view: resident.value.view,
|
|
741
1027
|
sampler: sampler.value,
|
|
1028
|
+
spotIndex: lights.spot.indexOf(source),
|
|
1029
|
+
lightSlotIndex: lights.point.length + lights.spot.indexOf(source),
|
|
742
1030
|
};
|
|
743
1031
|
}
|
|
744
1032
|
|
|
@@ -1023,7 +1311,6 @@ export function recordFrame(
|
|
|
1023
1311
|
skybox: SkyboxSnapshot | undefined,
|
|
1024
1312
|
skyboxCount: number,
|
|
1025
1313
|
postProcessParams: ReadonlyMap<string, Uint8Array>,
|
|
1026
|
-
fog: FogFrame | undefined,
|
|
1027
1314
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the
|
|
1028
1315
|
// full worlds[] list passed to `renderer.draw`, indexed by
|
|
1029
1316
|
// RenderableSnapshot.worldId so the record stage resolves each renderable's
|
|
@@ -1036,11 +1323,16 @@ export function recordFrame(
|
|
|
1036
1323
|
gpuDriven?: {
|
|
1037
1324
|
readonly owner: GpuDrivenProduction;
|
|
1038
1325
|
readonly scene: PersistentGpuDrivenState | undefined;
|
|
1039
|
-
readonly
|
|
1326
|
+
readonly onSceneDemand: (sceneRowsRequired: boolean) => void;
|
|
1327
|
+
readonly activeEntityKeys?: ReadonlySet<number>;
|
|
1328
|
+
readonly activeEntityRevision?: number;
|
|
1329
|
+
readonly telemetryCandidateCount?: number;
|
|
1330
|
+
readonly occlusion?: OcclusionFrameProjection;
|
|
1040
1331
|
},
|
|
1041
1332
|
renderReadLeases?: readonly RenderReadLease[],
|
|
1042
1333
|
featureGraphCandidate?: RenderFeatureGraphCandidate,
|
|
1043
1334
|
pointsLinesOwner?: import('./render-context').PointsLinesRecordOwner,
|
|
1335
|
+
cubeCapture?: CubeCaptureFrameInput,
|
|
1044
1336
|
environmentSignature = '',
|
|
1045
1337
|
fogSignature = '',
|
|
1046
1338
|
environmentReady = true,
|
|
@@ -1048,6 +1340,8 @@ export function recordFrame(
|
|
|
1048
1340
|
volumetricFog?: ExtractedVolumetricFog,
|
|
1049
1341
|
timingCapture?: GpuTimingCapture,
|
|
1050
1342
|
): boolean {
|
|
1343
|
+
frameState.reflectionFallbackObservationSource = undefined;
|
|
1344
|
+
frameState.reflectionFallbackCompletion = undefined;
|
|
1051
1345
|
// The `try / finally` wrapper advances `frameState.frameNumber` exactly
|
|
1052
1346
|
// once per `recordFrame` invocation regardless of which early-return
|
|
1053
1347
|
// branch is taken (camera missing, cap exceeded, pipeline pending,
|
|
@@ -1097,14 +1391,35 @@ export function recordFrame(
|
|
|
1097
1391
|
if (!camera) return false;
|
|
1098
1392
|
const pipelineState = internals.getPipelineState();
|
|
1099
1393
|
if (pipelineState === null) return false;
|
|
1394
|
+
if (pipelineState.device !== internals.device) {
|
|
1395
|
+
internals.errorRegistry.fire(
|
|
1396
|
+
new RendererOperationError('device-operation-failed', {
|
|
1397
|
+
operation: 'draw',
|
|
1398
|
+
cause: {
|
|
1399
|
+
code: 'pipeline-device-mismatch',
|
|
1400
|
+
expected: 'pipelineState.device === runtime.device',
|
|
1401
|
+
hint: 'discard stale pipeline state before recording the next frame',
|
|
1402
|
+
detail: {
|
|
1403
|
+
expected: 'pipelineState.device === runtime.device',
|
|
1404
|
+
hint: 'discard stale pipeline state before recording the next frame',
|
|
1405
|
+
},
|
|
1406
|
+
},
|
|
1407
|
+
}),
|
|
1408
|
+
);
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1100
1411
|
frameState.currentFrameObservationSource = undefined;
|
|
1412
|
+
if (frameState.reflectionFallbackReadback !== undefined) {
|
|
1413
|
+
internals.device.destroyBuffer(frameState.reflectionFallbackReadback.buffer);
|
|
1414
|
+
frameState.reflectionFallbackReadback = undefined;
|
|
1415
|
+
}
|
|
1101
1416
|
frameState.currentDirectionalShadowView = null;
|
|
1102
1417
|
frameState.currentSpotShadowView = null;
|
|
1103
1418
|
|
|
1104
1419
|
// Record-stage fold operator linear scan (groups transparent-sort entries
|
|
1105
1420
|
// into fold buckets + records the fold-eligible count metric). Extracted to
|
|
1106
1421
|
// computeFoldBuckets (M3/w18).
|
|
1107
|
-
const
|
|
1422
|
+
const lightingPreparation = runRecordProfilePhase(profilePhase, 'record/scene-state', () => {
|
|
1108
1423
|
const foldBuckets = runRecordProfilePhase(
|
|
1109
1424
|
profilePhase,
|
|
1110
1425
|
'record/scene-state/fold-buckets',
|
|
@@ -1113,13 +1428,13 @@ export function recordFrame(
|
|
|
1113
1428
|
|
|
1114
1429
|
// Multi-light warnings + point/spot shadow-snapshot pin + point-shadow atlas
|
|
1115
1430
|
// ensure + ExtractedLights three-arm destructure (directional fallback,
|
|
1116
|
-
// point/spot arrays, totalLightCount). Extracted to prepareFrameLighting
|
|
1431
|
+
// point/spot/Rect arrays, totalLightCount). Extracted to prepareFrameLighting
|
|
1117
1432
|
// (M3/w18) so recordFrame stays a skeleton.
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
'record/scene-state/lighting-prep',
|
|
1121
|
-
() => prepareFrameLighting(internals, frameState, lights),
|
|
1433
|
+
const lighting = runRecordProfilePhase(profilePhase, 'record/scene-state/lighting-prep', () =>
|
|
1434
|
+
prepareFrameLighting(internals, frameState, lights, camera, pipelineState),
|
|
1122
1435
|
);
|
|
1436
|
+
if (!lighting.ok) return lighting;
|
|
1437
|
+
const { light, totalLightCount, standard } = lighting.value;
|
|
1123
1438
|
|
|
1124
1439
|
runRecordProfilePhase(profilePhase, 'record/scene-state/ambient-resolution', () => {
|
|
1125
1440
|
// feat-20260520-skylight-ibl-cubemap M4 / t27 (AC-10 + F-4 nit):
|
|
@@ -1166,31 +1481,17 @@ export function recordFrame(
|
|
|
1166
1481
|
}
|
|
1167
1482
|
});
|
|
1168
1483
|
|
|
1169
|
-
// feat-20260608-cluster-lighting M5 / w21 + M6 / w23: HDRP per-frame CPU
|
|
1170
|
-
// cluster binning + light-data / cluster-grid / SSAO buffer uploads. Runs
|
|
1171
|
-
// only when the HDRP pipeline is active and there is at least one punctual
|
|
1172
|
-
// light. Extracted to a sub-function (M3/w18) so recordFrame stays a
|
|
1173
|
-
// skeleton; fail-soft error semantics are preserved verbatim.
|
|
1174
|
-
runRecordProfilePhase(profilePhase, 'record/scene-state/hdrp-cluster', () => {
|
|
1175
|
-
writeHdrpClusterAndSsaoBuffers(
|
|
1176
|
-
internals,
|
|
1177
|
-
frameState,
|
|
1178
|
-
camera,
|
|
1179
|
-
pointLights,
|
|
1180
|
-
spotLights,
|
|
1181
|
-
profilePhase,
|
|
1182
|
-
pipelineState.hdrpClusterMembershipPipeline !== null &&
|
|
1183
|
-
internals.device.caps?.compute === true,
|
|
1184
|
-
pipelineState.hdrpClusterMembershipBindGroupLayout,
|
|
1185
|
-
);
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
1484
|
// Zero-light standard-material once-warn (no Skylight + 0 direct light +
|
|
1189
1485
|
// >=1 lit material -> black). Extracted to warnZeroLightStandard (M3/w18).
|
|
1190
1486
|
warnZeroLightStandard(frameState, renderables, skylight, totalLightCount);
|
|
1191
1487
|
|
|
1192
|
-
return { foldBuckets, light };
|
|
1488
|
+
return { ok: true as const, value: { foldBuckets, light, standard } };
|
|
1193
1489
|
});
|
|
1490
|
+
if (!lightingPreparation.ok) {
|
|
1491
|
+
internals.errorRegistry.fire(lightingPreparation.error);
|
|
1492
|
+
return false;
|
|
1493
|
+
}
|
|
1494
|
+
const { foldBuckets, light, standard } = lightingPreparation.value;
|
|
1194
1495
|
|
|
1195
1496
|
// Case E (this commit): 0 renderables = legitimate scene (LO §1.1
|
|
1196
1497
|
// hello-window minimum semantic). Mirrors the Case C softening for
|
|
@@ -1276,13 +1577,32 @@ export function recordFrame(
|
|
|
1276
1577
|
|
|
1277
1578
|
let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
|
|
1278
1579
|
if (gpuDriven !== undefined) {
|
|
1580
|
+
const standardPbrArtifact = internals.getMaterialShaderArtifact?.(
|
|
1581
|
+
'forgeax::default-standard-pbr',
|
|
1582
|
+
);
|
|
1279
1583
|
const prepared = gpuDriven.owner.prepare({
|
|
1280
1584
|
scene: gpuDriven.scene,
|
|
1281
1585
|
camera,
|
|
1282
|
-
meshes: pipelineState.
|
|
1586
|
+
meshes: gpuDrivenMeshesForFrame(internals, pipelineState, worlds, gpuDriven.scene),
|
|
1283
1587
|
viewBindGroupLayout: pipelineState.viewBindGroupLayout,
|
|
1284
1588
|
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
1285
|
-
|
|
1589
|
+
// The built-in Standard graph (including its clustered transport) is
|
|
1590
|
+
// eligible for the GPU-driven LOD lane. The old HDRP boolean no
|
|
1591
|
+
// longer exists in the unified pipeline state, so it must not be
|
|
1592
|
+
// inferred from `standard.kind`.
|
|
1593
|
+
hdrp: false,
|
|
1594
|
+
...(gpuDriven.activeEntityKeys === undefined
|
|
1595
|
+
? {}
|
|
1596
|
+
: { activeEntityKeys: gpuDriven.activeEntityKeys }),
|
|
1597
|
+
...(gpuDriven.activeEntityRevision === undefined
|
|
1598
|
+
? {}
|
|
1599
|
+
: { activeEntityRevision: gpuDriven.activeEntityRevision }),
|
|
1600
|
+
...(gpuDriven.telemetryCandidateCount === undefined
|
|
1601
|
+
? {}
|
|
1602
|
+
: { telemetryCandidateCount: gpuDriven.telemetryCandidateCount }),
|
|
1603
|
+
...(gpuDriven.occlusion === undefined ? {} : { occlusion: gpuDriven.occlusion }),
|
|
1604
|
+
...(standardPbrArtifact === undefined ? {} : { standardPbrArtifact }),
|
|
1605
|
+
standardPbrPipelineState: pipelineState,
|
|
1286
1606
|
});
|
|
1287
1607
|
if (prepared.ok) preparedGpuDriven = prepared.value;
|
|
1288
1608
|
else if (prepared.error.code !== 'rhi-not-available')
|
|
@@ -1296,7 +1616,48 @@ export function recordFrame(
|
|
|
1296
1616
|
lights.pointShadow.length > 0 ||
|
|
1297
1617
|
lights.spot.some((spot) => spot.castShadow);
|
|
1298
1618
|
const gpuOwnsAllPrimaryRaster = preparedGpuDriven?.ownsAllRenderables === true;
|
|
1299
|
-
gpuDriven?.
|
|
1619
|
+
gpuDriven?.onSceneDemand(gpuDriven.owner.requiresSceneRows());
|
|
1620
|
+
|
|
1621
|
+
const cubeCaptureWork = prepareCubeCapture(internals, cubeCapture, camera);
|
|
1622
|
+
const reflectionFallbackCandidate = cubeCapture?.reflectionProbes?.fallbackDemand === true;
|
|
1623
|
+
|
|
1624
|
+
// Validate renderables before graph admission. Besides producing the draw
|
|
1625
|
+
// plan, this is the owner of per-frame cache eviction; running it before a
|
|
1626
|
+
// graph candidate can fail guarantees despawned instance buffers are
|
|
1627
|
+
// released even when the frame cannot be submitted.
|
|
1628
|
+
const gpuOwnedEntityKeys = preparedGpuDriven?.entityKeys ?? EMPTY_ENTITY_KEYS;
|
|
1629
|
+
const gpuDrivenWorldKeys = gpuDriven?.scene?.worldKeys;
|
|
1630
|
+
const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
1631
|
+
gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive && cubeCaptureWork.length === 0
|
|
1632
|
+
? []
|
|
1633
|
+
: validateRenderables(
|
|
1634
|
+
internals,
|
|
1635
|
+
world,
|
|
1636
|
+
worlds,
|
|
1637
|
+
pipelineState,
|
|
1638
|
+
frameState,
|
|
1639
|
+
renderables,
|
|
1640
|
+
transparentDispatch,
|
|
1641
|
+
cpuShadowCastersActive || cubeCaptureWork.length > 0
|
|
1642
|
+
? EMPTY_ENTITY_KEYS
|
|
1643
|
+
: gpuOwnedEntityKeys,
|
|
1644
|
+
gpuDrivenWorldKeys,
|
|
1645
|
+
),
|
|
1646
|
+
);
|
|
1647
|
+
gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedEntityKeys, gpuDrivenWorldKeys);
|
|
1648
|
+
cleanPerFrameCaches(internals, frameState, validated, gpuOwnedEntityKeys);
|
|
1649
|
+
|
|
1650
|
+
// S_fallback is the second output of the built-in Standard PBR BRDF. A
|
|
1651
|
+
// graph may still carry the detached owner tickets for inspection, but
|
|
1652
|
+
// the MRT is admitted only when every CPU-rendered primary row in this
|
|
1653
|
+
// frame uses that exact output contract. Unsupported compositions remain
|
|
1654
|
+
// fail-closed for SSR instead of submitting a device-invalid command.
|
|
1655
|
+
frameState.reflectionFallbackDemand =
|
|
1656
|
+
reflectionFallbackCandidate &&
|
|
1657
|
+
validated.length > 0 &&
|
|
1658
|
+
validated.every(
|
|
1659
|
+
(entry) => entry.source.material.materialShaderId === 'forgeax::default-standard-pbr',
|
|
1660
|
+
);
|
|
1300
1661
|
|
|
1301
1662
|
const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
|
|
1302
1663
|
ensureCompiledFrameGraph(
|
|
@@ -1310,14 +1671,68 @@ export function recordFrame(
|
|
|
1310
1671
|
graphShadowMapSize,
|
|
1311
1672
|
preparedGpuDriven,
|
|
1312
1673
|
featureGraphCandidate,
|
|
1674
|
+
cubeCapture?.state,
|
|
1313
1675
|
cameras.length === 0,
|
|
1314
1676
|
transmissionDemand,
|
|
1677
|
+
gpuDriven?.occlusion,
|
|
1315
1678
|
graphVolumetricFog,
|
|
1316
1679
|
volumeTopologyCandidate,
|
|
1680
|
+
standard,
|
|
1317
1681
|
),
|
|
1318
1682
|
);
|
|
1319
1683
|
if (preflightGraph === null) return false;
|
|
1320
1684
|
|
|
1685
|
+
// The compiled graph is the acceptance boundary for the prepared
|
|
1686
|
+
// Standard lighting declaration. Upload the same declaration only after
|
|
1687
|
+
// that graph is current; a failed candidate therefore cannot leave an old
|
|
1688
|
+
// graph consuming a new buffer generation.
|
|
1689
|
+
if (frameState.standardLightingGraphSignature !== standardLightingTopologySignature(standard)) {
|
|
1690
|
+
internals.errorRegistry.fire(
|
|
1691
|
+
new RendererOperationError('device-operation-failed', {
|
|
1692
|
+
operation: 'draw',
|
|
1693
|
+
cause: {
|
|
1694
|
+
code: 'standard-graph-generation-mismatch',
|
|
1695
|
+
expected: 'compiled graph and prepared Standard lighting share one topology signature',
|
|
1696
|
+
hint: 'retry the frame after the Standard graph candidate is accepted',
|
|
1697
|
+
detail: {
|
|
1698
|
+
expected: standardLightingTopologySignature(standard),
|
|
1699
|
+
actual: frameState.standardLightingGraphSignature,
|
|
1700
|
+
},
|
|
1701
|
+
},
|
|
1702
|
+
}),
|
|
1703
|
+
);
|
|
1704
|
+
return false;
|
|
1705
|
+
}
|
|
1706
|
+
if (standard.kind === 'clustered') {
|
|
1707
|
+
// The clustered HDRP payload owns the direct-light slot upload, so it
|
|
1708
|
+
// must also drive the shared Spot IES/Cookie resource upload from the
|
|
1709
|
+
// authoritative extracted-light corpus. The prepared local carrier can
|
|
1710
|
+
// omit the source payload on this path, leaving the shader bound to its
|
|
1711
|
+
// white Cookie fallback even though the authored handle is valid.
|
|
1712
|
+
writeSpotModifierTextures(internals, pipelineState, lights.spot);
|
|
1713
|
+
const clusterWrite = runRecordProfilePhase(
|
|
1714
|
+
profilePhase,
|
|
1715
|
+
'record/scene-state/hdrp-cluster',
|
|
1716
|
+
() =>
|
|
1717
|
+
writeHdrpClusterAndSsaoBuffers(
|
|
1718
|
+
internals,
|
|
1719
|
+
frameState,
|
|
1720
|
+
camera,
|
|
1721
|
+
standard.prepared,
|
|
1722
|
+
standard.transport,
|
|
1723
|
+
profilePhase,
|
|
1724
|
+
pipelineState.hdrpClusterMembershipPipeline !== null,
|
|
1725
|
+
pipelineState.hdrpClusterMembershipBindGroupLayout,
|
|
1726
|
+
preparedSpotLightProjector?.lightSlotIndex,
|
|
1727
|
+
pipelineState,
|
|
1728
|
+
),
|
|
1729
|
+
);
|
|
1730
|
+
if (!clusterWrite.ok) {
|
|
1731
|
+
internals.errorRegistry.fire(clusterWrite.error);
|
|
1732
|
+
return false;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1321
1736
|
// Acquire the swap-chain texture + colour view + target dimensions (with
|
|
1322
1737
|
// one reconfigure-and-retry on surface-outdated). Extracted to
|
|
1323
1738
|
// acquireSwapChainTarget (M3/w18); returns null on unrecoverable failure
|
|
@@ -1332,78 +1747,58 @@ export function recordFrame(
|
|
|
1332
1747
|
const targetW = swapTarget.targetW;
|
|
1333
1748
|
const targetH = swapTarget.targetH;
|
|
1334
1749
|
|
|
1335
|
-
const
|
|
1750
|
+
const previousTemporalView = frameState.lastSuccessfulTemporalView;
|
|
1751
|
+
const currentUnjitteredViewProjection = mat4.multiply(
|
|
1752
|
+
mat4.create(),
|
|
1753
|
+
computeProjectionMatrix(camera),
|
|
1754
|
+
computeViewMatrix(camera),
|
|
1755
|
+
);
|
|
1756
|
+
const currentCameraPosition = [
|
|
1757
|
+
camera.position[0] ?? 0,
|
|
1758
|
+
camera.position[1] ?? 0,
|
|
1759
|
+
camera.position[2] ?? 0,
|
|
1760
|
+
] as const;
|
|
1761
|
+
const temporalCandidate = createTemporalView({
|
|
1762
|
+
antialias: camera.antialias,
|
|
1763
|
+
width: Math.max(1, targetW),
|
|
1764
|
+
height: Math.max(1, targetH),
|
|
1765
|
+
frameIndex: frameState.successfulTemporalFrameIndex ?? 0,
|
|
1766
|
+
viewIdentity: `camera:${camera.entityKey ?? 0}`,
|
|
1767
|
+
environmentSignature,
|
|
1768
|
+
fogSignature,
|
|
1769
|
+
deviceGeneration: internals.deviceScope?.generation ?? 0,
|
|
1770
|
+
...(camera.historyVersion === undefined ? {} : { historyVersion: camera.historyVersion }),
|
|
1771
|
+
historyValid: false,
|
|
1772
|
+
currentUnjitteredViewProjection,
|
|
1773
|
+
currentCameraPosition,
|
|
1774
|
+
});
|
|
1775
|
+
const resetReason =
|
|
1336
1776
|
camera.antialias === 'taa' && environmentReady
|
|
1337
|
-
? (
|
|
1338
|
-
const created = createTemporalView({
|
|
1339
|
-
antialias: camera.antialias,
|
|
1340
|
-
width: targetW,
|
|
1341
|
-
height: targetH,
|
|
1342
|
-
frameIndex: frameState.successfulTemporalFrameIndex ?? 0,
|
|
1343
|
-
viewIdentity: `camera:${camera.entityKey ?? 0}`,
|
|
1344
|
-
environmentSignature,
|
|
1345
|
-
fogSignature,
|
|
1346
|
-
deviceGeneration: internals.deviceScope.generation,
|
|
1347
|
-
...(camera.historyVersion === undefined
|
|
1348
|
-
? {}
|
|
1349
|
-
: { historyVersion: camera.historyVersion }),
|
|
1350
|
-
});
|
|
1351
|
-
const previous = frameState.lastSuccessfulTemporalView;
|
|
1352
|
-
const currentUnjitteredViewProjection = mat4.multiply(
|
|
1353
|
-
mat4.create(),
|
|
1354
|
-
computeProjectionMatrix(camera),
|
|
1355
|
-
computeViewMatrix(camera),
|
|
1356
|
-
);
|
|
1357
|
-
const resetCandidate = createTemporalView({
|
|
1358
|
-
antialias: created.input.antialias,
|
|
1359
|
-
width: created.input.width,
|
|
1360
|
-
height: created.input.height,
|
|
1361
|
-
frameIndex: created.input.frameIndex,
|
|
1362
|
-
historyVersion: created.input.historyVersion,
|
|
1363
|
-
viewIdentity: created.input.viewIdentity,
|
|
1364
|
-
environmentSignature,
|
|
1365
|
-
fogSignature,
|
|
1366
|
-
deviceGeneration: internals.deviceScope.generation,
|
|
1367
|
-
cameraCut: created.input.cameraCut,
|
|
1368
|
-
historyValid: false,
|
|
1369
|
-
currentUnjitteredViewProjection,
|
|
1370
|
-
});
|
|
1371
|
-
const resetReason = resolveTemporalReset(previous, resetCandidate);
|
|
1372
|
-
return createTemporalView({
|
|
1373
|
-
...resetCandidate.input,
|
|
1374
|
-
frameIndex: resetReason === undefined ? resetCandidate.input.frameIndex : 0,
|
|
1375
|
-
historyValid: previous !== undefined && resetReason === undefined,
|
|
1376
|
-
currentUnjitteredViewProjection,
|
|
1377
|
-
resetReason,
|
|
1378
|
-
...(resetReason !== undefined ||
|
|
1379
|
-
previous?.currentUnjitteredViewProjection === undefined
|
|
1380
|
-
? {}
|
|
1381
|
-
: {
|
|
1382
|
-
previousUnjitteredViewProjection: previous.currentUnjitteredViewProjection,
|
|
1383
|
-
}),
|
|
1384
|
-
});
|
|
1385
|
-
})()
|
|
1777
|
+
? resolveTemporalReset(previousTemporalView, temporalCandidate)
|
|
1386
1778
|
: undefined;
|
|
1779
|
+
const canReusePrevious = previousTemporalView !== undefined && resetReason === undefined;
|
|
1780
|
+
const temporalView = createTemporalView({
|
|
1781
|
+
...temporalCandidate.input,
|
|
1782
|
+
frameIndex: resetReason === undefined ? temporalCandidate.input.frameIndex : 0,
|
|
1783
|
+
historyValid: camera.antialias === 'taa' && environmentReady && canReusePrevious,
|
|
1784
|
+
resetReason,
|
|
1785
|
+
...(canReusePrevious && previousTemporalView.currentUnjitteredViewProjection !== undefined
|
|
1786
|
+
? {
|
|
1787
|
+
previousUnjitteredViewProjection: previousTemporalView.currentUnjitteredViewProjection,
|
|
1788
|
+
}
|
|
1789
|
+
: {}),
|
|
1790
|
+
...(canReusePrevious && previousTemporalView.currentCameraPosition !== undefined
|
|
1791
|
+
? { previousCameraPosition: previousTemporalView.currentCameraPosition }
|
|
1792
|
+
: {}),
|
|
1793
|
+
});
|
|
1387
1794
|
frameState.pendingTemporalCommit =
|
|
1388
1795
|
cameras.length === 0 || !environmentReady || internals.deviceScope === undefined
|
|
1389
1796
|
? { kind: 'none' }
|
|
1390
|
-
: temporalView ===
|
|
1391
|
-
? {
|
|
1392
|
-
|
|
1393
|
-
view: createTemporalView({
|
|
1394
|
-
antialias: camera.antialias,
|
|
1395
|
-
width: targetW,
|
|
1396
|
-
height: targetH,
|
|
1397
|
-
frameIndex: frameState.successfulTemporalFrameIndex ?? 0,
|
|
1398
|
-
viewIdentity: `camera:${camera.entityKey ?? 0}`,
|
|
1399
|
-
environmentSignature,
|
|
1400
|
-
fogSignature,
|
|
1401
|
-
deviceGeneration: internals.deviceScope.generation,
|
|
1402
|
-
}),
|
|
1403
|
-
}
|
|
1404
|
-
: { kind: 'taa', view: temporalView };
|
|
1797
|
+
: temporalView.mode === 'taa'
|
|
1798
|
+
? { kind: 'taa', view: temporalView }
|
|
1799
|
+
: { kind: 'off', view: temporalView };
|
|
1405
1800
|
const recordCamera: CameraSnapshot =
|
|
1406
|
-
temporalView ===
|
|
1801
|
+
temporalView.mode === 'taa' ? { ...camera, temporal: temporalView } : camera;
|
|
1407
1802
|
let activeVolumetricFogContext: VolumetricFogFrameContext | undefined;
|
|
1408
1803
|
let activeVolumetricFogSignature: VolumeTemporalSignature | undefined;
|
|
1409
1804
|
if (graphVolumetricFog?.status === 'available' && baseVolumetricFogContext !== undefined) {
|
|
@@ -1501,41 +1896,6 @@ export function recordFrame(
|
|
|
1501
1896
|
const geometryColorResolveView: TextureView | null = null;
|
|
1502
1897
|
const ldrSpriteColorView: TextureView | null = null;
|
|
1503
1898
|
|
|
1504
|
-
// Validate renderable handles + collect render plan. Empty `renderables`
|
|
1505
|
-
// input or all-unregistered handles both produce `validatedOrdered.length === 0`,
|
|
1506
|
-
// which is the Case E (clear-pass-only) path.
|
|
1507
|
-
//
|
|
1508
|
-
// feat-20260520-2d-sprite-layer-mvp M-3 / w25 (@new-surface): per-entry
|
|
1509
|
-
// `renderableIndex` tracks the position into the original renderables[]
|
|
1510
|
-
// array so the bucket-aware reordering below can correlate
|
|
1511
|
-
// transparent-sort output (TransparentEntry.renderableIndex) back to
|
|
1512
|
-
// the validated row.
|
|
1513
|
-
// bug-20260527-renderstate-pipeline-dispatch-gap D-4:
|
|
1514
|
-
// build a renderableIndex -> renderState map from dispatch entries
|
|
1515
|
-
// so each ValidatedRenderable carries its per-material renderState
|
|
1516
|
-
// override without an O(n^2) back-scan in the draw loop.
|
|
1517
|
-
const gpuOwnedEntityKeys = preparedGpuDriven?.entityKeys ?? EMPTY_ENTITY_KEYS;
|
|
1518
|
-
const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
1519
|
-
gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive
|
|
1520
|
-
? []
|
|
1521
|
-
: validateRenderables(
|
|
1522
|
-
internals,
|
|
1523
|
-
world,
|
|
1524
|
-
worlds,
|
|
1525
|
-
pipelineState,
|
|
1526
|
-
frameState,
|
|
1527
|
-
renderables,
|
|
1528
|
-
transparentDispatch,
|
|
1529
|
-
cpuShadowCastersActive ? EMPTY_ENTITY_KEYS : gpuOwnedEntityKeys,
|
|
1530
|
-
),
|
|
1531
|
-
);
|
|
1532
|
-
gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedEntityKeys);
|
|
1533
|
-
|
|
1534
|
-
// Per-frame cache clean-up: drop per-entity BG cache entries + instance
|
|
1535
|
-
// buffers whose entityKey is absent from the current validated set (despawn
|
|
1536
|
-
// eviction). Extracted to cleanPerFrameCaches (M3/w18).
|
|
1537
|
-
cleanPerFrameCaches(internals, frameState, validated, gpuOwnedEntityKeys);
|
|
1538
|
-
|
|
1539
1899
|
// Dispatch-ordered render plan: reorder validated renderables to dispatch
|
|
1540
1900
|
// order, run the mesh-SSBO capacity gate (graceful truncation), and build
|
|
1541
1901
|
// the fold dispatch plan. Extracted to buildDispatchPlan (M3/w18).
|
|
@@ -1605,8 +1965,8 @@ export function recordFrame(
|
|
|
1605
1965
|
light,
|
|
1606
1966
|
lights,
|
|
1607
1967
|
frameState.spotShadowSnapshots,
|
|
1608
|
-
fog,
|
|
1609
1968
|
temporalView,
|
|
1969
|
+
preparedSpotLightProjector?.spotIndex,
|
|
1610
1970
|
);
|
|
1611
1971
|
if (pipelineState.pointsLinesViewBuffer !== undefined) {
|
|
1612
1972
|
writePointsLinesViewUbo(
|
|
@@ -1623,11 +1983,6 @@ export function recordFrame(
|
|
|
1623
1983
|
lights,
|
|
1624
1984
|
);
|
|
1625
1985
|
|
|
1626
|
-
// Per-frame full rewrite of the PointLight + SpotLight std430 storage
|
|
1627
|
-
// buffers (header + first-slice cap N=4 slots). Extracted to
|
|
1628
|
-
// writePointSpotLightBuffers (M3/w18).
|
|
1629
|
-
writePointSpotLightBuffers(internals, pipelineState, lights);
|
|
1630
|
-
|
|
1631
1986
|
// Per-renderable entity_world upload (batched): all N mat4+normalMatrix
|
|
1632
1987
|
// slots assembled into a single contiguous scratch buffer, then flushed
|
|
1633
1988
|
// as one writeBuffer call. Extracted to mesh-ssbo.ts (M3/w21) so its
|
|
@@ -1676,10 +2031,38 @@ export function recordFrame(
|
|
|
1676
2031
|
validated.length > 0 || preparedGpuDriven !== undefined,
|
|
1677
2032
|
bindGroupCounts,
|
|
1678
2033
|
undefined,
|
|
1679
|
-
|
|
2034
|
+
true,
|
|
2035
|
+
standard,
|
|
1680
2036
|
),
|
|
1681
2037
|
);
|
|
1682
2038
|
|
|
2039
|
+
for (const capture of cubeCaptureWork) {
|
|
2040
|
+
writeViewUbo(
|
|
2041
|
+
internals.device.queue,
|
|
2042
|
+
pipelineState.viewUniformBuffer,
|
|
2043
|
+
capture.faceCamera,
|
|
2044
|
+
light,
|
|
2045
|
+
lights,
|
|
2046
|
+
frameState.spotShadowSnapshots,
|
|
2047
|
+
capture.viewBindGroupDynamicOffset,
|
|
2048
|
+
preparedSpotLightProjector?.spotIndex,
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
for (const probe of cubeCapture?.reflectionProbes?.graph.work ?? []) {
|
|
2052
|
+
if (probe.faceCamera === undefined || probe.viewBindGroupDynamicOffset === undefined)
|
|
2053
|
+
continue;
|
|
2054
|
+
writeViewUbo(
|
|
2055
|
+
internals.device.queue,
|
|
2056
|
+
pipelineState.viewUniformBuffer,
|
|
2057
|
+
probe.faceCamera,
|
|
2058
|
+
light,
|
|
2059
|
+
lights,
|
|
2060
|
+
frameState.spotShadowSnapshots,
|
|
2061
|
+
probe.viewBindGroupDynamicOffset,
|
|
2062
|
+
preparedSpotLightProjector?.spotIndex,
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
|
|
1683
2066
|
// ── feat-20260529-rendergraph-pass-abstraction M4 / w13b ───────────
|
|
1684
2067
|
// The compiled typed graph is the sole per-frame pass owner.
|
|
1685
2068
|
//
|
|
@@ -1711,6 +2094,7 @@ export function recordFrame(
|
|
|
1711
2094
|
assets: internals.assets,
|
|
1712
2095
|
world,
|
|
1713
2096
|
gpuDrivenEntityKeys: gpuOwnedEntityKeys,
|
|
2097
|
+
...(gpuDrivenWorldKeys === undefined ? {} : { gpuDrivenWorldKeys }),
|
|
1714
2098
|
store: internals.gpuStore,
|
|
1715
2099
|
pipelineState,
|
|
1716
2100
|
bloomResources: pipelineState.perPassResources.getBloomResources?.() ?? null,
|
|
@@ -1755,16 +2139,34 @@ export function recordFrame(
|
|
|
1755
2139
|
dispatch: transparentDispatch,
|
|
1756
2140
|
hdrpClusterBindGroup,
|
|
1757
2141
|
hdrpClusterMembershipBindGroup,
|
|
2142
|
+
...(gpuDriven?.occlusion === undefined ? {} : { occlusion: gpuDriven.occlusion }),
|
|
2143
|
+
standardLighting: standard,
|
|
1758
2144
|
foldDispatchPlan,
|
|
1759
2145
|
materialSlotIndices,
|
|
1760
2146
|
materialSlots,
|
|
1761
2147
|
materialSlotOwners,
|
|
1762
2148
|
materialSlotCount,
|
|
2149
|
+
...(cubeCapture?.reflectionProbes === undefined
|
|
2150
|
+
? {}
|
|
2151
|
+
: { reflectionProbes: cubeCapture.reflectionProbes }),
|
|
1763
2152
|
pointsLines: pointsLinesOwner,
|
|
1764
2153
|
materialBgAssemblyCache: frameState.materialBgAssemblyCache,
|
|
1765
2154
|
directionalShadowCacheReuse: directionalShadowCache.reuse,
|
|
1766
2155
|
...(profilePhase !== undefined ? { profilePhase } : {}),
|
|
1767
2156
|
};
|
|
2157
|
+
const fallbackReadbackRequest = createReflectionFallbackReadbackRequest(internals, frameState);
|
|
2158
|
+
if (fallbackReadbackRequest !== undefined) {
|
|
2159
|
+
frameState.reflectionFallbackReadback = fallbackReadbackRequest;
|
|
2160
|
+
}
|
|
2161
|
+
let completion: Promise<unknown> | undefined;
|
|
2162
|
+
let fallbackReadback:
|
|
2163
|
+
| Promise<{
|
|
2164
|
+
readonly linearHdr: readonly [number, number, number, number];
|
|
2165
|
+
readonly hash: string;
|
|
2166
|
+
readonly graphGeneration: number;
|
|
2167
|
+
readonly textureIdentity: number;
|
|
2168
|
+
}>
|
|
2169
|
+
| undefined;
|
|
1768
2170
|
const stagedTemporalFrame = {
|
|
1769
2171
|
frameId: frameState.frameNumber,
|
|
1770
2172
|
currentViewProjection: new Float32Array(currentViewProjection),
|
|
@@ -1788,9 +2190,65 @@ export function recordFrame(
|
|
|
1788
2190
|
profilePhase === undefined
|
|
1789
2191
|
? undefined
|
|
1790
2192
|
: (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
|
|
2193
|
+
gpuDriven === undefined
|
|
2194
|
+
? undefined
|
|
2195
|
+
: {
|
|
2196
|
+
afterGraphExecute: () => gpuDriven.occlusion?.recordResolve(encoder) ?? ok(undefined),
|
|
2197
|
+
onSubmitted: () => {
|
|
2198
|
+
gpuDriven.occlusion?.commit(true, frameState.frameNumber, profilePhase);
|
|
2199
|
+
},
|
|
2200
|
+
onAborted: () => {
|
|
2201
|
+
gpuDriven.occlusion?.commit(false, frameState.frameNumber);
|
|
2202
|
+
},
|
|
2203
|
+
},
|
|
2204
|
+
cubeCaptureWork.length === 0 ? undefined : cubeCaptureWork.map((work) => work.faceIndex),
|
|
2205
|
+
(done) => {
|
|
2206
|
+
completion = done;
|
|
2207
|
+
if (fallbackReadbackRequest !== undefined) {
|
|
2208
|
+
fallbackReadback = done.then(
|
|
2209
|
+
() =>
|
|
2210
|
+
mapReflectionFallbackReadback(
|
|
2211
|
+
internals,
|
|
2212
|
+
fallbackReadbackRequest,
|
|
2213
|
+
cubeCapture?.reflectionProbes?.fallbackHasNonNeutral !== true,
|
|
2214
|
+
),
|
|
2215
|
+
(cause) => {
|
|
2216
|
+
internals.device.destroyBuffer(fallbackReadbackRequest.buffer);
|
|
2217
|
+
throw cause;
|
|
2218
|
+
},
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
},
|
|
1791
2222
|
timingCapture,
|
|
1792
2223
|
),
|
|
1793
2224
|
);
|
|
2225
|
+
if (cubeCapture !== undefined && cubeCaptureWork.length > 0) {
|
|
2226
|
+
const completed = cubeCapture.scheduler.completeSubmission(submitted, completion);
|
|
2227
|
+
if (!completed.ok) internals.errorRegistry.fire(completed.error);
|
|
2228
|
+
}
|
|
2229
|
+
if (!submitted && fallbackReadbackRequest !== undefined) {
|
|
2230
|
+
internals.device.destroyBuffer(fallbackReadbackRequest.buffer);
|
|
2231
|
+
}
|
|
2232
|
+
const reflectionFallbackCompletion = cubeCapture?.reflectionProbes?.completeSubmission(
|
|
2233
|
+
submitted,
|
|
2234
|
+
completion,
|
|
2235
|
+
fallbackReadbackRequest === undefined || fallbackReadback === undefined
|
|
2236
|
+
? undefined
|
|
2237
|
+
: {
|
|
2238
|
+
format: fallbackReadbackRequest.expected.format,
|
|
2239
|
+
size: {
|
|
2240
|
+
width: fallbackReadbackRequest.expected.width,
|
|
2241
|
+
height: fallbackReadbackRequest.expected.height,
|
|
2242
|
+
},
|
|
2243
|
+
frameId: fallbackReadbackRequest.expected.frameId,
|
|
2244
|
+
graphGeneration: fallbackReadbackRequest.expected.graphGeneration,
|
|
2245
|
+
textureIdentity: fallbackReadbackRequest.expected.textureIdentity,
|
|
2246
|
+
readback: fallbackReadback,
|
|
2247
|
+
},
|
|
2248
|
+
frameState.reflectionFallbackDemand,
|
|
2249
|
+
);
|
|
2250
|
+
frameState.reflectionFallbackCompletion = reflectionFallbackCompletion;
|
|
2251
|
+
frameState.reflectionFallbackObservationSource = undefined;
|
|
1794
2252
|
if (submitted && !directionalShadowCache.reuse) {
|
|
1795
2253
|
frameState.directionalShadowCache = frameState.directionalShadowCacheRecorded
|
|
1796
2254
|
? directionalShadowCache.next
|
|
@@ -1837,6 +2295,11 @@ export function recordFrame(
|
|
|
1837
2295
|
frameState.volumetricFogAccepted = volumetricFog;
|
|
1838
2296
|
frameState.volumetricFogAcceptedContext = preparedVolumetricFog;
|
|
1839
2297
|
}
|
|
2298
|
+
frameState.standardLightingInspection = inspectStandardLighting(standard);
|
|
2299
|
+
frameState.pointShadowInspection = inspectPointShadow(
|
|
2300
|
+
lights.pointShadow,
|
|
2301
|
+
SHADOW_ATLAS_DEFAULT_LAYERS,
|
|
2302
|
+
);
|
|
1840
2303
|
}
|
|
1841
2304
|
return submitted;
|
|
1842
2305
|
} finally {
|
|
@@ -1857,9 +2320,9 @@ export function recordFrame(
|
|
|
1857
2320
|
);
|
|
1858
2321
|
}
|
|
1859
2322
|
frameState.frameNumber += 1;
|
|
1860
|
-
// feat-20260608-cluster-lighting M5 / w22: clear
|
|
2323
|
+
// feat-20260608-cluster-lighting M5 / w22: clear Standard once-per-frame fired
|
|
1861
2324
|
// set so the next frame re-fires if the condition persists.
|
|
1862
|
-
frameState.
|
|
2325
|
+
frameState.standardOncePerFrameFired.clear();
|
|
1863
2326
|
}
|
|
1864
2327
|
}
|
|
1865
2328
|
|
|
@@ -1871,7 +2334,7 @@ interface DirectionalShadowCacheDecision {
|
|
|
1871
2334
|
function readWorldState(lease: RenderReadLease): DirectionalShadowWorldState {
|
|
1872
2335
|
return {
|
|
1873
2336
|
worldIdentity: lease.worldIdentity,
|
|
1874
|
-
|
|
2337
|
+
version: lease.captureVersion(),
|
|
1875
2338
|
};
|
|
1876
2339
|
}
|
|
1877
2340
|
|
|
@@ -1893,16 +2356,14 @@ function sameWorldState(
|
|
|
1893
2356
|
if (cachedState.worldIdentity !== lease.worldIdentity) return false;
|
|
1894
2357
|
let changes: ReturnType<RenderReadLease['readChanges']>;
|
|
1895
2358
|
try {
|
|
1896
|
-
changes = lease.readChanges(cachedState.
|
|
2359
|
+
changes = lease.readChanges(cachedState.version);
|
|
1897
2360
|
} catch {
|
|
1898
|
-
//
|
|
1899
|
-
//
|
|
1900
|
-
// rebuild the atlas from the current lease rather than reaching into
|
|
1901
|
-
// World for a second mutation clock.
|
|
2361
|
+
// Treat malformed or foreign version evidence as stale and rebuild the
|
|
2362
|
+
// atlas rather than reaching through World for a second mutation clock.
|
|
1902
2363
|
return false;
|
|
1903
2364
|
}
|
|
1904
2365
|
if (changes.status !== 'ok') return false;
|
|
1905
|
-
if (changes.world.
|
|
2366
|
+
if (changes.world.changedComponentIds.length !== 0 || changes.sharedRefs.records.length !== 0) {
|
|
1906
2367
|
return false;
|
|
1907
2368
|
}
|
|
1908
2369
|
}
|
|
@@ -1927,6 +2388,20 @@ function sameLightViewProj(
|
|
|
1927
2388
|
return true;
|
|
1928
2389
|
}
|
|
1929
2390
|
|
|
2391
|
+
function sameDirectionalShadowQuality(
|
|
2392
|
+
previous: DirectionalShadowQuality,
|
|
2393
|
+
next: DirectionalShadowQuality,
|
|
2394
|
+
): boolean {
|
|
2395
|
+
if (previous.kind !== next.kind) return false;
|
|
2396
|
+
if (previous.kind === 'pcf' && next.kind === 'pcf') return previous.kernel === next.kernel;
|
|
2397
|
+
if (previous.kind !== 'pcss' || next.kind !== 'pcss') return false;
|
|
2398
|
+
return (
|
|
2399
|
+
previous.preset === next.preset &&
|
|
2400
|
+
previous.angularRadiusRadians === next.angularRadiusRadians &&
|
|
2401
|
+
previous.maxPenumbraTexels === next.maxPenumbraTexels
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2404
|
+
|
|
1930
2405
|
function prepareDirectionalShadowCache(
|
|
1931
2406
|
internals: RenderSystemInternals,
|
|
1932
2407
|
frameState: RenderFrameState,
|
|
@@ -1946,6 +2421,7 @@ function prepareDirectionalShadowCache(
|
|
|
1946
2421
|
cascadeCount === undefined ||
|
|
1947
2422
|
cascadeCount <= 0 ||
|
|
1948
2423
|
lightViewProj === undefined ||
|
|
2424
|
+
lights.directionalShadowQuality === undefined ||
|
|
1949
2425
|
validatedOrdered.length === 0
|
|
1950
2426
|
) {
|
|
1951
2427
|
return { reuse: false, next: null };
|
|
@@ -1958,6 +2434,10 @@ function prepareDirectionalShadowCache(
|
|
|
1958
2434
|
cached !== null &&
|
|
1959
2435
|
cached.shadowMapSize === shadowMapSize &&
|
|
1960
2436
|
cached.cascadeCount === cascadeCount &&
|
|
2437
|
+
sameDirectionalShadowQuality(
|
|
2438
|
+
cached.directionalShadowQuality,
|
|
2439
|
+
lights.directionalShadowQuality,
|
|
2440
|
+
) &&
|
|
1961
2441
|
cached.pipelineHandle === frameState.installedPipelineHandle &&
|
|
1962
2442
|
cached.graphTopologyKey === frameState.compiledFrameGraphTopologyKey &&
|
|
1963
2443
|
cached.assetCatalogEpoch === assetCatalogEpoch &&
|
|
@@ -1986,6 +2466,7 @@ function prepareDirectionalShadowCache(
|
|
|
1986
2466
|
graphTopologyKey: frameState.compiledFrameGraphTopologyKey,
|
|
1987
2467
|
shadowMapSize,
|
|
1988
2468
|
cascadeCount,
|
|
2469
|
+
directionalShadowQuality: lights.directionalShadowQuality,
|
|
1989
2470
|
lightViewProj: lightViewProj.map((matrix) => new Float32Array(matrix)),
|
|
1990
2471
|
meshResidencyEpoch,
|
|
1991
2472
|
},
|
|
@@ -2018,6 +2499,7 @@ function validateRenderables(
|
|
|
2018
2499
|
renderables: readonly RenderableSnapshot[],
|
|
2019
2500
|
transparentDispatch: readonly DispatchEntry[],
|
|
2020
2501
|
excludedEntityKeys: ReadonlySet<number> = new Set<number>(),
|
|
2502
|
+
worldKeys?: readonly number[],
|
|
2021
2503
|
): ValidatedRenderable[] {
|
|
2022
2504
|
// bug-20260527-renderstate-pipeline-dispatch-gap D-4:
|
|
2023
2505
|
// build a renderableIndex -> renderState map from dispatch entries
|
|
@@ -2043,7 +2525,8 @@ function validateRenderables(
|
|
|
2043
2525
|
for (let rIdx = 0; rIdx < renderables.length; rIdx++) {
|
|
2044
2526
|
const r = renderables[rIdx];
|
|
2045
2527
|
if (r === undefined) continue;
|
|
2046
|
-
if (excludedEntityKeys.has(worldEntityKey(r.worldId, r.entityKey)))
|
|
2528
|
+
if (excludedEntityKeys.has(worldEntityKey(worldKeys?.[r.worldId] ?? r.worldId, r.entityKey)))
|
|
2529
|
+
continue;
|
|
2047
2530
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): resolve
|
|
2048
2531
|
// this renderable's mesh against the world it was EXTRACTED from
|
|
2049
2532
|
// (worlds[r.worldId]) — NOT the single resource-owner `world`. Builtin mesh
|
|
@@ -2078,11 +2561,17 @@ function validateRenderables(
|
|
|
2078
2561
|
// frames hit the O(1) cache.
|
|
2079
2562
|
const meshAssetHandle = toShared<'MeshAsset'>(r.assetHandle);
|
|
2080
2563
|
let meshHandles = internals.gpuStore.getMeshGpuHandles(meshAssetHandle, renderableWorld);
|
|
2564
|
+
const lodMeshes = r.lods?.map((lod) => internals.assets.lookup<MeshAsset>(lod.mesh));
|
|
2565
|
+
const resolvedLodMeshes =
|
|
2566
|
+
lodMeshes?.every((lod): lod is MeshAsset => lod?.kind === 'mesh') === true
|
|
2567
|
+
? lodMeshes
|
|
2568
|
+
: undefined;
|
|
2081
2569
|
if (meshHandles === undefined && r.assetHandle > handleSlot(HANDLE_NINESLICE_QUAD)) {
|
|
2082
2570
|
const residentRes = internals.gpuStore.ensureResident(
|
|
2083
2571
|
meshAssetHandle,
|
|
2084
2572
|
assetRes.value,
|
|
2085
2573
|
renderableWorld,
|
|
2574
|
+
resolvedLodMeshes,
|
|
2086
2575
|
);
|
|
2087
2576
|
if (residentRes.ok) {
|
|
2088
2577
|
meshHandles = residentRes.value;
|
|
@@ -2264,6 +2753,21 @@ function buildDispatchPlan(
|
|
|
2264
2753
|
materialSlotOwners: readonly number[];
|
|
2265
2754
|
materialSlotCount: number;
|
|
2266
2755
|
} {
|
|
2756
|
+
// A production GPU-owned frame has no CPU validated rows by design. The
|
|
2757
|
+
// dispatch list can still contain one entry per authored entity, but no
|
|
2758
|
+
// CPU plan, material table, or fold bucket is consumed in that lane. Return
|
|
2759
|
+
// the empty plan without walking that list so a steady 100k scene remains
|
|
2760
|
+
// bounded by the persistent GPU batches rather than per-entity record work.
|
|
2761
|
+
if (validated.length === 0) {
|
|
2762
|
+
return {
|
|
2763
|
+
validatedOrdered: [],
|
|
2764
|
+
foldDispatchPlan: null,
|
|
2765
|
+
materialSlotIndices: [],
|
|
2766
|
+
materialSlots: [],
|
|
2767
|
+
materialSlotOwners: [],
|
|
2768
|
+
materialSlotCount: 0,
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2267
2771
|
// M3 / w26: dispatch-ordered render. The dispatch list is pre-sorted
|
|
2268
2772
|
// by queue (ascending, stable) by the extract stage per plan-strategy D-3.
|
|
2269
2773
|
// Reorder validated renderables to follow the dispatch order, falling
|