@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
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
import { TRANSPARENT_SORT_MODE_LAYER_Z, TRANSPARENT_SORT_MODE_LAYER_Y } from './chunk-RXZ3RGOU.mjs';
|
|
2
|
-
import { DEFAULT_CLUSTER_GRID,
|
|
2
|
+
import { DEFAULT_CLUSTER_GRID, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GPU_TEXTURE_USAGE_COPY_SRC, EquirectProjectionFailedError, SHADOW_ATLAS_DEFAULT_LAYERS, SHADOW_ATLAS_DEFAULT_FACE_SIZE, ShadowAtlas, PointShadowAtlasUninitializedError, PointShadowAtlasBoundsViolationError, DEFAULT_STANDARD_PROFILE, StandardClusterTransportUnavailableError, createIblKernelCache, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, MAX_LIGHTS, CLUSTER_GRID_STRIDE_U32, LIGHT_INDEX_LIST_CAPACITY, StandardLightBudgetExceededError, createStandardClusterLayout, RenderTargetDescriptorInvalidError, RendererOperationError, StandardClusterIndexOverflowError, FXAA_POST_PROCESS_ID, VideoUploadUnsupportedError, VertexColorVariantConflictError } from './chunk-6OPXDQ2E.mjs';
|
|
3
3
|
import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-VFPLTKCU.mjs';
|
|
4
|
-
import {
|
|
5
|
-
import { ok, err as err$1, toShared, ASSET_ERROR_HINTS,
|
|
6
|
-
import { ok as ok$1, err, RhiError
|
|
7
|
-
import {
|
|
8
|
-
import { deriveVertexLayoutProjection, deriveVertexBufferLayout } from '@forgeax/engine-geometry';
|
|
4
|
+
import { worldEntityKey } from './chunk-BXBQQKFQ.mjs';
|
|
5
|
+
import { ok, derive, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, KNOWN_PASS_KINDS, resolveMaterialTextureCoordinates, handleSlot } from '@forgeax/engine-types';
|
|
6
|
+
import { ok as ok$1, err, RhiError } from '@forgeax/engine-rhi';
|
|
7
|
+
import { mat3, vec3, mat4 } from '@forgeax/engine-math';
|
|
9
8
|
import { DEFAULT_STANDARD_PBR_PARAM_SCHEMA, DEFAULT_MSDF_TEXT_PARAM_SCHEMA, DEFAULT_SPRITE_PARAM_SCHEMA, DEFAULT_UNLIT_PARAM_SCHEMA } from '@forgeax/engine-shader';
|
|
10
|
-
import {
|
|
9
|
+
import { deriveVertexLayoutProjection, deriveVertexBufferLayout } from '@forgeax/engine-geometry';
|
|
10
|
+
import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
11
11
|
import { probeVideoHighPerfUpload, VIDEO_ELEMENT_PROVIDER_KEY } from '@forgeax/engine-graphics-extras';
|
|
12
|
-
import {
|
|
12
|
+
import { isCompressedFormat, bytesPerRow, blockParamsForFormat } from '@forgeax/engine-codec';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
var GPU_TEXTURE_USAGE_TEXTURE_BINDING = 4;
|
|
18
|
-
var GPU_TEXTURE_USAGE_RENDER_ATTACHMENT = 16;
|
|
19
|
-
var GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING = GPU_TEXTURE_USAGE_RENDER_ATTACHMENT | GPU_TEXTURE_USAGE_TEXTURE_BINDING;
|
|
20
|
-
|
|
21
|
-
// src/render-pipeline.ts
|
|
14
|
+
function resolveOutputDither(config) {
|
|
15
|
+
return config?.outputDither ?? true;
|
|
16
|
+
}
|
|
22
17
|
function createRenderPipelineTarget(graph, label, descriptor, viewDescriptor = {}) {
|
|
23
18
|
const texture = graph.createTexture(label, descriptor);
|
|
24
19
|
if (!texture.ok) return texture;
|
|
@@ -88,628 +83,225 @@ function importRenderPipelineSurface(graph, topology) {
|
|
|
88
83
|
}
|
|
89
84
|
});
|
|
90
85
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
86
|
+
|
|
87
|
+
// src/gpu-stage.ts
|
|
88
|
+
var GPU_SHADER_STAGE_VERTEX = 1;
|
|
89
|
+
var GPU_SHADER_STAGE_FRAGMENT = 2;
|
|
90
|
+
var GPU_SHADER_STAGE_COMPUTE = 4;
|
|
91
|
+
|
|
92
|
+
// src/gpu-usage.ts
|
|
93
|
+
var GPU_BUFFER_USAGE_VERTEX = 32;
|
|
94
|
+
var GPU_BUFFER_USAGE_INDEX = 16;
|
|
95
|
+
var GPU_BUFFER_USAGE_COPY_DST = 8;
|
|
96
|
+
var GPU_BUFFER_USAGE_COPY_SRC = 4;
|
|
97
|
+
var GPU_BUFFER_USAGE_UNIFORM = 64;
|
|
98
|
+
var GPU_BUFFER_USAGE_STORAGE = 128;
|
|
99
|
+
var GPU_BUFFER_USAGE_INDIRECT = 256;
|
|
100
|
+
var GPU_BUFFER_USAGE_MAP_READ = 1;
|
|
101
|
+
var GPU_BUFFER_USAGE_QUERY_RESOLVE = 512;
|
|
102
|
+
var BYTES_PER_LIGHT_SLOT = 64;
|
|
103
|
+
var LightSlotKind = {
|
|
104
|
+
POINT: 0,
|
|
105
|
+
SPOT: 1
|
|
106
|
+
};
|
|
107
|
+
var STORAGE_BUFFER_MIN_REQUIRED = 4;
|
|
108
|
+
var POINT_LIGHT_STD430_BYTES = 32;
|
|
109
|
+
var SPOT_LIGHT_STD430_BYTES = 80;
|
|
110
|
+
var SPOT_LIGHT_SHADOW_TILE_SENTINEL = -1;
|
|
111
|
+
var SPOT_LIGHT_PROJECTOR_FLAG = 1073741824;
|
|
112
|
+
var SPOT_LIGHT_TILE_MASK = SPOT_LIGHT_PROJECTOR_FLAG - 1;
|
|
113
|
+
var LIGHT_ARRAY_HEADER_BYTES = 16;
|
|
114
|
+
var LIGHT_ARRAY_MAX_SLOTS = 4;
|
|
115
|
+
var POINT_LIGHT_SHADOW_LAYER_SENTINEL = -1;
|
|
116
|
+
function packLightSlot(snap, shadow, projectorSelected) {
|
|
117
|
+
const out = new Float32Array(16);
|
|
118
|
+
out[0] = snap.position[0] ?? 0;
|
|
119
|
+
out[1] = snap.position[1] ?? 0;
|
|
120
|
+
out[2] = snap.position[2] ?? 0;
|
|
121
|
+
out[3] = snap.invRangeSquared;
|
|
122
|
+
out[4] = snap.color[0] ?? 0;
|
|
123
|
+
out[5] = snap.color[1] ?? 0;
|
|
124
|
+
out[6] = snap.color[2] ?? 0;
|
|
125
|
+
out[7] = snap.kind === "point" ? 1 : snap.cosInner;
|
|
126
|
+
out[8] = snap.kind === "point" ? 0 : snap.direction[0] ?? 0;
|
|
127
|
+
out[9] = snap.kind === "point" ? 0 : snap.direction[1] ?? 0;
|
|
128
|
+
out[10] = snap.kind === "point" ? 0 : snap.direction[2] ?? 0;
|
|
129
|
+
out[11] = snap.kind === "point" ? 0 : snap.cosOuter;
|
|
130
|
+
const u32 = new Uint32Array(out.buffer);
|
|
131
|
+
u32[12] = snap.kind === "point" ? LightSlotKind.POINT : LightSlotKind.SPOT;
|
|
132
|
+
const i32 = new Int32Array(out.buffer);
|
|
133
|
+
if (snap.kind === "point" && shadow !== void 0) {
|
|
134
|
+
i32[13] = shadow.shadowAtlasLayer;
|
|
135
|
+
out[14] = shadow.near;
|
|
136
|
+
out[15] = shadow.far;
|
|
137
|
+
} else if (snap.kind === "spot") {
|
|
138
|
+
const hasProjector = projectorSelected ?? (snap.projectorAsset !== void 0 && snap.lightViewProj !== void 0);
|
|
139
|
+
const tile = snap.shadowAtlasTile >= 0 ? snap.shadowAtlasTile : 0;
|
|
140
|
+
i32[13] = hasProjector ? SPOT_LIGHT_PROJECTOR_FLAG | tile & SPOT_LIGHT_TILE_MASK : snap.shadowAtlasTile >= 0 ? snap.shadowAtlasTile : SPOT_LIGHT_SHADOW_TILE_SENTINEL;
|
|
141
|
+
out[14] = snap.shadowIntensity ?? 1;
|
|
142
|
+
out[15] = snap.pcfKernelSize ?? 3;
|
|
143
|
+
} else {
|
|
144
|
+
i32[13] = POINT_LIGHT_SHADOW_LAYER_SENTINEL;
|
|
114
145
|
}
|
|
115
|
-
|
|
116
|
-
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
function assertStorageBufferCap(cap) {
|
|
149
|
+
if (cap >= STORAGE_BUFFER_MIN_REQUIRED) {
|
|
150
|
+
return ok$1(true);
|
|
117
151
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
scratch.clusterDeltas = new Int32Array(differenceVolumeSize);
|
|
152
|
+
if (cap === 0) {
|
|
153
|
+
return ok$1(false);
|
|
121
154
|
}
|
|
155
|
+
return err(
|
|
156
|
+
new RhiError({
|
|
157
|
+
code: "limit-exceeded",
|
|
158
|
+
expected: `device.limits.maxStorageBuffersPerShaderStage >= ${STORAGE_BUFFER_MIN_REQUIRED}, or exactly 0 for uniform-fallback path`,
|
|
159
|
+
hint: `device.limits.maxStorageBuffersPerShaderStage = ${cap} < ${STORAGE_BUFFER_MIN_REQUIRED} and != 0. WebGPU spec default minimum is 8; this may be a downlevel adapter with partial storage support. Wait for todo-125 multi-light-pack which collapses point + spot into a single cluster storage buffer (1 entry instead of 2) for 2-3 buffer environments.`,
|
|
160
|
+
detail: {
|
|
161
|
+
maxStorageBufferBindingSize: cap,
|
|
162
|
+
requestedBytes: STORAGE_BUFFER_MIN_REQUIRED
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
);
|
|
122
166
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
167
|
+
var BGL_ONLY_SPEC_STUB = Object.freeze({
|
|
168
|
+
shader: { id: "", passKind: "forward", variantSet: void 0 },
|
|
169
|
+
attachments: { colorFormats: [], depthFormat: void 0, sampleCount: 1 },
|
|
170
|
+
geometry: { topology: "triangle-list", vertexLayout: {} },
|
|
171
|
+
renderState: void 0
|
|
172
|
+
});
|
|
173
|
+
function buildPbrMaterialUserRegionEntries(paramSchema = DEFAULT_STANDARD_PBR_PARAM_SCHEMA) {
|
|
174
|
+
const derived = derive(paramSchema);
|
|
175
|
+
const entries = derived.bglEntries.map(
|
|
176
|
+
(entry) => ({
|
|
177
|
+
binding: entry.binding,
|
|
178
|
+
visibility: entry.visibility,
|
|
179
|
+
...entry.buffer === void 0 ? {} : { buffer: entry.buffer },
|
|
180
|
+
...entry.sampler === void 0 ? {} : { sampler: entry.sampler },
|
|
181
|
+
...entry.texture === void 0 ? {} : { texture: entry.texture },
|
|
182
|
+
...entry.storageTexture === void 0 ? {} : { storageTexture: entry.storageTexture }
|
|
183
|
+
})
|
|
184
|
+
);
|
|
185
|
+
const ubo = entries[0];
|
|
186
|
+
if (ubo !== void 0 && ubo.binding === 0 && ubo.buffer?.type === "uniform") {
|
|
187
|
+
entries[0] = {
|
|
188
|
+
binding: 0,
|
|
189
|
+
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
190
|
+
buffer: { type: "uniform", hasDynamicOffset: true }
|
|
191
|
+
};
|
|
126
192
|
}
|
|
127
|
-
|
|
128
|
-
return Math.min(derived, 1e3);
|
|
193
|
+
return entries;
|
|
129
194
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const sy = corner[1];
|
|
230
|
-
const ndc = projectToNdc(sx, sy, zTest, proj);
|
|
231
|
-
ndcMinX = Math.min(ndcMinX, ndc[0]);
|
|
232
|
-
ndcMaxX = Math.max(ndcMaxX, ndc[0]);
|
|
233
|
-
ndcMinY = Math.min(ndcMinY, ndc[1]);
|
|
234
|
-
ndcMaxY = Math.max(ndcMaxY, ndc[1]);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
ndcMinX = Math.max(ndcMinX, -1);
|
|
240
|
-
ndcMinY = Math.max(ndcMinY, -1);
|
|
241
|
-
ndcMaxX = Math.min(ndcMaxX, 1);
|
|
242
|
-
ndcMaxY = Math.min(ndcMaxY, 1);
|
|
243
|
-
result.min[0] = ndcMinX;
|
|
244
|
-
result.min[1] = ndcMinY;
|
|
245
|
-
result.min[2] = minViewZ;
|
|
246
|
-
result.max[0] = ndcMaxX;
|
|
247
|
-
result.max[1] = ndcMaxY;
|
|
248
|
-
result.max[2] = maxViewZ;
|
|
249
|
-
return result;
|
|
250
|
-
}
|
|
251
|
-
function projectToNdc(vx, vy, vz, proj) {
|
|
252
|
-
const p00 = proj[0] ?? 0;
|
|
253
|
-
const p10 = proj[4] ?? 0;
|
|
254
|
-
const p20 = proj[8] ?? 0;
|
|
255
|
-
const p30 = proj[12] ?? 0;
|
|
256
|
-
const p01 = proj[1] ?? 0;
|
|
257
|
-
const p11 = proj[5] ?? 0;
|
|
258
|
-
const p21 = proj[9] ?? 0;
|
|
259
|
-
const p31 = proj[13] ?? 0;
|
|
260
|
-
const p02 = proj[2] ?? 0;
|
|
261
|
-
const p12 = proj[6] ?? 0;
|
|
262
|
-
const p22 = proj[10] ?? 0;
|
|
263
|
-
const p32 = proj[14] ?? 0;
|
|
264
|
-
const p03 = proj[3] ?? 0;
|
|
265
|
-
const p13 = proj[7] ?? 0;
|
|
266
|
-
const p23 = proj[11] ?? 0;
|
|
267
|
-
const p33 = proj[15] ?? 0;
|
|
268
|
-
const cx = p00 * vx + p10 * vy + p20 * vz + p30;
|
|
269
|
-
const cy = p01 * vx + p11 * vy + p21 * vz + p31;
|
|
270
|
-
const cz = p02 * vx + p12 * vy + p22 * vz + p32;
|
|
271
|
-
const cw = p03 * vx + p13 * vy + p23 * vz + p33;
|
|
272
|
-
if (Math.abs(cw) < 1e-10) {
|
|
273
|
-
return [vx < 0 ? -1 : 1, vy < 0 ? -1 : 1, cz];
|
|
274
|
-
}
|
|
275
|
-
const invW = 1 / cw;
|
|
276
|
-
return [cx * invW, cy * invW, cz * invW];
|
|
277
|
-
}
|
|
278
|
-
function ndcPositionToCluster(ndc, viewZ, gridX, gridY, gridZ, near, far) {
|
|
279
|
-
const ndcX = ndc[0] ?? 0;
|
|
280
|
-
const ndcY = ndc[1] ?? 0;
|
|
281
|
-
let cx = Math.floor((ndcX * 0.5 + 0.5) * gridX);
|
|
282
|
-
let cy = Math.floor((ndcY * 0.5 + 0.5) * gridY);
|
|
283
|
-
if (cx < 0) cx = 0;
|
|
284
|
-
if (cx >= gridX) cx = gridX - 1;
|
|
285
|
-
if (cy < 0) cy = 0;
|
|
286
|
-
if (cy >= gridY) cy = gridY - 1;
|
|
287
|
-
const cz = viewZToZSlice(viewZ, gridZ, near, far);
|
|
288
|
-
return { x: cx, y: cy, z: cz };
|
|
289
|
-
}
|
|
290
|
-
function calculateSphereClusterBounds(aabb, gridX, gridY, gridZ, near, far, output) {
|
|
291
|
-
const result = output ?? {
|
|
292
|
-
min: { x: 0, y: 0, z: 0 },
|
|
293
|
-
max: { x: 0, y: 0, z: 0 }
|
|
294
|
-
};
|
|
295
|
-
const minZ = aabb.min[2] ?? 0;
|
|
296
|
-
const maxZ = aabb.max[2] ?? 0;
|
|
297
|
-
const idxMin = ndcPositionToCluster(aabb.min, minZ, gridX, gridY, gridZ, near, far);
|
|
298
|
-
const idxMax = ndcPositionToCluster(aabb.max, maxZ, gridX, gridY, gridZ, near, far);
|
|
299
|
-
result.min.x = Math.min(idxMin.x, idxMax.x);
|
|
300
|
-
result.min.y = Math.min(idxMin.y, idxMax.y);
|
|
301
|
-
result.min.z = Math.min(idxMin.z, idxMax.z);
|
|
302
|
-
result.max.x = Math.max(idxMin.x, idxMax.x);
|
|
303
|
-
result.max.y = Math.max(idxMin.y, idxMax.y);
|
|
304
|
-
result.max.z = Math.max(idxMin.z, idxMax.z);
|
|
305
|
-
return result;
|
|
306
|
-
}
|
|
307
|
-
function bin(lights, view, proj, grid, near, far, clusterGrid, lightIndexList, capacity, scratch, profile, options = {}) {
|
|
308
|
-
const gridX = grid.x;
|
|
309
|
-
const gridY = grid.y;
|
|
310
|
-
const gridZ = grid.z;
|
|
311
|
-
const clusterCount = gridX * gridY * gridZ;
|
|
312
|
-
const binScratch = scratch ?? createClusterBinScratch();
|
|
313
|
-
ensureScratchCapacity(binScratch, clusterCount, lights.length, gridX, gridY, gridZ);
|
|
314
|
-
const { clusterCounts, clusterCursors, lightBounds, clusterDeltas } = binScratch;
|
|
315
|
-
clusterGrid.fill(0, 0, clusterCount * 2);
|
|
316
|
-
clusterCounts.fill(0, 0, clusterCount);
|
|
317
|
-
let attemptedTotal = 0;
|
|
318
|
-
runClusterBinProfilePhase(profile, "light-bounds-and-occupancy", () => {
|
|
319
|
-
runClusterBinProfilePhase(profile, "light-bounds-and-occupancy/light-aabb", () => {
|
|
320
|
-
for (let lightIdx = 0; lightIdx < lights.length; lightIdx++) {
|
|
321
|
-
const boundsOffset = lightIdx * 6;
|
|
322
|
-
lightBounds[boundsOffset] = -1;
|
|
323
|
-
const light = lights[lightIdx];
|
|
324
|
-
if (!light) continue;
|
|
325
|
-
const radius = deriveCullingRadius(light.range, 1);
|
|
326
|
-
if (radius <= 0) {
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
const aabb = clusterSpaceObjectAabb(
|
|
330
|
-
light.position,
|
|
331
|
-
radius,
|
|
332
|
-
view,
|
|
333
|
-
proj,
|
|
334
|
-
binScratch.aabbOutput
|
|
335
|
-
);
|
|
336
|
-
const bounds = calculateSphereClusterBounds(
|
|
337
|
-
aabb,
|
|
338
|
-
gridX,
|
|
339
|
-
gridY,
|
|
340
|
-
gridZ,
|
|
341
|
-
near,
|
|
342
|
-
far,
|
|
343
|
-
binScratch.boundsOutput
|
|
344
|
-
);
|
|
345
|
-
if (bounds.min.x > bounds.max.x || bounds.min.y > bounds.max.y || bounds.min.z > bounds.max.z) {
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
lightBounds[boundsOffset] = bounds.min.x;
|
|
349
|
-
lightBounds[boundsOffset + 1] = bounds.min.y;
|
|
350
|
-
lightBounds[boundsOffset + 2] = bounds.min.z;
|
|
351
|
-
lightBounds[boundsOffset + 3] = bounds.max.x;
|
|
352
|
-
lightBounds[boundsOffset + 4] = bounds.max.y;
|
|
353
|
-
lightBounds[boundsOffset + 5] = bounds.max.z;
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
runClusterBinProfilePhase(profile, "light-bounds-and-occupancy/cluster-occupancy", () => {
|
|
357
|
-
const diffX = gridX + 1;
|
|
358
|
-
const diffY = gridY + 1;
|
|
359
|
-
const diffRow = diffX * diffY;
|
|
360
|
-
clusterDeltas.fill(0, 0, diffRow * (gridZ + 1));
|
|
361
|
-
const addDifference = (x, y, z, delta) => {
|
|
362
|
-
const index = z * diffRow + y * diffX + x;
|
|
363
|
-
clusterDeltas[index] = (clusterDeltas[index] ?? 0) + delta;
|
|
364
|
-
};
|
|
365
|
-
for (let lightIdx = 0; lightIdx < lights.length; lightIdx++) {
|
|
366
|
-
const boundsOffset = lightIdx * 6;
|
|
367
|
-
if ((lightBounds[boundsOffset] ?? -1) < 0) continue;
|
|
368
|
-
const minX = lightBounds[boundsOffset] ?? 0;
|
|
369
|
-
const minY = lightBounds[boundsOffset + 1] ?? 0;
|
|
370
|
-
const minZ = lightBounds[boundsOffset + 2] ?? 0;
|
|
371
|
-
const maxX = lightBounds[boundsOffset + 3] ?? -1;
|
|
372
|
-
const maxY = lightBounds[boundsOffset + 4] ?? -1;
|
|
373
|
-
const maxZ = lightBounds[boundsOffset + 5] ?? -1;
|
|
374
|
-
const endX = maxX + 1;
|
|
375
|
-
const endY = maxY + 1;
|
|
376
|
-
const endZ = maxZ + 1;
|
|
377
|
-
addDifference(minX, minY, minZ, 1);
|
|
378
|
-
addDifference(endX, minY, minZ, -1);
|
|
379
|
-
addDifference(minX, endY, minZ, -1);
|
|
380
|
-
addDifference(minX, minY, endZ, -1);
|
|
381
|
-
addDifference(endX, endY, minZ, 1);
|
|
382
|
-
addDifference(endX, minY, endZ, 1);
|
|
383
|
-
addDifference(minX, endY, endZ, 1);
|
|
384
|
-
addDifference(endX, endY, endZ, -1);
|
|
385
|
-
}
|
|
386
|
-
for (let cz = 0; cz < gridZ; cz++) {
|
|
387
|
-
for (let cy = 0; cy < gridY; cy++) {
|
|
388
|
-
for (let cx = 0; cx < gridX; cx++) {
|
|
389
|
-
const diffIdx = cz * diffRow + cy * diffX + cx;
|
|
390
|
-
let count = clusterDeltas[diffIdx] ?? 0;
|
|
391
|
-
if (cx > 0) count += clusterDeltas[diffIdx - 1] ?? 0;
|
|
392
|
-
if (cy > 0) count += clusterDeltas[diffIdx - diffX] ?? 0;
|
|
393
|
-
if (cz > 0) count += clusterDeltas[diffIdx - diffRow] ?? 0;
|
|
394
|
-
if (cx > 0 && cy > 0) count -= clusterDeltas[diffIdx - diffX - 1] ?? 0;
|
|
395
|
-
if (cx > 0 && cz > 0) count -= clusterDeltas[diffIdx - diffRow - 1] ?? 0;
|
|
396
|
-
if (cy > 0 && cz > 0) count -= clusterDeltas[diffIdx - diffRow - diffX] ?? 0;
|
|
397
|
-
if (cx > 0 && cy > 0 && cz > 0) {
|
|
398
|
-
count += clusterDeltas[diffIdx - diffRow - diffX - 1] ?? 0;
|
|
399
|
-
}
|
|
400
|
-
clusterDeltas[diffIdx] = count;
|
|
401
|
-
const clusterIdx = cz * gridY * gridX + cy * gridX + cx;
|
|
402
|
-
clusterCounts[clusterIdx] = count;
|
|
403
|
-
attemptedTotal += count;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
});
|
|
409
|
-
if (attemptedTotal > capacity) {
|
|
410
|
-
return err({
|
|
411
|
-
code: "index-overflow",
|
|
412
|
-
expected: `writeCount <= ${capacity}`,
|
|
413
|
-
hint: `light index list overflow: needed ${attemptedTotal} entries, capacity ${capacity}; reduce lights, shrink grid, or shrink ranges (overrun = ${attemptedTotal - capacity})`,
|
|
414
|
-
detail: { actual: attemptedTotal, capacity }
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
const writeCount = runClusterBinProfilePhase(profile, "cluster-reserve", () => {
|
|
418
|
-
let reserved = 0;
|
|
419
|
-
for (let ci = 0; ci < clusterCount; ci++) {
|
|
420
|
-
const base = ci * 2;
|
|
421
|
-
clusterGrid[base] = reserved;
|
|
422
|
-
clusterGrid[base + 1] = clusterCounts[ci] ?? 0;
|
|
423
|
-
clusterCursors[ci] = reserved;
|
|
424
|
-
reserved += clusterCounts[ci] ?? 0;
|
|
425
|
-
}
|
|
426
|
-
return reserved;
|
|
427
|
-
});
|
|
428
|
-
if (options.writeMembership !== false) {
|
|
429
|
-
runClusterBinProfilePhase(profile, "light-index-write", () => {
|
|
430
|
-
for (let lightIdx = 0; lightIdx < lights.length; lightIdx++) {
|
|
431
|
-
const boundsOffset = lightIdx * 6;
|
|
432
|
-
let valid = false;
|
|
433
|
-
let minX = 0;
|
|
434
|
-
let minY = 0;
|
|
435
|
-
let minZ = 0;
|
|
436
|
-
let maxX = -1;
|
|
437
|
-
let maxY = -1;
|
|
438
|
-
let maxZ = -1;
|
|
439
|
-
runClusterBinProfilePhase(profile, "light-index-write/bounds-read", () => {
|
|
440
|
-
if ((lightBounds[boundsOffset] ?? -1) < 0) return;
|
|
441
|
-
valid = true;
|
|
442
|
-
minX = lightBounds[boundsOffset] ?? 0;
|
|
443
|
-
minY = lightBounds[boundsOffset + 1] ?? 0;
|
|
444
|
-
minZ = lightBounds[boundsOffset + 2] ?? 0;
|
|
445
|
-
maxX = lightBounds[boundsOffset + 3] ?? -1;
|
|
446
|
-
maxY = lightBounds[boundsOffset + 4] ?? -1;
|
|
447
|
-
maxZ = lightBounds[boundsOffset + 5] ?? -1;
|
|
448
|
-
});
|
|
449
|
-
if (!valid) continue;
|
|
450
|
-
runClusterBinProfilePhase(profile, "light-index-write/cluster-write", () => {
|
|
451
|
-
for (let cz = minZ; cz <= maxZ; cz++) {
|
|
452
|
-
for (let cy = minY; cy <= maxY; cy++) {
|
|
453
|
-
for (let cx = minX; cx <= maxX; cx++) {
|
|
454
|
-
const clusterIdx = cz * gridY * gridX + cy * gridX + cx;
|
|
455
|
-
const cursor = clusterCursors[clusterIdx] ?? 0;
|
|
456
|
-
lightIndexList[cursor] = lightIdx;
|
|
457
|
-
clusterCursors[clusterIdx] = cursor + 1;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
return ok$1(writeCount);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// src/gpu-stage.ts
|
|
469
|
-
var GPU_SHADER_STAGE_VERTEX = 1;
|
|
470
|
-
var GPU_SHADER_STAGE_FRAGMENT = 2;
|
|
471
|
-
var GPU_SHADER_STAGE_COMPUTE = 4;
|
|
472
|
-
|
|
473
|
-
// src/gpu-usage.ts
|
|
474
|
-
var GPU_BUFFER_USAGE_VERTEX = 32;
|
|
475
|
-
var GPU_BUFFER_USAGE_INDEX = 16;
|
|
476
|
-
var GPU_BUFFER_USAGE_COPY_DST = 8;
|
|
477
|
-
var GPU_BUFFER_USAGE_COPY_SRC = 4;
|
|
478
|
-
var GPU_BUFFER_USAGE_UNIFORM = 64;
|
|
479
|
-
var GPU_BUFFER_USAGE_STORAGE = 128;
|
|
480
|
-
var GPU_BUFFER_USAGE_INDIRECT = 256;
|
|
481
|
-
var BYTES_PER_LIGHT_SLOT = 64;
|
|
482
|
-
var LightSlotKind = {
|
|
483
|
-
POINT: 0,
|
|
484
|
-
SPOT: 1
|
|
485
|
-
};
|
|
486
|
-
var STORAGE_BUFFER_MIN_REQUIRED = 4;
|
|
487
|
-
var POINT_LIGHT_STD430_BYTES = 32;
|
|
488
|
-
var SPOT_LIGHT_STD430_BYTES = 64;
|
|
489
|
-
var SPOT_LIGHT_SHADOW_TILE_SENTINEL = -1;
|
|
490
|
-
var LIGHT_ARRAY_HEADER_BYTES = 16;
|
|
491
|
-
var LIGHT_ARRAY_MAX_SLOTS = 4;
|
|
492
|
-
var POINT_LIGHT_SHADOW_LAYER_SENTINEL = -1;
|
|
493
|
-
function packPointLight(snap) {
|
|
494
|
-
const out = new Float32Array(8);
|
|
495
|
-
out[0] = snap.position[0] ?? 0;
|
|
496
|
-
out[1] = snap.position[1] ?? 0;
|
|
497
|
-
out[2] = snap.position[2] ?? 0;
|
|
498
|
-
out[3] = snap.invRangeSquared;
|
|
499
|
-
out[4] = snap.color[0] ?? 0;
|
|
500
|
-
out[5] = snap.color[1] ?? 0;
|
|
501
|
-
out[6] = snap.color[2] ?? 0;
|
|
502
|
-
const i32 = new Int32Array(out.buffer);
|
|
503
|
-
i32[7] = snap.shadowAtlasLayer ?? POINT_LIGHT_SHADOW_LAYER_SENTINEL;
|
|
504
|
-
return out;
|
|
505
|
-
}
|
|
506
|
-
function packSpotLight(snap) {
|
|
507
|
-
const out = new Float32Array(16);
|
|
508
|
-
out[0] = snap.position[0] ?? 0;
|
|
509
|
-
out[1] = snap.position[1] ?? 0;
|
|
510
|
-
out[2] = snap.position[2] ?? 0;
|
|
511
|
-
out[3] = snap.invRangeSquared;
|
|
512
|
-
out[4] = snap.color[0] ?? 0;
|
|
513
|
-
out[5] = snap.color[1] ?? 0;
|
|
514
|
-
out[6] = snap.color[2] ?? 0;
|
|
515
|
-
out[7] = snap.cosInner;
|
|
516
|
-
out[8] = snap.direction[0] ?? 0;
|
|
517
|
-
out[9] = snap.direction[1] ?? 0;
|
|
518
|
-
out[10] = snap.direction[2] ?? 0;
|
|
519
|
-
out[11] = snap.cosOuter;
|
|
520
|
-
const i32 = new Int32Array(out.buffer);
|
|
521
|
-
i32[15] = snap.shadowAtlasTile ?? SPOT_LIGHT_SHADOW_TILE_SENTINEL;
|
|
522
|
-
return out;
|
|
523
|
-
}
|
|
524
|
-
function packLightArrayHeader(count) {
|
|
525
|
-
const buf = new ArrayBuffer(LIGHT_ARRAY_HEADER_BYTES);
|
|
526
|
-
const u32 = new Uint32Array(buf);
|
|
527
|
-
u32[0] = count >>> 0;
|
|
528
|
-
return buf;
|
|
529
|
-
}
|
|
530
|
-
function packLightSlot(snap, shadow) {
|
|
531
|
-
const out = new Float32Array(16);
|
|
532
|
-
out[0] = snap.position[0] ?? 0;
|
|
533
|
-
out[1] = snap.position[1] ?? 0;
|
|
534
|
-
out[2] = snap.position[2] ?? 0;
|
|
535
|
-
out[3] = snap.invRangeSquared;
|
|
536
|
-
out[4] = snap.color[0] ?? 0;
|
|
537
|
-
out[5] = snap.color[1] ?? 0;
|
|
538
|
-
out[6] = snap.color[2] ?? 0;
|
|
539
|
-
out[7] = snap.kind === "point" ? 1 : snap.cosInner;
|
|
540
|
-
out[8] = snap.kind === "point" ? 0 : snap.direction[0] ?? 0;
|
|
541
|
-
out[9] = snap.kind === "point" ? 0 : snap.direction[1] ?? 0;
|
|
542
|
-
out[10] = snap.kind === "point" ? 0 : snap.direction[2] ?? 0;
|
|
543
|
-
out[11] = snap.kind === "point" ? 0 : snap.cosOuter;
|
|
544
|
-
const u32 = new Uint32Array(out.buffer);
|
|
545
|
-
u32[12] = snap.kind === "point" ? LightSlotKind.POINT : LightSlotKind.SPOT;
|
|
546
|
-
const i32 = new Int32Array(out.buffer);
|
|
547
|
-
if (snap.kind === "point" && shadow !== void 0) {
|
|
548
|
-
i32[13] = shadow.shadowAtlasLayer;
|
|
549
|
-
out[14] = shadow.near;
|
|
550
|
-
out[15] = shadow.far;
|
|
551
|
-
} else if (snap.kind === "spot") {
|
|
552
|
-
i32[13] = snap.shadowAtlasTile;
|
|
553
|
-
} else {
|
|
554
|
-
i32[13] = POINT_LIGHT_SHADOW_LAYER_SENTINEL;
|
|
555
|
-
}
|
|
556
|
-
return out;
|
|
557
|
-
}
|
|
558
|
-
function assertStorageBufferCap(cap) {
|
|
559
|
-
if (cap >= STORAGE_BUFFER_MIN_REQUIRED) {
|
|
560
|
-
return ok$1(true);
|
|
561
|
-
}
|
|
562
|
-
if (cap === 0) {
|
|
563
|
-
return ok$1(false);
|
|
564
|
-
}
|
|
565
|
-
return err(
|
|
566
|
-
new RhiError({
|
|
567
|
-
code: "limit-exceeded",
|
|
568
|
-
expected: `device.limits.maxStorageBuffersPerShaderStage >= ${STORAGE_BUFFER_MIN_REQUIRED}, or exactly 0 for uniform-fallback path`,
|
|
569
|
-
hint: `device.limits.maxStorageBuffersPerShaderStage = ${cap} < ${STORAGE_BUFFER_MIN_REQUIRED} and != 0. WebGPU spec default minimum is 8; this may be a downlevel adapter with partial storage support. Wait for todo-125 multi-light-pack which collapses point + spot into a single cluster storage buffer (1 entry instead of 2) for 2-3 buffer environments.`,
|
|
570
|
-
detail: {
|
|
571
|
-
maxStorageBufferBindingSize: cap,
|
|
572
|
-
requestedBytes: STORAGE_BUFFER_MIN_REQUIRED
|
|
573
|
-
}
|
|
574
|
-
})
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
var BGL_ONLY_SPEC_STUB = Object.freeze({
|
|
578
|
-
shader: { id: "", passKind: "forward", variantSet: void 0 },
|
|
579
|
-
attachments: { colorFormats: [], depthFormat: void 0, sampleCount: 1 },
|
|
580
|
-
geometry: { topology: "triangle-list", vertexLayout: {} },
|
|
581
|
-
renderState: void 0
|
|
582
|
-
});
|
|
583
|
-
function buildPbrMaterialUserRegionEntries(paramSchema = DEFAULT_STANDARD_PBR_PARAM_SCHEMA) {
|
|
584
|
-
const derived = derive(paramSchema);
|
|
585
|
-
const entries = derived.bglEntries.map(
|
|
586
|
-
(entry) => ({
|
|
587
|
-
binding: entry.binding,
|
|
588
|
-
visibility: entry.visibility,
|
|
589
|
-
...entry.buffer === void 0 ? {} : { buffer: entry.buffer },
|
|
590
|
-
...entry.sampler === void 0 ? {} : { sampler: entry.sampler },
|
|
591
|
-
...entry.texture === void 0 ? {} : { texture: entry.texture },
|
|
592
|
-
...entry.storageTexture === void 0 ? {} : { storageTexture: entry.storageTexture }
|
|
593
|
-
})
|
|
594
|
-
);
|
|
595
|
-
const ubo = entries[0];
|
|
596
|
-
if (ubo !== void 0 && ubo.binding === 0 && ubo.buffer !== void 0) {
|
|
597
|
-
entries[0] = {
|
|
598
|
-
binding: 0,
|
|
599
|
-
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
600
|
-
buffer: { type: "uniform", hasDynamicOffset: true }
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
return entries;
|
|
604
|
-
}
|
|
605
|
-
function appendInjection(bgl, kind) {
|
|
606
|
-
const start = bgl.length;
|
|
607
|
-
switch (kind) {
|
|
608
|
-
case "ibl":
|
|
609
|
-
return [
|
|
610
|
-
// binding start+0: irradianceMap (texture_cube)
|
|
611
|
-
{
|
|
612
|
-
binding: start,
|
|
613
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
614
|
-
texture: { sampleType: "float", viewDimension: "cube" }
|
|
615
|
-
},
|
|
616
|
-
// binding start+1: irradianceSampler
|
|
617
|
-
{
|
|
618
|
-
binding: start + 1,
|
|
619
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
620
|
-
sampler: { type: "filtering" }
|
|
621
|
-
},
|
|
622
|
-
// binding start+2: prefilterMap (texture_cube)
|
|
623
|
-
{
|
|
624
|
-
binding: start + 2,
|
|
625
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
626
|
-
texture: { sampleType: "float", viewDimension: "cube" }
|
|
627
|
-
},
|
|
628
|
-
// binding start+3: prefilterSampler
|
|
629
|
-
{
|
|
630
|
-
binding: start + 3,
|
|
631
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
632
|
-
sampler: { type: "filtering" }
|
|
633
|
-
},
|
|
634
|
-
// binding start+4: brdfLut (texture_2d)
|
|
635
|
-
{
|
|
636
|
-
binding: start + 4,
|
|
637
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
638
|
-
texture: { sampleType: "float", viewDimension: "2d" }
|
|
639
|
-
},
|
|
640
|
-
// binding start+5: brdfLutSampler
|
|
641
|
-
{
|
|
642
|
-
binding: start + 5,
|
|
643
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
644
|
-
sampler: { type: "filtering" }
|
|
645
|
-
},
|
|
646
|
-
// binding start+6: uniform { intensity: f32 }
|
|
647
|
-
{
|
|
648
|
-
binding: start + 6,
|
|
649
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
650
|
-
buffer: { type: "uniform" }
|
|
651
|
-
}
|
|
652
|
-
];
|
|
653
|
-
case "lightmap":
|
|
654
|
-
return [
|
|
655
|
-
// emissive sampler + texture pair
|
|
656
|
-
{
|
|
657
|
-
binding: start,
|
|
658
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
659
|
-
sampler: { type: "filtering" }
|
|
660
|
-
},
|
|
661
|
-
{
|
|
662
|
-
binding: start + 1,
|
|
663
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
664
|
-
texture: { sampleType: "float", viewDimension: "2d" }
|
|
665
|
-
},
|
|
666
|
-
// occlusion sampler + texture pair
|
|
667
|
-
{
|
|
668
|
-
binding: start + 2,
|
|
669
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
670
|
-
sampler: { type: "filtering" }
|
|
671
|
-
},
|
|
672
|
-
{
|
|
673
|
-
binding: start + 3,
|
|
674
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
675
|
-
texture: { sampleType: "float", viewDimension: "2d" }
|
|
676
|
-
}
|
|
677
|
-
];
|
|
678
|
-
case "shadow":
|
|
679
|
-
return [
|
|
680
|
-
{
|
|
681
|
-
binding: start,
|
|
682
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
683
|
-
sampler: { type: "comparison" }
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
binding: start + 1,
|
|
687
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
688
|
-
texture: { sampleType: "depth", viewDimension: "2d" }
|
|
689
|
-
}
|
|
690
|
-
];
|
|
691
|
-
case "transmission":
|
|
692
|
-
return [
|
|
693
|
-
{
|
|
694
|
-
binding: start,
|
|
695
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
696
|
-
sampler: { type: "filtering" }
|
|
697
|
-
},
|
|
698
|
-
{
|
|
699
|
-
binding: start + 1,
|
|
700
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
701
|
-
texture: { sampleType: "float", viewDimension: "2d" }
|
|
702
|
-
}
|
|
703
|
-
];
|
|
195
|
+
function appendInjection(bgl, kind) {
|
|
196
|
+
const start = bgl.length;
|
|
197
|
+
switch (kind) {
|
|
198
|
+
case "ibl":
|
|
199
|
+
return [
|
|
200
|
+
// binding start+0: irradianceMap (texture_cube)
|
|
201
|
+
{
|
|
202
|
+
binding: start,
|
|
203
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
204
|
+
texture: { sampleType: "float", viewDimension: "cube" }
|
|
205
|
+
},
|
|
206
|
+
// binding start+1: irradianceSampler
|
|
207
|
+
{
|
|
208
|
+
binding: start + 1,
|
|
209
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
210
|
+
sampler: { type: "filtering" }
|
|
211
|
+
},
|
|
212
|
+
// binding start+2: prefilterMap (texture_cube)
|
|
213
|
+
{
|
|
214
|
+
binding: start + 2,
|
|
215
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
216
|
+
texture: { sampleType: "float", viewDimension: "cube" }
|
|
217
|
+
},
|
|
218
|
+
// binding start+3: prefilterSampler
|
|
219
|
+
{
|
|
220
|
+
binding: start + 3,
|
|
221
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
222
|
+
sampler: { type: "filtering" }
|
|
223
|
+
},
|
|
224
|
+
// binding start+4: brdfLut (texture_2d)
|
|
225
|
+
{
|
|
226
|
+
binding: start + 4,
|
|
227
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
228
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
229
|
+
},
|
|
230
|
+
// binding start+5: brdfLutSampler
|
|
231
|
+
{
|
|
232
|
+
binding: start + 5,
|
|
233
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
234
|
+
sampler: { type: "filtering" }
|
|
235
|
+
},
|
|
236
|
+
// binding start+6: uniform { intensity: f32 }
|
|
237
|
+
{
|
|
238
|
+
binding: start + 6,
|
|
239
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
240
|
+
buffer: { type: "uniform" }
|
|
241
|
+
}
|
|
242
|
+
];
|
|
243
|
+
case "lightmap":
|
|
244
|
+
return [
|
|
245
|
+
// emissive sampler + texture pair
|
|
246
|
+
{
|
|
247
|
+
binding: start,
|
|
248
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
249
|
+
sampler: { type: "filtering" }
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
binding: start + 1,
|
|
253
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
254
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
255
|
+
},
|
|
256
|
+
// occlusion sampler + texture pair
|
|
257
|
+
{
|
|
258
|
+
binding: start + 2,
|
|
259
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
260
|
+
sampler: { type: "filtering" }
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
binding: start + 3,
|
|
264
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
265
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
266
|
+
}
|
|
267
|
+
];
|
|
268
|
+
case "shadow":
|
|
269
|
+
return [
|
|
270
|
+
{
|
|
271
|
+
binding: start,
|
|
272
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
273
|
+
sampler: { type: "comparison" }
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
binding: start + 1,
|
|
277
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
278
|
+
texture: { sampleType: "depth", viewDimension: "2d" }
|
|
279
|
+
}
|
|
280
|
+
];
|
|
281
|
+
case "transmission":
|
|
282
|
+
return [
|
|
283
|
+
{
|
|
284
|
+
binding: start,
|
|
285
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
286
|
+
sampler: { type: "filtering" }
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
binding: start + 1,
|
|
290
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
291
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
292
|
+
}
|
|
293
|
+
];
|
|
704
294
|
}
|
|
705
295
|
}
|
|
706
296
|
function buildPbrViewBglEntries(caps) {
|
|
707
297
|
const lightBufType = caps.storageBuffer ? "read-only-storage" : "uniform";
|
|
708
|
-
|
|
298
|
+
const entries = [
|
|
709
299
|
{
|
|
710
300
|
binding: 0,
|
|
711
301
|
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
712
|
-
|
|
302
|
+
// View captures use the same bind group with a per-face view slot.
|
|
303
|
+
// Keeping this dynamic is also valid for the display slot at offset 0.
|
|
304
|
+
buffer: { type: "uniform", hasDynamicOffset: true }
|
|
713
305
|
},
|
|
714
306
|
{
|
|
715
307
|
binding: 1,
|
|
@@ -797,13 +389,28 @@ function buildPbrViewBglEntries(caps) {
|
|
|
797
389
|
binding: 8,
|
|
798
390
|
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
799
391
|
texture: { sampleType: "depth", viewDimension: "2d" }
|
|
800
|
-
},
|
|
801
|
-
{
|
|
802
|
-
binding: 10,
|
|
803
|
-
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
804
|
-
buffer: { type: "uniform", hasDynamicOffset: true }
|
|
805
392
|
}
|
|
806
393
|
];
|
|
394
|
+
if (caps.projectorAvailable !== false) {
|
|
395
|
+
entries.push(
|
|
396
|
+
{
|
|
397
|
+
binding: 11,
|
|
398
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
399
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
binding: 12,
|
|
403
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
404
|
+
sampler: { type: "filtering" }
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
entries.push({
|
|
409
|
+
binding: 10,
|
|
410
|
+
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
411
|
+
buffer: { type: "uniform", hasDynamicOffset: true }
|
|
412
|
+
});
|
|
413
|
+
return entries;
|
|
807
414
|
}
|
|
808
415
|
function buildPbrPipelineLayouts(device, caps) {
|
|
809
416
|
const viewBglRes = device.createBindGroupLayout(
|
|
@@ -849,8 +456,17 @@ function shadowCasterVariantSet(storageBuffer, skinned) {
|
|
|
849
456
|
if (storageBuffer && skinningDisabled) return "";
|
|
850
457
|
return `SKINNING_DISABLED=${String(skinningDisabled)}+STORAGE_BUFFER_AVAILABLE=${String(storageBuffer)}`;
|
|
851
458
|
}
|
|
852
|
-
function
|
|
853
|
-
|
|
459
|
+
function resolvePipelineGroup2Contract(source) {
|
|
460
|
+
const withoutComments = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/.*$/gmu, "");
|
|
461
|
+
const bindings = [
|
|
462
|
+
...withoutComments.matchAll(/@group\s*\(\s*2\s*\)\s*@binding\s*\(\s*(\d+)\s*\)/gu)
|
|
463
|
+
].map((match) => Number(match[1]));
|
|
464
|
+
const hasSkinBindings = bindings.includes(1) || bindings.includes(2);
|
|
465
|
+
const hasClusterBindings = bindings.some((binding) => binding >= 3);
|
|
466
|
+
if (hasSkinBindings && hasClusterBindings) return "skin-cluster";
|
|
467
|
+
if (hasSkinBindings) return "skin";
|
|
468
|
+
if (hasClusterBindings) return "cluster";
|
|
469
|
+
return "mesh";
|
|
854
470
|
}
|
|
855
471
|
function isStandardPbrMaterialShader(shaderId) {
|
|
856
472
|
return shaderId === "forgeax::default-standard-pbr" || shaderId === SKIN_MATERIAL_SHADER_ID || shaderId === "forgeax::default-standard-pbr-skin";
|
|
@@ -883,16 +499,43 @@ function buildPbrSkinLayouts(device, caps, pbr) {
|
|
|
883
499
|
bindGroupLayouts: layouts
|
|
884
500
|
};
|
|
885
501
|
}
|
|
502
|
+
function createPbrSkinMeshBindGroupEntries(meshBuffer, meshSize, paletteBuffer, paletteWindowBytes) {
|
|
503
|
+
return [
|
|
504
|
+
{
|
|
505
|
+
binding: 0,
|
|
506
|
+
resource: { kind: "buffer", value: { buffer: meshBuffer, offset: 0, size: meshSize } }
|
|
507
|
+
},
|
|
508
|
+
...[1, 2].map((binding) => ({
|
|
509
|
+
binding,
|
|
510
|
+
resource: {
|
|
511
|
+
kind: "buffer",
|
|
512
|
+
value: {
|
|
513
|
+
buffer: paletteBuffer,
|
|
514
|
+
offset: 0,
|
|
515
|
+
size: paletteWindowBytes
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}))
|
|
519
|
+
];
|
|
520
|
+
}
|
|
521
|
+
function pbrSkinMeshDynamicOffsets(meshOffset, paletteOffset) {
|
|
522
|
+
return [meshOffset, paletteOffset, paletteOffset];
|
|
523
|
+
}
|
|
886
524
|
var SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET = "";
|
|
887
|
-
function createHdrpBindGroupLayoutDescriptor(
|
|
888
|
-
const meshBufType =
|
|
889
|
-
const clusterBufType =
|
|
525
|
+
function createHdrpBindGroupLayoutDescriptor() {
|
|
526
|
+
const meshBufType = "read-only-storage";
|
|
527
|
+
const clusterBufType = "read-only-storage";
|
|
890
528
|
return {
|
|
891
529
|
label: "hdrp-unified-bgl-group2",
|
|
892
530
|
entries: [
|
|
893
531
|
{
|
|
894
532
|
binding: 0,
|
|
895
|
-
|
|
533
|
+
// Standard fragment lighting (transmission/refraction and clustered
|
|
534
|
+
// evaluation) reads the same mesh transform window as the vertex
|
|
535
|
+
// stage. Keep this visibility in the HDRP descriptor aligned with
|
|
536
|
+
// the URP mesh-array descriptor; restricting it to vertex makes the
|
|
537
|
+
// HDRP pipeline invalid when fs_main accesses `meshes[0]`.
|
|
538
|
+
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
896
539
|
buffer: { type: meshBufType, hasDynamicOffset: true }
|
|
897
540
|
},
|
|
898
541
|
// Bindings 1 and 2 stay absent for the URP physical isolation gap.
|
|
@@ -929,6 +572,31 @@ function createHdrpBindGroupLayoutDescriptor(storageBuffer = true) {
|
|
|
929
572
|
]
|
|
930
573
|
};
|
|
931
574
|
}
|
|
575
|
+
function createHdrpSkinBindGroupLayoutDescriptor() {
|
|
576
|
+
const base = createHdrpBindGroupLayoutDescriptor();
|
|
577
|
+
const baseEntries = base.entries;
|
|
578
|
+
if (baseEntries === void 0 || baseEntries[0] === void 0) {
|
|
579
|
+
throw new Error("HDRP base group(2) layout must declare mesh binding 0");
|
|
580
|
+
}
|
|
581
|
+
const paletteType = "read-only-storage";
|
|
582
|
+
return {
|
|
583
|
+
label: "hdrp-skin-unified-bgl-group2",
|
|
584
|
+
entries: [
|
|
585
|
+
baseEntries[0],
|
|
586
|
+
{
|
|
587
|
+
binding: 1,
|
|
588
|
+
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
589
|
+
buffer: { type: paletteType, hasDynamicOffset: true }
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
binding: 2,
|
|
593
|
+
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
594
|
+
buffer: { type: paletteType, hasDynamicOffset: true }
|
|
595
|
+
},
|
|
596
|
+
...baseEntries.slice(1)
|
|
597
|
+
]
|
|
598
|
+
};
|
|
599
|
+
}
|
|
932
600
|
function resolveMaterialParamSchema(spec, options) {
|
|
933
601
|
if (options.materialParamSchema !== void 0) return options.materialParamSchema;
|
|
934
602
|
if (options.registry !== void 0) {
|
|
@@ -993,6 +661,11 @@ function buildBindGroupLayoutDescriptor(spec, options) {
|
|
|
993
661
|
binding: 1,
|
|
994
662
|
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
995
663
|
buffer: { type: meshBufType, hasDynamicOffset: true }
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
binding: 2,
|
|
667
|
+
visibility: GPU_SHADER_STAGE_VERTEX,
|
|
668
|
+
buffer: { type: meshBufType, hasDynamicOffset: true }
|
|
996
669
|
}
|
|
997
670
|
]
|
|
998
671
|
};
|
|
@@ -1015,8 +688,7 @@ function buildBindGroupLayoutDescriptor(spec, options) {
|
|
|
1015
688
|
};
|
|
1016
689
|
}
|
|
1017
690
|
case "hdrp-7-slot": {
|
|
1018
|
-
const
|
|
1019
|
-
const desc = createHdrpBindGroupLayoutDescriptor(caps.storageBuffer);
|
|
691
|
+
const desc = createHdrpBindGroupLayoutDescriptor();
|
|
1020
692
|
return {
|
|
1021
693
|
label: desc.label ?? "hdrp-unified-bgl-group2",
|
|
1022
694
|
entries: [...desc.entries ?? []]
|
|
@@ -1107,8 +779,6 @@ function buildFullscreenPostInputEntries(spec) {
|
|
|
1107
779
|
}
|
|
1108
780
|
];
|
|
1109
781
|
}
|
|
1110
|
-
|
|
1111
|
-
// src/pipeline-spec.ts
|
|
1112
782
|
var DEFERRED_COLOR_FORMATS = [
|
|
1113
783
|
"rgba16float",
|
|
1114
784
|
"rgba8unorm",
|
|
@@ -1206,6 +876,24 @@ function variantSetFromVertexLayoutProjection(projection, authoredVariantSet) {
|
|
|
1206
876
|
parts.push(axis);
|
|
1207
877
|
return ok(parts.join("+"));
|
|
1208
878
|
}
|
|
879
|
+
function standardCapabilityVariantSet(clustered, storageBuffer, vertexColorAvailable) {
|
|
880
|
+
if (clustered && storageBuffer && vertexColorAvailable) return "";
|
|
881
|
+
return `CLUSTER_FORWARD_AVAILABLE=${clustered}+STORAGE_BUFFER_AVAILABLE=${storageBuffer}`;
|
|
882
|
+
}
|
|
883
|
+
function standardTopologyVariantSet(topology, storageBuffer, vertexColorAvailable) {
|
|
884
|
+
return standardCapabilityVariantSet(
|
|
885
|
+
topology?.kind === "clustered",
|
|
886
|
+
storageBuffer,
|
|
887
|
+
vertexColorAvailable
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
function standardTopologyBindGroupReady(topology, clusterBindGroup) {
|
|
891
|
+
return topology?.kind !== "clustered" || clusterBindGroup !== null && clusterBindGroup !== void 0;
|
|
892
|
+
}
|
|
893
|
+
function standardStorageVariantSet(storageBuffer, vertexColorAvailable) {
|
|
894
|
+
if (storageBuffer && vertexColorAvailable) return "";
|
|
895
|
+
return `STORAGE_BUFFER_AVAILABLE=${storageBuffer}+VERTEX_COLOR_AVAILABLE=${vertexColorAvailable}`;
|
|
896
|
+
}
|
|
1209
897
|
function buildPipelineDescriptor(spec, modules) {
|
|
1210
898
|
const { attachments, geometry, renderState } = spec;
|
|
1211
899
|
const vertexBuffers = geometry.vertexLayoutProjection === void 0 ? deriveVertexBufferLayout(geometry.vertexLayout, {
|
|
@@ -1528,7 +1216,7 @@ var TRI_GEOMETRY_PBR = {
|
|
|
1528
1216
|
vertexLayout: PROCEDURAL_ATTR_LAYOUT,
|
|
1529
1217
|
shaderUvSetCount: 8
|
|
1530
1218
|
};
|
|
1531
|
-
var
|
|
1219
|
+
var STANDARD_BOOT_VARIANT_SET = standardCapabilityVariantSet(false, true, false);
|
|
1532
1220
|
function buildSpecConstTable(ldrViewFormat) {
|
|
1533
1221
|
const TRI_ATTACHMENTS_LDR_S1 = {
|
|
1534
1222
|
colorFormats: [ldrViewFormat],
|
|
@@ -1607,19 +1295,19 @@ function buildSpecConstTable(ldrViewFormat) {
|
|
|
1607
1295
|
geometry: TRI_GEOMETRY_PBR,
|
|
1608
1296
|
renderState: void 0
|
|
1609
1297
|
},
|
|
1610
|
-
// ── M6 fix-up:
|
|
1611
|
-
//
|
|
1612
|
-
// `variantSet=
|
|
1298
|
+
// ── M6 fix-up: non-clustered standard-pbr prewarm ─────────────────────────
|
|
1299
|
+
// The default record path (`getMaterialShaderPipeline`) requests
|
|
1300
|
+
// `variantSet=STANDARD_BOOT_VARIANT_SET` for standard-shading entities; pre-M6
|
|
1613
1301
|
// the silent `selectStandardFallbackPipeline` shim served the no-variant
|
|
1614
1302
|
// boot prewarm during the 1-frame async-compile warmup. With M6's
|
|
1615
|
-
// explicit-failure surface, those
|
|
1303
|
+
// explicit-failure surface, those requests must hit the boot prewarm
|
|
1616
1304
|
// by their own variantSet — these 4 entries close that gap.
|
|
1617
1305
|
// standard PBR URP LDR S1
|
|
1618
1306
|
{
|
|
1619
1307
|
shader: {
|
|
1620
1308
|
id: "forgeax::default-standard-pbr",
|
|
1621
1309
|
passKind: "forward",
|
|
1622
|
-
variantSet:
|
|
1310
|
+
variantSet: STANDARD_BOOT_VARIANT_SET
|
|
1623
1311
|
},
|
|
1624
1312
|
attachments: TRI_ATTACHMENTS_LDR_S1,
|
|
1625
1313
|
geometry: TRI_GEOMETRY_PBR,
|
|
@@ -1630,7 +1318,7 @@ function buildSpecConstTable(ldrViewFormat) {
|
|
|
1630
1318
|
shader: {
|
|
1631
1319
|
id: "forgeax::default-standard-pbr",
|
|
1632
1320
|
passKind: "forward",
|
|
1633
|
-
variantSet:
|
|
1321
|
+
variantSet: STANDARD_BOOT_VARIANT_SET
|
|
1634
1322
|
},
|
|
1635
1323
|
attachments: TRI_ATTACHMENTS_LDR_S4,
|
|
1636
1324
|
geometry: TRI_GEOMETRY_PBR,
|
|
@@ -1641,7 +1329,7 @@ function buildSpecConstTable(ldrViewFormat) {
|
|
|
1641
1329
|
shader: {
|
|
1642
1330
|
id: "forgeax::default-standard-pbr",
|
|
1643
1331
|
passKind: "forward",
|
|
1644
|
-
variantSet:
|
|
1332
|
+
variantSet: STANDARD_BOOT_VARIANT_SET
|
|
1645
1333
|
},
|
|
1646
1334
|
attachments: TRI_ATTACHMENTS_HDR_S1,
|
|
1647
1335
|
geometry: TRI_GEOMETRY_PBR,
|
|
@@ -1652,7 +1340,7 @@ function buildSpecConstTable(ldrViewFormat) {
|
|
|
1652
1340
|
shader: {
|
|
1653
1341
|
id: "forgeax::default-standard-pbr",
|
|
1654
1342
|
passKind: "forward",
|
|
1655
|
-
variantSet:
|
|
1343
|
+
variantSet: STANDARD_BOOT_VARIANT_SET
|
|
1656
1344
|
},
|
|
1657
1345
|
attachments: TRI_ATTACHMENTS_HDR_S4,
|
|
1658
1346
|
geometry: TRI_GEOMETRY_PBR,
|
|
@@ -2078,7 +1766,8 @@ var UNIFORM_BYTES = 256;
|
|
|
2078
1766
|
var fallbackCache = /* @__PURE__ */ new WeakMap();
|
|
2079
1767
|
function getOrCreateSsaoFallbackTexture(runtime) {
|
|
2080
1768
|
const cached = fallbackCache.get(runtime);
|
|
2081
|
-
if (cached !== void 0) return cached;
|
|
1769
|
+
if (cached !== void 0 && cached.device === runtime.device) return cached;
|
|
1770
|
+
if (cached !== void 0) fallbackCache.delete(runtime);
|
|
2082
1771
|
const device = runtime.device;
|
|
2083
1772
|
const texRes = device.createTexture({
|
|
2084
1773
|
label: "hdrp-ssao-fallback-white",
|
|
@@ -2137,6 +1826,7 @@ function getOrCreateSsaoFallbackTexture(runtime) {
|
|
|
2137
1826
|
return null;
|
|
2138
1827
|
}
|
|
2139
1828
|
const resources = {
|
|
1829
|
+
device,
|
|
2140
1830
|
texture: texRes.value,
|
|
2141
1831
|
view: viewRes.value,
|
|
2142
1832
|
sampler: samplerRes.value
|
|
@@ -2147,7 +1837,8 @@ function getOrCreateSsaoFallbackTexture(runtime) {
|
|
|
2147
1837
|
var cache = /* @__PURE__ */ new WeakMap();
|
|
2148
1838
|
function getOrCreateSsaoBuffers(runtime) {
|
|
2149
1839
|
const cached = cache.get(runtime);
|
|
2150
|
-
if (cached !== void 0) return cached;
|
|
1840
|
+
if (cached !== void 0 && cached.device === runtime.device) return cached;
|
|
1841
|
+
if (cached !== void 0) cache.delete(runtime);
|
|
2151
1842
|
const device = runtime.device;
|
|
2152
1843
|
const kernelSamples = generateSsaoKernel();
|
|
2153
1844
|
const kernelData = new Float32Array(SSAO_KERNEL_SAMPLE_COUNT * 4);
|
|
@@ -2215,391 +1906,794 @@ function getOrCreateSsaoBuffers(runtime) {
|
|
|
2215
1906
|
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
2216
1907
|
mappedAtCreation: false
|
|
2217
1908
|
});
|
|
2218
|
-
if (!uniformBufRes.ok) {
|
|
2219
|
-
runtime.errorRegistry.fire(uniformBufRes.error);
|
|
1909
|
+
if (!uniformBufRes.ok) {
|
|
1910
|
+
runtime.errorRegistry.fire(uniformBufRes.error);
|
|
1911
|
+
return null;
|
|
1912
|
+
}
|
|
1913
|
+
const buffers = {
|
|
1914
|
+
device,
|
|
1915
|
+
kernelBuffer: kernelBufferRes.value,
|
|
1916
|
+
kernelBytes: SSAO_KERNEL_SAMPLE_COUNT * BYTES_PER_KERNEL_ELEMENT,
|
|
1917
|
+
noiseTexture: noiseTexRes.value,
|
|
1918
|
+
uniformBuffer: uniformBufRes.value,
|
|
1919
|
+
uniformBytes: UNIFORM_BYTES
|
|
1920
|
+
};
|
|
1921
|
+
cache.set(runtime, buffers);
|
|
1922
|
+
return buffers;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
// src/hdrp-buffers.ts
|
|
1926
|
+
function createHdrpClusterMembershipBindGroupLayoutDescriptor() {
|
|
1927
|
+
return {
|
|
1928
|
+
label: "hdrp-cluster-membership-bgl",
|
|
1929
|
+
entries: [
|
|
1930
|
+
{
|
|
1931
|
+
binding: 0,
|
|
1932
|
+
visibility: GPU_SHADER_STAGE_COMPUTE,
|
|
1933
|
+
buffer: { type: "read-only-storage", hasDynamicOffset: false }
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
binding: 1,
|
|
1937
|
+
visibility: GPU_SHADER_STAGE_COMPUTE,
|
|
1938
|
+
buffer: { type: "storage", hasDynamicOffset: false }
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
binding: 2,
|
|
1942
|
+
visibility: GPU_SHADER_STAGE_COMPUTE,
|
|
1943
|
+
buffer: { type: "uniform", hasDynamicOffset: false }
|
|
1944
|
+
},
|
|
1945
|
+
{
|
|
1946
|
+
binding: 3,
|
|
1947
|
+
visibility: GPU_SHADER_STAGE_COMPUTE,
|
|
1948
|
+
buffer: { type: "read-only-storage", hasDynamicOffset: false }
|
|
1949
|
+
}
|
|
1950
|
+
]
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
var cache2 = /* @__PURE__ */ new WeakMap();
|
|
1954
|
+
function getOrCreateHdrpBuffers(runtime, grid = DEFAULT_CLUSTER_GRID) {
|
|
1955
|
+
if (runtime.device.caps?.storageBuffer !== true) return null;
|
|
1956
|
+
const deviceSurface = runtime.device;
|
|
1957
|
+
if (typeof deviceSurface.createBuffer !== "function" || typeof deviceSurface.createBindGroupLayout !== "function" || typeof deviceSurface.destroyBuffer !== "function") {
|
|
1958
|
+
return null;
|
|
1959
|
+
}
|
|
1960
|
+
const cached = cache2.get(runtime);
|
|
1961
|
+
if (cached !== void 0 && cached.device === runtime.device) {
|
|
1962
|
+
const requestedCells = grid.x * grid.y * grid.z;
|
|
1963
|
+
const cachedCells = cached.grid.x * cached.grid.y * cached.grid.z;
|
|
1964
|
+
if (requestedCells <= cachedCells) return cached;
|
|
1965
|
+
}
|
|
1966
|
+
const device = runtime.device;
|
|
1967
|
+
const clusterUniformBytes = 32;
|
|
1968
|
+
const lightDataBytes = MAX_LIGHTS * BYTES_PER_LIGHT_SLOT;
|
|
1969
|
+
const clusterCells = grid.x * grid.y * grid.z;
|
|
1970
|
+
const clusterGridBytes = clusterCells * CLUSTER_GRID_STRIDE_U32 * 4;
|
|
1971
|
+
const lightIndexListBytes = LIGHT_INDEX_LIST_CAPACITY * 4;
|
|
1972
|
+
const lightBoundsBytes = MAX_LIGHTS * 6 * 4;
|
|
1973
|
+
const created = [];
|
|
1974
|
+
const releaseCreated = () => {
|
|
1975
|
+
for (const buffer of created) {
|
|
1976
|
+
const result = device.destroyBuffer(buffer);
|
|
1977
|
+
if (!result.ok) runtime.errorRegistry.fire(result.error);
|
|
1978
|
+
}
|
|
1979
|
+
created.length = 0;
|
|
1980
|
+
};
|
|
1981
|
+
const failAllocation = (error) => {
|
|
1982
|
+
runtime.errorRegistry.fire(error);
|
|
1983
|
+
releaseCreated();
|
|
1984
|
+
return null;
|
|
1985
|
+
};
|
|
1986
|
+
const lightData = device.createBuffer({
|
|
1987
|
+
label: "hdrp-light-data",
|
|
1988
|
+
size: lightDataBytes,
|
|
1989
|
+
usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST,
|
|
1990
|
+
mappedAtCreation: false
|
|
1991
|
+
});
|
|
1992
|
+
if (!lightData.ok) {
|
|
1993
|
+
return failAllocation(lightData.error);
|
|
1994
|
+
}
|
|
1995
|
+
created.push(lightData.value);
|
|
1996
|
+
const clusterGrid = device.createBuffer({
|
|
1997
|
+
label: "hdrp-cluster-grid",
|
|
1998
|
+
size: clusterGridBytes,
|
|
1999
|
+
usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST,
|
|
2000
|
+
mappedAtCreation: false
|
|
2001
|
+
});
|
|
2002
|
+
if (!clusterGrid.ok) {
|
|
2003
|
+
return failAllocation(clusterGrid.error);
|
|
2004
|
+
}
|
|
2005
|
+
created.push(clusterGrid.value);
|
|
2006
|
+
const lightIndexList = device.createBuffer({
|
|
2007
|
+
label: "hdrp-light-index-list",
|
|
2008
|
+
size: lightIndexListBytes,
|
|
2009
|
+
usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST,
|
|
2010
|
+
mappedAtCreation: false
|
|
2011
|
+
});
|
|
2012
|
+
if (!lightIndexList.ok) {
|
|
2013
|
+
return failAllocation(lightIndexList.error);
|
|
2014
|
+
}
|
|
2015
|
+
created.push(lightIndexList.value);
|
|
2016
|
+
const clusterUniform = device.createBuffer({
|
|
2017
|
+
label: "hdrp-cluster-uniform",
|
|
2018
|
+
size: clusterUniformBytes,
|
|
2019
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
2020
|
+
mappedAtCreation: false
|
|
2021
|
+
});
|
|
2022
|
+
if (!clusterUniform.ok) {
|
|
2023
|
+
return failAllocation(clusterUniform.error);
|
|
2024
|
+
}
|
|
2025
|
+
created.push(clusterUniform.value);
|
|
2026
|
+
const lightBounds = device.createBuffer({
|
|
2027
|
+
label: "hdrp-light-bounds",
|
|
2028
|
+
size: lightBoundsBytes,
|
|
2029
|
+
usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST,
|
|
2030
|
+
mappedAtCreation: false
|
|
2031
|
+
});
|
|
2032
|
+
if (!lightBounds.ok) {
|
|
2033
|
+
return failAllocation(lightBounds.error);
|
|
2034
|
+
}
|
|
2035
|
+
created.push(lightBounds.value);
|
|
2036
|
+
const unifiedBglRes = device.createBindGroupLayout(createHdrpBindGroupLayoutDescriptor());
|
|
2037
|
+
if (!unifiedBglRes.ok) {
|
|
2038
|
+
return failAllocation(unifiedBglRes.error);
|
|
2039
|
+
}
|
|
2040
|
+
const buffers = {
|
|
2041
|
+
device,
|
|
2042
|
+
lightDataBuffer: lightData.value,
|
|
2043
|
+
lightDataBytes,
|
|
2044
|
+
clusterGridBuffer: clusterGrid.value,
|
|
2045
|
+
clusterGridBytes,
|
|
2046
|
+
lightIndexListBuffer: lightIndexList.value,
|
|
2047
|
+
lightIndexListBytes,
|
|
2048
|
+
clusterUniformBuffer: clusterUniform.value,
|
|
2049
|
+
clusterUniformBytes,
|
|
2050
|
+
lightBoundsBuffer: lightBounds.value,
|
|
2051
|
+
lightBoundsBytes,
|
|
2052
|
+
grid: { x: grid.x, y: grid.y, z: grid.z },
|
|
2053
|
+
unifiedBindGroupLayout: unifiedBglRes.value
|
|
2054
|
+
};
|
|
2055
|
+
cache2.set(runtime, buffers);
|
|
2056
|
+
if (cached !== void 0) {
|
|
2057
|
+
retireHdrpBuffers(runtime, cached);
|
|
2058
|
+
}
|
|
2059
|
+
return buffers;
|
|
2060
|
+
}
|
|
2061
|
+
function resetHdrpBuffers(runtime) {
|
|
2062
|
+
const cached = cache2.get(runtime);
|
|
2063
|
+
if (cached === void 0) return;
|
|
2064
|
+
cache2.delete(runtime);
|
|
2065
|
+
retireHdrpBuffers(runtime, cached);
|
|
2066
|
+
}
|
|
2067
|
+
function retireHdrpBuffers(runtime, buffers) {
|
|
2068
|
+
const device = buffers.device;
|
|
2069
|
+
const destroy = () => {
|
|
2070
|
+
for (const buffer of [
|
|
2071
|
+
buffers.lightDataBuffer,
|
|
2072
|
+
buffers.clusterGridBuffer,
|
|
2073
|
+
buffers.lightIndexListBuffer,
|
|
2074
|
+
buffers.clusterUniformBuffer,
|
|
2075
|
+
buffers.lightBoundsBuffer
|
|
2076
|
+
]) {
|
|
2077
|
+
const result = device.destroyBuffer(buffer);
|
|
2078
|
+
if (!result.ok) runtime.errorRegistry.fire(result.error);
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
const queue = device.queue;
|
|
2082
|
+
if (typeof queue.onSubmittedWorkDone === "function") {
|
|
2083
|
+
queue.onSubmittedWorkDone().then(destroy, destroy);
|
|
2084
|
+
} else {
|
|
2085
|
+
destroy();
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
function packClusterUniform(grid, near, far, ssaoIntensity = 0, lightCount = 0) {
|
|
2089
|
+
const buf = new ArrayBuffer(32);
|
|
2090
|
+
const u32 = new Uint32Array(buf);
|
|
2091
|
+
const f32 = new Float32Array(buf);
|
|
2092
|
+
u32[0] = grid.x >>> 0;
|
|
2093
|
+
u32[1] = grid.y >>> 0;
|
|
2094
|
+
u32[2] = grid.z >>> 0;
|
|
2095
|
+
u32[3] = Math.min(Math.max(lightCount, 0), MAX_LIGHTS) >>> 0;
|
|
2096
|
+
f32[4] = near;
|
|
2097
|
+
f32[5] = far;
|
|
2098
|
+
f32[6] = near > 0 && far > near ? Math.log(far / near) : 0;
|
|
2099
|
+
f32[7] = ssaoIntensity;
|
|
2100
|
+
return buf;
|
|
2101
|
+
}
|
|
2102
|
+
function createHdrpUnifiedBindGroup(runtime, hdrpBuffers, meshStorageBuffer, ssaoOptions = { enabled: false }) {
|
|
2103
|
+
const fallback = getOrCreateSsaoFallbackTexture(runtime);
|
|
2104
|
+
if (fallback === null) return null;
|
|
2105
|
+
const ssaoTexView = ssaoOptions.enabled ? ssaoOptions.ssaoBlurredView : fallback.view;
|
|
2106
|
+
const ssaoSampler = fallback.sampler;
|
|
2107
|
+
const result = runtime.device.createBindGroup({
|
|
2108
|
+
label: "hdrp-unified-bg-group2",
|
|
2109
|
+
layout: hdrpBuffers.unifiedBindGroupLayout,
|
|
2110
|
+
entries: [
|
|
2111
|
+
{
|
|
2112
|
+
binding: 0,
|
|
2113
|
+
resource: {
|
|
2114
|
+
kind: "buffer",
|
|
2115
|
+
value: {
|
|
2116
|
+
buffer: meshStorageBuffer,
|
|
2117
|
+
offset: 0,
|
|
2118
|
+
size: MESH_PER_ENTITY_STRIDE
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
binding: 3,
|
|
2124
|
+
resource: {
|
|
2125
|
+
kind: "buffer",
|
|
2126
|
+
value: { buffer: hdrpBuffers.lightDataBuffer }
|
|
2127
|
+
}
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
binding: 4,
|
|
2131
|
+
resource: {
|
|
2132
|
+
kind: "buffer",
|
|
2133
|
+
value: { buffer: hdrpBuffers.clusterGridBuffer }
|
|
2134
|
+
}
|
|
2135
|
+
},
|
|
2136
|
+
{
|
|
2137
|
+
binding: 5,
|
|
2138
|
+
resource: {
|
|
2139
|
+
kind: "buffer",
|
|
2140
|
+
value: { buffer: hdrpBuffers.lightIndexListBuffer }
|
|
2141
|
+
}
|
|
2142
|
+
},
|
|
2143
|
+
{
|
|
2144
|
+
binding: 6,
|
|
2145
|
+
resource: {
|
|
2146
|
+
kind: "buffer",
|
|
2147
|
+
value: { buffer: hdrpBuffers.clusterUniformBuffer }
|
|
2148
|
+
}
|
|
2149
|
+
},
|
|
2150
|
+
{
|
|
2151
|
+
binding: 7,
|
|
2152
|
+
resource: { kind: "textureView", value: ssaoTexView }
|
|
2153
|
+
},
|
|
2154
|
+
{
|
|
2155
|
+
binding: 8,
|
|
2156
|
+
resource: { kind: "sampler", value: ssaoSampler }
|
|
2157
|
+
}
|
|
2158
|
+
]
|
|
2159
|
+
});
|
|
2160
|
+
if (!result.ok) {
|
|
2161
|
+
runtime.errorRegistry.fire(result.error);
|
|
2162
|
+
return null;
|
|
2163
|
+
}
|
|
2164
|
+
return result.value;
|
|
2165
|
+
}
|
|
2166
|
+
function createHdrpSkinUnifiedBindGroup(runtime, hdrpBuffers, layout, meshStorageBuffer, paletteBuffer, paletteBindingWindowBytes, ssaoOptions = { enabled: false }) {
|
|
2167
|
+
const fallback = getOrCreateSsaoFallbackTexture(runtime);
|
|
2168
|
+
if (fallback === null) return null;
|
|
2169
|
+
const meshSize = MESH_PER_ENTITY_STRIDE;
|
|
2170
|
+
const result = runtime.device.createBindGroup({
|
|
2171
|
+
label: "hdrp-skin-unified-bg-group2",
|
|
2172
|
+
layout,
|
|
2173
|
+
entries: [
|
|
2174
|
+
...createPbrSkinMeshBindGroupEntries(
|
|
2175
|
+
meshStorageBuffer,
|
|
2176
|
+
meshSize,
|
|
2177
|
+
paletteBuffer,
|
|
2178
|
+
paletteBindingWindowBytes
|
|
2179
|
+
),
|
|
2180
|
+
{ binding: 3, resource: { kind: "buffer", value: { buffer: hdrpBuffers.lightDataBuffer } } },
|
|
2181
|
+
{
|
|
2182
|
+
binding: 4,
|
|
2183
|
+
resource: { kind: "buffer", value: { buffer: hdrpBuffers.clusterGridBuffer } }
|
|
2184
|
+
},
|
|
2185
|
+
{
|
|
2186
|
+
binding: 5,
|
|
2187
|
+
resource: { kind: "buffer", value: { buffer: hdrpBuffers.lightIndexListBuffer } }
|
|
2188
|
+
},
|
|
2189
|
+
{
|
|
2190
|
+
binding: 6,
|
|
2191
|
+
resource: { kind: "buffer", value: { buffer: hdrpBuffers.clusterUniformBuffer } }
|
|
2192
|
+
},
|
|
2193
|
+
{
|
|
2194
|
+
binding: 7,
|
|
2195
|
+
resource: {
|
|
2196
|
+
kind: "textureView",
|
|
2197
|
+
value: ssaoOptions.enabled ? ssaoOptions.ssaoBlurredView : fallback.view
|
|
2198
|
+
}
|
|
2199
|
+
},
|
|
2200
|
+
{ binding: 8, resource: { kind: "sampler", value: fallback.sampler } }
|
|
2201
|
+
]
|
|
2202
|
+
});
|
|
2203
|
+
if (!result.ok) {
|
|
2204
|
+
runtime.errorRegistry.fire(result.error);
|
|
2220
2205
|
return null;
|
|
2221
2206
|
}
|
|
2222
|
-
|
|
2223
|
-
kernelBuffer: kernelBufferRes.value,
|
|
2224
|
-
kernelBytes: SSAO_KERNEL_SAMPLE_COUNT * BYTES_PER_KERNEL_ELEMENT,
|
|
2225
|
-
noiseTexture: noiseTexRes.value,
|
|
2226
|
-
uniformBuffer: uniformBufRes.value,
|
|
2227
|
-
uniformBytes: UNIFORM_BYTES
|
|
2228
|
-
};
|
|
2229
|
-
cache.set(runtime, buffers);
|
|
2230
|
-
return buffers;
|
|
2207
|
+
return result.value;
|
|
2231
2208
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
return {
|
|
2237
|
-
label: "hdrp-cluster-membership-bgl",
|
|
2209
|
+
function createHdrpClusterMembershipBindGroup(runtime, hdrpBuffers, layout) {
|
|
2210
|
+
const result = runtime.device.createBindGroup({
|
|
2211
|
+
label: "hdrp-cluster-membership-bg",
|
|
2212
|
+
layout,
|
|
2238
2213
|
entries: [
|
|
2239
2214
|
{
|
|
2240
2215
|
binding: 0,
|
|
2241
|
-
|
|
2242
|
-
buffer: { type: "read-only-storage", hasDynamicOffset: false }
|
|
2216
|
+
resource: { kind: "buffer", value: { buffer: hdrpBuffers.clusterGridBuffer } }
|
|
2243
2217
|
},
|
|
2244
2218
|
{
|
|
2245
2219
|
binding: 1,
|
|
2246
|
-
|
|
2247
|
-
buffer: { type: "storage", hasDynamicOffset: false }
|
|
2220
|
+
resource: { kind: "buffer", value: { buffer: hdrpBuffers.lightIndexListBuffer } }
|
|
2248
2221
|
},
|
|
2249
2222
|
{
|
|
2250
2223
|
binding: 2,
|
|
2251
|
-
|
|
2252
|
-
buffer: { type: "uniform", hasDynamicOffset: false }
|
|
2224
|
+
resource: { kind: "buffer", value: { buffer: hdrpBuffers.clusterUniformBuffer } }
|
|
2253
2225
|
},
|
|
2254
2226
|
{
|
|
2255
2227
|
binding: 3,
|
|
2256
|
-
|
|
2257
|
-
buffer: { type: "read-only-storage", hasDynamicOffset: false }
|
|
2228
|
+
resource: { kind: "buffer", value: { buffer: hdrpBuffers.lightBoundsBuffer } }
|
|
2258
2229
|
}
|
|
2259
2230
|
]
|
|
2260
|
-
};
|
|
2261
|
-
}
|
|
2262
|
-
var cache2 = /* @__PURE__ */ new WeakMap();
|
|
2263
|
-
function getOrCreateHdrpBuffers(runtime, grid = DEFAULT_CLUSTER_GRID) {
|
|
2264
|
-
const cached = cache2.get(runtime);
|
|
2265
|
-
if (cached !== void 0) return cached;
|
|
2266
|
-
const device = runtime.device;
|
|
2267
|
-
const storageBuffer = runtime.device.caps?.storageBuffer === true;
|
|
2268
|
-
const clusterUniformBytes = 32;
|
|
2269
|
-
const lightDataBytes = (storageBuffer ? MAX_LIGHTS : HDRP_UNIFORM_LIGHT_CAPACITY) * BYTES_PER_LIGHT_SLOT;
|
|
2270
|
-
const clusterCells = grid.x * grid.y * grid.z;
|
|
2271
|
-
const clusterGridBytes = storageBuffer ? clusterCells * CLUSTER_GRID_STRIDE_U32 * 4 : clusterUniformBytes;
|
|
2272
|
-
const lightIndexListBytes = storageBuffer ? LIGHT_INDEX_LIST_CAPACITY * 4 : clusterUniformBytes;
|
|
2273
|
-
const lightBoundsBytes = MAX_LIGHTS * 6 * 4;
|
|
2274
|
-
const lightData = device.createBuffer({
|
|
2275
|
-
label: "hdrp-light-data",
|
|
2276
|
-
size: lightDataBytes,
|
|
2277
|
-
usage: (storageBuffer ? GPU_BUFFER_USAGE_STORAGE : GPU_BUFFER_USAGE_UNIFORM) | GPU_BUFFER_USAGE_COPY_DST,
|
|
2278
|
-
mappedAtCreation: false
|
|
2279
2231
|
});
|
|
2280
|
-
if (!
|
|
2281
|
-
runtime.errorRegistry.fire(
|
|
2232
|
+
if (!result.ok) {
|
|
2233
|
+
runtime.errorRegistry.fire(result.error);
|
|
2282
2234
|
return null;
|
|
2283
2235
|
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2236
|
+
return result.value;
|
|
2237
|
+
}
|
|
2238
|
+
function runClusterBinProfilePhase(runner, phase, action) {
|
|
2239
|
+
return action() ;
|
|
2240
|
+
}
|
|
2241
|
+
function createClusterBinScratch() {
|
|
2242
|
+
return {
|
|
2243
|
+
clusterCounts: new Uint32Array(0),
|
|
2244
|
+
clusterCursors: new Uint32Array(0),
|
|
2245
|
+
lightBounds: new Int32Array(0),
|
|
2246
|
+
clusterDeltas: new Int32Array(0),
|
|
2247
|
+
aabbOutput: {
|
|
2248
|
+
min: vec3.create(0, 0, 0),
|
|
2249
|
+
max: vec3.create(0, 0, 0)
|
|
2250
|
+
},
|
|
2251
|
+
boundsOutput: {
|
|
2252
|
+
min: { x: 0, y: 0, z: 0 },
|
|
2253
|
+
max: { x: 0, y: 0, z: 0 }
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
function ensureScratchCapacity(scratch, clusterCount, lightCount, gridX, gridY, gridZ) {
|
|
2258
|
+
if (scratch.clusterCounts.length < clusterCount) {
|
|
2259
|
+
scratch.clusterCounts = new Uint32Array(clusterCount);
|
|
2260
|
+
scratch.clusterCursors = new Uint32Array(clusterCount);
|
|
2293
2261
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
size: lightIndexListBytes,
|
|
2297
|
-
usage: (storageBuffer ? GPU_BUFFER_USAGE_STORAGE : GPU_BUFFER_USAGE_UNIFORM) | GPU_BUFFER_USAGE_COPY_DST,
|
|
2298
|
-
mappedAtCreation: false
|
|
2299
|
-
});
|
|
2300
|
-
if (!lightIndexList.ok) {
|
|
2301
|
-
runtime.errorRegistry.fire(lightIndexList.error);
|
|
2302
|
-
return null;
|
|
2262
|
+
if (scratch.lightBounds.length < lightCount * 6) {
|
|
2263
|
+
scratch.lightBounds = new Int32Array(lightCount * 6);
|
|
2303
2264
|
}
|
|
2304
|
-
const
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
2308
|
-
mappedAtCreation: false
|
|
2309
|
-
});
|
|
2310
|
-
if (!clusterUniform.ok) {
|
|
2311
|
-
runtime.errorRegistry.fire(clusterUniform.error);
|
|
2312
|
-
return null;
|
|
2265
|
+
const differenceVolumeSize = (gridX + 1) * (gridY + 1) * (gridZ + 1);
|
|
2266
|
+
if (scratch.clusterDeltas.length < differenceVolumeSize) {
|
|
2267
|
+
scratch.clusterDeltas = new Int32Array(differenceVolumeSize);
|
|
2313
2268
|
}
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
mappedAtCreation: false
|
|
2319
|
-
});
|
|
2320
|
-
if (!lightBounds.ok) {
|
|
2321
|
-
runtime.errorRegistry.fire(lightBounds.error);
|
|
2322
|
-
return null;
|
|
2269
|
+
}
|
|
2270
|
+
function deriveCullingRadius(range, intensity, threshold = 1e-3) {
|
|
2271
|
+
if (Number.isFinite(range)) {
|
|
2272
|
+
return Math.max(0, range);
|
|
2323
2273
|
}
|
|
2324
|
-
const
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
if (
|
|
2329
|
-
|
|
2330
|
-
|
|
2274
|
+
const derived = Math.sqrt(intensity / threshold);
|
|
2275
|
+
return Math.min(derived, 1e3);
|
|
2276
|
+
}
|
|
2277
|
+
function viewZToZSlice(viewZ, gridZ, near, far) {
|
|
2278
|
+
if (viewZ >= -near) {
|
|
2279
|
+
return 0;
|
|
2280
|
+
}
|
|
2281
|
+
const logFarOverNear = Math.log(far / near);
|
|
2282
|
+
const slice = Math.floor(Math.log(-viewZ / near) / logFarOverNear * gridZ);
|
|
2283
|
+
if (slice < 0) return 0;
|
|
2284
|
+
if (slice >= gridZ) return gridZ - 1;
|
|
2285
|
+
return slice;
|
|
2286
|
+
}
|
|
2287
|
+
function clusterSpaceObjectAabb(center, radius, view, proj, output) {
|
|
2288
|
+
const result = output ?? {
|
|
2289
|
+
min: vec3.create(0, 0, 0),
|
|
2290
|
+
max: vec3.create(0, 0, 0)
|
|
2291
|
+
};
|
|
2292
|
+
const cx = center[0] ?? 0;
|
|
2293
|
+
const cy = center[1] ?? 0;
|
|
2294
|
+
const cz = center[2] ?? 0;
|
|
2295
|
+
const v00 = view[0] ?? 0;
|
|
2296
|
+
const v01 = view[1] ?? 0;
|
|
2297
|
+
const v02 = view[2] ?? 0;
|
|
2298
|
+
const v10 = view[4] ?? 0;
|
|
2299
|
+
const v11 = view[5] ?? 0;
|
|
2300
|
+
const v12 = view[6] ?? 0;
|
|
2301
|
+
const v20 = view[8] ?? 0;
|
|
2302
|
+
const v21 = view[9] ?? 0;
|
|
2303
|
+
const v22 = view[10] ?? 0;
|
|
2304
|
+
const v30 = view[12] ?? 0;
|
|
2305
|
+
const v31 = view[13] ?? 0;
|
|
2306
|
+
const v32 = view[14] ?? 0;
|
|
2307
|
+
const vx = v00 * cx + v10 * cy + v20 * cz + v30;
|
|
2308
|
+
const vy = v01 * cx + v11 * cy + v21 * cz + v31;
|
|
2309
|
+
const vz = v02 * cx + v12 * cy + v22 * cz + v32;
|
|
2310
|
+
const viewScaleX = Math.hypot(v00, v01, v02);
|
|
2311
|
+
const viewScaleY = Math.hypot(v10, v11, v12);
|
|
2312
|
+
const viewScaleZ = Math.hypot(v20, v21, v22);
|
|
2313
|
+
const rx = radius * viewScaleX;
|
|
2314
|
+
const ry = radius * viewScaleY;
|
|
2315
|
+
const rz = radius * viewScaleZ;
|
|
2316
|
+
const vMinX = vx - rx;
|
|
2317
|
+
const vMaxX = vx + rx;
|
|
2318
|
+
const vMinY = vy - ry;
|
|
2319
|
+
const vMaxY = vy + ry;
|
|
2320
|
+
const vMinZ = vz - rz;
|
|
2321
|
+
const vMaxZ = vz + rz;
|
|
2322
|
+
const minViewZ = vMinZ;
|
|
2323
|
+
const maxViewZ = Math.min(vMaxZ, -1e-5);
|
|
2324
|
+
if (minViewZ > maxViewZ) {
|
|
2325
|
+
result.min[0] = 1;
|
|
2326
|
+
result.min[1] = 1;
|
|
2327
|
+
result.min[2] = -1e-5;
|
|
2328
|
+
result.max[0] = -1;
|
|
2329
|
+
result.max[1] = -1;
|
|
2330
|
+
result.max[2] = -1e-5;
|
|
2331
|
+
return result;
|
|
2332
|
+
}
|
|
2333
|
+
let ndcMinX = Infinity;
|
|
2334
|
+
let ndcMaxX = -Infinity;
|
|
2335
|
+
let ndcMinY = Infinity;
|
|
2336
|
+
let ndcMaxY = -Infinity;
|
|
2337
|
+
const p00 = proj[0] ?? 0;
|
|
2338
|
+
const p10 = proj[4] ?? 0;
|
|
2339
|
+
const p20 = proj[8] ?? 0;
|
|
2340
|
+
const p30 = proj[12] ?? 0;
|
|
2341
|
+
const p01 = proj[1] ?? 0;
|
|
2342
|
+
const p11 = proj[5] ?? 0;
|
|
2343
|
+
const p21 = proj[9] ?? 0;
|
|
2344
|
+
const p31 = proj[13] ?? 0;
|
|
2345
|
+
const p23 = proj[11] ?? 0;
|
|
2346
|
+
const perspectiveWOnlyZ = Math.abs(proj[3] ?? 0) < 1e-8 && Math.abs(proj[7] ?? 0) < 1e-8 && Math.abs(proj[15] ?? 0) < 1e-8 && Math.abs(p23) >= 1e-5;
|
|
2347
|
+
if (perspectiveWOnlyZ) {
|
|
2348
|
+
const xExtent = Math.abs(p00) * rx + Math.abs(p10) * ry;
|
|
2349
|
+
const yExtent = Math.abs(p01) * rx + Math.abs(p11) * ry;
|
|
2350
|
+
for (let face = 0; face < 2; face++) {
|
|
2351
|
+
const zTest = face === 0 ? minViewZ : maxViewZ;
|
|
2352
|
+
if (zTest > 0) continue;
|
|
2353
|
+
const invW = 1 / (p23 * zTest);
|
|
2354
|
+
const xCenter = p20 * zTest + p30;
|
|
2355
|
+
const yCenter = p21 * zTest + p31;
|
|
2356
|
+
const x0 = (xCenter - xExtent) * invW;
|
|
2357
|
+
const x1 = (xCenter + xExtent) * invW;
|
|
2358
|
+
const y0 = (yCenter - yExtent) * invW;
|
|
2359
|
+
const y1 = (yCenter + yExtent) * invW;
|
|
2360
|
+
ndcMinX = Math.min(ndcMinX, x0, x1);
|
|
2361
|
+
ndcMaxX = Math.max(ndcMaxX, x0, x1);
|
|
2362
|
+
ndcMinY = Math.min(ndcMinY, y0, y1);
|
|
2363
|
+
ndcMaxY = Math.max(ndcMaxY, y0, y1);
|
|
2364
|
+
}
|
|
2365
|
+
} else {
|
|
2366
|
+
const corners = [
|
|
2367
|
+
[vMinX, vMinY],
|
|
2368
|
+
[vMinX, vMaxY],
|
|
2369
|
+
[vMaxX, vMinY],
|
|
2370
|
+
[vMaxX, vMaxY]
|
|
2371
|
+
];
|
|
2372
|
+
for (const zTest of [minViewZ, maxViewZ]) {
|
|
2373
|
+
if (zTest <= 0) {
|
|
2374
|
+
for (const corner of corners) {
|
|
2375
|
+
const sx = corner[0];
|
|
2376
|
+
const sy = corner[1];
|
|
2377
|
+
const ndc = projectToNdc(sx, sy, zTest, proj);
|
|
2378
|
+
ndcMinX = Math.min(ndcMinX, ndc[0]);
|
|
2379
|
+
ndcMaxX = Math.max(ndcMaxX, ndc[0]);
|
|
2380
|
+
ndcMinY = Math.min(ndcMinY, ndc[1]);
|
|
2381
|
+
ndcMaxY = Math.max(ndcMaxY, ndc[1]);
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
ndcMinX = Math.max(ndcMinX, -1);
|
|
2387
|
+
ndcMinY = Math.max(ndcMinY, -1);
|
|
2388
|
+
ndcMaxX = Math.min(ndcMaxX, 1);
|
|
2389
|
+
ndcMaxY = Math.min(ndcMaxY, 1);
|
|
2390
|
+
result.min[0] = ndcMinX;
|
|
2391
|
+
result.min[1] = ndcMinY;
|
|
2392
|
+
result.min[2] = minViewZ;
|
|
2393
|
+
result.max[0] = ndcMaxX;
|
|
2394
|
+
result.max[1] = ndcMaxY;
|
|
2395
|
+
result.max[2] = maxViewZ;
|
|
2396
|
+
return result;
|
|
2397
|
+
}
|
|
2398
|
+
function projectToNdc(vx, vy, vz, proj) {
|
|
2399
|
+
const p00 = proj[0] ?? 0;
|
|
2400
|
+
const p10 = proj[4] ?? 0;
|
|
2401
|
+
const p20 = proj[8] ?? 0;
|
|
2402
|
+
const p30 = proj[12] ?? 0;
|
|
2403
|
+
const p01 = proj[1] ?? 0;
|
|
2404
|
+
const p11 = proj[5] ?? 0;
|
|
2405
|
+
const p21 = proj[9] ?? 0;
|
|
2406
|
+
const p31 = proj[13] ?? 0;
|
|
2407
|
+
const p02 = proj[2] ?? 0;
|
|
2408
|
+
const p12 = proj[6] ?? 0;
|
|
2409
|
+
const p22 = proj[10] ?? 0;
|
|
2410
|
+
const p32 = proj[14] ?? 0;
|
|
2411
|
+
const p03 = proj[3] ?? 0;
|
|
2412
|
+
const p13 = proj[7] ?? 0;
|
|
2413
|
+
const p23 = proj[11] ?? 0;
|
|
2414
|
+
const p33 = proj[15] ?? 0;
|
|
2415
|
+
const cx = p00 * vx + p10 * vy + p20 * vz + p30;
|
|
2416
|
+
const cy = p01 * vx + p11 * vy + p21 * vz + p31;
|
|
2417
|
+
const cz = p02 * vx + p12 * vy + p22 * vz + p32;
|
|
2418
|
+
const cw = p03 * vx + p13 * vy + p23 * vz + p33;
|
|
2419
|
+
if (Math.abs(cw) < 1e-10) {
|
|
2420
|
+
return [vx < 0 ? -1 : 1, vy < 0 ? -1 : 1, cz];
|
|
2331
2421
|
}
|
|
2332
|
-
const
|
|
2333
|
-
|
|
2334
|
-
lightDataBuffer: lightData.value,
|
|
2335
|
-
lightDataBytes,
|
|
2336
|
-
clusterGridBuffer: clusterGrid.value,
|
|
2337
|
-
clusterGridBytes,
|
|
2338
|
-
lightIndexListBuffer: lightIndexList.value,
|
|
2339
|
-
lightIndexListBytes,
|
|
2340
|
-
clusterUniformBuffer: clusterUniform.value,
|
|
2341
|
-
clusterUniformBytes,
|
|
2342
|
-
lightBoundsBuffer: lightBounds.value,
|
|
2343
|
-
lightBoundsBytes,
|
|
2344
|
-
grid: { x: grid.x, y: grid.y, z: grid.z },
|
|
2345
|
-
unifiedBindGroupLayout: unifiedBglRes.value
|
|
2346
|
-
};
|
|
2347
|
-
cache2.set(runtime, buffers);
|
|
2348
|
-
return buffers;
|
|
2422
|
+
const invW = 1 / cw;
|
|
2423
|
+
return [cx * invW, cy * invW, cz * invW];
|
|
2349
2424
|
}
|
|
2350
|
-
function
|
|
2351
|
-
const
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
f32[7] = ssaoIntensity;
|
|
2362
|
-
return buf;
|
|
2425
|
+
function ndcPositionToCluster(ndc, viewZ, gridX, gridY, gridZ, near, far) {
|
|
2426
|
+
const ndcX = ndc[0] ?? 0;
|
|
2427
|
+
const ndcY = ndc[1] ?? 0;
|
|
2428
|
+
let cx = Math.floor((ndcX * 0.5 + 0.5) * gridX);
|
|
2429
|
+
let cy = Math.floor((ndcY * 0.5 + 0.5) * gridY);
|
|
2430
|
+
if (cx < 0) cx = 0;
|
|
2431
|
+
if (cx >= gridX) cx = gridX - 1;
|
|
2432
|
+
if (cy < 0) cy = 0;
|
|
2433
|
+
if (cy >= gridY) cy = gridY - 1;
|
|
2434
|
+
const cz = viewZToZSlice(viewZ, gridZ, near, far);
|
|
2435
|
+
return { x: cx, y: cy, z: cz };
|
|
2363
2436
|
}
|
|
2364
|
-
function
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
const
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2437
|
+
function calculateSphereClusterBounds(aabb, gridX, gridY, gridZ, near, far, output) {
|
|
2438
|
+
const result = output ?? {
|
|
2439
|
+
min: { x: 0, y: 0, z: 0 },
|
|
2440
|
+
max: { x: 0, y: 0, z: 0 }
|
|
2441
|
+
};
|
|
2442
|
+
const minZ = aabb.min[2] ?? 0;
|
|
2443
|
+
const maxZ = aabb.max[2] ?? 0;
|
|
2444
|
+
const idxMin = ndcPositionToCluster(aabb.min, minZ, gridX, gridY, gridZ, near, far);
|
|
2445
|
+
const idxMax = ndcPositionToCluster(aabb.max, maxZ, gridX, gridY, gridZ, near, far);
|
|
2446
|
+
result.min.x = Math.min(idxMin.x, idxMax.x);
|
|
2447
|
+
result.min.y = Math.min(idxMin.y, idxMax.y);
|
|
2448
|
+
result.min.z = Math.min(idxMin.z, idxMax.z);
|
|
2449
|
+
result.max.x = Math.max(idxMin.x, idxMax.x);
|
|
2450
|
+
result.max.y = Math.max(idxMin.y, idxMax.y);
|
|
2451
|
+
result.max.z = Math.max(idxMin.z, idxMax.z);
|
|
2452
|
+
return result;
|
|
2453
|
+
}
|
|
2454
|
+
function bin(lights, view, proj, grid, near, far, clusterGrid, lightIndexList, capacity, scratch, profile, options = {}) {
|
|
2455
|
+
const gridX = grid.x;
|
|
2456
|
+
const gridY = grid.y;
|
|
2457
|
+
const gridZ = grid.z;
|
|
2458
|
+
const clusterCount = gridX * gridY * gridZ;
|
|
2459
|
+
const binScratch = scratch ?? createClusterBinScratch();
|
|
2460
|
+
ensureScratchCapacity(binScratch, clusterCount, lights.length, gridX, gridY, gridZ);
|
|
2461
|
+
const { clusterCounts, clusterCursors, lightBounds, clusterDeltas } = binScratch;
|
|
2462
|
+
clusterGrid.fill(0, 0, clusterCount * 2);
|
|
2463
|
+
clusterCounts.fill(0, 0, clusterCount);
|
|
2464
|
+
let attemptedTotal = 0;
|
|
2465
|
+
runClusterBinProfilePhase(profile, "light-bounds-and-occupancy", () => {
|
|
2466
|
+
runClusterBinProfilePhase(profile, "light-bounds-and-occupancy/light-aabb", () => {
|
|
2467
|
+
for (let lightIdx = 0; lightIdx < lights.length; lightIdx++) {
|
|
2468
|
+
const boundsOffset = lightIdx * 6;
|
|
2469
|
+
lightBounds[boundsOffset] = -1;
|
|
2470
|
+
const light = lights[lightIdx];
|
|
2471
|
+
if (!light) continue;
|
|
2472
|
+
const radius = deriveCullingRadius(light.range, 1);
|
|
2473
|
+
if (radius <= 0) {
|
|
2474
|
+
continue;
|
|
2396
2475
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2476
|
+
const aabb = clusterSpaceObjectAabb(
|
|
2477
|
+
light.position,
|
|
2478
|
+
radius,
|
|
2479
|
+
view,
|
|
2480
|
+
proj,
|
|
2481
|
+
binScratch.aabbOutput
|
|
2482
|
+
);
|
|
2483
|
+
const bounds = calculateSphereClusterBounds(
|
|
2484
|
+
aabb,
|
|
2485
|
+
gridX,
|
|
2486
|
+
gridY,
|
|
2487
|
+
gridZ,
|
|
2488
|
+
near,
|
|
2489
|
+
far,
|
|
2490
|
+
binScratch.boundsOutput
|
|
2491
|
+
);
|
|
2492
|
+
if (bounds.min.x > bounds.max.x || bounds.min.y > bounds.max.y || bounds.min.z > bounds.max.z) {
|
|
2493
|
+
continue;
|
|
2403
2494
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2495
|
+
lightBounds[boundsOffset] = bounds.min.x;
|
|
2496
|
+
lightBounds[boundsOffset + 1] = bounds.min.y;
|
|
2497
|
+
lightBounds[boundsOffset + 2] = bounds.min.z;
|
|
2498
|
+
lightBounds[boundsOffset + 3] = bounds.max.x;
|
|
2499
|
+
lightBounds[boundsOffset + 4] = bounds.max.y;
|
|
2500
|
+
lightBounds[boundsOffset + 5] = bounds.max.z;
|
|
2501
|
+
}
|
|
2502
|
+
});
|
|
2503
|
+
runClusterBinProfilePhase(profile, "light-bounds-and-occupancy/cluster-occupancy", () => {
|
|
2504
|
+
const diffX = gridX + 1;
|
|
2505
|
+
const diffY = gridY + 1;
|
|
2506
|
+
const diffRow = diffX * diffY;
|
|
2507
|
+
clusterDeltas.fill(0, 0, diffRow * (gridZ + 1));
|
|
2508
|
+
const addDifference = (x, y, z, delta) => {
|
|
2509
|
+
const index = z * diffRow + y * diffX + x;
|
|
2510
|
+
clusterDeltas[index] = (clusterDeltas[index] ?? 0) + delta;
|
|
2511
|
+
};
|
|
2512
|
+
for (let lightIdx = 0; lightIdx < lights.length; lightIdx++) {
|
|
2513
|
+
const boundsOffset = lightIdx * 6;
|
|
2514
|
+
if ((lightBounds[boundsOffset] ?? -1) < 0) continue;
|
|
2515
|
+
const minX = lightBounds[boundsOffset] ?? 0;
|
|
2516
|
+
const minY = lightBounds[boundsOffset + 1] ?? 0;
|
|
2517
|
+
const minZ = lightBounds[boundsOffset + 2] ?? 0;
|
|
2518
|
+
const maxX = lightBounds[boundsOffset + 3] ?? -1;
|
|
2519
|
+
const maxY = lightBounds[boundsOffset + 4] ?? -1;
|
|
2520
|
+
const maxZ = lightBounds[boundsOffset + 5] ?? -1;
|
|
2521
|
+
const endX = maxX + 1;
|
|
2522
|
+
const endY = maxY + 1;
|
|
2523
|
+
const endZ = maxZ + 1;
|
|
2524
|
+
addDifference(minX, minY, minZ, 1);
|
|
2525
|
+
addDifference(endX, minY, minZ, -1);
|
|
2526
|
+
addDifference(minX, endY, minZ, -1);
|
|
2527
|
+
addDifference(minX, minY, endZ, -1);
|
|
2528
|
+
addDifference(endX, endY, minZ, 1);
|
|
2529
|
+
addDifference(endX, minY, endZ, 1);
|
|
2530
|
+
addDifference(minX, endY, endZ, 1);
|
|
2531
|
+
addDifference(endX, endY, endZ, -1);
|
|
2532
|
+
}
|
|
2533
|
+
for (let cz = 0; cz < gridZ; cz++) {
|
|
2534
|
+
for (let cy = 0; cy < gridY; cy++) {
|
|
2535
|
+
for (let cx = 0; cx < gridX; cx++) {
|
|
2536
|
+
const diffIdx = cz * diffRow + cy * diffX + cx;
|
|
2537
|
+
let count = clusterDeltas[diffIdx] ?? 0;
|
|
2538
|
+
if (cx > 0) count += clusterDeltas[diffIdx - 1] ?? 0;
|
|
2539
|
+
if (cy > 0) count += clusterDeltas[diffIdx - diffX] ?? 0;
|
|
2540
|
+
if (cz > 0) count += clusterDeltas[diffIdx - diffRow] ?? 0;
|
|
2541
|
+
if (cx > 0 && cy > 0) count -= clusterDeltas[diffIdx - diffX - 1] ?? 0;
|
|
2542
|
+
if (cx > 0 && cz > 0) count -= clusterDeltas[diffIdx - diffRow - 1] ?? 0;
|
|
2543
|
+
if (cy > 0 && cz > 0) count -= clusterDeltas[diffIdx - diffRow - diffX] ?? 0;
|
|
2544
|
+
if (cx > 0 && cy > 0 && cz > 0) {
|
|
2545
|
+
count += clusterDeltas[diffIdx - diffRow - diffX - 1] ?? 0;
|
|
2546
|
+
}
|
|
2547
|
+
clusterDeltas[diffIdx] = count;
|
|
2548
|
+
const clusterIdx = cz * gridY * gridX + cy * gridX + cx;
|
|
2549
|
+
clusterCounts[clusterIdx] = count;
|
|
2550
|
+
attemptedTotal += count;
|
|
2551
|
+
}
|
|
2410
2552
|
}
|
|
2411
|
-
},
|
|
2412
|
-
{
|
|
2413
|
-
binding: 7,
|
|
2414
|
-
resource: { kind: "textureView", value: ssaoTexView }
|
|
2415
|
-
},
|
|
2416
|
-
{
|
|
2417
|
-
binding: 8,
|
|
2418
|
-
resource: { kind: "sampler", value: ssaoSampler }
|
|
2419
2553
|
}
|
|
2420
|
-
|
|
2421
|
-
});
|
|
2422
|
-
if (
|
|
2423
|
-
|
|
2424
|
-
|
|
2554
|
+
});
|
|
2555
|
+
});
|
|
2556
|
+
if (attemptedTotal > capacity) {
|
|
2557
|
+
return err({
|
|
2558
|
+
code: "index-overflow",
|
|
2559
|
+
expected: `writeCount <= ${capacity}`,
|
|
2560
|
+
hint: `light index list overflow: needed ${attemptedTotal} entries, capacity ${capacity}; reduce lights, shrink grid, or shrink ranges (overrun = ${attemptedTotal - capacity})`,
|
|
2561
|
+
detail: { actual: attemptedTotal, capacity }
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
const writeCount = runClusterBinProfilePhase(profile, "cluster-reserve", () => {
|
|
2565
|
+
let reserved = 0;
|
|
2566
|
+
for (let ci = 0; ci < clusterCount; ci++) {
|
|
2567
|
+
const base = ci * 2;
|
|
2568
|
+
clusterGrid[base] = reserved;
|
|
2569
|
+
clusterGrid[base + 1] = clusterCounts[ci] ?? 0;
|
|
2570
|
+
clusterCursors[ci] = reserved;
|
|
2571
|
+
reserved += clusterCounts[ci] ?? 0;
|
|
2572
|
+
}
|
|
2573
|
+
return reserved;
|
|
2574
|
+
});
|
|
2575
|
+
if (options.writeMembership !== false) {
|
|
2576
|
+
runClusterBinProfilePhase(profile, "light-index-write", () => {
|
|
2577
|
+
for (let lightIdx = 0; lightIdx < lights.length; lightIdx++) {
|
|
2578
|
+
const boundsOffset = lightIdx * 6;
|
|
2579
|
+
let valid = false;
|
|
2580
|
+
let minX = 0;
|
|
2581
|
+
let minY = 0;
|
|
2582
|
+
let minZ = 0;
|
|
2583
|
+
let maxX = -1;
|
|
2584
|
+
let maxY = -1;
|
|
2585
|
+
let maxZ = -1;
|
|
2586
|
+
runClusterBinProfilePhase(profile, "light-index-write/bounds-read", () => {
|
|
2587
|
+
if ((lightBounds[boundsOffset] ?? -1) < 0) return;
|
|
2588
|
+
valid = true;
|
|
2589
|
+
minX = lightBounds[boundsOffset] ?? 0;
|
|
2590
|
+
minY = lightBounds[boundsOffset + 1] ?? 0;
|
|
2591
|
+
minZ = lightBounds[boundsOffset + 2] ?? 0;
|
|
2592
|
+
maxX = lightBounds[boundsOffset + 3] ?? -1;
|
|
2593
|
+
maxY = lightBounds[boundsOffset + 4] ?? -1;
|
|
2594
|
+
maxZ = lightBounds[boundsOffset + 5] ?? -1;
|
|
2595
|
+
});
|
|
2596
|
+
if (!valid) continue;
|
|
2597
|
+
runClusterBinProfilePhase(profile, "light-index-write/cluster-write", () => {
|
|
2598
|
+
for (let cz = minZ; cz <= maxZ; cz++) {
|
|
2599
|
+
for (let cy = minY; cy <= maxY; cy++) {
|
|
2600
|
+
for (let cx = minX; cx <= maxX; cx++) {
|
|
2601
|
+
const clusterIdx = cz * gridY * gridX + cy * gridX + cx;
|
|
2602
|
+
const cursor = clusterCursors[clusterIdx] ?? 0;
|
|
2603
|
+
lightIndexList[cursor] = lightIdx;
|
|
2604
|
+
clusterCursors[clusterIdx] = cursor + 1;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
});
|
|
2425
2611
|
}
|
|
2426
|
-
return
|
|
2612
|
+
return ok$1(writeCount);
|
|
2427
2613
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2614
|
+
|
|
2615
|
+
// src/pipeline/standard-lighting/errors.ts
|
|
2616
|
+
function fromClusterBinError(error) {
|
|
2617
|
+
return new StandardClusterIndexOverflowError(error.detail.actual, error.detail.capacity);
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
// src/pipeline/standard-lighting/membership.ts
|
|
2621
|
+
function deriveStandardMembership(frame, layout, scratch = createClusterBinScratch()) {
|
|
2622
|
+
const clusterGrid = new Uint32Array(layout.clusterGridU32Length);
|
|
2623
|
+
const lightIndexList = new Uint32Array(layout.lightIndexListCapacity);
|
|
2624
|
+
const result = bin(
|
|
2625
|
+
frame.local,
|
|
2626
|
+
frame.view,
|
|
2627
|
+
frame.projection,
|
|
2628
|
+
layout.grid,
|
|
2629
|
+
frame.near,
|
|
2630
|
+
frame.far,
|
|
2631
|
+
clusterGrid,
|
|
2632
|
+
lightIndexList,
|
|
2633
|
+
layout.lightIndexListCapacity,
|
|
2634
|
+
scratch
|
|
2635
|
+
);
|
|
2636
|
+
if (!result.ok) return err$1(fromClusterBinError(result.error));
|
|
2637
|
+
return ok({
|
|
2638
|
+
clusterGrid,
|
|
2639
|
+
lightIndexList,
|
|
2640
|
+
lightBounds: scratch.lightBounds.slice(0, frame.local.length * 6),
|
|
2641
|
+
membershipEntryCount: result.value,
|
|
2642
|
+
scratch
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
// src/pipeline/standard-lighting/prepare.ts
|
|
2647
|
+
function prepareStandardLighting(frame) {
|
|
2648
|
+
if (frame.local.length > frame.lightCount || frame.local.length > MAX_LIGHTS) {
|
|
2649
|
+
return err$1(new StandardLightBudgetExceededError(frame.local.length, frame.lightCount));
|
|
2650
|
+
}
|
|
2651
|
+
const layout = createStandardClusterLayout(frame.grid);
|
|
2652
|
+
const membership = deriveStandardMembership(frame, layout);
|
|
2653
|
+
if (!membership.ok) return membership;
|
|
2654
|
+
return ok({
|
|
2655
|
+
...membership.value,
|
|
2656
|
+
directional: frame.directional,
|
|
2657
|
+
local: frame.local,
|
|
2431
2658
|
layout,
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
resource: { kind: "buffer", value: { buffer: hdrpBuffers.clusterGridBuffer } }
|
|
2436
|
-
},
|
|
2437
|
-
{
|
|
2438
|
-
binding: 1,
|
|
2439
|
-
resource: { kind: "buffer", value: { buffer: hdrpBuffers.lightIndexListBuffer } }
|
|
2440
|
-
},
|
|
2441
|
-
{
|
|
2442
|
-
binding: 2,
|
|
2443
|
-
resource: { kind: "buffer", value: { buffer: hdrpBuffers.clusterUniformBuffer } }
|
|
2444
|
-
},
|
|
2445
|
-
{
|
|
2446
|
-
binding: 3,
|
|
2447
|
-
resource: { kind: "buffer", value: { buffer: hdrpBuffers.lightBoundsBuffer } }
|
|
2448
|
-
}
|
|
2449
|
-
]
|
|
2659
|
+
membershipLightCount: frame.local.length,
|
|
2660
|
+
renderPath: frame.renderPath,
|
|
2661
|
+
maxLights: frame.lightCount
|
|
2450
2662
|
});
|
|
2451
|
-
if (!result.ok) {
|
|
2452
|
-
runtime.errorRegistry.fire(result.error);
|
|
2453
|
-
return null;
|
|
2454
|
-
}
|
|
2455
|
-
return result.value;
|
|
2456
2663
|
}
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
texture = null;
|
|
2462
|
-
/**
|
|
2463
|
-
* Whole-atlas cube_array sampling view used by URP shaders that bind the
|
|
2464
|
-
* atlas at `@group(0) @binding(5)`. `null` until `ensure()` has run.
|
|
2465
|
-
*/
|
|
2466
|
-
cubeArrayView = null;
|
|
2467
|
-
/** Comparison sampler for `texture_depth_cube_array`. */
|
|
2468
|
-
compareSampler = null;
|
|
2469
|
-
/** Per-face 2D depth view cache, keyed by `layer * 6 + face`. */
|
|
2470
|
-
faceViews = /* @__PURE__ */ new Map();
|
|
2471
|
-
/** Per-face label cache (debug aid). Reused on re-creation. */
|
|
2472
|
-
device;
|
|
2473
|
-
/** Per-face square size in pixels. */
|
|
2474
|
-
faceSize;
|
|
2475
|
-
/** Cube layer count. */
|
|
2476
|
-
layers;
|
|
2477
|
-
constructor(device, options = {}) {
|
|
2478
|
-
this.device = device;
|
|
2479
|
-
this.faceSize = options.faceSize ?? SHADOW_ATLAS_DEFAULT_FACE_SIZE;
|
|
2480
|
-
this.layers = options.layers ?? SHADOW_ATLAS_DEFAULT_LAYERS;
|
|
2481
|
-
}
|
|
2482
|
-
/**
|
|
2483
|
-
* Returns `true` once the atlas texture is allocated. Used by the extract /
|
|
2484
|
-
* record path to gate "no PointLightShadow → no allocation" (AC-09) — the
|
|
2485
|
-
* caller calls {@link ensure} on a frame that has at least one snapshot;
|
|
2486
|
-
* before that, `isAllocated()` stays `false` and tests can assert no GPU
|
|
2487
|
-
* resources were created.
|
|
2488
|
-
*/
|
|
2489
|
-
isAllocated() {
|
|
2490
|
-
return this.texture !== null;
|
|
2491
|
-
}
|
|
2492
|
-
/**
|
|
2493
|
-
* Lazy-allocate the atlas texture + cube_array sampling view + comparison
|
|
2494
|
-
* sampler. Idempotent: subsequent calls are zero-cost no-ops once the
|
|
2495
|
-
* resources exist.
|
|
2496
|
-
*
|
|
2497
|
-
* Throws (via the RHI error registry) on allocation failure. This is the
|
|
2498
|
-
* only path that touches the GPU — keeping it inside one method makes the
|
|
2499
|
-
* "zero-shadow scene = zero allocation" gate easy to enforce: the renderer
|
|
2500
|
-
* never calls this method on a frame with no PointLightShadow entities.
|
|
2501
|
-
*/
|
|
2502
|
-
ensure() {
|
|
2503
|
-
if (this.texture !== null) return;
|
|
2504
|
-
const usage = GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING;
|
|
2505
|
-
const texDesc = cubeArrayDepthDescriptor(this.faceSize, this.layers, usage);
|
|
2506
|
-
const texRes = this.device.createTexture(texDesc);
|
|
2507
|
-
if (!texRes.ok) throw texRes.error;
|
|
2508
|
-
this.texture = texRes.value;
|
|
2509
|
-
const viewRes = this.device.createTextureView(this.texture, {
|
|
2510
|
-
label: "shadow-atlas-cube-array-view",
|
|
2511
|
-
dimension: "cube-array",
|
|
2512
|
-
aspect: "depth-only",
|
|
2513
|
-
baseArrayLayer: 0,
|
|
2514
|
-
arrayLayerCount: 6 * this.layers,
|
|
2515
|
-
baseMipLevel: 0,
|
|
2516
|
-
mipLevelCount: 1
|
|
2517
|
-
});
|
|
2518
|
-
if (!viewRes.ok) {
|
|
2519
|
-
this.device.destroyTexture(this.texture);
|
|
2520
|
-
this.texture = null;
|
|
2521
|
-
throw viewRes.error;
|
|
2522
|
-
}
|
|
2523
|
-
this.cubeArrayView = viewRes.value;
|
|
2524
|
-
const sampRes = this.device.createSampler(comparisonSamplerDescriptor());
|
|
2525
|
-
if (!sampRes.ok) {
|
|
2526
|
-
this.device.destroyTexture(this.texture);
|
|
2527
|
-
this.texture = null;
|
|
2528
|
-
this.cubeArrayView = null;
|
|
2529
|
-
throw sampRes.error;
|
|
2530
|
-
}
|
|
2531
|
-
this.compareSampler = sampRes.value;
|
|
2532
|
-
}
|
|
2533
|
-
/**
|
|
2534
|
-
* Whole-atlas cube_array sampling view (`texture_depth_cube_array`). Returns
|
|
2535
|
-
* `null` if the atlas is not yet allocated; the caller should gate on a
|
|
2536
|
-
* non-null pointShadow array before calling {@link ensure} + this getter.
|
|
2537
|
-
*/
|
|
2538
|
-
getAtlasView() {
|
|
2539
|
-
return this.cubeArrayView;
|
|
2540
|
-
}
|
|
2541
|
-
/**
|
|
2542
|
-
* Comparison sampler (`compare: 'less'`) for cube_array depth sampling.
|
|
2543
|
-
* Returns `null` if the atlas is not yet allocated.
|
|
2544
|
-
*/
|
|
2545
|
-
getComparisonSampler() {
|
|
2546
|
-
return this.compareSampler;
|
|
2547
|
-
}
|
|
2548
|
-
/**
|
|
2549
|
-
* Backing texture handle. Returns `null` if the atlas is not yet allocated.
|
|
2550
|
-
*/
|
|
2551
|
-
getTexture() {
|
|
2552
|
-
return this.texture;
|
|
2553
|
-
}
|
|
2554
|
-
/**
|
|
2555
|
-
* Lazy 2D depth view selecting `(layer, face)` (the caller passes
|
|
2556
|
-
* `shadowAtlasLayer` from the snapshot and a face index 0..5). Used as the
|
|
2557
|
-
* depth attachment of the shadow-caster render pass for one cube face.
|
|
2558
|
-
*
|
|
2559
|
-
* Caches per `(layer, face)` so the per-frame shadow loop pays zero
|
|
2560
|
-
* `createTextureView` cost after the first allocation. Cache is cleared on
|
|
2561
|
-
* {@link dispose}.
|
|
2562
|
-
*
|
|
2563
|
-
* @throws PointShadowAtlasUninitializedError if the atlas is not yet
|
|
2564
|
-
* allocated (caller must call {@link ensure} first).
|
|
2565
|
-
* @throws PointShadowAtlasBoundsViolationError if `layer` or `face` is out
|
|
2566
|
-
* of range.
|
|
2567
|
-
* @throws RhiError if the underlying `createTextureView` fails.
|
|
2568
|
-
*/
|
|
2569
|
-
faceView(layer, face) {
|
|
2570
|
-
if (this.texture === null) {
|
|
2571
|
-
throw new PointShadowAtlasUninitializedError();
|
|
2572
|
-
}
|
|
2573
|
-
if (layer < 0 || layer >= this.layers) {
|
|
2574
|
-
throw new PointShadowAtlasBoundsViolationError("layer", layer, this.layers);
|
|
2575
|
-
}
|
|
2576
|
-
if (face < 0 || face >= 6) {
|
|
2577
|
-
throw new PointShadowAtlasBoundsViolationError("face", face, 6);
|
|
2578
|
-
}
|
|
2579
|
-
const key = layer * 6 + face;
|
|
2580
|
-
const cached = this.faceViews.get(key);
|
|
2581
|
-
if (cached !== void 0) return cached;
|
|
2582
|
-
const desc = cubeArrayDepthFaceView(layer, face);
|
|
2583
|
-
const r = this.device.createTextureView(this.texture, desc);
|
|
2584
|
-
if (!r.ok) throw r.error;
|
|
2585
|
-
this.faceViews.set(key, r.value);
|
|
2586
|
-
return r.value;
|
|
2664
|
+
function occupiedClusterCount(prepared) {
|
|
2665
|
+
let occupied = 0;
|
|
2666
|
+
for (let index = 1; index < prepared.clusterGrid.length; index += 2) {
|
|
2667
|
+
if ((prepared.clusterGrid[index] ?? 0) > 0) occupied += 1;
|
|
2587
2668
|
}
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
this.cubeArrayView = null;
|
|
2600
|
-
this.compareSampler = null;
|
|
2669
|
+
return occupied;
|
|
2670
|
+
}
|
|
2671
|
+
function selectStandardClusterTransport(capabilities, prepared) {
|
|
2672
|
+
const requested = prepared.local.length;
|
|
2673
|
+
if (!capabilities.storageBuffer) {
|
|
2674
|
+
return err$1(
|
|
2675
|
+
new StandardClusterTransportUnavailableError(
|
|
2676
|
+
requested,
|
|
2677
|
+
"enable a proven storage-buffer Cluster transport; compute without storage is not a membership path"
|
|
2678
|
+
)
|
|
2679
|
+
);
|
|
2601
2680
|
}
|
|
2602
|
-
|
|
2681
|
+
const producer = capabilities.compute && capabilities.membershipPipelineReady ? "gpu" : "cpu";
|
|
2682
|
+
const kind = producer === "gpu" ? "compute-storage" : "cpu-storage";
|
|
2683
|
+
return ok({
|
|
2684
|
+
kind,
|
|
2685
|
+
producer,
|
|
2686
|
+
layout: prepared.layout,
|
|
2687
|
+
requestedLightCount: requested,
|
|
2688
|
+
admittedLightCount: requested,
|
|
2689
|
+
membershipEntryCount: prepared.membershipEntryCount,
|
|
2690
|
+
occupiedClusterCount: occupiedClusterCount(prepared),
|
|
2691
|
+
clusterGridBytes: prepared.layout.clusterGridU32Length * Uint32Array.BYTES_PER_ELEMENT,
|
|
2692
|
+
lightIndexListBytes: prepared.membershipEntryCount * Uint32Array.BYTES_PER_ELEMENT,
|
|
2693
|
+
lightDataBytes: requested * 64,
|
|
2694
|
+
lightBoundsBytes: prepared.lightBounds.byteLength
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2603
2697
|
|
|
2604
2698
|
// src/post-process-errors.ts
|
|
2605
2699
|
var POST_PROCESS_POLICY = {
|
|
@@ -2722,38 +2816,6 @@ function warnMultiLightDirectional(frameState, directionalCount, envOverride) {
|
|
|
2722
2816
|
}
|
|
2723
2817
|
}
|
|
2724
2818
|
}
|
|
2725
|
-
function warnMultiLightPoint(frameState, pointCount, envOverride) {
|
|
2726
|
-
if (!frameState.warnedMultiLightPoint && pointCount > 4) {
|
|
2727
|
-
frameState.warnedMultiLightPoint = true;
|
|
2728
|
-
const env = globalThis.process;
|
|
2729
|
-
if (env?.env?.NODE_ENV !== "production") {
|
|
2730
|
-
console.warn(
|
|
2731
|
-
"[forgeax] render-system-multi-light point: at most 4 entities (got N=" + pointCount + "). First 4 used; rest dropped.",
|
|
2732
|
-
{
|
|
2733
|
-
code: "render-system-multi-light",
|
|
2734
|
-
expected: "at most 4 point",
|
|
2735
|
-
detail: { type: "point", got: pointCount }
|
|
2736
|
-
}
|
|
2737
|
-
);
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
|
-
}
|
|
2741
|
-
function warnMultiLightSpot(frameState, spotCount, envOverride) {
|
|
2742
|
-
if (!frameState.warnedMultiLightSpot && spotCount > 4) {
|
|
2743
|
-
frameState.warnedMultiLightSpot = true;
|
|
2744
|
-
const env = globalThis.process;
|
|
2745
|
-
if (env?.env?.NODE_ENV !== "production") {
|
|
2746
|
-
console.warn(
|
|
2747
|
-
"[forgeax] render-system-multi-light spot: at most 4 entities (got N=" + spotCount + "). First 4 used; rest dropped.",
|
|
2748
|
-
{
|
|
2749
|
-
code: "render-system-multi-light",
|
|
2750
|
-
expected: "at most 4 spot",
|
|
2751
|
-
detail: { type: "spot", got: spotCount }
|
|
2752
|
-
}
|
|
2753
|
-
);
|
|
2754
|
-
}
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
2819
|
function warnMultiSkylight(frameState, skylightCount, winningEntityHandle) {
|
|
2758
2820
|
if (!frameState.warnedMultiSkylight && skylightCount > 1) {
|
|
2759
2821
|
frameState.warnedMultiSkylight = true;
|
|
@@ -2820,10 +2882,32 @@ var POINTS_LINES_VIEW_SLOT_COUNT = 1024;
|
|
|
2820
2882
|
var POINTS_LINES_VIEW_BUFFER_SIZE = POINTS_LINES_VIEW_SLOT_STRIDE * POINTS_LINES_VIEW_SLOT_COUNT;
|
|
2821
2883
|
var VIEW_UNIFORM_SLOT_STRIDE = 1024;
|
|
2822
2884
|
var POINT_SHADOW_VIEW_SLOT_COUNT = 24;
|
|
2823
|
-
var
|
|
2885
|
+
var CUBE_CAPTURE_VIEW_SLOT_BASE = 1 + POINT_SHADOW_VIEW_SLOT_COUNT;
|
|
2886
|
+
var REFLECTION_PROBE_VIEW_SLOT_BASE = CUBE_CAPTURE_VIEW_SLOT_BASE + 6;
|
|
2887
|
+
var REFLECTION_PROBE_VIEW_SLOT_COUNT = 16;
|
|
2888
|
+
var VIEW_UNIFORM_BUFFER_SIZE = VIEW_UNIFORM_SLOT_STRIDE * (REFLECTION_PROBE_VIEW_SLOT_BASE + REFLECTION_PROBE_VIEW_SLOT_COUNT);
|
|
2824
2889
|
function pointShadowViewOffset(layer, face) {
|
|
2825
2890
|
return VIEW_UNIFORM_SLOT_STRIDE * (1 + layer * 6 + face);
|
|
2826
2891
|
}
|
|
2892
|
+
function directionalShadowFilterCarrier(quality) {
|
|
2893
|
+
if (quality === void 0) return [0, 0, 0, 0];
|
|
2894
|
+
if (quality.kind === "pcss") {
|
|
2895
|
+
return [
|
|
2896
|
+
quality.preset === "medium" ? 4 : 5,
|
|
2897
|
+
quality.angularRadiusRadians,
|
|
2898
|
+
quality.maxPenumbraTexels,
|
|
2899
|
+
0
|
|
2900
|
+
];
|
|
2901
|
+
}
|
|
2902
|
+
switch (quality.kernel) {
|
|
2903
|
+
case 1:
|
|
2904
|
+
return [1, 0, 0, 0];
|
|
2905
|
+
case 3:
|
|
2906
|
+
return [2, 0, 0, 0];
|
|
2907
|
+
case 5:
|
|
2908
|
+
return [3, 0, 0, 0];
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2827
2911
|
function writePointsLinesViewUbo(queue, buffer, camera, width, height, model, style, byteOffset = 0) {
|
|
2828
2912
|
const projection = computeProjectionMatrix(camera);
|
|
2829
2913
|
const view = computeViewMatrix(camera);
|
|
@@ -2855,7 +2939,9 @@ function writePointsLinesViewUbo(queue, buffer, camera, width, height, model, st
|
|
|
2855
2939
|
const uploaded = queue.writeBuffer(buffer, byteOffset, payload);
|
|
2856
2940
|
if (!uploaded.ok) throw uploaded.error;
|
|
2857
2941
|
}
|
|
2858
|
-
function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShadowSnapshots,
|
|
2942
|
+
function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShadowSnapshots, fogOrTemporalOrOffset, temporal, projectorSpotIndex) {
|
|
2943
|
+
const byteOffset = typeof fogOrTemporalOrOffset === "number" ? fogOrTemporalOrOffset : 0;
|
|
2944
|
+
const fogOrTemporal = typeof fogOrTemporalOrOffset === "number" ? void 0 : fogOrTemporalOrOffset;
|
|
2859
2945
|
const isTemporal = fogOrTemporal !== void 0 && ("currentJitterUv" in fogOrTemporal || "currentUnjitteredViewProjection" in fogOrTemporal);
|
|
2860
2946
|
const fog = isTemporal ? void 0 : fogOrTemporal;
|
|
2861
2947
|
const resolvedTemporal = temporal ?? (isTemporal ? fogOrTemporal : void 0);
|
|
@@ -2911,14 +2997,20 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
|
|
|
2911
2997
|
viewPayload[230] = camera.projection === "orthographic" ? 1 : 0;
|
|
2912
2998
|
if (lights.splitPlanes !== void 0) {
|
|
2913
2999
|
for (let s = 0; s < 4; s++) {
|
|
2914
|
-
|
|
3000
|
+
for (let lane = 0; lane < 4; lane++) {
|
|
3001
|
+
viewPayload[108 + s * 4 + lane] = lights.splitPlanes[s * 4 + lane] ?? 0;
|
|
3002
|
+
}
|
|
2915
3003
|
}
|
|
2916
3004
|
}
|
|
2917
3005
|
viewPayload[124] = lights.cascadeCount ?? 0;
|
|
2918
3006
|
viewPayload[125] = lights.cascadeBlend ?? 0;
|
|
2919
3007
|
viewPayload[126] = lights.depthBias ?? 5e-3;
|
|
2920
3008
|
viewPayload[127] = lights.normalBias ?? 0.05;
|
|
2921
|
-
|
|
3009
|
+
const filterCarrier = directionalShadowFilterCarrier(lights.directionalShadowQuality);
|
|
3010
|
+
viewPayload[128] = filterCarrier[0] ?? 0;
|
|
3011
|
+
viewPayload[129] = filterCarrier[1] ?? 0;
|
|
3012
|
+
viewPayload[130] = filterCarrier[2] ?? 0;
|
|
3013
|
+
viewPayload[131] = filterCarrier[3] ?? 0;
|
|
2922
3014
|
{
|
|
2923
3015
|
const SPOT_LVP_BASE_FLOAT = 132;
|
|
2924
3016
|
const SPOT_LVP_LANE_COUNT = 4;
|
|
@@ -2928,10 +3020,11 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
|
|
|
2928
3020
|
const ss = spotSnaps[i];
|
|
2929
3021
|
if (ss === void 0) continue;
|
|
2930
3022
|
const tile = ss.shadowAtlasTile;
|
|
2931
|
-
if (tile < 0 || tile >= SPOT_LVP_LANE_COUNT) continue;
|
|
2932
3023
|
const lvp = ss.lightViewProj;
|
|
2933
3024
|
if (lvp === void 0) continue;
|
|
2934
|
-
const
|
|
3025
|
+
const lane = tile >= 0 && tile < SPOT_LVP_LANE_COUNT ? tile : i === projectorSpotIndex ? 0 : -1;
|
|
3026
|
+
if (lane < 0) continue;
|
|
3027
|
+
const base = SPOT_LVP_BASE_FLOAT + lane * SPOT_LVP_FLOATS_PER_LANE;
|
|
2935
3028
|
for (let f = 0; f < SPOT_LVP_FLOATS_PER_LANE; f++) {
|
|
2936
3029
|
viewPayload[base + f] = lvp[f] ?? 0;
|
|
2937
3030
|
}
|
|
@@ -2945,10 +3038,11 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
|
|
|
2945
3038
|
viewPayload[236] = fog.heightFalloff;
|
|
2946
3039
|
viewPayload[237] = fog.maxOpacity;
|
|
2947
3040
|
}
|
|
2948
|
-
const viewUploadResult = queue.writeBuffer(viewUniformBuffer,
|
|
3041
|
+
const viewUploadResult = queue.writeBuffer(viewUniformBuffer, byteOffset, viewPayload);
|
|
2949
3042
|
if (!viewUploadResult.ok) throw viewUploadResult.error;
|
|
2950
3043
|
for (const snapshot of lights.pointShadow ?? []) {
|
|
2951
|
-
if (snapshot.shadowAtlasLayer < 0 || snapshot.shadowAtlasLayer >=
|
|
3044
|
+
if (snapshot.shadowAtlasLayer < 0 || snapshot.shadowAtlasLayer >= SHADOW_ATLAS_DEFAULT_LAYERS)
|
|
3045
|
+
continue;
|
|
2952
3046
|
for (let face = 0; face < 6; face += 1) {
|
|
2953
3047
|
const facePayload = viewPayload.slice();
|
|
2954
3048
|
const matrix = snapshot.shadowMatrices.subarray(face * 16, (face + 1) * 16);
|
|
@@ -2964,14 +3058,42 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
|
|
|
2964
3058
|
}
|
|
2965
3059
|
|
|
2966
3060
|
// src/record/frame-lighting.ts
|
|
3061
|
+
function resolveReflectionProbeBinding(selection, table) {
|
|
3062
|
+
if (selection.kind !== "probe") return { probeIndex: void 0, useSkylight: true };
|
|
3063
|
+
const primitiveKey = `${selection.worldId}:${selection.entityKey}`;
|
|
3064
|
+
const tableIndex = table?.cpuIndexByPrimitive(primitiveKey);
|
|
3065
|
+
return { probeIndex: tableIndex ?? selection.entityKey, useSkylight: false };
|
|
3066
|
+
}
|
|
3067
|
+
function projectDeviceOwnedBindGroup(state, device, layout) {
|
|
3068
|
+
return state !== null && state !== void 0 && state.device === device && state.layout === layout ? state.bindGroup : null;
|
|
3069
|
+
}
|
|
2967
3070
|
function runHdrpClusterProfilePhase(runner, phase, action) {
|
|
2968
3071
|
return runner === void 0 ? action() : runner(phase, action);
|
|
2969
3072
|
}
|
|
2970
|
-
function
|
|
3073
|
+
function mapClusterUploadFailure(resource, error) {
|
|
3074
|
+
return new RendererOperationError("device-operation-failed", {
|
|
3075
|
+
operation: "draw",
|
|
3076
|
+
cause: {
|
|
3077
|
+
code: error.code,
|
|
3078
|
+
expected: `queue.writeBuffer succeeds for Standard Cluster ${resource}`,
|
|
3079
|
+
hint: `repair the device write for Standard Cluster ${resource} and retry the frame`,
|
|
3080
|
+
detail: error
|
|
3081
|
+
}
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValidated, bindGroupCounts, graphTargets, includeView = true, standardLighting) {
|
|
2971
3085
|
let viewBindGroup = null;
|
|
2972
3086
|
let meshBindGroup = null;
|
|
2973
3087
|
let hdrpClusterBindGroup = null;
|
|
2974
|
-
const
|
|
3088
|
+
const clusteredLighting = standardLighting?.kind === "clustered";
|
|
3089
|
+
const membershipLayout = pipelineState.hdrpClusterMembershipBindGroupLayout;
|
|
3090
|
+
const membership = frameState.hdrpClusterMembership;
|
|
3091
|
+
const expectedHdrpBuffers = hasValidated && clusteredLighting ? getOrCreateHdrpBuffers(internals, frameState.installedPipelineConfig?.clusterGrid) : null;
|
|
3092
|
+
const currentMembership = expectedHdrpBuffers !== null && membership?.clusterGridBuffer === expectedHdrpBuffers.clusterGridBuffer ? projectDeviceOwnedBindGroup(membership, internals.device, membershipLayout) : null;
|
|
3093
|
+
if (membership !== null && currentMembership === null) {
|
|
3094
|
+
frameState.hdrpClusterMembership = null;
|
|
3095
|
+
}
|
|
3096
|
+
const hdrpClusterMembershipBindGroup = clusteredLighting ? currentMembership : null;
|
|
2975
3097
|
if (hasValidated) {
|
|
2976
3098
|
if (includeView) {
|
|
2977
3099
|
const shadowSampler = pipelineState.perPassResources.shadowSampler;
|
|
@@ -2990,6 +3112,9 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
|
|
|
2990
3112
|
const b5View = atlasViewMaybe !== null ? atlasViewMaybe : pipelineState.shadowAtlasFallbackTextureView;
|
|
2991
3113
|
const graphSpotShadowView = graphTargets?.spotShadow;
|
|
2992
3114
|
const b8View = graphSpotShadowView !== void 0 ? graphSpotShadowView : pipelineState.shadowFallbackTextureView;
|
|
3115
|
+
const projectorAvailable = pipelineState.projectorAvailable !== false;
|
|
3116
|
+
const b11View = graphTargets?.projector ?? pipelineState.defaultWhiteTextureView;
|
|
3117
|
+
const b12Sampler = graphTargets?.projectorSampler ?? pipelineState.defaultSampler;
|
|
2993
3118
|
viewBindGroup = getOrCreateFromChain(
|
|
2994
3119
|
frameState.viewBindGroupCache,
|
|
2995
3120
|
[
|
|
@@ -3001,110 +3126,116 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
|
|
|
3001
3126
|
b5View,
|
|
3002
3127
|
pipelineState.shadowParamsBuffer,
|
|
3003
3128
|
b8View,
|
|
3129
|
+
...projectorAvailable ? [b11View, b12Sampler] : [],
|
|
3004
3130
|
pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer
|
|
3005
3131
|
],
|
|
3006
3132
|
"view-main",
|
|
3007
3133
|
() => {
|
|
3008
|
-
const
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
value: {
|
|
3097
|
-
buffer: pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
3098
|
-
size: POINTS_LINES_VIEW_BYTES
|
|
3099
|
-
}
|
|
3134
|
+
const entries = [
|
|
3135
|
+
{
|
|
3136
|
+
binding: 0,
|
|
3137
|
+
resource: {
|
|
3138
|
+
kind: "buffer",
|
|
3139
|
+
value: { buffer: pipelineState.viewUniformBuffer, size: VIEW_UNIFORM_BYTES }
|
|
3140
|
+
}
|
|
3141
|
+
},
|
|
3142
|
+
{
|
|
3143
|
+
binding: 1,
|
|
3144
|
+
resource: {
|
|
3145
|
+
kind: "buffer",
|
|
3146
|
+
value: { buffer: pipelineState.pointLightsBuffer }
|
|
3147
|
+
}
|
|
3148
|
+
},
|
|
3149
|
+
{
|
|
3150
|
+
binding: 2,
|
|
3151
|
+
resource: {
|
|
3152
|
+
kind: "buffer",
|
|
3153
|
+
value: { buffer: pipelineState.spotLightsBuffer }
|
|
3154
|
+
}
|
|
3155
|
+
},
|
|
3156
|
+
{
|
|
3157
|
+
binding: 3,
|
|
3158
|
+
resource: {
|
|
3159
|
+
kind: "textureView",
|
|
3160
|
+
value: b3View
|
|
3161
|
+
}
|
|
3162
|
+
},
|
|
3163
|
+
{
|
|
3164
|
+
binding: 4,
|
|
3165
|
+
resource: {
|
|
3166
|
+
kind: "sampler",
|
|
3167
|
+
value: shadowSampler
|
|
3168
|
+
}
|
|
3169
|
+
},
|
|
3170
|
+
// feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1:
|
|
3171
|
+
// cube_array shadow atlas view (real ShadowAtlas when point
|
|
3172
|
+
// shadows are active; else 1x1x6 fallback).
|
|
3173
|
+
{
|
|
3174
|
+
binding: 5,
|
|
3175
|
+
resource: {
|
|
3176
|
+
kind: "textureView",
|
|
3177
|
+
value: b5View
|
|
3178
|
+
}
|
|
3179
|
+
},
|
|
3180
|
+
// feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1:
|
|
3181
|
+
// shadowParams UBO (`array<vec4<f32>, 4>` = 64 B). Lane N
|
|
3182
|
+
// stores `(near, far, 1/(far-near), 0)` for the point light
|
|
3183
|
+
// with shadowAtlasLayer === N. Updated per frame from
|
|
3184
|
+
// `pointShadowSnapshots` below.
|
|
3185
|
+
{
|
|
3186
|
+
binding: 6,
|
|
3187
|
+
resource: {
|
|
3188
|
+
kind: "buffer",
|
|
3189
|
+
value: { buffer: pipelineState.shadowParamsBuffer }
|
|
3190
|
+
}
|
|
3191
|
+
},
|
|
3192
|
+
// feat-20260613-csm-cascaded-shadow-maps M5 / w28 (rebased to
|
|
3193
|
+
// binding 7 on 2026-06-13 to make room for point-shadow 5/6):
|
|
3194
|
+
// forward shaders declare binding 7 in common.wgsl (shared
|
|
3195
|
+
// view BGL) but never reference it; only shadow_caster.wgsl
|
|
3196
|
+
// reads it. Host writes a stable singleton buffer so every
|
|
3197
|
+
// forward bind group entry stays populated.
|
|
3198
|
+
{
|
|
3199
|
+
binding: 7,
|
|
3200
|
+
resource: {
|
|
3201
|
+
kind: "buffer",
|
|
3202
|
+
value: { buffer: pipelineState.shadowCasterCascadeBuffer }
|
|
3203
|
+
}
|
|
3204
|
+
},
|
|
3205
|
+
// feat-20260625-spot-light-shadow-mapping M3 / w21 (D-5):
|
|
3206
|
+
// spot shadow 2D atlas (real spotShadowDepth view when spot
|
|
3207
|
+
// shadows run this frame, else the 1x1 depth fallback). Always-on.
|
|
3208
|
+
{
|
|
3209
|
+
binding: 8,
|
|
3210
|
+
resource: {
|
|
3211
|
+
kind: "textureView",
|
|
3212
|
+
value: b8View
|
|
3213
|
+
}
|
|
3214
|
+
},
|
|
3215
|
+
{
|
|
3216
|
+
binding: 10,
|
|
3217
|
+
resource: {
|
|
3218
|
+
kind: "buffer",
|
|
3219
|
+
value: {
|
|
3220
|
+
buffer: pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
3221
|
+
size: POINTS_LINES_VIEW_BYTES
|
|
3100
3222
|
}
|
|
3101
3223
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3224
|
+
},
|
|
3225
|
+
...projectorAvailable ? [
|
|
3226
|
+
{ binding: 11, resource: { kind: "textureView", value: b11View } },
|
|
3227
|
+
{ binding: 12, resource: { kind: "sampler", value: b12Sampler } }
|
|
3228
|
+
] : []
|
|
3229
|
+
// feat-20260625-spot-light-shadow-mapping w25: the per-spot
|
|
3230
|
+
// fragment-read lightViewProj matrices fold into the View UBO
|
|
3231
|
+
// (binding 0, `view.spotLightViewProj`) — no standalone binding 9
|
|
3232
|
+
// (WebGL2 fragment uniform-buffer budget). binding 10 is the
|
|
3233
|
+
// dedicated vertex-only Points/Lines viewport UBO.
|
|
3234
|
+
];
|
|
3235
|
+
const viewBindGroupResult = internals.device.createBindGroup({
|
|
3236
|
+
label: "pbr-view-bg",
|
|
3237
|
+
layout: pipelineState.viewBindGroupLayout,
|
|
3238
|
+
entries
|
|
3108
3239
|
});
|
|
3109
3240
|
if (!viewBindGroupResult.ok) throw viewBindGroupResult.error;
|
|
3110
3241
|
return viewBindGroupResult.value;
|
|
@@ -3140,11 +3271,8 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
|
|
|
3140
3271
|
},
|
|
3141
3272
|
bindGroupCounts
|
|
3142
3273
|
);
|
|
3143
|
-
if (
|
|
3144
|
-
const hdrpBuffers =
|
|
3145
|
-
internals,
|
|
3146
|
-
frameState.installedPipelineConfig?.clusterGrid
|
|
3147
|
-
);
|
|
3274
|
+
if (clusteredLighting) {
|
|
3275
|
+
const hdrpBuffers = expectedHdrpBuffers;
|
|
3148
3276
|
if (hdrpBuffers !== null) {
|
|
3149
3277
|
hdrpClusterBindGroup = getOrCreateFromChain(
|
|
3150
3278
|
frameState.meshBindGroupCache,
|
|
@@ -3178,27 +3306,19 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
|
|
|
3178
3306
|
}
|
|
3179
3307
|
return { viewBindGroup, meshBindGroup, hdrpClusterBindGroup, hdrpClusterMembershipBindGroup };
|
|
3180
3308
|
}
|
|
3181
|
-
function prepareFrameLighting(internals, frameState, lights) {
|
|
3309
|
+
function prepareFrameLighting(internals, frameState, lights, camera, pipelineState) {
|
|
3182
3310
|
if (lights.directionalCount > 1) {
|
|
3183
3311
|
warnMultiLightDirectional(frameState, lights.directionalCount);
|
|
3184
3312
|
}
|
|
3185
|
-
if (!frameState.isHdrpActive) {
|
|
3186
|
-
if (lights.point.length > LIGHT_ARRAY_MAX_SLOTS) {
|
|
3187
|
-
warnMultiLightPoint(frameState, lights.point.length);
|
|
3188
|
-
}
|
|
3189
|
-
if (lights.spot.length > LIGHT_ARRAY_MAX_SLOTS) {
|
|
3190
|
-
warnMultiLightSpot(frameState, lights.spot.length);
|
|
3191
|
-
}
|
|
3192
|
-
}
|
|
3193
3313
|
frameState.pointShadowSnapshots = lights.pointShadow;
|
|
3194
3314
|
frameState.spotShadowSnapshots = lights.spot;
|
|
3195
3315
|
if (lights.pointShadow.length > 0) {
|
|
3196
3316
|
if (frameState.pointShadowAtlas === null) {
|
|
3197
3317
|
const firstSnap = lights.pointShadow[0];
|
|
3198
|
-
const faceSize = firstSnap?.mapSize ??
|
|
3318
|
+
const faceSize = firstSnap?.mapSize ?? SHADOW_ATLAS_DEFAULT_FACE_SIZE;
|
|
3199
3319
|
frameState.pointShadowAtlas = new ShadowAtlas(internals.device, {
|
|
3200
3320
|
faceSize,
|
|
3201
|
-
layers:
|
|
3321
|
+
layers: SHADOW_ATLAS_DEFAULT_LAYERS
|
|
3202
3322
|
});
|
|
3203
3323
|
}
|
|
3204
3324
|
try {
|
|
@@ -3221,49 +3341,60 @@ function prepareFrameLighting(internals, frameState, lights) {
|
|
|
3221
3341
|
intensity: 0
|
|
3222
3342
|
};
|
|
3223
3343
|
const totalLightCount = (directionalLight !== void 0 ? 1 : 0) + pointLights.length + spotLights.length;
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
)
|
|
3245
|
-
|
|
3344
|
+
const clusterGrid = frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
|
|
3345
|
+
const local = [
|
|
3346
|
+
...pointLights.map((source) => ({
|
|
3347
|
+
kind: "point",
|
|
3348
|
+
shadowed: source.shadowAtlasLayer !== void 0 && source.shadowAtlasLayer >= 0,
|
|
3349
|
+
position: source.position,
|
|
3350
|
+
range: Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0 ? Math.sqrt(1 / source.invRangeSquared) : 1e3,
|
|
3351
|
+
source
|
|
3352
|
+
})),
|
|
3353
|
+
...spotLights.map((source) => ({
|
|
3354
|
+
kind: "spot",
|
|
3355
|
+
shadowed: source.castShadow && source.shadowAtlasTile >= 0 && source.lightViewProj !== void 0,
|
|
3356
|
+
position: source.position,
|
|
3357
|
+
range: Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0 ? Math.sqrt(1 / source.invRangeSquared) : 1e3,
|
|
3358
|
+
source
|
|
3359
|
+
}))
|
|
3360
|
+
];
|
|
3361
|
+
const prepared = prepareStandardLighting({
|
|
3362
|
+
directional: directionalLight,
|
|
3363
|
+
local,
|
|
3364
|
+
view: computeViewMatrix(camera),
|
|
3365
|
+
projection: computeProjectionMatrix(camera),
|
|
3366
|
+
near: camera.near,
|
|
3367
|
+
far: camera.far,
|
|
3368
|
+
grid: clusterGrid,
|
|
3369
|
+
lightCount: internals.standardProfile?.lightCount ?? DEFAULT_STANDARD_PROFILE.lightCount,
|
|
3370
|
+
renderPath: internals.standardProfile?.renderPath ?? DEFAULT_STANDARD_PROFILE.renderPath
|
|
3371
|
+
});
|
|
3372
|
+
if (!prepared.ok) return prepared;
|
|
3373
|
+
if (prepared.value.local.length === 0 && !internals.device.caps.storageBuffer) {
|
|
3374
|
+
return ok({
|
|
3375
|
+
light,
|
|
3376
|
+
pointLights,
|
|
3377
|
+
spotLights,
|
|
3378
|
+
totalLightCount,
|
|
3379
|
+
standard: { kind: "no-local-lights", prepared: prepared.value }
|
|
3380
|
+
});
|
|
3246
3381
|
}
|
|
3247
|
-
const
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3382
|
+
const transport = selectStandardClusterTransport(
|
|
3383
|
+
{
|
|
3384
|
+
compute: internals.device.caps.compute,
|
|
3385
|
+
storageBuffer: internals.device.caps.storageBuffer,
|
|
3386
|
+
membershipPipelineReady: pipelineState !== void 0 && pipelineState.hdrpClusterMembershipPipeline !== null && pipelineState.hdrpClusterMembershipBindGroupLayout !== null
|
|
3387
|
+
},
|
|
3388
|
+
prepared.value
|
|
3253
3389
|
);
|
|
3254
|
-
if (!
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
offset,
|
|
3263
|
-
packed
|
|
3264
|
-
);
|
|
3265
|
-
if (!writeRes.ok) throw writeRes.error;
|
|
3266
|
-
}
|
|
3390
|
+
if (!transport.ok) return transport;
|
|
3391
|
+
return ok({
|
|
3392
|
+
light,
|
|
3393
|
+
pointLights,
|
|
3394
|
+
spotLights,
|
|
3395
|
+
totalLightCount,
|
|
3396
|
+
standard: { kind: "clustered", prepared: prepared.value, transport: transport.value }
|
|
3397
|
+
});
|
|
3267
3398
|
}
|
|
3268
3399
|
function writeShadowParamsBuffer(internals, frameState, pipelineState) {
|
|
3269
3400
|
const SHADOW_PARAMS_LANE_COUNT = 4;
|
|
@@ -3306,191 +3437,81 @@ function warnZeroLightStandard(frameState, renderables, skylight, totalLightCoun
|
|
|
3306
3437
|
}
|
|
3307
3438
|
}
|
|
3308
3439
|
}
|
|
3309
|
-
function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera,
|
|
3310
|
-
const
|
|
3311
|
-
|
|
3312
|
-
if (
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
const clusterSsaoIntensity = clusterSsaoConfig !== void 0 && clusterSsaoConfig.enabled === true ? clusterSsaoConfig.intensity ?? 1 : 0;
|
|
3319
|
-
const clusterUniformPayload = packClusterUniform(
|
|
3320
|
-
grid,
|
|
3321
|
-
camera.near,
|
|
3322
|
-
camera.far,
|
|
3323
|
-
clusterSsaoIntensity,
|
|
3324
|
-
0,
|
|
3325
|
-
lightCapacity
|
|
3326
|
-
);
|
|
3327
|
-
const clusterUniformUpload = internals.device.queue.writeBuffer(
|
|
3328
|
-
hdrpBuffers.clusterUniformBuffer,
|
|
3329
|
-
0,
|
|
3330
|
-
new Uint8Array(clusterUniformPayload)
|
|
3331
|
-
);
|
|
3332
|
-
if (!clusterUniformUpload.ok) internals.errorRegistry.fire(clusterUniformUpload.error);
|
|
3333
|
-
const clusterGridClear = internals.device.queue.writeBuffer(
|
|
3334
|
-
hdrpBuffers.clusterGridBuffer,
|
|
3335
|
-
0,
|
|
3336
|
-
new Uint8Array(hdrpBuffers.clusterGridBytes)
|
|
3337
|
-
);
|
|
3338
|
-
if (!clusterGridClear.ok) internals.errorRegistry.fire(clusterGridClear.error);
|
|
3339
|
-
const lightIndexListClear = internals.device.queue.writeBuffer(
|
|
3340
|
-
hdrpBuffers.lightIndexListBuffer,
|
|
3341
|
-
0,
|
|
3342
|
-
new Uint8Array(hdrpBuffers.lightIndexListBytes)
|
|
3440
|
+
function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, prepared, transport, profilePhase, gpuMembershipPipelineReady = false, membershipBindGroupLayout = null, projectorLightSlotIndex) {
|
|
3441
|
+
const configuredClusterGrid = prepared.layout.grid;
|
|
3442
|
+
const gpuMembership = transport.producer === "gpu";
|
|
3443
|
+
if (gpuMembership && (!gpuMembershipPipelineReady || membershipBindGroupLayout === null)) {
|
|
3444
|
+
return err$1(
|
|
3445
|
+
new StandardClusterTransportUnavailableError(
|
|
3446
|
+
prepared.local.length,
|
|
3447
|
+
"the selected GPU Cluster producer requires a ready membership pipeline and bind-group layout"
|
|
3448
|
+
)
|
|
3343
3449
|
);
|
|
3344
|
-
if (!lightIndexListClear.ok) internals.errorRegistry.fire(lightIndexListClear.error);
|
|
3345
|
-
return;
|
|
3346
|
-
}
|
|
3347
|
-
const HDRP_LIGHT_BUDGET = 256;
|
|
3348
|
-
let effectivePointLights = pointLights;
|
|
3349
|
-
let effectiveSpotLights = spotLights;
|
|
3350
|
-
if (hdrpLightCount > HDRP_LIGHT_BUDGET) {
|
|
3351
|
-
if (!frameState.hdrpOncePerFrameFired.has("hdrp-light-budget-exceeded")) {
|
|
3352
|
-
frameState.hdrpOncePerFrameFired.add("hdrp-light-budget-exceeded");
|
|
3353
|
-
internals.errorRegistry.fire(
|
|
3354
|
-
new HdrpLightBudgetExceededError(hdrpLightCount, HDRP_LIGHT_BUDGET)
|
|
3355
|
-
);
|
|
3356
|
-
}
|
|
3357
|
-
if (pointLights.length >= HDRP_LIGHT_BUDGET) {
|
|
3358
|
-
effectivePointLights = pointLights.slice(0, HDRP_LIGHT_BUDGET);
|
|
3359
|
-
effectiveSpotLights = [];
|
|
3360
|
-
} else {
|
|
3361
|
-
effectivePointLights = pointLights;
|
|
3362
|
-
effectiveSpotLights = spotLights.slice(0, HDRP_LIGHT_BUDGET - pointLights.length);
|
|
3363
|
-
}
|
|
3364
|
-
}
|
|
3365
|
-
const effectiveLightCount = effectivePointLights.length + effectiveSpotLights.length;
|
|
3366
|
-
const configuredClusterGrid = frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
|
|
3367
|
-
let gpuMembership = useGpuMembership && membershipBindGroupLayout !== null;
|
|
3368
|
-
if (gpuMembership && membershipBindGroupLayout !== null && frameState.hdrpClusterMembershipBindGroup === null) {
|
|
3369
|
-
const hdrpBuffers = getOrCreateHdrpBuffers(internals, configuredClusterGrid);
|
|
3370
|
-
if (hdrpBuffers !== null) {
|
|
3371
|
-
frameState.hdrpClusterMembershipBindGroup = createHdrpClusterMembershipBindGroup(
|
|
3372
|
-
internals,
|
|
3373
|
-
hdrpBuffers,
|
|
3374
|
-
membershipBindGroupLayout
|
|
3375
|
-
);
|
|
3376
|
-
}
|
|
3377
|
-
gpuMembership = frameState.hdrpClusterMembershipBindGroup !== null;
|
|
3378
3450
|
}
|
|
3379
|
-
const
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
const {
|
|
3386
|
-
hdrpLights,
|
|
3387
|
-
clusterGrid: clusterGrid2,
|
|
3388
|
-
gridX: gridX2,
|
|
3389
|
-
gridY: gridY2,
|
|
3390
|
-
gridZ: gridZ2,
|
|
3391
|
-
clusterGridBuf: clusterGridBuf2,
|
|
3392
|
-
lightIndexListBuf: lightIndexListBuf2,
|
|
3393
|
-
projMatrix,
|
|
3394
|
-
viewMatrix
|
|
3395
|
-
} = runHdrpClusterProfilePhase(
|
|
3396
|
-
profilePhase,
|
|
3397
|
-
"record/scene-state/hdrp-cluster/binner/input-preparation",
|
|
3398
|
-
() => {
|
|
3399
|
-
const hdrpLights2 = [];
|
|
3400
|
-
for (const pl of effectivePointLights) {
|
|
3401
|
-
const range = Number.isFinite(pl.invRangeSquared) && pl.invRangeSquared > 0 ? Math.sqrt(1 / pl.invRangeSquared) : 1e3;
|
|
3402
|
-
hdrpLights2.push({ position: pl.position, range });
|
|
3403
|
-
}
|
|
3404
|
-
for (const sl of effectiveSpotLights) {
|
|
3405
|
-
const range = Number.isFinite(sl.invRangeSquared) && sl.invRangeSquared > 0 ? Math.sqrt(1 / sl.invRangeSquared) : 1e3;
|
|
3406
|
-
hdrpLights2.push({ position: sl.position, range });
|
|
3407
|
-
}
|
|
3408
|
-
const clusterGrid3 = frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
|
|
3409
|
-
const gridX3 = clusterGrid3.x;
|
|
3410
|
-
const gridY3 = clusterGrid3.y;
|
|
3411
|
-
const gridZ3 = clusterGrid3.z;
|
|
3412
|
-
const clusterCount = gridX3 * gridY3 * gridZ3;
|
|
3413
|
-
const projMatrix2 = computeProjectionMatrix(camera);
|
|
3414
|
-
const viewMatrix2 = computeViewMatrix(camera);
|
|
3415
|
-
const clusterGridBuf3 = frameState.hdrpClusterGridScratch === null || frameState.hdrpClusterGridScratch.length < clusterCount * 2 ? new Uint32Array(clusterCount * 2) : frameState.hdrpClusterGridScratch;
|
|
3416
|
-
frameState.hdrpClusterGridScratch = clusterGridBuf3;
|
|
3417
|
-
const lightIndexListBuf3 = frameState.hdrpLightIndexListScratch === null || frameState.hdrpLightIndexListScratch.length < LIGHT_INDEX_LIST_CAPACITY ? new Uint32Array(LIGHT_INDEX_LIST_CAPACITY) : frameState.hdrpLightIndexListScratch;
|
|
3418
|
-
frameState.hdrpLightIndexListScratch = lightIndexListBuf3;
|
|
3419
|
-
return {
|
|
3420
|
-
hdrpLights: hdrpLights2,
|
|
3421
|
-
clusterGrid: clusterGrid3,
|
|
3422
|
-
gridX: gridX3,
|
|
3423
|
-
gridY: gridY3,
|
|
3424
|
-
gridZ: gridZ3,
|
|
3425
|
-
clusterGridBuf: clusterGridBuf3,
|
|
3426
|
-
lightIndexListBuf: lightIndexListBuf3,
|
|
3427
|
-
projMatrix: projMatrix2,
|
|
3428
|
-
viewMatrix: viewMatrix2
|
|
3429
|
-
};
|
|
3430
|
-
}
|
|
3431
|
-
);
|
|
3432
|
-
const binResult = runHdrpClusterProfilePhase(
|
|
3433
|
-
profilePhase,
|
|
3434
|
-
"record/scene-state/hdrp-cluster/binner/bin-core",
|
|
3435
|
-
() => bin(
|
|
3436
|
-
hdrpLights,
|
|
3437
|
-
viewMatrix,
|
|
3438
|
-
projMatrix,
|
|
3439
|
-
{ x: gridX2, y: gridY2, z: gridZ2 },
|
|
3440
|
-
camera.near,
|
|
3441
|
-
camera.far,
|
|
3442
|
-
clusterGridBuf2,
|
|
3443
|
-
lightIndexListBuf2,
|
|
3444
|
-
LIGHT_INDEX_LIST_CAPACITY,
|
|
3445
|
-
frameState.hdrpClusterBinScratch,
|
|
3446
|
-
binnerProfile,
|
|
3447
|
-
{ writeMembership: !gpuMembership }
|
|
3451
|
+
const hdrpBuffers = getOrCreateHdrpBuffers(internals, configuredClusterGrid);
|
|
3452
|
+
if (hdrpBuffers === null) {
|
|
3453
|
+
return err$1(
|
|
3454
|
+
new StandardClusterTransportUnavailableError(
|
|
3455
|
+
prepared.local.length,
|
|
3456
|
+
"the selected Standard Cluster transport could not allocate its persistent buffers"
|
|
3448
3457
|
)
|
|
3449
3458
|
);
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3459
|
+
}
|
|
3460
|
+
const existingMembership = projectDeviceOwnedBindGroup(
|
|
3461
|
+
frameState.hdrpClusterMembership,
|
|
3462
|
+
internals.device,
|
|
3463
|
+
membershipBindGroupLayout
|
|
3464
|
+
);
|
|
3465
|
+
const membershipMatchesBuffers = existingMembership !== null && frameState.hdrpClusterMembership?.clusterGridBuffer === hdrpBuffers.clusterGridBuffer;
|
|
3466
|
+
if (!membershipMatchesBuffers) frameState.hdrpClusterMembership = null;
|
|
3467
|
+
if (gpuMembership && membershipBindGroupLayout !== null && frameState.hdrpClusterMembership === null) {
|
|
3468
|
+
const bindGroup = createHdrpClusterMembershipBindGroup(
|
|
3469
|
+
internals,
|
|
3470
|
+
hdrpBuffers,
|
|
3471
|
+
membershipBindGroupLayout
|
|
3472
|
+
);
|
|
3473
|
+
if (bindGroup === null) {
|
|
3474
|
+
return err$1(
|
|
3475
|
+
new StandardClusterTransportUnavailableError(
|
|
3476
|
+
prepared.local.length,
|
|
3477
|
+
"the selected GPU Cluster producer could not create its membership bind group"
|
|
3478
|
+
)
|
|
3455
3479
|
);
|
|
3456
3480
|
}
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
clusterGrid: clusterGrid2,
|
|
3463
|
-
gridX: gridX2,
|
|
3464
|
-
gridY: gridY2,
|
|
3465
|
-
gridZ: gridZ2,
|
|
3466
|
-
clusterGridBuf: clusterGridBuf2,
|
|
3467
|
-
lightIndexListBuf: lightIndexListBuf2,
|
|
3468
|
-
lightIndexCount: binResult.ok ? binResult.value : 0
|
|
3481
|
+
frameState.hdrpClusterMembership = {
|
|
3482
|
+
device: internals.device,
|
|
3483
|
+
layout: membershipBindGroupLayout,
|
|
3484
|
+
bindGroup,
|
|
3485
|
+
clusterGridBuffer: hdrpBuffers.clusterGridBuffer
|
|
3469
3486
|
};
|
|
3470
|
-
}
|
|
3487
|
+
}
|
|
3488
|
+
const gridX = prepared.layout.grid.x;
|
|
3489
|
+
const gridY = prepared.layout.grid.y;
|
|
3490
|
+
const gridZ = prepared.layout.grid.z;
|
|
3491
|
+
const clusterGridBuf = prepared.clusterGrid;
|
|
3492
|
+
const lightIndexListBuf = prepared.lightIndexList;
|
|
3493
|
+
const lightIndexCount = prepared.membershipEntryCount;
|
|
3494
|
+
const envFalsify = globalThis.process?.env;
|
|
3495
|
+
if (envFalsify?.FORGEAX_HDRP_FALSIFY_CLUSTER_GRID_ZERO) clusterGridBuf.fill(0);
|
|
3471
3496
|
const hdrpPayload = runHdrpClusterProfilePhase(
|
|
3472
3497
|
profilePhase,
|
|
3473
3498
|
"record/scene-state/hdrp-cluster/payload-packing",
|
|
3474
3499
|
() => {
|
|
3475
|
-
const
|
|
3476
|
-
if (hdrpBuffers === null) return null;
|
|
3477
|
-
const lightCapacity = hdrpBuffers.storageBuffer ? 256 : HDRP_UNIFORM_LIGHT_CAPACITY;
|
|
3478
|
-
const lightDataPayload = new Float32Array(lightCapacity * 16);
|
|
3500
|
+
const lightDataPayload = new Float32Array(prepared.layout.lightDataSlotCount * 16);
|
|
3479
3501
|
let slotIdx = 0;
|
|
3480
|
-
for (const
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
const
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
const packed = packLightSlot(sl);
|
|
3502
|
+
for (const local of prepared.local) {
|
|
3503
|
+
const source = local.source;
|
|
3504
|
+
if (source === void 0) return null;
|
|
3505
|
+
const projectorSelected = projectorLightSlotIndex !== void 0 && projectorLightSlotIndex === slotIdx;
|
|
3506
|
+
const packed = source.kind === "point" && source.shadowAtlasLayer !== void 0 && source.shadowAtlasLayer >= 0 && source.shadowNear !== void 0 && source.shadowFar !== void 0 ? packLightSlot(
|
|
3507
|
+
source,
|
|
3508
|
+
{
|
|
3509
|
+
shadowAtlasLayer: source.shadowAtlasLayer,
|
|
3510
|
+
near: source.shadowNear,
|
|
3511
|
+
far: source.shadowFar
|
|
3512
|
+
},
|
|
3513
|
+
projectorSelected
|
|
3514
|
+
) : packLightSlot(source, void 0, projectorSelected);
|
|
3494
3515
|
lightDataPayload.set(packed, slotIdx * 16);
|
|
3495
3516
|
slotIdx += 1;
|
|
3496
3517
|
}
|
|
@@ -3501,8 +3522,7 @@ function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, pointLigh
|
|
|
3501
3522
|
camera.near,
|
|
3502
3523
|
camera.far,
|
|
3503
3524
|
clusterSsaoIntensity,
|
|
3504
|
-
|
|
3505
|
-
lightCapacity
|
|
3525
|
+
transport.admittedLightCount
|
|
3506
3526
|
);
|
|
3507
3527
|
return {
|
|
3508
3528
|
hdrpBuffers,
|
|
@@ -3511,73 +3531,97 @@ function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, pointLigh
|
|
|
3511
3531
|
};
|
|
3512
3532
|
}
|
|
3513
3533
|
);
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3534
|
+
if (hdrpPayload === null) {
|
|
3535
|
+
return err$1(
|
|
3536
|
+
new StandardClusterTransportUnavailableError(
|
|
3537
|
+
prepared.local.length,
|
|
3538
|
+
"the prepared Standard Cluster payload could not be materialized"
|
|
3539
|
+
)
|
|
3540
|
+
);
|
|
3541
|
+
}
|
|
3542
|
+
const bufferUpload = runHdrpClusterProfilePhase(
|
|
3543
|
+
profilePhase,
|
|
3544
|
+
"record/scene-state/hdrp-cluster/buffer-upload",
|
|
3545
|
+
() => {
|
|
3546
|
+
const { hdrpBuffers: hdrpBuffers2, lightDataUploadPayload, clusterUniformPayload } = hdrpPayload;
|
|
3517
3547
|
const lightDataUpload = internals.device.queue.writeBuffer(
|
|
3518
|
-
|
|
3548
|
+
hdrpBuffers2.lightDataBuffer,
|
|
3519
3549
|
0,
|
|
3520
3550
|
lightDataUploadPayload
|
|
3521
3551
|
);
|
|
3522
|
-
if (!lightDataUpload.ok)
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
0,
|
|
3528
|
-
frameState.hdrpClusterBinScratch.lightBounds.subarray(0, effectiveLightCount * 6)
|
|
3529
|
-
);
|
|
3530
|
-
if (!lightBoundsUpload.ok) internals.errorRegistry.fire(lightBoundsUpload.error);
|
|
3531
|
-
}
|
|
3532
|
-
const clusterGridUpload = internals.device.queue.writeBuffer(
|
|
3533
|
-
hdrpBuffers.clusterGridBuffer,
|
|
3552
|
+
if (!lightDataUpload.ok)
|
|
3553
|
+
return err$1(mapClusterUploadFailure("light-data", lightDataUpload.error));
|
|
3554
|
+
if (gpuMembership) {
|
|
3555
|
+
const lightBoundsUpload = internals.device.queue.writeBuffer(
|
|
3556
|
+
hdrpBuffers2.lightBoundsBuffer,
|
|
3534
3557
|
0,
|
|
3535
|
-
|
|
3558
|
+
prepared.lightBounds
|
|
3536
3559
|
);
|
|
3537
|
-
if (!
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3560
|
+
if (!lightBoundsUpload.ok)
|
|
3561
|
+
return err$1(mapClusterUploadFailure("light-bounds", lightBoundsUpload.error));
|
|
3562
|
+
}
|
|
3563
|
+
const clusterGridUpload = internals.device.queue.writeBuffer(
|
|
3564
|
+
hdrpBuffers2.clusterGridBuffer,
|
|
3565
|
+
0,
|
|
3566
|
+
clusterGridBuf.subarray(0, gridX * gridY * gridZ * 2)
|
|
3567
|
+
);
|
|
3568
|
+
if (!clusterGridUpload.ok)
|
|
3569
|
+
return err$1(mapClusterUploadFailure("cluster-grid", clusterGridUpload.error));
|
|
3570
|
+
if (!gpuMembership && lightIndexCount > 0) {
|
|
3571
|
+
const lightIndexListUpload = internals.device.queue.writeBuffer(
|
|
3572
|
+
hdrpBuffers2.lightIndexListBuffer,
|
|
3573
|
+
0,
|
|
3574
|
+
lightIndexListBuf.subarray(0, lightIndexCount)
|
|
3575
|
+
);
|
|
3576
|
+
if (!lightIndexListUpload.ok) {
|
|
3577
|
+
return err$1(mapClusterUploadFailure("light-index-list", lightIndexListUpload.error));
|
|
3545
3578
|
}
|
|
3546
3579
|
}
|
|
3547
3580
|
const clusterUniformUpload = internals.device.queue.writeBuffer(
|
|
3548
|
-
|
|
3581
|
+
hdrpBuffers2.clusterUniformBuffer,
|
|
3549
3582
|
0,
|
|
3550
3583
|
clusterUniformPayload
|
|
3551
3584
|
);
|
|
3552
|
-
if (!clusterUniformUpload.ok)
|
|
3585
|
+
if (!clusterUniformUpload.ok) {
|
|
3586
|
+
return err$1(mapClusterUploadFailure("cluster-uniform", clusterUniformUpload.error));
|
|
3587
|
+
}
|
|
3588
|
+
return ok(void 0);
|
|
3553
3589
|
}
|
|
3554
|
-
|
|
3590
|
+
);
|
|
3591
|
+
if (!bufferUpload.ok) return bufferUpload;
|
|
3592
|
+
const ssaoUpload = runHdrpClusterProfilePhase(
|
|
3593
|
+
profilePhase,
|
|
3594
|
+
"record/scene-state/hdrp-cluster/buffer-upload",
|
|
3595
|
+
() => {
|
|
3555
3596
|
const ssaoBufs = getOrCreateSsaoBuffers(internals);
|
|
3556
|
-
if (ssaoBufs
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3597
|
+
if (ssaoBufs === null) return ok(void 0);
|
|
3598
|
+
const sProj = computeProjectionMatrix(camera);
|
|
3599
|
+
const sView = computeViewMatrix(camera);
|
|
3600
|
+
const invProjOnly = mat4.create();
|
|
3601
|
+
mat4.invert(invProjOnly, sProj);
|
|
3602
|
+
const ssaoUniformPayload = new Float32Array(64);
|
|
3603
|
+
ssaoUniformPayload.set(sView, 0);
|
|
3604
|
+
ssaoUniformPayload.set(sProj, 16);
|
|
3605
|
+
ssaoUniformPayload.set(invProjOnly, 32);
|
|
3606
|
+
const ssaoConfig = frameState.installedPipelineConfig?.ssao;
|
|
3607
|
+
const parameters = getSsaoParameters(
|
|
3608
|
+
ssaoConfig !== void 0 && ssaoConfig.enabled === true ? ssaoConfig : void 0
|
|
3609
|
+
);
|
|
3610
|
+
ssaoUniformPayload[48] = parameters.intensity;
|
|
3611
|
+
ssaoUniformPayload[49] = parameters.radius;
|
|
3612
|
+
ssaoUniformPayload[50] = parameters.bias;
|
|
3613
|
+
const ssaoUniformRes = internals.device.queue.writeBuffer(
|
|
3614
|
+
ssaoBufs.uniformBuffer,
|
|
3615
|
+
0,
|
|
3616
|
+
ssaoUniformPayload
|
|
3617
|
+
);
|
|
3618
|
+
if (!ssaoUniformRes.ok)
|
|
3619
|
+
return err$1(mapClusterUploadFailure("ssao-uniform", ssaoUniformRes.error));
|
|
3620
|
+
return ok(void 0);
|
|
3579
3621
|
}
|
|
3580
|
-
|
|
3622
|
+
);
|
|
3623
|
+
if (!ssaoUpload.ok) return ssaoUpload;
|
|
3624
|
+
return ok(void 0);
|
|
3581
3625
|
}
|
|
3582
3626
|
function resolveSkyboxActive(internals, frameState, camera, skybox) {
|
|
3583
3627
|
const tonemapActive = camera.tonemap !== "none";
|
|
@@ -3603,6 +3647,7 @@ function getOrCreateIblCache(scope) {
|
|
|
3603
3647
|
const existing = scopeCaches.get(scope);
|
|
3604
3648
|
if (existing !== void 0) return existing;
|
|
3605
3649
|
const cache3 = {
|
|
3650
|
+
probeKernel: createIblKernelCache(scope.generation),
|
|
3606
3651
|
irradianceBakeCount: 0,
|
|
3607
3652
|
prefilterBakeCount: 0,
|
|
3608
3653
|
brdfLutBakeCount: 0
|
|
@@ -4643,35 +4688,6 @@ function deriveTextureExtent(format, width, height) {
|
|
|
4643
4688
|
uvScale: [width / physicalWidth, height / physicalHeight]
|
|
4644
4689
|
};
|
|
4645
4690
|
}
|
|
4646
|
-
function deriveMipUploadLayout(format, baseWidth, baseHeight, mipLevelCount) {
|
|
4647
|
-
const params = blockParamsForFormat(format);
|
|
4648
|
-
if (params === null) return [];
|
|
4649
|
-
const levels = [];
|
|
4650
|
-
let byteOffset = 0;
|
|
4651
|
-
for (let level = 0; level < mipLevelCount; level++) {
|
|
4652
|
-
const width = Math.max(1, baseWidth >> level);
|
|
4653
|
-
const height = Math.max(1, baseHeight >> level);
|
|
4654
|
-
const blockCols = Math.ceil(width / params.blockW);
|
|
4655
|
-
const blockRows = Math.ceil(height / params.blockH);
|
|
4656
|
-
const rowBytes = blockCols * params.bytesPerBlock;
|
|
4657
|
-
const byteLength = rowBytes * blockRows;
|
|
4658
|
-
levels.push({
|
|
4659
|
-
level,
|
|
4660
|
-
width,
|
|
4661
|
-
height,
|
|
4662
|
-
physicalWidth: blockCols * params.blockW,
|
|
4663
|
-
physicalHeight: blockRows * params.blockH,
|
|
4664
|
-
copyWidth: blockCols * params.blockW,
|
|
4665
|
-
copyHeight: blockRows * params.blockH,
|
|
4666
|
-
bytesPerRow: rowBytes,
|
|
4667
|
-
rowsPerImage: blockRows,
|
|
4668
|
-
byteOffset,
|
|
4669
|
-
byteLength
|
|
4670
|
-
});
|
|
4671
|
-
byteOffset += byteLength;
|
|
4672
|
-
}
|
|
4673
|
-
return levels;
|
|
4674
|
-
}
|
|
4675
4691
|
function deriveRenderDataMesh(mesh) {
|
|
4676
4692
|
const indices = mesh.indices;
|
|
4677
4693
|
const indexBytesUnpadded = indices === void 0 ? 0 : indices.byteLength;
|
|
@@ -4689,6 +4705,8 @@ function deriveRenderDataMesh(mesh) {
|
|
|
4689
4705
|
});
|
|
4690
4706
|
}
|
|
4691
4707
|
function deriveRenderDataTexture(tex) {
|
|
4708
|
+
const width = tex.shape.extent.width;
|
|
4709
|
+
const height = tex.shape.extent.height;
|
|
4692
4710
|
const isSrgbFormat = tex.format.endsWith("-srgb");
|
|
4693
4711
|
const expectedColorSpace = isSrgbFormat ? "srgb" : "linear";
|
|
4694
4712
|
if (tex.colorSpace !== expectedColorSpace) {
|
|
@@ -4701,8 +4719,17 @@ function deriveRenderDataTexture(tex) {
|
|
|
4701
4719
|
);
|
|
4702
4720
|
}
|
|
4703
4721
|
const compressed = isCompressedFormat(tex.format);
|
|
4704
|
-
const
|
|
4705
|
-
if (
|
|
4722
|
+
const layout = deriveTextureLayout({ shape: tex.shape, format: tex.format, mips: tex.mips });
|
|
4723
|
+
if (!layout.ok) {
|
|
4724
|
+
return err(
|
|
4725
|
+
projectionError({
|
|
4726
|
+
code: "invalid-source-format",
|
|
4727
|
+
expected: layout.error.expected,
|
|
4728
|
+
hint: layout.error.hint
|
|
4729
|
+
})
|
|
4730
|
+
);
|
|
4731
|
+
}
|
|
4732
|
+
if (compressed && tex.mips.kind === "generate") {
|
|
4706
4733
|
return err(
|
|
4707
4734
|
projectionError({
|
|
4708
4735
|
code: "mipgen-unsupported-compressed-format",
|
|
@@ -4711,9 +4738,18 @@ function deriveRenderDataTexture(tex) {
|
|
|
4711
4738
|
})
|
|
4712
4739
|
);
|
|
4713
4740
|
}
|
|
4714
|
-
const mipLevelCount =
|
|
4715
|
-
const
|
|
4716
|
-
if (
|
|
4741
|
+
const mipLevelCount = layout.value.levels.length;
|
|
4742
|
+
const baseLevel = layout.value.levels[0];
|
|
4743
|
+
if (baseLevel === void 0) {
|
|
4744
|
+
return err(
|
|
4745
|
+
projectionError({
|
|
4746
|
+
code: "invalid-source-format",
|
|
4747
|
+
expected: "texture layout contains a base mip level",
|
|
4748
|
+
hint: ASSET_ERROR_HINTS["invalid-source-format"]
|
|
4749
|
+
})
|
|
4750
|
+
);
|
|
4751
|
+
}
|
|
4752
|
+
if (!compressed && tex.data.byteLength < baseLevel.byteLength) {
|
|
4717
4753
|
return err(
|
|
4718
4754
|
projectionError({
|
|
4719
4755
|
code: "invalid-source-format",
|
|
@@ -4722,12 +4758,14 @@ function deriveRenderDataTexture(tex) {
|
|
|
4722
4758
|
})
|
|
4723
4759
|
);
|
|
4724
4760
|
}
|
|
4725
|
-
const bytesPerRow$1 = compressed ? bytesPerRow(tex.format,
|
|
4761
|
+
const bytesPerRow$1 = compressed ? bytesPerRow(tex.format, width) ?? baseLevel.bytesPerRow : baseLevel.bytesPerRow;
|
|
4726
4762
|
const usage = compressed ? GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_COPY_SRC : GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_COPY_SRC;
|
|
4727
4763
|
return ok$1({
|
|
4728
|
-
width
|
|
4729
|
-
height
|
|
4730
|
-
|
|
4764
|
+
width,
|
|
4765
|
+
height,
|
|
4766
|
+
shape: tex.shape,
|
|
4767
|
+
depthOrArrayLayers: tex.shape.viewDimension === "2d" ? 1 : tex.shape.viewDimension === "2d-array" ? tex.shape.extent.layers : tex.shape.extent.depth,
|
|
4768
|
+
...deriveTextureExtent(tex.format, width, height),
|
|
4731
4769
|
format: tex.format,
|
|
4732
4770
|
mipLevelCount,
|
|
4733
4771
|
usage,
|
|
@@ -4746,13 +4784,59 @@ function deriveRenderDataCubemap(source) {
|
|
|
4746
4784
|
})
|
|
4747
4785
|
);
|
|
4748
4786
|
}
|
|
4787
|
+
const sourceHeight = source.shape.viewDimension === "2d" ? source.shape.extent.height : 0;
|
|
4749
4788
|
return ok$1({
|
|
4750
|
-
cubeFaceSize:
|
|
4789
|
+
cubeFaceSize: sourceHeight,
|
|
4751
4790
|
outputFormat: "rgba16float",
|
|
4752
4791
|
needsHalfConversion: source.format === "rgba32float",
|
|
4753
4792
|
cubeUsage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_COPY_SRC
|
|
4754
4793
|
});
|
|
4755
4794
|
}
|
|
4795
|
+
|
|
4796
|
+
// src/targets/material-source.ts
|
|
4797
|
+
var sourceBindings = /* @__PURE__ */ new WeakMap();
|
|
4798
|
+
function sourceToken() {
|
|
4799
|
+
return Object.freeze({});
|
|
4800
|
+
}
|
|
4801
|
+
function resolveRenderTargetMaterialSource(source) {
|
|
4802
|
+
return sourceBindings.get(source);
|
|
4803
|
+
}
|
|
4804
|
+
function invalid(field, value, expected) {
|
|
4805
|
+
return {
|
|
4806
|
+
ok: false,
|
|
4807
|
+
error: new RenderTargetDescriptorInvalidError({ field, value, expected })
|
|
4808
|
+
};
|
|
4809
|
+
}
|
|
4810
|
+
function createRenderTargetMaterialSource(target, descriptor, options) {
|
|
4811
|
+
if (options.aspect !== "color") return invalid("aspect", options.aspect, "color");
|
|
4812
|
+
if (options.dimension !== descriptor.shape) {
|
|
4813
|
+
return invalid("dimension", options.dimension, `dimension matches ${descriptor.shape}`);
|
|
4814
|
+
}
|
|
4815
|
+
const mipCount = descriptor.mipLevels === 1 ? 1 : Math.floor(Math.log2(Math.max(descriptor.width, descriptor.height))) + 1;
|
|
4816
|
+
if (!Number.isInteger(options.mipLevel) || options.mipLevel < 0 || options.mipLevel >= mipCount) {
|
|
4817
|
+
return invalid("mipLevel", options.mipLevel, `an admitted mip level below ${mipCount}`);
|
|
4818
|
+
}
|
|
4819
|
+
if (!Number.isInteger(options.generation) || options.generation < 0) {
|
|
4820
|
+
return invalid("generation", options.generation, "a non-negative device generation");
|
|
4821
|
+
}
|
|
4822
|
+
const result = {
|
|
4823
|
+
ok: true,
|
|
4824
|
+
value: Object.freeze({
|
|
4825
|
+
source: sourceToken(),
|
|
4826
|
+
target,
|
|
4827
|
+
generation: options.generation,
|
|
4828
|
+
shape: descriptor.shape,
|
|
4829
|
+
format: descriptor.format,
|
|
4830
|
+
view: Object.freeze({
|
|
4831
|
+
dimension: options.dimension,
|
|
4832
|
+
mipLevel: options.mipLevel,
|
|
4833
|
+
resolveRequired: descriptor.sampleCount === 4
|
|
4834
|
+
})
|
|
4835
|
+
})
|
|
4836
|
+
};
|
|
4837
|
+
sourceBindings.set(result.value.source, result.value);
|
|
4838
|
+
return result;
|
|
4839
|
+
}
|
|
4756
4840
|
var STANDARD_PBR_UBO_SIZE = 384;
|
|
4757
4841
|
var MATERIAL_PER_ENTITY_STRIDE = 512;
|
|
4758
4842
|
function resolveSurfaceProfile(storageFormat, viewFormat, facts) {
|
|
@@ -4865,6 +4949,11 @@ function selectSwapChainFormat(storageBufferCapable, surfaceViewFormats = true)
|
|
|
4865
4949
|
}
|
|
4866
4950
|
|
|
4867
4951
|
// src/record/main-pass-material.ts
|
|
4952
|
+
function selectMaterialGroup2(clusterGroup, meshGroup, group2Contract) {
|
|
4953
|
+
if (group2Contract === "cluster") return clusterGroup;
|
|
4954
|
+
if (group2Contract === "mesh") return meshGroup;
|
|
4955
|
+
return null;
|
|
4956
|
+
}
|
|
4868
4957
|
var DERIVED_PARAM_SCHEMA_CACHE = /* @__PURE__ */ new WeakMap();
|
|
4869
4958
|
function derivedParamSchema(schema) {
|
|
4870
4959
|
const cached = DERIVED_PARAM_SCHEMA_CACHE.get(schema);
|
|
@@ -5081,13 +5170,21 @@ var DEFAULT_STANDARD_TEXTURE_COORDINATES = new Float32Array([
|
|
|
5081
5170
|
]);
|
|
5082
5171
|
function materialTextureUvScale(texture) {
|
|
5083
5172
|
if (texture === void 0) return [1, 1];
|
|
5084
|
-
return deriveTextureExtent(
|
|
5173
|
+
return deriveTextureExtent(
|
|
5174
|
+
texture.format,
|
|
5175
|
+
texture.shape.extent.width,
|
|
5176
|
+
texture.shape.extent.height
|
|
5177
|
+
).uvScale;
|
|
5085
5178
|
}
|
|
5086
5179
|
function materialTextureForField(material, field) {
|
|
5087
5180
|
if (field === "emissiveTexture") return material.emissiveTexture;
|
|
5088
5181
|
if (field === "occlusionTexture") return material.occlusionTexture;
|
|
5089
5182
|
return material.textureHandles?.get(field);
|
|
5090
5183
|
}
|
|
5184
|
+
function materialRenderTargetSourceForField(material, field, resolveSource) {
|
|
5185
|
+
const source = material.textureSources?.get(field);
|
|
5186
|
+
return source === void 0 ? void 0 : resolveSource?.(source) ?? resolveRenderTargetMaterialSource(source);
|
|
5187
|
+
}
|
|
5091
5188
|
function materialUboFloatView(payload) {
|
|
5092
5189
|
if (payload instanceof Float32Array) return payload;
|
|
5093
5190
|
return payload instanceof Uint8Array ? new Float32Array(payload.buffer, payload.byteOffset, payload.byteLength / 4) : new Float32Array(payload);
|
|
@@ -5179,7 +5276,11 @@ function userRegionTextureFieldOrder(schema) {
|
|
|
5179
5276
|
const fields = [...derivedParamSchema(schema).textureFieldNames];
|
|
5180
5277
|
return fields;
|
|
5181
5278
|
}
|
|
5182
|
-
function defaultViewForUserRegionField(field, pipelineState) {
|
|
5279
|
+
function defaultViewForUserRegionField(field, pipelineState, schema) {
|
|
5280
|
+
const parameter = schema?.find((entry) => entry.name === field);
|
|
5281
|
+
if (parameter?.type === "texture_cube") {
|
|
5282
|
+
return pipelineState.skylightFallback?.prefilterView ?? pipelineState.defaultWhiteTextureView;
|
|
5283
|
+
}
|
|
5183
5284
|
if (field === "normalTexture") return pipelineState.defaultNormalTextureView;
|
|
5184
5285
|
if (field === "baseColorTexture") return pipelineState.fallbackTextureView;
|
|
5185
5286
|
return pipelineState.defaultWhiteTextureView;
|
|
@@ -5289,11 +5390,14 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
|
|
|
5289
5390
|
materialSlice,
|
|
5290
5391
|
videoHighPerfAvailable,
|
|
5291
5392
|
skylightResources,
|
|
5393
|
+
resolveRenderTargetTextureSource,
|
|
5394
|
+
resolveReflectionProbeResources,
|
|
5292
5395
|
materialBgShared,
|
|
5293
5396
|
materialBgAssemblyCache,
|
|
5294
5397
|
transmissionBackdropView,
|
|
5295
5398
|
bindGroupCounts
|
|
5296
5399
|
} = deps;
|
|
5400
|
+
const effectiveSkylightResources = resolveReflectionProbeResources?.(materialWorld, entityKey) ?? skylightResources;
|
|
5297
5401
|
const smMaterialHandle = submeshMaterial.materialHandle;
|
|
5298
5402
|
const materialCacheKey = smMaterialHandle === void 0 ? void 0 : `${materialWorld.identity}:${smMaterialHandle}`;
|
|
5299
5403
|
const smHasVideoFields = (submeshMaterial.videoTextureFields?.size ?? 0) > 0;
|
|
@@ -5308,7 +5412,7 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
|
|
|
5308
5412
|
submeshMaterial,
|
|
5309
5413
|
smMaterialBgl,
|
|
5310
5414
|
pipelineState.materialUniformBuffer.buffer,
|
|
5311
|
-
|
|
5415
|
+
effectiveSkylightResources,
|
|
5312
5416
|
store.materialResourceEpoch,
|
|
5313
5417
|
transmissionBackdropView
|
|
5314
5418
|
)) {
|
|
@@ -5343,9 +5447,17 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
|
|
|
5343
5447
|
const field = smUserRegionFields[fi];
|
|
5344
5448
|
const samplerBinding = 1 + fi * 2;
|
|
5345
5449
|
const textureBinding = samplerBinding + 1;
|
|
5346
|
-
let smView = field !== void 0 ? defaultViewForUserRegionField(field, pipelineState) : pipelineState.defaultWhiteTextureView;
|
|
5450
|
+
let smView = field !== void 0 ? defaultViewForUserRegionField(field, pipelineState, smSchema) : pipelineState.defaultWhiteTextureView;
|
|
5347
5451
|
const smVideoClip = field !== void 0 ? submeshMaterial.videoTextureFields?.get(field) : void 0;
|
|
5348
|
-
|
|
5452
|
+
const smTargetSource = field === void 0 ? void 0 : materialRenderTargetSourceForField(
|
|
5453
|
+
submeshMaterial,
|
|
5454
|
+
field,
|
|
5455
|
+
resolveRenderTargetTextureSource
|
|
5456
|
+
);
|
|
5457
|
+
if (smTargetSource !== void 0) {
|
|
5458
|
+
if (smTargetSource.textureView !== void 0) smView = smTargetSource.textureView;
|
|
5459
|
+
else materialResourcesResident = false;
|
|
5460
|
+
} else if (smVideoClip !== void 0) {
|
|
5349
5461
|
const view = videoTextureView(
|
|
5350
5462
|
materialWorld,
|
|
5351
5463
|
runtime.dynamicTextureStore,
|
|
@@ -5374,11 +5486,15 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
|
|
|
5374
5486
|
}
|
|
5375
5487
|
);
|
|
5376
5488
|
}
|
|
5377
|
-
const transmissionSampler = transmissionBackdropView != null ?
|
|
5378
|
-
const smMergedEntries = assembleMaterialWithSkylightEntries(
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5489
|
+
const transmissionSampler = transmissionBackdropView != null ? effectiveSkylightResources.prefilterSampler : pipelineState.defaultSampler;
|
|
5490
|
+
const smMergedEntries = assembleMaterialWithSkylightEntries(
|
|
5491
|
+
smBaseEntries,
|
|
5492
|
+
effectiveSkylightResources,
|
|
5493
|
+
{
|
|
5494
|
+
sampler: transmissionSampler,
|
|
5495
|
+
...transmissionBackdropView === void 0 ? {} : { backdropView: transmissionBackdropView }
|
|
5496
|
+
}
|
|
5497
|
+
);
|
|
5382
5498
|
const createBindGroup = () => {
|
|
5383
5499
|
const result = runtime.device.createBindGroup({
|
|
5384
5500
|
label: "pbr-material-skylight-bg",
|
|
@@ -5403,7 +5519,7 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
|
|
|
5403
5519
|
materialResourceEpoch: store.materialResourceEpoch,
|
|
5404
5520
|
materialBgl: smMaterialBgl,
|
|
5405
5521
|
materialBuffer: pipelineState.materialUniformBuffer.buffer,
|
|
5406
|
-
skylightResources,
|
|
5522
|
+
skylightResources: effectiveSkylightResources,
|
|
5407
5523
|
bindGroup: smBindGroup
|
|
5408
5524
|
});
|
|
5409
5525
|
}
|
|
@@ -5464,9 +5580,11 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5464
5580
|
msaaActive,
|
|
5465
5581
|
validatedOrdered,
|
|
5466
5582
|
splitLdrSprite,
|
|
5467
|
-
viewBindGroup
|
|
5583
|
+
viewBindGroup,
|
|
5584
|
+
viewBindGroupDynamicOffset = 0
|
|
5468
5585
|
} = c;
|
|
5469
5586
|
const colorFormatOverride = c.transparentColorFormat;
|
|
5587
|
+
const clusteredLighting = c.standardLighting?.kind === "clustered";
|
|
5470
5588
|
const isHdrTarget = tonemapActive || colorFormatOverride === "rgba16float";
|
|
5471
5589
|
let lastVertexBuffer = null;
|
|
5472
5590
|
let lastIndexBuffer = null;
|
|
@@ -5485,7 +5603,6 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5485
5603
|
const profilePhase = c.profilePhase;
|
|
5486
5604
|
const materialGroup1DynamicOffsets = new Uint32Array(1);
|
|
5487
5605
|
const meshGroup2DynamicOffsets = [0];
|
|
5488
|
-
const skinGroup2DynamicOffsets = [0, 0];
|
|
5489
5606
|
let lastStencilReference = null;
|
|
5490
5607
|
const materialPipelineLookupCache = /* @__PURE__ */ new Map();
|
|
5491
5608
|
const resolveMaterialPipeline = (materialShaderId, renderState, topology, indexFormat, variantSet, colorFormat, layoutProjection) => {
|
|
@@ -5505,7 +5622,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5505
5622
|
return null;
|
|
5506
5623
|
}
|
|
5507
5624
|
const resolvedVariantSet = resolvedVariantSetResult.value;
|
|
5508
|
-
const resolvePipeline = () => runtime.
|
|
5625
|
+
const resolvePipeline = () => runtime.getMaterialShaderPipelineEntry?.(
|
|
5509
5626
|
materialShaderId,
|
|
5510
5627
|
isHdrTarget,
|
|
5511
5628
|
renderState,
|
|
@@ -5552,7 +5669,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5552
5669
|
for (let i = 0; i < validatedOrdered.length; i++) {
|
|
5553
5670
|
const entry = validatedOrdered[i];
|
|
5554
5671
|
if (entry === void 0) continue;
|
|
5555
|
-
const pointsLinesSubmission = c.pointsLines?.prepare(entry,
|
|
5672
|
+
const pointsLinesSubmission = c.pointsLines?.prepare(entry, clusteredLighting);
|
|
5556
5673
|
if (entry.source.pointsLines !== void 0 && pointsLinesSubmission?.plan.drawCount !== 1)
|
|
5557
5674
|
continue;
|
|
5558
5675
|
if (passKind === "forward" && c.gpuDrivenEntityKeys.has(worldEntityKey(entry.source.worldId, entry.source.entityKey))) {
|
|
@@ -5578,7 +5695,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5578
5695
|
entry.source.entityKey,
|
|
5579
5696
|
entry.world ?? c.world
|
|
5580
5697
|
);
|
|
5581
|
-
const
|
|
5698
|
+
const pointsLinesPipelineEntry = resolveMaterialPipeline(
|
|
5582
5699
|
POINTS_LINES_MATERIAL_SHADER_ID,
|
|
5583
5700
|
{
|
|
5584
5701
|
...pointsLinesMaterial.renderState,
|
|
@@ -5590,7 +5707,8 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5590
5707
|
colorFormatOverride,
|
|
5591
5708
|
pointsLinesSubmission.layoutProjection
|
|
5592
5709
|
);
|
|
5593
|
-
if (
|
|
5710
|
+
if (pointsLinesPipelineEntry === null) continue;
|
|
5711
|
+
const pointsLinesPipeline = pointsLinesPipelineEntry.pipeline;
|
|
5594
5712
|
if (pipelineState.pointsLinesViewBuffer === void 0) continue;
|
|
5595
5713
|
writePointsLinesViewUbo(
|
|
5596
5714
|
runtime.device.queue,
|
|
@@ -5605,9 +5723,9 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5605
5723
|
pass.setBindGroup(
|
|
5606
5724
|
0,
|
|
5607
5725
|
viewBindGroup,
|
|
5608
|
-
new Uint32Array([i * POINTS_LINES_VIEW_SLOT_STRIDE]),
|
|
5726
|
+
new Uint32Array([0, i * POINTS_LINES_VIEW_SLOT_STRIDE]),
|
|
5609
5727
|
0,
|
|
5610
|
-
|
|
5728
|
+
2
|
|
5611
5729
|
);
|
|
5612
5730
|
pass.setPipeline(pointsLinesPipeline);
|
|
5613
5731
|
pass.setVertexBuffer(0, pointsLinesSubmission.vertexBuffer);
|
|
@@ -5622,7 +5740,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5622
5740
|
identityInstanceDraws[0]?.instanceBindGroup ?? resolveGeometryInstancesBindGroup(c, identityInstanceBuffer)
|
|
5623
5741
|
);
|
|
5624
5742
|
recordPointsLinesDraw(pass, pointsLinesSubmission.plan);
|
|
5625
|
-
pass.setBindGroup(0, viewBindGroup, [0]);
|
|
5743
|
+
pass.setBindGroup(0, viewBindGroup, [viewBindGroupDynamicOffset, 0]);
|
|
5626
5744
|
continue;
|
|
5627
5745
|
}
|
|
5628
5746
|
if (entry.mesh.vertexBuffer !== lastVertexBuffer) {
|
|
@@ -5644,14 +5762,22 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5644
5762
|
const isSkinEntry = entry.source.skin !== void 0;
|
|
5645
5763
|
const skinAllocator = pipelineState.skinPaletteAllocator;
|
|
5646
5764
|
const skinSlice = entry.source.skin;
|
|
5647
|
-
const
|
|
5648
|
-
|
|
5765
|
+
const clusteredSkin = clusteredLighting;
|
|
5766
|
+
const skinMeshBindGroupLayout = clusteredSkin ? pipelineState.hdrpSkinMeshBindGroupLayout : pipelineState.pbrSkinMeshBindGroupLayout;
|
|
5767
|
+
const hdrpSkinBuffers = isSkinEntry && clusteredSkin ? getOrCreateHdrpBuffers(runtime, frameState.installedPipelineConfig?.clusterGrid) : null;
|
|
5768
|
+
const skinResources = isSkinEntry && skinMeshBindGroupLayout !== null && skinAllocator !== null && skinSlice !== void 0 && (!clusteredSkin || hdrpSkinBuffers !== null) ? {
|
|
5769
|
+
meshArrayBgl: skinMeshBindGroupLayout,
|
|
5649
5770
|
paletteBuffer: skinSlice.buffer,
|
|
5650
|
-
paletteBindingWindowBytes: skinAllocator.bindingWindowBytes
|
|
5771
|
+
paletteBindingWindowBytes: skinAllocator.bindingWindowBytes,
|
|
5772
|
+
hdrpBuffers: hdrpSkinBuffers
|
|
5651
5773
|
} : null;
|
|
5652
5774
|
const skinVariantSetResult = variantSetFromVertexLayoutProjection(
|
|
5653
5775
|
entry.mesh.layoutProjection,
|
|
5654
|
-
|
|
5776
|
+
standardTopologyVariantSet(
|
|
5777
|
+
c.standardLighting,
|
|
5778
|
+
runtime.device.caps.storageBuffer,
|
|
5779
|
+
entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === "color")
|
|
5780
|
+
)
|
|
5655
5781
|
);
|
|
5656
5782
|
if (!skinVariantSetResult.ok) runtime.errorRegistry.fire(skinVariantSetResult.error);
|
|
5657
5783
|
const skinVariantSet = skinVariantSetResult.ok ? skinVariantSetResult.value : void 0;
|
|
@@ -5678,45 +5804,45 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5678
5804
|
const skinMissesBefore = bindGroupCounts.createBindGroup;
|
|
5679
5805
|
const skinBindGroup = getOrCreateFromChain(
|
|
5680
5806
|
frameState.meshBindGroupCache,
|
|
5681
|
-
[
|
|
5807
|
+
[
|
|
5808
|
+
pipelineState.meshStorageBuffer.buffer,
|
|
5809
|
+
skinResources.paletteBuffer,
|
|
5810
|
+
...skinResources.hdrpBuffers === null ? [] : [
|
|
5811
|
+
skinResources.hdrpBuffers.lightDataBuffer,
|
|
5812
|
+
skinResources.hdrpBuffers.clusterGridBuffer,
|
|
5813
|
+
skinResources.hdrpBuffers.lightIndexListBuffer,
|
|
5814
|
+
skinResources.hdrpBuffers.clusterUniformBuffer
|
|
5815
|
+
]
|
|
5816
|
+
],
|
|
5682
5817
|
"pbr-skin-mesh",
|
|
5683
5818
|
() => {
|
|
5819
|
+
if (skinResources.hdrpBuffers !== null) {
|
|
5820
|
+
const result2 = createHdrpSkinUnifiedBindGroup(
|
|
5821
|
+
runtime,
|
|
5822
|
+
skinResources.hdrpBuffers,
|
|
5823
|
+
skinResources.meshArrayBgl,
|
|
5824
|
+
pipelineState.meshStorageBuffer.buffer,
|
|
5825
|
+
skinResources.paletteBuffer,
|
|
5826
|
+
skinResources.paletteBindingWindowBytes
|
|
5827
|
+
);
|
|
5828
|
+
if (result2 === null) {
|
|
5829
|
+
throw new RhiError({
|
|
5830
|
+
code: "webgpu-runtime-error",
|
|
5831
|
+
expected: "HDRP clustered skin BindGroup creation succeeds",
|
|
5832
|
+
hint: "inspect prior errorRegistry events for createBindGroup failure detail"
|
|
5833
|
+
});
|
|
5834
|
+
}
|
|
5835
|
+
return result2;
|
|
5836
|
+
}
|
|
5684
5837
|
const result = runtime.device.createBindGroup({
|
|
5685
5838
|
label: "pbr-skin-mesh-bg",
|
|
5686
5839
|
layout: skinResources.meshArrayBgl,
|
|
5687
|
-
entries:
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
buffer: pipelineState.meshStorageBuffer.buffer,
|
|
5694
|
-
offset: 0,
|
|
5695
|
-
size: meshBindSize
|
|
5696
|
-
}
|
|
5697
|
-
}
|
|
5698
|
-
},
|
|
5699
|
-
{
|
|
5700
|
-
binding: 1,
|
|
5701
|
-
resource: {
|
|
5702
|
-
kind: "buffer",
|
|
5703
|
-
value: {
|
|
5704
|
-
buffer: skinResources.paletteBuffer,
|
|
5705
|
-
offset: 0,
|
|
5706
|
-
// M6 SSOT: static BG entry size (= MAX_JOINTS * 64 =
|
|
5707
|
-
// 16320 B) sourced from the allocator. The per-draw
|
|
5708
|
-
// window slides via `group2DynamicOffsets[1]`
|
|
5709
|
-
// (= entry.source.skin.byteOffset) so this size
|
|
5710
|
-
// stays at the worst case across all skin entries
|
|
5711
|
-
// -- one BG covers every skinned draw in the frame
|
|
5712
|
-
// (m3-1b miss=1 + hit=N-1 contract). The allocator
|
|
5713
|
-
// guarantees `buffer.size >= byteOffset + this size`
|
|
5714
|
-
// so dynOffset[1] passes WebGPU validation.
|
|
5715
|
-
size: skinResources.paletteBindingWindowBytes
|
|
5716
|
-
}
|
|
5717
|
-
}
|
|
5718
|
-
}
|
|
5719
|
-
]
|
|
5840
|
+
entries: createPbrSkinMeshBindGroupEntries(
|
|
5841
|
+
pipelineState.meshStorageBuffer.buffer,
|
|
5842
|
+
meshBindSize,
|
|
5843
|
+
skinResources.paletteBuffer,
|
|
5844
|
+
skinResources.paletteBindingWindowBytes
|
|
5845
|
+
)
|
|
5720
5846
|
});
|
|
5721
5847
|
if (!result.ok) throw result.error;
|
|
5722
5848
|
return result.value;
|
|
@@ -5728,9 +5854,10 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5728
5854
|
else skinStats.hit += 1;
|
|
5729
5855
|
}
|
|
5730
5856
|
group2BindGroup = skinBindGroup;
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5857
|
+
group2DynamicOffsets = pbrSkinMeshDynamicOffsets(
|
|
5858
|
+
i * MESH_PER_ENTITY_STRIDE,
|
|
5859
|
+
entry.source.skin?.byteOffset ?? 0
|
|
5860
|
+
);
|
|
5734
5861
|
} else if (isSkinEntry) {
|
|
5735
5862
|
continue;
|
|
5736
5863
|
}
|
|
@@ -5780,6 +5907,8 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5780
5907
|
} : geometryRenderStateForTopology(smTopology, submeshMaterial.renderState);
|
|
5781
5908
|
const pipelineRenderState = geometryRenderStateForPass(basePipelineRenderState, passKind);
|
|
5782
5909
|
let smPipelineHandle;
|
|
5910
|
+
let materialGroup2Contract;
|
|
5911
|
+
let materialPipelineEntry = null;
|
|
5783
5912
|
if (smMaterialShaderId === void 0 || smMaterialShaderId === "forgeax::default-unlit") {
|
|
5784
5913
|
const unlitShaderId = smMaterialShaderId ?? "forgeax::default-unlit";
|
|
5785
5914
|
const unlitProjectionVariantResult = variantSetFromVertexLayoutProjection(
|
|
@@ -5791,7 +5920,10 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5791
5920
|
continue;
|
|
5792
5921
|
}
|
|
5793
5922
|
const unlitHasColor = unlitProjectionVariantResult.value === "VERTEX_COLOR_AVAILABLE=true";
|
|
5794
|
-
const unlitVariantSet =
|
|
5923
|
+
const unlitVariantSet = standardStorageVariantSet(
|
|
5924
|
+
runtime.device.caps.storageBuffer,
|
|
5925
|
+
unlitHasColor
|
|
5926
|
+
);
|
|
5795
5927
|
const unlitRsp = resolveMaterialPipeline(
|
|
5796
5928
|
unlitShaderId,
|
|
5797
5929
|
pipelineRenderState,
|
|
@@ -5801,12 +5933,17 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5801
5933
|
colorFormatOverride,
|
|
5802
5934
|
entry.mesh.layoutProjection
|
|
5803
5935
|
);
|
|
5804
|
-
|
|
5936
|
+
materialPipelineEntry = unlitRsp;
|
|
5937
|
+
smPipelineHandle = unlitRsp?.pipeline ?? (colorFormatOverride === void 0 ? selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive) : null);
|
|
5805
5938
|
} else if (smMaterialShaderId !== void 0) {
|
|
5806
5939
|
const hasVertexColor = entry.mesh.layoutProjection.attributes.some(
|
|
5807
5940
|
(attribute) => attribute.key === "color"
|
|
5808
5941
|
);
|
|
5809
|
-
const capabilityVariantSet =
|
|
5942
|
+
const capabilityVariantSet = standardTopologyVariantSet(
|
|
5943
|
+
c.standardLighting,
|
|
5944
|
+
runtime.device.caps.storageBuffer,
|
|
5945
|
+
hasVertexColor
|
|
5946
|
+
);
|
|
5810
5947
|
const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? capabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
|
|
5811
5948
|
const projectionVariantSetResult = isSpriteShader ? { ok: true, value: variantSet } : variantSetFromVertexLayoutProjection(entry.mesh.layoutProjection, variantSet);
|
|
5812
5949
|
if (!projectionVariantSetResult.ok) {
|
|
@@ -5822,23 +5959,24 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
|
|
|
5822
5959
|
colorFormatOverride,
|
|
5823
5960
|
entry.mesh.layoutProjection
|
|
5824
5961
|
);
|
|
5825
|
-
|
|
5962
|
+
materialPipelineEntry = cachedPipeline;
|
|
5963
|
+
smPipelineHandle = cachedPipeline?.pipeline ?? null;
|
|
5826
5964
|
} else {
|
|
5827
5965
|
smPipelineHandle = selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive);
|
|
5828
5966
|
}
|
|
5829
5967
|
if (smPipelineHandle === null) {
|
|
5830
5968
|
continue;
|
|
5831
5969
|
}
|
|
5832
|
-
const usesStandardClusterBindings = smMaterialShaderId === "forgeax::default-standard-pbr";
|
|
5833
5970
|
if (!isSkinEntry) {
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5971
|
+
materialGroup2Contract = materialPipelineEntry?.group2Contract;
|
|
5972
|
+
if (materialGroup2Contract === void 0) continue;
|
|
5973
|
+
const selectedGroup = selectMaterialGroup2(
|
|
5974
|
+
meshGroup2,
|
|
5975
|
+
meshBindGroup,
|
|
5976
|
+
materialGroup2Contract
|
|
5977
|
+
);
|
|
5978
|
+
if (selectedGroup === null) continue;
|
|
5979
|
+
group2BindGroup = selectedGroup;
|
|
5842
5980
|
}
|
|
5843
5981
|
pass.setBindGroup(2, group2BindGroup, group2DynamicOffsets);
|
|
5844
5982
|
if (profilePhase === void 0) {
|
|
@@ -5876,6 +6014,7 @@ function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws) {
|
|
|
5876
6014
|
const { runtime, pipelineState, frameState } = c;
|
|
5877
6015
|
const inst = entry.source.instances;
|
|
5878
6016
|
if (inst === void 0) return identityInstanceDraws;
|
|
6017
|
+
if (inst.instanceCount === 0) return [];
|
|
5879
6018
|
let instanceBuffer = pipelineState.identityInstanceBuffer;
|
|
5880
6019
|
let instanceCount = 1;
|
|
5881
6020
|
{
|
|
@@ -6095,8 +6234,13 @@ function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCo
|
|
|
6095
6234
|
ldrSpriteColorView,
|
|
6096
6235
|
geometryDepthView,
|
|
6097
6236
|
viewBindGroup,
|
|
6237
|
+
meshBindGroup,
|
|
6238
|
+
hdrpClusterBindGroup,
|
|
6239
|
+
viewBindGroupDynamicOffset = 0,
|
|
6098
6240
|
splitLdrSprite
|
|
6099
6241
|
} = c;
|
|
6242
|
+
const clusteredLighting = c.standardLighting?.kind === "clustered";
|
|
6243
|
+
const meshGroup2 = clusteredLighting ? hdrpClusterBindGroup : meshBindGroup;
|
|
6100
6244
|
const spriteIsHdr = c.tonemapActive || transparentPassColorFormat(c, pipelineState) === "rgba16float";
|
|
6101
6245
|
let geometryPassEnded = false;
|
|
6102
6246
|
if (splitLdrSprite && (graphPass !== void 0 || ldrSpritePassView !== null)) {
|
|
@@ -6124,7 +6268,7 @@ function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCo
|
|
|
6124
6268
|
{ colorLoadOp: "load", depthLoadOp: "load" }
|
|
6125
6269
|
)
|
|
6126
6270
|
);
|
|
6127
|
-
spritePass.setBindGroup(0, viewBindGroup, [0]);
|
|
6271
|
+
spritePass.setBindGroup(0, viewBindGroup, [viewBindGroupDynamicOffset, 0]);
|
|
6128
6272
|
const spritePremulBlend = {
|
|
6129
6273
|
depthWriteEnabled: false,
|
|
6130
6274
|
depthCompare: "less-equal",
|
|
@@ -6180,7 +6324,12 @@ function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCo
|
|
|
6180
6324
|
spritePremulBlend,
|
|
6181
6325
|
"triangle-list",
|
|
6182
6326
|
void 0,
|
|
6183
|
-
|
|
6327
|
+
// sprite-lit declares the same Standard capability axes as PBR. The
|
|
6328
|
+
// boot artifact is intentionally the direct compatibility variant,
|
|
6329
|
+
// so a clustered frame must request its explicit Cluster artifact;
|
|
6330
|
+
// otherwise the host binds the unified group(2) while the shader
|
|
6331
|
+
// still reads the now-cleared direct-light headers.
|
|
6332
|
+
standardTopologyVariantSet(c.standardLighting, runtime.device.caps.storageBuffer, false),
|
|
6184
6333
|
"forward",
|
|
6185
6334
|
void 0,
|
|
6186
6335
|
msaaActive ? 4 : 1,
|
|
@@ -6202,14 +6351,15 @@ function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCo
|
|
|
6202
6351
|
matchedIndices,
|
|
6203
6352
|
materialSlotIndices,
|
|
6204
6353
|
sampleCount,
|
|
6205
|
-
resolveMaterialBindGroup
|
|
6354
|
+
resolveMaterialBindGroup,
|
|
6355
|
+
meshGroup2
|
|
6206
6356
|
);
|
|
6207
6357
|
if (graphPass === void 0) spritePass.end();
|
|
6208
6358
|
}
|
|
6209
6359
|
return geometryPassEnded;
|
|
6210
6360
|
}
|
|
6211
|
-
function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, materialSlotIndices, sampleCount, resolveMaterialBindGroup) {
|
|
6212
|
-
const { runtime, pipelineState, frameState, bindGroupCounts, validatedOrdered
|
|
6361
|
+
function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, materialSlotIndices, sampleCount, resolveMaterialBindGroup, meshGroup2) {
|
|
6362
|
+
const { runtime, pipelineState, frameState, bindGroupCounts, validatedOrdered } = c;
|
|
6213
6363
|
const isHdrTarget = c.tonemapActive || transparentPassColorFormat(c, pipelineState) === "rgba16float";
|
|
6214
6364
|
let lastPbrSubPipelineHandle = null;
|
|
6215
6365
|
let lastPbrSubVertexBuffer = null;
|
|
@@ -6232,7 +6382,6 @@ function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, material
|
|
|
6232
6382
|
spritePass.setIndexBuffer(entry.mesh.indexBuffer.handle, entry.mesh.indexFormat);
|
|
6233
6383
|
lastPbrSubIndexBuffer = entry.mesh.indexBuffer;
|
|
6234
6384
|
}
|
|
6235
|
-
spritePass.setBindGroup(2, meshBindGroup, [i * MESH_PER_ENTITY_STRIDE]);
|
|
6236
6385
|
const identityInstBg = getOrCreatePerEntity(
|
|
6237
6386
|
frameState.instancesBgPerEntity,
|
|
6238
6387
|
worldEntityKey(entry.source.worldId, entry.source.entityKey),
|
|
@@ -6258,7 +6407,11 @@ function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, material
|
|
|
6258
6407
|
bindGroupCounts
|
|
6259
6408
|
);
|
|
6260
6409
|
spritePass.setBindGroup(3, identityInstBg);
|
|
6261
|
-
const subVariantSet =
|
|
6410
|
+
const subVariantSet = standardTopologyVariantSet(
|
|
6411
|
+
c.standardLighting,
|
|
6412
|
+
runtime.device.caps.storageBuffer,
|
|
6413
|
+
entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === "color")
|
|
6414
|
+
);
|
|
6262
6415
|
const projectionVariantSetResult = variantSetFromVertexLayoutProjection(
|
|
6263
6416
|
entry.mesh.layoutProjection,
|
|
6264
6417
|
subVariantSet
|
|
@@ -6276,15 +6429,7 @@ function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, material
|
|
|
6276
6429
|
if (submeshMaterial.transparent !== true) continue;
|
|
6277
6430
|
const smShaderId = submeshMaterial.materialShaderId;
|
|
6278
6431
|
if (smShaderId === void 0) continue;
|
|
6279
|
-
const
|
|
6280
|
-
const subBg = resolveMaterialBindGroup(
|
|
6281
|
-
materialSlot,
|
|
6282
|
-
submeshMaterial,
|
|
6283
|
-
entry.source.entityKey,
|
|
6284
|
-
entry.world ?? c.world
|
|
6285
|
-
);
|
|
6286
|
-
spritePass.setBindGroup(1, subBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
|
|
6287
|
-
const subPipeline = runtime.getMaterialShaderPipeline?.(
|
|
6432
|
+
const subPipelineEntry = c.runtime.getMaterialShaderPipelineEntry?.(
|
|
6288
6433
|
smShaderId,
|
|
6289
6434
|
isHdrTarget,
|
|
6290
6435
|
submeshMaterial.renderState,
|
|
@@ -6301,7 +6446,20 @@ function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, material
|
|
|
6301
6446
|
void 0,
|
|
6302
6447
|
entry.mesh.layoutProjection
|
|
6303
6448
|
) ?? null;
|
|
6304
|
-
if (
|
|
6449
|
+
if (subPipelineEntry === null) continue;
|
|
6450
|
+
const submeshContract = subPipelineEntry.group2Contract;
|
|
6451
|
+
const submeshGroup2 = selectMaterialGroup2(meshGroup2, c.meshBindGroup, submeshContract);
|
|
6452
|
+
if (submeshGroup2 === null) continue;
|
|
6453
|
+
spritePass.setBindGroup(2, submeshGroup2, [i * MESH_PER_ENTITY_STRIDE]);
|
|
6454
|
+
const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
|
|
6455
|
+
const subBg = resolveMaterialBindGroup(
|
|
6456
|
+
materialSlot,
|
|
6457
|
+
submeshMaterial,
|
|
6458
|
+
entry.source.entityKey,
|
|
6459
|
+
entry.world ?? c.world
|
|
6460
|
+
);
|
|
6461
|
+
spritePass.setBindGroup(1, subBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
|
|
6462
|
+
const subPipeline = subPipelineEntry.pipeline;
|
|
6305
6463
|
if (lastPbrSubPipelineHandle !== subPipeline) {
|
|
6306
6464
|
spritePass.setPipeline(subPipeline);
|
|
6307
6465
|
lastPbrSubPipelineHandle = subPipeline;
|
|
@@ -6533,6 +6691,7 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
|
|
|
6533
6691
|
);
|
|
6534
6692
|
spriteInstanceBuffer = _si.buffer;
|
|
6535
6693
|
spriteInstanceCount = _si.count;
|
|
6694
|
+
const spriteGroup2 = entityShaderId === "forgeax::sprite-lit" && c.standardLighting?.kind === "clustered" ? c.hdrpClusterBindGroup : meshBindGroup;
|
|
6536
6695
|
const spriteInstancesBg = getOrCreatePerEntity(
|
|
6537
6696
|
frameState.instancesBgPerEntity,
|
|
6538
6697
|
worldEntityKey(spriteEntry.source.worldId, spriteEntry.source.entityKey),
|
|
@@ -6557,7 +6716,7 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
|
|
|
6557
6716
|
},
|
|
6558
6717
|
bindGroupCounts
|
|
6559
6718
|
);
|
|
6560
|
-
spritePass.setBindGroup(2,
|
|
6719
|
+
spritePass.setBindGroup(2, spriteGroup2, [i * MESH_PER_ENTITY_STRIDE]);
|
|
6561
6720
|
const spriteTexHandle = spriteEntry.source.material.baseColorTexture;
|
|
6562
6721
|
let spriteTexView = pipelineState.defaultWhiteTextureView;
|
|
6563
6722
|
if (spriteTexHandle !== void 0) {
|
|
@@ -6725,6 +6884,7 @@ function ensureTypedShadowViewBg(c, viewOffset, cascadeOffset, variant) {
|
|
|
6725
6884
|
const { runtime, frameState, pipelineState } = c;
|
|
6726
6885
|
const shadowSampler = pipelineState.perPassResources.shadowSampler;
|
|
6727
6886
|
if (shadowSampler === null) return null;
|
|
6887
|
+
const projectorAvailable = pipelineState.projectorAvailable !== false;
|
|
6728
6888
|
try {
|
|
6729
6889
|
return getOrCreateFromChain(
|
|
6730
6890
|
frameState.viewBindGroupCache,
|
|
@@ -6738,6 +6898,7 @@ function ensureTypedShadowViewBg(c, viewOffset, cascadeOffset, variant) {
|
|
|
6738
6898
|
pipelineState.shadowParamsBuffer,
|
|
6739
6899
|
pipelineState.shadowCasterCascadeBuffer,
|
|
6740
6900
|
pipelineState.shadowFallbackTextureView,
|
|
6901
|
+
...projectorAvailable ? [pipelineState.defaultWhiteTextureView, pipelineState.defaultSampler] : [],
|
|
6741
6902
|
pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer
|
|
6742
6903
|
],
|
|
6743
6904
|
variant,
|
|
@@ -6808,7 +6969,20 @@ function ensureTypedShadowViewBg(c, viewOffset, cascadeOffset, variant) {
|
|
|
6808
6969
|
size: 80
|
|
6809
6970
|
}
|
|
6810
6971
|
}
|
|
6811
|
-
}
|
|
6972
|
+
},
|
|
6973
|
+
...projectorAvailable ? [
|
|
6974
|
+
{
|
|
6975
|
+
binding: 11,
|
|
6976
|
+
resource: {
|
|
6977
|
+
kind: "textureView",
|
|
6978
|
+
value: pipelineState.defaultWhiteTextureView
|
|
6979
|
+
}
|
|
6980
|
+
},
|
|
6981
|
+
{
|
|
6982
|
+
binding: 12,
|
|
6983
|
+
resource: { kind: "sampler", value: pipelineState.defaultSampler }
|
|
6984
|
+
}
|
|
6985
|
+
] : []
|
|
6812
6986
|
]
|
|
6813
6987
|
});
|
|
6814
6988
|
if (!created.ok) throw created.error;
|
|
@@ -6828,7 +7002,15 @@ function shadowShaderMap(c) {
|
|
|
6828
7002
|
const shaders = /* @__PURE__ */ new Map();
|
|
6829
7003
|
for (const entry of c.dispatch) {
|
|
6830
7004
|
if (entry.tags.LightMode === "ShadowCaster" && entry.materialShaderId !== void 0) {
|
|
6831
|
-
shaders.
|
|
7005
|
+
let byMaterial = shaders.get(entry.renderableIndex);
|
|
7006
|
+
if (byMaterial === void 0) {
|
|
7007
|
+
byMaterial = /* @__PURE__ */ new Map();
|
|
7008
|
+
shaders.set(entry.renderableIndex, byMaterial);
|
|
7009
|
+
}
|
|
7010
|
+
byMaterial.set(entry.materialHandle, {
|
|
7011
|
+
materialShaderId: entry.materialShaderId,
|
|
7012
|
+
renderState: entry.renderState
|
|
7013
|
+
});
|
|
6832
7014
|
}
|
|
6833
7015
|
}
|
|
6834
7016
|
return shaders;
|
|
@@ -6886,25 +7068,12 @@ function skinnedShadowMeshBindGroup(c, entry) {
|
|
|
6886
7068
|
const created = c.runtime.device.createBindGroup({
|
|
6887
7069
|
label: "shadow-pbr-skin-mesh-bg",
|
|
6888
7070
|
layout,
|
|
6889
|
-
entries:
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
buffer: c.pipelineState.meshStorageBuffer.buffer,
|
|
6896
|
-
size: c.runtime.device.caps.storageBuffer ? MESH_SSBO_BYTES : MESH_UBO_FULL_ARRAY_BYTES
|
|
6897
|
-
}
|
|
6898
|
-
}
|
|
6899
|
-
},
|
|
6900
|
-
{
|
|
6901
|
-
binding: 1,
|
|
6902
|
-
resource: {
|
|
6903
|
-
kind: "buffer",
|
|
6904
|
-
value: { buffer: skin.buffer, size: allocator.bindingWindowBytes }
|
|
6905
|
-
}
|
|
6906
|
-
}
|
|
6907
|
-
]
|
|
7071
|
+
entries: createPbrSkinMeshBindGroupEntries(
|
|
7072
|
+
c.pipelineState.meshStorageBuffer.buffer,
|
|
7073
|
+
c.runtime.device.caps.storageBuffer ? MESH_SSBO_BYTES : MESH_UBO_FULL_ARRAY_BYTES,
|
|
7074
|
+
skin.buffer,
|
|
7075
|
+
allocator.bindingWindowBytes
|
|
7076
|
+
)
|
|
6908
7077
|
});
|
|
6909
7078
|
if (!created.ok) throw created.error;
|
|
6910
7079
|
return created.value;
|
|
@@ -6926,7 +7095,7 @@ function encodeDirectionalShadowPass(c, pass, cascadeIndex, viewport) {
|
|
|
6926
7095
|
c.frameState.directionalShadowCacheRecorded = true;
|
|
6927
7096
|
pass.setViewport(viewport.x, viewport.y, viewport.w, viewport.h, 0, 1);
|
|
6928
7097
|
pass.setPipeline(pipeline);
|
|
6929
|
-
pass.setBindGroup(0, viewBg, [0]);
|
|
7098
|
+
pass.setBindGroup(0, viewBg, [0, 0]);
|
|
6930
7099
|
pass.setBindGroup(1, materialBg, [0]);
|
|
6931
7100
|
recordShadowCasterDraws(
|
|
6932
7101
|
c,
|
|
@@ -6950,7 +7119,7 @@ function encodePointShadowPass(c, pass, snapshotIndex, face) {
|
|
|
6950
7119
|
const materialBg = ensureSpotShadowMaterialBg(c);
|
|
6951
7120
|
if (pipeline === null || viewBg === null || materialBg === null) return;
|
|
6952
7121
|
pass.setPipeline(pipeline);
|
|
6953
|
-
pass.setBindGroup(0, viewBg, [0]);
|
|
7122
|
+
pass.setBindGroup(0, viewBg, [0, 0]);
|
|
6954
7123
|
pass.setBindGroup(1, materialBg, [0]);
|
|
6955
7124
|
recordShadowCasterDraws(
|
|
6956
7125
|
c,
|
|
@@ -6988,7 +7157,7 @@ function encodeSpotShadowPass(c, pass, snapshotIndex) {
|
|
|
6988
7157
|
1
|
|
6989
7158
|
);
|
|
6990
7159
|
pass.setPipeline(pipeline);
|
|
6991
|
-
pass.setBindGroup(0, viewBg, [0]);
|
|
7160
|
+
pass.setBindGroup(0, viewBg, [0, 0]);
|
|
6992
7161
|
pass.setBindGroup(1, materialBg, [0]);
|
|
6993
7162
|
recordShadowCasterDraws(
|
|
6994
7163
|
c,
|
|
@@ -7026,7 +7195,7 @@ function buildMatchedMaterialHandlesByRenderable(dispatch, selector) {
|
|
|
7026
7195
|
}
|
|
7027
7196
|
return handlesByRenderable;
|
|
7028
7197
|
}
|
|
7029
|
-
function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindGroup, matchedIndices,
|
|
7198
|
+
function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindGroup, matchedIndices, shadowDispatchByRenderableIdx) {
|
|
7030
7199
|
const { runtime, pipelineState, validatedOrdered } = c;
|
|
7031
7200
|
let shadowLastVertexBuffer = null;
|
|
7032
7201
|
let shadowLastIndexBuffer = null;
|
|
@@ -7035,32 +7204,13 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
|
|
|
7035
7204
|
const entry = validatedOrdered[i];
|
|
7036
7205
|
if (entry === void 0) continue;
|
|
7037
7206
|
if (matchedIndices !== null && !matchedIndices.has(entry.renderableIndex)) continue;
|
|
7038
|
-
const entryShadowShaderId = shadowShaderByRenderableIdx.get(entry.renderableIndex);
|
|
7039
|
-
let entryShadowPipeline = shadowPipeline2;
|
|
7040
7207
|
if (entry.source.skin !== void 0) {
|
|
7041
7208
|
const skinPipeline = skinnedShadowPipeline(c, entry);
|
|
7042
7209
|
if (skinPipeline === null) continue;
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
false,
|
|
7048
|
-
// isHdr — shadow depth pass is always LDR
|
|
7049
|
-
void 0,
|
|
7050
|
-
// renderState
|
|
7051
|
-
"triangle-list",
|
|
7052
|
-
// topology — shadow PSO targets triangle-list
|
|
7053
|
-
void 0,
|
|
7054
|
-
// indexFormat
|
|
7055
|
-
void 0,
|
|
7056
|
-
// variantSet — shadow caster has no variant axes
|
|
7057
|
-
"shadow-caster"
|
|
7058
|
-
// passKind
|
|
7059
|
-
) ?? shadowPipeline2;
|
|
7060
|
-
}
|
|
7061
|
-
if (entryShadowPipeline !== shadowLastPipeline && entryShadowPipeline !== null) {
|
|
7062
|
-
shadowPass.setPipeline(entryShadowPipeline);
|
|
7063
|
-
shadowLastPipeline = entryShadowPipeline;
|
|
7210
|
+
if (skinPipeline !== shadowLastPipeline) {
|
|
7211
|
+
shadowPass.setPipeline(skinPipeline);
|
|
7212
|
+
shadowLastPipeline = skinPipeline;
|
|
7213
|
+
}
|
|
7064
7214
|
}
|
|
7065
7215
|
const shadowSubmeshes = entry.mesh.submeshes;
|
|
7066
7216
|
const hasAnyShadowSubmesh = shadowSubmeshes.some(
|
|
@@ -7082,16 +7232,18 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
|
|
|
7082
7232
|
if (entry.source.skin !== void 0) {
|
|
7083
7233
|
const skinMeshBindGroup = skinnedShadowMeshBindGroup(c, entry);
|
|
7084
7234
|
if (skinMeshBindGroup === null) continue;
|
|
7085
|
-
shadowPass.setBindGroup(
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7235
|
+
shadowPass.setBindGroup(
|
|
7236
|
+
2,
|
|
7237
|
+
skinMeshBindGroup,
|
|
7238
|
+
pbrSkinMeshDynamicOffsets(i * MESH_PER_ENTITY_STRIDE, entry.source.skin.byteOffset)
|
|
7239
|
+
);
|
|
7089
7240
|
} else {
|
|
7090
7241
|
shadowPass.setBindGroup(2, shadowMeshBindGroup, [i * MESH_PER_ENTITY_STRIDE]);
|
|
7091
7242
|
}
|
|
7092
7243
|
let shadowInstanceBuffer = pipelineState.identityInstanceBuffer;
|
|
7093
7244
|
let shadowInstanceCount = 1;
|
|
7094
7245
|
const shadowInst = entry.source.instances;
|
|
7246
|
+
if (shadowInst?.instanceCount === 0) continue;
|
|
7095
7247
|
if (shadowInst !== void 0) {
|
|
7096
7248
|
const uniformFallback = runtime.device.caps.storageBuffer === false;
|
|
7097
7249
|
if (uniformFallback && shadowInst.instanceCount > MAX_UNIFORM_INSTANCES) {
|
|
@@ -7176,6 +7328,31 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
|
|
|
7176
7328
|
if (sm.topology !== "triangle-list" && sm.topology !== "triangle-strip") {
|
|
7177
7329
|
continue;
|
|
7178
7330
|
}
|
|
7331
|
+
const submeshMaterial = entry.source.materials[sm.materialSlot] ?? entry.source.material;
|
|
7332
|
+
const shadowDispatch = shadowDispatchByRenderableIdx.get(entry.renderableIndex)?.get(submeshMaterial.materialHandle ?? 0);
|
|
7333
|
+
const entryShadowShaderId = shadowDispatch?.materialShaderId;
|
|
7334
|
+
const entryShadowRenderState = shadowDispatch?.renderState;
|
|
7335
|
+
let entryShadowPipeline = entry.source.skin !== void 0 ? shadowLastPipeline : shadowPipeline2;
|
|
7336
|
+
if (entry.source.skin === void 0 && entryShadowShaderId !== void 0 && (entryShadowShaderId !== "forgeax::default-shadow-caster" || entryShadowRenderState !== void 0)) {
|
|
7337
|
+
entryShadowPipeline = runtime.getMaterialShaderPipeline?.(
|
|
7338
|
+
entryShadowShaderId,
|
|
7339
|
+
false,
|
|
7340
|
+
// isHdr — shadow depth pass is always LDR
|
|
7341
|
+
entryShadowRenderState,
|
|
7342
|
+
"triangle-list",
|
|
7343
|
+
// topology — shadow PSO targets triangle-list
|
|
7344
|
+
void 0,
|
|
7345
|
+
// indexFormat
|
|
7346
|
+
void 0,
|
|
7347
|
+
// variantSet — shadow caster has no variant axes
|
|
7348
|
+
"shadow-caster"
|
|
7349
|
+
// passKind
|
|
7350
|
+
) ?? shadowPipeline2;
|
|
7351
|
+
}
|
|
7352
|
+
if (entryShadowPipeline !== shadowLastPipeline && entryShadowPipeline !== null) {
|
|
7353
|
+
shadowPass.setPipeline(entryShadowPipeline);
|
|
7354
|
+
shadowLastPipeline = entryShadowPipeline;
|
|
7355
|
+
}
|
|
7179
7356
|
if (entry.mesh.indexed) {
|
|
7180
7357
|
shadowPass.drawIndexed(sm.indexCount, shadowInstanceCount, sm.indexOffset, 0, 0);
|
|
7181
7358
|
} else {
|
|
@@ -7281,6 +7458,7 @@ function recordMainPass(c, selector, options, graphPass) {
|
|
|
7281
7458
|
geometryDepthView,
|
|
7282
7459
|
validatedOrdered,
|
|
7283
7460
|
viewBindGroup,
|
|
7461
|
+
viewBindGroupDynamicOffset = 0,
|
|
7284
7462
|
meshBindGroup,
|
|
7285
7463
|
frameState,
|
|
7286
7464
|
bindGroupCounts,
|
|
@@ -7315,8 +7493,10 @@ function recordMainPass(c, selector, options, graphPass) {
|
|
|
7315
7493
|
const targetDepthView = options?.depthView ?? geometryDepthView;
|
|
7316
7494
|
const clearColor = options?.clearColor ?? clear;
|
|
7317
7495
|
const videoHighPerfAvailable = probeVideoHighPerfUpload(runtime.device);
|
|
7496
|
+
const clusteredLighting = c.standardLighting?.kind === "clustered";
|
|
7497
|
+
const clusteredBindGroupMissing = validatedOrdered.length > 0 && !standardTopologyBindGroupReady(c.standardLighting, hdrpClusterBindGroup);
|
|
7318
7498
|
let hdrpSsaoBindGroup = null;
|
|
7319
|
-
if (
|
|
7499
|
+
if (clusteredLighting && hdrpClusterBindGroup !== null && c.hdrpSsaoBlurredView !== void 0) {
|
|
7320
7500
|
const hdrpBuffers = getOrCreateHdrpBuffers(
|
|
7321
7501
|
runtime,
|
|
7322
7502
|
frameState.installedPipelineConfig?.clusterGrid
|
|
@@ -7330,7 +7510,7 @@ function recordMainPass(c, selector, options, graphPass) {
|
|
|
7330
7510
|
);
|
|
7331
7511
|
}
|
|
7332
7512
|
}
|
|
7333
|
-
const meshGroup2 =
|
|
7513
|
+
const meshGroup2 = clusteredLighting ? hdrpSsaoBindGroup ?? hdrpClusterBindGroup : meshBindGroup;
|
|
7334
7514
|
let geometryPassEnded = false;
|
|
7335
7515
|
const mainColorLoadOp = skyboxActive ? "load" : "clear";
|
|
7336
7516
|
const mainPassResolves = passKind === "forward" && msaaActive && geometryColorResolveView !== null && !splitLdrSprite;
|
|
@@ -7366,7 +7546,16 @@ function recordMainPass(c, selector, options, graphPass) {
|
|
|
7366
7546
|
...recordMode === "transmission" ? { variantSet: TRANSMISSION_VARIANT_SET } : {}
|
|
7367
7547
|
}))
|
|
7368
7548
|
};
|
|
7369
|
-
if (
|
|
7549
|
+
if (clusteredBindGroupMissing) {
|
|
7550
|
+
runtime.errorRegistry.fire(
|
|
7551
|
+
new RhiError({
|
|
7552
|
+
code: "webgpu-runtime-error",
|
|
7553
|
+
expected: "clustered Standard frame has a unified group(2) BindGroup",
|
|
7554
|
+
hint: "repair the clustered buffer/layout admission before retrying the frame; direct URP fallback is unsafe"
|
|
7555
|
+
})
|
|
7556
|
+
);
|
|
7557
|
+
}
|
|
7558
|
+
if (validatedOrdered.length > 0 && !clusteredBindGroupMissing) {
|
|
7370
7559
|
const MATERIAL_SLICE = STANDARD_PBR_UBO_SIZE;
|
|
7371
7560
|
const skylightFallback = pipelineState.skylightFallback;
|
|
7372
7561
|
if (skylightFallback === null) {
|
|
@@ -7417,6 +7606,31 @@ function recordMainPass(c, selector, options, graphPass) {
|
|
|
7417
7606
|
materialSlice: MATERIAL_SLICE,
|
|
7418
7607
|
videoHighPerfAvailable,
|
|
7419
7608
|
skylightResources,
|
|
7609
|
+
resolveRenderTargetTextureSource: runtime.resolveRenderTargetTextureSource,
|
|
7610
|
+
resolveReflectionProbeResources: (materialWorld, entityKey) => {
|
|
7611
|
+
const reflectionProbes = c.reflectionProbes;
|
|
7612
|
+
if (reflectionProbes === void 0) return void 0;
|
|
7613
|
+
const selection = reflectionProbes.selections.get(`${materialWorld.identity}:${entityKey}`);
|
|
7614
|
+
if (selection === void 0) return void 0;
|
|
7615
|
+
const binding = resolveReflectionProbeBinding(selection, reflectionProbes.table);
|
|
7616
|
+
if (binding.useSkylight || binding.probeIndex === void 0) return void 0;
|
|
7617
|
+
const row = reflectionProbes.table.rows.find(
|
|
7618
|
+
(candidate) => candidate.index === binding.probeIndex
|
|
7619
|
+
);
|
|
7620
|
+
if (row?.filteredView === void 0 || row.sampler === void 0 || row.uniformBuffer === void 0) {
|
|
7621
|
+
return void 0;
|
|
7622
|
+
}
|
|
7623
|
+
const probeResources = {
|
|
7624
|
+
irradianceView: skylightResources.irradianceView,
|
|
7625
|
+
irradianceSampler: skylightResources.irradianceSampler,
|
|
7626
|
+
prefilterView: row.filteredView,
|
|
7627
|
+
prefilterSampler: row.sampler,
|
|
7628
|
+
brdfLutView: skylightResources.brdfLutView,
|
|
7629
|
+
brdfLutSampler: skylightResources.brdfLutSampler,
|
|
7630
|
+
intensityBuffer: row.uniformBuffer
|
|
7631
|
+
};
|
|
7632
|
+
return probeResources;
|
|
7633
|
+
},
|
|
7420
7634
|
materialBgShared: frameState.materialBgShared,
|
|
7421
7635
|
materialBgAssemblyCache: c.materialBgAssemblyCache,
|
|
7422
7636
|
bindGroupCounts,
|
|
@@ -7525,7 +7739,7 @@ function recordMainPass(c, selector, options, graphPass) {
|
|
|
7525
7739
|
);
|
|
7526
7740
|
}
|
|
7527
7741
|
const resolveMaterialBindGroup = (materialSlot, material, entityKey, materialWorld = world) => material.materialShaderId !== POINTS_LINES_MATERIAL_SHADER_ID ? preparedMaterialBindGroups[materialSlot] ?? buildPerSubmeshMaterialBg2(material, entityKey, materialWorld) : buildPerSubmeshMaterialBg2(material, entityKey, materialWorld);
|
|
7528
|
-
pass.setBindGroup(0, viewBindGroup, [0]);
|
|
7742
|
+
pass.setBindGroup(0, viewBindGroup, [viewBindGroupDynamicOffset, 0]);
|
|
7529
7743
|
const recordGeometry = () => {
|
|
7530
7744
|
recordGeometryDraws(
|
|
7531
7745
|
recordContext,
|
|
@@ -7924,14 +8138,20 @@ function recordBloomCompositePass(_c, resolve, graphPass) {
|
|
|
7924
8138
|
function recordFxaaPass(c, resolve, graphPass) {
|
|
7925
8139
|
const { runtime, pipelineState, encoder, camera, currentTexture } = c;
|
|
7926
8140
|
const fxaaActive = camera.antialias === "fxaa";
|
|
7927
|
-
if (fxaaActive && pipelineState.perPassResources.fxaaPipeline !== null && pipelineState.perPassResources.fxaaBindGroupLayout !== null && pipelineState.perPassResources.fxaaSampler !== null) {
|
|
8141
|
+
if (fxaaActive && pipelineState.perPassResources.fxaaPipeline !== null && pipelineState.perPassResources.fxaaBindGroupLayout !== null && pipelineState.perPassResources.fxaaSampler !== null && runtime.getPostProcessParamsBuffer !== void 0) {
|
|
7928
8142
|
const inputView = resolve.resolve("ldrColor");
|
|
7929
8143
|
if (inputView === void 0) return;
|
|
8144
|
+
const fxaaParams = runtime.getPostProcessParamsBuffer(FXAA_POST_PROCESS_ID);
|
|
8145
|
+
if (fxaaParams === void 0) return;
|
|
8146
|
+
const params = new Float32Array(4);
|
|
8147
|
+
params[0] = resolveOutputDither(c.frameState.installedPipelineConfig) ? 1 : 0;
|
|
8148
|
+
const paramsWrite = runtime.device.queue.writeBuffer(fxaaParams, 0, params);
|
|
8149
|
+
if (!paramsWrite.ok) throw paramsWrite.error;
|
|
7930
8150
|
const fxaaBglLayout = pipelineState.perPassResources.fxaaBindGroupLayout;
|
|
7931
8151
|
const fxaaSampler = pipelineState.perPassResources.fxaaSampler;
|
|
7932
8152
|
const fxaaBg = getOrCreateFromChain(
|
|
7933
8153
|
c.frameState.postProcessBgCache,
|
|
7934
|
-
[inputView],
|
|
8154
|
+
[inputView, fxaaParams],
|
|
7935
8155
|
"fxaa",
|
|
7936
8156
|
() => {
|
|
7937
8157
|
const fxaaBgRes = runtime.device.createBindGroup({
|
|
@@ -7948,6 +8168,10 @@ function recordFxaaPass(c, resolve, graphPass) {
|
|
|
7948
8168
|
{
|
|
7949
8169
|
binding: 1,
|
|
7950
8170
|
resource: { kind: "sampler", value: fxaaSampler }
|
|
8171
|
+
},
|
|
8172
|
+
{
|
|
8173
|
+
binding: 2,
|
|
8174
|
+
resource: { kind: "buffer", value: { buffer: fxaaParams } }
|
|
7951
8175
|
}
|
|
7952
8176
|
]
|
|
7953
8177
|
});
|
|
@@ -7993,6 +8217,32 @@ var FULLSCREEN_DEFAULT_SPEC = Object.freeze({
|
|
|
7993
8217
|
geometry: { topology: "triangle-list", vertexLayout: {} },
|
|
7994
8218
|
renderState: void 0
|
|
7995
8219
|
});
|
|
8220
|
+
function postProcessSourceDigest(source) {
|
|
8221
|
+
let first = 2166136261;
|
|
8222
|
+
let second = 2654435769;
|
|
8223
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
8224
|
+
const code = source.charCodeAt(index);
|
|
8225
|
+
first = Math.imul(first ^ code, 16777619);
|
|
8226
|
+
second = Math.imul(second ^ code + index, 16777619);
|
|
8227
|
+
}
|
|
8228
|
+
return `${source.length.toString(16)}-${(first >>> 0).toString(16).padStart(8, "0")}-${(second >>> 0).toString(16).padStart(8, "0")}`;
|
|
8229
|
+
}
|
|
8230
|
+
function postProcessShaderPipelineLabel(identity, source) {
|
|
8231
|
+
return `post-process-${identity}-pso-${postProcessSourceDigest(source)}`;
|
|
8232
|
+
}
|
|
8233
|
+
function postProcessShaderModuleLabel(identity, source) {
|
|
8234
|
+
return `${postProcessShaderPipelineLabel(identity, source)}-module`;
|
|
8235
|
+
}
|
|
8236
|
+
function postProcessShaderEntrySignature(entry) {
|
|
8237
|
+
return JSON.stringify({
|
|
8238
|
+
source: entry.source,
|
|
8239
|
+
params: entry.params === void 0 ? void 0 : {
|
|
8240
|
+
byteSize: entry.params.byteSize,
|
|
8241
|
+
defaultValue: Array.from(entry.params.defaultValue)
|
|
8242
|
+
},
|
|
8243
|
+
reads: entry.reads
|
|
8244
|
+
});
|
|
8245
|
+
}
|
|
7996
8246
|
function isTemporalFullscreenBinding(values) {
|
|
7997
8247
|
return values.temporal !== void 0 || values.shader === "forgeax.taa-resolve" || values.shader === "forgeax.motion-blur";
|
|
7998
8248
|
}
|
|
@@ -8341,7 +8591,7 @@ function recordSsaoBlurPass(_c, resolveCtx, ssaoBlurredKey, ssaoRawKey, gbuf0Key
|
|
|
8341
8591
|
pass.draw(3, 1, 0, 0);
|
|
8342
8592
|
if (graphPass === void 0) pass.end();
|
|
8343
8593
|
}
|
|
8344
|
-
function dispatchFullscreenPass(ctx, name, shader, color, reads, resolveCtx, compositeOverSwapchain = false, rawSwapchainOutput = false, graphPass, graphOutputFormat, graphDepthView) {
|
|
8594
|
+
function dispatchFullscreenPass(ctx, name, shader, color, reads, resolveCtx, compositeOverSwapchain = false, rawSwapchainOutput = false, graphPass, graphOutputFormat, graphDepthView, paramsOverride) {
|
|
8345
8595
|
if (shader === "fxaa") {
|
|
8346
8596
|
if (resolveCtx === void 0) {
|
|
8347
8597
|
throw new PostProcessError({
|
|
@@ -8452,7 +8702,7 @@ function dispatchFullscreenPass(ctx, name, shader, color, reads, resolveCtx, com
|
|
|
8452
8702
|
if (entry.params !== void 0) {
|
|
8453
8703
|
const ubo = ctx.runtime.getPostProcessParamsBuffer?.(shader);
|
|
8454
8704
|
if (ubo !== void 0) {
|
|
8455
|
-
const data = ctx.postProcessParams.get(shader);
|
|
8705
|
+
const data = paramsOverride ?? ctx.postProcessParams.get(shader);
|
|
8456
8706
|
if (data !== void 0) {
|
|
8457
8707
|
if (data.byteLength !== entry.params.byteSize) {
|
|
8458
8708
|
throw new PostProcessError({
|
|
@@ -8499,9 +8749,7 @@ function dispatchFullscreenPass(ctx, name, shader, color, reads, resolveCtx, com
|
|
|
8499
8749
|
if (lookupPipeline === void 0) return;
|
|
8500
8750
|
const postColorFormat = writeFormat;
|
|
8501
8751
|
const pipeline = lookupPipeline(shader, built.bindGroupLayout, postColorFormat);
|
|
8502
|
-
if (pipeline === null)
|
|
8503
|
-
return;
|
|
8504
|
-
}
|
|
8752
|
+
if (pipeline === null) return;
|
|
8505
8753
|
const handle = built.createHandle(name, pipeline, paramsBuffer);
|
|
8506
8754
|
const pass = graphPass ?? ctx.encoder.beginRenderPass(
|
|
8507
8755
|
buildBeginRenderPassDescriptor(
|
|
@@ -8527,7 +8775,8 @@ function encodeFullscreenPass(ctx, pass, input) {
|
|
|
8527
8775
|
input.rawSwapchainOutput ?? false,
|
|
8528
8776
|
pass,
|
|
8529
8777
|
input.outputFormat,
|
|
8530
|
-
input.depthView
|
|
8778
|
+
input.depthView,
|
|
8779
|
+
input.paramsOverride
|
|
8531
8780
|
);
|
|
8532
8781
|
}
|
|
8533
8782
|
|
|
@@ -8847,7 +9096,14 @@ function addTypedScenePass(graph, options) {
|
|
|
8847
9096
|
internal.pipelineState,
|
|
8848
9097
|
internal.validated.length > 0 || options.gpuDriven !== void 0,
|
|
8849
9098
|
internal.bindGroupCounts,
|
|
8850
|
-
{
|
|
9099
|
+
{
|
|
9100
|
+
directionalShadow,
|
|
9101
|
+
spotShadow,
|
|
9102
|
+
projector: internal.spotLightProjector?.view ?? internal.volumetricFog?.projectorView,
|
|
9103
|
+
projectorSampler: internal.spotLightProjector?.sampler ?? internal.volumetricFog?.projectorSampler
|
|
9104
|
+
},
|
|
9105
|
+
true,
|
|
9106
|
+
internal.standardLighting
|
|
8851
9107
|
);
|
|
8852
9108
|
if (groups.viewBindGroup !== null) {
|
|
8853
9109
|
options.gpuDriven?.encode(groups.viewBindGroup, pass, resources);
|
|
@@ -9169,7 +9425,10 @@ function addTypedFullscreenPass(graph, options) {
|
|
|
9169
9425
|
}),
|
|
9170
9426
|
outputFormat: options.output.format,
|
|
9171
9427
|
...options.depth === void 0 ? {} : { depthView: resolvedDepthView(frame, resources, options.depth) },
|
|
9172
|
-
...options.rawSwapchainOutput === void 0 ? {} : { rawSwapchainOutput: options.rawSwapchainOutput }
|
|
9428
|
+
...options.rawSwapchainOutput === void 0 ? {} : { rawSwapchainOutput: options.rawSwapchainOutput },
|
|
9429
|
+
...options.paramsTransform === void 0 ? {} : {
|
|
9430
|
+
paramsOverride: options.paramsTransform(frame.postProcessParams.get(options.shader))
|
|
9431
|
+
}
|
|
9173
9432
|
});
|
|
9174
9433
|
}
|
|
9175
9434
|
});
|
|
@@ -9220,14 +9479,27 @@ function addTypedCompositePostEffects(graph, effects, input, output, depth, size
|
|
|
9220
9479
|
}
|
|
9221
9480
|
return ok(void 0);
|
|
9222
9481
|
}
|
|
9223
|
-
function addTypedOutputTransformPass(graph, input, output,
|
|
9482
|
+
function addTypedOutputTransformPass(graph, input, output, options = {}) {
|
|
9483
|
+
const outputOnly = options.outputOnly ?? false;
|
|
9224
9484
|
return addTypedFullscreenPass(graph, {
|
|
9225
9485
|
name: outputOnly ? "present" : "output-transform",
|
|
9226
9486
|
shader: STANDARD_OUTPUT_TRANSFORM_FEATURE_ID,
|
|
9227
9487
|
input,
|
|
9228
9488
|
output,
|
|
9229
9489
|
outputOnly,
|
|
9230
|
-
rawSwapchainOutput: outputOnly
|
|
9490
|
+
rawSwapchainOutput: outputOnly,
|
|
9491
|
+
...options.dither !== void 0 ? {
|
|
9492
|
+
paramsTransform: (params) => {
|
|
9493
|
+
if (params === void 0 || params.byteLength !== 16) return params;
|
|
9494
|
+
const transformed = params.slice();
|
|
9495
|
+
new DataView(
|
|
9496
|
+
transformed.buffer,
|
|
9497
|
+
transformed.byteOffset,
|
|
9498
|
+
transformed.byteLength
|
|
9499
|
+
).setFloat32(12, options.dither === true ? 1 : 0, true);
|
|
9500
|
+
return transformed;
|
|
9501
|
+
}
|
|
9502
|
+
} : {}
|
|
9231
9503
|
});
|
|
9232
9504
|
}
|
|
9233
9505
|
|
|
@@ -9318,6 +9590,6 @@ function addTaaResolvePass(graph, current, currentTemporal, previousColor, previ
|
|
|
9318
9590
|
});
|
|
9319
9591
|
}
|
|
9320
9592
|
|
|
9321
|
-
export { CAPTURE_VIEW_PROJS, CUBEMAP_FACE_VERTICES, DEFERRED_COLOR_FORMATS, DEPTH_MIN_PARAMS_BYTE_SIZE, FALLBACK_BYTES_PER_ROW, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_COPY_SRC, GPU_BUFFER_USAGE_INDEX, GPU_BUFFER_USAGE_INDIRECT, GPU_BUFFER_USAGE_STORAGE, GPU_BUFFER_USAGE_UNIFORM, GPU_BUFFER_USAGE_VERTEX, GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX,
|
|
9322
|
-
//# sourceMappingURL=chunk-
|
|
9323
|
-
//# sourceMappingURL=chunk-
|
|
9593
|
+
export { CAPTURE_VIEW_PROJS, CUBEMAP_FACE_VERTICES, CUBE_CAPTURE_VIEW_SLOT_BASE, DEFERRED_COLOR_FORMATS, DEPTH_MIN_PARAMS_BYTE_SIZE, FALLBACK_BYTES_PER_ROW, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_COPY_SRC, GPU_BUFFER_USAGE_INDEX, GPU_BUFFER_USAGE_INDIRECT, GPU_BUFFER_USAGE_MAP_READ, GPU_BUFFER_USAGE_QUERY_RESOLVE, GPU_BUFFER_USAGE_STORAGE, GPU_BUFFER_USAGE_UNIFORM, GPU_BUFFER_USAGE_VERTEX, GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX, GpuBuffer, GpuTexture, LIGHT_ARRAY_HEADER_BYTES, LIGHT_ARRAY_MAX_SLOTS, MATERIAL_PER_ENTITY_STRIDE, POINTS_LINES_MATERIAL_SHADER_ID, POINTS_LINES_VIEW_BUFFER_SIZE, POINT_LIGHT_STD430_BYTES, PREFILTER_MIP_LEVELS, PREFILTER_SIZE, PipelineSpecError, PostProcessError, REFLECTION_PROBE_VIEW_SLOT_BASE, SHADOW_CASTER_BUFFER_SIZE, SKIN_MATERIAL_SHADER_ID, SPOT_LIGHT_STD430_BYTES, TYPED_BLOOM_PASS_NAMES, VIEW_UNIFORM_BUFFER_SIZE, VIEW_UNIFORM_BYTES, VIEW_UNIFORM_SLOT_STRIDE, abortTemporalGpuSubmit, addStandardSceneDataPass, addTaaResolvePass, addTypedBloomPasses, addTypedCompositePostEffects, addTypedFrameObservationPass, addTypedFullscreenPass, addTypedOutputTransformPass, addTypedScenePass, addTypedSkyboxPass, addTypedSsaoPasses, addTypedTemporalResolvePass, aggregateTemporalDemand, applyParamSnapshotToUbo, assembleMaterialWithSkylightEntries, assertStorageBufferCap, buildBindGroupLayoutDescriptor, buildFoldDispatchPlan, buildFullscreenPostProcessPass, buildLinearLdrMaterialSpecTable, buildPbrMaterialUserRegionEntries, buildPbrPipelineLayouts, buildPbrSkinLayouts, buildPerFrameBindGroups, buildSpecConstTable, cacheKeyOf, cleanPerEntityCache, colorFormatsForPassKind, commitTemporalGpuSubmit, computeProjectionMatrix, computeSplitLdrSprite, computeViewMatrix, configureSurface, createClusterBinScratch, createFullscreenBindGroup, createHdrpBindGroupLayoutDescriptor, createHdrpClusterMembershipBindGroupLayoutDescriptor, createHdrpSkinBindGroupLayoutDescriptor, createIblPipelines, createPointsLinesLaneAdapter, createPointsLinesLaneContract, createRenderPipelineTarget, createRenderTargetMaterialSource, createSkylightFallback, createStandardSceneDataTarget, deriveRenderDataCubemap, deriveRenderDataMesh, deriveRenderDataTexture, describeTemporalDemand, driveLazyEquirectProjection, encodeDirectionalShadowPass, encodeMainPass, encodePointShadowPass, encodeSpotShadowPass, ensureMeshSsboCapacity, entryHasDepthRead, evaluateFoldBucketUniformCap, foldDispatchBuckets, geometryRenderStateForTopology, getOrBuildPipeline, getOrCreateHdrpBuffers, getOrCreateIblCache, getTemporalGpuState, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, incrementFoldedDrawsMetric, isStandardPbrMaterialShader, isTemporalFullscreenBinding, postProcessShaderEntrySignature, postProcessShaderModuleLabel, postProcessShaderPipelineLabel, prepareFrameLighting, resetHdrpBuffers, residentTextureView, resolveOutputDither, resolvePipelineGroup2Contract, resolveRenderTargetMaterialSource, resolveSkyboxActive, resolveSsaoParameters, resolveSurfaceFormatPair, resolveSurfaceProfile, retireTemporalGpuState, retireTemporalGpuStateAfterFence, runIblPrecompute, selectLazyEquirectHandle, selectPasses, selectSwapChainFormat, setIblComposedShaders, standardTemporalLaneAdmission, standardTemporalPostOrder, temporalReadIndex, temporalWriteIndex, typedFrameClearColor, uploadMeshSsboBatch, variantSetFromDefines, warnMultiSkybox, warnMultiSkylight, warnZeroLightStandard, writeHdrpClusterAndSsaoBuffers, writePointsLinesViewUbo, writeShadowCasterUniforms, writeShadowParamsBuffer, writeViewUbo };
|
|
9594
|
+
//# sourceMappingURL=chunk-5YADUCA7.mjs.map
|
|
9595
|
+
//# sourceMappingURL=chunk-5YADUCA7.mjs.map
|