@forgeax/engine-render 0.1.32 → 0.1.34
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 +1374 -142
- package/dist/assembly/analytic-fog-registration.d.ts +3 -0
- package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
- package/dist/assembly/backend-contract.d.ts +2 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
- package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
- package/dist/assembly/canvas-draw-guard.d.ts +3 -0
- package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
- package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
- package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +1 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/fxaa-registration.d.ts +4 -0
- package/dist/assembly/fxaa-registration.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +3 -3
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/lod-observation.d.ts +4 -0
- package/dist/assembly/lod-observation.d.ts.map +1 -0
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
- package/dist/assembly/material-shader-policy.d.ts +16 -2
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
- package/dist/assembly/motion-blur-registration.d.ts +3 -0
- package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/render-feature-post-processes.d.ts +6 -0
- package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
- package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +11 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
- package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
- package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +2 -2
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/barrel-distortion.d.ts +47 -0
- package/dist/barrel-distortion.d.ts.map +1 -0
- package/dist/bloom-admission.d.ts +13 -0
- package/dist/bloom-admission.d.ts.map +1 -0
- package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
- package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
- package/dist/{chunk-JCIRYQ4V.mjs → chunk-HEB564WF.mjs} +10 -4
- package/dist/chunk-HEB564WF.mjs.map +1 -0
- package/dist/{chunk-JRIPCMYC.mjs → chunk-MKDIT2EM.mjs} +1584 -375
- package/dist/chunk-MKDIT2EM.mjs.map +1 -0
- package/dist/{chunk-S665NJNF.mjs → chunk-OCDLEI6I.mjs} +3 -3
- package/dist/{chunk-S665NJNF.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
- package/dist/chunk-ODZWR6JQ.mjs +4927 -0
- package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
- package/dist/{chunk-DRR7D3KX.mjs → chunk-SHFUFJ5N.mjs} +192 -18
- package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
- package/dist/{chunk-4NNZQCL3.mjs → chunk-UI5SFODX.mjs} +223 -277
- package/dist/chunk-UI5SFODX.mjs.map +1 -0
- package/dist/{chunk-GWCEMDVR.mjs → chunk-UINHLCZA.mjs} +787 -80
- package/dist/chunk-UINHLCZA.mjs.map +1 -0
- package/dist/{chunk-6TFBXLRJ.mjs → chunk-WEX45HKH.mjs} +222 -337
- package/dist/chunk-WEX45HKH.mjs.map +1 -0
- package/dist/cloud/density.d.ts +55 -0
- package/dist/cloud/density.d.ts.map +1 -0
- package/dist/cloud/extract.d.ts +25 -0
- package/dist/cloud/extract.d.ts.map +1 -0
- package/dist/cloud/feature.d.ts +57 -0
- package/dist/cloud/feature.d.ts.map +1 -0
- package/dist/cloud/frame-context.d.ts +8 -0
- package/dist/cloud/frame-context.d.ts.map +1 -0
- package/dist/cloud/inspection.d.ts +59 -0
- package/dist/cloud/inspection.d.ts.map +1 -0
- package/dist/cloud/optics.d.ts +56 -0
- package/dist/cloud/optics.d.ts.map +1 -0
- package/dist/cloud/parameters.d.ts +73 -0
- package/dist/cloud/parameters.d.ts.map +1 -0
- package/dist/cloud/resources.d.ts +60 -0
- package/dist/cloud/resources.d.ts.map +1 -0
- package/dist/cloud/shadow.d.ts +42 -0
- package/dist/cloud/shadow.d.ts.map +1 -0
- package/dist/cloud/temporal.d.ts +74 -0
- package/dist/cloud/temporal.d.ts.map +1 -0
- package/dist/components/atmosphere.d.ts +2 -0
- package/dist/components/atmosphere.d.ts.map +1 -1
- package/dist/components/barrel-distortion.d.ts +13 -0
- package/dist/components/barrel-distortion.d.ts.map +1 -0
- package/dist/components/camera.d.ts +20 -4
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/cloud-layer.d.ts +29 -0
- package/dist/components/cloud-layer.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 +20 -6
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +6 -5
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/motion-blur.d.ts +1 -0
- package/dist/components/motion-blur.d.ts.map +1 -1
- package/dist/construct-renderer.d.ts +1 -1
- package/dist/construct-renderer.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +9751 -3528
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +6 -0
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/dynamic-geometry.d.ts +5 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/environment/analytic-fog.d.ts +6 -0
- package/dist/environment/analytic-fog.d.ts.map +1 -0
- package/dist/errors/cloud.d.ts +59 -0
- package/dist/errors/cloud.d.ts.map +1 -0
- package/dist/errors/render.d.ts +23 -5
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/examples/single-layer-medium-surface.d.ts +47 -0
- package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +12 -1
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/environment.d.ts +2 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/features/barrel-distortion.d.ts +29 -0
- package/dist/features/barrel-distortion.d.ts.map +1 -0
- package/dist/features/error-reporting.d.ts +17 -0
- package/dist/features/error-reporting.d.ts.map +1 -0
- package/dist/features/host.d.ts +15 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
- package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
- package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
- package/dist/features/plan.d.ts +15 -4
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +9 -5
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +5 -0
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +1 -0
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/targets.d.ts +2 -0
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +44 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +15 -7
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-dirty-ranges.d.ts +11 -0
- package/dist/gpu-dirty-ranges.d.ts.map +1 -0
- package/dist/gpu-driven/batch-topology.d.ts +17 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts +6 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +7 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster-material.d.ts +178 -0
- package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +50 -4
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/resource-allocation.d.ts +31 -0
- package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
- package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
- package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +35 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +11 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +2 -2
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +10 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +31 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +258 -8
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +178 -3
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instance-buffer-cache.d.ts +4 -4
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/instances.d.ts +49 -94
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +7 -7
- package/dist/pbr-pipeline.d.ts +19 -1
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
- package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-output/types.d.ts +4 -2
- package/dist/pipeline/standard-output/types.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +28 -19
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +1 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/barrel-distortion-frame.d.ts +15 -0
- package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
- package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
- package/dist/record/fold-buckets.d.ts +95 -0
- package/dist/record/fold-buckets.d.ts.map +1 -0
- package/dist/record/fold-instance-buffer.d.ts +6 -0
- package/dist/record/fold-instance-buffer.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +1 -0
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +40 -3
- 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 +14 -62
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-draw-receipts.d.ts +11 -0
- package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
- package/dist/record/gpu-timing.d.ts +8 -0
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +21 -5
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +13 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +6 -5
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/motion-blur-receipt.d.ts +11 -0
- package/dist/record/motion-blur-receipt.d.ts.map +1 -0
- package/dist/record/probe-blend-buffer.d.ts +16 -0
- package/dist/record/probe-blend-buffer.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +34 -31
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -0
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +6 -4
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +22 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +11 -3
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/record/volume-params.d.ts +9 -0
- package/dist/record/volume-params.d.ts.map +1 -0
- package/dist/recovery/render-system-candidate.d.ts +10 -0
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +66 -8
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +106 -3
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +3 -2
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +24 -10
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-presentation.d.ts +23 -0
- package/dist/render-system-presentation.d.ts.map +1 -0
- package/dist/render-system-producer-roots.d.ts +10 -0
- package/dist/render-system-producer-roots.d.ts.map +1 -0
- package/dist/render-system-residency.d.ts +12 -0
- package/dist/render-system-residency.d.ts.map +1 -0
- package/dist/render-system.d.ts +26 -15
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/probe-blend.d.ts +27 -0
- package/dist/scene/probe-blend.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +14 -5
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +9 -0
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/surface/dynamic-input.d.ts +133 -0
- package/dist/surface/dynamic-input.d.ts.map +1 -0
- package/dist/surface/gpu-driven.d.ts +41 -0
- package/dist/surface/gpu-driven.d.ts.map +1 -0
- package/dist/surface/submission-observation.d.ts +136 -0
- package/dist/surface/submission-observation.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +7 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +13 -1
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
- package/dist/temporal/view.d.ts +6 -1
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/transmission/single-layer-medium.d.ts +89 -0
- package/dist/transmission/single-layer-medium.d.ts.map +1 -0
- package/dist/typed-render-graph-primitives.d.ts +24 -9
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +9 -5
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/extract.d.ts +1 -1
- package/dist/volume/extract.d.ts.map +1 -1
- package/dist/volume/inspection.d.ts +2 -0
- package/dist/volume/inspection.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +4 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/resources.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +3 -1
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +22 -21
- package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
- package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
- package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
- package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
- package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
- package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
- package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
- package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
- package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
- package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
- package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
- package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
- package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
- package/src/__tests__/environment-selection.unit.test.ts +12 -0
- package/src/__tests__/factory-contract.integration.test.ts +626 -113
- package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
- package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
- package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
- package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
- package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
- package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +7 -6
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +103 -4
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
- package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
- package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
- package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
- package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
- package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
- package/src/__tests__/gpu-scene.unit.test.ts +210 -9
- package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
- package/src/__tests__/instances-culling.integration.test.ts +10 -36
- package/src/__tests__/instances-store.unit.test.ts +105 -238
- package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
- package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
- package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
- package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
- package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
- package/src/__tests__/motion-blur-compute.dawn.test.ts +589 -0
- package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
- package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
- package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
- package/src/__tests__/probe-bindings.unit.test.ts +582 -1
- package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
- package/src/__tests__/public-surface.test-d.ts +8 -0
- package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +9 -0
- package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +44 -0
- package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
- package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
- package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +37 -6
- package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
- package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
- package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
- package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +33 -11
- package/src/__tests__/resource-allocation.unit.test.ts +123 -0
- package/src/__tests__/shader-manifest-fixture.ts +4 -4
- package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
- package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +17 -0
- package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
- package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
- package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
- package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +200 -9
- package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
- package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
- package/src/__tests__/temporal-performance.integration.test.ts +3 -3
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
- package/src/__tests__/temporal-view.unit.test.ts +2 -1
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
- package/src/__tests__/visibility-instances.integration.test.ts +7 -19
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
- package/src/__tests__/volume-optics-oracle.unit.test.ts +7 -3
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +189 -15
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
- package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +558 -68
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +23 -5
- package/src/assembly/analytic-fog-registration.ts +14 -0
- package/src/assembly/backend-contract.ts +30 -0
- package/src/assembly/barrel-distortion-inspection.ts +22 -0
- package/src/assembly/canvas-draw-guard.ts +11 -0
- package/src/assembly/cloud-shadow-entry.ts +22 -0
- package/src/assembly/dynamic-geometry-host.ts +65 -37
- package/src/assembly/dynamic-geometry-runtime.ts +7 -1
- package/src/assembly/factory.ts +2 -30
- package/src/assembly/fxaa-registration.ts +17 -0
- package/src/assembly/host-contract.ts +3 -3
- package/src/assembly/lod-observation.ts +12 -0
- package/src/assembly/material/assembly.ts +25 -13
- package/src/assembly/material/pipeline-helpers.ts +53 -12
- package/src/assembly/material-shader-policy.ts +55 -0
- package/src/assembly/material-shader-prewarm.ts +21 -10
- package/src/assembly/motion-blur-registration.ts +17 -0
- package/src/assembly/recovery/device-loss-fanout.ts +13 -0
- package/src/assembly/recovery/renderer-recover.ts +41 -18
- package/src/assembly/render-feature-post-processes.ts +54 -0
- package/src/assembly/renderer-facade.ts +6 -1
- package/src/assembly/renderer-feature-inspection.ts +8 -2
- package/src/assembly/renderer-frame-transaction.ts +23 -0
- package/src/assembly/renderer-inspection.ts +28 -0
- package/src/assembly/skin-palette-owner.ts +1 -1
- package/src/assembly/temporal-post-process-prewarm.ts +40 -0
- package/src/assembly/webgpu-pbr-ready.ts +9 -1
- package/src/assembly/webgpu-ready-contract.ts +8 -3
- package/src/assembly/webgpu-ready-per-pass.ts +104 -0
- package/src/assembly/webgpu-ready.ts +296 -306
- package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
- package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
- package/src/assembly/webgpu-renderer.ts +228 -281
- package/src/assembly/webgpu-vertex-layouts.ts +1 -1
- package/src/barrel-distortion.ts +247 -0
- package/src/bloom-admission.ts +21 -0
- package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
- package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
- package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
- package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
- package/src/cloud/density.ts +600 -0
- package/src/cloud/extract.ts +110 -0
- package/src/cloud/feature.ts +1810 -0
- package/src/cloud/frame-context.ts +42 -0
- package/src/cloud/inspection.ts +130 -0
- package/src/cloud/optics.ts +285 -0
- package/src/cloud/parameters.ts +255 -0
- package/src/cloud/resources.ts +122 -0
- package/src/cloud/shadow.ts +167 -0
- package/src/cloud/temporal.ts +226 -0
- package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
- package/src/components/atmosphere.ts +4 -0
- package/src/components/barrel-distortion.ts +46 -0
- package/src/components/camera.ts +66 -10
- package/src/components/cloud-layer.ts +47 -0
- package/src/components/index.ts +2 -0
- package/src/components/instances.ts +18 -45
- package/src/components/mesh-filter.ts +11 -9
- package/src/components/motion-blur.ts +1 -0
- package/src/components/sprite-instances.ts +1 -1
- package/src/construct-renderer.ts +1 -1
- package/src/device/gpu-residency.ts +56 -12
- package/src/dynamic-geometry.ts +12 -1
- package/src/environment/analytic-fog.ts +44 -0
- package/src/environment/background.ts +2 -2
- package/src/errors/cloud.ts +103 -0
- package/src/errors/render.ts +58 -8
- package/src/examples/single-layer-medium-surface.ts +59 -0
- package/src/extract/camera.ts +35 -8
- package/src/extract/contracts.ts +12 -0
- package/src/extract/environment.ts +4 -0
- package/src/features/barrel-distortion.ts +172 -0
- package/src/features/error-reporting.ts +98 -0
- package/src/features/host.ts +138 -24
- package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
- package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
- package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
- package/src/features/motion-blur/motion-blur-params.ts +70 -2
- package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
- package/src/features/plan.ts +33 -8
- package/src/features/prepared-gpu-work.ts +86 -35
- package/src/features/render-graph-compute.ts +13 -5
- package/src/features/render-graph-contribution.ts +11 -0
- package/src/features/render-graph-raster.ts +45 -3
- package/src/features/targets.ts +66 -0
- package/src/features/types.ts +47 -0
- package/src/fullscreen-post-process-pass.ts +41 -9
- package/src/gpu-dirty-ranges.ts +48 -0
- package/src/gpu-driven/batch-topology.ts +53 -0
- package/src/gpu-driven/material-artifacts.ts +35 -32
- package/src/gpu-driven/prepared-draw.ts +27 -14
- package/src/gpu-driven/production-raster-material.ts +1894 -0
- package/src/gpu-driven/production-raster.ts +904 -787
- package/src/gpu-driven/resource-allocation.ts +142 -0
- package/src/gpu-driven/shadow-ownership.ts +71 -0
- package/src/gpu-driven/view-gpu.ts +181 -10
- package/src/gpu-scene.ts +259 -101
- package/src/hdrp-buffers.ts +4 -3
- package/src/ibl/skylight-bind-group.ts +49 -7
- package/src/index.ts +228 -9
- package/src/inspection-types.ts +222 -2
- package/src/instance-buffer-cache.ts +4 -4
- package/src/instances.ts +151 -492
- package/src/pbr-pipeline.ts +219 -13
- package/src/pipeline/single-layer-medium-passes.ts +267 -0
- package/src/pipeline/standard-forward-lane.ts +313 -57
- package/src/pipeline/standard-output/graph.ts +8 -2
- package/src/pipeline/standard-output/types.ts +12 -1
- package/src/pipeline/standard-pipeline.ts +291 -18
- package/src/pipeline/standard-post.ts +397 -102
- package/src/pipeline-builder.ts +1 -1
- package/src/pipeline-spec.ts +5 -5
- package/src/plugin.ts +4 -0
- package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
- package/src/prepare/prepared-graphics-resolver.ts +2 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
- package/src/record/__tests__/temporal-payload.unit.test.ts +3 -2
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
- package/src/record/barrel-distortion-frame.ts +110 -0
- package/src/record/dynamic-geometry-consumption.ts +39 -9
- package/src/record/fold-buckets.ts +852 -0
- package/src/record/fold-instance-buffer.ts +51 -0
- package/src/record/frame-lighting.ts +36 -2
- package/src/record/frame-snapshot.ts +44 -4
- package/src/record/frame-targets.ts +10 -6
- package/src/record/frame.ts +373 -1099
- package/src/record/gpu-draw-receipts.ts +38 -0
- package/src/record/gpu-timing.ts +48 -5
- package/src/record/main-pass-geometry.ts +281 -83
- package/src/record/main-pass-material.ts +66 -4
- package/src/record/main-pass-sprite-draws.ts +13 -66
- package/src/record/main-pass.ts +156 -20
- package/src/record/mesh-ssbo.ts +43 -6
- package/src/record/motion-blur-receipt.ts +91 -0
- package/src/record/probe-blend-buffer.ts +180 -0
- package/src/record/recovery-pipeline.ts +35 -3
- package/src/record/render-context.ts +51 -31
- package/src/record/shadow-pass.ts +36 -2
- package/src/record/skybox-post-pass.ts +150 -200
- package/src/record/typed-frame-graph.ts +824 -44
- package/src/record/view-ubo.ts +26 -3
- package/src/record/volume-params.ts +225 -0
- package/src/recovery/render-system-candidate.ts +257 -52
- package/src/reflection/record-owner.ts +7 -3
- package/src/render-contract.ts +71 -8
- package/src/render-graph-primitives.ts +7 -1
- package/src/render-pipeline.ts +136 -1
- package/src/render-system-extract-tail.ts +72 -60
- package/src/render-system-extract.ts +117 -87
- package/src/render-system-presentation.ts +56 -0
- package/src/render-system-producer-roots.ts +96 -0
- package/src/render-system-residency.ts +159 -0
- package/src/render-system.ts +601 -482
- package/src/scene/probe-blend.ts +96 -4
- package/src/scene/render-scene.ts +431 -281
- package/src/ssao-buffers.ts +95 -2
- package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
- package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
- package/src/surface/dynamic-input.ts +658 -0
- package/src/surface/gpu-driven.ts +147 -0
- package/src/surface/submission-observation.ts +517 -0
- package/src/systems/skin-palette-allocator.ts +4 -1
- package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
- package/src/temporal/frame-coordinator.ts +8 -1
- package/src/temporal/frame.ts +20 -0
- package/src/temporal/gpu.ts +101 -17
- package/src/temporal/inspection.ts +7 -1
- package/src/temporal/standard-scene-data.ts +5 -0
- package/src/temporal/target-coverage-attachment.ts +4 -0
- package/src/temporal/view.ts +13 -2
- package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
- package/src/transmission/single-layer-medium.ts +322 -0
- package/src/typed-render-graph-primitives.ts +191 -50
- package/src/volume/component.ts +10 -10
- package/src/volume/extract.ts +11 -5
- package/src/volume/inspection.ts +3 -0
- package/src/volume/passes.ts +95 -8
- package/src/volume/resources.ts +2 -1
- package/src/volume/temporal.ts +11 -0
- package/dist/chunk-4NNZQCL3.mjs.map +0 -1
- package/dist/chunk-6TFBXLRJ.mjs.map +0 -1
- package/dist/chunk-DRR7D3KX.mjs.map +0 -1
- package/dist/chunk-GWCEMDVR.mjs.map +0 -1
- package/dist/chunk-JCIRYQ4V.mjs.map +0 -1
- package/dist/chunk-JRIPCMYC.mjs.map +0 -1
- package/dist/chunk-LULCA3OF.mjs +0 -1713
- package/dist/chunk-LULCA3OF.mjs.map +0 -1
- package/dist/chunk-PA5DRJNI.mjs.map +0 -1
|
@@ -7,8 +7,13 @@ import { BatchTopology } from '../gpu-driven/batch-topology';
|
|
|
7
7
|
import { GPU_DRIVEN_VIEW_WGSL, GpuDrivenView } from '../gpu-driven/view-gpu';
|
|
8
8
|
import { GpuScene } from '../gpu-scene';
|
|
9
9
|
import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_MAP_READ } from '../gpu-usage';
|
|
10
|
+
import { getOpaqueResourceIdentity } from '../record/frame-snapshot';
|
|
10
11
|
import type { MaterialSnapshot, RenderableSnapshot } from '../render-system-extract';
|
|
11
12
|
import { RenderScene } from '../scene/render-scene';
|
|
13
|
+
import {
|
|
14
|
+
type SurfaceSubmissionCandidate,
|
|
15
|
+
SurfaceSubmissionObservationOwner,
|
|
16
|
+
} from '../surface/submission-observation';
|
|
12
17
|
|
|
13
18
|
const GPU_MAP_MODE_READ = 0x0001;
|
|
14
19
|
|
|
@@ -62,6 +67,10 @@ export interface GpuDrivenViewGpuEvidence {
|
|
|
62
67
|
readonly firstInstance: number;
|
|
63
68
|
readonly overflow: number;
|
|
64
69
|
readonly persistentTranslationX: number;
|
|
70
|
+
readonly firstReadbackFrame: number;
|
|
71
|
+
readonly recoveredReadbackFrame: number;
|
|
72
|
+
readonly firstVisible: number;
|
|
73
|
+
readonly recoveredVisible: number;
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
export interface GpuDrivenViewLifecycleEvidence {
|
|
@@ -71,7 +80,7 @@ export interface GpuDrivenViewLifecycleEvidence {
|
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
export async function runGpuDrivenViewLifecycleEvidence(
|
|
74
|
-
frames =
|
|
83
|
+
frames = 60,
|
|
75
84
|
): Promise<GpuDrivenViewLifecycleEvidence> {
|
|
76
85
|
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
77
86
|
const device = (await adapter.requestDevice()).unwrap();
|
|
@@ -247,7 +256,27 @@ export async function runGpuDrivenViewGpuEvidence(): Promise<GpuDrivenViewGpuEvi
|
|
|
247
256
|
})
|
|
248
257
|
.unwrap();
|
|
249
258
|
const graph = new RenderGraphBuilder<{ readonly encoder: RhiCommandEncoder }>();
|
|
250
|
-
|
|
259
|
+
const submissionOwner = new SurfaceSubmissionObservationOwner(() => 1);
|
|
260
|
+
const beginSubmission = (frameId: number, indirectOffset: number): SurfaceSubmissionCandidate => {
|
|
261
|
+
const candidate = submissionOwner.begin({
|
|
262
|
+
frameId,
|
|
263
|
+
requestedLane: 'gpu-driven',
|
|
264
|
+
deviceGeneration: 1,
|
|
265
|
+
resourceGeneration: view.inspect().resourceGeneration,
|
|
266
|
+
});
|
|
267
|
+
for (const pass of ['nearest-layer', 'color'] as const) {
|
|
268
|
+
candidate.record(pass, {
|
|
269
|
+
kind: 'draw-indexed-indirect',
|
|
270
|
+
indirectBufferIdentity: getOpaqueResourceIdentity(indirect as object),
|
|
271
|
+
indirectOffset,
|
|
272
|
+
pipelineIdentity: 33,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return candidate;
|
|
276
|
+
};
|
|
277
|
+
let currentSubmission = beginSubmission(10, 0);
|
|
278
|
+
view.setTelemetrySubmit({ frameId: 1, deviceGeneration: 0 });
|
|
279
|
+
view.addPasses(graph, 'gpu-driven', true, () => currentSubmission).unwrap();
|
|
251
280
|
const compiled = graph.compile({ device, surfaceSize: { width: 1, height: 1 } }).unwrap();
|
|
252
281
|
const encoder = device.createCommandEncoder({ label: 'gpu-driven-evidence' }).unwrap();
|
|
253
282
|
compiled.execute({ encoder }).unwrap();
|
|
@@ -262,6 +291,7 @@ export async function runGpuDrivenViewGpuEvidence(): Promise<GpuDrivenViewGpuEvi
|
|
|
262
291
|
64,
|
|
263
292
|
);
|
|
264
293
|
device.queue.submit([encoder.finish().unwrap()]).unwrap();
|
|
294
|
+
currentSubmission.submit(device.queue.onSubmittedWorkDone(), 1);
|
|
265
295
|
|
|
266
296
|
// Start observing immediately, then submit the cached graph again before
|
|
267
297
|
// the first mapAsync settles. The second copy must be skipped while the
|
|
@@ -276,9 +306,45 @@ export async function runGpuDrivenViewGpuEvidence(): Promise<GpuDrivenViewGpuEvi
|
|
|
276
306
|
compiled.execute({ encoder: overlappingEncoder }).unwrap();
|
|
277
307
|
device.queue.submit([overlappingEncoder.finish().unwrap()]).unwrap();
|
|
278
308
|
const lodSelection = await pendingLodSelection;
|
|
279
|
-
if (lodSelection === undefined
|
|
309
|
+
if (lodSelection === undefined) {
|
|
310
|
+
throw new Error('GPU-driven LOD selection readback unavailable or incomplete');
|
|
311
|
+
}
|
|
312
|
+
if (lodSelection.batches.length !== lodSelection.batchCount) {
|
|
280
313
|
throw new Error('GPU-driven LOD selection readback unavailable or incomplete');
|
|
281
314
|
}
|
|
315
|
+
const surfaceIndirectParameters = lodSelection.surfaceIndirectParameters;
|
|
316
|
+
if (surfaceIndirectParameters === undefined || surfaceIndirectParameters.length !== 2) {
|
|
317
|
+
throw new Error('GPU-driven Surface indirect parameter readback unavailable');
|
|
318
|
+
}
|
|
319
|
+
const firstSurfaceCommand = surfaceIndirectParameters[0];
|
|
320
|
+
if (
|
|
321
|
+
firstSurfaceCommand === undefined ||
|
|
322
|
+
firstSurfaceCommand.sequence !== lodSelection.surfaceReadback?.sequence ||
|
|
323
|
+
firstSurfaceCommand.frameId !== lodSelection.surfaceReadback?.frameId ||
|
|
324
|
+
firstSurfaceCommand.deviceGeneration !== lodSelection.surfaceReadback?.deviceGeneration ||
|
|
325
|
+
firstSurfaceCommand.resourceGeneration !== lodSelection.surfaceReadback?.resourceGeneration ||
|
|
326
|
+
firstSurfaceCommand.pass !== 'nearest-layer' ||
|
|
327
|
+
firstSurfaceCommand.indirectOffset !== 0 ||
|
|
328
|
+
firstSurfaceCommand.count !== 12 ||
|
|
329
|
+
firstSurfaceCommand.instanceCount !== 1 ||
|
|
330
|
+
firstSurfaceCommand.first !== 9 ||
|
|
331
|
+
firstSurfaceCommand.baseVertex !== 4 ||
|
|
332
|
+
firstSurfaceCommand.firstInstance !== 0
|
|
333
|
+
) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
`GPU-driven Surface indirect parameters do not match the submitted batch: ${JSON.stringify({
|
|
336
|
+
firstSurfaceCommand,
|
|
337
|
+
surfaceReadback: lodSelection.surfaceReadback,
|
|
338
|
+
})}`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
if (
|
|
342
|
+
lodSelection.submit === undefined ||
|
|
343
|
+
lodSelection.submit.frameId !== 1 ||
|
|
344
|
+
lodSelection.submit.deviceGeneration !== 0
|
|
345
|
+
) {
|
|
346
|
+
throw new Error('GPU-driven LOD telemetry lost the copy submit identity');
|
|
347
|
+
}
|
|
282
348
|
await device.queue.onSubmittedWorkDone();
|
|
283
349
|
|
|
284
350
|
// After unmap, a later cached-graph submission must restore telemetry
|
|
@@ -292,20 +358,49 @@ export async function runGpuDrivenViewGpuEvidence(): Promise<GpuDrivenViewGpuEvi
|
|
|
292
358
|
frustum.fromViewProjection(frustum.create(), recoveryProjection),
|
|
293
359
|
)
|
|
294
360
|
.unwrap();
|
|
361
|
+
view.setTelemetrySubmit({ frameId: 2, deviceGeneration: 0 });
|
|
295
362
|
const recoveryEncoder = device
|
|
296
363
|
.createCommandEncoder({ label: 'gpu-driven-evidence-recovery' })
|
|
297
364
|
.unwrap();
|
|
365
|
+
currentSubmission = beginSubmission(11, 32);
|
|
298
366
|
compiled.execute({ encoder: recoveryEncoder }).unwrap();
|
|
299
367
|
device.queue.submit([recoveryEncoder.finish().unwrap()]).unwrap();
|
|
368
|
+
currentSubmission.submit(device.queue.onSubmittedWorkDone(), 1);
|
|
300
369
|
await device.queue.onSubmittedWorkDone();
|
|
301
370
|
const recoveredLodSelection = await view.readLodSelection();
|
|
302
371
|
if (
|
|
303
372
|
recoveredLodSelection === undefined ||
|
|
304
373
|
recoveredLodSelection === lodSelection ||
|
|
305
|
-
recoveredLodSelection.visible === lodSelection.visible
|
|
374
|
+
recoveredLodSelection.visible === lodSelection.visible ||
|
|
375
|
+
lodSelection.surfaceReadback?.frameId !== 10 ||
|
|
376
|
+
recoveredLodSelection.surfaceReadback?.frameId !== 11
|
|
306
377
|
) {
|
|
307
378
|
throw new Error('GPU-driven LOD telemetry did not recover after a skipped copy');
|
|
308
379
|
}
|
|
380
|
+
if (
|
|
381
|
+
recoveredLodSelection.surfaceIndirectParameters?.some(
|
|
382
|
+
(command) =>
|
|
383
|
+
command.sequence !== recoveredLodSelection.surfaceReadback?.sequence ||
|
|
384
|
+
command.frameId !== recoveredLodSelection.surfaceReadback?.frameId ||
|
|
385
|
+
command.deviceGeneration !== recoveredLodSelection.surfaceReadback?.deviceGeneration ||
|
|
386
|
+
command.resourceGeneration !== recoveredLodSelection.surfaceReadback?.resourceGeneration,
|
|
387
|
+
)
|
|
388
|
+
) {
|
|
389
|
+
throw new Error('GPU-driven recovered Surface indirect parameters lost readback identity');
|
|
390
|
+
}
|
|
391
|
+
if (
|
|
392
|
+
recoveredLodSelection.submit === undefined ||
|
|
393
|
+
recoveredLodSelection.submit.frameId !== 2 ||
|
|
394
|
+
recoveredLodSelection.submit.deviceGeneration !== 0
|
|
395
|
+
) {
|
|
396
|
+
throw new Error('GPU-driven LOD telemetry reused a stale submit identity');
|
|
397
|
+
}
|
|
398
|
+
if (
|
|
399
|
+
lodSelection.surfaceReadback === undefined ||
|
|
400
|
+
recoveredLodSelection.surfaceReadback === undefined
|
|
401
|
+
) {
|
|
402
|
+
throw new Error('GPU-driven LOD telemetry lost Surface readback identity');
|
|
403
|
+
}
|
|
309
404
|
const validationError = await rawDevice.popErrorScope();
|
|
310
405
|
if (validationError !== null) {
|
|
311
406
|
throw new Error(`GPU-driven telemetry overlap validation failed: ${validationError.message}`);
|
|
@@ -337,6 +432,10 @@ export async function runGpuDrivenViewGpuEvidence(): Promise<GpuDrivenViewGpuEvi
|
|
|
337
432
|
firstInstance: indirectValue.getUint32(16, true),
|
|
338
433
|
overflow: overflowValue,
|
|
339
434
|
persistentTranslationX,
|
|
435
|
+
firstReadbackFrame: lodSelection.surfaceReadback.frameId,
|
|
436
|
+
recoveredReadbackFrame: recoveredLodSelection.surfaceReadback.frameId,
|
|
437
|
+
firstVisible: lodSelection.visible,
|
|
438
|
+
recoveredVisible: recoveredLodSelection.visible,
|
|
340
439
|
};
|
|
341
440
|
view.dispose();
|
|
342
441
|
sceneAvailability.scene.dispose();
|
|
@@ -12,7 +12,7 @@ import { RhiErrorListenerRegistry } from '../lifecycle';
|
|
|
12
12
|
import type { RenderFrameState } from '../record/frame-snapshot';
|
|
13
13
|
import { makeZeroCameraFallbackSnapshot } from '../record/frame-snapshot';
|
|
14
14
|
import type { PipelineState, RenderSystemInternals } from '../record/render-context';
|
|
15
|
-
import { ensureCompiledFrameGraph } from '../record/typed-frame-graph';
|
|
15
|
+
import { ensureCompiledFrameGraph, executeCompiledFrameGraph } from '../record/typed-frame-graph';
|
|
16
16
|
import type { RenderPipeline, RenderPipelineFrame } from '../render-pipeline';
|
|
17
17
|
import type {
|
|
18
18
|
ExtractedLights,
|
|
@@ -65,6 +65,7 @@ function updateSnapshot(value: RenderableSnapshot) {
|
|
|
65
65
|
function preparedFrame(view: GpuDrivenView): PreparedGpuDrivenFrame {
|
|
66
66
|
return {
|
|
67
67
|
topologySignature: `view-generation:${view.inspect().resourceGeneration}`,
|
|
68
|
+
worldKeys: [],
|
|
68
69
|
ownsAllDrawItems: true,
|
|
69
70
|
standardPbrFrameResources: {
|
|
70
71
|
materialBindGroups: [],
|
|
@@ -141,6 +142,7 @@ describe('GpuDrivenView graph projection', () => {
|
|
|
141
142
|
if (first === null) return;
|
|
142
143
|
const prepared: PreparedGpuDrivenFrame = {
|
|
143
144
|
topologySignature: 'stable-gpu-driven-test',
|
|
145
|
+
worldKeys: [],
|
|
144
146
|
ownsAllDrawItems: true,
|
|
145
147
|
standardPbrFrameResources: {
|
|
146
148
|
materialBindGroups: [],
|
|
@@ -239,7 +241,7 @@ describe('GpuDrivenView graph projection', () => {
|
|
|
239
241
|
{
|
|
240
242
|
name: 'gpu-driven.lod-selection-readback',
|
|
241
243
|
kind: 'copy',
|
|
242
|
-
dependencies: ['gpu-driven.frustum-compact'],
|
|
244
|
+
dependencies: ['gpu-driven.frustum-compact', 'gpu-driven.finalize-indirect'],
|
|
243
245
|
},
|
|
244
246
|
]);
|
|
245
247
|
const encoder = device.createCommandEncoder({ label: 'gpu-driven-view' }).unwrap();
|
|
@@ -390,7 +392,21 @@ describe('GpuDrivenView graph projection', () => {
|
|
|
390
392
|
const retirePrevious = vi.spyOn(compiledPrevious, 'retire');
|
|
391
393
|
const acceptedGraph = compile(preparedFrame(view), 2);
|
|
392
394
|
expect(acceptedGraph).not.toBe(compiledPrevious);
|
|
395
|
+
expect(retirePrevious).not.toHaveBeenCalled();
|
|
396
|
+
expect(destroyBuffer.mock.calls.some(([buffer]) => buffer === previousVisible)).toBe(false);
|
|
393
397
|
expect(frameState.compiledFrameGraphGeneration).toBe(2);
|
|
398
|
+
expect(retirePrevious).not.toHaveBeenCalled();
|
|
399
|
+
const acceptedEncoder = device
|
|
400
|
+
.createCommandEncoder({ label: 'gpu-driven-view-accepted' })
|
|
401
|
+
.unwrap();
|
|
402
|
+
expect(
|
|
403
|
+
executeCompiledFrameGraph(
|
|
404
|
+
internals,
|
|
405
|
+
frameState,
|
|
406
|
+
{ encoder: acceptedEncoder } as RenderPipelineFrame,
|
|
407
|
+
acceptedEncoder,
|
|
408
|
+
),
|
|
409
|
+
).toBe(true);
|
|
394
410
|
expect(retirePrevious).toHaveBeenCalledOnce();
|
|
395
411
|
await device.queue.onSubmittedWorkDone();
|
|
396
412
|
await Promise.resolve();
|
|
@@ -5,14 +5,7 @@ import {
|
|
|
5
5
|
} from './gpu-driven-view-gpu-evidence';
|
|
6
6
|
|
|
7
7
|
const browserReady = typeof navigator !== 'undefined' && navigator.gpu !== undefined;
|
|
8
|
-
const lifecycleFrames =
|
|
9
|
-
(
|
|
10
|
-
import.meta as ImportMeta & {
|
|
11
|
-
readonly env?: { readonly FORGEAX_BROWSER_CI_LIGHTWEIGHT?: string };
|
|
12
|
-
}
|
|
13
|
-
).env?.FORGEAX_BROWSER_CI_LIGHTWEIGHT === '1'
|
|
14
|
-
? 60
|
|
15
|
-
: 300;
|
|
8
|
+
const lifecycleFrames = 60;
|
|
16
9
|
|
|
17
10
|
describe.skipIf(!browserReady)('GPU-driven View Browser WebGPU', () => {
|
|
18
11
|
it('produces compact visible IDs and portable indirect args on the real browser device', async () => {
|
|
@@ -25,6 +18,10 @@ describe.skipIf(!browserReady)('GPU-driven View Browser WebGPU', () => {
|
|
|
25
18
|
firstInstance: 0,
|
|
26
19
|
overflow: 0,
|
|
27
20
|
persistentTranslationX: 0,
|
|
21
|
+
firstReadbackFrame: 10,
|
|
22
|
+
recoveredReadbackFrame: 11,
|
|
23
|
+
firstVisible: 1,
|
|
24
|
+
recoveredVisible: 0,
|
|
28
25
|
});
|
|
29
26
|
});
|
|
30
27
|
|
|
@@ -15,12 +15,16 @@ describe('GPU-driven View Dawn', () => {
|
|
|
15
15
|
firstInstance: 0,
|
|
16
16
|
overflow: 0,
|
|
17
17
|
persistentTranslationX: 0,
|
|
18
|
+
firstReadbackFrame: 10,
|
|
19
|
+
recoveredReadbackFrame: 11,
|
|
20
|
+
firstVisible: 1,
|
|
21
|
+
recoveredVisible: 0,
|
|
18
22
|
});
|
|
19
23
|
});
|
|
20
24
|
|
|
21
|
-
it('survives
|
|
25
|
+
it('survives 60 frames of spawn/despawn capacity crossings without stale buffers', async () => {
|
|
22
26
|
await expect(runGpuDrivenViewLifecycleEvidence()).resolves.toEqual({
|
|
23
|
-
frames:
|
|
27
|
+
frames: 60,
|
|
24
28
|
bufferRebuilds: 4,
|
|
25
29
|
candidateCapacity: 8,
|
|
26
30
|
});
|
|
@@ -99,7 +99,7 @@ describe('GPU-driven batch topology and CPU oracle', () => {
|
|
|
99
99
|
|
|
100
100
|
it('projects compact visible items into batch-local mesh and material indices', () => {
|
|
101
101
|
expect(GPU_DRIVEN_VIEW_WGSL).toMatch(
|
|
102
|
-
/
|
|
102
|
+
/vec4<u32>\(\s*candidate\.submitAdmission >> 1u,\s*materialOrPalette,\s*candidate\.primitiveIndex \+ 1u,\s*candidate\.generation,/,
|
|
103
103
|
);
|
|
104
104
|
expect(GPU_DRIVEN_VIEW_WGSL).toContain('(candidate.submitAdmission & 1u)');
|
|
105
105
|
expect(GPU_DRIVEN_VIEW_WGSL).toContain('instance.customDataStart');
|
|
@@ -145,6 +145,7 @@ describe('GPU pass timing generation-safe lifecycle', () => {
|
|
|
145
145
|
const oldReceipt = {
|
|
146
146
|
frameId: oldIdentity.frameId,
|
|
147
147
|
deviceGeneration: oldIdentity.deviceGeneration,
|
|
148
|
+
presentation: 'ready',
|
|
148
149
|
completed: Promise.resolve(ok(undefined)),
|
|
149
150
|
} as FrameReceipt;
|
|
150
151
|
store.register(oldReceipt, async () => {
|
|
@@ -17,9 +17,9 @@ describe.skipIf(!browserReady)('GPU pass timing Browser dev-server runner', () =
|
|
|
17
17
|
expect(omitted.timings).toBeUndefined();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// Each timing case creates a fresh renderer. Its lavapipe initialization can
|
|
21
|
+
// exceed Vitest's 15s default, including the raster-suppressed case below.
|
|
22
|
+
// Both retain the same bounded startup allowance and three-frame assertions.
|
|
23
23
|
it('keeps receipt facts and unsupported timing structured without stopping draw', {
|
|
24
24
|
timeout: 30_000,
|
|
25
25
|
}, async () => {
|
|
@@ -35,7 +35,9 @@ describe.skipIf(!browserReady)('GPU pass timing Browser dev-server runner', () =
|
|
|
35
35
|
expect(result.visible).toBe(true);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
it('keeps raster suppression as a test-only falsifier, never timing evidence',
|
|
38
|
+
it('keeps raster suppression as a test-only falsifier, never timing evidence', {
|
|
39
|
+
timeout: 30_000,
|
|
40
|
+
}, async () => {
|
|
39
41
|
const result = await runBrowserGpuPassTiming({ suppressRaster: true });
|
|
40
42
|
expect(result.rasterSuppressed).toBe(true);
|
|
41
43
|
expect(result.frames).toBe(3);
|
|
@@ -107,6 +107,13 @@ describe('GpuResidencyCache mesh stride', () => {
|
|
|
107
107
|
};
|
|
108
108
|
const world = new World();
|
|
109
109
|
const handle = world.allocSharedRef('MeshAsset', root);
|
|
110
|
+
// The root can be pulled before the lower pack finishes loading. A later
|
|
111
|
+
// call with the complete chain must upgrade the same cache key.
|
|
112
|
+
const rootOnly = store.ensureResident(handle, root);
|
|
113
|
+
expect(rootOnly.ok).toBe(true);
|
|
114
|
+
if (!rootOnly.ok) return;
|
|
115
|
+
expect(rootOnly.value.lodRanges).toBeUndefined();
|
|
116
|
+
|
|
110
117
|
const resident = store.ensureResident(handle, root, 0, [lower]);
|
|
111
118
|
expect(resident.ok).toBe(true);
|
|
112
119
|
if (!resident.ok) return;
|
|
@@ -120,6 +127,11 @@ describe('GpuResidencyCache mesh stride', () => {
|
|
|
120
127
|
expect(packedIndices).toBeDefined();
|
|
121
128
|
if (packedIndices === undefined) return;
|
|
122
129
|
expect(new Uint16Array(packedIndices.buffer)).toEqual(new Uint16Array([0, 1, 2, 0, 2, 1]));
|
|
130
|
+
|
|
131
|
+
// A stable chain remains an O(1) hit and does not replace the entry again.
|
|
132
|
+
const stable = store.ensureResident(handle, root, 0, [lower]);
|
|
133
|
+
expect(stable.ok).toBe(true);
|
|
134
|
+
if (stable.ok) expect(stable.value).toBe(resident.value);
|
|
123
135
|
});
|
|
124
136
|
|
|
125
137
|
it('refuses a payload whose bytes do not match the projected stride', async () => {
|
|
@@ -126,6 +126,34 @@ function instanceSnapshot(entityKey: number, translationX: number): RenderableSn
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
function multiInstanceSnapshot(
|
|
130
|
+
entityKey: number,
|
|
131
|
+
translations: readonly number[],
|
|
132
|
+
generations: readonly number[],
|
|
133
|
+
): RenderableSnapshot {
|
|
134
|
+
const base = snapshot(entityKey);
|
|
135
|
+
const transforms = new Float32Array(translations.length * 16);
|
|
136
|
+
for (let ordinal = 0; ordinal < translations.length; ordinal += 1) {
|
|
137
|
+
const offset = ordinal * 16;
|
|
138
|
+
transforms[offset] = 1;
|
|
139
|
+
transforms[offset + 5] = 1;
|
|
140
|
+
transforms[offset + 10] = 1;
|
|
141
|
+
transforms[offset + 12] = translations[ordinal] ?? 0;
|
|
142
|
+
transforms[offset + 15] = 1;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
...base,
|
|
146
|
+
instances: {
|
|
147
|
+
transforms,
|
|
148
|
+
generations: new Uint32Array(generations),
|
|
149
|
+
instanceCount: translations.length,
|
|
150
|
+
cacheKey: entityKey,
|
|
151
|
+
archVersion: 1,
|
|
152
|
+
revision: 1,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
129
157
|
function persistentFrame(
|
|
130
158
|
renderables: readonly RenderableSnapshot[],
|
|
131
159
|
cameras: ExtractedFrame['cameras'] = [],
|
|
@@ -163,6 +191,123 @@ function createDevice(queue: RecordingQueue): RhiNullDevice {
|
|
|
163
191
|
}
|
|
164
192
|
|
|
165
193
|
describe('GpuScene', () => {
|
|
194
|
+
it('keeps renderer-owned tables resident across World detach and reattach', () => {
|
|
195
|
+
const queue = new RecordingQueue();
|
|
196
|
+
const device = createDevice(queue);
|
|
197
|
+
const persistent = new PersistentRenderScene({ getDevice: () => device });
|
|
198
|
+
const firstWorld = new World();
|
|
199
|
+
const firstLease = createRenderReadLease(firstWorld);
|
|
200
|
+
|
|
201
|
+
persistent.extractComposition(
|
|
202
|
+
[firstWorld],
|
|
203
|
+
{ cameraOwner: 0, resourceOwner: 0 },
|
|
204
|
+
0,
|
|
205
|
+
() => persistentFrame([snapshot(1)]),
|
|
206
|
+
[firstLease],
|
|
207
|
+
);
|
|
208
|
+
const first = persistent.compositionGpuDrivenState();
|
|
209
|
+
expect(first).toBeDefined();
|
|
210
|
+
if (first === undefined) return;
|
|
211
|
+
|
|
212
|
+
persistent.detach(firstWorld);
|
|
213
|
+
firstLease.dispose();
|
|
214
|
+
const secondWorld = new World();
|
|
215
|
+
const secondLease = createRenderReadLease(secondWorld);
|
|
216
|
+
persistent.extractComposition(
|
|
217
|
+
[secondWorld],
|
|
218
|
+
{ cameraOwner: 0, resourceOwner: 0 },
|
|
219
|
+
1,
|
|
220
|
+
() => persistentFrame([snapshot(2)]),
|
|
221
|
+
[secondLease],
|
|
222
|
+
);
|
|
223
|
+
const second = persistent.compositionGpuDrivenState();
|
|
224
|
+
expect(second).toBeDefined();
|
|
225
|
+
expect(second?.scene).toBe(first.scene);
|
|
226
|
+
|
|
227
|
+
secondLease.dispose();
|
|
228
|
+
persistent.dispose();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('uploads only changed material bytes for shared value updates and retries failed writes', () => {
|
|
232
|
+
const queue = new FailingQueue();
|
|
233
|
+
const created = GpuScene.create(createDevice(queue), 8).unwrap();
|
|
234
|
+
expect(created.status).toBe('available');
|
|
235
|
+
if (created.status !== 'available') return;
|
|
236
|
+
const scene = created.scene;
|
|
237
|
+
const projection = new RenderScene();
|
|
238
|
+
const sources = Array.from({ length: 4 }, (_, index) => ({
|
|
239
|
+
...snapshot(index),
|
|
240
|
+
gpuDrivenDraws: [
|
|
241
|
+
{
|
|
242
|
+
kind: 'indexed' as const,
|
|
243
|
+
first: 0,
|
|
244
|
+
count: 36,
|
|
245
|
+
baseVertex: 0,
|
|
246
|
+
materialSlot: 0,
|
|
247
|
+
topology: 'triangle-list' as const,
|
|
248
|
+
pipelineClass: 'pbr',
|
|
249
|
+
materialResourceClass: '{}',
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
}));
|
|
253
|
+
scene.sync(projection.apply(sources.map(updateSnapshot))).unwrap();
|
|
254
|
+
const changed = { ...material, roughness: 0.3 };
|
|
255
|
+
const updated = sources.map((source) => ({
|
|
256
|
+
...source,
|
|
257
|
+
material: changed,
|
|
258
|
+
materials: [changed],
|
|
259
|
+
}));
|
|
260
|
+
const delta = projection.apply(updated.map(updateSnapshot));
|
|
261
|
+
queue.failWrites = true;
|
|
262
|
+
expect(scene.sync(delta).ok).toBe(false);
|
|
263
|
+
queue.failWrites = false;
|
|
264
|
+
queue.writes.length = 0;
|
|
265
|
+
const retried = scene.sync(projection.apply([])).unwrap();
|
|
266
|
+
expect(retried.bytes).toBe(4 * GPU_SCENE_LAYOUTS.material.stride);
|
|
267
|
+
expect(queue.writes).toHaveLength(1);
|
|
268
|
+
const roughness = gpuSceneFieldOffset(GPU_SCENE_LAYOUTS.material, 'roughness');
|
|
269
|
+
const upload = queue.writes[0];
|
|
270
|
+
if (upload === undefined) throw new Error('expected material upload');
|
|
271
|
+
for (let index = 0; index < 4; index++) {
|
|
272
|
+
expect(
|
|
273
|
+
new DataView(upload.bytes.buffer).getFloat32(
|
|
274
|
+
index * GPU_SCENE_LAYOUTS.material.stride + roughness,
|
|
275
|
+
true,
|
|
276
|
+
),
|
|
277
|
+
).toBeCloseTo(0.3);
|
|
278
|
+
}
|
|
279
|
+
// Fresh snapshots with identical bytes must not upload. Later in-place
|
|
280
|
+
// caller edits remain visible: packed rows are shared only within sync.
|
|
281
|
+
expect(scene.sync(projection.apply(updated.map(updateSnapshot))).unwrap().bytes).toBe(0);
|
|
282
|
+
changed.roughness = 0.6;
|
|
283
|
+
expect(scene.sync(projection.apply(updated.map(updateSnapshot))).unwrap().bytes).toBe(
|
|
284
|
+
4 * GPU_SCENE_LAYOUTS.material.stride,
|
|
285
|
+
);
|
|
286
|
+
const first = updated[0];
|
|
287
|
+
const draw = first?.gpuDrivenDraws[0];
|
|
288
|
+
if (first === undefined || draw === undefined) throw new Error('expected draw fixture');
|
|
289
|
+
queue.writes.length = 0;
|
|
290
|
+
const geometryChanged = {
|
|
291
|
+
...first,
|
|
292
|
+
localAabb: new Float32Array([-2, -1, -1, 2, 1, 1]),
|
|
293
|
+
gpuDrivenDraws: [{ ...draw, first: 6, count: 12, baseVertex: -3 }],
|
|
294
|
+
};
|
|
295
|
+
expect(scene.sync(projection.apply([updateSnapshot(geometryChanged)])).unwrap().bytes).toBe(
|
|
296
|
+
GPU_SCENE_LAYOUTS.primitive.stride + GPU_SCENE_LAYOUTS.drawTemplate.stride,
|
|
297
|
+
);
|
|
298
|
+
const drawUpload = queue.writes.find(
|
|
299
|
+
({ bytes }) => bytes.length === GPU_SCENE_LAYOUTS.drawTemplate.stride,
|
|
300
|
+
);
|
|
301
|
+
if (drawUpload === undefined) throw new Error('expected changed draw upload');
|
|
302
|
+
expect(
|
|
303
|
+
new DataView(drawUpload.bytes.buffer).getInt32(
|
|
304
|
+
gpuSceneFieldOffset(GPU_SCENE_LAYOUTS.drawTemplate, 'baseVertex'),
|
|
305
|
+
true,
|
|
306
|
+
),
|
|
307
|
+
).toBe(-3);
|
|
308
|
+
scene.dispose();
|
|
309
|
+
});
|
|
310
|
+
|
|
166
311
|
it('preserves authored base-color alpha in the scene material row', () => {
|
|
167
312
|
const row = packStandardPbrMaterialRow(createStandardPbrArtifactReceipt(), {
|
|
168
313
|
...material,
|
|
@@ -399,6 +544,69 @@ describe('GpuScene', () => {
|
|
|
399
544
|
});
|
|
400
545
|
});
|
|
401
546
|
|
|
547
|
+
it('maps GPU previous instance transforms by stable generation after reorder', () => {
|
|
548
|
+
const queue = new RecordingQueue();
|
|
549
|
+
const created = GpuScene.create(createDevice(queue), 4).unwrap();
|
|
550
|
+
if (created.status !== 'available') return;
|
|
551
|
+
const projection = new RenderScene();
|
|
552
|
+
projection.setTemporalTracking(true);
|
|
553
|
+
const initial = multiInstanceSnapshot(0, [2, 8], [101, 202]);
|
|
554
|
+
created.scene.sync(projection.apply([updateSnapshot(initial)])).unwrap();
|
|
555
|
+
projection.commitSubmission(projection.captureSubmission([{ worldId: 0, entityKey: 0 }]));
|
|
556
|
+
|
|
557
|
+
const reordered = multiInstanceSnapshot(0, [9, 3], [202, 101]);
|
|
558
|
+
const delta = projection.apply([updateInstances(reordered)]);
|
|
559
|
+
const slot = projection.slotsSnapshot()[0];
|
|
560
|
+
expect(slot).toBeDefined();
|
|
561
|
+
if (slot === undefined) return;
|
|
562
|
+
expect(projection.temporalSnapshotBySlot(slot.slot)?.motionValid).toBe(true);
|
|
563
|
+
|
|
564
|
+
queue.writes.length = 0;
|
|
565
|
+
created.scene
|
|
566
|
+
.sync(delta, (current) => projection.temporalSnapshotBySlot(current.slot))
|
|
567
|
+
.unwrap();
|
|
568
|
+
const transformWrite = queue.writes.find(
|
|
569
|
+
({ bytes }) => bytes.byteLength === 2 * GPU_SCENE_LAYOUTS.transform.stride,
|
|
570
|
+
);
|
|
571
|
+
expect(transformWrite).toBeDefined();
|
|
572
|
+
if (transformWrite === undefined) return;
|
|
573
|
+
const previousTranslation =
|
|
574
|
+
gpuSceneFieldOffset(GPU_SCENE_LAYOUTS.transform, 'previousWorld') + 12 * 4;
|
|
575
|
+
const transforms = new DataView(transformWrite.bytes.buffer);
|
|
576
|
+
expect(transforms.getFloat32(previousTranslation, true)).toBe(8);
|
|
577
|
+
expect(
|
|
578
|
+
transforms.getFloat32(GPU_SCENE_LAYOUTS.transform.stride + previousTranslation, true),
|
|
579
|
+
).toBe(2);
|
|
580
|
+
|
|
581
|
+
created.scene.commitTemporalFrame().unwrap();
|
|
582
|
+
projection.commitSubmission(projection.captureSubmission([{ worldId: 0, entityKey: 0 }]));
|
|
583
|
+
const retired = multiInstanceSnapshot(0, [11], [202]);
|
|
584
|
+
const retiredDelta = projection.apply([updateInstances(retired)]);
|
|
585
|
+
expect(projection.temporalSnapshotBySlot(slot.slot)?.motionValid).toBe(true);
|
|
586
|
+
queue.writes.length = 0;
|
|
587
|
+
created.scene
|
|
588
|
+
.sync(retiredDelta, (current) => projection.temporalSnapshotBySlot(current.slot))
|
|
589
|
+
.unwrap();
|
|
590
|
+
const compactedRows = queue.writes
|
|
591
|
+
.filter(({ bytes }) => bytes.byteLength % GPU_SCENE_LAYOUTS.transform.stride === 0)
|
|
592
|
+
.flatMap(({ bytes }) =>
|
|
593
|
+
Array.from({ length: bytes.byteLength / GPU_SCENE_LAYOUTS.transform.stride }, (_, row) => {
|
|
594
|
+
const view = new DataView(bytes.buffer);
|
|
595
|
+
return {
|
|
596
|
+
current: view.getFloat32(
|
|
597
|
+
row * GPU_SCENE_LAYOUTS.transform.stride + currentWorldOffset() + 12 * 4,
|
|
598
|
+
true,
|
|
599
|
+
),
|
|
600
|
+
previous: view.getFloat32(
|
|
601
|
+
row * GPU_SCENE_LAYOUTS.transform.stride + previousTranslation,
|
|
602
|
+
true,
|
|
603
|
+
),
|
|
604
|
+
};
|
|
605
|
+
}),
|
|
606
|
+
);
|
|
607
|
+
expect(compactedRows).toContainEqual({ current: 11, previous: 9 });
|
|
608
|
+
});
|
|
609
|
+
|
|
402
610
|
it('advances GPU previous transforms after a successful temporal commit', () => {
|
|
403
611
|
const queue = new RecordingQueue();
|
|
404
612
|
const created = GpuScene.create(createDevice(queue), 4).unwrap();
|
|
@@ -797,20 +1005,13 @@ describe('GpuScene', () => {
|
|
|
797
1005
|
expect(delta.updated).toBe(1);
|
|
798
1006
|
const result = created.scene.sync(delta).unwrap();
|
|
799
1007
|
expect(result.bytes).toBe(
|
|
800
|
-
GPU_SCENE_LAYOUTS.
|
|
801
|
-
GPU_SCENE_LAYOUTS.drawTemplate.stride +
|
|
802
|
-
GPU_SCENE_LAYOUTS.material.stride +
|
|
803
|
-
2 * GPU_SCENE_LAYOUTS.transform.stride,
|
|
1008
|
+
GPU_SCENE_LAYOUTS.material.stride + 2 * GPU_SCENE_LAYOUTS.transform.stride,
|
|
804
1009
|
);
|
|
805
1010
|
expect(result.grew).toBe(false);
|
|
806
1011
|
const next = { ...mixed, material: { ...nextMaterial, roughness: 0.7 } };
|
|
807
1012
|
next.materials = [next.material];
|
|
808
1013
|
const materialOnly = created.scene.sync(projection.apply([updateSnapshot(next)])).unwrap();
|
|
809
|
-
expect(materialOnly.bytes).toBe(
|
|
810
|
-
GPU_SCENE_LAYOUTS.primitive.stride +
|
|
811
|
-
GPU_SCENE_LAYOUTS.drawTemplate.stride +
|
|
812
|
-
GPU_SCENE_LAYOUTS.material.stride,
|
|
813
|
-
);
|
|
1014
|
+
expect(materialOnly.bytes).toBe(GPU_SCENE_LAYOUTS.material.stride);
|
|
814
1015
|
});
|
|
815
1016
|
});
|
|
816
1017
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { err, RhiError } from '@forgeax/engine-rhi';
|
|
2
|
+
import { rhi } from '@forgeax/engine-rhi-null';
|
|
3
|
+
import { expect, it, vi } from 'vitest';
|
|
4
|
+
import { GPU_SHADER_STAGE_VERTEX } from '../gpu-stage';
|
|
5
|
+
import { GPU_BUFFER_USAGE_STORAGE } from '../gpu-usage';
|
|
6
|
+
import { resolveGeometryInstanceBuffer } from '../record/main-pass-geometry';
|
|
7
|
+
|
|
8
|
+
it('refuses an instance draw when allocation fails instead of drawing the identity fallback', async () => {
|
|
9
|
+
const device = (await (await rhi.requestAdapter()).unwrap().requestDevice()).unwrap();
|
|
10
|
+
const identity = device.createBuffer({ size: 128, usage: GPU_BUFFER_USAGE_STORAGE }).unwrap();
|
|
11
|
+
const layout = device
|
|
12
|
+
.createBindGroupLayout({
|
|
13
|
+
entries: [
|
|
14
|
+
{ binding: 0, visibility: GPU_SHADER_STAGE_VERTEX, buffer: { type: 'read-only-storage' } },
|
|
15
|
+
],
|
|
16
|
+
})
|
|
17
|
+
.unwrap();
|
|
18
|
+
const failure = new RhiError({
|
|
19
|
+
code: 'rhi-not-available',
|
|
20
|
+
expected: 'an instance buffer allocation',
|
|
21
|
+
hint: 'recover the renderer',
|
|
22
|
+
});
|
|
23
|
+
const allocation = vi.spyOn(device, 'createBuffer').mockReturnValue(err(failure));
|
|
24
|
+
const bindGroup = vi.spyOn(device, 'createBindGroup');
|
|
25
|
+
const fire = vi.fn();
|
|
26
|
+
try {
|
|
27
|
+
const result = resolveGeometryInstanceBuffer(
|
|
28
|
+
{
|
|
29
|
+
runtime: { device, errorRegistry: { fire } },
|
|
30
|
+
pipelineState: { identityInstanceBuffer: identity, instancesBindGroupLayout: layout },
|
|
31
|
+
frameState: { instanceBuffers: new Map() },
|
|
32
|
+
bindGroupCounts: { createBindGroup: 0, keys: [] },
|
|
33
|
+
} as unknown as Parameters<typeof resolveGeometryInstanceBuffer>[0],
|
|
34
|
+
{
|
|
35
|
+
source: {
|
|
36
|
+
worldId: 0,
|
|
37
|
+
entityKey: 1,
|
|
38
|
+
instances: {
|
|
39
|
+
transforms: new Float32Array(32),
|
|
40
|
+
instanceCount: 2,
|
|
41
|
+
cacheKey: 1,
|
|
42
|
+
archVersion: 0,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
} as Parameters<typeof resolveGeometryInstanceBuffer>[1],
|
|
46
|
+
[],
|
|
47
|
+
false,
|
|
48
|
+
);
|
|
49
|
+
expect(result).toBeNull();
|
|
50
|
+
expect(fire).toHaveBeenCalledWith(failure);
|
|
51
|
+
expect(bindGroup).not.toHaveBeenCalled();
|
|
52
|
+
} finally {
|
|
53
|
+
allocation.mockRestore();
|
|
54
|
+
bindGroup.mockRestore();
|
|
55
|
+
device.destroyBuffer(identity).unwrap();
|
|
56
|
+
}
|
|
57
|
+
});
|