@forgeax/engine-render 0.1.3 → 0.1.6
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 +130 -641
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts +2 -0
- package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +1 -1
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +6 -0
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +1 -1
- package/dist/{gpu-driven/view-reference.d.ts → __tests__/gpu-driven-view-reference.d.ts} +4 -4
- package/dist/__tests__/gpu-driven-view-reference.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +1 -1
- package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts +2 -0
- package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts.map +1 -0
- package/dist/__tests__/morph-feature-plan.unit.test.d.ts +2 -0
- package/dist/__tests__/morph-feature-plan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-plan.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-plan.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-plan.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-plan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-config.unit.test.d.ts +2 -0
- package/dist/__tests__/ssao-config.unit.test.d.ts.map +1 -0
- package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts +2 -0
- package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts.map +1 -0
- package/dist/__tests__/standard-descriptors.test-d.d.ts +2 -0
- package/dist/__tests__/standard-descriptors.test-d.d.ts.map +1 -0
- package/dist/__tests__/standard-pipeline.dawn.test.d.ts +2 -0
- package/dist/__tests__/standard-pipeline.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/standard-pipeline.integration.test.d.ts +2 -0
- package/dist/__tests__/standard-pipeline.integration.test.d.ts.map +1 -0
- package/dist/__tests__/standard-profile-oracle.unit.test.d.ts +2 -0
- package/dist/__tests__/standard-profile-oracle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts.map +1 -0
- package/dist/{renderer/backend-selection.d.ts → assembly/backend-contract.d.ts} +15 -4
- package/dist/assembly/backend-contract.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +207 -0
- package/dist/assembly/factory.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +84 -0
- package/dist/assembly/host-contract.d.ts.map +1 -0
- package/dist/{renderer → assembly}/material/assembly.d.ts +1 -2
- package/dist/{renderer → assembly}/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-projection.d.ts +30 -0
- package/dist/assembly/material/pipeline-projection.d.ts.map +1 -0
- package/dist/authoring.d.ts +10 -11
- package/dist/authoring.d.ts.map +1 -1
- package/dist/authoring.mjs +25 -3
- package/dist/authoring.mjs.map +1 -1
- package/dist/chunk-CQXZC5XI.mjs +182 -0
- package/dist/chunk-CQXZC5XI.mjs.map +1 -0
- package/dist/chunk-QC4VW3XJ.mjs +79 -0
- package/dist/chunk-QC4VW3XJ.mjs.map +1 -0
- package/dist/chunk-SCRSAK5B.mjs +480 -0
- package/dist/chunk-SCRSAK5B.mjs.map +1 -0
- package/dist/components/__tests__/schema-contract.unit.test.d.ts +2 -0
- package/dist/components/__tests__/schema-contract.unit.test.d.ts.map +1 -0
- package/dist/components/glyph-text.d.ts +1 -1
- package/dist/components/glyph-text.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +1 -1
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +7 -7
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/scene-instance.d.ts +11 -11
- package/dist/components/skybox-background.d.ts +56 -0
- package/dist/components/skybox-background.d.ts.map +1 -0
- package/dist/components/skylight.d.ts +55 -0
- package/dist/components/skylight.d.ts.map +1 -0
- package/dist/components/sprite-animation.d.ts +2 -3
- package/dist/components/sprite-animation.d.ts.map +1 -1
- package/dist/components/sprite-instances.d.ts +1 -1
- package/dist/components/sprite-playback-mode.d.ts +6 -1
- package/dist/components/sprite-playback-mode.d.ts.map +1 -1
- package/dist/components/sprite-region-override.d.ts +2 -3
- package/dist/components/sprite-region-override.d.ts.map +1 -1
- package/dist/components/tile-layer.d.ts +5 -0
- package/dist/components/tile-layer.d.ts.map +1 -1
- package/dist/construct-renderer.d.ts +18 -1
- package/dist/construct-renderer.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +27008 -4
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/debug-draw-glue.d.ts +2 -53
- package/dist/debug-draw-glue.d.ts.map +1 -1
- package/dist/device/device-scope.d.ts +83 -0
- package/dist/device/device-scope.d.ts.map +1 -0
- package/dist/{gpu-resource-store.d.ts → device/gpu-residency.d.ts} +91 -48
- package/dist/device/gpu-residency.d.ts.map +1 -0
- package/dist/device/resource-types.d.ts +8 -0
- package/dist/device/resource-types.d.ts.map +1 -0
- package/dist/engine-metrics.d.ts +2 -2
- package/dist/engine-metrics.d.ts.map +1 -1
- package/dist/errors/recover.d.ts +28 -1
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +249 -258
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/features/fullscreen.d.ts +19 -0
- package/dist/features/fullscreen.d.ts.map +1 -0
- package/dist/features/host.d.ts +32 -26
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/morph/morph-feature.d.ts +39 -5
- package/dist/features/morph/morph-feature.d.ts.map +1 -1
- package/dist/features/plan.d.ts +182 -0
- package/dist/features/plan.d.ts.map +1 -0
- package/dist/features/prepared-gpu-work.d.ts +21 -17
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts +2 -1
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/features/prepared-graphics.d.ts +11 -21
- package/dist/features/prepared-graphics.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts +2 -2
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +4 -7
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +2 -2
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/render-graph-resources.d.ts +6 -6
- package/dist/features/render-graph-resources.d.ts.map +1 -1
- package/dist/features/targets.d.ts +5 -5
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +11 -190
- package/dist/features/types.d.ts.map +1 -1
- package/dist/features/vocabulary.d.ts +14 -0
- package/dist/features/vocabulary.d.ts.map +1 -0
- package/dist/fullscreen-post-process-pass.d.ts +4 -25
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/glyph-text-layout-system.d.ts +3 -2
- package/dist/glyph-text-layout-system.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +6 -75
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +13 -112
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +10 -66
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-resource.d.ts +3 -3
- package/dist/gpu-scene-schema.d.ts +1 -94
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +7 -33
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +3 -53
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +74 -180
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +1 -1
- package/dist/index.d.ts +13 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +165 -3
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +69 -0
- package/dist/inspection-types.d.ts.map +1 -0
- package/dist/instance-buffer-cache.d.ts +1 -4
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/internal.d.ts +1 -73
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +1 -43
- package/dist/internal.mjs.map +1 -1
- package/dist/lifecycle.d.ts +36 -2
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/materials.d.ts +0 -3
- package/dist/materials.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +0 -1
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +75 -29
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts +5 -0
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts +31 -0
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -0
- package/dist/pipeline/standard-profile.d.ts +40 -0
- package/dist/pipeline/standard-profile.d.ts.map +1 -0
- package/dist/pipeline-builder.d.ts +3 -3
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec-types.d.ts +24 -0
- package/dist/pipeline-spec-types.d.ts.map +1 -0
- package/dist/pipeline-spec.d.ts +16 -155
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts +1 -14
- package/dist/plugin.d.ts.map +1 -1
- package/dist/post-process-errors.d.ts +18 -18
- package/dist/post-process-errors.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +2 -5
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts +2 -0
- package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +7 -73
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +44 -44
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts +5 -38
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +64 -52
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +3 -2
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +5 -4
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +90 -59
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +28 -3
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +276 -3
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +235 -0
- package/dist/record/render-context.d.ts.map +1 -0
- package/dist/record/shadow-pass.d.ts +15 -88
- 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 +29 -7
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +3 -3
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/render-contract.d.ts +416 -0
- package/dist/render-contract.d.ts.map +1 -0
- package/dist/render-data.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +14 -362
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +25 -64
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +107 -338
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +21 -713
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +58 -0
- package/dist/scene/render-scene-types.d.ts.map +1 -0
- package/dist/scene/render-scene.d.ts +105 -0
- package/dist/scene/render-scene.d.ts.map +1 -0
- package/dist/scene-instances/post-spawn-resolve-joints.d.ts +2 -5
- package/dist/scene-instances/post-spawn-resolve-joints.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +22 -3
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/systems/skin-palette-allocator.d.ts +2 -4
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +14 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -0
- package/dist/systems/transparent-sort-config.d.ts +23 -2
- package/dist/systems/transparent-sort-config.d.ts.map +1 -1
- package/dist/tilemap-chunk-extract-system.d.ts +4 -3
- package/dist/tilemap-chunk-extract-system.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +2 -4
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +3 -3
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/__tests__/backend-selection-rhi-capture.test-d.ts +1 -1
- package/src/__tests__/callback-inference.test-d.ts +6 -3
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -4
- package/src/__tests__/construct-renderer-identity.unit.test.ts +15 -2
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +151 -0
- package/src/__tests__/device-scope-stale-matrix.unit.test.ts +42 -0
- package/src/__tests__/direct-extract-consumer-contract.test-d.ts +8 -19
- package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -13
- package/src/__tests__/factory-contract.integration.test.ts +158 -209
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +1 -4
- package/src/__tests__/frame-observation.contract.test.ts +17 -8
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +32 -0
- package/src/__tests__/glyph-text-layout-recovery.unit.test.ts +6 -11
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +9 -16
- package/src/__tests__/gpu-driven-production.integration.test.ts +32 -205
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +4 -7
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +87 -8
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +170 -8
- package/src/{gpu-driven/view-reference.ts → __tests__/gpu-driven-view-reference.ts} +4 -3
- package/src/__tests__/gpu-driven-view.browser.test.ts +12 -1
- package/src/__tests__/gpu-driven-view.dawn.test.ts +12 -1
- package/src/__tests__/gpu-driven-view.unit.test.ts +11 -267
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +3 -3
- package/src/__tests__/gpu-scene-render-graph-gpu.ts +2 -3
- package/src/__tests__/gpu-scene.dawn.test.ts +2 -6
- package/src/__tests__/gpu-scene.unit.test.ts +10 -24
- package/src/__tests__/ibl/face-uniforms.unit.test.ts +46 -0
- package/src/__tests__/infinite-grid-no-vertex-probe.unit.test.ts +34 -66
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -5
- package/src/__tests__/light-buffer-layout.unit.test.ts +4 -49
- package/src/__tests__/material-cooked-projection.unit.test.ts +2 -51
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +10 -15
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +4 -23
- package/src/__tests__/material-stride-surface.unit.test.ts +2 -2
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +7 -8
- package/src/__tests__/morph-culling-reentry.dawn.test.ts +29 -38
- package/src/__tests__/morph-feature-gpu-work.integration.test.ts +45 -43
- package/src/__tests__/morph-feature-plan.unit.test.ts +53 -0
- package/src/__tests__/morph-visual-evidence.integration.test.ts +47 -33
- package/src/__tests__/nineslice-handle-surface.unit.test.ts +11 -6
- package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -13
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +6 -81
- package/src/__tests__/prepared-graphics-generation.unit.test.ts +3 -17
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +6 -74
- package/src/__tests__/prepared-graphics-resolver.unit.test.ts +1 -3
- package/src/__tests__/prepared-graphics-snapshot.unit.test.ts +2 -2
- package/src/__tests__/profiler-default-off.test.ts +1 -1
- package/src/__tests__/profiler-phase-catalog.test.ts +2 -2
- package/src/__tests__/profiler-phase-integration.test.ts +5 -13
- package/src/__tests__/profiler-phase-token.test.ts +3 -8
- package/src/__tests__/public-surface.test-d.ts +65 -3
- package/src/__tests__/recover-error-policy-owner.unit.test.ts +1 -21
- package/src/__tests__/render-error-code-owner.test-d.ts +18 -23
- package/src/__tests__/render-error-detail-ssot.test-d.ts +0 -95
- package/src/__tests__/render-error-exhaustive.test-d.ts +8 -1
- package/src/__tests__/render-feature-assembly.test-d.ts +8 -16
- package/src/__tests__/render-feature-context.test-d.ts +16 -40
- package/src/__tests__/render-feature-contract.test-d.ts +9 -18
- package/src/__tests__/render-feature-diagnostics.unit.test.ts +6 -8
- package/src/__tests__/render-feature-docs.unit.test.ts +8 -7
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +14 -75
- package/src/__tests__/render-feature-plan.test-d.ts +161 -0
- package/src/__tests__/render-feature-plan.unit.test.ts +170 -0
- package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +8 -19
- package/src/__tests__/render-feature-prepared-errors.test-d.ts +3 -6
- package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +10 -71
- package/src/__tests__/render-feature-prepared-graphics-isolation.unit.test.ts +11 -61
- package/src/__tests__/render-feature-prepared-graphics-negative.test-d.ts +20 -43
- package/src/__tests__/render-feature-prepared-graphics.test-d.ts +56 -78
- package/src/__tests__/render-feature-public-surface.test-d.ts +6 -24
- package/src/__tests__/render-feature-retry.unit.test.ts +7 -48
- package/src/__tests__/render-feature-scheduling.unit.test.ts +15 -27
- package/src/__tests__/render-feature-stage-failed-policy-owner.unit.test.ts +0 -9
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +19 -109
- package/src/__tests__/render-feature-visibility-context.test-d.ts +1 -6
- package/src/__tests__/render-feature-visibility-context.unit.test.ts +1 -2
- package/src/__tests__/render-feature-zero-work.unit.test.ts +14 -20
- package/src/__tests__/render-scene-projection.unit.test.ts +73 -111
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +46 -50
- package/src/__tests__/renderer-lifecycle.integration.test.ts +49 -1
- package/src/__tests__/shader-stage-surface.unit.test.ts +14 -9
- package/src/__tests__/ssao-config.unit.test.ts +36 -0
- package/src/__tests__/ssao-kernel-surface.unit.test.ts +4 -5
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +28 -0
- package/src/__tests__/standard-descriptors.test-d.ts +22 -0
- package/src/__tests__/standard-pipeline.dawn.test.ts +61 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +124 -0
- package/src/__tests__/standard-profile-oracle.unit.test.ts +62 -0
- package/src/__tests__/surface-configuration.unit.test.ts +1 -2
- package/src/__tests__/texture-usage-surface.unit.test.ts +3 -6
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +40 -206
- package/src/__tests__/urp-pipeline-targets.unit.test.ts +1 -11
- package/src/__tests__/urp-pipeline.unit.test.ts +2 -2
- package/src/__tests__/vertex-color-pipeline.unit.test.ts +10 -2
- package/src/__tests__/vertex-layout-consumer.unit.test.ts +1 -1
- package/src/__tests__/visibility-diagnostics.integration.test.ts +7 -11
- package/src/__tests__/visibility-extract.integration.test.ts +20 -11
- package/src/__tests__/visibility-extract.perf.test.ts +66 -32
- package/src/__tests__/visibility-feature-dedup.integration.test.ts +2 -4
- package/src/__tests__/visibility-hidden-report.unit.test.ts +1 -2
- package/src/__tests__/visibility-instances.integration.test.ts +7 -11
- package/src/__tests__/visibility-multi-world.integration.test.ts +38 -6
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +18 -41
- package/src/__tests__/visibility-public-surface.test-d.ts +10 -9
- package/src/__tests__/visibility-resolver.unit.test.ts +2 -1
- package/src/__tests__/visibility-resource-short-circuit.unit.test.ts +80 -0
- package/src/__tests__/visibility-snapshot-contract.unit.test.ts +2 -1
- package/src/__tests__/vocabulary.unit.test.ts +1 -1
- package/src/assembly/backend-contract.ts +57 -0
- package/src/{renderer/renderer-factory.ts → assembly/factory.ts} +1709 -2693
- package/src/assembly/host-contract.ts +101 -0
- package/src/{renderer → assembly}/material/assembly.ts +1 -3
- package/src/assembly/material/pipeline-projection.ts +63 -0
- package/src/authoring.ts +14 -22
- package/src/components/__tests__/schema-contract.unit.test.ts +10 -0
- package/src/components/glyph-text.ts +3 -2
- package/src/components/index.ts +7 -9
- package/src/components/instances.ts +4 -5
- package/src/components/light-helpers.ts +1 -1
- package/src/components/mesh-filter.ts +11 -10
- package/src/components/post-process-params.ts +3 -3
- package/src/components/scene-instance.ts +12 -12
- package/src/components/skybox-background.ts +103 -0
- package/src/components/{environment.ts → skylight.ts} +2 -111
- package/src/components/sprite-animation.ts +3 -4
- package/src/components/sprite-instances.ts +2 -2
- package/src/components/sprite-playback-mode.ts +7 -1
- package/src/components/sprite-region-override.ts +2 -3
- package/src/components/tile-layer.ts +8 -2
- package/src/construct-renderer.ts +67 -4
- package/src/debug-draw-glue.ts +8 -129
- package/src/device/device-scope.ts +306 -0
- package/src/{gpu-resource-store.ts → device/gpu-residency.ts} +540 -639
- package/src/device/resource-types.ts +20 -0
- package/src/engine-metrics.ts +12 -19
- package/src/errors/recover.ts +45 -3
- package/src/errors/render.ts +357 -460
- package/src/features/fullscreen.ts +46 -0
- package/src/features/host.ts +575 -309
- package/src/features/morph/morph-feature.ts +195 -105
- package/src/features/plan.ts +447 -0
- package/src/features/prepared-gpu-work.ts +72 -15
- package/src/features/prepared-graphics-store.ts +20 -6
- package/src/features/prepared-graphics.ts +12 -71
- package/src/features/render-graph-compute.ts +14 -5
- package/src/features/render-graph-contribution.ts +43 -63
- package/src/features/render-graph-raster.ts +11 -6
- package/src/features/render-graph-resources.ts +25 -23
- package/src/features/targets.ts +8 -21
- package/src/features/types.ts +39 -227
- package/src/features/vocabulary.ts +24 -0
- package/src/fullscreen-post-process-pass.ts +14 -57
- package/src/glyph-text-layout-system.ts +70 -96
- package/src/gpu-driven/batch-topology.ts +24 -202
- package/src/gpu-driven/production-raster.ts +93 -1108
- package/src/gpu-driven/view-gpu.ts +46 -192
- package/src/gpu-resource.ts +4 -4
- package/src/gpu-scene-schema.ts +11 -99
- package/src/gpu-scene.ts +231 -364
- package/src/hdrp-buffers.ts +12 -221
- package/src/ibl/IblPipelineCache.ts +471 -711
- package/src/ibl/skylight-bind-group.ts +4 -4
- package/src/index.ts +58 -217
- package/src/inspection-types.ts +76 -0
- package/src/instance-buffer-cache.ts +1 -29
- package/src/internal.ts +6 -85
- package/src/lifecycle.ts +64 -19
- package/src/materials.ts +7 -29
- package/src/mesh-material-bindings.ts +0 -1
- package/src/pbr-pipeline.ts +434 -53
- package/src/{urp-pipeline.ts → pipeline/standard-forward-lane.ts} +38 -149
- package/src/{hdrp-pipeline.ts → pipeline/standard-pipeline.ts} +221 -191
- package/src/pipeline/standard-profile.ts +65 -0
- package/src/pipeline-builder.ts +60 -64
- package/src/pipeline-spec-types.ts +28 -0
- package/src/pipeline-spec.ts +72 -609
- package/src/plugin.ts +3 -75
- package/src/post-process-errors.ts +31 -33
- package/src/prepare/prepared-graphics-resolver.ts +5 -4
- package/src/record/__tests__/material-slot-plan.unit.test.ts +1 -33
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +41 -0
- package/src/record/__tests__/video-texture-view.unit.test.ts +1 -1
- package/src/record/frame-lighting.ts +74 -195
- package/src/record/frame-snapshot.ts +45 -60
- package/src/record/frame-targets.ts +8 -243
- package/src/record/frame.ts +509 -796
- package/src/record/helpers.ts +6 -11
- package/src/record/main-pass-geometry.ts +106 -147
- package/src/record/main-pass-material.ts +517 -190
- package/src/record/main-pass-sprite-draws.ts +198 -117
- package/src/record/main-pass.ts +162 -143
- package/src/record/mesh-ssbo.ts +517 -38
- package/src/record/render-context.ts +364 -0
- package/src/record/shadow-pass.ts +138 -1838
- package/src/record/skybox-post-pass.ts +12 -14
- package/src/record/typed-frame-graph.ts +215 -254
- package/src/record/view-ubo.ts +6 -37
- package/src/render-contract.ts +571 -0
- package/src/render-data.ts +3 -3
- package/src/render-graph-primitives.ts +76 -851
- package/src/render-pipeline.ts +104 -188
- package/src/render-system-extract.ts +1048 -1729
- package/src/render-system.ts +330 -1957
- package/src/scene/render-scene-types.ts +64 -0
- package/src/scene/render-scene.ts +1032 -0
- package/src/scene-instances/post-spawn-resolve-joints.ts +4 -7
- package/src/ssao-buffers.ts +167 -8
- package/src/systems/skin-palette-allocator.ts +21 -64
- package/src/systems/skin-palette-types.ts +14 -0
- package/src/systems/transparent-sort-config.ts +25 -3
- package/src/tilemap-chunk-extract-system.ts +53 -70
- package/src/typed-render-graph-primitives.ts +35 -24
- package/src/typed-shadow-passes.ts +26 -56
- package/dist/__tests__/analytic-sky-contract.test.d.ts +0 -2
- package/dist/__tests__/analytic-sky-contract.test.d.ts.map +0 -1
- package/dist/__tests__/analytic-sky-falsify.test.d.ts +0 -2
- package/dist/__tests__/analytic-sky-falsify.test.d.ts.map +0 -1
- package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts +0 -2
- package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts.map +0 -1
- package/dist/__tests__/backend-selection.integration.test.d.ts +0 -2
- package/dist/__tests__/backend-selection.integration.test.d.ts.map +0 -1
- package/dist/__tests__/camera-temporal.test-d.d.ts +0 -2
- package/dist/__tests__/camera-temporal.test-d.d.ts.map +0 -1
- package/dist/__tests__/camera-temporal.test.d.ts +0 -2
- package/dist/__tests__/camera-temporal.test.d.ts.map +0 -1
- package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts +0 -2
- package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts.map +0 -1
- package/dist/__tests__/environment-inspection.test.d.ts +0 -2
- package/dist/__tests__/environment-inspection.test.d.ts.map +0 -1
- package/dist/__tests__/fog-ai-user.fixture.d.ts +0 -2
- package/dist/__tests__/fog-ai-user.fixture.d.ts.map +0 -1
- package/dist/__tests__/fog-custom-render-feature.fixture.d.ts +0 -16
- package/dist/__tests__/fog-custom-render-feature.fixture.d.ts.map +0 -1
- package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-inspection.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-inspection.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-owner-state.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-owner-state.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-public-surface.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-public-surface.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-ray-contract.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-ray-contract.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-source-absence.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-source-absence.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-temporal.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-temporal.integration.test.d.ts.map +0 -1
- package/dist/__tests__/frame-chain.unit.test.d.ts +0 -2
- package/dist/__tests__/frame-chain.unit.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts +0 -2
- package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/hdrp-pipeline.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-pipeline.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts.map +0 -1
- package/dist/__tests__/material-derived-payload.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-payload.integration.test.d.ts.map +0 -1
- package/dist/__tests__/materials.unit.test.d.ts +0 -2
- package/dist/__tests__/materials.unit.test.d.ts.map +0 -1
- package/dist/__tests__/param-schema-projection-census.unit.test.d.ts +0 -2
- package/dist/__tests__/param-schema-projection-census.unit.test.d.ts.map +0 -1
- package/dist/__tests__/persistent-render-scene.unit.test.d.ts +0 -2
- package/dist/__tests__/persistent-render-scene.unit.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts +0 -2
- package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-pass-trace.test.d.ts +0 -2
- package/dist/__tests__/pipeline-pass-trace.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-stage-owner.test-d.d.ts +0 -2
- package/dist/__tests__/pipeline-stage-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/plugin.test.d.ts +0 -2
- package/dist/__tests__/plugin.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts.map +0 -1
- package/dist/__tests__/public-pipeline-id.test-d.d.ts +0 -2
- package/dist/__tests__/public-pipeline-id.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-dispatch-membership.unit.test.d.ts +0 -2
- package/dist/__tests__/render-dispatch-membership.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-construction.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-construction.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-graph.integration.test.d.ts +0 -2
- package/dist/__tests__/render-feature-graph.integration.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-order.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-order.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-staging.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-staging.unit.test.d.ts.map +0 -1
- package/dist/__tests__/renderer-render-scene-public.test-d.d.ts +0 -2
- package/dist/__tests__/renderer-render-scene-public.test-d.d.ts.map +0 -1
- package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts +0 -2
- package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts.map +0 -1
- package/dist/__tests__/sun-snapshot-consumers.test.d.ts +0 -2
- package/dist/__tests__/sun-snapshot-consumers.test.d.ts.map +0 -1
- package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts +0 -2
- package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/taa-history-store.unit.test.d.ts +0 -2
- package/dist/__tests__/taa-history-store.unit.test.d.ts.map +0 -1
- package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts +0 -2
- package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts.map +0 -1
- package/dist/__tests__/temporal-inspection.unit.test.d.ts +0 -2
- package/dist/__tests__/temporal-inspection.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/asset-decoders.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/asset-decoders.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/render-asset-port.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/render-asset-port.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts.map +0 -1
- package/dist/chunk-33UF5SAO.mjs +0 -7397
- package/dist/chunk-33UF5SAO.mjs.map +0 -1
- package/dist/chunk-A5QCJSNL.mjs +0 -29913
- package/dist/chunk-A5QCJSNL.mjs.map +0 -1
- package/dist/chunk-JMSB7AR4.mjs +0 -3
- package/dist/chunk-JMSB7AR4.mjs.map +0 -1
- package/dist/chunk-LXNGO6AO.mjs +0 -569
- package/dist/chunk-LXNGO6AO.mjs.map +0 -1
- package/dist/chunk-QRXPSX4X.mjs +0 -659
- package/dist/chunk-QRXPSX4X.mjs.map +0 -1
- package/dist/chunk-YAKEQPWO.mjs +0 -1439
- package/dist/chunk-YAKEQPWO.mjs.map +0 -1
- package/dist/components/__tests__/atmosphere-red.test.d.ts +0 -2
- package/dist/components/__tests__/atmosphere-red.test.d.ts.map +0 -1
- package/dist/components/__tests__/atmosphere.test.d.ts +0 -2
- package/dist/components/__tests__/atmosphere.test.d.ts.map +0 -1
- package/dist/components/__tests__/fog.test-d.d.ts +0 -2
- package/dist/components/__tests__/fog.test-d.d.ts.map +0 -1
- package/dist/components/__tests__/fog.test.d.ts +0 -2
- package/dist/components/__tests__/fog.test.d.ts.map +0 -1
- package/dist/components/atmosphere.d.ts +0 -18
- package/dist/components/atmosphere.d.ts.map +0 -1
- package/dist/components/environment.d.ts +0 -116
- package/dist/components/environment.d.ts.map +0 -1
- package/dist/deferred-lighting.d.ts +0 -13
- package/dist/deferred-lighting.d.ts.map +0 -1
- package/dist/dynamic-texture-store.d.ts +0 -42
- package/dist/dynamic-texture-store.d.ts.map +0 -1
- package/dist/environment/__tests__/failure-injection.test.d.ts +0 -2
- package/dist/environment/__tests__/failure-injection.test.d.ts.map +0 -1
- package/dist/environment/__tests__/generation-red.test.d.ts +0 -2
- package/dist/environment/__tests__/generation-red.test.d.ts.map +0 -1
- package/dist/environment/__tests__/generation.test.d.ts +0 -2
- package/dist/environment/__tests__/generation.test.d.ts.map +0 -1
- package/dist/environment/__tests__/graph-transaction.test.d.ts +0 -2
- package/dist/environment/__tests__/graph-transaction.test.d.ts.map +0 -1
- package/dist/environment/__tests__/inspection-red.test.d.ts +0 -2
- package/dist/environment/__tests__/inspection-red.test.d.ts.map +0 -1
- package/dist/environment/__tests__/lifecycle-evidence.test.d.ts +0 -2
- package/dist/environment/__tests__/lifecycle-evidence.test.d.ts.map +0 -1
- package/dist/environment/__tests__/pipeline-topology.test.d.ts +0 -2
- package/dist/environment/__tests__/pipeline-topology.test.d.ts.map +0 -1
- package/dist/environment/__tests__/readiness.test.d.ts +0 -2
- package/dist/environment/__tests__/readiness.test.d.ts.map +0 -1
- package/dist/environment/__tests__/recovery.test.d.ts +0 -2
- package/dist/environment/__tests__/recovery.test.d.ts.map +0 -1
- package/dist/environment/__tests__/resize-dispose.test.d.ts +0 -2
- package/dist/environment/__tests__/resize-dispose.test.d.ts.map +0 -1
- package/dist/environment/__tests__/signature-red.test.d.ts +0 -2
- package/dist/environment/__tests__/signature-red.test.d.ts.map +0 -1
- package/dist/environment/__tests__/signature.test.d.ts +0 -2
- package/dist/environment/__tests__/signature.test.d.ts.map +0 -1
- package/dist/environment/descriptors.d.ts +0 -2
- package/dist/environment/descriptors.d.ts.map +0 -1
- package/dist/environment/extracted-environment.d.ts +0 -53
- package/dist/environment/extracted-environment.d.ts.map +0 -1
- package/dist/environment/generation.d.ts +0 -62
- package/dist/environment/generation.d.ts.map +0 -1
- package/dist/environment/gpu-bundle.d.ts +0 -23
- package/dist/environment/gpu-bundle.d.ts.map +0 -1
- package/dist/environment/graph.d.ts +0 -108
- package/dist/environment/graph.d.ts.map +0 -1
- package/dist/environment/inspection.d.ts +0 -48
- package/dist/environment/inspection.d.ts.map +0 -1
- package/dist/environment/lifecycle.d.ts +0 -22
- package/dist/environment/lifecycle.d.ts.map +0 -1
- package/dist/environment/readiness.d.ts +0 -29
- package/dist/environment/readiness.d.ts.map +0 -1
- package/dist/environment/signature.d.ts +0 -70
- package/dist/environment/signature.d.ts.map +0 -1
- package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts +0 -2
- package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts.map +0 -1
- package/dist/extract/__tests__/environment-source.test.d.ts +0 -2
- package/dist/extract/__tests__/environment-source.test.d.ts.map +0 -1
- package/dist/extract/__tests__/schema-readers.unit.test.d.ts +0 -2
- package/dist/extract/__tests__/schema-readers.unit.test.d.ts.map +0 -1
- package/dist/extract/directional-csm.d.ts +0 -129
- package/dist/extract/directional-csm.d.ts.map +0 -1
- package/dist/extract/index.d.ts +0 -5
- package/dist/extract/index.d.ts.map +0 -1
- package/dist/extract/material-interface.d.ts +0 -56
- package/dist/extract/material-interface.d.ts.map +0 -1
- package/dist/extract/schema-readers.d.ts +0 -20
- package/dist/extract/schema-readers.d.ts.map +0 -1
- package/dist/features/graph-contribution.d.ts +0 -64
- package/dist/features/graph-contribution.d.ts.map +0 -1
- package/dist/features/index.d.ts +0 -21
- package/dist/features/index.d.ts.map +0 -1
- package/dist/features/morph/index.d.ts +0 -3
- package/dist/features/morph/index.d.ts.map +0 -1
- package/dist/features/morph/morph-bounds.d.ts +0 -42
- package/dist/features/morph/morph-bounds.d.ts.map +0 -1
- package/dist/features/scene-temporal.d.ts +0 -45
- package/dist/features/scene-temporal.d.ts.map +0 -1
- package/dist/features/taa-resolve.d.ts +0 -21
- package/dist/features/taa-resolve.d.ts.map +0 -1
- package/dist/frame-chain.d.ts +0 -14
- package/dist/frame-chain.d.ts.map +0 -1
- package/dist/gpu-driven/view-reference.d.ts.map +0 -1
- package/dist/gpu-resource-store.d.ts.map +0 -1
- package/dist/hdrp-cluster-membership.d.ts +0 -2
- package/dist/hdrp-cluster-membership.d.ts.map +0 -1
- package/dist/hdrp-pipeline.d.ts +0 -36
- package/dist/hdrp-pipeline.d.ts.map +0 -1
- package/dist/ibl/cubemap-projection.d.ts +0 -23
- package/dist/ibl/cubemap-projection.d.ts.map +0 -1
- package/dist/ibl/face-uniforms.d.ts +0 -65
- package/dist/ibl/face-uniforms.d.ts.map +0 -1
- package/dist/mesh-update-errors.d.ts +0 -71
- package/dist/mesh-update-errors.d.ts.map +0 -1
- package/dist/mipmap-generator.d.ts +0 -39
- package/dist/mipmap-generator.d.ts.map +0 -1
- package/dist/param-schema-projection-census.d.ts +0 -18
- package/dist/param-schema-projection-census.d.ts.map +0 -1
- package/dist/persistent-render-scene.d.ts +0 -158
- package/dist/persistent-render-scene.d.ts.map +0 -1
- package/dist/pipeline/index.d.ts +0 -11
- package/dist/pipeline/index.d.ts.map +0 -1
- package/dist/pipeline-errors.d.ts +0 -110
- package/dist/pipeline-errors.d.ts.map +0 -1
- package/dist/prepare/__tests__/schema-readers.unit.test.d.ts +0 -2
- package/dist/prepare/__tests__/schema-readers.unit.test.d.ts.map +0 -1
- package/dist/prepare/index.d.ts +0 -4
- package/dist/prepare/index.d.ts.map +0 -1
- package/dist/prepare/schema-readers.d.ts +0 -11
- package/dist/prepare/schema-readers.d.ts.map +0 -1
- package/dist/record/__tests__/environment-record.test.d.ts +0 -2
- package/dist/record/__tests__/environment-record.test.d.ts.map +0 -1
- package/dist/record/__tests__/frame-targets.unit.test.d.ts +0 -2
- package/dist/record/__tests__/frame-targets.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/membership-timing.unit.test.d.ts +0 -2
- package/dist/record/__tests__/membership-timing.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/render-submission.integration.test.d.ts +0 -2
- package/dist/record/__tests__/render-submission.integration.test.d.ts.map +0 -1
- package/dist/record/__tests__/render-submission.perf.test.d.ts +0 -2
- package/dist/record/__tests__/render-submission.perf.test.d.ts.map +0 -1
- package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts +0 -2
- package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts +0 -2
- package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts.map +0 -1
- package/dist/record/frame-observation.d.ts +0 -38
- package/dist/record/frame-observation.d.ts.map +0 -1
- package/dist/record/index.d.ts +0 -15
- package/dist/record/index.d.ts.map +0 -1
- package/dist/record/main-pass-skin.d.ts +0 -46
- package/dist/record/main-pass-skin.d.ts.map +0 -1
- package/dist/record/main-pass-sprite.d.ts +0 -69
- package/dist/record/main-pass-sprite.d.ts.map +0 -1
- package/dist/record/material-payload.d.ts +0 -43
- package/dist/record/material-payload.d.ts.map +0 -1
- package/dist/record/material-slot-plan.d.ts +0 -26
- package/dist/record/material-slot-plan.d.ts.map +0 -1
- package/dist/record/membership-timing.d.ts +0 -232
- package/dist/record/membership-timing.d.ts.map +0 -1
- package/dist/record/shadow-caster-uniform.d.ts +0 -8
- package/dist/record/shadow-caster-uniform.d.ts.map +0 -1
- package/dist/record/shadow-submit-mode.d.ts +0 -66
- package/dist/record/shadow-submit-mode.d.ts.map +0 -1
- package/dist/render-dispatch-membership.d.ts +0 -47
- package/dist/render-dispatch-membership.d.ts.map +0 -1
- package/dist/render-pipeline-context.d.ts +0 -289
- package/dist/render-pipeline-context.d.ts.map +0 -1
- package/dist/render-pipeline-target.d.ts +0 -16
- package/dist/render-pipeline-target.d.ts.map +0 -1
- package/dist/render-scene-projection.d.ts +0 -174
- package/dist/render-scene-projection.d.ts.map +0 -1
- package/dist/render-system-fold.d.ts +0 -266
- package/dist/render-system-fold.d.ts.map +0 -1
- package/dist/renderer/asset-service.d.ts +0 -10
- package/dist/renderer/asset-service.d.ts.map +0 -1
- package/dist/renderer/backend-selection.d.ts.map +0 -1
- package/dist/renderer/environment-classify.d.ts +0 -6
- package/dist/renderer/environment-classify.d.ts.map +0 -1
- package/dist/renderer/environment-error.d.ts +0 -13
- package/dist/renderer/environment-error.d.ts.map +0 -1
- package/dist/renderer/material/pipeline-projection.d.ts +0 -91
- package/dist/renderer/material/pipeline-projection.d.ts.map +0 -1
- package/dist/renderer/renderer-factory.d.ts +0 -283
- package/dist/renderer/renderer-factory.d.ts.map +0 -1
- package/dist/renderer.d.ts +0 -771
- package/dist/renderer.d.ts.map +0 -1
- package/dist/scene-utils/collect-subtree.d.ts +0 -11
- package/dist/scene-utils/collect-subtree.d.ts.map +0 -1
- package/dist/ssao-data.d.ts +0 -34
- package/dist/ssao-data.d.ts.map +0 -1
- package/dist/surface-format.d.ts +0 -10
- package/dist/surface-format.d.ts.map +0 -1
- package/dist/taa-history-store.d.ts +0 -88
- package/dist/taa-history-store.d.ts.map +0 -1
- package/dist/temporal-coverage.d.ts +0 -35
- package/dist/temporal-coverage.d.ts.map +0 -1
- package/dist/temporal-frame-transaction.d.ts +0 -92
- package/dist/temporal-frame-transaction.d.ts.map +0 -1
- package/dist/temporal-inspection.d.ts +0 -57
- package/dist/temporal-inspection.d.ts.map +0 -1
- package/dist/temporal-view.d.ts +0 -43
- package/dist/temporal-view.d.ts.map +0 -1
- package/dist/tilemap/resolve-tileset-runtime.d.ts +0 -23
- package/dist/tilemap/resolve-tileset-runtime.d.ts.map +0 -1
- package/dist/urp-pipeline.d.ts +0 -4
- package/dist/urp-pipeline.d.ts.map +0 -1
- package/src/__tests__/analytic-sky-contract.test.ts +0 -27
- package/src/__tests__/analytic-sky-falsify-carriers.mjs +0 -43
- package/src/__tests__/analytic-sky-falsify.test.ts +0 -94
- package/src/__tests__/assets-runtime-handoff.integration.test.ts +0 -36
- package/src/__tests__/backend-selection.integration.test.ts +0 -11
- package/src/__tests__/camera-temporal.test-d.ts +0 -8
- package/src/__tests__/camera-temporal.test.ts +0 -188
- package/src/__tests__/directional-shadow-probe-atlas-layout.unit.test.ts +0 -18
- package/src/__tests__/environment-inspection.test.ts +0 -45
- package/src/__tests__/fog-ai-user.fixture.ts +0 -350
- package/src/__tests__/fog-custom-render-feature.fixture.ts +0 -33
- package/src/__tests__/fog-custom-render-feature.integration.test.ts +0 -76
- package/src/__tests__/fog-inspection.integration.test.ts +0 -127
- package/src/__tests__/fog-owner-state.integration.test.ts +0 -112
- package/src/__tests__/fog-pipeline-topology.integration.test.ts +0 -93
- package/src/__tests__/fog-public-surface.integration.test.ts +0 -60
- package/src/__tests__/fog-ray-contract.integration.test.ts +0 -73
- package/src/__tests__/fog-source-absence.integration.test.ts +0 -51
- package/src/__tests__/fog-temporal.integration.test.ts +0 -115
- package/src/__tests__/frame-chain.unit.test.ts +0 -26
- package/src/__tests__/gpu-scene-field-type-owner.test-d.ts +0 -27
- package/src/__tests__/hdrp-pipeline.unit.test.ts +0 -100
- package/src/__tests__/ibl/ibl-fallback.unit.test.ts +0 -23
- package/src/__tests__/material-derived-error-stop-draw.integration.test.ts +0 -69
- package/src/__tests__/material-derived-payload.integration.test.ts +0 -164
- package/src/__tests__/materials.unit.test.ts +0 -25
- package/src/__tests__/param-schema-projection-census.unit.test.ts +0 -40
- package/src/__tests__/persistent-render-scene.unit.test.ts +0 -1311
- package/src/__tests__/pipeline-error-policy-owner.unit.test.ts +0 -167
- package/src/__tests__/pipeline-pass-trace.test.ts +0 -92
- package/src/__tests__/pipeline-stage-owner.test-d.ts +0 -13
- package/src/__tests__/plugin.test.ts +0 -25
- package/src/__tests__/prepared-graphics-backend-matrix.unit.test.ts +0 -129
- package/src/__tests__/prepared-graphics-resolver-recording.unit.test.ts +0 -180
- package/src/__tests__/public-pipeline-id.test-d.ts +0 -6
- package/src/__tests__/render-dispatch-membership.unit.test.ts +0 -117
- package/src/__tests__/render-feature-construction.unit.test.ts +0 -124
- package/src/__tests__/render-feature-error-code-owner.test-d.ts +0 -27
- package/src/__tests__/render-feature-gpu-buffer-usage-owner.test-d.ts +0 -46
- package/src/__tests__/render-feature-graph.integration.test.ts +0 -205
- package/src/__tests__/render-feature-graphics-staging.unit.test.ts +0 -200
- package/src/__tests__/render-feature-order.unit.test.ts +0 -66
- package/src/__tests__/render-feature-prepared-graphics-recording.unit.test.ts +0 -107
- package/src/__tests__/render-feature-staging.unit.test.ts +0 -94
- package/src/__tests__/renderer-render-scene-public.test-d.ts +0 -27
- package/src/__tests__/scene-temporal-coverage.unit.test.ts +0 -259
- package/src/__tests__/sun-snapshot-consumers.test.ts +0 -35
- package/src/__tests__/surface-backend-kind-owner.test-d.ts +0 -42
- package/src/__tests__/taa-history-store.unit.test.ts +0 -196
- package/src/__tests__/temporal-frame-transaction.integration.test.ts +0 -537
- package/src/__tests__/temporal-inspection.unit.test.ts +0 -138
- package/src/assets/__tests__/asset-decoders.unit.test.ts +0 -92
- package/src/assets/__tests__/mipmap-generator.unit.test.ts +0 -52
- package/src/assets/__tests__/render-asset-port.unit.test.ts +0 -54
- package/src/assets/__tests__/resolve-render-asset.unit.test.ts +0 -47
- package/src/components/__tests__/atmosphere-red.test.ts +0 -51
- package/src/components/__tests__/atmosphere.test.ts +0 -79
- package/src/components/__tests__/fog.test-d.ts +0 -49
- package/src/components/__tests__/fog.test.ts +0 -60
- package/src/components/atmosphere.ts +0 -64
- package/src/deferred-lighting.ts +0 -92
- package/src/dynamic-texture-store.ts +0 -111
- package/src/environment/README.md +0 -76
- package/src/environment/__tests__/failure-injection.test.ts +0 -50
- package/src/environment/__tests__/generation-red.test.ts +0 -64
- package/src/environment/__tests__/generation.test.ts +0 -37
- package/src/environment/__tests__/graph-transaction.test.ts +0 -85
- package/src/environment/__tests__/inspection-red.test.ts +0 -53
- package/src/environment/__tests__/lifecycle-evidence.test.ts +0 -40
- package/src/environment/__tests__/pipeline-topology.test.ts +0 -33
- package/src/environment/__tests__/readiness.test.ts +0 -42
- package/src/environment/__tests__/recovery.test.ts +0 -62
- package/src/environment/__tests__/resize-dispose.test.ts +0 -30
- package/src/environment/__tests__/signature-red.test.ts +0 -45
- package/src/environment/__tests__/signature.test.ts +0 -60
- package/src/environment/descriptors.ts +0 -7
- package/src/environment/extracted-environment.ts +0 -239
- package/src/environment/generation.ts +0 -158
- package/src/environment/gpu-bundle.ts +0 -144
- package/src/environment/graph.ts +0 -588
- package/src/environment/inspection.ts +0 -107
- package/src/environment/lifecycle.ts +0 -124
- package/src/environment/readiness.ts +0 -76
- package/src/environment/signature.ts +0 -185
- package/src/extract/__tests__/directional-csm-domain.unit.test.ts +0 -391
- package/src/extract/__tests__/environment-source.test.ts +0 -105
- package/src/extract/__tests__/schema-readers.unit.test.ts +0 -16
- package/src/extract/directional-csm.ts +0 -959
- package/src/extract/index.ts +0 -10
- package/src/extract/material-interface.ts +0 -604
- package/src/extract/schema-readers.ts +0 -27
- package/src/features/graph-contribution.ts +0 -577
- package/src/features/index.ts +0 -100
- package/src/features/morph/index.ts +0 -24
- package/src/features/morph/morph-bounds.ts +0 -123
- package/src/features/scene-temporal.ts +0 -274
- package/src/features/taa-resolve.ts +0 -244
- package/src/frame-chain.ts +0 -21
- package/src/hdrp-cluster-membership.ts +0 -70
- package/src/ibl/cubemap-projection.ts +0 -60
- package/src/ibl/face-uniforms.ts +0 -150
- package/src/mesh-update-errors.ts +0 -113
- package/src/mipmap-generator.ts +0 -244
- package/src/param-schema-projection-census.ts +0 -73
- package/src/persistent-render-scene.ts +0 -1752
- package/src/pipeline/index.ts +0 -40
- package/src/pipeline-errors.ts +0 -172
- package/src/prepare/__tests__/schema-readers.unit.test.ts +0 -16
- package/src/prepare/index.ts +0 -8
- package/src/prepare/schema-readers.ts +0 -20
- package/src/record/__tests__/environment-record.test.ts +0 -35
- package/src/record/__tests__/frame-targets.unit.test.ts +0 -54
- package/src/record/__tests__/membership-timing.unit.test.ts +0 -524
- package/src/record/__tests__/render-submission.integration.test.ts +0 -76
- package/src/record/__tests__/render-submission.perf.test.ts +0 -141
- package/src/record/__tests__/shadow-submit-mode.unit.test.ts +0 -88
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +0 -101
- package/src/record/frame-observation.ts +0 -122
- package/src/record/index.ts +0 -15
- package/src/record/main-pass-skin.ts +0 -51
- package/src/record/main-pass-sprite.ts +0 -166
- package/src/record/material-payload.ts +0 -292
- package/src/record/material-slot-plan.ts +0 -68
- package/src/record/membership-timing.ts +0 -1012
- package/src/record/shadow-caster-uniform.ts +0 -37
- package/src/record/shadow-submit-mode.ts +0 -277
- package/src/render-dispatch-membership.ts +0 -155
- package/src/render-pipeline-context.ts +0 -332
- package/src/render-pipeline-target.ts +0 -96
- package/src/render-scene-projection.ts +0 -1738
- package/src/render-system-fold.ts +0 -531
- package/src/renderer/asset-service.ts +0 -32
- package/src/renderer/backend-selection.ts +0 -121
- package/src/renderer/environment-classify.ts +0 -30
- package/src/renderer/environment-error.ts +0 -19
- package/src/renderer/material/pipeline-projection.ts +0 -140
- package/src/renderer.ts +0 -940
- package/src/scene-utils/collect-subtree.ts +0 -40
- package/src/ssao-data.ts +0 -106
- package/src/surface-format.ts +0 -37
- package/src/taa-history-store.ts +0 -429
- package/src/temporal-coverage.ts +0 -106
- package/src/temporal-frame-transaction.ts +0 -301
- package/src/temporal-inspection.ts +0 -194
- package/src/temporal-view.ts +0 -174
- package/src/tilemap/resolve-tileset-runtime.ts +0 -72
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// @forgeax/engine-runtime -
|
|
2
|
-
// (feat-20260601-gpu-
|
|
1
|
+
// @forgeax/engine-runtime - GpuResidencyCache
|
|
2
|
+
// (feat-20260601-device/gpu-residency-extraction M1; M2 added the deriveRenderData
|
|
3
3
|
// projection seam — see render-data.ts).
|
|
4
4
|
//
|
|
5
5
|
// Owns the GPU residency layer extracted from AssetRegistry: per-handle GPU
|
|
@@ -23,30 +23,42 @@
|
|
|
23
23
|
//
|
|
24
24
|
// Texture residency is SYNCHRONOUS (D-9): `uploadTexture`'s only async source
|
|
25
25
|
// was the one-time per-device mipmap shader-module build, which is hoisted to
|
|
26
|
-
// `prewarmMipmapPipeline` (called from `createRenderer.
|
|
26
|
+
// `prewarmMipmapPipeline` (called from `createRenderer.initialization`, already async).
|
|
27
27
|
// After prewarm the per-texture mipmap blit is pure synchronous encoder work,
|
|
28
28
|
// so the sync `draw(world)` frame contract is preserved. A format that was
|
|
29
29
|
// not prewarmed surfaces a structured RhiError on the sync path -- it is never
|
|
30
30
|
// lazily awaited (that would break the sync draw contract).
|
|
31
31
|
|
|
32
|
+
import {
|
|
33
|
+
blitMipmapsSync,
|
|
34
|
+
getOrCreateMipmapPipeline,
|
|
35
|
+
type MipmapShaderModuleFactory,
|
|
36
|
+
} from '@forgeax/engine-assets-runtime';
|
|
32
37
|
import type { World } from '@forgeax/engine-ecs';
|
|
33
|
-
import
|
|
38
|
+
import {
|
|
39
|
+
PROCEDURAL_FLOATS_PER_VERTEX,
|
|
40
|
+
type VertexLayoutProjection,
|
|
41
|
+
} from '@forgeax/engine-geometry';
|
|
34
42
|
import { halfFloat } from '@forgeax/engine-math';
|
|
35
43
|
import {
|
|
36
44
|
type Buffer,
|
|
45
|
+
type BufferDescriptor,
|
|
37
46
|
err,
|
|
38
47
|
ok,
|
|
39
48
|
type Result,
|
|
40
49
|
type RhiCaps,
|
|
41
50
|
type RhiDevice,
|
|
42
51
|
RhiError,
|
|
52
|
+
type RhiQueue,
|
|
43
53
|
type Sampler,
|
|
44
54
|
type Texture,
|
|
55
|
+
type TextureFormat,
|
|
45
56
|
type TextureView,
|
|
46
57
|
} from '@forgeax/engine-rhi';
|
|
47
58
|
import {
|
|
48
59
|
ASSET_ERROR_HINTS,
|
|
49
60
|
type AssetError,
|
|
61
|
+
countExtraUvSets,
|
|
50
62
|
type DecodedImage,
|
|
51
63
|
type EquirectAsset,
|
|
52
64
|
type Handle,
|
|
@@ -61,37 +73,29 @@ import {
|
|
|
61
73
|
type Submesh,
|
|
62
74
|
type TextureAsset,
|
|
63
75
|
type MeshAsset as TypesMeshAsset,
|
|
76
|
+
toShared,
|
|
64
77
|
} from '@forgeax/engine-types';
|
|
65
|
-
import { GpuBuffer, GpuTexture } from '
|
|
66
|
-
import {
|
|
78
|
+
import { GpuBuffer, GpuTexture } from '../gpu-resource';
|
|
79
|
+
import {
|
|
80
|
+
GPU_TEXTURE_USAGE_COPY_DST,
|
|
81
|
+
GPU_TEXTURE_USAGE_TEXTURE_BINDING,
|
|
82
|
+
} from '../gpu-texture-usage';
|
|
67
83
|
import {
|
|
68
84
|
GPU_BUFFER_USAGE_COPY_DST,
|
|
69
85
|
GPU_BUFFER_USAGE_INDEX,
|
|
86
|
+
GPU_BUFFER_USAGE_UNIFORM,
|
|
70
87
|
GPU_BUFFER_USAGE_VERTEX,
|
|
71
|
-
} from '
|
|
72
|
-
import {
|
|
73
|
-
createFaceUniformsBuffer,
|
|
74
|
-
createPrefilterUniformsBuffer,
|
|
75
|
-
writeAllFaceUniforms,
|
|
76
|
-
writeAllPrefilterUniforms,
|
|
77
|
-
} from './ibl/face-uniforms';
|
|
88
|
+
} from '../gpu-usage';
|
|
78
89
|
import {
|
|
90
|
+
CAPTURE_VIEW_PROJS,
|
|
79
91
|
CUBEMAP_FACE_VERTICES,
|
|
80
92
|
createIblPipelines,
|
|
93
|
+
type IblShaderModuleFactory,
|
|
94
|
+
PREFILTER_MIP_LEVELS,
|
|
95
|
+
PREFILTER_SIZE,
|
|
81
96
|
runIblPrecompute,
|
|
82
|
-
} from '
|
|
83
|
-
import type { RhiErrorListenerRegistry } from '
|
|
84
|
-
import {
|
|
85
|
-
type MeshRangeInvalidReason,
|
|
86
|
-
MeshUpdateError,
|
|
87
|
-
type MeshUpdateError as MeshUpdateErrorType,
|
|
88
|
-
} from './mesh-update-errors';
|
|
89
|
-
import {
|
|
90
|
-
blitMipmapsSync,
|
|
91
|
-
getOrCreateMipmapPipeline,
|
|
92
|
-
type MipmapBlitDevice,
|
|
93
|
-
type MipmapShaderModuleFactory,
|
|
94
|
-
} from './mipmap-generator';
|
|
97
|
+
} from '../ibl/IblPipelineCache';
|
|
98
|
+
import type { RhiErrorListenerRegistry } from '../lifecycle';
|
|
95
99
|
import {
|
|
96
100
|
type CubeRenderData,
|
|
97
101
|
deriveMipUploadLayout,
|
|
@@ -100,7 +104,114 @@ import {
|
|
|
100
104
|
deriveRenderDataTexture,
|
|
101
105
|
type MeshRenderData,
|
|
102
106
|
type TextureRenderData,
|
|
103
|
-
} from '
|
|
107
|
+
} from '../render-data';
|
|
108
|
+
import type { DeviceScope } from './device-scope';
|
|
109
|
+
|
|
110
|
+
const DYNAMIC_OFFSET_STRIDE = 256;
|
|
111
|
+
const FACE_COUNT = 6;
|
|
112
|
+
const PREFILTER_SUBPASS_COUNT = PREFILTER_MIP_LEVELS * FACE_COUNT;
|
|
113
|
+
|
|
114
|
+
export type FaceUniformsDevice = Pick<RhiDevice, 'createBuffer'> & {
|
|
115
|
+
readonly queue: Pick<RhiQueue, 'writeBuffer'>;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
function invalidUniformInput(expected: string, hint: string): Result<never, RhiError> {
|
|
119
|
+
return err(new RhiError({ code: 'webgpu-runtime-error', expected, hint }));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function createFaceUniformsBuffer(device: FaceUniformsDevice): Result<Buffer, RhiError> {
|
|
123
|
+
const descriptor: BufferDescriptor = {
|
|
124
|
+
label: 'ibl-face-uniforms',
|
|
125
|
+
size: FACE_COUNT * DYNAMIC_OFFSET_STRIDE,
|
|
126
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
127
|
+
mappedAtCreation: false,
|
|
128
|
+
};
|
|
129
|
+
return device.createBuffer(descriptor);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function createPrefilterUniformsBuffer(
|
|
133
|
+
device: FaceUniformsDevice,
|
|
134
|
+
): Result<Buffer, RhiError> {
|
|
135
|
+
const descriptor: BufferDescriptor = {
|
|
136
|
+
label: 'ibl-prefilter-uniforms',
|
|
137
|
+
size: PREFILTER_SUBPASS_COUNT * DYNAMIC_OFFSET_STRIDE,
|
|
138
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
139
|
+
mappedAtCreation: false,
|
|
140
|
+
};
|
|
141
|
+
return device.createBuffer(descriptor);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function writeFaceUniforms(
|
|
145
|
+
device: FaceUniformsDevice,
|
|
146
|
+
buffer: Buffer,
|
|
147
|
+
faceIdx: number,
|
|
148
|
+
viewProj: Float32Array,
|
|
149
|
+
): Result<void, RhiError> {
|
|
150
|
+
if (faceIdx < 0 || faceIdx >= FACE_COUNT) {
|
|
151
|
+
return invalidUniformInput(
|
|
152
|
+
'faceIdx is an integer in the inclusive range [0, 5]',
|
|
153
|
+
`writeFaceUniforms received faceIdx=${faceIdx}`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
if (viewProj.length !== 16) {
|
|
157
|
+
return invalidUniformInput(
|
|
158
|
+
'viewProj contains exactly 16 floats',
|
|
159
|
+
`writeFaceUniforms received ${viewProj.length} floats`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
return device.queue.writeBuffer(buffer, faceIdx * DYNAMIC_OFFSET_STRIDE, viewProj);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function writeAllFaceUniforms(
|
|
166
|
+
device: FaceUniformsDevice,
|
|
167
|
+
buffer: Buffer,
|
|
168
|
+
): Result<void, RhiError> {
|
|
169
|
+
for (let face = 0; face < FACE_COUNT; face++) {
|
|
170
|
+
const viewProj = CAPTURE_VIEW_PROJS[face];
|
|
171
|
+
if (viewProj === undefined) continue;
|
|
172
|
+
const result = writeFaceUniforms(device, buffer, face, viewProj);
|
|
173
|
+
if (!result.ok) return result;
|
|
174
|
+
}
|
|
175
|
+
return ok(undefined);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function writePrefilterUniforms(
|
|
179
|
+
device: FaceUniformsDevice,
|
|
180
|
+
buffer: Buffer,
|
|
181
|
+
subPassIdx: number,
|
|
182
|
+
roughness: number,
|
|
183
|
+
mipFaceSize: number,
|
|
184
|
+
): Result<void, RhiError> {
|
|
185
|
+
if (subPassIdx < 0 || subPassIdx >= PREFILTER_SUBPASS_COUNT) {
|
|
186
|
+
return invalidUniformInput(
|
|
187
|
+
`subPassIdx is an integer in the inclusive range [0, ${PREFILTER_SUBPASS_COUNT - 1}]`,
|
|
188
|
+
`writePrefilterUniforms received subPassIdx=${subPassIdx}`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
const payload = new Float32Array([roughness, mipFaceSize, 0, 0]);
|
|
192
|
+
return device.queue.writeBuffer(buffer, subPassIdx * DYNAMIC_OFFSET_STRIDE, payload);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function writeAllPrefilterUniforms(
|
|
196
|
+
device: FaceUniformsDevice,
|
|
197
|
+
buffer: Buffer,
|
|
198
|
+
): Result<void, RhiError> {
|
|
199
|
+
for (let mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) {
|
|
200
|
+
const roughness = mip / (PREFILTER_MIP_LEVELS - 1);
|
|
201
|
+
const mipFaceSize = PREFILTER_SIZE / 2 ** mip;
|
|
202
|
+
for (let face = 0; face < FACE_COUNT; face++) {
|
|
203
|
+
const result = writePrefilterUniforms(
|
|
204
|
+
device,
|
|
205
|
+
buffer,
|
|
206
|
+
mip * FACE_COUNT + face,
|
|
207
|
+
roughness,
|
|
208
|
+
mipFaceSize,
|
|
209
|
+
);
|
|
210
|
+
if (!result.ok) return result;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return ok(undefined);
|
|
214
|
+
}
|
|
104
215
|
|
|
105
216
|
// AssetError is constructed without importing the AssetRegistry's class; the
|
|
106
217
|
// store builds the 4-field surface (.code / .expected / .hint / .detail)
|
|
@@ -158,28 +269,6 @@ function makeImageError<C extends ImageErrorCode>(
|
|
|
158
269
|
return new RuntimeImageError<C>(detail);
|
|
159
270
|
}
|
|
160
271
|
|
|
161
|
-
// Extended device shape adding `queue.writeTexture` on top of
|
|
162
|
-
// `MipmapBlitDevice` (the mipmap-generator only needs the encoder + queue
|
|
163
|
-
// submit surface; uploadTexture additionally writes pixel bytes via
|
|
164
|
-
// `queue.writeTexture` -- spec anchor: WebGPU 26.6.4.4).
|
|
165
|
-
type MipmapBlitDeviceWithWriteTexture = MipmapBlitDevice & {
|
|
166
|
-
readonly queue: MipmapBlitDevice['queue'] & {
|
|
167
|
-
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
168
|
-
writeTexture(dst: any, data: any, layout: any, size: any): Result<void, any>;
|
|
169
|
-
};
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// Extended device shape adding `createBuffer` + `queue.writeBuffer` on top of
|
|
173
|
-
// `MipmapBlitDevice` for the user-mesh GPU upload path.
|
|
174
|
-
type MipmapBlitDeviceWithBuffer = MipmapBlitDevice & {
|
|
175
|
-
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
176
|
-
createBuffer(desc: any): Result<any, any>;
|
|
177
|
-
readonly queue: MipmapBlitDevice['queue'] & {
|
|
178
|
-
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
179
|
-
writeBuffer(buffer: any, offset: number, data: any): Result<void, any>;
|
|
180
|
-
};
|
|
181
|
-
};
|
|
182
|
-
|
|
183
272
|
/** Cube-POD register-relay injected by the wire layer (D-3). */
|
|
184
273
|
type RegisterCube = (
|
|
185
274
|
world: World,
|
|
@@ -215,13 +304,11 @@ interface TextureGpuEntry {
|
|
|
215
304
|
interface CubemapGpuEntry {
|
|
216
305
|
status: 'pending' | 'ready' | 'failed';
|
|
217
306
|
texture: GpuTexture | null;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
// biome-ignore lint/suspicious/noExplicitAny: per-face 2D views (6 faces; not GpuResources)
|
|
221
|
-
faceViews: readonly any[];
|
|
307
|
+
view: TextureView | undefined;
|
|
308
|
+
faceViews: readonly TextureView[];
|
|
222
309
|
}
|
|
223
310
|
|
|
224
|
-
interface
|
|
311
|
+
export interface MeshGpuHandles {
|
|
225
312
|
readonly vertexBuffer: GpuBuffer;
|
|
226
313
|
/**
|
|
227
314
|
* Index buffer wrapper, or `null` for a vertex-only mesh
|
|
@@ -239,15 +326,31 @@ interface MeshGpuEntry {
|
|
|
239
326
|
readonly vboBytes: number;
|
|
240
327
|
/** Allocation byte size for the ibo, or 0 when indexBuffer is null. */
|
|
241
328
|
readonly iboBytes: number;
|
|
242
|
-
/** Immutable geometry-owned layout projection used for all resident stride checks. */
|
|
243
|
-
readonly layoutProjection: VertexLayoutProjection;
|
|
244
329
|
readonly indexCount: number;
|
|
245
330
|
readonly indexFormat: 'uint16' | 'uint32';
|
|
246
|
-
/**
|
|
331
|
+
/**
|
|
332
|
+
* Vertex stride discriminator (feat-20260611). `'12F'` = 48 B
|
|
333
|
+
* (position+normal+uv+tangent); `'18F'` = 72 B (12F + skinIndex(uint16x4) +
|
|
334
|
+
* skinWeight(float32x4)). Mirrors `MeshRenderData.layout` in render-data.ts
|
|
335
|
+
* and `MeshRenderData.layout` -- the residency and render-data layout
|
|
336
|
+
* fields are the same union and move together.
|
|
337
|
+
*/
|
|
338
|
+
readonly layout: '12F' | '18F';
|
|
339
|
+
/** Geometry-owned vertex layout projection used by every GPU consumer. */
|
|
340
|
+
readonly layoutProjection: VertexLayoutProjection;
|
|
341
|
+
/**
|
|
342
|
+
* Number of UV sets the interleaved buffer carries (1 = single `uv`, +1 per
|
|
343
|
+
* `uv1..uv7`). feat-20260629-multi-uv-set-support: threaded to the record
|
|
344
|
+
* stage so the forward PSO's vertex layout stride matches the buffer for
|
|
345
|
+
* meshes with a real extra UV set. Mirrors MeshRenderData.uvSetCount /
|
|
346
|
+
* MeshRenderData.uvSetCount.
|
|
347
|
+
*/
|
|
348
|
+
readonly uvSetCount: number;
|
|
349
|
+
/** Vertex count uses 18F for skin or the geometry-owned canonical base stride. */
|
|
247
350
|
readonly vertexCount: number;
|
|
248
351
|
/** True when `MeshAsset.indices` is present (indexed draw path). */
|
|
249
352
|
readonly indexed: boolean;
|
|
250
|
-
/** Primitive topology (default 'triangle-list').
|
|
353
|
+
/** Primitive topology (default 'triangle-list'). */
|
|
251
354
|
readonly topology: PrimitiveTopology;
|
|
252
355
|
/**
|
|
253
356
|
* Submeshes from MeshAsset.submeshes, carried through to the record stage
|
|
@@ -257,41 +360,22 @@ interface MeshGpuEntry {
|
|
|
257
360
|
readonly submeshes: readonly import('@forgeax/engine-types').Submesh[];
|
|
258
361
|
}
|
|
259
362
|
|
|
260
|
-
function meshStrideBytes(layoutProjection: VertexLayoutProjection): number {
|
|
261
|
-
if (layoutProjection.arrayStride <= 0) {
|
|
262
|
-
throw new Error('mesh layout projection must have a positive array stride');
|
|
263
|
-
}
|
|
264
|
-
return layoutProjection.arrayStride;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function submeshesEqual(a: readonly Submesh[], b: readonly Submesh[]): boolean {
|
|
268
|
-
return (
|
|
269
|
-
a.length === b.length &&
|
|
270
|
-
a.every((left, index) => {
|
|
271
|
-
const right = b[index];
|
|
272
|
-
return (
|
|
273
|
-
right !== undefined &&
|
|
274
|
-
left.indexOffset === right.indexOffset &&
|
|
275
|
-
left.indexCount === right.indexCount &&
|
|
276
|
-
left.vertexCount === right.vertexCount &&
|
|
277
|
-
left.topology === right.topology &&
|
|
278
|
-
left.materialSlot === right.materialSlot
|
|
279
|
-
);
|
|
280
|
-
})
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
363
|
/**
|
|
285
364
|
* GPU residency store. Owns the per-handle GPU caches and the upload
|
|
286
365
|
* primitives that build them. Constructed once per renderer; wired with the
|
|
287
|
-
* GPU device via `configureGpuDevice` after `Renderer.
|
|
366
|
+
* GPU device via `configureGpuDevice` after `Renderer.initialization` resolves.
|
|
288
367
|
*/
|
|
289
|
-
export class
|
|
368
|
+
export class GpuResidencyCache {
|
|
290
369
|
/** Monotonic token for mesh GPU residency or contents changes. */
|
|
291
370
|
meshResidencyEpoch = 0;
|
|
292
371
|
/** Monotonic token for texture/sampler identities used by material bind groups. */
|
|
293
372
|
materialResourceEpoch = 0;
|
|
294
|
-
private gpuDevice:
|
|
373
|
+
private gpuDevice: RhiDevice | undefined = undefined;
|
|
374
|
+
/** RHI-only owner for IBL projection; never populated with a raw GPUDevice. */
|
|
375
|
+
private iblDevice: RhiDevice | undefined = undefined;
|
|
376
|
+
/** RHI Result-returning shader factory paired with `iblDevice`. */
|
|
377
|
+
private iblShaderModuleFactory: IblShaderModuleFactory | undefined = undefined;
|
|
378
|
+
private deviceScope: DeviceScope | undefined;
|
|
295
379
|
// Shader-module factory injected at `configureGpuDevice`; threaded into the
|
|
296
380
|
// mipmap-pipeline prewarm + the IBL precompute path. `undefined` until wired.
|
|
297
381
|
private asyncCreateShaderModule: MipmapShaderModuleFactory | undefined = undefined;
|
|
@@ -305,32 +389,45 @@ export class GpuResourceStore {
|
|
|
305
389
|
// path (_uploadCubemapFromEquirect) when `rgba16floatRenderable` is false.
|
|
306
390
|
private caps: RhiCaps | undefined = undefined;
|
|
307
391
|
|
|
308
|
-
private readonly textureGpuHandles: Map<number, TextureGpuEntry> = new Map();
|
|
309
|
-
private readonly samplerGpuHandles: Map<number, Sampler> = new Map();
|
|
392
|
+
private readonly textureGpuHandles: Map<string | number, TextureGpuEntry> = new Map();
|
|
393
|
+
private readonly samplerGpuHandles: Map<string | number, Sampler> = new Map();
|
|
310
394
|
private readonly cubemapGpuHandles: Map<number, CubemapGpuEntry> = new Map();
|
|
311
395
|
// Maps source EquirectAsset handle id -> minted cubemap handle so the same
|
|
312
396
|
// equirect source always resolves to the same cubemap (idempotent, A2).
|
|
313
397
|
private readonly cubemapIdempotentMap: Map<number, Handle<'EquirectAsset', 'shared'>> = new Map();
|
|
314
|
-
private readonly meshGpuHandles: Map<number,
|
|
398
|
+
private readonly meshGpuHandles: Map<string | number, MeshGpuHandles> = new Map();
|
|
315
399
|
|
|
316
400
|
/**
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
401
|
+
* Compose a cache key from the raw handle slot and its owning World. The
|
|
402
|
+
* draw-time world index is only a position in one frame's `worlds[]`; it is
|
|
403
|
+
* not an asset identity and can be reused by a later World. World objects
|
|
404
|
+
* therefore receive a renderer-local namespace, while numeric callers keep
|
|
405
|
+
* the legacy namespace for low-level tests and built-in upload paths.
|
|
321
406
|
*/
|
|
322
|
-
private
|
|
323
|
-
|
|
407
|
+
private readonly worldNamespaces = new WeakMap<World, number>();
|
|
408
|
+
private nextWorldNamespace = 1;
|
|
409
|
+
|
|
410
|
+
private worldKey(slot: number, world: World | number): string | number {
|
|
411
|
+
if (typeof world === 'number') {
|
|
412
|
+
return world === 0 ? slot : world * 0x1000000 + slot;
|
|
413
|
+
}
|
|
414
|
+
let namespace = this.worldNamespaces.get(world);
|
|
415
|
+
if (namespace === undefined) {
|
|
416
|
+
namespace = this.nextWorldNamespace;
|
|
417
|
+
this.nextWorldNamespace += 1;
|
|
418
|
+
this.worldNamespaces.set(world, namespace);
|
|
419
|
+
}
|
|
420
|
+
return `world:${namespace}:${slot}`;
|
|
324
421
|
}
|
|
325
422
|
|
|
326
423
|
/**
|
|
327
424
|
* Wire the GPU device, shader-module factory, and cube-POD register relay.
|
|
328
|
-
* Called once by createRenderer after `Renderer.
|
|
329
|
-
* performs NO replay
|
|
425
|
+
* Called once by createRenderer after `Renderer.initialization` resolves. Unlike the
|
|
426
|
+
* pre-extraction AssetRegistry.configureGpuDevice, this performs NO replay:
|
|
330
427
|
* the pull model builds resources lazily at first `ensureResident` access.
|
|
331
428
|
*/
|
|
332
429
|
configureGpuDevice(
|
|
333
|
-
device:
|
|
430
|
+
device: RhiDevice,
|
|
334
431
|
asyncCreateShaderModule: MipmapShaderModuleFactory | undefined,
|
|
335
432
|
registerCube: RegisterCube,
|
|
336
433
|
caps: RhiCaps,
|
|
@@ -342,6 +439,25 @@ export class GpuResourceStore {
|
|
|
342
439
|
this.caps = caps;
|
|
343
440
|
}
|
|
344
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Bind the IBL projection to the same opaque RhiDevice used by assembly.
|
|
444
|
+
* This is deliberately separate from the legacy-compatible mipmap fixture
|
|
445
|
+
* seam: unit fixtures that only exercise ordinary residency do not need an
|
|
446
|
+
* IBL device, while a real IBL projection never accepts a raw GPUDevice.
|
|
447
|
+
*/
|
|
448
|
+
configureIblDevice(
|
|
449
|
+
device: RhiDevice,
|
|
450
|
+
shaderModuleFactory: IblShaderModuleFactory | undefined,
|
|
451
|
+
): void {
|
|
452
|
+
this.iblDevice = device;
|
|
453
|
+
this.iblShaderModuleFactory = shaderModuleFactory;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Bind residency ownership to the renderer's single DeviceScope tree. */
|
|
457
|
+
bindDeviceScope(scope: DeviceScope): void {
|
|
458
|
+
this.deviceScope = scope;
|
|
459
|
+
}
|
|
460
|
+
|
|
345
461
|
/**
|
|
346
462
|
* Wrap a raw RHI Buffer handle into a GpuBuffer. The wrapper holds a
|
|
347
463
|
* reference to the device so `.destroy()` forwards to
|
|
@@ -353,19 +469,28 @@ export class GpuResourceStore {
|
|
|
353
469
|
* surfaces as a non-null assertion rather than a structured error.
|
|
354
470
|
*/
|
|
355
471
|
private wrapBuf(rawHandle: Buffer): GpuBuffer {
|
|
356
|
-
const device = this.gpuDevice
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
472
|
+
const device = this.gpuDevice;
|
|
473
|
+
if (device === undefined) {
|
|
474
|
+
throw new Error('GpuResidencyCache.wrapBuf called before configureGpuDevice');
|
|
475
|
+
}
|
|
476
|
+
const resource = new GpuBuffer(device, rawHandle);
|
|
477
|
+
this.deviceScope?._adopt('buffer', resource, (value: GpuBuffer) => {
|
|
478
|
+
if (!value.isDestroyed) value.destroy();
|
|
479
|
+
});
|
|
480
|
+
return resource;
|
|
363
481
|
}
|
|
364
482
|
|
|
365
483
|
/** Wrap a raw RHI Texture handle into a GpuTexture (mirror of wrapBuf). */
|
|
366
484
|
private wrapTex(rawHandle: Texture): GpuTexture {
|
|
367
|
-
const device = this.gpuDevice
|
|
368
|
-
|
|
485
|
+
const device = this.gpuDevice;
|
|
486
|
+
if (device === undefined) {
|
|
487
|
+
throw new Error('GpuResidencyCache.wrapTex called before configureGpuDevice');
|
|
488
|
+
}
|
|
489
|
+
const resource = new GpuTexture(device, rawHandle);
|
|
490
|
+
this.deviceScope?._adopt('texture', resource, (value: GpuTexture) => {
|
|
491
|
+
if (!value.isDestroyed) value.destroy();
|
|
492
|
+
});
|
|
493
|
+
return resource;
|
|
369
494
|
}
|
|
370
495
|
|
|
371
496
|
/**
|
|
@@ -386,16 +511,17 @@ export class GpuResourceStore {
|
|
|
386
511
|
*/
|
|
387
512
|
destroyAll(): void {
|
|
388
513
|
if (this.meshGpuHandles.size > 0) this.meshResidencyEpoch += 1;
|
|
514
|
+
const scopeOwnsResources = this.deviceScope !== undefined;
|
|
389
515
|
// The cubemap path registers two entries (sourceId + cubeId) sharing one
|
|
390
516
|
// GpuTexture wrapper, so destroyAll must dedupe on the wrapper identity
|
|
391
517
|
// before forwarding `.destroy()` -- otherwise the second call surfaces
|
|
392
518
|
// `'destroy-after-destroy'` from the RHI shim. The `isDestroyed` getter
|
|
393
519
|
// on the wrapper is the dedupe gate (architecture-principles §6).
|
|
394
520
|
const destroyTex = (gpuTex: GpuTexture): void => {
|
|
395
|
-
if (!gpuTex.isDestroyed) gpuTex.destroy();
|
|
521
|
+
if (!scopeOwnsResources && !gpuTex.isDestroyed) gpuTex.destroy();
|
|
396
522
|
};
|
|
397
523
|
const destroyBuf = (gpuBuf: GpuBuffer): void => {
|
|
398
|
-
if (!gpuBuf.isDestroyed) gpuBuf.destroy();
|
|
524
|
+
if (!scopeOwnsResources && !gpuBuf.isDestroyed) gpuBuf.destroy();
|
|
399
525
|
};
|
|
400
526
|
|
|
401
527
|
for (const entry of this.textureGpuHandles.values()) {
|
|
@@ -439,7 +565,7 @@ export class GpuResourceStore {
|
|
|
439
565
|
*/
|
|
440
566
|
evictTexture(
|
|
441
567
|
handle: Handle<'TextureAsset', 'shared'>,
|
|
442
|
-
worldId: number = 0,
|
|
568
|
+
worldId: World | number = 0,
|
|
443
569
|
): { freed: number; errors: RhiError[] } {
|
|
444
570
|
const id = this.worldKey(handleSlot(handle), worldId);
|
|
445
571
|
const entry = this.textureGpuHandles.get(id);
|
|
@@ -465,7 +591,7 @@ export class GpuResourceStore {
|
|
|
465
591
|
|
|
466
592
|
evictMesh(
|
|
467
593
|
handle: Handle<'MeshAsset', 'shared'>,
|
|
468
|
-
worldId: number = 0,
|
|
594
|
+
worldId: World | number = 0,
|
|
469
595
|
): { freed: number; errors: RhiError[] } {
|
|
470
596
|
const id = this.worldKey(handleSlot(handle), worldId);
|
|
471
597
|
const entry = this.meshGpuHandles.get(id);
|
|
@@ -533,9 +659,15 @@ export class GpuResourceStore {
|
|
|
533
659
|
const errors: RhiError[] = [];
|
|
534
660
|
let meshResidencyChanged = false;
|
|
535
661
|
let materialResidencyChanged = false;
|
|
662
|
+
const liveSlots = new Set([...liveSet].map((slot) => String(slot)));
|
|
663
|
+
const isLiveKey = (key: string | number): boolean => {
|
|
664
|
+
const slot =
|
|
665
|
+
typeof key === 'number' ? String(key % 0x1000000) : key.slice(key.lastIndexOf(':') + 1);
|
|
666
|
+
return liveSlots.has(slot);
|
|
667
|
+
};
|
|
536
668
|
|
|
537
669
|
for (const key of this.textureGpuHandles.keys()) {
|
|
538
|
-
if (!
|
|
670
|
+
if (!isLiveKey(key)) {
|
|
539
671
|
const entry = this.textureGpuHandles.get(key);
|
|
540
672
|
if (entry !== undefined) {
|
|
541
673
|
if (!entry.texture.isDestroyed) {
|
|
@@ -554,14 +686,14 @@ export class GpuResourceStore {
|
|
|
554
686
|
}
|
|
555
687
|
|
|
556
688
|
for (const key of this.samplerGpuHandles.keys()) {
|
|
557
|
-
if (!
|
|
689
|
+
if (!isLiveKey(key)) {
|
|
558
690
|
this.samplerGpuHandles.delete(key);
|
|
559
691
|
materialResidencyChanged = true;
|
|
560
692
|
}
|
|
561
693
|
}
|
|
562
694
|
|
|
563
695
|
for (const key of this.cubemapGpuHandles.keys()) {
|
|
564
|
-
if (!
|
|
696
|
+
if (!liveSlots.has(String(key))) {
|
|
565
697
|
const entry = this.cubemapGpuHandles.get(key);
|
|
566
698
|
if (entry !== undefined) {
|
|
567
699
|
// A 'failed' entry holds no GPU texture (texture:null) — nothing to free.
|
|
@@ -580,7 +712,7 @@ export class GpuResourceStore {
|
|
|
580
712
|
}
|
|
581
713
|
|
|
582
714
|
for (const key of this.meshGpuHandles.keys()) {
|
|
583
|
-
if (!
|
|
715
|
+
if (!isLiveKey(key)) {
|
|
584
716
|
const entry = this.meshGpuHandles.get(key);
|
|
585
717
|
if (entry !== undefined) {
|
|
586
718
|
if (!entry.vertexBuffer.isDestroyed) {
|
|
@@ -615,7 +747,7 @@ export class GpuResourceStore {
|
|
|
615
747
|
|
|
616
748
|
/**
|
|
617
749
|
* Prewarm the mipmap pipeline cache for the given texture formats (D-9).
|
|
618
|
-
* Called from `createRenderer.
|
|
750
|
+
* Called from `createRenderer.initialization` (already async): builds the one-time
|
|
619
751
|
* mipmap shader module + per-format render pipeline into the mipmap-generator
|
|
620
752
|
* deviceCache so the per-texture mipmap blit at record-stage `ensureResident`
|
|
621
753
|
* is pure synchronous encoder work. A format absent from this list will make
|
|
@@ -623,8 +755,8 @@ export class GpuResourceStore {
|
|
|
623
755
|
* than lazily await a build (which would break the sync draw contract).
|
|
624
756
|
*/
|
|
625
757
|
async prewarmMipmapPipeline(
|
|
626
|
-
device:
|
|
627
|
-
formats: readonly
|
|
758
|
+
device: RhiDevice,
|
|
759
|
+
formats: readonly TextureFormat[],
|
|
628
760
|
): Promise<Result<void, RhiError>> {
|
|
629
761
|
const factory = this.asyncCreateShaderModule;
|
|
630
762
|
if (factory === undefined) {
|
|
@@ -660,7 +792,7 @@ export class GpuResourceStore {
|
|
|
660
792
|
*/
|
|
661
793
|
_getTextureGpuTexture(
|
|
662
794
|
handle: Handle<'TextureAsset', 'shared'>,
|
|
663
|
-
worldId: number = 0,
|
|
795
|
+
worldId: World | number = 0,
|
|
664
796
|
): GpuTexture | undefined {
|
|
665
797
|
return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.texture;
|
|
666
798
|
}
|
|
@@ -671,7 +803,7 @@ export class GpuResourceStore {
|
|
|
671
803
|
*/
|
|
672
804
|
getTextureGpuView(
|
|
673
805
|
handle: Handle<'TextureAsset', 'shared'>,
|
|
674
|
-
worldId: number = 0,
|
|
806
|
+
worldId: World | number = 0,
|
|
675
807
|
): TextureView | undefined {
|
|
676
808
|
return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.view;
|
|
677
809
|
}
|
|
@@ -684,12 +816,12 @@ export class GpuResourceStore {
|
|
|
684
816
|
ensureSamplerResident(
|
|
685
817
|
handle: Handle<'SamplerAsset', 'shared'>,
|
|
686
818
|
pod: SamplerAsset,
|
|
687
|
-
worldId: number = 0,
|
|
819
|
+
worldId: World | number = 0,
|
|
688
820
|
): Result<Sampler, RhiError> {
|
|
689
821
|
const key = this.worldKey(handleSlot(handle), worldId);
|
|
690
822
|
const existing = this.samplerGpuHandles.get(key);
|
|
691
823
|
if (existing !== undefined) return ok(existing);
|
|
692
|
-
const device = this.gpuDevice
|
|
824
|
+
const device = this.gpuDevice;
|
|
693
825
|
if (device === undefined) {
|
|
694
826
|
return err(
|
|
695
827
|
new RhiError({
|
|
@@ -719,14 +851,14 @@ export class GpuResourceStore {
|
|
|
719
851
|
}
|
|
720
852
|
|
|
721
853
|
/** Return the full-cube texture view, or `undefined` if not uploaded yet. */
|
|
722
|
-
|
|
723
|
-
getCubemapGpuView(handle: Handle<'EquirectAsset', 'shared'>): any | undefined {
|
|
854
|
+
getCubemapGpuView(handle: Handle<'EquirectAsset', 'shared'>): TextureView | undefined {
|
|
724
855
|
return this.cubemapGpuHandles.get(handleSlot(handle))?.view;
|
|
725
856
|
}
|
|
726
857
|
|
|
727
858
|
/** Return per-face 2D views (6 faces), or `undefined` if not uploaded yet. */
|
|
728
|
-
|
|
729
|
-
|
|
859
|
+
getCubemapFaceViews(
|
|
860
|
+
handle: Handle<'EquirectAsset', 'shared'>,
|
|
861
|
+
): readonly TextureView[] | undefined {
|
|
730
862
|
return this.cubemapGpuHandles.get(handleSlot(handle))?.faceViews;
|
|
731
863
|
}
|
|
732
864
|
|
|
@@ -753,11 +885,30 @@ export class GpuResourceStore {
|
|
|
753
885
|
*/
|
|
754
886
|
getMeshGpuHandles(
|
|
755
887
|
handle: Handle<'MeshAsset', 'shared'>,
|
|
756
|
-
worldId: number = 0,
|
|
757
|
-
):
|
|
888
|
+
worldId: World | number = 0,
|
|
889
|
+
): MeshGpuHandles | undefined {
|
|
758
890
|
return this.meshGpuHandles.get(this.worldKey(handleSlot(handle), worldId));
|
|
759
891
|
}
|
|
760
892
|
|
|
893
|
+
/**
|
|
894
|
+
* Drop one mutable shared mesh's GPU copy after ECS publishes `markChanged`.
|
|
895
|
+
* The next record pass resolves the same POD and rebuilds residency through
|
|
896
|
+
* the normal pull path, so ECS remains the payload authority.
|
|
897
|
+
*/
|
|
898
|
+
invalidateMesh(handle: number, worldId: World | number = 0): void {
|
|
899
|
+
// SharedRefMutation publishes the encoded handle value, while the cache
|
|
900
|
+
// key is derived from the decoded slot. Keep the invalidation path aligned
|
|
901
|
+
// with getMeshGpuHandles/ensureResident so an in-place ECS payload change
|
|
902
|
+
// cannot leave the old VBO resident.
|
|
903
|
+
const key = this.worldKey(handleSlot(toShared<'MeshAsset'>(handle)), worldId);
|
|
904
|
+
const entry = this.meshGpuHandles.get(key);
|
|
905
|
+
if (entry === undefined) return;
|
|
906
|
+
entry.vertexBuffer.destroy();
|
|
907
|
+
if (entry.indexBuffer !== null) entry.indexBuffer.destroy();
|
|
908
|
+
this.meshGpuHandles.delete(key);
|
|
909
|
+
this.meshResidencyEpoch += 1;
|
|
910
|
+
}
|
|
911
|
+
|
|
761
912
|
/**
|
|
762
913
|
* Pull-model residency: build the GPU resource for `handle` from the
|
|
763
914
|
* caller-provided POD on a miss, return the cached handles on a hit (D-2).
|
|
@@ -772,12 +923,21 @@ export class GpuResourceStore {
|
|
|
772
923
|
* (`_uploadCubemapFromEquirect`). Builtin meshes are NOT routed here either
|
|
773
924
|
* (createRenderer step-3 owns them; D-1).
|
|
774
925
|
*/
|
|
926
|
+
ensureResident(
|
|
927
|
+
handle: Handle<'MeshAsset', 'shared'>,
|
|
928
|
+
pod: TypesMeshAsset,
|
|
929
|
+
worldId?: World | number,
|
|
930
|
+
): Result<MeshGpuHandles, RhiError | AssetError | ImageError>;
|
|
931
|
+
ensureResident(
|
|
932
|
+
handle: Handle<'TextureAsset', 'shared'>,
|
|
933
|
+
pod: TextureAsset,
|
|
934
|
+
worldId?: World | number,
|
|
935
|
+
): Result<TextureGpuEntry, RhiError | AssetError | ImageError>;
|
|
775
936
|
ensureResident(
|
|
776
937
|
handle: Handle<'MeshAsset', 'shared'> | Handle<'TextureAsset', 'shared'>,
|
|
777
938
|
pod: TypesMeshAsset | TextureAsset,
|
|
778
|
-
worldId: number = 0,
|
|
779
|
-
|
|
780
|
-
): Result<any, RhiError | AssetError | ImageError> {
|
|
939
|
+
worldId: World | number = 0,
|
|
940
|
+
): Result<TextureGpuEntry | MeshGpuHandles, RhiError | AssetError | ImageError> {
|
|
781
941
|
const id = handleSlot(handle);
|
|
782
942
|
// Hit: O(1) cache lookup, never re-projects (AC-09 -- deriveRenderData*
|
|
783
943
|
// runs only on a miss). The miss arms below dispatch on `pod.kind` with NO
|
|
@@ -825,7 +985,7 @@ export class GpuResourceStore {
|
|
|
825
985
|
handle: Handle<'TextureAsset', 'shared'>,
|
|
826
986
|
pod: TextureAsset,
|
|
827
987
|
decoded: DecodedImage,
|
|
828
|
-
worldId: number = 0,
|
|
988
|
+
worldId: World | number = 0,
|
|
829
989
|
): Promise<Result<void, AssetError | ImageError | RhiError>> {
|
|
830
990
|
const device = this.gpuDevice;
|
|
831
991
|
const projected = deriveRenderDataTexture(pod);
|
|
@@ -844,7 +1004,7 @@ export class GpuResourceStore {
|
|
|
844
1004
|
new RhiError({
|
|
845
1005
|
code: 'rhi-not-available',
|
|
846
1006
|
expected: 'asyncCreateShaderModule wired by configureGpuDevice',
|
|
847
|
-
hint: '
|
|
1007
|
+
hint: 'GpuResidencyCache.uploadTexture mipmap branch needs a shader-module factory; rhi-webgpu / rhi-wgpu shims expose it via pack.createShaderModule. Explicit-rhi instances must surface the factory on the RhiInstance.',
|
|
848
1008
|
}),
|
|
849
1009
|
);
|
|
850
1010
|
}
|
|
@@ -854,7 +1014,7 @@ export class GpuResourceStore {
|
|
|
854
1014
|
new RhiError({
|
|
855
1015
|
code: 'rhi-not-available',
|
|
856
1016
|
expected: `mipmap pipeline for format ${tex.format} builds`,
|
|
857
|
-
hint: '
|
|
1017
|
+
hint: 'GpuResidencyCache.uploadTexture could not build the mipmap pipeline',
|
|
858
1018
|
}),
|
|
859
1019
|
);
|
|
860
1020
|
}
|
|
@@ -900,7 +1060,7 @@ export class GpuResourceStore {
|
|
|
900
1060
|
pod: TextureAsset,
|
|
901
1061
|
decoded: DecodedImage,
|
|
902
1062
|
renderData: TextureRenderData,
|
|
903
|
-
worldId: number = 0,
|
|
1063
|
+
worldId: World | number = 0,
|
|
904
1064
|
): Result<TextureGpuEntry, AssetError | ImageError | RhiError> {
|
|
905
1065
|
const device = this.gpuDevice;
|
|
906
1066
|
const prepared = this.prepareTextureUpload(handle, pod, decoded, renderData);
|
|
@@ -909,7 +1069,7 @@ export class GpuResourceStore {
|
|
|
909
1069
|
return err(
|
|
910
1070
|
new RhiError({
|
|
911
1071
|
code: 'rhi-not-available',
|
|
912
|
-
expected: '
|
|
1072
|
+
expected: 'GpuResidencyCache.configureGpuDevice wired before ensureResident',
|
|
913
1073
|
hint: 'a texture ensureResident ran before the GPU device was wired; call gpuStore.configureGpuDevice in createRenderer',
|
|
914
1074
|
}),
|
|
915
1075
|
);
|
|
@@ -957,8 +1117,7 @@ export class GpuResourceStore {
|
|
|
957
1117
|
decoded: DecodedImage,
|
|
958
1118
|
renderData: TextureRenderData,
|
|
959
1119
|
): Result<
|
|
960
|
-
|
|
961
|
-
{ id: number; tex: TextureAsset; levels: number; gpuTexture: any | undefined },
|
|
1120
|
+
{ id: number; tex: TextureAsset; levels: number; gpuTexture: Texture | undefined },
|
|
962
1121
|
AssetError | ImageError | RhiError
|
|
963
1122
|
> {
|
|
964
1123
|
const id = handleSlot(handle);
|
|
@@ -996,9 +1155,7 @@ export class GpuResourceStore {
|
|
|
996
1155
|
// upload before allocating a handle; binding a texture that the backend
|
|
997
1156
|
// accepted syntactically but cannot use later surfaces only as a generic
|
|
998
1157
|
// invalid-bind-group submit error.
|
|
999
|
-
const maxDimension =
|
|
1000
|
-
(device as unknown as { readonly limits?: { readonly maxTextureDimension2D?: number } })
|
|
1001
|
-
.limits?.maxTextureDimension2D ?? 16_384;
|
|
1158
|
+
const maxDimension = device.limits.maxTextureDimension2D;
|
|
1002
1159
|
if (tex.width > maxDimension || tex.height > maxDimension) {
|
|
1003
1160
|
return err(
|
|
1004
1161
|
makeImageError({
|
|
@@ -1022,10 +1179,11 @@ export class GpuResourceStore {
|
|
|
1022
1179
|
format: renderData.format,
|
|
1023
1180
|
usage: renderData.usage,
|
|
1024
1181
|
viewFormats: [],
|
|
1182
|
+
textureBindingViewDimension: undefined,
|
|
1025
1183
|
});
|
|
1026
1184
|
if (!createTexRes.ok) return createTexRes;
|
|
1027
1185
|
const gpuTexture = createTexRes.value;
|
|
1028
|
-
const queue =
|
|
1186
|
+
const queue = device.queue;
|
|
1029
1187
|
|
|
1030
1188
|
if (renderData.compressed) {
|
|
1031
1189
|
// feat-20260707 M5 / w36 (AC-08): block-compressed textures carry their
|
|
@@ -1137,7 +1295,7 @@ export class GpuResourceStore {
|
|
|
1137
1295
|
return recordFailed(
|
|
1138
1296
|
makeAssetError({
|
|
1139
1297
|
code: 'asset-not-found',
|
|
1140
|
-
expected: '
|
|
1298
|
+
expected: 'GpuResidencyCache.configureGpuDevice wired with registerCube',
|
|
1141
1299
|
hint: 'call gpuStore.configureGpuDevice(device, factory, registerCube) before _uploadCubemapFromEquirect',
|
|
1142
1300
|
}),
|
|
1143
1301
|
);
|
|
@@ -1162,7 +1320,7 @@ export class GpuResourceStore {
|
|
|
1162
1320
|
// guard above. The entry holds no live GPU resources yet (record binds the
|
|
1163
1321
|
// white fallback while pending); the texture/views are filled in below when
|
|
1164
1322
|
// the projection completes (status flips to 'ready'), or replaced by a
|
|
1165
|
-
// 'failed' entry via recordFailed if
|
|
1323
|
+
// 'failed' entry via recordFailed if a later step errors.
|
|
1166
1324
|
this.cubemapGpuHandles.set(sourceId, {
|
|
1167
1325
|
status: 'pending',
|
|
1168
1326
|
texture: null,
|
|
@@ -1231,45 +1389,28 @@ export class GpuResourceStore {
|
|
|
1231
1389
|
// the cubemap and all three IBL side textures by IblPipelineCache.
|
|
1232
1390
|
const outputFormat = this.caps?.rgba16floatRenderable === false ? 'rgba8unorm' : tex.format;
|
|
1233
1391
|
|
|
1234
|
-
// Lazy projection runs from the record arm, where the device is always
|
|
1235
|
-
// wired (research R-5). The
|
|
1236
|
-
//
|
|
1237
|
-
|
|
1238
|
-
|
|
1392
|
+
// Lazy projection runs from the record arm, where the RHI device is always
|
|
1393
|
+
// wired (research R-5). The IBL path is deliberately independent from the
|
|
1394
|
+
// ordinary mipmap fixture seam: it accepts only the opaque RhiDevice and
|
|
1395
|
+
// its Result-returning shader factory, never a raw GPUDevice or unwrap
|
|
1396
|
+
// fallback.
|
|
1397
|
+
const device = this.iblDevice;
|
|
1398
|
+
const shaderModuleFactory = this.iblShaderModuleFactory;
|
|
1399
|
+
const scope = this.deviceScope;
|
|
1400
|
+
if (device === undefined || shaderModuleFactory === undefined || scope === undefined) {
|
|
1239
1401
|
return recordFailed(
|
|
1240
1402
|
new RhiError({
|
|
1241
1403
|
code: 'rhi-not-available',
|
|
1242
|
-
expected:
|
|
1243
|
-
|
|
1404
|
+
expected:
|
|
1405
|
+
'GpuResidencyCache is configured with an RhiDevice, shader factory, and DeviceScope',
|
|
1406
|
+
hint: 'bind the renderer DeviceScope before a cubemap projection; raw GPU fallback is not supported',
|
|
1244
1407
|
}),
|
|
1245
1408
|
);
|
|
1246
1409
|
}
|
|
1247
1410
|
|
|
1248
1411
|
// Usage flags for the small equirect helper texture (sampled source for the
|
|
1249
1412
|
// IBL precompute pass); the cube texture's usage comes from the projection.
|
|
1250
|
-
|
|
1251
|
-
const unwrap = (r: any): any => {
|
|
1252
|
-
if (r === null || r === undefined) return undefined;
|
|
1253
|
-
if (typeof r === 'object' && 'ok' in r) return r.ok ? r.value : undefined;
|
|
1254
|
-
return r;
|
|
1255
|
-
};
|
|
1256
|
-
// biome-ignore lint/suspicious/noExplicitAny: shim/raw device split
|
|
1257
|
-
const makeTextureView = (dev: any, texture: any, desc: any): any => {
|
|
1258
|
-
if (typeof dev.createTextureView === 'function') {
|
|
1259
|
-
const u = unwrap(dev.createTextureView(texture, desc));
|
|
1260
|
-
if (u !== undefined) return u;
|
|
1261
|
-
}
|
|
1262
|
-
if (texture !== null && texture !== undefined && typeof texture.createView === 'function') {
|
|
1263
|
-
try {
|
|
1264
|
-
return texture.createView(desc);
|
|
1265
|
-
} catch {
|
|
1266
|
-
return undefined;
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
return undefined;
|
|
1270
|
-
};
|
|
1271
|
-
|
|
1272
|
-
const gpuTextureRet = device.createTexture({
|
|
1413
|
+
const gpuTextureResult = device.createTexture({
|
|
1273
1414
|
label: `cubemap-${sourceId}`,
|
|
1274
1415
|
size: { width: cubeFaceSize, height: cubeFaceSize, depthOrArrayLayers: 6 },
|
|
1275
1416
|
mipLevelCount: 1,
|
|
@@ -1278,52 +1419,29 @@ export class GpuResourceStore {
|
|
|
1278
1419
|
format: outputFormat,
|
|
1279
1420
|
usage: projected.cubeUsage,
|
|
1280
1421
|
viewFormats: [],
|
|
1422
|
+
textureBindingViewDimension: undefined,
|
|
1281
1423
|
});
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
return recordFailed(
|
|
1285
|
-
makeAssetError({
|
|
1286
|
-
code: 'ibl-precompute-not-dispatched',
|
|
1287
|
-
expected: `device.createTexture cubemap-${sourceId} to return a valid texture`,
|
|
1288
|
-
hint: ASSET_ERROR_HINTS['ibl-precompute-not-dispatched'],
|
|
1289
|
-
}),
|
|
1290
|
-
);
|
|
1291
|
-
}
|
|
1424
|
+
if (!gpuTextureResult.ok) return recordFailed(gpuTextureResult.error);
|
|
1425
|
+
const gpuTexture = gpuTextureResult.value;
|
|
1292
1426
|
|
|
1293
|
-
const
|
|
1427
|
+
const cubeViewResult = device.createTextureView(gpuTexture, {
|
|
1294
1428
|
label: `cubemap-view-${sourceId}`,
|
|
1295
1429
|
dimension: 'cube',
|
|
1296
1430
|
arrayLayerCount: 6,
|
|
1297
1431
|
});
|
|
1298
|
-
if (
|
|
1299
|
-
|
|
1300
|
-
makeAssetError({
|
|
1301
|
-
code: 'ibl-precompute-not-dispatched',
|
|
1302
|
-
expected: `device.createTextureView cubemap-view-${sourceId} to return a valid view`,
|
|
1303
|
-
hint: ASSET_ERROR_HINTS['ibl-precompute-not-dispatched'],
|
|
1304
|
-
}),
|
|
1305
|
-
);
|
|
1306
|
-
}
|
|
1432
|
+
if (!cubeViewResult.ok) return recordFailed(cubeViewResult.error);
|
|
1433
|
+
const cubeView = cubeViewResult.value;
|
|
1307
1434
|
|
|
1308
|
-
|
|
1309
|
-
const faceViews: any[] = [];
|
|
1435
|
+
const faceViews: TextureView[] = [];
|
|
1310
1436
|
for (let face = 0; face < 6; face++) {
|
|
1311
|
-
const
|
|
1437
|
+
const faceViewResult = device.createTextureView(gpuTexture, {
|
|
1312
1438
|
label: `cubemap-face-${sourceId}-${face}`,
|
|
1313
1439
|
dimension: '2d',
|
|
1314
1440
|
baseArrayLayer: face,
|
|
1315
1441
|
arrayLayerCount: 1,
|
|
1316
1442
|
});
|
|
1317
|
-
if (
|
|
1318
|
-
|
|
1319
|
-
makeAssetError({
|
|
1320
|
-
code: 'ibl-precompute-not-dispatched',
|
|
1321
|
-
expected: `device.createTextureView cubemap-face-${sourceId}-${face} to return a valid view`,
|
|
1322
|
-
hint: ASSET_ERROR_HINTS['ibl-precompute-not-dispatched'],
|
|
1323
|
-
}),
|
|
1324
|
-
);
|
|
1325
|
-
}
|
|
1326
|
-
faceViews.push(fv);
|
|
1443
|
+
if (!faceViewResult.ok) return recordFailed(faceViewResult.error);
|
|
1444
|
+
faceViews.push(faceViewResult.value);
|
|
1327
1445
|
}
|
|
1328
1446
|
|
|
1329
1447
|
// M-3 / w11: a single GpuTexture wrapper is shared between sourceId
|
|
@@ -1367,89 +1485,95 @@ export class GpuResourceStore {
|
|
|
1367
1485
|
|
|
1368
1486
|
this.cubemapIdempotentMap.set(sourceId, cubeHandle);
|
|
1369
1487
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1488
|
+
const pipelinesResult = await createIblPipelines(
|
|
1489
|
+
scope,
|
|
1490
|
+
device,
|
|
1491
|
+
shaderModuleFactory,
|
|
1492
|
+
outputFormat,
|
|
1493
|
+
);
|
|
1494
|
+
if (!pipelinesResult.ok) {
|
|
1495
|
+
return recordFailed(
|
|
1496
|
+
makeAssetError({
|
|
1497
|
+
code: 'ibl-precompute-not-dispatched',
|
|
1498
|
+
expected: pipelinesResult.error.expected,
|
|
1499
|
+
hint: pipelinesResult.error.hint,
|
|
1500
|
+
}),
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
const equirectTextureResult = device.createTexture({
|
|
1505
|
+
label: `equirect-${sourceId}`,
|
|
1506
|
+
size: { width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
|
|
1507
|
+
mipLevelCount: 1,
|
|
1508
|
+
sampleCount: 1,
|
|
1509
|
+
dimension: '2d',
|
|
1510
|
+
format: tex.format,
|
|
1511
|
+
usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST,
|
|
1512
|
+
viewFormats: [],
|
|
1513
|
+
textureBindingViewDimension: undefined,
|
|
1514
|
+
});
|
|
1515
|
+
if (!equirectTextureResult.ok) return recordFailed(equirectTextureResult.error);
|
|
1516
|
+
const equirectGpuTex = equirectTextureResult.value;
|
|
1517
|
+
|
|
1518
|
+
const equirectViewResult = device.createTextureView(equirectGpuTex, {
|
|
1519
|
+
label: `equirect-view-${sourceId}`,
|
|
1520
|
+
dimension: '2d',
|
|
1521
|
+
});
|
|
1522
|
+
if (!equirectViewResult.ok) return recordFailed(equirectViewResult.error);
|
|
1523
|
+
const equirectView = equirectViewResult.value;
|
|
1524
|
+
|
|
1525
|
+
const bytesPerPixel = tex.format === 'rgba32float' ? 16 : tex.format === 'rgba16float' ? 8 : 4;
|
|
1526
|
+
const equirectWriteResult = device.queue.writeTexture(
|
|
1527
|
+
{ texture: equirectGpuTex },
|
|
1528
|
+
tex.data,
|
|
1529
|
+
{ bytesPerRow: tex.width * bytesPerPixel, rowsPerImage: tex.height },
|
|
1530
|
+
{ width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
|
|
1531
|
+
);
|
|
1532
|
+
if (!equirectWriteResult.ok) return recordFailed(equirectWriteResult.error);
|
|
1533
|
+
|
|
1534
|
+
const faceBufferResult = createFaceUniformsBuffer(device);
|
|
1535
|
+
if (!faceBufferResult.ok) return recordFailed(faceBufferResult.error);
|
|
1536
|
+
const faceBuf = faceBufferResult.value;
|
|
1537
|
+
const prefilterBufferResult = createPrefilterUniformsBuffer(device);
|
|
1538
|
+
if (!prefilterBufferResult.ok) return recordFailed(prefilterBufferResult.error);
|
|
1539
|
+
const prefBuf = prefilterBufferResult.value;
|
|
1540
|
+
|
|
1541
|
+
const cubeVertexResult = device.createBuffer({
|
|
1542
|
+
label: 'ibl-cube-verts',
|
|
1543
|
+
size: CUBEMAP_FACE_VERTICES.byteLength,
|
|
1544
|
+
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1545
|
+
mappedAtCreation: false,
|
|
1546
|
+
});
|
|
1547
|
+
if (!cubeVertexResult.ok) return recordFailed(cubeVertexResult.error);
|
|
1548
|
+
const cubeVertex = cubeVertexResult.value;
|
|
1549
|
+
|
|
1550
|
+
const cubeVertexWriteResult = device.queue.writeBuffer(cubeVertex, 0, CUBEMAP_FACE_VERTICES);
|
|
1551
|
+
if (!cubeVertexWriteResult.ok) return recordFailed(cubeVertexWriteResult.error);
|
|
1552
|
+
const faceUniformsResult = writeAllFaceUniforms(device, faceBuf);
|
|
1553
|
+
if (!faceUniformsResult.ok) return recordFailed(faceUniformsResult.error);
|
|
1554
|
+
const prefilterUniformsResult = writeAllPrefilterUniforms(device, prefBuf);
|
|
1555
|
+
if (!prefilterUniformsResult.ok) return recordFailed(prefilterUniformsResult.error);
|
|
1556
|
+
|
|
1557
|
+
const runResult = runIblPrecompute({
|
|
1558
|
+
scope,
|
|
1559
|
+
device,
|
|
1560
|
+
equirectGpuTex,
|
|
1561
|
+
equirectView,
|
|
1562
|
+
cubeGpuTex: gpuTexture,
|
|
1563
|
+
cubeView,
|
|
1564
|
+
cubeFaceViews: faceViews,
|
|
1565
|
+
faceUniformsBuffer: faceBuf,
|
|
1566
|
+
prefilterUniformsBuffer: prefBuf,
|
|
1567
|
+
cubeVertexBuffer: cubeVertex,
|
|
1568
|
+
});
|
|
1569
|
+
if (!runResult.ok) {
|
|
1570
|
+
return recordFailed(
|
|
1571
|
+
makeAssetError({
|
|
1572
|
+
code: 'ibl-precompute-not-dispatched',
|
|
1573
|
+
expected: runResult.error.expected,
|
|
1574
|
+
hint: runResult.error.hint,
|
|
1575
|
+
}),
|
|
1378
1576
|
);
|
|
1379
|
-
if (pipelinesRes.ok) {
|
|
1380
|
-
// biome-ignore lint/suspicious/noExplicitAny: device shim
|
|
1381
|
-
const fdevice = device as any;
|
|
1382
|
-
const equirectGpuTexRet = fdevice.createTexture({
|
|
1383
|
-
label: `equirect-${sourceId}`,
|
|
1384
|
-
size: { width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
|
|
1385
|
-
mipLevelCount: 1,
|
|
1386
|
-
sampleCount: 1,
|
|
1387
|
-
dimension: '2d',
|
|
1388
|
-
format: tex.format,
|
|
1389
|
-
usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST,
|
|
1390
|
-
viewFormats: [],
|
|
1391
|
-
});
|
|
1392
|
-
const equirectGpuTex = unwrap(equirectGpuTexRet);
|
|
1393
|
-
if (equirectGpuTex !== undefined && typeof fdevice.queue?.writeTexture === 'function') {
|
|
1394
|
-
const bytesPerPixel =
|
|
1395
|
-
tex.format === 'rgba32float' ? 16 : tex.format === 'rgba16float' ? 8 : 4;
|
|
1396
|
-
fdevice.queue.writeTexture(
|
|
1397
|
-
{ texture: equirectGpuTex },
|
|
1398
|
-
tex.data,
|
|
1399
|
-
{ bytesPerRow: tex.width * bytesPerPixel, rowsPerImage: tex.height },
|
|
1400
|
-
{ width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
|
|
1401
|
-
);
|
|
1402
|
-
}
|
|
1403
|
-
const equirectView =
|
|
1404
|
-
equirectGpuTex !== undefined
|
|
1405
|
-
? makeTextureView(fdevice, equirectGpuTex, {
|
|
1406
|
-
label: `equirect-view-${sourceId}`,
|
|
1407
|
-
dimension: '2d',
|
|
1408
|
-
})
|
|
1409
|
-
: undefined;
|
|
1410
|
-
const faceBuf = unwrap(createFaceUniformsBuffer(ipDevice));
|
|
1411
|
-
const prefBuf = unwrap(createPrefilterUniformsBuffer(ipDevice));
|
|
1412
|
-
const cubeVertex = unwrap(
|
|
1413
|
-
fdevice.createBuffer({
|
|
1414
|
-
label: 'ibl-cube-verts',
|
|
1415
|
-
size: CUBEMAP_FACE_VERTICES.byteLength,
|
|
1416
|
-
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1417
|
-
mappedAtCreation: false,
|
|
1418
|
-
}),
|
|
1419
|
-
);
|
|
1420
|
-
if (
|
|
1421
|
-
equirectGpuTex !== undefined &&
|
|
1422
|
-
equirectView !== undefined &&
|
|
1423
|
-
faceBuf !== undefined &&
|
|
1424
|
-
prefBuf !== undefined &&
|
|
1425
|
-
cubeVertex !== undefined &&
|
|
1426
|
-
typeof fdevice.queue?.writeBuffer === 'function'
|
|
1427
|
-
) {
|
|
1428
|
-
fdevice.queue.writeBuffer(cubeVertex, 0, CUBEMAP_FACE_VERTICES);
|
|
1429
|
-
writeAllFaceUniforms(ipDevice, faceBuf);
|
|
1430
|
-
writeAllPrefilterUniforms(ipDevice, prefBuf);
|
|
1431
|
-
const runRes = runIblPrecompute({
|
|
1432
|
-
device: ipDevice,
|
|
1433
|
-
equirectGpuTex,
|
|
1434
|
-
equirectView,
|
|
1435
|
-
cubeGpuTex: gpuTexture,
|
|
1436
|
-
cubeView,
|
|
1437
|
-
cubeFaceViews: faceViews,
|
|
1438
|
-
faceUniformsBuffer: faceBuf,
|
|
1439
|
-
prefilterUniformsBuffer: prefBuf,
|
|
1440
|
-
cubeVertexBuffer: cubeVertex,
|
|
1441
|
-
});
|
|
1442
|
-
if (!runRes.ok) {
|
|
1443
|
-
return recordFailed(
|
|
1444
|
-
makeAssetError({
|
|
1445
|
-
code: 'ibl-precompute-not-dispatched',
|
|
1446
|
-
expected: runRes.error.expected,
|
|
1447
|
-
hint: runRes.error.hint,
|
|
1448
|
-
}),
|
|
1449
|
-
);
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
1577
|
}
|
|
1454
1578
|
|
|
1455
1579
|
return ok(cubeHandle);
|
|
@@ -1461,116 +1585,88 @@ export class GpuResourceStore {
|
|
|
1461
1585
|
* `uploadMeshById` byte-for-byte. Returns the cached entry on success.
|
|
1462
1586
|
*/
|
|
1463
1587
|
private uploadMeshById(
|
|
1464
|
-
id: number,
|
|
1588
|
+
id: string | number,
|
|
1465
1589
|
mesh: TypesMeshAsset,
|
|
1466
1590
|
renderData: MeshRenderData,
|
|
1467
|
-
): Result<
|
|
1468
|
-
const device = this.gpuDevice
|
|
1591
|
+
): Result<MeshGpuHandles, RhiError> {
|
|
1592
|
+
const device = this.gpuDevice;
|
|
1469
1593
|
if (device === undefined) {
|
|
1470
1594
|
return err(
|
|
1471
1595
|
new RhiError({
|
|
1472
1596
|
code: 'rhi-not-available',
|
|
1473
|
-
expected: '
|
|
1597
|
+
expected: 'GpuResidencyCache.configureGpuDevice wired before mesh ensureResident',
|
|
1474
1598
|
hint: 'a mesh ensureResident ran before the GPU device was wired; call gpuStore.configureGpuDevice in createRenderer',
|
|
1475
1599
|
}),
|
|
1476
1600
|
);
|
|
1477
1601
|
}
|
|
1478
1602
|
|
|
1479
|
-
// biome-ignore lint/suspicious/noExplicitAny: union of shim/raw return shapes
|
|
1480
|
-
const unwrapBuffer = (r: any): any => {
|
|
1481
|
-
if (r === null || r === undefined) return undefined;
|
|
1482
|
-
if (typeof r === 'object' && 'ok' in r) return r.ok ? r.value : undefined;
|
|
1483
|
-
return r;
|
|
1484
|
-
};
|
|
1485
|
-
// biome-ignore lint/suspicious/noExplicitAny: union of shim/raw write return
|
|
1486
|
-
const writeOk = (r: any): boolean => {
|
|
1487
|
-
if (r === undefined) return true;
|
|
1488
|
-
if (typeof r === 'object' && 'ok' in r) return r.ok === true;
|
|
1489
|
-
return true;
|
|
1490
|
-
};
|
|
1491
|
-
|
|
1492
1603
|
const indices = mesh.indices;
|
|
1493
1604
|
const indexBytesUnpadded = indices === undefined ? 0 : indices.byteLength;
|
|
1494
1605
|
const indexBytes = renderData.indexByteLength;
|
|
1495
1606
|
|
|
1496
|
-
const
|
|
1607
|
+
const vboResult = device.createBuffer({
|
|
1497
1608
|
label: `mesh-${id}-vbo`,
|
|
1498
1609
|
size: renderData.vertexByteLength,
|
|
1499
1610
|
usage: renderData.vertexUsage,
|
|
1500
1611
|
mappedAtCreation: false,
|
|
1501
1612
|
});
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
return err(
|
|
1505
|
-
new RhiError({
|
|
1506
|
-
code: 'webgpu-runtime-error',
|
|
1507
|
-
expected: `device.createBuffer mesh-${id}-vbo to return a valid buffer`,
|
|
1508
|
-
hint: 'mesh vertex buffer allocation returned undefined',
|
|
1509
|
-
}),
|
|
1510
|
-
);
|
|
1511
|
-
}
|
|
1613
|
+
if (!vboResult.ok) return vboResult;
|
|
1614
|
+
const vbo = vboResult.value;
|
|
1512
1615
|
// Vertex-only mesh: skip the index buffer entirely. The indexed path below
|
|
1513
1616
|
// is unchanged byte-for-byte when `indices` is present.
|
|
1514
|
-
let ibo:
|
|
1617
|
+
let ibo: Buffer | null = null;
|
|
1515
1618
|
if (indices !== undefined) {
|
|
1516
|
-
const
|
|
1619
|
+
const iboResult = device.createBuffer({
|
|
1517
1620
|
label: `mesh-${id}-ibo`,
|
|
1518
1621
|
size: indexBytes,
|
|
1519
1622
|
usage: renderData.indexUsage,
|
|
1520
1623
|
mappedAtCreation: false,
|
|
1521
1624
|
});
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
return err(
|
|
1525
|
-
new RhiError({
|
|
1526
|
-
code: 'webgpu-runtime-error',
|
|
1527
|
-
expected: `device.createBuffer mesh-${id}-ibo to return a valid buffer`,
|
|
1528
|
-
hint: 'mesh index buffer allocation returned undefined',
|
|
1529
|
-
}),
|
|
1530
|
-
);
|
|
1531
|
-
}
|
|
1625
|
+
if (!iboResult.ok) return iboResult;
|
|
1626
|
+
ibo = iboResult.value;
|
|
1532
1627
|
}
|
|
1533
1628
|
|
|
1534
|
-
const
|
|
1535
|
-
if (!
|
|
1536
|
-
return err(
|
|
1537
|
-
new RhiError({
|
|
1538
|
-
code: 'webgpu-runtime-error',
|
|
1539
|
-
expected: `queue.writeBuffer mesh-${id}-vbo to succeed`,
|
|
1540
|
-
hint: 'mesh vertex buffer write failed',
|
|
1541
|
-
}),
|
|
1542
|
-
);
|
|
1543
|
-
}
|
|
1629
|
+
const vboWriteResult = device.queue.writeBuffer(vbo, 0, mesh.vertices);
|
|
1630
|
+
if (!vboWriteResult.ok) return err(vboWriteResult.error);
|
|
1544
1631
|
|
|
1545
1632
|
if (indices !== undefined) {
|
|
1546
1633
|
const indexSrc = new Uint8Array(indexBytes);
|
|
1547
1634
|
indexSrc.set(new Uint8Array(indices.buffer, indices.byteOffset, indexBytesUnpadded));
|
|
1548
|
-
|
|
1549
|
-
if (!writeOk(iboWriteRet)) {
|
|
1635
|
+
if (ibo === null)
|
|
1550
1636
|
return err(
|
|
1551
1637
|
new RhiError({
|
|
1552
1638
|
code: 'webgpu-runtime-error',
|
|
1553
|
-
expected: `
|
|
1554
|
-
hint: 'mesh index
|
|
1639
|
+
expected: `mesh-${id} index buffer to be allocated before its upload`,
|
|
1640
|
+
hint: 'the indexed mesh upload lost its RHI index-buffer owner',
|
|
1555
1641
|
}),
|
|
1556
1642
|
);
|
|
1557
|
-
|
|
1643
|
+
const iboWriteResult = device.queue.writeBuffer(ibo, 0, indexSrc);
|
|
1644
|
+
if (!iboWriteResult.ok) return err(iboWriteResult.error);
|
|
1558
1645
|
}
|
|
1559
1646
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1647
|
+
// The geometry projection already contains every packed attribute,
|
|
1648
|
+
// including skin and additional UV sets. Dividing by a second
|
|
1649
|
+
// attribute-derived count would double-count those fields and produce a
|
|
1650
|
+
// fractional vertex count for otherwise valid meshes.
|
|
1651
|
+
const extraUvSets = countExtraUvSets(mesh.attributes);
|
|
1652
|
+
const entry: MeshGpuHandles = {
|
|
1653
|
+
vertexBuffer: this.wrapBuf(vbo),
|
|
1654
|
+
indexBuffer: ibo === null ? null : this.wrapBuf(ibo),
|
|
1565
1655
|
vboBytes: renderData.vertexByteLength,
|
|
1566
1656
|
iboBytes: indices === undefined ? 0 : indexBytes,
|
|
1567
1657
|
indexCount: renderData.indexCount,
|
|
1568
1658
|
indexFormat: renderData.indexFormat,
|
|
1569
|
-
|
|
1659
|
+
layout: renderData.layoutProjection.attributes.some(
|
|
1660
|
+
(attribute) => attribute.key === 'skinIndex' || attribute.key === 'skinWeight',
|
|
1661
|
+
)
|
|
1662
|
+
? '18F'
|
|
1663
|
+
: '12F',
|
|
1664
|
+
layoutProjection: renderData.layoutProjection,
|
|
1665
|
+
uvSetCount: 1 + extraUvSets,
|
|
1666
|
+
vertexCount: mesh.vertices.byteLength / renderData.layoutProjection.arrayStride,
|
|
1570
1667
|
indexed: indices !== undefined,
|
|
1571
1668
|
topology: renderData.submeshes[0]?.topology ?? 'triangle-list',
|
|
1572
1669
|
submeshes: renderData.submeshes,
|
|
1573
|
-
layoutProjection,
|
|
1574
1670
|
};
|
|
1575
1671
|
this.meshGpuHandles.set(id, entry);
|
|
1576
1672
|
this.meshResidencyEpoch += 1;
|
|
@@ -1578,220 +1674,114 @@ export class GpuResourceStore {
|
|
|
1578
1674
|
}
|
|
1579
1675
|
|
|
1580
1676
|
/**
|
|
1581
|
-
* Update an existing
|
|
1582
|
-
*
|
|
1583
|
-
* or silently changes the draw contract.
|
|
1677
|
+
* Update an existing mesh's GPU buffer data in-place (or expand). The handle
|
|
1678
|
+
* must already be resident. Mirrors the pre-extraction `updateMeshById`.
|
|
1584
1679
|
*/
|
|
1585
1680
|
private updateMeshById(
|
|
1586
|
-
id: number,
|
|
1681
|
+
id: string | number,
|
|
1587
1682
|
newVertices: Float32Array,
|
|
1588
|
-
newIndices: Uint16Array
|
|
1683
|
+
newIndices: Uint16Array,
|
|
1589
1684
|
submeshes?: readonly Submesh[],
|
|
1590
|
-
):
|
|
1591
|
-
const device = this.gpuDevice
|
|
1592
|
-
if (device === undefined)
|
|
1593
|
-
return err(
|
|
1594
|
-
new RhiError({
|
|
1595
|
-
code: 'rhi-not-available',
|
|
1596
|
-
expected: 'GpuResourceStore.configureGpuDevice wired before mesh update',
|
|
1597
|
-
hint: 'configure the renderer GPU device before updating a mesh',
|
|
1598
|
-
}),
|
|
1599
|
-
);
|
|
1600
|
-
}
|
|
1685
|
+
): void {
|
|
1686
|
+
const device = this.gpuDevice;
|
|
1687
|
+
if (device === undefined) return;
|
|
1601
1688
|
const entry = this.meshGpuHandles.get(id);
|
|
1602
|
-
if (entry === undefined)
|
|
1603
|
-
return err(
|
|
1604
|
-
new MeshUpdateError({
|
|
1605
|
-
code: 'mesh-not-resident',
|
|
1606
|
-
detail: { handle: id },
|
|
1607
|
-
}),
|
|
1608
|
-
);
|
|
1609
|
-
}
|
|
1689
|
+
if (entry === undefined) return;
|
|
1610
1690
|
|
|
1611
|
-
const
|
|
1612
|
-
const
|
|
1613
|
-
const
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1691
|
+
const newVertexBytes = newVertices.byteLength;
|
|
1692
|
+
const indexBytesUnpadded = newIndices.byteLength;
|
|
1693
|
+
const newIndexBytes = ((indexBytesUnpadded + 3) >> 2) << 2;
|
|
1694
|
+
|
|
1695
|
+
// M-3 / w11: read allocation bytes from the entry's vboBytes / iboBytes
|
|
1696
|
+
// (tracked at upload time) instead of reaching into the opaque RHI Buffer
|
|
1697
|
+
// for a `.size` property -- the RHI Buffer interface is spec-aligned and
|
|
1698
|
+
// does NOT expose `.size`; allocation bytes remain derived cache facts.
|
|
1699
|
+
const existingIbo = entry.indexBuffer;
|
|
1700
|
+
const nextSubmeshes = submeshes ?? entry.submeshes;
|
|
1620
1701
|
if (
|
|
1621
|
-
|
|
1622
|
-
|
|
1702
|
+
existingIbo !== null &&
|
|
1703
|
+
newVertexBytes <= entry.vboBytes &&
|
|
1704
|
+
newIndexBytes <= entry.iboBytes
|
|
1623
1705
|
) {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
`${newVertices.byteLength} vertex bytes`,
|
|
1627
|
-
);
|
|
1628
|
-
}
|
|
1706
|
+
const vboWriteRet = device.queue.writeBuffer(entry.vertexBuffer.handle, 0, newVertices);
|
|
1707
|
+
if (!vboWriteRet.ok) return;
|
|
1629
1708
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
indexFormat
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
return
|
|
1709
|
+
const indexSrc = new Uint8Array(newIndexBytes);
|
|
1710
|
+
indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, indexBytesUnpadded));
|
|
1711
|
+
const iboWriteRet = device.queue.writeBuffer(existingIbo.handle, 0, indexSrc);
|
|
1712
|
+
if (!iboWriteRet.ok) return;
|
|
1713
|
+
|
|
1714
|
+
this.meshGpuHandles.set(id, {
|
|
1715
|
+
vertexBuffer: entry.vertexBuffer,
|
|
1716
|
+
indexBuffer: existingIbo,
|
|
1717
|
+
vboBytes: entry.vboBytes,
|
|
1718
|
+
iboBytes: entry.iboBytes,
|
|
1719
|
+
indexCount: newIndices.length,
|
|
1720
|
+
indexFormat: 'uint16',
|
|
1721
|
+
layout: '12F',
|
|
1722
|
+
layoutProjection: entry.layoutProjection,
|
|
1723
|
+
uvSetCount: entry.uvSetCount,
|
|
1724
|
+
vertexCount: newVertices.length / PROCEDURAL_FLOATS_PER_VERTEX,
|
|
1725
|
+
indexed: true,
|
|
1726
|
+
topology: entry.topology,
|
|
1727
|
+
submeshes: nextSubmeshes,
|
|
1728
|
+
});
|
|
1729
|
+
this.meshResidencyEpoch += 1;
|
|
1730
|
+
return;
|
|
1652
1731
|
}
|
|
1653
1732
|
|
|
1654
|
-
const
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1733
|
+
const vboResult = device.createBuffer({
|
|
1734
|
+
label: `mesh-${id}-vbo`,
|
|
1735
|
+
size: newVertexBytes,
|
|
1736
|
+
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1737
|
+
mappedAtCreation: false,
|
|
1738
|
+
});
|
|
1739
|
+
if (!vboResult.ok) return;
|
|
1740
|
+
const vbo = vboResult.value;
|
|
1741
|
+
const iboResult = device.createBuffer({
|
|
1742
|
+
label: `mesh-${id}-ibo`,
|
|
1743
|
+
size: newIndexBytes,
|
|
1744
|
+
usage: GPU_BUFFER_USAGE_INDEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1745
|
+
mappedAtCreation: false,
|
|
1746
|
+
});
|
|
1747
|
+
if (!iboResult.ok) return;
|
|
1748
|
+
const ibo = iboResult.value;
|
|
1658
1749
|
|
|
1659
|
-
const
|
|
1660
|
-
if (!
|
|
1661
|
-
if (newIndices !== undefined && entry.indexBuffer !== null) {
|
|
1662
|
-
const indexSrc = new Uint8Array(entry.iboBytes);
|
|
1663
|
-
indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, newIndices.byteLength));
|
|
1664
|
-
const indexWrite = device.queue.writeBuffer(entry.indexBuffer.handle, 0, indexSrc);
|
|
1665
|
-
if (!indexWrite.ok) return err(indexWrite.error);
|
|
1666
|
-
}
|
|
1667
|
-
this.meshResidencyEpoch += 1;
|
|
1668
|
-
return ok(undefined);
|
|
1669
|
-
}
|
|
1750
|
+
const vboWriteResult = device.queue.writeBuffer(vbo, 0, newVertices);
|
|
1751
|
+
if (!vboWriteResult.ok) return;
|
|
1670
1752
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
newIndices: Uint16Array | Uint32Array | undefined,
|
|
1680
|
-
submeshes?: readonly Submesh[],
|
|
1681
|
-
): Result<void, MeshUpdateErrorType | RhiError> {
|
|
1682
|
-
const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
|
|
1683
|
-
const entry = this.meshGpuHandles.get(id);
|
|
1684
|
-
if (device === undefined) {
|
|
1685
|
-
return err(
|
|
1686
|
-
new RhiError({
|
|
1687
|
-
code: 'rhi-not-available',
|
|
1688
|
-
expected: 'GpuResourceStore.configureGpuDevice wired before mesh replacement',
|
|
1689
|
-
hint: 'configure the renderer GPU device before replacing a mesh',
|
|
1690
|
-
}),
|
|
1691
|
-
);
|
|
1692
|
-
}
|
|
1693
|
-
if (entry === undefined) {
|
|
1694
|
-
return err(new MeshUpdateError({ code: 'mesh-not-resident', detail: { handle: id } }));
|
|
1695
|
-
}
|
|
1696
|
-
const strideBytes = meshStrideBytes(entry.layoutProjection);
|
|
1697
|
-
if (newVertices.byteLength === 0 || newVertices.byteLength % strideBytes !== 0) {
|
|
1698
|
-
return err(
|
|
1699
|
-
new MeshUpdateError({
|
|
1700
|
-
code: 'mesh-shape-mismatch',
|
|
1701
|
-
detail: {
|
|
1702
|
-
handle: id,
|
|
1703
|
-
expected: `vertex bytes divisible by ${strideBytes}`,
|
|
1704
|
-
actual: `${newVertices.byteLength} vertex bytes`,
|
|
1705
|
-
},
|
|
1706
|
-
}),
|
|
1707
|
-
);
|
|
1708
|
-
}
|
|
1709
|
-
const newVertexBytes = newVertices.byteLength;
|
|
1710
|
-
const newIndexBytes = newIndices === undefined ? 0 : ((newIndices.byteLength + 3) >> 2) << 2;
|
|
1711
|
-
const unwrapBuffer = (r: unknown): unknown => {
|
|
1712
|
-
if (r === null || r === undefined) return undefined;
|
|
1713
|
-
if (typeof r === 'object' && 'ok' in r) {
|
|
1714
|
-
return (r as { ok: boolean; value?: unknown }).ok
|
|
1715
|
-
? (r as unknown as { value: unknown }).value
|
|
1716
|
-
: undefined;
|
|
1717
|
-
}
|
|
1718
|
-
return r;
|
|
1719
|
-
};
|
|
1720
|
-
const vbo = unwrapBuffer(
|
|
1721
|
-
device.createBuffer({
|
|
1722
|
-
label: `mesh-${id}-vbo`,
|
|
1723
|
-
size: newVertexBytes,
|
|
1724
|
-
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1725
|
-
mappedAtCreation: false,
|
|
1726
|
-
}),
|
|
1727
|
-
);
|
|
1728
|
-
if (vbo === undefined) {
|
|
1729
|
-
return err(
|
|
1730
|
-
new RhiError({
|
|
1731
|
-
code: 'webgpu-runtime-error',
|
|
1732
|
-
expected: `device.createBuffer mesh-${id}-vbo to return a valid buffer`,
|
|
1733
|
-
hint: 'mesh replacement vertex buffer allocation failed',
|
|
1734
|
-
}),
|
|
1735
|
-
);
|
|
1736
|
-
}
|
|
1737
|
-
const ibo =
|
|
1738
|
-
newIndices === undefined
|
|
1739
|
-
? null
|
|
1740
|
-
: unwrapBuffer(
|
|
1741
|
-
device.createBuffer({
|
|
1742
|
-
label: `mesh-${id}-ibo`,
|
|
1743
|
-
size: newIndexBytes,
|
|
1744
|
-
usage: GPU_BUFFER_USAGE_INDEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1745
|
-
mappedAtCreation: false,
|
|
1746
|
-
}),
|
|
1747
|
-
);
|
|
1748
|
-
if (newIndices !== undefined && ibo === undefined) {
|
|
1749
|
-
this.destroyMeshBuffer(this.wrapBuf(vbo as Buffer));
|
|
1750
|
-
return err(
|
|
1751
|
-
new RhiError({
|
|
1752
|
-
code: 'webgpu-runtime-error',
|
|
1753
|
-
expected: `device.createBuffer mesh-${id}-ibo to return a valid buffer`,
|
|
1754
|
-
hint: 'mesh replacement index buffer allocation failed',
|
|
1755
|
-
}),
|
|
1756
|
-
);
|
|
1757
|
-
}
|
|
1758
|
-
const vertexBuffer = this.wrapBuf(vbo as Buffer);
|
|
1759
|
-
const indexBuffer = ibo === null ? null : this.wrapBuf(ibo as Buffer);
|
|
1760
|
-
const vertexWrite = device.queue.writeBuffer(vertexBuffer.handle, 0, newVertices);
|
|
1761
|
-
if (!vertexWrite.ok) {
|
|
1762
|
-
this.destroyMeshBuffer(vertexBuffer);
|
|
1763
|
-
if (indexBuffer !== null) this.destroyMeshBuffer(indexBuffer);
|
|
1764
|
-
return err(vertexWrite.error);
|
|
1765
|
-
}
|
|
1766
|
-
if (newIndices !== undefined && indexBuffer !== null) {
|
|
1767
|
-
const indexSrc = new Uint8Array(newIndexBytes);
|
|
1768
|
-
indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, newIndices.byteLength));
|
|
1769
|
-
const indexWrite = device.queue.writeBuffer(indexBuffer.handle, 0, indexSrc);
|
|
1770
|
-
if (!indexWrite.ok) {
|
|
1771
|
-
this.destroyMeshBuffer(vertexBuffer);
|
|
1772
|
-
this.destroyMeshBuffer(indexBuffer);
|
|
1773
|
-
return err(indexWrite.error);
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
const nextSubmeshes = submeshes ?? entry.submeshes;
|
|
1777
|
-
const nextEntry: MeshGpuEntry = {
|
|
1778
|
-
vertexBuffer,
|
|
1779
|
-
indexBuffer,
|
|
1753
|
+
const indexSrc = new Uint8Array(newIndexBytes);
|
|
1754
|
+
indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, indexBytesUnpadded));
|
|
1755
|
+
const iboWriteResult = device.queue.writeBuffer(ibo, 0, indexSrc);
|
|
1756
|
+
if (!iboWriteResult.ok) return;
|
|
1757
|
+
|
|
1758
|
+
this.meshGpuHandles.set(id, {
|
|
1759
|
+
vertexBuffer: this.wrapBuf(vbo),
|
|
1760
|
+
indexBuffer: this.wrapBuf(ibo),
|
|
1780
1761
|
vboBytes: newVertexBytes,
|
|
1781
1762
|
iboBytes: newIndexBytes,
|
|
1782
|
-
indexCount: newIndices
|
|
1783
|
-
indexFormat:
|
|
1763
|
+
indexCount: newIndices.length,
|
|
1764
|
+
indexFormat: 'uint16',
|
|
1765
|
+
layout: '12F',
|
|
1784
1766
|
layoutProjection: entry.layoutProjection,
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1767
|
+
uvSetCount: entry.uvSetCount,
|
|
1768
|
+
vertexCount: newVertices.length / PROCEDURAL_FLOATS_PER_VERTEX,
|
|
1769
|
+
indexed: true,
|
|
1770
|
+
topology: entry.topology,
|
|
1788
1771
|
submeshes: nextSubmeshes,
|
|
1789
|
-
};
|
|
1790
|
-
this.meshGpuHandles.set(id, nextEntry);
|
|
1772
|
+
});
|
|
1791
1773
|
this.meshResidencyEpoch += 1;
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1774
|
+
|
|
1775
|
+
// M-3 / w11: release the replaced buffers through their structured
|
|
1776
|
+
// GpuBuffer owner rather than reaching into opaque handles with a
|
|
1777
|
+
// backend-specific property.
|
|
1778
|
+
// `GpuBuffer.destroy()` routes through
|
|
1779
|
+
// `device.destroyBuffer(handle)` (RHI shim is the lifecycle SSOT;
|
|
1780
|
+
// architecture-principles §1 / charter §F1). Errors are swallowed here:
|
|
1781
|
+
// updateMeshById is the in-place reallocation path; the structured fail
|
|
1782
|
+
// surfaces on subsequent buffer use rather than blocking the resize.
|
|
1783
|
+
entry.vertexBuffer.destroy();
|
|
1784
|
+
if (existingIbo !== null) existingIbo.destroy();
|
|
1795
1785
|
}
|
|
1796
1786
|
|
|
1797
1787
|
/**
|
|
@@ -1801,101 +1791,12 @@ export class GpuResourceStore {
|
|
|
1801
1791
|
updateMesh(
|
|
1802
1792
|
handle: Handle<'MeshAsset', 'shared'>,
|
|
1803
1793
|
newVertices: Float32Array,
|
|
1804
|
-
newIndices: Uint16Array
|
|
1805
|
-
worldId: number = 0,
|
|
1794
|
+
newIndices: Uint16Array,
|
|
1795
|
+
worldId: World | number = 0,
|
|
1806
1796
|
submeshes?: readonly Submesh[],
|
|
1807
|
-
):
|
|
1808
|
-
const id = this.worldKey(handleSlot(handle), worldId);
|
|
1809
|
-
return this.updateMeshById(id, newVertices, newIndices, submeshes);
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
/** Explicit internal rebuild path; public callers should use a new asset handle. */
|
|
1813
|
-
replaceMesh(
|
|
1814
|
-
handle: Handle<'MeshAsset', 'shared'>,
|
|
1815
|
-
newVertices: Float32Array,
|
|
1816
|
-
newIndices: Uint16Array | Uint32Array | undefined,
|
|
1817
|
-
worldId: number = 0,
|
|
1818
|
-
submeshes?: readonly Submesh[],
|
|
1819
|
-
): Result<void, MeshUpdateErrorType | RhiError> {
|
|
1820
|
-
const id = this.worldKey(handleSlot(handle), worldId);
|
|
1821
|
-
return this.replaceMeshById(id, newVertices, newIndices, submeshes);
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
/** Upload a contiguous vertex range using the resident mesh stride. */
|
|
1825
|
-
updateMeshVertexRange(
|
|
1826
|
-
handle: Handle<'MeshAsset', 'shared'>,
|
|
1827
|
-
vertices: Float32Array,
|
|
1828
|
-
firstVertex: number,
|
|
1829
|
-
vertexCount: number,
|
|
1830
|
-
worldId: number = 0,
|
|
1831
|
-
): Result<void, MeshUpdateErrorType | RhiError> {
|
|
1797
|
+
): void {
|
|
1832
1798
|
const id = this.worldKey(handleSlot(handle), worldId);
|
|
1833
|
-
|
|
1834
|
-
if (device === undefined) {
|
|
1835
|
-
return err(
|
|
1836
|
-
new RhiError({
|
|
1837
|
-
code: 'rhi-not-available',
|
|
1838
|
-
expected: 'GpuResourceStore.configureGpuDevice wired before mesh range update',
|
|
1839
|
-
hint: 'configure the renderer GPU device before updating a mesh range',
|
|
1840
|
-
}),
|
|
1841
|
-
);
|
|
1842
|
-
}
|
|
1843
|
-
const entry = this.meshGpuHandles.get(id);
|
|
1844
|
-
if (entry === undefined) {
|
|
1845
|
-
return err(new MeshUpdateError({ code: 'mesh-not-resident', detail: { handle: id } }));
|
|
1846
|
-
}
|
|
1847
|
-
const strideBytes = meshStrideBytes(entry.layoutProjection);
|
|
1848
|
-
const invalidLocation = (
|
|
1849
|
-
reason: Exclude<MeshRangeInvalidReason, 'stride-mismatch'>,
|
|
1850
|
-
): Result<void, MeshUpdateErrorType | RhiError> =>
|
|
1851
|
-
err(
|
|
1852
|
-
new MeshUpdateError({
|
|
1853
|
-
code: 'mesh-range-invalid',
|
|
1854
|
-
detail: {
|
|
1855
|
-
handle: id,
|
|
1856
|
-
firstVertex,
|
|
1857
|
-
vertexCount,
|
|
1858
|
-
residentVertexCount: entry.vertexCount,
|
|
1859
|
-
reason,
|
|
1860
|
-
},
|
|
1861
|
-
}),
|
|
1862
|
-
);
|
|
1863
|
-
const invalidStride = (
|
|
1864
|
-
expectedByteLength: number,
|
|
1865
|
-
actualByteLength: number,
|
|
1866
|
-
): Result<void, MeshUpdateErrorType | RhiError> =>
|
|
1867
|
-
err(
|
|
1868
|
-
new MeshUpdateError({
|
|
1869
|
-
code: 'mesh-range-invalid',
|
|
1870
|
-
detail: {
|
|
1871
|
-
handle: id,
|
|
1872
|
-
firstVertex,
|
|
1873
|
-
vertexCount,
|
|
1874
|
-
residentVertexCount: entry.vertexCount,
|
|
1875
|
-
reason: 'stride-mismatch',
|
|
1876
|
-
expectedByteLength,
|
|
1877
|
-
actualByteLength,
|
|
1878
|
-
},
|
|
1879
|
-
}),
|
|
1880
|
-
);
|
|
1881
|
-
if (!Number.isInteger(firstVertex) || !Number.isInteger(vertexCount)) {
|
|
1882
|
-
return invalidLocation('non-integer');
|
|
1883
|
-
}
|
|
1884
|
-
if (firstVertex < 0 || vertexCount < 0) return invalidLocation('negative');
|
|
1885
|
-
if (vertexCount === 0) return invalidLocation('empty');
|
|
1886
|
-
if (firstVertex + vertexCount > entry.vertexCount) return invalidLocation('out-of-bounds');
|
|
1887
|
-
const expectedByteLength = vertexCount * strideBytes;
|
|
1888
|
-
if (vertices.byteLength !== expectedByteLength) {
|
|
1889
|
-
return invalidStride(expectedByteLength, vertices.byteLength);
|
|
1890
|
-
}
|
|
1891
|
-
const result = device.queue.writeBuffer(
|
|
1892
|
-
entry.vertexBuffer.handle,
|
|
1893
|
-
firstVertex * strideBytes,
|
|
1894
|
-
vertices,
|
|
1895
|
-
);
|
|
1896
|
-
if (!result.ok) return err(result.error);
|
|
1897
|
-
this.meshResidencyEpoch += 1;
|
|
1898
|
-
return ok(undefined);
|
|
1799
|
+
this.updateMeshById(id, newVertices, newIndices, submeshes);
|
|
1899
1800
|
}
|
|
1900
1801
|
}
|
|
1901
1802
|
|