@forgeax/engine-render 0.1.19 → 0.1.21
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 +215 -0
- package/dist/.tsbuildinfo +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 +34 -20
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +9 -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 +3 -0
- 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-RJRUS55L.mjs → chunk-5YADUCA7.mjs} +1896 -1624
- package/dist/chunk-5YADUCA7.mjs.map +1 -0
- package/dist/{chunk-LOSO4CTV.mjs → chunk-6OPXDQ2E.mjs} +423 -46
- package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
- package/dist/{chunk-IPXCABEP.mjs → chunk-BXBQQKFQ.mjs} +3 -9
- package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
- package/dist/chunk-FS2KNUQH.mjs +1294 -0
- package/dist/chunk-FS2KNUQH.mjs.map +1 -0
- 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 +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/reflection-probe.d.ts +17 -0
- package/dist/components/reflection-probe.d.ts.map +1 -0
- package/dist/components/spot-light.d.ts +12 -0
- package/dist/components/spot-light.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +8552 -1612
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -2
- package/dist/device/gpu-residency.d.ts.map +1 -1
- 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/render.d.ts +209 -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/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.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +0 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-usage.d.ts +2 -0
- package/dist/gpu-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +19 -17
- 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 +35 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +51 -0
- 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.mjs +1 -1
- package/dist/light-buffer-layout.d.ts +30 -13
- package/dist/light-buffer-layout.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 +20 -7
- 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 +12 -20
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +40 -7
- package/dist/pipeline-spec.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/record/frame-lighting.d.ts +29 -32
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +65 -15
- 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 +12 -5
- 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 +37 -0
- package/dist/record/gpu-timing.d.ts.map +1 -0
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +23 -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/render-context.d.ts +48 -3
- 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 +81 -5
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +7 -4
- 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/projection.d.ts +91 -0
- package/dist/reflection/projection.d.ts.map +1 -0
- package/dist/render-contract.d.ts +114 -7
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-data.d.ts +10 -6
- package/dist/render-data.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 +78 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +113 -20
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +24 -3
- package/dist/render-system.d.ts.map +1 -1
- package/dist/renderer-inspect.d.ts +29 -0
- package/dist/renderer-inspect.d.ts.map +1 -0
- package/dist/renderer.d.ts +43 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/scene/render-scene.d.ts +14 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- 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/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.d.ts +44 -0
- package/dist/temporal/frame.d.ts.map +1 -0
- package/dist/temporal/index.mjs +3 -3
- package/dist/typed-render-graph-primitives.d.ts +6 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-atlas.d.ts +10 -0
- package/dist/typed-shadow-atlas.d.ts.map +1 -0
- package/dist/typed-shadow-passes.d.ts +1 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/capability.d.ts +57 -0
- package/dist/volume/capability.d.ts.map +1 -0
- package/dist/volume/component.d.ts +61 -0
- package/dist/volume/component.d.ts.map +1 -0
- package/dist/volume/extract.d.ts +11 -0
- package/dist/volume/extract.d.ts.map +1 -0
- package/dist/volume/inspection.d.ts +38 -0
- package/dist/volume/inspection.d.ts.map +1 -0
- package/dist/volume/passes.d.ts +38 -0
- package/dist/volume/passes.d.ts.map +1 -0
- package/dist/volume/recovery.d.ts +38 -0
- package/dist/volume/recovery.d.ts.map +1 -0
- package/dist/volume/resources.d.ts +57 -0
- package/dist/volume/resources.d.ts.map +1 -0
- package/dist/volume/temporal.d.ts +35 -0
- package/dist/volume/temporal.d.ts.map +1 -0
- 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 +7 -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__/device-feature-admission.unit.test.ts +73 -0
- package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
- package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
- package/src/__tests__/factory-contract.integration.test.ts +255 -29
- package/src/__tests__/fog-component.unit.test.ts +67 -0
- package/src/__tests__/fog.integration.test.ts +86 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
- package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
- 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__/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__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
- package/src/__tests__/material-observation.integration.test.ts +101 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
- 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__/public-surface.test-d.ts +10 -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-pass-encode.integration.test.ts +101 -0
- package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -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 +23 -3
- package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
- 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 +76 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +1 -1
- 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 +132 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +32 -2
- 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 +232 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -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 +377 -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 -36
- 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-resolver.unit.test.ts +15 -0
- package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
- package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
- package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
- package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
- package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
- package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
- package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
- package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
- package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
- package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
- package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
- package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
- package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
- package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
- package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
- package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
- package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
- package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
- package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
- package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
- package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
- package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
- package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
- package/src/assembly/device-feature-admission.ts +73 -0
- package/src/assembly/directional-shadow-inspection.ts +122 -0
- package/src/assembly/factory.ts +1093 -246
- package/src/assembly/host-contract.ts +25 -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/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 +2 -0
- package/src/components/light-helpers.ts +85 -7
- package/src/components/reflection-probe.ts +49 -0
- package/src/components/spot-light.ts +22 -3
- package/src/device/gpu-residency.ts +168 -58
- package/src/environment/frame.ts +6 -24
- package/src/environment/lifecycle.ts +12 -0
- package/src/errors/render.ts +405 -143
- package/src/extract/directional-shadow-projection.ts +61 -0
- package/src/extract/environment.ts +57 -0
- package/src/extract/visibility.ts +25 -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 +1 -0
- package/src/gpu-driven/production-raster.ts +1 -3
- package/src/gpu-scene.ts +8 -1
- package/src/gpu-usage.ts +2 -0
- package/src/hdrp-buffers.ts +165 -59
- package/src/ibl/IblPipelineCache.ts +11 -0
- package/src/ibl/kernel-cache.ts +88 -0
- package/src/index.ts +186 -1
- package/src/inspection-types.ts +56 -0
- package/src/instances-derived-bounds.ts +171 -0
- package/src/light-buffer-layout.ts +74 -26
- package/src/mesh-material-bindings.ts +132 -0
- package/src/pbr-pipeline.ts +139 -27
- package/src/pipeline/standard-forward-lane.ts +65 -15
- package/src/pipeline/standard-lighting/errors.ts +6 -0
- package/src/pipeline/standard-lighting/graph.ts +157 -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 +34 -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 +77 -0
- package/src/pipeline/standard-pipeline.ts +141 -192
- package/src/pipeline/standard-post.ts +26 -7
- package/src/pipeline/standard-profile.ts +23 -30
- package/src/pipeline-spec.ts +84 -21
- package/src/point-shadow-inspection.ts +55 -0
- package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
- package/src/record/frame-lighting.ts +470 -516
- package/src/record/frame-snapshot.ts +76 -28
- package/src/record/frame-targets.ts +23 -2
- package/src/record/frame.ts +1169 -79
- 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 +233 -0
- package/src/record/main-pass-geometry.ts +120 -80
- package/src/record/main-pass-material.ts +101 -11
- package/src/record/main-pass-sprite-draws.ts +54 -25
- package/src/record/main-pass.ts +71 -15
- package/src/record/render-context.ts +77 -0
- package/src/record/shadow-pass.ts +104 -66
- package/src/record/skybox-post-pass.ts +20 -4
- package/src/record/typed-frame-graph.ts +1016 -32
- package/src/record/view-ubo.ts +63 -28
- package/src/reflection/filter.ts +81 -0
- package/src/reflection/gpu-table.ts +44 -0
- package/src/reflection/projection.ts +281 -0
- package/src/render-contract.ts +147 -4
- package/src/render-data.ts +50 -22
- package/src/render-graph-primitives.ts +9 -4
- package/src/render-pipeline.ts +161 -2
- package/src/render-system-extract.ts +886 -103
- package/src/render-system.ts +1174 -66
- package/src/renderer-inspect.ts +66 -0
- package/src/renderer.ts +222 -0
- package/src/scene/render-scene.ts +117 -9
- package/src/shadow-atlas.ts +5 -5
- package/src/ssao-buffers.ts +9 -2
- 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.ts +128 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +61 -23
- package/src/typed-render-graph-primitives.ts +34 -2
- package/src/typed-shadow-atlas.ts +17 -0
- package/src/typed-shadow-passes.ts +47 -40
- package/src/volume/capability.ts +173 -0
- package/src/volume/component.ts +110 -0
- package/src/volume/extract.ts +21 -0
- package/src/volume/inspection.ts +78 -0
- package/src/volume/passes.ts +686 -0
- package/src/volume/recovery.ts +86 -0
- package/src/volume/resources.ts +236 -0
- package/src/volume/temporal.ts +89 -0
- package/dist/chunk-IPXCABEP.mjs.map +0 -1
- package/dist/chunk-LOSO4CTV.mjs.map +0 -1
- package/dist/chunk-RJRUS55L.mjs.map +0 -1
- package/dist/chunk-YTQ5C7YQ.mjs +0 -584
- package/dist/chunk-YTQ5C7YQ.mjs.map +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { TextureFormat } from '@forgeax/engine-rhi';
|
|
2
|
+
import type { TemporalFrame, TemporalResetReason } from './temporal/frame';
|
|
3
|
+
|
|
4
|
+
export type TemporalInspectionStatus =
|
|
5
|
+
| 'authored-off'
|
|
6
|
+
| 'available'
|
|
7
|
+
| 'capability-unavailable'
|
|
8
|
+
| 'temporal-degraded';
|
|
9
|
+
|
|
10
|
+
export interface TemporalInspection {
|
|
11
|
+
readonly status: TemporalInspectionStatus;
|
|
12
|
+
readonly resetReason: TemporalResetReason;
|
|
13
|
+
readonly historyEpoch: number;
|
|
14
|
+
readonly deviceEpoch: number;
|
|
15
|
+
readonly backend: string | undefined;
|
|
16
|
+
readonly format: TextureFormat | undefined;
|
|
17
|
+
readonly limits: Readonly<{
|
|
18
|
+
readonly maxTextureDimension2D?: number;
|
|
19
|
+
readonly maxTextureDimension3D?: number;
|
|
20
|
+
readonly maxTextureArrayLayers?: number;
|
|
21
|
+
}>;
|
|
22
|
+
readonly compute: 'available' | 'unavailable';
|
|
23
|
+
readonly storage: 'available' | 'unavailable';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface TemporalInspectionInput {
|
|
27
|
+
readonly authored: boolean;
|
|
28
|
+
readonly capability: 'available' | 'unavailable';
|
|
29
|
+
readonly degraded?: boolean;
|
|
30
|
+
readonly frame?: TemporalFrame;
|
|
31
|
+
readonly backend?: string;
|
|
32
|
+
readonly format?: TextureFormat;
|
|
33
|
+
readonly limits?: Readonly<Record<string, number>>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function inspectTemporalFrame(input: TemporalInspectionInput): TemporalInspection {
|
|
37
|
+
const frame = input.frame;
|
|
38
|
+
const status: TemporalInspectionStatus = !input.authored
|
|
39
|
+
? 'authored-off'
|
|
40
|
+
: input.capability === 'unavailable'
|
|
41
|
+
? 'capability-unavailable'
|
|
42
|
+
: input.degraded === true
|
|
43
|
+
? 'temporal-degraded'
|
|
44
|
+
: 'available';
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
status,
|
|
47
|
+
resetReason: frame?.resetReason ?? 'none',
|
|
48
|
+
historyEpoch: frame?.historyEpoch ?? 0,
|
|
49
|
+
deviceEpoch: frame?.deviceEpoch ?? 0,
|
|
50
|
+
backend: input.backend,
|
|
51
|
+
format: input.format,
|
|
52
|
+
limits: Object.freeze({
|
|
53
|
+
...(typeof input.limits?.maxTextureDimension2D === 'number'
|
|
54
|
+
? { maxTextureDimension2D: input.limits.maxTextureDimension2D }
|
|
55
|
+
: {}),
|
|
56
|
+
...(typeof input.limits?.maxTextureDimension3D === 'number'
|
|
57
|
+
? { maxTextureDimension3D: input.limits.maxTextureDimension3D }
|
|
58
|
+
: {}),
|
|
59
|
+
...(typeof input.limits?.maxTextureArrayLayers === 'number'
|
|
60
|
+
? { maxTextureArrayLayers: input.limits.maxTextureArrayLayers }
|
|
61
|
+
: {}),
|
|
62
|
+
}),
|
|
63
|
+
compute: input.capability === 'available' ? 'available' : 'unavailable',
|
|
64
|
+
storage: input.capability === 'available' ? 'available' : 'unavailable',
|
|
65
|
+
});
|
|
66
|
+
}
|
package/src/renderer.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { err, ok } from '@forgeax/engine-types';
|
|
2
|
+
import type { RenderError } from './errors/render.js';
|
|
3
|
+
import { FrameReceiptStaleError } from './errors/render.js';
|
|
4
|
+
import type {
|
|
5
|
+
GpuPassTimingObservation,
|
|
6
|
+
GpuPassTimingReason,
|
|
7
|
+
GpuPassTimingRef,
|
|
8
|
+
} from './record/gpu-pass-timing/index.js';
|
|
9
|
+
import type {
|
|
10
|
+
FrameObservationRequest,
|
|
11
|
+
FrameReceipt,
|
|
12
|
+
FrameReceiptObservation,
|
|
13
|
+
RenderResult,
|
|
14
|
+
} from './render-contract.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Internal Render producer callback; public callers receive only frozen facts.
|
|
18
|
+
* The callback is receipt-bound and never exposes RHI handles or query state.
|
|
19
|
+
*/
|
|
20
|
+
export type GpuPassTimingObservationSource = () => Promise<GpuPassTimingObservation>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Receipt observation seam for the public `draw()` then `observe()` route.
|
|
24
|
+
* `status`, `completeness`, and `latestKnownGood` are separate projections;
|
|
25
|
+
* pass duration is not frame latency. Timing errors retain the closed code,
|
|
26
|
+
* expected, hint, and detail fields so a caller can follow producer recovery.
|
|
27
|
+
* See the bounded contract and fail-closed validator in the GPU pass timing
|
|
28
|
+
* record and benchmark modules.
|
|
29
|
+
*/
|
|
30
|
+
export interface GpuPassTimingObservationStore {
|
|
31
|
+
register(receipt: FrameReceipt, source?: GpuPassTimingObservationSource): void;
|
|
32
|
+
observe(
|
|
33
|
+
receipt: FrameReceipt,
|
|
34
|
+
request: FrameObservationRequest,
|
|
35
|
+
): Promise<RenderResult<FrameReceiptObservation, RenderError>>;
|
|
36
|
+
inspect(): {
|
|
37
|
+
readonly status: GpuPassTimingObservation['status'];
|
|
38
|
+
readonly completeness: 'complete' | 'partial' | 'unavailable' | 'failed';
|
|
39
|
+
readonly latestKnownGood?: GpuPassTimingRef | undefined;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Preserve the receipt observation contract without constructing the timing
|
|
45
|
+
* retention store when GPU timing was not opted in.
|
|
46
|
+
*/
|
|
47
|
+
export async function observeGpuPassTimingDisabled(
|
|
48
|
+
receipt: FrameReceipt,
|
|
49
|
+
request: FrameObservationRequest,
|
|
50
|
+
currentDeviceGeneration: () => number,
|
|
51
|
+
): Promise<RenderResult<FrameReceiptObservation, RenderError>> {
|
|
52
|
+
const completed = await receipt.completed;
|
|
53
|
+
if (!completed.ok) return completed;
|
|
54
|
+
if (receipt.deviceGeneration !== currentDeviceGeneration()) {
|
|
55
|
+
return err(
|
|
56
|
+
new FrameReceiptStaleError({
|
|
57
|
+
frameId: receipt.frameId,
|
|
58
|
+
receiptGeneration: receipt.deviceGeneration,
|
|
59
|
+
currentGeneration: currentDeviceGeneration(),
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const base = {
|
|
64
|
+
frameId: receipt.frameId,
|
|
65
|
+
deviceGeneration: receipt.deviceGeneration,
|
|
66
|
+
include: freeze([...request.include]),
|
|
67
|
+
} as const;
|
|
68
|
+
if (!request.include.includes('timings')) return ok(freeze(base));
|
|
69
|
+
return ok(freeze({ ...base, timings: notEnabled }));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface ReceiptRecord {
|
|
73
|
+
readonly source: GpuPassTimingObservationSource;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface ObservationStoreOptions {
|
|
77
|
+
readonly retentionFrames: number;
|
|
78
|
+
readonly currentDeviceGeneration: () => number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const notEnabled: GpuPassTimingObservation = {
|
|
82
|
+
status: 'unavailable',
|
|
83
|
+
reason: {
|
|
84
|
+
code: 'gpu-timing-not-enabled',
|
|
85
|
+
expected: 'gpuPassTiming is enabled in RendererOptions',
|
|
86
|
+
hint: 'set RendererOptions.gpuPassTiming to opt into bounded timing facts',
|
|
87
|
+
detail: {},
|
|
88
|
+
},
|
|
89
|
+
capability: { timestampQuery: false, timestampPeriodNanoseconds: null },
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function failedObservation(
|
|
93
|
+
error: GpuPassTimingReason,
|
|
94
|
+
latestKnownGood?: GpuPassTimingRef,
|
|
95
|
+
): GpuPassTimingObservation {
|
|
96
|
+
return {
|
|
97
|
+
status: 'failed',
|
|
98
|
+
error,
|
|
99
|
+
...(latestKnownGood === undefined ? {} : { latestKnownGood }),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function retentionExpired(): GpuPassTimingObservation {
|
|
104
|
+
return failedObservation({
|
|
105
|
+
code: 'timing-retention-expired',
|
|
106
|
+
expected: 'the requested receipt remains inside the bounded timing retention window',
|
|
107
|
+
hint: 'observe a newer receipt before the retention window expires',
|
|
108
|
+
detail: {},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function freeze<T>(value: T): T {
|
|
113
|
+
if (value === null || typeof value !== 'object' || Object.isFrozen(value)) return value;
|
|
114
|
+
Object.freeze(value);
|
|
115
|
+
for (const child of Object.values(value as Record<string, unknown>)) freeze(child);
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Keep receipt-bound observations in a bounded Render-owned retention ring.
|
|
121
|
+
* This helper does not expose RHI handles, query objects, or a timing
|
|
122
|
+
* controller; the public lifecycle remains `draw` then `observe`.
|
|
123
|
+
* Omitting `timings` from the observation request does not materialize facts.
|
|
124
|
+
*/
|
|
125
|
+
export function createGpuPassTimingObservationStore(
|
|
126
|
+
options: ObservationStoreOptions,
|
|
127
|
+
): GpuPassTimingObservationStore {
|
|
128
|
+
const records = new Map<FrameReceipt, ReceiptRecord>();
|
|
129
|
+
const order: FrameReceipt[] = [];
|
|
130
|
+
const expired = new WeakSet<FrameReceipt>();
|
|
131
|
+
let latestKnownGood: GpuPassTimingRef | undefined;
|
|
132
|
+
let currentStatus: GpuPassTimingObservation['status'] = 'unavailable';
|
|
133
|
+
let currentCompleteness: 'complete' | 'partial' | 'unavailable' | 'failed' = 'unavailable';
|
|
134
|
+
|
|
135
|
+
const evict = (): void => {
|
|
136
|
+
while (order.length > options.retentionFrames) {
|
|
137
|
+
const old = order.shift();
|
|
138
|
+
if (old === undefined) break;
|
|
139
|
+
records.delete(old);
|
|
140
|
+
expired.add(old);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
register(receipt, source): void {
|
|
146
|
+
records.set(receipt, { source: source ?? (async () => notEnabled) });
|
|
147
|
+
order.push(receipt);
|
|
148
|
+
evict();
|
|
149
|
+
},
|
|
150
|
+
async observe(receipt, request): Promise<RenderResult<FrameReceiptObservation, RenderError>> {
|
|
151
|
+
const record = records.get(receipt);
|
|
152
|
+
if (record === undefined) {
|
|
153
|
+
if (expired.has(receipt) && request.include.includes('timings')) {
|
|
154
|
+
return ok(
|
|
155
|
+
freeze({
|
|
156
|
+
frameId: receipt.frameId,
|
|
157
|
+
deviceGeneration: receipt.deviceGeneration,
|
|
158
|
+
include: freeze([...request.include]),
|
|
159
|
+
timings: retentionExpired(),
|
|
160
|
+
}),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return err(
|
|
164
|
+
new FrameReceiptStaleError({
|
|
165
|
+
frameId: receipt.frameId,
|
|
166
|
+
receiptGeneration: receipt.deviceGeneration,
|
|
167
|
+
currentGeneration: options.currentDeviceGeneration(),
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const completed = await receipt.completed;
|
|
172
|
+
if (!completed.ok) return completed;
|
|
173
|
+
if (receipt.deviceGeneration !== options.currentDeviceGeneration()) {
|
|
174
|
+
return err(
|
|
175
|
+
new FrameReceiptStaleError({
|
|
176
|
+
frameId: receipt.frameId,
|
|
177
|
+
receiptGeneration: receipt.deviceGeneration,
|
|
178
|
+
currentGeneration: options.currentDeviceGeneration(),
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const base = {
|
|
183
|
+
frameId: receipt.frameId,
|
|
184
|
+
deviceGeneration: receipt.deviceGeneration,
|
|
185
|
+
include: freeze([...request.include]),
|
|
186
|
+
} as const;
|
|
187
|
+
if (!request.include.includes('timings')) return ok(freeze(base));
|
|
188
|
+
let timings: GpuPassTimingObservation;
|
|
189
|
+
try {
|
|
190
|
+
timings = await record.source();
|
|
191
|
+
} catch (cause) {
|
|
192
|
+
timings = failedObservation(
|
|
193
|
+
{
|
|
194
|
+
code: 'timestamp-readback-failed',
|
|
195
|
+
expected: 'the Render-owned timing observation completes without throwing',
|
|
196
|
+
hint: 'observe a later receipt and inspect latestKnownGood',
|
|
197
|
+
detail: { frameId: receipt.frameId },
|
|
198
|
+
cause: { message: cause instanceof Error ? cause.message : String(cause) },
|
|
199
|
+
},
|
|
200
|
+
latestKnownGood,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
currentStatus = timings.status;
|
|
204
|
+
currentCompleteness = timings.status;
|
|
205
|
+
if (timings.status === 'complete') {
|
|
206
|
+
latestKnownGood = {
|
|
207
|
+
frameId: timings.frame.frameId,
|
|
208
|
+
deviceGeneration: timings.frame.deviceGeneration,
|
|
209
|
+
graphGeneration: timings.frame.graphGeneration,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return ok(freeze({ ...base, timings }));
|
|
213
|
+
},
|
|
214
|
+
inspect() {
|
|
215
|
+
return freeze({
|
|
216
|
+
status: currentStatus,
|
|
217
|
+
completeness: currentCompleteness,
|
|
218
|
+
latestKnownGood,
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
PointLight,
|
|
17
17
|
PointLightShadow,
|
|
18
18
|
PostProcessParams,
|
|
19
|
+
ReflectionProbe,
|
|
19
20
|
SkyboxBackground,
|
|
20
21
|
Skylight,
|
|
21
22
|
SpotLight,
|
|
@@ -30,8 +31,14 @@ import {
|
|
|
30
31
|
} from '../gpu-driven/batch-topology';
|
|
31
32
|
import { GpuScene } from '../gpu-scene';
|
|
32
33
|
import type { PersistentRenderSceneInspection } from '../inspection-types';
|
|
34
|
+
import { fingerprintNumericArray, InstanceBoundsCache } from '../instances-derived-bounds';
|
|
33
35
|
import type { PointsLinesInspection } from '../points-lines/inspection';
|
|
34
36
|
import type { PointsLinesRetainedSnapshot } from '../points-lines/snapshot';
|
|
37
|
+
import {
|
|
38
|
+
type ReflectionProbeFact,
|
|
39
|
+
ReflectionProbeProjection,
|
|
40
|
+
type ReflectionProbeSelectionResult,
|
|
41
|
+
} from '../reflection/projection';
|
|
35
42
|
import type { CameraSnapshot } from '../render-contract';
|
|
36
43
|
import type {
|
|
37
44
|
DispatchEntry,
|
|
@@ -47,6 +54,7 @@ import {
|
|
|
47
54
|
type TransmissionDemand,
|
|
48
55
|
type TransmissionDemandOperation,
|
|
49
56
|
} from '../transmission/projection';
|
|
57
|
+
import { VolumetricFog } from '../volume/component';
|
|
50
58
|
import type {
|
|
51
59
|
RenderSceneApplyResult,
|
|
52
60
|
RenderSceneBounds,
|
|
@@ -219,6 +227,22 @@ function worldBounds(snapshot: RenderableSnapshot): RenderSceneBounds | undefine
|
|
|
219
227
|
return { min, max };
|
|
220
228
|
}
|
|
221
229
|
|
|
230
|
+
function boundsFromArray(bounds: ArrayLike<number>): RenderSceneBounds | undefined {
|
|
231
|
+
if (bounds.length < 6) return undefined;
|
|
232
|
+
const min: [number, number, number] = [Number(bounds[0]), Number(bounds[1]), Number(bounds[2])];
|
|
233
|
+
const max: [number, number, number] = [Number(bounds[3]), Number(bounds[4]), Number(bounds[5])];
|
|
234
|
+
if (
|
|
235
|
+
!min.every(Number.isFinite) ||
|
|
236
|
+
!max.every(Number.isFinite) ||
|
|
237
|
+
min[0] > max[0] ||
|
|
238
|
+
min[1] > max[1] ||
|
|
239
|
+
min[2] > max[2]
|
|
240
|
+
) {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
return { min, max };
|
|
244
|
+
}
|
|
245
|
+
|
|
222
246
|
/**
|
|
223
247
|
* The renderer's single rebuildable CPU scene authority.
|
|
224
248
|
*
|
|
@@ -240,6 +264,7 @@ export class RenderScene {
|
|
|
240
264
|
// derived value by snapshot identity so repeated view queries do not redo
|
|
241
265
|
// the eight-corner transform for every view in the same frame.
|
|
242
266
|
private readonly worldBoundsCache = new WeakMap<RenderableSnapshot, RenderSceneBounds | null>();
|
|
267
|
+
private readonly instanceBoundsCache = new InstanceBoundsCache();
|
|
243
268
|
private orderedSlots: number[] = [];
|
|
244
269
|
private materialized: readonly RenderableSnapshot[] | undefined;
|
|
245
270
|
private slotSnapshot: readonly RenderSceneSlot[] | undefined;
|
|
@@ -551,11 +576,7 @@ export class RenderScene {
|
|
|
551
576
|
const record = this.slots[slot];
|
|
552
577
|
if (record === undefined) continue;
|
|
553
578
|
const snapshot = record.snapshot;
|
|
554
|
-
|
|
555
|
-
if (candidate === undefined && !this.worldBoundsCache.has(snapshot)) {
|
|
556
|
-
candidate = worldBounds(snapshot) ?? null;
|
|
557
|
-
this.worldBoundsCache.set(snapshot, candidate);
|
|
558
|
-
}
|
|
579
|
+
const candidate = this.cullingWorldBounds(snapshot);
|
|
559
580
|
if (candidate === null || candidate === undefined) continue;
|
|
560
581
|
if (intersects(candidate, bounds)) records.push(record);
|
|
561
582
|
}
|
|
@@ -628,6 +649,7 @@ export class RenderScene {
|
|
|
628
649
|
|
|
629
650
|
private release(record: RenderSceneSlot): void {
|
|
630
651
|
this.slots[record.slot] = undefined;
|
|
652
|
+
this.instanceBoundsCache.invalidate(record.entityKey, record.worldId);
|
|
631
653
|
this.unindexMaterials(record);
|
|
632
654
|
const entities = this.slotsByWorld.get(record.worldId);
|
|
633
655
|
entities?.delete(record.entityKey);
|
|
@@ -705,6 +727,41 @@ export class RenderScene {
|
|
|
705
727
|
this.materialized = undefined;
|
|
706
728
|
this.slotSnapshot = undefined;
|
|
707
729
|
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Return the CPU culling projection for one retained snapshot. Instances
|
|
733
|
+
* use a renderer-derived union, while the GPU scene keeps the mesh-local
|
|
734
|
+
* bounds for its independent per-instance visibility pass. Unknown or
|
|
735
|
+
* empty instance facts deliberately return undefined (conservative no-cull).
|
|
736
|
+
*/
|
|
737
|
+
cullingWorldBounds(snapshot: RenderableSnapshot): RenderSceneBounds | undefined {
|
|
738
|
+
let candidate = this.worldBoundsCache.get(snapshot);
|
|
739
|
+
if (candidate !== undefined || this.worldBoundsCache.has(snapshot)) {
|
|
740
|
+
return candidate ?? undefined;
|
|
741
|
+
}
|
|
742
|
+
if (snapshot.instances !== undefined) {
|
|
743
|
+
const instanceCount = snapshot.instances.instanceCount;
|
|
744
|
+
if (instanceCount === 0 || snapshot.localAabb === undefined) {
|
|
745
|
+
this.worldBoundsCache.set(snapshot, null);
|
|
746
|
+
return undefined;
|
|
747
|
+
}
|
|
748
|
+
const derived = this.instanceBoundsCache.get({
|
|
749
|
+
worldId: snapshot.worldId,
|
|
750
|
+
entityKey: snapshot.entityKey,
|
|
751
|
+
meshGeneration: fingerprintNumericArray(snapshot.localAabb) ^ (snapshot.assetHandle >>> 0),
|
|
752
|
+
transformGeneration: fingerprintNumericArray(snapshot.transform.world),
|
|
753
|
+
matrixGeneration: snapshot.instances.archVersion,
|
|
754
|
+
meshAabb: snapshot.localAabb,
|
|
755
|
+
entityWorld: snapshot.transform.world,
|
|
756
|
+
transforms: snapshot.instances.transforms,
|
|
757
|
+
});
|
|
758
|
+
candidate = boundsFromArray(derived ?? []);
|
|
759
|
+
} else {
|
|
760
|
+
candidate = worldBounds(snapshot);
|
|
761
|
+
}
|
|
762
|
+
this.worldBoundsCache.set(snapshot, candidate ?? null);
|
|
763
|
+
return candidate;
|
|
764
|
+
}
|
|
708
765
|
}
|
|
709
766
|
|
|
710
767
|
function readTransformWorld(world: World, entity: EntityHandle): Float32Array | undefined {
|
|
@@ -761,6 +818,8 @@ const RENDER_RELEVANT_COMPONENT_IDS = new Set([
|
|
|
761
818
|
componentId(Skylight),
|
|
762
819
|
componentId(SkyboxBackground),
|
|
763
820
|
componentId(PostProcessParams),
|
|
821
|
+
componentId(ReflectionProbe),
|
|
822
|
+
componentId(VolumetricFog),
|
|
764
823
|
]);
|
|
765
824
|
|
|
766
825
|
const NON_RENDERABLE_TRANSFORM_CONSUMERS = [
|
|
@@ -808,6 +867,7 @@ function cameraFrusta(cameras: readonly CameraSnapshot[]): readonly Float32Array
|
|
|
808
867
|
function cullPersistentFrame(
|
|
809
868
|
frame: ExtractedFrame,
|
|
810
869
|
candidates: readonly RenderableSnapshot[],
|
|
870
|
+
boundsOf: (snapshot: RenderableSnapshot) => RenderSceneBounds | undefined = worldBounds,
|
|
811
871
|
): ExtractedFrame {
|
|
812
872
|
const planes = cameraFrusta(frame.cameras);
|
|
813
873
|
const visible: RenderableSnapshot[] = [];
|
|
@@ -820,10 +880,16 @@ function cullPersistentFrame(
|
|
|
820
880
|
const candidate = candidates[index];
|
|
821
881
|
if (candidate === undefined) continue;
|
|
822
882
|
let isVisible = true;
|
|
823
|
-
|
|
883
|
+
const candidateBounds = boundsOf(candidate);
|
|
884
|
+
if (candidateBounds !== undefined) {
|
|
824
885
|
total += 1;
|
|
825
886
|
const worldAabb = box3.create();
|
|
826
|
-
|
|
887
|
+
worldAabb[0] = candidateBounds.min[0] ?? 0;
|
|
888
|
+
worldAabb[1] = candidateBounds.min[1] ?? 0;
|
|
889
|
+
worldAabb[2] = candidateBounds.min[2] ?? 0;
|
|
890
|
+
worldAabb[3] = candidateBounds.max[0] ?? 0;
|
|
891
|
+
worldAabb[4] = candidateBounds.max[1] ?? 0;
|
|
892
|
+
worldAabb[5] = candidateBounds.max[2] ?? 0;
|
|
827
893
|
isVisible = planes.length === 0;
|
|
828
894
|
for (const cameraPlanes of planes) {
|
|
829
895
|
if (
|
|
@@ -877,6 +943,7 @@ export class PersistentRenderScene {
|
|
|
877
943
|
| { readonly projection: RenderScene; readonly capture: RenderSceneSubmissionCapture }
|
|
878
944
|
| undefined;
|
|
879
945
|
private pointsLinesInspections: readonly PointsLinesInspection[] = [];
|
|
946
|
+
private readonly reflectionProbes = new ReflectionProbeProjection();
|
|
880
947
|
|
|
881
948
|
constructor(private readonly options: PersistentRenderSceneOptions = {}) {}
|
|
882
949
|
|
|
@@ -885,6 +952,37 @@ export class PersistentRenderScene {
|
|
|
885
952
|
return this.materialSnapshotCaches;
|
|
886
953
|
}
|
|
887
954
|
|
|
955
|
+
/** Project probe facts and per-primitive selection into the persistent scene owner. */
|
|
956
|
+
projectReflectionProbes(probes: readonly ReflectionProbeFact[]): void {
|
|
957
|
+
const projection = this.composition?.projection;
|
|
958
|
+
if (projection === undefined) return;
|
|
959
|
+
const primitives = projection
|
|
960
|
+
.slotsSnapshot()
|
|
961
|
+
.map((slot) => {
|
|
962
|
+
const resolved = worldBounds(slot.snapshot);
|
|
963
|
+
if (resolved === undefined || resolved === null) return undefined;
|
|
964
|
+
return {
|
|
965
|
+
worldId: slot.worldId,
|
|
966
|
+
entityKey: slot.entityKey,
|
|
967
|
+
center: [
|
|
968
|
+
(resolved.min[0] + resolved.max[0]) / 2,
|
|
969
|
+
(resolved.min[1] + resolved.max[1]) / 2,
|
|
970
|
+
(resolved.min[2] + resolved.max[2]) / 2,
|
|
971
|
+
] as [number, number, number],
|
|
972
|
+
};
|
|
973
|
+
})
|
|
974
|
+
.filter((primitive): primitive is NonNullable<typeof primitive> => primitive !== undefined);
|
|
975
|
+
this.reflectionProbes.update(probes, primitives);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
reflectionProbeSelection(worldId: number, entityKey: number): ReflectionProbeSelectionResult {
|
|
979
|
+
return this.reflectionProbes.selection(worldId, entityKey);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
reflectionProbeProjection(): ReturnType<ReflectionProbeProjection['snapshot']> {
|
|
983
|
+
return this.reflectionProbes.snapshot();
|
|
984
|
+
}
|
|
985
|
+
|
|
888
986
|
extractComposition(
|
|
889
987
|
worlds: readonly World[],
|
|
890
988
|
owner: { readonly cameraOwner: number; readonly resourceOwner: number },
|
|
@@ -1193,6 +1291,7 @@ export class PersistentRenderScene {
|
|
|
1193
1291
|
transmissionDemand.apply(transmissionDemandOperations(candidateFrame.renderables));
|
|
1194
1292
|
const topology = new BatchTopology();
|
|
1195
1293
|
topology.rebuild(projection.slotsSnapshot());
|
|
1294
|
+
this.projectReflectionProbes(candidateFrame.reflectionProbes ?? []);
|
|
1196
1295
|
const token = {};
|
|
1197
1296
|
const entry: PersistentCompositionEntry = {
|
|
1198
1297
|
token,
|
|
@@ -1222,7 +1321,11 @@ export class PersistentRenderScene {
|
|
|
1222
1321
|
const candidates = projection
|
|
1223
1322
|
.materialize()
|
|
1224
1323
|
.map((snapshot) => projection.withTemporal(snapshot));
|
|
1225
|
-
return cullPersistentFrame(
|
|
1324
|
+
return cullPersistentFrame(
|
|
1325
|
+
{ ...candidateFrame, renderables: [...candidates] },
|
|
1326
|
+
candidates,
|
|
1327
|
+
(snapshot) => projection.cullingWorldBounds(snapshot),
|
|
1328
|
+
);
|
|
1226
1329
|
}
|
|
1227
1330
|
|
|
1228
1331
|
/** Internal topology fact; the renderer does not expose demand as public API. */
|
|
@@ -1244,10 +1347,15 @@ export class PersistentRenderScene {
|
|
|
1244
1347
|
buildCandidateFrame: () => ExtractedFrame,
|
|
1245
1348
|
): ExtractedFrame {
|
|
1246
1349
|
const candidateFrame = buildCandidateFrame();
|
|
1350
|
+
this.projectReflectionProbes(candidateFrame.reflectionProbes ?? []);
|
|
1247
1351
|
const candidates = entry.projection
|
|
1248
1352
|
.materialize()
|
|
1249
1353
|
.map((snapshot) => entry.projection.withTemporal(snapshot));
|
|
1250
|
-
return cullPersistentFrame(
|
|
1354
|
+
return cullPersistentFrame(
|
|
1355
|
+
{ ...candidateFrame, renderables: [...candidates] },
|
|
1356
|
+
candidates,
|
|
1357
|
+
(snapshot) => entry.projection.cullingWorldBounds(snapshot),
|
|
1358
|
+
);
|
|
1251
1359
|
}
|
|
1252
1360
|
|
|
1253
1361
|
private syncGpuOwner(
|
package/src/shadow-atlas.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// feat-20260612-point-light-shadows-urp-hdrp M3 / T-M3-1 (plan-strategy §D-1).
|
|
4
4
|
//
|
|
5
5
|
// Lazy-allocated `texture_depth_cube_array` (depth32float) + per-face 2D
|
|
6
|
-
// TextureView cache. One layer per shadow-casting PointLight
|
|
7
|
-
//
|
|
6
|
+
// TextureView cache. One layer per admitted shadow-casting PointLight; the
|
|
7
|
+
// renderer owns the bounded atlas admission policy. Total texel footprint at
|
|
8
8
|
// the 512x512 default = 4 layers x 6 faces x 512 x 512 x 4 B = 24 MiB.
|
|
9
9
|
//
|
|
10
10
|
// Why a separate module (vs inlining into createRenderer / pipelineState):
|
|
@@ -53,19 +53,19 @@ import { GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING } from './gpu-t
|
|
|
53
53
|
export interface ShadowAtlasOptions {
|
|
54
54
|
/** Per-face square size in pixels. Default 512 (PointLightShadow.mapSize default). */
|
|
55
55
|
readonly faceSize?: number;
|
|
56
|
-
/** Number of cube layers. Default
|
|
56
|
+
/** Number of cube layers. Default is the renderer atlas admission capacity. */
|
|
57
57
|
readonly layers?: number;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/** Default per-face square size in pixels. Mirrors PointLightShadow.mapSize default. */
|
|
61
61
|
export const SHADOW_ATLAS_DEFAULT_FACE_SIZE = 512;
|
|
62
|
-
/** Default cube layer count.
|
|
62
|
+
/** Default cube layer count. Renderer-owned point-shadow admission capacity. */
|
|
63
63
|
export const SHADOW_ATLAS_DEFAULT_LAYERS = 4;
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Cube-array depth atlas for point-light shadows.
|
|
67
67
|
*
|
|
68
|
-
* Shape: `texture_depth_cube_array` (depth32float) with `layers
|
|
68
|
+
* Shape: `texture_depth_cube_array` (depth32float) with `layers` and per-
|
|
69
69
|
* face size `faceSize x faceSize`. The cube_array sampling view feeds
|
|
70
70
|
* `@group(0) @binding(5)` in URP shaders; the per-face 2D view feeds the
|
|
71
71
|
* shadow-caster render pass attachment for one (layer, face) at a time
|
package/src/ssao-buffers.ts
CHANGED
|
@@ -106,6 +106,7 @@ export const SSAO_UNIFORM_BYTES = UNIFORM_BYTES;
|
|
|
106
106
|
// recompile across enable/disable.
|
|
107
107
|
|
|
108
108
|
interface SsaoFallbackResources {
|
|
109
|
+
readonly device: RenderSystemRuntime['device'];
|
|
109
110
|
readonly texture: Texture;
|
|
110
111
|
readonly view: TextureView;
|
|
111
112
|
readonly sampler: Sampler;
|
|
@@ -126,7 +127,8 @@ export function getOrCreateSsaoFallbackTexture(
|
|
|
126
127
|
runtime: RenderSystemRuntime,
|
|
127
128
|
): SsaoFallbackResources | null {
|
|
128
129
|
const cached = fallbackCache.get(runtime);
|
|
129
|
-
if (cached !== undefined) return cached;
|
|
130
|
+
if (cached !== undefined && cached.device === runtime.device) return cached;
|
|
131
|
+
if (cached !== undefined) fallbackCache.delete(runtime);
|
|
130
132
|
|
|
131
133
|
const device = runtime.device;
|
|
132
134
|
const texRes = device.createTexture({
|
|
@@ -191,6 +193,7 @@ export function getOrCreateSsaoFallbackTexture(
|
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
const resources: SsaoFallbackResources = {
|
|
196
|
+
device,
|
|
194
197
|
texture: texRes.value,
|
|
195
198
|
view: viewRes.value,
|
|
196
199
|
sampler: samplerRes.value,
|
|
@@ -205,6 +208,8 @@ export function getOrCreateSsaoFallbackTexture(
|
|
|
205
208
|
* Fields are readonly per charter P5: one resource, one owner.
|
|
206
209
|
*/
|
|
207
210
|
export interface SsaoBuffers {
|
|
211
|
+
/** Device identity that owns every handle in this bundle. */
|
|
212
|
+
readonly device: RenderSystemRuntime['device'];
|
|
208
213
|
/** 64 padded vec4 uniform buffer for hemisphere samples. */
|
|
209
214
|
readonly kernelBuffer: Buffer;
|
|
210
215
|
readonly kernelBytes: number;
|
|
@@ -229,7 +234,8 @@ const cache = new WeakMap<RenderSystemRuntime, SsaoBuffers>();
|
|
|
229
234
|
*/
|
|
230
235
|
export function getOrCreateSsaoBuffers(runtime: RenderSystemRuntime): SsaoBuffers | null {
|
|
231
236
|
const cached = cache.get(runtime);
|
|
232
|
-
if (cached !== undefined) return cached;
|
|
237
|
+
if (cached !== undefined && cached.device === runtime.device) return cached;
|
|
238
|
+
if (cached !== undefined) cache.delete(runtime);
|
|
233
239
|
|
|
234
240
|
const device = runtime.device;
|
|
235
241
|
|
|
@@ -322,6 +328,7 @@ export function getOrCreateSsaoBuffers(runtime: RenderSystemRuntime): SsaoBuffer
|
|
|
322
328
|
}
|
|
323
329
|
|
|
324
330
|
const buffers: SsaoBuffers = {
|
|
331
|
+
device,
|
|
325
332
|
kernelBuffer: kernelBufferRes.value,
|
|
326
333
|
kernelBytes: SSAO_KERNEL_SAMPLE_COUNT * BYTES_PER_KERNEL_ELEMENT,
|
|
327
334
|
noiseTexture: noiseTexRes.value,
|