@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,182 @@
|
|
|
1
|
+
import type { RenderError } from '../errors/render';
|
|
2
|
+
import {
|
|
3
|
+
RenderTargetCapabilityMissingError,
|
|
4
|
+
RenderTargetDescriptorInvalidError,
|
|
5
|
+
} from '../errors/render';
|
|
6
|
+
import type { RenderResult } from '../render-contract';
|
|
7
|
+
|
|
8
|
+
export type RenderTargetFormat = 'rgba16float' | 'rgba8unorm' | 'rgba8unorm-srgb';
|
|
9
|
+
export type RenderTargetShape = '2d' | 'cube';
|
|
10
|
+
export type RenderTargetMipLevels = 1 | 'full';
|
|
11
|
+
export type RenderTargetSampleCount = 1 | 4;
|
|
12
|
+
export type RenderTargetDepthFormat = 'depth24plus-stencil8' | 'depth32float';
|
|
13
|
+
|
|
14
|
+
export interface RenderTargetDescriptor {
|
|
15
|
+
readonly shape: RenderTargetShape;
|
|
16
|
+
readonly width: number;
|
|
17
|
+
readonly height: number;
|
|
18
|
+
readonly format: RenderTargetFormat;
|
|
19
|
+
readonly mipLevels: RenderTargetMipLevels;
|
|
20
|
+
readonly sampleCount: RenderTargetSampleCount;
|
|
21
|
+
readonly depth?: RenderTargetDepthFormat;
|
|
22
|
+
readonly sampled: boolean;
|
|
23
|
+
readonly readback: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare const RenderTargetBrand: unique symbol;
|
|
27
|
+
declare const RenderTargetTextureSourceBrand: unique symbol;
|
|
28
|
+
declare const RenderTargetReadbackTicketBrand: unique symbol;
|
|
29
|
+
|
|
30
|
+
/** Opaque logical target lease owned by one Renderer. */
|
|
31
|
+
export interface RenderTarget {
|
|
32
|
+
readonly [RenderTargetBrand]: 'RenderTarget';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type RenderTargetTextureAspect = 'color';
|
|
36
|
+
|
|
37
|
+
export interface RenderTargetTextureSourceOptions {
|
|
38
|
+
readonly aspect: RenderTargetTextureAspect;
|
|
39
|
+
readonly dimension: RenderTargetShape;
|
|
40
|
+
readonly mipLevel: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Renderer-local runtime material source; it is not an asset handle. */
|
|
44
|
+
export interface RenderTargetTextureSource {
|
|
45
|
+
readonly [RenderTargetTextureSourceBrand]: 'RenderTargetTextureSource';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface RenderTargetReadbackRequest {
|
|
49
|
+
readonly mipLevel: number;
|
|
50
|
+
/** Cube array layer to copy; 2D targets accept only the omitted form. */
|
|
51
|
+
readonly face?: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** One-shot readback request bound to a future matching FrameReceipt. */
|
|
55
|
+
export interface RenderTargetReadbackTicket {
|
|
56
|
+
readonly [RenderTargetReadbackTicketBrand]: 'RenderTargetReadbackTicket';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Bytes released by observe after the ticket's matching frame completes. */
|
|
60
|
+
export interface RenderTargetReadbackData {
|
|
61
|
+
readonly ticket: RenderTargetReadbackTicket;
|
|
62
|
+
readonly bytes: Uint8Array;
|
|
63
|
+
readonly frameId: number;
|
|
64
|
+
readonly deviceGeneration: number;
|
|
65
|
+
readonly mipLevel: number;
|
|
66
|
+
readonly face?: number;
|
|
67
|
+
readonly bytesPerRow: number;
|
|
68
|
+
readonly byteLength: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface RenderTargetAdmissionLimits {
|
|
72
|
+
readonly maxTextureDimension2D: number;
|
|
73
|
+
readonly maxBytesPerTarget: number;
|
|
74
|
+
readonly renderableFormats: readonly RenderTargetFormat[];
|
|
75
|
+
readonly sampleCounts: readonly RenderTargetSampleCount[];
|
|
76
|
+
readonly depthFormats: readonly RenderTargetDepthFormat[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const FORMAT_BYTES: Readonly<Record<RenderTargetFormat, number>> = {
|
|
80
|
+
rgba16float: 8,
|
|
81
|
+
rgba8unorm: 4,
|
|
82
|
+
'rgba8unorm-srgb': 4,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function mipFactor(mipLevels: RenderTargetMipLevels): number {
|
|
86
|
+
return mipLevels === 'full' ? 4 / 3 : 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function estimatedBytes(descriptor: RenderTargetDescriptor): number {
|
|
90
|
+
const layers = descriptor.shape === 'cube' ? 6 : 1;
|
|
91
|
+
const samples = descriptor.sampleCount;
|
|
92
|
+
const depthBytes = descriptor.depth === undefined ? 0 : 4;
|
|
93
|
+
return Math.ceil(
|
|
94
|
+
descriptor.width *
|
|
95
|
+
descriptor.height *
|
|
96
|
+
FORMAT_BYTES[descriptor.format] *
|
|
97
|
+
layers *
|
|
98
|
+
samples *
|
|
99
|
+
mipFactor(descriptor.mipLevels) +
|
|
100
|
+
descriptor.width *
|
|
101
|
+
descriptor.height *
|
|
102
|
+
depthBytes *
|
|
103
|
+
layers *
|
|
104
|
+
samples *
|
|
105
|
+
mipFactor(descriptor.mipLevels),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function invalid(
|
|
110
|
+
field: string,
|
|
111
|
+
value: unknown,
|
|
112
|
+
expected: string,
|
|
113
|
+
): RenderResult<never, RenderError> {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
error: new RenderTargetDescriptorInvalidError({ field, value, expected }),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Validate descriptor facts before a renderer allocates a physical target. */
|
|
121
|
+
export function admitRenderTargetDescriptor(
|
|
122
|
+
descriptor: RenderTargetDescriptor,
|
|
123
|
+
limits: RenderTargetAdmissionLimits,
|
|
124
|
+
): RenderResult<RenderTargetDescriptor, RenderError> {
|
|
125
|
+
if (!Number.isInteger(descriptor.width) || descriptor.width < 1) {
|
|
126
|
+
return invalid('width', descriptor.width, 'an integer >= 1');
|
|
127
|
+
}
|
|
128
|
+
if (!Number.isInteger(descriptor.height) || descriptor.height < 1) {
|
|
129
|
+
return invalid('height', descriptor.height, 'an integer >= 1');
|
|
130
|
+
}
|
|
131
|
+
if (
|
|
132
|
+
descriptor.width > limits.maxTextureDimension2D ||
|
|
133
|
+
descriptor.height > limits.maxTextureDimension2D
|
|
134
|
+
) {
|
|
135
|
+
return invalid(
|
|
136
|
+
'extent',
|
|
137
|
+
{ width: descriptor.width, height: descriptor.height },
|
|
138
|
+
`width and height <= ${limits.maxTextureDimension2D}`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (descriptor.shape === 'cube' && descriptor.width !== descriptor.height) {
|
|
142
|
+
return invalid('shape', descriptor.shape, 'cube width === height');
|
|
143
|
+
}
|
|
144
|
+
if (!limits.renderableFormats.includes(descriptor.format)) {
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
error: new RenderTargetCapabilityMissingError({
|
|
148
|
+
operation: 'create',
|
|
149
|
+
requested: descriptor.format,
|
|
150
|
+
capability: 'renderableFormats',
|
|
151
|
+
actual: limits.renderableFormats.join(', '),
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (!limits.sampleCounts.includes(descriptor.sampleCount)) {
|
|
156
|
+
return {
|
|
157
|
+
ok: false,
|
|
158
|
+
error: new RenderTargetCapabilityMissingError({
|
|
159
|
+
operation: 'create',
|
|
160
|
+
requested: String(descriptor.sampleCount),
|
|
161
|
+
capability: 'sampleCounts',
|
|
162
|
+
actual: limits.sampleCounts.join(', '),
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (descriptor.depth !== undefined && !limits.depthFormats.includes(descriptor.depth)) {
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
error: new RenderTargetCapabilityMissingError({
|
|
170
|
+
operation: 'create',
|
|
171
|
+
requested: descriptor.depth,
|
|
172
|
+
capability: 'depthFormats',
|
|
173
|
+
actual: limits.depthFormats.join(', '),
|
|
174
|
+
}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const bytes = estimatedBytes(descriptor);
|
|
178
|
+
if (bytes > limits.maxBytesPerTarget) {
|
|
179
|
+
return invalid('bytes', bytes, `estimated allocation <= ${limits.maxBytesPerTarget}`);
|
|
180
|
+
}
|
|
181
|
+
return { ok: true, value: descriptor };
|
|
182
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GraphTexture,
|
|
3
|
+
GraphTextureView,
|
|
4
|
+
GraphTextureViewDescriptor,
|
|
5
|
+
RenderGraphBuilder,
|
|
6
|
+
RenderGraphFrame,
|
|
7
|
+
} from '@forgeax/engine-render-graph';
|
|
8
|
+
import type { Texture, TextureFormat, TextureView } from '@forgeax/engine-rhi';
|
|
9
|
+
import type { RenderError } from '../errors/render';
|
|
10
|
+
import { RenderTargetOperationFailedError, RenderTargetStateInvalidError } from '../errors/render';
|
|
11
|
+
import type { RenderResult } from '../render-contract';
|
|
12
|
+
import type { RenderTarget, RenderTargetDescriptor } from './contracts';
|
|
13
|
+
|
|
14
|
+
export interface RenderTargetGraphInput {
|
|
15
|
+
readonly target: RenderTarget;
|
|
16
|
+
readonly generation: number;
|
|
17
|
+
readonly descriptor: RenderTargetDescriptor;
|
|
18
|
+
readonly texture: Texture;
|
|
19
|
+
readonly view: TextureView;
|
|
20
|
+
readonly resolveView?: TextureView | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RenderTargetGraphSubresource {
|
|
24
|
+
readonly mipLevel?: number;
|
|
25
|
+
readonly face?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RenderTargetGraphProjection {
|
|
29
|
+
readonly ownership: 'imported';
|
|
30
|
+
readonly generation: number;
|
|
31
|
+
readonly texture: GraphTexture;
|
|
32
|
+
readonly view: GraphTextureView;
|
|
33
|
+
readonly sampleView: GraphTextureView;
|
|
34
|
+
readonly resolveView?: GraphTextureView | undefined;
|
|
35
|
+
readonly viewDescriptor: GraphTextureViewDescriptor;
|
|
36
|
+
readonly usage: readonly ('sampled-read' | 'copy-src')[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function operationFailed(generation: number, cause: unknown): RenderResult<never, RenderError> {
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
error: new RenderTargetOperationFailedError({
|
|
43
|
+
operation: 'source',
|
|
44
|
+
stage: 'copy',
|
|
45
|
+
generation,
|
|
46
|
+
cause,
|
|
47
|
+
recovery: 'retry',
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function mipCount(descriptor: RenderTargetDescriptor): number {
|
|
53
|
+
if (descriptor.mipLevels === 1) return 1;
|
|
54
|
+
return Math.floor(Math.log2(Math.max(descriptor.width, descriptor.height))) + 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function viewDescriptor(
|
|
58
|
+
descriptor: RenderTargetDescriptor,
|
|
59
|
+
subresource: RenderTargetGraphSubresource,
|
|
60
|
+
): GraphTextureViewDescriptor {
|
|
61
|
+
const mipLevel = subresource.mipLevel ?? 0;
|
|
62
|
+
return {
|
|
63
|
+
dimension: descriptor.shape,
|
|
64
|
+
baseMipLevel: mipLevel,
|
|
65
|
+
mipLevelCount: 1,
|
|
66
|
+
...(descriptor.shape === 'cube' && subresource.face === undefined
|
|
67
|
+
? {}
|
|
68
|
+
: subresource.face === undefined
|
|
69
|
+
? {}
|
|
70
|
+
: { baseArrayLayer: subresource.face, arrayLayerCount: 1 }),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function validateSubresource(
|
|
75
|
+
descriptor: RenderTargetDescriptor,
|
|
76
|
+
subresource: RenderTargetGraphSubresource,
|
|
77
|
+
): RenderResult<void, RenderError> {
|
|
78
|
+
const mipLevel = subresource.mipLevel ?? 0;
|
|
79
|
+
if (!Number.isInteger(mipLevel) || mipLevel < 0 || mipLevel >= mipCount(descriptor)) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
error: new RenderTargetStateInvalidError({
|
|
83
|
+
operation: 'source',
|
|
84
|
+
reason: 'uninitialized',
|
|
85
|
+
state: 'uninitialized',
|
|
86
|
+
generation: 0,
|
|
87
|
+
}),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (
|
|
91
|
+
subresource.face !== undefined &&
|
|
92
|
+
(!Number.isInteger(subresource.face) || subresource.face < 0 || subresource.face > 5)
|
|
93
|
+
) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
error: new RenderTargetStateInvalidError({
|
|
97
|
+
operation: 'source',
|
|
98
|
+
reason: 'uninitialized',
|
|
99
|
+
state: 'uninitialized',
|
|
100
|
+
generation: 0,
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return { ok: true, value: undefined };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function projectRenderTargetGraph<FrameCtx extends RenderGraphFrame>(
|
|
108
|
+
graph: RenderGraphBuilder<FrameCtx>,
|
|
109
|
+
input: RenderTargetGraphInput,
|
|
110
|
+
subresource: RenderTargetGraphSubresource = {},
|
|
111
|
+
): RenderResult<RenderTargetGraphProjection, RenderError> {
|
|
112
|
+
const valid = validateSubresource(input.descriptor, subresource);
|
|
113
|
+
if (!valid.ok) return valid;
|
|
114
|
+
if (input.descriptor.sampleCount === 4 && input.resolveView === undefined) {
|
|
115
|
+
return operationFailed(
|
|
116
|
+
input.generation,
|
|
117
|
+
new Error('multisample target requires a single-sample resolve view before sampling'),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
const descriptor = viewDescriptor(input.descriptor, subresource);
|
|
121
|
+
const imported = graph.importTexture(
|
|
122
|
+
`render-target.${input.generation}`,
|
|
123
|
+
{
|
|
124
|
+
format: input.descriptor.format as TextureFormat,
|
|
125
|
+
size: {
|
|
126
|
+
width: input.descriptor.width,
|
|
127
|
+
height: input.descriptor.height,
|
|
128
|
+
depthOrArrayLayers: input.descriptor.shape === 'cube' ? 6 : 1,
|
|
129
|
+
},
|
|
130
|
+
mipLevelCount: mipCount(input.descriptor),
|
|
131
|
+
sampleCount: input.descriptor.sampleCount,
|
|
132
|
+
dimension: '2d',
|
|
133
|
+
usage: 0x10 | 0x04 | (input.descriptor.readback ? 0x01 : 0),
|
|
134
|
+
},
|
|
135
|
+
() => input.texture,
|
|
136
|
+
);
|
|
137
|
+
if (!imported.ok) return operationFailed(input.generation, imported.error);
|
|
138
|
+
const view = graph.importView(imported.value, descriptor, () => input.view);
|
|
139
|
+
if (!view.ok) return operationFailed(input.generation, view.error);
|
|
140
|
+
let resolveView: GraphTextureView | undefined;
|
|
141
|
+
if (input.resolveView !== undefined) {
|
|
142
|
+
const resolved = graph.importView(
|
|
143
|
+
imported.value,
|
|
144
|
+
descriptor,
|
|
145
|
+
() => input.resolveView as TextureView,
|
|
146
|
+
);
|
|
147
|
+
if (!resolved.ok) return operationFailed(input.generation, resolved.error);
|
|
148
|
+
resolveView = resolved.value;
|
|
149
|
+
}
|
|
150
|
+
const sampleView = input.descriptor.sampleCount === 4 ? resolveView : view.value;
|
|
151
|
+
if (sampleView === undefined)
|
|
152
|
+
return operationFailed(input.generation, new Error('missing sample view'));
|
|
153
|
+
return {
|
|
154
|
+
ok: true,
|
|
155
|
+
value: {
|
|
156
|
+
ownership: 'imported',
|
|
157
|
+
generation: input.generation,
|
|
158
|
+
texture: imported.value,
|
|
159
|
+
view: view.value,
|
|
160
|
+
sampleView,
|
|
161
|
+
resolveView,
|
|
162
|
+
viewDescriptor: descriptor,
|
|
163
|
+
usage: [
|
|
164
|
+
...(input.descriptor.sampled ? (['sampled-read'] as const) : []),
|
|
165
|
+
...(input.descriptor.readback ? (['copy-src'] as const) : []),
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { TextureView } from '@forgeax/engine-rhi';
|
|
2
|
+
import type { RenderError } from '../errors/render';
|
|
3
|
+
import {
|
|
4
|
+
RenderTargetDescriptorInvalidError,
|
|
5
|
+
RenderTargetStateInvalidError,
|
|
6
|
+
} from '../errors/render';
|
|
7
|
+
import type { RenderResult } from '../render-contract';
|
|
8
|
+
import type {
|
|
9
|
+
RenderTarget,
|
|
10
|
+
RenderTargetDescriptor,
|
|
11
|
+
RenderTargetTextureSource,
|
|
12
|
+
RenderTargetTextureSourceOptions,
|
|
13
|
+
} from './contracts';
|
|
14
|
+
|
|
15
|
+
export interface RenderTargetMaterialSourceView {
|
|
16
|
+
readonly dimension: RenderTargetTextureSourceOptions['dimension'];
|
|
17
|
+
readonly mipLevel: number;
|
|
18
|
+
readonly resolveRequired: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RenderTargetMaterialSourceBinding {
|
|
22
|
+
readonly source: RenderTargetTextureSource;
|
|
23
|
+
readonly target: RenderTarget;
|
|
24
|
+
readonly generation: number;
|
|
25
|
+
readonly shape: RenderTargetDescriptor['shape'];
|
|
26
|
+
readonly format: RenderTargetDescriptor['format'];
|
|
27
|
+
readonly view: RenderTargetMaterialSourceView;
|
|
28
|
+
/** Renderer-local physical view, present only for the active generation. */
|
|
29
|
+
readonly textureView?: TextureView;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RenderTargetMaterialSourceExpectation {
|
|
33
|
+
readonly target: RenderTarget;
|
|
34
|
+
readonly generation: number;
|
|
35
|
+
readonly shape: RenderTargetDescriptor['shape'];
|
|
36
|
+
readonly format: RenderTargetDescriptor['format'];
|
|
37
|
+
readonly dimension: RenderTargetTextureSourceOptions['dimension'];
|
|
38
|
+
readonly mipLevel: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sourceBindings = new WeakMap<object, RenderTargetMaterialSourceBinding>();
|
|
42
|
+
|
|
43
|
+
function sourceToken(): RenderTargetTextureSource {
|
|
44
|
+
return Object.freeze({}) as RenderTargetTextureSource;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Resolve a source only inside the renderer-owned material projection. */
|
|
48
|
+
export function resolveRenderTargetMaterialSource(
|
|
49
|
+
source: RenderTargetTextureSource,
|
|
50
|
+
): RenderTargetMaterialSourceBinding | undefined {
|
|
51
|
+
return sourceBindings.get(source as object);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function invalid(
|
|
55
|
+
field: string,
|
|
56
|
+
value: unknown,
|
|
57
|
+
expected: string,
|
|
58
|
+
): RenderResult<never, RenderError> {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
error: new RenderTargetDescriptorInvalidError({ field, value, expected }),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function createRenderTargetMaterialSource(
|
|
66
|
+
target: RenderTarget,
|
|
67
|
+
descriptor: RenderTargetDescriptor,
|
|
68
|
+
options: RenderTargetTextureSourceOptions & { readonly generation: number },
|
|
69
|
+
): RenderResult<RenderTargetMaterialSourceBinding, RenderError> {
|
|
70
|
+
if (options.aspect !== 'color') return invalid('aspect', options.aspect, 'color');
|
|
71
|
+
if (options.dimension !== descriptor.shape) {
|
|
72
|
+
return invalid('dimension', options.dimension, `dimension matches ${descriptor.shape}`);
|
|
73
|
+
}
|
|
74
|
+
const mipCount =
|
|
75
|
+
descriptor.mipLevels === 1
|
|
76
|
+
? 1
|
|
77
|
+
: Math.floor(Math.log2(Math.max(descriptor.width, descriptor.height))) + 1;
|
|
78
|
+
if (!Number.isInteger(options.mipLevel) || options.mipLevel < 0 || options.mipLevel >= mipCount) {
|
|
79
|
+
return invalid('mipLevel', options.mipLevel, `an admitted mip level below ${mipCount}`);
|
|
80
|
+
}
|
|
81
|
+
if (!Number.isInteger(options.generation) || options.generation < 0) {
|
|
82
|
+
return invalid('generation', options.generation, 'a non-negative device generation');
|
|
83
|
+
}
|
|
84
|
+
const result: RenderResult<RenderTargetMaterialSourceBinding, RenderError> = {
|
|
85
|
+
ok: true,
|
|
86
|
+
value: Object.freeze({
|
|
87
|
+
source: sourceToken(),
|
|
88
|
+
target,
|
|
89
|
+
generation: options.generation,
|
|
90
|
+
shape: descriptor.shape,
|
|
91
|
+
format: descriptor.format,
|
|
92
|
+
view: Object.freeze({
|
|
93
|
+
dimension: options.dimension,
|
|
94
|
+
mipLevel: options.mipLevel,
|
|
95
|
+
resolveRequired: descriptor.sampleCount === 4,
|
|
96
|
+
}),
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
sourceBindings.set(result.value.source as object, result.value);
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function validateRenderTargetMaterialSource(
|
|
104
|
+
binding: RenderTargetMaterialSourceBinding,
|
|
105
|
+
expected: RenderTargetMaterialSourceExpectation,
|
|
106
|
+
): RenderResult<void, RenderError> {
|
|
107
|
+
if (binding.target !== expected.target) {
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
error: new RenderTargetStateInvalidError({
|
|
111
|
+
operation: 'source',
|
|
112
|
+
reason: 'foreign-renderer',
|
|
113
|
+
state: 'active',
|
|
114
|
+
generation: binding.generation,
|
|
115
|
+
}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (binding.generation !== expected.generation) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
error: new RenderTargetStateInvalidError({
|
|
122
|
+
operation: 'source',
|
|
123
|
+
reason: 'generation-mismatch',
|
|
124
|
+
state: 'active',
|
|
125
|
+
generation: binding.generation,
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (binding.shape !== expected.shape || binding.format !== expected.format) {
|
|
130
|
+
return invalid(
|
|
131
|
+
'descriptor',
|
|
132
|
+
{ shape: binding.shape, format: binding.format },
|
|
133
|
+
'matching target descriptor',
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (
|
|
137
|
+
binding.view.dimension !== expected.dimension ||
|
|
138
|
+
binding.view.mipLevel !== expected.mipLevel
|
|
139
|
+
) {
|
|
140
|
+
return invalid('view', binding.view, 'matching target view subresource');
|
|
141
|
+
}
|
|
142
|
+
return { ok: true, value: undefined };
|
|
143
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
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, RenderTargetDescriptor } from './contracts';
|
|
5
|
+
|
|
6
|
+
export type RenderTargetOwnerState =
|
|
7
|
+
| 'uninitialized'
|
|
8
|
+
| 'candidate'
|
|
9
|
+
| 'active'
|
|
10
|
+
| 'rebuilding'
|
|
11
|
+
| 'destroyed';
|
|
12
|
+
|
|
13
|
+
export interface RenderTargetPhysicalCandidate {
|
|
14
|
+
readonly generation: number;
|
|
15
|
+
readonly descriptor: RenderTargetDescriptor;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RenderTargetOwnerInspection {
|
|
19
|
+
readonly token: RenderTarget;
|
|
20
|
+
readonly state: RenderTargetOwnerState;
|
|
21
|
+
readonly generation: number;
|
|
22
|
+
readonly descriptor: RenderTargetDescriptor;
|
|
23
|
+
readonly candidate?: RenderTargetPhysicalCandidate;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RenderTargetOwnerOptions {
|
|
27
|
+
readonly rendererId: symbol;
|
|
28
|
+
readonly initialGeneration: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RenderTargetOwner {
|
|
32
|
+
create(descriptor: RenderTargetDescriptor): RenderResult<RenderTarget, RenderError>;
|
|
33
|
+
stage(
|
|
34
|
+
target: RenderTarget,
|
|
35
|
+
descriptor?: RenderTargetDescriptor,
|
|
36
|
+
): RenderResult<RenderTargetPhysicalCandidate, RenderError>;
|
|
37
|
+
promote(target: RenderTarget, generation: number): RenderResult<void, RenderError>;
|
|
38
|
+
rejectCandidate(
|
|
39
|
+
target: RenderTarget,
|
|
40
|
+
generation: number,
|
|
41
|
+
stage: 'allocation' | 'compile' | 'write' | 'submit',
|
|
42
|
+
): RenderResult<void, RenderError>;
|
|
43
|
+
beginRecovery(target: RenderTarget, generation: number): RenderResult<void, RenderError>;
|
|
44
|
+
finishRecovery(target: RenderTarget): RenderResult<void, RenderError>;
|
|
45
|
+
retire(target: RenderTarget, generation: number): RenderResult<void, RenderError>;
|
|
46
|
+
destroy(target: RenderTarget): RenderResult<void, RenderError>;
|
|
47
|
+
inspect(target: RenderTarget): RenderResult<RenderTargetOwnerInspection, RenderError>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface TargetState {
|
|
51
|
+
readonly target: RenderTarget;
|
|
52
|
+
readonly rendererId: symbol;
|
|
53
|
+
state: RenderTargetOwnerState;
|
|
54
|
+
generation: number;
|
|
55
|
+
descriptor: RenderTargetDescriptor;
|
|
56
|
+
candidate: RenderTargetPhysicalCandidate | undefined;
|
|
57
|
+
retiredGenerations: Set<number>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function frozenDescriptor(descriptor: RenderTargetDescriptor): RenderTargetDescriptor {
|
|
61
|
+
return Object.freeze({ ...descriptor });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function invalid(
|
|
65
|
+
operation: 'inspect' | 'resize' | 'source' | 'readback' | 'destroy',
|
|
66
|
+
reason: 'foreign-renderer' | 'destroyed' | 'uninitialized' | 'generation-mismatch',
|
|
67
|
+
state: RenderTargetOwnerState,
|
|
68
|
+
generation: number,
|
|
69
|
+
): RenderResult<never, RenderError> {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: new RenderTargetStateInvalidError({ operation, reason, state, generation }),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function token(): RenderTarget {
|
|
77
|
+
return Object.freeze({}) as RenderTarget;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function createRenderTargetOwner(options: RenderTargetOwnerOptions): RenderTargetOwner {
|
|
81
|
+
const targets = new WeakMap<object, TargetState>();
|
|
82
|
+
const requireTarget = (
|
|
83
|
+
target: RenderTarget,
|
|
84
|
+
operation: 'inspect' | 'resize' | 'source' | 'readback' | 'destroy',
|
|
85
|
+
): RenderResult<TargetState, RenderError> => {
|
|
86
|
+
const state = targets.get(target as object);
|
|
87
|
+
if (state === undefined) return invalid(operation, 'foreign-renderer', 'destroyed', 0);
|
|
88
|
+
if (state.rendererId !== options.rendererId) {
|
|
89
|
+
return invalid(operation, 'foreign-renderer', state.state, state.generation);
|
|
90
|
+
}
|
|
91
|
+
if (state.state === 'destroyed') {
|
|
92
|
+
return invalid(operation, 'destroyed', state.state, state.generation);
|
|
93
|
+
}
|
|
94
|
+
return { ok: true, value: state };
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
create(descriptor) {
|
|
99
|
+
const target = token();
|
|
100
|
+
targets.set(target as object, {
|
|
101
|
+
target,
|
|
102
|
+
rendererId: options.rendererId,
|
|
103
|
+
state: 'uninitialized',
|
|
104
|
+
generation: 0,
|
|
105
|
+
descriptor: frozenDescriptor(descriptor),
|
|
106
|
+
candidate: undefined,
|
|
107
|
+
retiredGenerations: new Set(),
|
|
108
|
+
});
|
|
109
|
+
return { ok: true, value: target };
|
|
110
|
+
},
|
|
111
|
+
stage(target, descriptor) {
|
|
112
|
+
const found = requireTarget(target, 'resize');
|
|
113
|
+
if (!found.ok) return found;
|
|
114
|
+
const state = found.value;
|
|
115
|
+
if (state.state === 'rebuilding') {
|
|
116
|
+
return invalid('resize', 'generation-mismatch', state.state, state.generation);
|
|
117
|
+
}
|
|
118
|
+
const candidate = Object.freeze({
|
|
119
|
+
generation: state.state === 'uninitialized' ? options.initialGeneration : state.generation,
|
|
120
|
+
descriptor: frozenDescriptor(descriptor ?? state.descriptor),
|
|
121
|
+
});
|
|
122
|
+
state.candidate = candidate;
|
|
123
|
+
state.state = 'candidate';
|
|
124
|
+
return { ok: true, value: candidate };
|
|
125
|
+
},
|
|
126
|
+
promote(target, generation) {
|
|
127
|
+
const found = requireTarget(target, 'resize');
|
|
128
|
+
if (!found.ok) return found;
|
|
129
|
+
const state = found.value;
|
|
130
|
+
if (state.candidate?.generation !== generation) {
|
|
131
|
+
return invalid('resize', 'generation-mismatch', state.state, state.generation);
|
|
132
|
+
}
|
|
133
|
+
state.descriptor = state.candidate.descriptor;
|
|
134
|
+
state.generation = generation;
|
|
135
|
+
state.candidate = undefined;
|
|
136
|
+
state.state = 'active';
|
|
137
|
+
return { ok: true, value: undefined };
|
|
138
|
+
},
|
|
139
|
+
rejectCandidate(target, generation, stage) {
|
|
140
|
+
const found = requireTarget(target, 'resize');
|
|
141
|
+
if (!found.ok) return found;
|
|
142
|
+
const state = found.value;
|
|
143
|
+
if (state.candidate?.generation !== generation) {
|
|
144
|
+
return invalid('resize', 'generation-mismatch', state.state, state.generation);
|
|
145
|
+
}
|
|
146
|
+
state.candidate = undefined;
|
|
147
|
+
state.state = state.generation === 0 ? 'uninitialized' : 'active';
|
|
148
|
+
return {
|
|
149
|
+
ok: false,
|
|
150
|
+
error: new RenderTargetOperationFailedError({
|
|
151
|
+
operation: 'resize',
|
|
152
|
+
stage,
|
|
153
|
+
generation,
|
|
154
|
+
cause: new Error(`candidate ${generation} failed during ${stage}`),
|
|
155
|
+
recovery: state.generation === 0 ? 'retry' : 'retain-last-known-good',
|
|
156
|
+
}),
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
beginRecovery(target, generation) {
|
|
160
|
+
const found = requireTarget(target, 'resize');
|
|
161
|
+
if (!found.ok) return found;
|
|
162
|
+
const state = found.value;
|
|
163
|
+
if (state.state !== 'active' || generation <= state.generation) {
|
|
164
|
+
return invalid('resize', 'generation-mismatch', state.state, state.generation);
|
|
165
|
+
}
|
|
166
|
+
state.generation = generation;
|
|
167
|
+
state.candidate = undefined;
|
|
168
|
+
state.state = 'rebuilding';
|
|
169
|
+
return { ok: true, value: undefined };
|
|
170
|
+
},
|
|
171
|
+
finishRecovery(target) {
|
|
172
|
+
const found = requireTarget(target, 'resize');
|
|
173
|
+
if (!found.ok) return found;
|
|
174
|
+
const state = found.value;
|
|
175
|
+
if (state.state !== 'rebuilding') {
|
|
176
|
+
return invalid('resize', 'generation-mismatch', state.state, state.generation);
|
|
177
|
+
}
|
|
178
|
+
state.state = 'uninitialized';
|
|
179
|
+
return { ok: true, value: undefined };
|
|
180
|
+
},
|
|
181
|
+
retire(target, generation) {
|
|
182
|
+
const found = requireTarget(target, 'destroy');
|
|
183
|
+
if (!found.ok) return found;
|
|
184
|
+
found.value.retiredGenerations.add(generation);
|
|
185
|
+
return { ok: true, value: undefined };
|
|
186
|
+
},
|
|
187
|
+
destroy(target) {
|
|
188
|
+
const state = targets.get(target as object);
|
|
189
|
+
if (state === undefined) return invalid('destroy', 'foreign-renderer', 'destroyed', 0);
|
|
190
|
+
if (state.rendererId !== options.rendererId) {
|
|
191
|
+
return invalid('destroy', 'foreign-renderer', state.state, state.generation);
|
|
192
|
+
}
|
|
193
|
+
if (state.state === 'destroyed') return { ok: true, value: undefined };
|
|
194
|
+
state.candidate = undefined;
|
|
195
|
+
state.state = 'destroyed';
|
|
196
|
+
return { ok: true, value: undefined };
|
|
197
|
+
},
|
|
198
|
+
inspect(target) {
|
|
199
|
+
const found = requireTarget(target, 'inspect');
|
|
200
|
+
if (!found.ok) return found;
|
|
201
|
+
const state = found.value;
|
|
202
|
+
return {
|
|
203
|
+
ok: true,
|
|
204
|
+
value: Object.freeze({
|
|
205
|
+
token: state.target,
|
|
206
|
+
state: state.state,
|
|
207
|
+
generation: state.generation,
|
|
208
|
+
descriptor: state.descriptor,
|
|
209
|
+
...(state.candidate === undefined ? {} : { candidate: state.candidate }),
|
|
210
|
+
}),
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|