@forgeax/engine-render 0.1.2
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/LICENSE +202 -0
- package/README.md +1019 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/analytic-sky-contract.test.d.ts +2 -0
- package/dist/__tests__/analytic-sky-contract.test.d.ts.map +1 -0
- package/dist/__tests__/analytic-sky-falsify.test.d.ts +2 -0
- package/dist/__tests__/analytic-sky-falsify.test.d.ts.map +1 -0
- package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts +2 -0
- package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts.map +1 -0
- package/dist/__tests__/authoring-path.unit.test.d.ts +2 -0
- package/dist/__tests__/authoring-path.unit.test.d.ts.map +1 -0
- package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts +2 -0
- package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts.map +1 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts +2 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts.map +1 -0
- package/dist/__tests__/callback-inference.test-d.d.ts +2 -0
- package/dist/__tests__/callback-inference.test-d.d.ts.map +1 -0
- package/dist/__tests__/camera-temporal.test-d.d.ts +2 -0
- package/dist/__tests__/camera-temporal.test-d.d.ts.map +1 -0
- package/dist/__tests__/camera-temporal.test.d.ts +2 -0
- package/dist/__tests__/camera-temporal.test.d.ts.map +1 -0
- package/dist/__tests__/cluster-binner.test.d.ts +2 -0
- package/dist/__tests__/cluster-binner.test.d.ts.map +1 -0
- package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/construct-renderer-identity.unit.test.d.ts +2 -0
- package/dist/__tests__/construct-renderer-identity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts +2 -0
- package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts.map +1 -0
- package/dist/__tests__/direct-light-snapshot.unit.test.d.ts +2 -0
- package/dist/__tests__/direct-light-snapshot.unit.test.d.ts.map +1 -0
- package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts +2 -0
- package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts.map +1 -0
- package/dist/__tests__/environment-inspection.test.d.ts +2 -0
- package/dist/__tests__/environment-inspection.test.d.ts.map +1 -0
- package/dist/__tests__/factory-contract.integration.test.d.ts +2 -0
- package/dist/__tests__/factory-contract.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fog-ai-user.fixture.d.ts +2 -0
- package/dist/__tests__/fog-ai-user.fixture.d.ts.map +1 -0
- package/dist/__tests__/fog-custom-render-feature.fixture.d.ts +16 -0
- package/dist/__tests__/fog-custom-render-feature.fixture.d.ts.map +1 -0
- package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fog-inspection.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-inspection.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fog-owner-state.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-owner-state.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fog-public-surface.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-public-surface.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fog-ray-contract.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-ray-contract.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fog-source-absence.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-source-absence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fog-temporal.integration.test.d.ts +2 -0
- package/dist/__tests__/fog-temporal.integration.test.d.ts.map +1 -0
- package/dist/__tests__/frame-chain.unit.test.d.ts +2 -0
- package/dist/__tests__/frame-chain.unit.test.d.ts.map +1 -0
- package/dist/__tests__/frame-observation.contract.test.d.ts +2 -0
- package/dist/__tests__/frame-observation.contract.test.d.ts.map +1 -0
- package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts +2 -0
- package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts +6 -0
- package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-production.integration.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-production.integration.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +12 -0
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-view.browser.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-view.browser.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-view.dawn.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-view.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-view.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-driven-view.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts +2 -0
- package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts +7 -0
- package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts +2 -0
- package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts +2 -0
- package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-scene-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene.dawn.test.d.ts +2 -0
- package/dist/__tests__/gpu-scene.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-scene.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-pipeline.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-pipeline.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts +2 -0
- package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts.map +1 -0
- package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts +2 -0
- package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts.map +1 -0
- package/dist/__tests__/injection-kind-owner.test-d.d.ts +2 -0
- package/dist/__tests__/injection-kind-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts +2 -0
- package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/light-buffer-layout.unit.test.d.ts +2 -0
- package/dist/__tests__/light-buffer-layout.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-contract-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/material-contract-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cooked-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cooked-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts +2 -0
- package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts.map +1 -0
- package/dist/__tests__/material-derived-payload.integration.test.d.ts +2 -0
- package/dist/__tests__/material-derived-payload.integration.test.d.ts.map +1 -0
- package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts +2 -0
- package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts.map +1 -0
- package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts +2 -0
- package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-stride-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/material-stride-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/materials.unit.test.d.ts +2 -0
- package/dist/__tests__/materials.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-material-bindings.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-material-bindings.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-stride-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-stride-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts +2 -0
- package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts +2 -0
- package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts.map +1 -0
- package/dist/__tests__/morph-visual-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/morph-visual-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/param-schema-projection-census.unit.test.d.ts +2 -0
- package/dist/__tests__/param-schema-projection-census.unit.test.d.ts.map +1 -0
- package/dist/__tests__/persistent-render-scene.unit.test.d.ts +2 -0
- package/dist/__tests__/persistent-render-scene.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-pass-trace.test.d.ts +2 -0
- package/dist/__tests__/pipeline-pass-trace.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-stage-owner.test-d.d.ts +2 -0
- package/dist/__tests__/pipeline-stage-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/plugin.test.d.ts +2 -0
- package/dist/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts +2 -0
- package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts +2 -0
- package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-store.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts +2 -0
- package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-default-off.test.d.ts +2 -0
- package/dist/__tests__/profiler-default-off.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-phase-catalog.test.d.ts +2 -0
- package/dist/__tests__/profiler-phase-catalog.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-phase-integration.test.d.ts +2 -0
- package/dist/__tests__/profiler-phase-integration.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-phase-token.test.d.ts +2 -0
- package/dist/__tests__/profiler-phase-token.test.d.ts.map +1 -0
- package/dist/__tests__/public-pipeline-id.test-d.d.ts +2 -0
- package/dist/__tests__/public-pipeline-id.test-d.d.ts.map +1 -0
- package/dist/__tests__/public-surface.test-d.d.ts +2 -0
- package/dist/__tests__/public-surface.test-d.d.ts.map +1 -0
- package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts +2 -0
- package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-dispatch-membership.unit.test.d.ts +2 -0
- package/dist/__tests__/render-dispatch-membership.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/render-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-error-detail-ssot.test-d.d.ts +2 -0
- package/dist/__tests__/render-error-detail-ssot.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts +2 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-assembly.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-assembly.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-construction.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-construction.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-context.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-context.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-contract.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-contract.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-docs.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-docs.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-errors.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts +2 -0
- package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/render-feature-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-order.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-order.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-public-surface.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-public-surface.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-retry.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-retry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-scheduling.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-scheduling.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-staging.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-staging.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-visibility-context.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-visibility-context.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-zero-work.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-zero-work.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-graph-compute-raster-gpu.d.ts +7 -0
- package/dist/__tests__/render-graph-compute-raster-gpu.d.ts.map +1 -0
- package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts +2 -0
- package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts.map +1 -0
- package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts +2 -0
- package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/render-graph-hzb-gpu.d.ts +7 -0
- package/dist/__tests__/render-graph-hzb-gpu.d.ts.map +1 -0
- package/dist/__tests__/render-graph-hzb.browser.test.d.ts +2 -0
- package/dist/__tests__/render-graph-hzb.browser.test.d.ts.map +1 -0
- package/dist/__tests__/render-graph-hzb.dawn.test.d.ts +2 -0
- package/dist/__tests__/render-graph-hzb.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/render-graph-primitives.unit.test.d.ts +2 -0
- package/dist/__tests__/render-graph-primitives.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts +12 -0
- package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts.map +1 -0
- package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts +2 -0
- package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts.map +1 -0
- package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts +2 -0
- package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/render-scene-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/render-scene-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-lifecycle.integration.test.d.ts +2 -0
- package/dist/__tests__/renderer-lifecycle.integration.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-render-scene-public.test-d.d.ts +2 -0
- package/dist/__tests__/renderer-render-scene-public.test-d.d.ts.map +1 -0
- package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts +2 -0
- package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts +2 -0
- package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts.map +1 -0
- package/dist/__tests__/shader-stage-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/shader-stage-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sun-snapshot-consumers.test.d.ts +2 -0
- package/dist/__tests__/sun-snapshot-consumers.test.d.ts.map +1 -0
- package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts +2 -0
- package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/surface-configuration.unit.test.d.ts +2 -0
- package/dist/__tests__/surface-configuration.unit.test.d.ts.map +1 -0
- package/dist/__tests__/taa-history-store.unit.test.d.ts +2 -0
- package/dist/__tests__/taa-history-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts +2 -0
- package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts.map +1 -0
- package/dist/__tests__/temporal-inspection.unit.test.d.ts +2 -0
- package/dist/__tests__/temporal-inspection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/texture-usage-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/texture-usage-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts +2 -0
- package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts.map +1 -0
- package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts +2 -0
- package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts.map +1 -0
- package/dist/__tests__/urp-pipeline.unit.test.d.ts +2 -0
- package/dist/__tests__/urp-pipeline.unit.test.d.ts.map +1 -0
- package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts +2 -0
- package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts.map +1 -0
- package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts +2 -0
- package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-diagnostics.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-diagnostics.integration.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-extract.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-extract.integration.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-extract.perf.test.d.ts +2 -0
- package/dist/__tests__/visibility-extract.perf.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-hidden-report.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-hidden-report.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-instances.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-instances.integration.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-multi-world.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-multi-world.integration.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-public-surface.test-d.d.ts +2 -0
- package/dist/__tests__/visibility-public-surface.test-d.d.ts.map +1 -0
- package/dist/__tests__/visibility-resolver.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-resolver.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/vocabulary.unit.test.d.ts +2 -0
- package/dist/__tests__/vocabulary.unit.test.d.ts.map +1 -0
- package/dist/assets/__tests__/asset-decoders.unit.test.d.ts +2 -0
- package/dist/assets/__tests__/asset-decoders.unit.test.d.ts.map +1 -0
- package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts +2 -0
- package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts.map +1 -0
- package/dist/assets/__tests__/render-asset-port.unit.test.d.ts +2 -0
- package/dist/assets/__tests__/render-asset-port.unit.test.d.ts.map +1 -0
- package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts +2 -0
- package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts.map +1 -0
- package/dist/assets/asset-decoders.d.ts +5 -0
- package/dist/assets/asset-decoders.d.ts.map +1 -0
- package/dist/assets/render-asset-port.d.ts +17 -0
- package/dist/assets/render-asset-port.d.ts.map +1 -0
- package/dist/assets/resolve-render-asset.d.ts +6 -0
- package/dist/assets/resolve-render-asset.d.ts.map +1 -0
- package/dist/authoring.d.ts +12 -0
- package/dist/authoring.d.ts.map +1 -0
- package/dist/authoring.mjs +5 -0
- package/dist/authoring.mjs.map +1 -0
- package/dist/chunk-33UF5SAO.mjs +7397 -0
- package/dist/chunk-33UF5SAO.mjs.map +1 -0
- package/dist/chunk-A5QCJSNL.mjs +29913 -0
- package/dist/chunk-A5QCJSNL.mjs.map +1 -0
- package/dist/chunk-JMSB7AR4.mjs +3 -0
- package/dist/chunk-JMSB7AR4.mjs.map +1 -0
- package/dist/chunk-LXNGO6AO.mjs +569 -0
- package/dist/chunk-LXNGO6AO.mjs.map +1 -0
- package/dist/chunk-QRXPSX4X.mjs +659 -0
- package/dist/chunk-QRXPSX4X.mjs.map +1 -0
- package/dist/chunk-YAKEQPWO.mjs +1439 -0
- package/dist/chunk-YAKEQPWO.mjs.map +1 -0
- package/dist/cluster-binner.d.ts +152 -0
- package/dist/cluster-binner.d.ts.map +1 -0
- package/dist/components/__tests__/atmosphere-red.test.d.ts +2 -0
- package/dist/components/__tests__/atmosphere-red.test.d.ts.map +1 -0
- package/dist/components/__tests__/atmosphere.test.d.ts +2 -0
- package/dist/components/__tests__/atmosphere.test.d.ts.map +1 -0
- package/dist/components/__tests__/fog.test-d.d.ts +2 -0
- package/dist/components/__tests__/fog.test-d.d.ts.map +1 -0
- package/dist/components/__tests__/fog.test.d.ts +2 -0
- package/dist/components/__tests__/fog.test.d.ts.map +1 -0
- package/dist/components/__tests__/schema-parity.unit.test.d.ts +2 -0
- package/dist/components/__tests__/schema-parity.unit.test.d.ts.map +1 -0
- package/dist/components/__tests__/visibility-schema.unit.test.d.ts +2 -0
- package/dist/components/__tests__/visibility-schema.unit.test.d.ts.map +1 -0
- package/dist/components/atmosphere.d.ts +18 -0
- package/dist/components/atmosphere.d.ts.map +1 -0
- package/dist/components/camera.d.ts +311 -0
- package/dist/components/camera.d.ts.map +1 -0
- package/dist/components/directional-light.d.ts +76 -0
- package/dist/components/directional-light.d.ts.map +1 -0
- package/dist/components/environment.d.ts +116 -0
- package/dist/components/environment.d.ts.map +1 -0
- package/dist/components/glyph-text.d.ts +26 -0
- package/dist/components/glyph-text.d.ts.map +1 -0
- package/dist/components/index.d.ts +22 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/instances.d.ts +41 -0
- package/dist/components/instances.d.ts.map +1 -0
- package/dist/components/layer.d.ts +34 -0
- package/dist/components/layer.d.ts.map +1 -0
- package/dist/components/light-helpers.d.ts +53 -0
- package/dist/components/light-helpers.d.ts.map +1 -0
- package/dist/components/mesh-filter.d.ts +24 -0
- package/dist/components/mesh-filter.d.ts.map +1 -0
- package/dist/components/mesh-renderer.d.ts +33 -0
- package/dist/components/mesh-renderer.d.ts.map +1 -0
- package/dist/components/point-light-shadow.d.ts +27 -0
- package/dist/components/point-light-shadow.d.ts.map +1 -0
- package/dist/components/point-light.d.ts +35 -0
- package/dist/components/point-light.d.ts.map +1 -0
- package/dist/components/post-process-params.d.ts +5 -0
- package/dist/components/post-process-params.d.ts.map +1 -0
- package/dist/components/scene-instance.d.ts +140 -0
- package/dist/components/scene-instance.d.ts.map +1 -0
- package/dist/components/sort-key.d.ts +44 -0
- package/dist/components/sort-key.d.ts.map +1 -0
- package/dist/components/spot-light.d.ts +81 -0
- package/dist/components/spot-light.d.ts.map +1 -0
- package/dist/components/sprite-animation.d.ts +75 -0
- package/dist/components/sprite-animation.d.ts.map +1 -0
- package/dist/components/sprite-instances.d.ts +51 -0
- package/dist/components/sprite-instances.d.ts.map +1 -0
- package/dist/components/sprite-playback-mode.d.ts +37 -0
- package/dist/components/sprite-playback-mode.d.ts.map +1 -0
- package/dist/components/sprite-region-override.d.ts +47 -0
- package/dist/components/sprite-region-override.d.ts.map +1 -0
- package/dist/components/tile-layer.d.ts +101 -0
- package/dist/components/tile-layer.d.ts.map +1 -0
- package/dist/components/tilemap.d.ts +25 -0
- package/dist/components/tilemap.d.ts.map +1 -0
- package/dist/components/visibility.d.ts +22 -0
- package/dist/components/visibility.d.ts.map +1 -0
- package/dist/construct-renderer.d.ts +2 -0
- package/dist/construct-renderer.d.ts.map +1 -0
- package/dist/construct-renderer.mjs +6 -0
- package/dist/construct-renderer.mjs.map +1 -0
- package/dist/debug-draw-glue.d.ts +56 -0
- package/dist/debug-draw-glue.d.ts.map +1 -0
- package/dist/deferred-lighting.d.ts +13 -0
- package/dist/deferred-lighting.d.ts.map +1 -0
- package/dist/dynamic-texture-store.d.ts +42 -0
- package/dist/dynamic-texture-store.d.ts.map +1 -0
- package/dist/engine-metrics.d.ts +23 -0
- package/dist/engine-metrics.d.ts.map +1 -0
- package/dist/environment/__tests__/failure-injection.test.d.ts +2 -0
- package/dist/environment/__tests__/failure-injection.test.d.ts.map +1 -0
- package/dist/environment/__tests__/generation-red.test.d.ts +2 -0
- package/dist/environment/__tests__/generation-red.test.d.ts.map +1 -0
- package/dist/environment/__tests__/generation.test.d.ts +2 -0
- package/dist/environment/__tests__/generation.test.d.ts.map +1 -0
- package/dist/environment/__tests__/graph-transaction.test.d.ts +2 -0
- package/dist/environment/__tests__/graph-transaction.test.d.ts.map +1 -0
- package/dist/environment/__tests__/inspection-red.test.d.ts +2 -0
- package/dist/environment/__tests__/inspection-red.test.d.ts.map +1 -0
- package/dist/environment/__tests__/lifecycle-evidence.test.d.ts +2 -0
- package/dist/environment/__tests__/lifecycle-evidence.test.d.ts.map +1 -0
- package/dist/environment/__tests__/pipeline-topology.test.d.ts +2 -0
- package/dist/environment/__tests__/pipeline-topology.test.d.ts.map +1 -0
- package/dist/environment/__tests__/readiness.test.d.ts +2 -0
- package/dist/environment/__tests__/readiness.test.d.ts.map +1 -0
- package/dist/environment/__tests__/recovery.test.d.ts +2 -0
- package/dist/environment/__tests__/recovery.test.d.ts.map +1 -0
- package/dist/environment/__tests__/resize-dispose.test.d.ts +2 -0
- package/dist/environment/__tests__/resize-dispose.test.d.ts.map +1 -0
- package/dist/environment/__tests__/signature-red.test.d.ts +2 -0
- package/dist/environment/__tests__/signature-red.test.d.ts.map +1 -0
- package/dist/environment/__tests__/signature.test.d.ts +2 -0
- package/dist/environment/__tests__/signature.test.d.ts.map +1 -0
- package/dist/environment/descriptors.d.ts +2 -0
- package/dist/environment/descriptors.d.ts.map +1 -0
- package/dist/environment/extracted-environment.d.ts +53 -0
- package/dist/environment/extracted-environment.d.ts.map +1 -0
- package/dist/environment/generation.d.ts +62 -0
- package/dist/environment/generation.d.ts.map +1 -0
- package/dist/environment/gpu-bundle.d.ts +23 -0
- package/dist/environment/gpu-bundle.d.ts.map +1 -0
- package/dist/environment/graph.d.ts +108 -0
- package/dist/environment/graph.d.ts.map +1 -0
- package/dist/environment/inspection.d.ts +48 -0
- package/dist/environment/inspection.d.ts.map +1 -0
- package/dist/environment/lifecycle.d.ts +22 -0
- package/dist/environment/lifecycle.d.ts.map +1 -0
- package/dist/environment/readiness.d.ts +29 -0
- package/dist/environment/readiness.d.ts.map +1 -0
- package/dist/environment/signature.d.ts +70 -0
- package/dist/environment/signature.d.ts.map +1 -0
- package/dist/errors/ecs-validation.d.ts +3 -0
- package/dist/errors/ecs-validation.d.ts.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/recover.d.ts +78 -0
- package/dist/errors/recover.d.ts.map +1 -0
- package/dist/errors/render.d.ts +633 -0
- package/dist/errors/render.d.ts.map +1 -0
- package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts +2 -0
- package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts.map +1 -0
- package/dist/extract/__tests__/environment-source.test.d.ts +2 -0
- package/dist/extract/__tests__/environment-source.test.d.ts.map +1 -0
- package/dist/extract/__tests__/schema-readers.unit.test.d.ts +2 -0
- package/dist/extract/__tests__/schema-readers.unit.test.d.ts.map +1 -0
- package/dist/extract/directional-csm.d.ts +129 -0
- package/dist/extract/directional-csm.d.ts.map +1 -0
- package/dist/extract/index.d.ts +5 -0
- package/dist/extract/index.d.ts.map +1 -0
- package/dist/extract/material-interface.d.ts +56 -0
- package/dist/extract/material-interface.d.ts.map +1 -0
- package/dist/extract/schema-readers.d.ts +20 -0
- package/dist/extract/schema-readers.d.ts.map +1 -0
- package/dist/extract/visibility.d.ts +26 -0
- package/dist/extract/visibility.d.ts.map +1 -0
- package/dist/features/graph-contribution.d.ts +64 -0
- package/dist/features/graph-contribution.d.ts.map +1 -0
- package/dist/features/host.d.ts +100 -0
- package/dist/features/host.d.ts.map +1 -0
- package/dist/features/index.d.ts +21 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/morph/index.d.ts +3 -0
- package/dist/features/morph/index.d.ts.map +1 -0
- package/dist/features/morph/morph-bounds.d.ts +42 -0
- package/dist/features/morph/morph-bounds.d.ts.map +1 -0
- package/dist/features/morph/morph-feature.d.ts +45 -0
- package/dist/features/morph/morph-feature.d.ts.map +1 -0
- package/dist/features/prepared-gpu-work.d.ts +113 -0
- package/dist/features/prepared-gpu-work.d.ts.map +1 -0
- package/dist/features/prepared-graphics-store.d.ts +65 -0
- package/dist/features/prepared-graphics-store.d.ts.map +1 -0
- package/dist/features/prepared-graphics.d.ts +225 -0
- package/dist/features/prepared-graphics.d.ts.map +1 -0
- package/dist/features/render-graph-compute.d.ts +13 -0
- package/dist/features/render-graph-compute.d.ts.map +1 -0
- package/dist/features/render-graph-contribution.d.ts +15 -0
- package/dist/features/render-graph-contribution.d.ts.map +1 -0
- package/dist/features/render-graph-raster.d.ts +34 -0
- package/dist/features/render-graph-raster.d.ts.map +1 -0
- package/dist/features/render-graph-resources.d.ts +11 -0
- package/dist/features/render-graph-resources.d.ts.map +1 -0
- package/dist/features/scene-temporal.d.ts +45 -0
- package/dist/features/scene-temporal.d.ts.map +1 -0
- package/dist/features/taa-resolve.d.ts +21 -0
- package/dist/features/taa-resolve.d.ts.map +1 -0
- package/dist/features/targets.d.ts +32 -0
- package/dist/features/targets.d.ts.map +1 -0
- package/dist/features/types.d.ts +247 -0
- package/dist/features/types.d.ts.map +1 -0
- package/dist/frame-chain.d.ts +14 -0
- package/dist/frame-chain.d.ts.map +1 -0
- package/dist/fullscreen-post-process-pass.d.ts +178 -0
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -0
- package/dist/glyph-text-layout-system.d.ts +18 -0
- package/dist/glyph-text-layout-system.d.ts.map +1 -0
- package/dist/gpu-driven/batch-topology.d.ts +124 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +150 -0
- package/dist/gpu-driven/production-raster.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +141 -0
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -0
- package/dist/gpu-driven/view-reference.d.ts +15 -0
- package/dist/gpu-driven/view-reference.d.ts.map +1 -0
- package/dist/gpu-resource-store.d.ts +294 -0
- package/dist/gpu-resource-store.d.ts.map +1 -0
- package/dist/gpu-resource.d.ts +73 -0
- package/dist/gpu-resource.d.ts.map +1 -0
- package/dist/gpu-scene-schema.d.ts +171 -0
- package/dist/gpu-scene-schema.d.ts.map +1 -0
- package/dist/gpu-scene.d.ts +81 -0
- package/dist/gpu-scene.d.ts.map +1 -0
- package/dist/gpu-stage.d.ts +4 -0
- package/dist/gpu-stage.d.ts.map +1 -0
- package/dist/gpu-texture-usage.d.ts +6 -0
- package/dist/gpu-texture-usage.d.ts.map +1 -0
- package/dist/gpu-usage.d.ts +9 -0
- package/dist/gpu-usage.d.ts.map +1 -0
- package/dist/hdrp-buffers.d.ts +213 -0
- package/dist/hdrp-buffers.d.ts.map +1 -0
- package/dist/hdrp-cluster-membership.d.ts +2 -0
- package/dist/hdrp-cluster-membership.d.ts.map +1 -0
- package/dist/hdrp-pipeline.d.ts +36 -0
- package/dist/hdrp-pipeline.d.ts.map +1 -0
- package/dist/ibl/IblPipelineCache.d.ts +274 -0
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -0
- package/dist/ibl/cubemap-projection.d.ts +23 -0
- package/dist/ibl/cubemap-projection.d.ts.map +1 -0
- package/dist/ibl/face-uniforms.d.ts +65 -0
- package/dist/ibl/face-uniforms.d.ts.map +1 -0
- package/dist/ibl/skylight-bind-group.d.ts +130 -0
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/index.mjs.map +1 -0
- package/dist/instance-buffer-cache.d.ts +59 -0
- package/dist/instance-buffer-cache.d.ts.map +1 -0
- package/dist/internal.d.ts +74 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.mjs +45 -0
- package/dist/internal.mjs.map +1 -0
- package/dist/lifecycle.d.ts +28 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/light-buffer-layout.d.ts +164 -0
- package/dist/light-buffer-layout.d.ts.map +1 -0
- package/dist/materials.d.ts +50 -0
- package/dist/materials.d.ts.map +1 -0
- package/dist/mesh-material-bindings.d.ts +44 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -0
- package/dist/mesh-update-errors.d.ts +71 -0
- package/dist/mesh-update-errors.d.ts.map +1 -0
- package/dist/mipmap-generator.d.ts +39 -0
- package/dist/mipmap-generator.d.ts.map +1 -0
- package/dist/param-schema-projection-census.d.ts +18 -0
- package/dist/param-schema-projection-census.d.ts.map +1 -0
- package/dist/pbr-pipeline.d.ts +253 -0
- package/dist/pbr-pipeline.d.ts.map +1 -0
- package/dist/persistent-render-scene.d.ts +158 -0
- package/dist/persistent-render-scene.d.ts.map +1 -0
- package/dist/pipeline/index.d.ts +11 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline-builder.d.ts +122 -0
- package/dist/pipeline-builder.d.ts.map +1 -0
- package/dist/pipeline-errors.d.ts +110 -0
- package/dist/pipeline-errors.d.ts.map +1 -0
- package/dist/pipeline-spec.d.ts +506 -0
- package/dist/pipeline-spec.d.ts.map +1 -0
- package/dist/plugin.d.ts +17 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/post-process-errors.d.ts +182 -0
- package/dist/post-process-errors.d.ts.map +1 -0
- package/dist/prepare/__tests__/schema-readers.unit.test.d.ts +2 -0
- package/dist/prepare/__tests__/schema-readers.unit.test.d.ts.map +1 -0
- package/dist/prepare/index.d.ts +4 -0
- package/dist/prepare/index.d.ts.map +1 -0
- package/dist/prepare/prepared-graphics-resolver.d.ts +62 -0
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -0
- package/dist/prepare/schema-readers.d.ts +11 -0
- package/dist/prepare/schema-readers.d.ts.map +1 -0
- package/dist/record/__tests__/environment-record.test.d.ts +2 -0
- package/dist/record/__tests__/environment-record.test.d.ts.map +1 -0
- package/dist/record/__tests__/frame-targets.unit.test.d.ts +2 -0
- package/dist/record/__tests__/frame-targets.unit.test.d.ts.map +1 -0
- package/dist/record/__tests__/material-bg-cache.unit.test.d.ts +2 -0
- package/dist/record/__tests__/material-bg-cache.unit.test.d.ts.map +1 -0
- package/dist/record/__tests__/material-slot-plan.unit.test.d.ts +2 -0
- package/dist/record/__tests__/material-slot-plan.unit.test.d.ts.map +1 -0
- package/dist/record/__tests__/membership-timing.unit.test.d.ts +2 -0
- package/dist/record/__tests__/membership-timing.unit.test.d.ts.map +1 -0
- package/dist/record/__tests__/render-submission.integration.test.d.ts +2 -0
- package/dist/record/__tests__/render-submission.integration.test.d.ts.map +1 -0
- package/dist/record/__tests__/render-submission.perf.test.d.ts +2 -0
- package/dist/record/__tests__/render-submission.perf.test.d.ts.map +1 -0
- package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts +2 -0
- package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts.map +1 -0
- package/dist/record/__tests__/video-texture-view.unit.test.d.ts +2 -0
- package/dist/record/__tests__/video-texture-view.unit.test.d.ts.map +1 -0
- package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts +2 -0
- package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +187 -0
- package/dist/record/frame-lighting.d.ts.map +1 -0
- package/dist/record/frame-observation.d.ts +38 -0
- package/dist/record/frame-observation.d.ts.map +1 -0
- package/dist/record/frame-snapshot.d.ts +429 -0
- package/dist/record/frame-snapshot.d.ts.map +1 -0
- package/dist/record/frame-targets.d.ts +53 -0
- package/dist/record/frame-targets.d.ts.map +1 -0
- package/dist/record/frame.d.ts +59 -0
- package/dist/record/frame.d.ts.map +1 -0
- package/dist/record/helpers.d.ts +101 -0
- package/dist/record/helpers.d.ts.map +1 -0
- package/dist/record/index.d.ts +15 -0
- package/dist/record/index.d.ts.map +1 -0
- package/dist/record/main-pass-geometry.d.ts +20 -0
- package/dist/record/main-pass-geometry.d.ts.map +1 -0
- package/dist/record/main-pass-material.d.ts +173 -0
- package/dist/record/main-pass-material.d.ts.map +1 -0
- package/dist/record/main-pass-skin.d.ts +46 -0
- package/dist/record/main-pass-skin.d.ts.map +1 -0
- package/dist/record/main-pass-sprite-draws.d.ts +18 -0
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -0
- package/dist/record/main-pass-sprite.d.ts +69 -0
- package/dist/record/main-pass-sprite.d.ts.map +1 -0
- package/dist/record/main-pass.d.ts +20 -0
- package/dist/record/main-pass.d.ts.map +1 -0
- package/dist/record/material-payload.d.ts +43 -0
- package/dist/record/material-payload.d.ts.map +1 -0
- package/dist/record/material-slot-plan.d.ts +26 -0
- package/dist/record/material-slot-plan.d.ts.map +1 -0
- package/dist/record/membership-timing.d.ts +232 -0
- package/dist/record/membership-timing.d.ts.map +1 -0
- package/dist/record/mesh-ssbo.d.ts +173 -0
- package/dist/record/mesh-ssbo.d.ts.map +1 -0
- package/dist/record/shadow-caster-uniform.d.ts +8 -0
- package/dist/record/shadow-caster-uniform.d.ts.map +1 -0
- package/dist/record/shadow-pass.d.ts +125 -0
- package/dist/record/shadow-pass.d.ts.map +1 -0
- package/dist/record/shadow-submit-mode.d.ts +66 -0
- package/dist/record/shadow-submit-mode.d.ts.map +1 -0
- package/dist/record/skybox-post-pass.d.ts +33 -0
- package/dist/record/skybox-post-pass.d.ts.map +1 -0
- package/dist/record/typed-frame-graph.d.ts +13 -0
- package/dist/record/typed-frame-graph.d.ts.map +1 -0
- package/dist/record/view-ubo.d.ts +39 -0
- package/dist/record/view-ubo.d.ts.map +1 -0
- package/dist/render-data.d.ts +143 -0
- package/dist/render-data.d.ts.map +1 -0
- package/dist/render-dispatch-membership.d.ts +47 -0
- package/dist/render-dispatch-membership.d.ts.map +1 -0
- package/dist/render-graph-primitives.d.ts +424 -0
- package/dist/render-graph-primitives.d.ts.map +1 -0
- package/dist/render-pipeline-context.d.ts +289 -0
- package/dist/render-pipeline-context.d.ts.map +1 -0
- package/dist/render-pipeline-target.d.ts +16 -0
- package/dist/render-pipeline-target.d.ts.map +1 -0
- package/dist/render-pipeline.d.ts +149 -0
- package/dist/render-pipeline.d.ts.map +1 -0
- package/dist/render-scene-projection.d.ts +174 -0
- package/dist/render-scene-projection.d.ts.map +1 -0
- package/dist/render-system-extract.d.ts +1096 -0
- package/dist/render-system-extract.d.ts.map +1 -0
- package/dist/render-system-fold.d.ts +266 -0
- package/dist/render-system-fold.d.ts.map +1 -0
- package/dist/render-system.d.ts +814 -0
- package/dist/render-system.d.ts.map +1 -0
- package/dist/renderer/asset-service.d.ts +10 -0
- package/dist/renderer/asset-service.d.ts.map +1 -0
- package/dist/renderer/backend-selection.d.ts +26 -0
- package/dist/renderer/backend-selection.d.ts.map +1 -0
- package/dist/renderer/environment-classify.d.ts +6 -0
- package/dist/renderer/environment-classify.d.ts.map +1 -0
- package/dist/renderer/environment-error.d.ts +13 -0
- package/dist/renderer/environment-error.d.ts.map +1 -0
- package/dist/renderer/material/assembly.d.ts +22 -0
- package/dist/renderer/material/assembly.d.ts.map +1 -0
- package/dist/renderer/material/pipeline-projection.d.ts +91 -0
- package/dist/renderer/material/pipeline-projection.d.ts.map +1 -0
- package/dist/renderer/renderer-factory.d.ts +283 -0
- package/dist/renderer/renderer-factory.d.ts.map +1 -0
- package/dist/renderer.d.ts +771 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/scene-instances/post-spawn-resolve-joints.d.ts +33 -0
- package/dist/scene-instances/post-spawn-resolve-joints.d.ts.map +1 -0
- package/dist/scene-utils/collect-subtree.d.ts +11 -0
- package/dist/scene-utils/collect-subtree.d.ts.map +1 -0
- package/dist/shadow-atlas.d.ts +117 -0
- package/dist/shadow-atlas.d.ts.map +1 -0
- package/dist/ssao-buffers.d.ts +31 -0
- package/dist/ssao-buffers.d.ts.map +1 -0
- package/dist/ssao-config.d.ts +28 -0
- package/dist/ssao-config.d.ts.map +1 -0
- package/dist/ssao-data.d.ts +34 -0
- package/dist/ssao-data.d.ts.map +1 -0
- package/dist/surface-format.d.ts +10 -0
- package/dist/surface-format.d.ts.map +1 -0
- package/dist/systems/active-camera.d.ts +53 -0
- package/dist/systems/active-camera.d.ts.map +1 -0
- package/dist/systems/pass-selector.d.ts +24 -0
- package/dist/systems/pass-selector.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +51 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -0
- package/dist/systems/transparent-sort-config.d.ts +91 -0
- package/dist/systems/transparent-sort-config.d.ts.map +1 -0
- package/dist/taa-history-store.d.ts +88 -0
- package/dist/taa-history-store.d.ts.map +1 -0
- package/dist/temporal-coverage.d.ts +35 -0
- package/dist/temporal-coverage.d.ts.map +1 -0
- package/dist/temporal-frame-transaction.d.ts +92 -0
- package/dist/temporal-frame-transaction.d.ts.map +1 -0
- package/dist/temporal-inspection.d.ts +57 -0
- package/dist/temporal-inspection.d.ts.map +1 -0
- package/dist/temporal-view.d.ts +43 -0
- package/dist/temporal-view.d.ts.map +1 -0
- package/dist/tilemap/resolve-tileset-runtime.d.ts +23 -0
- package/dist/tilemap/resolve-tileset-runtime.d.ts.map +1 -0
- package/dist/tilemap-chunk-extract-system.d.ts +142 -0
- package/dist/tilemap-chunk-extract-system.d.ts.map +1 -0
- package/dist/typed-render-graph-primitives.d.ts +59 -0
- package/dist/typed-render-graph-primitives.d.ts.map +1 -0
- package/dist/typed-shadow-passes.d.ts +11 -0
- package/dist/typed-shadow-passes.d.ts.map +1 -0
- package/dist/urp-pipeline.d.ts +4 -0
- package/dist/urp-pipeline.d.ts.map +1 -0
- package/package.json +94 -0
- package/src/__tests__/analytic-sky-contract.test.ts +27 -0
- package/src/__tests__/analytic-sky-falsify-carriers.mjs +43 -0
- package/src/__tests__/analytic-sky-falsify.test.ts +94 -0
- package/src/__tests__/assets-runtime-handoff.integration.test.ts +36 -0
- package/src/__tests__/authoring-path.unit.test.ts +14 -0
- package/src/__tests__/backend-selection-rhi-capture.test-d.ts +21 -0
- package/src/__tests__/backend-selection.integration.test.ts +11 -0
- package/src/__tests__/callback-inference.test-d.ts +5 -0
- package/src/__tests__/camera-temporal.test-d.ts +8 -0
- package/src/__tests__/camera-temporal.test.ts +188 -0
- package/src/__tests__/cluster-binner.test.ts +272 -0
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +17 -0
- package/src/__tests__/construct-renderer-identity.unit.test.ts +10 -0
- package/src/__tests__/direct-extract-consumer-contract.test-d.ts +34 -0
- package/src/__tests__/direct-light-snapshot.unit.test.ts +71 -0
- package/src/__tests__/directional-shadow-probe-atlas-layout.unit.test.ts +18 -0
- package/src/__tests__/environment-inspection.test.ts +45 -0
- package/src/__tests__/factory-contract.integration.test.ts +331 -0
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +18 -0
- package/src/__tests__/fog-ai-user.fixture.ts +350 -0
- package/src/__tests__/fog-custom-render-feature.fixture.ts +33 -0
- package/src/__tests__/fog-custom-render-feature.integration.test.ts +76 -0
- package/src/__tests__/fog-inspection.integration.test.ts +127 -0
- package/src/__tests__/fog-owner-state.integration.test.ts +112 -0
- package/src/__tests__/fog-pipeline-topology.integration.test.ts +93 -0
- package/src/__tests__/fog-public-surface.integration.test.ts +60 -0
- package/src/__tests__/fog-ray-contract.integration.test.ts +73 -0
- package/src/__tests__/fog-source-absence.integration.test.ts +51 -0
- package/src/__tests__/fog-temporal.integration.test.ts +115 -0
- package/src/__tests__/frame-chain.unit.test.ts +26 -0
- package/src/__tests__/frame-observation.contract.test.ts +103 -0
- package/src/__tests__/glyph-text-layout-recovery.unit.test.ts +172 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +347 -0
- package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +12 -0
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +16 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +609 -0
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +90 -0
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +192 -0
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +128 -0
- package/src/__tests__/gpu-driven-view.browser.test.ts +19 -0
- package/src/__tests__/gpu-driven-view.dawn.test.ts +17 -0
- package/src/__tests__/gpu-driven-view.unit.test.ts +455 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +59 -0
- package/src/__tests__/gpu-scene-field-type-owner.test-d.ts +27 -0
- package/src/__tests__/gpu-scene-render-graph-gpu.ts +309 -0
- package/src/__tests__/gpu-scene-render-graph.browser.test.ts +19 -0
- package/src/__tests__/gpu-scene-render-graph.dawn.test.ts +19 -0
- package/src/__tests__/gpu-scene-schema.unit.test.ts +16 -0
- package/src/__tests__/gpu-scene.dawn.test.ts +209 -0
- package/src/__tests__/gpu-scene.unit.test.ts +161 -0
- package/src/__tests__/hdrp-pipeline.unit.test.ts +100 -0
- package/src/__tests__/ibl/ibl-fallback.unit.test.ts +23 -0
- package/src/__tests__/infinite-grid-no-vertex-probe.unit.test.ts +124 -0
- package/src/__tests__/injection-kind-owner.test-d.ts +14 -0
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +719 -0
- package/src/__tests__/light-buffer-layout.unit.test.ts +85 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +24 -0
- package/src/__tests__/material-cooked-projection.unit.test.ts +177 -0
- package/src/__tests__/material-derived-error-stop-draw.integration.test.ts +69 -0
- package/src/__tests__/material-derived-payload.integration.test.ts +164 -0
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +58 -0
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +113 -0
- package/src/__tests__/material-stride-surface.unit.test.ts +32 -0
- package/src/__tests__/materials.unit.test.ts +25 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +107 -0
- package/src/__tests__/mesh-material-bindings.unit.test.ts +89 -0
- package/src/__tests__/mesh-stride-surface.unit.test.ts +16 -0
- package/src/__tests__/morph-culling-reentry.dawn.test.ts +166 -0
- package/src/__tests__/morph-feature-gpu-work.integration.test.ts +78 -0
- package/src/__tests__/morph-visual-evidence.integration.test.ts +66 -0
- package/src/__tests__/nineslice-handle-surface.unit.test.ts +14 -0
- package/src/__tests__/param-schema-projection-census.unit.test.ts +40 -0
- package/src/__tests__/persistent-render-scene.unit.test.ts +1311 -0
- package/src/__tests__/pipeline-error-policy-owner.unit.test.ts +167 -0
- package/src/__tests__/pipeline-pass-trace.test.ts +92 -0
- package/src/__tests__/pipeline-stage-owner.test-d.ts +13 -0
- package/src/__tests__/plugin.test.ts +25 -0
- package/src/__tests__/post-process-error-policy-owner.unit.test.ts +208 -0
- package/src/__tests__/prepared-buffer-kind-owner.test-d.ts +29 -0
- package/src/__tests__/prepared-graphics-backend-matrix.unit.test.ts +129 -0
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +140 -0
- package/src/__tests__/prepared-graphics-generation.unit.test.ts +175 -0
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +206 -0
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +29 -0
- package/src/__tests__/prepared-graphics-resolver-recording.unit.test.ts +180 -0
- package/src/__tests__/prepared-graphics-resolver.unit.test.ts +287 -0
- package/src/__tests__/prepared-graphics-snapshot.unit.test.ts +105 -0
- package/src/__tests__/prepared-graphics-store.unit.test.ts +154 -0
- package/src/__tests__/prepared-graphics-validation.unit.test.ts +176 -0
- package/src/__tests__/profiler-default-off.test.ts +22 -0
- package/src/__tests__/profiler-phase-catalog.test.ts +66 -0
- package/src/__tests__/profiler-phase-integration.test.ts +70 -0
- package/src/__tests__/profiler-phase-token.test.ts +84 -0
- package/src/__tests__/public-pipeline-id.test-d.ts +6 -0
- package/src/__tests__/public-surface.test-d.ts +32 -0
- package/src/__tests__/recover-error-policy-owner.unit.test.ts +127 -0
- package/src/__tests__/render-dispatch-membership.unit.test.ts +117 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +115 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +234 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +84 -0
- package/src/__tests__/render-feature-assembly.test-d.ts +51 -0
- package/src/__tests__/render-feature-construction.unit.test.ts +124 -0
- package/src/__tests__/render-feature-context.test-d.ts +81 -0
- package/src/__tests__/render-feature-contract.test-d.ts +58 -0
- package/src/__tests__/render-feature-diagnostics.unit.test.ts +151 -0
- package/src/__tests__/render-feature-docs.unit.test.ts +88 -0
- package/src/__tests__/render-feature-error-code-owner.test-d.ts +27 -0
- package/src/__tests__/render-feature-errors.unit.test.ts +47 -0
- package/src/__tests__/render-feature-gpu-buffer-usage-owner.test-d.ts +46 -0
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +377 -0
- package/src/__tests__/render-feature-graph.integration.test.ts +205 -0
- package/src/__tests__/render-feature-graphics-staging.unit.test.ts +200 -0
- package/src/__tests__/render-feature-order.unit.test.ts +66 -0
- package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +101 -0
- package/src/__tests__/render-feature-prepared-errors.test-d.ts +82 -0
- package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +141 -0
- package/src/__tests__/render-feature-prepared-graphics-isolation.unit.test.ts +122 -0
- package/src/__tests__/render-feature-prepared-graphics-negative.test-d.ts +59 -0
- package/src/__tests__/render-feature-prepared-graphics-recording.unit.test.ts +107 -0
- package/src/__tests__/render-feature-prepared-graphics.test-d.ts +96 -0
- package/src/__tests__/render-feature-prepared-graphics.unit.test.ts +121 -0
- package/src/__tests__/render-feature-public-surface.test-d.ts +75 -0
- package/src/__tests__/render-feature-retry.unit.test.ts +128 -0
- package/src/__tests__/render-feature-scheduling.unit.test.ts +122 -0
- package/src/__tests__/render-feature-stage-failed-policy-owner.unit.test.ts +146 -0
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +236 -0
- package/src/__tests__/render-feature-staging.unit.test.ts +94 -0
- package/src/__tests__/render-feature-visibility-context.test-d.ts +38 -0
- package/src/__tests__/render-feature-visibility-context.unit.test.ts +38 -0
- package/src/__tests__/render-feature-zero-work.unit.test.ts +82 -0
- package/src/__tests__/render-graph-compute-raster-gpu.ts +342 -0
- package/src/__tests__/render-graph-compute-raster.browser.test.ts +16 -0
- package/src/__tests__/render-graph-compute-raster.dawn.test.ts +22 -0
- package/src/__tests__/render-graph-hzb-gpu.ts +239 -0
- package/src/__tests__/render-graph-hzb.browser.test.ts +16 -0
- package/src/__tests__/render-graph-hzb.dawn.test.ts +23 -0
- package/src/__tests__/render-graph-primitives.unit.test.ts +22 -0
- package/src/__tests__/render-graph-storage-indirect-gpu.ts +162 -0
- package/src/__tests__/render-graph-storage-indirect.browser.test.ts +21 -0
- package/src/__tests__/render-graph-storage-indirect.dawn.test.ts +28 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +206 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +125 -0
- package/src/__tests__/renderer-lifecycle.integration.test.ts +10 -0
- package/src/__tests__/renderer-render-scene-public.test-d.ts +27 -0
- package/src/__tests__/rhi-backend-package-boundary.unit.test.ts +11 -0
- package/src/__tests__/scene-temporal-coverage.unit.test.ts +259 -0
- package/src/__tests__/scriptable-pack-consumer.test-d.ts +14 -0
- package/src/__tests__/shader-stage-surface.unit.test.ts +38 -0
- package/src/__tests__/ssao-kernel-surface.unit.test.ts +15 -0
- package/src/__tests__/sun-snapshot-consumers.test.ts +35 -0
- package/src/__tests__/surface-backend-kind-owner.test-d.ts +42 -0
- package/src/__tests__/surface-configuration.unit.test.ts +92 -0
- package/src/__tests__/taa-history-store.unit.test.ts +196 -0
- package/src/__tests__/temporal-frame-transaction.integration.test.ts +537 -0
- package/src/__tests__/temporal-inspection.unit.test.ts +138 -0
- package/src/__tests__/texture-usage-surface.unit.test.ts +61 -0
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +389 -0
- package/src/__tests__/urp-pipeline-targets.unit.test.ts +52 -0
- package/src/__tests__/urp-pipeline.unit.test.ts +14 -0
- package/src/__tests__/vertex-color-pipeline.unit.test.ts +124 -0
- package/src/__tests__/vertex-layout-consumer.unit.test.ts +25 -0
- package/src/__tests__/visibility-diagnostics.integration.test.ts +66 -0
- package/src/__tests__/visibility-extract.integration.test.ts +89 -0
- package/src/__tests__/visibility-extract.perf.test.ts +96 -0
- package/src/__tests__/visibility-feature-dedup.integration.test.ts +57 -0
- package/src/__tests__/visibility-hidden-report.unit.test.ts +37 -0
- package/src/__tests__/visibility-instances.integration.test.ts +50 -0
- package/src/__tests__/visibility-multi-world.integration.test.ts +54 -0
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +191 -0
- package/src/__tests__/visibility-producer-matrix.unit.test.ts +96 -0
- package/src/__tests__/visibility-public-surface.test-d.ts +26 -0
- package/src/__tests__/visibility-resolver.unit.test.ts +84 -0
- package/src/__tests__/visibility-snapshot-contract.unit.test.ts +59 -0
- package/src/__tests__/vocabulary.unit.test.ts +21 -0
- package/src/assets/__tests__/asset-decoders.unit.test.ts +92 -0
- package/src/assets/__tests__/mipmap-generator.unit.test.ts +52 -0
- package/src/assets/__tests__/render-asset-port.unit.test.ts +54 -0
- package/src/assets/__tests__/resolve-render-asset.unit.test.ts +47 -0
- package/src/assets/asset-decoders.ts +158 -0
- package/src/assets/render-asset-port.ts +44 -0
- package/src/assets/resolve-render-asset.ts +13 -0
- package/src/authoring.ts +24 -0
- package/src/cluster-binner.ts +700 -0
- package/src/components/__tests__/atmosphere-red.test.ts +51 -0
- package/src/components/__tests__/atmosphere.test.ts +79 -0
- package/src/components/__tests__/fog.test-d.ts +49 -0
- package/src/components/__tests__/fog.test.ts +60 -0
- package/src/components/__tests__/schema-parity.unit.test.ts +25 -0
- package/src/components/__tests__/visibility-schema.unit.test.ts +35 -0
- package/src/components/atmosphere.ts +64 -0
- package/src/components/camera.ts +512 -0
- package/src/components/directional-light.ts +120 -0
- package/src/components/environment.ts +206 -0
- package/src/components/glyph-text.ts +63 -0
- package/src/components/index.ts +49 -0
- package/src/components/instances.ts +207 -0
- package/src/components/layer.ts +69 -0
- package/src/components/light-helpers.ts +188 -0
- package/src/components/mesh-filter.ts +59 -0
- package/src/components/mesh-renderer.ts +61 -0
- package/src/components/point-light-shadow.ts +43 -0
- package/src/components/point-light.ts +65 -0
- package/src/components/post-process-params.ts +35 -0
- package/src/components/scene-instance.ts +193 -0
- package/src/components/sort-key.ts +75 -0
- package/src/components/spot-light.ts +124 -0
- package/src/components/sprite-animation.ts +169 -0
- package/src/components/sprite-instances.ts +179 -0
- package/src/components/sprite-playback-mode.ts +73 -0
- package/src/components/sprite-region-override.ts +106 -0
- package/src/components/tile-layer.ts +161 -0
- package/src/components/tilemap.ts +48 -0
- package/src/components/visibility.ts +53 -0
- package/src/construct-renderer.ts +4 -0
- package/src/debug-draw-glue.ts +169 -0
- package/src/deferred-lighting.ts +92 -0
- package/src/dynamic-texture-store.ts +111 -0
- package/src/engine-metrics.ts +89 -0
- package/src/environment/README.md +76 -0
- package/src/environment/__tests__/failure-injection.test.ts +50 -0
- package/src/environment/__tests__/generation-red.test.ts +64 -0
- package/src/environment/__tests__/generation.test.ts +37 -0
- package/src/environment/__tests__/graph-transaction.test.ts +85 -0
- package/src/environment/__tests__/inspection-red.test.ts +53 -0
- package/src/environment/__tests__/lifecycle-evidence.test.ts +40 -0
- package/src/environment/__tests__/pipeline-topology.test.ts +33 -0
- package/src/environment/__tests__/readiness.test.ts +42 -0
- package/src/environment/__tests__/recovery.test.ts +62 -0
- package/src/environment/__tests__/resize-dispose.test.ts +30 -0
- package/src/environment/__tests__/signature-red.test.ts +45 -0
- package/src/environment/__tests__/signature.test.ts +60 -0
- package/src/environment/descriptors.ts +7 -0
- package/src/environment/extracted-environment.ts +239 -0
- package/src/environment/generation.ts +158 -0
- package/src/environment/gpu-bundle.ts +144 -0
- package/src/environment/graph.ts +588 -0
- package/src/environment/inspection.ts +107 -0
- package/src/environment/lifecycle.ts +124 -0
- package/src/environment/readiness.ts +76 -0
- package/src/environment/signature.ts +185 -0
- package/src/errors/ecs-validation.ts +16 -0
- package/src/errors/index.ts +3 -0
- package/src/errors/recover.ts +119 -0
- package/src/errors/render.ts +1244 -0
- package/src/extract/__tests__/directional-csm-domain.unit.test.ts +391 -0
- package/src/extract/__tests__/environment-source.test.ts +105 -0
- package/src/extract/__tests__/schema-readers.unit.test.ts +16 -0
- package/src/extract/directional-csm.ts +959 -0
- package/src/extract/index.ts +10 -0
- package/src/extract/material-interface.ts +604 -0
- package/src/extract/schema-readers.ts +27 -0
- package/src/extract/visibility.ts +120 -0
- package/src/features/graph-contribution.ts +577 -0
- package/src/features/host.ts +1097 -0
- package/src/features/index.ts +100 -0
- package/src/features/morph/index.ts +24 -0
- package/src/features/morph/morph-bounds.ts +123 -0
- package/src/features/morph/morph-feature.ts +287 -0
- package/src/features/prepared-gpu-work.ts +902 -0
- package/src/features/prepared-graphics-store.ts +435 -0
- package/src/features/prepared-graphics.ts +479 -0
- package/src/features/render-graph-compute.ts +60 -0
- package/src/features/render-graph-contribution.ts +95 -0
- package/src/features/render-graph-raster.ts +308 -0
- package/src/features/render-graph-resources.ts +36 -0
- package/src/features/scene-temporal.ts +274 -0
- package/src/features/taa-resolve.ts +244 -0
- package/src/features/targets.ts +83 -0
- package/src/features/types.ts +291 -0
- package/src/frame-chain.ts +21 -0
- package/src/fullscreen-post-process-pass.ts +406 -0
- package/src/glyph-text-layout-system.ts +447 -0
- package/src/gpu-driven/batch-topology.ts +423 -0
- package/src/gpu-driven/production-raster.ts +1705 -0
- package/src/gpu-driven/view-gpu.ts +849 -0
- package/src/gpu-driven/view-reference.ts +110 -0
- package/src/gpu-resource-store.ts +1921 -0
- package/src/gpu-resource.ts +159 -0
- package/src/gpu-scene-schema.ts +224 -0
- package/src/gpu-scene.ts +855 -0
- package/src/gpu-stage.ts +9 -0
- package/src/gpu-texture-usage.ts +12 -0
- package/src/gpu-usage.ts +14 -0
- package/src/hdrp-buffers.ts +689 -0
- package/src/hdrp-cluster-membership.ts +70 -0
- package/src/hdrp-pipeline.ts +525 -0
- package/src/ibl/IblPipelineCache.ts +1246 -0
- package/src/ibl/cubemap-projection.ts +60 -0
- package/src/ibl/face-uniforms.ts +150 -0
- package/src/ibl/skylight-bind-group.ts +513 -0
- package/src/index.ts +271 -0
- package/src/instance-buffer-cache.ts +123 -0
- package/src/internal.ts +88 -0
- package/src/lifecycle.ts +123 -0
- package/src/light-buffer-layout.ts +346 -0
- package/src/materials.ts +236 -0
- package/src/mesh-material-bindings.ts +104 -0
- package/src/mesh-update-errors.ts +113 -0
- package/src/mipmap-generator.ts +244 -0
- package/src/param-schema-projection-census.ts +73 -0
- package/src/pbr-pipeline.ts +749 -0
- package/src/persistent-render-scene.ts +1752 -0
- package/src/pipeline/index.ts +40 -0
- package/src/pipeline-builder.ts +386 -0
- package/src/pipeline-errors.ts +172 -0
- package/src/pipeline-spec.ts +1768 -0
- package/src/plugin.ts +137 -0
- package/src/post-process-errors.ts +304 -0
- package/src/prepare/__tests__/schema-readers.unit.test.ts +16 -0
- package/src/prepare/index.ts +8 -0
- package/src/prepare/prepared-graphics-resolver.ts +455 -0
- package/src/prepare/schema-readers.ts +20 -0
- package/src/record/__tests__/environment-record.test.ts +35 -0
- package/src/record/__tests__/frame-targets.unit.test.ts +54 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +41 -0
- package/src/record/__tests__/material-slot-plan.unit.test.ts +76 -0
- package/src/record/__tests__/membership-timing.unit.test.ts +524 -0
- package/src/record/__tests__/render-submission.integration.test.ts +76 -0
- package/src/record/__tests__/render-submission.perf.test.ts +141 -0
- package/src/record/__tests__/shadow-submit-mode.unit.test.ts +88 -0
- package/src/record/__tests__/video-texture-view.unit.test.ts +90 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +101 -0
- package/src/record/frame-lighting.ts +1183 -0
- package/src/record/frame-observation.ts +122 -0
- package/src/record/frame-snapshot.ts +515 -0
- package/src/record/frame-targets.ts +372 -0
- package/src/record/frame.ts +1874 -0
- package/src/record/helpers.ts +289 -0
- package/src/record/index.ts +15 -0
- package/src/record/main-pass-geometry.ts +1014 -0
- package/src/record/main-pass-material.ts +716 -0
- package/src/record/main-pass-skin.ts +51 -0
- package/src/record/main-pass-sprite-draws.ts +1056 -0
- package/src/record/main-pass-sprite.ts +166 -0
- package/src/record/main-pass.ts +588 -0
- package/src/record/material-payload.ts +292 -0
- package/src/record/material-slot-plan.ts +68 -0
- package/src/record/membership-timing.ts +1012 -0
- package/src/record/mesh-ssbo.ts +406 -0
- package/src/record/shadow-caster-uniform.ts +37 -0
- package/src/record/shadow-pass.ts +2387 -0
- package/src/record/shadow-submit-mode.ts +277 -0
- package/src/record/skybox-post-pass.ts +594 -0
- package/src/record/typed-frame-graph.ts +499 -0
- package/src/record/view-ubo.ts +206 -0
- package/src/render-data.ts +385 -0
- package/src/render-dispatch-membership.ts +155 -0
- package/src/render-graph-primitives.ts +1586 -0
- package/src/render-pipeline-context.ts +332 -0
- package/src/render-pipeline-target.ts +96 -0
- package/src/render-pipeline.ts +343 -0
- package/src/render-scene-projection.ts +1738 -0
- package/src/render-system-extract.ts +5203 -0
- package/src/render-system-fold.ts +531 -0
- package/src/render-system.ts +3258 -0
- package/src/renderer/asset-service.ts +32 -0
- package/src/renderer/backend-selection.ts +121 -0
- package/src/renderer/environment-classify.ts +30 -0
- package/src/renderer/environment-error.ts +19 -0
- package/src/renderer/material/assembly.ts +53 -0
- package/src/renderer/material/pipeline-projection.ts +140 -0
- package/src/renderer/renderer-factory.ts +9062 -0
- package/src/renderer.ts +940 -0
- package/src/scene-instances/post-spawn-resolve-joints.ts +168 -0
- package/src/scene-utils/collect-subtree.ts +40 -0
- package/src/shadow-atlas.ts +244 -0
- package/src/ssao-buffers.ts +175 -0
- package/src/ssao-config.ts +61 -0
- package/src/ssao-data.ts +106 -0
- package/src/surface-format.ts +37 -0
- package/src/systems/active-camera.ts +116 -0
- package/src/systems/pass-selector.ts +49 -0
- package/src/systems/skin-palette-allocator.ts +294 -0
- package/src/systems/transparent-sort-config.ts +211 -0
- package/src/taa-history-store.ts +429 -0
- package/src/temporal-coverage.ts +106 -0
- package/src/temporal-frame-transaction.ts +301 -0
- package/src/temporal-inspection.ts +194 -0
- package/src/temporal-view.ts +174 -0
- package/src/tilemap/resolve-tileset-runtime.ts +72 -0
- package/src/tilemap-chunk-extract-system.ts +1307 -0
- package/src/typed-render-graph-primitives.ts +530 -0
- package/src/typed-shadow-passes.ts +195 -0
- package/src/urp-pipeline.ts +340 -0
|
@@ -0,0 +1,1921 @@
|
|
|
1
|
+
// @forgeax/engine-runtime - GpuResourceStore
|
|
2
|
+
// (feat-20260601-gpu-resource-store-extraction M1; M2 added the deriveRenderData
|
|
3
|
+
// projection seam — see render-data.ts).
|
|
4
|
+
//
|
|
5
|
+
// Owns the GPU residency layer extracted from AssetRegistry: per-handle GPU
|
|
6
|
+
// texture / cubemap / mesh buffer caches plus the upload primitives that
|
|
7
|
+
// build them. The store is engine-agnostic by default (no @webgpu/types
|
|
8
|
+
// imports) and holds ZERO reference to AssetRegistry (D-2): every upload
|
|
9
|
+
// primitive receives its source POD from the caller, never reaches back into
|
|
10
|
+
// a registry. The cubemap path mints its EquirectAsset POD handle through
|
|
11
|
+
// a wire-injected `registerCube` callback (D-3) so CPU cataloguing stays the
|
|
12
|
+
// registry's job while the store keeps the single-call upload contract. The
|
|
13
|
+
// cubemap projection (`_uploadCubemapFromEquirect`) is internal (@internal,
|
|
14
|
+
// feat-20260630): AI users declare `Skylight{equirect}` and the render-system
|
|
15
|
+
// record arm drives the projection; the method is never reached from user code.
|
|
16
|
+
//
|
|
17
|
+
// Residency model (D-2 pull): consumers call `ensureResident(handle, pod)`
|
|
18
|
+
// at first draw-time access; the store builds the GPU resource on a miss and
|
|
19
|
+
// returns the cached handles on subsequent O(1) hits. There is no global
|
|
20
|
+
// replay queue -- the pull model is purely lazy (user ruling). Builtin
|
|
21
|
+
// meshes are NOT routed through `ensureResident`; createRenderer seeds them
|
|
22
|
+
// via its step-3 direct upload + pipelineState.meshes (D-1).
|
|
23
|
+
//
|
|
24
|
+
// Texture residency is SYNCHRONOUS (D-9): `uploadTexture`'s only async source
|
|
25
|
+
// was the one-time per-device mipmap shader-module build, which is hoisted to
|
|
26
|
+
// `prewarmMipmapPipeline` (called from `createRenderer.ready`, already async).
|
|
27
|
+
// After prewarm the per-texture mipmap blit is pure synchronous encoder work,
|
|
28
|
+
// so the sync `draw(world)` frame contract is preserved. A format that was
|
|
29
|
+
// not prewarmed surfaces a structured RhiError on the sync path -- it is never
|
|
30
|
+
// lazily awaited (that would break the sync draw contract).
|
|
31
|
+
|
|
32
|
+
import type { World } from '@forgeax/engine-ecs';
|
|
33
|
+
import type { VertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
34
|
+
import { halfFloat } from '@forgeax/engine-math';
|
|
35
|
+
import {
|
|
36
|
+
type Buffer,
|
|
37
|
+
err,
|
|
38
|
+
ok,
|
|
39
|
+
type Result,
|
|
40
|
+
type RhiCaps,
|
|
41
|
+
type RhiDevice,
|
|
42
|
+
RhiError,
|
|
43
|
+
type Sampler,
|
|
44
|
+
type Texture,
|
|
45
|
+
type TextureView,
|
|
46
|
+
} from '@forgeax/engine-rhi';
|
|
47
|
+
import {
|
|
48
|
+
ASSET_ERROR_HINTS,
|
|
49
|
+
type AssetError,
|
|
50
|
+
type DecodedImage,
|
|
51
|
+
type EquirectAsset,
|
|
52
|
+
type Handle,
|
|
53
|
+
handleSlot,
|
|
54
|
+
IMAGE_ERROR_HINTS,
|
|
55
|
+
type ImageError,
|
|
56
|
+
type ImageErrorCode,
|
|
57
|
+
type ImageErrorDetailFor,
|
|
58
|
+
type ImageErrorFor,
|
|
59
|
+
type PrimitiveTopology,
|
|
60
|
+
type SamplerAsset,
|
|
61
|
+
type Submesh,
|
|
62
|
+
type TextureAsset,
|
|
63
|
+
type MeshAsset as TypesMeshAsset,
|
|
64
|
+
} from '@forgeax/engine-types';
|
|
65
|
+
import { GpuBuffer, GpuTexture } from './gpu-resource';
|
|
66
|
+
import { GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING } from './gpu-texture-usage';
|
|
67
|
+
import {
|
|
68
|
+
GPU_BUFFER_USAGE_COPY_DST,
|
|
69
|
+
GPU_BUFFER_USAGE_INDEX,
|
|
70
|
+
GPU_BUFFER_USAGE_VERTEX,
|
|
71
|
+
} from './gpu-usage';
|
|
72
|
+
import {
|
|
73
|
+
createFaceUniformsBuffer,
|
|
74
|
+
createPrefilterUniformsBuffer,
|
|
75
|
+
writeAllFaceUniforms,
|
|
76
|
+
writeAllPrefilterUniforms,
|
|
77
|
+
} from './ibl/face-uniforms';
|
|
78
|
+
import {
|
|
79
|
+
CUBEMAP_FACE_VERTICES,
|
|
80
|
+
createIblPipelines,
|
|
81
|
+
runIblPrecompute,
|
|
82
|
+
} from './ibl/IblPipelineCache';
|
|
83
|
+
import type { RhiErrorListenerRegistry } from './lifecycle';
|
|
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';
|
|
95
|
+
import {
|
|
96
|
+
type CubeRenderData,
|
|
97
|
+
deriveMipUploadLayout,
|
|
98
|
+
deriveRenderDataCubemap,
|
|
99
|
+
deriveRenderDataMesh,
|
|
100
|
+
deriveRenderDataTexture,
|
|
101
|
+
type MeshRenderData,
|
|
102
|
+
type TextureRenderData,
|
|
103
|
+
} from './render-data';
|
|
104
|
+
|
|
105
|
+
// AssetError is constructed without importing the AssetRegistry's class; the
|
|
106
|
+
// store builds the 4-field surface (.code / .expected / .hint / .detail)
|
|
107
|
+
// directly against the @forgeax/engine-types SSOT (charter P5 producer /
|
|
108
|
+
// consumer split; mirrors AssetRegistry's local RuntimeImageError).
|
|
109
|
+
class RuntimeAssetError extends Error implements AssetError {
|
|
110
|
+
readonly code: AssetError['code'];
|
|
111
|
+
readonly expected: string;
|
|
112
|
+
readonly hint: string;
|
|
113
|
+
constructor(fields: { code: AssetError['code']; expected: string; hint: string }) {
|
|
114
|
+
super(`[AssetError ${fields.code}] expected: ${fields.expected}; hint: ${fields.hint}`);
|
|
115
|
+
this.name = 'AssetError';
|
|
116
|
+
this.code = fields.code;
|
|
117
|
+
this.expected = fields.expected;
|
|
118
|
+
this.hint = fields.hint;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function makeAssetError(fields: {
|
|
123
|
+
code: AssetError['code'];
|
|
124
|
+
expected: string;
|
|
125
|
+
hint: string;
|
|
126
|
+
}): AssetError {
|
|
127
|
+
return new RuntimeAssetError(fields);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const IMAGE_ERROR_EXPECTED_LOCAL: Readonly<Record<string, string>> = {
|
|
131
|
+
'image-decode-failed': 'PNG / JPG byte stream decodes successfully',
|
|
132
|
+
'image-format-unsupported': "format ends in '-srgb' iff colorSpace is 'srgb' (linear otherwise)",
|
|
133
|
+
'image-dimension-out-of-bounds':
|
|
134
|
+
'width and height fall under device caps maxTextureDimension2D (or 16384 hard cap)',
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
class RuntimeImageError<C extends ImageErrorCode> extends Error implements ImageErrorFor<C> {
|
|
138
|
+
readonly code: C;
|
|
139
|
+
readonly expected: string;
|
|
140
|
+
readonly hint: string;
|
|
141
|
+
readonly detail: ImageErrorDetailFor<C>;
|
|
142
|
+
constructor(detail: ImageErrorDetailFor<C>) {
|
|
143
|
+
const code = detail.code;
|
|
144
|
+
const expected = IMAGE_ERROR_EXPECTED_LOCAL[code] ?? '';
|
|
145
|
+
const hint = IMAGE_ERROR_HINTS[code];
|
|
146
|
+
super(`[ImageError ${code}] expected: ${expected}; hint: ${hint}`);
|
|
147
|
+
this.name = 'ImageError';
|
|
148
|
+
this.code = code;
|
|
149
|
+
this.expected = expected;
|
|
150
|
+
this.hint = hint;
|
|
151
|
+
this.detail = detail;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function makeImageError<C extends ImageErrorCode>(
|
|
156
|
+
detail: ImageErrorDetailFor<C>,
|
|
157
|
+
): ImageErrorFor<C> {
|
|
158
|
+
return new RuntimeImageError<C>(detail);
|
|
159
|
+
}
|
|
160
|
+
|
|
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
|
+
/** Cube-POD register-relay injected by the wire layer (D-3). */
|
|
184
|
+
type RegisterCube = (
|
|
185
|
+
world: World,
|
|
186
|
+
pod: EquirectAsset,
|
|
187
|
+
) => Result<Handle<'EquirectAsset', 'shared'>, AssetError>;
|
|
188
|
+
|
|
189
|
+
// feat-20260612-rhi-destroy-renderer-dispose-gpu-lifecycle / M-3 / w11:
|
|
190
|
+
// the three handle map value types now hold GpuTexture / GpuBuffer wrappers
|
|
191
|
+
// (plan-strategy D-9). Views (TextureView) stay raw RHI handles -- they are
|
|
192
|
+
// not destroyable on their own; their lifetime is bound to the parent
|
|
193
|
+
// GpuTexture, which the wrapper owns. destroyAll() walks the GpuResource
|
|
194
|
+
// fields and forwards .destroy() to the RHI shim.
|
|
195
|
+
interface TextureGpuEntry {
|
|
196
|
+
readonly texture: GpuTexture;
|
|
197
|
+
/** Opaque RHI view handle; lifetime is bound to the parent texture. */
|
|
198
|
+
readonly view: TextureView;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// feat-20260630-equirect-kind-internalized-ibl-declarative-skyligh M2 / w11
|
|
202
|
+
// (D-3): the projection-status truth lives here (the store's cubemap map is the
|
|
203
|
+
// single authority — the SkylightSnapshot carries no status, D-3). `status`:
|
|
204
|
+
// - 'pending' — projection launched (fire-and-forget) but not complete; the
|
|
205
|
+
// record arm binds the white-cube fallback for this frame.
|
|
206
|
+
// - 'ready' — texture + cube view + 6 face views are live; bind the real
|
|
207
|
+
// IBL cube.
|
|
208
|
+
// - 'failed' — projection errored; recorded EXPLICITLY so the record arm
|
|
209
|
+
// stops and does NOT retry every frame (R-2 / AC-09). A failed entry has
|
|
210
|
+
// no GPU resources (texture/view are placeholders never bound).
|
|
211
|
+
// `texture`/`view`/`faceViews` are non-readonly so a 'pending' entry can be
|
|
212
|
+
// promoted to 'ready' in place (rebuilds the same slot). A 'failed' entry holds
|
|
213
|
+
// no GPU resources (`texture:null` / `view:undefined`): it exists only to mark
|
|
214
|
+
// the source as terminally failed so the record arm stops retrying (R-2).
|
|
215
|
+
interface CubemapGpuEntry {
|
|
216
|
+
status: 'pending' | 'ready' | 'failed';
|
|
217
|
+
texture: GpuTexture | null;
|
|
218
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU cubemap view (not a GpuResource)
|
|
219
|
+
view: any;
|
|
220
|
+
// biome-ignore lint/suspicious/noExplicitAny: per-face 2D views (6 faces; not GpuResources)
|
|
221
|
+
faceViews: readonly any[];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
interface MeshGpuEntry {
|
|
225
|
+
readonly vertexBuffer: GpuBuffer;
|
|
226
|
+
/**
|
|
227
|
+
* Index buffer wrapper, or `null` for a vertex-only mesh
|
|
228
|
+
* (no `MeshAsset.indices`). When `null` the record stage takes the
|
|
229
|
+
* non-indexed `pass.draw(vertexCount)` path and never calls
|
|
230
|
+
* `setIndexBuffer`. Gated on `indexed` below.
|
|
231
|
+
*/
|
|
232
|
+
readonly indexBuffer: GpuBuffer | null;
|
|
233
|
+
/**
|
|
234
|
+
* Allocation byte sizes for the vbo / ibo (mirrors GPUBuffer.size). Used
|
|
235
|
+
* by `updateMeshById` to decide reuse vs reallocation; tracked here so
|
|
236
|
+
* the runtime never reads `.size` off the opaque RHI handle (which is
|
|
237
|
+
* not on the spec-aligned RHI Buffer interface; charter §RHI form rules).
|
|
238
|
+
*/
|
|
239
|
+
readonly vboBytes: number;
|
|
240
|
+
/** Allocation byte size for the ibo, or 0 when indexBuffer is null. */
|
|
241
|
+
readonly iboBytes: number;
|
|
242
|
+
/** Immutable geometry-owned layout projection used for all resident stride checks. */
|
|
243
|
+
readonly layoutProjection: VertexLayoutProjection;
|
|
244
|
+
readonly indexCount: number;
|
|
245
|
+
readonly indexFormat: 'uint16' | 'uint32';
|
|
246
|
+
/** Vertex count is derived from the geometry-owned projection stride. */
|
|
247
|
+
readonly vertexCount: number;
|
|
248
|
+
/** True when `MeshAsset.indices` is present (indexed draw path). */
|
|
249
|
+
readonly indexed: boolean;
|
|
250
|
+
/** Primitive topology (default 'triangle-list'). Mirrors MeshGpuHandles. */
|
|
251
|
+
readonly topology: PrimitiveTopology;
|
|
252
|
+
/**
|
|
253
|
+
* Submeshes from MeshAsset.submeshes, carried through to the record stage
|
|
254
|
+
* so per-submesh drawIndexed can iterate independently (feat-20260608 M4 / w16).
|
|
255
|
+
* For single-submesh meshes this is a 1-element array (byte-identical to pre-M4).
|
|
256
|
+
*/
|
|
257
|
+
readonly submeshes: readonly import('@forgeax/engine-types').Submesh[];
|
|
258
|
+
}
|
|
259
|
+
|
|
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
|
+
/**
|
|
285
|
+
* GPU residency store. Owns the per-handle GPU caches and the upload
|
|
286
|
+
* primitives that build them. Constructed once per renderer; wired with the
|
|
287
|
+
* GPU device via `configureGpuDevice` after `Renderer.ready` resolves.
|
|
288
|
+
*/
|
|
289
|
+
export class GpuResourceStore {
|
|
290
|
+
/** Monotonic token for mesh GPU residency or contents changes. */
|
|
291
|
+
meshResidencyEpoch = 0;
|
|
292
|
+
/** Monotonic token for texture/sampler identities used by material bind groups. */
|
|
293
|
+
materialResourceEpoch = 0;
|
|
294
|
+
private gpuDevice: MipmapBlitDevice | undefined = undefined;
|
|
295
|
+
// Shader-module factory injected at `configureGpuDevice`; threaded into the
|
|
296
|
+
// mipmap-pipeline prewarm + the IBL precompute path. `undefined` until wired.
|
|
297
|
+
private asyncCreateShaderModule: MipmapShaderModuleFactory | undefined = undefined;
|
|
298
|
+
// Cube-POD register relay injected at `configureGpuDevice` (D-3); the store
|
|
299
|
+
// never imports AssetRegistry, so CPU cataloguing flows through this fn.
|
|
300
|
+
private registerCube: RegisterCube | undefined = undefined;
|
|
301
|
+
// Error registry injected by createRenderer; evict / releaseUnreferenced
|
|
302
|
+
// fire structured errors through this channel (feat-20260619 D-1/D-6).
|
|
303
|
+
private errorRegistry: RhiErrorListenerRegistry | undefined = undefined;
|
|
304
|
+
// Hardware-probe caps injected at `configureGpuDevice`; guards the HDR cubemap
|
|
305
|
+
// path (_uploadCubemapFromEquirect) when `rgba16floatRenderable` is false.
|
|
306
|
+
private caps: RhiCaps | undefined = undefined;
|
|
307
|
+
|
|
308
|
+
private readonly textureGpuHandles: Map<number, TextureGpuEntry> = new Map();
|
|
309
|
+
private readonly samplerGpuHandles: Map<number, Sampler> = new Map();
|
|
310
|
+
private readonly cubemapGpuHandles: Map<number, CubemapGpuEntry> = new Map();
|
|
311
|
+
// Maps source EquirectAsset handle id -> minted cubemap handle so the same
|
|
312
|
+
// equirect source always resolves to the same cubemap (idempotent, A2).
|
|
313
|
+
private readonly cubemapIdempotentMap: Map<number, Handle<'EquirectAsset', 'shared'>> = new Map();
|
|
314
|
+
private readonly meshGpuHandles: Map<number, MeshGpuEntry> = new Map();
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* feat-20260723-cross-world-gpu-cache-collision: compose a cache key from
|
|
318
|
+
* the raw handle slot and the worldId so two worlds that independently
|
|
319
|
+
* allocate assets at the same slot number never collide in the GPU store.
|
|
320
|
+
* worldId=0 produces the bare slot (backward compat with single-world path).
|
|
321
|
+
*/
|
|
322
|
+
private worldKey(slot: number, worldId: number): number {
|
|
323
|
+
return worldId === 0 ? slot : worldId * 0x1000000 + slot;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Wire the GPU device, shader-module factory, and cube-POD register relay.
|
|
328
|
+
* Called once by createRenderer after `Renderer.ready` resolves. This
|
|
329
|
+
* performs NO replay of CPU asset state:
|
|
330
|
+
* the pull model builds resources lazily at first `ensureResident` access.
|
|
331
|
+
*/
|
|
332
|
+
configureGpuDevice(
|
|
333
|
+
device: MipmapBlitDevice,
|
|
334
|
+
asyncCreateShaderModule: MipmapShaderModuleFactory | undefined,
|
|
335
|
+
registerCube: RegisterCube,
|
|
336
|
+
caps: RhiCaps,
|
|
337
|
+
): void {
|
|
338
|
+
this.meshResidencyEpoch += 1;
|
|
339
|
+
this.gpuDevice = device;
|
|
340
|
+
this.asyncCreateShaderModule = asyncCreateShaderModule;
|
|
341
|
+
this.registerCube = registerCube;
|
|
342
|
+
this.caps = caps;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Wrap a raw RHI Buffer handle into a GpuBuffer. The wrapper holds a
|
|
347
|
+
* reference to the device so `.destroy()` forwards to
|
|
348
|
+
* `device.destroyBuffer(handle)` (M-3 / w11; plan-strategy D-2).
|
|
349
|
+
*
|
|
350
|
+
* Pre: `configureGpuDevice` has been called. Callers that build a buffer
|
|
351
|
+
* via `device.createBuffer` already gate on a wired device, so this private
|
|
352
|
+
* helper trusts the caller; an unwired call is a programmer error and
|
|
353
|
+
* surfaces as a non-null assertion rather than a structured error.
|
|
354
|
+
*/
|
|
355
|
+
private wrapBuf(rawHandle: Buffer): GpuBuffer {
|
|
356
|
+
const device = this.gpuDevice as MipmapBlitDevice & RhiDevice;
|
|
357
|
+
return new GpuBuffer(device, rawHandle);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private destroyMeshBuffer(buffer: GpuBuffer): void {
|
|
361
|
+
const result = buffer.destroy();
|
|
362
|
+
if (!result.ok) this.errorRegistry?.fire(result.error);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** Wrap a raw RHI Texture handle into a GpuTexture (mirror of wrapBuf). */
|
|
366
|
+
private wrapTex(rawHandle: Texture): GpuTexture {
|
|
367
|
+
const device = this.gpuDevice as MipmapBlitDevice & RhiDevice;
|
|
368
|
+
return new GpuTexture(device, rawHandle);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Walk the three handle maps and destroy every GpuResource, then clear
|
|
373
|
+
* the maps. Called from `Renderer.dispose()` (M-5) as the first step of
|
|
374
|
+
* the dispose chain (plan-strategy D-2: dispose chain walks
|
|
375
|
+
* `gpuStore.destroyAll()` → `graph.drain()` → `instanceBuffers.clear()`
|
|
376
|
+
* → ...).
|
|
377
|
+
*
|
|
378
|
+
* Idempotent: a second call after the maps were cleared is a no-op
|
|
379
|
+
* (architecture-principles §6 idempotency). Each `.destroy()` call
|
|
380
|
+
* routes through the RHI shim's per-handle bookkeeping, so a stray
|
|
381
|
+
* second-destroy on a handle the runtime did not flip to destroyed
|
|
382
|
+
* surfaces as the structured `'destroy-after-destroy'` error from the
|
|
383
|
+
* RHI shim; that error is *not* re-thrown here -- destroyAll is a
|
|
384
|
+
* sweep that tolerates per-handle failures so the dispose chain can
|
|
385
|
+
* make progress (plan-strategy D-3 / D-8).
|
|
386
|
+
*/
|
|
387
|
+
destroyAll(): void {
|
|
388
|
+
if (this.meshGpuHandles.size > 0) this.meshResidencyEpoch += 1;
|
|
389
|
+
// The cubemap path registers two entries (sourceId + cubeId) sharing one
|
|
390
|
+
// GpuTexture wrapper, so destroyAll must dedupe on the wrapper identity
|
|
391
|
+
// before forwarding `.destroy()` -- otherwise the second call surfaces
|
|
392
|
+
// `'destroy-after-destroy'` from the RHI shim. The `isDestroyed` getter
|
|
393
|
+
// on the wrapper is the dedupe gate (architecture-principles §6).
|
|
394
|
+
const destroyTex = (gpuTex: GpuTexture): void => {
|
|
395
|
+
if (!gpuTex.isDestroyed) gpuTex.destroy();
|
|
396
|
+
};
|
|
397
|
+
const destroyBuf = (gpuBuf: GpuBuffer): void => {
|
|
398
|
+
if (!gpuBuf.isDestroyed) gpuBuf.destroy();
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
for (const entry of this.textureGpuHandles.values()) {
|
|
402
|
+
destroyTex(entry.texture);
|
|
403
|
+
}
|
|
404
|
+
if (this.textureGpuHandles.size > 0 || this.samplerGpuHandles.size > 0) {
|
|
405
|
+
this.materialResourceEpoch += 1;
|
|
406
|
+
}
|
|
407
|
+
this.textureGpuHandles.clear();
|
|
408
|
+
this.samplerGpuHandles.clear();
|
|
409
|
+
|
|
410
|
+
for (const entry of this.cubemapGpuHandles.values()) {
|
|
411
|
+
if (entry.texture !== null) destroyTex(entry.texture);
|
|
412
|
+
}
|
|
413
|
+
this.cubemapGpuHandles.clear();
|
|
414
|
+
this.cubemapIdempotentMap.clear();
|
|
415
|
+
|
|
416
|
+
for (const entry of this.meshGpuHandles.values()) {
|
|
417
|
+
destroyBuf(entry.vertexBuffer);
|
|
418
|
+
if (entry.indexBuffer !== null) destroyBuf(entry.indexBuffer);
|
|
419
|
+
}
|
|
420
|
+
this.meshGpuHandles.clear();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Set the error registry channel (wired by createRenderer). evict /
|
|
425
|
+
* releaseUnreferenced fire structured errors here instead of throwing -
|
|
426
|
+
* sweeps continue past individual failures (feat-20260619 D-1/D-6).
|
|
427
|
+
*/
|
|
428
|
+
setErrorRegistry(registry: RhiErrorListenerRegistry): void {
|
|
429
|
+
this.errorRegistry = registry;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* evictTexture / evictMesh / evictCubemap — per-handle evict primitives.
|
|
434
|
+
*
|
|
435
|
+
* Reuses destroyAll's isDestroyed dedup logic + cubemap wrapper
|
|
436
|
+
* shared-dedup (feat-20260619 D-1). Returns {freed, errors} so callers
|
|
437
|
+
* can consume the aggregate result (D-3). Key not present -> no-op
|
|
438
|
+
* returning {freed:0, errors:[]}.
|
|
439
|
+
*/
|
|
440
|
+
evictTexture(
|
|
441
|
+
handle: Handle<'TextureAsset', 'shared'>,
|
|
442
|
+
worldId: number = 0,
|
|
443
|
+
): { freed: number; errors: RhiError[] } {
|
|
444
|
+
const id = this.worldKey(handleSlot(handle), worldId);
|
|
445
|
+
const entry = this.textureGpuHandles.get(id);
|
|
446
|
+
if (entry === undefined) return { freed: 0, errors: [] };
|
|
447
|
+
|
|
448
|
+
let freed = 0;
|
|
449
|
+
const errors: RhiError[] = [];
|
|
450
|
+
|
|
451
|
+
if (!entry.texture.isDestroyed) {
|
|
452
|
+
const r = entry.texture.destroy();
|
|
453
|
+
if (r.ok) {
|
|
454
|
+
freed = 1;
|
|
455
|
+
} else {
|
|
456
|
+
errors.push(r.error);
|
|
457
|
+
if (this.errorRegistry) this.errorRegistry.fire(r.error);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
this.textureGpuHandles.delete(id);
|
|
462
|
+
this.materialResourceEpoch += 1;
|
|
463
|
+
return { freed, errors };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
evictMesh(
|
|
467
|
+
handle: Handle<'MeshAsset', 'shared'>,
|
|
468
|
+
worldId: number = 0,
|
|
469
|
+
): { freed: number; errors: RhiError[] } {
|
|
470
|
+
const id = this.worldKey(handleSlot(handle), worldId);
|
|
471
|
+
const entry = this.meshGpuHandles.get(id);
|
|
472
|
+
if (entry === undefined) return { freed: 0, errors: [] };
|
|
473
|
+
|
|
474
|
+
let freed = 0;
|
|
475
|
+
const errors: RhiError[] = [];
|
|
476
|
+
|
|
477
|
+
const destroyBuf = (gpuBuf: GpuBuffer): void => {
|
|
478
|
+
if (!gpuBuf.isDestroyed) {
|
|
479
|
+
const r = gpuBuf.destroy();
|
|
480
|
+
if (r.ok) {
|
|
481
|
+
freed += 1;
|
|
482
|
+
} else {
|
|
483
|
+
errors.push(r.error);
|
|
484
|
+
if (this.errorRegistry) this.errorRegistry.fire(r.error);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
destroyBuf(entry.vertexBuffer);
|
|
490
|
+
if (entry.indexBuffer !== null) destroyBuf(entry.indexBuffer);
|
|
491
|
+
|
|
492
|
+
this.meshGpuHandles.delete(id);
|
|
493
|
+
this.meshResidencyEpoch += 1;
|
|
494
|
+
return { freed: freed > 0 ? 1 : 0, errors };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
evictCubemap(id: number): { freed: number; errors: RhiError[] } {
|
|
498
|
+
const entry = this.cubemapGpuHandles.get(id);
|
|
499
|
+
if (entry === undefined) return { freed: 0, errors: [] };
|
|
500
|
+
|
|
501
|
+
let freed = 0;
|
|
502
|
+
const errors: RhiError[] = [];
|
|
503
|
+
|
|
504
|
+
// cubemap wrapper shared-dedup (D-1): sourceId and cubeId may share one
|
|
505
|
+
// GpuTexture wrapper. The isDestroyed gate ensures the underlying RHI
|
|
506
|
+
// texture is destroyed at most once, even when both entries are evicted.
|
|
507
|
+
// A 'failed' entry holds no GPU texture (texture:null) — nothing to free.
|
|
508
|
+
if (entry.texture !== null && !entry.texture.isDestroyed) {
|
|
509
|
+
const r = entry.texture.destroy();
|
|
510
|
+
if (r.ok) {
|
|
511
|
+
freed = 1;
|
|
512
|
+
} else {
|
|
513
|
+
errors.push(r.error);
|
|
514
|
+
if (this.errorRegistry) this.errorRegistry.fire(r.error);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
this.cubemapGpuHandles.delete(id);
|
|
519
|
+
return { freed, errors };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* releaseUnreferenced — iterate the three handle maps and evict entries
|
|
524
|
+
* whose key is NOT in `liveSet`.
|
|
525
|
+
*
|
|
526
|
+
* Iterates Map keys (not liveSet — store has no reverse index, D-8).
|
|
527
|
+
* IDs in liveSet that don't exist in the store are naturally ignored.
|
|
528
|
+
* Empty liveSet -> full release; second call -> no-op (maps empty,
|
|
529
|
+
* evict primitives are key-not-present no-op).
|
|
530
|
+
*/
|
|
531
|
+
releaseUnreferenced(liveSet: Set<number>): { freed: number; errors: RhiError[] } {
|
|
532
|
+
let freed = 0;
|
|
533
|
+
const errors: RhiError[] = [];
|
|
534
|
+
let meshResidencyChanged = false;
|
|
535
|
+
let materialResidencyChanged = false;
|
|
536
|
+
|
|
537
|
+
for (const key of this.textureGpuHandles.keys()) {
|
|
538
|
+
if (!liveSet.has(key)) {
|
|
539
|
+
const entry = this.textureGpuHandles.get(key);
|
|
540
|
+
if (entry !== undefined) {
|
|
541
|
+
if (!entry.texture.isDestroyed) {
|
|
542
|
+
const r = entry.texture.destroy();
|
|
543
|
+
if (r.ok) {
|
|
544
|
+
freed += 1;
|
|
545
|
+
} else {
|
|
546
|
+
errors.push(r.error);
|
|
547
|
+
if (this.errorRegistry) this.errorRegistry.fire(r.error);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
this.textureGpuHandles.delete(key);
|
|
551
|
+
materialResidencyChanged = true;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
for (const key of this.samplerGpuHandles.keys()) {
|
|
557
|
+
if (!liveSet.has(key)) {
|
|
558
|
+
this.samplerGpuHandles.delete(key);
|
|
559
|
+
materialResidencyChanged = true;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
for (const key of this.cubemapGpuHandles.keys()) {
|
|
564
|
+
if (!liveSet.has(key)) {
|
|
565
|
+
const entry = this.cubemapGpuHandles.get(key);
|
|
566
|
+
if (entry !== undefined) {
|
|
567
|
+
// A 'failed' entry holds no GPU texture (texture:null) — nothing to free.
|
|
568
|
+
if (entry.texture !== null && !entry.texture.isDestroyed) {
|
|
569
|
+
const r = entry.texture.destroy();
|
|
570
|
+
if (r.ok) {
|
|
571
|
+
freed += 1;
|
|
572
|
+
} else {
|
|
573
|
+
errors.push(r.error);
|
|
574
|
+
if (this.errorRegistry) this.errorRegistry.fire(r.error);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
this.cubemapGpuHandles.delete(key);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
for (const key of this.meshGpuHandles.keys()) {
|
|
583
|
+
if (!liveSet.has(key)) {
|
|
584
|
+
const entry = this.meshGpuHandles.get(key);
|
|
585
|
+
if (entry !== undefined) {
|
|
586
|
+
if (!entry.vertexBuffer.isDestroyed) {
|
|
587
|
+
const r = entry.vertexBuffer.destroy();
|
|
588
|
+
if (r.ok) {
|
|
589
|
+
freed += 1;
|
|
590
|
+
} else {
|
|
591
|
+
errors.push(r.error);
|
|
592
|
+
if (this.errorRegistry) this.errorRegistry.fire(r.error);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (entry.indexBuffer !== null && !entry.indexBuffer.isDestroyed) {
|
|
596
|
+
const r = entry.indexBuffer.destroy();
|
|
597
|
+
if (r.ok) {
|
|
598
|
+
freed += 1;
|
|
599
|
+
} else {
|
|
600
|
+
errors.push(r.error);
|
|
601
|
+
if (this.errorRegistry) this.errorRegistry.fire(r.error);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
this.meshGpuHandles.delete(key);
|
|
605
|
+
meshResidencyChanged = true;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (meshResidencyChanged) this.meshResidencyEpoch += 1;
|
|
611
|
+
if (materialResidencyChanged) this.materialResourceEpoch += 1;
|
|
612
|
+
|
|
613
|
+
return { freed, errors };
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Prewarm the mipmap pipeline cache for the given texture formats (D-9).
|
|
618
|
+
* Called from `createRenderer.ready` (already async): builds the one-time
|
|
619
|
+
* mipmap shader module + per-format render pipeline into the mipmap-generator
|
|
620
|
+
* deviceCache so the per-texture mipmap blit at record-stage `ensureResident`
|
|
621
|
+
* is pure synchronous encoder work. A format absent from this list will make
|
|
622
|
+
* the sync `ensureResident` texture arm return a structured RhiError rather
|
|
623
|
+
* than lazily await a build (which would break the sync draw contract).
|
|
624
|
+
*/
|
|
625
|
+
async prewarmMipmapPipeline(
|
|
626
|
+
device: MipmapBlitDevice,
|
|
627
|
+
formats: readonly GPUTextureFormat[],
|
|
628
|
+
): Promise<Result<void, RhiError>> {
|
|
629
|
+
const factory = this.asyncCreateShaderModule;
|
|
630
|
+
if (factory === undefined) {
|
|
631
|
+
return err(
|
|
632
|
+
new RhiError({
|
|
633
|
+
code: 'rhi-not-available',
|
|
634
|
+
expected: 'asyncCreateShaderModule wired by configureGpuDevice before prewarm',
|
|
635
|
+
hint: 'call gpuStore.configureGpuDevice(device, packShaderFactory, registerCube) before prewarmMipmapPipeline',
|
|
636
|
+
}),
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
for (const format of formats) {
|
|
640
|
+
const res = await getOrCreateMipmapPipeline(device, format, factory);
|
|
641
|
+
if (!res.ok) {
|
|
642
|
+
return err(
|
|
643
|
+
new RhiError({
|
|
644
|
+
code: 'rhi-not-available',
|
|
645
|
+
expected: `mipmap pipeline for format ${format} builds during prewarm`,
|
|
646
|
+
hint: `prewarmMipmapPipeline failed building the mipmap pipeline for ${format}; the format may be unsupported by the device`,
|
|
647
|
+
}),
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return ok(undefined);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Return the GpuTexture wrapper for a `Handle<TextureAsset>` if it has been
|
|
656
|
+
* made resident, else `undefined`.
|
|
657
|
+
*
|
|
658
|
+
* @internal — test-only seam for cow-survivor / AC-02 integration tests; not
|
|
659
|
+
* part of the engine's public API surface.
|
|
660
|
+
*/
|
|
661
|
+
_getTextureGpuTexture(
|
|
662
|
+
handle: Handle<'TextureAsset', 'shared'>,
|
|
663
|
+
worldId: number = 0,
|
|
664
|
+
): GpuTexture | undefined {
|
|
665
|
+
return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.texture;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Return the GPU texture-view for a `Handle<TextureAsset>` if it has been
|
|
670
|
+
* made resident, else `undefined`.
|
|
671
|
+
*/
|
|
672
|
+
getTextureGpuView(
|
|
673
|
+
handle: Handle<'TextureAsset', 'shared'>,
|
|
674
|
+
worldId: number = 0,
|
|
675
|
+
): TextureView | undefined {
|
|
676
|
+
return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.view;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Lazily materialise and cache a sampler descriptor for a material texture.
|
|
681
|
+
* Samplers are opaque, non-destroyable RHI handles, so disposal only drops
|
|
682
|
+
* this store's cache and lets the device own their lifetime.
|
|
683
|
+
*/
|
|
684
|
+
ensureSamplerResident(
|
|
685
|
+
handle: Handle<'SamplerAsset', 'shared'>,
|
|
686
|
+
pod: SamplerAsset,
|
|
687
|
+
worldId: number = 0,
|
|
688
|
+
): Result<Sampler, RhiError> {
|
|
689
|
+
const key = this.worldKey(handleSlot(handle), worldId);
|
|
690
|
+
const existing = this.samplerGpuHandles.get(key);
|
|
691
|
+
if (existing !== undefined) return ok(existing);
|
|
692
|
+
const device = this.gpuDevice as (MipmapBlitDevice & RhiDevice) | undefined;
|
|
693
|
+
if (device === undefined) {
|
|
694
|
+
return err(
|
|
695
|
+
new RhiError({
|
|
696
|
+
code: 'rhi-not-available',
|
|
697
|
+
expected: 'GPU device configured before SamplerAsset residency',
|
|
698
|
+
hint: 'call gpuStore.configureGpuDevice before drawing a material with a sampler',
|
|
699
|
+
}),
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
const { kind: _kind, ...descriptor } = pod;
|
|
703
|
+
const created = device.createSampler(descriptor);
|
|
704
|
+
if (!created.ok) return created;
|
|
705
|
+
this.samplerGpuHandles.set(key, created.value);
|
|
706
|
+
this.materialResourceEpoch += 1;
|
|
707
|
+
return created;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Return the GpuTexture wrapper for the cubemap, or `undefined` if not
|
|
712
|
+
* uploaded yet. Consumers that need the raw RHI Texture handle (e.g.
|
|
713
|
+
* `device.createTextureView` arguments) read `.handle` on the wrapper;
|
|
714
|
+
* `.destroy()` routes through the destroy chain (M-3 / w11).
|
|
715
|
+
*/
|
|
716
|
+
getCubemapGpuTexture(handle: Handle<'EquirectAsset', 'shared'>): GpuTexture | undefined {
|
|
717
|
+
// A 'failed' entry has texture:null; normalise to undefined (not resident).
|
|
718
|
+
return this.cubemapGpuHandles.get(handleSlot(handle))?.texture ?? undefined;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/** Return the full-cube texture view, or `undefined` if not uploaded yet. */
|
|
722
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture view
|
|
723
|
+
getCubemapGpuView(handle: Handle<'EquirectAsset', 'shared'>): any | undefined {
|
|
724
|
+
return this.cubemapGpuHandles.get(handleSlot(handle))?.view;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/** Return per-face 2D views (6 faces), or `undefined` if not uploaded yet. */
|
|
728
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture views
|
|
729
|
+
getCubemapFaceViews(handle: Handle<'EquirectAsset', 'shared'>): readonly any[] | undefined {
|
|
730
|
+
return this.cubemapGpuHandles.get(handleSlot(handle))?.faceViews;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Query the projection status for an equirect source handle (D-3 SSOT: the
|
|
735
|
+
* store's CubemapGpuEntry is the single authority). Returns `undefined` when
|
|
736
|
+
* no projection has been launched for this source yet (the record arm reads
|
|
737
|
+
* this to decide whether to fire the lazy projection; feat-20260630 M3 / w18):
|
|
738
|
+
* - undefined -> no entry: caller may launch projection (fire-and-forget)
|
|
739
|
+
* - 'pending' -> projection launched, not complete: bind white-cube fallback
|
|
740
|
+
* - 'ready' -> projected cube + IBL views live: bind real IBL
|
|
741
|
+
* - 'failed' -> projection errored: bind white fallback, do NOT retry (R-2)
|
|
742
|
+
*/
|
|
743
|
+
getCubemapStatus(
|
|
744
|
+
handle: Handle<'EquirectAsset', 'shared'>,
|
|
745
|
+
): 'pending' | 'ready' | 'failed' | undefined {
|
|
746
|
+
return this.cubemapGpuHandles.get(handleSlot(handle))?.status;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Return the GPU vertex / index buffer handles + index count for a
|
|
751
|
+
* `Handle<MeshAsset>` if resident, else `undefined`. Consumers treat this
|
|
752
|
+
* as the canonical "mesh asset has GPU residency" probe.
|
|
753
|
+
*/
|
|
754
|
+
getMeshGpuHandles(
|
|
755
|
+
handle: Handle<'MeshAsset', 'shared'>,
|
|
756
|
+
worldId: number = 0,
|
|
757
|
+
): MeshGpuEntry | undefined {
|
|
758
|
+
return this.meshGpuHandles.get(this.worldKey(handleSlot(handle), worldId));
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Pull-model residency: build the GPU resource for `handle` from the
|
|
763
|
+
* caller-provided POD on a miss, return the cached handles on a hit (D-2).
|
|
764
|
+
*
|
|
765
|
+
* - mesh POD -> synchronous vertex / index buffer upload (mirrors the
|
|
766
|
+
* pre-extraction `uploadMeshById`).
|
|
767
|
+
* - texture POD -> synchronous texture upload + mipmap blit (D-9). The
|
|
768
|
+
* mipmap pipeline MUST have been prewarmed via `prewarmMipmapPipeline`;
|
|
769
|
+
* an un-prewarmed format returns a structured RhiError (never awaits).
|
|
770
|
+
*
|
|
771
|
+
* Cubemap is NOT routed here -- it is an eager user call
|
|
772
|
+
* (`_uploadCubemapFromEquirect`). Builtin meshes are NOT routed here either
|
|
773
|
+
* (createRenderer step-3 owns them; D-1).
|
|
774
|
+
*/
|
|
775
|
+
ensureResident(
|
|
776
|
+
handle: Handle<'MeshAsset', 'shared'> | Handle<'TextureAsset', 'shared'>,
|
|
777
|
+
pod: TypesMeshAsset | TextureAsset,
|
|
778
|
+
worldId: number = 0,
|
|
779
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU handle entry union
|
|
780
|
+
): Result<any, RhiError | AssetError | ImageError> {
|
|
781
|
+
const id = handleSlot(handle);
|
|
782
|
+
// Hit: O(1) cache lookup, never re-projects (AC-09 -- deriveRenderData*
|
|
783
|
+
// runs only on a miss). The miss arms below dispatch on `pod.kind` with NO
|
|
784
|
+
// default: only the two GPU-resource kinds reachable here (mesh / texture)
|
|
785
|
+
// have arms, so a third reachable kind would surface as a `tsc -b`
|
|
786
|
+
// exhaustiveness error at the switch rather than a silent fallthrough
|
|
787
|
+
// (AC-06; the cube-texture kind is the eager `_uploadCubemapFromEquirect`
|
|
788
|
+
// path, not routed here).
|
|
789
|
+
switch (pod.kind) {
|
|
790
|
+
case 'mesh': {
|
|
791
|
+
const key = this.worldKey(id, worldId);
|
|
792
|
+
const existing = this.meshGpuHandles.get(key);
|
|
793
|
+
if (existing !== undefined) return ok(existing);
|
|
794
|
+
const projected = deriveRenderDataMesh(pod);
|
|
795
|
+
if (!projected.ok) return projected;
|
|
796
|
+
return this.uploadMeshById(key, pod, projected.value);
|
|
797
|
+
}
|
|
798
|
+
case 'texture': {
|
|
799
|
+
const texKey = this.worldKey(id, worldId);
|
|
800
|
+
const existing = this.textureGpuHandles.get(texKey);
|
|
801
|
+
if (existing !== undefined) return ok(existing);
|
|
802
|
+
const projected = deriveRenderDataTexture(pod);
|
|
803
|
+
if (!projected.ok) return projected;
|
|
804
|
+
const decoded: DecodedImage = decodedFromTexture(pod);
|
|
805
|
+
return this.uploadTextureSync(
|
|
806
|
+
handle as Handle<'TextureAsset', 'shared'>,
|
|
807
|
+
pod,
|
|
808
|
+
decoded,
|
|
809
|
+
projected.value,
|
|
810
|
+
worldId,
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Eager public surface: upload decoded image bytes into a GPU texture and
|
|
818
|
+
* cache (texture + view) under the handle. The TextureAsset POD supplies the
|
|
819
|
+
* GPU format (D-2: caller provides POD, store never reaches a registry); the
|
|
820
|
+
* DecodedImage supplies the pixel bytes + colorSpace. Retains the async
|
|
821
|
+
* signature so callers that have not prewarmed can drive a mipmap build here
|
|
822
|
+
* (the one async source).
|
|
823
|
+
*/
|
|
824
|
+
async uploadTexture(
|
|
825
|
+
handle: Handle<'TextureAsset', 'shared'>,
|
|
826
|
+
pod: TextureAsset,
|
|
827
|
+
decoded: DecodedImage,
|
|
828
|
+
worldId: number = 0,
|
|
829
|
+
): Promise<Result<void, AssetError | ImageError | RhiError>> {
|
|
830
|
+
const device = this.gpuDevice;
|
|
831
|
+
const projected = deriveRenderDataTexture(pod);
|
|
832
|
+
if (!projected.ok) return projected;
|
|
833
|
+
const prepared = this.prepareTextureUpload(handle, pod, decoded, projected.value);
|
|
834
|
+
if (!prepared.ok) return prepared;
|
|
835
|
+
if (device === undefined) return ok(undefined);
|
|
836
|
+
const { tex, levels, gpuTexture } = prepared.value;
|
|
837
|
+
const cacheKey = this.worldKey(handleSlot(handle), worldId);
|
|
838
|
+
if (gpuTexture === undefined) return ok(undefined);
|
|
839
|
+
|
|
840
|
+
if (!projected.value.compressed && decoded.mipmap === true && levels > 1) {
|
|
841
|
+
const factory = this.asyncCreateShaderModule;
|
|
842
|
+
if (factory === undefined) {
|
|
843
|
+
return err(
|
|
844
|
+
new RhiError({
|
|
845
|
+
code: 'rhi-not-available',
|
|
846
|
+
expected: 'asyncCreateShaderModule wired by configureGpuDevice',
|
|
847
|
+
hint: 'GpuResourceStore.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
|
+
}),
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
const pipelineRes = await getOrCreateMipmapPipeline(device, tex.format, factory);
|
|
852
|
+
if (!pipelineRes.ok) {
|
|
853
|
+
return err(
|
|
854
|
+
new RhiError({
|
|
855
|
+
code: 'rhi-not-available',
|
|
856
|
+
expected: `mipmap pipeline for format ${tex.format} builds`,
|
|
857
|
+
hint: 'GpuResourceStore.uploadTexture could not build the mipmap pipeline',
|
|
858
|
+
}),
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
const blitRes = blitMipmapsSync(device, gpuTexture, {
|
|
862
|
+
format: tex.format,
|
|
863
|
+
width: tex.width,
|
|
864
|
+
height: tex.height,
|
|
865
|
+
levels,
|
|
866
|
+
});
|
|
867
|
+
if (!blitRes.ok) return blitRes as Result<void, RhiError>;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const viewRes = device.createTextureView(gpuTexture, {
|
|
871
|
+
label: `texture-view-${cacheKey}`,
|
|
872
|
+
dimension: '2d',
|
|
873
|
+
});
|
|
874
|
+
if (!viewRes.ok) return viewRes;
|
|
875
|
+
const previous = this.textureGpuHandles.get(cacheKey);
|
|
876
|
+
const next = {
|
|
877
|
+
texture: this.wrapTex(gpuTexture),
|
|
878
|
+
view: viewRes.value,
|
|
879
|
+
};
|
|
880
|
+
this.textureGpuHandles.set(cacheKey, next);
|
|
881
|
+
this.materialResourceEpoch += 1;
|
|
882
|
+
if (
|
|
883
|
+
previous !== undefined &&
|
|
884
|
+
previous.texture !== next.texture &&
|
|
885
|
+
!previous.texture.isDestroyed
|
|
886
|
+
) {
|
|
887
|
+
previous.texture.destroy();
|
|
888
|
+
}
|
|
889
|
+
return ok(undefined);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Synchronous texture upload used by `ensureResident` (D-9). Identical to
|
|
894
|
+
* `uploadTexture` except the mipmap pipeline is read from the prewarmed
|
|
895
|
+
* cache via the synchronous `blitMipmapsSync` -- an un-prewarmed format
|
|
896
|
+
* returns a structured RhiError instead of awaiting a build.
|
|
897
|
+
*/
|
|
898
|
+
private uploadTextureSync(
|
|
899
|
+
handle: Handle<'TextureAsset', 'shared'>,
|
|
900
|
+
pod: TextureAsset,
|
|
901
|
+
decoded: DecodedImage,
|
|
902
|
+
renderData: TextureRenderData,
|
|
903
|
+
worldId: number = 0,
|
|
904
|
+
): Result<TextureGpuEntry, AssetError | ImageError | RhiError> {
|
|
905
|
+
const device = this.gpuDevice;
|
|
906
|
+
const prepared = this.prepareTextureUpload(handle, pod, decoded, renderData);
|
|
907
|
+
if (!prepared.ok) return prepared;
|
|
908
|
+
if (device === undefined || prepared.value.gpuTexture === undefined) {
|
|
909
|
+
return err(
|
|
910
|
+
new RhiError({
|
|
911
|
+
code: 'rhi-not-available',
|
|
912
|
+
expected: 'GpuResourceStore.configureGpuDevice wired before ensureResident',
|
|
913
|
+
hint: 'a texture ensureResident ran before the GPU device was wired; call gpuStore.configureGpuDevice in createRenderer',
|
|
914
|
+
}),
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
const { tex, levels, gpuTexture } = prepared.value;
|
|
918
|
+
const cacheKey = this.worldKey(handleSlot(handle), worldId);
|
|
919
|
+
|
|
920
|
+
if (!renderData.compressed && decoded.mipmap === true && levels > 1) {
|
|
921
|
+
const blitRes = blitMipmapsSync(device, gpuTexture, {
|
|
922
|
+
format: tex.format,
|
|
923
|
+
width: tex.width,
|
|
924
|
+
height: tex.height,
|
|
925
|
+
levels,
|
|
926
|
+
});
|
|
927
|
+
if (!blitRes.ok) return blitRes as Result<TextureGpuEntry, RhiError>;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const viewRes = device.createTextureView(gpuTexture, {
|
|
931
|
+
label: `texture-view-${cacheKey}`,
|
|
932
|
+
dimension: '2d',
|
|
933
|
+
});
|
|
934
|
+
if (!viewRes.ok) return viewRes;
|
|
935
|
+
const entry: TextureGpuEntry = { texture: this.wrapTex(gpuTexture), view: viewRes.value };
|
|
936
|
+
const previous = this.textureGpuHandles.get(cacheKey);
|
|
937
|
+
this.textureGpuHandles.set(cacheKey, entry);
|
|
938
|
+
this.materialResourceEpoch += 1;
|
|
939
|
+
if (
|
|
940
|
+
previous !== undefined &&
|
|
941
|
+
previous.texture !== entry.texture &&
|
|
942
|
+
!previous.texture.isDestroyed
|
|
943
|
+
) {
|
|
944
|
+
previous.texture.destroy();
|
|
945
|
+
}
|
|
946
|
+
return ok(entry);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Shared texture-upload prelude: format / colorSpace consistency assertion,
|
|
951
|
+
* GPU texture allocation + writeTexture. Returns `gpuTexture: undefined`
|
|
952
|
+
* (with ok) when no device is wired (deferred path).
|
|
953
|
+
*/
|
|
954
|
+
private prepareTextureUpload(
|
|
955
|
+
handle: Handle<'TextureAsset', 'shared'>,
|
|
956
|
+
pod: TextureAsset,
|
|
957
|
+
decoded: DecodedImage,
|
|
958
|
+
renderData: TextureRenderData,
|
|
959
|
+
): Result<
|
|
960
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
|
|
961
|
+
{ id: number; tex: TextureAsset; levels: number; gpuTexture: any | undefined },
|
|
962
|
+
AssetError | ImageError | RhiError
|
|
963
|
+
> {
|
|
964
|
+
const id = handleSlot(handle);
|
|
965
|
+
// The store does not hold the registry (D-2); the caller-provided POD is
|
|
966
|
+
// the GPU-format SSOT and `renderData` is its projection (format / usage /
|
|
967
|
+
// mipLevelCount). `decoded` supplies the pixel bytes + colorSpace; the
|
|
968
|
+
// assertion below guards the actual upload-source colorSpace against the
|
|
969
|
+
// projected format (the projection already checked the POD's own colorSpace).
|
|
970
|
+
const tex = pod;
|
|
971
|
+
const levels = renderData.mipLevelCount;
|
|
972
|
+
|
|
973
|
+
const formatExpected: 'srgb' | 'linear' = renderData.format.endsWith('-srgb')
|
|
974
|
+
? 'srgb'
|
|
975
|
+
: 'linear';
|
|
976
|
+
if (decoded.colorSpace !== formatExpected) {
|
|
977
|
+
const detail: ImageErrorDetailFor<'image-format-unsupported'> = {
|
|
978
|
+
code: 'image-format-unsupported',
|
|
979
|
+
actualMime: decoded.mime,
|
|
980
|
+
formatColorSpaceConflict: {
|
|
981
|
+
format: renderData.format,
|
|
982
|
+
colorSpace: decoded.colorSpace,
|
|
983
|
+
expected: formatExpected,
|
|
984
|
+
},
|
|
985
|
+
};
|
|
986
|
+
return err(makeImageError(detail));
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const device = this.gpuDevice;
|
|
990
|
+
if (device === undefined) {
|
|
991
|
+
return ok({ id, tex, levels, gpuTexture: undefined });
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// WebGL2/wgpu exposes texture limits that are lower than many source
|
|
995
|
+
// images (for example the 2085px LearnOpenGL Mars texture). Reject the
|
|
996
|
+
// upload before allocating a handle; binding a texture that the backend
|
|
997
|
+
// accepted syntactically but cannot use later surfaces only as a generic
|
|
998
|
+
// invalid-bind-group submit error.
|
|
999
|
+
const maxDimension =
|
|
1000
|
+
(device as unknown as { readonly limits?: { readonly maxTextureDimension2D?: number } })
|
|
1001
|
+
.limits?.maxTextureDimension2D ?? 16_384;
|
|
1002
|
+
if (tex.width > maxDimension || tex.height > maxDimension) {
|
|
1003
|
+
return err(
|
|
1004
|
+
makeImageError({
|
|
1005
|
+
code: 'image-dimension-out-of-bounds',
|
|
1006
|
+
requested: { width: tex.width, height: tex.height },
|
|
1007
|
+
limit: maxDimension,
|
|
1008
|
+
}),
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
const createTexRes = device.createTexture({
|
|
1013
|
+
label: `texture-${id}`,
|
|
1014
|
+
size: {
|
|
1015
|
+
width: renderData.physicalExtent.width,
|
|
1016
|
+
height: renderData.physicalExtent.height,
|
|
1017
|
+
depthOrArrayLayers: 1,
|
|
1018
|
+
},
|
|
1019
|
+
mipLevelCount: levels,
|
|
1020
|
+
sampleCount: 1,
|
|
1021
|
+
dimension: '2d',
|
|
1022
|
+
format: renderData.format,
|
|
1023
|
+
usage: renderData.usage,
|
|
1024
|
+
viewFormats: [],
|
|
1025
|
+
});
|
|
1026
|
+
if (!createTexRes.ok) return createTexRes;
|
|
1027
|
+
const gpuTexture = createTexRes.value;
|
|
1028
|
+
const queue = (device as unknown as MipmapBlitDeviceWithWriteTexture).queue;
|
|
1029
|
+
|
|
1030
|
+
if (renderData.compressed) {
|
|
1031
|
+
// feat-20260707 M5 / w36 (AC-08): block-compressed textures carry their
|
|
1032
|
+
// mip chain in `data` (offline-baked -- the GPU cannot generate compressed
|
|
1033
|
+
// mips). Upload each level with its own block-padded bytesPerRow /
|
|
1034
|
+
// rowsPerImage and mip-major byte offset (deriveMipUploadLayout, SSOT
|
|
1035
|
+
// block math). Full-subresource copies retain their logical size, including
|
|
1036
|
+
// non-block-aligned dimensions. queue.writeTexture avoids the 256 B
|
|
1037
|
+
// copyBufferToTexture alignment trap.
|
|
1038
|
+
const layout = deriveMipUploadLayout(renderData.format, tex.width, tex.height, levels);
|
|
1039
|
+
for (const lvl of layout) {
|
|
1040
|
+
const slice = decoded.bytes.subarray(lvl.byteOffset, lvl.byteOffset + lvl.byteLength);
|
|
1041
|
+
const writeRes = queue.writeTexture(
|
|
1042
|
+
{ texture: gpuTexture, mipLevel: lvl.level, origin: { x: 0, y: 0, z: 0 } },
|
|
1043
|
+
slice,
|
|
1044
|
+
{ offset: 0, bytesPerRow: lvl.bytesPerRow, rowsPerImage: lvl.rowsPerImage },
|
|
1045
|
+
{ width: lvl.copyWidth, height: lvl.copyHeight, depthOrArrayLayers: 1 },
|
|
1046
|
+
);
|
|
1047
|
+
if (!writeRes.ok) return writeRes;
|
|
1048
|
+
}
|
|
1049
|
+
return ok({ id, tex, levels, gpuTexture });
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// Uncompressed base-mip write; higher mips (when mipmap) are GPU-generated by
|
|
1053
|
+
// the caller's blit pass. rowsPerImage is the pixel height for linear RGBA.
|
|
1054
|
+
const bytesPerRow = renderData.bytesPerRow;
|
|
1055
|
+
const writeTextureRes = queue.writeTexture(
|
|
1056
|
+
{ texture: gpuTexture, mipLevel: 0, origin: { x: 0, y: 0, z: 0 } },
|
|
1057
|
+
decoded.bytes,
|
|
1058
|
+
{ offset: 0, bytesPerRow, rowsPerImage: tex.height },
|
|
1059
|
+
{ width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
|
|
1060
|
+
);
|
|
1061
|
+
if (!writeTextureRes.ok) return writeTextureRes;
|
|
1062
|
+
|
|
1063
|
+
return ok({ id, tex, levels, gpuTexture });
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Project an equirectangular HDR `EquirectAsset` into a GPU cubemap + IBL
|
|
1068
|
+
* precompute. NOT part of the `ensureResident` pull path.
|
|
1069
|
+
*
|
|
1070
|
+
* @internal — feat-20260630 D-3 / F-9: the cubemap projection is engine
|
|
1071
|
+
* internals, not a user surface. AI users declare `Skylight{equirect}` and
|
|
1072
|
+
* the render-system record arm (same package) drives this method per-frame;
|
|
1073
|
+
* the `_` prefix + `@internal` mark it package-internal (lint:internal gate)
|
|
1074
|
+
* so it never appears as a user-facing call. (A cross-file `private` is not
|
|
1075
|
+
* reachable by the record arm; package-internal is the correct visibility.)
|
|
1076
|
+
*
|
|
1077
|
+
* Idempotent: a second call with the same source handle returns the cached
|
|
1078
|
+
* cubemap handle (no second GPU texture). The minted cubemap handle is an
|
|
1079
|
+
* `EquirectAsset` shared ref catalogued via the injected `registerCube` relay
|
|
1080
|
+
* (D-3); the store never imports AssetRegistry.
|
|
1081
|
+
*
|
|
1082
|
+
* Status (D-3): every entry written carries `status`. A failed projection
|
|
1083
|
+
* records `status:'failed'` EXPLICITLY (R-2 / AC-09) so the caller never
|
|
1084
|
+
* retries by inferring "no entry => try again".
|
|
1085
|
+
*/
|
|
1086
|
+
async _uploadCubemapFromEquirect(
|
|
1087
|
+
world: World,
|
|
1088
|
+
sourceHandle: Handle<'EquirectAsset', 'shared'>,
|
|
1089
|
+
sourcePod: EquirectAsset,
|
|
1090
|
+
): Promise<Result<Handle<'EquirectAsset', 'shared'>, AssetError | RhiError>> {
|
|
1091
|
+
const sourceId = handleSlot(sourceHandle);
|
|
1092
|
+
|
|
1093
|
+
const existing = this.cubemapIdempotentMap.get(sourceId);
|
|
1094
|
+
if (existing !== undefined) {
|
|
1095
|
+
return ok(existing);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// R-2 / AC-09: a previously-failed projection is recorded EXPLICITLY as a
|
|
1099
|
+
// 'failed' entry. Short-circuit here so the record arm never retries the
|
|
1100
|
+
// upload every frame (the missing-key inference would loop forever).
|
|
1101
|
+
if (this.cubemapGpuHandles.get(sourceId)?.status === 'failed') {
|
|
1102
|
+
return err(
|
|
1103
|
+
new RhiError({
|
|
1104
|
+
code: 'feature-not-enabled',
|
|
1105
|
+
expected: 'a prior cubemap projection for this equirect source did not fail',
|
|
1106
|
+
hint: 'this equirect source already failed projection; the record arm must not retry (R-2). Inspect the original failure on the error channel',
|
|
1107
|
+
}),
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// feat-20260630 M3 / w18: a projection already in flight is marked
|
|
1112
|
+
// 'pending' synchronously below (before the first await). A re-entry while
|
|
1113
|
+
// pending short-circuits so the fire-and-forget record arm launches the
|
|
1114
|
+
// async projection exactly once per source (D-4 idempotent; the record arm
|
|
1115
|
+
// calls this every frame until status flips to ready/failed). The pending
|
|
1116
|
+
// entry holds no live GPU resources yet; the record arm binds the white
|
|
1117
|
+
// fallback while pending.
|
|
1118
|
+
if (this.cubemapGpuHandles.get(sourceId)?.status === 'pending') {
|
|
1119
|
+
return ok(sourceHandle);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Record a failed projection explicitly so a re-query short-circuits and the
|
|
1123
|
+
// record arm never retries every frame (R-2 / AC-09). The placeholder
|
|
1124
|
+
// texture wrapper is never bound (a 'failed' entry has no live GPU view).
|
|
1125
|
+
const recordFailed = <E>(error: E): Result<never, E> => {
|
|
1126
|
+
this.cubemapGpuHandles.set(sourceId, {
|
|
1127
|
+
status: 'failed',
|
|
1128
|
+
texture: null,
|
|
1129
|
+
view: undefined,
|
|
1130
|
+
faceViews: [],
|
|
1131
|
+
});
|
|
1132
|
+
return err(error);
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
const registerCube = this.registerCube;
|
|
1136
|
+
if (registerCube === undefined) {
|
|
1137
|
+
return recordFailed(
|
|
1138
|
+
makeAssetError({
|
|
1139
|
+
code: 'asset-not-found',
|
|
1140
|
+
expected: 'GpuResourceStore.configureGpuDevice wired with registerCube',
|
|
1141
|
+
hint: 'call gpuStore.configureGpuDevice(device, factory, registerCube) before _uploadCubemapFromEquirect',
|
|
1142
|
+
}),
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
if (sourcePod.kind !== 'equirect') {
|
|
1147
|
+
return recordFailed(
|
|
1148
|
+
makeAssetError({
|
|
1149
|
+
code: 'asset-not-found',
|
|
1150
|
+
expected: `source POD for handle id ${sourceId} is an EquirectAsset`,
|
|
1151
|
+
hint: ASSET_ERROR_HINTS['asset-not-found'],
|
|
1152
|
+
}),
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// feat-20260630 M3 / w18: mark the source 'pending' SYNCHRONOUSLY (this runs
|
|
1157
|
+
// before the first `await` below, so the fire-and-forget record arm that
|
|
1158
|
+
// never awaits still observes the pending entry on the next frame's
|
|
1159
|
+
// getCubemapStatus). All sync fail-fast gates (idempotent / failed / pending
|
|
1160
|
+
// / registerCube / caps / kind) have passed, so the projection is committed;
|
|
1161
|
+
// a re-entry while async work is in flight short-circuits on the 'pending'
|
|
1162
|
+
// guard above. The entry holds no live GPU resources yet (record binds the
|
|
1163
|
+
// white fallback while pending); the texture/views are filled in below when
|
|
1164
|
+
// the projection completes (status flips to 'ready'), or replaced by a
|
|
1165
|
+
// 'failed' entry via recordFailed if any later step errors.
|
|
1166
|
+
this.cubemapGpuHandles.set(sourceId, {
|
|
1167
|
+
status: 'pending',
|
|
1168
|
+
texture: null,
|
|
1169
|
+
view: undefined,
|
|
1170
|
+
faceViews: [],
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
// The cubemap projection + IBL precompute consume a 2D-image view of the
|
|
1174
|
+
// source. EquirectAsset mirrors the TextureAsset 2D surface (width / height
|
|
1175
|
+
// / format / data / colorSpace) but has no CPU mip chain, so the derived
|
|
1176
|
+
// texture view declares `mipmap:false` (the IBL prefilter mip chain is a
|
|
1177
|
+
// GPU-side pass, not a CPU-authored level set).
|
|
1178
|
+
const sourceTex: TextureAsset = {
|
|
1179
|
+
kind: 'texture',
|
|
1180
|
+
width: sourcePod.width,
|
|
1181
|
+
height: sourcePod.height,
|
|
1182
|
+
format: sourcePod.format,
|
|
1183
|
+
data: sourcePod.data,
|
|
1184
|
+
colorSpace: sourcePod.colorSpace,
|
|
1185
|
+
mipmap: false,
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
// Project the equirect source POD into the cubemap descriptor (D-5): the
|
|
1189
|
+
// asset-semantic decisions (format / colorSpace validation, square cube
|
|
1190
|
+
// face size, rgba32f->rgba16f narrowing) live in the pure projection; the
|
|
1191
|
+
// resource build (texture / views / IBL precompute) + the byte conversion
|
|
1192
|
+
// stay here in the store.
|
|
1193
|
+
const projected: CubeRenderData | undefined = (() => {
|
|
1194
|
+
const p = deriveRenderDataCubemap(sourceTex);
|
|
1195
|
+
return p.ok ? p.value : undefined;
|
|
1196
|
+
})();
|
|
1197
|
+
if (projected === undefined) {
|
|
1198
|
+
return recordFailed(
|
|
1199
|
+
makeAssetError({
|
|
1200
|
+
code: 'invalid-source-format',
|
|
1201
|
+
expected: "format 'rgba16float' or 'rgba32float' with colorSpace 'linear'",
|
|
1202
|
+
hint: ASSET_ERROR_HINTS['invalid-source-format'],
|
|
1203
|
+
}),
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const tex: TextureAsset = projected.needsHalfConversion
|
|
1208
|
+
? {
|
|
1209
|
+
kind: 'texture',
|
|
1210
|
+
width: sourceTex.width,
|
|
1211
|
+
height: sourceTex.height,
|
|
1212
|
+
format: projected.outputFormat,
|
|
1213
|
+
data: halfFloat.f32ToF16Bytes(
|
|
1214
|
+
sourceTex.data instanceof Uint8ClampedArray
|
|
1215
|
+
? new Uint8Array(
|
|
1216
|
+
sourceTex.data.buffer,
|
|
1217
|
+
sourceTex.data.byteOffset,
|
|
1218
|
+
sourceTex.data.byteLength,
|
|
1219
|
+
)
|
|
1220
|
+
: sourceTex.data,
|
|
1221
|
+
),
|
|
1222
|
+
colorSpace: 'linear',
|
|
1223
|
+
mipmap: false,
|
|
1224
|
+
}
|
|
1225
|
+
: sourceTex;
|
|
1226
|
+
|
|
1227
|
+
const cubeFaceSize = projected.cubeFaceSize;
|
|
1228
|
+
// The source equirect stays float for filtering, but WebGL2 cannot render
|
|
1229
|
+
// rgba16float attachments. Downlevel only the precompute outputs to the
|
|
1230
|
+
// universally renderable rgba8 path; the same format is threaded through
|
|
1231
|
+
// the cubemap and all three IBL side textures by IblPipelineCache.
|
|
1232
|
+
const outputFormat = this.caps?.rgba16floatRenderable === false ? 'rgba8unorm' : tex.format;
|
|
1233
|
+
|
|
1234
|
+
// Lazy projection runs from the record arm, where the device is always
|
|
1235
|
+
// wired (research R-5). The device-not-wired queue + placeholder cube path
|
|
1236
|
+
// is removed (D-7); a missing device here is a fail-fast, not a queue.
|
|
1237
|
+
const device = this.gpuDevice;
|
|
1238
|
+
if (device === undefined) {
|
|
1239
|
+
return recordFailed(
|
|
1240
|
+
new RhiError({
|
|
1241
|
+
code: 'rhi-not-available',
|
|
1242
|
+
expected: 'GpuResourceStore.configureGpuDevice wired before cubemap projection',
|
|
1243
|
+
hint: 'a cubemap projection ran before the GPU device was wired; the record arm projects only after the device is configured',
|
|
1244
|
+
}),
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Usage flags for the small equirect helper texture (sampled source for the
|
|
1249
|
+
// IBL precompute pass); the cube texture's usage comes from the projection.
|
|
1250
|
+
// biome-ignore lint/suspicious/noExplicitAny: union of shim/raw return shapes
|
|
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({
|
|
1273
|
+
label: `cubemap-${sourceId}`,
|
|
1274
|
+
size: { width: cubeFaceSize, height: cubeFaceSize, depthOrArrayLayers: 6 },
|
|
1275
|
+
mipLevelCount: 1,
|
|
1276
|
+
sampleCount: 1,
|
|
1277
|
+
dimension: '2d',
|
|
1278
|
+
format: outputFormat,
|
|
1279
|
+
usage: projected.cubeUsage,
|
|
1280
|
+
viewFormats: [],
|
|
1281
|
+
});
|
|
1282
|
+
const gpuTexture = unwrap(gpuTextureRet);
|
|
1283
|
+
if (gpuTexture === undefined) {
|
|
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
|
+
}
|
|
1292
|
+
|
|
1293
|
+
const cubeView = makeTextureView(device, gpuTexture, {
|
|
1294
|
+
label: `cubemap-view-${sourceId}`,
|
|
1295
|
+
dimension: 'cube',
|
|
1296
|
+
arrayLayerCount: 6,
|
|
1297
|
+
});
|
|
1298
|
+
if (cubeView === undefined) {
|
|
1299
|
+
return recordFailed(
|
|
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
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture views
|
|
1309
|
+
const faceViews: any[] = [];
|
|
1310
|
+
for (let face = 0; face < 6; face++) {
|
|
1311
|
+
const fv = makeTextureView(device, gpuTexture, {
|
|
1312
|
+
label: `cubemap-face-${sourceId}-${face}`,
|
|
1313
|
+
dimension: '2d',
|
|
1314
|
+
baseArrayLayer: face,
|
|
1315
|
+
arrayLayerCount: 1,
|
|
1316
|
+
});
|
|
1317
|
+
if (fv === undefined) {
|
|
1318
|
+
return recordFailed(
|
|
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);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// M-3 / w11: a single GpuTexture wrapper is shared between sourceId
|
|
1330
|
+
// and cubeId entries (the underlying RHI handle is the same physical
|
|
1331
|
+
// texture). destroyAll() walks each entry, so attempting to destroy
|
|
1332
|
+
// twice via two wrappers would surface 'destroy-after-destroy' on
|
|
1333
|
+
// the second pass; sharing one wrapper keeps the destroy chain
|
|
1334
|
+
// surfacing exactly one destroy call per physical resource.
|
|
1335
|
+
const gpuTextureWrapper = this.wrapTex(gpuTexture);
|
|
1336
|
+
|
|
1337
|
+
this.cubemapGpuHandles.set(sourceId, {
|
|
1338
|
+
status: 'ready',
|
|
1339
|
+
texture: gpuTextureWrapper,
|
|
1340
|
+
view: cubeView,
|
|
1341
|
+
faceViews,
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1344
|
+
// The minted cubemap handle is a synthetic shared ref (identity token for
|
|
1345
|
+
// the GPU cube residency); its POD is an EquirectAsset placeholder matching
|
|
1346
|
+
// the projected dimensions/format (D-3: the retired cube-texture asset kind
|
|
1347
|
+
// is gone; the relay mints an EquirectAsset shared ref instead).
|
|
1348
|
+
const cubeAsset: EquirectAsset = {
|
|
1349
|
+
kind: 'equirect',
|
|
1350
|
+
width: cubeFaceSize,
|
|
1351
|
+
height: cubeFaceSize,
|
|
1352
|
+
format: tex.format,
|
|
1353
|
+
data: tex.data,
|
|
1354
|
+
colorSpace: 'linear',
|
|
1355
|
+
};
|
|
1356
|
+
const regResult = registerCube(world, cubeAsset);
|
|
1357
|
+
if (!regResult.ok) return recordFailed(regResult.error);
|
|
1358
|
+
const cubeHandle = regResult.value;
|
|
1359
|
+
const cubeId = handleSlot(cubeHandle);
|
|
1360
|
+
|
|
1361
|
+
this.cubemapGpuHandles.set(cubeId, {
|
|
1362
|
+
status: 'ready',
|
|
1363
|
+
texture: gpuTextureWrapper,
|
|
1364
|
+
view: cubeView,
|
|
1365
|
+
faceViews,
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
this.cubemapIdempotentMap.set(sourceId, cubeHandle);
|
|
1369
|
+
|
|
1370
|
+
if (this.asyncCreateShaderModule !== undefined) {
|
|
1371
|
+
// biome-ignore lint/suspicious/noExplicitAny: device shim narrowed at runtime
|
|
1372
|
+
const ipDevice = device as any;
|
|
1373
|
+
const pipelinesRes = await createIblPipelines(
|
|
1374
|
+
ipDevice,
|
|
1375
|
+
// biome-ignore lint/suspicious/noExplicitAny: factory signatures are interoperable; device shim is structural
|
|
1376
|
+
this.asyncCreateShaderModule as any,
|
|
1377
|
+
outputFormat,
|
|
1378
|
+
);
|
|
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
|
+
}
|
|
1454
|
+
|
|
1455
|
+
return ok(cubeHandle);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* Upload `MeshAsset.vertices` + `MeshAsset.indices` to GPU buffers and cache
|
|
1460
|
+
* the handles under the asset id. Synchronous; mirrors the pre-extraction
|
|
1461
|
+
* `uploadMeshById` byte-for-byte. Returns the cached entry on success.
|
|
1462
|
+
*/
|
|
1463
|
+
private uploadMeshById(
|
|
1464
|
+
id: number,
|
|
1465
|
+
mesh: TypesMeshAsset,
|
|
1466
|
+
renderData: MeshRenderData,
|
|
1467
|
+
): Result<MeshGpuEntry, RhiError> {
|
|
1468
|
+
const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
|
|
1469
|
+
if (device === undefined) {
|
|
1470
|
+
return err(
|
|
1471
|
+
new RhiError({
|
|
1472
|
+
code: 'rhi-not-available',
|
|
1473
|
+
expected: 'GpuResourceStore.configureGpuDevice wired before mesh ensureResident',
|
|
1474
|
+
hint: 'a mesh ensureResident ran before the GPU device was wired; call gpuStore.configureGpuDevice in createRenderer',
|
|
1475
|
+
}),
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
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
|
+
const indices = mesh.indices;
|
|
1493
|
+
const indexBytesUnpadded = indices === undefined ? 0 : indices.byteLength;
|
|
1494
|
+
const indexBytes = renderData.indexByteLength;
|
|
1495
|
+
|
|
1496
|
+
const vboRet = device.createBuffer({
|
|
1497
|
+
label: `mesh-${id}-vbo`,
|
|
1498
|
+
size: renderData.vertexByteLength,
|
|
1499
|
+
usage: renderData.vertexUsage,
|
|
1500
|
+
mappedAtCreation: false,
|
|
1501
|
+
});
|
|
1502
|
+
const vbo = unwrapBuffer(vboRet);
|
|
1503
|
+
if (vbo === undefined) {
|
|
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
|
+
}
|
|
1512
|
+
// Vertex-only mesh: skip the index buffer entirely. The indexed path below
|
|
1513
|
+
// is unchanged byte-for-byte when `indices` is present.
|
|
1514
|
+
let ibo: unknown = null;
|
|
1515
|
+
if (indices !== undefined) {
|
|
1516
|
+
const iboRet = device.createBuffer({
|
|
1517
|
+
label: `mesh-${id}-ibo`,
|
|
1518
|
+
size: indexBytes,
|
|
1519
|
+
usage: renderData.indexUsage,
|
|
1520
|
+
mappedAtCreation: false,
|
|
1521
|
+
});
|
|
1522
|
+
ibo = unwrapBuffer(iboRet);
|
|
1523
|
+
if (ibo === undefined) {
|
|
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
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
const vboWriteRet = device.queue.writeBuffer(vbo, 0, mesh.vertices);
|
|
1535
|
+
if (!writeOk(vboWriteRet)) {
|
|
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
|
+
}
|
|
1544
|
+
|
|
1545
|
+
if (indices !== undefined) {
|
|
1546
|
+
const indexSrc = new Uint8Array(indexBytes);
|
|
1547
|
+
indexSrc.set(new Uint8Array(indices.buffer, indices.byteOffset, indexBytesUnpadded));
|
|
1548
|
+
const iboWriteRet = device.queue.writeBuffer(ibo, 0, indexSrc);
|
|
1549
|
+
if (!writeOk(iboWriteRet)) {
|
|
1550
|
+
return err(
|
|
1551
|
+
new RhiError({
|
|
1552
|
+
code: 'webgpu-runtime-error',
|
|
1553
|
+
expected: `queue.writeBuffer mesh-${id}-ibo to succeed`,
|
|
1554
|
+
hint: 'mesh index buffer write failed',
|
|
1555
|
+
}),
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
const layoutProjection = renderData.layoutProjection;
|
|
1561
|
+
const strideBytes = meshStrideBytes(layoutProjection);
|
|
1562
|
+
const entry: MeshGpuEntry = {
|
|
1563
|
+
vertexBuffer: this.wrapBuf(vbo as Buffer),
|
|
1564
|
+
indexBuffer: ibo === null ? null : this.wrapBuf(ibo as Buffer),
|
|
1565
|
+
vboBytes: renderData.vertexByteLength,
|
|
1566
|
+
iboBytes: indices === undefined ? 0 : indexBytes,
|
|
1567
|
+
indexCount: renderData.indexCount,
|
|
1568
|
+
indexFormat: renderData.indexFormat,
|
|
1569
|
+
vertexCount: mesh.vertices.byteLength / strideBytes,
|
|
1570
|
+
indexed: indices !== undefined,
|
|
1571
|
+
topology: renderData.submeshes[0]?.topology ?? 'triangle-list',
|
|
1572
|
+
submeshes: renderData.submeshes,
|
|
1573
|
+
layoutProjection,
|
|
1574
|
+
};
|
|
1575
|
+
this.meshGpuHandles.set(id, entry);
|
|
1576
|
+
this.meshResidencyEpoch += 1;
|
|
1577
|
+
return ok(entry);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/**
|
|
1581
|
+
* Update an existing resident mesh without changing its shape or metadata.
|
|
1582
|
+
* A shape change must use a new asset handle; this method never reallocates
|
|
1583
|
+
* or silently changes the draw contract.
|
|
1584
|
+
*/
|
|
1585
|
+
private updateMeshById(
|
|
1586
|
+
id: number,
|
|
1587
|
+
newVertices: Float32Array,
|
|
1588
|
+
newIndices: Uint16Array | Uint32Array | undefined,
|
|
1589
|
+
submeshes?: readonly Submesh[],
|
|
1590
|
+
): Result<void, MeshUpdateErrorType | RhiError> {
|
|
1591
|
+
const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
|
|
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
|
+
}
|
|
1601
|
+
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
|
+
}
|
|
1610
|
+
|
|
1611
|
+
const strideBytes = meshStrideBytes(entry.layoutProjection);
|
|
1612
|
+
const expectedVertexBytes = entry.vertexCount * strideBytes;
|
|
1613
|
+
const shapeMismatch = (expected: string, actual: string) =>
|
|
1614
|
+
err(
|
|
1615
|
+
new MeshUpdateError({
|
|
1616
|
+
code: 'mesh-shape-mismatch',
|
|
1617
|
+
detail: { handle: id, expected, actual },
|
|
1618
|
+
}),
|
|
1619
|
+
);
|
|
1620
|
+
if (
|
|
1621
|
+
newVertices.byteLength !== expectedVertexBytes ||
|
|
1622
|
+
newVertices.byteLength % strideBytes !== 0
|
|
1623
|
+
) {
|
|
1624
|
+
return shapeMismatch(
|
|
1625
|
+
`${expectedVertexBytes} vertex bytes at ${strideBytes}-byte stride`,
|
|
1626
|
+
`${newVertices.byteLength} vertex bytes`,
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
if (entry.indexed !== (newIndices !== undefined)) {
|
|
1631
|
+
return shapeMismatch(
|
|
1632
|
+
entry.indexed ? 'indexed mesh data' : 'vertex-only mesh data',
|
|
1633
|
+
newIndices === undefined ? 'vertex-only mesh data' : 'indexed mesh data',
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
if (newIndices !== undefined) {
|
|
1637
|
+
const indexFormat = newIndices instanceof Uint32Array ? 'uint32' : 'uint16';
|
|
1638
|
+
const indexBytes = ((newIndices.byteLength + 3) >> 2) << 2;
|
|
1639
|
+
if (
|
|
1640
|
+
entry.indexBuffer === null ||
|
|
1641
|
+
indexFormat !== entry.indexFormat ||
|
|
1642
|
+
newIndices.length !== entry.indexCount ||
|
|
1643
|
+
indexBytes !== entry.iboBytes
|
|
1644
|
+
) {
|
|
1645
|
+
return shapeMismatch(
|
|
1646
|
+
`${entry.indexFormat} index format with ${entry.indexCount} indices`,
|
|
1647
|
+
`${indexFormat} index format with ${newIndices.length} indices`,
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
} else if (entry.indexBuffer !== null) {
|
|
1651
|
+
return shapeMismatch('indexed mesh data', 'vertex-only mesh data');
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
const nextSubmeshes = submeshes ?? entry.submeshes;
|
|
1655
|
+
if (!submeshesEqual(entry.submeshes, nextSubmeshes)) {
|
|
1656
|
+
return shapeMismatch('unchanged submesh descriptors', 'different submesh descriptors');
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
const vertexWrite = device.queue.writeBuffer(entry.vertexBuffer.handle, 0, newVertices);
|
|
1660
|
+
if (!vertexWrite.ok) return err(vertexWrite.error);
|
|
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
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Internal explicit rebuild path used when an owner has deliberately
|
|
1673
|
+
* replaced a mesh shape. Public callers should register a new MeshAsset
|
|
1674
|
+
* handle so CPU recovery data and GPU residency remain one authority.
|
|
1675
|
+
*/
|
|
1676
|
+
private replaceMeshById(
|
|
1677
|
+
id: number,
|
|
1678
|
+
newVertices: Float32Array,
|
|
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,
|
|
1780
|
+
vboBytes: newVertexBytes,
|
|
1781
|
+
iboBytes: newIndexBytes,
|
|
1782
|
+
indexCount: newIndices?.length ?? 0,
|
|
1783
|
+
indexFormat: newIndices instanceof Uint32Array ? 'uint32' : 'uint16',
|
|
1784
|
+
layoutProjection: entry.layoutProjection,
|
|
1785
|
+
vertexCount: newVertexBytes / strideBytes,
|
|
1786
|
+
indexed: newIndices !== undefined,
|
|
1787
|
+
topology: nextSubmeshes[0]?.topology ?? entry.topology,
|
|
1788
|
+
submeshes: nextSubmeshes,
|
|
1789
|
+
};
|
|
1790
|
+
this.meshGpuHandles.set(id, nextEntry);
|
|
1791
|
+
this.meshResidencyEpoch += 1;
|
|
1792
|
+
this.destroyMeshBuffer(entry.vertexBuffer);
|
|
1793
|
+
if (entry.indexBuffer !== null) this.destroyMeshBuffer(entry.indexBuffer);
|
|
1794
|
+
return ok(undefined);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
/**
|
|
1798
|
+
* Public surface for updating an existing unmanaged mesh handle's GPU buffer
|
|
1799
|
+
* data in-place. The handle must have been made resident.
|
|
1800
|
+
*/
|
|
1801
|
+
updateMesh(
|
|
1802
|
+
handle: Handle<'MeshAsset', 'shared'>,
|
|
1803
|
+
newVertices: Float32Array,
|
|
1804
|
+
newIndices: Uint16Array | Uint32Array | undefined,
|
|
1805
|
+
worldId: number = 0,
|
|
1806
|
+
submeshes?: readonly Submesh[],
|
|
1807
|
+
): Result<void, MeshUpdateErrorType | RhiError> {
|
|
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> {
|
|
1832
|
+
const id = this.worldKey(handleSlot(handle), worldId);
|
|
1833
|
+
const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
|
|
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);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
/**
|
|
1903
|
+
* Synthesize the `DecodedImage` view the texture-upload prelude consumes from
|
|
1904
|
+
* a `TextureAsset` POD (the record-stage pull path holds only the POD). The
|
|
1905
|
+
* POD's `data` is the pixel bytes; `colorSpace` + `mipmap` drive the
|
|
1906
|
+
* consistency assertion + mip count.
|
|
1907
|
+
*/
|
|
1908
|
+
function decodedFromTexture(tex: TextureAsset): DecodedImage {
|
|
1909
|
+
const bytes =
|
|
1910
|
+
tex.data instanceof Uint8ClampedArray
|
|
1911
|
+
? new Uint8Array(tex.data.buffer, tex.data.byteOffset, tex.data.byteLength)
|
|
1912
|
+
: tex.data;
|
|
1913
|
+
return {
|
|
1914
|
+
bytes,
|
|
1915
|
+
width: tex.width,
|
|
1916
|
+
height: tex.height,
|
|
1917
|
+
mime: 'image/png',
|
|
1918
|
+
colorSpace: tex.colorSpace,
|
|
1919
|
+
mipmap: tex.mipmap,
|
|
1920
|
+
};
|
|
1921
|
+
}
|