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