@forgeax/engine-app 0.0.0-dev.8d955ade1c79
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 +424 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/animation-asset-lookup.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-asset-lookup.unit.test.d.ts.map +1 -0
- package/dist/__tests__/callback-deletion.unit.test.d.ts +2 -0
- package/dist/__tests__/callback-deletion.unit.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__/component-introspection.unit.test.d.ts +2 -0
- package/dist/__tests__/component-introspection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/consumer-migration.unit.test.d.ts +2 -0
- package/dist/__tests__/consumer-migration.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-error-retention.integration.test.d.ts +2 -0
- package/dist/__tests__/create-app-error-retention.integration.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-features.test.d.ts +2 -0
- package/dist/__tests__/create-app-features.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-listener-sync.test.d.ts +2 -0
- package/dist/__tests__/create-app-listener-sync.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-lock-provider.test.d.ts +2 -0
- package/dist/__tests__/create-app-lock-provider.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-physics-plugin-failure.integration.test.d.ts +2 -0
- package/dist/__tests__/create-app-physics-plugin-failure.integration.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-plugin.test.d.ts +2 -0
- package/dist/__tests__/create-app-plugin.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-stop.test.d.ts +2 -0
- package/dist/__tests__/create-app-stop.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-time.unit.test.d.ts +2 -0
- package/dist/__tests__/create-app-time.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-app.test-d.d.ts +2 -0
- package/dist/__tests__/create-app.test-d.d.ts.map +1 -0
- package/dist/__tests__/create-app.test.d.ts +2 -0
- package/dist/__tests__/create-app.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-error.integration.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-error.integration.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts.map +1 -0
- package/dist/__tests__/docs-gate-green.unit.test.d.ts +2 -0
- package/dist/__tests__/docs-gate-green.unit.test.d.ts.map +1 -0
- package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts +2 -0
- package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts.map +1 -0
- package/dist/__tests__/draw-source-single-world-regression.test.d.ts +2 -0
- package/dist/__tests__/draw-source-single-world-regression.test.d.ts.map +1 -0
- package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts +2 -0
- package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts.map +1 -0
- package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts +2 -0
- package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors-pointer-lock-failed.test.d.ts +2 -0
- package/dist/__tests__/errors-pointer-lock-failed.test.d.ts.map +1 -0
- package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-error-detail.test-d.d.ts +2 -0
- package/dist/__tests__/execution-error-detail.test-d.d.ts.map +1 -0
- package/dist/__tests__/execution-error-detail.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-error-detail.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-frame-order.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-frame-order.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-frame-protocol.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-frame-protocol.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-input-sample.contract.test.d.ts +2 -0
- package/dist/__tests__/execution-input-sample.contract.test.d.ts.map +1 -0
- package/dist/__tests__/execution-measurement.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-measurement.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-public-api.test-d.d.ts +2 -0
- package/dist/__tests__/execution-public-api.test-d.d.ts.map +1 -0
- package/dist/__tests__/execution-public-api.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-public-api.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-rebuild.contract.test.d.ts +2 -0
- package/dist/__tests__/execution-rebuild.contract.test.d.ts.map +1 -0
- package/dist/__tests__/execution-report.contract.test.d.ts +2 -0
- package/dist/__tests__/execution-report.contract.test.d.ts.map +1 -0
- package/dist/__tests__/execution-report.types.test-d.d.ts +2 -0
- package/dist/__tests__/execution-report.types.test-d.d.ts.map +1 -0
- package/dist/__tests__/execution-schema-freshness.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-schema-freshness.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-selector.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-selector.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-shared-kernel.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-shared-kernel.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fallback-camera.unit.test.d.ts +2 -0
- package/dist/__tests__/fallback-camera.unit.test.d.ts.map +1 -0
- package/dist/__tests__/frame-loop-time-elapsed.test.d.ts +2 -0
- package/dist/__tests__/frame-loop-time-elapsed.test.d.ts.map +1 -0
- package/dist/__tests__/frame-loop-time.unit.test.d.ts +2 -0
- package/dist/__tests__/frame-loop-time.unit.test.d.ts.map +1 -0
- package/dist/__tests__/frame-loop-world-array.test.d.ts +2 -0
- package/dist/__tests__/frame-loop-world-array.test.d.ts.map +1 -0
- package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts +2 -0
- package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts.map +1 -0
- package/dist/__tests__/migration-surface-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/migration-surface-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-policy.integration.test.d.ts +2 -0
- package/dist/__tests__/plugin-policy.integration.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-profiles.unit.test.d.ts +2 -0
- package/dist/__tests__/plugin-profiles.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-frame-token.test.d.ts +2 -0
- package/dist/__tests__/profiler-frame-token.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-remote-bridge.test.d.ts +2 -0
- package/dist/__tests__/profiler-remote-bridge.test.d.ts.map +1 -0
- package/dist/__tests__/remote-integration.test.d.ts +2 -0
- package/dist/__tests__/remote-integration.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-capture-capability.test-d.d.ts +2 -0
- package/dist/__tests__/rhi-capture-capability.test-d.d.ts.map +1 -0
- package/dist/__tests__/rhi-capture-host.integration.test.d.ts +2 -0
- package/dist/__tests__/rhi-capture-host.integration.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-capture.browser.test.d.ts +2 -0
- package/dist/__tests__/rhi-capture.browser.test.d.ts.map +1 -0
- package/dist/__tests__/surface-handoff.test.d.ts +2 -0
- package/dist/__tests__/surface-handoff.test.d.ts.map +1 -0
- package/dist/__tests__/types-m2.test-d.d.ts +2 -0
- package/dist/__tests__/types-m2.test-d.d.ts.map +1 -0
- package/dist/__tests__/visibility-remote.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-remote.integration.test.d.ts.map +1 -0
- package/dist/__tests__/worker-execution-stop.test.d.ts +2 -0
- package/dist/__tests__/worker-execution-stop.test.d.ts.map +1 -0
- package/dist/animation-asset-lookup.d.ts +7 -0
- package/dist/animation-asset-lookup.d.ts.map +1 -0
- package/dist/browser-frame-signal.d.ts +17 -0
- package/dist/browser-frame-signal.d.ts.map +1 -0
- package/dist/canvas-policy.d.ts +4 -0
- package/dist/canvas-policy.d.ts.map +1 -0
- package/dist/create-app.d.ts +78 -0
- package/dist/create-app.d.ts.map +1 -0
- package/dist/engine-worker-runtime.mjs +827 -0
- package/dist/engine-worker-runtime.mjs.map +1 -0
- package/dist/errors.d.ts +254 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/execution/attached-world-swap.d.ts +10 -0
- package/dist/execution/attached-world-swap.d.ts.map +1 -0
- package/dist/execution/bootstrap-entry.d.ts +30 -0
- package/dist/execution/bootstrap-entry.d.ts.map +1 -0
- package/dist/execution/bootstrap-url.d.ts +5 -0
- package/dist/execution/bootstrap-url.d.ts.map +1 -0
- package/dist/execution/capabilities.d.ts +5 -0
- package/dist/execution/capabilities.d.ts.map +1 -0
- package/dist/execution/control.d.ts +12 -0
- package/dist/execution/control.d.ts.map +1 -0
- package/dist/execution/engine-worker-runtime.d.ts +2 -0
- package/dist/execution/engine-worker-runtime.d.ts.map +1 -0
- package/dist/execution/engine-worker.d.ts +24 -0
- package/dist/execution/engine-worker.d.ts.map +1 -0
- package/dist/execution/host-controller.d.ts +16 -0
- package/dist/execution/host-controller.d.ts.map +1 -0
- package/dist/execution/index.d.ts +10 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/kernel-pool.d.ts +14 -0
- package/dist/execution/kernel-pool.d.ts.map +1 -0
- package/dist/execution/kernel-worker-runtime.d.ts +2 -0
- package/dist/execution/kernel-worker-runtime.d.ts.map +1 -0
- package/dist/execution/measurement.d.ts +7 -0
- package/dist/execution/measurement.d.ts.map +1 -0
- package/dist/execution/protocol.d.ts +87 -0
- package/dist/execution/protocol.d.ts.map +1 -0
- package/dist/execution/report.d.ts +5 -0
- package/dist/execution/report.d.ts.map +1 -0
- package/dist/execution/schema.d.ts +4 -0
- package/dist/execution/schema.d.ts.map +1 -0
- package/dist/execution/selector.d.ts +10 -0
- package/dist/execution/selector.d.ts.map +1 -0
- package/dist/execution/types.d.ts +123 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/fallback-camera.d.ts +8 -0
- package/dist/fallback-camera.d.ts.map +1 -0
- package/dist/game-context.d.ts +98 -0
- package/dist/game-context.d.ts.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +3980 -0
- package/dist/index.mjs.map +1 -0
- package/dist/input-map-plugin.d.ts +5 -0
- package/dist/input-map-plugin.d.ts.map +1 -0
- package/dist/input-plugin.d.ts +4 -0
- package/dist/input-plugin.d.ts.map +1 -0
- package/dist/internal/assembled-engine-profile.d.ts +5 -0
- package/dist/internal/assembled-engine-profile.d.ts.map +1 -0
- package/dist/internal/assets-world-plugin.d.ts +7 -0
- package/dist/internal/assets-world-plugin.d.ts.map +1 -0
- package/dist/internal/browser-remote-bridge.d.ts +24 -0
- package/dist/internal/browser-remote-bridge.d.ts.map +1 -0
- package/dist/internal/browser-rhi-debug-runtime.d.ts +18 -0
- package/dist/internal/browser-rhi-debug-runtime.d.ts.map +1 -0
- package/dist/internal/component-introspection.d.ts +24 -0
- package/dist/internal/component-introspection.d.ts.map +1 -0
- package/dist/internal/debug-draw.d.ts +9 -0
- package/dist/internal/debug-draw.d.ts.map +1 -0
- package/dist/internal/ecs-import.d.ts +6 -0
- package/dist/internal/ecs-import.d.ts.map +1 -0
- package/dist/internal/engine-profile-common.d.ts +9 -0
- package/dist/internal/engine-profile-common.d.ts.map +1 -0
- package/dist/internal/error-fanout.d.ts +66 -0
- package/dist/internal/error-fanout.d.ts.map +1 -0
- package/dist/internal/frame-loop.d.ts +38 -0
- package/dist/internal/frame-loop.d.ts.map +1 -0
- package/dist/internal/input-attach.d.ts +44 -0
- package/dist/internal/input-attach.d.ts.map +1 -0
- package/dist/internal/main-engine-profile.d.ts +19 -0
- package/dist/internal/main-engine-profile.d.ts.map +1 -0
- package/dist/internal/remote-serve-flag.d.ts +19 -0
- package/dist/internal/remote-serve-flag.d.ts.map +1 -0
- package/dist/internal/remote-server-plugin.d.ts +8 -0
- package/dist/internal/remote-server-plugin.d.ts.map +1 -0
- package/dist/internal/rhi-capture.d.ts +19 -0
- package/dist/internal/rhi-capture.d.ts.map +1 -0
- package/dist/internal/rhi-debug-flag.d.ts +18 -0
- package/dist/internal/rhi-debug-flag.d.ts.map +1 -0
- package/dist/internal/worker-engine-profile.d.ts +15 -0
- package/dist/internal/worker-engine-profile.d.ts.map +1 -0
- package/dist/kernel-worker-runtime.mjs +69 -0
- package/dist/kernel-worker-runtime.mjs.map +1 -0
- package/dist/load-game-errors.d.ts +131 -0
- package/dist/load-game-errors.d.ts.map +1 -0
- package/dist/load-game.d.ts +34 -0
- package/dist/load-game.d.ts.map +1 -0
- package/dist/plugin-factories.d.ts +3 -0
- package/dist/plugin-factories.d.ts.map +1 -0
- package/dist/renderer-plugin.d.ts +25 -0
- package/dist/renderer-plugin.d.ts.map +1 -0
- package/dist/tool-preview/bootstrap.d.ts +113 -0
- package/dist/tool-preview/bootstrap.d.ts.map +1 -0
- package/dist/tool-preview/evidence.d.ts +20 -0
- package/dist/tool-preview/evidence.d.ts.map +1 -0
- package/dist/tool-preview/framing.d.ts +13 -0
- package/dist/tool-preview/framing.d.ts.map +1 -0
- package/dist/tool-preview/recipe.d.ts +54 -0
- package/dist/tool-preview/recipe.d.ts.map +1 -0
- package/dist/types.d.ts +404 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +85 -0
- package/schema/execution-report.schema.json +168 -0
- package/src/__tests__/animation-asset-lookup.unit.test.ts +20 -0
- package/src/__tests__/callback-deletion.unit.test.ts +14 -0
- package/src/__tests__/callback-inference.test-d.ts +36 -0
- package/src/__tests__/component-introspection.unit.test.ts +56 -0
- package/src/__tests__/consumer-migration.unit.test.ts +34 -0
- package/src/__tests__/create-app-error-retention.integration.test.ts +125 -0
- package/src/__tests__/create-app-features.test.ts +51 -0
- package/src/__tests__/create-app-listener-sync.test.ts +381 -0
- package/src/__tests__/create-app-lock-provider.test.ts +212 -0
- package/src/__tests__/create-app-physics-plugin-failure.integration.test.ts +142 -0
- package/src/__tests__/create-app-plugin.test.ts +217 -0
- package/src/__tests__/create-app-stop.test.ts +609 -0
- package/src/__tests__/create-app-time.unit.test.ts +26 -0
- package/src/__tests__/create-app.test-d.ts +83 -0
- package/src/__tests__/create-app.test.ts +287 -0
- package/src/__tests__/create-renderer-error.integration.test.ts +25 -0
- package/src/__tests__/create-renderer-lifecycle.integration.test.ts +24 -0
- package/src/__tests__/docs-gate-green.unit.test.ts +61 -0
- package/src/__tests__/draw-source-injected-world-update-visibility.test.ts +176 -0
- package/src/__tests__/draw-source-single-world-regression.test.ts +184 -0
- package/src/__tests__/engine-worker-bootstrap-channel.unit.test.ts +82 -0
- package/src/__tests__/engine-worker-world-swap.unit.test.ts +69 -0
- package/src/__tests__/errors-pointer-lock-failed.test.ts +265 -0
- package/src/__tests__/execution-bootstrap-isolation.unit.test.ts +79 -0
- package/src/__tests__/execution-error-detail.test-d.ts +13 -0
- package/src/__tests__/execution-error-detail.unit.test.ts +35 -0
- package/src/__tests__/execution-frame-order.unit.test.ts +38 -0
- package/src/__tests__/execution-frame-protocol.unit.test.ts +60 -0
- package/src/__tests__/execution-input-sample.contract.test.ts +23 -0
- package/src/__tests__/execution-measurement.unit.test.ts +20 -0
- package/src/__tests__/execution-public-api.test-d.ts +14 -0
- package/src/__tests__/execution-public-api.unit.test.ts +9 -0
- package/src/__tests__/execution-rebuild.contract.test.ts +36 -0
- package/src/__tests__/execution-report.contract.test.ts +50 -0
- package/src/__tests__/execution-report.types.test-d.ts +16 -0
- package/src/__tests__/execution-schema-freshness.unit.test.ts +20 -0
- package/src/__tests__/execution-selector.unit.test.ts +72 -0
- package/src/__tests__/execution-shared-kernel.unit.test.ts +180 -0
- package/src/__tests__/fallback-camera.unit.test.ts +32 -0
- package/src/__tests__/frame-loop-time-elapsed.test.ts +115 -0
- package/src/__tests__/frame-loop-time.unit.test.ts +61 -0
- package/src/__tests__/frame-loop-world-array.test.ts +450 -0
- package/src/__tests__/kernel-worker-runtime.unit.test.ts +50 -0
- package/src/__tests__/migration-surface-inventory.unit.test.ts +13 -0
- package/src/__tests__/plugin-policy.integration.test.ts +15 -0
- package/src/__tests__/plugin-profiles.unit.test.ts +190 -0
- package/src/__tests__/profiler-default-off.test.ts +53 -0
- package/src/__tests__/profiler-frame-token.test.ts +235 -0
- package/src/__tests__/profiler-phase-catalog.test.ts +30 -0
- package/src/__tests__/profiler-phase-integration.test.ts +117 -0
- package/src/__tests__/profiler-remote-bridge.test.ts +31 -0
- package/src/__tests__/remote-integration.test.ts +224 -0
- package/src/__tests__/rhi-capture-capability.test-d.ts +22 -0
- package/src/__tests__/rhi-capture-host.integration.test.ts +157 -0
- package/src/__tests__/rhi-capture.browser.test.ts +51 -0
- package/src/__tests__/surface-handoff.test.ts +89 -0
- package/src/__tests__/types-m2.test-d.ts +81 -0
- package/src/__tests__/visibility-remote.integration.test.ts +116 -0
- package/src/__tests__/worker-execution-stop.test.ts +173 -0
- package/src/animation-asset-lookup.ts +13 -0
- package/src/browser-frame-signal.ts +44 -0
- package/src/canvas-policy.ts +16 -0
- package/src/create-app.ts +1264 -0
- package/src/errors.ts +442 -0
- package/src/execution/attached-world-swap.ts +33 -0
- package/src/execution/bootstrap-entry.ts +122 -0
- package/src/execution/bootstrap-url.ts +25 -0
- package/src/execution/capabilities.ts +102 -0
- package/src/execution/control.ts +48 -0
- package/src/execution/engine-worker-runtime.ts +383 -0
- package/src/execution/engine-worker.ts +163 -0
- package/src/execution/host-controller.ts +460 -0
- package/src/execution/index.ts +44 -0
- package/src/execution/kernel-pool.ts +236 -0
- package/src/execution/kernel-worker-runtime.ts +103 -0
- package/src/execution/measurement.ts +39 -0
- package/src/execution/protocol.ts +134 -0
- package/src/execution/report.ts +66 -0
- package/src/execution/schema.ts +168 -0
- package/src/execution/selector.ts +98 -0
- package/src/execution/types.ts +163 -0
- package/src/fallback-camera.ts +20 -0
- package/src/game-context.ts +126 -0
- package/src/index.ts +190 -0
- package/src/input-map-plugin.ts +21 -0
- package/src/input-plugin.ts +26 -0
- package/src/internal/assembled-engine-profile.ts +14 -0
- package/src/internal/assets-world-plugin.ts +33 -0
- package/src/internal/browser-remote-bridge.ts +269 -0
- package/src/internal/browser-rhi-debug-runtime.ts +67 -0
- package/src/internal/component-introspection.ts +116 -0
- package/src/internal/debug-draw.ts +38 -0
- package/src/internal/ecs-import.ts +41 -0
- package/src/internal/engine-profile-common.ts +9 -0
- package/src/internal/error-fanout.ts +129 -0
- package/src/internal/frame-loop.ts +539 -0
- package/src/internal/input-attach.ts +104 -0
- package/src/internal/main-engine-profile.ts +84 -0
- package/src/internal/remote-serve-flag.ts +41 -0
- package/src/internal/remote-server-plugin.ts +16 -0
- package/src/internal/rhi-capture.ts +211 -0
- package/src/internal/rhi-debug-flag.ts +40 -0
- package/src/internal/worker-engine-profile.ts +47 -0
- package/src/load-game-errors.ts +211 -0
- package/src/load-game.ts +115 -0
- package/src/plugin-factories.ts +2 -0
- package/src/renderer-plugin.ts +96 -0
- package/src/tool-preview/bootstrap.ts +835 -0
- package/src/tool-preview/evidence.ts +59 -0
- package/src/tool-preview/framing.ts +52 -0
- package/src/tool-preview/recipe.ts +137 -0
- package/src/types.ts +450 -0
- package/src/vite-env.d.ts +10 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
KernelDispatchFailure,
|
|
3
|
+
KernelDispatchResult,
|
|
4
|
+
KernelDispatchSpan,
|
|
5
|
+
SharedKernelDispatch,
|
|
6
|
+
SharedKernelExecutor,
|
|
7
|
+
SharedSpanBinding,
|
|
8
|
+
} from '@forgeax/engine-ecs/shared';
|
|
9
|
+
import { bindSharedSpan, isSharedSpan, splitSharedSpan } from '@forgeax/engine-ecs/shared';
|
|
10
|
+
|
|
11
|
+
export interface KernelPool extends SharedKernelExecutor {
|
|
12
|
+
readonly laneCount: number;
|
|
13
|
+
ready(): Promise<void>;
|
|
14
|
+
takeLastDispatch(): KernelDispatchResult | null;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface KernelJobMessage {
|
|
19
|
+
readonly kind: 'kernel-job';
|
|
20
|
+
readonly moduleUrl: string;
|
|
21
|
+
readonly binding: SharedSpanBinding;
|
|
22
|
+
readonly control: Int32Array;
|
|
23
|
+
readonly status: Int32Array;
|
|
24
|
+
readonly jobIndex: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface KernelInitMessage {
|
|
28
|
+
readonly kind: 'kernel-init';
|
|
29
|
+
readonly ready: Int32Array;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface KernelPreloadMessage {
|
|
33
|
+
readonly kind: 'kernel-preload';
|
|
34
|
+
readonly moduleUrl: string;
|
|
35
|
+
readonly control: Int32Array;
|
|
36
|
+
readonly status: Int32Array;
|
|
37
|
+
readonly jobIndex: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface KernelPreflight {
|
|
41
|
+
readonly control: Int32Array;
|
|
42
|
+
readonly status: Int32Array;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface KernelPoolOptions {
|
|
46
|
+
readonly lanes?: number;
|
|
47
|
+
readonly timeoutMs?: number;
|
|
48
|
+
readonly workerFactory?: () => Worker;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createKernelPool(options: KernelPoolOptions = {}): KernelPool {
|
|
52
|
+
const hardware = globalThis.navigator?.hardwareConcurrency ?? 2;
|
|
53
|
+
const laneCount = Math.max(1, Math.min(options.lanes ?? hardware - 1, 8));
|
|
54
|
+
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
55
|
+
const workers = Array.from(
|
|
56
|
+
{ length: laneCount },
|
|
57
|
+
() =>
|
|
58
|
+
options.workerFactory?.() ??
|
|
59
|
+
new Worker(new URL('./kernel-worker-runtime.mjs', import.meta.url), {
|
|
60
|
+
type: 'module',
|
|
61
|
+
name: 'forgeax-kernel',
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
let latest: KernelDispatchResult | null = null;
|
|
65
|
+
let ready = false;
|
|
66
|
+
const preflights = new Map<string, KernelPreflight>();
|
|
67
|
+
const readyControl = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
68
|
+
const initMessage: KernelInitMessage = { kind: 'kernel-init', ready: readyControl };
|
|
69
|
+
for (const worker of workers) worker.postMessage(initMessage);
|
|
70
|
+
const waitFor = (control: Int32Array, expected: number, label: string): Promise<void> =>
|
|
71
|
+
new Promise<void>((resolve, reject) => {
|
|
72
|
+
const started = performance.now();
|
|
73
|
+
const poll = (): void => {
|
|
74
|
+
if (Atomics.load(control, 0) === expected) {
|
|
75
|
+
resolve();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (performance.now() - started >= timeoutMs) {
|
|
79
|
+
for (const worker of workers) worker.terminate();
|
|
80
|
+
reject(new Error(`${label} did not complete within ${timeoutMs}ms.`));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
setTimeout(poll, 1);
|
|
84
|
+
};
|
|
85
|
+
poll();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const readyPromise = waitFor(
|
|
89
|
+
readyControl,
|
|
90
|
+
workers.length,
|
|
91
|
+
'SharedKernel worker initialization',
|
|
92
|
+
).then(async () => {
|
|
93
|
+
for (const [moduleUrl, preflight] of preflights) {
|
|
94
|
+
await waitFor(
|
|
95
|
+
preflight.control,
|
|
96
|
+
workers.length,
|
|
97
|
+
`SharedKernel module preflight for ${moduleUrl}`,
|
|
98
|
+
);
|
|
99
|
+
if (preflight.status.some((status) => status !== 1)) {
|
|
100
|
+
throw new Error(`SharedKernel module preflight failed for ${moduleUrl}.`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
ready = true;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
laneCount,
|
|
108
|
+
ready: () => readyPromise,
|
|
109
|
+
warmup(kernel): void {
|
|
110
|
+
if (preflights.has(kernel.moduleUrl)) return;
|
|
111
|
+
const control = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
112
|
+
const status = new Int32Array(
|
|
113
|
+
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * workers.length),
|
|
114
|
+
);
|
|
115
|
+
preflights.set(kernel.moduleUrl, { control, status });
|
|
116
|
+
for (const [jobIndex, worker] of workers.entries()) {
|
|
117
|
+
const message: KernelPreloadMessage = {
|
|
118
|
+
kind: 'kernel-preload',
|
|
119
|
+
moduleUrl: kernel.moduleUrl,
|
|
120
|
+
control,
|
|
121
|
+
status,
|
|
122
|
+
jobIndex,
|
|
123
|
+
};
|
|
124
|
+
worker.postMessage(message);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
takeLastDispatch: () => {
|
|
128
|
+
const dispatch = latest;
|
|
129
|
+
latest = null;
|
|
130
|
+
return dispatch;
|
|
131
|
+
},
|
|
132
|
+
execute(
|
|
133
|
+
kernel: SharedKernelDispatch,
|
|
134
|
+
spans: readonly KernelDispatchSpan[],
|
|
135
|
+
): KernelDispatchResult | KernelDispatchFailure {
|
|
136
|
+
const preflight = preflights.get(kernel.moduleUrl);
|
|
137
|
+
if (preflight === undefined) {
|
|
138
|
+
this.warmup?.(kernel);
|
|
139
|
+
latest = { mode: 'forced-inline', dispatched: 0, completed: 0, waitMs: 0 };
|
|
140
|
+
return {
|
|
141
|
+
cause: new Error('SharedKernel module was not preflighted before frame dispatch.'),
|
|
142
|
+
dispatched: 0,
|
|
143
|
+
completed: 0,
|
|
144
|
+
partialWrite: false,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (
|
|
148
|
+
Atomics.load(preflight.control, 0) !== workers.length ||
|
|
149
|
+
preflight.status.some((status) => status !== 1)
|
|
150
|
+
) {
|
|
151
|
+
latest = { mode: 'forced-inline', dispatched: 0, completed: 0, waitMs: 0 };
|
|
152
|
+
return {
|
|
153
|
+
cause: new Error('SharedKernel module preflight is incomplete or failed.'),
|
|
154
|
+
dispatched: 0,
|
|
155
|
+
completed: 0,
|
|
156
|
+
partialWrite: false,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (!ready && Atomics.load(readyControl, 0) === workers.length) ready = true;
|
|
160
|
+
if (!ready) {
|
|
161
|
+
latest = { mode: 'forced-inline', dispatched: 0, completed: 0, waitMs: 0 };
|
|
162
|
+
return {
|
|
163
|
+
cause: new Error('SharedKernel workers were not warmed before frame dispatch.'),
|
|
164
|
+
dispatched: 0,
|
|
165
|
+
completed: 0,
|
|
166
|
+
partialWrite: false,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const bindings = spans.flatMap(({ queryIndex, span }) =>
|
|
170
|
+
splitSharedSpan(bindSharedSpan(kernel, span, queryIndex), laneCount),
|
|
171
|
+
);
|
|
172
|
+
if (bindings.length === 0) {
|
|
173
|
+
latest = { mode: 'forced-inline', dispatched: 0, completed: 0, waitMs: 0 };
|
|
174
|
+
return latest;
|
|
175
|
+
}
|
|
176
|
+
if (!bindings.every(isSharedSpan)) {
|
|
177
|
+
latest = { mode: 'forced-inline', dispatched: 0, completed: 0, waitMs: 0 };
|
|
178
|
+
return {
|
|
179
|
+
cause: new Error('SharedKernel received a non-SAB QuerySpan.'),
|
|
180
|
+
dispatched: 0,
|
|
181
|
+
completed: 0,
|
|
182
|
+
partialWrite: false,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const control = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
186
|
+
const status = new Int32Array(
|
|
187
|
+
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * bindings.length),
|
|
188
|
+
);
|
|
189
|
+
const started = performance.now();
|
|
190
|
+
for (const [index, binding] of bindings.entries()) {
|
|
191
|
+
const message: KernelJobMessage = {
|
|
192
|
+
kind: 'kernel-job',
|
|
193
|
+
moduleUrl: kernel.moduleUrl,
|
|
194
|
+
binding,
|
|
195
|
+
control,
|
|
196
|
+
status,
|
|
197
|
+
jobIndex: index,
|
|
198
|
+
};
|
|
199
|
+
workers[index % workers.length]?.postMessage(message);
|
|
200
|
+
}
|
|
201
|
+
while (Atomics.load(control, 0) < bindings.length) {
|
|
202
|
+
const observed = Atomics.load(control, 0);
|
|
203
|
+
const wait = Atomics.wait(control, 0, observed, timeoutMs);
|
|
204
|
+
if (wait === 'timed-out') {
|
|
205
|
+
for (const worker of workers) worker.terminate();
|
|
206
|
+
return {
|
|
207
|
+
cause: new Error(`SharedKernel deadline exceeded after ${timeoutMs}ms.`),
|
|
208
|
+
dispatched: bindings.length,
|
|
209
|
+
completed: observed,
|
|
210
|
+
partialWrite: bindings.length > 0,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const completed = status.reduce((count, value) => count + (value === 1 ? 1 : 0), 0);
|
|
215
|
+
const failed = status.some((value) => value === -1);
|
|
216
|
+
if (failed) {
|
|
217
|
+
return {
|
|
218
|
+
cause: new Error('SharedKernel worker failed.'),
|
|
219
|
+
dispatched: bindings.length,
|
|
220
|
+
completed,
|
|
221
|
+
partialWrite: bindings.length > 0,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
latest = {
|
|
225
|
+
mode: 'shared',
|
|
226
|
+
dispatched: bindings.length,
|
|
227
|
+
completed,
|
|
228
|
+
waitMs: performance.now() - started,
|
|
229
|
+
};
|
|
230
|
+
return latest;
|
|
231
|
+
},
|
|
232
|
+
dispose(): void {
|
|
233
|
+
for (const worker of workers) worker.terminate();
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { QuerySpan } from '@forgeax/engine-ecs';
|
|
2
|
+
import type { SharedSpanBinding } from '@forgeax/engine-ecs/shared';
|
|
3
|
+
|
|
4
|
+
interface KernelJobMessage {
|
|
5
|
+
readonly kind: 'kernel-job';
|
|
6
|
+
readonly moduleUrl: string;
|
|
7
|
+
readonly binding: SharedSpanBinding;
|
|
8
|
+
readonly control: Int32Array;
|
|
9
|
+
readonly status: Int32Array;
|
|
10
|
+
readonly jobIndex: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface KernelInitMessage {
|
|
14
|
+
readonly kind: 'kernel-init';
|
|
15
|
+
readonly ready: Int32Array;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface KernelPreloadMessage {
|
|
19
|
+
readonly kind: 'kernel-preload';
|
|
20
|
+
readonly moduleUrl: string;
|
|
21
|
+
readonly control: Int32Array;
|
|
22
|
+
readonly status: Int32Array;
|
|
23
|
+
readonly jobIndex: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface LoadedKernel {
|
|
27
|
+
readonly run: (spans: readonly QuerySpan[]) => unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const scope = globalThis as unknown as {
|
|
31
|
+
onmessage:
|
|
32
|
+
| ((event: MessageEvent<KernelJobMessage | KernelInitMessage | KernelPreloadMessage>) => void)
|
|
33
|
+
| null;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const loadedKernels = new Map<string, LoadedKernel>();
|
|
37
|
+
|
|
38
|
+
async function loadKernel(moduleUrl: string): Promise<LoadedKernel> {
|
|
39
|
+
const module = await import(/* @vite-ignore */ moduleUrl);
|
|
40
|
+
const candidate = (module.default ?? module) as { run?: unknown };
|
|
41
|
+
if (typeof candidate.run !== 'function') {
|
|
42
|
+
throw new TypeError('SharedKernel module default export has no run function.');
|
|
43
|
+
}
|
|
44
|
+
return candidate as LoadedKernel;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function spanFromBinding(binding: SharedSpanBinding): QuerySpan {
|
|
48
|
+
return {
|
|
49
|
+
entities: binding.entities,
|
|
50
|
+
length: binding.length,
|
|
51
|
+
get(component) {
|
|
52
|
+
const fields = binding.read[component.name];
|
|
53
|
+
if (fields === undefined)
|
|
54
|
+
throw new Error(`Kernel read access is not declared for ${component.name}.`);
|
|
55
|
+
return fields as never;
|
|
56
|
+
},
|
|
57
|
+
mut(component) {
|
|
58
|
+
const fields = binding.write[component.name];
|
|
59
|
+
if (fields === undefined)
|
|
60
|
+
throw new Error(`Kernel write access is not declared for ${component.name}.`);
|
|
61
|
+
return fields as never;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
scope.onmessage = (event): void => {
|
|
67
|
+
const job = event.data;
|
|
68
|
+
if (job.kind === 'kernel-init') {
|
|
69
|
+
Atomics.add(job.ready, 0, 1);
|
|
70
|
+
Atomics.notify(job.ready, 0);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (job.kind === 'kernel-preload') {
|
|
74
|
+
void loadKernel(job.moduleUrl)
|
|
75
|
+
.then((kernel) => {
|
|
76
|
+
loadedKernels.set(job.moduleUrl, kernel);
|
|
77
|
+
Atomics.store(job.status, job.jobIndex, 1);
|
|
78
|
+
})
|
|
79
|
+
.catch(() => {
|
|
80
|
+
Atomics.store(job.status, job.jobIndex, -1);
|
|
81
|
+
})
|
|
82
|
+
.finally(() => {
|
|
83
|
+
Atomics.add(job.control, 0, 1);
|
|
84
|
+
Atomics.notify(job.control, 0);
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (job.kind !== 'kernel-job') return;
|
|
89
|
+
try {
|
|
90
|
+
const kernel = loadedKernels.get(job.moduleUrl);
|
|
91
|
+
if (kernel === undefined) {
|
|
92
|
+
throw new Error(`SharedKernel module was not preloaded: ${job.moduleUrl}`);
|
|
93
|
+
}
|
|
94
|
+
const returned = kernel.run([spanFromBinding(job.binding)]);
|
|
95
|
+
if (returned instanceof Promise) throw new TypeError('SharedKernel run must be synchronous.');
|
|
96
|
+
Atomics.store(job.status, job.jobIndex, 1);
|
|
97
|
+
} catch {
|
|
98
|
+
Atomics.store(job.status, job.jobIndex, -1);
|
|
99
|
+
} finally {
|
|
100
|
+
Atomics.add(job.control, 0, 1);
|
|
101
|
+
Atomics.notify(job.control, 0);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ExecutionMeasurement } from './types';
|
|
2
|
+
|
|
3
|
+
function percentile(sorted: readonly number[], fraction: number): number {
|
|
4
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * fraction) - 1));
|
|
5
|
+
return sorted[index] ?? 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface MeasurementSeries {
|
|
9
|
+
add(value: number): ExecutionMeasurement;
|
|
10
|
+
clear(): void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createMeasurementSeries(capacity = 240): MeasurementSeries {
|
|
14
|
+
const values: number[] = [];
|
|
15
|
+
let samples = 0;
|
|
16
|
+
return {
|
|
17
|
+
add(value): ExecutionMeasurement {
|
|
18
|
+
samples += 1;
|
|
19
|
+
values.push(value);
|
|
20
|
+
if (values.length > capacity) values.shift();
|
|
21
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
22
|
+
let jitter = 0;
|
|
23
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
24
|
+
jitter += Math.abs((values[index] ?? 0) - (values[index - 1] ?? 0));
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
samples,
|
|
28
|
+
p50: percentile(sorted, 0.5),
|
|
29
|
+
p95: percentile(sorted, 0.95),
|
|
30
|
+
p99: percentile(sorted, 0.99),
|
|
31
|
+
jitter: values.length < 2 ? 0 : jitter / (values.length - 1),
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
clear(): void {
|
|
35
|
+
values.length = 0;
|
|
36
|
+
samples = 0;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { AudioIntent } from '@forgeax/engine-audio';
|
|
2
|
+
import type { InputBackendSample } from '@forgeax/engine-input';
|
|
3
|
+
import type { CanvasDrawingBufferSize } from '../types';
|
|
4
|
+
import type { ExecutionAssetCatalog } from './types';
|
|
5
|
+
|
|
6
|
+
export interface ExecutionFrameMessage {
|
|
7
|
+
readonly kind: 'frame';
|
|
8
|
+
readonly worldIdentity: string;
|
|
9
|
+
readonly frameId: number;
|
|
10
|
+
readonly deltaSeconds: number;
|
|
11
|
+
readonly inputSample: InputBackendSample;
|
|
12
|
+
readonly canvasWidth: number;
|
|
13
|
+
readonly canvasHeight: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ExecutionFrameCompletion {
|
|
17
|
+
readonly kind: 'frame-complete';
|
|
18
|
+
readonly worldIdentity: string;
|
|
19
|
+
readonly frameId: number;
|
|
20
|
+
readonly engineUpdateMs: number;
|
|
21
|
+
readonly kernelWaitMs: number;
|
|
22
|
+
readonly audioIntents?: readonly AudioIntent[];
|
|
23
|
+
readonly kernelDispatch?: {
|
|
24
|
+
readonly eligible: boolean;
|
|
25
|
+
readonly usedShared: boolean;
|
|
26
|
+
readonly reason: import('./types').KernelDispatchReason;
|
|
27
|
+
readonly dispatched: number;
|
|
28
|
+
readonly completed: number;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type FrameCompletionDisposition = 'accepted' | 'duplicate' | 'stale-world' | 'late';
|
|
33
|
+
|
|
34
|
+
export class FrameCreditLedger {
|
|
35
|
+
private nextFrameId = 1;
|
|
36
|
+
private inFlight: number | null = null;
|
|
37
|
+
private completed = 0;
|
|
38
|
+
|
|
39
|
+
constructor(readonly worldIdentity: string) {}
|
|
40
|
+
|
|
41
|
+
issue(
|
|
42
|
+
deltaSeconds: number,
|
|
43
|
+
sampleInput: () => InputBackendSample,
|
|
44
|
+
canvasSize: CanvasDrawingBufferSize = { width: 0, height: 0 },
|
|
45
|
+
): ExecutionFrameMessage | undefined {
|
|
46
|
+
if (this.inFlight !== null) return undefined;
|
|
47
|
+
const frameId = this.nextFrameId;
|
|
48
|
+
this.nextFrameId += 1;
|
|
49
|
+
this.inFlight = frameId;
|
|
50
|
+
return {
|
|
51
|
+
kind: 'frame',
|
|
52
|
+
worldIdentity: this.worldIdentity,
|
|
53
|
+
frameId,
|
|
54
|
+
deltaSeconds,
|
|
55
|
+
inputSample: sampleInput(),
|
|
56
|
+
canvasWidth: canvasSize.width,
|
|
57
|
+
canvasHeight: canvasSize.height,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
complete(message: ExecutionFrameCompletion): FrameCompletionDisposition {
|
|
62
|
+
if (message.worldIdentity !== this.worldIdentity) return 'stale-world';
|
|
63
|
+
if (message.frameId <= this.completed) return 'duplicate';
|
|
64
|
+
if (message.frameId !== this.inFlight) return 'late';
|
|
65
|
+
this.completed = message.frameId;
|
|
66
|
+
this.inFlight = null;
|
|
67
|
+
return 'accepted';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get hasCreditInFlight(): boolean {
|
|
71
|
+
return this.inFlight !== null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ExecutionInitMessage {
|
|
76
|
+
readonly kind: 'init';
|
|
77
|
+
readonly canvas: OffscreenCanvas;
|
|
78
|
+
readonly bootstrapUrl: string;
|
|
79
|
+
readonly bootstrapData?: import('./types').ExecutionBootstrapValue;
|
|
80
|
+
readonly bootstrapPort?: MessagePort;
|
|
81
|
+
/** Realm-serializable asset catalog configuration. */
|
|
82
|
+
readonly assetCatalog?: ExecutionAssetCatalog;
|
|
83
|
+
readonly shaderManifestUrl?: string;
|
|
84
|
+
readonly time?: import('@forgeax/engine-ecs').TimePolicy;
|
|
85
|
+
readonly tier: import('./types').ExecutionTier;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ExecutionHostControlMessage {
|
|
89
|
+
readonly kind: 'host-control';
|
|
90
|
+
readonly command: 'set-pointer-lock-allowed';
|
|
91
|
+
readonly allowed: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ExecutionReadyMessage {
|
|
95
|
+
readonly kind: 'ready';
|
|
96
|
+
readonly worldIdentity: string;
|
|
97
|
+
readonly realm: 'worker';
|
|
98
|
+
readonly workerWebGpu: boolean;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ExecutionFaultMessage {
|
|
102
|
+
readonly kind: 'fault';
|
|
103
|
+
readonly worldIdentity: string | null;
|
|
104
|
+
readonly source: 'bootstrap' | 'handshake' | 'runtime' | 'kernel' | 'world' | 'rebuild';
|
|
105
|
+
readonly code: string;
|
|
106
|
+
readonly expected: string;
|
|
107
|
+
readonly hint: string;
|
|
108
|
+
readonly detail: unknown;
|
|
109
|
+
readonly partialWrite: boolean;
|
|
110
|
+
readonly retryable: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface ExecutionRebuildMessage {
|
|
114
|
+
readonly kind: 'rebuild';
|
|
115
|
+
readonly worldIdentity: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ExecutionRebuiltMessage {
|
|
119
|
+
readonly kind: 'rebuilt';
|
|
120
|
+
readonly previousWorldIdentity: string;
|
|
121
|
+
readonly worldIdentity: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type HostToEngineMessage =
|
|
125
|
+
| ExecutionInitMessage
|
|
126
|
+
| ExecutionFrameMessage
|
|
127
|
+
| ExecutionRebuildMessage
|
|
128
|
+
| { readonly kind: 'dispose' };
|
|
129
|
+
export type EngineToHostMessage =
|
|
130
|
+
| ExecutionReadyMessage
|
|
131
|
+
| ExecutionFrameCompletion
|
|
132
|
+
| ExecutionFaultMessage
|
|
133
|
+
| ExecutionRebuiltMessage
|
|
134
|
+
| ExecutionHostControlMessage;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AudioState } from '@forgeax/engine-audio';
|
|
2
|
+
import { EXECUTION_REPORT_SCHEMA_VERSION } from './schema';
|
|
3
|
+
import type {
|
|
4
|
+
ExecutionCapabilities,
|
|
5
|
+
ExecutionReport,
|
|
6
|
+
ExecutionRequestedTier,
|
|
7
|
+
ExecutionSelection,
|
|
8
|
+
} from './types';
|
|
9
|
+
|
|
10
|
+
export function executionAudioReport(state?: AudioState): ExecutionReport['audio'] {
|
|
11
|
+
const error = state?.lastError ?? null;
|
|
12
|
+
return {
|
|
13
|
+
owner: 'host',
|
|
14
|
+
contextState: state?.contextState ?? 'suspended',
|
|
15
|
+
activeSourceCount: state?.activeSourceCount ?? 0,
|
|
16
|
+
lastError:
|
|
17
|
+
error === null
|
|
18
|
+
? null
|
|
19
|
+
: {
|
|
20
|
+
code: error.code,
|
|
21
|
+
expected: error.expected,
|
|
22
|
+
hint: error.hint,
|
|
23
|
+
detail: error.detail,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createExecutionReport(
|
|
29
|
+
requestedTier: ExecutionRequestedTier,
|
|
30
|
+
capabilities: ExecutionCapabilities,
|
|
31
|
+
selection?: ExecutionSelection,
|
|
32
|
+
): ExecutionReport {
|
|
33
|
+
return {
|
|
34
|
+
schemaVersion: EXECUTION_REPORT_SCHEMA_VERSION,
|
|
35
|
+
requestedTier,
|
|
36
|
+
actualTier: selection?.actualTier ?? null,
|
|
37
|
+
selectionReason: selection?.selectionReason ?? null,
|
|
38
|
+
sharedEvidencePassed: selection?.sharedEvidencePassed ?? false,
|
|
39
|
+
capabilities,
|
|
40
|
+
engine: {
|
|
41
|
+
realm: selection?.actualTier === 'main-serial' ? 'host' : 'worker',
|
|
42
|
+
health: 'idle',
|
|
43
|
+
},
|
|
44
|
+
world: {
|
|
45
|
+
identity: null,
|
|
46
|
+
health: 'healthy',
|
|
47
|
+
partialWrite: false,
|
|
48
|
+
retryable: true,
|
|
49
|
+
},
|
|
50
|
+
kernelDispatch: {
|
|
51
|
+
eligible: false,
|
|
52
|
+
usedShared: false,
|
|
53
|
+
reason: 'no-eligible-kernel',
|
|
54
|
+
dispatched: 0,
|
|
55
|
+
completed: 0,
|
|
56
|
+
},
|
|
57
|
+
performance: {
|
|
58
|
+
hostFrameMs: null,
|
|
59
|
+
engineUpdateMs: null,
|
|
60
|
+
kernelWaitMs: null,
|
|
61
|
+
hostAudioMs: null,
|
|
62
|
+
},
|
|
63
|
+
audio: executionAudioReport(),
|
|
64
|
+
fault: null,
|
|
65
|
+
};
|
|
66
|
+
}
|