@downdraft/engine 0.1.1 → 0.2.0
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/app/README.md +11 -0
- package/app/src/renderer/bootstrap.ts +295 -19
- package/app/src/renderer/feature-log.ts +1 -1
- package/app/src/renderer/game-module.ts +60 -12
- package/app/src/renderer/hooks.ts +9 -1
- package/app/src/renderer/hot-reload.ts +9 -9
- package/app/src/renderer/mcp-harness.ts +2 -2
- package/app/src/renderer/standard-automation-tools.ts +104 -3
- package/app/src/shared/feature-log.ts +2 -1
- package/app/src/shared/mcp-discovery.ts +8 -3
- package/app/src/shared/mcp-proxy.ts +2 -2
- package/asset-bake/README.md +22 -0
- package/core/README.md +25 -0
- package/core/src/animation/skeleton-animator.ts +23 -5
- package/core/src/assets/manager.ts +4 -4
- package/core/src/ecs/events.ts +1 -1
- package/core/src/ecs/schedule.ts +1 -1
- package/core/src/ecs/world.ts +1 -1
- package/core/src/index.ts +3 -4
- package/core/src/input/local-player-manager.ts +2 -2
- package/core/src/library/host.ts +27 -13
- package/core/src/library/library.ts +13 -5
- package/core/src/module/cross-thread.ts +6 -0
- package/core/src/module/diagnostics.ts +10 -6
- package/core/src/module/host.ts +65 -18
- package/core/src/module/index.ts +1 -0
- package/core/src/module/module.ts +11 -5
- package/core/src/module/renderer-module.ts +15 -0
- package/core/src/physics/safety.ts +1 -1
- package/core/src/platform/bun-preload.ts +7 -2
- package/core/src/platform/glob-polyfill.ts +17 -8
- package/core/src/platform/pkg-root.ts +27 -0
- package/core/src/platform/runtime.ts +29 -0
- package/core/src/plugin/discovery.ts +1 -1
- package/core/src/plugin/extension-loaders.ts +6 -4
- package/core/src/plugin/host.ts +13 -13
- package/core/src/plugin/loader-asset.ts +5 -5
- package/core/src/plugin/loader-quickjs.ts +1 -1
- package/core/src/plugin/loader-wasm.ts +3 -3
- package/core/src/plugin/loader-worker.ts +1 -1
- package/core/src/plugin/manifest.ts +8 -3
- package/core/src/plugin/workshop.ts +3 -3
- package/core/src/profiling/index.ts +1 -1
- package/core/src/profiling/profiling-sab.ts +19 -0
- package/core/src/profiling/warnings.ts +1 -1
- package/core/src/profiling/worker-prelude.ts +1 -1
- package/core/src/render/bindless/frame-bindings.ts +1 -1
- package/core/src/render/bindless/material-manager.ts +2 -2
- package/core/src/render/bindless/texture-registry.ts +2 -2
- package/core/src/render/device.ts +5 -5
- package/core/src/render/frame-graph.ts +2 -2
- package/core/src/render/game-renderer.ts +42 -18
- package/core/src/render/hot-reload.ts +3 -3
- package/core/src/render/renderer-module-host.ts +32 -12
- package/core/src/render/shader-validator.ts +7 -7
- package/core/src/render/surface.ts +1 -1
- package/core/src/sab/errors.ts +1 -1
- package/core/src/sab/sab-polyfill.ts +1 -1
- package/core/src/save/migration-registry.ts +4 -4
- package/core/src/save/schema.ts +1 -1
- package/core/src/scene/prefab.ts +1 -1
- package/core/src/scene/scene-manager.ts +2 -2
- package/core/src/scene/scene.ts +4 -4
- package/core/src/scene/streaming.ts +2 -2
- package/core/src/scripting/hot-reload.ts +2 -2
- package/core/src/sim/hot-reload-pipeline.ts +30 -6
- package/core/src/sim/sim-state-helper.ts +1 -1
- package/core/src/sim/transient-state-registry.ts +1 -1
- package/core/src/sim/worker-loop.ts +2 -2
- package/core/src/telemetry/gpu-profiler.ts +3 -3
- package/core/src/util/index.ts +1 -1
- package/core/src/util/logger.ts +38 -7
- package/core/src/version.ts +13 -0
- package/core/src/worker/base-worker-host.ts +87 -25
- package/core/src/worker/buffer-sync.ts +1 -1
- package/core/src/worker/crash-recovery.ts +2 -2
- package/core/src/worker/instrumented-worker-host.ts +3 -3
- package/core/src/worker/rpc.ts +62 -11
- package/core/src/worker/sim-worker-base.ts +4 -9
- package/core/src/worker/task-pool.ts +2 -2
- package/dist/.dd-manifest.json +4119 -0
- package/dist/app/src/index.js +9 -0
- package/dist/app/src/renderer/bootstrap.js +446 -0
- package/dist/app/src/renderer/downdraft-base.css +93 -0
- package/dist/app/src/renderer/feature-log.js +180 -0
- package/dist/app/src/renderer/game-module.js +562 -0
- package/dist/app/src/renderer/hooks.js +82 -0
- package/dist/app/src/renderer/hot-reload.js +158 -0
- package/dist/app/src/renderer/import-cache.js +77 -0
- package/dist/app/src/renderer/index.js +149 -0
- package/dist/app/src/renderer/mcp-harness.js +112 -0
- package/dist/app/src/renderer/save-store-factory.js +180 -0
- package/dist/app/src/renderer/sim-bridge.js +138 -0
- package/dist/app/src/renderer/standard-automation-tools.js +720 -0
- package/dist/app/src/shared/feature-log.js +54 -0
- package/dist/app/src/shared/gpu-info.js +74 -0
- package/dist/app/src/shared/import-cache-store.js +123 -0
- package/dist/app/src/shared/index.js +4 -0
- package/dist/app/src/shared/mcp-discovery.js +55 -0
- package/dist/app/src/shared/mcp-proxy.js +71 -0
- package/dist/app/src/shared/types.js +13 -0
- package/dist/asset-bake/src/bake-audio.js +103 -0
- package/dist/asset-bake/src/bake-gltf.js +141 -0
- package/dist/asset-bake/src/bake.js +63 -0
- package/dist/asset-bake/src/cache.js +147 -0
- package/dist/asset-bake/src/config.js +93 -0
- package/dist/asset-bake/src/index.js +25 -0
- package/dist/core/src/animation/animation-event.js +24 -0
- package/dist/core/src/animation/bone-mask.js +103 -0
- package/dist/core/src/animation/clip.js +210 -0
- package/dist/core/src/animation/index.js +13 -0
- package/dist/core/src/animation/mixamo.js +78 -0
- package/dist/core/src/animation/morph-target.js +37 -0
- package/dist/core/src/animation/player.js +621 -0
- package/dist/core/src/animation/retarget.js +101 -0
- package/dist/core/src/animation/skeleton-animator.js +1361 -0
- package/dist/core/src/animation/skeleton.js +166 -0
- package/dist/core/src/animation/state-machine.js +252 -0
- package/dist/core/src/assets/blob-store.js +14 -0
- package/dist/core/src/assets/cache.js +168 -0
- package/dist/core/src/assets/cubemap-converter.js +129 -0
- package/dist/core/src/assets/environment-manager.js +84 -0
- package/dist/core/src/assets/import-cache.js +36 -0
- package/dist/core/src/assets/import-settings.js +34 -0
- package/dist/core/src/assets/importer.js +78 -0
- package/dist/core/src/assets/index.js +29 -0
- package/dist/core/src/assets/irradiance-generator.js +179 -0
- package/dist/core/src/assets/loader-audio.js +70 -0
- package/dist/core/src/assets/loader-cubemap.js +141 -0
- package/dist/core/src/assets/loader-dds.js +185 -0
- package/dist/core/src/assets/loader-hdr.js +430 -0
- package/dist/core/src/assets/loader-mesh.js +47 -0
- package/dist/core/src/assets/loader-shader.js +61 -0
- package/dist/core/src/assets/loader-texture.js +264 -0
- package/dist/core/src/assets/lod.js +150 -0
- package/dist/core/src/assets/manager.js +401 -0
- package/dist/core/src/assets/manifest.js +57 -0
- package/dist/core/src/assets/material-bridge.js +160 -0
- package/dist/core/src/assets/model-importer.js +32 -0
- package/dist/core/src/assets/model-normalizer.js +213 -0
- package/dist/core/src/assets/model-to-gpu.js +44 -0
- package/dist/core/src/assets/model-to-mesh.js +171 -0
- package/dist/core/src/assets/prefilter-generator.js +220 -0
- package/dist/core/src/audio/engine.js +153 -0
- package/dist/core/src/audio/interface.js +8 -0
- package/dist/core/src/audio/listener.js +19 -0
- package/dist/core/src/audio/mixer.js +124 -0
- package/dist/core/src/audio/registry.js +43 -0
- package/dist/core/src/audio/sab.js +74 -0
- package/dist/core/src/audio/source.js +60 -0
- package/dist/core/src/change-detection/atomics.js +1 -0
- package/dist/core/src/change-detection/tracker.js +26 -0
- package/dist/core/src/debug-draw/lines.js +27 -0
- package/dist/core/src/debug-draw/points.js +9 -0
- package/dist/core/src/debug-draw/queue.js +77 -0
- package/dist/core/src/debug-draw/text.js +12 -0
- package/dist/core/src/ecs/archetype.js +271 -0
- package/dist/core/src/ecs/component.js +109 -0
- package/dist/core/src/ecs/entity.js +11 -0
- package/dist/core/src/ecs/events.js +73 -0
- package/dist/core/src/ecs/hierarchy.js +104 -0
- package/dist/core/src/ecs/hmr-swap-registry.js +15 -0
- package/dist/core/src/ecs/index.js +14 -0
- package/dist/core/src/ecs/query.js +110 -0
- package/dist/core/src/ecs/resource.js +3 -0
- package/dist/core/src/ecs/schedule.js +157 -0
- package/dist/core/src/ecs/system-params.js +45 -0
- package/dist/core/src/ecs/system.js +18 -0
- package/dist/core/src/ecs/world.js +292 -0
- package/dist/core/src/index.js +191 -0
- package/dist/core/src/input/context.js +69 -0
- package/dist/core/src/input/dom-handler.js +288 -0
- package/dist/core/src/input/local-player-manager.js +145 -0
- package/dist/core/src/input/mapping.js +36 -0
- package/dist/core/src/input/multi-sab-bridge.js +224 -0
- package/dist/core/src/input/multi-state.js +48 -0
- package/dist/core/src/input/sab-bridge.js +66 -0
- package/dist/core/src/input/state.js +108 -0
- package/dist/core/src/input/ui-router.js +76 -0
- package/dist/core/src/library/host.js +291 -0
- package/dist/core/src/library/index.js +1 -0
- package/dist/core/src/library/library.js +20 -0
- package/dist/core/src/material/compiler.js +73 -0
- package/dist/core/src/material/graph-bridge.js +119 -0
- package/dist/core/src/material/index.js +6 -0
- package/dist/core/src/material/library.js +414 -0
- package/dist/core/src/material/material.js +120 -0
- package/dist/core/src/material/variants.js +37 -0
- package/dist/core/src/math/mat4.js +198 -0
- package/dist/core/src/math/perlin-noise-2d.js +66 -0
- package/dist/core/src/math/perlin-noise-3d.js +76 -0
- package/dist/core/src/math/quat.js +29 -0
- package/dist/core/src/math/rng.js +25 -0
- package/dist/core/src/math/trig.js +42 -0
- package/dist/core/src/math/value-noise.js +49 -0
- package/dist/core/src/mesh/builder.js +137 -0
- package/dist/core/src/mesh/csg.js +250 -0
- package/dist/core/src/mesh/greased-line.js +102 -0
- package/dist/core/src/mesh/mesh.js +6 -0
- package/dist/core/src/mesh/parametric.js +392 -0
- package/dist/core/src/mesh/skinning.js +30 -0
- package/dist/core/src/mesh/vertex-layout.js +65 -0
- package/dist/core/src/module/cross-thread.js +126 -0
- package/dist/core/src/module/diagnostics.js +99 -0
- package/dist/core/src/module/host.js +449 -0
- package/dist/core/src/module/index.js +6 -0
- package/dist/core/src/module/module.js +1 -0
- package/dist/core/src/module/registry.js +83 -0
- package/dist/core/src/module/renderer-module.js +13 -0
- package/dist/core/src/module/ts-loader.js +20 -0
- package/dist/core/src/particles/compute-pass.js +329 -0
- package/dist/core/src/particles/emitter.js +113 -0
- package/dist/core/src/particles/index.js +7 -0
- package/dist/core/src/particles/particle-data.js +33 -0
- package/dist/core/src/particles/render-pass.js +338 -0
- package/dist/core/src/particles/simulator.js +224 -0
- package/dist/core/src/particles/system.js +157 -0
- package/dist/core/src/physics/body.js +25 -0
- package/dist/core/src/physics/ccd-heuristic.js +46 -0
- package/dist/core/src/physics/character.js +184 -0
- package/dist/core/src/physics/collider.js +36 -0
- package/dist/core/src/physics/collision-system.js +62 -0
- package/dist/core/src/physics/events.js +74 -0
- package/dist/core/src/physics/index.js +24 -0
- package/dist/core/src/physics/interface.js +14 -0
- package/dist/core/src/physics/interpolation-buffer.js +182 -0
- package/dist/core/src/physics/lifecycle.js +66 -0
- package/dist/core/src/physics/load-shedder.js +155 -0
- package/dist/core/src/physics/physics-accumulator.js +94 -0
- package/dist/core/src/physics/physics-system.js +142 -0
- package/dist/core/src/physics/ragdoll-presets.js +248 -0
- package/dist/core/src/physics/ragdoll-system.js +193 -0
- package/dist/core/src/physics/ragdoll.js +117 -0
- package/dist/core/src/physics/raycast.js +97 -0
- package/dist/core/src/physics/realm-manager.js +431 -0
- package/dist/core/src/physics/realm-worker-pool.js +181 -0
- package/dist/core/src/physics/realm-worker.js +91 -0
- package/dist/core/src/physics/realm.js +137 -0
- package/dist/core/src/physics/registry.js +43 -0
- package/dist/core/src/physics/safety.js +211 -0
- package/dist/core/src/physics/snapshot-manager.js +118 -0
- package/dist/core/src/physics/worker-protocol.js +1 -0
- package/dist/core/src/platform/bun-preload.js +175 -0
- package/dist/core/src/platform/css-stub.js +4 -0
- package/dist/core/src/platform/glob-polyfill.js +137 -0
- package/dist/core/src/platform/hidpi.js +52 -0
- package/dist/core/src/platform/pkg-root.js +27 -0
- package/dist/core/src/platform/render-surface.js +15 -0
- package/dist/core/src/platform/rpc.js +82 -0
- package/dist/core/src/platform/runtime.js +93 -0
- package/dist/core/src/plugin/context.js +144 -0
- package/dist/core/src/plugin/diagnostics.js +20 -0
- package/dist/core/src/plugin/discovery.js +124 -0
- package/dist/core/src/plugin/extension-loaders.js +111 -0
- package/dist/core/src/plugin/host.js +542 -0
- package/dist/core/src/plugin/index.js +19 -0
- package/dist/core/src/plugin/loader-asset.js +105 -0
- package/dist/core/src/plugin/loader-quickjs.js +67 -0
- package/dist/core/src/plugin/loader-wasm.js +385 -0
- package/dist/core/src/plugin/loader-worker.js +228 -0
- package/dist/core/src/plugin/manifest.js +424 -0
- package/dist/core/src/plugin/material-registry.js +44 -0
- package/dist/core/src/plugin/mcp-tools.js +147 -0
- package/dist/core/src/plugin/permissions.js +152 -0
- package/dist/core/src/plugin/quickjs-bridge.js +219 -0
- package/dist/core/src/plugin/registry.js +92 -0
- package/dist/core/src/plugin/sandbox-shim.js +79 -0
- package/dist/core/src/plugin/sandbox-worker.js +194 -0
- package/dist/core/src/plugin/wasm-abi.js +62 -0
- package/dist/core/src/plugin/wasm-worker.js +234 -0
- package/dist/core/src/plugin/workshop.js +131 -0
- package/dist/core/src/profiling/event-loop.js +164 -0
- package/dist/core/src/profiling/index.js +23 -0
- package/dist/core/src/profiling/iops/idb-patch.js +211 -0
- package/dist/core/src/profiling/iops/opfs-patch.js +206 -0
- package/dist/core/src/profiling/profiling-sab.js +632 -0
- package/dist/core/src/profiling/task-latency.js +118 -0
- package/dist/core/src/profiling/thread-metrics.js +103 -0
- package/dist/core/src/profiling/trace-event-writer.js +165 -0
- package/dist/core/src/profiling/warnings.js +304 -0
- package/dist/core/src/profiling/worker-prelude.js +281 -0
- package/dist/core/src/render/bind-group.js +33 -0
- package/dist/core/src/render/bindless/bindless-struct.js +39 -0
- package/dist/core/src/render/bindless/bindless.wgsl.js +163 -0
- package/dist/core/src/render/bindless/frame-bindings.js +142 -0
- package/dist/core/src/render/bindless/index.js +6 -0
- package/dist/core/src/render/bindless/material-manager.js +182 -0
- package/dist/core/src/render/bindless/texture-registry.js +498 -0
- package/dist/core/src/render/buffer.js +74 -0
- package/dist/core/src/render/camera-2d.js +151 -0
- package/dist/core/src/render/camera-controller.js +370 -0
- package/dist/core/src/render/camera.js +332 -0
- package/dist/core/src/render/canvas-resize-watcher.js +36 -0
- package/dist/core/src/render/compute-kernel.js +130 -0
- package/dist/core/src/render/constants.js +2 -0
- package/dist/core/src/render/device.js +91 -0
- package/dist/core/src/render/frame-graph.js +594 -0
- package/dist/core/src/render/frustum.js +152 -0
- package/dist/core/src/render/fullscreen-vs.js +24 -0
- package/dist/core/src/render/g-buffer.js +156 -0
- package/dist/core/src/render/game-renderer.js +1165 -0
- package/dist/core/src/render/gpu-driven/cull-pass.js +352 -0
- package/dist/core/src/render/gpu-driven/hzb.js +183 -0
- package/dist/core/src/render/gpu-driven/index.js +6 -0
- package/dist/core/src/render/gpu-driven/indirect-draw-pass.js +300 -0
- package/dist/core/src/render/gpu-driven/instance-buffer.js +150 -0
- package/dist/core/src/render/gpu-driven/mesh-table.js +130 -0
- package/dist/core/src/render/gpu-driven/passes/hzb-build-pass.js +39 -0
- package/dist/core/src/render/gpu-utils.js +72 -0
- package/dist/core/src/render/hdr.js +69 -0
- package/dist/core/src/render/hot-reload.js +236 -0
- package/dist/core/src/render/ibl-bind-group.js +232 -0
- package/dist/core/src/render/ibl.js +148 -0
- package/dist/core/src/render/index.js +86 -0
- package/dist/core/src/render/input-manager.js +224 -0
- package/dist/core/src/render/lighting/area-light.js +142 -0
- package/dist/core/src/render/lighting/cluster-grid.js +255 -0
- package/dist/core/src/render/lighting/cluster-types.js +87 -0
- package/dist/core/src/render/lighting/ies-parser.js +111 -0
- package/dist/core/src/render/lighting.js +300 -0
- package/dist/core/src/render/material-types/depth.wgsl +44 -0
- package/dist/core/src/render/material-types/line.wgsl +50 -0
- package/dist/core/src/render/material-types/matcap.wgsl +51 -0
- package/dist/core/src/render/material-types/normal.wgsl +41 -0
- package/dist/core/src/render/material-types/physical.wgsl +152 -0
- package/dist/core/src/render/material-types/sprite.wgsl +46 -0
- package/dist/core/src/render/material-types/sss.wgsl +79 -0
- package/dist/core/src/render/material-types/toon.wgsl +79 -0
- package/dist/core/src/render/passes/README.md +121 -0
- package/dist/core/src/render/passes/atmosphere.js +107 -0
- package/dist/core/src/render/passes/cluster-lighting-pass.js +441 -0
- package/dist/core/src/render/passes/csm.js +338 -0
- package/dist/core/src/render/passes/cubemap-capture.js +93 -0
- package/dist/core/src/render/passes/debug-viz.js +248 -0
- package/dist/core/src/render/passes/debug.js +500 -0
- package/dist/core/src/render/passes/decal-mesh.js +86 -0
- package/dist/core/src/render/passes/decal-pass.js +344 -0
- package/dist/core/src/render/passes/deferred-lighting.js +406 -0
- package/dist/core/src/render/passes/depth-prepass.js +148 -0
- package/dist/core/src/render/passes/dome-360.js +68 -0
- package/dist/core/src/render/passes/fluid-render.js +116 -0
- package/dist/core/src/render/passes/gi-types.js +78 -0
- package/dist/core/src/render/passes/graph-compute.js +251 -0
- package/dist/core/src/render/passes/greased-line-pass.js +218 -0
- package/dist/core/src/render/passes/morph-target.js +35 -0
- package/dist/core/src/render/passes/opaque.js +966 -0
- package/dist/core/src/render/passes/point-light-shadow.js +221 -0
- package/dist/core/src/render/passes/procedural-texture.js +142 -0
- package/dist/core/src/render/passes/reflection-probe.js +100 -0
- package/dist/core/src/render/passes/rsm-pass.js +228 -0
- package/dist/core/src/render/passes/sdf-text.js +114 -0
- package/dist/core/src/render/passes/shadow-map.js +121 -0
- package/dist/core/src/render/passes/shadow.js +191 -0
- package/dist/core/src/render/passes/skinning-vs.js +102 -0
- package/dist/core/src/render/passes/skinning.js +282 -0
- package/dist/core/src/render/passes/sky-dome.js +380 -0
- package/dist/core/src/render/passes/skybox.js +146 -0
- package/dist/core/src/render/passes/spot-light-shadow.js +223 -0
- package/dist/core/src/render/passes/terrain.js +303 -0
- package/dist/core/src/render/passes/transparent.js +323 -0
- package/dist/core/src/render/passes/underwater-fog.js +173 -0
- package/dist/core/src/render/passes/video-texture.js +58 -0
- package/dist/core/src/render/passes/volumetric-pass.js +278 -0
- package/dist/core/src/render/passes/volumetric-types.js +96 -0
- package/dist/core/src/render/passes/water.js +697 -0
- package/dist/core/src/render/pbr.js +257 -0
- package/dist/core/src/render/pipeline.js +49 -0
- package/dist/core/src/render/render-pass.js +13 -0
- package/dist/core/src/render/renderer-input-bus.js +176 -0
- package/dist/core/src/render/renderer-module-host.js +539 -0
- package/dist/core/src/render/resource-tracker.js +59 -0
- package/dist/core/src/render/sab-canvas-overlay.js +83 -0
- package/dist/core/src/render/shader-validator.js +172 -0
- package/dist/core/src/render/shaders/debug/debug-line.wgsl +27 -0
- package/dist/core/src/render/shaders/debug/debug-point.wgsl +30 -0
- package/dist/core/src/render/shaders/debug/debug-text.wgsl +42 -0
- package/dist/core/src/render/shaders/debug-viz/aabb.wgsl +20 -0
- package/dist/core/src/render/shaders/debug-viz/depth.wgsl +29 -0
- package/dist/core/src/render/shaders/debug-viz/lod.wgsl +38 -0
- package/dist/core/src/render/shaders/debug-viz/normals.wgsl +30 -0
- package/dist/core/src/render/shaders/debug-viz/overdraw.wgsl +21 -0
- package/dist/core/src/render/shaders/debug-viz/tangents.wgsl +29 -0
- package/dist/core/src/render/shaders/debug-viz/wireframe.wgsl +33 -0
- package/dist/core/src/render/shaders/shadow-chunks.js +133 -0
- package/dist/core/src/render/skybox.js +123 -0
- package/dist/core/src/render/splitscreen.js +60 -0
- package/dist/core/src/render/surface.js +63 -0
- package/dist/core/src/render/tonemap.js +171 -0
- package/dist/core/src/render/tracked-render-pass.js +115 -0
- package/dist/core/src/render/viewport.js +81 -0
- package/dist/core/src/render/wgsl-hmr.js +66 -0
- package/dist/core/src/sab/core-input-channel.js +22 -0
- package/dist/core/src/sab/define.js +127 -0
- package/dist/core/src/sab/errors.js +60 -0
- package/dist/core/src/sab/game-input.js +299 -0
- package/dist/core/src/sab/gamepad-devices.js +205 -0
- package/dist/core/src/sab/grid-buffer.js +91 -0
- package/dist/core/src/sab/input.js +38 -0
- package/dist/core/src/sab/layout.js +160 -0
- package/dist/core/src/sab/multi-input-channel.js +39 -0
- package/dist/core/src/sab/record-buffer.js +130 -0
- package/dist/core/src/sab/sab-polyfill.js +47 -0
- package/dist/core/src/sab/sim-channel.js +397 -0
- package/dist/core/src/sab/slot-buffer.js +140 -0
- package/dist/core/src/sab/types.js +1 -0
- package/dist/core/src/safety/bounds.js +71 -0
- package/dist/core/src/safety/json.js +70 -0
- package/dist/core/src/safety/path.js +151 -0
- package/dist/core/src/save/binary-format.js +95 -0
- package/dist/core/src/save/game-save-system.js +101 -0
- package/dist/core/src/save/grid-save-system.js +102 -0
- package/dist/core/src/save/migrate.js +35 -0
- package/dist/core/src/save/migration-registry.js +66 -0
- package/dist/core/src/save/persist-types.js +4 -0
- package/dist/core/src/save/schema.js +36 -0
- package/dist/core/src/save/serializer.js +139 -0
- package/dist/core/src/scene/builder.js +31 -0
- package/dist/core/src/scene/camera.js +118 -0
- package/dist/core/src/scene/checkpoint.js +84 -0
- package/dist/core/src/scene/game-loop.js +155 -0
- package/dist/core/src/scene/index.js +14 -0
- package/dist/core/src/scene/layer.js +7 -0
- package/dist/core/src/scene/prefab.js +97 -0
- package/dist/core/src/scene/scene-manager.js +141 -0
- package/dist/core/src/scene/scene.js +147 -0
- package/dist/core/src/scene/sky.js +179 -0
- package/dist/core/src/scene/spatial-grid.js +140 -0
- package/dist/core/src/scene/streaming.js +247 -0
- package/dist/core/src/scene/vegetation.js +107 -0
- package/dist/core/src/scene/world.js +66 -0
- package/dist/core/src/scripting/binding.js +85 -0
- package/dist/core/src/scripting/hot-reload.js +123 -0
- package/dist/core/src/scripting/script.js +68 -0
- package/dist/core/src/sim/hot-reload-pipeline.js +65 -0
- package/dist/core/src/sim/sim-state-helper.js +40 -0
- package/dist/core/src/sim/transient-state-registry.js +63 -0
- package/dist/core/src/sim/types.js +4 -0
- package/dist/core/src/sim/worker-loop.js +168 -0
- package/dist/core/src/stores/game-store.js +81 -0
- package/dist/core/src/stores/hot-reload-store.js +19 -0
- package/dist/core/src/telemetry/collector.js +303 -0
- package/dist/core/src/telemetry/gc-controller.js +365 -0
- package/dist/core/src/telemetry/gc-profiler.js +82 -0
- package/dist/core/src/telemetry/gc-tracker.js +63 -0
- package/dist/core/src/telemetry/gpu-profiler.js +554 -0
- package/dist/core/src/telemetry/gpu-resource-tracker.js +234 -0
- package/dist/core/src/telemetry/gpu-timer-pool.js +223 -0
- package/dist/core/src/telemetry/gpu-timer.js +117 -0
- package/dist/core/src/telemetry/gpu-timestamp-gate.js +22 -0
- package/dist/core/src/telemetry/reporter.js +56 -0
- package/dist/core/src/types/engine-types.js +21 -0
- package/dist/core/src/ui-font-scale.js +111 -0
- package/dist/core/src/util/broad-phase-grid.js +77 -0
- package/dist/core/src/util/feature-log.js +176 -0
- package/dist/core/src/util/index.js +4 -0
- package/dist/core/src/util/logger.js +596 -0
- package/dist/core/src/version.js +11 -0
- package/dist/core/src/webgpu-destroy-augmentation.d.ts +25 -0
- package/dist/core/src/worker/base-worker-host.js +212 -0
- package/dist/core/src/worker/buffer-sync.js +454 -0
- package/dist/core/src/worker/crash-recovery.js +153 -0
- package/dist/core/src/worker/entity-sim-worker-host.js +128 -0
- package/dist/core/src/worker/instrumented-worker-host.js +139 -0
- package/dist/core/src/worker/rpc.js +244 -0
- package/dist/core/src/worker/sim-worker-base.js +558 -0
- package/dist/core/src/worker/sim-worker-host.js +271 -0
- package/dist/core/src/worker/task-pool.js +205 -0
- package/dist/core/src/worker/task-worker.js +77 -0
- package/dist/libraries/asset-browser/src/index.js +10 -0
- package/dist/libraries/asset-browser/src/software-thumbnails.js +940 -0
- package/dist/libraries/audio-kira/src/backend.js +405 -0
- package/dist/libraries/audio-kira/src/index.js +4 -0
- package/dist/libraries/audio-kira/src/library.js +54 -0
- package/dist/libraries/blitz-ui/src/index.js +13 -0
- package/dist/libraries/blitz-ui/src/native-module.js +162 -0
- package/dist/libraries/blitz-ui/src/native-osr-ffi.js +409 -0
- package/dist/libraries/blitz-ui/src/router.js +82 -0
- package/dist/libraries/blitz-ui/src/shaders/ui-blit.wgsl.js +32 -0
- package/dist/libraries/blitz-ui/src/ui-blit-pass.js +108 -0
- package/dist/libraries/character/src/character-animator.js +323 -0
- package/dist/libraries/character/src/customization.js +464 -0
- package/dist/libraries/character/src/index.js +19 -0
- package/dist/libraries/character/src/inspectable.js +77 -0
- package/dist/libraries/character/src/loader.js +336 -0
- package/dist/libraries/character/src/locomotion.js +84 -0
- package/dist/libraries/character/src/preview.js +280 -0
- package/dist/libraries/devtools/src/backend.js +449 -0
- package/dist/libraries/devtools/src/cdp-bridge.js +293 -0
- package/dist/libraries/devtools/src/collectors.js +318 -0
- package/dist/libraries/devtools/src/index.js +31 -0
- package/dist/libraries/devtools/src/native-providers.js +749 -0
- package/dist/libraries/devtools/src/snapshot.js +35 -0
- package/dist/libraries/devtools/src/verify.js +139 -0
- package/dist/libraries/devtools/src/web/browser.js +94 -0
- package/dist/libraries/devtools/src/web/host.js +183 -0
- package/dist/libraries/devtools/src/web/mirror.js +26 -0
- package/dist/libraries/devtools/src/web/server.js +187 -0
- package/dist/libraries/entities/src/index.js +3 -0
- package/dist/libraries/entities/src/library.js +46 -0
- package/dist/libraries/entities/src/model-renderer.js +1307 -0
- package/dist/libraries/entities/src/shaders/model.wgsl +375 -0
- package/dist/libraries/gamepad/src/feed-input.js +54 -0
- package/dist/libraries/gamepad/src/filters.js +44 -0
- package/dist/libraries/gamepad/src/glyphs.js +83 -0
- package/dist/libraries/gamepad/src/hub.js +164 -0
- package/dist/libraries/gamepad/src/index.js +10 -0
- package/dist/libraries/gamepad/src/library.js +56 -0
- package/dist/libraries/gamepad/src/source-sab.js +62 -0
- package/dist/libraries/gamepad/src/sysfs-enrich.js +125 -0
- package/dist/libraries/gaussian-splats/src/gpu-sort.js +506 -0
- package/dist/libraries/gaussian-splats/src/index.js +8 -0
- package/dist/libraries/gaussian-splats/src/library.js +44 -0
- package/dist/libraries/gaussian-splats/src/parser.js +297 -0
- package/dist/libraries/gaussian-splats/src/renderer.js +368 -0
- package/dist/libraries/gaussian-splats/src/sh-eval.js +196 -0
- package/dist/libraries/gaussian-splats/src/sorter.js +76 -0
- package/dist/libraries/gaussian-splats/src/tile-raster.js +424 -0
- package/dist/libraries/gpu-kernels/src/index.js +8 -0
- package/dist/libraries/gpu-kernels/src/kernel.js +591 -0
- package/dist/libraries/gpu-kernels/src/transpile.js +896 -0
- package/dist/libraries/html-ui-kit/src/behaviors.js +231 -0
- package/dist/libraries/html-ui-kit/src/components.js +219 -0
- package/dist/libraries/html-ui-kit/src/index.js +17 -0
- package/dist/libraries/html-ui-kit/src/nav/nav-controller.js +215 -0
- package/dist/libraries/html-ui-kit/src/nav/nav-input.js +138 -0
- package/dist/libraries/html-ui-kit/src/nav/osk.js +135 -0
- package/dist/libraries/html-ui-kit/src/nav/router.js +152 -0
- package/dist/libraries/html-ui-kit/src/nav/vcursor.js +177 -0
- package/dist/libraries/html-ui-kit/src/ten-foot.js +138 -0
- package/dist/libraries/html-ui-kit/src/theme.js +235 -0
- package/dist/libraries/inspectable/src/index.js +6 -0
- package/dist/libraries/inspectable/src/inspectable.js +205 -0
- package/dist/libraries/lighting/src/index.js +4 -0
- package/dist/libraries/lighting/src/library.js +48 -0
- package/dist/libraries/lighting/src/light-system.js +395 -0
- package/dist/libraries/lighting/src/lighting-system.js +57 -0
- package/dist/libraries/lighting/src/shaders/light-debug.wgsl +30 -0
- package/dist/libraries/marching-cubes/src/chunked-field.js +130 -0
- package/dist/libraries/marching-cubes/src/deformation.js +41 -0
- package/dist/libraries/marching-cubes/src/generator.js +356 -0
- package/dist/libraries/marching-cubes/src/index.js +13 -0
- package/dist/libraries/marching-cubes/src/library.js +54 -0
- package/dist/libraries/marching-cubes/src/lod.js +95 -0
- package/dist/libraries/marching-cubes/src/marching-tetrahedra.js +261 -0
- package/dist/libraries/marching-cubes/src/sab.js +40 -0
- package/dist/libraries/marching-cubes/src/streaming-config.js +28 -0
- package/dist/libraries/marching-cubes/src/streaming-manager.js +490 -0
- package/dist/libraries/marching-cubes/src/tables.js +314 -0
- package/dist/libraries/marching-cubes/src/types.js +1 -0
- package/dist/libraries/models/src/bake-node-transforms.js +146 -0
- package/dist/libraries/models/src/bind-unskinned-meshes.js +145 -0
- package/dist/libraries/models/src/codecs/basisu-codec.js +134 -0
- package/dist/libraries/models/src/codecs/draco-codec.js +236 -0
- package/dist/libraries/models/src/codecs/draco3dgltf.d.ts +40 -0
- package/dist/libraries/models/src/codecs/index.js +5 -0
- package/dist/libraries/models/src/codecs/meshopt-codec.js +59 -0
- package/dist/libraries/models/src/codecs/registry.js +111 -0
- package/dist/libraries/models/src/dae.js +665 -0
- package/dist/libraries/models/src/default-part-selection.js +58 -0
- package/dist/libraries/models/src/dxf.js +235 -0
- package/dist/libraries/models/src/fbx/index.js +438 -0
- package/dist/libraries/models/src/fbx/interpreter/animation.js +369 -0
- package/dist/libraries/models/src/fbx/interpreter/blend-shapes.js +283 -0
- package/dist/libraries/models/src/fbx/interpreter/connections.js +158 -0
- package/dist/libraries/models/src/fbx/interpreter/diagnostics.js +48 -0
- package/dist/libraries/models/src/fbx/interpreter/geometry.js +430 -0
- package/dist/libraries/models/src/fbx/interpreter/global-settings.js +77 -0
- package/dist/libraries/models/src/fbx/interpreter/materials.js +231 -0
- package/dist/libraries/models/src/fbx/interpreter/nodes.js +168 -0
- package/dist/libraries/models/src/fbx/interpreter/skeleton.js +454 -0
- package/dist/libraries/models/src/fbx/parsers/binary-parser.js +217 -0
- package/dist/libraries/models/src/fbx/types.js +88 -0
- package/dist/libraries/models/src/gltf-extensions.js +121 -0
- package/dist/libraries/models/src/gltf.js +724 -0
- package/dist/libraries/models/src/index.js +46 -0
- package/dist/libraries/models/src/library.js +44 -0
- package/dist/libraries/models/src/loader.js +218 -0
- package/dist/libraries/models/src/material-adapter.js +82 -0
- package/dist/libraries/models/src/mini-xml.js +145 -0
- package/dist/libraries/models/src/normalize.js +168 -0
- package/dist/libraries/models/src/obj.js +247 -0
- package/dist/libraries/models/src/off.js +94 -0
- package/dist/libraries/models/src/ply.js +329 -0
- package/dist/libraries/models/src/pose-sampler.js +183 -0
- package/dist/libraries/models/src/sidecar/blender-extras.js +32 -0
- package/dist/libraries/models/src/sidecar/ddmeta.js +98 -0
- package/dist/libraries/models/src/sidecar/godot-import.js +76 -0
- package/dist/libraries/models/src/sidecar/resolver.js +112 -0
- package/dist/libraries/models/src/sidecar/types.js +8 -0
- package/dist/libraries/models/src/sidecar/unity-meta.js +46 -0
- package/dist/libraries/models/src/skeleton-synthesis.js +122 -0
- package/dist/libraries/models/src/stl.js +129 -0
- package/dist/libraries/models/src/threeds.js +312 -0
- package/dist/libraries/models/src/threemf.js +203 -0
- package/dist/libraries/models/src/types.js +33 -0
- package/dist/libraries/models/src/vtk.js +242 -0
- package/dist/libraries/navmesh/src/crowd-system.js +207 -0
- package/dist/libraries/navmesh/src/debug-viz.js +52 -0
- package/dist/libraries/navmesh/src/index.js +7 -0
- package/dist/libraries/navmesh/src/library.js +47 -0
- package/dist/libraries/navmesh/src/navmesh-generator.js +364 -0
- package/dist/libraries/navmesh/src/navmesh.js +141 -0
- package/dist/libraries/navmesh/src/pathfinder.js +221 -0
- package/dist/libraries/navmesh/src/types.js +1 -0
- package/dist/libraries/networking/src/authority.js +107 -0
- package/dist/libraries/networking/src/connection.js +107 -0
- package/dist/libraries/networking/src/discovery.js +314 -0
- package/dist/libraries/networking/src/enhanced-replication.js +282 -0
- package/dist/libraries/networking/src/epic-adapter.js +101 -0
- package/dist/libraries/networking/src/index.js +16 -0
- package/dist/libraries/networking/src/library.js +45 -0
- package/dist/libraries/networking/src/lobby.js +84 -0
- package/dist/libraries/networking/src/platform-adapter.js +92 -0
- package/dist/libraries/networking/src/rcs-adapter.js +100 -0
- package/dist/libraries/networking/src/remote-input.js +162 -0
- package/dist/libraries/networking/src/replication.js +273 -0
- package/dist/libraries/networking/src/rpc.js +172 -0
- package/dist/libraries/networking/src/session.js +129 -0
- package/dist/libraries/networking/src/transport.js +156 -0
- package/dist/libraries/networking/src/webrtc.js +266 -0
- package/dist/libraries/pathfinding-2d/src/astar-grid.js +255 -0
- package/dist/libraries/pathfinding-2d/src/index.js +9 -0
- package/dist/libraries/persistence/src/autosave-manager.js +94 -0
- package/dist/libraries/persistence/src/binary-record-store.js +205 -0
- package/dist/libraries/persistence/src/browser.js +16 -0
- package/dist/libraries/persistence/src/file-save-store.js +507 -0
- package/dist/libraries/persistence/src/hash-utils.js +54 -0
- package/dist/libraries/persistence/src/index.js +17 -0
- package/dist/libraries/persistence/src/library.js +51 -0
- package/dist/libraries/persistence/src/mock-opfs.js +114 -0
- package/dist/libraries/persistence/src/node-fs-directory-handle.js +168 -0
- package/dist/libraries/persistence/src/opfs-save-store.js +584 -0
- package/dist/libraries/persistence/src/save-worker-proxy.js +96 -0
- package/dist/libraries/persistence/src/save-worker.js +76 -0
- package/dist/libraries/persistence/src/xxh3-ts.d.ts +10 -0
- package/dist/libraries/physics-native/src/backend.js +960 -0
- package/dist/libraries/physics-native/src/broadphase.js +79 -0
- package/dist/libraries/physics-native/src/index.js +6 -0
- package/dist/libraries/physics-native/src/library.js +57 -0
- package/dist/libraries/physics-native/src/narrowphase-shapes.js +348 -0
- package/dist/libraries/physics-native/src/narrowphase.js +60 -0
- package/dist/libraries/physics-native/src/solver.js +105 -0
- package/dist/libraries/physics-native/src/types.js +4 -0
- package/dist/libraries/physics-rapier/src/api.js +352 -0
- package/dist/libraries/physics-rapier/src/auto-backend.js +54 -0
- package/dist/libraries/physics-rapier/src/backend.js +1066 -0
- package/dist/libraries/physics-rapier/src/bulk-ops.js +46 -0
- package/dist/libraries/physics-rapier/src/ffi-backend.js +17 -0
- package/dist/libraries/physics-rapier/src/ffi-lib.js +605 -0
- package/dist/libraries/physics-rapier/src/index.js +9 -0
- package/dist/libraries/physics-rapier/src/library.js +71 -0
- package/dist/libraries/physics-rapier/src/rapier-backend.js +1102 -0
- package/dist/libraries/physics-rapier/src/realm-worker-script.js +233 -0
- package/dist/libraries/postfx/src/index.js +14 -0
- package/dist/libraries/postfx/src/library.js +207 -0
- package/dist/libraries/postfx/src/post-process-stack.js +1641 -0
- package/dist/libraries/postfx/src/shaders/post-process/afterimage.wgsl +14 -0
- package/dist/libraries/postfx/src/shaders/post-process/ascii.wgsl +24 -0
- package/dist/libraries/postfx/src/shaders/post-process/blit.wgsl +12 -0
- package/dist/libraries/postfx/src/shaders/post-process/bloom-blur.wgsl +21 -0
- package/dist/libraries/postfx/src/shaders/post-process/bloom-bright.wgsl +15 -0
- package/dist/libraries/postfx/src/shaders/post-process/bloom-composite.wgsl +14 -0
- package/dist/libraries/postfx/src/shaders/post-process/bloom-downsample.wgsl +60 -0
- package/dist/libraries/postfx/src/shaders/post-process/bloom-soft.wgsl +46 -0
- package/dist/libraries/postfx/src/shaders/post-process/bloom-upsample.wgsl +51 -0
- package/dist/libraries/postfx/src/shaders/post-process/channel-mixer.wgsl +36 -0
- package/dist/libraries/postfx/src/shaders/post-process/chromatic-aberration.wgsl +37 -0
- package/dist/libraries/postfx/src/shaders/post-process/dithering.wgsl +64 -0
- package/dist/libraries/postfx/src/shaders/post-process/dof.wgsl +131 -0
- package/dist/libraries/postfx/src/shaders/post-process/edges.wgsl +45 -0
- package/dist/libraries/postfx/src/shaders/post-process/fullscreen-vs.wgsl +15 -0
- package/dist/libraries/postfx/src/shaders/post-process/fxaa.wgsl +102 -0
- package/dist/libraries/postfx/src/shaders/post-process/gaussian-blur.wgsl +37 -0
- package/dist/libraries/postfx/src/shaders/post-process/glow-blur.wgsl +33 -0
- package/dist/libraries/postfx/src/shaders/post-process/glow-composite.wgsl +25 -0
- package/dist/libraries/postfx/src/shaders/post-process/grain.wgsl +37 -0
- package/dist/libraries/postfx/src/shaders/post-process/halftone.wgsl +80 -0
- package/dist/libraries/postfx/src/shaders/post-process/highlight-blur.wgsl +33 -0
- package/dist/libraries/postfx/src/shaders/post-process/highlight-composite.wgsl +32 -0
- package/dist/libraries/postfx/src/shaders/post-process/lens-distortion.wgsl +43 -0
- package/dist/libraries/postfx/src/shaders/post-process/lens-flare.wgsl +68 -0
- package/dist/libraries/postfx/src/shaders/post-process/lut.wgsl +30 -0
- package/dist/libraries/postfx/src/shaders/post-process/motion-blur.wgsl +55 -0
- package/dist/libraries/postfx/src/shaders/post-process/occluder-chunk.wgsl +28 -0
- package/dist/libraries/postfx/src/shaders/post-process/outline.wgsl +51 -0
- package/dist/libraries/postfx/src/shaders/post-process/pixelation.wgsl +47 -0
- package/dist/libraries/postfx/src/shaders/post-process/sharpen.wgsl +32 -0
- package/dist/libraries/postfx/src/shaders/post-process/sobel.wgsl +26 -0
- package/dist/libraries/postfx/src/shaders/post-process/split-tone.wgsl +36 -0
- package/dist/libraries/postfx/src/shaders/post-process/ssao-blur.wgsl +38 -0
- package/dist/libraries/postfx/src/shaders/post-process/ssao-composite.wgsl +25 -0
- package/dist/libraries/postfx/src/shaders/post-process/ssao.wgsl +147 -0
- package/dist/libraries/postfx/src/shaders/post-process/ssr.wgsl +155 -0
- package/dist/libraries/postfx/src/shaders/post-process/taa.wgsl +98 -0
- package/dist/libraries/postfx/src/shaders/post-process/tonemap.wgsl +40 -0
- package/dist/libraries/postfx/src/shaders/post-process/watercolor.wgsl +82 -0
- package/dist/libraries/postfx/src/shaders/post-process/white-balance.wgsl +41 -0
- package/dist/libraries/recast/src/backend.js +124 -0
- package/dist/libraries/recast/src/crowd-system.js +310 -0
- package/dist/libraries/recast/src/index.js +4 -0
- package/dist/libraries/recast/src/library.js +59 -0
- package/dist/libraries/recast/src/types.js +4 -0
- package/dist/libraries/sand/src/cell.js +32 -0
- package/dist/libraries/sand/src/fields.js +27 -0
- package/dist/libraries/sand/src/fluid-grid.js +267 -0
- package/dist/libraries/sand/src/grid-dims.js +47 -0
- package/dist/libraries/sand/src/grid-sim-buffer.js +184 -0
- package/dist/libraries/sand/src/index.js +20 -0
- package/dist/libraries/sand/src/library.js +67 -0
- package/dist/libraries/sand/src/materials.js +437 -0
- package/dist/libraries/sand/src/palette.js +87 -0
- package/dist/libraries/sand/src/particles.js +163 -0
- package/dist/libraries/sand/src/render/sand-grid-pass.js +377 -0
- package/dist/libraries/sand/src/rng.js +44 -0
- package/dist/libraries/sand/src/rules/builtin-rules.js +125 -0
- package/dist/libraries/sand/src/rules/rule-compiler.js +164 -0
- package/dist/libraries/sand/src/rules/rule-engine.js +210 -0
- package/dist/libraries/sand/src/rules/rule-types.js +27 -0
- package/dist/libraries/sand/src/sand-step-pool.js +458 -0
- package/dist/libraries/sand/src/sand-step-worker.js +101 -0
- package/dist/libraries/sand/src/sand-world.js +3382 -0
- package/dist/libraries/stickman/src/index.js +7 -0
- package/dist/libraries/stickman/src/library.js +44 -0
- package/dist/libraries/stickman/src/proportions.js +40 -0
- package/dist/libraries/stickman/src/shader.js +113 -0
- package/dist/libraries/stickman/src/skeleton.js +163 -0
- package/dist/libraries/stickman/src/stickman-pass.js +210 -0
- package/dist/libraries/stickman/src/thick-line.js +120 -0
- package/dist/libraries/surface-nets/src/chunked-field.js +5 -0
- package/dist/libraries/surface-nets/src/deformation.js +29 -0
- package/dist/libraries/surface-nets/src/index.js +15 -0
- package/dist/libraries/surface-nets/src/library.js +41 -0
- package/dist/libraries/surface-nets/src/lod.js +6 -0
- package/dist/libraries/surface-nets/src/sab.js +4 -0
- package/dist/libraries/surface-nets/src/streaming-config.js +4 -0
- package/dist/libraries/surface-nets/src/streaming-manager.js +9 -0
- package/dist/libraries/surface-nets/src/surface-nets.js +361 -0
- package/dist/libraries/surface-nets/src/types.js +1 -0
- package/dist/libraries/water/src/buoyancy.js +80 -0
- package/dist/libraries/water/src/index.js +12 -0
- package/dist/libraries/water/src/library.js +65 -0
- package/dist/libraries/water/src/shore-damping.js +70 -0
- package/dist/libraries/water/src/water-buffer.js +78 -0
- package/dist/libraries/water/src/water-chunks.js +5 -0
- package/dist/libraries/water/src/water-physics.js +315 -0
- package/dist/libraries/water/src/water-renderer.js +736 -0
- package/dist/libraries/water/src/water-sab.js +138 -0
- package/dist/libraries/water/src/wave-sources.js +45 -0
- package/dist/libraries/weather/src/index.js +5 -0
- package/dist/libraries/weather/src/library.js +48 -0
- package/dist/libraries/weather/src/types.js +29 -0
- package/dist/libraries/weather/src/weather-blend.js +108 -0
- package/dist/libraries/weather/src/weather-system.js +275 -0
- package/dist/libraries/weatherfx/src/cloud-provider.js +5 -0
- package/dist/libraries/weatherfx/src/cloud-system.js +294 -0
- package/dist/libraries/weatherfx/src/index.js +4 -0
- package/dist/libraries/weatherfx/src/library.js +41 -0
- package/dist/libraries/weatherfx/src/particle-system.js +350 -0
- package/dist/libraries/weatherfx/src/shaders/cloud.wgsl +111 -0
- package/dist/libraries/weatherfx/src/shaders/particle-compute.wgsl +187 -0
- package/dist/libraries/weatherfx/src/shaders/particle-render.wgsl +95 -0
- package/dist/mcp/src/client.js +669 -0
- package/dist/mcp/src/engine-context.js +99 -0
- package/dist/mcp/src/http-transport.js +423 -0
- package/dist/mcp/src/index.js +5 -0
- package/dist/mcp/src/prompts/add-entity.js +39 -0
- package/dist/mcp/src/prompts/create-scene.js +41 -0
- package/dist/mcp/src/prompts/debug-frame.js +36 -0
- package/dist/mcp/src/prompts/index.js +10 -0
- package/dist/mcp/src/resources/asset-list.js +28 -0
- package/dist/mcp/src/resources/checkpoint-list.js +29 -0
- package/dist/mcp/src/resources/entity-state.js +45 -0
- package/dist/mcp/src/resources/gpu-info.js +90 -0
- package/dist/mcp/src/resources/index.js +51 -0
- package/dist/mcp/src/resources/performance.js +33 -0
- package/dist/mcp/src/resources/scene-tree.js +37 -0
- package/dist/mcp/src/server.js +307 -0
- package/dist/mcp/src/tools/animation.js +102 -0
- package/dist/mcp/src/tools/asset.js +146 -0
- package/dist/mcp/src/tools/audio.js +80 -0
- package/dist/mcp/src/tools/build.js +128 -0
- package/dist/mcp/src/tools/camera.js +157 -0
- package/dist/mcp/src/tools/checkpoint.js +90 -0
- package/dist/mcp/src/tools/component.js +176 -0
- package/dist/mcp/src/tools/debug.js +273 -0
- package/dist/mcp/src/tools/entity.js +309 -0
- package/dist/mcp/src/tools/inspect.js +192 -0
- package/dist/mcp/src/tools/lighting.js +239 -0
- package/dist/mcp/src/tools/material.js +225 -0
- package/dist/mcp/src/tools/mesh.js +210 -0
- package/dist/mcp/src/tools/physics.js +251 -0
- package/dist/mcp/src/tools/scene.js +139 -0
- package/dist/mcp/src/tools/script.js +114 -0
- package/dist/mcp/src/types.js +9 -0
- package/dist/mcp/src/undo-redo.js +64 -0
- package/dist/modules/camera-controls/src/index.js +116 -0
- package/dist/modules/controller-ui/src/index.js +149 -0
- package/dist/modules/devtools/src/api.js +317 -0
- package/dist/modules/devtools/src/asset-utils.js +102 -0
- package/dist/modules/devtools/src/bind-debug-store.js +34 -0
- package/dist/modules/devtools/src/blitz/host.js +391 -0
- package/dist/modules/devtools/src/blitz/module.js +65 -0
- package/dist/modules/devtools/src/blitz/panels/console.js +217 -0
- package/dist/modules/devtools/src/blitz/panels/elements.js +79 -0
- package/dist/modules/devtools/src/blitz/panels/gpu.js +61 -0
- package/dist/modules/devtools/src/blitz/panels/performance.js +113 -0
- package/dist/modules/devtools/src/blitz/panels/snapshot.js +141 -0
- package/dist/modules/devtools/src/blitz/panels/types.js +55 -0
- package/dist/modules/devtools/src/blitz/theme.js +167 -0
- package/dist/modules/devtools/src/data-bridge.js +247 -0
- package/dist/modules/devtools/src/debug-overlay.js +181 -0
- package/dist/modules/devtools/src/debug-raycast.js +296 -0
- package/dist/modules/devtools/src/debug-store.js +28 -0
- package/dist/modules/devtools/src/debug-view-descriptors.js +24 -0
- package/dist/modules/devtools/src/doctor-panel.js +163 -0
- package/dist/modules/devtools/src/gizmo-module.js +95 -0
- package/dist/modules/devtools/src/graph-model.js +387 -0
- package/dist/modules/devtools/src/grid-renderer.js +145 -0
- package/dist/modules/devtools/src/height-ruler-renderer.js +189 -0
- package/dist/modules/devtools/src/index.js +46 -0
- package/dist/modules/devtools/src/init.js +204 -0
- package/dist/modules/devtools/src/label-overlay.js +129 -0
- package/dist/modules/devtools/src/material-stats-panel.js +171 -0
- package/dist/modules/devtools/src/profiling-bridge.js +288 -0
- package/dist/modules/devtools/src/profiling-hooks.js +63 -0
- package/dist/modules/devtools/src/renderer-adapter.js +61 -0
- package/dist/modules/devtools/src/scene-inspector.js +571 -0
- package/dist/modules/devtools/src/scene-store.js +212 -0
- package/dist/modules/devtools/src/scene-sync.js +92 -0
- package/dist/modules/devtools/src/shaders/transform-gizmo.wgsl +38 -0
- package/dist/modules/devtools/src/sim-stats-panel.js +250 -0
- package/dist/modules/devtools/src/sim-stats-provider.js +73 -0
- package/dist/modules/devtools/src/skeleton-renderer.js +326 -0
- package/dist/modules/devtools/src/transform-gizmo.js +677 -0
- package/dist/modules/devtools/src/types.js +5 -0
- package/dist/modules/devtools/src/worker-expose.js +59 -0
- package/dist/modules/devtools/src/worker-sync.js +115 -0
- package/dist/modules/editor/src/commands/component.js +151 -0
- package/dist/modules/editor/src/commands/entity.js +253 -0
- package/dist/modules/editor/src/commands/hierarchy.js +79 -0
- package/dist/modules/editor/src/commands/index.js +57 -0
- package/dist/modules/editor/src/commands/prefab.js +149 -0
- package/dist/modules/editor/src/commands/registry.js +246 -0
- package/dist/modules/editor/src/commands/scene-io.js +206 -0
- package/dist/modules/editor/src/commands/selection.js +71 -0
- package/dist/modules/editor/src/commands/transform.js +88 -0
- package/dist/modules/editor/src/ddscene.js +189 -0
- package/dist/modules/editor/src/document.js +96 -0
- package/dist/modules/editor/src/editor-context.js +115 -0
- package/dist/modules/editor/src/index.js +11 -0
- package/dist/modules/editor/src/mcp.js +91 -0
- package/dist/modules/editor/src/module.js +115 -0
- package/dist/modules/editor/src/selection.js +60 -0
- package/dist/modules/editor/src/shell.js +397 -0
- package/dist/modules/editor/src/viewport.js +186 -0
- package/dist/modules/html-ui/src/composite.js +116 -0
- package/dist/modules/html-ui/src/create-html-ui.js +137 -0
- package/dist/modules/html-ui/src/doc-backend.js +518 -0
- package/dist/modules/html-ui/src/host.js +819 -0
- package/dist/modules/html-ui/src/index.js +5 -0
- package/dist/modules/html-ui/src/jsx-dev-runtime.js +9 -0
- package/dist/modules/html-ui/src/jsx-runtime.js +121 -0
- package/dist/modules/html-ui/src/protocol.js +6 -0
- package/dist/modules/html-ui/src/ui-worker.js +10 -0
- package/dist/modules/mcp/src/index.js +62 -0
- package/dist/modules/movement-2d/src/grid-character.js +345 -0
- package/dist/modules/movement-2d/src/index.js +27 -0
- package/dist/modules/movement-3d/src/character-motor.js +166 -0
- package/dist/modules/movement-3d/src/index.js +27 -0
- package/dist/modules/native-osr/src/index.js +12 -0
- package/dist/modules/native-osr/src/input-router.js +335 -0
- package/dist/modules/native-osr/src/native-osr-manager.js +210 -0
- package/dist/modules/native-osr/src/native-texture-source.js +66 -0
- package/dist/modules/native-osr/src/types.js +8 -0
- package/dist/modules/native-osr/src/world-space-ui-pass.js +313 -0
- package/dist/modules/sailing/src/index.js +39 -0
- package/dist/modules/terrain/src/index.js +44 -0
- package/dist/modules/vitals/src/index.js +25 -0
- package/dist/modules/vitals/src/vitals.js +195 -0
- package/dist/modules/xr/src/camera-rig.js +119 -0
- package/dist/modules/xr/src/debug.js +45 -0
- package/dist/modules/xr/src/frame-loop.js +139 -0
- package/dist/modules/xr/src/index.js +10 -0
- package/dist/modules/xr/src/input.js +164 -0
- package/dist/modules/xr/src/layer.js +100 -0
- package/dist/modules/xr/src/session.js +133 -0
- package/dist/modules/xr/src/sim-module.js +21 -0
- package/dist/modules/xr/src/types.js +4 -0
- package/dist/modules/xr/src/webxr.d.ts +182 -0
- package/dist/modules/xr/src/xr-module.js +134 -0
- package/dist/shader-graph/src/chunks.js +340 -0
- package/dist/shader-graph/src/compiler.js +639 -0
- package/dist/shader-graph/src/compute-compiler.js +384 -0
- package/dist/shader-graph/src/compute-graph.js +55 -0
- package/dist/shader-graph/src/compute-profiles.js +39 -0
- package/dist/shader-graph/src/graph.js +38 -0
- package/dist/shader-graph/src/index.js +15 -0
- package/dist/shader-graph/src/profiles.js +305 -0
- package/dist/shader-graph/src/validator.js +42 -0
- package/dist/shader-graph/src/wgsl-binding-validator.js +110 -0
- package/dist/shader-graph/src/wgsl-struct-validator.js +222 -0
- package/dist/shader-graph/src/wgsl-struct.js +251 -0
- package/dist/test/src/index.js +1 -0
- package/dist/test/src/vision-test.js +228 -0
- package/libraries/asset-browser/src/software-thumbnails.ts +2 -2
- package/libraries/audio-kira/src/backend.ts +2 -1
- package/libraries/blitz-ui/native/src/shell.rs +35 -1
- package/libraries/blitz-ui/native-osr/Cargo.toml +8 -0
- package/libraries/blitz-ui/native-osr/src/lib.rs +213 -22
- package/libraries/blitz-ui/native-osr/src/mutate.rs +109 -14
- package/libraries/blitz-ui/src/native-osr-ffi.ts +71 -8
- package/libraries/blitz-ui/src/router.ts +3 -1
- package/libraries/blitz-ui/src/ui-blit-pass.ts +1 -1
- package/libraries/character/src/character-animator.ts +1 -1
- package/libraries/character/src/customization.ts +513 -0
- package/libraries/character/src/index.ts +6 -0
- package/libraries/character/src/inspectable.ts +111 -0
- package/libraries/character/src/loader.ts +38 -13
- package/libraries/character/src/locomotion.ts +13 -1
- package/libraries/character/src/preview.ts +94 -24
- package/libraries/devtools/README.md +16 -18
- package/libraries/devtools/src/backend.ts +505 -0
- package/libraries/devtools/src/cdp-bridge.ts +8 -8
- package/libraries/devtools/src/collectors.ts +41 -37
- package/libraries/devtools/src/index.ts +32 -14
- package/libraries/devtools/src/native-providers.ts +20 -12
- package/libraries/devtools/src/snapshot.ts +77 -0
- package/libraries/devtools/src/web/host.ts +11 -12
- package/libraries/devtools/src/web/mirror.ts +14 -405
- package/libraries/devtools/src/web/server.ts +4 -3
- package/libraries/entities/src/model-renderer.ts +96 -23
- package/libraries/gamepad/src/feed-input.ts +1 -1
- package/libraries/gamepad/src/hub.ts +2 -2
- package/libraries/gamepad/src/library.ts +2 -1
- package/libraries/gamepad/src/source-sab.ts +2 -2
- package/libraries/gamepad/src/sysfs-enrich.ts +2 -2
- package/libraries/gpu-kernels/src/kernel.ts +2 -2
- package/libraries/gpu-kernels/src/transpile.ts +1 -1
- package/libraries/html-ui-kit/src/nav/nav-controller.ts +1 -1
- package/libraries/html-ui-kit/src/nav/nav-input.ts +2 -2
- package/libraries/inspectable/src/index.ts +11 -0
- package/libraries/inspectable/src/inspectable.ts +277 -0
- package/libraries/marching-cubes/src/streaming-manager.ts +4 -4
- package/libraries/models/src/bind-unskinned-meshes.ts +131 -0
- package/libraries/models/src/index.ts +1 -0
- package/libraries/models/src/types.ts +22 -0
- package/libraries/networking/src/discovery.ts +8 -8
- package/libraries/persistence/src/autosave-manager.ts +1 -1
- package/libraries/persistence/src/binary-record-store.ts +1 -1
- package/libraries/persistence/src/file-save-store.ts +9 -9
- package/libraries/persistence/src/library.ts +1 -1
- package/libraries/persistence/src/opfs-save-store.ts +7 -7
- package/libraries/persistence/src/save-worker.ts +13 -14
- package/libraries/physics-rapier/src/ffi-lib.ts +2 -1
- package/libraries/physics-rapier/src/realm-worker-script.ts +1 -1
- package/libraries/postfx/src/post-process-stack.ts +1 -1
- package/libraries/sand/src/sand-step-pool.ts +5 -0
- package/libraries/water/src/water-renderer.ts +1 -1
- package/libraries/weatherfx/src/cloud-system.ts +3 -3
- package/mcp/README.md +20 -0
- package/mcp/src/client.ts +75 -22
- package/mcp/src/undo-redo.ts +2 -2
- package/modules/controller-ui/src/index.ts +1 -1
- package/modules/devtools/src/blitz/host.ts +430 -0
- package/modules/devtools/src/blitz/module.ts +101 -0
- package/modules/devtools/src/blitz/panels/console.ts +223 -0
- package/modules/devtools/src/blitz/panels/elements.ts +92 -0
- package/modules/devtools/src/blitz/panels/gpu.ts +70 -0
- package/modules/devtools/src/blitz/panels/performance.ts +120 -0
- package/modules/devtools/src/blitz/panels/snapshot.ts +152 -0
- package/modules/devtools/src/blitz/panels/types.ts +120 -0
- package/modules/devtools/src/blitz/theme.ts +168 -0
- package/modules/devtools/src/data-bridge.ts +1 -1
- package/modules/devtools/src/index.ts +6 -0
- package/modules/devtools/src/profiling-bridge.ts +17 -13
- package/modules/devtools/src/profiling-hooks.ts +22 -13
- package/modules/devtools/src/scene-inspector.ts +1 -1
- package/modules/devtools/src/worker-sync.ts +9 -1
- package/modules/editor/src/commands/registry.ts +2 -2
- package/modules/editor/src/module.ts +1 -1
- package/modules/html-ui/src/composite.ts +1 -1
- package/modules/html-ui/src/create-html-ui.ts +21 -9
- package/modules/html-ui/src/doc-backend.ts +189 -10
- package/modules/html-ui/src/host.ts +354 -40
- package/modules/html-ui/src/index.ts +1 -1
- package/modules/html-ui/src/jsx-runtime.ts +2 -2
- package/modules/html-ui/src/protocol.ts +53 -6
- package/modules/mcp/README.md +6 -0
- package/modules/mcp/src/index.ts +70 -3
- package/modules/movement-3d/src/character-motor.ts +83 -10
- package/package.json +838 -165
- package/shader-graph/README.md +25 -0
- package/test/README.md +16 -0
- package/libraries/blitz-ui/native-osr-spike.ts +0 -70
- package/libraries/devtools/native/Cargo.toml +0 -12
- package/libraries/devtools/native/src/lib.rs +0 -1646
- package/libraries/devtools/native/src/panels/console.rs +0 -338
- package/libraries/devtools/native/src/panels/dom_tree.rs +0 -47
- package/libraries/devtools/native/src/panels/generic.rs +0 -317
- package/libraries/devtools/native/src/panels/gpu.rs +0 -107
- package/libraries/devtools/native/src/panels/input.rs +0 -88
- package/libraries/devtools/native/src/panels/mod.rs +0 -154
- package/libraries/devtools/native/src/panels/perf_metrics.rs +0 -91
- package/libraries/devtools/native/src/panels/perf_recorder.rs +0 -158
- package/libraries/devtools/native/src/panels/scene.rs +0 -72
- package/libraries/devtools/native/src/state.rs +0 -394
- package/libraries/devtools/src/egui-ffi.ts +0 -804
- package/libraries/devtools/src/egui-renderer.ts +0 -407
- package/libraries/devtools/src/host.ts +0 -363
- package/libraries/devtools/src/mirror.ts +0 -794
- package/libraries/models/test-fixtures/fbx/LICENSE-notice.txt +0 -24
- package/libraries/models/test-fixtures/fbx/README.md +0 -46
- package/libraries/models/test-fixtures/fbx/m01_cube_phong.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m02_geo_ngons.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m03_normals.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m04_material_properties.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m05_external_diffuse.png +0 -0
- package/libraries/models/test-fixtures/fbx/m05_textures.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m06_uv_transform.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m07_multimaterial.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m08_transforms.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m09_skinning.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m10_morph.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m11_node_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m12_skeletal_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m13_morph_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m14_multiclip.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m15_camera_lights.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_axis_yup.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_axis_zup.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_units_254.fbx +0 -0
- package/libraries/physics-native/src/ffi-spike-worker.ts +0 -71
- package/libraries/physics-rapier/bench/ffi-bench.ts +0 -79
- package/libraries/physics-rapier/bench/wasm-baseline.ts +0 -90
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// GameRenderer — generic WebGPU render loop infrastructure
|
|
3
|
+
// Extracted from WebGPURenderer: device init, surface config, rAF loop,
|
|
4
|
+
// frame rate limiter, viewport management, telemetry, GPU UI pass, depth cache.
|
|
5
|
+
// Games register render passes via FrameGraph slots and provide game-specific
|
|
6
|
+
// logic through callback hooks.
|
|
7
|
+
// ============================================================================
|
|
8
|
+
import { UIInputRouter } from "../input/ui-router.js";
|
|
9
|
+
import { getHostCapabilities, getNativeHost } from "../platform/runtime.js";
|
|
10
|
+
import { TelemetryCollector } from "../telemetry/collector.js";
|
|
11
|
+
import { GPUProfiler } from "../telemetry/gpu-profiler.js";
|
|
12
|
+
import { GPUResourceTracker } from "../telemetry/gpu-resource-tracker.js";
|
|
13
|
+
import { createLogger } from "../util/logger.js";
|
|
14
|
+
import { CanvasResizeWatcher } from "./canvas-resize-watcher.js";
|
|
15
|
+
import { GPUDeviceManager } from "./device.js";
|
|
16
|
+
import { FrameGraph } from "./frame-graph.js";
|
|
17
|
+
import { InputManager } from "./input-manager.js";
|
|
18
|
+
import { RendererModuleHost } from "./renderer-module-host.js";
|
|
19
|
+
import { installShaderValidationGuard } from "./shader-validator.js";
|
|
20
|
+
const log = createLogger();
|
|
21
|
+
export class GameRenderer {
|
|
22
|
+
canvas;
|
|
23
|
+
device = null;
|
|
24
|
+
adapter = null;
|
|
25
|
+
context = null;
|
|
26
|
+
format = "bgra8unorm";
|
|
27
|
+
config;
|
|
28
|
+
depthFormat = "depth32float";
|
|
29
|
+
msaaSampleCount = 1;
|
|
30
|
+
mode = "3d";
|
|
31
|
+
// Infrastructure
|
|
32
|
+
deviceManager;
|
|
33
|
+
resizeWatcher = null;
|
|
34
|
+
inputManager;
|
|
35
|
+
frameGraph;
|
|
36
|
+
rendererModuleHost = null;
|
|
37
|
+
// Telemetry & profiling
|
|
38
|
+
gpuProfiler = null;
|
|
39
|
+
gpuResourceTracker = null;
|
|
40
|
+
telemetryCollector = null;
|
|
41
|
+
/** Input router for the active UI stack (html-ui via Blitz, or none). */
|
|
42
|
+
uiInputRouter = null;
|
|
43
|
+
/** Screen-space compositors (html-ui panels etc.) drawn in the UI pass. */
|
|
44
|
+
uiCompositors = new Set();
|
|
45
|
+
// Render loop state
|
|
46
|
+
running = false;
|
|
47
|
+
deviceLost = false;
|
|
48
|
+
/** True when init() borrowed the native host's device — the surface was
|
|
49
|
+
* already configured by the host, so the renderer must not reconfigure. */
|
|
50
|
+
hostConfiguredSurface = false;
|
|
51
|
+
lastTime = 0;
|
|
52
|
+
elapsedTime = 0;
|
|
53
|
+
fps = 0;
|
|
54
|
+
frameCount = 0;
|
|
55
|
+
fpsTimer = 0;
|
|
56
|
+
lastResourceStatsTime = 0;
|
|
57
|
+
static RESOURCE_STATS_INTERVAL = 1000;
|
|
58
|
+
// Frame rate limiter
|
|
59
|
+
targetFrameTime = 0;
|
|
60
|
+
limiterActive = false;
|
|
61
|
+
frameAccum = 0;
|
|
62
|
+
lastLimiterTick = 0;
|
|
63
|
+
rafInterval = 0;
|
|
64
|
+
rafSum = 0;
|
|
65
|
+
rafCount = 0;
|
|
66
|
+
lastRafTime = 0;
|
|
67
|
+
// Viewport management
|
|
68
|
+
viewportCount = 1;
|
|
69
|
+
viewports = [];
|
|
70
|
+
// Transient surface/depth handles (imported into the frame graph each frame)
|
|
71
|
+
colorHandle = null;
|
|
72
|
+
depthHandle = null;
|
|
73
|
+
graphCompiled = false;
|
|
74
|
+
// Reusable per-frame RenderContext (mutated per viewport — see renderViewport)
|
|
75
|
+
frameCtx = null;
|
|
76
|
+
// Depth texture cache (used when the graph does not own depth)
|
|
77
|
+
// LRU: Map insertion order = access order (delete + re-set on access).
|
|
78
|
+
depthTextures = new Map();
|
|
79
|
+
depthTextureCacheSize;
|
|
80
|
+
// Frame stats
|
|
81
|
+
frameDrawCalls = 0;
|
|
82
|
+
frameTriangles = 0;
|
|
83
|
+
// Callbacks
|
|
84
|
+
callbacks = {};
|
|
85
|
+
// Offscreen mode (set by game for postprocessing)
|
|
86
|
+
offscreenMode = null;
|
|
87
|
+
// XR render target provider (overrides canvas surface when set)
|
|
88
|
+
renderTargetProvider = null;
|
|
89
|
+
// rAF source override (for XR sessions)
|
|
90
|
+
rafSource = null;
|
|
91
|
+
cancelRaf = null;
|
|
92
|
+
currentRafId = 0;
|
|
93
|
+
// Dedup for the render chain: exactly one pending rAF may exist at a time.
|
|
94
|
+
// renderFrame() is also called outside the loop by renderOnce() (native
|
|
95
|
+
// screenshots, deterministic steps); letting it re-schedule leaked a
|
|
96
|
+
// permanent second (third, ...) render chain per call — each chain ran
|
|
97
|
+
// renderFrame() every pump tick, multiplying both real render work and the
|
|
98
|
+
// FPS counter (the "1100 fps" badge after MCP screenshot calls).
|
|
99
|
+
rafPending = false;
|
|
100
|
+
// Dpr
|
|
101
|
+
dpr = 1;
|
|
102
|
+
constructor(canvas, config = {}) {
|
|
103
|
+
this.canvas = canvas;
|
|
104
|
+
this.config = config;
|
|
105
|
+
this.depthFormat = config.depthFormat ?? "depth32float";
|
|
106
|
+
this.msaaSampleCount = config.msaaSampleCount ?? 1;
|
|
107
|
+
this.depthTextureCacheSize = config.depthTextureCacheSize ?? 3;
|
|
108
|
+
this.mode = config.mode ?? "3d";
|
|
109
|
+
this.deviceManager = new GPUDeviceManager();
|
|
110
|
+
this.inputManager = new InputManager(canvas, config.enablePointerLock ?? false);
|
|
111
|
+
this.frameGraph = new FrameGraph();
|
|
112
|
+
}
|
|
113
|
+
/** Build requiredLimits for the bindless binding model (clamped to adapter). */
|
|
114
|
+
buildRequiredLimits(adapter) {
|
|
115
|
+
const a = adapter.limits;
|
|
116
|
+
const clamp = (key, want) => {
|
|
117
|
+
const have = a[key];
|
|
118
|
+
if (have === undefined)
|
|
119
|
+
return null;
|
|
120
|
+
return [key, Math.min(want, Number(have))];
|
|
121
|
+
};
|
|
122
|
+
const entries = [];
|
|
123
|
+
[
|
|
124
|
+
clamp("maxTextureArrayLayers", 512),
|
|
125
|
+
clamp("maxStorageBuffersPerShaderStage", 8),
|
|
126
|
+
clamp("maxStorageBufferBindingSize", 64 * 1024 * 1024),
|
|
127
|
+
clamp("maxSampledTexturesPerShaderStage", 16),
|
|
128
|
+
].forEach((e) => {
|
|
129
|
+
if (e)
|
|
130
|
+
entries.push(e);
|
|
131
|
+
});
|
|
132
|
+
return Object.fromEntries(entries);
|
|
133
|
+
}
|
|
134
|
+
async init() {
|
|
135
|
+
try {
|
|
136
|
+
// Device resolution order: explicit config → native host's device →
|
|
137
|
+
// self-acquire. On native the host owns the only wgpu device and the
|
|
138
|
+
// surface context — borrowing it avoids a second requestDevice() and
|
|
139
|
+
// a reconfigure race on the same surface. In-process there is
|
|
140
|
+
// exactly one device.
|
|
141
|
+
const nativeHost = getNativeHost();
|
|
142
|
+
const borrowedDevice = this.config.device ?? nativeHost?.device;
|
|
143
|
+
const borrowedAdapter = this.config.adapter ?? nativeHost?.adapter;
|
|
144
|
+
// An adapter is only required when we must acquire the device ourselves;
|
|
145
|
+
// a borrowed device is already usable (adapter.info is null-tolerated
|
|
146
|
+
// below).
|
|
147
|
+
const adapter = borrowedAdapter ?? await this.requestAdapterWithFallback();
|
|
148
|
+
if (!adapter && !borrowedDevice) {
|
|
149
|
+
log.error("game-renderer", "No GPU adapter found — check GPU drivers and /dev/dri permissions");
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
this.device = borrowedDevice ?? await this.requestDeviceFromAdapter(adapter);
|
|
153
|
+
this.adapter = adapter;
|
|
154
|
+
// The host configured the surface with its own usage set at creation —
|
|
155
|
+
// reconfiguring here would tear down the swapchain and race the host's
|
|
156
|
+
// readback hook. Skip when borrowing the host's device.
|
|
157
|
+
this.hostConfiguredSurface =
|
|
158
|
+
borrowedDevice !== undefined && borrowedDevice === nativeHost?.device;
|
|
159
|
+
// Install the shader validation guard so all createShaderModule calls
|
|
160
|
+
// route through getCompilationInfo() validation.
|
|
161
|
+
installShaderValidationGuard(this.device);
|
|
162
|
+
// Wrap device with GPU resource tracker for VRAM visibility
|
|
163
|
+
this.gpuResourceTracker = new GPUResourceTracker();
|
|
164
|
+
this.gpuResourceTracker.wrapDevice(this.device);
|
|
165
|
+
// GPU profiler
|
|
166
|
+
const adapterInfo = adapter?.info ?? null;
|
|
167
|
+
this.context = this.canvas.getContext("webgpu");
|
|
168
|
+
this.format = navigator.gpu.getPreferredCanvasFormat();
|
|
169
|
+
this.gpuProfiler = new GPUProfiler();
|
|
170
|
+
this.gpuProfiler.init(this.device, adapterInfo, this.format, 32);
|
|
171
|
+
log.debug("game-renderer", `GPU timer pool supported: ${this.gpuProfiler.isGpuTimerSupported()} features: ${Array.from(this.device.features).join(", ")}`);
|
|
172
|
+
// Device lost handler
|
|
173
|
+
this.device.lost.then((info) => {
|
|
174
|
+
this.handleDeviceLost(info);
|
|
175
|
+
});
|
|
176
|
+
// Configure surface — skipped when the surface was already configured
|
|
177
|
+
// by the device owner (native host) or the caller opted out. A borrowed
|
|
178
|
+
// host surface is never reconfigured: the host's usage set and
|
|
179
|
+
// pre-present readback hook are bound to its configure call.
|
|
180
|
+
if (!this.hostConfiguredSurface && (this.config.configureSurface ?? true)) {
|
|
181
|
+
this.context.configure({
|
|
182
|
+
device: this.device,
|
|
183
|
+
format: this.format,
|
|
184
|
+
alphaMode: "premultiplied",
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// UI input router — the active UI stack (html-ui via Blitz) replaces
|
|
188
|
+
// this with its own router when its module registers.
|
|
189
|
+
this.uiInputRouter = new UIInputRouter();
|
|
190
|
+
this.inputManager.setUIInputRouter(this.uiInputRouter);
|
|
191
|
+
// Telemetry
|
|
192
|
+
this.telemetryCollector = new TelemetryCollector(this.config.enableTelemetry ?? true);
|
|
193
|
+
// Canvas resize watcher
|
|
194
|
+
this.resizeWatcher = new CanvasResizeWatcher(this.canvas, this);
|
|
195
|
+
// Input listeners
|
|
196
|
+
this.inputManager.setupListeners();
|
|
197
|
+
// Renderer plugin host — owns renderer-thread plugins (camera
|
|
198
|
+
// controllers, gizmos, XR frame loops, OSR). Created after InputManager
|
|
199
|
+
// so the host's input bus coexists with the FPS/pointer-lock layer.
|
|
200
|
+
this.rendererModuleHost = new RendererModuleHost(this.canvas, {
|
|
201
|
+
getSurface: () => this.canvas,
|
|
202
|
+
getDevice: () => this.device,
|
|
203
|
+
getFormat: () => this.format,
|
|
204
|
+
getGraph: () => this.frameGraph,
|
|
205
|
+
getSlotRegistry: () => this.frameGraph.getSlotRegistry(),
|
|
206
|
+
setOffscreenMode: (mode) => this.setOffscreenMode(mode),
|
|
207
|
+
setRenderTargetProvider: (provider) => this.setRenderTargetProvider(provider),
|
|
208
|
+
setRAFSource: (src, cancel) => {
|
|
209
|
+
if (src && cancel)
|
|
210
|
+
this.setRAFSource(src, cancel);
|
|
211
|
+
else
|
|
212
|
+
this.clearRAFSource();
|
|
213
|
+
},
|
|
214
|
+
setViewportCount: (count) => this.setViewportCount(count),
|
|
215
|
+
getUIInputRouter: () => this.uiInputRouter,
|
|
216
|
+
registerUiCompositor: (c) => {
|
|
217
|
+
this.uiCompositors.add(c);
|
|
218
|
+
return () => this.uiCompositors.delete(c);
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
// Initial viewport layout
|
|
222
|
+
this.updateViewports(1);
|
|
223
|
+
log.info("game-renderer", "initialized");
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
log.error("game-renderer", `Init failed: ${err}`);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// --- Device acquisition + loss recovery ---
|
|
232
|
+
/** Adapter fallback chain: high-performance → low-power → any. */
|
|
233
|
+
async requestAdapterWithFallback() {
|
|
234
|
+
let adapter = await navigator.gpu.requestAdapter({
|
|
235
|
+
powerPreference: "high-performance",
|
|
236
|
+
});
|
|
237
|
+
if (!adapter) {
|
|
238
|
+
log.warn("game-renderer", "No high-performance GPU adapter, trying low-power...");
|
|
239
|
+
adapter = await navigator.gpu.requestAdapter({
|
|
240
|
+
powerPreference: "low-power",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (!adapter) {
|
|
244
|
+
log.warn("game-renderer", "No low-power adapter, trying any...");
|
|
245
|
+
adapter = await navigator.gpu.requestAdapter({});
|
|
246
|
+
}
|
|
247
|
+
return adapter;
|
|
248
|
+
}
|
|
249
|
+
async requestDeviceFromAdapter(adapter) {
|
|
250
|
+
// Request timestamp-query features for GPU-side per-pass timing
|
|
251
|
+
const requiredFeatures = [];
|
|
252
|
+
if (adapter.features.has("timestamp-query")) {
|
|
253
|
+
requiredFeatures.push("timestamp-query");
|
|
254
|
+
}
|
|
255
|
+
return adapter.requestDevice({
|
|
256
|
+
requiredFeatures,
|
|
257
|
+
requiredLimits: this.buildRequiredLimits(adapter),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
recoveringDevice = false;
|
|
261
|
+
handleDeviceLost(info) {
|
|
262
|
+
if (this.deviceLost || this.recoveringDevice)
|
|
263
|
+
return; // already handled
|
|
264
|
+
this.deviceLost = true;
|
|
265
|
+
log.error("game-renderer", `WebGPU device lost: ${info?.reason ?? "unknown"} — ${info?.message ?? ""}`);
|
|
266
|
+
try {
|
|
267
|
+
this.callbacks.onDeviceLost?.(info);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
log.error("game-renderer", `onDeviceLost callback threw: ${err}`);
|
|
271
|
+
}
|
|
272
|
+
if ((this.config.deviceLossRecovery ?? "auto") === "reload" ||
|
|
273
|
+
info?.reason === "destroyed" ||
|
|
274
|
+
this.hostConfiguredSurface) {
|
|
275
|
+
// "destroyed" = device.destroy() was called deliberately — a new device
|
|
276
|
+
// can't help, the app is tearing down or opted out. A borrowed host
|
|
277
|
+
// device can't be recovered in place either: the host's surface
|
|
278
|
+
// configure and readback hook are bound to the dead device, and every
|
|
279
|
+
// other holder of it (bridge, devtools hosts) would still point at
|
|
280
|
+
// the old handle. Restart instead.
|
|
281
|
+
this.reloadForDeviceLoss();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this.recoverDevice();
|
|
285
|
+
}
|
|
286
|
+
reloadForDeviceLoss() {
|
|
287
|
+
setTimeout(() => {
|
|
288
|
+
// Host already tearing down (host.destroy() destroys the device,
|
|
289
|
+
// which resolves .lost with reason "destroyed") — restarting now
|
|
290
|
+
// would respawn a session on top of a dead window.
|
|
291
|
+
if (globalThis.__nativeHost?.destroyed)
|
|
292
|
+
return;
|
|
293
|
+
// Native runtime: window.location.reload() is a no-op. Route through
|
|
294
|
+
// the host's first-class restart hook (detached self-respawn — the
|
|
295
|
+
// real reload equivalent), then fall back to a dialog + clean quit if
|
|
296
|
+
// the restart budget is exhausted.
|
|
297
|
+
const req = globalThis.downdraft?.requestRestart;
|
|
298
|
+
if (typeof req === "function" && req("GPU device lost"))
|
|
299
|
+
return;
|
|
300
|
+
const nativeWin = globalThis.__nativeWindow;
|
|
301
|
+
if (nativeWin?.showMessageBox) {
|
|
302
|
+
try {
|
|
303
|
+
nativeWin.showMessageBox("GPU Device Lost", "The GPU device was lost and could not be recovered. The application will now close.");
|
|
304
|
+
}
|
|
305
|
+
catch { /* window may be gone */ }
|
|
306
|
+
try {
|
|
307
|
+
nativeWin.requestQuit?.();
|
|
308
|
+
}
|
|
309
|
+
catch { /* best-effort */ }
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
log.warn("game-renderer", "Attempting page reload for GPU recovery...");
|
|
313
|
+
window.location.reload();
|
|
314
|
+
}, 2000);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Attempt in-place device recovery: re-acquire adapter+device, reconfigure
|
|
318
|
+
* the surface, re-prepare UI/profiler and every registered frame-graph
|
|
319
|
+
* pass, then recompile the graph (transient textures get fresh allocations
|
|
320
|
+
* on the new device). Falls back to a page reload on any failure.
|
|
321
|
+
*/
|
|
322
|
+
async recoverDevice() {
|
|
323
|
+
this.recoveringDevice = true;
|
|
324
|
+
try {
|
|
325
|
+
const adapter = await this.requestAdapterWithFallback();
|
|
326
|
+
if (!adapter)
|
|
327
|
+
throw new Error("no GPU adapter after device loss");
|
|
328
|
+
const device = await this.requestDeviceFromAdapter(adapter);
|
|
329
|
+
device.lost.then((info) => this.handleDeviceLost(info));
|
|
330
|
+
this.device = device;
|
|
331
|
+
this.adapter = adapter;
|
|
332
|
+
installShaderValidationGuard(device);
|
|
333
|
+
this.gpuResourceTracker = new GPUResourceTracker();
|
|
334
|
+
this.gpuResourceTracker.wrapDevice(device);
|
|
335
|
+
this.context.configure({
|
|
336
|
+
device,
|
|
337
|
+
format: this.format,
|
|
338
|
+
alphaMode: "premultiplied",
|
|
339
|
+
});
|
|
340
|
+
const adapterInfo = adapter.info ?? null;
|
|
341
|
+
this.gpuProfiler?.init(device, adapterInfo, this.format, 32);
|
|
342
|
+
// Re-prepare every registered pass — pipelines/bind groups built on the
|
|
343
|
+
// dead device are invalid. Passes that can't recreate resources should
|
|
344
|
+
// throw here so we fall back to reload.
|
|
345
|
+
for (const pass of this.frameGraph.getPasses()) {
|
|
346
|
+
pass.prepare(device);
|
|
347
|
+
}
|
|
348
|
+
// Force graph recompile so transient textures are re-allocated on the
|
|
349
|
+
// new device — pooled physical textures and cached views all belong to
|
|
350
|
+
// the dead device and must not be reused.
|
|
351
|
+
this.frameGraph.invalidatePhysicalResources();
|
|
352
|
+
this.graphCompiled = false;
|
|
353
|
+
await this.onDeviceRecovered(device);
|
|
354
|
+
await this.callbacks.onDeviceRecovered?.(device);
|
|
355
|
+
this.deviceLost = false;
|
|
356
|
+
log.warn("game-renderer", "GPU device recovered — rendering resumed without reload");
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
log.error("game-renderer", `Device recovery failed: ${err}`);
|
|
360
|
+
this.reloadForDeviceLoss();
|
|
361
|
+
}
|
|
362
|
+
finally {
|
|
363
|
+
this.recoveringDevice = false;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Subclass hook — called during device-loss recovery after the engine has
|
|
368
|
+
* re-prepared its own resources and all registered passes. Recreate
|
|
369
|
+
* subclass-owned GPU resources here. Throwing aborts recovery → reload.
|
|
370
|
+
*/
|
|
371
|
+
async onDeviceRecovered(_device) { }
|
|
372
|
+
// --- CanvasResizeHandler ---
|
|
373
|
+
resolutionScale = 1;
|
|
374
|
+
lastCssW = 0;
|
|
375
|
+
lastCssH = 0;
|
|
376
|
+
lastRawDpr = 1;
|
|
377
|
+
/**
|
|
378
|
+
* Render-resolution scale [0.25..1]. Multiplies the effective DPR so the
|
|
379
|
+
* swap chain renders fewer pixels (performance) and the compositor
|
|
380
|
+
* upscales. Re-applies the last known canvas size immediately.
|
|
381
|
+
*/
|
|
382
|
+
setResolutionScale(scale) {
|
|
383
|
+
this.resolutionScale = Math.max(0.25, Math.min(1, scale));
|
|
384
|
+
if (!getHostCapabilities().hasDom) {
|
|
385
|
+
// On the native host the swapchain extent must equal the window's
|
|
386
|
+
// real size — wgpu compares the configured size against the surface's
|
|
387
|
+
// currentExtent at acquire time and returns Outdated forever when
|
|
388
|
+
// they differ. Shrinking canvas.width/height would also corrupt
|
|
389
|
+
// clientWidth (UI layout + input coords). Resolution scaling needs
|
|
390
|
+
// an offscreen-render + upscale path to work here; for now it is a
|
|
391
|
+
// no-op rather than a brick.
|
|
392
|
+
if (this.resolutionScale < 1) {
|
|
393
|
+
log.warn("game-renderer", "resolutionScale is not supported on the native surface (swapchain must match the window size) — ignoring");
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (this.lastCssW > 0)
|
|
397
|
+
this.onResize(this.lastCssW, this.lastCssH, this.lastRawDpr);
|
|
398
|
+
}
|
|
399
|
+
getResolutionScale() {
|
|
400
|
+
return this.resolutionScale;
|
|
401
|
+
}
|
|
402
|
+
onResize(cssWidth, cssHeight, dpr) {
|
|
403
|
+
this.lastCssW = cssWidth;
|
|
404
|
+
this.lastCssH = cssHeight;
|
|
405
|
+
this.lastRawDpr = dpr;
|
|
406
|
+
// Render at the display's real DPR — resolutionScale is the opt-in
|
|
407
|
+
// performance knob when fill rate is a concern. On native the
|
|
408
|
+
// swapchain is window-sized (see setResolutionScale) so the scale is
|
|
409
|
+
// never applied to the backing store.
|
|
410
|
+
const cappedDpr = dpr * (getHostCapabilities().hasDom ? this.resolutionScale : 1);
|
|
411
|
+
this.dpr = cappedDpr;
|
|
412
|
+
const w = Math.round(cssWidth * cappedDpr);
|
|
413
|
+
const h = Math.round(cssHeight * cappedDpr);
|
|
414
|
+
if (this.canvas.width !== w)
|
|
415
|
+
this.canvas.width = w;
|
|
416
|
+
if (this.canvas.height !== h)
|
|
417
|
+
this.canvas.height = h;
|
|
418
|
+
this.updateViewports(this.viewportCount);
|
|
419
|
+
this.callbacks.onResize?.(cssWidth, cssHeight, dpr);
|
|
420
|
+
this.rendererModuleHost?.dispatchResize(cssWidth, cssHeight, dpr);
|
|
421
|
+
}
|
|
422
|
+
// --- Viewport management ---
|
|
423
|
+
setViewportCount(count) {
|
|
424
|
+
this.updateViewports(count);
|
|
425
|
+
}
|
|
426
|
+
getViewportCount() {
|
|
427
|
+
return this.viewportCount;
|
|
428
|
+
}
|
|
429
|
+
getViewport(idx) {
|
|
430
|
+
return this.viewports[idx] ?? null;
|
|
431
|
+
}
|
|
432
|
+
getViewports() {
|
|
433
|
+
return this.viewports;
|
|
434
|
+
}
|
|
435
|
+
updateViewports(count) {
|
|
436
|
+
this.viewportCount = count;
|
|
437
|
+
this.viewports = [];
|
|
438
|
+
const w = this.canvas.width;
|
|
439
|
+
const h = this.canvas.height;
|
|
440
|
+
if (count === 1) {
|
|
441
|
+
this.viewports.push({ x: 0, y: 0, w, h });
|
|
442
|
+
}
|
|
443
|
+
else if (count === 2) {
|
|
444
|
+
this.viewports.push({ x: 0, y: 0, w: w / 2, h });
|
|
445
|
+
this.viewports.push({ x: w / 2, y: 0, w: w / 2, h });
|
|
446
|
+
}
|
|
447
|
+
else if (count === 3) {
|
|
448
|
+
this.viewports.push({ x: 0, y: 0, w, h: h / 2 });
|
|
449
|
+
this.viewports.push({ x: 0, y: h / 2, w: w / 2, h: h / 2 });
|
|
450
|
+
this.viewports.push({ x: w / 2, y: h / 2, w: w / 2, h: h / 2 });
|
|
451
|
+
}
|
|
452
|
+
else if (count >= 4) {
|
|
453
|
+
this.viewports.push({ x: 0, y: 0, w: w / 2, h: h / 2 });
|
|
454
|
+
this.viewports.push({ x: w / 2, y: 0, w: w / 2, h: h / 2 });
|
|
455
|
+
this.viewports.push({ x: 0, y: h / 2, w: w / 2, h: h / 2 });
|
|
456
|
+
this.viewports.push({ x: w / 2, y: h / 2, w: w / 2, h: h / 2 });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// --- Frame rate limiter ---
|
|
460
|
+
setFrameRateLimit(refreshRate) {
|
|
461
|
+
if (refreshRate > 0) {
|
|
462
|
+
this.targetFrameTime = 1000 / refreshRate;
|
|
463
|
+
this.updateLimiterState();
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
this.targetFrameTime = 0;
|
|
467
|
+
this.limiterActive = false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
updateLimiterState() {
|
|
471
|
+
if (this.targetFrameTime <= 0 || this.rafInterval <= 0) {
|
|
472
|
+
this.limiterActive = this.targetFrameTime > 0;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
this.limiterActive = this.rafInterval < this.targetFrameTime * 0.85;
|
|
476
|
+
}
|
|
477
|
+
// --- Render loop ---
|
|
478
|
+
start() {
|
|
479
|
+
this.running = true;
|
|
480
|
+
this.lastTime = performance.now();
|
|
481
|
+
// Go through scheduleRender() rather than invoking render() inline — a
|
|
482
|
+
// pending rAF (loop already primed) would otherwise survive alongside the
|
|
483
|
+
// new registration and double the chain.
|
|
484
|
+
this.scheduleRender();
|
|
485
|
+
}
|
|
486
|
+
stop() {
|
|
487
|
+
this.running = false;
|
|
488
|
+
// cancelRaf only exists when a rAF source override (XR) is installed —
|
|
489
|
+
// fall back to the global so the pending callback is actually removed.
|
|
490
|
+
const cancel = this.cancelRaf ?? (typeof cancelAnimationFrame === "function" ? cancelAnimationFrame : null);
|
|
491
|
+
if (cancel && this.currentRafId) {
|
|
492
|
+
cancel(this.currentRafId);
|
|
493
|
+
this.currentRafId = 0;
|
|
494
|
+
}
|
|
495
|
+
this.rafPending = false;
|
|
496
|
+
}
|
|
497
|
+
/** True while the render loop is started (frames are being scheduled). */
|
|
498
|
+
isRunning() {
|
|
499
|
+
return this.running;
|
|
500
|
+
}
|
|
501
|
+
/** Render a single frame on demand — deterministic/test mode and native
|
|
502
|
+
* screenshot capture call this while the loop is stopped. Bypasses the
|
|
503
|
+
* frame limiter; safe to call with the loop running or stopped. */
|
|
504
|
+
renderOnce() {
|
|
505
|
+
if (!this.device || !this.context || this.deviceLost)
|
|
506
|
+
return;
|
|
507
|
+
const savedLimiter = this.limiterActive;
|
|
508
|
+
this.limiterActive = false;
|
|
509
|
+
try {
|
|
510
|
+
this.prepareForcedFrame();
|
|
511
|
+
this.renderFrame();
|
|
512
|
+
}
|
|
513
|
+
finally {
|
|
514
|
+
this.limiterActive = savedLimiter;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/** Subclass hook: clear internal dirty-tracking so renderOnce() actually
|
|
518
|
+
* draws. Renderers that skip unchanged frames (e.g. MiningRenderer's
|
|
519
|
+
* forceDirty check) override this to force the next frame out. */
|
|
520
|
+
prepareForcedFrame() { }
|
|
521
|
+
/** Schedule the next render() invocation. Centralized here so renderFrame()
|
|
522
|
+
* stays scheduling-free — renderOnce() and test paths call renderFrame()
|
|
523
|
+
* directly and must never grow a parallel loop. */
|
|
524
|
+
scheduleRender() {
|
|
525
|
+
if (this.rafPending)
|
|
526
|
+
return;
|
|
527
|
+
this.rafPending = true;
|
|
528
|
+
this.currentRafId = this.rafSource ? this.rafSource(this.render) : requestAnimationFrame(this.render);
|
|
529
|
+
}
|
|
530
|
+
render = () => {
|
|
531
|
+
this.rafPending = false;
|
|
532
|
+
this.currentRafId = 0;
|
|
533
|
+
if (!this.running)
|
|
534
|
+
return;
|
|
535
|
+
if (!this.device || !this.context) {
|
|
536
|
+
// init() hasn't finished — keep the chain alive until it has.
|
|
537
|
+
this.scheduleRender();
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
// Measure rAF interval (rolling average over 60 samples)
|
|
541
|
+
const rafNow = performance.now();
|
|
542
|
+
if (this.lastRafTime > 0) {
|
|
543
|
+
this.rafSum += rafNow - this.lastRafTime;
|
|
544
|
+
this.rafCount++;
|
|
545
|
+
if (this.rafCount >= 60) {
|
|
546
|
+
this.rafInterval = this.rafSum / this.rafCount;
|
|
547
|
+
this.rafSum = 0;
|
|
548
|
+
this.rafCount = 0;
|
|
549
|
+
this.updateLimiterState();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
this.lastRafTime = rafNow;
|
|
553
|
+
if (this.deviceLost) {
|
|
554
|
+
// Keep the loop alive while the device is lost — handleDeviceLost()
|
|
555
|
+
// re-creates the device and clears this flag; returning without
|
|
556
|
+
// re-scheduling would permanently stop rendering after a transient
|
|
557
|
+
// GPU crash.
|
|
558
|
+
this.scheduleRender();
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
try {
|
|
562
|
+
this.renderFrame();
|
|
563
|
+
}
|
|
564
|
+
catch (err) {
|
|
565
|
+
log.error("game-renderer", `Render loop error: ${err.message}\n${err.stack}`);
|
|
566
|
+
if (this.device?.lost) {
|
|
567
|
+
this.device.lost.then((info) => {
|
|
568
|
+
this.handleDeviceLost(info);
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
this.scheduleRender();
|
|
573
|
+
};
|
|
574
|
+
renderFrame() {
|
|
575
|
+
const now = performance.now();
|
|
576
|
+
// Frame rate limiter: phase accumulator (skip during XR — XR drives its own cadence)
|
|
577
|
+
// Accumulate real elapsed time in frame units — NOT rafInterval ratios.
|
|
578
|
+
// The ratio variant starved on hosts with a faster-than-vsync rAF pump
|
|
579
|
+
// (native SDL loop ticks at ~µs cadence): updateLimiterState() resets
|
|
580
|
+
// frameAccum every 60 rAF samples, and 60 × rafInterval/targetFrameTime
|
|
581
|
+
// never reached 1, so present() was never called.
|
|
582
|
+
if (!this.renderTargetProvider && this.limiterActive && this.targetFrameTime > 0) {
|
|
583
|
+
if (this.lastLimiterTick === 0)
|
|
584
|
+
this.lastLimiterTick = now;
|
|
585
|
+
this.frameAccum += (now - this.lastLimiterTick) / this.targetFrameTime;
|
|
586
|
+
this.lastLimiterTick = now;
|
|
587
|
+
if (this.frameAccum < 1) {
|
|
588
|
+
// render() owns re-scheduling — just skip the frame's work.
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
// Cap carry at one frame — a stale lastLimiterTick (limiter toggled
|
|
592
|
+
// off→on) would otherwise burst-render to drain a huge accumulator.
|
|
593
|
+
this.frameAccum = Math.min(this.frameAccum - 1, 1);
|
|
594
|
+
}
|
|
595
|
+
const dt = Math.min(0.1, (now - this.lastTime) / 1000);
|
|
596
|
+
this.lastTime = now;
|
|
597
|
+
this.elapsedTime += dt;
|
|
598
|
+
// FPS counter
|
|
599
|
+
this.frameCount++;
|
|
600
|
+
this.fpsTimer += dt;
|
|
601
|
+
if (this.fpsTimer >= 1) {
|
|
602
|
+
this.fps = this.frameCount;
|
|
603
|
+
this.frameCount = 0;
|
|
604
|
+
this.fpsTimer = 0;
|
|
605
|
+
}
|
|
606
|
+
// Before frame callback (game-specific: camera updates, input processing)
|
|
607
|
+
this.callbacks.beforeFrame?.(dt, this.elapsedTime);
|
|
608
|
+
this.rendererModuleHost?.dispatchFrame("beforeFrame", dt, this.elapsedTime);
|
|
609
|
+
// Before viewports callback (game-specific: particle ticks, pre-viewport setup)
|
|
610
|
+
this.callbacks.beforeViewports?.(dt, this.elapsedTime);
|
|
611
|
+
this.rendererModuleHost?.dispatchFrame("beforeViewports", dt, this.elapsedTime);
|
|
612
|
+
// If an uncaptured GPU error has fired, skip all GPU work this frame to
|
|
613
|
+
// avoid the per-frame cascade of native "is invalid due to a previous
|
|
614
|
+
// error" validation warnings. Game logic callbacks still run so the
|
|
615
|
+
// simulation stays responsive. The flag is cleared by recovery code that
|
|
616
|
+
// recreates the invalid resources.
|
|
617
|
+
const gpuError = this.gpuProfiler?.hasUncapturedError() ?? false;
|
|
618
|
+
// XR render target provider beginFrame hook
|
|
619
|
+
if (this.renderTargetProvider && !gpuError) {
|
|
620
|
+
this.renderTargetProvider.beginFrame();
|
|
621
|
+
}
|
|
622
|
+
// Render each viewport
|
|
623
|
+
const offscreen = this.offscreenMode;
|
|
624
|
+
const useOffscreen = offscreen && offscreen.type !== "none";
|
|
625
|
+
if (offscreen && useOffscreen && offscreen.ensureTargets && !gpuError) {
|
|
626
|
+
offscreen.ensureTargets(this.canvas.width, this.canvas.height);
|
|
627
|
+
}
|
|
628
|
+
// Collect command buffers from all phases and submit once at the end
|
|
629
|
+
// (reduces CPU→GPU sync points from 3+ per frame to 1).
|
|
630
|
+
const frameCommandBuffers = [];
|
|
631
|
+
if (!gpuError) {
|
|
632
|
+
// 2D mode with viewportCount=0: clear the canvas if a clearColor is
|
|
633
|
+
// configured, then let the afterFrame callback do custom rendering.
|
|
634
|
+
if (this.viewportCount === 0 && this.device && this.context && this.config.clearColor !== null) {
|
|
635
|
+
const clearTex = this.getSurfaceTexture();
|
|
636
|
+
if (clearTex) {
|
|
637
|
+
const clearEncoder = this.device.createCommandEncoder();
|
|
638
|
+
const clearPass = clearEncoder.beginRenderPass({
|
|
639
|
+
colorAttachments: [{
|
|
640
|
+
view: clearTex.createView(),
|
|
641
|
+
clearValue: this.config.clearColor ?? { r: 0, g: 0, b: 0, a: 1 },
|
|
642
|
+
loadOp: "clear",
|
|
643
|
+
storeOp: "store",
|
|
644
|
+
}],
|
|
645
|
+
});
|
|
646
|
+
clearPass.end();
|
|
647
|
+
frameCommandBuffers.push(clearEncoder.finish());
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
for (let v = 0; v < this.viewportCount; v++) {
|
|
651
|
+
const cb = this.renderViewport(v, dt, offscreen);
|
|
652
|
+
if (cb)
|
|
653
|
+
frameCommandBuffers.push(cb);
|
|
654
|
+
}
|
|
655
|
+
// Apply postprocessing
|
|
656
|
+
if (offscreen && useOffscreen && offscreen.applyPostprocess) {
|
|
657
|
+
const canvasView = this.getSurfaceTexture()?.createView();
|
|
658
|
+
if (canvasView) {
|
|
659
|
+
const postEncoder = this.device.createCommandEncoder();
|
|
660
|
+
offscreen.applyPostprocess(postEncoder, canvasView, this.canvas.width, this.canvas.height);
|
|
661
|
+
frameCommandBuffers.push(postEncoder.finish());
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
// After viewports callback
|
|
666
|
+
this.callbacks.afterViewports?.(dt, this.elapsedTime);
|
|
667
|
+
this.rendererModuleHost?.dispatchFrame("afterViewports", dt, this.elapsedTime);
|
|
668
|
+
// Worker-produced passes (postPass → "preSubmit" slot on the native
|
|
669
|
+
// host's GpuPassMailbox) join this frame's submit batch — refs that
|
|
670
|
+
// arrived since the last drain execute after the owner's buffers.
|
|
671
|
+
// Stale-tolerant by design: a worker that misses the drain lands next
|
|
672
|
+
// frame instead of rendezvousing the render thread.
|
|
673
|
+
const passMailbox = getNativeHost()?.passMailbox;
|
|
674
|
+
if (passMailbox) {
|
|
675
|
+
frameCommandBuffers.push(...passMailbox.drain("preSubmit"));
|
|
676
|
+
}
|
|
677
|
+
// Submit all command buffers for this frame in a single queue.submit() call
|
|
678
|
+
if (frameCommandBuffers.length > 0) {
|
|
679
|
+
this.device.queue.submit(frameCommandBuffers);
|
|
680
|
+
}
|
|
681
|
+
// Read GPU timer results asynchronously (1-frame latency).
|
|
682
|
+
// Must be called AFTER queue.submit() — readGpuTimers() calls mapAsync on
|
|
683
|
+
// the read buffer, and a mapped/mapping-pending buffer cannot be used in a
|
|
684
|
+
// submitted command buffer (the copy-to-readBuffer was encoded above).
|
|
685
|
+
if (this.gpuProfiler) {
|
|
686
|
+
this.gpuProfiler.readGpuTimers().then(() => {
|
|
687
|
+
// Results available for next frame
|
|
688
|
+
}).catch(() => { });
|
|
689
|
+
}
|
|
690
|
+
// Record telemetry
|
|
691
|
+
if (this.telemetryCollector) {
|
|
692
|
+
this.telemetryCollector.recordFrame(dt * 1000);
|
|
693
|
+
this.telemetryCollector.recordDrawStats(this.frameDrawCalls, this.frameTriangles);
|
|
694
|
+
this.telemetryCollector.recordGraphSample(dt * 1000);
|
|
695
|
+
if (this.gpuProfiler) {
|
|
696
|
+
for (const timing of this.gpuProfiler.getPassTimings()) {
|
|
697
|
+
this.telemetryCollector.recordPassTiming(timing);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (this.gpuResourceTracker && now - this.lastResourceStatsTime > GameRenderer.RESOURCE_STATS_INTERVAL) {
|
|
701
|
+
this.lastResourceStatsTime = now;
|
|
702
|
+
const resStats = this.gpuResourceTracker.getStats();
|
|
703
|
+
this.telemetryCollector.recordResourceStats({
|
|
704
|
+
textureCount: resStats.textureCount,
|
|
705
|
+
bufferCount: resStats.bufferCount,
|
|
706
|
+
totalBytes: resStats.totalBytes,
|
|
707
|
+
textureBytes: resStats.textureBytes,
|
|
708
|
+
bufferBytes: resStats.bufferBytes,
|
|
709
|
+
resources: resStats.resources.map((r) => ({
|
|
710
|
+
id: r.id,
|
|
711
|
+
type: r.type,
|
|
712
|
+
label: r.label,
|
|
713
|
+
size: r.size,
|
|
714
|
+
callsite: r.callsite,
|
|
715
|
+
width: r.width,
|
|
716
|
+
height: r.height,
|
|
717
|
+
format: r.format,
|
|
718
|
+
})),
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
this.frameDrawCalls = 0;
|
|
722
|
+
this.frameTriangles = 0;
|
|
723
|
+
}
|
|
724
|
+
// XR render target provider endFrame hook (skip on GPU error)
|
|
725
|
+
if (!gpuError && this.renderTargetProvider && this.device) {
|
|
726
|
+
this.renderTargetProvider.endFrame(this.device.createCommandEncoder());
|
|
727
|
+
}
|
|
728
|
+
// After frame callback
|
|
729
|
+
this.callbacks.afterFrame?.(dt, this.elapsedTime);
|
|
730
|
+
this.rendererModuleHost?.dispatchFrame("afterFrame", dt, this.elapsedTime);
|
|
731
|
+
// Composite registered screen-space UI (html-ui panels) on top of the
|
|
732
|
+
// final image (skip on GPU error to avoid cascade). This must run AFTER
|
|
733
|
+
// afterFrame — 2D games (viewportCount=0) do their custom rendering in
|
|
734
|
+
// the afterFrame callback, so drawing UI earlier would put it underneath
|
|
735
|
+
// their clear pass.
|
|
736
|
+
if (!gpuError && this.device && this.context) {
|
|
737
|
+
const compositors = [...this.uiCompositors]
|
|
738
|
+
.filter((c) => c.hasContent())
|
|
739
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
740
|
+
const uiCanvasView = compositors.length > 0
|
|
741
|
+
? this.getSurfaceTexture()?.createView()
|
|
742
|
+
: undefined;
|
|
743
|
+
if (uiCanvasView) {
|
|
744
|
+
const uiEncoder = this.device.createCommandEncoder();
|
|
745
|
+
const uiPass = uiEncoder.beginRenderPass({
|
|
746
|
+
colorAttachments: [{
|
|
747
|
+
view: uiCanvasView,
|
|
748
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
749
|
+
loadOp: "load",
|
|
750
|
+
storeOp: "store",
|
|
751
|
+
}],
|
|
752
|
+
});
|
|
753
|
+
compositors.forEach((c) => { c.render(uiPass, this.canvas.width, this.canvas.height); });
|
|
754
|
+
uiPass.end();
|
|
755
|
+
// "preUi" slot — worker passes ordered ahead of the UI-composite
|
|
756
|
+
// submit so their output is visible to the compositor this frame.
|
|
757
|
+
this.device.queue.submit([...(passMailbox?.drain("preUi") ?? []), uiEncoder.finish()]);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
// Present the surface (native wgpu requires explicit presentation;
|
|
761
|
+
// in browsers this is automatic at the end of the frame). Must be last —
|
|
762
|
+
// all submissions targeting the surface texture happen above.
|
|
763
|
+
if (this.context && this.context.present) {
|
|
764
|
+
this.context.present();
|
|
765
|
+
}
|
|
766
|
+
const __rfTotal = performance.now() - now;
|
|
767
|
+
if (this.config.debugTimingWarnings && __rfTotal > 20)
|
|
768
|
+
log.warn("game-renderer", `renderFrame took ${__rfTotal.toFixed(1)}ms`);
|
|
769
|
+
// NOTE: renderFrame() intentionally does NOT schedule the next frame —
|
|
770
|
+
// render() owns the rAF chain so one-shot callers (renderOnce, MCP
|
|
771
|
+
// screenshot capture) can't leak parallel loops.
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Acquire this frame's swapchain texture. May return null on the native
|
|
775
|
+
* runtime while the surface can't produce a frame (in-flight resize,
|
|
776
|
+
* occluded window, transient swapchain failure) — callers must skip the
|
|
777
|
+
* canvas-targeted GPU work for that frame instead of crashing on
|
|
778
|
+
* `null.createView()`.
|
|
779
|
+
*/
|
|
780
|
+
getSurfaceTexture() {
|
|
781
|
+
try {
|
|
782
|
+
return this.context?.getCurrentTexture() ?? null;
|
|
783
|
+
}
|
|
784
|
+
catch {
|
|
785
|
+
return null;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
renderViewport(viewportIdx, dt, offscreen) {
|
|
789
|
+
if (!this.device || !this.context)
|
|
790
|
+
return null;
|
|
791
|
+
const xrProvider = this.renderTargetProvider;
|
|
792
|
+
const useOffscreen = !xrProvider && offscreen && offscreen.type !== "none";
|
|
793
|
+
const origViewport = xrProvider
|
|
794
|
+
? xrProvider.getViewportRect(viewportIdx, this.canvas.width, this.canvas.height)
|
|
795
|
+
: this.viewports[viewportIdx];
|
|
796
|
+
if (!origViewport)
|
|
797
|
+
return null;
|
|
798
|
+
const viewport = (useOffscreen && offscreen?.scaleViewport)
|
|
799
|
+
? offscreen.scaleViewport(origViewport)
|
|
800
|
+
: origViewport;
|
|
801
|
+
// Get camera info: viewport camera provider (XR) takes priority, then
|
|
802
|
+
// game callback, then renderer plugin host's camera controller fallback.
|
|
803
|
+
// The game callback is always called (even when the provider returns
|
|
804
|
+
// non-null) so side effects like per-eye uniform updates still run.
|
|
805
|
+
const providerInfo = this.rendererModuleHost?.getViewportCameraInfo(viewportIdx, dt, this.elapsedTime) ?? null;
|
|
806
|
+
const gameInfo = this.callbacks.onViewport?.(viewportIdx, dt, this.elapsedTime) ?? null;
|
|
807
|
+
let camInfo = providerInfo ?? gameInfo;
|
|
808
|
+
if (!camInfo && this.rendererModuleHost) {
|
|
809
|
+
const camState = this.rendererModuleHost.getCameraState(viewportIdx);
|
|
810
|
+
if (camState) {
|
|
811
|
+
camInfo = { camera: camState, viewport };
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
// 2D mode: if no camera info was provided, use a default orthographic
|
|
815
|
+
// camera that covers the full viewport. This allows 2D-only renderers
|
|
816
|
+
// (sand games, tile games) to use GameRenderer without a camera system.
|
|
817
|
+
if (!camInfo && this.mode === "2d") {
|
|
818
|
+
camInfo = {
|
|
819
|
+
camera: {
|
|
820
|
+
position: [0, 0, 1],
|
|
821
|
+
target: [0, 0, 0],
|
|
822
|
+
up: [0, 1, 0],
|
|
823
|
+
fov: 90,
|
|
824
|
+
near: 0.1,
|
|
825
|
+
far: 100,
|
|
826
|
+
aspect: viewport.w / viewport.h,
|
|
827
|
+
},
|
|
828
|
+
viewport,
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
if (!camInfo)
|
|
832
|
+
return null;
|
|
833
|
+
const isFirst = viewportIdx === 0;
|
|
834
|
+
const isLast = viewportIdx === this.viewportCount - 1;
|
|
835
|
+
// Determine color/depth views — XR provider takes precedence, then offscreen, then canvas
|
|
836
|
+
const __ctStart = performance.now();
|
|
837
|
+
const colorView = xrProvider
|
|
838
|
+
? xrProvider.getColorView(viewportIdx)
|
|
839
|
+
: (useOffscreen && offscreen)
|
|
840
|
+
? (offscreen.type === "pixelation"
|
|
841
|
+
? offscreen.getColorView()
|
|
842
|
+
: offscreen.type === "postprocess"
|
|
843
|
+
? (offscreen.getSceneColorView?.() ?? this.getSurfaceTexture()?.createView())
|
|
844
|
+
: this.getSurfaceTexture()?.createView())
|
|
845
|
+
: this.getSurfaceTexture()?.createView();
|
|
846
|
+
const __ctMs = performance.now() - __ctStart;
|
|
847
|
+
// Swapchain couldn't produce a frame (native resize in flight, occluded
|
|
848
|
+
// window, transient failure) — skip this viewport's GPU work; game
|
|
849
|
+
// callbacks already ran above and the loop stays alive for next frame.
|
|
850
|
+
if (!colorView)
|
|
851
|
+
return null;
|
|
852
|
+
if (this.config.debugTimingWarnings && __ctMs > 20)
|
|
853
|
+
log.warn("game-renderer", `getCurrentTexture took ${__ctMs.toFixed(1)}ms`);
|
|
854
|
+
const depthView = xrProvider
|
|
855
|
+
? xrProvider.getDepthView(viewportIdx, origViewport.w, origViewport.h)
|
|
856
|
+
: (useOffscreen && offscreen)
|
|
857
|
+
? (offscreen.type === "pixelation"
|
|
858
|
+
? offscreen.getDepthView()
|
|
859
|
+
: offscreen.type === "postprocess"
|
|
860
|
+
? (offscreen.getSceneDepthView?.() ?? this.createDepthTexture(origViewport.w, origViewport.h))
|
|
861
|
+
: this.createDepthTexture(origViewport.w, origViewport.h))
|
|
862
|
+
: this.createDepthTexture(origViewport.w, origViewport.h);
|
|
863
|
+
// Ensure graph handles exist for the imported color/depth views.
|
|
864
|
+
if (!this.colorHandle) {
|
|
865
|
+
this.colorHandle = this.frameGraph.importTextureView("color", null);
|
|
866
|
+
this.frameGraph.markDirty();
|
|
867
|
+
}
|
|
868
|
+
if (!this.depthHandle) {
|
|
869
|
+
this.depthHandle = this.frameGraph.importTextureView("depth", null);
|
|
870
|
+
this.frameGraph.markDirty();
|
|
871
|
+
}
|
|
872
|
+
this.frameGraph.setImportedTextureView(this.colorHandle, colorView);
|
|
873
|
+
this.frameGraph.setImportedTextureView(this.depthHandle, depthView);
|
|
874
|
+
if (isFirst) {
|
|
875
|
+
this.gpuProfiler.beginFrame();
|
|
876
|
+
}
|
|
877
|
+
if (this.frameGraph.isDirty() || !this.graphCompiled) {
|
|
878
|
+
this.frameGraph.compile(this.device, this.canvas.width, this.canvas.height);
|
|
879
|
+
this.graphCompiled = true;
|
|
880
|
+
}
|
|
881
|
+
const encoder = this.device.createCommandEncoder();
|
|
882
|
+
// Reuse a single RenderContext across viewports/frames — allocating the
|
|
883
|
+
// ~35-field literal per viewport per frame was pure GC churn. Fields that
|
|
884
|
+
// vary per viewport are reassigned below; the closures capture `this` and
|
|
885
|
+
// are created once.
|
|
886
|
+
if (!this.frameCtx) {
|
|
887
|
+
this.frameCtx = {
|
|
888
|
+
device: this.device,
|
|
889
|
+
encoder,
|
|
890
|
+
pass: null,
|
|
891
|
+
camera: camInfo.camera,
|
|
892
|
+
viewport,
|
|
893
|
+
viewportIdx,
|
|
894
|
+
viewportCount: this.viewportCount,
|
|
895
|
+
dt,
|
|
896
|
+
elapsedTime: this.elapsedTime,
|
|
897
|
+
isFirstViewport: isFirst,
|
|
898
|
+
isLastViewport: isLast,
|
|
899
|
+
width: viewport.w,
|
|
900
|
+
height: viewport.h,
|
|
901
|
+
viewProj: undefined,
|
|
902
|
+
invViewProj: undefined,
|
|
903
|
+
prevViewProj: undefined,
|
|
904
|
+
cameraPos: camInfo.camera.position,
|
|
905
|
+
lightData: null,
|
|
906
|
+
lightViewProj: undefined,
|
|
907
|
+
mesh: null,
|
|
908
|
+
modelMatrix: undefined,
|
|
909
|
+
shadowsEnabled: false,
|
|
910
|
+
bloomEnabled: false,
|
|
911
|
+
shadowSampler: null,
|
|
912
|
+
debugQueue: null,
|
|
913
|
+
opaqueVertexBuffer: null,
|
|
914
|
+
opaqueIndexBuffer: null,
|
|
915
|
+
opaqueIndexCount: 0,
|
|
916
|
+
opaqueIndexFormat: "uint32",
|
|
917
|
+
getView: (h) => this.frameGraph.getTextureView(h),
|
|
918
|
+
getTexture: (h) => this.frameGraph.getTexture(h),
|
|
919
|
+
addDrawCalls: (n) => { this.frameDrawCalls += n; },
|
|
920
|
+
addTriangles: (n) => { this.frameTriangles += n; },
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
const ctx = this.frameCtx;
|
|
924
|
+
ctx.device = this.device;
|
|
925
|
+
ctx.encoder = encoder;
|
|
926
|
+
ctx.camera = camInfo.camera;
|
|
927
|
+
ctx.viewport = viewport;
|
|
928
|
+
ctx.viewportIdx = viewportIdx;
|
|
929
|
+
ctx.viewportCount = this.viewportCount;
|
|
930
|
+
ctx.dt = dt;
|
|
931
|
+
ctx.elapsedTime = this.elapsedTime;
|
|
932
|
+
ctx.isFirstViewport = isFirst;
|
|
933
|
+
ctx.isLastViewport = isLast;
|
|
934
|
+
ctx.width = viewport.w;
|
|
935
|
+
ctx.height = viewport.h;
|
|
936
|
+
ctx.cameraPos = camInfo.camera.position;
|
|
937
|
+
this.frameGraph.execute(ctx);
|
|
938
|
+
// Resolve GPU timestamp queries on first viewport.
|
|
939
|
+
// NOTE: readGpuTimers() (which calls mapAsync on the read buffer) must NOT
|
|
940
|
+
// be called here — the command buffer containing the copy-to-readBuffer
|
|
941
|
+
// hasn't been submitted yet. Calling mapAsync before submit puts the buffer
|
|
942
|
+
// in a mapped-pending state, and the subsequent queue.submit() fails with
|
|
943
|
+
// "Buffer used in submit while mapped." readGpuTimers() is called after
|
|
944
|
+
// submit in renderFrame().
|
|
945
|
+
if (isFirst) {
|
|
946
|
+
this.gpuProfiler.resolveGpuTimers(encoder);
|
|
947
|
+
}
|
|
948
|
+
return encoder.finish();
|
|
949
|
+
}
|
|
950
|
+
// --- Depth texture cache ---
|
|
951
|
+
createDepthTexture(w, h) {
|
|
952
|
+
if (!this.device)
|
|
953
|
+
throw new Error("No device");
|
|
954
|
+
const key = `${w}x${h}`;
|
|
955
|
+
let tex = this.depthTextures.get(key);
|
|
956
|
+
if (tex) {
|
|
957
|
+
// LRU: move to most-recently-used by re-inserting.
|
|
958
|
+
this.depthTextures.delete(key);
|
|
959
|
+
this.depthTextures.set(key, tex);
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
tex = this.device.createTexture({
|
|
963
|
+
size: [w, h],
|
|
964
|
+
format: this.depthFormat,
|
|
965
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
966
|
+
});
|
|
967
|
+
this.depthTextures.set(key, tex);
|
|
968
|
+
// Evict oldest entries if over cap.
|
|
969
|
+
while (this.depthTextures.size > this.depthTextureCacheSize) {
|
|
970
|
+
const oldestKey = this.depthTextures.keys().next().value;
|
|
971
|
+
if (oldestKey === undefined)
|
|
972
|
+
break;
|
|
973
|
+
const oldest = this.depthTextures.get(oldestKey);
|
|
974
|
+
this.depthTextures.delete(oldestKey);
|
|
975
|
+
oldest?.destroy();
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return tex.createView();
|
|
979
|
+
}
|
|
980
|
+
// --- UI ---
|
|
981
|
+
// --- Offscreen mode ---
|
|
982
|
+
setOffscreenMode(mode) {
|
|
983
|
+
this.offscreenMode = mode;
|
|
984
|
+
}
|
|
985
|
+
getOffscreenMode() {
|
|
986
|
+
return this.offscreenMode;
|
|
987
|
+
}
|
|
988
|
+
// --- XR render target provider ---
|
|
989
|
+
setRenderTargetProvider(provider) {
|
|
990
|
+
this.renderTargetProvider = provider;
|
|
991
|
+
}
|
|
992
|
+
getRenderTargetProvider() {
|
|
993
|
+
return this.renderTargetProvider;
|
|
994
|
+
}
|
|
995
|
+
// --- rAF source override (for XR sessions) ---
|
|
996
|
+
setRAFSource(request, cancel) {
|
|
997
|
+
this.rafSource = request;
|
|
998
|
+
this.cancelRaf = cancel;
|
|
999
|
+
}
|
|
1000
|
+
clearRAFSource() {
|
|
1001
|
+
this.rafSource = null;
|
|
1002
|
+
this.cancelRaf = null;
|
|
1003
|
+
}
|
|
1004
|
+
// --- Callbacks ---
|
|
1005
|
+
setCallbacks(callbacks) {
|
|
1006
|
+
this.callbacks = callbacks;
|
|
1007
|
+
}
|
|
1008
|
+
/** Get the current frame callbacks (for wrapping/extending). */
|
|
1009
|
+
getCallbacks() {
|
|
1010
|
+
return this.callbacks;
|
|
1011
|
+
}
|
|
1012
|
+
// --- Renderer plugins ---
|
|
1013
|
+
useRendererModule(plugin) {
|
|
1014
|
+
if (!this.rendererModuleHost) {
|
|
1015
|
+
throw new Error("RendererModuleHost not initialized — call init() first");
|
|
1016
|
+
}
|
|
1017
|
+
this.rendererModuleHost.registerModule(plugin);
|
|
1018
|
+
}
|
|
1019
|
+
getRendererModuleHost() {
|
|
1020
|
+
return this.rendererModuleHost;
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Composite registered screen-space UI (html-ui panels via
|
|
1024
|
+
* ctx.registerUiCompositor) onto the current surface texture.
|
|
1025
|
+
*
|
|
1026
|
+
* Renderers with a custom drawFrame()/present path that bypass
|
|
1027
|
+
* renderFrame() must call this at end-of-frame — after post-processing
|
|
1028
|
+
* lands on the surface, before present — or module-registered UI will
|
|
1029
|
+
* never be drawn. renderFrame() itself uses the equivalent block inline;
|
|
1030
|
+
* this helper only runs the compositors.
|
|
1031
|
+
*/
|
|
1032
|
+
renderScreenUiCompositors() {
|
|
1033
|
+
const compositors = [...this.uiCompositors]
|
|
1034
|
+
.filter((c) => c.hasContent())
|
|
1035
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
1036
|
+
if (compositors.length === 0 || !this.device || !this.context)
|
|
1037
|
+
return;
|
|
1038
|
+
const canvasView = this.getSurfaceTexture()?.createView();
|
|
1039
|
+
if (!canvasView)
|
|
1040
|
+
return;
|
|
1041
|
+
const uiEncoder = this.device.createCommandEncoder();
|
|
1042
|
+
const uiPass = uiEncoder.beginRenderPass({
|
|
1043
|
+
colorAttachments: [{
|
|
1044
|
+
view: canvasView,
|
|
1045
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
1046
|
+
loadOp: "load",
|
|
1047
|
+
storeOp: "store",
|
|
1048
|
+
}],
|
|
1049
|
+
});
|
|
1050
|
+
compositors.forEach((c) => { c.render(uiPass, this.canvas.width, this.canvas.height); });
|
|
1051
|
+
uiPass.end();
|
|
1052
|
+
this.device.queue.submit([uiEncoder.finish()]);
|
|
1053
|
+
}
|
|
1054
|
+
// --- Getters ---
|
|
1055
|
+
getDevice() {
|
|
1056
|
+
return this.device;
|
|
1057
|
+
}
|
|
1058
|
+
getAdapter() {
|
|
1059
|
+
return this.adapter;
|
|
1060
|
+
}
|
|
1061
|
+
getContext() {
|
|
1062
|
+
return this.context;
|
|
1063
|
+
}
|
|
1064
|
+
getFormat() {
|
|
1065
|
+
return this.format;
|
|
1066
|
+
}
|
|
1067
|
+
getDepthFormat() {
|
|
1068
|
+
return this.depthFormat;
|
|
1069
|
+
}
|
|
1070
|
+
getMSAASampleCount() {
|
|
1071
|
+
return this.msaaSampleCount;
|
|
1072
|
+
}
|
|
1073
|
+
/** The render surface this renderer draws into. Canonical accessor. */
|
|
1074
|
+
getSurface() {
|
|
1075
|
+
return this.canvas;
|
|
1076
|
+
}
|
|
1077
|
+
/** @deprecated Use getSurface() — the surface is not necessarily a DOM canvas. */
|
|
1078
|
+
getCanvas() {
|
|
1079
|
+
return this.canvas;
|
|
1080
|
+
}
|
|
1081
|
+
getCanvasWidth() {
|
|
1082
|
+
return this.canvas.width;
|
|
1083
|
+
}
|
|
1084
|
+
getCanvasHeight() {
|
|
1085
|
+
return this.canvas.height;
|
|
1086
|
+
}
|
|
1087
|
+
getFPS() {
|
|
1088
|
+
return this.fps;
|
|
1089
|
+
}
|
|
1090
|
+
getElapsedTime() {
|
|
1091
|
+
return this.elapsedTime;
|
|
1092
|
+
}
|
|
1093
|
+
getInputManager() {
|
|
1094
|
+
return this.inputManager;
|
|
1095
|
+
}
|
|
1096
|
+
getGraph() {
|
|
1097
|
+
return this.frameGraph;
|
|
1098
|
+
}
|
|
1099
|
+
getSlotRegistry() {
|
|
1100
|
+
return this.frameGraph.getSlotRegistry();
|
|
1101
|
+
}
|
|
1102
|
+
getUIInputRouter() {
|
|
1103
|
+
return this.uiInputRouter;
|
|
1104
|
+
}
|
|
1105
|
+
getTelemetryCollector() {
|
|
1106
|
+
return this.telemetryCollector;
|
|
1107
|
+
}
|
|
1108
|
+
getGPUProfiler() {
|
|
1109
|
+
return this.gpuProfiler;
|
|
1110
|
+
}
|
|
1111
|
+
getFrameGraph() {
|
|
1112
|
+
if (!this.gpuProfiler)
|
|
1113
|
+
return null;
|
|
1114
|
+
const passTimings = this.gpuProfiler.getPassTimings();
|
|
1115
|
+
const ppInfo = this.callbacks.getPostProcessInfo?.() ?? {
|
|
1116
|
+
pixelationEnabled: false, pixelSize: 4, postProcessEffects: [],
|
|
1117
|
+
};
|
|
1118
|
+
const passNames = this.frameGraph.getPassOrder();
|
|
1119
|
+
return GPUProfiler.buildFrameGraphData(passTimings, ppInfo, passNames);
|
|
1120
|
+
}
|
|
1121
|
+
getGPUResourceTracker() {
|
|
1122
|
+
return this.gpuResourceTracker;
|
|
1123
|
+
}
|
|
1124
|
+
getGPUInfo() {
|
|
1125
|
+
if (!this.gpuProfiler)
|
|
1126
|
+
return null;
|
|
1127
|
+
return this.gpuProfiler.getGPUInfo(this.canvas, this.msaaSampleCount);
|
|
1128
|
+
}
|
|
1129
|
+
getAdapterInfo() {
|
|
1130
|
+
return this.gpuProfiler?.getAdapterInfo() ?? null;
|
|
1131
|
+
}
|
|
1132
|
+
getGPUErrors() {
|
|
1133
|
+
return this.gpuProfiler?.getGPUErrors() ?? [];
|
|
1134
|
+
}
|
|
1135
|
+
clearGPUErrors() {
|
|
1136
|
+
this.gpuProfiler?.clearGPUErrors();
|
|
1137
|
+
}
|
|
1138
|
+
getDpr() {
|
|
1139
|
+
return this.dpr;
|
|
1140
|
+
}
|
|
1141
|
+
// --- Frame stats (for game passes to update) ---
|
|
1142
|
+
addDrawCalls(n) {
|
|
1143
|
+
this.frameDrawCalls += n;
|
|
1144
|
+
}
|
|
1145
|
+
addTriangles(n) {
|
|
1146
|
+
this.frameTriangles += n;
|
|
1147
|
+
}
|
|
1148
|
+
// --- Destroy ---
|
|
1149
|
+
destroy() {
|
|
1150
|
+
this.running = false;
|
|
1151
|
+
this.resizeWatcher?.destroy();
|
|
1152
|
+
this.resizeWatcher = null;
|
|
1153
|
+
this.rendererModuleHost?.disposeAll();
|
|
1154
|
+
this.rendererModuleHost = null;
|
|
1155
|
+
this.inputManager.destroy();
|
|
1156
|
+
this.gpuProfiler?.destroy();
|
|
1157
|
+
this.gpuProfiler = null;
|
|
1158
|
+
this.telemetryCollector = null;
|
|
1159
|
+
this.device = null;
|
|
1160
|
+
for (const tex of this.depthTextures.values()) {
|
|
1161
|
+
tex.destroy();
|
|
1162
|
+
}
|
|
1163
|
+
this.depthTextures.clear();
|
|
1164
|
+
}
|
|
1165
|
+
}
|