@forgeax/engine-render 0.1.27 → 0.1.29
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 +842 -124
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-host.d.ts +52 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -0
- 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 +19 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +7 -0
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- 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/failure-location.d.ts +12 -0
- package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts +6 -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 +6 -0
- 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 +3 -3
- package/dist/{chunk-ADHHVYLW.mjs → chunk-33XZFTQU.mjs} +245 -39
- package/dist/chunk-33XZFTQU.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-KM2NOX2I.mjs → chunk-BQVQOQJH.mjs} +958 -254
- package/dist/chunk-BQVQOQJH.mjs.map +1 -0
- package/dist/{chunk-HKXTW355.mjs → chunk-BTWH7BP4.mjs} +386 -17
- package/dist/chunk-BTWH7BP4.mjs.map +1 -0
- package/dist/{chunk-TMIASV2N.mjs → chunk-FUODTTIA.mjs} +3934 -676
- package/dist/chunk-FUODTTIA.mjs.map +1 -0
- package/dist/{chunk-FJ6P52EE.mjs → chunk-ILQ5MEZF.mjs} +3 -17
- package/dist/chunk-ILQ5MEZF.mjs.map +1 -0
- package/dist/{chunk-E34VL5VI.mjs → chunk-JQTSEI3Z.mjs} +3 -3
- package/dist/{chunk-E34VL5VI.mjs.map → chunk-JQTSEI3Z.mjs.map} +1 -1
- package/dist/{chunk-GE3SDD72.mjs → chunk-NICXSHAT.mjs} +16 -38
- package/dist/chunk-NICXSHAT.mjs.map +1 -0
- package/dist/{chunk-ZZ4YQ474.mjs → chunk-NRNXBLIJ.mjs} +577 -7
- package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
- package/dist/{chunk-JDWARUOI.mjs → chunk-PA5DRJNI.mjs} +17 -3
- package/dist/chunk-PA5DRJNI.mjs.map +1 -0
- package/dist/{chunk-XDP6XE5V.mjs → chunk-S4DNKQYQ.mjs} +1118 -446
- package/dist/chunk-S4DNKQYQ.mjs.map +1 -0
- package/dist/components/camera.d.ts +62 -2
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -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/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/construct-renderer.mjs +13805 -7186
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +11 -17
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/device/mesh-residency-lifetime.d.ts +21 -0
- package/dist/device/mesh-residency-lifetime.d.ts.map +1 -0
- package/dist/dynamic-geometry.d.ts +125 -0
- package/dist/dynamic-geometry.d.ts.map +1 -0
- package/dist/environment/background.d.ts +6 -0
- package/dist/environment/background.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 +7 -4
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +33 -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/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.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +144 -23
- 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-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 +6 -0
- package/dist/hdrp-buffers.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.map +1 -1
- package/dist/index.d.ts +21 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +8 -7
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +164 -0
- 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 +118 -10
- 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 +5 -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 +21 -5
- package/dist/pbr-pipeline.d.ts.map +1 -1
- 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-builder.d.ts +2 -1
- 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/points-lines/standard-owner.d.ts +1 -1
- package/dist/points-lines/standard-owner.d.ts.map +1 -1
- 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 +106 -5
- 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 +39 -4
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +7 -8
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +38 -1
- 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 +12 -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 +2 -9
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +76 -26
- 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 +35 -7
- 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/record-owner.d.ts +0 -2
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +185 -30
- 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 +64 -5
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +7 -5
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +101 -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 +34 -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 +29 -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/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 +2 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/temporal-view.d.ts +2 -0
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +35 -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/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__/detached-inspection.integration.test.ts +5 -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-host.unit.test.ts +1123 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +233 -0
- package/src/__tests__/factory-contract.integration.test.ts +73 -22
- 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-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 +287 -6
- 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 +264 -0
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +349 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1417 -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 +1075 -93
- 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 +159 -52
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -1
- 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 +559 -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 +52 -2
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +53 -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 +37 -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__/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 +35 -4
- 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 +104 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +2 -1
- package/src/__tests__/render-error-code-owner.test-d.ts +4 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +22 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +15 -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 +914 -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 +61 -1
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
- package/src/__tests__/shader-manifest-fixture.ts +39 -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 +118 -14
- package/src/__tests__/specular-environment-scale.dawn.test.ts +156 -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-format-probe-gating.integration.test.ts +82 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +91 -0
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +112 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +946 -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 +44 -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 +38 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
- package/src/__tests__/standard-profile-oracle.unit.test.ts +22 -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 +193 -0
- package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +778 -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 +341 -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__/texture-usage-surface.unit.test.ts +4 -2
- 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/assembly/atmosphere-shader-prewarm.ts +38 -0
- package/src/assembly/dynamic-geometry-host.ts +887 -0
- 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 +36 -0
- package/src/assembly/material/assembly.ts +38 -0
- package/src/assembly/material-shader-policy.ts +71 -28
- package/src/assembly/recovery/failure-location.ts +62 -0
- package/src/assembly/recovery/renderer-recover.ts +19 -17
- package/src/assembly/renderer-facade.ts +29 -0
- 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 +32 -0
- 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 +142 -121
- 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 +675 -614
- package/src/assembly/webgpu-vertex-layouts.ts +233 -87
- package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
- package/src/components/camera.ts +287 -7
- package/src/components/index.ts +1 -0
- package/src/components/instances.ts +45 -18
- package/src/components/scene-instance.ts +2 -1
- package/src/components/screen-space-reflection.ts +21 -0
- package/src/components/sprite-animation.ts +3 -3
- package/src/components/sprite-instances.ts +1 -1
- package/src/device/gpu-residency.ts +96 -93
- package/src/device/mesh-residency-lifetime.ts +73 -0
- package/src/dynamic-geometry.ts +736 -0
- package/src/environment/background.ts +263 -0
- package/src/errors/gpu-driven.ts +39 -5
- package/src/errors/render.ts +27 -3
- package/src/extract/camera.ts +26 -1
- package/src/extract/gpu-driven.ts +185 -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 +424 -102
- package/src/gpu-driven/pbr-program.ts +31 -0
- package/src/gpu-driven/prepared-draw.ts +29 -4
- package/src/gpu-driven/production-raster.ts +2267 -694
- package/src/gpu-driven/shadow-views.ts +539 -0
- package/src/gpu-driven/view-gpu.ts +188 -41
- package/src/gpu-scene-schema.ts +52 -11
- package/src/gpu-scene.ts +186 -99
- package/src/gpu-texture-usage.ts +1 -0
- package/src/hdrp-buffers.ts +72 -36
- package/src/ibl/frame-resources.ts +38 -0
- package/src/ibl/skylight-bind-group.ts +5 -4
- package/src/index.ts +125 -1
- package/src/inspection-types.ts +191 -0
- 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 +145 -0
- package/src/materials.ts +44 -8
- package/src/pbr-pipeline.ts +159 -17
- package/src/pipeline/standard-forward-lane.ts +52 -24
- 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 +192 -59
- package/src/pipeline/standard-post.ts +186 -27
- package/src/pipeline-builder.ts +16 -6
- package/src/pipeline-spec-types.ts +4 -0
- package/src/pipeline-spec.ts +37 -29
- package/src/points-lines/standard-owner.ts +6 -14
- package/src/prepare/prepared-graphics-resolver.ts +9 -2
- package/src/record/__tests__/material-slot-plan.unit.test.ts +48 -0
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +2 -29
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +36 -5
- 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 +12 -11
- package/src/record/frame-snapshot.ts +133 -23
- package/src/record/frame-targets.ts +9 -0
- package/src/record/frame.ts +727 -133
- package/src/record/main-pass-geometry.ts +162 -98
- package/src/record/main-pass-material.ts +115 -26
- package/src/record/main-pass-sprite-draws.ts +18 -107
- package/src/record/main-pass.ts +78 -8
- package/src/record/material-uniforms.ts +1 -1
- package/src/record/mesh-ssbo.ts +59 -15
- package/src/record/prepared-material-bindings.ts +234 -0
- package/src/record/recovery-pipeline.ts +59 -136
- package/src/record/render-context.ts +92 -22
- package/src/record/shadow-pass.ts +1231 -209
- package/src/record/skybox-post-pass.ts +14 -3
- package/src/record/typed-frame-graph.ts +1006 -53
- package/src/record/view-ubo.ts +11 -0
- package/src/recovery/render-system-candidate.ts +90 -66
- package/src/recovery/render-system-roots.ts +113 -0
- package/src/recovery/types.ts +77 -0
- package/src/reflection/record-owner.ts +26 -54
- package/src/render-contract.ts +219 -25
- package/src/render-graph-primitives.ts +6 -2
- package/src/render-pipeline.ts +70 -1
- package/src/render-system-extract-tail.ts +409 -160
- package/src/render-system-extract.ts +376 -102
- package/src/render-system-projections.ts +241 -0
- package/src/render-system.ts +692 -791
- package/src/scene/render-scene-types.ts +13 -4
- package/src/scene/render-scene.ts +569 -355
- package/src/scene/shadow-visibility.ts +21 -0
- 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 +777 -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 +74 -12
- package/src/temporal/history.ts +3 -1
- package/src/temporal/inspection.ts +2 -1
- package/src/temporal/standard-scene-data.ts +3 -1
- package/src/temporal/temporal-view.ts +3 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +204 -139
- package/src/typed-render-graph-primitives.ts +266 -74
- package/src/typed-shadow-passes.ts +122 -12
- package/src/volume/temporal.ts +10 -0
- package/dist/chunk-ADHHVYLW.mjs.map +0 -1
- package/dist/chunk-FJ6P52EE.mjs.map +0 -1
- package/dist/chunk-GE3SDD72.mjs.map +0 -1
- package/dist/chunk-GF523LHF.mjs.map +0 -1
- package/dist/chunk-HKXTW355.mjs.map +0 -1
- package/dist/chunk-JDWARUOI.mjs.map +0 -1
- package/dist/chunk-KM2NOX2I.mjs.map +0 -1
- package/dist/chunk-TMIASV2N.mjs.map +0 -1
- package/dist/chunk-XDP6XE5V.mjs.map +0 -1
- package/dist/chunk-ZZ4YQ474.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
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ GPU, Browser, and Dawn evidence that cannot execute is `not-run` or
|
|
|
33
33
|
`unavailable`, never a verified structural substitute.
|
|
34
34
|
|
|
35
35
|
The public frame vocabulary is intentionally small. `Camera` is the authoring
|
|
36
|
-
owner for tone, antialiasing, bloom, and `historyVersion`; `Atmosphere` and
|
|
36
|
+
owner for tone, exposure, color grading, antialiasing, bloom, and `historyVersion`; `Atmosphere` and
|
|
37
37
|
`Fog` are independent ECS components. The renderer extracts these facts into
|
|
38
38
|
an immutable `FramePlan`, records one frame, and returns a `FrameReceipt`.
|
|
39
39
|
`FrameReceipt` is the only successful synchronous proof that the host submit
|
|
@@ -47,8 +47,10 @@ An AI consumer can start with the public sequence `state -> inspect -> recover
|
|
|
47
47
|
`alive` admits a frame; `device-lost` permits one explicit recovery flight;
|
|
48
48
|
`recovering` shares that flight; `faulted` requires a new Renderer; and
|
|
49
49
|
`disposed` is terminal. `inspect()` is detached POD evidence for the current
|
|
50
|
-
state and
|
|
50
|
+
state, recovery attempt, and named output contract. After `recover()` succeeds, submit the current
|
|
51
51
|
draw input and use the new `FrameReceipt` as the proof for that generation.
|
|
52
|
+
Recovery prepares the last successfully submitted workset; the next draw still
|
|
53
|
+
consumes current World edits and admits new resources through normal residency.
|
|
52
54
|
|
|
53
55
|
`inspect().recovery` is always present. Its `phase` is `null` outside an active
|
|
54
56
|
attempt and otherwise follows `quiesce`, `acquire-adapter`, `acquire-device`,
|
|
@@ -194,6 +196,28 @@ environment owners or multiple fog owners return structured errors with a
|
|
|
194
196
|
code-specific `detail`; they do not create a second registry or silently pick
|
|
195
197
|
the first entity. Frame facts contain IDs, revisions, and POD values only, not
|
|
196
198
|
textures, buffers, devices, or other live GPU objects.
|
|
199
|
+
|
|
200
|
+
The Standard graph renders an Atmosphere source as a 128-by-128, six-face
|
|
201
|
+
`rgba16float` sky cube and a background pass before scene geometry. The selected
|
|
202
|
+
DirectionalLight supplies the sun direction, color, and illuminance; the sun
|
|
203
|
+
angle is in radians, and zero radius disables the visible disc. Geometry covers
|
|
204
|
+
the background through ordinary scene rendering, including transparent blending.
|
|
205
|
+
The cube excludes the disc. Weak ambient lighting remains the explicit `Skylight`
|
|
206
|
+
input; selecting an Atmosphere does not silently add a second ambient light.
|
|
207
|
+
|
|
208
|
+
The cube has 786,432 texture payload bytes plus 280 bytes of graph-owned
|
|
209
|
+
parameter and vertex payload, before backend allocation alignment. Its six faces update together when the selected environment
|
|
210
|
+
signature changes, and become current only after successful frame submission.
|
|
211
|
+
An unchanged source runs only the background pass. Resize, topology replacement,
|
|
212
|
+
and recovery use normal graph retirement and reconstruction. GPU pass observations
|
|
213
|
+
name `atmosphere-prepare`, `atmosphere-cube-0` through `atmosphere-cube-5`, and
|
|
214
|
+
`atmosphere-background`; unavailable timestamps remain explicitly unavailable.
|
|
215
|
+
The source lifecycle inspection and graph resource accounting describe different
|
|
216
|
+
owners: a source revision is not a claim that its GPU cube was submitted.
|
|
217
|
+
|
|
218
|
+
The reusable [Wave 1 recipe](../../scripts/dev-verify/wave1-rendering/README.md)
|
|
219
|
+
shows the public scene inputs and associated acceptance gates.
|
|
220
|
+
|
|
197
221
|
## GPU pass timing: opt in, draw, observe, branch on status
|
|
198
222
|
|
|
199
223
|
GPU pass timing is disabled by default. Opt in once on `createRenderer`, keep the
|
|
@@ -291,6 +315,18 @@ over-budget requests. A World with no `PointLightShadow` remains `inactive`;
|
|
|
291
315
|
there is no pseudo-disabled shadow mode. Directional CSM remains a separate
|
|
292
316
|
shadow owner; PointLightShadow is not a second directional-light path.
|
|
293
317
|
|
|
318
|
+
The cube atlas uses negated world axes: cube lookup is `lightPos - worldPos`,
|
|
319
|
+
and each face camera looks opposite its cube axis with the corresponding face-up
|
|
320
|
+
vector. This matches WebGPU framebuffer V without reflecting clip-space Y or
|
|
321
|
+
changing authored front-face/culling state. Raster matrices and sampling must
|
|
322
|
+
change together; a standalone Z flip is not a valid conversion.
|
|
323
|
+
|
|
324
|
+
Camera-culled scene meshes remain shadow candidates when their bounds intersect
|
|
325
|
+
an admitted light frustum. Their projected dispatch contains only `ShadowCaster`,
|
|
326
|
+
never Forward/Deferred work. Persistent composition uses the merged light frame,
|
|
327
|
+
including lights from another World. `frustumStats.culled` continues to count
|
|
328
|
+
camera rejection even when the retained object casts a shadow.
|
|
329
|
+
|
|
294
330
|
## Render happy path
|
|
295
331
|
|
|
296
332
|
`RenderScene -> Standard Pipeline -> DeviceScope -> FrameReceipt` is the only frame
|
|
@@ -319,20 +355,50 @@ and keeps shadow, PBR, IBL, SSAO, bloom, tone, antialiasing, sky, material, VFX,
|
|
|
319
355
|
ownership inside the same graph and submit boundary. CPU and WebGL2 remain capability lanes for the
|
|
320
356
|
Cluster membership producer or for mesh/instance storage fallback; they do not reintroduce a
|
|
321
357
|
PointLight/SpotLight buffer or a fixed four-light ABI.
|
|
322
|
-
## SSR
|
|
358
|
+
## SSR admission and bounded inspection
|
|
359
|
+
|
|
360
|
+
Coverage counters are `null` until GPU measurements are supplied; missing
|
|
361
|
+
measurements must not be interpreted as zero hits. The hello-ssr Browser smoke
|
|
362
|
+
captures a v7 RHI tape, reads trace/temporal/compose outputs at their producing
|
|
363
|
+
work indices, and requires positive confidence plus a measurable composition
|
|
364
|
+
delta. It also verifies that the composed resource reaches a later draw and
|
|
365
|
+
retains its pixels through that draw. Ordinary `hello-ssr` startup enables SSR.
|
|
366
|
+
|
|
367
|
+
The Standard G-buffer stores the visible material's response once:
|
|
368
|
+
|
|
369
|
+
| Target | RGB | Alpha |
|
|
370
|
+
|:--|:--|:--|
|
|
371
|
+
| Normal/roughness | Encoded world normal | Roughness |
|
|
372
|
+
| Albedo/metallic | Linear albedo | Metallic |
|
|
373
|
+
| Specular response/AO | Unit-radiance specular BRDF response, including occlusion | AO |
|
|
374
|
+
|
|
375
|
+
Forward lighting still owns direct light and emissive contributions. SSR uses
|
|
376
|
+
one fullscreen additive draw, not a geometry redraw: its RGB delta is
|
|
377
|
+
`confidence * (filteredRadiance * specularResponse - fallback)`. This prevents
|
|
378
|
+
equal-depth overlapping geometry from subtracting the fallback more than once.
|
|
379
|
+
Resolved radiance has a confidence-weighted mip chain; composition samples
|
|
380
|
+
`roughness² * (mipCount - 1)` with trilinear reconstruction. Receiver admission
|
|
381
|
+
uses the current lighting coverage, compatible half-resolution normals, and
|
|
382
|
+
the shared View's enable/roughness cutoff. It does not clip the rough lobe to
|
|
383
|
+
the central mirror ray's hit confidence. Material roughness changes the
|
|
384
|
+
reflection's filter footprint, not just its opacity; confidence remains bounded
|
|
385
|
+
by the current receiver's roughness fade. Coarse mips are spatial approximations,
|
|
386
|
+
not separate roughness-dependent ray traces.
|
|
323
387
|
|
|
324
388
|
> [!IMPORTANT]
|
|
325
389
|
> SSR M0 is a consumer-only admission boundary. It reads detached producer,
|
|
326
|
-
> r32float format, and temporal receipts with one four-field
|
|
327
|
-
> identity.
|
|
328
|
-
> `fallback-only` and emits zero SSR work
|
|
329
|
-
>
|
|
390
|
+
> r32float format, and optional prior temporal receipts with one four-field
|
|
391
|
+
> integration identity. Missing required, stale, structural-only, or mismatched receipts stay
|
|
392
|
+
> `fallback-only` and emits zero SSR work. An admitted Standard frame then
|
|
393
|
+
> projects the renderer-owned M1 spatial chain and M2 history through
|
|
394
|
+
> `renderer.inspect().ssr`; paired Browser/Dawn pixel evidence is still the
|
|
395
|
+
> gate for calling the result SSR v1.
|
|
330
396
|
|
|
331
397
|
```mermaid
|
|
332
398
|
flowchart LR
|
|
333
399
|
P["producer detached receipt"] --> A["admitSsrM0"]
|
|
334
400
|
F["format stage receipt"] --> A
|
|
335
|
-
T["temporal
|
|
401
|
+
T["optional prior temporal receipt"] -.-> A
|
|
336
402
|
A -->|"all identity and verdict checks pass"| E["admitted: parent M1 may unlock"]
|
|
337
403
|
A -->|"missing, stale, unavailable, or mismatched"| B["fallback-only: zero work"]
|
|
338
404
|
```
|
|
@@ -355,16 +421,64 @@ if (admission.status === 'fallback-only') {
|
|
|
355
421
|
}
|
|
356
422
|
```
|
|
357
423
|
|
|
424
|
+
After the ordinary frame, read the detached spatial projection without opening
|
|
425
|
+
the graph or a GPU handle:
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
const spatial = renderer.inspect().ssr;
|
|
429
|
+
switch (spatial.status) {
|
|
430
|
+
case 'not-requested':
|
|
431
|
+
case 'requested':
|
|
432
|
+
case 'fallback-only':
|
|
433
|
+
// Base probe / Skylight / neutral reflection remains visible.
|
|
434
|
+
break;
|
|
435
|
+
case 'structural-only':
|
|
436
|
+
// The graph is admitted, but this run has no executable shader carrier.
|
|
437
|
+
break;
|
|
438
|
+
case 'admitted':
|
|
439
|
+
// Inspect history, passRoster, fallbackSource, and coverage before evidence.
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
| `renderer.inspect().ssr.status` | Meaning | SSR work |
|
|
445
|
+
|:--|:--|:--|
|
|
446
|
+
| `not-requested` | Active camera has no `ScreenSpaceReflection` | Exact zero |
|
|
447
|
+
| `requested` | Authoring is present; M0/spatial facts are not yet complete | Exact zero |
|
|
448
|
+
| `fallback-only` | A closed config, lane, input, capability, or owner receipt failed | Exact zero |
|
|
449
|
+
| `structural-only` | Spatial admission and graph topology exist, but no executable shader source is bound | Topology only |
|
|
450
|
+
| `admitted` | Standard spatial path and shader manifest are bound | Inspect `history`, `passRoster`, `fallbackSource`, and coverage |
|
|
451
|
+
|
|
452
|
+
`history.state`, `history.bytes`, `history.resetCount`, `passRoster`, and
|
|
453
|
+
`fallbackSource` are bounded POD facts. They are evidence about the current
|
|
454
|
+
owner state, not an acceptance claim: M1/M2 remain an implementation checkpoint
|
|
455
|
+
until the paired Browser/Dawn 300-frame carrier, readback, falsifiers, and
|
|
456
|
+
performance gates are present.
|
|
457
|
+
|
|
458
|
+
SSR temporal feedback and presentation use distinct coordinates without another
|
|
459
|
+
allocation. The persistent color/depth and normal/confidence slots use fixed,
|
|
460
|
+
unjittered half-resolution centers (`2*p + 0.5` in full-resolution pixels).
|
|
461
|
+
Current trace samples are reconstructed onto that grid once; history reprojection
|
|
462
|
+
uses unjittered motion and validates depth/normal before interpolation. The
|
|
463
|
+
separate resolved output stays on the current jittered raster grid for composition
|
|
464
|
+
and reflection mip generation. Never feed that presentation reconstruction back
|
|
465
|
+
into history: repeated jitter filtering loses reflected texture contrast.
|
|
466
|
+
|
|
358
467
|
Use `inspect -> owner recovery -> matching submit -> reinspect` as the complete
|
|
359
468
|
AI route. No returned field is a device, graph node, texture, buffer, encoder,
|
|
360
469
|
or other live handle. A stable generation keeps `resetCount`, `rebuildCount`,
|
|
361
470
|
and additional format probe work at zero; a changed generation must be
|
|
362
471
|
reinspected before admission can be considered again.
|
|
363
472
|
|
|
364
|
-
`inspect()` is a synchronous snapshot.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
473
|
+
`inspect()` is a synchronous snapshot. Explicit `RendererOptions.ssrIdentity`
|
|
474
|
+
opts into one real format probe during initialization of each device generation;
|
|
475
|
+
initialization and recovery await it before the first synchronous draw. A renderer
|
|
476
|
+
without that option does not probe. Camera demand still controls all per-frame
|
|
477
|
+
SSR attachments, passes, history and uploads. An unavailable format stays
|
|
478
|
+
fallback-only; an absent previous temporal receipt does not block fresh spatial
|
|
479
|
+
SSR. Current-frame motion/coverage comes from the Standard graph. History is
|
|
480
|
+
read only after its normal successful-submit validation, never fabricated to
|
|
481
|
+
admit the first frame.
|
|
368
482
|
|
|
369
483
|
There are two action projections. `admission.failure.detail.action` is the coarse
|
|
370
484
|
M0 gate instruction (`retry` for demand/readiness and `rebuild` for a missing or
|
|
@@ -377,8 +491,8 @@ API; use the existing owner operation shown here, then submit and inspect again:
|
|
|
377
491
|
| `reflectionFallbackInspection.recoveryAction = use-LKG` | Keep the compatible probe row and draw the next frame | Read the committed row and matching temporal receipt |
|
|
378
492
|
| `reflectionFallbackInspection.recoveryAction = use-Skylight` / `use-neutral` | Change the World-owned probe/`Skylight` source, then draw | Confirm selected source and committed generation |
|
|
379
493
|
| `reflectionFallbackInspection.recoveryAction = recapture` / `rebuild` / `retry` | Draw again so the producer/RHI owner retries pending work | Await completion and inspect the owner receipt/failure |
|
|
380
|
-
| `admission.failure.detail.action = retry` / `rebuild` | Retry the consumer frame, or rebuild the named owner receipt | Reinspect
|
|
381
|
-
| device-loss recovery | `await renderer.recover()` before the next draw | Confirm the replacement `deviceGeneration
|
|
494
|
+
| `admission.failure.detail.action = retry` / `rebuild` | Retry the consumer frame, or rebuild the named owner receipt | Reinspect required receipts and any supplied temporal receipt |
|
|
495
|
+
| device-loss recovery | `await renderer.recover()` before the next draw | Confirm the replacement `deviceGeneration` and its completed format probe |
|
|
382
496
|
|
|
383
497
|
SSR never invokes these operations or mutates producer/device state; this table
|
|
384
498
|
maps the typed action to the existing public World and Renderer seams.
|
|
@@ -410,18 +524,56 @@ The exact public names and stable consumer IDs are machine-readable in
|
|
|
410
524
|
|
|
411
525
|
Progressive disclosure is intentional: read Camera configuration first, then
|
|
412
526
|
the graph facts, backend facts, and finally the stable observation identity.
|
|
413
|
-
The JSON-safe `RenderInspection` projection
|
|
527
|
+
The JSON-safe `RenderInspection` projection keeps renderer lifecycle facts and
|
|
528
|
+
the output contract in separate named subtrees. Read `renderer.inspect().output`
|
|
529
|
+
for output facts; do not infer output state from renderer-level fields:
|
|
414
530
|
|
|
415
531
|
| Field | Meaning | Owner |
|
|
416
532
|
|:--|:--|:--|
|
|
417
|
-
| `outputTransform` | `forgeax::standard::output-transform` | Standard post chain |
|
|
418
|
-
| `displayEncoded` | final output is display encoded | Output Transform |
|
|
419
|
-
| `intermediateFormat` | linear intermediate format (`rgba16float`) | Standard target owner |
|
|
420
|
-
| `surfaceStorage` / `surfaceDisplay` | raw storage and display surface formats | surface adapter |
|
|
421
|
-
| `endpoint` | `surface.storage.raw` | backend adapter |
|
|
422
|
-
| `capability` / `error` | structured availability and recovery facts | RHI/render surface |
|
|
533
|
+
| `output.outputTransform` | `forgeax::standard::output-transform` | Standard post chain |
|
|
534
|
+
| `output.displayEncoded` | final output is display encoded | Output Transform |
|
|
535
|
+
| `output.intermediateFormat` | linear intermediate format (`rgba16float`) | Standard target owner |
|
|
536
|
+
| `output.surfaceStorage` / `output.surfaceDisplay` | raw storage and display surface formats | surface adapter |
|
|
537
|
+
| `output.endpoint` | `surface.storage.raw` | backend adapter |
|
|
538
|
+
| `output.capability` / `output.error` | structured availability and recovery facts | RHI/render surface |
|
|
423
539
|
| `observation.observationId` / `frameId` | stable correlation refs | renderer observation owner |
|
|
424
540
|
|
|
541
|
+
### Auto exposure and 3D LUT authoring
|
|
542
|
+
|
|
543
|
+
Exposure is a Camera-owned closed union. Manual exposure is a positive
|
|
544
|
+
multiplier; auto exposure carries a fallback, EV compensation, bounded EV
|
|
545
|
+
range, and non-negative adaptation rates. The renderer owns the GPU receipt,
|
|
546
|
+
but the author owns the Camera input:
|
|
547
|
+
|
|
548
|
+
```ts
|
|
549
|
+
world.set(camera, Camera, {
|
|
550
|
+
exposure: { kind: 'auto', fallback: 1, compensationEv: 0,
|
|
551
|
+
rangeEv: [-4, 4], rates: [2, 1] },
|
|
552
|
+
}).unwrap();
|
|
553
|
+
|
|
554
|
+
const output = renderer.inspect().output;
|
|
555
|
+
if (output.autoExposure?.receipt.committed !== true) {
|
|
556
|
+
throw new Error(output.error?.hint ?? 'auto exposure is not committed');
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Color grading uses the ordinary shared `TextureAsset` route. Import/cook the
|
|
561
|
+
3D LUT through Pack/Catalog, load the shared handle by its GUID/source key, and
|
|
562
|
+
bind it to the Camera; there is no second LUT registry or URL-derived identity:
|
|
563
|
+
|
|
564
|
+
```ts
|
|
565
|
+
world.set(camera, Camera, {
|
|
566
|
+
colorLut: world.allocSharedRef('TextureAsset', lutHandle),
|
|
567
|
+
colorLutStrength: 0.75,
|
|
568
|
+
}).unwrap();
|
|
569
|
+
const lut = renderer.inspect().output.standardLut;
|
|
570
|
+
// Match lut.sourceKey and lut.receipt.frameId to the submitted FrameReceipt.
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
`output.autoExposure`, `output.standardLut`, and `output.error` are detached
|
|
574
|
+
facts. A fallback or last-known-good row is a recovery observation, not proof
|
|
575
|
+
that a physical Browser/Dawn workload or timing gate completed.
|
|
576
|
+
|
|
425
577
|
`renderer.inspect()` and `renderer.observe()` never carry ROI pixels, scanlines,
|
|
426
578
|
brightness or level metrics. The hello-fxaa `hello-fxaa/dark-gradient/v1`
|
|
427
579
|
fixture report owns those fields and records the fixed camera, low-light scene,
|
|
@@ -508,6 +660,83 @@ gate exercises the browser delivery path. Runtime inspection uses the public
|
|
|
508
660
|
belongs in `packages/render/bench` and its dev-verify producer. Render does
|
|
509
661
|
not expose a timing controller or a second membership-specific API.
|
|
510
662
|
|
|
663
|
+
## TAA accumulation and private stability
|
|
664
|
+
|
|
665
|
+
TAA reconstructs unjittered current color, validates reprojected depth, and
|
|
666
|
+
accumulates HDR using luminance-preserving compressed-domain weights. History
|
|
667
|
+
depth validation recognizes mixed current-depth coverage, following Three's
|
|
668
|
+
TRAA edge exception: background/geometry and depth-discontinuous 3x3 edges
|
|
669
|
+
may retain clipped history instead of alternating between accumulated and
|
|
670
|
+
raw current color. Uniform-depth disocclusion, absent current geometry,
|
|
671
|
+
out-of-bounds reprojection, invalid global history, motion and reactivity
|
|
672
|
+
retain their rejection or weighting behavior. The GPU coverage regression
|
|
673
|
+
executes the complete resolve, including the non-edge negative cases.
|
|
674
|
+
History clipping uses YCoCg bounds. After eight accepted stationary frames, its bounds
|
|
675
|
+
cover the texels supporting the reconstructed neighborhood; motion, rejection,
|
|
676
|
+
or reactivity resets that private age. The history still updates every frame:
|
|
677
|
+
this is not a frozen screenshot or an appearance acceptance claim.
|
|
678
|
+
|
|
679
|
+
The private age continues to 128 accepted stationary frames without another
|
|
680
|
+
surface. History weight stays at 0.95 through frame 64, then smoothly rises to
|
|
681
|
+
0.99 by frame 128 to reduce residual phase response. The eight-frame clipping
|
|
682
|
+
footprint remains unchanged. Clipping relaxation runs earlier, from accepted
|
|
683
|
+
frame 8 through frame 64, while the 0.95 history weight still permits responsive
|
|
684
|
+
accumulation. Releasing it only after increasing history weight preserves a
|
|
685
|
+
biased clipped mean and causes prolonged static drift. Once settled, a single phase's unsupported
|
|
686
|
+
color does not erase accumulated subpixel coverage: clipping resumes only
|
|
687
|
+
after eight consecutive unsupported samples in the same signed dominant RGB
|
|
688
|
+
direction. Finding support or reversing direction breaks that streak.
|
|
689
|
+
Persistent unmarked color changes therefore restore color clipping within one
|
|
690
|
+
complete jitter cycle. This color correction preserves the accepted stationary
|
|
691
|
+
age and its reconstruction footprint; it must not collapse the next frame's
|
|
692
|
+
neighborhood from 5x5 to 3x3. Motion,
|
|
693
|
+
reactivity or rejection immediately reset
|
|
694
|
+
the age and restore fast recovery; this delay is necessary because zero
|
|
695
|
+
velocity alone does not establish settled radiance.
|
|
696
|
+
|
|
697
|
+
An optional secondary-source reactivity texture joins the scene reactive lane
|
|
698
|
+
before history weighting and stability counting. Standard forwards SSR's
|
|
699
|
+
existing hit-source mask through a typed sampled read; TAA takes a conservative
|
|
700
|
+
7x7 maximum at that texture's resolution. This covers the measured vacated
|
|
701
|
+
reflection edge missed by the former radius-one footprint; it is a bounded
|
|
702
|
+
dilation, not an arbitrary-speed reflected-motion guarantee. Reflected source motion can therefore
|
|
703
|
+
reset a stationary receiver without changing the receiver's velocity or depth.
|
|
704
|
+
The disabled path binds existing temporal data but does not sample it as a mask;
|
|
705
|
+
no extra texture or pass is allocated. The 658-frame Browser and Dawn feedback
|
|
706
|
+
variant checks secondary-motion reset and recovery; Dawn additionally verifies
|
|
707
|
+
a failing control that ignores the mask. Each cyclic input step checks the
|
|
708
|
+
Karis recurrence from the actual stored history against its adjacent FP16
|
|
709
|
+
values; an independent infinite-precision tail detects accumulated bias.
|
|
710
|
+
The premature high-weight counterexample must also fail. This does not establish visual
|
|
711
|
+
convergence or ghost-free SSR.
|
|
712
|
+
|
|
713
|
+
Color writeback stochastically selects adjacent representable FP16 values before
|
|
714
|
+
attachment conversion. This prevents consistently downward conversion from
|
|
715
|
+
accumulating a dark history bias without widening the history textures. Its
|
|
716
|
+
deterministic pixel/frame hash shares one RGB threshold, leaves alpha and
|
|
717
|
+
temporal metadata unchanged, and does not freeze the jitter sequence. Browser
|
|
718
|
+
and Dawn run the same 528-frame raster-feedback regression against an analytic
|
|
719
|
+
compressed-HDR recurrence; this precision gate is not visual acceptance.
|
|
720
|
+
|
|
721
|
+
| Ping-pong history | Format | Consumer |
|
|
722
|
+
|:--|:--|:--|
|
|
723
|
+
| Color | `rgba16float` | TAA and downstream HDR processing |
|
|
724
|
+
| Motion, depth, reactivity | `rgba16float` | Unchanged `temporal-v1` metadata, including Motion Blur |
|
|
725
|
+
| Stationary age and settled clipping streak | `r8unorm` | TAA only; never stored in the reactive lane |
|
|
726
|
+
|
|
727
|
+
The private byte uses codes 0–128 for stationary age and 129–170 for one
|
|
728
|
+
through seven unsupported samples in six signed RGB directions. The eighth
|
|
729
|
+
matching sample restores clipping and resets age. This is one encoded state,
|
|
730
|
+
not an additional attachment or a change to downstream temporal metadata.
|
|
731
|
+
Browser and Dawn raster-feedback tests cover phase-local coverage, unmarked
|
|
732
|
+
color steps, and a falsifier that permanently disables clipping.
|
|
733
|
+
|
|
734
|
+
These six surfaces share the existing successful-submit, abort, replacement,
|
|
735
|
+
and retirement lifecycle. Texture accounting is 34 bytes per output pixel;
|
|
736
|
+
`inspect().temporal.resources` includes active, candidate, and retiring states.
|
|
737
|
+
Fullscreen pipeline targets come from the declared attachment format list,
|
|
738
|
+
not from inspecting shader source text for a particular output structure.
|
|
739
|
+
|
|
511
740
|
## Motion Blur temporal consumer
|
|
512
741
|
|
|
513
742
|
The Standard camera may carry the presence-enabled `MotionBlur` component. Its
|
|
@@ -664,69 +893,135 @@ infer state from URLs, or repair a producer failure. Aggregate counts are
|
|
|
664
893
|
derived from the observations, so an AI can inspect first, repair or recook the
|
|
665
894
|
producer, and retry without guessing at hidden renderer state.
|
|
666
895
|
|
|
667
|
-
###
|
|
896
|
+
### Renderer-owned Instances and CPU bounds
|
|
668
897
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
898
|
+
`Instances` is a lightweight ECS association containing only a
|
|
899
|
+
renderer-owned `collectionId`. Create and mutate the canonical packed matrices
|
|
900
|
+
through the same `renderer.instances` owner; it validates mat4 stride, keeps
|
|
901
|
+
revision/capacity/dirty ranges, and returns detached inspection snapshots.
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
function identityMatrices(count: number): Float32Array {
|
|
905
|
+
const matrices = new Float32Array(count * 16);
|
|
906
|
+
for (let index = 0; index < count; index += 1) {
|
|
907
|
+
const offset = index * 16;
|
|
908
|
+
// Column-major identity, with a deterministic layout so the instances are visible.
|
|
909
|
+
matrices[offset] = 1;
|
|
910
|
+
matrices[offset + 5] = 1;
|
|
911
|
+
matrices[offset + 10] = 1;
|
|
912
|
+
matrices[offset + 15] = 1;
|
|
913
|
+
matrices[offset + 12] = (index % 100) * 2;
|
|
914
|
+
matrices[offset + 14] = Math.floor(index / 100) * 2;
|
|
915
|
+
}
|
|
916
|
+
return matrices;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const collection = renderer.instances.create({
|
|
920
|
+
transforms: identityMatrices(instanceCount),
|
|
921
|
+
}).unwrap();
|
|
922
|
+
world.spawn(
|
|
923
|
+
{ component: MeshFilter, data: { assetHandle: cube } },
|
|
924
|
+
{ component: MeshRenderer, data: {} },
|
|
925
|
+
{ component: Instances, data: { collectionId: collection.collectionId } },
|
|
926
|
+
);
|
|
927
|
+
|
|
928
|
+
const patch = identityMatrices(1);
|
|
929
|
+
patch[12] = 4;
|
|
930
|
+
renderer.instances.update(collection.collectionId, {
|
|
931
|
+
start: 0,
|
|
932
|
+
transforms: patch,
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// Replacement keeps the logical identity and publishes a new revision.
|
|
936
|
+
renderer.instances.replace(collection.collectionId, identityMatrices(instanceCount + 1));
|
|
937
|
+
const current = renderer.instances.inspect(collection.collectionId).unwrap();
|
|
938
|
+
const detached = renderer.instances.snapshot(collection.collectionId).unwrap();
|
|
939
|
+
renderer.instances.release(collection.collectionId);
|
|
940
|
+
```
|
|
673
941
|
|
|
674
|
-
|
|
942
|
+
The lifecycle is deliberately one owner: `create` allocates canonical CPU
|
|
943
|
+
storage, `replace` publishes a complete new revision (and grows capacity when
|
|
944
|
+
needed), `update` validates a bounded matrix interval and records its dirty
|
|
945
|
+
range, `inspect` returns identity/count/capacity/revision, `snapshot` returns a
|
|
946
|
+
detached matrix copy plus dirty ranges for extraction, and `release` makes the
|
|
947
|
+
collection id terminal. A caller never provides a GPU buffer or maintains
|
|
948
|
+
backend-specific chunks. `detached.transforms` is an observation, not mutable
|
|
949
|
+
renderer state.
|
|
950
|
+
|
|
951
|
+
Render snapshots one collection per frame and derives CPU union bounds from the
|
|
952
|
+
mesh AABB, entity world matrix, and detached matrices. Direct, GPU-driven, and
|
|
953
|
+
backend fallback lanes consume that same projection; internal GPU chunks never
|
|
954
|
+
become ECS or game-side state. Missing, malformed, or empty collections fail
|
|
955
|
+
closed or remain a conservative no-cull result without manufacturing an
|
|
956
|
+
identity instance. The initial payload must contain valid matrices: an
|
|
957
|
+
all-zero mat4 has a zero homogeneous `w` and can produce a blank frame. Do not
|
|
958
|
+
add matrix bytes, bounds, or chunk fields to the public `Instances` component or
|
|
959
|
+
duplicate this renderer fact in Pack/asset state.
|
|
960
|
+
|
|
961
|
+
#### Instance inspection and recovery
|
|
962
|
+
|
|
963
|
+
`renderer.inspect().instanceCollections` is detached, bounded evidence for
|
|
964
|
+
every live collection. Each row contains:
|
|
965
|
+
|
|
966
|
+
| Field | Meaning |
|
|
675
967
|
|:--|:--|
|
|
676
|
-
|
|
|
677
|
-
|
|
|
678
|
-
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
GPU validation and stable-upload receipts.
|
|
710
|
-
|
|
711
|
-
Population checks use the same World component route:
|
|
968
|
+
| `collectionId`, `count`, `capacity`, `revision` | Logical identity and canonical authoring revision. |
|
|
969
|
+
| `residentGeneration`, `lane` | Device generation and the selected `direct-storage`, `chunked-storage`, `direct-uniform`, `chunked-uniform`, `unresident`, or `unavailable` lane. |
|
|
970
|
+
| `dirtyRanges`, `uploadRanges` | Canonical pending edits and the ranges uploaded by the current resident. |
|
|
971
|
+
| `uploadedBytes`, `requestedBytes`, `supportedBytes` | Measured upload and admission byte facts; unsupported limits remain explicit. |
|
|
972
|
+
| `backend`, `owner` | Backend identity and the fixed owner `renderer.instances`. |
|
|
973
|
+
| `error` | A structured record-stage failure with `code`, `expected`, `hint`, and typed `detail` facts. |
|
|
974
|
+
|
|
975
|
+
Authoring failures use the same closed `InstanceCollectionError` contract.
|
|
976
|
+
For example, an invalid stride is rejected before publication and carries
|
|
977
|
+
typed facts instead of requiring message parsing:
|
|
978
|
+
|
|
979
|
+
```ts
|
|
980
|
+
const result = renderer.instances.create({ transforms: new Float32Array(17) });
|
|
981
|
+
if (!result.ok) {
|
|
982
|
+
const { code, expected, hint, detail, facts } = result.error;
|
|
983
|
+
// facts expose requestedBytes, supportedBytes, backend, owner, cause, and
|
|
984
|
+
// recovery; detail retains operation metadata. Repair the payload and retry.
|
|
985
|
+
void code;
|
|
986
|
+
void expected;
|
|
987
|
+
void hint;
|
|
988
|
+
void detail;
|
|
989
|
+
void facts;
|
|
990
|
+
}
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
After a `device-lost` transition, call `await renderer.recover()` and retry the
|
|
994
|
+
same draw request. The renderer drops generation-owned residents, keeps the
|
|
995
|
+
canonical collection, and uploads every matrix into the new resident before
|
|
996
|
+
recording; it does not reuse an old partial dirty range for uninitialized GPU
|
|
997
|
+
memory. If inspection reports `unavailable`, follow its `error.detail.recovery`
|
|
998
|
+
and repair the named capability/producer before retrying.
|
|
999
|
+
|
|
1000
|
+
The large-instance smoke exercises the real Dawn path for all admitted sizes:
|
|
712
1001
|
|
|
713
1002
|
```bash
|
|
714
|
-
|
|
715
|
-
INSTANCE_COUNT
|
|
1003
|
+
for count in 1500 10000 20000; do
|
|
1004
|
+
INSTANCE_COUNT=$count SMOKE_MIN_FRAMES=600 SMOKE_DURATION_MS=0 \
|
|
1005
|
+
pnpm --filter @forgeax/parity-instancing-static smoke
|
|
1006
|
+
done
|
|
716
1007
|
```
|
|
717
1008
|
|
|
718
|
-
|
|
1009
|
+
Each run must retain one logical collection, submit real work, upload on its
|
|
1010
|
+
first resident frame, and report zero transform-upload bytes on unchanged
|
|
1011
|
+
frames. Browser WebGPU acceptance additionally uses the browser/dev-server
|
|
1012
|
+
transport through the same production fixture and parameterizes the same three
|
|
1013
|
+
populations:
|
|
1014
|
+
|
|
1015
|
+
```bash
|
|
1016
|
+
pnpm exec vitest run --config vitest.browser.config.ts --project=browser \
|
|
1017
|
+
apps/parity/instancing-static/src/__tests__/instances.browser.test.ts
|
|
1018
|
+
```
|
|
719
1019
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
conservative no-cull result; it never creates a synthetic identity instance.
|
|
726
|
-
The GPU path keeps the mesh-local AABB and performs per-instance visibility,
|
|
727
|
-
while the CPU path uses the derived union only to avoid dropping an entity
|
|
728
|
-
whose origin is outside the view. Do not add `bounds` to the public
|
|
729
|
-
`Instances` component or duplicate this renderer fact in Pack/asset state.
|
|
1020
|
+
The Browser test submits 600 frames for each population, asserts a real
|
|
1021
|
+
`webgpu` backend, captures the first and stable `instanceCollections` upload
|
|
1022
|
+
witnesses, reads the presented canvas through the browser compositor, and
|
|
1023
|
+
requires visible spread at three or more grid samples with zero renderer/RHI
|
|
1024
|
+
error events. Dawn structural evidence alone is not a browser validation.
|
|
730
1025
|
|
|
731
1026
|
## Points and Lines authoring (M1)
|
|
732
1027
|
|
|
@@ -959,18 +1254,32 @@ state published by `World.update()`.
|
|
|
959
1254
|
|
|
960
1255
|
## Persistent render scene
|
|
961
1256
|
|
|
962
|
-
|
|
963
|
-
then
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1257
|
+
Every attached World composition bootstraps one renderer-owned CPU projection,
|
|
1258
|
+
then consumes each World's component and shared-reference change versions. One identity-based update
|
|
1259
|
+
publication merges content, root transforms, and instance changes, including
|
|
1260
|
+
when all occur in the same frame. Its GPU projection compares affected matrix
|
|
1261
|
+
and metadata rows before uploading them. An unchanged frame retains its existing
|
|
1262
|
+
snapshot; unrelated gameplay component writes do not invalidate render state.
|
|
1263
|
+
There is no exclusive transform/instance scene admission followed by a separate
|
|
1264
|
+
rebuild implementation. Missing producer evidence causes conservative source
|
|
1265
|
+
extraction into the same retained projection. World reordering and catalog
|
|
1266
|
+
reconciliation preserve surviving slots and their submitted temporal history.
|
|
967
1267
|
|
|
968
1268
|
Mutable shared payloads use the same explicit-dirty rule: mutate the resolved
|
|
969
1269
|
payload, then call `world.sharedRefs.markChanged(handle)`. The renderer compares
|
|
970
|
-
one monotonic shared-ref epoch on the no-change path,
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1270
|
+
one monotonic shared-ref epoch on the no-change path, reads changed handles
|
|
1271
|
+
when it advances, and refreshes the projection slots indexed by changed
|
|
1272
|
+
material or mesh handles. Missing source evidence requests conservative
|
|
1273
|
+
source reconciliation. Camera, light, and environment facts refresh independently
|
|
1274
|
+
of geometry publication; their edits do not discard retained geometry.
|
|
1275
|
+
|
|
1276
|
+
Instance collection revisions refresh only their consumers. Visibility and
|
|
1277
|
+
parent changes refresh the affected subtree, and joint changes refresh the
|
|
1278
|
+
retained skin consumers. Shadow pass facts belong to each retained renderable;
|
|
1279
|
+
frame ownership is derived before camera culling so offscreen casters remain
|
|
1280
|
+
available. CPU visibility, occlusion, and LOD consume this same projection on
|
|
1281
|
+
every frame, including frames submitted through GPU-driven raster. No scene
|
|
1282
|
+
classification enables a second extraction or visibility implementation.
|
|
974
1283
|
|
|
975
1284
|
When `RhiCaps.storageBuffer` is available, the same projection owns persistent
|
|
976
1285
|
Primitive, Instance, Transform, DrawTemplate, and Material GPU tables. The
|
|
@@ -998,11 +1307,128 @@ reason. The nested `gpu` status is one of `inactive`, `unsupported`, `resident`,
|
|
|
998
1307
|
`rebuild-pending`, or `error`; resident state additionally reports capacity,
|
|
999
1308
|
upload ranges and bytes, grows, clears, rebuilds, and no-change frames. The
|
|
1000
1309
|
`gpuDriven` inspection reports whether stable frames materialized or validated
|
|
1001
|
-
GPU-owned rows and whether candidate or batch topology bytes were uploaded.
|
|
1310
|
+
GPU-owned rows and whether candidate or batch topology bytes were uploaded. Its
|
|
1311
|
+
lifetime `residencyValidationScans` and `residencyValidationCacheHits` counters
|
|
1312
|
+
make the CPU residency boundary auditable: an unchanged persistent frame should
|
|
1313
|
+
reuse the producer rows, while mesh, device, pipeline, or catalog generations
|
|
1314
|
+
force a new validation.
|
|
1002
1315
|
GPU and renderer contract errors arrive through the single `renderer.subscribe`
|
|
1003
1316
|
event stream (`event.kind === 'error'`); the renderer does not expose a second
|
|
1004
1317
|
error listener registry.
|
|
1005
1318
|
|
|
1319
|
+
## GPU-driven PBR / shadow / skin navigation
|
|
1320
|
+
|
|
1321
|
+
The shortest public declaration uses the same `Materials.standard` producer as
|
|
1322
|
+
the runtime and imported-skin carriers. `alphaCutoff` is the Alpha Mask
|
|
1323
|
+
contract; `castShadow` publishes the matching ShadowCaster pass.
|
|
1324
|
+
|
|
1325
|
+
```ts
|
|
1326
|
+
import { Materials } from '@forgeax/engine/render';
|
|
1327
|
+
|
|
1328
|
+
const material = Materials.standard({
|
|
1329
|
+
baseColor: [1, 1, 1, 1],
|
|
1330
|
+
alphaCutoff: 0.5,
|
|
1331
|
+
castShadow: true,
|
|
1332
|
+
});
|
|
1333
|
+
const materialHandle = world.allocSharedRef('MaterialAsset', material);
|
|
1334
|
+
void materialHandle;
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
| Candidate | Main GPU lane | Shadow GPU lane | Closed result |
|
|
1338
|
+
|:--|:--:|:--:|:--|
|
|
1339
|
+
| Standard opaque or Alpha Mask, finite resources | supported | supported | `gpu` |
|
|
1340
|
+
| Standard skin with producer-authored finite animated bounds | supported | supported | `gpu` |
|
|
1341
|
+
| Skin bounds missing or non-finite | refused | refused | `cpu-deformation` |
|
|
1342
|
+
| Capable resource/pipeline not ready | refused | refused | `blocked` (`resource-not-ready`) |
|
|
1343
|
+
| Blend, transmission, morph, custom, or unsupported topology | refused | refused | `cpu-semantic` |
|
|
1344
|
+
| Clustered local lights or enabled SSAO, including zero local lights | refused | refused | `cpu-semantic` (`unsupported`) |
|
|
1345
|
+
|
|
1346
|
+
The current scene-index shader does not consume the unified Cluster surface
|
|
1347
|
+
bindings. Admission checks active surface requirements each frame; disabling
|
|
1348
|
+
SSAO restores GPU eligibility when no other unsupported input remains. The
|
|
1349
|
+
existing complete CPU submission path still renders on the selected GPU.
|
|
1350
|
+
|
|
1351
|
+
### Static TextureAsset admission
|
|
1352
|
+
|
|
1353
|
+
Static PBR and Alpha Mask resources are admitted only from registered shared
|
|
1354
|
+
`TextureAsset` and `SamplerAsset` payloads. The resource-class identity is the
|
|
1355
|
+
sorted handle-pair projection used by the prepared material; it is not an
|
|
1356
|
+
array-position guess. Dynamic `RenderTargetTextureSource` and video sources
|
|
1357
|
+
remain their own CPU semantic boundary and are never coerced into a static
|
|
1358
|
+
GPU texture candidate.
|
|
1359
|
+
|
|
1360
|
+
The focused topology fixture allocates and resolves real static shared refs for
|
|
1361
|
+
resource classes `1`, `16`, and `256`, then builds `100,000` candidates for each
|
|
1362
|
+
class. It is structural admission evidence, not a physical-GPU benchmark:
|
|
1363
|
+
|
|
1364
|
+
```sh
|
|
1365
|
+
FORGEAX_SKIP_HARNESS_SYNC=1 pnpm exec vitest run --project=@forgeax/engine-render \
|
|
1366
|
+
packages/render/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts \
|
|
1367
|
+
--no-file-parallelism --reporter=dot
|
|
1368
|
+
```
|
|
1369
|
+
|
|
1370
|
+
### Point and Spot shadow authoring → view inspection
|
|
1371
|
+
|
|
1372
|
+
`PointLight` and `SpotLight` shadow views are public light facts, not a hidden
|
|
1373
|
+
GPU slot API. A Spot author declares a companion `Transform`, outgoing
|
|
1374
|
+
`direction`, cone angles in degrees, and the embedded shadow policy:
|
|
1375
|
+
|
|
1376
|
+
```ts
|
|
1377
|
+
import { SpotLight } from '@forgeax/engine-render';
|
|
1378
|
+
import { Transform } from '@forgeax/engine-scene';
|
|
1379
|
+
|
|
1380
|
+
world.spawn(
|
|
1381
|
+
{ component: Transform, data: { pos: [0, 5, 0] } },
|
|
1382
|
+
{
|
|
1383
|
+
component: SpotLight,
|
|
1384
|
+
data: {
|
|
1385
|
+
direction: [0, -1, 0],
|
|
1386
|
+
range: 20,
|
|
1387
|
+
innerConeDeg: 15,
|
|
1388
|
+
outerConeDeg: 35,
|
|
1389
|
+
castShadow: true,
|
|
1390
|
+
mapSize: 1024,
|
|
1391
|
+
pcfKernelSize: 3,
|
|
1392
|
+
},
|
|
1393
|
+
},
|
|
1394
|
+
);
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
| Author fact | Contract |
|
|
1398
|
+
|:--|:--|
|
|
1399
|
+
| `direction` / `Transform` | Outgoing direction plus the light position; both are required for a useful view |
|
|
1400
|
+
| `range` | Meters; default `10` |
|
|
1401
|
+
| `innerConeDeg` / `outerConeDeg` | Degrees; `0 ≤ inner < outer ≤ 90`, defaults `0` / `45` |
|
|
1402
|
+
| `castShadow` | Defaults `true`; set `false` for the explicit no-shadow case |
|
|
1403
|
+
| `mapSize`, `pcfKernelSize` | Shadow map resolution and odd PCF width; defaults `2048` / `3` |
|
|
1404
|
+
| `cookie` / `projector` | Optional GUID-backed static texture facts; dynamic target/video sources stay outside GPU admission |
|
|
1405
|
+
|
|
1406
|
+
After a successful `draw`, inspect the same renderer projection:
|
|
1407
|
+
|
|
1408
|
+
```ts
|
|
1409
|
+
const channels = renderer.inspect().renderScene.gpuDriven.channels;
|
|
1410
|
+
const spotViews = channels.filter((channel) => channel.viewPass === 'spot-shadow');
|
|
1411
|
+
const pointFaces = channels.filter((channel) => channel.viewPass === 'point-shadow');
|
|
1412
|
+
void spotViews; // lane, reason, drawCount, and viewIndex are bounded facts.
|
|
1413
|
+
void pointFaces; // point views additionally expose the cube `face`.
|
|
1414
|
+
```
|
|
1415
|
+
|
|
1416
|
+
The per-view cache is keyed by light identity and view parameters. A cache hit
|
|
1417
|
+
does not dispatch or record a duplicate shadow view. The inspection labels and
|
|
1418
|
+
identity helpers are anchored in
|
|
1419
|
+
[`spot-light.ts`](src/components/spot-light.ts),
|
|
1420
|
+
[`shadow-views.ts`](src/gpu-driven/shadow-views.ts), and
|
|
1421
|
+
[`frame.ts`](src/record/frame.ts); repair the producer named by a structured
|
|
1422
|
+
`failure.detail` before retrying the unchanged frame.
|
|
1423
|
+
|
|
1424
|
+
For offline recovery, read `renderer.inspect().renderScene.gpuDriven.channels`.
|
|
1425
|
+
A blocked channel carries one bounded `failure` projection with the same
|
|
1426
|
+
closed `code`, `detail.owner`, `detail.reason`, and `detail.recovery` that the
|
|
1427
|
+
renderer error stream emits. Repair that owner, recook or republish its
|
|
1428
|
+
producer artifact, and retry the identical frame; `hint` is explanatory text,
|
|
1429
|
+
not a value to parse. A blocked capable candidate is not permission to issue a
|
|
1430
|
+
duplicate CPU draw.
|
|
1431
|
+
|
|
1006
1432
|
## GPU-driven view kernel
|
|
1007
1433
|
|
|
1008
1434
|
`BatchTopology` groups eligible rigid draw items by immutable geometry,
|
|
@@ -1012,41 +1438,53 @@ non-indexed five-word indirect command. A per-view typed graph records
|
|
|
1012
1438
|
`reset -> frustum/compact -> finalize` over persistent candidate and batch
|
|
1013
1439
|
buffers. The compute path validates generation and active flags, composes the
|
|
1014
1440
|
primitive world transform with each ordinary instance-local transform, writes a
|
|
1015
|
-
compact `(
|
|
1016
|
-
and emits indirect arguments with `firstInstance = 0`.
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1441
|
+
compact `(meshProjectionRow, materialOrPaletteRow)` stream, sets explicit
|
|
1442
|
+
overflow flags, and emits indirect arguments with `firstInstance = 0`. The low
|
|
1443
|
+
bit of the candidate admission word controls submission; its upper bits carry
|
|
1444
|
+
the batch-local projected mesh row. Rigid rows address the generated material
|
|
1445
|
+
table, while skin rows address the persistent palette through
|
|
1446
|
+
`Instance.customDataStart`.
|
|
1447
|
+
|
|
1448
|
+
The Standard frame activates the production raster lane only when compute,
|
|
1449
|
+
storage buffers, indirect drawing, and producer-owned Standard PBR artifacts
|
|
1450
|
+
are all ready. It covers opaque and Alpha Mask rigid/skin candidates whose
|
|
1451
|
+
geometry, resource class, reflection, palette address, and (for skin) finite
|
|
1452
|
+
producer-authored bounds satisfy the prepared contract. Main and directional,
|
|
1453
|
+
spot, or point shadow views consume the same scene and topology, but keep
|
|
1454
|
+
view-local visibility and projection bindings. Ownership is per draw item and
|
|
1455
|
+
view pass, so one draw can be GPU-owned in the main/shadow channels while an
|
|
1456
|
+
unrelated semantic pass remains CPU-owned.
|
|
1457
|
+
|
|
1458
|
+
Transparent/blend, transmission, morph, custom or unsupported prepared
|
|
1459
|
+
variants, and skin without a producer-authored conservative bound stay on an
|
|
1460
|
+
explicit CPU semantic or CPU deformation lane. WebGL2 selects capability
|
|
1461
|
+
fallback from the same persistent projection; it does not emulate compute.
|
|
1462
|
+
Missing capable-path artifacts and failed resource publication are structured
|
|
1463
|
+
errors that block graph promotion rather than silently drawing the same item a
|
|
1464
|
+
second time through CPU semantics.
|
|
1465
|
+
|
|
1466
|
+
> [!WARNING]
|
|
1467
|
+
> A GPU selector overflow is fail-closed: `finalize` emits zero indirect
|
|
1468
|
+
> instances for the affected batch and inspection reports `overflow=true`.
|
|
1469
|
+
> The last-known-good resource generation remains authoritative until the
|
|
1470
|
+
> producer can rebuild with sufficient capacity. Missing skin bounds are also
|
|
1471
|
+
> intentionally conservative: they remain CPU deformation work; bind-pose
|
|
1472
|
+
> bounds are never invented as animation bounds.
|
|
1473
|
+
|
|
1474
|
+
The bounded `gpuDriven.channels` inspection reports the selected lane and
|
|
1475
|
+
closed reason for each view pass. Blocked promotion additionally exposes the
|
|
1476
|
+
producer-owned `failure` (`code`, `expected`, `hint`, and typed `detail`) so an
|
|
1477
|
+
AI caller can branch on owner/recovery without decoding an error string. The
|
|
1478
|
+
same `GpuDrivenPreparationError` is delivered through the renderer error
|
|
1479
|
+
stream. The inspection also reports topology revision, candidates,
|
|
1480
|
+
batches, visible capacity, buffer capacities, update count, upload bytes,
|
|
1481
|
+
rebuild count, overflow, retry count, and last-known-good generation. RhiNull
|
|
1482
|
+
verifies graph dependency order and stable-frame zero work; local Dawn verifies
|
|
1483
|
+
the command, binding, palette, shadow-view, and readback contracts. The
|
|
1484
|
+
renderer-level integration also proves that the built-in Standard graph
|
|
1485
|
+
contains the compute chain before `main`, with no GPU plus CPU duplicate draws.
|
|
1486
|
+
The renderer retains one graph, one compile owner, one encoder, and one submit
|
|
1487
|
+
route.
|
|
1050
1488
|
|
|
1051
1489
|
> [!NOTE]
|
|
1052
1490
|
> Non-rigid multi-World composition, skinned lanes, visibility/hierarchy
|
|
@@ -1056,6 +1494,28 @@ encoder, and one submit route.
|
|
|
1056
1494
|
> semantics; later coverage can make those changes entity-local without adding
|
|
1057
1495
|
> another scene authority.
|
|
1058
1496
|
|
|
1497
|
+
### Offline inspect → repair owner → retry
|
|
1498
|
+
|
|
1499
|
+
Use the detached channel to identify the next owner without opening a browser
|
|
1500
|
+
or reading live GPU objects:
|
|
1501
|
+
|
|
1502
|
+
```mermaid
|
|
1503
|
+
flowchart LR
|
|
1504
|
+
A["inspect gpuDriven.channels"] --> B{"lane / reason"}
|
|
1505
|
+
B -->|"gpu / none"| C["record the same graph"]
|
|
1506
|
+
B -->|"blocked / resource-not-ready"| D["repair failure.detail.owner"]
|
|
1507
|
+
D --> E["recook or publish the receipt"]
|
|
1508
|
+
E --> F["retry the identical frame request"]
|
|
1509
|
+
```
|
|
1510
|
+
|
|
1511
|
+
The three public navigation entries are [`shader`](../shader/README.md#gpu-driven-pbr--shadow--skin-navigation),
|
|
1512
|
+
[`runtime`](../runtime/README.md#gpu-driven-pbr--shadow--skin-navigation), and
|
|
1513
|
+
this render owner. The two executable carriers are
|
|
1514
|
+
[`hello-skin`](../../apps/hello/skin/README.md#gpu-driven-skin-carrier) and
|
|
1515
|
+
[`hello-fbx-skin`](../../apps/hello/fbx-skin/README.md#gpu-driven-fbx-carrier).
|
|
1516
|
+
Their Dawn smoke commands remain independent evidence; RhiNull proves graph
|
|
1517
|
+
ownership and deterministic lane facts only.
|
|
1518
|
+
|
|
1059
1519
|
Features are supplied through the construction options (`features: [...]`) and
|
|
1060
1520
|
enter the same host-owned extract → plan → graph projection path. The public
|
|
1061
1521
|
`Renderer` intentionally has no install/uninstall methods: optional capability
|
|
@@ -1163,6 +1623,34 @@ targets, and draw/dispatch commands. Graph buffer and texture access is derived
|
|
|
1163
1623
|
from those roles; producers never author a second `reads`/`writes` ledger and
|
|
1164
1624
|
never receive an encoder or submit authority.
|
|
1165
1625
|
|
|
1626
|
+
Prepared material-input layouts accept `particleInputLanes` from 1 through 4
|
|
1627
|
+
(omission selects the canonical one-lane layout). The count is valid only for a
|
|
1628
|
+
material-input vertex layout. Render derives its instance stride and attributes
|
|
1629
|
+
from the base layout and uses the same complete multi-stream descriptor in the
|
|
1630
|
+
PipelineSpec cache identity. Invalid counts fail during resource preparation.
|
|
1631
|
+
|
|
1632
|
+
A prepared pipeline with empty `colorFormats` uses the depth-only route and
|
|
1633
|
+
preserves its declared `depthFormat`, depth state and complete vertex streams.
|
|
1634
|
+
A vertex-only shader needs no fragment entry. For a vertex-index-generated draw,
|
|
1635
|
+
declare `vertexLayout: 'none'` on the draw and supply the prepared empty group
|
|
1636
|
+
when the shader has no resources. This is depth-target rendering, not automatic
|
|
1637
|
+
registration as a Standard light-view shadow caster.
|
|
1638
|
+
|
|
1639
|
+
For light-view participation, declare a `shadow-caster` plan pass with the same
|
|
1640
|
+
draw vocabulary and a depth32float, zero-color graphics program. Standard owns
|
|
1641
|
+
the light-view binding, atlas, viewport and pass replication; the feature owns
|
|
1642
|
+
only geometry and instance inputs. It reuses the normal prepared draw encoder
|
|
1643
|
+
and the same graph buffer imports. Active GPU casters disable static directional
|
|
1644
|
+
atlas reuse because ECS mesh epochs do not describe GPU buffer contents.
|
|
1645
|
+
|
|
1646
|
+
The graph projects a feature's leading compute prefix before shadow views until
|
|
1647
|
+
the first scene-target dependency or ordinary raster. Remaining work stays at
|
|
1648
|
+
the normal scene contribution boundary. A caster whose projection is late reads
|
|
1649
|
+
the prior submitted instance data (initially empty); it must not be described as
|
|
1650
|
+
current-frame simulation. Owner-local pipeline implementations use
|
|
1651
|
+
`contributeShadowFeatures` with `addTypedShadowPasses`; graph construction and
|
|
1652
|
+
these helpers are not added to the public Render root.
|
|
1653
|
+
|
|
1166
1654
|
### Five render terms
|
|
1167
1655
|
|
|
1168
1656
|
| Term | Meaning | Owner |
|
|
@@ -1355,6 +1843,236 @@ sibling-loop report or an authored manifest. Read identity, generation, evidence
|
|
|
1355
1843
|
level, and closed failure fields before following the named owner recovery action.
|
|
1356
1844
|
Submit the same consumer request and reinspect after recovery. The route returns
|
|
1357
1845
|
detached facts only: no live GPU handle, second owner, or second ledger is exposed.
|
|
1358
|
-
Missing or mismatched receipts remain
|
|
1359
|
-
|
|
1360
|
-
|
|
1846
|
+
Missing or mismatched receipts remain `fallback-only` with zero SSR work. The
|
|
1847
|
+
current renderer may expose admitted structural Hi-Z, trace, temporal, compose,
|
|
1848
|
+
and history facts, but no single backend snapshot or non-black canvas is an SSR
|
|
1849
|
+
v1 acceptance result; use paired Browser/Dawn readback and the closed-loop gates
|
|
1850
|
+
before promoting that status.
|
|
1851
|
+
|
|
1852
|
+
The canonical paired carrier is `apps/hello/ssr`. It runs the same fixture at
|
|
1853
|
+
`http://127.0.0.1:4173/?forgeax-evidence=ssr` and
|
|
1854
|
+
`dawn://hello/ssr?forgeax-evidence=ssr`, records 300-frame identity-bound
|
|
1855
|
+
readbacks, and keeps visual rows in the form `observed` / `verdict` /
|
|
1856
|
+
`confidence`. Its performance lane derives the 1920x1080 descriptor and checks
|
|
1857
|
+
it against `estimateSsrSpatialMemory`; timestamp or paired-lane absence remains
|
|
1858
|
+
blocked.
|
|
1859
|
+
|
|
1860
|
+
## Standard dynamic MeshAsset candidates
|
|
1861
|
+
|
|
1862
|
+
The Renderer owns one bounded candidate lifecycle for geometry produced by an
|
|
1863
|
+
external consumer. The payload is the existing `MeshAsset` contract, not a
|
|
1864
|
+
voxel-specific asset kind. The consumer remains the World shared-reference
|
|
1865
|
+
owner and publishes `MeshFilter` / `MeshRenderer` through the ordinary ECS
|
|
1866
|
+
write barrier; the Renderer owns GPU residency, device generation, frame
|
|
1867
|
+
receipt, and delayed retirement.
|
|
1868
|
+
|
|
1869
|
+
```ts
|
|
1870
|
+
import type { MeshAsset } from '@forgeax/engine-types';
|
|
1871
|
+
import { FixedTime, type World } from '@forgeax/engine-ecs';
|
|
1872
|
+
import { Transform } from '@forgeax/engine-scene';
|
|
1873
|
+
import { MeshFilter, MeshRenderer } from '@forgeax/engine-render';
|
|
1874
|
+
import type { FrameCamera, FrameEnvironment, Renderer } from '@forgeax/engine-render';
|
|
1875
|
+
|
|
1876
|
+
declare const renderer: Renderer;
|
|
1877
|
+
declare const world: World;
|
|
1878
|
+
declare const mesh: MeshAsset;
|
|
1879
|
+
declare const camera: FrameCamera;
|
|
1880
|
+
declare const environment: FrameEnvironment;
|
|
1881
|
+
|
|
1882
|
+
// Attach first: the Renderer owns the lease/generation boundary, while the
|
|
1883
|
+
// World remains the MeshAsset handle and ECS scene owner.
|
|
1884
|
+
const attached = renderer.attach(world);
|
|
1885
|
+
if (!attached.ok) throw attached.error;
|
|
1886
|
+
const meshHandle = world.allocSharedRef('MeshAsset', mesh);
|
|
1887
|
+
const entity = world
|
|
1888
|
+
.spawn(
|
|
1889
|
+
{ component: Transform, data: {} },
|
|
1890
|
+
{ component: MeshFilter, data: { assetHandle: meshHandle } },
|
|
1891
|
+
{ component: MeshRenderer, data: { materials: [] } },
|
|
1892
|
+
)
|
|
1893
|
+
.unwrap();
|
|
1894
|
+
const fixedStep = world.getResource(FixedTime).tick;
|
|
1895
|
+
const prepared = renderer.prepareDynamicGeometry({
|
|
1896
|
+
world,
|
|
1897
|
+
entity,
|
|
1898
|
+
mesh,
|
|
1899
|
+
meshHandle,
|
|
1900
|
+
// Must match a live material handle/source slot; this mesh uses its default.
|
|
1901
|
+
materialIdentity: 'default',
|
|
1902
|
+
revision: 4,
|
|
1903
|
+
topologyRevision: 2,
|
|
1904
|
+
// For paired physics use the admission example below, not this draw-only path.
|
|
1905
|
+
});
|
|
1906
|
+
if (!prepared.ok) {
|
|
1907
|
+
// Branch on prepared.error.code; the previous visible mesh is retained.
|
|
1908
|
+
} else {
|
|
1909
|
+
// ECS FixedTime is the ordering proof for this render-only revision.
|
|
1910
|
+
const accepted = renderer.acceptDynamicGeometry(prepared.value, {
|
|
1911
|
+
world,
|
|
1912
|
+
fixedStep,
|
|
1913
|
+
});
|
|
1914
|
+
if (accepted.ok) {
|
|
1915
|
+
const frame = renderer.draw({
|
|
1916
|
+
leases: [attached.value],
|
|
1917
|
+
camera,
|
|
1918
|
+
environment,
|
|
1919
|
+
fixedStep,
|
|
1920
|
+
});
|
|
1921
|
+
if (frame.ok) {
|
|
1922
|
+
const geometryReceipt = renderer.dynamicGeometryReceipt(accepted.value);
|
|
1923
|
+
// geometryReceipt.frame.frameId === frame.value.frameId for this
|
|
1924
|
+
// generation; geometryReceipt.frame.completed fences GPU retirement.
|
|
1925
|
+
void geometryReceipt;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
```
|
|
1930
|
+
|
|
1931
|
+
The entity must already carry `Transform`, a live `MeshFilter`, and a
|
|
1932
|
+
`MeshRenderer` when the candidate is prepared and accepted; these are the same
|
|
1933
|
+
ECS prerequisites used by the render extraction query. Preparation records the
|
|
1934
|
+
old `MeshFilter.assetHandle` and stages the new standard handle without changing
|
|
1935
|
+
the visible ECS binding. Acceptance is the one ECS write-barrier that swaps in
|
|
1936
|
+
the candidate. If acceptance or GPU preparation fails, the old binding remains;
|
|
1937
|
+
cancelling an accepted candidate swaps that old handle back, and refuses to
|
|
1938
|
+
overwrite a newer external binding. The Renderer host reads
|
|
1939
|
+
`World.FixedTime.tick` itself; a caller-supplied `{ world, fixedStep }` object
|
|
1940
|
+
cannot bind an unrelated World or step. Preparation may precede acceptance by
|
|
1941
|
+
multiple fixed steps; acceptance records the actual current tick. If
|
|
1942
|
+
`physicsEntity` is supplied, acceptance requires that entity's active paired
|
|
1943
|
+
`PhysicsWorld` admission to match the revision and current step. An already
|
|
1944
|
+
published physics result is deliberately insufficient: it cannot roll back if
|
|
1945
|
+
geometry now fails. A
|
|
1946
|
+
candidate that is not present in the ECS render snapshot at the successful draw
|
|
1947
|
+
is left accepted without a receipt; the receipt is issued only after the
|
|
1948
|
+
submitted record stage consumed the live binding.
|
|
1949
|
+
|
|
1950
|
+
### Paired physics admission
|
|
1951
|
+
|
|
1952
|
+
Prepare both domain candidates invisibly, with matching revision and the geometry
|
|
1953
|
+
candidate's `physicsEntity` naming the physical parent. Queue the physical candidate
|
|
1954
|
+
with one synchronous geometry commit. The existing Physics fixed-step owner runs it
|
|
1955
|
+
after native staging and before step/writeback/publication; no second clock or queue
|
|
1956
|
+
is created. A refused geometry commit restores the old native state. Physics queries,
|
|
1957
|
+
mutations and Renderer draw are refused during this borrowed admission interval.
|
|
1958
|
+
|
|
1959
|
+
```ts
|
|
1960
|
+
import { err, ok } from '@forgeax/engine-types';
|
|
1961
|
+
import { FixedTime } from '@forgeax/engine-ecs';
|
|
1962
|
+
|
|
1963
|
+
physics.admitDerivedShapeCandidate(physicsCandidate, () => {
|
|
1964
|
+
const admitted = renderer.acceptDynamicGeometry(geometryCandidate, {
|
|
1965
|
+
world,
|
|
1966
|
+
fixedStep: world.getResource(FixedTime).tick,
|
|
1967
|
+
});
|
|
1968
|
+
if (!admitted.ok) return err(admitted.error);
|
|
1969
|
+
acceptedGeometry = admitted.value;
|
|
1970
|
+
return ok(undefined);
|
|
1971
|
+
}).unwrap();
|
|
1972
|
+
// Normal World FixedUpdate performs admission, physics step and writeback.
|
|
1973
|
+
// After World.update succeeds, inspect Physics publication/failure and draw.
|
|
1974
|
+
```
|
|
1975
|
+
|
|
1976
|
+
The commit must return immediately after its complete binding change, without
|
|
1977
|
+
additional fallible work. On physical preparation/native failure it is not called;
|
|
1978
|
+
cancel the still-prepared geometry using its normal owner. On a refused commit,
|
|
1979
|
+
inspect `getDerivedFailure` and cancel that prepared geometry. Success publication
|
|
1980
|
+
remains after physics writeback and the real Renderer submission, not callback return.
|
|
1981
|
+
|
|
1982
|
+
`meshHandle` is mandatory on the public Renderer path. The pure
|
|
1983
|
+
`createDynamicGeometryLifecycle` helper may omit it for CPU-only validation, in
|
|
1984
|
+
which case `gpuReady` is false and no Renderer receipt can be produced. The
|
|
1985
|
+
attached Renderer uses the existing `GpuResidencyCache.ensureResident` path
|
|
1986
|
+
before acceptance; its failure is `dynamic-geometry-gpu-failed`, while a
|
|
1987
|
+
missing handle is the distinct `dynamic-geometry-gpu-not-ready` error. The
|
|
1988
|
+
handle is never minted by the Renderer and no RHI buffer or queue escapes the
|
|
1989
|
+
host. A supplied `materialIdentity` must match a live MeshRenderer slot or the
|
|
1990
|
+
MeshAsset material-slot source key; otherwise the host returns structured
|
|
1991
|
+
`dynamic-geometry-invalid`. The normal draw path consumes the accepted standard
|
|
1992
|
+
handle from `MeshFilter`, so main, depth, shadow, and motion/history share one
|
|
1993
|
+
committed geometry identity. The lifecycle also bounds aggregate typed-geometry
|
|
1994
|
+
bytes (`inspect().meshBytes` / `maxMeshBytes`) in addition to candidate count.
|
|
1995
|
+
|
|
1996
|
+
```mermaid
|
|
1997
|
+
stateDiagram-v2
|
|
1998
|
+
[*] --> prepared: prepare
|
|
1999
|
+
prepared --> accepted: accept
|
|
2000
|
+
prepared --> cancelled: cancel / generation loss
|
|
2001
|
+
accepted --> published: successful draw FrameReceipt
|
|
2002
|
+
published --> retired: receipt-safe retirement
|
|
2003
|
+
accepted --> cancelled: detach / World teardown
|
|
2004
|
+
```
|
|
2005
|
+
|
|
2006
|
+
`DynamicGeometryCandidate` is a short-lived credential bound to the attached
|
|
2007
|
+
World object, live ECS entity, lifecycle owner identity, candidate revision,
|
|
2008
|
+
copied MeshAsset, material identity, and active device generation. A Renderer
|
|
2009
|
+
acceptance records the attached World's `FixedTime` ordering and performs the
|
|
2010
|
+
binding swap; a candidate with `physicsEntity` also records that entity's
|
|
2011
|
+
PhysicsWorld admission step. A frame publishes only candidates for the
|
|
2012
|
+
drawn lease Worlds whose candidate step is no newer than the actual World
|
|
2013
|
+
`FixedTime.tick`, and whose PhysicsWorld publication has the exact candidate
|
|
2014
|
+
revision. The optional frame `fixedStep` is checked against that World tick; it
|
|
2015
|
+
is an assertion, not a second clock. Thus a multi-fixed-step host frame can
|
|
2016
|
+
issue one receipt for the latest submitted frame without dropping an accepted
|
|
2017
|
+
candidate, while a successful draw for another World cannot publish an
|
|
2018
|
+
arbitrary credential. The public attached-Renderer receipt carries
|
|
2019
|
+
`recordStageConsumed: true` and the embedded `frame.completed` fence, making it
|
|
2020
|
+
a real record/submit proof, not a non-empty handle or counter. The detached
|
|
2021
|
+
`createDynamicGeometryLifecycle` helper may intentionally publish a CPU-only
|
|
2022
|
+
receipt with `recordStageConsumed: false`; that helper is not a Renderer draw
|
|
2023
|
+
path and must not be used as GPU evidence.
|
|
2024
|
+
|
|
2025
|
+
`dynamicGeometryReceipt(candidate)` is present only after the accepted candidate
|
|
2026
|
+
is published by a successful `FrameReceipt`; it carries the same `frameId`,
|
|
2027
|
+
`deviceGeneration`, preparation fixed step, and actual publication fixed step.
|
|
2028
|
+
A topology revision invalidates lower-revision prepared/accepted candidates for the same entity and
|
|
2029
|
+
marks older published history invalid, but retains each published receipt's
|
|
2030
|
+
completion fence; an already-published credential remains until an explicit
|
|
2031
|
+
retire can release its GPU owner after that fence resolves. Each later
|
|
2032
|
+
record-stage consumption emits a refreshed receipt and adds its
|
|
2033
|
+
`FrameReceipt.frame.completed` fence, so retirement waits for every observed
|
|
2034
|
+
in-flight frame (resolved or rejected), not only the latest receipt. The
|
|
2035
|
+
attached host revalidates the live `(World, entity, MeshFilter)` binding for
|
|
2036
|
+
each candidate, so a topology change on entity B does not suppress a real
|
|
2037
|
+
record-stage use of still-live entity A; only the candidate/entity whose
|
|
2038
|
+
binding changed is refused.
|
|
2039
|
+
The shared `GpuResidencyCache` owns allocation leases and submission fences.
|
|
2040
|
+
A lease captures the concrete GPU allocation, not a reusable World handle slot;
|
|
2041
|
+
cancelling B cannot invalidate a resource still used by A. Every submitted
|
|
2042
|
+
frame conservatively fences all resident meshes, including ordinary, shadow-only
|
|
2043
|
+
and cached GPU-driven uses, independently of publication or history validity.
|
|
2044
|
+
In-place mesh invalidation immediately allows a new upload while old submitted
|
|
2045
|
+
buffers retire behind their own completion. An accepted topology change resets
|
|
2046
|
+
the existing temporal history owner before the next draw. The candidate's World
|
|
2047
|
+
reference is also retained through completion so its handle slot cannot be reused.
|
|
2048
|
+
The detached inspection reports both history invalidation and concrete
|
|
2049
|
+
candidate invalidation. Stale revisions, cross-World/lifecycle candidates, old
|
|
2050
|
+
generations, invalid GPU readiness, invalid ordering, duplicate acceptance,
|
|
2051
|
+
unknown retirement, and bounded in-flight count/byte overflow are structured
|
|
2052
|
+
`DynamicGeometryError` results. Cancelling or invalidating work leaves the last
|
|
2053
|
+
published candidate untouched, cleans candidate-only residency when no other
|
|
2054
|
+
ECS entity or candidate shares the handle, and never releases a caller-owned
|
|
2055
|
+
shared reference. Call `retireDynamicGeometry` only after the receipt-bound
|
|
2056
|
+
consumer no longer needs the old geometry, then release the World shared
|
|
2057
|
+
reference through its normal owner. Retirement is a logical state transition
|
|
2058
|
+
first: the attached host keeps the candidate count and typed mesh bytes in the
|
|
2059
|
+
bounded budget until all receipt fences resolve and the candidate's shared GPU
|
|
2060
|
+
lease is released. If the candidate is still the live `MeshFilter`, retirement
|
|
2061
|
+
returns `dynamic-geometry-invalid` without deleting its receipt or GPU owner;
|
|
2062
|
+
swap the binding through ECS first so the completion fence can safely retire it.
|
|
2063
|
+
The pure lifecycle exposes `finalizeRetirement` for its owner to release that
|
|
2064
|
+
deferred count/byte reservation after cleanup.
|
|
2065
|
+
|
|
2066
|
+
This seam intentionally does not add a second asset registry, Pack kind,
|
|
2067
|
+
RenderFeature, worker, or frame clock. Asset ownership is SSOT: the producer
|
|
2068
|
+
owns sourceKey/GUID and Cooked Pack payloads, Catalog is only the projection,
|
|
2069
|
+
the runtime `AssetRegistry` resolves GUIDs, and the World owns the shared
|
|
2070
|
+
`MeshAsset` handle. `world.allocSharedRef` is the only handle creation in this
|
|
2071
|
+
example; Renderer never registers or mints an asset. Provider/Catalog/GUID cold
|
|
2072
|
+
loading therefore continues through the existing route; a producer that needs
|
|
2073
|
+
a custom voxel artifact owns that schema and loader outside the Engine, while
|
|
2074
|
+
its projected standard mesh enters this candidate path.
|
|
2075
|
+
|
|
2076
|
+
### Targeted spatial inspection
|
|
2077
|
+
|
|
2078
|
+
`renderer.bounds(world, entity)` returns a detached world-space AABB from the last extracted composition, including the existing instance-bound projection. It returns `undefined` for an absent World/entity or unknown/invalid producer bounds. This is renderer geometry evidence, not collision geometry or a synchronous update of authored World changes. A subsequent draw refreshes the projection; callers must not substitute an origin for an unavailable mesh bound. The query reuses culling ownership and retains no additional bounds cache.
|