@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,18 @@
|
|
|
1
|
+
import type { Texture, TextureView } from '@forgeax/engine-rhi';
|
|
2
|
+
import type { RenderTargetDescriptor } from './contracts';
|
|
3
|
+
|
|
4
|
+
/** Renderer-private physical storage for one logical target generation. */
|
|
5
|
+
export interface RenderTargetPhysical {
|
|
6
|
+
readonly generation: number;
|
|
7
|
+
readonly descriptor: RenderTargetDescriptor;
|
|
8
|
+
readonly texture: Texture;
|
|
9
|
+
readonly view: TextureView;
|
|
10
|
+
readonly mipViews: readonly TextureView[];
|
|
11
|
+
readonly colorTextures: readonly Texture[];
|
|
12
|
+
readonly faceViews: readonly TextureView[];
|
|
13
|
+
readonly depthTextures: readonly Texture[];
|
|
14
|
+
readonly depthViews: readonly TextureView[];
|
|
15
|
+
readonly resolveTexture?: Texture;
|
|
16
|
+
readonly resolveView: TextureView;
|
|
17
|
+
readonly resolveFaceViews: readonly TextureView[];
|
|
18
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { RenderError } from '../errors/render';
|
|
2
|
+
import { RenderTargetOperationFailedError, RenderTargetStateInvalidError } from '../errors/render';
|
|
3
|
+
import type { RenderResult } from '../render-contract';
|
|
4
|
+
import type { RenderTarget } from './contracts';
|
|
5
|
+
|
|
6
|
+
export interface RenderTargetReadbackReceipt {
|
|
7
|
+
readonly frameId: number;
|
|
8
|
+
readonly deviceGeneration: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RenderTargetReadbackTicketInput {
|
|
12
|
+
readonly frameId?: number;
|
|
13
|
+
deviceGeneration: number;
|
|
14
|
+
readonly mipLevel: number;
|
|
15
|
+
readonly face?: number;
|
|
16
|
+
readonly width: number;
|
|
17
|
+
readonly height: number;
|
|
18
|
+
readonly bytesPerPixel: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RenderTargetReadbackTicket {
|
|
22
|
+
readonly target: RenderTarget;
|
|
23
|
+
frameId: number | undefined;
|
|
24
|
+
deviceGeneration: number;
|
|
25
|
+
readonly mipLevel: number;
|
|
26
|
+
readonly face?: number;
|
|
27
|
+
readonly width: number;
|
|
28
|
+
readonly height: number;
|
|
29
|
+
readonly bytesPerRow: number;
|
|
30
|
+
readonly byteLength: number;
|
|
31
|
+
consumed: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface RenderTargetReadbackResult {
|
|
35
|
+
readonly bytes: Uint8Array;
|
|
36
|
+
readonly frameId: number;
|
|
37
|
+
readonly deviceGeneration: number;
|
|
38
|
+
readonly mipLevel: number;
|
|
39
|
+
readonly face?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function invalidTicket(
|
|
43
|
+
ticket: RenderTargetReadbackTicket,
|
|
44
|
+
reason: 'uninitialized' | 'generation-mismatch' | 'destroyed',
|
|
45
|
+
): RenderResult<never, RenderError> {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
error: new RenderTargetStateInvalidError({
|
|
49
|
+
operation: 'readback',
|
|
50
|
+
reason,
|
|
51
|
+
state: reason === 'destroyed' ? 'destroyed' : 'active',
|
|
52
|
+
generation: ticket.deviceGeneration,
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function bindRenderTargetReadbackTicket(
|
|
58
|
+
ticket: RenderTargetReadbackTicket,
|
|
59
|
+
receipt: RenderTargetReadbackReceipt,
|
|
60
|
+
): RenderResult<void, RenderError> {
|
|
61
|
+
if (ticket.consumed) return invalidTicket(ticket, 'destroyed');
|
|
62
|
+
if (ticket.frameId !== undefined) {
|
|
63
|
+
return invalidTicket(ticket, 'generation-mismatch');
|
|
64
|
+
}
|
|
65
|
+
ticket.frameId = receipt.frameId;
|
|
66
|
+
ticket.deviceGeneration = receipt.deviceGeneration;
|
|
67
|
+
return { ok: true, value: undefined };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createRenderTargetReadbackTicket(
|
|
71
|
+
target: RenderTarget,
|
|
72
|
+
input: RenderTargetReadbackTicketInput,
|
|
73
|
+
): RenderResult<RenderTargetReadbackTicket, RenderError> {
|
|
74
|
+
if (
|
|
75
|
+
(input.frameId !== undefined && (!Number.isInteger(input.frameId) || input.frameId < 0)) ||
|
|
76
|
+
!Number.isInteger(input.deviceGeneration) ||
|
|
77
|
+
input.deviceGeneration < 0 ||
|
|
78
|
+
!Number.isInteger(input.mipLevel) ||
|
|
79
|
+
input.mipLevel < 0 ||
|
|
80
|
+
!Number.isInteger(input.width) ||
|
|
81
|
+
input.width < 1 ||
|
|
82
|
+
!Number.isInteger(input.height) ||
|
|
83
|
+
input.height < 1 ||
|
|
84
|
+
!Number.isInteger(input.bytesPerPixel) ||
|
|
85
|
+
input.bytesPerPixel < 1
|
|
86
|
+
) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
error: new RenderTargetOperationFailedError({
|
|
90
|
+
operation: 'readback',
|
|
91
|
+
stage: 'copy',
|
|
92
|
+
generation: input.deviceGeneration,
|
|
93
|
+
cause: new Error('readback dimensions and identity must be non-negative integers'),
|
|
94
|
+
recovery: 'retry',
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const unalignedRowBytes = input.width * input.bytesPerPixel;
|
|
99
|
+
const bytesPerRow = Math.ceil(unalignedRowBytes / 256) * 256;
|
|
100
|
+
return {
|
|
101
|
+
ok: true,
|
|
102
|
+
value: {
|
|
103
|
+
target,
|
|
104
|
+
frameId: input.frameId,
|
|
105
|
+
deviceGeneration: input.deviceGeneration,
|
|
106
|
+
mipLevel: input.mipLevel,
|
|
107
|
+
...(input.face === undefined ? {} : { face: input.face }),
|
|
108
|
+
width: input.width,
|
|
109
|
+
height: input.height,
|
|
110
|
+
bytesPerRow,
|
|
111
|
+
byteLength: bytesPerRow * input.height,
|
|
112
|
+
consumed: false,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function completeRenderTargetReadback(
|
|
118
|
+
ticket: RenderTargetReadbackTicket,
|
|
119
|
+
receipt: RenderTargetReadbackReceipt,
|
|
120
|
+
bytes: Uint8Array,
|
|
121
|
+
): RenderResult<RenderTargetReadbackResult, RenderError> {
|
|
122
|
+
if (ticket.consumed) return invalidTicket(ticket, 'destroyed');
|
|
123
|
+
if (
|
|
124
|
+
ticket.frameId === undefined ||
|
|
125
|
+
receipt.frameId !== ticket.frameId ||
|
|
126
|
+
receipt.deviceGeneration !== ticket.deviceGeneration
|
|
127
|
+
) {
|
|
128
|
+
return invalidTicket(ticket, 'generation-mismatch');
|
|
129
|
+
}
|
|
130
|
+
if (bytes.byteLength !== ticket.byteLength) {
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
error: new RenderTargetOperationFailedError({
|
|
134
|
+
operation: 'readback',
|
|
135
|
+
stage: 'copy',
|
|
136
|
+
generation: ticket.deviceGeneration,
|
|
137
|
+
cause: new Error(`expected ${ticket.byteLength} bytes, received ${bytes.byteLength}`),
|
|
138
|
+
recovery: 'retry',
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
ticket.consumed = true;
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
value: {
|
|
146
|
+
bytes,
|
|
147
|
+
frameId: ticket.frameId,
|
|
148
|
+
deviceGeneration: ticket.deviceGeneration,
|
|
149
|
+
mipLevel: ticket.mipLevel,
|
|
150
|
+
...(ticket.face === undefined ? {} : { face: ticket.face }),
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
2
|
+
import { TemporalFrameSubmitError } from '../errors/render';
|
|
3
|
+
|
|
4
|
+
export type TemporalResetReason =
|
|
5
|
+
| 'none'
|
|
6
|
+
| 'camera-cut'
|
|
7
|
+
| 'resize'
|
|
8
|
+
| 'signature-change'
|
|
9
|
+
| 'depth-discontinuity'
|
|
10
|
+
| 'out-of-screen'
|
|
11
|
+
| 'queue-submit-failed'
|
|
12
|
+
| 'device-recovery';
|
|
13
|
+
|
|
14
|
+
export interface TemporalFrameInput {
|
|
15
|
+
readonly frameId: number;
|
|
16
|
+
readonly currentViewProjection: Float32Array;
|
|
17
|
+
readonly jitter: readonly [number, number];
|
|
18
|
+
readonly viewport: { readonly width: number; readonly height: number };
|
|
19
|
+
/** Current camera origin, retained only as accepted temporal history. */
|
|
20
|
+
readonly cameraPosition?: readonly [number, number, number];
|
|
21
|
+
readonly resetReason?: Exclude<TemporalResetReason, 'none' | 'queue-submit-failed'>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TemporalFrame {
|
|
25
|
+
readonly frameId: number;
|
|
26
|
+
readonly currentViewProjection: Float32Array;
|
|
27
|
+
readonly previousViewProjection: Float32Array | undefined;
|
|
28
|
+
readonly jitter: readonly [number, number];
|
|
29
|
+
readonly viewport: { readonly width: number; readonly height: number };
|
|
30
|
+
readonly currentCameraPosition: readonly [number, number, number];
|
|
31
|
+
readonly previousCameraPosition: readonly [number, number, number] | undefined;
|
|
32
|
+
readonly resetReason: TemporalResetReason;
|
|
33
|
+
readonly historyEpoch: number;
|
|
34
|
+
readonly deviceEpoch: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TemporalSubmitResult {
|
|
38
|
+
readonly accepted: boolean;
|
|
39
|
+
readonly reason?: 'queue-submit-failed';
|
|
40
|
+
readonly resetReason?: Exclude<TemporalResetReason, 'none' | 'queue-submit-failed'>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface TemporalFrameTransaction {
|
|
44
|
+
stage(input: TemporalFrameInput): void;
|
|
45
|
+
commit(submit: TemporalSubmitResult): Result<TemporalFrame, TemporalFrameSubmitError>;
|
|
46
|
+
snapshot(): TemporalFrame;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function copyInput(input: TemporalFrameInput): TemporalFrameInput {
|
|
50
|
+
return {
|
|
51
|
+
...input,
|
|
52
|
+
currentViewProjection: new Float32Array(input.currentViewProjection),
|
|
53
|
+
jitter: [input.jitter[0], input.jitter[1]],
|
|
54
|
+
viewport: { ...input.viewport },
|
|
55
|
+
...(input.cameraPosition === undefined
|
|
56
|
+
? {}
|
|
57
|
+
: { cameraPosition: [...input.cameraPosition] as [number, number, number] }),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function copyFrame(frame: TemporalFrame): TemporalFrame {
|
|
62
|
+
return {
|
|
63
|
+
...frame,
|
|
64
|
+
currentViewProjection: new Float32Array(frame.currentViewProjection),
|
|
65
|
+
previousViewProjection:
|
|
66
|
+
frame.previousViewProjection === undefined
|
|
67
|
+
? undefined
|
|
68
|
+
: new Float32Array(frame.previousViewProjection),
|
|
69
|
+
jitter: [frame.jitter[0], frame.jitter[1]],
|
|
70
|
+
viewport: { ...frame.viewport },
|
|
71
|
+
currentCameraPosition: [...frame.currentCameraPosition] as [number, number, number],
|
|
72
|
+
previousCameraPosition:
|
|
73
|
+
frame.previousCameraPosition === undefined
|
|
74
|
+
? undefined
|
|
75
|
+
: ([...frame.previousCameraPosition] as [number, number, number]),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function createTemporalFrameTransaction(options: {
|
|
80
|
+
readonly deviceEpoch: number;
|
|
81
|
+
}): TemporalFrameTransaction {
|
|
82
|
+
let accepted: TemporalFrame | undefined;
|
|
83
|
+
let staged: TemporalFrameInput | undefined;
|
|
84
|
+
let historyEpoch = 0;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
stage(input) {
|
|
88
|
+
staged = copyInput(input);
|
|
89
|
+
},
|
|
90
|
+
commit(submit) {
|
|
91
|
+
if (!submit.accepted) {
|
|
92
|
+
staged = undefined;
|
|
93
|
+
return err(new TemporalFrameSubmitError(submit.reason ?? 'queue-submit-failed'));
|
|
94
|
+
}
|
|
95
|
+
if (staged === undefined) {
|
|
96
|
+
return err(new TemporalFrameSubmitError('queue-submit-failed'));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const resetReason = submit.resetReason ?? staged.resetReason ?? 'none';
|
|
100
|
+
const shouldReset = resetReason !== 'none';
|
|
101
|
+
if (shouldReset) historyEpoch += 1;
|
|
102
|
+
const next: TemporalFrame = {
|
|
103
|
+
frameId: staged.frameId,
|
|
104
|
+
currentViewProjection: new Float32Array(staged.currentViewProjection),
|
|
105
|
+
previousViewProjection:
|
|
106
|
+
accepted === undefined || shouldReset
|
|
107
|
+
? undefined
|
|
108
|
+
: new Float32Array(accepted.currentViewProjection),
|
|
109
|
+
jitter: [staged.jitter[0], staged.jitter[1]],
|
|
110
|
+
viewport: { ...staged.viewport },
|
|
111
|
+
currentCameraPosition: staged.cameraPosition ?? [0, 0, 0],
|
|
112
|
+
previousCameraPosition:
|
|
113
|
+
accepted === undefined || shouldReset ? undefined : accepted.currentCameraPosition,
|
|
114
|
+
resetReason,
|
|
115
|
+
historyEpoch,
|
|
116
|
+
deviceEpoch: options.deviceEpoch,
|
|
117
|
+
};
|
|
118
|
+
accepted = next;
|
|
119
|
+
staged = undefined;
|
|
120
|
+
return ok(copyFrame(next));
|
|
121
|
+
},
|
|
122
|
+
snapshot() {
|
|
123
|
+
if (accepted === undefined)
|
|
124
|
+
throw new Error('TemporalFrame snapshot is unavailable before commit');
|
|
125
|
+
return copyFrame(accepted);
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
DirectionalLight,
|
|
15
15
|
MeshFilter,
|
|
16
16
|
MeshRenderer,
|
|
17
|
+
PointLight,
|
|
17
18
|
TONEMAP_LINEAR,
|
|
18
19
|
} from '../../components';
|
|
19
20
|
import { constructRendererHost, type RhiBackendPack } from '../../construct-renderer';
|
|
@@ -57,20 +58,21 @@ interface TransmissionCase {
|
|
|
57
58
|
readonly thicknessTexture?: number;
|
|
58
59
|
readonly masked?: boolean;
|
|
59
60
|
readonly overlap?: boolean;
|
|
60
|
-
readonly
|
|
61
|
+
readonly renderPath?: 'forward' | 'deferred';
|
|
61
62
|
readonly surfaceQuat?: readonly [number, number, number, number];
|
|
63
|
+
readonly directionalLight?: boolean;
|
|
64
|
+
readonly localLight?: boolean;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
function textureAsset(value: number): TextureAsset {
|
|
65
68
|
const channel = Math.round(value * 255);
|
|
66
69
|
return {
|
|
67
70
|
kind: 'texture',
|
|
68
|
-
width: 1,
|
|
69
|
-
height: 1,
|
|
71
|
+
shape: { viewDimension: '2d', extent: { width: 1, height: 1 } },
|
|
70
72
|
format: 'rgba8unorm',
|
|
71
73
|
data: Uint8Array.from([channel, channel, channel, 255]),
|
|
72
74
|
colorSpace: 'linear',
|
|
73
|
-
|
|
75
|
+
mips: { kind: 'none' },
|
|
74
76
|
};
|
|
75
77
|
}
|
|
76
78
|
|
|
@@ -107,6 +109,7 @@ async function renderCase(testCase: TransmissionCase): Promise<{
|
|
|
107
109
|
readonly pixel: readonly [number, number, number, number];
|
|
108
110
|
readonly passNames: readonly string[];
|
|
109
111
|
readonly observation: boolean;
|
|
112
|
+
readonly errors: readonly string[];
|
|
110
113
|
}> {
|
|
111
114
|
let device: GPUDevice | undefined;
|
|
112
115
|
let configuredDevice: GPUDevice | undefined;
|
|
@@ -158,7 +161,7 @@ async function renderCase(testCase: TransmissionCase): Promise<{
|
|
|
158
161
|
{
|
|
159
162
|
standardProfile: {
|
|
160
163
|
...DEFAULT_STANDARD_PROFILE,
|
|
161
|
-
|
|
164
|
+
renderPath: testCase.renderPath ?? DEFAULT_STANDARD_PROFILE.renderPath,
|
|
162
165
|
},
|
|
163
166
|
},
|
|
164
167
|
{ shaderManifestUrl: ENGINE_MANIFEST_URL },
|
|
@@ -173,6 +176,10 @@ async function renderCase(testCase: TransmissionCase): Promise<{
|
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
const { renderer } = host.value;
|
|
179
|
+
const errors: string[] = [];
|
|
180
|
+
const unsubscribe = renderer.subscribe((event) => {
|
|
181
|
+
if (event.kind === 'error') errors.push(event.error.code);
|
|
182
|
+
});
|
|
176
183
|
const world = new World();
|
|
177
184
|
const lease = renderer.attach(world);
|
|
178
185
|
if (!lease.ok) throw lease.error;
|
|
@@ -278,12 +285,22 @@ async function renderCase(testCase: TransmissionCase): Promise<{
|
|
|
278
285
|
);
|
|
279
286
|
expect(cameraEntity.ok, `${testCase.name}: camera spawn must succeed`).toBe(true);
|
|
280
287
|
if (!cameraEntity.ok) throw cameraEntity.error;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
288
|
+
if (testCase.directionalLight !== false) {
|
|
289
|
+
const lightEntity = world.spawn({
|
|
290
|
+
component: DirectionalLight,
|
|
291
|
+
data: { direction: [0, 0, -1], color: [1, 1, 1], intensity: 1, castShadow: false },
|
|
292
|
+
});
|
|
293
|
+
expect(lightEntity.ok, `${testCase.name}: light spawn must succeed`).toBe(true);
|
|
294
|
+
if (!lightEntity.ok) throw lightEntity.error;
|
|
295
|
+
}
|
|
296
|
+
if (testCase.localLight === true) {
|
|
297
|
+
const localLightEntity = world.spawn(
|
|
298
|
+
{ component: Transform, data: { pos: [0, 0, 1] } },
|
|
299
|
+
{ component: PointLight, data: { intensity: 8, range: 10 } },
|
|
300
|
+
);
|
|
301
|
+
expect(localLightEntity.ok, `${testCase.name}: local light spawn must succeed`).toBe(true);
|
|
302
|
+
if (!localLightEntity.ok) throw localLightEntity.error;
|
|
303
|
+
}
|
|
287
304
|
world.update().unwrap();
|
|
288
305
|
const drawn = renderer.draw({
|
|
289
306
|
leases: [lease.value],
|
|
@@ -300,10 +317,12 @@ async function renderCase(testCase: TransmissionCase): Promise<{
|
|
|
300
317
|
const renderDevice = configuredDevice ?? device;
|
|
301
318
|
const passNames = renderer.inspect().perFramePassNames;
|
|
302
319
|
expect(renderer.inspect().renderScene.projectionRecords).toBeGreaterThanOrEqual(1);
|
|
320
|
+
unsubscribe();
|
|
303
321
|
return {
|
|
304
322
|
pixel: await readCenter(renderDevice, target),
|
|
305
323
|
passNames,
|
|
306
324
|
observation: observation.ok,
|
|
325
|
+
errors,
|
|
307
326
|
};
|
|
308
327
|
}
|
|
309
328
|
|
|
@@ -404,9 +423,9 @@ describe('Standard transmission Dawn ROI', () => {
|
|
|
404
423
|
);
|
|
405
424
|
}, 120_000);
|
|
406
425
|
|
|
407
|
-
it('exercises
|
|
408
|
-
const
|
|
409
|
-
name: '
|
|
426
|
+
it('exercises both Standard render paths and transmission-before-BLEND overlap order', async () => {
|
|
427
|
+
const forward = await renderCase({
|
|
428
|
+
name: 'forward',
|
|
410
429
|
transmission: 1,
|
|
411
430
|
ior: 1.5,
|
|
412
431
|
thickness: 0.5,
|
|
@@ -414,10 +433,10 @@ describe('Standard transmission Dawn ROI', () => {
|
|
|
414
433
|
attenuationColor: [0.7, 0.8, 0.9],
|
|
415
434
|
attenuationDistance: 2,
|
|
416
435
|
overlap: true,
|
|
417
|
-
|
|
436
|
+
renderPath: 'forward',
|
|
418
437
|
});
|
|
419
|
-
const
|
|
420
|
-
name: '
|
|
438
|
+
const deferred = await renderCase({
|
|
439
|
+
name: 'deferred',
|
|
421
440
|
transmission: 1,
|
|
422
441
|
ior: 1.5,
|
|
423
442
|
thickness: 0.5,
|
|
@@ -425,14 +444,33 @@ describe('Standard transmission Dawn ROI', () => {
|
|
|
425
444
|
attenuationColor: [0.7, 0.8, 0.9],
|
|
426
445
|
attenuationDistance: 2,
|
|
427
446
|
overlap: true,
|
|
428
|
-
|
|
447
|
+
renderPath: 'deferred',
|
|
429
448
|
});
|
|
430
|
-
const transmissionIndex =
|
|
431
|
-
const transparentIndex =
|
|
449
|
+
const transmissionIndex = forward.passNames.indexOf('transmission-forward');
|
|
450
|
+
const transparentIndex = forward.passNames.indexOf('transparent');
|
|
432
451
|
expect(transmissionIndex).toBeGreaterThanOrEqual(0);
|
|
433
452
|
expect(transparentIndex).toBeGreaterThan(transmissionIndex);
|
|
434
|
-
expect(
|
|
435
|
-
expect(
|
|
436
|
-
|
|
453
|
+
expect(deferred.pixel.every(Number.isFinite)).toBe(true);
|
|
454
|
+
expect(forward.pixel.every(Number.isFinite)).toBe(true);
|
|
455
|
+
}, 120_000);
|
|
456
|
+
|
|
457
|
+
it('submits a real clustered local-light frame through the Standard front door', async () => {
|
|
458
|
+
const clustered = await renderCase({
|
|
459
|
+
name: 'clustered-local-light',
|
|
460
|
+
transmission: 0,
|
|
461
|
+
ior: 1.5,
|
|
462
|
+
thickness: 0,
|
|
463
|
+
roughness: 0.4,
|
|
464
|
+
attenuationColor: [1, 1, 1],
|
|
465
|
+
directionalLight: false,
|
|
466
|
+
localLight: true,
|
|
467
|
+
renderPath: 'forward',
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
expect(clustered.passNames).toEqual(
|
|
471
|
+
expect.arrayContaining(['cluster-membership-producer', 'main', 'output-transform']),
|
|
472
|
+
);
|
|
473
|
+
expect(clustered.observation).toBe(true);
|
|
474
|
+
expect(clustered.errors).toEqual([]);
|
|
437
475
|
}, 120_000);
|
|
438
476
|
});
|
|
@@ -215,7 +215,15 @@ export function addTypedScenePass(
|
|
|
215
215
|
internal.pipelineState,
|
|
216
216
|
internal.validated.length > 0 || options.gpuDriven !== undefined,
|
|
217
217
|
internal.bindGroupCounts,
|
|
218
|
-
{
|
|
218
|
+
{
|
|
219
|
+
directionalShadow,
|
|
220
|
+
spotShadow,
|
|
221
|
+
projector: internal.spotLightProjector?.view ?? internal.volumetricFog?.projectorView,
|
|
222
|
+
projectorSampler:
|
|
223
|
+
internal.spotLightProjector?.sampler ?? internal.volumetricFog?.projectorSampler,
|
|
224
|
+
},
|
|
225
|
+
true,
|
|
226
|
+
internal.standardLighting,
|
|
219
227
|
);
|
|
220
228
|
if (groups.viewBindGroup !== null) {
|
|
221
229
|
options.gpuDriven?.encode(groups.viewBindGroup, pass, resources);
|
|
@@ -461,6 +469,10 @@ export interface TypedFullscreenPassOptions {
|
|
|
461
469
|
readonly key: string;
|
|
462
470
|
readonly target: RenderPipelineTarget;
|
|
463
471
|
}[];
|
|
472
|
+
/** Optional per-pass copy of the params UBO payload. */
|
|
473
|
+
readonly paramsTransform?:
|
|
474
|
+
| ((params: Uint8Array | undefined) => Uint8Array | undefined)
|
|
475
|
+
| undefined;
|
|
464
476
|
}
|
|
465
477
|
|
|
466
478
|
export interface TypedTemporalResolveTargets {
|
|
@@ -615,6 +627,11 @@ export function addTypedFullscreenPass(
|
|
|
615
627
|
...(options.rawSwapchainOutput === undefined
|
|
616
628
|
? {}
|
|
617
629
|
: { rawSwapchainOutput: options.rawSwapchainOutput }),
|
|
630
|
+
...(options.paramsTransform === undefined
|
|
631
|
+
? {}
|
|
632
|
+
: {
|
|
633
|
+
paramsOverride: options.paramsTransform(frame.postProcessParams.get(options.shader)),
|
|
634
|
+
}),
|
|
618
635
|
});
|
|
619
636
|
},
|
|
620
637
|
});
|
|
@@ -681,8 +698,9 @@ export function addTypedOutputTransformPass(
|
|
|
681
698
|
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
682
699
|
input: RenderPipelineTarget,
|
|
683
700
|
output: RenderPipelineTarget,
|
|
684
|
-
outputOnly =
|
|
701
|
+
options: { readonly outputOnly?: boolean; readonly dither?: boolean } = {},
|
|
685
702
|
): Result<void, RenderGraphError> {
|
|
703
|
+
const outputOnly = options.outputOnly ?? false;
|
|
686
704
|
return addTypedFullscreenPass(graph, {
|
|
687
705
|
name: outputOnly ? 'present' : 'output-transform',
|
|
688
706
|
shader: STANDARD_OUTPUT_TRANSFORM_FEATURE_ID,
|
|
@@ -690,5 +708,19 @@ export function addTypedOutputTransformPass(
|
|
|
690
708
|
output,
|
|
691
709
|
outputOnly,
|
|
692
710
|
rawSwapchainOutput: outputOnly,
|
|
711
|
+
...(options.dither !== undefined
|
|
712
|
+
? {
|
|
713
|
+
paramsTransform: (params: Uint8Array | undefined): Uint8Array | undefined => {
|
|
714
|
+
if (params === undefined || params.byteLength !== 16) return params;
|
|
715
|
+
const transformed = params.slice();
|
|
716
|
+
new DataView(
|
|
717
|
+
transformed.buffer,
|
|
718
|
+
transformed.byteOffset,
|
|
719
|
+
transformed.byteLength,
|
|
720
|
+
).setFloat32(12, options.dither === true ? 1 : 0, true);
|
|
721
|
+
return transformed;
|
|
722
|
+
},
|
|
723
|
+
}
|
|
724
|
+
: {}),
|
|
693
725
|
});
|
|
694
726
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface DirectionalShadowAtlasGrid {
|
|
2
|
+
readonly columns: 1 | 2;
|
|
3
|
+
readonly rows: 1 | 2;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The directional CSM atlas layout is one compact contract for raster writes
|
|
8
|
+
* and shader reads: 1 -> 1x1, 2 -> 2x1, and 3/4 -> 2x2.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveDirectionalShadowAtlasGrid(
|
|
11
|
+
cascadeCount: number,
|
|
12
|
+
): DirectionalShadowAtlasGrid {
|
|
13
|
+
const count = Math.max(1, Math.min(4, Math.round(cascadeCount)));
|
|
14
|
+
if (count === 1) return { columns: 1, rows: 1 };
|
|
15
|
+
if (count === 2) return { columns: 2, rows: 1 };
|
|
16
|
+
return { columns: 2, rows: 2 };
|
|
17
|
+
}
|
|
@@ -10,9 +10,10 @@ import {
|
|
|
10
10
|
} from './record/shadow-pass';
|
|
11
11
|
import type { RenderPipelineFrame, RenderPipelineTopology } from './render-pipeline';
|
|
12
12
|
import { createRenderPipelineTarget, type RenderPipelineTarget } from './render-pipeline';
|
|
13
|
+
import { resolveDirectionalShadowAtlasGrid } from './typed-shadow-atlas';
|
|
13
14
|
|
|
14
15
|
export interface TypedShadowTargets {
|
|
15
|
-
readonly directional
|
|
16
|
+
readonly directional?: RenderPipelineTarget | undefined;
|
|
16
17
|
readonly spot: RenderPipelineTarget;
|
|
17
18
|
readonly point?: RenderPipelineTarget | undefined;
|
|
18
19
|
}
|
|
@@ -43,44 +44,50 @@ export function addTypedShadowPasses(
|
|
|
43
44
|
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
44
45
|
topology: RenderPipelineTopology,
|
|
45
46
|
): Result<TypedShadowTargets, RenderGraphError> {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
47
|
+
let directional: RenderPipelineTarget | undefined;
|
|
48
|
+
if (topology.shadow.directional !== 'disabled') {
|
|
49
|
+
const { mapSize, cascadeCount } = topology.shadow.directional;
|
|
50
|
+
const atlasGrid = resolveDirectionalShadowAtlasGrid(cascadeCount);
|
|
51
|
+
const target = createRenderPipelineTarget(graph, 'directional-shadow-depth', {
|
|
52
|
+
format: 'depth32float',
|
|
53
|
+
size: {
|
|
54
|
+
width: mapSize * atlasGrid.columns,
|
|
55
|
+
height: mapSize * atlasGrid.rows,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
if (!target.ok) return target;
|
|
59
|
+
const directionalTarget = target.value;
|
|
60
|
+
directional = directionalTarget;
|
|
61
|
+
for (let cascade = 0; cascade < cascadeCount; cascade += 1) {
|
|
62
|
+
const col = cascade % atlasGrid.columns;
|
|
63
|
+
const row = Math.floor(cascade / atlasGrid.columns);
|
|
64
|
+
const added = recordShadowPass(
|
|
65
|
+
graph,
|
|
66
|
+
`shadowCascade${cascade}`,
|
|
67
|
+
directionalTarget,
|
|
68
|
+
cascade === 0 ? 'clear' : 'load',
|
|
69
|
+
({ pass, frame }) =>
|
|
70
|
+
encodeDirectionalShadowPass(frame as never, pass, cascade, {
|
|
71
|
+
x: col * mapSize,
|
|
72
|
+
y: row * mapSize,
|
|
73
|
+
w: mapSize,
|
|
74
|
+
h: mapSize,
|
|
75
|
+
}),
|
|
76
|
+
(frame) => !(frame as _InternalRenderPipelineContext).directionalShadowCacheReuse,
|
|
77
|
+
);
|
|
78
|
+
if (!added.ok) return added;
|
|
79
|
+
}
|
|
80
|
+
const observation = graph.addCopyPass('directional-shadow-observation', {
|
|
81
|
+
accesses: [{ resource: directionalTarget.view, usage: 'copy-src' }],
|
|
82
|
+
encode: ({ frame, resources }) => {
|
|
83
|
+
const internal = frame as _InternalRenderPipelineContext;
|
|
84
|
+
const texture = resources.texture(directionalTarget.texture);
|
|
85
|
+
if (!texture.ok) throw texture.error;
|
|
86
|
+
internal.pipelineState.perPassResources.shadowTexture = texture.value;
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
if (!observation.ok) return observation;
|
|
73
90
|
}
|
|
74
|
-
const observation = graph.addCopyPass('directional-shadow-observation', {
|
|
75
|
-
accesses: [{ resource: directional.value.view, usage: 'copy-src' }],
|
|
76
|
-
encode: ({ frame, resources }) => {
|
|
77
|
-
const internal = frame as _InternalRenderPipelineContext;
|
|
78
|
-
const texture = resources.texture(directional.value.texture);
|
|
79
|
-
if (!texture.ok) throw texture.error;
|
|
80
|
-
internal.pipelineState.perPassResources.shadowTexture = texture.value;
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
if (!observation.ok) return observation;
|
|
84
91
|
|
|
85
92
|
let point: RenderPipelineTarget | undefined;
|
|
86
93
|
if (topology.shadow.pointCount > 0) {
|
|
@@ -147,7 +154,7 @@ export function addTypedShadowPasses(
|
|
|
147
154
|
|
|
148
155
|
const spot = createRenderPipelineTarget(graph, 'spot-shadow-depth', {
|
|
149
156
|
format: 'depth32float',
|
|
150
|
-
size: { width: topology.shadow.
|
|
157
|
+
size: { width: topology.shadow.spotMapSize * 2, height: topology.shadow.spotMapSize * 2 },
|
|
151
158
|
});
|
|
152
159
|
if (!spot.ok) return spot;
|
|
153
160
|
for (let spotIndex = 0; spotIndex < Math.max(1, topology.shadow.spotCount); spotIndex += 1) {
|
|
@@ -161,5 +168,5 @@ export function addTypedShadowPasses(
|
|
|
161
168
|
if (!added.ok) return added;
|
|
162
169
|
}
|
|
163
170
|
|
|
164
|
-
return ok({ directional
|
|
171
|
+
return ok({ directional, spot: spot.value, point });
|
|
165
172
|
}
|