@forgeax/engine-render 0.1.20 → 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 +21 -18
- 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-4XINDLAY.mjs → chunk-5YADUCA7.mjs} +1552 -1361
- package/dist/chunk-5YADUCA7.mjs.map +1 -0
- package/dist/{chunk-TBQJOK2L.mjs → chunk-6OPXDQ2E.mjs} +364 -51
- package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
- package/dist/{chunk-M2MP4ZOU.mjs → chunk-BXBQQKFQ.mjs} +3 -9
- package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
- package/dist/{chunk-CDTWTHZR.mjs → chunk-FS2KNUQH.mjs} +548 -6
- 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/construct-renderer.mjs +5826 -1003
- package/dist/construct-renderer.mjs.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 +141 -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/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 +28 -3
- 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 +10 -1
- 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 +14 -4
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts +2 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
- package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
- package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
- package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
- package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
- package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
- package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts +4 -13
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +5 -26
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-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 +27 -32
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +28 -14
- 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 +10 -4
- 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 +8 -3
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +22 -1
- 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 +43 -2
- 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 +72 -4
- 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 +49 -7
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +1 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +42 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +58 -17
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +17 -2
- package/dist/render-system.d.ts.map +1 -1
- package/dist/renderer.d.ts +43 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/scene/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/index.mjs +3 -3
- package/dist/typed-render-graph-primitives.d.ts +1 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +1 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +2 -2
- package/dist/volume/passes.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
- package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
- package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
- package/src/__tests__/cluster-binner.unit.test.ts +35 -0
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +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__/material-observation.integration.test.ts +101 -0
- package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
- package/src/__tests__/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 +18 -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 +48 -0
- package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
- package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
- package/src/__tests__/skin-motion-regression.unit.test.ts +132 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +11 -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 -38
- 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/assembly/device-feature-admission.ts +73 -0
- package/src/assembly/directional-shadow-inspection.ts +122 -0
- package/src/assembly/factory.ts +877 -286
- 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 +81 -7
- package/src/components/reflection-probe.ts +49 -0
- package/src/environment/frame.ts +6 -24
- package/src/environment/lifecycle.ts +12 -0
- package/src/errors/render.ts +286 -142
- 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/hdrp-buffers.ts +165 -59
- package/src/ibl/IblPipelineCache.ts +11 -0
- package/src/ibl/kernel-cache.ts +88 -0
- package/src/index.ts +148 -0
- package/src/inspection-types.ts +56 -0
- package/src/instances-derived-bounds.ts +171 -0
- package/src/light-buffer-layout.ts +20 -5
- package/src/mesh-material-bindings.ts +132 -0
- package/src/pbr-pipeline.ts +106 -17
- package/src/pipeline/standard-forward-lane.ts +46 -16
- 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 +122 -193
- package/src/pipeline/standard-post.ts +26 -7
- package/src/pipeline/standard-profile.ts +17 -47
- 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 +367 -427
- package/src/record/frame-snapshot.ts +35 -27
- package/src/record/frame-targets.ts +17 -0
- package/src/record/frame.ts +261 -74
- 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 +21 -12
- package/src/record/main-pass-geometry.ts +120 -80
- package/src/record/main-pass-material.ts +95 -9
- package/src/record/main-pass-sprite-draws.ts +54 -25
- package/src/record/main-pass.ts +71 -15
- package/src/record/render-context.ts +72 -0
- package/src/record/shadow-pass.ts +17 -28
- package/src/record/skybox-post-pass.ts +20 -4
- package/src/record/typed-frame-graph.ts +893 -44
- 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 +76 -5
- package/src/render-graph-primitives.ts +9 -4
- package/src/render-pipeline.ts +117 -2
- package/src/render-system-extract.ts +557 -100
- package/src/render-system.ts +1095 -66
- package/src/renderer.ts +222 -0
- package/src/scene/render-scene.ts +115 -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/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
- package/src/typed-render-graph-primitives.ts +5 -3
- package/src/typed-shadow-passes.ts +46 -40
- package/src/volume/passes.ts +15 -9
- package/dist/chunk-4XINDLAY.mjs.map +0 -1
- package/dist/chunk-CDTWTHZR.mjs.map +0 -1
- package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
- package/dist/chunk-TBQJOK2L.mjs.map +0 -1
|
@@ -17,12 +17,144 @@ export interface ResolvedMeshMaterialBinding {
|
|
|
17
17
|
readonly source: MeshMaterialBindingSource;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/** Lifecycle values projected from an already prepared resident binding. */
|
|
21
|
+
export type MeshMaterialBindingReadiness = 'ready' | 'pending' | 'failed' | 'last-known-good';
|
|
22
|
+
|
|
23
|
+
/** Structured producer failure retained by the renderer's bounded projection. */
|
|
24
|
+
export interface MeshMaterialBindingPreparationFailure {
|
|
25
|
+
readonly code: string;
|
|
26
|
+
readonly expected: string;
|
|
27
|
+
readonly hint: string;
|
|
28
|
+
readonly detail?: Readonly<Record<string, unknown>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface MeshMaterialBindingSamplerObservation {
|
|
32
|
+
readonly handle: number;
|
|
33
|
+
readonly resident: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface MeshMaterialBindingTextureObservation {
|
|
37
|
+
readonly handle: number;
|
|
38
|
+
readonly mipLevelCount: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One material-slot's bounded resident projection. The renderer reports only
|
|
43
|
+
* resources it actually resolved for the slot; Pack/AssetRegistry lifecycle
|
|
44
|
+
* records stay owned by the producer and are never copied here.
|
|
45
|
+
*/
|
|
46
|
+
export interface MeshMaterialBindingResidency {
|
|
47
|
+
readonly readiness: MeshMaterialBindingReadiness;
|
|
48
|
+
readonly samplers: readonly MeshMaterialBindingSamplerObservation[];
|
|
49
|
+
readonly textures: readonly MeshMaterialBindingTextureObservation[];
|
|
50
|
+
readonly preparationFailure?: MeshMaterialBindingPreparationFailure;
|
|
51
|
+
}
|
|
52
|
+
|
|
20
53
|
/** Public read-only observation of the bindings used by the last frame. */
|
|
21
54
|
export interface MeshMaterialBindingObservation {
|
|
22
55
|
readonly worldId: number;
|
|
23
56
|
readonly entityKey: number;
|
|
24
57
|
readonly bindings: readonly ResolvedMeshMaterialBinding[];
|
|
25
58
|
readonly diagnostics: readonly MeshMaterialBindingDiagnostic[];
|
|
59
|
+
/** One resident projection per resolved material slot. */
|
|
60
|
+
readonly residency: readonly MeshMaterialBindingResidency[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface MeshMaterialBindingSummary {
|
|
64
|
+
readonly total: number;
|
|
65
|
+
readonly ready: number;
|
|
66
|
+
readonly pending: number;
|
|
67
|
+
readonly failed: number;
|
|
68
|
+
readonly lastKnownGood: number;
|
|
69
|
+
readonly textureCount: number;
|
|
70
|
+
readonly samplerCount: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const EMPTY_RESIDENCY: MeshMaterialBindingResidency = Object.freeze({
|
|
74
|
+
readiness: 'pending',
|
|
75
|
+
samplers: Object.freeze([]),
|
|
76
|
+
textures: Object.freeze([]),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build the sole renderer observation projection from the resident seam.
|
|
81
|
+
* Missing facts remain `pending`; they are never guessed from a URL, asset
|
|
82
|
+
* name, or authoring field. Slot cardinality is normalized so aggregation is
|
|
83
|
+
* deterministic even while a preparation candidate is incomplete.
|
|
84
|
+
*/
|
|
85
|
+
export function projectMeshMaterialBindingObservation(input: {
|
|
86
|
+
readonly worldId: number;
|
|
87
|
+
readonly entityKey: number;
|
|
88
|
+
readonly bindings: readonly ResolvedMeshMaterialBinding[];
|
|
89
|
+
readonly diagnostics: readonly MeshMaterialBindingDiagnostic[];
|
|
90
|
+
readonly residency?: readonly MeshMaterialBindingResidency[];
|
|
91
|
+
}): MeshMaterialBindingObservation {
|
|
92
|
+
const residency = input.bindings.map((_, index) => {
|
|
93
|
+
const value = input.residency?.[index] ?? EMPTY_RESIDENCY;
|
|
94
|
+
return Object.freeze({
|
|
95
|
+
...value,
|
|
96
|
+
samplers: Object.freeze(value.samplers.map((sampler) => Object.freeze({ ...sampler }))),
|
|
97
|
+
textures: Object.freeze(value.textures.map((texture) => Object.freeze({ ...texture }))),
|
|
98
|
+
...(value.preparationFailure === undefined
|
|
99
|
+
? {}
|
|
100
|
+
: {
|
|
101
|
+
preparationFailure: Object.freeze({
|
|
102
|
+
...value.preparationFailure,
|
|
103
|
+
...(value.preparationFailure.detail === undefined
|
|
104
|
+
? {}
|
|
105
|
+
: { detail: Object.freeze({ ...value.preparationFailure.detail }) }),
|
|
106
|
+
}),
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
worldId: input.worldId,
|
|
112
|
+
entityKey: input.entityKey,
|
|
113
|
+
bindings: Object.freeze([...input.bindings]),
|
|
114
|
+
diagnostics: Object.freeze([...input.diagnostics]),
|
|
115
|
+
residency: Object.freeze(residency),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Recompute bounded readiness/resource counts from retained observations. */
|
|
120
|
+
export function summarizeMeshMaterialBindings(
|
|
121
|
+
observations: readonly MeshMaterialBindingObservation[],
|
|
122
|
+
): MeshMaterialBindingSummary {
|
|
123
|
+
let ready = 0;
|
|
124
|
+
let pending = 0;
|
|
125
|
+
let failed = 0;
|
|
126
|
+
let lastKnownGood = 0;
|
|
127
|
+
let textureCount = 0;
|
|
128
|
+
let samplerCount = 0;
|
|
129
|
+
for (const observation of observations) {
|
|
130
|
+
for (const residency of observation.residency) {
|
|
131
|
+
switch (residency.readiness) {
|
|
132
|
+
case 'ready':
|
|
133
|
+
ready += 1;
|
|
134
|
+
break;
|
|
135
|
+
case 'pending':
|
|
136
|
+
pending += 1;
|
|
137
|
+
break;
|
|
138
|
+
case 'failed':
|
|
139
|
+
failed += 1;
|
|
140
|
+
break;
|
|
141
|
+
case 'last-known-good':
|
|
142
|
+
lastKnownGood += 1;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
textureCount += residency.textures.length;
|
|
146
|
+
samplerCount += residency.samplers.length;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
total: ready + pending + failed + lastKnownGood,
|
|
151
|
+
ready,
|
|
152
|
+
pending,
|
|
153
|
+
failed,
|
|
154
|
+
lastKnownGood,
|
|
155
|
+
textureCount,
|
|
156
|
+
samplerCount,
|
|
157
|
+
};
|
|
26
158
|
}
|
|
27
159
|
|
|
28
160
|
export type ResolveMeshMaterialBindingsResult =
|
package/src/pbr-pipeline.ts
CHANGED
|
@@ -382,7 +382,9 @@ export function buildPbrViewBglEntries(caps: PbrCaps): GPUBindGroupLayoutEntry[]
|
|
|
382
382
|
{
|
|
383
383
|
binding: 0,
|
|
384
384
|
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
385
|
-
|
|
385
|
+
// View captures use the same bind group with a per-face view slot.
|
|
386
|
+
// Keeping this dynamic is also valid for the display slot at offset 0.
|
|
387
|
+
buffer: { type: 'uniform', hasDynamicOffset: true },
|
|
386
388
|
},
|
|
387
389
|
{
|
|
388
390
|
binding: 1,
|
|
@@ -600,6 +602,22 @@ export function isSkinnedShadowCasterVariant(
|
|
|
600
602
|
);
|
|
601
603
|
}
|
|
602
604
|
|
|
605
|
+
export type PipelineGroup2Contract = 'mesh' | 'skin' | 'cluster' | 'skin-cluster';
|
|
606
|
+
|
|
607
|
+
/** Resolve group(2) from the composed WGSL artifact used to build the PSO. */
|
|
608
|
+
export function resolvePipelineGroup2Contract(source: string): PipelineGroup2Contract {
|
|
609
|
+
const withoutComments = source.replace(/\/\*[\s\S]*?\*\//gu, '').replace(/\/\/.*$/gmu, '');
|
|
610
|
+
const bindings = [
|
|
611
|
+
...withoutComments.matchAll(/@group\s*\(\s*2\s*\)\s*@binding\s*\(\s*(\d+)\s*\)/gu),
|
|
612
|
+
].map((match) => Number(match[1]));
|
|
613
|
+
const hasSkinBindings = bindings.includes(1) || bindings.includes(2);
|
|
614
|
+
const hasClusterBindings = bindings.some((binding) => binding >= 3);
|
|
615
|
+
if (hasSkinBindings && hasClusterBindings) return 'skin-cluster';
|
|
616
|
+
if (hasSkinBindings) return 'skin';
|
|
617
|
+
if (hasClusterBindings) return 'cluster';
|
|
618
|
+
return 'mesh';
|
|
619
|
+
}
|
|
620
|
+
|
|
603
621
|
/** Returns true for the engine-shipped standard-PBR material shader family. */
|
|
604
622
|
export function isStandardPbrMaterialShader(shaderId: string | undefined): boolean {
|
|
605
623
|
return (
|
|
@@ -631,9 +649,10 @@ export function isStandardPbrMaterialShader(shaderId: string | undefined): boole
|
|
|
631
649
|
|
|
632
650
|
/**
|
|
633
651
|
* Build the PBR skin pipeline layout (4 slots `[view, material,
|
|
634
|
-
* mesh-array(
|
|
652
|
+
* mesh-array(3-entry), instances]`). View / material / instances BGLs are
|
|
635
653
|
* shared with the standard-PBR layout (passed in via `pbr` bundle); the only
|
|
636
|
-
* new BGL is the
|
|
654
|
+
* new BGL is the 3-entry mesh-array slot for `meshes` + current/previous
|
|
655
|
+
* `palette` buffers.
|
|
637
656
|
*
|
|
638
657
|
* Throws on each `createBindGroupLayout` / `createPipelineLayout` Result
|
|
639
658
|
* failure -- the engine bootstrap path wraps the call in `runShimSyncStep`
|
|
@@ -644,8 +663,9 @@ export function buildPbrSkinLayouts(
|
|
|
644
663
|
caps: PbrCaps,
|
|
645
664
|
pbr: PbrPipelineLayoutBundle,
|
|
646
665
|
): PbrPipelineLayoutBundle {
|
|
647
|
-
//
|
|
648
|
-
//
|
|
666
|
+
// 3-entry mesh-array BGL: binding 0 meshes + binding 1 current palette
|
|
667
|
+
// + binding 2 previous palette. All use dynamic offsets for the same
|
|
668
|
+
// per-entity window contract.
|
|
649
669
|
const skinMeshArrayBglRes = device.createBindGroupLayout(
|
|
650
670
|
buildBindGroupLayoutDescriptor(BGL_ONLY_SPEC_STUB, {
|
|
651
671
|
kind: 'pbr-skin-mesh-array',
|
|
@@ -676,6 +696,40 @@ export function buildPbrSkinLayouts(
|
|
|
676
696
|
};
|
|
677
697
|
}
|
|
678
698
|
|
|
699
|
+
/** Project the canonical group(2) resources for a PBR skin draw. */
|
|
700
|
+
export function createPbrSkinMeshBindGroupEntries(
|
|
701
|
+
meshBuffer: Buffer,
|
|
702
|
+
meshSize: number,
|
|
703
|
+
paletteBuffer: Buffer,
|
|
704
|
+
paletteWindowBytes: number,
|
|
705
|
+
): BindGroupEntry[] {
|
|
706
|
+
return [
|
|
707
|
+
{
|
|
708
|
+
binding: 0,
|
|
709
|
+
resource: { kind: 'buffer', value: { buffer: meshBuffer, offset: 0, size: meshSize } },
|
|
710
|
+
},
|
|
711
|
+
...[1, 2].map((binding) => ({
|
|
712
|
+
binding,
|
|
713
|
+
resource: {
|
|
714
|
+
kind: 'buffer' as const,
|
|
715
|
+
value: {
|
|
716
|
+
buffer: paletteBuffer,
|
|
717
|
+
offset: 0,
|
|
718
|
+
size: paletteWindowBytes,
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
})),
|
|
722
|
+
];
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/** Project all dynamic offsets for the canonical PBR skin group(2) shape. */
|
|
726
|
+
export function pbrSkinMeshDynamicOffsets(
|
|
727
|
+
meshOffset: number,
|
|
728
|
+
paletteOffset: number,
|
|
729
|
+
): readonly [number, number, number] {
|
|
730
|
+
return [meshOffset, paletteOffset, paletteOffset];
|
|
731
|
+
}
|
|
732
|
+
|
|
679
733
|
// ─── Unlit material BGL factory ─────────────────────────────────────────────
|
|
680
734
|
|
|
681
735
|
/**
|
|
@@ -763,7 +817,7 @@ export type { BindGroup };
|
|
|
763
817
|
// existing behaviour (plan-strategy D-4 "pbr / unlit / sprite-atlas
|
|
764
818
|
// behaviour unchanged" / requirements Edge Cases).
|
|
765
819
|
//
|
|
766
|
-
// Variant set string format mirrors the
|
|
820
|
+
// Variant set string format mirrors the Standard boot variant /
|
|
767
821
|
// HDRP variant set string idiom in `pipeline-spec.ts` (`+`-separated kv
|
|
768
822
|
// pairs); the record stage threads this string through the per-shader
|
|
769
823
|
// pipeline-cache key + lazy-build `getMaterialShaderPipeline` lookup.
|
|
@@ -812,7 +866,7 @@ export const SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET = '';
|
|
|
812
866
|
* spot lightViewProj matrices moved into the View UBO)
|
|
813
867
|
* - `'pbr-mesh-array'` — 1 entry: per-entity mesh SSBO (dynamic-offset)
|
|
814
868
|
* - `'pbr-instances'` — 1 entry: per-instance SSBO (no dynamic-offset)
|
|
815
|
-
* - `'pbr-skin-mesh-array'` —
|
|
869
|
+
* - `'pbr-skin-mesh-array'` — 3 entries: meshes + current/previous palette
|
|
816
870
|
* 3. Attachment-driven (fullscreen post-process):
|
|
817
871
|
* - `'fullscreen-post'` — 2 entries: input texture + sampler. The texture
|
|
818
872
|
* `sampleType` is derived from `spec.attachments` (plan §R3 fix):
|
|
@@ -855,17 +909,20 @@ export interface BindGroupLayoutDescriptorOutput {
|
|
|
855
909
|
}
|
|
856
910
|
|
|
857
911
|
/** Build the canonical HDRP group(2) bind-group layout. */
|
|
858
|
-
export function createHdrpBindGroupLayoutDescriptor(
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const meshBufType: GPUBufferBindingType = storageBuffer ? 'read-only-storage' : 'uniform';
|
|
862
|
-
const clusterBufType: GPUBufferBindingType = storageBuffer ? 'read-only-storage' : 'uniform';
|
|
912
|
+
export function createHdrpBindGroupLayoutDescriptor(): BindGroupLayoutDescriptor {
|
|
913
|
+
const meshBufType: GPUBufferBindingType = 'read-only-storage';
|
|
914
|
+
const clusterBufType: GPUBufferBindingType = 'read-only-storage';
|
|
863
915
|
return {
|
|
864
916
|
label: 'hdrp-unified-bgl-group2',
|
|
865
917
|
entries: [
|
|
866
918
|
{
|
|
867
919
|
binding: 0,
|
|
868
|
-
|
|
920
|
+
// Standard fragment lighting (transmission/refraction and clustered
|
|
921
|
+
// evaluation) reads the same mesh transform window as the vertex
|
|
922
|
+
// stage. Keep this visibility in the HDRP descriptor aligned with
|
|
923
|
+
// the URP mesh-array descriptor; restricting it to vertex makes the
|
|
924
|
+
// HDRP pipeline invalid when fs_main accesses `meshes[0]`.
|
|
925
|
+
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
869
926
|
buffer: { type: meshBufType, hasDynamicOffset: true },
|
|
870
927
|
},
|
|
871
928
|
// Bindings 1 and 2 stay absent for the URP physical isolation gap.
|
|
@@ -903,6 +960,33 @@ export function createHdrpBindGroupLayoutDescriptor(
|
|
|
903
960
|
};
|
|
904
961
|
}
|
|
905
962
|
|
|
963
|
+
/** Build the HDRP group(2) layout for the clustered skin variant. */
|
|
964
|
+
export function createHdrpSkinBindGroupLayoutDescriptor(): BindGroupLayoutDescriptor {
|
|
965
|
+
const base = createHdrpBindGroupLayoutDescriptor();
|
|
966
|
+
const baseEntries = base.entries;
|
|
967
|
+
if (baseEntries === undefined || baseEntries[0] === undefined) {
|
|
968
|
+
throw new Error('HDRP base group(2) layout must declare mesh binding 0');
|
|
969
|
+
}
|
|
970
|
+
const paletteType: GPUBufferBindingType = 'read-only-storage';
|
|
971
|
+
return {
|
|
972
|
+
label: 'hdrp-skin-unified-bgl-group2',
|
|
973
|
+
entries: [
|
|
974
|
+
baseEntries[0],
|
|
975
|
+
{
|
|
976
|
+
binding: 1,
|
|
977
|
+
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
978
|
+
buffer: { type: paletteType, hasDynamicOffset: true },
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
binding: 2,
|
|
982
|
+
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
983
|
+
buffer: { type: paletteType, hasDynamicOffset: true },
|
|
984
|
+
},
|
|
985
|
+
...baseEntries.slice(1),
|
|
986
|
+
],
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
|
|
906
990
|
/**
|
|
907
991
|
* Build a `GPUBindGroupLayoutDescriptor` from a PipelineSpec, dispatching on
|
|
908
992
|
* `options.kind` to one of 9 closed BGL shapes (D-13 round-2).
|
|
@@ -1037,6 +1121,11 @@ export function buildBindGroupLayoutDescriptor(
|
|
|
1037
1121
|
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
1038
1122
|
buffer: { type: meshBufType, hasDynamicOffset: true },
|
|
1039
1123
|
},
|
|
1124
|
+
{
|
|
1125
|
+
binding: 2,
|
|
1126
|
+
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
1127
|
+
buffer: { type: meshBufType, hasDynamicOffset: true },
|
|
1128
|
+
},
|
|
1040
1129
|
],
|
|
1041
1130
|
};
|
|
1042
1131
|
}
|
|
@@ -1068,10 +1157,10 @@ export function buildBindGroupLayoutDescriptor(
|
|
|
1068
1157
|
};
|
|
1069
1158
|
}
|
|
1070
1159
|
case 'hdrp-7-slot': {
|
|
1071
|
-
//
|
|
1072
|
-
//
|
|
1073
|
-
|
|
1074
|
-
const desc = createHdrpBindGroupLayoutDescriptor(
|
|
1160
|
+
// Standard unified BGL for group(2): 9 entries (binding 0 + 3..8).
|
|
1161
|
+
// Cluster variants are admitted only when storage buffers are present;
|
|
1162
|
+
// there is no uniform-backed cluster shape.
|
|
1163
|
+
const desc = createHdrpBindGroupLayoutDescriptor();
|
|
1075
1164
|
return {
|
|
1076
1165
|
label: desc.label ?? 'hdrp-unified-bgl-group2',
|
|
1077
1166
|
entries: [...(desc.entries ?? [])],
|
|
@@ -35,6 +35,12 @@ import {
|
|
|
35
35
|
} from '../typed-render-graph-primitives';
|
|
36
36
|
import { addTypedShadowPasses } from '../typed-shadow-passes';
|
|
37
37
|
import { addAuthoredVolumetricFogPasses } from '../volume/passes';
|
|
38
|
+
import {
|
|
39
|
+
addStandardClusterMembershipPass,
|
|
40
|
+
importStandardClusterBuffers,
|
|
41
|
+
standardClusterReadAccesses,
|
|
42
|
+
} from './standard-lighting/graph';
|
|
43
|
+
import type { StandardTopologyInputValue } from './standard-lighting/topology';
|
|
38
44
|
import { addStandardPost } from './standard-post';
|
|
39
45
|
|
|
40
46
|
function target(
|
|
@@ -48,6 +54,7 @@ function target(
|
|
|
48
54
|
function buildUrp(
|
|
49
55
|
context: RenderPipelineBuildContext<RenderPipelineFrame>,
|
|
50
56
|
topology: RenderPipelineTopology,
|
|
57
|
+
lighting: StandardTopologyInputValue,
|
|
51
58
|
): ReturnType<RenderPipeline['build']> {
|
|
52
59
|
const graph = context.graph;
|
|
53
60
|
const surface = importRenderPipelineSurface(graph, topology);
|
|
@@ -60,16 +67,14 @@ function buildUrp(
|
|
|
60
67
|
const fxaa = !clearOnly && topology.camera.antialias === 'fxaa';
|
|
61
68
|
const taa = !clearOnly && topology.camera.antialias === 'taa';
|
|
62
69
|
const rawOnly = !clearOnly && topology.surface.profile?.kind === 'raw-only';
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
// remain float even when tone mapping is disabled. This prevents an
|
|
67
|
-
// eight-bit quantization before the OETF/display-encoded boundary.
|
|
70
|
+
// Every non-clear lane keeps a linear float scene target until the shared
|
|
71
|
+
// Output Transform. This includes no-AA and the no-storage fallback: the
|
|
72
|
+
// final dither and display encoding must have one graph-owned boundary.
|
|
68
73
|
// MSAA resolves into a separate linear target before presentation. Treat
|
|
69
74
|
// that resolved target like linear LDR so the shared Output Transform owns
|
|
70
75
|
// its write into the surface storage view; without this, the resolve is
|
|
71
76
|
// never presented and MSAA is indistinguishable from the no-AA path.
|
|
72
|
-
const linearLdr = !clearOnly &&
|
|
77
|
+
const linearLdr = !clearOnly && !hdr;
|
|
73
78
|
const temporalDemand = aggregateTemporalDemand({
|
|
74
79
|
taa,
|
|
75
80
|
motionBlur: !clearOnly && topology.temporal?.motionBlur === true,
|
|
@@ -88,7 +93,14 @@ function buildUrp(
|
|
|
88
93
|
if (!depth.ok) return depth;
|
|
89
94
|
|
|
90
95
|
const sceneResolved = clearOnly
|
|
91
|
-
?
|
|
96
|
+
? surface.value.display === undefined
|
|
97
|
+
? target(graph, 'scene-color', {
|
|
98
|
+
format: topology.surface.storageFormat,
|
|
99
|
+
size: 'surface',
|
|
100
|
+
sampleCount: 1,
|
|
101
|
+
domain: 'display-encoded',
|
|
102
|
+
})
|
|
103
|
+
: ok(surface.value.display)
|
|
92
104
|
: hdr || fxaa || linearLdr || msaa || rawOnly || needsTemporalIntermediate
|
|
93
105
|
? target(graph, 'scene-color', {
|
|
94
106
|
format: sceneFormat,
|
|
@@ -126,6 +138,16 @@ function buildUrp(
|
|
|
126
138
|
const shadows = addTypedShadowPasses(graph, topology);
|
|
127
139
|
if (!shadows.ok) return shadows;
|
|
128
140
|
|
|
141
|
+
const clusterBuffers = clearOnly ? ok(null) : importStandardClusterBuffers(graph, lighting);
|
|
142
|
+
if (!clusterBuffers.ok) return clusterBuffers;
|
|
143
|
+
const clusterMembership =
|
|
144
|
+
clusterBuffers.value === null
|
|
145
|
+
? ok(undefined)
|
|
146
|
+
: addStandardClusterMembershipPass(graph, lighting, clusterBuffers.value);
|
|
147
|
+
if (!clusterMembership.ok) return clusterMembership;
|
|
148
|
+
const clusterReads =
|
|
149
|
+
clusterBuffers.value === null ? [] : standardClusterReadAccesses(clusterBuffers.value);
|
|
150
|
+
|
|
129
151
|
const skybox = addTypedSkyboxPass(graph, scene.value);
|
|
130
152
|
if (!skybox.ok) return skybox;
|
|
131
153
|
const gpuDriven = context.projectGpuDriven({
|
|
@@ -144,22 +166,23 @@ function buildUrp(
|
|
|
144
166
|
depth: depth.value,
|
|
145
167
|
...(msaa ? { resolve: sceneResolved.value } : {}),
|
|
146
168
|
sampled: [
|
|
147
|
-
shadows.value.directional,
|
|
169
|
+
...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
|
|
148
170
|
shadows.value.spot,
|
|
149
171
|
...(shadows.value.point === undefined ? [] : [shadows.value.point]),
|
|
150
172
|
],
|
|
151
|
-
|
|
173
|
+
...(shadows.value.directional === undefined
|
|
174
|
+
? {}
|
|
175
|
+
: { directionalShadow: shadows.value.directional }),
|
|
152
176
|
spotShadow: shadows.value.spot,
|
|
153
177
|
selector: { LightMode: ['Forward'] },
|
|
154
178
|
colorLoadOp: 'load',
|
|
179
|
+
extraAccesses: clusterReads,
|
|
155
180
|
...(transmissionActive ? { recordMode: 'opaque' as const } : {}),
|
|
156
181
|
...(gpuDriven.value === undefined ? {} : { gpuDriven: gpuDriven.value }),
|
|
157
182
|
});
|
|
158
183
|
if (!main.ok) return main;
|
|
159
184
|
|
|
160
|
-
const temporalLane = topology.lane.compute
|
|
161
|
-
? (topology.standardProfile?.lighting ?? 'direct')
|
|
162
|
-
: 'cpu-webgl2';
|
|
185
|
+
const temporalLane = topology.lane.compute ? 'clustered' : 'cpu-webgl2';
|
|
163
186
|
const temporalAdmission = standardTemporalLaneAdmission({
|
|
164
187
|
lane: temporalLane,
|
|
165
188
|
demand: temporalDemand,
|
|
@@ -191,6 +214,7 @@ function buildUrp(
|
|
|
191
214
|
const producer = addStandardSceneDataPass(graph, temporal.value.temporal, depth.value);
|
|
192
215
|
if (!producer.ok) return producer;
|
|
193
216
|
}
|
|
217
|
+
|
|
194
218
|
const transmission = addTransmissionBackdropPasses({
|
|
195
219
|
graph,
|
|
196
220
|
source: sceneResolved.value,
|
|
@@ -207,7 +231,7 @@ function buildUrp(
|
|
|
207
231
|
depth: depth.value,
|
|
208
232
|
...(msaa ? { resolve: sceneResolved.value } : {}),
|
|
209
233
|
sampled: [
|
|
210
|
-
shadows.value.directional,
|
|
234
|
+
...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
|
|
211
235
|
shadows.value.spot,
|
|
212
236
|
...(shadows.value.point === undefined ? [] : [shadows.value.point]),
|
|
213
237
|
],
|
|
@@ -217,6 +241,7 @@ function buildUrp(
|
|
|
217
241
|
colorLoadOp: 'load',
|
|
218
242
|
depthLoadOp: 'load',
|
|
219
243
|
recordMode: 'transmission',
|
|
244
|
+
extraAccesses: clusterReads,
|
|
220
245
|
// Inject the complete backdrop view so the Standard shader can select
|
|
221
246
|
// the producer's optional roughness mip level. mipViews[0] is only the
|
|
222
247
|
// copy destination view and intentionally exposes one level.
|
|
@@ -230,7 +255,7 @@ function buildUrp(
|
|
|
230
255
|
depth: depth.value,
|
|
231
256
|
...(msaa ? { resolve: sceneResolved.value } : {}),
|
|
232
257
|
sampled: [
|
|
233
|
-
shadows.value.directional,
|
|
258
|
+
...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
|
|
234
259
|
shadows.value.spot,
|
|
235
260
|
...(shadows.value.point === undefined ? [] : [shadows.value.point]),
|
|
236
261
|
],
|
|
@@ -240,6 +265,7 @@ function buildUrp(
|
|
|
240
265
|
colorLoadOp: 'load',
|
|
241
266
|
depthLoadOp: 'load',
|
|
242
267
|
recordMode: 'transparent',
|
|
268
|
+
extraAccesses: clusterReads,
|
|
243
269
|
});
|
|
244
270
|
if (!transparentPass.ok) return transparentPass;
|
|
245
271
|
const temporal = addTransmissionBackdropTemporalPass(graph, sceneResolved.value);
|
|
@@ -258,7 +284,7 @@ function buildUrp(
|
|
|
258
284
|
topology,
|
|
259
285
|
sceneResolved.value,
|
|
260
286
|
depthSample.value,
|
|
261
|
-
shadows.value.directional
|
|
287
|
+
shadows.value.directional?.view,
|
|
262
288
|
shadows.value.spot.view,
|
|
263
289
|
);
|
|
264
290
|
if (!volume.ok) return volume;
|
|
@@ -293,7 +319,7 @@ function buildUrp(
|
|
|
293
319
|
if (!observation.ok) return observation;
|
|
294
320
|
}
|
|
295
321
|
|
|
296
|
-
|
|
322
|
+
const post = addStandardPost(
|
|
297
323
|
context,
|
|
298
324
|
topology,
|
|
299
325
|
sceneResolved.value,
|
|
@@ -301,6 +327,10 @@ function buildUrp(
|
|
|
301
327
|
surface.value,
|
|
302
328
|
temporal.value?.temporal,
|
|
303
329
|
);
|
|
330
|
+
if (!post.ok) return post;
|
|
331
|
+
const cubeCaptures = context.contributeCubeCaptures?.();
|
|
332
|
+
if (cubeCaptures !== undefined && !cubeCaptures.ok) return cubeCaptures;
|
|
333
|
+
return ok(undefined);
|
|
304
334
|
}
|
|
305
335
|
|
|
306
336
|
export const buildStandardForwardLane = buildUrp;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ClusterBinError } from '../../cluster-binner';
|
|
2
|
+
import { StandardClusterIndexOverflowError } from '../../errors/render';
|
|
3
|
+
|
|
4
|
+
export function fromClusterBinError(error: ClusterBinError): StandardClusterIndexOverflowError {
|
|
5
|
+
return new StandardClusterIndexOverflowError(error.detail.actual, error.detail.capacity);
|
|
6
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GraphAccess,
|
|
3
|
+
GraphBuffer,
|
|
4
|
+
RenderGraphBuilder,
|
|
5
|
+
RenderGraphError,
|
|
6
|
+
} from '@forgeax/engine-render-graph';
|
|
7
|
+
import { ok, type Result } from '@forgeax/engine-types';
|
|
8
|
+
import {
|
|
9
|
+
GPU_BUFFER_USAGE_COPY_DST,
|
|
10
|
+
GPU_BUFFER_USAGE_STORAGE,
|
|
11
|
+
GPU_BUFFER_USAGE_UNIFORM,
|
|
12
|
+
} from '../../gpu-usage';
|
|
13
|
+
import { getOrCreateHdrpBuffers, type HdrpBuffers } from '../../hdrp-buffers';
|
|
14
|
+
import type { _InternalRenderPipelineContext } from '../../record/render-context';
|
|
15
|
+
import type { RenderPipelineFrame } from '../../render-pipeline';
|
|
16
|
+
import type { StandardTopologyInputValue } from './topology';
|
|
17
|
+
|
|
18
|
+
/** Graph resources projected from one Standard lighting topology input. */
|
|
19
|
+
export interface StandardClusterGraphBuffers {
|
|
20
|
+
readonly lightData: GraphBuffer;
|
|
21
|
+
readonly clusterGrid: GraphBuffer;
|
|
22
|
+
readonly lightIndexList: GraphBuffer;
|
|
23
|
+
readonly clusterUniform: GraphBuffer;
|
|
24
|
+
readonly lightBounds: GraphBuffer;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Stable graph-time projection; never retain a frame's typed-array payload. */
|
|
28
|
+
interface StandardClusterGraphDescriptor {
|
|
29
|
+
readonly grid: { readonly x: number; readonly y: number; readonly z: number };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function projectDescriptor(input: StandardTopologyInputValue): StandardClusterGraphDescriptor {
|
|
33
|
+
const { x, y, z } = input.prepared.layout.grid;
|
|
34
|
+
return { grid: { x, y, z } };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveBuffers(
|
|
38
|
+
frame: RenderPipelineFrame,
|
|
39
|
+
descriptor: StandardClusterGraphDescriptor,
|
|
40
|
+
): HdrpBuffers {
|
|
41
|
+
const internal = frame as _InternalRenderPipelineContext;
|
|
42
|
+
const buffers = getOrCreateHdrpBuffers(internal.runtime, descriptor.grid);
|
|
43
|
+
if (buffers === null) throw new Error('Standard persistent buffer allocation failed');
|
|
44
|
+
return buffers;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Resolve membership execution from the frame's current prepared payload. */
|
|
48
|
+
export function shouldExecuteStandardClusterMembershipPass(frame: RenderPipelineFrame): boolean {
|
|
49
|
+
const standardLighting = (frame as _InternalRenderPipelineContext).standardLighting;
|
|
50
|
+
return (
|
|
51
|
+
standardLighting?.kind === 'clustered' &&
|
|
52
|
+
standardLighting.transport.kind === 'compute-storage' &&
|
|
53
|
+
standardLighting.prepared.local.length > 0
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Import the shared Standard Cluster buffers with sizes derived from layout. */
|
|
58
|
+
export function importStandardClusterBuffers(
|
|
59
|
+
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
60
|
+
input: StandardTopologyInputValue,
|
|
61
|
+
): Result<StandardClusterGraphBuffers | null, RenderGraphError> {
|
|
62
|
+
if (input.kind === 'no-local-lights') return ok(null);
|
|
63
|
+
const layout = input.prepared.layout;
|
|
64
|
+
const descriptor = projectDescriptor(input);
|
|
65
|
+
const usage = GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST;
|
|
66
|
+
const lightData = graph.importBuffer(
|
|
67
|
+
'hdrp-light-data',
|
|
68
|
+
{ size: layout.lightDataSlotCount * 64, usage },
|
|
69
|
+
(frame) => resolveBuffers(frame, descriptor).lightDataBuffer,
|
|
70
|
+
);
|
|
71
|
+
if (!lightData.ok) return lightData;
|
|
72
|
+
const clusterGrid = graph.importBuffer(
|
|
73
|
+
'hdrp-cluster-grid',
|
|
74
|
+
{ size: layout.clusterGridU32Length * 4, usage },
|
|
75
|
+
(frame) => resolveBuffers(frame, descriptor).clusterGridBuffer,
|
|
76
|
+
);
|
|
77
|
+
if (!clusterGrid.ok) return clusterGrid;
|
|
78
|
+
const lightIndexList = graph.importBuffer(
|
|
79
|
+
'hdrp-light-index-list',
|
|
80
|
+
{ size: layout.lightIndexListCapacity * 4, usage },
|
|
81
|
+
(frame) => resolveBuffers(frame, descriptor).lightIndexListBuffer,
|
|
82
|
+
);
|
|
83
|
+
if (!lightIndexList.ok) return lightIndexList;
|
|
84
|
+
const clusterUniform = graph.importBuffer(
|
|
85
|
+
'hdrp-cluster-uniform',
|
|
86
|
+
{ size: 32, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST },
|
|
87
|
+
(frame) => resolveBuffers(frame, descriptor).clusterUniformBuffer,
|
|
88
|
+
);
|
|
89
|
+
if (!clusterUniform.ok) return clusterUniform;
|
|
90
|
+
const lightBounds = graph.importBuffer(
|
|
91
|
+
'hdrp-light-bounds',
|
|
92
|
+
{ size: layout.lightBoundsInt32Length * 4, usage },
|
|
93
|
+
(frame) => resolveBuffers(frame, descriptor).lightBoundsBuffer,
|
|
94
|
+
);
|
|
95
|
+
if (!lightBounds.ok) return lightBounds;
|
|
96
|
+
return ok({
|
|
97
|
+
lightData: lightData.value,
|
|
98
|
+
clusterGrid: clusterGrid.value,
|
|
99
|
+
lightIndexList: lightIndexList.value,
|
|
100
|
+
clusterUniform: clusterUniform.value,
|
|
101
|
+
lightBounds: lightBounds.value,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Accesses used by raster consumers of the shared Cluster payload. */
|
|
106
|
+
export function standardClusterReadAccesses(
|
|
107
|
+
buffers: StandardClusterGraphBuffers,
|
|
108
|
+
): readonly GraphAccess[] {
|
|
109
|
+
return [
|
|
110
|
+
{ resource: buffers.lightData, usage: 'storage-read' },
|
|
111
|
+
{ resource: buffers.clusterGrid, usage: 'storage-read' },
|
|
112
|
+
{ resource: buffers.lightIndexList, usage: 'storage-read' },
|
|
113
|
+
{ resource: buffers.clusterUniform, usage: 'uniform-read' },
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Add the sole GPU membership producer. CPU membership is already materialized
|
|
119
|
+
* in PreparedStandardLighting and therefore contributes no second pass.
|
|
120
|
+
*/
|
|
121
|
+
export function addStandardClusterMembershipPass(
|
|
122
|
+
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
123
|
+
input: StandardTopologyInputValue,
|
|
124
|
+
buffers: StandardClusterGraphBuffers,
|
|
125
|
+
): Result<void, RenderGraphError> {
|
|
126
|
+
if (input.kind === 'no-local-lights') return ok(undefined);
|
|
127
|
+
if (input.transport.kind !== 'compute-storage') return ok(undefined);
|
|
128
|
+
const descriptor = projectDescriptor(input);
|
|
129
|
+
return graph.addComputePass('cluster-membership-producer', {
|
|
130
|
+
accesses: [
|
|
131
|
+
{ resource: buffers.clusterGrid, usage: 'storage-read' },
|
|
132
|
+
{ resource: buffers.clusterUniform, usage: 'uniform-read' },
|
|
133
|
+
{ resource: buffers.lightBounds, usage: 'storage-read' },
|
|
134
|
+
{ resource: buffers.lightIndexList, usage: 'storage-write' },
|
|
135
|
+
],
|
|
136
|
+
// Keep the clustered graph/ABI stable for a directional-only frame, but
|
|
137
|
+
// do not require a producer bind group when there is no local corpus to
|
|
138
|
+
// bin. The zero-length prepared payload is already the complete result;
|
|
139
|
+
// executing this pass would only turn an otherwise valid frame into a
|
|
140
|
+
// null-backend encode failure.
|
|
141
|
+
executeIf: shouldExecuteStandardClusterMembershipPass,
|
|
142
|
+
encode: ({ pass, frame }) => {
|
|
143
|
+
const internal = frame as _InternalRenderPipelineContext;
|
|
144
|
+
const pipeline = internal.pipelineState.hdrpClusterMembershipPipeline;
|
|
145
|
+
const bindings = internal.hdrpClusterMembershipBindGroup;
|
|
146
|
+
if (pipeline === null || bindings === null) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
'standard-cluster-transport-unavailable: selected compute Cluster producer is not ready',
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const { x, y, z } = descriptor.grid;
|
|
152
|
+
pass.setPipeline(pipeline);
|
|
153
|
+
pass.setBindGroup(0, bindings);
|
|
154
|
+
pass.dispatchWorkgroups(Math.ceil((x * y * z) / 64));
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|