@forgeax/engine-render 0.1.28 → 0.1.30
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 +856 -126
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/device-feature-admission.d.ts +4 -3
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-host.d.ts +27 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +3 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/gpu-driven-owner.d.ts +10 -0
- package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +14 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +31 -1
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +24 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -0
- package/dist/assembly/material-shader-policy.d.ts +3 -1
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
- package/dist/assembly/recovery/failure-location.d.ts +12 -0
- package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
- package/dist/assembly/recovery/recovery-attempt.d.ts +1 -6
- package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +7 -4
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
- package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-helpers.d.ts +3 -0
- package/dist/assembly/renderer-helpers.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +4 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +7 -1
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
- package/dist/assembly/skin-palette-owner.d.ts +8 -0
- package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-device-loss.d.ts +15 -0
- package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +7 -3
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +7 -141
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
- package/dist/authoring.mjs +2 -2
- package/dist/capture/scene-projection.d.ts +41 -0
- package/dist/capture/scene-projection.d.ts.map +1 -0
- package/dist/{chunk-J52NMQKR.mjs → chunk-7MQ3XBSP.mjs} +1098 -844
- package/dist/chunk-7MQ3XBSP.mjs.map +1 -0
- package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
- package/dist/chunk-BLXGONR2.mjs.map +1 -0
- package/dist/{chunk-VSL23LZE.mjs → chunk-C3DDSXYK.mjs} +1188 -463
- package/dist/chunk-C3DDSXYK.mjs.map +1 -0
- package/dist/{chunk-IQMKJLNU.mjs → chunk-FUUO4UND.mjs} +443 -30
- package/dist/chunk-FUUO4UND.mjs.map +1 -0
- package/dist/{chunk-J6KACNOH.mjs → chunk-JRBDO2KB.mjs} +617 -7
- package/dist/chunk-JRBDO2KB.mjs.map +1 -0
- package/dist/{chunk-GE3SDD72.mjs → chunk-MDQJPY3T.mjs} +20 -40
- package/dist/chunk-MDQJPY3T.mjs.map +1 -0
- package/dist/{chunk-MYJ5RPJO.mjs → chunk-NO4WZCAH.mjs} +3 -3
- package/dist/chunk-NO4WZCAH.mjs.map +1 -0
- package/dist/{chunk-RBDBZ5R7.mjs → chunk-PA5DRJNI.mjs} +2 -2
- package/dist/chunk-PA5DRJNI.mjs.map +1 -0
- package/dist/{chunk-MBU4ZVE7.mjs → chunk-PSEQ6QPH.mjs} +4655 -892
- package/dist/chunk-PSEQ6QPH.mjs.map +1 -0
- package/dist/{chunk-JAASCMXA.mjs → chunk-S5UE2T5P.mjs} +289 -41
- package/dist/chunk-S5UE2T5P.mjs.map +1 -0
- package/dist/components/camera.d.ts +62 -2
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/directional-light.d.ts +2 -2
- package/dist/components/dynamic-resolution.d.ts +15 -0
- package/dist/components/dynamic-resolution.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/instances.d.ts +6 -20
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/reflection-probe.d.ts +1 -0
- package/dist/components/reflection-probe.d.ts.map +1 -1
- package/dist/components/scene-instance.d.ts +2 -1
- package/dist/components/scene-instance.d.ts.map +1 -1
- package/dist/components/screen-space-reflection.d.ts +14 -0
- package/dist/components/screen-space-reflection.d.ts.map +1 -0
- package/dist/components/skylight.d.ts +4 -1
- package/dist/components/skylight.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +14809 -7570
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts +2 -0
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +3 -14
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/environment/background.d.ts +7 -0
- package/dist/environment/background.d.ts.map +1 -0
- package/dist/environment/ibl.d.ts +11 -0
- package/dist/environment/ibl.d.ts.map +1 -0
- package/dist/environment/storage.d.ts +14 -0
- package/dist/environment/storage.d.ts.map +1 -0
- package/dist/errors/gpu-driven.d.ts +6 -3
- package/dist/errors/gpu-driven.d.ts.map +1 -1
- package/dist/errors/render.d.ts +41 -4
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +7 -0
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +50 -3
- package/dist/extract/gpu-driven.d.ts.map +1 -1
- package/dist/features/host.d.ts +23 -1
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/noise-texture.d.ts +17 -0
- package/dist/features/noise-texture.d.ts.map +1 -0
- package/dist/features/plan.d.ts +83 -2
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +37 -4
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/features/prepared-graphics.d.ts +5 -0
- package/dist/features/prepared-graphics.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts +5 -3
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +11 -3
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +19 -3
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/types.d.ts +11 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +2 -0
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +34 -1
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts +24 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -0
- package/dist/gpu-driven/material-bindings.d.ts +7 -0
- package/dist/gpu-driven/material-bindings.d.ts.map +1 -0
- package/dist/gpu-driven/pbr-program.d.ts +10 -0
- package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
- package/dist/gpu-driven/prepared-draw.d.ts +2 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +169 -27
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-views.d.ts +126 -0
- package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +18 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-resource.d.ts +5 -2
- package/dist/gpu-resource.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts +2 -8
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +14 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-texture-usage.d.ts +1 -0
- package/dist/gpu-texture-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +9 -0
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +2 -0
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts +8 -0
- package/dist/ibl/frame-resources.d.ts.map +1 -0
- package/dist/ibl/skylight-bind-group.d.ts +3 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +87 -75
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +10 -8
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +180 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances-derived-bounds.d.ts +11 -0
- package/dist/instances-derived-bounds.d.ts.map +1 -1
- package/dist/instances.d.ts +89 -44
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +117 -9
- package/dist/internal.mjs.map +1 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/material-row.d.ts +15 -0
- package/dist/material-row.d.ts.map +1 -0
- package/dist/materials.d.ts +15 -0
- package/dist/materials.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +23 -5
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/render-extent.d.ts +51 -0
- package/dist/pipeline/render-extent.d.ts.map +1 -0
- package/dist/pipeline/standard-build-context.d.ts +7 -0
- package/dist/pipeline/standard-build-context.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts +3 -2
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
- package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
- package/dist/pipeline/standard-output/graph.d.ts +15 -0
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
- package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/resources.d.ts +10 -0
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
- package/dist/pipeline/standard-output/types.d.ts +39 -0
- package/dist/pipeline/standard-output/types.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +2 -2
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +2 -1
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +5 -2
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec-types.d.ts +4 -0
- package/dist/pipeline-spec-types.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +1 -9
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/points-lines/standard-owner.d.ts +1 -1
- package/dist/points-lines/standard-owner.d.ts.map +1 -1
- package/dist/post-process-errors.d.ts +9 -2
- package/dist/post-process-errors.d.ts.map +1 -1
- package/dist/prepare/extended-lighting/resources.d.ts +3 -3
- package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +3 -3
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +109 -13
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +41 -5
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +13 -9
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +43 -3
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +2 -10
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +14 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +15 -6
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts +19 -0
- package/dist/record/prepared-material-bindings.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts +1 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +101 -22
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +62 -7
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +39 -8
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +1 -0
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +10 -2
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/recovery/render-system-roots.d.ts +11 -0
- package/dist/recovery/render-system-roots.d.ts.map +1 -0
- package/dist/recovery/types.d.ts +76 -0
- package/dist/recovery/types.d.ts.map +1 -0
- package/dist/reflection/background.d.ts +14 -0
- package/dist/reflection/background.d.ts.map +1 -0
- package/dist/reflection/filter.d.ts.map +1 -1
- package/dist/reflection/projection.d.ts +1 -0
- package/dist/reflection/projection.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts +4 -4
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +211 -53
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +1 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +81 -6
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +15 -6
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +128 -41
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-projections.d.ts +22 -0
- package/dist/render-system-projections.d.ts.map +1 -0
- package/dist/render-system.d.ts +27 -77
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +14 -7
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +31 -11
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/shadow-visibility.d.ts +5 -0
- package/dist/scene/shadow-visibility.d.ts.map +1 -0
- package/dist/ssao-config.d.ts +8 -6
- package/dist/ssao-config.d.ts.map +1 -1
- package/dist/ssr/admission.d.ts +70 -1
- package/dist/ssr/admission.d.ts.map +1 -1
- package/dist/ssr/compose.d.ts +13 -0
- package/dist/ssr/compose.d.ts.map +1 -0
- package/dist/ssr/errors.d.ts +35 -0
- package/dist/ssr/errors.d.ts.map +1 -1
- package/dist/ssr/graph.d.ts +60 -0
- package/dist/ssr/graph.d.ts.map +1 -0
- package/dist/ssr/history.d.ts +97 -0
- package/dist/ssr/history.d.ts.map +1 -0
- package/dist/ssr/hiz.d.ts +37 -0
- package/dist/ssr/hiz.d.ts.map +1 -0
- package/dist/ssr/inspection.d.ts +42 -0
- package/dist/ssr/inspection.d.ts.map +1 -0
- package/dist/ssr/resources.d.ts +58 -0
- package/dist/ssr/resources.d.ts.map +1 -0
- package/dist/ssr/temporal.d.ts +38 -0
- package/dist/ssr/temporal.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +32 -10
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +15 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +9 -0
- package/dist/systems/transparent-dispatch.d.ts.map +1 -0
- package/dist/targets/material-source.d.ts +7 -0
- package/dist/targets/material-source.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts +7 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +8 -0
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/history.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -6
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts +4 -2
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts +15 -0
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -0
- package/dist/temporal/temporal-view.d.ts +6 -0
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/temporal/view.d.ts +6 -0
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +41 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +3 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/component.d.ts +1 -0
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +6 -0
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -20
- package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
- package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
- package/src/__tests__/auto-exposure.browser.test.ts +33 -0
- package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
- package/src/__tests__/capture-scene-projection.unit.test.ts +24 -0
- package/src/__tests__/detached-inspection.integration.test.ts +5 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +15 -24
- package/src/__tests__/device-loss-fanout.unit.test.ts +43 -0
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
- package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
- package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
- package/src/__tests__/dynamic-geometry-batch.unit.test.ts +157 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +36 -0
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +81 -0
- package/src/__tests__/factory-contract.integration.test.ts +231 -9
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
- package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
- package/src/__tests__/feature-noise.unit.test.ts +38 -0
- package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
- package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
- package/src/__tests__/frame-observation.contract.test.ts +21 -0
- package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
- package/src/__tests__/frame-targets.unit.test.ts +24 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +307 -7
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
- package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
- package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +261 -0
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +346 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1422 -0
- package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +1076 -106
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
- package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
- package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
- package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
- package/src/__tests__/gpu-driven-view.unit.test.ts +160 -52
- package/src/__tests__/gpu-pass-timing-browser-runner.ts +15 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +20 -2
- package/src/__tests__/gpu-resource-scope.unit.test.ts +71 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
- package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
- package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
- package/src/__tests__/gpu-scene.unit.test.ts +613 -33
- package/src/__tests__/instances-culling.integration.test.ts +36 -10
- package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
- package/src/__tests__/instances-store.unit.test.ts +242 -39
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
- package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
- package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
- package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
- package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
- package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
- package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
- package/src/__tests__/material-texture-source.unit.test.ts +87 -0
- package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +57 -3
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +8 -0
- package/src/__tests__/morph-neutral-admission.unit.test.ts +19 -0
- package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
- package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +42 -3
- package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
- package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -5
- package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
- package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +251 -9
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
- package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
- package/src/__tests__/public-surface.test-d.ts +3 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
- package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +171 -0
- package/src/__tests__/reflection-probe-authoring.unit.test.ts +12 -10
- package/src/__tests__/reflection-probe-box.dawn.test.ts +71 -0
- package/src/__tests__/reflection-probe-filter.unit.test.ts +9 -0
- package/src/__tests__/reflection-probe-orientation.dawn.test.ts +294 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +5 -14
- package/src/__tests__/reflection-probe-updates.unit.test.ts +129 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +7 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +27 -1
- package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
- package/src/__tests__/render-extent.unit.test.ts +63 -0
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
- package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
- package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
- package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
- package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
- package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +915 -23
- package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
- package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
- package/src/__tests__/render-target-material-source.test-d.ts +27 -3
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +233 -9
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
- package/src/__tests__/shader-manifest-fixture.ts +45 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
- package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
- package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +125 -14
- package/src/__tests__/specular-environment-scale.dawn.test.ts +158 -0
- package/src/__tests__/ssao-config.unit.test.ts +7 -0
- package/src/__tests__/ssao-surface.dawn.test.ts +252 -0
- package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
- package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
- package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
- package/src/__tests__/ssr-cube-silhouette.dawn.test.ts +270 -0
- package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +99 -0
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +116 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +951 -0
- package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
- package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
- package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
- package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
- package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
- package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
- package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
- package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
- package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
- package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
- package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
- package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
- package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
- package/src/__tests__/ssr-reprojection-probe.ts +90 -0
- package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
- package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
- package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
- package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
- package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
- package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +84 -12
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
- package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
- package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
- package/src/__tests__/standard-output.browser.test.ts +15 -0
- package/src/__tests__/standard-output.dawn.test.ts +15 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +275 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
- package/src/__tests__/standard-profile-oracle.unit.test.ts +49 -0
- package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
- package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
- package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
- package/src/__tests__/taa-coverage-edge.dawn.test.ts +199 -0
- package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +782 -0
- package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
- package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
- package/src/__tests__/taa-stability-feedback.ts +342 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
- package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
- package/src/__tests__/temporal-view.unit.test.ts +17 -0
- package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
- package/src/__tests__/transmission-prewarm.unit.test.ts +57 -0
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
- package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
- package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
- package/src/__tests__/visibility-extract.perf.test.ts +20 -8
- package/src/__tests__/visibility-instances.integration.test.ts +19 -7
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +62 -13
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +7 -7
- package/src/assembly/atmosphere-shader-prewarm.ts +39 -0
- package/src/assembly/device-feature-admission.ts +9 -4
- package/src/assembly/dynamic-geometry-host.ts +284 -163
- package/src/assembly/dynamic-geometry-runtime.ts +71 -0
- package/src/assembly/factory.ts +4 -0
- package/src/assembly/gpu-driven-owner.ts +29 -0
- package/src/assembly/host-contract.ts +19 -0
- package/src/assembly/material/assembly.ts +383 -1
- package/src/assembly/material/pipeline-helpers.ts +100 -0
- package/src/assembly/material-shader-policy.ts +110 -46
- package/src/assembly/recovery/device-loss-fanout.ts +5 -0
- package/src/assembly/recovery/failure-location.ts +62 -0
- package/src/assembly/recovery/recovery-attempt.ts +2 -29
- package/src/assembly/recovery/renderer-recover.ts +16 -17
- package/src/assembly/renderer-facade.ts +34 -1
- package/src/assembly/renderer-feature-inspection.ts +13 -0
- package/src/assembly/renderer-frame-transaction.ts +63 -0
- package/src/assembly/renderer-helpers.ts +26 -0
- package/src/assembly/renderer-inspection.ts +6 -0
- package/src/assembly/shader-prewarm-policy.ts +46 -1
- package/src/assembly/skin-palette-owner.ts +21 -0
- package/src/assembly/ssr-shader-prewarm.ts +41 -0
- package/src/assembly/webgpu-device-loss.ts +75 -0
- package/src/assembly/webgpu-pbr-ready.ts +232 -0
- package/src/assembly/webgpu-ready-mipmap.ts +26 -0
- package/src/assembly/webgpu-ready.ts +161 -131
- package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
- package/src/assembly/webgpu-renderer-contract.ts +69 -0
- package/src/assembly/webgpu-renderer-guards.ts +32 -0
- package/src/assembly/webgpu-renderer-observation.ts +43 -0
- package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
- package/src/assembly/webgpu-renderer.ts +744 -730
- package/src/assembly/webgpu-vertex-layouts.ts +233 -87
- package/src/capture/scene-projection.ts +37 -0
- package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
- package/src/components/__tests__/dynamic-resolution-schema.unit.test.ts +65 -0
- package/src/components/__tests__/light-radius.integration.test.ts +59 -0
- package/src/components/camera.ts +287 -7
- package/src/components/directional-light.ts +3 -3
- package/src/components/dynamic-resolution.ts +67 -0
- package/src/components/index.ts +2 -0
- package/src/components/instances.ts +45 -18
- package/src/components/light-helpers.ts +6 -1
- package/src/components/reflection-probe.ts +1 -0
- package/src/components/scene-instance.ts +2 -1
- package/src/components/screen-space-reflection.ts +21 -0
- package/src/components/skylight.ts +4 -1
- package/src/components/sprite-animation.ts +2 -2
- package/src/components/sprite-instances.ts +1 -1
- package/src/device/device-scope.ts +13 -7
- package/src/device/gpu-residency.ts +45 -42
- package/src/dynamic-geometry.ts +13 -8
- package/src/environment/background.ts +281 -0
- package/src/environment/ibl.ts +211 -0
- package/src/environment/storage.ts +63 -0
- package/src/errors/gpu-driven.ts +49 -8
- package/src/errors/render.ts +91 -3
- package/src/extract/camera.ts +37 -1
- package/src/extract/contracts.ts +10 -0
- package/src/extract/gpu-driven.ts +240 -10
- package/src/features/host.ts +237 -19
- package/src/features/noise-texture.ts +91 -0
- package/src/features/plan.ts +553 -24
- package/src/features/prepared-gpu-work.ts +631 -49
- package/src/features/prepared-graphics-store.ts +31 -0
- package/src/features/prepared-graphics.ts +5 -0
- package/src/features/render-graph-compute.ts +41 -8
- package/src/features/render-graph-contribution.ts +141 -3
- package/src/features/render-graph-raster.ts +143 -30
- package/src/features/types.ts +11 -0
- package/src/fullscreen-post-process-pass.ts +3 -0
- package/src/gpu-driven/batch-topology.ts +452 -108
- package/src/gpu-driven/material-artifacts.ts +179 -0
- package/src/gpu-driven/material-bindings.ts +41 -0
- package/src/gpu-driven/pbr-program.ts +31 -0
- package/src/gpu-driven/prepared-draw.ts +42 -6
- package/src/gpu-driven/production-raster.ts +2806 -716
- package/src/gpu-driven/shadow-views.ts +539 -0
- package/src/gpu-driven/view-gpu.ts +188 -41
- package/src/gpu-resource.ts +21 -2
- package/src/gpu-scene-schema.ts +61 -11
- package/src/gpu-scene.ts +197 -99
- package/src/gpu-texture-usage.ts +1 -0
- package/src/hdrp-buffers.ts +141 -36
- package/src/ibl/IblPipelineCache.ts +5 -0
- package/src/ibl/frame-resources.ts +38 -0
- package/src/ibl/skylight-bind-group.ts +32 -21
- package/src/index.ts +194 -75
- package/src/inspection-types.ts +207 -1
- package/src/instances-derived-bounds.ts +258 -34
- package/src/instances.ts +459 -150
- package/src/internal.ts +22 -0
- package/src/lifecycle.ts +2 -0
- package/src/material-row.ts +194 -0
- package/src/materials.ts +44 -8
- package/src/pbr-pipeline.ts +172 -19
- package/src/pipeline/render-extent.ts +111 -0
- package/src/pipeline/standard-build-context.ts +7 -0
- package/src/pipeline/standard-forward-lane.ts +74 -29
- package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
- package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
- package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
- package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
- package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
- package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
- package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
- package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
- package/src/pipeline/standard-output/color-transform.ts +124 -0
- package/src/pipeline/standard-output/graph.ts +139 -0
- package/src/pipeline/standard-output/lut-admission.ts +121 -0
- package/src/pipeline/standard-output/lut-gpu.ts +399 -0
- package/src/pipeline/standard-output/lut-state.ts +176 -0
- package/src/pipeline/standard-output/resources.ts +121 -0
- package/src/pipeline/standard-output/types.ts +58 -0
- package/src/pipeline/standard-pipeline.ts +232 -70
- package/src/pipeline/standard-post.ts +190 -27
- package/src/pipeline/standard-profile.ts +2 -1
- package/src/pipeline-builder.ts +28 -9
- package/src/pipeline-spec-types.ts +4 -0
- package/src/pipeline-spec.ts +37 -29
- package/src/plugin.ts +4 -0
- package/src/points-lines/standard-owner.ts +6 -14
- package/src/post-process-errors.ts +26 -18
- package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +2 -2
- package/src/prepare/extended-lighting/resources.ts +3 -3
- package/src/prepare/prepared-graphics-resolver.ts +9 -2
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +68 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +45 -0
- package/src/record/__tests__/material-slot-plan.unit.test.ts +57 -0
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +20 -1
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +41 -6
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
- package/src/record/dynamic-geometry-consumption.ts +59 -0
- package/src/record/frame-lighting.ts +15 -12
- package/src/record/frame-snapshot.ts +146 -34
- package/src/record/frame-targets.ts +9 -0
- package/src/record/frame.ts +838 -170
- package/src/record/main-pass-geometry.ts +209 -103
- package/src/record/main-pass-material.ts +157 -40
- package/src/record/main-pass-sprite-draws.ts +16 -107
- package/src/record/main-pass.ts +141 -12
- package/src/record/material-uniforms.ts +1 -1
- package/src/record/mesh-ssbo.ts +59 -15
- package/src/record/prepared-material-bindings.ts +238 -0
- package/src/record/recovery-pipeline.ts +69 -90
- package/src/record/render-context.ts +123 -24
- package/src/record/shadow-pass.ts +1389 -230
- package/src/record/skybox-post-pass.ts +14 -3
- package/src/record/typed-frame-graph.ts +1064 -47
- package/src/record/view-ubo.ts +12 -1
- package/src/recovery/render-system-candidate.ts +135 -68
- package/src/recovery/render-system-roots.ts +113 -0
- package/src/recovery/types.ts +77 -0
- package/src/reflection/background.ts +151 -0
- package/src/reflection/filter.ts +3 -0
- package/src/reflection/projection.ts +16 -6
- package/src/reflection/record-owner.ts +308 -140
- package/src/render-contract.ts +237 -49
- package/src/render-graph-primitives.ts +34 -5
- package/src/render-pipeline.ts +90 -2
- package/src/render-system-extract-tail.ts +431 -162
- package/src/render-system-extract.ts +478 -119
- package/src/render-system-projections.ts +241 -0
- package/src/render-system.ts +715 -828
- package/src/scene/render-scene-types.ts +13 -4
- package/src/scene/render-scene.ts +577 -355
- package/src/scene/shadow-visibility.ts +21 -0
- package/src/ssao-config.ts +25 -8
- package/src/ssr/admission.ts +380 -14
- package/src/ssr/compose.ts +144 -0
- package/src/ssr/errors.ts +48 -0
- package/src/ssr/graph.ts +797 -0
- package/src/ssr/history.ts +436 -0
- package/src/ssr/hiz.ts +124 -0
- package/src/ssr/inspection.ts +124 -0
- package/src/ssr/resources.ts +202 -0
- package/src/ssr/temporal.ts +164 -0
- package/src/systems/skin-palette-allocator.ts +495 -148
- package/src/systems/skin-palette-types.ts +17 -0
- package/src/systems/transparent-dispatch.ts +90 -0
- package/src/targets/material-source.ts +34 -0
- package/src/temporal/coverage.ts +1 -1
- package/src/temporal/frame-coordinator.ts +23 -2
- package/src/temporal/gpu.ts +79 -12
- package/src/temporal/history.ts +3 -1
- package/src/temporal/inspection.ts +2 -1
- package/src/temporal/standard-scene-data.ts +10 -2
- package/src/temporal/target-coverage-attachment.ts +72 -0
- package/src/temporal/temporal-view.ts +11 -2
- package/src/temporal/view.ts +12 -2
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +219 -139
- package/src/typed-render-graph-primitives.ts +304 -74
- package/src/typed-shadow-passes.ts +122 -12
- package/src/volume/component.ts +1 -0
- package/src/volume/passes.ts +3 -3
- package/src/volume/temporal.ts +10 -0
- package/dist/chunk-GE3SDD72.mjs.map +0 -1
- package/dist/chunk-GF523LHF.mjs.map +0 -1
- package/dist/chunk-IQMKJLNU.mjs.map +0 -1
- package/dist/chunk-J52NMQKR.mjs.map +0 -1
- package/dist/chunk-J6KACNOH.mjs.map +0 -1
- package/dist/chunk-JAASCMXA.mjs.map +0 -1
- package/dist/chunk-MBU4ZVE7.mjs.map +0 -1
- package/dist/chunk-MYJ5RPJO.mjs.map +0 -1
- package/dist/chunk-RBDBZ5R7.mjs.map +0 -1
- package/dist/chunk-VSL23LZE.mjs.map +0 -1
- package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
- package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
|
@@ -3,20 +3,22 @@ import { World } from '@forgeax/engine-ecs';
|
|
|
3
3
|
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
4
4
|
import { vec3 } from '@forgeax/engine-math';
|
|
5
5
|
import { RenderGraphBuilder } from '@forgeax/engine-render-graph';
|
|
6
|
-
import type
|
|
6
|
+
import { type BindGroup, RhiError, type RhiRenderPassEncoder } from '@forgeax/engine-rhi';
|
|
7
7
|
import { type RhiNullDevice, rhi } from '@forgeax/engine-rhi-null';
|
|
8
|
-
import { Transform } from '@forgeax/engine-scene';
|
|
8
|
+
import { propagateTransforms, Transform } from '@forgeax/engine-scene';
|
|
9
9
|
import {
|
|
10
10
|
createStandardPbrArtifactReceipt,
|
|
11
11
|
DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
|
|
12
12
|
DEFAULT_UNLIT_PARAM_SCHEMA,
|
|
13
13
|
} from '@forgeax/engine-shader';
|
|
14
14
|
import { ok } from '@forgeax/engine-types';
|
|
15
|
-
import { describe, expect, it } from 'vitest';
|
|
15
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
16
16
|
import { createRenderer as constructRenderer } from '../assembly/factory';
|
|
17
17
|
import { Camera, MeshFilter, MeshRenderer, PointLight } from '../components';
|
|
18
18
|
import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
19
|
+
import { gpuDrivenDrawKey, gpuDrivenShadowDrawKey } from '../extract/gpu-driven';
|
|
19
20
|
import { BatchTopology } from '../gpu-driven/batch-topology';
|
|
21
|
+
import { standardPbrProgramKey } from '../gpu-driven/pbr-program';
|
|
20
22
|
import {
|
|
21
23
|
adaptStandardPbrFrameResources,
|
|
22
24
|
GpuDrivenProduction,
|
|
@@ -31,10 +33,28 @@ import {
|
|
|
31
33
|
GPU_BUFFER_USAGE_UNIFORM,
|
|
32
34
|
GPU_BUFFER_USAGE_VERTEX,
|
|
33
35
|
} from '../gpu-usage';
|
|
36
|
+
import { buildDispatchPlan } from '../record/frame';
|
|
34
37
|
import { makeZeroCameraFallbackSnapshot, worldEntityKey } from '../record/frame-snapshot';
|
|
38
|
+
import {
|
|
39
|
+
encodeDirectionalShadowPass,
|
|
40
|
+
encodePointShadowPass,
|
|
41
|
+
encodeSpotShadowPass,
|
|
42
|
+
recordShadowCasterDraws,
|
|
43
|
+
shadowShaderMap,
|
|
44
|
+
} from '../record/shadow-pass';
|
|
35
45
|
import type { RenderPipelineFrame } from '../render-pipeline';
|
|
36
|
-
import type {
|
|
37
|
-
|
|
46
|
+
import type {
|
|
47
|
+
DispatchEntry,
|
|
48
|
+
MaterialSnapshot,
|
|
49
|
+
RenderableSnapshot,
|
|
50
|
+
ShadowCasterMembership,
|
|
51
|
+
} from '../render-system-extract';
|
|
52
|
+
import { shadowCasterCpuReason } from '../render-system-extract';
|
|
53
|
+
import { PersistentRenderScene, RenderScene } from '../scene/render-scene';
|
|
54
|
+
import {
|
|
55
|
+
preparedPbrSnapshot as preparedPbrFixture,
|
|
56
|
+
standardPbrInputs,
|
|
57
|
+
} from './gpu-driven-production-fixture';
|
|
38
58
|
|
|
39
59
|
const STANDARD_PBR_RECEIPT = createStandardPbrArtifactReceipt();
|
|
40
60
|
|
|
@@ -136,6 +156,27 @@ function snapshot(entityKey: number, assetHandle: number): RenderableSnapshot {
|
|
|
136
156
|
};
|
|
137
157
|
}
|
|
138
158
|
|
|
159
|
+
function preparedSnapshot(entityKey: number, assetHandle: number): RenderableSnapshot {
|
|
160
|
+
return preparedPbrFixture(snapshot(entityKey, assetHandle));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function withoutPrepared(
|
|
164
|
+
draw: NonNullable<RenderableSnapshot['gpuDrivenDraws']>[number],
|
|
165
|
+
): Omit<typeof draw, 'prepared'> {
|
|
166
|
+
const { prepared, ...rest } = draw;
|
|
167
|
+
void prepared;
|
|
168
|
+
return rest;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function updateSnapshot(value: RenderableSnapshot) {
|
|
172
|
+
return {
|
|
173
|
+
kind: 'update' as const,
|
|
174
|
+
worldId: value.worldId,
|
|
175
|
+
entityKey: value.entityKey,
|
|
176
|
+
snapshot: value,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
139
180
|
function mesh(device: RhiNullDevice, indexed = true, withLod = false): MeshGpuHandles {
|
|
140
181
|
const vertex = device
|
|
141
182
|
.createBuffer({
|
|
@@ -158,7 +199,6 @@ function mesh(device: RhiNullDevice, indexed = true, withLod = false): MeshGpuHa
|
|
|
158
199
|
iboBytes: indexed ? 8 : 0,
|
|
159
200
|
indexCount: indexed ? 3 : 0,
|
|
160
201
|
indexFormat: 'uint16',
|
|
161
|
-
layout: '12F',
|
|
162
202
|
layoutProjection: deriveVertexLayoutProjection({
|
|
163
203
|
position: new Float32Array(9),
|
|
164
204
|
normal: new Float32Array(9),
|
|
@@ -199,10 +239,11 @@ describe('GPU-driven production projection', () => {
|
|
|
199
239
|
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
200
240
|
})
|
|
201
241
|
.unwrap();
|
|
242
|
+
const pbrInputs = standardPbrInputs(device, shader, viewLayout);
|
|
202
243
|
const projection = new RenderScene();
|
|
203
244
|
const lodMeshGuid = '00000000-0000-7000-8000-000000000001' as never;
|
|
204
245
|
const makeLodSnapshot = (entityKey: number): RenderableSnapshot => {
|
|
205
|
-
const base = snapshot(entityKey, 3);
|
|
246
|
+
const base = preparedPbrFixture(snapshot(entityKey, 3));
|
|
206
247
|
const draw = base.gpuDrivenDraws?.[0];
|
|
207
248
|
if (draw === undefined) throw new Error('LOD test snapshot has no draw');
|
|
208
249
|
return {
|
|
@@ -212,8 +253,8 @@ describe('GPU-driven production projection', () => {
|
|
|
212
253
|
};
|
|
213
254
|
};
|
|
214
255
|
const delta = projection.apply([
|
|
215
|
-
|
|
216
|
-
|
|
256
|
+
updateSnapshot(makeLodSnapshot(1)),
|
|
257
|
+
updateSnapshot(makeLodSnapshot(2)),
|
|
217
258
|
]);
|
|
218
259
|
const availability = GpuScene.create(device, 2).unwrap();
|
|
219
260
|
expect(availability.status).toBe('available');
|
|
@@ -224,23 +265,28 @@ describe('GPU-driven production projection', () => {
|
|
|
224
265
|
const production = new GpuDrivenProduction(device, {
|
|
225
266
|
createShaderModule: () => ok(shader),
|
|
226
267
|
});
|
|
268
|
+
const lodGpuMesh = mesh(device, true, true);
|
|
227
269
|
const prepared = production
|
|
228
270
|
.prepare({
|
|
271
|
+
...pbrInputs,
|
|
229
272
|
scene: {
|
|
230
273
|
scene: availability.scene,
|
|
231
274
|
plan: topology.plan(),
|
|
232
275
|
slots: projection.slotsSnapshot(),
|
|
233
276
|
},
|
|
234
277
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
235
|
-
|
|
278
|
+
meshBySlot: new Map([
|
|
279
|
+
[0, lodGpuMesh],
|
|
280
|
+
[1, lodGpuMesh],
|
|
281
|
+
]),
|
|
236
282
|
viewBindGroupLayout: viewLayout,
|
|
237
283
|
meshResidencyEpoch: 1,
|
|
238
|
-
|
|
284
|
+
|
|
239
285
|
activeEntityKeys: new Set([worldEntityKey(0, 1)]),
|
|
240
286
|
activeEntityRevision: 1,
|
|
241
287
|
})
|
|
242
288
|
.unwrap();
|
|
243
|
-
expect(prepared?.
|
|
289
|
+
expect(prepared?.drawKeys).toEqual(new Set(['1:-1:0', '2:-1:0']));
|
|
244
290
|
// Both candidates are uploaded to the selector (2 * 304 bytes), while
|
|
245
291
|
// only the active entity is admitted into the compacted draw stream.
|
|
246
292
|
expect(production.inspect()).toMatchObject({
|
|
@@ -254,7 +300,7 @@ describe('GPU-driven production projection', () => {
|
|
|
254
300
|
.unwrap();
|
|
255
301
|
expect(projected.accesses.length).toBeGreaterThan(0);
|
|
256
302
|
expect(production.inspect()).toMatchObject({
|
|
257
|
-
batchBindGroupCreates:
|
|
303
|
+
batchBindGroupCreates: 0,
|
|
258
304
|
indirectDrawCount: 1,
|
|
259
305
|
});
|
|
260
306
|
expect(production.inspect().indirectDrawCount).toBeGreaterThan(0);
|
|
@@ -262,18 +308,50 @@ describe('GPU-driven production projection', () => {
|
|
|
262
308
|
production.inspect().batchCount,
|
|
263
309
|
);
|
|
264
310
|
|
|
311
|
+
// The scene/view buffers and visible range are unchanged on a warm
|
|
312
|
+
// projection. The raster owner must reuse the physical batch bind group;
|
|
313
|
+
// a new group is only valid after one of those handles or the range
|
|
314
|
+
// changes (resize/recovery/LOD admission).
|
|
315
|
+
const warm = production
|
|
316
|
+
.prepare({
|
|
317
|
+
...pbrInputs,
|
|
318
|
+
scene: {
|
|
319
|
+
scene: availability.scene,
|
|
320
|
+
plan: topology.plan(),
|
|
321
|
+
slots: projection.slotsSnapshot(),
|
|
322
|
+
},
|
|
323
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
324
|
+
meshBySlot: new Map([
|
|
325
|
+
[0, lodGpuMesh],
|
|
326
|
+
[1, lodGpuMesh],
|
|
327
|
+
]),
|
|
328
|
+
viewBindGroupLayout: viewLayout,
|
|
329
|
+
meshResidencyEpoch: 1,
|
|
330
|
+
|
|
331
|
+
activeEntityKeys: new Set([worldEntityKey(0, 1)]),
|
|
332
|
+
activeEntityRevision: 1,
|
|
333
|
+
})
|
|
334
|
+
.unwrap();
|
|
335
|
+
expect(warm?.topologySignature).toBe(prepared.topologySignature);
|
|
336
|
+
warm?.project(new RenderGraphBuilder<RenderPipelineFrame>(), 'rgba8unorm', 1).unwrap();
|
|
337
|
+
expect(production.inspect()).toMatchObject({ batchBindGroupCreates: 0 });
|
|
338
|
+
|
|
265
339
|
const suppressed = production
|
|
266
340
|
.prepare({
|
|
341
|
+
...pbrInputs,
|
|
267
342
|
scene: {
|
|
268
343
|
scene: availability.scene,
|
|
269
344
|
plan: topology.plan(),
|
|
270
345
|
slots: projection.slotsSnapshot(),
|
|
271
346
|
},
|
|
272
347
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
273
|
-
|
|
348
|
+
meshBySlot: new Map([
|
|
349
|
+
[0, lodGpuMesh],
|
|
350
|
+
[1, lodGpuMesh],
|
|
351
|
+
]),
|
|
274
352
|
viewBindGroupLayout: viewLayout,
|
|
275
353
|
meshResidencyEpoch: 1,
|
|
276
|
-
|
|
354
|
+
|
|
277
355
|
activeEntityKeys: new Set(),
|
|
278
356
|
activeEntityRevision: 2,
|
|
279
357
|
})
|
|
@@ -286,16 +364,20 @@ describe('GPU-driven production projection', () => {
|
|
|
286
364
|
|
|
287
365
|
const restored = production
|
|
288
366
|
.prepare({
|
|
367
|
+
...pbrInputs,
|
|
289
368
|
scene: {
|
|
290
369
|
scene: availability.scene,
|
|
291
370
|
plan: topology.plan(),
|
|
292
371
|
slots: projection.slotsSnapshot(),
|
|
293
372
|
},
|
|
294
373
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
295
|
-
|
|
374
|
+
meshBySlot: new Map([
|
|
375
|
+
[0, lodGpuMesh],
|
|
376
|
+
[1, lodGpuMesh],
|
|
377
|
+
]),
|
|
296
378
|
viewBindGroupLayout: viewLayout,
|
|
297
379
|
meshResidencyEpoch: 1,
|
|
298
|
-
|
|
380
|
+
|
|
299
381
|
activeEntityKeys: new Set([worldEntityKey(0, 1)]),
|
|
300
382
|
activeEntityRevision: 3,
|
|
301
383
|
})
|
|
@@ -306,16 +388,18 @@ describe('GPU-driven production projection', () => {
|
|
|
306
388
|
indirectDrawCount: 1,
|
|
307
389
|
});
|
|
308
390
|
expect(GPU_DRIVEN_VIEW_WGSL.indexOf('atomicAdd(&counters[lodCounterIndex')).toBeLessThan(
|
|
309
|
-
GPU_DRIVEN_VIEW_WGSL.indexOf('if (candidate.submitAdmission == 0u)'),
|
|
391
|
+
GPU_DRIVEN_VIEW_WGSL.indexOf('if ((candidate.submitAdmission & 1u) == 0u)'),
|
|
310
392
|
);
|
|
311
|
-
expect(
|
|
393
|
+
expect(
|
|
394
|
+
GPU_DRIVEN_VIEW_WGSL.indexOf('if ((candidate.submitAdmission & 1u) == 0u)'),
|
|
395
|
+
).toBeLessThan(
|
|
312
396
|
GPU_DRIVEN_VIEW_WGSL.indexOf('atomicAdd(&counters[counterIndex(candidate.batchIndex)], 1u)'),
|
|
313
397
|
);
|
|
314
398
|
production.dispose();
|
|
315
399
|
availability.scene.dispose();
|
|
316
400
|
});
|
|
317
401
|
|
|
318
|
-
it('keeps
|
|
402
|
+
it('keeps an all-suppressed candidate out of the GPU raster frame', async () => {
|
|
319
403
|
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
320
404
|
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
321
405
|
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
@@ -324,9 +408,10 @@ describe('GPU-driven production projection', () => {
|
|
|
324
408
|
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
325
409
|
})
|
|
326
410
|
.unwrap();
|
|
411
|
+
const pbrInputs = standardPbrInputs(device, shader, viewLayout);
|
|
327
412
|
const projection = new RenderScene();
|
|
328
|
-
const candidate = snapshot(1, 3);
|
|
329
|
-
const delta = projection.apply([
|
|
413
|
+
const candidate = preparedPbrFixture(snapshot(1, 3));
|
|
414
|
+
const delta = projection.apply([updateSnapshot(candidate)]);
|
|
330
415
|
const availability = GpuScene.create(device, 1).unwrap();
|
|
331
416
|
expect(availability.status).toBe('available');
|
|
332
417
|
if (availability.status !== 'available') return;
|
|
@@ -338,31 +423,29 @@ describe('GPU-driven production projection', () => {
|
|
|
338
423
|
});
|
|
339
424
|
const prepared = production
|
|
340
425
|
.prepare({
|
|
426
|
+
...pbrInputs,
|
|
341
427
|
scene: {
|
|
342
428
|
scene: availability.scene,
|
|
343
429
|
plan: topology.plan(),
|
|
344
430
|
slots: projection.slotsSnapshot(),
|
|
345
431
|
},
|
|
346
432
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
347
|
-
|
|
433
|
+
meshBySlot: new Map([[0, mesh(device)]]),
|
|
348
434
|
viewBindGroupLayout: viewLayout,
|
|
349
435
|
meshResidencyEpoch: 1,
|
|
350
|
-
|
|
436
|
+
|
|
351
437
|
activeEntityKeys: new Set(),
|
|
352
438
|
activeEntityRevision: 1,
|
|
353
439
|
})
|
|
354
440
|
.unwrap();
|
|
355
|
-
expect(prepared).
|
|
356
|
-
expect(production.requiresSceneRows()).toBe(true);
|
|
441
|
+
expect(prepared).toBeUndefined();
|
|
357
442
|
expect(production.inspect()).toMatchObject({
|
|
358
|
-
batchCount:
|
|
443
|
+
batchCount: 0,
|
|
359
444
|
indirectDrawCount: 0,
|
|
360
445
|
batchBindGroupCreates: 0,
|
|
361
446
|
});
|
|
362
|
-
if (prepared === undefined) return;
|
|
363
|
-
prepared.project(new RenderGraphBuilder<RenderPipelineFrame>(), 'rgba8unorm', 1).unwrap();
|
|
364
447
|
expect(production.inspect()).toMatchObject({
|
|
365
|
-
batchCount:
|
|
448
|
+
batchCount: 0,
|
|
366
449
|
indirectDrawCount: 0,
|
|
367
450
|
batchBindGroupCreates: 0,
|
|
368
451
|
});
|
|
@@ -379,13 +462,11 @@ describe('GPU-driven production projection', () => {
|
|
|
379
462
|
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
380
463
|
})
|
|
381
464
|
.unwrap();
|
|
465
|
+
const pbrInputs = standardPbrInputs(device, shader, viewLayout);
|
|
382
466
|
const projection = new RenderScene();
|
|
383
|
-
const first = snapshot(1, 3);
|
|
384
|
-
const second = { ...snapshot(2, 3), worldId: 1 };
|
|
385
|
-
const delta = projection.apply([
|
|
386
|
-
{ kind: 'create', snapshot: first },
|
|
387
|
-
{ kind: 'create', snapshot: second },
|
|
388
|
-
]);
|
|
467
|
+
const first = preparedPbrFixture(snapshot(1, 3));
|
|
468
|
+
const second = { ...preparedPbrFixture(snapshot(2, 3)), worldId: 1 };
|
|
469
|
+
const delta = projection.apply([updateSnapshot(first), updateSnapshot(second)]);
|
|
389
470
|
const availability = GpuScene.create(device, 2).unwrap();
|
|
390
471
|
expect(availability.status).toBe('available');
|
|
391
472
|
if (availability.status !== 'available') return;
|
|
@@ -397,16 +478,19 @@ describe('GPU-driven production projection', () => {
|
|
|
397
478
|
});
|
|
398
479
|
const prepared = production
|
|
399
480
|
.prepare({
|
|
481
|
+
...pbrInputs,
|
|
400
482
|
scene: {
|
|
401
483
|
scene: availability.scene,
|
|
402
484
|
plan: topology.plan(),
|
|
403
485
|
slots: projection.slotsSnapshot(),
|
|
404
486
|
},
|
|
405
487
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
406
|
-
|
|
488
|
+
meshBySlot: new Map([
|
|
489
|
+
[0, mesh(device)],
|
|
490
|
+
[1, mesh(device)],
|
|
491
|
+
]),
|
|
407
492
|
viewBindGroupLayout: viewLayout,
|
|
408
493
|
meshResidencyEpoch: 1,
|
|
409
|
-
hdrp: false,
|
|
410
494
|
})
|
|
411
495
|
.unwrap();
|
|
412
496
|
expect(prepared).toBeDefined();
|
|
@@ -415,7 +499,7 @@ describe('GPU-driven production projection', () => {
|
|
|
415
499
|
availability.scene.dispose();
|
|
416
500
|
});
|
|
417
501
|
|
|
418
|
-
it('uses
|
|
502
|
+
it('uses concrete draw keys for CPU validation telemetry', async () => {
|
|
419
503
|
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
420
504
|
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
421
505
|
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
@@ -428,24 +512,24 @@ describe('GPU-driven production projection', () => {
|
|
|
428
512
|
lods: [{ mesh: '00000000-0000-7000-8000-000000000001' as never, screenCoverage: 0.5 }],
|
|
429
513
|
};
|
|
430
514
|
const plainFallbackRow = { ...snapshot(2, 3), worldId: 0 };
|
|
431
|
-
const gpuOwned = new Set([
|
|
432
|
-
|
|
515
|
+
const gpuOwned = new Set([
|
|
516
|
+
gpuDrivenDrawKey(worldEntityKey(gpuLodRow.worldId, gpuLodRow.entityKey), -1, 0),
|
|
517
|
+
]);
|
|
433
518
|
|
|
434
519
|
// A GPU-owned row is removed from validation before this telemetry hook;
|
|
435
520
|
// the remaining plain row must still be counted as the one CPU fallback.
|
|
436
|
-
production.recordCpuValidation([{ source: plainFallbackRow }], gpuOwned
|
|
521
|
+
production.recordCpuValidation([{ source: plainFallbackRow }], gpuOwned);
|
|
437
522
|
expect(production.inspect()).toMatchObject({
|
|
438
523
|
cpuFallbackDrawItems: 1,
|
|
439
524
|
validatedGpuOwnedRows: 0,
|
|
440
525
|
});
|
|
441
526
|
|
|
442
|
-
//
|
|
443
|
-
//
|
|
444
|
-
|
|
445
|
-
production.recordCpuValidation([{ source: gpuLodRow }], gpuOwned, worldKeys);
|
|
527
|
+
// A GPU-only row contributes ownership telemetry without inflating the
|
|
528
|
+
// CPU residual row count.
|
|
529
|
+
production.recordCpuValidation([{ source: gpuLodRow }], gpuOwned);
|
|
446
530
|
expect(production.inspect()).toMatchObject({
|
|
447
|
-
cpuFallbackDrawItems:
|
|
448
|
-
validatedGpuOwnedRows:
|
|
531
|
+
cpuFallbackDrawItems: 0,
|
|
532
|
+
validatedGpuOwnedRows: 0,
|
|
449
533
|
});
|
|
450
534
|
production.dispose();
|
|
451
535
|
});
|
|
@@ -459,10 +543,11 @@ describe('GPU-driven production projection', () => {
|
|
|
459
543
|
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
460
544
|
})
|
|
461
545
|
.unwrap();
|
|
546
|
+
const pbrInputs = standardPbrInputs(device, shader, viewLayout);
|
|
462
547
|
const projection = new RenderScene();
|
|
463
548
|
const lodMeshGuid = '00000000-0000-7000-8000-000000000001' as never;
|
|
464
549
|
const makeLodSnapshot = (entityKey: number): RenderableSnapshot => {
|
|
465
|
-
const base = snapshot(entityKey, 3);
|
|
550
|
+
const base = preparedPbrFixture(snapshot(entityKey, 3));
|
|
466
551
|
const draw = base.gpuDrivenDraws?.[0];
|
|
467
552
|
if (draw === undefined) throw new Error('LOD test snapshot has no draw');
|
|
468
553
|
return {
|
|
@@ -472,8 +557,8 @@ describe('GPU-driven production projection', () => {
|
|
|
472
557
|
};
|
|
473
558
|
};
|
|
474
559
|
const delta = projection.apply([
|
|
475
|
-
|
|
476
|
-
|
|
560
|
+
updateSnapshot(makeLodSnapshot(1)),
|
|
561
|
+
updateSnapshot(makeLodSnapshot(2)),
|
|
477
562
|
]);
|
|
478
563
|
const availability = GpuScene.create(device, 2).unwrap();
|
|
479
564
|
expect(availability.status).toBe('available');
|
|
@@ -484,18 +569,22 @@ describe('GPU-driven production projection', () => {
|
|
|
484
569
|
const production = new GpuDrivenProduction(device, {
|
|
485
570
|
createShaderModule: () => ok(shader),
|
|
486
571
|
});
|
|
572
|
+
const lodGpuMesh = mesh(device, true, true);
|
|
487
573
|
const prepared = production
|
|
488
574
|
.prepare({
|
|
575
|
+
...pbrInputs,
|
|
489
576
|
scene: {
|
|
490
577
|
scene: availability.scene,
|
|
491
578
|
plan: topology.plan(),
|
|
492
579
|
slots: projection.slotsSnapshot(),
|
|
493
580
|
},
|
|
494
581
|
camera: { ...makeZeroCameraFallbackSnapshot(), position: vec3.create(0, 0, 5) },
|
|
495
|
-
|
|
582
|
+
meshBySlot: new Map([
|
|
583
|
+
[0, lodGpuMesh],
|
|
584
|
+
[1, lodGpuMesh],
|
|
585
|
+
]),
|
|
496
586
|
viewBindGroupLayout: viewLayout,
|
|
497
587
|
meshResidencyEpoch: 1,
|
|
498
|
-
hdrp: false,
|
|
499
588
|
})
|
|
500
589
|
.unwrap();
|
|
501
590
|
expect(prepared).toBeDefined();
|
|
@@ -582,6 +671,7 @@ describe('GPU-driven production projection', () => {
|
|
|
582
671
|
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
583
672
|
})
|
|
584
673
|
.unwrap();
|
|
674
|
+
const pbrInputs = standardPbrInputs(device, shader, viewLayout);
|
|
585
675
|
const viewUniform = device
|
|
586
676
|
.createBuffer({ size: 784, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST })
|
|
587
677
|
.unwrap();
|
|
@@ -591,11 +681,11 @@ describe('GPU-driven production projection', () => {
|
|
|
591
681
|
entries: [{ binding: 0, resource: { kind: 'buffer', value: { buffer: viewUniform } } }],
|
|
592
682
|
})
|
|
593
683
|
.unwrap();
|
|
594
|
-
const first = snapshot(1, 3);
|
|
684
|
+
const first = preparedPbrFixture(snapshot(1, 3));
|
|
595
685
|
const firstDraw = first.gpuDrivenDraws?.[0];
|
|
596
686
|
expect(firstDraw).toBeDefined();
|
|
597
687
|
if (firstDraw === undefined) return;
|
|
598
|
-
const second = { ...snapshot(2, 4), worldId: 1 };
|
|
688
|
+
const second = { ...preparedPbrFixture(snapshot(2, 4)), worldId: 1 };
|
|
599
689
|
const secondDraw = second.gpuDrivenDraws?.[0];
|
|
600
690
|
expect(secondDraw).toBeDefined();
|
|
601
691
|
if (secondDraw === undefined) return;
|
|
@@ -621,9 +711,7 @@ describe('GPU-driven production projection', () => {
|
|
|
621
711
|
},
|
|
622
712
|
];
|
|
623
713
|
const projection = new RenderScene();
|
|
624
|
-
const delta = projection.apply(
|
|
625
|
-
renderables.map((renderable) => ({ kind: 'create' as const, snapshot: renderable })),
|
|
626
|
-
);
|
|
714
|
+
const delta = projection.apply(renderables.map((renderable) => updateSnapshot(renderable)));
|
|
627
715
|
const availability = GpuScene.create(device, 2).unwrap();
|
|
628
716
|
expect(availability.status).toBe('available');
|
|
629
717
|
if (availability.status !== 'available') return;
|
|
@@ -632,53 +720,59 @@ describe('GPU-driven production projection', () => {
|
|
|
632
720
|
topology.rebuild(projection.slotsSnapshot());
|
|
633
721
|
expect(topology.plan()).toMatchObject({
|
|
634
722
|
candidateCount: 3,
|
|
635
|
-
visibleCapacity:
|
|
636
|
-
batches: [{ visibleBase: 0 }, { visibleBase: 64 }
|
|
723
|
+
visibleCapacity: 65,
|
|
724
|
+
batches: [{ visibleBase: 0 }, { visibleBase: 64 }],
|
|
637
725
|
});
|
|
638
726
|
const production = new GpuDrivenProduction(device, {
|
|
639
727
|
createShaderModule: () => ok(shader),
|
|
640
728
|
});
|
|
641
729
|
const meshes = new Map([
|
|
642
|
-
[
|
|
643
|
-
[
|
|
730
|
+
[0, mesh(device)],
|
|
731
|
+
[1, mesh(device, false)],
|
|
644
732
|
]);
|
|
645
733
|
const prepared = production
|
|
646
734
|
.prepare({
|
|
735
|
+
...pbrInputs,
|
|
647
736
|
scene: {
|
|
648
737
|
scene: availability.scene,
|
|
649
738
|
plan: topology.plan(),
|
|
650
739
|
slots: projection.slotsSnapshot(),
|
|
651
740
|
},
|
|
652
741
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
653
|
-
meshes,
|
|
742
|
+
meshBySlot: meshes,
|
|
654
743
|
viewBindGroupLayout: viewLayout,
|
|
655
744
|
meshResidencyEpoch: 1,
|
|
656
|
-
hdrp: false,
|
|
657
745
|
})
|
|
658
746
|
.unwrap();
|
|
659
747
|
expect(prepared).toBeDefined();
|
|
660
748
|
if (prepared === undefined) return;
|
|
661
|
-
expect(
|
|
749
|
+
expect(prepared.drawKeys).toEqual(
|
|
750
|
+
new Set([
|
|
751
|
+
gpuDrivenDrawKey(worldEntityKey(0, 1), -1, 0),
|
|
752
|
+
gpuDrivenDrawKey(worldEntityKey(0, 1), -1, 1),
|
|
753
|
+
gpuDrivenDrawKey(worldEntityKey(1, 2), 42, 0),
|
|
754
|
+
]),
|
|
755
|
+
);
|
|
662
756
|
expect(production.inspect()).toMatchObject({
|
|
663
757
|
gpuOwnedSnapshotsMaterialized: 2,
|
|
664
758
|
filteredPlanBuilds: 1,
|
|
665
759
|
candidateUploadBytes: 912,
|
|
666
|
-
batchUploadBytes:
|
|
760
|
+
batchUploadBytes: 64,
|
|
667
761
|
viewBindGroupCreates: 1,
|
|
668
762
|
});
|
|
669
763
|
|
|
670
764
|
const stable = production
|
|
671
765
|
.prepare({
|
|
766
|
+
...pbrInputs,
|
|
672
767
|
scene: {
|
|
673
768
|
scene: availability.scene,
|
|
674
769
|
plan: topology.plan(),
|
|
675
770
|
slots: projection.slotsSnapshot(),
|
|
676
771
|
},
|
|
677
772
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
678
|
-
meshes,
|
|
773
|
+
meshBySlot: meshes,
|
|
679
774
|
viewBindGroupLayout: viewLayout,
|
|
680
775
|
meshResidencyEpoch: 1,
|
|
681
|
-
hdrp: false,
|
|
682
776
|
})
|
|
683
777
|
.unwrap();
|
|
684
778
|
expect(stable?.topologySignature).toBe(prepared.topologySignature);
|
|
@@ -690,40 +784,46 @@ describe('GPU-driven production projection', () => {
|
|
|
690
784
|
viewConstantsUploadBytes: 112,
|
|
691
785
|
viewBindGroupCreates: 0,
|
|
692
786
|
batchBindGroupCreates: 0,
|
|
693
|
-
indirectDrawCount:
|
|
787
|
+
indirectDrawCount: 2,
|
|
694
788
|
});
|
|
695
789
|
|
|
696
|
-
//
|
|
697
|
-
//
|
|
698
|
-
// candidate plan instead of reusing the previous view's heights.
|
|
790
|
+
// This mixed batch has no LOD relation. Camera movement updates view
|
|
791
|
+
// constants while retaining the scene topology and candidate uploads.
|
|
699
792
|
const movedCamera = {
|
|
700
793
|
...makeZeroCameraFallbackSnapshot(),
|
|
701
794
|
position: vec3.create(0, 0, 6),
|
|
702
795
|
};
|
|
703
796
|
const moved = production
|
|
704
797
|
.prepare({
|
|
798
|
+
...pbrInputs,
|
|
705
799
|
scene: {
|
|
706
800
|
scene: availability.scene,
|
|
707
801
|
plan: topology.plan(),
|
|
708
802
|
slots: projection.slotsSnapshot(),
|
|
709
803
|
},
|
|
710
804
|
camera: movedCamera,
|
|
711
|
-
meshes,
|
|
805
|
+
meshBySlot: meshes,
|
|
712
806
|
viewBindGroupLayout: viewLayout,
|
|
713
807
|
meshResidencyEpoch: 1,
|
|
714
|
-
hdrp: false,
|
|
715
808
|
})
|
|
716
809
|
.unwrap();
|
|
717
810
|
expect(moved).toBeDefined();
|
|
718
811
|
expect(production.inspect()).toMatchObject({
|
|
719
|
-
filteredPlanBuilds:
|
|
720
|
-
candidateUploadBytes:
|
|
721
|
-
batchUploadBytes:
|
|
812
|
+
filteredPlanBuilds: 0,
|
|
813
|
+
candidateUploadBytes: 0,
|
|
814
|
+
batchUploadBytes: 0,
|
|
722
815
|
});
|
|
723
816
|
if (moved === undefined) return;
|
|
724
817
|
|
|
725
818
|
const graph = new RenderGraphBuilder<RenderPipelineFrame>();
|
|
726
819
|
const gpu = moved.project(graph, 'rgba8unorm', 1).unwrap();
|
|
820
|
+
const materialBindGroup = device.createBindGroupLayout({ entries: [] }).unwrap();
|
|
821
|
+
const frameResources = {
|
|
822
|
+
...moved.standardPbrFrameResources,
|
|
823
|
+
materialBindGroups: [
|
|
824
|
+
device.createBindGroup({ layout: materialBindGroup, entries: [] }).unwrap(),
|
|
825
|
+
],
|
|
826
|
+
};
|
|
727
827
|
const color = graph
|
|
728
828
|
.createTexture('gpu-driven-production-color', {
|
|
729
829
|
format: 'rgba8unorm',
|
|
@@ -742,7 +842,7 @@ describe('GPU-driven production projection', () => {
|
|
|
742
842
|
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
743
843
|
},
|
|
744
844
|
],
|
|
745
|
-
encode: ({ pass, resources }) => gpu.encode(viewBindGroup, pass, resources),
|
|
845
|
+
encode: ({ pass, resources }) => gpu.encode(viewBindGroup, pass, resources, frameResources),
|
|
746
846
|
})
|
|
747
847
|
.unwrap();
|
|
748
848
|
const compiled = graph.compile({ device, surfaceSize: { width: 1, height: 1 } }).unwrap();
|
|
@@ -751,7 +851,13 @@ describe('GPU-driven production projection', () => {
|
|
|
751
851
|
dependencies: ['gpu-driven.frustum-compact', 'gpu-driven.finalize-indirect'],
|
|
752
852
|
});
|
|
753
853
|
const encoder = device.createCommandEncoder({ label: 'gpu-driven-production' }).unwrap();
|
|
754
|
-
compiled
|
|
854
|
+
compiled
|
|
855
|
+
.execute({
|
|
856
|
+
viewBindGroup,
|
|
857
|
+
encoder,
|
|
858
|
+
gpuDrivenStandardPbrFrameResources: frameResources,
|
|
859
|
+
} as unknown as RenderPipelineFrame)
|
|
860
|
+
.unwrap();
|
|
755
861
|
encoder.finish().unwrap();
|
|
756
862
|
expect(device.framePassNames).toEqual([
|
|
757
863
|
'gpu-driven.view-reset',
|
|
@@ -760,8 +866,8 @@ describe('GPU-driven production projection', () => {
|
|
|
760
866
|
'main',
|
|
761
867
|
]);
|
|
762
868
|
expect(device.totalDispatchCount).toBe(3);
|
|
763
|
-
expect(device.totalDrawCount).toBe(
|
|
764
|
-
expect(production.inspect().indirectDrawCount).toBe(
|
|
869
|
+
expect(device.totalDrawCount).toBe(2);
|
|
870
|
+
expect(production.inspect().indirectDrawCount).toBe(2);
|
|
765
871
|
expect(production.inspect().indirectDrawCount).toBeLessThanOrEqual(
|
|
766
872
|
production.inspect().batchCount,
|
|
767
873
|
);
|
|
@@ -771,40 +877,42 @@ describe('GPU-driven production projection', () => {
|
|
|
771
877
|
// plan without making the remaining entity fall back to CPU validation.
|
|
772
878
|
const activeOnly = production
|
|
773
879
|
.prepare({
|
|
880
|
+
...pbrInputs,
|
|
774
881
|
scene: {
|
|
775
882
|
scene: availability.scene,
|
|
776
883
|
plan: topology.plan(),
|
|
777
884
|
slots: projection.slotsSnapshot(),
|
|
778
885
|
},
|
|
779
886
|
camera: movedCamera,
|
|
780
|
-
meshes,
|
|
887
|
+
meshBySlot: meshes,
|
|
781
888
|
viewBindGroupLayout: viewLayout,
|
|
782
889
|
meshResidencyEpoch: 1,
|
|
783
|
-
|
|
890
|
+
|
|
784
891
|
activeEntityKeys: new Set([worldEntityKey(0, 1)]),
|
|
785
892
|
activeEntityRevision: 1,
|
|
786
893
|
})
|
|
787
894
|
.unwrap();
|
|
788
|
-
expect(activeOnly?.
|
|
789
|
-
expect(activeOnly?.
|
|
895
|
+
expect(activeOnly?.drawKeys?.size).toBe(3);
|
|
896
|
+
expect(activeOnly?.ownsAllDrawItems).toBe(true);
|
|
790
897
|
|
|
791
898
|
const changedActive = production
|
|
792
899
|
.prepare({
|
|
900
|
+
...pbrInputs,
|
|
793
901
|
scene: {
|
|
794
902
|
scene: availability.scene,
|
|
795
903
|
plan: topology.plan(),
|
|
796
904
|
slots: projection.slotsSnapshot(),
|
|
797
905
|
},
|
|
798
906
|
camera: movedCamera,
|
|
799
|
-
meshes,
|
|
907
|
+
meshBySlot: meshes,
|
|
800
908
|
viewBindGroupLayout: viewLayout,
|
|
801
909
|
meshResidencyEpoch: 1,
|
|
802
|
-
|
|
910
|
+
|
|
803
911
|
activeEntityKeys: new Set([worldEntityKey(1, 2)]),
|
|
804
912
|
activeEntityRevision: 2,
|
|
805
913
|
})
|
|
806
914
|
.unwrap();
|
|
807
|
-
expect(changedActive?.
|
|
915
|
+
expect(changedActive?.drawKeys?.size).toBe(3);
|
|
808
916
|
// Production inspection counters are per-prepare-frame facts. The changed
|
|
809
917
|
// revision must trigger exactly one rebuild for this frame, not accumulate
|
|
810
918
|
// the prior frame's count.
|
|
@@ -816,16 +924,16 @@ describe('GPU-driven production projection', () => {
|
|
|
816
924
|
replacement.scene.sync(delta).unwrap();
|
|
817
925
|
const replacementPrepared = production
|
|
818
926
|
.prepare({
|
|
927
|
+
...pbrInputs,
|
|
819
928
|
scene: {
|
|
820
929
|
scene: replacement.scene,
|
|
821
930
|
plan: topology.plan(),
|
|
822
931
|
slots: projection.slotsSnapshot(),
|
|
823
932
|
},
|
|
824
933
|
camera: makeZeroCameraFallbackSnapshot(),
|
|
825
|
-
meshes,
|
|
934
|
+
meshBySlot: meshes,
|
|
826
935
|
viewBindGroupLayout: viewLayout,
|
|
827
936
|
meshResidencyEpoch: 1,
|
|
828
|
-
hdrp: false,
|
|
829
937
|
})
|
|
830
938
|
.unwrap();
|
|
831
939
|
expect(replacementPrepared?.topologySignature).not.toBe(prepared.topologySignature);
|
|
@@ -850,9 +958,7 @@ describe('GPU-driven production projection', () => {
|
|
|
850
958
|
materialShaderId: 'forgeax::default-standard-pbr',
|
|
851
959
|
};
|
|
852
960
|
const projection = new RenderScene();
|
|
853
|
-
const delta = projection.apply([
|
|
854
|
-
{ kind: 'create', snapshot: { ...base, material, materials: [material] } },
|
|
855
|
-
]);
|
|
961
|
+
const delta = projection.apply([updateSnapshot({ ...base, material, materials: [material] })]);
|
|
856
962
|
const availability = GpuScene.create(device, 1).unwrap();
|
|
857
963
|
expect(availability.status).toBe('available');
|
|
858
964
|
if (availability.status !== 'available') return;
|
|
@@ -868,9 +974,8 @@ describe('GPU-driven production projection', () => {
|
|
|
868
974
|
plan: topology.plan(),
|
|
869
975
|
slots: projection.slotsSnapshot(),
|
|
870
976
|
},
|
|
871
|
-
|
|
977
|
+
meshBySlot: new Map([[0, mesh(device)]]),
|
|
872
978
|
viewBindGroupLayout: viewLayout,
|
|
873
|
-
hdrp: false,
|
|
874
979
|
} as const;
|
|
875
980
|
|
|
876
981
|
expect(
|
|
@@ -882,8 +987,7 @@ describe('GPU-driven production projection', () => {
|
|
|
882
987
|
})
|
|
883
988
|
.unwrap(),
|
|
884
989
|
).toBeUndefined();
|
|
885
|
-
expect(production.
|
|
886
|
-
expect(production.inspect().filteredPlanBuilds).toBe(1);
|
|
990
|
+
expect(production.inspect().filteredPlanBuilds).toBe(0);
|
|
887
991
|
expect(
|
|
888
992
|
production
|
|
889
993
|
.prepare({
|
|
@@ -893,7 +997,6 @@ describe('GPU-driven production projection', () => {
|
|
|
893
997
|
})
|
|
894
998
|
.unwrap(),
|
|
895
999
|
).toBeUndefined();
|
|
896
|
-
expect(production.requiresSceneRows()).toBe(false);
|
|
897
1000
|
expect(production.inspect()).toMatchObject({
|
|
898
1001
|
filteredPlanBuilds: 0,
|
|
899
1002
|
gpuOwnedSnapshotsMaterialized: 0,
|
|
@@ -904,7 +1007,7 @@ describe('GPU-driven production projection', () => {
|
|
|
904
1007
|
availability.scene.dispose();
|
|
905
1008
|
});
|
|
906
1009
|
|
|
907
|
-
it('
|
|
1010
|
+
it('keeps an ordinary mesh on the common CPU residual while the Standard graph stays active', async () => {
|
|
908
1011
|
const canvas = {
|
|
909
1012
|
width: 1,
|
|
910
1013
|
height: 1,
|
|
@@ -954,11 +1057,9 @@ describe('GPU-driven production projection', () => {
|
|
|
954
1057
|
);
|
|
955
1058
|
expect(renderer.perFramePassNames).toContain('output-transform');
|
|
956
1059
|
// Storage-buffer LDR frames keep the scene linear until the one output
|
|
957
|
-
// transform writes the encoded surface
|
|
958
|
-
//
|
|
959
|
-
//
|
|
960
|
-
// path for occlusion while GPU-driven raster + output-transform remain
|
|
961
|
-
// the only draws in this graph.
|
|
1060
|
+
// transform writes the encoded surface. The ordinary unprepared mesh
|
|
1061
|
+
// remains the CPU residual under common visibility projection, while the
|
|
1062
|
+
// clustered Standard graph and output transform stay active.
|
|
962
1063
|
expect(device.totalDrawCount).toBe(2);
|
|
963
1064
|
expect(renderer.inspect().lodOcclusion).toMatchObject({
|
|
964
1065
|
fallback: { active: false },
|
|
@@ -979,7 +1080,7 @@ describe('GPU-driven production projection', () => {
|
|
|
979
1080
|
batchBindGroupCreates: 0,
|
|
980
1081
|
viewBindGroupCreates: 0,
|
|
981
1082
|
validatedGpuOwnedRows: 0,
|
|
982
|
-
cpuFallbackDrawItems:
|
|
1083
|
+
cpuFallbackDrawItems: 1,
|
|
983
1084
|
},
|
|
984
1085
|
});
|
|
985
1086
|
renderer.dispose();
|
|
@@ -1030,7 +1131,7 @@ describe('GPU-driven production projection', () => {
|
|
|
1030
1131
|
projectionRecords: 1,
|
|
1031
1132
|
topology: { batchCount: 1, candidateCount: 1 },
|
|
1032
1133
|
gpu: { status: 'resident' },
|
|
1033
|
-
gpuDriven: { cpuFallbackDrawItems:
|
|
1134
|
+
gpuDriven: { cpuFallbackDrawItems: 1, validatedGpuOwnedRows: 0 },
|
|
1034
1135
|
});
|
|
1035
1136
|
|
|
1036
1137
|
sceneWorld.set(rendered, Transform, { pos: [1, 0, 0] }).unwrap();
|
|
@@ -1047,4 +1148,873 @@ describe('GPU-driven production projection', () => {
|
|
|
1047
1148
|
});
|
|
1048
1149
|
renderer.dispose();
|
|
1049
1150
|
});
|
|
1151
|
+
|
|
1152
|
+
it('routes mixed ordinary camera and light edits through common visibility projection', async () => {
|
|
1153
|
+
const canvas = {
|
|
1154
|
+
width: 1,
|
|
1155
|
+
height: 1,
|
|
1156
|
+
getContext: () => null,
|
|
1157
|
+
addEventListener: () => undefined,
|
|
1158
|
+
removeEventListener: () => undefined,
|
|
1159
|
+
} as unknown as HTMLCanvasElement;
|
|
1160
|
+
const renderer = await constructRenderer(
|
|
1161
|
+
canvas,
|
|
1162
|
+
{ rhi },
|
|
1163
|
+
{ shaderManifestUrl: manifestDataUrl() },
|
|
1164
|
+
);
|
|
1165
|
+
expect((await renderer.initialization).ok).toBe(true);
|
|
1166
|
+
const world = new World();
|
|
1167
|
+
expect(renderer.attach(world).ok).toBe(true);
|
|
1168
|
+
const camera = world
|
|
1169
|
+
.spawn(
|
|
1170
|
+
{ component: Transform, data: { pos: [0, 0, 5] } },
|
|
1171
|
+
{ component: Camera, data: { fov: Math.PI / 4, aspect: 1, near: 0.1, far: 100 } },
|
|
1172
|
+
)
|
|
1173
|
+
.unwrap();
|
|
1174
|
+
world
|
|
1175
|
+
.spawn(
|
|
1176
|
+
{ component: Transform, data: {} },
|
|
1177
|
+
{ component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
|
|
1178
|
+
{ component: MeshRenderer, data: {} },
|
|
1179
|
+
)
|
|
1180
|
+
.unwrap();
|
|
1181
|
+
const lamp = world
|
|
1182
|
+
.spawn(
|
|
1183
|
+
{ component: Transform, data: { pos: [0, 0, 1] } },
|
|
1184
|
+
{ component: PointLight, data: { intensity: 1, range: 10 } },
|
|
1185
|
+
)
|
|
1186
|
+
.unwrap();
|
|
1187
|
+
const updateVisibility = vi.spyOn(PersistentRenderScene.prototype, 'updateVisibilityFacet');
|
|
1188
|
+
const projectVisibility = vi.spyOn(PersistentRenderScene.prototype, 'projectVisibility');
|
|
1189
|
+
try {
|
|
1190
|
+
world.update(1 / 60).unwrap();
|
|
1191
|
+
expect(renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 }).ok).toBe(true);
|
|
1192
|
+
const warmUpdateCalls = updateVisibility.mock.calls.length;
|
|
1193
|
+
const warmProjectCalls = projectVisibility.mock.calls.length;
|
|
1194
|
+
const before = renderer.inspect().renderScene;
|
|
1195
|
+
|
|
1196
|
+
world.set(camera, Transform, { pos: [0, 1, 5] }).unwrap();
|
|
1197
|
+
world.set(lamp, PointLight, { intensity: 3, range: 8 }).unwrap();
|
|
1198
|
+
propagateTransforms(world).unwrap();
|
|
1199
|
+
world.update(1 / 60).unwrap();
|
|
1200
|
+
expect(renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 }).ok).toBe(true);
|
|
1201
|
+
|
|
1202
|
+
expect(updateVisibility.mock.calls.length).toBeGreaterThan(warmUpdateCalls);
|
|
1203
|
+
expect(projectVisibility.mock.calls.length).toBeGreaterThan(warmProjectCalls);
|
|
1204
|
+
const after = renderer.inspect().renderScene;
|
|
1205
|
+
expect(after).toMatchObject({
|
|
1206
|
+
fullRebuilds: 1,
|
|
1207
|
+
projectionRecords: before.projectionRecords,
|
|
1208
|
+
});
|
|
1209
|
+
expect(after.topology.revision).toBe(before.topology.revision);
|
|
1210
|
+
} finally {
|
|
1211
|
+
updateVisibility.mockRestore();
|
|
1212
|
+
projectVisibility.mockRestore();
|
|
1213
|
+
renderer.dispose();
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
it('admits prepared Alpha Mask draws to the GPU shadow lane', () => {
|
|
1218
|
+
const source = preparedSnapshot(10, 3);
|
|
1219
|
+
const alphaMaterial = {
|
|
1220
|
+
...source.material,
|
|
1221
|
+
paramSnapshot: { alphaCutoff: 0.5 },
|
|
1222
|
+
textureHandles: new Map([['baseColorTexture', 1]]) as never,
|
|
1223
|
+
samplerHandles: new Map([['baseColorTexture', 2]]) as never,
|
|
1224
|
+
} as MaterialSnapshot;
|
|
1225
|
+
const draw = source.gpuDrivenDraws?.[0];
|
|
1226
|
+
expect(draw?.prepared).toBeDefined();
|
|
1227
|
+
if (draw === undefined || draw.prepared === undefined) return;
|
|
1228
|
+
|
|
1229
|
+
// Alpha Mask is a prepared material fact: it keeps the opaque depth
|
|
1230
|
+
// state, carries an authored cutoff, and owns the base-color resource
|
|
1231
|
+
// needed by the shadow fragment discard. Alpha Blend is covered by the
|
|
1232
|
+
// adjacent residual-lane regression and must remain CPU-owned.
|
|
1233
|
+
expect(
|
|
1234
|
+
shadowCasterCpuReason(
|
|
1235
|
+
alphaMaterial,
|
|
1236
|
+
draw,
|
|
1237
|
+
[{ materialShaderId: 'forgeax::default-shadow-caster' } as DispatchEntry],
|
|
1238
|
+
undefined,
|
|
1239
|
+
undefined,
|
|
1240
|
+
),
|
|
1241
|
+
).toBeUndefined();
|
|
1242
|
+
|
|
1243
|
+
const projection = new RenderScene();
|
|
1244
|
+
projection.apply([
|
|
1245
|
+
updateSnapshot({ ...source, material: alphaMaterial, materials: [alphaMaterial] }),
|
|
1246
|
+
]);
|
|
1247
|
+
const topology = new BatchTopology();
|
|
1248
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
1249
|
+
expect(topology.plan()).toMatchObject({
|
|
1250
|
+
candidateCount: 1,
|
|
1251
|
+
batches: [{ key: { admission: 'alpha-mask' } }],
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
it('does not admit Alpha Blend or CPU transmission draw plans as GPU-owned', async () => {
|
|
1256
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
1257
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
1258
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
1259
|
+
const viewLayout = device
|
|
1260
|
+
.createBindGroupLayout({
|
|
1261
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
1262
|
+
})
|
|
1263
|
+
.unwrap();
|
|
1264
|
+
const materialLayout = device.createBindGroupLayout({ entries: [] }).unwrap();
|
|
1265
|
+
const meshLayout = device
|
|
1266
|
+
.createBindGroupLayout({
|
|
1267
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'read-only-storage' } }],
|
|
1268
|
+
})
|
|
1269
|
+
.unwrap();
|
|
1270
|
+
const instancesLayout = device
|
|
1271
|
+
.createBindGroupLayout({
|
|
1272
|
+
entries: [
|
|
1273
|
+
{ binding: 0, visibility: 0x1, buffer: { type: 'read-only-storage' } },
|
|
1274
|
+
{ binding: 1, visibility: 0x1, buffer: { type: 'read-only-storage' } },
|
|
1275
|
+
],
|
|
1276
|
+
})
|
|
1277
|
+
.unwrap();
|
|
1278
|
+
const pbrPipelineLayout = device
|
|
1279
|
+
.createPipelineLayout({
|
|
1280
|
+
bindGroupLayouts: [viewLayout, materialLayout, meshLayout, instancesLayout],
|
|
1281
|
+
})
|
|
1282
|
+
.unwrap();
|
|
1283
|
+
const opaque = preparedSnapshot(1, 3);
|
|
1284
|
+
const blendState = {
|
|
1285
|
+
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
1286
|
+
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
1287
|
+
} as const;
|
|
1288
|
+
const alphaMaterial = {
|
|
1289
|
+
...opaque.material,
|
|
1290
|
+
renderState: { cullMode: 'none' as const, blend: blendState, depthWriteEnabled: false },
|
|
1291
|
+
} as MaterialSnapshot;
|
|
1292
|
+
const alphaSnapshots = [2, 3].map(
|
|
1293
|
+
(entityKey): RenderableSnapshot => ({
|
|
1294
|
+
...preparedSnapshot(entityKey, 3),
|
|
1295
|
+
material: alphaMaterial,
|
|
1296
|
+
materials: [alphaMaterial],
|
|
1297
|
+
gpuDrivenDraws: [],
|
|
1298
|
+
}),
|
|
1299
|
+
);
|
|
1300
|
+
const transmissionMaterial = {
|
|
1301
|
+
...opaque.material,
|
|
1302
|
+
paramSnapshot: { transmission: 0.5 },
|
|
1303
|
+
} as MaterialSnapshot;
|
|
1304
|
+
const transmissionSnapshot: RenderableSnapshot = {
|
|
1305
|
+
...preparedSnapshot(4, 3),
|
|
1306
|
+
material: transmissionMaterial,
|
|
1307
|
+
materials: [transmissionMaterial],
|
|
1308
|
+
};
|
|
1309
|
+
const sceneProjection = new RenderScene();
|
|
1310
|
+
const delta = sceneProjection.apply([
|
|
1311
|
+
updateSnapshot(opaque),
|
|
1312
|
+
...alphaSnapshots.map((snapshot) => updateSnapshot(snapshot)),
|
|
1313
|
+
updateSnapshot(transmissionSnapshot),
|
|
1314
|
+
]);
|
|
1315
|
+
const availability = GpuScene.create(device, 3).unwrap();
|
|
1316
|
+
expect(availability.status).toBe('available');
|
|
1317
|
+
if (availability.status !== 'available') return;
|
|
1318
|
+
availability.scene.sync(delta).unwrap();
|
|
1319
|
+
const topology = new BatchTopology();
|
|
1320
|
+
topology.rebuild(sceneProjection.slotsSnapshot());
|
|
1321
|
+
const production = new GpuDrivenProduction(device, {
|
|
1322
|
+
createShaderModule: () => ok(shader),
|
|
1323
|
+
});
|
|
1324
|
+
const artifact = {
|
|
1325
|
+
material: 'forgeax::default-standard-pbr',
|
|
1326
|
+
pass: 'forward',
|
|
1327
|
+
wgsl: 'synthetic',
|
|
1328
|
+
layoutIdentity: STANDARD_PBR_RECEIPT.reflection.layoutIdentity,
|
|
1329
|
+
bindings: [],
|
|
1330
|
+
deps: [],
|
|
1331
|
+
vertexInputs: STANDARD_PBR_RECEIPT.vertexInputs as unknown as readonly Readonly<
|
|
1332
|
+
Record<string, unknown>
|
|
1333
|
+
>[],
|
|
1334
|
+
receipt: STANDARD_PBR_RECEIPT,
|
|
1335
|
+
};
|
|
1336
|
+
const programKey = standardPbrProgramKey(artifact.material);
|
|
1337
|
+
expect(programKey).toBeDefined();
|
|
1338
|
+
if (programKey === undefined) return;
|
|
1339
|
+
const prepared = production.prepare({
|
|
1340
|
+
scene: {
|
|
1341
|
+
scene: availability.scene,
|
|
1342
|
+
plan: topology.plan(),
|
|
1343
|
+
slots: sceneProjection.slotsSnapshot(),
|
|
1344
|
+
},
|
|
1345
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
1346
|
+
meshBySlot: new Map([[0, mesh(device)]]),
|
|
1347
|
+
viewBindGroupLayout: viewLayout,
|
|
1348
|
+
meshResidencyEpoch: 1,
|
|
1349
|
+
|
|
1350
|
+
standardPbrArtifact: artifact,
|
|
1351
|
+
standardPbrPipelineState: {
|
|
1352
|
+
pbrPipelineLayout,
|
|
1353
|
+
meshBindGroupLayout: meshLayout,
|
|
1354
|
+
instancesBindGroupLayout: instancesLayout,
|
|
1355
|
+
gpuDrivenInstancesBindGroupLayout: instancesLayout,
|
|
1356
|
+
gpuDrivenPbrPipelineLayout: pbrPipelineLayout,
|
|
1357
|
+
standardPbrShaderModule: shader,
|
|
1358
|
+
gpuDrivenPbrPrograms: new Map([[programKey, { module: shader, artifact }]]),
|
|
1359
|
+
} as never,
|
|
1360
|
+
});
|
|
1361
|
+
expect(prepared.ok).toBe(true);
|
|
1362
|
+
if (!prepared.ok) return;
|
|
1363
|
+
expect(prepared.value?.drawKeys).toEqual(new Set(['1:-1:0']));
|
|
1364
|
+
expect(prepared.value?.ownsAllDrawItems).toBe(false);
|
|
1365
|
+
production.dispose();
|
|
1366
|
+
availability.scene.dispose();
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
it('projects concrete ShadowCaster candidates and leaves the CPU residual disjoint', async () => {
|
|
1370
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
1371
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
1372
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
1373
|
+
const viewLayout = device
|
|
1374
|
+
.createBindGroupLayout({
|
|
1375
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
1376
|
+
})
|
|
1377
|
+
.unwrap();
|
|
1378
|
+
const materialLayout = device.createBindGroupLayout({ entries: [] }).unwrap();
|
|
1379
|
+
const meshLayout = device
|
|
1380
|
+
.createBindGroupLayout({
|
|
1381
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'read-only-storage' } }],
|
|
1382
|
+
})
|
|
1383
|
+
.unwrap();
|
|
1384
|
+
const instancesLayout = device
|
|
1385
|
+
.createBindGroupLayout({
|
|
1386
|
+
entries: [
|
|
1387
|
+
{ binding: 0, visibility: 0x1, buffer: { type: 'read-only-storage' } },
|
|
1388
|
+
{ binding: 1, visibility: 0x1, buffer: { type: 'read-only-storage' } },
|
|
1389
|
+
],
|
|
1390
|
+
})
|
|
1391
|
+
.unwrap();
|
|
1392
|
+
const pbrPipelineLayout = device
|
|
1393
|
+
.createPipelineLayout({
|
|
1394
|
+
bindGroupLayouts: [viewLayout, materialLayout, meshLayout, instancesLayout],
|
|
1395
|
+
})
|
|
1396
|
+
.unwrap();
|
|
1397
|
+
const source = preparedSnapshot(1, 3);
|
|
1398
|
+
const firstDraw = source.gpuDrivenDraws?.[0];
|
|
1399
|
+
expect(firstDraw).toBeDefined();
|
|
1400
|
+
if (firstDraw === undefined) return;
|
|
1401
|
+
const firstPrepared = firstDraw.prepared;
|
|
1402
|
+
expect(firstPrepared).toBeDefined();
|
|
1403
|
+
if (firstPrepared === undefined) return;
|
|
1404
|
+
const blendState = {
|
|
1405
|
+
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
1406
|
+
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
1407
|
+
} as const;
|
|
1408
|
+
const residualMaterial = {
|
|
1409
|
+
...source.material,
|
|
1410
|
+
materialHandle: 22,
|
|
1411
|
+
transparent: true,
|
|
1412
|
+
renderState: { cullMode: 'none' as const, blend: blendState, depthWriteEnabled: false },
|
|
1413
|
+
} as MaterialSnapshot;
|
|
1414
|
+
const residualDraw = {
|
|
1415
|
+
...firstDraw,
|
|
1416
|
+
first: 3,
|
|
1417
|
+
materialSlot: 1,
|
|
1418
|
+
materialResourceClass: 'alpha-blend',
|
|
1419
|
+
prepared: { ...firstPrepared, first: 3 },
|
|
1420
|
+
};
|
|
1421
|
+
const mixed = {
|
|
1422
|
+
...source,
|
|
1423
|
+
materials: [source.material, residualMaterial],
|
|
1424
|
+
gpuDrivenDraws: [firstDraw, residualDraw],
|
|
1425
|
+
} as RenderableSnapshot;
|
|
1426
|
+
const projection = new RenderScene();
|
|
1427
|
+
const delta = projection.apply([updateSnapshot(mixed)]);
|
|
1428
|
+
const availability = GpuScene.create(device, 1).unwrap();
|
|
1429
|
+
expect(availability.status).toBe('available');
|
|
1430
|
+
if (availability.status !== 'available') return;
|
|
1431
|
+
availability.scene.sync(delta).unwrap();
|
|
1432
|
+
const topology = new BatchTopology();
|
|
1433
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
1434
|
+
expect(topology.plan()).toMatchObject({ candidateCount: 1 });
|
|
1435
|
+
const production = new GpuDrivenProduction(device, {
|
|
1436
|
+
createShaderModule: () => ok(shader),
|
|
1437
|
+
});
|
|
1438
|
+
const artifact = {
|
|
1439
|
+
material: 'forgeax::default-standard-pbr',
|
|
1440
|
+
pass: 'forward',
|
|
1441
|
+
wgsl: 'synthetic',
|
|
1442
|
+
layoutIdentity: STANDARD_PBR_RECEIPT.reflection.layoutIdentity,
|
|
1443
|
+
bindings: [],
|
|
1444
|
+
deps: [],
|
|
1445
|
+
vertexInputs: STANDARD_PBR_RECEIPT.vertexInputs as unknown as readonly Readonly<
|
|
1446
|
+
Record<string, unknown>
|
|
1447
|
+
>[],
|
|
1448
|
+
receipt: STANDARD_PBR_RECEIPT,
|
|
1449
|
+
};
|
|
1450
|
+
const programKey = standardPbrProgramKey(artifact.material);
|
|
1451
|
+
expect(programKey).toBeDefined();
|
|
1452
|
+
if (programKey === undefined) return;
|
|
1453
|
+
const entity = worldEntityKey(mixed.worldId, mixed.entityKey);
|
|
1454
|
+
const gpuKey = gpuDrivenShadowDrawKey(entity, -1, 0, 0);
|
|
1455
|
+
const residualKey = gpuDrivenShadowDrawKey(entity, 22, 1, 0);
|
|
1456
|
+
const allShadowKeys = new Set([gpuKey, residualKey]);
|
|
1457
|
+
const prepareFrame = () =>
|
|
1458
|
+
production
|
|
1459
|
+
.prepare({
|
|
1460
|
+
scene: {
|
|
1461
|
+
scene: availability.scene,
|
|
1462
|
+
plan: topology.plan(),
|
|
1463
|
+
slots: projection.slotsSnapshot(),
|
|
1464
|
+
},
|
|
1465
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
1466
|
+
meshBySlot: new Map([[0, mesh(device)]]),
|
|
1467
|
+
viewBindGroupLayout: viewLayout,
|
|
1468
|
+
meshResidencyEpoch: 1,
|
|
1469
|
+
standardPbrArtifact: artifact,
|
|
1470
|
+
standardPbrPipelineState: {
|
|
1471
|
+
pbrPipelineLayout,
|
|
1472
|
+
meshBindGroupLayout: meshLayout,
|
|
1473
|
+
instancesBindGroupLayout: instancesLayout,
|
|
1474
|
+
gpuDrivenInstancesBindGroupLayout: instancesLayout,
|
|
1475
|
+
gpuDrivenPbrPipelineLayout: pbrPipelineLayout,
|
|
1476
|
+
standardPbrShaderModule: shader,
|
|
1477
|
+
gpuDrivenPbrPrograms: new Map([[programKey, { module: shader, artifact }]]),
|
|
1478
|
+
} as never,
|
|
1479
|
+
shadowCasterDrawKeys: allShadowKeys,
|
|
1480
|
+
})
|
|
1481
|
+
.unwrap();
|
|
1482
|
+
const prepared = prepareFrame();
|
|
1483
|
+
expect(prepared).toBeDefined();
|
|
1484
|
+
if (prepared === undefined) return;
|
|
1485
|
+
const claimed = new Set(prepared.shadowDrawKeys ?? []);
|
|
1486
|
+
expect([...claimed]).toEqual([gpuKey]);
|
|
1487
|
+
expect(prepared.ownsAllShadowCasters).toBe(false);
|
|
1488
|
+
const residual = [...allShadowKeys].filter((key) => !claimed.has(key));
|
|
1489
|
+
expect(residual).toEqual([residualKey]);
|
|
1490
|
+
expect([...new Set([...claimed, ...residual])].sort()).toEqual([...allShadowKeys].sort());
|
|
1491
|
+
const identity = { kind: 'directional' as const, index: 0 };
|
|
1492
|
+
const updated = prepared.updateShadowViews?.([
|
|
1493
|
+
{ identity, planes: new Float32Array(24), cacheToken: 'mixed-candidates' },
|
|
1494
|
+
]);
|
|
1495
|
+
expect(updated?.ok).toBe(true);
|
|
1496
|
+
expect(production.inspect().channels).toEqual(
|
|
1497
|
+
expect.arrayContaining([
|
|
1498
|
+
expect.objectContaining({
|
|
1499
|
+
viewPass: 'directional-shadow',
|
|
1500
|
+
lane: 'blocked',
|
|
1501
|
+
reason: 'shadow-ownership',
|
|
1502
|
+
drawCount: 1,
|
|
1503
|
+
failure: expect.objectContaining({
|
|
1504
|
+
code: 'shadow-ownership',
|
|
1505
|
+
detail: expect.objectContaining({
|
|
1506
|
+
owner: 'shadow',
|
|
1507
|
+
reason: 'shadow-ownership-missing',
|
|
1508
|
+
recovery: 'route-cpu-lane',
|
|
1509
|
+
}),
|
|
1510
|
+
}),
|
|
1511
|
+
}),
|
|
1512
|
+
]),
|
|
1513
|
+
);
|
|
1514
|
+
const submission = prepared.shadowViewPool?.submission(identity);
|
|
1515
|
+
expect(submission?.plan.candidateCount).toBe(1);
|
|
1516
|
+
const resumed = prepareFrame();
|
|
1517
|
+
expect(
|
|
1518
|
+
resumed?.updateShadowViews?.([
|
|
1519
|
+
{ identity, planes: new Float32Array(24), cacheToken: 'mixed-candidates' },
|
|
1520
|
+
]).ok,
|
|
1521
|
+
).toBe(true);
|
|
1522
|
+
expect(
|
|
1523
|
+
production.inspect().channels.some((channel) => channel.viewPass === 'directional-shadow'),
|
|
1524
|
+
).toBe(true);
|
|
1525
|
+
production.dispose();
|
|
1526
|
+
availability.scene.dispose();
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
it('claims exact ShadowCaster passes without swallowing residual or custom passes', async () => {
|
|
1530
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
1531
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
1532
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
1533
|
+
const viewLayout = device
|
|
1534
|
+
.createBindGroupLayout({
|
|
1535
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
1536
|
+
})
|
|
1537
|
+
.unwrap();
|
|
1538
|
+
const materialLayout = device.createBindGroupLayout({ entries: [] }).unwrap();
|
|
1539
|
+
const meshLayout = device
|
|
1540
|
+
.createBindGroupLayout({
|
|
1541
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'read-only-storage' } }],
|
|
1542
|
+
})
|
|
1543
|
+
.unwrap();
|
|
1544
|
+
const instancesLayout = device
|
|
1545
|
+
.createBindGroupLayout({
|
|
1546
|
+
entries: [
|
|
1547
|
+
{ binding: 0, visibility: 0x1, buffer: { type: 'read-only-storage' } },
|
|
1548
|
+
{ binding: 1, visibility: 0x1, buffer: { type: 'read-only-storage' } },
|
|
1549
|
+
],
|
|
1550
|
+
})
|
|
1551
|
+
.unwrap();
|
|
1552
|
+
const pbrPipelineLayout = device
|
|
1553
|
+
.createPipelineLayout({
|
|
1554
|
+
bindGroupLayouts: [viewLayout, materialLayout, meshLayout, instancesLayout],
|
|
1555
|
+
})
|
|
1556
|
+
.unwrap();
|
|
1557
|
+
const source = preparedSnapshot(7, 3);
|
|
1558
|
+
const firstDraw = source.gpuDrivenDraws?.[0];
|
|
1559
|
+
expect(firstDraw?.prepared).toBeDefined();
|
|
1560
|
+
if (firstDraw === undefined || firstDraw.prepared === undefined) return;
|
|
1561
|
+
const materials = [0, 1, 2].map((handle) => ({
|
|
1562
|
+
...source.material,
|
|
1563
|
+
materialHandle: handle + 10,
|
|
1564
|
+
})) as MaterialSnapshot[];
|
|
1565
|
+
const draws = [
|
|
1566
|
+
{ ...firstDraw, drawItemIndex: 0, materialSlot: 0, first: 0, count: 3 },
|
|
1567
|
+
{
|
|
1568
|
+
...withoutPrepared(firstDraw),
|
|
1569
|
+
drawItemIndex: 1,
|
|
1570
|
+
materialSlot: 1,
|
|
1571
|
+
first: 3,
|
|
1572
|
+
count: 3,
|
|
1573
|
+
},
|
|
1574
|
+
{ ...firstDraw, drawItemIndex: 2, materialSlot: 2, first: 6, count: 3 },
|
|
1575
|
+
];
|
|
1576
|
+
const mixed: RenderableSnapshot = {
|
|
1577
|
+
...source,
|
|
1578
|
+
material: materials[0] as MaterialSnapshot,
|
|
1579
|
+
materials,
|
|
1580
|
+
gpuDrivenDraws: draws,
|
|
1581
|
+
};
|
|
1582
|
+
const projection = new RenderScene();
|
|
1583
|
+
const delta = projection.apply([updateSnapshot(mixed)]);
|
|
1584
|
+
const availability = GpuScene.create(device, 1).unwrap();
|
|
1585
|
+
expect(availability.status).toBe('available');
|
|
1586
|
+
if (availability.status !== 'available') return;
|
|
1587
|
+
availability.scene.sync(delta).unwrap();
|
|
1588
|
+
const topology = new BatchTopology();
|
|
1589
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
1590
|
+
expect(topology.plan().candidateCount).toBe(3);
|
|
1591
|
+
const entity = worldEntityKey(mixed.worldId, mixed.entityKey);
|
|
1592
|
+
const membership: ShadowCasterMembership[] = [
|
|
1593
|
+
{
|
|
1594
|
+
worldEntity: entity,
|
|
1595
|
+
renderableIndex: 0,
|
|
1596
|
+
drawItemIndex: 0,
|
|
1597
|
+
materialHandle: 10,
|
|
1598
|
+
passIndex: 0,
|
|
1599
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
1600
|
+
},
|
|
1601
|
+
{
|
|
1602
|
+
worldEntity: entity,
|
|
1603
|
+
renderableIndex: 0,
|
|
1604
|
+
drawItemIndex: 1,
|
|
1605
|
+
materialHandle: 11,
|
|
1606
|
+
passIndex: 0,
|
|
1607
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
worldEntity: entity,
|
|
1611
|
+
renderableIndex: 0,
|
|
1612
|
+
drawItemIndex: 2,
|
|
1613
|
+
materialHandle: 12,
|
|
1614
|
+
passIndex: 0,
|
|
1615
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
worldEntity: entity,
|
|
1619
|
+
renderableIndex: 0,
|
|
1620
|
+
drawItemIndex: 2,
|
|
1621
|
+
materialHandle: 12,
|
|
1622
|
+
passIndex: 7,
|
|
1623
|
+
materialShaderId: 'custom-shadow-caster',
|
|
1624
|
+
},
|
|
1625
|
+
];
|
|
1626
|
+
const allKeys = new Set(
|
|
1627
|
+
membership.map((entry) =>
|
|
1628
|
+
gpuDrivenShadowDrawKey(
|
|
1629
|
+
entry.worldEntity,
|
|
1630
|
+
entry.materialHandle,
|
|
1631
|
+
entry.drawItemIndex,
|
|
1632
|
+
entry.passIndex,
|
|
1633
|
+
),
|
|
1634
|
+
),
|
|
1635
|
+
);
|
|
1636
|
+
const production = new GpuDrivenProduction(device, {
|
|
1637
|
+
createShaderModule: () => ok(shader),
|
|
1638
|
+
});
|
|
1639
|
+
const artifact = {
|
|
1640
|
+
material: 'forgeax::default-standard-pbr',
|
|
1641
|
+
pass: 'forward',
|
|
1642
|
+
wgsl: 'synthetic',
|
|
1643
|
+
layoutIdentity: STANDARD_PBR_RECEIPT.reflection.layoutIdentity,
|
|
1644
|
+
bindings: [],
|
|
1645
|
+
deps: [],
|
|
1646
|
+
vertexInputs: STANDARD_PBR_RECEIPT.vertexInputs as unknown as readonly Readonly<
|
|
1647
|
+
Record<string, unknown>
|
|
1648
|
+
>[],
|
|
1649
|
+
receipt: STANDARD_PBR_RECEIPT,
|
|
1650
|
+
};
|
|
1651
|
+
const programKey = standardPbrProgramKey(artifact.material);
|
|
1652
|
+
expect(programKey).toBeDefined();
|
|
1653
|
+
if (programKey === undefined) return;
|
|
1654
|
+
const multiMesh = {
|
|
1655
|
+
...mesh(device),
|
|
1656
|
+
submeshes: [0, 1, 2].map((materialSlot) => ({
|
|
1657
|
+
indexOffset: materialSlot * 3,
|
|
1658
|
+
indexCount: 3,
|
|
1659
|
+
vertexCount: 3,
|
|
1660
|
+
materialSlot,
|
|
1661
|
+
topology: 'triangle-list' as const,
|
|
1662
|
+
})),
|
|
1663
|
+
};
|
|
1664
|
+
const prepared = production
|
|
1665
|
+
.prepare({
|
|
1666
|
+
scene: {
|
|
1667
|
+
scene: availability.scene,
|
|
1668
|
+
plan: topology.plan(),
|
|
1669
|
+
slots: projection.slotsSnapshot(),
|
|
1670
|
+
},
|
|
1671
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
1672
|
+
meshBySlot: new Map([[0, multiMesh]]),
|
|
1673
|
+
viewBindGroupLayout: viewLayout,
|
|
1674
|
+
meshResidencyEpoch: 1,
|
|
1675
|
+
|
|
1676
|
+
standardPbrArtifact: artifact,
|
|
1677
|
+
standardPbrPipelineState: {
|
|
1678
|
+
pbrPipelineLayout,
|
|
1679
|
+
meshBindGroupLayout: meshLayout,
|
|
1680
|
+
instancesBindGroupLayout: instancesLayout,
|
|
1681
|
+
gpuDrivenInstancesBindGroupLayout: instancesLayout,
|
|
1682
|
+
gpuDrivenPbrPipelineLayout: pbrPipelineLayout,
|
|
1683
|
+
standardPbrShaderModule: shader,
|
|
1684
|
+
gpuDrivenPbrPrograms: new Map([[programKey, { module: shader, artifact }]]),
|
|
1685
|
+
} as never,
|
|
1686
|
+
shadowCasterDrawKeys: allKeys,
|
|
1687
|
+
shadowCasterMembership: membership,
|
|
1688
|
+
})
|
|
1689
|
+
.unwrap();
|
|
1690
|
+
expect(prepared).toBeDefined();
|
|
1691
|
+
if (prepared === undefined) return;
|
|
1692
|
+
const gpuKey = gpuDrivenShadowDrawKey(entity, 10, 0, 0);
|
|
1693
|
+
expect([...(prepared.shadowDrawKeys ?? [])]).toEqual([gpuKey]);
|
|
1694
|
+
expect(prepared.ownsAllShadowCasters).toBe(false);
|
|
1695
|
+
const residual = [...allKeys].filter((key) => !prepared.shadowDrawKeys?.has(key));
|
|
1696
|
+
expect(residual).toHaveLength(3);
|
|
1697
|
+
expect(new Set([...(prepared.shadowDrawKeys ?? []), ...residual])).toEqual(allKeys);
|
|
1698
|
+
const identity = { kind: 'directional' as const, index: 0 };
|
|
1699
|
+
expect(
|
|
1700
|
+
prepared.updateShadowViews?.([
|
|
1701
|
+
{ identity, planes: new Float32Array(24), cacheToken: 'exact-shadow-membership' },
|
|
1702
|
+
]).ok,
|
|
1703
|
+
).toBe(true);
|
|
1704
|
+
expect(prepared.shadowViewPool?.submission(identity)?.plan.candidateCount).toBe(1);
|
|
1705
|
+
expect(prepared.shadowDrawKeysByView?.get('directional:0:')).toEqual(new Set([gpuKey]));
|
|
1706
|
+
production.dispose();
|
|
1707
|
+
availability.scene.dispose();
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
it.each([false, true])('prepares off-camera CPU shadow materials (main rows=%s)', (withMain) => {
|
|
1711
|
+
const material = { ...preparedSnapshot(1, 3).material, materialHandle: 42 };
|
|
1712
|
+
const shadow = {
|
|
1713
|
+
source: { ...preparedSnapshot(1, 3), material, materials: [material] },
|
|
1714
|
+
renderableIndex: 0,
|
|
1715
|
+
} as never;
|
|
1716
|
+
const main = { source: preparedSnapshot(2, 3), renderableIndex: 0 } as never;
|
|
1717
|
+
const plan = buildDispatchPlan({} as never, withMain ? [main] : [], [], [], [shadow]);
|
|
1718
|
+
expect(plan.shadowValidatedOrdered).toEqual([shadow]);
|
|
1719
|
+
const slot = plan.materialSlots.indexOf(material);
|
|
1720
|
+
expect(slot).toBeGreaterThanOrEqual(0);
|
|
1721
|
+
expect(plan.materialSlotOwners[slot]).toBe(shadow);
|
|
1722
|
+
const dispatch = {
|
|
1723
|
+
renderableIndex: 0,
|
|
1724
|
+
materialHandle: 42,
|
|
1725
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1726
|
+
tags: { LightMode: 'ShadowCaster' },
|
|
1727
|
+
};
|
|
1728
|
+
const map = shadowShaderMap({ ...plan, shadowDispatch: [dispatch] } as never);
|
|
1729
|
+
expect(map.get(0)?.get(42)?.[0]?.materialSlot).toBe(slot);
|
|
1730
|
+
});
|
|
1731
|
+
|
|
1732
|
+
it.each([
|
|
1733
|
+
false,
|
|
1734
|
+
true,
|
|
1735
|
+
])('records exact CPU residuals with authored state (skinned=%s)', async (skinned) => {
|
|
1736
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
1737
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
1738
|
+
const source = preparedSnapshot(8, 3);
|
|
1739
|
+
const firstDraw = source.gpuDrivenDraws?.[0];
|
|
1740
|
+
expect(firstDraw?.prepared).toBeDefined();
|
|
1741
|
+
if (firstDraw === undefined || firstDraw.prepared === undefined) return;
|
|
1742
|
+
const materials = [0, 1, 2].map((handle) => ({
|
|
1743
|
+
...source.material,
|
|
1744
|
+
materialHandle: handle + 20,
|
|
1745
|
+
})) as MaterialSnapshot[];
|
|
1746
|
+
const draws = [
|
|
1747
|
+
{ ...firstDraw, drawItemIndex: 0, materialSlot: 0, first: 0, count: 3 },
|
|
1748
|
+
{ ...firstDraw, drawItemIndex: 1, materialSlot: 1, first: 3, count: 3 },
|
|
1749
|
+
{ ...firstDraw, drawItemIndex: 2, materialSlot: 2, first: 6, count: 3 },
|
|
1750
|
+
];
|
|
1751
|
+
const sourceForRecord: RenderableSnapshot = {
|
|
1752
|
+
...source,
|
|
1753
|
+
...(skinned ? { skin: { buffer: {} as never, byteOffset: 0 } as never } : {}),
|
|
1754
|
+
material: materials[0] as MaterialSnapshot,
|
|
1755
|
+
materials,
|
|
1756
|
+
gpuDrivenDraws: draws,
|
|
1757
|
+
};
|
|
1758
|
+
const geometry = {
|
|
1759
|
+
...mesh(device),
|
|
1760
|
+
submeshes: [0, 1, 2].map((materialSlot) => ({
|
|
1761
|
+
indexOffset: materialSlot * 3,
|
|
1762
|
+
indexCount: 3,
|
|
1763
|
+
vertexCount: 3,
|
|
1764
|
+
materialSlot,
|
|
1765
|
+
topology: 'triangle-list' as const,
|
|
1766
|
+
})),
|
|
1767
|
+
};
|
|
1768
|
+
const identityInstanceBuffer = device
|
|
1769
|
+
.createBuffer({ size: 64, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST })
|
|
1770
|
+
.unwrap();
|
|
1771
|
+
const instancesBindGroupLayout = device
|
|
1772
|
+
.createBindGroupLayout({
|
|
1773
|
+
entries: [
|
|
1774
|
+
{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } },
|
|
1775
|
+
{ binding: 1, visibility: 0x1, buffer: { type: 'uniform' } },
|
|
1776
|
+
],
|
|
1777
|
+
})
|
|
1778
|
+
.unwrap();
|
|
1779
|
+
const worldEntity = worldEntityKey(sourceForRecord.worldId, sourceForRecord.entityKey);
|
|
1780
|
+
const claimedKey = gpuDrivenShadowDrawKey(worldEntity, 20, 0, 0);
|
|
1781
|
+
const residualKeys = new Set([
|
|
1782
|
+
gpuDrivenShadowDrawKey(worldEntity, 21, 1, 0),
|
|
1783
|
+
gpuDrivenShadowDrawKey(worldEntity, 22, 2, 0),
|
|
1784
|
+
gpuDrivenShadowDrawKey(worldEntity, 22, 2, 7),
|
|
1785
|
+
]);
|
|
1786
|
+
const shadowDispatches = new Map([
|
|
1787
|
+
[
|
|
1788
|
+
0,
|
|
1789
|
+
new Map([
|
|
1790
|
+
[
|
|
1791
|
+
20,
|
|
1792
|
+
[
|
|
1793
|
+
{
|
|
1794
|
+
passIndex: 0,
|
|
1795
|
+
materialHandle: 20,
|
|
1796
|
+
vertexEntry: undefined,
|
|
1797
|
+
fragmentEntry: undefined,
|
|
1798
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
1799
|
+
renderState: undefined,
|
|
1800
|
+
materialSlot: 0,
|
|
1801
|
+
paramSnapshot: materials[0]?.paramSnapshot,
|
|
1802
|
+
},
|
|
1803
|
+
],
|
|
1804
|
+
],
|
|
1805
|
+
[
|
|
1806
|
+
21,
|
|
1807
|
+
[
|
|
1808
|
+
{
|
|
1809
|
+
passIndex: 0,
|
|
1810
|
+
materialHandle: 21,
|
|
1811
|
+
vertexEntry: undefined,
|
|
1812
|
+
fragmentEntry: undefined,
|
|
1813
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
1814
|
+
renderState: undefined,
|
|
1815
|
+
materialSlot: 1,
|
|
1816
|
+
paramSnapshot: materials[1]?.paramSnapshot,
|
|
1817
|
+
},
|
|
1818
|
+
],
|
|
1819
|
+
],
|
|
1820
|
+
[
|
|
1821
|
+
22,
|
|
1822
|
+
[
|
|
1823
|
+
{
|
|
1824
|
+
passIndex: 0,
|
|
1825
|
+
materialHandle: 22,
|
|
1826
|
+
vertexEntry: undefined,
|
|
1827
|
+
fragmentEntry: undefined,
|
|
1828
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
1829
|
+
renderState: undefined,
|
|
1830
|
+
materialSlot: 2,
|
|
1831
|
+
paramSnapshot: materials[2]?.paramSnapshot,
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
passIndex: 7,
|
|
1835
|
+
materialHandle: 22,
|
|
1836
|
+
vertexEntry: undefined,
|
|
1837
|
+
fragmentEntry: undefined,
|
|
1838
|
+
materialShaderId: 'custom-shadow-caster',
|
|
1839
|
+
renderState: { cullMode: 'none' as const, frontFace: 'cw' as const },
|
|
1840
|
+
materialSlot: 2,
|
|
1841
|
+
paramSnapshot: materials[2]?.paramSnapshot,
|
|
1842
|
+
},
|
|
1843
|
+
],
|
|
1844
|
+
],
|
|
1845
|
+
]),
|
|
1846
|
+
],
|
|
1847
|
+
]);
|
|
1848
|
+
const indexedDraws: number[][] = [];
|
|
1849
|
+
const pipelineRequests: unknown[][] = [];
|
|
1850
|
+
const pass = {
|
|
1851
|
+
setPipeline: () => undefined,
|
|
1852
|
+
setVertexBuffer: () => undefined,
|
|
1853
|
+
setIndexBuffer: () => undefined,
|
|
1854
|
+
setBindGroup: () => undefined,
|
|
1855
|
+
drawIndexed: (...args: number[]) => indexedDraws.push(args),
|
|
1856
|
+
draw: () => undefined,
|
|
1857
|
+
} as unknown as RhiRenderPassEncoder;
|
|
1858
|
+
const context = {
|
|
1859
|
+
runtime: {
|
|
1860
|
+
device,
|
|
1861
|
+
getMaterialShaderPipeline: (...args: unknown[]) => {
|
|
1862
|
+
pipelineRequests.push(args);
|
|
1863
|
+
return {} as never;
|
|
1864
|
+
},
|
|
1865
|
+
},
|
|
1866
|
+
pipelineState: {
|
|
1867
|
+
identityInstanceBuffer,
|
|
1868
|
+
pbrSkinMeshBindGroupLayout: {} as never,
|
|
1869
|
+
skinPaletteAllocator: { bindingWindowBytes: 256 },
|
|
1870
|
+
instancesBindGroupLayout,
|
|
1871
|
+
materialBindGroupLayout: {} as never,
|
|
1872
|
+
materialUniformBuffer: { buffer: {} as never, sizeInBytes: 256 },
|
|
1873
|
+
meshStorageBuffer: { buffer: {} as never, sizeInBytes: 256 },
|
|
1874
|
+
defaultSampler: {} as never,
|
|
1875
|
+
fallbackTextureView: {} as never,
|
|
1876
|
+
defaultNormalTextureView: {} as never,
|
|
1877
|
+
defaultWhiteTextureView: {} as never,
|
|
1878
|
+
skylightFallback: {
|
|
1879
|
+
irradianceView: {} as never,
|
|
1880
|
+
prefilterView: {} as never,
|
|
1881
|
+
brdfLutView: {} as never,
|
|
1882
|
+
sampler: {} as never,
|
|
1883
|
+
intensityBuffer: {} as never,
|
|
1884
|
+
},
|
|
1885
|
+
},
|
|
1886
|
+
validatedOrdered: [
|
|
1887
|
+
{
|
|
1888
|
+
source: sourceForRecord,
|
|
1889
|
+
mesh: geometry,
|
|
1890
|
+
renderableIndex: 0,
|
|
1891
|
+
},
|
|
1892
|
+
],
|
|
1893
|
+
frameState: {
|
|
1894
|
+
instanceBuffers: new Map(),
|
|
1895
|
+
meshBindGroupCache: new Map(),
|
|
1896
|
+
instancesBgPerEntity: new Map(),
|
|
1897
|
+
shadowMaterialBindGroups: new WeakMap(),
|
|
1898
|
+
materialBgShared: new Map(),
|
|
1899
|
+
},
|
|
1900
|
+
materialBgAssemblyCache: new Map(),
|
|
1901
|
+
store: { materialResourceEpoch: 0 } as never,
|
|
1902
|
+
world: new World(),
|
|
1903
|
+
bindGroupCounts: { createBindGroup: 0, keys: [] },
|
|
1904
|
+
dispatch: [],
|
|
1905
|
+
} as never;
|
|
1906
|
+
const recorded = recordShadowCasterDraws(
|
|
1907
|
+
context,
|
|
1908
|
+
pass,
|
|
1909
|
+
{} as never,
|
|
1910
|
+
{} as never,
|
|
1911
|
+
new Set([0]),
|
|
1912
|
+
new Map([[0, new Set([20, 21, 22])]]),
|
|
1913
|
+
shadowDispatches,
|
|
1914
|
+
new Set([claimedKey]),
|
|
1915
|
+
);
|
|
1916
|
+
expect(indexedDraws).toHaveLength(3);
|
|
1917
|
+
expect(recorded).toEqual(residualKeys);
|
|
1918
|
+
expect(recorded.has(claimedKey)).toBe(false);
|
|
1919
|
+
const authored = pipelineRequests.find((args) => args[0] === 'custom-shadow-caster');
|
|
1920
|
+
expect(authored).toBeDefined();
|
|
1921
|
+
expect(authored?.[2]).toEqual({ cullMode: 'none', frontFace: 'cw' });
|
|
1922
|
+
expect(authored?.[15]).toBe(skinned ? 'pbr-skin' : 'pbr');
|
|
1923
|
+
if (skinned) expect(authored?.[5]).toContain('SKINNING_DISABLED=false');
|
|
1924
|
+
});
|
|
1925
|
+
|
|
1926
|
+
it('fails closed at every shadow front door when expected casters lack bind groups', () => {
|
|
1927
|
+
const membership: ShadowCasterMembership[] = [
|
|
1928
|
+
{
|
|
1929
|
+
worldEntity: 1,
|
|
1930
|
+
renderableIndex: 0,
|
|
1931
|
+
drawItemIndex: 0,
|
|
1932
|
+
materialHandle: 20,
|
|
1933
|
+
passIndex: 0,
|
|
1934
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
1935
|
+
cpuReason: 'unprepared',
|
|
1936
|
+
},
|
|
1937
|
+
];
|
|
1938
|
+
const context = {
|
|
1939
|
+
runtime: {
|
|
1940
|
+
device: { caps: { storageBuffer: false } },
|
|
1941
|
+
getMaterialShaderPipeline: () => null,
|
|
1942
|
+
},
|
|
1943
|
+
pipelineState: { perPassResources: { shadowSampler: null } },
|
|
1944
|
+
frameState: {
|
|
1945
|
+
pointShadowSnapshots: [{ shadowAtlasLayer: 0 }],
|
|
1946
|
+
spotShadowSnapshots: [{ shadowAtlasTile: 0, lightViewProj: new Float32Array(16) }],
|
|
1947
|
+
},
|
|
1948
|
+
dispatch: [],
|
|
1949
|
+
shadowCasterMembership: membership,
|
|
1950
|
+
} as never;
|
|
1951
|
+
const pass = {} as RhiRenderPassEncoder;
|
|
1952
|
+
const calls = [
|
|
1953
|
+
() =>
|
|
1954
|
+
encodeDirectionalShadowPass(context, pass, 0, {
|
|
1955
|
+
x: 0,
|
|
1956
|
+
y: 0,
|
|
1957
|
+
w: 1,
|
|
1958
|
+
h: 1,
|
|
1959
|
+
}),
|
|
1960
|
+
() => encodePointShadowPass(context, pass, 0, 0),
|
|
1961
|
+
() => encodeSpotShadowPass(context, pass, 0),
|
|
1962
|
+
];
|
|
1963
|
+
for (const call of calls) {
|
|
1964
|
+
expect(call).toThrowError(RhiError);
|
|
1965
|
+
try {
|
|
1966
|
+
call();
|
|
1967
|
+
} catch (error) {
|
|
1968
|
+
expect(error).toBeInstanceOf(RhiError);
|
|
1969
|
+
expect((error as RhiError).code).toBe('rhi-not-available');
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
|
|
1974
|
+
it('labels a prepared-contract admission rejection for the CPU residual', () => {
|
|
1975
|
+
const source = preparedSnapshot(9, 3);
|
|
1976
|
+
const draw = source.gpuDrivenDraws?.[0];
|
|
1977
|
+
expect(draw?.prepared).toBeDefined();
|
|
1978
|
+
if (draw === undefined || draw.prepared === undefined) return;
|
|
1979
|
+
const mismatched = {
|
|
1980
|
+
...draw,
|
|
1981
|
+
prepared: {
|
|
1982
|
+
...draw.prepared,
|
|
1983
|
+
identity: {
|
|
1984
|
+
...draw.prepared.identity,
|
|
1985
|
+
material: 'forgeax::pbr-skin' as const,
|
|
1986
|
+
deformation: 'skin' as const,
|
|
1987
|
+
},
|
|
1988
|
+
},
|
|
1989
|
+
};
|
|
1990
|
+
const material = {
|
|
1991
|
+
...source.material,
|
|
1992
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1993
|
+
} as MaterialSnapshot;
|
|
1994
|
+
expect(
|
|
1995
|
+
shadowCasterCpuReason(
|
|
1996
|
+
material,
|
|
1997
|
+
mismatched,
|
|
1998
|
+
[
|
|
1999
|
+
{
|
|
2000
|
+
entityIndex: source.entityKey,
|
|
2001
|
+
materialHandle: material.materialHandle ?? -1,
|
|
2002
|
+
renderableIndex: 0,
|
|
2003
|
+
passIndex: 0,
|
|
2004
|
+
queue: 2000,
|
|
2005
|
+
layer: 0,
|
|
2006
|
+
tags: { LightMode: 'ShadowCaster' },
|
|
2007
|
+
renderState: undefined,
|
|
2008
|
+
defines: undefined,
|
|
2009
|
+
vertexEntry: undefined,
|
|
2010
|
+
fragmentEntry: undefined,
|
|
2011
|
+
materialShaderId: 'forgeax::default-shadow-caster',
|
|
2012
|
+
paramSnapshot: undefined,
|
|
2013
|
+
} as DispatchEntry,
|
|
2014
|
+
],
|
|
2015
|
+
undefined,
|
|
2016
|
+
undefined,
|
|
2017
|
+
),
|
|
2018
|
+
).toBe('prepared-contract');
|
|
2019
|
+
});
|
|
1050
2020
|
});
|